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