;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Metroid ; ; By: Schwa, additional functionality by Chdata ; ; ; ; This sprite hovers in the air, moving back and forth while bobbing up and down. ; ; When approached by Mario, it suddenly gives chase, and this thing is FAST. It ; ; will continue to chase Mario until it is outrun or until it catches him. If it ; ; catches him, it will latch on to him and drain his coins very fast. It can then ; ; be shaken off by mashing the controller buttons repeatedly, and when this happens ; ; the Metroid falls off and dies. ; ; ; ; Setting the extra bit will make the sprite hurt (or immediately kill) the player ; ; don't have any coins. ; ; ; ; The detection range can be modified by changing Extra Property Byte 2 in the CFG. ; ; ; ; It'll drain Air from Air Meter If Extra Property Byte 1 is set to anything. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The define below specifies if the Metroid should automatically kill the player ; ; (used when the extra bit is set), or just hurt them. Set to 1 if you want the ; ; player to be killed immediately, otherwise leave it at 0. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; !KillImmediately = 0 !FreeRam = $140C|!Base2 ;MUST BE THE SAME AS IN THE AIR METER SPRITE!!! ;;;;;;;;;;;;;;;; ; Init Routine ; ;;;;;;;;;;;;;;;; print "INIT ",pc %SubHorzPos() ; Face Mario TYA STA !157C,x STZ !1528,x ; \ set Mode and Button Count to 0 STZ !C2,x ; / RTL ;;;;;;;;;;;;;;;; ; Main Routine ; ;;;;;;;;;;;;;;;; print "MAIN ",pc PHB : PHK : PLB JSR MainCode PLB RTL ;;;;;;;;;;;;;;;;;;;; ; Main Sprite Code ; ;;;;;;;;;;;;;;;;;;;; X_Speed: db $08,$F8 Y_Speed: db $00,$F8,$F2,$F8,$00,$08,$0E,$08 ;;;;;;;;;;;;;;;;;;;;;;;;;; ; Normal Behavor Routine ; ;;;;;;;;;;;;;;;;;;;;;;;;;; Return1: RTS MainCode: JSR Graphics ; call gfx routine LDA !14C8,x ; \ handle sprite if off screen or dead CMP #$08 ; | BNE Return1 ; | LDA $9D ; | BNE Return1 ; | LDA #$00 ; | %SubOffScreen() ; / INC !1570,x ; increase how many frames sprite has been on screen LDA !C2,x ; load sprite Mode ... 0 is normal, 1 is chasing, 2 is latched onto Mario CMP #$01 ; \ branch to Chasing code if needed BEQ Mode_Chasing ; / CMP #$02 ; \ branch to Latched On code if needed BEQ Mode_Draining ; / Mode_Normal: LDA !1570,x ; \ change direction every once in a while AND #$7F ; | BNE .noChangeDir ; | LDA !157C,x ; | EOR #$01 ; | STA !157C,x ; / .noChangeDir LDY !157C,x ; \ set x speed based on facing direction LDA X_Speed,y ; | STA !sprite_speed_x,x ; / LDA !1570,x ; \ LSR A ; | set y speed based on frame count, so it does the wave motion LSR A ; | every 8th frame, change LSR A ; | AND #$07 ; | TAY ; | LDA Y_Speed,y ; | STA !sprite_speed_y,x ; / JSR Proximity ; \ check to see if Mario is close to the sprite BEQ FinishStuff ; | If so, set mode to Chasing Mode LDA #$01 ; | STA !C2,x ; / BRA FinishStuff Mode_Chasing: JSR DoChase FinishStuff: JSL $01802A|!BankB ; update speed (X/Y with gravity) JSL $01A7DC|!BankB ; check for Mario contact BCC .return STZ !1534,x LDA #$02 STA !C2,x .return RTS Mode_Draining: STZ !sprite_speed_y,x STZ !sprite_speed_x,x LDA $94 ; Set sprite X = player X STA !sprite_x_low,x LDA $95 STA !sprite_x_high,x LDA $19 BNE .big LDA $96 ; Set sprite Y = player Y + #$04 CLC : ADC #$04 STA !sprite_y_low,x LDA $97 ADC #$00 STA !sprite_y_high,x BRA .notBig .big LDA $96 ; Set sprite Y = player Y STA !sprite_y_low,x LDA $97 STA !sprite_y_high,x .notBig LDA $1490|!Base2 ; \ kill sprite if Mario has a star BEQ .NoStar ; / JMP StarKill .NoStar LDA $7D ; \ prevent Mario from jumping BPL .noMarioJump ; | LDA $77 ; | BNE .noMarioJump ; | STZ $7D ; / .noMarioJump %BEC(.drainCoins) ; Don't try to hurt if extra bit is clear LDA $0DBF|!Base2 ; Don't hurt if we still have coins BNE .drainCoins if !KillImmediately == 0 JSL $00F5B7|!BankB else JSL $00F606|!BankB endif .drainCoins LDA !extra_prop_1,x BEQ .Coins LDA #$01 ; Set Drain Flag STA !FreeRam BRA .DrainOrNot .Coins LDA $0DBF|!Base2 ; \ drain coins (Or Air) from Mario if he has any .DrainOrNot ; | BEQ .noDrainCoin ; | LDA $14 ; | AND #$07 ; | change AND #$07 to different values to change the rate of coin drainage BNE .noDrainCoin ; | Make sure it's a power of 2, though (i.e. 2, 4, 8, 16, 32, etc. but in hex) LDA !extra_prop_1,x ; | BEQ .CoinsAgain ; | Don't drain coins if set to drain air. BRA .Sound ; | .CoinsAgain ; | DEC $0F33|!Base2 ; | Will drain the time .Sound ; | LDA #$01 ; | STA $1DFC|!Base2 ; / .noDrainCoin LDA $16 ; \ if buttons are being pressed, increase the button counter BEQ .noPressButton ; | INC !1534,x ; / LDA !1534,x CMP #$14 ; change CMP #$14 to other values to change how many button presses are BCC .noPressButton ; required to shake off the Metroid BRA StarKill .noPressButton RTS ;;;;;;;;;;;;;;;;; ; Death Routine ; ;;;;;;;;;;;;;;;;; Killed_X_Speed: db $F0,$10 StarKill: LDA #$02 ; Kill sprite STA !14C8,x LDA #$D0 ; Set killed Y speed STA !sprite_speed_y,x %SubHorzPos() ; Set killed X speed LDA Killed_X_Speed,y STA !sprite_speed_x,x LDA #$02 STA !1602,x ; write frame to show RTS ;;;;;;;;;;;;;;;;; ; Chase Routine ; ;;;;;;;;;;;;;;;;; X_Accel: db $02,$FE X_MaxSpeed: db $20,$E0 Y_Accel: db $04,$FC Y_MaxSpeed: db $10,$F0 DoChase: %SubHorzPos() ; \ LDA !sprite_speed_x,x ; | chase Mario horizontally CMP X_MaxSpeed,y ; | BEQ .doY ; | CLC : ADC X_Accel,y ; | STA !sprite_speed_x,x ; / .doY %SubVertPos() ; \ LDA !sprite_speed_y,x ; | chase Mario vertically CMP Y_MaxSpeed,y ; | BEQ .return ; | CLC : ADC Y_Accel,y ; | STA !sprite_speed_y,x ; / .return RTS ;;;;;;;;;;;;;;;;;;;;; ; Detection Routine ; ;;;;;;;;;;;;;;;;;;;;; InvertTable: db $FF,$00 Proximity: LDA !sprite_x_high,x ; A = sprite X position (16-bit) XBA LDA !sprite_x_low,x REP #$20 SEC : SBC $94 ; Subtract player X SEP #$20 PHA %SubHorzPos() PLA EOR InvertTable,y ; Invert if needed CMP !extra_prop_2,x ; Range define by Extra Prop 2 BCS .outOfRange ; return if not within range LDA #$01 ; Z = 0 RTS .outOfRange LDA #$00 ; Z = 1 RTS ;;;;;;;;;;;;;;;;;;;; ; Graphics Routine ; ;;;;;;;;;;;;;;;;;;;; Tilemap: db $C8,$A8,$C8 ; the third frame is the death frame Graphics: %GetDrawInfo() LDA !157C,x ; $02 = direction STA $02 LDA !14C8,x ; If killed... CMP #$02 BNE NotKilled LDA #$02 ; ...set killed frame STA $03 LDA !15F6,x ; ...flip vertically ORA #$80 STA !15F6,x BRA DrawSprite NotKilled: LDA $14 ; Set walking frame based on frame counter rep 3 : LSR A CLC : ADC $15E9|!Base2 AND #$01 STA $03 DrawSprite: PHX LDA $00 ; Tile x position = sprite x location ($00) STA $0300|!Base2,y LDA $01 ; Tile y position = sprite y location ($01) STA $0301|!Base2,y LDA !15F6,x ; Tile properties YXPPCCCT ORA $64 ; Add in tile priority of level STA $0303|!Base2,y LDX $03 ; Store tile LDA Tilemap,x STA $0302|!Base2,y PLX LDY #$02 ; Set tiles to 16x16 LDA #$00 ; We drew 1 tile JSL $01B7B3|!BankB RTS