Compare commits
4 Commits
master
...
book-of-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d12e9ee5a6 | ||
|
|
8f94711e9a | ||
|
|
8639b8525b | ||
|
|
33cec3b1d2 |
147
.gitignore
vendored
147
.gitignore
vendored
@@ -10,150 +10,3 @@ oos111x.sfc
|
||||
oos111x.smp.sym
|
||||
oos111x.srm
|
||||
oos111x.sym
|
||||
zcompress.exe
|
||||
oos165.sfc
|
||||
oos165x-1.bst
|
||||
oos165x-2.bst
|
||||
oos165x-3.bst
|
||||
oos165x.bp
|
||||
oos165x.cpu.sym
|
||||
oos165x.sfc
|
||||
oos165x.smp.sym
|
||||
oos165x.srm
|
||||
buildJ.bat
|
||||
oos165x.cht
|
||||
Roms
|
||||
Dungeons/Assets
|
||||
Items/gfx/jump_frames.xcf
|
||||
Masks/etc
|
||||
Menu/rings/
|
||||
Overworld/project_files/DarkWorldMainSet.xcf
|
||||
Sprites/Project Files/
|
||||
Thumbs.db
|
||||
.favorites.json
|
||||
Dungeons/Assets/
|
||||
Roms/oos111.bps
|
||||
Roms/oos112.sfc
|
||||
Sprites/Enemies/deku_scrub_enemy.zsm
|
||||
Dungeons/Objects/object_files/kydreeok_body.zob
|
||||
Sprites/Objects/Minecart/
|
||||
Util/tools
|
||||
Util/rom_map.txt
|
||||
Sprites/NPCs/korok_hollo.asm
|
||||
Sprites/NPCs/korok_makar.asm
|
||||
Sprites/NPCs/korok_rown.asm
|
||||
Overworld/world_map/test_dw.bmp
|
||||
Overworld/world_map/zelda3.sfc
|
||||
Overworld/world_map/maptest.bmp
|
||||
Sprites/Objects/whirlpool_export.asm
|
||||
Sprites/Objects/ice_block_export.asm
|
||||
Masks/gfx/[108]tlp11
|
||||
Masks/gfx/extra/
|
||||
Overworld/project_files/OverworldCastleBridge.png
|
||||
Overworld/project_files/OverworldCastleBridge.xcf
|
||||
Overworld/gfx/boat2_1.bin
|
||||
Overworld/gfx/boat2_3.bin
|
||||
Overworld/gfx/boat2_4.bin
|
||||
Overworld/gfx/boat2_2.bin
|
||||
Sprites/NPCs/zora_princess_proj.zsm
|
||||
Util/SpcToASM/SpcConverter.deps.json
|
||||
Util/SpcToASM/SpcConverter.dll
|
||||
Util/SpcToASM/SpcConverter.exe
|
||||
Util/SpcToASM/SpcConverter.pdb
|
||||
Util/SpcToASM/SpcConverter.runtimeconfig.json
|
||||
Masks/gfx/deku_link.pal
|
||||
Masks/gfx/deku_new.pal
|
||||
Masks/gfx/dekulinkpal.pal
|
||||
Masks/gfx/dekulinkpal.pal.bak
|
||||
Masks/gfx/gbc-link.pal
|
||||
Masks/gfx/image.bin
|
||||
Masks/gfx/image.png
|
||||
Overworld/project_files/zora_overlay_2.zsa
|
||||
Overworld/project_files/zora_temple_overlay.zsa
|
||||
Sprites/wolfos_export.asm
|
||||
Sprites/wolf_boss.zsm
|
||||
Sprites/NPCs/project_files/korok_hollo.asm
|
||||
Sprites/NPCs/project_files/korok_makar.asm
|
||||
Sprites/NPCs/project_files/korok_rown.asm
|
||||
Sprites/NPCs/project_files/zora_princess_proj.zsm
|
||||
Util/zelda3.sfc
|
||||
Util/templatemusic.asm
|
||||
Util/ZSCustomOverworld.asm
|
||||
Sprites/Objects/Project Files/ice_block_export.asm
|
||||
Sprites/Objects/Project Files/whirlpool_export.asm
|
||||
Overworld/world_map/dw_gfx_new.bmp
|
||||
Menu/tilemaps/dung_map.bin
|
||||
Dungeons/intro_cutscene.asm
|
||||
Dungeons/water_switch.asm
|
||||
run.sh
|
||||
Sprites/Bosses/left_kydreeok_head.asm
|
||||
Sprites/Bosses/kydreeok_wings.asm
|
||||
Sprites/Bosses/kydreeok_body.zsm
|
||||
Sprites/Bosses/dark_link.pal
|
||||
Sprites/NPCs/deku_scrub_npcs.asm
|
||||
Sprites/Enemies/anti_kirby_v2.asm
|
||||
Masks/gfx/gbc_dark_link.4bpp
|
||||
Sprites/kydrog_intro.asm
|
||||
Util/ZScreamNew/ZScream.exe
|
||||
Util/ZScreamNew/ZSCustomOverworld.asm
|
||||
Util/dark_link.bin
|
||||
Sprites/NPCs/project_files/deku_scrub_npcs.asm
|
||||
Util/ZScreamNew/asar.dll
|
||||
Util/ZScreamNew/CustomCollision.asm
|
||||
Util/ZScreamNew/debug.asm
|
||||
Util/ZScreamNew/DefaultNames.txt
|
||||
Util/ZScreamNew/DW.png
|
||||
Util/ZScreamNew/Lidgren.Network.dll
|
||||
Util/ZScreamNew/Lidgren.Network.pdb
|
||||
Util/ZScreamNew/Logs.txt
|
||||
Util/ZScreamNew/LW.png
|
||||
Util/ZScreamNew/MapTest.png
|
||||
Util/ZScreamNew/newgraves.asm
|
||||
Util/ZScreamNew/ScratchPad.dat
|
||||
Util/ZScreamNew/SP.png
|
||||
Util/ZScreamNew/spritesmove.asm
|
||||
Util/ZScreamNew/tempPatch.asm
|
||||
Util/ZScreamNew/ZScream.application
|
||||
Util/ZScreamNew/ZScream.exe.config
|
||||
Util/ZScreamNew/ZScream.exe.manifest
|
||||
Util/ZScreamNew/ZS_Patches/Version.txt
|
||||
Util/ZScreamNew/ZS_Patches/Hex Edits/Misc Small Patches.asm
|
||||
Util/ZScreamNew/ZS_Patches/Items/AST Boots.asm
|
||||
Util/ZScreamNew/ZS_Patches/Misc/Big Bomb Requirements.asm
|
||||
Util/ZScreamNew/ZS_Patches/Misc/IntroSkip.asm
|
||||
Util/ZScreamNew/ZS_Patches/Misc/JP1.0 Glitches.asm
|
||||
Util/ZScreamNew/ZS_Patches/Misc/Link Bed Start Position.asm
|
||||
Util/ZScreamNew/ZS_Patches/Misc/NoRocks.asm
|
||||
Util/ZScreamNew/ZS_Patches/Misc/Rainstate Skip.asm
|
||||
Util/ZScreamNew/ZS_Patches/Misc/TorchTags.asm
|
||||
Util/ZScreamNew/ZS_Patches/Misc/Weathervane.asm
|
||||
Util/ZScreamNew/ZS_Patches/Music/LostWoodsExitMusic.asm
|
||||
Util/ZScreamNew/ZS_Patches/Npcs/Bottle Vendor.asm
|
||||
Util/ZScreamNew/ZS_Patches/Sprites/Crystalswitch Conveyor.asm
|
||||
Util/ZScreamNew/ZS_Patches/Sprites/Elemental Trinexx.asm
|
||||
Util/ZScreamNew/ZS_Patches/Sprites/Eye Lasers Active.asm
|
||||
Util/ZScreamNew/ZS_Patches/Sprites/Khodstare Speeds.asm
|
||||
Util/ZScreamNew/ZS_Patches/Sprites/Spike Damage.asm
|
||||
Util/ZScreamNew/ZS_Patches/Sprites/Spikes_Subtype.asm
|
||||
Sprites/Bosses/bigchuchu_grow.asm
|
||||
Sprites/Bosses/manhandla_chuchu_blast.asm
|
||||
Sprites/Bosses/manhandla_export_2.asm
|
||||
Sprites/Bosses/manhandla_export.asm
|
||||
Music/Z3_SongOfHealingThemeV1-00.asm
|
||||
Dungeons/experimental/HitBoxProperties.asm
|
||||
Dungeons/experimental/intro_cutscene.asm
|
||||
Dungeons/experimental/KillSprites
|
||||
Dungeons/experimental/warpswitch.asm
|
||||
Dungeons/experimental/water_switch.asm
|
||||
Overworld/weathervane.asm
|
||||
Util/banana.asm
|
||||
Menu/tilemaps/sheet.png
|
||||
Sprites/NPCs/project_files/eon_owl.zsm
|
||||
Music/wip/oot_title.asm
|
||||
Music/wip/phantom_ganon.asm
|
||||
Music/wip/template_song.asm
|
||||
Music/wip/twinrova_battle.asm
|
||||
Sprites/NPCs/piratian.zsm
|
||||
Masks/gfx/moosh.png
|
||||
Masks/gfx/moosh.pal
|
||||
Dungeons/DungeonMaps.asm
|
||||
@@ -1,47 +0,0 @@
|
||||
Expanded space used by ZScream as of 02/28/2025
|
||||
|
||||
Addresses are PC unless stated otherwise.
|
||||
|
||||
ZS reserves everything up to 1.5MB or up to 0x150000
|
||||
And an additional 3 banks at the end of the 2.0MB 0x1E8000 to 0x1FFFFF
|
||||
|
||||
0x100000 - 0x107FFF: 1 Bank
|
||||
Nothing?
|
||||
|
||||
0x108000 - 0x10FFFF: 1 Bank
|
||||
Title screen data
|
||||
Dungeon map data
|
||||
|
||||
0x110000 - 0x117FFF: 1 Bank
|
||||
Default room header location
|
||||
Seems to have been some old dungeon object data expansion but that has since been moved.
|
||||
|
||||
0x118000 - 0x11FFFF: 1 Bank
|
||||
Seems to have been some old dungeon object data expansion but that has since been moved.
|
||||
|
||||
0x120000 - 0x127FFF: 1 Bank
|
||||
Expanded overlay data
|
||||
|
||||
0x128000 - 0x12FFFF: 1 Bank
|
||||
Custom collision data
|
||||
|
||||
0x130000 - 0x137FFF: 1 Bank
|
||||
Overworld map data overflow
|
||||
|
||||
0x138000 - 0x13FFFF: 1 Bank
|
||||
Expanded Dungeon object data
|
||||
|
||||
0x140000 - 0x147FFF: 1 Bank
|
||||
Custom overworld data
|
||||
|
||||
0x148000 - 0x14FFFF: 1 Bank
|
||||
Expanded Dungeon object data
|
||||
|
||||
0x1E0000 - 0x1E7FFF: 1 Bank
|
||||
Custom ASM Patches
|
||||
|
||||
0x1E8000 - 0x1EFFFF: 1 Bank
|
||||
Expanded Tile16 space
|
||||
|
||||
0x1F0000 - 0x1FFFFF: 2 Banks
|
||||
Edpanded Tile32 space
|
||||
@@ -1,274 +0,0 @@
|
||||
; ==============================================================================
|
||||
; SNES Hardware Registers
|
||||
; ==============================================================================
|
||||
|
||||
; Shorthand legend:
|
||||
; Addr = Address
|
||||
; APU = Audio Processing Unit
|
||||
; BG = BackGround
|
||||
; CGRAM = Color Generator RAM
|
||||
; Des = Designation
|
||||
; H = Horizontal
|
||||
; HDMA = Horizontal Direct Memory Access
|
||||
; HV = H/V or Horizontal/Vertical
|
||||
; Init = Initial
|
||||
; IO = I/O or Input/Output
|
||||
; IRQ = Interupt ReQuest
|
||||
; NMI = Non-Maskable Interupt
|
||||
; Num = Number
|
||||
; MULT = Multiply/Multiplication
|
||||
; OAM = Object Attribute Memory
|
||||
; OBJ = Object
|
||||
; Pos = Position
|
||||
; PPU = Picture Processing Unit
|
||||
; V = Vertical
|
||||
; Val = Value
|
||||
; VRAM = Video RAM
|
||||
|
||||
; Names taken from:
|
||||
; https://en.wikibooks.org/wiki/Super_NES_Programming/SNES_Hardware_Registers
|
||||
|
||||
; Further details on each register can be found here:
|
||||
; https://github.com/gilligan/snesdev/blob/master/docs/snes_registers.txt
|
||||
; https://www.youtube.com/watch?v=-4OOuRvTXrM&t=167s
|
||||
|
||||
org $7E2100 ; Remove for asar 2.0.
|
||||
|
||||
struct SNES $7E2100
|
||||
{
|
||||
.ScreenDisplay: skip $01 ; $2100
|
||||
.OAMSizeAndDataDes: skip $01 ; $2101
|
||||
.OAMAccessAddr: skip $02 ; $2102
|
||||
.OMADataWrite: skip $01 ; $2104
|
||||
.BGModeAndTileSize: skip $01 ; $2105
|
||||
.MosaicAndBGEnable: skip $01 ; $2106
|
||||
|
||||
.BG1AddrAndSize: skip $01 ; $2107
|
||||
.BG2AddrAndSize: skip $01 ; $2108
|
||||
.BG3AddrAndSize: skip $01 ; $2109
|
||||
.BG4AddrAndSize: skip $01 ; $210A
|
||||
|
||||
.BG1And2TileDataDes: skip $01 ; $210B
|
||||
.BG3And4TileDataDes: skip $01 ; $210C
|
||||
|
||||
.BG1HScrollOffset: skip $01 ; $210D
|
||||
.BG1VScrollOffset: skip $01 ; $210E
|
||||
.BG2HScrollOffset: skip $01 ; $210F
|
||||
.BG2VScrollOffset: skip $01 ; $2110
|
||||
.BG3HScrollOffset: skip $01 ; $2111
|
||||
.BG3VScrollOffset: skip $01 ; $2112
|
||||
.BG4HScrollOffset: skip $01 ; $2113
|
||||
.BG4VScrollOffset: skip $01 ; $2114
|
||||
|
||||
.VRAMAddrIncrementVal: skip $01 ; $2115
|
||||
.VRAMAddrReadWriteLow: skip $01 ; $2116
|
||||
.VRAMAddrReadWriteHigh: skip $01 ; $2117
|
||||
.VRAMDataWriteLow: skip $01 ; $2118
|
||||
.VRAMDataWriteHigh: skip $01 ; $2119
|
||||
|
||||
.Mode7Init skip $01 ; $211A
|
||||
.Mode7MatrixA skip $01 ; $211B
|
||||
.Mode7MatrixB skip $01 ; $211C
|
||||
.Mode7MatrixC skip $01 ; $211D
|
||||
.Mode7MatrixD skip $01 ; $211E
|
||||
.Mode7CenterPosX skip $01 ; $211F
|
||||
.Mode7CenterPosY skip $01 ; $2120
|
||||
|
||||
.CGRAMWriteAddr skip $01 ; $2121
|
||||
.CGRAMWriteData skip $01 ; $2122
|
||||
|
||||
.BG1And2WindowMask skip $01 ; $2123
|
||||
.BG3And4WindowMask skip $01 ; $2124
|
||||
.OBJAndColorWindow skip $01 ; $2125
|
||||
|
||||
.Window1LeftPosDes skip $01 ; $2126
|
||||
.Window1RightPosDes skip $01 ; $2127
|
||||
.Window2LeftPosDes skip $01 ; $2128
|
||||
.Window2RightPosDes skip $01 ; $2129
|
||||
|
||||
.BG123And4WindowLogic skip $01 ; $212A
|
||||
.ColorAndOBJWindowLogic skip $01 ; $212B
|
||||
.BGAndOBJEnableMainScreen skip $01 ; $212C
|
||||
.BGAndOBJEnableSubScreen skip $01 ; $212D
|
||||
.WindowMaskDesMainScreen skip $01 ; $212E
|
||||
.WindowMaskDesSubScreen skip $01 ; $212F
|
||||
.InitColorAddition skip $01 ; $2130
|
||||
.AddSubtractSelectAndEnable skip $01 ; $2131
|
||||
.FixedColorData skip $01 ; $2132
|
||||
.ScreenInit skip $01 ; $2133
|
||||
|
||||
.MultResultLow skip $01 ; $2134
|
||||
.MultResultMid skip $01 ; $2135
|
||||
.MultResultHigh skip $01 ; $2136
|
||||
|
||||
.HVCounterSoftwareLatch skip $01 ; $2137
|
||||
|
||||
.OAMReadDataLowHigh skip $01 ; $2138
|
||||
.VRAMReadDataLow skip $01 ; $2139
|
||||
.VRAMReadDataHigh skip $01 ; $213A
|
||||
.CGRAMReadDataLowHigh skip $01 ; $213B
|
||||
|
||||
.HCounterData skip $01 ; $213C
|
||||
.VCounterData skip $01 ; $213D
|
||||
|
||||
.PPUStatusFlag1 skip $01 ; $213E
|
||||
.PPUStatusFlag2 skip $01 ; $213F
|
||||
|
||||
.APUIOPort0 skip $01 ; $2140
|
||||
.APUIOPort1 skip $01 ; $2141
|
||||
.APUIOPort2 skip $01 ; $2142
|
||||
.APUIOPort3 skip $01 ; $2143
|
||||
|
||||
base $2180
|
||||
.IndirectWorkRAMPort: skip $01 ; $2180
|
||||
.IndirectWorkRAMAddrLow: skip $01 ; $2180
|
||||
.IndirectWorkRAMAddrMid: skip $01 ; $2180
|
||||
.IndirectWorkRAMAddrHigh: skip $01 ; $2180
|
||||
|
||||
base $4200
|
||||
.NMIVHCountJoypadEnable: skip $01 ; $4200
|
||||
.ProgrammableIOPortOut: skip $01 ; $4201
|
||||
.MultiplicandA: skip $01 ; $4202
|
||||
.MultiplierB: skip $01 ; $4203
|
||||
.DividendLow: skip $01 ; $4204
|
||||
.DividendHigh: skip $01 ; $4205
|
||||
.DivisorB: skip $01 ; $4206
|
||||
.HCountTimer: skip $01 ; $4207
|
||||
.HCountTimerMSB: skip $01 ; $4208
|
||||
.VCountTImer: skip $01 ; $4209
|
||||
.VCountTimerMSB: skip $01 ; $420A
|
||||
|
||||
.DMAChannelEnable: skip $01 ; $420B
|
||||
.HDMAChannelEnable: skip $01 ; $420C
|
||||
.CycleSpeedDes: skip $01 ; $420D
|
||||
|
||||
base $4210
|
||||
.NMIFlagAndCPUVersionNum: skip $01 ; $4210
|
||||
.IRQFlagByHVCountTimer: skip $01 ; $4211
|
||||
.HVBlankFlagsAndJoyStatus: skip $01 ; $4212
|
||||
.ProgrammableIOPortIn: skip $01 ; $4213
|
||||
.DivideResultQuotientLow: skip $01 ; $4214
|
||||
.DivideResultQuotientHigh: skip $01 ; $4215
|
||||
.RemainderResultLow: skip $01 ; $4216
|
||||
.RemainderResultHigh: skip $01 ; $4217
|
||||
|
||||
.JoyPad1DataLow: skip $01 ; $4218
|
||||
.JoyPad2DataLow: skip $01 ; $4219
|
||||
.JoyPad3DataLow: skip $01 ; $421A
|
||||
.JoyPad4DataLow: skip $01 ; $421B
|
||||
.JoyPad1DataHigh: skip $01 ; $421C
|
||||
.JoyPad2DataHigh: skip $01 ; $421D
|
||||
.JoyPad3DataHigh: skip $01 ; $421E
|
||||
.JoyPad4DataHigh: skip $01 ; $421F
|
||||
}
|
||||
endstruct
|
||||
|
||||
struct DMA $7E4300
|
||||
{
|
||||
; Channel 0
|
||||
.0_TransferParameters: skip $01 ; $4300
|
||||
.0_DestinationAddr: skip $01 ; $4301
|
||||
.0_SourceAddrOffsetLow: skip $01 ; $4302
|
||||
.0_SourceAddrOffsetHigh: skip $01 ; $4303
|
||||
.0_SourceAddrBank: skip $01 ; $4304
|
||||
.0_TransferSizeLow: skip $01 ; $4305
|
||||
.0_TransferSizeHigh: skip $01 ; $4306
|
||||
.0_DataBank: skip $01 ; $4307
|
||||
.0_TableAddrLow: skip $01 ; $4308
|
||||
.0_TableAddrHigh: skip $01 ; $4309
|
||||
.0_TransferLineNum: skip $01 ; $430A
|
||||
|
||||
base $4310 ; Channel 1
|
||||
.1_TransferParameters: skip $01 ; $4310
|
||||
.1_DestinationAddr: skip $01 ; $4311
|
||||
.1_SourceAddrOffsetLow: skip $01 ; $4312
|
||||
.1_SourceAddrOffsetHigh: skip $01 ; $4313
|
||||
.1_SourceAddrBank: skip $01 ; $4314
|
||||
.1_TransferSizeLow: skip $01 ; $4315
|
||||
.1_TransferSizeHigh: skip $01 ; $4316
|
||||
.1_DataBank: skip $01 ; $4317
|
||||
.1_TableAddrLow: skip $01 ; $4318
|
||||
.1_TableAddrHigh: skip $01 ; $4319
|
||||
.1_TransferLineNum: skip $01 ; $431A
|
||||
|
||||
base $4320 ; Channel 2
|
||||
.2_TransferParameters: skip $01 ; $4320
|
||||
.2_DestinationAddr: skip $01 ; $4321
|
||||
.2_SourceAddrOffsetLow: skip $01 ; $4322
|
||||
.2_SourceAddrOffsetHigh: skip $01 ; $4323
|
||||
.2_SourceAddrBank: skip $01 ; $4324
|
||||
.2_TransferSizeLow: skip $01 ; $4325
|
||||
.2_TransferSizeHigh: skip $01 ; $4326
|
||||
.2_DataBank: skip $01 ; $4327
|
||||
.2_TableAddrLow: skip $01 ; $4328
|
||||
.2_TableAddrHigh: skip $01 ; $4329
|
||||
.2_TransferLineNum: skip $01 ; $432A
|
||||
|
||||
base $4330 ; Channel 3
|
||||
.3_TransferParameters: skip $01 ; $4330
|
||||
.3_DestinationAddr: skip $01 ; $4331
|
||||
.3_SourceAddrOffsetLow: skip $01 ; $4332
|
||||
.3_SourceAddrOffsetHigh: skip $01 ; $4333
|
||||
.3_SourceAddrBank: skip $01 ; $4334
|
||||
.3_TransferSizeLow: skip $01 ; $4335
|
||||
.3_TransferSizeHigh: skip $01 ; $4336
|
||||
.3_DataBank: skip $01 ; $4337
|
||||
.3_TableAddrLow: skip $01 ; $4338
|
||||
.3_TableAddrHigh: skip $01 ; $4339
|
||||
.3_TransferLineNum: skip $01 ; $433A
|
||||
|
||||
base $4340 ; Channel 4
|
||||
.4_TransferParameters: skip $01 ; $4340
|
||||
.4_DestinationAddr: skip $01 ; $4341
|
||||
.4_SourceAddrOffsetLow: skip $01 ; $4342
|
||||
.4_SourceAddrOffsetHigh: skip $01 ; $4343
|
||||
.4_SourceAddrBank: skip $01 ; $4344
|
||||
.4_TransferSizeLow: skip $01 ; $4345
|
||||
.4_TransferSizeHigh: skip $01 ; $4346
|
||||
.4_DataBank: skip $01 ; $4347
|
||||
.4_TableAddrLow: skip $01 ; $4348
|
||||
.4_TableAddrHigh: skip $01 ; $4349
|
||||
.4_TransferLineNum: skip $01 ; $434A
|
||||
|
||||
base $4350 ; Channel 5
|
||||
.5_TransferParameters: skip $01 ; $4350
|
||||
.5_DestinationAddr: skip $01 ; $4351
|
||||
.5_SourceAddrOffsetLow: skip $01 ; $4352
|
||||
.5_SourceAddrOffsetHigh: skip $01 ; $4353
|
||||
.5_SourceAddrBank: skip $01 ; $4354
|
||||
.5_TransferSizeLow: skip $01 ; $4355
|
||||
.5_TransferSizeHigh: skip $01 ; $4356
|
||||
.5_DataBank: skip $01 ; $4357
|
||||
.5_TableAddrLow: skip $01 ; $4358
|
||||
.5_TableAddrHigh: skip $01 ; $4359
|
||||
.5_TransferLineNum: skip $01 ; $435A
|
||||
|
||||
base $4360 ; Channel 6
|
||||
.6_TransferParameters: skip $01 ; $4360
|
||||
.6_DestinationAddr: skip $01 ; $4361
|
||||
.6_SourceAddrOffsetLow: skip $01 ; $4362
|
||||
.6_SourceAddrOffsetHigh: skip $01 ; $4363
|
||||
.6_SourceAddrBank: skip $01 ; $4364
|
||||
.6_TransferSizeLow: skip $01 ; $4365
|
||||
.6_TransferSizeHigh: skip $01 ; $4366
|
||||
.6_DataBank: skip $01 ; $4367
|
||||
.6_TableAddrLow: skip $01 ; $4368
|
||||
.6_TableAddrHigh: skip $01 ; $4369
|
||||
.6_TransferLineNum: skip $01 ; $436A
|
||||
|
||||
base $4370 ; Channel 7
|
||||
.7_TransferParameters: skip $01 ; $4370
|
||||
.7_DestinationAddr: skip $01 ; $4371
|
||||
.7_SourceAddrOffsetLow: skip $01 ; $4372
|
||||
.7_SourceAddrOffsetHigh: skip $01 ; $4373
|
||||
.7_SourceAddrBank: skip $01 ; $4374
|
||||
.7_TransferSizeLow: skip $01 ; $4375
|
||||
.7_TransferSizeHigh: skip $01 ; $4376
|
||||
.7_DataBank: skip $01 ; $4377
|
||||
.7_TableAddrLow: skip $01 ; $4378
|
||||
.7_TableAddrHigh: skip $01 ; $4379
|
||||
.7_TransferLineNum: skip $01 ; $437A
|
||||
}
|
||||
endstruct
|
||||
|
||||
; ==============================================================================
|
||||
185
Core/link.asm
185
Core/link.asm
@@ -1,185 +0,0 @@
|
||||
; =========================================================
|
||||
; Link RAM and Functions
|
||||
|
||||
LinkY = $20 ; Position Y of link
|
||||
LinkYH = $21 ; High position Y of link
|
||||
LinkX = $22 ; Position X of link
|
||||
LinkXH = $23 ; High position X of link
|
||||
LinkZ = $24 ; Position Z of link
|
||||
|
||||
; ----UDLR
|
||||
; [U Up][D Down][L Left][R Right]
|
||||
; Direction link is pushing against
|
||||
LinkPushDir = $26
|
||||
|
||||
; Link's recoiling speed
|
||||
; By themselves, these do not do much
|
||||
; They will be reset every frame Link is not in recoil state
|
||||
LinkRecoilY = $27
|
||||
LinkRecoilX = $28
|
||||
LinkRecoilZ = $29
|
||||
|
||||
; Link's subpixel velocity
|
||||
; when this value overflows, Link's main velocity gains an extra pixel
|
||||
; reset on direction change, so not really a positional subpixel
|
||||
LinkSubVelY = $2A
|
||||
LinkSubVelX = $2B
|
||||
|
||||
; Direction link is facing
|
||||
; 00:Up, 02:Down, 04:Left, 06:Right
|
||||
LinkFaceDir = $2F
|
||||
|
||||
; Last direction link moved towards
|
||||
; 00:Up, 01:Down, 02:Left, 03:Right
|
||||
LinkLastDir = $66
|
||||
|
||||
; ----UDLR
|
||||
; [U Up][D Down][L Left][R Right]
|
||||
; direction link is "walking towards"
|
||||
LinkMoveDir = $67
|
||||
|
||||
; 0: Not moving, 1: Moving cardinal, 2: Moving diagonally
|
||||
LinkMoveInfo = $6A
|
||||
|
||||
LinkVisible = $4B ; if set to 0x0C link will be invisible
|
||||
LinkBunnyGfx = $56 ; if set to 1 link will be bunny, otherwise link
|
||||
|
||||
; 0x00: normal speed, 0x01-0x0F: slow,<2C>> 0x10:fast
|
||||
LinkSpeed = $57
|
||||
|
||||
; 0x00: normal speed, 0x02: walking on stair speed, 0x10: dashing speed
|
||||
LinkSpeedTbl = $5E
|
||||
|
||||
; if is set to 0x02 or 0x03 link is falling
|
||||
LinkFalling = $5B
|
||||
FallTimer = $5C
|
||||
|
||||
; LinkState_Default : 0x00
|
||||
; LinkState_Pits : 0x01
|
||||
; LinkState_Recoil : 0x02
|
||||
; LinkState_SpinAttack : 0x03
|
||||
; LinkState_Swimming : 0x04 (ZoraDive)
|
||||
; LinkState_OnIce : 0x05
|
||||
; LinkState_Recoil : 0x06
|
||||
; LinkState_Zapped : 0x07
|
||||
; LinkState_UsingEther : 0x08
|
||||
; LinkState_UsingBombos : 0x09
|
||||
; LinkState_UsingQuake : 0x0A (DekuHover)
|
||||
; LinkState_HoppingSouthOW : 0x0B
|
||||
; LinkState_HoppingHorizontallyOW : 0x0C
|
||||
; LinkState_HoppingDiagonallyUpOW : 0x0D
|
||||
; LinkState_HoppingDiagonallyDownOW : 0x0E
|
||||
; LinkState_0F : 0x0F
|
||||
; LinkState_0F : 0x10
|
||||
; LinkState_Dashing : 0x11
|
||||
; LinkState_ExitingDash : 0x12
|
||||
; LinkState_Hookshotting : 0x13
|
||||
; LinkState_CrossingWorlds : 0x14
|
||||
; LinkState_ShowingOffItem : 0x15
|
||||
; LinkState_Sleeping : 0x16
|
||||
; LinkState_Bunny : 0x17
|
||||
; LinkState_HoldingBigRock : 0x18
|
||||
; LinkState_ReceivingEther : 0x19
|
||||
; LinkState_ReceivingBombos : 0x1A
|
||||
; LinkState_ReadingDesertTablet : 0x1B
|
||||
; LinkState_TemporaryBunny : 0x1C
|
||||
; LinkState_TreePull : 0x1D
|
||||
; LinkState_SpinAttack : 0x1E
|
||||
LinkState = $5D
|
||||
|
||||
; 0: Link is not in a doorway
|
||||
; 1: is in a vertical doorway
|
||||
; 2: is in horizontal doorway
|
||||
LinkDoorway = $6C
|
||||
|
||||
; 0: Nothing
|
||||
; 1: a hand in the air
|
||||
; 2: 2 hands in the air (like getting triforce)
|
||||
LinkGrabGfx = $02DA
|
||||
|
||||
; if not 0 add a poof gfx on link
|
||||
LinkPoofGfx = $02E1
|
||||
|
||||
; Bunny timer for link before transforming back
|
||||
LinkBunTimer = $02E2
|
||||
|
||||
; if not 0 prevent link from moving and opening the menu
|
||||
LinkMenuMove = $02E4
|
||||
|
||||
; if not 0 prevent link from getting any damages from sprites
|
||||
LinkDamage = $037B
|
||||
|
||||
; ----CCCC
|
||||
; [C Touching chest id]
|
||||
LinkColChest = $02E5
|
||||
|
||||
; 0: Not on somaria platform, 2: On somaria platform
|
||||
LinkSomaria = $02F5
|
||||
|
||||
; BP-AETHR
|
||||
; [B Boomerang][P Powder]
|
||||
; [A Bow&Arrows][E UnusedItem]
|
||||
; [T UnusedItem][H Hammer][R Rods]
|
||||
LinkItemUse = $0301
|
||||
|
||||
LinkItemY = $0303 ; Currently equipped item on the Y button
|
||||
|
||||
; 0: Nothing, 1:Picking up something, 2: Throwing something
|
||||
LinkCarrying = $0308
|
||||
|
||||
; .... ..tl
|
||||
; t - tossing object
|
||||
; l - lifting object
|
||||
LinkCarryOrToss = $0309
|
||||
|
||||
; 0: Normal
|
||||
; 1: Shovel
|
||||
; 2: Praying
|
||||
; 4: Hookshot
|
||||
; 8: Somaria
|
||||
; 10: Bug net
|
||||
; 20: Read book
|
||||
; 40: Tree pull
|
||||
LinkAnim = $037A
|
||||
|
||||
LinkWallCheat = $037F ; If non zero can walk through walls
|
||||
|
||||
; Animation step/graphics for spin attack animations; including medallions.
|
||||
LinkSpinGfx = $031C
|
||||
LinkSpinStep = $031D
|
||||
|
||||
; =========================================================
|
||||
|
||||
Link_ReceiveItem = $0799AD ; Y = item id
|
||||
|
||||
Link_CancelDash = $0791B9
|
||||
|
||||
Link_Initialize = $07F13C
|
||||
Link_ResetProperties_A = $07F1A3
|
||||
Link_ResetProperties_B = $07F1E6
|
||||
Link_ResetProperties_C = $07F1FA
|
||||
Link_ResetSwimmingState = $07983A
|
||||
Link_ResetStateAfterDamagingPit = $07984B
|
||||
Link_ItemReset_FromOverworldThings = $07B107
|
||||
|
||||
Link_CalculateSFXPan = $0DBB67
|
||||
|
||||
; Used by Agahnim2 fight
|
||||
CallForDuckIndoors = $07A45F
|
||||
|
||||
ApplyLinksMovementToCamera = $07E9D3
|
||||
|
||||
HandleIndoorCameraAndDoors = $07F42F
|
||||
|
||||
Link_HandleVelocityAndSandDrag = $07E3DD
|
||||
|
||||
Link_HandleMovingAnimation_FullLongEntry = $07E6A6
|
||||
Link_HandleMovingAnimation_General = $07E765
|
||||
Link_HandleMovingAnimationSwimming = $07E7FA
|
||||
|
||||
LinkHop_FindArbitraryLandingSpot = $07E370
|
||||
|
||||
CheckIfLinkIsBusy = $07F4D0
|
||||
Refund_Magic = $07B0E9
|
||||
|
||||
Hookshot_CheckTileCollision = $07D576
|
||||
142
Core/message.asm
142
Core/message.asm
@@ -1,142 +0,0 @@
|
||||
; Expanded Message Bank
|
||||
; Special thanks to Zarby89
|
||||
|
||||
!addr = $0EF3FF
|
||||
!looprun = $00
|
||||
while !looprun == $00
|
||||
if read1(!addr) == $7F
|
||||
!addr #= !addr+1
|
||||
;print hex(!addr) ; DEBUG LINE
|
||||
!looprun = $01
|
||||
|
||||
endif
|
||||
!addr #= !addr-1
|
||||
endwhile
|
||||
|
||||
; Temporary fix for the message bank
|
||||
; ZS does not clear message data when bank is changed
|
||||
; So the end of the data bank is not as easily searchable.
|
||||
org $0EEE75 : db $80
|
||||
|
||||
org !addr+1 : db $80
|
||||
|
||||
org $0ED436
|
||||
JML MessageExpand
|
||||
NOP #$06
|
||||
|
||||
org $2F8000
|
||||
MessageExpand:
|
||||
{
|
||||
; are we already in expanded bank?
|
||||
LDA.b $02 : AND.w #$00FF : CMP.w #$000E : BNE +
|
||||
LDA.w #MessageExpandedData : STA.b $00
|
||||
LDA.w #MessageExpandedData>>16 : STA.b $02
|
||||
JML $0ED3FC ; go back to original read message code pointers
|
||||
+
|
||||
; Restore vanilla code
|
||||
LDA.w #$DF40 : STA.b $00
|
||||
LDA.w #$000E : STA.b $02
|
||||
JML $0ED3FC ; go back to original read message code pointers
|
||||
}
|
||||
|
||||
MessageExpandedData:
|
||||
Message_18D:
|
||||
db $13, $B4, $20, $25, $1E, $42, $59, $13, $B4, $20, $25, $1E, $42, $59, $0A, $28, $28, $BB, $40, $0B, $22, $26, $29, $1A, $21, $3E, $75, $00, $21, $42, $59, $E3, $59, $2D, $21, $A6, $1E, $3E, $76, $03, $28, $59, $E3, $59, $D0, $1E, $24, $59, $BD, $29, $2C, $42, $59, $26, $32, $59, $1F, $2B, $22, $A5, $1D, $3F, $7E, $73, $13, $B4, $20, $25, $1E, $59, $AE, $59, $DB, $1A, $2F, $1E, $25, $A4, $1F, $1A, $2B, $59, $90, $73, $E2, $1D, $1E, $59, $1A, $1C, $2B, $28, $2C, $2C, $59, $0A, $1A, $25, $32, $31, $28, $59, $DA, $59, $1C, $B1, $2B, $2D, $73, $D8, $59, $2E, $27, $B8, $27, $3E, $59, $05, $C8, $59, $1A, $59, $2C, $26, $8E, $1F, $1E, $1E, $42, $7E, $73, $C6, $59, $1C, $28, $2E, $2B, $D0, $43, $73, $88, $44, $59, $16, $21, $91, $9F, $59, $E3, $59, $AD, $3F, $73, $88, $89, $08, $51, $26, $59, $28, $24, $1A, $32, $41, $68, $7F
|
||||
Message_18E:
|
||||
db $00, $21, $42, $59, $1A, $59, $30, $B5, $1E, $59, $1C, $21, $28, $22, $1C, $1E, $3E, $75, $16, $B6, $21, $59, $2D, $B0, $2C, $59, $BD, $29, $42, $59, $27, $28, $59, $D0, $1C, $CE, $2D, $76, $E2, $25, $25, $59, $97, $59, $B0, $1D, $1D, $A0, $A9, $26, $59, $E3, $3E, $7E, $73, $09, $2E, $D3, $59, $21, $8C, $28, $2F, $A1, $D8, $59, $2B, $DC, $1E, $1E, $2C, $73, $8C, $25, $1E, $2D, $59, $D8, $59, $1A, $1D, $2F, $A3, $2E, $CD, $97, $20, $B4, $3E, $7F
|
||||
Message_18F:
|
||||
db $0E, $21, $59, $1D, $A2, $41, $41, $41, $75, $08, $2D, $59, $D0, $1E, $26, $2C, $59, $E3, $2B, $59, $29, $28, $9C, $1E, $2D, $2C, $59, $8D, $1A, $76, $1A, $59, $1B, $B6, $59, $25, $22, $20, $21, $2D, $41, $59, $02, $28, $BE, $59, $96, $9C, $59, $E1, $A5, $7E, $73, $E3, $2B, $59, $DF, $25, $25, $1E, $2D, $59, $B5, $59, $1A, $2C, $59, $1F, $2E, $25, $25, $59, $1A, $2C, $59, $26, $B4, $1E, $3E, $73, $13, $B4, $20, $25, $1E, $59, $E2, $25, $25, $59, $DF, $B6, $42, $59, $9F, $27, $51, $2D, $73, $E3, $59, $30, $C8, $2B, $32, $3E, $7F
|
||||
Message_190:
|
||||
db $00, $21, $42, $59, $26, $32, $59, $97, $D3, $59, $1C, $2E, $D3, $28, $26, $A6, $3E, $75, $E8, $59, $8D, $97, $9B, $B3, $2A, $2E, $B6, $1E, $76, $D8, $59, $1C, $28, $25, $25, $1E, $1C, $2D, $C8, $59, $C6, $59, $BD, $29, $2C, $3E, $7E, $73, $0C, $1A, $32, $97, $59, $C7, $1E, $59, $1D, $1A, $32, $59, $13, $B4, $20, $25, $1E, $73, $E2, $25, $25, $59, $27, $1A, $BE, $59, $1A, $59, $29, $BA, $1C, $1E, $59, $1A, $1F, $2D, $A1, $E3, $3E, $7F
|
||||
Message_191:
|
||||
db $13, $B4, $20, $25, $1E, $42, $59, $13, $B4, $20, $25, $1E, $42, $59, $0A, $28, $28, $BB, $40, $0B, $22, $26, $29, $1A, $21, $3E, $75, $00, $21, $42, $59, $D0, $1E, $24, $B3, $D8, $59, $D0, $1C, $CE, $2D, $2C, $59, $C6, $76, $D8, $59, $0C, $2E, $D1, $2B, $28, $28, $26, $59, $06, $2B, $28, $2D, $DA, $42, $59, $8D, $E3, $3F, $7E, $73, $E6, $59, $13, $28, $1A, $1D, $D3, $28, $28, $25, $59, $16, $28, $28, $1D, $2C, $59, $8D, $1A, $73, $DB, $22, $9C, $32, $59, $29, $BA, $1C, $1E, $43, $59, $29, $94, $21, $2C, $59, $D0, $1E, $26, $59, $DA, $73, $2D, $30, $B5, $2D, $59, $8C, $2D, $2E, $2B, $27, $59, $A8, $A7, $A6, $41, $7E, $73, $0C, $1A, $32, $97, $59, $D2, $BE, $C7, $1E, $59, $B4, $59, $DA, $30, $27, $59, $AE, $73, $1F, $22, $20, $2E, $2B, $A4, $C5, $21, $28, $30, $59, $DA, $59, $D2, $25, $2F, $1E, $59, $B6, $2C, $73, $26, $32, $D3, $A6, $22, $1E, $2C, $3E, $7E, $73, $59, $07, $28, $30, $59, $9F, $1E, $2C, $59, $39, $34, $59, $2B, $DC, $1E, $1E, $2C, $59, $2C, $C4, $3F, $73, $88, $44, $59, $12, $C4, $2C, $59, $AC, $28, $1D, $73, $88, $89, $08, $51, $25, $25, $59, $2D, $1A, $24, $1E, $59, $26, $32, $59, $1C, $21, $93, $1C, $1E, $2C, $68, $7F
|
||||
Message_192:
|
||||
db $13, $B4, $20, $25, $1E, $42, $59, $13, $B4, $20, $25, $1E, $42, $59, $0A, $28, $28, $BB, $40, $0B, $22, $26, $29, $1A, $21, $3E, $75, $00, $21, $42, $59, $D8, $59, $13, $1A, $22, $25, $59, $0F, $1A, $BA, $1C, $1E, $3E, $76, $7E, $73, $E6, $59, $29, $94, $21, $59, $2D, $21, $2B, $28, $2E, $20, $21, $59, $D8, $59, $2C, $DF, $26, $29, $2C, $73, $B5, $59, $D2, $BE, $2D, $B0, $27, $20, $59, $C7, $B9, $1A, $59, $03, $1E, $24, $2E, $59, $1C, $28, $2E, $25, $1D, $73, $27, $1A, $2F, $22, $20, $94, $1E, $59, $9B, $A8, $2D, $1A, $1B, $25, $32, $43, $7E, $73, $59, $07, $28, $30, $59, $9F, $1E, $2C, $59, $3B, $39, $59, $2B, $DC, $1E, $1E, $2C, $59, $2C, $C4, $3F, $73, $88, $44, $59, $12, $C4, $2C, $59, $AC, $28, $1D, $73, $88, $89, $08, $51, $25, $25, $59, $2D, $1A, $24, $1E, $59, $26, $32, $59, $1C, $21, $93, $1C, $1E, $2C, $68, $7F
|
||||
Message_193:
|
||||
db $13, $B4, $20, $25, $1E, $42, $59, $13, $B4, $20, $25, $1E, $42, $59, $0A, $28, $28, $BB, $40, $0B, $22, $26, $29, $1A, $21, $3E, $75, $E6, $59, $20, $2B, $8C, $0A, $1A, $25, $32, $31, $28, $59, $02, $92, $25, $1E, $3E, $76, $7E, $73, $E6, $32, $59, $2C, $1A, $32, $59, $D8, $59, $0A, $B3, $C6, $59, $0A, $1A, $25, $32, $31, $28, $73, $B0, $1D, $59, $1A, $59, $CB, $A6, $1F, $2E, $25, $59, $E0, $1A, $29, $C7, $42, $59, $1D, $1E, $1E, $29, $73, $30, $B6, $B0, $27, $59, $D8, $59, $1C, $92, $25, $1E, $59, $DF, $25, $25, $2C, $41, $7E, $73, $59, $07, $28, $30, $59, $9F, $1E, $2C, $59, $35, $34, $34, $59, $2B, $DC, $1E, $1E, $2C, $59, $2C, $C4, $3F, $73, $88, $44, $59, $12, $C4, $2C, $59, $AC, $28, $1D, $73, $88, $89, $08, $51, $25, $25, $59, $2D, $1A, $24, $1E, $59, $26, $32, $59, $1C, $21, $93, $1C, $1E, $2C, $68, $7F
|
||||
Message_194:
|
||||
db $13, $B4, $20, $25, $1E, $42, $59, $13, $B4, $20, $25, $1E, $42, $59, $0A, $28, $28, $BB, $40, $0B, $22, $26, $29, $1A, $21, $3E, $75, $00, $21, $42, $59, $D8, $59, $19, $C8, $1A, $59, $13, $1E, $26, $CA, $3E, $76, $7E, $73, $E6, $59, $19, $C8, $1A, $59, $24, $B4, $20, $9F, $26, $59, $B5, $59, $B4, $73, $1C, $B1, $28, $2C, $41, $41, $41, $59, $D8, $59, $2B, $22, $2F, $A1, $8C, $D0, $1A, $73, $19, $C8, $1A, $2C, $59, $AD, $59, $97, $A0, $91, $28, $1D, $1D, $2C, $41, $7E, $73, $0F, $A6, $B1, $29, $2C, $59, $B6, $8B, $1C, $C7, $27, $1E, $1C, $2D, $A4, $DA, $73, $1A, $59, $1C, $A6, $2D, $8F, $41, $41, $41, $59, $2B, $28, $32, $1A, $25, $59, $2C, $1C, $90, $1A, $25, $3F, $7E, $73, $59, $07, $28, $30, $59, $9F, $1E, $2C, $59, $3C, $34, $59, $2B, $DC, $1E, $1E, $2C, $59, $2C, $C4, $3F, $73, $88, $44, $59, $12, $C4, $2C, $59, $AC, $28, $1D, $73, $88, $89, $08, $51, $25, $25, $59, $2D, $1A, $24, $1E, $59, $26, $32, $59, $1C, $21, $93, $1C, $1E, $2C, $68, $7F
|
||||
Message_195:
|
||||
db $13, $B4, $20, $25, $1E, $42, $59, $13, $B4, $20, $25, $1E, $42, $59, $0A, $28, $28, $BB, $40, $0B, $22, $26, $29, $1A, $21, $3E, $75, $00, $21, $42, $59, $E3, $59, $30, $B5, $21, $59, $DA, $59, $1E, $31, $29, $BB, $CE, $76, $06, $BA, $1C, $22, $1A, $59, $04, $D3, $94, $1E, $3F, $7E, $73, $00, $59, $29, $BA, $1C, $1E, $59, $C7, $1C, $1E, $59, $1F, $22, $25, $25, $A4, $30, $B6, $21, $59, $E0, $1A, $25, $2D, $21, $73, $8C, $CB, $A6, $41, $41, $41, $59, $1B, $2E, $2D, $59, $27, $28, $30, $3F, $59, $E6, $32, $73, $2C, $1A, $32, $59, $2C, $29, $22, $2B, $B6, $2C, $59, $DF, $25, $24, $59, $B6, $2C, $59, $B1, $25, $25, $2C, $43, $7E, $73, $00, $27, $1D, $59, $2D, $21, $91, $2D, $21, $A6, $1E, $8B, $D2, $BE, $C7, $1E, $73, $DF, $B6, $B3, $A8, $59, $E3, $41, $41, $41, $59, $1A, $59, $2F, $A6, $32, $73, $1C, $B0, $25, $25, $B3, $E0, $25, $9B, $1E, $41, $7E, $73, $59, $07, $28, $30, $59, $9F, $1E, $2C, $59, $3D, $34, $59, $2B, $DC, $1E, $1E, $2C, $59, $2C, $C4, $3F, $73, $88, $44, $59, $12, $C4, $2C, $59, $AC, $28, $1D, $73, $88, $89, $08, $51, $25, $25, $59, $2D, $1A, $24, $1E, $59, $26, $32, $59, $1C, $21, $93, $1C, $1E, $2C, $68, $7F
|
||||
Message_196:
|
||||
db $13, $B4, $20, $25, $1E, $42, $59, $13, $B4, $20, $25, $1E, $42, $59, $0A, $28, $28, $BB, $40, $0B, $22, $26, $29, $1A, $21, $3E, $75, $00, $21, $42, $59, $D8, $59, $06, $C8, $C7, $59, $0C, $B4, $1E, $2C, $3F, $76, $7E, $73, $08, $51, $2F, $1E, $59, $21, $A2, $1D, $59, $D8, $59, $06, $C8, $C7, $2C, $73, $8D, $CE, $D3, $25, $1E, $2C, $2C, $41, $41, $41, $59, $D2, $BE, $2D, $B0, $27, $20, $73, $1A, $98, $2E, $2D, $59, $26, $B5, $2C, $B3, $2B, $28, $9C, $59, $BE, $94, $3F, $7E, $73, $E8, $25, $25, $59, $27, $1E, $A4, $26, $C8, $1E, $59, $2D, $21, $93, $73, $B7, $59, $D3, $2B, $A5, $20, $2D, $21, $59, $DA, $59, $1C, $2B, $1A, $9C, $73, $2D, $21, $28, $D0, $59, $26, $B4, $1E, $2C, $59, $28, $29, $A5, $3E, $7E, $73, $59, $07, $28, $30, $59, $9F, $1E, $2C, $59, $3A, $34, $59, $2B, $DC, $1E, $1E, $2C, $59, $2C, $C4, $3F, $73, $88, $44, $59, $12, $C4, $2C, $59, $AC, $28, $1D, $73, $88, $89, $08, $51, $25, $25, $59, $2D, $1A, $24, $1E, $59, $26, $32, $59, $1C, $21, $93, $1C, $1E, $2C, $68, $7F
|
||||
Message_197:
|
||||
db $13, $B4, $20, $25, $1E, $42, $59, $13, $B4, $20, $25, $1E, $42, $59, $0A, $28, $28, $BB, $40, $0B, $22, $26, $29, $1A, $21, $3E, $75, $00, $21, $42, $59, $0A, $32, $1D, $2B, $28, $20, $8B, $2C, $B0, $29, $3E, $76, $7E, $73, $00, $59, $1F, $A2, $D2, $BE, $59, $2F, $1E, $2C, $D0, $25, $42, $59, $D8, $32, $59, $2C, $1A, $32, $73, $C7, $B9, $2D, $21, $28, $D0, $59, $E1, $28, $59, $1C, $93, $59, $D2, $1A, $2B, $73, $E2, $25, $25, $59, $CE, $1A, $1C, $21, $59, $B6, $3E, $7E, $73, $59, $07, $28, $30, $59, $9F, $1E, $2C, $59, $35, $36, $34, $59, $2B, $DC, $1E, $1E, $2C, $59, $2C, $C4, $3F, $73, $88, $44, $59, $12, $C4, $2C, $59, $AC, $28, $1D, $73, $88, $89, $08, $51, $25, $25, $59, $2D, $1A, $24, $1E, $59, $26, $32, $59, $1C, $21, $93, $1C, $1E, $2C, $68, $7F
|
||||
Message_198:
|
||||
db $00, $21, $42, $59, $E0, $25, $25, $59, $2D, $21, $94, $8B, $DA, $28, $59, $96, $1D, $41, $75, $02, $28, $BE, $59, $96, $9C, $59, $22, $1F, $59, $E3, $51, $CD, $B4, $2D, $A6, $1E, $D3, $1E, $1D, $76, $B4, $59, $13, $B4, $20, $25, $1E, $8B, $BD, $29, $2C, $3E, $7E, $73, $13, $B4, $20, $25, $1E, $42, $59, $13, $B4, $20, $25, $1E, $3E, $59, $0A, $28, $28, $BB, $28, $40, $0B, $22, $26, $29, $1A, $21, $3E, $73, $43, $E6, $D0, $59, $8D, $D8, $59, $BD, $20, $22, $1C, $59, $30, $C8, $1D, $2C, $73, $2D, $21, $91, $13, $B4, $20, $25, $1E, $59, $1C, $CE, $94, $A4, $B0, $26, $D0, $25, $1F, $41, $7E, $73, $03, $28, $C0, $D3, $1E, $1A, $25, $59, $D8, $26, $3E, $7F
|
||||
Message_199:
|
||||
db $E6, $59, $BF, $D1, $2B, $28, $28, $26, $59, $B5, $59, $27, $28, $59, $26, $A6, $1E, $75, $20, $2B, $28, $30, $2D, $21, $59, $C6, $59, $D8, $59, $A2, $2D, $21, $41, $76, $12, $2D, $1E, $1E, $29, $A4, $B4, $59, $93, $1C, $22, $A3, $59, $BD, $20, $22, $1C, $42, $59, $B6, $7E, $73, $1B, $2B, $B4, $20, $2C, $59, $A8, $2D, $21, $59, $E1, $91, $B5, $59, $B0, $1D, $1D, $A5, $41, $73, $08, $27, $59, $B6, $2C, $59, $CB, $1D, $A1, $25, $22, $1E, $2C, $59, $D8, $59, $CB, $A6, $73, $DA, $59, $2B, $A7, $1E, $1A, $25, $59, $DB, $2E, $1E, $59, $A8, $26, $2C, $42, $59, $D0, $1C, $CE, $2D, $2C, $7E, $73, $BB, $27, $20, $59, $1C, $C7, $1C, $1E, $1A, $25, $A4, $1B, $32, $59, $27, $94, $2E, $CE, $41, $7F
|
||||
Message_19A:
|
||||
db $E6, $59, $30, $B4, $1D, $2C, $59, $C7, $1C, $1E, $59, $1C, $1A, $2B, $2B, $22, $A4, $2D, $21, $28, $D0, $75, $E1, $28, $59, $D2, $1A, $2B, $A4, $30, $B6, $21, $59, $1F, $1E, $94, $21, $A1, $90, $76, $25, $1E, $1A, $1F, $42, $59, $2E, $27, $B0, $27, $1D, $A6, $A4, $1B, $32, $59, $D8, $59, $A2, $2D, $21, $41, $7E, $73, $01, $2E, $2D, $59, $30, $B6, $21, $59, $D8, $59, $2B, $B5, $1E, $59, $C6, $59, $1D, $1A, $2B, $24, $27, $1E, $2C, $2C, $42, $73, $D8, $59, $2C, $24, $32, $59, $2D, $2E, $2B, $27, $A4, $1A, $20, $8F, $D3, $59, $D8, $26, $41, $73, $0D, $28, $30, $42, $59, $C7, $B9, $2D, $21, $28, $D0, $59, $E1, $28, $59, $1E, $26, $1B, $2B, $1A, $1C, $1E, $7E, $73, $D8, $59, $2C, $24, $32, $8B, $20, $22, $1F, $2D, $2C, $59, $BD, $32, $59, $2B, $22, $1D, $1E, $59, $D8, $73, $1C, $2E, $2B, $2B, $A3, $2C, $59, $C6, $59, $1F, $CE, $1E, $9F, $26, $41, $7F
|
||||
Message_19B:
|
||||
db $00, $59, $1B, $BA, $1D, $1E, $59, $98, $2B, $27, $59, $B4, $59, $2C, $B1, $9F, $30, $42, $59, $D8, $75, $0C, $1E, $1A, $9F, $30, $59, $01, $BA, $1D, $1E, $59, $C7, $1C, $1E, $59, $D1, $C7, $1E, $59, $B4, $76, $D8, $59, $21, $90, $2C, $59, $C6, $59, $1A, $59, $21, $A6, $28, $41, $7E, $73, $01, $2E, $2D, $59, $1D, $1A, $2B, $24, $27, $1E, $2C, $2C, $59, $1C, $25, $2E, $27, $20, $59, $DA, $59, $B6, $2C, $73, $1E, $1D, $20, $1E, $42, $59, $8C, $D8, $59, $21, $A6, $28, $59, $DF, $2C, $59, $BB, $D3, $41, $73, $0D, $28, $30, $42, $59, $D8, $59, $1B, $BA, $1D, $1E, $59, $DF, $B6, $2C, $42, $59, $1B, $2E, $2B, $22, $1E, $1D, $7E, $73, $97, $27, $1E, $94, $21, $59, $D8, $59, $E0, $B2, $C6, $73, $A8, $AC, $2D, $2D, $A0, $1A, $26, $1B, $B6, $22, $C7, $2C, $41, $7F
|
||||
Message_19C:
|
||||
db $05, $2B, $28, $26, $59, $1C, $2B, $32, $D3, $1A, $25, $59, $8C, $30, $94, $A6, $42, $59, $D8, $75, $19, $C8, $1A, $59, $1C, $2B, $1A, $1F, $2D, $A4, $30, $C7, $1D, $A6, $2C, $59, $2D, $21, $94, $76, $29, $22, $A6, $1C, $A4, $D8, $59, $2F, $1E, $22, $25, $59, $C6, $59, $CE, $1A, $25, $26, $2C, $41, $7E, $73, $E6, $22, $2B, $59, $30, $C8, $24, $2C, $59, $28, $29, $A5, $A4, $29, $94, $21, $2C, $59, $DA, $73, $29, $BA, $1C, $1E, $2C, $59, $2E, $27, $D0, $A5, $42, $59, $1B, $2E, $2D, $59, $91, $20, $CE, $94, $73, $1C, $28, $D3, $41, $7E, $73, $16, $21, $91, $1F, $BB, $30, $2C, $59, $2D, $21, $2B, $28, $2E, $20, $21, $59, $D8, $59, $30, $94, $A6, $73, $BD, $32, $59, $25, $1E, $1A, $1D, $59, $DA, $59, $2C, $1A, $25, $2F, $94, $22, $C7, $43, $59, $C8, $73, $2B, $2E, $B4, $41, $7F
|
||||
Message_19D:
|
||||
db $E6, $59, $B1, $25, $25, $2C, $59, $C6, $59, $06, $BA, $1C, $22, $1A, $59, $C7, $1C, $1E, $59, $1E, $1C, $21, $28, $1E, $1D, $75, $30, $B6, $21, $59, $DF, $2B, $26, $2D, $21, $59, $8C, $E0, $1A, $25, $2D, $21, $42, $59, $32, $1E, $2D, $76, $22, $1C, $1E, $59, $1F, $22, $25, $25, $A4, $D8, $22, $2B, $59, $21, $A2, $2D, $2C, $41, $7E, $73, $08, $27, $59, $D8, $22, $2B, $59, $20, $CE, $1E, $1D, $42, $59, $D8, $32, $59, $30, $A6, $1E, $59, $1D, $2B, $1A, $30, $27, $73, $DA, $59, $CB, $A6, $2C, $59, $2D, $21, $91, $A9, $33, $1E, $59, $D8, $26, $59, $A9, $26, $73, $30, $B6, $B0, $27, $41, $7E, $73, $0D, $28, $30, $42, $59, $D8, $22, $2B, $59, $25, $1E, $20, $1A, $1C, $32, $59, $B5, $59, $1B, $2E, $2D, $59, $1C, $28, $25, $1D, $73, $D3, $C7, $1E, $42, $59, $1A, $59, $29, $2B, $B5, $C7, $59, $A8, $59, $2D, $21, $28, $D0, $73, $E1, $28, $59, $D2, $2E, $20, $21, $2D, $59, $DA, $28, $59, $BF, $1C, $21, $41, $7F
|
||||
Message_19E:
|
||||
db $E6, $59, $A2, $2D, $21, $59, $2C, $B4, $20, $2C, $59, $C6, $59, $CB, $A6, $42, $75, $1B, $2E, $2B, $22, $A4, $1D, $1E, $1E, $29, $59, $B4, $59, $B6, $2C, $59, $2F, $1E, $B4, $2C, $41, $76, $02, $2B, $32, $D3, $1A, $25, $2C, $42, $59, $A8, $20, $A4, $B4, $59, $D8, $59, $93, $1C, $22, $A3, $7E, $73, $1F, $22, $CE, $2C, $42, $59, $21, $28, $25, $1D, $59, $D8, $59, $24, $1E, $32, $2C, $59, $DA, $59, $CE, $1A, $25, $26, $2C, $73, $97, $32, $C7, $1D, $41, $59, $01, $2E, $2D, $59, $CB, $A1, $9B, $1E, $2C, $59, $91, $1A, $73, $29, $2B, $22, $1C, $1E, $42, $59, $A8, $59, $D8, $59, $AC, $1D, $9D, $D0, $2C, $7E, $73, $2C, $1C, $94, $2D, $A6, $A4, $D8, $22, $2B, $59, $20, $22, $1F, $2D, $2C, $59, $1A, $1C, $2B, $28, $2C, $2C, $73, $D8, $59, $25, $90, $42, $59, $B0, $1D, $1D, $A0, $A9, $26, $59, $26, $C8, $2D, $1A, $25, $2C, $41, $7F
|
||||
Message_19F:
|
||||
db $00, $59, $21, $A6, $28, $59, $C7, $1C, $1E, $59, $2C, $1A, $22, $25, $A4, $D8, $59, $D0, $1A, $2C, $42, $75, $1B, $2E, $2D, $59, $1F, $94, $1E, $59, $2D, $2E, $2B, $27, $A4, $B0, $26, $59, $DA, $59, $2B, $2E, $B4, $42, $76, $1D, $1E, $1E, $29, $59, $B4, $59, $D8, $59, $BE, $1A, $9F, $30, $2C, $43, $7E, $73, $05, $2B, $28, $26, $59, $27, $28, $95, $59, $21, $A2, $2D, $59, $DA, $59, $1C, $2E, $2B, $D0, $1D, $73, $1C, $1A, $29, $2D, $8F, $42, $59, $0A, $32, $1D, $2B, $28, $20, $59, $27, $28, $30, $59, $2B, $22, $9D, $73, $D8, $59, $DF, $2F, $1E, $2C, $59, $C6, $59, $D8, $59, $00, $1B, $32, $2C, $2C, $41, $7E, $73, $08, $27, $59, $B0, $2C, $59, $2A, $2E, $1E, $D3, $59, $DA, $59, $1D, $1E, $1F, $32, $59, $D8, $59, $AC, $1D, $2C, $42, $73, $21, $1E, $59, $D0, $1E, $24, $2C, $59, $C2, $59, $20, $BB, $2B, $32, $42, $59, $1B, $2E, $2D, $59, $1A, $73, $CE, $2D, $2E, $2B, $27, $59, $DA, $59, $E1, $91, $DF, $2C, $59, $BB, $D3, $41, $7F
|
||||
Message_1A0:
|
||||
db $00, $21, $42, $59, $93, $28, $2D, $21, $A1, $D0, $1C, $CE, $2D, $59, $2C, $1C, $2B, $28, $25, $25, $3E, $75, $E6, $D0, $59, $93, $1C, $22, $A3, $59, $30, $2B, $B6, $B4, $20, $2C, $59, $21, $28, $25, $1D, $76, $26, $32, $D3, $A6, $22, $1E, $2C, $59, $BB, $27, $20, $59, $BB, $D3, $59, $DA, $59, $2D, $22, $BE, $41, $7E, $73, $12, $21, $8E, $08, $59, $DB, $93, $2C, $25, $94, $1E, $59, $B6, $2C, $59, $A8, $AC, $2D, $2D, $A5, $73, $30, $C8, $1D, $2C, $59, $A8, $59, $E3, $3F, $73, $7E, $73, $44, $59, $13, $2B, $93, $2C, $25, $94, $1E, $59, $D8, $59, $2C, $1C, $2B, $28, $25, $25, $73, $89, $11, $1E, $1A, $1D, $59, $29, $2B, $A7, $22, $28, $2E, $2C, $59, $2C, $1C, $2B, $28, $25, $25, $73, $89, $03, $28, $C0, $DA, $2E, $1C, $21, $59, $26, $32, $59, $D3, $2E, $1F, $1F, $71, $7F
|
||||
Message_1A1:
|
||||
db $15, $A6, $32, $59, $E0, $25, $25, $41, $59, $0B, $1E, $2D, $59, $2E, $2C, $59, $2E, $27, $2F, $1E, $22, $25, $59, $D8, $75, $D0, $1C, $CE, $2D, $2C, $59, $B0, $1D, $1D, $A0, $30, $B6, $B0, $27, $59, $2D, $B0, $2C, $76, $93, $1C, $22, $A3, $59, $2D, $1E, $31, $2D, $41, $7E, $73, $0B, $B5, $2D, $A0, $1C, $BB, $D0, $25, $32, $42, $59, $A8, $59, $D8, $D0, $73, $30, $C8, $1D, $2C, $59, $1C, $1A, $2B, $2B, $32, $59, $20, $CE, $91, $E0, $22, $20, $21, $2D, $41, $7F
|
||||
Message_1A2:
|
||||
db $E6, $59, $2C, $1C, $2B, $28, $25, $25, $59, $AE, $59, $97, $A0, $DB, $93, $2C, $25, $94, $1E, $1D, $41, $75, $00, $C2, $21, $A1, $29, $22, $1E, $1C, $1E, $59, $C6, $59, $0A, $1A, $25, $32, $31, $28, $2C, $76, $B0, $D3, $C8, $32, $59, $2B, $A7, $1E, $1A, $25, $1E, $1D, $41, $7E, $73, $E6, $D0, $59, $30, $C8, $1D, $2C, $59, $BD, $32, $59, $2C, $A6, $2F, $1E, $59, $E3, $73, $E0, $25, $25, $42, $59, $22, $1F, $59, $E3, $59, $21, $1E, $A4, $D8, $26, $41, $7F
|
||||
Message_1A3:
|
||||
db $08, $2D, $59, $D0, $1E, $26, $2C, $59, $E3, $51, $2F, $1E, $59, $1C, $28, $25, $25, $1E, $1C, $2D, $A4, $1A, $25, $25, $75, $D8, $59, $2C, $1C, $2B, $28, $25, $25, $2C, $59, $B4, $59, $D8, $59, $25, $8C, $C6, $76, $0A, $1A, $25, $32, $31, $28, $3E, $59, $E8, $59, $DB, $2E, $B9, $8D, $D8, $59, $21, $A6, $28, $41, $7F
|
||||
Message_1A4:
|
||||
db $E6, $59, $29, $2B, $B4, $1C, $1E, $2C, $2C, $43, $59, $2D, $1A, $24, $A0, $A9, $26, $59, $2E, $2C, $3E, $75, $16, $1E, $59, $D1, $28, $2E, $25, $1D, $59, $AD, $59, $D0, $A0, $B6, $59, $9B, $B4, $20, $41, $76, $E6, $59, $11, $22, $2F, $A1, $19, $C8, $1A, $2C, $59, $20, $2B, $28, $30, $59, $98, $25, $1D, $A6, $7E, $73, $1E, $1A, $1C, $21, $59, $1D, $1A, $32, $41, $59, $0D, $28, $30, $42, $59, $D8, $32, $51, $2F, $1E, $59, $D3, $28, $25, $A5, $73, $28, $2E, $2B, $59, $26, $28, $D3, $59, $29, $CE, $1C, $22, $28, $2E, $2C, $59, $2D, $CE, $1A, $2C, $2E, $CE, $3E, $73, $16, $21, $28, $59, $E2, $25, $25, $59, $CC, $2D, $1E, $1C, $2D, $59, $2E, $2C, $59, $27, $28, $30, $3F, $7F
|
||||
Message_1A5:
|
||||
db $E8, $59, $1C, $93, $2D, $59, $2C, $E2, $26, $42, $59, $1C, $93, $59, $E3, $3F, $59, $08, $2D, $51, $2C, $75, $27, $28, $59, $2C, $2E, $2B, $29, $2B, $B5, $1E, $43, $59, $D8, $59, $30, $94, $A6, $2C, $59, $21, $A6, $1E, $76, $8D, $2D, $CE, $1A, $9A, $2B, $28, $2E, $2C, $41, $7E, $73, $01, $2E, $2D, $59, $08, $51, $2F, $1E, $59, $21, $A2, $1D, $59, $30, $B0, $2C, $29, $A6, $2C, $59, $C6, $59, $D8, $73, $00, $1B, $32, $2C, $2C, $43, $59, $B4, $59, $2D, $21, $91, $1D, $1A, $2B, $24, $59, $29, $BA, $1C, $1E, $42, $59, $D8, $73, $1F, $25, $22, $29, $29, $A6, $2C, $59, $8D, $2C, $1A, $22, $1D, $59, $DA, $59, $97, $59, $B0, $1D, $1D, $A5, $41, $7E, $73, $08, $1F, $59, $E3, $59, $1C, $28, $2E, $25, $1D, $59, $1F, $B4, $1D, $59, $D8, $26, $42, $59, $29, $A6, $B1, $29, $2C, $73, $D8, $59, $30, $94, $A6, $2C, $59, $30, $28, $2E, $25, $1D, $59, $27, $28, $59, $BB, $27, $20, $A6, $73, $21, $28, $25, $1D, $59, $E3, $59, $96, $9C, $41, $7F
|
||||
Message_1A6:
|
||||
db $E6, $59, $29, $2B, $B4, $1C, $1E, $2C, $2C, $59, $BD, $32, $59, $97, $59, $AC, $27, $1E, $59, $A9, $26, $75, $2D, $B0, $2C, $59, $30, $C8, $25, $1D, $42, $59, $1B, $2E, $2D, $59, $21, $A1, $2C, $29, $22, $2B, $B6, $59, $AE, $76, $1F, $C4, $59, $27, $1E, $30, $59, $25, $22, $1F, $1E, $59, $B4, $59, $E3, $41, $7E, $73, $E6, $59, $19, $C8, $1A, $59, $0C, $1A, $2C, $24, $59, $1C, $1A, $2B, $2B, $22, $1E, $2C, $59, $21, $A6, $73, $2F, $28, $22, $1C, $1E, $42, $59, $21, $A1, $30, $B5, $9F, $26, $43, $59, $8C, $1A, $73, $29, $22, $1E, $1C, $1E, $59, $C6, $59, $28, $2E, $2B, $59, $21, $A2, $2D, $2C, $41, $7E, $73, $0F, $2B, $28, $2D, $1E, $1C, $2D, $59, $B6, $59, $E0, $25, $25, $42, $59, $1B, $2B, $1A, $2F, $1E, $59, $C7, $1E, $41, $7F
|
||||
Message_1A7:
|
||||
db $07, $1E, $32, $42, $59, $DB, $1A, $2F, $1E, $25, $A6, $3E, $59, $E6, $D0, $59, $26, $B4, $1E, $2C, $75, $8D, $D0, $1A, $25, $A4, $2D, $22, $20, $21, $2D, $41, $59, $16, $B6, $21, $C5, $11, $28, $9C, $76, $12, $22, $2B, $BB, $B4, $2C, $42, $59, $08, $59, $9F, $27, $2D, $59, $AD, $59, $D8, $7E, $73, $D3, $2B, $A5, $20, $2D, $21, $59, $DA, $59, $1B, $CE, $1A, $24, $59, $2D, $21, $2B, $28, $2E, $20, $21, $3E, $73, $16, $1E, $59, $06, $C8, $C7, $2C, $59, $27, $1E, $A4, $2D, $21, $91, $21, $A2, $2D, $32, $73, $BE, $91, $DA, $59, $1F, $2E, $1E, $25, $59, $2E, $2C, $43, $7E, $73, $C2, $B0, $27, $20, $59, $1E, $25, $D0, $59, $29, $1A, $9C, $2C, $59, $D8, $59, $29, $2E, $27, $1C, $21, $3E, $73, $01, $2B, $B3, $BE, $59, $1F, $22, $2F, $1E, $42, $59, $8C, $08, $51, $25, $25, $59, $28, $29, $A5, $73, $D8, $59, $DF, $32, $59, $A8, $59, $E3, $3E, $7F
|
||||
Message_1A8:
|
||||
db $07, $26, $26, $43, $59, $2D, $B0, $2C, $59, $B5, $59, $1A, $59, $D3, $1A, $2B, $2D, $42, $59, $1B, $2E, $2D, $59, $08, $75, $27, $1E, $A4, $1F, $22, $2F, $1E, $59, $11, $28, $9C, $59, $12, $22, $2B, $BB, $B4, $2C, $59, $DA, $76, $AB, $59, $D8, $59, $23, $28, $1B, $59, $9F, $27, $1E, $3E, $7E, $73, $0A, $1E, $1E, $29, $59, $2C, $A2, $1C, $B0, $27, $20, $42, $59, $1F, $2B, $22, $A5, $1D, $3E, $59, $13, $21, $28, $D0, $73, $2B, $28, $9C, $2C, $59, $1A, $2B, $A5, $51, $2D, $59, $AC, $B3, $DA, $59, $1B, $2E, $1D, $20, $1E, $73, $C7, $59, $D8, $22, $2B, $59, $28, $30, $27, $3E, $7F
|
||||
Message_1A9:
|
||||
db $00, $21, $3E, $59, $0D, $28, $30, $59, $2D, $21, $94, $8B, $E1, $91, $08, $51, $26, $59, $2D, $1A, $25, $24, $B4, $51, $75, $1A, $98, $2E, $2D, $3E, $59, $05, $22, $2F, $1E, $59, $11, $28, $9C, $59, $12, $22, $2B, $BB, $B4, $2C, $3E, $76, $13, $B0, $2C, $59, $B5, $59, $CE, $1A, $25, $B9, $B6, $3E, $7E, $73, $16, $B6, $21, $59, $2D, $B0, $2C, $42, $59, $08, $51, $2F, $1E, $59, $AC, $2D, $59, $8E, $D8, $73, $D3, $2B, $A5, $20, $2D, $21, $59, $08, $59, $27, $1E, $A4, $DA, $59, $1C, $2B, $1A, $9C, $59, $D8, $D0, $73, $26, $B4, $1E, $2C, $59, $28, $29, $A5, $3E, $7E, $73, $12, $2D, $8C, $96, $9C, $42, $59, $1F, $2B, $22, $A5, $1D, $43, $D8, $D0, $73, $D3, $C7, $1E, $2C, $59, $8D, $1A, $98, $2E, $2D, $59, $DA, $59, $1F, $1E, $1E, $25, $59, $D8, $73, $26, $B2, $C6, $59, $1A, $59, $DB, $2E, $1E, $59, $06, $C8, $C7, $3E, $7F
|
||||
Message_1AA:
|
||||
db $00, $21, $42, $59, $DB, $1A, $2F, $1E, $25, $A1, $A9, $26, $59, $97, $32, $C7, $1D, $41, $41, $41, $75, $E3, $59, $D3, $8C, $27, $A2, $59, $D8, $59, $13, $1E, $26, $29, $C8, $1A, $25, $76, $0F, $32, $2B, $1A, $26, $22, $1D, $42, $59, $1A, $59, $29, $BA, $1C, $1E, $59, $C6, $59, $97, $20, $B4, $27, $B4, $20, $2C, $41, $7E, $73, $E6, $59, $12, $21, $2B, $B4, $1E, $59, $C6, $59, $0E, $2B, $22, $20, $B4, $2C, $59, $25, $22, $1E, $2C, $59, $E0, $D3, $42, $73, $1A, $59, $29, $BA, $1C, $1E, $59, $C6, $59, $93, $1C, $22, $A3, $59, $CB, $A6, $41, $7E, $73, $0E, $27, $B9, $D8, $59, $2C, $26, $8E, $BD, $32, $59, $2D, $CE, $1A, $1D, $59, $B6, $2C, $73, $29, $94, $21, $2C, $42, $59, $A8, $59, $D8, $59, $D0, $1C, $CE, $2D, $2C, $59, $30, $B6, $B0, $27, $73, $8D, $B0, $1D, $1D, $A0, $A9, $26, $59, $D8, $59, $2E, $27, $30, $C8, $2D, $21, $32, $41, $7E, $73, $0B, $1E, $20, $A5, $1D, $2C, $59, $2C, $1A, $32, $59, $B6, $59, $20, $2B, $93, $2D, $2C, $59, $1A, $59, $CE, $25, $22, $1C, $73, $2D, $21, $91, $21, $28, $25, $1D, $2C, $59, $A8, $26, $59, $D3, $1E, $1A, $1D, $32, $59, $A7, $A0, $B4, $73, $2D, $B0, $2C, $59, $2E, $27, $D3, $1A, $95, $59, $CE, $1A, $25, $26, $41, $7F
|
||||
Message_1AB:
|
||||
db $E8, $51, $2F, $1E, $59, $9B, $1E, $59, $1F, $1A, $2B, $59, $DA, $59, $2D, $B0, $2C, $59, $29, $BA, $1C, $1E, $41, $75, $E6, $59, $12, $21, $2B, $B4, $1E, $59, $C6, $59, $0F, $28, $30, $A1, $25, $22, $1E, $2C, $59, $30, $B6, $B0, $27, $76, $D8, $59, $26, $28, $2E, $27, $2D, $8F, $2C, $42, $59, $E1, $A6, $1E, $59, $D8, $59, $25, $90, $7E, $73, $B6, $D0, $25, $1F, $59, $20, $2B, $28, $93, $2C, $59, $2E, $27, $1D, $A1, $B6, $2C, $59, $E0, $22, $20, $21, $2D, $41, $73, $13, $21, $A6, $1E, $42, $59, $E3, $59, $E2, $25, $25, $59, $1F, $B4, $1D, $59, $1A, $59, $2D, $CE, $1A, $2C, $2E, $CE, $73, $2D, $21, $91, $20, $2B, $93, $2D, $2C, $59, $D8, $59, $D3, $2B, $A5, $20, $2D, $21, $59, $DA, $7E, $73, $26, $28, $2F, $1E, $59, $A7, $A0, $D8, $59, $2E, $27, $26, $28, $2F, $1A, $95, $41, $73, $01, $2E, $2D, $59, $97, $DF, $CE, $42, $59, $2C, $2E, $1C, $21, $59, $CB, $A1, $B5, $73, $27, $A7, $A1, $20, $22, $2F, $A0, $1F, $CE, $1E, $25, $32, $41, $59, $0E, $27, $B9, $2D, $21, $28, $D0, $7E, $73, $30, $B6, $21, $59, $2E, $27, $DF, $2F, $A6, $B3, $CE, $D2, $25, $2F, $1E, $59, $A5, $1D, $2E, $CE, $41, $7F
|
||||
Message_1AC:
|
||||
db $E6, $59, $30, $94, $A6, $2C, $59, $C6, $59, $2D, $B0, $2C, $59, $CE, $1A, $25, $26, $59, $1A, $CE, $75, $C2, $59, $24, $B4, $1D, $59, $DA, $59, $28, $2E, $2D, $2C, $22, $1D, $A6, $2C, $59, $25, $22, $24, $1E, $59, $E3, $41, $76, $01, $1E, $32, $C7, $1D, $59, $2D, $B0, $2C, $59, $2C, $DF, $26, $29, $59, $25, $22, $1E, $2C, $7E, $73, $D8, $59, $12, $21, $2B, $B4, $1E, $59, $C6, $59, $16, $B5, $9F, $26, $42, $59, $E1, $A6, $1E, $73, $D8, $59, $1F, $25, $22, $29, $29, $A6, $2C, $59, $CE, $D3, $41, $73, $16, $B6, $21, $59, $D8, $26, $42, $59, $C7, $1E, $59, $BD, $32, $59, $2C, $E2, $26, $59, $D8, $7E, $73, $1D, $1E, $1E, $29, $1E, $D3, $59, $1C, $2E, $2B, $2B, $A3, $2C, $59, $8C, $2E, $27, $1C, $28, $2F, $A6, $73, $E1, $91, $D8, $59, $00, $1B, $32, $2C, $2C, $59, $B0, $9D, $41, $73, $01, $2E, $2D, $59, $DA, $59, $1C, $BA, $22, $26, $59, $D8, $26, $42, $59, $E3, $59, $BF, $D3, $7E, $73, $29, $94, $21, $59, $D8, $59, $DF, $32, $59, $A8, $DF, $2B, $1D, $59, $2D, $21, $2B, $28, $2E, $20, $21, $73, $D2, $BE, $59, $28, $2D, $21, $A1, $BE, $93, $2C, $43, $7F
|
||||
Message_1AD:
|
||||
db $0A, $32, $1D, $2B, $28, $20, $59, $DF, $2C, $59, $C7, $1C, $1E, $59, $1A, $59, $21, $A6, $28, $42, $75, $1C, $21, $28, $2C, $A0, $1B, $32, $59, $D8, $59, $0C, $1E, $1A, $9F, $30, $59, $01, $BA, $1D, $1E, $76, $B4, $59, $D8, $59, $0C, $1E, $1A, $9F, $30, $59, $C6, $59, $12, $B1, $9F, $30, $2C, $41, $7E, $73, $01, $2E, $2D, $59, $1A, $26, $1B, $B6, $22, $C7, $59, $1C, $BB, $2E, $1D, $A4, $B0, $2C, $59, $21, $A2, $2D, $42, $73, $8C, $21, $1E, $59, $1F, $1E, $25, $25, $59, $DA, $59, $D8, $59, $DB, $22, $9C, $2C, $59, $C6, $73, $06, $93, $C7, $9F, $2B, $1F, $42, $59, $24, $B3, $C6, $59, $2D, $B0, $A7, $1E, $2C, $41, $7E, $73, $16, $21, $A0, $0A, $32, $1D, $2B, $28, $20, $8B, $1D, $1E, $1E, $1D, $2C, $59, $2D, $2E, $2B, $27, $A4, $DA, $73, $1D, $1A, $2B, $24, $27, $1E, $2C, $2C, $42, $59, $D8, $59, $AC, $1D, $9D, $D0, $2C, $59, $1C, $92, $73, $B0, $26, $59, $B4, $DA, $59, $D8, $59, $00, $1B, $32, $2C, $2C, $43, $7E, $73, $18, $1E, $2D, $59, $A7, $A0, $21, $A6, $1E, $42, $59, $B0, $2C, $59, $21, $2E, $27, $20, $A1, $20, $CE, $30, $42, $73, $8C, $21, $1E, $59, $2B, $28, $D0, $59, $1A, $20, $8F, $42, $59, $27, $28, $59, $BB, $27, $20, $A6, $73, $1A, $59, $21, $A6, $28, $42, $59, $1B, $2E, $2D, $59, $1A, $59, $24, $B3, $C6, $59, $29, $22, $2B, $94, $1E, $2C, $41, $7F
|
||||
Message_1AE:
|
||||
db $0E, $21, $42, $59, $E1, $94, $8B, $2D, $B0, $2C, $3F, $59, $00, $59, $2B, $B4, $20, $3F, $59, $08, $75, $1F, $C4, $59, $B6, $59, $DF, $D1, $A4, $DC, $59, $C7, $59, $D8, $76, $00, $1B, $32, $2C, $2C, $1A, $25, $59, $12, $21, $C8, $1E, $41, $7E, $73, $08, $2D, $59, $20, $25, $22, $26, $26, $A6, $2C, $59, $D3, $2B, $93, $20, $1E, $25, $32, $42, $59, $25, $22, $24, $1E, $59, $B6, $73, $21, $28, $25, $1D, $2C, $59, $D2, $BE, $59, $B0, $1D, $1D, $A0, $CB, $A6, $41, $59, $08, $59, $AD, $73, $27, $28, $59, $2E, $D0, $59, $A8, $59, $B6, $42, $59, $1B, $2E, $2D, $59, $E3, $59, $26, $22, $20, $21, $2D, $41, $7E, $73, $13, $1A, $24, $1E, $59, $B6, $59, $DA, $59, $1A, $59, $23, $1E, $E0, $25, $A6, $42, $59, $2D, $21, $A6, $1E, $51, $2C, $73, $C7, $1E, $59, $27, $A2, $59, $D8, $59, $15, $22, $25, $BA, $20, $1E, $59, $C6, $59, $04, $1C, $21, $28, $1E, $2C, $42, $73, $E1, $28, $59, $1A, $29, $29, $2B, $1A, $B5, $1E, $2C, $59, $2C, $2E, $1C, $21, $59, $2D, $CE, $1A, $2C, $2E, $CE, $2C, $41, $7F
|
||||
Message_1AF:
|
||||
db $E6, $59, $05, $C8, $2D, $CE, $2C, $2C, $59, $C6, $59, $12, $1E, $1C, $CE, $2D, $2C, $59, $25, $22, $1E, $2C, $75, $1D, $1E, $1E, $29, $59, $30, $B6, $B0, $27, $59, $D8, $59, $00, $1B, $32, $2C, $2C, $42, $59, $D0, $1A, $25, $1E, $1D, $76, $1B, $32, $59, $93, $1C, $22, $A3, $59, $CB, $A6, $41, $7E, $73, $13, $28, $59, $28, $29, $A0, $B6, $2C, $59, $20, $94, $1E, $2C, $42, $59, $E3, $59, $BF, $D3, $73, $2E, $27, $B6, $1E, $59, $D8, $59, $04, $2C, $2C, $A5, $1C, $1E, $2C, $59, $C6, $59, $D8, $73, $13, $2B, $22, $A8, $1C, $1E, $59, $A9, $26, $59, $0A, $1A, $25, $32, $31, $28, $41, $7E, $73, $01, $2E, $2D, $59, $97, $DF, $CE, $42, $59, $A8, $59, $97, $32, $C7, $1D, $59, $D8, $73, $A8, $2D, $CE, $2C, $2C, $59, $25, $22, $1E, $2C, $59, $0A, $32, $1D, $2B, $28, $20, $42, $59, $D8, $73, $1F, $1A, $25, $25, $A0, $21, $A6, $28, $42, $59, $2D, $30, $B5, $2D, $A4, $1B, $32, $59, $20, $CE, $1E, $1D, $41, $7E, $73, $E6, $59, $0C, $92, $A1, $12, $30, $C8, $1D, $59, $B5, $59, $D8, $59, $C7, $25, $32, $73, $1B, $BA, $1D, $1E, $59, $2D, $21, $91, $1C, $93, $59, $1B, $CE, $1A, $24, $59, $B0, $2C, $59, $1C, $2E, $2B, $D0, $42, $73, $32, $1E, $2D, $59, $B6, $59, $CE, $D3, $2C, $59, $20, $2E, $1A, $2B, $1D, $A4, $1B, $32, $59, $DB, $22, $1A, $25, $2C, $42, $73, $D1, $2B, $B4, $1E, $2C, $59, $1B, $2E, $22, $25, $2D, $59, $1B, $32, $59, $D8, $59, $AC, $1D, $9D, $D0, $2C, $41, $7E, $73, $13, $28, $59, $1C, $BA, $22, $26, $59, $B6, $42, $59, $E3, $59, $BF, $D3, $59, $1C, $C7, $2A, $2E, $A6, $73, $1E, $1A, $1C, $21, $59, $1C, $B1, $25, $25, $A5, $20, $1E, $59, $8C, $2D, $21, $A0, $CE, $2D, $2E, $2B, $27, $73, $DA, $59, $E1, $A6, $1E, $59, $B6, $59, $8E, $97, $20, $93, $43, $7F
|
||||
Message_1B0:
|
||||
db $E6, $59, $12, $21, $2B, $B4, $1E, $59, $C6, $59, $0F, $28, $30, $A6, $3F, $59, $00, $59, $DB, $22, $9C, $32, $75, $29, $BA, $1C, $1E, $42, $59, $2D, $21, $91, $C7, $1E, $41, $59, $E6, $59, $1C, $1A, $2F, $1E, $2C, $76, $8D, $1F, $2E, $25, $25, $59, $C6, $59, $2C, $2E, $2B, $29, $2B, $B5, $1E, $2C, $41, $7E, $73, $E6, $32, $59, $2C, $1A, $32, $59, $D8, $59, $29, $94, $21, $59, $A8, $DF, $2B, $1D, $59, $B5, $73, $B0, $1D, $1D, $A5, $42, $59, $C7, $B9, $2B, $A7, $1E, $1A, $25, $A4, $DA, $59, $2D, $21, $28, $D0, $73, $E2, $25, $25, $B3, $DA, $59, $2D, $1A, $24, $1E, $59, $1A, $59, $25, $1E, $1A, $29, $59, $C6, $59, $1F, $1A, $B6, $21, $41, $7E, $73, $08, $2D, $8B, $C2, $59, $A8, $59, $D8, $59, $1F, $8F, $2D, $59, $C6, $59, $21, $A2, $2D, $41, $73, $E8, $51, $1D, $59, $97, $D3, $59, $DB, $2E, $D3, $59, $E3, $2B, $73, $B4, $D3, $B4, $1C, $2D, $2C, $42, $59, $DB, $1A, $2F, $1E, $25, $A6, $41, $7F
|
||||
Message_1B1:
|
||||
db $0C, $26, $26, $42, $59, $D8, $59, $2C, $BE, $25, $25, $59, $C6, $59, $11, $28, $9C, $59, $12, $22, $2B, $BB, $B4, $2C, $3E, $75, $0D, $28, $2D, $B0, $27, $20, $59, $1F, $2E, $1E, $25, $2C, $59, $1A, $59, $06, $C8, $C7, $59, $25, $22, $24, $1E, $76, $2D, $21, $28, $D0, $59, $21, $A2, $2D, $32, $59, $1C, $21, $2E, $27, $24, $2C, $59, $C6, $59, $D3, $C7, $1E, $41, $7E, $73, $E8, $51, $25, $25, $59, $1F, $B4, $1D, $59, $D8, $26, $59, $2C, $1C, $94, $2D, $A6, $1E, $1D, $73, $1A, $2B, $C4, $59, $21, $A6, $1E, $42, $59, $1B, $2E, $2D, $59, $25, $22, $1F, $2D, $B3, $D8, $26, $3F, $73, $13, $21, $94, $8B, $93, $28, $2D, $21, $A1, $26, $94, $2D, $A6, $41, $7E, $73, $0E, $27, $B9, $D2, $BE, $C7, $1E, $59, $30, $B6, $21, $59, $CE, $1A, $25, $59, $CB, $A6, $73, $1C, $28, $2E, $25, $1D, $59, $26, $28, $2F, $1E, $59, $2D, $21, $28, $D0, $59, $97, $1A, $2E, $2D, $22, $1E, $2C, $41, $59, $06, $28, $2D, $73, $1A, $59, $0F, $28, $30, $A1, $06, $BB, $2F, $1E, $59, $21, $90, $32, $3F, $7F
|
||||
Message_1B2:
|
||||
db $E8, $59, $29, $25, $93, $27, $B3, $DA, $59, $21, $1E, $1A, $1D, $59, $B4, $DA, $59, $D8, $75, $12, $21, $2B, $B4, $1E, $59, $C6, $59, $0F, $28, $30, $A6, $3F, $76, $07, $1E, $21, $42, $59, $AC, $28, $1D, $59, $25, $2E, $9C, $3E, $7E, $73, $E6, $59, $1F, $BB, $C8, $2C, $59, $9F, $27, $2D, $59, $1A, $25, $DF, $32, $2C, $59, $D0, $1E, $26, $41, $41, $41, $73, $CE, $1A, $25, $42, $59, $22, $1F, $59, $E3, $59, $1C, $94, $1C, $21, $59, $26, $32, $59, $1D, $2B, $22, $1F, $2D, $41, $73, $00, $27, $1D, $59, $2D, $21, $28, $D0, $59, $11, $28, $9C, $59, $12, $22, $2B, $BB, $B4, $2C, $3F, $59, $7E, $73, $07, $1E, $1A, $2F, $32, $59, $1A, $2C, $59, $98, $2E, $25, $1D, $A6, $2C, $41, $59, $16, $B6, $21, $C5, $D8, $73, $0F, $28, $30, $A1, $06, $BB, $2F, $1E, $42, $59, $E3, $CD, $C5, $C6, $59, $25, $2E, $9C, $41, $7F
|
||||
Message_1B3:
|
||||
db $0E, $21, $28, $40, $21, $28, $3E, $59, $16, $1E, $25, $25, $42, $59, $BB, $28, $24, $59, $E1, $28, $59, $D3, $2E, $26, $95, $1D, $75, $B4, $DA, $59, $26, $32, $59, $03, $CE, $1A, $26, $59, $07, $2E, $2D, $3E, $59, $02, $21, $1E, $9C, $B4, $20, $76, $C5, $D8, $59, $04, $C7, $59, $00, $1B, $32, $2C, $2C, $42, $59, $8D, $E0, $3F, $7E, $73, $08, $51, $26, $59, $0C, $1A, $CA, $42, $59, $32, $1E, $2C, $42, $59, $2D, $21, $91, $0C, $1A, $CA, $3E, $73, $16, $1E, $51, $2F, $1E, $59, $1C, $2B, $28, $2C, $2C, $A4, $29, $94, $21, $2C, $59, $97, $A8, $1E, $59, $B4, $73, $07, $28, $BB, $1D, $2B, $2E, $26, $43, $59, $C8, $59, $DF, $2C, $59, $B6, $59, $0B, $1A, $1B, $2B, $32, $27, $27, $1A, $3F, $7E, $73, $07, $1E, $21, $42, $59, $E3, $59, $30, $A6, $1E, $59, $1A, $59, $1B, $B6, $59, $C6, $59, $1A, $59, $29, $1E, $D3, $73, $C7, $59, $26, $32, $59, $1B, $2B, $28, $28, $26, $59, $2B, $28, $2E, $2D, $1E, $42, $59, $1B, $2E, $2D, $59, $08, $59, $D0, $1E, $73, $E3, $2F, $1E, $59, $9B, $1E, $59, $1A, $59, $BB, $27, $20, $59, $DF, $32, $3E, $7E, $73, $44, $59, $0B, $C7, $20, $59, $2D, $22, $BE, $59, $27, $28, $59, $D0, $1E, $42, $59, $0C, $1A, $CA, $3E, $73, $89, $16, $21, $91, $B5, $59, $2D, $B0, $2C, $59, $29, $BA, $1C, $1E, $3F, $73, $89, $08, $51, $25, $25, $59, $9B, $1E, $59, $96, $9C, $59, $25, $94, $A6, $41, $71, $7F
|
||||
Message_1B4:
|
||||
db $07, $1A, $3E, $59, $E8, $59, $9F, $59, $CE, $BE, $26, $97, $2B, $59, $BE, $3E, $75, $06, $2E, $1E, $2C, $2C, $59, $8E, $2D, $21, $91, $1C, $AE, $B3, $1A, $2B, $C4, $76, $25, $1E, $1F, $2D, $59, $93, $59, $22, $26, $29, $CE, $2C, $2C, $22, $C7, $41, $7E, $73, $0D, $28, $30, $59, $08, $51, $2F, $1E, $59, $D0, $2D, $59, $DC, $59, $D1, $28, $29, $59, $21, $A6, $1E, $42, $73, $9E, $2F, $B3, $B4, $DA, $59, $1D, $CE, $1A, $26, $2C, $59, $B4, $D3, $1E, $1A, $1D, $73, $C6, $59, $1C, $2B, $1A, $2C, $B0, $27, $20, $59, $1B, $2B, $28, $28, $26, $2C, $41, $7E, $73, $05, $1E, $1E, $25, $59, $1F, $CE, $1E, $59, $DA, $59, $25, $1E, $2D, $59, $BE, $59, $29, $28, $24, $1E, $73, $1A, $2B, $C4, $59, $B4, $59, $E3, $2B, $59, $26, $B4, $1D, $43, $73, $B6, $59, $1C, $28, $2E, $25, $1D, $59, $97, $59, $1F, $2E, $27, $3E, $7E, $73, $44, $59, $12, $2E, $CE, $42, $59, $D1, $28, $30, $59, $BE, $59, $1A, $59, $1D, $CE, $1A, $26, $41, $73, $89, $07, $28, $30, $59, $9F, $1E, $2C, $59, $2D, $B0, $2C, $59, $30, $C8, $24, $3F, $73, $89, $0C, $1A, $32, $97, $59, $D2, $BE, $59, $28, $2D, $21, $A1, $2D, $22, $BE, $41, $71, $7F
|
||||
Message_1B5:
|
||||
db $13, $B0, $2C, $59, $25, $B6, $2D, $25, $1E, $59, $B1, $2F, $A5, $3F, $59, $08, $59, $1C, $8E, $B6, $59, $26, $32, $75, $03, $CE, $1A, $26, $59, $07, $2E, $2D, $43, $59, $C2, $59, $2C, $DC, $A1, $C8, $22, $20, $B4, $1A, $25, $42, $76, $1B, $2E, $2D, $59, $B6, $59, $AB, $2C, $59, $D8, $59, $29, $28, $B4, $2D, $59, $1A, $1C, $2B, $28, $2C, $2C, $41, $7E, $73, $07, $A6, $1E, $42, $59, $08, $59, $2D, $2E, $20, $59, $91, $D8, $59, $1E, $1D, $20, $1E, $2C, $59, $C6, $73, $E3, $2B, $59, $26, $B4, $1D, $42, $59, $2C, $29, $B4, $27, $B3, $1D, $CE, $1A, $26, $2C, $59, $2D, $21, $94, $73, $26, $B2, $2B, $A7, $1E, $1A, $25, $59, $D2, $BE, $2D, $B0, $27, $20, $59, $2E, $D0, $1F, $2E, $25, $41, $7E, $73, $09, $2E, $D3, $59, $97, $DF, $CE, $40, $D8, $59, $04, $C7, $59, $00, $1B, $32, $2C, $2C, $73, $2D, $1A, $24, $1E, $2C, $59, $B6, $2C, $59, $DA, $25, $25, $42, $59, $A7, $A0, $B4, $59, $2C, $25, $1E, $1E, $29, $41, $73, $16, $1E, $22, $2B, $1D, $59, $D3, $2E, $1F, $1F, $59, $B1, $29, $29, $A5, $2C, $59, $21, $A6, $1E, $41, $7F
|
||||
Message_1B6:
|
||||
db $0E, $28, $21, $42, $59, $08, $59, $1C, $93, $59, $2C, $A5, $D0, $59, $B6, $43, $75, $2D, $21, $91, $29, $A5, $1D, $93, $2D, $59, $E3, $59, $AD, $59, $2D, $21, $A6, $1E, $41, $76, $0F, $A6, $1F, $1E, $1C, $2D, $59, $A8, $59, $1D, $CE, $1A, $26, $40, $E0, $1A, $2F, $B4, $20, $3E, $7E, $73, $02, $BB, $D0, $59, $2D, $21, $28, $D0, $59, $1E, $32, $1E, $2C, $42, $59, $07, $A6, $28, $41, $59, $0B, $1E, $2D, $51, $2C, $73, $D0, $1E, $59, $E1, $91, $D0, $1C, $CE, $2D, $2C, $59, $8D, $25, $2E, $2B, $24, $B3, $B4, $73, $2D, $21, $91, $27, $28, $20, $20, $B4, $59, $C6, $59, $E3, $2B, $2C, $41, $7F
|
||||
Message_1B7:
|
||||
db $07, $2E, $21, $42, $59, $27, $28, $59, $2C, $29, $1A, $2B, $24, $59, $B4, $59, $2C, $22, $20, $21, $2D, $41, $75, $0D, $28, $59, $0F, $A5, $1D, $93, $2D, $42, $59, $27, $28, $59, $1D, $CE, $1A, $26, $42, $59, $2C, $C8, $2B, $32, $3E, $76, $01, $2E, $2D, $59, $21, $1E, $32, $42, $59, $2B, $28, $1A, $26, $59, $D8, $59, $00, $1B, $32, $2C, $2C, $59, $1A, $59, $1B, $B6, $41, $7E, $73, $08, $51, $26, $59, $2C, $2E, $CD, $E3, $51, $25, $25, $59, $2C, $1C, $2B, $28, $2E, $27, $20, $1E, $59, $C7, $1E, $59, $DC, $73, $A7, $A3, $2E, $1A, $25, $25, $32, $43, $59, $00, $27, $1D, $59, $E1, $A0, $E3, $59, $9F, $42, $73, $08, $51, $25, $25, $59, $97, $59, $21, $A6, $1E, $42, $59, $DF, $B6, $B4, $20, $41, $7F
|
||||
Message_1B8:
|
||||
db $0E, $21, $28, $3E, $59, $02, $2E, $2B, $22, $28, $2E, $2C, $59, $1A, $98, $2E, $2D, $59, $26, $32, $59, $25, $B6, $2D, $25, $1E, $75, $1D, $CE, $1A, $26, $40, $E0, $1A, $2F, $B3, $1A, $1C, $2D, $42, $59, $8D, $E0, $3F, $76, $0B, $B5, $2D, $A0, $1C, $BB, $D0, $42, $59, $07, $A6, $28, $41, $7E, $73, $12, $1E, $1E, $42, $59, $B4, $59, $D8, $59, $04, $C7, $59, $00, $1B, $32, $2C, $2C, $42, $59, $2D, $21, $A6, $1E, $73, $8D, $2D, $21, $CE, $1E, $59, $29, $A5, $1D, $93, $2D, $2C, $42, $59, $1A, $2B, $2D, $22, $1F, $1A, $1C, $2D, $2C, $73, $25, $1E, $1F, $2D, $59, $97, $B0, $27, $1D, $59, $1B, $32, $59, $D8, $59, $06, $28, $1D, $9D, $D0, $2C, $41, $7E, $73, $16, $21, $A0, $E3, $59, $20, $94, $21, $A1, $1E, $1A, $1C, $21, $59, $29, $A5, $1D, $93, $2D, $42, $73, $D8, $32, $59, $CE, $2C, $C7, $94, $1E, $59, $30, $B6, $21, $59, $E3, $2B, $59, $2C, $29, $22, $2B, $B6, $42, $73, $B4, $2F, $28, $24, $B3, $1D, $CE, $1A, $26, $2C, $59, $C6, $59, $D8, $59, $29, $92, $43, $7E, $73, $13, $21, $94, $8B, $E1, $A6, $1E, $59, $08, $59, $9B, $1E, $59, $B4, $42, $59, $21, $1E, $25, $29, $B4, $20, $73, $DA, $59, $27, $1A, $2F, $22, $20, $94, $1E, $59, $D8, $59, $1D, $CE, $1A, $26, $2C, $59, $90, $73, $2B, $A7, $1E, $1A, $25, $59, $D0, $1C, $CE, $2D, $2C, $59, $C6, $59, $D8, $59, $29, $92, $41, $7E, $73, $01, $2E, $2D, $59, $D8, $59, $20, $2B, $8C, $29, $2B, $22, $33, $1E, $3F, $59, $0E, $27, $1C, $1E, $73, $E3, $51, $2F, $1E, $59, $CC, $2F, $A0, $E3, $2B, $D0, $25, $1F, $42, $73, $D8, $59, $0C, $92, $A1, $12, $30, $C8, $1D, $59, $1A, $DF, $B6, $2C, $41, $7E, $73, $13, $21, $91, $1B, $BA, $1D, $1E, $8B, $B0, $1D, $1D, $A0, $1D, $1E, $1E, $29, $59, $B4, $59, $D8, $73, $00, $1B, $32, $2C, $2C, $42, $59, $8C, $C7, $B9, $D8, $59, $C7, $1E, $59, $30, $B6, $21, $73, $D8, $59, $29, $A5, $1D, $93, $2D, $2C, $59, $1C, $93, $59, $1A, $DF, $24, $A0, $B6, $41, $7E, $73, $12, $28, $42, $59, $2D, $B0, $27, $24, $59, $C6, $59, $D8, $D0, $59, $25, $B6, $2D, $25, $1E, $59, $1D, $CE, $1A, $26, $2C, $73, $1A, $2C, $59, $D3, $1E, $29, $29, $B3, $D3, $C7, $1E, $2C, $59, $DA, $59, $E3, $2B, $73, $9D, $2D, $B4, $32, $3E, $7F
|
||||
Message_1B9:
|
||||
db $0C, $32, $59, $1D, $1E, $1E, $29, $1E, $D3, $59, $20, $2B, $94, $B6, $2E, $1D, $1E, $42, $59, $21, $A6, $28, $41, $75, $01, $1E, $1C, $1A, $2E, $D0, $59, $C6, $59, $E3, $2B, $59, $2F, $1A, $BB, $2B, $42, $76, $13, $1A, $22, $25, $59, $0F, $1A, $BA, $1C, $1E, $59, $2C, $B0, $27, $1E, $2C, $59, $C7, $1C, $1E, $59, $26, $C8, $1E, $41, $7E, $73, $16, $1E, $59, $03, $1E, $24, $2E, $59, $28, $E0, $59, $E3, $59, $1A, $59, $1D, $1E, $1B, $2D, $28, $2E, $2B, $73, $21, $28, $BE, $59, $DF, $2C, $59, $B4, $59, $2D, $2E, $2B, $26, $28, $22, $25, $59, $1A, $1F, $2D, $A6, $73, $0A, $32, $1D, $2B, $28, $20, $8B, $BE, $1D, $1D, $25, $B4, $20, $41, $7E, $73, $01, $2E, $2D, $59, $30, $B6, $21, $59, $D8, $59, $0C, $28, $25, $9F, $2B, $26, $59, $1C, $1A, $25, $BE, $1D, $42, $73, $29, $1E, $1A, $1C, $1E, $59, $CE, $2D, $2E, $2B, $27, $2C, $59, $DA, $59, $28, $2E, $2B, $59, $B1, $25, $25, $2C, $42, $73, $2D, $21, $93, $24, $2C, $59, $DA, $59, $E3, $3E, $7F
|
||||
Message_1BA:
|
||||
db $00, $21, $42, $59, $E3, $59, $29, $25, $93, $59, $DA, $59, $21, $1E, $1A, $1D, $59, $C7, $DF, $2B, $1D, $42, $75, $32, $1E, $2C, $3F, $59, $E6, $59, $27, $1E, $31, $2D, $59, $2D, $21, $CE, $91, $BB, $28, $26, $2C, $76, $B4, $59, $0A, $1A, $25, $32, $31, $28, $59, $02, $92, $25, $1E, $41, $7E, $73, $08, $51, $2F, $1E, $59, $2D, $1A, $24, $A0, $D8, $59, $25, $22, $97, $2B, $2D, $32, $59, $C6, $73, $BD, $2B, $24, $B3, $D8, $59, $1C, $92, $25, $1E, $59, $C7, $59, $E3, $2B, $59, $BD, $29, $41, $73, $01, $1E, $DF, $CE, $59, $B6, $2C, $59, $2C, $B1, $9F, $30, $2C, $59, $2B, $2E, $27, $59, $1D, $1E, $1E, $29, $41, $7E, $73, $12, $1E, $1E, $24, $59, $D8, $59, $0C, $1E, $1A, $9F, $30, $59, $01, $BA, $1D, $1E, $59, $2D, $21, $A6, $1E, $42, $73, $25, $1E, $20, $A5, $1D, $2C, $59, $2C, $1A, $32, $59, $B6, $59, $21, $28, $25, $1D, $2C, $59, $CB, $A1, $1F, $B6, $73, $A8, $59, $E3, $2B, $59, $2A, $2E, $1E, $D3, $41, $7F
|
||||
Message_1BB:
|
||||
db $00, $21, $28, $32, $42, $59, $26, $94, $1E, $32, $3E, $59, $E8, $51, $CD, $BB, $28, $24, $B4, $51, $75, $A8, $59, $D8, $59, $0F, $22, $2B, $94, $1E, $59, $0A, $B4, $20, $42, $59, $1E, $21, $3F, $76, $16, $1E, $25, $25, $42, $59, $08, $59, $1C, $93, $51, $2D, $59, $21, $1E, $25, $29, $59, $E3, $59, $2D, $21, $A6, $1E, $41, $7E, $73, $01, $2E, $2D, $59, $08, $59, $1C, $93, $59, $2D, $1E, $25, $25, $59, $E3, $59, $2D, $B0, $2C, $59, $D8, $73, $0F, $22, $2B, $94, $1E, $59, $0A, $B3, $B1, $1D, $59, $93, $59, $28, $25, $1D, $59, $2C, $B0, $29, $42, $73, $21, $A6, $1E, $59, $B4, $59, $D8, $59, $1A, $1B, $32, $2C, $2C, $59, $D2, $BE, $E1, $A6, $1E, $41, $7E, $73, $08, $1F, $59, $E3, $51, $CD, $BB, $28, $24, $B4, $51, $59, $DA, $59, $1F, $B4, $1D, $59, $B0, $26, $42, $73, $2D, $21, $94, $8B, $1A, $59, $AC, $28, $1D, $59, $29, $BA, $1C, $1E, $59, $DA, $59, $D3, $1A, $2B, $2D, $41, $7F
|
||||
db $FF ; end of message pointers checks
|
||||
|
||||
|
||||
print "End of expanded dialogue ", pc
|
||||
|
||||
assert pc() <= $2FFFFF
|
||||
1876
Core/messages.org
1876
Core/messages.org
File diff suppressed because it is too large
Load Diff
1212
Core/messages_es.org
1212
Core/messages_es.org
File diff suppressed because it is too large
Load Diff
@@ -1,440 +0,0 @@
|
||||
; ==============================================
|
||||
; 1/4 = $48
|
||||
; 1/4 double = $6C
|
||||
; 1/4 triplet = $30
|
||||
; 1/8 = $24
|
||||
; 1/8 double = $36
|
||||
; 1/8 triplet = $18
|
||||
; 1/16 = $12
|
||||
; 1/16 double = $1B
|
||||
; 1/32 = $09
|
||||
|
||||
; db C4, !Tie, !Tie, !Tie : whole note (1/1)
|
||||
; db C4, !Tie : half note (1/2)
|
||||
|
||||
!4th = $48
|
||||
!4thD = $6C
|
||||
!4thT = $30
|
||||
!8th = $24
|
||||
!8thD = $36
|
||||
!8thT = $18
|
||||
!16th = $12
|
||||
!16thD = $1B
|
||||
!32nd = $09
|
||||
|
||||
; Note Parameters ($01-$7F)
|
||||
; [xy]
|
||||
; When $xy < $80
|
||||
; $x = Duration Rate (0-7)
|
||||
; $y = Velocity Rate (0-15)
|
||||
; VByte itself means the length of the following note
|
||||
; (48 = quarter note, usually).
|
||||
|
||||
macro SetDuration(v)
|
||||
db <v>
|
||||
endmacro
|
||||
|
||||
; n default is $7F
|
||||
macro SetDurationN(v, n)
|
||||
db <v>, <n>
|
||||
endmacro
|
||||
|
||||
; ==============================================
|
||||
; N-SPC Instruments
|
||||
|
||||
; 00 Noise
|
||||
; 01 Rain
|
||||
; 02 Tympani
|
||||
; 03 Square wave
|
||||
; 04 Saw wave
|
||||
; 05 Sine wave (clink)
|
||||
; 06 Wobbly lead
|
||||
; 07 Compound saw wave
|
||||
; 08 Tweet
|
||||
; 09 Strings A
|
||||
; 0A Strings B
|
||||
; 0B Trombone
|
||||
; 0C Cymbal
|
||||
; 0D Ocarina
|
||||
; 0E Chime
|
||||
; 0F Harp
|
||||
; 10 Splash
|
||||
; 11 Trumpet
|
||||
; 12 Horn
|
||||
; 13 Snare A
|
||||
; 14 Snare B
|
||||
; 15 Choir
|
||||
; 16 Flute
|
||||
; 17 Oof
|
||||
; 18 Piano
|
||||
|
||||
macro SetInstrument(v)
|
||||
db $E0, <v>
|
||||
endmacro
|
||||
|
||||
macro Tympani()
|
||||
%SetInstrument($02)
|
||||
endmacro
|
||||
|
||||
macro Trombone()
|
||||
%SetInstrument($0B)
|
||||
endmacro
|
||||
|
||||
macro Ocarina()
|
||||
%SetInstrument($0D)
|
||||
endmacro
|
||||
|
||||
macro Harp()
|
||||
%SetInstrument($0F)
|
||||
endmacro
|
||||
|
||||
macro Splash()
|
||||
%SetInstrument($10)
|
||||
endmacro
|
||||
|
||||
macro Trumpet()
|
||||
%SetInstrument($11)
|
||||
endmacro
|
||||
|
||||
macro Horn()
|
||||
%SetInstrument($12)
|
||||
endmacro
|
||||
|
||||
macro Snare()
|
||||
%SetInstrument($13)
|
||||
endmacro
|
||||
|
||||
macro Choir()
|
||||
%SetInstrument($15)
|
||||
endmacro
|
||||
|
||||
macro Flute()
|
||||
%SetInstrument($16)
|
||||
endmacro
|
||||
|
||||
macro Piano()
|
||||
%SetInstrument($18)
|
||||
endmacro
|
||||
|
||||
macro Cymbal()
|
||||
%SetInstrument($0C)
|
||||
endmacro
|
||||
|
||||
macro Strings()
|
||||
%SetInstrument($09)
|
||||
endmacro
|
||||
|
||||
macro Sawtooth()
|
||||
%SetInstrument($04)
|
||||
endmacro
|
||||
|
||||
macro Sine()
|
||||
%SetInstrument($05)
|
||||
endmacro
|
||||
|
||||
; ==============================================
|
||||
|
||||
macro SetChannelVolume(v)
|
||||
db $ED, <v>
|
||||
endmacro
|
||||
|
||||
macro SetMasterVolume(v)
|
||||
db $E5, <v>
|
||||
endmacro
|
||||
|
||||
macro SetTempo(v)
|
||||
db $E7, <v>
|
||||
endmacro
|
||||
|
||||
; - Play block $yyxx for $zz+1 times.
|
||||
; - Subroutine call cannot be nested.
|
||||
; - See also $00
|
||||
; The block $yyxx is played zz + 1 times. Subroutines cannot be nested.
|
||||
macro CallSubroutine(addr, repeat)
|
||||
db $EF
|
||||
dw <addr>
|
||||
db <repeat>
|
||||
endmacro
|
||||
|
||||
; Set the left and right position of the sound.
|
||||
; The range of values is as narrow as 0 to 20
|
||||
; (the actual setting ratio is defined in the internal table).
|
||||
; Depending on the version, it depends on whether the large value is left or right.
|
||||
; The upper 2 bits are used for phase inversion.
|
||||
; Lower-5bit for pan value (0-20), higher-2bit is used for phase reverse switch.
|
||||
macro SetPan(v)
|
||||
db $E1, <v>
|
||||
endmacro
|
||||
|
||||
; The position of the sound fades from the current value to yy over xx time.
|
||||
macro PanFade(length, dest)
|
||||
db $E2, <length>, <dest>
|
||||
endmacro
|
||||
|
||||
; Enables vibrato (pitch fluctuation / pitch swing).
|
||||
; Set zz-sized vibrato at yy speed after xx time.
|
||||
macro VibratoOn(delay, rate, depth)
|
||||
db $E3, <delay>, <rate>, <depth>
|
||||
endmacro
|
||||
|
||||
macro VibratoOff()
|
||||
db $E4
|
||||
endmacro
|
||||
|
||||
; The volume of the entire song fades from the current value to yy over xx time.
|
||||
macro MasterVolumeFade(length, dest)
|
||||
db $E6, <length>, <dest>
|
||||
endmacro
|
||||
|
||||
; Specifies the playing speed of the song.
|
||||
; Value of about 24/60 is written.
|
||||
macro TempoFade(length, dest)
|
||||
db $E8, <length>, <dest>
|
||||
endmacro
|
||||
|
||||
; Raises the playing pitch of all channels by xx
|
||||
; (negative numbers can be specified).
|
||||
macro GlobalTranspose(tone)
|
||||
db $E9, <tone>
|
||||
endmacro
|
||||
|
||||
; Raises the playing pitch of a single channel by xx
|
||||
; (negative numbers can also be specified).
|
||||
macro ChannelTranspose(tone)
|
||||
db $EA, <tone>
|
||||
endmacro
|
||||
|
||||
; Enable tremolo (volume fluctuation).
|
||||
; Set the tremolo of the size of zz to be applied at the speed of yy after xx time.
|
||||
macro TremoloOn(delay, rate, depth)
|
||||
db $EB, <delay>, <rate>, <depth>
|
||||
endmacro
|
||||
|
||||
macro TremoloOff()
|
||||
db $EC
|
||||
endmacro
|
||||
|
||||
macro ChannelVolumeFade(length, dest)
|
||||
db $EE, <length>, <dest>
|
||||
endmacro
|
||||
|
||||
; After temporarily setting the vibrato depth to 0,
|
||||
; it will smoothly return to the original value over xx time.
|
||||
macro VibratoFade(length)
|
||||
db $F0, <length>
|
||||
endmacro
|
||||
|
||||
; Specifies that subsequent sounds will be
|
||||
; "higher by zz over yy time after xx time."
|
||||
; zz is a semitone unit, and you can specify a negative number.
|
||||
macro PitchEnvelopeTo(delay, length, key)
|
||||
db $F1, <delay>, <length>, <key>
|
||||
endmacro
|
||||
|
||||
; Specifies that subsequent notes will be
|
||||
; "played at a pitch that is zz higher than normal,
|
||||
; and will return to normal pitch over yy time after xx time."
|
||||
; zz is a semitone unit, and you can specify a negative number.
|
||||
macro PitchEnvelopeFrom(delay, length, key)
|
||||
db $F2, <delay>, <length>, <key>
|
||||
endmacro
|
||||
|
||||
macro PitchEnvelopeOff()
|
||||
db $F3
|
||||
endmacro
|
||||
|
||||
; Change the pitch slightly.
|
||||
; Only positive numbers can be specified, so the pitch cannot be lowered.
|
||||
; xx = Unsigned. Make the pitch xx/256 semitones higher.
|
||||
macro Tuning(v)
|
||||
db $F4, <v>
|
||||
endmacro
|
||||
|
||||
; Specifies the channel and volume at which echo is enabled.
|
||||
; The value set in the register remains the same.
|
||||
; xx = Echo Switch (EON)
|
||||
; yy = Echo Left Volume (EVOL (L))
|
||||
; zz = Echo Right Volume (EVOL (R))
|
||||
macro EchoVBits(switch, left, right)
|
||||
db $F5, <switch>, <left>, <right>
|
||||
endmacro
|
||||
|
||||
macro EchoOff()
|
||||
db $F6
|
||||
endmacro
|
||||
|
||||
macro EchoParams(delay, feedback, filter)
|
||||
db $F7, <delay>, <feedback>, <filter>
|
||||
endmacro
|
||||
|
||||
macro EchoVolumeFade(length, left, right)
|
||||
db $F8, <length>, <left>, <right>
|
||||
endmacro
|
||||
|
||||
; This VCMD is handled at different timing than other VCMDs.
|
||||
; utter note $90, (wait), set instrument to $01, utter note $92
|
||||
; $90, $e0 $01, $92
|
||||
; utter note $90 then change the key immediately to note $91, (wait),
|
||||
; utter note $92.
|
||||
; $90, $f9 $00 $01 $91, $92
|
||||
; utter note $90 then change the key immediately to note $91, (wait),
|
||||
; set instrument to $01, utter note $92.
|
||||
; $90, $f9 $00 $01 $91, $e0 01, $92
|
||||
; <weird example> utter note $90, (wait), set instrument to $01,
|
||||
; (pitch slide vcmd appears but note $90 has been end), utter note $92.
|
||||
; $90, $e0 01, $f9 $00 $01 $91, $92
|
||||
|
||||
; Smoothly changes the pitch of the sound being pronounced.
|
||||
; After xx time from play, it will be changed to the zz pitch
|
||||
; (absolute designation) over yy time.
|
||||
; If you want to raise or lower the sound in the middle of one note,
|
||||
; write this command continuously.
|
||||
; If the pronunciation time is long, you can write it with Thai.
|
||||
|
||||
; Normally, after one Note, it waits for the length of the note
|
||||
; and then processes the next byte, but only this command
|
||||
; is read and processed immediately.
|
||||
macro PitchSlide(delay, length, note)
|
||||
db $F9, <delay>, <length>, <note>
|
||||
endmacro
|
||||
|
||||
; Determines the correspondence between the percussion value and the sound you hear.
|
||||
macro PercussionPatchBass(instrument)
|
||||
db $FA, <instrument>
|
||||
endmacro
|
||||
|
||||
; ==============================================
|
||||
|
||||
macro PlayQuarterNotes(...)
|
||||
if sizeof(...) > 0
|
||||
db !4th, <...>
|
||||
else
|
||||
db !4th
|
||||
endif
|
||||
endmacro
|
||||
|
||||
macro PlayEighthNotes(...)
|
||||
if sizeof(...) > 0
|
||||
db !8th, <...>
|
||||
else
|
||||
db !8th
|
||||
endif
|
||||
endmacro
|
||||
|
||||
macro PlayHalfNotes(...)
|
||||
if sizeof(...) > 0
|
||||
db !4th, <...>
|
||||
else
|
||||
db !4th
|
||||
endif
|
||||
; db !4th, <note>, $C8
|
||||
endmacro
|
||||
|
||||
macro SustainNoteN(note, num)
|
||||
db note
|
||||
if num > 1
|
||||
db $C8
|
||||
%SustainNoteN(note, num - 1)
|
||||
endif
|
||||
endmacro
|
||||
|
||||
End = $00
|
||||
Tie = $C8
|
||||
Rest = $C9
|
||||
|
||||
; Percussion Note ($CA-DF)
|
||||
; VByte itself means percussion note (#).
|
||||
; Relations between percussion note and SRCN depends on $FA.
|
||||
; By default, percussion uses the same instrument set as the song,
|
||||
; and all percussion is keyed on with a note of $A4.
|
||||
; The starting ID to use for all channels can be redefined by VCMD $FA.
|
||||
|
||||
|
||||
; =========================================================
|
||||
; Tone Map
|
||||
;
|
||||
; C C+ D D+ E F F+ G G+ A A+ B
|
||||
; Oc1 80 81 82 83 84 85 86 87 88 89 8A 8B
|
||||
; Oc2 8C 8D 8E 8F 90 91 92 93 94 95 96 97
|
||||
; Oc3 98 99 9A 9B 9C 9D 9E 9F A0 A1 A2 A3
|
||||
; Oc4 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
|
||||
; Oc5 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB
|
||||
; Oc6 BC BD BE BF C0 C1 C2 C3 C4 C5 C6 C7
|
||||
|
||||
C1 = $80
|
||||
C1s = $81
|
||||
D1 = $82
|
||||
D1s = $83
|
||||
E1 = $84
|
||||
F1 = $85
|
||||
F1s = $86
|
||||
G1 = $87
|
||||
G1s = $88
|
||||
A1 = $89
|
||||
A1s = $8A
|
||||
B1 = $8B
|
||||
|
||||
C2 = $8C
|
||||
C2s = $8D
|
||||
D2 = $8E
|
||||
D2s = $8F
|
||||
E2 = $90
|
||||
F2 = $91
|
||||
F2s = $92
|
||||
G2 = $93
|
||||
G2s = $94
|
||||
A2 = $95
|
||||
A2s = $96
|
||||
B2 = $97
|
||||
|
||||
C3 = $98
|
||||
C3s = $99
|
||||
D3 = $9A
|
||||
D3s = $9B
|
||||
E3 = $9C
|
||||
F3 = $9D
|
||||
F3s = $9E
|
||||
G3 = $9F
|
||||
G3s = $A0
|
||||
A3 = $A1
|
||||
A3s = $A2
|
||||
B3 = $A3
|
||||
|
||||
C4 = $A4
|
||||
C4s = $A5
|
||||
D4 = $A6
|
||||
D4s = $A7
|
||||
E4 = $A8
|
||||
F4 = $A9
|
||||
F4s = $AA
|
||||
G4 = $AB
|
||||
G4s = $AC
|
||||
A4 = $AD
|
||||
A4s = $AE
|
||||
B4 = $AF
|
||||
|
||||
C5 = $B0
|
||||
C5s = $B1
|
||||
D5 = $B2
|
||||
D5s = $B3
|
||||
E5 = $B4
|
||||
F5 = $B5
|
||||
F5s = $B6
|
||||
G5 = $B7
|
||||
G5s = $B8
|
||||
A5 = $B9
|
||||
A5s = $BA
|
||||
B5 = $BB
|
||||
|
||||
C6 = $BC
|
||||
C6s = $BD
|
||||
D6 = $BE
|
||||
D6s = $BF
|
||||
E6 = $C0
|
||||
F6 = $C1
|
||||
F6s = $C2
|
||||
G6 = $C3
|
||||
G6s = $C4
|
||||
A6 = $C5
|
||||
A6s = $C6
|
||||
B6 = $C7
|
||||
159
Core/patches.asm
159
Core/patches.asm
@@ -1,159 +0,0 @@
|
||||
; This file contains all direct patches to the original ROM.
|
||||
; It is included from Oracle_main.asm.
|
||||
|
||||
; UnderworldTransition_ScrollRoom
|
||||
org $02BE5E : JSL Graphics_Transfer
|
||||
|
||||
; Whirlpool
|
||||
org $1EEEE4 : JSL DontTeleportWithoutFlippers
|
||||
|
||||
; SpriteDraw_Roller
|
||||
org $058EE6 : JSL PutRollerBeneathLink
|
||||
|
||||
; =========================================================
|
||||
|
||||
; Sprite Recoil and Death
|
||||
; TODO: Sprite_AttemptKillingOfKin
|
||||
; Kydreeok Head die like Sidenexx
|
||||
org $06F003 : CMP.b #$CF
|
||||
|
||||
; Remove sidenexx death from booki
|
||||
org $06EFFF : NOP #4
|
||||
|
||||
; Make Dark Link die like sidenexx
|
||||
org $06F003 : CMP.b #$C1
|
||||
|
||||
; Make Helmet ChuChu recoil link
|
||||
org $06F37D : CMP.b #$05
|
||||
|
||||
; Make Kydreeok head recoil Link
|
||||
org $06F381 : CMP.b #$CF
|
||||
|
||||
; =========================================================
|
||||
|
||||
InCutScene = $7EF303
|
||||
|
||||
; Player2JoypadReturn
|
||||
org $0083F8
|
||||
LDA InCutScene : BEQ .notInCutscene
|
||||
STZ $F0
|
||||
STZ $F2
|
||||
STZ $F4
|
||||
STZ $F6
|
||||
STZ $F8
|
||||
STZ $FA ; kill all input
|
||||
.notInCutscene
|
||||
RTS
|
||||
|
||||
assert pc() <= $00841E
|
||||
|
||||
; =========================================================
|
||||
|
||||
org $1EF27D
|
||||
ShopItem_Banana:
|
||||
{
|
||||
JSR $F4CE ; SpriteDraw_ShopItem
|
||||
JSR $FE78 ; Sprite_CheckIfActive_Bank1E
|
||||
JSL $1EF4F3 ; Sprite_BehaveAsBarrier
|
||||
JSR $F391 ; ShopItem_CheckForAPress
|
||||
BCC .exit
|
||||
|
||||
LDA.l Bananas : CMP.b #$0A : BCS .error
|
||||
LDA.b #$1E : LDY.b #$00
|
||||
JSR $F39E ; ShopItem_HandleCost
|
||||
BCC $F1A1 ; ShopItem_GiveFailureMessage
|
||||
|
||||
STZ.w SprState,X
|
||||
INC.b Bananas
|
||||
|
||||
LDY.b #$42 : JSR $F366 ; ShopItem_HandleReceipt
|
||||
|
||||
.exit
|
||||
RTS
|
||||
.error
|
||||
JSR $F38A ; ShopItem_PlayBeep
|
||||
}
|
||||
assert pc() <= $1EF2AB
|
||||
|
||||
; =========================================================
|
||||
|
||||
; Shop item heart OAM
|
||||
; SpriteDraw_ShopItem
|
||||
org $1EF42E
|
||||
dw -4, 16 : db $03, $02, $00, $00 ; 3
|
||||
dw -4, 16 : db $03, $02, $00, $00 ; 3
|
||||
dw 4, 16 : db $30, $02, $00, $00 ; 0
|
||||
dw 0, 0 : db $E5, $03, $00, $02 ; item
|
||||
dw 4, 11 : db $38, $03, $00, $00 ; shadow
|
||||
|
||||
; =========================================================
|
||||
|
||||
; Octoballoon_FormBabby
|
||||
; Reduce by half the number of babies spawned
|
||||
org $06D814 : LDA.b #$02
|
||||
|
||||
; SpritePrep_HauntedGroveOstritch
|
||||
org $068BB2 : NOP #11
|
||||
|
||||
; HauntedGroveRabbit_Idle
|
||||
org $1E9A8F : NOP #5
|
||||
|
||||
; MedallionTablet (Goron)
|
||||
org $05F274 : LDA.l $7EF378 ; Unused SRAM
|
||||
|
||||
org $08C2E3 : dw $006F ; BUTTER SWORD DIALOGUE
|
||||
|
||||
; Fix the capital 'B' debug item cheat.
|
||||
org $0CDC26 : db $80 ; replace a $F0 (BEQ) with a $80 (BRA).
|
||||
|
||||
; Update Catfish Item Get to Bottle
|
||||
org $1DE184 : LDA.b #$16 : STA.w $0D90, X
|
||||
|
||||
; Follower_Disable
|
||||
; Don't disable Kiki so we can switch maps with him.
|
||||
org $09ACF3 : LDA.l $7EF3CC : CMP.b #$0E
|
||||
|
||||
; Kiki, don't care if we're not in dark world
|
||||
org $099FEB : LDA.b $8A : AND.b #$FF
|
||||
|
||||
org $1EE48E : NOP #6
|
||||
|
||||
; Kiki activate cutscene 3 (tail palace)
|
||||
org $1EE630 : LDA.b #$03 : STA.w $04C6
|
||||
|
||||
; Kid at ranch checks for flute
|
||||
org $05FF7D : LDA.l $7EF34C : CMP.b #$01
|
||||
|
||||
; Raven Damage (LW/DW)
|
||||
org $068963 : db $81, $84
|
||||
|
||||
; Running Man draw palette
|
||||
org $05E9CD
|
||||
SpriteDraw_RunningBoy:
|
||||
#_05E9CD: dw 0, -8 : db $2C, $00, $00, $02
|
||||
#_05E9D5: dw 0, 0 : db $EE, $0E, $00, $02
|
||||
|
||||
#_05E9DD: dw 0, -7 : db $2C, $00, $00, $02
|
||||
#_05E9E5: dw 0, 1 : db $EE, $4E, $00, $02
|
||||
|
||||
#_05E9ED: dw 0, -8 : db $2A, $00, $00, $02
|
||||
#_05E9F5: dw 0, 0 : db $CA, $0E, $00, $02
|
||||
|
||||
#_05E9FD: dw 0, -7 : db $2A, $00, $00, $02
|
||||
#_05EA05: dw 0, 1 : db $CA, $4E, $00, $02
|
||||
|
||||
#_05EA0D: dw 0, -8 : db $2E, $00, $00, $02
|
||||
#_05EA15: dw 0, 0 : db $CC, $0E, $00, $02
|
||||
|
||||
#_05EA1D: dw 0, -7 : db $2E, $00, $00, $02
|
||||
#_05EA25: dw 0, 1 : db $CE, $0E, $00, $02
|
||||
|
||||
#_05EA2D: dw 0, -8 : db $2E, $40, $00, $02
|
||||
#_05EA35: dw 0, 0 : db $CC, $4E, $00, $02
|
||||
|
||||
#_05EA3D: dw 0, -7 : db $2E, $40, $00, $02
|
||||
#_05EA45: dw 0, 1 : db $CE, $4E, $00, $02
|
||||
|
||||
; Sword Barrier Sprite Prep
|
||||
; Skip overworld flag check, sprite is indoors now
|
||||
org $06891B : NOP #12
|
||||
8439
Core/ram.asm
8439
Core/ram.asm
File diff suppressed because it is too large
Load Diff
321
Core/sfx.asm
321
Core/sfx.asm
@@ -1,321 +0,0 @@
|
||||
; =========================================================
|
||||
; SFX instruments - Table: ARAM $3E00, ROM $1A:9C04
|
||||
|
||||
; ID VOL L,R Pitch SRCN ADSR Gain Mult Name
|
||||
; -----------------------------------------------------------------------------
|
||||
; $00 $70, $70 $1000 $00 $F6 $6A $B8 $03 Fwoosh
|
||||
; $01 $70, $70 $1000 $01 $8E $E0 $B8 $02 Swish
|
||||
; $02 $70, $70 $1000 $14 $FE $6A $B8 $02 Bomp
|
||||
; $03 $70, $70 $1000 $03 $FE $F8 $B8 $0D Ting
|
||||
; $04 $70, $70 $1000 $04 $FE $6A $7F $03 Rrrrr
|
||||
; $05 $70, $70 $1000 $02 $FE $6A $7F $03 Clunk
|
||||
; $06 $70, $70 $1000 $05 $FE $6A $70 $03 Ching
|
||||
; $07 $70, $70 $1000 $06 $FE $6A $70 $03 Fwomp
|
||||
; $08 $70, $70 $1000 $08 $FA $6A $70 $03 Squee
|
||||
; $09 $70, $70 $1000 $06 $FE $6A $70 $01 Unused
|
||||
; $0A $70, $70 $1000 $07 $FE $6A $70 $05 Bzzzrt
|
||||
; $0B $70, $70 $1000 $0B $FE $6A $B8 $03 Brrfft
|
||||
; $0C $70, $70 $1000 $0C $FE $E0 $B8 $02 Brrwwww
|
||||
; $0D $70, $70 $1000 $0D $F9 $6E $B8 $03 Twee
|
||||
; $0E $70, $70 $1000 $0E $FE $F5 $B8 $07 Pwing
|
||||
; $0F $70, $70 $1000 $0F $FE $F5 $B8 $06 Pling
|
||||
; $10 $70, $70 $1000 $01 $FE $FC $B8 $03 Chshtsh
|
||||
; $11 $70, $70 $1000 $10 $8E $E0 $B8 $03 Splssh
|
||||
; $12 $70, $70 $1000 $08 $8E $E0 $B8 $02 Weewoo
|
||||
; $13 $70, $70 $1000 $14 $8E $E0 $B8 $02 Brbrbrb
|
||||
; $14 $70, $70 $1000 $0A $88 $E0 $B8 $02 Bwow
|
||||
; $15 $70, $70 $1000 $17 $8E $E0 $B8 $02 Uughf
|
||||
; $16 $70, $70 $1000 $15 $FF $E0 $B8 $04 Aaaaaa
|
||||
; $17 $70, $70 $1000 $03 $DF $11 $B8 $0F Twing
|
||||
; $18 $70, $70 $1000 $01 $88 $E0 $B8 $01 Whooo
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
; SFX instruments by usage
|
||||
|
||||
; $00 SFX1.13, SFX1.14
|
||||
; SFX2.07, SFX2.09, SFX2.0D, SFX2.0E, SFX2.2C, SFX2.3A
|
||||
; SFX3.05, SFX3.26
|
||||
; SFXU2533
|
||||
|
||||
; $01 SFX1.01, SFX1.02, SFX1.03, SFX1.04
|
||||
; SFX2.01, SFX2.02, SFX2.12, SFX2.1A, SFX2.1E, SFX2.1F
|
||||
; SFX2.21, SFX2.23, SFX2.29, SFX2.32, SFX2.39
|
||||
; SFX3.02, SFX3.1E, SFX3.23, SFX3.31
|
||||
|
||||
; $02 SFX2.03, SFX2.04, SFX2.08, SFX2.0B, SFX2.12, SFX2.1F, SFX2.21
|
||||
; SFX3.06, SFX3.0E
|
||||
; SFXU2831
|
||||
|
||||
; $03 SFX2.06
|
||||
; SFX3.0A, SFX3.30
|
||||
|
||||
; $04 SFX2.3C
|
||||
; SFX3.32
|
||||
; SFXU2831
|
||||
|
||||
; $05 SFX2.10, SFX2.11, SFX2.22
|
||||
; SFX3.18, SFX3.3E
|
||||
; SFXU252D
|
||||
|
||||
; $06 SFX2.05, SFX2.0A, SFX2.0F, SFX2.3B
|
||||
; SFX3.04, SFX3.14, SFX3.25
|
||||
|
||||
; $07 SFX2.14, SFX2.15, SFX2.33
|
||||
; SFX3.01, SFX3.11, SFX3.12, SFX3.19, SFX3.27, SFX3.28, SFX3.29, SFX3.35, SFX3.39
|
||||
; SFXU26A2
|
||||
|
||||
; $08 SFX3.17
|
||||
|
||||
; $09 nothing
|
||||
|
||||
; $0A SFX1.15, SFX1.16
|
||||
; SFX3.1C, SFX3.2A, SFX3.2B, SFX3.2C
|
||||
|
||||
; $0B SFX2.27
|
||||
; SFX3.0B, SFX3.0F, SFX3.2E, SFX3.34, SFX3.35, SFX3.36, SFX3.3C, SFX3.3D, SFX3.3F
|
||||
|
||||
; $0C SFX2.2A
|
||||
; SFX3.07, SFX3.08, SFX3.09
|
||||
|
||||
; $0D SFX1.0B, SFX1.0C, SFX1.17, SFX1.18, SFX1.1B, SFX1.1C
|
||||
; SFX2.13, SFX2.20, SFX2.31, SFX2.3E, SFX2.3F
|
||||
; SFX3.0C, SFX3.13, SFX3.24
|
||||
; SFXU1EE2, SFXU279D, SFXU27F6, SFXU2807, SFXU2818
|
||||
|
||||
; $0E SFX1.0D, SFX1.0E, SFX1.0F, SFX1.10, SFX1.1D, SFX1.1E, SFX1.1F, SFX1.20
|
||||
; SFX2.2B, SFX2.37
|
||||
; SFX3.0D, SFX3.10, SFX3.1B, SFX3.2F, SFX3.33, SFX3.3A, SFX3.3B
|
||||
|
||||
; $0F SFX2.2D
|
||||
; SFX3.1A, SFX3.1D, SFX3.20, SFX3.2D, SFX3.37
|
||||
; SFXU1D1C
|
||||
|
||||
; $10 SFX2.16, SFX2.17, SFX2.18, SFX2.19
|
||||
|
||||
; $11 SFX2.1B, SFX2.1C, SFX2.24, SFX2.25, SFX2.28, SFX2.2E, SFX2.34, SFX3.28, SFX2.3D
|
||||
|
||||
; $12 SFX3.04
|
||||
|
||||
; $13 SFX1.07, SFX1.08
|
||||
; SFX2.0C, SFX2.35, SFX2.36
|
||||
; SFX3.03, SFX3.15, SFX3.16, SFX3.25, SFX3.38
|
||||
|
||||
; $14 SFX3.21, SFX3.22
|
||||
; SFXU277E
|
||||
|
||||
; $15 SFX2.26, SFX2.30
|
||||
; SFXU1F13
|
||||
|
||||
; $16 SFX1.11, SFX1.12
|
||||
; SFX2.1D
|
||||
; SFX3.1F
|
||||
|
||||
; $17 SFX2.2C, SFX2.3A
|
||||
|
||||
; $18 SFX1.09, SFX1.0A
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; SFX1 - queued via $012D | Table: ARAM $17C0, ROM $1A:8B70
|
||||
|
||||
; ID ARAM ROM Name
|
||||
; -----------------------------------------------------------------------------
|
||||
; SFX1.01 $2652 $1A9A02 Rain / Zora area
|
||||
; SFX1.02 $2662 $1A9A12 Rain / Zora area (packaged with $01)
|
||||
; SFX1.03 $2677 $1A9A27 Rain
|
||||
; SFX1.04 $2687 $1A9A37 Rain (packaged with $03)
|
||||
; SFX1.05 $284F $1A9BFF Silence
|
||||
; SFX1.06 $284F $1A9BFF Silence (packaged with $05)
|
||||
; SFX1.07 $2739 $1A9AE9 The Rumbling
|
||||
; SFX1.08 $2736 $1A9AE6 The Rumbling (packaged with $08)
|
||||
; SFX1.09 $1C8E $1A903E Wind
|
||||
; SFX1.0A $1CBC $1A906C Wind (packaged with $09 by APU)
|
||||
; SFX1.0B $1BA3 $1A8F53 Flute song by flute boy
|
||||
; SFX1.0C $1B62 $1A8F12 Flute song by flute boy (packaged with $0B)
|
||||
; SFX1.0D $1B0E $1A8EBE Magic jingle
|
||||
; SFX1.0E $1B1D $1A8ECD Magic jingle (packaged with $0D)
|
||||
; SFX1.0F $1B2C $1A8EDC Crystal / Save and quit
|
||||
; SFX1.10 $1B3E $1A8EEE Crystal / Save and quit (packaged with $0F)
|
||||
; SFX1.11 $1EAC $1A925C Choir melody
|
||||
; SFX1.12 $1EC8 $1A9278 Choir countermelody (packaged with $11)
|
||||
; SFX1.13 $1AD2 $1A8E82 Large boss swoosh
|
||||
; SFX1.14 $1AE1 $1A8E91 Large boss swoosh (packaged with $13)
|
||||
; SFX1.15 $1AF0 $1A8EA0 Triforce door / Pyramid hole opening
|
||||
; SFX1.16 $1AFF $1A8EAF VOMP (packaged with $15)
|
||||
; SFX1.17 $1C24 $1A8FD4 Flute song for weathervane
|
||||
; SFX1.18 $1BE3 $1A8F93 Flute song for weathervane (packaged with $17)
|
||||
; SFX1.19 $0000 ------- Nothing (unused)
|
||||
; SFX1.1A $0000 ------- Nothing (unused; packaged with $19)
|
||||
; SFX1.1B $1BA3 $1A8F53 Flute song by flute boy duplicate (unused)
|
||||
; SFX1.1C $1B62 $1A8F12 Flute song by flute boy duplicate (unused; packaged with $1B)
|
||||
; SFX1.1D $1B0E $1A8EBE Magic jingle duplicate (unused)
|
||||
; SFX1.1E $1B1D $1A8ECD Magic jingle duplicate (unused; packaged with $1D)
|
||||
; SFX1.1F $1B2C $1A8EDC Crystal / Save and quit duplicate (unused)
|
||||
; SFX1.20 $1B3E $1A8EEE Crystal / Save and quit duplicate (unused; packaged with $1F)
|
||||
|
||||
; $80..$FF Initiates a fade to half volume for SFX1
|
||||
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; SFX2 - queued via $012E | Table: ARAM $1820, ROM $1A:8BD0
|
||||
|
||||
; ID ARAM ROM Name
|
||||
; -----------------------------------------------------------------------------
|
||||
; 00 $0020 ------- Undefined; when queued value of $40, $C0, $80
|
||||
; SFX2.01 $2614 $1A99C4 Slash
|
||||
; SFX2.02 $2625 $1A99D5 Slash
|
||||
; SFX2.03 $2634 $1A99E4 Slash
|
||||
; SFX2.04 $2643 $1A99F3 Slash
|
||||
; SFX2.05 $25DD $1A998D Clink
|
||||
; SFX2.06 $25D7 $1A9987 Bombable door clink
|
||||
; SFX2.07 $25B7 $1A9967 Fwoosh
|
||||
; SFX2.08 $25E3 $1A9993 Arrow smash
|
||||
; SFX2.09 $25AD $1A995D Boomerang fwish
|
||||
; SFX2.0A $25C7 $1A9977 Hookshot clink
|
||||
; SFX2.0B $2478 $1A9828 Placing bomb
|
||||
; SFX2.0C $269C $1A9A4C Explosion
|
||||
; SFX2.0D $2414 $1A97C4 Powder (paired $0D→$3F)
|
||||
; SFX2.0E $2404 $1A97B4 Fire rod shot
|
||||
; SFX2.0F $24C3 $1A9873 Ice rod shot
|
||||
; SFX2.10 $23FA $1A97AA Hammer use
|
||||
; SFX2.11 $23F0 $1A97A0 Hammering peg
|
||||
; SFX2.12 $23CD $1A977D Digging
|
||||
; SFX2.13 $23A0 $1A9750 Flute (paired $13→$3E)
|
||||
; SFX2.14 $2380 $1A9730 Cape on
|
||||
; SFX2.15 $2390 $1A9740 Cape off / Wallmaster grab
|
||||
; SFX2.16 $232C $1A96DC Staircase
|
||||
; SFX2.17 $2344 $1A96F4 Staircase
|
||||
; SFX2.18 $2356 $1A9706 Staircase
|
||||
; SFX2.19 $236E $1A971E Staircase
|
||||
; SFX2.1A $2316 $1A96C6 Tall grass / Hammer hitting bush
|
||||
; SFX2.1B $2307 $1A96B7 Shallow water
|
||||
; SFX2.1C $2301 $1A96B1 Mire shallow water
|
||||
; SFX2.1D $22BB $1A966B Lifting object
|
||||
; SFX2.1E $2577 $1A9927 Cutting grass
|
||||
; SFX2.1F $22E9 $1A9699 Item breaking
|
||||
; SFX2.20 $22DA $1A968A Item falling in pit
|
||||
; SFX2.21 $22CF $1A967F Bomb hitting ground / General thud
|
||||
; SFX2.22 $2107 $1A94B7 Pushing object / Armos bounce
|
||||
; SFX2.23 $22B1 $1A9661 Boots dust
|
||||
; SFX2.24 $22A5 $1A9655 Splashing (paired $24→$3D)
|
||||
; SFX2.25 $2296 $1A9646 Mire shallow water again?
|
||||
; SFX2.26 $2844 $1A9BF4 Link taking damage
|
||||
; SFX2.27 $2252 $1A9602 Fainting
|
||||
; SFX2.28 $2287 $1A9637 Item splash
|
||||
; SFX2.29 $243F $1A97EF Rupee refill (paired $29→$3B)
|
||||
; SFX2.2A $2033 $1A93E3 Fire splash / Bombos spell
|
||||
; SFX2.2B $1FF2 $1A93A2 Heart beep / Text box
|
||||
; SFX2.2C $1FD9 $1A9389 Sword up (paired $2C→$3A) (also uses instrument $17)
|
||||
; SFX2.2D $20A6 $1A9456 Magic drain
|
||||
; SFX2.2E $1FCA $1A937A GT opening (paired $2E→$39)
|
||||
; SFX2.2F $1F47 $1A92F7 GT opening / Water drain (paired $2F→$38)
|
||||
; SFX2.30 $1EF1 $1A92A1 Cucco
|
||||
; SFX2.31 $20CE $1A947E Fairy
|
||||
; SFX2.32 $1D47 $1A90F7 Bug net
|
||||
; SFX2.33 $1CDC $1A908C Teleport (paired $34→$33)
|
||||
; SFX2.34 $1F6F $1A931F Teleport (paired $34→$33)
|
||||
; SFX2.35 $1C67 $1A9017 Shaking
|
||||
; SFX2.36 $1C64 $1A9014 Mire entrance (extends above; paired $35→$36)
|
||||
; SFX2.37 $1A43 $1A8DF3 Spin charged
|
||||
; SFX2.38 $1F6F $1A931F Water sound (paired $2F→$38)
|
||||
; SFX2.39 $1F9C $1A934C Thunder (paired $2E→$39)
|
||||
; SFX2.3A $1FE7 $1A9397 Sword up (paired $2C→$3A)
|
||||
; SFX2.3B $2462 $1A9812 Rupee refill (paired $29→$3B)
|
||||
; SFX2.3C $1A37 $1A8DE7 Error beep
|
||||
; SFX2.3D $22AB $1A965B Big splash (paired $24→$3D)
|
||||
; SFX2.3E $23B5 $1A9765 Flute (paired $13→$3E)
|
||||
; SFX2.3F $2435 $1A97E5 Powder (paired $0D→$3F)
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; SFX3 - queued via $012F | Table: ARAM $191C, ROM $1A:8CCC
|
||||
|
||||
; ID ARAM ROM Name
|
||||
; -----------------------------------------------------------------------------
|
||||
; 00 $003C ------- Undefined; when queued value of $40, $C0, $80
|
||||
; SFX3.01 $1A18 $1A8DC8 Sword beam
|
||||
; SFX3.02 $254E $1A98FE TR opening
|
||||
; SFX3.03 $224A $1A95FA Pyramid hole
|
||||
; SFX3.04 $220E $1A95BE Angry soldier
|
||||
; SFX3.05 $25B7 $1A9967 Lynel shot / Javelin toss
|
||||
; SFX3.06 $21F5 $1A95A5 Swoosh
|
||||
; SFX3.07 $223D $1A95ED Cannon fire
|
||||
; SFX3.08 $21E6 $1A9596 Damage to enemy; $0BEX.4=1
|
||||
; SFX3.09 $21C1 $1A9571 Enemy death
|
||||
; SFX3.0A $21A9 $1A9559 Collecting rupee
|
||||
; SFX3.0B $2198 $1A9548 Collecting heart
|
||||
; SFX3.0C $218E $1A953E Non-blank text character
|
||||
; SFX3.0D $21B5 $1A9565 HUD heart
|
||||
; SFX3.0E $2182 $1A9532 Opening chest
|
||||
; SFX3.0F $24B9 $1A9869 ♪Do do do doooooo♫ (paired $0F→$3C→$3D→$3E→$3F)
|
||||
; SFX3.10 $216D $1A951D Map (paired $10→$3B)
|
||||
; SFX3.11 $214F $1A94FF Opening item menu / Bomb shop guy breathing
|
||||
; SFX3.12 $215E $1A950E Closing item menu / Bomb shop guy breathing
|
||||
; SFX3.13 $213B $1A94EB Throwing object / Stalfos jump
|
||||
; SFX3.14 $246C $1A981C Key door
|
||||
; SFX3.15 $212F $1A94DF Door / Chest (used with SFX2.29)
|
||||
; SFX3.16 $2123 $1A94D3 Armos Knight thud
|
||||
; SFX3.17 $25A6 $1A9956 Rat squeak
|
||||
; SFX3.18 $20DD $1A948D Dragging
|
||||
; SFX3.19 $250A $1A98BA Fireball / Laser shot
|
||||
; SFX3.1A $1E8A $1A923A Chest reveal jingle (paired $1A→$38)
|
||||
; SFX3.1B $20B6 $1A9466 Puzzle jingle (paired $1B→$3A)
|
||||
; SFX3.1C $1A62 $1A8E12 Damage to enemy
|
||||
; SFX3.1D $20A6 $1A9456 Magic meter
|
||||
; SFX3.1E $2091 $1A9441 Wing flapping
|
||||
; SFX3.1F $204B $1A93FB Link falling
|
||||
; SFX3.20 $276C $1A9B1C Menu / Text cursor moved
|
||||
; SFX3.21 $27E2 $1A9B92 Damage to boss
|
||||
; SFX3.22 $26CF $1A9A7F Boss dying / Deleting file
|
||||
; SFX3.23 $2001 $1A93B1 Spin attack swoosh (paired $23→$39)
|
||||
; SFX3.24 $2043 $1A93F3 OW map perspective change
|
||||
; SFX3.25 $1E9D $1A924D Pressure switch (also uses instrument $06)
|
||||
; SFX3.26 $1E7B $1A922B Lightning / Game over / Laser / Ganon bat / Trinexx lunge
|
||||
; SFX3.27 $1E40 $1A91F0 Agahnim charge
|
||||
; SFX3.28 $26F7 $1A9AA7 Agahnim / Ganon teleport
|
||||
; SFX3.29 $1E21 $1A91D1 Agahnim shot
|
||||
; SFX3.2A $1E12 $1A91C2 Somaria / Byrna / Ether spell / Helma fire ball
|
||||
; SFX3.2B $1DF3 $1A91A3 Electrocution
|
||||
; SFX3.2C $1DC0 $1A9170 Bees
|
||||
; SFX3.2D $1DA9 $1A9159 Milestone jingle (paired $2D→$37)
|
||||
; SFX3.2E $1D5D $1A910D Heart container jingle (paired $2E→$35→$34)
|
||||
; SFX3.2F $1D80 $1A9130 Key jingle (paired $2F→$33)
|
||||
; SFX3.30 $1B53 $1A8F03 Magic zap / Plop
|
||||
; SFX3.31 $1ACA $1A8E7A Sprite falling / Moldorm shuffle
|
||||
; SFX3.32 $1A78 $1A8E28 BOING
|
||||
; SFX3.33 $1D93 $1A9143 Key jingle (paired $2F→$33)
|
||||
; SFX3.34 $1D66 $1A9116 Heart container jingle (paired $2E→$35→$34)
|
||||
; SFX3.35 $1D73 $1A9123 Heart container jingle (paired $2E→$35→$34)
|
||||
; SFX3.36 $1AA7 $1A8E57 Magic attack
|
||||
; SFX3.37 $1DB4 $1A9164 Milestone jingle (paired $2D→$37)
|
||||
; SFX3.38 $1E93 $1A9243 Chest reveal jingle (paired $1A→$38)
|
||||
; SFX3.39 $2017 $1A93C7 Swish (paired $23→$39)
|
||||
; SFX3.3A $20C0 $1A9470 Puzzle jingle (paired $1B→$3A)
|
||||
; SFX3.3B $2176 $1A9526 Map (paired $10→$3B)
|
||||
; SFX3.3C $248A $1A983A Item jingle (paired $0F→$3C→$3D→$3E→$3F)
|
||||
; SFX3.3D $2494 $1A9844 Item jingle ($0F→$3C→$3D→$3E→$3F)
|
||||
; SFX3.3E $249E $1A984E Item jingle (paired $0F→$3C→$3D→$3E→$3F)
|
||||
; SFX3.3F $2480 $1A9830 Item jingle (paired $0F→$3C→$3D→$3E→$3F)
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Unused SFX
|
||||
|
||||
; ARAM ROM Description
|
||||
; -----------------------------------------------------------------------------
|
||||
; $1A5B $1A8E0B Noisy fsssh; bleeds into SFX3.1C
|
||||
; $1D1C $1A90CC Radar ping
|
||||
; $1EE2 $1A9292 Slide whistle / Chirp
|
||||
; $1F13 $1A92C3 Cucco clucking
|
||||
; $252D $1A98DD Brighter hammer peg
|
||||
; $2533 $1A98E3 Bat wings flapping
|
||||
; $2657 $1A9A07 Broken static
|
||||
; $267C $1A9A2C Static; Loops
|
||||
; $26A2 $1A9A52 Tuba jingle followed by a roar
|
||||
; $277E $1A9B2E UFO winding up
|
||||
; $279D $1A9B4D Distant whistling
|
||||
; $27C9 $1A9B79 Bwuuuoow
|
||||
; $27F6 $1A9BA6 Cat call
|
||||
; $2807 $1A9BB7 Higher pitched cat call
|
||||
; $2818 $1A9BC8 Reverse cat call
|
||||
; $2829 $1A9BD9 Dial-up
|
||||
; $2831 $1A9BE1 Bumper peg
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,360 +0,0 @@
|
||||
; Write Sprite Properties in the rom MACRO
|
||||
macro Set_Sprite_Properties(SprPrep, SprMain)
|
||||
{
|
||||
pushpc ; Save writing Position for the sprite
|
||||
org $0DB080+!SPRID ; Oam Harmless ($0E40)
|
||||
db ((!Harmless<<7)|(!HVelocity<<6)|!NbrTiles)
|
||||
|
||||
org $0DB173+!SPRID ; Sprite HP ($0E50)
|
||||
db !Health
|
||||
|
||||
org $0DB266+!SPRID ; Sprite Damage ($0CD2)
|
||||
db !Damage
|
||||
|
||||
org $0DB359+!SPRID ; Sprite Data ($0E60 / $0F50)
|
||||
db ((!DeathAnimation<<7)|(!ImperviousAll<<6)|(!SmallShadow<<5)|(!Shadow<<4)|(!Palette<<1))
|
||||
|
||||
org $0DB44C+!SPRID ; Sprite Hitbox ($0F60)
|
||||
db ((!CollisionLayer<<7)|(!Statis<<6)|(!Persist<<5)|(!Hitbox))
|
||||
|
||||
org $0DB53F+!SPRID ; Sprite Fall ($0B6B)
|
||||
db ((!DeflectArrow<<3)|(!Boss<<1)|!CanFall)
|
||||
|
||||
org $0DB632+!SPRID ; Sprite Prize ($0BE0)
|
||||
db ((!Interaction<<7)|(!WaterSprite<<6)|(!Blockable<<5)|(!Sound<<4)|!Prize)
|
||||
|
||||
org $0DB725+!SPRID ; Sprite ($0CAA)
|
||||
db ($40|(!Statue<<5)|(!DeflectProjectiles<<4)|(!ImpervSwordHammer<<2)|(!ImperviousArrow<<1))
|
||||
|
||||
org $069283+(!SPRID*2) ; Vanilla Sprite Main Pointer
|
||||
dw NewMainSprFunction
|
||||
|
||||
org $06865B+(!SPRID*2) ; Vanilla Sprite Prep Pointer
|
||||
dw NewSprPrepFunction
|
||||
|
||||
org NewSprRoutinesLong+(!SPRID*3) ; New Long Sprite Pointer
|
||||
dl <SprMain>
|
||||
|
||||
org NewSprPrepRoutinesLong+(!SPRID*3) ; New Long Sprite Pointer
|
||||
dl <SprPrep>
|
||||
pullpc ; Get back the writing position for the sprite
|
||||
}
|
||||
endmacro
|
||||
|
||||
macro sta(...)
|
||||
!a #= 0
|
||||
while !a < sizeof(...)
|
||||
STA <...>
|
||||
!a #= !a+1
|
||||
endwhile
|
||||
endmacro
|
||||
|
||||
macro m16()
|
||||
REP #$30
|
||||
endmacro
|
||||
|
||||
macro m8()
|
||||
SEP #$30
|
||||
endmacro
|
||||
|
||||
macro a16()
|
||||
REP #$20
|
||||
endmacro
|
||||
|
||||
macro a8()
|
||||
SEP #$20
|
||||
endmacro
|
||||
|
||||
macro index16()
|
||||
REP #$10
|
||||
endmacro
|
||||
|
||||
macro index8()
|
||||
SEP #$10
|
||||
endmacro
|
||||
|
||||
macro GotoAction(action)
|
||||
LDA.b #<action> : STA.w SprAction, X
|
||||
endmacro
|
||||
|
||||
macro SetFrame(frame)
|
||||
LDA.b #<frame> : STA.w SprFrame, X
|
||||
endmacro
|
||||
|
||||
macro JumpTable(index, ...)
|
||||
LDA.w <index>
|
||||
JSL JumpTableLocal
|
||||
|
||||
!a #= 0
|
||||
while !a < sizeof(...)
|
||||
dw <...[!a]>
|
||||
!a #= !a+1
|
||||
endwhile
|
||||
endmacro
|
||||
|
||||
macro SpriteJumpTable(...)
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
!a #= 0
|
||||
while !a < sizeof(...)
|
||||
dw <...[!a]>
|
||||
!a #= !a+1
|
||||
endwhile
|
||||
endmacro
|
||||
|
||||
macro SetFlag(flag_addr, bit_pos)
|
||||
LDA.b flag_addr : ORA.b #(1 << bit_pos) : STA.b flag_addr
|
||||
endmacro
|
||||
|
||||
macro ClearFlag(flag_addr, bit_pos)
|
||||
LDA.b flag_addr : AND.b #~(1 << bit_pos) : STA.b flag_addr
|
||||
endmacro
|
||||
|
||||
macro ToggleFlag(flag_addr, bit_pos)
|
||||
LDA.b flag_addr : EOR.b #(1 << bit_pos) : STA.b flag_addr
|
||||
endmacro
|
||||
|
||||
macro CheckFlag(flag_addr, bit_pos, set_label, clear_label)
|
||||
LDA.b flag_addr : AND.b #(1 << bit_pos) : BEQ clear_label
|
||||
BRA set_label
|
||||
endmacro
|
||||
|
||||
macro CheckFlagLong(flag_addr, bit_pos, set_label, clear_label)
|
||||
LDA.l flag_addr : AND.b #(1 << bit_pos) : BEQ clear_label
|
||||
BRA set_label
|
||||
endmacro
|
||||
|
||||
; Increase the sprite frame every (frames_wait) frames
|
||||
; reset to (frame_start) when reaching (frame_end)
|
||||
; This is using SprTimerB
|
||||
macro PlayAnimation(frame_start, frame_end, frame_wait)
|
||||
{
|
||||
LDA.w SprTimerB, X : BNE +
|
||||
LDA.w SprFrame, X : INC : STA.w SprFrame, X
|
||||
CMP.b #<frame_end>+1 : BCC ++
|
||||
LDA.b #<frame_start> : STA.w SprFrame, X
|
||||
++
|
||||
LDA.b #<frame_wait> : STA.w SprTimerB, X
|
||||
+
|
||||
}
|
||||
endmacro
|
||||
|
||||
macro PlayAnimBackwards(frame_start, frame_end, frame_wait)
|
||||
LDA.w SprTimerB, X : BNE +
|
||||
LDA.w SprFrame, X : DEC : STA.w SprFrame, X
|
||||
CMP.b #<frame_end> : BCS ++
|
||||
LDA.b #<frame_start> : STA.w SprFrame, X
|
||||
++
|
||||
LDA.b #<frame_wait> : STA.w SprTimerB, X
|
||||
+
|
||||
endmacro
|
||||
|
||||
macro StartOnFrame(frame)
|
||||
LDA.w SprFrame, x : CMP.b #<frame> : BCS +
|
||||
LDA.b #<frame> : STA.w SprFrame, x
|
||||
+
|
||||
endmacro
|
||||
|
||||
; Show message if the player is facing toward sprite and pressing A
|
||||
; Return Carry Set if message is displayed
|
||||
; can use BCC .label <> .label to see if message have been displayed
|
||||
macro ShowSolicitedMessage(message_id)
|
||||
LDY.b #(<message_id>)>>8
|
||||
LDA.b #<message_id>
|
||||
JSL Sprite_ShowSolicitedMessageIfPlayerFacing
|
||||
endmacro
|
||||
|
||||
macro ShowMessageOnContact(message_id)
|
||||
LDY.b #(<message_id>)>>8
|
||||
LDA.b #<message_id>
|
||||
JSL $05E1F0 ; Sprite_ShowMessageOnContact
|
||||
endmacro
|
||||
|
||||
; Show message no matter what (should not be used without code condition)
|
||||
macro ShowUnconditionalMessage(message_id)
|
||||
LDY.b #(<message_id>)>>8
|
||||
LDA.b #<message_id>
|
||||
JSL Sprite_ShowMessageUnconditional
|
||||
endmacro
|
||||
|
||||
; Make the sprite move towards the player at a speed of (speed)
|
||||
macro MoveTowardPlayer(speed)
|
||||
LDA.b #<speed>
|
||||
JSL Sprite_ApplySpeedTowardsPlayer
|
||||
JSL Sprite_MoveLong
|
||||
endmacro
|
||||
|
||||
; Prevent the player from passing through sprite hitbox
|
||||
macro PlayerCantPassThrough()
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
endmacro
|
||||
|
||||
; Do damage to player on contact if sprite is on same layer as player
|
||||
macro DoDamageToPlayerSameLayerOnContact()
|
||||
JSL Sprite_CheckDamageToPlayerSameLayer
|
||||
endmacro
|
||||
|
||||
; Set harmless flag, 0 = harmful, 1 = harmless
|
||||
macro SetHarmless(value)
|
||||
LDA.w SprNbrOAM, X
|
||||
AND #$7F
|
||||
if <value> != 0
|
||||
ORA.b #(<value>)<<7
|
||||
endif
|
||||
STA.w SprNbrOAM, X
|
||||
endmacro
|
||||
|
||||
macro SetImpervious(value)
|
||||
LDA.w SprDefl, X
|
||||
EOR.b #(<value>)<<2
|
||||
STA.w SprDefl, X
|
||||
endmacro
|
||||
|
||||
; Set Room Flag (Chest 6)
|
||||
; Do not use if you have more than 5 chests or a small key under a pot
|
||||
; in that room unless you want it to be already opened/taken
|
||||
macro SetRoomFlag(value)
|
||||
if <value> != 0
|
||||
LDA $0403 : ORA #$20 : STA $0403
|
||||
else
|
||||
LDA $0403 : AND #$DF : STA $0403
|
||||
endif
|
||||
endmacro
|
||||
|
||||
; Will prevent the player from moving or opening his menu
|
||||
macro PreventPlayerMovement()
|
||||
LDA #$01 : STA $02E4
|
||||
endmacro
|
||||
|
||||
; Will allow the player to move or open his menu
|
||||
macro AllowPlayerMovement()
|
||||
STZ.w $02E4
|
||||
endmacro
|
||||
|
||||
; This is a 16 bit will load A with current rupee count
|
||||
; to use with instructions CMP and BCC/BCS
|
||||
macro GetPlayerRupees()
|
||||
LDA $7EF360
|
||||
endmacro
|
||||
|
||||
; Set the velocity Y of the sprite at (speed) value
|
||||
; this require the use of the function JSL Sprite_MoveLong
|
||||
macro SetSpriteSpeedY(speed)
|
||||
LDA.b #<speed> : STA.w SprYSpeed, x
|
||||
endmacro
|
||||
|
||||
; Set the velocity X of the sprite at (speed) value
|
||||
; this require the use of the function JSL Sprite_MoveLong
|
||||
macro SetSpriteSpeedX(speed)
|
||||
LDA.b #<speed> : STA.w SprXSpeed, x
|
||||
endmacro
|
||||
|
||||
macro PlaySFX1(sfxid)
|
||||
LDA.b #<sfxid> : STA $012E
|
||||
endmacro
|
||||
|
||||
macro PlaySFX2(sfxid)
|
||||
LDA.b #<sfxid> : STA $012F
|
||||
endmacro
|
||||
|
||||
macro PlayMusic(musicid)
|
||||
LDA.b #<musicid> : STA $012C
|
||||
endmacro
|
||||
|
||||
macro SetTimerA(length)
|
||||
LDA.b #<length> : STA.w SprTimerA, X
|
||||
endmacro
|
||||
|
||||
macro SetTimerB(length)
|
||||
LDA.b #<length> : STA.w SprTimerB, X
|
||||
endmacro
|
||||
|
||||
macro SetTimerC(length)
|
||||
LDA.b #<length> : STA.w SprTimerC, X
|
||||
endmacro
|
||||
|
||||
macro SetTimerD(length)
|
||||
LDA.b #<length> : STA.w SprTimerD, X
|
||||
endmacro
|
||||
|
||||
macro SetTimerE(length)
|
||||
LDA.b #<length> : STA.w SprTimerE, X
|
||||
endmacro
|
||||
|
||||
macro SetTimerF(length)
|
||||
LDA.b #<length> : STA.w SprTimerF, X
|
||||
endmacro
|
||||
|
||||
macro ErrorBeep()
|
||||
LDA.b #$3C : STA.w $012E ; Error beep
|
||||
endmacro
|
||||
|
||||
macro NextAction()
|
||||
INC $0D80, X
|
||||
endmacro
|
||||
|
||||
macro GetTilePos(x, y)
|
||||
LDX.w #((<y>*$80)+(<x>*$02))
|
||||
endmacro
|
||||
|
||||
macro SetupDistanceFromSprite()
|
||||
LDA.w POSX : STA $02
|
||||
LDA.w POSY : STA $03
|
||||
LDA.w SprX, X : STA $04
|
||||
LDA.w SprY, X : STA $05
|
||||
endmacro
|
||||
|
||||
macro ProbCheck(mask, label)
|
||||
JSL GetRandomInt
|
||||
AND.b #<mask>
|
||||
BNE <label>
|
||||
endmacro
|
||||
|
||||
macro ProbCheck2(mask, label)
|
||||
JSL GetRandomInt
|
||||
AND.b #<mask>
|
||||
BEQ <label>
|
||||
endmacro
|
||||
|
||||
macro DrawSprite()
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
LDA.w SprGfx, X : CLC : ADC.w SprFrame, X : TAY
|
||||
LDA.w .start_index, Y : STA $06
|
||||
LDA.w SprFlash, X : STA $08
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ; amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
PHA ; Keep the value with animation index offset?
|
||||
ASL A : TAX
|
||||
REP #$20
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
; Put the sprite out of the way
|
||||
LDA.b #$F0 : STA ($90), Y : STA $0E
|
||||
.on_screen_y
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : ORA $08 : STA ($90), Y
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
PLX
|
||||
RTS
|
||||
}
|
||||
endmacro
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
pushpc
|
||||
org $06FFF8 ; New Jumptable for sprites
|
||||
NewMainSprFunction:
|
||||
JSL SpriteActiveExp_MainLong
|
||||
RTS
|
||||
|
||||
org $068EB9
|
||||
NewSprPrepFunction:
|
||||
JSL Sprite_PrepExp_Long
|
||||
RTS
|
||||
pullpc
|
||||
|
||||
|
||||
SpriteActiveExp_MainLong:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSL NewSprTable
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
|
||||
NewSprTable:
|
||||
{
|
||||
LDA $0E20, X ; Load Sprite ID
|
||||
REP #$30
|
||||
AND.w #$00FF
|
||||
STA $06
|
||||
ASL A ; *2
|
||||
CLC : ADC $06 ; *3
|
||||
TAY
|
||||
|
||||
LDA NewSprRoutinesLong, Y ; Load sprite Address
|
||||
STA $06
|
||||
SEP #$20
|
||||
LDA NewSprRoutinesLong+2, Y
|
||||
STA $08
|
||||
SEP #$30
|
||||
JMP [$0006]
|
||||
|
||||
;do a JML and sprite will RTL back to previous code
|
||||
}
|
||||
|
||||
Sprite_PrepExp_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSL NewSprPrepTable
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
|
||||
NewSprPrepTable:
|
||||
{
|
||||
LDA $0E20, X ; Load Sprite ID
|
||||
REP #$30
|
||||
AND.w #$00FF
|
||||
STA $06
|
||||
ASL A ; *2
|
||||
CLC : ADC $06 ; *3
|
||||
TAY
|
||||
|
||||
LDA NewSprPrepRoutinesLong, Y ; Load sprite Address
|
||||
STA $06
|
||||
SEP #$20 ; Previously SEP #$30 -_- (that's fine for sprites below ~0x40 over that it will crash)
|
||||
LDA NewSprPrepRoutinesLong+2, Y
|
||||
STA $08
|
||||
SEP #$30
|
||||
JMP [$0006]
|
||||
}
|
||||
|
||||
NewSprRoutinesLong:
|
||||
{
|
||||
fillbyte $00
|
||||
fill $2FD
|
||||
}
|
||||
|
||||
NewSprPrepRoutinesLong:
|
||||
{
|
||||
fillbyte $00
|
||||
fill $2FD
|
||||
}
|
||||
545
Core/sram.asm
545
Core/sram.asm
@@ -1,545 +0,0 @@
|
||||
|
||||
; Game state
|
||||
; 0x00 - Very start; progress cannot be saved in this state
|
||||
; 0x01 - Uncle reached
|
||||
; 0x02 - Farore intro over | Zelda rescued
|
||||
; 0x03 - Agahnim defeated
|
||||
GameState = $7EF3C5
|
||||
|
||||
; Red X on Hall of Secrets
|
||||
; Red X on Kalyxo Pyramid
|
||||
|
||||
; .fmp h.i.
|
||||
; f - fortress of secrets
|
||||
; m - master sword
|
||||
; p - pendant quest
|
||||
; h - hall of secrets
|
||||
; i - intro over, maku tree
|
||||
OOSPROG = $7EF3D6
|
||||
|
||||
; Bitfield of less important progression
|
||||
; .fbh .zsu
|
||||
; u - Uncle
|
||||
; s - Priest visited in sanc after Zelda is kidnapped again
|
||||
; z - Zelda brought to sanc
|
||||
; h - Uncle left Link's house (0: spawn | 1: gone)
|
||||
; b - Book of Mudora obtained/mentioned; controls Aginah dialog
|
||||
; f - Flipped by fortune tellers to decide fortune set to give
|
||||
OOSPROG2 = $7EF3C6
|
||||
|
||||
; .... ...m
|
||||
; m - maku tree has met link (0: no | 1: yes)
|
||||
MakuTreeQuest = $7EF3D4
|
||||
|
||||
; Map icon
|
||||
; 0x00 - Red X on Maku Tree/Maku Warp
|
||||
; 0x01 - Toadstool Woods Crystal
|
||||
; 0x02 - Kalyxo All Crystals
|
||||
; 0x03 -
|
||||
; 0x04 -
|
||||
; 0x05 -
|
||||
; 0x06 -
|
||||
; 0x07 -
|
||||
; 0x08 - Skull on GT | Climb Ganon's Tower
|
||||
MapIcon = $7EF3C7
|
||||
|
||||
; 01 - Fishing Rod
|
||||
; 02 - Portal Rod
|
||||
CustomRods = $7EF351
|
||||
|
||||
; Free SRAM Block 38A-3C4
|
||||
FishingRod = $7EF38A
|
||||
|
||||
; Collectibles
|
||||
Bananas = $7EF38B
|
||||
Pineapples = $7EF38D
|
||||
RockMeat = $7EF38F
|
||||
Seashells = $7EF391
|
||||
Honeycomb = $7EF393
|
||||
DekuSticks = $7EF395
|
||||
|
||||
TingleMaps = $7EF396
|
||||
TingleId = $7EF397
|
||||
|
||||
; .dgi zktm
|
||||
; m - Mushroom Grotto
|
||||
; t - Tail Palace
|
||||
; k - Kalyxo Castle
|
||||
; z - Zora Temple
|
||||
; i - Glacia Estate
|
||||
; g - Goron Mines
|
||||
; d - Dragon Ship
|
||||
Scrolls = $7EF398
|
||||
|
||||
; Keep track of the previous scroll
|
||||
; For re-reading old hints.
|
||||
PrevScroll = $7EF39A
|
||||
|
||||
; .dts fwpb
|
||||
; b - bean planted
|
||||
; w - plant watered
|
||||
; p - pollinated by bee
|
||||
; f - first day
|
||||
; s - second day
|
||||
; t - third day
|
||||
; d - done
|
||||
MagicBeanProg = $7EF39B
|
||||
|
||||
JournalState = $7EF39C
|
||||
|
||||
; State machine for Link's House intro sequence (custom_tag.asm)
|
||||
; 0x00 - Telepathic Plea phase
|
||||
; 0x01 - Wake Up Player phase
|
||||
; 0x02 - End (intro complete)
|
||||
StoryState = $7EF39E
|
||||
|
||||
; 7EF403 - 7EF4FD Unused block
|
||||
|
||||
; .... .cpw
|
||||
; c - courage
|
||||
; p - power
|
||||
; w - wisdom
|
||||
Dreams = $7EF410
|
||||
|
||||
; =========================================================
|
||||
; Items
|
||||
; =========================================================
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Bow
|
||||
; 0x02 - Bow and arrows
|
||||
; 0x03 - Silver bow
|
||||
; 0x04 - Silver bow and arrows
|
||||
; Picking the arrow and nonarrow versions is done by the HUD draw routines
|
||||
Bow = $7EF340
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Blue boomerang
|
||||
; 0x02 - Red boomerang
|
||||
Boomerang = $7EF341
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Hookshot
|
||||
; 0x02 - Goldstar (L/R)
|
||||
Hookshot = $7EF342
|
||||
|
||||
; Number of bombs
|
||||
Bombs = $7EF343
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Mushroom
|
||||
; 0x02 - Powder
|
||||
MagicPowder = $7EF344
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Fire rod
|
||||
FireRod = $7EF345
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Ice rod
|
||||
IceRod = $7EF346
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Zora Mask
|
||||
ZoraMask = $7EF347
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Bunny Hood
|
||||
BunnyHood = $7EF348
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Deku Mask
|
||||
DekuMask = $7EF349
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Lamp
|
||||
Lamp = $7EF34A
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Magic hammer
|
||||
Hammer = $7EF34B
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Shovel
|
||||
; 0x02 - Inactive flute
|
||||
; 0x03 - Active flute
|
||||
Flute = $7EF34C
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Roc's Feather
|
||||
RocsFeather = $7EF34D
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Book of Mudora
|
||||
Book = $7EF34E
|
||||
|
||||
; 0x00 - Nothing
|
||||
; Other values indicate the index of the currently selected bottle
|
||||
BottleIndex = $7EF34F
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Cane of Somaria
|
||||
Somaria = $7EF350
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Cane of Byrna
|
||||
Byrna = $7EF351
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Stone Mask
|
||||
StoneMask = $7EF352
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Letter (works like mirror)
|
||||
; 0x02 - Mirror
|
||||
; 0x03 - Deleted triforce item
|
||||
Mirror = $7EF353
|
||||
|
||||
; 0x00 - Lift 1 (nothing)
|
||||
; 0x01 - Lift 2 (power glove)
|
||||
; 0x02 - Lift 3 (titan's mitt)
|
||||
Gloves = $7EF354
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Pegasus boots
|
||||
; bit 2 of $7E:F379 also needs to be set to actually dash
|
||||
Boots = $7EF355
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Zora's flippers
|
||||
Flippers = $7EF356
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Moon pearl
|
||||
Pearl = $7EF357
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Wolf Mask
|
||||
WolfMask = $7EF358
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Fighter sword
|
||||
; 0x02 - Master sword
|
||||
; 0x03 - Tempered sword
|
||||
; 0x04 - Golden sword
|
||||
; 0xFF - Set when sword is handed in to smithy
|
||||
Sword = $7EF359
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Fighter shield
|
||||
; 0x02 - Fire shield
|
||||
; 0x03 - Mirror shield
|
||||
Shield = $7EF35A
|
||||
|
||||
; 0x00 - Green mail
|
||||
; 0x01 - Blue mail
|
||||
; 0x02 - Red mail
|
||||
Armor = $7EF35B
|
||||
|
||||
; 0x00 - Nothing
|
||||
; 0x01 - Mushroom (unused)
|
||||
; 0x02 - Empty bottle
|
||||
; 0x03 - Red potion
|
||||
; 0x04 - Green potion
|
||||
; 0x05 - Blue potion
|
||||
; 0x06 - Fairy
|
||||
; 0x07 - Bee
|
||||
; 0x08 - Good bee
|
||||
; 0x09 - Magic Bean
|
||||
; 0x0A - Milk Bottle
|
||||
Bottle1 = $7EF35C
|
||||
Bottle2 = $7EF35D
|
||||
Bottle3 = $7EF35E
|
||||
Bottle4 = $7EF35F
|
||||
|
||||
; Number of rupees you have
|
||||
; RUPEEDISP will be incremented or decremented until it reaches this value
|
||||
Rupees = $7EF360
|
||||
RupeesGoal = $7EF361
|
||||
|
||||
; Rupee count displayed on the HUD
|
||||
RUPEEDISP = $7EF362
|
||||
|
||||
; Bitfields for ownership of various dungeon items
|
||||
; SET 2 SET 1
|
||||
; xced aspm wihb tg..
|
||||
; c - Hyrule Castle
|
||||
; x - Sewers
|
||||
; a - Agahnim's Tower
|
||||
;
|
||||
; e - Eastern Palace
|
||||
; d - Desert Palace
|
||||
; h - Tower of Hera
|
||||
;
|
||||
; p - Palace of Darkness
|
||||
; s - Swamp Palace
|
||||
; w - Skull Woods
|
||||
; b - Thieves' Town
|
||||
; i - Ice Palace
|
||||
; m - Misery Mire
|
||||
; t - Turtle Rock
|
||||
; g - Ganon's Tower
|
||||
COMPASS1 = $7EF364
|
||||
COMPASS2 = $7EF365
|
||||
|
||||
BIGKEY1 = $7EF366
|
||||
BIGKEY2 = $7EF367
|
||||
|
||||
DNGMAP1 = $7EF368
|
||||
DNGMAP2 = $7EF369
|
||||
|
||||
; Number of rupees donated to fairies
|
||||
WISHRUP = $7EF36A
|
||||
|
||||
; Number of heart pieces towards next container
|
||||
; Intended to be a value from 0-3
|
||||
HEARTPC = $7EF36B
|
||||
|
||||
; Maximum health; 1 heart container = 0x08 HP
|
||||
MAXHP = $7EF36C
|
||||
|
||||
; Current health
|
||||
; You die at 0x00
|
||||
; You also die at ≥0xA8
|
||||
CURHP = $7EF36D
|
||||
|
||||
; Magic power, capped at 128
|
||||
MagicPower = $7EF36E
|
||||
|
||||
; Current number of keys for whatever dungeon is loaded
|
||||
KEYS = $7EF36F
|
||||
|
||||
; Number of capacity upgrades received
|
||||
BOMBCAP = $7EF370
|
||||
ARROWCAP = $7EF371
|
||||
|
||||
; Refills health
|
||||
; Expects multiples of 8
|
||||
HeartRefill = $7EF372
|
||||
|
||||
; Refills magic
|
||||
ZAPME = $7EF373
|
||||
|
||||
; ... ..gbr
|
||||
; r - Wisdom (red)
|
||||
; b - Power (blue)
|
||||
; g - Courage (green)
|
||||
Pendants = $7EF374
|
||||
|
||||
; Refills bombs
|
||||
BOMBME = $7EF375
|
||||
|
||||
; Refills arrows
|
||||
SHOOTME = $7EF376
|
||||
|
||||
; Arrow count
|
||||
Arrows = $7EF377
|
||||
|
||||
; Unused
|
||||
UNUSED_7EF378 = $7EF378
|
||||
|
||||
; Displays ability flags
|
||||
; lrtu pbsh
|
||||
; h - Pray (unused and mostly cut off by HUD borders)
|
||||
; s - Swim
|
||||
; b - Run
|
||||
; u - unused but set by default
|
||||
; p - Pull
|
||||
; t - Talk
|
||||
; r - Read
|
||||
; l - Lift
|
||||
; This only controls the display of "LIFT.1"
|
||||
; If this bit is unset but LIFT is set then the proper lift text is displayed
|
||||
Ability = $7EF379
|
||||
|
||||
; Dungeon ID Legend
|
||||
; Mushroom Grotto ID 0x0C (Palace of Darkness)
|
||||
; Tail Palace ID 0x0A (Swamp Palace)
|
||||
; Kalyxo Castle ID 0x10 (Skull Woods)
|
||||
; Zora Temple ID 0x16 (Thieves Town)
|
||||
; Glacia Estate 0x12 (Ice Palace)
|
||||
; Goron Mines 0x0E (Misery Mire)
|
||||
; Dragon Ship 0x18 (Turtle Rock)
|
||||
|
||||
; .wbs tipm
|
||||
; p - Palace of Darkness
|
||||
; s - Swamp Palace
|
||||
; w - Skull Woods
|
||||
; b - Thieves' Town
|
||||
; i - Ice Palace
|
||||
; m - Misery Mire
|
||||
; t - Turtle Rock
|
||||
Crystals = $7EF37A
|
||||
|
||||
; 0x00 - Normal magic
|
||||
; 0x01 - Half magic
|
||||
; 0x02 - Quarter magic
|
||||
; Quarter magic has no special HUD graphic, unlike half magic
|
||||
; Also, not everything is necessarily quarter magic
|
||||
MagicUsage = $7EF37B
|
||||
|
||||
; Keys earned per dungeon
|
||||
; Sewers and Castle are kept in sync
|
||||
KEYSSEWER = $7EF37C
|
||||
KEYSHYRULE = $7EF37D
|
||||
KEYSEAST = $7EF37E
|
||||
KEYSDESERT = $7EF37F
|
||||
KEYSAGA = $7EF380
|
||||
KEYSSWAMP = $7EF381
|
||||
KEYSPOD = $7EF382
|
||||
KEYSMIRE = $7EF383
|
||||
KEYSWOODS = $7EF384
|
||||
KEYSICE = $7EF385
|
||||
KEYSHERA = $7EF386
|
||||
KEYSTHIEF = $7EF387
|
||||
KEYSTROCK = $7EF388
|
||||
KEYSGANON = $7EF389
|
||||
|
||||
; Unused block of SRAM
|
||||
UNUSED_7EF38A = $7EF38A
|
||||
|
||||
; Game state
|
||||
; 0x00 - Very start; progress cannot be saved in this state
|
||||
; 0x01 - Uncle reached
|
||||
; 0x02 - Zelda rescued
|
||||
; 0x03 - Agahnim defeated
|
||||
GAMESTATE = $7EF3C5
|
||||
|
||||
; Bitfield of less important progression
|
||||
; .fbh .zsu
|
||||
; u - Uncle visited in secret passage; controls spawn (0: spawn | 1: gone)
|
||||
; s - Priest visited in sanc after Zelda is kidnapped again
|
||||
; z - Zelda brought to sanc
|
||||
; h - Uncle has left Link's house; controls spawn (0: spawn | 1: gone)
|
||||
; b - Book of Mudora obtained/mentioned; controls Aginah dialog
|
||||
; f - Flipped by fortune tellers to decide which fortune set to give
|
||||
PROGLITE = $7EF3C6
|
||||
|
||||
; Map icon to guide noob players
|
||||
; 0x00 - Red X on castle | Save zelda
|
||||
; 0x01 - Red X on Kakariko | Talk to villagers about elders
|
||||
; 0x02 - Red X on Eastern | Talk to Sahasrahla
|
||||
; 0x03 - Pendants and MS | Obtain the master sword
|
||||
; 0x04 - Master sword on LW | Grab the master sword
|
||||
; 0x05 - Skull on castle | Kill Agahnim
|
||||
; 0x06 - Crystal on POD | Get the first crystal
|
||||
; 0x07 - Crystals | Get all 7 crystals
|
||||
; 0x08 - Skull on GT | Climb Ganon's Tower
|
||||
MAPICON = $7EF3C7
|
||||
|
||||
; 0x00 - Link's house
|
||||
; 0x01 - Sanctuary
|
||||
; 0x02 - Prison
|
||||
; 0x03 - Uncle
|
||||
; 0x04 - Throne
|
||||
; 0x05 - Old man cave
|
||||
; 0x06 - Old man home
|
||||
SpawnPoint = $7EF3C8
|
||||
|
||||
; Another bitfield for progress
|
||||
; t.dp s.bh
|
||||
; t - smiths are currently tempering sword
|
||||
; d - swordsmith rescued
|
||||
; p - purple chest has been opened
|
||||
; s - stumpy has been stumped
|
||||
; b - bottle purchased from vendor
|
||||
; h - bottle received from hobo
|
||||
PROGLITE2 = $7EF3C9
|
||||
|
||||
; .d.. ....
|
||||
; d - World (0: Light World | 1: Dark World)
|
||||
SAVEWORLD = $7EF3CA
|
||||
|
||||
; Not used
|
||||
UNUSED_7EF3CB = $7EF3CB
|
||||
|
||||
; Current follower ID
|
||||
FOLLOWER = $7EF3CC
|
||||
|
||||
; Cache of follower properties
|
||||
FOLLOWCYL = $7EF3CD
|
||||
FOLLOWCYH = $7EF3CE
|
||||
FOLLOWCXL = $7EF3CF
|
||||
FOLLOWCXH = $7EF3D0
|
||||
|
||||
; Copies INDOORS
|
||||
FOLLOWERINOUT = $7EF3D1
|
||||
|
||||
; Copies LAYER
|
||||
FOLLOWERCLAYER = $7EF3D2
|
||||
|
||||
; Indicates the follower is currently following
|
||||
; 0x00 - Following
|
||||
; 0x80 - Not following
|
||||
FOLLOWERING = $7EF3D3
|
||||
|
||||
; Unused
|
||||
UNUSED_7EF3D4 = $7EF3D4
|
||||
UNUSED_7EF3D5 = $7EF3D5
|
||||
UNUSED_7EF3D6 = $7EF3D6
|
||||
|
||||
; Side Quest Progress Flags
|
||||
; .dgo mwcn
|
||||
; n - Met Mask Salesman (shown "need Ocarina" dialogue)
|
||||
; c - Found cursed Cucco at ranch (shown first dialogue)
|
||||
; w - Found withering Deku Scrub (shown first dialogue)
|
||||
; m - Got Mushroom from Toadstool Woods
|
||||
; o - Old Man Mountain quest active
|
||||
; g - Goron quest active (collecting rock meat)
|
||||
; d - (reserved)
|
||||
SideQuestProg = $7EF3D7
|
||||
|
||||
; Side Quest Progress Flags 2
|
||||
; .bts pfmr
|
||||
; r - Ranch Girl transformed back (dialogue shown)
|
||||
; m - Mask Salesman taught Song of Healing
|
||||
; f - Fortune teller visited (any fortune)
|
||||
; p - Potion shop visited with mushroom
|
||||
; s - Deku Scrub soul freed (before mask given)
|
||||
; t - Tingle met (any map purchased)
|
||||
; b - Bean beanstalk grown (final stage)
|
||||
SideQuestProg2 = $7EF3D8
|
||||
|
||||
; Player name
|
||||
NAME1L = $7EF3D9
|
||||
NAME1H = $7EF3DA
|
||||
NAME2L = $7EF3DB
|
||||
NAME2H = $7EF3DC
|
||||
NAME3L = $7EF3DD
|
||||
NAME3H = $7EF3DE
|
||||
NAME4L = $7EF3DF
|
||||
NAME4H = $7EF3E0
|
||||
|
||||
; Save file checksum; expected to be $55AA
|
||||
SCHKSML = $7EF3E1
|
||||
SCHKSMH = $7EF3E2
|
||||
|
||||
; Games played in each dungeon
|
||||
GPSEWER = $7EF3E3
|
||||
GPHYRULE = $7EF3E5
|
||||
GPEAST = $7EF3E7
|
||||
GPDESERT = $7EF3E9
|
||||
GPAGA = $7EF3EB
|
||||
GPSWAMP = $7EF3ED
|
||||
GPPOD = $7EF3EF
|
||||
GPMIRE = $7EF3F1
|
||||
GPWOODS = $7EF3F3
|
||||
GPICE = $7EF3F5
|
||||
GPHERA = $7EF3F7
|
||||
GPTHIEF = $7EF3F9
|
||||
GPTROCK = $7EF3FB
|
||||
GPGANON = $7EF3FD
|
||||
|
||||
; Games played for current segment
|
||||
GPNOW = $7EF3FF
|
||||
|
||||
; Total games played
|
||||
; No display on file select if 0xFFFF
|
||||
GAMESPLAYED = $7EF401
|
||||
|
||||
; Big unused block
|
||||
UNUSED_7EF403 = $7EF403
|
||||
DEATHS_MAXED = $7EF405
|
||||
|
||||
; Inverse checksum for save file
|
||||
SAVEICKSML = $7EF4FE
|
||||
SAVEICKSMH = $7EF4FF
|
||||
@@ -1,75 +0,0 @@
|
||||
struct Sprite $7E0BA0
|
||||
{
|
||||
.BulletProof: skip 16
|
||||
.AncillaId: skip 16
|
||||
.Slot: skip 16
|
||||
.OwDeath: skip 32
|
||||
.Prize: skip 16
|
||||
; 7E0BF0
|
||||
; 7E0C9A
|
||||
.OwScreen: skip 16
|
||||
.Deflect: skip 16
|
||||
.ForceDrop: skip 16
|
||||
.OverlordRoom: skip 8
|
||||
.BumpDamage: skip 16
|
||||
.CurrDamage: skip 16
|
||||
; 0CF2
|
||||
; 0CFF
|
||||
.yl: skip 16
|
||||
.xl: skip 16
|
||||
.yh: skip 16
|
||||
.xh: skip 16
|
||||
.vy: skip 16
|
||||
.vx: skip 16
|
||||
.sub_vy: skip 16
|
||||
.sub_vx: skip 16
|
||||
.action: skip 16
|
||||
.frame: skip 16
|
||||
.misc_a: skip 16
|
||||
.misc_b: skip 16
|
||||
.gfx: skip 16
|
||||
.state: skip 16
|
||||
.misc_c: skip 16
|
||||
.TimerA: skip 16
|
||||
.TimerB: skip 16
|
||||
.TimerC: skip 16
|
||||
.type: skip 16
|
||||
.subtype: skip 16
|
||||
.nbr_oam: skip 16
|
||||
.hp: skip 16
|
||||
.props: skip 16
|
||||
.collide: skip 16
|
||||
.delay: skip 16
|
||||
.misc_d: skip 16
|
||||
.recoil: skip 16
|
||||
.misc_e: skip 16
|
||||
.misc_f: skip 16
|
||||
.misc_g: skip 16
|
||||
.timer_d: skip 16
|
||||
.death_timer: skip 16
|
||||
.pause: skip 16
|
||||
.timer_e: skip 16
|
||||
.floor: skip 16
|
||||
.recoil_y: skip 16
|
||||
.recoil_x: skip 16
|
||||
.oam_prop: skip 16
|
||||
.collision_prop: skip 16
|
||||
.z: skip 16
|
||||
.vz: skip 16
|
||||
.sub_vz: skip 16
|
||||
}
|
||||
endstruct
|
||||
|
||||
struct TimeState $7EE000
|
||||
{
|
||||
.Hours: skip 1
|
||||
.Minutes: skip 1
|
||||
.Speed: skip 1
|
||||
.Padding: skip 13 ; Pad to $7EE010
|
||||
.BlueVal: skip 2
|
||||
.GreenVal: skip 2
|
||||
.RedVal: skip 2
|
||||
.TempColor: skip 2
|
||||
.SubColor: skip 2
|
||||
}
|
||||
endstruct
|
||||
759
Core/symbols.asm
759
Core/symbols.asm
@@ -1,759 +0,0 @@
|
||||
; =========================================================
|
||||
; WRAM in Use
|
||||
org $008000
|
||||
base $7E0730 ; MAP16OVERFLOW free ram region
|
||||
{
|
||||
MenuScrollLevelV: skip 1
|
||||
MenuScrollLevelH: skip 1
|
||||
MenuScrollHDirection: skip 2
|
||||
MenuItemValueSpoof: skip 2
|
||||
ShortSpoof: skip 1
|
||||
MusicNoteValue: skip 2
|
||||
GoldstarOrHookshot: skip 1
|
||||
Neck_Index: skip 1
|
||||
Neck1_OffsetX: skip 1
|
||||
Neck1_OffsetY: skip 1
|
||||
Neck2_OffsetX: skip 1
|
||||
Neck2_OffsetY: skip 1
|
||||
Neck3_OffsetX: skip 1
|
||||
Neck3_OffsetY: skip 1
|
||||
Offspring1_Id: skip 1
|
||||
Offspring2_Id: skip 1
|
||||
Offspring3_Id: skip 1
|
||||
Kydreeok_Id: skip 1
|
||||
FishingOrPortalRod: skip 1
|
||||
}
|
||||
base off
|
||||
|
||||
; =========================================================
|
||||
|
||||
function RGBto555(R,G,B) = ((R/8)<<10)|((G/8)<<5)|(B/8) ; zarby
|
||||
function menu_offset(y,x) = (y*64)+(x*2)
|
||||
|
||||
; Current Dream ID (0x00-0x03)
|
||||
CurrentDream = $0426
|
||||
|
||||
; Current Song
|
||||
CurrentSong = $030F
|
||||
|
||||
; 01 - Song of Healing
|
||||
; 02 - Song of Time
|
||||
; 03 - Song of Storms
|
||||
SongFlag = $FE
|
||||
|
||||
; Overlay camera cache variable
|
||||
CameraCache = $0632
|
||||
|
||||
; =========================================================
|
||||
; The record format for the low table is 4 bytes:
|
||||
; byte OBJ*4+0: xxxxxxxx
|
||||
; byte OBJ*4+1: yyyyyyyy
|
||||
; byte OBJ*4+2: cccccccc
|
||||
; byte OBJ*4+3: vhoopppN
|
||||
|
||||
; The record format for the high table is 2 bits:
|
||||
; bit 0/2/4/6 of byte OBJ/4: X
|
||||
; bit 1/3/5/7 of byte OBJ/4: s
|
||||
|
||||
; Xxxxxxxxx = X position of the sprite. signed but see below.
|
||||
; yyyyyyyy = Y position of the sprite.
|
||||
; cccccccc = First tile of the sprite.
|
||||
; N = Name table of the sprite. See below for VRAM address calculation
|
||||
; ppp = Palette of the sprite. The first palette index is 128+ppp*16.
|
||||
; oo = Sprite priority. See below for details.
|
||||
; h/v = Horizontal/Vertical flip flags.
|
||||
; s = Sprite size flag. See below for details.
|
||||
|
||||
OamPtr = $90
|
||||
OamPtrH = $92
|
||||
OamBackup = $0FEC
|
||||
|
||||
; =========================================================
|
||||
; Sprite RAM
|
||||
|
||||
SprY = $0D00
|
||||
SprX = $0D10
|
||||
SprYH = $0D20
|
||||
SprXH = $0D30
|
||||
|
||||
SprYSpeed = $0D40
|
||||
SprXSpeed = $0D50
|
||||
|
||||
SprYRound = $0D60
|
||||
SprXRound = $0D70
|
||||
|
||||
SprCachedX = $0FD8
|
||||
SprCachedY = $0FDA
|
||||
|
||||
SprAction = $0D80 ; Indexes the action jump table
|
||||
SprFrame = $0D90 ; Indexes the SprGfx for drawing
|
||||
SprGfx = $0DC0 ; Determine the GFX used for the sprite
|
||||
|
||||
SprMiscA = $0DA0 ; Direction, position, or other misc usage
|
||||
SprMiscB = $0DB0 ; Prober parent sprite ID, misc
|
||||
SprMiscC = $0DE0 ; Cardinal direction the sprite is facing
|
||||
SprMiscD = $0E90 ; Pikit stolen item, misc usage
|
||||
SprMiscE = $0EB0 ; Head direction 0123 -> udlr
|
||||
SprMiscF = $0EC0 ; Clones
|
||||
SprMiscG = $0ED0 ; Probe whistle sfx
|
||||
|
||||
SprCustom = $1CC0 ;
|
||||
|
||||
; Used in sprite state 0x03 (falling in water)
|
||||
; used as delay in most of the sprites
|
||||
SprDelay = $0E80
|
||||
SprFlash = $0B89 ; Enemy color flash buffer
|
||||
|
||||
SprTimerA = $0DF0 ; Action, decreased by 1 each frame
|
||||
SprTimerB = $0E00 ; Animation, decreased by 1 each frame
|
||||
SprTimerC = $0E10 ; decreased by 1 each frame
|
||||
SprTimerD = $0EE0 ; decreased by 1 each frame
|
||||
SprTimerE = $0F10 ; decreased by 1 each frame
|
||||
SprTimerF = $0F80 ; Gravity, decreased by 2 each frame
|
||||
|
||||
SprSlot = $0FA0 ; Current sprite slot being executed
|
||||
|
||||
SprStunTimer = $0B58 ; counts down from 0xFF
|
||||
|
||||
SprPause = $0F00 ; Inactive if nonzero
|
||||
SprFloor = $0F20 ; 0 (top layer), 1 (bottom layer)
|
||||
SprType = $0E20 ; Sprite ID
|
||||
SprSubtype = $0E30 ; Sprite subtype
|
||||
|
||||
; hmwo oooo
|
||||
; o - define the number of OAM slots used by the sprite
|
||||
; w - Causes enemies to go towards the walls
|
||||
; m - Master sword ceremony sprite flag
|
||||
; h - If set, harmless. Unset you take damage from contact.
|
||||
SprNbrOAM = $0E40
|
||||
SprHealth = $0E50
|
||||
|
||||
; 0x00 - Sprite is dead, totally inactive
|
||||
; 0x01 - Sprite falling into a pit with generic animation.
|
||||
; 0x02 - Sprite transforms into a puff of smoke, often producing an item
|
||||
; 0x03 - Sprite falling into deep water (optionally making a fish jump up?)
|
||||
; 0x04 - Death mode for bosses
|
||||
; 0x05 - Sprite falling into a pit that has a special animation
|
||||
; 0x06 - Death Mode for normal creatures.
|
||||
; 0x08 - Sprite is being spawned at load time.
|
||||
; An initialization routine will be run for one frame,
|
||||
; and then move on to the active state (0x09) next frame.
|
||||
; 0x09 - Sprite is in the normal, active mode.
|
||||
; 0x0A - Sprite is being carried by the player.
|
||||
; 0x0B - Sprite is frozen and / or stunned.
|
||||
SprState = $0DD0
|
||||
|
||||
; nios pppt
|
||||
; n - if set, don't draw extra death anim
|
||||
; i - impervious to attacks and collision (0: normal | 1: clink)
|
||||
; o - shadow size (0: normal | 1: small)
|
||||
; s - shadow (0: no shadow | 1: shadow)
|
||||
; p - palette used for OAM props
|
||||
; t - name table used for OAM props
|
||||
SprGfxProps = $0E60
|
||||
|
||||
; Direction of sprite collision with wall
|
||||
SprCollision = $0E70
|
||||
|
||||
; Definitely closely tied to the process of a sprite taking damage.
|
||||
; Seems to serve as a palette cycling index, or a state variable.
|
||||
; When this value is positive
|
||||
; 0x80 - Signal that the recoil process has finished
|
||||
; and will terminate during this frame.
|
||||
SprRecoil = $0EA0 ; Recoil Timer
|
||||
|
||||
; abbbbbbb:
|
||||
; a - start death timer
|
||||
; b - death timer
|
||||
SprDeath = $0EF0
|
||||
|
||||
SprYRecoil = $0F30
|
||||
SprXRecoil = $0F40
|
||||
|
||||
; DIWS UUUU
|
||||
; D - boss death
|
||||
; I - Impervious to all attacks
|
||||
; W - Water slash
|
||||
; S - Draw Shadow
|
||||
; U - Unused
|
||||
SprProps = $0F50
|
||||
|
||||
; ISPH HHHH
|
||||
; I - ignore collisions
|
||||
; S - Statis (not alive eg beamos)
|
||||
; P - Persist code still run outside of camera
|
||||
; H - Hitbox
|
||||
SprHitbox = $0F60
|
||||
SprHeight = $0F70 ; Distance from the shadow
|
||||
SprHeightS = $0F90 ; Distance from the shadow subpixel
|
||||
SprFreeze = $0FC1 ; Seems to freeze sprites
|
||||
|
||||
; Primarily deals with bump damage
|
||||
; tzpd bbbb
|
||||
; t - TODO
|
||||
; z - High priority target for bees to give hints
|
||||
; p - Powder interaction (0: normal | 1: ignore)
|
||||
; d - Behavior when a boss spawns (0: die | 1: live)
|
||||
; b - bump damage class
|
||||
; Bump damage classes are read from a table at $06F42D
|
||||
; Each table entry has 3 values, for green, blue, and red mails
|
||||
; class g b r
|
||||
; 0x00 2 1 1
|
||||
; 0x01 4 4 4
|
||||
; 0x02 0 0 0
|
||||
; 0x03 8 4 2
|
||||
; 0x04 8 8 8
|
||||
; 0x05 16 8 4
|
||||
; 0x06 32 16 8
|
||||
; 0x07 32 24 16
|
||||
; 0x08 24 16 8
|
||||
; 0x09 64 48 24
|
||||
SprBump = $0CD2
|
||||
|
||||
; Damage sprite is enduring
|
||||
SprDmgTaken = $0CE2
|
||||
|
||||
; Sprite Deflection Properties
|
||||
; abcdefgh
|
||||
; a - If set, sprite is active
|
||||
; b - Same as bit 'a' for Zora.
|
||||
; c - Never queried, pushable interaction flag
|
||||
; d - If hit from front, deflect Ice Rod, Somarian missile,
|
||||
; boomerang, hookshot, and sword beam, and arrows stick in
|
||||
; it harmlessly. If bit 1 is also set, frontal arrows will
|
||||
; instead disappear harmlessly.
|
||||
; e - If set, sprite collides with less tiles than usual
|
||||
; f - If set, impervious to sword and hammer type attacks
|
||||
; g - If set, impervious to arrows, but may have other additional meanings.
|
||||
; h - Handles behavior with previous deaths flagged in $7FDF80 (0: default | 1: ignore)
|
||||
SprDefl = $0CAA
|
||||
|
||||
; iwbs pppp
|
||||
; i - disable tile interaction
|
||||
; w - something water
|
||||
; b - sprite is blocked by shield
|
||||
; s - taking damage sfx to use TODO name
|
||||
; p - prize pack
|
||||
SprPrize = $0BE0
|
||||
|
||||
; tttt a.bp
|
||||
; t - tile interaction hitbox
|
||||
; a - deflect arrows TODO VERIFY
|
||||
; b - boss death
|
||||
; p - Sprite ignores falling into a pit when frozen?
|
||||
SprTileDie = $0B6B
|
||||
|
||||
; For sprites that interact with speical objects (arrows in particular)
|
||||
; the special object will identify its type to the sprite via this location.
|
||||
SprSpecial = $0BB0
|
||||
|
||||
; If nonzero, ancillae do not interact with the sprite
|
||||
; Bulletproof
|
||||
SprBulletproof = $0BA0
|
||||
|
||||
SprRoom = $0C9A ;X W Contains the area or room id the sprite has been loaded in
|
||||
SprDrop = $0CBA ;X W 00: Drop nothing, 01: drop normal key, 03: Drop green rupee, OtherValues: Drop big key
|
||||
|
||||
; Overlord
|
||||
OverlordId = $0B00
|
||||
OverlordX = $0B08
|
||||
OverlordXH = $0B10
|
||||
OverlordY = $0B18
|
||||
OverlordYH = $0B20
|
||||
|
||||
OverlordTimerA = $0B28
|
||||
OverlordTimerB = $0B30
|
||||
OverlordTimerC = $0B38
|
||||
|
||||
; =========================================================
|
||||
; Sprite Functions
|
||||
|
||||
SpriteData_OAMProp = $0DB359
|
||||
|
||||
; Clear all properties for sprites
|
||||
SpritePrep_ResetProperties = $0DB871
|
||||
|
||||
; set the oam coordinate for the sprite draw
|
||||
Sprite_PrepOamCoord = $06E416
|
||||
|
||||
; Draw the sprite depending of the position of the player
|
||||
; (if he has to be over or under link)
|
||||
Sprite_OAM_AllocateDeferToPlayer = $06F864
|
||||
|
||||
OAM_AllocateFromRegionA = $0DBA80 ; Above
|
||||
OAM_AllocateFromRegionB = $0DBA84 ; Below
|
||||
OAM_AllocateFromRegionC = $0DBA88 ; Above
|
||||
OAM_AllocateFromRegionD = $0DBA8C ; Above
|
||||
OAM_AllocateFromRegionE = $0DBA90 ; Above
|
||||
OAM_AllocateFromRegionF = $0DBA94 ; Above
|
||||
|
||||
Sprite_DrawMultiple_quantity_preset = $05DF70
|
||||
|
||||
; check if the sprite is getting damage from player or items
|
||||
Sprite_CheckDamageFromPlayer = $06F2AA
|
||||
|
||||
; check if the sprite is touching the player to damage
|
||||
Sprite_CheckDamageToPlayer = $06F121
|
||||
|
||||
; damage the player everywhere on screen?
|
||||
Sprite_AttemptDamageToPlayerPlusRecoil = $06F41F
|
||||
|
||||
; makes all the sprites on screen shaking
|
||||
ApplyRumbleToSprites = $0680FA
|
||||
|
||||
Sprite_MoveLong = $1D808C
|
||||
|
||||
Sprite_ProjectSpeedTowardsPlayer = $06EA1A
|
||||
|
||||
Sprite_Decelerate_X = $05E657
|
||||
Sprite_Decelerate_Y = $05E666
|
||||
|
||||
; =========================================================
|
||||
; returns carry clear if there was no overlap
|
||||
; args:
|
||||
pos1_x_low = $00
|
||||
pos1_y_low = $01
|
||||
pos1_size = $02
|
||||
pos1_height = $03
|
||||
|
||||
pos2_x_low = $04
|
||||
pos2_y_low = $05
|
||||
pos2_size = $06
|
||||
pos2_height = $07
|
||||
|
||||
pos1_x_high = $08
|
||||
pos1_y_high = $09
|
||||
pos2_x_high = $0A
|
||||
pos2_y_high = $0B
|
||||
|
||||
ans_low = $0F
|
||||
ans_high = $0C
|
||||
|
||||
CheckIfHitBoxesOverlap = $0683E6
|
||||
|
||||
; $0FD8 = sprite's X coordinate
|
||||
; $0FDA = sprite's Y coordinate
|
||||
Sprite_Get16BitCoords_Local = $0684C1
|
||||
Sprite_Get_16_bit_Coords = $0684BD
|
||||
|
||||
; load / draw a 16x16 sprite
|
||||
Sprite_PrepAndDrawSingleLarge = $06DBF0
|
||||
|
||||
; load / draw a 8x8 sprite
|
||||
Sprite_PrepAndDrawSingleSmall = $06DBF8
|
||||
|
||||
; draw shadow (requires additional oam allocation)
|
||||
Sprite_DrawShadow = $06DC54
|
||||
|
||||
Sprite_DrawWaterRipple = $059FFA
|
||||
Sprite_DrawRippleIfInWater = $1EFF8D
|
||||
|
||||
; check if the sprite is colliding with a solid tile set $0E70, X
|
||||
; ----udlr , u = up, d = down, l = left, r = right
|
||||
Sprite_CheckTileCollision = $06E496
|
||||
Sprite_CheckTileCollision_long = $06E496
|
||||
|
||||
; $00[0x02] - Entity Y coordinate
|
||||
; $02[0x03?] - Entity X coordinate
|
||||
; $0FA5 - Tile ID result
|
||||
Sprite_GetTileAttr = $06E87B
|
||||
|
||||
; check if the sprite is colliding with a solid sloped tile
|
||||
Sprite_CheckSlopedTileCollision = $06E8FD
|
||||
|
||||
; set the velocity x,y towards the player (A = speed)
|
||||
Sprite_ApplySpeedTowardsPlayer = $06EA12
|
||||
|
||||
; \return $0E is low byte of player_y_pos - sprite_y_pos
|
||||
; \return $0F is low byte of player_x_pos - sprite_x_pos
|
||||
Sprite_DirectionToFacePlayer = $06EAA0
|
||||
|
||||
; if Link is to the left of the sprite, Y = 1, otherwise Y = 0.
|
||||
Sprite_IsToRightOfPlayer = $06EACD
|
||||
|
||||
; return Y=1 sprite is below player, otherwise Y = 0
|
||||
Sprite_IsBelowPlayer = $06EAE4
|
||||
|
||||
; $06 = sprite's Y coordinate
|
||||
; $07 = sprite's X coordinate
|
||||
Sprite_IsBelowLocation = $06EB1D
|
||||
|
||||
; check damage done to player if they collide on same layer
|
||||
Sprite_CheckDamageToPlayerSameLayer = $06F129
|
||||
|
||||
; check damage done to player if they collide even if they are not on same layer
|
||||
Sprite_CheckDamageToPlayerIgnoreLayer = $06F131
|
||||
|
||||
; play a sound loaded in A
|
||||
Sound_SetSfx1PanLong = $0DBB6E
|
||||
Sound_SetSfx2PanLong = $0DBB7C
|
||||
Sound_SetSfx3PanLong = $0DBB8A
|
||||
|
||||
; =========================================================
|
||||
; spawn a new sprite on screen, A = sprite id
|
||||
; when using this function you have to set the position yourself
|
||||
; these values belong to the sprite who used that function not the new one
|
||||
; $00 low x, $01 high x
|
||||
; $02 low y, $03 high y
|
||||
; $04 height, $05 low x (overlord)
|
||||
; $06 high x (overlord), $07 low y (overlord)
|
||||
; $08 high y (overlord)
|
||||
Sprite_SpawnDynamically = $1DF65D
|
||||
Sprite_SpawnDynamically_slot_limited = $1DF65F
|
||||
|
||||
Sprite_SetSpawnedCoords = $09AE64
|
||||
Sprite_SetSpawnedCoordinates = $09AE64
|
||||
|
||||
; move the sprite if he stand on a conveyor belt
|
||||
Sprite_ApplyConveyorAdjustment = $1D8010
|
||||
|
||||
; Setup sprite hitbox for comparison with scrap ram
|
||||
Sprite_SetupHitBoxLong = $0683EA
|
||||
|
||||
; player can't pass through the sprite
|
||||
Sprite_BehaveAsBarrier = $1EF4F3
|
||||
Sprite_PlayerCantPassThrough = $1EF4F3
|
||||
|
||||
; player can't hookshot to that sprite
|
||||
Sprite_CancelHookshot = $0FF540
|
||||
Sprite_NullifyHookshotDrag = $0FF540
|
||||
|
||||
; show a message box without any condition
|
||||
; A = low byte of message ID to use.
|
||||
; Y = high byte of message ID to use.
|
||||
Sprite_ShowMessageUnconditional = $05E219
|
||||
|
||||
; show a message if we press A and face the sprite
|
||||
; A = low byte of message ID to use.
|
||||
; Y = high byte of message ID to use.
|
||||
Sprite_ShowSolicitedMessage = $05E1A7
|
||||
Sprite_ShowSolicitedMessageIfPlayerFacing = $05E1A7
|
||||
|
||||
; show a message if we touch the sprite
|
||||
; should be used with Sprite_PlayerCantPassThrough
|
||||
; A = low byte of message ID to use.
|
||||
; Y = high byte of message ID to use.
|
||||
Sprite_ShowMessageOnContact = $05E1F0
|
||||
Sprite_ShowMessageFromPlayerContact = $05E1F0
|
||||
|
||||
; Parameters: Stack, A
|
||||
JumpTableLocal = $008781
|
||||
UseImplicitRegIndexedLocalJumpTable = $008781
|
||||
|
||||
EnableForceBlank = $00893D
|
||||
|
||||
Snitch_SpawnGuard = $09C02F
|
||||
|
||||
Sprite_KillFriends = $09EF56
|
||||
|
||||
Sprite_KillSelf = $09F1F8
|
||||
|
||||
Dungeon_SpriteInducedTilemapUpdate = $01E7A9
|
||||
|
||||
; =========================================================
|
||||
; $04 = X
|
||||
; $05 = HighX
|
||||
; $06 = Y
|
||||
; $07 = HighY
|
||||
; A = Speed
|
||||
; \return $00 - Y Velocity
|
||||
; \return $01 - X Velocity
|
||||
|
||||
Sprite_ProjectSpeedTowardsEntityLong = $06EA22
|
||||
|
||||
; =========================================================
|
||||
; Guard and Prober functions
|
||||
; SprMiscB contains the ID of the parent sprite to alert
|
||||
|
||||
Guard_ChaseLinkOnOneAxis = $05C542
|
||||
Guard_ParrySwordAttacks = $06EB5E
|
||||
Probe_CheckTileSolidity = $0DC26E
|
||||
|
||||
ProbeAndSparkCheckDirXSpeed = $05C359
|
||||
ProbeAndSparkCheckDirYSpeed = $05C361
|
||||
ProbeAndSparkXSpeed = $05C369
|
||||
ProbeAndSparkYSpeed = $05C371
|
||||
|
||||
ColinearDirections = $05C381
|
||||
OrthogonalDirections = $05C389
|
||||
ColinearNextDirections = $05C391
|
||||
OrthogonalNextDirections = $05C399
|
||||
|
||||
Sprite_SpawnProbeAlways_long = $05C66E
|
||||
|
||||
Sprite_TrackBodyToHead = $05DCA2
|
||||
|
||||
; =========================================================
|
||||
; Misc long functions
|
||||
|
||||
GetRandomInt = $0DBA71
|
||||
|
||||
Sprite_SpawnFireball = $0DDA06
|
||||
Sprite_SpawnSmallSplash = $1EA820
|
||||
Sprite_SpawnSparkleGarnish = $058008
|
||||
Sprite_SpawnPoofGarnish = $05AB9C
|
||||
|
||||
Sprite_CheckIfLifted = $06AA0C
|
||||
|
||||
Sprite_TransmuteToBomb = $06AD50
|
||||
|
||||
Sprite_RepelDash = $079291
|
||||
|
||||
Sprite_LoadGfxProperties = $00FC41
|
||||
|
||||
ThrownSprite_TileAndSpriteInteraction_long = $06DFF2
|
||||
|
||||
Overworld_DrawMap16_Persist = $1BC97C
|
||||
|
||||
; =========================================================
|
||||
; Local functions which may be useful for sprites
|
||||
; Sprite_AttemptZapDamage - 06EC02
|
||||
|
||||
; =========================================================
|
||||
; Misc RAM
|
||||
|
||||
FrameCounter = $1A ; value that is increasing every frame
|
||||
Indoor = $1B ; 0: outside, 1: indoor
|
||||
UpdPalFlag = $15 ; Update all palettes $7EC500-$7EC700 if non-zero
|
||||
|
||||
RoomIndex = $A0 ; Return the current room ID
|
||||
AreaIndex = $8A ; Return the current overworld area ID
|
||||
|
||||
MsgChoice = $1CE8 ; Choice made in a message box
|
||||
|
||||
; set the mosaic setting ($2106) XXXXDCBA
|
||||
; [ABCD BG1/BG2/BG3/BG4][X size of the mosaic pixels 0-16]
|
||||
Mosaic = $95
|
||||
|
||||
DungeonMainCheck = $021B
|
||||
SpriteRanCheck = $8E
|
||||
|
||||
; Underworld:
|
||||
; Flags sprite deaths in underworld based on slot.
|
||||
; Indexed by 2 * room ID.
|
||||
; Overworld:
|
||||
; Holds ID+1 of sprite with each position being assigned a unique byte
|
||||
; 0x00 indicates no sprite in this slot
|
||||
UWDEATH = $7FDF80
|
||||
|
||||
RebuildHUD_long = $0DFA58
|
||||
|
||||
ForcePrizeDrop_long = $06FA54
|
||||
|
||||
TileDetect_BigArea_long = $07CF0A
|
||||
|
||||
Follower_Initialize = $099EFC
|
||||
|
||||
HandleFollowersAfterMirroring = $07AAA2
|
||||
|
||||
; =========================================================
|
||||
; Controllers
|
||||
|
||||
; BYSTUDLR
|
||||
; [B BButton][Y YButton]
|
||||
; [SSelect Button][TStart Button]
|
||||
; [UDLR dpad buttons Up, Down, Left, Right]
|
||||
RawJoypad1L = $F0
|
||||
; AXLRIIII
|
||||
; [A AButton][X Xbutton][L LButton][R RButton][I = controller ID]
|
||||
RawJoypad1H = $F2
|
||||
|
||||
; BYSTUDLR
|
||||
; [B BButton][Y YButton]
|
||||
; [SSelect Button][TStart Button]
|
||||
; [UDLR dpad buttons Up, Down, Left, Right]
|
||||
PressPad1L = $F4
|
||||
|
||||
; AXLRIIII
|
||||
; [A AButton][X Xbutton]
|
||||
; [L LButton][R RButton][I = controller ID]
|
||||
PressPad1H = $F6
|
||||
|
||||
ButtonAFlag = $3B ; bit7: Button A is down (A-------)
|
||||
|
||||
; Timer for B button
|
||||
; ssss tttt
|
||||
; s - spin attack in action; set to 0x9
|
||||
; t - sword swing timer
|
||||
; 0x00 - No swing
|
||||
; —0x08 - Sword swing
|
||||
; 0x09 - Sword primed
|
||||
; —0x0C - Poking wall
|
||||
;
|
||||
; Also used as a 16-bit countdown for various cutscenes:
|
||||
; Ether tablet: 0x00C0
|
||||
; Bombos tablet: 0x00E0
|
||||
; Desert tablet: 0x0001
|
||||
BFLAG = $3C
|
||||
|
||||
; =========================================================
|
||||
; Ancilla
|
||||
|
||||
AnciOAMPrior = $0280 ; Ancilla oam priority if non zero use highest priority for draw
|
||||
AnciColTimer = $028A ; Ancilla collision timer to prevent doing collision code too often set to 06 after a collision
|
||||
AnciZSpeed = $0294 ; Ancilla Z Speed
|
||||
AnciHeight = $029E ; Ancilla Height how far it is from its shadow
|
||||
AnciHeightH = $02A8 ; Ancilla Height hight byte how far it is from its shadow
|
||||
|
||||
AnciMiscA = $0BF0 ; This can be used to do anything in ancilla
|
||||
AnciMiscB = $0C54 ; This can be used to do anything in ancilla
|
||||
AnciMiscC = $0C5E ; This can be used to do anything in ancilla (often used to track item received)
|
||||
AnciMiscD = $0C72 ; This can be used to do anything in ancilla (often used to track direction)
|
||||
|
||||
; General use variable for ancillae. Only intended for front slots.
|
||||
; LENGTH: 0x05
|
||||
AnciMiscJ = $03CA
|
||||
|
||||
; General use variable for ancillae.
|
||||
; LENGTH: 0x0A
|
||||
ANC0MISCB = $0385
|
||||
|
||||
AnciTimerA = $0C68 ; This is a timer, value is decreased by 1 every frame
|
||||
|
||||
AnciY = $0BFA ; Position Y of the ancilla (Up to Down)
|
||||
AnciX = $0C04 ; Position X of the ancilla (Left to Right)
|
||||
AnciYH = $0C0E ; High (often determine the room) Position Y of the ancilla (Up to Down)
|
||||
AnciXH = $0C18 ; High (often determine the room) Position X of the ancilla (Left to Right)
|
||||
AnciXSpeed = $0C22 ; Y Speed of the ancilla can go negative to go up
|
||||
AnciYSpeed = $0C2C ; X Speed of the ancilla can go negative to go left
|
||||
AnciLayer = $0C7C ; return the floor where the ancilla is
|
||||
AnciOamBuf = $0C86 ; Oam buffer?
|
||||
AnciOAMNbr = $0C90 ; Number of OAM slots used
|
||||
|
||||
AnciYsub = $0C36 ; sub pixel for Y position for ancilla
|
||||
AnciXsub = $0C40 ; sub pixel for X position for ancilla
|
||||
|
||||
; Ancilla IDs
|
||||
; db $00 ; 0x00 - NOTHING
|
||||
; db $08 ; 0x01 - SOMARIA BULLET
|
||||
; db $0C ; 0x02 - FIRE ROD SHOT
|
||||
; db $10 ; 0x03 - UNUSED
|
||||
; db $10 ; 0x04 - BEAM HIT
|
||||
; db $04 ; 0x05 - BOOMERANG
|
||||
; db $10 ; 0x06 - WALL HIT
|
||||
; db $18 ; 0x07 - BOMB
|
||||
; db $08 ; 0x08 - DOOR DEBRIS
|
||||
; db $08 ; 0x09 - ARROW
|
||||
; db $08 ; 0x0A - ARROW IN THE WALL
|
||||
; db $00 ; 0x0B - ICE ROD SHOT
|
||||
; db $14 ; 0x0C - SWORD BEAM_BOUNCE
|
||||
; db $00 ; 0x0D - SPIN ATTACK FULL CHARGE SPARK
|
||||
; db $10 ; 0x0E - BLAST WALL EXPLOSION
|
||||
; db $28 ; 0x0F - BLAST WALL EXPLOSION
|
||||
; db $18 ; 0x10 - BLAST WALL EXPLOSION
|
||||
; db $10 ; 0x11 - ICE ROD WALL HIT
|
||||
; db $10 ; 0x12 - BLAST WALL EXPLOSION
|
||||
; db $10 ; 0x13 - ICE ROD SPARKLE
|
||||
; db $10 ; 0x14 - BAD POINTER
|
||||
; db $0C ; 0x15 - SPLASH
|
||||
; db $08 ; 0x16 - HIT STARS
|
||||
; db $08 ; 0x17 - SHOVEL DIRT
|
||||
; db $50 ; 0x18 - ETHER SPELL
|
||||
; db $00 ; 0x19 - BOMBOS SPELL
|
||||
; db $10 ; 0x1A - POWDER DUST
|
||||
; db $08 ; 0x1B - SWORD WALL HIT
|
||||
; db $40 ; 0x1C - QUAKE SPELL
|
||||
; db $00 ; 0x1D - SCREEN SHAKE
|
||||
; db $0C ; 0x1E - DASH DUST
|
||||
; db $24 ; 0x1F - HOOKSHOT
|
||||
; db $10 ; 0x20 - BLANKET
|
||||
; db $0C ; 0x21 - SNORE
|
||||
; db $08 ; 0x22 - ITEM GET
|
||||
; db $10 ; 0x23 - LINK POOF
|
||||
; db $10 ; 0x24 - GRAVESTONE
|
||||
; db $04 ; 0x25 - BAD POINTER
|
||||
; db $0C ; 0x26 - SWORD SWING SPARKLE
|
||||
; db $1C ; 0x27 - DUCK
|
||||
; db $00 ; 0x28 - WISH POND ITEM
|
||||
; db $10 ; 0x29 - MILESTONE ITEM GET
|
||||
; db $14 ; 0x2A - SPIN ATTACK SPARKLE A
|
||||
; db $14 ; 0x2B - SPIN ATTACK SPARKLE B
|
||||
; db $10 ; 0x2C - SOMARIA BLOCK
|
||||
; db $08 ; 0x2D - SOMARIA BLOCK FIZZ
|
||||
; db $20 ; 0x2E - SOMARIA BLOCK FISSION
|
||||
; db $10 ; 0x2F - LAMP FLAME
|
||||
; db $10 ; 0x30 - BYRNA WINDUP SPARK
|
||||
; db $10 ; 0x31 - BYRNA SPARK
|
||||
; db $04 ; 0x32 - BLAST WALL FIREBALL
|
||||
; db $00 ; 0x33 - BLAST WALL EXPLOSION
|
||||
; db $80 ; 0x34 - SKULL WOODS FIRE
|
||||
; db $10 ; 0x35 - MASTER SWORD GET
|
||||
; db $04 ; 0x36 - FLUTE
|
||||
; db $30 ; 0x37 - WEATHERVANE EXPLOSION
|
||||
; db $14 ; 0x38 - CUTSCENE DUCK
|
||||
; db $10 ; 0x39 - SOMARIA PLATFORM POOF
|
||||
; db $00 ; 0x3A - BIG BOMB EXPLOSION
|
||||
; db $10 ; 0x3B - SWORD UP SPARKLE
|
||||
; db $00 ; 0x3C - SPIN ATTACK CHARGE SPARKLE
|
||||
; db $00 ; 0x3D - ITEM SPLASH
|
||||
; db $08 ; 0x3E - RISING CRYSTAL
|
||||
; db $00 ; 0x3F - BUSH POOF
|
||||
; db $10 ; 0x40 - DWARF POOF
|
||||
; db $08 ; 0x41 - WATERFALL SPLASH
|
||||
; db $78 ; 0x42 - HAPPINESS POND RUPEES
|
||||
; db $80 ; 0x43 - GANONS TOWER CUTSCENE
|
||||
AnciType = $0C4A
|
||||
|
||||
AncillaInit_SetCoordsAndExit = $0980C3
|
||||
Ancilla_PrepOAMCoord_long = $08F6D9
|
||||
Ancilla_SetOAM_XY_long = $08F6FE
|
||||
Ancilla_GetRadialProjection_long = $08FB23
|
||||
|
||||
Ancilla_CheckForAvailableSlot = $0FF577
|
||||
Ancilla_CheckInitialTile_A = $099DD3
|
||||
|
||||
Ancilla_CheckSpriteCollision_long = $088DA2
|
||||
|
||||
Ancilla_CheckTileCollision_long = $08923B
|
||||
Ancilla_CheckTileCollision_Class2_long = $089243
|
||||
|
||||
Ancilla_CalculateSFXPan = $0DBB5E
|
||||
|
||||
Ancilla_CheckDamageToSprite = $06ECB7
|
||||
|
||||
; Table of ancilla properties
|
||||
AncillaObjectAllocation = $08806F
|
||||
|
||||
AncillaAdd_BombosSpell = $08AF66
|
||||
AncillaAdd_FireRodShot = $0880B3
|
||||
AncillaAdd_Snoring = $0980C8
|
||||
AncillaAdd_Bomb = $09811F
|
||||
AncillaAdd_Boomerang = $09820F
|
||||
AncillaAdd_BoomerangAsClink = $098345
|
||||
AncillaAdd_DugUpFlute = $098C73
|
||||
AncillaAdd_ChargedSpinAttackSparkle = $098CB1
|
||||
AncillaAdd_ExplodingWeatherVane = $098D11
|
||||
AncillaAdd_CutsceneDuck = $098D90
|
||||
AncillaAdd_SomariaPlatformPoof = $098DD2
|
||||
AncillaAdd_SuperBombExplosion = $098DF9
|
||||
|
||||
ConfigureRevivalAncillae = $098E4E
|
||||
|
||||
AncillaAdd_CaneOfByrnaInitSpark = $098EE0
|
||||
AncillaAdd_LampFlame = $098F1C
|
||||
AncillaAdd_ShovelDirt = $098F5B
|
||||
AncillaAdd_BlastWallFireball = $099031
|
||||
AncillaAdd_Arrow = $0990A4
|
||||
AncillaAdd_CapePoof = $09912C
|
||||
AncillaAdd_BushPoof = $0991C3
|
||||
AncillaAdd_EtherSpell = $0991FC
|
||||
AncillaAdd_VictorySpin = $0992AC
|
||||
AncillaAdd_MagicPowder = $0992F0
|
||||
AncillaAdd_WallTapSpark = $099395
|
||||
AncillaAdd_SwordSwingSparkle = $0993C2
|
||||
AncillaAdd_QuakeSpell = $099589
|
||||
AncillaAdd_IceRodShot = $099863
|
||||
AncillaAdd_Splash = $0998FC
|
||||
AncillaAdd_Hookshot = $099B10
|
||||
AncillaAdd_Blanket = $098091
|
||||
|
||||
AddHappinessPondRupees = $098AE0
|
||||
|
||||
DeleteBoomAndByrnaSparks = $0FFD86
|
||||
|
||||
Sparkle_PrepOAMFromRadial = $08DA17
|
||||
|
||||
Fireball_SpawnTrailGarnish = $09B020
|
||||
|
||||
SpriteSFX_QueueSFX2WithPan = $0DBB7C
|
||||
SpriteSFX_QueueSFX3WithPan = $0DBB8A
|
||||
415
Core/tables.asm
415
Core/tables.asm
@@ -1,415 +0,0 @@
|
||||
; Main Modules
|
||||
Module00_Intro
|
||||
Module01_FileSelect
|
||||
Module02_CopyFile
|
||||
Module03_KILLFile
|
||||
Module04_NameFile
|
||||
Module05_LoadFile
|
||||
Module06_UnderworldLoad
|
||||
Module07_Underworld
|
||||
Module08_OverworldLoad
|
||||
Module09_Overworld
|
||||
Module0A_OverworldSpecialLoad
|
||||
Module0B_OverworldSpecial
|
||||
Module0C_Unused
|
||||
Module0D_Unused
|
||||
Module0E_Interface
|
||||
Module0F_SpotlightClose
|
||||
Module10_SpotlightOpen
|
||||
Module11_UnderworldFallingEntrance
|
||||
Module12_GameOver
|
||||
Module13_BossVictory_Pendant
|
||||
Module14_Attract
|
||||
Module15_MirrorWarpFromAga
|
||||
Module16_BossVictory_Crystal
|
||||
Module17_SaveAndQuit
|
||||
Module18_GanonEmerges
|
||||
Module19_TriforceRoom
|
||||
Module1A_Credits
|
||||
Module1B_SpawnSelect
|
||||
|
||||
; Garnish IDs
|
||||
Garnish01_FireSnakeTail
|
||||
Garnish02_MothulaBeamTrail
|
||||
Garnish03_FallingTile
|
||||
Garnish04_LaserTrail
|
||||
Garnish05_SimpleSparkle
|
||||
Garnish06_ZoroTrail
|
||||
Garnish07_BabasuFlash
|
||||
Garnish08_KholdstareTrail
|
||||
Garnish09_LightningTrail
|
||||
Garnish0A_CannonSmoke
|
||||
Garnish0B_WaterTrail
|
||||
Garnish0C_TrinexxIceBreath
|
||||
$0000
|
||||
Garnish0E_TrinexxFireBreath
|
||||
Garnish0F_BlindLaserTrail
|
||||
Garnish10_GanonBatFlame
|
||||
Garnish11_WitheringGanonBatFlame
|
||||
Garnish12_Sparkle
|
||||
Garnish13_PyramidDebris
|
||||
Garnish14_KakKidDashDust
|
||||
Garnish15_ArrghusSplash
|
||||
Garnish16_ThrownItemDebris
|
||||
|
||||
|
||||
Module09_Overworld:
|
||||
Module09_00_PlayerControl ; 0x00
|
||||
Module09_LoadAuxGFX ; 0x01
|
||||
Module09_TriggerTilemapUpdate ; 0x02
|
||||
Module09_LoadNewMapAndGFX ; 0x03
|
||||
Module09_LoadNewSprites ; 0x04
|
||||
Overworld_StartScrollTransition ; 0x05
|
||||
Overworld_RunScrollTransition ; 0x06
|
||||
Overworld_EaseOffScrollTransition ; 0x07
|
||||
Overworld_FinalizeEntryOntoScreen ; 0x08
|
||||
Module09_09_OpenBigDoorFromExiting ; 0x09
|
||||
Module09_0A_WalkFromExiting_FacingDown ; 0x0A
|
||||
Module09_0B_WalkFromExiting_FacingUp ; 0x0B
|
||||
Module09_0C_OpenBigDoor ; 0x0C
|
||||
Overworld_StartMosaicTransition ; 0x0D
|
||||
Overworld_LoadSubscreenAndSilenceSFX1 ; 0x0E
|
||||
Module09_LoadAuxGFX ; 0x0F
|
||||
Module09_TriggerTilemapUpdate ; 0x10
|
||||
Module09_LoadNewMapAndGFX ; 0x11
|
||||
Module09_LoadNewSprites ; 0x12
|
||||
Overworld_StartScrollTransition ; 0x13
|
||||
Overworld_RunScrollTransition ; 0x14
|
||||
Overworld_EaseOffScrollTransition ; 0x15
|
||||
Module09_FadeBackInFromMosaic ; 0x16
|
||||
Overworld_StartMosaicTransition ; 0x17
|
||||
Module09_18 ; 0x18
|
||||
Module09_19 ; 0x19
|
||||
Module09_LoadAuxGFX ; 0x1A
|
||||
Module09_TriggerTilemapUpdate ; 0x1B
|
||||
Module09_1C ; 0x1C
|
||||
Module09_1D ; 0x1D
|
||||
Module09_1E ; 0x1E
|
||||
Module09_1F ; 0x1F
|
||||
Overworld_ReloadSubscreenOverlay ; 0x20
|
||||
Module09_21 ; 0x21
|
||||
Module09_22 ; 0x22
|
||||
Module09_MirrorWarp ; 0x23
|
||||
Overworld_StartMosaicTransition ; 0x24
|
||||
Module09_25 ; 0x25
|
||||
Module09_LoadAuxGFX ; 0x26
|
||||
Module09_TriggerTilemapUpdate ; 0x27
|
||||
Overworld_LoadAndBuildScreen ; 0x28
|
||||
Module09_FadeBackInFromMosaic ; 0x29
|
||||
Module09_2A_RecoverFromDrowning ; 0x2A
|
||||
Module09_2B ; 0x2B
|
||||
Module09_MirrorWarp ; 0x2C
|
||||
Module09_2D_WaitForBird ; 0x2D
|
||||
Module09_2E_Whirlpool ; 0x2E
|
||||
Module09_2F
|
||||
|
||||
|
||||
Module07_Underworld:
|
||||
Module07_00_PlayerControl ; 0x00
|
||||
Module07_01_IntraroomTransition ; 0x01
|
||||
Module07_02_InterroomTransition ; 0x02
|
||||
Module07_03_OverlayChange ; 0x03
|
||||
Module07_04_UnlockDoor ; 0x04
|
||||
Module07_05_ControlShutters ; 0x05
|
||||
Module07_06_FatInterRoomStairs ; 0x06
|
||||
Module07_07_FallingTransition ; 0x07
|
||||
Module07_08_NorthIntraRoomStairs ; 0x08
|
||||
Module07_09_OpenCrackedDoor ; 0x09
|
||||
Module07_0A_ChangeBrightness ; 0x0A
|
||||
Module07_0B_DrainSwampPool ; 0x0B
|
||||
Module07_0C_FloodSwampWater ; 0x0C
|
||||
Module07_0D_FloodDam ; 0x0D
|
||||
Module07_0E_SpiralStairs ; 0x0E
|
||||
Module07_0F_LandingWipe ; 0x0F
|
||||
Module07_10_SouthIntraRoomStairs ; 0x10
|
||||
Module07_11_StraightInterroomStairs ; 0x11
|
||||
Module07_11_StraightInterroomStairs ; 0x12
|
||||
Module07_11_StraightInterroomStairs ; 0x13
|
||||
Module07_14_RecoverFromFall ; 0x14
|
||||
Module07_15_WarpPad ; 0x15
|
||||
Module07_16_UpdatePegs ; 0x16
|
||||
Module07_17_PressurePlate ; 0x17
|
||||
Module07_18_RescuedMaiden ; 0x18
|
||||
Module07_19_MirrorFade ; 0x19
|
||||
Module07_1A_RoomDraw_OpenTriforceDoor_bounce ; 0x1A
|
||||
|
||||
|
||||
Link_ControlHandler:
|
||||
LinkState_Default ; 0x00
|
||||
LinkState_Pits ; 0x01
|
||||
LinkState_Recoil ; 0x02
|
||||
LinkState_SpinAttack ; 0x03
|
||||
LinkState_Swimming ; 0x04
|
||||
LinkState_OnIce ; 0x05
|
||||
LinkState_Recoil ; 0x06
|
||||
LinkState_Zapped ; 0x07
|
||||
LinkState_UsingEther ; 0x08
|
||||
LinkState_UsingBombos ; 0x09
|
||||
LinkState_UsingQuake ; 0x0A - DekuHover
|
||||
LinkState_HoppingSouthOW ; 0x0B
|
||||
LinkState_HoppingHorizontallyOW ; 0x0C
|
||||
LinkState_HoppingDiagonallyUpOW ; 0x0D
|
||||
LinkState_HoppingDiagonallyDownOW ; 0x0E
|
||||
LinkState_0F ; 0x0F
|
||||
LinkState_0F ; 0x10
|
||||
LinkState_Dashing ; 0x11
|
||||
LinkState_ExitingDash ; 0x12
|
||||
LinkState_Hookshotting ; 0x13
|
||||
LinkState_CrossingWorlds ; 0x14
|
||||
LinkState_ShowingOffItem ; 0x15
|
||||
LinkState_Sleeping ; 0x16
|
||||
LinkState_Bunny ; 0x17
|
||||
LinkState_HoldingBigRock ; 0x18
|
||||
LinkState_ReceivingEther ; 0x19
|
||||
LinkState_ReceivingBombos ; 0x1A
|
||||
LinkState_ReadingDesertTablet ; 0x1B
|
||||
LinkState_TemporaryBunny ; 0x1C
|
||||
LinkState_TreePull ; 0x1D
|
||||
LinkState_SpinAttack ; 0x1E
|
||||
|
||||
|
||||
Link_HandleYItem:
|
||||
LinkItem_Bombs
|
||||
LinkItem_Boomerang
|
||||
LinkItem_Bow
|
||||
LinkItem_Hammer
|
||||
|
||||
LinkItem_Rod
|
||||
LinkItem_Rod
|
||||
LinkItem_Net
|
||||
LinkItem_ShovelAndFlute
|
||||
|
||||
LinkItem_Lamp
|
||||
LinkItem_Powder
|
||||
LinkItem_Bottle
|
||||
LinkItem_Book
|
||||
|
||||
LinkItem_CaneOfByrna
|
||||
LinkItem_Hookshot
|
||||
LinkItem_Bombos
|
||||
LinkItem_Ether
|
||||
|
||||
LinkItem_Quake
|
||||
LinkItem_CaneOfSomaria
|
||||
LinkItem_Cape
|
||||
LinkItem_Mirror
|
||||
|
||||
|
||||
; Liftable object palettes
|
||||
; Sprites Aux 2 #8 for DW
|
||||
; Sprites Aux 2 #6 for LW
|
||||
; #7 and #9 are the yellow bush palettes
|
||||
|
||||
|
||||
OverworldPaletteSet:
|
||||
db $00, $FF, $07, $FF ; 0x00
|
||||
db $00, $01, $07, $FF ; 0x01
|
||||
db $00, $02, $07, $FF ; 0x02
|
||||
db $00, $03, $07, $FF ; 0x03
|
||||
db $00, $04, $07, $FF ; 0x04
|
||||
db $00, $05, $07, $FF ; 0x05
|
||||
db $00, $06, $07, $FF ; 0x06
|
||||
db $07, $06, $05, $FF ; 0x07
|
||||
db $00, $08, $07, $FF ; 0x08
|
||||
db $00, $09, $07, $FF ; 0x09
|
||||
db $00, $0A, $07, $FF ; 0x0A
|
||||
db $00, $0B, $07, $FF ; 0x0B
|
||||
db $00, $FF, $07, $FF ; 0x0C
|
||||
db $00, $FF, $07, $FF ; 0x0D
|
||||
db $03, $04, $07, $FF ; 0x0E
|
||||
db $04, $04, $03, $FF ; 0x0F
|
||||
db $10, $FF, $06, $FF ; 0x10
|
||||
db $10, $01, $06, $FF ; 0x11
|
||||
db $10, $11, $06, $FF ; 0x12
|
||||
db $10, $03, $06, $FF ; 0x13
|
||||
db $10, $04, $06, $FF ; 0x14
|
||||
db $10, $05, $06, $FF ; 0x15
|
||||
db $10, $06, $06, $FF ; 0x16
|
||||
db $12, $13, $04, $FF ; 0x17
|
||||
db $12, $05, $04, $FF ; 0x18
|
||||
db $10, $09, $06, $FF ; 0x19
|
||||
db $10, $0B, $06, $FF ; 0x1A
|
||||
db $10, $0C, $06, $FF ; 0x1B
|
||||
db $10, $0D, $06, $FF ; 0x1C
|
||||
db $10, $0E, $06, $FF ; 0x1D
|
||||
db $10, $0F, $06, $FF ; 0x1E
|
||||
|
||||
|
||||
LDA.l UnderworldPaletteSets+0,X
|
||||
STA.w $0AB6 ; PALBG
|
||||
|
||||
LDA.l UnderworldPaletteSets+1,X
|
||||
STA.w $0AAC ; PALSPR0
|
||||
|
||||
LDA.l UnderworldPaletteSets+2,X
|
||||
STA.w $0AAD ; PALSPR1
|
||||
|
||||
LDA.l UnderworldPaletteSets+3,X
|
||||
STA.w $0AAE ; PALSPR2
|
||||
|
||||
; PALBG
|
||||
; 0x00 - Kalyxo Castle
|
||||
; 0x01 - Blue
|
||||
; 0x02 - House
|
||||
; 0x03 - Green
|
||||
; 0x04 - Glacia Estate Ice
|
||||
; 0x05 - Zora Temple
|
||||
; 0x06 - Tail Palace Pink
|
||||
; 0x07 - Goron Mines Cave Red
|
||||
; 0x08 - Mushroom Grotto Gray
|
||||
; 0x09
|
||||
; 0x0A (10) - Ranch Pink
|
||||
; 0x0B (11) - Another green
|
||||
; 0x0C - Goron Mines Cave Red
|
||||
; 0x0D
|
||||
; 0x0E
|
||||
; 0x0F
|
||||
; 0x10 -
|
||||
; 0x (19) -
|
||||
|
||||
UnderworldPaletteSets:
|
||||
db $00, $00, $03, $01 ; 0x00
|
||||
db $02, $00, $03, $01 ; 0x01
|
||||
db $04, $00, $0A, $01 ; 0x02 House
|
||||
db $06, $00, $01, $07 ; 0x03 Fortress of Secrets
|
||||
db $0A, $02, $02, $07 ; 0x04 Zora Temple
|
||||
db $04, $04, $03, $0A ; 0x05 House
|
||||
db $0C, $05, $08, $14 ; 0x06 Tail Palace
|
||||
db $0E, $00, $03, $0A ; 0x07 Goron Mines/Caves
|
||||
db $02, $00, $0F, $14 ; 0x08 Castle Basement
|
||||
db $0A, $02, $00, $07 ; 0x09
|
||||
db $02, $00, $0F, $0C ; 0x0A
|
||||
db $06, $00, $06, $07 ; 0x0B
|
||||
db $00, $00, $0E, $12 ; 0x0C Kalyxo Castle
|
||||
db $12, $05, $05, $0B ; 0x0D
|
||||
db $12, $00, $02, $0C ; 0x0E
|
||||
db $10, $05, $0A, $07 ; 0x0F Mushroom Grotto
|
||||
db $10, $00, $10, $0C ; 0x10 Ranch?
|
||||
db $16, $07, $02, $07 ; 0x11 Hall of Secrets
|
||||
db $16, $00, $07, $0F ; 0x12
|
||||
db $08, $00, $04, $0C ; 0x13 Glacia Estate
|
||||
db $08, $00, $04, $09 ; 0x14
|
||||
db $04, $00, $03, $01 ; 0x15 House
|
||||
db $14, $00, $04, $04 ; 0x16
|
||||
db $14, $00, $14, $0C ; 0x17
|
||||
db $18, $05, $07, $0B ; 0x18 Lava Lands Cave/Turtle Rock
|
||||
db $18, $06, $10, $0C ; 0x19
|
||||
db $1A, $05, $08, $14 ; 0x1A Dragon Ship
|
||||
db $1A, $02, $00, $07 ; 0x1B Dragon Ship
|
||||
db $06, $00, $03, $0A ; 0x1C
|
||||
db $1C, $00, $03, $01 ; 0x1D
|
||||
db $1E, $00, $0B, $11 ; 0x1E Swordsmith
|
||||
db $04, $00, $0B, $11 ; 0x1F
|
||||
db $0E, $00, $00, $02 ; 0x20
|
||||
db $20, $08, $13, $0D ; 0x21 Ganondorf Boss
|
||||
db $0A, $00, $03, $0A ; 0x22 Zora Temple
|
||||
db $14, $00, $04, $04 ; 0x23
|
||||
db $1A, $02, $02, $07 ; 0x24 Dragon Ship
|
||||
db $1A, $0A, $00, $00 ; 0x25 Dragon Ship
|
||||
db $00, $00, $03, $02 ; 0x26
|
||||
db $0E, $00, $03, $07 ; 0x27
|
||||
db $1A, $05, $05, $0B ; 0x28 Dragon Ship
|
||||
|
||||
PrizePackRarities:
|
||||
{
|
||||
db $01 ; pack 1 : 50%
|
||||
db $01 ; pack 2 : 50%
|
||||
db $01 ; pack 3 : 50%
|
||||
db $00 ; pack 4 : 100%
|
||||
db $01 ; pack 5 : 50%
|
||||
db $01 ; pack 6 : 50%
|
||||
db $01 ; pack 7 : 50%
|
||||
db $00 ; pack 8 : 100% - doesn't exist
|
||||
}
|
||||
|
||||
ItemDropBounceProps:
|
||||
{
|
||||
db $24 ; GREEN RUPEE
|
||||
db $24 ; BLUE RUPEE
|
||||
db $24 ; RED RUPEE
|
||||
db $20 ; BOMB REFILL 1
|
||||
db $20 ; BOMB REFILL 4
|
||||
db $20 ; BOMB REFILL 8
|
||||
db $24 ; SMALL MAGIC DECANTER
|
||||
db $24 ; LARGE MAGIC DECANTER
|
||||
db $24 ; ARROW REFILL 5
|
||||
db $24 ; ARROW REFILL 10
|
||||
db $00 ; FAIRY
|
||||
db $24 ; SMALL KEY
|
||||
db $20 ; BIG KEY
|
||||
db $20 ; STOLEN SHIELD
|
||||
}
|
||||
|
||||
PrizePackPrizes:
|
||||
{
|
||||
.pack_1
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $D9 ; SPRITE D9 - green rupee
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $D9 ; SPRITE D9 - green rupee
|
||||
|
||||
.pack_2
|
||||
db $DA ; SPRITE DA - blue rupee
|
||||
db $D9 ; SPRITE D9 - green rupee
|
||||
db $DA ; SPRITE DA - blue rupee
|
||||
db $DB ; SPRITE DB - red rupee
|
||||
db $DA ; SPRITE DA - blue rupee
|
||||
db $D9 ; SPRITE D9 - green rupee
|
||||
db $DA ; SPRITE DA - blue rupee
|
||||
db $DA ; SPRITE DA - blue rupee
|
||||
|
||||
.pack_3
|
||||
db $E0 ; SPRITE E0 - full magic
|
||||
db $DF ; SPRITE DF - small magic
|
||||
db $DF ; SPRITE DF - small magic
|
||||
db $DA ; SPRITE DA - blue rupee
|
||||
db $E0 ; SPRITE E0 - full magic
|
||||
db $DF ; SPRITE DF - small magic
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $DF ; SPRITE DF - small magic
|
||||
|
||||
.pack_4
|
||||
db $DC ; SPRITE DC - 1 bomb
|
||||
db $DC ; SPRITE DC - 1 bomb
|
||||
db $DC ; SPRITE DC - 1 bomb
|
||||
db $DD ; SPRITE DD - 4 bombs
|
||||
db $DC ; SPRITE DC - 1 bomb
|
||||
db $DC ; SPRITE DC - 1 bomb
|
||||
db $DE ; SPRITE DE - 8 bombs
|
||||
db $DC ; SPRITE DC - 1 bomb
|
||||
|
||||
.pack_5
|
||||
db $E1 ; SPRITE E1 - 5 arrows
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $E1 ; SPRITE E1 - 5 arrows
|
||||
db $E2 ; SPRITE E2 - 10 arrows
|
||||
db $E1 ; SPRITE E1 - 5 arrows
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $E1 ; SPRITE E1 - 5 arrows
|
||||
db $E2 ; SPRITE E2 - 10 arrows
|
||||
|
||||
.pack_6
|
||||
db $DF ; SPRITE DF - small magic
|
||||
db $D9 ; SPRITE D9 - green rupee
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $E1 ; SPRITE E1 - 5 arrows
|
||||
db $DF ; SPRITE DF - small magic
|
||||
db $DC ; SPRITE DC - 1 bomb
|
||||
db $D9 ; SPRITE D9 - green rupee
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
|
||||
.pack_7
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $E3 ; SPRITE E3 - fairy
|
||||
db $E0 ; SPRITE E0 - full magic
|
||||
db $DB ; SPRITE DB - red rupee
|
||||
db $DE ; SPRITE DE - 8 bombs
|
||||
db $D8 ; SPRITE D8 - heart
|
||||
db $DB ; SPRITE DB - red rupee
|
||||
db $E2 ; SPRITE E2 - 10 arrows
|
||||
}
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
# Bank $07: Core Player (Link) Engine Analysis
|
||||
|
||||
**File**: `ALTTP/bank_07.asm`
|
||||
**Address Range**: `$078000` - `$07FFFF`
|
||||
|
||||
This bank is dedicated entirely to the player character, Link. It contains his core state machine, which governs everything from movement and physics to item usage and interaction with the world. It is executed every frame that the player has control and is not in a cutscene.
|
||||
|
||||
---
|
||||
|
||||
### 1. Main Entry Point: `Link_Main`
|
||||
|
||||
* **Routine**: `Link_Main` (`#_078000`)
|
||||
* **Purpose**: This is the top-level function for all player-related code, called once per frame from the main game loop when the game is in a playable state (e.g., Overworld or Underworld).
|
||||
* **Functionality**:
|
||||
1. It first checks if the player is in a state that prevents control (e.g., a cutscene, indicated by `$02E4` being non-zero).
|
||||
2. If the player has control, it calls `Link_ControlHandler`, which is the heart of the player engine.
|
||||
3. After the main handler runs, it calls `HandleSomariaAndGraves` to process interactions with those specific objects, which need to be checked every frame.
|
||||
|
||||
---
|
||||
|
||||
### 2. The Player State Machine: `Link_ControlHandler`
|
||||
|
||||
* **Routine**: `Link_ControlHandler` (`#_07807F`)
|
||||
* **Purpose**: This function acts as a state machine dispatcher. It reads Link's current state from a single, critical WRAM variable and jumps to the appropriate logic handler for that state.
|
||||
* **Critical WRAM Variable**: `$7E005D` (`LINKDO`) - This byte holds Link's current state ID. Modifying this value directly forces Link into a different state.
|
||||
|
||||
* **Execution Flow**:
|
||||
1. **Damage Check**: Before any other action, the handler checks if Link has taken damage (`$7E0373`, `HURTME`). If so, it processes the damage, checks for the Magic Cape (`$7E0055`), reduces health, and can trigger a state change to `LinkState_Recoil` or the death sequence.
|
||||
2. **State Dispatch**: It reads the value of `LINKDO`, multiplies it by two (since each entry is a 2-byte address), and uses it as an index into the `.vectors` jump table (`#_078041`). It then performs a `JMP` to the corresponding state handler routine.
|
||||
|
||||
---
|
||||
|
||||
### 3. Link State Vector Table
|
||||
|
||||
This table at `#_078041` defines all 31 possible states for Link. Understanding this is key to modifying player behavior.
|
||||
|
||||
| State ID | Label (`#_07....`) | Description |
|
||||
|:---:|---|---|
|
||||
| `0x00` | `LinkState_Default` | The normal on-foot state for walking, standing, and most basic interactions. |
|
||||
| `0x01` | `LinkState_Pits` | Handles the logic for falling into a pit. |
|
||||
| `0x02` | `LinkState_Recoil` | Handles being knocked back by an enemy or obstacle. |
|
||||
| `0x03` | `LinkState_SpinAttack` | Manages the spin attack animation and hitbox. |
|
||||
| `0x04` | `LinkState_Swimming` | The state for swimming in water. |
|
||||
| `0x05` | `LinkState_OnIce` | Handles movement physics for icy surfaces. |
|
||||
| `0x06` | `LinkState_Recoil` | A duplicate pointer to the recoil state, likely for a different impact type. |
|
||||
| `0x07` | `LinkState_Zapped` | A special recoil state for electrical damage. |
|
||||
| `0x08` | `LinkState_UsingEther` | Handles the animation and logic for using the Ether medallion. |
|
||||
| `0x09` | `LinkState_UsingBombos` | Handles the animation and logic for using the Bombos medallion. |
|
||||
| `0x0A` | `LinkState_UsingQuake` | Handles the animation and logic for using the Quake medallion. |
|
||||
| `0x0B` | `LinkState_HoppingSouthOW` | Manages the multi-frame action of hopping off a ledge to the south. |
|
||||
| `0x0C` | `LinkState_HoppingHorizontallyOW` | Manages hopping off a ledge to the east or west. |
|
||||
| `0x0D` | `LinkState_HoppingDiagonallyUpOW` | Manages hopping off a ledge diagonally up-left or up-right. |
|
||||
| `0x0E` | `LinkState_HoppingDiagonallyDownOW`| Manages hopping off a ledge diagonally down-left or down-right. |
|
||||
| `0x0F` | `LinkState_0F` | A generic ledge-hopping state. |
|
||||
| `0x10` | `LinkState_0F` | (Duplicate) A generic ledge-hopping state. |
|
||||
| `0x11` | `LinkState_Dashing` | The state for running with the Pegasus Boots. |
|
||||
| `0x12` | `LinkState_ExitingDash` | The brief turn-around animation after a dash collides with a wall. |
|
||||
| `0x13` | `LinkState_Hookshotting` | Manages Link's state while the hookshot is extended. |
|
||||
| `0x14`| `LinkState_CrossingWorlds` | Handles the Magic Mirror animation and world transition. |
|
||||
| `0x15` | `LinkState_ShowingOffItem` | The "item get" pose when Link holds an item above his head. |
|
||||
| `0x16` | `LinkState_Sleeping` | For the beginning of the game when Link is in bed. |
|
||||
| `0x17` | `LinkState_Bunny` | The state for when Link is transformed into a bunny in the Dark World. |
|
||||
| `0x18` | `LinkState_HoldingBigRock` | The state for lifting a heavy, dark-colored rock (requires Titan's Mitt). |
|
||||
| `0x19` | `LinkState_ReceivingEther` | The cutscene for receiving the Ether medallion from the tablet. |
|
||||
| `0x1A` | `LinkState_ReceivingBombos` | The cutscene for receiving the Bombos medallion from the tablet. |
|
||||
| `0x1B` | `LinkState_ReadingDesertTablet` | The cutscene for reading the Desert Palace tablet. |
|
||||
| `0x1C` | `LinkState_TemporaryBunny` | The brief bunny transformation sequence when entering the Dark World. |
|
||||
| `0x1D` | `LinkState_TreePull` | The state for pulling on the tree in the haunted grove for the race game. |
|
||||
| `0x1E` | `LinkState_SpinAttack` | (Duplicate) A second entry for the spin attack state. |
|
||||
|
||||
---
|
||||
|
||||
### 4. Analysis of Core States
|
||||
|
||||
#### `LinkState_Default` (`#_078109`)
|
||||
|
||||
This is the most complex state and serves as the foundation for player control. It is a large routine that dispatches to numerous sub-handlers.
|
||||
|
||||
* **Initial Checks**:
|
||||
* `Link_HandleBunnyTransformation`: Checks if Link should transform into a bunny.
|
||||
* Checks for recoil/damage (`$7E004D`) and branches to a simplified physics handler if necessary.
|
||||
* **Action Dispatching**: If not recoiling, it checks for player input and calls the appropriate action handler.
|
||||
* `Link_HandleToss`: Checks if Link is throwing a carried object.
|
||||
* `Link_HandleAPress`: Handles context-sensitive actions for the A button (talk, read, lift, open, dash).
|
||||
* `Link_HandleYItem`: Manages using the currently selected item (bow, boomerang, rods, etc.).
|
||||
* `Link_HandleSwordCooldown`: Manages sword swings and charging a spin attack.
|
||||
* **Physics and Collision**: If no other action is taken, it processes movement.
|
||||
* `ResetAllAcceleration`: Clears speed values if Link is standing still.
|
||||
* `Link_HandleDiagonalCollision` & `Link_HandleCardinalCollision`: Check for collisions with walls and objects.
|
||||
* `JSL Link_HandleVelocity`: The main physics engine. Applies acceleration, deceleration, and the final movement vector to Link's coordinates.
|
||||
* **Animation & Camera**:
|
||||
* `JSL Link_HandleMovingAnimation_FullLongEntry`: Updates Link's sprite graphics based on his direction and action.
|
||||
* `HandleIndoorCameraAndDoors`: Manages camera scrolling and door transitions indoors.
|
||||
|
||||
#### `LinkState_Recoil` (`#_0786B5`)
|
||||
|
||||
This state demonstrates how control is temporarily taken from the player.
|
||||
|
||||
* **Z-Axis Movement**: It uses `$7E0024` (Link's Z-position) and `$7E0029` (knockback Z-velocity) to handle Link being knocked into the air and falling back down.
|
||||
* **Timer-Based**: The duration of the recoil is controlled by a countdown timer in `$7E0046` (`INPAIN`). Once the timer reaches zero, Link's state is typically returned to `LinkState_Default`.
|
||||
* **Collision & Landing**: While in recoil, it still checks for collisions. It also has special checks for landing, such as `Link_SplashUponLanding` if he falls into water, which can change his state to `LinkState_Swimming`.
|
||||
|
||||
#### `LinkState_Bunny` (`#_0783A1`)
|
||||
|
||||
This state shows a persistent change in abilities.
|
||||
|
||||
* **Simplified Controls**: The bunny state has a much simpler control handler. It still allows for movement but disables all item and sword usage.
|
||||
* **State Check**: It constantly checks for the condition that allows Link to transform back: the presence of the Moon Pearl (`$7EF357`). If the pearl is obtained or Link leaves the Dark World, it triggers the transformation back to the default state.
|
||||
|
||||
---
|
||||
|
||||
### 5. Key WRAM Variables for Link
|
||||
|
||||
This bank relies on a large number of WRAM addresses to function. Understanding these is critical to debugging or modifying player logic.
|
||||
|
||||
| Address | Label | Description |
|
||||
|:---:|---|---|
|
||||
| `$7E005D` | `LINKDO` | **Link's State**: The primary state ID, used as an index for the state machine. |
|
||||
| `$7E0020/21`| `POSY` | Link's 16-bit Y-coordinate. |
|
||||
| `$7E0022/23`| `POSX` | Link's 16-bit X-coordinate. |
|
||||
| `$7E0024` | `POSZ` | Link's 8-bit Z-coordinate (height). |
|
||||
| `$7E0026` | - | Link's facing direction. |
|
||||
| `$7E0027/28`| - | Link's Y/X velocity. |
|
||||
| `$7E002A/2B`| - | Link's Y/X sub-pixel position. |
|
||||
| `$7E003A` | - | Action flags (bitfield for sword charging, etc.). |
|
||||
| `$7E0046` | `INPAIN` | Recoil/invincibility timer after taking damage. |
|
||||
| `$7E004D` | - | A flag indicating Link is in a recoil state. |
|
||||
| `$7E0303` | - | The ID of the currently selected Y-button item. |
|
||||
| `$7E0373` | `HURTME` | Damage value to be applied to Link on the next frame. |
|
||||
| `$7E037B` | - | A flag that temporarily disables taking damage. |
|
||||
| `$7E0372` | - | A flag indicating Link is currently dashing. |
|
||||
@@ -1,181 +0,0 @@
|
||||
# Memory Map
|
||||
|
||||
This document provides a detailed map of the WRAM and SRAM memory regions, serv| `$7EF358` | `WolfMask` | Flag indicating if the player has obtained the Wolf Mask. |
|
||||
|
||||
### Repurposed Vanilla SRAM Blocks
|
||||
|
||||
The following blocks were marked "unused" in vanilla ALTTP but are now utilized for OOS custom data:
|
||||
|
||||
#### **Block $7EF38A-$7EF3C4** - Collectibles & Custom Progression
|
||||
|
||||
*This is a large block of vanilla unused SRAM now used for various collectibles, side-quests, and tracking systems.*
|
||||
|
||||
| Address | Label | Description | Verified |
|
||||
|----------|---------------------|--------------------------------------------------------------------------|----------|
|
||||
| `$7EF38A` | `FishingRod` | Flag indicating if the player has the Fishing Rod. | ✓ |
|
||||
| `$7EF38B` | `Bananas` | Number of bananas collected for side-quest. | ✓ |
|
||||
| `$7EF38D` | `Pineapples` | Number of pineapples collected. | ✓ |
|
||||
| `$7EF38F` | `RockMeat` | Number of rock meat items collected. | ✓ |
|
||||
| `$7EF391` | `Seashells` | Number of secret seashells collected. | ✓ |
|
||||
| `$7EF393` | `Honeycomb` | Number of honeycombs collected. | ✓ |
|
||||
| `$7EF395` | `DekuSticks` | Number of Deku sticks collected. | ✓ |
|
||||
| `$7EF396` | `TingleMaps` | Tingle map collection tracking. | ✓ |
|
||||
| `$7EF397` | `TingleId` | Tingle identification value. | ✓ |
|
||||
| `$7EF398` | `Scrolls` | Bitfield tracking lore scroll collection (7 dungeons: `.dgi zktm`). | ✓ |
|
||||
| `$7EF39A` | `PrevScroll` | Tracks the previous scroll for re-reading old hints. | ✓ |
|
||||
| `$7EF39B` | `MagicBeanProg` | Multi-day growth cycle tracking for magic bean side-quest (`.dts fwpb`). | ✓ |
|
||||
| `$7EF39C` | `JournalState` | Current state of the player's journal. | ✓ |
|
||||
| `$7EF39D` | `SRAM_StormsActive` | Flag indicating if the Song of Storms effect is active. | ✓ |
|
||||
| `$7EF39E` | `StoryState` | State machine for Link's House intro sequence (0-2). | ✓ |
|
||||
|
||||
#### **Block $7EF403-$7EF4FD** - Partially Repurposed
|
||||
|
||||
*Most of this block remains unused, but OOS utilizes a portion for the Dreams collectible system.*
|
||||
|
||||
| Address | Label | Description | Verified |
|
||||
|----------|-----------|--------------------------------------------------------------------|----------|
|
||||
| `$7EF410` | `Dreams` | Bitfield tracking collection of three Dreams (`.cpw`: Courage, Power, Wisdom). | ✓ |
|
||||
|
||||
> **💡 Usage Notes:**
|
||||
> - **Scrolls** (`$7EF398`): One scroll per dungeon (7 total). Bitfield format: `.dgi zktm` where each letter represents a dungeon (m=Mushroom Grotto, t=Tail Palace, k=Kalyxo Castle, z=Zora Temple, i=Glacia Estate, g=Goron Mines, d=Dragon Ship).
|
||||
> - **MagicBeanProg** (`$7EF39B`): Tracks multi-day growth cycle with bitfield `.dts fwpb` (b=bean planted, w=watered, p=pollinated, f=first day, s=second day, t=third day, d=done).
|
||||
> - **Dreams** (`$7EF410`): Similar to vanilla Pendants, tracks three key collectibles. Bitfield: `.cpw` (c=Courage, p=Power, w=Wisdom).
|
||||
|
||||
### SRAM Address Contradictions (Source File Notes)
|
||||
|
||||
The following addresses appear with both active label definitions and `UNUSED_` markers in `Core/sram.asm`. **The label definitions take precedence** - these addresses are actively used:
|
||||
|
||||
- `$7EF3D4` - Both `MakuTreeQuest` and `UNUSED_7EF3D4` (✓ **In Use**)
|
||||
- `$7EF3D6` - Both `OOSPROG` and `UNUSED_7EF3D6` (✓ **In Use**)
|
||||
- `$7EF38A` - Both `FishingRod` and `UNUSED_7EF38A` (✓ **In Use**)
|
||||
|
||||
These contradictions appear to be legacy comments from the vanilla ALTTP disassembly that were not removed when OOS repurposed these addresses.
|
||||
|
||||
as a central reference for understanding the game's state.
|
||||
|
||||
## 1. WRAM (Work RAM) - `$7E0000`
|
||||
|
||||
This section details the layout of the game's volatile memory.
|
||||
|
||||
### Key Vanilla WRAM Variables
|
||||
|
||||
*This section contains a table listing critical vanilla WRAM addresses, their labels (from `Core/ram.asm` and `Core/symbols.asm`), and their purpose.*
|
||||
|
||||
| Address | Label | Description |
|
||||
|----------|------------|---------------------------------------------------|
|
||||
| `$7E0010` | `MODE` | The main game state/module index. |
|
||||
| `$7E0011` | `SUBMODE` | The sub-state for the current game mode. |
|
||||
| `$7E001A` | `FRAME` | A counter that increments each non-lagging frame. |
|
||||
| `$7E001B` | `INDOORS` | A flag indicating if Link is indoors (0x01) or outdoors (0x00). |
|
||||
| `$7E002F` | `DIR` | The direction Link is facing (0=U, 2=D, 4=L, 6=R). |
|
||||
| `$7E005D` | `LINKDO` | Link's personal state machine ID (walking, swimming, etc.). |
|
||||
| `$7E008A` | `OWSCR` | The current Overworld screen ID. |
|
||||
| `$7E00A0` | `ROOM` | The current Underworld room ID. |
|
||||
| `$7E02E0` | `BUNNY` | A flag indicating if Link is in his bunny form (0x01). |
|
||||
| `$7E031F` | `IFRAMES` | Link's invincibility frame timer after taking damage. |
|
||||
| `$7E0DD0` | `SprState` | An array storing the state for each of the 16 sprites. |
|
||||
| `$7E0E20` | `SprType` | An array storing the ID for each of the 16 sprites. |
|
||||
| `$7E0E50` | `SprHealth`| An array storing the health for each of the 16 sprites. |
|
||||
|
||||
### Custom WRAM Region - `$7E0730+`
|
||||
|
||||
*This section details the custom WRAM area defined in `Core/ram.asm` and `Core/symbols.asm`. It explains the purpose of each custom variable.*
|
||||
|
||||
| Address | Label | Description |
|
||||
|----------|------------------------|--------------------------------------------------------------------------|
|
||||
| `$7E0730` | `MenuScrollLevelV` | Vertical scroll position for the menu. |
|
||||
| `$7E0731` | `MenuScrollLevelH` | Horizontal scroll position for the menu. |
|
||||
| `$7E0732` | `MenuScrollHDirection` | The direction of horizontal scrolling in the menu. |
|
||||
| `$7E0734` | `MenuItemValueSpoof` | Used to temporarily override the displayed value of a menu item. |
|
||||
| `$7E0736` | `ShortSpoof` | A shorter version of the spoof value. |
|
||||
| `$7E0737` | `MusicNoteValue` | The value of the current music note being played. |
|
||||
| `$7E0739` | `GoldstarOrHookshot` | Differentiates between the vanilla Hookshot and the custom Goldstar item. |
|
||||
| `$7E073A` | `Neck_Index` | Used for multi-part sprites, like a centipede body. |
|
||||
| `$7E0745` | `FishingOrPortalRod` | Differentiates between the Fishing Rod and the Portal Rod. |
|
||||
|
||||
---
|
||||
|
||||
## 2. SRAM (Save RAM) - `$7EF000`
|
||||
|
||||
This section details the layout of the save file memory.
|
||||
|
||||
> **🔍 SRAM Verification Note**: All custom SRAM variables documented below have been cross-referenced with `Core/sram.asm`. Some addresses in the source file have contradictory `UNUSED_` markers alongside actual label definitions (e.g., `OOSPROG = $7EF3D6` vs `UNUSED_7EF3D6 = $7EF3D6`). **The actual usage takes precedence** - if a label is defined for an address, it is considered in-use regardless of `UNUSED_` markers. This appears to be a legacy comment artifact from the vanilla ALTTP disassembly.
|
||||
|
||||
### Key Vanilla SRAM Variables
|
||||
|
||||
*This section lists key vanilla save data locations, such as inventory, health, and progression flags, as defined in `Core/sram.asm`.*
|
||||
|
||||
| Address | Label | Description |
|
||||
|----------|------------|-------------------------------------------|
|
||||
| `$7EF340` | `Bow` | The player's current bow type (0x00-0x04). |
|
||||
| `$7EF343` | `Bombs` | The number of bombs the player has. |
|
||||
| `$7EF359` | `Sword` | The player's current sword type (0x00-0x04). |
|
||||
| `$7EF35A` | `Shield` | The player's current shield type (0x00-0x03). |
|
||||
| `$7EF360` | `Rupees` | The player's current rupee count. |
|
||||
| `$7EF36C` | `MAXHP` | The player's maximum health (1 heart = 8 HP). |
|
||||
| `$7EF36D` | `CURHP` | The player's current health. |
|
||||
| `$7EF374` | `Pendants` | A bitfield for the collected pendants (Courage, Power, Wisdom). |
|
||||
| `$7EF37A` | `Crystals` | A bitfield for the collected crystals from Dark World dungeons. |
|
||||
| `$7EF3C5` | `GameState`| The main progression state of the game. |
|
||||
|
||||
### Custom SRAM Region
|
||||
|
||||
*This is a critical section. It provides a comprehensive breakdown of all custom variables added to the SRAM, explaining what each flag or value represents. This information is primarily found in `Core/sram.asm`.*
|
||||
|
||||
| Address | Label | Description |
|
||||
|----------|-------------------|--------------------------------------------------------------------------|
|
||||
| `$7EF3D6` | `OOSPROG` | A primary bitfield for major quest milestones unique to Oracle of Secrets. |
|
||||
| `$7EF3C6` | `OOSPROG2` | A secondary bitfield for less critical progression flags. |
|
||||
| `$7EF3D4` | `MakuTreeQuest` | A flag indicating if the Maku Tree has met Link. |
|
||||
| `$7EF3C7` | `MapIcon` | Controls the position of the guiding 'X' on the world map. |
|
||||
| `$7EF351` | `CustomRods` | A flag to differentiate between the Fishing Rod (1) and Portal Rod (2). |
|
||||
| `$7EF38A` | `FishingRod` | Flag indicating if the player has the Fishing Rod. |
|
||||
| `$7EF38B` | `Bananas` | The number of bananas collected for a side-quest. |
|
||||
| `$7EF391` | `Seashells` | The number of secret seashells collected. |
|
||||
| `$7EF398` | `Scrolls` | A bitfield tracking which of the lore scrolls have been found. |
|
||||
| `$7EF39B` | `MagicBeanProg` | Tracks the multi-day growth cycle of the magic bean side-quest. |
|
||||
| `$7EF39C` | `JournalState` | The current state of the player's journal. |
|
||||
| `$7EF39D` | `SRAM_StormsActive`| A flag indicating if the Song of Storms effect is active. |
|
||||
| `$7EF39E` | `StoryState` | State machine for Link's House intro sequence (0=Telepathy, 1=WakeUp, 2=End). |
|
||||
| `$7EF410` | `Dreams` | A bitfield tracking the collection of the three "Dreams" (Courage, Power, Wisdom). |
|
||||
| `$7EF347` | `ZoraMask` | Flag indicating if the player has obtained the Zora Mask. |
|
||||
| `$7EF348` | `BunnyHood` | Flag indicating if the player has obtained the Bunny Hood. |
|
||||
| `$7EF349` | `DekuMask` | Flag indicating if the player has obtained the Deku Mask. |
|
||||
| `$7EF34D` | `RocsFeather` | Flag indicating if the player has obtained Roc's Feather. |
|
||||
| `$7EF352` | `StoneMask` | Flag indicating if the player has obtained the Stone Mask. |
|
||||
| `$7EF358` | `WolfMask` | Flag indicating if the player has obtained the Wolf Mask. |
|
||||
|
||||
## 3. Custom Code and Data Layout (ROM Banks)
|
||||
|
||||
This section details the allocation of custom code and data within the ROM banks, as defined by `org` directives in the project's assembly files. The order of `incsrc` directives in `Oracle_main.asm` is crucial for the final ROM layout.
|
||||
|
||||
| Bank (Hex) | Address Range (PC) | Purpose / Contents |
|
||||
|------------|-----------------------|--------------------------------------------------------|
|
||||
| $20 | `$208000` - `$20FFFF` | Expanded Music |
|
||||
| $21-$27 | | ZScream Reserved |
|
||||
| $28 | `$288000` - `$28FFFF` | ZSCustomOverworld data and code |
|
||||
| $29-$2A | | ZScream Reserved |
|
||||
| $2B | `$2B8000` - `$2BFFFF` | Items |
|
||||
| $2C | `$2C8000` - `$2CFFFF` | Underworld/Dungeons |
|
||||
| $2D | `$2D8000` - `$2DFFFF` | Menu |
|
||||
| $2E | `$2E8000` - `$2EFFFF` | HUD |
|
||||
| $2F | `$2F8000` - `$2FFFFF` | Expanded Message Bank |
|
||||
| $30 | `$308000` - `$30FFFF` | Sprites |
|
||||
| $31 | `$318000` - `$31FFFF` | Sprites |
|
||||
| $32 | `$328000` - `$32FFFF` | Sprites |
|
||||
| $33 | `$338000` - `$33FFFF` | Moosh Form Gfx and Palette |
|
||||
| $34 | `$348000` - `$34FFFF` | Time System, Custom Overworld Overlays, Gfx |
|
||||
| $35 | `$358000` - `$35FFFF` | Deku Link Gfx and Palette |
|
||||
| $36 | `$368000` - `$36FFFF` | Zora Link Gfx and Palette |
|
||||
| $37 | `$378000` - `$37FFFF` | Bunny Link Gfx and Palette |
|
||||
| $38 | `$388000` - `$38FFFF` | Wolf Link Gfx and Palette |
|
||||
| $39 | `$398000` - `$39FFFF` | Minish Link Gfx |
|
||||
| $3A | `$3A8000` - `$3AFFFF` | Mask Routines, Custom Ancillae (Deku Bubble) |
|
||||
| $3B | `$3B8000` - `$3BFFFF` | GBC Link Gfx |
|
||||
| $3C | | Unused |
|
||||
| $3D | | ZS Tile16 |
|
||||
| $3E | | LW ZS Tile32 |
|
||||
| $3F | | DW ZS Tile32 |
|
||||
| $40 | `$408000` - `$40FFFF` | LW World Map |
|
||||
| $41 | `$418000` - `$41FFFF` | DW World Map |
|
||||
| Patches | Various | Targeted modifications within vanilla ROM addresses | `Core/patches.asm`, `Util/item_cheat.asm` |
|
||||
104
Docs/Core/Ram.md
104
Docs/Core/Ram.md
@@ -1,104 +0,0 @@
|
||||
# RAM Analysis: The Engine's State
|
||||
|
||||
This document provides a high-level analysis of how Work RAM (WRAM) and Save RAM (SRAM) are used to manage the game's state. For a raw list of addresses, see `Core/ram.asm` and `Core/sram.asm`.
|
||||
|
||||
## 1. The Core Game Loop: WRAM in Motion
|
||||
|
||||
The entire game is driven by a master state machine whose state is stored in a single WRAM variable:
|
||||
|
||||
- **`MODE` (`$7E0010`):** This is the game's primary state index. The main loop in `bank_00.asm` reads this value every frame and jumps to the corresponding module in the `Module_MainRouting` table.
|
||||
- **`SUBMODE` (`$7E0011`):** Many modules have their own internal state machines. This variable holds the sub-state for the current `MODE`.
|
||||
|
||||
This `MODE`/`SUBMODE` pattern is the fundamental driver of the game's flow. For example:
|
||||
- When Link opens the menu, the game sets `MODE` to `0x0E` (Interface), which gives control to the menu engine.
|
||||
- When Link talks to a character, `Interface_PrepAndDisplayMessage` is called, which saves the current game state to `MODECACHE` (`$7E010C`) and then sets `MODE` to `0x0E` to display the text box. When the dialogue is finished, the previous state is restored from the cache.
|
||||
- Transitioning between the overworld and underworld involves setting `MODE` to `0x08` (Overworld Load) or `0x06` (Underworld Load), respectively.
|
||||
|
||||
## 2. Defining the World: Location and Environment
|
||||
|
||||
The player's location and the properties of their environment are controlled by a handful of key WRAM variables.
|
||||
|
||||
- **`INDOORS` (`$7E001B`):** A simple but powerful flag (`0x01` for indoors, `0x00` for outdoors). This variable is checked by numerous systems to alter their behavior. For instance, the `ZSCustomOverworld` system reads this flag to determine whether to apply day/night palettes, and the audio engine uses it to select the appropriate music track.
|
||||
|
||||
- **`OWSCR` (`$7E008A`) and `ROOM` (`$7E00A0`):** These variables store the player's current location. `OWSCR` holds the Overworld screen ID, while `ROOM` holds the Underworld room ID.
|
||||
|
||||
The interaction between these variables is central to world traversal. When Link enters a cave on `OWSCR` 0x35, the following happens:
|
||||
1. The game looks up the entrance data for that tile in `Overworld/entrances.asm`.
|
||||
2. This data specifies the destination `ROOM` ID (e.g., 0x0104).
|
||||
3. The `INDOORS` flag is set to `0x01`.
|
||||
4. The main game `MODE` is set to `0x06` (Underworld Load).
|
||||
5. The dungeon engine in `bank_01.asm` takes over. It reads the `ROOM` ID and uses it to look up the room's header in `ALTTP/rooms.asm`. This header contains pointers to all the data needed to draw the room, including its layout, objects, and sprites.
|
||||
|
||||
## 3. Room-Specific Behavior
|
||||
|
||||
Once a room is loaded, its specific behavior is governed by tags and flags.
|
||||
|
||||
- **`TAG1`/`TAG2` (`$7E00AE`/`$AF`):** These are "Room Effect" tags loaded from the room's header. They trigger special behaviors like kill rooms, shutter doors, or custom events defined in `Dungeons/custom_tag.asm`. For example, a kill room tag will cause the `Underworld_HandleRoomTags` routine to check if all sprites in the room (`$7E0E20+`) have been defeated.
|
||||
|
||||
- **`UWDEATH` (`$7FDF80`) and `OWDEATH` (`$7FEF80`):** These are large bitfields in SRAM that track the state of every overworld screen and underworld room. When a kill room is cleared or a key is taken from a chest, a bit is set in this array. This ensures that the state persists permanently in the save file, preventing enemies from respawning or chests from reappearing.
|
||||
|
||||
## 4. The Player and Entities
|
||||
|
||||
- **Link:** The player's state is managed by its own state machine in `bank_07.asm`, with the current state held in `LINKDO` (`$7E005D`). This is covered in detail in `Docs/Link.md`.
|
||||
|
||||
- **Sprites and Ancillae:** The WRAM regions from `$7E0D00` onwards are large arrays that hold the state of all active entities in the game (16 sprites, ~40 ancillae). These are defined as `structs` in `Core/structs.asm`. While there are dozens of variables for each sprite, the most important for general game logic are:
|
||||
- `SprState` (`$7E0DD0,X`): The sprite's main state (e.g., `0x09` for active, `0x0B` for stunned).
|
||||
- `SprType` (`$7E0E20,X`): The sprite's ID number.
|
||||
- `SprX`/`SprY` (`$0D10,X`/`$0D00,X`): The sprite's coordinates.
|
||||
|
||||
The sprite engine in `bank_06.asm` iterates through these arrays each frame, executing the logic for each active sprite.
|
||||
|
||||
## 4.5. Custom WRAM Region (`$7E0730+`)
|
||||
|
||||
Oracle of Secrets adds a custom WRAM region starting at `$7E0730`, utilizing the MAP16OVERFLOW free RAM space. All custom variables documented here have been verified against `Core/ram.asm` and are actively used by the project's custom systems.
|
||||
|
||||
### Verified Custom WRAM Variables
|
||||
|
||||
| Address | Label | Description | Verified |
|
||||
|------------|-------------------------|-----------------------------------------------------------|----------|
|
||||
| `$7E0730` | `MenuScrollLevelV` | Vertical scroll position for the custom menu system | ✓ |
|
||||
| `$7E0731` | `MenuScrollLevelH` | Horizontal scroll position for the custom menu system | ✓ |
|
||||
| `$7E0732` | `MenuScrollHDirection` | Direction flag for horizontal menu scrolling (2 bytes) | ✓ |
|
||||
| `$7E0734` | `MenuItemValueSpoof` | Temporary override for displayed menu item values (2 bytes)| ✓ |
|
||||
| `$7E0736` | `ShortSpoof` | Shorter version of the spoof value (1 byte) | ✓ |
|
||||
| `$7E0737` | `MusicNoteValue` | Current music note value for Ocarina system (2 bytes) | ✓ |
|
||||
| `$7E0739` | `GoldstarOrHookshot` | Differentiates Hookshot (0) from Goldstar (1) mode | ✓ |
|
||||
| `$7E073A` | `Neck_Index` | Index for multi-part sprite body tracking (e.g., bosses) | ✓ |
|
||||
| `$7E073B` | `Neck1_OffsetX` | X-offset for first neck/body segment | ✓ |
|
||||
| `$7E073C` | `Neck1_OffsetY` | Y-offset for first neck/body segment | ✓ |
|
||||
| `$7E073D` | `Neck2_OffsetX` | X-offset for second neck/body segment | ✓ |
|
||||
| `$7E073E` | `Neck2_OffsetY` | Y-offset for second neck/body segment | ✓ |
|
||||
| `$7E073F` | `Neck3_OffsetX` | X-offset for third neck/body segment | ✓ |
|
||||
| `$7E0740` | `Neck3_OffsetY` | Y-offset for third neck/body segment | ✓ |
|
||||
| `$7E0741` | `Offspring1_Id` | Sprite ID of first child sprite (for boss mechanics) | ✓ |
|
||||
| `$7E0742` | `Offspring2_Id` | Sprite ID of second child sprite (for boss mechanics) | ✓ |
|
||||
| `$7E0743` | `Offspring3_Id` | Sprite ID of third child sprite (for boss mechanics) | ✓ |
|
||||
| `$7E0744` | `Kydreeok_Id` | Sprite ID for Kydreeok boss entity | ✓ |
|
||||
| `$7E0745` | `FishingOrPortalRod` | Differentiates Fishing Rod (1) from Portal Rod (2) | ✓ |
|
||||
|
||||
### Usage Notes
|
||||
|
||||
- **Menu System**: Variables `$7E0730-$7E0736` are exclusively used by the custom menu system (`Menu/menu.asm`) to manage smooth scrolling between the Items and Quest Status pages.
|
||||
|
||||
- **Item Differentiation**: `GoldstarOrHookshot` and `FishingOrPortalRod` are critical for shared inventory slots. These allow two distinct items to occupy a single menu slot, with the player able to switch between them using the L/R shoulder buttons.
|
||||
|
||||
- **Boss Mechanics**: The `Neck_*` and `Offspring_*` variables enable complex multi-part boss sprites (e.g., Kydreeok with multiple heads, Manhandla with independent parts). The parent sprite uses these to track and coordinate its child sprite components.
|
||||
|
||||
- **Memory Safety**: All variables in this region are placed within the MAP16OVERFLOW free RAM area, which is guaranteed to be unused by the vanilla game engine. This prevents conflicts with vanilla systems.
|
||||
|
||||
## 5. Long-Term Progression: SRAM and Custom Flags
|
||||
|
||||
SRAM (`$7EF000+`) stores the player's save file and is the key to managing long-term quest progression. Oracle of Secrets heavily expands the vanilla save format to support its new data-driven systems.
|
||||
|
||||
- **`OOSPROG` (`$7EF3D6`) and `OOSPROG2` (`$7EF3C6`):** These are the primary bitfields for tracking major and minor quest milestones. They are the heart of the game's custom progression.
|
||||
- **Example Flow:**
|
||||
1. The player talks to the `village_elder` NPC for the first time.
|
||||
2. The NPC's code in `Sprites/NPCs/village_elder.asm` sets a specific bit in `OOSPROG` (e.g., `ORA.b #$10 : STA.l OOSPROG`).
|
||||
3. Later, the world map code in `Overworld/world_map.asm` checks this bit (`LDA.l OOSPROG : AND.b #$10`). If it's set, a new icon is displayed on the map.
|
||||
|
||||
- **Other Custom SRAM:** The project adds many other custom variables to SRAM to track new systems, such as:
|
||||
- **New Inventory:** `ZoraMask` (`$7EF347`), `RocsFeather` (`$7EF34D`), etc.
|
||||
- **Side-Quests:** `MagicBeanProg` (`$7EF39B`) tracks the growth of a magic bean over time.
|
||||
- **New Collectibles:** A block starting at `$7EF38B` tracks items like `Bananas` and `Seashells`.
|
||||
|
||||
This data-driven approach, centered on modifying and checking flags in SRAM, allows for complex, stateful quest design that persists across play sessions.
|
||||
@@ -1,540 +0,0 @@
|
||||
# Oracle of Secrets: 65816 ASM Style Guide
|
||||
|
||||
**Version**: 1.0
|
||||
**Purpose**: Reduce AI errors and ensure consistent code quality across the codebase.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
### Label Naming
|
||||
|
||||
| Type | Pattern | Example |
|
||||
|------|---------|---------|
|
||||
| Sprite function | `Sprite_{Name}_{Type}` | `Sprite_Booki_Main`, `Sprite_Darknut_Draw` |
|
||||
| Menu function | `Menu_{Purpose}` | `Menu_InitGraphics`, `Menu_DrawBackground` |
|
||||
| Link/Player | `Link_{Action}` | `Link_ConsumeMagicBagItem`, `Link_HandleYItem` |
|
||||
| Oracle namespace | `Oracle_{Function}` | `Oracle_CheckIfNight`, `Oracle_MainEntry` |
|
||||
| Local labels | `.lowercase_with_underscores` | `.not_being_pushed`, `.nextTile` |
|
||||
| Constants (macro) | `!UPPERCASE` | `!SPRID`, `!Health`, `!Damage` |
|
||||
| Memory addresses | `CamelCase` | `SprAction`, `LinkState`, `OOSPROG` |
|
||||
|
||||
### Processor State Checklist
|
||||
|
||||
- [ ] Always use size suffixes (`.b`, `.w`, `.l`) for ambiguous operations
|
||||
- [ ] Use `PHP`/`PLP` for functions called from unknown context
|
||||
- [ ] `REP #$30` = 16-bit A and X/Y, `SEP #$30` = 8-bit
|
||||
- [ ] `REP #$20` / `SEP #$20` = A only
|
||||
- [ ] `REP #$10` / `SEP #$10` = X/Y only
|
||||
|
||||
### Call Convention Checklist
|
||||
|
||||
- [ ] `JSL`/`RTL` for cross-bank calls (3-byte return address)
|
||||
- [ ] `JSR`/`RTS` for same-bank calls (2-byte return address)
|
||||
- [ ] **NEVER MIX** - mismatch causes crashes
|
||||
- [ ] External hooks use `JSL`, internal helpers use `JSR`
|
||||
|
||||
---
|
||||
|
||||
## 1. File Structure
|
||||
|
||||
### 1.1 File Header (Required for new files)
|
||||
|
||||
```asm
|
||||
; =========================================================
|
||||
; File: sprites/enemies/my_enemy.asm
|
||||
; Purpose: [Brief description of what this file implements]
|
||||
; Author: [Your name or handle]
|
||||
; =========================================================
|
||||
```
|
||||
|
||||
### 1.2 Section Organization
|
||||
|
||||
```asm
|
||||
; =========================================================
|
||||
; Sprite Properties
|
||||
; =========================================================
|
||||
!SPRID = $XX
|
||||
!NbrTiles = 02
|
||||
; ... (30 properties in standard order)
|
||||
|
||||
; =========================================================
|
||||
; Entry Points
|
||||
; =========================================================
|
||||
Sprite_MyEnemy_Long:
|
||||
{ ... }
|
||||
|
||||
Sprite_MyEnemy_Prep:
|
||||
{ ... }
|
||||
|
||||
; =========================================================
|
||||
; Main Logic
|
||||
; =========================================================
|
||||
Sprite_MyEnemy_Main:
|
||||
{ ... }
|
||||
|
||||
; =========================================================
|
||||
; Drawing
|
||||
; =========================================================
|
||||
Sprite_MyEnemy_Draw:
|
||||
{ ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Naming Conventions
|
||||
|
||||
### 2.1 Labels
|
||||
|
||||
**PascalCase with underscores for hierarchy:**
|
||||
|
||||
```asm
|
||||
; Good
|
||||
Sprite_Booki_Main
|
||||
Menu_InitGraphics
|
||||
Link_ConsumeMagicBagItem
|
||||
Oracle_CheckIfNight
|
||||
|
||||
; Bad
|
||||
spriteBookiMain ; No underscores
|
||||
sprite_booki_main ; All lowercase
|
||||
SPRITE_BOOKI_MAIN ; All uppercase
|
||||
```
|
||||
|
||||
### 2.2 Local Labels
|
||||
|
||||
**Lowercase with dot prefix:**
|
||||
|
||||
```asm
|
||||
Sprite_Move:
|
||||
{
|
||||
LDA.w SprAction, X : BNE .already_moving
|
||||
; Start movement
|
||||
INC.w SprAction, X
|
||||
.already_moving
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 Constants and Macros
|
||||
|
||||
```asm
|
||||
; Macro parameters: !UPPERCASE
|
||||
!SPRID = $D5
|
||||
!Health = 08
|
||||
!Damage = 02
|
||||
|
||||
; Debug flags: !UPPERCASE with LOG prefix
|
||||
!DEBUG = 1
|
||||
!LOG_MUSIC = 1
|
||||
!LOG_SPRITES = 0
|
||||
|
||||
; Memory addresses: CamelCase (matching vanilla convention)
|
||||
SprAction = $0D80
|
||||
LinkState = $5D
|
||||
OOSPROG = $7EF3D6
|
||||
```
|
||||
|
||||
### 2.4 Sprite Property Standard Order
|
||||
|
||||
All sprites MUST define properties in this order:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_MyEnemyID
|
||||
!NbrTiles = 02
|
||||
!Harmless = 00
|
||||
!Health = 08
|
||||
!Damage = 02
|
||||
!DeathAnimation = 00
|
||||
!ImperviousArrow = 00
|
||||
!ImperviousSword = 00
|
||||
!Boss = 00
|
||||
!Shadow = 01
|
||||
!Palette = 00
|
||||
!Hitbox = $00
|
||||
!Persist = 00
|
||||
!Statis = 00
|
||||
!CollisionLayer = 00
|
||||
!CanFall = 01
|
||||
!DeflectProjectiles = 00
|
||||
!WaterSprite = 00
|
||||
!Blockable = 00
|
||||
!Prize = 00
|
||||
!Sound = $00
|
||||
!Interaction = $00
|
||||
!Subtype2 = 00
|
||||
%Set_Sprite_Properties(Sprite_MyEnemy_Prep, Sprite_MyEnemy_Long)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Scoping and Indentation
|
||||
|
||||
### 3.1 Bracket Scoping
|
||||
|
||||
**ALL functions use `{ }` brackets** (NOT `subroutine`/`endsubroutine`):
|
||||
|
||||
```asm
|
||||
Sprite_Booki_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
dw StalkPlayer
|
||||
dw HideFromPlayer
|
||||
dw ApproachPlayer
|
||||
|
||||
StalkPlayer:
|
||||
{
|
||||
%PlayAnimation(0,1,16)
|
||||
JSR Sprite_Booki_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
HideFromPlayer:
|
||||
{
|
||||
; Nested content indented 2 spaces
|
||||
LDA.b #$00
|
||||
STA.w SprAction, X
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Indentation Rules
|
||||
|
||||
- **2-space indentation** for all nested content
|
||||
- Local labels at same indentation as containing block
|
||||
- Conditional code indented under branch:
|
||||
|
||||
```asm
|
||||
JSL Sprite_CheckActive : BCC .inactive
|
||||
; Active sprite code (indented)
|
||||
JSR DoActiveStuff
|
||||
.inactive
|
||||
PLB
|
||||
RTL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Processor State Management
|
||||
|
||||
### 4.1 Size Suffixes (REQUIRED)
|
||||
|
||||
Always use explicit size suffixes when processor state matters:
|
||||
|
||||
```asm
|
||||
; Good - explicit sizes
|
||||
LDA.w #$1234 ; 16-bit load
|
||||
LDA.b #$12 ; 8-bit load
|
||||
STA.l $7E0000 ; Long address
|
||||
|
||||
; Bad - ambiguous
|
||||
LDA #$12 ; Is this 8-bit or 16-bit?
|
||||
```
|
||||
|
||||
### 4.2 State Preservation
|
||||
|
||||
```asm
|
||||
; Functions called from unknown context
|
||||
SomePublicFunction:
|
||||
{
|
||||
PHP ; Save caller's state
|
||||
SEP #$30 ; Set known state (8-bit A, X/Y)
|
||||
|
||||
; ... function body ...
|
||||
|
||||
PLP ; Restore caller's state
|
||||
RTL
|
||||
}
|
||||
|
||||
; Internal helpers can assume state from caller
|
||||
.helper:
|
||||
; Assume 8-bit A from caller
|
||||
LDA.b #$00
|
||||
RTS
|
||||
```
|
||||
|
||||
### 4.3 Processor Mode Macros
|
||||
|
||||
Use these macros for clarity:
|
||||
|
||||
```asm
|
||||
%m8() ; SEP #$20 - 8-bit accumulator
|
||||
%m16() ; REP #$20 - 16-bit accumulator
|
||||
%a8() ; SEP #$20 - 8-bit A (alias)
|
||||
%a16() ; REP #$20 - 16-bit A (alias)
|
||||
%index8() ; SEP #$10 - 8-bit X/Y
|
||||
%index16() ; REP #$10 - 16-bit X/Y
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Hook and Patch Patterns
|
||||
|
||||
### 5.1 Small Patches (pushpc/pullpc)
|
||||
|
||||
```asm
|
||||
pushpc
|
||||
org $1EF27D
|
||||
ShopItem_Banana:
|
||||
{
|
||||
JSR $F4CE ; SpriteDraw_ShopItem
|
||||
; Custom code here
|
||||
RTS
|
||||
}
|
||||
assert pc() <= $1EF2AB ; Ensure we fit
|
||||
pullpc
|
||||
```
|
||||
|
||||
### 5.2 Hook Pattern
|
||||
|
||||
```asm
|
||||
; In patches.asm or near related code
|
||||
pushpc
|
||||
org $02XXXX ; Vanilla address
|
||||
JSL MyCustomHook ; 4-byte JSL
|
||||
NOP ; Pad if needed
|
||||
pullpc
|
||||
|
||||
; The hook implementation
|
||||
MyCustomHook:
|
||||
{
|
||||
; Preserve any clobbered code
|
||||
JSL OriginalRoutine
|
||||
|
||||
; Add custom logic
|
||||
LDA.w CustomFlag : BEQ .skip
|
||||
JSL DoCustomThing
|
||||
.skip
|
||||
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Hook Documentation
|
||||
|
||||
```asm
|
||||
; =========================================================
|
||||
; Hook: $02XXXX - Link's Y-Button Handler
|
||||
; Purpose: Add custom item handling for Magic Bag
|
||||
; Vanilla Code Replaced: JSL $07F44C
|
||||
; Side Effects: Clobbers A
|
||||
; =========================================================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Comments and Documentation
|
||||
|
||||
### 6.1 Section Dividers
|
||||
|
||||
Use exactly 57 equal signs:
|
||||
|
||||
```asm
|
||||
; =========================================================
|
||||
; Section Name
|
||||
; =========================================================
|
||||
```
|
||||
|
||||
### 6.2 Bitfield Documentation
|
||||
|
||||
```asm
|
||||
; Bitfield: hmwo oooo
|
||||
; o - OAM slot count (bits 0-4)
|
||||
; w - Wall-seeking behavior
|
||||
; m - Master sword ceremony flag
|
||||
; h - Harmless (no contact damage)
|
||||
SprNbrOAM = $0E40
|
||||
```
|
||||
|
||||
### 6.3 TODO Comments
|
||||
|
||||
```asm
|
||||
; TODO: Add chase animation when player is detected
|
||||
; TODO(scawful): Refactor this to use lookup table
|
||||
```
|
||||
|
||||
### 6.4 Magic Number Documentation
|
||||
|
||||
```asm
|
||||
LDA.b #$08 ; 8-frame animation delay
|
||||
CMP.w #$0100 ; Check if past screen boundary (256px)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Memory and Data Structures
|
||||
|
||||
### 7.1 Struct Definitions
|
||||
|
||||
```asm
|
||||
struct TimeState $7EE000
|
||||
{
|
||||
.Hours: skip 1
|
||||
.Minutes: skip 1
|
||||
.Speed: skip 1
|
||||
.Padding: skip 13
|
||||
.BlueVal: skip 2
|
||||
.GreenVal: skip 2
|
||||
.RedVal: skip 2
|
||||
}
|
||||
endstruct
|
||||
```
|
||||
|
||||
### 7.2 Inline Data Tables
|
||||
|
||||
```asm
|
||||
Sprite_Draw:
|
||||
{
|
||||
; ... draw code ...
|
||||
RTS
|
||||
|
||||
; Data tables use dot notation
|
||||
.start_index
|
||||
db $00, $04, $08, $0C
|
||||
.nbr_of_tiles
|
||||
db 3, 3, 3, 3
|
||||
.x_offsets
|
||||
dw 4, -4, 4, -4
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Error Prevention Checklist
|
||||
|
||||
### Before Submitting Code
|
||||
|
||||
- [ ] All labels use correct PascalCase_With_Underscores
|
||||
- [ ] All local labels use .dot_notation
|
||||
- [ ] Size suffixes on all ambiguous loads/stores
|
||||
- [ ] JSL/RTL and JSR/RTS pairs matched correctly
|
||||
- [ ] Hooks have `assert` statements to prevent overflow
|
||||
- [ ] Magic numbers have inline comments explaining purpose
|
||||
- [ ] Sprite properties in standard order
|
||||
- [ ] Section dividers between major code blocks
|
||||
|
||||
### Common Crash Causes
|
||||
|
||||
1. **JSL/JSR mismatch** - Using RTL with JSR or RTS with JSL
|
||||
2. **Bank crossing** - Forgetting to set DBR with PHK:PLB
|
||||
3. **Processor state** - Assuming 8-bit when 16-bit or vice versa
|
||||
4. **Hook overflow** - Patch exceeds available space
|
||||
5. **Missing pullpc** - Stack imbalance from pushpc
|
||||
|
||||
---
|
||||
|
||||
## 9. Oracle of Secrets Specific Patterns
|
||||
|
||||
### 9.1 Sprite Entry Point Pattern
|
||||
|
||||
```asm
|
||||
Sprite_MyEnemy_Long:
|
||||
{
|
||||
PHB : PHK : PLB ; Set data bank to current bank
|
||||
JSR Sprite_MyEnemy_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .inactive
|
||||
JSR Sprite_MyEnemy_Main
|
||||
.inactive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 State Machine Pattern
|
||||
|
||||
```asm
|
||||
Sprite_MyEnemy_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
dw State_Idle
|
||||
dw State_Chase
|
||||
dw State_Attack
|
||||
dw State_Retreat
|
||||
|
||||
State_Idle:
|
||||
{
|
||||
%PlayAnimation(0,1,16)
|
||||
; Check for player proximity
|
||||
JSL Sprite_CheckDamageToLink
|
||||
BCC .stay_idle
|
||||
INC.w SprAction, X ; Transition to Chase
|
||||
.stay_idle
|
||||
RTS
|
||||
}
|
||||
; ... more states ...
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 SRAM Flag Checking
|
||||
|
||||
```asm
|
||||
; Check Oracle progression flag
|
||||
LDA.l OOSPROG : AND.b #$01 : BEQ .not_complete
|
||||
; Player has completed this milestone
|
||||
.not_complete
|
||||
|
||||
; Bitfield reference for OOSPROG ($7EF3D6):
|
||||
; .fmp h.i.
|
||||
; i = Intro complete
|
||||
; h = Hall of Secrets visited
|
||||
; p = Pendant progress
|
||||
; m = Master Sword acquired
|
||||
; f = Fortress of Secrets
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. AI Agent Instructions
|
||||
|
||||
### 10.1 Before Writing Code
|
||||
|
||||
1. **Read existing patterns** - Search for similar implementations
|
||||
2. **Check memory map** - Verify address usage won't conflict
|
||||
3. **Identify hook points** - Use Hyrule Historian to find vanilla code
|
||||
4. **Verify bank space** - Check MemoryMap.md for free space
|
||||
|
||||
### 10.2 When Modifying Code
|
||||
|
||||
1. **Always read the file first** - Never assume structure
|
||||
2. **Match existing style** - Follow patterns in the same file
|
||||
3. **Use explicit sizes** - Never rely on assumed processor state
|
||||
4. **Add assert statements** - Prevent silent overflow errors
|
||||
|
||||
### 10.3 After Writing Code
|
||||
|
||||
1. **Run build** - Use `mcp__book-of-mudora__run_build()`
|
||||
2. **Run lint** - Use `mcp__book-of-mudora__lint_asm()`
|
||||
3. **Verify in emulator** - Test before marking complete
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Common Macro Reference
|
||||
|
||||
### Animation and Drawing
|
||||
- `%PlayAnimation(start, end, speed)` - Animate sprite frames
|
||||
- `%DrawSprite(...)` - Draw sprite OAM
|
||||
- `%SetFrame(n)` - Set current animation frame
|
||||
|
||||
### Sprite Control
|
||||
- `%GotoAction(n)` - Change sprite state
|
||||
- `%SetTimer*(n)` - Set various timers
|
||||
- `%SetSpriteSpeed*(n)` - Set movement speed
|
||||
- `%SetHarmless()` / `%SetImpervious()` - Damage flags
|
||||
|
||||
### Player
|
||||
- `%PreventPlayerMovement()` / `%AllowPlayerMovement()`
|
||||
- `%GetPlayerRupees()` - Return rupee count
|
||||
- `%ShowUnconditionalMessage(id)` - Display dialogue
|
||||
- `%ShowSolicitedMessage(id)` - Display on interaction
|
||||
|
||||
### Audio
|
||||
- `%PlaySFX1(id)` / `%PlaySFX2(id)` - Sound effects
|
||||
- `%PlayMusic(id)` - Change music
|
||||
|
||||
### Debugging
|
||||
- `%print_debug(msg)` - Build-time debug output
|
||||
- `%log_section(name, flag)` - Conditional section logging
|
||||
@@ -1,472 +0,0 @@
|
||||
# Oracle of Secrets: System Architecture
|
||||
|
||||
**Purpose**: Document how major systems interact to help AI agents understand the codebase structure.
|
||||
|
||||
---
|
||||
|
||||
## 1. High-Level Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Oracle_main.asm │
|
||||
│ (Master include file - controls ROM layout and build order) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────┼─────────────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ Core/ │ │ Sprites/ │ │ Overworld/ │
|
||||
│ - link.asm │ │ - all_sprites│ │ - ZSCustomOW │
|
||||
│ - sram.asm │ │ - Bosses/ │ │ - time_system│
|
||||
│ - symbols.asm│ │ - NPCs/ │ │ - overlays │
|
||||
│ - patches.asm│ │ - Enemies/ │ │ - lost_woods │
|
||||
│ - message.asm│ │ - Objects/ │ └───────────────┘
|
||||
└───────────────┘ └───────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐
|
||||
│ Items/ │ │ Menu/ │
|
||||
│ - all_items │ │ - menu.asm │
|
||||
│ - ocarina │ │ - menu_select│
|
||||
│ - magic_bag │ │ - menu_journal│
|
||||
└───────────────┘ └───────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Namespace Organization
|
||||
|
||||
### 2.1 Oracle Namespace
|
||||
|
||||
All custom Oracle of Secrets code lives inside the `Oracle` namespace:
|
||||
|
||||
```asm
|
||||
namespace Oracle
|
||||
{
|
||||
; Core systems
|
||||
incsrc "Core/link.asm"
|
||||
incsrc "Core/sram.asm"
|
||||
incsrc "Core/symbols.asm"
|
||||
|
||||
; Content
|
||||
incsrc "Music/all_music.asm"
|
||||
incsrc "Sprites/all_sprites.asm"
|
||||
incsrc "Items/all_items.asm"
|
||||
|
||||
; Patches go last
|
||||
incsrc "Core/patches.asm"
|
||||
}
|
||||
namespace off
|
||||
|
||||
; ZScream code is OUTSIDE the namespace
|
||||
incsrc "Overworld/ZSCustomOverworld.asm"
|
||||
```
|
||||
|
||||
### 2.2 Why This Matters
|
||||
|
||||
- Labels inside `namespace Oracle` become `Oracle.LabelName`
|
||||
- ZScream uses its own conventions and must be outside
|
||||
- Patches at end to ensure all labels are defined
|
||||
|
||||
---
|
||||
|
||||
## 3. Memory Map Overview
|
||||
|
||||
### 3.1 Bank Organization
|
||||
|
||||
| Bank Range | Purpose | Notes |
|
||||
|------------|---------|-------|
|
||||
| $00-$1F | Vanilla ALTTP + small patches | Limited free space |
|
||||
| $20-$29 | ZScream Overworld Data | ~1.5MB reserved |
|
||||
| $30 | Sprite prep/initialization | Oracle sprites start here |
|
||||
| $31 | Main sprite logic | Enemy/NPC behavior |
|
||||
| $32 | Boss logic | Complex sprite code |
|
||||
| $33-$3F | Free space | Available for expansion |
|
||||
|
||||
### 3.2 RAM Regions
|
||||
|
||||
| Region | Purpose |
|
||||
|--------|---------|
|
||||
| $7E0000-$7E1FFF | Scratch RAM (volatile) |
|
||||
| $7E2000-$7EFFFF | Game state RAM |
|
||||
| $7EE000-$7EE0FF | TimeState struct |
|
||||
| $7EF000-$7EFFFF | SRAM (saved data) |
|
||||
| $7EF3C5-$7EF3D6 | Oracle progression flags |
|
||||
| $7EF410 | Dreams bitfield |
|
||||
|
||||
### 3.3 Key SRAM Variables
|
||||
|
||||
```asm
|
||||
; GameState ($7EF3C5) - Overall progression
|
||||
; 0x00 = Very start
|
||||
; 0x01 = Uncle reached
|
||||
; 0x02 = Zelda rescued / Farore intro
|
||||
; 0x03 = Agahnim defeated
|
||||
|
||||
; OOSPROG ($7EF3D6) - Oracle progression bitfield
|
||||
; .fmp h.i.
|
||||
; i = Intro complete, Maku Tree met
|
||||
; h = Hall of Secrets visited
|
||||
; p = Pendant quest progress
|
||||
; m = Master Sword acquired
|
||||
; f = Fortress of Secrets
|
||||
|
||||
; OOSPROG2 ($7EF3C6) - Secondary progression
|
||||
; .fbh .zsu
|
||||
; u = Uncle visited
|
||||
; s = Priest visited in sanctuary
|
||||
; z = Zelda brought to sanctuary
|
||||
; h = Uncle left house
|
||||
; b = Book of Mudora obtained
|
||||
; f = Fortune teller flag
|
||||
|
||||
; Dreams ($7EF410) - Dream sequence tracking
|
||||
; .dts fwpb
|
||||
; (Individual dream completion flags)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. System Interactions
|
||||
|
||||
### 4.1 ZScream Custom Overworld (ZSOW) Integration
|
||||
|
||||
ZSOW manages:
|
||||
- Overworld map transitions
|
||||
- Palette events
|
||||
- Overlay data
|
||||
- Custom collision
|
||||
|
||||
**Hook Points**:
|
||||
```
|
||||
OverworldHandleTransitions ($028000 area)
|
||||
└── Oracle_CheckIfNight (time-based sprite loading)
|
||||
└── LostWoods_PuzzleHandler (navigation puzzle)
|
||||
└── SongOfStorms overlay effects
|
||||
```
|
||||
|
||||
**Known Conflicts**:
|
||||
- Lost Woods puzzle directly modifies transition logic
|
||||
- Day/Night sprites must check `Oracle_CheckIfNight`
|
||||
- Song of Storms overlays need coordination with ZSOW
|
||||
|
||||
### 4.2 Time System
|
||||
|
||||
**File**: `Overworld/time_system.asm`
|
||||
|
||||
**Structure**:
|
||||
```asm
|
||||
struct TimeState $7EE000
|
||||
{
|
||||
.Hours, .Minutes, .Speed
|
||||
.BlueVal, .GreenVal, .RedVal
|
||||
.TempColor, .SubColor
|
||||
}
|
||||
```
|
||||
|
||||
**Flow**:
|
||||
```
|
||||
RunClock (called each frame)
|
||||
├── TimeSystem_CheckCanRun
|
||||
│ └── Check game mode, indoors status
|
||||
├── TimeSystem_IncrementTime
|
||||
│ └── Update Hours/Minutes based on Speed
|
||||
└── TimeSystem_UpdatePalettes
|
||||
└── Apply color tinting based on time
|
||||
```
|
||||
|
||||
**Integration Points**:
|
||||
- `Oracle_CheckIfNight` - Called by ZSOW for sprite loading
|
||||
- Palette system - Affects overworld colors
|
||||
- NPC behavior - Some NPCs react to time
|
||||
|
||||
### 4.3 Sprite System
|
||||
|
||||
**Entry Points** (standard pattern):
|
||||
|
||||
```
|
||||
Sprite_*_Long (JSL entry from sprite table)
|
||||
├── PHB : PHK : PLB (set data bank)
|
||||
├── Sprite_*_Draw (JSR - render)
|
||||
├── Sprite_CheckActive (JSL - is active?)
|
||||
│ └── Sprite_*_Main (JSR - if active)
|
||||
├── PLB
|
||||
└── RTL
|
||||
```
|
||||
|
||||
**State Machine**:
|
||||
```
|
||||
SprAction (per-sprite state variable)
|
||||
└── JumpTableLocal dispatches to state handlers
|
||||
├── State_Idle
|
||||
├── State_Chase
|
||||
├── State_Attack
|
||||
└── State_Retreat
|
||||
```
|
||||
|
||||
**Key Variables** (indexed by X):
|
||||
| Address | Name | Purpose |
|
||||
|---------|------|---------|
|
||||
| $0D00 | SprY | Y position (low byte) |
|
||||
| $0D10 | SprX | X position (low byte) |
|
||||
| $0D80 | SprAction | Current state |
|
||||
| $0DA0 | SprHealth | Hit points remaining |
|
||||
| $0E40 | SprNbrOAM | OAM slot count |
|
||||
|
||||
### 4.4 Menu System
|
||||
|
||||
**File**: `Menu/menu.asm`
|
||||
|
||||
**State Machine**:
|
||||
```
|
||||
MenuMode ($0200) - Current menu state
|
||||
├── $00 = Not in menu
|
||||
├── $01 = Opening animation
|
||||
├── $02 = Item select
|
||||
├── $0C = Magic Bag submenu
|
||||
├── $0D = Ring Box submenu
|
||||
└── $0E = Song select submenu
|
||||
```
|
||||
|
||||
**Flow**:
|
||||
```
|
||||
Menu_Entry (from pause input)
|
||||
├── Menu_InitGraphics
|
||||
├── Menu_Upload* (VRAM transfers)
|
||||
└── Menu_MainLoop
|
||||
├── Handle input
|
||||
├── Update selection
|
||||
└── Menu_Draw*
|
||||
```
|
||||
|
||||
### 4.5 Dialogue System
|
||||
|
||||
**Files**: `Core/message.asm`, `Core/messages.org`
|
||||
|
||||
**Message Format** (in messages.org):
|
||||
```
|
||||
** 20 - Maku Tree Part1
|
||||
[W:02][S:03]Ah, [L]!
|
||||
[2]Thank the Goddesses you are
|
||||
[3]alright. I feared the worst.
|
||||
[V]A dark shadow has befallen us.[K]
|
||||
```
|
||||
|
||||
**Control Codes**:
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| `[2]`, `[3]` | Line 2, Line 3 |
|
||||
| `[K]` | Wait for button press |
|
||||
| `[V]` | Continue on same line |
|
||||
| `[W:XX]` | Wait time |
|
||||
| `[S:XX]` | Text speed |
|
||||
| `[SFX:XX]` | Play sound effect |
|
||||
| `[CH2I]` | 2-choice prompt |
|
||||
| `[CH3]` | 3-choice prompt |
|
||||
| `[L]` | Player name |
|
||||
|
||||
**Display Macros**:
|
||||
```asm
|
||||
%ShowUnconditionalMessage($20) ; Force display
|
||||
%ShowSolicitedMessage($20) ; On interaction only
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Hook Architecture
|
||||
|
||||
### 5.1 Hook Types
|
||||
|
||||
**Type 1: Inline Patch** (replace vanilla code)
|
||||
```asm
|
||||
pushpc
|
||||
org $02XXXX ; Vanilla address
|
||||
JSL MyHook ; Replace original instruction
|
||||
NOP : NOP ; Pad to match original size
|
||||
pullpc
|
||||
```
|
||||
|
||||
**Type 2: Table Override** (jump table entry)
|
||||
```asm
|
||||
pushpc
|
||||
org $07F000+($ID*2) ; Jump table for sprite $ID
|
||||
dw Sprite_Custom_Prep
|
||||
pullpc
|
||||
```
|
||||
|
||||
**Type 3: Extended Logic** (call original + extend)
|
||||
```asm
|
||||
MyHook:
|
||||
{
|
||||
JSL OriginalRoutine ; Preserve original behavior
|
||||
; Add custom logic
|
||||
LDA.w CustomFlag
|
||||
BEQ .skip
|
||||
JSL CustomHandler
|
||||
.skip
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Hook Documentation Pattern
|
||||
|
||||
Every hook should document:
|
||||
|
||||
```asm
|
||||
; =========================================================
|
||||
; Hook: $XXBANK:ADDR
|
||||
; Purpose: [What this hook adds/changes]
|
||||
; Vanilla Code: [What original code did]
|
||||
; Clobbered: [Registers modified]
|
||||
; Dependencies: [Other systems affected]
|
||||
; =========================================================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Build System
|
||||
|
||||
### 6.1 Build Order (Critical)
|
||||
|
||||
```
|
||||
1. Core/symbols.asm - Memory declarations
|
||||
2. Core/sram.asm - SRAM layout
|
||||
3. Core/link.asm - Player modifications
|
||||
4. Music/all_music.asm - SPC700 data
|
||||
5. Sprites/all_sprites.asm - All sprite code
|
||||
6. Items/all_items.asm - Item handlers
|
||||
7. Menu/menu.asm - Menu system
|
||||
8. Dungeons/ - Dungeon-specific code
|
||||
9. Core/patches.asm - Vanilla patches (LAST)
|
||||
10. Overworld/ZSCustomOverworld.asm - ZSOW (OUTSIDE namespace)
|
||||
```
|
||||
|
||||
### 6.2 Build Commands
|
||||
|
||||
```bash
|
||||
# Fast build
|
||||
./run.sh
|
||||
|
||||
# MCP tools
|
||||
mcp__book-of-mudora__run_build() # Build ROM
|
||||
mcp__book-of-mudora__lint_asm() # Check style
|
||||
mcp__book-of-mudora__analyze_patches() # Review patches
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Debugging Workflow
|
||||
|
||||
### 7.1 Common Debug Points
|
||||
|
||||
| Symptom | Check First |
|
||||
|---------|-------------|
|
||||
| Crash on room enter | Sprite prep routine, invalid JSL target |
|
||||
| Wrong graphics | SprGfx assignment, DMA timing |
|
||||
| Frozen game | Infinite loop in Main, missing RTS/RTL |
|
||||
| Black screen | Palette loading, HDMA setup |
|
||||
| Corrupt saves | SRAM address conflict, missing bank setup |
|
||||
|
||||
### 7.2 Debug Tools
|
||||
|
||||
**Yaze MCP** (in-development):
|
||||
```python
|
||||
mcp__yaze_mcp__read_memory("7E0010", 2) # Read RAM
|
||||
mcp__yaze_mcp__add_breakpoint("02XXXX") # Set breakpoint
|
||||
mcp__yaze_mcp__get_game_state() # Dump current state
|
||||
```
|
||||
|
||||
**Hyrule Historian**:
|
||||
```python
|
||||
mcp__hyrule-historian__lookup_address("02XXXX")
|
||||
mcp__hyrule-historian__search_oracle_code("Sprite_Booki")
|
||||
mcp__hyrule-historian__get_ram_info("SprAction")
|
||||
```
|
||||
|
||||
### 7.3 Logging
|
||||
|
||||
```asm
|
||||
; Build-time logging
|
||||
%log_section("Sprites", !LOG_SPRITES)
|
||||
|
||||
; Enable in build:
|
||||
!LOG_SPRITES = 1 ; Set in config
|
||||
|
||||
; Output appears in build log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Common Integration Patterns
|
||||
|
||||
### 8.1 Adding a New Sprite
|
||||
|
||||
1. Create file: `Sprites/Enemies/my_sprite.asm`
|
||||
2. Define properties using standard order
|
||||
3. Implement `_Long`, `_Prep`, `_Main`, `_Draw`
|
||||
4. Add include to `Sprites/all_sprites.asm`
|
||||
5. Add to sprite table with `org` directive
|
||||
6. Build and test
|
||||
|
||||
### 8.2 Adding a Quest Flag
|
||||
|
||||
1. Find free bit in OOSPROG/OOSPROG2
|
||||
2. Document in `Core/sram.asm`
|
||||
3. Update this file's SRAM section
|
||||
4. Use in code:
|
||||
```asm
|
||||
LDA.l OOSPROG : ORA.b #$XX : STA.l OOSPROG
|
||||
```
|
||||
|
||||
### 8.3 Adding a New Hook
|
||||
|
||||
1. Find vanilla address with Hyrule Historian
|
||||
2. Document what original code does
|
||||
3. Create hook with `pushpc`/`pullpc`
|
||||
4. Add `assert` to verify space
|
||||
5. Test vanilla behavior still works
|
||||
|
||||
---
|
||||
|
||||
## 9. System Dependency Graph
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ TimeSystem │
|
||||
└──────┬──────┘
|
||||
│ Oracle_CheckIfNight
|
||||
▼
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ Lost Woods │────▶│ ZSOW │◀────│ Overlays │
|
||||
│ Puzzle │ │ Transitions │ │ System │
|
||||
└─────────────┘ └──────┬──────┘ └─────────────┘
|
||||
│
|
||||
┌────────────┴────────────┐
|
||||
▼ ▼
|
||||
┌─────────────┐ ┌─────────────┐
|
||||
│ Sprites │ │ Events │
|
||||
│ (Loading) │ │ (Triggers) │
|
||||
└──────┬──────┘ └──────┬──────┘
|
||||
│ │
|
||||
└───────────┬─────────────┘
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ Player │
|
||||
│ (Link) │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. AI Agent Checklist
|
||||
|
||||
When working on Oracle of Secrets:
|
||||
|
||||
1. [ ] Check `oracle.org` for related tasks
|
||||
2. [ ] Read existing code in affected files
|
||||
3. [ ] Verify memory map for conflicts
|
||||
4. [ ] Use Hyrule Historian for vanilla lookups
|
||||
5. [ ] Follow StyleGuide.md conventions
|
||||
6. [ ] Run build after changes
|
||||
7. [ ] Document any new hooks
|
||||
8. [ ] Update this file if adding new systems
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,610 +0,0 @@
|
||||
# Castle Ambush & Guard Capture System - Implementation Plan
|
||||
|
||||
**Status:** 🚧 Planning Phase
|
||||
**Created:** October 3, 2025
|
||||
**Target:** Future Update
|
||||
**Related Files:** `Core/capture.asm`, `Sprites/Enemies/custom_guard.asm`, `Sprites/overlord_ref.asm`
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The **Castle Ambush System** will create a dynamic encounter where Link is detected by guards in the castle, captured, and warped to a dungeon. This combines:
|
||||
|
||||
1. **Probe Detection System** - Guards detect Link entering restricted areas
|
||||
2. **Guard Capture Mechanics** - Guards surround and capture Link
|
||||
3. **Warp System** - Link is transported to a dungeon entrance
|
||||
4. **Overlord Management** - Multi-screen guard coordination
|
||||
|
||||
---
|
||||
|
||||
## Current State Analysis
|
||||
|
||||
### Existing Components
|
||||
|
||||
#### ✅ Core/capture.asm
|
||||
**Status:** Implemented but untested
|
||||
|
||||
```asm
|
||||
Oracle_CaptureAndWarp:
|
||||
{
|
||||
STA.w $010E ; Set the target entrance ID
|
||||
LDA.b #$05 ; Game Mode 05: (hole/whirlpool transition)
|
||||
STA.b $10 ; Set the game mode
|
||||
STZ.b $2F ; Clear Link's action state
|
||||
STZ.b $5D ; Clear Link's state
|
||||
LDA.b #$02 : STA.b $71 ; Set transition flag
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
**Purpose:** Warps Link to a specific dungeon entrance (like WallMaster)
|
||||
|
||||
**Issues to Address:**
|
||||
- [ ] Test entrance ID values (need to determine correct dungeon entrance)
|
||||
- [ ] Verify game mode $05 works for this use case
|
||||
- [ ] Add screen fade/transition effect
|
||||
- [ ] Play capture sound effect
|
||||
- [ ] Store pre-capture location for potential escape sequence
|
||||
|
||||
#### 🚧 Sprites/Enemies/custom_guard.asm
|
||||
**Status:** Prototype with duplicate code
|
||||
|
||||
**Contains:**
|
||||
1. `Oracle_CaptureAndWarp` (DUPLICATE - already in Core/capture.asm)
|
||||
2. `Hooked_Guard_Main` - Modified guard behavior
|
||||
|
||||
**Issues:**
|
||||
- [ ] Remove duplicate `Oracle_CaptureAndWarp` function
|
||||
- [ ] Complete `Hooked_Guard_Main` implementation
|
||||
- [ ] Test guard capture trigger conditions
|
||||
- [ ] Integrate with vanilla guard sprites (ID $41, $42, $43)
|
||||
|
||||
#### 📚 Sprites/overlord_ref.asm
|
||||
**Status:** Reference material (now in experimental/)
|
||||
|
||||
**Purpose:** Documents overlord path patterns for crumbling tiles
|
||||
|
||||
**Relevance:** Can be adapted for guard patrol paths
|
||||
|
||||
---
|
||||
|
||||
## System Architecture
|
||||
|
||||
### Phase 1: Detection (Probe System)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Link enters castle restricted area │
|
||||
│ SRAM flag: $7EF??? = Castle infiltration active │
|
||||
└──────────────────┬──────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Guard spawns probe sprites every 32 frames │
|
||||
│ Probe checks: │
|
||||
│ - Link within 16px radius │
|
||||
│ - Same floor layer ($0F20) │
|
||||
│ - Not invisible/bunny │
|
||||
└──────────────────┬──────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────┴────────┐
|
||||
│ Probe Hit? │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────────────┼────────────┐
|
||||
│ YES │ NO
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ Trigger │ │ Continue │
|
||||
│ Alert State │ │ Patrol │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ Play alert sound ($1D) │
|
||||
│ Set SprTimerD = $B0 (176 frames) │
|
||||
│ Spawn reinforcement guards │
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Phase 2: Pursuit & Capture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Alert state active (SprTimerD > 0) │
|
||||
└──────────────────┬──────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Guards converge on Link's position │
|
||||
│ - Use Guard_ChaseLinkOnOneAxis │
|
||||
│ - Spawn additional guards from off-screen │
|
||||
│ - Maximum 4 guards active │
|
||||
└──────────────────┬──────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Check capture conditions (every frame) │
|
||||
│ - Link is surrounded (guards on 3+ sides) │
|
||||
│ - Link is not moving (speed = 0) │
|
||||
│ - Link has taken damage from guard │
|
||||
└──────────────────┬──────────────────────────────┘
|
||||
│
|
||||
┌────────┴────────┐
|
||||
│ Captured? │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────────────┼────────────┐
|
||||
│ YES │ NO
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ Initiate │ │ Continue │
|
||||
│ Capture │ │ Chase │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ Phase 3: Warp Sequence │
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Phase 3: Warp Sequence
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Freeze Link (disable input) │
|
||||
│ Play capture animation │
|
||||
│ - Link's sprite changes to "captured" pose │
|
||||
│ - Guards move to surround positions │
|
||||
│ - Screen shake effect (3 frames) │
|
||||
└──────────────────┬──────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Fade out screen ($0012 = $01) │
|
||||
│ Wait 32 frames │
|
||||
└──────────────────┬──────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Call Oracle_CaptureAndWarp │
|
||||
│ - A register = Dungeon entrance ID │
|
||||
│ - Sets game mode to $05 (transition) │
|
||||
│ - Clears Link state │
|
||||
└──────────────────┬──────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Link spawns in dungeon cell │
|
||||
│ - Set SRAM flag: $7EF??? = Captured │
|
||||
│ - Play jingle ($06) │
|
||||
│ - Trigger escape sequence │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Consolidation Plan
|
||||
|
||||
### Step 1: Organize Core Utilities
|
||||
|
||||
**Keep in `Core/capture.asm`:**
|
||||
```asm
|
||||
; Core warp functionality
|
||||
Oracle_CaptureAndWarp:
|
||||
{
|
||||
; (existing implementation)
|
||||
}
|
||||
|
||||
; NEW: Enhanced version with effects
|
||||
Oracle_CaptureAndWarp_Enhanced:
|
||||
{
|
||||
PHP
|
||||
|
||||
; Store entrance ID
|
||||
STA.w $010E
|
||||
|
||||
; Store capture flag in SRAM
|
||||
LDA.b #$01
|
||||
STA.l $7EF3D8 ; Custom flag: Has been captured
|
||||
|
||||
; Play capture sound
|
||||
LDA.b #$1D ; Alert/Capture sound
|
||||
STA.w $012E
|
||||
|
||||
; Fade out screen
|
||||
LDA.b #$01
|
||||
STA.w $0012 ; Request fade
|
||||
|
||||
; Set up timer for transition
|
||||
LDA.b #$40 ; Wait 64 frames
|
||||
STA.b $00
|
||||
|
||||
.wait_fade
|
||||
LDA.b $00 : BNE .wait_fade
|
||||
|
||||
; Execute warp
|
||||
LDA.w $010E ; Get entrance ID back
|
||||
STA.w $010E
|
||||
|
||||
LDA.b #$05 ; Game Mode 05
|
||||
STA.b $10
|
||||
STZ.b $2F
|
||||
STZ.b $5D
|
||||
|
||||
LDA.b #$02
|
||||
STA.b $71
|
||||
|
||||
PLP
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Create Unified Guard Sprite
|
||||
|
||||
**New file: `Sprites/Enemies/castle_guard.asm`**
|
||||
|
||||
This will replace `Sprites/Enemies/custom_guard.asm` with a complete implementation:
|
||||
|
||||
```asm
|
||||
; =========================================================
|
||||
; Castle Guard - Ambush & Capture Variant
|
||||
; =========================================================
|
||||
|
||||
!SPRID = Sprite_CastleGuard ; Use new sprite ID or override vanilla
|
||||
!NbrTiles = 02
|
||||
!Health = 08
|
||||
!Damage = 04
|
||||
; ... other properties ...
|
||||
|
||||
%Set_Sprite_Properties(Sprite_CastleGuard_Prep, Sprite_CastleGuard_Long)
|
||||
|
||||
; States
|
||||
!STATE_PATROL = 0
|
||||
!STATE_ALERT = 1
|
||||
!STATE_CHASE = 2
|
||||
!STATE_CAPTURE = 3
|
||||
|
||||
Sprite_CastleGuard_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_CastleGuard_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .inactive
|
||||
JSR Sprite_CastleGuard_Main
|
||||
.inactive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
|
||||
Sprite_CastleGuard_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
|
||||
; Check if castle ambush is active
|
||||
LDA.l $7EF3D7 : BEQ .no_ambush ; Custom flag: Castle ambush active
|
||||
; Enable enhanced AI
|
||||
LDA.b #$01 : STA.w $0E80, X ; Custom flag for this guard
|
||||
.no_ambush
|
||||
|
||||
; Standard health based on sword level
|
||||
LDA.l $7EF359 : TAY
|
||||
LDA.w .health, Y : STA.w SprHealth, X
|
||||
|
||||
; Enable parrying
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
|
||||
PLB
|
||||
RTL
|
||||
|
||||
.health
|
||||
db $04, $06, $08, $0A
|
||||
}
|
||||
|
||||
Sprite_CastleGuard_Main:
|
||||
{
|
||||
; State machine
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw CastleGuard_Patrol
|
||||
dw CastleGuard_Alert
|
||||
dw CastleGuard_Chase
|
||||
dw CastleGuard_Capture
|
||||
}
|
||||
|
||||
CastleGuard_Patrol:
|
||||
{
|
||||
; Check if castle ambush should activate
|
||||
LDA.l $7EF3D7 : BEQ .normal_patrol
|
||||
|
||||
; Check distance to Link
|
||||
JSL GetDistance8bit_Long : CMP.b #$80 : BCS .no_probe
|
||||
|
||||
; Spawn probe for detection
|
||||
LDA.w SprTimerA, X : BNE .no_probe
|
||||
LDA.b #$20 : STA.w SprTimerA, X ; Spawn every 32 frames
|
||||
JSL Sprite_SpawnProbeAlways_long
|
||||
.no_probe
|
||||
|
||||
; Check if probe triggered alert
|
||||
LDA.w SprTimerD, X : BEQ .normal_patrol
|
||||
; Probe detected Link!
|
||||
LDA.b #!STATE_ALERT : STA.w SprAction, X
|
||||
|
||||
; Play alert sound
|
||||
LDA.b #$1D : STA.w $012E
|
||||
|
||||
; Spawn reinforcements
|
||||
JSR CastleGuard_SpawnReinforcements
|
||||
|
||||
RTS
|
||||
|
||||
.normal_patrol
|
||||
; Standard patrol behavior
|
||||
JSR Guard_StandardPatrol
|
||||
RTS
|
||||
}
|
||||
|
||||
CastleGuard_Alert:
|
||||
{
|
||||
; Transition to chase after alert plays
|
||||
LDA.w SprTimerD, X : CMP.b #$A0 : BCS .stay_alert
|
||||
LDA.b #!STATE_CHASE : STA.w SprAction, X
|
||||
.stay_alert
|
||||
|
||||
; Face Link
|
||||
JSL Sprite_DirectionToFacePlayer : TYA : STA.w SprMiscC, X
|
||||
|
||||
; Draw with alert animation
|
||||
LDA.b #$08 : STA.w SprGfx, X
|
||||
RTS
|
||||
}
|
||||
|
||||
CastleGuard_Chase:
|
||||
{
|
||||
; Move toward Link
|
||||
LDA.b #$0C : JSL Sprite_ApplySpeedTowardsPlayer
|
||||
JSL Guard_ChaseLinkOnOneAxis
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
|
||||
; Check if Link is surrounded
|
||||
JSR CastleGuard_CheckSurrounded : BCC .not_surrounded
|
||||
; Capture Link!
|
||||
LDA.b #!STATE_CAPTURE : STA.w SprAction, X
|
||||
LDA.b #$60 : STA.w SprTimerC, X ; Capture animation duration
|
||||
|
||||
; Freeze Link
|
||||
LDA.b #$17 : STA.b $5D ; Link state: captured
|
||||
STZ.b $67 ; Stop Link's movement
|
||||
RTS
|
||||
|
||||
.not_surrounded
|
||||
; Continue chase
|
||||
JSL Guard_ParrySwordAttacks
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
RTS
|
||||
}
|
||||
|
||||
CastleGuard_Capture:
|
||||
{
|
||||
; Animate capture sequence
|
||||
LDA.w SprTimerC, X : BNE .animating
|
||||
; Capture complete - warp Link
|
||||
LDA.b #$42 ; Entrance ID for dungeon cell
|
||||
JSL Oracle_CaptureAndWarp_Enhanced
|
||||
RTS
|
||||
|
||||
.animating
|
||||
; Guards surround Link
|
||||
; ... capture animation logic ...
|
||||
RTS
|
||||
}
|
||||
|
||||
CastleGuard_CheckSurrounded:
|
||||
{
|
||||
; Count guards within range in each direction
|
||||
; Return carry set if Link is surrounded
|
||||
; (3+ guards within 32px, covering different quadrants)
|
||||
|
||||
; ... implementation ...
|
||||
|
||||
CLC ; Not surrounded
|
||||
RTS
|
||||
}
|
||||
|
||||
CastleGuard_SpawnReinforcements:
|
||||
{
|
||||
; Spawn additional castle guards off-screen
|
||||
LDY.b #$00
|
||||
LDX.b #$0F
|
||||
|
||||
.spawn_loop
|
||||
LDA.w $0DD0, X : BEQ .found_slot
|
||||
DEX : BPL .spawn_loop
|
||||
RTS ; No free slots
|
||||
|
||||
.found_slot
|
||||
; Spawn guard sprite
|
||||
LDA.b #Sprite_CastleGuard : STA.w $0E20, X
|
||||
LDA.b #$09 : STA.w $0DD0, X ; Active state
|
||||
|
||||
; Position off-screen based on Link's position
|
||||
; ... positioning logic ...
|
||||
|
||||
; Set to chase state immediately
|
||||
LDA.b #!STATE_CHASE : STA.w SprAction, X
|
||||
|
||||
INY
|
||||
CPY.b #$03 : BCC .spawn_loop ; Spawn up to 3 reinforcements
|
||||
|
||||
RTS
|
||||
}
|
||||
|
||||
; ... drawing routine ...
|
||||
```
|
||||
|
||||
### Step 3: Integrate with Existing Systems
|
||||
|
||||
**Modify `Sprites/all_sprites.asm`:**
|
||||
```asm
|
||||
org $318000
|
||||
%log_start("castle_guard", !LOG_SPRITES)
|
||||
incsrc "Sprites/Enemies/castle_guard.asm"
|
||||
%log_end("castle_guard", !LOG_SPRITES)
|
||||
```
|
||||
|
||||
**Add SRAM Flags to `Core/sram.asm`:**
|
||||
```asm
|
||||
; Castle Ambush System
|
||||
$7EF3D7 = CastleAmbushActive ; 01 = ambush scenario active
|
||||
$7EF3D8 = HasBeenCaptured ; 01 = player has been captured before
|
||||
$7EF3D9 = CaptureCount ; Number of times captured
|
||||
```
|
||||
|
||||
**Add Constants to `Core/symbols.asm`:**
|
||||
```asm
|
||||
; Castle Ambush
|
||||
CastleAmbushActive = $7EF3D7
|
||||
HasBeenCaptured = $7EF3D8
|
||||
CaptureCount = $7EF3D9
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Plan
|
||||
|
||||
### Test Case 1: Detection
|
||||
- [ ] Enter castle area with ambush flag set
|
||||
- [ ] Walk near guard
|
||||
- [ ] Verify probe spawns every 32 frames
|
||||
- [ ] Walk into probe's path
|
||||
- [ ] Verify alert sound plays
|
||||
- [ ] Verify guard enters alert state
|
||||
|
||||
### Test Case 2: Chase
|
||||
- [ ] Continue from Test Case 1
|
||||
- [ ] Verify guard chases Link
|
||||
- [ ] Verify reinforcements spawn
|
||||
- [ ] Verify multiple guards coordinate
|
||||
- [ ] Verify guards use parrying
|
||||
|
||||
### Test Case 3: Capture
|
||||
- [ ] Let guards surround Link
|
||||
- [ ] Verify capture check works
|
||||
- [ ] Verify Link is frozen
|
||||
- [ ] Verify capture animation plays
|
||||
- [ ] Verify screen fades out
|
||||
|
||||
### Test Case 4: Warp
|
||||
- [ ] Continue from Test Case 3
|
||||
- [ ] Verify Link warps to dungeon
|
||||
- [ ] Verify SRAM flag is set
|
||||
- [ ] Verify Link spawns in correct room
|
||||
- [ ] Verify capture count increments
|
||||
|
||||
### Test Case 5: Escape
|
||||
- [ ] Escape from dungeon cell
|
||||
- [ ] Return to castle
|
||||
- [ ] Verify guards remember previous capture
|
||||
- [ ] Verify harder difficulty on subsequent captures
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase A: Core Functionality (Week 1)
|
||||
- [ ] Clean up `Core/capture.asm`
|
||||
- [ ] Add `Oracle_CaptureAndWarp_Enhanced`
|
||||
- [ ] Test basic warp functionality
|
||||
- [ ] Determine correct entrance ID for dungeon cell
|
||||
|
||||
### Phase B: Guard AI (Week 2)
|
||||
- [ ] Create `Sprites/Enemies/castle_guard.asm`
|
||||
- [ ] Implement probe detection
|
||||
- [ ] Implement state machine
|
||||
- [ ] Test patrol → alert → chase transitions
|
||||
|
||||
### Phase C: Capture Mechanics (Week 3)
|
||||
- [ ] Implement surround check
|
||||
- [ ] Implement capture animation
|
||||
- [ ] Test capture trigger conditions
|
||||
- [ ] Add sound effects
|
||||
|
||||
### Phase D: Integration (Week 4)
|
||||
- [ ] Add SRAM flags
|
||||
- [ ] Integrate with quest system
|
||||
- [ ] Create dungeon escape sequence
|
||||
- [ ] Test full cycle
|
||||
|
||||
### Phase E: Polish (Week 5)
|
||||
- [ ] Add dialogue/cutscenes
|
||||
- [ ] Add visual effects
|
||||
- [ ] Balance difficulty
|
||||
- [ ] Add achievements/tracking
|
||||
|
||||
---
|
||||
|
||||
## Entrance IDs Reference
|
||||
|
||||
Need to determine correct entrance for dungeon cell:
|
||||
|
||||
```asm
|
||||
; Common dungeon entrances
|
||||
$00 = Hyrule Castle (main entrance)
|
||||
$04 = Hyrule Castle (throne room)
|
||||
$0E = Hyrule Castle (dark passage)
|
||||
$20 = Eastern Palace
|
||||
$42 = Dark Palace
|
||||
$?? = Custom dungeon cell (TBD)
|
||||
```
|
||||
|
||||
**Action Required:** Find or create appropriate dungeon cell entrance
|
||||
|
||||
---
|
||||
|
||||
## Files to Create/Modify
|
||||
|
||||
### Create:
|
||||
- [ ] `Sprites/Enemies/castle_guard.asm` - Main guard implementation
|
||||
- [ ] `Docs/Features/CastleAmbush.md` - System documentation
|
||||
- [ ] `Docs/Sprites/Enemies/CastleGuard.md` - Sprite documentation
|
||||
|
||||
### Modify:
|
||||
- [ ] `Core/capture.asm` - Add enhanced version
|
||||
- [ ] `Core/sram.asm` - Add SRAM flags
|
||||
- [ ] `Core/symbols.asm` - Add constants
|
||||
- [ ] `Sprites/all_sprites.asm` - Include castle_guard.asm
|
||||
|
||||
### Delete/Consolidate:
|
||||
- [ ] `Sprites/Enemies/custom_guard.asm` - Consolidate into castle_guard.asm
|
||||
- [ ] Remove duplicate `Oracle_CaptureAndWarp` from custom_guard.asm
|
||||
|
||||
---
|
||||
|
||||
## Questions to Resolve
|
||||
|
||||
1. **Entrance ID:** Which dungeon entrance should be used for the cell?
|
||||
2. **Quest Integration:** When should castle ambush activate?
|
||||
- After certain quest milestone?
|
||||
- When Link enters specific castle area?
|
||||
- Triggered by dialogue/cutscene?
|
||||
3. **Difficulty Scaling:** Should capture difficulty increase after first capture?
|
||||
4. **Escape Sequence:** How should the escape play out?
|
||||
- Find key item?
|
||||
- Stealth section?
|
||||
- Fight way out?
|
||||
5. **Sprite Slot:** New sprite ID or override vanilla guard ($41/$42/$43)?
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- `Docs/Sprites/ProbeSprites.md` - Probe detection system
|
||||
- `Docs/Sprites/Enemies/Darknut.md` - Similar guard-type enemy
|
||||
- `Docs/Guides/SpriteCreationGuide.md` - Sprite creation reference
|
||||
- `Core/capture.asm` - Core warp functionality
|
||||
- `Sprites/experimental/probe.asm` - Probe system reference
|
||||
@@ -1,84 +0,0 @@
|
||||
# Custom Items System
|
||||
|
||||
This document details the functionality of new and modified items in Oracle of Secrets, based on analysis of the `Items/` directory.
|
||||
|
||||
## 1. Overview
|
||||
|
||||
The item roster has been significantly expanded with new mechanics, and many vanilla items have been reworked to provide new functionality. The system is managed through a combination of hooks into the main player state machine and custom routines for each item.
|
||||
|
||||
## 2. Vanilla Item Modifications
|
||||
|
||||
Several items from the original game have been altered.
|
||||
|
||||
### Hookshot / Goldstar
|
||||
|
||||
- **Files:** `goldstar.asm`
|
||||
- **Functionality:** The Hookshot can be upgraded to the **Goldstar**, a powerful morning star weapon. The two items share an inventory slot.
|
||||
- **Switching:** When the Goldstar is obtained, the player can switch between the Hookshot and Goldstar by pressing the L/R shoulder buttons while it is selected in the menu. The current mode is tracked by the `GoldstarOrHookshot` WRAM variable.
|
||||
- **Goldstar Mechanics:** When active, the item functions as a short-range, powerful melee weapon with its own collision and damage properties, distinct from the Hookshot's grappling mechanic.
|
||||
|
||||
### Ice Rod
|
||||
|
||||
- **File:** `ice_rod.asm`
|
||||
- **Functionality:** The Ice Rod's projectile now freezes water tiles it hits, creating temporary 16x16 ice platforms. This allows the player to cross water gaps.
|
||||
- **Implementation:** The `LinkItem_IceRod` routine hooks into the ancilla tile collision logic. When the projectile hits a water tile, it dynamically modifies the tilemap properties in RAM and DMAs new ice graphics to VRAM.
|
||||
|
||||
### Bug-Catching Net -> Roc's Feather
|
||||
|
||||
- **File:** `jump_feather.asm`
|
||||
- **Functionality:** The vanilla Bug-Catching Net has been completely replaced by **Roc's Feather**. This item allows Link to perform a short hop.
|
||||
- **Implementation:** `LinkItem_JumpFeather` initiates the jump by setting Link's state to a recoil/ledge hop state and applying a burst of vertical velocity.
|
||||
|
||||
### Bottles
|
||||
|
||||
- **File:** `bottle_net.asm`
|
||||
- **Functionality:** The Bug-Catching Net is no longer required to catch bees, fairies, etc. The Bottle item now has a dual function:
|
||||
1. If the bottle is **empty**, using it initiates the `LinkItem_CatchBottle` routine, which performs a net-catching swing.
|
||||
2. If the bottle is **full**, using it calls `LinkItem_Bottles`, which consumes the contents (e.g., drinks a potion, releases a fairy).
|
||||
|
||||
### Book of Mudora -> Book of Secrets
|
||||
|
||||
- **File:** `book_of_secrets.asm`
|
||||
- **Functionality:** The Book of Mudora is now the **Book of Secrets**. While its vanilla function of translating Hylian text remains, it has a new secret-revealing capability.
|
||||
- **Implementation:** The `Dungeon_RevealSecrets` routine checks if the L button is held while inside a building. If it is, it disables the `BG2` layer, which can be used to hide secret passages or objects behind walls that are part of that background layer.
|
||||
|
||||
## 3. New Active Items
|
||||
|
||||
### Ocarina
|
||||
|
||||
- **File:** `ocarina.asm`
|
||||
- **Functionality:** A multi-song instrument. When selected, the player can cycle through learned songs using the L/R shoulder buttons. Pressing 'Y' plays the selected song, triggering its unique effect.
|
||||
- **Songs & Effects:**
|
||||
- **Song of Healing:** Heals certain NPCs or triggers quest events.
|
||||
- **Song of Storms:** Toggles a rain overlay on the overworld, which can affect the environment (e.g., watering the Magic Bean).
|
||||
- **Song of Soaring:** Warps the player to pre-defined locations (the vanilla flute's bird travel).
|
||||
- **Song of Time:** Toggles the in-game time between day and night.
|
||||
|
||||
### Shared Slot: Portal Rod & Fishing Rod
|
||||
|
||||
- **Files:** `portal_rod.asm`, `fishing_rod.asm`
|
||||
- **Functionality:** These two distinct items share a single inventory slot. If the player has the upgrade (`$7EF351 >= 2`), they can swap between the two by pressing L/R in the menu.
|
||||
- **Portal Rod:** Fires a projectile that creates a portal sprite (blue or orange). The `Ancilla_HandlePortalCollision` logic detects when another projectile (like an arrow) hits a portal and teleports it to the other portal's location.
|
||||
- **Fishing Rod:** Initiates a fishing minigame. `LinkItem_FishingRod` spawns a "floater" sprite, and the player can reel it in to catch fish or other items from a prize table.
|
||||
|
||||
## 4. New Passive Items
|
||||
|
||||
### Magic Rings
|
||||
|
||||
- **File:** `magic_rings.asm`
|
||||
- **Functionality:** Passive items that grant buffs when equipped in one of the three ring slots in the Quest Status menu. The effects are applied by hooking into various game logic routines.
|
||||
- **Implemented Rings:**
|
||||
- **Power Ring:** Increases sword damage.
|
||||
- **Armor Ring:** Reduces damage taken by half.
|
||||
- **Heart Ring:** Slowly regenerates health over time.
|
||||
- **Light Ring:** Allows the sword to shoot beams even when Link is not at full health (down to -2 hearts from max).
|
||||
- **Blast Ring:** Increases the damage of bombs.
|
||||
- **Steadfast Ring:** Prevents or reduces knockback from enemy hits.
|
||||
|
||||
## 5. Consumable Items (Magic Bag)
|
||||
|
||||
- **File:** `all_items.asm`
|
||||
- **Functionality:** The Magic Bag is a sub-menu that holds new consumable items. The `Link_ConsumeMagicBagItem` routine is a jump table that executes the effect for the selected item.
|
||||
- **Consumables:**
|
||||
- **Banana:** Implemented. Restores a small amount of health (`#$10`).
|
||||
- **Pineapple, Rock Meat, Seashells, Honeycombs, Deku Sticks:** Placeholder entries exist in the jump table, but their effects are not yet implemented (the routines just contain `RTS`).
|
||||
@@ -1,145 +0,0 @@
|
||||
# Mask System
|
||||
|
||||
This document provides a detailed analysis of the Mask System in Oracle of Secrets, based on the code in the `Masks/` directory. The system allows Link to transform into various forms, each with unique graphics, palettes, and abilities.
|
||||
|
||||
## 1. System Architecture
|
||||
|
||||
The Mask System is built around a central WRAM variable and a set of core routines that handle transformations, graphics, and palettes.
|
||||
|
||||
- **`!CurrentMask` (`$02B2`):** A WRAM variable that stores the ID of the currently active mask. A value of `0x00` represents Link's normal human form.
|
||||
- **`!LinkGraphics` (`$BC`):** A WRAM variable that holds the bank number for Link's current graphics sheet. The Mask System changes this value to load the appropriate sprite graphics for each form.
|
||||
|
||||
### Mask IDs
|
||||
|
||||
| ID | Mask / Form |
|
||||
|------|---------------|
|
||||
| `00` | Human (Default) |
|
||||
| `01` | Deku Mask |
|
||||
| `02` | Zora Mask |
|
||||
| `03` | Wolf Mask |
|
||||
| `04` | Bunny Hood |
|
||||
| `05` | Minish Form |
|
||||
| `06` | GBC Form |
|
||||
| `07` | Moosh Form |
|
||||
|
||||
## 2. Core Routines & Hooks (`Masks/mask_routines.asm`)
|
||||
|
||||
A set of shared routines and hooks form the backbone of the system.
|
||||
|
||||
- **`Link_TransformMask`:** This is the primary function for changing forms. It is typically called when the player uses a mask item.
|
||||
- **Trigger:** It requires a new R-button press (`CheckNewRButtonPress`) to prevent rapid toggling.
|
||||
- **Logic:** It takes a mask ID in the A register. If the requested mask is already active, it reverts Link to his human form. Otherwise, it sets `!CurrentMask`, updates the graphics bank in `$BC` from a lookup table, and calls `Palette_ArmorAndGloves` to apply the new look.
|
||||
- **Effect:** It spawns a "poof" of smoke (`AddTransformationCloud`) and plays a sound effect.
|
||||
|
||||
- **`Palette_ArmorAndGloves` (Hook):** This routine hooks the vanilla palette loading function (`$1BEDF9`). It checks `!CurrentMask` and jumps to the appropriate `Update...Palette` routine for the active form, ensuring the correct colors are loaded. If no mask is active, it proceeds with the vanilla logic for loading Link's tunic color.
|
||||
|
||||
- **`LinkItem_CheckForSwordSwing_Masks` (Hook):** This routine hooks the vanilla sword swing check (`$079CD9`). It prevents certain forms (Deku, Wolf, Minish, Moosh) from using the sword, while allowing others (Zora, GBC Link) to use it freely.
|
||||
|
||||
- **Reset Routines:**
|
||||
- `ResetToLinkGraphics`: Reverts Link to his default graphics and `!CurrentMask = 0`.
|
||||
- `ForceResetMask_GameOver` / `ForceResetMask_SaveAndQuit`: Hooks into the game over and save/quit routines to ensure Link's form is reset before the game saves or restarts.
|
||||
|
||||
## 3. Transformation Masks
|
||||
|
||||
These masks grant Link new forms with significant new abilities.
|
||||
|
||||
### Deku Mask
|
||||
|
||||
- **File:** `Masks/deku_mask.asm`
|
||||
- **Transformation:** Replaces the Quake Medallion. Pressing 'Y' with the item selected transforms Link.
|
||||
- **Abilities:**
|
||||
- **Spin Attack:** Pressing 'Y' performs a spinning attack.
|
||||
- **Deku Bubble:** If not on a Deku Flower, the spin attack also shoots a bubble projectile (`Ancilla0E_MagicBubble`).
|
||||
- **Hover:** If standing on a Deku Flower (tile property check sets WRAM `$71`), the spin attack launches Link into the air, allowing him to hover for a short time.
|
||||
- **Bomb Drop:** While hovering, pressing 'Y' drops a bomb.
|
||||
- **Cancel Hover:** Pressing 'B' or letting the timer expire cancels the hover.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x01`
|
||||
- `DekuFloating` (`$70`): Flag set when Link is hovering.
|
||||
- `DekuHover` (`$71`): Flag set when Link is standing on a Deku Flower, enabling the hover ability.
|
||||
- **Code Interactions:**
|
||||
- Hooks `LinkItem_Quake` (`$07A64B`).
|
||||
- Repurposes Link's "Using Quake Medallion" state (`$5D = 0x0A`) for the hover ability.
|
||||
- Hooks `LinkOAM_DrawShield` (`$0DA780`) to prevent the shield from being drawn.
|
||||
|
||||
### Zora Mask
|
||||
|
||||
- **File:** `Masks/zora_mask.asm`
|
||||
- **Transformation:** Replaces the Bombos Medallion. Pressing 'Y' with the item selected transforms Link.
|
||||
- **Abilities:**
|
||||
- **Diving:** Allows Link to dive in deep water by pressing 'Y'. Pressing 'Y' again resurfaces.
|
||||
- **Overworld Diving:** When diving in the overworld, Link becomes invincible, moves faster, and is hidden beneath a ripple effect.
|
||||
- **Dungeon Diving:** When diving in a dungeon, Link moves to the lower layer (`$EE=0`), allowing him to swim under floors and obstacles.
|
||||
- **Sword:** The Zora form can use the sword.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x02`
|
||||
- `!ZoraDiving` (`$0AAB`): Flag set when Link is currently underwater.
|
||||
- **Code Interactions:**
|
||||
- Hooks `LinkItem_Bombos` (`$07A569`).
|
||||
- Hooks the end of `LinkState_Swimming` (`$079781`) to handle the dive input.
|
||||
- Hooks the end of `LinkState_Default` (`$0782D2`) to handle resurfacing in dungeons.
|
||||
- Hooks `Link_HopInOrOutOfWater_Vertical` (`$07C307`) to reset the dive state when using water stairs.
|
||||
|
||||
### Wolf Mask
|
||||
|
||||
- **File:** `Masks/wolf_mask.asm`
|
||||
- **Transformation:** Shares an item slot with the Flute. When selected, it replaces the Shovel. Pressing 'Y' transforms Link.
|
||||
- **Abilities:**
|
||||
- **Dig:** When transformed, pressing 'Y' executes the vanilla `LinkItem_Shovel` routine, allowing Wolf Link to dig for items without needing the shovel.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x03`
|
||||
- **Code Interactions:**
|
||||
- Hooks the `LinkItem_Shovel` vector (`$07A313`) to a new `LinkItem_ShovelAndFlute` routine that dispatches between the Flute and Wolf Mask logic based on the selected item (`$0202`).
|
||||
|
||||
### Minish Form
|
||||
|
||||
- **File:** `Masks/minish_form.asm`
|
||||
- **Transformation:** Context-sensitive. When standing on a special portal tile (`ID 64`), pressing 'R' transforms Link into Minish form. Pressing 'R' on the portal again reverts him.
|
||||
- **Abilities:**
|
||||
- **Access Minish Areas:** Allows Link to pass through special small openings (`Tile ID 65`).
|
||||
- **Restricted Actions:** Cannot lift objects.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x05`
|
||||
- **Code Interactions:**
|
||||
- Hooks the overworld (`$07DAF2`) and underworld (`$07D8A0`) tile collision tables to add handlers for the portal and passage tiles.
|
||||
- Hooks the lift check (`$079C32`) to disable lifting while in Minish form.
|
||||
|
||||
### Moosh Form
|
||||
|
||||
- **File:** `Masks/moosh.asm`
|
||||
- **Transformation:** The trigger for transforming into Moosh is not defined within the mask's own file, but is handled by `Link_TransformMoosh`.
|
||||
- **Abilities:**
|
||||
- **Hover Dash:** Attempting to use the Pegasus Boots (dash) while in Moosh form will instead trigger a short hover, similar to the Deku Mask's ability.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x07`
|
||||
- **Code Interactions:**
|
||||
- Hooks the dash initiation logic (`$079093`) to intercept the dash and call `PrepareQuakeSpell`, which sets Link's state to `0x0A` (hover).
|
||||
- Shares the hover/recoil animation logic with the Deku Mask.
|
||||
|
||||
## 4. Passive & Cosmetic Forms
|
||||
|
||||
### Bunny Hood
|
||||
|
||||
- **File:** `Masks/bunny_hood.asm`
|
||||
- **Transformation:** Replaces the Ether Medallion. Pressing 'Y' activates the Bunny Hood state. This is a state change, not a visual transformation.
|
||||
- **Abilities:**
|
||||
- **Increased Speed:** While the Bunny Hood is the active mask (`!CurrentMask = 0x04`), Link's movement speed is increased across various actions (walking, carrying, etc.). The specific speed values are defined in `BunnySpeedTable`.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x04`
|
||||
- **Code Interactions:**
|
||||
- Hooks `LinkItem_Ether` (`$07A494`) to trigger the state change.
|
||||
- Hooks the velocity calculation in the player engine (`$07E330`) to load custom speed values from a table.
|
||||
|
||||
### GBC Form
|
||||
|
||||
- **File:** `Masks/gbc_form.asm`
|
||||
- **Transformation:** An automatic, cosmetic transformation that occurs whenever Link is in the Dark World.
|
||||
- **Abilities:**
|
||||
- Changes Link's graphics to a Game Boy Color-inspired sprite.
|
||||
- Applies a unique, limited-color palette. The palette correctly reflects Link's current tunic (Green, Blue, or Red).
|
||||
- This form can still use the sword.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x06`
|
||||
- `$0FFF`: The vanilla Dark World flag.
|
||||
- **Code Interactions:**
|
||||
- Hooks numerous overworld, underworld, and transition routines to consistently apply the effect when in the Dark World and remove it when in the Light World.
|
||||
@@ -1,77 +0,0 @@
|
||||
# Custom Menu & HUD System
|
||||
|
||||
This document provides a detailed analysis of the custom menu and Heads-Up Display (HUD) systems in Oracle of Secrets, based on the code in the `Menu/` directory.
|
||||
|
||||
## 1. Overview
|
||||
|
||||
The project features a completely custom menu and HUD, replacing the vanilla systems. The menu is a robust, multi-screen system, while the HUD provides a clean, modern interface for in-game stats.
|
||||
|
||||
- **Menu System**: A two-page design that separates selectable items from quest status and equipment. It is accessible by pressing the Start button.
|
||||
- **HUD System**: A persistent on-screen display for health, magic, rupees, and the currently equipped item.
|
||||
|
||||
## 2. Menu System Architecture
|
||||
|
||||
The entire menu operates as a large state machine, with the main entry point being `Menu_Entry` in `Menu/menu.asm`. The flow is controlled by the value in WRAM `$0200`.
|
||||
|
||||
### 2.1. Main State Machine
|
||||
|
||||
The `Menu_Entry` routine uses a jump table (`.vectors`) to execute different subroutines based on the state in `$0200`. This modular approach allows for a clean separation of tasks like initialization, drawing, input handling, and screen transitions.
|
||||
|
||||
**Key States in `$0200`:**
|
||||
|
||||
| State ID | Label | Purpose |
|
||||
|----------|----------------------------|-------------------------------------------------------------------------|
|
||||
| `0x00` | `Menu_InitGraphics` | Initializes the menu, clears player state, and prepares for drawing. |
|
||||
| `0x01` | `Menu_UploadRight` | Draws the entire right-hand screen (Quest Status). |
|
||||
| `0x02` | `Menu_UploadLeft` | Draws the entire left-hand screen (Item Selection). |
|
||||
| `0x04` | `Menu_ItemScreen` | The main interactive state for the Item screen. Handles cursor movement. |
|
||||
| `0x05` | `Menu_ScrollTo` | Handles the smooth scrolling animation when moving from Items to Quest. |
|
||||
| `0x06` | `Menu_StatsScreen` | The main interactive state for the Quest Status screen. |
|
||||
| `0x0A` | `Menu_Exit` | Exits the menu, restores the game state, and updates the equipped item. |
|
||||
| `0x0C` | `Menu_MagicBag` | A sub-menu for viewing collectible items. |
|
||||
| `0x0D` | `Menu_SongMenu` | A sub-menu for selecting Ocarina songs. |
|
||||
| `0x0E` | `Menu_Journal` | A sub-menu for reading the player's journal. |
|
||||
| `0x09` | `Menu_RingBox` | A sub-menu for managing magic rings. |
|
||||
|
||||
### 2.2. Item Selection Screen (Left Page)
|
||||
|
||||
This is the primary interactive screen where the player selects their Y-button item.
|
||||
|
||||
- **Drawing (`DrawYItems`):** This routine is responsible for rendering all 24 item slots. It reads the SRAM address for each slot from `Menu_AddressIndex` (`menu_select_item.asm`), checks if the player owns the item, and then calls `DrawMenuItem`.
|
||||
- **`DrawMenuItem`:** This generic function is the core of the drawing system. It takes an item's SRAM value (e.g., Sword level 0-4) and uses it to look up the correct 16x16 tile data from a large graphics table in `Menu/menu_gfx_table.asm`. This makes the menu highly data-driven.
|
||||
- **Selection (`menu_select_item.asm`):** Cursor movement is handled by `Menu_FindNextItem`, `Menu_FindPrevItem`, etc. These routines intelligently skip over empty slots, ensuring the cursor always lands on a valid item. The currently selected slot index is stored in `$0202`.
|
||||
|
||||
### 2.3. Quest Status Screen (Right Page)
|
||||
|
||||
This screen is a static display of the player's overall progress.
|
||||
|
||||
- **Drawing:** It is rendered by a series of functions in `menu_draw.asm`, including:
|
||||
- `Menu_DrawQuestItems`: Draws equipped sword, shield, tunic, etc.
|
||||
- `Menu_DrawPendantIcons` & `Menu_DrawTriforceIcons`: Reads SRAM flags to draw collected pendants and crystals.
|
||||
- `Menu_DrawCharacterName`: Reads the player's name from SRAM and renders it.
|
||||
- `DrawLocationName`: Reads the current overworld area (`$008A`) or underworld room (`$00A0`) and looks up the corresponding name from the tables in `menu_map_names.asm`.
|
||||
|
||||
## 3. HUD System Architecture
|
||||
|
||||
The HUD is a separate system that hooks the vanilla game's NMI rendering routines. Its main entry point is `HUD_Update` in `Menu/menu_hud.asm`.
|
||||
|
||||
- **Functionality:** The `HUD_Update` routine runs every frame during gameplay. It reads player stats directly from SRAM and WRAM and draws them to the VRAM buffer for the top of the screen.
|
||||
- **Key Drawing Logic:**
|
||||
- **Hearts:** `HUD_UpdateHearts` is a loop that draws empty hearts based on `MAXHP` (`$7EF36C`) and then overlays full/partial hearts based on `CURHP` (`$7EF36D`).
|
||||
- **Magic Meter:** It reads `MagicPower` (`$7EF36E`) and uses the `MagicTilemap` lookup table to find the correct tiles to display the green bar.
|
||||
- **Counters:** It uses a `HexToDecimal` routine to convert the values for Rupees, Bombs, and Arrows into drawable digits.
|
||||
- **Equipped Item:** `HUD_UpdateItemBox` reads the currently equipped item index (`$0202`), finds its graphics data in the `HudItems` table, and draws the icon in the top-left box.
|
||||
|
||||
## 4. Data-Driven Design & Areas for Improvement
|
||||
|
||||
The entire menu and HUD are heavily data-driven, which is a major strength.
|
||||
|
||||
- **Graphics:** All item icons for both the menu and HUD are defined in data tables in `menu_gfx_table.asm` and `menu_hud.asm`, not hardcoded.
|
||||
- **Item Layout:** The position and SRAM address of every item in the menu are defined in the `Menu_ItemCursorPositions` and `Menu_AddressIndex` tables, allowing the layout to be easily changed.
|
||||
- **Text:** Item names, location names, and other text are all stored in data tables in `menu_text.asm` and `menu_map_names.asm`.
|
||||
|
||||
This analysis confirms the suggestions in the placeholder `Menu.md` file:
|
||||
|
||||
1. **Refactor Redundant Code:** The input handling logic for the Magic Bag, Song Menu, and Ring Box is nearly identical and is a prime candidate for being refactored into a single, reusable subroutine.
|
||||
2. **Use `table` for Jump Tables:** The main `Menu_Entry` jump table is created with manual `dw` directives and would be cleaner and safer if generated with asar's `table` directive.
|
||||
3. **Replace Hardcoded Values:** Hardcoded state values (e.g., `LDA.b #$0C : STA.w $0200`) should be replaced with named constants (`!MENU_STATE_MAGIC_BAG = $0C`) for readability and maintainability.
|
||||
@@ -1,137 +0,0 @@
|
||||
# Music Creation Guide
|
||||
|
||||
This document details the process for creating and integrating custom music into Oracle of Secrets. The project uses the native Super Nintendo Packet Chip (N-SPC) music format, abstracted through a powerful set of `asar` macros.
|
||||
|
||||
## 1. N-SPC Music Format Primer
|
||||
|
||||
Music in the N-SPC engine is structured around eight independent channels. Each channel is a stream of bytes that are read sequentially. The stream consists of two types of data:
|
||||
|
||||
- **Commands:** Special bytes (from `$E0` to `$FF`) that control aspects of the sound, such as setting the instrument, changing volume, or calling a subroutine.
|
||||
- **Notes:** A note consists of a duration byte followed by one or more tone bytes. The duration byte (e.g., `$48` for a quarter note) determines how long the following tone(s) will play.
|
||||
|
||||
## 2. The Macro System (`Core/music_macros.asm`)
|
||||
|
||||
To make composing more intuitive, the project uses a comprehensive library of macros that wrap the raw N-SPC commands into readable names. All music files must include `Core/music_macros.asm`.
|
||||
|
||||
### Key Concepts
|
||||
|
||||
- **Note Durations:** Constants are defined for standard note lengths (e.g., `!4th`, `!8th`, `!16th`).
|
||||
- **Note Tones:** Constants are defined for all notes across several octaves (e.g., `C4`, `G4s` for G#4, `A5`).
|
||||
- **Special Notes:** `Tie` (`$C8`) continues the previous note for the new duration, and `Rest` (`$C9`) signifies silence.
|
||||
|
||||
### Core Macros
|
||||
|
||||
- **`%SetInstrument(id)`:** Sets the instrument for the current channel (e.g., `%SetInstrument($09)` for Strings). Helper macros like `%Strings()`, `%Piano()`, etc., exist for common instruments.
|
||||
- **`%SetTempo(value)`:** Sets the overall playback speed of the song.
|
||||
- **`%SetMasterVolume(value)` / `%SetChannelVolume(value)`:** Sets the volume for the entire song or just the current channel.
|
||||
- **`%CallSubroutine(address, repeats)`:** The most important macro for structuring songs. It jumps to a labeled subroutine, plays it `repeats+1` times, and then returns. **This is the primary method for looping musical phrases.**
|
||||
- **`%VibratoOn(delay, rate, depth)`:** Adds a vibrato effect.
|
||||
- **`%TremoloOn(delay, rate, depth)`:** Adds a tremolo (volume fluctuation) effect.
|
||||
- **`%SetPan(value)`:** Sets the stereo position (left/right) of the channel.
|
||||
- **`%EchoVBits(switch, left, right)`:** Enables and configures echo for the channel.
|
||||
|
||||
## 3. Song File Structure
|
||||
|
||||
Every song `.asm` file follows a standard structure.
|
||||
|
||||
#### 1. Header
|
||||
|
||||
The file begins with a header that defines metadata for the song engine.
|
||||
|
||||
```asm
|
||||
MyNewSong:
|
||||
!ARAMAddr = $D86A ; Base address in ARAM for this song
|
||||
dw !ARAMAddr+$0A ; Pointer to the Intro section
|
||||
dw !ARAMAddr+$1A ; Pointer to the Main (looping) section
|
||||
dw $00FF ; Default fade-in
|
||||
dw !ARAMAddr+$02 ; Start of the looping section data
|
||||
dw $0000
|
||||
```
|
||||
|
||||
#### 2. Channel Pointers
|
||||
|
||||
Next is a table of pointers to each of the eight channel data blocks. The `!ARAMC` constant is used to make these pointers relative to the song's ARAM address.
|
||||
|
||||
```asm
|
||||
.Channels
|
||||
!ARAMC = !ARAMAddr-MyNewSong
|
||||
dw .Channel0+!ARAMC
|
||||
dw .Channel1+!ARAMC
|
||||
; ...up to 8 channels, use dw $0000 for unused channels
|
||||
```
|
||||
|
||||
#### 3. Channel Data
|
||||
|
||||
Each channel is a block of code containing commands and notes.
|
||||
|
||||
```asm
|
||||
.Channel0
|
||||
%SetMasterVolume($DA)
|
||||
%SetTempo(62)
|
||||
%SetInstrument($02) ; Tympani
|
||||
%SetDurationN(!4th, $7F)
|
||||
%CallSubroutine(.sub1+!ARAMC, 23) ; Call subroutine .sub1 24 times
|
||||
db End ; $00, signifies end of channel data
|
||||
```
|
||||
|
||||
#### 4. Subroutines
|
||||
|
||||
The bulk of a song is made of small, labeled subroutines containing musical phrases. These are placed after the channel data.
|
||||
|
||||
```asm
|
||||
.sub1
|
||||
db !4th, B1, B1, !8th, Tie, C2, !4th, F3s
|
||||
db End ; Subroutines must also end with $00
|
||||
```
|
||||
|
||||
## 4. How to Add a New Song
|
||||
|
||||
1. **Create the File:** Create a new `.asm` file in the `Music/` directory.
|
||||
2. **Copy Template:** Copy the contents of an existing song (e.g., `stone_tower_temple_v2.asm`) into your new file to use as a template.
|
||||
3. **Set Header:** Change the main label (e.g., `MyNewSong:`) and set the `!ARAMAddr`. This address must be unique and not conflict with other songs.
|
||||
4. **Compose:** Write your music in the channel and subroutine blocks using the note constants and macros.
|
||||
5. **Integrate the Song:**
|
||||
- Open `Music/all_music.asm` and add an `incsrc` for your new file.
|
||||
- To replace a vanilla song, find its label in the ROM map and use `org` to place your new song at that address. For example, to replace the Lost Woods theme:
|
||||
```asm
|
||||
org $1AADDE ; Original address of Lost Woods theme
|
||||
incsrc "Music/MyNewSong.asm"
|
||||
```
|
||||
- To add a new song to the expanded Dark World bank, open `Music/expanded.asm` and add a new entry to the `SongBank_OverworldExpanded_Main` table.
|
||||
|
||||
## 5. Proposals for Improved Organization
|
||||
|
||||
The current system is functional but can be made more readable and maintainable.
|
||||
|
||||
1. **Standardize Subroutine Naming:** The current convention of `.sub1`, `.sub101`, etc., is ambiguous. A clearer naming scheme would greatly improve readability.
|
||||
- **Proposal:** Name subroutines based on their musical function, like `.MelodyVerseA`, `.BasslineIntro`, `.PercussionFill1`. This makes the main channel blocks easier to read as a high-level song structure.
|
||||
|
||||
2. **Create a Common Patterns Library:** Many songs use similar rhythmic or melodic patterns (e.g., a standard 4/4 drum beat, an arpeggiated chord).
|
||||
- **Proposal:** Create a `Music/common_patterns.asm` file. This file could contain a library of generic, reusable subroutines for things like drum patterns, basslines, or common arpeggios. Songs could then `incsrc` this library and call these patterns, reducing code duplication and speeding up composition.
|
||||
|
||||
3. **Develop Advanced Composition Macros:** The existing helper macros are basic. More advanced macros could abstract away the manual process of defining and calling subroutines.
|
||||
- **Proposal:**
|
||||
- `%DefineMeasure(Name, Notes...)`: A macro that takes a name and a list of notes and automatically creates a correctly formatted subroutine block.
|
||||
- `%PlayMeasure(Name, Repeats)`: A macro that automatically calculates the relative address (`+!ARAMC`) and calls `%CallSubroutine`.
|
||||
|
||||
- **Example Workflow with Proposed Macros:**
|
||||
```asm
|
||||
; --- Subroutine Definitions ---
|
||||
%DefineMeasure(VerseMelody, !8th, C4, D4, E4, F4, G4, A4, B4, C5)
|
||||
%DefineMeasure(VerseBass, !4th, C2, G2, A2, F2)
|
||||
|
||||
; --- Channel Data ---
|
||||
.Channel0
|
||||
; ... setup ...
|
||||
%PlayMeasure(VerseMelody, 4) ; Plays the melody 4 times
|
||||
db End
|
||||
|
||||
.Channel1
|
||||
; ... setup ...
|
||||
%PlayMeasure(VerseBass, 4) ; Plays the bassline 4 times
|
||||
db End
|
||||
```
|
||||
This approach would make the main channel data blocks read like a high-level song arrangement, significantly improving clarity.
|
||||
|
||||
4. **Improve In-File Documentation:**
|
||||
- **Proposal:** Encourage the use of comments to label major song sections directly within the channel data (e.g., `; --- VERSE 1 ---`, `; --- CHORUS ---`). This provides crucial signposting when navigating complex song files.
|
||||
848
Docs/GEMINI.md
848
Docs/GEMINI.md
@@ -1,848 +0,0 @@
|
||||
# Gemini Development Guidelines for Oracle of Secrets
|
||||
|
||||
This document outlines the established coding conventions, architectural patterns, and best practices observed in the Oracle of Secrets project. Adhering to these guidelines will ensure consistency and maintainability.
|
||||
|
||||
## 1. SNES 65816 Processor Basics
|
||||
|
||||
### 1.1. Architecture Overview
|
||||
|
||||
The 65816 is an 8/16-bit microprocessor used in the Super Nintendo Entertainment System (SNES). It operates in two modes: emulation mode (6502-compatible, 8-bit) and native mode (65816, 16-bit). The SNES typically runs in native mode.
|
||||
|
||||
### 1.2. Key Registers
|
||||
|
||||
- **A (Accumulator):** The primary register for data manipulation. Its size (8-bit or 16-bit) is controlled by the M flag in the Processor Status Register.
|
||||
- **X, Y (Index Registers):** Used for addressing memory. Their size (8-bit or 16-bit) is controlled by the X flag in the Processor Status Register.
|
||||
- **S (Stack Pointer):** Points to the current top of the stack.
|
||||
- **D (Direct Page Register):** Used for direct page addressing, allowing faster access to the first 256 bytes of each bank.
|
||||
- **DB (Data Bank Register):** Specifies the current 64KB data bank for memory accesses.
|
||||
- **PB (Program Bank Register):** Specifies the current 64KB program bank for instruction fetches.
|
||||
- **P (Processor Status Register):** A crucial 8-bit register containing various flags:
|
||||
- **N (Negative):** Set if the result of an operation is negative.
|
||||
- **V (Overflow):** Set if an arithmetic overflow occurs.
|
||||
- **M (Memory/Accumulator Select):** Controls the size of the A register (0=16-bit, 1=8-bit).
|
||||
- **X (Index Register Select):** Controls the size of the X and Y registers (0=16-bit, 1=8-bit).
|
||||
- **D (Decimal Mode):** Enables BCD arithmetic (rarely used in SNES development).
|
||||
- **I (IRQ Disable):** Disables interrupt requests.
|
||||
- **Z (Zero):** Set if the result of an operation is zero.
|
||||
- **C (Carry):** Used for arithmetic operations and bit shifts.
|
||||
|
||||
### 1.3. Processor Status Register (P) Manipulation
|
||||
|
||||
- **`SEP #$20` (or `SEP #$30`):** Sets the M flag (and X flag if $30 is used) to 1, switching A (and X/Y) to 8-bit mode.
|
||||
- **`REP #$20` (or `REP #$30`):** Resets the M flag (and X flag if $30 is used) to 0, switching A (and X/Y) to 16-bit mode.
|
||||
- **Importance:** Mismatched M/X flags between calling and called routines are a common cause of crashes (BRKs) or unexpected behavior. Always ensure the P register is in the expected state for a given routine, or explicitly set it.
|
||||
|
||||
**Practical Example:**
|
||||
```asm
|
||||
; INCORRECT: Size mismatch causes corruption
|
||||
REP #$20 ; A = 16-bit
|
||||
LDA.w #$1234 ; A = $1234
|
||||
|
||||
SEP #$20 ; A = 8-bit now
|
||||
LDA.w #$1234 ; ERROR: Assembler generates LDA #$34, $12 becomes opcode!
|
||||
|
||||
; CORRECT: Match processor state to operation
|
||||
REP #$20 ; A = 16-bit
|
||||
LDA.w #$1234 ; A = $1234
|
||||
|
||||
SEP #$20 ; A = 8-bit
|
||||
LDA.b #$12 ; Load only 8-bit value
|
||||
```
|
||||
|
||||
**Best Practice:**
|
||||
```asm
|
||||
MyFunction:
|
||||
PHP ; Save caller's processor state
|
||||
SEP #$30 ; Set to known 8-bit state
|
||||
; ... your code here ...
|
||||
PLP ; Restore caller's processor state
|
||||
RTL
|
||||
```
|
||||
|
||||
See `Docs/General/Troubleshooting.md` Section 3 for comprehensive processor state troubleshooting.
|
||||
|
||||
### 1.4. Memory Mapping
|
||||
|
||||
- The SNES has a 24-bit address space, allowing access to up to 16MB of ROM/RAM.
|
||||
- **Banks:** Memory is organized into 256 banks of 64KB each.
|
||||
- **Direct Page (Bank 00):** The first 256 bytes of bank 00 (`$0000-$00FF`) are special and can be accessed quickly using direct page addressing (when D=0).
|
||||
- **WRAM (Work RAM):** Located in banks $7E-$7F. This is where most game variables and temporary data are stored.
|
||||
- **SRAM (Save RAM):** Typically located in banks $70-$7D, used for saving game progress.
|
||||
|
||||
## 2. Asar Best Practices
|
||||
|
||||
### 2.1. `pushpc`/`pullpc` and `org`
|
||||
|
||||
- **Guideline:** While `pushpc`/`pullpc` is good for isolating small, targeted patches, extreme care must be taken. Patches that use `org` to place new code into freespace (e.g., `org $2B8000`) have a dependency on their location within the file. Moving these `org` blocks can break the ROM by changing the memory layout.
|
||||
- **Rationale:** The order of `incsrc` and `org` directives determines the final ROM layout. Moving a freespace `org` block to a central `patches.asm` file changes this order and will likely cause errors. Simple, single-line patches that modify existing vanilla code can often be moved, but larger blocks of new code should remain in their contextually relevant files.
|
||||
|
||||
### 2.2. Scoping and Style
|
||||
|
||||
- **Guideline:** The established code style uses labels followed by `{}` brackets to define scope for new blocks of logic. This convention must be followed. The `subroutine`/`endsubroutine` keywords are explicitly *not* to be used in this project.
|
||||
- **Rationale:** The `subroutine`/`endsubroutine` keywords are not used in this project. Maintaining a consistent style is crucial for readability.
|
||||
|
||||
### 2.3. Data Organization
|
||||
|
||||
- **Guideline:** For complex, related data (like sprite state or system configurations), use `struct`. For jump tables or data arrays, use `table`.
|
||||
- **Rationale:** These directives make data structures explicit and readable. They replace confusing pointer arithmetic and manual offset calculations with clear, named accessors, which is less error-prone.
|
||||
|
||||
### 2.4. Define Constants for Magic Numbers
|
||||
|
||||
- **Guideline:** Avoid hardcoding numerical values. Use `!` or `define()` to create named constants for RAM/SRAM addresses, item IDs, sprite states, tile IDs, etc.
|
||||
- **Rationale:** This makes the code self-documenting and significantly easier to maintain and debug.
|
||||
|
||||
### 2.5. Opcode Size Suffixes (.b, .w, .l)
|
||||
|
||||
`asar` can often infer operand sizes, but relying on this can lead to bugs when the processor state (M and X flags) is not what you expect. To write robust, readable, and safe code, you should use explicit size suffixes.
|
||||
|
||||
- **`.b` (byte):** Forces an 8-bit operation. Use this when you are certain you are working with a single byte.
|
||||
- Example: `LDA.b $7E0010` will correctly load a single byte into the accumulator, regardless of the M flag's state.
|
||||
- **`.w` (word):** Forces a 16-bit operation. Use this when working with two bytes (a word).
|
||||
- Example: `LDA.w $7E0022` will load a 16-bit value. This is essential for correctness if the M flag is 1 (8-bit mode).
|
||||
- **`.l` (long):** Forces a 24-bit operation, typically for addresses in `JML` or `JSL`.
|
||||
- Example: `JSL.l SomeRoutineInAnotherBank`
|
||||
|
||||
**Golden Rule:** A mismatch between the M/X flags and the intended operation size is a primary cause of crashes. When in doubt, wrap your code in `REP`/`SEP` to explicitly set the processor state, and use size suffixes to make your intent clear to both the assembler and future developers.
|
||||
|
||||
## 3. Project-Specific Conventions
|
||||
|
||||
### 3.1. File & Directory Structure
|
||||
|
||||
- The project is well-organized by functionality (`Core`, `Items`, `Sprites`, `Overworld`, etc.). New code should be placed in the appropriate directory.
|
||||
- Central include files (e.g., `all_items.asm`, `all_sprites.asm`) are used to aggregate modules. This is a good pattern to continue.
|
||||
|
||||
### 3.2. Patch Management
|
||||
|
||||
- **Guideline:** Hooks and patches should be placed logically near the code they relate to, not centralized in `Core/patches.asm`. This improves code organization, maintainability, and context preservation.
|
||||
- **Rationale:** When a hook modifies vanilla behavior to add custom functionality, placing the hook in the same file as the custom implementation keeps related code together. This makes it easier to understand the complete feature, debug issues, and maintain the codebase.
|
||||
- **Exception:** Only truly generic, cross-cutting patches that don't belong to any specific feature should be considered for `Core/patches.asm`.
|
||||
|
||||
### 3.3. Debugging
|
||||
|
||||
- The `!DEBUG` flag and `%print_debug()` macro in `Util/macros.asm` should be used for all build-time logging. This allows for easy enabling/disabling of diagnostic messages.
|
||||
|
||||
### 3.4. Referencing Vanilla Code (`usdasm`)
|
||||
|
||||
- When hooking or modifying vanilla code, it is essential to understand the original context. The `usdasm` disassembly is the primary reference for this.
|
||||
- To find the original code for a patch at a given address (e.g., `$07A3DB`), you can search for the SNES address in the `usdasm` files (e.g., `#_07A3DB:`).
|
||||
- **Vanilla labels are not included by default.** The `usdasm` project is a reference, not part of the build. If you need to call a vanilla routine, you must find its implementation in the disassembly and explicitly copy or recreate it within the `Oracle of Secrets` source, giving it a new label (e.g., inside the `Oracle` namespace).
|
||||
- **Disassembly files are for reference only.** Never modify any files within the `usdasm` directory. All changes must be made within the `Oracle of Secrets` project files.
|
||||
|
||||
### 3.5. Namespacing
|
||||
|
||||
- **Guideline:** The majority of the *Oracle of Secrets* codebase is organized within an `Oracle` namespace, as defined in `Oracle_main.asm`. However, some modules, notably `ZSCustomOverworld.asm`, are included *outside* of this namespace.
|
||||
- **Interaction:** To call a function that is inside the `Oracle` namespace from a file that is outside of it (like `ZSCustomOverworld.asm`), you must prefix the function's label with `Oracle_`. For example, to call the `CheckIfNight16Bit` function (defined inside the namespace), you must use `JSL Oracle_CheckIfNight16Bit`.
|
||||
- **Rationale:** The build process correctly resolves these `Oracle_` prefixed labels to their namespaced counterparts (e.g., `Oracle.CheckIfNight16Bit`). Do not add the `Oracle_` prefix to the original function definition; it is only used by the calling code outside the namespace.
|
||||
|
||||
**Practical Example - Oracle to ZScream:**
|
||||
```asm
|
||||
// In ZScream file (no namespace):
|
||||
LoadOverworldSprites_Interupt:
|
||||
{
|
||||
; ZScream code here
|
||||
RTL
|
||||
}
|
||||
|
||||
// Export to Oracle namespace:
|
||||
namespace Oracle
|
||||
{
|
||||
Oracle_LoadOverworldSprites_Interupt = LoadOverworldSprites_Interupt
|
||||
}
|
||||
|
||||
// Now Oracle code can call it:
|
||||
namespace Oracle
|
||||
{
|
||||
MyFunction:
|
||||
JSL Oracle_LoadOverworldSprites_Interupt ; Use prefix!
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
**Practical Example - ZScream to Oracle (Bridge Pattern):**
|
||||
```asm
|
||||
// Oracle implementation:
|
||||
namespace Oracle
|
||||
{
|
||||
CheckIfNight:
|
||||
LDA.l $7EE000 ; Check time system
|
||||
; ... logic ...
|
||||
RTL
|
||||
}
|
||||
|
||||
// Bridge function (no namespace):
|
||||
ZSO_CheckIfNight:
|
||||
{
|
||||
JSL Oracle_CheckIfNight ; Can call INTO Oracle
|
||||
RTL
|
||||
}
|
||||
|
||||
// Export bridge:
|
||||
namespace Oracle
|
||||
{
|
||||
Oracle_ZSO_CheckIfNight = ZSO_CheckIfNight
|
||||
}
|
||||
|
||||
// ZScream hook can use it:
|
||||
org $09C4C7
|
||||
LoadOverworldSprites_Hook:
|
||||
JSL Oracle_ZSO_CheckIfNight ; Bridge function
|
||||
```
|
||||
|
||||
For comprehensive namespace troubleshooting and advanced patterns, see:
|
||||
- `Docs/World/Overworld/ZSCustomOverworldAdvanced.md` Section 5 (Cross-Namespace Integration)
|
||||
- `Docs/General/Troubleshooting.md` Section 5 (Cross-Namespace Calling)
|
||||
- `Docs/General/DevelopmentGuidelines.md` Section 2.4 (Namespace Architecture)
|
||||
|
||||
### 3.6. Safe Hooking and Code Injection
|
||||
|
||||
When modifying vanilla game logic, it is critical to never edit the disassembly files in `ALTTP/` or `usdasm/` directly. Instead, use the following safe hooking method to inject custom code.
|
||||
|
||||
- **1. Identify a Target and Free Space:**
|
||||
- Locate the exact address in the vanilla code you want to modify (the "hook point").
|
||||
- Identify a free bank or region in the ROM to place your new, expanded code.
|
||||
|
||||
- **2. Choose the Appropriate File for Your Hook:**
|
||||
- **Feature-Specific Hooks:** Place hooks in the same file as the custom implementation they enable. For example, if you're adding a new item feature in `Items/magic_ring.asm`, place the vanilla hook in that same file.
|
||||
- **Module-Specific Hooks:** For hooks that modify core game systems (sprite engine, player engine, etc.), place them in the relevant module file within the `Core/` directory.
|
||||
- **Generic Patches:** Only place truly generic, cross-cutting modifications in `Core/patches.asm` (e.g., fixes to vanilla bugs, performance optimizations).
|
||||
- **Rationale:** Co-locating hooks with their implementations improves code organization, makes features self-contained, and provides better context for future maintenance.
|
||||
|
||||
- **3. Write the Hook:**
|
||||
- Use `pushpc` and `pullpc` to isolate your patch.
|
||||
- Use `org` to navigate to the target address in the vanilla code.
|
||||
- At the target address, overwrite the original instruction(s) with a `JSL` (or `JMP`) to your new custom routine in free space.
|
||||
- **Crucially, ensure your `JSL`/`JMP` instruction and any necessary `NOP`s perfectly replace the original instruction(s) byte-for-byte.** A `JSL` is 4 bytes. If you overwrite an instruction that is only 2 bytes, you must add `NOP` instructions to fill the remaining 2 bytes to avoid corrupting the subsequent instruction.
|
||||
|
||||
- **4. Implement the Custom Routine:**
|
||||
- In a `freedata` block (or using `org` with a free space address), write your new routine in the same file as the hook.
|
||||
- **Preserve Overwritten Code:** The first thing your new routine must do is execute the exact vanilla instruction(s) that you overwrote with your `JSL`. This is essential to maintain the original game's behavior.
|
||||
- After preserving the original logic, add your new custom code.
|
||||
- End your routine with an `RTL` (to return from a `JSL`) or `RTS` (to return from a `JSR`).
|
||||
|
||||
- **Example (Feature-Specific Hook):**
|
||||
```asm
|
||||
; In Items/magic_ring.asm
|
||||
|
||||
; 1. Place the new, expanded logic in a free bank.
|
||||
org $348000
|
||||
MagicRing_CustomEffect:
|
||||
{
|
||||
; 2. First, execute the original instruction(s) that were overwritten.
|
||||
LDA.b #$01 ; Example: Original instruction was LDA #$01 (2 bytes)
|
||||
STA.w $0DD0,X ; Example: Original instruction was STA $0DD0,X (3 bytes)
|
||||
|
||||
; 3. Now, add your new custom logic for the magic ring.
|
||||
LDA.l MagicRing ; Check if player has magic ring
|
||||
BEQ .no_ring
|
||||
LDA.b #$FF ; Apply ring's special effect
|
||||
STA.w $1234,X
|
||||
.no_ring
|
||||
|
||||
; 4. Return to the vanilla code.
|
||||
RTL
|
||||
}
|
||||
|
||||
; 5. Hook placement: In the same file, near the feature implementation
|
||||
pushpc
|
||||
org $05C227 ; Target address in vanilla sprite damage routine
|
||||
JSL MagicRing_CustomEffect ; JSL is 4 bytes.
|
||||
NOP ; Fill the 5th byte since we overwrote two instructions (2+3=5 bytes)
|
||||
pullpc
|
||||
```
|
||||
|
||||
- **Example (Core System Hook):**
|
||||
```asm
|
||||
; In Core/sprite_engine_hooks.asm (or similar)
|
||||
|
||||
org $348100
|
||||
CustomSprite_DeathHandler:
|
||||
{
|
||||
; Preserve original death logic
|
||||
LDA.w SprHealth, X
|
||||
BNE .not_dead
|
||||
; Original vanilla death code here
|
||||
JSL Sprite_SpawnDeathAnimation
|
||||
.not_dead
|
||||
|
||||
; Add custom death effects for Oracle sprites
|
||||
LDA.w SprType, X
|
||||
CMP.b #CustomSpriteID : BNE .skip_custom
|
||||
JSR CustomSprite_SpecialDeath
|
||||
.skip_custom
|
||||
|
||||
RTL
|
||||
}
|
||||
|
||||
; Hook in same file
|
||||
pushpc
|
||||
org $068450
|
||||
JSL CustomSprite_DeathHandler
|
||||
pullpc
|
||||
```
|
||||
|
||||
## 4. Build Process and ROM Management
|
||||
|
||||
- **Clean ROM**: The clean, unmodified "The Legend of Zelda: A Link to the Past" ROM should be placed at `Roms/oos169.sfc`. This path is included in `.gitignore`, so the ROM file will not be committed to the repository.
|
||||
- **Build Script**: A `build.sh` script is provided to automate the build process. For detailed usage, see `Docs/General/AsarUsage.md`.
|
||||
- **Workflow**: The build script creates a fresh copy of the clean ROM and applies the `Oracle_main.asm` patch to it using `asar`.
|
||||
- **Important**: Never apply patches directly to `Roms/oos169.sfc`. Always use the build script to create a new, patched ROM. This ensures the clean ROM remains untouched for future builds.
|
||||
|
||||
## 5. Debugging Tips for BRKs and Crashes
|
||||
|
||||
When encountering unexpected crashes (often indicated by a `BRK` instruction in emulators), especially after modifying code, consider the following:
|
||||
|
||||
**For comprehensive debugging guidance with step-by-step procedures, see `Docs/General/Troubleshooting.md`.**
|
||||
|
||||
### 5.1. Most Common Causes
|
||||
|
||||
- **Processor Status Register (P) Mismatch:** This is a very common cause. If a routine expects 8-bit accumulator/index registers (M=1, X=1) but is called when they are 16-bit (M=0, X=0), or vice-versa, memory accesses and arithmetic operations will be incorrect, leading to crashes.
|
||||
|
||||
**Example:**
|
||||
```asm
|
||||
; BAD: Size mismatch
|
||||
REP #$20 ; A = 16-bit
|
||||
JSL Function ; Function expects 8-bit!
|
||||
|
||||
; Inside Function:
|
||||
SEP #$20 ; Sets 8-bit mode
|
||||
LDA.b #$FF
|
||||
STA.w $1234 ; Only stores $FF, not $00FF!
|
||||
RTL ; ← Doesn't restore caller's 16-bit mode
|
||||
|
||||
; GOOD: Preserve state
|
||||
Function:
|
||||
PHP ; Save caller's state
|
||||
SEP #$20 ; Set to 8-bit
|
||||
LDA.b #$FF
|
||||
STA.w $1234
|
||||
PLP ; Restore caller's state
|
||||
RTL
|
||||
```
|
||||
|
||||
- **Stack Corruption:** JSL/JSR push the return address onto the stack. If a called routine pushes too much data onto the stack without popping it, or if the stack pointer (`S`) is corrupted, the return address can be overwritten, leading to a crash when `RTL`/`RTS` is executed.
|
||||
- **`JSR`/`RTS` vs `JSL`/`RTL` Mismatch:** This is a critical and common error.
|
||||
- `JSR` (Jump to Subroutine) pushes a 2-byte return address. It **must** be paired with `RTS` (Return from Subroutine), which pulls 2 bytes.
|
||||
- `JSL` (Jump to Subroutine Long) pushes a 3-byte return address (including the bank). It **must** be paired with `RTL` (Return from Subroutine Long), which pulls 3 bytes.
|
||||
|
||||
**Example:**
|
||||
```asm
|
||||
; BAD: Mismatched call/return
|
||||
MainFunction:
|
||||
JSL SubFunction ; Pushes 3 bytes ($02 $C4 $09)
|
||||
|
||||
SubFunction:
|
||||
; ... code ...
|
||||
RTS ; ← ERROR: Only pops 2 bytes! Stack corrupted!
|
||||
|
||||
; GOOD: Matched call/return
|
||||
MainFunction:
|
||||
JSL SubFunction ; Pushes 3 bytes
|
||||
|
||||
SubFunction:
|
||||
; ... code ...
|
||||
RTL ; ← Correct: Pops 3 bytes
|
||||
```
|
||||
|
||||
### 5.2. Debugging Tools
|
||||
|
||||
- **Mesen-S (Recommended):** The most powerful SNES debugger:
|
||||
- Set breakpoints with conditions: `A == #$42`
|
||||
- Memory watchpoints: `[W]$7E0730` (break on write)
|
||||
- Stack viewer to trace call history
|
||||
- Event viewer for NMI/IRQ timing
|
||||
- Break on BRK automatically
|
||||
|
||||
**Quick Mesen-S Workflow:**
|
||||
1. Enable "Break on BRK" in Debugger settings
|
||||
2. When crash occurs, check Stack viewer
|
||||
3. Read return addresses to trace call history
|
||||
4. Set breakpoint before suspected crash location
|
||||
5. Step through code examining registers
|
||||
|
||||
- **Breadcrumb Tracking:**
|
||||
```asm
|
||||
; Add markers to narrow down crash location
|
||||
LDA.b #$01 : STA.l $7F5000 ; Breadcrumb 1
|
||||
JSL SuspiciousFunction
|
||||
LDA.b #$02 : STA.l $7F5000 ; Breadcrumb 2
|
||||
JSL AnotherFunction
|
||||
LDA.b #$03 : STA.l $7F5000 ; Breadcrumb 3
|
||||
|
||||
; After crash, check $7F5000 in memory viewer
|
||||
; If value is $02, crash occurred in AnotherFunction
|
||||
```
|
||||
|
||||
### 5.3. Common Error Patterns
|
||||
|
||||
**Pattern 1: Jumping to $000000 (BRK)**
|
||||
- Cause: Corrupted jump address or return address
|
||||
- Debug: Check stack contents, verify JSL/JSR is called before RTL/RTS
|
||||
|
||||
**Pattern 2: Infinite Loop / Freeze**
|
||||
- Cause: Forgot to increment module/submodule, infinite recursion
|
||||
- Debug: Check that `$10` (module) or `$11` (submodule) is incremented
|
||||
|
||||
**Pattern 3: Wrong Graphics / Corrupted Screen**
|
||||
- Cause: DMA during active display, wrong VRAM address
|
||||
- Debug: Ensure graphics updates only during NMI or Force Blank
|
||||
|
||||
### 5.4. Cross-Reference Documentation
|
||||
|
||||
For specific debugging scenarios:
|
||||
- **BRK Crashes:** `Docs/General/Troubleshooting.md` Section 2
|
||||
- **Stack Corruption:** `Docs/General/Troubleshooting.md` Section 3
|
||||
- **Processor State Issues:** `Docs/General/Troubleshooting.md` Section 4
|
||||
- **Namespace Problems:** `Docs/General/Troubleshooting.md` Section 5
|
||||
- **Memory Conflicts:** `Docs/General/Troubleshooting.md` Section 6
|
||||
- **Graphics Issues:** `Docs/General/Troubleshooting.md` Section 7
|
||||
- **ZScream-Specific:** `Docs/General/Troubleshooting.md` Section 8
|
||||
|
||||
|
||||
|
||||
### 5.5. Recent Debugging Insights
|
||||
|
||||
During recent development and bug-fixing tasks, several critical patterns and debugging insights have emerged:
|
||||
|
||||
- **Processor Status Mismatch (M/X flags) and BRK (`$00` opcode):**
|
||||
- A common cause of crashes is calling a routine expecting a different processor mode (e.g., 16-bit Accumulator) than the current CPU state (e.g., 8-bit Accumulator).
|
||||
- Specifically, if an `AND.w #$00FF` instruction is encountered while the A-register is in 8-bit mode (`SEP #$20` active), the assembler may generate `29 FF 00`. The CPU will execute `29 FF` (AND immediate byte), and then interpret the trailing `00` as a `BRK` instruction, leading to a crash.
|
||||
- **Resolution:** Always explicitly set the processor state (`REP #$30` for 16-bit, `SEP #$30` for 8-bit) at the entry of routines that depend on a specific mode, and consider `PHP`/`PLP` for state preservation if the routine needs to temporarily change modes or be called from various contexts.
|
||||
|
||||
- **Input Polling Registers for Continuous Actions:**
|
||||
- For features requiring continuous input (e.g., holding a button to navigate in a menu or turn pages), use the joypad register that tracks **all pressed buttons** (`$F2` / `JOY1B_ALL`).
|
||||
- Avoid using registers that only signal **new button presses** (`$F6` / `JOY1B_NEW`), as these will only trigger an action for a single frame, making continuous interaction impossible.
|
||||
- **Resolution:** Pair `$F2` checks with a delay timer (`$0207` in our context) to prevent rapid-fire actions.
|
||||
|
||||
- **VRAM Update Flags (`$0116`, `$15`, `$17`) for Menu Graphics:**
|
||||
- The variable `$0116` acts as a crucial trigger for the vanilla NMI handler (`NMI_DoUpdates`) to perform VRAM updates. **Bit 0 of `$0116` must be set (`$01`, or part of `$21`, `$23`, `$25`, etc.)** for standard tilemap and OAM buffer uploads to occur. Values like `$22` (where bit 0 is clear) may be ignored by the vanilla NMI handler for general VRAM updates.
|
||||
- The `$15` flag (often referred to as the Palette/Refresh flag) should often be set alongside `$17` (NMI module selection) to ensure consistent and complete VRAM refreshes, especially for full-screen updates.
|
||||
- **Resolution:** When a menu state needs to update its tilemap or OAM visually, ensure `$0116` has bit 0 set, and consider setting `$15` for comprehensive refreshes.
|
||||
|
||||
- **Data Table Mismatches (Logical vs. Visual Indexing):**
|
||||
- In UI-heavy features (like inventory or submenus), a misalignment between a table defining the *logical order* of items (e.g., `Menu_AddressIndex`) and a table defining their *visual positions* (e.g., `Menu_ItemCursorPositions`) can lead to subtle bugs.
|
||||
- **Symptom:** A cursor might appear in the wrong place, or attempting to clear a cursor from one item might inadvertently clear another, resulting in visual artifacts.
|
||||
- **Resolution:** Rigorously align item indices across all related data tables, ensuring a 1:1 mapping between logical item order and visual screen coordinates.
|
||||
|
||||
- **Custom NMI Handlers and Vanilla System Integration:**
|
||||
- Be aware that extensive custom systems (like ZScream's overworld graphics streaming) may replace or heavily modify vanilla NMI routines. These custom handlers might be designed to process their *own* DMA requests and could potentially ignore standard vanilla flags (`$0116`, `$15`) or input registers.
|
||||
- **Symptom:** Standard game elements (like menus) may fail to update graphics or respond to input if the custom NMI handler does not explicitly integrate or defer to the vanilla update logic.
|
||||
- **Resolution:** If a custom NMI handler is in place, it must either pass control to the vanilla NMI handler when appropriate (e.g., when the game is in a menu state), or manually replicate the necessary vanilla update logic (e.g., checking `$0116` and initiating DMA for menu buffers).
|
||||
|
||||
## 6. Verification Policy
|
||||
|
||||
- **Bugs and Features:** Never mark a bug fix or feature implementation as `DONE` until it has been thoroughly tested and verified in an emulator. This ensures stability and prevents regressions.
|
||||
|
||||
|
||||
## 7. Memory and Symbol Analysis
|
||||
|
||||
This section details the layout and purpose of critical memory regions (WRAM and SRAM) and the symbol definition files that give them context.
|
||||
|
||||
**For comprehensive memory documentation, see:**
|
||||
- `Docs/Core/MemoryMap.md` - Complete WRAM/SRAM map with verified custom variables
|
||||
- `Docs/Core/Ram.md` - High-level overview of memory usage
|
||||
- `Docs/General/Troubleshooting.md` Section 6 - Memory conflict resolution
|
||||
|
||||
### 7.1. WRAM (Work RAM) Analysis
|
||||
|
||||
Work RAM (WRAM) holds the active, volatile state of the entire game. The following are some of the most critical variables for understanding real-time game logic.
|
||||
|
||||
* **Direct Page & Scrap (`$7E0000` - `$7E000F`):** A highly volatile scratchpad for temporary, single-frame calculations.
|
||||
* **Main Game State (`$7E0010` - `$7E001F`):**
|
||||
* `MODE` (`$7E0010`): The primary game state variable (Overworld, Underworld, Menu, etc.). This dictates which main module is executed each frame.
|
||||
* `INDOORS` (`$7E001B`): A flag (`0x01` for indoors, `0x00` for outdoors) controlling environmental factors.
|
||||
* **Link's State (`$7E0020`+):** A large block containing the player's immediate state.
|
||||
* `POSX`/`POSY`/`POSZ`: Link's 16-bit absolute coordinates.
|
||||
* `LINKDO` (`$7E005D`): Link's personal state machine variable (walking, swimming, lifting, etc.), used by the player engine in Bank $07.
|
||||
* `IFRAMES` (`$7E031F`): Invincibility frame timer after taking damage.
|
||||
* **Area & Room State (`$7E008A` - `$7E00AF`):**
|
||||
* `OWSCR` (`$7E008A`): The current Overworld screen ID.
|
||||
* `ROOM` (`$7E00A0`): The current Underworld room ID.
|
||||
* **Sprite and Ancilla Data (`$7E0D00+`):** `Core/symbols.asm` maps the data structures for all sprites and ancillae (projectiles, effects). Key variables include `SprState` (state machine), `SprType` (ID), `SprHealth`, and coordinates. This is fundamental to all NPC and enemy logic.
|
||||
* **Oracle of Secrets Custom WRAM (`$7E0730+`):** A custom region for new features. Notable variables include `GoldstarOrHookshot` and `FishingOrPortalRod`, used to manage the state of new custom items.
|
||||
|
||||
### 7.2. SRAM (Save RAM) Analysis
|
||||
|
||||
SRAM stores the player's save file, including long-term progression and inventory. `Core/sram.asm` reveals significant customization for Oracle of Secrets.
|
||||
|
||||
#### Vanilla ALTTP Save Data:
|
||||
* **Inventory:** `Bow` (`$7EF340`), `Bombs` (`$7EF343`), `Sword` (`$7EF359`), `Shield` (`$7EF35A`).
|
||||
* **Player Status:** `Rupees` (`$7EF360`), `MAXHP` (`$7EF36C`), `CURHP` (`$7EF36D`).
|
||||
* **Progression:** `Pendants` (`$7EF374`), `Crystals` (`$7EF37A`), `GAMESTATE` (`$7EF3C5`).
|
||||
|
||||
#### Oracle of Secrets (OOS) Custom SRAM Data:
|
||||
This highlights the major new features of the hack.
|
||||
* **New Items & Masks:** `ZoraMask` (`$7EF347`), `BunnyHood` (`$7EF348`), `DekuMask` (`$7EF349`), `RocsFeather` (`$7EF34D`), etc. These introduce major new player abilities.
|
||||
* **New Progression System:**
|
||||
* `OOSPROG` (`$7EF3D6`): A primary bitfield for major quest milestones unique to OOS.
|
||||
* `Dreams` (`$7EF410`): A new collectible concept.
|
||||
* **New Collectibles & Side-Quests:** A block from `$7EF38B` holds new items like `Bananas`, `Seashells`, and `Honeycomb`. `MagicBeanProg` (`$7EF39B`) tracks a new multi-day side-quest.
|
||||
|
||||
### 7.3. Symbols and Functions (`Core/symbols.asm`)
|
||||
|
||||
This file acts as a central header, defining constants and labels for memory addresses and functions to make the assembly code readable and maintainable.
|
||||
|
||||
* **Function Pointers:** It provides labels for critical functions across different ROM banks (e.g., `Sprite_CheckDamageToPlayer`, `EnableForceBlank`), allowing for modular code.
|
||||
* **Memory Maps:** It contains the definitive memory maps for WRAM structures, most notably for sprites and ancillae.
|
||||
* **Readability:** Its primary purpose is to replace "magic numbers" (raw addresses) with human-readable labels, which is essential for a project of this scale.
|
||||
|
||||
## 8. Documentation Reference
|
||||
|
||||
This section provides a comprehensive overview of all available documentation files, organized by category. These documents serve as key references for understanding the project's architecture, gameplay systems, and development practices.
|
||||
|
||||
### 8.1. Core System Documentation
|
||||
|
||||
* **`Docs/Core/MemoryMap.md`:** Complete WRAM and SRAM memory map with verified custom variables. Documents all custom memory regions from `$7E0730+` (WRAM) and OOS-specific SRAM including `$7EF3D6` (OOSPROG), `$7EF38A+` (collectibles block), and `$7EF410` (Dreams). Essential reference for any code that accesses RAM.
|
||||
|
||||
* **`Docs/Core/Ram.md`:** High-level overview of WRAM and SRAM usage patterns with descriptions of custom variable purposes.
|
||||
|
||||
* **`Docs/Core/Link.md`:** Documentation of Link's state machine, player mechanics, and control handlers.
|
||||
|
||||
* **`Docs/Core/SystemInteractions.md`:** Documentation of core game systems and their interactions.
|
||||
|
||||
### 8.2. Development Guidelines
|
||||
|
||||
* **`Docs/General/DevelopmentGuidelines.md`:** Comprehensive development guidelines covering architecture patterns, memory management, assembly best practices, coding standards, and debugging strategies. Expands on concepts in this GEMINI.md file with detailed examples and rationale. **Required reading for all contributors.**
|
||||
|
||||
* **`Docs/General/Troubleshooting.md`:** Comprehensive troubleshooting guide with step-by-step debugging procedures. Covers:
|
||||
- BRK crashes and stack traces
|
||||
- Stack corruption patterns (JSL/JSR vs RTL/RTS mismatches)
|
||||
- Processor status register issues (M/X flag problems)
|
||||
- Cross-namespace calling problems
|
||||
- Memory conflicts and bank collisions
|
||||
- Graphics/DMA timing issues
|
||||
- ZScream-specific problems
|
||||
- Debugging with Mesen-S and BSNES-Plus
|
||||
|
||||
* **`Docs/General/AsarUsage.md`:** Best practices for using the Asar assembler and the build script system. Covers `org` vs `pushpc`/`pullpc`, ROM management, and build workflow.
|
||||
|
||||
### 8.3. Creation Guides
|
||||
|
||||
* **`Docs/Guides/SpriteCreationGuide.md`:** Comprehensive 878-line tutorial for creating custom sprites from scratch. Covers:
|
||||
- File setup and bank organization
|
||||
- Sprite properties (23 configurable flags with memory addresses)
|
||||
- Initialization with 60+ WRAM variables documented
|
||||
- Main logic with 30+ macros and 20+ core functions
|
||||
- OAM drawing system with tile format specifications
|
||||
- Testing procedures and debugging strategies
|
||||
- Common issues and solutions
|
||||
- 10 advanced patterns:
|
||||
1. State Machines (Booki example)
|
||||
2. Multi-part Sprites (Kydreeok boss)
|
||||
3. Guard/Defense Mechanics (Darknut)
|
||||
4. Shared Logic (Goriya variations)
|
||||
5. Complex Boss Mechanics
|
||||
6. NPC Interactions (Followers)
|
||||
7. Interactive Objects (Minecart)
|
||||
8. Environmental Awareness
|
||||
9. Advanced AI Patterns
|
||||
10. Cross-System Integration
|
||||
- Real examples from Booki, Darknut, Kydreeok, Goriya, Followers, and Minecart sprites
|
||||
|
||||
* **`Docs/Guides/QuestFlow.md`:** Detailed guide to main story and side-quest progression, including trigger conditions, progression flags, and quest dependencies.
|
||||
|
||||
### 8.4. World System Documentation
|
||||
|
||||
* **`Docs/World/Overworld/ZSCustomOverworld.md`:** Basic guide to the ZScream custom overworld system and its data tables.
|
||||
|
||||
* **`Docs/World/Overworld/ZSCustomOverworldAdvanced.md`:** Advanced technical documentation for ZScream integration. **Critical for modifying overworld behavior.** Covers:
|
||||
- Internal hook architecture (38+ vanilla routine replacements)
|
||||
- Hook execution order during screen transitions
|
||||
- Memory management and state tracking (Bank $28 data pool)
|
||||
- Graphics loading pipeline with frame-by-frame analysis
|
||||
- Sprite loading system deep dive
|
||||
- Cross-namespace integration patterns (Oracle ↔ ZScream)
|
||||
- Performance considerations and optimization strategies
|
||||
- Adding custom features to the overworld system
|
||||
- Debugging ZScream-specific issues
|
||||
|
||||
* **`Docs/World/Overworld/TimeSystem.md`:** Documentation of the day/night cycle system implementation.
|
||||
|
||||
* **`Docs/World/Dungeons/Dungeons.md`:** Breakdown of dungeon systems, layouts, enemy placements, and puzzle mechanics.
|
||||
|
||||
### 8.5. Feature System Documentation
|
||||
|
||||
* **`Docs/Features/Menu/Menu.md`:** Analysis of the custom menu and HUD systems, including two-page menu layout and item drawing routines.
|
||||
|
||||
* **`Docs/Features/Items/Items.md`:** Guide to custom and modified items, including implementation details for Goldstar, Portal Rod, Ocarina, and others.
|
||||
|
||||
* **`Docs/Features/Music/Music.md`:** Guide to custom music tracks, sound effects, and adding new audio.
|
||||
|
||||
* **`Docs/Features/Masks/Masks.md`:** Comprehensive overview of the Mask System, including transformation mechanics and each mask's abilities.
|
||||
|
||||
### 8.6. Sprite Documentation
|
||||
|
||||
Each sprite category has both an overview document and individual files for specific sprites:
|
||||
|
||||
* **`Docs/Sprites/NPCs.md`:** Overview of NPC sprite system with links to individual NPC documentation in `Docs/Sprites/NPCs/` (BeanVendor, DekuScrub, EonOwl, Farore, Followers, Impa, Korok, Maple, MaskSalesman, Tingle, Vasu, ZoraPrincess, etc.)
|
||||
|
||||
* **`Docs/Sprites/Bosses.md`:** Overview of boss sprite system with links to individual boss documentation in `Docs/Sprites/Bosses/` (DarkLink, Kydreeok, Manhandla, etc.)
|
||||
|
||||
* **`Docs/Sprites/Enemies/`:** Individual documentation for enemy sprites (AntiKirby, Booki, BusinessScrub, Darknut, Goriya, Keese, Leever, Octorok, PolsVoice, Wolfos, etc.)
|
||||
|
||||
* **`Docs/Sprites/Objects.md`:** Overview of interactive object sprites with documentation in `Docs/Sprites/Objects/` (Collectible, DekuLeaf, IceBlock, Minecart, Pedestal, PortalSprite, etc.)
|
||||
|
||||
* **`Docs/Sprites/Overlords.md`:** Analysis of the Overlord sprite system used for environmental effects and multi-screen management.
|
||||
|
||||
## 9. Disassembly Analysis and Search Guide
|
||||
|
||||
This section provides a high-level analysis of key banks in the Link to the Past disassembly. Use this guide to quickly locate relevant code and understand the overall structure of the game.
|
||||
|
||||
### 9.1. Bank $00: Game Core & Main Loop
|
||||
|
||||
**File:** `ALTTP/bank_00.asm`
|
||||
**Address Range:** `$008000` - `$00FFFF`
|
||||
**Summary:** The heart of the game engine. Contains the main game loop, interrupt handlers, and the primary game state machine.
|
||||
|
||||
#### Key Structures & Routines:
|
||||
* **`Reset:` (#_008000)**: Game entry point on boot.
|
||||
* **`MainGameLoop:` (#_008034)**: Central loop, calls `Module_MainRouting`.
|
||||
* **`Module_MainRouting:` (#_0080B5)**: Primary state machine dispatcher. Reads `MODE` (`$7E0010`) and uses `pool Module_MainRouting` to jump to the correct game module.
|
||||
* **`Interrupt_NMI:` (#_0080C9)**: Runs every frame. Handles input (`NMI_ReadJoypads`), graphics DMA (`NMI_DoUpdates`), and sprite preparation (`NMI_PrepareSprites`).
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Game Module Logic (Overworld, Underworld, Menus):** Search `bank_00.asm` for the `pool Module_MainRouting` jump table. The labels (e.g., `Module09_Overworld`) are the entry points for each game state, determined by WRAM `$7E0010` (`MODE`).
|
||||
* **Per-Frame Logic:** Search `bank_00.asm` for `Interrupt_NMI:`. Key routines called from here are `NMI_ReadJoypads` (input) and `NMI_DoUpdates` (graphics DMA).
|
||||
* **Initialization Logic:** Start at the `Reset:` label in `bank_00.asm` and trace `JSR`/`JSL` calls to routines like `InitializeMemoryAndSRAM`.
|
||||
|
||||
### 9.2. Bank $01: Dungeon Engine
|
||||
|
||||
**File:** `ALTTP/bank_01.asm`
|
||||
**Address Range:** `$018000` - `$01FFFF`
|
||||
**Summary:** Responsible for loading, drawing, and managing all aspects of interior rooms (dungeons, houses, caves).
|
||||
|
||||
#### Key Structures & Routines:
|
||||
* **`Underworld_LoadRoom:` (#_01873A)**: Main entry point for loading a dungeon room.
|
||||
* **`DrawObjects` Tables:** A set of tables at the top of the bank defining object graphics and drawing routines.
|
||||
* **`RoomDraw_DrawAllObjects:` (#_0188E4)**: Iterates through a room's object list.
|
||||
* **`RoomDraw_RoomObject:` (#_01893C)**: Main dispatcher for drawing a single object based on its ID.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Room Construction Logic:** In `bank_01.asm`, start at `Underworld_LoadRoom` and trace the call sequence: `Underworld_LoadHeader` -> `RoomDraw_DrawFloors` -> `RoomDraw_DrawAllObjects`.
|
||||
* **Specific Dungeon Object Code:** To find an object's drawing code, search the `.type1_subtype_..._routine` tables at the start of `bank_01.asm` for the object's ID. The corresponding label is the drawing routine. To find its tile data, search the `.type1_subtype_..._data_offset` tables.
|
||||
|
||||
### 9.3. Bank $02: Overworld & Transitions
|
||||
|
||||
**File:** `ALTTP/bank_02.asm`
|
||||
**Address Range:** `$028000` - `$02FFFF`
|
||||
**Summary:** Manages loading the overworld, transitioning between areas, and handling special game sequences.
|
||||
|
||||
#### Key Structures & Routines:
|
||||
* **`Module06_UnderworldLoad:` (#_02821E)**: Primary module for transitioning into and loading an underworld room.
|
||||
* **`Module08_OverworldLoad:` (#_0283BF)**: Primary module for loading the overworld.
|
||||
* **`Module07_Underworld:` (#_0287A2)**: Main logic loop for when the player is in the underworld. Dispatches to submodules based on WRAM `$11`.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Overworld Loading:** Start at `Module08_OverworldLoad` in `bank_02.asm`. Logic checks WRAM `$8A` (overworld area number) to determine behavior.
|
||||
* **Underworld Gameplay:** Start at `Module07_Underworld` in `bank_02.asm`. Examine the `.submodules` jump table to see the different states, determined by WRAM `$11`.
|
||||
* **Transition Logic:** Search for code that sets the game `MODE` (`$10`) to `$08` (Overworld Load) or `$06` (Underworld Load) to find the start of a transition.
|
||||
|
||||
### 9.4. Bank $03: Tile32 Overworld Layout Data
|
||||
|
||||
### 9.5. Bank $04: Tile32 Overworld Layout Data, Dungeon Room Headers
|
||||
|
||||
### 9.6. Bank $07: Core Player (Link) Engine
|
||||
|
||||
**File:** `ALTTP/bank_07.asm`
|
||||
**Address Range:** `$078000` - `$07FFFF`
|
||||
**Summary:** Contains Link's core state machine, governing movement, physics, item usage, and interactions.
|
||||
|
||||
#### Key Structures & Routines:
|
||||
* **`Link_Main:` (#_078000)**: Top-level entry point for all player logic.
|
||||
* **`Link_ControlHandler:` (#_07807F)**: The heart of the player engine. A state machine dispatcher that reads `LINKDO` (`$7E005D`) and jumps via the `pool Link_ControlHandler` table.
|
||||
* **`LinkState_Default` (#_078109):** The most common state, handling walking and dispatching to action sub-handlers like `Link_HandleYItem`.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Player Action Logic (walking, swimming):** In `bank_07.asm`, search for `pool Link_ControlHandler`. The state ID is from WRAM `$7E005D` (`LINKDO`). Find the label for the desired state (e.g., `LinkState_Default`) to locate its main routine.
|
||||
* **Player Physics/Collision:** Within a player state routine, search for calls to `JSL Link_HandleVelocity` (physics) and `JSR Link_HandleCardinalCollision` (collision).
|
||||
* **Y-Button Item Logic:** In `LinkState_Default`, search for the call to `JSR Link_HandleYItem`.
|
||||
* **Player Damage Logic:** Search for writes to WRAM `$7E0373` (`HURTME`).
|
||||
|
||||
### 9.7. Bank $05: Specialized Sprite & Object Engine
|
||||
|
||||
**File:** `ALTTP/bank_05.asm`
|
||||
**Address Range:** `$058000` - `$05FFFF`
|
||||
**Summary:** Code for unique, complex, and scripted sprites that do not fit the standard enemy AI model (e.g., cutscene sprites, minigame hosts, complex traps).
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Unique/Non-Enemy Sprites:** When looking for a unique sprite (minigame, cutscene object, complex trap), check `bank_05.asm` first.
|
||||
* **Finding Sprite Logic:** Search for the sprite's name (e.g., "MasterSword") or its hexadecimal ID (e.g., `Sprite_62`) to find its main routine.
|
||||
|
||||
### 9.8. Bank $06: Main Sprite Engine & Helpers
|
||||
|
||||
**File:** `ALTTP/bank_06.asm`
|
||||
**Address Range:** `$068000` - `$06FFFF`
|
||||
**Summary:** Contains the main sprite processing engine and a vast library of shared helper subroutines used by sprites game-wide.
|
||||
|
||||
#### Key Structures & Routines:
|
||||
* **`Sprite_Main:` (#_068328)**: The master sprite loop that iterates through all 16 sprite slots.
|
||||
* **`Sprite_ExecuteSingle:` (#_0684E2)**: The state machine dispatcher for an individual sprite, reading `SprState` (`$7E0DD0,X`).
|
||||
* **`SpriteModule_Initialize:` (#_06864D)**: Master initialization routine. Contains a massive jump table pointing to a specific `SpritePrep_...` routine for nearly every sprite type.
|
||||
* **`Sprite_SpawnSecret` (`#_068264`):** Determines the "secret" item that appears under a liftable bush or rock.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Sprite Initialization (HP, damage, etc.):** In `bank_06.asm`, go to `SpriteModule_Initialize`. Find the sprite's ID in the large jump table to get the label for its `SpritePrep_...` routine.
|
||||
* **Sprite Core AI:** In `bank_06.asm`, go to `SpriteModule_Active`. Find the sprite's ID in its jump table to find the entry point to its main AI logic (which may be in another bank).
|
||||
* **Bush/Rock Item Drops:** Locate the `Sprite_SpawnSecret` routine and examine the `.ID` table at `#_0681F4` to see the prize mappings.
|
||||
|
||||
### 9.9. Bank $08: Ancilla Engine
|
||||
|
||||
**File:** `ALTTP/bank_08.asm`
|
||||
**Address Range:** `$088000` - `$08FFFF`
|
||||
**Summary:** The engine for "Ancillae" (projectiles, particle effects, etc.). Contains the execution logic for entities like arrows, bombs, and magic spells.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Projectile/Effect Logic:** In `bank_08.asm`, find the main jump table in `Ancilla_ExecuteOne` (at `#_08837F`). Look up the ancilla's ID in this table to find the label for its logic routine (e.g., `Ancilla07_Bomb`).
|
||||
* **Projectile Properties (speed, graphics):** Go to the ancilla's main logic routine (e.g., `Ancilla09_Arrow`) and look for writes to its WRAM properties (e.g., `$0C2C` for X-speed).
|
||||
|
||||
### 9.10. Bank $09: Ancilla Spawning & Item Logic
|
||||
|
||||
**File:** `ALTTP/bank_09.asm`
|
||||
**Address Range:** `$098000` - `$09FFFF`
|
||||
**Summary:** Contains the ancilla *creation* engine (a library of `AncillaAdd_...` functions) and the critical logic for giving items to the player.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Projectile/Effect Creation:** To find where a projectile is created, search the codebase for `JSL` calls to its corresponding `AncillaAdd_...` function in this bank (e.g., `JSL AncillaAdd_Bomb`).
|
||||
* **Item "Get" Properties:** To change the properties of an item the player receives, find the `AncillaAdd_ItemReceipt` routine and examine the large data tables starting at `#_098404`.
|
||||
|
||||
### 9.11. Bank $0A: World Map & Flute Menu Engine
|
||||
|
||||
**File:** `ALTTP/bank_0A.asm`
|
||||
**Address Range:** `$0A8000` - `$0AFFFF`
|
||||
**Summary:** Controls all full-screen map interfaces (pause menu map, flute destination map).
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Flute Warp Destinations:** In `bank_0A.asm`, find the `FluteMenu_LoadTransport` routine. The table within it maps the 8 flute spots to screen indexes.
|
||||
* **Map Icon Locations:** Search for the `WorldMapIcon_posx_...` and `WorldMapIcon_posy_...` tables to adjust icon coordinates.
|
||||
|
||||
### 9.12. Bank $0B: Overworld Environment & State Helpers
|
||||
|
||||
**File:** `ALTTP/bank_0B.asm`
|
||||
**Address Range:** `$0B8000` - `$0BFFFF`
|
||||
**Summary:** Miscellaneous helper functions related to the overworld environment and player state.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Overworld Area Palette:** To change the background color of an overworld area, modify the color values loaded in `Overworld_SetFixedColAndScroll`. The logic checks WRAM `$8A` to decide which color to use.
|
||||
* **Wall Master Capture:** To change what happens when captured, find the `WallMaster_SendPlayerToLastEntrance` routine.
|
||||
|
||||
### 9.13. Bank $0C: Intro & Credits Sequence
|
||||
|
||||
**File:** `ALTTP/bank_0C.asm`
|
||||
**Address Range:** `$0C8000` - `$0CFFFF`
|
||||
**Summary:** Handles the game's intro and end-game credits sequences.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Intro/Credits Scene Logic:** Start at the `Module00_Intro` or `Module1A_Credits` jump tables. The sub-mode in WRAM `$11` determines which part of the sequence is running. Follow the jump table to the routine for the scene you want to change.
|
||||
|
||||
### 9.14. Bank $0D: Link Animation & OAM Data
|
||||
|
||||
**File:** `ALTTP/bank_0D.asm`
|
||||
**Address Range:** `$0D8000` - `$0DFFFF`
|
||||
**Summary:** A massive graphical database defining every frame of Link's animation. It is not executable code.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Link's Animation Sequence:** To modify an animation, find the action in `LinkOAM_AnimationSteps`. The values are indices into the `LinkOAM_PoseData` table, which defines the body parts for each frame.
|
||||
* **Link's Item Positioning:** To change how Link holds an item, find the animation frame index in `LinkOAM_AnimationSteps` and use it to find the corresponding entries in the `LinkOAM_SwordOffsetX/Y` or `LinkOAM_ShieldOffsetX/Y` tables.
|
||||
|
||||
### 9.15. Bank $0E: Tile Properties & Credits Engine
|
||||
|
||||
**File:** `ALTTP/bank_0E.asm`
|
||||
**Address Range:** `$0E8000` - `$0EFFFF`
|
||||
**Summary:** Contains fundamental game assets (font, tile properties) and the credits engine.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Tile Behavior (e.g., making a wall walkable):** Identify the tile's graphical ID and find its entry in the `OverworldTileTypes` or `UnderworldTileTypes` tables. Change its byte value to match a tile with the desired properties.
|
||||
* **Custom Tile Physics (e.g., ice):** Search for the `Underworld_LoadCustomTileTypes` function to see how alternate tile property sets are loaded for specific dungeons.
|
||||
|
||||
### 9.16. Bank $0F: Miscellaneous Game Logic & Helpers
|
||||
|
||||
**File:** `ALTTP/bank_0F.asm`
|
||||
**Address Range:** `$0F8000` - `$0FFFFF`
|
||||
**Summary:** A collection of important miscellaneous subroutines, including player death and dialogue box initiation.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Player Death Sequence:** The entry points are `PrepareToDie` and `Link_SpinAndDie`.
|
||||
* **Dialogue Box Trigger:** Search for `JSL Interface_PrepAndDisplayMessage`. The code immediately preceding it sets up the message ID to be displayed.
|
||||
|
||||
### 9.17. Bank $10-$18: Graphics Sheets for Link, Dungeon, Overworld, Sprites
|
||||
|
||||
### 9.18. Bank $19: Sound Data
|
||||
|
||||
### 9.19. Bank $1A: Miscellaneous Sprites & Cutscenes
|
||||
|
||||
**File:** `ALTTP/bank_1A.asm`
|
||||
**Address Range:** `$1A8000` - `$1AFFFF`
|
||||
**Summary:** Logic for a variety of unique sprites, NPCs, and cutscene events that are too specific for the main sprite engine.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Pyramid of Power Opening:** Search for `BatCrash` or `CreatePyramidHole`.
|
||||
* **Waterfall of Wishing Splash:** Search for `SpawnHammerWaterSplash`.
|
||||
* **Secret Item Substitution:** To understand how items under rocks are sometimes replaced by enemies, analyze `Overworld_SubstituteAlternateSecret`.
|
||||
|
||||
### 9.20. Bank $1B: Overworld Interaction & Palettes
|
||||
|
||||
**File:** `ALTTP/bank_1B.asm`
|
||||
**Address Range:** `$1B8000` - `$1BFFFF`
|
||||
**Summary:** The heart of the overworld interaction system. Manages all entrances, pits, and item-based tile interactions (digging, bombing). Also contains a very large store of palette data.
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Overworld Entrances:** To change where a door leads, find its entry in the `Overworld_Entrance...` tables at the top of the bank.
|
||||
* **Hidden Item Locations:** To change the item under a specific bush, find the correct `OverworldData_HiddenItems_Screen_XX` table and modify the entry for that bush's coordinates.
|
||||
* **Sprite/Armor Colors:** To change a color, find the correct palette in the `PaletteData` section and modify the desired color values.
|
||||
|
||||
### 9.21. Bank $1C: Text Data
|
||||
|
||||
### 9.22. Bank $1D & $1E: Advanced Sprite & Boss AI
|
||||
|
||||
**Files:** `ALTTP/bank_1D.asm`, `ALTTP/bank_1E.asm`
|
||||
**Summary:** These banks contain the specific, complex AI for most of the game's major bosses and late-game enemies (Ganon, Moldorm, Trinexx, Helmasaur King, Kholdstare, Agahnim, etc.).
|
||||
|
||||
#### Search Heuristics:
|
||||
* **Boss/Enemy AI:** To modify a specific boss or advanced enemy, search for its `Sprite_...` routine in these two banks (e.g., `Sprite_92_HelmasaurKing` in bank $1E).
|
||||
* **Sprite Dispatch Table:** The jump table at `SpriteModule_Active_Bank1E` in `bank_1E.asm` provides a comprehensive list of all sprites managed by that bank and is a good starting point for investigation.
|
||||
|
||||
### 9.23. Bank $1F: Dungeon Room Data
|
||||
|
||||
## 10. ZScream Expanded ROM Map
|
||||
|
||||
> **Last Updated:** 02/28/2025
|
||||
> **Note:** All addresses are in PC format unless otherwise stated.
|
||||
> **Note:** Some features are supported in yaze (yet another zelda3 editor) but not all.
|
||||
|
||||
ZScream reserves:
|
||||
- All space up to **1.5MB** (`0x150000`)
|
||||
- An additional **3 banks** at the end of the 2.0MB range (`0x1E8000` to `0x1FFFFF`)
|
||||
|
||||
### Bank Allocation Overview
|
||||
|
||||
| Address Range | Size | Purpose/Contents |
|
||||
|---------------------- |---------- |----------------------------------------------------|
|
||||
| `0x100000 - 0x107FFF` | 1 Bank | *(Unused?)* |
|
||||
| `0x108000 - 0x10FFFF` | 1 Bank | Title screen data, Dungeon map data |
|
||||
| `0x110000 - 0x117FFF` | 1 Bank | Default room header location |
|
||||
| | | (Old dungeon object data expansion, now moved) |
|
||||
| `0x118000 - 0x11FFFF` | 1 Bank | (Old dungeon object data expansion, now moved) |
|
||||
| `0x120000 - 0x127FFF` | 1 Bank | Expanded overlay data |
|
||||
| `0x128000 - 0x12FFFF` | 1 Bank | Custom collision data |
|
||||
| `0x130000 - 0x137FFF` | 1 Bank | Overworld map data overflow |
|
||||
| `0x138000 - 0x13FFFF` | 1 Bank | Expanded dungeon object data |
|
||||
| `0x140000 - 0x147FFF` | 1 Bank | Custom overworld data |
|
||||
| `0x148000 - 0x14FFFF` | 1 Bank | Expanded dungeon object data |
|
||||
| `0x1E0000 - 0x1E7FFF` | 1 Bank | Custom ASM patches |
|
||||
| `0x1E8000 - 0x1EFFFF` | 1 Bank | Expanded Tile16 space |
|
||||
| `0x1F0000 - 0x1FFFFF` | 2 Banks | Expanded Tile32 space |
|
||||
|
||||
## 11. Oracle of Secrets Specific Guidelines for Gemini
|
||||
|
||||
To ensure accurate and consistent modifications within the Oracle of Secrets project, adhere to the following guidelines regarding memory management and code placement:
|
||||
|
||||
- **Understand `incsrc` Order:** The order of `incsrc` directives in `Oracle_main.asm` is paramount. It dictates the final ROM layout, especially for code and data placed using `org`. Always consult `Oracle_main.asm` and `Docs/Core/MemoryMap.md` to understand the current memory allocation before introducing new `org` directives.
|
||||
|
||||
- **`org` for New Features:** When implementing new features that require significant code or data, use `org $XXXXXX` to place them in an appropriate free bank. Refer to the detailed memory map in `Docs/Core/MemoryMap.md` and the `Oracle_main.asm` comment for available and designated banks. If a new bank is needed, ensure it does not conflict with existing allocations or ZScream reserved space.
|
||||
|
||||
- **`pushpc`/`pullpc` for Patches:** For small, targeted modifications to vanilla code or data (e.g., changing a few bytes, hooking a jump), use `pushpc`/`pullpc`. These directives are ideal for non-intrusive patches that don't require a dedicated bank. Examine `Core/patches.asm` and `Util/item_cheat.asm` for examples of this usage.
|
||||
|
||||
- **Consult Memory Map:** Before any code modification involving `org` or `pushpc`/`pullpc`, always cross-reference with `Docs/Core/MemoryMap.md` and the `Oracle_main.asm` comment to prevent memory conflicts and ensure proper placement.
|
||||
|
||||
- **Prioritize Existing Conventions:** Mimic the existing style and structure of the codebase. If a new feature is similar to an existing one, follow its implementation pattern, including how it manages memory.
|
||||
|
||||
- **Avoid Arbitrary `org`:** Never use `org` without a clear understanding of the target address and its implications for the overall ROM layout. Unplanned `org` directives can lead to crashes or unexpected behavior.
|
||||
@@ -1,51 +0,0 @@
|
||||
# Asar Usage and ROM Management
|
||||
|
||||
This document outlines the best practices for using Asar and managing ROM files within the Oracle of Secrets project.
|
||||
|
||||
## Safety First: Preserve the Clean ROM
|
||||
|
||||
The most important rule is to **never modify the clean ROM directly**. The clean ROM for this project is expected to be `Roms/oos169.sfc`. All patches must be applied to a *copy* of this file. This ensures that you always have a pristine base to work from and prevents irreversible changes to the original game file.
|
||||
|
||||
The `Roms/` directory is ignored by git, so you don't have to worry about accidentally committing large ROM files.
|
||||
|
||||
## The Build Script
|
||||
|
||||
A `build.sh` script is provided to automate the build process and enforce safe ROM management.
|
||||
|
||||
### Usage
|
||||
|
||||
To build the ROM, run the script from the project root. You can optionally provide a version number.
|
||||
|
||||
**Build with a version number:**
|
||||
```sh
|
||||
./build.sh 1.0
|
||||
```
|
||||
This will create a patched ROM named `Roms/oos-v1.0.sfc`.
|
||||
|
||||
**Build without a version number:**
|
||||
```sh
|
||||
./build.sh
|
||||
```
|
||||
This will create a patched ROM named `Roms/oos-patched.sfc`.
|
||||
|
||||
### What it Does
|
||||
|
||||
1. **Copies the ROM**: It creates a copy of `Roms/oos169.sfc`.
|
||||
2. **Applies the Patch**: It runs `asar` to apply the main patch file (`Oracle_main.asm`) to the newly created ROM copy.
|
||||
3. **Output**: The final, patched ROM is placed in the `Roms/` directory.
|
||||
|
||||
## Manual Build Process (Not Recommended)
|
||||
|
||||
If you need to run the build process manually, follow these steps:
|
||||
|
||||
1. **Create a copy of the clean ROM**:
|
||||
```sh
|
||||
cp Roms/oos169.sfc Roms/my_patched_rom.sfc
|
||||
```
|
||||
|
||||
2. **Run Asar**:
|
||||
```sh
|
||||
asar Oracle_main.asm Roms/my_patched_rom.sfc
|
||||
```
|
||||
|
||||
Using the `build.sh` script is highly recommended to avoid mistakes.
|
||||
@@ -1,332 +0,0 @@
|
||||
# Oracle of Secrets Development Guidelines
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
This document outlines the established coding conventions, architectural patterns, and best practices for the Oracle of Secrets project. Adhering to these guidelines is crucial for maintaining code quality, consistency, and long-term maintainability.
|
||||
|
||||
The Oracle of Secrets is a large-scale ROM hack of "The Legend of Zelda: A Link to the Past" for the Super Nintendo. It is built using the `asar` assembler and features a highly modular and data-driven architecture. The project's core philosophy is to replace hardcoded vanilla logic with flexible, data-driven systems, allowing for easier expansion and modification.
|
||||
|
||||
## 2. Core Architecture
|
||||
|
||||
### 2.1. Game State Management
|
||||
|
||||
The game's main loop and state management are handled in `bank_00.asm`. The `Module_MainRouting` routine acts as the primary state machine, using a jump table to execute the logic for the current game state (e.g., Overworld, Underworld, Menu).
|
||||
|
||||
### 2.2. Memory Management
|
||||
|
||||
The project makes extensive use of both WRAM and SRAM to store custom data and game state.
|
||||
|
||||
* **WRAM (Work RAM):** Located at `$7E0000`, WRAM holds the game's volatile state. A custom region starting at `$7E0730` is reserved for new features, including the Time System, Mask System, and custom item states.
|
||||
* **SRAM (Save RAM):** Located at `$7EF000`, SRAM stores the player's save data. The save format has been significantly expanded to accommodate new items, progress flags (`OOSPROG`), and new item data.
|
||||
|
||||
### 2.3. Assembly Best Practices (`asar`)
|
||||
|
||||
To ensure modern and maintainable assembly code, the project adheres to the following `asar` best practices:
|
||||
|
||||
* **`org` for New Code and Data:** Use `org $XXXXXX` to place larger blocks of new code or data into designated free space banks. The `incsrc` order in `Oracle_main.asm` is critical when using `org`, as it directly determines the final ROM layout. Moving `org` blocks or changing the `incsrc` order can lead to memory conflicts or incorrect addressing.
|
||||
* **`pushpc`/`pullpc` for Targeted Patches:** Use `pushpc`/`pullpc` for small, targeted modifications to existing vanilla code or data. This directive temporarily changes the program counter, allowing a small patch to be inserted at a specific address without affecting the surrounding `org` context. Files like `Core/patches.asm` and `Util/item_cheat.asm` extensively use `pushpc`/`pullpc` for this purpose.
|
||||
* **Scoping:** Use labels followed by `{}` to define local scopes for new logic blocks. This is the established convention, and `subroutine`/`endsubroutine` are not used.
|
||||
* **Data Structures:** Use `struct` for complex, related data (e.g., sprite state) and `table` for jump tables or data arrays. This improves readability and reduces errors from manual offset calculations.
|
||||
* **Constants:** Use `!` or `define()` to create named constants for RAM/SRAM addresses, item IDs, sprite states, and other "magic numbers." This makes the code self-documenting and easier to maintain.
|
||||
|
||||
### 2.4. Namespace Architecture
|
||||
|
||||
Oracle of Secrets uses a mixed namespace architecture to organize code and manage symbol visibility:
|
||||
|
||||
* **`Oracle` Namespace:** Most custom code is placed within the `namespace Oracle { }` block. This includes Items, Menu, Masks, Time System, and most custom features.
|
||||
* **ZScream (No Namespace):** The `ZSCustomOverworld` system operates outside any namespace, as it needs to hook directly into vanilla bank addresses.
|
||||
* **Cross-Namespace Calling:** When Oracle code needs to call ZScream functions, or vice versa, proper exports must be defined:
|
||||
|
||||
```asm
|
||||
// In ZScream file:
|
||||
LoadOverworldSprites_Interupt:
|
||||
{
|
||||
; ... ZScream code ...
|
||||
RTL
|
||||
}
|
||||
|
||||
// Export to Oracle namespace:
|
||||
namespace Oracle
|
||||
{
|
||||
Oracle_LoadOverworldSprites_Interupt = LoadOverworldSprites_Interupt
|
||||
}
|
||||
```
|
||||
|
||||
* **Bridge Functions:** When ZScream needs to call Oracle code, use a bridge function pattern:
|
||||
|
||||
```asm
|
||||
// Oracle implementation:
|
||||
namespace Oracle
|
||||
{
|
||||
CheckIfNight:
|
||||
; Main implementation
|
||||
RTL
|
||||
}
|
||||
|
||||
// Bridge function (no namespace):
|
||||
ZSO_CheckIfNight:
|
||||
{
|
||||
JSL Oracle_CheckIfNight ; Can call INTO Oracle
|
||||
RTL
|
||||
}
|
||||
|
||||
// Export bridge:
|
||||
namespace Oracle
|
||||
{
|
||||
Oracle_ZSO_CheckIfNight = ZSO_CheckIfNight
|
||||
}
|
||||
```
|
||||
|
||||
**Important:** Always use the `Oracle_` prefix when calling exported functions from within the Oracle namespace. See `Docs/World/Overworld/ZSCustomOverworldAdvanced.md` Section 5 for detailed examples.
|
||||
|
||||
### 2.5. Processor State Management
|
||||
|
||||
The 65816 processor has critical flags (M and X) that control register sizes:
|
||||
|
||||
* **M Flag (bit 5):** Controls Accumulator size (0=16-bit, 1=8-bit)
|
||||
* **X Flag (bit 4):** Controls Index register size (0=16-bit, 1=8-bit)
|
||||
|
||||
**Best Practices:**
|
||||
|
||||
1. **Initialize at function entry:**
|
||||
```asm
|
||||
MyFunction:
|
||||
PHP ; Save caller's state
|
||||
SEP #$30 ; Set to known 8-bit state
|
||||
; ... your code ...
|
||||
PLP ; Restore caller's state
|
||||
RTL
|
||||
```
|
||||
|
||||
2. **Be explicit about sizes:**
|
||||
```asm
|
||||
REP #$20 ; A = 16-bit
|
||||
LDA.w #$1234 ; Load 16-bit value
|
||||
|
||||
SEP #$20 ; A = 8-bit
|
||||
LDA.b #$12 ; Load 8-bit value only
|
||||
```
|
||||
|
||||
3. **Document function requirements:**
|
||||
```asm
|
||||
; Function: CalculateOffset
|
||||
; Inputs: A=16-bit (offset), X=8-bit (index)
|
||||
; Outputs: A=16-bit (result)
|
||||
; Status: Returns with P unchanged (uses PHP/PLP)
|
||||
```
|
||||
|
||||
4. **Cross-namespace calls:** Be especially careful when calling between Oracle and ZScream code, as they may use different processor states.
|
||||
|
||||
See `Docs/General/Troubleshooting.md` Section 3 for common processor state issues and solutions.
|
||||
|
||||
## 3. Key Custom Systems
|
||||
|
||||
Oracle of Secrets introduces several major custom systems that form the foundation of the hack.
|
||||
|
||||
### 3.1. `ZSCustomOverworld`
|
||||
|
||||
`ZSCustomOverworld` is a data-driven system for managing the overworld. It is configured via a series of tables located at `org $288000` in `Overworld/ZSCustomOverworld.asm`. This system controls:
|
||||
|
||||
* **Palettes:** Day/night and seasonal palette transitions.
|
||||
* **Graphics:** Custom graphics sets for different areas.
|
||||
* **Overlays:** Data-driven overlays for weather effects and other visual enhancements.
|
||||
* **Layouts:** Custom tile arrangements and area layouts.
|
||||
|
||||
### 3.2. Time System
|
||||
|
||||
The Time System, implemented in `Overworld/time_system.asm`, provides a full day/night cycle. It interacts closely with `ZSCustomOverworld` to manage palette changes and other time-of-day effects.
|
||||
|
||||
### 3.3. Mask System
|
||||
|
||||
The Mask System, located in the `Masks/` directory, allows Link to transform into different forms with unique abilities. The core transformation logic is handled by the `Link_TransformMask` routine. Each mask has its own file (e.g., `deku_mask.asm`, `zora_mask.asm`) that defines its specific behavior and abilities. The system uses custom WRAM to store the current mask and its state.
|
||||
|
||||
### 3.4. Custom Menu & HUD
|
||||
|
||||
The `Menu/` directory contains a completely new menu and HUD system. This includes:
|
||||
|
||||
* A two-page menu for items and quest status.
|
||||
* A custom item layout and drawing routines.
|
||||
* A detailed quest status screen with new icons and text.
|
||||
* A custom HUD with a new magic meter and layout.
|
||||
|
||||
### 3.5. Custom Items
|
||||
|
||||
The `Items/` directory contains the implementation for all new items, such as the Goldstar, Portal Rod, and Ocarina songs. These items often have complex interactions with the player state machine and other game systems.
|
||||
|
||||
### 3.6. Sprite Engine
|
||||
|
||||
While the main sprite engine from the original game is still used (`bank_06.asm`), Oracle of Secrets introduces a custom sprite system for managing complex sprite behaviors and interactions. The `Sprites/` directory contains the code for all new custom sprites, including NPCs, enemies, and bosses.
|
||||
`Core/sprite_functions.asm`, `Core/sprite_macros.asm`, `Core/sprite_new_table.asm` and `Sprites/all_sprites.asm` contain the overriden sprite logic and includes for all custom sprites.
|
||||
|
||||
## 4. Coding Standards & Style
|
||||
|
||||
### 4.1. File and Directory Structure
|
||||
|
||||
The project is organized into a modular directory structure. New code should be placed in the appropriate directory based on its functionality (e.g., new items in `Items/`, new sprites in `Sprites/`).
|
||||
|
||||
### 4.2. Naming Conventions
|
||||
|
||||
* **Labels:** Use descriptive, CamelCase names for labels (e.g., `LinkState_Swimming`, `Menu_DrawItemName`).
|
||||
* **Variables:** Use uppercase for constants (`!CONSTANT_NAME`) and CamelCase for RAM/SRAM variables (`VariableName`).
|
||||
* **Macros:** Use CamelCase for macro names (`%MacroName()`).
|
||||
|
||||
### 4.3. Commenting
|
||||
|
||||
Comments should be used to explain the *why* behind a piece of code, not the *what*. For complex logic, a brief explanation of the algorithm or purpose is helpful. Avoid excessive or obvious comments.
|
||||
|
||||
### 4.4. Macros
|
||||
|
||||
Macros are used extensively to simplify common tasks and improve code readability. When creating new macros, follow the existing style and ensure they are well-documented.
|
||||
|
||||
## 5. Debugging
|
||||
|
||||
### 5.1. Common Issues
|
||||
|
||||
* **`BRK` Instructions:** A `BRK` instruction indicates a crash. This is often caused by:
|
||||
* Jumping to invalid memory (uninitialized ROM, data instead of code)
|
||||
* P-register mismatch (e.g., calling a 16-bit routine when in 8-bit mode)
|
||||
* Stack corruption (unbalanced push/pop, JSR/JSL vs RTS/RTL mismatch)
|
||||
* Return without matching call (RTL executed without previous JSL)
|
||||
|
||||
* **P-Register Mismatches:** Always ensure the M and X flags of the processor status register are in the correct state before calling a routine. Use `SEP` and `REP` to switch between 8-bit and 16-bit modes as needed.
|
||||
|
||||
* **Stack Corruption:** Ensure push/pop operations are balanced and that JSR is paired with RTS (2 bytes) while JSL is paired with RTL (3 bytes). Never mix them.
|
||||
|
||||
* **Namespace Visibility:** If you get "label not found" errors, verify:
|
||||
* Label is exported with `Oracle_` prefix
|
||||
* File is included in correct build order in `Oracle_main.asm`
|
||||
* Namespace block is properly closed
|
||||
|
||||
* **Memory Conflicts:** If you get "overwrote some code" warnings:
|
||||
* Check for overlapping `org` directives
|
||||
* Use `assert pc() <= $ADDRESS` to protect boundaries
|
||||
* Review ROM map in Section 6 for available space
|
||||
|
||||
**For comprehensive troubleshooting guidance, see `Docs/General/Troubleshooting.md` which covers:**
|
||||
- BRK crash debugging with emulator tools
|
||||
- Stack corruption patterns
|
||||
- Processor status register issues
|
||||
- Cross-namespace calling problems
|
||||
- Memory conflicts and bank collisions
|
||||
- Graphics/DMA timing issues
|
||||
- ZScream-specific problems
|
||||
|
||||
### 5.2. Debugging Tools
|
||||
|
||||
* **Mesen-S (Recommended):** The most powerful SNES debugger with:
|
||||
* Execution breakpoints with conditions
|
||||
* Memory watchpoints (read/write/execute)
|
||||
* Stack viewer
|
||||
* Event viewer (NMI/IRQ timing)
|
||||
* Live memory updates
|
||||
|
||||
* **BSNES-Plus:** Cycle-accurate emulator with:
|
||||
* Memory editor with search
|
||||
* Tilemap and VRAM viewers
|
||||
* Debugger with disassembly
|
||||
|
||||
* **`!DEBUG` Flag:** The `!DEBUG` flag in `Util/macros.asm` can be used to enable or disable build-time logging.
|
||||
|
||||
* **`%print_debug()` Macro:** This macro can be used to print debug messages and register values during assembly. It is an invaluable tool for tracing code execution and identifying issues.
|
||||
|
||||
* **Breadcrumb Tracking:** Add markers to narrow down crash locations:
|
||||
```asm
|
||||
LDA.b #$01 : STA.l $7F5000 ; Breadcrumb 1
|
||||
JSL SuspiciousFunction
|
||||
LDA.b #$02 : STA.l $7F5000 ; Breadcrumb 2
|
||||
; After crash, check $7F5000 to see which breadcrumb was reached
|
||||
```
|
||||
|
||||
* **Vanilla Disassembly:** The ALTTP disassembly in `ALTTP/` is the primary reference for the original game's code. Use it to understand the context of vanilla routines that are being hooked or modified.
|
||||
|
||||
### 5.3. Debugging Checklist
|
||||
|
||||
When encountering an issue:
|
||||
|
||||
1. ✅ Check error message carefully - Asar errors are usually precise
|
||||
2. ✅ Verify namespace - Is label prefixed correctly?
|
||||
3. ✅ Check stack balance - Equal push/pop counts?
|
||||
4. ✅ Verify processor state - REP/SEP correct for operation?
|
||||
5. ✅ Check memory bounds - Assertions in place?
|
||||
6. ✅ Test in Mesen-S first - Best debugger for SNES
|
||||
7. ✅ Use breadcrumbs - Narrow down crash location
|
||||
8. ✅ Check build order - Files included in correct order?
|
||||
9. ✅ Review recent changes - Compare with known working version
|
||||
10. ✅ Read vanilla code - Understand what you're hooking
|
||||
|
||||
## 6. ROM Map & Memory Layout
|
||||
|
||||
Oracle of Secrets utilizes the ZScream expanded ROM map, providing significant additional space for new code and data. The allocation of custom code and data within these banks is managed through `org` directives in the assembly files. The `incsrc` order in `Oracle_main.asm` is crucial, as it dictates the final placement of these blocks in the ROM.
|
||||
|
||||
Here is a detailed overview of the custom ROM bank allocations:
|
||||
|
||||
| Bank (Hex) | Address Range (PC) | Purpose / Contents | Defining File(s) |
|
||||
|------------|--------------------|--------------------------------------------------------|---------------------------------------|
|
||||
| $20 | `$208000` - `$20FFFF` | Expanded Music | `Music/all_music.asm` |
|
||||
| $21-$27 | | ZScream Reserved | |
|
||||
| $28 | `$288000` - `$28FFFF` | ZSCustomOverworld data and code | `Overworld/ZSCustomOverworld.asm` |
|
||||
| $29-$2A | | ZScream Reserved | |
|
||||
| $2B | `$2B8000` - `$2BFFFF` | Items | `Items/all_items.asm` |
|
||||
| $2C | `$2C8000` - `$2CFFFF` | Underworld/Dungeons | `Dungeons/dungeons.asm` |
|
||||
| $2D | `$2D8000` - `$2DFFFF` | Menu | `Menu/menu.asm` |
|
||||
| $2E | `$2E8000` - `$2EFFFF` | HUD | `Menu/menu.asm` |
|
||||
| $2F | `$2F8000` - `$2FFFFF` | Expanded Message Bank | `Core/message.asm` |
|
||||
| $30 | `$308000` - `$30FFFF` | Sprites | `Sprites/all_sprites.asm` |
|
||||
| $31 | `$318000` - `$31FFFF` | Sprites | `Sprites/all_sprites.asm` |
|
||||
| $32 | `$328000` - `$32FFFF` | Sprites | `Sprites/all_sprites.asm` |
|
||||
| $33 | `$338000` - `$33FFFF` | Moosh Form Gfx and Palette | `Masks/all_masks.asm` |
|
||||
| $34 | `$348000` - `$34FFFF` | Time System, Custom Overworld Overlays, Gfx | `Masks/all_masks.asm` |
|
||||
| $35 | `$358000` - `$35FFFF` | Deku Link Gfx and Palette | `Masks/all_masks.asm` |
|
||||
| $36 | `$368000` - `$36FFFF` | Zora Link Gfx and Palette | `Masks/all_masks.asm` |
|
||||
| $37 | `$378000` - `$37FFFF` | Bunny Link Gfx and Palette | `Masks/all_masks.asm` |
|
||||
| $38 | `$388000` - `$38FFFF` | Wolf Link Gfx and Palette | `Masks/all_masks.asm` |
|
||||
| $39 | `$398000` - `$39FFFF` | Minish Link Gfx | `Masks/all_masks.asm` |
|
||||
| $3A | `$3A8000` - `$3AFFFF` | Mask Routines, Custom Ancillae (Deku Bubble) | `Masks/all_masks.asm` |
|
||||
| $3B | `$3B8000` - `$3BFFFF` | GBC Link Gfx | `Masks/all_masks.asm` |
|
||||
| $3C | | Unused | |
|
||||
| $3D | | ZS Tile16 | |
|
||||
| $3E | | LW ZS Tile32 | |
|
||||
| $3F | | DW ZS Tile32 | |
|
||||
| $40 | `$408000` - `$40FFFF` | LW World Map | `Overworld/overworld.asm` |
|
||||
| $41 | `$418000` - `$41FFFF` | DW World Map | `Overworld/overworld.asm` |
|
||||
| Patches | Various | Targeted modifications within vanilla ROM addresses | `Core/patches.asm`, `Util/item_cheat.asm` |
|
||||
|
||||
For a more detailed breakdown of the ROM map, refer to the `ZS ROM MAP.txt` file in the `Core/` directory, and `Docs/Core/MemoryMap.md` for a comprehensive overview of all custom memory regions.
|
||||
|
||||
---
|
||||
|
||||
## 7. Documentation
|
||||
|
||||
The following documents have been generated by analyzing the codebase and project files. They serve as key references for understanding the project's architecture and gameplay systems.
|
||||
|
||||
* **`Docs/Core/MemoryMap.md`:** A comprehensive map of all custom WRAM and SRAM variables, including repurposed vanilla blocks. See [MemoryMap.md](../Core/MemoryMap.md) for details.
|
||||
|
||||
* **`Docs/Core/Ram.md`:** High-level overview of WRAM and SRAM usage with verified custom variables. See [Ram.md](../Core/Ram.md) for details.
|
||||
|
||||
* **`Docs/World/Overworld/ZSCustomOverworldAdvanced.md`:** Advanced technical guide for ZScream integration, including hook architecture, sprite loading system, cross-namespace integration, and performance considerations. See [ZSCustomOverworldAdvanced.md](../World/Overworld/ZSCustomOverworldAdvanced.md) for details.
|
||||
|
||||
* **`Docs/General/Troubleshooting.md`:** Comprehensive troubleshooting guide covering BRK crashes, stack corruption, processor state issues, namespace problems, memory conflicts, and graphics issues. See [Troubleshooting.md](Troubleshooting.md) for details.
|
||||
|
||||
* **`Docs/QuestFlow.md`:** A detailed guide to the main story and side-quest progression, including trigger conditions and progression flags. See [QuestFlow.md](../QuestFlow.md) for details.
|
||||
|
||||
* **`Docs/SpriteCreationGuide.md`:** A step-by-step tutorial for creating a new custom sprite using the project's frameworks and conventions. See [SpriteCreationGuide.md](SpriteCreationGuide.md) for details.
|
||||
|
||||
* **`Docs/Menu.md`:** A detailed analysis of the custom menu and HUD systems. See [Menu.md](Menu.md) for details.
|
||||
|
||||
* **`Docs/Items.md`:** A detailed guide to the custom and modified items in the game. See [Items.md](Items.md) for details.
|
||||
|
||||
* **`Docs/Music.md`:** A guide to the custom music tracks and sound effects, including how to add new audio. See [Music.md](Music.md) for details.
|
||||
|
||||
* **`Docs/Masks.md`:** A comprehensive overview of the Mask System, including each mask's abilities and implementation details. See [Masks.md](Masks.md) for details.
|
||||
|
||||
* **`Docs/Dungeons.md`:** A breakdown of all dungeons, including layouts, enemy placements, and puzzle solutions. See [Dungeons.md](Dungeons.md) for details.
|
||||
|
||||
* **`Docs/Overworld.md`:** An analysis of the overworld systems, including `ZSCustomOverworld`, the time system, and other custom features. See [Overworld.md](Overworld.md) for details.
|
||||
|
||||
* **`Docs/NPCs.md`:** An analysis of the various NPC sprites. See [NPCs.md](NPCs.md) for details.
|
||||
|
||||
* **`Docs/Bosses.md`:** An analysis of the custom boss sprites. See [Bosses.md](Bosses.md) for details.
|
||||
|
||||
* **`Docs/Objects.md`:** An analysis of interactive object sprites. See [Objects.md](Objects.md) for details.
|
||||
|
||||
* **`Docs/Overlord.md`:** An analysis of the Overlord sprite system. See [Overlord.md](Overlord.md) for details.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,177 +0,0 @@
|
||||
# Quest & Event Flow
|
||||
|
||||
This document outlines the progression of the main story and major side-quests. It details the flags and conditions that control the game's narrative flow, making it easier to understand how events are triggered.
|
||||
|
||||
## 1. Main Quest Progression
|
||||
|
||||
*This section provides a step-by-step guide to the main story, detailing the sequence of events, required items, and the flags that are set at each milestone.*
|
||||
|
||||
### Chapter 0: A Hero is Born
|
||||
|
||||
1. **Trigger:** The game begins.
|
||||
2. **Events:**
|
||||
* The Farore intro sequence plays, explaining the backstory of the Triforce and the sealing of the Sacred Realm.
|
||||
* Link is shipwrecked and awakens in the Eon Abyss.
|
||||
* The Kydrog intro sequence plays, showing the game's antagonist.
|
||||
* Link is transported to the Temporal Pyramid.
|
||||
3. **Player Actions:**
|
||||
* Navigate the Temporal Pyramid to find the **Moon Pearl**.
|
||||
* Exit the pyramid to arrive in the Forest of Dreams.
|
||||
* Obtain the **Lv1 Sword and Shield**.
|
||||
4. **Progression Flags:**
|
||||
|
||||
| Flag | Address | Value/Bit | Notes |
|
||||
|------------|----------|----------------|-------------------------------------|
|
||||
| `GameState`| `$7EF3C5`| `0x02` | Set after the Farore intro. |
|
||||
| `OosProg2` | `$7EF3C6`| `bit $04` set | Set after the Kydrog intro. |
|
||||
|
||||
### Chapter 1: The Maku Tree Awakens
|
||||
|
||||
1. **Trigger:** Player talks to the Maku Tree for the first time (`Sprites/NPCs/maku_tree.asm`).
|
||||
2. **Events:** The Maku Tree speaks to Link, explaining the plight of the land.
|
||||
3. **Reward:** A Heart Container is given to the player (`Link_ReceiveItem` with Y=`$3E`).
|
||||
4. **Progression Flags & Consequences:**
|
||||
|
||||
| Flag | Address | Value/Bit | Consequence |
|
||||
|-----------------|----------|---------------|--------------------------------------------------------------------------|
|
||||
| `MakuTreeQuest` | `$7EF3D4`| `0x01` | The Maku Tree will now use a different dialogue branch on subsequent talks. |
|
||||
| `MapIcon` | `$7EF3C7`| `0x01` | A red 'X' appears on the map over the Mushroom Grotto. |
|
||||
| `OOSPROG` | `$7EF3D6`| `bit $02` set | A major story flag indicating the quest has officially begun. |
|
||||
|
||||
### Chapter 2: The Mushroom Grotto (D1)
|
||||
|
||||
1. **Trigger:** Player enters the Mushroom Grotto, west of Wayward Village.
|
||||
2. **Events:** Player navigates the dungeon, facing the Vampire Bat miniboss and the Mothra boss.
|
||||
3. **Reward:** **Bow**.
|
||||
|
||||
### Chapter 3: The Tail Palace (D2)
|
||||
|
||||
1. **Trigger:** Player enters Tail Palace.
|
||||
2. **Events:** Player defeats the boss, a vanilla-style Big Moldorm.
|
||||
3. **Reward:** **Roc's Feather**.
|
||||
4. **World State Changes:** After completion, Deku NPCs will appear in the overworld area near Tail Palace.
|
||||
|
||||
### Chapter 4: The Path to the Castle
|
||||
|
||||
1. **Trigger:** This is a multi-part quest chain required to access Kalyxo Castle.
|
||||
2. **Player Actions:**
|
||||
* **Ocarina:** Complete the "Lost Ranch Girl" side-quest to obtain the Ocarina.
|
||||
* **Song of Healing:** Learn the Song of Healing from the Happy Mask Salesman.
|
||||
* **Running Boots:** Play the Song of Healing for the sick child in Wayward Village to receive the Running Boots.
|
||||
* **Book of Secrets:** Use the Running Boots to get the Book of Secrets from the village library.
|
||||
3. **Consequence:** The Book of Secrets is required to open the gates to Kalyxo Castle.
|
||||
|
||||
### Chapter 5: Kalyxo Castle (D3)
|
||||
|
||||
1. **Trigger:** Player enters Kalyxo Castle.
|
||||
2. **Required Items:** Book of Secrets.
|
||||
3. **Events:** Player defeats the Armos Knights boss.
|
||||
4. **Reward:** **Meadow Blade (Lv2 Sword)**.
|
||||
|
||||
### Chapter 6: The Shrine of Wisdom (S1)
|
||||
|
||||
1. **Trigger:** Player enters the Shrine of Wisdom.
|
||||
2. **Events:** Player must navigate a swampy overworld area.
|
||||
3. **Reward:** **Zora Flippers**.
|
||||
|
||||
### Chapter 7: Zora Temple (D4)
|
||||
|
||||
1. **Trigger:** Player enters the Zora Temple.
|
||||
2. **Required Items:** Zora Flippers.
|
||||
3. **Events:** Player defeats an advanced variant of the Arrghus boss.
|
||||
4. **Reward:** **Hookshot**, **Zora Mask** (via side-quest within the dungeon).
|
||||
|
||||
### Chapter 8: Glacia Estate (D5)
|
||||
|
||||
1. **Trigger:** Player enters Glacia Estate.
|
||||
2. **Required Items:** **Goldstar** (from the "Old Man Mountain Quest").
|
||||
3. **Events:** Player navigates ice puzzles and defeats the Twinrova boss.
|
||||
4. **Reward:** **Fire Rod**.
|
||||
|
||||
### Chapter 9: The Shrine of Power (S2)
|
||||
|
||||
1. **Trigger:** Player enters the Shrine of Power.
|
||||
2. **Reward:** **Power Glove**.
|
||||
|
||||
### Chapter 10: Goron Mines (D6)
|
||||
|
||||
1. **Trigger:** Player enters the Goron Mines.
|
||||
2. **Required Items:** **Power Glove**, Completion of the "Goron Mines Quest".
|
||||
3. **Events:** Player defeats the Lanmolas and the King Dodongo (Helmasaur variant) boss.
|
||||
4. **Reward:** **Hammer**.
|
||||
|
||||
### Chapter 11: Dragon Ship (D7)
|
||||
|
||||
1. **Trigger:** Player enters the Dragon Ship.
|
||||
2. **Reward:** **Somaria Rod**.
|
||||
|
||||
### Chapter 12: The Shrine of Courage (S3)
|
||||
|
||||
1. **Trigger:** Player enters the Shrine of Courage.
|
||||
2. **Events:** Player defeats the boss Vaati (Vitreous variant).
|
||||
3. **Reward:** **Mirror Shield**.
|
||||
|
||||
### Chapter 13: Fortress of Secrets (D8)
|
||||
|
||||
1. **Trigger:** Player enters the Fortress of Secrets.
|
||||
2. **Events:** Player defeats Dark Link.
|
||||
3. **Reward:** **Portal Rod**.
|
||||
|
||||
### Chapter 14: The Eon Core (Endgame)
|
||||
|
||||
1. **Trigger:** Player enters the final dungeon.
|
||||
2. **Events:** Player faces the final bosses: Kydreeok and Ganon.
|
||||
3. **Reward:** **The Triforce**.
|
||||
|
||||
---
|
||||
|
||||
## 2. Major Side-Quests
|
||||
|
||||
### The Lost Ranch Girl (Ocarina Quest)
|
||||
|
||||
1. **Mushroom:** Get a Mushroom from the old woman's house in the Mushroom Grotto area.
|
||||
2. **Magic Powder:** Trade the Mushroom to the Potion Shop owner. Leave the area and return later to receive the Magic Powder.
|
||||
3. **Ocarina:** Use the Magic Powder on the sleeping Cucco in the Ranch House. This wakes it up and it gives you the Ocarina.
|
||||
|
||||
### The Mask Salesman
|
||||
|
||||
1. **Trigger:** Player must have the Ocarina.
|
||||
2. **Action:** Talk to the Happy Mask Salesman.
|
||||
3. **Reward:** He teaches Link the **Song of Healing**.
|
||||
|
||||
### The Zora Mask
|
||||
|
||||
1. **Trigger:** Player talks to the Zora Princess in the Zora Temple. She gives message `$0C5`.
|
||||
2. **Action:** Player must play the Song of Healing.
|
||||
3. **Reward:** The princess gives the player the **Zora Mask**.
|
||||
4. **Flag:** `ZoraMask` (`$7EF347`) is set in SRAM.
|
||||
|
||||
### The Wolf Mask
|
||||
|
||||
1. **Trigger:** A Wolfos sprite appears outside Kalyxo Castle at night.
|
||||
2. **Action:** Player must defeat the Wolfos and then play the Song of Healing.
|
||||
3. **Reward:** **Wolf Mask**.
|
||||
|
||||
### Old Man Mountain Quest
|
||||
|
||||
1. **Trigger:** Player takes the warp portal at the northwest point of Mount Snowpeak.
|
||||
2. **Action:** Enter the Lava Lands cave to find an Old Man sprite. Escort him to a rock formation and use the Magic Mirror.
|
||||
3. **Reward:** **Goldstar** (upgrade for the Hookshot).
|
||||
|
||||
### Goron Mines Quest
|
||||
|
||||
1. **Trigger:** Player needs to open the Goron Mines.
|
||||
2. **Required Item:** Power Glove.
|
||||
3. **Action:**
|
||||
* Collect five pieces of Goron Rock Meat from Lupo Mountain.
|
||||
* Give the five pieces to the Kalyxian Goron NPC in the desert.
|
||||
4. **Consequence:** The Goron NPC opens the entrance to the Goron Mines.
|
||||
|
||||
### The Magic Bean
|
||||
|
||||
1. **Purchase:** Player buys the Magic Bean from the Bean Vendor for 100 rupees. Requires an empty bottle.
|
||||
2. **Planting:** Player takes the bean to the fertile soil patch on the ranch. `MagicBeanProg` (`$7EF39B`) has bit `$01` set.
|
||||
3. **Watering:** Player plays the Song of Storms. `MagicBeanProg` has bit `$04` set.
|
||||
4. **Pollination:** Player must release a Good Bee from a bottle near the bean sprout. `MagicBeanProg` has bit `$02` set.
|
||||
5. **Growth:** After 3 in-game day/night cycles, the beanstalk grows into a large flower.
|
||||
6. **Reward:** The player can ride the flower to a Heart Container. `MagicBeanProg` has bit `$40` set upon completion.
|
||||
@@ -1,877 +0,0 @@
|
||||
# Sprite Creation Guide
|
||||
|
||||
This guide provides a step-by-step walkthrough for creating a new custom sprite in Oracle of Secrets using the project's modern sprite system.
|
||||
|
||||
## 1. File Setup
|
||||
|
||||
1. **Create the Sprite File:** Create a new `.asm` file for your sprite in the appropriate subdirectory of `Sprites/`:
|
||||
* `Sprites/Enemies/` - For enemy sprites
|
||||
* `Sprites/Bosses/` - For boss sprites
|
||||
* `Sprites/NPCs/` - For non-playable character sprites
|
||||
* `Sprites/Objects/` - For interactive objects and items
|
||||
|
||||
2. **Include the File:** Open `Sprites/all_sprites.asm` and add an `incsrc` directive to include your new file. The file must be placed in the correct bank section:
|
||||
* **Bank 30** (`$308000`) - First bank, includes `sprite_new_table.asm` and some core sprites
|
||||
* **Bank 31** (`$318000`) - Second bank, includes `sprite_functions.asm` and more sprites
|
||||
* **Bank 32** (`$328000`) - Third bank for additional sprites
|
||||
* **Bank 2C** (Dungeon bank) - For sprites that are part of dungeon-specific content
|
||||
|
||||
Example:
|
||||
```asm
|
||||
; In Sprites/all_sprites.asm
|
||||
org $318000 ; Bank 31
|
||||
%log_start("my_new_enemy", !LOG_SPRITES)
|
||||
incsrc "Sprites/Enemies/MyNewEnemy.asm"
|
||||
%log_end("my_new_enemy", !LOG_SPRITES)
|
||||
```
|
||||
|
||||
3. **Assign a Sprite ID:** Choose an unused sprite ID for your sprite. You can either:
|
||||
* Use a completely new ID (e.g., `$A0` through `$FF` range)
|
||||
* Override a vanilla sprite ID (for replacing existing sprites)
|
||||
* Share an ID with another sprite and use `SprSubtype` to differentiate behaviors
|
||||
|
||||
## 2. Sprite Properties
|
||||
|
||||
At the top of your new sprite file, define its core properties using the provided template. These `!` constants are used by the `%Set_Sprite_Properties` macro to automatically configure the sprite's behavior and integrate it into the game.
|
||||
|
||||
```asm
|
||||
; =========================================================
|
||||
; Sprite Properties
|
||||
; =========================================================
|
||||
|
||||
!SPRID = $XX ; CHOOSE AN UNUSED SPRITE ID or use a constant like Sprite_MyNewEnemy
|
||||
!NbrTiles = 02 ; Number of 8x8 tiles used in the largest frame
|
||||
!Harmless = 00 ; 00 = Harmful, 01 = Harmless
|
||||
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
||||
!Health = 10 ; Number of Health the sprite has
|
||||
!Damage = 04 ; Damage dealt to Link on contact (08 = whole heart, 04 = half heart)
|
||||
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
||||
!ImperviousAll = 00 ; 00 = Can be attacked, 01 = all attacks clink harmlessly
|
||||
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
||||
!Shadow = 00 ; 00 = don't draw shadow, 01 = draw a shadow
|
||||
!Palette = 00 ; Unused in this template (can be 0 to 7)
|
||||
!Hitbox = 08 ; 00 to 31, can be viewed in sprite draw tool
|
||||
!Persist = 00 ; 01 = sprite continues to live offscreen
|
||||
!Statis = 00 ; 00 = sprite is alive? (kill all enemies room)
|
||||
!CollisionLayer = 00 ; 01 = will check both layers for collision
|
||||
!CanFall = 00 ; 01 = sprite can fall in holes, 00 = can't fall
|
||||
!DeflectArrow = 00 ; 01 = deflect arrows
|
||||
!WaterSprite = 00 ; 01 = can only walk in shallow water
|
||||
!Blockable = 00 ; 01 = can be blocked by Link's shield
|
||||
!Prize = 01 ; 00-15 = the prize pack the sprite will drop from
|
||||
!Sound = 00 ; 01 = Play different sound when taking damage
|
||||
!Interaction = 00 ; ?? No documentation
|
||||
!Statue = 00 ; 01 = Sprite is a statue
|
||||
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
||||
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
||||
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
||||
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
||||
|
||||
; This macro MUST be called after the properties
|
||||
%Set_Sprite_Properties(Sprite_MyNewEnemy_Prep, Sprite_MyNewEnemy_Long)
|
||||
```
|
||||
|
||||
### Property Details
|
||||
|
||||
**Memory Mapping:** The `%Set_Sprite_Properties` macro writes these properties to specific ROM addresses:
|
||||
* `$0DB080+!SPRID` - OAM/Harmless/HVelocity/NbrTiles
|
||||
* `$0DB173+!SPRID` - Sprite HP
|
||||
* `$0DB266+!SPRID` - Sprite Damage
|
||||
* `$0DB359+!SPRID` - Death Animation/Impervious/Shadow/Palette flags
|
||||
* `$0DB44C+!SPRID` - Collision Layer/Statis/Persist/Hitbox
|
||||
* `$0DB53F+!SPRID` - DeflectArrow/Boss/CanFall flags
|
||||
* `$0DB632+!SPRID` - Interaction/WaterSprite/Blockable/Sound/Prize
|
||||
* `$0DB725+!SPRID` - Statue/DeflectProjectiles/Impervious flags
|
||||
|
||||
The macro also sets up the jump table entries at:
|
||||
* `$069283+(!SPRID*2)` - Vanilla Sprite Main Pointer
|
||||
* `$06865B+(!SPRID*2)` - Vanilla Sprite Prep Pointer
|
||||
* `NewSprRoutinesLong+(!SPRID*3)` - New Long Sprite Pointer
|
||||
* `NewSprPrepRoutinesLong+(!SPRID*3)` - New Long Sprite Prep Pointer
|
||||
|
||||
### Design Considerations
|
||||
|
||||
* **Multi-purpose Sprite IDs:** A single `!SPRID` can be used for multiple distinct behaviors (e.g., Keese, Fire Keese, Ice Keese, Vampire Bat all share sprite IDs) through the use of `SprSubtype`. This is a powerful technique for reusing sprite slots and creating variations of enemies.
|
||||
* **Damage Handling for Bosses:** For boss sprites, `!Damage = 00` is common if damage is applied through other means, such as spawned projectiles or direct contact logic within the main routine.
|
||||
* **Dynamic Health:** Many sprites set health dynamically in their `_Prep` routine based on game progression (e.g., Booki sets health based on Link's sword level, Darknut based on sword upgrades).
|
||||
* **Custom Boss Logic:** Setting `!Boss = 00` for a boss sprite indicates that custom boss logic is being used, rather than relying on vanilla boss flags.
|
||||
* **Shared Sprite IDs:** Multiple distinct NPCs or objects can share a single `!SPRID` by using `SprSubtype` for differentiation (e.g., `Sprite_Mermaid = $F0` is used for Mermaid, Maple, and Librarian with different subtypes).
|
||||
|
||||
## 3. Main Structure (`_Long` routine)
|
||||
|
||||
This is the main entry point for your sprite, called by the game engine every frame. Its primary job is to call the drawing and logic routines.
|
||||
|
||||
```asm
|
||||
Sprite_MyNewEnemy_Long:
|
||||
{
|
||||
PHB : PHK : PLB ; Set up bank registers (Push Bank, Push K, Pull Bank)
|
||||
JSR Sprite_MyNewEnemy_Draw
|
||||
JSL Sprite_DrawShadow ; Optional: Draw a shadow (use appropriate shadow function)
|
||||
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive ; Only run logic if active
|
||||
JSR Sprite_MyNewEnemy_Main
|
||||
.SpriteIsNotActive
|
||||
|
||||
PLB ; Restore bank register
|
||||
RTL ; Return from long routine
|
||||
}
|
||||
```
|
||||
|
||||
### Important Notes
|
||||
|
||||
* **Bank Register Management:** Always use `PHB : PHK : PLB` at the start and `PLB` before `RTL` to ensure proper bank context.
|
||||
* **Sprite_CheckActive:** This critical function checks if the sprite should execute logic based on its state, freeze status, and pause flags. Returns carry set if active.
|
||||
* **Drawing Order:** Drawing is typically done before the main logic, though the order can vary based on sprite needs.
|
||||
* **Conditional Drawing:** Shadow drawing might be conditional based on the sprite's current action or state (e.g., Thunder Ghost only draws shadow when grounded).
|
||||
|
||||
## 4. Initialization (`_Prep` routine)
|
||||
|
||||
This routine runs *once* when the sprite is first spawned. Use it to set initial values for timers, its action state, and any other properties. For dynamic difficulty scaling, you can adjust properties based on game progression here.
|
||||
|
||||
```asm
|
||||
Sprite_MyNewEnemy_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
|
||||
; Set dynamic health based on sword level (optional)
|
||||
LDA.l Sword : DEC A : TAY
|
||||
LDA.w .health, Y : STA.w SprHealth, X
|
||||
|
||||
%GotoAction(0) ; Set the initial state to the first one in the jump table
|
||||
%SetTimerA(120) ; Set a general-purpose timer to 120 frames (2 seconds)
|
||||
|
||||
PLB
|
||||
RTL
|
||||
|
||||
; Optional: Dynamic health table
|
||||
.health
|
||||
db $04, $08, $10, $18 ; Health values for sword levels 1-4
|
||||
}
|
||||
```
|
||||
|
||||
### Available Sprite RAM Variables
|
||||
|
||||
The following WRAM addresses are available for sprite-specific data (all indexed by X):
|
||||
|
||||
**Position & Movement:**
|
||||
* `SprY, SprX` ($0D00, $0D10) - 8-bit position coordinates (low byte)
|
||||
* `SprYH, SprXH` ($0D20, $0D30) - High bytes of position
|
||||
* `SprYSpeed, SprXSpeed` ($0D40, $0D50) - Movement velocities
|
||||
* `SprYRound, SprXRound` ($0D60, $0D70) - Sub-pixel precision
|
||||
* `SprCachedX, SprCachedY` ($0FD8, $0FDA) - Cached coordinates
|
||||
|
||||
**Animation & Graphics:**
|
||||
* `SprAction` ($0D80) - Current state in state machine
|
||||
* `SprFrame` ($0D90) - Current animation frame index
|
||||
* `SprGfx` ($0DC0) - Graphics offset for drawing
|
||||
* `SprFlash` ($0B89) - Flash color for damage indication
|
||||
|
||||
**Timers:**
|
||||
* `SprTimerA-F` ($0DF0, $0E00, $0E10, $0EE0, $0F10, $0F80) - Six general-purpose timers
|
||||
* Note: `SprTimerF` decreases by 2 each frame (used for gravity)
|
||||
|
||||
**Miscellaneous Data:**
|
||||
* `SprMiscA-G` ($0DA0, $0DB0, $0DE0, $0E90, $0EB0, $0EC0, $0ED0) - Seven general-purpose variables
|
||||
* `SprCustom` ($1CC0) - Additional custom data storage
|
||||
|
||||
**State & Properties:**
|
||||
* `SprState` ($0DD0) - Sprite state (0x00=dead, 0x08=spawning, 0x09=active, etc.)
|
||||
* `SprType` ($0E20) - Sprite ID
|
||||
* `SprSubtype` ($0E30) - Sprite subtype for variations
|
||||
* `SprHealth` ($0E50) - Current health
|
||||
* `SprNbrOAM` ($0E40) - Number of OAM slots + flags
|
||||
* `SprFloor` ($0F20) - Layer (0=top, 1=bottom)
|
||||
* `SprHeight` ($0F80) - Z-position for altitude/jumping
|
||||
|
||||
### Common Initialization Patterns
|
||||
|
||||
```asm
|
||||
; Set sprite to be impervious initially (e.g., for a boss with phases)
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
|
||||
; Configure tile collision behavior
|
||||
LDA.b #%01100000 : STA.w SprTileDie, X
|
||||
|
||||
; Set bump damage type
|
||||
LDA.b #$09 : STA.w SprBump, X
|
||||
|
||||
; Initialize custom variables
|
||||
STZ.w SprMiscA, X
|
||||
STZ.w SprMiscB, X
|
||||
```
|
||||
|
||||
## 5. Main Logic & State Machine (`_Main` routine)
|
||||
|
||||
This is the heart of your sprite. Use the `%SpriteJumpTable` macro to create a state machine. The sprite's current state is stored in `SprAction, X`.
|
||||
|
||||
```asm
|
||||
Sprite_MyNewEnemy_Main:
|
||||
{
|
||||
%SpriteJumpTable(State_Idle, State_Attacking, State_Hurt)
|
||||
|
||||
State_Idle:
|
||||
{
|
||||
%PlayAnimation(0, 1, 15) ; Animate between frames 0 and 1 every 15 game frames
|
||||
|
||||
; Check distance to player. If less than 80 pixels, switch to attacking state.
|
||||
JSL GetDistance8bit_Long : CMP.b #$50 : BCS .player_is_far
|
||||
%GotoAction(1) ; Switch to State_Attacking
|
||||
.player_is_far
|
||||
RTS
|
||||
}
|
||||
|
||||
State_Attacking:
|
||||
{
|
||||
%PlayAnimation(2, 3, 8)
|
||||
%MoveTowardPlayer(12) ; Move toward the player with speed 12
|
||||
%DoDamageToPlayerSameLayerOnContact()
|
||||
|
||||
; Check if the player has hit the sprite
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_damage
|
||||
%GotoAction(2) ; Switch to State_Hurt
|
||||
.no_damage
|
||||
RTS
|
||||
}
|
||||
|
||||
State_Hurt:
|
||||
{
|
||||
; Sprite was hit, flash and get knocked back
|
||||
JSL Sprite_DamageFlash_Long
|
||||
JSL Sprite_CheckIfRecoiling
|
||||
|
||||
; Return to attacking after recoil
|
||||
LDA.w SprRecoil, X : BNE .still_recoiling
|
||||
%GotoAction(1)
|
||||
.still_recoiling
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Available Macros
|
||||
|
||||
**State Management:**
|
||||
* `%GotoAction(action)` - Set `SprAction` to switch states
|
||||
* `%SpriteJumpTable(state1, state2, ...)` - Create state machine jump table
|
||||
* `%JumpTable(index, state1, state2, ...)` - Jump table with custom index
|
||||
|
||||
**Animation:**
|
||||
* `%PlayAnimation(start, end, speed)` - Animate frames (uses `SprTimerB`)
|
||||
* `%PlayAnimBackwards(start, end, speed)` - Animate in reverse
|
||||
* `%StartOnFrame(frame)` - Ensure animation starts at a minimum frame
|
||||
* `%SetFrame(frame)` - Directly set animation frame
|
||||
|
||||
**Movement:**
|
||||
* `%MoveTowardPlayer(speed)` - Apply speed toward player and move
|
||||
* `%SetSpriteSpeedX(speed)` - Set horizontal velocity
|
||||
* `%SetSpriteSpeedY(speed)` - Set vertical velocity
|
||||
|
||||
**Timers:**
|
||||
* `%SetTimerA-F(length)` - Set timer values
|
||||
|
||||
**Player Interaction:**
|
||||
* `%DoDamageToPlayerSameLayerOnContact()` - Damage on contact (same layer only)
|
||||
* `%PlayerCantPassThrough()` - Prevent Link from passing through sprite
|
||||
* `%ShowSolicitedMessage(id)` - Show message when player presses A
|
||||
* `%ShowMessageOnContact(id)` - Show message on contact
|
||||
* `%ShowUnconditionalMessage(id)` - Show message immediately
|
||||
|
||||
**Sprite Properties:**
|
||||
* `%SetHarmless(value)` - 0=harmful, 1=harmless
|
||||
* `%SetImpervious(value)` - Toggle invulnerability
|
||||
* `%SetRoomFlag(value)` - Set room completion flag
|
||||
|
||||
**Audio:**
|
||||
* `%PlaySFX1(id)`, `%PlaySFX2(id)` - Play sound effect
|
||||
* `%PlayMusic(id)` - Change background music
|
||||
* `%ErrorBeep()` - Play error sound
|
||||
|
||||
**Utility:**
|
||||
* `%ProbCheck(mask, label)` - Random check, branch if result is non-zero
|
||||
* `%ProbCheck2(mask, label)` - Random check, branch if result is zero
|
||||
* `%SetupDistanceFromSprite()` - Setup distance calculation
|
||||
|
||||
### Common Functions
|
||||
|
||||
**Movement & Physics:**
|
||||
* `Sprite_Move` / `Sprite_MoveLong` - Apply velocity to position
|
||||
* `Sprite_MoveHoriz` / `Sprite_MoveVert` - Move in one axis
|
||||
* `Sprite_BounceFromTileCollision` - Bounce off walls
|
||||
* `Sprite_CheckTileCollision` - Check for tile collision
|
||||
* `Sprite_ApplySpeedTowardsPlayer` - Calculate speed toward player
|
||||
* `Sprite_FloatTowardPlayer` - Float toward player with altitude
|
||||
* `Sprite_FloatAwayFromPlayer` - Float away from player
|
||||
* `Sprite_InvertSpeed_X` / `Sprite_InvertSpeed_Y` - Reverse velocity
|
||||
|
||||
**Combat:**
|
||||
* `Sprite_CheckDamageFromPlayer` - Check if player attacked sprite
|
||||
* `Sprite_CheckDamageToPlayer` - Check if sprite damaged player
|
||||
* `Sprite_DamageFlash_Long` - Flash sprite when damaged
|
||||
* `Sprite_CheckIfRecoiling` - Handle knockback after being hit
|
||||
* `Guard_ParrySwordAttacks` - Parry sword attacks (like Darknut)
|
||||
|
||||
**Spawning:**
|
||||
* `Sprite_SpawnDynamically` - Spawn a new sprite
|
||||
* `Sprite_SpawnProbeAlways_long` - Spawn probe projectile
|
||||
* `Sprite_SpawnSparkleGarnish` - Spawn sparkle effect
|
||||
|
||||
**Distance & Direction:**
|
||||
* `GetDistance8bit_Long` - Get 8-bit distance to player
|
||||
* `Sprite_DirectionToFacePlayer` - Get direction to face player
|
||||
* `Sprite_IsToRightOfPlayer` - Check if sprite is to right of player
|
||||
|
||||
**Randomness:**
|
||||
* `GetRandomInt` - Get random 8-bit value
|
||||
|
||||
### Code Style Guidelines
|
||||
|
||||
* **Named Constants:** Always use named constants for magic numbers:
|
||||
```asm
|
||||
GoriyaMovementSpeed = 10
|
||||
LDA.b #GoriyaMovementSpeed : STA.w SprXSpeed, X
|
||||
```
|
||||
* **Processor Status Flags:** Explicitly manage 8-bit/16-bit mode with `REP #$20` (16-bit) and `SEP #$20` (8-bit), especially during OAM calculations
|
||||
* **State Machine Pattern:** Use `SprAction` with `%SpriteJumpTable` for clear state management
|
||||
* **Timer Usage:** Use dedicated timers for different purposes (e.g., `SprTimerA` for state changes, `SprTimerB` for animation, `SprTimerC` for cooldowns)
|
||||
|
||||
## 6. Drawing (`_Draw` routine)
|
||||
|
||||
This routine renders your sprite's graphics. The project provides the `%DrawSprite()` macro for standard drawing, which reads from data tables you define.
|
||||
|
||||
### Standard Drawing with %DrawSprite()
|
||||
|
||||
```asm
|
||||
Sprite_MyNewEnemy_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord ; Prepare OAM coordinates
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer ; Allocate OAM slots
|
||||
|
||||
%DrawSprite()
|
||||
|
||||
; --- OAM Data Tables ---
|
||||
.start_index ; Starting index in the tables for each animation frame
|
||||
db $00, $02, $04, $06
|
||||
.nbr_of_tiles ; Number of tiles to draw for each frame (actual count minus 1)
|
||||
db 1, 1, 1, 1
|
||||
|
||||
.x_offsets ; X-position offset for each tile (16-bit values)
|
||||
dw -8, 8, -8, 8, -8, 8, -8, 8
|
||||
.y_offsets ; Y-position offset for each tile (16-bit values)
|
||||
dw -8, -8, -8, -8, -8, -8, -8, -8
|
||||
.chr ; The character (tile) number from the graphics sheet
|
||||
db $C0, $C2, $C4, $C6, $C8, $CA, $CC, $CE
|
||||
.properties ; OAM properties (palette, priority, flips)
|
||||
db $3B, $7B, $3B, $7B, $3B, $7B, $3B, $7B
|
||||
.sizes ; Size of each tile (e.g., $02 for 16x16)
|
||||
db $02, $02, $02, $02, $02, $02, $02, $02
|
||||
}
|
||||
```
|
||||
|
||||
### OAM Property Byte Format
|
||||
|
||||
The `.properties` byte contains flags for each tile:
|
||||
* Bits 0-2: Palette (0-7)
|
||||
* Bit 3: Priority (0=front, 1=behind BG)
|
||||
* Bit 4: Unused
|
||||
* Bit 5: Horizontal flip
|
||||
* Bit 6: Vertical flip
|
||||
* Bit 7: Unused
|
||||
|
||||
Example values:
|
||||
* `$39` = Palette 1, no flip, front priority
|
||||
* `$79` = Palette 1, horizontal flip, front priority
|
||||
* `$B9` = Palette 1, vertical flip, front priority
|
||||
|
||||
### Custom Drawing Logic
|
||||
|
||||
For complex drawing needs (multi-part sprites, dynamic flipping, etc.), implement custom drawing:
|
||||
|
||||
```asm
|
||||
Sprite_MyNewEnemy_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprGfx, X : CLC : ADC.w SprFrame, X : TAY ; Get animation frame
|
||||
LDA.w .start_index, Y : STA $06 ; Get starting index
|
||||
LDA.w SprFlash, X : STA $08 ; Store flash value
|
||||
LDA.w SprMiscC, X : STA $09 ; Store direction for flipping
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ; Load number of tiles minus 1
|
||||
LDY.b #$00 ; OAM buffer index
|
||||
|
||||
.nextTile
|
||||
PHX ; Save tile index
|
||||
TXA : CLC : ADC $06 : PHA ; Calculate absolute tile index
|
||||
ASL A : TAX ; Multiply by 2 for 16-bit offsets
|
||||
|
||||
REP #$20 ; 16-bit accumulator
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y ; Write X position
|
||||
AND.w #$0100 : STA $0E ; Store X high bit
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y ; Write Y position
|
||||
CLC : ADC #$0010 : CMP.w #$0100 ; Check if on screen
|
||||
SEP #$20 ; Back to 8-bit
|
||||
BCC .on_screen_y
|
||||
LDA.b #$F0 : STA ($90), Y : STA $0E ; Move offscreen
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Restore absolute tile index
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y ; Write character
|
||||
INY
|
||||
|
||||
; Apply horizontal flip based on direction
|
||||
LDA.b $09 : BEQ .no_flip
|
||||
LDA.b #$79 : JMP .write_prop
|
||||
.no_flip
|
||||
LDA .properties, X
|
||||
.write_prop
|
||||
ORA $08 : STA ($90), Y ; Write properties with flash
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; Write size
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
RTS
|
||||
|
||||
; Data tables follow...
|
||||
}
|
||||
```
|
||||
|
||||
### Important Drawing Notes
|
||||
|
||||
* **16-bit Calculations:** Always use `REP #$20` before 16-bit position calculations and `SEP #$20` afterward
|
||||
* **OAM Allocation:** Different allocation functions for different scenarios:
|
||||
* `Sprite_OAM_AllocateDeferToPlayer` - Standard allocation
|
||||
* `OAM_AllocateFromRegionE` - For large sprites (bosses)
|
||||
* `Sprite_OAM_AllocateDeferToPlayerLong` - Long version
|
||||
* **Shadow Drawing:** Call `Sprite_DrawShadow` in the `_Long` routine, not in `_Draw`
|
||||
* **Multi-Layer Drawing:** For objects like minecarts that Link can be "inside", draw in multiple parts from different OAM regions to create depth
|
||||
* **Conditional Drawing:** Some sprites (like followers or bosses) dispatch to different draw routines based on `SprSubtype` or current state
|
||||
|
||||
## 7. Final Integration
|
||||
|
||||
The `%Set_Sprite_Properties()` macro you added in Step 2 handles the final integration automatically. It:
|
||||
|
||||
1. Writes your sprite properties to the appropriate ROM addresses
|
||||
2. Sets up pointers in the vanilla sprite jump tables
|
||||
3. Adds your `_Prep` and `_Long` routines to the new sprite table in `Core/sprite_new_table.asm`
|
||||
|
||||
Your sprite is now ready to be placed in the game world using your level editor!
|
||||
|
||||
## 8. Testing Your Sprite
|
||||
|
||||
1. **Build the ROM:** Run your build script (`build.sh` or `build.bat`)
|
||||
2. **Place in Editor:** Use your level editor to place the sprite in a room
|
||||
3. **Test Behavior:** Load the room and verify:
|
||||
* Sprite spawns correctly
|
||||
* Animation plays as expected
|
||||
* Movement works properly
|
||||
* Collision detection functions
|
||||
* Damage and health mechanics work
|
||||
* State transitions occur correctly
|
||||
|
||||
## 9. Common Issues and Solutions
|
||||
|
||||
### Sprite Doesn't Appear
|
||||
* Check that the sprite ID is not already in use
|
||||
* Verify the `incsrc` directive is in the correct bank
|
||||
* Ensure `%Set_Sprite_Properties` is called after property definitions
|
||||
* Check that the sprite is being placed in a compatible room type
|
||||
|
||||
### Graphics are Corrupted
|
||||
* Verify 16-bit mode (`REP #$20`) is used for OAM calculations
|
||||
* Check that `.start_index`, `.nbr_of_tiles`, and data tables are correctly sized
|
||||
* Ensure `.sizes` table uses correct values ($00=8x8, $02=16x16)
|
||||
* Verify character numbers (`.chr`) match your graphics sheet
|
||||
|
||||
### Sprite Behaves Incorrectly
|
||||
* Check that timers are being set and checked correctly
|
||||
* Verify state transitions in the jump table
|
||||
* Ensure `Sprite_CheckActive` is called before main logic
|
||||
* Check that collision functions are being called in the right order
|
||||
|
||||
### Performance Issues
|
||||
* Reduce `!NbrTiles` if using too many tiles
|
||||
* Optimize drawing routine (avoid redundant calculations)
|
||||
* Use simpler collision detection where possible
|
||||
* Consider using `!Persist = 00` for non-critical sprites
|
||||
|
||||
## 10. Advanced Sprite Design Patterns
|
||||
|
||||
## 10. Advanced Sprite Design Patterns
|
||||
|
||||
### 10.1. Multi-Part Sprites and Child Sprites
|
||||
|
||||
For complex bosses or entities, break them down into a main parent sprite and multiple child sprites. Examples include Kydreeok (body + heads), Darknut (knight + probes), Goriya (enemy + boomerang), and Helmet Chuchu (body + detachable helmet).
|
||||
|
||||
**Parent Sprite Responsibilities:**
|
||||
* Spawns and manages child sprites using `Sprite_SpawnDynamically`
|
||||
* Stores child sprite IDs in global variables or `SprMisc` slots
|
||||
* Monitors child sprite states to determine phases or defeat conditions
|
||||
* Handles overall movement, phase transitions, and global effects
|
||||
|
||||
**Child Sprite Responsibilities:**
|
||||
* Handles independent logic, movement, and attacks
|
||||
* May be positioned relative to parent sprite
|
||||
* Uses `SprSubtype` to differentiate between multiple instances
|
||||
|
||||
**Example: Kydreeok Boss**
|
||||
```asm
|
||||
; In Kydreeok body sprite
|
||||
SpawnLeftHead:
|
||||
{
|
||||
LDA #$CF ; Kydreeok Head sprite ID
|
||||
JSL Sprite_SpawnDynamically : BMI .return
|
||||
TYA : STA.w Offspring1_Id ; Store child ID globally
|
||||
LDA.b #$00 : STA.w SprSubtype, Y ; Subtype 0 = left head
|
||||
; Position relative to parent
|
||||
REP #$20
|
||||
LDA.w SprCachedX : SEC : SBC.w #$0010
|
||||
SEP #$20
|
||||
STA.w SprX, Y : XBA : STA.w SprXH, Y
|
||||
; ... more initialization
|
||||
.return
|
||||
RTS
|
||||
}
|
||||
|
||||
; Check if all heads are defeated
|
||||
Sprite_Kydreeok_CheckIfDead:
|
||||
{
|
||||
LDA.w Offspring1_Id : TAY
|
||||
LDA.w SprState, Y : BNE .not_dead ; Check if left head alive
|
||||
LDA.w Offspring2_Id : TAY
|
||||
LDA.w SprState, Y : BNE .not_dead ; Check if right head alive
|
||||
; All heads defeated - trigger death sequence
|
||||
.not_dead
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
**Shared Sprite IDs for Variations:**
|
||||
A single sprite ID can represent different enemy types using `SprSubtype`:
|
||||
* Keese sprite ID shared by: Regular Keese, Fire Keese, Ice Keese, Vampire Bat
|
||||
* Mermaid sprite ID ($F0) shared by: Mermaid, Maple, Librarian (all using different subtypes)
|
||||
* This efficiently reuses sprite slots and base logic
|
||||
|
||||
### 10.2. Quest Integration and Dynamic Progression
|
||||
|
||||
Boss fights and NPC interactions can be deeply integrated with quest progression using SRAM flags, dynamic health management, and multi-phase battles.
|
||||
|
||||
**Phase Transitions:**
|
||||
Trigger new phases based on health thresholds, timers, or child sprite states:
|
||||
```asm
|
||||
; Check health threshold for phase change
|
||||
LDA.w SprHealth, X : CMP.b #$10 : BCS .phase_one
|
||||
LDA.w SprMiscD, X : CMP.b #$02 : BEQ .already_phase_two
|
||||
LDA.b #$02 : STA.w SprMiscD, X ; Switch to phase 2
|
||||
JSR LoadPhase2Graphics
|
||||
JSR SpawnPhase2Adds
|
||||
.already_phase_two
|
||||
.phase_one
|
||||
```
|
||||
|
||||
**Health Management:**
|
||||
* **Direct Health:** Use `SprHealth` for straightforward health tracking
|
||||
* **Indirect Health:** Base defeat on child sprite states (e.g., Kydreeok defeated when all heads are killed)
|
||||
* **Phase-Based Health:** Refill health between phases for extended boss fights
|
||||
* **Dynamic Scaling:** Adjust health based on Link's sword level or progression
|
||||
|
||||
**Quest Integration Examples:**
|
||||
* **Wolfos:** After being subdued, plays Song of Healing animation and grants Wolf Mask
|
||||
* **Bug Net Kid:** Dialogue changes based on whether Link has the Bug Net
|
||||
* **Maple:** Spawns items and interacts with Link differently based on quest flags
|
||||
* **Mask Salesman:** Complex shop system with inventory checks and rupee deduction
|
||||
* **Zora Princess:** Quest rewards and dialogue conditional on SRAM flags
|
||||
|
||||
**SRAM Flag Usage:**
|
||||
```asm
|
||||
; Check if quest item has been obtained
|
||||
LDA.l $7EF3XX : CMP.b #$XX : BNE .not_obtained
|
||||
; Quest item obtained - change behavior
|
||||
%ShowUnconditionalMessage(MessageID)
|
||||
JMP .quest_complete
|
||||
.not_obtained
|
||||
```
|
||||
|
||||
### 10.4. Code Reusability and Best Practices
|
||||
|
||||
**Shared Logic Functions:**
|
||||
Create reusable functions for common behaviors across multiple sprites:
|
||||
```asm
|
||||
; Shared by Goriya and Darknut
|
||||
Goriya_HandleTileCollision:
|
||||
{
|
||||
JSL Sprite_CheckTileCollision
|
||||
LDA.w SprCollision, X : BEQ .no_collision
|
||||
JSL GetRandomInt : AND.b #$03 : STA.w SprAction, X
|
||||
STA.w SprMiscE, X
|
||||
%SetTimerC(60)
|
||||
.no_collision
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
**Named Constants:**
|
||||
Always use named constants instead of magic numbers:
|
||||
```asm
|
||||
; Good
|
||||
GoriyaMovementSpeed = 10
|
||||
MinecartSpeed = 20
|
||||
DoubleSpeed = 30
|
||||
|
||||
LDA.b #GoriyaMovementSpeed : STA.w SprXSpeed, X
|
||||
|
||||
; Bad
|
||||
LDA.b #10 : STA.w SprXSpeed, X ; What does 10 mean?
|
||||
```
|
||||
|
||||
**Processor Status Management:**
|
||||
Explicitly manage 8-bit/16-bit modes:
|
||||
```asm
|
||||
REP #$20 ; 16-bit accumulator
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
SEP #$20 ; Back to 8-bit
|
||||
```
|
||||
|
||||
**State Machine Pattern:**
|
||||
Use `SprAction` with jump tables for clear state management:
|
||||
```asm
|
||||
Sprite_Enemy_Main:
|
||||
{
|
||||
%SpriteJumpTable(State_Idle, State_Chase, State_Attack, State_Retreat)
|
||||
|
||||
State_Idle: { /* ... */ RTS }
|
||||
State_Chase: { /* ... */ RTS }
|
||||
State_Attack: { /* ... */ RTS }
|
||||
State_Retreat: { /* ... */ RTS }
|
||||
}
|
||||
```
|
||||
|
||||
**Timer Management:**
|
||||
Use different timers for different purposes:
|
||||
* `SprTimerA` - State transitions, cooldowns
|
||||
* `SprTimerB` - Animation (automatically used by `%PlayAnimation`)
|
||||
* `SprTimerC` - Movement changes, direction changes
|
||||
* `SprTimerD` - Attack cooldowns
|
||||
* `SprTimerE` - Special effects
|
||||
* `SprTimerF` - Gravity/altitude (decrements by 2)
|
||||
|
||||
### 10.5. Centralized Handlers and Multi-Purpose Sprites
|
||||
|
||||
Many sprite files serve as central handlers for multiple distinct entities, using conditional logic to dispatch behaviors.
|
||||
|
||||
**Examples:**
|
||||
* **Followers** (`followers.asm`) - Zora Baby, Old Man, Kiki
|
||||
* **Mermaid** (`mermaid.asm`) - Mermaid (subtype 0), Maple (subtype 1), Librarian (subtype 2)
|
||||
* **Zora** (`zora.asm`) - Various Zora NPCs with different roles
|
||||
* **Collectible** (`collectible.asm`) - Different collectible items
|
||||
* **Deku Leaf** (`deku_leaf.asm`) - Deku Leaf and Beach Whirlpool
|
||||
|
||||
**Implementation Pattern:**
|
||||
```asm
|
||||
Sprite_MultiPurpose_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
|
||||
; Dispatch based on subtype
|
||||
LDA.w SprSubtype, X
|
||||
JSL JumpTableLocal
|
||||
dw Type0_Routine
|
||||
dw Type1_Routine
|
||||
dw Type2_Routine
|
||||
|
||||
Type0_Routine:
|
||||
JSR Type0_Draw
|
||||
JSR Type0_Main
|
||||
PLB : RTL
|
||||
|
||||
Type1_Routine:
|
||||
JSR Type1_Draw
|
||||
JSR Type1_Main
|
||||
PLB : RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 10.6. Overriding Vanilla Sprites
|
||||
|
||||
To replace vanilla sprite behavior while keeping the original sprite ID:
|
||||
|
||||
```asm
|
||||
; In a patch file or at the start of your sprite file
|
||||
pushpc
|
||||
org $069283+($XX*2) ; Replace vanilla main pointer
|
||||
dw NewCustomBehavior_Main
|
||||
org $06865B+($XX*2) ; Replace vanilla prep pointer
|
||||
dw NewCustomBehavior_Prep
|
||||
pullpc
|
||||
|
||||
NewCustomBehavior_Main:
|
||||
{
|
||||
; Check if custom behavior should activate
|
||||
LDA.l $7EF3XX : CMP.b #$YY : BNE .use_vanilla
|
||||
JSL CustomImplementation_Long
|
||||
RTS
|
||||
.use_vanilla
|
||||
JML $OriginalVanillaAddress
|
||||
}
|
||||
```
|
||||
|
||||
### 10.7. Interactive Objects and Environmental Triggers
|
||||
|
||||
**Player-Manipulated Objects:**
|
||||
Objects like Ice Block and Minecart require precise collision and alignment:
|
||||
```asm
|
||||
; Round position to 8-pixel grid for proper alignment
|
||||
RoundCoords:
|
||||
{
|
||||
LDA.b $00 : CLC : ADC.b #$04 : AND.b #$F8 : STA.b $00 : STA.w SprY, X
|
||||
LDA.b $02 : CLC : ADC.b #$04 : AND.b #$F8 : STA.b $02 : STA.w SprX, X
|
||||
JSR UpdateCachedCoords
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
**Environmental Triggers:**
|
||||
Switch objects respond to player actions and modify game state:
|
||||
```asm
|
||||
; Mine switch changes track configuration
|
||||
Sprite_Mineswitch_OnActivate:
|
||||
{
|
||||
LDA.w SprMiscA, X : BEQ .currently_off
|
||||
; Switch is on, turn it off
|
||||
STZ.w SprMiscA, X
|
||||
JSR UpdateTrackTiles_Off
|
||||
JMP .done
|
||||
.currently_off
|
||||
; Switch is off, turn it on
|
||||
LDA.b #$01 : STA.w SprMiscA, X
|
||||
JSR UpdateTrackTiles_On
|
||||
.done
|
||||
%PlaySFX2($14) ; Switch sound
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
### 10.8. Shop and Item Management
|
||||
|
||||
**Transaction System:**
|
||||
```asm
|
||||
Shopkeeper_SellItem:
|
||||
{
|
||||
; Check if player has enough rupees
|
||||
REP #$20
|
||||
LDA.l $7EF360 : CMP.w #ItemCost : BCC .not_enough
|
||||
; Deduct rupees
|
||||
SEC : SBC.w #ItemCost : STA.l $7EF360
|
||||
SEP #$20
|
||||
; Grant item
|
||||
LDA.b #ItemID : STA.l $7EF3XX
|
||||
%ShowUnconditionalMessage(ThankYouMessage)
|
||||
RTS
|
||||
.not_enough
|
||||
SEP #$20
|
||||
%ErrorBeep()
|
||||
%ShowUnconditionalMessage(NotEnoughRupeesMessage)
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
**Item Granting with Quest Tracking:**
|
||||
```asm
|
||||
NPC_GrantQuestItem:
|
||||
{
|
||||
; Check if already received
|
||||
LDA.l $7EF3XX : BNE .already_obtained
|
||||
; Grant item
|
||||
LDA.b #$01 : STA.l $7EF3XX
|
||||
LDA.b #ItemID
|
||||
JSL Link_ReceiveItem
|
||||
%ShowUnconditionalMessage(ItemReceivedMessage)
|
||||
RTS
|
||||
.already_obtained
|
||||
%ShowUnconditionalMessage(AlreadyHaveMessage)
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
### 10.9. Player State Manipulation
|
||||
|
||||
For cinematic sequences and special interactions:
|
||||
```asm
|
||||
Cutscene_LinkSleep:
|
||||
{
|
||||
; Prevent player input
|
||||
%PreventPlayerMovement()
|
||||
|
||||
; Set Link's animation
|
||||
LDA.b #$XX : STA.w LinkAction
|
||||
|
||||
; Play sleep animation
|
||||
LDA.b #$XX : STA.w LinkGraphics
|
||||
|
||||
; Wait for timer
|
||||
LDA.w SprTimerA, X : BNE .still_waiting
|
||||
%AllowPlayerMovement()
|
||||
%GotoAction(NextState)
|
||||
.still_waiting
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
### 10.10. Error Handling and Player Feedback
|
||||
|
||||
**Robust Error Prevention:**
|
||||
```asm
|
||||
; Portal sprite checks for valid placement
|
||||
Sprite_Portal_CheckValidTile:
|
||||
{
|
||||
LDA.w CurrentTileType : CMP.b #ValidTileMin : BCC .invalid
|
||||
CMP.b #ValidTileMax : BCS .invalid
|
||||
CMP.b #$XX : BEQ .invalid ; Check specific invalid tiles
|
||||
; Valid placement
|
||||
SEC
|
||||
RTS
|
||||
.invalid
|
||||
%ErrorBeep()
|
||||
STZ.w SprState, X ; Despawn sprite
|
||||
CLC
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
**Clear Player Feedback:**
|
||||
```asm
|
||||
; Provide audio/visual feedback
|
||||
%ErrorBeep() ; Sound for errors
|
||||
%PlaySFX1($14) ; Sound for success
|
||||
JSL Sprite_ShowMessageUnconditional ; Text feedback
|
||||
```
|
||||
|
||||
## 11. Additional Resources
|
||||
|
||||
**Core Files:**
|
||||
* `Core/sprite_macros.asm` - All available macros and their implementations
|
||||
* `Core/sprite_functions.asm` - Reusable sprite functions
|
||||
* `Core/sprite_new_table.asm` - Sprite table initialization
|
||||
* `Core/symbols.asm` - RAM address definitions
|
||||
* `Core/structs.asm` - Sprite structure definitions
|
||||
|
||||
**Documentation:**
|
||||
* `Docs/Sprites/` - Detailed documentation for existing sprites
|
||||
* `Docs/Sprites/Overlords.md` - Overlord system documentation
|
||||
* `Sprites/all_sprites.asm` - See how sprites are organized and included
|
||||
|
||||
**Example Sprites:**
|
||||
* **Simple Enemy:** `Sprites/Enemies/sea_urchin.asm` - Basic enemy with minimal logic
|
||||
* **Advanced Enemy:** `Sprites/Enemies/booki.asm` - Dynamic AI with state management
|
||||
* **Boss:** `Sprites/Bosses/kydreeok.asm` - Multi-part boss with child sprites
|
||||
* **Interactive Object:** `Sprites/Objects/minecart.asm` - Complex player interaction
|
||||
* **NPC:** `Sprites/NPCs/mask_salesman.asm` - Shop system and dialogue
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# BG Color / Overlay Regression — Resolved (March 2026)
|
||||
|
||||
## Resolution Summary
|
||||
The "Too Bright" and "Flash to Day" bugs have been resolved by fixing the register management in `ZSCustomOverworld.asm`.
|
||||
|
||||
**The Problem:**
|
||||
When transitioning from an area with an overlay (like Rain/Storms) to an area without one (Overlay ID `$FF`), the code cleared the Subscreen Enable register (`$1D`) but **failed to clear the Color Math Control register (`$9A`)**.
|
||||
- Rain sets `$9A` to `$72` (Additive Math).
|
||||
- If `$9A` remains `$72` in a normal area, the SNES PPU continues to perform additive color math using the Fixed Color registers (`$9C`/`$9D`).
|
||||
- This caused the background to appear significantly brighter (approx +6 per channel), turning the dark night tint into a "bright yellow-ish green".
|
||||
- This also caused the "Flash to Day" effect during transitions, as the additive brightness kicked in immediately.
|
||||
|
||||
**The Fix:**
|
||||
Modified `ZSCustomOverworld.asm` in two key locations to ensure `$9A` is always cleared when no overlay is present.
|
||||
|
||||
1. **Walking Transitions (`Overworld_ReloadSubscreenOverlay_Interupt`):**
|
||||
Added a check for Overlay `$FF` to explicitly clear `$9A`. This prevents the brightness glitch during scrolling.
|
||||
|
||||
```asm
|
||||
; In Overworld_ReloadSubscreenOverlay_Interupt
|
||||
CPX.b #$FF : BNE .checkScroll
|
||||
LDA.b #$00 ; Disable Color Math
|
||||
BRA .loadOverlay
|
||||
```
|
||||
|
||||
2. **Dungeon/Warp/Bird Transitions (`Overworld_LoadBGColorAndSubscreenOverlay`):**
|
||||
Added `STZ.b $9A` to the block handling the `$FF` case. This prevents the glitch when exiting dungeons or warping.
|
||||
|
||||
```asm
|
||||
; In Overworld_LoadBGColorAndSubscreenOverlay
|
||||
CMP.w #$00FF : BNE .noCustomFixedColor
|
||||
SEP #$30
|
||||
STZ.b $9A ; FIX: Clear color math
|
||||
; ...
|
||||
```
|
||||
|
||||
## Verification
|
||||
- **Brightness:** The background color in normal areas should now correctly reflect the Time System tint without extra brightness.
|
||||
- **Transitions:** Walking from a Rain area to a Normal area should no longer result in a brightness jump.
|
||||
- **Song of Storms:** Summoning and dismissing storms should work correctly, with the overlay and color math engaging and disengaging as expected.
|
||||
|
||||
## Technical Details
|
||||
- **File:** `Overworld/ZSCustomOverworld.asm`
|
||||
- **Routines:** `Overworld_LoadBGColorAndSubscreenOverlay`, `Overworld_ReloadSubscreenOverlay_Interupt`.
|
||||
- **Registers:** `$1D` (Subscreen), `$9A` (CGADDSUB Mirror), `$9C`/`$9D` (COLDATA Mirrors).
|
||||
|
||||
## Outstanding Issues
|
||||
- None related to BG Color Brightness.
|
||||
@@ -1,45 +0,0 @@
|
||||
# Issue: Lost Woods Transition Coordinate Desync
|
||||
|
||||
## Status: Active / Low Priority
|
||||
**Created:** March 2026
|
||||
**Impact:** Visual/Gameplay discontinuity when exiting the Lost Woods (Area 0x29) back to the West (0x28).
|
||||
|
||||
## Problem Description
|
||||
The custom Lost Woods puzzle uses a coordinate manipulation trick (`INC/DEC $21`, `INC/DEC $E7`) to simulate an infinite loop.
|
||||
- **Symptoms:**
|
||||
- When completing the puzzle (Exit East -> 0x2A), the fix implemented (`LostWoods_ResetCoordinates`) correctly snaps Link to the left edge of the new screen, preventing him from skipping the map.
|
||||
- **Regression:** When *returning* to the previous map (Exit West -> 0x28), Link may appear at incorrect coordinates or the camera may be misaligned relative to the player.
|
||||
- The "Snapping" logic forces Link's X/Y to the base of Area 0x29 (e.g., X=0x0200). However, the transition logic in `ZSCustomOverworld.asm` uses these coordinates to calculate the *destination* position in the new area. If the snap happens too early or incorrectly, the destination calculation (Start X - Offset) might underflow or misalign.
|
||||
|
||||
## Technical Analysis
|
||||
|
||||
### Custom Logic (`lost_woods.asm`)
|
||||
The puzzle modifies:
|
||||
- `$21` / `$23`: Link's High-Byte Coordinates (World Grid Position).
|
||||
- `$E1` / `$E7` / `$E9`: Overlay and BG Scroll Registers.
|
||||
|
||||
This desynchronizes the "Visible" position from the "Logical" position expected by the standard Overworld engine.
|
||||
|
||||
### ZSOW Transition Logic
|
||||
`OverworldHandleTransitions` in `ZSCustomOverworld.asm` relies on:
|
||||
- `$20` / `$22`: Link's 16-bit absolute coordinates.
|
||||
- `Pool_OverworldTransitionPositionX/Y`: Lookup tables for screen boundaries.
|
||||
|
||||
### Root Cause Hypothesis
|
||||
1. **Coordinate Mismatch:** The `LostWoods_ResetCoordinates` routine snaps Link to `X=0x0200` (Left edge of 0x29).
|
||||
2. **Transition Calc:** When moving West to 0x28, the engine expects Link to be crossing the boundary.
|
||||
3. **Vanilla vs. Custom:** Vanilla ALTTP does not use infinite looping coordinates in the overworld. This mechanic is entirely custom and fights the static grid nature of the engine.
|
||||
|
||||
## Future Investigation Strategy (Reference `usdasm`)
|
||||
1. **Vanilla Transitions:** Study `Bank02.asm` in `usdasm` to see how `Module09_Overworld` handles coordinate handoffs.
|
||||
- Look for `Overworld_ScrollMap` and `Overworld_HandleCardinalCollision`.
|
||||
2. **Camera Re-centering:** Search for routines that "center" the camera on Link after a transition (`Overworld_SetCameraBoundaries`). We may need to manually invoke this *after* the transition logic finishes, rather than snapping coordinates *before*.
|
||||
3. **Scroll Register Reset:** Instead of zeroing `$E1` etc., we might need to recalculate them based on the *new* area's properties immediately upon load.
|
||||
|
||||
## Workaround
|
||||
The bug is non-fatal. Players can navigate out of the area, though the visual transition may be jarring.
|
||||
|
||||
## Related Files
|
||||
- `Overworld/lost_woods.asm`
|
||||
- `Overworld/ZSCustomOverworld.asm`
|
||||
- `usdasm/bank_02.asm` (Reference)
|
||||
@@ -1,26 +0,0 @@
|
||||
# Oracle of Secrets Documentation
|
||||
|
||||
Welcome to the documentation for Oracle of Secrets. This directory is organized to help you find information about the project's architecture, systems, and content.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
- `./General/`: High-level project information, including development guidelines and build instructions.
|
||||
- `./Core/`: Documentation for the core engine components, such as memory maps, the player engine (`Link.md`), and system interaction analysis.
|
||||
- `./Features/`: Detailed analysis of major gameplay features.
|
||||
- `./Features/Items/`: Information on custom and modified items.
|
||||
- `./Features/Masks/`: Details on the mask transformation system.
|
||||
- `./Features/Menu/`: Analysis of the custom menu and HUD.
|
||||
- `./Features/Music/`: Guide to the music system and composition workflow.
|
||||
- `./World/`: Information about the game world's construction.
|
||||
- `./World/Overworld/`: Documentation for the overworld engine, including `ZSCustomOverworld` and the time system.
|
||||
- `./World/Dungeons/`: Details on dungeon mechanics and custom features.
|
||||
- `./Sprites/`: Analysis of all sprite types, including bosses, NPCs, and interactive objects.
|
||||
- `./Guides/`: Step-by-step guides and tutorials, such as the sprite creation guide and the main quest flowchart.
|
||||
|
||||
## Key Documents
|
||||
|
||||
- **`General/DevelopmentGuidelines.md`**: The primary guide for coding standards, architecture, and best practices. Start here to understand the project's philosophy.
|
||||
- **`Core/MemoryMap.md`**: A comprehensive map of custom WRAM and SRAM variables.
|
||||
- **`Guides/QuestFlow.md`**: A detailed walkthrough of the main story and side-quest progression.
|
||||
- **`Guides/SpriteCreationGuide.md`**: A tutorial for creating new custom sprites.
|
||||
- **`World/Overworld/ZSCustomOverworld.md`**: A deep dive into the data-driven overworld engine that powers the game world.
|
||||
@@ -1,77 +0,0 @@
|
||||
# Bosses Analysis
|
||||
|
||||
This document provides an analysis of the boss sprites found in the `Sprites/Bosses/` directory. These sprites are typically complex, with multiple phases and unique behaviors.
|
||||
|
||||
## File Overview
|
||||
|
||||
| Filename | Sprite ID(s) | Description |
|
||||
|---|---|---|
|
||||
| `arrghus.asm` | (Hooks `$1EB593`) | A custom version of Arrghus that spawns fireballs. |
|
||||
| `dark_link.asm` | `Sprite_DarkLink` (`$C1`) | A multi-phase boss fight against a shadow version of Link. |
|
||||
| `king_dodongo.asm`| (Hooks `$1E811A`) | A custom version of King Dodongo with a new health system. |
|
||||
| `kydreeok.asm` | `Sprite_Kydreeok` | A multi-headed sea monster boss. Parent sprite for `kydreeok_head.asm`. |
|
||||
| `kydreeok_head.asm`| `Sprite_KydreeokHead` | The individual head of the Kydreeok boss, which can be attacked. |
|
||||
| `kydrog.asm` | `Sprite_KydrogNPC` | A cutscene version of Kydrog that appears before the boss fight. |
|
||||
| `kydrog_boss.asm` | `Sprite_KydrogBoss` | The main Kydrog boss fight, a large amphibious creature. |
|
||||
| `lanmola.asm` | (Hooks `$05A377`) | A custom version of the Lanmola boss. |
|
||||
| `lanmola_expanded.asm`| (Expansion) | Contains additional data and drawing logic for the custom Lanmola. |
|
||||
| `manhandla.asm` | `Sprite_Manhandla` | A two-phase boss fight against Manhandla, which later becomes a Big Chuchu. |
|
||||
| `octoboss.asm` | (Hooks `Sprite_A2_Kholdstare`) | A custom boss fight against two octopuses, replacing Kholdstare. |
|
||||
| `twinrova.asm` | `Sprite_Twinrova` (`$CE`) | A custom boss fight replacing Blind the Thief with the twin witches, Koume and Kotake. |
|
||||
| `vampire_bat.asm` | (Subtype of Keese) | A vampire bat mini-boss. |
|
||||
| `wolfos.asm` | `Sprite_Wolfos` | A Wolfos mini-boss that guards the Wolf Mask. |
|
||||
|
||||
## Detailed Boss Analysis
|
||||
|
||||
### `dark_link.asm`
|
||||
- **Sprite ID:** `Sprite_DarkLink` (`$C1`)
|
||||
- **Summary:** A complex, multi-action boss that mimics Link's abilities. The fight has multiple stages, including a Ganon-like form.
|
||||
- **Key Logic:**
|
||||
- The main routine is a large state machine driven by `SprAction, X`.
|
||||
- **Actions:** Includes standard walking, sword slashes, jump attacks, dodging, and using items like bombs and the Magic Cape.
|
||||
- **AI:** The AI in the `Handler` routine decides which action to take based on the distance to the player. It can choose to slash, dodge, or use a special attack.
|
||||
- **Enrage Mechanic:** At a certain health threshold (`SprHealth < $20`), the boss can enter an "enraged" state (`Enraging` action), which refills its health and makes its attacks faster.
|
||||
- **Ganon Subtype:** If `SprSubtype` is 5, it uses Ganon's draw and main logic, acting as a final phase.
|
||||
|
||||
### `kydreeok.asm` / `kydreeok_head.asm`
|
||||
- **Sprite IDs:** `Sprite_Kydreeok` (body), `Sprite_KydreeokHead` (head, ID `$CF`)
|
||||
- **Summary:** A large, stationary sea monster boss with multiple heads that act as its weak points. The main body sprite (`Kydreeok`) is a controller that spawns and manages the head sprites.
|
||||
- **Key Logic:**
|
||||
- **`Kydreeok_Prep`:** Spawns two `KydreeokHead` sprites (`SpawnLeftHead`, `SpawnRightHead`).
|
||||
- **`Kydreeok_Main`:** The body moves around the arena, and the heads follow its position, controlled via shared RAM addresses (`LeftNeck1_X`, etc.).
|
||||
- **`KydreeokHead_Main`:** The heads have their own AI. They move in a rotational pattern and randomly shoot fireballs at the player.
|
||||
- **Damage:** Only the head sprites can be damaged. When both heads are defeated, the main body sprite is killed.
|
||||
|
||||
### `manhandla.asm`
|
||||
- **Sprite ID:** `Sprite_Manhandla` (`$88`)
|
||||
- **Summary:** A two-phase boss. The first phase is a plant-like creature with three heads. The second phase is a large Big Chuchu.
|
||||
- **Key Logic:**
|
||||
- **Phase 1 (Manhandla):** The main body (`Manhandla_Body`) moves around the room, and three head sprites are spawned as offspring. The boss is only vulnerable when the heads are defeated.
|
||||
- **Phase Transition:** When all three heads are killed, `Sprite_Manhandla_CheckForNextPhaseOrDeath` sets `SprMiscD` to 1, refills the boss's health, and transitions to the `BigChuchu_Emerge` action.
|
||||
- **Phase 2 (Big Chuchu):** The sprite changes its appearance and behavior to that of a large slime. It moves around, spawns smaller slime projectiles (`Chuchu_SpawnBlast`), and has a central flower weak point.
|
||||
|
||||
### `octoboss.asm`
|
||||
- **Sprite ID:** (Replaces `Sprite_A2_Kholdstare`)
|
||||
- **Summary:** A custom boss fight featuring two octopus brothers who replace the vanilla Kholdstare boss.
|
||||
- **Key Logic:**
|
||||
- The fight begins when the player approaches, triggering the `Emerge` sequence.
|
||||
- The first octopus spawns its brother (`SpawnAndAwakeHisBrother`), and they both don hats (`SpawnPirateHats`).
|
||||
- The two sprites then move independently, attacking the player. Their total health is tracked via `ReturnTotalHealth`.
|
||||
- When their combined health is low enough, they surrender (`WaitMessageBeforeSurrender`), remove their hats, and one submerges to give the player the Quake Medallion.
|
||||
|
||||
### `twinrova.asm`
|
||||
- **Sprite ID:** `Sprite_Twinrova` (`$CE`, replaces Blind the Thief)
|
||||
- **Summary:** A custom, multi-phase boss fight against the twin witches Koume (fire) and Kotake (ice). This sprite completely replaces the vanilla Blind the Thief boss, including the cutscene leading up to it.
|
||||
- **Key Logic:**
|
||||
- **Trigger:** The fight begins when the Blind Maiden follower is brought into the boss room. `Follower_CheckBlindTrigger` detects this, and `Blind_SpawnFromMaiden` despawns the maiden and spawns the Twinrova sprite.
|
||||
- **Phase 1:** The combined Twinrova form moves around the room, alternating between fire (`Twinrova_FireAttack`) and ice (`Twinrova_IceAttack`) attacks.
|
||||
- **Phase 2:** When health is low, the boss splits. The `Twinrova_MoveState` action will randomly choose between `KoumeMode` (fire) and `KotakeMode` (ice). In these modes, the boss has different attack patterns, including spawning Keese and changing the floor tiles.
|
||||
|
||||
### `wolfos.asm`
|
||||
- **Sprite ID:** `Sprite_Wolfos`
|
||||
- **Summary:** A mini-boss that guards the Wolf Mask. It is designed to be subdued rather than killed.
|
||||
- **Key Logic:**
|
||||
- The Wolfos attacks Link with lunges and swipes.
|
||||
- When its health is depleted, `Sprite_Wolfos_CheckIfDefeated` transitions it to the `Wolfos_Subdued` action instead of killing it.
|
||||
- In the subdued state, it waits for the player to play the Song of Healing (`SongFlag`).
|
||||
- Once the song is played, it grants the Wolf Mask (`ITEMGET` ID `$10F`) and then despawns.
|
||||
@@ -1,31 +0,0 @@
|
||||
# Dark Link Sprite Analysis
|
||||
|
||||
## Overview
|
||||
Dark Link (Sprite ID: `$C1`) is a boss sprite known for its dynamic and challenging combat. It features a variety of attacks, including sword slashes, jump attacks, and projectile spawning. A notable aspect of this sprite is its ability to transform into a Ganon-like entity via a subtype, suggesting a multi-phase boss encounter.
|
||||
|
||||
## Key Properties:
|
||||
* **Sprite ID:** `$C1`
|
||||
* **Number of Tiles:** 4
|
||||
* **Health:** 34 (decimal)
|
||||
* **Damage:** 0 (Damage is handled by spawned attacks or direct contact logic, not directly by the sprite's `!Damage` property.)
|
||||
* **Special Properties:**
|
||||
* `!DeflectProjectiles = 01` (Deflects all projectiles)
|
||||
* `!ImperviousArrow = 01` (Impervious to arrows)
|
||||
* `!Boss = 00` (Despite being a boss, this flag is not set, indicating custom boss logic rather than reliance on vanilla boss flags.)
|
||||
|
||||
## Subtypes:
|
||||
* **Subtype `$05` (Ganon):** This subtype completely alters Dark Link's behavior to that of a Ganon boss, executing `Sprite_Ganon_Main` and `Sprite_Ganon_Draw`. This mechanism allows for a multi-stage boss fight or an entirely different boss using the same sprite slot.
|
||||
* **Subtype `$01` (Sword Damage):** This subtype is used for a temporary sprite spawned during Dark Link's sword attacks to handle collision and damage detection.
|
||||
|
||||
## In-Game Behavior:
|
||||
Dark Link is an active and engaging boss. It moves strategically towards Link, performs various sword attacks (including a jump attack with screen shake), can utilize a cape for evasion, and throws bombs. It reacts to damage with visual recoil and flashing, and enters an "enraging" state (indicated by a red palette change) which likely alters its attack patterns or aggression. The Ganon subtype suggests a significant shift in combat during the fight.
|
||||
|
||||
## Original Sprite Replaced:
|
||||
The code does not explicitly state which vanilla sprite `dark_link` replaces. However, the integration of `GanonInit` and Ganon-related logic strongly suggests it either heavily modifies an existing Ganon boss fight or is a completely new boss utilizing a custom sprite ID.
|
||||
|
||||
## Development Goals for Oracle of Secrets:
|
||||
* **Variety in Attacks:** Introduce more diverse attack patterns and abilities to enhance the fight's complexity and challenge.
|
||||
* **Unique Oracle of Secrets Attacks:** Implement attacks that are thematic and unique to the Oracle of Secrets project, moving beyond standard ALTTP boss mechanics.
|
||||
|
||||
## Code Quality Notes:
|
||||
The code, while functional and effective in creating a competent boss, is noted to be somewhat "messy" due to its origin from Zarby89's ZScream project. This implies that while it works, future modifications might require careful navigation through its structure.
|
||||
@@ -1,65 +0,0 @@
|
||||
# Kydreeok Sprite Analysis
|
||||
|
||||
## Overview
|
||||
The `kydreeok` sprite (ID: `Sprite_Kydreeok`, which is `$7A`) represents the main Kydreeok boss. It orchestrates the entire boss fight, including spawning and managing its child head sprites (`kydreeok_head`), controlling its own movement phases, and handling its overall defeat. This is a multi-headed boss where the heads are separate sprites.
|
||||
|
||||
## Key Properties:
|
||||
* **Sprite ID:** `Sprite_Kydreeok` (`$7A`)
|
||||
* **Description:** The main Kydreeok boss, controlling the overall fight and its child heads.
|
||||
* **Number of Tiles:** 8
|
||||
* **Health:** `00` (The boss's health is managed through its child heads and custom logic, not directly by this sprite's `!Health` property.)
|
||||
* **Damage:** `00` (Damage dealt to Link is likely handled by its heads or custom logic.)
|
||||
* **Special Properties:**
|
||||
* `!Boss = 01` (This sprite is correctly identified as a boss.)
|
||||
* `!Hitbox = $07`
|
||||
|
||||
## Main States/Actions (`Sprite_Kydreeok_Main` Jump Table):
|
||||
The boss's behavior is divided into several phases:
|
||||
* **`Kydreeok_Start` (0x00):** Initial state. Applies graphics and palette, prevents Link from passing through, and transitions to `Kydreeok_StageControl` after a timer. Stores its own sprite index in `Kydreeok_Id`.
|
||||
* **`Kydreeok_StageControl` (0x01):** Manages the boss's movement stage, setting velocities and checking boundaries.
|
||||
* **`Kydreeok_MoveXandY` (0x02):** Moves the boss in both X and Y directions towards Link, checking boundaries and handling damage.
|
||||
* **`Kydreeok_MoveXorY` (0x03):** Moves the boss in either X or Y direction towards Link, checking boundaries and handling damage.
|
||||
* **`Kydreeok_KeepWalking` (0x04):** Continues walking, with a random chance to transition to a flying state.
|
||||
* **`Kydreeok_Dead` (0x05):** Handles the boss's death sequence, including visual effects (flickering, explosions) and eventually despawning the sprite.
|
||||
* **`Kydreeok_Flying` (0x06):** The boss enters a flying state, moving towards Link at a set height, checking boundaries and handling damage.
|
||||
|
||||
## Initialization (`Sprite_Kydreeok_Prep`):
|
||||
* Sets initial timers and movement speeds.
|
||||
* Caches its own origin position.
|
||||
* **Spawns its child heads:** Calls `JSR SpawnLeftHead` and `JSR SpawnRightHead`. A `SpawnCenterHead` routine is commented out, suggesting a potential for a three-headed boss.
|
||||
* Initializes neck offsets to zero.
|
||||
* Applies a custom palette (`JSR ApplyPalette`).
|
||||
* Sets the boss theme music.
|
||||
|
||||
## Death and Respawn Logic (`Sprite_Kydreeok_CheckIfDead`, `MaybeRespawnHead`):
|
||||
* **`Sprite_Kydreeok_CheckIfDead`:** This crucial routine checks the state of its child heads (`Offspring1_Id`, `Offspring2_Id`). If both heads are defeated, it triggers a "dead" phase, changes its graphics, respawns both heads, and then transitions to the `Kydreeok_Dead` state. This indicates a multi-phase boss where heads can be temporarily defeated.
|
||||
* **`MaybeRespawnHead`:** Randomly respawns a head if its corresponding child sprite is dead, adding a dynamic challenge to the fight.
|
||||
|
||||
## Head Spawning (`SpawnLeftHead`, `SpawnRightHead`):
|
||||
* These routines spawn `Sprite_KydreeokHead` (`$CF`) sprites.
|
||||
* They assign `SprSubtype` to the spawned heads (`$00` for left, `$01` for right), allowing the child sprites to differentiate their behavior.
|
||||
* They store the IDs of the spawned heads in global variables (`Offspring1_Id`, `Offspring2_Id`).
|
||||
* They set the initial position of the heads relative to the main boss and initialize neck segment coordinates.
|
||||
|
||||
## Movement (`MoveBody`, `StopIfOutOfBounds`):
|
||||
* **`MoveBody`:** Manages the main body's movement, calling `JSL Sprite_Move` and updating background scrolling based on its movement. It reuses logic from `Trinexx_MoveBody`.
|
||||
* **`StopIfOutOfBounds`:** Prevents the boss from moving beyond screen boundaries. It also subtly adjusts the neck positions when hitting a boundary, creating a visual "pushing" effect.
|
||||
|
||||
## Palette Management (`ApplyPalette`, `ApplyEndPalette`):
|
||||
* **`ApplyPalette`:** Sets the initial palette for the boss.
|
||||
* **`ApplyEndPalette`:** Sets a different palette, likely for a defeated state or phase change.
|
||||
|
||||
## Graphics Transfer (`ApplyKydreeokGraphics`):
|
||||
* Handles DMA transfer of graphics data (`kydreeok.bin`, `kydreeok_phase2.bin`) to VRAM, allowing for different graphical appearances across phases.
|
||||
|
||||
## Global Variables for Neck Control:
|
||||
* `LeftNeck1_X` to `LeftNeck3_Y`, `RightNeck1_X` to `RightNeck3_Y`: Global RAM addresses used to store the coordinates of the neck segments, enabling the heads to track them.
|
||||
* `Kydreeok_Id`: Stores the sprite index of the main Kydreeok boss.
|
||||
* `Offspring1_Id`, `Offspring2_Id`: Store the sprite indices of the spawned heads.
|
||||
|
||||
## Discrepancies/Notes:
|
||||
* The `!Health` and `!Damage` properties are `00`, confirming that the boss's health and damage are managed through its heads (`Sprite_KydreeokHead`) and custom logic within `Sprite_Kydreeok_CheckIfDead`.
|
||||
* The `Sprite_Kydreeok_CheckIfDead` routine clearly defines a multi-phase fight where the heads can be defeated, respawned, and ultimately lead to the main boss's defeat.
|
||||
* The commented-out `SpawnCenterHead` suggests a potential for a three-headed Kydreeok that was either removed or is an unimplemented feature.
|
||||
* Reusing movement logic from `Trinexx_MoveBody` is efficient but should be considered for unique boss feel.
|
||||
* Hardcoded addresses for `JSL` calls could be replaced with named labels for better maintainability.
|
||||
@@ -1,68 +0,0 @@
|
||||
# Kydreeok Head Sprite Analysis
|
||||
|
||||
## Overview
|
||||
The `kydreeok_head` sprite (ID: `Sprite_KydreeokHead`, which is `$CF`) is a child sprite of the main `Kydreeok` boss. It represents one of the multi-headed boss's individual heads, possessing independent movement, attack patterns, and damage handling. Its primary role is to move, rotate, and attack Link, contributing to the overall boss encounter.
|
||||
|
||||
## Key Properties:
|
||||
* **Sprite ID:** `Sprite_KydreeokHead` (`$CF`)
|
||||
* **Description:** Child sprite of the Kydreeok boss, responsible for individual head behavior.
|
||||
* **Number of Tiles:** 7
|
||||
* **Health:** `$C8` (200 decimal) - This high health value indicates it's a significant component of the boss fight.
|
||||
* **Damage:** `00` (Damage is likely applied through its spawned attacks.)
|
||||
* **Special Properties:**
|
||||
* `!Boss = 00` (Not marked as a boss itself, as it's a component of a larger boss.)
|
||||
* `!Hitbox = 09`
|
||||
|
||||
## Subtypes:
|
||||
The `SprSubtype, X` register is crucial for differentiating the heads and their behavior:
|
||||
* **Subtype `$00`:** Controls the "Left Head" via `Neck1_Control`.
|
||||
* **Subtype `$01`:** Controls the "Right Head" via `Neck2_Control`.
|
||||
This allows the same sprite ID to manage multiple distinct heads.
|
||||
|
||||
## Main States/Actions (`Sprite_KydreeokHead_Main` Jump Table):
|
||||
The head's behavior is governed by a state machine:
|
||||
* **`KydreeokHead_ForwardAnim` (0x00):** Default state, plays forward animation, handles damage, performs rotational movement, and randomly attacks. Transitions to other directional states based on Link's position.
|
||||
* **`KydreeokHead_RightAnim` (0x01):** Plays right-facing animation, handles damage, rotation, and attacks.
|
||||
* **`KydreeokHead_LeftAnim` (0x02):** Plays left-facing animation, handles damage, rotation, and attacks.
|
||||
* **`KydreeokHead_FarRight` (0x03):** Plays far-right animation, moves towards Link, handles damage, rotation, and attacks.
|
||||
* **`KydreeokHead_FarLeft` (0x04):** Plays far-left animation, moves towards Link, handles damage, rotation, and attacks.
|
||||
* **`KydreeokHead_SummonFire` (0x05):** Moves towards Link, checks damage, and utilizes `JSL Sprite_Twinrova_FireAttack` to deal damage. The head sprite is then killed after a timer.
|
||||
|
||||
## Initialization (`Sprite_KydreeokHead_Prep`):
|
||||
* Sets initial health to `$FF` (255 decimal), though the `!Health` property is `$C8`. This discrepancy might be overridden by the parent `Kydreeok` sprite or is a temporary value.
|
||||
* Sets `SprBump = $09` (bump damage type).
|
||||
* Initializes `SprMiscE, X` to `0`.
|
||||
|
||||
## Drawing (`Sprite_KydreeokHead_Draw`):
|
||||
* Uses standard OAM allocation routines.
|
||||
* The main drawing routine calls `JMP Sprite_KydreeokHead_DrawNeck`, indicating that the neck segments are drawn after the head.
|
||||
* Includes logic for flashing when damaged.
|
||||
|
||||
## Neck Control (`KydreeokHead_NeckControl`, `Neck1_Control`, `Neck2_Control`, `Sprite_KydreeokHead_DrawNeck`, `DrawNeckPart`):
|
||||
This is a sophisticated system for managing the multi-segmented neck:
|
||||
* `KydreeokHead_NeckControl` dispatches to `Neck1_Control` (for the left head) or `Neck2_Control` (for the right head) based on `SprSubtype, X`.
|
||||
* `Neck1_Control` and `Neck2_Control` manage the movement and positioning of three neck segments, ensuring they follow the head while maintaining specific distances.
|
||||
* `Sprite_KydreeokHead_DrawNeck` and `DrawNeckPart` handle the rendering of these segments.
|
||||
|
||||
## Movement and Rotation (`KydreeokHead_RotationMove`, `RotateHeadUsingSpeedValues`, `MoveWithBody`):
|
||||
* **`KydreeokHead_RotationMove`:** Generates random speeds, dispatches to neck control, moves with the main body, and applies rotational movement.
|
||||
* **`RotateHeadUsingSpeedValues`:** Uses sine/cosine tables (`XSpeedSin`, `YSpeedSin`) to apply smooth rotational movement.
|
||||
* **`MoveWithBody`:** Ensures the head's position is correctly offset and relative to the main `Kydreeok` boss, adjusting for left or right heads.
|
||||
|
||||
## Attacks (`RandomlyAttack`, `KydreeokHead_SummonFire`):
|
||||
* **`RandomlyAttack`:** Randomly spawns a fire-based projectile (which is actually the `Sprite_KydreeokHead` itself entering the `SummonFire` state).
|
||||
* **`KydreeokHead_SummonFire`:** This state is entered when a fire projectile is spawned. It moves towards Link and uses `JSL Sprite_Twinrova_FireAttack` to deal damage, after which the head sprite is killed.
|
||||
|
||||
## Key Macros/Functions Used:
|
||||
* `%Set_Sprite_Properties`, `%GotoAction`, `%StartOnFrame`, `%PlayAnimation`, `%MoveTowardPlayer`
|
||||
* `JSL JumpTableLocal`, `JSL Sprite_CheckDamageFromPlayer`, `JSL Sprite_CheckDamageToPlayer`, `JSL Sprite_DamageFlash_Long`
|
||||
* `JSL GetRandomInt`, `JSL Sprite_MoveLong`, `JSL Sprite_IsToRightOfPlayer`
|
||||
* `JSL Sprite_SpawnDynamically`, `JSL Sprite_SetSpawnedCoords`
|
||||
* `JSL Sprite_Twinrova_FireAttack`, `JSL Fireball_SpawnTrailGarnish`
|
||||
* `JSR GetDistance8bit`
|
||||
|
||||
## Discrepancies/Notes:
|
||||
* The `!Health` property is `$C8`, but `Sprite_KydreeokHead_Prep` sets `SprHealth, X` to `$FF`. This needs clarification.
|
||||
* The reuse of `JSL Sprite_Twinrova_FireAttack` for Kydreeok's head is an example of code reuse, but it's important to ensure it fits the thematic design of Kydreeok.
|
||||
* The neck control system is quite intricate, highlighting advanced sprite design.
|
||||
* Several hardcoded addresses for `JSL` calls could be replaced with named labels for better maintainability.
|
||||
@@ -1,90 +0,0 @@
|
||||
# Kydrog Boss Sprite Analysis
|
||||
|
||||
## Overview
|
||||
The `kydrog_boss` sprite (ID: `Sprite_KydrogBoss`, which is `$CB`) represents the main Kydrog boss. This boss features multiple phases, dynamic movement, and the ability to summon stalfos offspring. It's a complex encounter designed to challenge the player through varied attack patterns and phase transitions.
|
||||
|
||||
## Key Properties:
|
||||
* **Sprite ID:** `Sprite_KydrogBoss` (`$CB`)
|
||||
* **Description:** The main Kydrog boss, controlling its own movement, phases, and spawning stalfos offspring.
|
||||
* **Number of Tiles:** 11
|
||||
* **Health:** `00` (The boss's health is managed through `CheckForNextPhase` and `Sprite_KydrogBoss_CheckIfDead`, not directly by this property.)
|
||||
* **Damage:** `00` (Damage dealt to Link is likely handled by its attacks or spawned offspring.)
|
||||
* **Special Properties:**
|
||||
* `!Boss = $01` (Correctly identified as a boss.)
|
||||
* `!Shadow = 01` (Draws a shadow.)
|
||||
* `!Hitbox = 03`
|
||||
|
||||
## Custom Variables:
|
||||
* `!ConsecutiveHits = $AC`: Tracks consecutive hits on the boss, influencing its behavior.
|
||||
* `!KydrogPhase = $7A`: Manages the current phase of the boss fight.
|
||||
* `!WalkSpeed = 10`: Defines the boss's walking speed.
|
||||
|
||||
## Main States/Actions (`Sprite_KydrogBoss_Main` Jump Table):
|
||||
The boss's behavior is governed by a detailed state machine:
|
||||
* **`KydrogBoss_Init` (0x00):** Initial state, plays an "Arms Crossed" animation, and transitions to `KydrogBoss_WalkState` after an intro timer.
|
||||
* **`KydrogBoss_WalkState` (0x01):** The primary walking state. Manages phase transitions, handles damage, taunting, and determines the next walking direction (forward, backward, left, right) based on Link's position and proximity.
|
||||
* **`KydrogBoss_WalkForward` (0x02), `KydrogBoss_WalkLeft` (0x03), `KydrogBoss_WalkRight` (0x04), `KydrogBoss_WalkBackward` (0x05):** These states handle movement in specific directions, playing corresponding animations and executing core movement logic.
|
||||
* **`KydrogBoss_TakeDamage` (0x06):** Manages the boss taking damage. Increments `!ConsecutiveHits`, plays a damage animation, spawns stalfos offspring, and can trigger an ascend action.
|
||||
* **`KydrogBoss_TauntPlayer` (0x07):** Plays a taunting animation, handles damage, and transitions to `KydrogBoss_SummonStalfos`.
|
||||
* **`KydrogBoss_SummonStalfos` (0x08):** Plays a summoning animation, handles damage, spawns stalfos offspring, and can throw a bone projectile at Link.
|
||||
* **`KydrogBoss_Death` (0x09):** Handles the boss's death sequence, including killing spawned friends, playing a flickering animation, and despawning.
|
||||
* **`KydrogBoss_Ascend` (0x0A):** The boss ascends off-screen, increasing its `SprHeight` and spawning stalfos offspring. Transitions to `KydrogBoss_Descend`.
|
||||
* **`KydrogBoss_Descend` (0x0B):** The boss descends, tracking Link's position, decreasing its `SprHeight`, and spawning stalfos offspring. Transitions back to `KydrogBoss_WalkState`.
|
||||
* **`KydrogBoss_Abscond` (0x0C):** The boss moves away from Link, increasing its speed, and transitions back to `KydrogBoss_WalkState`.
|
||||
|
||||
## Initialization (`Sprite_KydrogBoss_Prep`):
|
||||
* Initializes `!KydrogPhase` to `00`.
|
||||
* Sets initial health to `$A0` (160 decimal).
|
||||
* Configures deflection (`SprDefl`), hitbox (`SprHitbox`), and bump damage (`SprBump`).
|
||||
* Sets `SprGfxProps` to not invincible.
|
||||
* Calls `JSR KydrogBoss_Set_Damage` to define its damage vulnerabilities.
|
||||
* Sets initial sprite speeds and `!Harmless = 00`.
|
||||
* Sets an intro timer (`SprTimerD = $80`).
|
||||
|
||||
## Death Check (`Sprite_KydrogBoss_CheckIfDead`):
|
||||
* Monitors `SprHealth, X`. If health is zero or negative, it triggers the boss's death sequence, setting `SprState = $04` (kill sprite boss style) and `SprAction = $09` (KydrogBoss_Death stage).
|
||||
|
||||
## Phase Management (`CheckForNextPhase`):
|
||||
This routine dynamically manages the boss's phases based on its current health:
|
||||
* **Phase One (`!KydrogPhase = $00`):** Transitions to Phase Two when health drops below `$20`.
|
||||
* **Phase Two (`!KydrogPhase = $01`):** Transitions to Phase Three when health drops below `$20`. Resets health to `$80`, sets action to `KydrogBoss_WalkState`, and increments `SprFlash, X`.
|
||||
* **Phase Three (`!KydrogPhase = $02`):** Transitions to Phase Four when health drops below `$20`. Resets health to `$80`, sets action to `KydrogBoss_WalkState`.
|
||||
* **Phase Four (`!KydrogPhase = $03`):** Sets action to `KydrogBoss_WalkState`.
|
||||
|
||||
## Damage Table (`KydrogBoss_Set_Damage`):
|
||||
* Defines how KydrogBoss reacts to various attack types (Boomerang, Sword, Arrow, Bomb, etc.), stored in a damage properties table.
|
||||
|
||||
## Offspring Spawning (`RandomStalfosOffspring`, `Sprite_Offspring_Spawn`, `Sprite_Offspring_SpawnHead`):
|
||||
* **`RandomStalfosOffspring`:** Randomly spawns either a normal stalfos offspring (`Sprite_Offspring_Spawn`) or a stalfos head offspring (`Sprite_Offspring_SpawnHead`), with a limit of 4 active stalfos.
|
||||
* **`Sprite_Offspring_Spawn`:** Spawns a stalfos offspring (Sprite ID `$A7` or `$85`).
|
||||
* **`Sprite_Offspring_SpawnHead`:** Spawns a stalfos head offspring (Sprite ID `$7C` or `$02`).
|
||||
|
||||
## Attacks (`Kydrog_ThrowBoneAtPlayer`):
|
||||
* **`Kydrog_ThrowBoneAtPlayer`:** Spawns a bone projectile (Sprite ID `$A7`) that moves towards Link.
|
||||
|
||||
## Movement (`KydrogBoss_DoMovement`, `BounceBasedOnPhase`):
|
||||
* **`KydrogBoss_DoMovement`:** Handles damage checks, applies damage to Link on contact, flashes when damaged, and incorporates phase-based bouncing and stalfos spawning.
|
||||
* **`BounceBasedOnPhase`:** Adjusts the boss's bounce speed based on the current `!KydrogPhase`.
|
||||
|
||||
## Drawing (`Sprite_KydrogBoss_Draw`):
|
||||
* Uses standard OAM allocation routines.
|
||||
* Handles complex animation frames, x/y offsets, character data, properties, and sizes for drawing the boss.
|
||||
* Utilizes 16-bit operations (`REP #$30`, `SEP #$30`) for precise drawing calculations.
|
||||
|
||||
## Other Routines:
|
||||
* **`StopIfTooClose()` macro:** Prevents the boss from getting too close to Link.
|
||||
* **`Sprite_CheckIfFrozen`:** Checks if the sprite is frozen and unfreezes it after a timer.
|
||||
* **`GetNumberSpawnStalfos`:** Counts the number of active stalfos offspring.
|
||||
* **`SpawnSplash`:** Spawns a splash effect.
|
||||
* **`SpawnBossPoof`:** Spawns a boss poof effect.
|
||||
* **`HandleMovingSplash`:** Handles splash effects during movement.
|
||||
* **`SpawnMedallion` / `SpawnMedallionAlt`:** Spawns a medallion.
|
||||
|
||||
## Discrepancies/Notes:
|
||||
* The main boss's health is intricately managed through `SprHealth, X` and `!KydrogPhase`, requiring a clear understanding of their interplay.
|
||||
* The stalfos offspring are spawned using specific sprite IDs, which should be cross-referenced for full understanding.
|
||||
* Many hardcoded values for timers, speeds, and offsets could be replaced with named constants for improved readability and maintainability.
|
||||
* The code includes direct calls to sound effect functions (`JSL $0DBB8A`) and a commented-out call to `JSL $01F3EC` (Light Torch), which might be a leftover or an unimplemented feature.
|
||||
|
||||
## Hardcoded Activation Trigger:
|
||||
* As noted by the user, the activation trigger for KydrogBoss is hardcoded. Specifically, in the `WaitForPlayerToApproach` routine, the boss checks `LDA.b $20 : CMP #$08C8`. `$20` represents Link's Y position, and `$08C8` is a hardcoded Y-coordinate. This means the boss will only activate when Link reaches this specific Y-coordinate, making it difficult to relocate the boss to other overworld maps without modifying this value.
|
||||
@@ -1,79 +0,0 @@
|
||||
# Manhandla / Big Chuchu Sprite Analysis
|
||||
|
||||
## Overview
|
||||
The `manhandla` sprite (ID: `Sprite_Manhandla`, which is `$88`) is a multi-phase boss. It begins as Manhandla, a multi-headed plant-like enemy, and upon the defeat of its individual heads, it transforms into a Big Chuchu. This design creates a dynamic and evolving boss encounter.
|
||||
|
||||
## Key Properties:
|
||||
* **Sprite ID:** `Sprite_Manhandla` (`$88`)
|
||||
* **Description:** A multi-phase boss that transforms from Manhandla to Big Chuchu.
|
||||
* **Number of Tiles:** 3
|
||||
* **Health:** `00` (Health is managed by its spawned heads in the first phase and then by its own `SprHealth` in the second phase.)
|
||||
* **Damage:** `00` (Damage dealt to Link is likely handled by its heads or spawned projectiles.)
|
||||
* **Special Properties:**
|
||||
* `!Boss = 01` (Correctly identified as a boss.)
|
||||
* `!DeathAnimation = 01` (Indicates custom death handling rather than a standard animation.)
|
||||
* `!Hitbox = 00`
|
||||
|
||||
## Custom Variables:
|
||||
* `Offspring1_Id`, `Offspring2_Id`, `Offspring3_Id`: Global variables used to track the sprite indices of the spawned Manhandla heads.
|
||||
|
||||
## Main States/Actions (`Sprite_Manhandla_Main` Jump Table):
|
||||
The boss's behavior is governed by a detailed state machine across its phases:
|
||||
* **`Manhandla_Intro` (0x00):** Initial state. Spawns the three Manhandla heads (`SpawnLeftManhandlaHead`, `SpawnRightManhandlaHead`, `SpawnCenterMandhandlaHead`) and transitions to `Manhandla_Body`.
|
||||
* **`Manhandla_FrontHead` (0x01), `Manhandla_LeftHead` (0x02), `Manhandla_RightHead` (0x03):** These states are likely executed by the individual Manhandla head child sprites, managing their movement, damage, and contact with Link.
|
||||
* **`BigChuchu_Main` (0x04):** The primary state for the Big Chuchu phase. Handles movement, damage, and can spawn Chuchu blasts.
|
||||
* **`Flower_Flicker` (0x05):** A transitional state that flickers the background (BG2) and spawns a new `Sprite_Manhandla` (with `SprSubtype = $08`, representing the Big Chuchu head) after a timer, as part of the transformation.
|
||||
* **`Manhandla_Body` (0x06):** The main state for the Manhandla body. Handles movement, damage, updates the positions of its spawned heads, and can spawn Mothula beams.
|
||||
* **`BigChuchu_Emerge` (0x07):** Manages the emergence animation of the Big Chuchu.
|
||||
* **`BigChuchu_Flower` (0x08):** A state for the Big Chuchu, possibly related to its visual appearance or an attack.
|
||||
* **`BigChuchu_Dead` (0x09):** Handles the death sequence of the Big Chuchu.
|
||||
* **`ChuchuBlast` (0x0A):** Manages the movement and damage of the spawned Chuchu blast projectile.
|
||||
|
||||
## Initialization (`Sprite_Manhandla_Prep`):
|
||||
* Sets initial movement speeds and enables BG1 movement.
|
||||
* Configures deflection properties (`SprDefl = $80`).
|
||||
* Sets initial health to `$80`.
|
||||
* Initializes `SprAction, X` based on `SprSubtype, X`.
|
||||
|
||||
## Phase Transition and Death Check (`Sprite_Manhandla_CheckForNextPhaseOrDeath`):
|
||||
This critical routine orchestrates the boss's transformation:
|
||||
* It checks if all three Manhandla heads (`Offspring1_Id`, `Offspring2_Id`, `Offspring3_Id`) are dead.
|
||||
* If all heads are defeated, it triggers the transition to the Big Chuchu phase:
|
||||
* Sets `SprMiscD, X = $01` (phase flag).
|
||||
* Refills health (`SprHealth = $40`).
|
||||
* Adjusts OAM entries (`SprNbrOAM = $08`).
|
||||
* Sets `SprAction = $07` (BigChuchu_Emerge).
|
||||
* It also manages the Big Chuchu's defeat, transitioning to `BigChuchu_Dead` when its health drops below `$04`.
|
||||
|
||||
## Head Spawning (`SpawnLeftManhandlaHead`, `SpawnRightManhandlaHead`, `SpawnCenterMandhandlaHead`):
|
||||
* These routines spawn `Sprite_Manhandla` (`$88`) sprites as child heads.
|
||||
* They assign specific `SprSubtype` values (`$03` for left, `$02` for right, `$01` for center) to differentiate the heads.
|
||||
* They store the IDs of the spawned heads in global variables (`Offspring1_Id`, `Offspring2_Id`, `Offspring3_Id`).
|
||||
* They set the initial position, health, and properties for each head.
|
||||
|
||||
## Head Positioning (`SetLeftHeadPos`, `SetRightHeadPos`, `SetCenterHeadPos`):
|
||||
* These routines dynamically calculate and set the positions of the spawned heads relative to the main Manhandla body.
|
||||
|
||||
## Movement (`Sprite_Manhandla_Move`, `Manhandla_StopIfOutOfBounds`):
|
||||
* **`Sprite_Manhandla_Move`:** The core movement logic for the Manhandla body, utilizing a jump table for `StageControl`, `MoveXandY`, `MoveXorY`, and `KeepWalking` states.
|
||||
* **`Manhandla_StopIfOutOfBounds`:** Prevents the boss from moving beyond predefined screen boundaries.
|
||||
|
||||
## Attacks (`Chuchu_SpawnBlast`, `Mothula_SpawnBeams`):
|
||||
* **`Chuchu_SpawnBlast`:** Spawns a Chuchu blast projectile (Sprite ID `$88` with `SprSubtype = $0A`).
|
||||
* **`Mothula_SpawnBeams`:** Spawns beam projectiles (Sprite ID `$89`), called from `Manhandla_Body`.
|
||||
|
||||
## Drawing (`Sprite_Manhandla_Draw`, `Sprite_BigChuchu_Draw`):
|
||||
* **`Sprite_Manhandla_Draw`:** Renders the Manhandla body and its heads.
|
||||
* **`Sprite_BigChuchu_Draw`:** Renders the Big Chuchu form.
|
||||
* Both utilize standard OAM allocation routines and handle animation frames, offsets, character data, properties, and sizes.
|
||||
|
||||
## Graphics and Palette (`ApplyManhandlaGraphics`, `ApplyManhandlaPalette`):
|
||||
* **`ApplyManhandlaGraphics`:** Handles DMA transfer of graphics data (`manhandla.bin`) to VRAM.
|
||||
* **`ApplyManhandlaPalette`:** Sets the custom palette for Manhandla.
|
||||
|
||||
## Discrepancies/Notes:
|
||||
* The `!Health` property is `00`, indicating that the boss's health is managed by its heads in the first phase and then by its own `SprHealth` in the Big Chuchu phase.
|
||||
* The `Sprite_Manhandla` ID (`$88`) is efficiently reused for the main boss, its heads, and the Chuchu blast projectile, with `SprSubtype` differentiating their roles.
|
||||
* The reuse of `Mothula_SpawnBeams` for Manhandla is an example of code reuse.
|
||||
* Hardcoded values for timers, speeds, and offsets could be replaced with named constants for improved readability and maintainability.
|
||||
* A commented-out `org` for `Sprite_DoTheDeath#PrepareEnemyDrop.post_death_stuff` suggests potential modifications to the death routine.
|
||||
@@ -1,254 +0,0 @@
|
||||
# Anti Kirby Sprite Analysis
|
||||
|
||||
## 1. Overview
|
||||
The Anti Kirby sprite (`Sprite_AntiKirby`) is an enemy that exhibits unique behaviors, including a "suck" attack that can steal Link's items (bombs, arrows, rupees, or shield). It has distinct states for walking, sucking, being full (after stealing an item), and being "hatted" (presumably after Link gets his item back or a specific condition is met).
|
||||
|
||||
## 2. Sprite Properties
|
||||
The sprite properties are defined at the beginning of `Sprites/Enemies/anti_kirby.asm`:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_AntiKirby
|
||||
!NbrTiles = 02
|
||||
!Harmless = 00
|
||||
!HVelocity = 00
|
||||
!Health = $08
|
||||
!Damage = 04
|
||||
!DeathAnimation = 00
|
||||
!ImperviousAll = 00
|
||||
!SmallShadow = 00
|
||||
!Shadow = 01
|
||||
!Palette = 00
|
||||
!Hitbox = 03
|
||||
!Persist = 00
|
||||
!Statis = 00
|
||||
!CollisionLayer = 00
|
||||
!CanFall = 00
|
||||
!DeflectArrow = 00
|
||||
!WaterSprite = 00
|
||||
!Blockable = 00
|
||||
!Prize = 00
|
||||
!Sound = 00
|
||||
!Interaction = 00
|
||||
!Statue = 00
|
||||
!DeflectProjectiles = 00
|
||||
!ImperviousArrow = 00
|
||||
!ImpervSwordHammer = 00
|
||||
!Boss = 00
|
||||
```
|
||||
|
||||
**Key Observations:**
|
||||
* `!SPRID = Sprite_AntiKirby`: This uses a named constant for the sprite ID, which is good practice.
|
||||
* `!Health = $08`: Anti Kirby has 8 health points.
|
||||
* `!Damage = 04`: Deals half a heart of damage to Link.
|
||||
* `!Hitbox = 03`: A relatively small hitbox.
|
||||
* `!Shadow = 01`: It draws a shadow.
|
||||
* `!Boss = 00`: It is not classified as a boss sprite, despite its complex behavior.
|
||||
|
||||
## 3. Main Structure (`Sprite_AntiKirby_Long`)
|
||||
This routine follows the standard structure for sprites, calling the draw routine, shadow routine, and then the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_AntiKirby_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_AntiKirby_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_AntiKirby_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Initialization (`Sprite_AntiKirby_Prep`)
|
||||
The `_Prep` routine initializes several sprite-specific variables and sets its `SprBump`, `SprHealth`, and `SprPrize` based on Link's current sword level (or a similar progression metric, inferred from `LDA.l Sword : DEC : TAY`). This is an interesting way to scale enemy difficulty.
|
||||
|
||||
```asm
|
||||
Sprite_AntiKirby_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
STZ.w SprDefl, X
|
||||
STZ.w SprTileDie, X
|
||||
STZ.w SprMiscB, X
|
||||
LDA.l Sword : DEC : TAY
|
||||
LDA .bump_damage, Y : STA.w SprBump, X
|
||||
LDA .health, Y : STA.w SprHealth, X
|
||||
LDA .prize_pack, Y : STA.w SprPrize, X
|
||||
PLB
|
||||
RTL
|
||||
|
||||
.bump_damage
|
||||
db $81, $88, $88, $88
|
||||
.health
|
||||
db 06, 10, 20, 20
|
||||
.prize_pack
|
||||
db 6, 3, 3, 3
|
||||
}
|
||||
```
|
||||
**Insight:** The use of `LDA.l Sword : DEC : TAY` to index into `.bump_damage`, `.health`, and `.prize_pack` tables demonstrates a dynamic difficulty scaling mechanism based on player progression (likely sword upgrades). This is a valuable pattern for making enemies adapt to the player's power level.
|
||||
|
||||
## 5. Main Logic & State Machine (`Sprite_AntiKirby_Main`)
|
||||
The `_Main` routine implements a complex state machine using `JSL JumpTableLocal` and a series of `dw` (define word) entries pointing to different states.
|
||||
|
||||
```asm
|
||||
Sprite_AntiKirby_Main:
|
||||
{
|
||||
JSL Sprite_IsToRightOfPlayer
|
||||
TYA : CMP #$01 : BNE .WalkRight
|
||||
.WalkLeft
|
||||
LDA.b #$40 : STA.w SprMiscC, X
|
||||
JMP +
|
||||
.WalkRight
|
||||
STZ.w SprMiscC, X
|
||||
+
|
||||
|
||||
JSL Sprite_DamageFlash_Long
|
||||
JSL Sprite_CheckIfRecoiling
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw AntiKirby_Main ; State 0: Normal movement/attack
|
||||
dw AntiKirby_Hurt ; State 1: Recoiling from damage
|
||||
dw AntiKirby_BeginSuck ; State 2: Initiating suck attack
|
||||
dw AntiKirby_Sucking ; State 3: Actively sucking Link
|
||||
dw AntiKirby_Full ; State 4: Full after stealing item
|
||||
dw AntiKirby_Hatted ; State 5: Hatted (after Link gets item back?)
|
||||
dw AntiKirby_HattedHurt ; State 6: Hatted and hurt
|
||||
dw AntiKirby_Death ; State 7: Death animation
|
||||
|
||||
; ... (State implementations below) ...
|
||||
}
|
||||
```
|
||||
|
||||
**State Breakdown:**
|
||||
* **`AntiKirby_Main` (State 0):**
|
||||
* Checks health and transitions to `AntiKirby_Full` if health is low (this seems like a bug, should probably be `AntiKirby_Death`).
|
||||
* Randomly initiates the `AntiKirby_BeginSuck` state.
|
||||
* Plays walking animation (`%PlayAnimation(0, 2, 10)`).
|
||||
* Handles damage from player and transitions to `AntiKirby_Hurt`.
|
||||
* Deals damage to Link on contact (`%DoDamageToPlayerSameLayerOnContact()`).
|
||||
* Moves toward Link (`%MoveTowardPlayer(8)`) and bounces from tile collisions.
|
||||
* **`AntiKirby_Hurt` (State 1):** Plays a hurt animation and waits for a timer (`SprTimerA`) to expire before returning to `AntiKirby_Main`.
|
||||
* **`AntiKirby_BeginSuck` (State 2):**
|
||||
* Plays a "suck" animation (`%PlayAnimation(4, 5, 10)`).
|
||||
* Checks for damage from player.
|
||||
* Checks Link's proximity (`$0E`, `$0F` are likely relative X/Y coordinates to Link). If Link is close enough, it transitions to `AntiKirby_Sucking` and sets up a projectile speed towards Link.
|
||||
* **`AntiKirby_Sucking` (State 3):**
|
||||
* Plays a "sucking" animation (`%PlayAnimation(5, 5, 10)`).
|
||||
* Uses `JSL Sprite_DirectionToFacePlayer` and `JSL DragPlayer` to pull Link towards it if he's close enough.
|
||||
* If Link is very close, it "consumes" Link, storing Link's position in `SprMiscB` and `SprMiscA`, sets a timer, and transitions to `AntiKirby_Full`.
|
||||
* **`AntiKirby_Full` (State 4):**
|
||||
* Plays a "full" animation (`%PlayAnimation(10, 10, 10)`).
|
||||
* Sets Link's position to the stored `SprMiscA`/`SprMiscB` (effectively "spitting" Link out).
|
||||
* Transitions to `AntiKirby_Hatted` after a timer.
|
||||
* **`AntiKirby_Hatted` (State 5):**
|
||||
* Plays a "hatted" animation (`%PlayAnimation(6, 8, 10)`).
|
||||
* Moves toward Link, deals damage, and handles damage from player (transitions to `AntiKirby_HattedHurt`).
|
||||
* **`AntiKirby_HattedHurt` (State 6):** Plays a hurt animation for the "hatted" state and returns to `AntiKirby_Hatted`.
|
||||
* **`AntiKirby_Death` (State 7):** Sets `SprState` to `$06` (likely a death state) and plays a sound effect.
|
||||
|
||||
**Insight:** The `AntiKirby_Main` state's health check `LDA.w SprHealth, X : CMP.b #$01 : BCS .NotDead : %GotoAction(4)` seems to incorrectly transition to `AntiKirby_Full` (State 4) instead of `AntiKirby_Death` (State 7) when health is 0. This might be a bug or an intentional design choice for a specific game mechanic.
|
||||
|
||||
## 6. Item Stealing Logic (`AntiKirby_StealItem`)
|
||||
This is a separate routine that is likely called when Anti Kirby successfully "sucks" Link. It checks Link's inventory and steals a random item (bomb, arrow, rupee, or shield).
|
||||
|
||||
```asm
|
||||
AntiKirby_StealItem:
|
||||
{
|
||||
REP #$20
|
||||
; ... (collision checks) ...
|
||||
SEP #$20
|
||||
LDA.w SprTimerA, X : CMP.b #$2E : BCS .exit ; Timer check
|
||||
JSL GetRandomInt
|
||||
AND.b #$03
|
||||
INC A
|
||||
STA.w SprMiscG, X
|
||||
STA.w SprMiscE, X
|
||||
|
||||
CMP.b #$01 : BNE .dont_steal_bomb
|
||||
LDA.l $7EF343 : BEQ .dont_steal_anything ; Check bombs
|
||||
DEC A
|
||||
STA.l $7EF343
|
||||
RTS
|
||||
.dont_steal_anything
|
||||
SEP #$20
|
||||
STZ.w SprMiscG,X
|
||||
RTS
|
||||
.dont_steal_bomb
|
||||
|
||||
CMP.b #$02 : BNE .dont_steal_arrow
|
||||
LDA.l $7EF377 : BEQ .dont_steal_anything ; Check arrows
|
||||
DEC A
|
||||
STA.l $7EF377
|
||||
RTS
|
||||
.dont_steal_arrow
|
||||
|
||||
CMP.b #$03 : BNE .dont_steal_rupee
|
||||
REP #$20
|
||||
LDA.l $7EF360 : BEQ .dont_steal_anything ; Check rupees
|
||||
DEC A
|
||||
STA.l $7EF360
|
||||
.exit
|
||||
SEP #$20
|
||||
RTS
|
||||
; -----------------------------------------------------
|
||||
|
||||
.dont_steal_rupee
|
||||
LDA.l $7EF35A : STA.w SprSubtype, X : BEQ .dont_steal_anything ; Check shield
|
||||
CMP.b #$03 : BEQ .dont_steal_anything
|
||||
LDA.b #$00
|
||||
STA.l $7EF35A
|
||||
RTS
|
||||
}
|
||||
```
|
||||
**Key Observations:**
|
||||
* Uses `REP #$20` and `SEP #$20` to explicitly control the accumulator size (16-bit for address calculations, 8-bit for item counts). This is crucial for correct memory access.
|
||||
* Randomly selects an item to steal using `JSL GetRandomInt : AND.b #$03 : INC A`.
|
||||
* Directly modifies SRAM addresses (`$7EF343` for bombs, `$7EF377` for arrows, `$7EF360` for rupees, `$7EF35A` for shield) to decrement item counts or remove the shield.
|
||||
* The shield stealing logic (`LDA.l $7EF35A : STA.w SprSubtype, X : BEQ .dont_steal_anything : CMP.b #$03 : BEQ .dont_steal_anything : LDA.b #$00 : STA.l $7EF35A`) is a bit convoluted. It seems to check the shield type and only steals if it's not a specific type (possibly the Mirror Shield, which is type 3).
|
||||
|
||||
**Insight:** The `AntiKirby_StealItem` routine is a good example of how to interact directly with Link's inventory in SRAM. It also highlights the importance of explicitly managing the processor status flags (`REP`/`SEP`) when dealing with mixed 8-bit and 16-bit operations, especially when accessing memory.
|
||||
|
||||
## 7. Drawing (`Sprite_AntiKirby_Draw`)
|
||||
The drawing routine uses `JSL Sprite_PrepOamCoord` and `JSL Sprite_OAM_AllocateDeferToPlayer` for OAM management. It then uses a series of tables (`.start_index`, `.nbr_of_tiles`, `.x_offsets`, `.y_offsets`, `.chr`, `.properties`, `.sizes`) to define the sprite's animation frames and tile data.
|
||||
|
||||
```asm
|
||||
Sprite_AntiKirby_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprGfx, X : CLC : ADC.w SprFrame, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
LDA.w SprFlash, X : STA $08
|
||||
LDA.w SprMiscC, X : STA $09
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
; ... (OAM manipulation logic) ...
|
||||
|
||||
.start_index
|
||||
db $00, $01, $02, $03, $04, $05, $06, $08, $0A, $0C, $0E, $10
|
||||
.nbr_of_tiles
|
||||
db 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1
|
||||
; ... (other OAM tables) ...
|
||||
}
|
||||
```
|
||||
**Key Observations:**
|
||||
* The drawing logic includes a check for `SprMiscC, X` to determine if the sprite is facing left or right, and uses different `.x_offsets` tables (`.x_offsets` vs `.x_offsets_2`) accordingly. This is a common pattern for horizontal flipping.
|
||||
* The `.properties` table defines the palette, priority, and flip bits for each tile.
|
||||
* The `.sizes` table defines the size of each tile (e.g., `$02` for 16x16).
|
||||
|
||||
## 8. Advanced Design Patterns Demonstrated
|
||||
|
||||
* **Dynamic Difficulty Scaling:** The `_Prep` routine adjusts health, bump damage, and prize based on `Link's Sword` level.
|
||||
* **Complex State Machine:** The `_Main` routine uses a jump table to manage multiple distinct behaviors (walking, sucking, full, hatted, hurt, death).
|
||||
* **Direct SRAM Interaction:** The `AntiKirby_StealItem` routine directly modifies Link's inventory in SRAM, demonstrating how to implement item-related mechanics.
|
||||
* **Explicit Processor Status Management:** The `AntiKirby_StealItem` routine explicitly uses `REP #$20` and `SEP #$20` to ensure correct 8-bit/16-bit operations when accessing SRAM.
|
||||
* **Conditional Drawing/Flipping:** The `_Draw` routine uses `SprMiscC, X` to conditionally flip the sprite horizontally.
|
||||
@@ -1,290 +0,0 @@
|
||||
# Booki Sprite Analysis
|
||||
|
||||
This document provides a detailed analysis of the `booki.asm` sprite, outlining its properties, core routines, and behavioral patterns.
|
||||
|
||||
## 1. Sprite Properties
|
||||
|
||||
The following `!SPRID` constants define Booki's fundamental characteristics:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_Booki
|
||||
!NbrTiles = 02 ; Number of tiles used in a frame
|
||||
!Harmless = 00 ; 00 = Sprite is Harmful, 01 = Sprite is Harmless
|
||||
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
||||
!Health = 00 ; Number of Health the sprite have (dynamically set in _Prep)
|
||||
!Damage = 00 ; (08 is a whole heart), 04 is half heart
|
||||
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
||||
!ImperviousAll = 00 ; 00 = Can be attack, 01 = attack will clink on it
|
||||
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
||||
!Shadow = 00 ; 00 = don't draw shadow, 01 = draw a shadow
|
||||
!Palette = 00 ; Unused in this template (can be 0 to 7)
|
||||
!Hitbox = 00 ; 00 to 31, can be viewed in sprite draw tool
|
||||
!Persist = 00 ; 01 = your sprite continue to live offscreen
|
||||
!Statis = 00 ; 00 = is sprite is alive?, (kill all enemies room)
|
||||
!CollisionLayer = 00 ; 01 = will check both layer for collision
|
||||
!CanFall = 00 ; 01 sprite can fall in hole, 01 = can't fall
|
||||
!DeflectArrow = 00 ; 01 = deflect arrows
|
||||
!WaterSprite = 00 ; 01 = can only walk shallow water
|
||||
!Blockable = 00 ; 01 = can be blocked by link's shield?
|
||||
!Prize = 00 ; 00-15 = the prize pack the sprite will drop from
|
||||
!Sound = 00 ; 01 = Play different sound when taking damage
|
||||
!Interaction = 00 ; ?? No documentation
|
||||
!Statue = 00 ; 01 = Sprite is statue
|
||||
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
||||
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
||||
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
||||
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
||||
```
|
||||
**Note:** `!Health` and `!Damage` are initially set to `00` but are dynamically determined during initialization.
|
||||
|
||||
## 2. Core Routines
|
||||
|
||||
### 2.1. `Sprite_Booki_Long` (Main Loop)
|
||||
|
||||
This is the primary entry point for Booki's per-frame execution, called by the game engine. It handles drawing, shadow rendering, and then dispatches to the main logic routine if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Booki_Long:
|
||||
{
|
||||
PHB : PHK : PLB ; Set up bank registers
|
||||
JSR Sprite_Booki_Draw ; Call drawing routine
|
||||
JSL Sprite_DrawShadow ; Draw a shadow (if !Shadow is 01)
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive ; Check if sprite is active
|
||||
JSR Sprite_Booki_Main ; If active, run main logic
|
||||
.SpriteIsNotActive
|
||||
PLB ; Restore bank register
|
||||
RTL ; Return from long routine
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. `Sprite_Booki_Prep` (Initialization)
|
||||
|
||||
This routine is executed once when Booki is first spawned. It dynamically sets Booki's health based on Link's current sword level and initializes `SprMiscB`.
|
||||
|
||||
```asm
|
||||
Sprite_Booki_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.l Sword : DEC A : TAY ; Get Link's sword level (0-3), adjust to 0-indexed
|
||||
LDA.w .health, Y : STA.w SprHealth, X ; Set health based on sword level
|
||||
STZ.w SprMiscB, X ; Initialize SprMiscB to 0
|
||||
PLB
|
||||
RTL
|
||||
|
||||
.health ; Health values for each sword level
|
||||
db $04, $08, $10, $18 ; 4, 8, 16, 24 HP
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3. `Sprite_Booki_Main` (Behavioral State Machine)
|
||||
|
||||
This routine manages Booki's AI through a state machine, using `SprAction, X` to determine the current behavior. It utilizes `JumpTableLocal` for efficient state transitions.
|
||||
|
||||
```asm
|
||||
Sprite_Booki_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal ; Jump to the routine specified by SprAction
|
||||
|
||||
dw StalkPlayer ; State 0
|
||||
dw HideFromPlayer ; State 1
|
||||
dw HiddenFromPlayer ; State 2
|
||||
dw ApproachPlayer ; State 3
|
||||
|
||||
StalkPlayer:
|
||||
{
|
||||
%PlayAnimation(0,1,16) ; Animate frames 0-1 every 16 frames
|
||||
JSR Sprite_Booki_Move ; Handle movement
|
||||
RTS
|
||||
}
|
||||
|
||||
HideFromPlayer:
|
||||
{
|
||||
%PlayAnimation(0,4,16) ; Animate frames 0-4 every 16 frames
|
||||
LDA.w SprTimerA, X : BNE + ; Check timer
|
||||
INC.w SprAction, X ; If timer is 0, transition to HiddenFromPlayer
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
HiddenFromPlayer:
|
||||
{
|
||||
%PlayAnimation(4,4,16) ; Animate frame 4 every 16 frames (static)
|
||||
JSR Sprite_Booki_Move ; Handle movement
|
||||
JSL GetRandomInt : AND.b #$03 : BEQ + ; Random chance to transition
|
||||
INC.w SprAction, X ; If random condition met, transition to ApproachPlayer
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
ApproachPlayer:
|
||||
{
|
||||
%PlayAnimation(5,9,16) ; Animate frames 5-9 every 16 frames
|
||||
JSR Sprite_Booki_Move ; Handle movement
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4. `Sprite_Booki_Move` (Movement and Interaction Logic)
|
||||
|
||||
This routine is called by the various states in `Sprite_Booki_Main` to handle Booki's physical interactions and movement. It also manages Booki's "float" behavior (`SlowFloat` or `FloatAway`) based on `SprMiscB`.
|
||||
|
||||
```asm
|
||||
Sprite_Booki_Move:
|
||||
{
|
||||
JSL Sprite_Move ; Apply velocity
|
||||
JSL Sprite_BounceFromTileCollision ; Handle collision with tiles
|
||||
JSL Sprite_PlayerCantPassThrough ; Prevent player from passing through Booki
|
||||
JSL Sprite_DamageFlash_Long ; Handle damage flashing
|
||||
JSL Sprite_CheckIfRecoiling ; Check for recoil state
|
||||
|
||||
JSL Sprite_IsToRightOfPlayer : CPY.b #$01 : BNE .ToRight ; Determine if Booki is to the right of Link
|
||||
LDA.b #$01 : STA.w SprMiscC, X ; Set SprMiscC to 1 (for horizontal flip)
|
||||
JMP .Continue
|
||||
.ToRight
|
||||
STZ.w SprMiscC, X ; Set SprMiscC to 0 (no flip)
|
||||
.Continue
|
||||
|
||||
JSL Sprite_CheckDamageToPlayer ; Check if Booki damages Link
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_damage ; Check if Link damages Booki
|
||||
LDA.b #$01 : STA.w SprMiscB, X ; If damaged, set SprMiscB to 1 (FloatAway state)
|
||||
.no_damage
|
||||
|
||||
LDA.w SprMiscB, X
|
||||
JSL JumpTableLocal ; Jump to movement routine based on SprMiscB
|
||||
|
||||
dw SlowFloat ; SprMiscB = 0
|
||||
dw FloatAway ; SprMiscB = 1
|
||||
|
||||
SlowFloat:
|
||||
{
|
||||
LDY #$04
|
||||
JSL GetRandomInt : AND.b #$04 ; Introduce some randomness to movement
|
||||
JSL Sprite_FloatTowardPlayer ; Float towards Link
|
||||
|
||||
PHX
|
||||
JSL Sprite_DirectionToFacePlayer ; Update facing direction
|
||||
; Check if too close to player
|
||||
LDA.b $0E : CMP.b #$1A : BCS .NotTooClose
|
||||
LDA.b $0F : CMP.b #$1A : BCS .NotTooClose
|
||||
LDA.b #$01 : STA.w SprMiscB, X ; If too close, switch to FloatAway
|
||||
LDA.b #$20 : STA.w SprTimerA, X ; Set timer
|
||||
%GotoAction(1) ; Transition to HideFromPlayer state
|
||||
.NotTooClose
|
||||
PLX
|
||||
|
||||
RTS
|
||||
}
|
||||
|
||||
FloatAway:
|
||||
{
|
||||
JSL GetRandomInt : AND.b #$04 ; Introduce some randomness to movement
|
||||
JSL Sprite_FloatAwayFromPlayer ; Float away from Link
|
||||
|
||||
PHX
|
||||
JSL Sprite_DirectionToFacePlayer ; Update facing direction
|
||||
; Check if far enough from player
|
||||
LDA.b $0E : CMP.b #$1B : BCC .NotTooClose
|
||||
LDA.b #$1B : CMP.b $0F : BCC .NotTooClose ; Corrected comparison for $0F
|
||||
LDA.b #$00 : STA.w SprMiscB, X ; If far enough, switch to SlowFloat
|
||||
%GotoAction(0) ; Transition to StalkPlayer state
|
||||
.NotTooClose
|
||||
PLX
|
||||
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5. `Sprite_Booki_Draw` (Drawing Routine)
|
||||
|
||||
This routine is responsible for rendering Booki's graphics. It uses a custom OAM (Object Attribute Memory) allocation and manipulation logic rather than the `%DrawSprite()` macro. It dynamically determines the animation frame and applies horizontal flipping based on `SprMiscC`.
|
||||
|
||||
```asm
|
||||
Sprite_Booki_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord ; Prepare OAM coordinates
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer ; Allocate OAM slots, deferring to player
|
||||
|
||||
LDA.w SprGfx, X : CLC : ADC.w SprFrame, X : TAY ; Calculate animation frame index
|
||||
LDA .start_index, Y : STA $06 ; Store start index for tiles
|
||||
|
||||
LDA.w SprFlash, X : STA $08 ; Store flash status
|
||||
LDA.w SprMiscC, X : STA $09 ; Store horizontal flip status (0 or 1)
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ; Load number of tiles for current frame (minus 1)
|
||||
LDY.b #$00 ; Initialize Y for OAM buffer offset
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
PHA ; Keep the value with animation index offset
|
||||
|
||||
ASL A : TAX ; Multiply by 2 for word access
|
||||
|
||||
REP #$20 ; Set A to 16-bit mode
|
||||
|
||||
LDA $00 : STA ($90), Y ; Store Y-coordinate
|
||||
AND.w #$0100 : STA $0E ; Check if Y-coord is off-screen (high bit)
|
||||
INY
|
||||
LDA $02 : STA ($90), Y ; Store X-coordinate
|
||||
CLC : ADC #$0010 : CMP.w #$0100 ; Check if X-coord is off-screen
|
||||
SEP #$20 ; Set A to 8-bit mode
|
||||
BCC .on_screen_y ; If on screen, continue
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ; If off-screen, move sprite off-screen
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Restore Tile Index
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y ; Store character (tile) number
|
||||
INY
|
||||
|
||||
LDA.b $09 : BEQ .ToRight ; Check SprMiscC for horizontal flip
|
||||
LDA.b #$29 : JMP .Prop ; If 1, use properties for flipped
|
||||
.ToRight
|
||||
LDA.b #$69 ; If 0, use properties for normal
|
||||
.Prop
|
||||
ORA $08 : STA ($90), Y ; Apply flash and store OAM properties
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY ; Calculate OAM buffer index for size
|
||||
LDA.b #$02 : ORA $0F : STA ($92), Y ; Store size (16x16) in OAM buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile ; Loop for next tile
|
||||
|
||||
PLX ; Restore X (sprite index)
|
||||
|
||||
RTS
|
||||
|
||||
; =========================================================
|
||||
; OAM Data Tables
|
||||
.start_index
|
||||
db $00, $01, $02, $03, $04, $05, $06, $07, $08, $09
|
||||
.nbr_of_tiles
|
||||
db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; All frames use 1 tile (0-indexed)
|
||||
.chr
|
||||
db $0E, $0C, $0A, $2C, $2E, $2E, $0A, $2C, $0C, $0E ; Tile numbers for each frame
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Key Behaviors and Implementation Details
|
||||
|
||||
* **Dynamic Health:** Booki's health is not a fixed property but is determined at spawn time based on Link's current sword level. This allows for dynamic difficulty scaling.
|
||||
* **State Management:** Booki employs a robust state machine using `SprAction, X` and `JumpTableLocal` to manage its behaviors: `StalkPlayer`, `HideFromPlayer`, `HiddenFromPlayer`, and `ApproachPlayer`. Transitions between these states are triggered by timers, random chance, or player proximity.
|
||||
* **Player Interaction:**
|
||||
* **Stalking/Approaching:** Booki uses `Sprite_FloatTowardPlayer` to move towards Link.
|
||||
* **Hiding/Floating Away:** Booki uses `Sprite_FloatAwayFromPlayer` to retreat from Link, often triggered by taking damage or getting too close.
|
||||
* **Damage:** Booki can damage Link on contact (`Sprite_CheckDamageToPlayer`) and reacts to damage from Link by transitioning to a `FloatAway` state.
|
||||
* **Directional Facing:** `SprMiscC, X` is used as a flag to control horizontal flipping in the drawing routine, ensuring Booki always faces Link.
|
||||
* **Custom OAM Drawing:** Unlike many sprites that might use the `%DrawSprite()` macro, Booki implements its OAM drawing logic directly. This provides fine-grained control over its appearance, including dynamic tile selection and horizontal flipping. The `REP #$20` and `SEP #$20` instructions are used to temporarily switch the accumulator to 16-bit mode for coordinate calculations, demonstrating careful management of the Processor Status Register.
|
||||
* **Randomness:** `GetRandomInt` is used to introduce variability in Booki's movement patterns and state transitions, making its behavior less predictable.
|
||||
* **`SprMiscB` Usage:** This variable acts as a sub-state for movement, toggling between `SlowFloat` (approaching) and `FloatAway` (retreating) behaviors.
|
||||
* **`SprTimerA` Usage:** Used in the `HideFromPlayer` state to control how long Booki remains in that state before transitioning.
|
||||
* **`Sprite_PlayerCantPassThrough`:** Ensures Booki acts as a solid object that Link cannot simply walk through.
|
||||
@@ -1,122 +0,0 @@
|
||||
# Business Scrub
|
||||
|
||||
## Overview
|
||||
The Business Scrub is a custom enemy sprite that likely overrides a vanilla sprite ID. It is characterized by its low health and harmless contact damage, suggesting its primary threat comes from projectiles or other interactions defined within its main logic.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `$00` (Vanilla sprite ID, likely overridden)
|
||||
* **`!NbrTiles`**: `$02`
|
||||
* **`!Health`**: `$01`
|
||||
* **`!Damage`**: `$00` (Harmless contact)
|
||||
* **`!Harmless`**: `$00`
|
||||
* **`!Hitbox`**: `$08`
|
||||
* **`!ImperviousAll`**: `$00`
|
||||
* **`!Statue`**: `$00`
|
||||
* **`!Prize`**: `$00`
|
||||
* **`!Boss`**: `$00`
|
||||
* **Collision Properties**: All collision-related properties (`!Defl`, `!SprColl`, etc.) are set to `$00`, indicating that direct contact damage and knockback are not handled by these properties. Interaction and damage are likely managed within the sprite's main logic.
|
||||
|
||||
## Main Structure (`Sprite_BusinessScrub_Long`)
|
||||
This routine is the main entry point for the Business Scrub, executed every frame. It sets up bank registers, calls the drawing routine, and then executes the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_BusinessScrub_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_BusinessScrub_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_BusinessScrub_Main
|
||||
.SpriteIsNotActive
|
||||
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_BusinessScrub_Prep`)
|
||||
This routine runs once when the Business Scrub is spawned. It initializes the sprite's action state to `0` and sets a general-purpose timer (`SprTimerA`) to `120` frames (2 seconds).
|
||||
|
||||
```asm
|
||||
Sprite_BusinessScrub_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
%GotoAction(0)
|
||||
%SetTimerA(120)
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_BusinessScrub_Main`)
|
||||
The core behavior of the Business Scrub is managed by a state machine using `%SpriteJumpTable`. The current states are:
|
||||
|
||||
* **`State_Idle`**: The initial state where the scrub is idle. It plays an animation and checks for player proximity. If Link is within 80 pixels, it transitions to `State_Attacking`.
|
||||
* **`State_Attacking`**: In this state, the scrub plays an attack animation, moves towards the player, and deals damage on contact. It also checks if it has been hit by the player and transitions to `State_Hurt` if so.
|
||||
* **`State_Hurt`**: This state handles the sprite being hit, causing it to flash and be knocked back.
|
||||
|
||||
```asm
|
||||
Sprite_BusinessScrub_Main:
|
||||
{
|
||||
%SpriteJumpTable(State_Idle, State_Attacking, State_Hurt)
|
||||
|
||||
State_Idle:
|
||||
{
|
||||
%PlayAnimation(0, 1, 15)
|
||||
|
||||
JSL GetDistance8bit_Long : CMP.b #$50 : BCS .player_is_far
|
||||
%GotoAction(1)
|
||||
.player_is_far
|
||||
RTS
|
||||
}
|
||||
|
||||
State_Attacking:
|
||||
{
|
||||
%PlayAnimation(2, 3, 8)
|
||||
%MoveTowardPlayer(12)
|
||||
%DoDamageToPlayerSameLayerOnContact()
|
||||
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_damage
|
||||
%GotoAction(2)
|
||||
.no_damage
|
||||
RTS
|
||||
}
|
||||
|
||||
State_Hurt:
|
||||
{
|
||||
JSL Sprite_DamageFlash_Long
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_BusinessScrub_Draw`)
|
||||
The drawing routine uses the `%DrawSprite()` macro to render the sprite's graphics based on defined OAM data tables.
|
||||
|
||||
```asm
|
||||
Sprite_BusinessScrub_Draw:
|
||||
{
|
||||
%DrawSprite()
|
||||
|
||||
.start_index
|
||||
db $00, $02, $04, $06
|
||||
.nbr_of_tiles
|
||||
db 1, 1, 1, 1
|
||||
|
||||
.x_offsets
|
||||
dw -8, 8, -8, 8, -8, 8, -8, 8
|
||||
.y_offsets
|
||||
dw -8, -8, -8, -8, -8, -8, -8, -8
|
||||
.chr
|
||||
db $C0, $C2, $C4, $C6, $C8, $CA, $CC, $CE
|
||||
.properties
|
||||
db $3B, $7B, $3B, $7B, $3B, $7B, $3B, $7B
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **State Machine**: Utilizes a clear state machine (`%SpriteJumpTable`) for managing different behaviors (Idle, Attacking, Hurt).
|
||||
* **Player Interaction**: Incorporates distance checks (`GetDistance8bit_Long`) to trigger state changes and direct damage on contact (`DoDamageToPlayerSameLayerOnContact`).
|
||||
* **Damage Handling**: Includes a basic damage reaction (`Sprite_CheckDamageFromPlayer`, `Sprite_DamageFlash_Long`).
|
||||
* **Vanilla ID Override**: The use of `!SPRID = $00` suggests this sprite is intended to replace or modify the behavior of a vanilla sprite with ID $00.
|
||||
@@ -1,328 +0,0 @@
|
||||
# Darknut Sprite Analysis
|
||||
|
||||
This document provides a detailed analysis of the `darknut.asm` sprite, outlining its properties, core routines, and behavioral patterns.
|
||||
|
||||
## 1. Sprite Properties
|
||||
|
||||
The following `!SPRID` constants define Darknut's fundamental characteristics:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_Darknut
|
||||
!NbrTiles = 03 ; Number of tiles used in a frame
|
||||
!Harmless = 00 ; 00 = Sprite is Harmful, 01 = Sprite is Harmless
|
||||
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
||||
!Health = 12 ; Number of Health the sprite have (dynamically set in _Prep)
|
||||
!Damage = 00 ; (08 is a whole heart), 04 is half heart
|
||||
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
||||
!ImperviousAll = 00 ; 00 = Can be attack, 01 = attack will clink on it
|
||||
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
||||
!Shadow = 00 ; 00 = don't draw shadow, 01 = draw a shadow
|
||||
!Palette = 00 ; Unused in this template (can be 0 to 7)
|
||||
!Hitbox = 12 ; 00 to 31, can be viewed in sprite draw tool
|
||||
!Persist = 00 ; 01 = your sprite continue to live offscreen
|
||||
!Statis = 00 ; 00 = is sprite is alive?, (kill all enemies room)
|
||||
!CollisionLayer = 00 ; 01 = will check both layer for collision
|
||||
!CanFall = 00 ; 01 sprite can fall in hole, 01 = can't fall
|
||||
!DeflectArrow = 00 ; 01 = deflect arrows
|
||||
!WaterSprite = 00 ; 01 = can only walk shallow water
|
||||
!Blockable = 00 ; 01 = can be blocked by link's shield?
|
||||
!Prize = 00 ; 00-15 = the prize pack the sprite will drop from
|
||||
!Sound = 00 ; 01 = Play different sound when taking damage
|
||||
!Interaction = 00 ; ?? No documentation
|
||||
!Statue = 00 ; 01 = Sprite is statue
|
||||
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
||||
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
||||
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
||||
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
||||
```
|
||||
**Note:** `!Health` is initially set to `12` but is dynamically determined during initialization based on Link's sword level. `!Damage` is `00`, implying damage is handled through other means (e.g., contact with Link's sword).
|
||||
|
||||
## 2. Core Routines
|
||||
|
||||
### 2.1. `Sprite_Darknut_Long` (Main Loop)
|
||||
|
||||
This is the primary entry point for Darknut's per-frame execution, called by the game engine. It handles drawing, shadow rendering, and then dispatches to the main logic routine if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Darknut_Long:
|
||||
{
|
||||
PHB : PHK : PLB ; Set up bank registers
|
||||
JSR Sprite_Darknut_Draw ; Call drawing routine
|
||||
JSL Sprite_DrawShadow ; Draw a shadow (if !Shadow is 01)
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive ; Check if sprite is active
|
||||
JSR Sprite_Darknut_Main ; If active, run main logic
|
||||
.SpriteIsNotActive
|
||||
PLB ; Restore bank register
|
||||
RTL ; Return from long routine
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. `Sprite_Darknut_Prep` (Initialization)
|
||||
|
||||
This routine is executed once when Darknut is first spawned. It dynamically sets Darknut's health based on Link's current sword level, initializes `SprDefl` (deflection timer), and `SprTileDie` (tile for death animation).
|
||||
|
||||
```asm
|
||||
Sprite_Darknut_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.l $7EF359 : TAY ; Get Link's sword level (0-3), adjust to 0-indexed
|
||||
LDA.w .health, Y : STA.w SprHealth, X ; Set health based on sword level
|
||||
LDA.b #$80 : STA.w SprDefl, X ; Initialize deflection timer
|
||||
LDA.b #%01100000 : STA.w SprTileDie, X ; Set tile for death animation
|
||||
PLB
|
||||
RTL
|
||||
|
||||
.health ; Health values for each sword level
|
||||
db $04, $06, $08, $0A ; 4, 6, 8, 10 HP
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3. `Sprite_Darknut_Main` (Behavioral Logic)
|
||||
|
||||
This routine manages Darknut's AI, including probe spawning, parrying, movement, and animation. It uses `SprAction, X` to control its facing direction and animation.
|
||||
|
||||
```asm
|
||||
Sprite_Darknut_Main:
|
||||
{
|
||||
JSL GetDistance8bit_Long : CMP.b #$80 : BCS .no_probe ; Check distance to Link
|
||||
JSL Sprite_SpawnProbeAlways_long ; If close, spawn a probe
|
||||
.no_probe
|
||||
|
||||
JSL Guard_ParrySwordAttacks ; Handle parrying Link's sword attacks
|
||||
|
||||
JSL Sprite_Move ; Apply velocity
|
||||
JSL Sprite_BounceFromTileCollision ; Handle collision with tiles
|
||||
JSL Sprite_DamageFlash_Long ; Handle damage flashing
|
||||
|
||||
JSL Sprite_CheckIfRecoiling ; Check for recoil state
|
||||
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_dano ; Check if Link damages Darknut
|
||||
LDA.b #$FF : STA.w SprTimerD, X ; If damaged, set timer D
|
||||
.no_dano
|
||||
|
||||
LDA.w SprTimerA, X : BEQ + ; Check timer A
|
||||
LDA.b #$90 : STA.w SprTimerD, X ; If timer A is not 0, set timer D
|
||||
+
|
||||
LDA.w SprTimerD, X : BEQ ++ ; Check timer D
|
||||
LDA.b #$08 : JSL Sprite_ApplySpeedTowardsPlayer ; Apply speed towards Link
|
||||
JSL Sprite_DirectionToFacePlayer ; Update facing direction
|
||||
TYA
|
||||
STA.w SprMiscC, X ; Store facing direction in SprMiscC
|
||||
STA.w SprMiscE, X ; Store facing direction in SprMiscE
|
||||
STA.w SprAction, X ; Set SprAction to facing direction
|
||||
JSL Guard_ChaseLinkOnOneAxis ; Chase Link along one axis
|
||||
JMP +++
|
||||
++
|
||||
JSR Sprite_Darknut_BasicMove ; If no timers, use basic movement
|
||||
+++
|
||||
|
||||
JSR Goriya_HandleTileCollision ; Handle tile collision (specific to Goriya, but used here)
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal ; Jump to animation routine based on SprAction
|
||||
|
||||
dw FaceRight
|
||||
dw FaceLeft
|
||||
dw FaceDown
|
||||
dw FaceUp
|
||||
|
||||
FaceUp:
|
||||
{
|
||||
%PlayAnimation(0,1,10) ; Animate frames 0-1 every 10 frames
|
||||
RTS
|
||||
}
|
||||
|
||||
FaceDown:
|
||||
{
|
||||
%StartOnFrame(2)
|
||||
%PlayAnimation(2,3,10) ; Animate frames 2-3 every 10 frames
|
||||
RTS
|
||||
}
|
||||
|
||||
FaceLeft:
|
||||
{
|
||||
%StartOnFrame(4)
|
||||
%PlayAnimation(4,5,10) ; Animate frames 4-5 every 10 frames
|
||||
RTS
|
||||
}
|
||||
|
||||
FaceRight:
|
||||
{
|
||||
%StartOnFrame(6)
|
||||
%PlayAnimation(6,7,10) ; Animate frames 6-7 every 10 frames
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4. `Sprite_Darknut_BasicMove` (Basic Movement Logic)
|
||||
|
||||
This routine defines Darknut's basic movement patterns, which are executed when no special conditions (like being damaged or timers) are active. It uses `SprAction, X` to determine the current movement direction.
|
||||
|
||||
```asm
|
||||
Sprite_Darknut_BasicMove:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal ; Jump to movement routine based on SprAction
|
||||
|
||||
dw MoveRight
|
||||
dw MoveLeft
|
||||
dw MoveDown
|
||||
dw MoveUp
|
||||
|
||||
MoveUp:
|
||||
{
|
||||
LDA.b #-DarknutSpeed : STA.w SprYSpeed, X ; Set Y-speed to negative (move up)
|
||||
STZ.w SprXSpeed, X ; Clear X-speed
|
||||
RTS
|
||||
}
|
||||
|
||||
MoveDown:
|
||||
{
|
||||
LDA.b #DarknutSpeed : STA.w SprYSpeed, X ; Set Y-speed to positive (move down)
|
||||
STZ.w SprXSpeed, X ; Clear X-speed
|
||||
RTS
|
||||
}
|
||||
|
||||
MoveLeft:
|
||||
{
|
||||
LDA.b #-DarknutSpeed : STA.w SprXSpeed, X ; Set X-speed to negative (move left)
|
||||
STZ.w SprYSpeed, X ; Clear Y-speed
|
||||
RTS
|
||||
}
|
||||
|
||||
MoveRight:
|
||||
{
|
||||
LDA.b #DarknutSpeed : STA.w SprXSpeed, X ; Set X-speed to positive (move right)
|
||||
STZ.w SprYSpeed, X ; Clear Y-speed
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5. `Sprite_Darknut_Draw` (Drawing Routine)
|
||||
|
||||
This routine is responsible for rendering Darknut's graphics. It uses a custom OAM (Object Attribute Memory) allocation and manipulation logic, similar to Booki, to handle its multi-tile appearance and animation. It dynamically determines the animation frame and applies offsets for each tile.
|
||||
|
||||
```asm
|
||||
Sprite_Darknut_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord ; Prepare OAM coordinates
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer ; Allocate OAM slots, deferring to player
|
||||
|
||||
LDA.w SprGfx, X : CLC : ADC $0D90, X : TAY ; Calculate animation frame index
|
||||
LDA .start_index, Y : STA $06 ; Store start index for tiles
|
||||
LDA.w SprFlash, X : STA $08 ; Store flash status
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ; Load number of tiles for current frame (minus 1)
|
||||
LDY.b #$00 ; Initialize Y for OAM buffer offset
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index
|
||||
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset
|
||||
|
||||
ASL A : TAX ; Multiply by 2 for word access
|
||||
|
||||
REP #$20 ; Set A to 16-bit mode
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y ; Store Y-coordinate with X-offset
|
||||
AND.w #$0100 : STA $0E ; Check if Y-coord is off-screen (high bit)
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y ; Store X-coordinate with Y-offset
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20 ; Set A to 8-bit mode
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ; If off-screen, move sprite off-screen
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Restore Tile Index
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y ; Store character (tile) number
|
||||
INY
|
||||
LDA .properties, X : ORA $08 : STA ($90), Y ; Apply flash and store OAM properties
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY ; Calculate OAM buffer index for size
|
||||
|
||||
LDA.w .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile ; Loop for next tile
|
||||
|
||||
PLX ; Restore X (sprite index)
|
||||
|
||||
RTS
|
||||
|
||||
; =========================================================
|
||||
; OAM Data Tables
|
||||
.start_index
|
||||
db $00, $03, $06, $09, $0C, $0E, $10, $12
|
||||
.nbr_of_tiles
|
||||
db 2, 2, 2, 2, 1, 1, 1, 1
|
||||
.x_offsets
|
||||
dw 0, 0, 0
|
||||
dw 0, 0, 0
|
||||
dw 0, 0, 0
|
||||
dw 0, 0, 0
|
||||
dw 0, -12
|
||||
dw 0, -12
|
||||
dw 0, 12
|
||||
dw 0, 12
|
||||
.y_offsets
|
||||
dw -4, 0, -12
|
||||
dw -4, 0, -12
|
||||
dw 0, 12, 20
|
||||
dw 0, 12, 20
|
||||
dw 0, 8
|
||||
dw 0, 8
|
||||
dw 0, 8
|
||||
dw 0, 8
|
||||
.chr
|
||||
db $EF, $E6, $FF
|
||||
db $EF, $E6, $FF
|
||||
db $E2, $EF, $FF
|
||||
db $E2, $EF, $FF
|
||||
db $E0, $E8
|
||||
db $E4, $E8
|
||||
db $E0, $E8
|
||||
db $E4, $E8
|
||||
.properties
|
||||
db $B9, $39, $B9
|
||||
db $B9, $79, $B9
|
||||
db $39, $39, $39
|
||||
db $79, $39, $39
|
||||
db $39, $79
|
||||
db $39, $79
|
||||
db $79, $39
|
||||
db $79, $39
|
||||
.sizes
|
||||
db $00, $02, $00
|
||||
db $00, $02, $00
|
||||
db $02, $00, $00
|
||||
db $02, $00, $00
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Key Behaviors and Implementation Details
|
||||
|
||||
* **Dynamic Health:** Darknut's health is determined at spawn time based on Link's current sword level, similar to Booki, allowing for dynamic difficulty scaling.
|
||||
* **Probe Spawning:** Darknut has the ability to spawn a probe (`Sprite_SpawnProbeAlways_long`) when Link is within a certain distance, adding a ranged attack or detection mechanism.
|
||||
* **Parrying Mechanics:** The `Guard_ParrySwordAttacks` routine suggests Darknut can actively defend against Link's sword attacks, potentially deflecting them or becoming temporarily invulnerable.
|
||||
* **Chasing on One Axis:** When damaged or under certain timer conditions, Darknut uses `Guard_ChaseLinkOnOneAxis` to pursue Link along either the horizontal or vertical axis, making its movement more predictable but still challenging.
|
||||
* **Basic Movement:** Darknut has a set of basic directional movements (`MoveUp`, `MoveDown`, `MoveLeft`, `MoveRight`) that it cycles through when not actively chasing or reacting to damage.
|
||||
* **Custom OAM Drawing:** Darknut utilizes a custom OAM drawing routine, similar to Booki, to handle its multi-tile sprite. This routine precisely positions and animates multiple 8x8 tiles to form the larger Darknut sprite. The use of `REP #$20` and `SEP #$20` for 16-bit coordinate calculations is also present here.
|
||||
* **`SprDefl` and `SprTileDie`:** `SprDefl` is used as a deflection timer, likely related to the parrying mechanic. `SprTileDie` specifies a custom tile to be used during its death animation.
|
||||
* **`SprMiscC` and `SprMiscE`:** These variables are used to store Darknut's facing direction, which influences both its movement and animation. `SprMiscC` is likely used for animation frame selection, while `SprMiscE` might be used for other directional logic.
|
||||
* **`Goriya_HandleTileCollision`:** The use of a collision handler named `Goriya_HandleTileCollision` suggests code reuse from another sprite, indicating a shared collision logic for certain enemy types.
|
||||
@@ -1,122 +0,0 @@
|
||||
# Eon Scrub
|
||||
|
||||
## Overview
|
||||
The Eon Scrub is a custom enemy sprite, likely a variation of the Business Scrub, that overrides a vanilla sprite ID. It shares similar characteristics with the Business Scrub, including low health and harmless contact damage, implying its primary threat comes from projectiles or other interactions defined within its main logic.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `$00` (Vanilla sprite ID, likely overridden)
|
||||
* **`!NbrTiles`**: `$02`
|
||||
* **`!Health`**: `$01`
|
||||
* **`!Damage`**: `$00` (Harmless contact)
|
||||
* **`!Harmless`**: `$00`
|
||||
* **`!Hitbox`**: `$08`
|
||||
* **`!ImperviousAll`**: `$00`
|
||||
* **`!Statue`**: `$00`
|
||||
* **`!Prize`**: `$00`
|
||||
* **`!Boss`**: `$00`
|
||||
* **Collision Properties**: All collision-related properties (`!Defl`, `!SprColl`, etc.) are set to `$00`, indicating that direct contact damage and knockback are not handled by these properties. Interaction and damage are likely managed within the sprite's main logic.
|
||||
|
||||
## Main Structure (`Sprite_EonScrub_Long`)
|
||||
This routine is the main entry point for the Eon Scrub, executed every frame. It sets up bank registers, calls the drawing routine, and then executes the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_EonScrub_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_EonScrub_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_EonScrub_Main
|
||||
.SpriteIsNotActive
|
||||
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_EonScrub_Prep`)
|
||||
This routine runs once when the Eon Scrub is spawned. It initializes the sprite's action state to `0` and sets a general-purpose timer (`SprTimerA`) to `120` frames (2 seconds).
|
||||
|
||||
```asm
|
||||
Sprite_EonScrub_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
%GotoAction(0)
|
||||
%SetTimerA(120)
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_EonScrub_Main`)
|
||||
The core behavior of the Eon Scrub is managed by a state machine using `%SpriteJumpTable`. The current states are:
|
||||
|
||||
* **`State_Idle`**: The initial state where the scrub is idle. It plays an animation and checks for player proximity. If Link is within 80 pixels, it transitions to `State_Attacking`.
|
||||
* **`State_Attacking`**: In this state, the scrub plays an attack animation, moves towards the player, and deals damage on contact. It also checks if it has been hit by the player and transitions to `State_Hurt` if so.
|
||||
* **`State_Hurt`**: This state handles the sprite being hit, causing it to flash and be knocked back.
|
||||
|
||||
```asm
|
||||
Sprite_EonScrub_Main:
|
||||
{
|
||||
%SpriteJumpTable(State_Idle, State_Attacking, State_Hurt)
|
||||
|
||||
State_Idle:
|
||||
{
|
||||
%PlayAnimation(0, 1, 15)
|
||||
|
||||
JSL GetDistance8bit_Long : CMP.b #$50 : BCS .player_is_far
|
||||
%GotoAction(1)
|
||||
.player_is_far
|
||||
RTS
|
||||
}
|
||||
|
||||
State_Attacking:
|
||||
{
|
||||
%PlayAnimation(2, 3, 8)
|
||||
%MoveTowardPlayer(12)
|
||||
%DoDamageToPlayerSameLayerOnContact()
|
||||
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_damage
|
||||
%GotoAction(2)
|
||||
.no_damage
|
||||
RTS
|
||||
}
|
||||
|
||||
State_Hurt:
|
||||
{
|
||||
JSL Sprite_DamageFlash_Long
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_EonScrub_Draw`)
|
||||
The drawing routine uses the `%DrawSprite()` macro to render the sprite's graphics based on defined OAM data tables.
|
||||
|
||||
```asm
|
||||
Sprite_EonScrub_Draw:
|
||||
{
|
||||
%DrawSprite()
|
||||
|
||||
.start_index
|
||||
db $00, $02, $04, $06
|
||||
.nbr_of_tiles
|
||||
db 1, 1, 1, 1
|
||||
|
||||
.x_offsets
|
||||
dw -8, 8, -8, 8, -8, 8, -8, 8
|
||||
.y_offsets
|
||||
dw -8, -8, -8, -8, -8, -8, -8, -8
|
||||
.chr
|
||||
db $C0, $C2, $C4, $C6, $C8, $CA, $CC, $CE
|
||||
.properties
|
||||
db $3B, $7B, $3B, $7B, $3B, $7B, $3B, $7B
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **State Machine**: Utilizes a clear state machine (`%SpriteJumpTable`) for managing different behaviors (Idle, Attacking, Hurt).
|
||||
* **Player Interaction**: Incorporates distance checks (`GetDistance8bit_Long`) to trigger state changes and direct damage on contact (`DoDamageToPlayerSameLayerOnContact`).
|
||||
* **Damage Handling**: Includes a basic damage reaction (`Sprite_CheckDamageFromPlayer`, `Sprite_DamageFlash_Long`).
|
||||
* **Vanilla ID Override**: The use of `!SPRID = $00` suggests this sprite is intended to replace or modify the behavior of a vanilla sprite with ID $00.
|
||||
@@ -1,469 +0,0 @@
|
||||
# Goriya Sprite Analysis
|
||||
|
||||
This document provides a detailed analysis of the `goriya.asm` sprite, outlining its properties, core routines, and behavioral patterns.
|
||||
|
||||
## 1. Sprite Properties
|
||||
|
||||
The following `!SPRID` constants define Goriya's fundamental characteristics:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_Goriya
|
||||
!NbrTiles = 03 ; Number of tiles used in a frame
|
||||
!Harmless = 00 ; 00 = Sprite is Harmful, 01 = Sprite is Harmless
|
||||
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
||||
!Health = 00 ; Number of Health the sprite have (dynamically set in _Prep)
|
||||
!Damage = 00 ; (08 is a whole heart), 04 is half heart
|
||||
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
||||
!ImperviousAll = 00 ; 00 = Can be attack, 01 = attack will clink on it
|
||||
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
||||
!Shadow = 00 ; 00 = don't draw shadow, 01 = draw a shadow
|
||||
!Palette = 00 ; Unused in this template (can be 0 to 7)
|
||||
!Hitbox = 00 ; 00 to 31, can be viewed in sprite draw tool
|
||||
!Persist = 00 ; 01 = your sprite continue to live offscreen
|
||||
!Statis = 00 ; 00 = is sprite is alive?, (kill all enemies room)
|
||||
!CollisionLayer = 00 ; 01 = will check both layer for collision
|
||||
!CanFall = 00 ; 01 sprite can fall in hole, 01 = can't fall
|
||||
!DeflectArrow = 00 ; 01 = deflect arrows
|
||||
!WaterSprite = 00 ; 01 = can only walk shallow water
|
||||
!Blockable = 00 ; 01 = can be blocked by link's shield?
|
||||
!Prize = 00 ; 00-15 = the prize pack the sprite will drop from
|
||||
!Sound = 00 ; 01 = Play different sound when taking damage
|
||||
!Interaction = 00 ; ?? No documentation
|
||||
!Statue = 00 ; 01 = Sprite is statue
|
||||
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
||||
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
||||
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
||||
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
||||
```
|
||||
**Note:** `!Health` and `!Damage` are initially set to `00` but are dynamically determined during initialization.
|
||||
|
||||
## 2. Core Routines
|
||||
|
||||
### 2.1. `Sprite_Goriya_Long` (Main Loop)
|
||||
|
||||
This is the primary entry point for Goriya's per-frame execution, called by the game engine. It handles drawing, shadow rendering, and then dispatches to the main logic routine if the sprite is active. Notably, it checks `SprSubtype, X` to determine if it's the main Goriya sprite or a boomerang, and calls the appropriate drawing routine.
|
||||
|
||||
```asm
|
||||
Sprite_Goriya_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.w SprSubtype, X : BEQ + ; Check SprSubtype
|
||||
JSR Sprite_Boomerang_Draw ; If SprSubtype is not 0, draw as boomerang
|
||||
JMP ++
|
||||
+
|
||||
JSR Sprite_Goriya_Draw ; If SprSubtype is 0, draw as Goriya
|
||||
JSL Sprite_DrawShadow
|
||||
++
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive ; Check if sprite is active
|
||||
JSR Sprite_Goriya_Main ; If active, run main logic
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. `Sprite_Goriya_Prep` (Initialization)
|
||||
|
||||
This routine is executed once when Goriya is first spawned. It sets Goriya's health to `08` (one heart).
|
||||
|
||||
```asm
|
||||
Sprite_Goriya_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$08 : STA.w SprHealth, X ; Set health to 8
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3. `Sprite_Goriya_Main` (Behavioral State Machine)
|
||||
|
||||
This routine manages Goriya's AI through a state machine, using `SprAction, X` to determine the current behavior. It utilizes `JumpTableLocal` for efficient state transitions, including walking in different directions and a boomerang attack state.
|
||||
|
||||
```asm
|
||||
Sprite_Goriya_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal ; Jump to the routine specified by SprAction
|
||||
|
||||
dw Goriya_WalkingUp
|
||||
dw Goriya_WalkingDown
|
||||
dw Goriya_WalkingLeft
|
||||
dw Goriya_WalkingRight
|
||||
dw BoomerangAttack
|
||||
|
||||
Goriya_WalkingUp:
|
||||
{
|
||||
%PlayAnimation(0, 1, 10) ; Animate frames 0-1 every 10 frames
|
||||
JSR Sprite_Goriya_Move ; Handle movement
|
||||
RTS
|
||||
}
|
||||
|
||||
Goriya_WalkingDown:
|
||||
{
|
||||
%PlayAnimation(2, 3, 10) ; Animate frames 2-3 every 10 frames
|
||||
JSR Sprite_Goriya_Move ; Handle movement
|
||||
RTS
|
||||
}
|
||||
|
||||
Goriya_WalkingLeft:
|
||||
{
|
||||
%StartOnFrame(4)
|
||||
%PlayAnimation(4, 5, 10) ; Animate frames 4-5 every 10 frames
|
||||
JSR Sprite_Goriya_Move ; Handle movement
|
||||
RTS
|
||||
}
|
||||
|
||||
Goriya_WalkingRight:
|
||||
{
|
||||
%StartOnFrame(6)
|
||||
%PlayAnimation(6, 7, 10) ; Animate frames 6-7 every 10 frames
|
||||
JSR Sprite_Goriya_Move ; Handle movement
|
||||
RTS
|
||||
}
|
||||
|
||||
BoomerangAttack:
|
||||
{
|
||||
%PlayAnimation(0, 3, 6) ; Animate frames 0-3 every 6 frames
|
||||
|
||||
LDA.w SprTimerD, X : BNE + ; Check timer D
|
||||
LDA.b #$16
|
||||
JSL Sprite_ApplySpeedTowardsPlayer ; Apply speed towards Link
|
||||
%SetTimerD($50) ; Set timer D
|
||||
+
|
||||
|
||||
JSL Sprite_Move ; Apply velocity
|
||||
JSL Sprite_SpawnSparkleGarnish ; Spawn sparkle effect
|
||||
|
||||
JSL Sprite_CheckDamageToPlayer : BCC .no_dano ; Check if Goriya damages Link
|
||||
LDA.b #$FF : STA.w SprTimerD, X ; If damaged, set timer D
|
||||
JSL Sprite_InvertSpeed_XY ; Invert speed
|
||||
.no_dano
|
||||
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC + ; Check if Link damages Goriya
|
||||
JSL Sprite_InvertSpeed_XY ; If damaged, invert speed
|
||||
+
|
||||
|
||||
JSL Sprite_CheckTileCollision ; Check for tile collision
|
||||
LDA.w SprCollision, X : BEQ + ; If no collision
|
||||
STZ.w SprState, X ; Clear sprite state (despawn?)
|
||||
+
|
||||
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4. `Sprite_Goriya_Move` (Movement and Interaction Logic)
|
||||
|
||||
This routine is called by the various walking states in `Sprite_Goriya_Main` to handle Goriya's physical interactions and movement. It also manages the logic for throwing a boomerang and changing movement directions.
|
||||
|
||||
```asm
|
||||
Sprite_Goriya_Move:
|
||||
{
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
JSL Sprite_DamageFlash_Long
|
||||
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
|
||||
JSL Sprite_CheckIfRecoiling
|
||||
|
||||
JSR Goriya_HandleTileCollision ; Handle tile collision and change direction
|
||||
|
||||
LDA.w SprTimerD, X : BNE ++
|
||||
JSL GetRandomInt : AND.b #$9F : BNE ++
|
||||
LDA.b #$04 : STA.w SprMiscB, X ; Set SprMiscB for boomerang attack
|
||||
%SetTimerD($FF)
|
||||
JSR Goriya_BoomerangAttack ; Spawn boomerang
|
||||
JMP +
|
||||
++
|
||||
|
||||
LDA.w SprTimerC, X : BNE +
|
||||
JSL GetRandomInt : AND.b #$03
|
||||
STA.w SprMiscB, X ; Set SprMiscB for new movement direction
|
||||
%SetTimerC(60)
|
||||
+
|
||||
|
||||
LDA.w SprMiscB, X
|
||||
JSL JumpTableLocal ; Jump to movement routine based on SprMiscB
|
||||
|
||||
dw Goriya_MoveUp
|
||||
dw Goriya_MoveDown
|
||||
dw Goriya_MoveLeft
|
||||
dw Goriya_MoveRight
|
||||
dw Goriya_Wait
|
||||
|
||||
Goriya_MoveUp:
|
||||
{
|
||||
LDA.b #-GoriyaMovementSpeed : STA.w SprYSpeed, X
|
||||
STZ.w SprXSpeed, X
|
||||
%GotoAction(0) ; Transition to Goriya_WalkingUp
|
||||
LDA.b #$00 : STA.w SprMiscE, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Goriya_MoveDown:
|
||||
{
|
||||
LDA.b #GoriyaMovementSpeed : STA.w SprYSpeed, X
|
||||
STZ.w SprXSpeed, X
|
||||
%GotoAction(1) ; Transition to Goriya_WalkingDown
|
||||
LDA.b #$01 : STA.w SprMiscE, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Goriya_MoveLeft:
|
||||
{
|
||||
STZ.w SprYSpeed, X
|
||||
LDA.b #-GoriyaMovementSpeed : STA.w SprXSpeed, X
|
||||
%GotoAction(2) ; Transition to Goriya_WalkingLeft
|
||||
LDA.b #$02 : STA.w SprMiscE, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Goriya_MoveRight:
|
||||
{
|
||||
STZ.w SprYSpeed, X
|
||||
LDA.b #GoriyaMovementSpeed : STA.w SprXSpeed, X
|
||||
%GotoAction(3) ; Transition to Goriya_WalkingRight
|
||||
LDA.b #$03 : STA.w SprMiscE, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Goriya_Wait:
|
||||
{
|
||||
STZ.w SprXSpeed, X
|
||||
STZ.w SprYSpeed, X
|
||||
%GotoAction(0) ; Transition to Goriya_WalkingUp (default)
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5. `Goriya_HandleTileCollision`
|
||||
|
||||
This routine is called to handle Goriya's collision with tiles. Upon collision, it randomly selects a new movement direction and sets a timer (`SprTimerC`).
|
||||
|
||||
```asm
|
||||
Goriya_HandleTileCollision:
|
||||
{
|
||||
JSL Sprite_CheckTileCollision
|
||||
LDA.w SprCollision, X : BEQ ++
|
||||
JSL GetRandomInt : AND.b #$03 : STA.w SprAction, X ; Randomly choose new direction
|
||||
+
|
||||
STA.w SprMiscE, X
|
||||
%SetTimerC(60) ; Set timer C for 60 frames
|
||||
++
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
### 2.6. `Goriya_BoomerangAttack`
|
||||
|
||||
This routine is responsible for spawning the boomerang sprite. It sets up the boomerang's initial properties, including its `SprSubtype` (to differentiate it from the main Goriya), action, position, and health.
|
||||
|
||||
```asm
|
||||
Goriya_BoomerangAttack:
|
||||
{
|
||||
LDA.b #$2C ; Sprite ID for boomerang (assuming $2C is the boomerang sprite ID)
|
||||
JSL Sprite_SpawnDynamically : BMI + ; Spawn a new sprite dynamically
|
||||
LDA.b #$01 : STA.w SprSubtype, Y ; Set SprSubtype to 1 for the boomerang
|
||||
LDA.b #$04 : STA.w SprAction, Y ; Set action for boomerang (e.g., flying)
|
||||
LDA.w SprX, X : STA.w SprX, Y ; Copy Goriya's X position to boomerang
|
||||
LDA.w SprY, X : STA.w SprY, Y ; Copy Goriya's Y position to boomerang
|
||||
LDA.w SprXH, X : STA.w SprXH, Y
|
||||
LDA.w SprYH, X : STA.w SprYH, Y
|
||||
LDA.b #$01 : STA.w SprNbrOAM, Y ; Set number of OAM entries
|
||||
LDA.b #$40 : STA.w SprHealth, Y ; Set boomerang health
|
||||
LDA.b #$00 : STA.w SprHitbox, Y ; Set boomerang hitbox
|
||||
+
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
### 2.7. `Sprite_Goriya_Draw` (Goriya Drawing Routine)
|
||||
|
||||
This routine is responsible for rendering Goriya's graphics. It uses a custom OAM allocation and manipulation logic to handle its multi-tile appearance and animation. It dynamically determines the animation frame and applies offsets for each tile.
|
||||
|
||||
```asm
|
||||
Sprite_Goriya_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprGfx, X : CLC : ADC.w SprFrame, X : TAY ; Calculate animation frame index
|
||||
LDA.w .start_index, Y : STA $06
|
||||
LDA.w SprFlash, X : STA $08
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles - 1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
; -------------------------------------------------------
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E : INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
; Put the sprite out of the way
|
||||
LDA.b #$F0 : STA ($90), Y : STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y : INY
|
||||
LDA .properties, X : ORA $08 : STA ($90), Y
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA.b #$02 : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
.start_index
|
||||
db $00, $02, $04, $06, $08, $0A, $0C, $0E
|
||||
.nbr_of_tiles
|
||||
db 1, 1, 1, 1, 1, 1, 1, 1
|
||||
.x_offsets
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
.y_offsets
|
||||
dw 0, -9
|
||||
dw 0, -9
|
||||
dw 0, -10
|
||||
dw 0, -10
|
||||
dw 0, -10
|
||||
dw 0, -9
|
||||
dw 0, -9
|
||||
dw -1, -10
|
||||
.chr
|
||||
; Body Head
|
||||
db $E4, $C0
|
||||
db $E4, $C0
|
||||
db $E6, $C2
|
||||
db $E6, $C2
|
||||
db $E2, $C4
|
||||
db $E0, $C4
|
||||
db $E2, $C4
|
||||
db $E0, $C4
|
||||
.properties
|
||||
db $2D, $2D
|
||||
db $6D, $2D
|
||||
db $2D, $2D
|
||||
db $6D, $2D
|
||||
db $2D, $2D
|
||||
db $2D, $2D
|
||||
db $6D, $6D
|
||||
db $6D, $6D
|
||||
}
|
||||
```
|
||||
|
||||
### 2.8. `Sprite_Boomerang_Draw` (Boomerang Drawing Routine)
|
||||
|
||||
This routine is responsible for rendering the boomerang's graphics. It also uses a custom OAM allocation and manipulation logic.
|
||||
|
||||
```asm
|
||||
Sprite_Boomerang_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprGfx, X : CLC : ADC.w SprFrame, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
LDA.w SprFlash, X : STA $08
|
||||
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : ORA $08 : STA ($90), Y
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA #$02 : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
|
||||
.start_index
|
||||
db $00, $01, $02, $03
|
||||
.nbr_of_tiles
|
||||
db 0, 0, 0, 0
|
||||
.chr
|
||||
db $26
|
||||
db $26
|
||||
db $26
|
||||
db $26
|
||||
.properties
|
||||
db $22
|
||||
db $A2
|
||||
db $E2
|
||||
db $62
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Key Behaviors and Implementation Details
|
||||
|
||||
* **Dual Role (Goriya and Boomerang):** The `Sprite_Goriya_Long` routine uses `SprSubtype, X` to determine if the current sprite instance is the main Goriya or a boomerang it has thrown. This allows a single sprite ID to manage two distinct entities.
|
||||
* **Boomerang Attack:** Goriya actively throws a boomerang (`Goriya_BoomerangAttack`) at Link, which then becomes an independent sprite with its own drawing and movement logic (`BoomerangAttack` state in `Sprite_Goriya_Main` and `Sprite_Boomerang_Draw`).
|
||||
* **Dynamic Health:** Goriya's health is set to a fixed value of `08` (one heart) during initialization.
|
||||
* **State Management:** Goriya uses `SprAction, X` and `JumpTableLocal` to manage its walking states (`Goriya_WalkingUp`, `Goriya_WalkingDown`, `Goriya_WalkingLeft`, `Goriya_WalkingRight`) and its `BoomerangAttack` state.
|
||||
* **Movement Patterns:** Goriya moves in random directions, with `Goriya_HandleTileCollision` triggering a new random direction upon hitting a tile. It also has a `Goriya_Wait` state.
|
||||
* **Custom OAM Drawing:** Both the Goriya and its boomerang utilize custom OAM drawing routines (`Sprite_Goriya_Draw` and `Sprite_Boomerang_Draw`) for precise control over their multi-tile graphics and animation. The use of `REP`/`SEP` for 16-bit coordinate calculations is present in both.
|
||||
* **Code Reuse:** The `Goriya_HandleTileCollision` routine is notably reused by the Darknut sprite, indicating a shared and modular approach to tile collision handling for certain enemy types.
|
||||
* **`SprMiscB` Usage:** This variable is used to store the current movement direction (0-4) for Goriya's random movement.
|
||||
* **`SprMiscE` Usage:** This variable also stores the current movement direction, likely for animation or other directional logic.
|
||||
* **`SprTimerC` and `SprTimerD` Usage:** These timers are used to control the duration of movement states and the frequency of boomerang attacks.
|
||||
@@ -1,433 +0,0 @@
|
||||
# Helmet Chuchu Sprite Analysis
|
||||
|
||||
This document provides a detailed analysis of the `helmet_chuchu.asm` sprite, outlining its properties, core routines, and behavioral patterns.
|
||||
|
||||
## 1. Sprite Properties
|
||||
|
||||
The following `!SPRID` constants define Helmet Chuchu's fundamental characteristics:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_HelmetChuchu
|
||||
!NbrTiles = 03 ; Number of tiles used in a frame
|
||||
!Harmless = 00 ; 00 = Sprite is Harmful, 01 = Sprite is Harmless
|
||||
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
||||
!Health = $10 ; Number of Health the sprite have
|
||||
!Damage = 04 ; (08 is a whole heart), 04 is half heart
|
||||
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
||||
!ImperviousAll = 00 ; 00 = Can be attack, 01 = attack will clink on it
|
||||
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
||||
!Shadow = 00 ; 00 = don't draw shadow, 01 = draw a shadow
|
||||
!Palette = 00 ; Unused in this template (can be 0 to 7)
|
||||
!Hitbox = 00 ; 00 to 31, can be viewed in sprite draw tool
|
||||
!Persist = 00 ; 01 = your sprite continue to live offscreen
|
||||
!Statis = 00 ; 00 = is sprite is alive?, (kill all enemies room)
|
||||
!CollisionLayer = 00 ; 01 = will check both layer for collision
|
||||
!CanFall = 00 ; 01 sprite can fall in hole, 01 = can't fall
|
||||
!DeflectArrow = 00 ; 01 = deflect arrows
|
||||
!WaterSprite = 00 ; 01 = can only walk shallow water
|
||||
!Blockable = 00 ; 01 = can be blocked by link's shield?
|
||||
!Prize = 00 ; 00-15 = the prize pack the sprite will drop from
|
||||
!Sound = 00 ; 01 = Play different sound when taking damage
|
||||
!Interaction = 00 ; ?? No documentation
|
||||
!Statue = 00 ; 01 = Sprite is statue
|
||||
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
||||
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
||||
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
||||
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
||||
```
|
||||
**Note:** `!Health` is initially set to `$10` but is dynamically determined during initialization based on Link's sword level.
|
||||
|
||||
## 2. Core Routines
|
||||
|
||||
### 2.1. `Sprite_HelmetChuchu_Long` (Main Loop)
|
||||
|
||||
This is the primary entry point for Helmet Chuchu's per-frame execution. It handles drawing, shadow rendering, and then dispatches to the main logic routine if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_HelmetChuchu_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_HelmetChuchu_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_HelmetChuchu_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. `Sprite_HelmetChuchu_Prep` (Initialization)
|
||||
|
||||
This routine is executed once when Helmet Chuchu is first spawned. It sets its health based on Link's sword level, randomly assigns an initial `SprAction` (determining its type and initial frame), and initializes `SprMiscB` and `SprMiscD` to zero.
|
||||
|
||||
```asm
|
||||
Sprite_HelmetChuchu_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.l Sword : DEC A : TAY
|
||||
LDA.w .health, Y : STA.w SprHealth, X ; Set health based on sword level
|
||||
JSL GetRandomInt : AND.b #$02 : STA.w SprAction, X ; Randomly set initial action (0, 1, or 2)
|
||||
STZ.w SprMiscB, X
|
||||
STZ.w SprMiscD, X
|
||||
LDA.w SprAction, X : BNE +
|
||||
LDA.b #$04 : STA.w SprFrame, X ; If action 0, set frame to 4 (Helmet Green)
|
||||
+
|
||||
CMP.b #$02 : BNE +
|
||||
LDA.b #$02 : STA.w SprFrame, X ; If action 2, set frame to 2 (Mask Red)
|
||||
+
|
||||
PLB
|
||||
RTL
|
||||
|
||||
.health
|
||||
db $08, $0C, $0F, $10 ; Health values for each sword level
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3. `Sprite_HelmetChuchu_Main` (Behavioral State Machine)
|
||||
|
||||
This routine manages Helmet Chuchu's AI through a state machine, using `SprAction, X` to determine its current behavior. It includes states for different Chuchu types (Green/Red, Helmet/No Helmet, Mask/No Mask) and separate states for the detached helmet and mask.
|
||||
|
||||
```asm
|
||||
Sprite_HelmetChuchu_Main:
|
||||
{
|
||||
JSL Sprite_DamageFlash_Long
|
||||
%SpriteJumpTable(GreenChuchu_Helmet,
|
||||
GreenChuchu_NoHelmet,
|
||||
RedChuchu_Masked,
|
||||
RedChuchu_NoMask,
|
||||
HelmetSubtype,
|
||||
MaskSubtype)
|
||||
|
||||
GreenChuchu_Helmet:
|
||||
{
|
||||
%StartOnFrame(4)
|
||||
%PlayAnimation(4, 5, 16)
|
||||
JSR Sprite_CheckForHookshot : BCC +
|
||||
LDA.w SprFlash, X : BEQ +
|
||||
%GotoAction(1) ; Transition to GreenChuchu_NoHelmet if hookshot hit and not flashing
|
||||
+
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
JSR Sprite_Chuchu_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
GreenChuchu_NoHelmet:
|
||||
{
|
||||
%StartOnFrame(0)
|
||||
%PlayAnimation(0, 1, 16)
|
||||
LDA.w SprMiscD, X : BNE +
|
||||
JSR HelmetChuchu_SpawnHookshotDrag ; Spawn detached helmet
|
||||
LDA.b #$01 : STA.w SprMiscD, X ; Set flag to prevent re-spawning
|
||||
+
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
JSR Sprite_Chuchu_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
RedChuchu_Masked:
|
||||
{
|
||||
%StartOnFrame(2)
|
||||
%PlayAnimation(2, 3, 16)
|
||||
JSR Sprite_CheckForHookshot : BCC +
|
||||
LDA.w SprFlash, X : BEQ +
|
||||
%GotoAction(3) ; Transition to RedChuchu_NoMask if hookshot hit and not flashing
|
||||
+
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
JSR Sprite_Chuchu_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
RedChuchu_NoMask:
|
||||
{
|
||||
%StartOnFrame(6)
|
||||
%PlayAnimation(6, 7, 16)
|
||||
LDA.w SprMiscD, X : BNE +
|
||||
JSR HelmetChuchu_SpawnHookshotDrag ; Spawn detached mask
|
||||
LDA.b #$01 : STA.w SprMiscD, X ; Set flag to prevent re-spawning
|
||||
+
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
JSR Sprite_Chuchu_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
HelmetSubtype:
|
||||
{
|
||||
%StartOnFrame(8)
|
||||
%PlayAnimation(8, 8, 16)
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_CheckIfLifted
|
||||
JSL Sprite_CheckIfRecoiling
|
||||
JSL ThrownSprite_TileAndSpriteInteraction_long
|
||||
RTS
|
||||
}
|
||||
|
||||
MaskSubtype:
|
||||
{
|
||||
%StartOnFrame(8)
|
||||
%PlayAnimation(9, 9, 16)
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_CheckIfLifted
|
||||
JSL Sprite_CheckIfRecoiling
|
||||
JSL ThrownSprite_TileAndSpriteInteraction_long
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4. `Sprite_Chuchu_Move` (Movement and Interaction Logic)
|
||||
|
||||
This routine handles Helmet Chuchu's movement, which involves bouncing towards or recoiling from the player. It uses `SprMiscB, X` to switch between these two behaviors.
|
||||
|
||||
```asm
|
||||
Sprite_Chuchu_Move:
|
||||
{
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
JSL Sprite_CheckIfRecoiling
|
||||
|
||||
LDA.w SprMiscB, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw BounceTowardPlayer
|
||||
dw RecoilFromPlayer
|
||||
|
||||
BounceTowardPlayer:
|
||||
{
|
||||
JSL GetRandomInt : AND.b #$02 : STA $09 ; Speed
|
||||
JSL GetRandomInt : AND.b #$07 : STA $08 ; Height
|
||||
|
||||
JSL Sprite_MoveAltitude
|
||||
DEC.w $0F80,X : DEC.w $0F80,X
|
||||
LDA.w SprHeight, X : BPL .aloft
|
||||
STZ.w SprHeight, X
|
||||
LDA.b $08 : STA.w $0F80, X ; set height from 08
|
||||
LDA.b $09
|
||||
JSL Sprite_ApplySpeedTowardsPlayer
|
||||
.aloft
|
||||
LDA.w SprHeight, X : BEQ .dontmove
|
||||
JSL Sprite_Move
|
||||
.dontmove
|
||||
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_damage
|
||||
INC.w SprMiscB, X ; Switch to RecoilFromPlayer
|
||||
LDA.b #$20 : STA.w SprTimerB, X
|
||||
.no_damage
|
||||
|
||||
JSL Sprite_CheckDamageToPlayer : BCC .no_attack
|
||||
INC.w SprMiscB, X ; Switch to RecoilFromPlayer
|
||||
LDA.b #$20 : STA.w SprTimerB, X
|
||||
.no_attack
|
||||
|
||||
RTS
|
||||
}
|
||||
|
||||
RecoilFromPlayer:
|
||||
{
|
||||
JSL GetRandomInt : AND.b #$02 : STA $09 ; Speed
|
||||
LDA.w SprX, X : CLC : ADC $09 : STA $04
|
||||
LDA.w SprY, X : SEC : SBC $09 : STA $06
|
||||
LDA.w SprXH, X : ADC #$00 : STA $05
|
||||
LDA.w SprYH, X : ADC #$00 : STA $07
|
||||
LDA $09 : STA $00 : STA $01
|
||||
JSL Sprite_ProjectSpeedTowardsEntityLong
|
||||
|
||||
LDA.w SprTimerB, X : BNE .not_done
|
||||
JSR HelmetChuchu_SpawnHookshotDrag ; Spawn detached helmet/mask
|
||||
STZ.w SprMiscB, X ; Switch back to BounceTowardPlayer
|
||||
.not_done
|
||||
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5. `HelmetChuchu_SpawnHookshotDrag`
|
||||
|
||||
This routine is responsible for spawning the detached helmet or mask as a separate sprite when the Chuchu is hit by a hookshot. It determines whether to spawn a helmet or a mask based on the Chuchu's current `SprAction`.
|
||||
|
||||
```asm
|
||||
HelmetChuchu_SpawnHookshotDrag:
|
||||
{
|
||||
; Based on the subtype either spawn the helmet or the mask
|
||||
PHX
|
||||
LDA.w SprAction, X : CMP.b #$01 : BEQ .spawn_helmet
|
||||
CMP.b #$03 : BEQ .spawn_mask
|
||||
|
||||
.spawn_helmet
|
||||
LDA.b #$05 ; Sprite ID for helmet/mask (assuming $05 is the ID)
|
||||
JSL Sprite_SpawnDynamically : BMI .no_space
|
||||
LDA.b #$05 : STA.w SprAction, Y ; Set action for detached helmet
|
||||
JMP .prepare_mask
|
||||
.no_space
|
||||
JMP .no_space2
|
||||
|
||||
.spawn_mask
|
||||
LDA.b #$05 ; Sprite ID for helmet/mask
|
||||
JSL Sprite_SpawnDynamically : BMI .no_space2
|
||||
LDA.b #$04 : STA.w SprAction, Y ; Set action for detached mask
|
||||
.prepare_mask
|
||||
JSL Sprite_SetSpawnedCoordinates
|
||||
LDA.b #$10 : STA.w SprHealth, Y
|
||||
LDA.b #$00 : STA.w SprMiscB, Y
|
||||
LDA.b #$80 : STA.w SprTimerA, Y
|
||||
LDA.b #$01 : STA.w SprNbrOAM, Y
|
||||
LDA.w .speed_x, X : STA.w SprXSpeed, Y
|
||||
LDA.w .speed_y, X : STA.w SprYSpeed, Y
|
||||
.no_space2
|
||||
PLX
|
||||
RTS
|
||||
|
||||
.speed_x
|
||||
db 16, -11, -16, 11
|
||||
|
||||
.speed_y
|
||||
db 0, 11, 0, -11
|
||||
}
|
||||
```
|
||||
|
||||
### 2.6. `Sprite_CheckForHookshot`
|
||||
|
||||
This routine checks if a hookshot is currently active and interacting with the Chuchu. It iterates through ancilla slots to find a hookshot (`$1F`) and returns with the carry flag set if found.
|
||||
|
||||
```asm
|
||||
Sprite_CheckForHookshot:
|
||||
{
|
||||
PHX
|
||||
LDX.b #$0A
|
||||
.next_ancilla
|
||||
LDA.w $0C4A, X : CMP.b #$1F : BNE .not_hooker ; Check ancilla type (assuming $1F is hookshot)
|
||||
PLX
|
||||
SEC ; Carry set if hookshot found
|
||||
RTS
|
||||
.not_hooker
|
||||
DEX
|
||||
BPL .next_ancilla
|
||||
PLX
|
||||
CLC ; Carry clear if no hookshot found
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
### 2.7. `Sprite_HelmetChuchu_Draw` (Drawing Routine)
|
||||
|
||||
This routine is responsible for rendering Helmet Chuchu's graphics. It uses a custom OAM allocation and manipulation logic to handle its multi-tile appearance and animation, dynamically adjusting based on its current state (helmet/mask, color, animation frame).
|
||||
|
||||
```asm
|
||||
Sprite_HelmetChuchu_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprGfx, X : CLC : ADC.w SprFrame, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
LDA.w SprFlash, X : STA $08
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : ORA $08 : STA ($90), Y
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA.b #$02 : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
|
||||
; =======================================================
|
||||
; chr prop
|
||||
; Mask $04 $37
|
||||
; Helmet $08 $3B
|
||||
|
||||
.start_index
|
||||
db $00, $02, $03, $06, $08, $0A, $0C, $0E, $0F, $10
|
||||
.nbr_of_tiles
|
||||
db 1, 0, 2, 1, 1, 1, 1, 0, 0, 0
|
||||
.y_offsets
|
||||
dw 0, -8
|
||||
dw 0
|
||||
dw 0, -8, -8
|
||||
dw 0, -4
|
||||
dw 0, -8
|
||||
dw 0, -4
|
||||
dw 0, -8
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
.chr
|
||||
; No Helmet Green
|
||||
db $26, $16
|
||||
db $24
|
||||
; Mask Red
|
||||
db $26, $16, $04
|
||||
db $24, $04
|
||||
; Helmet Green
|
||||
db $26, $08
|
||||
db $24, $08
|
||||
; No Helmet Red
|
||||
db $26, $16
|
||||
db $24
|
||||
; Mask
|
||||
db $04
|
||||
; Helmet
|
||||
db $08
|
||||
.properties
|
||||
db $2B, $2B
|
||||
db $2B
|
||||
db $25, $25, $27
|
||||
db $25, $27
|
||||
db $2B, $29
|
||||
db $2B, $29
|
||||
db $25, $25
|
||||
db $25
|
||||
; mask
|
||||
db $27
|
||||
; helmet
|
||||
db $29
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Key Behaviors and Implementation Details
|
||||
|
||||
* **Dynamic Appearance and State:** Helmet Chuchu is a highly dynamic sprite that changes its appearance and behavior based on whether it has a helmet/mask and its color (green/red). This is managed through its `SprAction` and `SprFrame` values.
|
||||
* **Conditional Damage Handling:** The Chuchu's vulnerability to damage is tied to the presence of its helmet or mask. When hit by a hookshot, the helmet/mask detaches, making the Chuchu vulnerable.
|
||||
* **Hookshot Interaction:** Special logic (`Sprite_CheckForHookshot`) is implemented to detect interaction with Link's hookshot, which triggers the detachment of the helmet/mask.
|
||||
* **Detached Helmet/Mask as Separate Sprites:** When the helmet or mask is detached, it is spawned as an independent sprite (`HelmetSubtype` or `MaskSubtype`) with its own movement (`Sprite_Move`), collision (`ThrownSprite_TileAndSpriteInteraction_long`), and interaction logic. This demonstrates a sophisticated use of child sprites.
|
||||
* **Movement Patterns:** The Chuchu moves by bouncing towards (`BounceTowardPlayer`) and recoiling from (`RecoilFromPlayer`) the player, with randomness introduced in speed and height. This creates a distinct and challenging movement pattern.
|
||||
* **Custom OAM Drawing:** The `Sprite_HelmetChuchu_Draw` routine is a complex example of custom OAM manipulation. It dynamically selects tiles and properties based on the Chuchu's current state, allowing for seamless transitions between helmeted, masked, and vulnerable forms.
|
||||
* **`SprMiscB` Usage:** This variable controls the Chuchu's movement sub-states (`BounceTowardPlayer` and `RecoilFromPlayer`). It also plays a role in the detached helmet/mask sprites.
|
||||
* **`SprMiscD` Usage:** This variable acts as a flag to ensure that the `HelmetChuchu_SpawnHookshotDrag` routine is called only once when the helmet/mask is detached.
|
||||
* **`SprTimerB` Usage:** Used to control the duration of the recoil state.
|
||||
@@ -1,293 +0,0 @@
|
||||
# Keese
|
||||
|
||||
## Overview
|
||||
The Keese sprite (`!SPRID = $11`) is a versatile enemy that encompasses multiple variations: Ice Keese, Fire Keese, and Vampire Bat. Its behavior is dynamically determined by its `SprSubtype`.
|
||||
|
||||
## Subtypes
|
||||
* `00` - Ice Keese
|
||||
* `01` - Fire Keese
|
||||
* `02` - Vampire Bat
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `$11` (Vanilla sprite ID for Keese/Vampire Bat)
|
||||
* **`!NbrTiles`**: `08`
|
||||
* **`!Harmless`**: `00`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00` (Dynamically set in `_Prep` based on subtype)
|
||||
* **`!Damage`**: `00` (Damage is handled by projectiles or specific attack logic)
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `01`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00` (Dynamically set in `_Prep` based on subtype)
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Keese_Long`)
|
||||
This routine dispatches to different drawing and main logic routines based on the sprite's `SprSubtype`.
|
||||
|
||||
```asm
|
||||
Sprite_Keese_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.w SprSubtype, X : CMP.b #$02 : BEQ +
|
||||
JSR Sprite_Keese_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Keese_Main
|
||||
.SpriteIsNotActive
|
||||
JMP ++
|
||||
+
|
||||
JSR Sprite_VampireBat_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC ++
|
||||
JSR Sprite_VampireBat_Main
|
||||
++
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Keese_Prep`)
|
||||
This routine initializes sprite properties upon spawning, including health and prize, based on its subtype.
|
||||
|
||||
```asm
|
||||
Sprite_Keese_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
LDA.b #$30 : STA.w SprTimerC, X
|
||||
LDA.w SprSubtype, X : CMP.b #$02 : BNE +
|
||||
LDA.b #$20 : STA.w SprHealth, X
|
||||
BRA ++
|
||||
+
|
||||
LDA.b #$03 : STA.w SprNbrOAM, X
|
||||
LDA.b #$03 : STA.w SprPrize, X
|
||||
++
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Keese_Main`)
|
||||
The Keese's behavior is managed by a state machine with `Keese_Idle` and `Keese_FlyAround` states.
|
||||
|
||||
* **`Keese_Idle`**: The sprite remains stationary until Link is within a certain distance, then transitions to `Keese_FlyAround`.
|
||||
* **`Keese_FlyAround`**: The Keese flies around, plays an animation, checks for collisions with Link and tiles, and can initiate an attack. It uses `GetRandomInt` for varied movement and `Sprite_ProjectSpeedTowardsPlayer` to move towards Link.
|
||||
|
||||
```asm
|
||||
Sprite_Keese_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Keese_Idle
|
||||
dw Keese_FlyAround
|
||||
|
||||
Keese_Idle:
|
||||
{
|
||||
STZ.w SprFrame, X
|
||||
; Wait til the player is nearby then fly around
|
||||
LDA.w SprTimerC, X : BEQ .move
|
||||
JSL GetDistance8bit_Long : CMP.b #$20 : BCS +
|
||||
.move
|
||||
INC.w SprAction, X
|
||||
JSL GetRandomInt
|
||||
STA.w SprTimerA, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Keese_FlyAround:
|
||||
{
|
||||
%PlayAnimation(0,5,8)
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC +
|
||||
JSL ForcePrizeDrop_long
|
||||
+
|
||||
JSL Sprite_DamageFlash_Long
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
|
||||
JSL GetRandomInt : AND.b #$3F : BNE +
|
||||
LDA.b #$10 : STA.w SprTimerC, X
|
||||
+
|
||||
JSR Sprite_Keese_Attack
|
||||
|
||||
LDA.w SprTimerA, X : AND.b #$10 : BNE +
|
||||
LDA.b #$40
|
||||
JSL Sprite_ProjectSpeedTowardsPlayer
|
||||
+
|
||||
|
||||
JSL Sprite_SelectNewDirection
|
||||
JSL Sprite_Move
|
||||
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
STZ.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Attack Logic (`Sprite_Keese_Attack`)
|
||||
This routine handles the Keese's attack, which varies by subtype:
|
||||
|
||||
* **Ice Keese (`SprSubtype = 0`)**: Spawns sparkle garnish and a blind laser trail.
|
||||
* **Fire Keese (`SprSubtype = 1`)**: Utilizes `Sprite_Twinrova_FireAttack`.
|
||||
|
||||
```asm
|
||||
Sprite_Keese_Attack:
|
||||
{
|
||||
LDA.w SprTimerC, X : BEQ +
|
||||
LDA.w SprSubtype, X : BEQ ++
|
||||
JSL Sprite_Twinrova_FireAttack
|
||||
JMP +
|
||||
++
|
||||
JSL Sprite_SpawnSparkleGarnish
|
||||
JSL BlindLaser_SpawnTrailGarnish
|
||||
+
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_Keese_Draw`)
|
||||
The drawing routine handles OAM allocation, animation, and palette adjustments based on the sprite's subtype. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations.
|
||||
|
||||
```asm
|
||||
Sprite_Keese_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprFrame, X : TAY ;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
LDA.w SprFlash, X : STA $08
|
||||
LDA.w SprMiscB, X : STA $09
|
||||
LDA.w SprSubtype, X : CMP.b #$01 : BNE +
|
||||
LDA.b #$0A : EOR $08 : STA $08
|
||||
+
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
|
||||
; If SprMiscA != 0, then use 4th sheet
|
||||
LDA.b $09 : BEQ +
|
||||
LDA .chr_2, X : STA ($90), Y
|
||||
JMP ++
|
||||
+
|
||||
LDA .chr, X : STA ($90), Y
|
||||
++
|
||||
INY
|
||||
LDA .properties, X : ORA $08 : STA ($90), Y
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY
|
||||
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
.start_index
|
||||
db $00, $01, $03, $04, $06, $08
|
||||
.nbr_of_tiles
|
||||
db 0, 1, 0, 1, 1, 0
|
||||
.x_offsets
|
||||
dw 0
|
||||
dw -4, 4
|
||||
dw 0
|
||||
dw -4, 4
|
||||
dw -4, 4
|
||||
dw 0
|
||||
.y_offsets
|
||||
dw 0
|
||||
dw 0, 0
|
||||
dw 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0
|
||||
.chr
|
||||
db $80
|
||||
db $A2, $A2
|
||||
db $82
|
||||
db $84, $84
|
||||
db $A4, $A4
|
||||
db $A0
|
||||
.chr_2
|
||||
db $C0
|
||||
db $E2, $E2
|
||||
db $C2
|
||||
db $C4, $C4
|
||||
db $E4, $E4
|
||||
db $E0
|
||||
.properties
|
||||
db $35
|
||||
db $35, $75
|
||||
db $35
|
||||
db $35, $75
|
||||
db $35, $75
|
||||
db $35
|
||||
.sizes
|
||||
db $02
|
||||
db $02, $02
|
||||
db $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Subtype-based Behavior**: The sprite uses `SprSubtype` to implement distinct behaviors and appearances for Ice Keese, Fire Keese, and Vampire Bat, all under a single `!SPRID`.
|
||||
* **Dynamic Property Initialization**: Health and prize values are set dynamically during the `_Prep` routine based on the sprite's subtype.
|
||||
* **Conditional Drawing and Palette**: The drawing routine adjusts the sprite's palette and potentially its graphics based on its subtype, allowing for visual differentiation.
|
||||
* **Randomized Movement**: Utilizes `GetRandomInt` to introduce variability in movement patterns, making the enemy less predictable.
|
||||
* **Projectile Attacks**: Implements different projectile attacks based on subtype, showcasing varied offensive capabilities.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, which is crucial for accurate sprite rendering.
|
||||
@@ -1,242 +0,0 @@
|
||||
# Leever
|
||||
|
||||
## Overview
|
||||
The Leever sprite is a custom implementation that overrides the vanilla Leever behavior (`Sprite_71_Leever`). It features distinct states for being underground, emerging, attacking, and digging back down, with randomized timers controlling its transitions.
|
||||
|
||||
## Vanilla Override
|
||||
This custom Leever implementation hooks into the vanilla sprite ID $71. It uses a custom flag at `$0FFF` to determine whether to execute its custom logic (`Sprite_Leever_Long`) or fall back to the original vanilla Leever behavior (`Sprite_71_Leever`).
|
||||
|
||||
```asm
|
||||
pushpc
|
||||
|
||||
Sprite_71_Leever = $06CBA2
|
||||
|
||||
org $069365 : dw Sprite_71_Leever_Alt
|
||||
|
||||
Sprite_71_Leever_Alt:
|
||||
{
|
||||
LDA.w $0FFF : BEQ +
|
||||
JSL Sprite_Leever_Long
|
||||
JMP ++
|
||||
+
|
||||
JSR Sprite_71_Leever
|
||||
++
|
||||
RTS
|
||||
}
|
||||
assert pc() <= $06A5C0
|
||||
|
||||
pullpc
|
||||
```
|
||||
|
||||
## Sprite Properties
|
||||
Explicit sprite properties (`!SPRID`, `!Health`, etc.) are not defined within this file, suggesting it either inherits vanilla properties for sprite ID $71 or these are defined in a separate configuration file.
|
||||
|
||||
## Main Structure (`Sprite_Leever_Long`)
|
||||
This routine is the main entry point for the custom Leever logic, executed every frame. It handles bank setup, conditional drawing (skipping drawing when underground), and dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Leever_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.w SprAction, X : BEQ +
|
||||
JSR Sprite_Leever_Draw
|
||||
+
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Leever_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Movement Routine (`Sprite_Leever_Move`)
|
||||
A shared routine for handling the Leever's movement, including applying speed towards the player, moving the sprite, and bouncing off tiles.
|
||||
|
||||
```asm
|
||||
Sprite_Leever_Move:
|
||||
{
|
||||
JSL Sprite_ApplySpeedTowardsPlayer
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Leever_Main`)
|
||||
The Leever's core behavior is managed by a state machine with four distinct states:
|
||||
|
||||
* **`Leever_Underground`**: The Leever moves underground. After a timer (`SprTimerA`) expires, it transitions to `Leever_Emerge`.
|
||||
* **`Leever_Emerge`**: The Leever plays a backwards animation as it emerges. After a randomized timer, it transitions to `Leever_Attack`.
|
||||
* **`Leever_Attack`**: The Leever plays an attack animation, checks for damage to/from Link, and moves. After a timer, it transitions to `Leever_Dig`.
|
||||
* **`Leever_Dig`**: The Leever plays an animation as it digs back into the ground. After a randomized timer, it transitions back to `Leever_Underground`.
|
||||
|
||||
```asm
|
||||
Sprite_Leever_Main:
|
||||
{
|
||||
JSL Sprite_DamageFlash_Long
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Leever_Underground
|
||||
dw Leever_Emerge
|
||||
dw Leever_Attack
|
||||
dw Leever_Dig
|
||||
|
||||
Leever_Underground:
|
||||
{
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
LDA.b #$40 : STA.w SprTimerA, X
|
||||
INC.w SprAction, X
|
||||
+
|
||||
LDA.b #$10
|
||||
JSR Sprite_Leever_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
Leever_Emerge:
|
||||
{
|
||||
%PlayAnimBackwards(3, 2, 10)
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
JSL GetRandomInt
|
||||
AND.b #$3F
|
||||
ADC.b #$A0
|
||||
STA.w $0DF0,X
|
||||
INC.w SprAction, X
|
||||
STZ.w SprXSpeed, X : STZ.w SprYSpeed, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Leever_Attack:
|
||||
{
|
||||
%PlayAnimation(0, 1, 10)
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
LDA.b #$7F : STA.w SprTimerA, X
|
||||
INC.w SprAction, X
|
||||
+
|
||||
PHX
|
||||
JSL Sprite_CheckIfRecoiling
|
||||
JSL Sprite_CheckDamageToPlayerSameLayer
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
PLX
|
||||
LDA.b #$0C
|
||||
JSR Sprite_Leever_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
Leever_Dig:
|
||||
{
|
||||
%PlayAnimation(2, 3, 10)
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
JSL GetRandomInt
|
||||
AND.b #$1F
|
||||
ADC.b #$40
|
||||
STA.w $0DF0,X
|
||||
STZ.w SprAction, X
|
||||
+
|
||||
LDA.b #$08
|
||||
JSR Sprite_Leever_Move
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_Leever_Draw`)
|
||||
The drawing routine handles OAM allocation and animation. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations.
|
||||
|
||||
```asm
|
||||
Sprite_Leever_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA $0DC0, X : CLC : ADC $0D90, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
LDA.w SprFlash, X : STA $08
|
||||
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : ORA $08 : STA ($90), Y
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY
|
||||
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
.start_index
|
||||
db $00, $01, $02, $03
|
||||
.nbr_of_tiles
|
||||
db 0, 0, 0, 0
|
||||
.x_offsets
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
.y_offsets
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
.chr
|
||||
db $C4
|
||||
db $C6
|
||||
db $C2
|
||||
db $C0
|
||||
.properties
|
||||
db $33
|
||||
db $33
|
||||
db $33
|
||||
db $33
|
||||
.sizes
|
||||
db $02
|
||||
db $02
|
||||
db $02
|
||||
db $02
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Vanilla Override**: Explicitly overrides a vanilla sprite's behavior, demonstrating how to replace existing game logic with custom implementations.
|
||||
* **Conditional Logic**: Uses a custom flag (`$0FFF`) to dynamically switch between vanilla and custom behaviors, offering flexibility in game design.
|
||||
* **Emerging/Digging State Machine**: Implements a robust state machine to manage the Leever's characteristic emerging from and digging back into the ground, with randomized timers for unpredictable transitions.
|
||||
* **Animation Control**: Utilizes `%PlayAnimBackwards` for specific animation effects, such as the Leever emerging from the ground.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, essential for accurate sprite rendering.
|
||||
@@ -1,79 +0,0 @@
|
||||
# Octoboss Sprite Analysis
|
||||
|
||||
## Overview
|
||||
The `octoboss` sprite (ID: `Sprite_Octoboss`, which is `$3C`) is a multi-phase boss, likely an octopus-like creature. It features a unique mechanic involving a "brother" Octoboss, and can summon stalfos offspring. The fight progresses through distinct phases including emergence, movement, taunting, ascending, submerging, and a surrender sequence.
|
||||
|
||||
## Key Properties:
|
||||
* **Sprite ID:** `Sprite_Octoboss` (`$3C`)
|
||||
* **Description:** A multi-phase boss with a "brother" Octoboss, capable of summoning stalfos and engaging in various movement and attack patterns.
|
||||
* **Number of Tiles:** 11
|
||||
* **Health:** `00` (Health is managed by `ReturnTotalHealth` and `CheckForNextPhase`, combining the health of both Octobosses.)
|
||||
* **Damage:** `00` (Damage dealt to Link is likely handled by its attacks or spawned offspring.)
|
||||
* **Special Properties:**
|
||||
* `!Boss = $01` (Correctly identified as a boss.)
|
||||
* `!Shadow = 01` (Draws a shadow.)
|
||||
* `!Hitbox = 03`
|
||||
|
||||
## Custom Variables:
|
||||
* `!ConsecutiveHits = $AC`: Tracks consecutive hits on the boss.
|
||||
* `!KydrogPhase = $7A`: Tracks the current phase of the boss fight. (Note: This variable name is `KydrogPhase`, suggesting shared logic or a copy-paste from the Kydrog boss.)
|
||||
* `!WalkSpeed = 10`: Defines the boss's walking speed.
|
||||
* `BrotherSpr = $0EB0`: Stores the sprite index of the "brother" Octoboss.
|
||||
|
||||
## Main Logic Flow (`Sprite_Octoboss_Main` and `Sprite_Octoboss_Secondary`):
|
||||
The Octoboss has two primary main routines, `Sprite_Octoboss_Main` and `Sprite_Octoboss_Secondary`, which are called based on `SprMiscF, X`. This suggests different forms or behaviors for the two Octobosses.
|
||||
|
||||
**`Sprite_Octoboss_Main` Jump Table (for the primary Octoboss):**
|
||||
* **`WaitForPlayerToApproach` (0x00):** Waits for Link to reach a specific Y-coordinate (`$08C8`) to trigger activation.
|
||||
* **`Emerge` (0x01):** Emerges from the water, preventing Link's movement.
|
||||
* **`EmergedShowMessage` (0x02):** Displays an introductory message.
|
||||
* **`SpawnAndAwakeHisBrother` (0x03):** Spawns a "brother" Octoboss (`Sprite_Octoboss` with `SprMiscF = $01`) and stores its ID in `BrotherSpr`.
|
||||
* **`WaitForBrotherEmerge` (0x04):** Waits for the brother to emerge and displays a message.
|
||||
* **`SpawnPirateHats` (0x05):** Spawns "boss poof" effects for both Octobosses, changes their frames, and spawns walls using `Overworld_DrawMap16_Persist` macros.
|
||||
* **`IdlePhase` (0x06):** Idles, can spawn fireballs, and checks total health (`ReturnTotalHealth`) to potentially trigger surrender.
|
||||
* **`PickDirection` (0x07):** Picks a random direction and speed for movement.
|
||||
* **`Moving` (0x08):** Moves around, handles boundary checks, spawns splash effects, and checks total health to potentially trigger surrender.
|
||||
* **`WaitMessageBeforeSurrender` (0x09):** Displays a surrender message (`$004A`), sets the brother's action, and transitions to `RemoveHat`.
|
||||
* **`RemoveHat` (0x0A):** Removes hats from both Octobosses with "boss poof" effects, displays a message (`$004B`), and transitions to `Submerge`.
|
||||
* **`Submerge` (0x0B):** Submerges, playing an animation and spawning a splash effect.
|
||||
* **`SubmergeWaitWall` (0x0C):** Submerges further, drawing walls using `Overworld_DrawMap16_Persist` macros.
|
||||
* **`EmergeWaitGiveItem` (0x0D):** Emerges, spawns a medallion (`SpawnMedallion`), and sets an SRAM flag.
|
||||
* **`SubmergeForeverKill` (0x0E):** Submerges completely, despawns, and allows Link to move again.
|
||||
|
||||
**`Sprite_Octoboss_Secondary` Jump Table (for the secondary/brother Octoboss, when `SprMiscF, X` is set):**
|
||||
This routine largely mirrors `Sprite_Octoboss_Main` but includes specific states like `WaitDialog` and `Moving2`, suggesting slight behavioral differences.
|
||||
|
||||
## Initialization (`Sprite_Octoboss_Long` and `Sprite_Octoboss_Prep`):
|
||||
* **`Sprite_Octoboss_Long`:** Main entry point. Handles sprite initialization, including setting OAM properties, bulletproofing, frame, and palette values. It also checks for boss defeat and medallion spawning.
|
||||
* **`Sprite_Octoboss_Prep`:** Initializes `!KydrogPhase` to `00`, sets initial health (`$A0`), configures deflection, hitbox, bump damage, and calls `JSR KydrogBoss_Set_Damage` to set up the damage table. Sets initial sprite speeds and an intro timer.
|
||||
|
||||
## Health Management (`Sprite_Octoboss_CheckIfDead`, `ReturnTotalHealth`, `CheckForNextPhase`):
|
||||
* **`Sprite_Octoboss_CheckIfDead`:** Monitors `SprHealth, X`. If health is zero or negative, it triggers the boss's death sequence.
|
||||
* **`ReturnTotalHealth`:** Calculates the combined health of both Octobosses (`SprHealth, X` and `SprHealth, Y` of `BrotherSpr`).
|
||||
* **`CheckForNextPhase`:** Manages the boss's phases based on health thresholds, similar to the Kydrog boss.
|
||||
|
||||
## Offspring Spawning (`RandomStalfosOffspring`, `Sprite_Offspring_Spawn`, `Sprite_Offspring_SpawnHead`):
|
||||
* These routines are identical to those found in `kydrog_boss.asm`, spawning stalfos offspring.
|
||||
|
||||
## Attacks (`Chuchu_SpawnBlast`, `Mothula_SpawnBeams`, `Kydrog_ThrowBoneAtPlayer`):
|
||||
* **`Chuchu_SpawnBlast`:** Spawns a Chuchu blast projectile.
|
||||
* **`Mothula_SpawnBeams`:** Spawns beam projectiles.
|
||||
* **`Kydrog_ThrowBoneAtPlayer`:** Spawns a bone projectile (reused from Kydrog).
|
||||
|
||||
## Drawing (`Sprite_Octoboss_Draw`, `Sprite_Octoboss_Draw2`):
|
||||
* **`Sprite_Octoboss_Draw`:** Draws the primary Octoboss.
|
||||
* **`Sprite_Octoboss_Draw2`:** Draws the secondary/brother Octoboss.
|
||||
* Both use standard OAM allocation routines and handle complex animation frames, offsets, character data, properties, and sizes.
|
||||
|
||||
## Other Routines:
|
||||
* **`SpawnSplash`:** Spawns a splash effect.
|
||||
* **`SpawnBossPoof`:** Spawns a boss poof effect.
|
||||
* **`HandleMovingSplash`:** Handles splash effects during movement.
|
||||
* **`SpawnMedallion` / `SpawnMedallionAlt`:** Spawns a medallion.
|
||||
|
||||
## Discrepancies/Notes:
|
||||
* **Shared Variables/Code with Kydrog:** The extensive use of `!KydrogPhase`, `KydrogBoss_Set_Damage`, and `Kydrog_ThrowBoneAtPlayer` suggests significant code reuse or copy-pasting from the Kydrog boss. This could lead to unexpected interactions or make maintenance challenging if changes are made to one boss but not the other. Refactoring shared logic into common functions or macros would be beneficial.
|
||||
* **`Sprite_Octoboss_Secondary`:** The existence of a separate main routine for the "brother" Octoboss indicates that the two Octobosses might have slightly different behaviors or phases.
|
||||
|
||||
## Hardcoded Activation Trigger:
|
||||
* As noted by the user, the activation trigger for Octoboss is hardcoded. In the `WaitForPlayerToApproach` routine, the boss checks `LDA.b $20 : CMP #$08C8`. `$20` represents Link's Y position, and `$08C8` is a hardcoded Y-coordinate. This means the boss will only activate when Link reaches this specific Y-coordinate, making it difficult to relocate the boss to other overworld maps without modifying this value. This hardcoded dependency needs to be addressed for improved reusability.
|
||||
@@ -1,547 +0,0 @@
|
||||
# Octorok
|
||||
|
||||
## Overview
|
||||
The Octorok sprite (`!SPRID = $08`) is a complex enemy implementation that supports both land-based and water-based variations. It features dynamic behavior, including transformation between forms, distinct movement patterns, and projectile attacks.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `$08` (Vanilla sprite ID for Octorok)
|
||||
* **`!NbrTiles`**: `05`
|
||||
* **`!Harmless`**: `00`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00` (Health is likely set dynamically or is vanilla)
|
||||
* **`!Damage`**: `00` (Damage is likely from projectiles)
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00` (Shadow is drawn conditionally in `_Long`)
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Octorok_Long`)
|
||||
This routine acts as a dispatcher, determining whether to execute Land Octorok or Water Octorok logic based on `SprSubtype`.
|
||||
|
||||
```asm
|
||||
Sprite_Octorok_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_Octorok_Draw
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
LDA.w SprSubtype, X : BEQ +
|
||||
JSL Sprite_DrawWaterRipple
|
||||
JSR Sprite_WaterOctorok_Main
|
||||
JMP ++
|
||||
+
|
||||
JSL Sprite_DrawShadow
|
||||
JSR Sprite_Octorok_Main
|
||||
++
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Octorok_Prep`)
|
||||
This routine is currently empty, indicating that initial setup is minimal or handled by vanilla routines.
|
||||
|
||||
## Land Octorok Main Logic (`Sprite_Octorok_Main`)
|
||||
This routine handles the behavior of a land-based Octorok, including movement and potential transformation into a Water Octorok.
|
||||
|
||||
* **Movement**: Calls `Sprite_Octorok_Move` for general movement.
|
||||
* **Transformation**: Checks the tile type the Octorok is on. If it's a water tile, the Octorok transforms into a Water Octorok by setting `SprSubtype, X` to `01`.
|
||||
* **Directional States**: Uses a jump table to manage animations for moving in different directions (Down, Up, Left, Right).
|
||||
|
||||
```asm
|
||||
Sprite_Octorok_Main:
|
||||
{
|
||||
JSR Sprite_Octorok_Move
|
||||
|
||||
; TILETYPE 08
|
||||
LDA.l $7FF9C2,X : CMP.b #$08 : BEQ .water_tile
|
||||
; TILETYPE 09
|
||||
CMP.b #$09 : BNE .not_water_tile
|
||||
.water_tile
|
||||
LDA.b #$01 : STA.w SprSubtype, X
|
||||
STZ.w SprAction, X
|
||||
STZ.w SprMiscG, X
|
||||
RTS
|
||||
.not_water_tile
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Octorok_MoveDown
|
||||
dw Octorok_MoveUp
|
||||
dw Octorok_MoveLeft
|
||||
dw Octorok_MoveRight
|
||||
|
||||
Octorok_MoveDown:
|
||||
{
|
||||
%PlayAnimation(0,1,10)
|
||||
RTS
|
||||
}
|
||||
|
||||
Octorok_MoveUp:
|
||||
{
|
||||
%StartOnFrame(2)
|
||||
%PlayAnimation(2,3,10)
|
||||
RTS
|
||||
}
|
||||
|
||||
Octorok_MoveLeft:
|
||||
{
|
||||
%StartOnFrame(4)
|
||||
%PlayAnimation(4,5,10)
|
||||
RTS
|
||||
}
|
||||
|
||||
Octorok_MoveRight:
|
||||
{
|
||||
%StartOnFrame(6)
|
||||
%PlayAnimation(6,7,10)
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Octorok Movement (`Sprite_Octorok_Move`)
|
||||
This shared routine handles the Octorok's general movement, damage reactions, and tile collision.
|
||||
|
||||
* **Damage & Collision**: Handles damage flash (`Sprite_DamageFlash_Long`), movement (`Sprite_Move`), and checks for damage to/from Link.
|
||||
* **Directional Logic**: Sets the sprite's action based on its direction (`SprMiscC, X`).
|
||||
* **Tile Collision**: Detects tile collisions and changes the Octorok's direction accordingly.
|
||||
* **Barrage Logic**: Contains logic related to a potential projectile barrage (`octorok_used_barrage`).
|
||||
|
||||
```asm
|
||||
Sprite_Octorok_Move:
|
||||
{
|
||||
JSL Sprite_DamageFlash_Long
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
|
||||
; Set the SprAction based on the direction
|
||||
LDA.w SprMiscC, X : AND.b #$03 : TAY
|
||||
LDA.w .direction, Y : STA.w SprAction, X
|
||||
|
||||
LDA.w SprMiscF, X : AND.b #$01 : BNE .octorok_used_barrage
|
||||
LDA.w SprMiscC, X : AND.b #$02 : ASL A : STA.b $00
|
||||
INC.w SprDelay, X
|
||||
LDA.w SprDelay, X
|
||||
LSR A
|
||||
LSR A
|
||||
LSR A
|
||||
AND.b #$03
|
||||
ORA.b $00
|
||||
STA.w SprGfx, X
|
||||
|
||||
LDA.w SprTimerA, X : BNE .wait
|
||||
INC.w SprMiscF,X
|
||||
|
||||
LDY.w SprType,X
|
||||
LDA.w .timer-8,Y : STA.w SprTimerA,X
|
||||
|
||||
RTS
|
||||
|
||||
.wait
|
||||
LDY.w SprMiscC, X
|
||||
|
||||
LDA.w .speed_x, Y : STA.w SprXSpeed, X
|
||||
LDA.w .speed_y, Y : STA.w SprYSpeed, X
|
||||
|
||||
JSL Sprite_CheckTileCollision
|
||||
LDA.w $0E70, X : BEQ .no_collision
|
||||
LDA.w SprMiscC,X : EOR.b #$01 : STA.w SprMiscC,X
|
||||
BRA .exit
|
||||
.no_collision
|
||||
RTS
|
||||
|
||||
.octorok_used_barrage
|
||||
STZ.w SprXSpeed, X : STZ.w SprYSpeed,X
|
||||
LDA.w SprTimerA, X : BNE Octorock_ShootEmUp
|
||||
INC.w SprMiscF, X
|
||||
LDA.w SprMiscC, X
|
||||
PHA
|
||||
JSL GetRandomInt : AND.b #$3F : ADC.b #$30 : STA.w SprTimerA, X
|
||||
AND.b #$03 : STA.w SprMiscC, X
|
||||
PLA
|
||||
CMP.w SprMiscC, X : BEQ .exit
|
||||
EOR.w SprMiscC, X : BNE .exit
|
||||
LDA.b #$08 : STA.w SprTimerB,X
|
||||
.exit
|
||||
RTS
|
||||
|
||||
.direction
|
||||
db 3, 2, 0, 1
|
||||
|
||||
.speed_x
|
||||
db 24, -24, 0, 0
|
||||
|
||||
.speed_y
|
||||
db 0, 0, 24, -24
|
||||
|
||||
.timer
|
||||
db 60, 128, 160, 128
|
||||
}
|
||||
```
|
||||
|
||||
## Octorok Projectile Logic (`Octorock_ShootEmUp`)
|
||||
This routine determines the Octorok's shooting behavior, allowing for both single-shot and four-way attacks.
|
||||
|
||||
```asm
|
||||
Octorock_ShootEmUp:
|
||||
{
|
||||
; Use SprMiscD as a flag to shoot 4 ways for awhile before going back to single shot
|
||||
|
||||
LDA.w SprMiscD, X : BEQ .continue
|
||||
LDA.w SprTimerD, X : BNE .four_ways
|
||||
LDA.b #$01 : STA.w SprMiscD, X
|
||||
.continue
|
||||
JSL GetRandomInt : AND.b #$1F : BNE .single_shot
|
||||
.four_ways
|
||||
LDA.b #$01 : STA.w SprMiscD, X
|
||||
LDA.b #$20 : STA.w SprTimerD, X
|
||||
JSR Octorok_Shoot4Ways
|
||||
RTS
|
||||
.single_shot
|
||||
JSR Octorok_ShootSingle
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Water Octorok Main Logic (`Sprite_WaterOctorok_Main`)
|
||||
This routine governs the behavior of a water-based Octorok, including its attack patterns and states.
|
||||
|
||||
* **Attack**: Calls `Sprite_WaterOctorok_Attack`.
|
||||
* **Facing Directions**: Uses a jump table to manage animations for facing different directions (Down, Up, Left, Right) and a hidden state.
|
||||
|
||||
```asm
|
||||
Sprite_WaterOctorok_Main:
|
||||
{
|
||||
JSR Sprite_WaterOctorok_Attack
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw WaterOctorok_FaceDown
|
||||
dw WaterOctorok_FaceUp
|
||||
dw WaterOctorok_FaceLeft
|
||||
dw WaterOctorok_FaceRight
|
||||
dw WaterOctorok_FaceHidden
|
||||
|
||||
WaterOctorok_FaceDown:
|
||||
{
|
||||
%PlayAnimation(0,1,10)
|
||||
RTS
|
||||
}
|
||||
|
||||
WaterOctorok_FaceUp:
|
||||
{
|
||||
%StartOnFrame(2)
|
||||
%PlayAnimation(2,3,10)
|
||||
RTS
|
||||
}
|
||||
|
||||
WaterOctorok_FaceLeft:
|
||||
{
|
||||
%StartOnFrame(4)
|
||||
%PlayAnimation(4,5,10)
|
||||
RTS
|
||||
}
|
||||
|
||||
WaterOctorok_FaceRight:
|
||||
{
|
||||
%StartOnFrame(6)
|
||||
%PlayAnimation(6,7,10)
|
||||
RTS
|
||||
}
|
||||
|
||||
WaterOctorok_FaceHidden:
|
||||
{
|
||||
%StartOnFrame(8)
|
||||
%PlayAnimation(8,8,10)
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Water Octorok Attack Logic (`Sprite_WaterOctorok_Attack`)
|
||||
This routine manages the Water Octorok's attack states, including hiding, emerging, attacking, and re-hiding.
|
||||
|
||||
* **States**: Uses `SprMiscG, X` as a state machine for `WaterOctorok_Hidden`, `WaterOctorok_PoppingUp`, `WaterOctorok_Attacking`, and `WaterOctorok_Hiding`.
|
||||
* **`WaterOctorok_Hidden`**: Remains hidden until Link is within a certain distance, then transitions to `WaterOctorok_PoppingUp`.
|
||||
* **`WaterOctorok_PoppingUp`**: Emerges from the water, faces Link, and then transitions to `WaterOctorok_Attacking`.
|
||||
* **`WaterOctorok_Attacking`**: Shoots a single projectile (`Octorok_ShootSingle`) after a timer, then transitions to `WaterOctorok_Hiding`.
|
||||
* **`WaterOctorok_Hiding`**: Hides back in the water and transitions to `WaterOctorok_Hidden`.
|
||||
|
||||
```asm
|
||||
Sprite_WaterOctorok_Attack:
|
||||
{
|
||||
JSL Sprite_DamageFlash_Long
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
|
||||
LDA.w SprMiscG, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw WaterOctorok_Hidden
|
||||
dw WaterOctorok_PoppingUp
|
||||
dw WaterOctorok_Attacking
|
||||
dw WaterOctorok_Hiding
|
||||
|
||||
WaterOctorok_Hidden:
|
||||
{
|
||||
LDA.w SprTimerA, X : BEQ +
|
||||
RTS
|
||||
+
|
||||
|
||||
JSL GetDistance8bit_Long
|
||||
CMP.b #$40 : BCC .not_close_enough ; LD < 64
|
||||
INC.w SprMiscG, X
|
||||
%SetTimerA($10)
|
||||
.not_close_enough
|
||||
RTS
|
||||
}
|
||||
|
||||
WaterOctorok_PoppingUp:
|
||||
{
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
INC.w SprMiscG, X
|
||||
%SetTimerA($20)
|
||||
JSL Sprite_DirectionToFacePlayer
|
||||
; LDA.w SprMiscC, X : AND.b #$03 : TAY
|
||||
; LDA.w Sprite_Octorok_Move_direction, Y : STA.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
WaterOctorok_Attacking:
|
||||
{
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
INC.w SprMiscG, X
|
||||
%SetTimerA($10)
|
||||
RTS
|
||||
+
|
||||
JSR Octorok_ShootSingle
|
||||
RTS
|
||||
}
|
||||
|
||||
WaterOctorok_Hiding:
|
||||
{
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
LDA.b #$04 : STA.w SprAction, X
|
||||
STZ.w SprMiscG, X
|
||||
%SetTimerA($40)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Projectile Spawning (`Octorok_ShootSingle`, `Octorok_Shoot4Ways`, `Octorok_SpawnRock`)
|
||||
These routines handle the spawning and animation of Octorok projectiles.
|
||||
|
||||
* **`Octorok_ShootSingle`**: Manages the animation and timing for shooting a single rock projectile.
|
||||
* **`Octorok_Shoot4Ways`**: Manages the animation, timing, and direction changes for shooting rock projectiles in four cardinal directions.
|
||||
* **`Octorok_SpawnRock`**: Spawns a rock projectile (sprite ID `$0C`) with specific initial offsets and speeds based on the Octorok's current direction.
|
||||
|
||||
```asm
|
||||
Octorok_ShootSingle:
|
||||
{
|
||||
LDA.w SprTimerA, X : CMP.b #$1C : BNE .bide_time
|
||||
PHA
|
||||
JSR Octorok_SpawnRock
|
||||
PLA
|
||||
.bide_time
|
||||
LSR #3
|
||||
TAY
|
||||
LDA.w .mouth_anim_step, Y : STA.w SprMiscB, X
|
||||
RTS
|
||||
|
||||
.mouth_anim_step
|
||||
db $00, $02, $02, $02
|
||||
db $01, $01, $01, $00
|
||||
db $00, $00, $00, $00
|
||||
db $02, $02, $02, $02
|
||||
db $02, $01, $01, $00
|
||||
}
|
||||
|
||||
Octorok_Shoot4Ways:
|
||||
{
|
||||
LDA.w SprTimerA, X
|
||||
PHA
|
||||
CMP.b #$80 : BCS .animate
|
||||
AND.b #$0F : BNE .delay_turn
|
||||
PHA
|
||||
LDY.w SprMiscC, X
|
||||
LDA.w .next_direction, Y : STA.w SprMiscC, X
|
||||
PLA
|
||||
.delay_turn
|
||||
CMP.b #$08 : BNE .animate
|
||||
JSR Octorok_SpawnRock
|
||||
.animate
|
||||
PLA
|
||||
LSR #4
|
||||
TAY
|
||||
LDA.w .mouth_anim_step, Y : STA.w SprMiscB, X
|
||||
RTS
|
||||
|
||||
.next_direction
|
||||
db $02, $03, $01, $00
|
||||
|
||||
.mouth_anim_step
|
||||
db $02, $02, $02, $02
|
||||
db $02, $02, $02, $02
|
||||
db $01, $00
|
||||
}
|
||||
|
||||
Octorok_SpawnRock:
|
||||
{
|
||||
LDA.b #$07 : JSL SpriteSFX_QueueSFX2WithPan
|
||||
LDA.b #$0C : JSL Sprite_SpawnDynamically : BMI .fired_a_blank
|
||||
PHX
|
||||
|
||||
LDA.w SprMiscC,X
|
||||
TAX
|
||||
|
||||
LDA.b $00 : CLC : ADC.w .offset_x_low,X : STA.w SprX,Y
|
||||
LDA.b $01 : ADC.w .offset_x_high,X : STA.w SprXH,Y
|
||||
LDA.b $02 : CLC : ADC.w .offset_y_low,X : STA.w SprY,Y
|
||||
LDA.b $03 : ADC.w .offset_y_high,X : STA.w SprYH,Y
|
||||
|
||||
LDA.w SprMiscC,Y
|
||||
TAX
|
||||
|
||||
LDA.w .rock_speed_x,X : STA.w SprXSpeed,Y
|
||||
LDA.w .rock_speed_y,X : STA.w SprYSpeed,Y
|
||||
|
||||
PLX
|
||||
.fired_a_blank
|
||||
RTS
|
||||
|
||||
.offset_x_low
|
||||
db 12, -12, 0, 0
|
||||
|
||||
.offset_x_high
|
||||
db 0, -1, 0, 0
|
||||
|
||||
.offset_y_low
|
||||
db 4, 4, 12, -12
|
||||
|
||||
.offset_y_high
|
||||
db 0, 0, 0, -1
|
||||
|
||||
.rock_speed_x
|
||||
db 44, -44, 0, 0
|
||||
|
||||
.rock_speed_y
|
||||
db 0, 0, 44, -44
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_Octorok_Draw`)
|
||||
The drawing routine handles OAM allocation, animation, and palette adjustments. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations.
|
||||
|
||||
```asm
|
||||
Sprite_Octorok_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprFrame, X : TAY ;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
LDA.w SprFlash : STA $08
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : ORA $08 : STA ($90), Y
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA.b #$02 : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
|
||||
; =========================================================
|
||||
|
||||
.start_index
|
||||
db $00, $01, $02, $03, $04, $05, $06, $07, $08
|
||||
.nbr_of_tiles
|
||||
db 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
.chr
|
||||
db $80
|
||||
db $80
|
||||
db $82
|
||||
db $82
|
||||
db $A0
|
||||
db $A2
|
||||
db $A0
|
||||
db $A2
|
||||
db $AA ; Water Octorok
|
||||
.properties
|
||||
db $0D
|
||||
db $4D
|
||||
db $0D
|
||||
db $4D
|
||||
db $0D
|
||||
db $0D
|
||||
db $4D
|
||||
db $4D
|
||||
db $3D ; Water Octorok
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Subtype-based Behavior**: The Octorok utilizes `SprSubtype` to implement distinct behaviors for Land and Water Octoroks, including different main logic routines and conditional drawing (shadow vs. water ripple).
|
||||
* **Dynamic Transformation**: A Land Octorok can dynamically transform into a Water Octorok if it moves onto a water tile, showcasing a unique environmental interaction.
|
||||
* **Complex State Machines**: Both Land and Water Octoroks employ intricate state machines to manage their movement, attack patterns, and emerging/hiding behaviors, making them engaging enemies.
|
||||
* **Projectile Attacks**: The Octorok can perform both single-shot and four-way projectile attacks, adding variety to its offensive capabilities.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering and positioning.
|
||||
@@ -1,221 +0,0 @@
|
||||
# Pols Voice Sprite Analysis
|
||||
|
||||
This document provides a detailed analysis of the `pols_voice.asm` sprite, outlining its properties, core routines, and behavioral patterns.
|
||||
|
||||
## 1. Sprite Properties
|
||||
|
||||
The following `!SPRID` constants define Pols Voice's fundamental characteristics:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_PolsVoice
|
||||
!NbrTiles = 02 ; Number of tiles used in a frame
|
||||
!Harmless = 00 ; 00 = Sprite is Harmful, 01 = Sprite is Harmless
|
||||
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
||||
!Health = 10 ; Number of Health the sprite have
|
||||
!Damage = 00 ; (08 is a whole heart), 04 is half heart
|
||||
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
||||
!ImperviousAll = 00 ; 00 = Can be attack, 01 = attack will clink on it
|
||||
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
||||
!Shadow = 00 ; 00 = don't draw shadow, 01 = draw a shadow
|
||||
!Palette = 00 ; Unused in this template (can be 0 to 7)
|
||||
!Hitbox = 00 ; 00 to 31, can be viewed in sprite draw tool
|
||||
!Persist = 00 ; 01 = your sprite continue to live offscreen
|
||||
!Statis = 00 ; 00 = is sprite is alive?, (kill all enemies room)
|
||||
!CollisionLayer = 00 ; 01 = will check both layer for collision
|
||||
!CanFall = 00 ; 01 sprite can fall in hole, 01 = can't fall
|
||||
!DeflectArrow = 00 ; 01 = deflect arrows
|
||||
!WaterSprite = 00 ; 01 = can only walk shallow water
|
||||
!Blockable = 00 ; 01 = can be blocked by link's shield?
|
||||
!Prize = 00 ; 00-15 = the prize pack the sprite will drop from
|
||||
!Sound = 00 ; 01 = Play different sound when taking damage
|
||||
!Interaction = 00 ; ?? No documentation
|
||||
!Statue = 00 ; 01 = Sprite is statue
|
||||
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
||||
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
||||
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
||||
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
||||
```
|
||||
**Note:** `!Health` is set to `10` and is not dynamically determined by Link's sword level.
|
||||
|
||||
## 2. Core Routines
|
||||
|
||||
### 2.1. `Sprite_PolsVoice_Long` (Main Loop)
|
||||
|
||||
This is the primary entry point for Pols Voice's per-frame execution. It handles drawing, shadow rendering, and then dispatches to the main logic routine if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_PolsVoice_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_PolsVoice_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_PolsVoice_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. `Sprite_PolsVoice_Prep` (Initialization)
|
||||
|
||||
This routine is executed once when Pols Voice is first spawned. It initializes `SprTimerA` to `$80` and clears `SprDefl` and `SprTileDie`.
|
||||
|
||||
```asm
|
||||
Sprite_PolsVoice_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$80 : STA.w SprTimerA, X
|
||||
STZ.w SprDefl, X
|
||||
STZ.w SprTileDie, X
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3. `Sprite_PolsVoice_Main` (Behavioral State Machine)
|
||||
|
||||
This routine manages Pols Voice's AI through a state machine, using `SprAction, X` to determine its current behavior. It includes states for moving around and hopping around, with a unique interaction based on the flute song.
|
||||
|
||||
```asm
|
||||
Sprite_PolsVoice_Main:
|
||||
{
|
||||
JSR PolsVoice_CheckForFluteSong ; Check for flute song interaction
|
||||
|
||||
%SpriteJumpTable(PolsVoice_MoveAround,
|
||||
PolsVoice_HopAround)
|
||||
|
||||
PolsVoice_MoveAround:
|
||||
{
|
||||
%StartOnFrame(0)
|
||||
%PlayAnimation(0,3,10)
|
||||
|
||||
;$09 = speed, $08 = max height
|
||||
LDA #$05 : STA $09
|
||||
LDA #$02 : STA $08
|
||||
JSL Sprite_BounceTowardPlayer
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
JSL Sprite_DamageFlash_Long
|
||||
|
||||
%DoDamageToPlayerSameLayerOnContact()
|
||||
|
||||
JSL GetRandomInt : AND #$3F : BNE .not_done ; Random chance to change state
|
||||
LDA #$04 : STA.w SprTimerA, X
|
||||
%GotoAction(1) ; Transition to PolsVoice_HopAround
|
||||
.not_done
|
||||
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_damage ; Check if Link damages Pols Voice
|
||||
JSL Sprite_DirectionToFacePlayer
|
||||
|
||||
; Apply the speed positive or negative speed
|
||||
LDA $0E : BPL .not_up
|
||||
LDA #$20 : STA.w SprYSpeed, X
|
||||
BRA .not_down
|
||||
.not_up
|
||||
LDA #$E0 : STA.w SprYSpeed, X
|
||||
.not_down
|
||||
LDA $0F : BPL .not_right
|
||||
LDA #$20 : STA.w SprXSpeed, X
|
||||
BRA .not_left
|
||||
.not_right
|
||||
LDA #$E0 : STA.w SprXSpeed, X
|
||||
.not_left
|
||||
LDA #$04 : STA.w SprTimerA, X
|
||||
%GotoAction(1) ; Transition to PolsVoice_HopAround
|
||||
.no_damage
|
||||
RTS
|
||||
}
|
||||
|
||||
PolsVoice_HopAround:
|
||||
{
|
||||
%StartOnFrame(4)
|
||||
%PlayAnimation(4,4,10)
|
||||
|
||||
JSL Sprite_MoveXyz
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
JSL Sprite_DamageFlash_Long
|
||||
|
||||
%DoDamageToPlayerSameLayerOnContact()
|
||||
|
||||
LDA.w SprTimerA, X : BNE .not_done ; If timer A is not 0
|
||||
%GotoAction(0) ; Transition back to PolsVoice_MoveAround
|
||||
.not_done
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_damage ; Check if Link damages Pols Voice
|
||||
JSL Sprite_InvertSpeed_XY ; Invert speed
|
||||
.no_damage
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4. `PolsVoice_CheckForFluteSong`
|
||||
|
||||
This routine checks if the player is currently playing the flute (`SongFlag`). If the flute is being played, Pols Voice despawns (`STZ.w SprState, X`) and forces a prize drop.
|
||||
|
||||
```asm
|
||||
PolsVoice_CheckForFluteSong:
|
||||
{
|
||||
; If the player plays the flute
|
||||
LDA.b SongFlag : BEQ + ; Check SongFlag
|
||||
LDA.b #$03 : STA.w SprState, X ; Set sprite state to despawn
|
||||
JSL ForcePrizeDrop_long ; Force prize drop
|
||||
+
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5. `Sprite_PolsVoice_Draw` (Drawing Routine)
|
||||
|
||||
This routine is responsible for rendering Pols Voice's graphics. It uses the `%DrawSprite()` macro, which reads from a set of data tables to handle its appearance and animation.
|
||||
|
||||
```asm
|
||||
Sprite_PolsVoice_Draw:
|
||||
{
|
||||
%DrawSprite()
|
||||
|
||||
.start_index
|
||||
db $00, $01, $02, $03, $04
|
||||
.nbr_of_tiles
|
||||
db 0, 0, 0, 0, 1
|
||||
.x_offsets
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0, 0
|
||||
.y_offsets
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw -4, -20
|
||||
.chr
|
||||
db $6C
|
||||
db $6A
|
||||
db $6C
|
||||
db $6A
|
||||
db $6E, $4E
|
||||
.properties
|
||||
db $3B
|
||||
db $3B
|
||||
db $3B
|
||||
db $7B
|
||||
db $3B, $3B
|
||||
.sizes
|
||||
db $02
|
||||
db $02
|
||||
db $02
|
||||
db $02
|
||||
db $02, $02
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Key Behaviors and Implementation Details
|
||||
|
||||
* **Fixed Health:** Unlike many other sprites, Pols Voice has a fixed health of `10` and its health is not dynamically scaled based on Link's sword level.
|
||||
* **State Management:** Pols Voice uses `SprAction, X` and `%SpriteJumpTable` to manage its `PolsVoice_MoveAround` and `PolsVoice_HopAround` states. Transitions between these states are triggered by timers or random chance.
|
||||
* **Movement Patterns:** Pols Voice moves by bouncing towards the player (`Sprite_BounceTowardPlayer`) and also has a hopping movement (`PolsVoice_HopAround`). It reacts to tile collisions by bouncing (`Sprite_BounceFromTileCollision`).
|
||||
* **Flute Song Interaction:** A unique and defining characteristic of Pols Voice is its vulnerability to the flute song. When Link plays the flute (`SongFlag` is set), Pols Voice immediately despawns and drops a prize (`ForcePrizeDrop_long`). This is a classic Zelda enemy mechanic.
|
||||
* **Damage Reaction:** When damaged by Link, Pols Voice inverts its speed (`Sprite_InvertSpeed_XY`) and transitions to the `PolsVoice_HopAround` state, providing a temporary reprieve or change in behavior.
|
||||
* **Custom OAM Drawing:** Pols Voice uses the `%DrawSprite()` macro with OAM data tables to render its appearance and animations.
|
||||
* **`SprTimerA` Usage:** This timer controls the duration of the `PolsVoice_HopAround` state before transitioning back to `PolsVoice_MoveAround`.
|
||||
@@ -1,233 +0,0 @@
|
||||
# Poltergeist Sprite Analysis
|
||||
|
||||
This document provides a detailed analysis of the `poltergeist.asm` sprite, outlining its properties, core routines, and behavioral patterns.
|
||||
|
||||
## 1. Sprite Properties
|
||||
|
||||
The following `!SPRID` constants define Poltergeist's fundamental characteristics:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_PolsVoice
|
||||
!NbrTiles = 02 ; Number of tiles used in a frame
|
||||
!Harmless = 00 ; 00 = Sprite is Harmful, 01 = Sprite is Harmless
|
||||
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
||||
!Health = 10 ; Number of Health the sprite have
|
||||
!Damage = 00 ; (08 is a whole heart), 04 is half heart
|
||||
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
||||
!ImperviousAll = 00 ; 00 = Can be attack, 01 = attack will clink on it
|
||||
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
||||
!Shadow = 00 ; 00 = don't draw shadow, 01 = draw a shadow
|
||||
!Palette = 00 ; Unused in this template (can be 0 to 7)
|
||||
!Hitbox = 00 ; 00 to 31, can be viewed in sprite draw tool
|
||||
!Persist = 00 ; 01 = your sprite continue to live offscreen
|
||||
!Statis = 00 ; 00 = is sprite is alive?, (kill all enemies room)
|
||||
!CollisionLayer = 00 ; 01 = will check both layer for collision
|
||||
!CanFall = 00 ; 01 sprite can fall in hole, 01 = can't fall
|
||||
!DeflectArrow = 00 ; 01 = deflect arrows
|
||||
!WaterSprite = 00 ; 01 = can only walk shallow water
|
||||
!Blockable = 00 ; 01 = can be blocked by link's shield?
|
||||
!Prize = 00 ; 00-15 = the prize pack the sprite will drop from
|
||||
!Sound = 00 ; 01 = Play different sound when taking damage
|
||||
!Interaction = 00 ; ?? No documentation
|
||||
!Statue = 00 ; 01 = Sprite is statue
|
||||
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
||||
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
||||
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
||||
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
||||
```
|
||||
**Note:** `!Health` is set to `10` and is dynamically determined during initialization based on Link's sword level.
|
||||
|
||||
## 2. Core Routines
|
||||
|
||||
### 2.1. `Sprite_Poltergeist_Long` (Main Loop)
|
||||
|
||||
This is the primary entry point for Poltergeist's per-frame execution. It handles drawing, shadow rendering, and then dispatches to the main logic routine if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Poltergeist_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_Poltergeist_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Poltergeist_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. `Sprite_Poltergeist_Prep` (Initialization)
|
||||
|
||||
This routine is executed once when Poltergeist is first spawned. It sets its health based on Link's sword level and initializes `SprTimerA` and `SprTimerB`.
|
||||
|
||||
```asm
|
||||
Sprite_Poltergeist_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.l Sword : DEC A : TAY
|
||||
LDA.w .health, Y : STA.w SprHealth, X
|
||||
LDA.b #$80 : STA.w SprTimerA, X
|
||||
LDA.b #$80 : STA.w SprTimerB, X
|
||||
PLB
|
||||
RTL
|
||||
|
||||
.health
|
||||
db $06, $0A, $0C, $10
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3. `Sprite_Poltergeist_Main` (Behavioral State Machine)
|
||||
|
||||
This routine manages Poltergeist's AI through a state machine, using `SprAction, X` to determine its current behavior. It includes states for moving, attacking, and being stunned.
|
||||
|
||||
```asm
|
||||
Sprite_Poltergeist_Main:
|
||||
{
|
||||
JSL Sprite_DamageFlash_Long
|
||||
|
||||
%SpriteJumpTable(Poltergeist_Move,
|
||||
Poltergeist_Attack,
|
||||
Poltergeist_Stunned)
|
||||
|
||||
Poltergeist_Move:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
JSR Sprite_Poltergeist_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
Poltergeist_Attack:
|
||||
{
|
||||
%PlayAnimation(2, 3, 16)
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
LDA.w SprTimerA, X : BNE + ; If timer A is not 0
|
||||
%GotoAction(0) ; Transition back to Poltergeist_Move
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Poltergeist_Stunned:
|
||||
{
|
||||
%PlayAnimation(4, 5, 16)
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
LDA.w SprTimerA, X : BNE + ; If timer A is not 0
|
||||
%GotoAction(0) ; Transition back to Poltergeist_Move
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4. `Sprite_Poltergeist_Move` (Movement Logic)
|
||||
|
||||
This routine handles Poltergeist's movement patterns, including moving towards Link, bouncing from tile collisions, and changing direction randomly.
|
||||
|
||||
```asm
|
||||
Sprite_Poltergeist_Move:
|
||||
{
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
JSL Sprite_CheckIfRecoiling
|
||||
|
||||
LDA.w SprTimerC, X : BNE ++ ; Check timer C
|
||||
JSL GetRandomInt : AND #$3F : BNE ++ ; Random chance to change direction
|
||||
LDA.b #$40 : STA.w SprTimerC, X
|
||||
JSL Sprite_SelectNewDirection
|
||||
++
|
||||
|
||||
LDA.w SprTimerA, X : BNE + ; Check timer A
|
||||
JSL Sprite_IsToRightOfPlayer : CPY.b #$01 : BNE .ToRight
|
||||
%GotoAction(1) ; Transition to Poltergeist_Attack
|
||||
JMP .Continue
|
||||
.ToRight
|
||||
%GotoAction(1) ; Transition to Poltergeist_Attack
|
||||
LDA.b #$20 : STA.w SprTimerA, X
|
||||
JMP .Continue
|
||||
+
|
||||
%GotoAction(0) ; Transition to Poltergeist_Move
|
||||
.Continue
|
||||
|
||||
LDA.w SprMiscB, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw PoltergeistMove
|
||||
|
||||
PoltergeistMove:
|
||||
{
|
||||
JSL GetRandomInt : AND.b #$03
|
||||
JSL Sprite_ApplySpeedTowardsPlayer
|
||||
JSL Sprite_CheckTileCollision
|
||||
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5. `Sprite_Poltergeist_Draw` (Drawing Routine)
|
||||
|
||||
This routine is responsible for rendering Poltergeist's graphics. It uses the `%DrawSprite()` macro, which reads from a set of data tables to handle its multi-tile appearance and animation.
|
||||
|
||||
```asm
|
||||
Sprite_Poltergeist_Draw:
|
||||
{
|
||||
%DrawSprite()
|
||||
|
||||
.start_index
|
||||
db $00, $03, $06, $09, $0C, $0F
|
||||
.nbr_of_tiles
|
||||
db 2, 2, 2, 2, 2, 2
|
||||
.x_offsets
|
||||
dw 0, 0, 8
|
||||
dw 8, 0, 0
|
||||
dw 0, 0, 8
|
||||
dw 0, 0, 8
|
||||
dw 0, 8, 0
|
||||
dw 0, 8, 0
|
||||
.y_offsets
|
||||
dw -8, 0, -8
|
||||
dw -8, 0, -8
|
||||
dw 0, -8, -8
|
||||
dw 0, -8, -8
|
||||
dw 0, -8, -8
|
||||
dw 0, -8, -8
|
||||
.chr
|
||||
db $3A, $02, $3B
|
||||
db $3A, $02, $3B
|
||||
db $20, $00, $01
|
||||
db $22, $10, $11
|
||||
db $20, $00, $01
|
||||
db $22, $10, $11
|
||||
.properties
|
||||
db $2B, $2B, $2B
|
||||
db $6B, $6B, $6B
|
||||
db $2B, $2B, $2B
|
||||
db $2B, $2B, $2B
|
||||
db $6B, $6B, $6B
|
||||
db $6B, $6B, $6B
|
||||
.sizes
|
||||
db $00, $02, $00
|
||||
db $00, $02, $00
|
||||
db $02, $00, $00
|
||||
db $02, $00, $00
|
||||
db $02, $00, $00
|
||||
db $02, $00, $00
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Key Behaviors and Implementation Details
|
||||
|
||||
* **Dynamic Health:** Poltergeist's health is determined at spawn time based on Link's current sword level, allowing for dynamic difficulty scaling.
|
||||
* **State Management:** Poltergeist uses `SprAction, X` and `%SpriteJumpTable` to manage its `Poltergeist_Move`, `Poltergeist_Attack`, and `Poltergeist_Stunned` states. Transitions between these states are triggered by timers and player proximity.
|
||||
* **Movement Patterns:** Poltergeist moves towards Link (`Sprite_ApplySpeedTowardsPlayer`) with random direction changes (`Sprite_SelectNewDirection`). It also handles bouncing from tile collisions and cannot be passed through by Link.
|
||||
* **Attack Behavior:** Poltergeist transitions to an `Poltergeist_Attack` state, which likely involves a direct contact attack or a projectile, and then returns to its movement state after a timer.
|
||||
* **Stunned State:** When damaged, Poltergeist enters a `Poltergeist_Stunned` state, during which it is temporarily incapacitated. It recovers from this state after a timer.
|
||||
* **Conditional Invulnerability:** The sprite properties indicate `!ImpervSwordHammer = 00`, but the code does not explicitly set it to `01` when stunned. This might be an oversight or handled by a global routine. However, the presence of `SprDefl` in `_Prep` suggests some form of deflection is intended.
|
||||
* **Custom OAM Drawing:** Poltergeist uses the `%DrawSprite()` macro with detailed OAM data tables to render its multi-tile appearance and animations across its different states.
|
||||
* **`SprTimerA`, `SprTimerB`, `SprTimerC` Usage:** These timers control the duration of attack and stunned states, and the frequency of direction changes.
|
||||
@@ -1,287 +0,0 @@
|
||||
# Puffstool Sprite Analysis
|
||||
|
||||
This document provides a detailed analysis of the `puffstool.asm` sprite, outlining its properties, core routines, and behavioral patterns.
|
||||
|
||||
## 1. Sprite Properties
|
||||
|
||||
The following `!SPRID` constants define Puffstool's fundamental characteristics:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_Puffstool
|
||||
!NbrTiles = 02 ; Number of tiles used in a frame
|
||||
!Harmless = 00 ; 00 = Sprite is Harmful, 01 = Sprite is Harmless
|
||||
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
||||
!Health = 0 ; Number of Health the sprite have (dynamically set in _Prep)
|
||||
!Damage = 0 ; (08 is a whole heart), 04 is half heart
|
||||
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
||||
!ImperviousAll = 00 ; 00 = Can be attack, 01 = attack will clink on it
|
||||
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
||||
!Shadow = 00 ; 00 = don't draw shadow, 01 = draw a shadow
|
||||
!Palette = 0 ; Unused in this template (can be 0 to 7)
|
||||
!Hitbox = 0 ; 00 to 31, can be viewed in sprite draw tool
|
||||
!Persist = 00 ; 01 = your sprite continue to live offscreen
|
||||
!Statis = 00 ; 00 = is sprite is alive?, (kill all enemies room)
|
||||
!CollisionLayer = 00 ; 01 = will check both layer for collision
|
||||
!CanFall = 00 ; 01 sprite can fall in hole, 01 = can't fall
|
||||
!DeflectArrow = 00 ; 01 = deflect arrows
|
||||
!WaterSprite = 00 ; 01 = can only walk shallow water
|
||||
!Blockable = 00 ; 01 = can be blocked by link's shield?
|
||||
!Prize = 0 ; 00-15 = the prize pack the sprite will drop from
|
||||
!Sound = 00 ; 01 = Play different sound when taking damage
|
||||
!Interaction = 00 ; ?? No documentation
|
||||
!Statue = 00 ; 01 = Sprite is statue
|
||||
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
||||
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
||||
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
||||
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
||||
```
|
||||
**Note:** `!Health`, `!Damage`, `!Hitbox`, and `!Prize` are initially set to `0` but are dynamically determined during initialization.
|
||||
|
||||
## 2. Core Routines
|
||||
|
||||
### 2.1. `Sprite_Puffstool_Long` (Main Loop)
|
||||
|
||||
This is the primary entry point for Puffstool's per-frame execution. It handles drawing, shadow rendering, and then dispatches to the main logic routine if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Puffstool_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_Puffstool_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Puffstool_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. `Sprite_Puffstool_Prep` (Initialization)
|
||||
|
||||
This routine is executed once when Puffstool is first spawned. It sets its health based on Link's sword level and initializes `SprDefl`.
|
||||
|
||||
```asm
|
||||
Sprite_Puffstool_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.l $7EF359 : TAY
|
||||
LDA.w .health, Y : STA.w SprHealth, X ; Set health based on sword level
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
PLB
|
||||
RTL
|
||||
|
||||
.health
|
||||
db $04, $08, $0A, $10 ; Health values for each sword level
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3. `Sprite_Puffstool_Main` (Behavioral State Machine)
|
||||
|
||||
This routine manages Puffstool's AI through a state machine, using `SprAction, X` to determine its current behavior. It includes states for walking, being stunned, and spawning spores.
|
||||
|
||||
```asm
|
||||
Sprite_Puffstool_Main:
|
||||
{
|
||||
%SpriteJumpTable(Puffstool_Walking,
|
||||
Puffstool_Stunned,
|
||||
Puffstool_Spores)
|
||||
|
||||
Puffstool_Walking:
|
||||
{
|
||||
%PlayAnimation(0,6,10)
|
||||
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
LDA.w SprTimerA, X : BNE + ; If timer A is not 0
|
||||
JSL Sprite_SelectNewDirection ; Select a new direction
|
||||
+
|
||||
JSL Sprite_MoveXyz
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
JSL Sprite_DamageFlash_Long
|
||||
JSL ThrownSprite_TileAndSpriteInteraction_long ; Interact with thrown objects
|
||||
JSL Sprite_CheckIfRecoiling
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_dano ; Check if Link damages Puffstool
|
||||
%GotoAction(1) ; Transition to Puffstool_Stunned
|
||||
%SetTimerA($60)
|
||||
%SetTimerF($20)
|
||||
.no_dano
|
||||
|
||||
RTS
|
||||
}
|
||||
|
||||
Puffstool_Stunned:
|
||||
{
|
||||
%PlayAnimation(7,7,10)
|
||||
|
||||
JSL Sprite_CheckIfLifted
|
||||
JSL Sprite_DamageFlash_Long
|
||||
JSL ThrownSprite_TileAndSpriteInteraction_long
|
||||
|
||||
LDA.w SprTimerA, X : BNE + ; If timer A is not 0
|
||||
%GotoAction(0) ; Transition back to Puffstool_Walking
|
||||
|
||||
JSL GetRandomInt : AND.b #$1F : BEQ .bomb ; Random chance to spawn bomb
|
||||
JSR Puffstool_SpawnSpores ; Spawn spores
|
||||
RTS
|
||||
.bomb
|
||||
LDA.b #$4A ; SPRITE 4A (Bomb sprite ID)
|
||||
LDY.b #$0B
|
||||
JSL Sprite_SpawnDynamically : BMI .no_space
|
||||
JSL Sprite_SetSpawnedCoordinates
|
||||
JSL Sprite_TransmuteToBomb ; Transform into a bomb
|
||||
.no_space
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Puffstool_Spores:
|
||||
{
|
||||
%StartOnFrame(8)
|
||||
%PlayAnimation(8,11,10)
|
||||
|
||||
JSL Sprite_MoveXyz
|
||||
JSL Sprite_CheckDamageToPlayerSameLayer
|
||||
|
||||
LDA.w SprTimerC, X : BNE + ; If timer C is not 0
|
||||
JSL ForcePrizeDrop_long ; Force prize drop
|
||||
STZ.w SprState, X ; Clear sprite state (despawn?)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4. `Puffstool_SpawnSpores`
|
||||
|
||||
This routine is responsible for spawning spore projectiles. It plays a sound effect and then spawns multiple spore sprites, setting their initial properties like speed, altitude, and timers.
|
||||
|
||||
```asm
|
||||
Puffstool_SpawnSpores:
|
||||
{
|
||||
LDA.b #$0C ; SFX2.0C
|
||||
JSL $0DBB7C ; SpriteSFX_QueueSFX2WithPan
|
||||
|
||||
LDA.b #$03 : STA.b $0D ; Number of spores to spawn
|
||||
|
||||
.nth_child
|
||||
LDA.b #$B1 ; Spore sprite ID (assuming $B1 is the spore sprite ID)
|
||||
JSL Sprite_SpawnDynamically : BMI .no_space
|
||||
JSL Sprite_SetSpawnedCoordinates
|
||||
PHX
|
||||
|
||||
LDX.b $0D
|
||||
LDA.w .speed_x, X : STA.w SprXSpeed, Y
|
||||
LDA.w .speed_y, X : STA.w SprYSpeed, Y
|
||||
LDA.b #$20 : STA.w $0F80, Y ; Altitude
|
||||
LDA.b #$FF : STA.w $0E80, Y ; Gravity
|
||||
LDA.b #$40 : STA.w SprTimerC, Y
|
||||
LDA.b #$01 : STA.w SprSubtype, Y
|
||||
LDA.b #$02 : STA.w SprAction, Y
|
||||
|
||||
PLX
|
||||
.no_space
|
||||
DEC.b $0D
|
||||
BPL .nth_child
|
||||
RTS
|
||||
|
||||
.speed_x
|
||||
db 11, -11, -11, 11
|
||||
|
||||
.speed_y
|
||||
db 0, 11, 0, -11
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5. `Sprite_Puffstool_Draw` (Drawing Routine)
|
||||
|
||||
This routine is responsible for rendering Puffstool's graphics. It uses the `%DrawSprite()` macro, which reads from a set of data tables to handle its multi-tile appearance and animation.
|
||||
|
||||
```asm
|
||||
Sprite_Puffstool_Draw:
|
||||
{
|
||||
%DrawSprite()
|
||||
|
||||
.start_index
|
||||
db $00, $02, $04, $06, $08, $0A, $0C, $0E, $0F, $10, $11, $12
|
||||
.nbr_of_tiles
|
||||
db 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
|
||||
.x_offsets
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 4
|
||||
.y_offsets
|
||||
dw -8, 0
|
||||
dw 0, -8
|
||||
dw 0, -8
|
||||
dw 0, -8
|
||||
dw 0, -8
|
||||
dw 0, -8
|
||||
dw 0, -8
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 4
|
||||
.chr
|
||||
db $C0, $D0
|
||||
db $D2, $C2
|
||||
db $D4, $C4
|
||||
db $D2, $C2
|
||||
db $D0, $C0
|
||||
db $D2, $C2
|
||||
db $D4, $C4
|
||||
db $D6
|
||||
db $EA
|
||||
db $C8
|
||||
db $E8
|
||||
db $F7
|
||||
.properties
|
||||
db $33, $33
|
||||
db $33, $33
|
||||
db $33, $33
|
||||
db $33, $33
|
||||
db $33, $33
|
||||
db $73, $73
|
||||
db $73, $73
|
||||
db $3D
|
||||
db $33
|
||||
db $33
|
||||
db $33
|
||||
db $33
|
||||
.sizes
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02
|
||||
db $02
|
||||
db $02
|
||||
db $02
|
||||
db $00
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Key Behaviors and Implementation Details
|
||||
|
||||
* **Dynamic Health:** Puffstool's health is determined at spawn time based on Link's current sword level, allowing for dynamic difficulty scaling.
|
||||
* **State Management:** Puffstool uses `SprAction, X` and `%SpriteJumpTable` to manage its `Puffstool_Walking`, `Puffstool_Stunned`, and `Puffstool_Spores` states.
|
||||
* **Movement Patterns:** In its walking state, Puffstool moves with random direction changes (`Sprite_SelectNewDirection`) and interacts with the environment (`Sprite_MoveXyz`, `Sprite_BounceFromTileCollision`).
|
||||
* **Stunned State and Counter-Attack:** When damaged, Puffstool enters a `Puffstool_Stunned` state. After a timer, it either spawns multiple spores (`Puffstool_SpawnSpores`) or, with a random chance, transforms into a bomb (`Sprite_TransmuteToBomb`). This provides a unique counter-attack mechanism.
|
||||
* **Spore Attack:** Puffstool can spawn multiple spore projectiles (`Puffstool_SpawnSpores`) that have their own movement and interaction logic. These spores are spawned with initial speed, altitude, and gravity.
|
||||
* **Bomb Spawning/Transformation:** A unique behavior where Puffstool can transform into a bomb (`Sprite_TransmuteToBomb`) when stunned, adding an element of surprise and danger.
|
||||
* **Interaction with Thrown Objects:** The use of `ThrownSprite_TileAndSpriteInteraction_long` suggests Puffstool can be lifted and thrown by Link, or interacts with other thrown objects.
|
||||
* **Custom OAM Drawing:** Puffstool uses the `%DrawSprite()` macro with detailed OAM data tables to render its multi-tile appearance and animations across its different states.
|
||||
* **`SprTimerA`, `SprTimerF`, `SprTimerC` Usage:** These timers control the duration of the stunned state, the delay before spawning spores/bombs, and the lifespan of the spawned spores.
|
||||
@@ -1,221 +0,0 @@
|
||||
# Sea Urchin Sprite Analysis
|
||||
|
||||
This document provides a detailed analysis of the `sea_urchin.asm` sprite, outlining its properties, core routines, and behavioral patterns.
|
||||
|
||||
## 1. Sprite Properties
|
||||
|
||||
The following `!SPRID` constants define Sea Urchin's fundamental characteristics:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_SeaUrchin
|
||||
!NbrTiles = 04 ; Number of tiles used in a frame
|
||||
!Harmless = 00 ; 00 = Sprite is Harmful, 01 = Sprite is Harmless
|
||||
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
||||
!Health = 06 ; Number of Health the sprite have
|
||||
!Damage = 04 ; (08 is a whole heart), 04 is half heart
|
||||
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
||||
!ImperviousAll = 00 ; 00 = Can be attack, 01 = attack will clink on it
|
||||
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
||||
!Shadow = 01 ; 00 = don't draw shadow, 01 = draw a shadow
|
||||
!Palette = 00 ; Unused in this template (can be 0 to 7)
|
||||
!Hitbox = 00 ; 00 to 31, can be viewed in sprite draw tool
|
||||
!Persist = 00 ; 01 = your sprite continue to live offscreen
|
||||
!Statis = 00 ; 00 = is sprite is alive?, (kill all enemies room)
|
||||
!CollisionLayer = 00 ; 01 = will check both layer for collision
|
||||
!CanFall = 00 ; 01 sprite can fall in hole, 01 = can't fall
|
||||
!DeflectArrow = 00 ; 01 = deflect arrows
|
||||
!WaterSprite = 00 ; 01 = can only walk shallow water
|
||||
!Blockable = 01 ; 01 = can be blocked by link's shield?
|
||||
!Prize = 03 ; 00-15 = the prize pack the sprite will drop from
|
||||
!Sound = 00 ; 01 = Play different sound when taking damage
|
||||
!Interaction = 00 ; ?? No documentation
|
||||
!Statue = 00 ; 01 = Sprite is statue
|
||||
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
||||
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
||||
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
||||
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
||||
```
|
||||
**Note:** `!Health` is fixed at `06` and `!Damage` is `04` (half a heart).
|
||||
|
||||
## 2. Core Routines
|
||||
|
||||
### 2.1. `Sprite_SeaUrchin_Long` (Main Loop)
|
||||
|
||||
This is the primary entry point for Sea Urchin's per-frame execution. It handles drawing, shadow rendering, and then dispatches to the main logic routine if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_SeaUrchin_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_SeaUrchin_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_SeaUrchin_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. `Sprite_SeaUrchin_Prep` (Initialization)
|
||||
|
||||
This routine is executed once when Sea Urchin is first spawned. It sets the initial prize and conditionally modifies its imperviousness and prize based on the `WORLDFLAG` (likely for different game states or areas, such as the Eon Sea).
|
||||
|
||||
```asm
|
||||
Sprite_SeaUrchin_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA #$01 : STA.w SprPrize, X ; Default prize
|
||||
LDA.w WORLDFLAG : BEQ + ; Check WORLDFLAG
|
||||
; Eon Sea Urchin impervious to sword
|
||||
LDA.b #%10000100 : STA.w SprDefl, X ; Set imperviousness flags
|
||||
LDA.b #$07 : STA.w SprPrize, X ; Change prize for Eon Sea
|
||||
+
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3. `Sprite_SeaUrchin_Main` (Behavioral State Machine)
|
||||
|
||||
This routine manages Sea Urchin's AI through a simple state machine, using `SprAction, X` to determine its current behavior. It includes `Idle` and `Death` states.
|
||||
|
||||
```asm
|
||||
Sprite_SeaUrchin_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Idle
|
||||
dw Death
|
||||
|
||||
Idle:
|
||||
{
|
||||
%PlayAnimation(0,3,8)
|
||||
%PlayerCantPassThrough()
|
||||
%DoDamageToPlayerSameLayerOnContact()
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .NoDamage
|
||||
%GotoAction(1) ; Transition to Death state if damaged
|
||||
.NoDamage
|
||||
RTS
|
||||
}
|
||||
|
||||
Death:
|
||||
{
|
||||
LDA.b #$06 : STA.w SprState, X ; Set sprite state to despawn
|
||||
LDA.b #$0A : STA.w SprTimerA, X
|
||||
STZ.w SprPrize,X
|
||||
JSL ForcePrizeDrop_long ; Force prize drop
|
||||
LDA.b #$09 : JSL SpriteSFX_QueueSFX3WithPan ; Play sound effect
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4. `Sprite_SeaUrchin_Draw` (Drawing Routine)
|
||||
|
||||
This routine is responsible for rendering Sea Urchin's graphics. It uses a custom OAM allocation and manipulation logic to handle its multi-tile appearance and animation.
|
||||
|
||||
```asm
|
||||
Sprite_SeaUrchin_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA $0DC0, X : CLC : ADC $0D90, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA.b #$02 : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
|
||||
.start_index
|
||||
db $00, $01, $02, $03, $04, $05, $06, $07
|
||||
.nbr_of_tiles
|
||||
db 0, 0, 0, 0, 0, 0, 0, 0
|
||||
.x_offsets
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
.y_offsets
|
||||
dw 0
|
||||
dw -1
|
||||
dw 0
|
||||
dw -1
|
||||
dw 0
|
||||
dw -1
|
||||
dw 0
|
||||
dw -1
|
||||
.chr
|
||||
db $EA
|
||||
db $EC
|
||||
db $EA
|
||||
db $EC
|
||||
db $EA
|
||||
db $EC
|
||||
db $EA
|
||||
db $EC
|
||||
.properties
|
||||
db $29
|
||||
db $29
|
||||
db $69
|
||||
db $69
|
||||
db $29
|
||||
db $29
|
||||
db $69
|
||||
db $69
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Key Behaviors and Implementation Details
|
||||
|
||||
* **Fixed Health:** Sea Urchin has a fixed health of `06` and its health is not dynamically scaled based on Link's sword level.
|
||||
* **Dynamic Prize Drop and Imperviousness:** A notable feature is its conditional behavior based on the `WORLDFLAG`. If this flag is set (e.g., indicating a specific game area like the Eon Sea), the Sea Urchin becomes impervious to sword attacks (`SprDefl`) and drops a different prize. This demonstrates how global game state variables can influence individual sprite properties.
|
||||
* **State Management:** Sea Urchin uses a simple state machine with `Idle` and `Death` states, managed by `SprAction, X` and `JumpTableLocal`.
|
||||
* **Movement Patterns:** The Sea Urchin has a simple idle animation (`%PlayAnimation(0,3,8)`) and does not exhibit complex movement behaviors. It remains stationary but can be pushed by Link (`%PlayerCantPassThrough()`).
|
||||
* **Damage Handling:** Upon taking damage from Link (`Sprite_CheckDamageFromPlayer`), the Sea Urchin transitions to its `Death` state. In this state, it despawns (`STZ.w SprState, X`), forces a prize drop (`ForcePrizeDrop_long`), and plays a sound effect (`SpriteSFX_QueueSFX3WithPan`).
|
||||
* **Custom OAM Drawing:** Sea Urchin utilizes a custom OAM drawing routine to render its multi-tile appearance and animation. The drawing logic includes coordinate calculations with `REP`/`SEP` for 16-bit operations.
|
||||
@@ -1,313 +0,0 @@
|
||||
# Thunder Ghost Sprite Analysis
|
||||
|
||||
This document provides a detailed analysis of the `thunder_ghost.asm` sprite, outlining its properties, core routines, and behavioral patterns.
|
||||
|
||||
## 1. Sprite Properties
|
||||
|
||||
The following `!SPRID` constants define Thunder Ghost's fundamental characteristics:
|
||||
|
||||
```asm
|
||||
!SPRID = Sprite_ThunderGhost
|
||||
!NbrTiles = 03 ; Number of tiles used in a frame
|
||||
!Harmless = 00 ; 00 = Sprite is Harmful, 01 = Sprite is Harmless
|
||||
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
||||
!Health = 10 ; Number of Health the sprite have (dynamically set in _Prep)
|
||||
!Damage = 00 ; (08 is a whole heart), 04 is half heart
|
||||
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
||||
!ImperviousAll = 00 ; 00 = Can be attack, 01 = attack will clink on it
|
||||
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
||||
!Shadow = 00 ; 00 = don't draw shadow, 01 = draw a shadow
|
||||
!Palette = 00 ; Unused in this template (can be 0 to 7)
|
||||
!Hitbox = 09 ; 00 to 31, can be viewed in sprite draw tool
|
||||
!Persist = 00 ; 01 = your sprite continue to live offscreen
|
||||
!Statis = 00 ; 00 = is sprite is alive?, (kill all enemies room)
|
||||
!CollisionLayer = 00 ; 01 = will check both layer for collision
|
||||
!CanFall = 00 ; 01 sprite can fall in hole, 01 = can't fall
|
||||
!DeflectArrow = 00 ; 01 = deflect arrows
|
||||
!WaterSprite = 00 ; 01 = can only walk shallow water
|
||||
!Blockable = 00 ; 01 = can be blocked by link's shield?
|
||||
!Prize = 00 ; 00-15 = the prize pack the sprite will drop from
|
||||
!Sound = 00 ; 01 = Play different sound when taking damage
|
||||
!Interaction = 00 ; ?? No documentation
|
||||
!Statue = 00 ; 01 = Sprite is statue
|
||||
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
||||
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
||||
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
||||
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
||||
```
|
||||
**Note:** `!Health` is initially set to `10` but is dynamically determined during initialization based on Link's sword level.
|
||||
|
||||
## 2. Core Routines
|
||||
|
||||
### 2.1. `Sprite_ThunderGhost_Long` (Main Loop)
|
||||
|
||||
This is the primary entry point for Thunder Ghost's per-frame execution. It handles drawing, shadow rendering (conditionally), and then dispatches to the main logic routine if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_ThunderGhost_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_ThunderGhost_Draw
|
||||
LDA.w SprAction, X : CMP.b #$03 : BCS + ; Don't draw shadow if casting thunder
|
||||
JSL Sprite_DrawShadow
|
||||
+
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_ThunderGhost_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. `Sprite_ThunderGhost_Prep` (Initialization)
|
||||
|
||||
This routine is executed once when Thunder Ghost is first spawned. It sets its health based on Link's sword level and initializes `SprTimerA` and `SprTimerB`.
|
||||
|
||||
```asm
|
||||
Sprite_ThunderGhost_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.l Sword : DEC A : TAY
|
||||
LDA.w .health, Y : STA.w SprHealth, X ; Set health based on sword level
|
||||
LDA.b #$08 : STA.w SprTimerB, X
|
||||
LDA.b #$08 : STA.w SprTimerA, X
|
||||
PLB
|
||||
RTL
|
||||
|
||||
.health
|
||||
db $06, $0A, $0C, $10 ; Health values for each sword level
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3. `Sprite_ThunderGhost_Main` (Behavioral State Machine)
|
||||
|
||||
This routine manages Thunder Ghost's AI through a state machine, using `SprAction, X` to determine its current behavior. It includes states for facing forward, left, and right, as well as states for casting thunder in different directions.
|
||||
|
||||
```asm
|
||||
Sprite_ThunderGhost_Main:
|
||||
{
|
||||
%SpriteJumpTable(ThunderGhostFaceForward,
|
||||
ThunderGhostLeft,
|
||||
ThunderGhostRight,
|
||||
CastThunderLeft,
|
||||
CastThunderRight)
|
||||
|
||||
ThunderGhostFaceForward:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
JSR Sprite_ThunderGhost_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
ThunderGhostLeft:
|
||||
{
|
||||
%PlayAnimation(2, 3, 16)
|
||||
JSR Sprite_ThunderGhost_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
ThunderGhostRight:
|
||||
{
|
||||
%PlayAnimation(4, 5, 16)
|
||||
JSR Sprite_ThunderGhost_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
CastThunderLeft:
|
||||
{
|
||||
%StartOnFrame(6)
|
||||
%PlayAnimation(6, 6, 16)
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
JSL Sprite_MoveLong
|
||||
|
||||
LDA.w SprTimerA, X : BNE + ; If timer A is not 0
|
||||
STZ.w SprState, X ; Clear sprite state (despawn?)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
CastThunderRight:
|
||||
{
|
||||
%StartOnFrame(6)
|
||||
%PlayAnimation(7, 7, 16)
|
||||
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
JSL Sprite_MoveLong
|
||||
|
||||
LDA.w SprTimerA, X : BNE + ; If timer A is not 0
|
||||
STZ.w SprState, X ; Clear sprite state (despawn?)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4. `Sprite_ThunderGhost_Move` (Movement and Interaction Logic)
|
||||
|
||||
This routine handles Thunder Ghost's movement, collision, and interaction with the player. It also includes logic for randomly triggering lightning attacks and changing its facing direction.
|
||||
|
||||
```asm
|
||||
Sprite_ThunderGhost_Move:
|
||||
{
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
JSL Sprite_DamageFlash_Long
|
||||
JSL Sprite_CheckIfRecoiling
|
||||
LDA.w SprTimerC, X : BNE ++ ; Check timer C
|
||||
JSL GetRandomInt : AND #$3F : BNE ++ ; Random chance to spawn lightning
|
||||
LDA.b #$40 : STA.w SprTimerC, X ; Set timer C
|
||||
JSR SpawnLightningAttack ; Spawn lightning attack
|
||||
++
|
||||
|
||||
LDA.w SprTimerA, X : BNE + ; Check timer A
|
||||
JSL Sprite_IsToRightOfPlayer : CPY.b #$01 : BNE .ToRight ; Determine if to the right of Link
|
||||
%GotoAction(1) ; Transition to ThunderGhostLeft
|
||||
JMP .Continue
|
||||
.ToRight
|
||||
%GotoAction(2) ; Transition to ThunderGhostRight
|
||||
LDA.b #$20 : STA.w SprTimerA, X ; Set timer A
|
||||
JMP .Continue
|
||||
+
|
||||
%GotoAction(0) ; Transition to ThunderGhostFaceForward
|
||||
.Continue
|
||||
|
||||
LDA.w SprMiscB, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw ThunderGhostMove
|
||||
|
||||
ThunderGhostMove:
|
||||
{
|
||||
JSL GetRandomInt : AND.b #$03
|
||||
JSL Sprite_ApplySpeedTowardsPlayer
|
||||
JSL Sprite_CheckTileCollision
|
||||
|
||||
JSL Sprite_CheckDamageFromPlayer
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5. `SpawnLightningAttack`
|
||||
|
||||
This routine is responsible for spawning the lightning attack sprite. It sets up the lightning's initial properties, including its `SprSubtype`, action, position, and speed, based on Thunder Ghost's position relative to Link.
|
||||
|
||||
```asm
|
||||
SpawnLightningAttack:
|
||||
{
|
||||
PHX
|
||||
LDA.b #$CD ; Sprite ID for lightning (assuming $CD is the lightning sprite ID)
|
||||
JSL Sprite_SpawnDynamically : BMI .no_space
|
||||
; Use SprXSpeed, SprYSpeed, SprXRound, SprYRound
|
||||
; SprX, SprY, SprXH, SprY, to cast the lightning spell
|
||||
; and make it move off to the bottom left or bottom right
|
||||
|
||||
; Y is the ID of the new attack sprite
|
||||
; X is the ID of the current source sprite
|
||||
|
||||
; Left 0 or Right 1
|
||||
PHY
|
||||
JSL Sprite_IsToRightOfPlayer : TAY : CMP.b #$01 : BEQ + ; Determine if to the right of Link
|
||||
LDA.b #$00
|
||||
JMP .Continue
|
||||
+
|
||||
LDA.b #$01
|
||||
.Continue
|
||||
CLC : ADC.b #$03
|
||||
PLY
|
||||
STA.w SprSubtype, Y ; Set SprSubtype for lightning
|
||||
STA.w SprAction, Y ; Set action for lightning
|
||||
|
||||
LDA.w SprX, X : STA.w SprX, Y
|
||||
LDA.w SprY, X : STA.w SprY, Y
|
||||
LDA.w SprXH, X : STA.w SprXH, Y
|
||||
LDA.w SprYH, X : STA.w SprYH, Y
|
||||
|
||||
LDA.w SprXSpeed, X : STA.w SprXSpeed, Y
|
||||
LDA.w SprYSpeed, X : STA.w SprYSpeed, Y
|
||||
LDA.b #$02 : STA.w SprXRound, Y
|
||||
LDA.b #$02 : STA.w SprYRound, Y
|
||||
LDA.b #$30 : STA.w SprTimerA, Y
|
||||
LDA.b #$30 : STA.w SprTimerB, Y
|
||||
.no_space
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
### 2.6. `Sprite_ThunderGhost_Draw` (Drawing Routine)
|
||||
|
||||
This routine is responsible for rendering Thunder Ghost's graphics. It uses the `%DrawSprite()` macro, which reads from a set of data tables to handle its multi-tile appearance and animation.
|
||||
|
||||
```asm
|
||||
Sprite_ThunderGhost_Draw:
|
||||
{
|
||||
%DrawSprite()
|
||||
|
||||
.start_index
|
||||
db $00, $03, $06, $09, $0C, $0F, $12, $15
|
||||
.nbr_of_tiles
|
||||
db 2, 2, 2, 2, 2, 2, 2, 2
|
||||
.x_offsets
|
||||
dw 0, 0, 8
|
||||
dw 8, 0, 0
|
||||
dw 0, 0, 8
|
||||
dw 0, 0, 8
|
||||
dw 0, 8, 0
|
||||
dw 0, 8, 0
|
||||
dw -12, -8, -16
|
||||
dw 12, 16, 20
|
||||
.y_offsets
|
||||
dw -8, 0, -8
|
||||
dw -8, 0, -8
|
||||
dw 0, -8, -8
|
||||
dw 0, -8, -8
|
||||
dw 0, -8, -8
|
||||
dw 0, -8, -8
|
||||
dw 12, 24, 20
|
||||
dw 12, 24, 12
|
||||
.chr
|
||||
db $3A, $02, $3B
|
||||
db $3A, $02, $3B
|
||||
db $20, $00, $01
|
||||
db $22, $10, $11
|
||||
db $20, $00, $01
|
||||
db $22, $10, $11
|
||||
db $28, $2A, $2B
|
||||
db $28, $2A, $2B
|
||||
.properties
|
||||
db $2B, $2B, $2B
|
||||
db $6B, $6B, $6B
|
||||
db $2B, $2B, $2B
|
||||
db $2B, $2B, $2B
|
||||
db $6B, $6B, $6B
|
||||
db $6B, $6B, $6B
|
||||
db $2B, $2B, $2B
|
||||
db $6B, $2B, $2B
|
||||
.sizes
|
||||
db $00, $02, $00
|
||||
db $00, $02, $00
|
||||
db $02, $00, $00
|
||||
db $02, $00, $00
|
||||
db $02, $00, $00
|
||||
db $02, $00, $00
|
||||
db $02, $00, $00
|
||||
db $02, $00, $00
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Key Behaviors and Implementation Details
|
||||
|
||||
* **Dynamic Health:** Thunder Ghost's health is determined at spawn time based on Link's current sword level, allowing for dynamic difficulty scaling.
|
||||
* **Conditional Shadow Drawing:** The shadow is not drawn when Thunder Ghost is in its `CastThunderLeft` or `CastThunderRight` states, suggesting a visual distinction during its attack.
|
||||
* **Lightning Attack:** Thunder Ghost has a random chance to spawn a lightning attack (`SpawnLightningAttack`) at regular intervals, which then becomes an independent sprite with its own movement and interaction logic.
|
||||
* **State Management:** Thunder Ghost uses `SprAction, X` and `%SpriteJumpTable` to manage its facing direction (forward, left, right) and its thunder-casting states.
|
||||
* **Movement Patterns:** Thunder Ghost moves randomly and applies speed towards the player, while also bouncing from tile collisions and being unable to be passed through by Link.
|
||||
* **Projectile Spawning with Directional Logic:** The `SpawnLightningAttack` routine demonstrates how to spawn a projectile (`$CD`) and initialize its properties, including its `SprSubtype` and `SprAction`, based on Thunder Ghost's position relative to Link.
|
||||
* **`SprTimerA`, `SprTimerB`, `SprTimerC` Usage:** These timers are used to control the frequency of lightning attacks and the duration of facing/movement states.
|
||||
* **`Sprite_MoveLong`:** Used in the `CastThunderLeft` and `CastThunderRight` states, suggesting a specific movement behavior during the attack animation.
|
||||
@@ -1,80 +0,0 @@
|
||||
# Twinrova Boss Sprite Analysis
|
||||
|
||||
## Overview
|
||||
The `twinrova` sprite (ID: `Sprite_Twinrova`, which is `$CE`) is a complex, multi-phase boss designed to override the vanilla Blind and Blind Maiden sprites. It features a dramatic transformation from Blind Maiden into Twinrova, followed by alternating phases where Twinrova switches between Koume (fire) and Kotake (ice) forms, each possessing distinct attacks and environmental interactions.
|
||||
|
||||
## Key Properties:
|
||||
* **Sprite ID:** `Sprite_Twinrova` (`$CE`)
|
||||
* **Description:** A multi-phase boss that transforms from Blind Maiden, then alternates between fire (Koume) and ice (Kotake) forms.
|
||||
* **Number of Tiles:** 6
|
||||
* **Health:** `00` (Health is managed by `Sprite_Twinrova_CheckIfDead` and phase transitions.)
|
||||
* **Damage:** `00` (Damage dealt to Link is likely handled by its attacks.)
|
||||
* **Special Properties:**
|
||||
* `!Boss = 01` (Correctly identified as a boss.)
|
||||
* `!Shadow = 01` (Draws a shadow.)
|
||||
* `!Hitbox = 03`
|
||||
* `!CollisionLayer = 01` (Checks both layers for collision.)
|
||||
|
||||
## Custom Variables/Macros:
|
||||
* `!AnimSpeed = 8`: Defines the animation speed for various states.
|
||||
* `Twinrova_Front()`, `Twinrova_Back()`, `Twinrova_Ready()`, `Twinrova_Attack()`, `Show_Koume()`, `Show_Kotake()`, `Twinrova_Hurt()`: Macros for playing specific animations, enhancing code readability.
|
||||
* `$AC`: A RAM address used to store the current attack type (Fire or Ice).
|
||||
|
||||
## Main Logic Flow (`Sprite_Twinrova_Main`):
|
||||
The boss's behavior is governed by a detailed state machine:
|
||||
* **`Twinrova_Init` (0x00):** Initial state. Displays an introductory message and transitions to `Twinrova_MoveState`.
|
||||
* **`Twinrova_MoveState` (0x01):** The core movement and phase management state. It checks `SprHealth, X` to determine if Twinrova is in Phase 1 (single entity) or Phase 2 (alternating forms).
|
||||
* **Phase 1:** Twinrova moves around, randomly spawning Fire/Ice Keese, or preparing Fire/Ice attacks.
|
||||
* **Phase 2:** Twinrova alternates between `Twinrova_KoumeMode` (fire) and `Twinrova_KotakeMode` (ice) forms.
|
||||
* **`Twinrova_MoveForwards` (0x02), `Twinrova_MoveBackwards` (0x03):** Handles movement using `Sprite_FloatTowardPlayer` and `Sprite_CheckTileCollision`.
|
||||
* **`Twinrova_PrepareAttack` (0x04):** Prepares either a Fire or Ice attack based on the value in `$AC`.
|
||||
* **`Twinrova_FireAttack` (0x05):** Executes a Fire attack. Restores floor tiles, uses `JSL Sprite_Twinrova_FireAttack` (a shared function for the actual attack), and randomly releases fireballs (`ReleaseFireballs`).
|
||||
* **`Twinrova_IceAttack` (0x06):** Executes an Ice attack using `JSL Sprite_Twinrova_IceAttack` (a shared function).
|
||||
* **`Twinrova_Hurt` (0x07):** Manages Twinrova taking damage. Plays a hurt animation and, after a timer, determines whether to dodge or retaliate with a fire or ice attack.
|
||||
* **`Twinrova_KoumeMode` (0x08):** Koume (fire) form. Spawns pit hazards (`AddPitHazard`), falling tiles (`Ganon_SpawnFallingTilesOverlord`), and fireballs (`Sprite_SpawnFireball`). Uses `RageModeMove` for dynamic movement.
|
||||
* **`Twinrova_KotakeMode` (0x09):** Kotake (ice) form. Can spawn lightning (`JSL $1DE612`) and uses `RageModeMove` for dynamic movement.
|
||||
* **`Twinrova_Dead` (0x0A):** Handles Twinrova's death sequence, killing all spawned friends and playing a hurt animation.
|
||||
|
||||
## Initialization (`Sprite_Twinrova_Prep`):
|
||||
* Checks for the presence of the Blind Maiden (`$7EF3CC = $06`). If the Maiden is present, Twinrova is killed, indicating that Twinrova spawns *from* the Blind Maiden.
|
||||
* Sets initial health to `$5A` (90 decimal).
|
||||
* Configures deflection (`SprDefl = $80`), bump damage (`SprBump = $04`), and ensures Twinrova is not invincible.
|
||||
* Configures Blind Boss startup parameters and initializes various timers and `SprMisc` variables.
|
||||
|
||||
## Death Check (`Sprite_Twinrova_CheckIfDead`):
|
||||
* Monitors `SprHealth, X`. If health is zero or negative, it triggers the boss's death sequence, setting `SprState = $04` (kill sprite boss style) and `SprAction = $0A` (Twinrova_Dead stage).
|
||||
|
||||
## Movement (`RageModeMove`, `DoRandomStrafe`, `VelocityOffsets`):
|
||||
* **`RageModeMove`:** A sophisticated routine for dynamic, floaty movement. It randomly determines a movement mode (predictive movement towards player, random strafe, random dodge, stay in place) based on timers and probabilities. It also handles evasive actions.
|
||||
* **`DoRandomStrafe`:** Generates random strafing movement.
|
||||
* **`VelocityOffsets`:** A table defining X and Y speed offsets for movement.
|
||||
|
||||
## Environmental Interactions (`Twinrova_RestoreFloorTile`, `RestoreFloorTile`, `AddPitHazard`, `Ganon_SpawnFallingTilesOverlord`):
|
||||
* **`Twinrova_RestoreFloorTile` / `RestoreFloorTile`:** Restores floor tiles, likely after they have been modified by an attack.
|
||||
* **`AddPitHazard`:** Adds a pit hazard to the floor.
|
||||
* **`Ganon_SpawnFallingTilesOverlord`:** Spawns falling tiles (reused from Ganon's mechanics).
|
||||
|
||||
## Drawing (`Sprite_Twinrova_Draw`):
|
||||
* Uses standard OAM allocation routines.
|
||||
* Handles complex animation frames, x/y offsets, character data, properties, and sizes for drawing Twinrova.
|
||||
* Utilizes 16-bit operations for precise drawing calculations.
|
||||
|
||||
## Graphics Transfer (`ApplyTwinrovaGraphics`):
|
||||
* Handles DMA transfer of graphics data (`twinrova.bin`) to VRAM.
|
||||
|
||||
## Attack Spawning (`Fireball_Configure`, `ReleaseFireballs`, `Sprite_SpawnFireKeese`, `Sprite_SpawnIceKeese`, `JSL Sprite_SpawnFireball`, `JSL $1DE612` (Sprite_SpawnLightning)):
|
||||
* Twinrova can spawn various projectiles and enemies, including fireballs, Fire Keese, Ice Keese, and lightning.
|
||||
|
||||
## Blind Maiden Integration:
|
||||
Twinrova's fight is deeply integrated with the Blind Maiden mechanics:
|
||||
* **`Follower_BasicMover`:** This routine is hooked to check if the follower is the Blind Maiden, triggering the transformation to Twinrova.
|
||||
* **`Follower_CheckBlindTrigger`:** Checks if the Blind Maiden follower is within a specific trigger area.
|
||||
* **`Blind_SpawnFromMaiden`:** This is the core routine for the transformation. It applies Twinrova graphics, sets Twinrova's initial state and position based on the Maiden's, and sets various timers and properties.
|
||||
* **`SpritePrep_Blind_PrepareBattle`:** This routine is overridden to handle Twinrova's prep or to despawn if a room flag is set.
|
||||
|
||||
## Discrepancies/Notes:
|
||||
* **Health Management:** The `!Health` property is `00`. The boss's health is managed by `Sprite_Twinrova_CheckIfDead` and phase transitions.
|
||||
* **Code Reuse:** There is extensive code reuse from other sprites/bosses (e.g., `Sprite_Twinrova_FireAttack` is also used by KydreeokHead, `Ganon_SpawnFallingTilesOverlord` in Koume mode, `Sprite_SpawnFireKeese`/`Sprite_SpawnIceKeese` in MoveState). This is an efficient practice but requires careful management to ensure thematic consistency and avoid unintended side effects.
|
||||
* **Hardcoded Addresses:** Several `JSL` calls are to hardcoded addresses (e.g., `JSL $1DE612` for lightning). These should ideally be replaced with named labels for better maintainability.
|
||||
* **Blind Maiden Overrides:** The boss heavily relies on overriding vanilla Blind Maiden behavior, which is a common ROM hacking technique but requires careful understanding of the original game's code.
|
||||
* **`TargetPositions`:** This table is defined but appears unused in the provided code.
|
||||
@@ -1,46 +0,0 @@
|
||||
# Vampire Bat Mini-Boss Sprite Analysis
|
||||
|
||||
## Overview
|
||||
The `vampire_bat` sprite is a mini-boss, a specialized enemy that utilizes the generic Keese sprite ID (`$11`) but differentiates its behavior through `SprSubtype = 02`. It features more complex movement patterns and attacks compared to a standard Keese, including ascending, flying around, descending, and spawning other Keese.
|
||||
|
||||
## Key Properties:
|
||||
* **Sprite ID:** `0x11` (Custom Keese Subtype 02)
|
||||
* **Description:** A mini-boss variant of the Keese, with enhanced movement and attack capabilities.
|
||||
* **Number of Tiles:** 8 (Inherited from the base Keese sprite.)
|
||||
* **Health:** `32` (decimal, set in `Sprite_Keese_Prep` based on subtype.)
|
||||
* **Damage:** `00` (Damage dealt to Link is likely handled by its attacks.)
|
||||
* **Special Properties:**
|
||||
* `!Boss = 00` (Not marked as a boss, but functions as a mini-boss/special enemy.)
|
||||
* `!Shadow = 01` (Draws a shadow.)
|
||||
|
||||
## Main Logic Flow (`Sprite_VampireBat_Main`):
|
||||
The Vampire Bat's behavior is governed by a state machine:
|
||||
* **`VampireBat_Idle` (0x00):** Waits for Link to approach within a specified distance (`$24`). Transitions to `VampireBat_Ascend`.
|
||||
* **`VampireBat_Ascend` (0x01):** Plays an ascending animation, increases its `SprHeight` to `$50`, and randomly spawns a Fire Keese (`Sprite_SpawnFireKeese`). Transitions to `VampireBat_FlyAround`.
|
||||
* **`VampireBat_FlyAround` (0x02):** Plays a flying animation, moves towards Link (`Sprite_ProjectSpeedTowardsPlayer`), and randomly selects new directions (`Sprite_SelectNewDirection`). Transitions to `VampireBat_Descend` after a timer.
|
||||
* **`VampireBat_Descend` (0x03):** Plays a descending animation, decreases its `SprHeight` until it's on the ground, and randomly uses `Sprite_Twinrova_FireAttack`. Transitions back to `VampireBat_Idle` after a timer.
|
||||
|
||||
## Initialization (from `Sprite_Keese_Prep` in `keese.asm`):
|
||||
The Vampire Bat does not have its own `_Prep` routine and relies on the generic `Sprite_Keese_Prep` routine in `keese.asm`. When `SprSubtype = 02`:
|
||||
* `SprHealth` is set to `$20` (32 decimal).
|
||||
* `SprDefl` is set to `$80`.
|
||||
* `SprTimerC` is set to `$30`.
|
||||
|
||||
## Drawing (`Sprite_VampireBat_Draw`):
|
||||
* This routine is called from `Sprite_Keese_Long` in `keese.asm` when `SprSubtype = 02`.
|
||||
* Uses standard OAM allocation routines.
|
||||
* Handles animation frames, x/y offsets, character data, properties, and sizes specific to the Vampire Bat's appearance.
|
||||
|
||||
## Attack Spawning (`Sprite_SpawnFireKeese`, `Sprite_SpawnIceKeese`):
|
||||
* **`Sprite_SpawnFireKeese`:** Spawns a Keese sprite (`$11`) with `SprSubtype = $01` (Fire Keese).
|
||||
* **`Sprite_SpawnIceKeese`:** Spawns a Keese sprite (`$11`) with `SprSubtype = $00` (Ice Keese).
|
||||
|
||||
## Interactions:
|
||||
* **Damage:** Responds to damage from Link, including flashing and bouncing from tile collisions.
|
||||
* **Attacks:** Can spawn Fire Keese and utilize `Sprite_Twinrova_FireAttack` (a shared attack function).
|
||||
|
||||
## Discrepancies/Notes:
|
||||
* **Shared Sprite ID:** The Vampire Bat efficiently reuses the generic Keese sprite ID (`$11`), with `SprSubtype = 02` serving as the primary differentiator for its unique behavior.
|
||||
* **Health Management:** Its health is configured within the generic `Sprite_Keese_Prep` routine based on its subtype.
|
||||
* **Code Reuse:** It reuses `Sprite_Twinrova_FireAttack`, demonstrating efficient code sharing across different boss/mini-boss sprites.
|
||||
* **Hardcoded Values:** Many numerical values for timers, speeds, and offsets are hardcoded. Replacing these with named constants would improve readability and maintainability.
|
||||
@@ -1,62 +0,0 @@
|
||||
# Wolfos Mini-Boss Sprite Analysis
|
||||
|
||||
## Overview
|
||||
The `wolfos` sprite (ID: `Sprite_Wolfos`, which is `$A9`) functions as a mini-boss or special enemy. It engages Link in combat with various movement and attack patterns. A key aspect of this sprite is its integration into a mask quest, where it can be subdued and, under specific conditions, grants Link the Wolf Mask.
|
||||
|
||||
## Key Properties:
|
||||
* **Sprite ID:** `Sprite_Wolfos` (`$A9`)
|
||||
* **Description:** A mini-boss/special enemy that fights Link and is part of a mask quest.
|
||||
* **Number of Tiles:** 4
|
||||
* **Health:** `30` (decimal)
|
||||
* **Damage:** `00` (Damage dealt to Link is likely handled by its attacks.)
|
||||
* **Special Properties:**
|
||||
* `!Boss = 00` (Not marked as a boss, but functions as a mini-boss/special enemy.)
|
||||
* `!ImperviousArrow = 01` (Impervious to arrows.)
|
||||
|
||||
## Custom Variables/Macros:
|
||||
* `WolfosDialogue = SprMiscD`: Stores a flag to control Wolfos dialogue.
|
||||
* `Wolfos_AnimateAction = SprMiscE`: Stores the current animation action.
|
||||
* `AttackForward()`, `AttackBack()`, `WalkRight()`, `WalkLeft()`, `AttackRight()`, `AttackLeft()`, `Subdued()`, `GrantMask()`, `Dismiss()`: Macros for setting `SprAction` and `Wolfos_AnimateAction`, improving code clarity.
|
||||
* `!NormalSpeed = $08`, `!AttackSpeed = $0F`: Constants for movement speeds.
|
||||
|
||||
## Main Logic Flow (`Sprite_Wolfos_Main`):
|
||||
The Wolfos's behavior is governed by a state machine:
|
||||
* **`Wolfos_AttackForward` (0x00), `Wolfos_AttackBack` (0x01), `Wolfos_WalkRight` (0x02), `Wolfos_WalkLeft` (0x03), `Wolfos_AttackRight` (0x04), `Wolfos_AttackLeft` (0x05):** These states manage the Wolfos's movement and attacks. They call `Wolfos_Move` and can randomly trigger attack actions with increased speed and temporary imperviousness.
|
||||
* **`Wolfos_Subdued` (0x06):** In this state, the Wolfos stops moving, displays dialogue (`$23`), and waits for Link to play the Song of Healing (`SongFlag = $01`). If the song is played, it transitions to `Wolfos_GrantMask`.
|
||||
* **`Wolfos_GrantMask` (0x07):** Displays the Wolfos mask graphic, shows a message (`$10F`), grants Link the `WolfMask` item, and transitions to `Wolfos_Dismiss`.
|
||||
* **`Wolfos_Dismiss` (0x08):** Stops moving, kills the sprite, and clears Link's `BRANDISH` flag.
|
||||
|
||||
## Initialization (`Sprite_Wolfos_Prep`):
|
||||
* Checks if Link is outdoors (`$1B`). If so, it further checks if the Wolfos has already been defeated (`$7EF303 = $01`). If defeated, the sprite is killed to prevent respawning.
|
||||
* Sets initial timers (`SprTimerA = $40`, `SprTimerC = $40`).
|
||||
* Configures deflection properties (`SprDefl = $82`, making it impervious to arrows).
|
||||
* Sets `SprNbrOAM = $08` and initializes `SprMiscG, X` and `SprMiscE, X` to `0`.
|
||||
|
||||
## Defeat Check (`Sprite_Wolfos_CheckIfDefeated`):
|
||||
* Checks if Link is outdoors. If `SprHealth, X` drops below `$04`, the Wolfos is considered "defeated."
|
||||
* Upon defeat, it sets `SprAction = $06` (Wolfos_Subdued), `SprState = $09` (normal state, avoiding a full death animation), refills its health to `$40`, and clears `WolfosDialogue`. This indicates pacification rather than outright killing.
|
||||
|
||||
## Movement (`Wolfos_Move`, `Wolfos_DecideAction`, `Wolfos_MoveAction_Basic`, `Wolfos_MoveAction_CirclePlayer`, `Wolfos_MoveAction_Dodge`):
|
||||
* **`Wolfos_Move`:** Handles damage flash, checks damage from player, prevents player from passing through, bounces from tile collision, checks for recoiling, moves the sprite, and calls `Wolfos_DecideAction`.
|
||||
* **`Wolfos_DecideAction`:** Determines the Wolfos's next movement action based on timers and random chance. It uses a jump table to select between `Wolfos_MoveAction_Basic`, `Wolfos_MoveAction_CirclePlayer`, and `Wolfos_MoveAction_Dodge`.
|
||||
* **`Wolfos_MoveAction_Basic`:** Basic movement towards or away from Link based on distance.
|
||||
* **`Wolfos_MoveAction_CirclePlayer`:** Attempts to circle the player.
|
||||
* **`Wolfos_MoveAction_Dodge`:** Dodges by applying speed towards the player.
|
||||
|
||||
## Animation (`Sprite_Wolfos_Animate`):
|
||||
* This routine is called from `Sprite_Wolfos_Main`.
|
||||
* It uses `Wolfos_AnimateAction` (stored in `SprMiscE, X`) to determine which animation to play.
|
||||
* It has separate animation routines for `AttackForward`, `AttackBack`, `WalkRight`, `WalkLeft`, `AttackRight`, `AttackLeft`, and `Subdued`.
|
||||
* It also spawns sparkle garnishes (`JSL Sprite_SpawnSparkleGarnish`).
|
||||
|
||||
## Drawing (`Sprite_Wolfos_Draw`):
|
||||
* Uses standard OAM allocation routines.
|
||||
* Handles animation frames, x/y offsets, character data, properties, and sizes for drawing the Wolfos.
|
||||
* Includes a special frame for the Wolf Mask (`$CC`) when granting the item.
|
||||
|
||||
## Discrepancies/Notes:
|
||||
* **Mask Quest Integration:** The Wolfos is directly integrated into a mask quest, where playing the Song of Healing subdues it and leads to receiving the Wolf Mask.
|
||||
* **Health Refill on Defeat:** When defeated, its health is refilled to `$40`, and its state is set to `Wolfos_Subdued`, indicating it's not truly killed but rather pacified.
|
||||
* **Hardcoded Values:** Many numerical values for timers, speeds, and offsets are hardcoded. Replacing these with named constants would improve readability and maintainability.
|
||||
* **`JSL Link_ReceiveItem`:** This is a standard function for giving items to Link.
|
||||
* **`JSL Sprite_SpawnSparkleGarnish`:** This is a generic garnish spawning function.
|
||||
@@ -1,90 +0,0 @@
|
||||
# NPCs Analysis
|
||||
|
||||
This document provides an analysis of the Non-Player Character (NPC) sprites found in the `Sprites/NPCs/` directory.
|
||||
|
||||
## File Overview
|
||||
|
||||
| Filename | Sprite ID(s) | Description |
|
||||
|---|---|---|
|
||||
| `bean_vendor.asm` | `Sprite_BeanVendor` | Handles the logic for the bean vendor who sells Magic Beans to the player. |
|
||||
| `bottle_vendor.asm` | (Vanilla Hook) | Modifies the vanilla bottle vendor to handle selling milk. |
|
||||
| `bug_net_kid.asm` | (Vanilla Hook) | Modifies the Sick Kid to grant the Pegasus Boots after playing the Song of Healing. |
|
||||
| `deku_scrub.asm` | `Sprite_DekuScrubNPCs` | Manages various Deku Scrub NPCs, including one who gives the Deku Mask. |
|
||||
| `eon_owl.asm` | `Sprite_EonOwl` | The owl that guides Link. Includes logic for both the Eon Owl and Kaepora Gaebora. |
|
||||
| `eon_zora.asm` | (Part of `zora.asm`) | A friendly Zora NPC found in the Eon Abyss. |
|
||||
| `eon_zora_elder.asm`| (Part of `zora.asm`) | The elder Zora in the Eon Abyss. |
|
||||
| `farore.asm` | `Sprite_Farore` | The Oracle Farore, who appears in cutscenes and guides the player. |
|
||||
| `followers.asm` | (Vanilla Hooks) | Contains logic for various follower characters like the Zora Baby and the Old Man. |
|
||||
| `fortune_teller.asm`| (Vanilla Hook) | Modifies the fortune teller's dialogue to provide hints relevant to the hack's progression. |
|
||||
| `goron.asm` | `Sprite_Goron` | Handles both the Kalyxo Goron who opens the mines and the Eon Gorons. |
|
||||
| `hyrule_dream.asm` | (Part of `farore.asm`) | Logic for NPCs appearing in Link's dream sequences (Zelda, King, Soldier). |
|
||||
| `impa.asm` | (Vanilla Hook) | Modifies Impa's behavior, particularly in setting spawn points. |
|
||||
| `korok.asm` | `Sprite_Korok` | A friendly Korok NPC. |
|
||||
| `maku_tree.asm` | `Sprite_MakuTree` | The Maku Tree, a key story NPC who provides a Heart Container. |
|
||||
| `maple.asm` | (Part of `mermaid.asm`)| Maple the witch, who can send Link to dream worlds. |
|
||||
| `mask_salesman.asm` | `Sprite_MaskSalesman` | The Happy Mask Salesman, who sells the Bunny Hood and Stone Mask. |
|
||||
| `mermaid.asm` | `Sprite_Mermaid` | A friendly mermaid NPC. Also contains logic for Maple and the Librarian. |
|
||||
| `piratian.asm` | `$0E` | A friendly pirate-like NPC that becomes aggressive if attacked. |
|
||||
| `ranch_girl.asm` | (Vanilla Hook) | Modifies the chicken lady at the ranch to give the Ocarina. |
|
||||
| `tingle.asm` | `$22` | Tingle, who sells dungeon maps to the player. |
|
||||
| `vasu.asm` | `Sprite_Vasu` | Vasu, the jeweler who appraises magic rings. Also includes logic for Error. |
|
||||
| `village_dog.asm` | `Sprite_VillageDog` | A friendly dog that interacts with the player. Includes logic for the Eon Dog. |
|
||||
| `village_elder.asm` | (Part of `bean_vendor.asm`)| The village elder NPC. |
|
||||
| `zora_princess.asm` | `Sprite_ZoraPrincess` | The Zora Princess, who grants the Zora Mask. |
|
||||
| `zora.asm` | `Sprite_Zora` | A friendly Zora NPC. Also contains logic for the Zora Princess and Eon Zoras. |
|
||||
|
||||
## Detailed NPC Analysis
|
||||
|
||||
### `bean_vendor.asm` / `village_elder.asm`
|
||||
- **Sprite ID:** `Sprite_BeanVendor`
|
||||
- **Summary:** This file contains the logic for two NPCs. The primary is the Bean Vendor, who sells Magic Beans for 100 rupees. The second is the Village Elder.
|
||||
- **Key Logic:**
|
||||
- **BeanVendor:** Initiates a dialogue on contact. If the player agrees to buy, it checks for sufficient rupees, deducts the cost, and spawns a collectible Magic Bean sprite.
|
||||
- **VillageElder:** Engages in dialogue and sets a progress flag (`OOSPROG`) after the first interaction.
|
||||
|
||||
### `bug_net_kid.asm`
|
||||
- **Sprite ID:** (Hooks `SpritePrep_SickKid`)
|
||||
- **Summary:** This modifies the vanilla "Sick Kid" NPC. Instead of giving the Bug Net, he gives the player the Pegasus Boots.
|
||||
- **Key Logic:** The `SickKid_CheckForSongOfHealing` routine checks if the `SongFlag` is set. If it is, the `BugNetKid_GrantBugNet` routine is called, which uses `Link_ReceiveItem` to give the boots (`ITEMGET` ID `$4B`).
|
||||
|
||||
### `deku_scrub.asm`
|
||||
- **Sprite ID:** `Sprite_DekuScrubNPCs`
|
||||
- **Summary:** Manages several Deku Scrub NPCs, including the Deku Butler and Deku Princess. A key interaction involves a withered Deku Scrub who, after being healed with the Song of Healing, gives the player the Deku Mask.
|
||||
- **Key Logic:**
|
||||
- The main state machine checks for the `SongFlag`.
|
||||
- If the song is played, it transitions through a dialogue sequence (`QuiereCuracion`, `DarMascara`).
|
||||
- Finally, in the `Regalo` state, it calls `Link_ReceiveItem` with item ID `$11` (Deku Mask) and sets a progress flag (`$7EF301`).
|
||||
|
||||
### `eon_owl.asm`
|
||||
- **Sprite ID:** `Sprite_EonOwl`
|
||||
- **Summary:** This is the guide owl, appearing in both the overworld (as Eon Owl) and the Hall of Secrets (as Kaepora Gaebora).
|
||||
- **Key Logic:**
|
||||
- **Eon Owl:** In the overworld, it triggers introductory dialogue when the player gets close and then flies away.
|
||||
- **Kaepora Gaebora:** In the Hall of Secrets, it appears only after all 7 crystals are collected and before the player has the Song of Soaring. It offers to teach the player the song.
|
||||
|
||||
### `farore.asm` / `hyrule_dream.asm`
|
||||
- **Sprite ID:** `Sprite_Farore`
|
||||
- **Summary:** Handles the Oracle Farore and NPCs that appear in dream sequences.
|
||||
- **Key Logic:**
|
||||
- **Farore:** Manages the introductory cutscene where she follows Link, sets the main story state (`$B6`), and changes the game state to post-pendants (`$7EF3C5 = 2`).
|
||||
- **Dream NPCs:** Contains simple display logic for Zelda, the King, and a soldier during the `MakuTree_HasMetLink` dream sequence.
|
||||
|
||||
### `followers.asm`
|
||||
- **Sprite ID:** (Hooks vanilla follower system)
|
||||
- **Summary:** Contains significant custom logic for follower characters, most notably the Zora Baby (Locksmith) and the Old Man.
|
||||
- **Key Logic:**
|
||||
- **Zora Baby:**
|
||||
- Replaces the Locksmith sprite (`$39`).
|
||||
- Can be picked up and carried by Link.
|
||||
- When placed on a water gate switch, it triggers the switch.
|
||||
- Transitions from a follower to a standard sprite when on a star tile in a dungeon.
|
||||
- **Old Man:** Logic is modified to grant the Goldstar (Hookshot Lv2 upgrade) instead of the Magic Mirror.
|
||||
|
||||
### `vasu.asm`
|
||||
- **Sprite ID:** `Sprite_Vasu`
|
||||
- **Summary:** This is the ring jeweler, Vasu. He can appraise rings the player has found. The file also contains logic for the "I am Error" NPC.
|
||||
- **Key Logic:**
|
||||
- Vasu's main loop presents a choice: "Appraise" or "Explain".
|
||||
- If "Appraise" is chosen, it checks if the player has any unappraised rings (`FOUNDRINGS`).
|
||||
- It charges 20 rupees (the first one is free) and transfers the bits from `FOUNDRINGS` to `MAGICRINGS`, making them usable.
|
||||
- The Error NPC appears as a subtype and gives the player a random ring when spoken to.
|
||||
@@ -1,225 +0,0 @@
|
||||
# Bean Vendor
|
||||
|
||||
## Overview
|
||||
The Bean Vendor is an NPC (Non-Player Character) sprite designed for player interaction, primarily through dialogue. It features a simple state machine to manage its idle and talking behaviors.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `$00` (Vanilla sprite ID, likely overridden)
|
||||
* **`!NbrTiles`**: `08`
|
||||
* **`!Harmless`**: `01` (Indicates the sprite is harmless to Link)
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `01` (Indicates the sprite is impervious to all attacks)
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `01`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_BeanVendor_Long`)
|
||||
This routine is the main entry point for the Bean Vendor, executed every frame. It handles drawing, shadow rendering, and dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_BeanVendor_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_BeanVendor_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_BeanVendor_Main
|
||||
.SpriteIsNotActive
|
||||
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_BeanVendor_Prep`)
|
||||
This routine runs once when the Bean Vendor is spawned. It initializes `SprDefl, X`, `SprTimerC, X`, `SprNbrOAM, X`, and `SprPrize, X`.
|
||||
|
||||
```asm
|
||||
Sprite_BeanVendor_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
LDA.b #$30 : STA.w SprTimerC, X
|
||||
LDA.b #$03 : STA.w SprNbrOAM, X
|
||||
LDA.b #$03 : STA.w SprPrize, X
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_BeanVendor_Main`)
|
||||
The Bean Vendor's core behavior is managed by a state machine with `BeanVendor_Idle` and `BeanVendor_Talk` states.
|
||||
|
||||
* **`BeanVendor_Idle`**: The vendor plays an idle animation. When Link is nearby (`GetDistance8bit_Long`), it transitions to the `BeanVendor_Talk` state.
|
||||
* **`BeanVendor_Talk`**: The vendor plays a talking animation and displays a message using `JSL Interface_PrepAndDisplayMessage`. Once the message is dismissed, it transitions back to the `BeanVendor_Idle` state.
|
||||
|
||||
```asm
|
||||
Sprite_BeanVendor_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw BeanVendor_Idle
|
||||
dw BeanVendor_Talk
|
||||
|
||||
BeanVendor_Idle:
|
||||
{
|
||||
%PlayAnimation(0,1,15)
|
||||
JSL GetDistance8bit_Long : CMP.b #$20 : BCS +
|
||||
INC.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
BeanVendor_Talk:
|
||||
{
|
||||
%PlayAnimation(2,3,8)
|
||||
JSL Interface_PrepAndDisplayMessage : BCC +
|
||||
STZ.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_BeanVendor_Draw`)
|
||||
The drawing routine handles OAM allocation and animation. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations.
|
||||
|
||||
```asm
|
||||
Sprite_BeanVendor_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprFrame, X : TAY ;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
LDA.w SprFlash, X : STA $08
|
||||
LDA.w SprMiscB, X : STA $09
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
|
||||
; If SprMiscA != 0, then use 4th sheet
|
||||
LDA.b $09 : BEQ +
|
||||
LDA .chr_2, X : STA ($90), Y
|
||||
JMP ++
|
||||
+
|
||||
LDA .chr, X : STA ($90), Y
|
||||
++
|
||||
INY
|
||||
LDA .properties, X : ORA $08 : STA ($90), Y
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY
|
||||
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
.start_index
|
||||
db $00, $01, $03, $04, $06, $08
|
||||
.nbr_of_tiles
|
||||
db 0, 1, 0, 1, 1, 0
|
||||
.x_offsets
|
||||
dw 0
|
||||
dw -4, 4
|
||||
dw 0
|
||||
dw -4, 4
|
||||
dw -4, 4
|
||||
dw 0
|
||||
.y_offsets
|
||||
dw 0
|
||||
dw 0, 0
|
||||
dw 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0
|
||||
.chr
|
||||
db $80
|
||||
db $A2, $A2
|
||||
db $82
|
||||
db $84, $84
|
||||
db $A4, $A4
|
||||
db $A0
|
||||
.chr_2
|
||||
db $C0
|
||||
db $E2, $E2
|
||||
db $C2
|
||||
db $C4, $C4
|
||||
db $E4, $E4
|
||||
db $E0
|
||||
.properties
|
||||
db $35
|
||||
db $35, $75
|
||||
db $35
|
||||
db $35, $75
|
||||
db $35, $75
|
||||
db $35
|
||||
.sizes
|
||||
db $02
|
||||
db $02, $02
|
||||
db $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **NPC Interaction**: The sprite is designed to engage with the player through dialogue, triggered by proximity.
|
||||
* **State Machine**: Employs a simple state machine to manage its `Idle` and `Talk` behaviors, ensuring appropriate animations and actions based on player interaction.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,225 +0,0 @@
|
||||
# Bottle Vendor
|
||||
|
||||
## Overview
|
||||
The Bottle Vendor is an NPC (Non-Player Character) sprite designed for player interaction, primarily through dialogue. It features a simple state machine to manage its idle and talking behaviors, very similar in structure to the Bean Vendor.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `$00` (Vanilla sprite ID, likely overridden)
|
||||
* **`!NbrTiles`**: `08`
|
||||
* **`!Harmless`**: `01` (Indicates the sprite is harmless to Link)
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `01` (Indicates the sprite is impervious to all attacks)
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `01`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_BottleVendor_Long`)
|
||||
This routine is the main entry point for the Bottle Vendor, executed every frame. It handles drawing, shadow rendering, and dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_BottleVendor_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_BottleVendor_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_BottleVendor_Main
|
||||
.SpriteIsNotActive
|
||||
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_BottleVendor_Prep`)
|
||||
This routine runs once when the Bottle Vendor is spawned. It initializes `SprDefl, X`, `SprTimerC, X`, `SprNbrOAM, X`, and `SprPrize, X`.
|
||||
|
||||
```asm
|
||||
Sprite_BottleVendor_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
LDA.b #$30 : STA.w SprTimerC, X
|
||||
LDA.b #$03 : STA.w SprNbrOAM, X
|
||||
LDA.b #$03 : STA.w SprPrize, X
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_BottleVendor_Main`)
|
||||
The Bottle Vendor's core behavior is managed by a state machine with `BottleVendor_Idle` and `BottleVendor_Talk` states.
|
||||
|
||||
* **`BottleVendor_Idle`**: The vendor plays an idle animation. When Link is nearby (`GetDistance8bit_Long`), it transitions to the `BottleVendor_Talk` state.
|
||||
* **`BottleVendor_Talk`**: The vendor plays a talking animation and displays a message using `JSL Interface_PrepAndDisplayMessage`. Once the message is dismissed, it transitions back to the `BottleVendor_Idle` state.
|
||||
|
||||
```asm
|
||||
Sprite_BottleVendor_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw BottleVendor_Idle
|
||||
dw BottleVendor_Talk
|
||||
|
||||
BottleVendor_Idle:
|
||||
{
|
||||
%PlayAnimation(0,1,15)
|
||||
JSL GetDistance8bit_Long : CMP.b #$20 : BCS +
|
||||
INC.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
BottleVendor_Talk:
|
||||
{
|
||||
%PlayAnimation(2,3,8)
|
||||
JSL Interface_PrepAndDisplayMessage : BCC +
|
||||
STZ.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_BottleVendor_Draw`)
|
||||
The drawing routine handles OAM allocation and animation. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations.
|
||||
|
||||
```asm
|
||||
Sprite_BottleVendor_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprFrame, X : TAY ;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
LDA.w SprFlash, X : STA $08
|
||||
LDA.w SprMiscB, X : STA $09
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
|
||||
; If SprMiscA != 0, then use 4th sheet
|
||||
LDA.b $09 : BEQ +
|
||||
LDA .chr_2, X : STA ($90), Y
|
||||
JMP ++
|
||||
+
|
||||
LDA .chr, X : STA ($90), Y
|
||||
++
|
||||
INY
|
||||
LDA .properties, X : ORA $08 : STA ($90), Y
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY
|
||||
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
.start_index
|
||||
db $00, $01, $03, $04, $06, $08
|
||||
.nbr_of_tiles
|
||||
db 0, 1, 0, 1, 1, 0
|
||||
.x_offsets
|
||||
dw 0
|
||||
dw -4, 4
|
||||
dw 0
|
||||
dw -4, 4
|
||||
dw -4, 4
|
||||
dw 0
|
||||
.y_offsets
|
||||
dw 0
|
||||
dw 0, 0
|
||||
dw 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0
|
||||
.chr
|
||||
db $80
|
||||
db $A2, $A2
|
||||
db $82
|
||||
db $84, $84
|
||||
db $A4, $A4
|
||||
db $A0
|
||||
.chr_2
|
||||
db $C0
|
||||
db $E2, $E2
|
||||
db $C2
|
||||
db $C4, $C4
|
||||
db $E4, $E4
|
||||
db $E0
|
||||
.properties
|
||||
db $35
|
||||
db $35, $75
|
||||
db $35
|
||||
db $35, $75
|
||||
db $35, $75
|
||||
db $35
|
||||
.sizes
|
||||
db $02
|
||||
db $02, $02
|
||||
db $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **NPC Interaction**: The sprite is designed to engage with the player through dialogue, triggered by proximity.
|
||||
* **State Machine**: Employs a simple state machine to manage its `Idle` and `Talk` behaviors, ensuring appropriate animations and actions based on player interaction.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,86 +0,0 @@
|
||||
# Bug Net Kid (Sick Kid)
|
||||
|
||||
## Overview
|
||||
The Bug Net Kid, also referred to as the Sick Kid, is an NPC sprite that plays a role in a quest involving the "Song of Healing" and the acquisition of the Boots (referred to as Bug Net in the comments). This sprite is implemented by overriding vanilla game code to introduce custom interactions and progression.
|
||||
|
||||
## Vanilla Overrides
|
||||
This sprite extensively uses `pushpc`/`pullpc` blocks and `org` directives to inject custom logic into existing vanilla routines. This approach allows for modifying the behavior of a vanilla NPC without creating a new sprite ID.
|
||||
|
||||
* **`org $068D7F`**: Overrides the vanilla `SpritePrep_SickKid` routine.
|
||||
* **`org $06B962`**: Overrides a routine related to the kid's resting state (`BugNetKid_Resting`).
|
||||
* **`org $06B9C6`**: Overrides a routine responsible for granting the item (`BugNetKid_GrantBugNet`).
|
||||
|
||||
## `SickKid_CheckForSongOfHealing`
|
||||
This routine is a core component of the Bug Net Kid's logic. It checks if the "Song of Healing" has been played by examining a `SongFlag` (likely a WRAM address like `$7E001F`). If the song has been played, it updates internal sprite state variables (`$0D80, X`, `$02E4`) and clears the `SongFlag`.
|
||||
|
||||
```asm
|
||||
SickKid_CheckForSongOfHealing:
|
||||
{
|
||||
LDA.b SongFlag : CMP.b #$01 : BNE .no_song
|
||||
INC $0D80, X
|
||||
INC $02E4
|
||||
STZ.b SongFlag
|
||||
.no_song
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## `SpritePrep_SickKid` (Initialization)
|
||||
This routine is executed when the Sick Kid sprite is initialized. It checks an SRAM flag (`$7EF355`) to determine if Link has already obtained the Boots. If so, it sets `$0D80, X` to `$03`. It also increments `SprBulletproof, X`, making the kid invulnerable to attacks.
|
||||
|
||||
```asm
|
||||
SpritePrep_SickKid:
|
||||
{
|
||||
LDA.l $7EF355 : BEQ .no_boots
|
||||
LDA.b #$03 : STA $0D80, X
|
||||
.no_boots
|
||||
INC.w SprBulletproof, X
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## `BugNetKid_Resting` (Main Logic)
|
||||
This routine controls the kid's behavior when not actively granting an item. It checks for player preoccupation and damage, and crucially, calls `SickKid_CheckForSongOfHealing`. If Link has not yet received the Boots, it displays a solicited message to the player.
|
||||
|
||||
```asm
|
||||
BugNetKid_Resting:
|
||||
{
|
||||
JSL Sprite_CheckIfPlayerPreoccupied : BCS .dont_awaken
|
||||
JSR Sprite_CheckDamageToPlayer_same_layer : BCC .dont_awaken
|
||||
JSL SickKid_CheckForSongOfHealing
|
||||
LDA.l $7EF355
|
||||
CMP.b #$01 : BCC .no_boots
|
||||
.dont_awaken
|
||||
RTS
|
||||
|
||||
.no_boots
|
||||
LDA.b #$04
|
||||
LDY.b #$01
|
||||
JSL Sprite_ShowSolicitedMessageIfPlayerFacing
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## `BugNetKid_GrantBugNet` (Item Granting)
|
||||
This routine is responsible for giving Link the Boots. It sets the item ID (`LDY.b #$4B`), clears a flag (`$02E9`), calls `JSL Link_ReceiveItem` to add the item to Link's inventory, and updates internal sprite state variables (`$0D80, X`, `$02E4`).
|
||||
|
||||
```asm
|
||||
BugNetKid_GrantBugNet:
|
||||
{
|
||||
; Give Link the Boots
|
||||
LDY.b #$4B
|
||||
STZ $02E9
|
||||
PHX
|
||||
JSL Link_ReceiveItem
|
||||
PLX
|
||||
INC $0D80, X
|
||||
STZ $02E4
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Vanilla Override**: This sprite is a prime example of overriding vanilla game code to introduce new NPC interactions and quest elements without creating entirely new sprite definitions.
|
||||
* **Quest/Item Gating**: The sprite's behavior and the ability to receive the Boots are directly tied to specific game progression flags, such as the `SongFlag` and the SRAM flag for the Boots (`$7EF355`).
|
||||
* **NPC Interaction**: The sprite interacts with the player by displaying messages and granting a key item, driving forward a specific questline.
|
||||
* **Global Flags and SRAM Usage**: Utilizes global WRAM flags (`$02E4`, `SongFlag`) and SRAM (`$7EF355`) to maintain and track the state of the quest across game sessions.
|
||||
@@ -1,307 +0,0 @@
|
||||
# Deku Scrub
|
||||
|
||||
## Overview
|
||||
The Deku Scrub sprite is a highly versatile NPC implementation capable of representing multiple distinct characters, including a Withered Deku Scrub, Deku Butler, and Deku Princess. Its behavior is intricately tied to game progression, player actions, and specific in-game locations.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_DekuScrubNPCs` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `06`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `03`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_DekuScrub_Long`)
|
||||
This routine is the main entry point for the Deku Scrub, executed every frame. It handles drawing and dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_DekuScrub_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_DekuScrub_Draw
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_DekuScrub_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_DekuScrub_Prep`)
|
||||
This routine runs once when the Deku Scrub is spawned. It sets `SprDefl, X` and then determines the initial `SprAction, X` based on the current `AreaIndex`, `SprSubtype, X`, and whether the Deku Mask has been obtained (`$7EF301`). It also checks if Tail Palace is cleared (`Crystals`) to potentially set `SprState, X` to `0`.
|
||||
|
||||
```asm
|
||||
Sprite_DekuScrub_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
|
||||
; Peacetime Deku Scrub NPCs
|
||||
LDA.b AreaIndex : CMP.b #$2E : BNE .check_next
|
||||
; Deku Butler
|
||||
LDA.b #$07 : STA.w SprAction, X
|
||||
JMP +
|
||||
.check_next
|
||||
CMP.b #$2F : BNE .continue
|
||||
LDA.b #$08 : STA.w SprAction, X
|
||||
JMP +
|
||||
.continue
|
||||
|
||||
LDA.w SprSubtype, X : CMP.b #$01 : BEQ .DekuButler
|
||||
CMP.b #$02 : BEQ .DekuPrincess
|
||||
LDA.l $7EF301 : BEQ +
|
||||
LDA.b #$04 : STA.w SprAction, X
|
||||
JMP +
|
||||
.DekuButler
|
||||
LDA.b #$05 : STA.w SprAction, X
|
||||
JMP ++
|
||||
.DekuPrincess
|
||||
LDA.b #$06 : STA.w SprAction, X
|
||||
++
|
||||
; Check if tail palace is cleared
|
||||
LDA.l Crystals : AND #$10 : BEQ +
|
||||
STZ.w SprState, X
|
||||
+
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_DekuScrub_Main`)
|
||||
The Deku Scrub's core behavior is managed by a complex state machine with several states, many of which are named in Spanish:
|
||||
|
||||
* **`EstadoInactivo` (Inactive State)**: The scrub plays an idle animation, prevents player passage, and transitions to `QuiereCuracion` upon player interaction.
|
||||
* **`QuiereCuracion` (Wants Healing)**: Plays an animation and checks if the "Song of Healing" (`SongFlag`) has been played. If so, it clears the flag, sets a timer, and transitions to `DarMascara`.
|
||||
* **`DarMascara` (Give Mask)**: Plays an animation, displays a message after a timer, and then transitions to `Regalo`.
|
||||
* **`Regalo` (Gift)**: After a timer, grants Link the Deku Mask (`$11`) and updates the Deku Mask flag (`$7EF301`), then transitions to `Withered`.
|
||||
* **`Withered`**: Plays a withered animation.
|
||||
* **`DekuButler`**: Plays a specific animation, prevents player passage, and displays a message.
|
||||
* **`DekuPrincess`**: Plays a specific animation, prevents player passage, and displays a message.
|
||||
* **`DekuButler_Peacetime`**: Plays a specific animation, prevents player passage, and displays a message. If the message is dismissed, it sets `MapIcon` to `$02`.
|
||||
* **`DekuPrinces_Peacetime`**: Plays a specific animation, prevents player passage, and displays a message. If the message is dismissed, it sets `MapIcon` to `$02`.
|
||||
|
||||
```asm
|
||||
Sprite_DekuScrub_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw EstadoInactivo
|
||||
dw QuiereCuracion
|
||||
dw DarMascara
|
||||
dw Regalo
|
||||
dw Withered
|
||||
dw DekuButler
|
||||
dw DekuPrincess
|
||||
|
||||
dw DekuButler_Peacetime
|
||||
dw DekuPrinces_Peacetime
|
||||
|
||||
EstadoInactivo:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
%ShowSolicitedMessage($140) : BCC .no_hablaba
|
||||
%GotoAction(1)
|
||||
.no_hablaba
|
||||
RTS
|
||||
}
|
||||
|
||||
QuiereCuracion:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
LDA.b SongFlag : CMP.b #$01 : BNE .ninguna_cancion
|
||||
STZ.b SongFlag
|
||||
LDA.b #$C0 : STA.w SprTimerD, X
|
||||
%GotoAction(2)
|
||||
.ninguna_cancion
|
||||
RTS
|
||||
}
|
||||
|
||||
DarMascara:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
LDA.w SprTimerD, X : BNE +
|
||||
%ShowUnconditionalMessage($141)
|
||||
LDA.b #$C0 : STA.w SprTimerD, X
|
||||
%GotoAction(3)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Regalo:
|
||||
{
|
||||
LDA.w SprTimerD, X : BNE +
|
||||
LDY #$11 : STZ $02E9 ; Give the Deku Mask
|
||||
JSL Link_ReceiveItem
|
||||
LDA.b #$01 : STA.l $7EF301
|
||||
%GotoAction(4)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Withered:
|
||||
{
|
||||
%PlayAnimation(2, 2, 10)
|
||||
RTS
|
||||
}
|
||||
|
||||
DekuButler:
|
||||
{
|
||||
%PlayAnimation(3, 3, 10)
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
%ShowSolicitedMessage($080)
|
||||
RTS
|
||||
}
|
||||
|
||||
DekuPrincess:
|
||||
{
|
||||
%PlayAnimation(4, 4, 10)
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
%ShowSolicitedMessage($0C3)
|
||||
RTS
|
||||
}
|
||||
|
||||
DekuButler_Peacetime:
|
||||
{
|
||||
%StartOnFrame(3)
|
||||
%PlayAnimation(3, 3, 10)
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
%ShowSolicitedMessage($1B9) : BCC +
|
||||
LDA.b #$02 : STA.l MapIcon
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
DekuPrinces_Peacetime:
|
||||
{
|
||||
%StartOnFrame(4)
|
||||
%PlayAnimation(4, 4, 10)
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
%ShowSolicitedMessage($1BA) : BCC +
|
||||
LDA.b #$02 : STA.l MapIcon
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_DekuScrub_Draw`)
|
||||
The drawing routine handles OAM allocation and animation. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations.
|
||||
|
||||
```asm
|
||||
Sprite_DekuScrub_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA $0DC0, X : CLC : ADC $0D90, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
.start_index
|
||||
db $00, $04, $08, $0C, $10
|
||||
.nbr_of_tiles
|
||||
db 3, 3, 3, 3, 3
|
||||
.x_offsets
|
||||
dw 4, 4, -4, -4
|
||||
dw 4, -4, -4, 4
|
||||
dw -8, -8, 8, 8
|
||||
dw -4, 4, -4, 4
|
||||
dw -4, -4, 4, 4
|
||||
.y_offsets
|
||||
dw 4, -4, -4, 4
|
||||
dw 4, 4, -4, -4
|
||||
dw 4, -12, -12, 4
|
||||
dw -12, -12, 4, 4
|
||||
dw 4, -12, 4, -12
|
||||
.chr
|
||||
db $2E, $0E, $0E, $2E
|
||||
db $2C, $2C, $0C, $0C
|
||||
db $20, $00, $02, $22
|
||||
db $04, $05, $24, $25
|
||||
db $27, $07, $27, $07
|
||||
.properties
|
||||
db $3B, $7B, $3B, $7B
|
||||
db $3B, $7B, $3B, $7B
|
||||
db $3B, $3B, $3B, $3B
|
||||
db $3B, $3B, $3B, $3B
|
||||
db $3B, $3B, $7B, $7B
|
||||
.sizes
|
||||
db $02, $02, $02, $02
|
||||
db $02, $02, $02, $02
|
||||
db $02, $02, $02, $02
|
||||
db $02, $02, $02, $02
|
||||
db $02, $02, $02, $02
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Character NPC**: A single sprite definition is used to represent multiple distinct NPC characters (Withered Deku Scrub, Deku Butler, Deku Princess), with their specific roles determined by `SprSubtype` and `AreaIndex`.
|
||||
* **Quest Progression Integration**: The sprite's behavior is deeply integrated with various quest elements, checking for specific items (Deku Mask), songs (Song of Healing), and cleared dungeons (Tail Palace) to determine its current state and interactions.
|
||||
* **Conditional Behavior**: Extensive use of conditional logic based on `AreaIndex`, `SprSubtype`, and global game state flags allows for dynamic changes in the NPC's role, dialogue, and actions.
|
||||
* **NPC Interaction**: Provides rich interaction with the player through dialogue (`%ShowSolicitedMessage`, `%ShowUnconditionalMessage`) and the granting of key items (`Link_ReceiveItem`).
|
||||
* **Player Collision**: Implements `Sprite_PlayerCantPassThrough` to make the NPC a solid object that Link cannot walk through.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, essential for accurate sprite rendering.
|
||||
@@ -1,204 +0,0 @@
|
||||
# Eon Owl / Kaepora Gaebora
|
||||
|
||||
## Overview
|
||||
This sprite is a sophisticated NPC implementation that serves as both the "Eon Owl" and "Kaepora Gaebora" (a character from The Legend of Zelda: Ocarina of Time). Its appearance, behavior, and interactions are highly conditional, depending on the player's location and various game progression flags.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_EonOwl` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `03`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `01` (Deflects all projectiles)
|
||||
* **`!ImperviousArrow`**: `01` (Impervious to arrows)
|
||||
* **`!ImpervSwordHammer`**: `01` (Impervious to sword and hammer attacks)
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_EonOwl_Long`)
|
||||
This routine serves as a dispatcher for the Eon Owl and Kaepora Gaebora, and includes logic for conditional despawning based on game state.
|
||||
|
||||
* **Kaepora Gaebora Logic**: If the `AreaIndex` is `$0E` (Hall of Secrets map) and certain conditions regarding collected crystals (`$7EF37A`) and the player's possession of the "Song of Soaring" (`$7EF34C`) are met, the sprite is identified as Kaepora Gaebora (`SprSubtype, X` set to `01`) and `Sprite_KaeporaGaebora_Draw` is called.
|
||||
* **Eon Owl Logic**: Otherwise, `Sprite_EonOwl_Draw` is called.
|
||||
* **Despawning**: If conditions for either character are not met, the sprite despawns (`STZ.w SprState, X`).
|
||||
|
||||
```asm
|
||||
Sprite_EonOwl_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
; If it is not the Hall of Secrets map
|
||||
LDA.b $8A : CMP.b #$0E : BNE .NotGaebora
|
||||
; If the map doesn't have the 6 crystals
|
||||
LDA.l $7EF37A : CMP.b #$77 : BNE .Despawn
|
||||
; If the player has the Song of Soaring, despawn
|
||||
LDA.l $7EF34C : CMP.b #$03 : BCS .Despawn
|
||||
LDA.b #$01 : STA.w SprSubtype, X
|
||||
JSR Sprite_KaeporaGaebora_Draw
|
||||
JMP .HandleSprite
|
||||
.NotGaebora
|
||||
JSR Sprite_EonOwl_Draw
|
||||
.HandleSprite
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_EonOwl_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
.Despawn
|
||||
STZ.w SprState, X
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_EonOwl_Prep`)
|
||||
This routine initializes the sprite upon spawning, including setting its hitbox and handling conditional despawning for the intro sequence.
|
||||
|
||||
* **Hitbox**: `SprHitbox, X` is set to `0`.
|
||||
* **Kaepora Gaebora Initialization**: If `AreaIndex` is `$0E`, `SprTimerA, X` is set to `$20` and `SprAction, X` to `$03`.
|
||||
* **Intro Despawn**: If `AreaIndex` is `$50` (Intro Map) and Link already has the Sword, the sprite despawns.
|
||||
|
||||
```asm
|
||||
Sprite_EonOwl_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
|
||||
STZ.w SprHitbox, X
|
||||
|
||||
LDA.b $8A : CMP.b #$0E : BNE .NotGaebora
|
||||
LDA.b #$20 : STA.w SprTimerA, X
|
||||
LDA.b #$03 : STA.w SprAction, X
|
||||
.NotGaebora
|
||||
LDA.w AreaIndex : CMP.b #$50 : BNE .not_intro
|
||||
; If Map 0x50, don't spawn after getting sword
|
||||
LDA.l Sword : CMP.b #$01 : BCC .continue
|
||||
STZ.w SprState, X
|
||||
.continue
|
||||
.not_intro
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_EonOwl_Main`)
|
||||
This routine manages the various states and behaviors of both the Eon Owl and Kaepora Gaebora.
|
||||
|
||||
* **`EonOwl_Idle`**: The Eon Owl plays an idle animation and transitions to `EonOwl_IntroDialogue` when Link is nearby.
|
||||
* **`EonOwl_IntroDialogue`**: Displays an introductory message and then transitions to `EonOwl_FlyingAway`.
|
||||
* **`EonOwl_FlyingAway`**: The Eon Owl plays a flying animation, moves upwards, and despawns after a timer.
|
||||
* **`KaeporaGaebora`**: Kaepora Gaebora plays an idle animation and, if Link is at a certain distance and a timer allows, displays a message and transitions to `KaeporaGaebora_Respond`.
|
||||
* **`KaeporaGaebora_Respond`**: Processes the player's dialogue choice. If the player declines, it transitions back to `KaeporaGaebora`. If the player accepts, it transitions to `KaeporaGaebora_FlyAway` and grants the "Song of Soaring" (`$7EF34C`).
|
||||
* **`KaeporaGaebora_FlyAway`**: Kaepora Gaebora flies upwards and despawns after a timer.
|
||||
|
||||
```asm
|
||||
Sprite_EonOwl_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw EonOwl_Idle
|
||||
dw EonOwl_IntroDialogue
|
||||
dw EonOwl_FlyingAway
|
||||
|
||||
dw KaeporaGaebora
|
||||
dw KaeporaGaebora_Respond
|
||||
dw KaeporaGaebora_FlyAway
|
||||
|
||||
EonOwl_Idle:
|
||||
{
|
||||
%PlayAnimation(0,1,16)
|
||||
JSL GetDistance8bit_Long : CMP #$28 : BCS .not_too_close
|
||||
%GotoAction(1)
|
||||
.not_too_close
|
||||
RTS
|
||||
}
|
||||
|
||||
EonOwl_IntroDialogue:
|
||||
{
|
||||
%PlayAnimation(0,1,16)
|
||||
%ShowUnconditionalMessage($00E6)
|
||||
LDA.b #$C0 : STA.w SprTimerA, X
|
||||
%GotoAction(2)
|
||||
RTS
|
||||
}
|
||||
|
||||
EonOwl_FlyingAway:
|
||||
{
|
||||
%PlayAnimation(2,3,10)
|
||||
LDA.b #$F8 : STA.w SprYSpeed, X
|
||||
JSL Sprite_Move
|
||||
|
||||
LDA.w SprTimerA, X : CMP.b #$80 : BNE +
|
||||
LDA.b #$40 : STA.w SprXSpeed, X
|
||||
+
|
||||
|
||||
LDA.w SprTimerA, X : BNE .not_done
|
||||
STZ.w SprState, X
|
||||
.not_done
|
||||
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x03 - Kaepora Gaebora
|
||||
KaeporaGaebora:
|
||||
{
|
||||
%PlayAnimation(0,0,1)
|
||||
JSL GetDistance8bit_Long : CMP.b #$50 : BCC .not_ready
|
||||
LDA.w SprTimerA, X : BNE .not_ready
|
||||
%ShowUnconditionalMessage($146)
|
||||
%GotoAction(4)
|
||||
.not_ready
|
||||
RTS
|
||||
}
|
||||
|
||||
KaeporaGaebora_Respond:
|
||||
{
|
||||
LDA $1CE8 : BNE .player_said_no
|
||||
%GotoAction(3)
|
||||
RTS
|
||||
.player_said_no
|
||||
%GotoAction(5)
|
||||
LDA.b #$60 : STA.w SprTimerA, X
|
||||
LDA.b #$03 : STA.l $7EF34C
|
||||
RTS
|
||||
}
|
||||
|
||||
FlyAwaySpeed = 10
|
||||
KaeporaGaebora_FlyAway:
|
||||
{
|
||||
LDA.b #-FlyAwaySpeed : STA.w SprYSpeed, X
|
||||
JSL Sprite_Move
|
||||
LDA.w SprTimerA, X : BNE .not_ready
|
||||
STZ.w SprState, X
|
||||
.not_ready
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_EonOwl_Draw` and `Sprite_KaeporaGaebora_Draw`)
|
||||
Both drawing routines handle OAM allocation and animation, using `REP #$20` and `SEP #$20` for 16-bit coordinate calculations. Each has its own specific OAM data for rendering the respective character.
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Character NPC**: A single sprite definition dynamically represents two distinct NPCs (Eon Owl and Kaepora Gaebora) based on `AreaIndex` and game state, showcasing efficient sprite reuse.
|
||||
* **Conditional Spawning/Despawning**: The sprite's visibility and existence are tightly controlled by game progression, including collected items (crystals, sword) and player inventory (Song of Soaring), making it appear only when relevant to the narrative.
|
||||
* **Quest Progression Integration**: The sprite's dialogue and actions are directly linked to specific quest milestones, guiding the player through the game's story.
|
||||
* **NPC Interaction with Dialogue Choices**: Kaepora Gaebora presents the player with dialogue options, and the player's choice influences game outcomes, such as receiving the "Song of Soaring."
|
||||
* **Flying Behavior**: Implements realistic flying animations and movement, including flying away sequences with controlled speed and timers.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering and positioning.
|
||||
@@ -1,214 +0,0 @@
|
||||
# Eon Zora
|
||||
|
||||
## Overview
|
||||
The Eon Zora is an NPC (Non-Player Character) sprite found in the Eon Abyss. Its behavior is characterized by random movement and context-sensitive dialogue that changes based on Link's current location within the game world.
|
||||
|
||||
## Sprite Properties
|
||||
Explicit sprite properties (`!SPRID`, `!NbrTiles`, etc.) are not defined within this file. It is assumed that these properties are either inherited from a vanilla sprite ID or defined in a separate configuration file, as this file focuses on the sprite's behavior and drawing.
|
||||
|
||||
## Main Logic (`Sprite_EonZora_Main`)
|
||||
This routine is the main entry point for the Eon Zora, executed every frame. It orchestrates the Zora's dialogue, movement, and animation.
|
||||
|
||||
* **Dialogue**: Calls `EonZora_HandleDialogue` to manage interactions with the player.
|
||||
* **Movement**: Calls `EonZora_Walk` for random movement, followed by `JSL Sprite_Move` and `JSL Sprite_BounceFromTileCollision` for physical movement and collision handling.
|
||||
* **Directional Animations**: Uses a jump table to play specific animations based on the Zora's current direction (Forward, Left, Right, Back).
|
||||
|
||||
```asm
|
||||
Sprite_EonZora_Main:
|
||||
{
|
||||
JSR EonZora_HandleDialogue
|
||||
JSR EonZora_Walk
|
||||
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw EonZora_Forward
|
||||
dw EonZora_Left
|
||||
dw EonZora_Right
|
||||
dw EonZora_Back
|
||||
|
||||
EonZora_Forward:
|
||||
%PlayAnimation(0,1,10)
|
||||
RTS
|
||||
EonZora_Left:
|
||||
%PlayAnimation(2,3,10)
|
||||
RTS
|
||||
EonZora_Right:
|
||||
%PlayAnimation(4,5,10)
|
||||
RTS
|
||||
EonZora_Back:
|
||||
%PlayAnimation(6,7,10)
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Movement Routine (`EonZora_Walk`)
|
||||
This routine controls the Eon Zora's random walking behavior. It uses a timer (`SprTimerA, X`) to periodically select a new random direction and update the sprite's `SprXSpeed, X` and `SprYSpeed, X`.
|
||||
|
||||
```asm
|
||||
EonZora_Walk:
|
||||
{
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
JSL GetRandomInt : AND.b #$03 : STA.w SprAction, X : TAY
|
||||
LDA.w .speed_x, Y : STA.w SprXSpeed, X
|
||||
LDA.w .speed_y, Y : STA.w SprYSpeed, X
|
||||
LDA.b #$6A : STA.w SprTimerA, X
|
||||
+
|
||||
RTS
|
||||
|
||||
.speed_x
|
||||
db 0, -4, 4, 0
|
||||
.speed_y
|
||||
db 4, 0, 0, -4
|
||||
}
|
||||
```
|
||||
|
||||
## Dialogue Handling (`EonZora_HandleDialogue`)
|
||||
This routine manages the Eon Zora's dialogue, which is context-sensitive based on Link's current `AreaIndex`. It checks for specific `AreaIndex` values to display tailored messages. If no specific area matches, a default message is displayed, and interacting with it can randomly set the `FOUNDRINGS` global variable.
|
||||
|
||||
```asm
|
||||
EonZora_HandleDialogue:
|
||||
{
|
||||
LDA.w AreaIndex : CMP.b #$63 : BNE .not_wisdom
|
||||
%ShowSolicitedMessage($01AC)
|
||||
JMP ++
|
||||
.not_wisdom
|
||||
CMP.b #$5B : BNE .not_power
|
||||
%ShowSolicitedMessage($01AB)
|
||||
JMP ++
|
||||
.not_power
|
||||
CMP.b #$40 : BNE .not_pyramid
|
||||
%ShowSolicitedMessage($01AA)
|
||||
JMP ++
|
||||
.not_pyramid
|
||||
CMP.b #$70 : BNE .not_underwater
|
||||
%ShowSolicitedMessage($01AD)
|
||||
JMP ++
|
||||
.not_underwater
|
||||
CMP.b #$42 : BNE .not_portal
|
||||
%ShowSolicitedMessage($01AF)
|
||||
JMP ++
|
||||
.not_portal
|
||||
%ShowSolicitedMessage($01AE) : BCC .no_talk
|
||||
JSL GetRandomInt : AND.b #$06 : STA.l FOUNDRINGS
|
||||
.no_talk
|
||||
++
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_EonZora_Draw`)
|
||||
The drawing routine handles OAM allocation and animation. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
```asm
|
||||
Sprite_EonZora_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprFrame, X : TAY ;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
.start_index
|
||||
db $00, $02, $04, $06, $08, $0A, $0C, $0D
|
||||
.nbr_of_tiles
|
||||
db 1, 1, 1, 1, 1, 1, 0, 0
|
||||
.x_offsets
|
||||
dw 0, 16
|
||||
dw 0, -16
|
||||
dw 0, 8
|
||||
dw 0, 8
|
||||
dw 0, -8
|
||||
dw 0, -8
|
||||
dw 0
|
||||
dw 0
|
||||
.y_offsets
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0
|
||||
dw 0
|
||||
.chr
|
||||
db $60, $62
|
||||
db $60, $62
|
||||
db $40, $41
|
||||
db $43, $44
|
||||
db $40, $41
|
||||
db $43, $44
|
||||
db $64
|
||||
db $64
|
||||
.properties
|
||||
db $39, $39
|
||||
db $79, $79
|
||||
db $39, $39
|
||||
db $39, $39
|
||||
db $79, $79
|
||||
db $79, $79
|
||||
db $39
|
||||
db $79
|
||||
.sizes
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02
|
||||
db $02
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Context-Sensitive Dialogue**: The NPC's dialogue dynamically changes based on Link's current `AreaIndex`, providing a rich and immersive storytelling experience tailored to the player's location.
|
||||
* **Random Movement**: The Zora exhibits random walking behavior, contributing to the environmental ambiance and making the world feel more alive.
|
||||
* **NPC Interaction**: Provides dialogue and has the potential to grant items (randomly setting `FOUNDRINGS`), adding an element of surprise and reward to player interactions.
|
||||
* **Animation-Driven Movement**: The sprite's movement states are directly tied to specific animations for each direction, ensuring visual consistency between its actions and appearance.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,127 +0,0 @@
|
||||
# Eon Zora Elder
|
||||
|
||||
## Overview
|
||||
The Eon Zora Elder is an NPC (Non-Player Character) sprite primarily characterized by its animation-driven states. Its main function is to visually convey different moods or actions through distinct animations, such as idle, surprised, or holding a rod.
|
||||
|
||||
## Sprite Properties
|
||||
Explicit sprite properties (`!SPRID`, `!NbrTiles`, etc.) are not defined within this file. It is assumed that these properties are either inherited from a vanilla sprite ID or defined in a separate configuration file, as this file focuses on the sprite's behavior and drawing.
|
||||
|
||||
## Main Logic & State Machine (`Sprite_EonZoraElder_Main`)
|
||||
The Eon Zora Elder's core behavior is managed by a simple state machine that primarily controls its animations:
|
||||
|
||||
* **`EonZoraElder_Idle`**: Plays an idle animation (`%PlayAnimation(0,1,10)`).
|
||||
* **`EonZoraElder_Surprised`**: Plays a surprised animation (`%PlayAnimation(2,3,10)`).
|
||||
* **`EonZoraElder_WithRod`**: Plays an animation depicting the elder holding a rod (`%PlayAnimation(4,4,10)`).
|
||||
|
||||
```asm
|
||||
Sprite_EonZoraElder_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw EonZoraElder_Idle
|
||||
dw EonZoraElder_Surprised
|
||||
dw EonZoraElder_WithRod
|
||||
|
||||
EonZoraElder_Idle:
|
||||
%PlayAnimation(0,1,10)
|
||||
RTS
|
||||
EonZoraElder_Surprised:
|
||||
%PlayAnimation(2,3,10)
|
||||
RTS
|
||||
EonZoraElder_WithRod:
|
||||
%PlayAnimation(4,4,10)
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_EonZoraElder_Draw`)
|
||||
The drawing routine handles OAM allocation and animation. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
```asm
|
||||
Sprite_EonZoraElder_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprFrame, X : TAY ;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
|
||||
.start_index
|
||||
db $00, $02, $04, $06
|
||||
.nbr_of_tiles
|
||||
db 1, 1, 1, 2
|
||||
.x_offsets
|
||||
dw 0, 8
|
||||
dw 0, 8
|
||||
dw 0, 8
|
||||
dw 0, 8, -4
|
||||
.y_offsets
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0, 0
|
||||
.chr
|
||||
db $46, $47
|
||||
db $49, $4A
|
||||
db $66, $67
|
||||
db $69, $6A, $6C
|
||||
.properties
|
||||
db $39, $39
|
||||
db $39, $39
|
||||
db $39, $39
|
||||
db $39, $39, $39
|
||||
.sizes
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02, $02
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Animation-Driven States**: The sprite's states are primarily used to control which animation is currently playing, allowing for visual feedback to the player (e.g., idle, surprised, holding a rod).
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,333 +0,0 @@
|
||||
# Farore
|
||||
|
||||
## Overview
|
||||
Farore, the Oracle of Secrets, is a pivotal NPC sprite deeply integrated into the game's narrative and cutscene system. Her behavior is highly dynamic, adapting to the player's location (indoors/outdoors) and various game progression flags. She plays a crucial role in guiding the player and controlling cinematic sequences.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_Farore` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `2`
|
||||
* **`!Harmless`**: `00` (Unusual for an NPC, might indicate specific interaction or placeholder)
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `0`
|
||||
* **`!Damage`**: `0`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `01`
|
||||
* **`!Shadow`**: `01`
|
||||
* **`!Palette`**: `0`
|
||||
* **`!Hitbox`**: `0`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `0`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Farore_Long`)
|
||||
This routine acts as a dispatcher, conditionally calling different drawing and main logic routines based on whether Link is `INDOORS`. This indicates that the `Farore` sprite ID is reused for a different entity (likely "Hyrule Dream") when indoors.
|
||||
|
||||
```asm
|
||||
Sprite_Farore_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b INDOORS : BEQ .outdoors
|
||||
JSR Sprite_HyruleDream_Draw
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_HyruleDream_Main
|
||||
JMP .SpriteIsNotActive
|
||||
.outdoors
|
||||
JSR Sprite_Farore_Draw
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Farore_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Farore_Prep`)
|
||||
This routine initializes Farore upon spawning. It sets `SprDefl, X` to `$80` to prevent despawning off-screen. It also includes conditional initialization based on `INDOORS` and a check for `$7EF300` (likely a flag for Farore's presence) to potentially despawn the sprite.
|
||||
|
||||
```asm
|
||||
Sprite_Farore_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$80 : STA.w SprDefl, X ; Don't kill Farore when she goes off screen
|
||||
LDA.b INDOORS : BEQ .outdoors
|
||||
JSR Sprite_HyruleDream_Prep
|
||||
JMP .PlayIntro
|
||||
.outdoors
|
||||
LDA.l $7EF300 : BEQ .PlayIntro
|
||||
STZ.w SprState, X ; Kill the sprite
|
||||
.PlayIntro
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Farore_Main`)
|
||||
Farore's core behavior is managed by a complex state machine heavily involved in cutscenes and quest progression:
|
||||
|
||||
* **`IntroStart`**: Initiates a cutscene (`InCutScene = 01`) and transitions to different states based on `STORY_STATE` (`$B6`).
|
||||
* **`MoveUpTowardsFarore`**: Controls Link's movement during a cutscene, slowing him down and moving him north. Transitions to `MoveLeftTowardsFarore` when Link reaches a certain Y-position.
|
||||
* **`MoveLeftTowardsFarore`**: Continues Link's controlled movement, moving him west. Stops auto-movement, sets a timer, and transitions to `WaitAndMessage`.
|
||||
* **`WaitAndMessage`**: Displays a message after a timer, applies speed towards the player, and transitions to `Farore_ProceedWithCutscene`.
|
||||
* **`Farore_ProceedWithCutscene`**: A transitional state that leads to `FaroreFollowPlayer` after a timer.
|
||||
* **`FaroreFollowPlayer`**: Farore follows Link, controlling his movement and updating various game state flags (`GAMESTATE`, `STORY_STATE`, rain sound). Transitions to `MakuArea_FaroreFollowPlayer`.
|
||||
* **`MakuArea_FaroreFollowPlayer`**: Farore continues to follow Link in the Maku Area.
|
||||
* **`MakuArea_FaroreWaitForKydrog`**: Farore waits in the Maku Area.
|
||||
|
||||
```asm
|
||||
Sprite_Farore_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw IntroStart
|
||||
dw MoveUpTowardsFarore
|
||||
dw MoveLeftTowardsFarore
|
||||
dw WaitAndMessage
|
||||
dw Farore_ProceedWithCutscene
|
||||
dw FaroreFollowPlayer
|
||||
dw MakuArea_FaroreFollowPlayer
|
||||
dw MakuArea_FaroreWaitForKydrog
|
||||
|
||||
; 00
|
||||
IntroStart:
|
||||
{
|
||||
LDA #$01 : STA InCutScene
|
||||
LDA $B6 : CMP.b #$01 : BEQ .maku_area
|
||||
CMP.b #$02 : BEQ .waiting
|
||||
%GotoAction(1)
|
||||
RTS
|
||||
.maku_area
|
||||
%GotoAction(6)
|
||||
RTS
|
||||
|
||||
.waiting
|
||||
%GotoAction(7)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 01
|
||||
MoveUpTowardsFarore:
|
||||
{
|
||||
LDA.w WALKSPEED : STA.b $57 ; Slow Link down for the cutscene
|
||||
LDA.b #$08 : STA.b $49 ; Auto-movement north
|
||||
|
||||
; Link's Y Position - Y = 6C
|
||||
LDA.b $20 : CMP.b #$9C : BCC .linkistoofar
|
||||
%GotoAction(2)
|
||||
.linkistoofar
|
||||
%PlayAnimation(6, 6, 8) ; Farore look towards Link
|
||||
RTS
|
||||
}
|
||||
|
||||
; 02
|
||||
MoveLeftTowardsFarore:
|
||||
{
|
||||
; Move Link Left
|
||||
LDA.w WALKSPEED : STA.b $57 ; Slow Link down for the cutscene
|
||||
LDA.b #$02 : STA.b $49
|
||||
|
||||
; Link's X position
|
||||
LDA.b $22 : CMP.b #$1A : BCS .linkistoofar
|
||||
STZ.b $49 ; kill automove
|
||||
LDA.b #$20
|
||||
STA.w SprTimerA, X ; set timer A to 0x10
|
||||
%PlayAnimation(0, 0, 8)
|
||||
%GotoAction(3)
|
||||
.linkistoofar
|
||||
RTS
|
||||
}
|
||||
|
||||
; 03
|
||||
WaitAndMessage:
|
||||
{
|
||||
%PlayAnimation(1, 2, 8)
|
||||
LDA.b #$15
|
||||
JSL Sprite_ApplySpeedTowardsPlayer
|
||||
JSL Sprite_MoveVert
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
STZ $2F
|
||||
LDA #$00 : STA InCutScene
|
||||
; "I am Farore, the Oracle of Secrets."
|
||||
%ShowUnconditionalMessage($0E)
|
||||
%GotoAction(4)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
; 04
|
||||
Farore_ProceedWithCutscene:
|
||||
{
|
||||
LDA.w SprTimerA, X : BNE ++
|
||||
%GotoAction(5)
|
||||
++
|
||||
RTS
|
||||
}
|
||||
|
||||
; 05
|
||||
FaroreFollowPlayer:
|
||||
{
|
||||
LDA #$01 : STA InCutScene
|
||||
LDA.w WALKSPEED : STA.b $57 ; Slow Link down for the cutscene
|
||||
LDA.b #$08 : STA.b $49 ; Auto-movement north
|
||||
%PlayAnimation(3, 4, 8)
|
||||
|
||||
LDA.b #$15
|
||||
JSL Sprite_ApplySpeedTowardsPlayer
|
||||
JSL Sprite_MoveVert
|
||||
|
||||
LDA #$02 : STA $7EF3C5 ; (0 - intro, 1 - pendants, 2 - crystals)
|
||||
LDA #$05 : STA $012D ; turn off rain sound
|
||||
LDA #$01 : STA $B6 ; Set Story State
|
||||
JSL Sprite_LoadGfxProperties
|
||||
|
||||
%GotoAction(6)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 06
|
||||
MakuArea_FaroreFollowPlayer:
|
||||
{
|
||||
%PlayAnimation(3, 4, 8)
|
||||
|
||||
LDA.b #$15
|
||||
JSL Sprite_ApplySpeedTowardsPlayer
|
||||
JSL Sprite_MoveVert
|
||||
|
||||
%GotoAction(6)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 07
|
||||
MakuArea_FaroreWaitForKydrog:
|
||||
{
|
||||
%PlayAnimation(5, 5, 8)
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_Farore_Draw`)
|
||||
This routine handles OAM allocation and animation for Farore. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations.
|
||||
|
||||
```asm
|
||||
Sprite_Farore_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA $0DC0, X : CLC : ADC $0D90, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY
|
||||
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
.start_index
|
||||
db $00, $02, $04, $06, $08, $0A, $0C
|
||||
.nbr_of_tiles
|
||||
db 1, 1, 1, 1, 1, 1, 1
|
||||
.x_offsets
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, -1
|
||||
.y_offsets
|
||||
dw -8, 4
|
||||
dw -8, 4
|
||||
dw 4, -8
|
||||
dw -8, 4
|
||||
dw 4, -7
|
||||
dw -8, 4
|
||||
dw 4, -7
|
||||
.chr
|
||||
db $A8, $AA
|
||||
db $A8, $88
|
||||
db $AA, $A8
|
||||
db $8A, $8C
|
||||
db $8C, $8A
|
||||
db $8A, $AC
|
||||
db $AA, $86
|
||||
.properties
|
||||
db $3B, $3B
|
||||
db $3B, $7B
|
||||
db $3B, $3B
|
||||
db $3B, $3B
|
||||
db $7B, $3B
|
||||
db $3B, $3B
|
||||
db $3B, $7B
|
||||
.sizes
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Character Sprite (Conditional Drawing/Logic)**: The sprite ID is reused for "Hyrule Dream" when indoors, demonstrating a powerful technique for resource optimization and context-sensitive character representation.
|
||||
* **Cutscene Control**: Farore's logic is heavily integrated with cutscenes, controlling Link's movement, displaying messages, and managing game state transitions to create cinematic sequences.
|
||||
* **Quest Progression Integration**: The sprite's appearance and behavior are tied to `STORY_STATE` and other game flags, indicating its crucial role in advancing the narrative.
|
||||
* **Player Movement Manipulation**: During cutscenes, Farore's script directly controls Link's speed and auto-movement, ensuring precise choreography for story events.
|
||||
* **Global State Management**: Modifies `InCutScene`, `GAMESTATE`, `STORY_STATE`, and other global variables to reflect and control the current game context.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,94 +0,0 @@
|
||||
# Followers
|
||||
|
||||
## Overview
|
||||
The `followers.asm` file is a comprehensive collection of routines and data structures that implement a sophisticated follower system within Oracle of Secrets. It manages various NPC types, including the Zora Baby, Old Man, Kiki, and a Minecart, each with unique behaviors, interactions, and integration into the game world. This file heavily utilizes vanilla overrides to inject custom logic and expand upon existing game mechanics.
|
||||
|
||||
## Follower Data Memory Locations
|
||||
This section defines various WRAM addresses used to store and cache follower-related data, enabling complex interactions and animations:
|
||||
|
||||
* **`FollowerYL`, `FollowerYH`, `FollowerXL`, `FollowerXH`, `FollowerZ`, `FollowerLayer`**: Stores position (Y, X, Z coordinates) and layer information for followers, with a cache for 20 steps of animation and movement.
|
||||
* **`FollowerHeadOffset`, `FollowerHeadOffsetH`, `FollowerBodyOffset`, `FollowerBodyOffsetH`**: Stores offsets for follower head and body graphics, used to adjust their appearance based on direction (e.g., facing Link).
|
||||
* **`Flwhgfxt`, `Flwhgfxth`, `Flwhgfxb`, `Flwhgfxbh`, `Flwbgfxt`, `Flwbgfxth`, `Flwbgfxb`, `Flwbgfxbh`**: Graphics data for follower head and body.
|
||||
* **`Flwanimir`**: Index for reading follower animation steps.
|
||||
* **`FollowerHook`**: Flag indicating when a follower is being used with the Hookshot.
|
||||
* **`FollowerHookI`**: Caches `FLWANIMIW` when Hookshotting is finished.
|
||||
* **`FLWGRABTIME`**: Countdown timer preventing followers from being immediately regrabbed after being dropped.
|
||||
* **`FLWANIMIW`**: Index for writing follower animation steps.
|
||||
* **`FollowCacheYL`, `FollowCacheYH`, `FollowCacheXL`, `FollowCacheXH`**: Cache of follower properties in SRAM.
|
||||
|
||||
## `Follower_WatchLink`
|
||||
This routine adjusts a follower's head and body graphics offsets to make them turn and face Link, providing a more interactive and responsive NPC presence.
|
||||
|
||||
## Zora Baby Follower
|
||||
The Zora Baby follower is a key NPC involved in specific puzzles and interactions, particularly with water switches.
|
||||
|
||||
* **`ZoraBaby_RevertToSprite`**: This routine spawns a `Sprite 0x39 Locksmith` (which is the Zora Baby sprite) and initializes its properties based on the follower's cached data. It sets `SprBulletproof`, `SprAction`, and `SprTimerB`, and clears relevant follower flags.
|
||||
* **`CheckForZoraBabyTransitionToSprite`**: Checks if the Zora Baby is currently a follower (`$7EF3CC = $09`). If Link is standing on a star tile (`$0114 = $3B`), it calls `ZoraBaby_RevertToSprite` to transition the follower back into a regular sprite. If Link is outdoors, it clears the follower flag.
|
||||
* **`CheckForZoraBabyFollower`**: A utility routine to check if the Zora Baby is currently a follower.
|
||||
* **`UploadZoraBabyGraphicsPrep`**: Prepares the graphics for the Zora Baby, setting `$7EF3CC` to `$09` and calling `LoadFollowerGraphics`.
|
||||
* **`ZoraBaby_CheckForWaterSwitchSprite`**: Checks for the presence of a `Sprite 0x21` (Water Gate Switch) and determines if the Zora Baby is positioned on top of it.
|
||||
* **`ZoraBaby_CheckForWaterGateSwitch`**: Checks for a `Sprite 0x04` (Water Gate Switch) and performs a precise coordinate check to see if the Zora Baby is on top of it.
|
||||
* **`ZoraBaby_GlobalBehavior`**: This is the main behavior routine for the Zora Baby. It makes the Zora Baby act as a barrier (`Sprite_BehaveAsBarrier`), makes it watch Link (`Follower_WatchLink`), and handles interactions like being lifted (`Sprite_CheckIfLifted`) and thrown (`ThrownSprite_TileAndSpriteInteraction_long`). Crucially, it detects if the Zora Baby is on a water switch and triggers the `ZoraBaby_PullSwitch` state.
|
||||
|
||||
### Zora Baby Vanilla Overrides
|
||||
* **`org $09AA5E`**: Injects `JSL CheckForZoraBabyFollower` to enable the Zora Baby's swaying animation.
|
||||
* **`org $09A19C`**: Injects `JSL CheckForZoraBabyTransitionToSprite` for follower basic movement.
|
||||
* **`org $09A902`**: Sets the Zora Baby follower's palette to blue.
|
||||
* **`org $09A8CF`**: Sets the Zora Baby character data offset.
|
||||
* **`org $06BD9C`**: Defines the Zora Baby Sprite Idle OAM data.
|
||||
* **`org $068D59` (`SpritePrep_Locksmith`)**: Overrides the `SpritePrep_Locksmith` routine. It makes the Zora Baby bulletproof, prevents spawning if already following, and calls `UploadZoraBabyGraphicsPrep`.
|
||||
* **`org $06BCAC` (`Sprite_39_ZoraBaby`)**: Overrides `Sprite_39_Locksmith`. This is the main state machine for the Zora Baby, including states like `LockSmith_Chillin` (idle), `ZoraBaby_FollowLink`, `ZoraBaby_OfferService`, `ZoraBaby_RespondToAnswer`, `ZoraBaby_AgreeToWait`, `ZoraBaby_PullSwitch`, and `ZoraBaby_PostSwitch`. These states manage dialogue, following behavior, and interaction with switches.
|
||||
|
||||
## Old Man Follower
|
||||
This section includes logic for the Old Man follower, particularly concerning his spawning conditions and item interactions.
|
||||
|
||||
* **`OldMan_ExpandedPrep`**: Prevents the Old Man sprite from spawning in his home room if Link already has him as a follower.
|
||||
|
||||
### Old Man Vanilla Overrides
|
||||
* **`org $1EE9FF`**: Modifies the item given by the Old Man to be the Goldstar Hookshot upgrade.
|
||||
* **`org $1BBD3C`**: Modifies `FindEntrance` for the Old Man.
|
||||
* **`org $02D98B`**: Modifies `Underworld_LoadEntrance` for the Old Man.
|
||||
* **`org $1EE8F1` (`SpritePrep_OldMan`)**: Overrides `SpritePrep_OldMan`. It makes the Old Man bulletproof, uses `OldMan_ExpandedPrep`, checks for the Lv2 Hookshot, and sets `$7EF3CC` to `$04` (Old Man follower) before calling `LoadFollowerGraphics`.
|
||||
* **`org $09A4C8` (`Follower_HandleTriggerData`)**: This is a large data block defining trigger coordinates and messages for various followers, including the Old Man, Zelda, and Blind Maiden.
|
||||
|
||||
## Kiki Follower
|
||||
This section contains logic for the Kiki follower, focusing on her reaction to Link's health.
|
||||
|
||||
* **`Kiki_CheckIfScared`**: If Link's health is low and Kiki is flashing, she will run away from him.
|
||||
|
||||
### Kiki Vanilla Overrides
|
||||
* **`org $09A1C6`**: Injects `JSL Kiki_CheckIfScared` to implement Kiki's fear behavior.
|
||||
* **`org $1EE2E9` (`Kiki_WalkOnRoof`)**: Defines speed data for Kiki walking on a roof.
|
||||
* **`org $1EE576` (`Kiki_HopToSpot`)**: Defines target coordinates for Kiki to hop to a spot.
|
||||
* **`org $1EE5E9` (`Kiki_WalkOnRoof_Ext`)**: Defines step and timer data for Kiki's extended roof walk.
|
||||
|
||||
## Minecart Follower
|
||||
This section details the implementation of the Minecart follower, including its drawing, transition, and Link's interaction with it.
|
||||
|
||||
* **`FollowerDraw_CalculateOAMCoords`**: A helper routine to calculate OAM coordinates for followers.
|
||||
* **`MinecartFollower_Top` / `MinecartFollower_Bottom`**: Drawing routines for the top and bottom halves of the Minecart follower.
|
||||
* **`Minecart_AnimDirection`**: Data for Minecart animation direction.
|
||||
* **`MinecartFollower_TransitionToSprite`**: Transitions the Minecart follower back into a regular sprite.
|
||||
* **`DrawMinecartFollower`**: The main drawing routine for the Minecart follower, which also handles its transition to a sprite if Link is in the cart and not in a submodule.
|
||||
* **`FollowerDraw_CachePosition`**: Caches the follower's position for drawing, adjusting coordinates relative to Link.
|
||||
* **`CheckForMinecartFollowerDraw`**: Checks if the Minecart follower should be drawn.
|
||||
* **`CheckForFollowerInterroomTransition` / `CheckForFollowerIntraroomTransition`**: Handles transitions for followers between rooms and within rooms.
|
||||
* **`LinkState_Minecart`**: Defines Link's behavior when he is in a Minecart, including movement, collision, and animation.
|
||||
* **`TileBehavior_TL_Long` / `TileBehavior_StopLeft_Long`**: Tile behaviors for Minecart tracks.
|
||||
|
||||
### Minecart Vanilla Overrides
|
||||
* **`org $07A5F7`**: Injects `JSL LinkState_Minecart` to control Link's state when in a Minecart.
|
||||
* **`org $07D938`**: Defines Minecart Track tile types.
|
||||
* **`org $09A41F`**: Injects `JSL CheckForMinecartFollowerDraw`.
|
||||
* **`org $028A5B`**: Injects `JSL CheckForFollowerInterroomTransition`.
|
||||
* **`org $0289BF`**: Injects `JSL CheckForFollowerIntraroomTransition`.
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Purpose File**: This file serves as a central repository for various follower-related logic, demonstrating how to manage diverse NPC behaviors within a single module.
|
||||
* **Follower System**: Implements a robust and flexible follower system with features like position caching, animation, and complex interaction logic.
|
||||
* **Vanilla Overrides**: Extensive use of `org` directives to modify vanilla sprite behaviors and integrate custom follower logic, showcasing advanced ROM hacking techniques.
|
||||
* **Context-Sensitive Behavior**: Follower behavior dynamically changes based on game state, player actions, and environmental factors (e.g., Zora Baby on water switch, Old Man's spawning conditions, Kiki's fear of low-health Link).
|
||||
* **Cutscene Integration**: Some followers (like the Zora Baby) are involved in cutscene-like sequences, demonstrating how to choreograph NPC actions within narrative events.
|
||||
* **Item Gating/Progression**: The Old Man's appearance and item offerings are tied to the player's possession of specific items (e.g., Lv2 Hookshot), integrating followers into the game's progression system.
|
||||
* **Player State Manipulation**: Routines like `LinkState_Minecart` directly control Link's movement and animation when interacting with followers, providing a seamless player experience.
|
||||
* **16-bit OAM Calculations**: Explicitly uses `REP #$20` and `SEP #$20` for precise 16-bit OAM calculations in drawing routines, ensuring accurate sprite rendering.
|
||||
@@ -1,88 +0,0 @@
|
||||
# Fortune Teller
|
||||
|
||||
## Overview
|
||||
The `fortune_teller.asm` file is not a complete sprite definition but rather a set of routines and data that override and extend the behavior of the vanilla Fortune Teller NPC. Its primary function is to provide highly context-sensitive messages to Link, offering guidance or commentary based on his current inventory, collected items, and overall game progression.
|
||||
|
||||
## Vanilla Overrides
|
||||
This file directly modifies existing vanilla code related to the Fortune Teller:
|
||||
|
||||
* **`org $0DC829`**: Overrides the `FortuneTellerMessage` data table, which contains the message IDs that the Fortune Teller can display.
|
||||
* **`org $0DC849`**: Overrides the `FortuneTeller_PerformPseudoScience` routine, which is the core logic for determining and displaying messages.
|
||||
|
||||
## `FortuneTellerMessage` Data Table
|
||||
This table stores a sequence of byte values, each representing a message ID. These IDs correspond to specific dialogue options that the Fortune Teller can present to Link.
|
||||
|
||||
```asm
|
||||
org $0DC829
|
||||
FortuneTellerMessage:
|
||||
.low
|
||||
#_0DC829: db $EA ; MESSAGE 00EA
|
||||
#_0DC82A: db $EB ; MESSAGE 00EB
|
||||
#_0DC82B: db $EC ; MESSAGE 00EC
|
||||
#_0DC82C: db $ED ; MESSAGE 00ED
|
||||
#_0DC82D: db $EE ; MESSAGE 00EE
|
||||
#_0DC82E: db $EF ; MESSAGE 00EF
|
||||
#_0DC82F: db $F0 ; MESSAGE 00F0
|
||||
#_0DC830: db $F1 ; MESSAGE 00F1
|
||||
#_0DC831: db $F6 ; MESSAGE 00F6
|
||||
#_0DC832: db $F7 ; MESSAGE 00F7
|
||||
#_0DC833: db $F8 ; MESSAGE 00F8
|
||||
#_0DC834: db $F9 ; MESSAGE 00F9
|
||||
#_0DC835: db $FA ; MESSAGE 00FA
|
||||
#_0DC836: db $FB ; MESSAGE 00FB
|
||||
#_0DC837: db $FC ; MESSAGE 00FC
|
||||
#_0DC838: db $FD ; MESSAGE 00FD
|
||||
|
||||
.high
|
||||
#_0DC839: db $00
|
||||
; ... (rest of the table)
|
||||
```
|
||||
|
||||
## `FortuneTeller_PerformPseudoScience` Routine
|
||||
This routine is the central logic for the custom Fortune Teller. It dynamically selects which message to display to Link based on a series of checks against his inventory and various game progression flags stored in SRAM.
|
||||
|
||||
* **Initializations**: Performs some initial state manipulations (`STZ.w $0DC0,X`, `INC.w $0D80,X`, `STZ.b $03`).
|
||||
* **Progression Check (`$7EF3D6`)**: Determines a base message category based on a custom progression flag.
|
||||
* **Extensive Item and Game State Checks**: The routine then proceeds through a series of conditional checks, each corresponding to a specific item or game event. For example, it checks for:
|
||||
* `$7EF344` (Mushroom/Powder)
|
||||
* `$7EF37A` (Crystals, specifically if Tail Palace is beaten)
|
||||
* `$7EF355` (Boots)
|
||||
* `$7EF356` (Flippers)
|
||||
* `$7EF345` (Fire Rod)
|
||||
* `$7EF37B` (Magic Upgrade)
|
||||
* `$7EF354` (Glove)
|
||||
* `$7EF358` (Wolf Mask)
|
||||
* `$7EF3C9` (Smithy Rescued flag)
|
||||
* `$7EF352` (Cape)
|
||||
* `$7EF354` (Titans Mitt)
|
||||
* `$7EF359` (Sword level)
|
||||
* **Message Display**: Based on these checks, it calls `FortuneTeller_PrepareNextMessage` with the appropriate message index and then `FortuneTeller_DisplayMessage` to show the message to the player.
|
||||
|
||||
```asm
|
||||
FortuneTeller_PerformPseudoScience:
|
||||
#_0DC849: STZ.w $0DC0,X
|
||||
#_0DC84C: INC.w $0D80,X
|
||||
#_0DC84F: STZ.b $03
|
||||
#_0DC851: LDA.l $7EF3D6
|
||||
#_0DC855: CMP.b #$02
|
||||
#_0DC857: BCS .map_icon_past_pendants
|
||||
#_0DC859: STZ.b $00
|
||||
#_0DC85B: STZ.b $01
|
||||
#_0DC85D: JMP.w FortuneTeller_DisplayMessage
|
||||
|
||||
.map_icon_past_pendants
|
||||
#_0DC860: LDA.l $7EF344
|
||||
#_0DC864: BNE .have_shroom_or_powder
|
||||
#_0DC866: LDA.b #$02
|
||||
#_0DC868: JSR FortuneTeller_PrepareNextMessage
|
||||
#_0DC86B: BCC .have_shroom_or_powder
|
||||
#_0DC86D: JMP.w FortuneTeller_DisplayMessage
|
||||
|
||||
; ... (rest of the conditional item checks)
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Vanilla Override**: This file exemplifies how to directly modify and extend the behavior of existing vanilla NPCs through targeted code injection.
|
||||
* **Context-Sensitive Dialogue**: The Fortune Teller's messages are highly dynamic and personalized, adapting to Link's current inventory and game progression. This creates a more engaging and responsive NPC interaction.
|
||||
* **Quest Progression Tracking**: The routine extensively utilizes SRAM flags and item possession checks to track Link's progress through various quests and milestones, influencing the dialogue provided.
|
||||
* **Modular Message System**: The use of `FortuneTeller_PrepareNextMessage` and `FortuneTeller_DisplayMessage` allows for a structured and modular approach to managing and displaying NPC dialogue.
|
||||
@@ -1,179 +0,0 @@
|
||||
# Goron
|
||||
|
||||
## Overview
|
||||
The Goron sprite (`!SPRID = $F2`) is a versatile NPC implementation that can represent two distinct Goron characters: the "Kalyxo Goron" and the "Eon Goron." Their specific behaviors and appearances are determined by the global `WORLDFLAG` and the current `AreaIndex`. These Gorons primarily serve as interactive NPCs, engaging Link through dialogue and potentially triggering game events.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `$F2` (Vanilla sprite ID, likely for a generic NPC)
|
||||
* **`!NbrTiles`**: `04`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `02`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Goron_Long`)
|
||||
This routine acts as a dispatcher, selecting the appropriate drawing routine based on the `WORLDFLAG` (Kalyxo Goron if `0`, Eon Goron otherwise). It also handles shadow drawing and dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Goron_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.w WORLDFLAG : BEQ .kalyxo
|
||||
JSR Sprite_EonGoron_Draw
|
||||
JMP +
|
||||
.kalyxo
|
||||
JSR Sprite_KalyxoGoron_Draw
|
||||
+
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Goron_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Goron_Prep`)
|
||||
This routine initializes the Goron upon spawning. It sets `SprDefl, X` to `$80`. The initial `SprAction, X` is determined by `WORLDFLAG` and `AreaIndex`. For Eon Gorons, it can randomly set their initial action to `EonGoron_Main`, `EonGoron_Sing`, or `EonGoron_Punch`. For Kalyxo Gorons, it checks a specific flag (`$7EF280, X`) to set their initial action to `KalyxoGoron_Main` or `KalyxoGoron_MinesOpened`.
|
||||
|
||||
```asm
|
||||
Sprite_Goron_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.w WORLDFLAG : BEQ +
|
||||
LDA.w AreaIndex : CMP.b #$55 : BNE .not_sing
|
||||
LDA.b #$04 : STA.w SprAction, X
|
||||
.not_sing
|
||||
JSL GetRandomInt : AND.b #$01 : BEQ .rand
|
||||
LDA.b #$05 : STA.w SprAction, X
|
||||
JMP ++
|
||||
.rand
|
||||
LDA.b #$03 : STA.w SprAction, X
|
||||
JMP ++
|
||||
+
|
||||
PHX
|
||||
LDX $8A
|
||||
LDA.l $7EF280, X : CMP.b #$20 : BEQ +++
|
||||
PLX
|
||||
STZ.w SprAction, X
|
||||
++
|
||||
PLB
|
||||
RTL
|
||||
+++
|
||||
PLX
|
||||
LDA.b #$02 : STA.w SprAction, X
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Goron_Main`)
|
||||
This routine manages the various states and behaviors of both Kalyxo and Eon Gorons.
|
||||
|
||||
* **Player Collision**: Prevents Link from passing through the Goron (`Sprite_PlayerCantPassThrough`).
|
||||
* **`KalyxoGoron_Main`**: Displays messages (`%ShowSolicitedMessage`) based on the `RockMeat` item count. Can transition to `KalyxoGoron_OpenMines` under certain conditions.
|
||||
* **`KalyxoGoron_OpenMines`**: Plays an animation, sets a flag (`$04C6`) to open mines, and transitions to `KalyxoGoron_MinesOpened`.
|
||||
* **`KalyxoGoron_MinesOpened`**: Plays an animation.
|
||||
* **`EonGoron_Main`**: Plays an animation and displays a message.
|
||||
* **`EonGoron_Sing`**: Plays a singing animation and displays a message.
|
||||
* **`EonGoron_Punch`**: Plays a punching animation and displays a message.
|
||||
|
||||
```asm
|
||||
Sprite_Goron_Main:
|
||||
{
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw KalyxoGoron_Main
|
||||
dw KalyxoGoron_OpenMines
|
||||
dw KalyxoGoron_MinesOpened
|
||||
dw EonGoron_Main
|
||||
dw EonGoron_Sing
|
||||
dw EonGoron_Punch
|
||||
|
||||
KalyxoGoron_Main:
|
||||
{
|
||||
LDA.l RockMeat : BEQ +
|
||||
CMP.b #$05 : BCC ++
|
||||
%ShowSolicitedMessage($01A9) : BCC +++
|
||||
INC.w SprAction, X
|
||||
+++
|
||||
RTS
|
||||
+
|
||||
%ShowSolicitedMessage($01A7)
|
||||
RTS
|
||||
++
|
||||
%ShowSolicitedMessage($01A8)
|
||||
RTS
|
||||
}
|
||||
|
||||
KalyxoGoron_OpenMines:
|
||||
{
|
||||
%PlayAnimation(1,1,10)
|
||||
LDA.b #$04 : STA $04C6
|
||||
INC.w SprAction, X
|
||||
RTS
|
||||
}
|
||||
|
||||
KalyxoGoron_MinesOpened:
|
||||
{
|
||||
%PlayAnimation(1,1,10)
|
||||
RTS
|
||||
}
|
||||
|
||||
EonGoron_Main:
|
||||
{
|
||||
%PlayAnimation(0, 1, 10)
|
||||
%ShowSolicitedMessage($01B0)
|
||||
RTS
|
||||
}
|
||||
|
||||
EonGoron_Sing:
|
||||
{
|
||||
%PlayAnimation(2, 3, 10)
|
||||
%ShowSolicitedMessage($01B2)
|
||||
RTS
|
||||
}
|
||||
|
||||
EonGoron_Punch:
|
||||
{
|
||||
%PlayAnimation(4, 5, 10)
|
||||
%ShowSolicitedMessage($01B1)
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_KalyxoGoron_Draw` and `Sprite_EonGoron_Draw`)
|
||||
Both drawing routines handle OAM allocation and animation for their respective Goron types. They explicitly use `REP #$20` and `SEP #$20` for 16-bit coordinate calculations.
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Character NPC (Conditional Drawing/Logic)**: A single sprite definition is used to represent two distinct Goron characters (Kalyxo and Eon) based on `WORLDFLAG`, demonstrating efficient resource utilization and context-sensitive character representation.
|
||||
* **Quest Progression Integration**: The Gorons' dialogue and actions are tied to game state (e.g., `RockMeat` item count, `AreaIndex`), indicating their role in advancing the narrative and triggering specific events like opening mines.
|
||||
* **Conditional Behavior**: Extensive use of conditional logic based on `WORLDFLAG` and `AreaIndex` allows for dynamic changes in the Goron's role, dialogue, and actions.
|
||||
* **NPC Interaction**: Provides rich interaction with the player through dialogue (`%ShowSolicitedMessage`) and can trigger game events.
|
||||
* **Player Collision**: Implements `Sprite_PlayerCantPassThrough` to make the NPC a solid object that Link cannot walk through.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,152 +0,0 @@
|
||||
# Hyrule Dream
|
||||
|
||||
## Overview
|
||||
The Hyrule Dream sprite represents a special NPC that appears indoors and plays a role in a specific questline. It interacts with Link through dialogue and grants a unique "Dream" item, with its presence and actions tied to game progression.
|
||||
|
||||
## Sprite Properties
|
||||
Explicit sprite properties (`!SPRID`, `!NbrTiles`, etc.) are not defined within this file. It is assumed that these properties are either inherited from a vanilla sprite ID or defined in a separate configuration file, as this file focuses on the sprite's behavior and drawing.
|
||||
|
||||
## Main Logic (`Sprite_HyruleDream_Main`)
|
||||
This routine manages the Hyrule Dream's behavior through a state machine:
|
||||
|
||||
* **`HyruleDream_Idle`**: The sprite plays an idle animation. When Link is within a certain proximity (`GetDistance8bit_Long`), it transitions to the `HyruleDream_Talk` state.
|
||||
* **`HyruleDream_Talk`**: The sprite continues its idle animation and displays a solicited message (`%ShowSolicitedMessage($01B3)`). Upon completion of the message, it transitions to `HyruleDream_GiveItem`.
|
||||
* **`HyruleDream_GiveItem`**: The sprite maintains its idle animation. After a timer (`SprTimerA, X`) expires, it grants Link the "Dream" item (`LDY #$12`, `JSL Link_ReceiveItem`), sets a flag (`$7EF410`) indicating the Dream has been obtained, and then despawns itself (`STZ.w SprState, X`).
|
||||
|
||||
```asm
|
||||
Sprite_HyruleDream_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw HyruleDream_Idle
|
||||
dw HyruleDream_Talk
|
||||
dw HyruleDream_GiveItem
|
||||
|
||||
HyruleDream_Idle:
|
||||
{
|
||||
%PlayAnimation(0,0,1)
|
||||
JSL GetDistance8bit_Long : CMP.b #$20 : BCS +
|
||||
INC.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
HyruleDream_Talk:
|
||||
{
|
||||
%PlayAnimation(0,0,1)
|
||||
%ShowSolicitedMessage($01B3) : BCC +
|
||||
INC.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
HyruleDream_GiveItem:
|
||||
{
|
||||
%PlayAnimation(0,0,1)
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
LDY #$12 : JSL Link_ReceiveItem
|
||||
LDA.b #$01 : STA.l $7EF410
|
||||
STZ.w SprState, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_HyruleDream_Prep`)
|
||||
This routine initializes the Hyrule Dream sprite upon spawning. It sets `SprDefl, X` to `$80` (preventing despawning off-screen). Crucially, it checks the "Dream" flag (`$7EF410`). If Link has already obtained the Dream, the sprite immediately despawns (`STZ.w SprState, X`), ensuring it only appears once.
|
||||
|
||||
```asm
|
||||
Sprite_HyruleDream_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
LDA.l $7EF410 : BNE +
|
||||
STZ.w SprState, X
|
||||
+
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_HyruleDream_Draw`)
|
||||
The drawing routine handles OAM allocation and animation. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
```asm
|
||||
Sprite_HyruleDream_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprFrame, X : TAY ;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY
|
||||
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
.start_index
|
||||
db $00
|
||||
.nbr_of_tiles
|
||||
db 3
|
||||
.x_offsets
|
||||
dw -8, 8, -8, 8
|
||||
.y_offsets
|
||||
dw -8, -8, 8, 8
|
||||
.chr
|
||||
db $C0, $C2, $E0, $E2
|
||||
.properties
|
||||
db $3B, $7B, $3B, $7B
|
||||
.sizes
|
||||
db $02, $02, $02, $02
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **NPC Interaction**: The sprite is designed to engage with the player through dialogue and the granting of a unique item, driving a specific questline.
|
||||
* **Quest Progression Integration**: The sprite's appearance and item-granting are directly tied to a flag (`$7EF410`) for the "Dream" item, ensuring it appears only once per playthrough.
|
||||
* **Conditional Spawning/Despawning**: The sprite dynamically despawns if Link has already obtained the "Dream" item, preventing redundant interactions.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,43 +0,0 @@
|
||||
# Impa (Zelda)
|
||||
|
||||
## Overview
|
||||
The `impa.asm` file is not a complete sprite definition but rather a collection of routines and vanilla overrides that modify the behavior of the character Impa (or Zelda, as indicated by some labels). Its primary purpose is to manage spawn points and manipulate game state during critical events, likely cutscenes or key progression points within the game.
|
||||
|
||||
## Spawn Point Definitions
|
||||
The file defines a WRAM address `SPAWNPT = $7EF3C8` for a spawn point flag, along with comments detailing various spawn point IDs:
|
||||
|
||||
* `0x00` - Link's house
|
||||
* `0x01` - Sanctuary (Hall of Secrets)
|
||||
* `0x02` - Castle Prison
|
||||
* `0x03` - Castle Basement
|
||||
* `0x04` - Throne
|
||||
* `0x05` - Old man cave
|
||||
* `0x06` - Old man home
|
||||
|
||||
## `Impa_SetSpawnPointFlag`
|
||||
This routine is responsible for setting a specific spawn point flag. It stores a value into `$7EF372` (likely the actual spawn point ID) and then sets bit `$04` in `$7EF3D6` (a custom progression flag), indicating that a particular event has occurred.
|
||||
|
||||
```asm
|
||||
Impa_SetSpawnPointFlag:
|
||||
{
|
||||
STA.l $7EF372
|
||||
LDA.l $7EF3D6 : ORA.b #$04 : STA.l $7EF3D6
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Vanilla Overrides
|
||||
This file extensively uses `pushpc`/`pullpc` blocks and `org` directives to inject custom code into specific vanilla routines, thereby altering the game's default behavior:
|
||||
|
||||
* **`org $05EE46` (`Zelda_AtSanctuary`)**: Injects `JSL Impa_SetSpawnPointFlag`. This modification ensures that when Zelda (or Impa) is at the Sanctuary, a specific spawn point flag is set, influencing where Link might respawn or transition.
|
||||
* **`org $05EBCF`**: Modifies a comparison involving `$7EF359` (likely Link's Sword level) with `$05`. This is noted as a `TODO`, suggesting it's an incomplete or placeholder modification.
|
||||
* **`org $029E2E` (`Module15_0C`)**: Modifies an overlay that Impa activates after the intro sequence. It sets bit `$20` in `$7EF2A3` (likely an overlay control flag), potentially changing the visual environment.
|
||||
* **`org $05ED43` (`Zelda_BecomeFollower`)**: Prevents Impa from setting a spawn point by clearing `$02E4` and `$7EF3C8`. This suggests a custom handling of Impa's follower state.
|
||||
* **`org $05ED63`**: NOPs out 5 bytes, effectively disabling some vanilla code at this address.
|
||||
* **`org $05ED10` (`Zelda_ApproachHero`)**: NOPs out 5 bytes, preventing Impa from changing the game's background music or sound, indicating custom control over audio during this event.
|
||||
|
||||
## Design Patterns
|
||||
* **Vanilla Override**: This file is a prime example of how to extensively override vanilla code to integrate custom NPC behaviors and progression systems into an existing game.
|
||||
* **Quest Progression Tracking**: Utilizes custom progression flags (`$7EF3D6`) and spawn point flags (`$7EF372`, `$7EF3C8`) to meticulously track key events and the player's progress through the game's narrative.
|
||||
* **Game State Manipulation**: Directly modifies WRAM addresses to control various aspects of the game, such as visual overlays and audio, providing fine-grained control over the player's experience.
|
||||
* **Modular Code Injection**: The use of `org` directives allows for precise injection of custom routines into specific points of the vanilla codebase, enabling targeted modifications without disrupting unrelated functionality.
|
||||
@@ -1,184 +0,0 @@
|
||||
# Korok
|
||||
|
||||
## Overview
|
||||
The Korok sprite (`!SPRID = Sprite_Korok`) implements a multi-variant NPC system, allowing for different Korok characters (Makar, Hollo, Rown) to appear from a single sprite definition. These Koroks exhibit random walking behavior, engage in dialogue, and are liftable, contributing to environmental interactions and minor puzzles.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_Korok` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `08`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `01` (Impervious to all attacks)
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `01`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `03`
|
||||
* **`!Persist`**: `01` (Continues to live off-screen)
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Korok_Long`)
|
||||
This routine acts as a dispatcher for drawing the correct Korok variant based on its `SprSubtype, X`. It also handles shadow drawing and dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Korok_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA $0AA5 : BEQ .done
|
||||
LDA.w SprSubtype, X : BEQ .draw_makar
|
||||
CMP.b #$01 : BEQ .draw_hollo
|
||||
CMP.b #$02 : BEQ .draw_rown
|
||||
.draw_makar
|
||||
JSR Sprite_Korok_DrawMakar
|
||||
BRA .done
|
||||
.draw_hollo
|
||||
JSR Sprite_Korok_DrawHollo
|
||||
BRA .done
|
||||
.draw_rown
|
||||
JSR Sprite_Korok_DrawRown
|
||||
BRA .done
|
||||
.done
|
||||
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Korok_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Korok_Prep`)
|
||||
This routine initializes the Korok upon spawning by randomly assigning a `SprSubtype, X` (0-3). This subtype determines which Korok variant (Makar, Hollo, or Rown) the sprite will represent.
|
||||
|
||||
```asm
|
||||
Sprite_Korok_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSL GetRandomInt : AND.b #$03 : STA.w SprSubtype, X
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Korok_Main`)
|
||||
The Korok's core behavior is managed by a state machine that includes idle, walking, and liftable states.
|
||||
|
||||
* **`Sprite_Korok_Idle`**: The Korok plays an idle animation. Upon player interaction (`%ShowSolicitedMessage($001D)`), it randomly transitions to a walking state. It also prevents player passage (`Sprite_PlayerCantPassThrough`).
|
||||
* **`Sprite_Korok_WalkingDown` / `Up` / `Left` / `Right`**: These states control the Korok's movement in different directions. Each state plays a specific walking animation, sets the appropriate speed (`KorokWalkSpeed`), moves the sprite (`Sprite_Move`), and after a timer (`SprTimerB, X`), randomly transitions to another walking state.
|
||||
* **`Sprite_Korok_Liftable`**: This state handles the Korok's interaction when lifted (`Sprite_CheckIfLifted`) and thrown (`ThrownSprite_TileAndSpriteInteraction_long`).
|
||||
|
||||
```asm
|
||||
Sprite_Korok_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Sprite_Korok_Idle
|
||||
dw Sprite_Korok_WalkingDown
|
||||
dw Sprite_Korok_WalkingUp
|
||||
dw Sprite_Korok_WalkingLeft
|
||||
dw Sprite_Korok_WalkingRight
|
||||
dw Sprite_Korok_Liftable
|
||||
|
||||
Sprite_Korok_Idle:
|
||||
{
|
||||
%PlayAnimation(0, 0, 10)
|
||||
|
||||
LDA $0AA5 : BNE +
|
||||
PHX
|
||||
JSL ApplyKorokSpriteSheets
|
||||
PLX
|
||||
LDA.b #$01 : STA.w $0AA5
|
||||
+
|
||||
|
||||
%ShowSolicitedMessage($001D) : BCC .no_talk
|
||||
JSL GetRandomInt : AND.b #$03
|
||||
STA.w SprAction, X
|
||||
RTS
|
||||
.no_talk
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
RTS
|
||||
}
|
||||
|
||||
Sprite_Korok_WalkingDown:
|
||||
{
|
||||
%PlayAnimation(0, 2, 10)
|
||||
LDA.b #KorokWalkSpeed : STA.w SprYSpeed, X
|
||||
JSL Sprite_Move
|
||||
LDA.w SprTimerB, X : BNE +
|
||||
JSL GetRandomInt : AND.b #$03 : STA.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Sprite_Korok_WalkingUp:
|
||||
{
|
||||
%PlayAnimation(3, 5, 10)
|
||||
LDA.b #-KorokWalkSpeed : STA.w SprYSpeed, X
|
||||
JSL Sprite_Move
|
||||
LDA.w SprTimerB, X : BNE +
|
||||
JSL GetRandomInt : AND.b #$03 : STA.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Sprite_Korok_WalkingLeft:
|
||||
{
|
||||
%PlayAnimation(6, 8, 10)
|
||||
LDA.b #KorokWalkSpeed : STA.w SprXSpeed, X
|
||||
JSL Sprite_Move
|
||||
LDA.w SprTimerB, X : BNE +
|
||||
JSL GetRandomInt : AND.b #$03 : STA.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Sprite_Korok_WalkingRight:
|
||||
{
|
||||
%PlayAnimation(9, 11, 10)
|
||||
LDA.b #-KorokWalkSpeed : STA.w SprXSpeed, X
|
||||
JSL Sprite_Move
|
||||
|
||||
LDA.w SprTimerB, X : BNE +
|
||||
JSL GetRandomInt : AND.b #$03 : STA.w SprAction, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Sprite_Korok_Liftable:
|
||||
{
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_CheckIfLifted
|
||||
JSL ThrownSprite_TileAndSpriteInteraction_long
|
||||
RTS
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_Korok_DrawMakar`, `Sprite_Korok_DrawHollo`, `Sprite_Korok_DrawRown`)
|
||||
Each Korok variant has its own dedicated drawing routine (`Sprite_Korok_DrawMakar`, `Sprite_Korok_DrawHollo`, `Sprite_Korok_DrawRown`). These routines handle OAM allocation and animation, and explicitly use `REP #$20` and `SEP #$20` for 16-bit coordinate calculations. Each routine contains its own specific OAM data for rendering the respective Korok character.
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Variant NPC**: A single sprite definition (`Sprite_Korok`) is used to represent multiple distinct Korok characters (Makar, Hollo, Rown) based on a randomly assigned `SprSubtype`, showcasing efficient resource utilization and varied visual appearances.
|
||||
* **Randomized Behavior**: The Korok's initial variant and its walking directions are randomized, adding an element of unpredictability and variety to encounters.
|
||||
* **NPC Interaction**: The Korok can be interacted with through dialogue (`%ShowSolicitedMessage`) and is liftable (`Sprite_CheckIfLifted`), allowing for environmental puzzles or simple interactions.
|
||||
* **Conditional Drawing**: The drawing routine dispatches to different sub-routines based on the Korok's subtype, allowing for distinct visual appearances for each variant.
|
||||
* **Player Collision**: Implements `Sprite_PlayerCantPassThrough` to make the NPC a solid object that Link cannot walk through.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,137 +0,0 @@
|
||||
# Maku Tree
|
||||
|
||||
## Overview
|
||||
The Maku Tree sprite (`!SPRID = Sprite_MakuTree`) represents a significant NPC in the game, likely serving as a quest giver or a key character in the narrative. Its interactions are primarily dialogue-driven and tied to game progression, culminating in the granting of a Heart Container. Notably, its graphics are not handled by a conventional sprite drawing routine within this file, suggesting it might be a background element or drawn by a separate system.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_MakuTree` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `00` (Indicates graphics are handled externally or as a background)
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `0`
|
||||
* **`!Damage`**: `0`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `0`
|
||||
* **`!Hitbox`**: `$0D`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `0`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_MakuTree_Long`)
|
||||
This routine primarily checks if the Maku Tree sprite is active and then dispatches to its main logic routine. The absence of a direct drawing call (`JSR Sprite_MakuTree_Draw`) here suggests its visual representation is managed outside of the standard sprite drawing pipeline.
|
||||
|
||||
```asm
|
||||
Sprite_MakuTree_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_MakuTree_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_MakuTree_Prep`)
|
||||
This routine initializes the Maku Tree upon spawning. It sets `SprDefl, X` and includes logic to potentially play the "Maku Song" by checking a custom progression flag (`OOSPROG2`).
|
||||
|
||||
```asm
|
||||
Sprite_MakuTree_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
; Play the Maku Song
|
||||
LDA.l OOSPROG2 : AND.b #$04 : BEQ +
|
||||
LDA.b #$03 : STA.w $012C
|
||||
+
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_MakuTree_Main`)
|
||||
The Maku Tree's core behavior is managed by a state machine that guides its interaction with Link and quest progression.
|
||||
|
||||
* **Player Collision**: Prevents Link from passing through the Maku Tree (`JSL Sprite_PlayerCantPassThrough`).
|
||||
* **`MakuTree_Handler`**: Checks the `MakuTreeQuest` flag to determine if Link has already met the Maku Tree, transitioning to `MakuTree_MeetLink` or `MakuTree_HasMetLink` accordingly.
|
||||
* **`MakuTree_MeetLink`**: When Link is close enough, the Maku Tree displays an unconditional message (`%ShowUnconditionalMessage($20)`), updates quest flags (`MakuTreeQuest`, `MapIcon`, `$7EF3D6`), and transitions to `MakuTree_SpawnHeartContainer`.
|
||||
* **`MakuTree_SpawnHeartContainer`**: Grants Link a Heart Container (`LDY #$3E : JSL Link_ReceiveItem`) and then transitions to `MakuTree_HasMetLink`.
|
||||
* **`MakuTree_HasMetLink`**: Displays a solicited message (`%ShowSolicitedMessage($22)`) and updates a progression flag (`$7EF3D6`) upon message dismissal.
|
||||
|
||||
```asm
|
||||
Sprite_MakuTree_Main:
|
||||
{
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw MakuTree_Handler
|
||||
dw MakuTree_MeetLink
|
||||
dw MakuTree_SpawnHeartContainer
|
||||
dw MakuTree_HasMetLink
|
||||
|
||||
MakuTree_Handler:
|
||||
{
|
||||
; Check the progress flags
|
||||
LDA.l MakuTreeQuest : AND.b #$01 : BNE .has_met_link
|
||||
%GotoAction(1)
|
||||
RTS
|
||||
.has_met_link
|
||||
%GotoAction(3)
|
||||
RTS
|
||||
}
|
||||
|
||||
MakuTree_MeetLink:
|
||||
{
|
||||
JSL GetDistance8bit_Long : CMP #$28 : BCS .not_too_close
|
||||
%ShowUnconditionalMessage($20)
|
||||
LDA.b #$01 : STA.l MakuTreeQuest
|
||||
LDA.b #$01 : STA.l MapIcon ; Mushroom Grotto
|
||||
LDA.l $7EF3D6 : ORA.b #$02 : STA.l $7EF3D6
|
||||
%GotoAction(2)
|
||||
.not_too_close
|
||||
RTS
|
||||
}
|
||||
|
||||
MakuTree_SpawnHeartContainer:
|
||||
{
|
||||
; Give Link a heart container
|
||||
LDY #$3E : JSL Link_ReceiveItem
|
||||
%GotoAction(3)
|
||||
RTS
|
||||
}
|
||||
|
||||
MakuTree_HasMetLink:
|
||||
{
|
||||
%ShowSolicitedMessage($22) : BCC .no_talk
|
||||
LDA.l $7EF3D6 : ORA.b #$02 : STA.l $7EF3D6
|
||||
.no_talk
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing
|
||||
There is no `Sprite_MakuTree_Draw` routine defined within this file. This strongly suggests that the Maku Tree's graphical representation is handled as a background element or by a separate drawing system, rather than as a conventional sprite with its own OAM data.
|
||||
|
||||
## Design Patterns
|
||||
* **Background/Static NPC**: The Maku Tree functions as a static NPC whose visual representation is likely integrated into the background, as evidenced by the absence of a dedicated sprite drawing routine.
|
||||
* **Quest Gating/Progression**: The Maku Tree's interactions are deeply tied to custom quest flags (`MakuTreeQuest`, `OOSPROG2`, `$7EF3D6`), controlling when Link can meet it, engage in dialogue, and receive rewards.
|
||||
* **Item Granting**: The Maku Tree serves as a source for a significant reward, granting Link a Heart Container upon meeting specific quest conditions.
|
||||
* **Player Collision**: Implements `Sprite_PlayerCantPassThrough` to make the Maku Tree a solid, impassable object in the game world.
|
||||
* **Dialogue-Driven Progression**: Dialogue (`%ShowUnconditionalMessage`, `%ShowSolicitedMessage`) is strategically used to advance the quest narrative and provide context to the player's journey.
|
||||
@@ -1,227 +0,0 @@
|
||||
# Maple
|
||||
|
||||
## Overview
|
||||
The `maple.asm` file defines the behavior for the NPC "Maple," a significant character involved in a branching "Dream" questline. Maple interacts with Link through extensive dialogue, offers explanations about game mechanics, and possesses the unique ability to put Link to sleep, triggering special dream sequences that advance the narrative and potentially grant rewards.
|
||||
|
||||
## Main Logic (`MapleHandler`)
|
||||
This routine orchestrates Maple's complex interactions with Link, managing dialogue, quest progression, and the initiation of dream sequences.
|
||||
|
||||
* **Player Collision**: Prevents Link from passing through Maple (`JSL Sprite_PlayerCantPassThrough`).
|
||||
* **`Maple_Idle`**: Displays a solicited message (`%ShowSolicitedMessage($01B3)`). Upon dismissal, it transitions to `Maple_HandleFirstResponse`. It also includes logic to set a flag (`$7EF351`) and a timer (`$012F`) for a specific event.
|
||||
* **`Maple_HandleFirstResponse`**: Processes Link's initial dialogue response (`$1CE8`), leading to different branches: `Maple_Idle`, `Maple_ExplainHut`, or `Maple_DreamOrExplain`.
|
||||
* **`Maple_DreamOrExplain`**: Displays an unconditional message (`%ShowUnconditionalMessage($01B4)`) and, based on Link's response, transitions to `Maple_ExplainPendants`, `Maple_CheckForPendant`, or back to `Maple_Idle`.
|
||||
* **`Maple_ExplainHut`**: Displays an unconditional message (`%ShowUnconditionalMessage($01B5)`) and returns to `Maple_Idle`.
|
||||
* **`Maple_ExplainPendants`**: Displays an unconditional message (`%ShowUnconditionalMessage($01B8)`) and returns to `Maple_Idle`.
|
||||
* **`Maple_CheckForPendant`**: Checks Link's collected Pendants (`Pendants` SRAM flag) and Dreams (`Dreams` SRAM flag) to determine if a new Dream is available. If so, it sets `CurrentDream`, displays a message (`%ShowUnconditionalMessage($01B6)`), and transitions to `Maple_PutLinkToSleep`. Otherwise, it transitions to `Maple_NoNewPendant`.
|
||||
* **`Maple_NoNewPendant`**: Displays an unconditional message (`%ShowUnconditionalMessage($01B7)`) and returns to `Maple_Idle`.
|
||||
* **`Maple_PutLinkToSleep`**: Calls `Sprite_PutLinkToSleep` to initiate the sleep sequence and then transitions to `Maple_HandleDreams`.
|
||||
* **`Maple_HandleDreams`**: After a timer (`SprTimerA, X`), calls `Link_HandleDreams` to process the dream sequence.
|
||||
|
||||
```asm
|
||||
MapleHandler:
|
||||
{
|
||||
%PlayAnimation(0,1,16)
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Maple_Idle
|
||||
dw Maple_HandleFirstResponse
|
||||
dw Maple_DreamOrExplain
|
||||
dw Maple_ExplainHut
|
||||
dw Maple_ExplainPendants
|
||||
dw Maple_CheckForPendant
|
||||
dw Maple_NoNewPendant
|
||||
dw Maple_PutLinkToSleep
|
||||
dw Maple_HandleDreams
|
||||
|
||||
|
||||
Maple_Idle:
|
||||
{
|
||||
%ShowSolicitedMessage($01B3) : BCC +
|
||||
INC.w SprAction, X
|
||||
+
|
||||
LDA.l $7EF351 : BEQ +
|
||||
LDA.b #$02 : STA.l $7EF351
|
||||
LDA.b #$1B : STA.w $012F
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Maple_HandleFirstResponse:
|
||||
{
|
||||
LDA.w $1CE8 : CMP.b #$02 : BNE +
|
||||
STZ.w SprAction, X
|
||||
RTS
|
||||
+
|
||||
CMP.b #$01 : BNE .next_response
|
||||
LDA.b #$03 : STA.w SprAction, X
|
||||
RTS
|
||||
.next_response
|
||||
INC.w SprAction, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Maple_DreamOrExplain:
|
||||
{
|
||||
%ShowUnconditionalMessage($01B4)
|
||||
LDA.w $1CE8 : BEQ .check_for_pendant
|
||||
CMP.b #$01 : BNE .another_time
|
||||
LDA.b #$04 : STA.w SprAction, X
|
||||
RTS
|
||||
.check_for_pendant
|
||||
LDA.b #$05 : STA.w SprAction, X
|
||||
RTS
|
||||
|
||||
.another_time
|
||||
STZ.w SprAction, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Maple_ExplainHut:
|
||||
{
|
||||
%ShowUnconditionalMessage($01B5)
|
||||
STZ.w SprAction, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Maple_ExplainPendants:
|
||||
{
|
||||
%ShowUnconditionalMessage($01B8)
|
||||
STZ.w SprAction, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Maple_CheckForPendant:
|
||||
{
|
||||
; Check for pendant
|
||||
LDA.l Pendants : AND.b #$04 : BNE .courage
|
||||
LDA.l Pendants : AND.b #$02 : BNE .power
|
||||
LDA.l Pendants : AND.b #$01 : BNE .wisdom
|
||||
JMP .none
|
||||
.courage
|
||||
LDA.l Dreams : AND.b #$04 : BNE .power
|
||||
LDA.b #$02 : STA.w CurrentDream : BRA +
|
||||
.power
|
||||
LDA.l Dreams : AND.b #$02 : BNE .wisdom
|
||||
LDA.b #$01 : STA.w CurrentDream : BRA +
|
||||
.wisdom
|
||||
LDA.l Dreams : AND.b #$01 : BNE .none
|
||||
STZ.w CurrentDream
|
||||
+
|
||||
%ShowUnconditionalMessage($01B6)
|
||||
LDA.b #$07 : STA.w SprAction, X
|
||||
LDA.b #$40 : STA.w SprTimerA, X
|
||||
RTS
|
||||
.none
|
||||
INC.w SprAction, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Maple_NoNewPendant:
|
||||
{
|
||||
%ShowUnconditionalMessage($01B7)
|
||||
STZ.w SprAction, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Maple_PutLinkToSleep:
|
||||
{
|
||||
JSR Sprite_PutLinkToSleep
|
||||
INC.w SprAction, X
|
||||
RTS
|
||||
}
|
||||
|
||||
Maple_HandleDreams:
|
||||
{
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
JSR Link_HandleDreams
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `Sprite_PutLinkToSleep`
|
||||
This routine initiates a cinematic sleep sequence for Link. It adjusts Link's coordinates, sets his state to sleeping (`$5D = $16`), spawns a blanket ancilla, adjusts Link's OAM coordinates, and applies a blinding white palette filter to transition into a dream sequence.
|
||||
|
||||
```asm
|
||||
Sprite_PutLinkToSleep:
|
||||
{
|
||||
PHX
|
||||
LDA.b $20 : SEC : SBC.b #$14 : STA.b $20
|
||||
LDA.b $22 : CLC : ADC.b #$18 : STA.b $22
|
||||
|
||||
LDA.b #$16 : STA.b $5D ; Set Link to sleeping
|
||||
LDA.b #$20 : JSL AncillaAdd_Blanket
|
||||
LDA.b $20 : CLC : ADC.b #$04 : STA.w $0BFA,X
|
||||
LDA.b $21 : STA.w $0C0E,X
|
||||
LDA.b $22 : SEC : SBC.b #$08 : STA.w $0C04,X
|
||||
LDA.b $23 : STA.w $0C18,X
|
||||
JSL PaletteFilter_StartBlindingWhite
|
||||
JSL ApplyPaletteFilter
|
||||
PLX
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## `Link_HandleDreams`
|
||||
This routine manages the different dream sequences based on the `CurrentDream` variable. It sets specific bits in the `Dreams` SRAM flag and warps Link to a designated room using `Link_WarpToRoom`.
|
||||
|
||||
* **`Dream_Wisdom`**: Sets bit `0` in `Dreams`, warps Link to a room, and sets `$EE` to `$01`.
|
||||
* **`Dream_Power`**: Sets bit `1` in `Dreams` and warps Link to a room.
|
||||
* **`Dream_Courage`**: Sets bit `2` in `Dreams` and warps Link to a room.
|
||||
|
||||
```asm
|
||||
Link_HandleDreams:
|
||||
{
|
||||
LDA.w CurrentDream
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Dream_Wisdom
|
||||
dw Dream_Power
|
||||
dw Dream_Courage
|
||||
|
||||
Dream_Wisdom:
|
||||
{
|
||||
LDA.l Dreams : ORA.b #%00000001 : STA.l Dreams
|
||||
LDX.b #$00
|
||||
JSR Link_WarpToRoom
|
||||
LDA.b #$01 : STA.b $EE
|
||||
RTS
|
||||
}
|
||||
|
||||
Dream_Power:
|
||||
{
|
||||
LDA.l Dreams : ORA.b #%00000010 : STA.l Dreams
|
||||
LDX.b #$01
|
||||
JSR Link_WarpToRoom
|
||||
RTS
|
||||
}
|
||||
|
||||
Dream_Courage:
|
||||
{
|
||||
LDA.l Dreams : ORA.b #%00000100 : STA.l Dreams
|
||||
LDX.b #$02
|
||||
JSR Link_WarpToRoom
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `Link_WarpToRoom`
|
||||
This routine sets Link's state for warping, including setting his `LinkState` and room coordinates, and uses a `.room` data table to determine the target room for the warp.
|
||||
|
||||
## `Link_FallIntoDungeon`
|
||||
This routine sets Link's state for falling into a dungeon, including setting the entrance ID and various state flags. It uses an `.entrance` data table to determine the target entrance.
|
||||
|
||||
## Vanilla Override
|
||||
* **`org $068C9C`**: Sets a byte to `$0F`, likely a minor adjustment to vanilla code.
|
||||
|
||||
## Design Patterns
|
||||
* **Complex Dialogue System**: Maple's interactions feature a highly branching and conditional dialogue system, where player choices and game progression influence the conversation flow and subsequent events.
|
||||
* **Quest Gating/Progression**: Maple is a central figure in a "Dream" questline, with her dialogue and actions dynamically adapting based on Link's collected Pendants and Dreams, guiding the player through a significant narrative arc.
|
||||
* **Player State Manipulation**: Maple possesses the unique ability to put Link to sleep, which triggers special dream sequences and warps him to different locations, creating immersive and story-rich transitions.
|
||||
* **Cinematic Sequences**: The `Sprite_PutLinkToSleep` routine orchestrates a cinematic effect, incorporating screen transitions, visual filters, and the spawning of ancillae to enhance the player's experience during dream initiations.
|
||||
* **Global State Management**: The sprite extensively modifies `Pendants`, `Dreams`, `CurrentDream`, and other global variables to meticulously track and influence quest progress, ensuring a consistent and evolving game world.
|
||||
@@ -1,363 +0,0 @@
|
||||
# Mask Salesman
|
||||
|
||||
## Overview
|
||||
The Mask Salesman sprite (`!SPRID = Sprite_MaskSalesman`) is a complex NPC that functions as a vendor and a quest-giver, offering masks for sale and teaching Link songs. His interactions are highly conditional, branching based on Link's inventory (Ocarina, learned songs, owned masks) and current rupee count.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_MaskSalesman` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `02`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `01` (Impervious to all attacks)
|
||||
* **`!SmallShadow`**: `01`
|
||||
* **`!Shadow`**: `01`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `02`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_MaskSalesman_Long`)
|
||||
This routine handles the Mask Salesman's drawing and dispatches to its main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_MaskSalesman_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_MaskSalesman_Draw
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_MaskSalesman_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_MaskSalesman_Prep`)
|
||||
This routine is empty, indicating that the Mask Salesman requires no custom initialization upon spawning.
|
||||
|
||||
## Main Logic & State Machine (`Sprite_MaskSalesman_Main`)
|
||||
The Mask Salesman's core behavior is managed by a complex state machine that facilitates a branching dialogue and transaction system.
|
||||
|
||||
* **Player Collision**: Prevents Link from passing through the Mask Salesman (`JSL Sprite_PlayerCantPassThrough`).
|
||||
* **`InquiryHandler`**: Plays an animation, checks Link's Ocarina status (`$7EF34C`), and displays a solicited message asking if Link wants to buy a mask. Based on Link's response (`$1CE8`) and inventory, it transitions to various states like `NoOcarina`, `HasOcarina`, `OfferBunnyHood`, `OfferStoneMask`, or `PlayerSaidNoToMask`.
|
||||
* **`NoOcarina`**: Displays a message instructing Link to get the Ocarina first, then returns to `InquiryHandler`.
|
||||
* **`HasOcarina`**: Displays a message acknowledging Link has the Ocarina, then transitions to `TeachLinkSong`.
|
||||
* **`TeachLinkSong`**: Increments Link's learned songs count (`$7EF34C`), plays a song learned sound, and returns to `InquiryHandler`.
|
||||
* **`OfferBunnyHood`**: Displays a message offering the Bunny Hood for 100 rupees, then transitions to `BoughtBunnyHood`.
|
||||
* **`OfferStoneMask`**: Displays a message offering the Stone Mask for 650 rupees, then transitions to `BoughtStoneMask`.
|
||||
* **`PlayerSaidNoToMask`**: Displays a message and returns to `InquiryHandler`.
|
||||
* **`PlayerHasAllMasks`**: Displays a message indicating Link has all masks, then returns to `InquiryHandler`.
|
||||
* **`BoughtBunnyHood`**: Processes Link's decision to buy the Bunny Hood. Checks rupee count, grants the item (`LDY #$10`, `JSL Link_ReceiveItem`), deducts rupees, displays a confirmation message, and returns to `InquiryHandler`. If rupees are insufficient, it transitions to `NotEnoughMoney`.
|
||||
* **`BoughtStoneMask`**: Similar to `BoughtBunnyHood`, but for the Stone Mask (`LDY #$19`) and a higher rupee cost.
|
||||
* **`NotEnoughMoney`**: Displays a message indicating insufficient funds, then returns to `InquiryHandler`.
|
||||
|
||||
```asm
|
||||
Sprite_MaskSalesman_Main:
|
||||
{
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw InquiryHandler
|
||||
dw NoOcarina
|
||||
dw HasOcarina
|
||||
dw TeachLinkSong
|
||||
dw OfferBunnyHood
|
||||
dw OfferStoneMask
|
||||
dw PlayerSaidNoToMask
|
||||
dw PlayerHasAllMasks
|
||||
dw BoughtBunnyHood
|
||||
dw BoughtStoneMask
|
||||
dw NotEnoughMoney
|
||||
|
||||
; 0x00
|
||||
InquiryHandler:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
; Player has a Lv1 Ocarina, skip to the you got it message
|
||||
LDA.l $7EF34C : CMP.b #$01 : BEQ .has_ocarina
|
||||
; Player has no Ocarina or Lv2 Ocarina
|
||||
; Do you want to buy a mask?
|
||||
%ShowSolicitedMessage($E5) : BCC .didnt_converse
|
||||
LDA $1CE8 : BNE .player_said_no
|
||||
|
||||
; Player wants to buy a mask
|
||||
LDA.l $7EF34C : CMP.b #$02 : BCS .has_song_healing
|
||||
|
||||
; No Ocarina yet
|
||||
%GotoAction(1)
|
||||
RTS
|
||||
|
||||
.has_ocarina
|
||||
%GotoAction(2)
|
||||
RTS
|
||||
|
||||
.has_song_healing
|
||||
LDA.l $7EF348 : CMP.b #$01 : BCS .has_bunny_mask
|
||||
%GotoAction(4)
|
||||
RTS
|
||||
.has_bunny_mask
|
||||
LDA.l $7EF352 : CMP.b #$01 : BCS .has_stone_mask
|
||||
%GotoAction(5)
|
||||
RTS
|
||||
.has_stone_mask
|
||||
%GotoAction(7)
|
||||
RTS
|
||||
|
||||
.player_said_no
|
||||
%GotoAction(6)
|
||||
.didnt_converse
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x01 - Link has not yet gotten the Ocarina
|
||||
NoOcarina:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
%ShowUnconditionalMessage($E9) ; Go get the Ocarina first!
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x02 - Link has the Ocarina, but not all the songs
|
||||
HasOcarina:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
%ShowSolicitedMessage($081) ; Oh! You got it!
|
||||
%GotoAction(3)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x03
|
||||
TeachLinkSong:
|
||||
{
|
||||
LDA #$02 : STA $7EF34C ; Increment the number of songs Link has
|
||||
LDA.b #$13
|
||||
STA.w $0CF8
|
||||
JSL $0DBB67 ; Link_CalculateSFXPan
|
||||
ORA.w $0CF8
|
||||
STA $012E ; Play the song learned sound
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x04 - Offer Bunny Hood
|
||||
OfferBunnyHood:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
%ShowUnconditionalMessage($07F) ; Bunny Hood for 100 rupees?
|
||||
%GotoAction(8)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x05 - Offer Stone Mask
|
||||
OfferStoneMask:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
%ShowUnconditionalMessage($082) ; Stone Mask for 650 rupees?
|
||||
%GotoAction(9)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x06 - Player said no to buying a mask
|
||||
PlayerSaidNoToMask:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
%ShowUnconditionalMessage($E8)
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x07 - Player has all the masks
|
||||
PlayerHasAllMasks:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
%ShowUnconditionalMessage($028)
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
}
|
||||
|
||||
BoughtBunnyHood:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
LDA $1CE8 : BNE .player_said_no
|
||||
REP #$20
|
||||
LDA.l $7EF360 : CMP.w #$64 ; 100 rupees
|
||||
SEP #$30
|
||||
BCC .not_enough_rupees
|
||||
|
||||
LDY.b #$10 ; Bunny Hood
|
||||
STZ.w $02E9
|
||||
PHX
|
||||
JSL Link_ReceiveItem
|
||||
PLX
|
||||
|
||||
REP #$20
|
||||
LDA.l $7EF360
|
||||
SEC : SBC.w #$64 ; Subtract 100 rupees
|
||||
STA.l $7EF360
|
||||
SEP #$30
|
||||
|
||||
%ShowUnconditionalMessage($063)
|
||||
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
|
||||
.not_enough_rupees
|
||||
%GotoAction($0A)
|
||||
RTS
|
||||
.player_said_no
|
||||
%GotoAction(6)
|
||||
RTS
|
||||
}
|
||||
|
||||
BoughtStoneMask:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
LDA $1CE8 : BNE .player_said_no
|
||||
REP #$20
|
||||
LDA.l $7EF360 : CMP.w #$352 ; 850 rupees
|
||||
SEP #$30
|
||||
BCC .not_enough_rupees
|
||||
|
||||
LDY #$19 ; Stone Mask
|
||||
STZ.w $02E9
|
||||
PHX
|
||||
JSL Link_ReceiveItem
|
||||
PLX
|
||||
|
||||
REP #$20
|
||||
LDA.l $7EF360
|
||||
SEC : SBC.w #$352 ; Subtract 850 rupees
|
||||
STA.l $7EF360
|
||||
SEP #$30
|
||||
|
||||
%ShowUnconditionalMessage($055)
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
|
||||
.not_enough_rupees
|
||||
%GotoAction($0A)
|
||||
RTS
|
||||
.player_said_no
|
||||
%GotoAction(6)
|
||||
RTS
|
||||
}
|
||||
|
||||
NotEnoughMoney:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
%ShowUnconditionalMessage($029)
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_MaskSalesman_Draw`)
|
||||
The drawing routine handles OAM allocation and animation. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
```asm
|
||||
Sprite_MaskSalesman_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA $0DC0, X : CLC : ADC $0D90, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY
|
||||
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
|
||||
.start_index
|
||||
db $00, $04
|
||||
.nbr_of_tiles
|
||||
db 3, 3
|
||||
.x_offsets
|
||||
dw -4, 12, 0, 0
|
||||
dw 4, -12, 0, 0
|
||||
.y_offsets
|
||||
dw -8, -8, 0, -11
|
||||
dw -8, -8, 0, -10
|
||||
.chr
|
||||
db $82, $84, $A0, $80
|
||||
db $82, $84, $A0, $80
|
||||
.properties
|
||||
db $39, $39, $39, $39
|
||||
db $79, $79, $79, $39
|
||||
.sizes
|
||||
db $02, $02, $02, $02
|
||||
db $02, $02, $02, $02
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Complex Dialogue and Shop System**: The Mask Salesman implements a sophisticated dialogue tree that functions as a shop, offering items (masks) and services (teaching songs) based on player choices and inventory. This creates a dynamic and interactive vendor experience.
|
||||
* **Quest Gating/Progression**: Interactions with the Mask Salesman are gated by Link's possession of the Ocarina and the number of songs he has learned, integrating the NPC into the game's progression system.
|
||||
* **Conditional Transactions**: The process of buying masks involves checking Link's current rupee count and deducting the cost upon a successful purchase, simulating a real in-game economy.
|
||||
* **Player Choice and Branching Dialogue**: Link's responses to the Mask Salesman's inquiries directly influence the flow of conversation and the available options, leading to a personalized interaction.
|
||||
* **Item Granting**: The Mask Salesman grants masks to Link and teaches him new songs, providing valuable rewards and abilities.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,119 +0,0 @@
|
||||
# Mermaid / Maple / Librarian
|
||||
|
||||
## Overview
|
||||
The `mermaid.asm` file is a highly versatile sprite definition that implements three distinct NPC characters: the "Mermaid," "Maple," and "Librarian." This multi-purpose sprite leverages `SprSubtype` and `SprMiscE, X` to dispatch to different behaviors and drawing routines, allowing for efficient resource reuse and complex, context-sensitive interactions within the game world.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_Mermaid` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `02`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Mermaid_Long`)
|
||||
This routine acts as a central dispatcher, selecting the appropriate drawing routine based on `SprMiscE, X` (0 for Mermaid, 1 for Maple, 2 for Librarian). It also handles shadow drawing and dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Mermaid_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.w SprMiscE, X : BEQ .MermaidDraw
|
||||
CMP.b #$02 : BEQ .LibrarianDraw
|
||||
JSR Sprite_Maple_Draw
|
||||
JMP .Continue
|
||||
.LibrarianDraw
|
||||
JSR Sprite_Librarian_Draw
|
||||
JMP .Continue
|
||||
.MermaidDraw
|
||||
JSR Sprite_Mermaid_Draw
|
||||
.Continue
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Mermaid_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Mermaid_Prep`)
|
||||
This routine initializes the sprite upon spawning. It sets `SprDefl, X`, `SprTimerA, X`, and `SprHitbox, X`. Crucially, it sets `SprMiscE, X` based on `SprSubtype, X` to determine which character the sprite will represent (0 for Mermaid, 1 for Maple, 2 for Librarian).
|
||||
|
||||
```asm
|
||||
Sprite_Mermaid_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
LDA.b #$40 : STA.w SprTimerA, X
|
||||
LDA.b #$07 : STA.w SprHitbox, X
|
||||
|
||||
; Mermaid Sprite
|
||||
STZ.w SprMiscE, X
|
||||
|
||||
; Maple Sprite
|
||||
LDA.w SprSubtype, X : CMP.b #$01 : BNE +
|
||||
LDA.b #$01 : STA.w SprMiscE, X
|
||||
+
|
||||
|
||||
; Librarian Sprite
|
||||
CMP.b #$02 : BNE ++
|
||||
LDA.b #$02 : STA.w SprMiscE, X
|
||||
++
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Mermaid_Main`)
|
||||
This routine acts as a dispatcher for the main logic, calling the appropriate handler (`MermaidHandler`, `MapleHandler`, or `LibrarianHandler`) based on `SprMiscE, X`.
|
||||
|
||||
### `MermaidHandler`
|
||||
Manages the Mermaid's behavior through a state machine:
|
||||
|
||||
* **`MermaidWait`**: Plays an idle animation, prevents player passage, and displays a message on contact. Upon message dismissal, it transitions to `MermaidDive`.
|
||||
* **`MermaidDive`**: Plays a diving animation, moves horizontally, and transitions to `MermaidSwim` after a timer.
|
||||
* **`MermaidSwim`**: Plays a swimming animation, moves, sets `SprXSpeed, X`, spawns a splash effect, and can despawn or change direction after a timer.
|
||||
|
||||
### `LibrarianHandler`
|
||||
Manages the Librarian's behavior, primarily focused on a map and scroll translation quest:
|
||||
|
||||
* **`LibrarianIdle`**: Plays an animation, prevents player passage, and displays messages based on whether Link has no maps, all maps, or new scrolls. Transitions to `Librarian_CheckResponse` if new scrolls are available.
|
||||
* **`Librarian_CheckResponse`**: Processes Link's response to the translation offer, transitioning to `Librarian_OfferTranslation` or back to `LibrarianIdle`.
|
||||
* **`Librarian_OfferTranslation`**: Displays a message, prevents player passage, and checks `Scrolls` and `DNGMAP1`/`DNGMAP2` to identify new scrolls. If found, it updates `Scrolls`, sets `SprMiscG, X` to the scroll ID, and transitions to `Librarian_TranslateScroll`.
|
||||
* **`Librarian_TranslateScroll`**: Displays a message based on the scroll ID and transitions to `Librarian_FinishTranslation`.
|
||||
* **`Librarian_FinishTranslation`**: Displays a final message and returns to `LibrarianIdle`.
|
||||
|
||||
## `Librarian_CheckForAllMaps` and `Librarian_CheckForNoMaps`
|
||||
These helper routines check `DNGMAP1` and `DNGMAP2` (SRAM flags for dungeon maps) to determine Link's map collection status.
|
||||
|
||||
## Drawing (`Sprite_Mermaid_Draw`, `Sprite_Maple_Draw`, `Sprite_Librarian_Draw`)
|
||||
Each character has its own dedicated drawing routine. These routines handle OAM allocation and animation, and explicitly use `REP #$20` and `SEP #$20` for 16-bit coordinate calculations. Each routine contains its own specific OAM data for rendering the respective character.
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Character Sprite (Conditional Drawing/Logic)**: A single sprite definition (`Sprite_Mermaid`) is used to represent three distinct NPCs (Mermaid, Maple, Librarian) based on `SprSubtype` and `SprMiscE`, showcasing efficient resource utilization and context-sensitive character representation.
|
||||
* **Quest Progression Integration**: The Librarian's dialogue and actions are tied to collected dungeon maps and scrolls, indicating its role in a translation quest, driving narrative progression.
|
||||
* **Context-Sensitive Dialogue**: The Librarian's messages dynamically change based on whether Link has maps, all maps, or new scrolls, providing a personalized and evolving interaction.
|
||||
* **Player Collision**: Implements `Sprite_PlayerCantPassThrough` to make NPCs solid objects that Link cannot walk through.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,181 +0,0 @@
|
||||
# Piratian
|
||||
|
||||
## Overview
|
||||
The Piratian sprite (`!SPRID = $0E`) represents an NPC that initially behaves in a friendly manner, engaging Link through dialogue and moving randomly. However, it possesses an "aggro" system, becoming hostile and attacking Link if provoked. A unique aspect of this sprite is its dynamic health scaling, which adjusts based on Link's current Sword level.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `$0E` (Vanilla sprite ID, likely for a generic NPC)
|
||||
* **`!NbrTiles`**: `02`
|
||||
* **`!Harmless`**: `00` (Initially harmful, but `Sprite_Piratian_Friendly` suggests otherwise)
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00` (Dynamically set in `_Prep`)
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Piratian_Long`)
|
||||
This routine handles the Piratian's drawing, shadow rendering, and dispatches to its main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Piratian_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_Piratian_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Piratian_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Piratian_Prep`)
|
||||
This routine initializes the Piratian upon spawning. Its health (`SprHealth, X`) is dynamically set based on Link's current Sword level (`$7EF359`), providing a form of difficulty scaling. `SprMiscA, X` is initialized to `0` (likely an aggro flag), and a specific bit of `SprNbrOAM, X` is set, potentially for drawing behavior.
|
||||
|
||||
```asm
|
||||
Sprite_Piratian_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.l $7EF359 : TAY
|
||||
LDA.w .health, Y : STA.w SprHealth, X
|
||||
STZ.w SprMiscA, X
|
||||
LDA.w SprNbrOAM, X : ORA.b #$80 : STA.w SprNbrOAM, X
|
||||
PLB
|
||||
RTL
|
||||
|
||||
.health
|
||||
db $08, $0A, $0C, $0F
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Piratian_Main`)
|
||||
This routine orchestrates the Piratian's movement and animation, calling `Sprite_Piratian_Move` and then dispatching to various animation states based on `SprAction, X`.
|
||||
|
||||
* **`Piratian_MoveDown` / `Up` / `Left` / `Right`**: Each state plays a specific walking animation.
|
||||
* **`SkullHead`**: Plays a specific animation, likely when the Piratian is defeated or in a particular state.
|
||||
|
||||
```asm
|
||||
Sprite_Piratian_Main:
|
||||
{
|
||||
JSR Sprite_Piratian_Move
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Piratian_MoveDown
|
||||
dw Piratian_MoveUp
|
||||
dw Piratian_MoveLeft
|
||||
dw Piratian_MoveRight
|
||||
dw SkullHead
|
||||
|
||||
Piratian_MoveDown:
|
||||
{
|
||||
%PlayAnimation(0,1,16)
|
||||
RTS
|
||||
}
|
||||
|
||||
Piratian_MoveUp:
|
||||
{
|
||||
%PlayAnimation(2,3,16)
|
||||
RTS
|
||||
}
|
||||
|
||||
Piratian_MoveLeft:
|
||||
{
|
||||
%PlayAnimation(4,5,16)
|
||||
RTS
|
||||
}
|
||||
|
||||
Piratian_MoveRight:
|
||||
{
|
||||
%PlayAnimation(6,7,16)
|
||||
RTS
|
||||
}
|
||||
|
||||
SkullHead:
|
||||
{
|
||||
%PlayAnimation(8,9,16)
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Movement and Interaction (`Sprite_Piratian_Move`)
|
||||
This routine handles the Piratian's movement, collision, damage reactions, and implements its "aggro" system.
|
||||
|
||||
* **Random Movement**: If `SprTimerA, X` is `0`, the Piratian randomly selects a new direction (`Sprite_SelectNewDirection`) and updates its animation state (`SprAction, X`).
|
||||
* **Physics & Collision**: Moves the sprite (`JSL Sprite_MoveXyz`), handles tile collision (`JSL Sprite_BounceFromTileCollision`), damage flash (`JSL Sprite_DamageFlash_Long`), and interaction with thrown sprites (`JSL ThrownSprite_TileAndSpriteInteraction_long`).
|
||||
* **Aggro Logic**: If the Piratian takes damage from Link (`JSL Sprite_CheckDamageFromPlayer`), it sets `SprMiscA, X` to `01` (aggro flag), changes its drawing behavior (`SprNbrOAM, X`), sets timers, and begins to attack Link (`Sprite_ProjectSpeedTowardsPlayer`, `Sprite_CheckDamageToPlayer`).
|
||||
* **Friendly Behavior**: If not in an aggro state, it calls `Sprite_Piratian_Friendly` for dialogue interaction.
|
||||
|
||||
```asm
|
||||
Sprite_Piratian_Move:
|
||||
{
|
||||
LDA.w SprTimerA, X : BNE +
|
||||
JSL Sprite_SelectNewDirection
|
||||
TYA
|
||||
CMP.b #$03 : BCC ++
|
||||
SEC : SBC.b #$03
|
||||
++
|
||||
STA.w SprAction, X
|
||||
+
|
||||
|
||||
JSL Sprite_MoveXyz
|
||||
JSL Sprite_BounceFromTileCollision
|
||||
JSL Sprite_DamageFlash_Long
|
||||
JSL ThrownSprite_TileAndSpriteInteraction_long
|
||||
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_dano
|
||||
LDA.b #$01 : STA.w SprMiscA, X
|
||||
LDA.w SprNbrOAM, X : AND.b #$7F : STA.w SprNbrOAM, X
|
||||
%SetTimerA($60)
|
||||
%SetTimerF($20)
|
||||
.no_dano
|
||||
|
||||
LDA.w SprMiscA, X : BEQ .no_aggro
|
||||
LDA.b #$10 : STA.w SprTimerA, X
|
||||
LDA.b #$08
|
||||
JSL Sprite_ProjectSpeedTowardsPlayer
|
||||
JSL Sprite_CheckDamageToPlayer
|
||||
JMP .return
|
||||
.no_aggro
|
||||
|
||||
JSR Sprite_Piratian_Friendly
|
||||
.return
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Friendly Interaction (`Sprite_Piratian_Friendly`)
|
||||
This routine handles the Piratian's friendly dialogue. If `SprTimerD, X` is `0`, it displays a message on contact (`%ShowMessageOnContact($01BB)`). Upon message dismissal, it sets `SprTimerD, X` to `$FF`.
|
||||
|
||||
## Drawing (`Sprite_Piratian_Draw`)
|
||||
The drawing routine uses the `%DrawSprite()` macro to render the Piratian's graphics based on defined OAM data tables.
|
||||
|
||||
## Design Patterns
|
||||
* **Dynamic Health Scaling**: The Piratian's health is dynamically adjusted based on Link's current Sword level, providing a form of adaptive difficulty.
|
||||
* **Aggro System**: The Piratian features an "aggro" system where it transitions from a friendly, dialogue-based NPC to a hostile enemy if Link attacks it, adding depth to interactions.
|
||||
* **Random Movement**: The Piratian moves randomly, contributing to its NPC-like behavior and making its movements less predictable.
|
||||
* **NPC Interaction**: The Piratian can be interacted with through dialogue when in its friendly state, offering context or hints.
|
||||
* **Conditional Behavior**: The sprite's behavior changes significantly based on its "friendly" or "aggro" state, demonstrating complex state management.
|
||||
* **16-bit OAM Calculations**: Although `%DrawSprite()` is used, the underlying drawing routines likely utilize `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,95 +0,0 @@
|
||||
# Ranch Girl
|
||||
|
||||
## Overview
|
||||
The `ranch_girl.asm` file defines the behavior for the "Ranch Girl" NPC, who is involved in a "Chicken Easter Egg" quest. This character plays a crucial role in granting Link the Ocarina and teaching him a song. Her behavior is implemented as a vanilla override, modifying the existing `ChickenLady` routine.
|
||||
|
||||
## Vanilla Overrides
|
||||
This file directly modifies the vanilla `ChickenLady` routine at `org $01AFECF` to inject custom logic for the Ranch Girl's interactions.
|
||||
|
||||
## `RanchGirl_Message`
|
||||
This routine handles the dialogue displayed by the Ranch Girl. It checks Link's Ocarina status (`$7EF34C`).
|
||||
|
||||
* If Link already possesses the Ocarina, it displays message `$010E`.
|
||||
* Otherwise, it displays message `$017D` and sets `SprMiscD, X` to `$01`, indicating the start of the Ocarina quest.
|
||||
|
||||
```asm
|
||||
RanchGirl_Message:
|
||||
{
|
||||
LDA $7EF34C : CMP.b #$01 : BCS .has_ocarina
|
||||
%ShowUnconditionalMessage($017D)
|
||||
LDA #$01 : STA.w SprMiscD, X
|
||||
RTL
|
||||
.has_ocarina
|
||||
%ShowUnconditionalMessage($010E)
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## `RanchGirl_TeachSong`
|
||||
This routine is responsible for teaching Link a song (specifically the "Song of Storms") and granting him the Ocarina. It checks the Ocarina quest flag (`SprMiscD, X`) and Link's current Ocarina status (`$7EF34C`).
|
||||
|
||||
* If the conditions are met, it plays the "Song of Storms" sound, gives Link the Ocarina (`LDY #$14`, `JSL Link_ReceiveItem`), and sets `$7EF34C` to `$01`.
|
||||
|
||||
```asm
|
||||
RanchGirl_TeachSong:
|
||||
{
|
||||
LDA.w SprMiscD, X : CMP.b #$01 : BNE .not_started
|
||||
LDA $10 : CMP.b #$0E : BEQ .running_dialog
|
||||
LDA $7EF34C : CMP.b #$01 : BCS .has_song
|
||||
|
||||
; Play the song of storms
|
||||
LDA.b #$2F
|
||||
STA.w $0CF8
|
||||
JSL $0DBB67 ; Link_CalculateSFXPan
|
||||
ORA.w $0CF8
|
||||
STA $012E ; Play the song learned sound
|
||||
|
||||
; Give Link the Ocarina
|
||||
LDY #$14
|
||||
; Clear the item receipt ID
|
||||
STZ $02E9
|
||||
PHX
|
||||
JSL Link_ReceiveItem
|
||||
PLX
|
||||
|
||||
LDA #$01 : STA $7EF34C ; The item gives 02 by default, so decrement that for now
|
||||
|
||||
.not_started
|
||||
.running_dialog
|
||||
.has_song
|
||||
LDA.b $1A : LSR #4 : AND.b #$01 : STA.w $0DC0,X
|
||||
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## `ChickenLady` (Vanilla Override)
|
||||
This is the main entry point for the Ranch Girl's custom behavior, overriding the vanilla `ChickenLady` routine. It sets `SprMiscC, X` to `$01`, calls vanilla drawing and activity check routines, and then executes `RanchGirl_Message` and `RanchGirl_TeachSong`.
|
||||
|
||||
```asm
|
||||
org $01AFECF
|
||||
ChickenLady:
|
||||
{
|
||||
JSR .main
|
||||
RTL
|
||||
|
||||
.main
|
||||
LDA.b #$01 : STA.w SprMiscC, X
|
||||
|
||||
JSL SpriteDraw_RaceGameLady
|
||||
JSR Sprite_CheckIfActive_Bank1A
|
||||
|
||||
LDA.w SprTimerA, X : CMP.b #$01 : BNE .no_message
|
||||
JSL RanchGirl_Message
|
||||
.no_message
|
||||
JSL RanchGirl_TeachSong
|
||||
.return
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Vanilla Override**: This file directly modifies a vanilla routine (`ChickenLady`) to implement custom NPC behavior, demonstrating a common ROM hacking technique.
|
||||
* **Quest Gating/Progression**: The Ranch Girl's dialogue and the granting of the Ocarina are tied to Link's possession of the Ocarina and the state of the Ocarina quest, integrating her into the game's progression system.
|
||||
* **Item Granting**: The Ranch Girl serves as a source for the Ocarina, a key item in the game.
|
||||
* **Game State Manipulation**: Directly modifies `$7EF34C` (Ocarina flag) and `SprMiscD, X` (quest flag) to track and influence game state.
|
||||
@@ -1,250 +0,0 @@
|
||||
# Tingle
|
||||
|
||||
## Overview
|
||||
The Tingle sprite (`!SPRID = Sprite_Tingle`) implements the iconic map salesman character. Tingle's primary function is to sell a map to Link, with his interactions and dialogue flow being conditional on Link's rupee count and whether the map has already been purchased.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_Tingle` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `02`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `01` (Impervious to all attacks)
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `01`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `02`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Tingle_Long`)
|
||||
This routine handles Tingle's drawing, shadow rendering, and dispatches to his main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Tingle_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_Tingle_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Tingle_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Tingle_Prep`)
|
||||
This routine is empty, indicating that Tingle requires no custom initialization upon spawning.
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Tingle_Main`)
|
||||
Tingle's core behavior is managed by a state machine that facilitates a dialogue and transaction flow for selling a map.
|
||||
|
||||
* **Player Collision**: Prevents Link from passing through Tingle (`JSL Sprite_PlayerCantPassThrough`).
|
||||
* **`Tingle_Idle`**: Plays an animation. Checks if Link has already bought the map (`$7EF3D6` bit `$01`). If so, it transitions to `Tingle_AlreadyBoughtMap`. Otherwise, it displays a solicited message (`%ShowSolicitedMessage($01A4)`) asking if Link wants to buy a map. Based on Link's response (`$1CE8`), it transitions to `Tingle_BuyMap` or `Tingle_PlayerSaidNo`.
|
||||
* **`Tingle_BuyMap`**: Plays an animation. Checks if Link has enough rupees (`$7EF360`). If sufficient, it deducts the rupees, sets the map bought flag (`$7EF3D6` bit `$01`), displays a confirmation message (`%ShowUnconditionalMessage($01A5)`), and transitions to `Tingle_AlreadyBoughtMap`. If rupees are insufficient, it transitions to `Tingle_NotEnoughMoney`.
|
||||
* **`Tingle_PlayerSaidNo`**: Plays an animation, displays a message (`%ShowUnconditionalMessage($01A6)`), and returns to `Tingle_Idle`.
|
||||
* **`Tingle_AlreadyBoughtMap`**: Plays an animation, displays a message (`%ShowUnconditionalMessage($01A3)`) confirming the map has been bought, and returns to `Tingle_Idle`.
|
||||
* **`Tingle_NotEnoughMoney`**: Plays an animation, displays a message (`%ShowUnconditionalMessage($029)`) about insufficient funds, and returns to `Tingle_Idle`.
|
||||
|
||||
```asm
|
||||
Sprite_Tingle_Main:
|
||||
{
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Tingle_Idle
|
||||
dw Tingle_BuyMap
|
||||
dw Tingle_PlayerSaidNo
|
||||
dw Tingle_AlreadyBoughtMap
|
||||
dw Tingle_NotEnoughMoney
|
||||
|
||||
; 0x00
|
||||
Tingle_Idle:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
; Player has already bought the map
|
||||
LDA.l $7EF3D6 : AND.b #$01 : BNE .already_bought_map
|
||||
|
||||
%ShowSolicitedMessage($01A4) : BCC .didnt_converse
|
||||
LDA $1CE8 : BEQ .buy_map
|
||||
|
||||
; Player said no
|
||||
%GotoAction(2)
|
||||
RTS
|
||||
|
||||
.buy_map
|
||||
%GotoAction(1)
|
||||
RTS
|
||||
|
||||
.already_bought_map
|
||||
%GotoAction(3)
|
||||
RTS
|
||||
|
||||
.didnt_converse
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x01
|
||||
Tingle_BuyMap:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
REP #$20
|
||||
LDA.l $7EF360 : CMP.w #$0064 ; 100 rupees
|
||||
SEP #$30
|
||||
BCC .not_enough_money
|
||||
|
||||
; Deduct rupees
|
||||
REP #$20
|
||||
LDA.l $7EF360
|
||||
SEC : SBC.w #$0064
|
||||
STA.l $7EF360
|
||||
SEP #$30
|
||||
|
||||
; Set map bought flag
|
||||
LDA.l $7EF3D6 : ORA.b #$01 : STA.l $7EF3D6
|
||||
|
||||
%ShowUnconditionalMessage($01A5)
|
||||
%GotoAction(3)
|
||||
RTS
|
||||
|
||||
.not_enough_money
|
||||
%GotoAction(4)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x02
|
||||
Tingle_PlayerSaidNo:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
%ShowUnconditionalMessage($01A6)
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x03
|
||||
Tingle_AlreadyBoughtMap:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
%ShowUnconditionalMessage($01A3)
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
}
|
||||
|
||||
; 0x04
|
||||
Tingle_NotEnoughMoney:
|
||||
{
|
||||
%PlayAnimation(0, 1, 16)
|
||||
%ShowUnconditionalMessage($029)
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_Tingle_Draw`)
|
||||
The drawing routine handles OAM allocation and animation. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
```asm
|
||||
Sprite_Tingle_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA $0DC0, X : CLC : ADC $0D90, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY
|
||||
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
|
||||
.start_index
|
||||
db $00, $04
|
||||
.nbr_of_tiles
|
||||
db 3, 3
|
||||
.x_offsets
|
||||
dw -4, 12, 0, 0
|
||||
dw 4, -12, 0, 0
|
||||
.y_offsets
|
||||
dw -8, -8, 0, -11
|
||||
dw -8, -8, 0, -10
|
||||
.chr
|
||||
db $82, $84, $A0, $80
|
||||
db $82, $84, $A0, $80
|
||||
.properties
|
||||
db $39, $39, $39, $39
|
||||
db $79, $79, $79, $39
|
||||
.sizes
|
||||
db $02, $02, $02, $02
|
||||
db $02, $02, $02, $02
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Shop System**: Tingle implements a basic shop system for selling a map, including price checks and rupee deduction, providing a functional in-game vendor.
|
||||
* **Quest Gating/Progression**: The availability of the map and Tingle's dialogue are conditional on whether Link has already purchased the map (`$7EF3D6` bit `$01`), ensuring a logical progression of events.
|
||||
* **Conditional Transactions**: The process of buying the map involves checking Link's rupee count and deducting the cost upon a successful purchase, simulating a real in-game economy.
|
||||
* **Player Choice and Branching Dialogue**: Link's responses (`$1CE8`) to Tingle's inquiries directly influence the flow of conversation and the available options, leading to a personalized interaction.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,210 +0,0 @@
|
||||
# Vasu / Error
|
||||
|
||||
## Overview
|
||||
The `vasu.asm` file defines the behavior for a multi-character NPC sprite that can represent either "Vasu," the ring appraiser, or a special "Error" sprite. The specific character displayed and its interactions are determined by `SprSubtype, X`. Vasu offers a service to appraise rings, with conditional dialogue and transactions based on Link's inventory and rupee count.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_Vasu` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `03`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `09`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Vasu_Long`)
|
||||
This routine acts as a dispatcher for drawing, selecting `Sprite_Vasu_Draw` for Vasu (`SprSubtype, X = 0`) or `Sprite_Error_Draw` for the Error sprite. It also handles shadow drawing and dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Vasu_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.w SprSubtype, X : BNE +
|
||||
JSR Sprite_Vasu_Draw
|
||||
JMP ++
|
||||
+
|
||||
JSR Sprite_Error_Draw
|
||||
++
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Vasu_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Vasu_Prep`)
|
||||
This routine initializes the sprite upon spawning. It sets `SprDefl, X` to `$80`. If the sprite is Vasu (`SprSubtype, X = 0`), it sets `SprAction, X` to `$04`.
|
||||
|
||||
```asm
|
||||
Sprite_Vasu_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
LDA.w SprSubtype, X : BEQ +
|
||||
LDA.b #$04 : STA.w SprAction, X
|
||||
+
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Vasu_Main`)
|
||||
This routine manages the various states and interactions for both Vasu and the Error sprite.
|
||||
|
||||
* **Player Collision**: Prevents Link from passing through (`JSL Sprite_PlayerCantPassThrough`).
|
||||
* **`Vasu_Idle`**: Plays an animation and displays a solicited message (`%ShowSolicitedMessage($00A9)`). Upon message dismissal, it transitions to `Vasu_MessageHandler`.
|
||||
* **`Vasu_MessageHandler`**: Plays an animation and processes Link's choice (`MsgChoice`). It can lead to `Vasu_AppraiseRing`, `Vasu_ExplainRings` (displays message `$00AA` and returns to `Vasu_Idle`), or return to `Vasu_Idle` if Link chooses "nevermind."
|
||||
* **`Vasu_AppraiseRing`**: Plays an animation. Checks `FOUNDRINGS` (SRAM flag for found rings). If no rings are found, it displays a message (`%ShowUnconditionalMessage($00AD)`) and returns to `Vasu_Idle`. If rings are found, it checks `MAGICRINGS` (SRAM flag for owned rings). If Link has no rings yet, it offers the first appraisal for free (`%ShowUnconditionalMessage($00AB)`). Otherwise, it checks for 20 rupees (`$7EF360`). If Link has enough, it deducts the rupees, updates `MAGICRINGS` by ORing with `FOUNDRINGS`, and transitions to `Vasu_RingAppraised`. If not enough rupees, it displays a message (`%ShowUnconditionalMessage($0189)`) and returns to `Vasu_Idle`.
|
||||
* **`Vasu_RingAppraised`**: Plays an animation, displays a message (`%ShowUnconditionalMessage($00AC)`), and returns to `Vasu_Idle`.
|
||||
* **`Error_Idle`**: Plays an animation and displays a solicited message (`%ShowSolicitedMessage($0121)`) "I am Error." Upon message dismissal, it randomly sets `FOUNDRINGS`.
|
||||
|
||||
```asm
|
||||
Sprite_Vasu_Main:
|
||||
{
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Vasu_Idle
|
||||
dw Vasu_MessageHandler
|
||||
dw Vasu_AppraiseRing
|
||||
dw Vasu_RingAppraised
|
||||
|
||||
dw Error_Idle
|
||||
|
||||
Vasu_Idle:
|
||||
{
|
||||
%PlayAnimation(0,1,20)
|
||||
%ShowSolicitedMessage($00A9) : BCC .didnt_talk
|
||||
%GotoAction(1)
|
||||
.didnt_talk
|
||||
RTS
|
||||
}
|
||||
|
||||
Vasu_MessageHandler:
|
||||
{
|
||||
%PlayAnimation(0,1,20)
|
||||
LDA.w MsgChoice : BEQ .appraise_rings
|
||||
CMP.b #$01 : BEQ .explain_rings
|
||||
; Player said nevermind.
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
.explain_rings
|
||||
%ShowUnconditionalMessage($00AA)
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
.appraise_rings
|
||||
LDA.b #$40 : STA.w SprTimerB, X
|
||||
%GotoAction(2)
|
||||
RTS
|
||||
}
|
||||
|
||||
Vasu_AppraiseRing:
|
||||
{
|
||||
%PlayAnimation(0,1,20)
|
||||
|
||||
; Check if the player has found any rings to appraise
|
||||
REP #$30
|
||||
LDA.l FOUNDRINGS
|
||||
AND.w #$00FF
|
||||
SEP #$30
|
||||
BEQ .no_rings
|
||||
; Check if the player has any rings, if not give them one for free
|
||||
LDA.l MAGICRINGS : BEQ .no_rings_yet
|
||||
REP #$20
|
||||
LDA.l $7EF360
|
||||
CMP.w #$14 ; 20 rupees
|
||||
SEP #$30
|
||||
BCC .not_enough_rupees
|
||||
|
||||
REP #$20
|
||||
LDA.l $7EF360
|
||||
SEC
|
||||
SBC.w #$14 ; Subtract 20 rupees
|
||||
STA.l $7EF360
|
||||
SEP #$30
|
||||
|
||||
JMP .appraise_me
|
||||
|
||||
.not_enough_rupees
|
||||
%ShowUnconditionalMessage($0189) ; 'You don't have enough rupees!'
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
|
||||
.no_rings_yet
|
||||
%ShowUnconditionalMessage($00AB) ; 'First one is free!'
|
||||
JMP .appraise_me
|
||||
|
||||
.no_rings
|
||||
%ShowUnconditionalMessage($00AD) ; 'You don't have any rings!'
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
|
||||
.appraise_me
|
||||
; Check the found rings and set the saved rings
|
||||
; Get the bit from found rings and set it in MAGICRINGS
|
||||
LDA.l FOUNDRINGS
|
||||
ORA.l MAGICRINGS
|
||||
STA.l MAGICRINGS
|
||||
|
||||
%GotoAction(3)
|
||||
|
||||
RTS
|
||||
}
|
||||
|
||||
Vasu_RingAppraised:
|
||||
{
|
||||
%PlayAnimation(0,1,20)
|
||||
%ShowUnconditionalMessage($00AC) ; 'Come back later for more appraisals!'
|
||||
|
||||
%GotoAction(0)
|
||||
RTS
|
||||
}
|
||||
|
||||
Error_Idle:
|
||||
{
|
||||
%PlayAnimation(0,1,24)
|
||||
; "I am Error"
|
||||
%ShowSolicitedMessage($0121) : BCC +
|
||||
JSL GetRandomInt : AND.b #$06 : STA.l FOUNDRINGS
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_Vasu_Draw` and `Sprite_Error_Draw`)
|
||||
Both drawing routines handle OAM allocation and animation for their respective characters. They explicitly use `REP #$20` and `SEP #$20` for 16-bit coordinate calculations. Each routine contains its own specific OAM data for rendering the character.
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Character Sprite (Conditional Drawing/Logic)**: A single sprite definition (`Sprite_Vasu`) is used to represent two distinct characters (Vasu and the "Error" sprite) based on `SprSubtype`, showcasing efficient resource utilization and varied visual appearances.
|
||||
* **Shop/Service System**: Vasu implements a service where he appraises rings for a fee (or for free the first time), integrating a transactional element into NPC interactions.
|
||||
* **Quest Gating/Progression**: Vasu's interactions are conditional on Link having found rings (`FOUNDRINGS`) and his rupee count, ensuring that the appraisal service is available only when relevant.
|
||||
* **Conditional Transactions**: The appraisal process involves checking Link's rupee count and deducting the cost, simulating a real in-game economy.
|
||||
* **Player Choice and Branching Dialogue**: Link's choices (`MsgChoice`) directly influence the flow of conversation, leading to different outcomes and information from Vasu.
|
||||
* **Item Management**: Vasu interacts with `FOUNDRINGS` and `MAGICRINGS` (SRAM flags) to manage Link's ring collection, updating his inventory based on appraisals.
|
||||
* **Easter Egg/Hidden Content**: The "Error" sprite, with its unique dialogue, likely serves as an Easter egg or a placeholder for debugging, adding a touch of humor or mystery.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,263 +0,0 @@
|
||||
# Village Dog / Eon Dog
|
||||
|
||||
## Overview
|
||||
The `village_dog.asm` file defines the behavior for two distinct dog NPCs: the "Village Dog" and the "Eon Dog." Their appearance and behavior are dynamically determined by the global `WORLDFLAG`. These dogs exhibit random movement, react to Link's proximity, can be lifted and thrown, and offer context-sensitive dialogue based on Link's current form.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_VillageDog` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `08`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `01`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `09`
|
||||
* **`!Persist`**: `01` (Continues to live off-screen)
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `01` (Checks both layers for collision)
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_VillageDog_Long`)
|
||||
This routine acts as a dispatcher for drawing, selecting `Sprite_VillageDog_Draw` for the Village Dog (`WORLDFLAG = 0`) or `Sprite_EonDog_Draw` for the Eon Dog. It also handles shadow drawing and dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_VillageDog_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.w WORLDFLAG : BEQ .village
|
||||
JSR Sprite_EonDog_Draw
|
||||
JMP +
|
||||
.village
|
||||
JSR Sprite_VillageDog_Draw
|
||||
+
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_VillageDog_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_VillageDog_Prep`)
|
||||
This routine initializes the dog upon spawning. If it's an Eon Dog (`WORLDFLAG` is not `0`), it sets `SprAction, X` to `$07` and `SprTimerA, X` to `$40`.
|
||||
|
||||
```asm
|
||||
Sprite_VillageDog_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.w WORLDFLAG : BEQ .village
|
||||
LDA.b #$07 : STA.w SprAction, X
|
||||
LDA.b #$40 : STA.w SprTimerA, X
|
||||
.village
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## `HandleTossedDog`
|
||||
This routine manages the vertical movement of a dog that has been tossed. If `SprHeight, X` is not `0`, it decrements it, simulating gravity.
|
||||
|
||||
## `LiftOrTalk`
|
||||
This routine determines whether Link can lift the dog or engage in dialogue. It checks Link's current form (`$02B2`). If Link is in Wolf or Minish form, it calls `ShowMessageIfMinish`. Otherwise, it checks if the dog is lifted (`JSL Sprite_CheckIfLifted`) and handles interactions with thrown sprites (`JSL ThrownSprite_TileAndSpriteInteraction_long`).
|
||||
|
||||
## Main Logic & State Machine (`Sprite_VillageDog_Main`)
|
||||
This routine manages the various states and behaviors of both Village Dogs and Eon Dogs.
|
||||
|
||||
* **`Dog_Handler`**: Plays a sitting animation, calls `HandleTossedDog`, and if Link is nearby, sets a timer and transitions to `Dog_LookLeftAtLink` or `Dog_LookRightAtLink`. It also calls `LiftOrTalk`.
|
||||
* **`Dog_LookLeftAtLink` / `Dog_LookRightAtLink`**: Plays an animation of the dog looking towards Link. After a timer, it transitions to `Dog_MoveLeftTowardsLink` or `Dog_MoveRightTowardsLink`.
|
||||
* **`Dog_MoveLeftTowardsLink` / `Dog_MoveRightTowardsLink`**: Plays a walking animation, calls `HandleTossedDog`, and if Link is nearby, transitions to `Dog_WagTailLeft` or `Dog_WagTailRight`. It handles tile collisions, applies speed towards Link, and calls `LiftOrTalk`. After a timer, it returns to `Dog_Handler`.
|
||||
* **`Dog_WagTailLeft` / `Dog_WagTailRight`**: Plays a wagging tail animation, calls `LiftOrTalk` and `HandleTossedDog`. After a timer, it returns to `Dog_Handler`.
|
||||
* **`EonDog_Handler` / `EonDog_Right`**: These states are specific to the Eon Dog, playing animations and calling `EonDog_Walk`.
|
||||
|
||||
```asm
|
||||
Sprite_VillageDog_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Dog_Handler ; 00
|
||||
dw Dog_LookLeftAtLink ; 01
|
||||
dw Dog_LookRightAtLink ; 02
|
||||
dw Dog_MoveLeftTowardsLink ; 03
|
||||
dw Dog_MoveRightTowardsLink ; 04
|
||||
dw Dog_WagTailLeft ; 05
|
||||
dw Dog_WagTailRight ; 06
|
||||
|
||||
dw EonDog_Handler ; 07
|
||||
dw EonDog_Right ; 08
|
||||
|
||||
; 0
|
||||
Dog_Handler:
|
||||
{
|
||||
%PlayAnimation(8,8,8) ; Sitting
|
||||
JSR HandleTossedDog
|
||||
LDA $0309 : AND #$03 : BNE .lifting
|
||||
LDA #$20 : STA.w SprTimerD, X
|
||||
JSL Sprite_IsToRightOfPlayer : TYA : BEQ .walk_right
|
||||
%GotoAction(1)
|
||||
JMP .lifting
|
||||
.walk_right
|
||||
%GotoAction(2)
|
||||
.lifting
|
||||
JSR LiftOrTalk
|
||||
JSL Sprite_Move
|
||||
RTS
|
||||
}
|
||||
|
||||
; 01
|
||||
Dog_LookLeftAtLink:
|
||||
{
|
||||
%PlayAnimation(9,9,8)
|
||||
JSR HandleTossedDog
|
||||
LDA.w SprTimerD, X : BNE +
|
||||
; Load the timer for the run
|
||||
LDA.b #$60 : STA.w SprTimerD, X
|
||||
%GotoAction(3)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
; 02
|
||||
Dog_LookRightAtLink:
|
||||
{
|
||||
%PlayAnimation(10,10,8)
|
||||
JSR HandleTossedDog
|
||||
LDA.w SprTimerD, X : BNE +
|
||||
; Load the timer for the run
|
||||
LDA.b #$60 : STA.w SprTimerD, X
|
||||
%GotoAction(4)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
; 03
|
||||
Dog_MoveLeftTowardsLink:
|
||||
{
|
||||
%PlayAnimation(2,4,6)
|
||||
JSR HandleTossedDog
|
||||
; Check if the dog is near link, then wag the tail
|
||||
JSR CheckIfPlayerIsNearby : BCC +
|
||||
%GotoAction(5)
|
||||
+
|
||||
|
||||
; Check for collision
|
||||
JSL Sprite_CheckTileCollision
|
||||
LDA $0E70, X : BEQ .no_collision
|
||||
%GotoAction(0)
|
||||
.no_collision
|
||||
|
||||
LDA.b #$0A
|
||||
JSL Sprite_ApplySpeedTowardsPlayer
|
||||
STZ $06 : STZ $07
|
||||
JSL Sprite_MoveLong
|
||||
|
||||
JSR LiftOrTalk
|
||||
|
||||
LDA.w SprTimerD, X : BNE +
|
||||
%GotoAction(0)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
; 04
|
||||
Dog_MoveRightTowardsLink:
|
||||
{
|
||||
%PlayAnimation(5,7,6)
|
||||
JSR HandleTossedDog
|
||||
JSR CheckIfPlayerIsNearby : BCC +
|
||||
%GotoAction(6)
|
||||
+
|
||||
|
||||
; Check for collision
|
||||
JSL Sprite_CheckTileCollision
|
||||
LDA $0E70, X : BEQ .no_collision
|
||||
%GotoAction(0)
|
||||
.no_collision
|
||||
|
||||
LDA.b #$0A
|
||||
JSL Sprite_ApplySpeedTowardsPlayer
|
||||
STZ $06 : STZ $07
|
||||
JSL Sprite_MoveLong
|
||||
JSR LiftOrTalk
|
||||
|
||||
LDA.w SprTimerD, X : BNE ++
|
||||
%GotoAction(0)
|
||||
++
|
||||
RTS
|
||||
}
|
||||
|
||||
; 05
|
||||
Dog_WagTailLeft:
|
||||
{
|
||||
%PlayAnimation(0,1, 8)
|
||||
JSR LiftOrTalk
|
||||
JSR HandleTossedDog
|
||||
LDA.w SprTimerD, X : BNE +
|
||||
%GotoAction(0)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
; 06
|
||||
Dog_WagTailRight:
|
||||
{
|
||||
%PlayAnimation(11,12,8)
|
||||
JSR LiftOrTalk
|
||||
JSR HandleTossedDog
|
||||
LDA.w SprTimerD, X : BNE +
|
||||
%GotoAction(0)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
EonDog_Handler:
|
||||
{
|
||||
%PlayAnimation(0,1,8)
|
||||
JSR EonDog_Walk
|
||||
RTS
|
||||
}
|
||||
|
||||
EonDog_Right:
|
||||
{
|
||||
%PlayAnimation(2,3,8)
|
||||
JSR EonDog_Walk
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `EonDog_Walk`
|
||||
This routine handles the Eon Dog's random movement. It moves the sprite (`JSL Sprite_MoveLong`), handles tile collision (`JSL Sprite_BounceFromTileCollision`), and after a timer, randomly selects a new direction and updates its speed and action.
|
||||
|
||||
## `CheckIfPlayerIsNearby`
|
||||
This routine checks if Link is within a specific rectangular area around the dog, returning with the carry flag set if true.
|
||||
|
||||
## `ShowMessageIfMinish`
|
||||
This routine displays a context-sensitive message based on Link's current form (`$02B2`). If Link is in Minish form, it displays message `$18`; otherwise, it displays message `$1B`.
|
||||
|
||||
## Drawing (`Sprite_VillageDog_Draw` and `Sprite_EonDog_Draw`)
|
||||
Both drawing routines handle OAM allocation and animation for their respective dog types. They explicitly use `REP #$20` and `SEP #$20` for 16-bit coordinate calculations. Each routine contains its own specific OAM data for rendering the character.
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Character NPC (Conditional Drawing/Logic)**: A single sprite definition (`Sprite_VillageDog`) is used to represent two distinct dog characters (Village Dog and Eon Dog) based on `WORLDFLAG`, showcasing efficient resource utilization and varied visual appearances.
|
||||
* **Random Movement**: The dogs exhibit random movement patterns, contributing to the environmental ambiance and making their movements less predictable.
|
||||
* **Player Interaction**: The dogs can be lifted and thrown (`LiftOrTalk`), and they react to Link's presence by looking at him and wagging their tails, adding to the interactive elements of the game world.
|
||||
* **Conditional Dialogue**: The `ShowMessageIfMinish` routine provides context-sensitive dialogue based on Link's current form, enhancing the narrative and player experience.
|
||||
* **Player Collision**: Implements `Sprite_PlayerCantPassThrough` to make the dogs solid objects that Link cannot walk through.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,38 +0,0 @@
|
||||
# Village Elder
|
||||
|
||||
## Overview
|
||||
The `village_elder.asm` file defines the behavior for the "Village Elder" NPC. This is a straightforward NPC whose primary function is to interact with Link through dialogue. The content of the dialogue is conditional, changing based on whether Link has previously met the elder, as tracked by a custom progression flag (`OOSPROG`).
|
||||
|
||||
## Main Logic (`Sprite_VillageElder_Main`)
|
||||
This routine manages the Village Elder's interactions and dialogue flow:
|
||||
|
||||
* **Animation**: Plays a specific animation (`%PlayAnimation(2,3,16)`).
|
||||
* **Player Collision**: Prevents Link from passing through the elder (`JSL Sprite_PlayerCantPassThrough`).
|
||||
* **Progression Check (`OOSPROG`)**: It checks the `OOSPROG` (Oracle of Secrets Progression) flag. Specifically, it checks if bit `$10` is set, which indicates that Link has already met the elder.
|
||||
* **First Meeting**: If Link has not yet met the elder, it displays a solicited message (`%ShowSolicitedMessage($143)`). Upon dismissal of this message, it sets bit `$10` in `OOSPROG` to mark that Link has now met the elder.
|
||||
* **Subsequent Meetings**: If Link has already met the elder, a different solicited message (`%ShowSolicitedMessage($019)`) is displayed.
|
||||
|
||||
```asm
|
||||
Sprite_VillageElder_Main:
|
||||
{
|
||||
%PlayAnimation(2,3,16)
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
REP #$30
|
||||
LDA.l OOSPROG : AND.w #$00FF
|
||||
SEP #$30
|
||||
AND.b #$10 : BNE .already_met
|
||||
%ShowSolicitedMessage($143) : BCC .no_message
|
||||
LDA.l OOSPROG : ORA.b #$10 : STA.l OOSPROG
|
||||
.no_message
|
||||
RTS
|
||||
|
||||
.already_met
|
||||
%ShowSolicitedMessage($019)
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Simple NPC Interaction**: The Village Elder provides basic dialogue interaction with Link.
|
||||
* **Quest Progression Tracking**: Utilizes a custom progression flag (`OOSPROG`) to track whether Link has met the elder, allowing for dynamic changes in dialogue based on game state.
|
||||
* **Player Collision**: Implements `JSL Sprite_PlayerCantPassThrough` to make the elder a solid object that Link cannot walk through.
|
||||
@@ -1,218 +0,0 @@
|
||||
# Zora (Generic Sea Zora Handler)
|
||||
|
||||
## Overview
|
||||
The `zora.asm` file serves as a centralized handler for various Zora NPCs within the game. It acts as a dispatcher, directing execution to specific drawing and main logic routines for the Zora Princess, Eon Zora, Eon Zora Elder, and a generic Sea Zora. This dynamic dispatch is based on the current `ROOM`, `WORLDFLAG`, and `SprSubtype`, allowing for a single sprite definition to manage a diverse cast of Zora characters.
|
||||
|
||||
## Main Structure (`Sprite_Zora_Long`)
|
||||
This routine is a complex dispatcher that determines which Zora variant to draw and process based on several game state variables:
|
||||
|
||||
* **Zora Princess**: If the current `ROOM` is `$0105`, it calls `Sprite_ZoraPrincess_Draw` and sets `SprMiscG, X` to `$01`.
|
||||
* **Eon Zora**: If `WORLDFLAG` is not `0`, it calls `Sprite_EonZora_Draw`, `Sprite_DrawShadow`, and sets `SprMiscG, X` to `$02`.
|
||||
* **Eon Zora Elder**: If `SprSubtype, X` is not `0` (and not the Princess or Eon Zora), it calls `Sprite_EonZoraElder_Draw` and sets `SprMiscG, X` to `$03`.
|
||||
* **Generic Sea Zora**: Otherwise, it calls `Sprite_Zora_Draw`, `Sprite_DrawShadow`, and sets `SprMiscG, X` to `0`.
|
||||
* After drawing, it calls `Sprite_CheckActive` and then `Sprite_Zora_Handler` if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Zora_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
|
||||
; Check what Zora we are drawing
|
||||
REP #$30
|
||||
LDA.w ROOM : CMP.w #$0105 : BNE .not_princess
|
||||
SEP #$30
|
||||
JSR Sprite_ZoraPrincess_Draw
|
||||
LDA.b #$01 : STA.w SprMiscG, X
|
||||
JMP +
|
||||
.not_princess
|
||||
LDA.w WORLDFLAG : AND.w #$00FF : BEQ .eon_draw
|
||||
SEP #$30
|
||||
JSR Sprite_EonZora_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
LDA.b #$02 : STA.w SprMiscG, X
|
||||
JMP +
|
||||
.eon_draw
|
||||
SEP #$30
|
||||
LDA.w SprSubtype, X : BNE .special_zora
|
||||
JSR Sprite_Zora_Draw
|
||||
JSL Sprite_DrawShadow
|
||||
STZ.w SprMiscG, X
|
||||
JMP +
|
||||
.special_zora
|
||||
JSR Sprite_EonZoraElder_Draw
|
||||
LDA.b #$03 : STA.w SprMiscG, X
|
||||
+
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Zora_Handler
|
||||
.SpriteIsNotActive
|
||||
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Zora_Prep`)
|
||||
This routine is empty, indicating that custom initialization for the Zora handler is minimal or handled by the individual Zora sprite routines.
|
||||
|
||||
## Main Logic Dispatcher (`Sprite_Zora_Handler`)
|
||||
This routine dispatches to the appropriate main logic routine for the specific Zora variant based on the value of `SprMiscG, X`:
|
||||
|
||||
* `$01`: Calls `Sprite_ZoraPrincess_Main`
|
||||
* `$00`: Calls `Sprite_Zora_Main` (Generic Sea Zora)
|
||||
* `$02`: Calls `Sprite_EonZora_Main`
|
||||
* `$03`: Calls `Sprite_EonZoraElder_Main`
|
||||
|
||||
```asm
|
||||
Sprite_Zora_Handler:
|
||||
{
|
||||
LDA.w SprMiscG, X
|
||||
CMP.b #$01 : BNE .not_princess
|
||||
JSR Sprite_ZoraPrincess_Main
|
||||
RTS
|
||||
.not_princess
|
||||
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Sprite_Zora_Main
|
||||
dw Sprite_ZoraPrincess_Main
|
||||
dw Sprite_EonZora_Main
|
||||
dw Sprite_EonZoraElder_Main
|
||||
}
|
||||
```
|
||||
|
||||
## `Sprite_Zora_Main` (Generic Sea Zora)
|
||||
This routine defines the behavior for a generic Sea Zora NPC.
|
||||
|
||||
* **Head Tracking**: Calls `Zora_TrackHeadToPlayer` to make the Zora face Link.
|
||||
* **Player Collision**: Prevents Link from passing through the Zora (`JSL Sprite_PlayerCantPassThrough`).
|
||||
* **Dialogue**: Calls `Zora_HandleDialogue` for context-sensitive dialogue interactions.
|
||||
* **Animation**: Uses a jump table for animation states: `Zora_Forward`, `Zora_Right`, `Zora_Left`, each playing a specific animation.
|
||||
|
||||
```asm
|
||||
Sprite_Zora_Main:
|
||||
{
|
||||
JSR Zora_TrackHeadToPlayer
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
JSR Zora_HandleDialogue
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Zora_Forward
|
||||
dw Zora_Right
|
||||
dw Zora_Left
|
||||
|
||||
Zora_Forward:
|
||||
{
|
||||
%PlayAnimation(0,0,10)
|
||||
RTS
|
||||
}
|
||||
|
||||
Zora_Right:
|
||||
{
|
||||
%PlayAnimation(1,1,10)
|
||||
RTS
|
||||
}
|
||||
|
||||
Zora_Left:
|
||||
{
|
||||
%PlayAnimation(1,1,10)
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `Zora_TrackHeadToPlayer`
|
||||
This routine makes the Zora face Link by setting `SprAction, X` to `0` (forward) or `1` (right/left) based on Link's horizontal position relative to the Zora.
|
||||
|
||||
## `Zora_HandleDialogue`
|
||||
This routine handles context-sensitive dialogue for the generic Sea Zora. It checks the `Crystals` SRAM flag (specifically bit `$20`) to determine if a certain crystal has been collected. Based on this and the Zora's `SprAction, X`, it displays different solicited messages (`$01A6`, `$01A5`, or `$01A4`).
|
||||
|
||||
## Drawing (`Sprite_Zora_Draw`)
|
||||
This routine handles OAM allocation and animation for the generic Sea Zora. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
```asm
|
||||
Sprite_Zora_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA.w SprFrame, X : TAY ;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
.start_index
|
||||
db $00, $02, $04
|
||||
.nbr_of_tiles
|
||||
db 1, 1, 1
|
||||
.x_offsets
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
dw 0, 0
|
||||
.y_offsets
|
||||
dw -8, 0
|
||||
dw -8, 0
|
||||
dw -8, 0
|
||||
.chr
|
||||
db $DE, $EE
|
||||
db $DC, $EC
|
||||
db $DC, $EC
|
||||
.properties
|
||||
db $35, $35
|
||||
db $35, $35
|
||||
db $75, $75
|
||||
.sizes
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
db $02, $02
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Centralized NPC Handler**: This file acts as a central dispatcher for multiple Zora-type NPCs (Zora Princess, Eon Zora, Eon Zora Elder, and generic Sea Zora), demonstrating efficient management of diverse character behaviors from a single entry point.
|
||||
* **Multi-Character Sprite (Conditional Drawing/Logic)**: A single sprite ID is used to represent various Zora characters, with their specific drawing and main logic routines dynamically selected based on game state variables like `ROOM`, `WORLDFLAG`, and `SprSubtype`.
|
||||
* **Context-Sensitive Dialogue**: The generic Sea Zora's dialogue changes based on collected crystals and its current `SprAction`, providing dynamic and responsive interactions with the player.
|
||||
* **Player Collision**: Implements `JSL Sprite_PlayerCantPassThrough` to make the Zora NPCs solid objects that Link cannot walk through.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,219 +0,0 @@
|
||||
# Zora Princess
|
||||
|
||||
## Overview
|
||||
The Zora Princess sprite (`!SPRID = Sprite_ZoraPrincess`) is a key NPC involved in a quest that culminates in Link receiving the Zora Mask. Her interactions are conditional, primarily triggered by Link playing the "Song of Healing," and her presence is tied to whether Link has already obtained the Zora Mask.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_ZoraPrincess` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `9`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `02`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_ZoraPrincess_Long`)
|
||||
This routine handles the Zora Princess's drawing and dispatches to her main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_ZoraPrincess_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_ZoraPrincess_Draw
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_ZoraPrincess_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_ZoraPrincess_Prep`)
|
||||
This routine initializes the Zora Princess upon spawning. It checks the Zora Mask flag (`$7EF302`). If Link already possesses the Zora Mask, the sprite immediately despawns (`STZ.w SprState, X`), ensuring the quest is a one-time event. It also sets `SprDefl, X` and `SprTileDie, X` to `0`.
|
||||
|
||||
```asm
|
||||
Sprite_ZoraPrincess_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.l $7EF302 : BEQ .doesnt_have_mask
|
||||
STZ.w SprState, X ; Kill the sprite
|
||||
.doesnt_have_mask
|
||||
|
||||
LDA #$00 : STA.w SprDefl, X
|
||||
LDA #$00 : STA.w SprTileDie, X
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_ZoraPrincess_Main`)
|
||||
This routine manages the Zora Princess's interactions and the process of granting the Zora Mask.
|
||||
|
||||
* **Player Collision**: Prevents Link from passing through the Zora Princess (`JSL Sprite_PlayerCantPassThrough`).
|
||||
* **`WaitForLink`**: Plays an animation and displays a solicited message (`%ShowSolicitedMessage($0C5)`). Upon message dismissal, it transitions to `CheckForSongOfHealing`.
|
||||
* **`CheckForSongOfHealing`**: Plays an animation and checks the `SongFlag`. If the "Song of Healing" has been played, it clears the `SongFlag`, sets a timer (`SprTimerD, X`), and transitions to `ThanksMessage`.
|
||||
* **`ThanksMessage`**: Plays an animation. After a timer (`SprTimerD, X`), it displays an unconditional message (`%ShowUnconditionalMessage($0C6)`) and transitions to `GiveZoraMask`.
|
||||
* **`GiveZoraMask`**: After a timer (`SprTimerD, X`), it grants Link the Zora Mask (`LDY #$0F`, `JSL Link_ReceiveItem`), sets the Zora Mask obtained flag (`$7EF302` to `$01`), and despawns the sprite (`STZ.w SprState, X`).
|
||||
|
||||
```asm
|
||||
Sprite_ZoraPrincess_Main:
|
||||
{
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw WaitForLink
|
||||
dw CheckForSongOfHealing
|
||||
dw ThanksMessage
|
||||
dw GiveZoraMask
|
||||
|
||||
WaitForLink:
|
||||
{
|
||||
%PlayAnimation(0, 1, 10)
|
||||
%ShowSolicitedMessage($0C5) : BCC .no_hablaba
|
||||
%GotoAction(1)
|
||||
.no_hablaba
|
||||
RTS
|
||||
}
|
||||
|
||||
CheckForSongOfHealing:
|
||||
{
|
||||
%PlayAnimation(0, 1, 10)
|
||||
LDA.b SongFlag : BEQ .ninguna_cancion
|
||||
STZ.b SongFlag
|
||||
LDA.b #$C0 : STA.w SprTimerD, X
|
||||
%GotoAction(2)
|
||||
.ninguna_cancion
|
||||
RTS
|
||||
}
|
||||
|
||||
ThanksMessage:
|
||||
{
|
||||
%PlayAnimation(0, 1, 10)
|
||||
LDA.w SprTimerD, X : BNE +
|
||||
%ShowUnconditionalMessage($0C6)
|
||||
LDA.b #$C0 : STA.w SprTimerD, X
|
||||
%GotoAction(3)
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
GiveZoraMask:
|
||||
{
|
||||
LDA.w SprTimerD, X : BNE +
|
||||
LDY #$0F : STZ $02E9 ; Give the Zora Mask
|
||||
JSL Link_ReceiveItem
|
||||
LDA #$01 : STA.l $7EF302
|
||||
LDA.b #$00 : STA.w SprState, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_ZoraPrincess_Draw`)
|
||||
The drawing routine handles OAM allocation and animation. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
```asm
|
||||
Sprite_ZoraPrincess_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
JSL Sprite_OAM_AllocateDeferToPlayer
|
||||
|
||||
LDA $0DC0, X : CLC : ADC $0D90, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : CLC : ADC .x_offsets, X : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
TYA : LSR #2 : TAY
|
||||
LDA .sizes, X : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
PLY : INY
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
|
||||
.start_index
|
||||
db $00, $04
|
||||
.nbr_of_tiles
|
||||
db 3, 11
|
||||
.x_offsets
|
||||
dw -4, 4, -4, 4
|
||||
dw 4, 4, 4, 4, -4, -4, -4, -4, 12, 12, 12, 12
|
||||
.y_offsets
|
||||
dw -8, -8, 8, 8
|
||||
dw -8, 0, 8, 16, -8, 0, 8, 16, -8, 0, 8, 16
|
||||
.chr
|
||||
db $C0, $C1, $E0, $E1
|
||||
db $C1, $D1, $E1, $F1, $C3, $D3, $E3, $F3, $C3, $D3, $E3, $F3
|
||||
.properties
|
||||
db $33, $33, $33, $33
|
||||
db $33, $33, $33, $33, $33, $33, $33, $33, $73, $73, $73, $73
|
||||
.sizes
|
||||
db $02, $02, $02, $02
|
||||
db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Quest Gating/Progression**: The Zora Princess's appearance and the granting of the Zora Mask are conditional on Link not already possessing the mask and playing the "Song of Healing," integrating her into a specific questline.
|
||||
* **NPC Interaction**: The Zora Princess interacts with the player through dialogue and grants a key item (the Zora Mask), which is essential for progression.
|
||||
* **Conditional Spawning/Despawning**: The sprite dynamically despawns if Link has already obtained the Zora Mask, ensuring that the quest is a one-time event and preventing redundant interactions.
|
||||
* **Item Granting**: The Zora Princess serves as the source for the Zora Mask, a significant item that likely grants new abilities or access to new areas.
|
||||
* **Player Collision**: Implements `JSL Sprite_PlayerCantPassThrough` to make the Zora Princess a solid object that Link cannot walk through.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,56 +0,0 @@
|
||||
# Objects Analysis
|
||||
|
||||
This document provides an analysis of the object sprites found in the `Sprites/Objects/` directory. These sprites are typically interactive elements of the environment rather than enemies.
|
||||
|
||||
## File Overview
|
||||
|
||||
| Filename | Sprite ID(s) | Description |
|
||||
|---|---|---|
|
||||
| `collectible.asm` | `$52` | A generic collectible sprite that can represent a Pineapple, Seashell, Sword/Shield, or Rock Sirloin. |
|
||||
| `deku_leaf.asm` | `Sprite_DekuLeaf` | A Deku Leaf platform that Link can stand on. Also used for whirlpools. |
|
||||
| `ice_block.asm` | `$D5` | A pushable ice block used in puzzles. |
|
||||
| `minecart.asm` | `Sprite_Minecart` (`$A3`) | A rideable minecart used for transportation puzzles in dungeons. |
|
||||
| `mineswitch.asm` | `Sprite_Mineswitch` | A lever switch that can be hit to change the state of other objects, like minecart tracks. |
|
||||
| `pedestal.asm` | (Hooks `$1EE05F`) | Logic for the magic pedestals where Link can read text with the Book of Mudora. |
|
||||
| `portal_sprite.asm` | `Sprite_Portal` | The blue and orange portals created by the Portal Rod. |
|
||||
| `switch_track.asm` | `Sprite_SwitchTrack` (`$B0`) | The visual component of a switchable minecart track, which rotates when a lever is hit. |
|
||||
|
||||
## Detailed Object Analysis
|
||||
|
||||
### `collectible.asm`
|
||||
- **Sprite ID:** `$52`
|
||||
- **Summary:** This is a versatile sprite that acts as a world collectible. Its behavior and appearance are determined by its `SprAction` state, which is set during prep.
|
||||
- **Key Logic:**
|
||||
- **`Sprite_Collectible_Prep`:** Checks the current map (`$8A`) to determine what the sprite should be. For example, on map `$58` (intro), it becomes the sword and shield.
|
||||
- **`Sprite_Collectible_Main`:** The main state machine. On contact with the player (`Sprite_CheckDamageToPlayer`), it increments the corresponding counter in SRAM (e.g., `Pineapples`, `Seashells`) or grants an item (`Link_ReceiveItem`) and then despawns itself.
|
||||
|
||||
### `ice_block.asm`
|
||||
- **Sprite ID:** `$D5`
|
||||
- **Summary:** A block that slides on icy floors when pushed by Link. It is used for puzzles.
|
||||
- **Key Logic:**
|
||||
- **Pushing:** When Link is in contact (`Sprite_CheckDamageToPlayerSameLayer`), the `Sprite_ApplyPush` routine gives the block velocity based on the direction Link is facing.
|
||||
- **Sliding:** The block continues to move in that direction (`Sprite_Move`) until it hits a wall (`Sprite_CheckTileCollision`) or another object.
|
||||
- **Switch Interaction:** `Sprite_IceBlock_CheckForSwitch` checks if the block is on top of a floor switch tile. If so, it sets a flag (`$0642`) to activate the switch and stops moving.
|
||||
|
||||
### `minecart.asm` / `switch_track.asm` / `mineswitch.asm`
|
||||
- **Sprite IDs:** `Sprite_Minecart` (`$A3`), `Sprite_SwitchTrack` (`$B0`), `Sprite_Mineswitch`
|
||||
- **Summary:** This is a complex system of three interconnected sprites that create minecart puzzles.
|
||||
- **Key Logic:**
|
||||
- **`Sprite_Minecart`:**
|
||||
- The player can press B to enter the cart (`Minecart_WaitHoriz`/`Vert`). This sets the `!LinkInCart` flag and attaches the player.
|
||||
- The cart moves along a path defined by custom tile types (`$B0`-`$BE`).
|
||||
- At intersections (`$B6`, etc.), it reads player input (`$F0`) to determine which way to turn (`CheckForPlayerInput`).
|
||||
- At corners (`$B2`-`$B5`), it automatically turns (`CheckForCornerTiles`).
|
||||
- At dynamic switch tracks (`$D0`-`$D3`), it checks the state of the corresponding `Sprite_Mineswitch` to determine its path (`HandleDynamicSwitchTileDirections`).
|
||||
- At dungeon transitions, it converts into a follower sprite (`MinecartFollower_TransitionToSprite`) to persist between rooms.
|
||||
- **`Sprite_SwitchTrack`:** This is a purely visual sprite. Its frame is set based on the on/off state of its corresponding `Mineswitch`, which is stored in `SwitchRam`.
|
||||
- **`Sprite_Mineswitch`:** This is a lever. When hit by the player, it toggles its state in the `SwitchRam` array, which is then read by the `SwitchTrack` and `Minecart` sprites.
|
||||
|
||||
### `portal_sprite.asm`
|
||||
- **Sprite ID:** `Sprite_Portal`
|
||||
- **Summary:** This sprite handles the logic for the portals created by the Portal Rod.
|
||||
- **Key Logic:**
|
||||
- **Spawning:** Two portals can exist at once: one blue and one orange. The `StateHandler` determines which type to create based on the `$7E0FA6` flag.
|
||||
- **Warping:** When Link overlaps with a portal (`CheckIfHitBoxesOverlap`), it triggers a warp. The destination is the location of the *other* portal, whose coordinates are stored in RAM (`BluePortal_X/Y`, `OrangePortal_X/Y`).
|
||||
- **Dismissal:** The `CheckForDismissPortal` routine despawns the oldest portal when a third one is created.
|
||||
- **Invalid Placement:** `RejectOnTileCollision` prevents portals from being placed on invalid surfaces like walls and despawns the sprite if they are.
|
||||
@@ -1,172 +0,0 @@
|
||||
# Collectible Sprites (Pineapple, Seashell, Sword/Shield, Rock Sirloin)
|
||||
|
||||
## Overview
|
||||
The Collectible sprite (`!SPRID = $52`) is a versatile implementation designed to represent various collectible items within the game, including Pineapples, Seashells, the starting Sword/Shield, and Rock Sirloin. Its specific appearance and behavior are dynamically determined by the `SprAction, X` state and the current `AreaIndex`, allowing for context-sensitive item placement and interaction.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `$52` (Vanilla sprite ID, likely for a generic collectible)
|
||||
* **`!NbrTiles`**: `03`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Collectible_Long`)
|
||||
This routine acts as a dispatcher for drawing, selecting the appropriate drawing routine based on the `AreaIndex`:
|
||||
|
||||
* If `AreaIndex` is `$58` (Intro Sword area), it calls `Sprite_SwordShield_Draw`.
|
||||
* If `AreaIndex` is `$4B` (Lupo Mountain area), it calls `Sprite_RockSirloin_Draw`.
|
||||
* Otherwise, it calls `Sprite_Pineapple_Draw`.
|
||||
* It also handles shadow drawing and dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Collectible_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
|
||||
LDA.b $8A : CMP.b #$58 : BNE .not_intro_sword
|
||||
JSR Sprite_SwordShield_Draw
|
||||
BRA +
|
||||
.not_intro_sword
|
||||
LDA.b $8A : CMP.b #$4B : BNE .not_lupo_mountain
|
||||
JSR Sprite_RockSirloin_Draw
|
||||
BRA +
|
||||
.not_lupo_mountain
|
||||
JSR Sprite_Pineapple_Draw
|
||||
+
|
||||
JSL Sprite_DrawShadow
|
||||
JSL Sprite_CheckActive
|
||||
BCC .SpriteIsNotActive
|
||||
|
||||
JSR Sprite_Collectible_Main
|
||||
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Collectible_Prep`)
|
||||
This routine initializes the collectible sprite upon spawning, with conditional logic based on the `AreaIndex`:
|
||||
|
||||
* **Intro Sword**: If `AreaIndex` is `$58`, it checks Link's Sword flag (`$7EF359`). If Link already has the Sword, the sprite despawns. It also sets `SprAction, X` to `$02` (SwordShield).
|
||||
* **Rock Sirloin**: If `AreaIndex` is `$4B`, it sets `SprAction, X` to `$03` (RockSirloin).
|
||||
|
||||
```asm
|
||||
Sprite_Collectible_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
|
||||
; Don't spawn the sword if we have it.
|
||||
LDA.b $8A : CMP.b #$58 : BNE .not_intro_sword
|
||||
LDA.l $7EF359 : BEQ +
|
||||
STZ.w SprState, X
|
||||
+
|
||||
LDA.b #$02 : STA.w SprAction, X
|
||||
.not_intro_sword
|
||||
LDA.b $8A : CMP.b #$4B : BNE .not_lupo_mountain
|
||||
LDA.b #$03 : STA.w SprAction, X
|
||||
.not_lupo_mountain
|
||||
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Collectible_Main`)
|
||||
This routine manages the behavior of various collectible items through a jump table based on `SprAction, X`:
|
||||
|
||||
* **`Pineapple`**: Moves the sprite (`JSL Sprite_Move`). If Link touches it (`JSL Sprite_CheckDamageToPlayer`), it increments the `Pineapples` custom item count and despawns the sprite.
|
||||
* **`Seashell`**: Similar to Pineapple, but increments the `Seashells` custom item count.
|
||||
* **`SwordShield`**: Plays an animation, moves the sprite. If Link touches it, it grants Link the Sword (`LDY.b #$00`, `JSL Link_ReceiveItem`) and despawns the sprite.
|
||||
* **`RockSirloin`**: Moves the sprite. It checks Link's Glove flag (`$7EF354`). If Link has the Glove, it checks for player contact. If touched, it handles interaction with thrown sprites (`JSL ThrownSprite_TileAndSpriteInteraction_long`), increments the `RockMeat` custom item count, and despawns the sprite.
|
||||
|
||||
```asm
|
||||
Sprite_Collectible_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw Pineapple
|
||||
dw Seashell
|
||||
dw SwordShield
|
||||
dw RockSirloin
|
||||
|
||||
Pineapple:
|
||||
{
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_CheckDamageToPlayer : BCC +
|
||||
LDA.l Pineapples : INC A : STA.l Pineapples
|
||||
STZ.w SprState, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
Seashell:
|
||||
{
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_CheckDamageToPlayer : BCC +
|
||||
LDA.l Seashells : INC A : STA.l Seashells
|
||||
STZ.w SprState, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
SwordShield:
|
||||
{
|
||||
%PlayAnimation(0,0,1)
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_CheckDamageToPlayer : BCC +
|
||||
LDY.b #$00 : STZ $02E9
|
||||
JSL Link_ReceiveItem
|
||||
STZ.w SprState, X
|
||||
+
|
||||
RTS
|
||||
}
|
||||
|
||||
RockSirloin:
|
||||
{
|
||||
JSL Sprite_Move
|
||||
LDA.l $7EF354 : BEQ .do_you_even_lift_bro
|
||||
JSL Sprite_CheckDamageToPlayer : BCC +
|
||||
JSL ThrownSprite_TileAndSpriteInteraction_long
|
||||
LDA.l RockMeat : INC A : STA.l RockMeat
|
||||
STZ.w SprState, X
|
||||
+
|
||||
.do_you_even_lift_bro
|
||||
RTS
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_Pineapple_Draw`, `Sprite_SwordShield_Draw`, `Sprite_RockSirloin_Draw`)
|
||||
Each collectible type has its own dedicated drawing routine. These routines handle OAM allocation and animation, and explicitly use `REP #$20` and `SEP #$20` for 16-bit coordinate calculations. Each routine contains its own specific OAM data for rendering the respective item.
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Item Collectible**: A single sprite definition (`!SPRID = $52`) is used to represent multiple distinct collectible items, with their specific appearance and behavior determined by `SprAction, X` and `AreaIndex`. This allows for efficient reuse of sprite slots for various in-game items.
|
||||
* **Context-Sensitive Spawning/Drawing**: The sprite's initial appearance and drawing routine are dynamically selected based on the `AreaIndex`, enabling specific items to appear in designated locations within the game world.
|
||||
* **Item Granting**: Collectibles grant items to Link upon contact, directly influencing his inventory and progression.
|
||||
* **Quest Progression Integration**: The Sword/Shield collectible despawns if Link already possesses the Sword, and the Rock Sirloin requires Link to have the Glove to interact with it, integrating these items into the game's quest and progression systems.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,147 +0,0 @@
|
||||
# Deku Leaf / Whirlpool
|
||||
|
||||
## Overview
|
||||
The `deku_leaf.asm` file defines a versatile sprite (`!SPRID = Sprite_DekuLeaf`) that can function as two distinct in-game objects: the "Deku Leaf" and a "Whirlpool." Its specific behavior and visual representation are dynamically determined by the current `AreaIndex`, allowing it to serve different purposes in various locations.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_DekuLeaf` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `00` (Graphics are handled externally or as a background)
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `$0D`
|
||||
* **`!Persist`**: `01` (Continues to live off-screen)
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_DekuLeaf_Long`)
|
||||
This routine acts as a dispatcher for drawing, selecting `Sprite_Whirlpool_Draw` if `AreaIndex` is `$3D` (Whirlpool area), and `Sprite_DekuLeaf_Draw` otherwise. It also dispatches to the main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_DekuLeaf_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA $8A : CMP.b #$3D : BEQ .whirlpool
|
||||
JSR Sprite_DekuLeaf_Draw
|
||||
JMP +
|
||||
.whirlpool
|
||||
JSR Sprite_Whirlpool_Draw
|
||||
+
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_DekuLeaf_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_DekuLeaf_Prep`)
|
||||
This routine initializes the sprite upon spawning. If `AreaIndex` is `$3D` (Whirlpool area), it sets `SprAction, X` to `$01` (`Whirlpool_Main`), indicating its role as a whirlpool.
|
||||
|
||||
```asm
|
||||
Sprite_DekuLeaf_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA $8A : CMP.b #$3D : BNE .not_whirlpool
|
||||
LDA.b #$01 : STA.w SprAction, X
|
||||
.not_whirlpool
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic & State Machine (`Sprite_DekuLeaf_Main`)
|
||||
This routine manages the behavior of both the Deku Leaf and the Whirlpool through a jump table based on `SprAction, X`:
|
||||
|
||||
* **`WaitForPlayer` (Deku Leaf)**: Plays an idle animation. It checks if Link is on the leaf (`JSR CheckIfPlayerIsOn`). If so, it sets a flag (`$71`) and, if Link is in Minish form, spawns a poof garnish. Otherwise, it clears the flag.
|
||||
* **`Whirlpool_Main`**: Plays an animation. If Link is on the whirlpool and the underwater flag (`$0AAB`) is set, it resets various Link state flags (`$55`, `$0AAB`, `$0351`, `$037B`, `$02B2`). If Link's state is not `$0B` (Mirror), it saves Link's coordinates and sets `GameMode` to `$23` to initiate a warp, similar to the Mirror effect.
|
||||
|
||||
```asm
|
||||
Sprite_DekuLeaf_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw WaitForPlayer
|
||||
dw Whirlpool_Main
|
||||
|
||||
WaitForPlayer:
|
||||
{
|
||||
%StartOnFrame(0)
|
||||
%PlayAnimation(0, 0, 10)
|
||||
|
||||
JSR CheckIfPlayerIsOn : BCC +
|
||||
LDA.b #$01 : STA.b $71
|
||||
LDA.w $02B2 : CMP.b #$01 : BNE ++
|
||||
JSL Sprite_SpawnPoofGarnish
|
||||
++
|
||||
RTS
|
||||
+
|
||||
STZ.b $71
|
||||
RTS
|
||||
}
|
||||
|
||||
Whirlpool_Main:
|
||||
{
|
||||
%PlayAnimation(0, 2, 10)
|
||||
JSR CheckIfPlayerIsOn : BCC .not_on
|
||||
|
||||
LDA $0AAB : BEQ .not_on
|
||||
|
||||
STZ $55 ; Reset cape flag
|
||||
STZ $0AAB ; Reset underwater flag
|
||||
STZ $0351 ; Reset ripple flag
|
||||
STZ $037B ; Reset invincibility flag
|
||||
STZ $02B2 ; Reset mask flag
|
||||
|
||||
LDA.b $10 : CMP.b #$0B : BEQ .exit
|
||||
LDA.b $8A : AND.b #$40 : STA.b $7B : BEQ .no_mirror_portal
|
||||
LDA.b $20 : STA.w $1ADF
|
||||
LDA.b $21 : STA.w $1AEF
|
||||
LDA.b $22 : STA.w $1ABF
|
||||
LDA.b $23 : STA.w $1ACF
|
||||
.no_mirror_portal
|
||||
LDA.b #$23
|
||||
|
||||
#SetGameModeLikeMirror:
|
||||
STA.b $11
|
||||
STZ.w $03F8
|
||||
LDA.b #$01 : STA.w $02DB
|
||||
STZ.b $B0
|
||||
STZ.b $27 : STZ.b $28
|
||||
LDA.b #$14 : STA.b $5D
|
||||
|
||||
.not_on
|
||||
.exit
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_DekuLeaf_Draw` and `Sprite_Whirlpool_Draw`)
|
||||
Each object type has its own dedicated drawing routine. These routines handle OAM allocation and animation, and explicitly use `REP #$20` and `SEP #$20` for 16-bit coordinate calculations. Each routine contains its own specific OAM data for rendering the respective object.
|
||||
|
||||
## Design Patterns
|
||||
* **Multi-Object Sprite (Conditional Drawing/Logic)**: A single sprite definition (`Sprite_DekuLeaf`) is used to represent two distinct objects (Deku Leaf and Whirlpool) based on `AreaIndex`, showcasing efficient resource utilization and varied functionality.
|
||||
* **Context-Sensitive Behavior**: The sprite's behavior changes entirely based on the `AreaIndex`, allowing it to function as a traversal item (Deku Leaf) or a warp point (Whirlpool), adapting to different game contexts.
|
||||
* **Player Interaction**: The Deku Leaf allows Link to stand on it for traversal, while the Whirlpool provides a warp mechanism, both offering unique forms of player interaction.
|
||||
* **Game State Manipulation**: The Whirlpool modifies various Link state flags to initiate a warp, demonstrating direct control over the player's game state during transitions.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,208 +0,0 @@
|
||||
# Ice Block (Pushable)
|
||||
|
||||
## Overview
|
||||
The Ice Block sprite (`!SPRID = $D5`) is an interactive object designed as a puzzle element that Link can push. It features complex logic for detecting Link's push, applying movement with momentum, and interacting with switch tiles. This sprite is impervious to most attacks and behaves like a solid statue.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `$D5` (Vanilla sprite ID, likely for a pushable block)
|
||||
* **`!NbrTiles`**: `02`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `01` (Impervious to all attacks)
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `09`
|
||||
* **`!Persist`**: `00`
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `01` (Behaves like a solid statue)
|
||||
* **`!DeflectProjectiles`**: `01` (Deflects all projectiles)
|
||||
* **`!ImperviousArrow`**: `01` (Impervious to arrows)
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Constants
|
||||
* **`!ICE_BLOCK_SPEED`**: `16` (Speed at which the ice block moves when pushed)
|
||||
* **`!PUSH_CONFIRM_FRAMES`**: `10` (Number of frames Link must maintain a push for it to be confirmed)
|
||||
* **`!ALIGN_TOLERANCE`**: `4` (Pixel tolerance for Link's alignment with the block)
|
||||
* **`!WRAM_FLAG_0642`**: `$0642` (WRAM address for a flag related to switch activation)
|
||||
* **`!WRAM_TILE_ATTR`**: `$0FA5` (WRAM address for tile attributes)
|
||||
* **`!SPRITE_LOOP_MAX`**: `$0F` (Max index for sprite loops)
|
||||
* **`!SPRITE_TYPE_STATUE`**: `$1C` (Sprite ID for a generic statue)
|
||||
* **`!SPRITE_STATE_ACTIVE`**: `$09` (Sprite state for active sprites)
|
||||
* **`!TILE_ATTR_ICE`**: `$0E` (Tile attribute for ice, currently unused)
|
||||
* **`!SWITCH_TILE_ID_1` to `!SWITCH_TILE_ID_4`**: IDs for various switch tiles.
|
||||
* **`!SWITCH_TILE_COUNT_MINUS_1`**: `$03`
|
||||
|
||||
## Main Structure (`Sprite_IceBlock_Long`)
|
||||
This routine handles the Ice Block's drawing and dispatches to its main logic if active. It also manages Link's interaction with the block, setting Link's speed and actions when pushing.
|
||||
|
||||
```asm
|
||||
Sprite_IceBlock_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
|
||||
LDA.w SprMiscC, X : BEQ .not_being_pushed
|
||||
STZ.w SprMiscC, X
|
||||
STZ.b LinkSpeedTbl
|
||||
STZ.b $48 ; Clear push actions bitfield
|
||||
.not_being_pushed
|
||||
|
||||
LDA.w SprTimerA, X : BEQ .retain_momentum
|
||||
LDA.b #$01 : STA.w SprMiscC, X
|
||||
LDA.b #$84 : STA.b $48 ; Set statue and push block actions
|
||||
LDA.b #$04 : STA.b LinkSpeedTbl ; Slipping into pit speed
|
||||
.retain_momentum
|
||||
|
||||
JSR Sprite_IceBlock_Draw
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_IceBlock_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_IceBlock_Prep`)
|
||||
This routine initializes the Ice Block upon spawning. It caches the sprite's initial position in `SprMiscD, X` through `SprMiscG, X`. It sets `SprDefl, X` to `$04` (designating it as a pushable statue) and initializes `SprMiscB, X` to `0` (movement state).
|
||||
|
||||
```asm
|
||||
Sprite_IceBlock_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
; Cache Sprite position
|
||||
LDA.w SprX, X : STA.w SprMiscD, X
|
||||
LDA.w SprY, X : STA.w SprMiscE, X
|
||||
LDA.w SprXH, X : STA.w SprMiscF, X
|
||||
LDA.w SprYH, X : STA.w SprMiscG, X
|
||||
|
||||
LDA.b #$04 : STA.w SprDefl, X ; Set as pushable statue
|
||||
|
||||
LDA.w SprHitbox, X : ORA.b #$09 : STA.w SprHitbox, X
|
||||
; Initialize movement state tracking
|
||||
STZ.w SprMiscB, X ; Clear movement state
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Main Logic (`Sprite_IceBlock_Main`)
|
||||
This routine manages the Ice Block's behavior, including push detection, movement, and interaction with switches.
|
||||
|
||||
* **Animation**: Plays a static animation (`%PlayAnimation(0, 0, 1)`).
|
||||
* **Sprite-to-Sprite Collision**: Calls `IceBlock_HandleSpriteToSpriteCollision` to manage interactions with other sprites.
|
||||
* **Damage Reaction**: If the block takes damage, its position, speed, and movement state are reset.
|
||||
* **Switch Detection**: Calls `Sprite_IceBlock_CheckForSwitch`. If the block is on a switch, it stops movement, sets `!WRAM_FLAG_0642` to `01`, and resets its movement state.
|
||||
* **Push Logic**: This is a core part of the routine. If the block is not moving, it checks if Link is in contact and correctly aligned (`IceBlock_CheckLinkPushAlignment`). If so, a push timer (`SprTimerA, X`) is initiated. If the timer expires while Link is still pushing, the block snaps to the grid, applies push speed (`Sprite_ApplyPush`), and begins moving. If the block is already moving, it continues to move (`JSL Sprite_Move`) and checks for tile collisions, stopping if an obstacle is encountered.
|
||||
|
||||
```asm
|
||||
Sprite_IceBlock_Main:
|
||||
{
|
||||
%PlayAnimation(0, 0, 1)
|
||||
|
||||
JSR IceBlock_HandleSpriteToSpriteCollision ; Renamed from Statue_BlockSprites
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .no_damage
|
||||
LDA.w SprMiscD, X : STA.w SprX, X
|
||||
LDA.w SprY, X : STA.w SprY, X
|
||||
LDA.w SprXH, X : STA.w SprXH, X
|
||||
LDA.w SprYH, X : STA.w SprYH, X
|
||||
STZ.w SprXSpeed, X : STZ.w SprYSpeed, X
|
||||
STZ.w SprTimerA, X : STZ.w SprMiscA, X
|
||||
STZ.w SprMiscB, X ; Reset movement state when hit
|
||||
.no_damage
|
||||
|
||||
STZ.w !WRAM_FLAG_0642
|
||||
JSR Sprite_IceBlock_CheckForSwitch : BCC .no_switch
|
||||
STZ.w SprXSpeed, X : STZ.w SprYSpeed, X
|
||||
LDA.b #$01 : STA.w !WRAM_FLAG_0642
|
||||
STZ.w SprMiscB, X ; Reset movement state when hitting switch
|
||||
.no_switch
|
||||
|
||||
; If the block is currently moving, apply movement and check for collisions
|
||||
LDA.w SprMiscB, X
|
||||
BNE .block_is_moving
|
||||
|
||||
; --- Block is NOT moving, check for push initiation ---
|
||||
JSL Sprite_CheckDamageToPlayerSameLayer : BCC .NotInContact
|
||||
; Link is in contact. Now check if he's properly aligned and facing the block.
|
||||
JSR IceBlock_CheckLinkPushAlignment
|
||||
BCC .NotInContact ; Link is not aligned or facing correctly.
|
||||
|
||||
; Link is aligned and facing the block. Start or continue the push timer.
|
||||
LDA.w SprTimerA, X
|
||||
BNE .timer_is_running ; Timer already started, let it count down.
|
||||
|
||||
; Start the timer for the first time.
|
||||
LDA.b #!PUSH_CONFIRM_FRAMES
|
||||
STA.w SprTimerA, X
|
||||
RTS ; Wait for next frame
|
||||
|
||||
.timer_is_running
|
||||
; Timer is running. Has it reached zero? (SprTimerA is decremented by engine)
|
||||
LDA.w SprTimerA, X
|
||||
BNE .NotInContact ; Not zero yet, keep waiting.
|
||||
|
||||
; --- PUSH CONFIRMED ---
|
||||
; Timer reached zero while still in contact and aligned.
|
||||
; Snap to grid before setting speed for clean movement.
|
||||
LDA.w SprX, X : AND.b #$F8 : STA.w SprX, X
|
||||
LDA.w SprY, X : AND.b #$F8 : STA.w SprY, X
|
||||
|
||||
JSR Sprite_ApplyPush ; Set speed based on Link's direction.
|
||||
LDA.b #$01 : STA.w SprMiscB, X ; Set "is moving" flag.
|
||||
RTS
|
||||
|
||||
.NotInContact
|
||||
; No contact or improper alignment, reset push timer.
|
||||
STZ.w SprTimerA, X
|
||||
RTS
|
||||
|
||||
.block_is_moving
|
||||
JSL Sprite_Move
|
||||
JSL Sprite_Get_16_bit_Coords
|
||||
JSL Sprite_CheckTileCollision
|
||||
; ----udlr , u = up, d = down, l = left, r = right
|
||||
LDA.w SprCollision, X : AND.b #$0F : BEQ + ; If no collision, continue moving
|
||||
STZ.w SprXSpeed, X : STZ.w SprYSpeed, X ; Stop movement
|
||||
STZ.w SprMiscB, X ; Reset movement state
|
||||
+
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## `IceBlock_CheckLinkPushAlignment`
|
||||
This complex routine precisely determines if Link is correctly aligned and facing the ice block to initiate a push. It calculates the relative positions of Link and the block, considers Link's facing direction, and uses `!ALIGN_TOLERANCE` to allow for slight pixel variations. It returns with the carry flag set for success or clear for failure.
|
||||
|
||||
## `Sprite_ApplyPush`
|
||||
This routine sets the Ice Block's `SprXSpeed, X` or `SprYSpeed, X` based on Link's facing direction (`SprMiscA, X`) and the predefined `!ICE_BLOCK_SPEED`.
|
||||
|
||||
## `IceBlock_CheckForGround`
|
||||
This routine is currently unused but was intended to check if the tile beneath the sprite was a sliding ice tile.
|
||||
|
||||
## `Sprite_IceBlock_CheckForSwitch`
|
||||
This routine checks if any of the four corners of the Ice Block are currently positioned on a switch tile (identified by `!SWITCH_TILE_ID_1` to `!SWITCH_TILE_ID_4`). It returns with the carry flag set if any corner is on a switch tile.
|
||||
|
||||
## `IceBlock_HandleSpriteToSpriteCollision`
|
||||
This routine (renamed from `Statue_BlockSprites`) manages collisions between the Ice Block and other active sprites. It iterates through other sprites, checks for collision, and applies recoil or other effects to them.
|
||||
|
||||
## Drawing (`Sprite_IceBlock_Draw`)
|
||||
This routine handles OAM allocation and animation for the Ice Block. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
## Design Patterns
|
||||
* **Interactive Puzzle Element**: The Ice Block is a core interactive puzzle element that Link can manipulate by pushing, requiring precise player input and environmental interaction.
|
||||
* **Precise Collision and Alignment Detection**: Implements detailed logic to ensure Link is correctly positioned and facing the block before a push is registered, providing a robust and fair interaction mechanism.
|
||||
* **Movement with Momentum**: The block retains momentum after being pushed, sliding across the terrain until it encounters an obstacle, adding a realistic physics element.
|
||||
* **Switch Activation**: The block can activate switch tiles upon contact, integrating it into environmental puzzles and triggering game events.
|
||||
* **Sprite-to-Sprite Collision**: Handles interactions with other sprites, applying recoil effects to them, demonstrating complex inter-sprite dynamics.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,166 +0,0 @@
|
||||
# Mine Switch
|
||||
|
||||
## Overview
|
||||
The Mine Switch sprite (`!SPRID = Sprite_Mineswitch`) is an interactive puzzle element, typically found in the Goron Mines. It functions as a lever-style switch that Link can activate by attacking it, altering the state of minecart tracks or other game elements. This sprite supports both a regular on/off switch and a speed-controlling switch, with its behavior and appearance changing based on its current state.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_Mineswitch` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `02`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `01`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `01` (Continues to live off-screen)
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `01`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_LeverSwitch_Long`)
|
||||
This routine handles the Mine Switch's drawing and dispatches to its main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_LeverSwitch_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_LeverSwitch_Draw
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_LeverSwitch_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_LeverSwitch_Prep`)
|
||||
This routine initializes the Mine Switch upon spawning. It sets `SprDefl, X` to `0`. It retrieves the switch's on/off state from `SwitchRam`, indexed by `SprSubtype, X`, and sets `SprAction, X` and `SprFrame, X` accordingly. It also sets `SprTileDie, X` to `0` and `SprBulletproof, X` to `0`.
|
||||
|
||||
```asm
|
||||
Sprite_LeverSwitch_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
|
||||
LDA.b #$00 : STA.w SprDefl, X
|
||||
|
||||
; Get the subtype of the switch so that we can get its on/off state.
|
||||
LDA.w SprSubtype, X : TAY
|
||||
|
||||
LDA.w SwitchRam, Y : STA.w SprAction, X : STA.w SprFrame, X
|
||||
LDA.b #$00 : STA.w SprTileDie, X
|
||||
STZ.w SprBulletproof, X
|
||||
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Constants
|
||||
* **`SwitchRam = $0230`**: A WRAM address that stores the state (on/off) of each individual switch, indexed by its `SprSubtype`.
|
||||
|
||||
## Main Logic & State Machine (`Sprite_LeverSwitch_Main`)
|
||||
This routine manages the Mine Switch's behavior through a jump table, supporting different types of switches:
|
||||
|
||||
* **Player Collision**: Prevents Link from passing through the switch (`JSL Sprite_PlayerCantPassThrough`).
|
||||
* **`SwitchOff`**: Plays an animation. If Link attacks it (`JSL Sprite_CheckDamageFromPlayer`) and a timer (`SprTimerA, X`) allows, it plays a sound (`$25`), turns the switch on (`STA.w SwitchRam, Y` to `01`), sets a timer, and transitions to `SwitchOn`.
|
||||
* **`SwitchOn`**: Plays an animation. If Link attacks it and a timer allows, it plays a sound (`$25`), turns the switch off (`STA.w SwitchRam, Y` to `00`), sets a timer, and transitions to `SwitchOff`.
|
||||
* **`SpeedSwitchOff`**: Plays an animation. If Link attacks it, it plays a sound (`$25`), sets `$36` to `01` (likely a global speed flag for minecarts), and transitions to `SpeedSwitchOn`.
|
||||
* **`SpeedSwitchOn`**: Plays an animation. If Link attacks it, it plays a sound (`$25`), clears `$36`, and transitions to `SpeedSwitchOff`.
|
||||
|
||||
```asm
|
||||
Sprite_LeverSwitch_Main:
|
||||
{
|
||||
JSL Sprite_PlayerCantPassThrough
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL UseImplicitRegIndexedLocalJumpTable
|
||||
|
||||
dw SwitchOff
|
||||
dw SwitchOn
|
||||
dw SpeedSwitchOff
|
||||
dw SpeedSwitchOn
|
||||
|
||||
SwitchOff:
|
||||
{
|
||||
%PlayAnimation(0,0,4)
|
||||
LDA.w SprTimerA, X : BNE .NoDamage
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .NoDamage
|
||||
LDA #$25 : STA $012F
|
||||
|
||||
; Get the subtype of the switch so that we can get its on/off state.
|
||||
LDA.w SprSubtype, X : TAY
|
||||
|
||||
; Turn the switch on.
|
||||
LDA #$01 : STA.w SwitchRam, Y
|
||||
LDA #$10 : STA.w SprTimerA, X
|
||||
%GotoAction(1)
|
||||
.NoDamage
|
||||
RTS
|
||||
}
|
||||
|
||||
SwitchOn:
|
||||
{
|
||||
%PlayAnimation(1,1,4)
|
||||
LDA.w SprTimerA, X : BNE .NoDamage
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .NoDamage
|
||||
LDA #$25 : STA $012F
|
||||
|
||||
; Get the subtype of the switch so that we can get its on/off state.
|
||||
LDA.w SprSubtype, X : TAY
|
||||
|
||||
; Turn the switch off.
|
||||
LDA #$00 : STA.w SwitchRam, Y
|
||||
LDA #$10 : STA.w SprTimerA, X
|
||||
%GotoAction(0)
|
||||
.NoDamage
|
||||
RTS
|
||||
}
|
||||
|
||||
SpeedSwitchOff:
|
||||
{
|
||||
%PlayAnimation(0,0,4)
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .NoDamage
|
||||
LDA.b #$25 : STA $012F
|
||||
LDA.b #$01 : STA $36
|
||||
%GotoAction(3)
|
||||
.NoDamage
|
||||
RTS
|
||||
}
|
||||
|
||||
SpeedSwitchOn:
|
||||
{
|
||||
%PlayAnimation(1,1,4)
|
||||
JSL Sprite_CheckDamageFromPlayer : BCC .NoDamage
|
||||
LDA #$25 : STA $012F
|
||||
STZ.w $36
|
||||
%GotoAction(2)
|
||||
.NoDamage
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_LeverSwitch_Draw`)
|
||||
This routine handles OAM allocation and animation for the Mine Switch. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
## Design Patterns
|
||||
* **Interactive Puzzle Element**: The Mine Switch is a key interactive puzzle element that Link can activate by attacking it, triggering changes in the game environment.
|
||||
* **State-Based Behavior**: The switch has distinct "on" and "off" states, with different animations and effects, providing clear visual feedback to the player.
|
||||
* **Subtype-Driven State**: The `SprSubtype` is used to index into `SwitchRam`, allowing each individual switch to maintain its own independent state, enabling complex puzzle designs with multiple switches.
|
||||
* **Speed Control**: The "Speed Switch" variant directly controls a global speed flag (`$36`), likely affecting the speed of minecarts or other moving objects, adding another layer of interaction to the minecart system.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,113 +0,0 @@
|
||||
# Minecart
|
||||
|
||||
## Overview
|
||||
The Minecart sprite (`!SPRID = Sprite_Minecart`) is a highly complex and interactive object primarily used in the Goron Mines. It allows Link to ride it through a network of tracks, with its movement dictated by various track tile types, player input, and seamless dungeon transitions. The Minecart system features persistent state across rooms and intricate collision detection.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_Minecart` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `08`
|
||||
* **`!Harmless`**: `01`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `01` (Impervious to all attacks)
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `14`
|
||||
* **`!Persist`**: `01` (Continues to live off-screen)
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Constants
|
||||
* **`!LinkInCart`**: `$35` (Flag indicating Link is currently riding the Minecart)
|
||||
* **`!MinecartSpeed`**: `20` (Normal movement speed)
|
||||
* **`!DoubleSpeed`**: `30` (Faster movement speed, possibly for boosts)
|
||||
* **Directions**: `North`, `East`, `South`, `West` (Used for `!MinecartDirection` and `SprMiscB`)
|
||||
* **Sprite Facing Directions**: `Up`, `Down`, `Left`, `Right` (Used for `!SpriteDirection`)
|
||||
* **`!MinecartDirection`**: `$0DE0` (Maps to `SprMiscC`, stores the current movement direction)
|
||||
* **`!SpriteDirection`**: `$0DE0` (Stores the sprite's visual facing direction)
|
||||
* **Track Persistence**: A system for saving and loading minecart state across rooms:
|
||||
* **`!MinecartTrackRoom`**: `$0728` (Stores the room ID where a specific track was left)
|
||||
* **`!MinecartTrackX`**: `$0768` (Stores the X position of a track)
|
||||
* **`!MinecartTrackY`**: `$07A8` (Stores the Y position of a track)
|
||||
* **Active Cart Tracking**: Variables to manage the currently active minecart:
|
||||
* **`!MinecartTrackCache`**: `$07E8` (Stores the ID of the track Link is currently on)
|
||||
* **`!MinecartDirectionCache`**: `$07E9` (Stores the direction during room transitions)
|
||||
* **`!MinecartCurrent`**: `$07EA` (Stores the sprite slot index of the current minecart)
|
||||
|
||||
## Collision Setup (Tile Types)
|
||||
Defines various tile types that represent different parts of the minecart track, including straight sections, corners, intersections, stop tiles, and dynamic switch tiles. These are crucial for guiding the minecart's movement and interaction with the environment.
|
||||
|
||||
## Main Structure (`Sprite_Minecart_Long`)
|
||||
This routine handles the Minecart's multi-layered drawing (top and bottom portions) and dispatches to its main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Minecart_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_Minecart_DrawTop ; Draw behind Link
|
||||
JSR Sprite_Minecart_DrawBottom ; Draw in front of Link
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Minecart_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Minecart_Prep`)
|
||||
This routine initializes the Minecart upon spawning. It updates cached coordinates, manages track persistence (initializing track data if not already set), and handles despawning if the cart is not in its designated room or its coordinates don't match. It sets various sprite properties and determines the initial movement direction based on the tile the minecart is placed on.
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Minecart_Main`)
|
||||
This routine manages the Minecart's complex behavior through a state machine:
|
||||
|
||||
* **`Minecart_WaitHoriz` / `Minecart_WaitVert`**: The cart waits in a horizontal or vertical orientation. If Link is on the cart (`CheckIfPlayerIsOn`) and presses the B button, it saves the track ID, cancels Link's dash, sets `LinkSomaria` and `!LinkInCart`, adjusts Link's position, and transitions to a movement state (`Minecart_MoveEast`, `Minecart_MoveWest`, `Minecart_MoveNorth`, `Minecart_MoveSouth`).
|
||||
* **`Minecart_MoveNorth` / `MoveEast` / `MoveSouth` / `MoveWest`**: The cart moves in the specified direction. It plays animations, sets speed (`!MinecartSpeed` or `!DoubleSpeed`), moves the sprite, drags Link along (`JSL DragPlayer`), handles the player camera, and processes track tiles (`HandleTileDirections`).
|
||||
* **`Minecart_Release`**: Stops the cart, releases Link, and transitions back to a `Minecart_Wait` state.
|
||||
|
||||
## Helper Routines
|
||||
* **`HandlePlayerCameraAndMoveCart`**: Manages Link's animation, camera, and plays cart sound effects.
|
||||
* **`StopCart`**: Stops the cart, releases Link, rounds its coordinates, and saves its position to track variables for persistence.
|
||||
* **`InitMovement`**: Caches Link's coordinates for movement calculations.
|
||||
* **`Minecart_SetDirectionNorth` / `East` / `South` / `West`**: Set the cart's direction, animation, and update track caches.
|
||||
* **`HandleTileDirections`**: A crucial routine that checks the tile the minecart is currently on and determines its next action, handling out-of-bounds, stop tiles, player input at intersections, corner tiles, and dynamic switch tiles.
|
||||
* **`CheckForOutOfBounds`**: Determines if the cart is on an out-of-bounds tile.
|
||||
* **`CheckForStopTiles`**: Checks for stop tiles and sets the cart's next direction.
|
||||
* **`CheckForPlayerInput`**: Detects player input on intersection tiles to allow Link to choose the cart's direction.
|
||||
* **`CheckForCornerTiles`**: Handles direction changes when the cart encounters corner tiles.
|
||||
* **`HandleDynamicSwitchTileDirections`**: Manages movement on dynamic switch tiles, which can alter the cart's path.
|
||||
* **`CheckTrackSpritePresence`**: Checks for the presence and collision of a `Sprite $B0` (Switch Track) with the minecart.
|
||||
* **`CheckIfPlayerIsOn`**: Determines if Link is overlapping the minecart.
|
||||
* **`ResetTrackVars`**: Resets all minecart track-related variables.
|
||||
* **`Minecart_HandleToss` / `Minecart_HandleTossedCart` / `Minecart_HandleLiftAndToss`**: Routines for handling the minecart being tossed or lifted by Link.
|
||||
|
||||
## Drawing (`Sprite_Minecart_DrawTop` and `Sprite_Minecart_DrawBottom`)
|
||||
These routines draw the Minecart in two separate portions (top and bottom) to create the illusion of Link riding inside it. They utilize `JSL Sprite_PrepOamCoord` and `OAM_AllocateFromRegionB`/`C` for OAM allocation, and explicitly use `REP #$20` and `SEP #$20` for 16-bit coordinate calculations.
|
||||
|
||||
## Vanilla Overrides
|
||||
* **`RoomTag_ShutterDoorRequiresCart`**: Modifies a room tag to require Link to be in a cart to open a shutter door, integrating the Minecart into dungeon puzzles.
|
||||
* **`org $028260`**: Injects `JSL ResetTrackVars` to ensure minecart track variables are reset at specific points.
|
||||
|
||||
## Design Patterns
|
||||
* **Complex Interactive Object**: The Minecart is a highly interactive object with intricate movement, collision, and state management, providing a unique traversal mechanic.
|
||||
* **Track-Based Movement**: Movement is precisely governed by specific track tile types (stops, corners, intersections), requiring careful design of the minecart routes.
|
||||
* **Player Input for Direction**: Link can influence the minecart's direction at intersections, adding an element of player control to the ride.
|
||||
* **Persistent State**: Minecart position and direction are saved across room transitions, ensuring continuity and allowing for complex multi-room puzzles.
|
||||
* **Multi-Part Drawing**: The minecart is drawn in two separate parts to allow Link to appear "inside" it, enhancing visual immersion.
|
||||
* **Player State Manipulation**: The minecart directly controls Link's state (`!LinkInCart`, `LinkSomaria`, `LinkState`), seamlessly integrating the ride into Link's overall actions.
|
||||
* **Dynamic Room Transitions**: Handles seamless transitions between rooms while Link is in the minecart, maintaining the flow of gameplay.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,76 +0,0 @@
|
||||
# Pedestal (Magic Pedestal Plaque)
|
||||
|
||||
## Overview
|
||||
The `pedestal.asm` file defines the custom behavior for a "Magic Pedestal" sprite, which is an interactive object that responds to Link's actions. This implementation overrides a vanilla pedestal plaque sprite (`Sprite_B3_PedestalPlaque`) to trigger specific game events based on Link's inventory, input, and the current `AreaIndex`.
|
||||
|
||||
## Vanilla Overrides
|
||||
* **`org $1EE05F`**: Injects `JSL CheckForBook` into the `Sprite_B3_PedestalPlaque` routine. This means the custom logic defined in `CheckForBook` will execute when the vanilla pedestal plaque sprite is processed.
|
||||
|
||||
## `CheckForBook`
|
||||
This routine is the primary entry point for the custom pedestal logic. It checks several conditions to determine if Link is interacting with the pedestal in a specific way:
|
||||
|
||||
* **Link's Action**: Checks `$2F` (Link's current action/state).
|
||||
* **Player Contact**: Checks for damage to player (`JSL Sprite_CheckDamageToPlayer`), which in this context likely means Link is in contact with the pedestal.
|
||||
* **Item Held**: Checks if Link is holding a specific item (`$0202` compared to `$0F`, which likely corresponds to a book item).
|
||||
* **Player Input**: Checks if Link is pressing the Y button (`BIT.b $F4`).
|
||||
* **State Manipulation**: If Link is holding the book and pressing Y, it sets `$0300` to `0`, `$037A` to `$20`, and `$012E` to `0` (these are likely related to Link's animation or state changes).
|
||||
* **Event Trigger**: Calls `JSR PedestalPlaque` to execute area-specific logic.
|
||||
|
||||
```asm
|
||||
CheckForBook:
|
||||
{
|
||||
LDA.b $2F : BNE .exit
|
||||
JSL Sprite_CheckDamageToPlayer : BCC .exit
|
||||
LDA.w $0202 : CMP.b #$0F : BNE .not_holding_book
|
||||
LDY.b #$01 : BIT.b $F4 : BVS .not_pressing_y
|
||||
.not_holding_book
|
||||
LDY.b #$00
|
||||
.not_pressing_y
|
||||
CPY.b #$01 : BNE .no_book_pose
|
||||
STZ.w $0300
|
||||
LDA.b #$20
|
||||
STA.w $037A
|
||||
STZ.w $012E
|
||||
.no_book_pose
|
||||
JSR PedestalPlaque
|
||||
.exit
|
||||
LDA.b AreaIndex : CMP.b #$30
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## `PedestalPlaque`
|
||||
This routine contains the area-specific logic for the pedestal, triggering different events based on the current `AreaIndex`:
|
||||
|
||||
* **Zora Temple (`AreaIndex = $1E`)**: Checks a flag (`$7EF29E` bit `$20`) and `SongFlag` (`$03`). If specific conditions are met (e.g., a certain event has not occurred and a particular song has been played), it sets `$04C6` to `$01` (likely a flag to open a gate or trigger an event) and clears `SongFlag`.
|
||||
* **Goron Desert (`AreaIndex = $36`)**: No specific logic defined in this file.
|
||||
* **Fortress Secrets (`AreaIndex = $5E`)**: No specific logic defined in this file.
|
||||
|
||||
```asm
|
||||
PedestalPlaque:
|
||||
{
|
||||
LDA.b AreaIndex : CMP.b #$1E : BEQ .zora_temple
|
||||
CMP.b #$36 : BEQ .goron_desert
|
||||
CMP.b #$5E : BEQ .fortress_secrets
|
||||
JMP .return
|
||||
.zora_temple
|
||||
|
||||
LDA.l $7EF29E : AND.b #$20 : BNE .return
|
||||
LDA.b SongFlag : CMP.b #$03 : BNE .return
|
||||
LDA.b #$01 : STA $04C6
|
||||
STZ.b SongFlag
|
||||
JMP .return
|
||||
.goron_desert
|
||||
|
||||
.fortress_secrets
|
||||
|
||||
.return
|
||||
RTS
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Vanilla Override**: This file directly modifies the vanilla pedestal plaque sprite to implement custom interactive behavior, demonstrating how to integrate new puzzle mechanics into existing game elements.
|
||||
* **Context-Sensitive Interaction**: The pedestal responds specifically when Link is holding a particular item (a book) and pressing a button, creating a unique and logical interaction for puzzle solving.
|
||||
* **Quest Progression Integration**: The pedestal triggers events based on the `AreaIndex` and various game state flags (e.g., `SongFlag`, `$7EF29E`), indicating its role in advancing specific quests and unlocking new areas.
|
||||
* **Game State Manipulation**: Directly modifies WRAM addresses (`$04C6`, `SongFlag`) to trigger game events, such as opening gates or clearing flags, which are crucial for puzzle resolution and progression.
|
||||
@@ -1,281 +0,0 @@
|
||||
# Portal Sprite
|
||||
|
||||
## Overview
|
||||
The Portal sprite (`!SPRID = Sprite_Portal`) implements a sophisticated two-way warping system within the game. It allows Link to instantly travel between designated Blue and Orange portals, which can be placed in both dungeons and the overworld. This sprite features complex logic for portal activation, collision detection with Link, and seamless management of Link's state during warps.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_Portal` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `01`
|
||||
* **`!Harmless`**: `00`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `00`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `01` (Continues to live off-screen)
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_Portal_Long`)
|
||||
This routine handles the Portal's drawing and dispatches to its main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_Portal_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_Portal_Draw
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_Portal_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_Portal_Prep`)
|
||||
This routine initializes the Portal upon spawning. It sets `SprDefl, X` to `0` (ensuring persistence outside the camera view), modifies `SprHitbox, X` properties, sets `SprTileDie, X` to `0`, and makes the portal bulletproof (`SprBulletproof, X` to `$FF`).
|
||||
|
||||
```asm
|
||||
Sprite_Portal_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
; Persist outside of camera
|
||||
LDA #$00 : STA.w SprDefl, X
|
||||
LDA.w SprHitbox, X : AND.b #$C0 : STA.w SprHitbox, X
|
||||
STZ.w SprTileDie, X
|
||||
LDA.b #$FF : STA.w SprBulletproof, X
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Portal Data Memory Locations
|
||||
* **`BluePortal_X`, `BluePortal_Y`, `OrangePortal_X`, `OrangePortal_Y`**: WRAM addresses storing the X and Y coordinates of the Blue and Orange portals, respectively.
|
||||
* **`BlueActive`, `OrangeActive`**: Flags indicating whether the Blue and Orange portals are currently active.
|
||||
* **`OrangeSpriteIndex`, `BlueSpriteIndex`**: Store the sprite indices of the Orange and Blue portals.
|
||||
|
||||
## Main Logic & State Machine (`Sprite_Portal_Main`)
|
||||
This routine manages the various states and behaviors of the portals, including their creation, activation, and warping functionality.
|
||||
|
||||
* **`StateHandler`**: Calls `CheckForDismissPortal` and `RejectOnTileCollision`. It then checks `$7E0FA6` (likely a flag indicating which portal is being spawned). If `$7E0FA6` is `0`, it sets up an Orange Portal (stores coordinates, sets `SprSubtype, X` to `01`, and transitions to `OrangePortal`). Otherwise, it sets up a Blue Portal (stores coordinates, sets `SprSubtype, X` to `02`, and transitions to `BluePortal`).
|
||||
* **`BluePortal` / `OrangePortal`**: Plays an animation. It checks if Link has been warped (`$11` compared to `$2A`). It then checks for overlap with Link's hitbox (`CheckIfHitBoxesOverlap`). If Link overlaps, it determines if Link is in a dungeon or overworld (`$1B`) and transitions to the appropriate warp state (`BluePortal_WarpDungeon`, `OrangePortal_WarpDungeon`, `BluePortal_WarpOverworld`, `OrangePortal_WarpOverworld`).
|
||||
* **`BluePortal_WarpDungeon` / `OrangePortal_WarpDungeon`**: Warps Link's coordinates (`$20`, `$22`), sets camera scroll boundaries, stores the other portal's coordinates, sets its `SprTimerD, X`, sets `$11` to `$14`, and returns to the respective portal state.
|
||||
* **`BluePortal_WarpOverworld` / `OrangePortal_WarpOverworld`**: Warps Link's coordinates (`$20`, `$22`), sets camera scroll boundaries, applies Link's movement to the camera (`JSL ApplyLinksMovementToCamera`), stores the other portal's coordinates, sets its `SprTimerD, X`, sets `$5D` to `$01`, and returns to the respective portal state.
|
||||
|
||||
```asm
|
||||
Sprite_Portal_Main:
|
||||
{
|
||||
LDA.w SprAction, X
|
||||
JSL JumpTableLocal
|
||||
|
||||
dw StateHandler
|
||||
dw BluePortal
|
||||
dw OrangePortal
|
||||
|
||||
dw BluePortal_WarpDungeon
|
||||
dw OrangePortal_WarpDungeon
|
||||
|
||||
dw BluePortal_WarpOverworld
|
||||
dw OrangePortal_WarpOverworld
|
||||
|
||||
StateHandler:
|
||||
{
|
||||
JSR CheckForDismissPortal
|
||||
JSR RejectOnTileCollision
|
||||
|
||||
LDA $7E0FA6 : BNE .BluePortal
|
||||
LDA #$01 : STA $0307
|
||||
TXA : STA.w OrangeSpriteIndex
|
||||
LDA.w SprY, X : STA.w OrangePortal_X
|
||||
LDA.w SprX, X : STA.w OrangePortal_Y
|
||||
LDA.b #$01 : STA.w SprSubtype, X
|
||||
%GotoAction(2)
|
||||
RTS
|
||||
.BluePortal
|
||||
LDA #$02 : STA $0307
|
||||
TXA : STA.w BlueSpriteIndex
|
||||
LDA.w SprY, X : STA.w BluePortal_X
|
||||
LDA.w SprX, X : STA.w BluePortal_Y
|
||||
LDA.b #$02 : STA.w SprSubtype, X
|
||||
%GotoAction(1)
|
||||
RTS
|
||||
}
|
||||
|
||||
BluePortal:
|
||||
{
|
||||
%StartOnFrame(0)
|
||||
%PlayAnimation(0,1,8)
|
||||
|
||||
LDA $11 : CMP.b #$2A : BNE .not_warped_yet
|
||||
STZ $11
|
||||
.not_warped_yet
|
||||
CLC
|
||||
|
||||
LDA.w SprTimerD, X : BNE .NoOverlap
|
||||
JSL Link_SetupHitBox
|
||||
JSL $0683EA ; Sprite_SetupHitbox_long
|
||||
JSL CheckIfHitBoxesOverlap : BCC .NoOverlap
|
||||
CLC
|
||||
LDA $1B : BEQ .outdoors
|
||||
%GotoAction(3) ; BluePortal_WarpDungeon
|
||||
.NoOverlap
|
||||
RTS
|
||||
|
||||
.outdoors
|
||||
%GotoAction(5) ; BluePortal_WarpOverworld
|
||||
RTS
|
||||
}
|
||||
|
||||
OrangePortal:
|
||||
{
|
||||
%StartOnFrame(2)
|
||||
%PlayAnimation(2,3,8)
|
||||
LDA $11 : CMP.b #$2A : BNE .not_warped_yet
|
||||
STZ $11
|
||||
.not_warped_yet
|
||||
CLC
|
||||
LDA.w SprTimerD, X : BNE .NoOverlap
|
||||
JSL Link_SetupHitBox
|
||||
JSL $0683EA ; Sprite_SetupHitbox_long
|
||||
|
||||
JSL CheckIfHitBoxesOverlap : BCC .NoOverlap
|
||||
CLC
|
||||
; JSL $01FF28 ; Player_CacheStatePriorToHandler
|
||||
|
||||
LDA $1B : BEQ .outdoors
|
||||
%GotoAction(4) ; OrangePortal_WarpDungeon
|
||||
.NoOverlap
|
||||
RTS
|
||||
|
||||
.outdoors
|
||||
%GotoAction(6) ; OrangePortal_WarpOverworld
|
||||
RTS
|
||||
}
|
||||
|
||||
BluePortal_WarpDungeon:
|
||||
{
|
||||
LDA $7EC184 : STA $20
|
||||
LDA $7EC186 : STA $22
|
||||
|
||||
LDA $7EC188 : STA $0600
|
||||
LDA $7EC18A : STA $0604
|
||||
LDA $7EC18C : STA $0608
|
||||
LDA $7EC18E : STA $060C
|
||||
|
||||
PHX
|
||||
LDA.w OrangeSpriteIndex : TAX
|
||||
LDA #$40 : STA.w SprTimerD, X
|
||||
LDA.w SprY, X : STA $7EC184
|
||||
STA.w BluePortal_Y
|
||||
LDA.w SprX, X : STA $7EC186
|
||||
STA.w BluePortal_X
|
||||
PLX
|
||||
|
||||
LDA #$14 : STA $11
|
||||
%GotoAction(1) ; Return to BluePortal
|
||||
RTS
|
||||
}
|
||||
|
||||
OrangePortal_WarpDungeon:
|
||||
{
|
||||
LDA $7EC184 : STA $20
|
||||
LDA $7EC186 : STA $22
|
||||
|
||||
; Camera Scroll Boundaries
|
||||
LDA $7EC188 : STA $0600 ; Small Room North
|
||||
LDA $7EC18A : STA $0604 ; Small Room South
|
||||
LDA $7EC18C : STA $0608 ; Small Room West
|
||||
LDA $7EC18E : STA $060C ; Small Room South
|
||||
|
||||
PHX
|
||||
LDA.w BlueSpriteIndex : TAX
|
||||
LDA #$40 : STA.w SprTimerD, X
|
||||
LDA.w SprY, X : STA $7EC184
|
||||
STA.w OrangePortal_Y
|
||||
LDA.w SprX, X : STA $7EC186
|
||||
STA.w OrangePortal_X
|
||||
PLX
|
||||
|
||||
LDA #$14 : STA $11
|
||||
%GotoAction(2) ; Return to OrangePortal
|
||||
RTS
|
||||
}
|
||||
|
||||
BluePortal_WarpOverworld:
|
||||
{
|
||||
LDA.w OrangePortal_X : STA $20
|
||||
LDA.w OrangePortal_Y : STA $22
|
||||
LDA $7EC190 : STA $0610
|
||||
LDA $7EC192 : STA $0612
|
||||
LDA $7EC194 : STA $0614
|
||||
LDA $7EC196 : STA $0616
|
||||
|
||||
JSL ApplyLinksMovementToCamera
|
||||
|
||||
PHX ; Infinite loop prevention protocol
|
||||
LDA.w OrangeSpriteIndex : TAX
|
||||
LDA #$40 : STA.w SprTimerD, X
|
||||
|
||||
PLX
|
||||
LDA #$01 : STA $5D
|
||||
;LDA #$2A : STA $11
|
||||
%GotoAction(1) ; Return to BluePortal
|
||||
RTS
|
||||
}
|
||||
|
||||
OrangePortal_WarpOverworld:
|
||||
{
|
||||
LDA.w BluePortal_X : STA $20
|
||||
LDA.w BluePortal_Y : STA $22
|
||||
LDA $7EC190 : STA $0610
|
||||
LDA $7EC192 : STA $0612
|
||||
LDA $7EC194 : STA $0614
|
||||
LDA $7EC196 : STA $0616
|
||||
|
||||
JSL ApplyLinksMovementToCamera
|
||||
|
||||
PHX
|
||||
LDA.w BlueSpriteIndex : TAX
|
||||
LDA #$40 : STA.w SprTimerD, X
|
||||
PLX
|
||||
|
||||
LDA #$01 : STA $5D
|
||||
;LDA #$2A : STA $11
|
||||
|
||||
%GotoAction(2) ; Return to BluePortal
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Routines
|
||||
* **`CheckForDismissPortal`**: Checks a ticker (`$06FE`). If it exceeds `02`, it despawns the active portals (Blue and Orange) and decrements the ticker. Otherwise, it increments the ticker. This ticker needs to be reset during room and map transitions.
|
||||
* **`RejectOnTileCollision`**: Checks for tile collision. If a portal is placed on an invalid tile (tile attribute `0` or `48`), it despawns the portal, plays an error sound (`SFX2.3C`), and decrements the ticker (`$06FE`).
|
||||
|
||||
## Drawing (`Sprite_Portal_Draw`)
|
||||
This routine handles OAM allocation and animation for the Portal. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
## Design Patterns
|
||||
* **Two-Way Warping System**: Implements a complex two-way portal system that allows Link to instantly travel between two designated points, enhancing exploration and puzzle design.
|
||||
* **Context-Sensitive Warping**: Portals can intelligently warp Link between dungeons and the overworld, adapting to the current game context and providing seamless transitions.
|
||||
* **Persistent Portal Locations**: Portal coordinates are stored in WRAM, allowing them to be placed and remembered across game sessions, enabling dynamic puzzle setups.
|
||||
* **Link State Management**: Modifies Link's coordinates, camera boundaries, and game mode during warps, ensuring a smooth and consistent player experience during transitions.
|
||||
* **Collision Detection**: Utilizes `CheckIfHitBoxesOverlap` to accurately detect when Link enters a portal, triggering the warp sequence.
|
||||
* **Error Handling**: Includes logic to dismiss portals if they are placed on invalid tiles, preventing game-breaking scenarios and providing feedback to the player.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,232 +0,0 @@
|
||||
# Switch Track
|
||||
|
||||
## Overview
|
||||
The Switch Track sprite (`!SPRID = Sprite_SwitchTrack`) is an interactive object designed to function as a rotating segment of a minecart track. Its visual appearance and implied path change dynamically based on its `SprAction` (which represents its mode of rotation) and the on/off state of a corresponding switch, stored in `SwitchRam`.
|
||||
|
||||
## Sprite Properties
|
||||
* **`!SPRID`**: `Sprite_SwitchTrack` (Custom symbol, likely a remapped vanilla ID)
|
||||
* **`!NbrTiles`**: `02`
|
||||
* **`!Harmless`**: `00`
|
||||
* **`!HVelocity`**: `00`
|
||||
* **`!Health`**: `01`
|
||||
* **`!Damage`**: `00`
|
||||
* **`!DeathAnimation`**: `00`
|
||||
* **`!ImperviousAll`**: `00`
|
||||
* **`!SmallShadow`**: `00`
|
||||
* **`!Shadow`**: `00`
|
||||
* **`!Palette`**: `00`
|
||||
* **`!Hitbox`**: `00`
|
||||
* **`!Persist`**: `01` (Continues to live off-screen)
|
||||
* **`!Statis`**: `00`
|
||||
* **`!CollisionLayer`**: `00`
|
||||
* **`!CanFall`**: `00`
|
||||
* **`!DeflectArrow`**: `00`
|
||||
* **`!WaterSprite`**: `00`
|
||||
* **`!Blockable`**: `00`
|
||||
* **`!Prize`**: `00`
|
||||
* **`!Sound`**: `00`
|
||||
* **`!Interaction`**: `00`
|
||||
* **`!Statue`**: `00`
|
||||
* **`!DeflectProjectiles`**: `00`
|
||||
* **`!ImperviousArrow`**: `00`
|
||||
* **`!ImpervSwordHammer`**: `00`
|
||||
* **`!Boss`**: `00`
|
||||
|
||||
## Main Structure (`Sprite_RotatingTrack_Long`)
|
||||
This routine handles the Switch Track's drawing and dispatches to its main logic if the sprite is active.
|
||||
|
||||
```asm
|
||||
Sprite_RotatingTrack_Long:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
JSR Sprite_RotatingTrack_Draw
|
||||
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
||||
JSR Sprite_RotatingTrack_Main
|
||||
.SpriteIsNotActive
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization (`Sprite_RotatingTrack_Prep`)
|
||||
This routine initializes the Switch Track upon spawning. It sets `SprDefl, X` to `$80`. It then calculates the tile attributes of the tile directly above the switch track and sets `SprAction, X` based on the `SPRTILE` value (normalized by subtracting `$D0`). This `SprAction, X` likely determines the initial mode or orientation of the track.
|
||||
|
||||
```asm
|
||||
Sprite_RotatingTrack_Prep:
|
||||
{
|
||||
PHB : PHK : PLB
|
||||
LDA.b #$80 : STA.w SprDefl, X
|
||||
|
||||
; Setup Minecart position to look for tile IDs
|
||||
; We use AND #$F8 to clamp to a 8x8 grid.
|
||||
; Subtract 8 from the Y position to get the tile right above instead.
|
||||
LDA.w SprY, X : AND #$F8 : SEC : SBC.b #$08 : STA.b $00
|
||||
LDA.w SprYH, X : STA.b $01
|
||||
|
||||
LDA.w SprX, X : AND #$F8 : STA.b $02
|
||||
LDA.w SprXH, X : STA.b $03
|
||||
|
||||
; Fetch tile attributes based on current coordinates
|
||||
LDA.b #$00 : JSL Sprite_GetTileAttr
|
||||
|
||||
LDA.w SPRTILE : SEC : SBC.b #$D0 : STA.w SprAction, X
|
||||
|
||||
PLB
|
||||
RTL
|
||||
}
|
||||
```
|
||||
|
||||
## Constants
|
||||
* **`SwitchRam = $0230`**: A WRAM address that stores the state (on/off) of each individual switch, indexed by its `SprSubtype`. This allows for multiple independent switch tracks.
|
||||
|
||||
## Main Logic & State Machine (`Sprite_RotatingTrack_Main`)
|
||||
This routine manages the visual state of the Switch Track based on its `SprAction` (mode of rotation) and the corresponding switch state in `SwitchRam`.
|
||||
|
||||
* **Modes**: The `SprAction, X` determines the mode of rotation, with four defined modes:
|
||||
* `0` = TopLeft -> TopRight
|
||||
* `1` = BottomLeft -> TopLeft
|
||||
* `2` = TopRight -> BottomRight
|
||||
* `3` = BottomRight -> BottomLeft
|
||||
* **State-Based Animation**: For each mode, the `SprFrame, X` (animation frame) is set based on the on/off state of the switch (`SwitchRam, Y`). This visually changes the track's orientation.
|
||||
|
||||
```asm
|
||||
Sprite_RotatingTrack_Main:
|
||||
{
|
||||
; Get the subtype of the track so that we can get its on/off state.
|
||||
LDA.w SprSubtype, X : TAY
|
||||
|
||||
LDA.w SprAction, X
|
||||
JSL UseImplicitRegIndexedLocalJumpTable
|
||||
|
||||
dw TopLeftToTopRight
|
||||
dw BottomLeftToTopLeft
|
||||
dw TopRightToBottomRight
|
||||
dw BottomRightToBottomLeft
|
||||
|
||||
; 00 = TopLeft -> TopRight
|
||||
TopLeftToTopRight:
|
||||
{
|
||||
LDA.w SwitchRam, Y : BNE .part2
|
||||
LDA.b #$00 : STA.w SprFrame, X
|
||||
RTS
|
||||
.part2
|
||||
LDA.b #$01 : STA.w SprFrame, X
|
||||
RTS
|
||||
}
|
||||
|
||||
; 01 = BottomLeft -> TopLeft
|
||||
BottomLeftToTopLeft:
|
||||
{
|
||||
LDA.w SwitchRam, Y : BNE .part2_c
|
||||
LDA.b #$03 : STA.w SprFrame, X
|
||||
RTS
|
||||
.part2_c
|
||||
LDA.b #$00 : STA.w SprFrame, X
|
||||
RTS
|
||||
}
|
||||
|
||||
; 02 = TopRight -> BottomRight
|
||||
TopRightToBottomRight:
|
||||
{
|
||||
LDA.w SwitchRam, Y : BNE .part2_a
|
||||
LDA.b #$01 : STA.w SprFrame, X
|
||||
RTS
|
||||
.part2_a
|
||||
LDA.b #$02 : STA.w SprFrame, X
|
||||
RTS
|
||||
}
|
||||
|
||||
; 03 = BottomRight -> BottomLeft
|
||||
BottomRightToBottomLeft:
|
||||
{
|
||||
LDA.w SwitchRam, Y : BEQ .part2_b
|
||||
LDA.b #$03 : STA.w SprFrame, X
|
||||
RTS
|
||||
.part2_b
|
||||
LDA.b #$02 : STA.w SprFrame, X
|
||||
RTS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Drawing (`Sprite_RotatingTrack_Draw`)
|
||||
This routine handles OAM allocation and animation for the Switch Track. It explicitly uses `REP #$20` and `SEP #$20` for 16-bit coordinate calculations, ensuring accurate sprite rendering.
|
||||
|
||||
```asm
|
||||
Sprite_RotatingTrack_Draw:
|
||||
{
|
||||
JSL Sprite_PrepOamCoord
|
||||
LDA.b #$04 : JSL OAM_AllocateFromRegionB
|
||||
|
||||
LDA $0DC0, X : CLC : ADC $0D90, X : TAY;Animation Frame
|
||||
LDA .start_index, Y : STA $06
|
||||
|
||||
PHX
|
||||
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
||||
LDY.b #$00
|
||||
.nextTile
|
||||
|
||||
PHX ; Save current Tile Index?
|
||||
|
||||
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
||||
|
||||
PHA ; Keep the value with animation index offset?
|
||||
|
||||
ASL A : TAX
|
||||
|
||||
REP #$20
|
||||
|
||||
LDA $00 : STA ($90), Y
|
||||
AND.w #$0100 : STA $0E
|
||||
INY
|
||||
LDA $02 : STA ($90), Y
|
||||
CLC : ADC #$0010 : CMP.w #$0100
|
||||
SEP #$20
|
||||
BCC .on_screen_y
|
||||
|
||||
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
||||
STA $0E
|
||||
.on_screen_y
|
||||
|
||||
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
||||
INY
|
||||
LDA .chr, X : STA ($90), Y
|
||||
INY
|
||||
LDA .properties, X : STA ($90), Y
|
||||
|
||||
PHY
|
||||
|
||||
TYA : LSR #2 : TAY
|
||||
|
||||
LDA.b #$02 : ORA $0F : STA ($92), Y ; store size in oam buffer
|
||||
|
||||
PLY : INY
|
||||
|
||||
PLX : DEX : BPL .nextTile
|
||||
|
||||
PLX
|
||||
|
||||
RTS
|
||||
|
||||
.start_index
|
||||
db $00, $01, $02, $03
|
||||
.nbr_of_tiles
|
||||
db 0, 0, 0, 0
|
||||
.chr
|
||||
db $44
|
||||
db $44
|
||||
db $44
|
||||
db $44
|
||||
.properties
|
||||
db $3D
|
||||
db $7D
|
||||
db $FD
|
||||
db $BD
|
||||
}
|
||||
```
|
||||
|
||||
## Design Patterns
|
||||
* **Interactive Puzzle Element**: The Switch Track is a key puzzle element that changes its orientation based on an external switch (likely the `mineswitch` sprite), directly influencing the path of minecarts.
|
||||
* **State-Based Animation**: The track's animation frame (`SprFrame, X`) is directly controlled by the on/off state of a corresponding switch in `SwitchRam`, providing clear visual feedback to the player about its current configuration.
|
||||
* **Subtype-Driven State**: The `SprSubtype` is used to index into `SwitchRam`, allowing each individual Switch Track to maintain its own independent state. This enables complex puzzle designs with multiple, distinct switch tracks.
|
||||
* **16-bit OAM Calculations**: Demonstrates explicit use of `REP #$20` and `SEP #$20` for precise 16-bit OAM coordinate calculations, crucial for accurate sprite rendering.
|
||||
@@ -1,28 +0,0 @@
|
||||
# Overlord Sprite Analysis
|
||||
|
||||
This document provides an analysis of the "Overlord" sprite system, which is a special type of sprite that acts as a controller for spawning other sprites or triggering events within a room. The main logic is found in `Sprites/overlord.asm`.
|
||||
|
||||
## Overview
|
||||
|
||||
Overlord sprites are invisible, non-interactive sprites that are placed in a room via a level editor. Their purpose is to run logic in the background, often tied to room-specific events or conditions. They are distinct from standard sprites and are processed by a separate loop.
|
||||
|
||||
In this project, the primary use of the Overlord system is to dynamically spawn soldiers in Hyrule Castle after the player acquires the Master Sword.
|
||||
|
||||
## `overlord.asm` Analysis
|
||||
|
||||
- **File:** `Sprites/overlord.asm`
|
||||
- **Summary:** This file contains the logic for `Overlord04`, which is hooked into the game at `$09B7AE`. This specific overlord is responsible for continuously spawning soldiers in Hyrule Castle to create a sense of alarm and danger.
|
||||
|
||||
### Key Logic
|
||||
|
||||
- **`Overlord_KalyxoCastleGuards`:** This is the main entry point for the overlord's logic. It is a simple routine that calls `SummonGuards`.
|
||||
|
||||
- **`SummonGuards`:**
|
||||
- **Trigger Condition:** This routine first checks if Link has the Master Sword (`LDA.l Sword : CMP.b #$02`). It will only proceed if the sword level is 2 or greater.
|
||||
- **Spawning Logic:** If the condition is met, it calls `Overlord_SpawnSoldierPath`.
|
||||
|
||||
- **`Overlord_SpawnSoldierPath`:**
|
||||
- **Spawn Timer:** This routine uses `OverlordTimerB` as a countdown timer to manage the rate of spawning. It will not spawn a new soldier until the timer reaches zero.
|
||||
- **Sprite Limit:** It checks the number of active soldiers (`Sprite Type $41`) on screen. If there are already 5 or more, it will not spawn a new one.
|
||||
- **Spawning:** If the conditions are met, it calls `Sprite_SpawnDynamically_slot_limited` to create a new Blue Soldier (`$41`).
|
||||
- **Positioning:** The new soldier's position and initial direction are determined by data tables within the routine (`soldier_position_x`, `soldier_position_y`, `soldier_direction`), allowing for multiple spawn points.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user