Annotation of early-roguelike/arogue7/chase.c, Revision 1.1
1.1 ! rubenllo 1: /*
! 2: * chase.c - Code for one object to chase another
! 3: *
! 4: * Advanced Rogue
! 5: * Copyright (C) 1984, 1985, 1986 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: /*
! 16: * Code for one object to chase another
! 17: *
! 18: */
! 19:
! 20: #include <ctype.h>
! 21: #include <limits.h>
! 22: #include <stdlib.h>
! 23: #include "curses.h"
! 24: #include "rogue.h"
! 25: #define MAXINT INT_MAX
! 26: #define MININT INT_MIN
! 27:
! 28: bool straight_shot(int ery, int erx, int eey, int eex, coord *shooting);
! 29:
! 30: /*
! 31: * Canblink checks if the monster can teleport (blink). If so, it will
! 32: * try to blink the monster next to the player.
! 33: */
! 34:
! 35: bool
! 36: can_blink(struct thing *tp)
! 37: {
! 38: register int y, x, index=9;
! 39: coord tryp; /* To hold the coordinates for use in diag_ok */
! 40: bool spots[9], found_one=FALSE;
! 41:
! 42: /*
! 43: * First, can the monster even blink? And if so, there is only a 50%
! 44: * chance that it will do so. And it won't blink if it is running or
! 45: * held.
! 46: */
! 47: if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) ||
! 48: on(*tp, ISFLEE) ||
! 49: tp->t_action == A_FREEZE ||
! 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 prey (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: /*
! 136: * They must be in the same room or very close (at door)
! 137: */
! 138: if (roomin(er) != roomin(ee) && DISTANCE(er->y,er->x,ee->y,ee->x) > 1)
! 139: return(NULL);
! 140:
! 141: /* Do we have a straight shot? */
! 142: if (!straight_shot(er->y, er->x, ee->y, ee->x, &shoot_dir)) return(NULL);
! 143: else return(&shoot_dir);
! 144: }
! 145:
! 146: /*
! 147: * chase:
! 148: * Find the spot for the chaser(er) to move closer to the
! 149: * chasee(ee). Rer is the room of the chaser, and ree is the
! 150: * room of the creature being chased (chasee). Flee is true if
! 151: * destination (ee) is player and monster is running away
! 152: * or the player is in a wall and the monster can't get to it
! 153: */
! 154:
! 155: void
! 156: chase(struct thing *tp, coord *ee, struct room *rer, struct room *ree,
! 157: bool flee)
! 158: {
! 159: int dist, thisdist, monst_dist = MAXINT;
! 160: register coord *er = &tp->t_pos;
! 161: struct thing *prey; /* What we are chasing */
! 162: coord ch_ret; /* Where chasing takes you */
! 163: char ch, mch;
! 164: bool next_player = FALSE;
! 165:
! 166: /*
! 167: * set the distance from the chas(er) to the chas(ee) here and then
! 168: * we won't have to reset it unless the chas(er) moves (instead of shoots)
! 169: */
! 170: dist = DISTANCE(er->y, er->x, ee->y, ee->x);
! 171:
! 172: /*
! 173: * See if our destination is a monster or player. If so, make "prey" point
! 174: * to it.
! 175: */
! 176: if (ce(hero, *ee)) prey = &player; /* Is it the player? */
! 177: else if (tp->t_dest && ce(*(tp->t_dest), *ee)) { /* Is it a monster? */
! 178: struct linked_list *item;
! 179:
! 180: /* What is the monster we're chasing? */
! 181: item = find_mons(ee->y, ee->x);
! 182: if (item != NULL) prey = THINGPTR(item);
! 183: else prey = NULL;
! 184: }
! 185: else prey = NULL;
! 186:
! 187: /* We will use at least one movement period */
! 188: tp->t_no_move = movement(tp);
! 189: if (on(*tp, ISFLY)) /* If the creature is flying, speed it up */
! 190: tp->t_no_move /= 2;
! 191:
! 192: /*
! 193: * If the thing is confused or it can't see the player,
! 194: * let it move randomly.
! 195: */
! 196: if ((on(*tp, ISHUH) && rnd(10) < 8) ||
! 197: (prey && on(*prey, ISINVIS) && off(*tp, CANSEE))) { /* invisible prey */
! 198: /*
! 199: * get a valid random move
! 200: */
! 201: tp->t_newpos = *rndmove(tp);
! 202: dist = DISTANCE(tp->t_newpos.y, tp->t_newpos.x, ee->y, ee->x);
! 203: }
! 204:
! 205: /*
! 206: * Otherwise, find the empty spot next to the chaser that is
! 207: * closest to the chasee.
! 208: */
! 209: else {
! 210: register int ey, ex, x, y;
! 211: int dist_to_old = MININT; /* Dist from goal to old position */
! 212:
! 213: /*
! 214: * This will eventually hold where we move to get closer
! 215: * If we can't find an empty spot, we stay where we are.
! 216: */
! 217: dist = flee ? 0 : MAXINT;
! 218: ch_ret = *er;
! 219:
! 220: /* Are we at our goal already? */
! 221: if (!flee && ce(ch_ret, *ee)) {
! 222: turn_off(*tp, ISRUN); /* So stop running! */
! 223: return;
! 224: }
! 225:
! 226: ey = er->y + 1;
! 227: ex = er->x + 1;
! 228:
! 229: /* Check all possible moves */
! 230: for (x = er->x - 1; x <= ex; x++) {
! 231: if (x < 0 || x >= cols) /* Don't try off the board */
! 232: continue;
! 233: for (y = er->y - 1; y <= ey; y++) {
! 234: coord tryp;
! 235:
! 236: if ((y < 1) || (y >= lines - 2)) /* Don't try off the board */
! 237: continue;
! 238:
! 239: /* Don't try the player if not going after the player */
! 240: if ((flee || !ce(hero, *ee) || on(*tp, ISFRIENDLY)) &&
! 241: x == hero.x && y == hero.y) {
! 242: next_player = TRUE;
! 243: continue;
! 244: }
! 245:
! 246: tryp.x = x;
! 247: tryp.y = y;
! 248:
! 249: /* Is there a monster on this spot closer to our goal?
! 250: * Don't look in our spot or where we were.
! 251: */
! 252: if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) &&
! 253: isalpha(mch = CCHAR( mvwinch(mw, y, x) ) )) {
! 254: int test_dist;
! 255:
! 256: test_dist = DISTANCE(y, x, ee->y, ee->x);
! 257: if (test_dist <= 25 && /* Let's be fairly close */
! 258: test_dist < monst_dist) {
! 259: /* Could we really move there? */
! 260: mvwaddch(mw, y, x, ' '); /* Temporarily blank monst */
! 261: if (diag_ok(er, &tryp, tp)) monst_dist = test_dist;
! 262: mvwaddch(mw, y, x, mch); /* Restore monster */
! 263: }
! 264: }
! 265:
! 266: /* Can we move onto the spot? */
! 267: if (!diag_ok(er, &tryp, tp)) continue;
! 268:
! 269: ch = CCHAR( mvwinch(cw, y, x) ); /* Screen character */
! 270:
! 271: /*
! 272: * Stepping on player is NOT okay if we are fleeing.
! 273: * If we are friendly to the player and there is a monster
! 274: * in the way that is not of our race, it is okay to move
! 275: * there.
! 276: */
! 277: if (step_ok(y, x, FIGHTOK, tp) &&
! 278: (off(*tp, ISFLEE) || ch != PLAYER))
! 279: {
! 280: /*
! 281: * If it is a trap, an intelligent monster may not
! 282: * step on it (unless our hero is on top!)
! 283: */
! 284: if ((isatrap(ch)) &&
! 285: (rnd(10) < tp->t_stats.s_intel) &&
! 286: (!on(*tp, ISFLY)) &&
! 287: (y != hero.y || x != hero.x))
! 288: continue;
! 289:
! 290: /*
! 291: * OK -- this place counts
! 292: */
! 293: thisdist = DISTANCE(y, x, ee->y, ee->x);
! 294:
! 295: /* Adjust distance if we are being shot at */
! 296: if (tp->t_wasshot && tp->t_stats.s_intel > 5 &&
! 297: prey != NULL) {
! 298: /* Move out of line of sight */
! 299: if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL)) {
! 300: if (flee) thisdist -= SHOTPENALTY;
! 301: else thisdist += SHOTPENALTY;
! 302: }
! 303:
! 304: /* But do we want to leave the room? */
! 305: else if (rer && rer == ree && ch == DOOR)
! 306: thisdist += DOORPENALTY;
! 307: }
! 308:
! 309: /* Don't move to the last position if we can help it
! 310: * (unless out prey just moved there)
! 311: */
! 312: if (ce(tryp, tp->t_oldpos) && (flee || !ce(tryp, hero)))
! 313: dist_to_old = thisdist;
! 314:
! 315: else if ((flee && (thisdist > dist)) ||
! 316: (!flee && (thisdist < dist)))
! 317: {
! 318: ch_ret = tryp;
! 319: dist = thisdist;
! 320: }
! 321: }
! 322: }
! 323: }
! 324:
! 325: /* If we aren't trying to get the player, but he is in our way,
! 326: * hit him (unless we have been turned or are friendly). next_player
! 327: * being TRUE -> we are next to the player but don't want to hit him.
! 328: *
! 329: * If we are friendly to the player, following him, and standing next
! 330: * to him, we will try to help him out in battle.
! 331: */
! 332: if (next_player && off(*tp, WASTURNED)) {
! 333: if (off(*tp, ISFRIENDLY) &&
! 334: ((flee && ce(ch_ret, *er)) ||
! 335: (!flee && DISTANCE(er->y, er->x, ee->y, ee->x) < dist)) &&
! 336: step_ok(tp->t_dest->y, tp->t_dest->x, NOMONST, tp)) {
! 337: /* Okay to hit player */
! 338: debug("Switching to hero.");
! 339: tp->t_newpos = hero;
! 340: tp->t_action = A_MOVE;
! 341: return;
! 342: }
! 343: else if (on(*tp, ISFRIENDLY) && !flee && ce(*ee, hero)) {
! 344: /*
! 345: * Look all around the player. If there is a fightable
! 346: * creature next to both of us, hit it. Otherwise, if
! 347: * there is a fightable creature next to the player, try
! 348: * to move next to it.
! 349: */
! 350: dist = MAXINT;
! 351: for (x = hero.x - 1; x <= hero.x + 1; x++) {
! 352: if (x < 0 || x >= cols) /* Don't try off the board */
! 353: continue;
! 354: for (y = hero.y - 1; y <= hero.y + 1; y++) {
! 355: if ((y < 1) || (y >= lines - 2)) /* Stay on the board */
! 356: continue;
! 357:
! 358: /* Is there a fightable monster here? */
! 359: if (isalpha(mvwinch(mw, y, x)) &&
! 360: step_ok(y, x, FIGHTOK, tp) &&
! 361: off(*tp, ISSTONE)) {
! 362: thisdist = DISTANCE(er->y, er->x, y, x);
! 363: if (thisdist < dist) {
! 364: dist = thisdist;
! 365: ch_ret.y = y;
! 366: ch_ret.x = x;
! 367: }
! 368: }
! 369: }
! 370: }
! 371:
! 372: /* Are we next to a bad guy? */
! 373: if (dist <= 2) { /* Get him! */
! 374: tp->t_newpos = ch_ret;
! 375: tp->t_action = A_MOVE;
! 376: }
! 377:
! 378: /* Try to move to the bad guy */
! 379: else if (dist < MAXINT)
! 380: chase(tp, &ch_ret,
! 381: roomin(&tp->t_pos), roomin(&ch_ret), FALSE);
! 382:
! 383: else tp->t_action = A_NIL;
! 384:
! 385: return;
! 386: }
! 387: }
! 388:
! 389:
! 390: /*
! 391: * If we have decided that we can move onto a monster (we are
! 392: * friendly to the player, go to it.
! 393: */
! 394: if (!ce(ch_ret, *er) && isalpha(mvwinch(mw, ch_ret.y, ch_ret.x))) {
! 395: debug("Attack monster");
! 396: tp->t_newpos = ch_ret;
! 397: tp->t_action = A_MOVE;
! 398: return;
! 399: }
! 400:
! 401: /* If we can't get closer to the player (if that's our goal)
! 402: * because other monsters are in the way, just stay put
! 403: */
! 404: if (!flee && ce(hero, *ee) && monst_dist < MAXINT &&
! 405: DISTANCE(er->y, er->x, hero.y, hero.x) < dist) {
! 406: tp->t_action = A_NIL; /* do nothing for awhile */
! 407: return;
! 408: }
! 409:
! 410: /* Do we want to go back to the last position? */
! 411: else if (dist_to_old != MININT && /* It is possible to move back */
! 412: ((flee && dist == 0) || /* No other possible moves */
! 413: (!flee && dist == MAXINT))) {
! 414: /* Do we move back or just stay put (default)? */
! 415: dist = DISTANCE(er->y, er->x, ee->y, ee->x); /* Current distance */
! 416: if (!flee || (flee && (dist_to_old > dist))) ch_ret = tp->t_oldpos;
! 417: }
! 418:
! 419: /* Record the new destination */
! 420: tp->t_newpos = ch_ret;
! 421: }
! 422:
! 423: /*
! 424: * Do we want to fight or move? If our selected destination (ch_ret)
! 425: * is our hero, then we want to fight. Otherwise, we want to move.
! 426: */
! 427: if (ce(tp->t_newpos, hero)) {
! 428: /* Fight! (or sell) */
! 429: if (on(*tp, CANSELL)) {
! 430: tp->t_action = A_SELL;
! 431: tp->t_no_move += movement(tp); /* takes a little time to sell */
! 432: }
! 433: else {
! 434: tp->t_action = A_ATTACK;
! 435:
! 436: /*
! 437: * Try to find a weapon to wield. Wield_weap will return a
! 438: * projector if weapon is a projectile (eg. bow for arrow).
! 439: * If weapon is NULL (the case here), it will try to find
! 440: * a suitable weapon.
! 441: *
! 442: * Add in rest of time. Fight is
! 443: * movement() + weap_move() + FIGHTBASE
! 444: */
! 445: tp->t_using = wield_weap(NULL, tp);
! 446: if (tp->t_using == NULL)
! 447: tp->t_no_move += weap_move(tp, NULL);
! 448: else
! 449: tp->t_no_move += weap_move(tp, OBJPTR(tp->t_using));
! 450:
! 451: if (on(*tp, ISHASTE))
! 452: tp->t_no_move += FIGHTBASE/2;
! 453: else if (on(*tp, ISSLOW))
! 454: tp->t_no_move += FIGHTBASE*2;
! 455: else
! 456: tp->t_no_move += FIGHTBASE;
! 457: }
! 458: }
! 459: else {
! 460: /* Move */
! 461: tp->t_action = A_MOVE;
! 462:
! 463: /*
! 464: * Check if the creature is not next to the player. If it
! 465: * is not and has held or suffocated the player, then stop it!
! 466: * Note that this code should more appropriately appear in
! 467: * the area that actually moves the monster, but for now it
! 468: * is okay here because the player can't move while held or
! 469: * suffocating.
! 470: */
! 471: if (dist > 2) {
! 472: if (on(*tp, DIDHOLD)) {
! 473: turn_off(*tp, DIDHOLD);
! 474: turn_on(*tp, CANHOLD);
! 475: if (--hold_count == 0)
! 476: turn_off(player, ISHELD);
! 477: }
! 478:
! 479: /* If monster was suffocating, stop it */
! 480: if (on(*tp, DIDSUFFOCATE)) {
! 481: turn_off(*tp, DIDSUFFOCATE);
! 482: turn_on(*tp, CANSUFFOCATE);
! 483: extinguish(suffocate);
! 484: msg("You can breathe again.....Whew!");
! 485: }
! 486: }
! 487: }
! 488: }
! 489:
! 490: /*
! 491: * do_chase:
! 492: * Make one thing chase another.
! 493: */
! 494:
! 495: void
! 496: do_chase(struct thing *th)
! 497: {
! 498: register struct room *orig_rer, /* Original room of chaser */
! 499: *new_room; /* new room of monster */
! 500: char floor, rch, sch;
! 501: coord old_pos, /* Old position of monster */
! 502: ch_ret; /* Where we want to go */
! 503:
! 504: if (on(*th, NOMOVE)) return;
! 505:
! 506: ch_ret = th->t_newpos; /* Record our desired new position */
! 507:
! 508: /*
! 509: * Make sure we have an open spot (no other monster's gotten in our way,
! 510: * someone didn't just drop a scare monster there, our prey didn't just
! 511: * get there, etc.)
! 512: */
! 513: if (!step_ok(th->t_newpos.y, th->t_newpos.x, FIGHTOK, th)) {
! 514: /*
! 515: * Most monsters get upset now. Guardians are all friends,
! 516: * and we don't want to see 50 messages in a row!
! 517: */
! 518: if (th->t_stats.s_intel > 4 &&
! 519: off(*th, ISUNDEAD) &&
! 520: off(*th, ISGUARDIAN) &&
! 521: off(*th, AREMANY) &&
! 522: off(*th, ISHUH) &&
! 523: off(player, ISBLIND) &&
! 524: cansee(unc(th->t_pos)) &&
! 525: !invisible(th))
! 526: msg("%s motions angrily.", prname(monster_name(th), TRUE));
! 527: return;
! 528: }
! 529: else if (ce(th->t_newpos, hero) || /* Player just got in our way */
! 530: isalpha(mvwinch(mw, th->t_newpos.y, th->t_newpos.x))) {
! 531: bool fightplayer = ce(th->t_newpos, hero);
! 532:
! 533: /* If we were turned or are friendly, we just have to sit here! */
! 534: if (fightplayer && (on(*th, WASTURNED) || on(*th, ISFRIENDLY))) return;
! 535:
! 536: /* Do we want to sell something? */
! 537: if (fightplayer && on(*th, CANSELL)) {
! 538: th->t_action = A_SELL;
! 539: th->t_no_move += movement(th); /* takes a little time to sell */
! 540: return;
! 541: }
! 542:
! 543: /* Let's hit him */
! 544: th->t_action = A_ATTACK;
! 545:
! 546: /*
! 547: * Try to find a weapon to wield. Wield_weap will return a
! 548: * projector if weapon is a projectile (eg. bow for arrow).
! 549: * If weapon is NULL (the case here), it will try to find
! 550: * a suitable weapon.
! 551: */
! 552: th->t_using = wield_weap(NULL, th);
! 553: /*
! 554: * add in rest of time
! 555: */
! 556: if (th->t_using == NULL)
! 557: th->t_no_move += weap_move(th, NULL);
! 558: else
! 559: th->t_no_move += weap_move(th, OBJPTR(th->t_using));
! 560: if (on(*th, ISHASTE))
! 561: th->t_no_move += FIGHTBASE/2;
! 562: else if (on(*th, ISSLOW))
! 563: th->t_no_move += FIGHTBASE*2;
! 564: else
! 565: th->t_no_move += FIGHTBASE;
! 566: return;
! 567: }
! 568:
! 569: /*
! 570: * Blank out the old position and record the new position --
! 571: * the blanking must be done first in case the positions are the same.
! 572: */
! 573: mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' ');
! 574: mvwaddch(mw, ch_ret.y, ch_ret.x, th->t_type);
! 575:
! 576: /* Get new and old rooms of monster */
! 577: new_room = roomin(&ch_ret);
! 578: orig_rer = roomin(&th->t_pos);
! 579:
! 580: /* Store the critter's old position and update the current one */
! 581: old_pos = th->t_pos;
! 582: th->t_pos = ch_ret;
! 583: floor = (roomin(&ch_ret) == NULL) ? PASSAGE : FLOOR;
! 584:
! 585: /* If we have a scavenger, it can pick something up */
! 586: if (off(*th, ISGUARDIAN)) {
! 587: register struct linked_list *n_item, *o_item;
! 588: register int item_count = 0;
! 589: bool want_something = FALSE;
! 590:
! 591: while ((n_item = find_obj(ch_ret.y, ch_ret.x)) != NULL) {
! 592: register struct object *n_obj, *o_obj;
! 593: bool wants_it;
! 594:
! 595: /* Does this monster want anything? */
! 596: if (want_something == FALSE) {
! 597: if (on(*th, ISSCAVENGE) || on(*th, CARRYFOOD) ||
! 598: on(*th, CARRYGOLD) || on(*th, CARRYSCROLL) ||
! 599: on(*th, CARRYPOTION) || on(*th, CARRYRING) ||
! 600: on(*th, CARRYSTICK) || on(*th, CARRYMISC) ||
! 601: on(*th, CARRYWEAPON) || on(*th, CARRYARMOR) ||
! 602: on(*th, CARRYDAGGER)) {
! 603: want_something = TRUE;
! 604:
! 605: /*
! 606: * Blank the area. We have to do it only before the
! 607: * first item in case an item gets dropped in same
! 608: * place. We don't want to blank it out after it get
! 609: * dropped.
! 610: */
! 611: mvaddch(ch_ret.y, ch_ret.x, floor);
! 612:
! 613: /* Were we specifically after something here? */
! 614: if (ce(*th->t_dest, ch_ret)) {
! 615: /* If we're mean, we go after the hero */
! 616: if (on(*th, ISMEAN)) runto(th, &hero);
! 617:
! 618: /* Otherwise just go back to sleep */
! 619: else {
! 620: turn_off(*th, ISRUN);
! 621: th->t_dest = NULL;
! 622: }
! 623: }
! 624: }
! 625: else break;
! 626: }
! 627:
! 628: item_count++; /* Count the number of items */
! 629:
! 630: /*
! 631: * see if he's got one of this group already
! 632: */
! 633: o_item = NULL;
! 634: n_obj = OBJPTR(n_item);
! 635: detach(lvl_obj, n_item);
! 636:
! 637: /* See if he wants it */
! 638: if (n_obj->o_type == SCROLL && n_obj->o_which == S_SCARE &&
! 639: th->t_stats.s_intel < 16)
! 640: wants_it = FALSE; /* Most monsters don't want a scare monster */
! 641: else if (on(*th, ISSCAVENGE)) wants_it = TRUE;
! 642: else {
! 643: wants_it = FALSE; /* Default case */
! 644: switch (n_obj->o_type) {
! 645: case FOOD: if(on(*th, CARRYFOOD)) wants_it = TRUE;
! 646: when GOLD: if(on(*th, CARRYGOLD)) wants_it = TRUE;
! 647: when SCROLL:if(on(*th, CARRYSCROLL)) wants_it = TRUE;
! 648: when POTION:if(on(*th, CARRYPOTION)) wants_it = TRUE;
! 649: when RING: if(on(*th, CARRYRING)) wants_it = TRUE;
! 650: when STICK: if(on(*th, CARRYSTICK)) wants_it = TRUE;
! 651: when MM: if(on(*th, CARRYMISC)) wants_it = TRUE;
! 652: when ARMOR: if(on(*th, CARRYARMOR)) wants_it = TRUE;
! 653: when WEAPON:if(on(*th, CARRYWEAPON) ||
! 654: (on(*th,CARRYDAGGER)&&n_obj->o_which==DAGGER))
! 655: wants_it = TRUE;
! 656: }
! 657: }
! 658: /*
! 659: * The quartermaster doesn't sell cursed stuff so he won't
! 660: * pick it up
! 661: */
! 662: if (on(*th, CANSELL) && (n_obj->o_flags & ISCURSED))
! 663: wants_it = FALSE;
! 664:
! 665: /* If he doesn't want it, throw it away */
! 666: if (wants_it == FALSE) {
! 667: fall(n_item, FALSE);
! 668: continue;
! 669: }
! 670:
! 671: /* Otherwise, let's pick it up */
! 672: if (n_obj->o_group) {
! 673: for(o_item = th->t_pack; o_item != NULL; o_item = next(o_item)){
! 674: o_obj = OBJPTR(o_item);
! 675: if (o_obj->o_group == n_obj->o_group) {
! 676: o_obj->o_count += n_obj->o_count;
! 677: o_discard(n_item);
! 678: break;
! 679: }
! 680: }
! 681: }
! 682: if (o_item == NULL) { /* didn't find it */
! 683: attach(th->t_pack, n_item);
! 684: }
! 685: }
! 686:
! 687: /* If there was anything here, we may have to update the screen */
! 688: if (item_count) {
! 689: if (cansee(ch_ret.y, ch_ret.x))
! 690: mvwaddch(cw, ch_ret.y, ch_ret.x, mvinch(ch_ret.y, ch_ret.x));
! 691: updpack(TRUE, th); /* Update the monster's encumberance, too */
! 692: }
! 693: }
! 694:
! 695:
! 696: rch = CCHAR( mvwinch(stdscr, old_pos.y, old_pos.x) );
! 697: if (th->t_oldch == floor && rch != floor && !isatrap(rch))
! 698: mvwaddch(cw, old_pos.y, old_pos.x, rch);
! 699: else
! 700: mvwaddch(cw, old_pos.y, old_pos.x, th->t_oldch);
! 701: sch = CCHAR( mvwinch(cw, ch_ret.y, ch_ret.x) ); /* What player sees */
! 702: rch = CCHAR( mvwinch(stdscr, ch_ret.y, ch_ret.x) ); /* What's really there */
! 703:
! 704: /* If we have a tunneling monster, it may be making a tunnel */
! 705: if (on(*th, CANTUNNEL) &&
! 706: (rch == SECRETDOOR || rch == WALL || rch == '|' || rch == '-')) {
! 707: char nch; /* The new look to the tunnel */
! 708:
! 709: if (rch == WALL) nch = PASSAGE;
! 710: else if (levtype == MAZELEV) nch = FLOOR;
! 711: else nch = DOOR;
! 712: addch(nch);
! 713:
! 714: if (cansee(ch_ret.y, ch_ret.x)) sch = nch; /* Can player see this? */
! 715:
! 716: /* Does this make a new exit? */
! 717: if (rch == '|' || rch == '-') {
! 718: struct linked_list *newroom;
! 719: coord *exit;
! 720:
! 721: newroom = new_item(sizeof(coord));
! 722: exit = DOORPTR(newroom);
! 723: *exit = ch_ret;
! 724: attach(new_room->r_exit, newroom);
! 725: }
! 726: }
! 727:
! 728: /* Mark if the monster is inside a wall */
! 729: if (isrock(mvinch(ch_ret.y, ch_ret.x))) turn_on(*th, ISINWALL);
! 730: else turn_off(*th, ISINWALL);
! 731:
! 732: /* If the monster can illuminate rooms, check for a change */
! 733: if (on(*th, HASFIRE)) {
! 734: register struct linked_list *fire_item;
! 735:
! 736: /* Is monster entering a room? */
! 737: if (orig_rer != new_room && new_room != NULL) {
! 738: fire_item = creat_item(); /* Get an item-only structure */
! 739: ldata(fire_item) = (char *) th;
! 740:
! 741: attach(new_room->r_fires, fire_item);
! 742: new_room->r_flags |= HASFIRE;
! 743:
! 744: if (cansee(ch_ret.y, ch_ret.x) && next(new_room->r_fires) == NULL)
! 745: light(&hero);
! 746: }
! 747:
! 748: /* Is monster leaving a room? */
! 749: if (orig_rer != new_room && orig_rer != NULL) {
! 750: /* Find the bugger in the list and delete him */
! 751: for (fire_item = orig_rer->r_fires; fire_item != NULL;
! 752: fire_item = next(fire_item)) {
! 753: if (THINGPTR(fire_item) == th) { /* Found him! */
! 754: detach(orig_rer->r_fires, fire_item);
! 755: destroy_item(fire_item);
! 756: if (orig_rer->r_fires == NULL) {
! 757: orig_rer->r_flags &= ~HASFIRE;
! 758: if (cansee(old_pos.y, old_pos.x))
! 759: light(&old_pos);
! 760: }
! 761: break;
! 762: }
! 763: }
! 764: }
! 765: }
! 766:
! 767: /* If monster is entering player's room and player can see it,
! 768: * stop the player's running.
! 769: */
! 770: if (new_room != orig_rer && new_room != NULL &&
! 771: new_room == roomin(th->t_dest) && cansee(unc(ch_ret)) &&
! 772: (off(*th, ISINVIS) || on(player, CANSEE)) &&
! 773: (off(*th, ISSHADOW) || on(player, CANSEE)) &&
! 774: (off(*th, CANSURPRISE) || ISWEARING(R_ALERT))) {
! 775: running = FALSE;
! 776: if (fight_flush) md_flushinp();
! 777: }
! 778:
! 779: th->t_oldch = sch;
! 780:
! 781: /* Let's display those creatures that we can see. */
! 782: if (cansee(unc(ch_ret)) &&
! 783: off(*th, ISINWALL) &&
! 784: !invisible(th))
! 785: mvwaddch(cw, ch_ret.y, ch_ret.x, th->t_type);
! 786:
! 787: /* Record monster's last position (if new one is different) */
! 788: if (!ce(ch_ret, old_pos)) th->t_oldpos = old_pos;
! 789:
! 790: /* If the monster is on a trap, trap it */
! 791: sch = CCHAR( mvinch(ch_ret.y, ch_ret.x) );
! 792: if (isatrap(sch)) {
! 793: if (cansee(ch_ret.y, ch_ret.x)) th->t_oldch = sch;
! 794: be_trapped(th, &ch_ret);
! 795: }
! 796: }
! 797:
! 798:
! 799: /*
! 800: * Get_hurl returns the weapon that the monster will "throw" if he has one
! 801: */
! 802:
! 803: struct linked_list *
! 804: get_hurl(struct thing *tp)
! 805: {
! 806: struct linked_list *arrow=NULL, *bolt=NULL, *rock=NULL,
! 807: *spear = NULL, *dagger=NULL, *dart=NULL, *aklad=NULL;
! 808: register struct linked_list *pitem;
! 809: register struct object *obj;
! 810: bool bow=FALSE, crossbow=FALSE, sling=FALSE;
! 811:
! 812: for (pitem=tp->t_pack; pitem; pitem=next(pitem)) {
! 813: obj = OBJPTR(pitem);
! 814: if (obj->o_type == WEAPON)
! 815: switch (obj->o_which) {
! 816: case BOW: bow = TRUE;
! 817: when CROSSBOW: crossbow = TRUE;
! 818: when SLING: sling = TRUE;
! 819: when ROCK: rock = pitem;
! 820: when ARROW: arrow = pitem;
! 821: when BOLT: bolt = pitem;
! 822: when SPEAR: spear = pitem;
! 823: when DAGGER:
! 824: /* Don't throw the dagger if it's our last one */
! 825: if (obj->o_count > 1) dagger = pitem;
! 826: when DART: dart = pitem;
! 827: }
! 828: else if (obj->o_type == RELIC &&
! 829: obj->o_which == AXE_AKLAD)
! 830: aklad = pitem;
! 831: }
! 832:
! 833: /* Do we have that all-powerful Aklad Axe? */
! 834: if (aklad) return(aklad);
! 835:
! 836: /* Use crossbow bolt if possible */
! 837: if (crossbow && bolt) return(bolt);
! 838: if (bow && arrow) return(arrow);
! 839: if (spear) return(spear);
! 840: if (dagger) return(dagger);
! 841: if (sling && rock) return(rock);
! 842: if (dart) return(dart);
! 843: return(NULL);
! 844: }
! 845:
! 846: /*
! 847: * runto:
! 848: * Set a monster running after something
! 849: */
! 850:
! 851: void
! 852: runto(struct thing *runner, coord *spot)
! 853: {
! 854: if (on(*runner, ISSTONE))
! 855: return;
! 856:
! 857: /* If we are chasing a new creature, forget about thrown weapons */
! 858: if (runner->t_dest && !ce(*runner->t_dest, *spot)) runner->t_wasshot=FALSE;
! 859:
! 860: /*
! 861: * Start the beastie running
! 862: */
! 863: runner->t_dest = spot;
! 864: turn_on(*runner, ISRUN);
! 865: turn_off(*runner, ISDISGUISE);
! 866: }
! 867:
! 868:
! 869:
! 870: /*
! 871: * straight_shot:
! 872: * See if there is a straight line of sight between the two
! 873: * given coordinates. If shooting is not NULL, it is a pointer
! 874: * to a structure which should be filled with the direction
! 875: * to shoot (if there is a line of sight). If shooting, monsters
! 876: * get in the way. Otherwise, they do not.
! 877: */
! 878:
! 879: bool
! 880: straight_shot(int ery, int erx, int eey, int eex, coord *shooting)
! 881: {
! 882: register int dy, dx; /* Deltas */
! 883: char ch;
! 884:
! 885: /* Does the monster have a straight shot at prey */
! 886: if ((ery != eey) && (erx != eex) &&
! 887: (abs(ery - eey) != abs(erx - eex))) return(FALSE);
! 888:
! 889: /* Get the direction to shoot */
! 890: if (eey > ery) dy = 1;
! 891: else if (eey == ery) dy = 0;
! 892: else dy = -1;
! 893:
! 894: if (eex > erx) dx = 1;
! 895: else if (eex == erx) dx = 0;
! 896: else dx = -1;
! 897:
! 898: /* Make sure we have free area all the way to the player */
! 899: ery += dy;
! 900: erx += dx;
! 901: while ((ery != eey) || (erx != eex)) {
! 902: switch (ch = CCHAR( winat(ery, erx) )) {
! 903: case '|':
! 904: case '-':
! 905: case WALL:
! 906: case DOOR:
! 907: case SECRETDOOR:
! 908: case FOREST:
! 909: return(FALSE);
! 910: default:
! 911: if (shooting && isalpha(ch)) return(FALSE);
! 912: }
! 913: ery += dy;
! 914: erx += dx;
! 915: }
! 916:
! 917: if (shooting) { /* If we are shooting -- put in the directions */
! 918: shooting->y = dy;
! 919: shooting->x = dx;
! 920: }
! 921: return(TRUE);
! 922: }
CVSweb