BSD 4_3_Tahoe release
[unix-history] / usr / src / games / hunt / shots.c
/*
* Copyright (c) 1985 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static char sccsid[] = "@(#)shots.c 5.2 (Berkeley) 6/27/88";
#endif /* not lint */
/*
* Hunt
* Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
* San Francisco, California
*/
# include "hunt.h"
# include <signal.h>
# define PLUS_DELTA(x, max) if (x < max) x++; else x--
# define MINUS_DELTA(x, min) if (x > min) x--; else x++
/*
* moveshots:
* Move the shots already in the air, taking explosions into account
*/
moveshots()
{
register BULLET *bp, *next;
register PLAYER *pp;
register int x, y;
register BULLET *blist;
register int i;
rollexpl();
if (Bullets == NULL)
goto ret;
/*
* First we move through the bullet list BULSPD times, looking
* for things we may have run into. If we do run into
* something, we set up the explosion and disappear, checking
* for damage to any player who got in the way.
*/
blist = Bullets;
Bullets = NULL;
for (bp = blist; bp != NULL; bp = next) {
next = bp->b_next;
x = bp->b_x;
y = bp->b_y;
Maze[y][x] = bp->b_over;
for (pp = Player; pp < End_player; pp++)
check(pp, y, x);
# ifdef MONITOR
for (pp = Monitor; pp < End_monitor; pp++)
check(pp, y, x);
# endif MONITOR
for (i = 0; i < BULSPD; i++) {
if (bp->b_expl)
break;
x = bp->b_x;
y = bp->b_y;
switch (bp->b_face) {
case LEFTS:
x--;
break;
case RIGHT:
x++;
break;
case ABOVE:
y--;
break;
case BELOW:
y++;
break;
}
switch (Maze[y][x]) {
case SHOT:
if (rand_num(100) < 5) {
zapshot(Bullets, bp);
zapshot(next, bp);
}
break;
case GRENADE:
if (rand_num(100) < 10) {
zapshot(Bullets, bp);
zapshot(next, bp);
}
break;
# ifdef REFLECT
case WALL4: /* reflecting walls */
switch (bp->b_face) {
case LEFTS:
bp->b_face = BELOW;
break;
case RIGHT:
bp->b_face = ABOVE;
break;
case ABOVE:
bp->b_face = RIGHT;
break;
case BELOW:
bp->b_face = LEFTS;
break;
}
Maze[y][x] = WALL5;
# ifdef MONITOR
for (pp = Monitor; pp < End_monitor; pp++)
check(pp, y, x);
# endif MONITOR
break;
case WALL5:
switch (bp->b_face) {
case LEFTS:
bp->b_face = ABOVE;
break;
case RIGHT:
bp->b_face = BELOW;
break;
case ABOVE:
bp->b_face = LEFTS;
break;
case BELOW:
bp->b_face = RIGHT;
break;
}
Maze[y][x] = WALL4;
# ifdef MONITOR
for (pp = Monitor; pp < End_monitor; pp++)
check(pp, y, x);
# endif MONITOR
break;
# endif REFLECT
# ifdef RANDOM
case DOOR:
switch (rand_num(4)) {
case 0:
bp->b_face = ABOVE;
break;
case 1:
bp->b_face = BELOW;
break;
case 2:
bp->b_face = LEFTS;
break;
case 3:
bp->b_face = RIGHT;
break;
}
break;
# endif RANDOM
case LEFTS:
case RIGHT:
case BELOW:
case ABOVE:
# ifdef FLY
case FLYER:
# endif FLY
/*
* give the person a chance to catch a
* grenade if s/he is facing it
*/
if (rand_num(100) < 10
&& opposite(bp->b_face, Maze[y][x])) {
if (bp->b_owner != NULL)
message(bp->b_owner,
"Your charge was absorbed!");
pp = play_at(y, x);
pp->p_ammo += bp->b_charge;
(void) sprintf(Buf,
"Absorbed charge (good shield!)");
message(pp, Buf);
free((char *) bp);
(void) sprintf(Buf, "%3d", pp->p_ammo);
cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
outstr(pp, Buf, 3);
goto next_bullet;
}
/* FALLTHROUGH */
# ifndef RANDOM
case DOOR:
# endif RANDOM
case WALL1:
case WALL2:
case WALL3:
bp->b_expl = TRUE;
break;
}
bp->b_x = x;
bp->b_y = y;
}
bp->b_next = Bullets;
Bullets = bp;
next_bullet:
;
}
blist = Bullets;
Bullets = NULL;
for (bp = blist; bp != NULL; bp = next) {
next = bp->b_next;
if (!bp->b_expl) {
save_bullet(bp);
# ifdef MONITOR
for (pp = Monitor; pp < End_monitor; pp++)
check(pp, bp->b_y, bp->b_x);
# endif MONITOR
continue;
}
chkshot(bp);
free((char *) bp);
}
for (pp = Player; pp < End_player; pp++)
Maze[pp->p_y][pp->p_x] = pp->p_face;
ret:
for (pp = Player; pp < End_player; pp++) {
# ifdef FLY
if (pp->p_flying >= 0) {
Maze[pp->p_y][pp->p_x] = pp->p_over;
x = pp->p_x + pp->p_flyx;
y = pp->p_y + pp->p_flyy;
if (x < 1) {
x = 1 - x;
pp->p_flyx = -pp->p_flyx;
}
else if (x > WIDTH - 2) {
x = (WIDTH - 2) - (x - (WIDTH - 2));
pp->p_flyx = -pp->p_flyx;
}
if (y < 1) {
y = 1 - y;
pp->p_flyy = -pp->p_flyy;
}
else if (y > HEIGHT - 2) {
y = (HEIGHT - 2) - (y - (HEIGHT - 2));
pp->p_flyy = -pp->p_flyy;
}
again: switch (Maze[y][x]) {
case LEFTS:
case RIGHT:
case ABOVE:
case BELOW:
case FLYER:
switch (rand_num(4)) {
case 0:
PLUS_DELTA(x, WIDTH - 2);
break;
case 1:
MINUS_DELTA(x, 1);
break;
case 2:
PLUS_DELTA(y, HEIGHT - 2);
break;
case 3:
MINUS_DELTA(y, 1);
break;
}
goto again;
case WALL1:
case WALL2:
case WALL3:
# ifdef REFLECT
case WALL4:
case WALL5:
# endif REFLECT
# ifdef RANDOM
case DOOR:
# endif RANDOM
if (pp->p_flying == 0)
pp->p_flying++;
break;
case MINE:
checkdam(pp, NULL, NULL, MINDAM, MINE);
Maze[y][x] = SPACE;
break;
case GMINE:
checkdam(pp, NULL, NULL, MINDAM, GMINE);
checkdam(pp, NULL, NULL, MINDAM, GMINE);
Maze[y][x] = SPACE;
break;
}
pp->p_y = y;
pp->p_x = x;
pp->p_over = Maze[y][x];
if (pp->p_flying-- == 0) {
checkdam(pp, NULL, NULL,
rand_num(pp->p_damage / 5), FALL);
rand_face(pp);
showstat(pp);
}
Maze[y][x] = pp->p_face;
showexpl(y, x, pp->p_face);
}
# endif FLY
sendcom(pp, REFRESH); /* Flush out the explosions */
look(pp);
sendcom(pp, REFRESH);
}
# ifdef MONITOR
for (pp = Monitor; pp < End_monitor; pp++)
sendcom(pp, REFRESH);
# endif MONITOR
# ifdef CONSTANT_MOVE
if (Bullets != NULL) {
bul_alarm(1);
return;
}
for (i = 0; i < EXPLEN; i++)
if (Expl[i] != NULL) {
bul_alarm(1);
return;
}
bul_alarm(0);
# endif CONSTANT_MOVE
return;
}
save_bullet(bp)
register BULLET *bp;
{
bp->b_over = Maze[bp->b_y][bp->b_x];
switch (bp->b_over) {
case SHOT:
case GRENADE:
case SATCHEL:
case BOMB:
# ifdef OOZE
case SLIME:
# ifdef VOLCANO
case LAVA:
# endif VOLCANO
# endif OOZE
find_under(Bullets, bp);
break;
}
switch (bp->b_over) {
case LEFTS:
case RIGHT:
case ABOVE:
case BELOW:
# ifdef FLY
case FLYER:
# endif FLY
mark_player(bp);
break;
default:
Maze[bp->b_y][bp->b_x] = bp->b_type;
break;
}
bp->b_next = Bullets;
Bullets = bp;
}
/*
* chkshot
* Handle explosions
*/
chkshot(bp)
register BULLET *bp;
{
register int y, x;
register int dy, dx, absdy;
register int delta, damage;
register char expl;
register PLAYER *pp;
switch (bp->b_type) {
case SHOT:
case MINE:
delta = 0;
break;
case GRENADE:
case GMINE:
delta = 1;
break;
case SATCHEL:
delta = 2;
break;
case BOMB:
delta = 3;
break;
# ifdef OOZE
case SLIME:
# ifdef VOLCANO
case LAVA:
# endif VOLCANO
chkslime(bp);
return;
# endif OOZE
}
for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) {
if (y < 0 || y >= HEIGHT)
continue;
dy = y - bp->b_y;
absdy = (dy < 0) ? -dy : dy;
for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) {
if (x < 0 || x >= WIDTH)
continue;
dx = x - bp->b_x;
if (dx == 0)
expl = (dy == 0) ? '*' : '|';
else if (dy == 0)
expl = '-';
else if (dx == dy)
expl = '\\';
else if (dx == -dy)
expl = '/';
else
expl = '*';
showexpl(y, x, expl);
switch (Maze[y][x]) {
case LEFTS:
case RIGHT:
case ABOVE:
case BELOW:
# ifdef FLY
case FLYER:
# endif FLY
if (dx < 0)
dx = -dx;
if (absdy > dx)
damage = delta - absdy + 1;
else
damage = delta - dx + 1;
pp = play_at(y, x);
while (damage-- > 0)
checkdam(pp, bp->b_owner, bp->b_score,
MINDAM, bp->b_type);
break;
case GMINE:
case MINE:
add_shot((Maze[y][x] == GMINE) ?
GRENADE : SHOT,
y, x, LEFTS,
(Maze[y][x] == GMINE) ?
GRENREQ : BULREQ,
(PLAYER *) NULL, TRUE, SPACE);
Maze[y][x] = SPACE;
break;
}
}
}
}
# ifdef OOZE
/*
* chkslime:
* handle slime shot exploding
*/
chkslime(bp)
register BULLET *bp;
{
register BULLET *nbp;
switch (Maze[bp->b_y][bp->b_x]) {
case WALL1:
case WALL2:
case WALL3:
# ifdef REFLECT
case WALL4:
case WALL5:
# endif REFLECT
# ifdef RANDOM
case DOOR:
# endif RANDOM
switch (bp->b_face) {
case LEFTS:
bp->b_x++;
break;
case RIGHT:
bp->b_x--;
break;
case ABOVE:
bp->b_y++;
break;
case BELOW:
bp->b_y--;
break;
}
break;
}
nbp = (BULLET *) malloc(sizeof (BULLET));
*nbp = *bp;
# ifdef VOLCANO
moveslime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED);
# else VOLCANO
moveslime(nbp, SLIMESPEED);
# endif VOLCANO
}
/*
* moveslime:
* move the given slime shot speed times and add it back if
* it hasn't fizzled yet
*/
moveslime(bp, speed)
register BULLET *bp;
register int speed;
{
register int i, j, dirmask, count;
register PLAYER *pp;
register BULLET *nbp;
if (speed == 0) {
if (bp->b_charge <= 0)
free((char *) bp);
else
save_bullet(bp);
return;
}
# ifdef VOLCANO
showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*');
# else VOLCANO
showexpl(bp->b_y, bp->b_x, '*');
# endif VOLCANO
switch (Maze[bp->b_y][bp->b_x]) {
case LEFTS:
case RIGHT:
case ABOVE:
case BELOW:
# ifdef FLY
case FLYER:
# endif FLY
pp = play_at(bp->b_y, bp->b_x);
message(pp, "You've been slimed.");
checkdam(pp, bp->b_owner, bp->b_score, MINDAM, bp->b_type);
break;
}
if (--bp->b_charge <= 0) {
free((char *) bp);
return;
}
dirmask = 0;
count = 0;
switch (bp->b_face) {
case LEFTS:
if (!iswall(bp->b_y, bp->b_x - 1))
dirmask |= WEST, count++;
if (!iswall(bp->b_y - 1, bp->b_x))
dirmask |= NORTH, count++;
if (!iswall(bp->b_y + 1, bp->b_x))
dirmask |= SOUTH, count++;
if (dirmask == 0)
if (!iswall(bp->b_y, bp->b_x + 1))
dirmask |= EAST, count++;
break;
case RIGHT:
if (!iswall(bp->b_y, bp->b_x + 1))
dirmask |= EAST, count++;
if (!iswall(bp->b_y - 1, bp->b_x))
dirmask |= NORTH, count++;
if (!iswall(bp->b_y + 1, bp->b_x))
dirmask |= SOUTH, count++;
if (dirmask == 0)
if (!iswall(bp->b_y, bp->b_x - 1))
dirmask |= WEST, count++;
break;
case ABOVE:
if (!iswall(bp->b_y - 1, bp->b_x))
dirmask |= NORTH, count++;
if (!iswall(bp->b_y, bp->b_x - 1))
dirmask |= WEST, count++;
if (!iswall(bp->b_y, bp->b_x + 1))
dirmask |= EAST, count++;
if (dirmask == 0)
if (!iswall(bp->b_y + 1, bp->b_x))
dirmask |= SOUTH, count++;
break;
case BELOW:
if (!iswall(bp->b_y + 1, bp->b_x))
dirmask |= SOUTH, count++;
if (!iswall(bp->b_y, bp->b_x - 1))
dirmask |= WEST, count++;
if (!iswall(bp->b_y, bp->b_x + 1))
dirmask |= EAST, count++;
if (dirmask == 0)
if (!iswall(bp->b_y - 1, bp->b_x))
dirmask |= NORTH, count++;
break;
}
if (count == 0) {
/*
* No place to go. Just sit here for a while and wait
* for adjacent squares to clear out.
*/
save_bullet(bp);
return;
}
if (bp->b_charge < count) {
/* Only bp->b_charge paths may be taken */
while (count > bp->b_charge) {
if (dirmask & WEST)
dirmask &= ~WEST;
else if (dirmask & EAST)
dirmask &= ~EAST;
else if (dirmask & NORTH)
dirmask &= ~NORTH;
else if (dirmask & SOUTH)
dirmask &= ~SOUTH;
count--;
}
}
i = bp->b_charge / count;
j = bp->b_charge % count;
if (dirmask & WEST) {
count--;
nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS,
i, bp->b_owner, bp->b_score, TRUE, SPACE);
moveslime(nbp, speed - 1);
}
if (dirmask & EAST) {
count--;
nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT,
(count < j) ? i + 1 : i, bp->b_owner, bp->b_score,
TRUE, SPACE);
moveslime(nbp, speed - 1);
}
if (dirmask & NORTH) {
count--;
nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE,
(count < j) ? i + 1 : i, bp->b_owner, bp->b_score,
TRUE, SPACE);
moveslime(nbp, speed - 1);
}
if (dirmask & SOUTH) {
count--;
nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW,
(count < j) ? i + 1 : i, bp->b_owner, bp->b_score,
TRUE, SPACE);
moveslime(nbp, speed - 1);
}
free((char *) bp);
}
/*
* iswall:
* returns whether the given location is a wall
*/
iswall(y, x)
register int y, x;
{
if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH)
return TRUE;
switch (Maze[y][x]) {
case WALL1:
case WALL2:
case WALL3:
# ifdef REFLECT
case WALL4:
case WALL5:
# endif REFLECT
# ifdef RANDOM
case DOOR:
# endif RANDOM
# ifdef VOLCANO
case LAVA:
# endif VOLCANO
return TRUE;
}
return FALSE;
}
# endif OOZE
/*
* zapshot:
* Take a shot out of the air.
*/
zapshot(blist, obp)
register BULLET *blist, *obp;
{
register BULLET *bp;
register FLAG explode;
explode = FALSE;
for (bp = blist; bp != NULL; bp = bp->b_next) {
if (bp->b_x != obp->b_x || bp->b_y != obp->b_y)
continue;
if (bp->b_face == obp->b_face)
continue;
explode = TRUE;
break;
}
if (!explode)
return;
explshot(blist, obp->b_y, obp->b_x);
}
/*
* explshot -
* Make all shots at this location blow up
*/
explshot(blist, y, x)
register BULLET *blist;
register int y, x;
{
register BULLET *bp;
for (bp = blist; bp != NULL; bp = bp->b_next)
if (bp->b_x == x && bp->b_y == y) {
bp->b_expl = TRUE;
if (bp->b_owner != NULL)
message(bp->b_owner, "Shot intercepted");
}
}
/*
* play_at:
* Return a pointer to the player at the given location
*/
PLAYER *
play_at(y, x)
register int y, x;
{
register PLAYER *pp;
for (pp = Player; pp < End_player; pp++)
if (pp->p_x == x && pp->p_y == y)
return pp;
fprintf(stderr, "driver: couldn't find player at (%d,%d)\n", x, y);
abort();
/* NOTREACHED */
}
/*
* opposite:
* Return TRUE if the bullet direction faces the opposite direction
* of the player in the maze
*/
opposite(face, dir)
int face;
char dir;
{
switch (face) {
case LEFTS:
return (dir == RIGHT);
case RIGHT:
return (dir == LEFTS);
case ABOVE:
return (dir == BELOW);
case BELOW:
return (dir == ABOVE);
default:
return FALSE;
}
}
/*
* is_bullet:
* Is there a bullet at the given coordinates? If so, return
* a pointer to the bullet, otherwise return NULL
*/
BULLET *
is_bullet(y, x)
register int y, x;
{
register BULLET *bp;
for (bp = Bullets; bp != NULL; bp = bp->b_next)
if (bp->b_y == y && bp->b_x == x)
return bp;
return NULL;
}
/*
* fixshots:
* change the underlying character of the shots at a location
* to the given character.
*/
fixshots(y, x, over)
register int y, x;
char over;
{
register BULLET *bp;
for (bp = Bullets; bp != NULL; bp = bp->b_next)
if (bp->b_y == y && bp->b_x == x)
bp->b_over = over;
}
/*
* find_under:
* find the underlying character for a bullet when it lands
* on another bullet.
*/
find_under(blist, bp)
register BULLET *blist, *bp;
{
register BULLET *nbp;
for (nbp = blist; nbp != NULL; nbp = nbp->b_next)
if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) {
bp->b_over = nbp->b_over;
break;
}
}
/*
* mark_player:
* mark a player as under a shot
*/
mark_player(bp)
register BULLET *bp;
{
register PLAYER *pp;
for (pp = Player; pp < End_player; pp++)
if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
pp->p_undershot = TRUE;
break;
}
}