Annotation of early-roguelike/rogue5/chase.c, Revision 1.1.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