- Created detailed documentation for the Minecart sprite, outlining its properties, constants, collision setup, main logic, and design patterns. - Added documentation for the Pedestal sprite, including its vanilla overrides, custom logic for item interaction, and event triggering based on area context. - Introduced documentation for the Portal sprite, detailing its two-way warping system, initialization, main logic, and helper routines for seamless transitions. - Documented the Switch Track sprite, explaining its interactive behavior, state-based animation, and integration with external switches for dynamic track manipulation.
434 lines
13 KiB
Markdown
434 lines
13 KiB
Markdown
# Helmet Chuchu Sprite Analysis
|
|
|
|
This document provides a detailed analysis of the `helmet_chuchu.asm` sprite, outlining its properties, core routines, and behavioral patterns.
|
|
|
|
## 1. Sprite Properties
|
|
|
|
The following `!SPRID` constants define Helmet Chuchu's fundamental characteristics:
|
|
|
|
```asm
|
|
!SPRID = Sprite_HelmetChuchu
|
|
!NbrTiles = 03 ; Number of tiles used in a frame
|
|
!Harmless = 00 ; 00 = Sprite is Harmful, 01 = Sprite is Harmless
|
|
!HVelocity = 00 ; Is your sprite going super fast? put 01 if it is
|
|
!Health = $10 ; Number of Health the sprite have
|
|
!Damage = 04 ; (08 is a whole heart), 04 is half heart
|
|
!DeathAnimation = 00 ; 00 = normal death, 01 = no death animation
|
|
!ImperviousAll = 00 ; 00 = Can be attack, 01 = attack will clink on it
|
|
!SmallShadow = 00 ; 01 = small shadow, 00 = no shadow
|
|
!Shadow = 00 ; 00 = don't draw shadow, 01 = draw a shadow
|
|
!Palette = 00 ; Unused in this template (can be 0 to 7)
|
|
!Hitbox = 00 ; 00 to 31, can be viewed in sprite draw tool
|
|
!Persist = 00 ; 01 = your sprite continue to live offscreen
|
|
!Statis = 00 ; 00 = is sprite is alive?, (kill all enemies room)
|
|
!CollisionLayer = 00 ; 01 = will check both layer for collision
|
|
!CanFall = 00 ; 01 sprite can fall in hole, 01 = can't fall
|
|
!DeflectArrow = 00 ; 01 = deflect arrows
|
|
!WaterSprite = 00 ; 01 = can only walk shallow water
|
|
!Blockable = 00 ; 01 = can be blocked by link's shield?
|
|
!Prize = 00 ; 00-15 = the prize pack the sprite will drop from
|
|
!Sound = 00 ; 01 = Play different sound when taking damage
|
|
!Interaction = 00 ; ?? No documentation
|
|
!Statue = 00 ; 01 = Sprite is statue
|
|
!DeflectProjectiles = 00 ; 01 = Sprite will deflect ALL projectiles
|
|
!ImperviousArrow = 00 ; 01 = Impervious to arrows
|
|
!ImpervSwordHammer = 00 ; 01 = Impervious to sword and hammer attacks
|
|
!Boss = 00 ; 00 = normal sprite, 01 = sprite is a boss
|
|
```
|
|
**Note:** `!Health` is initially set to `$10` but is dynamically determined during initialization based on Link's sword level.
|
|
|
|
## 2. Core Routines
|
|
|
|
### 2.1. `Sprite_HelmetChuchu_Long` (Main Loop)
|
|
|
|
This is the primary entry point for Helmet Chuchu's per-frame execution. It handles drawing, shadow rendering, and then dispatches to the main logic routine if the sprite is active.
|
|
|
|
```asm
|
|
Sprite_HelmetChuchu_Long:
|
|
{
|
|
PHB : PHK : PLB
|
|
JSR Sprite_HelmetChuchu_Draw
|
|
JSL Sprite_DrawShadow
|
|
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
|
|
JSR Sprite_HelmetChuchu_Main
|
|
.SpriteIsNotActive
|
|
PLB
|
|
RTL
|
|
}
|
|
```
|
|
|
|
### 2.2. `Sprite_HelmetChuchu_Prep` (Initialization)
|
|
|
|
This routine is executed once when Helmet Chuchu is first spawned. It sets its health based on Link's sword level, randomly assigns an initial `SprAction` (determining its type and initial frame), and initializes `SprMiscB` and `SprMiscD` to zero.
|
|
|
|
```asm
|
|
Sprite_HelmetChuchu_Prep:
|
|
{
|
|
PHB : PHK : PLB
|
|
LDA.l Sword : DEC A : TAY
|
|
LDA.w .health, Y : STA.w SprHealth, X ; Set health based on sword level
|
|
JSL GetRandomInt : AND.b #$02 : STA.w SprAction, X ; Randomly set initial action (0, 1, or 2)
|
|
STZ.w SprMiscB, X
|
|
STZ.w SprMiscD, X
|
|
LDA.w SprAction, X : BNE +
|
|
LDA.b #$04 : STA.w SprFrame, X ; If action 0, set frame to 4 (Helmet Green)
|
|
+
|
|
CMP.b #$02 : BNE +
|
|
LDA.b #$02 : STA.w SprFrame, X ; If action 2, set frame to 2 (Mask Red)
|
|
+
|
|
PLB
|
|
RTL
|
|
|
|
.health
|
|
db $08, $0C, $0F, $10 ; Health values for each sword level
|
|
}
|
|
```
|
|
|
|
### 2.3. `Sprite_HelmetChuchu_Main` (Behavioral State Machine)
|
|
|
|
This routine manages Helmet Chuchu's AI through a state machine, using `SprAction, X` to determine its current behavior. It includes states for different Chuchu types (Green/Red, Helmet/No Helmet, Mask/No Mask) and separate states for the detached helmet and mask.
|
|
|
|
```asm
|
|
Sprite_HelmetChuchu_Main:
|
|
{
|
|
JSL Sprite_DamageFlash_Long
|
|
%SpriteJumpTable(GreenChuchu_Helmet,
|
|
GreenChuchu_NoHelmet,
|
|
RedChuchu_Masked,
|
|
RedChuchu_NoMask,
|
|
HelmetSubtype,
|
|
MaskSubtype)
|
|
|
|
GreenChuchu_Helmet:
|
|
{
|
|
%StartOnFrame(4)
|
|
%PlayAnimation(4, 5, 16)
|
|
JSR Sprite_CheckForHookshot : BCC +
|
|
LDA.w SprFlash, X : BEQ +
|
|
%GotoAction(1) ; Transition to GreenChuchu_NoHelmet if hookshot hit and not flashing
|
|
+
|
|
JSL Sprite_CheckDamageFromPlayer
|
|
JSR Sprite_Chuchu_Move
|
|
RTS
|
|
}
|
|
|
|
GreenChuchu_NoHelmet:
|
|
{
|
|
%StartOnFrame(0)
|
|
%PlayAnimation(0, 1, 16)
|
|
LDA.w SprMiscD, X : BNE +
|
|
JSR HelmetChuchu_SpawnHookshotDrag ; Spawn detached helmet
|
|
LDA.b #$01 : STA.w SprMiscD, X ; Set flag to prevent re-spawning
|
|
+
|
|
JSL Sprite_CheckDamageFromPlayer
|
|
JSR Sprite_Chuchu_Move
|
|
RTS
|
|
}
|
|
|
|
RedChuchu_Masked:
|
|
{
|
|
%StartOnFrame(2)
|
|
%PlayAnimation(2, 3, 16)
|
|
JSR Sprite_CheckForHookshot : BCC +
|
|
LDA.w SprFlash, X : BEQ +
|
|
%GotoAction(3) ; Transition to RedChuchu_NoMask if hookshot hit and not flashing
|
|
+
|
|
JSL Sprite_CheckDamageFromPlayer
|
|
JSR Sprite_Chuchu_Move
|
|
RTS
|
|
}
|
|
|
|
RedChuchu_NoMask:
|
|
{
|
|
%StartOnFrame(6)
|
|
%PlayAnimation(6, 7, 16)
|
|
LDA.w SprMiscD, X : BNE +
|
|
JSR HelmetChuchu_SpawnHookshotDrag ; Spawn detached mask
|
|
LDA.b #$01 : STA.w SprMiscD, X ; Set flag to prevent re-spawning
|
|
+
|
|
JSL Sprite_CheckDamageFromPlayer
|
|
JSR Sprite_Chuchu_Move
|
|
RTS
|
|
}
|
|
|
|
HelmetSubtype:
|
|
{
|
|
%StartOnFrame(8)
|
|
%PlayAnimation(8, 8, 16)
|
|
JSL Sprite_Move
|
|
JSL Sprite_CheckIfLifted
|
|
JSL Sprite_CheckIfRecoiling
|
|
JSL ThrownSprite_TileAndSpriteInteraction_long
|
|
RTS
|
|
}
|
|
|
|
MaskSubtype:
|
|
{
|
|
%StartOnFrame(8)
|
|
%PlayAnimation(9, 9, 16)
|
|
JSL Sprite_Move
|
|
JSL Sprite_CheckIfLifted
|
|
JSL Sprite_CheckIfRecoiling
|
|
JSL ThrownSprite_TileAndSpriteInteraction_long
|
|
RTS
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2.4. `Sprite_Chuchu_Move` (Movement and Interaction Logic)
|
|
|
|
This routine handles Helmet Chuchu's movement, which involves bouncing towards or recoiling from the player. It uses `SprMiscB, X` to switch between these two behaviors.
|
|
|
|
```asm
|
|
Sprite_Chuchu_Move:
|
|
{
|
|
JSL Sprite_Move
|
|
JSL Sprite_BounceFromTileCollision
|
|
JSL Sprite_PlayerCantPassThrough
|
|
JSL Sprite_CheckIfRecoiling
|
|
|
|
LDA.w SprMiscB, X
|
|
JSL JumpTableLocal
|
|
|
|
dw BounceTowardPlayer
|
|
dw RecoilFromPlayer
|
|
|
|
BounceTowardPlayer:
|
|
{
|
|
JSL GetRandomInt : AND.b #$02 : STA $09 ; Speed
|
|
JSL GetRandomInt : AND.b #$07 : STA $08 ; Height
|
|
|
|
JSL Sprite_MoveAltitude
|
|
DEC.w $0F80,X : DEC.w $0F80,X
|
|
LDA.w SprHeight, X : BPL .aloft
|
|
STZ.w SprHeight, X
|
|
LDA.b $08 : STA.w $0F80, X ; set height from 08
|
|
LDA.b $09
|
|
JSL Sprite_ApplySpeedTowardsPlayer
|
|
.aloft
|
|
LDA.w SprHeight, X : BEQ .dontmove
|
|
JSL Sprite_Move
|
|
.dontmove
|
|
|
|
JSL Sprite_CheckDamageFromPlayer : BCC .no_damage
|
|
INC.w SprMiscB, X ; Switch to RecoilFromPlayer
|
|
LDA.b #$20 : STA.w SprTimerB, X
|
|
.no_damage
|
|
|
|
JSL Sprite_CheckDamageToPlayer : BCC .no_attack
|
|
INC.w SprMiscB, X ; Switch to RecoilFromPlayer
|
|
LDA.b #$20 : STA.w SprTimerB, X
|
|
.no_attack
|
|
|
|
RTS
|
|
}
|
|
|
|
RecoilFromPlayer:
|
|
{
|
|
JSL GetRandomInt : AND.b #$02 : STA $09 ; Speed
|
|
LDA.w SprX, X : CLC : ADC $09 : STA $04
|
|
LDA.w SprY, X : SEC : SBC $09 : STA $06
|
|
LDA.w SprXH, X : ADC #$00 : STA $05
|
|
LDA.w SprYH, X : ADC #$00 : STA $07
|
|
LDA $09 : STA $00 : STA $01
|
|
JSL Sprite_ProjectSpeedTowardsEntityLong
|
|
|
|
LDA.w SprTimerB, X : BNE .not_done
|
|
JSR HelmetChuchu_SpawnHookshotDrag ; Spawn detached helmet/mask
|
|
STZ.w SprMiscB, X ; Switch back to BounceTowardPlayer
|
|
.not_done
|
|
|
|
RTS
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2.5. `HelmetChuchu_SpawnHookshotDrag`
|
|
|
|
This routine is responsible for spawning the detached helmet or mask as a separate sprite when the Chuchu is hit by a hookshot. It determines whether to spawn a helmet or a mask based on the Chuchu's current `SprAction`.
|
|
|
|
```asm
|
|
HelmetChuchu_SpawnHookshotDrag:
|
|
{
|
|
; Based on the subtype either spawn the helmet or the mask
|
|
PHX
|
|
LDA.w SprAction, X : CMP.b #$01 : BEQ .spawn_helmet
|
|
CMP.b #$03 : BEQ .spawn_mask
|
|
|
|
.spawn_helmet
|
|
LDA.b #$05 ; Sprite ID for helmet/mask (assuming $05 is the ID)
|
|
JSL Sprite_SpawnDynamically : BMI .no_space
|
|
LDA.b #$05 : STA.w SprAction, Y ; Set action for detached helmet
|
|
JMP .prepare_mask
|
|
.no_space
|
|
JMP .no_space2
|
|
|
|
.spawn_mask
|
|
LDA.b #$05 ; Sprite ID for helmet/mask
|
|
JSL Sprite_SpawnDynamically : BMI .no_space2
|
|
LDA.b #$04 : STA.w SprAction, Y ; Set action for detached mask
|
|
.prepare_mask
|
|
JSL Sprite_SetSpawnedCoordinates
|
|
LDA.b #$10 : STA.w SprHealth, Y
|
|
LDA.b #$00 : STA.w SprMiscB, Y
|
|
LDA.b #$80 : STA.w SprTimerA, Y
|
|
LDA.b #$01 : STA.w SprNbrOAM, Y
|
|
LDA.w .speed_x, X : STA.w SprXSpeed, Y
|
|
LDA.w .speed_y, X : STA.w SprYSpeed, Y
|
|
.no_space2
|
|
PLX
|
|
RTS
|
|
|
|
.speed_x
|
|
db 16, -11, -16, 11
|
|
|
|
.speed_y
|
|
db 0, 11, 0, -11
|
|
}
|
|
```
|
|
|
|
### 2.6. `Sprite_CheckForHookshot`
|
|
|
|
This routine checks if a hookshot is currently active and interacting with the Chuchu. It iterates through ancilla slots to find a hookshot (`$1F`) and returns with the carry flag set if found.
|
|
|
|
```asm
|
|
Sprite_CheckForHookshot:
|
|
{
|
|
PHX
|
|
LDX.b #$0A
|
|
.next_ancilla
|
|
LDA.w $0C4A, X : CMP.b #$1F : BNE .not_hooker ; Check ancilla type (assuming $1F is hookshot)
|
|
PLX
|
|
SEC ; Carry set if hookshot found
|
|
RTS
|
|
.not_hooker
|
|
DEX
|
|
BPL .next_ancilla
|
|
PLX
|
|
CLC ; Carry clear if no hookshot found
|
|
RTS
|
|
}
|
|
```
|
|
|
|
### 2.7. `Sprite_HelmetChuchu_Draw` (Drawing Routine)
|
|
|
|
This routine is responsible for rendering Helmet Chuchu's graphics. It uses a custom OAM allocation and manipulation logic to handle its multi-tile appearance and animation, dynamically adjusting based on its current state (helmet/mask, color, animation frame).
|
|
|
|
```asm
|
|
Sprite_HelmetChuchu_Draw:
|
|
{
|
|
JSL Sprite_PrepOamCoord
|
|
JSL Sprite_OAM_AllocateDeferToPlayer
|
|
|
|
LDA.w SprGfx, X : CLC : ADC.w SprFrame, X : TAY;Animation Frame
|
|
LDA .start_index, Y : STA $06
|
|
LDA.w SprFlash, X : STA $08
|
|
|
|
PHX
|
|
LDX .nbr_of_tiles, Y ;amount of tiles -1
|
|
LDY.b #$00
|
|
.nextTile
|
|
|
|
PHX ; Save current Tile Index?
|
|
TXA : CLC : ADC $06 ; Add Animation Index Offset
|
|
|
|
PHA ; Keep the value with animation index offset?
|
|
|
|
ASL A : TAX
|
|
|
|
REP #$20
|
|
|
|
LDA $00 : STA ($90), Y
|
|
AND.w #$0100 : STA $0E
|
|
INY
|
|
LDA $02 : CLC : ADC .y_offsets, X : STA ($90), Y
|
|
CLC : ADC #$0010 : CMP.w #$0100
|
|
SEP #$20
|
|
BCC .on_screen_y
|
|
|
|
LDA.b #$F0 : STA ($90), Y ;Put the sprite out of the way
|
|
STA $0E
|
|
.on_screen_y
|
|
|
|
PLX ; Pullback Animation Index Offset (without the *2 not 16bit anymore)
|
|
INY
|
|
LDA .chr, X : STA ($90), Y
|
|
INY
|
|
LDA .properties, X : ORA $08 : STA ($90), Y
|
|
|
|
PHY
|
|
TYA : LSR #2 : TAY
|
|
LDA.b #$02 : ORA $0F : STA ($92), Y ; store size in oam buffer
|
|
PLY : INY
|
|
PLX : DEX : BPL .nextTile
|
|
|
|
PLX
|
|
|
|
RTS
|
|
|
|
|
|
; =======================================================
|
|
; chr prop
|
|
; Mask $04 $37
|
|
; Helmet $08 $3B
|
|
|
|
.start_index
|
|
db $00, $02, $03, $06, $08, $0A, $0C, $0E, $0F, $10
|
|
.nbr_of_tiles
|
|
db 1, 0, 2, 1, 1, 1, 1, 0, 0, 0
|
|
.y_offsets
|
|
dw 0, -8
|
|
dw 0
|
|
dw 0, -8, -8
|
|
dw 0, -4
|
|
dw 0, -8
|
|
dw 0, -4
|
|
dw 0, -8
|
|
dw 0
|
|
dw 0
|
|
dw 0
|
|
.chr
|
|
; No Helmet Green
|
|
db $26, $16
|
|
db $24
|
|
; Mask Red
|
|
db $26, $16, $04
|
|
db $24, $04
|
|
; Helmet Green
|
|
db $26, $08
|
|
db $24, $08
|
|
; No Helmet Red
|
|
db $26, $16
|
|
db $24
|
|
; Mask
|
|
db $04
|
|
; Helmet
|
|
db $08
|
|
.properties
|
|
db $2B, $2B
|
|
db $2B
|
|
db $25, $25, $27
|
|
db $25, $27
|
|
db $2B, $29
|
|
db $2B, $29
|
|
db $25, $25
|
|
db $25
|
|
; mask
|
|
db $27
|
|
; helmet
|
|
db $29
|
|
}
|
|
```
|
|
|
|
## 3. Key Behaviors and Implementation Details
|
|
|
|
* **Dynamic Appearance and State:** Helmet Chuchu is a highly dynamic sprite that changes its appearance and behavior based on whether it has a helmet/mask and its color (green/red). This is managed through its `SprAction` and `SprFrame` values.
|
|
* **Conditional Damage Handling:** The Chuchu's vulnerability to damage is tied to the presence of its helmet or mask. When hit by a hookshot, the helmet/mask detaches, making the Chuchu vulnerable.
|
|
* **Hookshot Interaction:** Special logic (`Sprite_CheckForHookshot`) is implemented to detect interaction with Link's hookshot, which triggers the detachment of the helmet/mask.
|
|
* **Detached Helmet/Mask as Separate Sprites:** When the helmet or mask is detached, it is spawned as an independent sprite (`HelmetSubtype` or `MaskSubtype`) with its own movement (`Sprite_Move`), collision (`ThrownSprite_TileAndSpriteInteraction_long`), and interaction logic. This demonstrates a sophisticated use of child sprites.
|
|
* **Movement Patterns:** The Chuchu moves by bouncing towards (`BounceTowardPlayer`) and recoiling from (`RecoilFromPlayer`) the player, with randomness introduced in speed and height. This creates a distinct and challenging movement pattern.
|
|
* **Custom OAM Drawing:** The `Sprite_HelmetChuchu_Draw` routine is a complex example of custom OAM manipulation. It dynamically selects tiles and properties based on the Chuchu's current state, allowing for seamless transitions between helmeted, masked, and vulnerable forms.
|
|
* **`SprMiscB` Usage:** This variable controls the Chuchu's movement sub-states (`BounceTowardPlayer` and `RecoilFromPlayer`). It also plays a role in the detached helmet/mask sprites.
|
|
* **`SprMiscD` Usage:** This variable acts as a flag to ensure that the `HelmetChuchu_SpawnHookshotDrag` routine is called only once when the helmet/mask is detached.
|
|
* **`SprTimerB` Usage:** Used to control the duration of the recoil state.
|