diff --git a/2026items.bin b/2026items.bin new file mode 100644 index 00000000..1e78f4ce Binary files /dev/null and b/2026items.bin differ diff --git a/2026items.gfx b/2026items.gfx new file mode 100644 index 00000000..a62c2105 Binary files /dev/null and b/2026items.gfx differ diff --git a/LTTP_RND_GeneralBugfixes.asm b/LTTP_RND_GeneralBugfixes.asm index 762e743f..3eeaea4e 100644 --- a/LTTP_RND_GeneralBugfixes.asm +++ b/LTTP_RND_GeneralBugfixes.asm @@ -116,7 +116,7 @@ incsrc itemdowngrade.asm incsrc bugfixes.asm incsrc darkworldspawn.asm incsrc lampmantlecone.asm -incsrc floodgatesoftlock.asm +incsrc floodgates.asm incsrc heartpieces.asm incsrc npcitems.asm incsrc utilities.asm @@ -138,7 +138,6 @@ incsrc maidencrystals.asm incsrc zoraking.asm incsrc catfish.asm incsrc flute.asm -incsrc dungeondrops.asm incsrc halfmagicbat.asm incsrc newitems.asm incsrc mantle.asm @@ -148,10 +147,6 @@ incsrc map.asm incsrc msu.asm incsrc dialog.asm incsrc entrances.asm -incsrc clock.asm -incsrc accessability.asm -incsrc heartbeep.asm -incsrc capacityupgrades.asm print "End of Coda Bank $20: ", pc warnpc $A18000 @@ -179,6 +174,52 @@ org $A1FF00 ; static mapping area incsrc init.asm org $A48000 ; code bank - PUT NEW CODE HERE + +; Current-dungeon key rings picked up from floor objects should add the +; dungeon's key quantity and update HUD/stats without the normal item fanfare. +Link_ReceiveItem_HandleCurrentDungeonKeyRing: + CPY.b #$CE : BNE .normalReceive + LDA !MULTIWORLD_ITEM_PLAYER_ID : BNE .normalReceive + JSL.l BossPrizeReceiveContextMatches : BCS .normalReceive + LDA $02E9 : CMP.b #$03 : BNE .normalReceive + JSL.l ReceiveCurrentDungeonKeyRingQuiet + JSL.l Player_HaltDashAttackLong + CLC + PLB + RTL + + .normalReceive + LDA $02E9 + BEQ .fromTextOrObject + CMP.b #$03 : BEQ .fromTextOrObject + .fromChestOrSprite + JML $0799F2 + .fromTextOrObject + JML $0799C8 + +; Grant floor-object current-dungeon key rings directly because they should not +; use the normal hold-up animation. +ReceiveCurrentDungeonKeyRingQuiet: + PHX : PHY + LDA $040C : CMP.b #$FF : BEQ .cleanup + LSR : TAX + LDA.l KeyRingQuantities, X : BEQ .cleanup + CLC : ADC $7EF36F : STA $7EF36F + JSL.l UpdateKeys + LDY.b #$CE + JSL.l FullInventoryExternal + JSL.l CountChestKeyLong + JSL.l HUD_RebuildLong + .cleanup + STZ $02D8 : STZ $02D9 : STZ $02E9 + STZ $02DA : STZ $037B : STZ $02E4 + PLY : PLX +RTL + +incsrc clock.asm +incsrc accessability.asm +incsrc heartbeep.asm +incsrc capacityupgrades.asm incsrc timer.asm incsrc doorframefixes.asm incsrc music.asm @@ -217,6 +258,7 @@ incsrc darkroomitems.asm incsrc multiworld.asm incsrc terrorpin.asm incsrc RandSprite.asm +incsrc dungeondrops.asm print "End of Coda Bank $24: ", pc warnpc $A58000 @@ -264,8 +306,10 @@ warnpc $31A800 org $31A800 GFX_New_Items: -incbin newitems.gfx +;incbin newitems.gfx ;incbin eventitems.gfx ; *EVENT* +; 2026 item graphics include the key ring art +incbin 2026items.gfx warnpc $31B000 org $31B000 diff --git a/c2e3e.bin b/c2e3e.bin index 9675a12a..98f66f96 100644 Binary files a/c2e3e.bin and b/c2e3e.bin differ diff --git a/c2e3e.gfx b/c2e3e.gfx index 1298f28e..18f9de64 100644 Binary files a/c2e3e.gfx and b/c2e3e.gfx differ diff --git a/contrib.asm b/contrib.asm index 0fbf3540..b4f80116 100644 --- a/contrib.asm +++ b/contrib.asm @@ -109,6 +109,7 @@ Sprite_ShowSolicitedMessageIfPlayerFacing_Alt: .SahasrahlaDialogs REP #$20 : LDA.l MapReveal_Sahasrahla : ORA !MAP_OVERLAY : STA !MAP_OVERLAY : SEP #$20 + JSL.l TrackHintRead JSL DialogSahasrahla : BRA .SayNothing .BombShopGuyDialog diff --git a/damage_table.bin b/damage_table.bin index fe0d0465..32cee7e8 100644 Binary files a/damage_table.bin and b/damage_table.bin differ diff --git a/darkroomitems.asm b/darkroomitems.asm index a2d14741..ffd3c32d 100644 --- a/darkroomitems.asm +++ b/darkroomitems.asm @@ -1,17 +1,25 @@ -CheckReceivedItemPropertiesBeforeLoad: - LDA $A0 : BEQ .normalCode - LDA $7EC005 : BNE .lightOff - .normalCode - LDA.l AddReceivedItemExpanded_properties, X ;Restore Rando Code - RTL - -.lightOff - PHX : PHY : PHB - LDA.l AddReceivedItemExpanded_properties, X ; get palette - - REP #$30 - AND #$0007 ; mask out palette - ASL #5 ; multiply by 32 +; Load received-item palette properties, using the boss-prize display item when +; the receive animation is showing a substituted/progressive prize. +CheckReceivedItemPropertiesBeforeLoad: + PHX + LDA $7F504C : BEQ + + LDA $7F504D : TAX + + + LDA $A0 : BEQ .normalCode + LDA $7EC005 : BNE .lightOff + .normalCode + JSR .loadProperty + PLX + CMP.b #$00 ; preserve vanilla BPL behavior by setting flags from the loaded property, not restored X + RTL + + .lightOff + PHY : PHB + JSR .loadProperty + + REP #$30 + AND #$0007 ; mask out palette + ASL #5 ; multiply by 32 ADC #$C610 ; offset to latter half TAX ; give to destination @@ -19,9 +27,53 @@ CheckReceivedItemPropertiesBeforeLoad: LDA #$000F ; 16 bytes MVN $7E, $7E ; move palette - - SEP #$30 - PLB : PLY : PLX - INC $15 - LDA #$00 - RTL + + SEP #$30 + PLB : PLY : PLX + INC $15 + LDA #$00 + RTL + + ; Swords and shields use inventory-dependent palette properties, so boss + ; prizes need the resolved display item rather than the original item ID. + .loadProperty + LDA $7F504C : BEQ .normalProperty + TXA : CMP.b #$49 : BEQ .masterSword + CMP.b #$01 : BEQ .masterSword + CMP.b #$50 : BEQ .masterSword + CMP.b #$02 : BEQ .temperedSword + CMP.b #$03 : BEQ .goldenSword + CMP.b #$04 : BCC .normalProperty + CMP.b #$07 : BCC .shield + + .normalProperty + LDA.l AddReceivedItemExpanded_properties, X ; Restore Rando Code + RTS + + .masterSword + LDA.b #$02 + RTS + + .temperedSword + LDA.b #$01 + RTS + + .goldenSword + LDA.b #$04 + RTS + + .shield + LDA.b #$05 + RTS + +; Boss-prize crystals are spawned as ordinary receive-item objects, but still +; need the crystal-specific fanfare/state path once they are collected. +CrystalItemBehaviorCheck: + LDA $0C5E, X : CMP.b #$20 : BNE .ordinaryItem + LDA $0C54, X : CMP.b #$03 : BEQ .bossPrizeCrystal + +.ordinaryItem + JML.l CrystalFanfareContinue + +.bossPrizeCrystal + JML.l CrystalSpecialBehaviorContinue diff --git a/dialog.asm b/dialog.asm index 3216fe09..49d8b8f4 100644 --- a/dialog.asm +++ b/dialog.asm @@ -177,7 +177,7 @@ FreeDungeonItemNotice: + : LDA.l FreeItemText : AND.b #$08 : CMP.b #$08 : BNE + ; show message for dungeon big key LDA !ITEM_TEMPORARY : AND.b #$F0 : CMP.b #$90 : BNE + ; big key of... %CopyDialog(Notice_BigKeyOf) - BRA .dungeon + BRL .dungeon + : LDA.l FreeItemText : AND.b #$01 : CMP.b #$01 : BNE + ; show message for dungeon small key LDA !ITEM_TEMPORARY : AND.b #$F0 : CMP.b #$A0 : BNE + ; small key of... LDA !ITEM_TEMPORARY : CMP.b #$AF : BNE ++ : BRL .skip : ++ @@ -185,6 +185,16 @@ FreeDungeonItemNotice: PLA : AND.b #$0F : STA $7F5020 : LDA.b #$0F : !SUB $7F5020 : PHA LDA #$01 : STA $7F5010 ; set up a flip for small keys BRA .dungeon + ; Key rings share the small-key notice flag but use their own text prefix. + + : LDA.l FreeItemText : AND.b #$01 : CMP.b #$01 : BNE + ; show message for dungeon key ring + LDA !ITEM_TEMPORARY : AND.b #$F0 : CMP.b #$C0 : BNE + ; key ring of... + LDA !ITEM_TEMPORARY : CMP.b #$CE : !BLT .keyRingNotice + BRL .skip + .keyRingNotice + %CopyDialog(Notice_KeyRingOf) + PLA : AND.b #$0F : STA $7F5020 : LDA.b #$0F : !SUB $7F5020 : PHA + LDA #$01 : STA $7F5010 ; set up a flip for key rings + BRA .dungeon + BRL .skip ; it's not something we are going to give a notice for @@ -398,20 +408,71 @@ DialogBombShopGuy: JSL.l Sprite_ShowMessageUnconditional RTL ;-------------------------------------------------------------------------------- +!HINT_READ_FLAGS = "$7EF4F1" +;-------------------------------------------------------------------------------- +TrackHintRead: + PHX : PHP + REP #$30 + LDA.w $1CF0 + LDX.w #$0000 + + - + CMP.l HintReadTable, X : BEQ .match + LDA.l HintReadTable, X : CMP.w #$FFFF : BEQ .done + INX #4 + LDA.w $1CF0 + BRA - + + .match + LDA.l HintReadTable+2, X + PHA + AND.w #$00FF + TAX + PLA + XBA + SEP #$20 + ORA.l !HINT_READ_FLAGS, X + STA.l !HINT_READ_FLAGS, X + .done + PLP : PLX +RTL +;-------------------------------------------------------------------------------- +Main_ShowTextMessage_TrackHintRead: + ; Are we in text mode? If so then end the routine. + LDA $10 : CMP.b #$0E : BEQ .already_in_text_mode + + JSL.l TrackHintRead + + STZ $0223 ; Otherwise set it so we are in text mode. + STZ $1CD8 ; Initialize the step in the submodule + + ; Go to text display mode (as opposed to maps, etc) + LDA.b #$02 : STA $11 + + ; Store the current module in the temporary location. + LDA $10 : STA $010C + + ; Switch the main module ($10) to text mode. + LDA.b #$0E : STA $10 + +.already_in_text_mode +RTL +;-------------------------------------------------------------------------------- Main_ShowTextMessage_Alt: ; Are we in text mode? If so then end the routine. LDA $10 : CMP.b #$0E : BEQ .already_in_text_mode Sprite_ShowMessageMinimal_Alt: STZ $11 - PHX : PHY + PHX : PHY : PHP + SEP #$20 LDA.b $00 : PHA LDA.b $01 : PHA LDA.b $02 : PHA LDA.b #$1C : STA.b $02 REP #$30 - LDA.w $1CF0 : ASL : TAX + LDA.w $1CF0 : ASL : CLC : ADC.w $1CF0 : TAX LDA.l $7f71c0, X STA.b $00 SEP #$30 @@ -427,6 +488,8 @@ Sprite_ShowMessageMinimal_Alt: BRL .end + + JSL.l TrackHintRead + STZ $0223 ; Otherwise set it so we are in text mode. STZ $1CD8 ; Initialize the step in the submodule @@ -442,7 +505,7 @@ Sprite_ShowMessageMinimal_Alt: PLA : STA.b $02 PLA : STA.b $01 PLA : STA.b $00 - PLY : PLX + PLP : PLY : PLX Main_ShowTextMessage_Alt_already_in_text_mode: RTL diff --git a/dungeondrops.asm b/dungeondrops.asm index ac40e11d..4ea75023 100644 --- a/dungeondrops.asm +++ b/dungeondrops.asm @@ -1,9 +1,981 @@ ;================================================================================ ; Dungeon & Boss Drop Fixes ;-------------------------------------------------------------------------------- +!BOSS_PRIZE_ACTIVE = "$7F5047" +!BOSS_PRIZE_ROOM = "$7F5048" +!BOSS_PRIZE_SLOT = "$7F504A" +!BOSS_PRIZE_NARROW_SHADOW = "$7F504B" +!BOSS_PRIZE_DRAW_ACTIVE = "$7F504C" +!BOSS_PRIZE_DISPLAY_ITEM = "$7F504D" +!BOSS_PRIZE_X_OFFSET_APPLIED = "$7F504E" +!ITEM_BUSY = "$7F5091" + +; Boss prize shuffle tracks one spawned receive-item ancilla so the normal +; receive-item draw and collection code can identify the shuffled prize path. DropSafeDungeon: LDA $040C : CMP #$08 : BEQ + LDA $01C6FC, X : JSL Sprite_SpawnFallingItem + RTL -;-------------------------------------------------------------------------------- \ No newline at end of file +;-------------------------------------------------------------------------------- +; Replace the vanilla boss room tag handler. In shuffle mode this spawns the +; configured item as a falling receive-item object instead of the vanilla prize. +BossPrizeRoomTag: + LDA BossPrizeShuffle : BEQ .vanilla + JSL.l CheckIfBossRoom : BCC .vanilla + + LDA $0403 : AND.b #$80 : BEQ .heartContainerStillExists + JSL.l BossPrizeDungeonCompletionMatches : BCS .criticalItemAlreadyObtained + LDA !ITEM_BUSY : BNE .heartContainerStillExists + JSL.l BossPrizeSpawnReady : BCC .heartContainerStillExists + + LDA $0E : PHA + JSL.l LoadBossPrizeRoomValue + JSL.l SpawnBossPrizeFallingItem : BCC + + PLA : STA $0E + BRA .heartContainerStillExists + + + JSL.l SetBossPrizeContext + PLA : STA $0E + BRA .clearTagFlag + +.vanilla + LDA $0403 : AND.b #$80 : BEQ .heartContainerStillExists + + LDA $040C : LSR A : TAX + LDA.l CrystalPendantFlags_2, X : BNE .inDarkWorld + LDA $7EF374 : AND.l CrystalPendantFlags, X : BNE .criticalItemAlreadyObtained + BRA .giveCriticalItem + +.inDarkWorld + LDA $7EF37A : AND.l CrystalPendantFlags, X : BNE .criticalItemAlreadyObtained + +.giveCriticalItem + LDA.b #$80 : STA $04C2 + LDA $0E : PHA + LDA $040C : LSR A : TAX + JSL.l DropSafeDungeon + PLA : STA $0E + BRA .clearTagFlag + +.criticalItemAlreadyObtained + JSL.l ClearBossPrizeContext + +.clearTagFlag + LDX $0E + STZ $AE, X + +.heartContainerStillExists +RTL +;-------------------------------------------------------------------------------- +; Mark the current room as owning an active shuffled boss prize. +SetBossPrizeContext: + LDA.b #$01 : STA !BOSS_PRIZE_ACTIVE + PHP + REP #$20 + LDA $A0 : STA !BOSS_PRIZE_ROOM + PLP +RTL +;-------------------------------------------------------------------------------- +; Clear all state used to recognize and draw the shuffled boss prize ancilla. +ClearBossPrizeContext: + LDA.b #$00 + STA !BOSS_PRIZE_ACTIVE + STA !BOSS_PRIZE_ROOM + STA !BOSS_PRIZE_ROOM+1 + LDA.b #$FF : STA !BOSS_PRIZE_SLOT + LDA.b #$00 + STA !BOSS_PRIZE_NARROW_SHADOW + STA !BOSS_PRIZE_DRAW_ACTIVE + STA !BOSS_PRIZE_DISPLAY_ITEM + STA !BOSS_PRIZE_X_OFFSET_APPLIED +RTL +;-------------------------------------------------------------------------------- +; Spawn the shuffled prize with vanilla receive-item behavior and preload the +; display graphics for the resolved item that Link will actually show. +SpawnBossPrizeFallingItem: + PHA + JSL.l BossPrizeResolveItem + JSL.l BossPrizeResolvePrizeLocationDisplayItem + STA !BOSS_PRIZE_DISPLAY_ITEM + PLA : PHA + + LDY.b #$04 + LDA.b #$29 + JSL.l AddAncillaLong : BCC .spawned + PLA + SEC + RTL + + .spawned + PLA : STA $0C5E, X + LDA !BOSS_PRIZE_DISPLAY_ITEM : TAY + + ; Swords and shields need their freestanding/decompressed graphics, while + ; other items can use the normal received-item graphics index directly. + PHX : PHB + LDA.b #AddReceivedItemExpanded_item_graphics_indices>>16 : PHA : PLB + LDA.w AddReceivedItemExpanded_item_graphics_indices, Y : STA $72 + CMP.b #$FF : BEQ .invalidItem + TYA + CMP.b #$49 : BEQ .freestandingSwordItem + CMP.b #$01 : BEQ .freestandingSwordItem + CMP.b #$50 : BEQ .freestandingSwordItem + CMP.b #$02 : BEQ .freestandingSwordItem + CMP.b #$03 : BEQ .freestandingSwordItem + LDA $72 + CMP.b #$20 : BEQ .shieldItem + CMP.b #$2D : BEQ .shieldItem + CMP.b #$2E : BNE .getItemTiles + + .shieldItem + TYA + JSL.l GetSpriteID + STA $72 + JSL.l BossPrizeDecompResolvedShieldGfx + LDA $72 + BRA .getItemTiles + + .freestandingSwordItem + TYA + JSL.l GetSpriteID + STA $72 + BRA .getItemTiles + + .invalidItem + LDA.b #$00 + + .getItemTiles + JSL.l GetAnimatedSpriteTile_variable + + LDA $72 : CMP.b #$06 : BEQ .swordItem + CMP.b #$18 : BNE .notSwordItem + .swordItem + JSL.l BossPrizeDecompResolvedSwordGfx + .notSwordItem + PLB : PLX + + LDA.b #$D0 : STA $0294, X + STZ $0C22, X + STZ $0C2C, X + STZ $0C54, X + LDA.b #$80 : STA $029E, X + LDA.b #$09 : STA $03B1, X + STZ $03A4, X + LDA.b #$05 : STA $0BF0, X + STZ $039F, X + STZ $0385, X + STZ $0394, X + + LDA $0C5E, X : STA $02D8 + + LDA $040C : CMP.b #$14 : BNE .normalCoords + LDA $21 : AND.b #$FE : INC A : STA $01 + STZ $00 + LDA $23 : AND.b #$FE : INC A : STA $03 + STZ $02 + BRA .setCoords + + .normalCoords + REP #$20 + LDA $E8 : !ADD.w #$0078 : STA $00 + LDA $E2 : !ADD.w #$0078 : STA $02 + SEP #$20 + + .setCoords + LDA $00 : STA $0BFA, X + LDA $01 : STA $0C0E, X + LDA $02 : STA $0C04, X + LDA $03 : STA $0C18, X + TXA : STA !BOSS_PRIZE_SLOT + CLC +RTL +;-------------------------------------------------------------------------------- +; Temporarily present the resolved sword level to the vanilla sword decompressor. +BossPrizeDecompResolvedSwordGfx: + PHP + SEP #$20 + LDA $7EF359 : PHA + LDA !BOSS_PRIZE_DISPLAY_ITEM + CMP.b #$49 : BEQ .fighterSword + CMP.b #$01 : BEQ .masterSword + CMP.b #$50 : BEQ .masterSword + CMP.b #$02 : BEQ .temperedSword + LDA.b #$04 : BRA .loadSword + +.fighterSword + LDA.b #$01 : BRA .loadSword + +.masterSword + LDA.b #$02 : BRA .loadSword + +.temperedSword + LDA.b #$03 + +.loadSword + STA $7EF359 + JSL.l DecompSwordGfx + JSL.l Palette_Sword + PLA : STA $7EF359 + PLP +RTL +;-------------------------------------------------------------------------------- +; Temporarily present the resolved shield level to the vanilla shield decompressor. +BossPrizeDecompResolvedShieldGfx: + PHP + SEP #$20 + LDA $7EF35A : PHA + LDA !BOSS_PRIZE_DISPLAY_ITEM + CMP.b #$04 : BEQ .fighterShield + CMP.b #$05 : BEQ .redShield + LDA.b #$03 : BRA .loadShield + +.fighterShield + LDA.b #$01 : BRA .loadShield + +.redShield + LDA.b #$02 + +.loadShield + STA $7EF35A + JSL.l DecompShieldGfx + JSL.l Palette_Shield + PLA : STA $7EF35A + PLP +RTL +;-------------------------------------------------------------------------------- +; True when the current room is the room that spawned the active shuffled prize. +BossPrizeContextMatchesRoom: + LDA BossPrizeShuffle : BEQ .mismatch + LDA !BOSS_PRIZE_ACTIVE : BEQ .mismatch + LDA $1B : BEQ .mismatch + + PHP + REP #$20 + LDA !BOSS_PRIZE_ROOM : CMP $A0 : BNE .notCurrentRoom + PLP + SEC + RTL + +.notCurrentRoom + PLP + +.mismatch + CLC +RTL +;-------------------------------------------------------------------------------- +; True when X is the receive-item ancilla slot used by the shuffled prize. +BossPrizeContextMatchesSlot: + LDA !BOSS_PRIZE_SLOT : CMP.b #$FF : BEQ .mismatch + TXA : CMP !BOSS_PRIZE_SLOT : BNE .mismatch + SEC + RTL + +.mismatch + CLC +RTL +;-------------------------------------------------------------------------------- +; True while Link is receiving the tracked shuffled boss prize. +BossPrizeReceiveContextMatches: + LDA $02E9 : CMP.b #$03 : BNE .mismatch + JSL.l BossPrizeContextMatchesRoom : BCC .mismatch + JSL.l BossPrizeContextMatchesSlot : BCC .mismatch + SEC + RTL + +.mismatch + CLC +RTL +;-------------------------------------------------------------------------------- +; True while the tracked boss-prize ancilla is still being animated. +BossPrizeObjectContextMatches: + LDA $0C54, X : CMP.b #$03 : BNE .mismatch + JSL.l BossPrizeContextMatchesRoom : BCC .mismatch + JSL.l BossPrizeContextMatchesSlot : BCC .mismatch + SEC +RTL + +.mismatch + CLC +RTL +;-------------------------------------------------------------------------------- +; Every fourth received Piece of Heart obtained is converted into a second +; receive-item animation, then the original ancilla is cleared without entering +; the ordinary object-finished path. If that original item was a shuffled boss +; prize, move the boss-prize context to the new completion-heart ancilla so the +; existing finish hook can warp after the full-heart animation ends. +BossPrizeRetargetHeartPieceCompletion: + JSL.l BossPrizeObjectContextMatches : BCC .done + PHX + LDX.b #$09 + +.nextAncilla + LDA $0C4A, X : CMP.b #$22 : BNE .next + LDA $0C5E, X : CMP.b #$26 : BNE .next + LDA.b #$03 : STA $0C54, X + TXA : STA !BOSS_PRIZE_SLOT + BRA .found + +.next + DEX : BPL .nextAncilla + +.found + PLX + +.done +RTL +;-------------------------------------------------------------------------------- +; Replacement for the vanilla Piece of Heart completion branch in receive-item +; object cleanup. This preserves the normal completion-heart animation and adds +; the boss-prize retargeting above before clearing the original receive object. +BossPrizeHeartPieceCompletionBranch: + PHX + LDY.b #$26 + JSL.l Link_ReceiveItem + PLX + JSL.l BossPrizeRetargetHeartPieceCompletion + STZ $0C4A, X + STZ $0FC1 +RTL +;-------------------------------------------------------------------------------- +; Mark the location check and apply the configured multiworld recipient before +; the item is granted. +BossPrizeApplyItemPlayer: + JSL.l BossPrizeReceiveContextMatches : BCC .done + JSL.l MarkBossPrizeDungeonCompletion + JSL.l BossPrizeGetPlayer : STA !MULTIWORLD_ITEM_PLAYER_ID + .done +RTL +;-------------------------------------------------------------------------------- +; Non-pendant shuffled prizes still need the victory fanfare before warping out. +BossPrizeItemNeedsVictoryFanfare: + JSL.l BossPrizeObjectContextMatches : BCC .noFanfare + SEC + RTL + +.noFanfare + CLC +RTL +;-------------------------------------------------------------------------------- +; Extend the pendant music wait to any shuffled boss prize that uses the victory fanfare. +BossPrizePendantWaitCheck: + LDA $0C5E, X : CMP.b #$37 : BEQ .waitForMusic + CMP.b #$38 : BEQ .waitForMusic + CMP.b #$39 : BEQ .waitForMusic + + JSL.l BossPrizeItemNeedsVictoryFanfare : BCC .dontWaitForMusic + +.waitForMusic + JML.l PendantFanfareWait + +.dontWaitForMusic + JML.l PendantFanfareDone +;-------------------------------------------------------------------------------- +; Finish a shuffled boss prize by showing deferred text if needed, then routing +; to the dungeon-exit path. +HandleBossPrizeObjectFinished: + JSL.l BossPrizeObjectContextMatches : BCC .normalObjectFinished + JSL.l ClearBossPrizeContext + + LDA $7F509F : BEQ + + LDA.b #$04 : STA $0C54, X + JSL.l HideBossPrizeItemVisual + STZ $1CF0 : STZ $1CF1 + JSL.l Main_ShowTextMessage_Alt + LDA.b #$00 : STA $7F509F + JML.l Ancilla_ReceiveItem_return + + + + STZ $0C4A, X + STZ $0FC1 + LDA $0C54, X : PHA : PHX + JSL.l PrepDungeonExit + PLX : PLA + + JML.l Ancilla_ReceiveItem_objectFinished+44 + +.normalObjectFinished + STZ $0C4A, X + STZ $0FC1 + JML.l Ancilla_ReceiveItem_objectFinished+6 +;-------------------------------------------------------------------------------- +; Move the item object offscreen while deferred text owns the receive-item state. +HideBossPrizeItemVisual: + PHP + REP #$20 + LDA $E8 : SEC : SBC.w #$0018 + SEP #$20 + STA $0BFA, X + XBA + STA $0C0E, X + + REP #$20 + LDA $E2 : SEC : SBC.w #$0018 + SEP #$20 + STA $0C04, X + XBA + STA $0C18, X + PLP +RTL +;-------------------------------------------------------------------------------- +; Record shuffled boss-prize completion separately from vanilla pendant/crystal bits. +MarkBossPrizeDungeonCompletion: + JSR BossPrizeRoomCompletionMask : BCC .done + CPX.b #$01 : BEQ .highByte + ORA $7EF46B : STA $7EF46B + BRA .done + .highByte + ORA $7EF46C : STA $7EF46C +.done +RTL + +; Check the shuffled boss-prize completion bit for the current boss-prize room. +BossPrizeDungeonCompletionMatches: + JSR BossPrizeRoomCompletionMask : BCC .mismatch + CPX.b #$01 : BEQ .highByte + AND.l $7EF46B : BNE .match + BRA .mismatch + .highByte + AND.l $7EF46C : BNE .match + +.mismatch + CLC +RTL + +.match + SEC +RTL + +; Map the current boss room to the boss-prize completion byte and mask used by +; the client. Carry set on match. X=0 means $7EF46B; X=1 means $7EF46C. +BossPrizeRoomCompletionMask: + REP #$20 ; set 16-bit accumulator + LDA $A0 + CMP.w #200 : BNE + + SEP #$20 : LDA.b #$04 : LDX.b #$00 : SEC : RTS + + CMP.w #51 : BNE + + SEP #$20 : LDA.b #$08 : LDX.b #$00 : SEC : RTS + + CMP.w #7 : BNE + + SEP #$20 : LDA.b #$04 : LDX.b #$01 : SEC : RTS + + CMP.w #90 : BNE + + SEP #$20 : LDA.b #$40 : LDX.b #$00 : SEC : RTS + + CMP.w #6 : BNE + + SEP #$20 : LDA.b #$20 : LDX.b #$00 : SEC : RTS + + CMP.w #41 : BNE + + SEP #$20 : LDA.b #$01 : LDX.b #$01 : SEC : RTS + + CMP.w #172 : BNE + + SEP #$20 : LDA.b #$08 : LDX.b #$01 : SEC : RTS + + CMP.w #222 : BNE + + SEP #$20 : LDA.b #$02 : LDX.b #$01 : SEC : RTS + + CMP.w #144 : BNE + + SEP #$20 : LDA.b #$80 : LDX.b #$00 : SEC : RTS + + CMP.w #164 : BNE + + SEP #$20 : LDA.b #$10 : LDX.b #$01 : SEC : RTS + + + SEP #$20 + CLC +RTS + +; Tag routine 0x16 "clear level to open doors". In shuffled-prize mode, +; use the boss-prize completion bits instead of vanilla pendant/crystal bits. +BossPrizeClearLevelToOpenDoors: + LDA BossPrizeShuffle : BEQ .vanilla + JSL.l CheckIfBossRoom : BCC .vanilla + JSL.l BossPrizeDungeonCompletionMatches : BCS .openDoors + BRA .dontHaveGoalItem + +.vanilla + LDA $040C : LSR A : TAX + LDA.l CrystalPendantFlags_2, X : BNE .inDarkWorld + LDA $7EF374 : AND.l CrystalPendantFlags, X : BNE .openDoors + BRA .dontHaveGoalItem + +.inDarkWorld + LDA $7EF37A : AND.l CrystalPendantFlags, X : BEQ .dontHaveGoalItem + +.openDoors + REP #$30 + STZ $0468 + STZ $068E + STZ $0690 + SEP #$30 + LDA.b #$05 : STA $11 + LDX $0E + STZ $AE, X + +.dontHaveGoalItem + SEP #$30 +RTL + +; Convert a dungeon index into a single-bit mask for completion storage. +BossPrizeValueShift: + PHX + TAX : LDA.b #$01 + - + CPX #$00 : BEQ + + ASL + DEX + BRA - + + + PLX +RTS +;-------------------------------------------------------------------------------- +; Resolve progressive/substituted local prizes for display before the item is granted. +BossPrizeResolveItem: + PHA + JSL.l BossPrizeGetPlayer : CMP.b #$00 : BEQ .local + +.remote + PLA + RTL + +.local + PLA + JSL.l AttemptItemSubstitutionLong + CMP.b #$4E : BNE .notProgressiveMagic + +.progressiveMagic + LDA $7EF37B : BNE + + RTL + + + LDA.b #$4F + RTL + +.notProgressiveMagic + CMP.b #$5E : BNE .notProgressiveSword + +.progressiveSword + LDA $7EF359 : CMP.l ProgressiveSwordLimit : !BLT + + LDA.l ProgressiveSwordReplacement : RTL + + + LDA $7EF359 : CMP.b #$FF : BNE + + LDA.b #$49 : RTL + + : CMP.b #$00 : BNE + + LDA.b #$49 : RTL + + : CMP.b #$01 : BNE + + LDA.b #$50 : RTL + + : CMP.b #$02 : BNE + + LDA.b #$02 : RTL + + LDA.b #$03 : RTL + +.notProgressiveSword + CMP.b #$5F : BNE .notProgressiveShield + +.progressiveShield + LDA !PROGRESSIVE_SHIELD : LSR #6 : CMP.l ProgressiveShieldLimit : !BLT + + LDA.l ProgressiveShieldReplacement : RTL + + + LDA !PROGRESSIVE_SHIELD : AND.b #$C0 : BNE + + LDA.b #$04 : RTL + + : CMP.b #$40 : BNE + + LDA.b #$05 : RTL + + LDA.b #$06 : RTL + +.notProgressiveShield + CMP.b #$60 : BNE .notProgressiveArmor + +.progressiveArmor + LDA $7EF35B : CMP.l ProgressiveArmorLimit : !BLT + + LDA.l ProgressiveArmorReplacement : RTL + + + LDA $7EF35B : CMP.b #$00 : BNE + + LDA.b #$22 : RTL + + LDA.b #$23 : RTL + +.notProgressiveArmor + CMP.b #$61 : BNE .notProgressiveGlove + +.progressiveGlove + LDA $7EF354 : BNE + + LDA.b #$1B : RTL + + LDA.b #$1C : RTL + +.notProgressiveGlove + CMP.b #$64 : BEQ .progressiveBow + CMP.b #$65 : BNE .displayResolved + + .progressiveBow + LDA $7EF340 : INC : LSR : CMP.l ProgressiveBowLimit : !BLT + + LDA.l ProgressiveBowReplacement : RTL + + + LDA $7EF340 : INC : LSR : CMP.b #$00 : BNE + + LDA.b #$3A : RTL + + LDA.b #$3B : RTL + + .displayResolved + RTL +;-------------------------------------------------------------------------------- +; Boss-prize Null/trap items use the blank received-item graphic. Show a random +; ordinary item graphic at the prize location while keeping the real item ID. +BossPrizeResolvePrizeLocationDisplayItem: + PHA : PHY : PHB + TAY + LDA.b #AddReceivedItemExpanded_item_graphics_indices>>16 : PHA : PLB + LDA.w AddReceivedItemExpanded_item_graphics_indices, Y + CMP.b #$47 : BEQ .randomDisguise + PLB : PLY : PLA +RTL + +.randomDisguise + PLB : PLY : PLA + JSL.l GetRandomInt : AND.b #$3F + BNE + : LDA.b #$49 : + + CMP.b #$26 : BNE + : LDA.b #$6A : + +RTL +;-------------------------------------------------------------------------------- +; Prepare receive-item drawing to use the resolved boss-prize display item. +BossPrizeDrawPrep: + LDA.b #$00 + STA !BOSS_PRIZE_NARROW_SHADOW + STA !BOSS_PRIZE_DRAW_ACTIVE + STA !BOSS_PRIZE_X_OFFSET_APPLIED + LDA $0C4A, X : CMP.b #$29 : BNE .restore + JSL.l BossPrizeContextMatchesRoom : BCC .restore + JSL.l BossPrizeContextMatchesSlot : BCC .restore + LDA.b #$01 : STA !BOSS_PRIZE_DRAW_ACTIVE + PHX + LDA !BOSS_PRIZE_DISPLAY_ITEM : TAX + JSR.w BossPrizeLoadDisplayWideItemFlag + PLX + CMP.b #$00 : BEQ .narrowItem + JSR.w BossPrizeApplyDisplayXOffset + BRA .restore + +.narrowItem + LDA.b #$01 : STA !BOSS_PRIZE_NARROW_SHADOW + +.restore + REP #$20 + LDA $00 : CLC : ADC.w #$0008 : STA $08 + SEP #$20 + PHX + LDA $0BF0, X : STA $74 + JML.l BossPrizeDrawContinue +;-------------------------------------------------------------------------------- +; Undo any boss-prize X adjustment before the milestone-item shadow is drawn. +BossPrizeMilestoneShadowPrep: + SEP #$20 + LDA !BOSS_PRIZE_X_OFFSET_APPLIED : BEQ .drawShadow + JSR.w BossPrizeUndoDisplayXOffset + +.drawShadow + LDA !BOSS_PRIZE_NARROW_SHADOW : BEQ .prepShadowY + LDX.b #$02 + BRA .prepShadowY + +.prepShadowY + REP #$20 + +.shadowYReady + LDA $06 : CLC : ADC.w #$000C : STA $00 +RTL +;-------------------------------------------------------------------------------- +; Match vanilla receive-item X centering for the resolved boss-prize item. +BossPrizeApplyDisplayXOffset: + PHX : PHY + LDA !BOSS_PRIZE_DISPLAY_ITEM : TAY + JSR.w BossPrizeLoadDisplayXOffset + BEQ .shiftRight + BRA .done + +.shiftRight + LDA.b #$01 : STA !BOSS_PRIZE_X_OFFSET_APPLIED + REP #$20 + LDA $02 : CLC : ADC.w #$0001 : STA $02 + SEP #$20 + +.done + PLY : PLX +RTS +;-------------------------------------------------------------------------------- +; Restore the draw X position after the boss-prize item body is drawn. +BossPrizeUndoDisplayXOffset: + PHX : PHY + LDA !BOSS_PRIZE_DISPLAY_ITEM : TAY + JSR.w BossPrizeLoadDisplayXOffset + BEQ .shiftLeft + BRA .done + +.shiftLeft + REP #$20 + LDA $02 : SEC : SBC.w #$0001 : STA $02 + SEP #$20 + +.done + PLY : PLX +RTS +;-------------------------------------------------------------------------------- +; Treat freestanding swords as wide sprites even though their item IDs bypass the table. +BossPrizeLoadDisplayWideItemFlag: + TXA + CMP.b #$49 : BEQ .wideSword + CMP.b #$01 : BEQ .wideSword + CMP.b #$50 : BEQ .wideSword + CMP.b #$02 : BEQ .wideSword + CMP.b #$03 : BEQ .wideSword + LDA.l AddReceivedItemExpanded_wide_item_flag, X + RTS + +.wideSword + LDA.b #$02 +RTS +;-------------------------------------------------------------------------------- +; Load the display item's X offset, ignoring offsets for narrow item layouts. +BossPrizeLoadDisplayXOffset: + TYA + TAX + JSR.w BossPrizeLoadDisplayWideItemFlag : BEQ .ignore + LDA.l AddReceivedItemExpanded_x_offsets, X + RTS + +.ignore + LDA.b #$04 +RTS +;-------------------------------------------------------------------------------- +; Use the resolved display item when the upper tile of a boss-prize object is drawn. +BossPrizeShiftUpperItemTileAndLoadWideItemFlag: + PHX : PHY + LDA !BOSS_PRIZE_DRAW_ACTIVE : BEQ .normalItem + LDA !BOSS_PRIZE_DISPLAY_ITEM : TAX + JSR.w BossPrizeLoadDisplayWideItemFlag : CMP.b #$02 : BEQ .done + + .narrowItem + TYA : ASL #2 : TAY + LDA ($90), Y : CLC : ADC.b #$04 : STA ($90), Y + LDA !BOSS_PRIZE_DISPLAY_ITEM : TAX + BRA .loadWideFlag + +.normalItem +.loadWideFlag + LDA.l AddReceivedItemExpanded_wide_item_flag, X + BRA .done + +.done + PLY : PLX +RTL +;-------------------------------------------------------------------------------- +; Store the resolved display item's narrow/wide flag into the OAM size buffer. +BossPrizeLoadNarrowObject: + PHX : PHY + LDA !BOSS_PRIZE_DRAW_ACTIVE : BEQ .normalItem + LDA !BOSS_PRIZE_DISPLAY_ITEM : TAX + JSR.w BossPrizeLoadDisplayWideItemFlag + BRA .storeWideFlag + +.normalItem + LDA.l AddReceivedItemExpanded_wide_item_flag, X + +.storeWideFlag + STA ($92), Y + CMP.b #$02 : BEQ .done + LDA !BOSS_PRIZE_DRAW_ACTIVE : BEQ .done + + TYA : ASL #2 : TAY + LDA ($90), Y : CLC : ADC.b #$04 : STA ($90), Y + +.done + PLY : PLX +RTL +;-------------------------------------------------------------------------------- +; Boss-prize swords should use the sprite/freestanding source path, not the held-up source. +BossPrizeMasterSwordSourceCheck: + LDA $02E9 : CMP.b #$02 : BEQ .spriteSource + JSL.l BossPrizeReceiveContextMatches : BCS .spriteSource + JML.l BossPrizeMasterSwordSourceNormal + +.spriteSource + JML.l BossPrizeMasterSwordFromSprite +;-------------------------------------------------------------------------------- +; Show "free dungeon item" text for local shuffled boss prizes after substitution. +BossPrizeQueueFreeItemNotice: + JSL.l BossPrizeReceiveContextMatches : BCC .done + JSL.l BossPrizeGetPlayer : CMP.b #$00 : BNE .done + LDA $02D8 : JSL.l FreeDungeonItemNotice +.done +RTL +;-------------------------------------------------------------------------------- +; Avoid spawning the prize while another receive-item ancilla is still active. +BossPrizeSpawnReady: + LDX.b #$09 + +.nextAncilla + LDA $0C4A, X : CMP.b #$22 : BEQ .blocked + DEX : BPL .nextAncilla + SEC + RTL + +.blocked + CLC + RTL +;-------------------------------------------------------------------------------- +; Route boss-prize receive states through object/text handling and wait for text close. +BossPrizeReceiveDispatch: + LDA $0C54, X : BEQ .fromTextOrObject + CMP.b #$03 : BEQ .fromTextOrObject + CMP.b #$04 : BEQ .waitForTextClose + JML.l Ancilla_ReceiveItem_fromChestOrSprite + +.fromTextOrObject + JML.l Ancilla_ReceiveItem_fromTextOrObject + +.waitForTextClose + LDA $10 : CMP.b #$0E : BEQ .keepWaiting + LDA $11 : BNE .keepWaiting + LDA $1CD8 : BNE .keepWaiting + + STZ $0C4A, X + STZ $0FC1 + LDA $0C54, X : PHA : PHX + JSL.l PrepDungeonExit + PLX : PLA + JML.l Ancilla_ReceiveItem_objectFinished+44 + +.keepWaiting + LDA.b #$01 : STA $02E4 + LDA.b #$01 : STA $0FC1 + JML.l Ancilla_ReceiveItem_return +;-------------------------------------------------------------------------------- +; Ordinary pendant/crystal item IDs collected outside boss rooms still set vanilla bits. +MaybeSetOrdinaryBossPrizeBits: + CMP.b #$B6 : BNE + + LDA $7EF374 : ORA.b #$04 : STA $7EF374 + SEC : RTL + + CMP.b #$B7 : BNE + + LDA $7EF374 : ORA.b #$02 : STA $7EF374 + SEC : RTL + + CMP.b #$B8 : BNE + + LDA $7EF374 : ORA.b #$01 : STA $7EF374 + SEC : RTL + + CMP.b #$B9 : BNE + + LDA $7EF37A : ORA.b #$02 : STA $7EF37A + SEC : RTL + + CMP.b #$BA : BNE + + LDA $7EF37A : ORA.b #$10 : STA $7EF37A + SEC : RTL + + CMP.b #$BB : BNE + + LDA $7EF37A : ORA.b #$40 : STA $7EF37A + SEC : RTL + + CMP.b #$BC : BNE + + LDA $7EF37A : ORA.b #$20 : STA $7EF37A + SEC : RTL + + CMP.b #$BD : BNE + + LDA $7EF37A : ORA.b #$04 : STA $7EF37A + SEC : RTL + + CMP.b #$BE : BNE + + LDA $7EF37A : ORA.b #$01 : STA $7EF37A + SEC : RTL + + CMP.b #$BF : BNE + + LDA $7EF37A : ORA.b #$08 : STA $7EF37A + SEC : RTL + + + CLC +RTL +;-------------------------------------------------------------------------------- +; Count ordinary pendant/crystal item IDs for inventory and stats displays. +MaybeIncrementOrdinaryBossPrizeCounts: + CPY.b #$B6 : BCC .miss + CPY.b #$B9 : BCS .checkCrystals + LDA $7EF429 : INC : AND #$03 : TAX + LDA $7EF429 : AND #$FC : STA $7EF429 + TXA : ORA $7EF429 : STA $7EF429 + SEC + RTL + +.checkCrystals + CPY.b #$C0 : BCS .miss + LDA $7EF422 : INC : AND #$07 : TAX + LDA $7EF422 : AND #$F8 : STA $7EF422 + TXA : ORA $7EF422 : STA $7EF422 + SEC + RTL + +.miss + CLC +RTL +;-------------------------------------------------------------------------------- +; Map the current boss room to its configured shuffled prize item. +LoadBossPrizeRoomValue: + PHP + REP #$20 ; set 16-bit accumulator + LDA $A0 ; these are all decimal because i got them that way + CMP.w #200 : BNE + + %GetPossiblyEncryptedItem(BossPrize_ArmosKnights, BossPrizeValues) + BRL .done + + CMP.w #51 : BNE + + %GetPossiblyEncryptedItem(BossPrize_Lanmolas, BossPrizeValues) + BRL .done + + CMP.w #7 : BNE + + %GetPossiblyEncryptedItem(BossPrize_Moldorm, BossPrizeValues) + BRL .done + + CMP.w #90 : BNE + + %GetPossiblyEncryptedItem(BossPrize_HelmasaurKing, BossPrizeValues) + BRL .done + + CMP.w #6 : BNE + + %GetPossiblyEncryptedItem(BossPrize_Arrghus, BossPrizeValues) + BRL .done + + CMP.w #41 : BNE + + %GetPossiblyEncryptedItem(BossPrize_Mothula, BossPrizeValues) + BRL .done + + CMP.w #172 : BNE + + %GetPossiblyEncryptedItem(BossPrize_Blind, BossPrizeValues) + BRL .done + + CMP.w #222 : BNE + + %GetPossiblyEncryptedItem(BossPrize_Kholdstare, BossPrizeValues) + BRL .done + + CMP.w #144 : BNE + + %GetPossiblyEncryptedItem(BossPrize_Vitreous, BossPrizeValues) + BRL .done + + CMP.w #164 : BNE + + %GetPossiblyEncryptedItem(BossPrize_Trinexx, BossPrizeValues) + BRL .done + + + LDA.w #$0000 + +.done + AND.w #$00FF + PLP +RTL +;-------------------------------------------------------------------------------- +; Map the current boss room to the configured multiworld recipient. +BossPrizeGetPlayer: + PHP + REP #$20 ; set 16-bit accumulator + LDA $A0 ; these are all decimal because i got them that way + CMP.w #200 : BNE + + %GetPossiblyEncryptedPlayerID(BossPrize_ArmosKnights_Player) + BRL .done + + CMP.w #51 : BNE + + %GetPossiblyEncryptedPlayerID(BossPrize_Lanmolas_Player) + BRL .done + + CMP.w #7 : BNE + + %GetPossiblyEncryptedPlayerID(BossPrize_Moldorm_Player) + BRL .done + + CMP.w #90 : BNE + + %GetPossiblyEncryptedPlayerID(BossPrize_HelmasaurKing_Player) + BRL .done + + CMP.w #6 : BNE + + %GetPossiblyEncryptedPlayerID(BossPrize_Arrghus_Player) + BRL .done + + CMP.w #41 : BNE + + %GetPossiblyEncryptedPlayerID(BossPrize_Mothula_Player) + BRL .done + + CMP.w #172 : BNE + + %GetPossiblyEncryptedPlayerID(BossPrize_Blind_Player) + BRL .done + + CMP.w #222 : BNE + + %GetPossiblyEncryptedPlayerID(BossPrize_Kholdstare_Player) + BRL .done + + CMP.w #144 : BNE + + %GetPossiblyEncryptedPlayerID(BossPrize_Vitreous_Player) + BRL .done + + CMP.w #164 : BNE + + %GetPossiblyEncryptedPlayerID(BossPrize_Trinexx_Player) + BRL .done + + + LDA.w #$0000 + +.done + AND.w #$00FF + PLP +RTL +;-------------------------------------------------------------------------------- diff --git a/enemy_adjustments.asm b/enemy_adjustments.asm index a0206f26..b13ed0c9 100644 --- a/enemy_adjustments.asm +++ b/enemy_adjustments.asm @@ -28,3 +28,29 @@ RTL RTL ;-------------------------------------------------------------------------------- +;-------------------------------------------------------------------------------- +; WallmastersStayDeadCheckDamage: +; optionally clear the active Wallmaster spawner when one of its spawned +; Wallmasters is killed, preventing further spawns in this room visit. +;-------------------------------------------------------------------------------- +WallmastersStayDeadCheckDamage: + JSL.l $06F2B0 ; Sprite_CheckDamageFromPlayerLong, what we wrote over + LDA.l WallmastersStayDead : BEQ .return + LDA $0DD0, X : CMP.b #$09 : BNE .clear_spawners + LDA $0E20, X : CMP.b #$90 : BNE .clear_spawners ; transformed into another sprite + LDA $0CE2, X : BEQ .return + CMP.b #$FD : BEQ .clear_spawners ; incinerated + CMP.b #$FB : BCS .return ; stunned or frozen, not killed + CMP $0E50, X : BCC .return ; pending damage is not lethal +.clear_spawners + PHX + LDX.b #$07 +.next_spawner + LDA $0B00, X : CMP.b #$09 : BNE .skip_spawner + STZ $0B00, X +.skip_spawner + DEX : BPL .next_spawner + PLX +.return + RTL +;-------------------------------------------------------------------------------- diff --git a/events.asm b/events.asm index 13d9d437..a0d4de91 100644 --- a/events.asm +++ b/events.asm @@ -241,6 +241,9 @@ PostItemAnimation: LDA.b #$00 : STA !ITEM_BUSY ; mark item as finished LDA $7F509F : BEQ + + ; Boss-prize collection may defer text until its custom finish path, so + ; leave the message queued while the boss-prize receive context is active. + JSL.l BossPrizeReceiveContextMatches : BCS + STZ $1CF0 : STZ $1CF1 ; reset decompression buffer JSL.l Main_ShowTextMessage_Alt LDA.b #$00 : STA $7F509F diff --git a/floodgates.asm b/floodgates.asm new file mode 100644 index 00000000..c3ca911c --- /dev/null +++ b/floodgates.asm @@ -0,0 +1,75 @@ +;================================================================================ +; Floodgate Fixes +;-------------------------------------------------------------------------------- +FloodGateAndMasterSwordFollowerReset: + JSL.l MasterSwordFollowerClear +FloodGateReset: + LDA.l PersistentFloodgate : BNE + + LDA $7EF2BB : AND.b #$DF : STA $7EF2BB ; reset water outside floodgate + LDA $7EF2FB : AND.b #$DF : STA $7EF2FB ; reset water outside swamp palace + LDA $7EF216 : AND.b #$7F : STA $7EF216 ; clear water inside floodgate + LDA $7EF051 : AND.b #$FE : STA $7EF051 ; clear water front room (room 40) + + +FloodGateResetInner: + LDA.l Bugfix_SwampWaterLevel : BEQ .done + LDA.l ShuffleKeyDrops : BNE .check_room_55 + LDA $279004 : BEQ .check_room_53 ; Only do the check for room 55 if on door rando or key drop shuffle + .check_room_55 + LDA $7EF06F : AND.b #$04 : BEQ .drain_room_55 ; Check if key in room 55 has been collected. + LDA $7EF356 : AND.b #$01 : BNE .check_room_53 ; Check for flippers. This can otherwise softlock doors if flooded without flippers and no way to reset. + .drain_room_55 + LDA $7EF06E : AND.b #$7F : STA $7EF06E ; clear water room 55 - outer room you shouldn't be able to softlock except in major glitches + .check_room_53 + LDA $7EF06B : AND.b #$04 : BNE .done ; Check if key in room 53 has been collected. + ; no need to check for flippers on the inner room, as you can't get to the west door no matter what, without flippers. + LDA $7EF06A : AND.b #$7F : STA $7EF06A ; clear water room 53 - inner room with the easy key flood softlock + .done +RTL +;================================================================================ + +; Puzzle randomization can place the good Floodgate lever on the left side of the +; room. Pulling it from there leaves the camera farther west than vanilla expects, +; which makes the watergate HDMA window start partly off-screen. +; +; $E2 is the BG2 camera scroll mirror. The watergate object position in $0680 +; still needs to be converted to screen space, but for this one room we clamp +; the effective camera position to the vanilla-ish right-lever view and clip the +; packed left/right HDMA bounds so the later gate opening intervals do not wrap +; and make the water disappear. + +ClampFloodgateWatergateHdmaScroll: + LDA $A0 : CMP.w #$010B : BNE .normal ; Dam/Floodgate room 267. + LDA $E2 : CMP.w #$1660 : BCS .normal + LDA $0680 : SEC : SBC.w #$1660 + RTL + + .normal + LDA $0680 : SEC : SBC $E2 +RTL +;================================================================================ + +BuildFloodgateWatergateLineBounds: + STA $0C + LDA $A0 : CMP.w #$010B : BEQ .clipped ; Dam/Floodgate room 267. + + .normal + LDA $0C : CLC : ADC $0670 : STA $02 + LDA $0670 : SEC : SBC $0C : AND.w #$00FF : STA $00 + LDA $02 : AND.w #$00FF : XBA : ORA $00 : STA $0C + RTL + + .clipped + LDA $0670 : SEC : SBC $0C : BPL .leftInBounds + LDA.w #$0000 + + .leftInBounds + AND.w #$00FF : STA $00 + + LDA $0670 : CLC : ADC $0C + CMP.w #$0100 : BCC .rightInBounds + LDA.w #$00FF + + .rightInBounds + AND.w #$00FF : XBA : ORA $00 : STA $0C +RTL +;================================================================================ diff --git a/floodgatesoftlock.asm b/floodgatesoftlock.asm deleted file mode 100644 index 7dfe0ca9..00000000 --- a/floodgatesoftlock.asm +++ /dev/null @@ -1,26 +0,0 @@ -;================================================================================ -; Floodgate Softlock Fix -;-------------------------------------------------------------------------------- -FloodGateAndMasterSwordFollowerReset: - JSL.l MasterSwordFollowerClear -FloodGateReset: - LDA.l PersistentFloodgate : BNE + - LDA $7EF2BB : AND.b #$DF : STA $7EF2BB ; reset water outside floodgate - LDA $7EF2FB : AND.b #$DF : STA $7EF2FB ; reset water outside swamp palace - LDA $7EF216 : AND.b #$7F : STA $7EF216 ; clear water inside floodgate - LDA $7EF051 : AND.b #$FE : STA $7EF051 ; clear water front room (room 40) - + -FloodGateResetInner: - LDA.l Bugfix_SwampWaterLevel : BEQ .done - LDA $279004 : BEQ .check_room_53 ; Only do the check for room 55 if on door rando - LDA $7EF06F : AND.b #$04 : BEQ .drain_room_55 ; Check if key in room 55 has been collected. - LDA $7EF356 : AND.b #$01 : BNE .check_room_53 ; Check for flippers. This can otherwise softlock doors if flooded without flippers and no way to reset. - .drain_room_55 - LDA $7EF06E : AND.b #$7F : STA $7EF06E ; clear water room 55 - outer room you shouldn't be able to softlock except in major glitches - .check_room_53 - LDA $7EF06B : AND.b #$04 : BNE .done ; Check if key in room 53 has been collected. - ; no need to check for flippers on the inner room, as you can't get to the west door no matter what, without flippers. - LDA $7EF06A : AND.b #$7F : STA $7EF06A ; clear water room 53 - inner room with the easy key flood softlock - .done -RTL -;================================================================================ diff --git a/gfx_compress.py b/gfx_compress.py new file mode 100644 index 00000000..93952358 --- /dev/null +++ b/gfx_compress.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +"""Compress raw graphics into ALTTP graphics-pack format. + +This intentionally uses only the simpler command types: +0 = literal bytes +1 = repeated byte +2 = alternating 2-byte word +3 = incrementing byte sequence + +That is enough to produce valid .gfx files for the paired assets in this repo +while staying inside the existing reserved ROM slots. +""" + +from __future__ import annotations + +import argparse +from pathlib import Path + + +MAX_CHUNK_LEN = 32 +TERMINATOR = 0xFF + + +def emit_command(out: bytearray, command: int, length: int, payload: bytes) -> None: + if not 1 <= length <= MAX_CHUNK_LEN: + raise ValueError(f"invalid command length {length}") + out.append((command << 5) | (length - 1)) + out.extend(payload) + + +def compress(data: bytes) -> bytes: + out = bytearray() + literal = bytearray() + index = 0 + + def flush_literal() -> None: + nonlocal literal + while literal: + chunk = bytes(literal[:MAX_CHUNK_LEN]) + del literal[:MAX_CHUNK_LEN] + emit_command(out, 0, len(chunk), chunk) + + while index < len(data): + best_kind = None + best_len = 0 + + repeated_len = 1 + while ( + index + repeated_len < len(data) + and repeated_len < MAX_CHUNK_LEN + and data[index + repeated_len] == data[index] + ): + repeated_len += 1 + if repeated_len >= 2: + best_kind = "repeat" + best_len = repeated_len + + if index + 1 < len(data): + first = data[index] + second = data[index + 1] + alternating_len = 0 + while index + alternating_len < len(data) and alternating_len < MAX_CHUNK_LEN: + expected = first if alternating_len % 2 == 0 else second + if data[index + alternating_len] != expected: + break + alternating_len += 1 + if alternating_len >= 4 and alternating_len > best_len: + best_kind = "alternating" + best_len = alternating_len + + incrementing_len = 1 + start_value = data[index] + while ( + index + incrementing_len < len(data) + and incrementing_len < MAX_CHUNK_LEN + and data[index + incrementing_len] == ((start_value + incrementing_len) & 0xFF) + ): + incrementing_len += 1 + if incrementing_len >= 3 and incrementing_len > best_len: + best_kind = "incrementing" + best_len = incrementing_len + + if best_kind is None: + literal.append(data[index]) + index += 1 + if len(literal) == MAX_CHUNK_LEN: + flush_literal() + continue + + flush_literal() + + if best_kind == "repeat": + emit_command(out, 1, best_len, bytes([data[index]])) + elif best_kind == "alternating": + emit_command(out, 2, best_len, data[index:index + 2]) + else: + emit_command(out, 3, best_len, bytes([data[index]])) + + index += best_len + + flush_literal() + out.append(TERMINATOR) + return bytes(out) + + +def decompress(data: bytes) -> bytes: + out = bytearray() + index = 0 + + while True: + command_byte = data[index] + index += 1 + + if command_byte == TERMINATOR: + return bytes(out) + + if (command_byte & 0xE0) == 0xE0: + command = (command_byte >> 2) & 0x07 + length = (((command_byte & 0x03) << 8) | data[index]) + 1 + index += 1 + else: + command = (command_byte >> 5) & 0x07 + length = (command_byte & 0x1F) + 1 + + if command == 0: + out.extend(data[index:index + length]) + index += length + continue + + if command == 1: + out.extend([data[index]] * length) + index += 1 + continue + + if command == 2: + first = data[index] + second = data[index + 1] + index += 2 + for offset in range(length): + out.append(first if offset % 2 == 0 else second) + continue + + if command == 3: + value = data[index] + index += 1 + for _ in range(length): + out.append(value) + value = (value + 1) & 0xFF + continue + + raise ValueError(f"unsupported command {command}; this script only emits 0-3") + + +def parse_max_size(value: str) -> int: + return int(value, 0) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("input", type=Path, help="raw decompressed graphics file, usually .bin") + parser.add_argument( + "output", + type=Path, + nargs="?", + help="output .gfx path; defaults to input path with .gfx suffix", + ) + parser.add_argument( + "--max-size", + type=parse_max_size, + help="fail if compressed size exceeds this value (accepts hex like 0x800)", + ) + parser.add_argument( + "--no-verify", + action="store_true", + help="skip decompressing the result to verify round-trip correctness", + ) + args = parser.parse_args() + + input_path = args.input + output_path = args.output or input_path.with_suffix(".gfx") + + raw = input_path.read_bytes() + compressed = compress(raw) + + if not args.no_verify and decompress(compressed) != raw: + raise RuntimeError("round-trip verification failed") + + if args.max_size is not None and len(compressed) > args.max_size: + raise RuntimeError( + f"compressed output is {len(compressed)} bytes, exceeds limit {args.max_size}" + ) + + output_path.write_bytes(compressed) + print( + f"{input_path.name}: {len(raw)} -> {len(compressed)} bytes " + f"({len(raw) - len(compressed)} saved)" + ) + print(f"wrote {output_path}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/goalitem.asm b/goalitem.asm index 6b956a10..5fb89fa2 100644 --- a/goalitem.asm +++ b/goalitem.asm @@ -27,11 +27,8 @@ CheckGanonVulnerability: + : CMP #$01 : BEQ .fail ;#$01 = On + : CMP #$02 : BNE + - ;#$02 = Require All Dungeons - LDA $7EF374 : AND.b #$07 : CMP #$07 : BNE .fail ; require all pendants - LDA $7EF37A : AND.b #$7F : CMP #$7F : BNE .fail ; require all crystals - LDA $7EF3C5 : CMP.b #$03 : !BLT .fail ; require post-aga world state - LDA $7EF2DB : AND.b #$20 : CMP #$20 : BNE .fail ; require aga2 defeated (pyramid hole open) + ;#$02 = Require Number of Dungeons + JSL CheckEnoughDungeonsForGanon : !BLT .fail ; require specified number of boss rooms/Aga fights BRA .success + : CMP #$04 : BNE + ;#$04 = Require Crystals @@ -79,7 +76,30 @@ CheckEnoughCrystalsForGanon: PHX : PHY LDA $7EF37A : JSL CountBits ; the comparison is against 1 less PLY : PLX - CMP.l NumberOfCrystalsRequiredForGanon + CMP.l GanonRequirementCount +RTL +;-------------------------------------------------------------------------------- +CheckEnoughDungeonsForGanon: + PHX : PHY + LDX.b #$00 + + LDA.l $7EF191 : AND.b #$08 : BEQ + : INX : + ; Eastern Palace / Armos Knights + LDA.l $7EF067 : AND.b #$08 : BEQ + : INX : + ; Desert Palace / Lanmolas + LDA.l $7EF00F : AND.b #$08 : BEQ + : INX : + ; Tower of Hera / Moldorm + LDA.l $7EF0B5 : AND.b #$08 : BEQ + : INX : + ; Palace of Darkness / Helmasaur King + LDA.l $7EF00D : AND.b #$08 : BEQ + : INX : + ; Swamp Palace / Arrghus + LDA.l $7EF053 : AND.b #$08 : BEQ + : INX : + ; Skull Woods / Mothula + LDA.l $7EF159 : AND.b #$08 : BEQ + : INX : + ; Thieves' Town / Blind + LDA.l $7EF1BD : AND.b #$08 : BEQ + : INX : + ; Ice Palace / Kholdstare + LDA.l $7EF121 : AND.b #$08 : BEQ + : INX : + ; Misery Mire / Vitreous + LDA.l $7EF149 : AND.b #$08 : BEQ + : INX : + ; Turtle Rock / Trinexx + + LDA.l $7EF041 : AND.b #$08 : BEQ + : INX : + ; Agahnim 1 + LDA.l $7EF01B : AND.b #$08 : BEQ + : INX : + ; Agahnim 2 + + TXA + PLY : PLX + CMP.l GanonRequirementCount RTL ;-------------------------------------------------------------------------------- CheckEnoughCrystalsForTower: diff --git a/hardmode.asm b/hardmode.asm index 0bda6344..9f5fdeda 100644 --- a/hardmode.asm +++ b/hardmode.asm @@ -82,7 +82,22 @@ GetItemDamageValue: CPX.b #$3d : BEQ .hookshot .normal - lda $0db8f1,x ;what we wrote over + LDA $0DB8F1, X ; what we wrote over + CMP.b #$FB : !BLT .done + CMP.b #$FD : BEQ .done ; incinerate should still apply + + ; The caller has the sprite slot saved under the JSL return address. + TAY + LDA $04,s : TAX + LDA $0DD0, X : CMP.b #$0B : BNE .restoreDamage + STZ $47 + STZ $02E3 + LDA.b #$00 +RTL + + .restoreDamage + TYA + .done RTL .boomerang LDA.l StunItemAction : AND #$01 : BNE .normal diff --git a/heartpieces.asm b/heartpieces.asm index ce753580..49502441 100644 --- a/heartpieces.asm +++ b/heartpieces.asm @@ -1,6 +1,8 @@ ;================================================================================ ; Randomize Heart Pieces ;-------------------------------------------------------------------------------- +!BOSS_HEART_FANFARE = "$7F5072" + HeartPieceGet: PHX : PHY LDY $0E80, X ; load item value into Y register @@ -30,6 +32,7 @@ HeartPieceGet: BRL .done ; finished .unfinished_heart + LDA.b #$00 : STA !BOSS_HEART_FANFARE SEC ; return true .done @@ -47,6 +50,8 @@ HeartContainerGet: JSL.l LoadHeartContainerRoomValue : TAY + + LDA.b #$01 : STA !BOSS_HEART_FANFARE + LDA.b #$2E : JSL Sound_SetSfx3PanLong BRA HeartPieceGet_skipLoad ;-------------------------------------------------------------------------------- !REDRAW = "$7F5000" @@ -94,38 +99,47 @@ DrawHeartContainerGFX: BRA DrawHeartPieceGFX_skipLoad ;-------------------------------------------------------------------------------- HeartContainerSound: - LDA !MULTIWORLD_ITEM_PLAYER_ID : BNE + - CPY.b #$20 : BEQ + ; Skip for Crystal - CPY.b #$37 : BEQ + ; Skip for Pendants - CPY.b #$38 : BEQ + - CPY.b #$39 : BEQ + - JSL.l CheckIfBossRoom : BCC + ; Skip if not in a boss room - LDA.b #$2E - SEC - RTL - + CLC RTL ;-------------------------------------------------------------------------------- NormalItemSkipSound: - LDA !MULTIWORLD_ITEM_PLAYER_ID : BEQ + + LDA !BOSS_HEART_FANFARE : BEQ + + LDA.b #$00 : STA !BOSS_HEART_FANFARE SEC RTL + LDA $0C5E, X ; thing we wrote over - - CPY.b #$20 : BEQ + ; Skip for Crystal - CPY.b #$37 : BEQ + ; Skip for Pendants - CPY.b #$38 : BEQ + - CPY.b #$39 : BEQ + - PHA - JSL.l CheckIfBossRoom + ; Shuffled boss prizes keep the victory fanfare even when the prize is not + ; one of the vanilla pendant/crystal item IDs. + JSL.l BossPrizeItemNeedsVictoryFanfare : BCC + + PLA + JSL Sound_SetSfxPanWithPlayerCoords + ORA.b #$13 : STA $012C + SEC + RTL + + PLA -RTL + + LDA !MULTIWORLD_ITEM_PLAYER_ID : BEQ + + SEC + RTL + + + LDA $0C5E, X ; thing we wrote over + CMP.b #$CE : BNE + + PHA + LDA $0C54, X : CMP.b #$03 + PLA + BEQ .skipSound ; Skip floor-object current-dungeon key ring pickup fanfare + + + CLC + RTL + +.skipSound + SEC RTL ;-------------------------------------------------------------------------------- HeartUpgradeSpawnDecision: ; this should return #$00 to make the hp spawn diff --git a/hooks.asm b/hooks.asm index 93a9f2fd..57f10970 100755 --- a/hooks.asm +++ b/hooks.asm @@ -74,6 +74,57 @@ org $02E21B ; <- 1621B - Bank02.asm : 11211 (STA $040C) JSL.l OnDungeonExit : NOP #2 ;-------------------------------------------------------------------------------- +;================================================================================ +; Puzzle-shuffled torch counts +;-------------------------------------------------------------------------------- +; Vanilla torch room tags require 4 lit torches. Puzzle shuffle can place torch +; tags in rooms 0x0B and 0x74, which only have 2 usable torches, and room 0x83, +; which has 3 usable torches. +org $01C642 ; <- Bank01.asm, tag 0x33 "light torches to open" (LDA $00 : CMP #$0004) +JSL.l ComparePuzzleTorchRequirement +NOP + +org $01C8C7 ; <- Bank01.asm, tag 0x3E "light torches to get chest" (LDA $00 : CMP #$0004) +JSL.l ComparePuzzleTorchRequirement +NOP + +org $36F000 +ComparePuzzleTorchRequirement: + LDA $A0 : CMP.w #$000B : BEQ .twoTorches + CMP.w #$0074 : BEQ .twoTorches + CMP.w #$0083 : BEQ .threeTorches + LDA $00 : CMP.w #$0004 + RTL + +.twoTorches + LDA $00 : CMP.w #$0002 + RTL + +.threeTorches + LDA $00 : CMP.w #$0003 + RTL +;-------------------------------------------------------------------------------- + +;================================================================================ +; Floodgate HDMA scroll fix +;-------------------------------------------------------------------------------- +org $00F664 ; <- Bank00.asm, Hdma_ConfigureWaterTable (LDA $0680 : SUB $E2) +JSL.l ClampFloodgateWatergateHdmaScroll +NOP #2 + +org $00F742 ; <- Bank00.asm, Hdma_ConfigureWaterTableForWaterfall (LDA $0680 : SUB $E2) +JSL.l ClampFloodgateWatergateHdmaScroll +NOP #2 + +org $00F754 ; <- Bank00.asm, Hdma_ConfigureWaterTableForWaterfall first line-bounds packing +JSL.l BuildFloodgateWatergateLineBounds +NOP #22 + +org $00F78D ; <- Bank00.asm, Hdma_ConfigureWaterTableForWaterfall second line-bounds packing +JSL.l BuildFloodgateWatergateLineBounds +NOP #23 +;-------------------------------------------------------------------------------- + ;================================================================================ ; Quit Hook (for both types of save and quit) ;-------------------------------------------------------------------------------- @@ -640,6 +691,9 @@ NOP #2 ;================================================================================ ; Wallmaster camera fix ;-------------------------------------------------------------------------------- +org $1EAF10 ; <- F2F10 sprite_wallmaster.asm : 68 (JSL Sprite_CheckDamageFromPlayerLong) +JSL.l WallmastersStayDeadCheckDamage + org $1EAF77 ; <- F2F77 sprite_wallmaster.asm : 141 (LDA.b #$2A : JSL Sound_SetSfx3PanLong) JSL.l WallmasterCameraFix @@ -799,9 +853,6 @@ org $06B49D ; <- 3349D - sprite_smithy_bros.asm : 485 (.tempered_sword_or_better Smithy_AlreadyGotSword: org $06B561 ; <- 33561 - sprite_smithy_bros.asm : 640 (JSL Link_ReceiveItem) ;-------------------------------------------------------------------------------- -org $06ED55 ; <- 36D55 - Bank06.asm : 4817 -JSL.l LoadSwordForDamage ; moth gold sword fix -;-------------------------------------------------------------------------------- org $08C5F7 ; <- 445F7 - ancilla_receive_item.asm : 400 (LDA.b #$09 : STA $012D) NOP #5 ; remove spooky telepathy sound ;-------------------------------------------------------------------------------- @@ -916,8 +967,24 @@ LDA.w AddReceivedItemExpanded_x_offsets, Y org $08C6C8 ; 446C8 - ancilla_receive_item.asm:538 (LDA AddReceiveItem.properties, X) JSL CheckReceivedItemPropertiesBeforeLoad +; Boss-prize items draw with the resolved display item, not always the stored item ID. +org $08C6B4 ; 446B4 - ancilla_receive_item.asm:520 alternate draw entry (PHX : LDA $0BF0, X : STA $74) +JML.l BossPrizeDrawPrep +NOP #2 +org $08C6BA +BossPrizeDrawContinue: + +; Route shuffled boss-prize swords through the freestanding/sprite source path. +org $0987BB ; 487BB - ancilla_init.asm (LDA $02E9 : CMP.b #$02 : BEQ .masterSwordFromSprite) +JML.l BossPrizeMasterSwordSourceCheck +NOP #3 +org $0987C2 +BossPrizeMasterSwordSourceNormal: +org $0987E2 +BossPrizeMasterSwordFromSprite: + org $08C6DE ; 446DE - ancilla_receive_item.asm:550 (LDA .wide_item_flag, X) -LDA.l AddReceivedItemExpanded_wide_item_flag, X +JSL.l BossPrizeShiftUpperItemTileAndLoadWideItemFlag org $08C6F9 ; 446F9 - ancilla_receive_item.asm:570 (LDA AddReceiveItem.properties, X) JSL CheckReceivedItemPropertiesBeforeLoad @@ -925,6 +992,11 @@ JSL CheckReceivedItemPropertiesBeforeLoad org $08C70F ; 4470F - ancilla_receive_item.asm : 582 - (LDA.b #$00 : STA ($92), Y) JSL.l LoadNarrowObject +; Boss-prize narrow items need the shadow position adjusted after the item body. +org $08CBF3 ; 44BF3 - ancilla_milestone_item.asm:239 (REP #$20 : LDA $06 : ADD.w #$000C : STA $00) +JSL.l BossPrizeMilestoneShadowPrep +NOP #6 + ;org $0985ED ; 485ED - ancilla_init.asm:693 (LDA $02E9 : CMP.b #$01) ;JSL.l AddReceivedItemExpandedGetItem ;NOP @@ -1001,7 +1073,12 @@ JSL.l LockAgahnimDoors : BNE Overworld_Entrance_BRANCH_EPSILON : NOP #6 org $1BBCC1 ; <- DBCC1 - Bank1B.asm : 223 (LDA $0F8004, X : AND.w #$01FF : STA $00) Overworld_Entrance_BRANCH_EPSILON: ; go here to lock doors ;-------------------------------------------------------------------------------- -; -- HOOK THIS LATER TO FUCK WITH BOSS DROPS -- +; Replace the boss-room tag so shuffled prizes can spawn ordinary item IDs. +org $01C709 ; <- C709 - Bank01.asm : 10347 +JSL.l BossPrizeRoomTag +RTS +NOP #48 +;-------------------------------------------------------------------------------- org $01C73E ; <- C73E - Bank01.asm : 10377 (LDA $01C6FC, X : JSL Sprite_SpawnFallingItem) JSL.l DropSafeDungeon NOP #4 @@ -1551,9 +1628,20 @@ org $08C421 ; <- AD4021 F005 - ancilla_receive_item.asm:108 (LDA $2140 : BEQ .wa JML PendantFanfareWait : NOP PendantFanfareContinue: +; Non-pendant shuffled boss prizes still wait for victory music before finishing. +org $08C412 ; <- BD5E0C C937 F008 - ancilla_receive_item.asm:101 (LDA $0C5E, X : CMP.b #$37 : BEQ .isPendant) +JML.l BossPrizePendantWaitCheck +NOP #11 + org $08C42B PendantFanfareDone: +; Crystals spawned by boss-prize shuffle should use the crystal-special branch. +org $08C61D ; <- BD5E0C C920 - ancilla_receive_item.asm:435 (LDA $0C5E, X : CMP.b #$20) +JML.l CrystalItemBehaviorCheck +NOP +CrystalSpecialBehaviorContinue: + org $08C62A ; <- AD4021 D008 - ancilla_receive_item.asm:442 (LDA $2140 : BNE .waitForSilence) JML CrystalFanfareWait : NOP CrystalFanfareDone: @@ -1728,13 +1816,10 @@ JSL.l FlipLWDWFlag NOP #2 ;================================================================================ ;Clear level to open doors -org $01C50D ; 0xC50D - Bank01.asm:10032 - (LDA $7EF3CA : BNE .inDarkWorld) -LDA CrystalPendantFlags_2, X -;================================================================================ -;Kill enemy to clear level -org $01C715 ; <- C715 - Bank01.asm:10358 - (LDA $7EF3CA : BNE .inDarkWorld) -LDA CrystalPendantFlags_2, X -;JSL.l GetPendantCrystalWorld +org $01C508 ; 0xC508 - Bank01.asm:10027 - tag routine 0x16 +JSL.l BossPrizeClearLevelToOpenDoors +RTS +NOP #52 ;================================================================================ ;org $0AC5C3 ; <- 545C3 - Bank0A.asm:1859 - (LDA $7EF374 : AND $0AC5A6, X : BEQ .fail) ;NOP #10 @@ -1896,15 +1981,15 @@ org $05FA8E Sprite_ShowMessageMinimal: JML.l Sprite_ShowMessageMinimal_Alt ;-------------------------------------------------------------------------------- -;org $1CFD69 -;Main_ShowTextMessage: -;JML.l Main_ShowTextMessage_Alt +org $1CFD69 +Main_ShowTextMessage: +JML.l Main_ShowTextMessage_TrackHintRead ;-------------------------------------------------------------------------------- org $07b0cc ; <- 3b0d0 - Bank 07.asm : 7767 (JSL Main_ShowTextMessage) -JSL.l Main_ShowTextMessage_Alt +JSL.l Main_ShowTextMessage_TrackHintRead ;-------------------------------------------------------------------------------- org $08c5fe ; <- 445FE - ancilla_receive_item.asm : 408 (JSL Main_ShowTextMessage) -JSL.l Main_ShowTextMessage_Alt +JSL.l Main_ShowTextMessage_TrackHintRead ;-------------------------------------------------------------------------------- org $05E21F ; <- 2E21F - Bank05.asm : 2691 (STZ $0223) JSL.l Sprite_ShowMessageMinimal_Alt @@ -2021,6 +2106,27 @@ org $08C45F ; <- 4445F - ancilla_recieve_item.asm:157 (STZ $02E9 : LDA $0C5E, X) Ancilla_ReceiveItem_optimus: JML.l PostItemAnimation : NOP #2 +org $08C46F ; <- 4446F - ancilla_receive_item.asm:164 (PHX : LDY.b #$26 : JSL Link_ReceiveItem ...) +JSL.l BossPrizeHeartPieceCompletionBranch +RTS +NOP #10 + +; Split receive-item dispatch so boss-prize text/object states can be handled. +org $08C3D0 ; <- 443D0 - ancilla_receive_item.asm:62 (LDA $0C54, X) +JML.l BossPrizeReceiveDispatch +NOP #8 +org $08C3DC +Ancilla_ReceiveItem_fromTextOrObject: +org $08C538 +Ancilla_ReceiveItem_return: +org $08C539 +Ancilla_ReceiveItem_fromChestOrSprite: + +; Finish shuffled boss prizes by marking completion and starting the dungeon exit. +org $08C505 ; <- 44505 - ancilla_receive_item.asm:256 (STZ $0C4A, X : STZ $0FC1) +JML.l HandleBossPrizeObjectFinished +NOP #2 + org $08C548 ; <- 44548 - ancilla_recieve_item.asm:297 (CMP.b #$28 : BNE .dontGiveRupees) JML.l Multiworld_Ancilla_ReceiveItem_stillInMotion Ancilla_ReceiveItem_stillInMotion_moveon: @@ -2190,6 +2296,11 @@ BCC Link_ReceiveItem_notHeartContainer org $0799BA ; 399BA - Bank07.asm:4070 (LDA.b #$60 : STA $02D9) Link_ReceiveItem_notHeartContainer: ;-------------------------------------------------------------------------------- +; Current-dungeon key rings are granted quietly by a custom receive-item branch. +org $0799BF ; 399BF - Bank07.asm:4072 (LDA $02E9 : BEQ .fromTextOrObject) +JML.l Link_ReceiveItem_HandleCurrentDungeonKeyRing +NOP #5 +;-------------------------------------------------------------------------------- org $09887F ; <- 4887F - ancilla_init.asm : 1163 (LDA $0C5E, X : CMP.b #$3E : BEQ .doneWithSoundEffects) JSL NormalItemSkipSound NOP @@ -2679,4 +2790,3 @@ org $1BEE1B JSL Palette_ArmorAndGlovesRandSprite_part_two RTL ;-------------------------------------------------------------------------------- - diff --git a/inventory.asm b/inventory.asm index 1bf7cfb7..c37eef7c 100644 --- a/inventory.asm +++ b/inventory.asm @@ -466,6 +466,10 @@ AddInventory: JSR .incrementPendant JSR .setDungeonCompletion BRL .done + ; Shuffled boss prizes use ordinary item IDs and still need pendant/crystal counts. + + JSL.l MaybeIncrementOrdinaryBossPrizeCounts : BCC + + BRL .done + + + CPY.b #$3A : !BLT + ; Items $3A - $3B - Bow & Silver Arrows CPY.b #$3C : !BGE + JSR .incrementBow @@ -544,6 +548,11 @@ AddInventory: CPY.b #$B0 : !BGE + JSR .incrementKey BRL .done + ; Key rings count as key inventory checks for the owning dungeon. + + CPY.b #$C0 : !BLT + ; Items $C0 - $CE - Key Rings + CPY.b #$CF : !BGE + + JSR .incrementKey + BRL .done + .done PLP : PLX : PLA @@ -1184,14 +1193,14 @@ SpawnShovelItem: LDX.b #$00 LDA $2F : CMP.b #$04 : BEQ + : INX : + - LDA.l .x_speeds, X : STA $0D50, Y + LDA.w .x_speeds, X : STA $0D50, Y LDA.b #$00 : STA $0D40, Y LDA.b #$18 : STA $0F80, Y LDA.b #$FF : STA $0B58, Y LDA.b #$30 : STA $0F10, Y - LDA $22 : !ADD.l .x_offsets, X + LDA $22 : !ADD.w .x_offsets, X AND.b #$F0 : STA $0D10, Y LDA $23 : ADC.b #$00 : STA $0D30, Y diff --git a/itemtext.asm b/itemtext.asm index f4d32c17..83ade1e6 100644 --- a/itemtext.asm +++ b/itemtext.asm @@ -27,6 +27,13 @@ Notice_SmallKeyOf: db $75, $00, $BC, $00, $B6, $00, $AA, $00, $B5, $00, $B5, $00, $FF, $00, $B4, $00, $AE, $00, $C2, $00, $FF, $00, $BD, $00, $B8 dw #$7F7F +; this is a +; key ring to +Notice_KeyRingOf: + db $74, $00, $BD, $00, $B1, $00, $B2, $00, $BC, $00, $FF, $00, $B2, $00, $BC, $00, $FF, $00, $AA + db $75, $00, $B4, $00, $AE, $00, $C2, $00, $FF, $00, $BB, $00, $B2, $00, $B7, $00, $B0, $00, $FF, $00, $BD, $00, $B8 + dw #$7F7F + ; light world Notice_LightWorld: db $76, $00, $B5, $00, $B2, $00, $B0, $00, $B1, $00, $BD, $00, $FF, $00, $C0, $00, $B8, $00, $BB, $00, $B5, $00, $AD diff --git a/keydropshuffle.asm b/keydropshuffle.asm index 403ff707..bbe25621 100644 --- a/keydropshuffle.asm +++ b/keydropshuffle.asm @@ -133,7 +133,10 @@ SpriteKeyDrawGFX: cmp #$24 : bne + lda $a0 : cmp #$80 : bne - : lda #$24 + jsl DrawDynamicTile ; see DrawHeartPieceGFX if problems - cmp #$03 : bne + + cmp #$03 : beq .narrow + pha : lda $0e60, x : and.b #$DF : sta $0E60, x : pla + bra + + .narrow pha : lda $0e60, x : ora.b #$20 : sta $0E60, x : pla + jsl.l Sprite_DrawShadowLong @@ -160,6 +163,14 @@ KeyGet: - JSL.l FullInventoryExternal : jsl CountChestKeyLong : plx : pla : rtl + cmp #$af : beq - ; universal key cmp #$24 : beq - ; small key for this dungeon + cmp #$CE : bne + ; small key ring for this dungeon + ; Current-dungeon key rings add the configured quantity to the + ; saved key count before the normal key pickup returns. + lda.l KeyRingQuantities, x : beq - + clc : adc $02, S : dec + sta $02, S + bra - + + plx .receive jsl.l $0791b3 ; Player_HaltDashAttackLong diff --git a/multiworld.asm b/multiworld.asm index 0f6b2ea2..03b44667 100644 --- a/multiworld.asm +++ b/multiworld.asm @@ -44,6 +44,12 @@ PHX : PHY : PHP PLP : PLY : PLX endmacro +Multiworld_PrintSentTo: +{ + %Print_Text(HUD_SentTo, #$0010, !MULTIWORLD_ITEM_PLAYER_ID) +RTL +} + WriteText: { PHA : PHX : PHP @@ -136,34 +142,43 @@ GetMultiworldItem: + PLA + ; The added current-dungeon key ring cases make some branches exceed 8-bit + ; range, so near labels trampoline to the original key handling blocks. + BRA .checkCurrentDungeonKey + .thisdungeon_near + BRL .thisdungeon + .keyend_near + BRL .keyend + .checkCurrentDungeonKey + ; Check if we have a key for the dungeon we are currently in LDX $040C ; Escape CMP #$A0 : BNE + : CPX #$00 : BEQ ++ : CPX #$02 : BEQ ++ : BRL .keyend : ++ : BRL .thisdungeon : + ; Eastern - CMP #$A2 : BNE + : CPX #$04 : BEQ .thisdungeon : BRA .keyend : + + CMP #$A2 : BNE + : CPX #$04 : BEQ .thisdungeon_near : BRA .keyend_near : + ; Desert - CMP #$A3 : BNE + : CPX #$06 : BEQ .thisdungeon : BRA .keyend : + + CMP #$A3 : BNE + : CPX #$06 : BEQ .thisdungeon_near : BRA .keyend_near : + ; Hera - CMP #$AA : BNE + : CPX #$14 : BEQ .thisdungeon : BRA .keyend : + + CMP #$AA : BNE + : CPX #$14 : BEQ .thisdungeon_near : BRA .keyend_near : + ; Aga - CMP #$A4 : BNE + : CPX #$08 : BEQ .thisdungeon : BRA .keyend : + + CMP #$A4 : BNE + : CPX #$08 : BEQ .thisdungeon_near : BRA .keyend_near : + ; PoD - CMP #$A6 : BNE + : CPX #$0C : BEQ .thisdungeon : BRA .keyend : + + CMP #$A6 : BNE + : CPX #$0C : BEQ .thisdungeon_near : BRA .keyend_near : + ; Swamp - CMP #$A5 : BNE + : CPX #$0A : BEQ .thisdungeon : BRA .keyend : + + CMP #$A5 : BNE + : CPX #$0A : BEQ .thisdungeon_near : BRA .keyend_near : + ; SW - CMP #$A8 : BNE + : CPX #$10 : BEQ .thisdungeon : BRA .keyend : + + CMP #$A8 : BNE + : CPX #$10 : BEQ .thisdungeon_near : BRA .keyend_near : + ; TT - CMP #$AB : BNE + : CPX #$16 : BEQ .thisdungeon : BRA .keyend : + + CMP #$AB : BNE + : CPX #$16 : BEQ .thisdungeon_near : BRA .keyend_near : + ; Ice - CMP #$A9 : BNE + : CPX #$12 : BEQ .thisdungeon : BRA .keyend : + + CMP #$A9 : BNE + : CPX #$12 : BEQ .thisdungeon_near : BRA .keyend_near : + ; Mire - CMP #$A7 : BNE + : CPX #$0E : BEQ .thisdungeon : BRA .keyend : + + CMP #$A7 : BNE + : CPX #$0E : BEQ .thisdungeon_near : BRA .keyend_near : + ; TR - CMP #$AC : BNE + : CPX #$18 : BEQ .thisdungeon : BRA .keyend : + + CMP #$AC : BNE + : CPX #$18 : BNE ++ : BRL .thisdungeon : ++ : BRL .keyend : + ; GT - CMP #$AD : BNE + : CPX #$1A : BEQ .thisdungeon : BRA .keyend : + + CMP #$AD : BNE + : CPX #$1A : BNE ++ : BRL .thisdungeon : ++ : BRL .keyend : + ; GT BK CMP #$92 : BNE .keyend : CPX #$1A : BNE .keyend : LDA #$32 : BRA .keyend .thisdungeon @@ -264,15 +279,22 @@ Multiworld_AddReceivedItem_notCrystal: { TYA : STA $02E4 : PHX ; things we wrote over - LDA !MULTIWORLD_ITEM_PLAYER_ID : BEQ + + LDA !MULTIWORLD_ITEM_PLAYER_ID : BNE .remote + JML.l AddReceivedItem_notCrystal+5 + + .remote + JSL.l BossPrizeReceiveContextMatches : BCC .showSentTo + PHY : LDY $02D8 : JSL AddInventory : PLY + JSL.l Multiworld_PrintSentTo + JML.l AddReceivedItem_gfxHandling + + .showSentTo PHY : LDY $02D8 : JSL AddInventory : PLY - %Print_Text(HUD_SentTo, #$0010, !MULTIWORLD_ITEM_PLAYER_ID) + JSL.l Multiworld_PrintSentTo LDA #$33 : STA $012F JML.l AddReceivedItem_gfxHandling - + - JML.l AddReceivedItem_notCrystal+5 } Multiworld_Ancilla_ReceiveItem_stillInMotion: diff --git a/newitems.asm b/newitems.asm old mode 100755 new mode 100644 index 9bee8f4c..f0b31372 --- a/newitems.asm +++ b/newitems.asm @@ -40,6 +40,18 @@ ; #$90 - Big Keys ; #$A0 - Small Keys ; #$B0 - Bee Trap +; #$B6 - Green Pendant +; #$B7 - Blue Pendant +; #$B8 - Red Pendant +; #$B9 - Crystal 1 +; #$BA - Crystal 2 +; #$BB - Crystal 3 +; #$BC - Crystal 4 +; #$BD - Crystal 5 +; #$BE - Crystal 6 +; #$BF - Crystal 7 +; #$C0 - Key Rings +; #$CE - Small Key Ring of Current Dungeon ; #$FE - Server Request (Asychronous Chest) ; #$FF - Null Chest ;-------------------------------------------------------------------------------- @@ -224,38 +236,38 @@ AddReceivedItemExpandedGetItem: ++ LDA.b #$04 ; bow with arrow +++ - STA $7EF340 - .noequip - LDA !INVENTORY_SWAP_2 : ORA #$40 : STA !INVENTORY_SWAP_2 ; mark silver bow on y-toggle - BRL .done + STA $7EF340 + .noequip + LDA !INVENTORY_SWAP_2 : ORA #$40 : STA !INVENTORY_SWAP_2 ; mark silver bow on y-toggle + BRL .done + CMP.b #$4C : BNE + ; 50 bombs - ;LDA.b #$07 : STA $7EF370 ; upgrade bombs - LDA.b #50 : !SUB.l StartingMaxBombs : STA $7EF370 ; upgrade bombs - LDA.b #50 : STA $7EF375 ; fill bombs - BRL .done + ;LDA.b #$07 : STA $7EF370 ; upgrade bombs + LDA.b #50 : !SUB.l StartingMaxBombs : STA $7EF370 ; upgrade bombs + LDA.b #50 : STA $7EF375 ; fill bombs + BRL .done + CMP.b #$4D : BNE + ; 70 arrows - ;LDA #$07 : STA $7EF371 ; upgrade arrows - LDA.b #70 : !SUB.l StartingMaxArrows : STA $7EF371 ; upgrade arrows - LDA.b #70 : STA $7EF376 ; fill arrows - BRL .done + ;LDA #$07 : STA $7EF371 ; upgrade arrows + LDA.b #70 : !SUB.l StartingMaxArrows : STA $7EF371 ; upgrade arrows + LDA.b #70 : STA $7EF376 ; fill arrows + BRL .done + CMP.b #$4E : BNE + ; 1/2 magic - LDA $7EF37B : CMP #$02 : !BGE ++ - INC : STA $7EF37B ; upgrade magic - ++ - LDA.b #$80 : STA $7EF373 ; fill magic - BRL .done + LDA $7EF37B : CMP #$02 : !BGE ++ + INC : STA $7EF37B ; upgrade magic + ++ + LDA.b #$80 : STA $7EF373 ; fill magic + BRL .done + CMP.b #$4F : BNE + ; 1/4 magic - LDA.b #$02 : STA $7EF37B ; upgrade magic - LDA.b #$80 : STA $7EF373 ; fill magic - BRL .done + LDA.b #$02 : STA $7EF37B ; upgrade magic + LDA.b #$80 : STA $7EF373 ; fill magic + BRL .done + CMP.b #$50 : BNE + ; Master Sword (Safe) - LDA $7EF359 : CMP.b #$02 : !BGE + ; skip if we have a better sword - LDA.b #$02 : STA $7EF359 ; set master sword - BRL .done + LDA $7EF359 : CMP.b #$02 : !BGE + ; skip if we have a better sword + LDA.b #$02 : STA $7EF359 ; set master sword + BRL .done + CMP.b #$51 : BNE + ; +5 Bombs - LDA $7EF370 : !ADD.b #$05 : STA $7EF370 ; upgrade bombs +5 - LDA.l Upgrade5BombsRefill : STA $7EF375 ; fill bombs - BRL .done + LDA $7EF370 : !ADD.b #$05 : STA $7EF370 ; upgrade bombs +5 + LDA.l Upgrade5BombsRefill : STA $7EF375 ; fill bombs + BRL .done + CMP.b #$52 : BNE + ; +10 Bombs LDA $7EF370 : !ADD.b #$0A : STA $7EF370 ; upgrade bombs +10 LDA.l Upgrade10BombsRefill : STA $7EF375 ; fill bombs @@ -331,63 +343,63 @@ AddReceivedItemExpandedGetItem: BRL .done + CMP.b #$6B : BNE + ; Goal Collectable (Multi/Power Star) BRA .multi_collect - + CMP.b #$6C : BNE + ; Goal Collectable (Multi/Power Star) Alternate Graphic - .multi_collect - REP #$20 - LDA GoalItemRequirement : BEQ ++ - LDA !GOAL_COUNTER : INC : STA !GOAL_COUNTER + + CMP.b #$6C : BNE + ; Goal Collectable (Multi/Power Star) Alternate Graphic + .multi_collect + REP #$20 + LDA GoalItemRequirement : BEQ ++ + LDA !GOAL_COUNTER : INC : STA !GOAL_COUNTER CMP GoalItemRequirement : !BLT ++ SEP #$20 LDA TurnInGoalItems : BNE ++ JSL.l ActivateGoal - ++ - SEP #$20 - BRL .done - + CMP.b #$6D : BNE + ; Server Request F0 - JSL.l ItemGetServiceRequest_F0 - BRL .done - + CMP.b #$6E : BNE + ; Server Request F1 - JSL.l ItemGetServiceRequest_F1 - BRL .done - + CMP.b #$6F : BNE + ; Server Request F2 - JSL.l ItemGetServiceRequest_F2 - BRL .done + ++ + SEP #$20 + BRL .done + + CMP.b #$6D : BNE + ; Server Request F0 + JSL.l ItemGetServiceRequest_F0 + BRL .done + + CMP.b #$6E : BNE + ; Server Request F1 + JSL.l ItemGetServiceRequest_F1 + BRL .done + + CMP.b #$6F : BNE + ; Server Request F2 + JSL.l ItemGetServiceRequest_F2 + BRL .done ;+ CMP.b #$FE : BNE + ; Server Request (Null Chest) ; JSL.l ItemGetServiceRequest ; BRL .done - + CMP.b #$70 : !BLT + : CMP.b #$80 : !BGE + ; Free Map - AND #$0F : CMP #$08 : !BGE ++ - %ValueShift() - ORA $7EF368 : STA $7EF368 ; Map 1 - BRL .done + + CMP.b #$70 : !BLT + : CMP.b #$80 : !BGE + ; Free Map + AND #$0F : CMP #$08 : !BGE ++ + %ValueShift() + ORA $7EF368 : STA $7EF368 ; Map 1 + BRL .done ++ - !SUB #$08 - %ValueShift() - BIT.b #$C0 : BEQ +++ : LDA.b #$C0 : +++ ; Make Hyrule Castle / Sewers Count for Both - ORA $7EF369 : STA $7EF369 ; Map 2 - BRL .done - + CMP.b #$80 : !BLT + : CMP.b #$90 : !BGE + ; Free Compass - AND #$0F : CMP #$08 : !BGE ++ - %ValueShift() - ORA $7EF364 : STA $7EF364 ; Compass 1 + !SUB #$08 + %ValueShift() + BIT.b #$C0 : BEQ +++ : LDA.b #$C0 : +++ ; Make Hyrule Castle / Sewers Count for Both + ORA $7EF369 : STA $7EF369 ; Map 2 BRL .done + + CMP.b #$80 : !BLT + : CMP.b #$90 : !BGE + ; Free Compass + AND #$0F : CMP #$08 : !BGE ++ + %ValueShift() + ORA $7EF364 : STA $7EF364 ; Compass 1 + BRL .done ++ - !SUB #$08 - %ValueShift() - BIT.b #$C0 : BEQ +++ : LDA.b #$C0 : +++ ; Make Hyrule Castle / Sewers Count for Both - ORA $7EF365 : STA $7EF365 ; Compass 2 - BRL .done - + CMP.b #$90 : !BLT + : CMP.b #$A0 : !BGE + ; Free Big Key - AND #$0F : CMP #$08 : !BGE ++ - %ValueShift() - ORA $7EF366 : STA $7EF366 ; Big Key 1 + !SUB #$08 + %ValueShift() + BIT.b #$C0 : BEQ +++ : LDA.b #$C0 : +++ ; Make Hyrule Castle / Sewers Count for Both + ORA $7EF365 : STA $7EF365 ; Compass 2 BRL .done + + CMP.b #$90 : !BLT + : CMP.b #$A0 : !BGE + ; Free Big Key + AND #$0F : CMP #$08 : !BGE ++ + %ValueShift() + ORA $7EF366 : STA $7EF366 ; Big Key 1 + BRL .done ++ - !SUB #$08 - %ValueShift() - BIT.b #$C0 : BEQ +++ : LDA.b #$C0 : +++ ; Make Hyrule Castle / Sewers Count for Both - ORA $7EF367 : STA $7EF367 ; Big Key 2 - BRL .done + !SUB #$08 + %ValueShift() + BIT.b #$C0 : BEQ +++ : LDA.b #$C0 : +++ ; Make Hyrule Castle / Sewers Count for Both + ORA $7EF367 : STA $7EF367 ; Big Key 2 + BRL .done + CMP.b #$A0 : !BLT + : CMP.b #$B0 : !BGE + ; Free Small Key AND #$0F : TAX LDA $7EF37C, X : INC : STA $7EF37C, X ; Increment Key Count @@ -407,6 +419,36 @@ AddReceivedItemExpandedGetItem: LDA $7EF36F : INC : STA $7EF36F ++ BRL .done + + CMP.b #$CE : BNE + ; Small Key Ring (Current Dungeon) + LDA $040C : LSR : TAX + BRA .keyRingCount + + CMP.b #$C0 : !BLT + : CMP.b #$D0 : !BGE + ; Key Ring + AND #$0F : TAX + .keyRingCount + ; Key rings add the configured quantity for their dungeon and mirror the + ; HC/Sewers shared key count behavior used by single keys. + LDA.l KeyRingQuantities, X : BEQ .keyRingDone + PHA + CLC : ADC $7EF37C, X : STA $7EF37C, X + + CPX.b #$00 : BNE ++ + STA $7EF37D ; copy HC to sewers + ++ : CPX.b #$01 : BNE ++ + STA $7EF37C ; copy sewers to HC + ++ + + LDA.l GenericKeys : BEQ .keyRingDungeon + PLA : CLC : ADC $7EF36F : STA $7EF36F + BRL .done + .keyRingDungeon + TXA : ASL : CMP $040C : BNE .keyRingDiscard + PLA : CLC : ADC $7EF36F : STA $7EF36F + BRL .done + .keyRingDiscard + PLA + BRL .done + .keyRingDone + BRL .done + CMP.b #$B0 : BNE + ; Bee Trap LDA.b #$79 : JSL Sprite_SpawnDynamically : BMI + ; DashBeeHive_SpawnBee LDA $22 : STA $0D10, Y : LDA $23 : STA $0D30, Y ; from enemizer's Spawn_Bees @@ -438,6 +480,7 @@ AddReceivedItemExpandedGetItem: LDA.b #$1F : STA $0E00, Y LDA.b #$03 : STA $0E40, Y + + JSL.l MaybeSetOrdinaryBossPrizeBits : BCS .done .done PLX LDA $02E9 : CMP.b #$01 ; thing we wrote over @@ -464,7 +507,12 @@ RTL AddReceivedItemExpanded: { PHA : PHX - LDA RemoteItems : BEQ + : LDA !MULTIWORLD_ITEM_PLAYER_ID : BEQ + + ; Boss prizes may be remote items, so set the recipient before normal + ; local/remote inventory handling runs. They still need the normal + ; receive-item object path for the prize fanfare and dungeon exit. + JSL.l BossPrizeApplyItemPlayer + JSL.l BossPrizeReceiveContextMatches : BCS .normalReceive + LDA RemoteItems : BEQ .normalReceive : LDA !MULTIWORLD_ITEM_PLAYER_ID : BEQ .normalReceive LDA $02E9 : BEQ ++ : CMP #$03 : BNE +++ : ++ ; fromTextOrObject LDA $0345 : BEQ ++ : LDA.b #$04 : ++ : STA $5D ; Restore Link to his swimming state @@ -474,7 +522,8 @@ AddReceivedItemExpanded: STZ $02D8 : STZ $02D9 : STZ $02E9 PHY : LDY.b #$00 : JSL AddInventory : PLY PLX : PLA : RTL - + + + .normalReceive JSL.l PreItemGet @@ -485,6 +534,9 @@ AddReceivedItemExpanded: JSR IncrementItemCounters + + ; Boss-prize display items are resolved before collection, so queue any + ; free-dungeon-item notice after substitution. + JSL.l BossPrizeQueueFreeItemNotice LDA $02D8 ; Item Value CMP.b #$16 : BEQ .bottle ; Bottle @@ -614,7 +666,7 @@ AddReceivedItemExpanded: db -5 ; Master Sword (Safe) db -4, -4, -4, -4 ; +5/+10 Bomb Arrows db -4, -4, -4 ; 3x Programmable Item - db -4 ; Upgrade-Only Sivler Arrows + db -2 ; Upgrade-Only Silver Arrows db -4 ; 1 Rupoor db -4 ; Null Item db -4, -4, -4 ; Red, Blue & Green Clocks @@ -671,7 +723,7 @@ AddReceivedItemExpanded: db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; Free Small Key db 0 ; Bee Trap db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Unused - db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Unused + db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Key Ring db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Unused db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Unused @@ -696,7 +748,7 @@ AddReceivedItemExpanded: db $18 ; Master Sword (Safe) db $3D, $3E, $3F, $40 ; +5/+10 Bomb Arrows db $00, $00, $00 ; 3x Programmable Item - db $41 ; Upgrade-Only Sivler Arrows + db $33 ; Upgrade-Only Silver Arrows db $24 ; 1 Rupoor db $47 ; Null Item db $48, $48, $48 ; Red, Blue & Green Clocks @@ -717,8 +769,11 @@ AddReceivedItemExpanded: db $47 ; Bee Trap db $47, $2C, $47, $47 ; Fae, Bee, Jar, Apple - db $47, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49 ; Unused - db $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49 ; Unused + db $47, $23, $23, $23, $28, $28, $28, $28, $28, $28, $28 ; Unused, Pendants, Crystals + db $41, $41, $41, $41 ; Key Ring + db $41, $41, $41, $41 + db $41, $41, $41, $41 + db $41, $41, $41, $41 db $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49 ; Unused db $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49 ; Unused @@ -742,7 +797,7 @@ AddReceivedItemExpanded: db $00 ; Master Sword (Safe) db $02, $02, $02, $02 ; +5/+10 Bomb Arrows db $02, $02, $02 ; 3x Programmable Item - db $02 ; Upgrade-Only Sivler Arrows + db $00 ; Upgrade-Only Silver Arrows db $00 ; 1 Rupoor db $02 ; Null Item db $02, $02, $02 ; Red, Blue & Green Clocks @@ -759,7 +814,7 @@ AddReceivedItemExpanded: db $02 ; Bee Trap db $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02 ; Unused - db $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02 ; Unused + db $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02 ; Key Ring db $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02 ; Unused db $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02 ; Unused db $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02, $02 ; Unused @@ -799,7 +854,7 @@ AddReceivedItemExpanded: db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; Free Big Key db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; Free Small Key db 1 ; Bee Trap - db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; Unused + db 4, 4, 4, 4, 4, 4, 2, 1, 2, 2, 2, 2, 1, 1, 2 ; Unused, Pendants, Crystals db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; Unused db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; Unused db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; Unused @@ -1158,6 +1213,7 @@ IncrementItemCounters: RTS ;-------------------------------------------------------------------------------- AttemptItemSubstitution: + JSR SubstituteBombsWithRupeesWhenCapacityIsZero PHX : PHA LDX.b #$00 - @@ -1180,6 +1236,11 @@ AttemptItemSubstitution: PLA : PLX RTS ;-------------------------------------------------------------------------------- +; Long-call wrapper for boss-prize code that lives outside this bank. +AttemptItemSubstitutionLong: + JSR AttemptItemSubstitution +RTL +;-------------------------------------------------------------------------------- CountBottles: PHX LDX.b #$00 diff --git a/pendantcrystalhud.asm b/pendantcrystalhud.asm index 9d01296b..eeff56bd 100644 --- a/pendantcrystalhud.asm +++ b/pendantcrystalhud.asm @@ -291,75 +291,114 @@ DrawHUDDungeonItems: ; write black LDX.w #$0000 ; Paint entire box black & draw empty pendants and crystals - - LDA #$24F5 : STA $1686, X : STA $16C6, X + LDA #$24F5 : STA $1686, X : STA $16C6, X : STA $1706, X INX #2 : CPX.w #$0020 : BCC - + ; Dungeon clears used by the Dungeons goal. Hyrule Castle/Sewers has no boss flag. + LDA.w #$2828 : STA $1684 ; dungeon clear icon + LDA $7EF041 : AND.w #$0008 : BEQ + ; Agahnim 1 + LDA.w #$2826 : STA $1688 + + + LDA $7EF191 : AND.w #$0008 : BEQ + ; Eastern Palace / Armos Knights + LDA.w #$2826 : STA $168C + + + LDA $7EF067 : AND.w #$0008 : BEQ + ; Desert Palace / Lanmolas + LDA.w #$2826 : STA $168E + + + LDA $7EF00F : AND.w #$0008 : BEQ + ; Tower of Hera / Moldorm + LDA.w #$2826 : STA $1690 + + + LDA $7EF0B5 : AND.w #$0008 : BEQ + ; Palace of Darkness / Helmasaur King + LDA.w #$2826 : STA $1694 + + + LDA $7EF00D : AND.w #$0008 : BEQ + ; Swamp Palace / Arrghus + LDA.w #$2826 : STA $1696 + + + LDA $7EF053 : AND.w #$0008 : BEQ + ; Skull Woods / Mothula + LDA.w #$2826 : STA $1698 + + + LDA $7EF159 : AND.w #$0008 : BEQ + ; Thieves' Town / Blind + LDA.w #$2826 : STA $169A + + + LDA $7EF1BD : AND.w #$0008 : BEQ + ; Ice Palace / Kholdstare + LDA.w #$2826 : STA $169C + + + LDA $7EF121 : AND.w #$0008 : BEQ + ; Misery Mire / Vitreous + LDA.w #$2826 : STA $169E + + + LDA $7EF149 : AND.w #$0008 : BEQ + ; Turtle Rock / Trinexx + LDA.w #$2826 : STA $16A0 + + + LDA $7EF01B : AND.w #$0008 : BEQ + ; Agahnim 2 + LDA.w #$2826 : STA $16A4 + + + LDA !HUD_FLAG : AND.w #$0020 : BEQ + : BRL +++ : + LDA HUDDungeonItems : AND.w #$0001 : BNE + : BRL ++ : + - LDA.w #$2810 : STA $1684 ; small keys icon + LDA.w #$2810 : STA $16C4 ; small keys icon SEP #$20 ; set 8-bit accumulator ; Small Keys - LDA.b #$16 : !ADD $7EF37D : STA $1686 : LDA.b #$28 : ADC #$00 : sta.w $1686+1 ; Hyrule Castle - LDA.b #$16 : !ADD $7EF380 : STA $1688 : LDA.b #$28 : ADC #$00 : sta.w $1688+1 ; Agahnims Tower + LDA.b #$16 : !ADD $7EF37D : STA $16C6 : LDA.b #$28 : ADC #$00 : sta.w $16C6+1 ; Hyrule Castle + LDA.b #$16 : !ADD $7EF380 : STA $16C8 : LDA.b #$28 : ADC #$00 : sta.w $16C8+1 ; Agahnims Tower - LDA.b #$16 : !ADD $7EF37E : STA $168C : LDA.b #$28 : ADC #$00 : sta.w $168C+1 ; Eastern - LDA.b #$16 : !ADD $7EF37F : STA $168E : LDA.b #$28 : ADC #$00 : sta.w $168E+1 ; Desert - LDA.b #$16 : !ADD $7EF386 : STA $1690 : LDA.b #$28 : ADC #$00 : sta.w $1690+1 ; Hera + LDA.b #$16 : !ADD $7EF37E : STA $16CC : LDA.b #$28 : ADC #$00 : sta.w $16CC+1 ; Eastern + LDA.b #$16 : !ADD $7EF37F : STA $16CE : LDA.b #$28 : ADC #$00 : sta.w $16CE+1 ; Desert + LDA.b #$16 : !ADD $7EF386 : STA $16D0 : LDA.b #$28 : ADC #$00 : sta.w $16D0+1 ; Hera - LDA.b #$16 : !ADD $7EF382 : STA $1694 : LDA.b #$28 : ADC #$00 : sta.w $1694+1 ; PoD - LDA.b #$16 : !ADD $7EF381 : STA $1696 : LDA.b #$28 : ADC #$00 : sta.w $1696+1 ; Swamp - LDA.b #$16 : !ADD $7EF384 : STA $1698 : LDA.b #$28 : ADC #$00 : sta.w $1698+1 ; Skull Woods - LDA.b #$16 : !ADD $7EF387 : STA $169A : LDA.b #$28 : ADC #$00 : sta.w $169A+1 ; Thieves Town - LDA.b #$16 : !ADD $7EF385 : STA $169C : LDA.b #$28 : ADC #$00 : sta.w $169C+1 ; Ice - LDA.b #$16 : !ADD $7EF383 : STA $169E : LDA.b #$28 : ADC #$00 : sta.w $169E+1 ; Mire - LDA.b #$16 : !ADD $7EF388 : STA $16A0 : LDA.b #$28 : ADC #$00 : sta.w $16A0+1 ; Turtle Rock + LDA.b #$16 : !ADD $7EF382 : STA $16D4 : LDA.b #$28 : ADC #$00 : sta.w $16D4+1 ; PoD + LDA.b #$16 : !ADD $7EF381 : STA $16D6 : LDA.b #$28 : ADC #$00 : sta.w $16D6+1 ; Swamp + LDA.b #$16 : !ADD $7EF384 : STA $16D8 : LDA.b #$28 : ADC #$00 : sta.w $16D8+1 ; Skull Woods + LDA.b #$16 : !ADD $7EF387 : STA $16DA : LDA.b #$28 : ADC #$00 : sta.w $16DA+1 ; Thieves Town + LDA.b #$16 : !ADD $7EF385 : STA $16DC : LDA.b #$28 : ADC #$00 : sta.w $16DC+1 ; Ice + LDA.b #$16 : !ADD $7EF383 : STA $16DE : LDA.b #$28 : ADC #$00 : sta.w $16DE+1 ; Mire + LDA.b #$16 : !ADD $7EF388 : STA $16E0 : LDA.b #$28 : ADC #$00 : sta.w $16E0+1 ; Turtle Rock - LDA.b #$16 : !ADD $7EF389 : STA $16A4 : LDA.b #$28 : ADC #$00 : sta.w $16A4+1 ; Ganon's Tower + LDA.b #$16 : !ADD $7EF389 : STA $16E4 : LDA.b #$28 : ADC #$00 : sta.w $16E4+1 ; Ganon's Tower REP #$20 ; set 16-bit accumulator ++ ; Big Keys LDA HUDDungeonItems : AND.w #$0002 : BNE + : BRL ++ : + - LDA.w #$2811 : STA $16C4 ; big key icon + LDA.w #$2811 : STA $1704 ; big key icon LDA $7EF367 : AND.w #$0040 : BEQ + ; Hyrule Castle - LDA.w #$2826 : STA $16C6 + LDA.w #$2826 : STA $1706 + LDA $7EF367 : AND.w #$0008 : BEQ + ; Agahnims Tower - LDA.w #$2826 : STA $16C8 + LDA.w #$2826 : STA $1708 + LDA $7EF367 : AND.w #$0020 : BEQ + ; Eastern - LDA.w #$2826 : STA $16CC + LDA.w #$2826 : STA $170C + LDA $7EF367 : AND.w #$0010 : BEQ + ; Desert - LDA.w #$2826 : STA $16CE + LDA.w #$2826 : STA $170E + LDA $7EF366 : AND.w #$0020 : BEQ + ; Hera - LDA.w #$2826 : STA $16D0 + LDA.w #$2826 : STA $1710 + LDA $7EF367 : AND.w #$0002 : BEQ + ; PoD - LDA.w #$2826 : STA $16D4 + LDA.w #$2826 : STA $1714 + LDA $7EF367 : AND.w #$0004 : BEQ + ; Swamp - LDA.w #$2826 : STA $16D6 + LDA.w #$2826 : STA $1716 + LDA $7EF366 : AND.w #$0080 : BEQ + ; Skull Woods - LDA.w #$2826 : STA $16D8 + LDA.w #$2826 : STA $1718 + LDA $7EF366 : AND.w #$0010 : BEQ + ; Thieves Town - LDA.w #$2826 : STA $16DA + LDA.w #$2826 : STA $171A + LDA $7EF366 : AND.w #$0040 : BEQ + ; Ice - LDA.w #$2826 : STA $16DC + LDA.w #$2826 : STA $171C + LDA $7EF367 : AND.w #$0001 : BEQ + ; Mire - LDA.w #$2826 : STA $16DE + LDA.w #$2826 : STA $171E + LDA $7EF366 : AND.w #$0008 : BEQ + ; Turtle Rock - LDA.w #$2826 : STA $16E0 + LDA.w #$2826 : STA $1720 + LDA $7EF366 : AND.w #$0004 : BEQ + ; Ganon's Tower - LDA.w #$2826 : STA $16E4 + LDA.w #$2826 : STA $1724 + ++ @@ -368,89 +407,89 @@ DrawHUDDungeonItems: LDA !HUD_FLAG : AND.w #$0020 : BNE + : BRL +++ : + ; Maps LDA HUDDungeonItems : AND.w #$0004 : BNE + : BRL ++ : + - LDA.w #$2821 : STA $1684 ; map icon + LDA.w #$2821 : STA $16C4 ; map icon LDA $7EF369 : AND.w #$0040 : BEQ + ; Hyrule Castle - LDA.w #$2826 : STA $1686 + LDA.w #$2826 : STA $16C6 + LDA $7EF369 : AND.w #$0008 : BEQ + ; Agahnims Tower - LDA.w #$2826 : STA $1688 + LDA.w #$2826 : STA $16C8 + LDA $7EF369 : AND.w #$0020 : BEQ + ; Eastern - LDA.w #$2826 : STA $168C + LDA.w #$2826 : STA $16CC + LDA $7EF369 : AND.w #$0010 : BEQ + ; Desert - LDA.w #$2826 : STA $168E + LDA.w #$2826 : STA $16CE + LDA $7EF368 : AND.w #$0020 : BEQ + ; Hera - LDA.w #$2826 : STA $1690 + LDA.w #$2826 : STA $16D0 + LDA $7EF369 : AND.w #$0002 : BEQ + ; PoD - LDA.w #$2826 : STA $1694 + LDA.w #$2826 : STA $16D4 + LDA $7EF369 : AND.w #$0004 : BEQ + ; Swamp - LDA.w #$2826 : STA $1696 + LDA.w #$2826 : STA $16D6 + LDA $7EF368 : AND.w #$0080 : BEQ + ; Skull Woods - LDA.w #$2826 : STA $1698 + LDA.w #$2826 : STA $16D8 + LDA $7EF368 : AND.w #$0010 : BEQ + ; Thieves Town - LDA.w #$2826 : STA $169A + LDA.w #$2826 : STA $16DA + LDA $7EF368 : AND.w #$0040 : BEQ + ; Ice - LDA.w #$2826 : STA $169C + LDA.w #$2826 : STA $16DC + LDA $7EF369 : AND.w #$0001 : BEQ + ; Mire - LDA.w #$2826 : STA $169E + LDA.w #$2826 : STA $16DE + LDA $7EF368 : AND.w #$0008 : BEQ + ; Turtle Rock - LDA.w #$2826 : STA $16A0 + LDA.w #$2826 : STA $16E0 + LDA $7EF368 : AND.w #$0004 : BEQ + ; Ganon's Tower - LDA.w #$2826 : STA $16A4 + LDA.w #$2826 : STA $16E4 + ++ ; Compasses LDA HUDDungeonItems : AND.w #$0008 : BNE + : BRL ++ : + - LDA.w #$2C20 : STA $16C4 ; compass icon + LDA.w #$2C20 : STA $1704 ; compass icon LDA $7EF365 : AND.w #$0040 : BEQ + ; Hyrule Castle - LDA.w #$2C26 : STA $16C6 + LDA.w #$2C26 : STA $1706 + LDA $7EF365 : AND.w #$0008 : BEQ + ; Agahnims Tower - LDA.w #$2C26 : STA $16C8 + LDA.w #$2C26 : STA $1708 + LDA $7EF365 : AND.w #$0020 : BEQ + ; Eastern - LDA.w #$2C26 : STA $16CC + LDA.w #$2C26 : STA $170C + LDA $7EF365 : AND.w #$0010 : BEQ + ; Desert - LDA.w #$2C26 : STA $16CE + LDA.w #$2C26 : STA $170E + LDA $7EF364 : AND.w #$0020 : BEQ + ; Hera - LDA.w #$2C26 : STA $16D0 + LDA.w #$2C26 : STA $1710 + LDA $7EF365 : AND.w #$0002 : BEQ + ; PoD - LDA.w #$2C26 : STA $16D4 + LDA.w #$2C26 : STA $1714 + LDA $7EF365 : AND.w #$0004 : BEQ + ; Swamp - LDA.w #$2C26 : STA $16D6 + LDA.w #$2C26 : STA $1716 + LDA $7EF364 : AND.w #$0080 : BEQ + ; Skull Woods - LDA.w #$2C26 : STA $16D8 + LDA.w #$2C26 : STA $1718 + LDA $7EF364 : AND.w #$0010 : BEQ + ; Thieves Town - LDA.w #$2C26 : STA $16DA + LDA.w #$2C26 : STA $171A + LDA $7EF364 : AND.w #$0040 : BEQ + ; Ice - LDA.w #$2C26 : STA $16DC + LDA.w #$2C26 : STA $171C + LDA $7EF365 : AND.w #$0001 : BEQ + ; Mire - LDA.w #$2C26 : STA $16DE + LDA.w #$2C26 : STA $171E + LDA $7EF364 : AND.w #$0008 : BEQ + ; Turtle Rock - LDA.w #$2C26 : STA $16E0 + LDA.w #$2C26 : STA $1720 + LDA $7EF364 : AND.w #$0004 : BEQ + ; Ganon's Tower - LDA.w #$2C26 : STA $16E4 + LDA.w #$2C26 : STA $1724 + ++ : +++ PLP @@ -588,4 +627,4 @@ dw $A8FB, $A8F9, $A8F9, $A8F9, $A8F9, $A8F9, $A8F9, $A8F9, $A8F9, $E8FB ;0x1A - Ganon's Tower ;0x1C - ??? possibly unused. (Were they planning two extra dungeons perhaps?) ;0x1E - ??? possibly unused. -;================================================================================ \ No newline at end of file +;================================================================================ diff --git a/shopkeeper.asm b/shopkeeper.asm index 680235d9..7d6e3529 100644 --- a/shopkeeper.asm +++ b/shopkeeper.asm @@ -355,10 +355,17 @@ SetupShopItem: JSL GetRandomInt : AND #$3F : STA !BEE_TRAP_DISGUISE BNE ++ : LDA #$49 : ++ : CMP #$26 : BNE ++ : LDA #$6A : ++ ; if 0 (fighter's sword + shield), set to just sword, if filled container (bugged palette), switch to triforce piece STA.l !SHOP_INVENTORY_DISGUISE, X : PLX - + : TAY + + + PHA : PHX + LDA #0 : XBA : TYA : LSR #2 : TAX + LDA.l !SHOP_INVENTORY_PLAYER, X : STA !MULTIWORLD_SPRITEITEM_PLAYER_ID + PLX : PLA + TAY REP #$20 ; set 16-bit accumulator LDA 1,s : TAX : LDA.l .tile_offsets, X : TAX JSR LoadTile + SEP #$20 ; set 8-bit accumulator + LDA #$00 : STA !MULTIWORLD_SPRITEITEM_PLAYER_ID PLY : PLX RTS .tile_offsets @@ -682,6 +689,8 @@ Shopkeeper_BuyItem: LDA !SHOP_TYPE : AND.b #$80 : BEQ + : BRL .buy : + ; don't charge if this is a take-any .custom_price + ; High bit marks non-rupee prices; split that path so rupee prices keep + ; the vanilla 16-bit rupee comparison below. LDA !SHOP_INVENTORY+2, X : AND.b #$80 : BNE .price_is_not_rupees ; i honestly can't think of a better way to do this block because i haven't done asm in like 8 months JMP .price_is_rupees .price_is_not_rupees @@ -957,6 +966,11 @@ Shopkeeper_DrawNextItem: ++ SEP #$20 ; set 8-bit accumulator PLY + + PHX + LDA #0 : XBA : TXA : LSR #2 : TAX + LDA.l !SHOP_INVENTORY_PLAYER, X : STA !MULTIWORLD_SPRITEITEM_PLAYER_ID + PLX PHX : LDA #0 : XBA : TXA : LSR #2 : TAX : LDA.l !SHOP_INVENTORY_DISGUISE, X : PLX : CMP #$0 : BNE ++ LDA.l !SHOP_INVENTORY, X ; get item palette @@ -1035,6 +1049,7 @@ Shopkeeper_DrawNextItem: + .next + LDA.b #$00 : STA !MULTIWORLD_SPRITEITEM_PLAYER_ID INY INX #4 RTS diff --git a/stats.asm b/stats.asm index e839b763..1de59cc4 100644 --- a/stats.asm +++ b/stats.asm @@ -134,7 +134,7 @@ ;-------------------------------------------------------------------------------- ; $7EF4F0 - Fresh File Marker. ;-------------------------------------------------------------------------------- -; $7EF4F1 - 7EF4FD - Free space +; $7EF4F1 - 7EF4FD - Read in-game hint flags ;-------------------------------------------------------------------------------- ; 7EF4FEw[2] - Save Checksum ;-------------------------------------------------------------------------------- @@ -265,8 +265,14 @@ CountChestKey: ; called by neighbor functions PHA : PHX LDA !MULTIWORLD_ITEM_PLAYER_ID : bne .end CPY #$24 : BEQ + ; small key for this dungeon - use $040C - CPY #$A0 : !BLT .end ; Ignore most items - CPY #$AE : !BGE .end ; Ignore reserved key and generic key + CPY #$CE : BEQ + ; small key ring for this dungeon - use $040C + CPY #$A0 : !BLT .checkKeyRing ; Ignore most items + CPY #$AE : !BLT .dungeonKey ; Count dungeon small keys + .checkKeyRing + ; Dungeon-specific key rings count as key checks for the owning dungeon. + CPY #$C0 : !BLT .end + CPY #$CE : !BGE .end ; Ignore reserved key ring slots + .dungeonKey TYA : AND.B #$0F : BNE ++ ; If this is a sewers key, instead count it as an HC key INC ++ TAX : BRA .count ; use Key id instead of $040C (Keysanity) @@ -288,7 +294,14 @@ CountBonkItem: ; called from GetBonkItem in bookofmudora.asm %GetPossiblyEncryptedItem(BonkKey_GTower, HeartPieceIndoorValues) : BRA ++ + LDA.B #$24 ; default to small key ++ - CMP #$24 : BNE + + CMP #$24 : BEQ .count + CMP #$A0 : !BLT .checkKeyRing + CMP #$AE : !BLT .count + .checkKeyRing + ; Bonk/dig style key drops can also resolve to key rings. + CMP #$C0 : !BLT + + CMP #$CE : !BGE + + .count PHY TAY : JSR CountChestKey PLY @@ -513,4 +526,4 @@ NoEGtoTriforce: ; If it is not possible to beat ganon, then make it impossible t JML $02B797 .no_triforce - JML $02B7A1 \ No newline at end of file + JML $02B7A1 diff --git a/swordswap.asm b/swordswap.asm index 9901dcf2..70b8abef 100644 --- a/swordswap.asm +++ b/swordswap.asm @@ -19,26 +19,6 @@ ; ;JSL ItemSet_SmithSword - too early ;JML.l Smithy_DoesntHaveSword ;================================================================================ -;LoadSwordForDamage: -; LDA $7EF359 : CMP #$04 : BNE .done ; skip if not gold sword -; LDA $1B : BEQ + ; skip if outdoors -; LDA $A0 : CMP #41 : BNE + ; decimal 41 ; skip if not in the mothula room -; LDA #$03 ; pretend we're using tempered -; BRA .done -; + -; LDA #$04 ; nvm gold sword is fine -; .done -;RTL -;================================================================================ -LoadSwordForDamage: - LDA $0E20, X : CMP.b #$88 : BNE .notMoth - JSR.w LoadModifiedSwordLevel ; load normal sword value - CMP.b #$04 : !BLT + : DEC : + ; if it's gold sword, change it to tempered - RTL - .notMoth - JSR.w LoadModifiedSwordLevel ; load normal sword value -RTL -;================================================================================ ;!StalfosBombDamage = "$7F509D" LookupDamageLevel: CPX.w #$0918 : BNE + diff --git a/tables.asm b/tables.asm index e03de986..c6221939 100644 --- a/tables.asm +++ b/tables.asm @@ -179,9 +179,9 @@ InvincibleGanon: db #$00 ; #$00 = Off (default) ; #$01 = On -; #$02 = Require All Dungeons -; #$03 = Require "NumberOfCrystalsRequiredForGanon" Crystals and Aga2 -; #$04 = Require "NumberOfCrystalsRequiredForGanon" Crystals +; #$02 = Require "GanonRequirementCount" dungeon completions +; #$03 = Require "GanonRequirementCount" Crystals and Aga2 +; #$04 = Require "GanonRequirementCount" Crystals ; #$05 = Require "GoalItemRequirement" Goal Items ; #$06 = Require Master sword pedestal to be pulled ;-------------------------------------------------------------------------------- @@ -289,9 +289,10 @@ CrystalPendantFlags_2: org $30805E ; PC 0x18005E - Number of crystals required to enter GT NumberOfCrystalsRequiredForTower: db #$07 ; #$07 = 7 Crystals -org $30805F ; PC 0x18005F - Number of crystals required to kill Ganon +org $30805F ; PC 0x18005F - Count required to kill Ganon +GanonRequirementCount: NumberOfCrystalsRequiredForGanon: -db #$07 ; #$07 = 7 Crystals +db #$07 ; Crystals for modes $03/$04, dungeon completions for mode $02 ;-------------------------------------------------------------------------------- org $308060 ; PC 0x180060 - 0x18007E ProgrammableItemLogicJump_1: @@ -941,7 +942,13 @@ db #$00 RainDeathRefillArrows_Mantle: db #$00 ;================================================================================ -; 0x18018E - 0x18018F (unused) +org $30818E ; PC 0x18018E +BossPrizeShuffle: +db #$00 ; #$00 = vanilla boss prizes (default) - #$01 = shuffled boss prizes use ordinary item IDs and warp after collection +;================================================================================ +org $30818F ; PC 0x18018F +WallmastersStayDead: +db #$00 ; #$00 = vanilla behavior (default) - #$01 = killing a Wallmaster stops further Wallmaster spawns ;================================================================================ org $308190 ; PC 0x180190 - 0x180192 TimerStyle: @@ -959,7 +966,30 @@ org $308194 ; PC 0x180194 TurnInGoalItems: db #$01 ; #$00 = Instant win if last goal item collected. $01 = (Default) must turn in goal items ;-------------------------------------------------------------------------------- -; 0x180195 - 0x1801FF (unused) +org $308195 ; PC 0x180195 - 0x18019E [encrypted] +BossPrizeValues: +BossPrize_ArmosKnights: + db #$B6 ; Green Pendant +BossPrize_Lanmolas: + db #$B7 ; Blue Pendant +BossPrize_Moldorm: + db #$B8 ; Red Pendant +BossPrize_HelmasaurKing: + db #$B9 ; Crystal 1 +BossPrize_Arrghus: + db #$BA ; Crystal 2 +BossPrize_Mothula: + db #$BB ; Crystal 3 +BossPrize_Blind: + db #$BC ; Crystal 4 +BossPrize_Kholdstare: + db #$BD ; Crystal 5 +BossPrize_Vitreous: + db #$BE ; Crystal 6 +BossPrize_Trinexx: + db #$BF ; Crystal 7 +;-------------------------------------------------------------------------------- +; 0x18019F - 0x1801FF (unused) ;================================================================================ org $308200 ; PC 0x180200 - 0x18020F RedClockAmount: @@ -1401,6 +1431,22 @@ db $04 ;AD - Small Key of Ganon's Tower ;AE - Reserved ;AF - Generic Small Key +;C0 - Small Key Ring of Sewers +;C1 - Small Key Ring of Hyrule Castle +;C2 - Small Key Ring of Eastern Palace +;C3 - Small Key Ring of Desert Palace +;C4 - Small Key Ring of Agahnim's Tower +;C5 - Small Key Ring of Swamp Palace +;C6 - Small Key Ring of Dark Palace +;C7 - Small Key Ring of Misery Mire +;C8 - Small Key Ring of Skull Woods +;C9 - Small Key Ring of Ice Palace +;CA - Small Key Ring of Tower of Hera +;CB - Small Key Ring of Thieves' Town +;CC - Small Key Ring of Turtle Rock +;CD - Small Key Ring of Ganon's Tower +;CE - Small Key Ring of Current Dungeon +;CF - Reserved ;================================================================================ ;;Residual Portal ;org $0283E0 ; PC 0x103E0 (Bank02.asm:816) (BNE) @@ -1501,11 +1547,18 @@ dw #9999 ; Rupee Limit ; $7F5042 - Tile Upload Offset Override (Low) ; $7F5043 - Tile Upload Offset Override (High) ; $7F5044 - $7F5046 - NMI Auxiliary Function -; $7F5047 - $7F504E - Unused +; $7F5047 - Boss Prize Active Flag +; $7F5048 - $7F5049 - Boss Prize Room ID +; $7F504A - Boss Prize Ancilla Slot +; $7F504B - Boss Prize Narrow Shadow Flag +; $7F504C - Boss Prize Draw Active Flag +; $7F504D - Boss Prize Display Item +; $7F504E - Unused ; $7F504F - $7F506F - Shop Block ; $7F5070 - Reserved for OneMind ; $7F5071 - Reserved for OneMind -; $7F5072 - $7F507D - Unused +; $7F5072 - Boss Heart Fanfare / Normal Sound Suppress Flag +; $7F5073 - $7F507D - Unused ; $7F507E - Clock Status ; $7F507F - Always Zero ; $7F5080 - $7F5083 - Clock Hours @@ -1917,8 +1970,47 @@ db #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, db #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00 db #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00, #$00 +; 0x186390 - 18639F +org $30E390 ; PC 0x186390 +; Number of small keys awarded by each dungeon key ring, indexed by dungeon ID. +KeyRingQuantities: +db $04, $04, $02, $04, $04, $06, $06, $06, $05, $06, $01, $03, $06, $08, $00, $00 + +;-------------------------------------------------------------------------------- +org $30E3A0 ; PC 0x1863A0 - 0x1863A9 +; Multiworld player IDs for each shuffled boss prize, indexed in boss-room order. +BossPrize_ArmosKnights_Player: +db #$00 +BossPrize_Lanmolas_Player: +db #$00 +BossPrize_Moldorm_Player: +db #$00 +BossPrize_HelmasaurKing_Player: +db #$00 +BossPrize_Arrghus_Player: +db #$00 +BossPrize_Mothula_Player: +db #$00 +BossPrize_Blind_Player: +db #$00 +BossPrize_Kholdstare_Player: +db #$00 +BossPrize_Vitreous_Player: +db #$00 +BossPrize_Trinexx_Player: +db #$00 + +;-------------------------------------------------------------------------------- +org $30E3B0 ; PC 0x1863B0 - 0x1864AF +; AP in-game hint tracking table. +; Entry format: dw message_id : db flag_byte_offset, flag_bit_mask +; $FFFF terminates the table. +HintReadTable: +dw $FFFF : db $FF, $FF +warnpc $30E4B0 + ;-------------------------------------------------------------------------------- -; 0x186380 - 187FFF (unused) +; 0x1864B0 - 187FFF (unused) ;-------------------------------------------------------------------------------- org $30EFFF ; PC 0x186FFF diff --git a/tablets.asm b/tablets.asm index 338bef70..8218c641 100644 --- a/tablets.asm +++ b/tablets.asm @@ -95,7 +95,7 @@ IsMedallion: RTL ;-------------------------------------------------------------------------------- LoadNarrowObject: - LDA.l AddReceivedItemExpanded_wide_item_flag, X : STA ($92), Y ; AddReceiveItem.wide_item_flag? + JSL.l BossPrizeLoadNarrowObject RTL ;-------------------------------------------------------------------------------- DrawNarrowDroppedObject: @@ -132,4 +132,4 @@ DrawNarrowDroppedObject: PLY .large_sprite RTL -;-------------------------------------------------------------------------------- \ No newline at end of file +;-------------------------------------------------------------------------------- diff --git a/utilities.asm b/utilities.asm index ae3ce088..73b50560 100644 --- a/utilities.asm +++ b/utilities.asm @@ -4,12 +4,43 @@ !PROGRESSIVE_SHIELD = "$7EF416" ; ss-- ---- !BEE_TRAP_DISGUISE = "$7EF4DA" ;-------------------------------------------------------------------------------- +SubstituteBombsWithRupeesWhenCapacityIsZero: + PHA + LDA $7EF370 + !ADD.l StartingMaxBombs + BEQ .substitute + PLA +RTS + +.substitute + PLA + CMP.b #$27 : BNE + ; Bomb + LDA.b #$34 ; Green Rupee + RTS + + + CMP.b #$28 : BNE + ; 3 Bombs + LDA.b #$35 ; Blue Rupee + RTS + + + CMP.b #$31 : BNE + ; 10 Bombs + LDA.b #$36 ; Red Rupee + + +RTS +;-------------------------------------------------------------------------------- ; GetSpriteTile ; in: A - Loot ID ; out: A - Sprite GFX ID ;-------------------------------------------------------------------------------- GetSpriteID: - ;JSR AttemptItemSubstitution + ; Skip local capacity substitutions for items that will be sent to another player. + PHA + LDA !MULTIWORLD_SPRITEITEM_PLAYER_ID : BNE + + PLA + JSR SubstituteBombsWithRupeesWhenCapacityIsZero + BRA ++ + + + PLA + ++ CMP.b #$16 : BEQ .bottle ; Bottle CMP.b #$2B : BEQ .bottle ; Red Potion w/bottle CMP.b #$2C : BEQ .bottle ; Green Potion w/bottle @@ -49,7 +80,7 @@ GetSpriteID: PHX PHB : PHK : PLB ;-------- - TAX : LDA.l .gfxSlots, X ; look up item gfx + TAX : LDA.w .gfxSlots, X ; look up item gfx PLB : PLX CMP.b #$F8 : !BGE .specialHandling RTL @@ -142,7 +173,7 @@ RTL db $44 ; Safe Master Sword db $3D, $3E, $3F, $40 ; Bomb & Arrow +5/+10 db $2C, $00, $00 ; 3x Programmable Item - db $41 ; Upgrade-Only Silver Arrows + db $33 ; Upgrade-Only Silver Arrows db $24 ; 1 Rupoor db $47 ; Null Item db $48, $48, $48 ; Red, Blue & Green Clocks @@ -168,10 +199,17 @@ RTL ;Ax db $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F ; Free Small Key + ;Bx db $2C ; Bee Trap db $2B, $2C, $32, $31 ; Fae, Bee, Jar, Apple - db $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49 ; Unused - db $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49 ; Unused + db $49, $23, $23, $23, $28, $28, $28, $28, $28, $28, $28 ; Unused, Pendants, Crystals + + ;Cx + ; Key rings use the dynamic slot where the Silver Arrow graphic is stored. + db $41, $41, $41, $41 ; Key Ring + db $41, $41, $41, $41 + db $41, $41, $41, $41 + db $41, $41, $41, $41 db $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49 ; Unused db $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49 ; Unused db $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49, $49 ; Reserved @@ -184,7 +222,15 @@ RTL ; out: A - Palette ;-------------------------------------------------------------------------------- GetSpritePalette: - ;JSR AttemptItemSubstitution + ; Skip local capacity substitutions for items that will be sent to another player. + PHA + LDA !MULTIWORLD_SPRITEITEM_PLAYER_ID : BNE + + PLA + JSR SubstituteBombsWithRupeesWhenCapacityIsZero + BRA ++ + + + PLA + ++ CMP.b #$16 : BEQ .bottle ; Bottle CMP.b #$2B : BEQ .bottle ; Red Potion w/bottle CMP.b #$2C : BEQ .bottle ; Green Potion w/bottle @@ -210,7 +256,7 @@ GetSpritePalette: PHX PHB : PHK : PLB ;-------- - TAX : LDA.l .gfxPalettes, X ; look up item gfx + TAX : LDA.w .gfxPalettes, X ; look up item gfx PLB : PLX CMP.b #$F8 : !BGE .specialHandling RTL @@ -276,7 +322,7 @@ RTL db $08, $08, $02, $02, $04, $02, $02, $02 db $04, $02, $04, $02, $08, $08, $04, $02 - db $0A, $02, $04, $02, $04, $04, $00, $04 + db $0C, $02, $04, $02, $04, $04, $00, $04 db $04, $08, $02, $02, $08, $04, $02, $08 db $04, $04, $08, $08, $08, $04, $02, $08 @@ -307,8 +353,8 @@ RTL db $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08 ; Free Small Key db $04 ; Bee Trap db $08, $02, $08, $02 ; Fae, Bee, Jar, Apple - db $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08 ; Unused - db $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08 ; Unused + db $08, $08, $04, $02, $04, $04, $04, $04, $02, $02, $04 ; Unused, Pendants, Crystals + db $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08 ; Key Ring db $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08 ; Unused db $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08 ; Unused db $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08, $08 ; Unused @@ -323,7 +369,15 @@ RTL IsNarrowSprite: PHA : PHX PHB : PHK : PLB - ;JSR AttemptItemSubstitution + ; Skip local capacity substitutions for items that will be sent to another player. + PHA + LDA !MULTIWORLD_SPRITEITEM_PLAYER_ID : BNE + + PLA + JSR SubstituteBombsWithRupeesWhenCapacityIsZero + BRA ++ + + + PLA + ++ ;-------- CMP.b #$16 : BEQ .bottle ; Bottle CMP.b #$2B : BEQ .bottle ; Red Potion w/bottle @@ -379,7 +433,7 @@ IsNarrowSprite: LDX.b #$00 ; set index counter to 0 ;---- - - CPX.b #$24 : !BGE .false ; finish if we've done the whole list + CPX.b #$25 : !BGE .false ; finish if we've done the whole list CMP.l .smallSprites, X : BNE + ; skip to next if we don't match ;-- SEC ; set true state @@ -400,9 +454,9 @@ RTL .smallSprites db $04, $07, $08, $09, $0A, $0B, $0C, $13 db $15, $18, $24, $2A, $34, $35, $36, $42 - db $43, $45, $59, $A0, $A1, $A2, $A3, $A4 - db $A5, $A6, $A7, $A8, $A9, $AA, $AB, $AC - db $AD, $AE, $AF, $B3, $FF, $FF, $FF, $FF + db $43, $45, $58, $59, $A0, $A1, $A2, $A3 + db $A4, $A5, $A6, $A7, $A8, $A9, $AA, $AB + db $AC, $AD, $AE, $AF, $B3 } ;-------------------------------------------------------------------------------- @@ -485,9 +539,8 @@ RTS !SKIP_EOR = "$7F5008" ;-------------------------------------------------------------------------------- DrawDynamicTile: - JSR PrepDrawRemoteItemSprite - - JSL.l IsNarrowSprite : BCS .narrow + JSR PrepDrawRemoteItemSprite + JSL.l IsNarrowSprite : BCS .narrow .full LDA.b #$01 : STA $06 @@ -516,9 +569,8 @@ DrawDynamicTile: RTL ;-------------------------------------------------------------------------------- DrawDynamicTileNoShadow: - JSR PrepDrawRemoteItemSprite - - JSL.l IsNarrowSprite : BCS .narrow + JSR PrepDrawRemoteItemSprite + JSL.l IsNarrowSprite : BCS .narrow .full LDA.b #$01 : STA $06 @@ -546,8 +598,8 @@ RTL ;-------------------------------------------------------------------------------- PrepDrawRemoteItemSprite: - PHA - LDA RemoteItems : BEQ + + PHA + LDA RemoteItems : BEQ + PLA CMP !MULTIWORLD_SCOUTREPLY_LOCATION : BNE ++ LDA !MULTIWORLD_SCOUT_LOCATION : BEQ +++