Reduce the command queue system to just stone tables - pret/pokecrystal GitHub Wiki
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.
Contents
- 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
1. Remove the command queue–related constants
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_value
2. Replace writecmdqueue and delcmdqueue with usestonetable and clearstonetable
Edit 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
ret
And edit wram.asm:
-wCmdQueue:: ds CMDQUEUE_CAPACITY * CMDQUEUE_ENTRY_SIZE
+wStoneTableAddress:: dw
- ds 40
+ ds 62
Now 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.
3. Replace MAPCALLBACK_CMDQUEUE with MAPCALLBACK_STONETABLE
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_NEWMAP
Then 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
ret
4. Update the map scripts that use stone tables
Edit 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 ; end
And 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 ; end
Both 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.
5. Implement the simplified stone table system
Edit engine/overworld/events.asm:
HandleMap:
call ResetOverworldDelay
call HandleMapTimeAndJoypad
- farcall HandleCmdQueue ; no need to farcall
+ call HandleStoneTable
call MapEvents
Then 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
ret
Everything 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, c
Since 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!