Refactor logging statements to use %log_end macro for consistency across item and menu scripts

This commit is contained in:
scawful
2025-10-03 14:36:19 -04:00
parent f2b92e816b
commit 4289e134aa
14 changed files with 146 additions and 30 deletions

View File

@@ -96,7 +96,7 @@ This section details the layout of the save file memory.
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. 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 | | Bank (Hex) | Address Range (PC) | Purpose / Contents |
|------------|--------------------|--------------------------------------------------------| |------------|-----------------------|--------------------------------------------------------|
| $20 | `$208000` - `$20FFFF` | Expanded Music | | $20 | `$208000` - `$20FFFF` | Expanded Music |
| $21-$27 | | ZScream Reserved | | $21-$27 | | ZScream Reserved |
| $28 | `$288000` - `$28FFFF` | ZSCustomOverworld data and code | | $28 | `$288000` - `$28FFFF` | ZSCustomOverworld data and code |

113
Docs/Sprites/Overlords.md Normal file
View File

@@ -0,0 +1,113 @@
# Overlord Sprite System Analysis
This document provides a comprehensive analysis of the "Overlord" sprite system, a special class of sprite used for room-level event scripting and control.
## 1. What is an Overlord?
An Overlord is an invisible, non-interactive sprite placed in a room via a level editor. Unlike normal sprites (enemies, NPCs), their purpose is not to interact directly with Link, but to execute logic in the background. They function as "room controllers" or "event triggers."
Common uses for Overlords include:
- Spawning other sprites under specific conditions.
- Modifying the environment (e.g., creating falling tiles, moving floors).
- Coordinating the behavior of multiple other sprites.
- Setting up room-specific traps or puzzles.
In `Oracle of Secrets`, the most prominent example is `Overlord04`, which is used to continuously spawn soldier sprites in Hyrule Castle after Link acquires the Master Sword, creating a sense of alarm.
## 2. How Overlords Work: The Engine Loop
The core logic for the Overlord system resides in **Bank $09** of the vanilla ROM.
1. **Main Entry Point:** The standard sprite processing loop (`bank_06`) calls `JSL Overlord_Main` (at `$068398`), which jumps to the main overlord handler at **`$09B770`**.
2. **Execution Loop (`Overlord_ExecuteAll`):** At `$09B781`, the routine `Overlord_ExecuteAll` begins. It loops five times, once for each of the five available overlord "slots" in RAM. In each iteration, it calls `Overlord_ExecuteSingle`.
3. **Single Overlord Execution (`Overlord_ExecuteSingle`):** This routine, starting at `$09B791`, is the heart of the system. For a given overlord slot, it performs these steps:
a. It calls `Overlord_CheckIfActive` (`$09C08A`) to see if the slot contains an active overlord.
b. It reads the **Overlord Type** from WRAM address `$0F90,X` (where X is the overlord slot 0-4).
c. It uses this Type ID as an index into a jump table located at **`$09B7A8`**.
d. It executes the routine pointed to by the jump table entry, running the specific logic for that overlord type.
## 3. Overlord Jump Table
The jump table at `$09B7A8` is the key to customizing overlords. It contains pointers to the code for each of the 26 (1A) possible overlord types.
| Address | Vanilla Label | Oracle of Secrets Usage |
|---|---|---|
| `$09B7A8` | `Overlord01_PositionTarget` | Unused |
| `$09B7AA` | `Overlord02_FullRoomCannons` | Unused |
| `$09B7AC` | `Overlord03_VerticalCannon` | Unused |
| **`$09B7AE`** | `Overlord04_Unused` | **Hooked for `Overlord_KalyxoCastleGuards`** |
| `$09B7B0` | `Overlord05_FallingStalfos` | Unused |
| ... | ... | ... |
`Oracle of Secrets` replaces the pointer at `$09B7AE` to point to its own custom logic for the castle guard spawner.
## 4. Overlord RAM Data Structure
Each of the five active overlords has its data stored in a series of arrays in WRAM, indexed by the overlord slot (0-4).
| Address | Description |
|---|---|
| `$0F90,X` | **Overlord Type:** The ID (1-26) that determines which logic to run via the jump table.
| `$0FA0,X` | **Overlord State/Action:** The current state of the overlord's internal state machine, similar to `SprAction` for normal sprites.
| `$0FB0,X` | **Overlord Timer A:** A general-purpose timer.
| `$0FC0,X` | **Overlord Timer B:** A second general-purpose timer.
| `$0FD0,X` | **Overlord Timer C:** A third general-purpose timer.
| `$0B08,X` | X-Coordinate (and other properties, loaded from room data).
| `$0B10,X` | Y-Coordinate (and other properties, loaded from room data).
When a room is loaded, the `Underworld_LoadSingleOverlord` (`$09C35A`) or `Overworld_LoadSingleOverlord` (`$09C779`) routines read the overlord data defined in the level editor and populate these WRAM slots.
## 5. Creating a Custom Overlord
To create your own custom overlord, follow these steps:
1. **Choose an Overlord Slot:** Find an unused overlord type in the jump table at `$09B7A8`. `Overlord04` is already taken, but others may be available. For this example, let's assume you choose to replace `Overlord05_FallingStalfos` at `$09B7B0`.
2. **Write Your Logic:** Create a new `.asm` file (e.g., `Sprites/Overlords/my_custom_overlord.asm`) or add to the existing `Sprites/overlord.asm`. Your code should define the main routine for your overlord.
```asm
; In my_custom_overlord.asm
MyCustomOverlord_Main:
{
; Your logic here.
; You can use the Overlord Timers and State registers.
; For example, let's use the state register to create a simple two-state machine.
LDA.w $0FA0,X ; Load the current state
JSL JumpTableLocal
dw .State0_Wait
dw .State1_DoSomething
.State0_Wait
; Decrement Timer A. When it hits zero, switch to state 1.
LDA.w $0FB0,X : BNE .keep_waiting
INC.w $0FA0,X ; Go to state 1
LDA.b #$80 : STA.w $0FB0,X ; Reset timer
.keep_waiting
RTS
.State1_DoSomething
; Do something, like spawn a sprite.
; Then go back to state 0.
LDA.b #<Sprite_ID>
JSL Sprite_SpawnDynamically
STZ.w $0FA0,X ; Go back to state 0
RTS
}
```
3. **Hook into the Jump Table:** In a file that is included in your main build file (like `Core/patches.asm`), add an `org` directive to overwrite the vanilla pointer in the jump table.
```asm
; In Core/patches.asm
incsrc ../Sprites/Overlords/my_custom_overlord.asm
pushpc
org $09B7B0 ; Address for Overlord05
dw MyCustomOverlord_Main ; Replace pointer with your routine
pullpc
```
4. **Place in a Room:** Use your level editor to place an "Overlord" object in a room. Set its **Type** to the one you chose (e.g., `05`). When the room is loaded, the game will load your overlord into an active slot, and the main loop will execute your custom code.

