;----------------------------------------- ;ROGUE NINJA ;Assemble IT - GAME 7 ;Written by Richard Bayliss ;Graphics by Alf Yngve and Richard Bayliss ;Music by Richard Bayliss ;----------------------------------------- ;Generate C64 program RogueNinja.prg !to "rogueninja.prg",cbm ;Some variables TitleMusicInit = $1000 ;Init title music TitleMusicPlay = $1003 ;Play title music GameMusicInit = $9000 ;Init game music GameMusicPlay = $9003 ;Play game music chartempbyte = $02 ;A zeropage for storing water anim bytes XPos = $03 ;A zero page for scroll control (Title Screen) XScrollSpeed = 1 ;Speed of title screen scroll text ;Collision values for player/bullet/enemy CollisionXLeft = $06 ;Left collision boundary area CollisionXRight = $0c ;Right collision boundary area CollisionYUp = $0c ;Top collision boundary area CollisionYDown = $18 ;Bottom collision boundary area ;Possible floor positions for each enemy/chest object to appear and ;spawn to Floor_Top = $66 ;Top floor Floor_Mid = $8e ;Middle floor Floor_Btm = $b6 ;Bottom floor ;Possible chest positions for each chest (of stars) to ;appear on screen Chest_Lft = $10 ;Very left of screen Chest_Mid = $58 ;Middle of screen Chest_Rgt = $a0 ;Very right of screen ;Player / Bullet speed properties PlayerRunSpeed = 1 ;The speed in which is used for the player moving PlayerJumpSpeed = 3 ;The speed for the player jumping (Push up) PlayerFallSpeed = 3 ;The speed for the player falling (Push down) BulletSpeed = 4 ;The speed for the player bullet (Any X direction) ;Left/Right values for enemy spawning (Conditional) EnemyLeft = 0 ;Enemy move right = FALSE EnemyRight = 1 ;Enemy move right = TRUE ;Score /screen pointers ScoreDigitFont = 86 ScoreStore = $074f ;Screen RAM position for storing score chars HiScoreStore = $0769 ;Screen RAM position for storing Hi Score chars LivesStore = $079f ;Screen RAM position for storing Lives chars StarsStore = $07b9 ;Screen RAM position for storing Stars chars ;No BASIC run this time, as charset will be placed at $0800. ;Instead set a packer / cross platform cruncher to JMP $4000 ;in order to run the program (Or type SYS16384 from BASIC). ;Import the game character set binary (Exported from Charpad V2.0) *=$0800 !bin "bin/gamecharset.bin" ;Import title music (,,2 prefix for prg, no prefix for raw bin) *=$1000 !bin "bin/titlemusic.prg",,2 ;Import game sprites binary (Exported from Sprite Pad) *=$2000 !bin "bin/gamesprites.bin" ;Import game screen data/matrix binary (Exported from Charpad V2.0) *=$2800 GameMatrix !bin "bin/gamemap.bin" ;Import game screen colour attributes binary (Exported from Charpad V2.0) *=$2c00 GameAttribs !bin "bin/gameattribs.bin" ;Import title screen charset (Exported from Charpad V2.0) *=$3000 !bin "bin/titlecharset.bin" ;Import title screen logo matrix (Exported from Charpad V2.0) *=$3800 LogoMatrix !bin "bin/titlemap.bin" ;Import title charset colour attributes (Exported from Charpad V2.0) *=$3c00 LogoColours !bin "bin/titleattribs.bin" ;==================== ;Main game code here: ;==================== *=$4000 sei lda #$36 sta $01 lda #$12 ;Point to charset at $0800 sta $d018 lda #$18 ;Multicolour mode on sta $d016 lda #$03 sta $dd00;Default screen mode lda #$09 ;Background Colour ldx #$00 ;Multicolour 1 ldy #$01 ;Multicolour 2 sta $d021 stx $d022 stx $d020 sty $d023 jmp TitleScreen ;Main code for game code GameStart ;Kill all interrupts sei ldx #$31 ldy #$ea stx $0314 sty $0315 lda #$0b sta $d011 lda #$81 sta $dc0d sta $dd0d lda #$00 sta $d019 sta $d01a lda #$12 sta $d018 ;Ensure game charset is set! lda #$09 sta $d021 ;Background colour = Brown lda #$00 sta $d022 ;Char Multicolour #1 = Black lda #$01 sta $d023 ;Char multicolour #2 = White ;Setup default sprites lda #%11111111 sta $d015 ;All sprites on sta $d01c ;Multicolour mode on ldx #$00 ;Set sprite multicolours ldy #$01 stx $d025 sty $d026 ;Now set up default frames of all sprite objects ldx #$00 SetDFLT lda ObjectStartTable,x sta ObjectSpriteFrame,x lda ObjectStartColour,x sta $d027,x inx cpx #$08 bne SetDFLT ;Do the same with the sprite positions ldx #$00 PositionSprites lda StartPositionTable,x sta Obj_Position,x inx cpx #$10 bne PositionSprites ;Draw the main game screen ldx #$00 DrawGameScreen lda GameMatrix,x sta $0400,x lda GameMatrix+$100,x sta $0500,x lda GameMatrix+$200,x sta $0600,x lda GameMatrix+$2e8,x sta $06e8,x inx bne DrawGameScreen ;Set the colours to the game screen ldx #$00 PaintGameScreen ldy $0400,x lda GameAttribs,y sta $d800,x ldy $0500,x lda GameAttribs,y sta $d900,x ldy $0600,x lda GameAttribs,y sta $da00,x ldy $06e8,x lda GameAttribs,y sta $dae8,x inx bne PaintGameScreen ;Zero fill score - Also set default lives to 3. ;We are starting a new game after all :) ldx #$00 ZeroScore lda #$00 sta Score,x inx cpx #6 bne ZeroScore ldx #$00 ;/50 stars to start with (2 pointers) ldy #$05 stx Stars+1 sty Stars lda #$03 ;3 lives to start with sta Lives ;Initialise game pointers lda #$00 ;At the start of the game, the player sta PlayerJumping ;should not be jumping, falling, nor sta PlayerFalling ;should enemies be dead. sta PlayerDead sta Enemy1Dead sta Enemy2Dead sta Enemy3Dead sta Enemy4Dead sta Enemy5Dead sta Enemy6Dead sta FireButton ;Enable fire to be pressed lda #1 sta PlayerDir ;Set pointer direction of player = RIGHT sta BulletDir ;Direction of bullet sta SelectPointer ;Init SelectionPointer sta SpawnPointer ;Reset spawn pointer ;Automatically default the source / destination jump / fall address lda #Floor_Mid ;The jump + fall source / destination sta JumpSource ;should always be set in order to have sta FallSource ;an accurate jump/fall position during lda #Floor_Top ;this game. sta JumpDest lda #Floor_Btm sta FallDest lda #1 ;Automatically enable the player's shield sta PlayerShieldEnabled lda #0 ;Reset shield time sta PlayerShieldTime lda #0 ;Disable TOSS/FALL, related to player death sta PlayerToss sta PlayerFall lda #0 ;Reset all pointers for chest (Chest released = 0) sta ChestWaitDelay sta ChestWaitTime sta ExplodeDelay sta ExplodePointer sta ChestCanSpawn lda #1 ;Reset chest position pointer sta ChestPositionPointer ;Force game music to PAL/NTSC music ;player check routine. Since music addresses ;are swapped. ldx #GameMusicPlay stx MusicPlayer+1 sty MusicPlayer+2 ;Setup the interrupts (As usual) ldx #irq lda #$2e stx $0314 sty $0315 sta $d012 lda #$7f sta $dc0d lda #$1b sta $d011 lda #$01 sta $d01a lda #$00 jsr GameMusicInit ;Init in game music cli GameLoop jsr SyncTimer ;Synchronize timer outside IRQ jsr ExpandSpriteMSB ;Expand sprite positioning jsr WaterFlow ;Animate the flowing water charset jsr AnimateSpritesToObjects ;Animate sprites to objects; jsr PlayerProperties ;Test player properties, collision, etc. jsr PlayerProperties ;Test player properties, joystick, etc jsr BulletProperties ;Test player bullet properties jsr EnemyProperties ;Test enemy properties jsr TestPlayerShield ;Test player shield count jsr ChestProperties ;Test properties of the chest jsr MaskStatus ;Mask the status pointers to screen ;Always the same sprite type. Player bullet's animation ;is set as the player bullet sprite object. lda OBJ_Bullet sta ObjectSpriteFrame+1 jmp GameLoop ;Synchronise timer SyncTimer lda GameSync and #$00 sta GameSync cmp GameSync beq *-3 rts ;Allow sprites to move more than just 256 pixels, ;so that they can use the whole screen area. ExpandSpriteMSB ldx #$00 XPLoop lda Obj_Position+1,x ;Get current Y position of object sta $d001,x ;Store to hardware Sprite Y lda Obj_Position,x ;Get current X position of object asl ror $d010 ;Force sprite position expansion sta $d000,x ;Store to hardware Sprite X inx inx cpx #16 ;16 Sprites max bne XPLoop rts ;Animate the flowing water charset WaterFlow lda CharAnimDelay ;Allow the water char to delay cmp #2 ;before shifting it beq DoCharAnim inc CharAnimDelay rts DoCharAnim lda $08c7 sta chartempbyte lda #$00 ;Reset the animation delay sta CharAnimDelay ldx #$07 CharAnimLoop lda $08bf,x ;Fetch all 8 bytes for char 24 sta $08c0,x ;Push those forwards dex bne CharAnimLoop lda chartempbyte ;Grab very last byte of char 24 sta $08c0 ;Store to very first byte of char 24 rts ;Multi-loop sprite animation sequence AnimateSpritesToObjects lda AnimDelay1 cmp #$06 ;Sprite Animdelay max speed beq DoAnimSpritesToObjects inc AnimDelay1 rts ;Sprite animation routine is now ready ;Reset sprite animation pointer and then ;generate the in game objects DoAnimSpritesToObjects lda #$00 ;Reset animation delay sta AnimDelay1 jsr Anim2FrameObjects ;Animate 2 sprite frame objects jsr Anim4FrameObjects ;Animate 3 sprite frame objects jsr SingleFrameObjects;Produce single frame objects rts ;Animate 2 frame objects, by reading the sprite ;table data and then storing those to the chosen ;object Anim2FrameObjects ldx AnimPointer1 lda SPR_Player_Left,x ;Fetch from sprite table data sta OBJ_Player_Left ;Store to object type lda SPR_Player_Right,x sta OBJ_Player_Right lda SPR_Enemy2_Right,x sta OBJ_Enemy2_Right lda SPR_Enemy2_Left,x sta OBJ_Enemy2_Left lda SPR_Enemy3_Left,x sta OBJ_Enemy3_Left lda SPR_Enemy3_Right,x sta OBJ_Enemy3_Right lda SPR_Player_Bullet,x sta OBJ_Bullet lda SPR_Chest,x sta OBJ_Chest inx cpx #2 ;2 frames max beq Reset2FrameAnim inc AnimPointer1 rts Reset2FrameAnim ldx #$00 ;Reset pointer to loop animation stx AnimPointer1 rts ;Animate 4 frame table Objects, like we did with the ;2 frame objects Anim4FrameObjects ldx AnimPointer2 ;Use a new pointer to animate lda SPR_Enemy1_Left,x sta OBJ_Enemy1_Left lda SPR_Enemy1_Right,x sta OBJ_Enemy1_Right inx cpx #$04 ;4 frames max beq Reset3FrameAnim inc AnimPointer2 rts Reset3FrameAnim ldx #$00 stx AnimPointer2 rts ;Produce single frame objects (Objects requiring no animation) SingleFrameObjects lda SPR_Player_Up ;No table this time (1 frame) sta OBJ_Player_Up lda SPR_Player_Down sta OBJ_Player_Down lda SPR_Player_Dead sta OBJ_Player_Dead rts ;Check whether or not the player is dead. If the player ;is dead. Skip control. NOTE. If player is jumping, it ;is also allowed to move LEFT of RIGHT. PlayerProperties lda PlayerDead ;Check if player is dead cmp #$01 bne _PlayerAlive ;Player isn't dead. Allow joystick control jmp PlayerDeadSequence ;Call death sequence routine (Player is dead) _PlayerAlive jmp PlayerAlive ;Call player active routine ;The player has been hit by an enemy, so check whether or not the ;player is to be TOSSED into the air, or whether the player has to ;FALL then check lives counter. If still above 0 then the player ;is allowed to respawn. Othewise, just GAME OVER! PlayerDeadSequence lda PlayerToss ;During player death sequence is the cmp #1 ;player being tossed into the air? beq _ThrowPlayer ;YES ... lda PlayerFall ;Or ... is the player just plummeting cmp #1 beq _DropPlayer ;YES ... ;Otherwise the player has lost a life. ;Check whether or not the player can respawn or ;not. jmp CheckPlayerRespawn _ThrowPlayer ;Player being tossed jmp ThrowPlayer _DropPlayer ;Player falling jmp DropPlayer CheckPlayerRespawn lda #0 sta Obj_Position+0 sta Obj_Position+1 sta PlayerShieldTime lda #$84 sta ObjectSpriteFrame ;Decrement number of lives and check if ;amount of lives = 0 then GAME OVER ;COMMENT 'dec Lives' for infinite lives dec Lives lda Lives cmp #$00 bne PlayerLives jmp GameOver ;Otherwise respawn the player, and re-enable ;player shield PlayerLives lda StartPositionTable sta Obj_Position lda StartPositionTable+1 sta Obj_Position+1 lda #0 sta PlayerShieldTime lda #1 sta PlayerShieldEnabled ;Reset player platform properties lda #Floor_Mid sta JumpSource sta FallSource lda #Floor_Top sta JumpDest lda #Floor_Btm sta FallDest lda #0 sta PlayerDead rts ;The game is lost, so now clear the entire game screen (Except ;for the status panel) GameOver ldx #$00 FillScreen1 lda #$0b sta $d800,x sta $d900,x sta $da00,x lda #79 ;Chosen char (Black space) (See Charpad) sta $0400,x sta $0500,x sta $0600,x inx bne FillScreen1 ldx #$00 FillScreen2 lda #$0b sta $db00,x lda #79 sta $0700,x inx cpx #$20 bne FillScreen2 ;Clear all sprites lda #0 sta $d015 sta FireButton ldx #$00 PlaceGameOverText lda GameOverLine1,x sta $0579,x lda GameOverLine2,x sta $05a1,x inx cpx #$04 bne PlaceGameOverText ;Before allowing to press fire to start a new game. Check whether ;or not the player's score is a brand new hi score or not. CheckHiScore lda Score sec lda HiScore+5 sbc Score+5 lda HiScore+4 sbc Score+4 lda HiScore+3 sbc Score+3 lda HiScore+2 sbc Score+2 lda HiScore+1 sbc Score+1 lda HiScore sbc Score bpl NoHiScore ;The player's score is higher than the high score ;so automatically make it a new high score for the ;player. ldy #$00 NewHi lda Score,y sta HiScore,y iny cpy #6 bne NewHi ;Wait for firebutton to be pressed before ;we exit the game. NoHiScore jsr MaskStatus ;Mask status once more lda #$00 ;Reset fire button press again sta FireButton WaitGameOver lda $dc00 lsr lsr lsr lsr lsr bit FireButton ror FireButton bmi WaitGameOver bvc WaitGameOver ;Switch off all of the interrupts once more sei ldx #$31 ldy #$ea stx $0314 sty $0315 lda #$81 sta $dc0d sta $dd0d lda #$00 sta $d019 sta $d01a jmp $4000 ;Temporary :) ;This routine tosses the player to the highest point before making ;the player plummet out of the game screen. ThrowPlayer lda SPR_Player_Down sta ObjectSpriteFrame lda Obj_Position+1 sec sbc #PlayerJumpSpeed cmp #$32 bcs UpdateThrow lda #0 sta PlayerToss lda #1 sta PlayerFall rts UpdateThrow sta Obj_Position+1 rts ;The player is falling to the very bottom floor. DropPlayer lda SPR_Player_Dead sta ObjectSpriteFrame lda Obj_Position+1 clc adc #PlayerFallSpeed cmp #$fa bcc UpdateDrop lda #0 ;Disable toss + fall sta PlayerToss sta PlayerFall rts UpdateDrop sta Obj_Position+1 rts ;The player is alive, which means player/enemy ;collision can be tested, also allow the joystick ;to be used for movement/jumping. PlayerAlive jsr TestPlayerCollision ;Joystick control lda PlayerFalling ;Is the player already falling? cmp #1 ;YES bne NotFalling ;No player not falling jsr DoFall ;Call subroutine to do actual fall jmp ReadLeft ;Jump straight to reading left/right control NotFalling lda PlayerJumping ;Is the player already jumping? cmp #1 ;YES bne ReadUp ;NO - Read the next joystick control jsr DoJump ;Call subroutine to do actual jump ReadUp lda #1 ;Read UP on joystick bit $dc00 ;read from Joystick port 2 bne ReadDown ;Up not pressed, so next process jsr CheckJumpSourceDest ;Check current source/destination ;for player to jump jmp ReadLeft ;Jump to routine to check joystick left ReadDown lda #2 ;Read DOWN on joystick bit $dc00 ;read from Joystick port 2 bne ReadLeft jsr CheckFallSourceDest ;Check current source/destination ;for player to fall to ReadLeft lda #4 ;Read LEFT on joystick bit $dc00 ;read from Joystick port 2 bne ReadRight ;Left not pressed, read right. lda #0 ;Set player direction to LEFT sta PlayerDir jsr MoveLeft ;Call subroutine to move player left jmp ReadFire ;Then jump to ReadFire routine. ReadRight lda #8 ;Read RIGHT on joystick bit $dc00 ;read from Joystick port 2 bne ReadFire ;Right not pressed, read Fire lda #1 sta PlayerDir ;Set player firing direction right jsr MoveRight ReadFire lda $dc00 ;Read joystick - FIRE lsr ;Reset joystick controls (up,down,left,right lsr ;fire, then check if fire is already held. If lsr ;it is, then don't allow the player to fire lsr ;a ninja star. lsr bit FireButton ror FireButton bmi CannotFire bvc CannotFire lda Obj_Position+3;Check if bullet (ninja stare is homed) cmp #$00 ;Check for bullet off screen (POSITION 0) bne CannotFire ;The bullet is on screen, so cannot fire ;Check whether the amount of stars = 0 for both ;counters. lda Stars ;First digit of the stars pointer cmp #$00 ;<>0 then CountOK bne CountOK lda Stars+1 ;Second digit of the stars pointer cmp #$00 ;<>0 then CountOK bne CountOK ;The player counter has no stars to throw. Therefore the ;player is unable to fire. jmp CannotFire ;Set direction player is facing. Then store it as ;the direction of the bullet. CountOK lda PlayerDir sta BulletDir ;Then position the bullet object ON to the player ;to fire the weapon. lda Obj_Position+0 ;Store position of player X sta Obj_Position+2 ;Store to position of bullet X lda Obj_Position+1 ;Store position of player Y sta Obj_Position+3 ;Store to position of bullet Y ;To do: Check for amount of ninja stars in ;possession. If no stars remain, player can ;only do small jumps. dec Stars+1 ;Reduce the second pointer for lda Stars+1 ;Stars. If the value of the pointer+1 cmp #$00 ;>0 then call HIGHER. bpl HIGHER lda #$09 ;Else. Place 9 to second pointer sta Stars+1 ;for Stars and also dec Stars ;reduce the first pointer for stars lda Stars ;If the value of the pointer+0 >0 then cmp #$00 ;once again call HIGHER bpl HIGHER lda #$00 ;Otherwise in order to avoid an illegal sta Stars+1 ;character on screen. Set 00 to Stars pointers sta Stars HIGHER ;HIGHER and CannotFire do nothing else. CannotFire rts ;Player jumping. Force the player to jump from one floor to ;another (Depending on where it is based). DoJump lda OBJ_Player_Up sta ObjectSpriteFrame lda Obj_Position+1 ;Grab Y position of player sec smSpeed1 sbc #PlayerJumpSpeed ;Self-mod speed code via timer/table cmp JumpDest ;Where should the player stop bcs UpdateJumpPos ;Not there yet ... lda JumpDest ;Set current floor player jumped to as the sta JumpSource ;new source for jumping. sta FallSource ;also set source for falling sta Obj_Position+1 ;Also force the position of the player lda #0 ;and DISABLE the player jumping sta PlayerJumping jsr UpdateDirFrame ;After jumping, update player frame rts UpdateJumpPos sta Obj_Position+1 ;Update the new position for the player rts ;Player falling. Force the player to fall from one floor ;to one floor below. (Depending on where it is based DoFall lda OBJ_Player_Down sta ObjectSpriteFrame lda Obj_Position+1 ;Grab Y position of player clc smSpeed2 adc #PlayerFallSpeed ;Self-mod speed code via timer/table cmp FallDest ;Has the player fell to platform bcc UpdateFallPos ;No, update new position lda FallDest ;Set current floor player fell to sta FallSource ;Then set as source falling sta JumpSource ;and also for jumping sta Obj_Position+1 ;and player position lda #0 sta PlayerFalling jsr UpdateDirFrame ;After falling, update player dir frame rts UpdateFallPos sta Obj_Position+1 rts ;Check Source/Destination position for where the player should ;jump. CheckJumpSourceDest lda JumpSource ;Which platform is the player on cmp #Floor_Top ;Is he on the top floor bne NotJumpFromTopFloor ;NO ... Check next routine jmp SetJumpFromTopFloor ;Otherwise jump to set new source/dest. NotJumpFromTopFloor cmp #Floor_Mid ;Or is he on the middle floor bne NotJumpFromMidFloor ;NO... Check next routine jmp SetJumpFromMidFloor ;Otherwise jump to set new source/dest. NotJumpFromMidFloor cmp #Floor_Btm ;Or maybe the bottom floor bne NotJumpFromBtmFloor ;NO ... Nothing to be done jmp SetJumpFromBtmFloor ;Otherwise jump to set new source/dest NotJumpFromBtmFloor rts ;Set destination platform position for top floor position SetJumpFromTopFloor lda #Floor_Top sta JumpDest jmp ActivateJump ;Set destination platform position for middle floor ;to top floor. SetJumpFromMidFloor lda #Floor_Top sta JumpDest jmp ActivateJump ;Set destination platform position for bottom floor SetJumpFromBtmFloor lda #Floor_Mid sta JumpDest ActivateJump ;Activate jump lda #1 sta PlayerJumping rts ;Now check fall source CheckFallSourceDest lda FallSource cmp #Floor_Top ;Can player fall from top floor bne NotFallFromTop jmp SetFallToMidFloor NotFallFromTop cmp #Floor_Mid ;What about the middle floor? bne NotAllowedToFall jmp SetFallToBtmFloor ;No other possibilities for the player to fall, so ;don't do anything here. NotAllowedToFall rts ;Player can fall from top platform to middle SetFallToMidFloor lda #Floor_Mid sta FallDest jmp ActivateFall ;Player can fall to the bottom floor SetFallToBtmFloor lda #Floor_Btm sta FallDest ActivateFall lda #$01 sta PlayerFalling rts ;Update the player's dir frame according to the ;previous direction the player was facing. 0 = Left, 1 = Right ;After selected, update hardware sprite ObjectSpriteFrame with the last ;sprite frame before jumping or falling. UpdateDirFrame lda PlayerDir cmp #1 ;Right beq UpdateFrameRight UpdateFrameLeft lda OBJ_Player_Left sta ObjectSpriteFrame rts UpdateFrameRight lda OBJ_Player_Right sta ObjectSpriteFrame rts ;Player moving to the left of the screen. The player has a limit ;where it can move. MoveLeft lda OBJ_Player_Left ;Animation of player sta ObjectSpriteFrame ;Store to ObjectSpriteFrame (Which is inside top raster) lda Obj_Position ;Grab current position of player sec sbc #PlayerRunSpeed ;Speed of player to move Speed=Speed-PlayerRunSpeed cmp #$0c ;Has the player reached stopping value? bcs StoreLeft ;Not yet. lda #$0c ;Force stop position to player StoreLeft sta Obj_Position ;Update new position to the player rts ;Move player to the right until it reaches the ;stopping position. MoveRight lda OBJ_Player_Right ;Animation of player sta ObjectSpriteFrame ;Store to ObjectSpriteFrame (Which is inside top raster) lda Obj_Position ;Grab current position of player clc adc #PlayerRunSpeed ;Speed of player to move Speed=Speed+PlayerRunSpeed cmp #$a1 ;Has the player reached stopping value? bcc StoreRight ;Not yet. lda #$a0 ;Force stop position to player StoreRight sta Obj_Position ;Update new position to the player rts ;Test player to enemy collision TestPlayerCollision lda Obj_Position ;Read player X sec sbc #CollisionXLeft ;Set X Left collision boundary sta Collision clc adc #CollisionXRight;Set X Right collision boundary sta Collision+1 lda Obj_Position+1 ;Read player Y sec sbc #CollisionYUp ;Set Y Up collision boundary sta Collision+2 clc adc #CollisionYDown ;Set Y Down collision boundary sta Collision+3 rts ;Player bullet properties. Basically check which direction the ;player's bullet is being fired. Is it being fired to the left ;or fired to the right. Also after firing the bullet to a certain ;position in the game. Remove it off screen. BulletProperties jsr TestBulletCollision lda BulletDir ;Grab bullet direction cmp #1 ;Bullet direction RIGHT = TRUE? ;Direction = RIGHT? bne MoveBulletLeft ;No. Make it move LEFT instead jmp MoveBulletRight ;Allow bullet to fire RIGHT rts ;The bullet is fired to the left of the screen MoveBulletLeft lda Obj_Position+2 ;Grab Player Bullet X Position sec sbc #BulletSpeed ;Force bullet speed backwards (Speed=Speed-BulletSpeed) cmp #$fa ;has the bullet reached position #$fa outside border bcc StoreBull1 ;No... Just update new position lda #$00 ;ELSE ... Make bullet offset sta Obj_Position+3 ;Bullet sprite Y position = 0 StoreBull1 sta Obj_Position+2 ;Update new X position of bullet. rts ;The bullet is fired to the right of the screen MoveBulletRight lda Obj_Position+2 ;Sprite VPOS 1X (Bullet) clc adc #BulletSpeed ;Force bullet speed forwards cmp #$a6 ;Very far right of the screen bcc StoreBull2 lda #$00 ;Home X/Y sprite position sta Obj_Position+3 StoreBull2 sta Obj_Position+2 rts ;Test player bullet collision (Similar to setting up ;the player's collision boundary) TestBulletCollision lda Obj_Position+2 sec sbc #CollisionXLeft sta Collision+4 clc adc #CollisionXRight sta Collision+5 lda Obj_Position+3 sec sbc #CollisionYUp sta Collision+6 clc adc #CollisionYDown sta Collision+7 rts ;Enemy properties. A few JSRs to test enemy properties. The multiple ;JSRs call for a test on each enemy before enemies can be moved, ;killed or similar. EnemyProperties jsr TestEnemy1 jsr TestEnemy2 jsr TestEnemy3 jsr TestEnemy4 jsr TestEnemy5 rts ;ENEMY 1 Test ... TestEnemy1 ;Check whether or not the enemy is officially dead ;if it is, then the enemy should be using explosion ;sequence. lda Enemy1Dead cmp #$01 beq Enemy1IsDead ;The enemy is dead jmp Enemy1Alive ;Enemy 1 is dead. Make the enemy fall off the screen Enemy1IsDead lda Obj_Position+5 ;basically make the enemy clc ;automatically fall off the adc #3 ;screen at a fast(ish)speed cmp #$fa ;Reached bottom? bcc Enemy1NotOutYet;Not quite lda #0 ;After leaving the screen, the sta Enemy1Dead ;enemy is no longer dead. Enemy1NotOutYet sta Obj_Position+5 rts ;Enemy1 is alive, so now setup the Self-Mod animation frame ;and colour to the enemy object Enemy1Alive lda PlayerDead ;Skip all collision if player is dead cmp #1 beq E1PNotHit ;Test enemy to player range ... lda Obj_Position+4 ;Check enemy X position with collision cmp Collision ;range bcc E1PNotHit cmp Collision+1 bcs E1PNotHit lda Obj_Position+5 ;Check enemy Y position with collision cmp Collision+2 ;range bcc E1PNotHit cmp Collision+3 bcs E1PNotHit ;Enemy to player not hit jmp PlayerHit ;Player has been hit E1PNotHit ;Test enemy to player bullet range ... lda Obj_Position+4 cmp Collision+4 bcc E1BNotHit cmp Collision+5 bcs E1BNotHit lda Obj_Position+5 cmp Collision+6 bcc E1BNotHit cmp Collision+7 bcs E1BNotHit ;ENEMY has been hit by the bullet ... ;Compare to a number of lives dec Enemy1Lives ;Decrement one life from the enemy lda Enemy1Lives cmp #0 bne Enemy1IsNotDead ;Enemy is not dead jmp Enemy1Hit ;Otherwise call subroutine that will kill enemy Enemy1IsNotDead jsr HomeBullet ;Remove player bullet from screen rts Enemy1Hit jsr HomeBullet ;Remove bullet from screen lda #$02 ;Colour RED - To indicate enemy hit sta $d029 ldy #$00 AddScoreLoop jsr AddScore ;Add points to score according to iny ;the value of Enemy1Score cpy Enemy1Score bne AddScoreLoop lda #1 ;Trigger enemy 1 dead sta Enemy1Dead rts E1BNotHit E1Frame lda Ninja1_Left ;Pick stored Self-Mod frame animation sta ObjectSpriteFrame+2 ;Store it to raster sprite pointer E1Colour lda #$06 ;Pick stored Self-Mod frame colour sta $d029 ;Store it to raster sprite pointer lda Enemy1Delay cmp Enemy1DelayLimit bne SkipEnemy1Movement lda #$00 sta Enemy1Delay jmp MoveEnemy1 SkipEnemy1Movement inc Enemy1Delay rts ;Move enemy 1 according to direction it has been set from the ;stored value from the object spawning table. MoveEnemy1 lda Enemy1Dir ;Enemy1Dir = 0 (Left), or 1 (Right) cmp #1 ;Enemy1Dir = 1 (Right) ???? beq MoveE1Right ;Yes, move enemy 1 right MoveE1Left ;Else move enemy 1 left lda Obj_Position+4 sec ;Subtract from the value of the sbc Enemy1Speed ;enemy speed before updating new position cmp #$fa ;Leaves the game screen? bcc StoreE1Left ;No just update new position jmp SpawnNextEnemy1 ;Enemy reached its position. Spawn next enemy StoreE1Left sta Obj_Position+4 ;New position has been stored rts MoveE1Right ;Move enemy 1 to the right lda Obj_Position+4 clc ;Add by enemy 1 speed before updating adc Enemy1Speed ;new position. cmp #$b2 ;Enemy reached inside border at the right bcc StoreE1Right ;No, object stored to right jmp SpawnNextEnemy1 ;Enemy reached its position. Spawn next enemy rts StoreE1Right sta Obj_Position+4 ;New position has been stored rts SpawnNextEnemy1 jsr SpawnNewEnemy ;Subroutine to select next series of bytes lda NewEnemyColour ;Pick new enemy colour then sta E1Colour+1 ;store it to the new enemy lda NewEnemyDelay ;Pick new delay value for enemy sta Enemy1DelayLimit ;Set it to delay limit lda NewFloorPosition ;Pick new enemy floor position sta Obj_Position+5 ;Store it to object position Y lda SMStartXPosition ;Fetch new starting position sta Obj_Position+4 ;Store it to object position X lda SMFrameLow ;Fetch low-byte of enemy animation sta E1Frame+1 ;Store to Self-Mod lowbyte anim position lda SMFrameHi ;Fetch hi-byte of enemy animation sta E1Frame+2 ;Store to Self-Mod hibyte anim position lda SMHitsToKill ;Get Self-Mod hits to kill value sta Enemy1Lives ;Store to amount of lives set lda SMScoreValue ;Get Self-Mod amount of points sta Enemy1Score ;Store to enemy score lda SMDirection ;get Self-Mod direction sta Enemy1Dir ;Store to enemy direction lda #0 sta Enemy1Dead ;Object is now re-spawned so we are finished here rts ;ENEMY 2 Test ... TestEnemy2 ;Check whether or not the enemy is officially dead ;if it is, then the enemy should be using explosion ;sequence. lda Enemy2Dead cmp #$01 beq Enemy2IsDead ;The enemy is dead jmp Enemy2Alive ;Enemy 2 is dead. Make the enemy fall off the screen Enemy2IsDead lda Obj_Position+7 ;basically make the enemy clc ;automatically fall off the adc #3 ;screen at a fast(ish)speed cmp #$fa ;Reached bottom? bcc Enemy2NotOutYet;Not quite lda #0 ;After leaving the screen sta Enemy2Dead ;the enemy is no longer dead Enemy2NotOutYet sta Obj_Position+7 rts Enemy2Alive lda PlayerDead ;Skip all collision if player is dead cmp #1 beq E2PNotHit ;Test enemy to player range ... lda Obj_Position+6 ;Check enemy X position with collision cmp Collision ;range bcc E2PNotHit cmp Collision+1 bcs E2PNotHit lda Obj_Position+7 ;Check enemy Y position with collision cmp Collision+2 ;range bcc E2PNotHit cmp Collision+3 bcs E2PNotHit ;Enemy to player not hit jmp PlayerHit ;Player has been it E2PNotHit ;Test enemy to player bullet range ... lda Obj_Position+6 cmp Collision+4 bcc E2BNotHit cmp Collision+5 bcs E2BNotHit lda Obj_Position+7 cmp Collision+6 bcc E2BNotHit cmp Collision+7 bcs E2BNotHit ;Compare to a number of lives dec Enemy2Lives lda Enemy2Lives cmp #0 bne Enemy2IsNotDead jmp Enemy2Hit Enemy2IsNotDead jsr HomeBullet rts Enemy2Hit jsr HomeBullet ;Remove bullet from screen lda #$02 ;Colour RED - To indicate enemy hit sta $d02a ldy #$00 AddScoreLoop2 jsr AddScore ;Add points to score according to iny ;the value of Enemy1Score cpy Enemy2Score bne AddScoreLoop2 lda #1 ;Trigger enemy 2 dead sta Enemy2Dead rts E2BNotHit E2Frame lda Ninja2_Right ;Pick stored Self-Mod frame animation sta ObjectSpriteFrame+3 ;Store it to raster sprite pointer E2Colour lda #$02 ;Pick stored Self-Mod frame colour sta $d02a ;Store it to hardware sprite pointer lda Enemy2Delay cmp Enemy2DelayLimit bne SkipEnemy2Movement lda #$00 sta Enemy2Delay jmp MoveEnemy2 SkipEnemy2Movement inc Enemy2Delay rts ;Move enemy 2 according to direction it has been set from the ;stored value from the object spawning table. MoveEnemy2 lda Enemy2Dir ;Enemy2Dir = 0 (Left), or 1 (Right) cmp #1 ;Enemy2Dir = 1 (Right) ???? beq MoveE2Right ;Yes, move enemy 2 right MoveE2Left ;Else move enemy 2 left lda Obj_Position+6 sec ;Subtract from the value of the sbc Enemy2Speed ;enemy speed before updating new position cmp #$fa ;Leaves the game screen? bcc StoreE2Left ;No just update new position jmp SpawnNextEnemy2 ;Enemy reached its position. Spawn next enemy StoreE2Left sta Obj_Position+6 ;New position has been stored rts MoveE2Right ;Move enemy 2 to the right lda Obj_Position+6 clc ;Add by enemy 2 speed before updating adc Enemy2Speed ;new position. cmp #$b2 ;Enemy reached inside border at the right? bcc StoreE2Right ;No, object stored to right jmp SpawnNextEnemy2 ;Enemy reached its position. Spawn next enemy rts StoreE2Right sta Obj_Position+6 ;New position has been stored rts SpawnNextEnemy2 jsr SpawnNewEnemy ;Subroutine to select next series of bytes ;that will form the next enemy properties lda NewEnemyColour ;Pick new enemy colour then sta E2Colour+1 ;store it to the new enemy lda NewEnemyDelay ;Pick new delay value for enemy sta Enemy2DelayLimit ;Set it to delay limit lda NewFloorPosition ;Pick new enemy floor position sta Obj_Position+7 ;Store it to object position Y lda SMStartXPosition ;Fetch new starting position sta Obj_Position+6 ;Store it to object position X lda SMFrameLow ;Fetch low-byte of enemy animation sta E2Frame+1 ;Store to Self-Mod lowbyte anim position lda SMFrameHi ;Fetch hi-byte of enemy animation sta E2Frame+2 ;Store to Self-Mod hibyte anim position lda SMHitsToKill ;Get Self-Mod hits to kill value sta Enemy2Lives ;Store to amount of lives set lda SMScoreValue ;Get Self-Mod amount of points sta Enemy2Score ;Store to enemy score lda SMDirection ;get Self-Mod direction sta Enemy2Dir ;Store to enemy direction lda #0 sta Enemy2Dead ;Object is now re-spawned so we are finished here rts ;ENEMY 3 Test ... TestEnemy3 ;Check whether or not the enemy is officially dead ;if it is, then the enemy should be using explosion ;sequence. lda Enemy3Dead cmp #$01 beq Enemy3IsDead ;The enemy is dead jmp Enemy3Alive ;Enemy 3 is dead. Make the enemy fall off the screen Enemy3IsDead lda Obj_Position+9 ;basically make the enemy clc ;automatically fall off the adc #3 ;screen at a fast(ish)speed cmp #$fa ;Reached bottom? bcc Enemy3NotOutYet;Not quite lda #0 ;After leaving the screen sta Enemy3Dead ;the enemy is no longer dead Enemy3NotOutYet sta Obj_Position+9 rts ;Enemy is alive, so now setup the Self-Mod animation frame ;and colour to the enemy object Enemy3Alive lda PlayerDead ;Skip all collision if player is dead cmp #1 beq E3PNotHit ;Test enemy to player range ... lda Obj_Position+8 ;Check enemy X position with collision cmp Collision ;range bcc E3PNotHit cmp Collision+1 bcs E3PNotHit lda Obj_Position+9 ;Check enemy Y position with collision cmp Collision+2 ;range bcc E3PNotHit cmp Collision+3 bcs E3PNotHit ;Enemy to player not hit jmp PlayerHit ;Player has been it E3PNotHit ;Test enemy to player bullet range ... lda Obj_Position+8 cmp Collision+4 bcc E3BNotHit cmp Collision+5 bcs E3BNotHit lda Obj_Position+9 cmp Collision+6 bcc E3BNotHit cmp Collision+7 bcs E3BNotHit ;Compare to a number of lives dec Enemy3Lives lda Enemy3Lives cmp #0 bne Enemy3IsNotDead jmp Enemy3Hit Enemy3IsNotDead jsr HomeBullet rts Enemy3Hit jsr HomeBullet ;Remove bullet from screen lda #$02 sta $d02b ldy #$00 AddScoreLoop3 jsr AddScore ;Add points to score according to iny ;the value of Enemy3Score cpy Enemy3Score bne AddScoreLoop3 lda #1 ;Trigger enemy 3 dead sta Enemy3Dead rts E3BNotHit E3Frame lda Ninja1_Left ;Pick stored Self-Mod frame animation sta ObjectSpriteFrame+4 ;Store it to raster sprite pointer E3Colour lda #$06 ;Pick stored Self-Mod frame colour sta $d02b ;Store it to hardware sprite pointer lda Enemy3Delay cmp Enemy3DelayLimit bne SkipEnemy3Movement lda #$00 sta Enemy3Delay jmp MoveEnemy3 SkipEnemy3Movement inc Enemy3Delay rts ;Move enemy 3 according to direction it has been set from the ;stored value from the object spawning table. MoveEnemy3 lda Enemy3Dir ;Enemy1Dir = 0 (Left), or 1 (Right) cmp #1 ;Enemy1Dir = 1 (Right) ???? beq MoveE3Right ;Yes, move enemy 3 right MoveE3Left ;Else move enemy 3 left lda Obj_Position+8 sec ;Subtract from the value of the sbc Enemy3Speed ;enemy speed before updating new position cmp #$fa ;Leaves the game screen? bcc StoreE3Left ;No just update new position jmp SpawnNextEnemy3 ;Enemy reached its position. Spawn next enemy StoreE3Left sta Obj_Position+8 ;New position has been stored rts MoveE3Right ;Move enemy 3 to the right lda Obj_Position+8 clc ;Add by enemy 3 speed before updating adc Enemy3Speed ;new position. cmp #$b2 ;Enemy reached inside border at the right bcc StoreE3Right ;No, object stored to right jmp SpawnNextEnemy3 ;Enemy reached its position. Spawn next enemy rts StoreE3Right sta Obj_Position+8 ;New position has been stored rts SpawnNextEnemy3 jsr SpawnNewEnemy ;Subroutine to select next series of bytes ;that will form the next enemy properties lda NewEnemyColour ;Pick new enemy colour then sta E3Colour+1 ;store it to the new enemy lda NewEnemyDelay ;Pick new delay value for enemy sta Enemy3DelayLimit ;Set it to delay limit lda NewFloorPosition ;Pick new enemy floor position sta Obj_Position+9 ;Store it to object position Y lda SMStartXPosition ;Fetch new starting position sta Obj_Position+8 ;Store it to object position X lda SMFrameLow ;Fetch low-byte of enemy animation sta E3Frame+1 ;Store to Self-Mod lowbyte anim position lda SMFrameHi ;Fetch hi-byte of enemy animation sta E3Frame+2 ;Store to Self-Mod hibyte anim position lda SMHitsToKill ;Get Self-Mod hits to kill value sta Enemy3Lives ;Store to amount of lives set lda SMScoreValue ;Get Self-Mod amount of points sta Enemy3Score ;Store to enemy score lda SMDirection ;get Self-Mod direction sta Enemy3Dir ;Store to enemy direction lda #0 sta Enemy3Dead ;Object is now re-spawned so we are finished here rts ;ENEMY 4 Test ... TestEnemy4 ;Check whether or not the enemy is officially dead ;if it is, then the enemy should be using explosion ;sequence. lda Enemy4Dead cmp #$01 beq Enemy4IsDead ;The enemy is dead jmp Enemy4Alive ;Enemy 4 is dead. Make the enemy fall off the screen Enemy4IsDead lda Obj_Position+11 ;basically make the enemy clc ;automatically fall off the adc #3 ;screen at a fast(ish)speed cmp #$fa ;Reached bottom? bcc Enemy4NotOutYet;Not quite lda #0 ;After leaving the screen sta Enemy4Dead ;enemy 4 is no longer dead Enemy4NotOutYet sta Obj_Position+11 rts ;Enemy is alive, so now setup the Self-Mod animation frame ;and colour to the enemy object Enemy4Alive lda PlayerDead ;Skip all collision if player is dead cmp #1 beq E4PNotHit ;Test enemy to player range ... lda Obj_Position+10 ;Check enemy X position with collision cmp Collision ;range bcc E4PNotHit cmp Collision+1 bcs E4PNotHit lda Obj_Position+11 ;Check enemy Y position with collision cmp Collision+2 ;range bcc E4PNotHit cmp Collision+3 bcs E4PNotHit ;Enemy to player not hit jmp PlayerHit ;Player has been it E4PNotHit ;Test enemy to player bullet range ... lda Obj_Position+10 cmp Collision+4 bcc E4BNotHit cmp Collision+5 bcs E4BNotHit lda Obj_Position+11 cmp Collision+6 bcc E4BNotHit cmp Collision+7 bcs E4BNotHit ;Compare to a number of lives dec Enemy4Lives lda Enemy4Lives cmp #0 bne Enemy4IsNotDead jmp Enemy4Hit Enemy4IsNotDead jsr HomeBullet rts Enemy4Hit lda #0 jsr HomeBullet ;Remove bullet from screen lda #$02 sta $d02c ldy #$00 AddScoreLoop4 jsr AddScore ;Add points to score according to iny ;the value of Enemy4Score cpy Enemy4Score bne AddScoreLoop4 lda #1 ;Trigger enemy 4 dead sta Enemy4Dead rts E4BNotHit E4Frame lda Ninja1_Right ;Pick stored Self-Mod frame animation sta ObjectSpriteFrame+5 ;Store it to hardware sprite pointer E4Colour lda #$09 ;Pick stored Self-Mod frame colour sta $d02c ;Store it to hardware sprite pointer lda Enemy4Delay cmp Enemy4DelayLimit bne SkipEnemy4Movement lda #$00 sta Enemy4Delay jmp MoveEnemy4 SkipEnemy4Movement inc Enemy4Delay rts ;Move enemy 4 according to direction it has been set from the ;stored value from the object spawning table. MoveEnemy4 lda Enemy4Dir ;Enemy4Dir = 0 (Left), or 1 (Right) cmp #1 ;Enemy4Dir = 1 (Right) ???? beq MoveE4Right ;Yes, move enemy 4 right MoveE4Left ;Else move enemy 4 left lda Obj_Position+10 sec ;Subtract from the value of the sbc Enemy4Speed ;enemy speed before updating new position cmp #$fa ;Leaves the game screen? bcc StoreE4Left ;No just update new position jmp SpawnNextEnemy4 ;Enemy reached its position. Spawn next enemy StoreE4Left sta Obj_Position+10 ;New position has been stored rts MoveE4Right ;Move enemy 4 to the right lda Obj_Position+10 clc ;Add by enemy 4 speed before updating adc Enemy4Speed ;new position. cmp #$b2 ;Enemy reached inside border at the right bcc StoreE4Right ;No, object stored to right jmp SpawnNextEnemy4 ;Enemy reached its position. Spawn next enemy rts StoreE4Right sta Obj_Position+10 ;New position has been stored rts SpawnNextEnemy4 jsr SpawnNewEnemy ;Subroutine to select next series of bytes ;that will form the next enemy properties lda NewEnemyColour ;Pick new enemy colour then sta E4Colour+1 ;store it to the new enemy lda NewEnemyDelay ;Pick new delay value for enemy sta Enemy4DelayLimit ;Set it to delay limit lda NewFloorPosition ;Pick new enemy floor position sta Obj_Position+11 ;Store it to object position Y lda SMStartXPosition ;Fetch new starting position sta Obj_Position+10 ;Store it to object position X lda SMFrameLow ;Fetch low-byte of enemy animation sta E4Frame+1 ;Store to Self-Mod lowbyte anim position lda SMFrameHi ;Fetch hi-byte of enemy animation sta E4Frame+2 ;Store to Self-Mod hibyte anim position lda SMHitsToKill ;Get Self-Mod hits to kill value sta Enemy4Lives ;Store to amount of lives set lda SMScoreValue ;Get Self-Mod amount of points sta Enemy4Score ;Store to enemy score lda SMDirection ;get Self-Mod direction sta Enemy4Dir ;Store to enemy direction lda #0 sta Enemy4Dead ;Object is now re-spawned so we are finished here rts ;ENEMY 5 Test ... TestEnemy5 ;Check whether or not the enemy is officially dead ;if it is, then the enemy should be using explosion ;sequence. lda Enemy5Dead cmp #$01 beq Enemy5IsDead ;The enemy is dead jmp Enemy5Alive ;Enemy 5 is dead. Make the enemy fall off the screen Enemy5IsDead lda Obj_Position+13 ;basically make the enemy clc ;automatically fall off the adc #3 ;screen at a fast(ish)speed cmp #$fa ;Reached bottom? bcc Enemy5NotOutYet ;Not quite lda #0 ;After leaving the screen sta Enemy5Dead ;Enemy 5 is no longer dead. Enemy5NotOutYet sta Obj_Position+13 rts ;Enemy 5 is alive, so now setup the Self-Mod animation frame ;and colour to the enemy object Enemy5Alive lda PlayerDead ;Skip all collision if player is dead cmp #1 beq E5PNotHit ;Test enemy to player range ... lda Obj_Position+12 ;Check enemy X position with collision cmp Collision ;range bcc E5PNotHit cmp Collision+1 bcs E5PNotHit lda Obj_Position+13 ;Check enemy Y position with collision cmp Collision+2 ;range bcc E5PNotHit cmp Collision+3 bcs E5PNotHit ;Enemy to player not hit jmp PlayerHit ;Player has been it E5PNotHit ;Test enemy to player bullet range ... lda Obj_Position+12 cmp Collision+4 bcc E5BNotHit cmp Collision+5 bcs E5BNotHit lda Obj_Position+13 cmp Collision+6 bcc E5BNotHit cmp Collision+7 bcs E5BNotHit ;Compare to a number of lives dec Enemy5Lives lda Enemy5Lives cmp #0 bne Enemy5IsNotDead jmp Enemy5Hit Enemy5IsNotDead jsr HomeBullet rts Enemy5Hit lda #0 jsr HomeBullet ;Remove bullet from screen lda #$02 sta $d02d ldy #$00 AddScoreLoop5 jsr AddScore ;Add points to score according to iny ;the value of Enemy5Score cpy Enemy5Score bne AddScoreLoop5 lda #1 ;Trigger enemy 5 dead sta Enemy5Dead rts E5BNotHit E5Frame lda Ninja1_Left ;Pick stored Self-Mod frame animation sta ObjectSpriteFrame+6 ;Store it to hardware sprite pointer E5Colour lda #$04 ;Pick stored Self-Mod frame colour sta $d02d ;Store it to hardware sprite pointer lda Enemy5Delay cmp Enemy5DelayLimit bne SkipEnemy5Movement lda #$00 sta Enemy5Delay jmp MoveEnemy5 SkipEnemy5Movement inc Enemy5Delay rts ;Move enemy 5 according to direction it has been set from the ;stored value from the object spawning table. MoveEnemy5 lda Enemy5Dir ;Enemy5Dir = 0 (Left), or 1 (Right) cmp #1 ;Enemy5Dir = 1 (Right) ???? beq MoveE5Right ;Yes, move enemy 5 right MoveE5Left ;Else move enemy 5 left lda Obj_Position+12 sec ;Subtract from the value of the sbc Enemy5Speed ;enemy speed before updating new position cmp #$fa ;Leaves the game screen? bcc StoreE5Left ;No just update new position jmp SpawnNextEnemy5 ;Enemy reached its position. Spawn next enemy StoreE5Left sta Obj_Position+12 ;New position has been stored rts MoveE5Right ;Move enemy 5 to the right lda Obj_Position+12 clc ;Add by enemy 5 speed before updating adc Enemy5Speed ;new position. cmp #$b2 ;Enemy reached inside border at the right bcc StoreE5Right ;No, object stored to right jmp SpawnNextEnemy5 ;Enemy reached its position. Spawn next enemy rts StoreE5Right sta Obj_Position+12 ;New position has been stored rts SpawnNextEnemy5 jsr SpawnNewEnemy ;Subroutine to select next series of bytes lda NewEnemyColour ;Pick new enemy colour then sta E5Colour+1 ;store it to the new enemy lda NewEnemyDelay ;Pick new delay value for enemy sta Enemy5DelayLimit ;Set it to delay limit lda NewFloorPosition ;Pick new enemy floor position sta Obj_Position+13 ;Store it to object position Y lda SMStartXPosition ;Fetch new starting position sta Obj_Position+12 ;Store it to object position X lda SMFrameLow ;Fetch low-byte of enemy animation sta E5Frame+1 ;Store to Self-Mod lowbyte anim position lda SMFrameHi ;Fetch hi-byte of enemy animation sta E5Frame+2 ;Store to Self-Mod hibyte anim position lda SMHitsToKill ;Get Self-Mod hits to kill value sta Enemy5Lives ;Store to amount of lives set lda SMScoreValue ;Get Self-Mod amount of points sta Enemy5Score ;Store to enemy score lda SMDirection ;get Self-Mod direction sta Enemy5Dir ;Store to enemy direction lda #0 sta Enemy5Dead ;Object is now re-spawned so we are finished here rts ;Main enemy spawn routine, which will read 2 different pointers. ;These will read specific tables and store those to temp bytes or ;SM pointers. After finishing, a subroutine should write the new ;object properties to a specific enemy that has either been killed ;or left the screen. SpawnNewEnemy ldx SpawnPointer ;Load X value to SpawnPointer lda SpawnTable,x ;Read a byte from the SpawnTable sta SelectPointer ;Store to SelectPointer lda EnemyColourTable,x ;Pick a colour from table sta NewEnemyColour ;Store as NewEnemyColour lda EnemyDelayTable,x ;Pick a delayed speed from EnemyDelayTable sta NewEnemyDelay ;Store as NewEnemyDelay lda EnemyStartTable,x ;Pick a byte from the enemy start table sta NewFloorPosition ;Store to the New floor position inx cpx #254 ;Reached limit (254) beq GameIsWon ;YES. Player survived - The Game is won! inc SpawnPointer ;Otherwise increment X value to SpawnPointer ldy SelectPointer ;Load Y Value to SelectPointer lda EnemyTypeTableLo,y;Read the lo-byte table for enemy type sta SMFrameLow ;Store it to a Self mod Pointer SMFrameLow lda EnemyTypeTableHi,y;Read the hi-byte table for enemy type sta SMFrameHi ;Store it to a Self mod Pointer SMFrameHi lda EnemyKillTable,y ;Read the table to set hits to kill enemy sta SMHitsToKill ;Store it to a Self mod pointer SMHitsToKill lda EnemyScoreTable,y ;Read the table to set enemy score value sta SMScoreValue ;Store the score value SMScoreValue lda EnemyDirTable,y ;Read the enemy direction table sta SMDirection ;Store to self mod pointer SMDirection lda EnemyStartXTable,y;Read the enemy direction (X-direction) table sta SMStartXPosition ;Store to self mod pointer SMStartXPosition rts ;Call game complete routine GameIsWon jmp GameComplete ;Corresponding to the enemy / bullet collision, give the player ;points per enemy hit. Simply add every 100 to each score, via ;usage of pointers. (The actual score will then be stored to ;the screen using the MaskScore subroutine). AddScore inc Score+3 ;There are 6 digits in total. We start scoring by ldx #3 ;incrementing the THIRD digit, and call a loop ScoreLoop ;to check the values of the score pointers. lda Score,x cmp #$0a ;Illegal character ($0a) - If detected - Should be 0 bne ScoreOK lda #$00 sta Score,x inc Score-1,x ;One byte read, value higher. Move to previous digit ScoreOK dex bne ScoreLoop rts ;Home player's bullet outside screen (While it is moving) HomeBullet lda #$00 sta Obj_Position+3 rts ;The player has been hit by an enemy. Check whether or not ;the player is temporarily invincible or not (In order to ;cheat collision with enemies). If the player's shield is on ;enemies will just stop at the player. Otherwise the player ;can move out the way PlayerHit lda PlayerShieldEnabled ;Player is still invulnerable? cmp #1 ;Yes ... beq SkipCollision ;No need to do anything else. lda #1 ;Otherwise the player is dead. Allow sta PlayerToss ;the player to be tossed off the platform lda #1 ;also set PlayerDead to enabled. sta PlayerDead SkipCollision ;Otherwise ... Do nothing! rts ;Test whether or not the player's shield is enabled ;if it is enabled, allow the player to flash. Otherwise ;the shield is disabled and prevents the player from ;flashing, and the player is restored to blue TestPlayerShield lda PlayerShieldEnabled ;Shield enabled? cmp #1 ;Yes? beq AllowPlayerShield ;Subroutine to reduce shield time lda #6 ;No Shield. Paint player blue. sta $d027 rts ;The player's shield is enabled. Generate a timer for which the ;player's shield can stay enabled for some time before the ;shield time runs out. AllowPlayerShield lda PlayerShieldTime cmp #254 ;Has shield limit been reached? beq DisablePlayerShield ;Yes it has ... Run DisablePlayerShield inc PlayerShieldTime ;Otherwise increase the timer inc $d027 ;and flash player by incrementing colour rts ;Reset shield timer, then disable shield. DisablePlayerShield lda #0 sta PlayerShieldTime sta PlayerShieldEnabled rts ;Mask status pointers (Score, Hi Score, Lives, Stars ;to the designated screen position according to the ;design of the score panel. MaskStatus ldx #$00 MaskScore lda Score,x ;Grab score pointers clc adc #ScoreDigitFont ;Convert to score digit font sta ScoreStore,x ;Store to screen lda HiScore,x ;Grab hiscore pointers clc adc #ScoreDigitFont ;Convert pointers into the correct characters sta HiScoreStore,x inx cpx #$06 ;6 Digits max bne MaskScore lda Stars ;Do the same for ninja stars clc adc #ScoreDigitFont sta StarsStore lda Stars+1 clc adc #ScoreDigitFont sta StarsStore+1 lda Lives ;And also lives clc adc #ScoreDigitFont sta LivesStore rts ;Chest properties. The chest is timed to appear and also ;disappear. ChestProperties lda ChestCanSpawn cmp #$01 beq ChestCanAppear ;The chest can't spawn yet, so allow it ;to wait for a short delay or so. lda ChestWaitDelay cmp #3 beq ChestWaitDelayOK inc ChestWaitDelay rts ;Reset delay, then increase spawn wait timer ;ChestWaitTime until it has reached a limit ;after limit has been reached. Reset Delay+Timer ;and set spawn position for chest. ChestWaitDelayOK lda #0 sta ChestWaitDelay inc ChestWaitTime lda ChestWaitTime cmp #50 ;Time reached beq SetNewPosition rts SetNewPosition lda #0 ;Reset the timer sta ChestWaitTime ;Read table to set new position for ;chest. ldx ChestPositionPointer lda EnemyStartTable,x sta Obj_Position+15 ;New Y position for chest read lda ChestStartTable,x sta Obj_Position+14 ;New X position for chest read inx cpx #254 ;Like with enemy spawning total amount beq ResetChestRespawn ;Reset after amount inc ChestPositionPointer lda #1 sta ChestCanSpawn rts ResetChestRespawn ldx #0 stx ChestPositionPointer rts ChestCanAppear ;Animation subroutine that will make ;the chest explode on to screen. lda ExplodeDelay cmp #3 beq ExplodeOK inc ExplodeDelay rts ExplodeOK lda #0 sta ExplodeDelay ldx ExplodePointer lda SPR_Explosion,x sta ObjectSpriteFrame+7 lda #$08 sta $d02e inx cpx #SPR_ExplosionEnd-SPR_Explosion beq ChestReady inc ExplodePointer rts ChestReady lda #5 ;Set 5th frame as flashing chest sta ExplodePointer ;then store to explode pointer lda #1 ;and re-enable player/chest collision. sta ChestCollisionEnabled jsr TestPlayerToChestCollision ;Re-use the wait timer and pointer once more ;to remove the chest from the screen. lda ChestWaitDelay cmp #3 beq ChestWaitDelayOutOK inc ChestWaitDelay rts ChestWaitDelayOutOK lda #0 sta ChestWaitDelay inc ChestWaitTime lda ChestWaitTime cmp #20 beq RemoveChest ;inc $d021 rts ;The chest has been on screen for long enough ;remove it. RemoveChest lda #0 sta ChestWaitDelay sta ChestWaitTime sta Obj_Position+15 sta ChestCanSpawn sta ChestCollisionEnabled sta ExplodeDelay sta ExplodePointer lda #$9c ;Add blank frame sta ObjectSpriteFrame+7 rts ;Test player to chest collision. If the collision ;detection is enabled. Remove the chest from the ;screen, and also award points for the player, ;as well as give the player some more ninja stars. TestPlayerToChestCollision lda Obj_Position+14 cmp Collision ;Check left boundary bcc NoChestPickedUp cmp Collision+1 ;Check right boundary bcs NoChestPickedUp lda Obj_Position+15 cmp Collision+2 ;Check upper boundary bcc NoChestPickedUp cmp Collision+3 ;Check lower boundary bcs NoChestPickedUp jmp ChestCollected ;Chest has been picked up NoChestPickedUp rts ;No collision with the player ;found ;The chest has been collected. Perform two different operations ;1. Award the player 1500 points. 2. Award the player 10 ninja ;stars to throw. ChestCollected lda #$00 sta Obj_Position+15 ;Remove chest object. ldy #$00 ;Loop is called to award 1500 points. Award1500Points jsr AddScore ;Add score reads 100 points, but inside iny ;this loop. 15*100 = 1500 ;) cpy #15 bne Award1500Points ldy #$00 ;Award 10 ninja stars (10*1 = 10) AwardNinjaStars jsr AddStars iny cpy #10 bne AwardNinjaStars rts ;Small subroutine to award the player 10 ninja stars. AddStars inc Stars+1 ;Increment the second digit for Stars lda Stars+1 ;Then check if one byte is past $09. cmp #$0a bne StarsNotOver ;If not ... Then all is OK. lda #$00 ;Otherwise reset the second digit byte sta Stars+1 inc Stars ;Increment the first digit byte for stars lda Stars ;... and do the same check again. cmp #$0a ;If over $09 ... bne StarsNotOver ;Else not over lda #$09 ;Set 99 as MAX no of stars. (Stars, Stars+1 = 9) sta Stars sta Stars+1 StarsNotOver rts ;A double IRQ interrupt is used in order to mask ;the invisible sprites over the panel. ;The first IRQ will draw the actual object ;sprite data to the hardware sprite frame ;at the top raster. irq inc $d019 lda $dc0d sta $dd0d lda #$ce sta $d012 lda ObjectSpriteFrame sta $07f8 lda ObjectSpriteFrame+1 sta $07f9 lda ObjectSpriteFrame+2 sta $07fa lda ObjectSpriteFrame+3 sta $07fb lda ObjectSpriteFrame+4 sta $07fc lda ObjectSpriteFrame+5 sta $07fd lda ObjectSpriteFrame+6 sta $07fe lda ObjectSpriteFrame+7 sta $07ff lda #$01 sta GameSync jsr PalNTSCMusicCheck ;Subroutine to PAL/NTSC check to match music player ldx #irq2 ;for the second raster position. stx $0314 sty $0315 jmp $ea7e ;The second IRQ will make sprites invisible, as they ;fall through the bottom area of the screen - avoiding ;being seen over the score panel. irq2 inc $d019 lda #$fa sta $d012 ;Mask a blank sprite in order to make a clean ;disappearance for the player or an enemy. lda #$9c ;Value of blank sprite set, to fake sprites sta $07f8 ;disappearing underneath the top raster into the sta $07f9 ;bottom raster. sta $07fa sta $07fb sta $07fc sta $07fd sta $07fe sta $07ff ldx #irq ;piece both rasters together. stx $0314 sty $0315 jmp $ea7e ;Check for PAL/NTSC music check so ;music is timed to play correctly PalNTSCMusicCheck lda $02a6 ;C64 Machine type? (0 = NTSC, 1 = PAL) cmp #$01 beq PAL inc NTSC_Timer ;Set NTSC timer. Every 6th frame = NTSC player lda NTSC_Timer cmp #6 beq ResetNTSCTimer PAL MusicPlayer ;Play music jsr GameMusicPlay rts ResetNTSCTimer lda #$00 sta NTSC_Timer rts ;======================= ;END SCREEN code here ;======================= ;The game has been completed. Switch off all interrupts ;(As a final interrupt is going to be called). GameComplete sei ldx #$31 ldy #$ea stx $0314 sty $0315 lda #$0b sta $d011 lda #$81 sta $dc0d sta $dd0d lda #$18 sta $d016 lda #$1c ;Read charset at $3000 sta $d018 lda #$00 ;Border + background black / disable sprites ldx #$09 ;Multicolour 1 brown ldy #$01 ;Multicolour 2 white sta $d020 sta $d021 sta $d015 stx $d022 sty $d023 ;Clear screen - Avoiding overwriting status panel, since ;a double split is going to be used for the raster. ldx #$00 ClearEntireScreen2 lda #79 sta $0400,x sta $0500,x sta $0600,x lda #$0b ;Multicolour cyan sta $d800,x sta $d900,x sta $da00,x inx bne ClearEntireScreen2 ldx #$00 ClearEntireScreen3 lda #79 sta $0700,x lda #$0f sta $db00,x inx cpx #$20 bne ClearEntireScreen3 ;Setup the game completion text ldx #$00 PutGameCompleteText lda GameCompleteText,x sta $04c8,x lda GameCompleteText+40,x sta $04c8+80,x lda GameCompleteText+80,x sta $04c8+120,x lda GameCompleteText+120,x sta $04c8+160,x lda GameCompleteText+160,x sta $04c8+240,x lda GameCompleteText+200,x sta $04c8+320,x inx cpx #40 bne PutGameCompleteText ;Setup the multi-raster IRQ interrupt for the end sequence ;there is no need to re-initialise the music as the in game ;music is still going to be played on the ending screen. ldx #EndIRQ stx $0314 sty $0315 lda #$36 sta $d012 lda #$7f sta $dc0d sta $dd0d lda #$1b sta $d011 lda #$01 sta $d01a cli jmp CheckHiScore ;Check hiscore. ;The end IRQ for the game (Set the same as the ;game IRQ). EndIRQ inc $d019 lda $dc0d sta $dd0d lda #$ce ;Set position of raster split sta $d012 lda #$00 ;Set background colour to black sta $d021 lda #$09 ;Set multicolour #1 to brown sta $d022 lda #$01 ;Set multicolour #2 to white sta $d023 lda #$1c ;Title screen text/logo charset position $3000-$37ff sta $d018 jsr PalNTSCMusicCheck ;Call subroutine to check PAL/NTSC music player ldx #EndIRQ2 stx $0314 sty $0315 jmp $ea7e ;The second IRQ is EXACTLY the same as the bottom interrupt ;in the game IRQ - except that we don't need sprites this time ;round. EndIRQ2 inc $d019 lda #$fa ;Set raster to very bottom position sta $d012 lda #$09 ;Set status background colour to brown sta $d021 lda #$00 ;Set status multicolour #1 to black sta $d022 lda #$01 ;Set status multicolour #2 to white sta $d023 lda #$12 ;Game charset (which masks status panel) sta $d018 ldx #EndIRQ stx $0314 sty $0315 jmp $ea7e ;==================== ;Main TITLESCREEN ;code here ;==================== TitleScreen ;Kill all existing interrupts, refresh the ;firebutton control sei ldx #$31 ldy #$ea stx $0314 sty $0315 lda #$81 sta $dc0d sta $dd0d lda #$0b sta $d011 lda #$00 sta FireButton sta $d019 sta $d01a ldx #$00 BlankScreen lda #79 ;Pick character to fill the screen sta $0400,x sta $0500,x sta $0600,x sta $06e8,x lda #$0b;Fill with grey/multicolour cyan sta $d800,x sta $d900,x sta $da00,x sta $dae8,x inx bne BlankScreen ;Draw the status panel, and mask score/hi like ;before ldx #$00 ReDrawStatus lda GameMatrix+$0320,x sta $0720,x inx cpx #$c8 ;That will be enough for the panel bne ReDrawStatus ;Set status panel attribs to screen ldx #$00 SetPanelAttribs ldy $0720,x lda GameAttribs,y sta $db20,x inx cpx #$c8 bne SetPanelAttribs ;Now draw the title screen logo. The logo consists ;of 10 rows. ldx #$00 DrawLogo lda LogoMatrix,x sta $0400,x inx bne DrawLogo ldx #$00 DrawLogo2 lda LogoMatrix+$100,x sta $0500,x inx cpx #$90 bne DrawLogo2 ;Setup the logo's colour attributes, so that the ;colour data is in the correct place. ldx #$00 SetLogoAttribs ldy $0400,x lda LogoColours,y sta $d800,x inx bne SetLogoAttribs ldx #$00 SetLogoAttribs2 ldy $0500,x lda LogoColours,y sta $d900,x inx cpx #$90 bne SetLogoAttribs2 ;Place the credits on to the screen ldx #$00 SetCredits lda TitleCreditsText,x sta $05b8,x lda TitleCreditsText+40,x sta $05b8+80,x lda TitleCreditsText+80,x sta $05b8+120,x lda TitleCreditsText+120,x sta $05b8+200,x lda #$0d ;Multicolour green sta $d9b8,x lda #$0b ;Multicolour cyan sta $d9b8+80,x lda #$0f ;Multicolour yellow sta $d9b8+120,x lda #$0a ;Multicolour red sta $d9b8+200,x lda #$0c ;Multicolour purple sta $d9b8+280,x inx cpx #$28 bne SetCredits ;Force title music into the music player ldx #TitleMusicPlay stx MusicPlayer+1 sty MusicPlayer+2 lda #$10 ;Init smooth scroll position sta XPos lda #ScrollText sta MessRead+2 ;Mask the previous score/hiscore jsr MaskStatus ;Setup / Initialise interrupts ldx #TitleIRQ stx $0314 sty $0315 lda #$32 sta $d012 lda #$7f sta $dc0d lda #$1b sta $d011 lda #$01 sta $d01a lda #$00 jsr TitleMusicInit cli ;Main title loop, similar to the game loop except ;for the subroutines only correspond to the title screen TitleLoop jsr SyncTimer jsr ScrollMessage lda $dc00 lsr lsr lsr lsr lsr bit FireButton ror FireButton bmi TitleLoop bvc TitleLoop lda #0 sta FireButton jmp GameStart ;Title screen scroll text subroutine. This will ;produce a smooth scrolling 1x1 text. ScrollMessage lda XPos sec sbc #XScrollSpeed and #$07 sta XPos bcs EndScroll ldx #$00 ShiftChar lda $06d1,x sta $06d0,x inx cpx #39 ;Total no of chars to shift. bne ShiftChar MessRead lda ScrollText ;Read scroll text ... cmp #$00 ;Until byte 0 is read to reset the scroll bne StoreChar lda #ScrollText ;Then reset the scroll read code. sta MessRead+2 jmp MessRead StoreChar sta $06f7 ;Char position for storing a character inc MessRead+1 ;Shift low byte to next address to read bne EndScroll ;next character inc MessRead+2 ;Shift hi byte to next $100 address to read ;next character EndScroll rts ;Main title screen interrupts TitleIRQ ;Interrupt 1 - Static screen ... / Credits ;Title screen charset inc $d019 lda $dc0d sta $dd0d lda #$be ;Top split position sta $d012 lda #$00 ;Black screen colour sta $d021 lda #$09 ;Brown multicolour 1 sta $d022 lda #$01 ;White multicolour 2 sta $d023 lda #$1c ;Title screen charset sta $d018 lda #$18 ;Screen multicolour (STILL) sta $d016 lda #$01 sta GameSync ;Synchronize timer mode jsr PalNTSCMusicCheck ;Also music test ldx #TitleIRQ2 stx $0314 sty $0315 jmp $ea7e ;TitleIRQ 2 - The smooth scrolling message TitleIRQ2 inc $d019 lda #$ce ;Second raster split position sta $d012 lda XPos ;Custom smooth scroller zeropage ora #$10 ;Set to multicolour mode. sta $d016 ldx #TitleIRQ3 stx $0314 sty $0315 jmp $ea7e ;TITLEIRQ 3 - The status panel ;Change screen colour, multicolours and ;also charset TitleIRQ3 inc $d019 lda #$fa ;Next raster position sta $d012 lda #$18 ;Screen multicolour mode on STILL sta $d016 lda #$09 sta $d021 lda #$00 ;Black Multicolour #1 sta $d022 lda #$12 ;Game charset sta $d018 ldx #TitleIRQ stx $0314 sty $0315 jmp $ea7e ;==================== ;Game pointers + tables ;-------------------- FireButton !byte 0 ;Prevent firing whilst button is held GameSync !byte 0 ;Synchronized timer NTSC_Timer !byte 0 ;NTSC Music playing delay ;Char Animation Delay CharAnimDelay !byte 0 ;Sprite Animation pointers AnimDelay1 !byte 0 ;Delay for 1 or 2 frame animation AnimDelay2 !byte 0 ;Delay for 3 frame animation AnimPointer1 !byte 0 ;For all 1 or 2 framed animation AnimPointer2 !byte 0 ;For all 3 framed animation AnimPointer3 !byte 0 ;For explosion animation PlayerJumping !byte 0 ;Player is jumping PlayerFalling !byte 0 ;Player falling JumpSource !byte 0 ;Current position to jump from JumpDest !byte 0 ;Target position to jump to FallSource !byte 0 ;Current position to fall from FallDest !byte 0 ;Current position to fall to PlayerDead !byte 0 ;Only occurs when the player is hit PlayerDir !byte 0 ;Direction the player faces before throwing stars Enemy1Dead !byte 0 ;Only if enemy is dead Enemy2Dead !byte 0 ; " Enemy3Dead !byte 0 ; " Enemy4Dead !byte 0 ; " Enemy5Dead !byte 0 ; " Enemy6Dead !byte 0 ; ;Self-Modifying pointers for Spawning/Producing ;new enemy objects, colour ets. NewEnemyColour !byte 0 ;Pointer to setup a new colour for the next enemy NewEnemyDelay !byte 0 ;Pointer to setup a new enemy delay/speed NewFloorPosition !byte 0 ;Pointer to setup new floor start position for the enemy SMFrameLow !byte 0 ;SelfMod Lo-byte of animation frame address stored SMFrameHi !byte 0 ;SelfMod Hi-byte of animation frame address stored SMHitsToKill !byte 0 ;SelfMod amount of hits to kill enemy stored SMScoreValue !byte 0 ;SelfMod scoring value per play SMDirection !byte 0 ;SelfMod enemy direction stored SMStartXPosition !byte 0 ;SelfMod enemy start position (X) stored ;Bullet pointers BulletDir !byte 0 ;Corresponds to direction player faces 0 = left, 1 = right ;Tables for virtual sprite X/Y position for all 8 sprites ;Notes: Any enemy object can become a chest at occasions. ;The idea is for the player to survive during game play. Obj_Position !byte $00,$00 ;Player 1 !byte $00,$00 ;Player 1 bullet !byte $00,$00 ;Enemy Object 1 !byte $00,$00 ;Enemy Object 2 !byte $00,$00 ;Enemy Object 3 !byte $00,$00 ;Enemy Object 4 !byte $00,$00 ;Enemy Object 5 !byte $00,$00 ;Enemy Object 6 ;Collision table ;Player Bullet ;xx xx yy yy xx xx yy yy Collision !byte $00,$00,$00,$00,$00,$00,$00,$00 ;Default sprite frames and colour, sprite start position ;table. ObjectStartTable !byte $82,$86,$8f,$89,$92,$9a !byte $99,$8c ObjectStartColour !byte $06,$01,$08,$04,$06,$02 !byte $05,$07 StartPositionTable !byte $50,$8f ;Areas (Notes... $b7, $8f, $67 !byte $00,$00 !byte $40,$00 !byte $60,$00 !byte $20,$00 !byte $40,$00 !byte $60,$00 !byte $80,$00 ObjectSpriteFrame !byte $9c,$9c,$9c,$9c,$9c,$9c,$9c,$9c ;Blank sprite frame ;Sprite animated object pointers (Where the frames are ;stored to the specific objects) OBJ_Player_Left !byte $80 ;Object for player facing left OBJ_Player_Right !byte $82 ;Object for player facing right OBJ_Player_Up !byte $85 ;Object for player jumping up OBJ_Player_Down !byte $84 ;Object for player jumping down OBJ_Bullet !byte $86 ;Object for ninja stars OBJ_Player_Dead !byte $88 ;Object for player death/fall OBJ_Chest !byte $8f ;Object for treasure chest OBJ_Enemy1_Left Samurai_Left !byte $89 ;Object for samurai facing left OBJ_Enemy1_Right Samurai_Right !byte $8c ;Object for samurai facing right OBJ_Enemy2_Left Ninja2_Left !byte $92 ;Object for ninja black left OBJ_Enemy2_Right Ninja2_Right !byte $90 ;Object for ninja black right OBJ_Enemy3_Left Ninja1_Left !byte $99 ;Object for ninja trainee left OBJ_Enemy3_Right Ninja1_Right !byte $9b ;Object for ninja trainee right OBJ_Explosion !byte $9c ;Object for explosion ;Enemy spawn pointers - Only one for each setup is needed here ;) ;since only one loop will be called to generate a random object, ;lo/hi byte selfmod frame pointer, colour, direction and movment speed SpawnPointer !byte 0 ;When selecting each enemy this is used SelectPointer !byte 0 ;Selected object type pointer EnemyTypeStoreLo !byte 0 ;Lo-byte of object animation frame stored EnemyTypeStoreHi !byte 0 ;Hi-byte of object animation frame stored EnemyColourStore !byte 0 ;Selected colour for new object spawned EnemyDirStore !byte 0 ;Direction, depending on selected frame EnemyLivesStore !byte 0 ;Amount hits to kill to store for next enemy EnemyScoreStore !byte 0 ;Amount of points x100 per enemy killed EnemyDelayStore !byte 0 ;Delayed speed of enemy movement ;Player Shield pointers PlayerShieldEnabled !byte 0 ;Protection enabled/disabled PlayerShieldTime !byte 0 ;Pointer to represent player's protection ;Player death condition pointers PlayerToss !byte 0 ;Player being thrown off the floor enabled/disabled PlayerFall !byte 0 ;Player plummeting out of the screen after tossing enabled/disabled ;Object orientated directional control (Enemy6 = Chest) Enemy1Dir !byte 0 Enemy2Dir !byte 1 Enemy3Dir !byte 0 Enemy4Dir !byte 1 Enemy5Dir !byte 0 ;Enemy speed control - Depending on the direction an enemy ;is moving. A random speed table is read to store the object Enemy1Speed !byte 1 Enemy2Speed !byte 2 Enemy3Speed !byte 1 Enemy4Speed !byte 2 Enemy5Speed !byte 1 ;Enemy delay counter Enemy1Delay !byte 0 Enemy2Delay !byte 0 Enemy3Delay !byte 0 Enemy4Delay !byte 0 Enemy5Delay !byte 0 ;Enemy speed delay limit Enemy1DelayLimit !byte 2 Enemy2DelayLimit !byte 2 Enemy3DelayLimit !byte 2 Enemy4DelayLimit !byte 2 Enemy5DelayLimit !byte 2 ;Amount of lives for an enemy before it gets killed Enemy1Lives !byte 1 Enemy2Lives !byte 1 Enemy3Lives !byte 1 Enemy4Lives !byte 1 Enemy5Lives !byte 1 ;Scoring per enemy Enemy1Score !byte 0 Enemy2Score !byte 0 Enemy3Score !byte 0 Enemy4Score !byte 0 Enemy5Score !byte 0 ;Explosion delay / pointer for making a chest appear on ;screen (Which will allow the player to pick up ninja ;stars) ExplodeDelay !byte 0 ExplodePointer !byte 0 ;Pointers for chest waiting ChestWaitDelay !byte 0 ChestWaitTime !byte 0,0 ChestCanSpawn !byte 0 ChestPositionPointer !byte 0 ChestCollisionEnabled !byte 0 ;NEW ENEMY SPAWN TABLE ;This is just a series of numbers, which is randomly selected ;from the SpawnPointer, indicating which object should be stored ;The numbers are as follows: ;1 - Trainee Ninja (coloured suit) - 1 hit to kill, 100 points - Moving Left ;2 - Trainee Ninja (coloured suit) - 1 hit to kill, 100 points - Moving Right ;3 - Trained Ninja (black suit) - 2 hits to kill, 200 points - Moving Left ;4 - Trained Ninja (black suit) - 2 hits to kill, 200 points - Moving Right ;5 - Samurai Guard (Coloured gown) - 3 hits to kill, 500 points - Moving left ;6 - Samurai Guard (Coloured gown) - 3 hits to kill, 500 points - Moving right ;Make this progressive 256 bytes SpawnTable !byte 1,1,2,1,2,1,2,1,2,2,1,2,1,2,1,2 !byte 2,1,2,3,2,1,1,2,2,1,2,1,2,2,1,2 !byte 3,1,2,4,1,2,3,2,1,4,2,2,1,2,1,4 !byte 4,3,1,2,1,4,3,2,1,3,2,1,4,1,2,3 !byte 4,2,1,4,2,3,1,4,3,1,4,1,4,2,3,3 !byte 4,2,1,3,2,1,4,5,2,1,4,3,1,2,6,2 !byte 4,1,3,2,1,4,1,2,4,3,1,2,4,3,1,3 !byte 4,1,5,3,5,2,3,3,4,4,1,1,6,2,1,4 !byte 3,1,1,3,5,4,1,2,4,3,2,6,4,1,3,1 !byte 4,3,1,3,5,2,5,4,3,6,4,6,2,5,4,1 !byte 5,1,6,3,1,2,6,2,1,4,3,1,2,3,1,5 !byte 6,1,2,2,1,3,2,1,4,1,3,1,6,3,4,5 !byte 1,3,1,5,4,3,1,2,2,5,6,1,2,2,4,4 !byte 3,5,1,6,4,3,5,1,2,5,4,3,1,6,2,4 !byte 5,3,4,6,6,5,5,3,4,3,6,5,5,6,5,4 !byte 3,1,3,3,4,3,1,2,3,4,4,3,4,4,3,5 ;Next, a selection of colours that can be picked from table ;(256 bytes again) EnemyColourTable !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 !byte 2,4,6,11,14,5,8,12,2,4,6,11,14,5,8,12 ;Next, a selection of delay counters that can be picked from ;the the table (256 bytes again). EnemyDelayTable !byte 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 !byte 2,2,2,2,1,2,1,2,1,1,1,1,1,1,1,2 !byte 2,1,1,1,2,1,1,1,2,1,1,1,2,1,1,1 !byte 1,1,2,1,1,1,2,1,1,2,1,1,2,1,1,2 !byte 2,1,1,1,1,2,1,1,1,1,2,1,1,0,1,2 !byte 2,0,1,2,1,0,2,1,1,0,2,0,1,2,1,1 !byte 0,1,1,2,1,1,0,2,1,0,1,0,1,0,2,1 !byte 1,1,2,1,0,1,1,0,1,2,0,1,2,1,1,1 !byte 1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0 !byte 1,1,0,1,1,0,1,1,0,1,1,1,1,0,1,1 !byte 1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0 !byte 0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1 !byte 0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1 !byte 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0 !byte 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0 !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;Enemy vertical starting position table (256 bytes again) ;This can also occur with chest object as well. ;$67 = Floor_Top ;$87 = Floor_Mid ;$bf = Floor_Btm EnemyStartTable !byte Floor_Top,Floor_Btm,Floor_Mid,Floor_Top,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid !byte Floor_Btm,Floor_Mid,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Top,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Btm !byte Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid,Floor_Top,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Mid,Floor_Btm,Floor_Top,Floor_Top,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid !byte Floor_Btm,Floor_Top,Floor_Mid,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Mid,Floor_Top !byte Floor_Top,Floor_Btm,Floor_Mid,Floor_Top,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid !byte Floor_Btm,Floor_Mid,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Top,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Btm !byte Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid,Floor_Top,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Mid,Floor_Btm,Floor_Top,Floor_Top,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid !byte Floor_Btm,Floor_Top,Floor_Mid,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Mid,Floor_Top !byte Floor_Top,Floor_Btm,Floor_Mid,Floor_Top,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid !byte Floor_Btm,Floor_Mid,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Top,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Btm !byte Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid,Floor_Top,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Mid,Floor_Btm,Floor_Top,Floor_Top,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid !byte Floor_Btm,Floor_Top,Floor_Mid,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Mid,Floor_Top !byte Floor_Top,Floor_Btm,Floor_Mid,Floor_Top,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid !byte Floor_Btm,Floor_Mid,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Top,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Btm !byte Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid,Floor_Top,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Mid,Floor_Btm,Floor_Top,Floor_Top,Floor_Mid,Floor_Btm,Floor_Top,Floor_Mid !byte Floor_Btm,Floor_Top,Floor_Mid,Floor_Mid,Floor_Top,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Top,Floor_Btm,Floor_Btm,Floor_Mid,Floor_Btm,Floor_Mid,Floor_Mid,Floor_Top ;Series of tables to represent the X position of where the chest ;should be placed after it has spawned. ChestStartTable !byte Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid !byte Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft !byte Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft !byte Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid !byte Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Lft,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft !byte Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Mid,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Mid !byte Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid !byte Chest_Mid,Chest_Rgt,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft !byte Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Mid,Chest_Rgt !byte Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft !byte Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid !byte Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft !byte Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt !byte Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Mid,Chest_Rgt !byte Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Lft,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft !byte Chest_Mid,Chest_Lft,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Mid,Chest_Rgt,Chest_Lft,Chest_Rgt,Chest_Mid,Chest_Lft ;ENEMY FRAME TABLE (LOWBYTE/HI BYTE). Since there are only ;6 different enemy object types in the game. Set the correct ;LO/HI byte table for each enemy type (Excluding byte 0) EnemyTypeTableLo !byte 0 ;Skip enemy frame here since the pointer ;SelectPointer won't read from 0 !byte Ninja1_Left ;Trainee Ninja LEFT (Hi-Byte) !byte >Ninja1_Right ;Trainee Ninja RIGHT (Hi-Byte) !byte >Ninja2_Left ;Black Ninja LEFT (Hi-Byte) !byte >Ninja2_Right ;Black Ninja RIGHT (Hi-Byte) !byte >Samurai_Left ;Samurai Guard LEFT (Hi-Byte) !byte >Samurai_Right;Samurai Guard RIGHT (Hi-Byte) ;Enemy Hits to Kill table (Sets the amount of lives an enemy ;must have before killed) EnemyKillTable !byte 0 ;Skip 0 since SelectPointer starts from 1 - 6 !byte 1 ;Trainee Ninja Left = 1 hit !byte 1 ;Trainee Ninja Right = 1 hit !byte 2 ;Black Ninja Left = 2 hits !byte 2 ;Black Ninja Right = 2 hits !byte 3 ;Samurai Guard Left = 3 hits !byte 3 ;Samurai Guard Right = 3 hits ;Enemy Scoring (x100) per game EnemyScoreTable !byte 0 ;Skip 0 since SelectPointer starts from 1 - 6 !byte 1 ;Trainee Ninja Left = 100 pts (1x100) !byte 1 ;Trainee Ninja Right = 100 !byte 2 ;Black Ninja Left = 200 pts (2x100) !byte 2 ;Black Ninja Right = 200 !byte 3 ;Samurai Guard Left = 500 pts (5x100) !byte 3 ;Samurai Guard Right = 500 ;Enemy X-Start position EnemyStartXTable !byte 0 !byte $b2 ;Trainee Ninja Left !byte $00 ;Trainee Ninja Right !byte $b2 ;Black Ninja Left !byte $00 ;Black Ninja Right !byte $b2 ;Samurai Guard Left !byte $00 ;Samurai Guard Right ;Enemy direction table EnemyDirTable !byte 0 ;Skip pointer 0 !byte 0 ;Trainee Ninja Left !byte 1 ;Trainee Ninja Right !byte 0 ;Black Ninja Left !byte 1 ;Black Ninja Right !byte 0 ;Samurai Guard Left !byte 1 ;Samurai Guard Right ;Sprite Animation frames SPR_Player_Left !byte $80,$81 SPR_Player_Right !byte $82,$83 SPR_Player_Down !byte $84 SPR_Player_Up !byte $85 SPR_Player_Bullet !byte $86,$87 SPR_Player_Dead !byte $88 SPR_Chest !byte $8f,$9b SPR_Enemy1_Left !byte $89,$8a,$8b,$8a SPR_Enemy1_Right !byte $8c,$8d,$8e,$8d SPR_Enemy2_Right !byte $90,$91 SPR_Enemy2_Left !byte $92,$93 SPR_Enemy3_Right !byte $97,$98 SPR_Enemy3_Left !byte $99,$9a SPR_Explosion !byte $94,$95,$96,$95,$94 SPR_ChestObject !byte $9b,$8f SPR_ExplosionEnd ;Score, lives and stars Score !byte $00,$00,$00,$00,$00,$00 HiScore !byte $00,$00,$00,$00,$00,$00 Lives !byte $03 Stars !byte $03,$00 ;Game Over Text GameOverChars GameOverLine1 !byte 97,98,99,100 ;Custom chars representing GAME GameOverLine2 !byte 102,103,104,105 ;Custom chars representing OVER !ct scr ;Game Complete Text GameCompleteText !text " congratulations " !text "the master chow mein, was very impressed" !text "with your ninja training skills, he has " !text "decided to set you free from his temple!" !text "you are a true ninja. " !text " - press fire to continue - " TitleCreditsText !text " (c)2018 the new dimension " !text "programming and music .. richard bayliss" !text "graphics ............... alf yngve " !text " - press fire to start - " ScrollText !text "... the new dimension " !text "and alf yngve are very proud to present to " !text "you - rogue ninja - ... you are the rogue ninja, " !text "who has been captured and thrown into the temple of the master, " !text "chow mein ... he sets you a challenge of skill, physical endurance " !text "and survival ... you are to fight a series of chow meins' " !text "army of ninja trainees, black ninjas and samurai guards ... " !text "scores and hits to kill vary according to the enemy type ... " !text "you will be given 50 ninja stars at the start of the game, and " !text "also temporary protection for a few seconds or so ... if a ninja " !text "captures you while you are invulnerable, it will stop and wait " !text "unless you walk away or jump to another floor from it ... if " !text "you are caught, you will be thrown out ... there is help at " !text "hand ... at times chests will appear on screen ... if you " !text "pick up a chest, you will gain an additional 10 stars and 1500 points ... " !text "if you survive your penultimate test, you will be set free ... we " !text "will have to wait and see ... just plug a joystick into port 2 " !text "then press the fire button to start the challenge ... " !text " " !byte 0 ;==================== ;Game Code End ;==================== ;Import in game music (,,2 prefix for prg) *=$9000 !bin "bin/gamemusic.prg",,2