Annotation of early-roguelike/urogue/chase.c, Revision 1.1
1.1 ! rubenllo 1: /*
! 2: chase.c - Code for one creature to chase another
! 3:
! 4: UltraRogue: The Ultimate Adventure in the Dungeons of Doom
! 5: Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
! 6: All rights reserved.
! 7:
! 8: Based on "Advanced Rogue"
! 9: Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
! 10: All rights reserved.
! 11:
! 12: Based on "Rogue: Exploring the Dungeons of Doom"
! 13: Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
! 14: All rights reserved.
! 15:
! 16: See the file LICENSE.TXT for full copyright and licensing information.
! 17: */
! 18:
! 19: #include <stdlib.h>
! 20: #include <ctype.h>
! 21: #include <limits.h>
! 22: #include "rogue.h"
! 23:
! 24: /*
! 25: do_chase()
! 26: Make one thing chase another.
! 27: */
! 28:
! 29: void
! 30: do_chase(struct thing *th, int flee)
! 31: {
! 32: struct room *rer; /* room of chaser */
! 33: struct room *ree; /* room of chasee */
! 34: struct room *old_room; /* old room of monster */
! 35: struct room *new_room; /* new room of monster */
! 36:
! 37: int i, mindist = INT_MAX, maxdist = INT_MIN, dist = INT_MIN;
! 38:
! 39: int last_door = -1; /* Door we just came from */
! 40: int stoprun = FALSE; /* TRUE means we are there */
! 41: int rundoor; /* TRUE means run to a door */
! 42: int hit_bad = FALSE; /* TRUE means hit bad monster */
! 43: int mon_attack; /* TRUE means find a monster to hit */
! 44:
! 45: char sch;
! 46: struct linked_list *item;
! 47: coord this; /* Temporary destination for chaser */
! 48:
! 49: if (!th->t_ischasing)
! 50: return;
! 51:
! 52: /* Make sure the monster can move */
! 53:
! 54: if (th->t_no_move != 0)
! 55: {
! 56: th->t_no_move--;
! 57: return;
! 58: }
! 59:
! 60: /*
! 61: * Bad monsters check for a good monster to hit, friendly monsters
! 62: * check for a bad monster to hit.
! 63: */
! 64:
! 65: mon_attack = FALSE;
! 66:
! 67: if (good_monster(*th))
! 68: {
! 69: hit_bad = TRUE;
! 70: mon_attack = TRUE;
! 71: }
! 72: else if (on(*th, ISMEAN))
! 73: {
! 74: hit_bad = FALSE;
! 75: mon_attack = TRUE;
! 76: }
! 77:
! 78: if (mon_attack)
! 79: {
! 80: struct linked_list *mon_to_hit;
! 81:
! 82: mon_to_hit = f_mons_a(th->t_pos.y, th->t_pos.x, hit_bad);
! 83:
! 84: if (mon_to_hit)
! 85: {
! 86: mon_mon_attack(th, mon_to_hit, pick_weap(th), NOTHROWN);
! 87: return;
! 88: }
! 89: }
! 90:
! 91: /* no nearby monster to hit */
! 92:
! 93: rer = roomin(th->t_pos); /* Find room of chaser */
! 94: ree = roomin(th->t_chasee->t_pos); /* Find room of chasee */
! 95:
! 96: /*
! 97: * We don't count doors as inside rooms for this routine
! 98: */
! 99:
! 100: if (mvwinch(stdscr, th->t_pos.y, th->t_pos.x) == DOOR)
! 101: rer = NULL;
! 102:
! 103: this = th->t_chasee->t_pos;
! 104:
! 105: /*
! 106: * If we are not in a corridor and not a phasing monster, then if we
! 107: * are running after the player, we run to a door if he is not in the
! 108: * same room. If we are fleeing, we run to a door if he IS in the
! 109: * same room. Note: We don't bother with doors in mazes. Phasing
! 110: * monsters don't need to look for doors. There are no doors in mazes
! 111: * and throne rooms.
! 112: */
! 113:
! 114: if (levtype != MAZELEV && levtype != THRONE && rer != NULL && off(*th, CANINWALL))
! 115: {
! 116: if (flee)
! 117: rundoor = (rer == ree);
! 118: else
! 119: rundoor = (rer != ree);
! 120: }
! 121: else
! 122: rundoor = FALSE;
! 123:
! 124: if (rundoor)
! 125: {
! 126: coord d_exit; /* A particular door */
! 127: int exity, exitx; /* Door's coordinates */
! 128:
! 129: if (th->t_doorgoal != -1)
! 130: { /* Do we already have the goal? */
! 131: this = rer->r_exit[th->t_doorgoal];
! 132: dist = 0; /* Indicate that we have our door */
! 133: }
! 134: else
! 135: for (i = 0; i < rer->r_nexits; i++)
! 136: { /* Loop through doors */
! 137: d_exit = rer->r_exit[i];
! 138: exity = d_exit.y;
! 139: exitx = d_exit.x;
! 140:
! 141: /* Avoid secret doors */
! 142: if (mvwinch(stdscr, exity, exitx) == DOOR)
! 143: {
! 144: /* Were we just on this door? */
! 145: if (ce(d_exit, th->t_oldpos))
! 146: last_door = i;
! 147: else
! 148: {
! 149: dist = DISTANCE(th->t_chasee->t_pos, d_exit);
! 150:
! 151: /*
! 152: * If fleeing, we want to
! 153: * maximize distance from
! 154: * door to what we flee, and
! 155: * minimize distance from
! 156: * door to us.
! 157: */
! 158:
! 159: if (flee)
! 160: dist-=DISTANCE(th->t_pos,d_exit);
! 161:
! 162: /*
! 163: * Maximize distance if
! 164: * fleeing, otherwise
! 165: * minimize it
! 166: */
! 167:
! 168: if ((flee && (dist > maxdist)) ||
! 169: (!flee && (dist < mindist)))
! 170: {
! 171: th->t_doorgoal = i; /* Use this door */
! 172: this = d_exit;
! 173: mindist = maxdist = dist;
! 174: }
! 175: }
! 176: }
! 177: }
! 178:
! 179: /* Could we not find a door? */
! 180: if (dist == INT_MIN)
! 181: {
! 182: /* If we were on a door, go ahead and use it */
! 183: if (last_door != -1)
! 184: {
! 185: th->t_doorgoal = last_door;
! 186: this = th->t_oldpos;
! 187: dist = 0; /* Indicate that we found a door */
! 188: }
! 189: }
! 190:
! 191: /* Indicate that we do not want to flee from the door */
! 192: if (dist != INT_MIN)
! 193: flee = FALSE;
! 194: }
! 195: else
! 196: th->t_doorgoal = -1; /* Not going to any door */
! 197:
! 198: /*
! 199: * this now contains what we want to run to this time so we run to
! 200: * it. If we hit it we either want to fight it or stop running
! 201: */
! 202:
! 203: if (!chase(th, &this, flee))
! 204: {
! 205: if (ce(th->t_nxtpos, hero))
! 206: {
! 207: /* merchants try to sell something */
! 208:
! 209: if (on(*th, CANSELL))
! 210: {
! 211: sell(th);
! 212: return;
! 213: }
! 214: else if (off(*th, ISFRIENDLY) && off(*th, ISCHARMED)
! 215: && (off(*th, CANFLY) || (on(*th, CANFLY) && rnd(2))))
! 216: attack(th, pick_weap(th), FALSE);
! 217: return;
! 218: }
! 219: else if (on(*th, NOMOVE))
! 220: stoprun = TRUE;
! 221: }
! 222:
! 223: if (!curr_mons)
! 224: return; /* Did monster get itself killed? */
! 225:
! 226: if (on(*th, NOMOVE))
! 227: return;
! 228:
! 229: /* If we have a scavenger, it can pick something up */
! 230:
! 231: if ((item = find_obj(th->t_nxtpos.y, th->t_nxtpos.x)) != NULL)
! 232: {
! 233: struct linked_list *node, *top = item;
! 234: struct object *obt;
! 235:
! 236: while(top)
! 237: {
! 238: /* grab all objects that qualify */
! 239:
! 240: struct object *obj = OBJPTR(item);
! 241:
! 242: obt = OBJPTR(top);
! 243: node = obt->next_obj;
! 244:
! 245: if (on(*th, ISSCAVENGE) ||
! 246: ((on(*th, CANWIELD) || on(*th, CANSHOOT)) &&
! 247: (obj->o_type == WEAPON || obj->o_type == ARMOR)) ||
! 248: (on(*th, CANCAST) && is_magic(obj)))
! 249: {
! 250: rem_obj(top, FALSE);
! 251: attach(th->t_pack, top);
! 252: }
! 253:
! 254: top = node;
! 255: }
! 256:
! 257: light(&hero);
! 258: }
! 259:
! 260: mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch);
! 261: sch = CCHAR( mvwinch(cw, th->t_nxtpos.y, th->t_nxtpos.x) );
! 262:
! 263: /* Get old and new room of monster */
! 264: old_room = roomin(th->t_pos);
! 265: new_room = roomin(th->t_nxtpos);
! 266:
! 267: /* If the monster can illuminate rooms, check for a change */
! 268: if (on(*th, HASFIRE))
! 269: {
! 270: /* Is monster entering a room? */
! 271: if (old_room != new_room && new_room != NULL)
! 272: {
! 273: new_room->r_flags |= HASFIRE;
! 274: new_room->r_fires++;
! 275: if (cansee(th->t_nxtpos.y, th->t_nxtpos.x) && new_room->r_fires==1)
! 276: light(&hero);
! 277: }
! 278:
! 279: /* Is monster leaving a room? */
! 280: if (old_room != new_room && old_room != NULL)
! 281: {
! 282: if (--(old_room->r_fires) <= 0)
! 283: {
! 284: old_room->r_flags &= ~HASFIRE;
! 285: if (cansee(th->t_pos.y, th->t_pos.x))
! 286: light(&th->t_pos);
! 287: }
! 288: }
! 289: }
! 290:
! 291: /*
! 292: * If monster is entering player's room and player can see it, stop
! 293: * the player's running.
! 294: */
! 295:
! 296: if (new_room != old_room && new_room != NULL &&
! 297: new_room == ree && cansee(th->t_nxtpos.y, th->t_nxtpos.x) &&
! 298: (off(*th, ISINVIS) || (off(*th, ISSHADOW) || rnd(10) == 0) ||
! 299: on(player, CANSEE)) && off(*th, CANSURPRISE))
! 300: running = FALSE;
! 301:
! 302: if (rer != NULL && (rer->r_flags & ISDARK) &&
! 303: !(rer->r_flags & HASFIRE) && sch == FLOOR &&
! 304: DISTANCE(th->t_nxtpos, th->t_pos) < see_dist &&
! 305: off(player, ISBLIND))
! 306: th->t_oldch = ' ';
! 307: else
! 308: th->t_oldch = sch;
! 309:
! 310: if (cansee(th->t_nxtpos.y, th->t_nxtpos.x) &&
! 311: off(*th, ISINWALL) &&
! 312: ((off(*th, ISINVIS) && (off(*th, ISSHADOW) || rnd(100) < 10)) ||
! 313: on(player, CANSEE)) &&
! 314: off(*th, CANSURPRISE))
! 315: mvwaddch(cw, th->t_nxtpos.y, th->t_nxtpos.x, th->t_type);
! 316:
! 317: mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' ');
! 318: mvwaddch(mw, th->t_nxtpos.y, th->t_nxtpos.x, th->t_type);
! 319:
! 320: /* Record monster's last position (if new one is different) */
! 321:
! 322: if (!ce(th->t_nxtpos, th->t_pos))
! 323: th->t_oldpos = th->t_pos;
! 324:
! 325: th->t_pos = th->t_nxtpos; /* Mark the monster's new position */
! 326:
! 327: /* If the monster is on a trap, trap it */
! 328:
! 329: sch = CCHAR(mvinch(th->t_nxtpos.y, th->t_nxtpos.x));
! 330:
! 331: if (isatrap(sch))
! 332: {
! 333: debug("Monster trapped by %c.", sch);
! 334:
! 335: if (cansee(th->t_nxtpos.y, th->t_nxtpos.x))
! 336: th->t_oldch = sch;
! 337:
! 338: be_trapped(th, th->t_nxtpos);
! 339: }
! 340:
! 341: /* And stop running if need be */
! 342:
! 343: if (stoprun && ce(th->t_pos, th->t_chasee->t_pos))
! 344: {
! 345: th->t_ischasing = FALSE;
! 346: turn_off(*th, ISRUN);
! 347: }
! 348: }
! 349:
! 350: /*
! 351: chase_it()
! 352: Set a monster running after something or stop it from running (for
! 353: when it dies)
! 354: */
! 355:
! 356: void
! 357: chase_it(coord *runner, struct thing *th)
! 358: {
! 359: struct linked_list *item;
! 360: struct thing *tp;
! 361:
! 362: /* If we couldn't find him, something is funny */
! 363:
! 364: if ((item = find_mons(runner->y, runner->x)) == NULL)
! 365: {
! 366: debug("CHASER '%s'", unctrl(winat(runner->y, runner->x)));
! 367: return;
! 368: }
! 369:
! 370: tp = THINGPTR(item);
! 371:
! 372: /* Start the beastie running */
! 373:
! 374: tp->t_ischasing = TRUE;
! 375: tp->t_chasee = th;
! 376:
! 377: turn_on(*tp, ISRUN);
! 378: turn_off(*tp, ISDISGUISE);
! 379:
! 380: return;
! 381: }
! 382:
! 383: /*
! 384: chase()
! 385: Find the spot for the chaser(er) to move closer to the chasee(ee).
! 386: Returns TRUE if we want to keep on chasing later, FALSE if we reach the
! 387: goal.
! 388: */
! 389:
! 390: int
! 391: chase(struct thing *tp, coord *ee, int flee)
! 392: {
! 393: int x, y;
! 394: int dist, thisdist, monst_dist = INT_MAX;
! 395: struct linked_list *weapon;
! 396: coord *er = &tp->t_pos;
! 397: coord shoot;
! 398: coord *shootit_dir = NULL;
! 399: int ch;
! 400: char mch;
! 401: int next_player = FALSE;
! 402:
! 403: /* Take care of shooting directions */
! 404:
! 405: if (on(*tp, CANBREATHE) || on(*tp, CANSHOOT) || on(*tp, CANCAST))
! 406: {
! 407: if (good_monster(*tp))
! 408: {
! 409: shootit_dir = find_shoot(tp, &shoot); /* find a mean monster */
! 410:
! 411: if (wizard && shootit_dir)
! 412: msg("Found monster to attack towards (%d,%d).",
! 413: shootit_dir->x, shootit_dir->y);
! 414: }
! 415: else
! 416: shootit_dir = can_shoot(er, ee, &shoot); /* shoot hero */
! 417: }
! 418:
! 419: /*
! 420: * If the thing is confused, let it move randomly. Some monsters are
! 421: * slightly confused all of the time.
! 422: */
! 423:
! 424: if ((on(*tp, ISHUH) && rnd(10) < 8) ||
! 425: ((on(*tp, ISINVIS) || on(*tp, ISSHADOW)) && rnd(100) < 20) ||
! 426: (on(player, ISINVIS) && off(*tp, CANSEE)))
! 427: { /* Player is invisible */
! 428:
! 429: /* get a valid random move */
! 430:
! 431: tp->t_nxtpos = rndmove(tp);
! 432:
! 433: dist = DISTANCE(tp->t_nxtpos, *ee);
! 434:
! 435: if (on(*tp, ISHUH) && rnd(20) == 0) /* monster might lose confusion */
! 436: turn_off(*tp, ISHUH);
! 437:
! 438: /*
! 439: * check to see if random move takes creature away from
! 440: * player if it does then turn off ISHELD
! 441: */
! 442:
! 443: if (dist > 1 && on(*tp, DIDHOLD))
! 444: {
! 445: turn_off(*tp, DIDHOLD);
! 446: turn_on(*tp, CANHOLD);
! 447:
! 448: if (--hold_count == 0)
! 449: turn_off(player, ISHELD);
! 450: }
! 451: } /* If we can breathe, we may do so */
! 452: else if (on(*tp, CANBREATHE) && (shootit_dir) && (rnd(100) < 67) &&
! 453: (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)) &&
! 454: (DISTANCE(*er, *ee) < BOLT_LENGTH * BOLT_LENGTH))
! 455: {
! 456: int chance;
! 457: char *breath;
! 458:
! 459: /* Will it breathe at random */
! 460:
! 461: if (on(*tp, CANBRANDOM))
! 462: {
! 463: if (rnd(level / 20) == 0 && tp->t_index != nummonst + 1
! 464: && !(good_monster(*tp)))
! 465: turn_off(*tp, CANBRANDOM);
! 466:
! 467: /* Select type of breath */
! 468:
! 469: chance = rnd(100);
! 470:
! 471: if (chance < 11)
! 472: breath = "acid";
! 473: else if (chance < 22)
! 474: breath = "flame";
! 475: else if (chance < 33)
! 476: breath = "lightning bolt";
! 477: else if (chance < 44)
! 478: breath = "chlorine gas";
! 479: else if (chance < 55)
! 480: breath = "ice";
! 481: else if (chance < 66)
! 482: breath = "nerve gas";
! 483: else if (chance < 77)
! 484: breath = "sleeping gas";
! 485: else if (chance < 88)
! 486: breath = "slow gas";
! 487: else
! 488: breath = "fear gas";
! 489: } /* Or can it breathe acid? */
! 490: else if (on(*tp, CANBACID))
! 491: {
! 492: if (!good_monster(*tp) && rnd(level / 15) == 0)
! 493: turn_off(*tp, CANBACID);
! 494:
! 495: breath = "acid";
! 496: } /* Or can it breathe fire */
! 497: else if (on(*tp, CANBFIRE))
! 498: {
! 499: if (!good_monster(*tp) && rnd(level / 15) == 0)
! 500: turn_off(*tp, CANBFIRE);
! 501:
! 502: breath = "flame";
! 503: } /* Or can it breathe electricity? */
! 504: else if (on(*tp, CANBBOLT))
! 505: {
! 506: if (!good_monster(*tp) && rnd(level / 15) == 0)
! 507: turn_off(*tp, CANBBOLT);
! 508:
! 509: breath = "lightning bolt";
! 510: } /* Or can it breathe gas? */
! 511: else if (on(*tp, CANBGAS))
! 512: {
! 513: if (!good_monster(*tp) && rnd(level / 15) == 0)
! 514: turn_off(*tp, CANBGAS);
! 515:
! 516: breath = "chlorine gas";
! 517: } /* Or can it breathe ice? */
! 518: else if (on(*tp, CANBICE))
! 519: {
! 520: if (!good_monster(*tp) && rnd(level / 15) == 0)
! 521: turn_off(*tp, CANBICE);
! 522:
! 523: breath = "ice";
! 524: }
! 525: else if (on(*tp, CANBPGAS))
! 526: {
! 527: if (!good_monster(*tp) && rnd(level / 15) == 0)
! 528: turn_off(*tp, CANBPGAS);
! 529:
! 530: breath = "nerve gas";
! 531: }
! 532: else if (on(*tp, CANBSGAS))
! 533: {
! 534: if (!good_monster(*tp) && rnd(level / 15) == 0)
! 535: turn_off(*tp, CANBSGAS);
! 536:
! 537: breath = "sleeping gas";
! 538: }
! 539: else if (on(*tp, CANBSLGAS))
! 540: {
! 541: if (!good_monster(*tp) && rnd(level / 15) == 0)
! 542: turn_off(*tp, CANBSLGAS);
! 543:
! 544: breath = "slow gas";
! 545: }
! 546: else
! 547: {
! 548: if (!good_monster(*tp) && rnd(level / 15) == 0)
! 549: turn_off(*tp, CANBFGAS);
! 550:
! 551: breath = "fear gas";
! 552: }
! 553:
! 554: shoot_bolt(tp, *er, *shootit_dir, (tp == THINGPTR(fam_ptr)),
! 555: tp->t_index, breath, roll(tp->t_stats.s_lvl, 6));
! 556:
! 557: tp->t_nxtpos = *er;
! 558:
! 559: dist = DISTANCE(tp->t_nxtpos, *ee);
! 560:
! 561: if (!curr_mons)
! 562: return (TRUE);
! 563: }
! 564: else if (shootit_dir && on(*tp, CANCAST) &&
! 565: (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)))
! 566: {
! 567: /*
! 568: If we can cast spells we might do so - even if adjacent fleeing
! 569: monsters are restricted to certain spells
! 570: */
! 571:
! 572: incant(tp, *shootit_dir);
! 573: tp->t_nxtpos = *er;
! 574: dist = DISTANCE(tp->t_nxtpos, *ee);
! 575: }
! 576: else if (shootit_dir && on(*tp, CANSHOOT))
! 577: {
! 578: weapon = get_hurl(tp);
! 579:
! 580: if (weapon &&
! 581: (off(*tp, ISFLEE) || rnd(DISTANCE(*er, *ee)) > 2) &&
! 582: (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)))
! 583: {
! 584: /*
! 585: Should we shoot or throw something? fleeing monsters
! 586: may to shoot anyway if far enough away
! 587: */
! 588:
! 589: missile(shootit_dir->y, shootit_dir->x, weapon, tp);
! 590: tp->t_nxtpos = *er;
! 591: dist = DISTANCE(tp->t_nxtpos, *ee);
! 592: }
! 593: }
! 594: else
! 595: {
! 596: /*
! 597: Otherwise, find the empty spot next to the chaser that is closest
! 598: to the chasee.
! 599: */
! 600: int ey, ex;
! 601: struct room *rer, *ree;
! 602: int dist_to_old = INT_MIN; /* Dist from goal to old position */
! 603:
! 604: /* Get rooms */
! 605: rer = roomin(*er);
! 606: ree = roomin(*ee);
! 607:
! 608: /*
! 609: * This will eventually hold where we move to get closer. If
! 610: * we can't find an empty spot, we stay where we are.
! 611: */
! 612:
! 613: dist = flee ? 0 : INT_MAX;
! 614: tp->t_nxtpos = *er;
! 615:
! 616: /* Are we at our goal already? */
! 617:
! 618: if (!flee && ce(tp->t_nxtpos, *ee))
! 619: return (FALSE);
! 620:
! 621: ey = er->y + 1;
! 622: ex = er->x + 1;
! 623:
! 624: for (x = er->x - 1; x <= ex; x++)
! 625: for (y = er->y - 1; y <= ey; y++)
! 626: {
! 627: coord tryp; /* test position */
! 628:
! 629: /* Don't try off the screen */
! 630:
! 631: if ((x < 0) || (x >= COLS) || (y < 1) || (y >= LINES - 2))
! 632: continue;
! 633:
! 634: /*
! 635: * Don't try the player if not going after
! 636: * the player or he's disguised and monster is dumb
! 637: */
! 638:
! 639: if (((off(*tp, ISFLEE) && !ce(hero, *ee)) ||
! 640: (on(player, ISDISGUISE) && (rnd(tp->t_stats.s_lvl) < 6))
! 641: || good_monster(*tp))
! 642: && x == hero.x && y == hero.y)
! 643: continue;
! 644:
! 645: tryp.x = x;
! 646: tryp.y = y;
! 647:
! 648: /*
! 649: * Is there a monster on this spot closer to
! 650: * our goal? Don't look in our spot or where
! 651: * we were.
! 652: */
! 653:
! 654: if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) &&
! 655: isalpha( (mch = CCHAR(mvwinch(mw, y, x))) ) )
! 656: {
! 657: int test_dist;
! 658:
! 659: test_dist = DISTANCE(tryp,*ee);
! 660: if (test_dist <= 25 && /* Let's be fairly close */
! 661: test_dist < monst_dist)
! 662: {
! 663:
! 664: /* Could we really move there? */
! 665:
! 666: mvwaddch(mw, y, x, ' '); /* Temp blank monst */
! 667:
! 668: if (diag_ok(er, &tryp, tp))
! 669: monst_dist = test_dist;
! 670:
! 671: mvwaddch(mw, y, x, mch); /* Restore monster */
! 672: }
! 673: }
! 674:
! 675: if (!diag_ok(er, &tryp, tp))
! 676: continue;
! 677:
! 678: ch = mvwinch(cw, y, x); /* Screen character */
! 679:
! 680: /*
! 681: * Stepping on player is NOT okay if we are
! 682: * fleeing
! 683: */
! 684:
! 685: if (on(*tp, ISFLEE) && (ch == PLAYER))
! 686: next_player = TRUE;
! 687:
! 688: if (step_ok(y, x, NOMONST, tp) &&
! 689: (off(*tp, ISFLEE) || ch != PLAYER))
! 690: {
! 691:
! 692: /*
! 693: * If it is a trap, an intelligent
! 694: * monster may not step on it (unless
! 695: * our hero is on top!)
! 696: */
! 697:
! 698: if (isatrap(ch))
! 699: {
! 700: if (!(ch == RUSTTRAP) &&
! 701: !(ch == FIRETRAP && on(*tp, NOFIRE)) &&
! 702: rnd(10) < tp->t_stats.s_intel &&
! 703: (y != hero.y || x != hero.x))
! 704: continue;
! 705: }
! 706:
! 707: /*
! 708: * OK -- this place counts
! 709: */
! 710:
! 711: thisdist = DISTANCE(tryp, *ee);
! 712:
! 713: /*
! 714: * Adjust distance if we are being
! 715: * shot at to moving out of line of sight.
! 716: */
! 717:
! 718: if (tp->t_wasshot && tp->t_stats.s_intel > 5 &&
! 719: ce(hero, *ee))
! 720: {
! 721: /* Move out of line of sight */
! 722: if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL))
! 723: {
! 724: if (flee)
! 725: thisdist -= SHOTPENALTY;
! 726: else
! 727: thisdist += SHOTPENALTY;
! 728: }
! 729:
! 730: /*
! 731: * But do we want to leave
! 732: * the room?
! 733: */
! 734: else if (rer && rer == ree && ch == DOOR)
! 735: thisdist += DOORPENALTY;
! 736: }
! 737:
! 738: /*
! 739: * Don't move to the last position if
! 740: * we can help it
! 741: */
! 742:
! 743: if (ce(tryp, tp->t_oldpos))
! 744: dist_to_old = thisdist;
! 745: else if ((flee && (thisdist > dist)) ||
! 746: (!flee && (thisdist < dist)))
! 747: {
! 748: tp->t_nxtpos = tryp;
! 749: dist = thisdist;
! 750: }
! 751: }
! 752: }
! 753:
! 754: /*
! 755: * If we are running from the player and he is in our way, go
! 756: * ahead and slug him.
! 757: */
! 758:
! 759: if (next_player && DISTANCE(*er,*ee) < dist &&
! 760: step_ok(tp->t_chasee->t_pos.y, tp->t_chasee->t_pos.x, NOMONST, tp))
! 761: {
! 762: tp->t_nxtpos = tp->t_chasee->t_pos; /* Okay to hit player */
! 763: return(FALSE);
! 764: }
! 765:
! 766:
! 767: /*
! 768: * If we can't get closer to the player (if that's our goal)
! 769: * because other monsters are in the way, just stay put
! 770: */
! 771:
! 772: if (!flee && ce(hero, *ee) && monst_dist < INT_MAX &&
! 773: DISTANCE(*er, hero) < dist)
! 774: tp->t_nxtpos = *er;
! 775:
! 776: /* Do we want to go back to the last position? */
! 777: else if (dist_to_old != INT_MIN && /* It is possible to move back */
! 778: ((flee && dist == 0) || /* No other possible moves */
! 779: (!flee && dist == INT_MAX)))
! 780: {
! 781: /* Do we move back or just stay put (default)? */
! 782:
! 783: dist = DISTANCE(*er,*ee); /* Current distance */
! 784:
! 785: if (!flee || (flee && (dist_to_old > dist)))
! 786: tp->t_nxtpos = tp->t_oldpos;
! 787: }
! 788: }
! 789:
! 790: /* Make sure we have the real distance now */
! 791: dist = DISTANCE(tp->t_nxtpos, *ee);
! 792:
! 793: /* Mark monsters in a wall */
! 794:
! 795: switch(mvinch(tp->t_nxtpos.y, tp->t_nxtpos.x))
! 796: {
! 797: case WALL:
! 798: case '-':
! 799: case '|':
! 800: turn_on(*tp, ISINWALL);
! 801: break;
! 802: default:
! 803: turn_off(*tp, ISINWALL);
! 804: }
! 805:
! 806: if (off(*tp, ISFLEE) &&
! 807: !(!SAME_POS((tp->t_chasee->t_pos),hero) || off(player, ISINWALL) || on(*tp, CANINWALL)))
! 808: return(dist != 0);
! 809: else /* May actually hit here from a confused move */
! 810: return(!ce(tp->t_nxtpos, hero));
! 811: }
! 812:
! 813: /*
! 814: roomin(coord *cp)
! 815:
! 816: Find what room some coordinates are in.
! 817: NULL means they aren't in any room.
! 818: */
! 819:
! 820: struct room *
! 821: roomin(coord cp)
! 822: {
! 823: struct room *rp;
! 824: int i;
! 825:
! 826: for (i = 0; i < MAXROOMS; i++)
! 827: {
! 828: rp = &rooms[i];
! 829:
! 830: if ((cp.x <= (rp->r_pos.x + (rp->r_max.x - 1))) &&
! 831: (cp.y <= (rp->r_pos.y + (rp->r_max.y - 1))) &&
! 832: (cp.x >= rp->r_pos.x) &&
! 833: (cp.y >= rp->r_pos.y))
! 834: {
! 835: return(rp);
! 836: }
! 837: }
! 838:
! 839: return(NULL);
! 840: }
! 841:
! 842: /*
! 843: * find_mons: Find the monster from his corrdinates
! 844: */
! 845:
! 846: struct linked_list *
! 847: find_mons(int y, int x)
! 848: {
! 849: struct linked_list *item;
! 850:
! 851: for (item = mlist; item != NULL; item = next(item))
! 852: {
! 853: struct thing *th = THINGPTR(item);
! 854:
! 855: if (th->t_pos.y == y && th->t_pos.x == x)
! 856: return item;
! 857: }
! 858: return NULL;
! 859: }
! 860:
! 861: /*
! 862: * Find an unfriendly monster around us to hit
! 863: */
! 864:
! 865: struct linked_list *
! 866: f_mons_a(int y, int x, int hit_bad)
! 867: {
! 868: int row, col;
! 869: struct linked_list *item;
! 870: struct thing *tp;
! 871:
! 872: for (row = x - 1; row <= x + 1; row++)
! 873: for (col = y - 1; col <= y + 1; col++)
! 874: if (row == x && col == y)
! 875: continue;
! 876: else if (col > 0 && row > 0 &&
! 877: isalpha(mvwinch(mw, col, row)) &&
! 878: ((item = find_mons(col, row)) != NULL))
! 879: {
! 880: tp = THINGPTR(item);
! 881: if ((good_monster(*tp) && !hit_bad) ||
! 882: (!good_monster(*tp) && hit_bad))
! 883: return (item);
! 884: }
! 885:
! 886: return (NULL);
! 887: }
! 888:
! 889:
! 890: /*
! 891: diag_ok()
! 892: Check to see if the move is legal if it is diagonal
! 893: */
! 894:
! 895: int
! 896: diag_ok(coord *sp, coord *ep, struct thing *flgptr)
! 897: {
! 898: if (ep->x == sp->x || ep->y == sp->y)
! 899: return TRUE;
! 900:
! 901: return (step_ok(ep->y, sp->x, MONSTOK, flgptr) &&
! 902: step_ok(sp->y, ep->x, MONSTOK, flgptr));
! 903: }
! 904:
! 905: /*
! 906: cansee()
! 907: returns true if the hero can see a certain coordinate.
! 908: */
! 909:
! 910: int
! 911: cansee(int y, int x)
! 912: {
! 913: struct room *rer;
! 914: coord tp;
! 915:
! 916: if (on(player, ISBLIND))
! 917: return FALSE;
! 918:
! 919: tp.y = y;
! 920: tp.x = x;
! 921: rer = roomin(tp);
! 922:
! 923: /*
! 924: * We can only see if the hero in the same room as the coordinate and
! 925: * the room is lit or if it is close.
! 926: */
! 927:
! 928: return ((rer != NULL &&
! 929: rer == roomin(hero) &&
! 930: (!(rer->r_flags & ISDARK) || (rer->r_flags & HASFIRE)) &&
! 931: (levtype != MAZELEV || /* Maze level needs direct line */
! 932: maze_view(tp.y, tp.x))) ||
! 933: DISTANCE(tp,hero) < see_dist);
! 934: }
! 935:
! 936: coord *
! 937: find_shoot(struct thing *tp, coord *dir)
! 938: {
! 939: struct room *rtp;
! 940: int ulx, uly, xmx, ymx, xmon, ymon, tpx, tpy, row, col;
! 941: struct linked_list *mon;
! 942: struct thing *ick;
! 943:
! 944: rtp = roomin(tp->t_pos); /* Find room of chaser */
! 945:
! 946: if (rtp == NULL)
! 947: return NULL;
! 948:
! 949: ulx = rtp->r_pos.x;
! 950: uly = rtp->r_pos.y;
! 951: xmx = rtp->r_max.x;
! 952: ymx = rtp->r_max.y;
! 953:
! 954: tpx = tp->t_pos.x;
! 955: tpy = tp->t_pos.y;
! 956:
! 957: for (col = ulx; col < (ulx + xmx); col++)
! 958: for (row = uly; row < (uly + ymx); row++)
! 959: {
! 960: if (row > 0 && col > 0 && isalpha(mvwinch(mw, row, col)))
! 961: {
! 962: mon = find_mons(row, col);
! 963:
! 964: if (mon)
! 965: {
! 966: ick = THINGPTR(mon);
! 967: xmon = ick->t_pos.x;
! 968: ymon = ick->t_pos.y;
! 969:
! 970: if (!(good_monster(*ick)))
! 971: {
! 972: if (straight_shot(tpy, tpx, ymon, xmon, dir))
! 973: return(dir);
! 974: }
! 975: }
! 976: }
! 977: }
! 978:
! 979: return(NULL);
! 980: }
! 981:
! 982: /*
! 983: can_shoot()
! 984: determines if the monster (er) has a direct line of shot at the
! 985: player (ee). If so, it returns the direction in which to shoot.
! 986: */
! 987:
! 988: coord *
! 989: can_shoot(coord *er, coord *ee, coord *dir)
! 990: {
! 991: int ery, erx, eey, eex;
! 992:
! 993: /* Make sure we are chasing the player */
! 994:
! 995: if (!ce((*ee), hero))
! 996: return(NULL);
! 997:
! 998: /* They must be in the same room */
! 999:
! 1000: if (roomin(*er) != roomin(hero))
! 1001: return(NULL);
! 1002:
! 1003: ery = er->y;
! 1004: erx = er->x;
! 1005: eey = ee->y;
! 1006: eex = ee->x;
! 1007:
! 1008: /* Will shoot unless next to player, then 80% prob will fight */
! 1009:
! 1010: if ((DISTANCE(*er,*ee) < 4) && (rnd(100) < 80))
! 1011: return(NULL);
! 1012:
! 1013: /* Do we have a straight shot? */
! 1014:
! 1015: if (!straight_shot(ery, erx, eey, eex, dir))
! 1016: return(NULL);
! 1017: else
! 1018: return(dir);
! 1019: }
! 1020:
! 1021: /*
! 1022: straight_shot()
! 1023: See if there is a straight line of sight between the two
! 1024: given coordinates. If shooting is not NULL, it is a pointer to a
! 1025: structure which should be filled with the direction to shoot (if
! 1026: there is a line of sight). If shooting, monsters get in the way.
! 1027: Otherwise, they do not.
! 1028: */
! 1029:
! 1030: int
! 1031: straight_shot(int ery, int erx, int eey, int eex, coord *dir)
! 1032: {
! 1033: int dy, dx; /* Deltas */
! 1034: int ch;
! 1035:
! 1036: /* Does the monster have a straight shot at player */
! 1037:
! 1038: if ((ery != eey) && (erx != eex) &&
! 1039: (abs(ery - eey) != abs(erx - eex)))
! 1040: return (FALSE);
! 1041:
! 1042: /* Get the direction to shoot */
! 1043:
! 1044: if (eey > ery)
! 1045: dy = 1;
! 1046: else if (eey == ery)
! 1047: dy = 0;
! 1048: else
! 1049: dy = -1;
! 1050:
! 1051: if (eex > erx)
! 1052: dx = 1;
! 1053: else if (eex == erx)
! 1054: dx = 0;
! 1055: else
! 1056: dx = -1;
! 1057:
! 1058: /* Make sure we have free area all the way to the player */
! 1059:
! 1060: ery += dy;
! 1061: erx += dx;
! 1062:
! 1063: while ((ery != eey) || (erx != eex))
! 1064: {
! 1065: switch(ch = winat(ery, erx))
! 1066: {
! 1067: case '|':
! 1068: case '-':
! 1069: case WALL:
! 1070: case DOOR:
! 1071: case SECRETDOOR:
! 1072: return(FALSE);
! 1073: default:
! 1074: if (dir && isalpha(ch))
! 1075: return(FALSE);
! 1076: }
! 1077:
! 1078: ery += dy;
! 1079: erx += dx;
! 1080: }
! 1081:
! 1082: if (dir)
! 1083: { /* If we are shooting -- put in the directions */
! 1084: dir->y = dy;
! 1085: dir->x = dx;
! 1086: }
! 1087:
! 1088: return(TRUE);
! 1089: }
! 1090:
! 1091: /*
! 1092: get_hurl
! 1093: returns the weapon that the monster will "throw" if it has one
! 1094: */
! 1095:
! 1096: struct linked_list *
! 1097: get_hurl(struct thing *tp)
! 1098: {
! 1099: struct linked_list *arrow, *bolt, *rock, *silverarrow, *fbbolt;
! 1100: struct linked_list *bullet, *firearrow, *dart, *dagger, *shuriken;
! 1101: struct linked_list *oil, *grenade;
! 1102:
! 1103: struct linked_list *pitem;
! 1104: int bow = FALSE, crossbow = FALSE, sling = FALSE, footbow = FALSE;
! 1105:
! 1106: /* Don't point to anything to begin with */
! 1107:
! 1108: arrow = bolt = rock = silverarrow = fbbolt = NULL;
! 1109: bullet = firearrow = dart = dagger = shuriken = NULL;
! 1110: oil = grenade = NULL;
! 1111:
! 1112: for (pitem = tp->t_pack; pitem != NULL; pitem = next(pitem))
! 1113: if ((OBJPTR(pitem))->o_type == WEAPON)
! 1114: switch ((OBJPTR(pitem))->o_which)
! 1115: {
! 1116: case BOW:bow = TRUE; break;
! 1117: case CROSSBOW:crossbow = TRUE; break;
! 1118: case SLING:sling = TRUE; break;
! 1119: case FOOTBOW:footbow = TRUE; break;
! 1120: case ROCK:rock = pitem; break;
! 1121: case ARROW:arrow = pitem; break;
! 1122: case SILVERARROW:silverarrow = pitem; break;
! 1123: case BOLT:bolt = pitem; break;
! 1124: case FBBOLT:fbbolt = pitem; break;
! 1125: case BULLET:bullet = pitem; break;
! 1126: case FLAMEARROW:firearrow = pitem; break;
! 1127: case DART:dart = pitem; break;
! 1128: case DAGGER:dagger = pitem; break;
! 1129: case SHURIKEN:shuriken = pitem; break;
! 1130: case MOLOTOV:oil = pitem; break;
! 1131: case GRENADE:shuriken = pitem; break;
! 1132: }
! 1133:
! 1134: if (bow && silverarrow)
! 1135: return(silverarrow);
! 1136:
! 1137: if (crossbow && bolt)
! 1138: return(bolt);
! 1139:
! 1140: if (bow && firearrow)
! 1141: return(firearrow);
! 1142:
! 1143: if (off(*tp, ISCHARMED) && oil)
! 1144: return(oil);
! 1145:
! 1146: if (off(*tp, ISCHARMED) && grenade)
! 1147: return(grenade);
! 1148:
! 1149: if (footbow && fbbolt)
! 1150: return(fbbolt);
! 1151:
! 1152: if (bow && arrow)
! 1153: return(arrow);
! 1154:
! 1155: if (sling && bullet)
! 1156: return(bullet);
! 1157:
! 1158: if (sling && rock)
! 1159: return(rock);
! 1160:
! 1161: if (shuriken)
! 1162: return(shuriken);
! 1163:
! 1164: if (dagger)
! 1165: return(dagger);
! 1166:
! 1167: if (silverarrow)
! 1168: return(silverarrow);
! 1169:
! 1170: if (firearrow)
! 1171: return(firearrow);
! 1172:
! 1173: if (fbbolt)
! 1174: return(fbbolt);
! 1175:
! 1176: if (bolt)
! 1177: return(bolt);
! 1178:
! 1179: if (bullet)
! 1180: return(bullet);
! 1181:
! 1182: if (dart)
! 1183: return(dart);
! 1184:
! 1185: if (rock)
! 1186: return(rock);
! 1187:
! 1188: return(NULL);
! 1189: }
! 1190:
! 1191: /*
! 1192: pick_weap()
! 1193: returns the biggest weapon that the monster will wield if it
! 1194: has a non-launching or non-missile weapon returns NULL if no weapon, or
! 1195: bare hands is better
! 1196: */
! 1197:
! 1198: struct object *
! 1199: pick_weap(struct thing *tp)
! 1200: {
! 1201: int weap_dam = maxdamage(tp->t_stats.s_dmg);
! 1202: struct object *ret_obj = NULL;
! 1203: struct linked_list *pitem;
! 1204:
! 1205: if (on(*tp, CANWIELD))
! 1206: {
! 1207: for (pitem = tp->t_pack; pitem != NULL; pitem = next(pitem))
! 1208: {
! 1209: struct object *obj = OBJPTR(pitem);
! 1210:
! 1211: if (obj->o_type != WEAPON && !(obj->o_flags&(ISLAUNCHER|ISMISL)) &&
! 1212: maxdamage(obj->o_damage) > weap_dam)
! 1213: {
! 1214: weap_dam = maxdamage(obj->o_damage);
! 1215: ret_obj = obj;
! 1216: }
! 1217: }
! 1218: }
! 1219:
! 1220: return (ret_obj);
! 1221: }
! 1222:
! 1223: /*
! 1224: canblink()
! 1225: checks if the monster can teleport (blink). If so, it will try
! 1226: to blink the monster next to the player.
! 1227: */
! 1228:
! 1229: int
! 1230: can_blink(struct thing *tp)
! 1231: {
! 1232: int y, x, index = 9;
! 1233: coord tryp; /* To hold the coordinates for use in diag_ok */
! 1234: int spots[9], found_one = FALSE;
! 1235:
! 1236: /*
! 1237: * First, can the monster even blink? And if so, there is only a 30%
! 1238: * chance that it will do so. And it won't blink if it is running.
! 1239: */
! 1240:
! 1241: if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) ||
! 1242: on(*tp, ISFLEE) ||
! 1243: (on(*tp, ISSLOW) && off(*tp, ISHASTE) && !(tp->t_turn)) ||
! 1244: (rnd(10) < 9))
! 1245: return (FALSE);
! 1246:
! 1247: /* Initialize the spots as illegal */
! 1248:
! 1249: do
! 1250: {
! 1251: spots[--index] = FALSE;
! 1252: }
! 1253: while (index > 0);
! 1254:
! 1255: /* Find a suitable spot next to the player */
! 1256:
! 1257: for (y = hero.y - 1; y < hero.y + 2; y++)
! 1258: for (x = hero.x - 1; x < hero.x + 2; x++, index++)
! 1259: {
! 1260: /*
! 1261: * Make sure x coordinate is in range and that we are
! 1262: * not at the player's position
! 1263: */
! 1264:
! 1265: if (x < 0 || x >= COLS || index == 4)
! 1266: continue;
! 1267:
! 1268: /* Is it OK to move there? */
! 1269:
! 1270: if (!step_ok(y, x, NOMONST, tp))
! 1271: spots[index] = FALSE;
! 1272: else
! 1273: {
! 1274:
! 1275: /*
! 1276: * OK, we can go here. But don't go there if
! 1277: * monster can't get at player from there
! 1278: */
! 1279:
! 1280: tryp.y = y;
! 1281: tryp.x = x;
! 1282: if (diag_ok(&tryp, &hero, tp))
! 1283: {
! 1284: spots[index] = TRUE;
! 1285: found_one = TRUE;
! 1286: }
! 1287: }
! 1288: }
! 1289:
! 1290: /* If we found one, go to it */
! 1291:
! 1292: if (found_one)
! 1293: {
! 1294: /* Find a legal spot */
! 1295:
! 1296: while (spots[index = rnd(9)] == FALSE)
! 1297: continue;
! 1298:
! 1299: /* Get the coordinates */
! 1300:
! 1301: y = hero.y + (index / 3) - 1;
! 1302: x = hero.x + (index % 3) - 1;
! 1303:
! 1304: /* Move the monster from the old space */
! 1305:
! 1306: mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch);
! 1307:
! 1308: /* Move it to the new space */
! 1309:
! 1310: tp->t_oldch = CCHAR( mvwinch(cw, y, x) );
! 1311:
! 1312: if (cansee(y, x) &&
! 1313: off(*tp, ISINWALL) &&
! 1314: ((off(*tp, ISINVIS) &&
! 1315: (off(*tp, ISSHADOW) || rnd(100) < 10)) || on(player, CANSEE)) &&
! 1316: off(*tp, CANSURPRISE))
! 1317: mvwaddch(cw, y, x, tp->t_type);
! 1318:
! 1319: mvwaddch(mw, tp->t_pos.y,tp->t_pos.x,' '); /*Clear old position */
! 1320: mvwaddch(mw, y, x, tp->t_type);
! 1321: tp->t_pos.y = y;
! 1322: tp->t_pos.x = x;
! 1323: }
! 1324:
! 1325: return (found_one);
! 1326: }
CVSweb