Bug Summary

File:src/games/hunt/huntd/shots.c
Warning:line 418, column 8
Access to field 'i_missed' results in a dereference of a null pointer (loaded from field 'b_score')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name shots.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/games/hunt/huntd/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/games/hunt/huntd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/games/hunt/huntd/shots.c
1/* $OpenBSD: shots.c,v 1.14 2017/01/21 08:22:57 krw Exp $ */
2/* $NetBSD: shots.c,v 1.3 1997/10/11 08:13:50 lukem Exp $ */
3/*
4 * Copyright (c) 1983-2003, Regents of the University of California.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * + Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * + Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * + Neither the name of the University of California, San Francisco nor
17 * the names of its contributors may be used to endorse or promote
18 * products derived from this software without specific prior written
19 * permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/select.h>
35#include <stdlib.h>
36#include <syslog.h>
37
38#include "conf.h"
39#include "hunt.h"
40#include "server.h"
41
42#define PLUS_DELTA(x, max)if (x < max) x++; else x-- if (x < max) x++; else x--
43#define MINUS_DELTA(x, min)if (x > min) x--; else x++ if (x > min) x--; else x++
44
45static void chkshot(BULLET *, BULLET *);
46static void chkslime(BULLET *, BULLET *);
47static void explshot(BULLET *, int, int);
48static void find_under(BULLET *, BULLET *);
49static int iswall(int, int);
50static void mark_boot(BULLET *);
51static void mark_player(BULLET *);
52static int move_drone(BULLET *);
53static void move_flyer(PLAYER *);
54static int move_normal_shot(BULLET *);
55static void move_slime(BULLET *, int, BULLET *);
56static void save_bullet(BULLET *);
57static void zapshot(BULLET *, BULLET *);
58
59/* Return true if there is pending activity */
60int
61can_moveshots(void)
62{
63 PLAYER *pp;
64
65 /* Bullets are moving? */
66 if (Bullets)
67 return 1;
68
69 /* Explosions are happening? */
70 if (can_rollexpl())
71 return 1;
72
73 /* Things are flying? */
74 for (pp = Boot; pp < &Boot[NBOOTS2]; pp++)
75 if (pp->p_flying >= 0)
76 return 1;
77 for (pp = Player; pp < End_player; pp++)
78 if (pp->p_flying >= 0)
79 return 1;
80
81 /* Everything is quiet: */
82 return 0;
83}
84
85/*
86 * moveshots:
87 * Move the shots already in the air, taking explosions into account
88 */
89void
90moveshots(void)
91{
92 BULLET *bp, *next;
93 PLAYER *pp;
94 int x, y;
95 BULLET *blist;
96
97 rollexpl();
98 if (Bullets == NULL((void *)0))
1
Assuming 'Bullets' is not equal to NULL
2
Taking false branch
99 goto no_bullets;
100
101 /*
102 * First we move through the bullet list conf_bulspd times, looking
103 * for things we may have run into. If we do run into
104 * something, we set up the explosion and disappear, checking
105 * for damage to any player who got in the way.
106 */
107
108 /* Move the list to a working list */
109 blist = Bullets;
110 Bullets = NULL((void *)0);
111
112 /* Work with bullets on the working list (blist) */
113 for (bp = blist; bp
2.1
'bp' is not equal to NULL
!= NULL((void *)0); bp = next) {
3
Loop condition is true. Entering loop body
114 next = bp->b_next;
115
116 x = bp->b_x;
117 y = bp->b_y;
118
119 /* Un-draw the bullet on all screens: */
120 Maze[y][x] = bp->b_over;
121 check(ALL_PLAYERS((PLAYER *)1), y, x);
122
123 /* Decide how to move the bullet: */
124 switch (bp->b_type) {
4
Control jumps to 'case 36:' at line 139
125
126 /* Normal, atomic bullets: */
127 case SHOT':':
128 case GRENADE'o':
129 case SATCHEL'O':
130 case BOMB'@':
131 if (move_normal_shot(bp)) {
132 /* Still there: put back on the active list */
133 bp->b_next = Bullets;
134 Bullets = bp;
135 }
136 break;
137
138 /* Slime bullets that explode into slime on impact: */
139 case SLIME'$':
140 if (bp->b_expl || move_normal_shot(bp)) {
5
Assuming field 'b_expl' is 0
6
Calling 'move_normal_shot'
141 /* Still there: put back on the active list */
142 bp->b_next = Bullets;
143 Bullets = bp;
144 }
145 break;
146
147 /* Drones that wander about: */
148 case DSHOT'?':
149 if (move_drone(bp)) {
150 /* Still there: put back on the active list */
151 bp->b_next = Bullets;
152 Bullets = bp;
153 }
154 break;
155
156 /* Other/unknown: */
157 default:
158 /* Place it back on the active list: */
159 bp->b_next = Bullets;
160 Bullets = bp;
161 break;
162 }
163 }
164
165 /* Again, hang the Bullets list off `blist' and work with that: */
166 blist = Bullets;
167 Bullets = NULL((void *)0);
168 for (bp = blist; bp != NULL((void *)0); bp = next) {
169 next = bp->b_next;
170 /* Is the bullet exploding? */
171 if (!bp->b_expl) {
172 /*
173 * Its still flying through the air.
174 * Put it back on the bullet list.
175 */
176 save_bullet(bp);
177
178 /* All the monitors can see the bullet: */
179 for (pp = Monitor; pp < End_monitor; pp++)
180 check(pp, bp->b_y, bp->b_x);
181
182 /* All the scanning players can see the drone: */
183 if (bp->b_type == DSHOT'?')
184 for (pp = Player; pp < End_player; pp++)
185 if (pp->p_scan >= 0)
186 check(pp, bp->b_y, bp->b_x);
187 } else {
188 /* It is exploding. Check what we hit: */
189 chkshot(bp, next);
190 /* Release storage for the destroyed bullet: */
191 free(bp);
192 }
193 }
194
195 /* Re-draw all the players: (in case a bullet wiped them out) */
196 for (pp = Player; pp < End_player; pp++)
197 Maze[pp->p_y][pp->p_x] = pp->p_face;
198
199no_bullets:
200
201 /* Move flying boots through the air: */
202 for (pp = Boot; pp < &Boot[NBOOTS2]; pp++)
203 if (pp->p_flying >= 0)
204 move_flyer(pp);
205
206 /* Move flying players through the air: */
207 for (pp = Player; pp < End_player; pp++) {
208 if (pp->p_flying >= 0)
209 move_flyer(pp);
210 /* Flush out the explosions: */
211 sendcom(pp, REFRESH('r' | 0200));
212 look(pp);
213 }
214
215 /* Flush out and synchronise all the displays: */
216 sendcom(ALL_PLAYERS((PLAYER *)1), REFRESH('r' | 0200));
217}
218
219/*
220 * move_normal_shot:
221 * Move a normal shot along its trajectory.
222 * Returns false if the bullet no longer needs tracking.
223 */
224static int
225move_normal_shot(BULLET *bp)
226{
227 int i, x, y;
228 PLAYER *pp;
229
230 /*
231 * Walk an unexploded bullet along conf_bulspd times, moving it
232 * one unit along each step. We flag it as exploding if it
233 * meets something.
234 */
235
236 for (i = 0; i < conf_bulspd; i++) {
7
Assuming 'i' is < 'conf_bulspd'
8
Loop condition is true. Entering loop body
237
238 /* Stop if the bullet has already exploded: */
239 if (bp->b_expl
8.1
Field 'b_expl' is 0
)
9
Taking false branch
240 break;
241
242 /* Adjust the bullet's co-ordinates: */
243 x = bp->b_x;
244 y = bp->b_y;
245 switch (bp->b_face) {
10
'Default' branch taken. Execution continues on line 262
246 case LEFTS'{':
247 x--;
248 break;
249 case RIGHT'}':
250 x++;
251 break;
252 case ABOVE'i':
253 y--;
254 break;
255 case BELOW'!':
256 y++;
257 break;
258 }
259
260
261 /* Look at what the bullet is colliding with : */
262 switch (Maze[y][x]) {
11
Control jumps to 'case 105:' at line 342
263 /* Gun shots have a chance of collision: */
264 case SHOT':':
265 if (rand_num(100) < conf_pshot_coll) {
266 zapshot(Bullets, bp);
267 zapshot(bp->b_next, bp);
268 }
269 break;
270 /* Grenades only have a chance of collision: */
271 case GRENADE'o':
272 if (rand_num(100) < conf_pgren_coll) {
273 zapshot(Bullets, bp);
274 zapshot(bp->b_next, bp);
275 }
276 break;
277 /* Reflecting walls richochet the bullet: */
278 case WALL4'/':
279 switch (bp->b_face) {
280 case LEFTS'{':
281 bp->b_face = BELOW'!';
282 break;
283 case RIGHT'}':
284 bp->b_face = ABOVE'i';
285 break;
286 case ABOVE'i':
287 bp->b_face = RIGHT'}';
288 break;
289 case BELOW'!':
290 bp->b_face = LEFTS'{';
291 break;
292 }
293 Maze[y][x] = WALL5'\\';
294 for (pp = Monitor; pp < End_monitor; pp++)
295 check(pp, y, x);
296 break;
297 case WALL5'\\':
298 switch (bp->b_face) {
299 case LEFTS'{':
300 bp->b_face = ABOVE'i';
301 break;
302 case RIGHT'}':
303 bp->b_face = BELOW'!';
304 break;
305 case ABOVE'i':
306 bp->b_face = LEFTS'{';
307 break;
308 case BELOW'!':
309 bp->b_face = RIGHT'}';
310 break;
311 }
312 Maze[y][x] = WALL4'/';
313 for (pp = Monitor; pp < End_monitor; pp++)
314 check(pp, y, x);
315 break;
316 /* Dispersion doors randomly disperse bullets: */
317 case DOOR'#':
318 switch (rand_num(4)) {
319 case 0:
320 bp->b_face = ABOVE'i';
321 break;
322 case 1:
323 bp->b_face = BELOW'!';
324 break;
325 case 2:
326 bp->b_face = LEFTS'{';
327 break;
328 case 3:
329 bp->b_face = RIGHT'}';
330 break;
331 }
332 break;
333 /* Bullets zing past fliers: */
334 case FLYER'&':
335 pp = play_at(y, x);
336 message(pp, "Zing!");
337 break;
338 /* Bullets encountering a player: */
339 case LEFTS'{':
340 case RIGHT'}':
341 case BELOW'!':
342 case ABOVE'i':
343 /*
344 * Give the person a chance to catch a
345 * grenade if s/he is facing it:
346 */
347 pp = play_at(y, x);
348 pp->p_ident->i_shot += bp->b_charge;
349 if (opposite(bp->b_face, Maze[y][x])) {
12
Calling 'opposite'
15
Returning from 'opposite'
16
Taking false branch
350 /* Give them a 10% chance: */
351 if (rand_num(100) < conf_pgren_catch) {
352 /* They caught it! */
353 if (bp->b_owner != NULL((void *)0))
354 message(bp->b_owner,
355 "Your charge was absorbed!");
356
357 /*
358 * The target player stole from the bullet's
359 * owner. Charge stolen statistics:
360 */
361 if (bp->b_score != NULL((void *)0))
362 bp->b_score->i_robbed += bp->b_charge;
363
364 /* They acquire more ammo: */
365 pp->p_ammo += bp->b_charge;
366
367 /* Check if it would have destroyed them: */
368 if (pp->p_damage + bp->b_size * conf_mindam
369 > pp->p_damcap)
370 /* Lucky escape statistics: */
371 pp->p_ident->i_saved++;
372
373 /* Tell them: */
374 message(pp, "Absorbed charge (good shield!)");
375
376 /* Absorbtion statistics: */
377 pp->p_ident->i_absorbed += bp->b_charge;
378
379 /* Deallocate storage: */
380 free(bp);
381
382 /* Update ammo display: */
383 ammo_update(pp);
384
385 /* No need for caller to keep tracking it: */
386 return FALSE0;
387 }
388
389 /* Bullets faced head-on (statistics): */
390 pp->p_ident->i_faced += bp->b_charge;
391 }
392
393 /*
394 * Small chance that the bullet just misses the
395 * person. If so, the bullet just goes on its
396 * merry way without exploding. (5% chance)
397 */
398 if (rand_num(100) < conf_pmiss) {
17
Assuming the condition is true
18
Taking true branch
399 /* Ducked statistics: */
400 pp->p_ident->i_ducked += bp->b_charge;
401
402 /* Check if it would have killed them: */
403 if (pp->p_damage + bp->b_size * conf_mindam
19
Assuming the condition is false
20
Taking false branch
404 > pp->p_damcap)
405 /* Lucky escape statistics: */
406 pp->p_ident->i_saved++;
407
408 /* Shooter missed statistics: */
409 if (bp->b_score != NULL((void *)0))
21
Assuming field 'b_score' is equal to NULL
22
Taking false branch
410 bp->b_score->i_missed += bp->b_charge;
411
412 /* Tell target that they were missed: */
413 message(pp, "Zing!");
414
415 /* Tell the bullet owner they missed: */
416 if (bp->b_owner != NULL((void *)0))
23
Assuming field 'b_owner' is not equal to NULL
24
Taking true branch
417 message(bp->b_owner,
418 ((bp->b_score->i_missed & 0x7) == 0x7) ?
25
Access to field 'i_missed' results in a dereference of a null pointer (loaded from field 'b_score')
419 "My! What a bad shot you are!" :
420 "Missed him");
421
422 /* Don't fall through */
423 break;
424 } else {
425 /* The player is to be blown up: */
426 bp->b_expl = TRUE1;
427 }
428 break;
429 /* Bullet hits a wall, and always explodes: */
430 case WALL1'-':
431 case WALL2'|':
432 case WALL3'+':
433 bp->b_expl = TRUE1;
434 break;
435 }
436
437 /* Update the bullet's new position: */
438 bp->b_x = x;
439 bp->b_y = y;
440 }
441
442 /* Caller should keep tracking the bullet: */
443 return TRUE1;
444}
445
446/*
447 * move_drone:
448 * Move the drone to the next square
449 * Returns FALSE if the drone need no longer be tracked.
450 */
451static int
452move_drone(BULLET *bp)
453{
454 int mask, count;
455 int n, dir = -1;
456 PLAYER *pp;
457
458 /* See if we can give someone a blast: */
459 if (is_player(Maze[bp->b_y][bp->b_x - 1])(Maze[bp->b_y][bp->b_x - 1] == '{' || Maze[bp->b_y][
bp->b_x - 1] == '}' || Maze[bp->b_y][bp->b_x - 1] ==
'i' || Maze[bp->b_y][bp->b_x - 1] == '!' || Maze[bp->
b_y][bp->b_x - 1] == '&')
) {
460 dir = WEST020;
461 goto drone_move;
462 }
463 if (is_player(Maze[bp->b_y - 1][bp->b_x])(Maze[bp->b_y - 1][bp->b_x] == '{' || Maze[bp->b_y -
1][bp->b_x] == '}' || Maze[bp->b_y - 1][bp->b_x] ==
'i' || Maze[bp->b_y - 1][bp->b_x] == '!' || Maze[bp->
b_y - 1][bp->b_x] == '&')
) {
464 dir = NORTH01;
465 goto drone_move;
466 }
467 if (is_player(Maze[bp->b_y + 1][bp->b_x])(Maze[bp->b_y + 1][bp->b_x] == '{' || Maze[bp->b_y +
1][bp->b_x] == '}' || Maze[bp->b_y + 1][bp->b_x] ==
'i' || Maze[bp->b_y + 1][bp->b_x] == '!' || Maze[bp->
b_y + 1][bp->b_x] == '&')
) {
468 dir = SOUTH02;
469 goto drone_move;
470 }
471 if (is_player(Maze[bp->b_y][bp->b_x + 1])(Maze[bp->b_y][bp->b_x + 1] == '{' || Maze[bp->b_y][
bp->b_x + 1] == '}' || Maze[bp->b_y][bp->b_x + 1] ==
'i' || Maze[bp->b_y][bp->b_x + 1] == '!' || Maze[bp->
b_y][bp->b_x + 1] == '&')
) {
472 dir = EAST010;
473 goto drone_move;
474 }
475
476 /* Find out what directions are clear and move that way: */
477 mask = count = 0;
478 if (!iswall(bp->b_y, bp->b_x - 1))
479 mask |= WEST020, count++;
480 if (!iswall(bp->b_y - 1, bp->b_x))
481 mask |= NORTH01, count++;
482 if (!iswall(bp->b_y + 1, bp->b_x))
483 mask |= SOUTH02, count++;
484 if (!iswall(bp->b_y, bp->b_x + 1))
485 mask |= EAST010, count++;
486
487 /* All blocked up, just wait: */
488 if (count == 0)
489 return TRUE1;
490
491 /* Only one way to go: */
492 if (count == 1) {
493 dir = mask;
494 goto drone_move;
495 }
496
497 /* Avoid backtracking, and remove the direction we came from: */
498 switch (bp->b_face) {
499 case LEFTS'{':
500 if (mask & EAST010)
501 mask &= ~EAST010, count--;
502 break;
503 case RIGHT'}':
504 if (mask & WEST020)
505 mask &= ~WEST020, count--;
506 break;
507 case ABOVE'i':
508 if (mask & SOUTH02)
509 mask &= ~SOUTH02, count--;
510 break;
511 case BELOW'!':
512 if (mask & NORTH01)
513 mask &= ~NORTH01, count--;
514 break;
515 }
516
517 /* Pick one of the remaining directions: */
518 n = rand_num(count);
519 if (n >= 0 && mask & NORTH01)
520 dir = NORTH01, n--;
521 if (n >= 0 && mask & SOUTH02)
522 dir = SOUTH02, n--;
523 if (n >= 0 && mask & EAST010)
524 dir = EAST010, n--;
525 if (n >= 0 && mask & WEST020)
526 dir = WEST020, n--;
527
528drone_move:
529 /* Move the drone: */
530 switch (dir) {
531 case -1:
532 /* no move */
533 case WEST020:
534 bp->b_x--;
535 bp->b_face = LEFTS'{';
536 break;
537 case EAST010:
538 bp->b_x++;
539 bp->b_face = RIGHT'}';
540 break;
541 case NORTH01:
542 bp->b_y--;
543 bp->b_face = ABOVE'i';
544 break;
545 case SOUTH02:
546 bp->b_y++;
547 bp->b_face = BELOW'!';
548 break;
549 }
550
551 /* Look at what the drone moved onto: */
552 switch (Maze[bp->b_y][bp->b_x]) {
553 case LEFTS'{':
554 case RIGHT'}':
555 case BELOW'!':
556 case ABOVE'i':
557 /*
558 * Players have a 1% chance of absorbing a drone,
559 * if they are facing it.
560 */
561 if (rand_num(100) < conf_pdroneabsorb && opposite(bp->b_face,
562 Maze[bp->b_y][bp->b_x])) {
563
564 /* Feel the power: */
565 pp = play_at(bp->b_y, bp->b_x);
566 pp->p_ammo += bp->b_charge;
567 message(pp, "**** Absorbed drone ****");
568
569 /* Release drone storage: */
570 free(bp);
571
572 /* Update ammo: */
573 ammo_update(pp);
574
575 /* No need for caller to keep tracking drone: */
576 return FALSE0;
577 }
578 /* Detonate the drone: */
579 bp->b_expl = TRUE1;
580 break;
581 }
582
583 /* Keep tracking the drone. */
584 return TRUE1;
585}
586
587/*
588 * save_bullet:
589 * Put a bullet back onto the bullet list
590 */
591static void
592save_bullet(BULLET *bp)
593{
594
595 /* Save what the bullet will be flying over: */
596 bp->b_over = Maze[bp->b_y][bp->b_x];
597
598 switch (bp->b_over) {
599 /* Bullets that can pass through each other: */
600 case SHOT':':
601 case GRENADE'o':
602 case SATCHEL'O':
603 case BOMB'@':
604 case SLIME'$':
605 case LAVA'~':
606 case DSHOT'?':
607 find_under(Bullets, bp);
608 break;
609 }
610
611 switch (bp->b_over) {
612 /* A bullet hits a player: */
613 case LEFTS'{':
614 case RIGHT'}':
615 case ABOVE'i':
616 case BELOW'!':
617 case FLYER'&':
618 mark_player(bp);
619 break;
620
621 /* A bullet passes a boot: */
622 case BOOT'b':
623 case BOOT_PAIR'B':
624 mark_boot(bp);
625 /* FALLTHROUGH */
626
627 /* The bullet flies over everything else: */
628 default:
629 Maze[bp->b_y][bp->b_x] = bp->b_type;
630 break;
631 }
632
633 /* Insert the bullet into the Bullets list: */
634 bp->b_next = Bullets;
635 Bullets = bp;
636}
637
638/*
639 * move_flyer:
640 * Update the position of a player in flight
641 */
642static void
643move_flyer(PLAYER *pp)
644{
645 int x, y;
646
647 if (pp->p_undershot) {
648 fixshots(pp->p_y, pp->p_x, pp->p_over);
649 pp->p_undershot = FALSE0;
650 }
651
652 /* Restore what the flier was flying over */
653 Maze[pp->p_y][pp->p_x] = pp->p_over;
654
655 /* Fly: */
656 x = pp->p_x + pp->p_flyx;
657 y = pp->p_y + pp->p_flyy;
658
659 /* Bouncing off the edges of the maze: */
660 if (x < 1) {
661 x = 1 - x;
662 pp->p_flyx = -pp->p_flyx;
663 }
664 else if (x > WIDTH51 - 2) {
665 x = (WIDTH51 - 2) - (x - (WIDTH51 - 2));
666 pp->p_flyx = -pp->p_flyx;
667 }
668 if (y < 1) {
669 y = 1 - y;
670 pp->p_flyy = -pp->p_flyy;
671 }
672 else if (y > HEIGHT23 - 2) {
673 y = (HEIGHT23 - 2) - (y - (HEIGHT23 - 2));
674 pp->p_flyy = -pp->p_flyy;
675 }
676
677 /* Make sure we don't land on something we can't: */
678again:
679 switch (Maze[y][x]) {
680 default:
681 /*
682 * Flier is over something other than space, a wall
683 * or a door. Randomly move (drift) the flier a little bit
684 * and then try again:
685 */
686 switch (rand_num(4)) {
687 case 0:
688 PLUS_DELTA(x, WIDTH - 2)if (x < 51 - 2) x++; else x--;
689 break;
690 case 1:
691 MINUS_DELTA(x, 1)if (x > 1) x--; else x++;
692 break;
693 case 2:
694 PLUS_DELTA(y, HEIGHT - 2)if (y < 23 - 2) y++; else y--;
695 break;
696 case 3:
697 MINUS_DELTA(y, 1)if (y > 1) y--; else y++;
698 break;
699 }
700 goto again;
701 /* Give a little boost when about to land on a wall or door: */
702 case WALL1'-':
703 case WALL2'|':
704 case WALL3'+':
705 case WALL4'/':
706 case WALL5'\\':
707 case DOOR'#':
708 if (pp->p_flying == 0)
709 pp->p_flying++;
710 break;
711 /* Spaces are okay: */
712 case SPACE' ':
713 break;
714 }
715
716 /* Update flier's coordinates: */
717 pp->p_y = y;
718 pp->p_x = x;
719
720 /* Consume 'flying' time: */
721 if (pp->p_flying-- == 0) {
722 /* Land: */
723 if (pp->p_face != BOOT'b' && pp->p_face != BOOT_PAIR'B') {
724 /* Land a player - they stustain a fall: */
725 checkdam(pp, (PLAYER *) NULL((void *)0), (IDENT *) NULL((void *)0),
726 rand_num(pp->p_damage / conf_fall_frac), FALL'F');
727 pp->p_face = rand_dir();
728 showstat(pp);
729 } else {
730 /* Land boots: */
731 if (Maze[y][x] == BOOT'b')
732 pp->p_face = BOOT_PAIR'B';
733 Maze[y][x] = SPACE' ';
734 }
735 }
736
737 /* Save under the flier: */
738 pp->p_over = Maze[y][x];
739 /* Draw in the flier: */
740 Maze[y][x] = pp->p_face;
741 showexpl(y, x, pp->p_face);
742}
743
744/*
745 * chkshot
746 * Handle explosions
747 */
748static void
749chkshot(BULLET *bp, BULLET *next)
750{
751 int y, x;
752 int dy, dx, absdy;
753 int delta, damage;
754 char expl;
755 PLAYER *pp;
756
757 delta = 0;
758 switch (bp->b_type) {
759 case SHOT':':
760 case MINE';':
761 case GRENADE'o':
762 case GMINE'g':
763 case SATCHEL'O':
764 case BOMB'@':
765 delta = bp->b_size - 1;
766 break;
767 case SLIME'$':
768 case LAVA'~':
769 chkslime(bp, next);
770 return;
771 case DSHOT'?':
772 bp->b_type = SLIME'$';
773 chkslime(bp, next);
774 return;
775 }
776
777 /* Draw the explosion square: */
778 for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) {
779 if (y < 0 || y >= HEIGHT23)
780 continue;
781 dy = y - bp->b_y;
782 absdy = (dy < 0) ? -dy : dy;
783 for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) {
784 /* Draw a part of the explosion cloud: */
785 if (x < 0 || x >= WIDTH51)
786 continue;
787 dx = x - bp->b_x;
788 if (dx == 0)
789 expl = (dy == 0) ? '*' : '|';
790 else if (dy == 0)
791 expl = '-';
792 else if (dx == dy)
793 expl = '\\';
794 else if (dx == -dy)
795 expl = '/';
796 else
797 expl = '*';
798 showexpl(y, x, expl);
799
800 /* Check what poor bastard was in the explosion: */
801 switch (Maze[y][x]) {
802 case LEFTS'{':
803 case RIGHT'}':
804 case ABOVE'i':
805 case BELOW'!':
806 case FLYER'&':
807 if (dx < 0)
808 dx = -dx;
809 if (absdy > dx)
810 damage = bp->b_size - absdy;
811 else
812 damage = bp->b_size - dx;
813
814 /* Everybody hurts, sometimes. */
815 pp = play_at(y, x);
816 checkdam(pp, bp->b_owner, bp->b_score,
817 damage * conf_mindam, bp->b_type);
818 break;
819 case GMINE'g':
820 case MINE';':
821 /* Mines detonate in a chain reaction: */
822 add_shot((Maze[y][x] == GMINE'g') ?
823 GRENADE'o' : SHOT':',
824 y, x, LEFTS'{',
825 (Maze[y][x] == GMINE'g') ?
826 GRENREQ9 : BULREQ1,
827 (PLAYER *) NULL((void *)0), TRUE1, SPACE' ');
828 Maze[y][x] = SPACE' ';
829 break;
830 }
831 }
832 }
833}
834
835/*
836 * chkslime:
837 * handle slime shot exploding
838 */
839static void
840chkslime(BULLET *bp, BULLET *next)
841{
842 BULLET *nbp;
843
844 switch (Maze[bp->b_y][bp->b_x]) {
845 /* Slime explodes on walls and doors: */
846 case WALL1'-':
847 case WALL2'|':
848 case WALL3'+':
849 case WALL4'/':
850 case WALL5'\\':
851 case DOOR'#':
852 switch (bp->b_face) {
853 case LEFTS'{':
854 bp->b_x++;
855 break;
856 case RIGHT'}':
857 bp->b_x--;
858 break;
859 case ABOVE'i':
860 bp->b_y++;
861 break;
862 case BELOW'!':
863 bp->b_y--;
864 break;
865 }
866 break;
867 }
868
869 /* Duplicate the unit of slime: */
870 nbp = malloc(sizeof (BULLET));
871 if (nbp == NULL((void *)0)) {
872 logit(LOG_ERR3, "malloc");
873 return;
874 }
875 *nbp = *bp;
876
877 /* Move it around: */
878 move_slime(nbp, nbp->b_type == SLIME'$' ? conf_slimespeed :
879 conf_lavaspeed, next);
880}
881
882/*
883 * move_slime:
884 * move the given slime shot speed times and add it back if
885 * it hasn't fizzled yet
886 */
887static void
888move_slime(BULLET *bp, int speed, BULLET *next)
889{
890 int i, j, dirmask, count;
891 PLAYER *pp;
892 BULLET *nbp;
893
894 if (speed == 0) {
895 if (bp->b_charge <= 0)
896 free(bp);
897 else
898 save_bullet(bp);
899 return;
900 }
901
902 /* Draw it: */
903 showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA'~' ? LAVA'~' : '*');
904
905 switch (Maze[bp->b_y][bp->b_x]) {
906 /* Someone got hit by slime or lava: */
907 case LEFTS'{':
908 case RIGHT'}':
909 case ABOVE'i':
910 case BELOW'!':
911 case FLYER'&':
912 pp = play_at(bp->b_y, bp->b_x);
913 message(pp, "You've been slimed.");
914 checkdam(pp, bp->b_owner, bp->b_score, conf_mindam, bp->b_type);
915 break;
916 /* Bullets detonate in slime and lava: */
917 case SHOT':':
918 case GRENADE'o':
919 case SATCHEL'O':
920 case BOMB'@':
921 case DSHOT'?':
922 explshot(next, bp->b_y, bp->b_x);
923 explshot(Bullets, bp->b_y, bp->b_x);
924 break;
925 }
926
927
928 /* Drain the slime/lava of some energy: */
929 if (--bp->b_charge <= 0) {
930 /* It fizzled: */
931 free(bp);
932 return;
933 }
934
935 /* Figure out which way the slime should flow: */
936 dirmask = 0;
937 count = 0;
938 switch (bp->b_face) {
939 case LEFTS'{':
940 if (!iswall(bp->b_y, bp->b_x - 1))
941 dirmask |= WEST020, count++;
942 if (!iswall(bp->b_y - 1, bp->b_x))
943 dirmask |= NORTH01, count++;
944 if (!iswall(bp->b_y + 1, bp->b_x))
945 dirmask |= SOUTH02, count++;
946 if (dirmask == 0)
947 if (!iswall(bp->b_y, bp->b_x + 1))
948 dirmask |= EAST010, count++;
949 break;
950 case RIGHT'}':
951 if (!iswall(bp->b_y, bp->b_x + 1))
952 dirmask |= EAST010, count++;
953 if (!iswall(bp->b_y - 1, bp->b_x))
954 dirmask |= NORTH01, count++;
955 if (!iswall(bp->b_y + 1, bp->b_x))
956 dirmask |= SOUTH02, count++;
957 if (dirmask == 0)
958 if (!iswall(bp->b_y, bp->b_x - 1))
959 dirmask |= WEST020, count++;
960 break;
961 case ABOVE'i':
962 if (!iswall(bp->b_y - 1, bp->b_x))
963 dirmask |= NORTH01, count++;
964 if (!iswall(bp->b_y, bp->b_x - 1))
965 dirmask |= WEST020, count++;
966 if (!iswall(bp->b_y, bp->b_x + 1))
967 dirmask |= EAST010, count++;
968 if (dirmask == 0)
969 if (!iswall(bp->b_y + 1, bp->b_x))
970 dirmask |= SOUTH02, count++;
971 break;
972 case BELOW'!':
973 if (!iswall(bp->b_y + 1, bp->b_x))
974 dirmask |= SOUTH02, count++;
975 if (!iswall(bp->b_y, bp->b_x - 1))
976 dirmask |= WEST020, count++;
977 if (!iswall(bp->b_y, bp->b_x + 1))
978 dirmask |= EAST010, count++;
979 if (dirmask == 0)
980 if (!iswall(bp->b_y - 1, bp->b_x))
981 dirmask |= NORTH01, count++;
982 break;
983 }
984 if (count == 0) {
985 /*
986 * No place to go. Just sit here for a while and wait
987 * for adjacent squares to clear out.
988 */
989 save_bullet(bp);
990 return;
991 }
992 if (bp->b_charge < count) {
993 /* Only bp->b_charge paths may be taken */
994 while (count > bp->b_charge) {
995 if (dirmask & WEST020)
996 dirmask &= ~WEST020;
997 else if (dirmask & EAST010)
998 dirmask &= ~EAST010;
999 else if (dirmask & NORTH01)
1000 dirmask &= ~NORTH01;
1001 else if (dirmask & SOUTH02)
1002 dirmask &= ~SOUTH02;
1003 count--;
1004 }
1005 }
1006
1007 /* Spawn little slimes off in every possible direction: */
1008 i = bp->b_charge / count;
1009 j = bp->b_charge % count;
1010 if (dirmask & WEST020) {
1011 count--;
1012 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS'{',
1013 i, bp->b_size, bp->b_owner, bp->b_score, TRUE1, SPACE' ');
1014 move_slime(nbp, speed - 1, next);
1015 }
1016 if (dirmask & EAST010) {
1017 count--;
1018 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT'}',
1019 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
1020 bp->b_score, TRUE1, SPACE' ');
1021 move_slime(nbp, speed - 1, next);
1022 }
1023 if (dirmask & NORTH01) {
1024 count--;
1025 nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE'i',
1026 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
1027 bp->b_score, TRUE1, SPACE' ');
1028 move_slime(nbp, speed - 1, next);
1029 }
1030 if (dirmask & SOUTH02) {
1031 count--;
1032 nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW'!',
1033 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
1034 bp->b_score, TRUE1, SPACE' ');
1035 move_slime(nbp, speed - 1, next);
1036 }
1037
1038 free(bp);
1039}
1040
1041/*
1042 * iswall:
1043 * returns whether the given location is a wall
1044 */
1045static int
1046iswall(int y, int x)
1047{
1048 if (y < 0 || x < 0 || y >= HEIGHT23 || x >= WIDTH51)
1049 return TRUE1;
1050 switch (Maze[y][x]) {
1051 case WALL1'-':
1052 case WALL2'|':
1053 case WALL3'+':
1054 case WALL4'/':
1055 case WALL5'\\':
1056 case DOOR'#':
1057 case SLIME'$':
1058 case LAVA'~':
1059 return TRUE1;
1060 }
1061 return FALSE0;
1062}
1063
1064/*
1065 * zapshot:
1066 * Take a shot out of the air.
1067 */
1068static void
1069zapshot(BULLET *blist, BULLET *obp)
1070{
1071 BULLET *bp;
1072
1073 for (bp = blist; bp != NULL((void *)0); bp = bp->b_next) {
1074 /* Find co-located bullets not facing the same way: */
1075 if (bp->b_face != obp->b_face
1076 && bp->b_x == obp->b_x && bp->b_y == obp->b_y)
1077 {
1078 /* Bullet collision: */
1079 explshot(blist, obp->b_y, obp->b_x);
1080 return;
1081 }
1082 }
1083}
1084
1085/*
1086 * explshot -
1087 * Make all shots at this location blow up
1088 */
1089static void
1090explshot(BULLET *blist, int y, int x)
1091{
1092 BULLET *bp;
1093
1094 for (bp = blist; bp != NULL((void *)0); bp = bp->b_next)
1095 if (bp->b_x == x && bp->b_y == y) {
1096 bp->b_expl = TRUE1;
1097 if (bp->b_owner != NULL((void *)0))
1098 message(bp->b_owner, "Shot intercepted.");
1099 }
1100}
1101
1102/*
1103 * play_at:
1104 * Return a pointer to the player at the given location
1105 */
1106PLAYER *
1107play_at(int y, int x)
1108{
1109 PLAYER *pp;
1110
1111 for (pp = Player; pp < End_player; pp++)
1112 if (pp->p_x == x && pp->p_y == y)
1113 return pp;
1114
1115 /* Internal fault: */
1116 logx(LOG_ERR3, "play_at: not a player");
1117 abort();
1118}
1119
1120/*
1121 * opposite:
1122 * Return TRUE if the bullet direction faces the opposite direction
1123 * of the player in the maze
1124 */
1125int
1126opposite(int face, char dir)
1127{
1128 switch (face) {
13
Control jumps to the 'default' case at line 1137
1129 case LEFTS'{':
1130 return (dir == RIGHT'}');
1131 case RIGHT'}':
1132 return (dir == LEFTS'{');
1133 case ABOVE'i':
1134 return (dir == BELOW'!');
1135 case BELOW'!':
1136 return (dir == ABOVE'i');
1137 default:
1138 return FALSE0;
14
Returning zero, which participates in a condition later
1139 }
1140}
1141
1142/*
1143 * is_bullet:
1144 * Is there a bullet at the given coordinates? If so, return
1145 * a pointer to the bullet, otherwise return NULL
1146 */
1147BULLET *
1148is_bullet(int y, int x)
1149{
1150 BULLET *bp;
1151
1152 for (bp = Bullets; bp != NULL((void *)0); bp = bp->b_next)
1153 if (bp->b_y == y && bp->b_x == x)
1154 return bp;
1155 return NULL((void *)0);
1156}
1157
1158/*
1159 * fixshots:
1160 * change the underlying character of the shots at a location
1161 * to the given character.
1162 */
1163void
1164fixshots(int y, int x, char over)
1165{
1166 BULLET *bp;
1167
1168 for (bp = Bullets; bp != NULL((void *)0); bp = bp->b_next)
1169 if (bp->b_y == y && bp->b_x == x)
1170 bp->b_over = over;
1171}
1172
1173/*
1174 * find_under:
1175 * find the underlying character for a bullet when it lands
1176 * on another bullet.
1177 */
1178static void
1179find_under(BULLET *blist, BULLET *bp)
1180{
1181 BULLET *nbp;
1182
1183 for (nbp = blist; nbp != NULL((void *)0); nbp = nbp->b_next)
1184 if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) {
1185 bp->b_over = nbp->b_over;
1186 break;
1187 }
1188}
1189
1190/*
1191 * mark_player:
1192 * mark a player as under a shot
1193 */
1194static void
1195mark_player(BULLET *bp)
1196{
1197 PLAYER *pp;
1198
1199 for (pp = Player; pp < End_player; pp++)
1200 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
1201 pp->p_undershot = TRUE1;
1202 break;
1203 }
1204}
1205
1206/*
1207 * mark_boot:
1208 * mark a boot as under a shot
1209 */
1210static void
1211mark_boot(BULLET *bp)
1212{
1213 PLAYER *pp;
1214
1215 for (pp = Boot; pp < &Boot[NBOOTS2]; pp++)
1216 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
1217 pp->p_undershot = TRUE1;
1218 break;
1219 }
1220}