Mercurial > hg > early-roguelike
comparison arogue5/chase.c @ 63:0ed67132cf10
Import Advanced Rogue 5.8 from the Roguelike Restoration Project (r1490)
author | elwin |
---|---|
date | Thu, 09 Aug 2012 22:58:48 +0000 |
parents | |
children | 56e748983fa8 |
comparison
equal
deleted
inserted
replaced
62:0ef99244acb8 | 63:0ed67132cf10 |
---|---|
1 /* | |
2 * Code for one object to chase another | |
3 * | |
4 * Advanced Rogue | |
5 * Copyright (C) 1984, 1985 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 #include <ctype.h> | |
16 #include <limits.h> | |
17 #include "curses.h" | |
18 #include "rogue.h" | |
19 #define MAXINT INT_MAX | |
20 #define MININT INT_MIN | |
21 | |
22 coord ch_ret; /* Where chasing takes you */ | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 /* | |
29 * Canblink checks if the monster can teleport (blink). If so, it will | |
30 * try to blink the monster next to the player. | |
31 */ | |
32 | |
33 bool | |
34 can_blink(tp) | |
35 register struct thing *tp; | |
36 { | |
37 register int y, x, index=9; | |
38 coord tryp; /* To hold the coordinates for use in diag_ok */ | |
39 bool spots[9], found_one=FALSE; | |
40 | |
41 /* | |
42 * First, can the monster even blink? And if so, there is only a 50% | |
43 * chance that it will do so. And it won't blink if it is running or | |
44 * held. | |
45 */ | |
46 if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) || | |
47 on(*tp, ISFLEE) || | |
48 (on(*tp, ISSLOW) && off(*tp, ISHASTE) && !(tp->t_turn)) || | |
49 tp->t_no_move || | |
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 player (ee). If so, it returns the direction in which to shoot. | |
128 */ | |
129 | |
130 coord * | |
131 can_shoot(er, ee) | |
132 register coord *er, *ee; | |
133 { | |
134 static coord shoot_dir; | |
135 | |
136 /* Make sure we are chasing the player */ | |
137 if (!ce((*ee), hero)) return(NULL); | |
138 | |
139 /* | |
140 * They must be in the same room or very close (at door) | |
141 */ | |
142 if (roomin(er) != roomin(&hero) && DISTANCE(er->y,er->x,ee->y,ee->x) > 1) | |
143 return(NULL); | |
144 | |
145 /* Do we have a straight shot? */ | |
146 if (!straight_shot(er->y, er->x, ee->y, ee->x, &shoot_dir)) return(NULL); | |
147 else return(&shoot_dir); | |
148 } | |
149 | |
150 /* | |
151 * chase: | |
152 * Find the spot for the chaser(er) to move closer to the | |
153 * chasee(ee). Returns TRUE if we want to keep on chasing later | |
154 * FALSE if we reach the goal. | |
155 */ | |
156 | |
157 chase(tp, ee, flee, mdead) | |
158 register struct thing *tp; | |
159 register coord *ee; | |
160 bool flee; /* True if destination (ee) is player and monster is running away | |
161 * or the player is in a wall and the monster can't get to it | |
162 */ | |
163 bool *mdead; | |
164 { | |
165 int damage, dist, thisdist, monst_dist = MAXINT; | |
166 struct linked_list *weapon; | |
167 register coord *er = &tp->t_pos; | |
168 coord *shoot_dir; | |
169 char ch, mch; | |
170 bool next_player = FALSE; | |
171 | |
172 if (mdead != NULL) | |
173 *mdead = 0; | |
174 | |
175 /* | |
176 * set the distance from the chas(er) to the chas(ee) here and then | |
177 * we won't have to reset it unless the chas(er) moves (instead of shoots) | |
178 */ | |
179 dist = DISTANCE(er->y, er->x, ee->y, ee->x); | |
180 | |
181 /* | |
182 * If the thing is confused or it can't see the player, | |
183 * let it move randomly. | |
184 */ | |
185 if ((on(*tp, ISHUH) && rnd(10) < 8) || | |
186 (on(player, ISINVIS) && off(*tp, CANSEE))) { /* Player is invisible */ | |
187 /* | |
188 * get a valid random move | |
189 */ | |
190 ch_ret = *rndmove(tp); | |
191 dist = DISTANCE(ch_ret.y, ch_ret.x, ee->y, ee->x); | |
192 /* | |
193 * check to see if random move takes creature away from player | |
194 * if it does then turn off ISHELD | |
195 */ | |
196 if (dist > 2) { | |
197 if (on(*tp, DIDHOLD)) { | |
198 turn_off(*tp, DIDHOLD); | |
199 turn_on(*tp, CANHOLD); | |
200 if (--hold_count == 0) | |
201 turn_off(player, ISHELD); | |
202 } | |
203 | |
204 /* If monster was suffocating, stop it */ | |
205 if (on(*tp, DIDSUFFOCATE)) { | |
206 turn_off(*tp, DIDSUFFOCATE); | |
207 turn_on(*tp, CANSUFFOCATE); | |
208 extinguish(suffocate); | |
209 } | |
210 } | |
211 } | |
212 | |
213 /* If we can breathe, we may do so */ | |
214 else if (on(*tp, CANBREATHE) && | |
215 (dist < BOLT_LENGTH*BOLT_LENGTH) && | |
216 (shoot_dir = can_shoot(er, ee)) && | |
217 !on(player, ISINWALL) && | |
218 (rnd(100) < 75)) { | |
219 register char *breath = NULL; | |
220 | |
221 damage = tp->t_stats.s_hpt; | |
222 /* Will it breathe at random */ | |
223 if (on(*tp, CANBRANDOM)) { | |
224 /* Turn off random breath */ | |
225 turn_off(*tp, CANBRANDOM); | |
226 | |
227 /* Select type of breath */ | |
228 switch (rnd(10)) { | |
229 case 0: breath = "acid"; | |
230 turn_on(*tp, NOACID); | |
231 when 1: breath = "flame"; | |
232 turn_on(*tp, NOFIRE); | |
233 when 2: breath = "lightning bolt"; | |
234 turn_on(*tp, NOBOLT); | |
235 when 3: breath = "chlorine gas"; | |
236 turn_on(*tp, NOGAS); | |
237 when 4: breath = "ice"; | |
238 turn_on(*tp, NOCOLD); | |
239 when 5: breath = "nerve gas"; | |
240 turn_on(*tp, NOPARALYZE); | |
241 when 6: breath = "sleeping gas"; | |
242 turn_on(*tp, NOSLEEP); | |
243 when 7: breath = "slow gas"; | |
244 turn_on(*tp, NOSLOW); | |
245 when 8: breath = "confusion gas"; | |
246 turn_on(*tp, ISCLEAR); | |
247 when 9: breath = "fear gas"; | |
248 turn_on(*tp, NOFEAR); | |
249 } | |
250 } | |
251 | |
252 /* Or can it breathe acid? */ | |
253 else if (on(*tp, CANBACID)) { | |
254 turn_off(*tp, CANBACID); | |
255 breath = "acid"; | |
256 } | |
257 | |
258 /* Or can it breathe fire */ | |
259 else if (on(*tp, CANBFIRE)) { | |
260 turn_off(*tp, CANBFIRE); | |
261 breath = "flame"; | |
262 } | |
263 | |
264 /* Or can it breathe electricity? */ | |
265 else if (on(*tp, CANBBOLT)) { | |
266 turn_off(*tp, CANBBOLT); | |
267 breath = "lightning bolt"; | |
268 } | |
269 | |
270 /* Or can it breathe gas? */ | |
271 else if (on(*tp, CANBGAS)) { | |
272 turn_off(*tp, CANBGAS); | |
273 breath = "chlorine gas"; | |
274 } | |
275 | |
276 /* Or can it breathe ice? */ | |
277 else if (on(*tp, CANBICE)) { | |
278 turn_off(*tp, CANBICE); | |
279 breath = "ice"; | |
280 } | |
281 | |
282 else if (on(*tp, CANBPGAS)) { | |
283 turn_off(*tp, CANBPGAS); | |
284 breath = "nerve gas"; | |
285 } | |
286 | |
287 /* can it breathe sleeping gas */ | |
288 else if (on(*tp, CANBSGAS)) { | |
289 turn_off(*tp, CANBSGAS); | |
290 breath = "sleeping gas"; | |
291 } | |
292 | |
293 /* can it breathe slow gas */ | |
294 else if (on(*tp, CANBSLGAS)) { | |
295 turn_off(*tp, CANBSLGAS); | |
296 breath = "slow gas"; | |
297 } | |
298 /* can it breathe confusion gas */ | |
299 else if (on(*tp, CANBCGAS)) { | |
300 turn_off(*tp, CANBCGAS); | |
301 breath = "confusion gas"; | |
302 } | |
303 /* can it breathe fear gas */ | |
304 else { | |
305 turn_off(*tp, CANBFGAS); | |
306 breath = "fear gas"; | |
307 } | |
308 | |
309 /* Now breathe -- sets "monst_dead" if it kills someone */ | |
310 *mdead = shoot_bolt( tp, *er, *shoot_dir, FALSE, | |
311 tp->t_index, breath, damage); | |
312 | |
313 ch_ret = *er; | |
314 running = FALSE; | |
315 if (*mdead) return(TRUE); | |
316 } | |
317 | |
318 /* We may shoot missiles if we can */ | |
319 else if (on(*tp, CANMISSILE) && | |
320 (shoot_dir = can_shoot(er, ee)) && | |
321 !on(player, ISINWALL) && | |
322 (rnd(100) < 75)) { | |
323 static struct object missile = | |
324 { | |
325 MISSILE, {0, 0}, "", 0, "", "0d4 " , NULL, 0, WS_MISSILE, 100, 1 | |
326 }; | |
327 | |
328 sprintf(missile.o_hurldmg, "%dd4", tp->t_stats.s_lvl); | |
329 do_motion(&missile, shoot_dir->y, shoot_dir->x, tp); | |
330 hit_monster(unc(missile.o_pos), &missile, tp); | |
331 turn_off(*tp, CANMISSILE); | |
332 ch_ret = *er; | |
333 running = FALSE; | |
334 } | |
335 | |
336 /* We may use a sonic blast if we can */ | |
337 else if (on(*tp, CANSONIC) && | |
338 (dist < BOLT_LENGTH*2) && | |
339 (shoot_dir = can_shoot(er, ee)) && | |
340 !on(player, ISINWALL) && | |
341 (rnd(100) < 50)) { | |
342 static struct object blast = | |
343 { | |
344 MISSILE, {0, 0}, "", 0, "", "150" , NULL, 0, 0, 0, 0 | |
345 }; | |
346 | |
347 turn_off(*tp, CANSONIC); | |
348 do_motion(&blast, shoot_dir->y, shoot_dir->x, tp); | |
349 damage = 150; | |
350 if (save(VS_BREATH, &player, -3)) | |
351 damage /= 2; | |
352 msg ("The %s's sonic blast hits you", monsters[tp->t_index].m_name); | |
353 if ((pstats.s_hpt -= damage) <= 0) | |
354 death(tp->t_index); | |
355 ch_ret = *er; | |
356 running = FALSE; | |
357 } | |
358 /* | |
359 * If we have a special magic item, we might use it. We will restrict | |
360 * this options to uniques with relics for now. | |
361 */ | |
362 else if (on(*tp, ISUNIQUE) && m_use_item(tp, er, ee)) { | |
363 ch_ret = *er; | |
364 running = FALSE; | |
365 } | |
366 /* | |
367 * If we can shoot or throw something, we might do so. | |
368 * If next to player, then 80% prob will fight. | |
369 */ | |
370 else if(on(*tp, CANSHOOT) && | |
371 (shoot_dir = can_shoot(er, ee)) && | |
372 !on(player, ISINWALL) && | |
373 (dist > 3 || (rnd(100) > 80)) && | |
374 (weapon = get_hurl(tp))) { | |
375 missile(shoot_dir->y, shoot_dir->x, weapon, tp); | |
376 ch_ret = *er; | |
377 } | |
378 | |
379 /* | |
380 * Otherwise, find the empty spot next to the chaser that is | |
381 * closest to the chasee. | |
382 */ | |
383 else { | |
384 register int ey, ex, x, y; | |
385 register struct room *rer, *ree; | |
386 int dist_to_old = MININT; /* Dist from goal to old position */ | |
387 | |
388 /* Get rooms */ | |
389 rer = roomin(er); /* Room the chasER (monster) is in */ | |
390 ree = roomin(ee); /* Room the chasEE is in */ | |
391 | |
392 /* | |
393 * This will eventually hold where we move to get closer | |
394 * If we can't find an empty spot, we stay where we are. | |
395 */ | |
396 dist = flee ? 0 : MAXINT; | |
397 ch_ret = *er; | |
398 | |
399 /* Are we at our goal already? */ | |
400 if (!flee && ce(ch_ret, *ee)) return(FALSE); | |
401 | |
402 ey = er->y + 1; | |
403 ex = er->x + 1; | |
404 | |
405 /* Check all possible moves */ | |
406 for (x = er->x - 1; x <= ex; x++) { | |
407 if (x < 0 || x >= COLS) /* Don't try off the board */ | |
408 continue; | |
409 for (y = er->y - 1; y <= ey; y++) { | |
410 coord tryp; | |
411 | |
412 if ((y < 1) || (y >= LINES - 2)) /* Don't try off the board */ | |
413 continue; | |
414 | |
415 /* Don't try the player if not going after the player */ | |
416 if ((flee || !ce(hero, *ee)) && x == hero.x && y == hero.y) { | |
417 next_player = TRUE; | |
418 continue; | |
419 } | |
420 | |
421 tryp.x = x; | |
422 tryp.y = y; | |
423 | |
424 /* Is there a monster on this spot closer to our goal? | |
425 * Don't look in our spot or where we were. | |
426 */ | |
427 if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) && | |
428 isalpha(mch = CCHAR( mvwinch(mw, y, x) ) )) { | |
429 int test_dist; | |
430 | |
431 test_dist = DISTANCE(y, x, ee->y, ee->x); | |
432 if (test_dist <= 25 && /* Let's be fairly close */ | |
433 test_dist < monst_dist) { | |
434 /* Could we really move there? */ | |
435 mvwaddch(mw, y, x, ' '); /* Temporarily blank monst */ | |
436 if (diag_ok(er, &tryp, tp)) monst_dist = test_dist; | |
437 mvwaddch(mw, y, x, mch); /* Restore monster */ | |
438 } | |
439 } | |
440 | |
441 /* Can we move onto the spot? */ | |
442 if (!diag_ok(er, &tryp, tp)) continue; | |
443 | |
444 ch = CCHAR( mvwinch(cw, y, x) ); /* Screen character */ | |
445 | |
446 /* Stepping on player is NOT okay if we are fleeing */ | |
447 if (step_ok(y, x, NOMONST, tp) && | |
448 (off(*tp, ISFLEE) || ch != PLAYER)) | |
449 { | |
450 /* | |
451 * If it is a trap, an intelligent monster may not | |
452 * step on it (unless our hero is on top!) | |
453 */ | |
454 if ((isatrap(ch)) && | |
455 (rnd(10) < tp->t_stats.s_intel) && | |
456 (!on(*tp, ISFLY)) && | |
457 (y != hero.y || x != hero.x)) | |
458 continue; | |
459 | |
460 /* | |
461 * OK -- this place counts | |
462 */ | |
463 thisdist = DISTANCE(y, x, ee->y, ee->x); | |
464 | |
465 /* Adjust distance if we are being shot at */ | |
466 if (tp->t_wasshot && tp->t_stats.s_intel > 5 && | |
467 ce(hero, *ee)) { | |
468 /* Move out of line of sight */ | |
469 if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL)) { | |
470 if (flee) thisdist -= SHOTPENALTY; | |
471 else thisdist += SHOTPENALTY; | |
472 } | |
473 | |
474 /* But do we want to leave the room? */ | |
475 else if (rer && rer == ree && ch == DOOR) | |
476 thisdist += DOORPENALTY; | |
477 } | |
478 | |
479 /* Don't move to the last position if we can help it | |
480 * (unless out prey just moved there) | |
481 */ | |
482 if (ce(tryp, tp->t_oldpos) && (flee || !ce(tryp, hero))) | |
483 dist_to_old = thisdist; | |
484 | |
485 else if ((flee && (thisdist > dist)) || | |
486 (!flee && (thisdist < dist))) | |
487 { | |
488 ch_ret = tryp; | |
489 dist = thisdist; | |
490 } | |
491 } | |
492 } | |
493 } | |
494 | |
495 /* If we aren't trying to get the player, but he is in our way, | |
496 * hit him (unless we have been turned) | |
497 */ | |
498 if (next_player && off(*tp, WASTURNED) && | |
499 ((flee && ce(ch_ret, *er)) || | |
500 (!flee && DISTANCE(er->y, er->x, ee->y, ee->x) < dist)) && | |
501 step_ok(tp->t_dest->y, tp->t_dest->x, NOMONST, tp)) { | |
502 /* Okay to hit player */ | |
503 ch_ret = hero; | |
504 return(FALSE); | |
505 } | |
506 | |
507 | |
508 /* If we can't get closer to the player (if that's our goal) | |
509 * because other monsters are in the way, just stay put | |
510 */ | |
511 if (!flee && ce(hero, *ee) && monst_dist < MAXINT && | |
512 DISTANCE(er->y, er->x, hero.y, hero.x) < dist) | |
513 ch_ret = *er; | |
514 | |
515 /* Do we want to go back to the last position? */ | |
516 else if (dist_to_old != MININT && /* It is possible to move back */ | |
517 ((flee && dist == 0) || /* No other possible moves */ | |
518 (!flee && dist == MAXINT))) { | |
519 /* Do we move back or just stay put (default)? */ | |
520 dist = DISTANCE(er->y, er->x, ee->y, ee->x); /* Current distance */ | |
521 if (!flee || (flee && (dist_to_old > dist))) ch_ret = tp->t_oldpos; | |
522 } | |
523 } | |
524 | |
525 /* May actually hit here from a confused move */ | |
526 return(!ce(ch_ret, hero)); | |
527 } | |
528 | |
529 /* | |
530 * do_chase: | |
531 * Make one thing chase another. | |
532 */ | |
533 | |
534 do_chase(th, flee) | |
535 register struct thing *th; | |
536 register bool flee; /* True if running away or player is inaccessible in wall */ | |
537 { | |
538 register struct room *rer, *ree, /* room of chaser, room of chasee */ | |
539 *orig_rer, /* Original room of chaser */ | |
540 *new_room; /* new room of monster */ | |
541 int dist = MININT; | |
542 int mindist = MAXINT, maxdist = MININT; | |
543 bool stoprun = FALSE, /* TRUE means we are there */ | |
544 rundoor; /* TRUE means run to a door */ | |
545 bool mdead = 0; | |
546 char rch, sch; | |
547 coord *last_door=0, /* Door we just came from */ | |
548 this; /* Temporary destination for chaser */ | |
549 | |
550 /* Make sure the monster can move */ | |
551 if (th->t_no_move != 0) { | |
552 th->t_no_move--; | |
553 return; | |
554 } | |
555 | |
556 rer = roomin(&th->t_pos); /* Find room of chaser */ | |
557 ree = roomin(th->t_dest); /* Find room of chasee */ | |
558 orig_rer = rer; /* Original room of chaser (including doors) */ | |
559 | |
560 /* | |
561 * We don't count monsters on doors as inside rooms for this routine | |
562 */ | |
563 if ((sch = CCHAR( mvwinch(stdscr, th->t_pos.y, th->t_pos.x) )) == DOOR || | |
564 sch == PASSAGE) { | |
565 rer = NULL; | |
566 } | |
567 this = *th->t_dest; | |
568 | |
569 /* | |
570 * If we are not in a corridor and not a Xorn, then if we are running | |
571 * after the player, we run to a door if he is not in the same room. | |
572 * If we are fleeing, we run to a door if he IS in the same room. | |
573 * Note: We don't bother with doors in mazes. | |
574 */ | |
575 if (levtype != MAZELEV && rer != NULL && off(*th, CANINWALL)) { | |
576 if (flee) rundoor = (rer == ree); | |
577 else rundoor = (rer != ree); | |
578 } | |
579 else rundoor = FALSE; | |
580 | |
581 if (rundoor) { | |
582 register struct linked_list *exitptr; /* For looping through exits */ | |
583 coord *exit; /* A particular door */ | |
584 int exity, exitx; /* Door's coordinates */ | |
585 char dch='\0'; /* Door character */ | |
586 | |
587 if (th->t_doorgoal) | |
588 dch = CCHAR( mvwinch(stdscr, th->t_doorgoal->y, th->t_doorgoal->x) ); | |
589 | |
590 /* Do we have a valid goal? */ | |
591 if ((dch == PASSAGE || dch == DOOR) && /* A real door */ | |
592 (!flee || !ce(*th->t_doorgoal, hero))) { /* Player should not | |
593 * be at door if we are | |
594 * running away | |
595 */ | |
596 this = *th->t_doorgoal; | |
597 dist = 0; /* Indicate that we have our door */ | |
598 } | |
599 | |
600 /* Go through all the doors */ | |
601 else for (exitptr = rer->r_exit; exitptr; exitptr = next(exitptr)) { | |
602 exit = DOORPTR(exitptr); | |
603 exity = exit->y; | |
604 exitx = exit->x; | |
605 | |
606 /* Make sure it is a real door */ | |
607 dch = CCHAR( mvwinch(stdscr, exity, exitx) ); | |
608 if (dch == PASSAGE || dch == DOOR) { | |
609 /* Don't count a door if we are fleeing from the player and | |
610 * he is standing on it | |
611 */ | |
612 if (flee && ce(*exit, hero)) continue; | |
613 | |
614 /* Were we just on this door? */ | |
615 if (ce(*exit, th->t_oldpos)) last_door = exit; | |
616 | |
617 else { | |
618 dist = DISTANCE(th->t_dest->y, th->t_dest->x, exity, exitx); | |
619 | |
620 /* If fleeing, we want to maximize distance from door to | |
621 * what we flee, and minimize distance from door to us. | |
622 */ | |
623 if (flee) | |
624 dist -= DISTANCE(th->t_pos.y, th->t_pos.x, exity, exitx); | |
625 | |
626 /* Maximize distance if fleeing, otherwise minimize it */ | |
627 if ((flee && (dist > maxdist)) || | |
628 (!flee && (dist < mindist))) { | |
629 th->t_doorgoal = exit; /* Use this door */ | |
630 this = *exit; | |
631 mindist = maxdist = dist; | |
632 } | |
633 } | |
634 } | |
635 } | |
636 | |
637 /* Could we not find a door? */ | |
638 if (dist == MININT) { | |
639 /* If we were on a door, go ahead and use it */ | |
640 if (last_door) { | |
641 th->t_doorgoal = last_door; | |
642 this = th->t_oldpos; | |
643 dist = 0; /* Indicate that we found a door */ | |
644 } | |
645 else th->t_doorgoal = NULL; /* No more door goal */ | |
646 } | |
647 | |
648 /* Indicate that we do not want to flee from the door */ | |
649 if (dist != MININT) flee = FALSE; | |
650 } | |
651 else th->t_doorgoal = 0; /* Not going to any door */ | |
652 | |
653 /* | |
654 * this now contains what we want to run to this time | |
655 * so we run to it. If we hit it we either want to fight it | |
656 * or stop running | |
657 */ | |
658 if (!chase(th, &this, flee, &mdead)) { | |
659 if (ce(ch_ret, hero)) { | |
660 /* merchants try to sell something --> others attack */ | |
661 if (on(*th, CANSELL)) sell(th); | |
662 else attack(th, NULL, FALSE); | |
663 return; | |
664 } | |
665 else if (on(*th, NOMOVE)) | |
666 stoprun = TRUE; | |
667 } | |
668 | |
669 if (mdead) return; /* Did monster kill someone? */ | |
670 | |
671 if (on(*th, NOMOVE)) return; | |
672 | |
673 /* If we have a scavenger, it can pick something up */ | |
674 if (on(*th, ISSCAVENGE)) { | |
675 register struct linked_list *n_item, *o_item; | |
676 | |
677 while ((n_item = find_obj(ch_ret.y, ch_ret.x)) != NULL) { | |
678 char floor = (roomin(&ch_ret) == NULL) ? PASSAGE : FLOOR; | |
679 register struct object *n_obj, *o_obj; | |
680 | |
681 /* | |
682 * see if he's got one of this group already | |
683 */ | |
684 o_item = NULL; | |
685 n_obj = OBJPTR(n_item); | |
686 detach(lvl_obj, n_item); | |
687 if (n_obj->o_group) { | |
688 for(o_item = th->t_pack; o_item != NULL; o_item = next(o_item)){ | |
689 o_obj = OBJPTR(o_item); | |
690 if (o_obj->o_group == n_obj->o_group) { | |
691 o_obj->o_count += n_obj->o_count; | |
692 o_discard(n_item); | |
693 break; | |
694 } | |
695 } | |
696 } | |
697 if (o_item == NULL) { /* didn't find it */ | |
698 attach(th->t_pack, n_item); | |
699 } | |
700 if (cansee(ch_ret.y, ch_ret.x)) | |
701 mvwaddch(cw, ch_ret.y, ch_ret.x, floor); | |
702 mvaddch(ch_ret.y, ch_ret.x, floor); | |
703 } | |
704 } | |
705 | |
706 mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch); | |
707 sch = CCHAR( mvwinch(cw, ch_ret.y, ch_ret.x) ); /* What player sees */ | |
708 rch = CCHAR( mvwinch(stdscr, ch_ret.y, ch_ret.x) ); /* What's really there */ | |
709 | |
710 /* Get new room of monster */ | |
711 new_room=roomin(&ch_ret); | |
712 | |
713 /* If we have a tunneling monster, it may be making a tunnel */ | |
714 if (on(*th, CANTUNNEL) && | |
715 (rch == SECRETDOOR || rch == WALL || rch == '|' || rch == '-')) { | |
716 char nch; /* The new look to the tunnel */ | |
717 | |
718 if (rch == WALL) nch = PASSAGE; | |
719 else if (levtype == MAZELEV) nch = FLOOR; | |
720 else nch = DOOR; | |
721 addch(nch); | |
722 | |
723 if (cansee(ch_ret.y, ch_ret.x)) sch = nch; /* Can player see this? */ | |
724 | |
725 /* Does this make a new exit? */ | |
726 if (rch == '|' || rch == '-') { | |
727 struct linked_list *newroom; | |
728 coord *exit; | |
729 | |
730 newroom = new_item(sizeof(coord)); | |
731 exit = DOORPTR(newroom); | |
732 *exit = ch_ret; | |
733 attach(new_room->r_exit, newroom); | |
734 } | |
735 } | |
736 | |
737 /* Mark if the monster is inside a wall */ | |
738 if (isrock(mvinch(ch_ret.y, ch_ret.x))) turn_on(*th, ISINWALL); | |
739 else turn_off(*th, ISINWALL); | |
740 | |
741 /* If the monster can illuminate rooms, check for a change */ | |
742 if (on(*th, HASFIRE)) { | |
743 register struct linked_list *fire_item; | |
744 | |
745 /* Is monster entering a room? */ | |
746 if (orig_rer != new_room && new_room != NULL) { | |
747 fire_item = creat_item(); /* Get an item-only structure */ | |
748 ldata(fire_item) = (char *) th; | |
749 | |
750 attach(new_room->r_fires, fire_item); | |
751 new_room->r_flags |= HASFIRE; | |
752 | |
753 if (cansee(ch_ret.y, ch_ret.x) && next(new_room->r_fires) == NULL) | |
754 light(&hero); | |
755 } | |
756 | |
757 /* Is monster leaving a room? */ | |
758 if (orig_rer != new_room && orig_rer != NULL) { | |
759 /* Find the bugger in the list and delete him */ | |
760 for (fire_item = orig_rer->r_fires; fire_item != NULL; | |
761 fire_item = next(fire_item)) { | |
762 if (THINGPTR(fire_item) == th) { /* Found him! */ | |
763 detach(orig_rer->r_fires, fire_item); | |
764 destroy_item(fire_item); | |
765 if (orig_rer->r_fires == NULL) { | |
766 orig_rer->r_flags &= ~HASFIRE; | |
767 if (cansee(th->t_pos.y, th->t_pos.x)) | |
768 light(&th->t_pos); | |
769 } | |
770 break; | |
771 } | |
772 } | |
773 } | |
774 } | |
775 | |
776 /* If monster is entering player's room and player can see it, | |
777 * stop the player's running. | |
778 */ | |
779 if (new_room != orig_rer && new_room != NULL && | |
780 new_room == ree && cansee(unc(ch_ret)) && | |
781 (off(*th, ISINVIS) || on(player, CANSEE)) && | |
782 (off(*th, ISSHADOW) || on(player, CANSEE)) && | |
783 (off(*th, CANSURPRISE) || ISWEARING(R_ALERT))) | |
784 running = FALSE; | |
785 | |
786 /* | |
787 if (rer != NULL && !lit_room(orig_rer) && sch == FLOOR && | |
788 DISTANCE(ch_ret.y, ch_ret.x, th->t_pos.y, th->t_pos.x) < 3 && | |
789 off(player, ISBLIND)) | |
790 th->t_oldch = ' '; | |
791 else | |
792 */ | |
793 th->t_oldch = sch; | |
794 | |
795 /* Let's display those creatures that we can see. */ | |
796 if (cansee(unc(ch_ret)) && | |
797 off(*th, ISINWALL) && | |
798 !invisible(th)) | |
799 mvwaddch(cw, ch_ret.y, ch_ret.x, th->t_type); | |
800 | |
801 /* | |
802 * Blank out the old position and record the new position -- | |
803 * the blanking must be done first in case the positions are the same. | |
804 */ | |
805 mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' '); | |
806 mvwaddch(mw, ch_ret.y, ch_ret.x, th->t_type); | |
807 | |
808 /* Record monster's last position (if new one is different) */ | |
809 if (!ce(ch_ret, th->t_pos)) th->t_oldpos = th->t_pos; | |
810 th->t_pos = ch_ret; /* Mark the monster's new position */ | |
811 | |
812 /* If the monster is on a trap, trap it */ | |
813 sch = CCHAR( mvinch(ch_ret.y, ch_ret.x) ); | |
814 if (isatrap(sch)) { | |
815 if (cansee(ch_ret.y, ch_ret.x)) th->t_oldch = sch; | |
816 be_trapped(th, &ch_ret); | |
817 } | |
818 | |
819 | |
820 /* | |
821 * And stop running if need be | |
822 */ | |
823 if (stoprun && ce(th->t_pos, *(th->t_dest))) | |
824 turn_off(*th, ISRUN); | |
825 } | |
826 | |
827 | |
828 /* | |
829 * Get_hurl returns the weapon that the monster will "throw" if he has one | |
830 */ | |
831 | |
832 struct linked_list * | |
833 get_hurl(tp) | |
834 register struct thing *tp; | |
835 { | |
836 struct linked_list *arrow, *bolt, *rock; | |
837 register struct linked_list *pitem; | |
838 bool bow=FALSE, crossbow=FALSE, sling=FALSE; | |
839 | |
840 arrow = bolt = rock = NULL; /* Don't point to anything to begin with */ | |
841 for (pitem=tp->t_pack; pitem; pitem=next(pitem)) | |
842 if ((OBJPTR(pitem))->o_type == WEAPON) | |
843 switch ((OBJPTR(pitem))->o_which) { | |
844 case BOW: bow = TRUE; | |
845 when CROSSBOW: crossbow = TRUE; | |
846 when SLING: sling = TRUE; | |
847 when ROCK: rock = pitem; | |
848 when ARROW: arrow = pitem; | |
849 when BOLT: bolt = pitem; | |
850 } | |
851 | |
852 /* Use crossbow bolt if possible */ | |
853 if (crossbow && bolt) return(bolt); | |
854 if (bow && arrow) return(arrow); | |
855 if (sling && rock) return(rock); | |
856 return(NULL); | |
857 } | |
858 | |
859 /* | |
860 * runners: | |
861 * Make all the running monsters move. | |
862 */ | |
863 | |
864 runners() | |
865 { | |
866 register struct linked_list *item; | |
867 register struct thing *tp = NULL; | |
868 | |
869 /* | |
870 * loop thru the list of running (wandering) monsters and see what | |
871 * each one will do this time. | |
872 * | |
873 * Note: the special case that one of this buggers kills another. | |
874 * if this happens than we have to see if the monster killed | |
875 * himself or someone else. In case its himself we have to get next | |
876 * one immediately. If it wasn't we have to get next one at very | |
877 * end in case he killed the next one. | |
878 */ | |
879 | |
880 for (item = mlist; item != NULL; item = next(item)) { | |
881 tp = THINGPTR(item); | |
882 turn_on(*tp, ISREADY); | |
883 } | |
884 | |
885 for (;;) { | |
886 for (item = mlist; item != NULL; item = next(item)) { | |
887 tp = THINGPTR(item); | |
888 | |
889 if (on(*tp, ISREADY)) | |
890 break; | |
891 } | |
892 | |
893 if (item == NULL) | |
894 break; | |
895 | |
896 turn_off(*tp, ISREADY); | |
897 | |
898 if (on(*tp, ISHELD) && rnd(tp->t_stats.s_lvl) > 11) { | |
899 turn_off(*tp, ISHELD); | |
900 turn_on(*tp, ISRUN); | |
901 turn_off(*tp, ISDISGUISE); | |
902 tp->t_dest = &hero; | |
903 if (tp->t_stats.s_hpt < tp->maxstats.s_hpt) | |
904 turn_on(*tp, ISFLEE); | |
905 if (cansee(tp->t_pos.y, tp->t_pos.x)) | |
906 msg("The %s breaks free from the hold spell", | |
907 monsters[tp->t_index].m_name); | |
908 } | |
909 if (off(*tp, ISHELD) && on(*tp, ISRUN)) { | |
910 register bool flee; | |
911 | |
912 /* Should monster run away? */ | |
913 flee = on(*tp, ISFLEE) || | |
914 ((tp->t_dest == &hero) && on(player, ISINWALL) && | |
915 off(*tp, CANINWALL)); | |
916 | |
917 if (off(*tp, ISSLOW) || tp->t_turn) { | |
918 doctor(tp); | |
919 do_chase(tp, flee); | |
920 } | |
921 if (off(*tp, ISDEAD) && off(*tp, ISELSEWHERE) && on(*tp, ISHASTE)) { | |
922 doctor(tp); | |
923 do_chase(tp, flee); | |
924 } | |
925 if (off(*tp, ISDEAD) && off(*tp, ISELSEWHERE)) { | |
926 tp->t_turn ^= TRUE; | |
927 tp->t_wasshot = FALSE; /* Not shot anymore */ | |
928 } | |
929 } | |
930 } | |
931 } | |
932 | |
933 /* | |
934 * runto: | |
935 * Set a monster running after something | |
936 */ | |
937 | |
938 runto(runner, spot) | |
939 register struct thing *runner; | |
940 coord *spot; | |
941 { | |
942 /* | |
943 * Start the beastie running | |
944 */ | |
945 runner->t_dest = spot; | |
946 turn_on(*runner, ISRUN); | |
947 turn_off(*runner, ISDISGUISE); | |
948 } | |
949 | |
950 | |
951 | |
952 /* | |
953 * straight_shot: | |
954 * See if there is a straight line of sight between the two | |
955 * given coordinates. If shooting is not NULL, it is a pointer | |
956 * to a structure which should be filled with the direction | |
957 * to shoot (if there is a line of sight). If shooting, monsters | |
958 * get in the way. Otherwise, they do not. | |
959 */ | |
960 | |
961 bool | |
962 straight_shot(ery, erx, eey, eex, shooting) | |
963 register int ery, erx, eey, eex; | |
964 register coord *shooting; | |
965 { | |
966 register int dy, dx; /* Deltas */ | |
967 char ch; | |
968 | |
969 /* Does the monster have a straight shot at player */ | |
970 if ((ery != eey) && (erx != eex) && | |
971 (abs(ery - eey) != abs(erx - eex))) return(FALSE); | |
972 | |
973 /* Get the direction to shoot */ | |
974 if (eey > ery) dy = 1; | |
975 else if (eey == ery) dy = 0; | |
976 else dy = -1; | |
977 | |
978 if (eex > erx) dx = 1; | |
979 else if (eex == erx) dx = 0; | |
980 else dx = -1; | |
981 | |
982 /* Make sure we have free area all the way to the player */ | |
983 ery += dy; | |
984 erx += dx; | |
985 while ((ery != eey) || (erx != eex)) { | |
986 switch (ch = CCHAR( winat(ery, erx) )) { | |
987 case '|': | |
988 case '-': | |
989 case WALL: | |
990 case DOOR: | |
991 case SECRETDOOR: | |
992 case FOREST: | |
993 return(FALSE); | |
994 default: | |
995 if (shooting && isalpha(ch)) return(FALSE); | |
996 } | |
997 ery += dy; | |
998 erx += dx; | |
999 } | |
1000 | |
1001 if (shooting) { /* If we are shooting -- put in the directions */ | |
1002 shooting->y = dy; | |
1003 shooting->x = dx; | |
1004 } | |
1005 return(TRUE); | |
1006 } | |
1007 | |
1008 |