// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» // .  º Gravity Force º // . . ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ  // // . by . // //  James Shaughnessy . // // (C) 1998 #include #include "pad.h" #include "gforce.h" main() { // Initialise working variables //u_char counter=0; // used to slow down ships' turning for 50Hz u_char incP1, incP2, r=0, g=0, b=0; u_char n; // These are permantly set pix.w = 1; pix.h = 1; tmp.w = 15; tmp.h = 15; // Must call once to init Graphics and some variable; VidModeChange(); // Must init once PadInit(); InitFrameBuffer(); MapLoad(1); // Load map 1 into virtual screen // TITLE SCREEN AND GAME SELECTION CODE/FUNCTION InitPlayers(2); // MAIN LOOP: uses break to exit while(1) { // Read in controller data from last vertical blank PadStatus = PadRead(); // Quit if both Select and Start pressed if (PadStatus & PAD1select && PadStatus & PAD1start) break; // Centre players on screen (MUST bound PXscr. straight after) if (player1.lives) { P1scr.x = player1.x + 224; if (player2.playing) P1scr.y = player1.y - 60; else P1scr.y = player1.y - 120; } if (player2.playing && player2.lives) { P2scr.x = player2.x + 224; P2scr.y = player2.y - 60; } // Bound screen offsets if (P1scr.x < 384) P1scr.x = 384; else if (P1scr.x > 704) P1scr.x = 704; if (P1scr.y < 0) P1scr.y = 0; else if (P1scr.y > maxyoffset) P1scr.y = maxyoffset; if (player2.playing) { if (P2scr.x < 384) P2scr.x = 384; else if (P2scr.x > 704) P2scr.x = 704; if (P2scr.y < 0) P2scr.y = 0; else if (P2scr.y > maxyoffset) P2scr.y = maxyoffset; } // If player2 is playing split screen, else full if (player2.playing) { P1scr.h = 120; P2scr.h = 120; maxyoffset = 391; } else { P1scr.h = 240; maxyoffset = 271; } // DEBUGGING & TESTING ******** * Allows in-game VidMode/PLAYERS change *** // normally initialized in VidModeChange() // or start of a game if (PadStatus & PAD1L1) MapLoad(1); if (PadStatus & PAD1L2) MapLoad(2); if (PadStatus & PAD1select) VidModeChange(); if (PadStatus & PAD1R1) player2.playing = FALSE; if (PadStatus & PAD1R2) player2.playing = TRUE; if (PadStatus & PAD1Rleft) printf("player1.x = %d, player1.y = %d, player1.lives = %d, player2.lives = %d\n", player1.x, player1.y, player1.lives, player2.lives); // ************************************************************************** // Test PADs and act on player variables (THRUST and TURN) if (player1.playing && player1.alive) { if (PadStatus & PAD1Lup || PadStatus & PAD1Rright) FireBullet(1); if (PadStatus & PAD1Rdown) player1.thrust = TRUE; else player1.thrust = FALSE; if (PadStatus & PAD1Lleft && !player1.onbase) player1.angle--; if (PadStatus & PAD1Lright && !player1.onbase) player1.angle++; if (player1.angle < 0) player1.angle +=36; else if (player1.angle > 35) player1.angle -=36; } if (player2.playing && player2.alive) { if (PadStatus & PAD2Lup || PadStatus & PAD2Rright) FireBullet(2); if (PadStatus & PAD2Rdown) player2.thrust = TRUE; else player2.thrust = FALSE; if (PadStatus & PAD2Lleft && !player2.onbase) player2.angle--; if (PadStatus & PAD2Lright && !player2.onbase) player2.angle++; if (player2.angle < 0) player2.angle +=36; else if (player2.angle > 35) player2.angle -=36; } n = UpdatePlayers(); if (n & 1) { ResetPlayer(1); // ** LOSE A LIFE player1.lives--; } if (n & 2) { ResetPlayer(2); // ** LOSE A LIFE player2.lives--; } // // OTHER DRAWING FUNCTIONS // // Wait for end of drawing, then a vertical blank DrawSync(0); VSync(0); // Copy virtual screen area(s) to visual screen MoveImage(&P1scr, 0, 0); if (player2.playing) MoveImage(&P2scr, 0, 120); // Signal Display (using single buffer) GsSwapDispBuff(); } ResetGraph(0); } // Draws next frame // Returns death flag, 0, 1, 2 or 3 (3=both players dead) int UpdatePlayers() { int returnval = 0; // Engine thrust if (player1.thrust) { player1.vx += ENGINE_POWER*SIN[player1.angle]; player1.vy -= ENGINE_POWER*COS[player1.angle]; } if (player2.playing) { if (player2.thrust) { player2.vx += ENGINE_POWER*SIN[player2.angle]; player2.vy -= ENGINE_POWER*COS[player2.angle]; } } // Add Gravity Force to y velocity minus air resistance on each x and y player1.vx -= player1.vx*AIR_RESISTANCE; player1.vy -= player1.vy*AIR_RESISTANCE + GRAVITY_FORCE; if (player2.playing) { player2.vx -= player2.vx*AIR_RESISTANCE; player2.vy -= player2.vy*AIR_RESISTANCE + GRAVITY_FORCE; } // Move ships by component velocities player1.px += player1.vx; player1.py += player1.vy; if (player2.playing) { player2.px += player2.vx; player2.py += player2.vy; } // BOUNCE off edges of screen if (player1.px < 0 || player1.px > 625) { player1.px -= player1.vx; player1.vx = -player1.vx; } if (player1.py < 0 || player1.py > 497) { player1.py -= player1.vy; player1.vy = -player1.vy; } if (player2.playing) { if (player2.px < 0 || player2.px > 625) { player2.px -= player2.vx; player2.vx = -player2.vx; } if (player2.py < 0 || player2.py > 497) { player2.py -= player2.vy; player2.vy = -player2.vy; } } // Set actual integer screen coords for ship (ONLY manipulate .px & .py) player1.x = player1.px; player1.y = player1.py; player2.x = player2.px; player2.y = player2.py; // Land on base (don't crash if angle within 20degrees of up) if (player1.x > 406 && player1.x < 452 && (player1.y == 276 || player1.y == 277) && (player1.angle == 0 || player1.angle == 1 || player1.angle == 35)) { // Too fast? then bounce if (abs(player1.vx) > 0.001 || abs(player1.vy) > 0.001) { player1.angle = 0; player1.vy = -player1.vy; } else { player1.y = 276; player1.py = 276; player1.angle = 0; player1.vx = 0; player1.vy = 0; player1.onbase = TRUE; } } else player1.onbase = FALSE; if (player2.x > 438 && player2.x < 493 && (player2.y == 204 || player2.y == 205) && (player2.angle == 0 || player2.angle == 1 || player2.angle == 35)) { // Too fast? then bounce if (abs(player2.vx) > 0.001 || abs(player2.vy) > 0.001) { player2.angle = 0; player2.vy = -player2.vy; } else { player2.y = 204; player2.py = 204; player2.angle = 0; player2.vx = 0; player2.vy = 0; player2.onbase = TRUE; } } else player2.onbase = FALSE; // If not a newplayer restoreOLDarea (uses xold and yold) if (!player1.newplayer) PutPlayerBKImage(1); if (player2.playing) if (!player2.newplayer) PutPlayerBKImage(2); // CLEAR ALL BULLETS ClearBullets(); // SaveNEWarea where players will be drawn (uses x and y) SavePlayerBKImage(1); if (player2.playing) SavePlayerBKImage(2); // DRAW ALL BULLETS DrawBullets(); // Physically draw players on the virtual screen if (DrawPlayer(1)) returnval += 1; if (player2.playing) if (DrawPlayer(2)) returnval += 2; // Save previous location player1.xold = player1.x; player1.yold = player1.y; if (player2.playing) { player2.xold = player2.x; player2.yold = player2.y; } if (player1.newplayer) { player1.newplayer = FALSE; player2.newplayer = FALSE; } return returnval; } // Resets player to starting location (restores GetImage & saves new) void ResetPlayer(u_char player) { if (player == 1) { PutPlayerBKImage(1); player1.px = StartP1.x; player1.py = StartP1.y; player1.x = player1.px; player1.y = player1.py; player1.xold = player1.x; player1.yold = player1.y; player1.vx = 0; player1.vy = 0; player1.angle = 0; SavePlayerBKImage(1); } else if (player == 2) { PutPlayerBKImage(2); player2.px = StartP2.x; player2.py = StartP2.y; player2.x = player2.px; player2.y = player2.py; player2.xold = player2.x; player2.yold = player2.y; player2.vx = 0; player2.vy = 0; player2.angle = 0; SavePlayerBKImage(2); } } // Initialises a bullet into bulletlist if not full void FireBullet(u_char player) { int bul; if (player == 1) { // Find empty slot in list for (bul=0; bul < MAX_BULLETS; bul++) if (!P1Bullet[bul].out) break; if (!P1Bullet[bul].out) { P1Bullet[bul].out = BULLET_LIFE; P1Bullet[bul].x = 7 + player1.px + 8 * SIN[player1.angle]; P1Bullet[bul].y = 7 + player1.py - 8 * COS[player1.angle]; P1Bullet[bul].vx = player1.vx + BULLET_POWER * SIN[player1.angle]; P1Bullet[bul].vy = player1.vy - BULLET_POWER * COS[player1.angle]; } } else if (player == 2) { // Find empty slot in list for (bul=0; bul < MAX_BULLETS; bul++) if (!P2Bullet[bul].out) break; if (!P2Bullet[bul].out) { P2Bullet[bul].out = BULLET_LIFE; P2Bullet[bul].x = 7 + player2.px + 8 * SIN[player2.angle]; P2Bullet[bul].y = 7 + player2.py - 8 * COS[player2.angle]; P2Bullet[bul].vx = player2.vx + BULLET_POWER * SIN[player2.angle]; P2Bullet[bul].vy = player2.vy - BULLET_POWER * COS[player2.angle]; } } } // Clears all bullets from screen void ClearBullets() { int bul; for (bul=0; bul < MAX_BULLETS; bul++) { if (P1Bullet[bul].out) { pix.x = P1Bullet[bul].xold + 384; pix.y = P1Bullet[bul].yold; ClearImage(&pix, 0, 0, 0); } if (P2Bullet[bul].out) { pix.x = P2Bullet[bul].x + 384; pix.y = P2Bullet[bul].y; ClearImage(&pix, 0, 0, 0); } } } // Draws all bullets on screen void DrawBullets() { int bul; for (bul=0; bul < MAX_BULLETS; bul++) { if (P1Bullet[bul].out) { P1Bullet[bul].x += P1Bullet[bul].vx; P1Bullet[bul].y += P1Bullet[bul].vy; pix.x = P1Bullet[bul].x + 384; pix.y = P1Bullet[bul].y; if (!GetPixel(pix.x, pix.y)) { if (--P1Bullet[bul].out) { ClearImage(&pix, 240, 240, 240); P1Bullet[bul].xold = P1Bullet[bul].x; P1Bullet[bul].yold = P1Bullet[bul].y; } } else P1Bullet[bul].out = FALSE; } if (P2Bullet[bul].out) { P2Bullet[bul].x += P2Bullet[bul].vx; P2Bullet[bul].y += P2Bullet[bul].vy; pix.x = P2Bullet[bul].x + 384; pix.y = P2Bullet[bul].y; if (!GetPixel(pix.x, pix.y)) { if (--P2Bullet[bul].out) { ClearImage(&pix, 240, 240, 240); P2Bullet[bul].xold = P2Bullet[bul].x; P2Bullet[bul].yold = P2Bullet[bul].y; } } else P2Bullet[bul].out = FALSE; } } } // Initialises player variables void InitPlayers(int num) { int bul; player1.newplayer = TRUE; player2.newplayer = TRUE; player1.playing = TRUE; if (num == 2) player2.playing = TRUE; else player2.playing = FALSE; player1.lives = NUM_LIVES; player2.lives = NUM_LIVES; player1.alive = TRUE; player2.alive = TRUE; player1.thrust = FALSE; player2.thrust = FALSE; player1.x = StartP1.x; player1.y = StartP1.y; player2.x = StartP2.x; player2.y = StartP2.y; player1.px = (float)StartP1.x; player1.py = (float)StartP1.y; player2.px = (float)StartP2.x; player2.py = (float)StartP2.y; player1.vx = 0.0; player1.vy = 0.0; player2.vx = 0.0; player2.vy = 0.0; player1.angle = 0; // (0-35) 0 = up, 1 = 10 degrees to right player2.angle = 0; // Resets bulletlist flags for (bul=0; bul < MAX_BULLETS; bul++) { P1Bullet[bul].out = FALSE; P2Bullet[bul].out = FALSE; } } // Toggle video mode PAL/NTSC void VidModeChange() { u_char delay; int VidMode = 1; // Default to PAL if (Init_Once) { VidMode = 1 - GetVideoMode(); ResetGraph(0); } else { // Initialize variables scr->x = 0; scr->y = 0; scr->w = 320; scr->h = 240; // Clear visible screen ClearImage(scr, 0, 0, 0); // Set virtual screen area of frame buffer vir.x = 384; vir.y = 0; vir.w = 640; vir.h = 512; // Set both players screen widths (always 320) P1scr.w = 320; P2scr.w = 320; } SetVideoMode(VidMode); if (VidMode) GsInitGraph(320, 240, 0, 0, 0); else GsInitGraph(320, 256, 0, 0, 0); GsDefDispBuff(0, 0, 0, 0); VSync(0); GsSwapDispBuff(); GsSetClip2D(&vir); GsSetDrawBuffClip(); // Delay execution for 30 VSyncs before returning to avoid overswitching if (Init_Once) for (delay=0; delay < 30; delay++) VSync(0); Init_Once = TRUE; } // Load Dogfight map mapnum from MAPADDR, & inits player map variables void MapLoad(u_char mapnum) { u_short x = 0, y = 0; u_char pixel = 0; // Clear Virtual screen ClearImage(&vir, 0, 0, 0); // Memory offset to nearest 4bytes, bytenum: byte offset (0-3) offset = 0; bytenum = 0; if (mapnum == 1) { MAPADDR = (u_long *)0x80090000; StartP1.x = 428; StartP1.y = 276; StartP2.x = 465; StartP2.y = 204; } else if (mapnum == 2) { MAPADDR = (u_long *)0x800a9000; StartP1.x = 147; StartP1.y = 147; StartP2.x = 300; StartP2.y = 246; } else return; do { pixel = ReadByte(); // read byte if (pixel) // non zero value { PutPixel(x+384, y, pixel); // draw dot if (++x == 640) { x=0; y++; } } else // zero - black line { pixel = ReadByte(); // read length of black line (must be non-zero) x+=pixel; if (x >= 640) { x-=640; // add onto x; end of scanline y++; } } } while ((x < 640) && (y < 512)); DrawSync(0); } // Reads next byte from .GFM/.DAT data (little endian) u_char ReadByte() { u_char pbyte=0; u_long bytes4; bytes4 = (u_long)MAPADDR[offset]; if (bytenum == 0) pbyte = (u_char)(bytes4); else if (bytenum == 1) pbyte = (u_char)(bytes4 >> 8); else if (bytenum == 2) pbyte = (u_char)(bytes4 >> 16); else if (bytenum == 3) pbyte = (u_char)(bytes4 >> 24); if (++bytenum == 4) { offset++; bytenum = 0; } return pbyte; } // Creates my own CLUT (256 pixels on frame buffer from 383,0 to 383,255) // 'y' element defines palette index // and loads in 135x90 RAW sprite data to 0,256 void InitFrameBuffer() { int i, x = 0, y = 0; u_char pixel = 0; u_long c; Clut.x = 383; Clut.y = 0; Clut.w = 1; Clut.h = 1; // DRAW CLUT at (383,0) - (383,255) for (i=0; i < NUMCOLOURS; i++) { c = (PALETTE[i*3]>>3) + (PALETTE[i*3+1]<<2) + (PALETTE[i*3+2]<<7); // RGB elements LoadImage(&Clut, &c); // Plot pixel Clut.y++; } MAPADDR = (u_long *)0x8013d000; // reset index pointers for ReadByte() offset = 0; bytenum = 0; // DRAW SPRITES at (0,256) - (135,346) for (y = 0; y < 90; y++) { for (x = 0; x < 135; x++) { pixel = ReadByte(); PutPixel(x, y+256, pixel); } } DrawSync(0); } // Cheeky PutPixel that puts a single pixel onto the frame buffer TOO SLOW!:( void PutPixel(short x, short y, u_char col) { // Sets CLUT index to colour Clut.y = col; // Virtual screen coordinates MoveImage(&Clut, x, y); DrawSync(0); } // GetPixel returns 1 if drawn on a non-zero pixel (for collision detection) u_char GetPixel(short x, short y) { u_long bytes4; pix.x = x; pix.y = y; StoreImage(&pix, &bytes4); if (0xFFFF & bytes4) return 1; else return 0; DrawSync(0); } // Saves area where player will be drawn for restoration at next frame void SavePlayerBKImage(u_char player) { if (player == 1) { tmp.x = player1.x + 384; tmp.y = player1.y; MoveImage(&tmp, 320, 0); } else { tmp.x = player2.x + 384; tmp.y = player2.y; MoveImage(&tmp, 335, 0); } } // Restores a saved image area void PutPlayerBKImage(u_char player) { if (player == 1) { tmp.x = 320; tmp.y = 0; MoveImage(&tmp, player1.xold + 384, player1.yold); } else { tmp.x = 335; tmp.y = 0; MoveImage(&tmp, player2.xold + 384, player2.yold); } } // Physically draws a player on the virtual screen at x, y // returns collision info u_char DrawPlayer(u_char player) { POINT src, dst; // Source & destination points RECT Rsrc; // same, but used for MoveImage u_char collided = 0; // Pixel overwrite flag int x, y; Rsrc.w = 1; Rsrc.h = 1; if (player == 1) { dst.x = player1.x; dst.y = player1.y; if (player1.angle >= 0 && player1.angle <= 8) { src.x = player1.angle * 15; src.y = 256; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + x + 384, dst.y + y)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + x + 384, dst.y + y); } } } } } else if (player1.angle >= 10 && player1.angle <= 18) { src.x = 120 - (player1.angle-10) * 15; src.y = 256; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + 384 + x, dst.y + 14 - y)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + 384 + x, dst.y + 14 - y); } } } } } else if (player1.angle >= 28 && player1.angle <= 35) { src.x = 120 - (player1.angle-28) * 15; src.y = 256; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + 398 - x, dst.y + y)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + 398 - x, dst.y + y); } } } } } else if (player1.angle >= 19 && player1.angle <= 26) { src.x = (player1.angle-19) * 15; src.y = 256; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + 398 - x, dst.y + 14 - y)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + 398 - x, dst.y + 14 - y); } } } } } else if (player1.angle == 27) { src.x = 0; src.y = 256; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + y + 384, dst.y + x)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + y + 384, dst.y + x); } } } } } else if (player1.angle == 9) { src.x = 0; src.y = 256; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + 398 - y, dst.y + x)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + 398 - y, dst.y + x); } } } } } } else { dst.x = player2.x; dst.y = player2.y; if (player2.angle >= 0 && player2.angle <= 8) { src.x = player2.angle * 15; src.y = 271; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + x + 384, dst.y + y)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + x + 384, dst.y + y); } } } } } else if (player2.angle >= 10 && player2.angle <= 18) { src.x = 120 - (player2.angle-10) * 15; src.y = 271; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + 384 + x, dst.y + 14 - y)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + 384 + x, dst.y + 14 - y); } } } } } else if (player2.angle >= 28 && player2.angle <= 35) { src.x = 120 - (player2.angle-28) * 15; src.y = 271; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + 398 - x, dst.y + y)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + 398 - x, dst.y + y); } } } } } else if (player2.angle >= 19 && player2.angle <= 26) { src.x = (player2.angle-19) * 15; src.y = 271; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + 398 - x, dst.y + 14 - y)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + 398 - x, dst.y + 14 - y); } } } } } if (player2.angle == 27) { src.x = 0; src.y = 271; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + y + 384, dst.y + x)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + y + 384, dst.y + x); } } } } } else if (player2.angle == 9) { src.x = 0; src.y = 271; for (x = 0; x < 15; x++) { for (y = 0; y < 15; y++) { if (GetPixel(src.x + x, src.y + y)) { if (GetPixel(dst.x + 398 - y, dst.y + x)) collided = 1; else { Rsrc.x = src.x + x; Rsrc.y = src.y + y; MoveImage(&Rsrc, dst.x + 398 - y, dst.y + x); } } } } } } return collided; } // ****** PAD routines ****** // // low-level pad buffers: never need to touch volatile u_char *bb0, *bb1; // call once only in program initialisation void PadInit (void) { GetPadBuf(&bb0, &bb1); } // call once per VSync(0) // puts controller pad status into unsigned long integer // please refer to the manuals if you want explanation // of the internals of this function u_long PadRead(void) { return(~(*(bb0+3) | *(bb0+2) << 8 | *(bb1+3) << 16 | *(bb1+2) << 24)); }