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 1. [Remove the command queue–related constants](#1-remove-the-command-queuerelated-constants) 2. [Replace `writecmdqueue` and `delcmdqueue` with `usestonetable` and `clearstonetable`](#2-replace-writecmdqueue-and-delcmdqueue-with-usestonetable-and-clearstonetable) 3. [Replace `MAPCALLBACK_CMDQUEUE` with `MAPCALLBACK_STONETABLE`](#3-replace-mapcallback_cmdqueue-with-mapcallback_stonetable) 4. [Update the map scripts that use stone tables](#4-update-the-map-scripts-that-use-stone-tables) 5. [Implement the simplified stone table system](#5-implement-the-simplified-stone-table-system) ## 1. Remove the command queue–related constants Edit [constants/script_constants.asm](../blob/master/constants/script_constants.asm): ```diff -; 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](../blob/master/macros/scripts/events.asm): ```diff - const writecmdqueue_command ; $7d -writecmdqueue: MACRO - db writecmdqueue_command - dw \1 ; queue_pointer -ENDM + const usestonetable_command ; $7d +usestonetable: MACRO + db usestonetable_command + dw \1 ; stonetable_pointer +ENDM - const delcmdqueue_command ; $7e -delcmdqueue: MACRO - db delcmdqueue_command - db \1 ; byte -ENDM + const clearstonetable_command ; $7e +clearstonetable: MACRO + 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](../blob/master/engine/overworld/scripting.asm): ```diff - dw Script_writecmdqueue ; 7d - dw Script_delcmdqueue ; 7e + dw Script_usestonetable ; 7d + dw Script_clearstonetable ; 7e ``` ```diff -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](../blob/master/wram.asm): ```diff -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](../blob/master/constants/map_setup_constants.asm): ```diff ; 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](../blob/master/engine/overworld/warp_connection.asm): ```diff 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](../blob/master/maps/BlackthornGym2F.asm): ```diff 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](../blob/master/maps/IcePathB1F.asm): ```diff 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](../blob/master/engine/overworld/events.asm): ```diff HandleMap: call ResetOverworldDelay call HandleMapTimeAndJoypad - farcall HandleCmdQueue ; no need to farcall + call HandleStoneTable call MapEvents ``` Then edit [engine/overworld/cmd_queue.asm](../blob/master/engine/overworld/cmd_queue.asm): ```diff -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](../blob/master/home/stone_queue.asm): ```diff .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.