Files
oracle-of-secrets/Sprites/Bosses/manhandla.asm

730 lines
17 KiB
NASM

; =========================================================
; Sprite Properties
; =========================================================
!SPRID = $88 ; The sprite ID you are overwriting (HEX)
!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
!Damage = 00 ; (08 is a whole heart), 04 is half heart
!DeathAnimation = 01 ; 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 = 01 ; 00 = normal sprite, 01 = sprite is a boss
%Set_Sprite_Properties(Sprite_Manhandla_Prep, Sprite_Manhandla_Long)
; =========================================================
Sprite_Manhandla_Long:
{
PHB : PHK : PLB
JSR Sprite_Manhandla_CheckForNextPhaseOrDeath
LDA.w SprMiscD, X : BEQ .phase1
JSR Sprite_BigChuchu_Draw
JMP .continue
.phase1
JSR Sprite_Manhandla_Draw
.continue
JSL Sprite_CheckActive ; Check if game is not paused
BCC .SpriteIsNotActive ; Skip Main code is sprite is innactive
JSR Sprite_Manhandla_Main ; Call the main sprite code
.SpriteIsNotActive
PLB ; Get back the databank we stored previously
RTL ; Go back to original code
}
; =========================================================
Sprite_Manhandla_Prep:
{
PHB : PHK : PLB
LDA.b #$04 : STA $36 ; Stores initial movement speeds
LDA.b #$06 : STA $0428 ; Allows BG1 to move
LDA.b #$80 : STA.w SprDefl, X
LDA.b #$20 : STA.w SprHealth, X
LDA.w SprSubtype, X : STA.w SprAction, X
PLB
RTL
}
; If both the heads are dead, it's okay for this sprite to "die"
; Then we transition to the chuchu phase.
Sprite_Manhandla_CheckForNextPhaseOrDeath:
{
LDA Offspring1_Id : TAY
LDA.w SprState, Y : BEQ .offspring1_dead
JMP .not_dead
.offspring1_dead
LDA Offspring2_Id : TAY
LDA.w SprState, Y : BEQ .offspring2_dead
JMP .not_dead
.offspring2_dead
LDA.w SprMiscD, X : BNE .phase2
LDA.w SprHealth, X : CMP.b #$08 : BCS .not_dead
LDA.b #$01 : STA.w SprMiscD, X
LDA.b #$20 : STA.w SprHealth, X
LDA.b #$08 : STA.w SprNbrOAM, X
.not_dead
RTS
.phase2
LDA.w SprMiscD, X : CMP.b #$02 : BEQ +
LDA.w SprHealth, X : CMP.b #$08 : BCS .phase2_not_dead
LDA.b #$20 : STA.w SprTimerA, X
LDA.b #$05 : STA.w SprAction, X
LDA.b #$13 : STA $012C
LDA.b #$02 : STA.w SprMiscD, X
.phase2_not_dead
+
RTS
}
; =========================================================
macro SetLeftHeadPos()
REP #$20
LDA SprCachedX : SEC : SBC.w #$0016
SEP #$20
STA.w SprX, Y : XBA : STA.w SprXH, Y
REP #$20
LDA SprCachedY : SEC : SBC.w #$000F
SEP #$20
STA.w SprY, Y : XBA : STA.w SprYH, Y
endmacro
macro SetRightHeadPos()
REP #$20
LDA SprCachedX : CLC : ADC.w #$0016
SEP #$20
STA.w SprX, Y : XBA : STA.w SprXH, Y
REP #$20
LDA SprCachedY : SEC : SBC.w #$000F
SEP #$20
STA.w SprY, Y : XBA : STA.w SprYH, Y
endmacro
Sprite_Manhandla_Main:
{
LDA.w SprAction, X
JSL UseImplicitRegIndexedLocalJumpTable
dw Manhandla_Intro
dw Manhandla_FrontHead
dw Manhandla_LeftHead
dw Manhandla_RightHead
dw BigChuchu_Main
dw BigChuChu_Dead
Manhandla_Intro:
{
LDA.w SprSubtype, X : BNE .not_main
LDA.w SprX : SEC : SBC.b #$04 : STA.w SprX
JSR ApplyManhandlaGraphics
JSR ApplyManhandlaPalette
JSR SpawnLeftManhandlaHead
JSR SpawnRightManhandlaHead
INC.w SprAction, X
RTS
.not_main
LDA.w SprSubtype, X : STA.w SprAction, X
RTS
}
Manhandla_FrontHead:
{
%PlayAnimation(0,1,16)
JSL GetRandomInt : AND.b #$7F : BNE +
JSR Mothula_SpawnBeams
+
JSR Sprite_Manhandla_Move
JSL Sprite_DamageFlash_Long
JSL Sprite_CheckDamageFromPlayerLong
%DoDamageToPlayerSameLayerOnContact()
PHX
LDY.w Offspring1_Id
LDA.w SprType, Y : CMP.b #$88 : BNE .not_head
LDA.w SprState, Y : BEQ .offspring1_dead
%SetLeftHeadPos()
.offspring1_dead
.not_head
LDY.w Offspring2_Id
LDA.w SprType, Y : CMP.b #$88 : BNE .not_head2
LDA.w SprState, Y : BEQ .offspring2_dead
%SetRightHeadPos()
.offspring2_dead
.not_head2
PLX
RTS
}
Manhandla_LeftHead:
{
%StartOnFrame(2)
%PlayAnimation(2,3,16)
JSL Sprite_Move
JSL Sprite_DamageFlash_Long
JSL Sprite_CheckDamageFromPlayerLong
%DoDamageToPlayerSameLayerOnContact()
RTS
}
Manhandla_RightHead:
{
%StartOnFrame(4)
%PlayAnimation(4,5,16)
PHX
JSL Sprite_Move
JSL Sprite_DamageFlash_Long
JSL Sprite_CheckDamageFromPlayerLong
%DoDamageToPlayerSameLayerOnContact()
PLX
RTS
}
BigChuchu_Main:
{
%PlayAnimation(0,2,16)
PHX
JSR Sprite_Manhandla_Move
JSL Sprite_DamageFlash_Long
JSL Sprite_CheckDamageFromPlayerLong
%DoDamageToPlayerSameLayerOnContact()
PLX
RTS
}
BigChuChu_Dead:
{
LDA $1C : ORA.b #$01 : STA $1C ;turn on BG2 (Body)
; Flicker the body every other frame using the timer
LDA SprTimerA, X : AND.b #$01 : BEQ .flicker
LDA $1C : AND.b #$FE : STA $1C ;turn off BG2 (Body)
.flicker
LDA SprTimerA, X : BNE .continue
STZ.w $0422
STZ.w $0424
LDA $1C : AND.b #$FE : STA $1C ;turn off BG2 (Body)
LDA.b #$04 : STA.w SprState, X
STZ.w SprHealth, X
.continue
RTS
}
}
Sprite_Manhandla_Move:
{
LDA.w SprMiscC, X
JSL UseImplicitRegIndexedLocalJumpTable
dw StageControl
dw MoveXandY
dw MoveXorY
dw KeepWalking
StageControl:
{
STZ.w SprYSpeed : STZ.w SprXSpeed ;set velocitys to 0
JSL MoveBody
JSR Manhandla_StopIfOutOfBounds
LDA SprTimerA, X : BNE .continue
INC.w SprMiscC, X
.continue
RTS
}
MoveXandY:
{
LDA $36
JSL Sprite_ApplySpeedTowardsPlayer
JSR Manhandla_StopIfOutOfBounds
JSL MoveBody
INC.w SprMiscC, X
RTS
}
MoveXorY:
{
LDA $36 : STA $00
JSL Sprite_ApplySpeedTowardsPlayerXOrY_Long
JSR Manhandla_StopIfOutOfBounds
JSL MoveBody
INC.w SprMiscC, X
RTS
}
KeepWalking:
{
PHX
REP #$20
; Use a range of + 0x05 because being exact equal didnt trigger consistently
LDA $20 : SBC SprCachedY : CMP.w #$FFFB : BCC .notEqualY
SEP #$20
LDA.b #$02 : STA.w SprMiscC, X
BRA .notEqualX
.notEqualY
; Use a range of + 0x05 because being exact equal didnt trigger consistently
LDA $22 : SBC SprCachedX : CMP.w #$FFFB : BCC .notEqualX
SEP #$20
LDA.b #$02 : STA.w SprMiscC, X
.notEqualX
SEP #$20
JSR Manhandla_StopIfOutOfBounds
;if both velocities are 0 go back to the Stalk_Player_XORY to re-set the course
LDA.w SprYSpeed : BNE .notZero
LDA.w SprXSpeed : BNE .notZero
LDA.b #$03 : STA.w SprMiscC, X
.notZero
JSL MoveBody
PLX ;restores X
RTS
}
}
Manhandla_StopIfOutOfBounds:
{
; Set A to 00 if outside of certain bounds
REP #$20
LDA SprCachedX : CMP.w #$1528 : BCS .not_out_of_bounds_Left
SEP #$20
LDA.w SprXSpeed : CMP.b #$7F : BCC .not_out_of_bounds_Left
LDA.b #-10 : STA.w SprXSpeed : STA SprXRound
.not_out_of_bounds_Left
SEP #$20
REP #$20
LDA SprCachedX : CMP.w #$15C8 : BCC .not_out_of_bounds_Right
SEP #$20
LDA.w SprXSpeed : CMP.b #$80 : BCS .not_out_of_bounds_Right
LDA.b #$00 : STA.w SprXSpeed : STA SprXRound
.not_out_of_bounds_Right
SEP #$20
; Upper bound
REP #$20
LDA SprCachedY : CMP.w #$0B30 : BCS .not_out_of_bounds_Up
SEP #$20
LDA.w SprYSpeed : CMP.b #$7F : BCC .not_out_of_bounds_Up
LDA.b #$00 : STA.w SprYSpeed : STA SprYRound
.not_out_of_bounds_Up
SEP #$20
REP #$20
LDA SprCachedY : CMP.w #$0BC0 : BCC .not_out_of_bounds_Down
SEP #$20
LDA.w SprYSpeed : CMP.b #$80 : BCS .not_out_of_bounds_Down
LDA.b #-10 : STA.w SprYSpeed : STA SprYRound ; Reverse the direction
.not_out_of_bounds_Down
SEP #$20
RTS
}
; =========================================================
Sprite_Manhandla_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 SprMiscA, 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, $02, $04, $08, $0C, $10, $14, $17, $1A
.nbr_of_tiles
db 1, 1, 3, 3, 3, 3, 2, 2, 2
.x_offsets
dw 0, 0
dw 0, 0
dw 0, 8, 8, 0
dw 0, 8, 0, 8
dw 0, -8, -8, 0
dw 0, -8, 0, -8
dw -12, -4, 12
dw -12, -4, 12
dw 12, 4, -12
.y_offsets
dw -8, 8
dw 0, 16
dw -4, -4, 12, 12
dw -4, -4, 12, 12
dw -4, -4, 12, 12
dw -4, -4, 12, 12
dw 0, 0, 0
dw 0, 0, 0
dw 0, 0, 0
.chr
db $00, $20
db $02, $22
db $04, $05, $25, $24
db $07, $08, $27, $28
db $04, $05, $25, $24
db $07, $08, $27, $28
db $40, $41, $40
db $43, $44, $46
db $43, $44, $46
.properties
db $33, $33
db $33, $33
db $33, $33, $33, $33
db $33, $33, $33, $33
db $73, $73, $73, $73
db $73, $73, $73, $73
db $33, $33, $73
db $33, $33, $73
db $73, $73, $33
.sizes
db $02, $02
db $02, $02
db $02, $02, $02, $02
db $02, $02, $02, $02
db $02, $02, $02, $02
db $02, $02, $02, $02
db $02, $02, $02
db $02, $02, $02
db $02, $02, $02
}
Sprite_BigChuchu_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 SprMiscA, 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, $09, $12
.nbr_of_tiles
db 8, 8, 8
.x_offsets
dw -12, 4, 20, -12, 4, 12, -12, 4, 12
dw -12, 4, 20, -12, 4, 12, -12, 4, 12
dw -12, 4, 20, -12, 4, 12, -8, 8, 16
.y_offsets
dw 8, 8, 8, 16, 16, 16, 32, 32, 32
dw 8, 8, 8, 16, 16, 16, 32, 32, 32
dw 8, 8, 8, 16, 16, 16, 32, 32, 32
.chr
db $80, $82, $84, $90, $92, $93, $B0, $B2, $B3
db $80, $82, $84, $90, $92, $93, $D0, $D2, $D3
db $80, $82, $84, $90, $92, $93, $D6, $D8, $D9
.properties
db $33, $33, $33, $33, $33, $33, $33, $33, $33
db $33, $33, $33, $33, $33, $33, $33, $33, $33
db $33, $33, $33, $33, $33, $33, $33, $33, $33
.sizes
db $02, $02, $02, $02, $02, $02, $02, $02, $02
db $02, $02, $02, $02, $02, $02, $02, $02, $02
db $02, $02, $02, $02, $02, $02, $02, $02, $02
}
Mothula_SpawnBeams:
{
LDA.b #$36 ; SFX3.36
JSL $0DBB8A ; SpriteSFX_QueueSFX3WithPan
LDA.b #$02
STA.w $0FB5
.next_spawn
LDA.b #$89 ; SPRITE 89
JSL Sprite_SpawnDynamically
BMI .no_space
JSL Sprite_SetSpawnedCoordinates
LDA.b $02 : SEC : SBC.b $04
CLC : ADC.b #$03 : STA.w $0D00,Y
LDA.b #$10 : STA.w $0DF0,Y : STA.w $0BA0,Y
PHX
LDX.w $0FB5
LDA.b $00 : CLC : ADC.w .speed_and_offset_x,X
STA.w $0D10,Y
LDA.w .speed_and_offset_x,X : STA.w $0D50,Y
LDA.w .speed_y,X
STA.w $0D40,Y
LDA.b #$00
STA.w $0F70,Y
PLX
.no_space
DEC.w $0FB5
BPL .next_spawn
RTS
.speed_and_offset_x
db -16, 0, 16
.speed_y
db 24, 32, 24
}
SpawnLeftManhandlaHead:
{
LDA #$88
JSL Sprite_SpawnDynamically : BMI .return
TYA : STA Offspring1_Id
PHX
%SetLeftHeadPos()
; store the sub-type
LDA.b #$03 : STA.w $0E30, Y
STA.w SprSubtype, Y
LDA.b #$10 : STA.w SprHealth, Y
LDA.b #$90 : STA.w SprTileDie, Y
TYX
STZ.w SprYRound, X
STZ.w SprXRound, X
PLX
.return
RTS
}
SpawnRightManhandlaHead:
{
LDA #$88
JSL Sprite_SpawnDynamically : BMI .return
TYA : STA Offspring2_Id
PHX
%SetRightHeadPos()
LDA.b #$02 : STA $0E30, Y
STA.w SprSubtype, Y
LDA.b #$10 : STA.w SprHealth, Y
LDA.b #$90 : STA.w SprTileDie, Y
TYX
STZ.w SprYRound, X
STZ.w SprXRound, X
PLX
.return
RTS
}
ApplyManhandlaPalette:
{
REP #$20 ;Set A in 16bit mode
;note, this uses adresses like 7EC300 and not 7EC500 because the game
;will fade the colors into 7EC500 based on the colors found in 7EC300
LDA #$7FFF : STA $7EC5E2 ;BG2
LDA #$08D9 : STA $7EC5E4
LDA #$1E07 : STA $7EC5E6
LDA #$4ACA : STA $7EC5E8
LDA #$14A5 : STA $7EC5EA
LDA #$133F : STA $7EC5EC
LDA #$19DF : STA $7EC5EE
INC $15
SEP #$20 ;Set A in 8bit mode
RTS
}
ApplyManhandlaGraphics:
{
PHX
REP #$20 ; A = 16, XY = 8
LDX #$80 : STX $2100 ; turn the screen off (required)
LDX #$80 : STX $2115 ; Set the video port register every time we write it increase by 1
LDA #$5000 : STA $2116 ; Destination of the DMA $5800 in vram <- this need to be divided by 2
LDA #$1801 : STA $4300 ; DMA Transfer Mode and destination register
; "001 => 2 registers write once (2 bytes: p, p+1)"
LDA.w #ManhandlaGraphics : STA $4302
LDX.b #ManhandlaGraphics>>16 : STX $4304
LDA #$2000 : STA $4305 ; Size of the transfer 4 sheets of $800 each
LDX #$01 : STX $420B ; Do the DMA
LDX #$0F : STX $2100 ; Turn the screen back on
SEP #$30
PLX
RTS
ManhandlaGraphics:
incbin manhandla.bin
}