/********************************* - A M A T E U R W A R S - ********************************** AKA Nick's First Attempt To Code a NY "game... Started: 24/5/98 (13:36) v1.0: 6/7/98 (21:55) Finished: ??? NB: This is *NOT* my GDUK entry :-) WRITTEN FOR PAL TVs (revenge! revenge!) ---------------------------------- Nick Ferguson, Edinburgh 6 July, 1998 nickf@saqnet.co.uk http://www.netyaroze-europe.com/~rookie1 http://www.saqnet.co.uk/users/nickf http://www.n64gazetta.com ---------------------------------- TIMELINE: 24/5/98 Initial goal - 2 sprites moving about the screen in response to input from both pads... (DID IT - 23:30)!!! 25/5/98 Goal - set up collision detection between the two sprites and improve boundaries... (DID IT - 11:30 29/5/98)!!! 29/5/98 Goal - add a pretty backdrop (a sprite!) and change the player sprites to something a bit cooler. (DID IT at last - 16:30 31/5/98)!!! 1/6/98 Goal - add some animation to the ships. Ideas: Gunfire? Rockets? Direction changes - rotation vs animation? (ADDED ship rotation for turns - 25/6/98) (BIG 3 WEEK BREAK DUE TO UNIVERSITY WORK) 25/6/98 Goal - add bullets/laser fire to ships. (and try to remember how Yaroze works after 3 weeks...) (KIND OF DID IT eventually - 1/7/98) 2/7/98 Goal - rewriting program to be a bit more sensible, coherent and generally more moral. (MUCHO IMPROVED 3/7/98) 3/7/98 Goal - get lasers firing properly (ie not taking up huge amounts of space in memory). (WAHEY! LASERS AHOY 4/7/98) 4/7/98 Goal - add transparent energy bars to the top of the screen for each player sprite, work out the collision detection to apply to the lasers on the ships and *maybe* even get the lasers to decrement the counter...(whew!) (DONE IT! 5/7/98) 5/7/98 Goal - add a scores counter and make a loop so that there is a Game Over screen and the program can be looped...generally tidy it up and get v1.0 FINISHED!!! (DONE IT! 6/7/98) Goals for (much?) later: add explosion animations to ships... add decent homedone font for menu/announcements? add titlescreen? transparent lasers? add laser recharge bar? Or "superlaser" - to be fired only once per round? KNOWN BUGS: A single laser usually fires when a new round begins. Presumably this is a hang-up from the last round, but if everything is reset (which I *think* it is) then how does it happen??? THANKS TO: Ira Rainey & Scott Campbell for their sprite tutorials and code John Ward for his understandable-if-uncommented "Spacies" source James Shaugnessy for the brill Gravitation Source and SCEE and Net Yaroze for estranging my girlfriend ;-/ SPECIAL THANKS TO MR FROSTY FOR HIS BUG-HUNTING EXPERTISE (!), PEEKS AT THE SBF SOURCE, AND FCONTROL v1.5 CONTROLLER LIBRARY! (Visit the omni-talented Mr F at the URL below:) http://www.netyaroze-europe.com/~mrfrosty *********************************/ /* HEADER FILES */ #include // Hooray for LIBPS! #include "fcont1_5.h" // fcontrol v1.5 (from Mr Frosty) #define SPRITE_CNT (MAX_LASERS*2+5) // sprite count #define SCREEN_W 320 // setup screen stuff. Dur. #define SCREEN_H 240 #define player1Add 0x80090000 // memory address of player sprites #define player2Add 0x80092000 #define laser1Add 0x800a7000 // memory add. of laser sprites #define laser2Add 0x800a9000 #define BGAdd 0x80094000 // memory add. of background #define ROTATE_UP (0) // Rotation definitions #define ROTATE_DOWN (0-ONE*180) #define ROTATE_LEFT (0-ONE*90) #define ROTATE_RIGHT (0+ONE*90) #define OT_LENGTH 2 //Ordering Table length /*GAME CONTROL DEFINITIONS */ // You can play about with these if you want :-) #define PLAYERSPEED 2 // controls the speed of the player ships (default=2) #define LASERSPEED 5 // controls the speed of lasers 'n stuff (default=5) #define LASER_GAP 10 // this is the gap between each laser fired (default=10) #define MAX_LASERS 20 // the max number of lasers "fireable" (is that a word?) at a time (default=20) #define PLAYER_LIFE 80 // the length of players' energy bars (default=80) #define LASER_DAMAGE 5 // the damage done to energy bars by laser damage (default=5) // I wouldn't change these... #define TOP_WALL 13 // collision boundaries for the "arena" #define BOTTOM_WALL 225 #define LEFT_WALL 15 #define RIGHT_WALL 305 /* PROTOTYPE FUNCTIONS */ void InitialiseGraphics(void); // I-N-I-T-I-A-L-I-S-E.... G-R-A-P-H-I-C-S. Oh! void InitialiseOTs(void); // You can guess what this does, too. void FSetPortBuffers(void); // Setup pad reading routine (Courtesy of Mr Frosty) void InitialisePlayers(void); // Sets up those SEXY player sprites and THAT funky BG... void InitialiseLasers(void); // Set up the lasers (hidden under the ships) void InitialiseLife(void); // Set up players' energy bars void ResetMovement(void); // solves a "drift" problem/bug... (maybe an option later?) void GameOverCheck(void); // If game is over, prompts for and then restarts void ReadPads(void); // Reads. The. Pads. Using FCONTROL v1.5 (1.6 now available!!!) void MoveShips(void); // Moves. The. Ships. (Good names, huh?) void RotateShips(void); // Actually, this DOESN'T rotate the ships. // Oh, OK then, I'm lying. It does. void MoveLasers(void); // Hey, it's a secret :-) void CheckLaserCollision(void); // Try typing that in a hurry (checks laser collision) int CheckLasers1(int num); // Checks for laser collision between P1 lasers and P2 int CheckLasers2(int num); // Checks for laser collision between P2 lasers and P1 void CountGap(void); // Decrements the L1(or 2)_GAP each turn. Keeps lasers from // flying as a long line/solid mass! Doesn't really need to // be a seperate function, but I like my cheesy functions... void PrintScores(void); // Prints da scores // Complex (ahem) collision detection functions, using the old "bounding box" method... int CheckCollision(long P1x_minus, long P1y_minus, long P1x_plus, long P1y_plus, long P2x_minus, long P2y_minus, long P2x_plus, long P2y_plus); int Check(); /* GLOBALS 'N STUFF */ GsOT WorldOT[2]; GsOT_TAG OTTags[2][1<attribute = (1<<24); sp->x = player1.x; sp->y = player1.y; sp->w = (timData.pw*4); sp->h = (timData.ph); sp->tpage = GetTPage(0,0,timData.px,timData.py); sp->u = 0; sp->v = 0; sp->cx = timData.cx; sp->cy = timData.cy; sp->r = 128; sp->g = 128; sp->b = 128; sp->mx = sp->w/2; sp->my = sp->h/2; sp->scalex = ONE; sp->scaley = ONE; sp->rotate = player1.rotate; } // Setup LASER2 GsGetTimInfo((u_long*)(laser2Add+4),&timData); rect.x=timData.px; rect.y=timData.py; rect.w=timData.pw; rect.h=timData.ph; LoadImage(&rect, timData.pixel); rect.x=timData.cx; rect.y=timData.cy; rect.w=timData.cw; rect.h=timData.ch; LoadImage(&rect, timData.clut); for(sp=laser2,i=0;iattribute = (1<<24); sp->x = player2.x; sp->y = player2.y; sp->w = (timData.pw*4); sp->h = (timData.ph); sp->tpage = GetTPage(0,0,timData.px,timData.py); sp->u = 0; sp->v = 0; sp->cx = timData.cx; sp->cy = timData.cy; sp->r = 128; sp->g = 128; sp->b = 128; sp->mx = sp->w/2; sp->my = sp->h/2; sp->scalex = ONE; sp->scaley = ONE; sp->rotate = player2.rotate; } DrawSync(0); } void InitialiseLife() { P1_LIFE.attribute = (1<<30); P1_LIFE.x = 10; P1_LIFE.y = 10; P1_LIFE.w = PLAYER_LIFE; P1_LIFE.h = 10; P1_LIFE.r = 0; P1_LIFE.g = 180; P1_LIFE.b = 20; P2_LIFE.attribute = (1<<30); P2_LIFE.x = 310-PLAYER_LIFE; P2_LIFE.y = 10; P2_LIFE.w = PLAYER_LIFE; P2_LIFE.h = 10; P2_LIFE.r = 0; P2_LIFE.g = 180; P2_LIFE.b = 20; } /*********************** MAIN FUNCTIONS ***********************/ void ReadPads() //reads both pad inputs, and defines control variables as true or false depending... { SPAD1 = FSPadRead(PORT_ONE); SPAD2 = FSPadRead(PORT_TWO); if(SPAD1) { if(SPAD1 & SPADleft) P1_left=1; if(!(SPAD1 & SPADleft)) P1_left=0; if(SPAD1 & SPADright) P1_right=1; else P1_right=0; if(SPAD1 & SPADup) P1_up=1; else P1_up=0; if(SPAD1 & SPADdown) P1_down=1; else P1_down=0; if(SPAD1 & SPADsq) P1_hold=1; else P1_hold=0; if(SPAD1 & SPADci) P1_rotate=1; else P1_rotate=0; if(SPAD1 & SPADcr) P1_fire=1; else P1_fire=0; if(SPAD1 & SPADstart) P1_start=1; else P1_start=0; } if(SPAD2) { if(SPAD2 & SPADleft) P2_left=1; else P2_left=0; if(SPAD2 & SPADright) P2_right=1; else P2_right=0; if(SPAD2 & SPADup) P2_up=1; else P2_up=0; if(SPAD2 & SPADdown) P2_down=1; else P2_down=0; if(SPAD2 & SPADsq) P2_hold=1; else P2_hold=0; if(SPAD2 & SPADci) P2_rotate=1; else P2_rotate=0; if(SPAD2 & SPADcr) P2_fire=1; else P2_fire=0; if(SPAD2 & SPADstart) P2_start=1; else P2_start=0; } } void MoveShips() { int player_x = player1.x; // Backup original X Y stuff just in case... int player_y = player1.y; if(P1_up) player1.y-= PLAYERSPEED; if(player1.y < TOP_WALL) player1.y = TOP_WALL; if(P1_down) player1.y+= PLAYERSPEED; if(player1.y > BOTTOM_WALL) player1.y = BOTTOM_WALL; if(P1_left) player1.x-= PLAYERSPEED; if(player1.x < LEFT_WALL) player1.x = LEFT_WALL; if(P1_right) player1.x+= PLAYERSPEED; if(player1.x > RIGHT_WALL) player1.x = RIGHT_WALL; if((Check())||(P1_rotate)) // move back to original position if a collision with player2 occurs // or if the "rotate" button is held down. { player1.x = player_x; player1.y = player_y; } // SAME THING FOR PLAYER 2, basically. player_x = player2.x; player_y = player2.y; if(P2_up!=0) player2.y-= PLAYERSPEED; if(player2.y < TOP_WALL) player2.y = TOP_WALL; if(P2_down!=0) player2.y+= PLAYERSPEED; if(player2.y > BOTTOM_WALL) player2.y = BOTTOM_WALL; if(P2_left!=0) player2.x-= PLAYERSPEED; if(player2.x < LEFT_WALL) player2.x = LEFT_WALL; if(P2_right!=0) player2.x+= PLAYERSPEED; if(player2.x > RIGHT_WALL) player2.x = RIGHT_WALL; if((Check())||(P2_rotate)) { player2.x = player_x; player2.y = player_y; } } void RotateShips() { // rotate Player 1 using EZ-understand DEFINEs if((P1_up) && (!P1_hold)) player1.rotate = ROTATE_UP; if((P1_down) && (!P1_hold)) player1.rotate = ROTATE_DOWN; if((P1_left) && (!P1_hold)) player1.rotate = ROTATE_LEFT; if((P1_right) && (!P1_hold)) player1.rotate = ROTATE_RIGHT; // rotate Player 2 as above if((P2_up) && (!P2_hold)) player2.rotate = ROTATE_UP; if((P2_down) && (!P2_hold)) player2.rotate = ROTATE_DOWN; if((P2_left) && (!P2_hold)) player2.rotate = ROTATE_LEFT; if((P2_right) && (!P2_hold)) player2.rotate = ROTATE_RIGHT; } void MoveLasers() // "Home" lasers are kept under the player ship, "Fired" lasers // travel on their own in the direction they were fired { int i=0; if((P1_fire) && (L1_GAP==0)) // i.e if P1 fire is pressed and the laser gap is OK { i=0; while(L1_LAUNCHED[i]) i++; // skims through array to find next "home" laser L1_LAUNCHED[i]=1; // sets the laser as "fired" L1_GAP = LASER_GAP; // sets the laser gap to maximum } for(i=0;i= 250) { laser1[i].x = player1.x; laser1[i].y = player1.y; laser1[i].rotate = player1.rotate; L1_LAUNCHED[i] = 0; } } if(laser1[i].rotate==ROTATE_LEFT) // same sort of thing as above { laser1[i].x -= LASERSPEED; if(laser1[i].x <= -10) { laser1[i].x = player1.x; laser1[i].y = player1.y; laser1[i].rotate = player1.rotate; L1_LAUNCHED[i] = 0; } } if(laser1[i].rotate==ROTATE_RIGHT) // same sort of thing as above { laser1[i].x += LASERSPEED; if(laser1[i].x >= 330) { laser1[i].x = player1.x; laser1[i].y = player1.y; laser1[i].rotate = player1.rotate; L1_LAUNCHED[i] = 0; } } } else // if L1_LAUNCHED is 0 ie laser is "Home" // it is kept under the ship { laser1[i].x = player1.x; laser1[i].y = player1.y; laser1[i].rotate = player1.rotate; } } // EXACTLY THE SAME THING AS ABOVE GOES FOR PLAYER 2 if((P2_fire) && (L2_GAP==0)) { i=0; while(L2_LAUNCHED[i]) i++; L2_LAUNCHED[i]=1; L2_GAP = LASER_GAP; } for(i=0;i= 250) { laser2[i].x = player2.x; laser2[i].y = player2.y; laser2[i].rotate = player2.rotate; L2_LAUNCHED[i] = 0; } } if(laser2[i].rotate==ROTATE_LEFT) { laser2[i].x -= LASERSPEED; if(laser2[i].x <= -10) { laser2[i].x = player2.x; laser2[i].y = player2.y; laser2[i].rotate = player2.rotate; L2_LAUNCHED[i] = 0; } } if(laser2[i].rotate==ROTATE_RIGHT) { laser2[i].x += LASERSPEED; if(laser2[i].x >= 330) { laser2[i].x = player2.x; laser2[i].y = player2.y; laser2[i].rotate = player2.rotate; L2_LAUNCHED[i] = 0; } } } else { laser2[i].x = player2.x; laser2[i].y = player2.y; laser2[i].rotate = player2.rotate; } } } void CountGap() { L1_GAP--; //reduce laser gap distance by 1 (needs to be 0 for next laser) if(L1_GAP<0) L1_GAP=0; L2_GAP--; //reduce laser gap distance by 1 (needs to be 0 for next laser) if(L2_GAP<0) L2_GAP=0; } // Bounding Box method ahoy! int CheckCollision(long P1x_minus, long P1y_minus, long P1x_plus, long P1y_plus, long P2x_minus, long P2y_minus, long P2x_plus, long P2y_plus) { if((P2x_minus <= P1x_plus) & (P2x_plus >= P1x_minus) & (P2y_minus <= P1y_plus) & (P2y_plus >= P1y_minus)) return(1); else return(0); } // for checking the inter-player collision... int Check() { return(CheckCollision((player1.x)-((player1.w/2)-3), (player1.y)-((player1.h/2)-3), (player1.x)+((player1.w/2)-3), (player1.y)+((player1.h/2)-3), (player2.x)-((player2.w/2)-3), (player2.y)-((player2.h/2)-3), (player2.x)+((player2.w/2)-3), (player2.y)+((player2.h/2)-3) )); } void ResetMovement() //solves drifting weirdness (remove this function from main() to see what I mean) { P1_up = 0; P1_down = 0; P1_left = 0; P1_right = 0; P1_fire = 0; P1_start = 0; P2_up = 0; P2_down = 0; P2_left = 0; P2_right = 0; P2_fire = 0; P1_start = 0; } void CheckLaserCollision() { int i; for(i=0;i PLAYER_LIFE) P2_LIFE.w = 0; laser1[i].x = player1.x; laser1[i].y = player1.y; laser1[i].rotate = player1.rotate; L1_LAUNCHED[i] = 0; } if(CheckLasers2(i)) //if P2's laser hits P1 { P1_LIFE.w -= LASER_DAMAGE; if(P1_LIFE.w > PLAYER_LIFE) P1_LIFE.w = 0; laser2[i].x = player2.x; laser2[i].y = player2.y; laser2[i].rotate = player2.rotate; L2_LAUNCHED[i] = 0; } } } int CheckLasers1(int num) { return(CheckCollision(laser1[num].x-((laser1[num].w/2)-2), laser1[num].y-((laser1[num].h/2)-4), laser1[num].x+((laser1[num].w/2)-2), laser1[num].y+((laser1[num].h/2)-4), player2.x-((player2.w/2)-3), player2.y-((player2.h/2)-3), player2.x+((player2.w/2)-3), player2.y+((player2.h/2)-3) )); } int CheckLasers2(int num) { return(CheckCollision(laser2[num].x-((laser2[num].w/2)-2), laser2[num].y-((laser2[num].h/2)-4), laser2[num].x+((laser2[num].w/2)-2), laser2[num].y+((laser2[num].h/2)-4), player1.x-((player1.w/2)-3), player1.y-((player1.h/2)-3), player1.x+((player1.w/2)-3), player1.y+((player1.h/2)-3) )); } void GameOverCheck() //handles Game Over stuff { while(!((P1_start)||(P2_start))) //pause until P1 or P2 hits start { ReadPads(); } GAMEOVER=0; // the game is no longer "over" InitialisePlayers(); //re-initialise sprites, boxes and movement. InitialiseLife(); InitialiseLasers(); ResetMovement(); L1_GAP = LASER_GAP; L2_GAP = LASER_GAP; } void PrintScores() { FntLoad(960, 256); FntOpen( 15, 227, 320, 256, 0, 1024); FntPrint("P1 SCORE: %d P2 SCORE: %d",(P1_SCORE/2),(P2_SCORE/2)); //easier just to print on one long line // Note that the P1_SCORE etc must be divided by two. This is because the scores are incremented by one // TWICE after a win (ie they go up by 2). This is fixable in a more elegant way, but I'm getting tired :-) FntFlush(-1); } /**************************************** I HOPE YOU LEARNT SOMETHING FROM THIS! (KEEP UP THE GOOD WORK...) NICK F WILL RETURN WITH - C H E M I C A L W A R F A R E - (It's not big. It's not clever. Just fun.) (DUE SEPTEMBER 1998) *****************************************/