1 /* $NetBSD: shots.c,v 1.14 2014/03/29 21:43:19 dholland 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.14 2014/03/29 21:43:19 dholland 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 /* FALLTHROUGH */ 351 #ifndef RANDOM 352 case DOOR: 353 #endif 354 case WALL1: 355 case WALL2: 356 case WALL3: 357 bp->b_expl = true; 358 break; 359 } 360 361 bp->b_x = x; 362 bp->b_y = y; 363 } 364 return true; 365 } 366 367 #ifdef DRONE 368 /* 369 * move_drone: 370 * Move the drone to the next square 371 */ 372 static void 373 move_drone(BULLET *bp) 374 { 375 int mask, count; 376 int n, dir; 377 PLAYER *pp; 378 379 /* 380 * See if we can give someone a blast 381 */ 382 if (isplayer(Maze[bp->b_y][bp->b_x - 1])) { 383 dir = WEST; 384 goto drone_move; 385 } 386 if (isplayer(Maze[bp->b_y - 1][bp->b_x])) { 387 dir = NORTH; 388 goto drone_move; 389 } 390 if (isplayer(Maze[bp->b_y + 1][bp->b_x])) { 391 dir = SOUTH; 392 goto drone_move; 393 } 394 if (isplayer(Maze[bp->b_y][bp->b_x + 1])) { 395 dir = EAST; 396 goto drone_move; 397 } 398 399 /* 400 * Find out what directions are clear 401 */ 402 mask = count = 0; 403 if (!iswall(bp->b_y, bp->b_x - 1)) 404 mask |= WEST, count++; 405 if (!iswall(bp->b_y - 1, bp->b_x)) 406 mask |= NORTH, count++; 407 if (!iswall(bp->b_y + 1, bp->b_x)) 408 mask |= SOUTH, count++; 409 if (!iswall(bp->b_y, bp->b_x + 1)) 410 mask |= EAST, count++; 411 412 /* 413 * All blocked up, just you wait 414 */ 415 if (count == 0) 416 return true; 417 418 /* 419 * Only one way to go. 420 */ 421 if (count == 1) { 422 dir = mask; 423 goto drone_move; 424 } 425 426 /* 427 * Get rid of the direction that we came from 428 */ 429 switch (bp->b_face) { 430 case LEFTS: 431 if (mask & EAST) 432 mask &= ~EAST, count--; 433 break; 434 case RIGHT: 435 if (mask & WEST) 436 mask &= ~WEST, count--; 437 break; 438 case ABOVE: 439 if (mask & SOUTH) 440 mask &= ~SOUTH, count--; 441 break; 442 case BELOW: 443 if (mask & NORTH) 444 mask &= ~NORTH, count--; 445 break; 446 } 447 448 /* 449 * Pick one of the remaining directions 450 */ 451 n = rand_num(count); 452 if (n >= 0 && mask & NORTH) 453 dir = NORTH, n--; 454 if (n >= 0 && mask & SOUTH) 455 dir = SOUTH, n--; 456 if (n >= 0 && mask & EAST) 457 dir = EAST, n--; 458 if (n >= 0 && mask & WEST) 459 dir = WEST, n--; 460 461 /* 462 * Now that we know the direction of movement, 463 * just update the position of the drone 464 */ 465 drone_move: 466 switch (dir) { 467 case WEST: 468 bp->b_x--; 469 bp->b_face = LEFTS; 470 break; 471 case EAST: 472 bp->b_x++; 473 bp->b_face = RIGHT; 474 break; 475 case NORTH: 476 bp->b_y--; 477 bp->b_face = ABOVE; 478 break; 479 case SOUTH: 480 bp->b_y++; 481 bp->b_face = BELOW; 482 break; 483 } 484 switch (Maze[bp->b_y][bp->b_x]) { 485 case LEFTS: 486 case RIGHT: 487 case BELOW: 488 case ABOVE: 489 /* 490 * give the person a chance to catch a 491 * drone if s/he is facing it 492 */ 493 if (rand_num(100) < 1 && 494 opposite(bp->b_face, Maze[bp->b_y][bp->b_x])) { 495 pp = play_at(bp->b_y, bp->b_x); 496 pp->p_ammo += bp->b_charge; 497 message(pp, "**** Absorbed drone ****"); 498 free(bp); 499 (void) snprintf(Buf, sizeof(buf), "%3d", pp->p_ammo); 500 cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL); 501 outstr(pp, Buf, 3); 502 return false; 503 } 504 bp->b_expl = true; 505 break; 506 } 507 return true; 508 } 509 #endif 510 511 /* 512 * save_bullet: 513 * Put this bullet back onto the bullet list 514 */ 515 static void 516 save_bullet(BULLET *bp) 517 { 518 bp->b_over = Maze[bp->b_y][bp->b_x]; 519 switch (bp->b_over) { 520 case SHOT: 521 case GRENADE: 522 case SATCHEL: 523 case BOMB: 524 #ifdef OOZE 525 case SLIME: 526 #ifdef VOLCANO 527 case LAVA: 528 #endif 529 #endif 530 #ifdef DRONE 531 case DSHOT: 532 #endif 533 find_under(Bullets, bp); 534 break; 535 } 536 537 switch (bp->b_over) { 538 case LEFTS: 539 case RIGHT: 540 case ABOVE: 541 case BELOW: 542 #ifdef FLY 543 case FLYER: 544 #endif 545 mark_player(bp); 546 break; 547 #ifdef BOOTS 548 case BOOT: 549 case BOOT_PAIR: 550 mark_boot(bp); 551 #endif 552 553 default: 554 Maze[bp->b_y][bp->b_x] = bp->b_type; 555 break; 556 } 557 558 bp->b_next = Bullets; 559 Bullets = bp; 560 } 561 562 /* 563 * move_flyer: 564 * Update the position of a player in flight 565 */ 566 static void 567 move_flyer(PLAYER *pp) 568 { 569 int x, y; 570 571 if (pp->p_undershot) { 572 fixshots(pp->p_y, pp->p_x, pp->p_over); 573 pp->p_undershot = false; 574 } 575 Maze[pp->p_y][pp->p_x] = pp->p_over; 576 x = pp->p_x + pp->p_flyx; 577 y = pp->p_y + pp->p_flyy; 578 if (x < 1) { 579 x = 1 - x; 580 pp->p_flyx = -pp->p_flyx; 581 } 582 else if (x > WIDTH - 2) { 583 x = (WIDTH - 2) - (x - (WIDTH - 2)); 584 pp->p_flyx = -pp->p_flyx; 585 } 586 if (y < 1) { 587 y = 1 - y; 588 pp->p_flyy = -pp->p_flyy; 589 } 590 else if (y > HEIGHT - 2) { 591 y = (HEIGHT - 2) - (y - (HEIGHT - 2)); 592 pp->p_flyy = -pp->p_flyy; 593 } 594 again: 595 switch (Maze[y][x]) { 596 default: 597 switch (rand_num(4)) { 598 case 0: 599 PLUS_DELTA(x, WIDTH - 2); 600 break; 601 case 1: 602 MINUS_DELTA(x, 1); 603 break; 604 case 2: 605 PLUS_DELTA(y, HEIGHT - 2); 606 break; 607 case 3: 608 MINUS_DELTA(y, 1); 609 break; 610 } 611 goto again; 612 case WALL1: 613 case WALL2: 614 case WALL3: 615 #ifdef REFLECT 616 case WALL4: 617 case WALL5: 618 #endif 619 #ifdef RANDOM 620 case DOOR: 621 #endif 622 if (pp->p_flying == 0) 623 pp->p_flying++; 624 break; 625 case SPACE: 626 break; 627 } 628 pp->p_y = y; 629 pp->p_x = x; 630 if (pp->p_flying-- == 0) { 631 #ifdef BOOTS 632 if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) { 633 #endif 634 checkdam(pp, NULL, NULL, 635 rand_num(pp->p_damage / 5), FALL); 636 pp->p_face = rand_dir(); 637 showstat(pp); 638 #ifdef BOOTS 639 } 640 else { 641 if (Maze[y][x] == BOOT) 642 pp->p_face = BOOT_PAIR; 643 Maze[y][x] = SPACE; 644 } 645 #endif 646 } 647 pp->p_over = Maze[y][x]; 648 Maze[y][x] = pp->p_face; 649 showexpl(y, x, pp->p_face); 650 } 651 652 /* 653 * chkshot 654 * Handle explosions 655 */ 656 static void 657 chkshot(BULLET *bp, BULLET *next) 658 { 659 int y, x; 660 int dy, dx, absdy; 661 int delta, damage; 662 char expl; 663 PLAYER *pp; 664 665 delta = 0; 666 switch (bp->b_type) { 667 case SHOT: 668 case MINE: 669 case GRENADE: 670 case GMINE: 671 case SATCHEL: 672 case BOMB: 673 delta = bp->b_size - 1; 674 break; 675 #ifdef OOZE 676 case SLIME: 677 #ifdef VOLCANO 678 case LAVA: 679 #endif 680 chkslime(bp, next); 681 return; 682 #endif 683 #ifdef DRONE 684 case DSHOT: 685 bp->b_type = SLIME; 686 chkslime(bp, next); 687 return; 688 #endif 689 } 690 for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) { 691 if (y < 0 || y >= HEIGHT) 692 continue; 693 dy = y - bp->b_y; 694 absdy = (dy < 0) ? -dy : dy; 695 for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) { 696 if (x < 0 || x >= WIDTH) 697 continue; 698 dx = x - bp->b_x; 699 if (dx == 0) 700 expl = (dy == 0) ? '*' : '|'; 701 else if (dy == 0) 702 expl = '-'; 703 else if (dx == dy) 704 expl = '\\'; 705 else if (dx == -dy) 706 expl = '/'; 707 else 708 expl = '*'; 709 showexpl(y, x, expl); 710 switch (Maze[y][x]) { 711 case LEFTS: 712 case RIGHT: 713 case ABOVE: 714 case BELOW: 715 #ifdef FLY 716 case FLYER: 717 #endif 718 if (dx < 0) 719 dx = -dx; 720 if (absdy > dx) 721 damage = bp->b_size - absdy; 722 else 723 damage = bp->b_size - dx; 724 pp = play_at(y, x); 725 checkdam(pp, bp->b_owner, bp->b_score, 726 damage * MINDAM, bp->b_type); 727 break; 728 case GMINE: 729 case MINE: 730 add_shot((Maze[y][x] == GMINE) ? 731 GRENADE : SHOT, 732 y, x, LEFTS, 733 (Maze[y][x] == GMINE) ? 734 GRENREQ : BULREQ, 735 NULL, true, SPACE); 736 Maze[y][x] = SPACE; 737 break; 738 } 739 } 740 } 741 } 742 743 #ifdef OOZE 744 /* 745 * chkslime: 746 * handle slime shot exploding 747 */ 748 static void 749 chkslime(BULLET *bp, BULLET *next) 750 { 751 BULLET *nbp; 752 753 switch (Maze[bp->b_y][bp->b_x]) { 754 case WALL1: 755 case WALL2: 756 case WALL3: 757 #ifdef REFLECT 758 case WALL4: 759 case WALL5: 760 #endif 761 #ifdef RANDOM 762 case DOOR: 763 #endif 764 switch (bp->b_face) { 765 case LEFTS: 766 bp->b_x++; 767 break; 768 case RIGHT: 769 bp->b_x--; 770 break; 771 case ABOVE: 772 bp->b_y++; 773 break; 774 case BELOW: 775 bp->b_y--; 776 break; 777 } 778 break; 779 } 780 nbp = malloc(sizeof(*nbp)); 781 *nbp = *bp; 782 #ifdef VOLCANO 783 move_slime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED, next); 784 #else 785 move_slime(nbp, SLIMESPEED, next); 786 #endif 787 } 788 789 /* 790 * move_slime: 791 * move the given slime shot speed times and add it back if 792 * it hasn't fizzled yet 793 */ 794 static void 795 move_slime(BULLET *bp, int speed, BULLET *next) 796 { 797 int i, j, dirmask, count; 798 PLAYER *pp; 799 BULLET *nbp; 800 801 if (speed == 0) { 802 if (bp->b_charge <= 0) 803 free(bp); 804 else 805 save_bullet(bp); 806 return; 807 } 808 809 #ifdef VOLCANO 810 showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*'); 811 #else 812 showexpl(bp->b_y, bp->b_x, '*'); 813 #endif 814 switch (Maze[bp->b_y][bp->b_x]) { 815 case LEFTS: 816 case RIGHT: 817 case ABOVE: 818 case BELOW: 819 #ifdef FLY 820 case FLYER: 821 #endif 822 pp = play_at(bp->b_y, bp->b_x); 823 message(pp, "You've been slimed."); 824 checkdam(pp, bp->b_owner, bp->b_score, MINDAM, bp->b_type); 825 break; 826 case SHOT: 827 case GRENADE: 828 case SATCHEL: 829 case BOMB: 830 #ifdef DRONE 831 case DSHOT: 832 #endif 833 explshot(next, bp->b_y, bp->b_x); 834 explshot(Bullets, bp->b_y, bp->b_x); 835 break; 836 } 837 838 if (--bp->b_charge <= 0) { 839 free(bp); 840 return; 841 } 842 843 dirmask = 0; 844 count = 0; 845 switch (bp->b_face) { 846 case LEFTS: 847 if (!iswall(bp->b_y, bp->b_x - 1)) 848 dirmask |= WEST, count++; 849 if (!iswall(bp->b_y - 1, bp->b_x)) 850 dirmask |= NORTH, count++; 851 if (!iswall(bp->b_y + 1, bp->b_x)) 852 dirmask |= SOUTH, count++; 853 if (dirmask == 0) 854 if (!iswall(bp->b_y, bp->b_x + 1)) 855 dirmask |= EAST, count++; 856 break; 857 case RIGHT: 858 if (!iswall(bp->b_y, bp->b_x + 1)) 859 dirmask |= EAST, count++; 860 if (!iswall(bp->b_y - 1, bp->b_x)) 861 dirmask |= NORTH, count++; 862 if (!iswall(bp->b_y + 1, bp->b_x)) 863 dirmask |= SOUTH, count++; 864 if (dirmask == 0) 865 if (!iswall(bp->b_y, bp->b_x - 1)) 866 dirmask |= WEST, count++; 867 break; 868 case ABOVE: 869 if (!iswall(bp->b_y - 1, bp->b_x)) 870 dirmask |= NORTH, count++; 871 if (!iswall(bp->b_y, bp->b_x - 1)) 872 dirmask |= WEST, count++; 873 if (!iswall(bp->b_y, bp->b_x + 1)) 874 dirmask |= EAST, count++; 875 if (dirmask == 0) 876 if (!iswall(bp->b_y + 1, bp->b_x)) 877 dirmask |= SOUTH, count++; 878 break; 879 case BELOW: 880 if (!iswall(bp->b_y + 1, bp->b_x)) 881 dirmask |= SOUTH, count++; 882 if (!iswall(bp->b_y, bp->b_x - 1)) 883 dirmask |= WEST, count++; 884 if (!iswall(bp->b_y, bp->b_x + 1)) 885 dirmask |= EAST, count++; 886 if (dirmask == 0) 887 if (!iswall(bp->b_y - 1, bp->b_x)) 888 dirmask |= NORTH, count++; 889 break; 890 } 891 if (count == 0) { 892 /* 893 * No place to go. Just sit here for a while and wait 894 * for adjacent squares to clear out. 895 */ 896 save_bullet(bp); 897 return; 898 } 899 if (bp->b_charge < count) { 900 /* Only bp->b_charge paths may be taken */ 901 while (count > bp->b_charge) { 902 if (dirmask & WEST) 903 dirmask &= ~WEST; 904 else if (dirmask & EAST) 905 dirmask &= ~EAST; 906 else if (dirmask & NORTH) 907 dirmask &= ~NORTH; 908 else if (dirmask & SOUTH) 909 dirmask &= ~SOUTH; 910 count--; 911 } 912 } 913 914 i = bp->b_charge / count; 915 j = bp->b_charge % count; 916 if (dirmask & WEST) { 917 count--; 918 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS, 919 i, bp->b_size, bp->b_owner, bp->b_score, true, SPACE); 920 move_slime(nbp, speed - 1, next); 921 } 922 if (dirmask & EAST) { 923 count--; 924 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT, 925 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, 926 bp->b_score, true, SPACE); 927 move_slime(nbp, speed - 1, next); 928 } 929 if (dirmask & NORTH) { 930 count--; 931 nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE, 932 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, 933 bp->b_score, true, SPACE); 934 move_slime(nbp, speed - 1, next); 935 } 936 if (dirmask & SOUTH) { 937 count--; 938 nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW, 939 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, 940 bp->b_score, true, SPACE); 941 move_slime(nbp, speed - 1, next); 942 } 943 944 free(bp); 945 } 946 947 /* 948 * iswall: 949 * returns whether the given location is a wall 950 */ 951 static bool 952 iswall(int y, int x) 953 { 954 if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH) 955 return true; 956 switch (Maze[y][x]) { 957 case WALL1: 958 case WALL2: 959 case WALL3: 960 #ifdef REFLECT 961 case WALL4: 962 case WALL5: 963 #endif 964 #ifdef RANDOM 965 case DOOR: 966 #endif 967 #ifdef OOZE 968 case SLIME: 969 #ifdef VOLCANO 970 case LAVA: 971 #endif 972 #endif 973 return true; 974 } 975 return false; 976 } 977 #endif 978 979 /* 980 * zapshot: 981 * Take a shot out of the air. 982 */ 983 static void 984 zapshot(BULLET *blist, BULLET *obp) 985 { 986 BULLET *bp; 987 bool explode; 988 989 explode = false; 990 for (bp = blist; bp != NULL; bp = bp->b_next) { 991 if (bp->b_x != obp->b_x || bp->b_y != obp->b_y) 992 continue; 993 if (bp->b_face == obp->b_face) 994 continue; 995 explode = true; 996 break; 997 } 998 if (!explode) 999 return; 1000 explshot(blist, obp->b_y, obp->b_x); 1001 } 1002 1003 /* 1004 * explshot - 1005 * Make all shots at this location blow up 1006 */ 1007 static void 1008 explshot(BULLET *blist, int y, int x) 1009 { 1010 BULLET *bp; 1011 1012 for (bp = blist; bp != NULL; bp = bp->b_next) 1013 if (bp->b_x == x && bp->b_y == y) { 1014 bp->b_expl = true; 1015 if (bp->b_owner != NULL) 1016 message(bp->b_owner, "Shot intercepted"); 1017 } 1018 } 1019 1020 /* 1021 * play_at: 1022 * Return a pointer to the player at the given location 1023 */ 1024 PLAYER * 1025 play_at(int y, int x) 1026 { 1027 PLAYER *pp; 1028 1029 for (pp = Player; pp < End_player; pp++) 1030 if (pp->p_x == x && pp->p_y == y) 1031 return pp; 1032 errx(1, "driver: couldn't find player at (%d,%d)", x, y); 1033 /* NOTREACHED */ 1034 } 1035 1036 /* 1037 * opposite: 1038 * Return true if the bullet direction faces the opposite direction 1039 * of the player in the maze 1040 */ 1041 bool 1042 opposite(int face, char dir) 1043 { 1044 switch (face) { 1045 case LEFTS: 1046 return (dir == RIGHT); 1047 case RIGHT: 1048 return (dir == LEFTS); 1049 case ABOVE: 1050 return (dir == BELOW); 1051 case BELOW: 1052 return (dir == ABOVE); 1053 default: 1054 return false; 1055 } 1056 } 1057 1058 /* 1059 * is_bullet: 1060 * Is there a bullet at the given coordinates? If so, return 1061 * a pointer to the bullet, otherwise return NULL 1062 */ 1063 BULLET * 1064 is_bullet(int y, int x) 1065 { 1066 BULLET *bp; 1067 1068 for (bp = Bullets; bp != NULL; bp = bp->b_next) 1069 if (bp->b_y == y && bp->b_x == x) 1070 return bp; 1071 return NULL; 1072 } 1073 1074 /* 1075 * fixshots: 1076 * change the underlying character of the shots at a location 1077 * to the given character. 1078 */ 1079 void 1080 fixshots(int y, int x, char over) 1081 { 1082 BULLET *bp; 1083 1084 for (bp = Bullets; bp != NULL; bp = bp->b_next) 1085 if (bp->b_y == y && bp->b_x == x) 1086 bp->b_over = over; 1087 } 1088 1089 /* 1090 * find_under: 1091 * find the underlying character for a bullet when it lands 1092 * on another bullet. 1093 */ 1094 static void 1095 find_under(BULLET *blist, BULLET *bp) 1096 { 1097 BULLET *nbp; 1098 1099 for (nbp = blist; nbp != NULL; nbp = nbp->b_next) 1100 if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) { 1101 bp->b_over = nbp->b_over; 1102 break; 1103 } 1104 } 1105 1106 /* 1107 * mark_player: 1108 * mark a player as under a shot 1109 */ 1110 static void 1111 mark_player(BULLET *bp) 1112 { 1113 PLAYER *pp; 1114 1115 for (pp = Player; pp < End_player; pp++) 1116 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) { 1117 pp->p_undershot = true; 1118 break; 1119 } 1120 } 1121 1122 #ifdef BOOTS 1123 /* 1124 * mark_boot: 1125 * mark a boot as under a shot 1126 */ 1127 static void 1128 mark_boot(BULLET *bp) 1129 { 1130 PLAYER *pp; 1131 1132 for (pp = Boot; pp < &Boot[NBOOTS]; pp++) 1133 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) { 1134 pp->p_undershot = true; 1135 break; 1136 } 1137 } 1138 #endif 1139