Files
oracle-of-secrets/Docs/Guides/SpriteCreationGuide.md
scawful 6ba634caa4 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.
2025-10-02 12:44:30 -04:00

145 lines
4.9 KiB
Markdown

# Sprite Creation Guide
This guide provides a step-by-step walkthrough for creating a new custom sprite in Oracle of Secrets using the project's modern sprite system.
## 1. File Setup
1. **Create the Sprite File:** Create a new `.asm` file for your sprite in the appropriate subdirectory of `Sprites/` (e.g., `Sprites/Enemies/MyNewEnemy.asm`).
2. **Include the File:** Open `Sprites/all_sprites.asm` and add an `incsrc` directive to include your new file. Make sure to place it in the correct bank section (e.g., Bank 30, 31, or 32) to ensure it gets compiled into the ROM.
```asm
; In Sprites/all_sprites.asm
org $318000 ; Bank 31
...
incsrc "Sprites/Enemies/MyNewEnemy.asm"
```
## 2. Sprite Properties
At the top of your new sprite file, define its core properties using the provided template. These `!` constants are used by the `%Set_Sprite_Properties` macro to automatically configure the sprite's behavior and integrate it into the game.
```asm
; Properties for MyNewEnemy
!SPRID = $XX ; CHOOSE AN UNUSED SPRITE ID!
!NbrTiles = 02 ; Number of 8x8 tiles used in the largest frame
!Health = 10 ; Health points
!Damage = 04 ; Damage dealt to Link on contact (04 = half a heart)
!Harmless = 00 ; 00 = Harmful, 01 = Harmless
!Hitbox = 08 ; Hitbox size (0-31)
!ImperviousAll = 00 ; 01 = All attacks clink harmlessly
!Statue = 00 ; 01 = Behaves like a solid statue
!Prize = 01 ; Prize pack dropped on death (0-15)
; ... and so on for all properties ...
; This macro MUST be called after the properties
%Set_Sprite_Properties(Sprite_MyNewEnemy_Prep, Sprite_MyNewEnemy_Long)
```
## 3. Main Structure (`_Long` routine)
This is the main entry point for your sprite, called by the game engine every frame. Its primary job is to call the drawing and logic routines.
```asm
Sprite_MyNewEnemy_Long:
{
PHB : PHK : PLB ; Set up bank registers
JSR Sprite_MyNewEnemy_Draw
JSL Sprite_DrawShadow ; Optional: Draw a shadow
JSL Sprite_CheckActive : BCC .SpriteIsNotActive ; Only run logic if active
JSR Sprite_MyNewEnemy_Main
.SpriteIsNotActive
PLB ; Restore bank register
RTL ; Return from long routine
}
```
## 4. Initialization (`_Prep` routine)
This routine runs *once* when the sprite is first spawned. Use it to set initial values for timers, its action state, and any other properties.
```asm
Sprite_MyNewEnemy_Prep:
{
PHB : PHK : PLB
%GotoAction(0) ; Set the initial state to the first one in the jump table
%SetTimerA(120) ; Set a general-purpose timer to 120 frames (2 seconds)
PLB
RTL
}
```
## 5. Main Logic & State Machine (`_Main` routine)
This is the heart of your sprite. Use the `%SpriteJumpTable` macro to create a state machine. The sprite's current state is stored in `SprAction, X`.
```asm
Sprite_MyNewEnemy_Main:
{
%SpriteJumpTable(State_Idle, State_Attacking, State_Hurt)
State_Idle:
{
%PlayAnimation(0, 1, 15) ; Animate between frames 0 and 1 every 15 game frames
; Check distance to player. If less than 80 pixels, switch to attacking state.
JSL GetDistance8bit_Long : CMP.b #$50 : BCS .player_is_far
%GotoAction(1) ; Switch to State_Attacking
.player_is_far
RTS
}
State_Attacking:
{
%PlayAnimation(2, 3, 8)
%MoveTowardPlayer(12) ; Move toward the player with speed 12
%DoDamageToPlayerSameLayerOnContact()
; Check if the player has hit the sprite
JSL Sprite_CheckDamageFromPlayer : BCC .no_damage
%GotoAction(2) ; Switch to State_Hurt
.no_damage
RTS
}
State_Hurt:
{
; Sprite was hit, flash and get knocked back
JSL Sprite_DamageFlash_Long
RTS
}
}
```
## 6. Drawing (`_Draw` routine)
This routine renders your sprite's graphics. The easiest method is to use the `%DrawSprite()` macro, which reads from a set of data tables you define.
```asm
Sprite_MyNewEnemy_Draw:
{
%DrawSprite()
; --- OAM Data Tables ---
.start_index ; Starting index in the tables for each animation frame
db $00, $02, $04, $06
.nbr_of_tiles ; Number of tiles to draw for each frame (minus 1)
db 1, 1, 1, 1
.x_offsets ; X-position offset for each tile
dw -8, 8, -8, 8, -8, 8, -8, 8
.y_offsets ; Y-position offset for each tile
dw -8, -8, -8, -8, -8, -8, -8, -8
.chr ; The character (tile) number from the graphics sheet
db $C0, $C2, $C4, $C6, $C8, $CA, $CC, $CE
.properties ; OAM properties (palette, priority, flips)
db $3B, $7B, $3B, $7B, $3B, $7B, $3B, $7B
.sizes ; Size of each tile (e.g., $02 for 16x16)
db $02, $02, $02, $02, $02, $02, $02, $02
}
```
## 7. Final Integration
The `%Set_Sprite_Properties()` macro you added in Step 2 handles the final integration. It automatically adds your sprite's `_Prep` and `_Long` routines to the game's sprite tables in `Core/sprite_new_table.asm`. Your sprite is now ready to be placed in the game world!