View File

@@ -11,7 +11,7 @@ incsrc "Items/portal_rod.asm"
incsrc "Items/fishing_rod.asm" incsrc "Items/fishing_rod.asm"
incsrc "Items/magic_rings.asm" incsrc "Items/magic_rings.asm"
incsrc "Items/fist_damage.asm" incsrc "Items/fist_damage.asm"
%print_debug("End of Items/fist_damage.asm ") %log_end("Items/fist_damage.asm", !LOG_ITEMS)
MagicBeanGfx: MagicBeanGfx:
incbin "gfx/magic_bean.bin" incbin "gfx/magic_bean.bin"
@@ -80,5 +80,5 @@ Link_ConsumeMagicBagItem:
pushpc pushpc
; League of its own ; League of its own
incsrc "Items/ice_rod.asm" incsrc "Items/ice_rod.asm"
%print_debug("End of Items/ice_rod.asm ") %log_end("Items/ice_rod.asm", !LOG_ITEMS)
pullpc pullpc

View File

@@ -73,5 +73,5 @@ Dungeon_RevealSecrets:
RTL RTL
} }
print "End of Items/book_of_secrets.asm ", pc %log_end("Items/book_of_secrets.asm", !LOG_ITEMS)
pushpc pushpc

View File

@@ -215,5 +215,5 @@ Bottle_DrinkMilk:
RTL RTL
} }
print "End of Items/bottle_net.asm ", pc %log_end("Items/bottle_net.asm", !LOG_ITEMS)
pushpc pushpc

View File

@@ -515,4 +515,4 @@ DismissRodFromMenu:
RTL RTL
} }
print "End of Items/fishing_rod.asm ", pc %log_end("Items/fishing_rod.asm", !LOG_ITEMS)

View File

@@ -88,5 +88,5 @@ CheckIfJumpingForSpikeDamage:
db $04 ; red db $04 ; red
} }
print "End of Items/jump_feather.asm ", pc %log_end("Items/jump_feather.asm", !LOG_ITEMS)
pushpc pushpc

View File

@@ -386,7 +386,7 @@ UpdateFluteSong_Long:
.not_pressed .not_pressed
RTL RTL
} }
print "End of Items/ocarina.asm ", pc %log_end("Items/ocarina.asm", !LOG_ITEMS)
pushpc ; Bank2B freespace pushpc ; Bank2B freespace

