comparison rogue5/chase.c @ 33:f502bf60e6e4

Import Rogue 5.4 from the Roguelike Restoration Project (r1490)
author elwin
date Mon, 24 May 2010 20:10:59 +0000
parents
children
comparison
equal deleted inserted replaced
32:2dcd75e6a736 33:f502bf60e6e4
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 }