-
Notifications
You must be signed in to change notification settings - Fork 806
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
writecmdqueue
anddelcmdqueue
withusestonetable
andclearstonetable
- Replace
MAPCALLBACK_CMDQUEUE
withMAPCALLBACK_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_value
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.
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
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
.
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!