// Author = Jason Spreadborough // Format = Playstation // Date = 8-11-98 // version= 1.0 // // Description: // Clone of a popular game, recognise it?? // Simple game where you have to pop the balls/bubbles, when // popped the ball attempts to split into two. If the ball // is to small it just dissappears. Said it was simple. // // Romantic Novel: // // Just kidding, don't read this bit if you get bored easy. // // I guess if you looking at this then you either want to steal // something or you want to improve the code or maybe add some // features. If so Im very honoured, thank you. Anyway a few // suggestions, you might want to add a count down thing, say // to give the player 60 seconds to complete the level or something. // Different behaviour to the balls could be interesting, how about // adding creatures that try to steal the balls ? // // I hope you find this code straight forward, I mean there are only // a few twists and turns in here! If you do change anything please // tell me so I can see what you've done. // INCLUDES //---------- #include #include #include "lib2d.h" #include "pad.h" #include "addrs.h" #include "sound.h" #include "levels.h" #include "hiscore.h" // DEFINES //--------- // Loop Flags #define TITLE1 (0) #define TITLE2 (1) #define GAME_TOUR (2) #define GAME_RUSH (3) #define HISCORES (4) #define QUIT (9) // Screen Z prioritys #define PLAYER_PRIORITY (50) #define BULLET_PRIORITY (40) #define BEAM_PRIORITY (60) #define POWERUP_PRIORITY (20) #define HUD_PRIORITY (10) // constraints #define MAX_BULLETS (20) // bullet types #define SINGLE_BEAM (0) #define DOUBLE_BEAM (1) #define HOOK_BEAM (2) #define DOUBLE_HOOK_BEAM (3) #define DOUBLE_GUN (4) // player states #define DEAD (0) #define DYING (1) #define ALIVE (2) #define LEFT (0) #define RIGHT (1) #define MIDDLE (2) #define COMPLETE (3) /////////////////////////////////////////////////////////////////// // STRUCTS / UNION //----------------- // Generic bullet structure. typedef struct { u_long state; // FLAG is the bullet active long counter; // How long is bullet active for GsSPRITE sprite; GsBOXF beamSprite; RECT beamBox; // Bounding box for trailing beam RECT box; // bounding box long vx,vy; // its velocity u_long type; // what type of bullet is it. u_long beam; // does bullet have beam. } BULLET; // Generic player structure. typedef struct{ u_long state; // players state u_long cheat; // FLAG is cheat active. u_long fired; // FLAG has player just fired the gun. u_long first; // FLAG is this the first shot. used for double weapons u_long second; // FLAG is this the second shot. long counter; // counter. when to allow the next fire of bullet u_long anim; // which animation to use. long animCounter; // counter befor change of animation frame long animThreshold; // number that counter has to reach. long animFrame; // which animation frame to display. GsSPRITE sprite; RECT box; // bounding box long velocity; // velocity (only left or right) long score; // its score DOH! long lives; // how many lives left BULLET bullets[MAX_BULLETS]; // array to hold the players active bullets u_long powerUp; // which powerup does the player have. } PLAYER; ///////////////////////////////////////////////////////////// // PROTOTYPES //------------ void DrawBackground(); void ResetGame(); void MainReset(); void ResetGameRush(); void SetupTitles(); void RenderWorldTitle1(); void UpdateWorldTitle1(); u_long ProcessUserInputTitle1(); void RenderWorldTitle2(); void UpdateWorldTitle2(); u_long ProcessUserInputTitle2(); void RenderWorldGame(); void UpdateWorldGame(); void UpdateWorldGameRush(); u_long ProcessUserInputGame(); void UpdatePowerups(); void DrawPowerups(); void SetupHUD(); void DrawHUD(); void DrawCollisionBoxes(); void UpdatePlayer(PLAYER *player); void DrawPlayer(PLAYER *player); void SetupPlayer(PLAYER *player); u_long UpdateWorldPlayerDying(PLAYER *player); void SetupBullets(PLAYER *player); void FireBullet(PLAYER *player,long direction); void UpdateBullets(); void DrawBullets(); void DestroyBullet(PLAYER *player,long index); long ProcessUserInputHiscore(); /////////////////////////////////////////////////////////////// // GLOBALS //--------- PLAYER player1; // The player data u_long showCollision; // Show all bounding boxes (DEBUG) u_long debug; // Display debug information (DEBUG) u_long playerCollision; // Is there a player collision (DEBUG) u_long collision; // Is there a collision (DEBUG) long FontID; // ID to tell which font area to print to long HUD; // Heads Up Display. font area for display // lives, level so on. Sad name I know ?? u_long GameState; // FLAG. Which part of game are we in. (e.g title) u_long choice; // FLAG. Which game is user choosing. u_long buttonPressed; // FLAG. are any buttons pressed ? u_long level; // current level the player has reached. GsSPRITE titleMain; // title sprite GsSPRITE titleOptions; // options sprite GsBOXF highLight; // highlight selected option GsSPRITE titleTour; // Tour option sprite GsSPRITE titleRush; // Rush option sprite GsSPRITE hud1; // first half of hud display GsSPRITE hud2; // second half GsSPRITE livesLeft[3]; // lives left sprite GsSPRITE currentPower; // hud powerup sprite GsSPRITE power; GsBOXF powerSquare; // power up on screen sprite GsSPRITE frames[60]; // border sprites u_long powerStarted=FALSE; // has powerup started to fall u_long displayAll=FALSE; // display options on title screen voice_data twango; // sounds voice_data animal; voice_data beam; voice_data cart; voice_data cling; voice_data door; voice_data fall; voice_data pause; voice_data pop; voice_data select; voice_data shot; voice_data begin; GsSPRITE background1; // Image used for the background GsSPRITE background2; ////////////////////////////////////////////////////////////////////// // /\/\/\/\/\/\/ FUNCTIONS /\/\/\/\/\/\/ //-------------------------------------- // ** MISCELANIOUS ** //------------------- void LoadBackground() { SetSpriteInfo(&background1,BACK1_TIM,80,120); SetSpriteInfo(&background2,BACK2_TIM,239,120); } void DrawBackground() { DrawSprite(&background1,151); DrawSprite(&background2,150); } // This function resets everything. void MainReset() { player1.cheat = FALSE; showCollision = FALSE; debug = FALSE; playerCollision = FALSE; collision = FALSE; player1.lives = 3; player1.score = 0; player1.powerUp = SINGLE_BEAM; level = 1; } // This functions just makes sure the player // starts RUSH mode with a double beam weapon. // And make sure level is ready. void ResetGameRush() { u_long index; powerStarted = FALSE; player1.powerUp = DOUBLE_BEAM; ballsInGame = 0; bricksInGame = 0; player1.state = ALIVE; player1.sprite.x = 160; player1.velocity = 0; player1.fired = FALSE; player1.first = TRUE; player1.second = TRUE; player1.counter = 0; for(index=0; index < MAX_BULLETS; index++) { DestroyBullet(&player1,index); BitSet(&player1.bullets[index].beamSprite.attribute,1<<30); SetLineRGB(&player1.bullets[index].beamSprite,255,0,0); } SetupBricksBlank(); } // Resets important varibles to their default values void ResetGame() { u_long index; ballsInGame = 0; bricksInGame = 0; SetupLevel(level); powerStarted = FALSE; player1.state = ALIVE; player1.sprite.x = 160; player1.velocity = 0; player1.fired = FALSE; player1.first = TRUE; player1.second = TRUE; player1.counter = 0; for(index=0; index < MAX_BULLETS; index++) { DestroyBullet(&player1,index); BitSet(&player1.bullets[index].beamSprite.attribute,1<<30); SetLineRGB(&player1.bullets[index].beamSprite,255,0,0); } } // General function that sets up a 'RECT'. //void SetBoxInfo(RECT *rect, long x, long y, long w, long h) //{ // rect->x = x; // rect->y = y; // rect->w = w; // rect->h = h; //} // ** FUNCTIONS RELEVANT TO THE TITLE SCREENS ** // --------------------------------------------- void SetupTitles() { SetSpriteInfo(&titleMain,TITLE_TIM,140,80); SetSpriteInfo(&titleOptions,OPTIONS_TIM,140,180); SetLineRGB(&highLight,0,240,225); SetSpriteInfo(&titleRush,RUSH_TIM,200,120); SetSpriteInfo(&titleTour,TOUR_TIM,90,120); } // Draw 1st title screen void RenderWorldTitle1() { RenderPrepare(); DrawSprite(&titleMain,100); //if(displayAll==TRUE) //{ //FntPrint(" 1 = start\n 2 = quit\n"); //FntPrint(" choice = %d ",choice); DrawSprite(&titleOptions,50); DrawBox(&highLight,80); //} FntFlush(-1); RenderFinish(); } // Updates 1st title. After a counter period the options // are displayed. Just makes it look at little better? void UpdateWorldTitle1() { static long counter=0; highLight.x = 90; highLight.w = 100; highLight.h = 40; //if(displayAll==FALSE) //{ if(++counter > 100) { counter=0; displayAll=TRUE; PlaySFX(&twango); } //} //else //{ // set box position. if(choice==1) highLight.y = 135; else if(choice == 2) highLight.y = 180; //} } // u_long ProcessUserInputTitle1() { static long lastChoice=1; static long counter=0; // create PAD variable to hold states of both joypads u_long PAD = PadRead(); if(!buttonPressed) { if (PAD& PADLup) choice = 1; if (PAD& PADLdown)choice = 2; if (PAD& PADselect) { if(choice == 1) choice = 2; else choice = 1; buttonPressed = TRUE; counter = 0; } if(choice != lastChoice) { PlaySFX(&select); lastChoice = choice; } if (PAD& PADstart) { PlaySFX(&begin); buttonPressed = TRUE; counter = 0; if(choice == 1) return TITLE2; if(choice == 2) return QUIT; } } else if(!(PAD& PADselect) && !(PAD& PADstart)) buttonPressed = FALSE; if(++counter > 400) { counter = 0; return(HISCORES); } else return(TITLE1); } long ProcessUserInputHiscore() { // create PAD variable to hold states of both joypads u_long PAD = PadRead(); if(!buttonPressed) { if (PAD& PADstart) { PlaySFX(&begin); buttonPressed = TRUE; return (1); } } else if(!(PAD& PADselect) && !(PAD& PADstart)) buttonPressed = FALSE; return(0); } // void RenderWorldTitle2() { RenderPrepare(); DrawSprite(&titleTour,50); DrawSprite(&titleRush,50); DrawBox(&highLight,80); FntPrint("\n\n\n\n\n\n\tTOUR - RUSH"); FntFlush(-1); RenderFinish(); } // void UpdateWorldTitle2() { highLight.y = 70; highLight.w = 110; highLight.h = 110; // set box position. if(choice==1) highLight.x = 35; else if(choice == 2) highLight.x = 145; } // u_long ProcessUserInputTitle2() { static lastChoice = 1; // create PAD variable to hold states of both joypads u_long PAD = PadRead(); if(!buttonPressed) { if (PAD& PADLleft) choice = 1; if (PAD& PADLright) choice = 2; if (PAD& PADselect) { if(choice == 1) choice = 2; else choice = 1; buttonPressed = TRUE; } if(choice != lastChoice) { PlaySFX(&select); lastChoice = choice; } if (PAD& PADstart) { PlaySFX(&begin); buttonPressed = TRUE; if(choice == 1) return GAME_TOUR; if(choice == 2) return GAME_RUSH; } } else if(!(PAD& PADselect) && !(PAD& PADstart)) buttonPressed = FALSE; return(TITLE2); } // ** GAME FUNCTIONS ** //---------------------- // void RenderWorldGame() { RenderPrepare(); DrawBalls(); DrawBricks(); DrawPlayer(&player1); DrawBullets(); DrawPowerups(); DrawHUD(); if(debug) { if(showCollision) DrawCollisionBoxes(); if(player1.cheat) FntPrint(" \n Invincible \n"); FntPrint(" Fired = %d \n",player1.fired); FntPrint(" Counter = %d \n",player1.counter); FntPrint(" PowerUp = %d \n",player1.powerUp); FntPrint(" Level = %d \n",level); FntPrint(" Lives = %d \n", player1.lives); FntPrint("1 Score = %d",player1.score); FntPrint(" \n%d/320\n ",VerticalSync); } FntFlush(-1); DrawBackground(); RenderFinish(); } // void RenderWorldGamePaused() { static GsBOXF fade; static firstRUN = TRUE; if(firstRUN == TRUE) { SetBoxPos(&fade,0,0,320,240); BitSet(&fade.attribute,1<<30); SetLineRGB(&fade,0,0,0); firstRUN = FALSE; } RenderPrepare(); DrawBox(&fade,0); DrawBalls(); DrawBricks(); DrawPlayer(&player1); DrawBullets(); DrawPowerups(); DrawHUD(); DrawBackground(); FntPrint(FontID,"Game Paused\n-----------\n\n"); FntPrint(FontID,"Square quits\nCircle continues\n"); FntPrint(FontID,"\nTriangle DEBUG!\n"); FntFlush(FontID); RenderFinish(); } // void UpdateWorldGame() { static u_long animationRunning = TRUE; static long localAnimCounter =0; // if there are any balls on the screen level // must still be playing. if(ballsInGame) { UpdateBalls(); UpdatePlayer(&player1); UpdateBullets(); UpdatePowerups(); } else { //Do end of level animation. if(animationRunning) { if(localAnimCounter == 1) PlaySFX(&animal); if(++player1.animCounter > player1.animThreshold) { player1.animCounter = 0; if(++player1.animFrame > 3 ) player1.animFrame = 0; } SetSpriteAnim(&player1.sprite,COMPLETE,player1.animFrame,25,25); if(++localAnimCounter >= 200) animationRunning = FALSE; } else { // move to next level. if(++level > 10) level =1; ResetGame(); animationRunning = TRUE; localAnimCounter = 0; } } }; // Updates the world when playing in RUSH game mode. void UpdateWorldGameRush() { static long timer = 0; // once the timer is reset add a random ball. if(timer == 0) { AddBall(rand()%260+30,80,rand()%3+1,rand()%4); } // The usual updates UpdateBalls(); UpdatePlayer(&player1); UpdateBullets(); // Once player score is at certain amount increase the level. if(player1.score > ((level*10000)+(level*5000))) { level++; } // if timer reachers certain amount reset. Note that this // value is affected by the level. if(++timer > (300-(level*2))) timer = 0; //FntPrint("timer %d",timer); } // Process user input ! u_long ProcessUserInputGame() { u_long exit = FALSE; // create PAD variable to hold states of both joypads u_long PAD = PadRead(); if(PAD& PADLleft) player1.velocity =-2; if(PAD& PADLright)player1.velocity = 2; if(!buttonPressed) { if(!player1.fired) { if(PAD& PADsquare) { FireBullet(&player1,NW); buttonPressed = player1.fired = TRUE; } else if(PAD& PADtriangle) { FireBullet(&player1,N); buttonPressed = player1.fired = TRUE; } else if(PAD& PADcircle) { FireBullet(&player1,NE); buttonPressed = player1.fired = TRUE; } else if(PAD& PADcross) { FireBullet(&player1, N); buttonPressed = player1.fired = TRUE; } } // if debug is active allow for extra input including // cheat stuff like skip level and invincibilty. if(debug) { if(PAD& PADL1) showCollision = TRUE; if(PAD& PADL2) showCollision = FALSE; if(PAD& PADR1) player1.cheat = TRUE; if(PAD& PADR2) player1.cheat = FALSE; if(PAD& PADselect) { buttonPressed = TRUE; if(++player1.powerUp > 4) player1.powerUp = 0; } } // Pause if pad1 has start pressed if (PAD& PADstart) { buttonPressed = TRUE; PlaySFX(&pause); while(!exit) { PAD = PadRead(); if(PAD& PADcircle) exit = TRUE; if(PAD& PADsquare) return(TITLE1); if(PAD& PADtriangle) debug = TRUE; RenderWorldGamePaused(); } } } else if(!(PAD& PADselect) && !(PAD& PADstart) && !(PAD& PADsquare) && !(PAD& PADtriangle) && !(PAD& PADcircle) && !(PAD& PADcross)) { buttonPressed = FALSE; } return(GameState); } // This is just used for debugging collisions void DrawCollisionBoxes() { static GsBOXF left[MAX_BRICKS]; static GsBOXF right[MAX_BRICKS]; static GsBOXF top[MAX_BRICKS]; static GsBOXF bottom[MAX_BRICKS]; static GsBOXF ball[MAX_BALLS]; static GsBOXF bullplay[MAX_BULLETS]; static GsBOXF player; static long index; for(index=0; index < bricksInGame; index++) { left[index].x = bricks[index].left.x; left[index].y = bricks[index].left.y; left[index].w = bricks[index].left.w; left[index].h = bricks[index].left.h; SetLineRGB(&left[index],0,0,255); BitSet(&left[index].attribute,1<<30); right[index].x = bricks[index].right.x; right[index].y = bricks[index].right.y; right[index].w = bricks[index].right.w; right[index].h = bricks[index].right.h; SetLineRGB(&right[index],0,255,0); BitSet(&right[index].attribute,1<<30); top[index].x = bricks[index].top.x; top[index].y = bricks[index].top.y; top[index].w = bricks[index].top.w; top[index].h = bricks[index].top.h; SetLineRGB(&top[index],255,0,0); BitSet(&top[index].attribute,1<<30); bottom[index].x = bricks[index].bottom.x; bottom[index].y = bricks[index].bottom.y; bottom[index].w = bricks[index].bottom.w; bottom[index].h = bricks[index].bottom.h; SetLineRGB(&bottom[index],255,255,0); BitSet(&bottom[index].attribute,1<<30); DrawBox(&left[index],0); DrawBox(&right[index],0); DrawBox(&top[index],0); DrawBox(&bottom[index],0); } for(index =0; index < ballsInGame; index++) { ball[index].x = balls[index].box.x; ball[index].y = balls[index].box.y; ball[index].w = balls[index].box.w; ball[index].h = balls[index].box.h; SetLineRGB(&ball[index],0,255,255); BitSet(&ball[index].attribute,1<<30); DrawBox(&ball[index],0); } for(index = 0; index < MAX_BULLETS; index++) { bullplay[index].x = player1.bullets[index].box.x; bullplay[index].y = player1.bullets[index].box.y; bullplay[index].w = player1.bullets[index].box.w; bullplay[index].h = player1.bullets[index].box.h; SetLineRGB(&bullplay[index],255,0,255); BitSet(&bullplay[index].attribute,1<<30); DrawBox(&bullplay[index],0); } player.x = player1.box.x; player.y = player1.box.y; player.w = player1.box.w; player.h = player1.box.h; SetLineRGB(&player,255,0,255); BitSet(&player.attribute,1<<30); DrawBox(&player,0); if(collision) FntPrint("COLLISION !!!!!!!!!\n"); if(playerCollision) FntPrint("PLAYER COLLISION\n"); } // ** PLAYER SPECIFIC FUNCTIONS ** //--------------------------------- // set player varibles to initial settings void SetupPlayer(PLAYER *player) { SetSpriteInfo(&player->sprite,PLAYER_TIM,120,217); player->anim = MIDDLE; player->animCounter = 0; player->animThreshold = 10; player->animFrame = 0; player->box.y = 211; player->box.w = 10; player->box.h = 16; } // updates player by animate it and moving using its velocity // varible. Collision with the balls is also taken care of here. void UpdatePlayer(PLAYER *player) { playerCollision = FALSE; player->sprite.x += player->velocity; if(player->sprite.x < 30) player->sprite.x = 31; else if(player->sprite.x > 290) player->sprite.x = 289; player->box.x = player->sprite.x-5; if(player->counter > 0) player->counter--; if(player->counter <= 0) player->fired = FALSE; // animate player if(++player->animCounter > player->animThreshold) { player->animCounter = 0; if(++player->animFrame > 3 ) player->animFrame = 0; } if(player->velocity == 0) { player->anim = MIDDLE; player->animFrame = 0; } if(player->velocity < 0) player->anim = LEFT; if(player->velocity > 0) player->anim = RIGHT; SetSpriteAnim(&player->sprite,player->anim,player->animFrame,25,25); player->velocity = 0; // check for player collisions with the balls if(CollisionBalls(player->box)) { playerCollision = TRUE; if(player->cheat == FALSE) { player->state = DYING; PlaySFX(&cart); } } } u_long UpdateWorldPlayerDying(PLAYER *player) { static long counter=0; if(++counter > 99) { player->lives --; player->state = DEAD; counter = 0; } if(player->lives < 0) { if(GoodScore(player->score)) { EnterNewScore(player->score); return HISCORES; } else return(TITLE1); } return(GameState); } // void DrawPlayer(PLAYER *player) { DrawSprite(&player->sprite,PLAYER_PRIORITY); } // void SetupBullets(PLAYER *player) { u_long index; for(index=0; index < MAX_BULLETS; index++) { SetSpriteInfo(&player->bullets[index].sprite,BULLET_TIM,0,0); player->bullets[index].state = 0; player->bullets[index].box.w = 5; player->bullets[index].box.h = 5; } } // void FireBullet(PLAYER *player,long direction) { u_long index; for(index=0; index < MAX_BULLETS; index++) { if(player->bullets[index].state == 0) { switch(player->powerUp) { case SINGLE_BEAM: case DOUBLE_BEAM: case HOOK_BEAM: case DOUBLE_HOOK_BEAM: { if(player->first == TRUE) { player->bullets[index].sprite.x = player->sprite.x; player->bullets[index].sprite.y = player->sprite.y; player->bullets[index].state = 1; player->bullets[index].type = player->powerUp; player->bullets[index].beam = TRUE; player->bullets[index].counter = 2; player->counter = 0; player->bullets[index].vx = 0; player->bullets[index].vy =-2; player->first = FALSE; PlaySFX(&beam); } // Allow for two shot guns else if((player->powerUp == DOUBLE_BEAM) || (player->powerUp == DOUBLE_HOOK_BEAM)) { if(player->second == TRUE) { player->bullets[index].sprite.x = player->sprite.x; player->bullets[index].sprite.y = player->sprite.y; player->bullets[index].state = 1; player->bullets[index].type = player->powerUp; player->bullets[index].beam = TRUE; player->bullets[index].counter = 2; player->counter = 0; player->bullets[index].vx = 0; player->bullets[index].vy =-2; player->second = FALSE; PlaySFX(&beam); } } } break; case DOUBLE_GUN: { PlaySFX(&shot); player->bullets[index].sprite.x = player->sprite.x; player->bullets[index].sprite.y = player->sprite.y; player->bullets[index].state = 1; player->bullets[index].type = player->powerUp; player->bullets[index].counter = 200; player->counter = 30; switch(direction) { case N: player->bullets[index].vx = 0; player->bullets[index].vy =-3; break; case NE: player->bullets[index].vx = 3; player->bullets[index].vy =-3; break; case NW: player->bullets[index].vx =-3; player->bullets[index].vy =-3; break; } } break; } index = MAX_BULLETS; // break out of loop } } } // void UpdateBullets() { u_long index; long splitThis; for(index=0; index < MAX_BULLETS;index++) { if(player1.bullets[index].state == 1) { switch(player1.bullets[index].type) { case HOOK_BEAM: case DOUBLE_HOOK_BEAM: { if(!CollisionBottom(player1.bullets[index].box)) { player1.bullets[index].sprite.x += player1.bullets[index].vx; player1.bullets[index].sprite.y += player1.bullets[index].vy; player1.bullets[index].box.x = player1.bullets[index].sprite.x-2; player1.bullets[index].box.y = player1.bullets[index].sprite.y-2; player1.bullets[index].beamBox.x = player1.bullets[index].sprite.x-1; player1.bullets[index].beamBox.y = player1.bullets[index].sprite.y-1; player1.bullets[index].beamBox.h -= player1.bullets[index].vy; player1.bullets[index].beamSprite.x = player1.bullets[index].beamBox.x; player1.bullets[index].beamSprite.y = player1.bullets[index].beamBox.y; player1.bullets[index].beamSprite.h = player1.bullets[index].beamBox.h; // set Timer so the beam disappears after hooking on. player1.bullets[index].counter = 100; } else if(player1.bullets[index].counter >= 99) { PlaySFX(&cling); } } break; case SINGLE_BEAM: case DOUBLE_BEAM: { player1.bullets[index].sprite.x += player1.bullets[index].vx; player1.bullets[index].sprite.y += player1.bullets[index].vy; player1.bullets[index].box.x = player1.bullets[index].sprite.x-2; player1.bullets[index].box.y = player1.bullets[index].sprite.y-2; player1.bullets[index].beamBox.x = player1.bullets[index].sprite.x-1; player1.bullets[index].beamBox.y = player1.bullets[index].sprite.y-1; player1.bullets[index].beamBox.h -= player1.bullets[index].vy; player1.bullets[index].beamSprite.x = player1.bullets[index].beamBox.x; player1.bullets[index].beamSprite.y = player1.bullets[index].beamBox.y; player1.bullets[index].beamSprite.h = player1.bullets[index].beamBox.h; player1.bullets[index].counter = 2; if(CollisionBottom(player1.bullets[index].box)) { player1.bullets[index].counter = 0; // effectively destroys bullet //Play falling sound PlaySFX(&fall); } } break; case DOUBLE_GUN: { player1.bullets[index].sprite.x += player1.bullets[index].vx; player1.bullets[index].sprite.y += player1.bullets[index].vy; player1.bullets[index].box.x = player1.bullets[index].sprite.x-5; player1.bullets[index].box.y = player1.bullets[index].sprite.y-5; // Check for collisions with scenery/bricks. if(CollisionLeft(player1.bullets[index].box)) { if(player1.bullets[index].vx > 0) player1.bullets[index].vx = -player1.bullets[index].vx; } else if(CollisionRight(player1.bullets[index].box)) { if(player1.bullets[index].vx < 0) player1.bullets[index].vx = -player1.bullets[index].vx; } if(CollisionTop(player1.bullets[index].box)) { if(player1.bullets[index].vy > 0) player1.bullets[index].vy = -player1.bullets[index].vy; } else if(CollisionBottom(player1.bullets[index].box)) { if(player1.bullets[index].vy < 0) player1.bullets[index].vy = -player1.bullets[index].vy; } player1.bullets[index].box.x = player1.bullets[index].sprite.x-5; player1.bullets[index].box.y = player1.bullets[index].sprite.y-5; } break; } player1.bullets[index].counter--; // check for collisions with balls splitThis = CollisionBalls(player1.bullets[index].box); if(splitThis) { collision = TRUE; //Destroy this bullet. DestroyBullet(&player1,index); //Attempt to split the ball. player1.score += SplitBall(splitThis-1); // allow player to shoot again player1.counter = 0; if(player1.second == FALSE) player1.second = TRUE; else player1.first = TRUE; //Play pop sound PlaySFX(&pop); } else if(player1.bullets[index].beam == TRUE) { splitThis = CollisionBalls(player1.bullets[index].beamBox); if(splitThis) { collision = TRUE; //Destroy this bullet. DestroyBullet(&player1,index); //Attempt to split the ball. player1.score += SplitBall(splitThis-1); // take some points of to annoy player (haha) ;-) player1.score -= 15; // allow player to shoot again player1.counter = 0; if(player1.second == FALSE) player1.second = TRUE; else player1.first = TRUE; //Play pop sound PlaySFX(&pop); } } // if bullet is active for its amount of time then destroy it if(player1.bullets[index].counter < 1) { DestroyBullet(&player1,index); if(player1.second == FALSE) player1.second = TRUE; else player1.first = TRUE; } } } } //Destroy this bullet. void DestroyBullet(PLAYER *player,long index) { player->bullets[index].state = 0; player->bullets[index].box.x = -15; player->bullets[index].box.y = -15; player->bullets[index].vx= 0; player->bullets[index].vy= 0; player->bullets[index].sprite.x=-10; player->bullets[index].sprite.y=-10; player->bullets[index].beam = FALSE; player->bullets[index].beamBox.x=-15; player->bullets[index].beamBox.y=-15; player->bullets[index].beamBox.w= 3; player->bullets[index].beamBox.h= 10; player->bullets[index].beamSprite.x=-15; player->bullets[index].beamSprite.y=-15; player->bullets[index].beamSprite.w= 3; player->bullets[index].beamSprite.h= 10; } // void DrawBullets() { u_long index; for(index=0; index < MAX_BULLETS; index++) { if(player1.bullets[index].state == 1) { DrawSprite(&player1.bullets[index].sprite,BULLET_PRIORITY); if(player1.bullets[index].beam == TRUE) { DrawBox(&player1.bullets[index].beamSprite,BEAM_PRIORITY); } } } } // ** Display / HUD FUNCTIONS ** // ----------------------------- // Loads sprites for the information display bar. void SetupHUD() { u_long index,x=0,y=0; SetSpriteInfo(&hud1,HUD_TIM,80,20); SetSpriteInfo(&hud2,HUD2_TIM,240,20); SetSpriteInfo(&livesLeft[0],LIVES_TIM,260,20); SetSpriteInfo(&livesLeft[1],LIVES_TIM,280,20); SetSpriteInfo(&livesLeft[2],LIVES_TIM,300,20); SetSpriteInfo(¤tPower,POWERS_TIM,182,27); // corners of the frame SetSpriteInfo(&frames[0],FRAME4_TIM,16,46); SetSpriteInfo(&frames[1],FRAME3_TIM,16,234); SetSpriteInfo(&frames[2],FRAME2_TIM,304,46); SetSpriteInfo(&frames[3],FRAME_TIM,304,234); x=32; y=62; //edges of the frame for(index=4; index < 38; index+=2) { SetSpriteInfo(&frames[index],FRAME6_TIM,x,46); SetSpriteInfo(&frames[index+1],FRAME6_TIM,x,234); x+=16; } for(index=38; index < 60; index+=2) { SetSpriteInfo(&frames[index],FRAME5_TIM,16,y); SetSpriteInfo(&frames[index+1],FRAME5_TIM,304,y); y+=16; } // This is for the power up that appear when // the players score gets to a certain level // strange place to put it maybe?? SetSpriteInfo(&power,POWERS_TIM,160,0); SetLineRGB(&powerSquare,135,34,233); } // Displays player information such as scores and lives // as well as drawing a pretty border. void DrawHUD() { u_long index; SetSpriteAnim(¤tPower,0,player1.powerUp,20,20); // Draw display at top of screen DrawSprite(&hud1,HUD_PRIORITY); DrawSprite(&hud2,HUD_PRIORITY); DrawSprite(¤tPower,HUD_PRIORITY-1); for(index=0; index<60; index++) { DrawSprite(&frames[index],HUD_PRIORITY); } switch(player1.lives) { case 3: DrawSprite(&livesLeft[0],HUD_PRIORITY-1); case 2: DrawSprite(&livesLeft[1],HUD_PRIORITY-1); case 1: DrawSprite(&livesLeft[2],HUD_PRIORITY-1); case 0: break; } FntPrint("\n\n %d",player1.score); //FntPrint(FontID,"\t %d",level); } // ** POWER UP FUNCTIONS ** //------------------------- // void UpdatePowerups() { static long trigger=10000; static u_long powerType; static RECT powerBox; if(player1.score ==0) trigger = 10000; if(player1.score > trigger) { trigger += 10000; // start power up powerStarted = TRUE; powerType = rand()%3+1; power.y = -10; powerSquare.w = powerBox.w = 20; powerSquare.h = powerBox.h = 20; powerSquare.x = powerBox.x = 150; powerSquare.y = powerBox.y = -20; SetSpriteAnim(&power,0,powerType,20,20); } // animated power up if(powerStarted == TRUE) { if(++powerBox.y > 240) { powerStarted = FALSE; power.y = -10; powerSquare.y = powerBox.y =-20; } else { power.y++; powerSquare.y++; } } // has player collected power up if ((powerBox.x+powerBox.w) >= player1.box.x) if ((powerBox.y+powerBox.h) >= player1.box.y) if (powerBox.x <= (player1.box.x+player1.box.w)) if (powerBox.y <= (player1.box.y+player1.box.h)) { PlaySFX(&door); player1.powerUp = powerType; powerStarted = FALSE; power.y = -10; powerSquare.y = powerBox.y =-20; player1.score += 200; } } // void DrawPowerups() { if(powerStarted == TRUE) { DrawSprite(&power,POWERUP_PRIORITY); DrawBox(&powerSquare,POWERUP_PRIORITY+1); } } //////////////////////////////////////////////////////////////////////////// // /\/\/\/\/\/\/ MAIN \/\/\/\/\/\/\/\ //------------------------------------- void main() { u_long GameRunning = TRUE; // FLAG. Has game been exited // Initialisations //----------------- InitSound(); // Setup text stuff FntLoad(960, 256); FntOpen(10,10,240,320,0,512); FontID = FntOpen(80,100,140,50,0,100); //setup sounds InitSFX(&twango,vab_id,11,0,68,0,127,127); InitSFX(&animal,vab_id,0,0,68,0,127,127); InitSFX(&beam,vab_id,1,0,68,0,127,127); InitSFX(&cart,vab_id,2,0,68,0,127,127); InitSFX(&cling,vab_id,3,0,68,0,127,127); InitSFX(&door,vab_id,4,0,68,0,127,127); InitSFX(&fall,vab_id,5,0,68,0,127,127); InitSFX(&pause,vab_id,6,0,68,0,127,127); InitSFX(&pop,vab_id,7,0,68,0,127,127); InitSFX(&select,vab_id,8,0,68,0,127,127); InitSFX(&shot,vab_id,9,0,68,0,127,127); InitSFX(&begin,vab_id,10,0,68,0,127,127); PadInit(); LoadTextures(); LoadBackground(); Initialise2DGraphics(); GameState = TITLE1; // set game to title mode choice = 1; buttonPressed = FALSE; SetupTitles(); SetupHUD(); SetupHiScoreTable(); // Main Game Loop while(GameRunning == TRUE) { MainReset(); SetupBullets(&player1); SetupPlayer(&player1); displayAll = FALSE; choice = 1; while(GameState == TITLE1) { GameState = ProcessUserInputTitle1(); UpdateWorldTitle1(); RenderWorldTitle1(); } choice = 1; while(GameState == TITLE2) { GameState = ProcessUserInputTitle2(); UpdateWorldTitle2(); RenderWorldTitle2(); switch(GameState) { case GAME_TOUR: ResetGame(); break; case GAME_RUSH: ResetGameRush(); SetupBricksBlank(); break; default: break; } } while(GameState == GAME_TOUR) { switch(player1.state) { case ALIVE: GameState = ProcessUserInputGame(); UpdateWorldGame(); break; case DYING: GameState = UpdateWorldPlayerDying(&player1); break; case DEAD: ResetGame(); break; } RenderWorldGame(); } while(GameState == GAME_RUSH) { switch(player1.state) { case ALIVE: GameState = ProcessUserInputGame(); UpdateWorldGameRush(); break; case DYING: GameState = UpdateWorldPlayerDying(&player1); break; case DEAD: ResetGameRush(); break; } RenderWorldGame(); } while(GameState == HISCORES) { if(UpdateHiScoreDisplay()) { GameState = HISCORES; } else GameState = TITLE1; if(ProcessUserInputHiscore()) GameState = TITLE1; DrawHiScoreDisplay(); } if(GameState == QUIT) { GameRunning = FALSE; } } StopSound(); ResetGraph(0); }