View File

@@ -221,7 +221,7 @@ ScrollToPortal:
RTL RTL
} }
print "End of Items/portal_rod.asm ", pc %log_end("Items/portal_rod.asm", !LOG_ITEMS)
pushpc pushpc
; ; Portal Rod logic based on Fire Rod ; ; Portal Rod logic based on Fire Rod

View File

@@ -762,9 +762,9 @@ Submenu_Return:
menu_frame: incbin "tilemaps/menu_frame.tilemap" menu_frame: incbin "tilemaps/menu_frame.tilemap"
quest_icons: incbin "tilemaps/quest_icons.tilemap" quest_icons: incbin "tilemaps/quest_icons.tilemap"
incsrc "menu_map_names.asm" incsrc "menu_map_names.asm"
%print_debug("End of Menu/menu.asm ") %log_end("Menu/menu.asm", !LOG_MENU)
incsrc "menu_hud.asm" incsrc "menu_hud.asm"
%print_debug("End of Menu/menu_hud.asm ") %log_end("Menu/menu_hud.asm", !LOG_MENU)
incsrc "menu_journal.asm" incsrc "menu_journal.asm"
%print_debug("End of Menu/menu_journal.asm ") %log_end("Menu/menu_journal.asm", !LOG_MENU)

View File

@@ -39,7 +39,7 @@
incsrc "Util/macros.asm" incsrc "Util/macros.asm"
incsrc "Overworld/ZSCustomOverworld.asm" incsrc "Overworld/ZSCustomOverworld.asm"
%print_debug("End of ZSCustomOverworld.asm ") %log_end("ZSCustomOverworld.asm", !LOG_OVERWORLD)
; Vanilla WRAM and SRAM ; Vanilla WRAM and SRAM
incsrc "Core/ram.asm" incsrc "Core/ram.asm"
@@ -76,6 +76,7 @@ namespace Oracle
incsrc "Core/patches.asm" incsrc "Core/patches.asm"
; incsrc "Core/capture.asm" ; incsrc "Core/capture.asm"
print "\nFinished applying patches" print ""
print "Finished applying patches"
} }
namespace off namespace off

View File

@@ -37,25 +37,25 @@ org $0EDE29
; ========================================================= ; =========================================================
incsrc "Overworld/lost_woods.asm" incsrc "Overworld/lost_woods.asm"
print "End of Overworld/lost_woods.asm ", pc %log_end("Overworld/lost_woods.asm", !LOG_OVERWORLD)
org $348000 ; Free space org $348000 ; Free space
pushpc pushpc
incsrc "Overworld/time_system.asm" incsrc "Overworld/time_system.asm"
print "End of Overworld/time_system.asm ", pc %log_end("Overworld/time_system.asm", !LOG_OVERWORLD)
incsrc "Overworld/overlays.asm" incsrc "Overworld/overlays.asm"
print "End of Overworld/overlays.asm ", pc %log_end("Overworld/overlays.asm", !LOG_OVERWORLD)
incsrc "Overworld/entrances.asm" incsrc "Overworld/entrances.asm"
print "End of Overworld/entrances.asm ", pc %log_end("Overworld/entrances.asm", !LOG_OVERWORLD)
incsrc "Overworld/custom_gfx.asm" incsrc "Overworld/custom_gfx.asm"
print "End of Overworld/custom_gfx.asm ", pc %log_end("Overworld/custom_gfx.asm", !LOG_OVERWORLD)
pushpc pushpc
incsrc "Overworld/world_map.asm" incsrc "Overworld/world_map.asm"
print "End of world_map.asm ", pc %log_end("Overworld/world_map.asm", !LOG_OVERWORLD)
; ========================================================= ; =========================================================
; Get Lv2 Sword from chest ; Get Lv2 Sword from chest

View File

@@ -415,7 +415,7 @@ Sprite_39_ZoraBaby:
RTS RTS
} }
} }
print "End of Sprite 39 Locksmith ", pc %log_end("Sprite 39 Locksmith", !LOG_SPRITES)
assert pc() <= $06BD9C assert pc() <= $06BD9C
pullpc pullpc

View File

@@ -25,7 +25,9 @@ endmacro
; Usage: %log_section("Sprites", !LOG_SPRITES) ; Usage: %log_section("Sprites", !LOG_SPRITES)
macro log_section(name, flag) macro log_section(name, flag)
if !DEBUG == 1 && <flag> == 1 if !DEBUG == 1 && <flag> == 1
print "\n--- <name> ---" print ""
print "--- <name> ---"
print ""
endif endif
endmacro endmacro