/*
* 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 <stdio.h>
#include <video.h>
/* 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] = {
" \\^^^/ ",
" <o$o> ",
" \\vvv/ ",
" <=%=< ",
" <$:$- ",
" <=%=< ",
" O---O ",
" ):: ) ",
" O---O " }
/* ASCII graphics of Invaders - view 2 */
char invaders2[3][3][8] = {
" /^^^\\ ",
" <o$o> ",
" /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(" ***^*** ");
}