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