Files
oracle-of-secrets/Docs/Sprites/Leever.md
scawful 8c3bf9d95b Add documentation for custom enemy sprites: Eon Scrub, Keese, Leever, and Octorok
- Created detailed markdown files for Eon Scrub, Keese, Leever, and Octorok sprites.
- Included sprite properties, main logic, drawing routines, and design patterns for each sprite.
- Highlighted unique behaviors, state machines, and interactions with the player for each enemy type.
- Ensured clarity in the implementation details and provided assembly code snippets for reference.
2025-10-03 00:31:19 -04:00

6.0 KiB

Leever

Overview

The Leever sprite is a custom implementation that overrides the vanilla Leever behavior (Sprite_71_Leever). It features distinct states for being underground, emerging, attacking, and digging back down, with randomized timers controlling its transitions.

Vanilla Override

This custom Leever implementation hooks into the vanilla sprite ID $71. It uses a custom flag at $0FFF to determine whether to execute its custom logic (Sprite_Leever_Long) or fall back to the original vanilla Leever behavior (Sprite_71_Leever).

pushpc

Sprite_71_Leever =  $06CBA2

org $069365 : dw Sprite_71_Leever_Alt

Sprite_71_Leever_Alt:
{
  LDA.w $0FFF : BEQ +
    JSL Sprite_Leever_Long
    JMP ++
  +
  JSR Sprite_71_Leever
  ++
  RTS
}
assert pc() <= $06A5C0

pullpc

Sprite Properties

Explicit sprite properties (!SPRID, !Health, etc.) are not defined within this file, suggesting it either inherits vanilla properties for sprite ID $71 or these are defined in a separate configuration file.

Main Structure (Sprite_Leever_Long)

This routine is the main entry point for the custom Leever logic, executed every frame. It handles bank setup, conditional drawing (skipping drawing when underground), and dispatches to the main logic if the sprite is active.

Sprite_Leever_Long:
{
  PHB : PHK : PLB
  LDA.w SprAction, X : BEQ +
    JSR Sprite_Leever_Draw
  +
  JSL Sprite_CheckActive : BCC .SpriteIsNotActive
    JSR Sprite_Leever_Main
  .SpriteIsNotActive
  PLB
  RTL
}

Movement Routine (Sprite_Leever_Move)

A shared routine for handling the Leever's movement, including applying speed towards the player, moving the sprite, and bouncing off tiles.

Sprite_Leever_Move:
{
  JSL Sprite_ApplySpeedTowardsPlayer
  JSL Sprite_Move
  JSL Sprite_BounceFromTileCollision
  RTS
}

Main Logic & State Machine (Sprite_Leever_Main)

The Leever's core behavior is managed by a state machine with four distinct states:

  • Leever_Underground: The Leever moves underground. After a timer (SprTimerA) expires, it transitions to Leever_Emerge.
  • Leever_Emerge: The Leever plays a backwards animation as it emerges. After a randomized timer, it transitions to Leever_Attack.
  • Leever_Attack: The Leever plays an attack animation, checks for damage to/from Link, and moves. After a timer, it transitions to Leever_Dig.
  • Leever_Dig: The Leever plays an animation as it digs back into the ground. After a randomized timer, it transitions back to Leever_Underground.
Sprite_Leever_Main:
{
  JSL Sprite_DamageFlash_Long
  LDA.w SprAction, X
  JSL JumpTableLocal

  dw Leever_Underground
  dw Leever_Emerge
  dw Leever_Attack
  dw Leever_Dig

  Leever_Underground:
  {
    LDA.w SprTimerA, X : BNE +
      LDA.b #$40 : STA.w SprTimerA, X
      INC.w SprAction, X
    +
    LDA.b #$10
    JSR Sprite_Leever_Move
    RTS
  }

  Leever_Emerge:
  {
    %PlayAnimBackwards(3, 2, 10)
    LDA.w SprTimerA, X : BNE +
      JSL GetRandomInt
      AND.b #$3F
      ADC.b #$A0
      STA.w $0DF0,X
      INC.w SprAction, X
      STZ.w SprXSpeed, X : STZ.w SprYSpeed, X
    +
    RTS
  }

  Leever_Attack:
  {
    %PlayAnimation(0, 1, 10)
    LDA.w SprTimerA, X : BNE +
      LDA.b #$7F : STA.w SprTimerA, X
      INC.w SprAction, X
    +
    PHX
    JSL Sprite_CheckIfRecoiling
    JSL Sprite_CheckDamageToPlayerSameLayer
    JSL Sprite_CheckDamageFromPlayer
    PLX
    LDA.b #$0C
    JSR Sprite_Leever_Move
    RTS
  }

  Leever_Dig:
  {
    %PlayAnimation(2, 3, 10)
    LDA.w SprTimerA, X : BNE +
      JSL GetRandomInt
      AND.b #$1F
      ADC.b #$40
      STA.w $0DF0,X
      STZ.w SprAction, X
    +
    LDA.b #$08
    JSR Sprite_Leever_Move
    RTS
  }
}

Drawing (Sprite_Leever_Draw)

The drawing routine handles OAM allocation and animation. It explicitly uses REP #$20 and SEP #$20 for 16-bit coordinate calculations.

Sprite_Leever_Draw:
{
  JSL Sprite_PrepOamCoord
  JSL Sprite_OAM_AllocateDeferToPlayer

  LDA $0DC0, X : CLC : ADC $0D90, 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 : 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

  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 .sizes, X : 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
  .x_offsets
  dw 0
  dw 0
  dw 0
  dw 0
  .y_offsets
  dw 0
  dw 0
  dw 0
  dw 0
  .chr
  db $C4
  db $C6
  db $C2
  db $C0
  .properties
  db $33
  db $33
  db $33
  db $33
  .sizes
  db $02
  db $02
  db $02
  db $02
}

Design Patterns

  • Vanilla Override: Explicitly overrides a vanilla sprite's behavior, demonstrating how to replace existing game logic with custom implementations.
  • Conditional Logic: Uses a custom flag ($0FFF) to dynamically switch between vanilla and custom behaviors, offering flexibility in game design.
  • Emerging/Digging State Machine: Implements a robust state machine to manage the Leever's characteristic emerging from and digging back into the ground, with randomized timers for unpredictable transitions.
  • Animation Control: Utilizes %PlayAnimBackwards for specific animation effects, such as the Leever emerging from the ground.
  • 16-bit OAM Calculations: Demonstrates explicit use of REP #$20 and SEP #$20 for precise 16-bit OAM coordinate calculations, essential for accurate sprite rendering.