-
Notifications
You must be signed in to change notification settings - Fork 912
Reduce the command queue system to just stone tables
One feature of the Gen 2 overworld engine is the "command queue". Event scripts may queue up to six entries with the writecmdqueue and delcmdqueue script commands. Then every time the player takes a step, the queued commands run depending on their type. In practice, the only usable command type is CMDQUEUE_STONETABLE, for pushing Strength boulders into holes. The other command types are dummied out or incomplete.
This tutorial simplifies the system to replace the entire queue with a single pointer to a "stone table", which may be null or may point to a table defining how Strength boulders interact with holes. It saves ROM and RAM space, and simplifies the map callbacks that set up stone tables.
- Remove the command queue–related constants
- Replace
writecmdqueueanddelcmdqueuewithusestonetableandclearstonetable - Replace
MAPCALLBACK_CMDQUEUEwithMAPCALLBACK_STONETABLE - Update the map scripts that use stone tables
- Implement the simplified stone table system
Edit constants/script_constants.asm:
-; command queue members
-CMDQUEUE_TYPE EQU 0
-CMDQUEUE_ADDR EQU 1
-CMDQUEUE_02 EQU 2
-CMDQUEUE_03 EQU 3
-CMDQUEUE_04 EQU 4
-CMDQUEUE_05 EQU 5
-CMDQUEUE_ENTRY_SIZE EQU 6
-CMDQUEUE_CAPACITY EQU 4
-
-; HandleQueuedCommand.Jumptable indexes (see engine/overworld/events.asm)
- const_def
- const CMDQUEUE_NULL
- const CMDQUEUE_TYPE1
- const CMDQUEUE_STONETABLE
- const CMDQUEUE_TYPE3
- const CMDQUEUE_TYPE4
-NUM_CMDQUEUE_TYPES EQU const_valueEdit macros/scripts/events.asm:
- const writecmdqueue_command ; $7d
-MACRO writecmdqueue
- db writecmdqueue_command
- dw \1 ; queue_pointer
-ENDM
+ const usestonetable_command ; $7d
+MACRO usestonetable
+ db usestonetable_command
+ dw \1 ; stonetable_pointer
+ENDM
- const delcmdqueue_command ; $7e
-MACRO delcmdqueue
- db delcmdqueue_command
- db \1 ; byte
-ENDM
+ const clearstonetable_command ; $7e
+MACRO clearstonetable
+ db clearstonetable_command
+ENDM(The clearstonetable command isn't really necessary, since maps are unlikely to need it and usestonetable NULL will do the same thing, but I'm including it for completeness.)
Then edit engine/overworld/scripting.asm:
- dw Script_writecmdqueue ; 7d
- dw Script_delcmdqueue ; 7e
+ dw Script_usestonetable ; 7d
+ dw Script_clearstonetable ; 7e-Script_writecmdqueue:
+Script_usestonetable:
call GetScriptByte
- ld e, a
+ ld [wStoneTableAddress], a
call GetScriptByte
- ld d, a
+ ld [wStoneTableAddress+1], a
- ld a, [wScriptBank]
- ld b, a
- farcall WriteCmdQueue ; no need to farcall
ret
-Script_delcmdqueue:
+Script_clearstonetable:
xor a
- ld [wScriptVar], a
+ ld [wStoneTableAddress], a
+ ld [wStoneTableAddress+1], a
- call GetScriptByte
- ld b, a
- farcall DelCmdQueue ; no need to farcall
- ret c
- ld a, TRUE
- ld [wScriptVar], a
retAnd edit wram.asm:
-wCmdQueue:: ds CMDQUEUE_CAPACITY * CMDQUEUE_ENTRY_SIZE
+wStoneTableAddress:: dw
- ds 40
+ ds 62Now usestonetable .StoneTable will store the address .StoneTable in wStoneTableAddress, and clearstonetable will set wStoneTableAddress to NULL. The wStoneTableAddress pointer only needs two bytes, whereas wCmdQueue needed 24, so this saves 22 bytes of RAM.
Edit constants/map_setup_constants.asm:
; callback types
const_def 1
const MAPCALLBACK_TILES
const MAPCALLBACK_OBJECTS
- const MAPCALLBACK_CMDQUEUE
+ const MAPCALLBACK_STONETABLE
const MAPCALLBACK_SPRITES
const MAPCALLBACK_NEWMAPThen edit engine/overworld/warp_connection.asm:
HandleContinueMap:
- farcall ClearCmdQueue
+ xor a
+ ld [wStoneTableAddress], a
+ ld [wStoneTableAddress+1], a
- ld a, MAPCALLBACK_CMDQUEUE
+ ld a, MAPCALLBACK_STONETABLE
call RunMapCallback
call GetMapTimeOfDay
ld [wMapTimeOfDay], a
retEdit maps/BlackthornGym2F.asm:
def_callbacks
- callback MAPCALLBACK_CMDQUEUE, .SetUpStoneTable
+ callback MAPCALLBACK_STONETABLE, .SetUpStoneTable
.SetUpStoneTable:
- writecmdqueue .CommandQueue
+ usestonetable .StoneTable
endcallback
-.CommandQueue:
- cmdqueue CMDQUEUE_STONETABLE, .StoneTable ; check if any stones are sitting on a warp
-
.StoneTable:
stonetable 5, BLACKTHORNGYM2F_BOULDER1, .Boulder1
stonetable 3, BLACKTHORNGYM2F_BOULDER2, .Boulder2
stonetable 4, BLACKTHORNGYM2F_BOULDER3, .Boulder3
db -1 ; endAnd edit maps/IcePathB1F.asm:
def_callbacks
- callback MAPCALLBACK_CMDQUEUE, .SetUpStoneTable
+ callback MAPCALLBACK_STONETABLE, .SetUpStoneTable
.SetUpStoneTable:
- writecmdqueue .CommandQueue
+ usestonetable .StoneTable
endcallback
-.CommandQueue:
- cmdqueue CMDQUEUE_STONETABLE, .StoneTable ; check if any stones are sitting on a warp
-
.StoneTable:
stonetable 3, ICEPATHB1F_BOULDER1, .Boulder1
stonetable 4, ICEPATHB1F_BOULDER2, .Boulder2
stonetable 5, ICEPATHB1F_BOULDER3, .Boulder3
stonetable 6, ICEPATHB1F_BOULDER4, .Boulder4
db -1 ; endBoth of these maps allow you to push boulders into holes with Strength. Now they use the new callback type and event commands.
Previously, the writecmdqueue event command took a pointer to a command queue definition (.CommandQueue), and that command queue then pointed to its own data depending on its type (so the CMDQUEUE_STONETABLE had the .StoneTable pointer with stonetable data). Now, the usestonetable event command directly takes a .StoneTable pointer to stonetable data, without needing an intermediate cmdqueue.
Edit engine/overworld/events.asm:
HandleMap:
call ResetOverworldDelay
call HandleMapTimeAndJoypad
- farcall HandleCmdQueue ; no need to farcall
+ call HandleStoneTable
call MapEventsThen edit engine/overworld/cmd_queue.asm:
-ClearCmdQueue::
- ...
-
-HandleCmdQueue::
- ...
-
-GetNthCmdQueueEntry: ; unreferenced
- ...
-
-WriteCmdQueue::
- ...
-
-DelCmdQueue::
- ...
-
-HandleQueuedCommand:
- ...
-
-CmdQueues_AnonJumptable:
- ...
-
-CmdQueues_IncAnonJumptableIndex:
- ...
-
-CmdQueues_DecAnonJumptableIndex:
- ...
-
-CmdQueue_Null:
- ...
-
-CmdQueue_Type1:
- ...
-
-CmdQueue_Type4:
- ...
-
-CmdQueue_Type3:
- ...
-
-CmdQueue_StoneTable:
+HandleStoneTable::
+ ld hl, wStoneTableAddress
+ ld a, [hli]
+ ld b, [hl]
+ ld c, a
+ or b
+ ret z
ld de, wPlayerStruct
ld a, NUM_OBJECT_STRUCTS
.loop
push af
...
pop af
dec a
jr nz, .loop
ret
.fall_down_hole
pop af
retEverything gets deleted except CmdQueue_StoneTable, which we rename to HandleStoneTable and add some code at the beginning to use wStoneTableAddress (or return if it's NULL).
Finally, edit home/stone_queue.asm:
.IsObjectInStoneTable:
inc e
- ld hl, CMDQUEUE_ADDR
- add hl, bc
- ld a, [hli]
- ld h, [hl]
- ld l, a
+ ld h, b
+ ld l, cSince HandleStoneTable now stores the wStoneTableAddress value in bc, here we need to use that value instead of getting the relevant part of some command queue data.
With these changes, the Blackthorn Gym and Ice Path puzzles with Strength boulders will work just like before, but the code and data are more efficient.
Doing all this frees a mere 357 bytes or 0.018% of the 2 Mb ROM. But every little helps!