Code;
; super mario sunshine style blooper
; drawn by eminus
; coded by von fahrenheit
;
; use cfg editor to toggle things like fireball kills
; set extra bit to make sprite immobile
; if you place the sprite on a ceiling, it will stick to it
; sprite tables
!ceiling = !1504 ; used for sticking to ceilings
!attacktimer = !1528 ; counts up while mario is within range until an attack is made
!turntimer = !1540 ; used for patrol
!animtimer = !1570 ; used for animations
!image = !1602 ; also works as state
!Map16ActsLike = $06F624|!BankB ; don't change this, just set !actslike to 0 if you don't want to use it
; toggles
!actslike = 1 ; 0 = use raw map16 numbers, 1 = use acts like setting
!score = 1 ; 0 = no score when jumping on sprite, 1 = score when jumping on sprite
!jumpatledge = 1 ; 0 = sprite will turn around at ledges, 1 = sprite will jump at ledges
; numeric defines
!speed = 36 ; horizontal speed
!shootspeed = 55 ; aggregate ink speed
!attacktime = 64 ; how many frames it takes the sprite to prepare an attack
!jumpspeedx = $08 ; x speed when jumping, only used if !jumpatledge = 1
!jumpspeedy = $C0 ; y speed when jumping, only used if !jumpatledge = 1
!projectilenum = $15 ; custom sprite number of ink sprite
; cheat sheet for easily remapping graphics:
; tiles 000-07F is SP1
; tiles 080-0FF is SP2
; tiles 100-17F is SP3
; tiles 180-1FF is SP4
!idle1_head1 = $120 ; 16x16
!idle1_head2 = $122 ; 16x16
!idle1_bottom = $140 ; 32x8
!idle2_head1 = $124 ; 16x16
!idle2_head2 = $126 ; 16x16
!idle2_bottom = $144 ; 32x8
!idle3_head1 = $106 ; 16x16
!idle3_head2 = $10A ; 16x16
!idle3_bottom = $150 ; 32x8
!shoot1_head1 = $10C ; 16x16
!shoot1_head2 = $10E ; 16x16
!shoot1_bottom = $154 ; 32x8
!shoot2_head1 = $148 ; 16x16
!shoot2_head2 = $14A ; 16x16
!shoot2_bottom = $15C ; 32x8
!flat_tile = $174 ; 16x8
; these need to match in the ink file
!ink1_tile = $12D ; 8x8
!ink2_tile = $12E ; 8x8
!ink3_tile = $13D ; 8x8
!ink4_tile = $13E ; 8x8
; list of tiles that can be stuck to, ceiling-style
; you can add or remove numbers, just don't change the labels
CeilingTiles:
dw $0111
dw $0112
dw $0113
dw $0114
dw $0115
dw $0116
dw $0117
dw $0118
dw $0119
dw $011A
dw $011B
dw $011C
dw $011D
dw $011E
dw $011F
dw $0120
dw $0121
dw $0122
dw $0123
dw $0124
dw $0125
dw $0126
dw $0127
dw $0128
dw $0129
dw $012A
dw $012B
dw $012C
dw $012D
dw $012E
dw $012F
dw $0130
dw $014E
.End
print "INIT ", pc
INIT:
PHB : PHK : PLB
%SubHorzPos()
TYA : STA !157C,x
STZ !sprite_speed_x,x
JSL $01802A|!BankB
LDA !1588,x
AND #$04 : BEQ .Air
.Ground
PLB
RTL
.Air
LDA !sprite_y_low,x
AND #$F0
SEC : SBC #$03
STA !sprite_y_low,x
LDA !sprite_y_high,x
SBC #$00
STA !sprite_y_high,x
LDA !sprite_x_low,x
CLC : ADC #$08
STA $9A
LDA !sprite_x_high,x
ADC #$00
STA $9B
LDA !sprite_y_low,x
SEC : SBC #$08
STA $98
LDA !sprite_y_high,x
SBC #$00
STA $99
STZ $1933|!Base2
%GetMap16()
if !actslike == 1
PHP
REP #$30
AND #$3FFF
ASL A
TAY
LDA.l !Map16ActsLike : STA $00
LDA.l !Map16ActsLike+1 : STA $01
LDA [$00],y
PLP
endif
REP #$20
LDY #$00
.Loop CPY.b #CeilingTiles_End-CeilingTiles : BCS .Done
CMP CeilingTiles,y : BEQ .Stick
INY #2
BRA .Loop
.Done
SEP #$20
PLB
RTL
.Stick
SEP #$20
LDA #$01 : STA !ceiling,x
PLB
RTL
print "MAIN ", pc
MAIN:
PHB : PHK : PLB
LDA !sprite_status,x
SEC : SBC #$08
ORA $9D : BNE .Lock
.Process
LDA !animtimer,x : BEQ +
DEC !animtimer,x
+
LDA !animtimer,x : BNE +
LDA !image,x
INC A
CMP #$07 : BNE .NoDeath
STZ !sprite_status,x
PLB
RTL
.NoDeath
CMP #$05 : BEQ Shoot
CMP #$04
BCC $02 : LDA #$00
STA !image,x
LDA #$08 : STA !animtimer,x
+
.Main
LDA !image,x ;\
CMP #$06 : BNE .Alive ; |
LDA !sprite_speed_y,x : BPL + ; |
STZ !sprite_speed_y,x ; |
+ LDA !1588,x ; | when dead:
AND #$04 : BEQ + ; | - only positive y speed allowed
STZ !sprite_speed_x,x ; | - keep x speed until touching the ground
+ JSL $01802A|!BankB ; |
BRA .Lock ;/
.Alive JSR Physics
JSR Interaction
.Lock LDA !sprite_status,x
CMP #$02 : BEQ .DeathFall
CMP #$08 : BNE .Return
.Draw
JSR Graphics
.Return
PLB
RTL
.DeathFall
LDA !sprite_speed_y,x : BMI +
CMP #$40 : BCS ++
+ INC #3
STA !sprite_speed_y,x
++ JSL $01801A|!BankB
JSL $018022|!BankB
BRA .Draw
Shoot:
LDA !sprite_status,x ;\
CMP #$07 : BEQ MAIN_Main ; | no attacks while being eaten
LDA !15D0,x : BNE MAIN_Main ;/
STA !image,x
LDA #$0E : STA $1DF9|!Base2
LDY !157C,x
LDA .XDisp,y : STA $00
LDA #$08 : STA $01
SEC : LDA.b #!projectilenum
%SpawnSprite()
LDA #$08 : STA !animtimer,x
BCS MAIN_Main
LDA !sprite_x_low,y : STA $00
LDA !sprite_x_high,y : STA $01
LDA !sprite_y_low,y : STA $02
LDA !sprite_y_high,y : STA $03
REP #$20
LDA $00
SEC : SBC $94
STA $00
LDA $02
SEC : SBC #$0010
SEC : SBC $96
STA $02
SEP #$20
LDA.b #!shootspeed
PHY
%Aiming()
PLY
LDA $00 : STA.w !sprite_speed_x,y
LDA $02 : STA.w !sprite_speed_y,y
JMP MAIN_Main
.XDisp
db $0D,$FB
Physics:
LDA !image,x
CMP #$04 : BCC .ProcessSpeed
RTS
.ProcessSpeed
LDA !sprite_x_low,x
SEC : SBC #$80
STA $04
LDA !sprite_x_high,x
SBC #$00
STA $0A
LDY !ceiling,x
LDA !sprite_y_low,x
CLC : ADC .YSight,y
STA $05
LDA !sprite_y_high,x
ADC .YSight+2,y
STA $0B
LDA #$7F
STA $06
STA $07
JSL $03B664|!BankB
JSL $03B72B|!BankB
BCS .Seen
LDA $04
CLC : ADC #$80
STA $04
BCC $02 : INC $0A
JSL $03B72B|!BankB
BCS .Seen
STZ !attacktimer,x
BRA .NoSight
.Seen
LDA !ceiling,x : BNE + ; ignore following rule if sticking to a ceiling
LDA !1588,x ;\ can't attack or turn around in midair
AND #$04 : BEQ .NoTurn ;/
+ LDA !attacktimer,x
INC A
CMP.b #!attacktime : BCC .NoAttack
LDA #$04 : STA !image,x
LDA #$10 : STA !animtimer,x
STZ !turntimer,x
LDA #$00
.NoAttack
STA !attacktimer,x
LDA !turntimer,x : BNE .NoTurn
%SubHorzPos()
TYA : STA !157C,x
LDA #$18 : STA !turntimer,x
.NoTurn
.NoSight
LDA !extra_bits,x
AND #$04 : BNE +
LDA !1588,x
AND #$04 : BEQ + ; don't walk in midair
LDY !157C,x
LDA .XSpeed,y : STA !sprite_speed_x,x
+ LDA !1588,x
AND #$03 : BNE .Turn
LDA !1588,x
AND #$04
PHA
LDA !ceiling,x : BEQ +
STZ !sprite_speed_y,x
+ JSL $01802A|!BankB
PLA : BEQ .Return
EOR !1588,x
AND #$04 : BEQ .Ground
RTS
.Turn
if !jumpatledge == 1
LDY !157C,x
LDA .JumpSpeedX,y : STA !sprite_speed_x,x
LDA.b #!jumpspeedy : STA !sprite_speed_y,x
JSL $01802A|!BankB
RTS
.JumpSpeedX
db !jumpspeedx,-!jumpspeedx
else
LDA !sprite_speed_y,x
EOR #$FF : INC A
STA !sprite_speed_y,x
endif
LDA !sprite_speed_x,x
EOR #$FF : INC A
STA !sprite_speed_x,x
LDA !157C,x
EOR #$01
STA !157C,x
LDA #$18 : STA !turntimer,x
JSL $01802A|!BankB
.Return
RTS
.Ground
STZ !sprite_speed_y,x
RTS
.XSpeed
db !speed,-!speed
.YSight
db $A0,$00
db $FF,$00
Interaction:
LDA !sprite_x_low,x
SEC : SBC #$04
STA $04
LDA !sprite_x_high,x
SBC #$00
STA $0A
LDA !sprite_y_low,x
SEC : SBC #$08
STA $05
LDA !sprite_y_high,x
SBC #$00
STA $0B
LDA #$18
STA $06
STA $07
JSR CheckQuake
BCC $03 : JMP QuakeKill
JSL $03B664|!BankB
JSL $03B72B|!BankB
BCS .Contact
JMP .NoContact
.Contact
LDA $1490|!Base2 : BEQ .NoStar
LDA #$02 : STA !sprite_status,x
LDA $7B
JSR Halve
STA !sprite_speed_x,x
LDA #$D8 : STA !sprite_speed_y,x
STZ $00
STZ $01
LDA #$08 : STA $02
LDA #$02
%SpawnSmoke()
if !score == 1
LDA $18D2|!Base2 ;\
INC A ; |
CMP #$08 ; | increment number consecutive enemies stomped
BCC $02 : LDA #$08 ; |
STA $18D2|!Base2 ;/
JSL $02ACE5|!BankB ; give mario points
LDY $18D2|!Base2 ;\ score SFX
LDA .ScoreSFX,y : STA $1DF9|!Base2 ;/
else
LDA #$02 : STA $1DF9|!Base2
endif
RTS
.NoStar
LDA $77
AND #$04 : BEQ $03 : JMP .HurtMario
LDA $01
CMP $05
LDA $09
SBC $0B
BCC $03 : JMP .HurtMario
+ JSL $01AB99+5|!BankB
JSL $01AA33|!BankB
if !score == 1
LDA $1697|!Base2 ;\
INC A ; |
CMP #$08 ; | increment number consecutive enemies killed
BCC $02 : LDA #$08 ; |
STA $1697|!Base2 ;/
JSL $02ACE5|!BankB ; give mario points
LDY $1697|!Base2 ;\ score SFX
LDA .ScoreSFX,y : STA $1DF9|!Base2 ;/
else
LDA #$02 : STA $1DF9|!Base2
endif
LDA $140D|!Base2
ORA $187A|!Base2
BEQ .NoSpin
LDA #$04 : STA !sprite_status,x
LDA #$1F : STA !1540,x
LDA #$08 : STA $1DF9|!Base2 ; spin kill sfx
JSL $07FC3B|!BankB
RTS
.NoSpin
LDA #$06 : STA !image,x
LDA #$40 : STA !animtimer,x
.NoContact
LDY.b #!SprSize
- LDA !sprite_status,y
CMP #$0A : BNE +
PHY
PHX
TYX
JSL $03B6E5|!BankB
JSL $03B72B|!BankB
PLX
PLY
BCC +
LDA.w !sprite_speed_x,y
JSR Halve
STA.w !sprite_speed_x,y
LDA #$C0 : STA.w !sprite_speed_y,y
LDA #$02 : STA !sprite_status,y
LDA !1686,y
ORA #$80
STA !1686,y
LDA #$08 : STA $1DFC|!Base2 ; bounce sfx
PHX
TYX
STZ $00
STZ $01
LDA #$08 : STA $02
LDA #$02
%SpawnSmoke()
PLX
+ DEY : BPL -
RTS
if !score == 1
.ScoreSFX db $00,$13,$14,$15,$16,$17,$18,$19,$03
endif
.HurtMario
HurtPlayer:
LDA $187A|!Base2 : BEQ .NoYoshi
LDA $1490|!Base2 : BNE .Return
LDY $18E2|!Base2 : BEQ .NoYoshi
DEY
LDA #$10 : STA !163E,y
LDA #$03 : STA $1DFA|!Base2 ; disable yoshi drums
LDA #$13 : STA $1DFC|!Base2 ; lose yoshi sfx
LDA #$02 : STA.w !C2|!Base1,y
STZ $187A|!Base2
STZ $7B
LDA #$C0 : STA $7D
PHX
TYX
%SubHorzPos()
PHX
TYX
LDA.l $01EBBE|!BankB,x
PLX
STA !sprite_speed_x,x
STZ !1594,x
STZ !151C,x
STZ $18AE|!Base2
STZ $0DC1|!Base2
LDA #$30 : STA $1497|!Base2
LDA !sprite_y_low,x
SEC : SBC #$04
STA $96
STA $D3
LDA !sprite_y_high,x
SBC #$00
STA $97
STA $D4
PLX
.Return
RTS
.NoYoshi
JSL $00F5B7|!BankB
RTS
Graphics:
LDA !image,x
CMP #$06 : BNE +
LDA !animtimer,x
AND #$02 : BEQ +
RTS
+
STZ $0E
STZ $0F
LDA !sprite_y_low,x : STA $02
LDA !sprite_y_high,x : STA $03
LDA !sprite_x_high,x : XBA
LDA !sprite_x_low,x
REP #$20
SEC : SBC $1A
CMP #$0180 : BCC +
CMP #$FF80 : BCS +
INC $0F
+
STA $00
LDA $02
SEC : SBC $1C
CMP #$0180 : BCC +
CMP #$FF80 : BCS +
INC $0F
+
STA $02
SEP #$20
LDA $0F : BEQ .Go ;\
LDA !sprite_status,x ; | despawn if too far offscreen
STZ !sprite_status,x ;/
CMP #$08 : BNE + ; > check status
LDA !image,x ;\
CMP #$06 : BEQ + ; |
PHX ; |
LDA !sprite_index_in_level,x : TAX ; | mark for reload
LDA #$00 : STA !sprite_load_table,x ; | (unless dead)
PLX ; |
+ RTS ;/
.Go
LDA !157C,x
BEQ $02 : LDA #$40
EOR #$40
LDY !ceiling,x
BEQ $02 : ORA #$80
STA $04
LDA !15F6,x
AND #$0E
TSB $04
PHX
LDY !sprite_oam_index,x
LDA !image,x
AND #$7F
TAX
LDA Tilemap_Tiles,x : STA $08
LDA Tilemap_Index,x : TAX
.Loop LDA Tilemap+3,x
AND #$C1
EOR $04
STA $05
LDA Tilemap+3,x
AND #$02
BEQ $02 : LDA #$80
STA $09
REP #$20
LDA Tilemap,x
AND #$00FF
CMP #$0080
BCC $03 : ORA #$FF00
BIT $05-1 : BVC + ;\
EOR #$FFFF : INC A ; | horizontal flip
BIT $09-1 ; |
BMI $04 : CLC : ADC #$0008 ;/
+ CLC : ADC $00
STA $0300|!Base2,y
STA $0E
INX
CMP #$0100 : BCC +
CMP #$FFF0 : BCC .BadY
+ LDA Tilemap,x
AND #$00FF
CMP #$0080
BCC $03 : ORA #$FF00
BIT $05-1 : BPL + ;\
EOR #$FFFF : INC A ; | vertical flip
BIT $09-1 ; |
BMI $04 : CLC : ADC #$0008 ;/
+ CLC : ADC $02
CMP #$00E0 : BCC +
CMP #$FFF0 : BCS +
.BadY LDA #$00E0
+ STA $0301|!Base2,y
INX
SEP #$20
LDA Tilemap,x : STA $0302|!Base2,y
INX
LDA $05
ORA $64
STA $0303|!Base2,y
PHY
TYA
LSR #2
TAY
LDA $0F
AND #$01
BIT $09
BPL $02 : INC #2
STA $0460|!Base2,y
PLY
INX
INY #4
DEC $08 : BMI .Return
JMP .Loop
.Return
PLX
RTS
Tilemap:
; format:
; - xdisp
; - ydisp
; - tile number
; - YX bits of prop + tile size + T bit of prop
; repeat for each tile
.idle1
db $F8,$F8,!idle1_head1,$02|(!idle1_head1>>8)
db $08,$F8,!idle1_head2,$02|(!idle1_head2>>8)
db $F8,$08,!idle1_bottom+$00,$00|(!idle1_bottom>>8)
db $00,$08,!idle1_bottom+$01,$00|(!idle1_bottom>>8)
db $08,$08,!idle1_bottom+$02,$00|(!idle1_bottom>>8)
db $10,$08,!idle1_bottom+$03,$00|(!idle1_bottom>>8)
.idle2
db $F8,$F8,!idle2_head1,$02|(!idle2_head1>>8)
db $08,$F8,!idle2_head2,$02|(!idle2_head2>>8)
db $F8,$08,!idle2_bottom+$00,$00|(!idle2_bottom>>8)
db $00,$08,!idle2_bottom+$01,$00|(!idle2_bottom>>8)
db $08,$08,!idle2_bottom+$02,$00|(!idle2_bottom>>8)
db $10,$08,!idle2_bottom+$03,$00|(!idle2_bottom>>8)
.idle3
db $F8,$F8,!idle3_head1,$02|(!idle3_head1>>8)
db $08,$F8,!idle3_head2,$02|(!idle3_head2>>8)
db $F8,$08,!idle3_bottom+$00,$00|(!idle3_bottom>>8)
db $00,$08,!idle3_bottom+$01,$00|(!idle3_bottom>>8)
db $08,$08,!idle3_bottom+$02,$00|(!idle3_bottom>>8)
db $10,$08,!idle3_bottom+$03,$00|(!idle3_bottom>>8)
.shoot1
db $F8,$F8,!shoot1_head1,$02|(!shoot1_head1>>8)
db $08,$F8,!shoot1_head2,$02|(!shoot1_head2>>8)
db $F8,$08,!shoot1_bottom+$00,$00|(!shoot1_bottom>>8)
db $00,$08,!shoot1_bottom+$01,$00|(!shoot1_bottom>>8)
db $08,$08,!shoot1_bottom+$02,$00|(!shoot1_bottom>>8)
db $10,$08,!shoot1_bottom+$03,$00|(!shoot1_bottom>>8)
.shoot2
db $F8,$F8,!shoot2_head1,$02|(!shoot2_head1>>8)
db $08,$F8,!shoot2_head2,$02|(!shoot2_head2>>8)
db $F8,$08,!shoot2_bottom+$00,$00|(!shoot2_bottom>>8)
db $00,$08,!shoot2_bottom+$01,$00|(!shoot2_bottom>>8)
db $08,$08,!shoot2_bottom+$02,$00|(!shoot2_bottom>>8)
db $10,$08,!shoot2_bottom+$03,$00|(!shoot2_bottom>>8)
.flat
db $F8,$08,!flat_tile+$00,$00|(!flat_tile>>8)
db $00,$08,!flat_tile+$01,$00|(!flat_tile>>8)
db $00,$08,!flat_tile+$01,$40|(!flat_tile>>8)
db $F8,$08,!flat_tile+$00,$40|(!flat_tile>>8)
.Index
db .idle1-Tilemap ; 00
db .idle2-Tilemap ; 01
db .idle3-Tilemap ; 02
db .idle2-Tilemap ; 03
db .shoot1-Tilemap ; 04
db .shoot2-Tilemap ; 05
db .flat-Tilemap ; 06
.Tiles
db $05 ; 00
db $05 ; 01
db $05 ; 02
db $05 ; 03
db $05 ; 04
db $05 ; 05
db $03 ; 06
Halve:
BPL .pos
.neg EOR #$FF
LSR A
EOR #$FF
RTS
.pos LSR A
RTS
CheckQuake:
LDA #$10
STA $02
STA $03
LDY #$03
- LDA $16CD|!Base2,y : BEQ +
LDA $16D1|!Base2,y : STA $00
LDA $16D5|!Base2,y : STA $08
LDA $16D9|!Base2,y : STA $01
LDA $16DD|!Base2,y : STA $09
JSL $03B72B|!BankB
BCS .Return
+ DEY : BPL -
CLC
.Return RTS
QuakeKill:
LDA #$02 : STA !sprite_status,x
STZ !sprite_speed_x,x
LDA #$D8 : STA !sprite_speed_y,x
STZ $00
STZ $01
LDA #$08 : STA $02
LDA #$02
%SpawnSmoke()
LDA #$03 : STA $1DF9|!Base2
if !score == 1
LDA #$00 ; quake kill worth 100 points
JSL $02ACE5|!BankB ; give mario points
endif
RTS