Annotation of early-roguelike/rogue5/chase.c, Revision 1.1
1.1 ! rubenllo 1: /*
! 2: * Code for one creature to chase another
! 3: *
! 4: * @(#)chase.c 4.57 (Berkeley) 02/05/99
! 5: *
! 6: * Rogue: Exploring the Dungeons of Doom
! 7: * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
! 8: * All rights reserved.
! 9: *
! 10: * See the file LICENSE.TXT for full copyright and licensing information.
! 11: */
! 12:
! 13: #include <stdlib.h>
! 14: #include <curses.h>
! 15: #include "rogue.h"
! 16:
! 17: #define DRAGONSHOT 5 /* one chance in DRAGONSHOT that a dragon will flame */
! 18:
! 19: static coord ch_ret; /* Where chasing takes you */
! 20:
! 21: /*
! 22: * runners:
! 23: * Make all the running monsters move.
! 24: */
! 25: void
! 26: runners(void)
! 27: {
! 28: THING *tp;
! 29: THING *next;
! 30: int wastarget;
! 31: coord orig_pos;
! 32:
! 33: for (tp = mlist; tp != NULL; tp = next)
! 34: {
! 35: /* remember this in case the monster's "next" is changed */
! 36: next = next(tp);
! 37: if (!on(*tp, ISHELD) && on(*tp, ISRUN))
! 38: {
! 39: orig_pos = tp->t_pos;
! 40: wastarget = on(*tp, ISTARGET);
! 41: if (move_monst(tp) == -1)
! 42: continue;
! 43: if (on(*tp, ISFLY) && dist_cp(&hero, &tp->t_pos) >= 3)
! 44: move_monst(tp);
! 45: if (wastarget && !ce(orig_pos, tp->t_pos))
! 46: {
! 47: tp->t_flags &= ~ISTARGET;
! 48: to_death = FALSE;
! 49: }
! 50: }
! 51: }
! 52: if (has_hit)
! 53: {
! 54: endmsg();
! 55: has_hit = FALSE;
! 56: }
! 57: }
! 58:
! 59: /*
! 60: * move_monst:
! 61: * Execute a single turn of running for a monster
! 62: */
! 63: int
! 64: move_monst(THING *tp)
! 65: {
! 66: if (!on(*tp, ISSLOW) || tp->t_turn)
! 67: if (do_chase(tp) == -1)
! 68: return(-1);
! 69: if (on(*tp, ISHASTE))
! 70: if (do_chase(tp) == -1)
! 71: return(-1);
! 72: tp->t_turn ^= TRUE;
! 73: return(0);
! 74: }
! 75:
! 76: /*
! 77: * relocate:
! 78: * Make the monster's new location be the specified one, updating
! 79: * all the relevant state.
! 80: */
! 81: void
! 82: relocate(THING *th, const coord *new_loc)
! 83: {
! 84: struct room *oroom;
! 85:
! 86: if (!ce(*new_loc, th->t_pos))
! 87: {
! 88: mvaddch(th->t_pos.y, th->t_pos.x, th->t_oldch);
! 89: th->t_room = roomin(new_loc);
! 90: set_oldch(th, new_loc);
! 91: oroom = th->t_room;
! 92: moat(th->t_pos.y, th->t_pos.x) = NULL;
! 93:
! 94: if (oroom != th->t_room)
! 95: th->t_dest = find_dest(th);
! 96: th->t_pos = *new_loc;
! 97: moat(new_loc->y, new_loc->x) = th;
! 98: }
! 99: move(new_loc->y, new_loc->x);
! 100: if (see_monst(th))
! 101: addch(th->t_disguise);
! 102: else if (on(player, SEEMONST))
! 103: {
! 104: standout();
! 105: addch(th->t_type);
! 106: standend();
! 107: }
! 108: }
! 109:
! 110: /*
! 111: * do_chase:
! 112: * Make one thing chase another.
! 113: */
! 114: int
! 115: do_chase(THING *th)
! 116: {
! 117: coord *cp;
! 118: struct room *rer, *ree; /* room of chaser, room of chasee */
! 119: int mindist = 32767, curdist;
! 120: int stoprun = FALSE; /* TRUE means we are there */
! 121: int door;
! 122: THING *obj;
! 123: coord this; /* Temporary destination for chaser */
! 124:
! 125: rer = th->t_room; /* Find room of chaser */
! 126: if (on(*th, ISGREED) && rer->r_goldval == 0)
! 127: th->t_dest = &hero; /* If gold has been taken, run after hero */
! 128: if (th->t_dest == &hero) /* Find room of chasee */
! 129: ree = proom;
! 130: else
! 131: ree = roomin(th->t_dest);
! 132: /*
! 133: * We don't count doors as inside rooms for this routine
! 134: */
! 135: door = (chat(th->t_pos.y, th->t_pos.x) == DOOR);
! 136: /*
! 137: * If the object of our desire is in a different room,
! 138: * and we are not in a corridor, run to the door nearest to
! 139: * our goal.
! 140: */
! 141: over:
! 142: if (rer != ree)
! 143: {
! 144: for (cp = rer->r_exit; cp < &rer->r_exit[rer->r_nexits]; cp++)
! 145: {
! 146: curdist = dist_cp(th->t_dest, cp);
! 147: if (curdist < mindist)
! 148: {
! 149: this = *cp;
! 150: mindist = curdist;
! 151: }
! 152: }
! 153: if (door)
! 154: {
! 155: rer = &passages[flat(th->t_pos.y, th->t_pos.x) & F_PNUM];
! 156: door = FALSE;
! 157: goto over;
! 158: }
! 159: }
! 160: else
! 161: {
! 162: this = *th->t_dest;
! 163: /*
! 164: * For dragons check and see if (a) the hero is on a straight
! 165: * line from it, and (b) that it is within shooting distance,
! 166: * but outside of striking range.
! 167: */
! 168: if (th->t_type == 'D' && (th->t_pos.y == hero.y || th->t_pos.x == hero.x
! 169: || abs(th->t_pos.y - hero.y) == abs(th->t_pos.x - hero.x))
! 170: && dist_cp(&th->t_pos, &hero) <= BOLT_LENGTH * BOLT_LENGTH
! 171: && !on(*th, ISCANC) && rnd(DRAGONSHOT) == 0)
! 172: {
! 173: delta.y = sign(hero.y - th->t_pos.y);
! 174: delta.x = sign(hero.x - th->t_pos.x);
! 175: if (has_hit)
! 176: endmsg();
! 177: fire_bolt(&th->t_pos, &delta, "flame");
! 178: running = FALSE;
! 179: count = 0;
! 180: quiet = 0;
! 181: if (to_death && !on(*th, ISTARGET))
! 182: {
! 183: to_death = FALSE;
! 184: kamikaze = FALSE;
! 185: }
! 186: return(0);
! 187: }
! 188: }
! 189: /*
! 190: * This now contains what we want to run to this time
! 191: * so we run to it. If we hit it we either want to fight it
! 192: * or stop running
! 193: */
! 194: if (!chase(th, &this))
! 195: {
! 196: if (ce(this, hero))
! 197: {
! 198: return( attack(th) );
! 199: }
! 200: else if (ce(this, *th->t_dest))
! 201: {
! 202: for (obj = lvl_obj; obj != NULL; obj = next(obj))
! 203: if (th->t_dest == &obj->o_pos)
! 204: {
! 205: detach(lvl_obj, obj);
! 206: attach(th->t_pack, obj);
! 207: chat(obj->o_pos.y, obj->o_pos.x) =
! 208: (th->t_room->r_flags & ISGONE) ? PASSAGE : FLOOR;
! 209: th->t_dest = find_dest(th);
! 210: break;
! 211: }
! 212: if (th->t_type != 'F')
! 213: stoprun = TRUE;
! 214: }
! 215: }
! 216: else
! 217: {
! 218: if (th->t_type == 'F')
! 219: return(0);
! 220: }
! 221: relocate(th, &ch_ret);
! 222: /*
! 223: * And stop running if need be
! 224: */
! 225: if (stoprun && ce(th->t_pos, *(th->t_dest)))
! 226: th->t_flags &= ~ISRUN;
! 227: return(0);
! 228: }
! 229:
! 230: /*
! 231: * set_oldch:
! 232: * Set the oldch character for the monster
! 233: */
! 234: void
! 235: set_oldch(THING *tp, const coord *cp)
! 236: {
! 237: int sch;
! 238:
! 239: if (ce(tp->t_pos, *cp))
! 240: return;
! 241:
! 242: sch = tp->t_oldch;
! 243: tp->t_oldch = CCHAR( mvinch(cp->y,cp->x) );
! 244: if (!on(player, ISBLIND))
! 245: {
! 246: if ((sch == FLOOR || tp->t_oldch == FLOOR) &&
! 247: (tp->t_room->r_flags & ISDARK))
! 248: tp->t_oldch = ' ';
! 249: else if (dist_cp(cp, &hero) <= LAMPDIST && see_floor)
! 250: tp->t_oldch = chat(cp->y, cp->x);
! 251: }
! 252: }
! 253:
! 254: /*
! 255: * see_monst:
! 256: * Return TRUE if the hero can see the monster
! 257: */
! 258: int
! 259: see_monst(const THING *mp)
! 260: {
! 261: int y, x;
! 262:
! 263: if (on(player, ISBLIND))
! 264: return FALSE;
! 265: if (on(*mp, ISINVIS) && !on(player, CANSEE))
! 266: return FALSE;
! 267: y = mp->t_pos.y;
! 268: x = mp->t_pos.x;
! 269: if (dist(y, x, hero.y, hero.x) < LAMPDIST)
! 270: {
! 271: if (y != hero.y && x != hero.x &&
! 272: !step_ok(chat(y, hero.x)) && !step_ok(chat(hero.y, x)))
! 273: return FALSE;
! 274: return TRUE;
! 275: }
! 276: if (mp->t_room != proom)
! 277: return FALSE;
! 278: return (!(mp->t_room->r_flags & ISDARK));
! 279: }
! 280:
! 281: /*
! 282: * runto:
! 283: * Set a monster running after the hero.
! 284: */
! 285: void
! 286: runto(const coord *runner)
! 287: {
! 288: THING *tp;
! 289:
! 290: /*
! 291: * If we couldn't find him, something is funny
! 292: */
! 293: if ((tp = moat(runner->y, runner->x)) == NULL)
! 294: {
! 295: #ifdef MASTER
! 296: msg("couldn't find monster in runto at (%d,%d)", runner->y, runner->x);
! 297: #endif
! 298: return;
! 299: }
! 300:
! 301: /*
! 302: * Start the beastie running
! 303: */
! 304: tp->t_flags |= ISRUN;
! 305: tp->t_flags &= ~ISHELD;
! 306: tp->t_dest = find_dest(tp);
! 307: }
! 308:
! 309: /*
! 310: * chase:
! 311: * Find the spot for the chaser(er) to move closer to the
! 312: * chasee(ee). Returns TRUE if we want to keep on chasing later
! 313: * FALSE if we reach the goal.
! 314: */
! 315: int
! 316: chase(THING *tp, const coord *ee)
! 317: {
! 318: THING *obj;
! 319: int x, y;
! 320: int curdist, thisdist;
! 321: const coord *er = &tp->t_pos;
! 322: int ch;
! 323: int plcnt = 1;
! 324: coord tryp;
! 325:
! 326: /*
! 327: * If the thing is confused, let it move randomly. Invisible
! 328: * Stalkers are slightly confused all of the time, and bats are
! 329: * quite confused all the time
! 330: */
! 331: if ((on(*tp, ISHUH) && rnd(5) != 0) || (tp->t_type == 'P' && rnd(5) == 0)
! 332: || (tp->t_type == 'B' && rnd(2) == 0))
! 333: {
! 334: /*
! 335: * get a valid random move
! 336: */
! 337: ch_ret = rndmove(tp);
! 338: curdist = dist_cp(&ch_ret, ee);
! 339: /*
! 340: * Small chance that it will become un-confused
! 341: */
! 342: if (rnd(20) == 0)
! 343: tp->t_flags &= ~ISHUH;
! 344: }
! 345: /*
! 346: * Otherwise, find the empty spot next to the chaser that is
! 347: * closest to the chasee.
! 348: */
! 349: else
! 350: {
! 351: int ey, ex;
! 352: /*
! 353: * This will eventually hold where we move to get closer
! 354: * If we can't find an empty spot, we stay where we are.
! 355: */
! 356: curdist = dist_cp(er, ee);
! 357: ch_ret = *er;
! 358:
! 359: ey = er->y + 1;
! 360: if (ey >= NUMLINES - 1)
! 361: ey = NUMLINES - 2;
! 362: ex = er->x + 1;
! 363: if (ex >= NUMCOLS)
! 364: ex = NUMCOLS - 1;
! 365:
! 366: for (x = er->x - 1; x <= ex; x++)
! 367: {
! 368: if (x < 0)
! 369: continue;
! 370: tryp.x = x;
! 371: for (y = er->y - 1; y <= ey; y++)
! 372: {
! 373: tryp.y = y;
! 374: if (!diag_ok(er, &tryp))
! 375: continue;
! 376: ch = winat(y, x);
! 377: if (step_ok(ch))
! 378: {
! 379: /*
! 380: * If it is a scroll, it might be a scare monster scroll
! 381: * so we need to look it up to see what type it is.
! 382: */
! 383: if (ch == SCROLL)
! 384: {
! 385: for (obj = lvl_obj; obj != NULL; obj = next(obj))
! 386: {
! 387: if (y == obj->o_pos.y && x == obj->o_pos.x)
! 388: break;
! 389: }
! 390: if (obj != NULL && obj->o_which == S_SCARE)
! 391: continue;
! 392: }
! 393: /*
! 394: * It can also be a Xeroc, which we shouldn't step on
! 395: */
! 396: if ((obj = moat(y, x)) != NULL && obj->t_type == 'X')
! 397: continue;
! 398: /*
! 399: * If we didn't find any scrolls at this place or it
! 400: * wasn't a scare scroll, then this place counts
! 401: */
! 402: thisdist = dist(y, x, ee->y, ee->x);
! 403: if (thisdist < curdist)
! 404: {
! 405: plcnt = 1;
! 406: ch_ret = tryp;
! 407: curdist = thisdist;
! 408: }
! 409: else if (thisdist == curdist && rnd(++plcnt) == 0)
! 410: {
! 411: ch_ret = tryp;
! 412: curdist = thisdist;
! 413: }
! 414: }
! 415: }
! 416: }
! 417: }
! 418: return (curdist != 0 && !ce(ch_ret, hero));
! 419: }
! 420:
! 421: /*
! 422: * roomin:
! 423: * Find what room some coordinates are in. NULL means they aren't
! 424: * in any room.
! 425: */
! 426: struct room *
! 427: roomin(const coord *cp)
! 428: {
! 429: struct room *rp;
! 430: int *fp;
! 431:
! 432: fp = &flat(cp->y, cp->x);
! 433: if (*fp & F_PASS)
! 434: return &passages[*fp & F_PNUM];
! 435:
! 436: for (rp = rooms; rp < &rooms[MAXROOMS]; rp++)
! 437: if (cp->x <= rp->r_pos.x + rp->r_max.x && rp->r_pos.x <= cp->x
! 438: && cp->y <= rp->r_pos.y + rp->r_max.y && rp->r_pos.y <= cp->y)
! 439: return rp;
! 440:
! 441: msg("in some bizarre place (%d, %d)", unc(*cp));
! 442: #ifdef MASTER
! 443: abort();
! 444: return NULL;
! 445: #else
! 446: return NULL;
! 447: #endif
! 448: }
! 449:
! 450: /*
! 451: * diag_ok:
! 452: * Check to see if the move is legal if it is diagonal
! 453: */
! 454: int
! 455: diag_ok(const coord *sp, const coord *ep)
! 456: {
! 457: if (ep->x < 0 || ep->x >= NUMCOLS || ep->y <= 0 || ep->y >= NUMLINES - 1)
! 458: return FALSE;
! 459: if (ep->x == sp->x || ep->y == sp->y)
! 460: return TRUE;
! 461: return (step_ok(chat(ep->y, sp->x)) && step_ok(chat(sp->y, ep->x)));
! 462: }
! 463:
! 464: /*
! 465: * cansee:
! 466: * Returns true if the hero can see a certain coordinate.
! 467: */
! 468: int
! 469: cansee(int y, int x)
! 470: {
! 471: struct room *rer;
! 472: coord tp;
! 473:
! 474: if (on(player, ISBLIND))
! 475: return FALSE;
! 476: if (dist(y, x, hero.y, hero.x) < LAMPDIST)
! 477: {
! 478: if (flat(y, x) & F_PASS)
! 479: if (y != hero.y && x != hero.x &&
! 480: !step_ok(chat(y, hero.x)) && !step_ok(chat(hero.y, x)))
! 481: return FALSE;
! 482: return TRUE;
! 483: }
! 484: /*
! 485: * We can only see if the hero in the same room as
! 486: * the coordinate and the room is lit or if it is close.
! 487: */
! 488: tp.y = y;
! 489: tp.x = x;
! 490: return ((rer = roomin(&tp)) == proom && !(rer->r_flags & ISDARK));
! 491: }
! 492:
! 493: /*
! 494: * find_dest:
! 495: * find the proper destination for the monster
! 496: */
! 497: const coord *
! 498: find_dest(const THING *tp)
! 499: {
! 500: THING *obj;
! 501: int prob;
! 502:
! 503: if ((prob = monsters[tp->t_type - 'A'].m_carry) <= 0 || tp->t_room == proom
! 504: || see_monst(tp))
! 505: return &hero;
! 506: for (obj = lvl_obj; obj != NULL; obj = next(obj))
! 507: {
! 508: if (obj->o_type == SCROLL && obj->o_which == S_SCARE)
! 509: continue;
! 510: if (roomin(&obj->o_pos) == tp->t_room && rnd(100) < prob)
! 511: {
! 512: for (tp = mlist; tp != NULL; tp = next(tp))
! 513: if (tp->t_dest == &obj->o_pos)
! 514: break;
! 515: if (tp == NULL)
! 516: return &obj->o_pos;
! 517: }
! 518: }
! 519: return &hero;
! 520: }
! 521:
! 522: /*
! 523: * dist:
! 524: * Calculate the "distance" between to points. Actually,
! 525: * this calculates d^2, not d, but that's good enough for
! 526: * our purposes, since it's only used comparitively.
! 527: */
! 528: int
! 529: dist(int y1, int x1, int y2, int x2)
! 530: {
! 531: return ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
! 532: }
! 533:
! 534: /*
! 535: * dist_cp:
! 536: * Call dist() with appropriate arguments for coord pointers
! 537: */
! 538: int
! 539: dist_cp(const coord *c1, const coord *c2)
! 540: {
! 541: return dist(c1->y, c1->x, c2->y, c2->x);
! 542: }
CVSweb