- 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.
11 KiB
Anti Kirby Sprite Analysis
1. Overview
The Anti Kirby sprite (Sprite_AntiKirby) is an enemy that exhibits unique behaviors, including a "suck" attack that can steal Link's items (bombs, arrows, rupees, or shield). It has distinct states for walking, sucking, being full (after stealing an item), and being "hatted" (presumably after Link gets his item back or a specific condition is met).
2. Sprite Properties
The sprite properties are defined at the beginning of Sprites/Enemies/anti_kirby.asm:
!SPRID = Sprite_AntiKirby
!NbrTiles = 02
!Harmless = 00
!HVelocity = 00
!Health = $08
!Damage = 04
!DeathAnimation = 00
!ImperviousAll = 00
!SmallShadow = 00
!Shadow = 01
!Palette = 00
!Hitbox = 03
!Persist = 00
!Statis = 00
!CollisionLayer = 00
!CanFall = 00
!DeflectArrow = 00
!WaterSprite = 00
!Blockable = 00
!Prize = 00
!Sound = 00
!Interaction = 00
!Statue = 00
!DeflectProjectiles = 00
!ImperviousArrow = 00
!ImpervSwordHammer = 00
!Boss = 00
Key Observations:
!SPRID = Sprite_AntiKirby: This uses a named constant for the sprite ID, which is good practice.!Health = $08: Anti Kirby has 8 health points.!Damage = 04: Deals half a heart of damage to Link.!Hitbox = 03: A relatively small hitbox.!Shadow = 01: It draws a shadow.!Boss = 00: It is not classified as a boss sprite, despite its complex behavior.
3. Main Structure (Sprite_AntiKirby_Long)
This routine follows the standard structure for sprites, calling the draw routine, shadow routine, and then the main logic if the sprite is active.
Sprite_AntiKirby_Long:
{
PHB : PHK : PLB
JSR Sprite_AntiKirby_Draw
JSL Sprite_DrawShadow
JSL Sprite_CheckActive : BCC .SpriteIsNotActive
JSR Sprite_AntiKirby_Main
.SpriteIsNotActive
PLB
RTL
}
4. Initialization (Sprite_AntiKirby_Prep)
The _Prep routine initializes several sprite-specific variables and sets its SprBump, SprHealth, and SprPrize based on Link's current sword level (or a similar progression metric, inferred from LDA.l Sword : DEC : TAY). This is an interesting way to scale enemy difficulty.
Sprite_AntiKirby_Prep:
{
PHB : PHK : PLB
STZ.w SprDefl, X
STZ.w SprTileDie, X
STZ.w SprMiscB, X
LDA.l Sword : DEC : TAY
LDA .bump_damage, Y : STA.w SprBump, X
LDA .health, Y : STA.w SprHealth, X
LDA .prize_pack, Y : STA.w SprPrize, X
PLB
RTL
.bump_damage
db $81, $88, $88, $88
.health
db 06, 10, 20, 20
.prize_pack
db 6, 3, 3, 3
}
Insight: The use of LDA.l Sword : DEC : TAY to index into .bump_damage, .health, and .prize_pack tables demonstrates a dynamic difficulty scaling mechanism based on player progression (likely sword upgrades). This is a valuable pattern for making enemies adapt to the player's power level.
5. Main Logic & State Machine (Sprite_AntiKirby_Main)
The _Main routine implements a complex state machine using JSL JumpTableLocal and a series of dw (define word) entries pointing to different states.
Sprite_AntiKirby_Main:
{
JSL Sprite_IsToRightOfPlayer
TYA : CMP #$01 : BNE .WalkRight
.WalkLeft
LDA.b #$40 : STA.w SprMiscC, X
JMP +
.WalkRight
STZ.w SprMiscC, X
+
JSL Sprite_DamageFlash_Long
JSL Sprite_CheckIfRecoiling
LDA.w SprAction, X
JSL JumpTableLocal
dw AntiKirby_Main ; State 0: Normal movement/attack
dw AntiKirby_Hurt ; State 1: Recoiling from damage
dw AntiKirby_BeginSuck ; State 2: Initiating suck attack
dw AntiKirby_Sucking ; State 3: Actively sucking Link
dw AntiKirby_Full ; State 4: Full after stealing item
dw AntiKirby_Hatted ; State 5: Hatted (after Link gets item back?)
dw AntiKirby_HattedHurt ; State 6: Hatted and hurt
dw AntiKirby_Death ; State 7: Death animation
; ... (State implementations below) ...
}
State Breakdown:
AntiKirby_Main(State 0):- Checks health and transitions to
AntiKirby_Fullif health is low (this seems like a bug, should probably beAntiKirby_Death). - Randomly initiates the
AntiKirby_BeginSuckstate. - Plays walking animation (
%PlayAnimation(0, 2, 10)). - Handles damage from player and transitions to
AntiKirby_Hurt. - Deals damage to Link on contact (
%DoDamageToPlayerSameLayerOnContact()). - Moves toward Link (
%MoveTowardPlayer(8)) and bounces from tile collisions.
- Checks health and transitions to
AntiKirby_Hurt(State 1): Plays a hurt animation and waits for a timer (SprTimerA) to expire before returning toAntiKirby_Main.AntiKirby_BeginSuck(State 2):- Plays a "suck" animation (
%PlayAnimation(4, 5, 10)). - Checks for damage from player.
- Checks Link's proximity (
$0E,$0Fare likely relative X/Y coordinates to Link). If Link is close enough, it transitions toAntiKirby_Suckingand sets up a projectile speed towards Link.
- Plays a "suck" animation (
AntiKirby_Sucking(State 3):- Plays a "sucking" animation (
%PlayAnimation(5, 5, 10)). - Uses
JSL Sprite_DirectionToFacePlayerandJSL DragPlayerto pull Link towards it if he's close enough. - If Link is very close, it "consumes" Link, storing Link's position in
SprMiscBandSprMiscA, sets a timer, and transitions toAntiKirby_Full.
- Plays a "sucking" animation (
AntiKirby_Full(State 4):- Plays a "full" animation (
%PlayAnimation(10, 10, 10)). - Sets Link's position to the stored
SprMiscA/SprMiscB(effectively "spitting" Link out). - Transitions to
AntiKirby_Hattedafter a timer.
- Plays a "full" animation (
AntiKirby_Hatted(State 5):- Plays a "hatted" animation (
%PlayAnimation(6, 8, 10)). - Moves toward Link, deals damage, and handles damage from player (transitions to
AntiKirby_HattedHurt).
- Plays a "hatted" animation (
AntiKirby_HattedHurt(State 6): Plays a hurt animation for the "hatted" state and returns toAntiKirby_Hatted.AntiKirby_Death(State 7): SetsSprStateto$06(likely a death state) and plays a sound effect.
Insight: The AntiKirby_Main state's health check LDA.w SprHealth, X : CMP.b #$01 : BCS .NotDead : %GotoAction(4) seems to incorrectly transition to AntiKirby_Full (State 4) instead of AntiKirby_Death (State 7) when health is 0. This might be a bug or an intentional design choice for a specific game mechanic.
6. Item Stealing Logic (AntiKirby_StealItem)
This is a separate routine that is likely called when Anti Kirby successfully "sucks" Link. It checks Link's inventory and steals a random item (bomb, arrow, rupee, or shield).
AntiKirby_StealItem:
{
REP #$20
; ... (collision checks) ...
SEP #$20
LDA.w SprTimerA, X : CMP.b #$2E : BCS .exit ; Timer check
JSL GetRandomInt
AND.b #$03
INC A
STA.w SprMiscG, X
STA.w SprMiscE, X
CMP.b #$01 : BNE .dont_steal_bomb
LDA.l $7EF343 : BEQ .dont_steal_anything ; Check bombs
DEC A
STA.l $7EF343
RTS
.dont_steal_anything
SEP #$20
STZ.w SprMiscG,X
RTS
.dont_steal_bomb
CMP.b #$02 : BNE .dont_steal_arrow
LDA.l $7EF377 : BEQ .dont_steal_anything ; Check arrows
DEC A
STA.l $7EF377
RTS
.dont_steal_arrow
CMP.b #$03 : BNE .dont_steal_rupee
REP #$20
LDA.l $7EF360 : BEQ .dont_steal_anything ; Check rupees
DEC A
STA.l $7EF360
.exit
SEP #$20
RTS
; -----------------------------------------------------
.dont_steal_rupee
LDA.l $7EF35A : STA.w SprSubtype, X : BEQ .dont_steal_anything ; Check shield
CMP.b #$03 : BEQ .dont_steal_anything
LDA.b #$00
STA.l $7EF35A
RTS
}
Key Observations:
- Uses
REP #$20andSEP #$20to explicitly control the accumulator size (16-bit for address calculations, 8-bit for item counts). This is crucial for correct memory access. - Randomly selects an item to steal using
JSL GetRandomInt : AND.b #$03 : INC A. - Directly modifies SRAM addresses (
$7EF343for bombs,$7EF377for arrows,$7EF360for rupees,$7EF35Afor shield) to decrement item counts or remove the shield. - The shield stealing logic (
LDA.l $7EF35A : STA.w SprSubtype, X : BEQ .dont_steal_anything : CMP.b #$03 : BEQ .dont_steal_anything : LDA.b #$00 : STA.l $7EF35A) is a bit convoluted. It seems to check the shield type and only steals if it's not a specific type (possibly the Mirror Shield, which is type 3).
Insight: The AntiKirby_StealItem routine is a good example of how to interact directly with Link's inventory in SRAM. It also highlights the importance of explicitly managing the processor status flags (REP/SEP) when dealing with mixed 8-bit and 16-bit operations, especially when accessing memory.
7. Drawing (Sprite_AntiKirby_Draw)
The drawing routine uses JSL Sprite_PrepOamCoord and JSL Sprite_OAM_AllocateDeferToPlayer for OAM management. It then uses a series of tables (.start_index, .nbr_of_tiles, .x_offsets, .y_offsets, .chr, .properties, .sizes) to define the sprite's animation frames and tile data.
Sprite_AntiKirby_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
LDA.w SprMiscC, X : STA $09
PHX
LDX .nbr_of_tiles, Y ;amount of tiles -1
LDY.b #$00
.nextTile
; ... (OAM manipulation logic) ...
.start_index
db $00, $01, $02, $03, $04, $05, $06, $08, $0A, $0C, $0E, $10
.nbr_of_tiles
db 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1
; ... (other OAM tables) ...
}
Key Observations:
- The drawing logic includes a check for
SprMiscC, Xto determine if the sprite is facing left or right, and uses different.x_offsetstables (.x_offsetsvs.x_offsets_2) accordingly. This is a common pattern for horizontal flipping. - The
.propertiestable defines the palette, priority, and flip bits for each tile. - The
.sizestable defines the size of each tile (e.g.,$02for 16x16).
8. Advanced Design Patterns Demonstrated
- Dynamic Difficulty Scaling: The
_Preproutine adjusts health, bump damage, and prize based onLink's Swordlevel. - Complex State Machine: The
_Mainroutine uses a jump table to manage multiple distinct behaviors (walking, sucking, full, hatted, hurt, death). - Direct SRAM Interaction: The
AntiKirby_StealItemroutine directly modifies Link's inventory in SRAM, demonstrating how to implement item-related mechanics. - Explicit Processor Status Management: The
AntiKirby_StealItemroutine explicitly usesREP #$20andSEP #$20to ensure correct 8-bit/16-bit operations when accessing SRAM. - Conditional Drawing/Flipping: The
_Drawroutine usesSprMiscC, Xto conditionally flip the sprite horizontally.