#include // Mega32 specification file #include #include "game.h" // Game Data Structures. #include "ports.h" // Port Info. #include "display.h" // TV Display parameters #include "global.h" // Various Global Parameters #include "physics.h" // Physics Engine #include "letter.h" // Display Text #include "flash.h" // Import all the ship bitmaps. #include "letterFlash.h" // Import all the letter bitmaps. #include "menu.h" // Menu Features. #include #pragma regalloc- PlayerInfoDesc p1InfoDesc[1], p2InfoDesc[1]; // Player 1 & 2 Information. PlayerInfoDesc* p1Info; // Pointers to Player 1 and 2 Information. PlayerInfoDesc* p2Info; ShipInfoDesc s1Info[1], s2Info[1]; // Ship 1 and Ship 2 Information. char state; // Current State of the game. char zoomCounter; // Counter used by zoomCheck char zoomX; // X coordinate of center of Zoom Field. char zoomY; // Y coordinate of center of Zoom Field. char deathMsg[17]; // Death Message. char p1Life[7]; // Display Life char p2Life[7]; // Display Life. int deathExplosion; // Delay for Death Explosion #pragma regalloc+ // Main Method of our program. void main(void) { char PlayerTurn=1; int minX, minY; // Initialize the program initialize(); initializeShips(); initializeDisplay(); initializeMenu(p1Info->ship, p2Info->ship); while(1) { //stall here until next line starts //sleep enable; mode=idle //use sleep to make entry into sync ISR uniform time #asm ("sleep"); //The following code executes during the vertical blanking //Code here can be as long as //a total of 60 lines x 63.5 uSec/line x 8 cycles/uSec if (lineCount == 231) { switch(state) { case GM_TITLE: // Update Menu FSM. - if it returns true, we can start the game. if (nextMenuState()) { state = GM_GAME; // Prepare Background - Planet drawPlanet(); renderPlanet(); // Initialize Ships initializeShips(); } break; case GM_GAME: if (PlayerTurn == 1){ // Display Life. if (isZoomed()) { // Disable Life. sprintf(p1Life, " ",p1Info->ship->life); video_putsmalls(0,95,p1Life); video_putsmalls(100,95,p1Life); } else { // Display Each Player's life. sprintf(p1Life, "P1 %4d",p1Info->ship->life); video_putsmalls(0,95,p1Life); sprintf(p2Life, "P2 %4d",p2Info->ship->life); video_putsmalls(100,95,p2Life); } // Collision Detection. hitOpponent(p1Info->ship, p2Info->ship); hitOpponent(p2Info->ship, p1Info->ship); // Erase Explosions for ship 1 eraseExplosions(); drawExplosions(p1Info->ship); renderExplosions(); // Draw Ship 2 drawShip(p2Info->ship->xpix, p2Info->ship->ypix, p2Info->ship->theta, p2Info->ship->type, 1); renderShip(); PlayerTurn =2; } else if (PlayerTurn == 2) { // Get user input for p1 and p2 acquireCommandsP1(p1Info->ship); updateShip(p1Info->ship); acquireCommandsP2(p2Info->ship); updateShip(p2Info->ship); // Update Bullets life, position, velocity for player 1 deactBullets(p1Info->ship); updateBullets(p1Info->ship, p2Info->ship); // Draw the bullets. eraseBullets(0); drawBullets(p1Info->ship,0); renderBullets(0); // Update Bullets life, position, velocity for player 2 deactBullets(p2Info->ship); updateBullets(p2Info->ship, p1Info->ship); eraseBullets(1); // Draw the bullets. drawBullets(p2Info->ship,1); renderBullets(1); // Erase Explosions for ship 2 eraseExplosions(); drawExplosions(p2Info->ship); renderExplosions(); // Draw Ship 1. drawShip(p1Info->ship->xpix, p1Info->ship->ypix, p1Info->ship->theta, p1Info->ship->type, 0); renderShip(); // Check to see if ships are within range for zoom. checkZoom(p1Info->ship, p2Info->ship); PlayerTurn = 1; renderPlanet(); } break; case GM_GAMEOVER: // Explosion! if (deathExplosion > 0) { eraseExplosions(); // Determine who died and zoom. if (p1Info->ship->life <= 0) { drawExplosion(p1Info->ship->xpix, p1Info->ship->ypix, (4 * EXPLODEFRAME - deathExplosion) >> 3); // Calculate Zoom minX = ((signed char)p1Info->ship->xpix - 32 > 0)? p1Info->ship->xpix - 32: 0; minY = ((signed char)p1Info->ship->ypix - 25 > 0)? p1Info->ship->ypix - 25: 0; } else { // Else Redraw Ship. drawShip(p1Info->ship->xpix, p1Info->ship->ypix,p1Info->ship->theta,p1Info->ship->type, 0); } if (p2Info->ship->life <= 0) { drawExplosion(p2Info->ship->xpix, p2Info->ship->ypix, (4 * EXPLODEFRAME - deathExplosion) >> 3); // Calculate Zoom. minX = ((signed char)p2Info->ship->xpix - 32 > 0)? p2Info->ship->xpix - 32: 0; minY = ((signed char)p2Info->ship->ypix - 25 > 0)? p2Info->ship->ypix - 25: 0; } else { // Else Redraw Ship. drawShip(p2Info->ship->xpix, p2Info->ship->ypix,p2Info->ship->theta,p2Info->ship->type, 1); } minX = ((unsigned char)minX > 64)?64:minX; minY = ((unsigned char)minY > 50)?50:minY; // Zoom in on explosion! zoomEnable(minX, minY); renderShip(); // Draw Ships. renderExplosions(); // Draw Explosions drawPlanet(); // Draw Planet renderPlanet(); // Calculate Zoom. deathExplosion--; } else if (deathExplosion <= 0 && deathExplosion > -100) { eraseExplosions(); // Display Death Message if (p1Info->ship->life <= 0) { sprintf(deathMsg, "PLAYER 1 DIED"); video_putsmalls(minX & 0xFC, minY, deathMsg); } else { // Else Redraw Ship. drawShip(p1Info->ship->xpix, p1Info->ship->ypix,p1Info->ship->theta,p1Info->ship->type, 0); } // Display Death Message if (p2Info->ship->life <= 0) { sprintf(deathMsg, "PLAYER 2 DIED"); video_putsmalls(minX & 0xFC, minY + 45, deathMsg); } else { // Else Redraw Ship. drawShip(p2Info->ship->xpix, p2Info->ship->ypix,p2Info->ship->theta,p2Info->ship->type, 1); } renderShip(); // Draw Ship. deathExplosion--; // Decrement Counter. drawPlanet(); // Draw Planet renderPlanet(); } else if (deathExplosion <= -100) { // Go back to Menu Mode. drawPlanet(); renderPlanet(); // Unzoom. zoomDisable(); state = GM_TITLE; } break; } } //end if state game } //end while } // Initialize the port and interrupt settings of the game. void initialize() { // Initialize Timer 1 to generate the sync signal. OCR1A = 1018; // One NTSC line TCCR1B = 0x9; // Bits: 1-0 - Full Speed (no prescaling) // Bit: 3 - Clear Timer On Compare TCCR1A = 0x00; // Turn off pwn and oc lines. // Bits: 1-0 - Clear Timer On Compare TIMSK = 0x10; // Bit 4: Enable Output Compare A Match Interrupt // Initialize Ports VIDEO_DDR = 0xF0; // Video Output. P1_DDR = 0x0; // P1 Genesis Controller P2_DDR = 0x0; // P2 Genesis Controller // Initialize Sync Constants lineCount = 1; // Start with line 1. syncOn = _DISP_SYNC; // Initial syncs are horizontal syncs. syncOff = _DISP_LOW; // Sleep Mode MCUCR = 0b10000000; // Enable Sleep (used to improve Video Out // Interrupt 1 timing accuracy) // Initialize the Ships p1Info->ship->type = 1; p2Info->ship->type = 1; // Enable Interrupts. #asm ("sei"); //init the state state = GM_TITLE; // Zoom Paramter zoomCounter = 0; } void initializeShips() { // Temporary Variables char indexinit1; // Allocate the room necessary for p1Info = &(p1InfoDesc[0]); p2Info = &(p2InfoDesc[0]); p1Info->ship = &(s1Info[0]); p2Info->ship = &(s2Info[0]); //player 1 init p1Info->ship->xpix = 07; p1Info->ship->ypix = 50; p1Info->ship->theta = 0b11010000; p1Info->ship->xfrac = 0; p1Info->ship->yfrac = 0; p1Info->ship->vx = 0; p1Info->ship->vy = 0; p1Info->ship->accel = 0; p1Info->ship->life = shipTable[p1Info->ship->type][MAXLIFE]; p1Info->ship->fireDelay1 = 0; p1Info->ship->fireDelay2 = 0; p1Info->ship->W1type = shipTable[p1Info->ship->type][W1TYPE]; p1Info->ship->W2type = shipTable[p1Info->ship->type][W2TYPE]; p1Info->ship->activebullets =0; p1Info->ship->boostcount=0; for(indexinit1 = 0; indexinit1 < MAX_BULLETS; indexinit1++) { p1Info->ship->BulletType[indexinit1] =0; p1Info->ship->BulletPosX[indexinit1] =0; p1Info->ship->BulletPosY[indexinit1] =0; p1Info->ship->BulletXFrac[indexinit1] =0; p1Info->ship->BulletYFrac[indexinit1] =0; p1Info->ship->BulletDirection[indexinit1] =0; p1Info->ship->BulletActive[indexinit1] =0; p1Info->ship->BulletLife[indexinit1] = 0; p1Info->ship->explodeActive[indexinit1]=0; p1Info->ship->explodeLife[indexinit1]=0; p2Info->ship->BulletType[indexinit1] =0; p2Info->ship->BulletPosX[indexinit1] =0; p2Info->ship->BulletPosY[indexinit1] =0; p2Info->ship->BulletXFrac[indexinit1] =0; p2Info->ship->BulletYFrac[indexinit1] =0; p2Info->ship->BulletDirection[indexinit1] =0; p2Info->ship->BulletActive[indexinit1] =0; p2Info->ship->BulletLife[indexinit1] = 0; p2Info->ship->explodeActive[indexinit1]=0; p2Info->ship->explodeLife[indexinit1]=0; } //player 2 init p2Info->ship->xpix = 112; p2Info->ship->ypix = 50; p2Info->ship->theta = 0b01000000; p2Info->ship->xfrac = 0; p2Info->ship->yfrac = 0; p2Info->ship->vx = 0; p2Info->ship->vy = 0; p2Info->ship->accel = 0; p2Info->ship->life = shipTable[p2Info->ship->type][MAXLIFE]; p2Info->ship->fireDelay1 = 0; p2Info->ship->fireDelay2 = 0; p2Info->ship->W1type = shipTable[p2Info->ship->type][W1TYPE]; p2Info->ship->W2type = shipTable[p2Info->ship->type][W2TYPE]; p2Info->ship->activebullets =0; p2Info->ship->boostcount=0; } // Decode the commands from user (gamepad) void acquireCommandsP1(ShipInfoDesc* ship) { if (ship->fireDelay1 >0){ ship->fireDelay1--; //decrement firing delay counters (might do in ISR instead??) *********** } if (ship->fireDelay2 >0){ ship->fireDelay2--; } ship->accel = (P1_PIN & GEN_UP)? 1:0; //check up arrow ship->theta = (P1_PIN & GEN_LEFT)? ship->theta + shipTable[ship->type][TURNING]:ship->theta; //update left turning ship->theta = (P1_PIN & GEN_RIGHT)? ship->theta - shipTable[ship->type][TURNING]:ship->theta; //update right turning if (P1_PIN & GEN_W1){ if(ship->activebullets < MAX_BULLETS && ship->fireDelay1 ==0){ char index1 = 0; while (index1 < MAX_BULLETS){ //break out if you find and fill and empty space if(ship->BulletActive[index1]==0){ ship->BulletType[index1] = shipTable[ship->type][W1TYPE]; ship->BulletPosX[index1] = ship->xpix; ship->BulletPosY[index1] = ship->ypix; ship->BulletXFrac[index1] = ship->xfrac; ship->BulletYFrac[index1] = ship->yfrac; ship->BulletDirection[index1] = ship->theta; ship->BulletActive[index1] = 1; ship->BulletLife[index1] = bulletTable[ship->W1type][LIFETIME]; ship->fireDelay1 = shipTable[ship->type][FIREDEL1]; break; } else { index1++; } } ship->activebullets++; //increment the number of active bullets } } //end if weapon1 if (P1_PIN & GEN_W2){ if(ship->W2type <4 && ship->activebullets fireDelay2 ==0){ char index2 = 0; while (index2 < MAX_BULLETS){ //break out if you find and fill and empty space if(ship->BulletActive[index2]==0){ ship->BulletType[index2] = shipTable[ship->type][W2TYPE]; ship->BulletPosX[index2] = ship->xpix; ship->BulletPosY[index2] = ship->ypix; ship->BulletXFrac[index2] = ship->xfrac; ship->BulletYFrac[index2] = ship->yfrac; ship->BulletDirection[index2] = ship->theta; ship->BulletActive[index2] = 1; ship->BulletLife[index2] = bulletTable[ship->W2type][LIFETIME]; ship->fireDelay2 = shipTable[ship->type][FIREDEL2]; break; } else { index2++; } } ship->activebullets++; //increment the number of active bullets } else if (ship->W2type == 4 && ship->fireDelay2 ==0){ ship->vx = 2*ship->vx; ship->vy = 2*ship->vy; ship->fireDelay2 = shipTable[ship->type][FIREDEL2]; //Reset the countdown if weapon2 successfully used ship->boostcount = 30; } else if(ship->W2type == 5 && ship->fireDelay2 ==0){ ship-> life = ship->life + 20; ship->fireDelay2 = shipTable[ship->type][FIREDEL2]; if(ship->life > shipTable[ship->type][MAXLIFE]){ ship->life = shipTable[ship->type][MAXLIFE]; } } } //end if weapon2 if (ship->boostcount>0) { //maintain boost if counter not 0 ship->vx = 2*ship->vx; ship->vy = 2*ship->vy;; } }//end controller input function P1 // Decode the commands from user (gamepad) void acquireCommandsP2(ShipInfoDesc* ship) { if (ship->fireDelay1 >0){ ship->fireDelay1--; //decrement firing delay counters (might do in ISR instead??) *********** } if (ship->fireDelay2 >0){ ship->fireDelay2--; } PORTC = P2_PIN; ship->accel = (P2_PIN & GEN_UP)? 1:0; //check up arrow ship->theta = (P2_PIN & GEN_LEFT)? ship->theta + shipTable[ship->type][TURNING]:ship->theta; //update left turning ship->theta = (P2_PIN & GEN_RIGHT)? ship->theta - shipTable[ship->type][TURNING]:ship->theta; //update right turning if (P2_PIN & GEN_W1){ if( /*weapon is projectile && */ship->activebullets < MAX_BULLETS && ship->fireDelay1 ==0){ char index1 = 0; while (index1 < MAX_BULLETS){ //break out if you find and fill and empty space if(ship->BulletActive[index1]==0){ ship->BulletType[index1] = shipTable[ship->type][W1TYPE]; ship->BulletPosX[index1] = ship->xpix; ship->BulletPosY[index1] = ship->ypix; ship->BulletXFrac[index1] = ship->xfrac; ship->BulletYFrac[index1] = ship->yfrac; ship->BulletDirection[index1] = ship->theta; ship->BulletActive[index1] = 1; ship->BulletLife[index1] = bulletTable[ship->W1type][LIFETIME]; ship->fireDelay1 = shipTable[ship->type][FIREDEL1]; break; } else { index1++; } } ship->activebullets++; //increment the number of active bullets } } //end if weapon1 if (P2_PIN & GEN_W2){ if( ship->W2type <4 && ship->activebullets fireDelay2 ==0){ char index2 = 0; while (index2 < MAX_BULLETS){ //break out if you find and fill and empty space if(ship->BulletActive[index2]==0){ ship->BulletType[index2] = shipTable[ship->type][W2TYPE]; ship->BulletPosX[index2] = ship->xpix; ship->BulletPosY[index2] = ship->ypix; ship->BulletXFrac[index2] = ship->xfrac; ship->BulletYFrac[index2] = ship->yfrac; ship->BulletDirection[index2] = ship->theta; ship->BulletActive[index2] = 1; ship->BulletLife[index2] = bulletTable[ship->W2type][LIFETIME]; ship->fireDelay2 = shipTable[ship->type][FIREDEL2]; break; } else { index2++; } } ship->activebullets++; //increment the number of active bullets } else if (ship->W2type == 4 && ship->fireDelay2 ==0){ ship->vx = 2*ship->vx; ship->vy = 2*ship->vy; ship->fireDelay2 = shipTable[ship->type][FIREDEL2]; //Reset the countdown if weapon2 successfully used ship->boostcount = 60; } else if(ship->W2type == 5 && ship->fireDelay2 ==0){ ship-> life = ship->life + 20; ship->fireDelay2 = shipTable[ship->type][FIREDEL2]; if(ship->life > shipTable[ship->type][MAXLIFE]){ ship->life = shipTable[ship->type][MAXLIFE]; } } } //end if weapon2 if (ship->boostcount>0) { //maintain boost if counter not 0 ship->vx = 2*ship->vx; ship->vy = 2*ship->vy;; } }//end controller input function P2 // Deactivate Dead or Timed out Bullets for given ship. void deactBullets(ShipInfoDesc* ship) { register char indexBullet; // Go through each bullet for the ship. for (indexBullet = 0; indexBullet < MAX_BULLETS; indexBullet++){ // Decrement life. if (ship->BulletLife[indexBullet]>0){ ship->BulletLife[indexBullet]--; } // If no more life, deactivate the bullet. if (ship->BulletLife[indexBullet] == 0){ ship->BulletActive[indexBullet] = 0; } } } // Draw Bullets onto screen for given ship. void drawBullets(ShipInfoDesc* ship, char player) { register char i; // Go through the bullet array for the given ship. for (i = 0; i < MAX_BULLETS; i++) { // If active, draw bullet. if (ship->BulletActive[i]) drawBullet(ship->BulletPosX[i],ship->BulletPosY[i],ship->BulletDirection[i],ship->BulletType[i], player); } } // Draw Explosions onto screen for given ship. void drawExplosions(ShipInfoDesc* ship) { register char i; // Go through the bullet array for the given ship. for (i = 0; i < MAX_BULLETS; i++) { // If active, draw bullet. if (ship->explodeActive[i]) drawExplosion(ship->explodeX[i],ship->explodeY[i],(EXPLODEFRAME - ship->explodeLife[i]) >> 1 ); } } //calculate dist squared btw two points unsigned int distanceSQ(char x1, char y1, char x2, char y2){ signed int xdiff, ydiff; unsigned int distSQ; xdiff = (signed int)x1 - (signed int)x2; //xdiff = (xdiff <0)? -xdiff: xdiff; ydiff = (signed int)y1 - (signed int)y2; //ydiff = (ydiff <0)? -ydiff: ydiff; distSQ = xdiff*xdiff + ydiff*ydiff; return distSQ; }//end distance //hit detection, explosion handling, endgame detection void hitOpponent(ShipInfoDesc* shipmain, ShipInfoDesc* shiptarg){ char index; unsigned int distsqu; unsigned int distplanet; unsigned int distbullplan; distplanet = distanceSQ(shipmain->xpix,shipmain->ypix,60, 50); //distance from planet //you die if you hit the planet if(distplanet < (shipTable[shipmain->type][COLLRAD]+PLANET_RADIUS)*(shipTable[shipmain->type][COLLRAD]+PLANET_RADIUS)){ shipmain->life = 0; } //parse the ship's bullet list for active ones, operate on these only for (index=0; index < MAX_BULLETS; index++){ if(shipmain->BulletActive[index]==1){ //calculate distance from bullet to opponent, bullet explodes if it hits opponent, opponent's life goes down distsqu = distanceSQ(shipmain->BulletPosX[index],shipmain->BulletPosY[index],shiptarg->xpix, shiptarg->ypix); if (distsqu < (shipTable[shiptarg->type][COLLRAD] + bulletTable[shipmain->BulletType[index]][RADIUS])*(shipTable[shiptarg->type][COLLRAD] + bulletTable[shipmain->BulletType[index]][RADIUS])){ shiptarg->life = shiptarg->life - bulletTable[shipmain->BulletType[index]][DAMAGE]; shipmain->explodeActive[index] = 1; shipmain->explodeX[index] = shipmain->BulletPosX[index]; shipmain->explodeY[index] = shipmain->BulletPosY[index]; shipmain->BulletLife[index] =0; shipmain->BulletActive[index] = 0; shipmain->explodeLife[index] = EXPLODEFRAME; } //end if dist to targ //calculate distance from bullet to planet, bullet explodes if it hits planet distbullplan = distanceSQ(shipmain->BulletPosX[index],shipmain->BulletPosY[index],60, 50); if (distbullplan < (PLANET_RADIUS + bulletTable[shipmain->BulletType[index]][RADIUS])*(PLANET_RADIUS + bulletTable[shipmain->BulletType[index]][RADIUS])){ shipmain->explodeActive[index] = 1; shipmain->explodeX[index] = shipmain->BulletPosX[index]; shipmain->explodeY[index] = shipmain->BulletPosY[index]; shipmain->BulletLife[index] =0; shipmain->BulletActive[index] = 0; shipmain->explodeLife[index] = EXPLODEFRAME; } }//end if bullet active if (shipmain->explodeLife[index]>0){ //decrease explosion lifetime shipmain->explodeLife[index]--; } if (shipmain->explodeLife[index] ==0){ //if lifetime is zero, deactivate the explosion shipmain->explodeActive[index]=0; } }//end for parsing //detect if the opponent is dead or you are dead, zoom in on death explosion, go to Game Over mode if(shiptarg->life <=0 || shipmain->life <= 0){ //PRINT "Player has been destroyed" "Now Returning to Menu..." deathExplosion = 4 * EXPLODEFRAME; // Erase Life Display. sprintf(p1Life, " ",p1Info->ship->life); video_putsmalls(0,95,p1Life); video_putsmalls(100,95,p1Life); state = GM_GAMEOVER; } //end if life <0 } //end hitopp // Triggers zoom mode, if appropriate void checkZoom (ShipInfoDesc* ship1, ShipInfoDesc* ship2) { if (!isZoomed()) { // Check Distance if (distanceSQ(ship1->xpix, ship1->ypix, ship2->xpix, ship2->ypix) <= 600) { register signed char minX, minY; minX = (ship1->xpix >> 1) + (ship2->xpix >> 1); minY = (ship1->ypix >> 1) + (ship2->ypix >> 1); minX = (minX - 32 > 0)? minX - 32: 0; minY = (minY - 25 > 0)? minY - 25: 0; minX = ((unsigned char)minX > 64)?64:minX; minY = ((unsigned char)minY > 50)?50:minY; if (zoomCounter == 0) zoomEnable(minX, minY); zoomX = minX + 3; zoomY = minY + 3; } } else { if (ship1->xpix < zoomX || ship1->xpix > zoomX + 58 || ship1->ypix < zoomY || ship1->ypix > zoomY + 44 || ship2->xpix < zoomX || ship2->xpix > zoomX + 58 || ship2->ypix < zoomY || ship2->ypix > zoomY + 44) { zoomDisable(); zoomCounter = 20; } } if (zoomCounter > 0) zoomCounter--; }