1 /* $NetBSD: shots.c,v 1.6 2006/03/17 23:34:37 abs 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.6 2006/03/17 23:34:37 abs 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 int 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() 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((char *) 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(bp) 187 BULLET *bp; 188 { 189 int i, x, y; 190 PLAYER *pp; 191 192 for (i = 0; i < BULSPD; i++) { 193 if (bp->b_expl) 194 break; 195 196 x = bp->b_x; 197 y = bp->b_y; 198 199 switch (bp->b_face) { 200 case LEFTS: 201 x--; 202 break; 203 case RIGHT: 204 x++; 205 break; 206 case ABOVE: 207 y--; 208 break; 209 case BELOW: 210 y++; 211 break; 212 } 213 214 switch (Maze[y][x]) { 215 case SHOT: 216 if (rand_num(100) < 5) { 217 zapshot(Bullets, bp); 218 zapshot(bp->b_next, bp); 219 } 220 break; 221 case GRENADE: 222 if (rand_num(100) < 10) { 223 zapshot(Bullets, bp); 224 zapshot(bp->b_next, bp); 225 } 226 break; 227 # ifdef REFLECT 228 case WALL4: /* reflecting walls */ 229 switch (bp->b_face) { 230 case LEFTS: 231 bp->b_face = BELOW; 232 break; 233 case RIGHT: 234 bp->b_face = ABOVE; 235 break; 236 case ABOVE: 237 bp->b_face = RIGHT; 238 break; 239 case BELOW: 240 bp->b_face = LEFTS; 241 break; 242 } 243 Maze[y][x] = WALL5; 244 # ifdef MONITOR 245 for (pp = Monitor; pp < End_monitor; pp++) 246 check(pp, y, x); 247 # endif 248 break; 249 case WALL5: 250 switch (bp->b_face) { 251 case LEFTS: 252 bp->b_face = ABOVE; 253 break; 254 case RIGHT: 255 bp->b_face = BELOW; 256 break; 257 case ABOVE: 258 bp->b_face = LEFTS; 259 break; 260 case BELOW: 261 bp->b_face = RIGHT; 262 break; 263 } 264 Maze[y][x] = WALL4; 265 # ifdef MONITOR 266 for (pp = Monitor; pp < End_monitor; pp++) 267 check(pp, y, x); 268 # endif 269 break; 270 # endif 271 # ifdef RANDOM 272 case DOOR: 273 switch (rand_num(4)) { 274 case 0: 275 bp->b_face = ABOVE; 276 break; 277 case 1: 278 bp->b_face = BELOW; 279 break; 280 case 2: 281 bp->b_face = LEFTS; 282 break; 283 case 3: 284 bp->b_face = RIGHT; 285 break; 286 } 287 break; 288 # endif 289 # ifdef FLY 290 case FLYER: 291 pp = play_at(y, x); 292 message(pp, "Zing!"); 293 break; 294 # endif 295 case LEFTS: 296 case RIGHT: 297 case BELOW: 298 case ABOVE: 299 /* 300 * give the person a chance to catch a 301 * grenade if s/he is facing it 302 */ 303 pp = play_at(y, x); 304 pp->p_ident->i_shot += bp->b_charge; 305 if (opposite(bp->b_face, Maze[y][x])) { 306 if (rand_num(100) < 10) { 307 if (bp->b_owner != NULL) 308 message(bp->b_owner, 309 "Your charge was absorbed!"); 310 if (bp->b_score != NULL) 311 bp->b_score->i_robbed += bp->b_charge; 312 pp->p_ammo += bp->b_charge; 313 if (pp->p_damage + bp->b_size * MINDAM 314 > pp->p_damcap) 315 pp->p_ident->i_saved++; 316 message(pp, "Absorbed charge (good shield!)"); 317 pp->p_ident->i_absorbed += bp->b_charge; 318 free((char *) bp); 319 (void) sprintf(Buf, "%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(bp) 374 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((char *) bp); 500 (void) sprintf(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(bp) 518 BULLET *bp; 519 { 520 bp->b_over = Maze[bp->b_y][bp->b_x]; 521 switch (bp->b_over) { 522 case SHOT: 523 case GRENADE: 524 case SATCHEL: 525 case BOMB: 526 # ifdef OOZE 527 case SLIME: 528 # ifdef VOLCANO 529 case LAVA: 530 # endif 531 # endif 532 # ifdef DRONE 533 case DSHOT: 534 # endif 535 find_under(Bullets, bp); 536 break; 537 } 538 539 switch (bp->b_over) { 540 case LEFTS: 541 case RIGHT: 542 case ABOVE: 543 case BELOW: 544 # ifdef FLY 545 case FLYER: 546 # endif 547 mark_player(bp); 548 break; 549 # ifdef BOOTS 550 case BOOT: 551 case BOOT_PAIR: 552 mark_boot(bp); 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(pp) 570 PLAYER *pp; 571 { 572 int x, y; 573 574 if (pp->p_undershot) { 575 fixshots(pp->p_y, pp->p_x, pp->p_over); 576 pp->p_undershot = FALSE; 577 } 578 Maze[pp->p_y][pp->p_x] = pp->p_over; 579 x = pp->p_x + pp->p_flyx; 580 y = pp->p_y + pp->p_flyy; 581 if (x < 1) { 582 x = 1 - x; 583 pp->p_flyx = -pp->p_flyx; 584 } 585 else if (x > WIDTH - 2) { 586 x = (WIDTH - 2) - (x - (WIDTH - 2)); 587 pp->p_flyx = -pp->p_flyx; 588 } 589 if (y < 1) { 590 y = 1 - y; 591 pp->p_flyy = -pp->p_flyy; 592 } 593 else if (y > HEIGHT - 2) { 594 y = (HEIGHT - 2) - (y - (HEIGHT - 2)); 595 pp->p_flyy = -pp->p_flyy; 596 } 597 again: 598 switch (Maze[y][x]) { 599 default: 600 switch (rand_num(4)) { 601 case 0: 602 PLUS_DELTA(x, WIDTH - 2); 603 break; 604 case 1: 605 MINUS_DELTA(x, 1); 606 break; 607 case 2: 608 PLUS_DELTA(y, HEIGHT - 2); 609 break; 610 case 3: 611 MINUS_DELTA(y, 1); 612 break; 613 } 614 goto again; 615 case WALL1: 616 case WALL2: 617 case WALL3: 618 # ifdef REFLECT 619 case WALL4: 620 case WALL5: 621 # endif 622 # ifdef RANDOM 623 case DOOR: 624 # endif 625 if (pp->p_flying == 0) 626 pp->p_flying++; 627 break; 628 case SPACE: 629 break; 630 } 631 pp->p_y = y; 632 pp->p_x = x; 633 if (pp->p_flying-- == 0) { 634 # ifdef BOOTS 635 if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) { 636 # endif 637 checkdam(pp, (PLAYER *) NULL, (IDENT *) NULL, 638 rand_num(pp->p_damage / 5), FALL); 639 pp->p_face = rand_dir(); 640 showstat(pp); 641 # ifdef BOOTS 642 } 643 else { 644 if (Maze[y][x] == BOOT) 645 pp->p_face = BOOT_PAIR; 646 Maze[y][x] = SPACE; 647 } 648 # endif 649 } 650 pp->p_over = Maze[y][x]; 651 Maze[y][x] = pp->p_face; 652 showexpl(y, x, pp->p_face); 653 } 654 655 /* 656 * chkshot 657 * Handle explosions 658 */ 659 static void 660 chkshot(bp, next) 661 BULLET *bp; 662 BULLET *next; 663 { 664 int y, x; 665 int dy, dx, absdy; 666 int delta, damage; 667 char expl; 668 PLAYER *pp; 669 670 delta = 0; 671 switch (bp->b_type) { 672 case SHOT: 673 case MINE: 674 case GRENADE: 675 case GMINE: 676 case SATCHEL: 677 case BOMB: 678 delta = bp->b_size - 1; 679 break; 680 # ifdef OOZE 681 case SLIME: 682 # ifdef VOLCANO 683 case LAVA: 684 # endif 685 chkslime(bp, next); 686 return; 687 # endif 688 # ifdef DRONE 689 case DSHOT: 690 bp->b_type = SLIME; 691 chkslime(bp, next); 692 return; 693 # endif 694 } 695 for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) { 696 if (y < 0 || y >= HEIGHT) 697 continue; 698 dy = y - bp->b_y; 699 absdy = (dy < 0) ? -dy : dy; 700 for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) { 701 if (x < 0 || x >= WIDTH) 702 continue; 703 dx = x - bp->b_x; 704 if (dx == 0) 705 expl = (dy == 0) ? '*' : '|'; 706 else if (dy == 0) 707 expl = '-'; 708 else if (dx == dy) 709 expl = '\\'; 710 else if (dx == -dy) 711 expl = '/'; 712 else 713 expl = '*'; 714 showexpl(y, x, expl); 715 switch (Maze[y][x]) { 716 case LEFTS: 717 case RIGHT: 718 case ABOVE: 719 case BELOW: 720 # ifdef FLY 721 case FLYER: 722 # endif 723 if (dx < 0) 724 dx = -dx; 725 if (absdy > dx) 726 damage = bp->b_size - absdy; 727 else 728 damage = bp->b_size - dx; 729 pp = play_at(y, x); 730 checkdam(pp, bp->b_owner, bp->b_score, 731 damage * MINDAM, bp->b_type); 732 break; 733 case GMINE: 734 case MINE: 735 add_shot((Maze[y][x] == GMINE) ? 736 GRENADE : SHOT, 737 y, x, LEFTS, 738 (Maze[y][x] == GMINE) ? 739 GRENREQ : BULREQ, 740 (PLAYER *) NULL, TRUE, SPACE); 741 Maze[y][x] = SPACE; 742 break; 743 } 744 } 745 } 746 } 747 748 # ifdef OOZE 749 /* 750 * chkslime: 751 * handle slime shot exploding 752 */ 753 static void 754 chkslime(bp, next) 755 BULLET *bp; 756 BULLET *next; 757 { 758 BULLET *nbp; 759 760 switch (Maze[bp->b_y][bp->b_x]) { 761 case WALL1: 762 case WALL2: 763 case WALL3: 764 # ifdef REFLECT 765 case WALL4: 766 case WALL5: 767 # endif 768 # ifdef RANDOM 769 case DOOR: 770 # endif 771 switch (bp->b_face) { 772 case LEFTS: 773 bp->b_x++; 774 break; 775 case RIGHT: 776 bp->b_x--; 777 break; 778 case ABOVE: 779 bp->b_y++; 780 break; 781 case BELOW: 782 bp->b_y--; 783 break; 784 } 785 break; 786 } 787 nbp = (BULLET *) malloc(sizeof (BULLET)); 788 *nbp = *bp; 789 # ifdef VOLCANO 790 move_slime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED, next); 791 # else 792 move_slime(nbp, SLIMESPEED, next); 793 # endif 794 } 795 796 /* 797 * move_slime: 798 * move the given slime shot speed times and add it back if 799 * it hasn't fizzled yet 800 */ 801 void 802 move_slime(bp, speed, next) 803 BULLET *bp; 804 int speed; 805 BULLET *next; 806 { 807 int i, j, dirmask, count; 808 PLAYER *pp; 809 BULLET *nbp; 810 811 if (speed == 0) { 812 if (bp->b_charge <= 0) 813 free((char *) bp); 814 else 815 save_bullet(bp); 816 return; 817 } 818 819 # ifdef VOLCANO 820 showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*'); 821 # else 822 showexpl(bp->b_y, bp->b_x, '*'); 823 # endif 824 switch (Maze[bp->b_y][bp->b_x]) { 825 case LEFTS: 826 case RIGHT: 827 case ABOVE: 828 case BELOW: 829 # ifdef FLY 830 case FLYER: 831 # endif 832 pp = play_at(bp->b_y, bp->b_x); 833 message(pp, "You've been slimed."); 834 checkdam(pp, bp->b_owner, bp->b_score, MINDAM, bp->b_type); 835 break; 836 case SHOT: 837 case GRENADE: 838 case SATCHEL: 839 case BOMB: 840 # ifdef DRONE 841 case DSHOT: 842 # endif 843 explshot(next, bp->b_y, bp->b_x); 844 explshot(Bullets, bp->b_y, bp->b_x); 845 break; 846 } 847 848 if (--bp->b_charge <= 0) { 849 free((char *) bp); 850 return; 851 } 852 853 dirmask = 0; 854 count = 0; 855 switch (bp->b_face) { 856 case LEFTS: 857 if (!iswall(bp->b_y, bp->b_x - 1)) 858 dirmask |= WEST, count++; 859 if (!iswall(bp->b_y - 1, bp->b_x)) 860 dirmask |= NORTH, count++; 861 if (!iswall(bp->b_y + 1, bp->b_x)) 862 dirmask |= SOUTH, count++; 863 if (dirmask == 0) 864 if (!iswall(bp->b_y, bp->b_x + 1)) 865 dirmask |= EAST, count++; 866 break; 867 case RIGHT: 868 if (!iswall(bp->b_y, bp->b_x + 1)) 869 dirmask |= EAST, count++; 870 if (!iswall(bp->b_y - 1, bp->b_x)) 871 dirmask |= NORTH, count++; 872 if (!iswall(bp->b_y + 1, bp->b_x)) 873 dirmask |= SOUTH, count++; 874 if (dirmask == 0) 875 if (!iswall(bp->b_y, bp->b_x - 1)) 876 dirmask |= WEST, count++; 877 break; 878 case ABOVE: 879 if (!iswall(bp->b_y - 1, bp->b_x)) 880 dirmask |= NORTH, count++; 881 if (!iswall(bp->b_y, bp->b_x - 1)) 882 dirmask |= WEST, count++; 883 if (!iswall(bp->b_y, bp->b_x + 1)) 884 dirmask |= EAST, count++; 885 if (dirmask == 0) 886 if (!iswall(bp->b_y + 1, bp->b_x)) 887 dirmask |= SOUTH, count++; 888 break; 889 case BELOW: 890 if (!iswall(bp->b_y + 1, bp->b_x)) 891 dirmask |= SOUTH, count++; 892 if (!iswall(bp->b_y, bp->b_x - 1)) 893 dirmask |= WEST, count++; 894 if (!iswall(bp->b_y, bp->b_x + 1)) 895 dirmask |= EAST, count++; 896 if (dirmask == 0) 897 if (!iswall(bp->b_y - 1, bp->b_x)) 898 dirmask |= NORTH, count++; 899 break; 900 } 901 if (count == 0) { 902 /* 903 * No place to go. Just sit here for a while and wait 904 * for adjacent squares to clear out. 905 */ 906 save_bullet(bp); 907 return; 908 } 909 if (bp->b_charge < count) { 910 /* Only bp->b_charge paths may be taken */ 911 while (count > bp->b_charge) { 912 if (dirmask & WEST) 913 dirmask &= ~WEST; 914 else if (dirmask & EAST) 915 dirmask &= ~EAST; 916 else if (dirmask & NORTH) 917 dirmask &= ~NORTH; 918 else if (dirmask & SOUTH) 919 dirmask &= ~SOUTH; 920 count--; 921 } 922 } 923 924 i = bp->b_charge / count; 925 j = bp->b_charge % count; 926 if (dirmask & WEST) { 927 count--; 928 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS, 929 i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE); 930 move_slime(nbp, speed - 1, next); 931 } 932 if (dirmask & EAST) { 933 count--; 934 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT, 935 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, 936 bp->b_score, TRUE, SPACE); 937 move_slime(nbp, speed - 1, next); 938 } 939 if (dirmask & NORTH) { 940 count--; 941 nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE, 942 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, 943 bp->b_score, TRUE, SPACE); 944 move_slime(nbp, speed - 1, next); 945 } 946 if (dirmask & SOUTH) { 947 count--; 948 nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW, 949 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, 950 bp->b_score, TRUE, SPACE); 951 move_slime(nbp, speed - 1, next); 952 } 953 954 free((char *) bp); 955 } 956 957 /* 958 * iswall: 959 * returns whether the given location is a wall 960 */ 961 static int 962 iswall(y, x) 963 int y, x; 964 { 965 if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH) 966 return TRUE; 967 switch (Maze[y][x]) { 968 case WALL1: 969 case WALL2: 970 case WALL3: 971 # ifdef REFLECT 972 case WALL4: 973 case WALL5: 974 # endif 975 # ifdef RANDOM 976 case DOOR: 977 # endif 978 # ifdef OOZE 979 case SLIME: 980 # ifdef VOLCANO 981 case LAVA: 982 # endif 983 # endif 984 return TRUE; 985 } 986 return FALSE; 987 } 988 # endif 989 990 /* 991 * zapshot: 992 * Take a shot out of the air. 993 */ 994 static void 995 zapshot(blist, obp) 996 BULLET *blist, *obp; 997 { 998 BULLET *bp; 999 FLAG explode; 1000 1001 explode = FALSE; 1002 for (bp = blist; bp != NULL; bp = bp->b_next) { 1003 if (bp->b_x != obp->b_x || bp->b_y != obp->b_y) 1004 continue; 1005 if (bp->b_face == obp->b_face) 1006 continue; 1007 explode = TRUE; 1008 break; 1009 } 1010 if (!explode) 1011 return; 1012 explshot(blist, obp->b_y, obp->b_x); 1013 } 1014 1015 /* 1016 * explshot - 1017 * Make all shots at this location blow up 1018 */ 1019 void 1020 explshot(blist, y, x) 1021 BULLET *blist; 1022 int y, x; 1023 { 1024 BULLET *bp; 1025 1026 for (bp = blist; bp != NULL; bp = bp->b_next) 1027 if (bp->b_x == x && bp->b_y == y) { 1028 bp->b_expl = TRUE; 1029 if (bp->b_owner != NULL) 1030 message(bp->b_owner, "Shot intercepted"); 1031 } 1032 } 1033 1034 /* 1035 * play_at: 1036 * Return a pointer to the player at the given location 1037 */ 1038 PLAYER * 1039 play_at(y, x) 1040 int y, x; 1041 { 1042 PLAYER *pp; 1043 1044 for (pp = Player; pp < End_player; pp++) 1045 if (pp->p_x == x && pp->p_y == y) 1046 return pp; 1047 errx(1, "driver: couldn't find player at (%d,%d)", x, y); 1048 /* NOTREACHED */ 1049 } 1050 1051 /* 1052 * opposite: 1053 * Return TRUE if the bullet direction faces the opposite direction 1054 * of the player in the maze 1055 */ 1056 int 1057 opposite(face, dir) 1058 int face; 1059 char dir; 1060 { 1061 switch (face) { 1062 case LEFTS: 1063 return (dir == RIGHT); 1064 case RIGHT: 1065 return (dir == LEFTS); 1066 case ABOVE: 1067 return (dir == BELOW); 1068 case BELOW: 1069 return (dir == ABOVE); 1070 default: 1071 return FALSE; 1072 } 1073 } 1074 1075 /* 1076 * is_bullet: 1077 * Is there a bullet at the given coordinates? If so, return 1078 * a pointer to the bullet, otherwise return NULL 1079 */ 1080 BULLET * 1081 is_bullet(y, x) 1082 int y, x; 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 return bp; 1089 return NULL; 1090 } 1091 1092 /* 1093 * fixshots: 1094 * change the underlying character of the shots at a location 1095 * to the given character. 1096 */ 1097 void 1098 fixshots(y, x, over) 1099 int y, x; 1100 char over; 1101 { 1102 BULLET *bp; 1103 1104 for (bp = Bullets; bp != NULL; bp = bp->b_next) 1105 if (bp->b_y == y && bp->b_x == x) 1106 bp->b_over = over; 1107 } 1108 1109 /* 1110 * find_under: 1111 * find the underlying character for a bullet when it lands 1112 * on another bullet. 1113 */ 1114 static void 1115 find_under(blist, bp) 1116 BULLET *blist, *bp; 1117 { 1118 BULLET *nbp; 1119 1120 for (nbp = blist; nbp != NULL; nbp = nbp->b_next) 1121 if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) { 1122 bp->b_over = nbp->b_over; 1123 break; 1124 } 1125 } 1126 1127 /* 1128 * mark_player: 1129 * mark a player as under a shot 1130 */ 1131 static void 1132 mark_player(bp) 1133 BULLET *bp; 1134 { 1135 PLAYER *pp; 1136 1137 for (pp = Player; pp < End_player; pp++) 1138 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) { 1139 pp->p_undershot = TRUE; 1140 break; 1141 } 1142 } 1143 1144 # ifdef BOOTS 1145 /* 1146 * mark_boot: 1147 * mark a boot as under a shot 1148 */ 1149 static void 1150 mark_boot(bp) 1151 BULLET *bp; 1152 { 1153 PLAYER *pp; 1154 1155 for (pp = Boot; pp < &Boot[NBOOTS]; pp++) 1156 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) { 1157 pp->p_undershot = TRUE; 1158 break; 1159 } 1160 } 1161 # endif 1162