Annotation of early-roguelike/arogue5/chase.c, Revision 1.1
1.1 ! rubenllo 1: /*
! 2: * Code for one object to chase another
! 3: *
! 4: * Advanced Rogue
! 5: * Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T
! 6: * All rights reserved.
! 7: *
! 8: * Based on "Rogue: Exploring the Dungeons of Doom"
! 9: * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
! 10: * All rights reserved.
! 11: *
! 12: * See the file LICENSE.TXT for full copyright and licensing information.
! 13: */
! 14:
! 15: #include <stdlib.h>
! 16: #include <ctype.h>
! 17: #include <limits.h>
! 18: #include "curses.h"
! 19: #include "rogue.h"
! 20: #define MAXINT INT_MAX
! 21: #define MININT INT_MIN
! 22:
! 23: coord ch_ret; /* Where chasing takes you */
! 24:
! 25: struct linked_list *get_hurl(struct thing *tp);
! 26: bool straight_shot(int ery, int erx, int eey, int eex, coord *shooting);
! 27:
! 28:
! 29: /*
! 30: * Canblink checks if the monster can teleport (blink). If so, it will
! 31: * try to blink the monster next to the player.
! 32: */
! 33:
! 34: bool
! 35: can_blink(struct thing *tp)
! 36: {
! 37: register int y, x, index=9;
! 38: coord tryp; /* To hold the coordinates for use in diag_ok */
! 39: bool spots[9], found_one=FALSE;
! 40:
! 41: /*
! 42: * First, can the monster even blink? And if so, there is only a 50%
! 43: * chance that it will do so. And it won't blink if it is running or
! 44: * held.
! 45: */
! 46: if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) ||
! 47: on(*tp, ISFLEE) ||
! 48: (on(*tp, ISSLOW) && off(*tp, ISHASTE) && !(tp->t_turn)) ||
! 49: tp->t_no_move ||
! 50: (rnd(12) < 6)) return(FALSE);
! 51:
! 52:
! 53: /* Initialize the spots as illegal */
! 54: do {
! 55: spots[--index] = FALSE;
! 56: } while (index > 0);
! 57:
! 58: /* Find a suitable spot next to the player */
! 59: for (y=hero.y-1; y<hero.y+2; y++)
! 60: for (x=hero.x-1; x<hero.x+2; x++, index++) {
! 61: /* Make sure x coordinate is in range and that we are
! 62: * not at the player's position
! 63: */
! 64: if (x<0 || x >= COLS || index == 4) continue;
! 65:
! 66: /* Is it OK to move there? */
! 67: if (step_ok(y, x, NOMONST, tp) &&
! 68: (!isatrap(mvwinch(cw, y, x)) ||
! 69: rnd(10) >= tp->t_stats.s_intel ||
! 70: on(*tp, ISFLY))) {
! 71: /* OK, we can go here. But don't go there if
! 72: * monster can't get at player from there
! 73: */
! 74: tryp.y = y;
! 75: tryp.x = x;
! 76: if (diag_ok(&tryp, &hero, tp)) {
! 77: spots[index] = TRUE;
! 78: found_one = TRUE;
! 79: }
! 80: }
! 81: }
! 82:
! 83: /* If we found one, go to it */
! 84: if (found_one) {
! 85: char rch; /* What's really where the creatures moves to */
! 86:
! 87: /* Find a legal spot */
! 88: while (spots[index=rnd(9)] == FALSE) continue;
! 89:
! 90: /* Get the coordinates */
! 91: y = hero.y + (index/3) - 1;
! 92: x = hero.x + (index % 3) - 1;
! 93:
! 94: /* Move the monster from the old space */
! 95: mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch);
! 96:
! 97: /* Move it to the new space */
! 98: tp->t_oldch = CCHAR( mvwinch(cw, y, x) );
! 99:
! 100: /* Display the creature if our hero can see it */
! 101: if (cansee(y, x) &&
! 102: off(*tp, ISINWALL) &&
! 103: !invisible(tp))
! 104: mvwaddch(cw, y, x, tp->t_type);
! 105:
! 106: /* Fix the monster window */
! 107: mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, ' '); /* Clear old position */
! 108: mvwaddch(mw, y, x, tp->t_type);
! 109:
! 110: /* Record the new position */
! 111: tp->t_pos.y = y;
! 112: tp->t_pos.x = x;
! 113:
! 114: /* If the monster is on a trap, trap it */
! 115: rch = CCHAR( mvinch(y, x) );
! 116: if (isatrap(rch)) {
! 117: if (cansee(y, x)) tp->t_oldch = rch;
! 118: be_trapped(tp, &(tp->t_pos));
! 119: }
! 120: }
! 121:
! 122: return(found_one);
! 123: }
! 124:
! 125: /*
! 126: * Can_shoot determines if the monster (er) has a direct line of shot
! 127: * at the player (ee). If so, it returns the direction in which to shoot.
! 128: */
! 129:
! 130: coord *
! 131: can_shoot(coord *er, coord *ee)
! 132: {
! 133: static coord shoot_dir;
! 134:
! 135: /* Make sure we are chasing the player */
! 136: if (!ce((*ee), hero)) return(NULL);
! 137:
! 138: /*
! 139: * They must be in the same room or very close (at door)
! 140: */
! 141: if (roomin(er) != roomin(&hero) && DISTANCE(er->y,er->x,ee->y,ee->x) > 1)
! 142: return(NULL);
! 143:
! 144: /* Do we have a straight shot? */
! 145: if (!straight_shot(er->y, er->x, ee->y, ee->x, &shoot_dir)) return(NULL);
! 146: else return(&shoot_dir);
! 147: }
! 148:
! 149: /*
! 150: * chase:
! 151: * Find the spot for the chaser(er) to move closer to the
! 152: * chasee(ee). Returns TRUE if we want to keep on chasing later
! 153: * FALSE if we reach the goal.
! 154: */
! 155:
! 156: /* flee: True if destination (ee) is player and monster is running
! 157: * away or the player is in a wall and the monster can't get to it
! 158: */
! 159: bool
! 160: chase(struct thing *tp, coord *ee, bool flee, bool *mdead)
! 161: {
! 162: int damage, dist, thisdist, monst_dist = MAXINT;
! 163: struct linked_list *weapon;
! 164: register coord *er = &tp->t_pos;
! 165: coord *shoot_dir;
! 166: char ch, mch;
! 167: bool next_player = FALSE;
! 168:
! 169: if (mdead != NULL)
! 170: *mdead = 0;
! 171:
! 172: /*
! 173: * set the distance from the chas(er) to the chas(ee) here and then
! 174: * we won't have to reset it unless the chas(er) moves (instead of shoots)
! 175: */
! 176: dist = DISTANCE(er->y, er->x, ee->y, ee->x);
! 177:
! 178: /*
! 179: * If the thing is confused or it can't see the player,
! 180: * let it move randomly.
! 181: */
! 182: if ((on(*tp, ISHUH) && rnd(10) < 8) ||
! 183: (on(player, ISINVIS) && off(*tp, CANSEE))) { /* Player is invisible */
! 184: /*
! 185: * get a valid random move
! 186: */
! 187: ch_ret = *rndmove(tp);
! 188: dist = DISTANCE(ch_ret.y, ch_ret.x, ee->y, ee->x);
! 189: /*
! 190: * check to see if random move takes creature away from player
! 191: * if it does then turn off ISHELD
! 192: */
! 193: if (dist > 2) {
! 194: if (on(*tp, DIDHOLD)) {
! 195: turn_off(*tp, DIDHOLD);
! 196: turn_on(*tp, CANHOLD);
! 197: if (--hold_count == 0)
! 198: turn_off(player, ISHELD);
! 199: }
! 200:
! 201: /* If monster was suffocating, stop it */
! 202: if (on(*tp, DIDSUFFOCATE)) {
! 203: turn_off(*tp, DIDSUFFOCATE);
! 204: turn_on(*tp, CANSUFFOCATE);
! 205: extinguish(suffocate);
! 206: }
! 207: }
! 208: }
! 209:
! 210: /* If we can breathe, we may do so */
! 211: else if (on(*tp, CANBREATHE) &&
! 212: (dist < BOLT_LENGTH*BOLT_LENGTH) &&
! 213: (shoot_dir = can_shoot(er, ee)) &&
! 214: !on(player, ISINWALL) &&
! 215: (rnd(100) < 75)) {
! 216: register char *breath = NULL;
! 217:
! 218: damage = tp->t_stats.s_hpt;
! 219: /* Will it breathe at random */
! 220: if (on(*tp, CANBRANDOM)) {
! 221: /* Turn off random breath */
! 222: turn_off(*tp, CANBRANDOM);
! 223:
! 224: /* Select type of breath */
! 225: switch (rnd(10)) {
! 226: case 0: breath = "acid";
! 227: turn_on(*tp, NOACID);
! 228: when 1: breath = "flame";
! 229: turn_on(*tp, NOFIRE);
! 230: when 2: breath = "lightning bolt";
! 231: turn_on(*tp, NOBOLT);
! 232: when 3: breath = "chlorine gas";
! 233: turn_on(*tp, NOGAS);
! 234: when 4: breath = "ice";
! 235: turn_on(*tp, NOCOLD);
! 236: when 5: breath = "nerve gas";
! 237: turn_on(*tp, NOPARALYZE);
! 238: when 6: breath = "sleeping gas";
! 239: turn_on(*tp, NOSLEEP);
! 240: when 7: breath = "slow gas";
! 241: turn_on(*tp, NOSLOW);
! 242: when 8: breath = "confusion gas";
! 243: turn_on(*tp, ISCLEAR);
! 244: when 9: breath = "fear gas";
! 245: turn_on(*tp, NOFEAR);
! 246: }
! 247: }
! 248:
! 249: /* Or can it breathe acid? */
! 250: else if (on(*tp, CANBACID)) {
! 251: turn_off(*tp, CANBACID);
! 252: breath = "acid";
! 253: }
! 254:
! 255: /* Or can it breathe fire */
! 256: else if (on(*tp, CANBFIRE)) {
! 257: turn_off(*tp, CANBFIRE);
! 258: breath = "flame";
! 259: }
! 260:
! 261: /* Or can it breathe electricity? */
! 262: else if (on(*tp, CANBBOLT)) {
! 263: turn_off(*tp, CANBBOLT);
! 264: breath = "lightning bolt";
! 265: }
! 266:
! 267: /* Or can it breathe gas? */
! 268: else if (on(*tp, CANBGAS)) {
! 269: turn_off(*tp, CANBGAS);
! 270: breath = "chlorine gas";
! 271: }
! 272:
! 273: /* Or can it breathe ice? */
! 274: else if (on(*tp, CANBICE)) {
! 275: turn_off(*tp, CANBICE);
! 276: breath = "ice";
! 277: }
! 278:
! 279: else if (on(*tp, CANBPGAS)) {
! 280: turn_off(*tp, CANBPGAS);
! 281: breath = "nerve gas";
! 282: }
! 283:
! 284: /* can it breathe sleeping gas */
! 285: else if (on(*tp, CANBSGAS)) {
! 286: turn_off(*tp, CANBSGAS);
! 287: breath = "sleeping gas";
! 288: }
! 289:
! 290: /* can it breathe slow gas */
! 291: else if (on(*tp, CANBSLGAS)) {
! 292: turn_off(*tp, CANBSLGAS);
! 293: breath = "slow gas";
! 294: }
! 295: /* can it breathe confusion gas */
! 296: else if (on(*tp, CANBCGAS)) {
! 297: turn_off(*tp, CANBCGAS);
! 298: breath = "confusion gas";
! 299: }
! 300: /* can it breathe fear gas */
! 301: else {
! 302: turn_off(*tp, CANBFGAS);
! 303: breath = "fear gas";
! 304: }
! 305:
! 306: /* Now breathe -- sets "monst_dead" if it kills someone */
! 307: *mdead = shoot_bolt( tp, *er, *shoot_dir, FALSE,
! 308: tp->t_index, breath, damage);
! 309:
! 310: ch_ret = *er;
! 311: running = FALSE;
! 312: if (*mdead) return(TRUE);
! 313: }
! 314:
! 315: /* We may shoot missiles if we can */
! 316: else if (on(*tp, CANMISSILE) &&
! 317: (shoot_dir = can_shoot(er, ee)) &&
! 318: !on(player, ISINWALL) &&
! 319: (rnd(100) < 75)) {
! 320: static struct object missile =
! 321: {
! 322: MISSILE, {0, 0}, "", 0, "", "0d4 " , NULL, 0, WS_MISSILE, 100, 1
! 323: };
! 324:
! 325: sprintf(missile.o_hurldmg, "%dd4", tp->t_stats.s_lvl);
! 326: do_motion(&missile, shoot_dir->y, shoot_dir->x, tp);
! 327: hit_monster(unc(missile.o_pos), &missile, tp);
! 328: turn_off(*tp, CANMISSILE);
! 329: ch_ret = *er;
! 330: running = FALSE;
! 331: }
! 332:
! 333: /* We may use a sonic blast if we can */
! 334: else if (on(*tp, CANSONIC) &&
! 335: (dist < BOLT_LENGTH*2) &&
! 336: (shoot_dir = can_shoot(er, ee)) &&
! 337: !on(player, ISINWALL) &&
! 338: (rnd(100) < 50)) {
! 339: static struct object blast =
! 340: {
! 341: MISSILE, {0, 0}, "", 0, "", "150" , NULL, 0, 0, 0, 0
! 342: };
! 343:
! 344: turn_off(*tp, CANSONIC);
! 345: do_motion(&blast, shoot_dir->y, shoot_dir->x, tp);
! 346: damage = 150;
! 347: if (save(VS_BREATH, &player, -3))
! 348: damage /= 2;
! 349: msg ("The %s's sonic blast hits you", monsters[tp->t_index].m_name);
! 350: if ((pstats.s_hpt -= damage) <= 0)
! 351: death(tp->t_index);
! 352: ch_ret = *er;
! 353: running = FALSE;
! 354: }
! 355: /*
! 356: * If we have a special magic item, we might use it. We will restrict
! 357: * this options to uniques with relics for now.
! 358: */
! 359: else if (on(*tp, ISUNIQUE) && m_use_item(tp, er, ee)) {
! 360: ch_ret = *er;
! 361: running = FALSE;
! 362: }
! 363: /*
! 364: * If we can shoot or throw something, we might do so.
! 365: * If next to player, then 80% prob will fight.
! 366: */
! 367: else if(on(*tp, CANSHOOT) &&
! 368: (shoot_dir = can_shoot(er, ee)) &&
! 369: !on(player, ISINWALL) &&
! 370: (dist > 3 || (rnd(100) > 80)) &&
! 371: (weapon = get_hurl(tp))) {
! 372: missile(shoot_dir->y, shoot_dir->x, weapon, tp);
! 373: ch_ret = *er;
! 374: }
! 375:
! 376: /*
! 377: * Otherwise, find the empty spot next to the chaser that is
! 378: * closest to the chasee.
! 379: */
! 380: else {
! 381: register int ey, ex, x, y;
! 382: register struct room *rer, *ree;
! 383: int dist_to_old = MININT; /* Dist from goal to old position */
! 384:
! 385: /* Get rooms */
! 386: rer = roomin(er); /* Room the chasER (monster) is in */
! 387: ree = roomin(ee); /* Room the chasEE is in */
! 388:
! 389: /*
! 390: * This will eventually hold where we move to get closer
! 391: * If we can't find an empty spot, we stay where we are.
! 392: */
! 393: dist = flee ? 0 : MAXINT;
! 394: ch_ret = *er;
! 395:
! 396: /* Are we at our goal already? */
! 397: if (!flee && ce(ch_ret, *ee)) return(FALSE);
! 398:
! 399: ey = er->y + 1;
! 400: ex = er->x + 1;
! 401:
! 402: /* Check all possible moves */
! 403: for (x = er->x - 1; x <= ex; x++) {
! 404: if (x < 0 || x >= COLS) /* Don't try off the board */
! 405: continue;
! 406: for (y = er->y - 1; y <= ey; y++) {
! 407: coord tryp;
! 408:
! 409: if ((y < 1) || (y >= LINES - 2)) /* Don't try off the board */
! 410: continue;
! 411:
! 412: /* Don't try the player if not going after the player */
! 413: if ((flee || !ce(hero, *ee)) && x == hero.x && y == hero.y) {
! 414: next_player = TRUE;
! 415: continue;
! 416: }
! 417:
! 418: tryp.x = x;
! 419: tryp.y = y;
! 420:
! 421: /* Is there a monster on this spot closer to our goal?
! 422: * Don't look in our spot or where we were.
! 423: */
! 424: if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) &&
! 425: isalpha(mch = CCHAR( mvwinch(mw, y, x) ) )) {
! 426: int test_dist;
! 427:
! 428: test_dist = DISTANCE(y, x, ee->y, ee->x);
! 429: if (test_dist <= 25 && /* Let's be fairly close */
! 430: test_dist < monst_dist) {
! 431: /* Could we really move there? */
! 432: mvwaddch(mw, y, x, ' '); /* Temporarily blank monst */
! 433: if (diag_ok(er, &tryp, tp)) monst_dist = test_dist;
! 434: mvwaddch(mw, y, x, mch); /* Restore monster */
! 435: }
! 436: }
! 437:
! 438: /* Can we move onto the spot? */
! 439: if (!diag_ok(er, &tryp, tp)) continue;
! 440:
! 441: ch = CCHAR( mvwinch(cw, y, x) ); /* Screen character */
! 442:
! 443: /* Stepping on player is NOT okay if we are fleeing */
! 444: if (step_ok(y, x, NOMONST, tp) &&
! 445: (off(*tp, ISFLEE) || ch != PLAYER))
! 446: {
! 447: /*
! 448: * If it is a trap, an intelligent monster may not
! 449: * step on it (unless our hero is on top!)
! 450: */
! 451: if ((isatrap(ch)) &&
! 452: (rnd(10) < tp->t_stats.s_intel) &&
! 453: (!on(*tp, ISFLY)) &&
! 454: (y != hero.y || x != hero.x))
! 455: continue;
! 456:
! 457: /*
! 458: * OK -- this place counts
! 459: */
! 460: thisdist = DISTANCE(y, x, ee->y, ee->x);
! 461:
! 462: /* Adjust distance if we are being shot at */
! 463: if (tp->t_wasshot && tp->t_stats.s_intel > 5 &&
! 464: ce(hero, *ee)) {
! 465: /* Move out of line of sight */
! 466: if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL)) {
! 467: if (flee) thisdist -= SHOTPENALTY;
! 468: else thisdist += SHOTPENALTY;
! 469: }
! 470:
! 471: /* But do we want to leave the room? */
! 472: else if (rer && rer == ree && ch == DOOR)
! 473: thisdist += DOORPENALTY;
! 474: }
! 475:
! 476: /* Don't move to the last position if we can help it
! 477: * (unless out prey just moved there)
! 478: */
! 479: if (ce(tryp, tp->t_oldpos) && (flee || !ce(tryp, hero)))
! 480: dist_to_old = thisdist;
! 481:
! 482: else if ((flee && (thisdist > dist)) ||
! 483: (!flee && (thisdist < dist)))
! 484: {
! 485: ch_ret = tryp;
! 486: dist = thisdist;
! 487: }
! 488: }
! 489: }
! 490: }
! 491:
! 492: /* If we aren't trying to get the player, but he is in our way,
! 493: * hit him (unless we have been turned)
! 494: */
! 495: if (next_player && off(*tp, WASTURNED) &&
! 496: ((flee && ce(ch_ret, *er)) ||
! 497: (!flee && DISTANCE(er->y, er->x, ee->y, ee->x) < dist)) &&
! 498: step_ok(tp->t_dest->y, tp->t_dest->x, NOMONST, tp)) {
! 499: /* Okay to hit player */
! 500: ch_ret = hero;
! 501: return(FALSE);
! 502: }
! 503:
! 504:
! 505: /* If we can't get closer to the player (if that's our goal)
! 506: * because other monsters are in the way, just stay put
! 507: */
! 508: if (!flee && ce(hero, *ee) && monst_dist < MAXINT &&
! 509: DISTANCE(er->y, er->x, hero.y, hero.x) < dist)
! 510: ch_ret = *er;
! 511:
! 512: /* Do we want to go back to the last position? */
! 513: else if (dist_to_old != MININT && /* It is possible to move back */
! 514: ((flee && dist == 0) || /* No other possible moves */
! 515: (!flee && dist == MAXINT))) {
! 516: /* Do we move back or just stay put (default)? */
! 517: dist = DISTANCE(er->y, er->x, ee->y, ee->x); /* Current distance */
! 518: if (!flee || (flee && (dist_to_old > dist))) ch_ret = tp->t_oldpos;
! 519: }
! 520: }
! 521:
! 522: /* May actually hit here from a confused move */
! 523: return(!ce(ch_ret, hero));
! 524: }
! 525:
! 526: /*
! 527: * do_chase:
! 528: * Make one thing chase another.
! 529: */
! 530: /* flee: True if running away or player is inaccessible in wall */
! 531:
! 532: void
! 533: do_chase(struct thing *th, bool flee)
! 534: {
! 535: register struct room *rer, *ree, /* room of chaser, room of chasee */
! 536: *orig_rer, /* Original room of chaser */
! 537: *new_room; /* new room of monster */
! 538: int dist = MININT;
! 539: int mindist = MAXINT, maxdist = MININT;
! 540: bool stoprun = FALSE, /* TRUE means we are there */
! 541: rundoor; /* TRUE means run to a door */
! 542: bool mdead = 0;
! 543: char rch, sch;
! 544: coord *last_door=0, /* Door we just came from */
! 545: this; /* Temporary destination for chaser */
! 546:
! 547: /* Make sure the monster can move */
! 548: if (th->t_no_move != 0) {
! 549: th->t_no_move--;
! 550: return;
! 551: }
! 552:
! 553: rer = roomin(&th->t_pos); /* Find room of chaser */
! 554: ree = roomin(th->t_dest); /* Find room of chasee */
! 555: orig_rer = rer; /* Original room of chaser (including doors) */
! 556:
! 557: /*
! 558: * We don't count monsters on doors as inside rooms for this routine
! 559: */
! 560: if ((sch = CCHAR( mvwinch(stdscr, th->t_pos.y, th->t_pos.x) )) == DOOR ||
! 561: sch == PASSAGE) {
! 562: rer = NULL;
! 563: }
! 564: this = *th->t_dest;
! 565:
! 566: /*
! 567: * If we are not in a corridor and not a Xorn, then if we are running
! 568: * after the player, we run to a door if he is not in the same room.
! 569: * If we are fleeing, we run to a door if he IS in the same room.
! 570: * Note: We don't bother with doors in mazes.
! 571: */
! 572: if (levtype != MAZELEV && rer != NULL && off(*th, CANINWALL)) {
! 573: if (flee) rundoor = (rer == ree);
! 574: else rundoor = (rer != ree);
! 575: }
! 576: else rundoor = FALSE;
! 577:
! 578: if (rundoor) {
! 579: register struct linked_list *exitptr; /* For looping through exits */
! 580: coord *exit; /* A particular door */
! 581: int exity, exitx; /* Door's coordinates */
! 582: char dch='\0'; /* Door character */
! 583:
! 584: if (th->t_doorgoal)
! 585: dch = CCHAR( mvwinch(stdscr, th->t_doorgoal->y, th->t_doorgoal->x) );
! 586:
! 587: /* Do we have a valid goal? */
! 588: if ((dch == PASSAGE || dch == DOOR) && /* A real door */
! 589: (!flee || !ce(*th->t_doorgoal, hero))) { /* Player should not
! 590: * be at door if we are
! 591: * running away
! 592: */
! 593: this = *th->t_doorgoal;
! 594: dist = 0; /* Indicate that we have our door */
! 595: }
! 596:
! 597: /* Go through all the doors */
! 598: else for (exitptr = rer->r_exit; exitptr; exitptr = next(exitptr)) {
! 599: exit = DOORPTR(exitptr);
! 600: exity = exit->y;
! 601: exitx = exit->x;
! 602:
! 603: /* Make sure it is a real door */
! 604: dch = CCHAR( mvwinch(stdscr, exity, exitx) );
! 605: if (dch == PASSAGE || dch == DOOR) {
! 606: /* Don't count a door if we are fleeing from the player and
! 607: * he is standing on it
! 608: */
! 609: if (flee && ce(*exit, hero)) continue;
! 610:
! 611: /* Were we just on this door? */
! 612: if (ce(*exit, th->t_oldpos)) last_door = exit;
! 613:
! 614: else {
! 615: dist = DISTANCE(th->t_dest->y, th->t_dest->x, exity, exitx);
! 616:
! 617: /* If fleeing, we want to maximize distance from door to
! 618: * what we flee, and minimize distance from door to us.
! 619: */
! 620: if (flee)
! 621: dist -= DISTANCE(th->t_pos.y, th->t_pos.x, exity, exitx);
! 622:
! 623: /* Maximize distance if fleeing, otherwise minimize it */
! 624: if ((flee && (dist > maxdist)) ||
! 625: (!flee && (dist < mindist))) {
! 626: th->t_doorgoal = exit; /* Use this door */
! 627: this = *exit;
! 628: mindist = maxdist = dist;
! 629: }
! 630: }
! 631: }
! 632: }
! 633:
! 634: /* Could we not find a door? */
! 635: if (dist == MININT) {
! 636: /* If we were on a door, go ahead and use it */
! 637: if (last_door) {
! 638: th->t_doorgoal = last_door;
! 639: this = th->t_oldpos;
! 640: dist = 0; /* Indicate that we found a door */
! 641: }
! 642: else th->t_doorgoal = NULL; /* No more door goal */
! 643: }
! 644:
! 645: /* Indicate that we do not want to flee from the door */
! 646: if (dist != MININT) flee = FALSE;
! 647: }
! 648: else th->t_doorgoal = 0; /* Not going to any door */
! 649:
! 650: /*
! 651: * this now contains what we want to run to this time
! 652: * so we run to it. If we hit it we either want to fight it
! 653: * or stop running
! 654: */
! 655: if (!chase(th, &this, flee, &mdead)) {
! 656: if (ce(ch_ret, hero)) {
! 657: /* merchants try to sell something --> others attack */
! 658: if (on(*th, CANSELL)) sell(th);
! 659: else attack(th, NULL, FALSE);
! 660: return;
! 661: }
! 662: else if (on(*th, NOMOVE))
! 663: stoprun = TRUE;
! 664: }
! 665:
! 666: if (mdead) return; /* Did monster kill someone? */
! 667:
! 668: if (on(*th, NOMOVE)) return;
! 669:
! 670: /* If we have a scavenger, it can pick something up */
! 671: if (on(*th, ISSCAVENGE)) {
! 672: register struct linked_list *n_item, *o_item;
! 673:
! 674: while ((n_item = find_obj(ch_ret.y, ch_ret.x)) != NULL) {
! 675: char floor = (roomin(&ch_ret) == NULL) ? PASSAGE : FLOOR;
! 676: register struct object *n_obj, *o_obj;
! 677:
! 678: /*
! 679: * see if he's got one of this group already
! 680: */
! 681: o_item = NULL;
! 682: n_obj = OBJPTR(n_item);
! 683: detach(lvl_obj, n_item);
! 684: if (n_obj->o_group) {
! 685: for(o_item = th->t_pack; o_item != NULL; o_item = next(o_item)){
! 686: o_obj = OBJPTR(o_item);
! 687: if (o_obj->o_group == n_obj->o_group) {
! 688: o_obj->o_count += n_obj->o_count;
! 689: o_discard(n_item);
! 690: break;
! 691: }
! 692: }
! 693: }
! 694: if (o_item == NULL) { /* didn't find it */
! 695: attach(th->t_pack, n_item);
! 696: }
! 697: if (cansee(ch_ret.y, ch_ret.x))
! 698: mvwaddch(cw, ch_ret.y, ch_ret.x, floor);
! 699: mvaddch(ch_ret.y, ch_ret.x, floor);
! 700: }
! 701: }
! 702:
! 703: mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch);
! 704: sch = CCHAR( mvwinch(cw, ch_ret.y, ch_ret.x) ); /* What player sees */
! 705: rch = CCHAR( mvwinch(stdscr, ch_ret.y, ch_ret.x) ); /* What's really there */
! 706:
! 707: /* Get new room of monster */
! 708: new_room=roomin(&ch_ret);
! 709:
! 710: /* If we have a tunneling monster, it may be making a tunnel */
! 711: if (on(*th, CANTUNNEL) &&
! 712: (rch == SECRETDOOR || rch == WALL || rch == '|' || rch == '-')) {
! 713: char nch; /* The new look to the tunnel */
! 714:
! 715: if (rch == WALL) nch = PASSAGE;
! 716: else if (levtype == MAZELEV) nch = FLOOR;
! 717: else nch = DOOR;
! 718: addch(nch);
! 719:
! 720: if (cansee(ch_ret.y, ch_ret.x)) sch = nch; /* Can player see this? */
! 721:
! 722: /* Does this make a new exit? */
! 723: if (rch == '|' || rch == '-') {
! 724: struct linked_list *newroom;
! 725: coord *exit;
! 726:
! 727: newroom = new_item(sizeof(coord));
! 728: exit = DOORPTR(newroom);
! 729: *exit = ch_ret;
! 730: attach(new_room->r_exit, newroom);
! 731: }
! 732: }
! 733:
! 734: /* Mark if the monster is inside a wall */
! 735: if (isrock(mvinch(ch_ret.y, ch_ret.x))) turn_on(*th, ISINWALL);
! 736: else turn_off(*th, ISINWALL);
! 737:
! 738: /* If the monster can illuminate rooms, check for a change */
! 739: if (on(*th, HASFIRE)) {
! 740: register struct linked_list *fire_item;
! 741:
! 742: /* Is monster entering a room? */
! 743: if (orig_rer != new_room && new_room != NULL) {
! 744: fire_item = creat_item(); /* Get an item-only structure */
! 745: ldata(fire_item) = (char *) th;
! 746:
! 747: attach(new_room->r_fires, fire_item);
! 748: new_room->r_flags |= HASFIRE;
! 749:
! 750: if (cansee(ch_ret.y, ch_ret.x) && next(new_room->r_fires) == NULL)
! 751: light(&hero);
! 752: }
! 753:
! 754: /* Is monster leaving a room? */
! 755: if (orig_rer != new_room && orig_rer != NULL) {
! 756: /* Find the bugger in the list and delete him */
! 757: for (fire_item = orig_rer->r_fires; fire_item != NULL;
! 758: fire_item = next(fire_item)) {
! 759: if (THINGPTR(fire_item) == th) { /* Found him! */
! 760: detach(orig_rer->r_fires, fire_item);
! 761: destroy_item(fire_item);
! 762: if (orig_rer->r_fires == NULL) {
! 763: orig_rer->r_flags &= ~HASFIRE;
! 764: if (cansee(th->t_pos.y, th->t_pos.x))
! 765: light(&th->t_pos);
! 766: }
! 767: break;
! 768: }
! 769: }
! 770: }
! 771: }
! 772:
! 773: /* If monster is entering player's room and player can see it,
! 774: * stop the player's running.
! 775: */
! 776: if (new_room != orig_rer && new_room != NULL &&
! 777: new_room == ree && cansee(unc(ch_ret)) &&
! 778: (off(*th, ISINVIS) || on(player, CANSEE)) &&
! 779: (off(*th, ISSHADOW) || on(player, CANSEE)) &&
! 780: (off(*th, CANSURPRISE) || ISWEARING(R_ALERT)))
! 781: running = FALSE;
! 782:
! 783: /*
! 784: if (rer != NULL && !lit_room(orig_rer) && sch == FLOOR &&
! 785: DISTANCE(ch_ret.y, ch_ret.x, th->t_pos.y, th->t_pos.x) < 3 &&
! 786: off(player, ISBLIND))
! 787: th->t_oldch = ' ';
! 788: else
! 789: */
! 790: th->t_oldch = sch;
! 791:
! 792: /* Let's display those creatures that we can see. */
! 793: if (cansee(unc(ch_ret)) &&
! 794: off(*th, ISINWALL) &&
! 795: !invisible(th))
! 796: mvwaddch(cw, ch_ret.y, ch_ret.x, th->t_type);
! 797:
! 798: /*
! 799: * Blank out the old position and record the new position --
! 800: * the blanking must be done first in case the positions are the same.
! 801: */
! 802: mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' ');
! 803: mvwaddch(mw, ch_ret.y, ch_ret.x, th->t_type);
! 804:
! 805: /* Record monster's last position (if new one is different) */
! 806: if (!ce(ch_ret, th->t_pos)) th->t_oldpos = th->t_pos;
! 807: th->t_pos = ch_ret; /* Mark the monster's new position */
! 808:
! 809: /* If the monster is on a trap, trap it */
! 810: sch = CCHAR( mvinch(ch_ret.y, ch_ret.x) );
! 811: if (isatrap(sch)) {
! 812: if (cansee(ch_ret.y, ch_ret.x)) th->t_oldch = sch;
! 813: be_trapped(th, &ch_ret);
! 814: }
! 815:
! 816:
! 817: /*
! 818: * And stop running if need be
! 819: */
! 820: if (stoprun && ce(th->t_pos, *(th->t_dest)))
! 821: turn_off(*th, ISRUN);
! 822: }
! 823:
! 824:
! 825: /*
! 826: * Get_hurl returns the weapon that the monster will "throw" if he has one
! 827: */
! 828:
! 829: struct linked_list *
! 830: get_hurl(struct thing *tp)
! 831: {
! 832: struct linked_list *arrow, *bolt, *rock;
! 833: register struct linked_list *pitem;
! 834: bool bow=FALSE, crossbow=FALSE, sling=FALSE;
! 835:
! 836: arrow = bolt = rock = NULL; /* Don't point to anything to begin with */
! 837: for (pitem=tp->t_pack; pitem; pitem=next(pitem))
! 838: if ((OBJPTR(pitem))->o_type == WEAPON)
! 839: switch ((OBJPTR(pitem))->o_which) {
! 840: case BOW: bow = TRUE;
! 841: when CROSSBOW: crossbow = TRUE;
! 842: when SLING: sling = TRUE;
! 843: when ROCK: rock = pitem;
! 844: when ARROW: arrow = pitem;
! 845: when BOLT: bolt = pitem;
! 846: }
! 847:
! 848: /* Use crossbow bolt if possible */
! 849: if (crossbow && bolt) return(bolt);
! 850: if (bow && arrow) return(arrow);
! 851: if (sling && rock) return(rock);
! 852: return(NULL);
! 853: }
! 854:
! 855: /*
! 856: * runners:
! 857: * Make all the running monsters move.
! 858: */
! 859:
! 860: void
! 861: runners(void)
! 862: {
! 863: register struct linked_list *item;
! 864: register struct thing *tp = NULL;
! 865:
! 866: /*
! 867: * loop thru the list of running (wandering) monsters and see what
! 868: * each one will do this time.
! 869: *
! 870: * Note: the special case that one of this buggers kills another.
! 871: * if this happens than we have to see if the monster killed
! 872: * himself or someone else. In case its himself we have to get next
! 873: * one immediately. If it wasn't we have to get next one at very
! 874: * end in case he killed the next one.
! 875: */
! 876:
! 877: for (item = mlist; item != NULL; item = next(item)) {
! 878: tp = THINGPTR(item);
! 879: turn_on(*tp, ISREADY);
! 880: }
! 881:
! 882: for (;;) {
! 883: for (item = mlist; item != NULL; item = next(item)) {
! 884: tp = THINGPTR(item);
! 885:
! 886: if (on(*tp, ISREADY))
! 887: break;
! 888: }
! 889:
! 890: if (item == NULL)
! 891: break;
! 892:
! 893: turn_off(*tp, ISREADY);
! 894:
! 895: if (on(*tp, ISHELD) && rnd(tp->t_stats.s_lvl) > 11) {
! 896: turn_off(*tp, ISHELD);
! 897: turn_on(*tp, ISRUN);
! 898: turn_off(*tp, ISDISGUISE);
! 899: tp->t_dest = &hero;
! 900: if (tp->t_stats.s_hpt < tp->maxstats.s_hpt)
! 901: turn_on(*tp, ISFLEE);
! 902: if (cansee(tp->t_pos.y, tp->t_pos.x))
! 903: msg("The %s breaks free from the hold spell",
! 904: monsters[tp->t_index].m_name);
! 905: }
! 906: if (off(*tp, ISHELD) && on(*tp, ISRUN)) {
! 907: register bool flee;
! 908:
! 909: /* Should monster run away? */
! 910: flee = on(*tp, ISFLEE) ||
! 911: ((tp->t_dest == &hero) && on(player, ISINWALL) &&
! 912: off(*tp, CANINWALL));
! 913:
! 914: if (off(*tp, ISSLOW) || tp->t_turn) {
! 915: doctor(tp);
! 916: do_chase(tp, flee);
! 917: }
! 918: if (off(*tp, ISDEAD) && off(*tp, ISELSEWHERE) && on(*tp, ISHASTE)) {
! 919: doctor(tp);
! 920: do_chase(tp, flee);
! 921: }
! 922: if (off(*tp, ISDEAD) && off(*tp, ISELSEWHERE)) {
! 923: tp->t_turn ^= TRUE;
! 924: tp->t_wasshot = FALSE; /* Not shot anymore */
! 925: }
! 926: }
! 927: }
! 928: }
! 929:
! 930: /*
! 931: * runto:
! 932: * Set a monster running after something
! 933: */
! 934:
! 935: void
! 936: runto(struct thing *runner, coord *spot)
! 937: {
! 938: /*
! 939: * Start the beastie running
! 940: */
! 941: runner->t_dest = spot;
! 942: turn_on(*runner, ISRUN);
! 943: turn_off(*runner, ISDISGUISE);
! 944: }
! 945:
! 946:
! 947:
! 948: /*
! 949: * straight_shot:
! 950: * See if there is a straight line of sight between the two
! 951: * given coordinates. If shooting is not NULL, it is a pointer
! 952: * to a structure which should be filled with the direction
! 953: * to shoot (if there is a line of sight). If shooting, monsters
! 954: * get in the way. Otherwise, they do not.
! 955: */
! 956:
! 957: bool
! 958: straight_shot(int ery, int erx, int eey, int eex, coord *shooting)
! 959: {
! 960: register int dy, dx; /* Deltas */
! 961: char ch;
! 962:
! 963: /* Does the monster have a straight shot at player */
! 964: if ((ery != eey) && (erx != eex) &&
! 965: (abs(ery - eey) != abs(erx - eex))) return(FALSE);
! 966:
! 967: /* Get the direction to shoot */
! 968: if (eey > ery) dy = 1;
! 969: else if (eey == ery) dy = 0;
! 970: else dy = -1;
! 971:
! 972: if (eex > erx) dx = 1;
! 973: else if (eex == erx) dx = 0;
! 974: else dx = -1;
! 975:
! 976: /* Make sure we have free area all the way to the player */
! 977: ery += dy;
! 978: erx += dx;
! 979: while ((ery != eey) || (erx != eex)) {
! 980: switch (ch = CCHAR( winat(ery, erx) )) {
! 981: case '|':
! 982: case '-':
! 983: case WALL:
! 984: case DOOR:
! 985: case SECRETDOOR:
! 986: case FOREST:
! 987: return(FALSE);
! 988: default:
! 989: if (shooting && isalpha(ch)) return(FALSE);
! 990: }
! 991: ery += dy;
! 992: erx += dx;
! 993: }
! 994:
! 995: if (shooting) { /* If we are shooting -- put in the directions */
! 996: shooting->y = dy;
! 997: shooting->x = dx;
! 998: }
! 999: return(TRUE);
! 1000: }
! 1001:
! 1002:
CVSweb