;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Fire-Spitting Piranha Plant, by imamelia ;; ;; This is a Piranha Plant that spits fireballs when it emerges from its pipe. ;; ;; Uses first extra bit: YES ;; ;; If the first extra bit is clear, the plant will spit only one pair of fireballs. ;; If the first extra bit is set, the plant will spit three pairs of fireballs. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;incsrc subroutinedefsx.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; defines and tables ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; $151C,x = holder for the sprite's initial Y position low byte ; $1528,x = holder for the sprite's initial X position low byte Speed: ; the Piranha Plant's speed for each sprite state (inverted for down and right plants) db $00,$F0,$00,$10 ; in the pipe, moving forward, resting at the apex, moving backward TimeInState: ; the time the sprite will spend in each sprite state, indexed by bits 2, 4, and 5 of the behavior table db $30,$20,$30,$20 ; long Piranha Plants db $30,$18,$30,$18 ; short Piranha Plants !StemYOffset = $10 ; HeadTilemap: db $AE,$AC !StemTile = $CE ; These two are indexed by ------lc, where c = color and l = length. ; Add 1 to each of these values if you want the tile to use the second graphics page. StemPalette: ; the palette of the stem tiles db $0A,$08,$0A,$08 HeadPalette: ; the palette of the head tiles db $08,$08,$0A,$08 ; This tile will be invisible because it has sprite priority setting 0, ; but it will go in front of the plant tiles to cover it up when it is in a pipe. ; That way, the plant tiles don't need to have hardcoded priority. ; This tile should be as close to square as possible. ; Note: The default value WILL NOT completely hide the tiles unless you have changed its graphics! ; But the only completely square tile in a vanilla GFX00/01 is the message box tile, which is set to be overwritten by default. !CoverUpTile = $40 ; the invisible tile used to cover up the sprite when it is in a pipe !InitOffsetYLo = $FF !InitOffsetYHi = $FF !InitOffsetXLo = $08 !InitOffsetXHi = $00 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; init routine wrapper ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; print "INIT ",pc PHB PHK PLB JSR PiranhaInit PLB RTL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; init routine ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PiranhaInit: LDA !7FAB28,x ; AND #$04 ; LSR #2 ; STA !1510,x ; LDA !D8,x ; CLC ; ADC #!InitOffsetYLo ; Y position low byte STA !D8,x ; LDA !14D4,x ; ADC #!InitOffsetYHi ; Y position high byte STA !14D4,x ; LDA !E4,x ; CLC ; ADC #!InitOffsetXLo ; X position low byte STA !E4,x ; LDA !14E0,x ; ADC #!InitOffsetXHi ; X position high byte STA !14E0,x ; LDA !1510,x ; get the bits for the sprite state timer index AND #$01 ; ASL #2 ; STA !1504,x ; LDA !D8,x ; STA !151C,x ; back up the sprite's initial XY position (low bytes) LDA !E4,x ; STA !1528,x ; RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; main routine wrapper ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; print "MAIN ",pc PHB PHK PLB JSR PiranhaPlantsMain PLB RTL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; main routine ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; EndMain: RTS PiranhaPlantsMain: ;LDA $1594,x ; if the sprite is in a pipe and the player is near... ;BNE NoGFX ; don't draw the sprite LDA !C2,x ; BEQ NoGFX ; JSR PiranhaPlantGFX ; draw the sprite NoGFX: ; LDA $9D ; if sprites are locked... BNE EndMain ; terminate the main routine right here %SubOffScreen() ; PiranhaAnimation: ; JSR SetAnimationFrame ; determine which frame the plant should show LDA !1594,x ; if the plant is in a pipe... BNE NoInteraction ; don't let it interact with the player JSL $01803A|!BankB ; interact with the player and other sprites NoInteraction: ; LDA !C2,x ; if the extra bit is set... CMP #$02 ; then make sure it's in the correct sprite state (resting at the apex) BNE NoFireCheck ; LDA !7FAB10,x ; AND #$04 ; if the extra bit isn't set... BEQ NoFireCheck ; don't check certain timer values to see if it should spit a fireball LDA !1540,x ; CMP #$20 ; if the fire timer BEQ Fire ; is at certain numbers... CMP #$10 ; spit a fireball BEQ Fire ; NoFireCheck: LDA !C2,x ; use the sprite state AND #$03 ; to determine what the sprite's speed should be TAY ; LDA !1540,x ; if the timer for changing states has run out... BEQ ChangePiranhaState ; LDA Speed,y ; load the base speed STA !AA,x ; store the speed value to the sprite Y speed table JSL $01801A|!BankB ; update sprite Y position without gravity RTS ; ChangePiranhaState: ; LDA !C2,x ; sprite state AND #$03 ; 4 possible states, so we need only 2 bits STA $00 ; store to scratch RAM for subsequent use LDA !1510,x ; AND #$02 ; if the plant is a red one... ORA $00 ; or the sprite isn't in the pipe... BNE NoProximityCheck ; don't check to see if the player is near %SubHorzPos() ; get the horizontal distance between the player and the sprite LDA #$01 ; STA !1594,x ; set the invisibility flag if necessary LDA $0E ; CLC ; ADC #$1B ; if the sprite is within a certain distance... CMP #$37 ; BCC EndStateChange ; don't change the sprite state NoProximityCheck: ; STZ !1594,x ; if the sprite is out of range, clear the invisibility flag LDA !C2,x ; INC ; increment the sprite state AND #$03 ; STA !C2,x ; STA $00 ; LDA !1510,x ; AND #$01 ; use the stem length bit ASL #2 ; ORA $00 TAY ; to set the timer for changing sprite state LDA TimeInState,y ; STA !1540,x ; set the time to change state CPY #$02 ; if the sprite state isn't 02... BNE EndStateChange ; don't spit fire JSR SpitFireballs ; EndStateChange: ; RTS SetAnimationFrame: ; INC !1570,x ; $1570,x - individual sprite frame counter, in this context LDA !1570,x ; LSR #3 ; change image every 8 frames AND #$01 ; STA !1602,x ; set the resulting image RTS Fire: ; JMP SpitFireballs ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; graphics routine ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PiranhaPlantGFX: ; I made my own graphics routine, since the Piranha Plant uses a shared routine. %GetDrawInfo() ; set some variables up for writing to OAM LDA !1510,x ; AND #$04 ; stem length LSR ; STA $04 ; LDA !1510,x ; AND #$08 ; LSR #3 ; plus color TSB $04 ; LDA !1602,x ; STA $03 ; frame = bit 0 of the index LDA !1510,x ; AND #$01 ; if the plant has a short stem... BNE AlwaysCovered ; then the stem is always partially obscured by the cover-up tile LDA !C2,x ; CMP #$02 ; if the sprite is all the way out of the pipe... BEQ StemOnly ; then draw just the stem AlwaysCovered: ; LDA !D8,x ; SEC ; SBC !151C,x ; STA $06 ; LDA !E4,x ; SEC ; SBC !1528,x ; CLC ; ADC $06 ; CLC ; ADC #$10 ; CMP #$20 ; BCC CoverUpTileOnly ; StemAndCoverUpTile: ; JSR DrawCoverUpTile ; INY #4 ; JSR DrawStem ; LDA #$02 ; EndGFX: ; PHA ; INY #4 ; LDX $03 ; JSR DrawHead ; the head tile is always drawn PLA ; LDY #$02 ; LDX $15E9|!Base2 ; JSL $01B7B3|!BankB ; RTS ; StemOnly: ; JSR DrawStem ; LDA #$01 ; BRA EndGFX ; CoverUpTileOnly: ; JSR DrawCoverUpTile ; LDA #$01 ; BRA EndGFX ; DrawHead: LDA $00 ; STA $0300|!Base2,y ; LDA $01 ; STA $0301|!Base2,y ; LDA HeadTilemap,x ; set the tile for the head STA $0302|!Base2,y LDX $04 ; load the palette index LDA HeadPalette,x ; add in the palette/GFX page bits ORA $64 ; and the level's sprite priority STA $0303|!Base2,y ; RTS DrawStem: LDX $03 LDA $00 ; STA $0300|!Base2,y ; LDA $01 ; CLC ; ADC #$10 ; STA $0301|!Base2,y ; LDA #!StemTile ; set the tile for the stem STA $0302|!Base2,y LDX $04 ; load the palette index LDA StemPalette,x ; add in the palette/GFX page bits ORA $64 ; and the level's sprite priority STA $0303|!Base2,y ; RTS ; DrawCoverUpTile: ; LDX $15E9|!Base2 ; LDA !1528,x ; STA $09 ; LDA !151C,x ; make backups of the XY init positions STA $0A ; LDA $09 ; SEC ; SBC $1A ; STA $0300|!Base2,y ; LDA $0A ; SEC ; SBC $1C ; STA $0301|!Base2,y ; LDA #!CoverUpTile ; STA $0302|!Base2,y ; LDA #$00 ; STA $0303|!Base2,y ; RTS ; LDX $15E9|!Base2 ; sprite index back into X LDY #$02 ; the tiles were 16x16 LDA $05 ; we drew 2 or 3 tiles JSL $01B7B3|!BankB ; RTS ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; fireball-spit routine ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; !FireXOffset = $04 !FireYSpeed = $D0 FireXSpeeds: db $10,$F0 SpitFireballs: LDA !E4,x STA $01 LDA !14E0,x STA $02 LDA !D8,x STA $03 LDA !14D4,x STA $04 LDX #$01 SpitFireballLoop: LDY #$07 ExSpriteLoop: LDA $170B|!Base2,y BEQ FoundExSlot DEY BPL ExSpriteLoop RTS FoundExSlot: STY $00 LDA #$0B STA $170B|!Base2,y LDA $01 CLC ADC #!FireXOffset STA $171F|!Base2,y LDA $02 ADC #$00 STA $1733|!Base2,y LDA $03 STA $1715|!Base2,y LDA $04 STA $1729|!Base2,y LDA FireXSpeeds,x STA $1747|!Base2,y LDA #!FireYSpeed STA $173D|!Base2,y LDA #$FF STA $176F|!Base2,y DEX BPL SpitFireballLoop LDX $15E9|!Base2 RTS