//RS-178 Video output standard //D.5 is sync:1000 ohm + diode to 75 ohm resistor //D.6 is video:330 ohm + diode to 75 ohm resistor /* Gamepad buttons ------------- \ 0 1 2 3 4 / \ 5 6 7 8 / -------- Pad Pin Assignment PORT B Pin Game Control 0 Vcc Vcc - 1 (right) 0 ship movement/cursor 2 (left) 1 ship movement/cursor 3 (down) 2 high score/title screen select 4 (up) 3 high score/title screen select 5 (Button C) 4 start/pause/enter/reset (with B) 6 GND GND - 7 Vcc (Mux select) VCC - 8 (Button B) 5 high score/fire/reset (with C) */ #pragma regalloc- //I allocate the registers myself #pragma optsize- //optimize for speed #include #include #include #include #include //============================================================================== // TV Variable Declarations //============================================================================== //cycles = 63.625 * 16 Note NTSC is 63.55 //but this line duration makes each frame exactly 1/60 sec //which is nice for keeping a realtime clock #define lineTime 1018 #define ScreenTop 30 #define ScreenBot 230 //NOTE that v1 to v8 and i must be in registers! register char v1 @4; register char v2 @5; register char v3 @6; register char v4 @7; register char v5 @8; register char v6 @9; register char v7 @10; register char v8 @11; register int i @12; #pragma regalloc+ char syncON, syncOFF; int LineCount; unsigned int readdata, bitmaptemp; //used for video routines int count; //general purpose counter char screen[1600], t, ts[10]; //used for writing unaligned small chars flash unsigned int datamask[] = { 0b0001111111111111, 0b1000111111111111, 0b1100011111111111, 0b1110001111111111, 0b1111000111111111, 0b1111100011111111, 0b1111110001111111, 0b1111111000111111 }; flash unsigned char linemask[] = { 0b01111111, 0b10111111, 0b11011111, 0b11101111, 0b11110111, 0b11111011, 0b11111101, 0b11111110 }; //Point plot lookup table //One bit masks flash char pos[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; //============================================================================== //============================================================================== // Game Variable Declarations //============================================================================== #define numRows 5 //number of rows of enemy ships #define shipsPerRow 5 //number of enemy ships / row #define numShips 25 //number of enemy ships / level #define maxBullets 9 //maximum number of enemy bullets #define invTime 120 //invulnerability time //probShoot sets the frequency of enemy bullets //must be < 32767. larger is higher probability #define probShoot 16535 //game state variables char gameState; enum {title, init, game, pause, win, lose, gameover, drawtitle, clearscreen, die, scorescreen, newscorescreen, twoplayerinit, twoplayergame, player1win, player2win, clear2player, drawlevel, delaygame}; char initCount; //counter int gameovertimer; //counter for game over screen int scoretimer; //counter for high score screen int levelscreenpause; //counter for level display screen int clearCounter; //counter for clearing 2 player game char deathCount; //used for death animation char menuitem; //selection between single and versus mode //player variables //single/versus mode char playerx, playery; //player 1 position char lives; //player 1 lives int score; //player score (single player) int level; //level the player is on char bullet, bulletx, bullety; //player 1 bullet flag/position int bulletHit; //flag for bullets hitting enemy ships unsigned char invulnerable, pgraphic; //used for player invulnerability char lifeFlag; //tells whether to gain a life char deathFlag; //tells when the ship has died //versus mode char player2x, player2y; //player 2 position char lives2; //player 2 lives char bullet1Count; //used for player 1 shooting char bullet2Count; //used for player 2 shooting char bullet1[10], bullet1x[10], bullet1y[10]; //player 1 bullet flag/position char bullet2[10], bullet2x[10], bullet2y[10]; //player 2 bullet flag/position //enemy ship variables int scrollTimer; //counter for enemy movement int scrollVal; //tells how long to wait to scroll char row1Live[5], //liveness of enemy ships in row 1 row2Live[5], row3Live[5], row4Live[5], row5Live[5]; char *ptr; //used for hit detection (column position) char activeRow; //used for hit detection (row position) char hPos; //x position of leftmost enemy ships //vertical positions const char row1Pos = 3, row2Pos = 10, row3Pos = 17, row4Pos = 24, row5Pos = 31; char scrollDirection; //0 = left, 1 = right char shipCount; //number of enemy ships remaining on level //ship that breaks off (attacking) char xpos, ypos; //attacking ship position char row, col; //original location of attacking ship char attacking; //flag for seeing if an enemy ship is attacking //enemy bullet variables unsigned char ebulletsx[maxBullets]; //x position of enemy bullets unsigned char ebulletsy[maxBullets]; //y position of enemy bullets unsigned char ebulletslive[maxBullets]; //liveness of enemy bullets char bulletTimer; //scroll timer for enemy bullets int bulletTimerVal; //value of scroll timer unsigned char bulletsPerLevel; //number of bullets on a level unsigned char bulletCount; //used for finding active enemy bullets int someRandom; //random number generator //button debounce variables char buttonCpush, buttonCwaspressed; char buttonBpush, buttonBwaspressed; char buttonUPpush, buttonUPwaspressed; char buttonDOWNpush, buttonDOWNwaspressed; char buttonLEFTpush, buttonLEFTwaspressed; char buttonRIGHTpush, buttonRIGHTwaspressed; char buttontimer; //for 2nd player button char buttonB2push, buttonB2waspressed; //high scores //0th location is highest score char highNamestemp[3] = {13, 13, 13}; //stores input name char highpos; //used to find where to place score char curpos = 40; //cursor position eeprom int highScores[] = {5, 4, 3, 2, 1}; //stores high scores eeprom char highNames [5][3] = //stores initials for high scores { 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 17, 17, 17 }; //============================================================================== //============================================================================== // Bitmaps //============================================================================== //logo screen bitmap flash char logo[32][10] = { 0x00, 0x1c, 0x0f, 0xe3, 0xf0, 0x60, 0xc0, 0x00, 0x00, 0x00, 0xf0, 0x16, 0x18, 0x31, 0x98, 0x38, 0xe0, 0x00, 0x00, 0x00, 0x98, 0x1a, 0x10, 0x0c, 0xc4, 0x24, 0xb8, 0x00, 0x00, 0x00, 0xcf, 0x0b, 0x11, 0x04, 0x63, 0xb4, 0x8c, 0x00, 0x00, 0x00, 0x61, 0x8d, 0x91, 0xc6, 0x30, 0x96, 0x86, 0x00, 0x00, 0x00, 0x30, 0xc4, 0x91, 0x63, 0x10, 0xd2, 0xc3, 0x00, 0x00, 0x00, 0x10, 0x64, 0x91, 0xb1, 0x10, 0x72, 0x49, 0x80, 0x00, 0x00, 0x10, 0x34, 0xd0, 0x91, 0x10, 0x62, 0x4c, 0xc0, 0x00, 0x00, 0x30, 0x1c, 0x50, 0xd1, 0x18, 0x66, 0x40, 0x60, 0x00, 0x00, 0x21, 0x8c, 0x50, 0x53, 0x08, 0x64, 0x4c, 0x30, 0x00, 0x00, 0x63, 0xc0, 0x50, 0x72, 0x0c, 0x0c, 0x4e, 0x18, 0x00, 0x00, 0x46, 0x70, 0x58, 0x02, 0x04, 0x08, 0x65, 0x08, 0x00, 0x00, 0x5c, 0x18, 0x48, 0x02, 0x06, 0x08, 0x24, 0x8c, 0x00, 0x00, 0x70, 0x0c, 0xcf, 0xc6, 0x03, 0x98, 0x24, 0xc4, 0x00, 0x00, 0x00, 0x07, 0x80, 0x3c, 0x00, 0xf0, 0x3c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0xf0, 0x3f, 0x80, 0x00, 0xe0, 0xf1, 0xfc, 0x00, 0x1d, 0xe3, 0x1c, 0x30, 0xe0, 0xf1, 0xb9, 0x93, 0x06, 0x00, 0x70, 0x22, 0x07, 0x20, 0x60, 0x98, 0x89, 0x12, 0x03, 0x00, 0x40, 0x32, 0x01, 0x27, 0x30, 0x8c, 0xc9, 0x13, 0x39, 0x00, 0x4f, 0x93, 0x01, 0x2d, 0x10, 0x84, 0x4e, 0x31, 0xaf, 0x00, 0x68, 0xf1, 0x8f, 0x29, 0x10, 0x84, 0x40, 0x60, 0xa0, 0x00, 0x28, 0x00, 0x98, 0x4f, 0x10, 0x84, 0x40, 0xc0, 0xb0, 0x00, 0x27, 0x80, 0x90, 0xc0, 0x30, 0x84, 0x41, 0x81, 0x9c, 0x00, 0x30, 0xc0, 0x90, 0x81, 0xe1, 0x8c, 0x41, 0x81, 0x06, 0x00, 0x18, 0x61, 0xb1, 0x0d, 0x81, 0x08, 0xc8, 0xc1, 0x3c, 0x0f, 0xe8, 0x31, 0x21, 0x1c, 0xc3, 0x18, 0x8c, 0x63, 0x60, 0x06, 0x38, 0x11, 0x22, 0x34, 0x66, 0x31, 0x8e, 0x22, 0x78, 0x02, 0x00, 0x33, 0x62, 0x24, 0x34, 0x21, 0x1b, 0x32, 0x0c, 0x01, 0x80, 0x62, 0xc2, 0x46, 0x17, 0xc3, 0x31, 0x33, 0x98, 0x00, 0xff, 0x83, 0x82, 0xc3, 0xf0, 0x01, 0xe1, 0xe0, 0xf0, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 }; //------------------------------------------------------------------------------ //define some character bitmaps //5x7 characters // arrayNum - character flash char bitmap[39][7]= { //0 - 0 0b01110000, 0b10001000, 0b10011000, 0b10101000, 0b11001000, 0b10001000, 0b01110000, //1 - 1 0b00100000, 0b01100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b01110000, //2 - 2 0b01110000, 0b10001000, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b11111000, //3 - 3 0b11111000, 0b00010000, 0b00100000, 0b00010000, 0b00001000, 0b10001000, 0b01110000, //4 - 4 0b00010000, 0b00110000, 0b01010000, 0b10010000, 0b11111000, 0b00010000, 0b00010000, //5 - 5 0b11111000, 0b10000000, 0b11110000, 0b00001000, 0b00001000, 0b10001000, 0b01110000, //6 - 6 0b01000000, 0b10000000, 0b10000000, 0b11110000, 0b10001000, 0b10001000, 0b01110000, //7 - 7 0b11111000, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000, 0b10000000, //8 - 8 0b01110000, 0b10001000, 0b10001000, 0b01110000, 0b10001000, 0b10001000, 0b01110000, //9 - 9 0b01110000, 0b10001000, 0b10001000, 0b01111000, 0b00001000, 0b00001000, 0b00010000, //10 - A 0b01110000, 0b10001000, 0b10001000, 0b10001000, 0b11111000, 0b10001000, 0b10001000, //11 - B 0b11110000, 0b10001000, 0b10001000, 0b11110000, 0b10001000, 0b10001000, 0b11110000, //C 0b01110000, 0b10001000, 0b10000000, 0b10000000, 0b10000000, 0b10001000, 0b01110000, //D 0b11110000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b11110000, //E 0b11111000, 0b10000000, 0b10000000, 0b11111000, 0b10000000, 0b10000000, 0b11111000, //F 0b11111000, 0b10000000, 0b10000000, 0b11111000, 0b10000000, 0b10000000, 0b10000000, //G 0b01110000, 0b10001000, 0b10000000, 0b10011000, 0b10001000, 0b10001000, 0b01110000, //H 0b10001000, 0b10001000, 0b10001000, 0b11111000, 0b10001000, 0b10001000, 0b10001000, //I 0b01110000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b01110000, //J 0b00111000, 0b00010000, 0b00010000, 0b00010000, 0b00010000, 0b10010000, 0b01100000, //K 0b10001000, 0b10010000, 0b10100000, 0b11000000, 0b10100000, 0b10010000, 0b10001000, //L 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b11111000, //M 0b10001000, 0b11011000, 0b10101000, 0b10101000, 0b10001000, 0b10001000, 0b10001000, //N 0b10001000, 0b10001000, 0b11001000, 0b10101000, 0b10011000, 0b10001000, 0b10001000, //O 0b01110000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b01110000, //P 0b11110000, 0b10001000, 0b10001000, 0b11110000, 0b10000000, 0b10000000, 0b10000000, //Q 0b01110000, 0b10001000, 0b10001000, 0b10001000, 0b10101000, 0b10010000, 0b01101000, //R 0b11110000, 0b10001000, 0b10001000, 0b11110000, 0b10100000, 0b10010000, 0b10001000, //S 0b01111000, 0b10000000, 0b10000000, 0b01110000, 0b00001000, 0b00001000, 0b11110000, //T 0b11111000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, //U 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b01110000, //V 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b01010000, 0b00100000, //W 0b10001000, 0b10001000, 0b10001000, 0b10101000, 0b10101000, 0b10101000, 0b01010000, //X 0b10001000, 0b10001000, 0b01010000, 0b00100000, 0b01010000, 0b10001000, 0b10001000, //Y 0b10001000, 0b10001000, 0b10001000, 0b01010000, 0b00100000, 0b00100000, 0b00100000, //Z 0b11111000, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000, 0b11111000, //figure1 0b01110000, 0b00100000, 0b01110000, 0b10101000, 0b00100000, 0b01010000, 0b10001000, //figure2 0b01110000, 0b10101000, 0b01110000, 0b00100000, 0b00100000, 0b01010000, 0b10001000, //blank 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 }; //------------------------------------------------------------------------------ //3x5 font numbers, then letters flash char smallbitmap[49][5]= { //0 0b11101110, 0b10101010, 0b10101010, 0b10101010, 0b11101110, //1 0b01000100, 0b11001100, 0b01000100, 0b01000100, 0b11101110, //2 0b11101110, 0b00100010, 0b11101110, 0b10001000, 0b11101110, //3 0b11101110, 0b00100010, 0b11101110, 0b00100010, 0b11101110, //4 0b10101010, 0b10101010, 0b11101110, 0b00100010, 0b00100010, //5 0b11101110, 0b10001000, 0b11101110, 0b00100010, 0b11101110, //6 0b11001100, 0b10001000, 0b11101110, 0b10101010, 0b11101110, //7 0b11101110, 0b00100010, 0b01000100, 0b10001000, 0b10001000, //8 0b11101110, 0b10101010, 0b11101110, 0b10101010, 0b11101110, //9 0b11101110, 0b10101010, 0b11101110, 0b00100010, 0b01100110, //10 - : 0b00000000, 0b01000100, 0b00000000, 0b01000100, 0b00000000, //11 - = 0b00000000, 0b11101110, 0b00000000, 0b11101110, 0b00000000, //12 - blank 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, //13 - A 0b11101110, 0b10101010, 0b11101110, 0b10101010, 0b10101010, //14 - B 0b11001100, 0b10101010, 0b11101110, 0b10101010, 0b11001100, //15 - C 0b11101110, 0b10001000, 0b10001000, 0b10001000, 0b11101110, //16 - D 0b11001100, 0b10101010, 0b10101010, 0b10101010, 0b11001100, //17 - E 0b11101110, 0b10001000, 0b11101110, 0b10001000, 0b11101110, //18 - F 0b11101110, 0b10001000, 0b11101110, 0b10001000, 0b10001000, //19 - G 0b11101110, 0b10001000, 0b10001000, 0b10101010, 0b11101110, //20 - H 0b10101010, 0b10101010, 0b11101110, 0b10101010, 0b10101010, //21 - I 0b11101110, 0b01000100, 0b01000100, 0b01000100, 0b11101110, //22 - J 0b00100010, 0b00100010, 0b00100010, 0b10101010, 0b11101110, //23 - K 0b10001000, 0b10101010, 0b11001100, 0b11001100, 0b10101010, //24 - L 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b11101110, //25 - M 0b10101010, 0b11101110, 0b11101110, 0b10101010, 0b10101010, //26 - N 0b00000000, 0b11001100, 0b10101010, 0b10101010, 0b10101010, //27 - O 0b01000100, 0b10101010, 0b10101010, 0b10101010, 0b01000100, //28 - P 0b11101110, 0b10101010, 0b11101110, 0b10001000, 0b10001000, //29 - Q 0b01000100, 0b10101010, 0b10101010, 0b11101110, 0b01100110, //30 - R 0b11101110, 0b10101010, 0b11001100, 0b11101110, 0b10101010, //31 - S 0b11101110, 0b10001000, 0b11101110, 0b00100010, 0b11101110, //32 - T 0b11101110, 0b01000100, 0b01000100, 0b01000100, 0b01000100, //33 - U 0b10101010, 0b10101010, 0b10101010, 0b10101010, 0b11101110, //34 - V 0b10101010, 0b10101010, 0b10101010, 0b10101010, 0b01000100, //35 - W 0b10101010, 0b10101010, 0b11101110, 0b11101110, 0b10101010, //36 - X 0b00000000, 0b10101010, 0b01000100, 0b01000100, 0b10101010, //37 - Y 0b10101010, 0b10101010, 0b01000100, 0b01000100, 0b01000100, //38 - Z 0b11101110, 0b00100010, 0b01000100, 0b10001000, 0b11101110, //39 - Player ship 0b01000100, 0b11101110, 0b01000100, 0b11101110, 0b10101010, //40 - Enemy ship #1 0b10101010, 0b01000100, 0b11101110, 0b01000100, 0b01000100, //41 - Enemy ship #2 0b11101110, 0b01000100, 0b11101110, 0b11101110, 0b01000100, //42 - Enemy ship #3 0b11101110, 0b01000100, 0b11101110, 0b10101010, 0b11101110, //43 - Enemy ship #4 0b10101010, 0b11101110, 0b01000100, 0b11101110, 0b11101110, //44 - Enemy ship #5 0b10101010, 0b11101110, 0b11101110, 0b01000100, 0b01000100, //45 - ! 0b01000100, 0b01000100, 0b01000100, 0b00000000, 0b01000100, //46 - Invulnerable player ship 0b01000100, 0b11101110, 0b11101110, 0b11101110, 0b11101110, //47 - death of player ship 0b10101010, 0b01000100, 0b10101010, 0b01000100, 0b10101010, //48 - continuing death of player ship 0b00000000, 0b00100010, 0b00000000, 0b00000000, 0b10001000 }; //============================================================================== //============================================================================== // Video Generation Code //============================================================================== //This is the sync generator and raster generator. It MUST be entered from //sleep mode to get accurate timing of the sync pulses #pragma warn- interrupt [TIM1_COMPA] void tim1_cmpA(void) { //start the Horizontal sync pulse PORTD = syncON; //update the curent scanline number LineCount ++ ; //begin inverted (Vertical) synch after line 247 if (LineCount==248) { syncON = 0b00100000; syncOFF = 0; } //back to regular sync after line 250 if (LineCount==251) { syncON = 0; syncOFF = 0b00100000; } //start new frame after line 262 if (LineCount==263) { LineCount = 1; } delay_us(2); //adjust to make 5 us pulses //end sync pulse PORTD = syncOFF; if (LineCount=ScreenTop) { //compute byte index for beginning of the next line //left-shift 4 would be individual lines // <<3 means line-double the pixels //The 0xfff8 truncates the odd line bit //i=(LineCount-ScreenTop)<<3 & 0xfff8; // #asm push r16 lds r12, _LineCount lds r13, _Linecount+1 ldi r16, 30 sub r12, r16 ldi r16,0 sbc r13, r16 lsl r12 rol r13 lsl r12 rol r13 lsl r12 rol r13 mov r16,r12 andi r16,0xf0 mov r12,r16 pop r16 #endasm //load 16 registers with screen info #asm push r14 push r15 push r16 push r17 push r18 push r19 push r26 push r27 ldi r26,low(_screen) ;base address of screen ldi r27,high(_screen) add r26,r12 ;offset into screen (add i) adc r27,r13 ld r4,x+ ;load 16 registers and inc pointer ld r5,x+ ld r6,x+ ld r7,x+ ld r8,x+ ld r9,x+ ld r10,x+ ld r11,x+ ld r12,x+ ld r13,x+ ld r14,x+ ld r15,x+ ld r16,x+ ld r17,x+ ld r18,x+ ld r19,x pop r27 pop r26 #endasm delay_us(4); //adjust to center image on screen //blast 16 bytes to the screen #asm ;but first a macro to make the code shorter ;the macro takes a register number as a parameter ;and dumps its bits serially to portD.6 ;the nop can be eliminated to make the display narrower .macro videobits ;regnum BST @0,7 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,6 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,5 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,4 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,3 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,2 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,1 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,0 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 .endm videobits r4 ;video line -- byte 1 videobits r5 ;byte 2 videobits r6 ;byte 3 videobits r7 ;byte 4 videobits r8 ;byte 5 videobits r9 ;byte 6 videobits r10 ;byte 7 videobits r11 ;byte 8 videobits r12 ;byte 9 videobits r13 ;byte 10 videobits r14 ;byte 11 videobits r15 ;byte 12 videobits r16 ;byte 13 videobits r17 ;byte 14 videobits r18 ;byte 15 videobits r19 ;byte 16 clt ;clear video after the last pixel on the line IN R30,0x12 BLD R30,6 OUT 0x12,R30 pop r19 pop r18 pop r17 pop r16 pop r15 pop r14 #endasm } } #pragma warn+ //------------------------------------------------------------------------------ //plot one point //at x,y with color 1=white 0=black 2=invert #pragma warn- void video_pt(char x, char y, char c) { #asm ; i=(x>>3) + ((int)y<<4) ; the byte with the pixel in it push r16 ldd r30,y+2 ;get x lsr r30 lsr r30 lsr r30 ;divide x by 8 ldd r12,y+1 ;get y lsl r12 ;mult y by 16 clr r13 lsl r12 rol r13 lsl r12 rol r13 lsl r12 rol r13 add r12, r30 ;add in x/8 ;v2 = screen[i]; r5 ;v3 = pos[x & 7]; r6 ;v4 = c r7 ldi r30,low(_screen) ldi r31,high(_screen) add r30, r12 adc r31, r13 ld r5,Z ;get screen byte ldd r26,y+2 ;get x ldi r27,0 andi r26,0x07 ;form x & 7 ldi r30,low(_pos*2) ldi r31,high(_pos*2) add r30,r26 adc r31,r27 lpm r6,Z ld r16,y ;get c ;if (v4==1) screen[i] = v2 | v3 ; ;if (v4==0) screen[i] = v2 & ~v3; ;if (v4==2) screen[i] = v2 ^ v3 ; cpi r16,1 brne tst0 or r5,r6 tst0: cpi r16,0 brne tst2 com r6 and r5,r6 tst2: cpi r16,2 brne writescrn eor r5,r6 writescrn: ldi r30,low(_screen) ldi r31,high(_screen) add r30, r12 adc r31, r13 st Z, r5 ;write the byte back to the screen pop r16 #endasm } #pragma warn+ //------------------------------------------------------------------------------ // put a big character on the screen // c is index into bitmap void video_putchar(char x, char y, char c) { v7 = x; for (v6=0;v6<7;v6++) { v1 = bitmap[c][v6]; v8 = y+v6; video_pt(v7, v8, (v1 & 0x80)==0x80); video_pt(v7+1, v8, (v1 & 0x40)==0x40); video_pt(v7+2, v8, (v1 & 0x20)==0x20); video_pt(v7+3, v8, (v1 & 0x10)==0x10); video_pt(v7+4, v8, (v1 & 0x08)==0x08); } } //------------------------------------------------------------------------------ // put a string of big characters on the screen void video_puts(char x, char y, char *str) { char i ; for (i=0; str[i]!=0; i++) { if (str[i]>=0x30 && str[i]<=0x3a) video_putchar(x,y,str[i]-0x30); else video_putchar(x,y,str[i]-0x40+9); x = x+6; } } //------------------------------------------------------------------------------ // put a small character on the screen // x-cood must be on divisible by 4 // c is index into bitmap void video_smallchar(char x, char y, char c) { char mask; i=((int)x>>3) + ((int)y<<4) ; if (x == (x & 0xf8)) mask = 0x0f; //f8 else mask = 0xf0; screen[i] = (screen[i] & mask) | (smallbitmap[c][0] & ~mask); screen[i+16] = (screen[i+16] & mask) | (smallbitmap[c][1] & ~mask); screen[i+32] = (screen[i+32] & mask) | (smallbitmap[c][2] & ~mask); screen[i+48] = (screen[i+48] & mask) | (smallbitmap[c][3] & ~mask); screen[i+64] = (screen[i+64] & mask) | (smallbitmap[c][4] & ~mask); } //------------------------------------------------------------------------------ // put a small character on the screen (unaligned) // c is index into bitmap // does not have to be mod 4 aligned // screen is 16 bytes wide = 128 pixels // 100 bytes high = 100 pixels // We first load the existing data from the screen. // Then a line of the 3x5 bitmap is loaded. // We zero out the bits that will be written to // and zero out the bits we don't use in the bitmap. // Finally, we add those together and write. void video_smallchar_ua(char x, char y, char c) { unsigned int p; unsigned char z; p = ((int)y * 16) + ((int)x / 8); //must be less than 1600 z = x % 8; //read the data readdata = (unsigned char)screen[p]; //load left byte readdata = readdata << 8; //shift to upper 8 bits readdata += (unsigned char)screen[p+1]; //load right byte readdata = readdata & datamask[z]; //clear the bits to be written to //read the bitmap //one line of the char to be written (3 bits) bitmaptemp = (unsigned char)smallbitmap[c][0]; bitmaptemp = bitmaptemp & 0x00e0; bitmaptemp = bitmaptemp << (8 - z); //shift bits into writing position //merge //write the new bits (can use | instead of +) readdata = readdata + bitmaptemp; //writeback screen[p] = (readdata >> 8) & 0x00ff; //write left byte screen[p+1] = readdata; //write right byte (int to char conversion) //next line p += 16; readdata = (unsigned char)screen[p]; readdata = readdata << 8; readdata += (unsigned char)screen[p+1]; readdata = readdata & datamask[z]; bitmaptemp = (unsigned char)smallbitmap[c][1]; bitmaptemp = bitmaptemp & 0x00e0; bitmaptemp = bitmaptemp << (8 - z); readdata = readdata + bitmaptemp; screen[p] = (readdata >> 8) & 0x00ff; screen[p+1] = readdata; //next line p += 16; readdata = (unsigned char)screen[p]; readdata = readdata << 8; readdata += (unsigned char)screen[p+1]; readdata = readdata & datamask[z]; bitmaptemp = (unsigned char)smallbitmap[c][2]; bitmaptemp = bitmaptemp & 0x00e0; bitmaptemp = bitmaptemp << (8 - z); readdata = readdata + bitmaptemp; screen[p] = (readdata >> 8) & 0x00ff; screen[p+1] = readdata; //next line p += 16; readdata = (unsigned char)screen[p]; readdata = readdata << 8; readdata += (unsigned char)screen[p+1]; readdata = readdata & datamask[z]; bitmaptemp = (unsigned char)smallbitmap[c][3]; bitmaptemp = bitmaptemp & 0x00e0; bitmaptemp = bitmaptemp << (8 - z); readdata = readdata + bitmaptemp; screen[p] = (readdata >> 8) & 0x00ff; screen[p+1] = readdata; //next line p += 16; readdata = (unsigned char)screen[p]; readdata = readdata << 8; readdata += (unsigned char)screen[p+1]; readdata = readdata & datamask[z]; bitmaptemp = (unsigned char)smallbitmap[c][4]; bitmaptemp = bitmaptemp & 0x00e0; bitmaptemp = bitmaptemp << (8 - z); readdata = readdata + bitmaptemp; screen[p] = (readdata >> 8) & 0x00ff; screen[p+1] = readdata; } //------------------------------------------------------------------------------ // put a small character on the screen // c is index into bitmap // does not have to be mod 4 aligned // uses the bitmap as a mask - // toggles pixels on screen that are // high in the bitmap void video_smallchar_ua_mask(char x, char y, char c) { v7 = x; for (v6=0;v6<5;v6++) { v1 = smallbitmap[c][v6]; v8 = y+v6; if ((v1 & 0x80)==0x80) video_pt(v7, v8, 2); if ((v1 & 0x40)==0x40) video_pt(v7+1, v8, 2); if ((v1 & 0x20)==0x20) video_pt(v7+2, v8, 2); } } //------------------------------------------------------------------------------ // put a string of small characters on the screen // x-cood must be on divisible by 4 void video_putsmalls(char x, char y, char *str) { char i ; for (i=0; str[i]!=0; i++) { if (str[i]>=0x30 && str[i]<=0x3a) video_smallchar(x,y,str[i]-0x30); else video_smallchar(x,y,str[i]-0x40+12); x = x+4; } } //------------------------------------------------------------------------------ //plot a line //at x1,y1 to x2,y2 with color 1=white 0=black 2=invert //NOTE: this function requires signed chars //Code is from David Rodgers, //"Procedural Elements of Computer Graphics",1985 void video_line(char x1, char y1, char x2, char y2, char c) { int e; signed char dx,dy,j, temp; signed char s1,s2, xchange; signed char x,y; x = x1; y = y1; dx = cabs(x2-x1); dy = cabs(y2-y1); s1 = csign(x2-x1); s2 = csign(y2-y1); xchange = 0; if (dy>dx) { temp = dx; dx = dy; dy = temp; xchange = 1; } e = ((int)dy<<1) - dx; for (j=0; j<=dx; j++) { video_pt(x,y,c) ; if (e>=0) { if (xchange==1) x = x + s1; else y = y + s2; e = e - ((int)dx<<1); } if (xchange==1) y = y + s2; else x = x + s1; e = e + ((int)dy<<1); } } //------------------------------------------------------------------------------ //draw a rectangle void video_rect(char x1, char y1, char x2, char y2, char c) { unsigned char j; for (j = y1; j <= y2; j++) video_line(x1, j, x2, j, c); } //------------------------------------------------------------------------------ //draw a black vertical line //requires y2 > y1 void video_vertline_black(char x1, char y1, char y2) { unsigned int p; unsigned char z; unsigned char vertmask; p = ((int)y1 * 16) + ((int)x1 / 8); //must be less than 1600 z = x1 % 8; vertmask = linemask[z]; for (z = y1; z <= y2; z++) { screen[p] &= vertmask; p += 16; } } //------------------------------------------------------------------------------ // Draws the logo on the screen // logo is 80x32 pixels = 10 x 32 bytes void logo_draw() { unsigned int i, j; for (i = 8; i < 18; i++) for (j = 26; j < 58; j++) screen[j * 16 + i - 7] = logo[j - 26][i - 8]; } //------------------------------------------------------------------------------ // clears the logo drawn by logo_draw() void logo_blank() { unsigned int i, j; for (i = 8; i < 18; i++) for (j = 26; j < 58; j++) screen[j * 16 + i - 7] = 0; } //------------------------------------------------------------------------------ //return the value of one point //at x,y with color 1=white 0=black 2=invert char video_set(char x, char y) { //The following construction //detects exactly one bit at the x,y location i=((int)x>>3) + ((int)y<<4) ; return ( screen[i] & 1<<(7-(x & 0x7))); } //============================================================================== //============================================================================== // Game Initialization //============================================================================== void main(void) { // set up the ports and timers //init timer 1 to generate sync OCR1A = lineTime; //One NTSC line TCCR1B = 9; //full speed; clear-on-match TCCR1A = 0x00; //turn off pwm and oc lines TIMSK = 0x10; //enable interrupt T1 cmp //begin init ports DDRD = 0xf0; //video out and switches //D.5 is sync:1000 ohm + diode to 75 ohm resistor //D.6 is video:330 ohm + diode to 75 ohm resistor DDRB = 0x00; //gamepad input port PORTB = 0xff; //enable pullups DDRC = 0x00; //gamepad input output PORTC = 0xff; //enable pullups //initialize synch constants LineCount = 1; syncON = 0b00000000; syncOFF = 0b00100000; //init game vars initCount = 0; //initialize state counter gameState = title; //start in title screen menuitem = 0; //default cursor to 1 player mode buttontimer = 10; //initialize debounce timer //single player mode init playerx = 1; playery = 90; //player ship init pos pgraphic = 39; //set player graphic to non-invulnerable lives = 3; //player lives init bullet = 0; //no player bullet shipCount = numShips; //init number of enemy ships attacking = 0; //no attacking ship //versus mode init player2x = 1; //player 2 ship init pos player2y = 5; lives2 = 3; //init player 2 lives //Game screen setup //draw game boundaries video_line(0, 0, 126, 0, 1); video_line(0, 99, 126, 99, 1); video_line(0, 0, 0, 99, 1); video_line(126, 0, 126, 99, 1); video_line(91, 0, 91, 99, 1); //draw initial logo logo_draw(); //print "1 PLAYER" video_smallchar_ua(29, 64, 1); video_smallchar_ua(33, 64, 12); video_smallchar_ua(37, 64, 28); video_smallchar_ua(41, 64, 24); video_smallchar_ua(45, 64, 13); video_smallchar_ua(49, 64, 37); video_smallchar_ua(53, 64, 17); video_smallchar_ua(57, 64, 30); //print "VERSUS" video_smallchar_ua(33, 73, 34); video_smallchar_ua(37, 73, 17); video_smallchar_ua(41, 73, 30); video_smallchar_ua(45, 73, 31); video_smallchar_ua(49, 73, 33); video_smallchar_ua(53, 73, 31); //highlight menu option video_smallchar_ua(25, 64, 47); video_smallchar_ua(61, 64, 47); //print SCORE video_smallchar(96, 4, 31); video_smallchar(100, 4, 15); video_smallchar(104, 4, 27); video_smallchar(108, 4, 30); video_smallchar(112, 4, 17); video_smallchar(116, 4, 10); //print 00000 video_smallchar_ua(96, 11, 0); video_smallchar_ua(100, 11, 0); video_smallchar_ua(104, 11, 0); video_smallchar_ua(108, 11, 0); video_smallchar_ua(112, 11, 0); //print LIVES video_smallchar_ua(96, 22, 24); video_smallchar_ua(100, 22, 21); video_smallchar_ua(104, 22, 34); video_smallchar_ua(108, 22, 17); video_smallchar_ua(112, 22, 31); video_smallchar_ua(116, 22, 10); //print 3 ships video_smallchar_ua(100, 29, 39); video_smallchar_ua(104, 29, 39); video_smallchar_ua(108, 29, 39); //enable sleep mode MCUCR = 0b10000000; #asm ("sei"); //============================================================================== //The following loop executes once/video line during lines //1-230, then does all of the frame-end processing 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) { //============================================================================== // Button debounce //============================================================================== //simple button debounce //output signal goes active once button is released //initialize debounce variables buttonCpush = 0; buttonBpush = 0; buttonUPpush = 0; buttonDOWNpush = 0; buttonLEFTpush = 0; buttonRIGHTpush = 0; buttonB2push = 0; //check timing for debouncing if (--buttontimer == 0) { buttontimer = 5; //reset debounce timer //if button is pressed now if (~PINB.0) buttonRIGHTwaspressed = 1; //if button is not pressed now but was before else if (buttonRIGHTwaspressed) { buttonRIGHTwaspressed = 0; buttonRIGHTpush = 1; //debounce output } if (~PINB.1) buttonLEFTwaspressed = 1; else if (buttonLEFTwaspressed) { buttonLEFTwaspressed = 0; buttonLEFTpush = 1; } if (~PINB.2) buttonDOWNwaspressed = 1; else if (buttonDOWNwaspressed) { buttonDOWNwaspressed = 0; buttonDOWNpush = 1; } if (~PINB.3) buttonUPwaspressed = 1; else if (buttonUPwaspressed) { buttonUPwaspressed = 0; buttonUPpush = 1; } if (~PINB.4) buttonCwaspressed = 1; else if (buttonCwaspressed) { buttonCwaspressed = 0; buttonCpush = 1; } if (~PINB.5) buttonBwaspressed = 1; else if (buttonBwaspressed) { buttonBwaspressed = 0; buttonBpush = 1; } if (~PINC.5) buttonB2waspressed = 1; else if (buttonB2waspressed) { buttonB2waspressed = 0; buttonB2push = 1; } } //============================================================================== //============================================================================== // Debounced button actions //============================================================================== //check scrolling of title screen cursor if (buttonDOWNpush && (gameState == title) && (menuitem == 0)) { menuitem = 1; //erase old cursor position video_smallchar_ua(25, 64, 12); video_smallchar_ua(61, 64, 12); //move cursor to new position video_smallchar_ua(25, 73, 47); video_smallchar_ua(61, 73, 47); } if (buttonUPpush && (gameState == title) && (menuitem == 1)) { menuitem = 0; //erase old cursor position video_smallchar_ua(25, 64, 47); video_smallchar_ua(61, 64, 47); //move cursor to new position video_smallchar_ua(25, 73, 12); video_smallchar_ua(61, 73, 12); } //at title screen, if B is pressed, show high scores if (buttonBpush && (gameState == title)) { //erase title logo_blank(); //erase menu items for (count = 25; count <= 63; count++) video_vertline_black(count, 63, 78); scoretimer = 11; //initialize high score screen timer gameState = scorescreen; //go to high score screen } //read button c, start button if (buttonCpush) { //erase title screen, start the init if (gameState == title) { //erase title logo_blank(); //erase menu items for (count = 25; count <= 63; count++) video_vertline_black(count, 63, 78); //1 player game if (menuitem == 0) { gameState = init; //go to game initialize playerx = 44; //player ship init x pos playery = 90; //player ship init y pos bullet = 0; //no bullets being fired right now } //cursor on versus mode else if (menuitem == 1) gameState = twoplayerinit; //go to versus mode } //pause/unpause the game else if (gameState == game) { gameState = pause; //go to pause state //print "PAUSED" video_smallchar_ua(32, 74, 28); video_smallchar_ua(36, 74, 13); video_smallchar_ua(40, 74, 33); video_smallchar_ua(44, 74, 31); video_smallchar_ua(48, 74, 17); video_smallchar_ua(52, 74, 16); } else if (gameState == pause) { gameState = delaygame; //delay the game to avoid glitching //clear "PAUSED" video_smallchar_ua(32, 74, 12); video_smallchar_ua(36, 74, 12); video_smallchar_ua(40, 74, 12); video_smallchar_ua(44, 74, 12); video_smallchar_ua(48, 74, 12); video_smallchar_ua(52, 74, 12); //reset the game if conditions are met if (~PINB.5) { gameState = clearscreen; //clear the screen //clear "PAUSED" video_smallchar_ua(32, 74, 12); video_smallchar_ua(36, 74, 12); video_smallchar_ua(40, 74, 12); video_smallchar_ua(44, 74, 12); video_smallchar_ua(48, 74, 12); video_smallchar_ua(52, 74, 12); } } } //============================================================================== //============================================================================== // Single Player mode initialization //============================================================================== //initialize single player mode if (gameState == init) { initCount = 0; //set counter to 0 gameState = drawlevel; //go to draw level screen scrollDirection = 0; //init enemy ships to scroll left scrollTimer = 15; //enemy ships scroll once every 15 frames scrollVal = 15; bulletTimer = 7; //bullets scroll once every 7 frames bulletTimerVal = 7; bulletsPerLevel = 5; //5 bullets on level 1 invulnerable = 0; //player not invulnerable attacking = 0; //no attacking ships curpos = 40; //initialize cursor position //initialize lives lives = 3; //3 lives shipCount = numShips; //25 enemy ships score = 0; //0 score lifeFlag = 0; //player does not get extra life deathFlag = 0; //player has not died deathCount = 0; //init death animation timer //print 00000 video_smallchar(96, 11, 0); video_smallchar(100, 11, 0); video_smallchar(104, 11, 0); video_smallchar(108, 11, 0); video_smallchar(112, 11, 0); //make enemy ships alive for (count = 0; count < shipsPerRow; count++) { row1Live[count] = 1; row2Live[count] = 1; row3Live[count] = 1; row4Live[count] = 1; row5Live[count] = 1; } //kill all enemy bullets for (count = 0; count < bulletsPerLevel; count++) ebulletslive[count] = 0; hPos = 30; //init enemy ship position level = 1; //init level //print level xx video_smallchar_ua(96, 84, 24); video_smallchar_ua(100, 84, 17); video_smallchar_ua(104, 84, 34); video_smallchar_ua(108, 84, 17); video_smallchar_ua(112, 84, 24); video_smallchar_ua(116, 84, 10); video_smallchar_ua(104, 90, 0); video_smallchar_ua(108, 90, 1); } //============================================================================== //============================================================================== // Versus Mode Initialization //============================================================================== //initialize versus mode if (gameState == twoplayerinit) { initCount = 0; //initialize counter gameState = twoplayergame; //set game to versus mode curpos = 40; //init cursor position //initialize lives lives = 3; //player 1 has 3 lives lives2 = 3; //player 2 has 3 lives //print 00000 video_smallchar_ua(96, 11, 0); video_smallchar_ua(100, 11, 0); video_smallchar_ua(104, 11, 0); video_smallchar_ua(108, 11, 0); video_smallchar_ua(112, 11, 0); //player 1 ship init pos playerx = 44; playery = 90; video_smallchar_ua(playerx, playery, 39); //draw player 1 graphic //player 2 ship init pos player2x = 44; player2y = 5; video_smallchar_ua(player2x, player2y, 42); //draw player 2 graphic //initialize liveness of player bullets for (count = 0; count < 10; count++) { bullet1[count] = 0; bullet2[count] = 0; } //draw 3 lives for player 2 video_smallchar_ua(100, 37, 42); video_smallchar_ua(104, 37, 42); video_smallchar_ua(108, 37, 42); } //============================================================================== //============================================================================== // Versus Mode Gameplay //============================================================================== //in versus mode, play the game if (gameState == twoplayergame) { //poll gamepad and move player 1's ship if ((~PINB.0) && (playerx < 88)) //move player 1 to the right { //erase old ship position video_vertline_black(playerx, playery, playery+4); //draw new ship position video_smallchar_ua(++playerx, playery, 39); } else if ((~PINB.1) && (playerx > 1)) //mave player 1 to the left { video_vertline_black(playerx+2, playery, playery+4); video_smallchar_ua(--playerx, playery, 39); } else //don't move player 1 video_smallchar_ua(playerx, playery, 39); //poll player 1's fire button and create a bullet when valid if (buttonBpush) { //find first available bullet for (bullet1Count = 0; bullet1Count < 10; bullet1Count++) if (!bullet1[bullet1Count]) break; //if player 1 hasn't shot the max # of bullets yet if (bullet1Count < 10) { //create bullet position/liveness bullet1x[bullet1Count] = playerx + 1; bullet1y[bullet1Count] = 89; bullet1[bullet1Count] = 1; } } //poll gamepad and move player 2's ship if ((~PINC.0) && (player2x < 88)) //move player 2 to the right { //erase old ship position video_vertline_black(player2x, player2y, player2y+4); //draw new ship position video_smallchar_ua(++player2x, player2y, 42); } else if ((~PINC.1) && (player2x > 1)) //mave player 2 to the left { video_vertline_black(player2x+2, player2y, player2y+4); video_smallchar_ua(--player2x, player2y, 42); } else //don't move player 2 video_smallchar_ua(player2x, player2y, 42); //poll player 2's fire button and create a bullet when valid if (buttonB2push) { //find first available bullet for (bullet2Count = 0; bullet2Count < 10; bullet2Count++) if (!bullet2[bullet2Count]) break; //if player 2 has not shot the max # of bullets if (bullet2Count < 10) { //create bullet position/liveness bullet2x[bullet2Count] = player2x + 1; bullet2y[bullet2Count] = 11; bullet2[bullet2Count] = 1; } } //checks if a bullet has hit player2 //need to check this each frame for (count = 0; count < 10; count++) { //live ammo if (bullet1[count]) { //check for a collision with player 2 if ((bullet1x[count] >= player2x) && (bullet1x[count] <= (player2x + 2)) && (bullet1y[count] >= player2y) && (bullet1y[count] < (player2y + 5))) { //erase the bullet video_pt(bullet1x[count], bullet1y[count], 0); bullet1[count] = 0; //kill the bullet lives2--; //decrement player 2's lives //erase lives on side of screen as necessary if (lives2 == 2) video_smallchar_ua(108, 37, 12); else if (lives2 == 1) video_smallchar_ua(104, 37, 12); else //player 2 has no more lives { video_smallchar_ua(100, 37, 12); gameState = player1win; //draw player 1 win screen } } } } //checks if a bullet has hit player 1 //need to check this each frame for (count = 0; count < 10; count++) { //live ammo if (bullet2[count]) { //check for a collision with the player 1 if ((bullet2x[count] >= playerx) && (bullet2x[count] <= (playerx + 2)) && (bullet2y[count] >= playery) && (bullet2y[count] < (playery + 5))) { //erase the bullet video_pt(bullet2x[count], bullet2y[count], 0); bullet2[count] = 0; //kill the bullet lives--; //decrement player 1's lives //erase lives on side of screen as necessary if (lives == 2) video_smallchar_ua(108, 29, 12); else if (lives == 1) video_smallchar_ua(104, 29, 12); else //player 1 has no more lives { video_smallchar_ua(100, 29, 12); gameState = player2win; //draw player 2 win screen } } } } //update bullets on the screen for (count = 0; count < 10; count++) { //player 1's live ammo if (bullet1[count]) { //kill runaway bullets if (bullet1y[count] <= 1) { //erase the bullet video_pt(bullet1x[count], bullet1y[count], 0); bullet1[count] = 0; //kill the bullet } //move the bullet down otherwise else { //erase the bullet's old position video_pt(bullet1x[count], bullet1y[count], 0); //change y coord of bullet bullet1y[count] = bullet1y[count] - 1; //draw bullet in new position video_pt(bullet1x[count], bullet1y[count], 1); } } //player 2's live ammo if (bullet2[count]) { //kill runaway bullets if (bullet2y[count] >= 98) { video_pt(bullet2x[count], bullet2y[count], 0); bullet2[count] = 0; } //move the bullet down otherwise else { video_pt(bullet2x[count], bullet2y[count], 0); bullet2y[count] = bullet2y[count] + 1; video_pt(bullet2x[count], bullet2y[count], 1); } } } } //============================================================================== //============================================================================== // Versus Mode Victory Screens //============================================================================== //player 1 win screen if (gameState == player1win) { //erase player 1 ship video_smallchar_ua(playerx, playery, 12); //erase player 2 ship video_smallchar_ua(player2x, player2y, 12); //erase bullets on the screen for (count = 0; count < 10; count++) { if (bullet1[count]) video_pt(bullet1x[count], bullet1y[count], 0); if (bullet2[count]) video_pt(bullet2x[count], bullet2y[count], 0); } //print player 1 wins video_smallchar_ua(19, 45, 28); video_smallchar_ua(23, 45, 24); video_smallchar_ua(27, 45, 13); video_smallchar_ua(31, 45, 37); video_smallchar_ua(35, 45, 17); video_smallchar_ua(39, 45, 30); video_smallchar_ua(47, 45, 1); video_smallchar_ua(55, 45, 35); video_smallchar_ua(59, 45, 21); video_smallchar_ua(63, 45, 26); video_smallchar_ua(67, 45, 31); clearCounter = 0; //initialize clear counter gameState = clear2player; //clear the screen } //player 2 win screen if (gameState == player2win) { //erase game screen //erase player ships video_smallchar_ua(playerx, playery, 12); video_smallchar_ua(player2x, player2y, 12); //erase players bullets for (count = 0; count < 10; count++) { if (bullet1[count]) video_pt(bullet1x[count], bullet1y[count], 0); if (bullet2[count]) video_pt(bullet2x[count], bullet2y[count], 0); } //player 2 wins video_smallchar_ua(19, 45, 28); video_smallchar_ua(23, 45, 24); video_smallchar_ua(27, 45, 13); video_smallchar_ua(31, 45, 37); video_smallchar_ua(35, 45, 17); video_smallchar_ua(39, 45, 30); video_smallchar_ua(47, 45, 2); video_smallchar_ua(55, 45, 35); video_smallchar_ua(59, 45, 21); video_smallchar_ua(63, 45, 26); video_smallchar_ua(67, 45, 31); //erase player 2 lives from side of screen video_smallchar_ua(100, 37, 12); video_smallchar_ua(104, 37, 12); video_smallchar_ua(108, 37, 12); clearCounter = 0; //initialize clear counter gameState = clear2player; //clear the screen } //clear victory screen if (gameState == clear2player) { clearCounter++; //increment timer //if 4 seconds have passed, clear the victory screen if (clearCounter == 120) { clearCounter = 0; //reset clear counter gameState = clearscreen; //clear screen //erase player x wins video_smallchar_ua(19, 45, 12); video_smallchar_ua(23, 45, 12); video_smallchar_ua(27, 45, 12); video_smallchar_ua(31, 45, 12); video_smallchar_ua(35, 45, 12); video_smallchar_ua(39, 45, 12); video_smallchar_ua(47, 45, 12); video_smallchar_ua(55, 45, 12); video_smallchar_ua(59, 45, 12); video_smallchar_ua(63, 45, 12); video_smallchar_ua(67, 45, 12); } } //============================================================================== //============================================================================== // Single Player Mode Gameplay //============================================================================== //play the single player game if (gameState == game) { //scroll the enemy ships if (--scrollTimer <= 0) { scrollTimer = scrollVal; //reset scroll timer //make enemy bullets //get the first empty bullet position for (bulletCount = 0; bulletCount < bulletsPerLevel; bulletCount++) if (!ebulletslive[bulletCount]) break; //see if we can make another bullet if (bulletCount < bulletsPerLevel) { //randomly determine to fire an enemy bullet on this iteration someRandom = rand(); if (someRandom < probShoot) { someRandom = rand() % 6; //pick a column/attacking ship at random //shoot from the ships in formation if (someRandom != 5) { //set the x coordinate ebulletsx[bulletCount] = (someRandom * 7) + hPos + 1; //make the bullet live ebulletslive[bulletCount] = 1; //set the y coordinate if (row5Live[someRandom]) ebulletsy[bulletCount] = row5Pos + 5; else if (row4Live[someRandom]) ebulletsy[bulletCount] = row4Pos + 5; else if (row3Live[someRandom]) ebulletsy[bulletCount] = row3Pos + 5; else if (row2Live[someRandom]) ebulletsy[bulletCount] = row2Pos + 5; else if (row1Live[someRandom]) ebulletsy[bulletCount] = row1Pos + 5; // don't make it live if no ships else ebulletslive[bulletCount] = 0; } //attacking ship selected to shoot else { //only shoot if there is an attacking ship if (attacking) { //make the bullet live ebulletslive[bulletCount] = 1; //set x and y coords for the bullet ebulletsx[bulletCount] = xpos + 1; ebulletsy[bulletCount] = ypos + 5; } } } } //need to erase before drawing new ship positions so you don't //erase part of ship if (attacking) video_smallchar_ua(xpos, ypos, 12); //erase previous position //enemy ships scrolling to the left if (scrollDirection == 0) { //left edge of screen if (hPos == 1) { //draw vertical lines to erase ship artifacts for (count=0; count < shipsPerRow; count++) video_vertline_black(hPos + count*7, 2, row5Pos+5); hPos++; //start moving in other direction //reverse direction scrollDirection = 1; //redraw live ships one pos to the right for (count = 0; count < shipsPerRow; count++) { if (row1Live[count] == 1) video_smallchar_ua(hPos + count * 7, row1Pos, 42); if (row2Live[count] == 1) video_smallchar_ua(hPos + count * 7, row2Pos, 43); if (row3Live[count] == 1) video_smallchar_ua(hPos + count * 7, row3Pos, 44); if (row4Live[count] == 1) video_smallchar_ua(hPos + count * 7, row4Pos, 40); if (row5Live[count] == 1) video_smallchar_ua(hPos + count * 7, row5Pos, 41); } } //anywhere else on screen else { //draw vertical lines to erase ship artifacts for (count=0; count < shipsPerRow; count++) video_vertline_black(hPos+2 + count*7, 2, row5Pos+5); hPos--; //keep moving left //redraw live ships one pos to the left for(count = 0; count < shipsPerRow; count++) { if(row1Live[count] == 1) video_smallchar_ua(hPos + count * 7, row1Pos, 42); if (row2Live[count] == 1) video_smallchar_ua(hPos + count * 7, row2Pos, 43); if (row3Live[count] == 1) video_smallchar_ua(hPos + count * 7, row3Pos, 44); if (row4Live[count] == 1) video_smallchar_ua(hPos + count * 7, row4Pos, 40); if (row5Live[count] == 1) video_smallchar_ua(hPos + count * 7, row5Pos, 41); } } } //scrolling to the right else { //if at right edge of game screen if (hPos == 60) { //draw vertical lines to erase ship artifacts for (count=0; count < shipsPerRow; count++) video_vertline_black(hPos+2 + count*7, 2, row5Pos + 5); hPos--; //start moving left //reverse direction scrollDirection = 0; //draw live ships for (count = 0; count < shipsPerRow; count++) { if(row1Live[count] == 1) video_smallchar_ua(hPos + count * 7, row1Pos, 42); if (row2Live[count] == 1) video_smallchar_ua(hPos + count * 7, row2Pos, 43); if (row3Live[count] == 1) video_smallchar_ua(hPos + count * 7, row3Pos, 44); if (row4Live[count] == 1) video_smallchar_ua(hPos + count * 7, row4Pos, 40); if (row5Live[count] == 1) video_smallchar_ua(hPos + count * 7, row5Pos, 41); } } //anywhere else else { //draw vertical lines to erase ship artifacts for (count=0; count < shipsPerRow; count++) video_vertline_black(hPos + count*7, 2, row5Pos+5); hPos++; //keep moving right //draw live ships for(count = 0; count < shipsPerRow; count++) { if(row1Live[count] == 1) video_smallchar_ua(hPos + count * 7, row1Pos, 42); if (row2Live[count] == 1) video_smallchar_ua(hPos + count * 7, row2Pos, 43); if (row3Live[count] == 1) video_smallchar_ua(hPos + count * 7, row3Pos, 44); if (row4Live[count] == 1) video_smallchar_ua(hPos + count * 7, row4Pos, 40); if (row5Live[count] == 1) video_smallchar_ua(hPos + count * 7, row5Pos, 41); } } } //check if there's a ship attacking if (attacking) { //random number so ship moves a random # of spacees in x direction someRandom = rand() % 2 + 1; //home in on player ship if (playerx < xpos) { //move towards player ship if (xpos > 3) xpos = xpos - someRandom; else xpos = 1; } else if (playerx > xpos) { //move towards player ship if (xpos < 87) xpos = xpos + someRandom; else xpos = 88; } ypos++; //keep moving down the screen //find which ship to draw switch (row) { case 1: video_smallchar_ua(xpos, ypos, 42); break; case 2: video_smallchar_ua(xpos, ypos, 43); break; case 3: video_smallchar_ua(xpos, ypos, 44); break; case 4: video_smallchar_ua(xpos, ypos, 40); break; case 5: video_smallchar_ua(xpos, ypos, 41); break; } //if attacking ship at end of screen, put it back in ship formation if (ypos > 92) { attacking = 0; //ship no longer attacking video_smallchar_ua(xpos, ypos, 12); //erase the attacking ship ypos = 2; //reset y position to avoid glitches //find which row the ship came from switch (row) { case 1: row1Live[col] = 1; break; case 2: row2Live[col] = 1; break; case 3: row3Live[col] = 1; break; case 4: row4Live[col] = 1; break; case 5: row5Live[col] = 1; break; } } } //no attacking ship, make a ship attack else { //pick a ship to attack at random someRandom = rand() % 5; //pick the column at random //search for a live ship at the chosen column in each row of ships if (row5Live[someRandom]) { attacking = 1; //ship is now attacking //store original position of attacking ship row = 5; col = someRandom; //get x and y position of attacking ship xpos = hPos + someRandom * 7; ypos = row5Pos + 5; //delete ship from enemy ship formation row5Live[someRandom] = 0; //erase old ship position video_smallchar_ua(hPos + someRandom * 7, row5Pos, 12); //draw new ship position (attacking) video_smallchar_ua(xpos, ypos, 41); } else if (row4Live[someRandom]) { attacking = 1; row = 4; col = someRandom; xpos = hPos + someRandom * 7; ypos = row4Pos + 5; row4Live[someRandom] = 0; video_smallchar_ua(hPos + someRandom * 7, row4Pos, 12); video_smallchar_ua(xpos, ypos, 40); } else if (row3Live[someRandom]) { attacking = 1; row = 3; col = someRandom; xpos = hPos + someRandom * 7; ypos = row3Pos + 5; row3Live[someRandom] = 0; video_smallchar_ua(hPos + someRandom * 7, row3Pos, 12); video_smallchar_ua(xpos, ypos, 44); } else if (row2Live[someRandom]) { attacking = 1; row = 2; col = someRandom; xpos = hPos + someRandom * 7; ypos = row2Pos + 5; row2Live[someRandom] = 0; video_smallchar_ua(hPos + someRandom * 7, row2Pos, 12); video_smallchar_ua(xpos, ypos, 43); } else if (row1Live[someRandom]) { attacking = 1; row = 1; col = someRandom; xpos = hPos + someRandom * 7; ypos = row1Pos + 5; row1Live[someRandom] = 0; video_smallchar_ua(hPos + someRandom * 7, row1Pos, 12); video_smallchar_ua(xpos, ypos, 42); } } }//end scroll timer //countdown invulnerability timer if (invulnerable) { invulnerable--; //decrement timer pgraphic = 46; //select invulnerable player graphic } else pgraphic = 39; //select non-invulnerable player graphic //checks if a bullet has hit the player //need to check this each frame for (count = 0; count < bulletsPerLevel; count++) { //live ammo if (ebulletslive[count]) { //check for a collision with the player if ((ebulletsx[count] >= playerx) && (ebulletsx[count] <= (playerx + 2)) && (ebulletsy[count] >= playery) && (ebulletsy[count] < (playery + 5))) { //erase the enemy bullet video_pt(ebulletsx[count], ebulletsy[count], 0); //kill the bullet ebulletslive[count] = 0; //check if the player is invulnerable if (!invulnerable) { gameState = die; //draw death animation deathFlag = 1; //player has just died } } } } //check if an attacking ship has hit the player/player not invulnerable if ((ypos + 4 >= playery) && (ypos < playery + 5) && (xpos >= playerx - 2) && (xpos <= playerx + 2) && !invulnerable) { //erase attacking ship video_smallchar_ua(xpos, ypos, 12); ypos = 2; //reset y position of attacking ship gameState = die; //draw death animation deathFlag = 1; //player has just died attacking = 0; //destroy the enemy ship shipCount--; //decrement # of enemy ships } //updates live enemy bullets //runs every bulletTimer frames if (--bulletTimer <= 0) { bulletTimer = bulletTimerVal; //reset bullet timer //go through bullet arrays and move bullets for (count = 0; count < bulletsPerLevel; count++) { //live ammo if (ebulletslive[count]) { //kill runaway bullets if (ebulletsy[count] >= 98) { //erase the bullet video_pt(ebulletsx[count], ebulletsy[count], 0); //kill the bullet ebulletslive[count] = 0; } //move the bullet down otherwise else { //erase bullet old position video_pt(ebulletsx[count], ebulletsy[count], 0); //move the bullet down the screen ebulletsy[count] = ebulletsy[count] + 1; //draw the new position of the bullet video_pt(ebulletsx[count], ebulletsy[count], 1); } } } } //poll gamepad and move player's ship if ((~PINB.0) && (playerx < 88)) //move player to the right { //erase previous position of player ship video_vertline_black(playerx, playery, playery+4); //draw new position of player ship video_smallchar_ua(++playerx, playery, pgraphic); } else if ((~PINB.1) && (playerx > 1)) //move player to the left { video_vertline_black(playerx+2, playery, playery+4); video_smallchar_ua(--playerx, playery, pgraphic); } else //don't move the player video_smallchar_ua(playerx, playery, pgraphic); //poll the fire button and create a player bullet when valid if ((~PINB.5) && (!bullet)) { //store coordinates of player bullet bulletx = playerx + 1; bullety = 89; //make the bullet live bullet = 1; } //shot was fired, do update if (bullet) { //calculation of row of enemy ship being hit if (bullety >= row5Pos) {ptr = row5Live; activeRow = row5Pos; } else if (bullety >= row4Pos) {ptr = row4Live; activeRow = row4Pos; } else if (bullety >= row3Pos) {ptr = row3Live; activeRow = row3Pos; } else if (bullety >= row2Pos) {ptr = row2Live; activeRow = row2Pos; } else {ptr = row1Live; activeRow = row1Pos;} //column of enemy ship being hit bulletHit = (bulletx - hPos) / 7; //check if bullet hit an enemy ship if ((bullety <= (row5Pos + 4)) //bottom edge && (bullety >= 3) //top edge && (bulletx >= hPos) //left edge && (bulletx <= (hPos + 7 * shipsPerRow - 5)) //right edge && (((bulletx - hPos) % 7) < 3) //not in empty space between ships && (((bullety - 3) % 7) <= 4) //need valid column, live ship && ((bulletHit >= 0) && (bulletHit <= 4) && (ptr[bulletHit]))) { ptr[bulletHit] = 0; //kill the ship bullet = 0; //bullet inactive //erase the destroyed ship video_smallchar_ua(hPos + bulletHit * 7, activeRow, 12); video_pt(bulletx, bullety, 0); //erase the bullet shipCount--; //decrement # of enemy ships score++; //increment player score lifeFlag = 1; //if requirements met, player can gain a life } //if a bullet hit an attacking ship else if (attacking && (bulletx >= xpos) && (bulletx < xpos + 3) && (bullety >= ypos) && (bullety < ypos + 5)) { //erase the attacking ship video_smallchar_ua(xpos, ypos, 12); attacking = 0; //enemy not attacking bullet = 0; //bullet inactive video_pt(bulletx, bullety, 0); //erase the bullet shipCount--; //decrement # of enemy ships score++; //increment player score lifeFlag = 1; //player can gain a life } //no ship in bullet's path else { //erase the old bullet video_pt(bulletx, bullety, 0); //draw bullet in its new position video_pt(bulletx, --bullety, 1); //if bullet at top of screen if (bullety == 1) { bullet = 0; //kill the bullet video_pt(bulletx, bullety, 0); //erase the bullet } } //add a life if the score is a multiple of 100 if ((lives < 3) && (score % 100 == 0) && (score != 0) && lifeFlag) { lifeFlag = 0; //can't gain another life this point //increase the number of lives/draw another ship on side screen if (lives == 2) video_smallchar_ua(108, 29, 39); else if (lives == 1) video_smallchar_ua(104, 29, 39); lives++; } } //print score digits video_smallchar(96, 11, score / 10000); video_smallchar(100, 11, (score % 10000) / 1000); video_smallchar(104, 11, (score % 1000) / 100); video_smallchar(108, 11, (score % 100) / 10); video_smallchar(112, 11, (score % 10)); //check if the player won (no more enemy ships) if (!shipCount) gameState = win; //go to end of level screen } // gameState == game //============================================================================== //============================================================================== // Go To Next Level: //============================================================================== //Start new level if (gameState == win) { //make the game more difficult //change the scroll speed of enemy ships if (scrollTimer > 0) scrollVal--; scrollTimer = scrollVal; //initialize scroll timer scrollDirection = 0; //initialize scroll direction to left bulletTimerVal = scrollVal / 2 ; //increase speed of enemy bullets bulletTimer = bulletTimerVal; //initialize bullet scroll timer invulnerable = 0; //player isn't invulnerable shipCount = numShips; //25 enemy ships attacking = 0; //no attacking ships //erase the enemy bullets for (count = 0; count < bulletsPerLevel; count++) if (ebulletslive[count]) video_pt(ebulletsx[count], ebulletsy[count], 0); bulletsPerLevel = maxBullets; //number of bullets enemy ships shoot //make enemy ships alive for (count = 0; count < shipsPerRow; count++) { row1Live[count] = 1; row2Live[count] = 1; row3Live[count] = 1; row4Live[count] = 1; row5Live[count] = 1; } //kill all enemy bullets for (count = 0; count < bulletsPerLevel; count++) ebulletslive[count] = 0; hPos = 30; //initialize position of enemy ship formation gameState = drawlevel; //draw level screen level++; //increment the level //print level # in side screen video_smallchar_ua(104, 90, (char)(level / 10)); video_smallchar_ua(108, 90, (char)(level % 10)); } //draw the screen between levels if (gameState == drawlevel) { //check timer for this state if (levelscreenpause == 1) { //erase player ship video_smallchar_ua(playerx, playery, 12); //draw level xx video_smallchar_ua(29, 30, 24); video_smallchar_ua(33, 30, 17); video_smallchar_ua(37, 30, 34); video_smallchar_ua(41, 30, 17); video_smallchar_ua(45, 30, 24); video_smallchar_ua(53, 30, (char)(level / 10)); video_smallchar_ua(57, 30, (char)(level % 10)); } //clear the screen after 2 seconds, start the new level if (levelscreenpause == 60) { //clear video_smallchar_ua(29, 30, 12); video_smallchar_ua(33, 30, 12); video_smallchar_ua(37, 30, 12); video_smallchar_ua(41, 30, 12); video_smallchar_ua(45, 30, 12); video_smallchar_ua(53, 30, 12); video_smallchar_ua(57, 30, 12); playerx = 44; //initialize player position levelscreenpause = 0; //reset state timer gameState = game; //go to the game } levelscreenpause++; //increment timer } //============================================================================== //============================================================================== // Game Over //============================================================================== //Clear the game screen if (gameState == lose) { //erase the player ship video_smallchar_ua(playerx, playery, 12); //erase the enemy ships for (count = 0; count < 5; count++) { if (row1Live[count] == 1) video_smallchar_ua(hPos + count * 7, row1Pos, 12); if (row2Live[count] == 1) video_smallchar_ua(hPos + count * 7, row2Pos, 12); if (row3Live[count] == 1) video_smallchar_ua(hPos + count * 7, row3Pos, 12); if (row4Live[count] == 1) video_smallchar_ua(hPos + count * 7, row4Pos, 12); if (row5Live[count] == 1) video_smallchar_ua(hPos + count * 7, row5Pos, 12); } //erase the enemy bullets for (count = 0; count < bulletsPerLevel; count++) if (ebulletslive[count]) video_pt(ebulletsx[count], ebulletsy[count], 0); //erase the player bullet if (bullet) video_pt(bulletx, bullety, 0); //erase attacking ship if (attacking) video_smallchar_ua(xpos, ypos, 12); //erase level video_smallchar_ua(96, 84, 12); video_smallchar_ua(100, 84, 12); video_smallchar_ua(104, 84, 12); video_smallchar_ua(108, 84, 12); video_smallchar_ua(112, 84, 12); video_smallchar_ua(116, 84, 12); video_smallchar_ua(104, 90, 12); video_smallchar_ua(108, 90, 12); gameovertimer = 210; //initialize animation timer gameState = gameover; //draw the game over animation } //do game over animation if (gameState == gameover) { //start animation if (gameovertimer == 210) { //print game over on the sides of the game screen video_smallchar_ua(1, 43, 19); video_smallchar_ua(5, 43, 13); video_smallchar_ua(9, 43, 25); video_smallchar_ua(12, 43, 17); video_smallchar_ua(75, 43, 27); video_smallchar_ua(79, 43, 34); video_smallchar_ua(83, 43, 17); video_smallchar_ua(87, 43, 30); } //scroll game over else if ((gameovertimer > 183) && (gameovertimer < 210)) { //erase old game video_vertline_black(210 - gameovertimer, 43, 48); video_vertline_black(214 - gameovertimer, 43, 48); video_vertline_black(218 - gameovertimer, 43, 48); video_vertline_black(222 - gameovertimer, 43, 48); //draw new game video_smallchar_ua(211 - gameovertimer, 43, 19); video_smallchar_ua(215 - gameovertimer, 43, 13); video_smallchar_ua(219 - gameovertimer, 43, 25); video_smallchar_ua(223 - gameovertimer, 43, 17); //erase old over video_vertline_black(gameovertimer - 132, 43, 48); video_vertline_black(gameovertimer - 128, 43, 48); video_vertline_black(gameovertimer - 124, 43, 48); video_vertline_black(gameovertimer - 120, 43, 48); //draw new over video_smallchar_ua(gameovertimer - 135, 43, 27); video_smallchar_ua(gameovertimer - 131, 43, 34); video_smallchar_ua(gameovertimer - 127, 43, 17); video_smallchar_ua(gameovertimer - 123, 43, 30); } //draw enemy ship at the top of the screen else if (gameovertimer == 183) video_smallchar_ua(44, 1, 42); //scroll enemy ship down screen til between the words game over else if ((gameovertimer > 140) && (gameovertimer < 183)) { video_smallchar_ua(44, 183 - gameovertimer, 12); video_smallchar_ua(44, 184 - gameovertimer, 42); } //draw player ship on bottom of screen/shoot bullet else if (gameovertimer == 140) { video_smallchar_ua(44, 94, 39); video_pt(45, 93, 1); } //scroll the bullet else if ((gameovertimer > 93) && (gameovertimer < 139)) { video_pt(45, gameovertimer - 46, 0); video_pt(45, gameovertimer - 47, 1); } //erase enemy ship else if (gameovertimer == 93) video_smallchar_ua(44, 43, 12); //scroll player ship in between words game over else if ((gameovertimer > 41) && (gameovertimer < 93)) { video_smallchar_ua(44, gameovertimer + 2, 12); video_smallchar_ua(44, gameovertimer + 1, 39); } //erase the screen else if (gameovertimer == -120) //decrease to make larger delay { //erase game over screen //erase game video_smallchar_ua(27, 43, 12); video_smallchar_ua(31, 43, 12); video_smallchar_ua(35, 43, 12); video_smallchar_ua(39, 43, 12); //erase player ship in middle video_smallchar_ua(44, 43, 12); //erase over video_smallchar_ua(49, 43, 12); video_smallchar_ua(53, 43, 12); video_smallchar_ua(57, 43, 12); video_smallchar_ua(61, 43, 12); scoretimer = 11; //initialize high score timer //check if the score is a high score if (score > highScores[4]) gameState = newscorescreen; //go to screen to enter new score else gameState = scorescreen; //show high score screen } gameovertimer--; //decrement animation timer } //============================================================================== //============================================================================== // Drawing Title Screen //============================================================================== //draw the title (logo) screen if (gameState == drawtitle) { //space out drawing to avoid glitching if (initCount++ == 0) { //draw the logo logo_draw(); //set menu item to 1 player mode menuitem = 0; //print "1 PLAYER" video_smallchar_ua(29, 64, 1); video_smallchar_ua(33, 64, 12); video_smallchar_ua(37, 64, 28); video_smallchar_ua(41, 64, 24); video_smallchar_ua(45, 64, 13); video_smallchar_ua(49, 64, 37); video_smallchar_ua(53, 64, 17); video_smallchar_ua(57, 64, 30); //print "VERSUS" video_smallchar_ua(33, 73, 34); video_smallchar_ua(37, 73, 17); video_smallchar_ua(41, 73, 30); video_smallchar_ua(45, 73, 31); video_smallchar_ua(49, 73, 33); video_smallchar_ua(53, 73, 31); //highlight menu option video_smallchar_ua(25, 64, 47); video_smallchar_ua(61, 64, 47); } else { //draw 3 ships on side screen (for lives) video_smallchar_ua(100, 29, 39); video_smallchar_ua(104, 29, 39); video_smallchar_ua(108, 29, 39); //draw 00000 as score video_smallchar_ua(96, 11, 0); video_smallchar_ua(100, 11, 0); video_smallchar_ua(104, 11, 0); video_smallchar_ua(108, 11, 0); video_smallchar_ua(112, 11, 0); gameState = title; //go to title screen state } } //============================================================================== //============================================================================== // Clear Game Screen //============================================================================== //Clear screen for reset if (gameState == clearscreen) { //erase the player ship video_smallchar_ua(playerx, playery, 12); //clear the enemy ships for (count = 0; count < 5; count++) { if (row1Live[count] == 1) video_smallchar_ua(hPos + count * 7, row1Pos, 12); if (row2Live[count] == 1) video_smallchar_ua(hPos + count * 7, row2Pos, 12); if (row3Live[count] == 1) video_smallchar_ua(hPos + count * 7, row3Pos, 12); if (row4Live[count] == 1) video_smallchar_ua(hPos + count * 7, row4Pos, 12); if (row5Live[count] == 1) video_smallchar_ua(hPos + count * 7, row5Pos, 12); } //erase the enemy bullets for (count = 0; count < bulletsPerLevel; count++) if (ebulletslive[count]) video_pt(ebulletsx[count], ebulletsy[count], 0); //erase the player bullet if (bullet) video_pt(bulletx, bullety, 0); //erase attacking ship if (attacking) video_smallchar_ua(xpos, ypos, 12); //erase level from side screen video_smallchar_ua(96, 84, 12); video_smallchar_ua(100, 84, 12); video_smallchar_ua(104, 84, 12); video_smallchar_ua(108, 84, 12); video_smallchar_ua(112, 84, 12); video_smallchar_ua(116, 84, 12); video_smallchar_ua(104, 90, 12); video_smallchar_ua(108, 90, 12); gameState = drawtitle; //go to state to draw the title screen } //============================================================================== //============================================================================== // Single Player Mode Death Animation //============================================================================== //draw death animation if (gameState == die) { deathCount++; //increment animation timer //draw the explosion of the player ship if (deathCount == 20) { video_smallchar_ua(playerx, playery, 48); video_pt(playerx + 1, playery - 1, 1); video_pt(playerx + 2, playery + 5, 1); if (playerx > 1) { video_pt(playerx - 1, playery, 1); video_pt(playerx - 1, playery + 3, 1); } if (playerx < 88) { video_pt(playerx + 3, playery, 1); video_pt(playerx + 3, playery + 3, 1); } } else if (deathCount == 40) { video_smallchar_ua(playerx, playery, 12); video_pt(playerx + 1, playery - 1, 0); video_pt(playerx + 2, playery + 5, 0); video_pt(playerx, playery - 3, 1); video_pt(playerx + 1, playery + 6, 1); if (playerx > 1) { video_pt(playerx - 1, playery, 0); video_pt(playerx - 1, playery + 3, 0); } if (playerx < 88) { video_pt(playerx + 3, playery, 0); video_pt(playerx + 3, playery + 3, 0); } if (playerx > 3) { video_pt(playerx - 3, playery - 1, 1); video_pt(playerx - 3, playery + 4, 1); video_pt(playerx - 2, playery + 5, 1); } if (playerx < 85) { video_pt(playerx + 3, playery - 2, 1); video_pt(playerx + 5, playery + 1, 1); video_pt(playerx + 4, playery + 5, 1); } } else if (deathCount == 60) { video_pt(playerx, playery - 3, 0); video_pt(playerx + 1, playery + 6, 0); if (playerx > 3) { video_pt(playerx - 3, playery - 1, 0); video_pt(playerx - 3, playery + 4, 0); video_pt(playerx - 2, playery + 5, 0); } if (playerx < 85) { video_pt(playerx + 3, playery - 2, 0); video_pt(playerx + 5, playery + 1, 0); video_pt(playerx + 4, playery + 5, 0); } } //have the ship scroll up from the bottom of the screen else if (deathCount == 80) video_pt(45, 98, 1); else if (deathCount == 90) { video_pt(45, 97, 1); video_pt(44, 98, 1); video_pt(46, 98, 1); } else if (deathCount == 100) { video_pt(44, 98, 0); video_pt(46, 98, 0); video_pt(45, 96, 1); video_pt(44, 97, 1); video_pt(46, 97, 1); } else if (deathCount == 110) { video_pt(44, 97, 0); video_pt(46, 97, 0); video_pt(45, 95, 1); video_pt(44, 96, 1); video_pt(46, 96, 1); video_pt(44, 98, 1); video_pt(46, 98, 1); } else if (deathCount == 120) video_smallchar_ua(44, 94, 39); else if (deathCount == 130) { video_smallchar_ua(44, 94, 12); video_smallchar_ua(44, 93, 39); } else if (deathCount == 140) { video_smallchar_ua(44, 93, 12); video_smallchar_ua(44, 92, 39); } else if (deathCount == 150) { video_smallchar_ua(44, 92, 12); video_smallchar_ua(44, 91, 39); } else if (deathCount == 160) { video_smallchar_ua(44, 91, 12); video_smallchar_ua(44, 90, 39); playerx = 44; gameState = game; deathCount = 0; } //continue scrolling enemies and bullets during animation if (--scrollTimer <= 0) { scrollTimer = scrollVal; //reset scroll timer //need to erase before drawing new ship positions //so you don't erase part of ship if (attacking) video_smallchar_ua(xpos, ypos, 12); //scrolling to the left if (scrollDirection == 0) { //left edge of screen if (hPos == 1) { //draw vertical lines to erase ship artifacts for (count=0; count < shipsPerRow; count++) video_vertline_black(hPos + count*7, 2, row5Pos+5); hPos++; //move enemy ships to the right //reverse direction scrollDirection = 1; //redraw live ships one pos to the right for (count = 0; count < shipsPerRow; count++) { if (row1Live[count] == 1) video_smallchar_ua(hPos + count * 7, row1Pos, 42); if (row2Live[count] == 1) video_smallchar_ua(hPos + count * 7, row2Pos, 43); if (row3Live[count] == 1) video_smallchar_ua(hPos + count * 7, row3Pos, 44); if (row4Live[count] == 1) video_smallchar_ua(hPos + count * 7, row4Pos, 40); if (row5Live[count] == 1) video_smallchar_ua(hPos + count * 7, row5Pos, 41); } } //anywhere else on screen else { //draw vertical lines to erase ship artifacts for (count=0; count < shipsPerRow; count++) video_vertline_black(hPos+2 + count*7, 2, row5Pos+5); hPos--; //move enemy ships to the left //redraw live ships one pos to the left for(count = 0; count < shipsPerRow; count++) { if(row1Live[count] == 1) video_smallchar_ua(hPos + count * 7, row1Pos, 42); if (row2Live[count] == 1) video_smallchar_ua(hPos + count * 7, row2Pos, 43); if (row3Live[count] == 1) video_smallchar_ua(hPos + count * 7, row3Pos, 44); if (row4Live[count] == 1) video_smallchar_ua(hPos + count * 7, row4Pos, 40); if (row5Live[count] == 1) video_smallchar_ua(hPos + count * 7, row5Pos, 41); } } } //scrolling to the right else { //if at right edge if (hPos == 60) { //draw vertical lines to erase ship artifacts for (count=0; count < shipsPerRow; count++) video_vertline_black(hPos+2 + count*7, 2, row5Pos + 5); hPos--; //move enemy ships to the left //reverse direction scrollDirection = 0; //draw live ships for (count = 0; count < shipsPerRow; count++) { if(row1Live[count] == 1) video_smallchar_ua(hPos + count * 7, row1Pos, 42); if (row2Live[count] == 1) video_smallchar_ua(hPos + count * 7, row2Pos, 43); if (row3Live[count] == 1) video_smallchar_ua(hPos + count * 7, row3Pos, 44); if (row4Live[count] == 1) video_smallchar_ua(hPos + count * 7, row4Pos, 40); if (row5Live[count] == 1) video_smallchar_ua(hPos + count * 7, row5Pos, 41); } } //anywhere else else { //draw vertical lines to erase ship artifacts for (count=0; count < shipsPerRow; count++) video_vertline_black(hPos + count*7, 2, row5Pos+5); hPos++; //move enemy ships to the right //draw live ships for(count = 0; count < shipsPerRow; count++) { if(row1Live[count] == 1) video_smallchar_ua(hPos + count * 7, row1Pos, 42); if (row2Live[count] == 1) video_smallchar_ua(hPos + count * 7, row2Pos, 43); if (row3Live[count] == 1) video_smallchar_ua(hPos + count * 7, row3Pos, 44); if (row4Live[count] == 1) video_smallchar_ua(hPos + count * 7, row4Pos, 40); if (row5Live[count] == 1) video_smallchar_ua(hPos + count * 7, row5Pos, 41); } } } //scroll attacking ship if (attacking) { //random number so ship attacks at random in x direction someRandom = rand() % 2 + 1; //move attacking ship towards the previous position of the dead ship if (playerx < xpos) { if (xpos > 3) xpos = xpos - someRandom; else xpos = 1; } else if (playerx > xpos) { if (xpos < 87) xpos = xpos + someRandom; else xpos = 88; } ypos++; //scroll attacking ship to bottom of the screen //find which ship to draw switch (row) { case 1: video_smallchar_ua(xpos, ypos, 42); break; case 2: video_smallchar_ua(xpos, ypos, 43); break; case 3: video_smallchar_ua(xpos, ypos, 44); break; case 4: video_smallchar_ua(xpos, ypos, 40); break; case 5: video_smallchar_ua(xpos, ypos, 41); break; } //if attacking ship at end of screen, put it back in formation if (ypos > 92) { attacking = 0; //ship no longer attacking //erase the attacking ship video_smallchar_ua(xpos, ypos, 12); ypos = 2; //reset the position //reinsert the attacking ship into the enemy ship formation switch (row) { case 1: row1Live[col] = 1; break; case 2: row2Live[col] = 1; break; case 3: row3Live[col] = 1; break; case 4: row4Live[col] = 1; break; case 5: row5Live[col] = 1; break; } } } } //scroll enemy bullets //runs every bulletTimer frames if (--bulletTimer <= 0) { bulletTimer = bulletTimerVal; //reset bullet timer //scroll through bullet arrays for (count = 0; count < bulletsPerLevel; count++) { //live ammo if (ebulletslive[count]) { //kill runaway bullets if (ebulletsy[count] >= 98) { //erase the bullet video_pt(ebulletsx[count], ebulletsy[count], 0); //kill the bullet ebulletslive[count] = 0; } //move the bullet down otherwise else { //erase the old bullet position video_pt(ebulletsx[count], ebulletsy[count], 0); //scroll the bullet down the screen ebulletsy[count] = ebulletsy[count] + 1; //draw the new position of the bullet video_pt(ebulletsx[count], ebulletsy[count], 1); } } } } //player bullet was fired, do update if (bullet) { //row of ships being hit if (bullety >= row5Pos) {ptr = row5Live; activeRow = row5Pos; } else if (bullety >= row4Pos) {ptr = row4Live; activeRow = row4Pos; } else if (bullety >= row3Pos) {ptr = row3Live; activeRow = row3Pos; } else if (bullety >= row2Pos) {ptr = row2Live; activeRow = row2Pos; } else {ptr = row1Live; activeRow = row1Pos;} //column of ship being hit bulletHit = (bulletx - hPos) / 7; //check if bullet hit an enemy ship if ((bullety <= (row5Pos + 4)) //bottom edge && (bullety >= 3) //top edge && (bulletx >= hPos) //left edge && (bulletx <= (hPos + 7 * shipsPerRow - 5)) //right edge && (((bulletx - hPos) % 7) < 3) //not in null space between ships && (((bullety - 3) % 7) <= 4) //need valid column, live ship && ((bulletHit >= 0) && (bulletHit <= 4) && (ptr[bulletHit]))) { ptr[bulletHit] = 0; //kill the ship bullet = 0; //bullet inactive //erase the destroyed ship video_smallchar_ua(hPos + bulletHit * 7, activeRow, 12); //erase the bullet video_pt(bulletx, bullety, 0); shipCount--; //decrement the enemy ship count score++; //increase player score lifeFlag = 1; //player can gain another life } //if a bullet hit an attacking ship else if (attacking && (bulletx >= xpos) && (bulletx < xpos + 3) && (bullety >= ypos) && (bullety < ypos + 5)) { //erase the attacking ship video_smallchar_ua(xpos, ypos, 12); attacking = 0; //enemy not attacking bullet = 0; //bullet inactive video_pt(bulletx, bullety, 0); //erase the bullet shipCount--; //decrement the enemy ship count score++; //increase player score lifeFlag = 1; //player can gain another life } else //no ship in bullet's path { //erase the old bullet video_pt(bulletx, bullety, 0); //draw the new bullet one position higher video_pt(bulletx, --bullety, 1); //if bullet at top of screen if (bullety == 1) { bullet = 0; //kill the bullet video_pt(bulletx, bullety, 0); //erase the bullet } } //add a life if the score is a multiple of 100 if ((lives < 3) && (score % 100 == 0) && (score != 0) && lifeFlag) { lifeFlag = 0; //player can't gain another life yet //add life to side screen if (lives == 2) video_smallchar_ua(108, 29, 39); else if (lives == 1) video_smallchar_ua(104, 29, 39); lives++; //increase number of lives } } //if the player has just died if (deathFlag) { //erase player ship video_smallchar_ua(playerx, playery, 12); //draw dead player ship video_smallchar_ua(playerx, playery, 47); deathFlag = 0; //player has not just died invulnerable = invTime; //player is invulnerable //update players lives lives--; //decrement lives //erase life from side screen if (lives == 2) video_smallchar_ua(108, 29, 12); else if (lives == 1) video_smallchar_ua(104, 29, 12); //out of lives else { video_smallchar_ua(100, 29, 12); gameState = lose; //go to game over animation } } } //============================================================================== //============================================================================== // High Score Screen //============================================================================== //lets the user enter initials for a high score if (gameState == newscorescreen) { //check timer if (scoretimer == 10) { //print "NEW HIGH" video_smallchar_ua(17, 12, 26); video_smallchar_ua(21, 12, 17); video_smallchar_ua(25, 12, 35); video_smallchar_ua(33, 12, 20); video_smallchar_ua(37, 12, 21); video_smallchar_ua(41, 12, 19); video_smallchar_ua(45, 12, 20); } else if (scoretimer == 9) { //print "SCORE" video_smallchar_ua(53, 12, 31); video_smallchar_ua(57, 12, 15); video_smallchar_ua(61, 12, 27); video_smallchar_ua(65, 12, 30); video_smallchar_ua(69, 12, 17); } else if (scoretimer == 8) { //print line for cursor video_line(curpos, 64, curpos+2, 64, 1); //determine which position player gets stored to //shift lower scores/initials down if necessary if (score > highScores[0]) { highpos = 0; highScores[4] = highScores[3]; highScores[3] = highScores[2]; highScores[2] = highScores[1]; highScores[1] = highScores[0]; for (count = 4; count >= 1; count--) { highNames[count][0] = highNames[count-1][0]; highNames[count][1] = highNames[count-1][1]; highNames[count][2] = highNames[count-1][2]; } } else if (score > highScores[1]) { highpos = 1; highScores[4] = highScores[3]; highScores[3] = highScores[2]; highScores[2] = highScores[1]; for (count = 4; count >= 2; count--) { highNames[count][0] = highNames[count-1][0]; highNames[count][1] = highNames[count-1][1]; highNames[count][2] = highNames[count-1][2]; } } else if (score > highScores[2]) { highpos = 2; highScores[4] = highScores[3]; highScores[3] = highScores[2]; for (count = 4; count >= 3; count--) { highNames[count][0] = highNames[count-1][0]; highNames[count][1] = highNames[count-1][1]; highNames[count][2] = highNames[count-1][2]; } } else if (score > highScores[3]) { highpos = 3; highScores[4] = highScores[3]; highNames[4][0] = highNames[3][0]; highNames[4][1] = highNames[3][1]; highNames[4][2] = highNames[3][2]; } else highpos = 4; } else if (scoretimer == 7) { scoretimer = 8; //stall in this state until user confirms //display the initials video_smallchar_ua(40, 58, highNamestemp[0]); video_smallchar_ua(44, 58, highNamestemp[1]); video_smallchar_ua(48, 58, highNamestemp[2]); //down arrow press, scroll down through alphabet if (buttonDOWNpush) { if (curpos == 40) { highNamestemp[0]++; if (highNamestemp[0] == 39) highNamestemp[0] = 12; } else if (curpos == 44) { highNamestemp[1]++; if (highNamestemp[1] == 39) highNamestemp[1] = 12; } else if (curpos == 48) { highNamestemp[2]++; if (highNamestemp[2] == 39) highNamestemp[2] = 12; } } //up arrow press, scroll up through alphabet if (buttonUPpush) { if (curpos == 40) { highNamestemp[0]--; if (highNamestemp[0] == 11) highNamestemp[0] = 38; } else if (curpos == 44) { highNamestemp[1]--; if (highNamestemp[1] == 11) highNamestemp[1] = 38; } else if (curpos == 48) { highNamestemp[2]--; if (highNamestemp[2] == 11) highNamestemp[2] = 38; } } //left arrow press, move cursor left if (buttonLEFTpush) { if (curpos == 48) { video_line(curpos, 64, curpos+2, 64, 0); curpos = 44; video_line(curpos, 64, curpos+2, 64, 1); } else if (curpos == 44) { video_line(curpos, 64, curpos+2, 64, 0); curpos = 40; video_line(curpos, 64, curpos+2, 64, 1); } } //right arrow press, move cursor right if (buttonRIGHTpush) { if (curpos == 40) { video_line(curpos, 64, curpos+2, 64, 0); curpos = 44; video_line(curpos, 64, curpos+2, 64, 1); } else if (curpos == 44) { video_line(curpos, 64, curpos+2, 64, 0); curpos = 48; video_line(curpos, 64, curpos+2, 64, 1); } } //C button press, store the score/initials and continue if (buttonCpush) { highNames[highpos][0] = highNamestemp[0]; highNames[highpos][1] = highNamestemp[1]; highNames[highpos][2] = highNamestemp[2]; highScores[highpos] = score; scoretimer = 7; } } else if (scoretimer == 6) { //clear the screen for (count = 17; count <= 71; count++) video_vertline_black(count, 12, 64); // display high scores scoretimer = 12; gameState = scorescreen; } scoretimer--; //decrement timer } //displays the high score screen if (gameState == scorescreen) { if (scoretimer == 10) { //print "HIGH SCORES" video_smallchar_ua(23, 12, 20); video_smallchar_ua(27, 12, 21); video_smallchar_ua(31, 12, 19); video_smallchar_ua(35, 12, 20); video_smallchar_ua(43, 12, 31); video_smallchar_ua(47, 12, 15); video_smallchar_ua(51, 12, 27); video_smallchar_ua(55, 12, 30); video_smallchar_ua(59, 12, 17); video_smallchar_ua(63, 12, 31); } else if (scoretimer == 9) video_line(23, 18, 65, 18, 1); else if (scoretimer == 8) { //print score 0 video_smallchar_ua(23, 21, highNames[0][0]); video_smallchar_ua(27, 21, highNames[0][1]); video_smallchar_ua(31, 21, highNames[0][2]); video_smallchar_ua(47, 21, highScores[0] / 10000); video_smallchar_ua(51, 21, (highScores[0] % 10000) / 1000); video_smallchar_ua(55, 21, (highScores[0] % 1000) / 100); video_smallchar_ua(59, 21, (highScores[0] % 100) / 10); video_smallchar_ua(63, 21, (highScores[0] % 10)); } else if (scoretimer == 7) { //print score 1 video_smallchar_ua(23, 28, highNames[1][0]); video_smallchar_ua(27, 28, highNames[1][1]); video_smallchar_ua(31, 28, highNames[1][2]); video_smallchar_ua(47, 28, highScores[1] / 10000); video_smallchar_ua(51, 28, (highScores[1] % 10000) / 1000); video_smallchar_ua(55, 28, (highScores[1] % 1000) / 100); video_smallchar_ua(59, 28, (highScores[1] % 100) / 10); video_smallchar_ua(63, 28, (highScores[1] % 10)); } else if (scoretimer == 6) { //print score 2 video_smallchar_ua(23, 35, highNames[2][0]); video_smallchar_ua(27, 35, highNames[2][1]); video_smallchar_ua(31, 35, highNames[2][2]); video_smallchar_ua(47, 35, highScores[2] / 10000); video_smallchar_ua(51, 35, (highScores[2] % 10000) / 1000); video_smallchar_ua(55, 35, (highScores[2] % 1000) / 100); video_smallchar_ua(59, 35, (highScores[2] % 100) / 10); video_smallchar_ua(63, 35, (highScores[2] % 10)); } else if (scoretimer == 5) { //print score 3 video_smallchar_ua(23, 42, highNames[3][0]); video_smallchar_ua(27, 42, highNames[3][1]); video_smallchar_ua(31, 42, highNames[3][2]); video_smallchar_ua(47, 42, highScores[3] / 10000); video_smallchar_ua(51, 42, (highScores[3] % 10000) / 1000); video_smallchar_ua(55, 42, (highScores[3] % 1000) / 100); video_smallchar_ua(59, 42, (highScores[3] % 100) / 10); video_smallchar_ua(63, 42, (highScores[3] % 10)); } else if (scoretimer == 4) { //print score 4 video_smallchar_ua(23, 49, highNames[4][0]); video_smallchar_ua(27, 49, highNames[4][1]); video_smallchar_ua(31, 49, highNames[4][2]); video_smallchar_ua(47, 49, highScores[4] / 10000); video_smallchar_ua(51, 49, (highScores[4] % 10000) / 1000); video_smallchar_ua(55, 49, (highScores[4] % 1000) / 100); video_smallchar_ua(59, 49, (highScores[4] % 100) / 10); video_smallchar_ua(63, 49, (highScores[4] % 10)); } else if (scoretimer == -209) { //clear screen for (count = 23; count <= 65; count++) video_vertline_black(count, 12, 49); } else if (scoretimer == -210) //decrease to increase delay { //go back to title screen gameState = drawtitle; initCount = 0; } scoretimer--; //decrement timer } //============================================================================== //delays game from resuming for 1 frame //needed to avoid glitching from drawing too much //(should have used an if-else if ladder for game states) if (gameState == delaygame) gameState = game; } //line 231 } //while } //main