1*ac1fa6a8Skrw /* $OpenBSD: shots.c,v 1.14 2017/01/21 08:22:57 krw Exp $ */
23faf6791Sd /* $NetBSD: shots.c,v 1.3 1997/10/11 08:13:50 lukem Exp $ */
33faf6791Sd /*
4598075eaSpjanzen * Copyright (c) 1983-2003, Regents of the University of California.
5598075eaSpjanzen * All rights reserved.
6598075eaSpjanzen *
7598075eaSpjanzen * Redistribution and use in source and binary forms, with or without
8598075eaSpjanzen * modification, are permitted provided that the following conditions are
9598075eaSpjanzen * met:
10598075eaSpjanzen *
11598075eaSpjanzen * + Redistributions of source code must retain the above copyright
12598075eaSpjanzen * notice, this list of conditions and the following disclaimer.
13598075eaSpjanzen * + Redistributions in binary form must reproduce the above copyright
14598075eaSpjanzen * notice, this list of conditions and the following disclaimer in the
15598075eaSpjanzen * documentation and/or other materials provided with the distribution.
16598075eaSpjanzen * + Neither the name of the University of California, San Francisco nor
17598075eaSpjanzen * the names of its contributors may be used to endorse or promote
18598075eaSpjanzen * products derived from this software without specific prior written
19598075eaSpjanzen * permission.
20598075eaSpjanzen *
21598075eaSpjanzen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22598075eaSpjanzen * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23598075eaSpjanzen * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24598075eaSpjanzen * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25598075eaSpjanzen * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26598075eaSpjanzen * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27598075eaSpjanzen * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28598075eaSpjanzen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29598075eaSpjanzen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30598075eaSpjanzen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31598075eaSpjanzen * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
323faf6791Sd */
333faf6791Sd
34e29f8a1bSguenther #include <sys/select.h>
353faf6791Sd #include <stdlib.h>
36fab1dce0Sd #include <syslog.h>
379ef48543Smestre
38fab1dce0Sd #include "conf.h"
399ef48543Smestre #include "hunt.h"
40fab1dce0Sd #include "server.h"
413faf6791Sd
423faf6791Sd #define PLUS_DELTA(x, max) if (x < max) x++; else x--
433faf6791Sd #define MINUS_DELTA(x, min) if (x > min) x--; else x++
443faf6791Sd
45c72b5b24Smillert static void chkshot(BULLET *, BULLET *);
46c72b5b24Smillert static void chkslime(BULLET *, BULLET *);
47c72b5b24Smillert static void explshot(BULLET *, int, int);
48c72b5b24Smillert static void find_under(BULLET *, BULLET *);
49c72b5b24Smillert static int iswall(int, int);
50c72b5b24Smillert static void mark_boot(BULLET *);
51c72b5b24Smillert static void mark_player(BULLET *);
52c72b5b24Smillert static int move_drone(BULLET *);
53c72b5b24Smillert static void move_flyer(PLAYER *);
54c72b5b24Smillert static int move_normal_shot(BULLET *);
55c72b5b24Smillert static void move_slime(BULLET *, int, BULLET *);
56c72b5b24Smillert static void save_bullet(BULLET *);
57c72b5b24Smillert static void zapshot(BULLET *, BULLET *);
583faf6791Sd
59c6d62522Sd /* Return true if there is pending activity */
60c6d62522Sd int
can_moveshots(void)610f16a76cSmestre can_moveshots(void)
62c6d62522Sd {
63c6d62522Sd PLAYER *pp;
64c6d62522Sd
65c6d62522Sd /* Bullets are moving? */
66c6d62522Sd if (Bullets)
67c6d62522Sd return 1;
68c6d62522Sd
69c6d62522Sd /* Explosions are happening? */
70c6d62522Sd if (can_rollexpl())
71c6d62522Sd return 1;
72c6d62522Sd
73c6d62522Sd /* Things are flying? */
74c6d62522Sd for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
75c6d62522Sd if (pp->p_flying >= 0)
76c6d62522Sd return 1;
77c6d62522Sd for (pp = Player; pp < End_player; pp++)
78c6d62522Sd if (pp->p_flying >= 0)
79c6d62522Sd return 1;
80c6d62522Sd
81c6d62522Sd /* Everything is quiet: */
82c6d62522Sd return 0;
83c6d62522Sd }
84c6d62522Sd
853faf6791Sd /*
863faf6791Sd * moveshots:
873faf6791Sd * Move the shots already in the air, taking explosions into account
883faf6791Sd */
893faf6791Sd void
moveshots(void)900f16a76cSmestre moveshots(void)
913faf6791Sd {
923faf6791Sd BULLET *bp, *next;
933faf6791Sd PLAYER *pp;
943faf6791Sd int x, y;
953faf6791Sd BULLET *blist;
963faf6791Sd
973faf6791Sd rollexpl();
983faf6791Sd if (Bullets == NULL)
99fab1dce0Sd goto no_bullets;
1003faf6791Sd
1013faf6791Sd /*
102fab1dce0Sd * First we move through the bullet list conf_bulspd times, looking
1033faf6791Sd * for things we may have run into. If we do run into
1043faf6791Sd * something, we set up the explosion and disappear, checking
1053faf6791Sd * for damage to any player who got in the way.
1063faf6791Sd */
1073faf6791Sd
108fab1dce0Sd /* Move the list to a working list */
1093faf6791Sd blist = Bullets;
1103faf6791Sd Bullets = NULL;
111fab1dce0Sd
112fab1dce0Sd /* Work with bullets on the working list (blist) */
1133faf6791Sd for (bp = blist; bp != NULL; bp = next) {
1143faf6791Sd next = bp->b_next;
115fab1dce0Sd
1163faf6791Sd x = bp->b_x;
1173faf6791Sd y = bp->b_y;
1183faf6791Sd
119fab1dce0Sd /* Un-draw the bullet on all screens: */
120fab1dce0Sd Maze[y][x] = bp->b_over;
121fab1dce0Sd check(ALL_PLAYERS, y, x);
122fab1dce0Sd
123fab1dce0Sd /* Decide how to move the bullet: */
1243faf6791Sd switch (bp->b_type) {
125fab1dce0Sd
126fab1dce0Sd /* Normal, atomic bullets: */
1273faf6791Sd case SHOT:
1283faf6791Sd case GRENADE:
1293faf6791Sd case SATCHEL:
1303faf6791Sd case BOMB:
1313faf6791Sd if (move_normal_shot(bp)) {
132fab1dce0Sd /* Still there: put back on the active list */
1333faf6791Sd bp->b_next = Bullets;
1343faf6791Sd Bullets = bp;
1353faf6791Sd }
1363faf6791Sd break;
137fab1dce0Sd
138fab1dce0Sd /* Slime bullets that explode into slime on impact: */
1393faf6791Sd case SLIME:
1403faf6791Sd if (bp->b_expl || move_normal_shot(bp)) {
141fab1dce0Sd /* Still there: put back on the active list */
1423faf6791Sd bp->b_next = Bullets;
1433faf6791Sd Bullets = bp;
1443faf6791Sd }
1453faf6791Sd break;
146fab1dce0Sd
147fab1dce0Sd /* Drones that wander about: */
1483faf6791Sd case DSHOT:
1493faf6791Sd if (move_drone(bp)) {
150fab1dce0Sd /* Still there: put back on the active list */
1513faf6791Sd bp->b_next = Bullets;
1523faf6791Sd Bullets = bp;
1533faf6791Sd }
1543faf6791Sd break;
155fab1dce0Sd
156fab1dce0Sd /* Other/unknown: */
1573faf6791Sd default:
158fab1dce0Sd /* Place it back on the active list: */
1593faf6791Sd bp->b_next = Bullets;
1603faf6791Sd Bullets = bp;
1613faf6791Sd break;
1623faf6791Sd }
1633faf6791Sd }
1643faf6791Sd
165fab1dce0Sd /* Again, hang the Bullets list off `blist' and work with that: */
1663faf6791Sd blist = Bullets;
1673faf6791Sd Bullets = NULL;
1683faf6791Sd for (bp = blist; bp != NULL; bp = next) {
1693faf6791Sd next = bp->b_next;
170fab1dce0Sd /* Is the bullet exploding? */
1713faf6791Sd if (!bp->b_expl) {
172fab1dce0Sd /*
173fab1dce0Sd * Its still flying through the air.
174fab1dce0Sd * Put it back on the bullet list.
175fab1dce0Sd */
1763faf6791Sd save_bullet(bp);
177fab1dce0Sd
178fab1dce0Sd /* All the monitors can see the bullet: */
1793faf6791Sd for (pp = Monitor; pp < End_monitor; pp++)
1803faf6791Sd check(pp, bp->b_y, bp->b_x);
181fab1dce0Sd
182fab1dce0Sd /* All the scanning players can see the drone: */
1833faf6791Sd if (bp->b_type == DSHOT)
1843faf6791Sd for (pp = Player; pp < End_player; pp++)
1853faf6791Sd if (pp->p_scan >= 0)
1863faf6791Sd check(pp, bp->b_y, bp->b_x);
187fab1dce0Sd } else {
188fab1dce0Sd /* It is exploding. Check what we hit: */
1893faf6791Sd chkshot(bp, next);
190fab1dce0Sd /* Release storage for the destroyed bullet: */
191fab1dce0Sd free(bp);
192fab1dce0Sd }
1933faf6791Sd }
1943faf6791Sd
195fab1dce0Sd /* Re-draw all the players: (in case a bullet wiped them out) */
1963faf6791Sd for (pp = Player; pp < End_player; pp++)
1973faf6791Sd Maze[pp->p_y][pp->p_x] = pp->p_face;
1983faf6791Sd
199fab1dce0Sd no_bullets:
200fab1dce0Sd
201fab1dce0Sd /* Move flying boots through the air: */
2023faf6791Sd for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
2033faf6791Sd if (pp->p_flying >= 0)
2043faf6791Sd move_flyer(pp);
205fab1dce0Sd
206fab1dce0Sd /* Move flying players through the air: */
2073faf6791Sd for (pp = Player; pp < End_player; pp++) {
2083faf6791Sd if (pp->p_flying >= 0)
2093faf6791Sd move_flyer(pp);
210fab1dce0Sd /* Flush out the explosions: */
211fab1dce0Sd sendcom(pp, REFRESH);
2123faf6791Sd look(pp);
2133faf6791Sd }
2143faf6791Sd
215fab1dce0Sd /* Flush out and synchronise all the displays: */
216fab1dce0Sd sendcom(ALL_PLAYERS, REFRESH);
2173faf6791Sd }
2183faf6791Sd
2193faf6791Sd /*
2203faf6791Sd * move_normal_shot:
221fab1dce0Sd * Move a normal shot along its trajectory.
222fab1dce0Sd * Returns false if the bullet no longer needs tracking.
2233faf6791Sd */
2243faf6791Sd static int
move_normal_shot(BULLET * bp)2250f16a76cSmestre move_normal_shot(BULLET *bp)
2263faf6791Sd {
2273faf6791Sd int i, x, y;
2283faf6791Sd PLAYER *pp;
2293faf6791Sd
230fab1dce0Sd /*
231fab1dce0Sd * Walk an unexploded bullet along conf_bulspd times, moving it
232fab1dce0Sd * one unit along each step. We flag it as exploding if it
233fab1dce0Sd * meets something.
234fab1dce0Sd */
235fab1dce0Sd
236fab1dce0Sd for (i = 0; i < conf_bulspd; i++) {
237fab1dce0Sd
238fab1dce0Sd /* Stop if the bullet has already exploded: */
2393faf6791Sd if (bp->b_expl)
2403faf6791Sd break;
2413faf6791Sd
242fab1dce0Sd /* Adjust the bullet's co-ordinates: */
2433faf6791Sd x = bp->b_x;
2443faf6791Sd y = bp->b_y;
2453faf6791Sd switch (bp->b_face) {
2463faf6791Sd case LEFTS:
2473faf6791Sd x--;
2483faf6791Sd break;
2493faf6791Sd case RIGHT:
2503faf6791Sd x++;
2513faf6791Sd break;
2523faf6791Sd case ABOVE:
2533faf6791Sd y--;
2543faf6791Sd break;
2553faf6791Sd case BELOW:
2563faf6791Sd y++;
2573faf6791Sd break;
2583faf6791Sd }
2593faf6791Sd
260fab1dce0Sd
261fab1dce0Sd /* Look at what the bullet is colliding with : */
2623faf6791Sd switch (Maze[y][x]) {
263fab1dce0Sd /* Gun shots have a chance of collision: */
2643faf6791Sd case SHOT:
265fab1dce0Sd if (rand_num(100) < conf_pshot_coll) {
2663faf6791Sd zapshot(Bullets, bp);
2673faf6791Sd zapshot(bp->b_next, bp);
2683faf6791Sd }
2693faf6791Sd break;
270fab1dce0Sd /* Grenades only have a chance of collision: */
2713faf6791Sd case GRENADE:
272fab1dce0Sd if (rand_num(100) < conf_pgren_coll) {
2733faf6791Sd zapshot(Bullets, bp);
2743faf6791Sd zapshot(bp->b_next, bp);
2753faf6791Sd }
2763faf6791Sd break;
277fab1dce0Sd /* Reflecting walls richochet the bullet: */
278fab1dce0Sd case WALL4:
2793faf6791Sd switch (bp->b_face) {
2803faf6791Sd case LEFTS:
2813faf6791Sd bp->b_face = BELOW;
2823faf6791Sd break;
2833faf6791Sd case RIGHT:
2843faf6791Sd bp->b_face = ABOVE;
2853faf6791Sd break;
2863faf6791Sd case ABOVE:
2873faf6791Sd bp->b_face = RIGHT;
2883faf6791Sd break;
2893faf6791Sd case BELOW:
2903faf6791Sd bp->b_face = LEFTS;
2913faf6791Sd break;
2923faf6791Sd }
2933faf6791Sd Maze[y][x] = WALL5;
2943faf6791Sd for (pp = Monitor; pp < End_monitor; pp++)
2953faf6791Sd check(pp, y, x);
2963faf6791Sd break;
2973faf6791Sd case WALL5:
2983faf6791Sd switch (bp->b_face) {
2993faf6791Sd case LEFTS:
3003faf6791Sd bp->b_face = ABOVE;
3013faf6791Sd break;
3023faf6791Sd case RIGHT:
3033faf6791Sd bp->b_face = BELOW;
3043faf6791Sd break;
3053faf6791Sd case ABOVE:
3063faf6791Sd bp->b_face = LEFTS;
3073faf6791Sd break;
3083faf6791Sd case BELOW:
3093faf6791Sd bp->b_face = RIGHT;
3103faf6791Sd break;
3113faf6791Sd }
3123faf6791Sd Maze[y][x] = WALL4;
3133faf6791Sd for (pp = Monitor; pp < End_monitor; pp++)
3143faf6791Sd check(pp, y, x);
3153faf6791Sd break;
316fab1dce0Sd /* Dispersion doors randomly disperse bullets: */
3173faf6791Sd case DOOR:
3183faf6791Sd switch (rand_num(4)) {
3193faf6791Sd case 0:
3203faf6791Sd bp->b_face = ABOVE;
3213faf6791Sd break;
3223faf6791Sd case 1:
3233faf6791Sd bp->b_face = BELOW;
3243faf6791Sd break;
3253faf6791Sd case 2:
3263faf6791Sd bp->b_face = LEFTS;
3273faf6791Sd break;
3283faf6791Sd case 3:
3293faf6791Sd bp->b_face = RIGHT;
3303faf6791Sd break;
3313faf6791Sd }
3323faf6791Sd break;
333fab1dce0Sd /* Bullets zing past fliers: */
3343faf6791Sd case FLYER:
3353faf6791Sd pp = play_at(y, x);
3363faf6791Sd message(pp, "Zing!");
3373faf6791Sd break;
338fab1dce0Sd /* Bullets encountering a player: */
3393faf6791Sd case LEFTS:
3403faf6791Sd case RIGHT:
3413faf6791Sd case BELOW:
3423faf6791Sd case ABOVE:
3433faf6791Sd /*
344fab1dce0Sd * Give the person a chance to catch a
345fab1dce0Sd * grenade if s/he is facing it:
3463faf6791Sd */
3473faf6791Sd pp = play_at(y, x);
3483faf6791Sd pp->p_ident->i_shot += bp->b_charge;
3493faf6791Sd if (opposite(bp->b_face, Maze[y][x])) {
350fab1dce0Sd /* Give them a 10% chance: */
351fab1dce0Sd if (rand_num(100) < conf_pgren_catch) {
352fab1dce0Sd /* They caught it! */
3533faf6791Sd if (bp->b_owner != NULL)
3543faf6791Sd message(bp->b_owner,
3553faf6791Sd "Your charge was absorbed!");
356fab1dce0Sd
357fab1dce0Sd /*
358fab1dce0Sd * The target player stole from the bullet's
359fab1dce0Sd * owner. Charge stolen statistics:
360fab1dce0Sd */
3613faf6791Sd if (bp->b_score != NULL)
3623faf6791Sd bp->b_score->i_robbed += bp->b_charge;
363fab1dce0Sd
364fab1dce0Sd /* They acquire more ammo: */
3653faf6791Sd pp->p_ammo += bp->b_charge;
366fab1dce0Sd
367fab1dce0Sd /* Check if it would have destroyed them: */
368fab1dce0Sd if (pp->p_damage + bp->b_size * conf_mindam
3693faf6791Sd > pp->p_damcap)
370fab1dce0Sd /* Lucky escape statistics: */
3713faf6791Sd pp->p_ident->i_saved++;
372fab1dce0Sd
373fab1dce0Sd /* Tell them: */
3743faf6791Sd message(pp, "Absorbed charge (good shield!)");
375fab1dce0Sd
376fab1dce0Sd /* Absorbtion statistics: */
3773faf6791Sd pp->p_ident->i_absorbed += bp->b_charge;
378fab1dce0Sd
379fab1dce0Sd /* Deallocate storage: */
38034944f90Stedu free(bp);
381fab1dce0Sd
382fab1dce0Sd /* Update ammo display: */
383fab1dce0Sd ammo_update(pp);
384fab1dce0Sd
385fab1dce0Sd /* No need for caller to keep tracking it: */
3863faf6791Sd return FALSE;
3873faf6791Sd }
388fab1dce0Sd
389fab1dce0Sd /* Bullets faced head-on (statistics): */
3903faf6791Sd pp->p_ident->i_faced += bp->b_charge;
3913faf6791Sd }
392fab1dce0Sd
3933faf6791Sd /*
3943faf6791Sd * Small chance that the bullet just misses the
3953faf6791Sd * person. If so, the bullet just goes on its
396fab1dce0Sd * merry way without exploding. (5% chance)
3973faf6791Sd */
398fab1dce0Sd if (rand_num(100) < conf_pmiss) {
399fab1dce0Sd /* Ducked statistics: */
4003faf6791Sd pp->p_ident->i_ducked += bp->b_charge;
401fab1dce0Sd
402fab1dce0Sd /* Check if it would have killed them: */
403fab1dce0Sd if (pp->p_damage + bp->b_size * conf_mindam
4043faf6791Sd > pp->p_damcap)
405fab1dce0Sd /* Lucky escape statistics: */
4063faf6791Sd pp->p_ident->i_saved++;
407fab1dce0Sd
408fab1dce0Sd /* Shooter missed statistics: */
4093faf6791Sd if (bp->b_score != NULL)
4103faf6791Sd bp->b_score->i_missed += bp->b_charge;
411fab1dce0Sd
412fab1dce0Sd /* Tell target that they were missed: */
4133faf6791Sd message(pp, "Zing!");
414fab1dce0Sd
415fab1dce0Sd /* Tell the bullet owner they missed: */
416fab1dce0Sd if (bp->b_owner != NULL)
4173faf6791Sd message(bp->b_owner,
4183faf6791Sd ((bp->b_score->i_missed & 0x7) == 0x7) ?
4193faf6791Sd "My! What a bad shot you are!" :
4203faf6791Sd "Missed him");
421fab1dce0Sd
422fab1dce0Sd /* Don't fall through */
4233faf6791Sd break;
424fab1dce0Sd } else {
425fab1dce0Sd /* The player is to be blown up: */
426fab1dce0Sd bp->b_expl = TRUE;
4273faf6791Sd }
428fab1dce0Sd break;
429fab1dce0Sd /* Bullet hits a wall, and always explodes: */
4303faf6791Sd case WALL1:
4313faf6791Sd case WALL2:
4323faf6791Sd case WALL3:
4333faf6791Sd bp->b_expl = TRUE;
4343faf6791Sd break;
4353faf6791Sd }
4363faf6791Sd
437fab1dce0Sd /* Update the bullet's new position: */
4383faf6791Sd bp->b_x = x;
4393faf6791Sd bp->b_y = y;
4403faf6791Sd }
441fab1dce0Sd
442fab1dce0Sd /* Caller should keep tracking the bullet: */
4433faf6791Sd return TRUE;
4443faf6791Sd }
4453faf6791Sd
4463faf6791Sd /*
4473faf6791Sd * move_drone:
4483faf6791Sd * Move the drone to the next square
449fab1dce0Sd * Returns FALSE if the drone need no longer be tracked.
4503faf6791Sd */
451fab1dce0Sd static int
move_drone(BULLET * bp)4520f16a76cSmestre move_drone(BULLET *bp)
4533faf6791Sd {
4543faf6791Sd int mask, count;
455fab1dce0Sd int n, dir = -1;
4563faf6791Sd PLAYER *pp;
4573faf6791Sd
458fab1dce0Sd /* See if we can give someone a blast: */
45950aad205Spjanzen if (is_player(Maze[bp->b_y][bp->b_x - 1])) {
4603faf6791Sd dir = WEST;
4613faf6791Sd goto drone_move;
4623faf6791Sd }
46350aad205Spjanzen if (is_player(Maze[bp->b_y - 1][bp->b_x])) {
4643faf6791Sd dir = NORTH;
4653faf6791Sd goto drone_move;
4663faf6791Sd }
46750aad205Spjanzen if (is_player(Maze[bp->b_y + 1][bp->b_x])) {
4683faf6791Sd dir = SOUTH;
4693faf6791Sd goto drone_move;
4703faf6791Sd }
47150aad205Spjanzen if (is_player(Maze[bp->b_y][bp->b_x + 1])) {
4723faf6791Sd dir = EAST;
4733faf6791Sd goto drone_move;
4743faf6791Sd }
4753faf6791Sd
476fab1dce0Sd /* Find out what directions are clear and move that way: */
4773faf6791Sd mask = count = 0;
4783faf6791Sd if (!iswall(bp->b_y, bp->b_x - 1))
4793faf6791Sd mask |= WEST, count++;
4803faf6791Sd if (!iswall(bp->b_y - 1, bp->b_x))
4813faf6791Sd mask |= NORTH, count++;
4823faf6791Sd if (!iswall(bp->b_y + 1, bp->b_x))
4833faf6791Sd mask |= SOUTH, count++;
4843faf6791Sd if (!iswall(bp->b_y, bp->b_x + 1))
4853faf6791Sd mask |= EAST, count++;
4863faf6791Sd
487fab1dce0Sd /* All blocked up, just wait: */
4883faf6791Sd if (count == 0)
4893faf6791Sd return TRUE;
4903faf6791Sd
491fab1dce0Sd /* Only one way to go: */
4923faf6791Sd if (count == 1) {
4933faf6791Sd dir = mask;
4943faf6791Sd goto drone_move;
4953faf6791Sd }
4963faf6791Sd
497fab1dce0Sd /* Avoid backtracking, and remove the direction we came from: */
4983faf6791Sd switch (bp->b_face) {
4993faf6791Sd case LEFTS:
5003faf6791Sd if (mask & EAST)
5013faf6791Sd mask &= ~EAST, count--;
5023faf6791Sd break;
5033faf6791Sd case RIGHT:
5043faf6791Sd if (mask & WEST)
5053faf6791Sd mask &= ~WEST, count--;
5063faf6791Sd break;
5073faf6791Sd case ABOVE:
5083faf6791Sd if (mask & SOUTH)
5093faf6791Sd mask &= ~SOUTH, count--;
5103faf6791Sd break;
5113faf6791Sd case BELOW:
5123faf6791Sd if (mask & NORTH)
5133faf6791Sd mask &= ~NORTH, count--;
5143faf6791Sd break;
5153faf6791Sd }
5163faf6791Sd
517fab1dce0Sd /* Pick one of the remaining directions: */
5183faf6791Sd n = rand_num(count);
5193faf6791Sd if (n >= 0 && mask & NORTH)
5203faf6791Sd dir = NORTH, n--;
5213faf6791Sd if (n >= 0 && mask & SOUTH)
5223faf6791Sd dir = SOUTH, n--;
5233faf6791Sd if (n >= 0 && mask & EAST)
5243faf6791Sd dir = EAST, n--;
5253faf6791Sd if (n >= 0 && mask & WEST)
5263faf6791Sd dir = WEST, n--;
5273faf6791Sd
5283faf6791Sd drone_move:
529fab1dce0Sd /* Move the drone: */
5303faf6791Sd switch (dir) {
531fab1dce0Sd case -1:
532fab1dce0Sd /* no move */
5333faf6791Sd case WEST:
5343faf6791Sd bp->b_x--;
5353faf6791Sd bp->b_face = LEFTS;
5363faf6791Sd break;
5373faf6791Sd case EAST:
5383faf6791Sd bp->b_x++;
5393faf6791Sd bp->b_face = RIGHT;
5403faf6791Sd break;
5413faf6791Sd case NORTH:
5423faf6791Sd bp->b_y--;
5433faf6791Sd bp->b_face = ABOVE;
5443faf6791Sd break;
5453faf6791Sd case SOUTH:
5463faf6791Sd bp->b_y++;
5473faf6791Sd bp->b_face = BELOW;
5483faf6791Sd break;
5493faf6791Sd }
550fab1dce0Sd
551fab1dce0Sd /* Look at what the drone moved onto: */
5523faf6791Sd switch (Maze[bp->b_y][bp->b_x]) {
5533faf6791Sd case LEFTS:
5543faf6791Sd case RIGHT:
5553faf6791Sd case BELOW:
5563faf6791Sd case ABOVE:
5573faf6791Sd /*
558fab1dce0Sd * Players have a 1% chance of absorbing a drone,
559fab1dce0Sd * if they are facing it.
5603faf6791Sd */
561fab1dce0Sd if (rand_num(100) < conf_pdroneabsorb && opposite(bp->b_face,
562fab1dce0Sd Maze[bp->b_y][bp->b_x])) {
563fab1dce0Sd
564fab1dce0Sd /* Feel the power: */
5653faf6791Sd pp = play_at(bp->b_y, bp->b_x);
5663faf6791Sd pp->p_ammo += bp->b_charge;
5673faf6791Sd message(pp, "**** Absorbed drone ****");
568fab1dce0Sd
569fab1dce0Sd /* Release drone storage: */
57034944f90Stedu free(bp);
571fab1dce0Sd
572fab1dce0Sd /* Update ammo: */
573fab1dce0Sd ammo_update(pp);
574fab1dce0Sd
575fab1dce0Sd /* No need for caller to keep tracking drone: */
5763faf6791Sd return FALSE;
5773faf6791Sd }
578fab1dce0Sd /* Detonate the drone: */
5793faf6791Sd bp->b_expl = TRUE;
5803faf6791Sd break;
5813faf6791Sd }
582fab1dce0Sd
583fab1dce0Sd /* Keep tracking the drone. */
5843faf6791Sd return TRUE;
5853faf6791Sd }
5863faf6791Sd
5873faf6791Sd /*
5883faf6791Sd * save_bullet:
589fab1dce0Sd * Put a bullet back onto the bullet list
5903faf6791Sd */
5913faf6791Sd static void
save_bullet(BULLET * bp)5920f16a76cSmestre save_bullet(BULLET *bp)
5933faf6791Sd {
594fab1dce0Sd
595fab1dce0Sd /* Save what the bullet will be flying over: */
5963faf6791Sd bp->b_over = Maze[bp->b_y][bp->b_x];
597fab1dce0Sd
5983faf6791Sd switch (bp->b_over) {
599fab1dce0Sd /* Bullets that can pass through each other: */
6003faf6791Sd case SHOT:
6013faf6791Sd case GRENADE:
6023faf6791Sd case SATCHEL:
6033faf6791Sd case BOMB:
6043faf6791Sd case SLIME:
6053faf6791Sd case LAVA:
6063faf6791Sd case DSHOT:
6073faf6791Sd find_under(Bullets, bp);
6083faf6791Sd break;
6093faf6791Sd }
6103faf6791Sd
6113faf6791Sd switch (bp->b_over) {
612fab1dce0Sd /* A bullet hits a player: */
6133faf6791Sd case LEFTS:
6143faf6791Sd case RIGHT:
6153faf6791Sd case ABOVE:
6163faf6791Sd case BELOW:
6173faf6791Sd case FLYER:
6183faf6791Sd mark_player(bp);
6193faf6791Sd break;
620fab1dce0Sd
621fab1dce0Sd /* A bullet passes a boot: */
6223faf6791Sd case BOOT:
6233faf6791Sd case BOOT_PAIR:
6243faf6791Sd mark_boot(bp);
625fab1dce0Sd /* FALLTHROUGH */
6263faf6791Sd
627fab1dce0Sd /* The bullet flies over everything else: */
6283faf6791Sd default:
6293faf6791Sd Maze[bp->b_y][bp->b_x] = bp->b_type;
6303faf6791Sd break;
6313faf6791Sd }
6323faf6791Sd
633fab1dce0Sd /* Insert the bullet into the Bullets list: */
6343faf6791Sd bp->b_next = Bullets;
6353faf6791Sd Bullets = bp;
6363faf6791Sd }
6373faf6791Sd
6383faf6791Sd /*
6393faf6791Sd * move_flyer:
6403faf6791Sd * Update the position of a player in flight
6413faf6791Sd */
6423faf6791Sd static void
move_flyer(PLAYER * pp)6430f16a76cSmestre move_flyer(PLAYER *pp)
6443faf6791Sd {
6453faf6791Sd int x, y;
6463faf6791Sd
6473faf6791Sd if (pp->p_undershot) {
6483faf6791Sd fixshots(pp->p_y, pp->p_x, pp->p_over);
6493faf6791Sd pp->p_undershot = FALSE;
6503faf6791Sd }
651fab1dce0Sd
652fab1dce0Sd /* Restore what the flier was flying over */
6533faf6791Sd Maze[pp->p_y][pp->p_x] = pp->p_over;
654fab1dce0Sd
655fab1dce0Sd /* Fly: */
6563faf6791Sd x = pp->p_x + pp->p_flyx;
6573faf6791Sd y = pp->p_y + pp->p_flyy;
658fab1dce0Sd
659fab1dce0Sd /* Bouncing off the edges of the maze: */
6603faf6791Sd if (x < 1) {
6613faf6791Sd x = 1 - x;
6623faf6791Sd pp->p_flyx = -pp->p_flyx;
6633faf6791Sd }
6643faf6791Sd else if (x > WIDTH - 2) {
6653faf6791Sd x = (WIDTH - 2) - (x - (WIDTH - 2));
6663faf6791Sd pp->p_flyx = -pp->p_flyx;
6673faf6791Sd }
6683faf6791Sd if (y < 1) {
6693faf6791Sd y = 1 - y;
6703faf6791Sd pp->p_flyy = -pp->p_flyy;
6713faf6791Sd }
6723faf6791Sd else if (y > HEIGHT - 2) {
6733faf6791Sd y = (HEIGHT - 2) - (y - (HEIGHT - 2));
6743faf6791Sd pp->p_flyy = -pp->p_flyy;
6753faf6791Sd }
676fab1dce0Sd
677fab1dce0Sd /* Make sure we don't land on something we can't: */
6783faf6791Sd again:
6793faf6791Sd switch (Maze[y][x]) {
6803faf6791Sd default:
681fab1dce0Sd /*
682fab1dce0Sd * Flier is over something other than space, a wall
683fab1dce0Sd * or a door. Randomly move (drift) the flier a little bit
684fab1dce0Sd * and then try again:
685fab1dce0Sd */
6863faf6791Sd switch (rand_num(4)) {
6873faf6791Sd case 0:
6883faf6791Sd PLUS_DELTA(x, WIDTH - 2);
6893faf6791Sd break;
6903faf6791Sd case 1:
6913faf6791Sd MINUS_DELTA(x, 1);
6923faf6791Sd break;
6933faf6791Sd case 2:
6943faf6791Sd PLUS_DELTA(y, HEIGHT - 2);
6953faf6791Sd break;
6963faf6791Sd case 3:
6973faf6791Sd MINUS_DELTA(y, 1);
6983faf6791Sd break;
6993faf6791Sd }
7003faf6791Sd goto again;
701fab1dce0Sd /* Give a little boost when about to land on a wall or door: */
7023faf6791Sd case WALL1:
7033faf6791Sd case WALL2:
7043faf6791Sd case WALL3:
7053faf6791Sd case WALL4:
7063faf6791Sd case WALL5:
7073faf6791Sd case DOOR:
7083faf6791Sd if (pp->p_flying == 0)
7093faf6791Sd pp->p_flying++;
7103faf6791Sd break;
711fab1dce0Sd /* Spaces are okay: */
7123faf6791Sd case SPACE:
7133faf6791Sd break;
7143faf6791Sd }
715fab1dce0Sd
716fab1dce0Sd /* Update flier's coordinates: */
7173faf6791Sd pp->p_y = y;
7183faf6791Sd pp->p_x = x;
719fab1dce0Sd
720fab1dce0Sd /* Consume 'flying' time: */
7213faf6791Sd if (pp->p_flying-- == 0) {
722fab1dce0Sd /* Land: */
7233faf6791Sd if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) {
724fab1dce0Sd /* Land a player - they stustain a fall: */
7253faf6791Sd checkdam(pp, (PLAYER *) NULL, (IDENT *) NULL,
726fab1dce0Sd rand_num(pp->p_damage / conf_fall_frac), FALL);
7273faf6791Sd pp->p_face = rand_dir();
7283faf6791Sd showstat(pp);
729fab1dce0Sd } else {
730fab1dce0Sd /* Land boots: */
7313faf6791Sd if (Maze[y][x] == BOOT)
7323faf6791Sd pp->p_face = BOOT_PAIR;
7333faf6791Sd Maze[y][x] = SPACE;
7343faf6791Sd }
7353faf6791Sd }
736fab1dce0Sd
737fab1dce0Sd /* Save under the flier: */
7383faf6791Sd pp->p_over = Maze[y][x];
739fab1dce0Sd /* Draw in the flier: */
7403faf6791Sd Maze[y][x] = pp->p_face;
7413faf6791Sd showexpl(y, x, pp->p_face);
7423faf6791Sd }
7433faf6791Sd
7443faf6791Sd /*
7453faf6791Sd * chkshot
7463faf6791Sd * Handle explosions
7473faf6791Sd */
7483faf6791Sd static void
chkshot(BULLET * bp,BULLET * next)7490f16a76cSmestre chkshot(BULLET *bp, BULLET *next)
7503faf6791Sd {
7513faf6791Sd int y, x;
7523faf6791Sd int dy, dx, absdy;
7533faf6791Sd int delta, damage;
7543faf6791Sd char expl;
7553faf6791Sd PLAYER *pp;
7563faf6791Sd
7573faf6791Sd delta = 0;
7583faf6791Sd switch (bp->b_type) {
7593faf6791Sd case SHOT:
7603faf6791Sd case MINE:
7613faf6791Sd case GRENADE:
7623faf6791Sd case GMINE:
7633faf6791Sd case SATCHEL:
7643faf6791Sd case BOMB:
7653faf6791Sd delta = bp->b_size - 1;
7663faf6791Sd break;
7673faf6791Sd case SLIME:
7683faf6791Sd case LAVA:
7693faf6791Sd chkslime(bp, next);
7703faf6791Sd return;
7713faf6791Sd case DSHOT:
7723faf6791Sd bp->b_type = SLIME;
7733faf6791Sd chkslime(bp, next);
7743faf6791Sd return;
7753faf6791Sd }
776fab1dce0Sd
777fab1dce0Sd /* Draw the explosion square: */
7783faf6791Sd for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) {
7793faf6791Sd if (y < 0 || y >= HEIGHT)
7803faf6791Sd continue;
7813faf6791Sd dy = y - bp->b_y;
7823faf6791Sd absdy = (dy < 0) ? -dy : dy;
7833faf6791Sd for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) {
784fab1dce0Sd /* Draw a part of the explosion cloud: */
7853faf6791Sd if (x < 0 || x >= WIDTH)
7863faf6791Sd continue;
7873faf6791Sd dx = x - bp->b_x;
7883faf6791Sd if (dx == 0)
7893faf6791Sd expl = (dy == 0) ? '*' : '|';
7903faf6791Sd else if (dy == 0)
7913faf6791Sd expl = '-';
7923faf6791Sd else if (dx == dy)
7933faf6791Sd expl = '\\';
7943faf6791Sd else if (dx == -dy)
7953faf6791Sd expl = '/';
7963faf6791Sd else
7973faf6791Sd expl = '*';
7983faf6791Sd showexpl(y, x, expl);
799fab1dce0Sd
800fab1dce0Sd /* Check what poor bastard was in the explosion: */
8013faf6791Sd switch (Maze[y][x]) {
8023faf6791Sd case LEFTS:
8033faf6791Sd case RIGHT:
8043faf6791Sd case ABOVE:
8053faf6791Sd case BELOW:
8063faf6791Sd case FLYER:
8073faf6791Sd if (dx < 0)
8083faf6791Sd dx = -dx;
8093faf6791Sd if (absdy > dx)
8103faf6791Sd damage = bp->b_size - absdy;
8113faf6791Sd else
8123faf6791Sd damage = bp->b_size - dx;
813fab1dce0Sd
814fab1dce0Sd /* Everybody hurts, sometimes. */
8153faf6791Sd pp = play_at(y, x);
8163faf6791Sd checkdam(pp, bp->b_owner, bp->b_score,
817fab1dce0Sd damage * conf_mindam, bp->b_type);
8183faf6791Sd break;
8193faf6791Sd case GMINE:
8203faf6791Sd case MINE:
821fab1dce0Sd /* Mines detonate in a chain reaction: */
8223faf6791Sd add_shot((Maze[y][x] == GMINE) ?
8233faf6791Sd GRENADE : SHOT,
8243faf6791Sd y, x, LEFTS,
8253faf6791Sd (Maze[y][x] == GMINE) ?
8263faf6791Sd GRENREQ : BULREQ,
8273faf6791Sd (PLAYER *) NULL, TRUE, SPACE);
8283faf6791Sd Maze[y][x] = SPACE;
8293faf6791Sd break;
8303faf6791Sd }
8313faf6791Sd }
8323faf6791Sd }
8333faf6791Sd }
8343faf6791Sd
8353faf6791Sd /*
8363faf6791Sd * chkslime:
8373faf6791Sd * handle slime shot exploding
8383faf6791Sd */
8393faf6791Sd static void
chkslime(BULLET * bp,BULLET * next)8400f16a76cSmestre chkslime(BULLET *bp, BULLET *next)
8413faf6791Sd {
8423faf6791Sd BULLET *nbp;
8433faf6791Sd
8443faf6791Sd switch (Maze[bp->b_y][bp->b_x]) {
845fab1dce0Sd /* Slime explodes on walls and doors: */
8463faf6791Sd case WALL1:
8473faf6791Sd case WALL2:
8483faf6791Sd case WALL3:
8493faf6791Sd case WALL4:
8503faf6791Sd case WALL5:
8513faf6791Sd case DOOR:
8523faf6791Sd switch (bp->b_face) {
8533faf6791Sd case LEFTS:
8543faf6791Sd bp->b_x++;
8553faf6791Sd break;
8563faf6791Sd case RIGHT:
8573faf6791Sd bp->b_x--;
8583faf6791Sd break;
8593faf6791Sd case ABOVE:
8603faf6791Sd bp->b_y++;
8613faf6791Sd break;
8623faf6791Sd case BELOW:
8633faf6791Sd bp->b_y--;
8643faf6791Sd break;
8653faf6791Sd }
8663faf6791Sd break;
8673faf6791Sd }
868fab1dce0Sd
869fab1dce0Sd /* Duplicate the unit of slime: */
870ca161728Sderaadt nbp = malloc(sizeof (BULLET));
871fab1dce0Sd if (nbp == NULL) {
8723fc386a2Sespie logit(LOG_ERR, "malloc");
873fab1dce0Sd return;
874fab1dce0Sd }
8753faf6791Sd *nbp = *bp;
876fab1dce0Sd
877fab1dce0Sd /* Move it around: */
878fab1dce0Sd move_slime(nbp, nbp->b_type == SLIME ? conf_slimespeed :
879fab1dce0Sd conf_lavaspeed, next);
8803faf6791Sd }
8813faf6791Sd
8823faf6791Sd /*
8833faf6791Sd * move_slime:
8843faf6791Sd * move the given slime shot speed times and add it back if
8853faf6791Sd * it hasn't fizzled yet
8863faf6791Sd */
887fab1dce0Sd static void
move_slime(BULLET * bp,int speed,BULLET * next)8880f16a76cSmestre move_slime(BULLET *bp, int speed, BULLET *next)
8893faf6791Sd {
8903faf6791Sd int i, j, dirmask, count;
8913faf6791Sd PLAYER *pp;
8923faf6791Sd BULLET *nbp;
8933faf6791Sd
8943faf6791Sd if (speed == 0) {
8953faf6791Sd if (bp->b_charge <= 0)
89634944f90Stedu free(bp);
8973faf6791Sd else
8983faf6791Sd save_bullet(bp);
8993faf6791Sd return;
9003faf6791Sd }
9013faf6791Sd
902fab1dce0Sd /* Draw it: */
9033faf6791Sd showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*');
904fab1dce0Sd
9053faf6791Sd switch (Maze[bp->b_y][bp->b_x]) {
906fab1dce0Sd /* Someone got hit by slime or lava: */
9073faf6791Sd case LEFTS:
9083faf6791Sd case RIGHT:
9093faf6791Sd case ABOVE:
9103faf6791Sd case BELOW:
9113faf6791Sd case FLYER:
9123faf6791Sd pp = play_at(bp->b_y, bp->b_x);
9133faf6791Sd message(pp, "You've been slimed.");
914fab1dce0Sd checkdam(pp, bp->b_owner, bp->b_score, conf_mindam, bp->b_type);
9153faf6791Sd break;
916fab1dce0Sd /* Bullets detonate in slime and lava: */
9173faf6791Sd case SHOT:
9183faf6791Sd case GRENADE:
9193faf6791Sd case SATCHEL:
9203faf6791Sd case BOMB:
9213faf6791Sd case DSHOT:
9223faf6791Sd explshot(next, bp->b_y, bp->b_x);
9233faf6791Sd explshot(Bullets, bp->b_y, bp->b_x);
9243faf6791Sd break;
9253faf6791Sd }
9263faf6791Sd
927fab1dce0Sd
928fab1dce0Sd /* Drain the slime/lava of some energy: */
9293faf6791Sd if (--bp->b_charge <= 0) {
930fab1dce0Sd /* It fizzled: */
931fab1dce0Sd free(bp);
9323faf6791Sd return;
9333faf6791Sd }
9343faf6791Sd
935fab1dce0Sd /* Figure out which way the slime should flow: */
9363faf6791Sd dirmask = 0;
9373faf6791Sd count = 0;
9383faf6791Sd switch (bp->b_face) {
9393faf6791Sd case LEFTS:
9403faf6791Sd if (!iswall(bp->b_y, bp->b_x - 1))
9413faf6791Sd dirmask |= WEST, count++;
9423faf6791Sd if (!iswall(bp->b_y - 1, bp->b_x))
9433faf6791Sd dirmask |= NORTH, count++;
9443faf6791Sd if (!iswall(bp->b_y + 1, bp->b_x))
9453faf6791Sd dirmask |= SOUTH, count++;
9463faf6791Sd if (dirmask == 0)
9473faf6791Sd if (!iswall(bp->b_y, bp->b_x + 1))
9483faf6791Sd dirmask |= EAST, count++;
9493faf6791Sd break;
9503faf6791Sd case RIGHT:
9513faf6791Sd if (!iswall(bp->b_y, bp->b_x + 1))
9523faf6791Sd dirmask |= EAST, count++;
9533faf6791Sd if (!iswall(bp->b_y - 1, bp->b_x))
9543faf6791Sd dirmask |= NORTH, count++;
9553faf6791Sd if (!iswall(bp->b_y + 1, bp->b_x))
9563faf6791Sd dirmask |= SOUTH, count++;
9573faf6791Sd if (dirmask == 0)
9583faf6791Sd if (!iswall(bp->b_y, bp->b_x - 1))
9593faf6791Sd dirmask |= WEST, count++;
9603faf6791Sd break;
9613faf6791Sd case ABOVE:
9623faf6791Sd if (!iswall(bp->b_y - 1, bp->b_x))
9633faf6791Sd dirmask |= NORTH, count++;
9643faf6791Sd if (!iswall(bp->b_y, bp->b_x - 1))
9653faf6791Sd dirmask |= WEST, count++;
9663faf6791Sd if (!iswall(bp->b_y, bp->b_x + 1))
9673faf6791Sd dirmask |= EAST, count++;
9683faf6791Sd if (dirmask == 0)
9693faf6791Sd if (!iswall(bp->b_y + 1, bp->b_x))
9703faf6791Sd dirmask |= SOUTH, count++;
9713faf6791Sd break;
9723faf6791Sd case BELOW:
9733faf6791Sd if (!iswall(bp->b_y + 1, bp->b_x))
9743faf6791Sd dirmask |= SOUTH, count++;
9753faf6791Sd if (!iswall(bp->b_y, bp->b_x - 1))
9763faf6791Sd dirmask |= WEST, count++;
9773faf6791Sd if (!iswall(bp->b_y, bp->b_x + 1))
9783faf6791Sd dirmask |= EAST, count++;
9793faf6791Sd if (dirmask == 0)
9803faf6791Sd if (!iswall(bp->b_y - 1, bp->b_x))
9813faf6791Sd dirmask |= NORTH, count++;
9823faf6791Sd break;
9833faf6791Sd }
9843faf6791Sd if (count == 0) {
9853faf6791Sd /*
9863faf6791Sd * No place to go. Just sit here for a while and wait
9873faf6791Sd * for adjacent squares to clear out.
9883faf6791Sd */
9893faf6791Sd save_bullet(bp);
9903faf6791Sd return;
9913faf6791Sd }
9923faf6791Sd if (bp->b_charge < count) {
9933faf6791Sd /* Only bp->b_charge paths may be taken */
9943faf6791Sd while (count > bp->b_charge) {
9953faf6791Sd if (dirmask & WEST)
9963faf6791Sd dirmask &= ~WEST;
9973faf6791Sd else if (dirmask & EAST)
9983faf6791Sd dirmask &= ~EAST;
9993faf6791Sd else if (dirmask & NORTH)
10003faf6791Sd dirmask &= ~NORTH;
10013faf6791Sd else if (dirmask & SOUTH)
10023faf6791Sd dirmask &= ~SOUTH;
10033faf6791Sd count--;
10043faf6791Sd }
10053faf6791Sd }
10063faf6791Sd
1007fab1dce0Sd /* Spawn little slimes off in every possible direction: */
10083faf6791Sd i = bp->b_charge / count;
10093faf6791Sd j = bp->b_charge % count;
10103faf6791Sd if (dirmask & WEST) {
10113faf6791Sd count--;
10123faf6791Sd nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS,
10133faf6791Sd i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE);
10143faf6791Sd move_slime(nbp, speed - 1, next);
10153faf6791Sd }
10163faf6791Sd if (dirmask & EAST) {
10173faf6791Sd count--;
10183faf6791Sd nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT,
10193faf6791Sd (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
10203faf6791Sd bp->b_score, TRUE, SPACE);
10213faf6791Sd move_slime(nbp, speed - 1, next);
10223faf6791Sd }
10233faf6791Sd if (dirmask & NORTH) {
10243faf6791Sd count--;
10253faf6791Sd nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE,
10263faf6791Sd (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
10273faf6791Sd bp->b_score, TRUE, SPACE);
10283faf6791Sd move_slime(nbp, speed - 1, next);
10293faf6791Sd }
10303faf6791Sd if (dirmask & SOUTH) {
10313faf6791Sd count--;
10323faf6791Sd nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW,
10333faf6791Sd (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
10343faf6791Sd bp->b_score, TRUE, SPACE);
10353faf6791Sd move_slime(nbp, speed - 1, next);
10363faf6791Sd }
10373faf6791Sd
103834944f90Stedu free(bp);
10393faf6791Sd }
10403faf6791Sd
10413faf6791Sd /*
10423faf6791Sd * iswall:
10433faf6791Sd * returns whether the given location is a wall
10443faf6791Sd */
10453faf6791Sd static int
iswall(int y,int x)10460f16a76cSmestre iswall(int y, int x)
10473faf6791Sd {
10483faf6791Sd if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH)
10493faf6791Sd return TRUE;
10503faf6791Sd switch (Maze[y][x]) {
10513faf6791Sd case WALL1:
10523faf6791Sd case WALL2:
10533faf6791Sd case WALL3:
10543faf6791Sd case WALL4:
10553faf6791Sd case WALL5:
10563faf6791Sd case DOOR:
10573faf6791Sd case SLIME:
10583faf6791Sd case LAVA:
10593faf6791Sd return TRUE;
10603faf6791Sd }
10613faf6791Sd return FALSE;
10623faf6791Sd }
10633faf6791Sd
10643faf6791Sd /*
10653faf6791Sd * zapshot:
10663faf6791Sd * Take a shot out of the air.
10673faf6791Sd */
10683faf6791Sd static void
zapshot(BULLET * blist,BULLET * obp)10690f16a76cSmestre zapshot(BULLET *blist, BULLET *obp)
10703faf6791Sd {
10713faf6791Sd BULLET *bp;
10723faf6791Sd
10733faf6791Sd for (bp = blist; bp != NULL; bp = bp->b_next) {
1074fab1dce0Sd /* Find co-located bullets not facing the same way: */
1075fab1dce0Sd if (bp->b_face != obp->b_face
1076fab1dce0Sd && bp->b_x == obp->b_x && bp->b_y == obp->b_y)
1077fab1dce0Sd {
1078fab1dce0Sd /* Bullet collision: */
10793faf6791Sd explshot(blist, obp->b_y, obp->b_x);
1080fab1dce0Sd return;
1081fab1dce0Sd }
1082fab1dce0Sd }
10833faf6791Sd }
10843faf6791Sd
10853faf6791Sd /*
10863faf6791Sd * explshot -
10873faf6791Sd * Make all shots at this location blow up
10883faf6791Sd */
1089fab1dce0Sd static void
explshot(BULLET * blist,int y,int x)10900f16a76cSmestre explshot(BULLET *blist, int y, int x)
10913faf6791Sd {
10923faf6791Sd BULLET *bp;
10933faf6791Sd
10943faf6791Sd for (bp = blist; bp != NULL; bp = bp->b_next)
10953faf6791Sd if (bp->b_x == x && bp->b_y == y) {
10963faf6791Sd bp->b_expl = TRUE;
10973faf6791Sd if (bp->b_owner != NULL)
1098fab1dce0Sd message(bp->b_owner, "Shot intercepted.");
10993faf6791Sd }
11003faf6791Sd }
11013faf6791Sd
11023faf6791Sd /*
11033faf6791Sd * play_at:
11043faf6791Sd * Return a pointer to the player at the given location
11053faf6791Sd */
11063faf6791Sd PLAYER *
play_at(int y,int x)11070f16a76cSmestre play_at(int y, int x)
11083faf6791Sd {
11093faf6791Sd PLAYER *pp;
11103faf6791Sd
11113faf6791Sd for (pp = Player; pp < End_player; pp++)
11123faf6791Sd if (pp->p_x == x && pp->p_y == y)
11133faf6791Sd return pp;
1114fab1dce0Sd
1115fab1dce0Sd /* Internal fault: */
1116c6d62522Sd logx(LOG_ERR, "play_at: not a player");
1117fab1dce0Sd abort();
11183faf6791Sd }
11193faf6791Sd
11203faf6791Sd /*
11213faf6791Sd * opposite:
11223faf6791Sd * Return TRUE if the bullet direction faces the opposite direction
11233faf6791Sd * of the player in the maze
11243faf6791Sd */
11253faf6791Sd int
opposite(int face,char dir)11260f16a76cSmestre opposite(int face, char dir)
11273faf6791Sd {
11283faf6791Sd switch (face) {
11293faf6791Sd case LEFTS:
11303faf6791Sd return (dir == RIGHT);
11313faf6791Sd case RIGHT:
11323faf6791Sd return (dir == LEFTS);
11333faf6791Sd case ABOVE:
11343faf6791Sd return (dir == BELOW);
11353faf6791Sd case BELOW:
11363faf6791Sd return (dir == ABOVE);
11373faf6791Sd default:
11383faf6791Sd return FALSE;
11393faf6791Sd }
11403faf6791Sd }
11413faf6791Sd
11423faf6791Sd /*
11433faf6791Sd * is_bullet:
11443faf6791Sd * Is there a bullet at the given coordinates? If so, return
11453faf6791Sd * a pointer to the bullet, otherwise return NULL
11463faf6791Sd */
11473faf6791Sd BULLET *
is_bullet(int y,int x)11480f16a76cSmestre is_bullet(int y, int x)
11493faf6791Sd {
11503faf6791Sd BULLET *bp;
11513faf6791Sd
11523faf6791Sd for (bp = Bullets; bp != NULL; bp = bp->b_next)
11533faf6791Sd if (bp->b_y == y && bp->b_x == x)
11543faf6791Sd return bp;
11553faf6791Sd return NULL;
11563faf6791Sd }
11573faf6791Sd
11583faf6791Sd /*
11593faf6791Sd * fixshots:
11603faf6791Sd * change the underlying character of the shots at a location
11613faf6791Sd * to the given character.
11623faf6791Sd */
11633faf6791Sd void
fixshots(int y,int x,char over)11640f16a76cSmestre fixshots(int y, int x, char over)
11653faf6791Sd {
11663faf6791Sd BULLET *bp;
11673faf6791Sd
11683faf6791Sd for (bp = Bullets; bp != NULL; bp = bp->b_next)
11693faf6791Sd if (bp->b_y == y && bp->b_x == x)
11703faf6791Sd bp->b_over = over;
11713faf6791Sd }
11723faf6791Sd
11733faf6791Sd /*
11743faf6791Sd * find_under:
11753faf6791Sd * find the underlying character for a bullet when it lands
11763faf6791Sd * on another bullet.
11773faf6791Sd */
11783faf6791Sd static void
find_under(BULLET * blist,BULLET * bp)11790f16a76cSmestre find_under(BULLET *blist, BULLET *bp)
11803faf6791Sd {
11813faf6791Sd BULLET *nbp;
11823faf6791Sd
11833faf6791Sd for (nbp = blist; nbp != NULL; nbp = nbp->b_next)
11843faf6791Sd if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) {
11853faf6791Sd bp->b_over = nbp->b_over;
11863faf6791Sd break;
11873faf6791Sd }
11883faf6791Sd }
11893faf6791Sd
11903faf6791Sd /*
11913faf6791Sd * mark_player:
11923faf6791Sd * mark a player as under a shot
11933faf6791Sd */
11943faf6791Sd static void
mark_player(BULLET * bp)11950f16a76cSmestre mark_player(BULLET *bp)
11963faf6791Sd {
11973faf6791Sd PLAYER *pp;
11983faf6791Sd
11993faf6791Sd for (pp = Player; pp < End_player; pp++)
12003faf6791Sd if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
12013faf6791Sd pp->p_undershot = TRUE;
12023faf6791Sd break;
12033faf6791Sd }
12043faf6791Sd }
12053faf6791Sd
12063faf6791Sd /*
12073faf6791Sd * mark_boot:
12083faf6791Sd * mark a boot as under a shot
12093faf6791Sd */
12103faf6791Sd static void
mark_boot(BULLET * bp)12110f16a76cSmestre mark_boot(BULLET *bp)
12123faf6791Sd {
12133faf6791Sd PLAYER *pp;
12143faf6791Sd
12153faf6791Sd for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
12163faf6791Sd if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
12173faf6791Sd pp->p_undershot = TRUE;
12183faf6791Sd break;
12193faf6791Sd }
12203faf6791Sd }
1221