;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Bowser Jr., by dahnamics ;; ;; Credit: mikeyk (Shy Guy), Yoshicookiezeus (Thwomp boss v. 2.0), Schwa (sledge_ex) ;; ;; Description: A Shy Guy with many available configurations. ;; ;; Note: When rideable, clipping tables values should be: 03 0A FE 0E ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Shy Guy, by mikeyk ;; ;; Description: A Shy Guy with many available configurations. ;; ;; Note: When rideable, clipping tables values should be: 03 0A FE 0E ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Uses first extra bit: NO ;; ;; Extra Property Byte 1 ;; bit 0 - enable spin killing ;; bit 1 - stay on ledges ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; incsrc "sprites/EnemyHPDefines/EnemyHP.asm" incsrc "sprites/EnemyHPDefines/GraphicalBarDefines.asm" incsrc "sprites/EnemyHPDefines/SubroutinesLocations.asm" !HPSystemType = 0 ;>0 = vanilla (count up), 1 = 16-bit HP (direct use) !HPToStart = 20 ;>Decimal, amount of HP the enemy has. !StompDamage = 5 ;>Decimal, amount of damage from stomping. !SPRITE_Y_SPEED = $AA !SPRITE_X_SPEED = $B6 !SPRITE_Y_POS = $D8 !SPRITE_Y_POS_HI = $14D4 !SPRITE_X_POS = $E4 !SPRITE_X_POS_HI = $14E0 !SPRITE_FACING = $157C !SPRITE_CUSTOMSPRNUMB = $7FAB9E !AlreadyEarthquake = $151C ;>So that it earthquakes for 1 frame !H_OFFSCREEN = $15A0 !V_OFFSCREEN = $186C !FREEZE_TIMER = $1540 !SMASH_STATUS = $1602 !STATE_TIMER = $163E ;>Not used !RAM_ThrowTimer = $1504 ;>Used as a misc timer, like when will it hop. !SPR_OBJ_STATUS = $1588 ;>Blocked status table. !ClimbingPeak = $0110 ; ^During wall climb, if the Y position the koopas reaches that value, will revert to walking ; The lower the value, the higher. Here is the format: $XXyz. Uppercase XX is what screen number ; (or subscreen in a horizontal level, while lowercase Y is what 16x16 block, and z is pixels ; within the 16x16 block). !DeathWait = 120 ; ^Amount of time (in frames; 60 is 1 sec.) before the koopa lunges into the wall upon defeat. !EXTRA_BITS = $7FAB10 !EXTRA_PROP_1 = $7FAB28 !GetSpriteClippingA = $03B69F !CheckForContact = $03B72B !GetSpriteClippingB = $03B6E5 !ShowSprContactGfx = $01AB72 !Invuln_Timer = $1564 ; The RAM address for Clawgrip's stun duration !MAX_Y_SPEED = $3E ;\Not used? !MAX_Y_SPEED2 = $C2 ;/ ; definitions of bits (don't change these) !IS_ON_GROUND = $04 !IS_ON_CEILING = $08 !TIME_ON_GROUND = $A0 !TIME_TO_SHAKE = $18 !RISE_SPEED = $C0 !SOUND_EFFECT = $09 !SPRITE_GRAVITY = $20 !SPRITE_GRAVITY2 = $04 !TIME_TO_EXPLODE = $00 X_SPEED: db $20,$E0 X_SPEED2: db $40,$C0 KILLED_X_SPEED: db $F0,$10 !SPRITE_STATE = $C2 ;>What actions the sprite is doing. !HIT_POINTS = $09 ;>How much hits to defeat, do note that the hit counter, $1534 increments. ;Sprite to spawn: !SPRITE_TO_GEN = $B5 ;>Custom sprite number for fireball. !SPRITE_TO_GEN2 = $B6 ;>Custom sprite number for rock. !SPRITE_TO_GEN3 = $00 ;>Normal sprite to spawn when a green shell throws 2 things. !SPRITE_TO_GEN4 = $00 ;>A second version of above. !NumbOfSprSlots = 12 ;>Number of slots: 12 for normal, 22 for sa-1. !JumpPeriod = $C0 ;>The lower, the more often it jumps ($C0 default) XMAX: db $20,$D0 XACCEL: db $02,$FE NEXT_STATE: db $05,$03,$04,$06 DATA_03CC38: db $F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0 ; x positions to appear DATA_03CC40: db $30,$40,$50,$60,$70,$80,$90,$A0 ; y positions to appear, Lemmy DATA_03CC5A: db $00,$01,$02,$03,$04,$05,$06,$00 ;\ indexes for x position table db $01,$02,$03,$04,$05,$06,$00,$01 ; | db $02,$03,$04,$05,$06,$00,$01,$02 ; | db $03,$04,$05,$06,$00,$01,$02,$03 ; | db $04,$05,$06,$00,$01,$02,$03,$04 ; | db $05,$06,$00,$01,$02,$03,$04,$05 ;/ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; sprite init JSL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; print "INIT ",pc if !HPSystemType != 0 .StartWithFullHP LDA.b #!HPToStart ;\Full HP (low byte) STA !Freeram_SprTbl_CurrHPLow,x ;| STA !Freeram_SprTbl_MaxHPLow,x ;/ LDA.b #!HPToStart>>8 ;\Full HP (High byte) STA !Freeram_SprTbl_CurrHPHi,x ;| STA !Freeram_SprTbl_MaxHPHi,x ;/ endif LDA #$01 ;\Prevent premature earthquake STA !AlreadyEarthquake,x ;/ LDA $E4,x ;\Move sprite 8 pixels down CLC ;| ADC #$08 ;| STA $E4,x ;/ LDA $14D4,x ;\[new]fix an overflow, this time, uses 2 bytes of the position ADC #$00 ;| STA $14D4,x ;/ TXA ;\[GHB's note] I do not know why this sprite is programmed AND #$03 ;|to use its current sprite slot for the state and throw timer. ASL A ;| ASL A ;| ASL A ;| ASL A ;| ASL A ;| STA !STATE_TIMER,x ;| CLC ;| ADC #$32 ;| STA !RAM_ThrowTimer,x ;/ PHY JSR SUB_HORZ_POS TYA STA !SPRITE_FACING,x PLY LDA !SPR_OBJ_STATUS,x ; if on the ground, reset the turn counter ORA #$04 STA !SPR_OBJ_STATUS,x ; if on the ground, reset the turn counter RTL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; sprite code JSL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; print "MAIN ",pc PHB ;\main sprite function, just calls local subroutine PHK ;| PLB ;| JSR SPRITE_CODE_START ;| PLB ;| RTL ;/ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; sprite main code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; RETURN: RTS SPRITE_CODE_START: ; LDA $1534,x ;>[debug] display HP on hud (only 0-9 works correctly) ; LDA !AlreadyEarthquake,x ;>[debug] display earthquake status on HUD ; STA $0EF9 ;>write to HUD. ;debug2 ; LDA !RAM_ThrowTimer,x ;\[debug] Display hop timer ; LSR #$04 ;| ; STA $0EF9 ;| ; LDA !RAM_ThrowTimer,x ;| ; AND #$0F ;| ; STA $0EFA ;/ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;HP value converter (if vanilla) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; if !HPSystemType == 0 LDA.b #!HPToStart ;\Set max HP STA !Freeram_SprTbl_MaxHPLow,x ;/ SEC ;\RemainingHitsLeft = KillingValue - TotalDamageTaken SBC $1534,x ;/ STA !Freeram_SprTbl_CurrHPLow,x ;>And display HP correctly LDA #$00 ;\Rid high bytes. STA !Freeram_SprTbl_CurrHPHi,x ;| STA !Freeram_SprTbl_MaxHPHi,x ;/ endif JSL !SubrAddr_RemoveRecordEffect ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Graphics based on sprite state ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LDA !SPRITE_STATE,x ;\Palette for when the koopa is walking or waiting CMP #$01 ;| BNE RED_SHELL ;| JSR SUB_GFX3 ;| BRA CONTINUE ;/ RED_SHELL: LDA !SPRITE_STATE,x ;\Palette for when the koopa is shooting fireballs CMP #$03 ;| BNE YELLOW_SHELL ;| JSR SUB_GFXF ;| BRA CONTINUE ;/ YELLOW_SHELL: LDA !SPRITE_STATE,x ;\Palette when pelting rocks. CMP #$05 ;| BNE BLUE_SHELL ;| JSR SUB_GFXR ;| BRA CONTINUE ;/ BLUE_SHELL: LDA !SPRITE_STATE,x ;\Palette for wall climbing CMP #$06 ;| BNE GREEN_SHELL ;| JSR SUB_GFXJ ;| BRA CONTINUE ;/ GREEN_SHELL: JSR SUB_GFX ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;HUGE contact routine below ;;;;;;;;;;;;;;;;;;;;;;;;;;;; CONTINUE: LDA $14C8,x ; return if sprite status != 8 CMP #$08 ;\If sprite status isn't normal (like dying), return BNE RETURN ;/ LDA $9D ;\If freeze time is set, return BNE RETURN ;/ JSR SUB_OFF_SCREEN_X3 ; handle off screen situation LDA !SPRITE_X_POS,x ;\Preserve normal X position PHA ;| LDA !SPRITE_X_POS_HI,x ;| PHA ;/ LDA !SPRITE_Y_POS,x ;\Preserve normal Y position PHA ;| LDA !SPRITE_Y_POS_HI,x ;| PHA ;/ DONE_WITH_SPEED: JSL $018032 ;>interact with other sprites ; LDA $1656,x ;\Set collision points for objects. ; ORA.b #%00000111 ;| ; STA $1656,x ;| ; LDA $1656,x ;| ; AND.b #%11110111 ;| ; STA $1656,x ;/ LDA !SPRITE_STATE,x ;\If koopa is walking, then use the 32x40 CMP #$01 ;|hitbox for mairo. BEQ STANDING_UP ;/ ;IN_MASK: LDA !SPRITE_Y_POS,x ;\Adjust Y pos... CLC ;| ADC #$08 ;| STA !SPRITE_Y_POS,x ;/ LDA !SPRITE_Y_POS_HI,x ;\...High byte ADC #$00 ;| STA !SPRITE_Y_POS_HI,x ;/ LDA #$23 ;\Modify clipping (32x32) STA $1662,x ;/ JSL $01A7DC ;>interact with mario (with shifted position) BRA CLIPPING_DONE STANDING_UP: LDA !SPRITE_X_POS,x ;\Adjust X pos... CLC ;| ADC #$08 ;| STA !SPRITE_X_POS,x ;/ LDA !SPRITE_X_POS_HI,x ;\...high byte ADC #$00 ;| STA !SPRITE_X_POS_HI,x ;/ LDA #$28 ;\Modify clipping (32x40) STA $1662,x ;/ JSL $01A7DC ;>interact with mario (with shifted position) CLIPPING_DONE: PLA ;\Restore Y pos STA !SPRITE_Y_POS_HI,x ;| PLA ;| STA !SPRITE_Y_POS,x ;/ PLA ;\Restore X pos STA !SPRITE_X_POS_HI,x ;| PLA ;| STA !SPRITE_X_POS,x ;/ BCS + ;>return if no contact (stupid branch out of range) JML NO_CONTACT + LDA $154C,x ;\If invulnerability is running, can't touch BNE NO_CONTACT ;/ LDA #$08 ;\sprite invincibility timer = $08 STA $154C,x ;/ ; LDA $7D ;\If mario's Y speed is between #$70-#$FF OR #$00-#$10, then sprite ; CMP #$10 ;|damages Mario (this is buggy, if you touch a sprite ON ANY SIDE while ; BMI SPRITE_WINS ;/going downwards fast enough, ALWAYS registers as a stomp). LDA !SPRITE_Y_POS_HI,x ;\This basically checks if mairo is in the upper section of the XBA ;|sprite's hitbox. If mario is below or sprite is above mario, assumes LDA !SPRITE_Y_POS,x ;|touching the sides and bottom and hurts the player. REP #$20 ;| SEC ;|\Move the line between "stomps counts" and "boss hurts mario" upwards. SBC #$0028 ;|/ CMP $96 ;/ SEP #$20 ; BMI SPRITE_WINS ;>If sprite's "hurtbox" is much higher up than mairo, damage mario. MARIO_WINS: JSL $01AA33 ;>set Mario Y speed JSL $01AB99 ;>display contact graphic LDA !SPRITE_STATE,x ;\If the sprite is not walking, its invulnerable CMP #$01 ;| BNE NO_COUNT ;/ ;Damage the boss: JSR SUB_STOMP_PTS ;>give Mario points LDA #$28 ;\ sound effect STA $1DFC ;/ LDA #$A0 ;\ Set THROW_FIRE timer STA !Invuln_Timer,x ;/ if !HPSystemType == 0 if !StompDamage <= 1 ;>In case if a dumb user wants the boss to take 0 damage. INC $1534,x ;>increment sprite hit counter else LDA $1534,x CLC ADC.b #!StompDamage STA $1534,x endif LDA #$60 ;\Freeze record effect every hit. STA !Freeram_SprTbl_RecordEffTmr,x ;/ TXA ;>Copy sprite index to A STA !Freeram_CurrSprSlot ;>Make HP bar switch to measure GMK's HP. LDA $1534,x ;\If it has incremented to the "max", do death animation. CMP.b #!HPToStart ;/ BCC SPRITE_NOT_DEAD LDA.b #!HPToStart ;\Cannot be damaged even further (else remainding hits left becomes negative) STA $1534,x ;/ BRA SPRITE_DEAD else REP #$20 LDA.w #!StompDamage STA $00 SEP #$20 JSL !SubrAddr_LoseHP BCS SPRITE_DEAD endif SPRITE_NOT_DEAD: LDA #$02 ;\Sprite retreats into its mask STA !SPRITE_STATE,x ;/ BRA NEW_RETURN ;>And return. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SPRITE_DEAD: LDA #$07 ;\Initalize death countdown STA !SPRITE_STATE,x ;/ LDA.b #!DeathWait ;\Set timer before lunging at the player STA !FREEZE_TIMER,x ;/ NO_COUNT: LDA #$02 ;\sound effect, like trying to normal-jump a flashing kamikaze STA $1DF9 ;/koopa shell. NEW_RETURN: RTS ;>return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SPRITE_WINS: LDA $1497 ;\If mario is invincible or riding yoshi, return ORA $187A ;| BNE NO_CONTACT ;/ JSR SUB_HORZ_POS ;\Switch direction. TYA ;| STA !SPRITE_FACING,x ;/ JSL $00F5B7 ;>hurt Mario NO_CONTACT: ; LDA !SPRITE_STATE,x ; CMP #$00 ; BEQ WAITING ; CMP #$01 ; BEQ WALKING0 ; CMP #$02 ; BEQ SPINNING0 ; CMP #$03 ; BEQ THROW_FIRE0 ; CMP #$04 ; BEQ TOSS_KOOPA0 ; CMP #$05 ; BEQ SPAWN_ROCKS0 ; CMP #$06 ; BEQ RISING0 ; CMP #$07 ; BEQ DEAD0 ; CMP #$08 ; BEQ COUNTDOWN0 ; ;WALKING0: BRA WALKING1 ;SPINNING0: JMP SPINNING ;THROW_FIRE0: JMP THROW_FIRE ;TOSS_KOOPA0: JMP TOSS_KOOPA ;SPAWN_ROCKS0: JMP SPAWN_ROCKS ;RISING0: JMP RISING ;DEAD0: JMP DEAD ;COUNTDOWN0: JMP COUNTDOWN ;^This is inefficient, there exist a pointer table that is much smaller: ;(warning: does not work on long address, only 2-byte address). PHX ;>Preserve x since its reserved for current sprite slot. LDA !SPRITE_STATE,x ;>Load what points to what loaction ASL ;>Multiply by 2 because the address are 16-bit (2 bytes), using dw TAX ;>Transfer to x register BCS .LargeTbl ;>In case if you have more than 128 different possibilities JMP.w (States,x) ;>Pick table .LargeTbl ; JMP.w (States+256,x) ;>Pick table, for larger size. ;In case if you are wondering, JMP($xxxx,y) or JML ($xxxxxx,y) does not exist in the opcodes ;list. States: dw WAITING ;>$00 (x=$00) dw WALKING ;>$01 (x=$02) dw SPINNING ;>$02 (x=$04) dw THROW_FIRE ;>$03 (x=$06) dw TOSS_KOOPA ;>$04 (x=$08) dw SPAWN_ROCKS ;>$05 (x=$0A) dw RISING ;>$06 (x=$0C) dw DEAD ;>$07 (x=$0E) dw COUNTDOWN ;>$08 (x=$10) ;----------------------------------------------------------------------------------------- ; state 0 ;----------------------------------------------------------------------------------------- WAITING: PLX LDA !V_OFFSCREEN,x ;fall if offscreen vertically BNE SET_WALKING LDA !H_OFFSCREEN,x ;return if offscreen horizontally BNE RETURN0 JSR SUB_HORZ_POS ;determine if mario is close and act accordingly TYA STA !SPRITE_FACING,x LDA !SPR_OBJ_STATUS,x ;\If sprite has its left or right blocked status set, AND #$03 ;|then flip its left/right direction. BEQ NO_OBJ_CONTACT0 ;| LDA !SPRITE_FACING,x ;| EOR #$01 ;| STA !SPRITE_FACING,x ;/ NO_OBJ_CONTACT0: LDA !SPR_OBJ_STATUS,x ; run the subroutine if the sprite is in the air... ORA !SPRITE_STATE,x ; ...and not already turning BNE ON_GROUND0 ; JSR SUB_CHANGE_DIR ; LDA #$01 ; STA !SPRITE_STATE,x ;>[GHB's fix], that should not store to origional Y position ON_GROUND0: LDA !SPR_OBJ_STATUS,x ;\if on the ground, reset the turn counter AND #$04 ;| BEQ RETURN0 ;/ STZ !SPRITE_STATE,x ;\Hang in the air STZ !SPRITE_Y_SPEED,x ;/ BRA X_TIME0 FALLING0: LDA !SPR_OBJ_STATUS,x ;\if on the ground, reset the turn counter AND #$04 ;| BEQ RETURN0 ;/ LDA #$10 ;\Set Y speed. STA !SPRITE_Y_SPEED,x ;/ X_TIME0: STZ !SPRITE_X_SPEED,x ;>Clear X speed BCS RETURN0 LDA $0F ;\Use close proximity similar to a thwomp CLC ;| ADC #$40 ;| CMP #$80 ;| BCS THWOMP_4 ;/ THWOMP_4: LDA $0F ;\Similar to above but when in its "suspicious face" CLC ;| ADC #$80 ;| CMP #$50 ;| BCS RETURN0 ;/ ;Basically, those two above determine if the sprite should "wake up" to initiate the boss fight. SET_WALKING: ;Since this section of code here runs once, this is the best place to have an intro HP meter. TXA ;>Copy sprite index to A STA !Freeram_CurrSprSlot ;>Make HP bar switch to measure GMK's HP. LDA #$00 ;\Start empty and fill up. STA !Freeram_SprTbl_RecordEfft,x ;/ INC !SPRITE_STATE,x ;>Switch state to walking. WALKING1: JMP WALKING_PULLX_Done ;>Run walking routine RETURN0: JSL $01802A ; update position based on speed values (gravity + layer interaction) RTS ;----------------------------------------------------------------------------------------- ; state 1, walking (mario can damage the boss in this state) ;----------------------------------------------------------------------------------------- WALKING: PLX WALKING_PULLX_Done: EarthquakeRoutine: LDA !SPR_OBJ_STATUS,x ;\If not on ground, return (prevent air quakes). AND #$04 ;| BEQ .AirClear ;/ LDA !SPRITE_Y_SPEED,x ;\so it doesn't get confused between jumping and landing CMP #$08 ;| BMI .EarthquakeDone ;/ LDA !AlreadyEarthquake,x ;\If earthquake already set, don't run this again. BNE .EarthquakeDone ;/ INC !AlreadyEarthquake,x ;>And flag it LDA #!TIME_TO_SHAKE ;\shake ground STA $1887 ;/ LDA $77 ;\\If mairo is on the ground... AND #$04 ;|/ BEQ .NO_LOCK_MARIO ;|>Then no stun. LDA $1497 ;|>OR if mairo is invulnerabe after a hit... BNE .NO_LOCK_MARIO ;|>then no stun (comment it out if stun while immume). LDA #!TIME_TO_SHAKE ;| STA $18BD ;/ .NO_LOCK_MARIO LDA #!SOUND_EFFECT ;\play sound effect STA $1DFC ;/ JSR SUB_SMOKE ;\Smoke effect JSR SUB_SMOKE2 ;/ BRA .EarthquakeDone .AirClear STZ !AlreadyEarthquake,x .EarthquakeDone LDA !SPR_OBJ_STATUS,x ;\If sprite has its left or right blocked status AND #$03 ;|set, then flip horizontal direction. BEQ + ;| LDA !SPRITE_FACING,x ;| EOR #$01 ;| STA !SPRITE_FACING,x ;/ + LDY !SPRITE_FACING,x ;\ set x speed based on direction LDA X_SPEED,y ;| STA !SPRITE_X_SPEED,x ;/ JSR Hop ;>Jump to custom code IN_THE_AIR: JSL $01802A ; update position based on speed values (gravity + layer interaction) RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Hop: LDA #!JumpPeriod ;\If timer less than C0.. CMP !RAM_ThrowTimer,x ;| BCS IncreaseHop ;/then Increase, and return. STZ !RAM_ThrowTimer,x ;>Reset timer so it doesn't overflow (just in case). LDA !SPR_OBJ_STATUS,x ;\If not on ground, return (prevent air jumping). AND #$04 ;| BEQ RETURN68 ;/ LDA #$C0 ;\Set jump up-speed. STA !SPRITE_Y_SPEED,x ;/ ; LDA #$01 ;\Jump sfx, remove comment if want to. ; STA $1DFA ;/ RETURN68: RTS IncreaseHop: INC !RAM_ThrowTimer,x ;>Increase timer. RTS ;>Return ;----------------------------------------------------------------------------------------- ; state 2 (chasing mario in its shell) ;----------------------------------------------------------------------------------------- SPINNING: PLX NOSPRC: JSR SUB_HORZ_POS ;\Determine if mario is either left or right of the sprite. TYA ;|And that the sprite will direct itself towards mario LDA XMAX,y ;|\Prevent over-acceleration. CMP !SPRITE_X_SPEED,x ;|| BEQ SETSPD ;// LDA !SPRITE_X_SPEED,x ;\In laws of physics, acceleration is simply the change in velocity. CLC ;| ADC XACCEL,y ;| STA !SPRITE_X_SPEED,x ;/ SETSPD: LDA !SPR_OBJ_STATUS,x ;\If not on the ground, then detect air AND #$04 ;| BEQ NOWALLS ;/ STZ !SPRITE_Y_SPEED,x ;>Zero Y speed to prevent falling through the ground. NOWALLS: LDA !SPR_OBJ_STATUS,x ;\If not contacting the wall, keep going in that direction. AND #$03 ;| BEQ INAIR ;/ LDA !SPRITE_X_SPEED,x ;\Otherwise flip x speed EOR #$FF ;| INC A ;| STA !SPRITE_X_SPEED,x ;/ NOSMSH: LDA $77 ;\If mario is not on ground, then switch state AND #$04 ;| BEQ INITIALIZE ;| LDA #$20 ;| STA $18BD ;/ INITIALIZE: PHX ;\Execute random number generation for switching sprite state LDA #$03 ;| JSL RANDOM ;| TAX ;| LDA NEXT_STATE,x ;| PLX ;| STA !SPRITE_STATE,x ;/ INAIR: JSL $01802A ; update position based on speed values (gravity + layer interaction) RETURN2: RTS ;------------------------------------------------------------------------------------------ RANDOM: PHX PHP SEP #$30 PHA JSL $01ACF9 ; Random number generation routine PLX CPX #$FF ;\ Handle glitch if max is FF BNE NORMALRT ;| LDA $148B ;| BRA ENDRANDOM ;/ NORMALRT: INX ; Amount in plus 1 LDA $148B ;\ STA $4202 ;| Multiply with hardware regsisters STX $4203 ;| NOP ;| NOP ;| NOP ;| NOP ;/ LDA $4217 ENDRANDOM: PLP PLX RTL ;----------------------------------------------------------------------------------------- ; state 3 (red shell throwing fireballs at mario) ;----------------------------------------------------------------------------------------- THROW_FIRE: PLX LDA !SPR_OBJ_STATUS,x ;\if sprite is in contact with an object... AND #$03 ;|flip the direction status BEQ NO_OBJ_CONTACTF ;| LDA !SPRITE_FACING,x ;| EOR #$01 ;| STA !SPRITE_FACING,x ;/ NO_OBJ_CONTACTF: STZ !SPRITE_X_SPEED,x JSR FIRE_SPAWN ;>Jump to custom code JSL $01802A ; update position based on speed values (gravity + layer interaction) FIRE_SPAWN: LDA #$C0 ;\If timer isn't A0.. CMP $1528,x ;| BNE IncreaseSpawn ;|Increase it. STZ $1528,x ;/Reset timer. LDA #$01 ;\Switch to walking STA !SPRITE_STATE,x ;/ STA !AlreadyEarthquake,x ;>And don't earthquake upon switching back RTS ;>Return IncreaseSpawn: LDA $1528,x ;\If timer = #$20, shoot a fireball CMP #$20 ;| BNE ONE_SHOT ;/ JSR SUB_HAMMER_THROW ;>shoot fireball subroutine (the author didn't rename the labels, but doesn't matter) BRA INC_TIME ;>Increment timer and return ONE_SHOT: CMP #$40 ;\If timer = #$40, shoot 2 fireballs BNE TWO_SHOTS ;| JSR SUB_HAMMER_THROW ;/ BRA INC_TIME ;>Increment timer and return TWO_SHOTS: CMP #$60 ;\If timer = #$60, shoot 3 fireballs BNE THREE_SHOTS ;| JSR SUB_HAMMER_THROW ;/ BRA INC_TIME ;>Increment tiemr and return THREE_SHOTS: CMP #$80 ;\If timer = #$80, shoot 4 fireballs BNE FOUR_SHOTS ;| JSR SUB_HAMMER_THROW ;/ BRA INC_TIME ;>increment timer and return. FOUR_SHOTS: CMP #$A0 ;\If one last time, return BNE NO_FIRE ;/ JSR SUB_HAMMER_THROW ;>Shoot 1 extra fireball? NO_FIRE: INC_TIME: INC $1528,x ;>Increase timer. RETURN65: RTS ;>and return. ;----------------------------------------------------------------------------------------- ; state 4 (throw 2 green beach koopas). ;----------------------------------------------------------------------------------------- TOSS_KOOPA: PLX LDA !SPR_OBJ_STATUS,x ;\if sprite is in contact with an object... AND #$03 ;|flip the direction status BEQ NO_OBJ_CONTACTK ;| LDA !SPRITE_FACING,x ;| EOR #$01 ;| STA !SPRITE_FACING,x ;/ NO_OBJ_CONTACTK: LDY !SPRITE_FACING,x ;\ set x speed based on direction LDA X_SPEED,y ;| STA !SPRITE_X_SPEED,x ;/ JSR KOOPA_TOSS ;>Jump to custom code IN_THE_AIR_ONE: JSL $01802A ; update position based on speed values (gravity + layer interaction) KOOPA_TOSS: LDA #$80 ;\If timer isn't 80.. CMP $1528,x ;/ BNE IncreaseToss ;>Increase it. STZ $1528,x ;>Reset timer. JSR SUB_HAMMER_THROW3 ;\Again, the author didn't rename the labels, but doesn't matter JSR SUB_HAMMER_THROW4 ;/they spawn koopas LDA #$01 ;\switch to walking state STA !SPRITE_STATE,x ;/ STA !AlreadyEarthquake,x ;>And don't earthquake upon switching back RTS ;>Return IncreaseToss: INC $1528,x ;>Increase timer. RETURN81: RTS ;----------------------------------------------------------------------------------------- ; state 5 (yellow shell pelting rocks) ;----------------------------------------------------------------------------------------- SPAWN_ROCKS: PLX LDA !SPR_OBJ_STATUS,x ;\if sprite is in contact with an object... AND #$03 ;|flip the direction status BEQ NO_OBJ_CONTACTR ;| LDA !SPRITE_FACING,x ;| EOR #$01 ;| STA !SPRITE_FACING,x ;/ NO_OBJ_CONTACTR: STZ !SPRITE_X_SPEED,x ;>Stop horizontally JSR ROCK_SPAWN ;Jump to custom code JSL $01802A ; update position based on speed values (gravity + layer interaction) ROCK_SPAWN: LDA #$C0 ; If timer isn't C0.. CMP $1528,x ; BNE IncreaseSpawn2 ; Increase it. STZ $1528,x ; Reset timer. LDA #$01 ;\Switch to walking STA !SPRITE_STATE,x ;/ STA !AlreadyEarthquake,x ;>And don't earthquake upon switching back RTS ; Return IncreaseSpawn2: LDA $1528,x ;>Check if the timer is greater than $20 CMP #$20 ;\Same as the fireballs, if the timer is BNE ONE_ROCK ;|divisible by #$20 but not higher than #$A0, JSR SUB_HAMMER_THROW2 ;|spawn a rock BRA INC_TIME2 ;| ONE_ROCK: CMP #$40 ;| BNE TWO_ROCKS ;| JSR SUB_HAMMER_THROW2 ;| BRA INC_TIME2 ;| TWO_ROCKS: CMP #$60 ;| BNE THREE_ROCKS ;| JSR SUB_HAMMER_THROW2 ;| BRA INC_TIME2 ;| THREE_ROCKS: CMP #$80 ;| BNE FOUR_ROCKS ;| JSR SUB_HAMMER_THROW2 ;| BRA INC_TIME2 ;| FOUR_ROCKS: CMP #$A0 ;| BNE NO_ROCKS ;| JSR SUB_HAMMER_THROW2 ;/ NO_ROCKS: INC_TIME2: INC $1528,x ; Increase timer. RETURN75: RTS ;----------------------------------------------------------------------------------------- ; state 6 (blue shell that wall climbs) ;----------------------------------------------------------------------------------------- RISING: PLX LDA !FREEZE_TIMER,x ;if we're still waiting on the ground, return BNE RETURN7 ;LDA #$08 ;\Whats this?!? this will either glitch up the position ;STA !OrigPos,x ;/or make it die when attempting this move! ;LDA !OrigPos,x ;check if the sprite is in original position ;CMP !SomeYPos,x ;BNE RISE LDA $14D4,x ;\Transfer sprite Y high position to high byte of the A register. XBA ;/(A= $YYyy), note that A has a high byte, even in 8-bit mode, unlike x or y register. LDA $D8,x ;>Load low byte of Y position REP #$20 ;>16-bit A (placed AFTER the LDA so it doesn't load 2 slots at $D8). CMP #!ClimbingPeak ;\If the sprite is below the climbing peak, keep going up. SEP #$20 ;|(as the Y position increases, the sprite moves downwards, thats how positioning works). BPL RISE ;/ ;LDA #$80 ;\[debug] Execution test, as an attempt to fix a "boss leaves the arena" ;STA $7D ;/when performing this move in midair after touching the wall. LDA #$01 ;\Switch to walking. STA !SPRITE_STATE,x ;/ RTS ;>return RISE: LDY $1528,x LDA #!RISE_SPEED ;\set rising speed STA !SPRITE_Y_SPEED,x ;/ JSL $01801A ;>Apply speed RETURN7: RTS ;----------------------------------------------------------------------------------------- ; state 7 timer before lunging at the player ;----------------------------------------------------------------------------------------- DEAD: PLX LDA !FREEZE_TIMER,x BNE RETURN10 STZ !SPRITE_X_SPEED,x ;>Clear X speed STZ !SPRITE_Y_SPEED,x ;>Clear Y speed INC !SMASH_STATUS,X ;\If smash status incremented to #$02, then switch state. LDA !SMASH_STATUS,x ;| CMP #$02 ;| BCC + ;/ INC !SPRITE_STATE,x ;chage state to dying JSR SUB_HORZ_POS ;\Update direction UNTIL the sprite lunges (direct to mario) TYA ;| STA !SPRITE_FACING,x ;/ + JSL $01801A ;>Don't float in the air if stomped in air. RETURN10: RTS ;----------------------------------------------------------------------------------------- ; state 8 (about to die) ;----------------------------------------------------------------------------------------- COUNTDOWN: PLX LDA !SPR_OBJ_STATUS,x ;\if sprite is in contact with an object... AND #$03 ;|then die BEQ NO_OBJ_CONTACT1 ;| LDA #$04 ;| STA $14C8,x ;/ JSL $07FC3B ;>show star animation LDA #$08 ;\ play sound effect STA $1DF9 ;/ LDA !EXTRA_BITS,x ;\If extrabit is clear, then skip ending the level. AND #$04 ;| BEQ CLEAR ;/ GOAL: DEC $13C6 ; prevent Mario from walking at the level end LDA #$FF ; \ set goal STA $1493 ; / LDA #$0B ; \ set ending music STA $1DFB ; / RTS ; return ;BRA CONTINUE_END ;>what? This is nonsence, you don't need that. CLEAR: CONTINUE_END: NO_OBJ_CONTACT1: LDY !SPRITE_FACING,x ;\ set x speed based on direction LDA X_SPEED2,y ;| STA !SPRITE_X_SPEED,x ;/ RETURN70: JSL $01802A ; update position based on speed values (gravity + layer interaction) RTS ;return ;Subroutines below: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; hammer routine (spawns fireballs actually) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; HitboxOffsets: dw $0000, $0010 ;16x32hitbox, 16x16hitbox X_OFFSET: db $06,$FA X_OFFSET2: db $00,$FF SUB_HAMMER_THROW: LDA #!SPRITE_TO_GEN ;\Fireball's sprite number STA $00 ;/ SEC ;>Custom sprite mode JSR SpawnSprite ;>Spawn custom sprite (Y = new sprite's index) BCC + JMP .RETURN ;>If all slots indexes taken, return (not even a sound) + LDA #$17 ;\ sound effect STA $1DFC ;/ PHY ;>Save y index of the new sprite LDA !SPRITE_FACING,x ;\Transfer facing to X position the fireball spawns TAY ;/ LDA $E4,x ;\ set xlo position for new sprite CLC ;| ADC X_OFFSET,y ;| PLY ;|>Get new sprite's y index STA $00E4,y ;/>And set the new sprite's position PHY ;>Save y index of the new sprite LDA !SPRITE_FACING,x ;\ TAY ;/ LDA $14E0,x ;\set xhi position for new sprite ADC X_OFFSET2,y ;| PLY ;|>Get new sprite's y index STA $14E0,y ;/ LDA !SPRITE_Y_POS,x ;\set y position for new sprite SEC ;|(y position of generator - 1) SBC #$0E ;| STA $00D8,y ;| LDA $14D4,x ;| SBC #$00 ;| STA $14D4,y ;/ ;setup aiming input: LDA !SPRITE_X_POS_HI,y ;\find horizontal distance (signed, D = S - M) XBA ;| LDA.w !SPRITE_X_POS,y ;| REP #$20 ;| SEC ;| SBC $94 ;| STA $00 ;| SEP #$20 ;/ PHX ;>Save GMK's slot index LDA $19 ;\If small mario, 16x16 hitbox, aim for bottom of 16x32 BEQ .16x16Hitbox ;/ LDA $73 ;\While big, if crouching, aim for bottom of 16x32 BNE .16x16Hitbox ;/ ;16x32Hitbox LDX #$00 ;>Load for aiming towards the bottom half of mario BRA + .16x16Hitbox LDX #$02 ;>Load for aiming towards the top half of mario + LDA !SPRITE_Y_POS_HI,y ;\Same as above but vertical. XBA ;| LDA.w !SPRITE_Y_POS,y ;| REP #$20 ;| SEC ;| SBC $96 ;| SEC ;|\Don't aim for the upper half of mairo if small/crouching. SBC HitboxOffsets,x ;|/ STA $02 ;| SEP #$30 ;/ PLX ;>Restore GMK's slot index LDA #$20 ;>Speed of fireball, including diagonals. JSR Aiming ;>Calculate the velocity. LDA $00 ;\Set X speed STA.w !SPRITE_X_SPEED,y ;/ LDA $02 ;\Set Y speed STA.w !SPRITE_Y_SPEED,y ;/ .RETURN RTS ; return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; hammer routine (spawns rocks actually) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SUB_HAMMER_THROW2: LDA #!SPRITE_TO_GEN2 ;\Rock sprite number STA $00 ;/ SEC ;>Custom sprite mode JSR SpawnSprite ;>Spawn sprite BCS RETURN20 ;>If indexes full, return LDA #$07 ;\play the crumbling sound effect STA $1DF9 ;/ LDA $94 ;\Sprite appears at Mario's X position STA $00E4,y ;| LDA $95 ;| STA $14E0,y ;/ LDA !SPRITE_Y_POS,x ;\set y position for new sprite SEC ;|(y position of generator - 1) SBC #$F0 ;| STA $00D8,y ;| LDA $14D4,x ;| SBC #$00 ;| STA $14D4,y ;/ ; PHX ; \ before: X must have index of sprite being generated ; TYX ; | routine clears *all* old sprite values... ; JSL $07F7D2 ; | ...and loads in new values for the 6 main sprite tables ; JSL $0187A7 ; get table values for custom sprite ; LDA #$88 ; STA !EXTRA_BITS,x ; PLX ; / ; LDA !SPRITE_FACING,x ;\set rock's facing to be the same as the GMK's facing ; STA !SPRITE_FACING,y ;/(thats weird that it flips) RETURN20: RTS ; return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;hammer routine (spawns koopas actually) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; X_OFFSET3: db $F0,$10 X_OFFSET4: db $FF,$00 X_THROW_SPEED2: db $18,$E8 RETURN27: RTS SUB_HAMMER_THROW3: LDA !H_OFFSCREEN,x ;\don't spawn if off screen ORA !V_OFFSCREEN,x ;| ORA $15D0,x ;| BNE RETURN27 ;/ ; JSL $02A9DE ;\ get an index to an unused sprite slot, return if all slots full ; BMI RETURN27 ;/ after: Y has index of sprite being generated LDA #!SPRITE_TO_GEN3 ;>sprite number CLC ;>Clear carry for normal sprites JSR SpawnSprite ;>Spawn sprite subroutine BCS RETURN27 ;>If all slots full, return (don't play sfx while fail to spawn) LDA #$20 ;\SFX for tossing a koopa STA $1DF9 ;/ PHY ;\Depending on where the koopas are spawn, LDA !SPRITE_FACING,x ;|it relies on GMK's current facing direction. TAY ;| LDA $E4,x ;| CLC ;| ADC X_OFFSET3,y ;| PLY ;| STA $00E4,y ;/ PHY ;\Same as above but for high byte LDA !SPRITE_FACING,x ;| TAY ;| LDA $14E0,x ;| ADC X_OFFSET4,y ;| PLY ;| STA $14E0,y ;/ LDA !SPRITE_Y_POS,x ;\set y position for new sprite SEC ;|(y position of generator - 1). SBC #$0E ;| STA $00D8,y ;| LDA $14D4,x ;| SBC #$00 ;| STA $14D4,y ;/ PHY ;\Set X speed for each koopa based on GMK facing LDA !SPRITE_FACING,x ;| TAY ;| LDA X_THROW_SPEED2,y ;| PLY ;| STA $00B6,y ;/ LDA #$C0 ;\Set Y speed STA $00AA,y ;/ RETURN28: RTS ;>return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;hammer routine (spawns koopas actually), ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; X_OFFSET5: db $F0,$10 X_OFFSET6: db $FF,$00 X_THROW_SPEED3: db $E8,$18 RETURN23: RTS SUB_HAMMER_THROW4: LDA !H_OFFSCREEN,x ;\don't spawn if off screen ORA !V_OFFSCREEN,x ;| ORA $15D0,x ;| BNE RETURN23 ;/ ; JSL $02A9DE ;\ get an index to an unused sprite slot, return if all slots full ; BMI RETURN23 ;/ after: Y has index of sprite being generated LDA #!SPRITE_TO_GEN4 CLC ;>Clear carry for normal sprite JSR SpawnSprite ;>Spawn sprite subroutine BCS RETURN23 ;>If all slots full, return (don't play sfx while fail to spawn) PHY ;\Depending on where the koopas are spawn, LDA !SPRITE_FACING,x ;|it relies on GMK's current facing direction. TAY ;| LDA $E4,x ;| CLC ;| ADC X_OFFSET5,y ;| PLY ;| STA $00E4,y ;/ PHY ;\Same as above but for high byte LDA !SPRITE_FACING,x ;| TAY ;| LDA $14E0,x ;| ADC X_OFFSET6,y ;| PLY ;| STA $14E0,y ;/ LDA !SPRITE_Y_POS,x ;\set y position for new sprite SEC ;|(y position of generator - 1) SBC #$0E ;| STA $00D8,y ;| LDA $14D4,x ;| SBC #$00 ;| STA $14D4,y ;/ PHY ;\Set X speed for each koopa based on GMK facing LDA !SPRITE_FACING,x ;| TAY ;| LDA X_THROW_SPEED3,y ;| PLY ;| STA $00B6,y ;/ LDA #$C0 ;\Set Y speed STA $00AA,y ;/ RETURN22: RTS ;>return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; MarioE's aiming routine ; input: ;A = projectile speed (8-bit) ;$00 = (shooter x - targetx) ;$02 = (shooter y - targety) ; output: ;$00 = x speed ;$02 = y speed ;WARNING: Distances over #$0100 (256 in decimal) will glitch ; ;MarioE's explanation: Suppose one fired the projectile with X speed dx, and Y speed dy. ;Then its speed would be sqrt(dx2+dy2). Thus, we can adjust its speed by multiplying by ;speed / sqrt(dx2+dy2). This routine calculates the reciprocal 1 / sqrt(dx2+dy2), ;multiplies by speed, then multiplies by either dx or dy. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; recip_sqrt_lookup: dw $0000,$FFFF,$B505,$93CD,$8000,$727D,$6883,$60C2 dw $5A82,$5555,$50F4,$4D30,$49E7,$4700,$446B,$4219 dw $4000,$3E17,$3C57,$3ABB,$393E,$37DD,$3694,$3561 dw $3441,$3333,$3235,$3144,$3061,$2F8A,$2EBD,$2DFB dw $2D41,$2C90,$2BE7,$2B46,$2AAB,$2A16,$2987,$28FE dw $287A,$27FB,$2780,$270A,$2698,$262A,$25BF,$2557 dw $24F3,$2492,$2434,$23D9,$2380,$232A,$22D6,$2285 dw $2236,$21E8,$219D,$2154,$210D,$20C7,$2083,$2041 dw $2000,$1FC1,$1F83,$1F46,$1F0B,$1ED2,$1E99,$1E62 dw $1E2B,$1DF6,$1DC2,$1D8F,$1D5D,$1D2D,$1CFC,$1CCD dw $1C9F,$1C72,$1C45,$1C1A,$1BEF,$1BC4,$1B9B,$1B72 dw $1B4A,$1B23,$1AFC,$1AD6,$1AB1,$1A8C,$1A68,$1A44 dw $1A21,$19FE,$19DC,$19BB,$199A,$1979,$1959,$1939 dw $191A,$18FC,$18DD,$18C0,$18A2,$1885,$1869,$184C dw $1831,$1815,$17FA,$17DF,$17C5,$17AB,$1791,$1778 dw $175F,$1746,$172D,$1715,$16FD,$16E6,$16CE,$16B7 dw $16A1,$168A,$1674,$165E,$1648,$1633,$161D,$1608 dw $15F4,$15DF,$15CB,$15B7,$15A3,$158F,$157C,$1568 dw $1555,$1542,$1530,$151D,$150B,$14F9,$14E7,$14D5 dw $14C4,$14B2,$14A1,$1490,$147F,$146E,$145E,$144D dw $143D,$142D,$141D,$140D,$13FE,$13EE,$13DF,$13CF dw $13C0,$13B1,$13A2,$1394,$1385,$1377,$1368,$135A dw $134C,$133E,$1330,$1322,$1315,$1307,$12FA,$12ED dw $12DF,$12D2,$12C5,$12B8,$12AC,$129F,$1292,$1286 dw $127A,$126D,$1261,$1255,$1249,$123D,$1231,$1226 dw $121A,$120F,$1203,$11F8,$11EC,$11E1,$11D6,$11CB dw $11C0,$11B5,$11AA,$11A0,$1195,$118A,$1180,$1176 dw $116B,$1161,$1157,$114D,$1142,$1138,$112E,$1125 dw $111B,$1111,$1107,$10FE,$10F4,$10EB,$10E1,$10D8 dw $10CF,$10C5,$10BC,$10B3,$10AA,$10A1,$1098,$108F dw $1086,$107E,$1075,$106C,$1064,$105B,$1052,$104A dw $1042,$1039,$1031,$1029,$1020,$1018,$1010,$1008 Aiming: PHX PHY PHP SEP #$30 STA $0F LDX #$00 REP #$20 LDA $00 BPL .pos_dx EOR #$FFFF INC INX INX STA $00 .pos_dx SEP #$20 STA $4202 STA $4203 NOP NOP NOP REP #$20 LDA $4216 STA $04 LDA $02 BPL .pos_dy EOR #$FFFF INC INX STA $02 .pos_dy SEP #$20 STA $4202 STA $4203 STX $0E REP #$30 LDA $04 CLC ADC $4216 LDY #$0000 BCC .loop INY ROR LSR .loop CMP #$0100 BCC + INY LSR LSR BRA .loop + CLC ASL TAX LDA recip_sqrt_lookup,x - DEY BMI + LSR BRA - + SEP #$30 STA $4202 LDA $0F STA $4203 NOP STZ $05 STZ $07 LDA $4217 STA $04 XBA STA $4202 LDA $0F STA $4203 REP #$20 LDA $04 CLC ADC $4216 STA $04 SEP #$20 LDX #$02 - LDA $04 STA $4202 LDA $00,x STA $4203 NOP NOP NOP NOP LDA $4217 STA $06 LDA $05 STA $4202 LDA $00,x STA $4203 REP #$20 LDA $06 CLC ADC $4216 SEP #$20 LSR $0E BCS + EOR #$FF INC + STA $00,x DEX DEX BPL - PLP PLY PLX RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; graphics routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PROPERTIES: db $7B,$3B ;The tables must now have 16 bytes. ;THE LAST 8 ARE ONLY CREATED BECAUSE OF XDISP. ;0-4 BYTE - FRAME 1 RIGHT ;4-8 BYTE - FRAME 2 RIGHT ;8-12 BYTE - FRAME 1 LEFT ;12-16 BYTE - FRAME 2 LEFT TILEMAP: db $04,$06,$08,$0A ; SPIN 1 ;\ RIGHT db $0C,$0E,$2C,$2E ; SPIN 2 ; | RIGHT db $4C,$4E,$6C,$6E ; SPIN 3 ; | RIGHT db $80,$82,$A0,$A2 ; SPIN 4 ;/ RIGHT db $04,$06,$08,$0A ; SPIN 1 ;\ LEFT db $0C,$0E,$2C,$2E ; SPIN 2 ; | LEFT db $4C,$4E,$6C,$6E ; SPIN 3 ; | LEFT db $80,$82,$A0,$A2 ; SPIN 4 ;/ LEFT YDISP: db $F0,$F0,$00,$00 ; SPIN 1 ;\ RIGHT db $F0,$F0,$00,$00 ; SPIN 2 ; | RIGHT db $F0,$F0,$00,$00 ; SPIN 3 ; | RIGHT db $F0,$F0,$00,$00 ; SPIN 4 ;/ RIGHT db $F0,$F0,$00,$00 ; SPIN 1 ;\ LEFT db $F0,$F0,$00,$00 ; SPIN 2 ; | LEFT db $F0,$F0,$00,$00 ; SPIN 3 ; | LEFT db $F0,$F0,$00,$00 ; SPIN 4 ;/ LEFT XDISP: db $F8,$08,$F8,$08 ; SPIN 1 ;\ RIGHT db $F8,$08,$F8,$08 ; SPIN 2 ; | RIGHT db $F8,$08,$F8,$08 ; SPIN 3 ; | RIGHT db $F8,$08,$F8,$08 ; SPIN 4 ;/ RIGHT db $08,$F8,$08,$F8 ; SPIN 1 ;\ LEFT db $08,$F8,$08,$F8 ; SPIN 2 ; | LEFT db $08,$F8,$08,$F8 ; SPIN 3 ; | LEFT db $08,$F8,$08,$F8 ; SPIN 4 ;/ LEFT SUB_GFX: JSR GET_DRAW_INFO LDA !SMASH_STATUS,x STA $03 ; | $03 = index to frame start (0 or 4) LDA $14 ;\ Frame counter .. LSR A ; | LSR A ; | Add in frame animation rate; More LSRs for slower animation. LSR A ; | AND #$03 ; | 01 means we animate between 2 frames (00 and 01). ASL A ; | ASL A ; | ASL x2 (0-4) makes it switch between the first byte and fifth byte, STA $03 ;/ i.e. first animation and second animation. The result is stored into $03. DONE_WALKING: LDA !SPRITE_FACING,x STA $02 ; Store direction to $02 for use with property routine later. BNE NoAdd LDA $03 ;\ CLC ; | If sprite faces left .. ADC #$10 ; | Adding 16 more bytes to the table. STA $03 ;/ So we can invert XDISP to not mess up the sprite's appearance. NoAdd: PHX ;\ Push sprite index .. LDX #$03 ;/ And load X with number of tiles to loop through. Loop: PHX ; Push number of tiles to loop through. TXA ;\ ORA $03 ;/ Transfer it to X and add in the "left displacement" if necessary. TAX ;\ Get it back into X for an index. LDA $00 ;\ CLC ; | Apply X displacement of the sprite. ADC XDISP,x ; | STA $0300,y ;/ LDA $01 ;\ CLC ; | Y displacement is added for the Y position, so one tile is higher than the other. ADC YDISP,x ; | Otherwise, both tiles would have been drawn to the same position! STA $0301,y ; | If X is 00, i.e. first tile, then load the first value from the table and apply that ;/ as the displacement. For the second tile, F0 is added to make it higher than the first. LDA TILEMAP,x STA $0302,y PHX ; Push number of times to go through loop + "left" displacement if necessary. LDX $02 ;\ LDA PROPERTIES,x ; | Set properties based on direction. STA $0303,y ;/ PLX ; Pull number of times to go through loop. INY ;\ INY ; | The OAM is 8x8, but our sprite is 16x16 .. INY ; | So increment it 4 times. INY ;/ PLX ; Pull current tile back. DEX ; After drawing this tile, decrease number of tiles to go through loop. If the second tile ; is drawn, then loop again to draw the first tile. BPL Loop ; Loop until X becomes negative (FF). PLX ; Pull back the sprite index! We pushed it at the beginning of the routine. LDY #$02 ; Y ends with the tile size .. 02 means it's 16x16 LDA #$03 ; A -> number of tiles drawn - 1. ; I drew 2 tiles, so 2-1 = 1. A = 01. JSL $01B7B3 ; Call the routine that draws the sprite. RTS ; Never forget this! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; graphics routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PROPERTIESF: db $79,$39 ;The tables must now have 16 bytes. ;THE LAST 8 ARE ONLY CREATED BECAUSE OF XDISP. ;0-4 BYTE - FRAME 1 RIGHT ;4-8 BYTE - FRAME 2 RIGHT ;8-12 BYTE - FRAME 1 LEFT ;12-16 BYTE - FRAME 2 LEFT TILEMAPF: db $04,$06,$08,$0A ; SPIN 1 ;\ RIGHT db $0C,$0E,$2C,$2E ; SPIN 2 ; | RIGHT db $4C,$4E,$6C,$6E ; SPIN 3 ; | RIGHT db $80,$82,$A0,$A2 ; SPIN 4 ;/ RIGHT db $04,$06,$08,$0A ; SPIN 1 ;\ LEFT db $0C,$0E,$2C,$2E ; SPIN 2 ; | LEFT db $4C,$4E,$6C,$6E ; SPIN 3 ; | LEFT db $80,$82,$A0,$A2 ; SPIN 4 ;/ LEFT YDISPF: db $F0,$F0,$00,$00 ; SPIN 1 ;\ RIGHT db $F0,$F0,$00,$00 ; SPIN 2 ; | RIGHT db $F0,$F0,$00,$00 ; SPIN 3 ; | RIGHT db $F0,$F0,$00,$00 ; SPIN 4 ;/ RIGHT db $F0,$F0,$00,$00 ; SPIN 1 ;\ LEFT db $F0,$F0,$00,$00 ; SPIN 2 ; | LEFT db $F0,$F0,$00,$00 ; SPIN 3 ; | LEFT db $F0,$F0,$00,$00 ; SPIN 4 ;/ LEFT XDISPF: db $F8,$08,$F8,$08 ; SPIN 1 ;\ RIGHT db $F8,$08,$F8,$08 ; SPIN 2 ; | RIGHT db $F8,$08,$F8,$08 ; SPIN 3 ; | RIGHT db $F8,$08,$F8,$08 ; SPIN 4 ;/ RIGHT db $08,$F8,$08,$F8 ; SPIN 1 ;\ LEFT db $08,$F8,$08,$F8 ; SPIN 2 ; | LEFT db $08,$F8,$08,$F8 ; SPIN 3 ; | LEFT db $08,$F8,$08,$F8 ; SPIN 4 ;/ LEFT SUB_GFXF: JSR GET_DRAW_INFO LDA !SMASH_STATUS,x STA $03 ; | $03 = index to frame start (0 or 4) LDA $14 ;\ Frame counter .. LSR A ; | LSR A ; | Add in frame animation rate; More LSRs for slower animation. LSR A ; | AND #$03 ; | 01 means we animate between 2 frames (00 and 01). ASL A ; | ASL A ; | ASL x2 (0-4) makes it switch between the first byte and fifth byte, STA $03 ;/ i.e. first animation and second animation. The result is stored into $03. DONE_WALKINGF: LDA !SPRITE_FACING,x STA $02 ; Store direction to $02 for use with property routine later. BNE NoAddF LDA $03 ;\ CLC ; | If sprite faces left .. ADC #$10 ; | Adding 16 more bytes to the table. STA $03 ;/ So we can invert XDISP to not mess up the sprite's appearance. NoAddF: PHX ;\ Push sprite index .. LDX #$03 ;/ And load X with number of tiles to loop through. LoopF: PHX ; Push number of tiles to loop through. TXA ;\ ORA $03 ;/ Transfer it to X and add in the "left displacement" if necessary. TAX ;\ Get it back into X for an index. LDA $00 ;\ CLC ; | Apply X displacement of the sprite. ADC XDISPF,x ; | STA $0300,y ;/ LDA $01 ;\ CLC ; | Y displacement is added for the Y position, so one tile is higher than the other. ADC YDISPF,x ; | Otherwise, both tiles would have been drawn to the same position! STA $0301,y ; | If X is 00, i.e. first tile, then load the first value from the table and apply that ;/ as the displacement. For the second tile, F0 is added to make it higher than the first. LDA TILEMAPF,x STA $0302,y PHX ; Push number of times to go through loop + "left" displacement if necessary. LDX $02 ;\ LDA PROPERTIESF,x ; | Set properties based on direction. STA $0303,y ;/ PLX ; Pull number of times to go through loop. INY ;\ INY ; | The OAM is 8x8, but our sprite is 16x16 .. INY ; | So increment it 4 times. INY ;/ PLX ; Pull current tile back. DEX ; After drawing this tile, decrease number of tiles to go through loop. If the second tile ; is drawn, then loop again to draw the first tile. BPL LoopF ; Loop until X becomes negative (FF). PLX ; Pull back the sprite index! We pushed it at the beginning of the routine. LDY #$02 ; Y ends with the tile size .. 02 means it's 16x16 LDA #$03 ; A -> number of tiles drawn - 1. ; I drew 2 tiles, so 2-1 = 1. A = 01. JSL $01B7B3 ; Call the routine that draws the sprite. RTS ; Never forget this! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; graphics routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PROPERTIESR: db $75,$35 ;The tables must now have 16 bytes. ;THE LAST 8 ARE ONLY CREATED BECAUSE OF XDISP. ;0-4 BYTE - FRAME 1 RIGHT ;4-8 BYTE - FRAME 2 RIGHT ;8-12 BYTE - FRAME 1 LEFT ;12-16 BYTE - FRAME 2 LEFT TILEMAPR: db $04,$06,$08,$0A ; SPIN 1 ;\ RIGHT db $0C,$0E,$2C,$2E ; SPIN 2 ; | RIGHT db $4C,$4E,$6C,$6E ; SPIN 3 ; | RIGHT db $80,$82,$A0,$A2 ; SPIN 4 ;/ RIGHT db $04,$06,$08,$0A ; SPIN 1 ;\ LEFT db $0C,$0E,$2C,$2E ; SPIN 2 ; | LEFT db $4C,$4E,$6C,$6E ; SPIN 3 ; | LEFT db $80,$82,$A0,$A2 ; SPIN 4 ;/ LEFT YDISPR: db $F0,$F0,$00,$00 ; SPIN 1 ;\ RIGHT db $F0,$F0,$00,$00 ; SPIN 2 ; | RIGHT db $F0,$F0,$00,$00 ; SPIN 3 ; | RIGHT db $F0,$F0,$00,$00 ; SPIN 4 ;/ RIGHT db $F0,$F0,$00,$00 ; SPIN 1 ;\ LEFT db $F0,$F0,$00,$00 ; SPIN 2 ; | LEFT db $F0,$F0,$00,$00 ; SPIN 3 ; | LEFT db $F0,$F0,$00,$00 ; SPIN 4 ;/ LEFT XDISPR: db $F8,$08,$F8,$08 ; SPIN 1 ;\ RIGHT db $F8,$08,$F8,$08 ; SPIN 2 ; | RIGHT db $F8,$08,$F8,$08 ; SPIN 3 ; | RIGHT db $F8,$08,$F8,$08 ; SPIN 4 ;/ RIGHT db $08,$F8,$08,$F8 ; SPIN 1 ;\ LEFT db $08,$F8,$08,$F8 ; SPIN 2 ; | LEFT db $08,$F8,$08,$F8 ; SPIN 3 ; | LEFT db $08,$F8,$08,$F8 ; SPIN 4 ;/ LEFT SUB_GFXR: JSR GET_DRAW_INFO LDA !SMASH_STATUS,x STA $03 ; | $03 = index to frame start (0 or 4) LDA $14 ;\ Frame counter .. LSR A ; | LSR A ; | Add in frame animation rate; More LSRs for slower animation. LSR A ; | AND #$03 ; | 01 means we animate between 2 frames (00 and 01). ASL A ; | ASL A ; | ASL x2 (0-4) makes it switch between the first byte and fifth byte, STA $03 ;/ i.e. first animation and second animation. The result is stored into $03. DONE_WALKINGR: LDA !SPRITE_FACING,x STA $02 ; Store direction to $02 for use with property routine later. BNE NoAddR LDA $03 ;\ CLC ; | If sprite faces left .. ADC #$10 ; | Adding 16 more bytes to the table. STA $03 ;/ So we can invert XDISP to not mess up the sprite's appearance. NoAddR: PHX ;\ Push sprite index .. LDX #$03 ;/ And load X with number of tiles to loop through. LoopR: PHX ; Push number of tiles to loop through. TXA ;\ ORA $03 ;/ Transfer it to X and add in the "left displacement" if necessary. TAX ;\ Get it back into X for an index. LDA $00 ;\ CLC ; | Apply X displacement of the sprite. ADC XDISPR,x ; | STA $0300,y ;/ LDA $01 ;\ CLC ; | Y displacement is added for the Y position, so one tile is higher than the other. ADC YDISPR,x ; | Otherwise, both tiles would have been drawn to the same position! STA $0301,y ; | If X is 00, i.e. first tile, then load the first value from the table and apply that ;/ as the displacement. For the second tile, F0 is added to make it higher than the first. LDA TILEMAPR,x STA $0302,y PHX ; Push number of times to go through loop + "left" displacement if necessary. LDX $02 ;\ LDA PROPERTIESR,x ; | Set properties based on direction. STA $0303,y ;/ PLX ; Pull number of times to go through loop. INY ;\ INY ; | The OAM is 8x8, but our sprite is 16x16 .. INY ; | So increment it 4 times. INY ;/ PLX ; Pull current tile back. DEX ; After drawing this tile, decrease number of tiles to go through loop. If the second tile ; is drawn, then loop again to draw the first tile. BPL LoopR ; Loop until X becomes negative (FF). PLX ; Pull back the sprite index! We pushed it at the beginning of the routine. LDY #$02 ; Y ends with the tile size .. 02 means it's 16x16 LDA #$03 ; A -> number of tiles drawn - 1. ; I drew 2 tiles, so 2-1 = 1. A = 01. JSL $01B7B3 ; Call the routine that draws the sprite. RTS ; Never forget this! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; graphics routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PROPERTIESJ: db $77,$37 ;The tables must now have 16 bytes. ;THE LAST 8 ARE ONLY CREATED BECAUSE OF XDISP. ;0-4 BYTE - FRAME 1 RIGHT ;4-8 BYTE - FRAME 2 RIGHT ;8-12 BYTE - FRAME 1 LEFT ;12-16 BYTE - FRAME 2 LEFT TILEMAPJ: db $04,$06,$08,$0A ; SPIN 1 ;\ RIGHT db $0C,$0E,$2C,$2E ; SPIN 2 ; | RIGHT db $4C,$4E,$6C,$6E ; SPIN 3 ; | RIGHT db $80,$82,$A0,$A2 ; SPIN 4 ;/ RIGHT db $04,$06,$08,$0A ; SPIN 1 ;\ LEFT db $0C,$0E,$2C,$2E ; SPIN 2 ; | LEFT db $4C,$4E,$6C,$6E ; SPIN 3 ; | LEFT db $80,$82,$A0,$A2 ; SPIN 4 ;/ LEFT YDISPJ: db $F0,$F0,$00,$00 ; SPIN 1 ;\ RIGHT db $F0,$F0,$00,$00 ; SPIN 2 ; | RIGHT db $F0,$F0,$00,$00 ; SPIN 3 ; | RIGHT db $F0,$F0,$00,$00 ; SPIN 4 ;/ RIGHT db $F0,$F0,$00,$00 ; SPIN 1 ;\ LEFT db $F0,$F0,$00,$00 ; SPIN 2 ; | LEFT db $F0,$F0,$00,$00 ; SPIN 3 ; | LEFT db $F0,$F0,$00,$00 ; SPIN 4 ;/ LEFT XDISPJ: db $F8,$08,$F8,$08 ; SPIN 1 ;\ RIGHT db $F8,$08,$F8,$08 ; SPIN 2 ; | RIGHT db $F8,$08,$F8,$08 ; SPIN 3 ; | RIGHT db $F8,$08,$F8,$08 ; SPIN 4 ;/ RIGHT db $08,$F8,$08,$F8 ; SPIN 1 ;\ LEFT db $08,$F8,$08,$F8 ; SPIN 2 ; | LEFT db $08,$F8,$08,$F8 ; SPIN 3 ; | LEFT db $08,$F8,$08,$F8 ; SPIN 4 ;/ LEFT SUB_GFXJ: JSR GET_DRAW_INFO LDA !SMASH_STATUS,x STA $03 ; | $03 = index to frame start (0 or 4) LDA $14 ;\ Frame counter .. LSR A ; | LSR A ; | Add in frame animation rate; More LSRs for slower animation. LSR A ; | AND #$03 ; | 01 means we animate between 2 frames (00 and 01). ASL A ; | ASL A ; | ASL x2 (0-4) makes it switch between the first byte and fifth byte, STA $03 ;/ i.e. first animation and second animation. The result is stored into $03. DONE_WALKINGJ: LDA !SPRITE_FACING,x STA $02 ; Store direction to $02 for use with property routine later. BNE NoAddJ LDA $03 ;\ CLC ; | If sprite faces left .. ADC #$10 ; | Adding 16 more bytes to the table. STA $03 ;/ So we can invert XDISP to not mess up the sprite's appearance. NoAddJ: PHX ;\ Push sprite index .. LDX #$03 ;/ And load X with number of tiles to loop through. LoopJ: PHX ; Push number of tiles to loop through. TXA ;\ ORA $03 ;/ Transfer it to X and add in the "left displacement" if necessary. TAX ;\ Get it back into X for an index. LDA $00 ;\ CLC ; | Apply X displacement of the sprite. ADC XDISPJ,x ; | STA $0300,y ;/ LDA $01 ;\ CLC ; | Y displacement is added for the Y position, so one tile is higher than the other. ADC YDISPJ,x ; | Otherwise, both tiles would have been drawn to the same position! STA $0301,y ; | If X is 00, i.e. first tile, then load the first value from the table and apply that ;/ as the displacement. For the second tile, F0 is added to make it higher than the first. LDA TILEMAPJ,x STA $0302,y PHX ; Push number of times to go through loop + "left" displacement if necessary. LDX $02 ;\ LDA PROPERTIESJ,x ; | Set properties based on direction. STA $0303,y ;/ PLX ; Pull number of times to go through loop. INY ;\ INY ; | The OAM is 8x8, but our sprite is 16x16 .. INY ; | So increment it 4 times. INY ;/ PLX ; Pull current tile back. DEX ; After drawing this tile, decrease number of tiles to go through loop. If the second tile ; is drawn, then loop again to draw the first tile. BPL LoopJ ; Loop until X becomes negative (FF). PLX ; Pull back the sprite index! We pushed it at the beginning of the routine. LDY #$02 ; Y ends with the tile size .. 02 means it's 16x16 LDA #$03 ; A -> number of tiles drawn - 1. ; I drew 2 tiles, so 2-1 = 1. A = 01. JSL $01B7B3 ; Call the routine that draws the sprite. RTS ; Never forget this! ;=================================== ;Graphics Code ;=================================== PROPERTIES3: db $7B,$3B ;The tables must now have 16 bytes. ;THE LAST 8 ARE ONLY CREATED BECAUSE OF XDISP. ;0-4 BYTE - FRAME 1 RIGHT ;4-8 BYTE - FRAME 2 RIGHT ;8-12 BYTE - FRAME 1 LEFT ;12-16 BYTE - FRAME 2 LEFT TILEMAP3: db $00,$02,$20,$22,$40,$42,$60,$62 ; FRAME 1 ;\ RIGHT db $00,$02,$24,$26,$44,$46,$64,$66 ; FRAME 1 ;\ RIGHT db $00,$02,$20,$22,$40,$42,$60,$62 ; FRAME 1 ;\ RIGHT db $00,$02,$24,$26,$44,$46,$64,$66 ; FRAME 1 ;\ RIGHT YDISP3: db $D0,$D0,$E0,$E0,$F0,$F0,$00,$00 ; FRAME 1 ;\ RIGHT db $D0,$D0,$E0,$E0,$F0,$F0,$00,$00 ; FRAME 1 ;\ RIGHT db $D0,$D0,$E0,$E0,$F0,$F0,$00,$00 ; FRAME 1 ;\ RIGHT db $D0,$D0,$E0,$E0,$F0,$F0,$00,$00 ; FRAME 1 ;\ RIGH XDISP3: db $F0,$00,$F0,$00,$F0,$00,$F0,$00 db $F0,$00,$F0,$00,$F0,$00,$F0,$00 db $10,$00,$10,$00,$10,$00,$10,$00 ; FRAME 1 ;\ LEFT db $10,$00,$10,$00,$10,$00,$10,$00 ; FRAME 2 ;/ LEFT SUB_GFX3: JSR GET_DRAW_INFO LDA $14 ;\ Frame counter .. LSR A ; | LSR A ; | Add in frame animation rate; More LSRs for slower animation. LSR A AND #$01 ; | 01 means we animate between 2 frames (00 and 01). ASL A ; | ASL A ; | ASL x2 (0-4) makes it switch between the first byte and fifth byte ASL A STA $03 ;/ i.e. first animation and second animation. The result is stored into $03. LDA !SPRITE_FACING,x STA $02 ; Store direction to $02 for use with property routine later. BNE NoAdd3 LDA $03 ;\ CLC ; | If sprite faces left .. ADC #$10 ; | Adding 8 more bytes to the table. STA $03 ;/ So we can invert XDISP to not mess up the sprite's appearance. NoAdd3: PHX ;\ Push sprite index .. LDX #$07 ;/ And load X with number of tiles to loop through. Loop3: PHX ; Push number of tiles to loop through. TXA ;\ ORA $03 ;/ Transfer it to X and add in the "left displacement" if necessary. TAX ;\ Get it back into X for an index. LDA $00 ;\ CLC ; | Apply X displacement of the sprite. ADC XDISP3,x ; | STA $0300,y ;/ LDA $01 ;\ CLC ; | Y displacement is added for the Y position, so one tile is higher than the other. ADC YDISP3,x ; | Otherwise, both tiles would have been drawn to the same position! STA $0301,y ; | If X is 00, i.e. first tile, then load the first value from the table and apply that ;/ as the displacement. For the second tile, F0 is added to make it higher than the first. LDA TILEMAP3,x STA $0302,y PHX ; Push number of times to go through loop + "left" displacement if necessary. LDX $02 ;\ LDA PROPERTIES3,x ; | Set properties based on direction. STA $0303,y ;/ PLX ; Pull number of times to go through loop. INY ;\ INY ; | The OAM is 8x8, but our sprite is 16x16 .. INY ; | So increment it 4 times. INY ;/ PLX ; Pull current tile back. DEX ; After drawing this tile, decrease number of tiles to go through loop. If the second tile ; is drawn, then loop again to draw the first tile. BPL Loop3 ; Loop until X becomes negative (FF). PLX ; Pull back the sprite index! We pushed it at the beginning of the routine. LDY #$02 ; Y ends with the tile size .. 02 means it's 16x16 LDA #$07 ; A -> number of tiles drawn - 1. ; I drew 2 tiles, so 2-1 = 1. A = 01. JSL $01B7B3 ; Call the routine that draws the sprite. RTS ; Never forget this! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SUB_CHANGE_DIR ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;org $019098 SUB_CHANGE_DIR: ;LDA $15AC,x ;BNE LABEL41 ;LDA #$08 ;STA $15AC,x LDA !SPRITE_X_SPEED,x EOR #$FF INC A STA !SPRITE_X_SPEED,x LDA !SPRITE_FACING,x EOR #$01 STA !SPRITE_FACING,x LABEL41: RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; speed related ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SUB_9A04: LDA !SPR_OBJ_STATUS,x BMI THWOMP_1 LDA #$00 LDY $15B8,x BEQ THWOMP_2 THWOMP_1: LDA #$18 THWOMP_2: STA !SPRITE_Y_SPEED,x RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; display smoke effect for bullet bill shooter ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SUB_SMOKE: LDY #$03 ; \ find a free slot to display effect FINDFREE: LDA $17C0,y ; | BEQ FOUNDONE ; | DEY ; | BPL FINDFREE ; | RTS ; / return if no slots open X_OFFSET_ONE: db $F4,$0C FOUNDONE: LDA #$01 ; \ set effect graphic to smoke graphic STA $17C0,y ; / LDA !SPRITE_Y_POS,x ; \ smoke y position = generator y position CLC ADC #$12 STA $17C4,y ; / LDA #$1B ; \ set time to show smoke STA $17CC,y ; / LDA $E4,x ; \ load generator x position and store it for later CLC ADC #$F0 STA $17C8,y ; / LDX $15E9 RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; display smoke effect for bullet bill shooter 2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SUB_SMOKE2: LDY #$03 ; \ find a free slot to display effect FINDFREE2: LDA $17C0,y ; | BEQ FOUNDONE2 ; | DEY ; | BPL FINDFREE2 ; | RTS ; / return if no slots open X_OFFSET2_ONE: db $F4,$0C FOUNDONE2: LDA #$01 ; \ set effect graphic to smoke graphic STA $17C0,y ; / LDA !SPRITE_Y_POS,x ; \ smoke y position = generator y position CLC ADC #$14 STA $17C4,y ; / LDA #$1B ; \ set time to show smoke STA $17CC,y ; / LDA $E4,x ; \ load generator x position and store it for later CLC ADC #$0F STA $17C8,y ; / LDX $15E9 RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; routines below can be shared by all sprites. they are ripped from original ; SMW and poorly documented ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; points routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; STAR_SOUNDS: db $00,$13,$14,$15,$16,$17,$18,$19 SUB_STOMP_PTS: PHY ; LDA $1697 ; \ CLC ; | ADC $1626,x ; / some enemies give higher pts/1ups quicker?? INC $1697 ; increase consecutive enemies stomped TAY ; INY ; CPY #$08 ; \ if consecutive enemies stomped >= 8 ... BCS NO_SOUND ; / ... don't play sound LDA STAR_SOUNDS,y ; \ play sound effect STA $1DF9 ; / NO_SOUND: TYA ; \ CMP #$08 ; | if consecutive enemies stomped >= 8, reset to 8 BCC NO_RESET ; | LDA #$08 ; / NO_RESET: JSL $02ACE5 ; give mario points PLY ; RTS ; return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; $B760 - graphics routine helper - shared ; sets off screen flags and sets index to OAM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;org $03B75C TABLE1: db $0C,$1C TABLE2: db $01,$02 GET_DRAW_INFO: STZ !V_OFFSCREEN,x ; reset sprite offscreen flag, vertical STZ !H_OFFSCREEN,x ; reset sprite offscreen flag, horizontal LDA $E4,x ; \ CMP $1A ; | set horizontal offscreen if necessary LDA $14E0,x ; | SBC $1B ; | BEQ ON_SCREEN_X ; | INC !H_OFFSCREEN,x ; / ON_SCREEN_X: LDA $14E0,x ; \ XBA ; | LDA $E4,x ; | REP #$20 ; | SEC ; | SBC $1A ; | mark sprite invalid if far enough off screen CLC ; | ADC.w #$0040 ; | CMP.w #$0180 ; | SEP #$20 ; | ROL A ; | AND #$01 ; | STA $15C4,x ; / BNE INVALID ; LDY #$00 ; \ set up loop: LDA $1662,x ; | AND #$20 ; | if not smushed (1662 & 0x20), go through loop twice BEQ ON_SCREEN_LOOP ; | else, go through loop once INY ; / ON_SCREEN_LOOP: LDA !SPRITE_Y_POS,x ; \ CLC ; | set vertical offscreen if necessary ADC TABLE1,y ; | PHP ; | CMP $1C ; | (vert screen boundry) ROL $00 ; | PLP ; | LDA $14D4,x ; | ADC #$00 ; | LSR $00 ; | SBC $1D ; | BEQ ON_SCREEN_Y ; | LDA !V_OFFSCREEN,x ; | (vert offscreen) ORA TABLE2,y ; | STA !V_OFFSCREEN,x ; | ON_SCREEN_Y: DEY ; | BPL ON_SCREEN_LOOP ; / LDY $15EA,x ; get offset to sprite OAM LDA $E4,x ; \ SEC ; | SBC $1A ; | $00 = sprite x position relative to screen boarder STA $00 ; / LDA !SPRITE_Y_POS,x ; \ SEC ; | SBC $1C ; | $01 = sprite y position relative to screen boarder STA $01 ; / RTS ; return INVALID: PLA ; \ return from *main gfx routine* subroutine... PLA ; | ...(not just this subroutine) RTS ; / ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; $B829 - vertical mario/sprite position check - shared ; Y = 1 if mario below sprite?? ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;org $03B829 SUB_VERT_POS: LDY #$00 ;A:25A1 X:0007 Y:0001 D:0000 DB:03 S:01EA P:envMXdizCHC:0130 VC:085 00 FL:924 LDA $96 ;A:25A1 X:0007 Y:0000 D:0000 DB:03 S:01EA P:envMXdiZCHC:0146 VC:085 00 FL:924 SEC ;A:2546 X:0007 Y:0000 D:0000 DB:03 S:01EA P:envMXdizCHC:0170 VC:085 00 FL:924 SBC !SPRITE_Y_POS,x ;A:2546 X:0007 Y:0000 D:0000 DB:03 S:01EA P:envMXdizCHC:0184 VC:085 00 FL:924 STA $0F ;A:25D6 X:0007 Y:0000 D:0000 DB:03 S:01EA P:eNvMXdizcHC:0214 VC:085 00 FL:924 LDA $97 ;A:25D6 X:0007 Y:0000 D:0000 DB:03 S:01EA P:eNvMXdizcHC:0238 VC:085 00 FL:924 SBC $14D4,x ;A:2501 X:0007 Y:0000 D:0000 DB:03 S:01EA P:envMXdizcHC:0262 VC:085 00 FL:924 BPL LABEL11 ;A:25FF X:0007 Y:0000 D:0000 DB:03 S:01EA P:eNvMXdizcHC:0294 VC:085 00 FL:924 INY ;A:25FF X:0007 Y:0000 D:0000 DB:03 S:01EA P:eNvMXdizcHC:0310 VC:085 00 FL:924 LABEL11: RTS ;A:25FF X:0007 Y:0001 D:0000 DB:03 S:01EA P:envMXdizcHC:0324 VC:085 00 FL:924 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; $B817 - horizontal mario/sprite check - shared ; Y = 1 if mario left of sprite?? ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;org $03B817 SUB_HORZ_POS: LDY #$00 ;A:25D0 X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizCHC:1020 VC:097 00 FL:31642 LDA $94 ;A:25D0 X:0006 Y:0000 D:0000 DB:03 S:01ED P:envMXdiZCHC:1036 VC:097 00 FL:31642 SEC ;A:25F0 X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizCHC:1060 VC:097 00 FL:31642 SBC $E4,x ;A:25F0 X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizCHC:1074 VC:097 00 FL:31642 STA $0F ;A:25F4 X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizcHC:1104 VC:097 00 FL:31642 LDA $95 ;A:25F4 X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizcHC:1128 VC:097 00 FL:31642 SBC $14E0,x ;A:2500 X:0006 Y:0000 D:0000 DB:03 S:01ED P:envMXdiZcHC:1152 VC:097 00 FL:31642 BPL LABEL16 ;A:25FF X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizcHC:1184 VC:097 00 FL:31642 INY ;A:25FF X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizcHC:1200 VC:097 00 FL:31642 LABEL16: RTS ;A:25FF X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:1214 VC:097 00 FL:31642 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; $B85D - off screen processing code - shared ; sprites enter at different points ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;org $03B83B TABLE3: db $40,$B0 TABLE6: db $01,$FF TABLE4: db $30,$C0,$A0,$80,$A0,$40,$60,$B0 TABLE5: db $01,$FF,$01,$FF,$01,$00,$01,$FF SUB_OFF_SCREEN_X0: LDA #$06 ; \ entry point of routine determines value of $03 BRA STORE_03 ; | SUB_OFF_SCREEN_X1: LDA #$04 ; | BRA STORE_03 ; | SUB_OFF_SCREEN_X2: LDA #$02 ; | STORE_03: STA $03 ; | BRA START_SUB ; | SUB_OFF_SCREEN_X3: STZ $03 ; / START_SUB: JSR SUB_IS_OFF_SCREEN ; \ if sprite is not off screen, return BEQ RETURN_2 ; / LDA $5B ; \ goto VERTICAL_LEVEL if vertical level AND #$01 ; | BNE VERTICAL_LEVEL ; / LDA !SPRITE_Y_POS,x ; \ CLC ; | ADC #$50 ; | if the sprite has gone off the bottom of the level... LDA $14D4,x ; | (if adding 0x50 to the sprite y position would make the high byte >= 2) ADC #$00 ; | CMP #$02 ; | BPL ERASE_SPRITE ; / ...erase the sprite LDA $167A,x ; \ if "process offscreen" flag is set, return AND #$04 ; | BNE RETURN_2 ; / LDA $13 ; \ AND #$01 ; | ORA $03 ; | STA $01 ; | TAY ; / LDA $1A ;x boundry ;A:0101 X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:0256 VC:090 00 FL:16953 CLC ;A:0100 X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdiZcHC:0280 VC:090 00 FL:16953 ADC TABLE4,y ;A:0100 X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdiZcHC:0294 VC:090 00 FL:16953 ROL $00 ;A:01C0 X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizcHC:0326 VC:090 00 FL:16953 CMP $E4,x ;x pos ;A:01C0 X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizcHC:0364 VC:090 00 FL:16953 PHP ;A:01C0 X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizCHC:0394 VC:090 00 FL:16953 LDA $1B ;x boundry hi ;A:01C0 X:0006 Y:0001 D:0000 DB:03 S:01EC P:eNvMXdizCHC:0416 VC:090 00 FL:16953 LSR $00 ;A:0100 X:0006 Y:0001 D:0000 DB:03 S:01EC P:envMXdiZCHC:0440 VC:090 00 FL:16953 ADC TABLE5,y ;A:0100 X:0006 Y:0001 D:0000 DB:03 S:01EC P:envMXdizcHC:0478 VC:090 00 FL:16953 PLP ;A:01FF X:0006 Y:0001 D:0000 DB:03 S:01EC P:eNvMXdizcHC:0510 VC:090 00 FL:16953 SBC $14E0,x ;x pos high ;A:01FF X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizCHC:0538 VC:090 00 FL:16953 STA $00 ;A:01FE X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizCHC:0570 VC:090 00 FL:16953 LSR $01 ;A:01FE X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizCHC:0594 VC:090 00 FL:16953 BCC LABEL20 ;A:01FE X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdiZCHC:0632 VC:090 00 FL:16953 EOR #$80 ;A:01FE X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdiZCHC:0648 VC:090 00 FL:16953 STA $00 ;A:017E X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizCHC:0664 VC:090 00 FL:16953 LABEL20: LDA $00 ;A:017E X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizCHC:0688 VC:090 00 FL:16953 BPL RETURN_2 ;A:017E X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizCHC:0712 VC:090 00 FL:16953 ERASE_SPRITE: LDA $14C8,x ; \ if sprite status < 8, permanently erase sprite CMP #$08 ; | BCC KILL_SPRITE ; / LDY $161A,x ;A:FF08 X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdiZCHC:0140 VC:071 00 FL:21152 CPY #$FF ;A:FF08 X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizCHC:0172 VC:071 00 FL:21152 BEQ KILL_SPRITE ;A:FF08 X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:0188 VC:071 00 FL:21152 LDA #$00 ; \ mark sprite to come back A:FF08 X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:0204 VC:071 00 FL:21152 STA $1938,y ; / A:FF00 X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdiZcHC:0220 VC:071 00 FL:21152 KILL_SPRITE: STZ $14C8,x ; erase sprite RETURN_2: RTS ; return VERTICAL_LEVEL: LDA $167A,x ; \ if "process offscreen" flag is set, return AND #$04 ; | BNE RETURN_2 ; / LDA $13 ; \ only handle every other frame?? LSR A ; | BCS RETURN_2 ; / AND #$01 ;A:0227 X:0006 Y:00EC D:0000 DB:03 S:01ED P:envMXdizcHC:0228 VC:112 00 FL:1142 STA $01 ;A:0201 X:0006 Y:00EC D:0000 DB:03 S:01ED P:envMXdizcHC:0244 VC:112 00 FL:1142 TAY ;A:0201 X:0006 Y:00EC D:0000 DB:03 S:01ED P:envMXdizcHC:0268 VC:112 00 FL:1142 LDA $1C ;A:0201 X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:0282 VC:112 00 FL:1142 CLC ;A:02BD X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizcHC:0306 VC:112 00 FL:1142 ADC TABLE3,y ;A:02BD X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizcHC:0320 VC:112 00 FL:1142 ROL $00 ;A:026D X:0006 Y:0001 D:0000 DB:03 S:01ED P:enVMXdizCHC:0352 VC:112 00 FL:1142 CMP !SPRITE_Y_POS,x ;A:026D X:0006 Y:0001 D:0000 DB:03 S:01ED P:enVMXdizCHC:0390 VC:112 00 FL:1142 PHP ;A:026D X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNVMXdizcHC:0420 VC:112 00 FL:1142 LDA.w $001D ;A:026D X:0006 Y:0001 D:0000 DB:03 S:01EC P:eNVMXdizcHC:0442 VC:112 00 FL:1142 LSR $00 ;A:0200 X:0006 Y:0001 D:0000 DB:03 S:01EC P:enVMXdiZcHC:0474 VC:112 00 FL:1142 ADC TABLE6,y ;A:0200 X:0006 Y:0001 D:0000 DB:03 S:01EC P:enVMXdizCHC:0512 VC:112 00 FL:1142 PLP ;A:0200 X:0006 Y:0001 D:0000 DB:03 S:01EC P:envMXdiZCHC:0544 VC:112 00 FL:1142 SBC $14D4,x ;A:0200 X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNVMXdizcHC:0572 VC:112 00 FL:1142 STA $00 ;A:02FF X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizcHC:0604 VC:112 00 FL:1142 LDY $01 ;A:02FF X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizcHC:0628 VC:112 00 FL:1142 BEQ LABEL22 ;A:02FF X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:0652 VC:112 00 FL:1142 EOR #$80 ;A:02FF X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:0668 VC:112 00 FL:1142 STA $00 ;A:027F X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:0684 VC:112 00 FL:1142 LABEL22: LDA $00 ;A:027F X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:0708 VC:112 00 FL:1142 BPL RETURN_2 ;A:027F X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:0732 VC:112 00 FL:1142 BMI ERASE_SPRITE ;A:0280 X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizCHC:0170 VC:064 00 FL:1195 SUB_IS_OFF_SCREEN: LDA !H_OFFSCREEN,x ; \ if sprite is on screen, accumulator = 0 ORA !V_OFFSCREEN,x ; | RTS ; / return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Spawn sprite routine, this ignores the effect of sprite memory setting, ALWAYS uses ;the maximum number of slots reguardless. ;Input: ;A = sprite number ;Carry = extra bit flag, custom sprite if set, otherwise smw's sprite ;Output: ;Y = current slot index of new sprite spawned ;Carry = 0 if there is open index, 1 if 12/12 or 22/22 indexes filled, ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SpawnSprite: PHX XBA ;>Transfer sprite # to high byte LDX.b #!NumbOfSprSlots-1 ;>Start # of loops -1 .Loop LDA $14C8,x ;\If current slot open, setup sprite BEQ .SetupSpr ;/Otherwise, if open, use that. .NextSlot DEX ;>Go to another slot index BPL .Loop ;>If index is 0 or positive (valid index), loop. SEC ;>Indicate that there is no open slot indexes. BRA .SpwnSprRet .SetupSpr XBA ;>Obtain sprite number in A STA $9E,x ;>Set sprite number JSL $07F7D2 ;>Reset sprite tables. BCC + ;>if custom carry clear, skip spawning custom LDA $9E,x ;\Set sprite number to be the same as custom sprite (IDK why) STA !SPRITE_CUSTOMSPRNUMB,x ;/ JSL $0187A7 ;>It jumps to JSL $9081D7, this subroutine sets the tweaker, some other important things. LDA #$08 ;\Set extra bits STA !EXTRA_BITS,x ;/ + LDA #$01 ;\Initalize sprite STA $14C8,x ;/ CLC ;>Indicate that spawn was sucessful. .SpwnSprRet TXY ;>Transfer new sprite's index to Y PLX ;>Pull x to restore it to be the shooter's current slot RTS