/* * ASCII INVADERS! * * This is a simple "space invaders" type game in which you move your "gun" * back and forth to shoot at invaders who are decending from above. Above * the gun are "shields" which protect you from alien fire, but disintegrate] * a little with each hit. * * Both you and the invaders can have up to 5 concurrent shots visible on the * screen. Your gun holds 5 shots, and reloads one shot every 10 "ticks" of * the game. (Each tick is one movement of the invaders). * * Use the LEFT and RIGHT arrow keys to move your gun. SPACE fires. ESCAPE * quits the game. * * Copyright 1994-1995 Dave Dunfield * All rights reserved. * * Compile command: cc invader -fop */ #include #include /* Misc. game parameters */ #define MAX_X 24 /* Maximum horizontal movement */ #define SHIELD_LINE 23 /* Shield is on this line */ #define PLAY_WIDTH 7 /* Width of player "gun" */ #define MAX_SHOT 5 /* Number of concurrent shots (each side) */ #define INVADERS 24 /* Number of invaders */ #define RELOAD 10 /* Reload frequency */ #define SHOTS 5 /* Gun holds this many shots */ /* PC graphic characters */ #define SHOT 4 /* Shot in progress */ #define SHIELD1 0xB0 /* Really weak shield */ #define SHIELD2 0xB1 /* Somewhat weak shield */ #define SHIELD3 0xB2 /* Fairly strong shield */ #define SHIELD4 0xDB /* Really strong shield */ /* Sound frequencies */ #define SKILL 70 /* Invader dies */ #define SISHOT 250 /* Invader shoots */ #define SPSHOT 350 /* Player shoots */ #define SSHIELD 100 /* Shield gets hit */ #define SMOVE 9000 /* Invaders are moving */ /* Melodies for winning and dieing */ unsigned Swin[] = { 100, 200, 300, 400, 500, 0 }; unsigned Sdie[] = { 500, 400, 300, 200, 100, 0 }; /* ASCII graphics of Invaders - view 1 */ char invaders1[3][3][8] = { " \\^^^/ ", " ", " \\vvv/ ", " <=%=< ", " <$:$- ", " <=%=< ", " O---O ", " ):: ) ", " O---O " } /* ASCII graphics of Invaders - view 2 */ char invaders2[3][3][8] = { " /^^^\\ ", " ", " /vvv\\ ", " >=%=> ", " -$:$> ", " >=%=> ", " O---O ", " ( ::( ", " O---O " } /* Status of invaders 0 = Alive, !0 = Dead */ char invader_status[8][3]; /* Status of player shield 0=None ... 4=Full strength */ char shield[80] = { 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }; /* Characters to depict varying shield strengths */ char shield_graphic[] = { ' ', SHIELD1, SHIELD2, SHIELD3, SHIELD4 }; /* Active shots .. Hold X-Y coordinates of bullets in motion */ int invader_shots[MAX_SHOT][2], int player_shots[MAX_SHOT][2]; int X = 0, /* X position of invader matrix */ Y = 0, /* Y position of invader matrix */ player = 35, /* X position of players gun */ shots = SHOTS, /* Number of shots in gun */ killed = 0; /* Number of invaders killed */ main() { int i; static int reload = RELOAD; vopen(); vcursor_off(); draw_invaders(invaders1); draw_player(); for(;;) { if(random(100) < 20) invader_shot(); for(i=0; i < 5; ++i) { update_shots(); move_player(); delay(50); } sound(SMOVE); vgotoxy(0, SHIELD_LINE); for(i=0; i < sizeof(shield); ++i) vputc(shield_graphic[shield[i]]); move_invaders(); if(!--reload) { reload = RELOAD; if(shots < SHOTS) ++shots; } } } /* * Exit program with message */ terminate(unsigned *tune, char *msg) { vclscr(); vputs(msg); while(*tune) { sound(*tune++); delay(250); } vcursor_line(); while(vtstc()); sound_off(); exit(0); } /* * Draw the invader matrix */ draw_invaders(char invaders[3][3][8]) { int i, j, k, x; for(i=0; i < 8; ++i) { x = (i*7) + X; for(j=0; j < 3; ++j) { if((k = (j*4)+Y) < SHIELD_LINE) { vgotoxy(x, k); vputs(" "); } if(!invader_status[i][j]) { if(k >= (SHIELD_LINE-3)) terminate(Sdie, "The aliens have landed!"); vgotoxy(x, k+1); vputs(invaders[j][0]); vgotoxy(x, k+2); vputs(invaders[j][1]); vgotoxy(x, k+3); vputs(invaders[j][2]); } } } } /* * Move the invaders */ move_invaders() { static char direction = 0, toggle = 0; draw_invaders((++toggle & 1) ? invaders2 : invaders1); if(direction) { if(--X < 0) { X = 0; ++Y; direction = 0; } } else { if(++X >= MAX_X) { X = MAX_X - 1; ++Y; direction = -1; } } sound_off(); } /* * Make an invader take a shot */ invader_shot() { int i, j, x; /* Locate an empty slot in the shot tracking array */ for(i=0; i < MAX_SHOT; ++i) if(!invader_shots[i][0]) goto free1; return; free1: /* Locate a low invader to make the shot */ sound(SISHOT); x = random(8); for(;;) { j = 3; while(--j >= 0) if(!invader_status[x][j]) goto free2; x = (x + 1) % 8; } free2: invader_shots[i][0] = (j*4) + Y + 2; invader_shots[i][1] = (x * 7) + X + 3; } /* * Update the positions of the shots on the screen */ update_shots() { int i, j, k, x, y, x1, y1; for(i=0; i < MAX_SHOT; ++i) { /* Update pending invader shot */ if(invader_shots[i][0]) { vgotoxy(x=invader_shots[i][1], y=invader_shots[i][0]++); vputc(' '); if(y < SHIELD_LINE) { vgotoxy(x, ++y); vputc(SHOT); } else if(y == SHIELD_LINE) { if(shield[x]) { sound(SSHIELD); --shield[x]; invader_shots[i][0] = 0; } } else if(y > SHIELD_LINE) { invader_shots[i][0] = 0; if((x > player) && (x <= (player+PLAY_WIDTH))) terminate(Sdie, "You died!"); } } /* Update pending player shot */ if(player_shots[i][0]) { vgotoxy(x = player_shots[i][1], y=player_shots[i][0]--); vputc(' '); if(--y) { vgotoxy(x, y); vputc(SHOT); } for(j=0; j < 8; ++j) { x1 = (j*7) + X; if((x1 > x) || ((x1+6) < x)) continue; for(k=0; k < 3; ++k) { if(invader_status[j][k]) continue; y1 = (k*4) + Y; if(((y1+1) > y) || ((y1+4) < y)) continue; vgotoxy(x, y); vputc(' '); player_shots[i][0] = 0; kill_invader(j, k); } } } } } /* * Terminate an invader */ kill_invader(int x, int y) { int i; sound(SKILL); invader_status[x][y] = -1; /* Record kill */ x = (x*7)+X; for(i=0; i < 4; ++i) { /* Erase from screen */ vgotoxy(x, (y*4)+Y+i); vputs(" "); } if(++killed >= INVADERS) /* Check for victory */ terminate(Swin, "You WIN!"); } /* * Handle player moves */ move_player() { int i, j; switch(vtstc()) { case _KLA : /* Move LEFT */ if(player) --player; draw_player(); return; case _KRA : /* Move RIGHT */ if(player < (80-(PLAY_WIDTH+3))) ++player; draw_player(); return; case 0x1B : /* Quit game */ terminate(Sdie, "Chicken!"); case ' ' : /* Fire cannon */ if(shots) { for(i=0; i < MAX_SHOT; ++i) if(!player_shots[i][0]) { sound(SPSHOT); --shots; if(shield[j = player+((PLAY_WIDTH/2)+1)]) --shield[j]; else { player_shots[i][0] = SHIELD_LINE; player_shots[i][1] = j; } return; } } } } /* * Draw the player at his current position */ draw_player() { vgotoxy(player, SHIELD_LINE+1); vputs(" ***^*** "); }