Add comprehensive documentation for sprites and systems
- Introduced detailed analysis for the Minecart system, highlighting its state machine, track system, and areas for improvement. - Created an NPCs analysis document, summarizing various NPC sprites and their functionalities. - Added an Objects analysis document, covering interactive elements like collectibles, ice blocks, and minecarts. - Documented the Overlord sprite system, detailing its role in spawning other sprites and managing events. - Compiled a Dungeons & Indoor Areas document, outlining custom room tags, enhanced mechanics, and advanced collision systems. - Developed an Overworld Systems Analysis, focusing on the ZSCustomOverworld architecture and its core features. - Added a Time System document, explaining the in-game clock and day/night cycle management. - Documented the ZScream Custom Overworld, detailing its data-driven approach and key features.
This commit is contained in:
84
Docs/Features/Items/Items.md
Normal file
84
Docs/Features/Items/Items.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Custom Items System
|
||||
|
||||
This document details the functionality of new and modified items in Oracle of Secrets, based on analysis of the `Items/` directory.
|
||||
|
||||
## 1. Overview
|
||||
|
||||
The item roster has been significantly expanded with new mechanics, and many vanilla items have been reworked to provide new functionality. The system is managed through a combination of hooks into the main player state machine and custom routines for each item.
|
||||
|
||||
## 2. Vanilla Item Modifications
|
||||
|
||||
Several items from the original game have been altered.
|
||||
|
||||
### Hookshot / Goldstar
|
||||
|
||||
- **Files:** `goldstar.asm`
|
||||
- **Functionality:** The Hookshot can be upgraded to the **Goldstar**, a powerful morning star weapon. The two items share an inventory slot.
|
||||
- **Switching:** When the Goldstar is obtained, the player can switch between the Hookshot and Goldstar by pressing the L/R shoulder buttons while it is selected in the menu. The current mode is tracked by the `GoldstarOrHookshot` WRAM variable.
|
||||
- **Goldstar Mechanics:** When active, the item functions as a short-range, powerful melee weapon with its own collision and damage properties, distinct from the Hookshot's grappling mechanic.
|
||||
|
||||
### Ice Rod
|
||||
|
||||
- **File:** `ice_rod.asm`
|
||||
- **Functionality:** The Ice Rod's projectile now freezes water tiles it hits, creating temporary 16x16 ice platforms. This allows the player to cross water gaps.
|
||||
- **Implementation:** The `LinkItem_IceRod` routine hooks into the ancilla tile collision logic. When the projectile hits a water tile, it dynamically modifies the tilemap properties in RAM and DMAs new ice graphics to VRAM.
|
||||
|
||||
### Bug-Catching Net -> Roc's Feather
|
||||
|
||||
- **File:** `jump_feather.asm`
|
||||
- **Functionality:** The vanilla Bug-Catching Net has been completely replaced by **Roc's Feather**. This item allows Link to perform a short hop.
|
||||
- **Implementation:** `LinkItem_JumpFeather` initiates the jump by setting Link's state to a recoil/ledge hop state and applying a burst of vertical velocity.
|
||||
|
||||
### Bottles
|
||||
|
||||
- **File:** `bottle_net.asm`
|
||||
- **Functionality:** The Bug-Catching Net is no longer required to catch bees, fairies, etc. The Bottle item now has a dual function:
|
||||
1. If the bottle is **empty**, using it initiates the `LinkItem_CatchBottle` routine, which performs a net-catching swing.
|
||||
2. If the bottle is **full**, using it calls `LinkItem_Bottles`, which consumes the contents (e.g., drinks a potion, releases a fairy).
|
||||
|
||||
### Book of Mudora -> Book of Secrets
|
||||
|
||||
- **File:** `book_of_secrets.asm`
|
||||
- **Functionality:** The Book of Mudora is now the **Book of Secrets**. While its vanilla function of translating Hylian text remains, it has a new secret-revealing capability.
|
||||
- **Implementation:** The `Dungeon_RevealSecrets` routine checks if the L button is held while inside a building. If it is, it disables the `BG2` layer, which can be used to hide secret passages or objects behind walls that are part of that background layer.
|
||||
|
||||
## 3. New Active Items
|
||||
|
||||
### Ocarina
|
||||
|
||||
- **File:** `ocarina.asm`
|
||||
- **Functionality:** A multi-song instrument. When selected, the player can cycle through learned songs using the L/R shoulder buttons. Pressing 'Y' plays the selected song, triggering its unique effect.
|
||||
- **Songs & Effects:**
|
||||
- **Song of Healing:** Heals certain NPCs or triggers quest events.
|
||||
- **Song of Storms:** Toggles a rain overlay on the overworld, which can affect the environment (e.g., watering the Magic Bean).
|
||||
- **Song of Soaring:** Warps the player to pre-defined locations (the vanilla flute's bird travel).
|
||||
- **Song of Time:** Toggles the in-game time between day and night.
|
||||
|
||||
### Shared Slot: Portal Rod & Fishing Rod
|
||||
|
||||
- **Files:** `portal_rod.asm`, `fishing_rod.asm`
|
||||
- **Functionality:** These two distinct items share a single inventory slot. If the player has the upgrade (`$7EF351 >= 2`), they can swap between the two by pressing L/R in the menu.
|
||||
- **Portal Rod:** Fires a projectile that creates a portal sprite (blue or orange). The `Ancilla_HandlePortalCollision` logic detects when another projectile (like an arrow) hits a portal and teleports it to the other portal's location.
|
||||
- **Fishing Rod:** Initiates a fishing minigame. `LinkItem_FishingRod` spawns a "floater" sprite, and the player can reel it in to catch fish or other items from a prize table.
|
||||
|
||||
## 4. New Passive Items
|
||||
|
||||
### Magic Rings
|
||||
|
||||
- **File:** `magic_rings.asm`
|
||||
- **Functionality:** Passive items that grant buffs when equipped in one of the three ring slots in the Quest Status menu. The effects are applied by hooking into various game logic routines.
|
||||
- **Implemented Rings:**
|
||||
- **Power Ring:** Increases sword damage.
|
||||
- **Armor Ring:** Reduces damage taken by half.
|
||||
- **Heart Ring:** Slowly regenerates health over time.
|
||||
- **Light Ring:** Allows the sword to shoot beams even when Link is not at full health (down to -2 hearts from max).
|
||||
- **Blast Ring:** Increases the damage of bombs.
|
||||
- **Steadfast Ring:** Prevents or reduces knockback from enemy hits.
|
||||
|
||||
## 5. Consumable Items (Magic Bag)
|
||||
|
||||
- **File:** `all_items.asm`
|
||||
- **Functionality:** The Magic Bag is a sub-menu that holds new consumable items. The `Link_ConsumeMagicBagItem` routine is a jump table that executes the effect for the selected item.
|
||||
- **Consumables:**
|
||||
- **Banana:** Implemented. Restores a small amount of health (`#$10`).
|
||||
- **Pineapple, Rock Meat, Seashells, Honeycombs, Deku Sticks:** Placeholder entries exist in the jump table, but their effects are not yet implemented (the routines just contain `RTS`).
|
||||
145
Docs/Features/Masks/Masks.md
Normal file
145
Docs/Features/Masks/Masks.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Mask System
|
||||
|
||||
This document provides a detailed analysis of the Mask System in Oracle of Secrets, based on the code in the `Masks/` directory. The system allows Link to transform into various forms, each with unique graphics, palettes, and abilities.
|
||||
|
||||
## 1. System Architecture
|
||||
|
||||
The Mask System is built around a central WRAM variable and a set of core routines that handle transformations, graphics, and palettes.
|
||||
|
||||
- **`!CurrentMask` (`$02B2`):** A WRAM variable that stores the ID of the currently active mask. A value of `0x00` represents Link's normal human form.
|
||||
- **`!LinkGraphics` (`$BC`):** A WRAM variable that holds the bank number for Link's current graphics sheet. The Mask System changes this value to load the appropriate sprite graphics for each form.
|
||||
|
||||
### Mask IDs
|
||||
|
||||
| ID | Mask / Form |
|
||||
|------|---------------|
|
||||
| `00` | Human (Default) |
|
||||
| `01` | Deku Mask |
|
||||
| `02` | Zora Mask |
|
||||
| `03` | Wolf Mask |
|
||||
| `04` | Bunny Hood |
|
||||
| `05` | Minish Form |
|
||||
| `06` | GBC Form |
|
||||
| `07` | Moosh Form |
|
||||
|
||||
## 2. Core Routines & Hooks (`Masks/mask_routines.asm`)
|
||||
|
||||
A set of shared routines and hooks form the backbone of the system.
|
||||
|
||||
- **`Link_TransformMask`:** This is the primary function for changing forms. It is typically called when the player uses a mask item.
|
||||
- **Trigger:** It requires a new R-button press (`CheckNewRButtonPress`) to prevent rapid toggling.
|
||||
- **Logic:** It takes a mask ID in the A register. If the requested mask is already active, it reverts Link to his human form. Otherwise, it sets `!CurrentMask`, updates the graphics bank in `$BC` from a lookup table, and calls `Palette_ArmorAndGloves` to apply the new look.
|
||||
- **Effect:** It spawns a "poof" of smoke (`AddTransformationCloud`) and plays a sound effect.
|
||||
|
||||
- **`Palette_ArmorAndGloves` (Hook):** This routine hooks the vanilla palette loading function (`$1BEDF9`). It checks `!CurrentMask` and jumps to the appropriate `Update...Palette` routine for the active form, ensuring the correct colors are loaded. If no mask is active, it proceeds with the vanilla logic for loading Link's tunic color.
|
||||
|
||||
- **`LinkItem_CheckForSwordSwing_Masks` (Hook):** This routine hooks the vanilla sword swing check (`$079CD9`). It prevents certain forms (Deku, Wolf, Minish, Moosh) from using the sword, while allowing others (Zora, GBC Link) to use it freely.
|
||||
|
||||
- **Reset Routines:**
|
||||
- `ResetToLinkGraphics`: Reverts Link to his default graphics and `!CurrentMask = 0`.
|
||||
- `ForceResetMask_GameOver` / `ForceResetMask_SaveAndQuit`: Hooks into the game over and save/quit routines to ensure Link's form is reset before the game saves or restarts.
|
||||
|
||||
## 3. Transformation Masks
|
||||
|
||||
These masks grant Link new forms with significant new abilities.
|
||||
|
||||
### Deku Mask
|
||||
|
||||
- **File:** `Masks/deku_mask.asm`
|
||||
- **Transformation:** Replaces the Quake Medallion. Pressing 'Y' with the item selected transforms Link.
|
||||
- **Abilities:**
|
||||
- **Spin Attack:** Pressing 'Y' performs a spinning attack.
|
||||
- **Deku Bubble:** If not on a Deku Flower, the spin attack also shoots a bubble projectile (`Ancilla0E_MagicBubble`).
|
||||
- **Hover:** If standing on a Deku Flower (tile property check sets WRAM `$71`), the spin attack launches Link into the air, allowing him to hover for a short time.
|
||||
- **Bomb Drop:** While hovering, pressing 'Y' drops a bomb.
|
||||
- **Cancel Hover:** Pressing 'B' or letting the timer expire cancels the hover.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x01`
|
||||
- `DekuFloating` (`$70`): Flag set when Link is hovering.
|
||||
- `DekuHover` (`$71`): Flag set when Link is standing on a Deku Flower, enabling the hover ability.
|
||||
- **Code Interactions:**
|
||||
- Hooks `LinkItem_Quake` (`$07A64B`).
|
||||
- Repurposes Link's "Using Quake Medallion" state (`$5D = 0x0A`) for the hover ability.
|
||||
- Hooks `LinkOAM_DrawShield` (`$0DA780`) to prevent the shield from being drawn.
|
||||
|
||||
### Zora Mask
|
||||
|
||||
- **File:** `Masks/zora_mask.asm`
|
||||
- **Transformation:** Replaces the Bombos Medallion. Pressing 'Y' with the item selected transforms Link.
|
||||
- **Abilities:**
|
||||
- **Diving:** Allows Link to dive in deep water by pressing 'Y'. Pressing 'Y' again resurfaces.
|
||||
- **Overworld Diving:** When diving in the overworld, Link becomes invincible, moves faster, and is hidden beneath a ripple effect.
|
||||
- **Dungeon Diving:** When diving in a dungeon, Link moves to the lower layer (`$EE=0`), allowing him to swim under floors and obstacles.
|
||||
- **Sword:** The Zora form can use the sword.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x02`
|
||||
- `!ZoraDiving` (`$0AAB`): Flag set when Link is currently underwater.
|
||||
- **Code Interactions:**
|
||||
- Hooks `LinkItem_Bombos` (`$07A569`).
|
||||
- Hooks the end of `LinkState_Swimming` (`$079781`) to handle the dive input.
|
||||
- Hooks the end of `LinkState_Default` (`$0782D2`) to handle resurfacing in dungeons.
|
||||
- Hooks `Link_HopInOrOutOfWater_Vertical` (`$07C307`) to reset the dive state when using water stairs.
|
||||
|
||||
### Wolf Mask
|
||||
|
||||
- **File:** `Masks/wolf_mask.asm`
|
||||
- **Transformation:** Shares an item slot with the Flute. When selected, it replaces the Shovel. Pressing 'Y' transforms Link.
|
||||
- **Abilities:**
|
||||
- **Dig:** When transformed, pressing 'Y' executes the vanilla `LinkItem_Shovel` routine, allowing Wolf Link to dig for items without needing the shovel.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x03`
|
||||
- **Code Interactions:**
|
||||
- Hooks the `LinkItem_Shovel` vector (`$07A313`) to a new `LinkItem_ShovelAndFlute` routine that dispatches between the Flute and Wolf Mask logic based on the selected item (`$0202`).
|
||||
|
||||
### Minish Form
|
||||
|
||||
- **File:** `Masks/minish_form.asm`
|
||||
- **Transformation:** Context-sensitive. When standing on a special portal tile (`ID 64`), pressing 'R' transforms Link into Minish form. Pressing 'R' on the portal again reverts him.
|
||||
- **Abilities:**
|
||||
- **Access Minish Areas:** Allows Link to pass through special small openings (`Tile ID 65`).
|
||||
- **Restricted Actions:** Cannot lift objects.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x05`
|
||||
- **Code Interactions:**
|
||||
- Hooks the overworld (`$07DAF2`) and underworld (`$07D8A0`) tile collision tables to add handlers for the portal and passage tiles.
|
||||
- Hooks the lift check (`$079C32`) to disable lifting while in Minish form.
|
||||
|
||||
### Moosh Form
|
||||
|
||||
- **File:** `Masks/moosh.asm`
|
||||
- **Transformation:** The trigger for transforming into Moosh is not defined within the mask's own file, but is handled by `Link_TransformMoosh`.
|
||||
- **Abilities:**
|
||||
- **Hover Dash:** Attempting to use the Pegasus Boots (dash) while in Moosh form will instead trigger a short hover, similar to the Deku Mask's ability.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x07`
|
||||
- **Code Interactions:**
|
||||
- Hooks the dash initiation logic (`$079093`) to intercept the dash and call `PrepareQuakeSpell`, which sets Link's state to `0x0A` (hover).
|
||||
- Shares the hover/recoil animation logic with the Deku Mask.
|
||||
|
||||
## 4. Passive & Cosmetic Forms
|
||||
|
||||
### Bunny Hood
|
||||
|
||||
- **File:** `Masks/bunny_hood.asm`
|
||||
- **Transformation:** Replaces the Ether Medallion. Pressing 'Y' activates the Bunny Hood state. This is a state change, not a visual transformation.
|
||||
- **Abilities:**
|
||||
- **Increased Speed:** While the Bunny Hood is the active mask (`!CurrentMask = 0x04`), Link's movement speed is increased across various actions (walking, carrying, etc.). The specific speed values are defined in `BunnySpeedTable`.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x04`
|
||||
- **Code Interactions:**
|
||||
- Hooks `LinkItem_Ether` (`$07A494`) to trigger the state change.
|
||||
- Hooks the velocity calculation in the player engine (`$07E330`) to load custom speed values from a table.
|
||||
|
||||
### GBC Form
|
||||
|
||||
- **File:** `Masks/gbc_form.asm`
|
||||
- **Transformation:** An automatic, cosmetic transformation that occurs whenever Link is in the Dark World.
|
||||
- **Abilities:**
|
||||
- Changes Link's graphics to a Game Boy Color-inspired sprite.
|
||||
- Applies a unique, limited-color palette. The palette correctly reflects Link's current tunic (Green, Blue, or Red).
|
||||
- This form can still use the sword.
|
||||
- **Key Flags & Variables:**
|
||||
- `!CurrentMask`: `0x06`
|
||||
- `$0FFF`: The vanilla Dark World flag.
|
||||
- **Code Interactions:**
|
||||
- Hooks numerous overworld, underworld, and transition routines to consistently apply the effect when in the Dark World and remove it when in the Light World.
|
||||
77
Docs/Features/Menu/Menu.md
Normal file
77
Docs/Features/Menu/Menu.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Custom Menu & HUD System
|
||||
|
||||
This document provides a detailed analysis of the custom menu and Heads-Up Display (HUD) systems in Oracle of Secrets, based on the code in the `Menu/` directory.
|
||||
|
||||
## 1. Overview
|
||||
|
||||
The project features a completely custom menu and HUD, replacing the vanilla systems. The menu is a robust, multi-screen system, while the HUD provides a clean, modern interface for in-game stats.
|
||||
|
||||
- **Menu System**: A two-page design that separates selectable items from quest status and equipment. It is accessible by pressing the Start button.
|
||||
- **HUD System**: A persistent on-screen display for health, magic, rupees, and the currently equipped item.
|
||||
|
||||
## 2. Menu System Architecture
|
||||
|
||||
The entire menu operates as a large state machine, with the main entry point being `Menu_Entry` in `Menu/menu.asm`. The flow is controlled by the value in WRAM `$0200`.
|
||||
|
||||
### 2.1. Main State Machine
|
||||
|
||||
The `Menu_Entry` routine uses a jump table (`.vectors`) to execute different subroutines based on the state in `$0200`. This modular approach allows for a clean separation of tasks like initialization, drawing, input handling, and screen transitions.
|
||||
|
||||
**Key States in `$0200`:**
|
||||
|
||||
| State ID | Label | Purpose |
|
||||
|----------|----------------------------|-------------------------------------------------------------------------|
|
||||
| `0x00` | `Menu_InitGraphics` | Initializes the menu, clears player state, and prepares for drawing. |
|
||||
| `0x01` | `Menu_UploadRight` | Draws the entire right-hand screen (Quest Status). |
|
||||
| `0x02` | `Menu_UploadLeft` | Draws the entire left-hand screen (Item Selection). |
|
||||
| `0x04` | `Menu_ItemScreen` | The main interactive state for the Item screen. Handles cursor movement. |
|
||||
| `0x05` | `Menu_ScrollTo` | Handles the smooth scrolling animation when moving from Items to Quest. |
|
||||
| `0x06` | `Menu_StatsScreen` | The main interactive state for the Quest Status screen. |
|
||||
| `0x0A` | `Menu_Exit` | Exits the menu, restores the game state, and updates the equipped item. |
|
||||
| `0x0C` | `Menu_MagicBag` | A sub-menu for viewing collectible items. |
|
||||
| `0x0D` | `Menu_SongMenu` | A sub-menu for selecting Ocarina songs. |
|
||||
| `0x0E` | `Menu_Journal` | A sub-menu for reading the player's journal. |
|
||||
| `0x09` | `Menu_RingBox` | A sub-menu for managing magic rings. |
|
||||
|
||||
### 2.2. Item Selection Screen (Left Page)
|
||||
|
||||
This is the primary interactive screen where the player selects their Y-button item.
|
||||
|
||||
- **Drawing (`DrawYItems`):** This routine is responsible for rendering all 24 item slots. It reads the SRAM address for each slot from `Menu_AddressIndex` (`menu_select_item.asm`), checks if the player owns the item, and then calls `DrawMenuItem`.
|
||||
- **`DrawMenuItem`:** This generic function is the core of the drawing system. It takes an item's SRAM value (e.g., Sword level 0-4) and uses it to look up the correct 16x16 tile data from a large graphics table in `Menu/menu_gfx_table.asm`. This makes the menu highly data-driven.
|
||||
- **Selection (`menu_select_item.asm`):** Cursor movement is handled by `Menu_FindNextItem`, `Menu_FindPrevItem`, etc. These routines intelligently skip over empty slots, ensuring the cursor always lands on a valid item. The currently selected slot index is stored in `$0202`.
|
||||
|
||||
### 2.3. Quest Status Screen (Right Page)
|
||||
|
||||
This screen is a static display of the player's overall progress.
|
||||
|
||||
- **Drawing:** It is rendered by a series of functions in `menu_draw.asm`, including:
|
||||
- `Menu_DrawQuestItems`: Draws equipped sword, shield, tunic, etc.
|
||||
- `Menu_DrawPendantIcons` & `Menu_DrawTriforceIcons`: Reads SRAM flags to draw collected pendants and crystals.
|
||||
- `Menu_DrawCharacterName`: Reads the player's name from SRAM and renders it.
|
||||
- `DrawLocationName`: Reads the current overworld area (`$008A`) or underworld room (`$00A0`) and looks up the corresponding name from the tables in `menu_map_names.asm`.
|
||||
|
||||
## 3. HUD System Architecture
|
||||
|
||||
The HUD is a separate system that hooks the vanilla game's NMI rendering routines. Its main entry point is `HUD_Update` in `Menu/menu_hud.asm`.
|
||||
|
||||
- **Functionality:** The `HUD_Update` routine runs every frame during gameplay. It reads player stats directly from SRAM and WRAM and draws them to the VRAM buffer for the top of the screen.
|
||||
- **Key Drawing Logic:**
|
||||
- **Hearts:** `HUD_UpdateHearts` is a loop that draws empty hearts based on `MAXHP` (`$7EF36C`) and then overlays full/partial hearts based on `CURHP` (`$7EF36D`).
|
||||
- **Magic Meter:** It reads `MagicPower` (`$7EF36E`) and uses the `MagicTilemap` lookup table to find the correct tiles to display the green bar.
|
||||
- **Counters:** It uses a `HexToDecimal` routine to convert the values for Rupees, Bombs, and Arrows into drawable digits.
|
||||
- **Equipped Item:** `HUD_UpdateItemBox` reads the currently equipped item index (`$0202`), finds its graphics data in the `HudItems` table, and draws the icon in the top-left box.
|
||||
|
||||
## 4. Data-Driven Design & Areas for Improvement
|
||||
|
||||
The entire menu and HUD are heavily data-driven, which is a major strength.
|
||||
|
||||
- **Graphics:** All item icons for both the menu and HUD are defined in data tables in `menu_gfx_table.asm` and `menu_hud.asm`, not hardcoded.
|
||||
- **Item Layout:** The position and SRAM address of every item in the menu are defined in the `Menu_ItemCursorPositions` and `Menu_AddressIndex` tables, allowing the layout to be easily changed.
|
||||
- **Text:** Item names, location names, and other text are all stored in data tables in `menu_text.asm` and `menu_map_names.asm`.
|
||||
|
||||
This analysis confirms the suggestions in the placeholder `Menu.md` file:
|
||||
|
||||
1. **Refactor Redundant Code:** The input handling logic for the Magic Bag, Song Menu, and Ring Box is nearly identical and is a prime candidate for being refactored into a single, reusable subroutine.
|
||||
2. **Use `table` for Jump Tables:** The main `Menu_Entry` jump table is created with manual `dw` directives and would be cleaner and safer if generated with asar's `table` directive.
|
||||
3. **Replace Hardcoded Values:** Hardcoded state values (e.g., `LDA.b #$0C : STA.w $0200`) should be replaced with named constants (`!MENU_STATE_MAGIC_BAG = $0C`) for readability and maintainability.
|
||||
137
Docs/Features/Music/Music.md
Normal file
137
Docs/Features/Music/Music.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Music Creation Guide
|
||||
|
||||
This document details the process for creating and integrating custom music into Oracle of Secrets. The project uses the native Super Nintendo Packet Chip (N-SPC) music format, abstracted through a powerful set of `asar` macros.
|
||||
|
||||
## 1. N-SPC Music Format Primer
|
||||
|
||||
Music in the N-SPC engine is structured around eight independent channels. Each channel is a stream of bytes that are read sequentially. The stream consists of two types of data:
|
||||
|
||||
- **Commands:** Special bytes (from `$E0` to `$FF`) that control aspects of the sound, such as setting the instrument, changing volume, or calling a subroutine.
|
||||
- **Notes:** A note consists of a duration byte followed by one or more tone bytes. The duration byte (e.g., `$48` for a quarter note) determines how long the following tone(s) will play.
|
||||
|
||||
## 2. The Macro System (`Core/music_macros.asm`)
|
||||
|
||||
To make composing more intuitive, the project uses a comprehensive library of macros that wrap the raw N-SPC commands into readable names. All music files must include `Core/music_macros.asm`.
|
||||
|
||||
### Key Concepts
|
||||
|
||||
- **Note Durations:** Constants are defined for standard note lengths (e.g., `!4th`, `!8th`, `!16th`).
|
||||
- **Note Tones:** Constants are defined for all notes across several octaves (e.g., `C4`, `G4s` for G#4, `A5`).
|
||||
- **Special Notes:** `Tie` (`$C8`) continues the previous note for the new duration, and `Rest` (`$C9`) signifies silence.
|
||||
|
||||
### Core Macros
|
||||
|
||||
- **`%SetInstrument(id)`:** Sets the instrument for the current channel (e.g., `%SetInstrument($09)` for Strings). Helper macros like `%Strings()`, `%Piano()`, etc., exist for common instruments.
|
||||
- **`%SetTempo(value)`:** Sets the overall playback speed of the song.
|
||||
- **`%SetMasterVolume(value)` / `%SetChannelVolume(value)`:** Sets the volume for the entire song or just the current channel.
|
||||
- **`%CallSubroutine(address, repeats)`:** The most important macro for structuring songs. It jumps to a labeled subroutine, plays it `repeats+1` times, and then returns. **This is the primary method for looping musical phrases.**
|
||||
- **`%VibratoOn(delay, rate, depth)`:** Adds a vibrato effect.
|
||||
- **`%TremoloOn(delay, rate, depth)`:** Adds a tremolo (volume fluctuation) effect.
|
||||
- **`%SetPan(value)`:** Sets the stereo position (left/right) of the channel.
|
||||
- **`%EchoVBits(switch, left, right)`:** Enables and configures echo for the channel.
|
||||
|
||||
## 3. Song File Structure
|
||||
|
||||
Every song `.asm` file follows a standard structure.
|
||||
|
||||
#### 1. Header
|
||||
|
||||
The file begins with a header that defines metadata for the song engine.
|
||||
|
||||
```asm
|
||||
MyNewSong:
|
||||
!ARAMAddr = $D86A ; Base address in ARAM for this song
|
||||
dw !ARAMAddr+$0A ; Pointer to the Intro section
|
||||
dw !ARAMAddr+$1A ; Pointer to the Main (looping) section
|
||||
dw $00FF ; Default fade-in
|
||||
dw !ARAMAddr+$02 ; Start of the looping section data
|
||||
dw $0000
|
||||
```
|
||||
|
||||
#### 2. Channel Pointers
|
||||
|
||||
Next is a table of pointers to each of the eight channel data blocks. The `!ARAMC` constant is used to make these pointers relative to the song's ARAM address.
|
||||
|
||||
```asm
|
||||
.Channels
|
||||
!ARAMC = !ARAMAddr-MyNewSong
|
||||
dw .Channel0+!ARAMC
|
||||
dw .Channel1+!ARAMC
|
||||
; ...up to 8 channels, use dw $0000 for unused channels
|
||||
```
|
||||
|
||||
#### 3. Channel Data
|
||||
|
||||
Each channel is a block of code containing commands and notes.
|
||||
|
||||
```asm
|
||||
.Channel0
|
||||
%SetMasterVolume($DA)
|
||||
%SetTempo(62)
|
||||
%SetInstrument($02) ; Tympani
|
||||
%SetDurationN(!4th, $7F)
|
||||
%CallSubroutine(.sub1+!ARAMC, 23) ; Call subroutine .sub1 24 times
|
||||
db End ; $00, signifies end of channel data
|
||||
```
|
||||
|
||||
#### 4. Subroutines
|
||||
|
||||
The bulk of a song is made of small, labeled subroutines containing musical phrases. These are placed after the channel data.
|
||||
|
||||
```asm
|
||||
.sub1
|
||||
db !4th, B1, B1, !8th, Tie, C2, !4th, F3s
|
||||
db End ; Subroutines must also end with $00
|
||||
```
|
||||
|
||||
## 4. How to Add a New Song
|
||||
|
||||
1. **Create the File:** Create a new `.asm` file in the `Music/` directory.
|
||||
2. **Copy Template:** Copy the contents of an existing song (e.g., `stone_tower_temple_v2.asm`) into your new file to use as a template.
|
||||
3. **Set Header:** Change the main label (e.g., `MyNewSong:`) and set the `!ARAMAddr`. This address must be unique and not conflict with other songs.
|
||||
4. **Compose:** Write your music in the channel and subroutine blocks using the note constants and macros.
|
||||
5. **Integrate the Song:**
|
||||
- Open `Music/all_music.asm` and add an `incsrc` for your new file.
|
||||
- To replace a vanilla song, find its label in the ROM map and use `org` to place your new song at that address. For example, to replace the Lost Woods theme:
|
||||
```asm
|
||||
org $1AADDE ; Original address of Lost Woods theme
|
||||
incsrc "Music/MyNewSong.asm"
|
||||
```
|
||||
- To add a new song to the expanded Dark World bank, open `Music/expanded.asm` and add a new entry to the `SongBank_OverworldExpanded_Main` table.
|
||||
|
||||
## 5. Proposals for Improved Organization
|
||||
|
||||
The current system is functional but can be made more readable and maintainable.
|
||||
|
||||
1. **Standardize Subroutine Naming:** The current convention of `.sub1`, `.sub101`, etc., is ambiguous. A clearer naming scheme would greatly improve readability.
|
||||
- **Proposal:** Name subroutines based on their musical function, like `.MelodyVerseA`, `.BasslineIntro`, `.PercussionFill1`. This makes the main channel blocks easier to read as a high-level song structure.
|
||||
|
||||
2. **Create a Common Patterns Library:** Many songs use similar rhythmic or melodic patterns (e.g., a standard 4/4 drum beat, an arpeggiated chord).
|
||||
- **Proposal:** Create a `Music/common_patterns.asm` file. This file could contain a library of generic, reusable subroutines for things like drum patterns, basslines, or common arpeggios. Songs could then `incsrc` this library and call these patterns, reducing code duplication and speeding up composition.
|
||||
|
||||
3. **Develop Advanced Composition Macros:** The existing helper macros are basic. More advanced macros could abstract away the manual process of defining and calling subroutines.
|
||||
- **Proposal:**
|
||||
- `%DefineMeasure(Name, Notes...)`: A macro that takes a name and a list of notes and automatically creates a correctly formatted subroutine block.
|
||||
- `%PlayMeasure(Name, Repeats)`: A macro that automatically calculates the relative address (`+!ARAMC`) and calls `%CallSubroutine`.
|
||||
|
||||
- **Example Workflow with Proposed Macros:**
|
||||
```asm
|
||||
; --- Subroutine Definitions ---
|
||||
%DefineMeasure(VerseMelody, !8th, C4, D4, E4, F4, G4, A4, B4, C5)
|
||||
%DefineMeasure(VerseBass, !4th, C2, G2, A2, F2)
|
||||
|
||||
; --- Channel Data ---
|
||||
.Channel0
|
||||
; ... setup ...
|
||||
%PlayMeasure(VerseMelody, 4) ; Plays the melody 4 times
|
||||
db End
|
||||
|
||||
.Channel1
|
||||
; ... setup ...
|
||||
%PlayMeasure(VerseBass, 4) ; Plays the bassline 4 times
|
||||
db End
|
||||
```
|
||||
This approach would make the main channel data blocks read like a high-level song arrangement, significantly improving clarity.
|
||||
|
||||
4. **Improve In-File Documentation:**
|
||||
- **Proposal:** Encourage the use of comments to label major song sections directly within the channel data (e.g., `; --- VERSE 1 ---`, `; --- CHORUS ---`). This provides crucial signposting when navigating complex song files.
|
||||
Reference in New Issue
Block a user