1: /* 2: * Hunt 3: * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold 4: * San Francisco, California 5: * 6: * Copyright (c) 1985 Regents of the University of California. 7: * All rights reserved. The Berkeley software License Agreement 8: * specifies the terms and conditions for redistribution. 9: */ 10: 11: # include "hunt.h" 12: # include <signal.h> 13: 14: # define PLUS_DELTA(x, max) if (x < max) x++; else x-- 15: # define MINUS_DELTA(x, min) if (x > min) x--; else x++ 16: 17: /* 18: * moveshots: 19: * Move the shots already in the air, taking explosions into account 20: */ 21: moveshots() 22: { 23: register BULLET *bp, *next; 24: register PLAYER *pp; 25: register int x, y; 26: register BULLET *blist; 27: register int i; 28: 29: rollexpl(); 30: if (Bullets == NULL) 31: goto ret; 32: 33: /* 34: * First we move through the bullet list BULSPD times, looking 35: * for things we may have run into. If we do run into 36: * something, we set up the explosion and disappear, checking 37: * for damage to any player who got in the way. 38: */ 39: 40: blist = Bullets; 41: Bullets = NULL; 42: for (bp = blist; bp != NULL; bp = next) { 43: next = bp->b_next; 44: x = bp->b_x; 45: y = bp->b_y; 46: Maze[y][x] = bp->b_over; 47: for (pp = Player; pp < End_player; pp++) 48: check(pp, y, x); 49: # ifdef MONITOR 50: for (pp = Monitor; pp < End_monitor; pp++) 51: check(pp, y, x); 52: # endif MONITOR 53: 54: for (i = 0; i < BULSPD; i++) { 55: if (bp->b_expl) 56: break; 57: 58: x = bp->b_x; 59: y = bp->b_y; 60: 61: switch (bp->b_face) { 62: case LEFTS: 63: x--; 64: break; 65: case RIGHT: 66: x++; 67: break; 68: case ABOVE: 69: y--; 70: break; 71: case BELOW: 72: y++; 73: break; 74: } 75: 76: switch (Maze[y][x]) { 77: case SHOT: 78: if (rand_num(100) < 5) { 79: zapshot(Bullets, bp); 80: zapshot(next, bp); 81: } 82: break; 83: case GRENADE: 84: if (rand_num(100) < 10) { 85: zapshot(Bullets, bp); 86: zapshot(next, bp); 87: } 88: break; 89: # ifdef REFLECT 90: case WALL4: /* reflecting walls */ 91: switch (bp->b_face) { 92: case LEFTS: 93: bp->b_face = BELOW; 94: break; 95: case RIGHT: 96: bp->b_face = ABOVE; 97: break; 98: case ABOVE: 99: bp->b_face = RIGHT; 100: break; 101: case BELOW: 102: bp->b_face = LEFTS; 103: break; 104: } 105: Maze[y][x] = WALL5; 106: # ifdef MONITOR 107: for (pp = Monitor; pp < End_monitor; pp++) 108: check(pp, y, x); 109: # endif MONITOR 110: break; 111: case WALL5: 112: switch (bp->b_face) { 113: case LEFTS: 114: bp->b_face = ABOVE; 115: break; 116: case RIGHT: 117: bp->b_face = BELOW; 118: break; 119: case ABOVE: 120: bp->b_face = LEFTS; 121: break; 122: case BELOW: 123: bp->b_face = RIGHT; 124: break; 125: } 126: Maze[y][x] = WALL4; 127: # ifdef MONITOR 128: for (pp = Monitor; pp < End_monitor; pp++) 129: check(pp, y, x); 130: # endif MONITOR 131: break; 132: # endif REFLECT 133: # ifdef RANDOM 134: case DOOR: 135: switch (rand_num(4)) { 136: case 0: 137: bp->b_face = ABOVE; 138: break; 139: case 1: 140: bp->b_face = BELOW; 141: break; 142: case 2: 143: bp->b_face = LEFTS; 144: break; 145: case 3: 146: bp->b_face = RIGHT; 147: break; 148: } 149: break; 150: # endif RANDOM 151: case LEFTS: 152: case RIGHT: 153: case BELOW: 154: case ABOVE: 155: # ifdef FLY 156: case FLYER: 157: # endif FLY 158: /* 159: * give the person a chance to catch a 160: * grenade if s/he is facing it 161: */ 162: if (rand_num(100) < 10 163: && opposite(bp->b_face, Maze[y][x])) { 164: if (bp->b_owner != NULL) 165: message(bp->b_owner, 166: "Your charge was absorbed!"); 167: pp = play_at(y, x); 168: pp->p_ammo += bp->b_charge; 169: (void) sprintf(Buf, 170: "Absorbed charge (good shield!)"); 171: message(pp, Buf); 172: free((char *) bp); 173: (void) sprintf(Buf, "%3d", pp->p_ammo); 174: cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL); 175: outstr(pp, Buf, 3); 176: goto next_bullet; 177: } 178: /* FALLTHROUGH */ 179: # ifndef RANDOM 180: case DOOR: 181: # endif RANDOM 182: case WALL1: 183: case WALL2: 184: case WALL3: 185: bp->b_expl = TRUE; 186: break; 187: } 188: 189: bp->b_x = x; 190: bp->b_y = y; 191: } 192: 193: bp->b_next = Bullets; 194: Bullets = bp; 195: next_bullet: 196: ; 197: } 198: 199: blist = Bullets; 200: Bullets = NULL; 201: for (bp = blist; bp != NULL; bp = next) { 202: next = bp->b_next; 203: if (!bp->b_expl) { 204: save_bullet(bp); 205: # ifdef MONITOR 206: for (pp = Monitor; pp < End_monitor; pp++) 207: check(pp, bp->b_y, bp->b_x); 208: # endif MONITOR 209: continue; 210: } 211: 212: chkshot(bp); 213: free((char *) bp); 214: } 215: for (pp = Player; pp < End_player; pp++) 216: Maze[pp->p_y][pp->p_x] = pp->p_face; 217: ret: 218: for (pp = Player; pp < End_player; pp++) { 219: # ifdef FLY 220: if (pp->p_flying >= 0) { 221: Maze[pp->p_y][pp->p_x] = pp->p_over; 222: x = pp->p_x + pp->p_flyx; 223: y = pp->p_y + pp->p_flyy; 224: if (x < 1) { 225: x = 1 - x; 226: pp->p_flyx = -pp->p_flyx; 227: } 228: else if (x > WIDTH - 2) { 229: x = (WIDTH - 2) - (x - (WIDTH - 2)); 230: pp->p_flyx = -pp->p_flyx; 231: } 232: if (y < 1) { 233: y = 1 - y; 234: pp->p_flyy = -pp->p_flyy; 235: } 236: else if (y > HEIGHT - 2) { 237: y = (HEIGHT - 2) - (y - (HEIGHT - 2)); 238: pp->p_flyy = -pp->p_flyy; 239: } 240: again: switch (Maze[y][x]) { 241: case LEFTS: 242: case RIGHT: 243: case ABOVE: 244: case BELOW: 245: case FLYER: 246: switch (rand_num(4)) { 247: case 0: 248: PLUS_DELTA(x, WIDTH - 2); 249: break; 250: case 1: 251: MINUS_DELTA(x, 1); 252: break; 253: case 2: 254: PLUS_DELTA(y, HEIGHT - 2); 255: break; 256: case 3: 257: MINUS_DELTA(y, 1); 258: break; 259: } 260: goto again; 261: case WALL1: 262: case WALL2: 263: case WALL3: 264: # ifdef REFLECT 265: case WALL4: 266: case WALL5: 267: # endif REFLECT 268: # ifdef RANDOM 269: case DOOR: 270: # endif RANDOM 271: if (pp->p_flying == 0) 272: pp->p_flying++; 273: break; 274: case MINE: 275: checkdam(pp, NULL, NULL, MINDAM, MINE); 276: Maze[y][x] = SPACE; 277: break; 278: case GMINE: 279: checkdam(pp, NULL, NULL, MINDAM, GMINE); 280: checkdam(pp, NULL, NULL, MINDAM, GMINE); 281: Maze[y][x] = SPACE; 282: break; 283: } 284: pp->p_y = y; 285: pp->p_x = x; 286: pp->p_over = Maze[y][x]; 287: if (pp->p_flying-- == 0) { 288: checkdam(pp, NULL, NULL, 289: rand_num(pp->p_damage / 5), FALL); 290: rand_face(pp); 291: showstat(pp); 292: } 293: Maze[y][x] = pp->p_face; 294: showexpl(y, x, pp->p_face); 295: } 296: # endif FLY 297: sendcom(pp, REFRESH); /* Flush out the explosions */ 298: look(pp); 299: sendcom(pp, REFRESH); 300: } 301: # ifdef MONITOR 302: for (pp = Monitor; pp < End_monitor; pp++) 303: sendcom(pp, REFRESH); 304: # endif MONITOR 305: 306: # ifdef CONSTANT_MOVE 307: if (Bullets != NULL) { 308: bul_alarm(1); 309: return; 310: } 311: for (i = 0; i < EXPLEN; i++) 312: if (Expl[i] != NULL) { 313: bul_alarm(1); 314: return; 315: } 316: bul_alarm(0); 317: # endif CONSTANT_MOVE 318: 319: return; 320: } 321: 322: save_bullet(bp) 323: register BULLET *bp; 324: { 325: bp->b_over = Maze[bp->b_y][bp->b_x]; 326: switch (bp->b_over) { 327: case SHOT: 328: case GRENADE: 329: case SATCHEL: 330: case BOMB: 331: # ifdef OOZE 332: case SLIME: 333: # ifdef VOLCANO 334: case LAVA: 335: # endif VOLCANO 336: # endif OOZE 337: find_under(Bullets, bp); 338: break; 339: } 340: 341: switch (bp->b_over) { 342: case LEFTS: 343: case RIGHT: 344: case ABOVE: 345: case BELOW: 346: # ifdef FLY 347: case FLYER: 348: # endif FLY 349: mark_player(bp); 350: break; 351: 352: default: 353: Maze[bp->b_y][bp->b_x] = bp->b_type; 354: break; 355: } 356: 357: bp->b_next = Bullets; 358: Bullets = bp; 359: } 360: 361: /* 362: * chkshot 363: * Handle explosions 364: */ 365: chkshot(bp) 366: register BULLET *bp; 367: { 368: register int y, x; 369: register int dy, dx, absdy; 370: register int delta, damage; 371: register char expl; 372: register PLAYER *pp; 373: 374: switch (bp->b_type) { 375: case SHOT: 376: case MINE: 377: delta = 0; 378: break; 379: case GRENADE: 380: case GMINE: 381: delta = 1; 382: break; 383: case SATCHEL: 384: delta = 2; 385: break; 386: case BOMB: 387: delta = 3; 388: break; 389: # ifdef OOZE 390: case SLIME: 391: # ifdef VOLCANO 392: case LAVA: 393: # endif VOLCANO 394: chkslime(bp); 395: return; 396: # endif OOZE 397: } 398: for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) { 399: if (y < 0 || y >= HEIGHT) 400: continue; 401: dy = y - bp->b_y; 402: absdy = (dy < 0) ? -dy : dy; 403: for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) { 404: if (x < 0 || x >= WIDTH) 405: continue; 406: dx = x - bp->b_x; 407: if (dx == 0) 408: expl = (dy == 0) ? '*' : '|'; 409: else if (dy == 0) 410: expl = '-'; 411: else if (dx == dy) 412: expl = '\\'; 413: else if (dx == -dy) 414: expl = '/'; 415: else 416: expl = '*'; 417: showexpl(y, x, expl); 418: switch (Maze[y][x]) { 419: case LEFTS: 420: case RIGHT: 421: case ABOVE: 422: case BELOW: 423: # ifdef FLY 424: case FLYER: 425: # endif FLY 426: if (dx < 0) 427: dx = -dx; 428: if (absdy > dx) 429: damage = delta - absdy + 1; 430: else 431: damage = delta - dx + 1; 432: pp = play_at(y, x); 433: while (damage-- > 0) 434: checkdam(pp, bp->b_owner, bp->b_score, 435: MINDAM, bp->b_type); 436: break; 437: case GMINE: 438: case MINE: 439: add_shot((Maze[y][x] == GMINE) ? 440: GRENADE : SHOT, 441: y, x, LEFTS, 442: (Maze[y][x] == GMINE) ? 443: GRENREQ : BULREQ, 444: (PLAYER *) NULL, TRUE, SPACE); 445: Maze[y][x] = SPACE; 446: break; 447: } 448: } 449: } 450: } 451: 452: # ifdef OOZE 453: /* 454: * chkslime: 455: * handle slime shot exploding 456: */ 457: chkslime(bp) 458: register BULLET *bp; 459: { 460: register BULLET *nbp; 461: 462: switch (Maze[bp->b_y][bp->b_x]) { 463: case WALL1: 464: case WALL2: 465: case WALL3: 466: # ifdef REFLECT 467: case WALL4: 468: case WALL5: 469: # endif REFLECT 470: # ifdef RANDOM 471: case DOOR: 472: # endif RANDOM 473: switch (bp->b_face) { 474: case LEFTS: 475: bp->b_x++; 476: break; 477: case RIGHT: 478: bp->b_x--; 479: break; 480: case ABOVE: 481: bp->b_y++; 482: break; 483: case BELOW: 484: bp->b_y--; 485: break; 486: } 487: break; 488: } 489: nbp = (BULLET *) malloc(sizeof (BULLET)); 490: *nbp = *bp; 491: # ifdef VOLCANO 492: moveslime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED); 493: # else VOLCANO 494: moveslime(nbp, SLIMESPEED); 495: # endif VOLCANO 496: } 497: 498: /* 499: * moveslime: 500: * move the given slime shot speed times and add it back if 501: * it hasn't fizzled yet 502: */ 503: moveslime(bp, speed) 504: register BULLET *bp; 505: register int speed; 506: { 507: register int i, j, dirmask, count; 508: register PLAYER *pp; 509: register BULLET *nbp; 510: 511: if (speed == 0) { 512: if (bp->b_charge <= 0) 513: free((char *) bp); 514: else 515: save_bullet(bp); 516: return; 517: } 518: 519: # ifdef VOLCANO 520: showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*'); 521: # else VOLCANO 522: showexpl(bp->b_y, bp->b_x, '*'); 523: # endif VOLCANO 524: switch (Maze[bp->b_y][bp->b_x]) { 525: case LEFTS: 526: case RIGHT: 527: case ABOVE: 528: case BELOW: 529: # ifdef FLY 530: case FLYER: 531: # endif FLY 532: pp = play_at(bp->b_y, bp->b_x); 533: message(pp, "You've been slimed."); 534: checkdam(pp, bp->b_owner, bp->b_score, MINDAM, bp->b_type); 535: break; 536: } 537: 538: if (--bp->b_charge <= 0) { 539: free((char *) bp); 540: return; 541: } 542: 543: dirmask = 0; 544: count = 0; 545: switch (bp->b_face) { 546: case LEFTS: 547: if (!iswall(bp->b_y, bp->b_x - 1)) 548: dirmask |= WEST, count++; 549: if (!iswall(bp->b_y - 1, bp->b_x)) 550: dirmask |= NORTH, count++; 551: if (!iswall(bp->b_y + 1, bp->b_x)) 552: dirmask |= SOUTH, count++; 553: if (dirmask == 0) 554: if (!iswall(bp->b_y, bp->b_x + 1)) 555: dirmask |= EAST, count++; 556: break; 557: case RIGHT: 558: if (!iswall(bp->b_y, bp->b_x + 1)) 559: dirmask |= EAST, count++; 560: if (!iswall(bp->b_y - 1, bp->b_x)) 561: dirmask |= NORTH, count++; 562: if (!iswall(bp->b_y + 1, bp->b_x)) 563: dirmask |= SOUTH, count++; 564: if (dirmask == 0) 565: if (!iswall(bp->b_y, bp->b_x - 1)) 566: dirmask |= WEST, count++; 567: break; 568: case ABOVE: 569: if (!iswall(bp->b_y - 1, bp->b_x)) 570: dirmask |= NORTH, count++; 571: if (!iswall(bp->b_y, bp->b_x - 1)) 572: dirmask |= WEST, count++; 573: if (!iswall(bp->b_y, bp->b_x + 1)) 574: dirmask |= EAST, count++; 575: if (dirmask == 0) 576: if (!iswall(bp->b_y + 1, bp->b_x)) 577: dirmask |= SOUTH, count++; 578: break; 579: case BELOW: 580: if (!iswall(bp->b_y + 1, bp->b_x)) 581: dirmask |= SOUTH, count++; 582: if (!iswall(bp->b_y, bp->b_x - 1)) 583: dirmask |= WEST, count++; 584: if (!iswall(bp->b_y, bp->b_x + 1)) 585: dirmask |= EAST, count++; 586: if (dirmask == 0) 587: if (!iswall(bp->b_y - 1, bp->b_x)) 588: dirmask |= NORTH, count++; 589: break; 590: } 591: if (count == 0) { 592: /* 593: * No place to go. Just sit here for a while and wait 594: * for adjacent squares to clear out. 595: */ 596: save_bullet(bp); 597: return; 598: } 599: if (bp->b_charge < count) { 600: /* Only bp->b_charge paths may be taken */ 601: while (count > bp->b_charge) { 602: if (dirmask & WEST) 603: dirmask &= ~WEST; 604: else if (dirmask & EAST) 605: dirmask &= ~EAST; 606: else if (dirmask & NORTH) 607: dirmask &= ~NORTH; 608: else if (dirmask & SOUTH) 609: dirmask &= ~SOUTH; 610: count--; 611: } 612: } 613: 614: i = bp->b_charge / count; 615: j = bp->b_charge % count; 616: if (dirmask & WEST) { 617: count--; 618: nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS, 619: i, bp->b_owner, bp->b_score, TRUE, SPACE); 620: moveslime(nbp, speed - 1); 621: } 622: if (dirmask & EAST) { 623: count--; 624: nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT, 625: (count < j) ? i + 1 : i, bp->b_owner, bp->b_score, 626: TRUE, SPACE); 627: moveslime(nbp, speed - 1); 628: } 629: if (dirmask & NORTH) { 630: count--; 631: nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE, 632: (count < j) ? i + 1 : i, bp->b_owner, bp->b_score, 633: TRUE, SPACE); 634: moveslime(nbp, speed - 1); 635: } 636: if (dirmask & SOUTH) { 637: count--; 638: nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW, 639: (count < j) ? i + 1 : i, bp->b_owner, bp->b_score, 640: TRUE, SPACE); 641: moveslime(nbp, speed - 1); 642: } 643: 644: free((char *) bp); 645: } 646: 647: /* 648: * iswall: 649: * returns whether the given location is a wall 650: */ 651: iswall(y, x) 652: register int y, x; 653: { 654: if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH) 655: return TRUE; 656: switch (Maze[y][x]) { 657: case WALL1: 658: case WALL2: 659: case WALL3: 660: # ifdef REFLECT 661: case WALL4: 662: case WALL5: 663: # endif REFLECT 664: # ifdef RANDOM 665: case DOOR: 666: # endif RANDOM 667: # ifdef VOLCANO 668: case LAVA: 669: # endif VOLCANO 670: return TRUE; 671: } 672: return FALSE; 673: } 674: # endif OOZE 675: 676: /* 677: * zapshot: 678: * Take a shot out of the air. 679: */ 680: zapshot(blist, obp) 681: register BULLET *blist, *obp; 682: { 683: register BULLET *bp; 684: register FLAG explode; 685: 686: explode = FALSE; 687: for (bp = blist; bp != NULL; bp = bp->b_next) { 688: if (bp->b_x != obp->b_x || bp->b_y != obp->b_y) 689: continue; 690: if (bp->b_face == obp->b_face) 691: continue; 692: explode = TRUE; 693: break; 694: } 695: if (!explode) 696: return; 697: explshot(blist, obp->b_y, obp->b_x); 698: } 699: 700: /* 701: * explshot - 702: * Make all shots at this location blow up 703: */ 704: explshot(blist, y, x) 705: register BULLET *blist; 706: register int y, x; 707: { 708: register BULLET *bp; 709: 710: for (bp = blist; bp != NULL; bp = bp->b_next) 711: if (bp->b_x == x && bp->b_y == y) { 712: bp->b_expl = TRUE; 713: if (bp->b_owner != NULL) 714: message(bp->b_owner, "Shot intercepted"); 715: } 716: } 717: 718: /* 719: * play_at: 720: * Return a pointer to the player at the given location 721: */ 722: PLAYER * 723: play_at(y, x) 724: register int y, x; 725: { 726: register PLAYER *pp; 727: 728: for (pp = Player; pp < End_player; pp++) 729: if (pp->p_x == x && pp->p_y == y) 730: return pp; 731: fprintf(stderr, "driver: couldn't find player at (%d,%d)\n", x, y); 732: abort(); 733: /* NOTREACHED */ 734: } 735: 736: /* 737: * opposite: 738: * Return TRUE if the bullet direction faces the opposite direction 739: * of the player in the maze 740: */ 741: opposite(face, dir) 742: int face; 743: char dir; 744: { 745: switch (face) { 746: case LEFTS: 747: return (dir == RIGHT); 748: case RIGHT: 749: return (dir == LEFTS); 750: case ABOVE: 751: return (dir == BELOW); 752: case BELOW: 753: return (dir == ABOVE); 754: default: 755: return FALSE; 756: } 757: } 758: 759: /* 760: * is_bullet: 761: * Is there a bullet at the given coordinates? If so, return 762: * a pointer to the bullet, otherwise return NULL 763: */ 764: BULLET * 765: is_bullet(y, x) 766: register int y, x; 767: { 768: register BULLET *bp; 769: 770: for (bp = Bullets; bp != NULL; bp = bp->b_next) 771: if (bp->b_y == y && bp->b_x == x) 772: return bp; 773: return NULL; 774: } 775: 776: /* 777: * fixshots: 778: * change the underlying character of the shots at a location 779: * to the given character. 780: */ 781: fixshots(y, x, over) 782: register int y, x; 783: char over; 784: { 785: register BULLET *bp; 786: 787: for (bp = Bullets; bp != NULL; bp = bp->b_next) 788: if (bp->b_y == y && bp->b_x == x) 789: bp->b_over = over; 790: } 791: 792: /* 793: * find_under: 794: * find the underlying character for a bullet when it lands 795: * on another bullet. 796: */ 797: find_under(blist, bp) 798: register BULLET *blist, *bp; 799: { 800: register BULLET *nbp; 801: 802: for (nbp = blist; nbp != NULL; nbp = nbp->b_next) 803: if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) { 804: bp->b_over = nbp->b_over; 805: break; 806: } 807: } 808: 809: /* 810: * mark_player: 811: * mark a player as under a shot 812: */ 813: mark_player(bp) 814: register BULLET *bp; 815: { 816: register PLAYER *pp; 817: 818: for (pp = Player; pp < End_player; pp++) 819: if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) { 820: pp->p_undershot = TRUE; 821: break; 822: } 823: }