- 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.
14 KiB
Goriya Sprite Analysis
This document provides a detailed analysis of the goriya.asm sprite, outlining its properties, core routines, and behavioral patterns.
1. Sprite Properties
The following !SPRID constants define Goriya's fundamental characteristics:
!SPRID = Sprite_Goriya
!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 = 00 ; Number of Health the sprite have (dynamically set in _Prep)
!Damage = 00 ; (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 and !Damage are initially set to 00 but are dynamically determined during initialization.
2. Core Routines
2.1. Sprite_Goriya_Long (Main Loop)
This is the primary entry point for Goriya's per-frame execution, called by the game engine. It handles drawing, shadow rendering, and then dispatches to the main logic routine if the sprite is active. Notably, it checks SprSubtype, X to determine if it's the main Goriya sprite or a boomerang, and calls the appropriate drawing routine.
Sprite_Goriya_Long:
{
PHB : PHK : PLB
LDA.w SprSubtype, X : BEQ + ; Check SprSubtype
JSR Sprite_Boomerang_Draw ; If SprSubtype is not 0, draw as boomerang
JMP ++
+
JSR Sprite_Goriya_Draw ; If SprSubtype is 0, draw as Goriya
JSL Sprite_DrawShadow
++
JSL Sprite_CheckActive : BCC .SpriteIsNotActive ; Check if sprite is active
JSR Sprite_Goriya_Main ; If active, run main logic
.SpriteIsNotActive
PLB
RTL
}
2.2. Sprite_Goriya_Prep (Initialization)
This routine is executed once when Goriya is first spawned. It sets Goriya's health to 08 (one heart).
Sprite_Goriya_Prep:
{
PHB : PHK : PLB
LDA.b #$08 : STA.w SprHealth, X ; Set health to 8
PLB
RTL
}
2.3. Sprite_Goriya_Main (Behavioral State Machine)
This routine manages Goriya's AI through a state machine, using SprAction, X to determine the current behavior. It utilizes JumpTableLocal for efficient state transitions, including walking in different directions and a boomerang attack state.
Sprite_Goriya_Main:
{
LDA.w SprAction, X
JSL JumpTableLocal ; Jump to the routine specified by SprAction
dw Goriya_WalkingUp
dw Goriya_WalkingDown
dw Goriya_WalkingLeft
dw Goriya_WalkingRight
dw BoomerangAttack
Goriya_WalkingUp:
{
%PlayAnimation(0, 1, 10) ; Animate frames 0-1 every 10 frames
JSR Sprite_Goriya_Move ; Handle movement
RTS
}
Goriya_WalkingDown:
{
%PlayAnimation(2, 3, 10) ; Animate frames 2-3 every 10 frames
JSR Sprite_Goriya_Move ; Handle movement
RTS
}
Goriya_WalkingLeft:
{
%StartOnFrame(4)
%PlayAnimation(4, 5, 10) ; Animate frames 4-5 every 10 frames
JSR Sprite_Goriya_Move ; Handle movement
RTS
}
Goriya_WalkingRight:
{
%StartOnFrame(6)
%PlayAnimation(6, 7, 10) ; Animate frames 6-7 every 10 frames
JSR Sprite_Goriya_Move ; Handle movement
RTS
}
BoomerangAttack:
{
%PlayAnimation(0, 3, 6) ; Animate frames 0-3 every 6 frames
LDA.w SprTimerD, X : BNE + ; Check timer D
LDA.b #$16
JSL Sprite_ApplySpeedTowardsPlayer ; Apply speed towards Link
%SetTimerD($50) ; Set timer D
+
JSL Sprite_Move ; Apply velocity
JSL Sprite_SpawnSparkleGarnish ; Spawn sparkle effect
JSL Sprite_CheckDamageToPlayer : BCC .no_dano ; Check if Goriya damages Link
LDA.b #$FF : STA.w SprTimerD, X ; If damaged, set timer D
JSL Sprite_InvertSpeed_XY ; Invert speed
.no_dano
JSL Sprite_CheckDamageFromPlayer : BCC + ; Check if Link damages Goriya
JSL Sprite_InvertSpeed_XY ; If damaged, invert speed
+
JSL Sprite_CheckTileCollision ; Check for tile collision
LDA.w SprCollision, X : BEQ + ; If no collision
STZ.w SprState, X ; Clear sprite state (despawn?)
+
RTS
}
}
2.4. Sprite_Goriya_Move (Movement and Interaction Logic)
This routine is called by the various walking states in Sprite_Goriya_Main to handle Goriya's physical interactions and movement. It also manages the logic for throwing a boomerang and changing movement directions.
Sprite_Goriya_Move:
{
JSL Sprite_Move
JSL Sprite_BounceFromTileCollision
JSL Sprite_PlayerCantPassThrough
JSL Sprite_DamageFlash_Long
JSL Sprite_CheckDamageToPlayer
JSL Sprite_CheckDamageFromPlayer
JSL Sprite_CheckIfRecoiling
JSR Goriya_HandleTileCollision ; Handle tile collision and change direction
LDA.w SprTimerD, X : BNE ++
JSL GetRandomInt : AND.b #$9F : BNE ++
LDA.b #$04 : STA.w SprMiscB, X ; Set SprMiscB for boomerang attack
%SetTimerD($FF)
JSR Goriya_BoomerangAttack ; Spawn boomerang
JMP +
++
LDA.w SprTimerC, X : BNE +
JSL GetRandomInt : AND.b #$03
STA.w SprMiscB, X ; Set SprMiscB for new movement direction
%SetTimerC(60)
+
LDA.w SprMiscB, X
JSL JumpTableLocal ; Jump to movement routine based on SprMiscB
dw Goriya_MoveUp
dw Goriya_MoveDown
dw Goriya_MoveLeft
dw Goriya_MoveRight
dw Goriya_Wait
Goriya_MoveUp:
{
LDA.b #-GoriyaMovementSpeed : STA.w SprYSpeed, X
STZ.w SprXSpeed, X
%GotoAction(0) ; Transition to Goriya_WalkingUp
LDA.b #$00 : STA.w SprMiscE, X
RTS
}
Goriya_MoveDown:
{
LDA.b #GoriyaMovementSpeed : STA.w SprYSpeed, X
STZ.w SprXSpeed, X
%GotoAction(1) ; Transition to Goriya_WalkingDown
LDA.b #$01 : STA.w SprMiscE, X
RTS
}
Goriya_MoveLeft:
{
STZ.w SprYSpeed, X
LDA.b #-GoriyaMovementSpeed : STA.w SprXSpeed, X
%GotoAction(2) ; Transition to Goriya_WalkingLeft
LDA.b #$02 : STA.w SprMiscE, X
RTS
}
Goriya_MoveRight:
{
STZ.w SprYSpeed, X
LDA.b #GoriyaMovementSpeed : STA.w SprXSpeed, X
%GotoAction(3) ; Transition to Goriya_WalkingRight
LDA.b #$03 : STA.w SprMiscE, X
RTS
}
Goriya_Wait:
{
STZ.w SprXSpeed, X
STZ.w SprYSpeed, X
%GotoAction(0) ; Transition to Goriya_WalkingUp (default)
RTS
}
}
2.5. Goriya_HandleTileCollision
This routine is called to handle Goriya's collision with tiles. Upon collision, it randomly selects a new movement direction and sets a timer (SprTimerC).
Goriya_HandleTileCollision:
{
JSL Sprite_CheckTileCollision
LDA.w SprCollision, X : BEQ ++
JSL GetRandomInt : AND.b #$03 : STA.w SprAction, X ; Randomly choose new direction
+
STA.w SprMiscE, X
%SetTimerC(60) ; Set timer C for 60 frames
++
RTS
}
2.6. Goriya_BoomerangAttack
This routine is responsible for spawning the boomerang sprite. It sets up the boomerang's initial properties, including its SprSubtype (to differentiate it from the main Goriya), action, position, and health.
Goriya_BoomerangAttack:
{
LDA.b #$2C ; Sprite ID for boomerang (assuming $2C is the boomerang sprite ID)
JSL Sprite_SpawnDynamically : BMI + ; Spawn a new sprite dynamically
LDA.b #$01 : STA.w SprSubtype, Y ; Set SprSubtype to 1 for the boomerang
LDA.b #$04 : STA.w SprAction, Y ; Set action for boomerang (e.g., flying)
LDA.w SprX, X : STA.w SprX, Y ; Copy Goriya's X position to boomerang
LDA.w SprY, X : STA.w SprY, Y ; Copy Goriya's Y position to boomerang
LDA.w SprXH, X : STA.w SprXH, Y
LDA.w SprYH, X : STA.w SprYH, Y
LDA.b #$01 : STA.w SprNbrOAM, Y ; Set number of OAM entries
LDA.b #$40 : STA.w SprHealth, Y ; Set boomerang health
LDA.b #$00 : STA.w SprHitbox, Y ; Set boomerang hitbox
+
RTS
}
2.7. Sprite_Goriya_Draw (Goriya Drawing Routine)
This routine is responsible for rendering Goriya's graphics. It uses a custom OAM allocation and manipulation logic to handle its multi-tile appearance and animation. It dynamically determines the animation frame and applies offsets for each tile.
Sprite_Goriya_Draw:
{
JSL Sprite_PrepOamCoord
JSL Sprite_OAM_AllocateDeferToPlayer
LDA.w SprGfx, X : CLC : ADC.w SprFrame, X : TAY ; Calculate animation frame index
LDA.w .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 : CLC : ADC .x_offsets, X : 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
; Put the sprite out of the way
LDA.b #$F0 : STA ($90), Y : 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
.start_index
db $00, $02, $04, $06, $08, $0A, $0C, $0E
.nbr_of_tiles
db 1, 1, 1, 1, 1, 1, 1, 1
.x_offsets
dw 0, 0
dw 0, 0
dw 0, 0
dw 0, 0
dw 0, 0
dw 0, 0
dw 0, 0
dw 0, 0
.y_offsets
dw 0, -9
dw 0, -9
dw 0, -10
dw 0, -10
dw 0, -10
dw 0, -9
dw 0, -9
dw -1, -10
.chr
; Body Head
db $E4, $C0
db $E4, $C0
db $E6, $C2
db $E6, $C2
db $E2, $C4
db $E0, $C4
db $E2, $C4
db $E0, $C4
.properties
db $2D, $2D
db $6D, $2D
db $2D, $2D
db $6D, $2D
db $2D, $2D
db $2D, $2D
db $6D, $6D
db $6D, $6D
}
2.8. Sprite_Boomerang_Draw (Boomerang Drawing Routine)
This routine is responsible for rendering the boomerang's graphics. It also uses a custom OAM allocation and manipulation logic.
Sprite_Boomerang_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 : 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 #$02 : ORA $0F : STA ($92), Y ; store size in oam buffer
PLY : INY
PLX : DEX : BPL .nextTile
PLX
RTS
.start_index
db $00, $01, $02, $03
.nbr_of_tiles
db 0, 0, 0, 0
.chr
db $26
db $26
db $26
db $26
.properties
db $22
db $A2
db $E2
db $62
}
3. Key Behaviors and Implementation Details
- Dual Role (Goriya and Boomerang): The
Sprite_Goriya_Longroutine usesSprSubtype, Xto determine if the current sprite instance is the main Goriya or a boomerang it has thrown. This allows a single sprite ID to manage two distinct entities. - Boomerang Attack: Goriya actively throws a boomerang (
Goriya_BoomerangAttack) at Link, which then becomes an independent sprite with its own drawing and movement logic (BoomerangAttackstate inSprite_Goriya_MainandSprite_Boomerang_Draw). - Dynamic Health: Goriya's health is set to a fixed value of
08(one heart) during initialization. - State Management: Goriya uses
SprAction, XandJumpTableLocalto manage its walking states (Goriya_WalkingUp,Goriya_WalkingDown,Goriya_WalkingLeft,Goriya_WalkingRight) and itsBoomerangAttackstate. - Movement Patterns: Goriya moves in random directions, with
Goriya_HandleTileCollisiontriggering a new random direction upon hitting a tile. It also has aGoriya_Waitstate. - Custom OAM Drawing: Both the Goriya and its boomerang utilize custom OAM drawing routines (
Sprite_Goriya_DrawandSprite_Boomerang_Draw) for precise control over their multi-tile graphics and animation. The use ofREP/SEPfor 16-bit coordinate calculations is present in both. - Code Reuse: The
Goriya_HandleTileCollisionroutine is notably reused by the Darknut sprite, indicating a shared and modular approach to tile collision handling for certain enemy types. SprMiscBUsage: This variable is used to store the current movement direction (0-4) for Goriya's random movement.SprMiscEUsage: This variable also stores the current movement direction, likely for animation or other directional logic.SprTimerCandSprTimerDUsage: These timers are used to control the duration of movement states and the frequency of boomerang attacks.