/* $NetBSD: shots.c,v 1.16 2021/05/02 12:50:45 rillig Exp $ */
/*
* Copyright (c) 1983-2003, Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* + Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* + Neither the name of the University of California, San Francisco nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: shots.c,v 1.16 2021/05/02 12:50:45 rillig Exp $");
#endif /* not lint */
#include <err.h>
#include <signal.h>
#include <stdlib.h>
#include "hunt.h"
#define PLUS_DELTA(x, max) if (x < max) x++; else x--
#define MINUS_DELTA(x, min) if (x > min) x--; else x++
static void chkshot(BULLET *, BULLET *);
static void chkslime(BULLET *, BULLET *);
static void explshot(BULLET *, int, int);
static void find_under(BULLET *, BULLET *);
static bool iswall(int, int);
static void mark_boot(BULLET *);
static void mark_player(BULLET *);
#ifdef DRONE
static void move_drone(BULLET *);
#endif
static void move_flyer(PLAYER *);
static int move_normal_shot(BULLET *);
static void move_slime(BULLET *, int, BULLET *);
static void save_bullet(BULLET *);
static void zapshot(BULLET *, BULLET *);
/*
* moveshots:
* Move the shots already in the air, taking explosions into account
*/
void
moveshots(void)
{
BULLET *bp, *next;
PLAYER *pp;
int x, y;
BULLET *blist;
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
switch (bp->b_type) {
case SHOT:
case GRENADE:
case SATCHEL:
case BOMB:
if (move_normal_shot(bp)) {
bp->b_next = Bullets;
Bullets = bp;
}
break;
#ifdef OOZE
case SLIME:
if (bp->b_expl || move_normal_shot(bp)) {
bp->b_next = Bullets;
Bullets = bp;
}
break;
#endif
#ifdef DRONE
case DSHOT:
if (move_drone(bp)) {
bp->b_next = Bullets;
Bullets = bp;
}
break;
#endif
default:
bp->b_next = Bullets;
Bullets = bp;
break;
}
}
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
#ifdef DRONE
if (bp->b_type == DSHOT)
for (pp = Player; pp < End_player; pp++)
if (pp->p_scan >= 0)
check(pp, bp->b_y, bp->b_x);
#endif
continue;
}
chkshot(bp, next);
free(bp);
}
for (pp = Player; pp < End_player; pp++)
Maze[pp->p_y][pp->p_x] = pp->p_face;
ret:
#ifdef BOOTS
for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
if (pp->p_flying >= 0)
move_flyer(pp);
#endif
for (pp = Player; pp < End_player; pp++) {
#ifdef FLY
if (pp->p_flying >= 0)
move_flyer(pp);
#endif
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
return;
}
/*
* move_normal_shot:
* Move a normal shot along its trajectory
*/
static int
move_normal_shot(BULLET *bp)
{
int i, x, y;
PLAYER *pp;
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(bp->b_next, bp);
}
break;
case GRENADE:
if (rand_num(100) < 10) {
zapshot(Bullets, bp);
zapshot(bp->b_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
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
break;
#endif
#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
#ifdef FLY
case FLYER:
pp = play_at(y, x);
message(pp, "Zing!");
break;
#endif
case LEFTS:
case RIGHT:
case BELOW:
case ABOVE:
/*
* give the person a chance to catch a
* grenade if s/he is facing it
*/
pp = play_at(y, x);
pp->p_ident->i_shot += bp->b_charge;
if (opposite(bp->b_face, Maze[y][x])) {
if (rand_num(100) < 10) {
if (bp->b_owner != NULL)
message(bp->b_owner,
"Your charge was absorbed!");
if (bp->b_score != NULL)
bp->b_score->i_robbed += bp->b_charge;
pp->p_ammo += bp->b_charge;
if (pp->p_damage + bp->b_size * MINDAM
> pp->p_damcap)
pp->p_ident->i_saved++;
message(pp, "Absorbed charge (good shield!)");
pp->p_ident->i_absorbed += bp->b_charge;
free(bp);
(void) snprintf(Buf, sizeof(Buf),
"%3d", pp->p_ammo);
cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
outstr(pp, Buf, 3);
return false;
}
pp->p_ident->i_faced += bp->b_charge;
}
/*
* Small chance that the bullet just misses the
* person. If so, the bullet just goes on its
* merry way without exploding.
*/
if (rand_num(100) < 5) {
pp->p_ident->i_ducked += bp->b_charge;
if (pp->p_damage + bp->b_size * MINDAM
> pp->p_damcap)
pp->p_ident->i_saved++;
if (bp->b_score != NULL)
bp->b_score->i_missed += bp->b_charge;
message(pp, "Zing!");
if (bp->b_owner == NULL)
break;
message(bp->b_owner, bp->b_score &&
((bp->b_score->i_missed & 0x7) == 0x7) ?
"My! What a bad shot you are!" :
"Missed him");
break;
}
/*
* The shot hit that sucker! Blow it up.
*/
#ifndef RANDOM
/* FALLTHROUGH */
case DOOR:
#endif
/* FALLTHROUGH */
case WALL1:
case WALL2:
case WALL3:
bp->b_expl = true;
break;
}
bp->b_x = x;
bp->b_y = y;
}
return true;
}
#ifdef DRONE
/*
* move_drone:
* Move the drone to the next square
*/
static void
move_drone(BULLET *bp)
{
int mask, count;
int n, dir;
PLAYER *pp;
/*
* See if we can give someone a blast
*/
if (isplayer(Maze[bp->b_y][bp->b_x - 1])) {
dir = WEST;
goto drone_move;
}
if (isplayer(Maze[bp->b_y - 1][bp->b_x])) {
dir = NORTH;
goto drone_move;
}
if (isplayer(Maze[bp->b_y + 1][bp->b_x])) {
dir = SOUTH;
goto drone_move;
}
if (isplayer(Maze[bp->b_y][bp->b_x + 1])) {
dir = EAST;
goto drone_move;
}
/*
* Find out what directions are clear
*/
mask = count = 0;
if (!iswall(bp->b_y, bp->b_x - 1))
mask |= WEST, count++;
if (!iswall(bp->b_y - 1, bp->b_x))
mask |= NORTH, count++;
if (!iswall(bp->b_y + 1, bp->b_x))
mask |= SOUTH, count++;
if (!iswall(bp->b_y, bp->b_x + 1))
mask |= EAST, count++;
/*
* All blocked up, just you wait
*/
if (count == 0)
return true;
/*
* Only one way to go.
*/
if (count == 1) {
dir = mask;
goto drone_move;
}
/*
* Get rid of the direction that we came from
*/
switch (bp->b_face) {
case LEFTS:
if (mask & EAST)
mask &= ~EAST, count--;
break;
case RIGHT:
if (mask & WEST)
mask &= ~WEST, count--;
break;
case ABOVE:
if (mask & SOUTH)
mask &= ~SOUTH, count--;
break;
case BELOW:
if (mask & NORTH)
mask &= ~NORTH, count--;
break;
}
/*
* Pick one of the remaining directions
*/
n = rand_num(count);
if (n >= 0 && mask & NORTH)
dir = NORTH, n--;
if (n >= 0 && mask & SOUTH)
dir = SOUTH, n--;
if (n >= 0 && mask & EAST)
dir = EAST, n--;
if (n >= 0 && mask & WEST)
dir = WEST, n--;
/*
* Now that we know the direction of movement,
* just update the position of the drone
*/
drone_move:
switch (dir) {
case WEST:
bp->b_x--;
bp->b_face = LEFTS;
break;
case EAST:
bp->b_x++;
bp->b_face = RIGHT;
break;
case NORTH:
bp->b_y--;
bp->b_face = ABOVE;
break;
case SOUTH:
bp->b_y++;
bp->b_face = BELOW;
break;
}
switch (Maze[bp->b_y][bp->b_x]) {
case LEFTS:
case RIGHT:
case BELOW:
case ABOVE:
/*
* give the person a chance to catch a
* drone if s/he is facing it
*/
if (rand_num(100) < 1 &&
opposite(bp->b_face, Maze[bp->b_y][bp->b_x])) {
pp = play_at(bp->b_y, bp->b_x);
pp->p_ammo += bp->b_charge;
message(pp, "**** Absorbed drone ****");
free(bp);
(void) snprintf(Buf, sizeof(buf), "%3d", pp->p_ammo);
cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
outstr(pp, Buf, 3);
return false;
}
bp->b_expl = true;
break;
}
return true;
}
#endif
/*
* save_bullet:
* Put this bullet back onto the bullet list
*/
static void
save_bullet(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
#endif
#ifdef DRONE
case DSHOT:
#endif
find_under(Bullets, bp);
break;
}
switch (bp->b_over) {
case LEFTS:
case RIGHT:
case ABOVE:
case BELOW:
#ifdef FLY
case FLYER:
#endif
mark_player(bp);
break;
#ifdef BOOTS
case BOOT:
case BOOT_PAIR:
mark_boot(bp);
break;
#endif
default:
Maze[bp->b_y][bp->b_x] = bp->b_type;
break;
}
bp->b_next = Bullets;
Bullets = bp;
}
/*
* move_flyer:
* Update the position of a player in flight
*/
static void
move_flyer(PLAYER *pp)
{
int x, y;
if (pp->p_undershot) {
fixshots(pp->p_y, pp->p_x, pp->p_over);
pp->p_undershot = false;
}
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]) {
default:
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
#ifdef RANDOM
case DOOR:
#endif
if (pp->p_flying == 0)
pp->p_flying++;
break;
case SPACE:
break;
}
pp->p_y = y;
pp->p_x = x;
if (pp->p_flying-- == 0) {
#ifdef BOOTS
if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) {
#endif
checkdam(pp, NULL, NULL,
rand_num(pp->p_damage / 5), FALL);
pp->p_face = rand_dir();
showstat(pp);
#ifdef BOOTS
}
else {
if (Maze[y][x] == BOOT)
pp->p_face = BOOT_PAIR;
Maze[y][x] = SPACE;
}
#endif
}
pp->p_over = Maze[y][x];
Maze[y][x] = pp->p_face;
showexpl(y, x, pp->p_face);
}
/*
* chkshot
* Handle explosions
*/
static void
chkshot(BULLET *bp, BULLET *next)
{
int y, x;
int dy, dx, absdy;
int delta, damage;
char expl;
PLAYER *pp;
delta = 0;
switch (bp->b_type) {
case SHOT:
case MINE:
case GRENADE:
case GMINE:
case SATCHEL:
case BOMB:
delta = bp->b_size - 1;
break;
#ifdef OOZE
case SLIME:
#ifdef VOLCANO
case LAVA:
#endif
chkslime(bp, next);
return;
#endif
#ifdef DRONE
case DSHOT:
bp->b_type = SLIME;
chkslime(bp, next);
return;
#endif
}
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
if (dx < 0)
dx = -dx;
if (absdy > dx)
damage = bp->b_size - absdy;
else
damage = bp->b_size - dx;
pp = play_at(y, x);
checkdam(pp, bp->b_owner, bp->b_score,
damage * 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,
NULL, true, SPACE);
Maze[y][x] = SPACE;
break;
}
}
}
}
#ifdef OOZE
/*
* chkslime:
* handle slime shot exploding
*/
static void
chkslime(BULLET *bp, BULLET *next)
{
BULLET *nbp;
switch (Maze[bp->b_y][bp->b_x]) {
case WALL1:
case WALL2:
case WALL3:
#ifdef REFLECT
case WALL4:
case WALL5:
#endif
#ifdef RANDOM
case DOOR:
#endif
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 = malloc(sizeof(*nbp));
*nbp = *bp;
#ifdef VOLCANO
move_slime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED, next);
#else
move_slime(nbp, SLIMESPEED, next);
#endif
}
/*
* move_slime:
* move the given slime shot speed times and add it back if
* it hasn't fizzled yet
*/
static void
move_slime(BULLET *bp, int speed, BULLET *next)
{
int i, j, dirmask, count;
PLAYER *pp;
BULLET *nbp;
if (speed == 0) {
if (bp->b_charge <= 0)
free(bp);
else
save_bullet(bp);
return;
}
#ifdef VOLCANO
showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*');
#else
showexpl(bp->b_y, bp->b_x, '*');
#endif
switch (Maze[bp->b_y][bp->b_x]) {
case LEFTS:
case RIGHT:
case ABOVE:
case BELOW:
#ifdef FLY
case FLYER:
#endif
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;
case SHOT:
case GRENADE:
case SATCHEL:
case BOMB:
#ifdef DRONE
case DSHOT:
#endif
explshot(next, bp->b_y, bp->b_x);
explshot(Bullets, bp->b_y, bp->b_x);
break;
}
if (--bp->b_charge <= 0) {
free(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_size, bp->b_owner, bp->b_score, true, SPACE);
move_slime(nbp, speed - 1, next);
}
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_size, bp->b_owner,
bp->b_score, true, SPACE);
move_slime(nbp, speed - 1, next);
}
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_size, bp->b_owner,
bp->b_score, true, SPACE);
move_slime(nbp, speed - 1, next);
}
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_size, bp->b_owner,
bp->b_score, true, SPACE);
move_slime(nbp, speed - 1, next);
}
free(bp);
}
/*
* iswall:
* returns whether the given location is a wall
*/
static bool
iswall(int y, int 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
#ifdef RANDOM
case DOOR:
#endif
#ifdef OOZE
case SLIME:
#ifdef VOLCANO
case LAVA:
#endif
#endif
return true;
}
return false;
}
#endif
/*
* zapshot:
* Take a shot out of the air.
*/
static void
zapshot(BULLET *blist, BULLET *obp)
{
BULLET *bp;
bool 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
*/
static void
explshot(BULLET *blist, int y, int x)
{
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(int y, int x)
{
PLAYER *pp;
for (pp = Player; pp < End_player; pp++)
if (pp->p_x == x && pp->p_y == y)
return pp;
errx(1, "driver: couldn't find player at (%d,%d)", x, y);
/* NOTREACHED */
}
/*
* opposite:
* Return true if the bullet direction faces the opposite direction
* of the player in the maze
*/
bool
opposite(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(int y, int x)
{
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.
*/
void
fixshots(int y, int x, char over)
{
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.
*/
static void
find_under(BULLET *blist, BULLET *bp)
{
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
*/
static void
mark_player(BULLET *bp)
{
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;
}
}
#ifdef BOOTS
/*
* mark_boot:
* mark a boot as under a shot
*/
static void
mark_boot(BULLET *bp)
{
PLAYER *pp;
for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
pp->p_undershot = true;
break;
}
}
#endif