1 /* $NetBSD: shots.c,v 1.16 2021/05/02 12:50:45 rillig Exp $ */ 2 /* 3 * Copyright (c) 1983-2003, Regents of the University of California. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * + Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * + Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * + Neither the name of the University of California, San Francisco nor 16 * the names of its contributors may be used to endorse or promote 17 * products derived from this software without specific prior written 18 * permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 #ifndef lint 35 __RCSID("$NetBSD: shots.c,v 1.16 2021/05/02 12:50:45 rillig Exp $"); 36 #endif /* not lint */ 37 38 #include <err.h> 39 #include <signal.h> 40 #include <stdlib.h> 41 #include "hunt.h" 42 43 #define PLUS_DELTA(x, max) if (x < max) x++; else x-- 44 #define MINUS_DELTA(x, min) if (x > min) x--; else x++ 45 46 static void chkshot(BULLET *, BULLET *); 47 static void chkslime(BULLET *, BULLET *); 48 static void explshot(BULLET *, int, int); 49 static void find_under(BULLET *, BULLET *); 50 static bool iswall(int, int); 51 static void mark_boot(BULLET *); 52 static void mark_player(BULLET *); 53 #ifdef DRONE 54 static void move_drone(BULLET *); 55 #endif 56 static void move_flyer(PLAYER *); 57 static int move_normal_shot(BULLET *); 58 static void move_slime(BULLET *, int, BULLET *); 59 static void save_bullet(BULLET *); 60 static void zapshot(BULLET *, BULLET *); 61 62 /* 63 * moveshots: 64 * Move the shots already in the air, taking explosions into account 65 */ 66 void 67 moveshots(void) 68 { 69 BULLET *bp, *next; 70 PLAYER *pp; 71 int x, y; 72 BULLET *blist; 73 74 rollexpl(); 75 if (Bullets == NULL) 76 goto ret; 77 78 /* 79 * First we move through the bullet list BULSPD times, looking 80 * for things we may have run into. If we do run into 81 * something, we set up the explosion and disappear, checking 82 * for damage to any player who got in the way. 83 */ 84 85 blist = Bullets; 86 Bullets = NULL; 87 for (bp = blist; bp != NULL; bp = next) { 88 next = bp->b_next; 89 x = bp->b_x; 90 y = bp->b_y; 91 Maze[y][x] = bp->b_over; 92 for (pp = Player; pp < End_player; pp++) 93 check(pp, y, x); 94 #ifdef MONITOR 95 for (pp = Monitor; pp < End_monitor; pp++) 96 check(pp, y, x); 97 #endif 98 99 switch (bp->b_type) { 100 case SHOT: 101 case GRENADE: 102 case SATCHEL: 103 case BOMB: 104 if (move_normal_shot(bp)) { 105 bp->b_next = Bullets; 106 Bullets = bp; 107 } 108 break; 109 #ifdef OOZE 110 case SLIME: 111 if (bp->b_expl || move_normal_shot(bp)) { 112 bp->b_next = Bullets; 113 Bullets = bp; 114 } 115 break; 116 #endif 117 #ifdef DRONE 118 case DSHOT: 119 if (move_drone(bp)) { 120 bp->b_next = Bullets; 121 Bullets = bp; 122 } 123 break; 124 #endif 125 default: 126 bp->b_next = Bullets; 127 Bullets = bp; 128 break; 129 } 130 } 131 132 blist = Bullets; 133 Bullets = NULL; 134 for (bp = blist; bp != NULL; bp = next) { 135 next = bp->b_next; 136 if (!bp->b_expl) { 137 save_bullet(bp); 138 #ifdef MONITOR 139 for (pp = Monitor; pp < End_monitor; pp++) 140 check(pp, bp->b_y, bp->b_x); 141 #endif 142 #ifdef DRONE 143 if (bp->b_type == DSHOT) 144 for (pp = Player; pp < End_player; pp++) 145 if (pp->p_scan >= 0) 146 check(pp, bp->b_y, bp->b_x); 147 #endif 148 continue; 149 } 150 151 chkshot(bp, next); 152 free(bp); 153 } 154 155 for (pp = Player; pp < End_player; pp++) 156 Maze[pp->p_y][pp->p_x] = pp->p_face; 157 158 ret: 159 #ifdef BOOTS 160 for (pp = Boot; pp < &Boot[NBOOTS]; pp++) 161 if (pp->p_flying >= 0) 162 move_flyer(pp); 163 #endif 164 for (pp = Player; pp < End_player; pp++) { 165 #ifdef FLY 166 if (pp->p_flying >= 0) 167 move_flyer(pp); 168 #endif 169 sendcom(pp, REFRESH); /* Flush out the explosions */ 170 look(pp); 171 sendcom(pp, REFRESH); 172 } 173 #ifdef MONITOR 174 for (pp = Monitor; pp < End_monitor; pp++) 175 sendcom(pp, REFRESH); 176 #endif 177 178 return; 179 } 180 181 /* 182 * move_normal_shot: 183 * Move a normal shot along its trajectory 184 */ 185 static int 186 move_normal_shot(BULLET *bp) 187 { 188 int i, x, y; 189 PLAYER *pp; 190 191 for (i = 0; i < BULSPD; i++) { 192 if (bp->b_expl) 193 break; 194 195 x = bp->b_x; 196 y = bp->b_y; 197 198 switch (bp->b_face) { 199 case LEFTS: 200 x--; 201 break; 202 case RIGHT: 203 x++; 204 break; 205 case ABOVE: 206 y--; 207 break; 208 case BELOW: 209 y++; 210 break; 211 } 212 213 switch (Maze[y][x]) { 214 case SHOT: 215 if (rand_num(100) < 5) { 216 zapshot(Bullets, bp); 217 zapshot(bp->b_next, bp); 218 } 219 break; 220 case GRENADE: 221 if (rand_num(100) < 10) { 222 zapshot(Bullets, bp); 223 zapshot(bp->b_next, bp); 224 } 225 break; 226 #ifdef REFLECT 227 case WALL4: /* reflecting walls */ 228 switch (bp->b_face) { 229 case LEFTS: 230 bp->b_face = BELOW; 231 break; 232 case RIGHT: 233 bp->b_face = ABOVE; 234 break; 235 case ABOVE: 236 bp->b_face = RIGHT; 237 break; 238 case BELOW: 239 bp->b_face = LEFTS; 240 break; 241 } 242 Maze[y][x] = WALL5; 243 #ifdef MONITOR 244 for (pp = Monitor; pp < End_monitor; pp++) 245 check(pp, y, x); 246 #endif 247 break; 248 case WALL5: 249 switch (bp->b_face) { 250 case LEFTS: 251 bp->b_face = ABOVE; 252 break; 253 case RIGHT: 254 bp->b_face = BELOW; 255 break; 256 case ABOVE: 257 bp->b_face = LEFTS; 258 break; 259 case BELOW: 260 bp->b_face = RIGHT; 261 break; 262 } 263 Maze[y][x] = WALL4; 264 #ifdef MONITOR 265 for (pp = Monitor; pp < End_monitor; pp++) 266 check(pp, y, x); 267 #endif 268 break; 269 #endif 270 #ifdef RANDOM 271 case DOOR: 272 switch (rand_num(4)) { 273 case 0: 274 bp->b_face = ABOVE; 275 break; 276 case 1: 277 bp->b_face = BELOW; 278 break; 279 case 2: 280 bp->b_face = LEFTS; 281 break; 282 case 3: 283 bp->b_face = RIGHT; 284 break; 285 } 286 break; 287 #endif 288 #ifdef FLY 289 case FLYER: 290 pp = play_at(y, x); 291 message(pp, "Zing!"); 292 break; 293 #endif 294 case LEFTS: 295 case RIGHT: 296 case BELOW: 297 case ABOVE: 298 /* 299 * give the person a chance to catch a 300 * grenade if s/he is facing it 301 */ 302 pp = play_at(y, x); 303 pp->p_ident->i_shot += bp->b_charge; 304 if (opposite(bp->b_face, Maze[y][x])) { 305 if (rand_num(100) < 10) { 306 if (bp->b_owner != NULL) 307 message(bp->b_owner, 308 "Your charge was absorbed!"); 309 if (bp->b_score != NULL) 310 bp->b_score->i_robbed += bp->b_charge; 311 pp->p_ammo += bp->b_charge; 312 if (pp->p_damage + bp->b_size * MINDAM 313 > pp->p_damcap) 314 pp->p_ident->i_saved++; 315 message(pp, "Absorbed charge (good shield!)"); 316 pp->p_ident->i_absorbed += bp->b_charge; 317 free(bp); 318 (void) snprintf(Buf, sizeof(Buf), 319 "%3d", pp->p_ammo); 320 cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL); 321 outstr(pp, Buf, 3); 322 return false; 323 } 324 pp->p_ident->i_faced += bp->b_charge; 325 } 326 /* 327 * Small chance that the bullet just misses the 328 * person. If so, the bullet just goes on its 329 * merry way without exploding. 330 */ 331 if (rand_num(100) < 5) { 332 pp->p_ident->i_ducked += bp->b_charge; 333 if (pp->p_damage + bp->b_size * MINDAM 334 > pp->p_damcap) 335 pp->p_ident->i_saved++; 336 if (bp->b_score != NULL) 337 bp->b_score->i_missed += bp->b_charge; 338 message(pp, "Zing!"); 339 if (bp->b_owner == NULL) 340 break; 341 message(bp->b_owner, bp->b_score && 342 ((bp->b_score->i_missed & 0x7) == 0x7) ? 343 "My! What a bad shot you are!" : 344 "Missed him"); 345 break; 346 } 347 /* 348 * The shot hit that sucker! Blow it up. 349 */ 350 #ifndef RANDOM 351 /* FALLTHROUGH */ 352 case DOOR: 353 #endif 354 /* FALLTHROUGH */ 355 case WALL1: 356 case WALL2: 357 case WALL3: 358 bp->b_expl = true; 359 break; 360 } 361 362 bp->b_x = x; 363 bp->b_y = y; 364 } 365 return true; 366 } 367 368 #ifdef DRONE 369 /* 370 * move_drone: 371 * Move the drone to the next square 372 */ 373 static void 374 move_drone(BULLET *bp) 375 { 376 int mask, count; 377 int n, dir; 378 PLAYER *pp; 379 380 /* 381 * See if we can give someone a blast 382 */ 383 if (isplayer(Maze[bp->b_y][bp->b_x - 1])) { 384 dir = WEST; 385 goto drone_move; 386 } 387 if (isplayer(Maze[bp->b_y - 1][bp->b_x])) { 388 dir = NORTH; 389 goto drone_move; 390 } 391 if (isplayer(Maze[bp->b_y + 1][bp->b_x])) { 392 dir = SOUTH; 393 goto drone_move; 394 } 395 if (isplayer(Maze[bp->b_y][bp->b_x + 1])) { 396 dir = EAST; 397 goto drone_move; 398 } 399 400 /* 401 * Find out what directions are clear 402 */ 403 mask = count = 0; 404 if (!iswall(bp->b_y, bp->b_x - 1)) 405 mask |= WEST, count++; 406 if (!iswall(bp->b_y - 1, bp->b_x)) 407 mask |= NORTH, count++; 408 if (!iswall(bp->b_y + 1, bp->b_x)) 409 mask |= SOUTH, count++; 410 if (!iswall(bp->b_y, bp->b_x + 1)) 411 mask |= EAST, count++; 412 413 /* 414 * All blocked up, just you wait 415 */ 416 if (count == 0) 417 return true; 418 419 /* 420 * Only one way to go. 421 */ 422 if (count == 1) { 423 dir = mask; 424 goto drone_move; 425 } 426 427 /* 428 * Get rid of the direction that we came from 429 */ 430 switch (bp->b_face) { 431 case LEFTS: 432 if (mask & EAST) 433 mask &= ~EAST, count--; 434 break; 435 case RIGHT: 436 if (mask & WEST) 437 mask &= ~WEST, count--; 438 break; 439 case ABOVE: 440 if (mask & SOUTH) 441 mask &= ~SOUTH, count--; 442 break; 443 case BELOW: 444 if (mask & NORTH) 445 mask &= ~NORTH, count--; 446 break; 447 } 448 449 /* 450 * Pick one of the remaining directions 451 */ 452 n = rand_num(count); 453 if (n >= 0 && mask & NORTH) 454 dir = NORTH, n--; 455 if (n >= 0 && mask & SOUTH) 456 dir = SOUTH, n--; 457 if (n >= 0 && mask & EAST) 458 dir = EAST, n--; 459 if (n >= 0 && mask & WEST) 460 dir = WEST, n--; 461 462 /* 463 * Now that we know the direction of movement, 464 * just update the position of the drone 465 */ 466 drone_move: 467 switch (dir) { 468 case WEST: 469 bp->b_x--; 470 bp->b_face = LEFTS; 471 break; 472 case EAST: 473 bp->b_x++; 474 bp->b_face = RIGHT; 475 break; 476 case NORTH: 477 bp->b_y--; 478 bp->b_face = ABOVE; 479 break; 480 case SOUTH: 481 bp->b_y++; 482 bp->b_face = BELOW; 483 break; 484 } 485 switch (Maze[bp->b_y][bp->b_x]) { 486 case LEFTS: 487 case RIGHT: 488 case BELOW: 489 case ABOVE: 490 /* 491 * give the person a chance to catch a 492 * drone if s/he is facing it 493 */ 494 if (rand_num(100) < 1 && 495 opposite(bp->b_face, Maze[bp->b_y][bp->b_x])) { 496 pp = play_at(bp->b_y, bp->b_x); 497 pp->p_ammo += bp->b_charge; 498 message(pp, "**** Absorbed drone ****"); 499 free(bp); 500 (void) snprintf(Buf, sizeof(buf), "%3d", pp->p_ammo); 501 cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL); 502 outstr(pp, Buf, 3); 503 return false; 504 } 505 bp->b_expl = true; 506 break; 507 } 508 return true; 509 } 510 #endif 511 512 /* 513 * save_bullet: 514 * Put this bullet back onto the bullet list 515 */ 516 static void 517 save_bullet(BULLET *bp) 518 { 519 bp->b_over = Maze[bp->b_y][bp->b_x]; 520 switch (bp->b_over) { 521 case SHOT: 522 case GRENADE: 523 case SATCHEL: 524 case BOMB: 525 #ifdef OOZE 526 case SLIME: 527 #ifdef VOLCANO 528 case LAVA: 529 #endif 530 #endif 531 #ifdef DRONE 532 case DSHOT: 533 #endif 534 find_under(Bullets, bp); 535 break; 536 } 537 538 switch (bp->b_over) { 539 case LEFTS: 540 case RIGHT: 541 case ABOVE: 542 case BELOW: 543 #ifdef FLY 544 case FLYER: 545 #endif 546 mark_player(bp); 547 break; 548 #ifdef BOOTS 549 case BOOT: 550 case BOOT_PAIR: 551 mark_boot(bp); 552 break; 553 #endif 554 555 default: 556 Maze[bp->b_y][bp->b_x] = bp->b_type; 557 break; 558 } 559 560 bp->b_next = Bullets; 561 Bullets = bp; 562 } 563 564 /* 565 * move_flyer: 566 * Update the position of a player in flight 567 */ 568 static void 569 move_flyer(PLAYER *pp) 570 { 571 int x, y; 572 573 if (pp->p_undershot) { 574 fixshots(pp->p_y, pp->p_x, pp->p_over); 575 pp->p_undershot = false; 576 } 577 Maze[pp->p_y][pp->p_x] = pp->p_over; 578 x = pp->p_x + pp->p_flyx; 579 y = pp->p_y + pp->p_flyy; 580 if (x < 1) { 581 x = 1 - x; 582 pp->p_flyx = -pp->p_flyx; 583 } 584 else if (x > WIDTH - 2) { 585 x = (WIDTH - 2) - (x - (WIDTH - 2)); 586 pp->p_flyx = -pp->p_flyx; 587 } 588 if (y < 1) { 589 y = 1 - y; 590 pp->p_flyy = -pp->p_flyy; 591 } 592 else if (y > HEIGHT - 2) { 593 y = (HEIGHT - 2) - (y - (HEIGHT - 2)); 594 pp->p_flyy = -pp->p_flyy; 595 } 596 again: 597 switch (Maze[y][x]) { 598 default: 599 switch (rand_num(4)) { 600 case 0: 601 PLUS_DELTA(x, WIDTH - 2); 602 break; 603 case 1: 604 MINUS_DELTA(x, 1); 605 break; 606 case 2: 607 PLUS_DELTA(y, HEIGHT - 2); 608 break; 609 case 3: 610 MINUS_DELTA(y, 1); 611 break; 612 } 613 goto again; 614 case WALL1: 615 case WALL2: 616 case WALL3: 617 #ifdef REFLECT 618 case WALL4: 619 case WALL5: 620 #endif 621 #ifdef RANDOM 622 case DOOR: 623 #endif 624 if (pp->p_flying == 0) 625 pp->p_flying++; 626 break; 627 case SPACE: 628 break; 629 } 630 pp->p_y = y; 631 pp->p_x = x; 632 if (pp->p_flying-- == 0) { 633 #ifdef BOOTS 634 if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) { 635 #endif 636 checkdam(pp, NULL, NULL, 637 rand_num(pp->p_damage / 5), FALL); 638 pp->p_face = rand_dir(); 639 showstat(pp); 640 #ifdef BOOTS 641 } 642 else { 643 if (Maze[y][x] == BOOT) 644 pp->p_face = BOOT_PAIR; 645 Maze[y][x] = SPACE; 646 } 647 #endif 648 } 649 pp->p_over = Maze[y][x]; 650 Maze[y][x] = pp->p_face; 651 showexpl(y, x, pp->p_face); 652 } 653 654 /* 655 * chkshot 656 * Handle explosions 657 */ 658 static void 659 chkshot(BULLET *bp, BULLET *next) 660 { 661 int y, x; 662 int dy, dx, absdy; 663 int delta, damage; 664 char expl; 665 PLAYER *pp; 666 667 delta = 0; 668 switch (bp->b_type) { 669 case SHOT: 670 case MINE: 671 case GRENADE: 672 case GMINE: 673 case SATCHEL: 674 case BOMB: 675 delta = bp->b_size - 1; 676 break; 677 #ifdef OOZE 678 case SLIME: 679 #ifdef VOLCANO 680 case LAVA: 681 #endif 682 chkslime(bp, next); 683 return; 684 #endif 685 #ifdef DRONE 686 case DSHOT: 687 bp->b_type = SLIME; 688 chkslime(bp, next); 689 return; 690 #endif 691 } 692 for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) { 693 if (y < 0 || y >= HEIGHT) 694 continue; 695 dy = y - bp->b_y; 696 absdy = (dy < 0) ? -dy : dy; 697 for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) { 698 if (x < 0 || x >= WIDTH) 699 continue; 700 dx = x - bp->b_x; 701 if (dx == 0) 702 expl = (dy == 0) ? '*' : '|'; 703 else if (dy == 0) 704 expl = '-'; 705 else if (dx == dy) 706 expl = '\\'; 707 else if (dx == -dy) 708 expl = '/'; 709 else 710 expl = '*'; 711 showexpl(y, x, expl); 712 switch (Maze[y][x]) { 713 case LEFTS: 714 case RIGHT: 715 case ABOVE: 716 case BELOW: 717 #ifdef FLY 718 case FLYER: 719 #endif 720 if (dx < 0) 721 dx = -dx; 722 if (absdy > dx) 723 damage = bp->b_size - absdy; 724 else 725 damage = bp->b_size - dx; 726 pp = play_at(y, x); 727 checkdam(pp, bp->b_owner, bp->b_score, 728 damage * MINDAM, bp->b_type); 729 break; 730 case GMINE: 731 case MINE: 732 add_shot((Maze[y][x] == GMINE) ? 733 GRENADE : SHOT, 734 y, x, LEFTS, 735 (Maze[y][x] == GMINE) ? 736 GRENREQ : BULREQ, 737 NULL, true, SPACE); 738 Maze[y][x] = SPACE; 739 break; 740 } 741 } 742 } 743 } 744 745 #ifdef OOZE 746 /* 747 * chkslime: 748 * handle slime shot exploding 749 */ 750 static void 751 chkslime(BULLET *bp, BULLET *next) 752 { 753 BULLET *nbp; 754 755 switch (Maze[bp->b_y][bp->b_x]) { 756 case WALL1: 757 case WALL2: 758 case WALL3: 759 #ifdef REFLECT 760 case WALL4: 761 case WALL5: 762 #endif 763 #ifdef RANDOM 764 case DOOR: 765 #endif 766 switch (bp->b_face) { 767 case LEFTS: 768 bp->b_x++; 769 break; 770 case RIGHT: 771 bp->b_x--; 772 break; 773 case ABOVE: 774 bp->b_y++; 775 break; 776 case BELOW: 777 bp->b_y--; 778 break; 779 } 780 break; 781 } 782 nbp = malloc(sizeof(*nbp)); 783 *nbp = *bp; 784 #ifdef VOLCANO 785 move_slime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED, next); 786 #else 787 move_slime(nbp, SLIMESPEED, next); 788 #endif 789 } 790 791 /* 792 * move_slime: 793 * move the given slime shot speed times and add it back if 794 * it hasn't fizzled yet 795 */ 796 static void 797 move_slime(BULLET *bp, int speed, BULLET *next) 798 { 799 int i, j, dirmask, count; 800 PLAYER *pp; 801 BULLET *nbp; 802 803 if (speed == 0) { 804 if (bp->b_charge <= 0) 805 free(bp); 806 else 807 save_bullet(bp); 808 return; 809 } 810 811 #ifdef VOLCANO 812 showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*'); 813 #else 814 showexpl(bp->b_y, bp->b_x, '*'); 815 #endif 816 switch (Maze[bp->b_y][bp->b_x]) { 817 case LEFTS: 818 case RIGHT: 819 case ABOVE: 820 case BELOW: 821 #ifdef FLY 822 case FLYER: 823 #endif 824 pp = play_at(bp->b_y, bp->b_x); 825 message(pp, "You've been slimed."); 826 checkdam(pp, bp->b_owner, bp->b_score, MINDAM, bp->b_type); 827 break; 828 case SHOT: 829 case GRENADE: 830 case SATCHEL: 831 case BOMB: 832 #ifdef DRONE 833 case DSHOT: 834 #endif 835 explshot(next, bp->b_y, bp->b_x); 836 explshot(Bullets, bp->b_y, bp->b_x); 837 break; 838 } 839 840 if (--bp->b_charge <= 0) { 841 free(bp); 842 return; 843 } 844 845 dirmask = 0; 846 count = 0; 847 switch (bp->b_face) { 848 case LEFTS: 849 if (!iswall(bp->b_y, bp->b_x - 1)) 850 dirmask |= WEST, count++; 851 if (!iswall(bp->b_y - 1, bp->b_x)) 852 dirmask |= NORTH, count++; 853 if (!iswall(bp->b_y + 1, bp->b_x)) 854 dirmask |= SOUTH, count++; 855 if (dirmask == 0) 856 if (!iswall(bp->b_y, bp->b_x + 1)) 857 dirmask |= EAST, count++; 858 break; 859 case RIGHT: 860 if (!iswall(bp->b_y, bp->b_x + 1)) 861 dirmask |= EAST, count++; 862 if (!iswall(bp->b_y - 1, bp->b_x)) 863 dirmask |= NORTH, count++; 864 if (!iswall(bp->b_y + 1, bp->b_x)) 865 dirmask |= SOUTH, count++; 866 if (dirmask == 0) 867 if (!iswall(bp->b_y, bp->b_x - 1)) 868 dirmask |= WEST, count++; 869 break; 870 case ABOVE: 871 if (!iswall(bp->b_y - 1, bp->b_x)) 872 dirmask |= NORTH, count++; 873 if (!iswall(bp->b_y, bp->b_x - 1)) 874 dirmask |= WEST, count++; 875 if (!iswall(bp->b_y, bp->b_x + 1)) 876 dirmask |= EAST, count++; 877 if (dirmask == 0) 878 if (!iswall(bp->b_y + 1, bp->b_x)) 879 dirmask |= SOUTH, count++; 880 break; 881 case BELOW: 882 if (!iswall(bp->b_y + 1, bp->b_x)) 883 dirmask |= SOUTH, count++; 884 if (!iswall(bp->b_y, bp->b_x - 1)) 885 dirmask |= WEST, count++; 886 if (!iswall(bp->b_y, bp->b_x + 1)) 887 dirmask |= EAST, count++; 888 if (dirmask == 0) 889 if (!iswall(bp->b_y - 1, bp->b_x)) 890 dirmask |= NORTH, count++; 891 break; 892 } 893 if (count == 0) { 894 /* 895 * No place to go. Just sit here for a while and wait 896 * for adjacent squares to clear out. 897 */ 898 save_bullet(bp); 899 return; 900 } 901 if (bp->b_charge < count) { 902 /* Only bp->b_charge paths may be taken */ 903 while (count > bp->b_charge) { 904 if (dirmask & WEST) 905 dirmask &= ~WEST; 906 else if (dirmask & EAST) 907 dirmask &= ~EAST; 908 else if (dirmask & NORTH) 909 dirmask &= ~NORTH; 910 else if (dirmask & SOUTH) 911 dirmask &= ~SOUTH; 912 count--; 913 } 914 } 915 916 i = bp->b_charge / count; 917 j = bp->b_charge % count; 918 if (dirmask & WEST) { 919 count--; 920 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS, 921 i, bp->b_size, bp->b_owner, bp->b_score, true, SPACE); 922 move_slime(nbp, speed - 1, next); 923 } 924 if (dirmask & EAST) { 925 count--; 926 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT, 927 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, 928 bp->b_score, true, SPACE); 929 move_slime(nbp, speed - 1, next); 930 } 931 if (dirmask & NORTH) { 932 count--; 933 nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE, 934 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, 935 bp->b_score, true, SPACE); 936 move_slime(nbp, speed - 1, next); 937 } 938 if (dirmask & SOUTH) { 939 count--; 940 nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW, 941 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, 942 bp->b_score, true, SPACE); 943 move_slime(nbp, speed - 1, next); 944 } 945 946 free(bp); 947 } 948 949 /* 950 * iswall: 951 * returns whether the given location is a wall 952 */ 953 static bool 954 iswall(int y, int x) 955 { 956 if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH) 957 return true; 958 switch (Maze[y][x]) { 959 case WALL1: 960 case WALL2: 961 case WALL3: 962 #ifdef REFLECT 963 case WALL4: 964 case WALL5: 965 #endif 966 #ifdef RANDOM 967 case DOOR: 968 #endif 969 #ifdef OOZE 970 case SLIME: 971 #ifdef VOLCANO 972 case LAVA: 973 #endif 974 #endif 975 return true; 976 } 977 return false; 978 } 979 #endif 980 981 /* 982 * zapshot: 983 * Take a shot out of the air. 984 */ 985 static void 986 zapshot(BULLET *blist, BULLET *obp) 987 { 988 BULLET *bp; 989 bool explode; 990 991 explode = false; 992 for (bp = blist; bp != NULL; bp = bp->b_next) { 993 if (bp->b_x != obp->b_x || bp->b_y != obp->b_y) 994 continue; 995 if (bp->b_face == obp->b_face) 996 continue; 997 explode = true; 998 break; 999 } 1000 if (!explode) 1001 return; 1002 explshot(blist, obp->b_y, obp->b_x); 1003 } 1004 1005 /* 1006 * explshot - 1007 * Make all shots at this location blow up 1008 */ 1009 static void 1010 explshot(BULLET *blist, int y, int x) 1011 { 1012 BULLET *bp; 1013 1014 for (bp = blist; bp != NULL; bp = bp->b_next) 1015 if (bp->b_x == x && bp->b_y == y) { 1016 bp->b_expl = true; 1017 if (bp->b_owner != NULL) 1018 message(bp->b_owner, "Shot intercepted"); 1019 } 1020 } 1021 1022 /* 1023 * play_at: 1024 * Return a pointer to the player at the given location 1025 */ 1026 PLAYER * 1027 play_at(int y, int x) 1028 { 1029 PLAYER *pp; 1030 1031 for (pp = Player; pp < End_player; pp++) 1032 if (pp->p_x == x && pp->p_y == y) 1033 return pp; 1034 errx(1, "driver: couldn't find player at (%d,%d)", x, y); 1035 /* NOTREACHED */ 1036 } 1037 1038 /* 1039 * opposite: 1040 * Return true if the bullet direction faces the opposite direction 1041 * of the player in the maze 1042 */ 1043 bool 1044 opposite(int face, char dir) 1045 { 1046 switch (face) { 1047 case LEFTS: 1048 return (dir == RIGHT); 1049 case RIGHT: 1050 return (dir == LEFTS); 1051 case ABOVE: 1052 return (dir == BELOW); 1053 case BELOW: 1054 return (dir == ABOVE); 1055 default: 1056 return false; 1057 } 1058 } 1059 1060 /* 1061 * is_bullet: 1062 * Is there a bullet at the given coordinates? If so, return 1063 * a pointer to the bullet, otherwise return NULL 1064 */ 1065 BULLET * 1066 is_bullet(int y, int x) 1067 { 1068 BULLET *bp; 1069 1070 for (bp = Bullets; bp != NULL; bp = bp->b_next) 1071 if (bp->b_y == y && bp->b_x == x) 1072 return bp; 1073 return NULL; 1074 } 1075 1076 /* 1077 * fixshots: 1078 * change the underlying character of the shots at a location 1079 * to the given character. 1080 */ 1081 void 1082 fixshots(int y, int x, char over) 1083 { 1084 BULLET *bp; 1085 1086 for (bp = Bullets; bp != NULL; bp = bp->b_next) 1087 if (bp->b_y == y && bp->b_x == x) 1088 bp->b_over = over; 1089 } 1090 1091 /* 1092 * find_under: 1093 * find the underlying character for a bullet when it lands 1094 * on another bullet. 1095 */ 1096 static void 1097 find_under(BULLET *blist, BULLET *bp) 1098 { 1099 BULLET *nbp; 1100 1101 for (nbp = blist; nbp != NULL; nbp = nbp->b_next) 1102 if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) { 1103 bp->b_over = nbp->b_over; 1104 break; 1105 } 1106 } 1107 1108 /* 1109 * mark_player: 1110 * mark a player as under a shot 1111 */ 1112 static void 1113 mark_player(BULLET *bp) 1114 { 1115 PLAYER *pp; 1116 1117 for (pp = Player; pp < End_player; pp++) 1118 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) { 1119 pp->p_undershot = true; 1120 break; 1121 } 1122 } 1123 1124 #ifdef BOOTS 1125 /* 1126 * mark_boot: 1127 * mark a boot as under a shot 1128 */ 1129 static void 1130 mark_boot(BULLET *bp) 1131 { 1132 PLAYER *pp; 1133 1134 for (pp = Boot; pp < &Boot[NBOOTS]; pp++) 1135 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) { 1136 pp->p_undershot = true; 1137 break; 1138 } 1139 } 1140 #endif 1141