From 2ffcb1c3bd8542dbd6c1207958c8278731555e31 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 29 Oct 2025 00:08:16 -0500 Subject: [PATCH] Implemented Custom Goal Framework --- dialog.asm | 8 +- elder.asm | 20 ++- events.asm | 4 +- goalitem.asm | 380 ++++++++++++++++++++++++++++++-------------------- tables.asm | 113 ++++++++++----- utilities.asm | 17 +++ 6 files changed, 339 insertions(+), 203 deletions(-) diff --git a/dialog.asm b/dialog.asm index 5cc89a2..4a38b70 100644 --- a/dialog.asm +++ b/dialog.asm @@ -265,7 +265,7 @@ RTL RTL ;-------------------------------------------------------------------------------- DialogGanon1: - JSL CheckGanonVulnerability + LDA.b #$01 : JSL CheckConditionPass REP #$20 LDA.w #$018C BCC + @@ -284,7 +284,7 @@ RTL ; s = silver arrow bow ; p = 2nd progressive bow DialogGanon2: - JSL CheckGanonVulnerability + LDA.b #$01 : JSL CheckConditionPass REP #$20 BCS + @@ -372,8 +372,8 @@ RTL ;--------------------------------------------------------------------------------------------------- AgahnimAsksAboutPed: - LDA.l GanonVulnerableMode - CMP.b #$06 : BNE .vanilla + ; seems light_speed option to change some aga text is unused for now + BRA .vanilla LDA.l OverworldEventDataWRAM+$80 ; check ped flag AND.b #$40 diff --git a/elder.asm b/elder.asm index d464ece..fa5a838 100644 --- a/elder.asm +++ b/elder.asm @@ -39,32 +39,30 @@ RTL Elder_Code: { - REP #$20 - LDA.l GoalItemRequirement : BEQ .despawn - LDA.l GanonVulnerableMode : AND.w #$00FF : CMP.w #$0005 : BEQ .despawn - LDA.l TurnInGoalItems : AND.w #$00FF : BNE + + TXY : LDX.b #$06 + REP #$30 + LDA.l GoalConditionTable, X + TAX : LDA.l $B00000, X + SEP #$30 + TYX + CMP.b #$00 : BEQ .despawn ; no goal, despawn + LDA.l TurnInGoalItems : BNE + .despawn - SEP #$20 STZ.w SpriteAITable, X ; despawn self RTS + - SEP #$20 LDA.b GameSubMode BNE .done LDA.b #$96 LDY.b #$01 JSL Sprite_ShowSolicitedMessageIfPlayerFacing_PreserveMessage : BCC .dont_show - REP #$20 - LDA.l GoalCounter - CMP.l GoalItemRequirement : !BLT + - SEP #$20 + LDA.b #$03 : JSL CheckConditionPass : BCC + JSL ActivateTriforceCutscene + .dont_show .done - SEP #$20 LDA.b FrameCounter : LSR #5 : AND.b #$01 : STA.w SpriteGFXControl, X RTS } diff --git a/events.asm b/events.asm index 6247a7a..e288cc5 100644 --- a/events.asm +++ b/events.asm @@ -107,8 +107,8 @@ RTL ;-------------------------------------------------------------------------------- OnAga1Defeated: STA.l ProgressIndicator ; vanilla game state stuff we overwrote - LDA.l GanonVulnerableMode - CMP.b #$06 : BNE + + ; seems light_speed option to auto triforce room is unused for now + BRA + .light_speed REP #$20 LDA.w #$0019 : STA.b GameMode diff --git a/goalitem.asm b/goalitem.asm index 1484dc2..8c7a100 100644 --- a/goalitem.asm +++ b/goalitem.asm @@ -1,6 +1,6 @@ GoalItemGanonCheck: LDA.w SpriteTypeTable, X : CMP.b #$D6 : BNE .success ; skip if not ganon - JSL CheckGanonVulnerability + LDA.b #$01 : JSL CheckConditionPass BCS .success .fail @@ -11,104 +11,162 @@ RTL LDA.b OAMOffsetY : CMP.b #$80 ; thing we wrote over RTL ;-------------------------------------------------------------------------------- -;Carry clear = ganon invincible -;Carry set = ganon vulnerable -CheckGanonVulnerability: - PHX - LDA.l GanonVulnerableMode - ASL - TAX +; Input A = Type of condition check +; Carry clear = failed check +; Carry set = successful check +CheckConditionPass: + PHX : PHY + PHB + LDY.b #GoalConditionTable>>16 : PHY : PLB : STY.b Scrap02 + REP #$20 + ASL : TAY + LDA.w GoalConditionTable, Y : STA.b Scrap00 + PHK : PLB + SEP #$20 + LDY.b #$00 + - LDA.b [Scrap00], Y : CMP.b #$FF : BEQ .exit + INY : ROL : TAX + JSR (.conditions, X) : BCC .exit : BRA - - ; Carry - ; 0 - invulnerable - ; 1 - vulnerable - JSR (.goals, X) +.exit + PLB : PLY : PLX +RTL - PLX - RTL - - -.goals - dw .vulnerable - dw .invulnerable - dw .all_dungeons - dw .crystals_and_aga +; Y = index after condition code +; Carry = Set if using default target value +.conditions + dw .always_fail + dw .pendants dw .crystals + dw .pendant_bosses + dw .crystal_bosses + dw .bosses + dw .agahnim_defeated + dw .agahnim2_defeated dw .goal_item - dw .light_speed - dw .crystals_and_bosses - dw .bosses_only - dw .all_dungeons_no_agahnim - dw .all_items - dw .completionist + dw .collection_rate + dw .custom_goal + dw .bingo + dw .success + dw .success + dw .success + dw .success -; 00 = always vulnerable -.vulnerable +.agahnim2_defeated + LDA.l RoomDataWRAM[$0D].high : AND.b #$08 : BEQ .fail +.bingo ; not implemented yet .success - SEC - RTS - -; 01 = always invulnerable -.invulnerable + SEC : RTS +.always_fail .fail - CLC - RTS - -; 02 = All dungeons -.all_dungeons - LDA.l ProgressIndicator : CMP.b #$03 : BCC .fail ; require post-aga world state - -; 09 = All dungeons except agahnim -.all_dungeons_no_agahnim - LDA.l PendantsField : AND.b #$07 : CMP.b #$07 : BNE .fail ; require all pendants - LDA.l CrystalsField : AND.b #$7F : CMP.b #$7F : BNE .fail ; require all crystals - LDA.l RoomDataWRAM[$0D].high : AND.b #$08 : BEQ .fail ; require aga2 defeated (pyramid hole open) - BRA .success - -; 03 = crystals and aga 2 -.crystals_and_aga - LDA.l RoomDataWRAM[$0D].high : AND.b #$08 : BEQ .fail ; check aga2 first then bleed in - -; 04 = crystals only + CLC : RTS +.pendants + PHP + LDA.l PendantCounter : PLP : BCC + + CMP.b #$03 : RTS .crystals - JSL CheckEnoughCrystalsForGanon - RTS - -; 05 = require goal item + PHP + LDA.l CrystalCounter : PLP : BCC + + CMP.b #$07 : RTS +.pendant_bosses + PHP + LDA.b #$07 : STA.b Scrap03 : STZ.b Scrap04 + JSR CheckForBossesDefeated : PLP : BCC + + CMP.b #$03 : RTS +.crystal_bosses + PHP + LDA.b #$40 : STA.b Scrap03 : STZ.b Scrap04 + JSR CheckForBossesDefeated : PLP : BCC + + CMP.b #$07 : RTS +.bosses + PHP + LDA.b #$47 : STA.b Scrap03 : STZ.b Scrap04 + JSR CheckForBossesDefeated : PLP : BCC + + CMP.b #$10 : RTS + + CMP.b [Scrap00], Y : INY : RTS +.agahnim_defeated + LDA.l ProgressIndicator : CMP.b #$03 : RTS .goal_item - REP #$20 - LDA.l GoalCounter : CMP.l GoalItemRequirement - SEP #$20 + REP #$20 + LDA.l GoalCounter : BCC + + CMP.l GoalItemRequirement : BRA ++ +.collection_rate + REP #$20 + LDA.l TotalItemCounter : BCC + + CMP.l TotalItemCount : BRA ++ + + CMP.b [Scrap00], Y : INY : INY : ++ + SEP #$20 RTS +.custom_goal + LDA.b [Scrap00], Y : INY ; options + PHA : AND.b #$07 : ASL : TAX : PLA + ;JMP CheckConditionPassCustom + ; flows into next function, do not insert code after without uncommenting above -; 06 = light speed -.light_speed - BRA .fail +; -------------------------------------------------------------------------------- +; Input A = Options value, see GoalConditionTable for format +; Input X = Condition check type index +; Input Y = Index after Options byte +CheckConditionPassCustom: + PHX : PHA : BIT.b #$08 : PHP + REP #$30 + LDA.b [Scrap00], Y : INY : INY ; address + PLP : REP #$30 : BEQ .byte +.word + TAX + SEP #$20 + PLA + AND.b #$10 + REP #$20 + BNE + + LDA.l $7E0000, X : BRA ++ + + + LDA.l $7F0000, X : ++ + SEP #$10 + PLX + REP #$10 + JSR (.comparisons, X) + INY + SEP #$30 + RTS +.byte + TAX + SEP #$20 + PLA + AND.b #$10 : BNE + + LDA.l $7E0000, X : BRA ++ + + + LDA.l $7F0000, X : ++ + SEP #$10 + PLX + JMP (.comparisons, X) -; 07 = Crystals and bosses -.crystals_and_bosses - JSL CheckEnoughCrystalsForGanon ; check crystals first then bleed in to next - BCC .fail - -; 08 = Crystal bosses but no crystals -.bosses_only - JMP CheckForCrystalBossesDefeated - -; 09 = 100% item collection rate -.all_items - REP #$20 - LDA.l TotalItemCounter : CMP.l TotalItemCount - SEP #$20 - RTS - -; 0A = 100% item collection rate and all dungeons -.completionist - REP #$20 - LDA.l TotalItemCounter : CMP.l TotalItemCount - SEP #$20 - BCC .fail - BRA .all_dungeons +.comparisons + dw .minimum + dw .exact + dw .bitfield_nonzero + dw .bitfield_match + dw .count_bits + dw .fail + dw .fail + dw .fail +.pass + INY : SEC : RTS +.count_bits + JSL CountBits +.minimum + CMP.b [Scrap00], Y : INY + RTS +.bitfield_match + AND.b [Scrap00], Y +.exact + CMP.b [Scrap00], Y : BEQ .pass + INY : CLC : RTS +.bitfield_nonzero + AND.b [Scrap00], Y : BNE .pass +.fail + INY : CLC : RTS ;-------------------------------------------------------------------------------- GTCutscene_TransferGfx: PHA @@ -203,47 +261,83 @@ GTCutscene_ActivateSparkle_SelectCrystal: RTL ;-------------------------------------------------------------------------------- GTCutscene_NumberOfCrystals: + PHX : PHY : PHP REP #$20 - LDA.l GanonsTowerOpenAddress : CMP.w #CrystalCounter : BEQ + - LDA.w #$0001 : BRA .done - + LDA.l GanonsTowerOpenTarget - .done + LDA.l GanonsTowerOpenGfx+2 : BEQ .not_multiple_gfx + LDX.b #$04 + - LDA.l GanonsTowerOpenGfx, X : BEQ + + INX : INX : CPX.b #$0E : BCC - + + + TXA : LSR + JMP .done +.not_multiple_gfx + LDX.b #$00 : LDA.l GoalConditionTable, X + TXY : STY.b Scrap00 + REP #$10 SEP #$20 + TAX + - LDA.l $B00000, X : CMP.b #$FF : BEQ .use_y + ROL : PHP : CMP.b #$10 : BCS .not_8bit_compare + ; uses 8-bit targets + CMP.b #$04 : BNE .not_crystal_goal ; crystal goal + PLP : BCC + + LDY.b #$07 : BRA .use_y + .not_crystal_goal + CMP.b #$02 : BNE .not_pendant_goal ; pendant goal + PLP : BCC + + LDY.b #$03 : BRA .use_y + + + INX : LDA.l $B00000, X : TAY : BRA .use_y + .not_pendant_goal + PLP : INX : BCS + + INX + + + BRA - + .not_8bit_compare + CMP.b #$14 : BCS .custom_goal + ; uses 16-bit targets + PLP : INX : BCS + + INX : INX + + + BRA - + .custom_goal + BNE .unknown + PLP + INC.b Scrap00 + INX : LDA.l $B00000, X : BIT.b #$08 : PHP + INX : INX : INX : AND.b #$03 : BEQ .use_custom_target + INY + INX : PLP : BEQ + + INX + + + BRA - + .use_custom_target + PLP : BEQ .8bit_target + ; 16-bit target + REP #$20 + LDA.l $B00000, X : CMP.w #$0008 : SEP #$20 : INX : BRA + + .8bit_target + LDA.l $B00000, X : CMP.b #$08 : + : BCC + + INY : INX : BRA - + + + PHY : ADC.b 1,S : PLY : TAY + INX : JMP - + .unknown ; unknown condition, exit with safe value + PLP : INY +.use_y + TYA : CMP.b #$08 : BCC .done + LDA.b Scrap00 +.done + PLP : PLY : PLX RTS ;-------------------------------------------------------------------------------- -CheckEnoughCrystalsForGanon: - REP #$20 - LDA.l CrystalCounter - CMP.l GanonVulnerableTarget - SEP #$20 -RTL -;-------------------------------------------------------------------------------- CheckTowerOpen: - LDA.l GanonsTowerOpenMode : ASL : TAX - JSR (.tower_open_modes,X) -RTL - .tower_open_modes - dw .vanilla - dw .arbitrary_cmp - - .vanilla - LDA.l CrystalsField - AND.b #$7F : CMP.b #$7F - RTS - - .arbitrary_cmp - REP #$30 - LDA.l GanonsTowerOpenAddress : TAX - LDA.l $7E0000,X - CMP.l GanonsTowerOpenTarget - SEP #$30 - RTS - + LDA.b #$00 : JML CheckConditionPass ;--------------------------------------------------------------------------------------------------- CheckAgaForPed: REP #$20 - LDA.l GanonVulnerableMode - CMP.w #$0006 : BNE .vanilla + ; seems light_speed option to force blue balls is unused for now + BRA .vanilla .light_speed SEP #$20 @@ -264,7 +358,7 @@ CheckAgaForPed: RTL ;--------------------------------------------------------------------------------------------------- -CheckForCrystalBossesDefeated: +CheckForBossesDefeated: PHB : PHX : PHY LDA.b #CrystalPendantFlags_2>>16 @@ -273,13 +367,13 @@ CheckForCrystalBossesDefeated: REP #$30 ; count of number of bosses killed - STZ.b Scrap00 + STZ.b Scrap04 LDY.w #10 .next_check LDA.w CrystalPendantFlags_2+2,Y - BIT.w #$0040 + BIT.w Scrap03 BEQ ++ TYA @@ -293,7 +387,7 @@ CheckForCrystalBossesDefeated: AND.w #$0800 BEQ ++ - INC.b Scrap00 + INC.b Scrap04 ++ DEY BPL .next_check @@ -301,36 +395,22 @@ CheckForCrystalBossesDefeated: SEP #$30 PLY : PLX : PLB - LDA.b Scrap00 : CMP.l GanonVulnerableTarget - + LDA.b Scrap04 RTS ;--------------------------------------------------------------------------------------------------- CheckPedestalPull: ; Out: c - Successful ped pull if set, do nothing if unset. - PHX - LDA.l PedCheckMode : ASL : TAX - JSR (.pedestal_modes,X) - PLX + LDA.b #$02 : JSL CheckConditionPass : BCS .return + PHX : PHP + LDA.b GameMode : CMP.b #$0E : BEQ + + REP #$30 + LDX.w #$0004 : LDA.l GoalConditionTable, X : TAX + LDA.l $B00000, X : CMP.w #$FF81 : BEQ + + SEP #$30 + LDA.b #$97 : LDY.b #$01 + JSL Sprite_ShowMessageUnconditional + + + PLP : PLX +.return RTL - - .pedestal_modes - dw .vanilla - dw .arbitrary_cmp - - .vanilla - LDA.l PendantsField - AND.b #$07 : CMP.b #$07 : BNE ..nopull - SEC - RTS - ..nopull - CLC - RTS - - .arbitrary_cmp - REP #$30 - LDA.l PedPullAddress : TAX - LDA.l $7E0000,X - CMP.l PedPullTarget - SEP #$30 - RTS diff --git a/tables.asm b/tables.asm index e29bbfe..40af715 100644 --- a/tables.asm +++ b/tables.asm @@ -892,43 +892,84 @@ org $B08196 ; PC 0x180196-0x180197 TotalItemCount: ; Total item count for HUD. Only counts items that use "item get" animation. dw $00D8 ; 216 -org $B08198 ; PC 0x180198-0x1801A9 -GanonsTowerOpenAddress: ; 0x180198-0x180199 -dw CrystalCounter ; Target address for GT open check -GanonsTowerOpenTarget: ; 0x18019A-0x18019B -dw $0007 ; Target amount for GT open modes to compare -GanonsTowerOpenMode: ; 0x18019C-0x18019D -dw $0001 ; $00 = Vanilla | $01 = Compare target with address -PedPullAddress: ; 0x18019E-0x18019F -dw PendantCounter ; Target address for ped pull check -PedPullTarget: ; 0x1801A0-0x1801A1 -dw $0003 ; Target amount for ped pull modes to check -PedCheckMode: ; 0x1801A2-0x1801A3 -dw $0000 ; $00 = vanilla | $01 = Compare address to target value -GanonVulnerableAddress: ; 0x1801A4-0x1801A5 -dw CrystalCounter ; Target address for ped pull check -GanonVulnerableTarget: ; 0x1801A6-0x1801A7 -dw $0007 ; Target amount for Ganon vulnerability modes to compare -GanonVulnerableMode: ; 0x1801A8-0x1801A9 -dw $0000 ; #$00 = Off (default) - ; #$01 = On - ; #$02 = Require All Dungeons - ; #$03 = Require "GanonVulnerableTarget" Crystals and Aga2 - ; #$04 = Require "GanonVulnerableTarget" Crystals - ; #$05 = Require "GoalItemRequirement" Goal Items - ; #$06 = Light Speed - ; #$07 = Require All Crystals and Crystal Bosses - ; #$08 = Require All Crystal Bosses only - ; #$09 = Require All Dungeons No Agahnim - ; #$0A = Require 100% Item Collection - ; #$0B = Require 100% Item Collection and All Dungeons -GanonsTowerOpenGfx: ; 0x1801AA-0x1801AB -dw $0000 ; Gfx used for GT open animation, similar to StandingItemGraphicsOffsets -GanonsTowerOpenPalette: ; 0x1801AC -db $00 ; Palette for GanonsTowerOpenGfx -;VHPP CCC O +org $B08198 ; PC 0x180198-0x1801D7 (variable tables, $FF terminated) +GoalConditionTable: + dw GanonsTowerOpen + dw GanonVulnerable + dw PedPull + dw MurahdahlaComplete + +GanonsTowerOpen: + db $82 ; Crystal Count >= Default 7 + db $FF +GanonVulnerable: + db $82 ; Crystal Count >= Default 7 + db $07 ; Agahnim 2 defeated + db $FF +PedPull: + db $81 ; Pendant Count >= Default 3 + db $FF +MurahdahlaComplete: + db $88 ; Triforce Pieces >= Default Goal + db $FF +; These are lists of conditions to check for various goals, +; each terminated by #$FF. +; First byte is condition type, with most significant bit +; indicating the default value should be used, else the +; following byte/s indicate the custom target value. +; ----------------------------------------------------------- +; Condition Types: +; #$00 = Always Invulnerable +; #$01 = Require Pendants - Default is 3 +; #$02 = Require Crystals - Default is 7 +; #$03 = Require Pendant Bosses - Default is 3 +; #$04 = Require Crystal Bosses - Default is 7 +; #$05 = Require Prize Bosses - Default is 10 +; #$06 = Require Agahnim 1 +; #$07 = Require Agahnim 2 +; #$08 = Require Goal Items (ie. Triforce Pieces) - Default is value at GoalItemRequirement +; #$09 = Require Item Collection - Default is Max Collection Rate +; #$0A = Require Custom Goal +; ----------------------------------------------------------- +; For Custom Goal, one byte indicates the bitfield options, +; followed by two bytes for the target address, followed +; by one or two bytes for the custom target value. +; Options bitfield: +; ---b accc +; b - bank flag - 0 = $7E - 1 = $7F +; a - addressing mode - 0 = 8-bit - 1 = 16-bit +; c - comparison mode +; 0 = minimum (>=) +; 1 = exact (==) +; 2 = bitfield any +; 3 = bitfield match +; 4 = count bits set in bitfield +; 5-7 = reserved ;-------------------------------------------------------------------------------- -; 0x1801AD - 0x1801FF (unused) +org $B081D8 ; PC 0x1801D8 - 0x1801FE +GanonsTowerOpenGfx: ; 0x1801D8-0x1801E5 +dw $0000 ; Gfx used for GT open animation, similar to StandingItemGraphicsOffsets +dw $0000, $0000, $0000, $0000, $0000, $0000 +GanonsTowerOpenPalette: ; 0x1801E6-0x1801EC +db $00 ; Palette for GanonsTowerOpenGfx +db $00, $00, $00, $00, $00, $00 +; VHPP CCCO (VertFlip, HorizFlip, Priority, ColorPalette, OAM Sheet) +PedPullGfx: ; 0x1801ED-0x1801F2 +dw $0000 ; Gfx used for ped pull animation, similar to StandingItemGraphicsOffsets +dw $0000, $0000 +PedPullPalette: ; 0x1801F3-0x1801F5 +db $00 ; Palette for PedPullGfx +db $00, $00 +; VHPP CCCO (VertFlip, HorizFlip, Priority, ColorPalette, OAM Sheet) +MurahdahlaGfx: ; 0x1801F6-0x1801FB +dw $0000 ; Gfx used for ped pull animation, similar to StandingItemGraphicsOffsets +dw $0000, $0000 +MurahdahlaPalette: ; 0x1801FC-0x1801FE +db $00 ; Palette for MurahdahlaGfx +db $00, $00 +; VHPP CCCO (VertFlip, HorizFlip, Priority, ColorPalette, OAM Sheet) +;-------------------------------------------------------------------------------- +; 0x1801FF (unused) ;================================================================================ org $B08200 ; PC 0x180200 - 0x18020B RedClockAmount: diff --git a/utilities.asm b/utilities.asm index 5a1414a..7e4b71f 100644 --- a/utilities.asm +++ b/utilities.asm @@ -520,3 +520,20 @@ AuxPaletteCheck: REP #$30 PLX RTS + +CountBits: +; In: A - value to count bits set in +; Out: A - number of bits set +; Flexible to use with 8 or 16-bit mode + PHX : TAX + PHY : PHP + SEP #$20 + LDA.b 1,S : BIT.b #$20 : BNE + + PLP : TXA : LDX.w #$000F : LDY.w #$0000 + BRA ++ + + PLP : TXA : LDX.b #$07 : LDY.b #$00 + ++ - LSR : BCC + + INY + + DEX : BPL - + TYA : PLY : PLX +RTL