Mercurial > hg > early-roguelike
comparison urogue/chase.c @ 256:c495a4f288c6
Import UltraRogue from the Roguelike Restoration Project (r1490)
author | John "Elwin" Edwards |
---|---|
date | Tue, 31 Jan 2017 19:56:04 -0500 |
parents | |
children | 0250220d8cdd |
comparison
equal
deleted
inserted
replaced
253:d9badb9c0179 | 256:c495a4f288c6 |
---|---|
1 /* | |
2 chase.c - Code for one creature to chase another | |
3 | |
4 UltraRogue: The Ultimate Adventure in the Dungeons of Doom | |
5 Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong | |
6 All rights reserved. | |
7 | |
8 Based on "Advanced Rogue" | |
9 Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka | |
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 <stdlib.h> | |
20 #include <ctype.h> | |
21 #include <limits.h> | |
22 #include "rogue.h" | |
23 | |
24 /* | |
25 do_chase() | |
26 Make one thing chase another. | |
27 */ | |
28 | |
29 void | |
30 do_chase(struct thing *th, int flee) | |
31 { | |
32 struct room *rer; /* room of chaser */ | |
33 struct room *ree; /* room of chasee */ | |
34 struct room *old_room; /* old room of monster */ | |
35 struct room *new_room; /* new room of monster */ | |
36 | |
37 int i, mindist = INT_MAX, maxdist = INT_MIN, dist = INT_MIN; | |
38 | |
39 int last_door = -1; /* Door we just came from */ | |
40 int stoprun = FALSE; /* TRUE means we are there */ | |
41 int rundoor; /* TRUE means run to a door */ | |
42 int hit_bad = FALSE; /* TRUE means hit bad monster */ | |
43 int mon_attack; /* TRUE means find a monster to hit */ | |
44 | |
45 char sch; | |
46 struct linked_list *item; | |
47 coord this; /* Temporary destination for chaser */ | |
48 | |
49 if (!th->t_ischasing) | |
50 return; | |
51 | |
52 /* Make sure the monster can move */ | |
53 | |
54 if (th->t_no_move != 0) | |
55 { | |
56 th->t_no_move--; | |
57 return; | |
58 } | |
59 | |
60 /* | |
61 * Bad monsters check for a good monster to hit, friendly monsters | |
62 * check for a bad monster to hit. | |
63 */ | |
64 | |
65 mon_attack = FALSE; | |
66 | |
67 if (good_monster(*th)) | |
68 { | |
69 hit_bad = TRUE; | |
70 mon_attack = TRUE; | |
71 } | |
72 else if (on(*th, ISMEAN)) | |
73 { | |
74 hit_bad = FALSE; | |
75 mon_attack = TRUE; | |
76 } | |
77 | |
78 if (mon_attack) | |
79 { | |
80 struct linked_list *mon_to_hit; | |
81 | |
82 mon_to_hit = f_mons_a(th->t_pos.y, th->t_pos.x, hit_bad); | |
83 | |
84 if (mon_to_hit) | |
85 { | |
86 mon_mon_attack(th, mon_to_hit, pick_weap(th), NOTHROWN); | |
87 return; | |
88 } | |
89 } | |
90 | |
91 /* no nearby monster to hit */ | |
92 | |
93 rer = roomin(th->t_pos); /* Find room of chaser */ | |
94 ree = roomin(th->t_chasee->t_pos); /* Find room of chasee */ | |
95 | |
96 /* | |
97 * We don't count doors as inside rooms for this routine | |
98 */ | |
99 | |
100 if (mvwinch(stdscr, th->t_pos.y, th->t_pos.x) == DOOR) | |
101 rer = NULL; | |
102 | |
103 this = th->t_chasee->t_pos; | |
104 | |
105 /* | |
106 * If we are not in a corridor and not a phasing monster, then if we | |
107 * are running after the player, we run to a door if he is not in the | |
108 * same room. If we are fleeing, we run to a door if he IS in the | |
109 * same room. Note: We don't bother with doors in mazes. Phasing | |
110 * monsters don't need to look for doors. There are no doors in mazes | |
111 * and throne rooms. | |
112 */ | |
113 | |
114 if (levtype != MAZELEV && levtype != THRONE && rer != NULL && off(*th, CANINWALL)) | |
115 { | |
116 if (flee) | |
117 rundoor = (rer == ree); | |
118 else | |
119 rundoor = (rer != ree); | |
120 } | |
121 else | |
122 rundoor = FALSE; | |
123 | |
124 if (rundoor) | |
125 { | |
126 coord d_exit; /* A particular door */ | |
127 int exity, exitx; /* Door's coordinates */ | |
128 | |
129 if (th->t_doorgoal != -1) | |
130 { /* Do we already have the goal? */ | |
131 this = rer->r_exit[th->t_doorgoal]; | |
132 dist = 0; /* Indicate that we have our door */ | |
133 } | |
134 else | |
135 for (i = 0; i < rer->r_nexits; i++) | |
136 { /* Loop through doors */ | |
137 d_exit = rer->r_exit[i]; | |
138 exity = d_exit.y; | |
139 exitx = d_exit.x; | |
140 | |
141 /* Avoid secret doors */ | |
142 if (mvwinch(stdscr, exity, exitx) == DOOR) | |
143 { | |
144 /* Were we just on this door? */ | |
145 if (ce(d_exit, th->t_oldpos)) | |
146 last_door = i; | |
147 else | |
148 { | |
149 dist = DISTANCE(th->t_chasee->t_pos, d_exit); | |
150 | |
151 /* | |
152 * If fleeing, we want to | |
153 * maximize distance from | |
154 * door to what we flee, and | |
155 * minimize distance from | |
156 * door to us. | |
157 */ | |
158 | |
159 if (flee) | |
160 dist-=DISTANCE(th->t_pos,d_exit); | |
161 | |
162 /* | |
163 * Maximize distance if | |
164 * fleeing, otherwise | |
165 * minimize it | |
166 */ | |
167 | |
168 if ((flee && (dist > maxdist)) || | |
169 (!flee && (dist < mindist))) | |
170 { | |
171 th->t_doorgoal = i; /* Use this door */ | |
172 this = d_exit; | |
173 mindist = maxdist = dist; | |
174 } | |
175 } | |
176 } | |
177 } | |
178 | |
179 /* Could we not find a door? */ | |
180 if (dist == INT_MIN) | |
181 { | |
182 /* If we were on a door, go ahead and use it */ | |
183 if (last_door != -1) | |
184 { | |
185 th->t_doorgoal = last_door; | |
186 this = th->t_oldpos; | |
187 dist = 0; /* Indicate that we found a door */ | |
188 } | |
189 } | |
190 | |
191 /* Indicate that we do not want to flee from the door */ | |
192 if (dist != INT_MIN) | |
193 flee = FALSE; | |
194 } | |
195 else | |
196 th->t_doorgoal = -1; /* Not going to any door */ | |
197 | |
198 /* | |
199 * this now contains what we want to run to this time so we run to | |
200 * it. If we hit it we either want to fight it or stop running | |
201 */ | |
202 | |
203 if (!chase(th, &this, flee)) | |
204 { | |
205 if (ce(th->t_nxtpos, hero)) | |
206 { | |
207 /* merchants try to sell something */ | |
208 | |
209 if (on(*th, CANSELL)) | |
210 { | |
211 sell(th); | |
212 return; | |
213 } | |
214 else if (off(*th, ISFRIENDLY) && off(*th, ISCHARMED) | |
215 && (off(*th, CANFLY) || (on(*th, CANFLY) && rnd(2)))) | |
216 attack(th, pick_weap(th), FALSE); | |
217 return; | |
218 } | |
219 else if (on(*th, NOMOVE)) | |
220 stoprun = TRUE; | |
221 } | |
222 | |
223 if (!curr_mons) | |
224 return; /* Did monster get itself killed? */ | |
225 | |
226 if (on(*th, NOMOVE)) | |
227 return; | |
228 | |
229 /* If we have a scavenger, it can pick something up */ | |
230 | |
231 if ((item = find_obj(th->t_nxtpos.y, th->t_nxtpos.x)) != NULL) | |
232 { | |
233 struct linked_list *node, *top = item; | |
234 struct object *obt; | |
235 | |
236 while(top) | |
237 { | |
238 /* grab all objects that qualify */ | |
239 | |
240 struct object *obj = OBJPTR(item); | |
241 | |
242 obt = OBJPTR(top); | |
243 node = obt->next_obj; | |
244 | |
245 if (on(*th, ISSCAVENGE) || | |
246 ((on(*th, CANWIELD) || on(*th, CANSHOOT)) && | |
247 (obj->o_type == WEAPON || obj->o_type == ARMOR)) || | |
248 (on(*th, CANCAST) && is_magic(obj))) | |
249 { | |
250 rem_obj(top, FALSE); | |
251 attach(th->t_pack, top); | |
252 } | |
253 | |
254 top = node; | |
255 } | |
256 | |
257 light(&hero); | |
258 } | |
259 | |
260 mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch); | |
261 sch = CCHAR( mvwinch(cw, th->t_nxtpos.y, th->t_nxtpos.x) ); | |
262 | |
263 /* Get old and new room of monster */ | |
264 old_room = roomin(th->t_pos); | |
265 new_room = roomin(th->t_nxtpos); | |
266 | |
267 /* If the monster can illuminate rooms, check for a change */ | |
268 if (on(*th, HASFIRE)) | |
269 { | |
270 /* Is monster entering a room? */ | |
271 if (old_room != new_room && new_room != NULL) | |
272 { | |
273 new_room->r_flags |= HASFIRE; | |
274 new_room->r_fires++; | |
275 if (cansee(th->t_nxtpos.y, th->t_nxtpos.x) && new_room->r_fires==1) | |
276 light(&hero); | |
277 } | |
278 | |
279 /* Is monster leaving a room? */ | |
280 if (old_room != new_room && old_room != NULL) | |
281 { | |
282 if (--(old_room->r_fires) <= 0) | |
283 { | |
284 old_room->r_flags &= ~HASFIRE; | |
285 if (cansee(th->t_pos.y, th->t_pos.x)) | |
286 light(&th->t_pos); | |
287 } | |
288 } | |
289 } | |
290 | |
291 /* | |
292 * If monster is entering player's room and player can see it, stop | |
293 * the player's running. | |
294 */ | |
295 | |
296 if (new_room != old_room && new_room != NULL && | |
297 new_room == ree && cansee(th->t_nxtpos.y, th->t_nxtpos.x) && | |
298 (off(*th, ISINVIS) || (off(*th, ISSHADOW) || rnd(10) == 0) || | |
299 on(player, CANSEE)) && off(*th, CANSURPRISE)) | |
300 running = FALSE; | |
301 | |
302 if (rer != NULL && (rer->r_flags & ISDARK) && | |
303 !(rer->r_flags & HASFIRE) && sch == FLOOR && | |
304 DISTANCE(th->t_nxtpos, th->t_pos) < see_dist && | |
305 off(player, ISBLIND)) | |
306 th->t_oldch = ' '; | |
307 else | |
308 th->t_oldch = sch; | |
309 | |
310 if (cansee(th->t_nxtpos.y, th->t_nxtpos.x) && | |
311 off(*th, ISINWALL) && | |
312 ((off(*th, ISINVIS) && (off(*th, ISSHADOW) || rnd(100) < 10)) || | |
313 on(player, CANSEE)) && | |
314 off(*th, CANSURPRISE)) | |
315 mvwaddch(cw, th->t_nxtpos.y, th->t_nxtpos.x, th->t_type); | |
316 | |
317 mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' '); | |
318 mvwaddch(mw, th->t_nxtpos.y, th->t_nxtpos.x, th->t_type); | |
319 | |
320 /* Record monster's last position (if new one is different) */ | |
321 | |
322 if (!ce(th->t_nxtpos, th->t_pos)) | |
323 th->t_oldpos = th->t_pos; | |
324 | |
325 th->t_pos = th->t_nxtpos; /* Mark the monster's new position */ | |
326 | |
327 /* If the monster is on a trap, trap it */ | |
328 | |
329 sch = CCHAR(mvinch(th->t_nxtpos.y, th->t_nxtpos.x)); | |
330 | |
331 if (isatrap(sch)) | |
332 { | |
333 debug("Monster trapped by %c.", sch); | |
334 | |
335 if (cansee(th->t_nxtpos.y, th->t_nxtpos.x)) | |
336 th->t_oldch = sch; | |
337 | |
338 be_trapped(th, th->t_nxtpos); | |
339 } | |
340 | |
341 /* And stop running if need be */ | |
342 | |
343 if (stoprun && ce(th->t_pos, th->t_chasee->t_pos)) | |
344 { | |
345 th->t_ischasing = FALSE; | |
346 turn_off(*th, ISRUN); | |
347 } | |
348 } | |
349 | |
350 /* | |
351 chase_it() | |
352 Set a monster running after something or stop it from running (for | |
353 when it dies) | |
354 */ | |
355 | |
356 void | |
357 chase_it(coord *runner, struct thing *th) | |
358 { | |
359 struct linked_list *item; | |
360 struct thing *tp; | |
361 | |
362 /* If we couldn't find him, something is funny */ | |
363 | |
364 if ((item = find_mons(runner->y, runner->x)) == NULL) | |
365 { | |
366 debug("CHASER '%s'", unctrl(winat(runner->y, runner->x))); | |
367 return; | |
368 } | |
369 | |
370 tp = THINGPTR(item); | |
371 | |
372 /* Start the beastie running */ | |
373 | |
374 tp->t_ischasing = TRUE; | |
375 tp->t_chasee = th; | |
376 | |
377 turn_on(*tp, ISRUN); | |
378 turn_off(*tp, ISDISGUISE); | |
379 | |
380 return; | |
381 } | |
382 | |
383 /* | |
384 chase() | |
385 Find the spot for the chaser(er) to move closer to the chasee(ee). | |
386 Returns TRUE if we want to keep on chasing later, FALSE if we reach the | |
387 goal. | |
388 */ | |
389 | |
390 int | |
391 chase(struct thing *tp, coord *ee, int flee) | |
392 { | |
393 int x, y; | |
394 int dist, thisdist, monst_dist = INT_MAX; | |
395 struct linked_list *weapon; | |
396 coord *er = &tp->t_pos; | |
397 coord shoot; | |
398 coord *shootit_dir = NULL; | |
399 int ch; | |
400 char mch; | |
401 int next_player = FALSE; | |
402 | |
403 /* Take care of shooting directions */ | |
404 | |
405 if (on(*tp, CANBREATHE) || on(*tp, CANSHOOT) || on(*tp, CANCAST)) | |
406 { | |
407 if (good_monster(*tp)) | |
408 { | |
409 shootit_dir = find_shoot(tp, &shoot); /* find a mean monster */ | |
410 | |
411 if (wizard && shootit_dir) | |
412 msg("Found monster to attack towards (%d,%d).", | |
413 shootit_dir->x, shootit_dir->y); | |
414 } | |
415 else | |
416 shootit_dir = can_shoot(er, ee, &shoot); /* shoot hero */ | |
417 } | |
418 | |
419 /* | |
420 * If the thing is confused, let it move randomly. Some monsters are | |
421 * slightly confused all of the time. | |
422 */ | |
423 | |
424 if ((on(*tp, ISHUH) && rnd(10) < 8) || | |
425 ((on(*tp, ISINVIS) || on(*tp, ISSHADOW)) && rnd(100) < 20) || | |
426 (on(player, ISINVIS) && off(*tp, CANSEE))) | |
427 { /* Player is invisible */ | |
428 | |
429 /* get a valid random move */ | |
430 | |
431 tp->t_nxtpos = rndmove(tp); | |
432 | |
433 dist = DISTANCE(tp->t_nxtpos, *ee); | |
434 | |
435 if (on(*tp, ISHUH) && rnd(20) == 0) /* monster might lose confusion */ | |
436 turn_off(*tp, ISHUH); | |
437 | |
438 /* | |
439 * check to see if random move takes creature away from | |
440 * player if it does then turn off ISHELD | |
441 */ | |
442 | |
443 if (dist > 1 && on(*tp, DIDHOLD)) | |
444 { | |
445 turn_off(*tp, DIDHOLD); | |
446 turn_on(*tp, CANHOLD); | |
447 | |
448 if (--hold_count == 0) | |
449 turn_off(player, ISHELD); | |
450 } | |
451 } /* If we can breathe, we may do so */ | |
452 else if (on(*tp, CANBREATHE) && (shootit_dir) && (rnd(100) < 67) && | |
453 (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)) && | |
454 (DISTANCE(*er, *ee) < BOLT_LENGTH * BOLT_LENGTH)) | |
455 { | |
456 int chance; | |
457 char *breath; | |
458 | |
459 /* Will it breathe at random */ | |
460 | |
461 if (on(*tp, CANBRANDOM)) | |
462 { | |
463 if (rnd(level / 20) == 0 && tp->t_index != nummonst + 1 | |
464 && !(good_monster(*tp))) | |
465 turn_off(*tp, CANBRANDOM); | |
466 | |
467 /* Select type of breath */ | |
468 | |
469 chance = rnd(100); | |
470 | |
471 if (chance < 11) | |
472 breath = "acid"; | |
473 else if (chance < 22) | |
474 breath = "flame"; | |
475 else if (chance < 33) | |
476 breath = "lightning bolt"; | |
477 else if (chance < 44) | |
478 breath = "chlorine gas"; | |
479 else if (chance < 55) | |
480 breath = "ice"; | |
481 else if (chance < 66) | |
482 breath = "nerve gas"; | |
483 else if (chance < 77) | |
484 breath = "sleeping gas"; | |
485 else if (chance < 88) | |
486 breath = "slow gas"; | |
487 else | |
488 breath = "fear gas"; | |
489 } /* Or can it breathe acid? */ | |
490 else if (on(*tp, CANBACID)) | |
491 { | |
492 if (!good_monster(*tp) && rnd(level / 15) == 0) | |
493 turn_off(*tp, CANBACID); | |
494 | |
495 breath = "acid"; | |
496 } /* Or can it breathe fire */ | |
497 else if (on(*tp, CANBFIRE)) | |
498 { | |
499 if (!good_monster(*tp) && rnd(level / 15) == 0) | |
500 turn_off(*tp, CANBFIRE); | |
501 | |
502 breath = "flame"; | |
503 } /* Or can it breathe electricity? */ | |
504 else if (on(*tp, CANBBOLT)) | |
505 { | |
506 if (!good_monster(*tp) && rnd(level / 15) == 0) | |
507 turn_off(*tp, CANBBOLT); | |
508 | |
509 breath = "lightning bolt"; | |
510 } /* Or can it breathe gas? */ | |
511 else if (on(*tp, CANBGAS)) | |
512 { | |
513 if (!good_monster(*tp) && rnd(level / 15) == 0) | |
514 turn_off(*tp, CANBGAS); | |
515 | |
516 breath = "chlorine gas"; | |
517 } /* Or can it breathe ice? */ | |
518 else if (on(*tp, CANBICE)) | |
519 { | |
520 if (!good_monster(*tp) && rnd(level / 15) == 0) | |
521 turn_off(*tp, CANBICE); | |
522 | |
523 breath = "ice"; | |
524 } | |
525 else if (on(*tp, CANBPGAS)) | |
526 { | |
527 if (!good_monster(*tp) && rnd(level / 15) == 0) | |
528 turn_off(*tp, CANBPGAS); | |
529 | |
530 breath = "nerve gas"; | |
531 } | |
532 else if (on(*tp, CANBSGAS)) | |
533 { | |
534 if (!good_monster(*tp) && rnd(level / 15) == 0) | |
535 turn_off(*tp, CANBSGAS); | |
536 | |
537 breath = "sleeping gas"; | |
538 } | |
539 else if (on(*tp, CANBSLGAS)) | |
540 { | |
541 if (!good_monster(*tp) && rnd(level / 15) == 0) | |
542 turn_off(*tp, CANBSLGAS); | |
543 | |
544 breath = "slow gas"; | |
545 } | |
546 else | |
547 { | |
548 if (!good_monster(*tp) && rnd(level / 15) == 0) | |
549 turn_off(*tp, CANBFGAS); | |
550 | |
551 breath = "fear gas"; | |
552 } | |
553 | |
554 shoot_bolt(tp, *er, *shootit_dir, (tp == THINGPTR(fam_ptr)), | |
555 tp->t_index, breath, roll(tp->t_stats.s_lvl, 6)); | |
556 | |
557 tp->t_nxtpos = *er; | |
558 | |
559 dist = DISTANCE(tp->t_nxtpos, *ee); | |
560 | |
561 if (!curr_mons) | |
562 return (TRUE); | |
563 } | |
564 else if (shootit_dir && on(*tp, CANCAST) && | |
565 (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6))) | |
566 { | |
567 /* | |
568 If we can cast spells we might do so - even if adjacent fleeing | |
569 monsters are restricted to certain spells | |
570 */ | |
571 | |
572 incant(tp, *shootit_dir); | |
573 tp->t_nxtpos = *er; | |
574 dist = DISTANCE(tp->t_nxtpos, *ee); | |
575 } | |
576 else if (shootit_dir && on(*tp, CANSHOOT)) | |
577 { | |
578 weapon = get_hurl(tp); | |
579 | |
580 if (weapon && | |
581 (off(*tp, ISFLEE) || rnd(DISTANCE(*er, *ee)) > 2) && | |
582 (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6))) | |
583 { | |
584 /* | |
585 Should we shoot or throw something? fleeing monsters | |
586 may to shoot anyway if far enough away | |
587 */ | |
588 | |
589 missile(shootit_dir->y, shootit_dir->x, weapon, tp); | |
590 tp->t_nxtpos = *er; | |
591 dist = DISTANCE(tp->t_nxtpos, *ee); | |
592 } | |
593 } | |
594 else | |
595 { | |
596 /* | |
597 Otherwise, find the empty spot next to the chaser that is closest | |
598 to the chasee. | |
599 */ | |
600 int ey, ex; | |
601 struct room *rer, *ree; | |
602 int dist_to_old = INT_MIN; /* Dist from goal to old position */ | |
603 | |
604 /* Get rooms */ | |
605 rer = roomin(*er); | |
606 ree = roomin(*ee); | |
607 | |
608 /* | |
609 * This will eventually hold where we move to get closer. If | |
610 * we can't find an empty spot, we stay where we are. | |
611 */ | |
612 | |
613 dist = flee ? 0 : INT_MAX; | |
614 tp->t_nxtpos = *er; | |
615 | |
616 /* Are we at our goal already? */ | |
617 | |
618 if (!flee && ce(tp->t_nxtpos, *ee)) | |
619 return (FALSE); | |
620 | |
621 ey = er->y + 1; | |
622 ex = er->x + 1; | |
623 | |
624 for (x = er->x - 1; x <= ex; x++) | |
625 for (y = er->y - 1; y <= ey; y++) | |
626 { | |
627 coord tryp; /* test position */ | |
628 | |
629 /* Don't try off the screen */ | |
630 | |
631 if ((x < 0) || (x >= COLS) || (y < 1) || (y >= LINES - 2)) | |
632 continue; | |
633 | |
634 /* | |
635 * Don't try the player if not going after | |
636 * the player or he's disguised and monster is dumb | |
637 */ | |
638 | |
639 if (((off(*tp, ISFLEE) && !ce(hero, *ee)) || | |
640 (on(player, ISDISGUISE) && (rnd(tp->t_stats.s_lvl) < 6)) | |
641 || good_monster(*tp)) | |
642 && x == hero.x && y == hero.y) | |
643 continue; | |
644 | |
645 tryp.x = x; | |
646 tryp.y = y; | |
647 | |
648 /* | |
649 * Is there a monster on this spot closer to | |
650 * our goal? Don't look in our spot or where | |
651 * we were. | |
652 */ | |
653 | |
654 if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) && | |
655 isalpha( (mch = CCHAR(mvwinch(mw, y, x))) ) ) | |
656 { | |
657 int test_dist; | |
658 | |
659 test_dist = DISTANCE(tryp,*ee); | |
660 if (test_dist <= 25 && /* Let's be fairly close */ | |
661 test_dist < monst_dist) | |
662 { | |
663 | |
664 /* Could we really move there? */ | |
665 | |
666 mvwaddch(mw, y, x, ' '); /* Temp blank monst */ | |
667 | |
668 if (diag_ok(er, &tryp, tp)) | |
669 monst_dist = test_dist; | |
670 | |
671 mvwaddch(mw, y, x, mch); /* Restore monster */ | |
672 } | |
673 } | |
674 | |
675 if (!diag_ok(er, &tryp, tp)) | |
676 continue; | |
677 | |
678 ch = mvwinch(cw, y, x); /* Screen character */ | |
679 | |
680 /* | |
681 * Stepping on player is NOT okay if we are | |
682 * fleeing | |
683 */ | |
684 | |
685 if (on(*tp, ISFLEE) && (ch == PLAYER)) | |
686 next_player = TRUE; | |
687 | |
688 if (step_ok(y, x, NOMONST, tp) && | |
689 (off(*tp, ISFLEE) || ch != PLAYER)) | |
690 { | |
691 | |
692 /* | |
693 * If it is a trap, an intelligent | |
694 * monster may not step on it (unless | |
695 * our hero is on top!) | |
696 */ | |
697 | |
698 if (isatrap(ch)) | |
699 { | |
700 if (!(ch == RUSTTRAP) && | |
701 !(ch == FIRETRAP && on(*tp, NOFIRE)) && | |
702 rnd(10) < tp->t_stats.s_intel && | |
703 (y != hero.y || x != hero.x)) | |
704 continue; | |
705 } | |
706 | |
707 /* | |
708 * OK -- this place counts | |
709 */ | |
710 | |
711 thisdist = DISTANCE(tryp, *ee); | |
712 | |
713 /* | |
714 * Adjust distance if we are being | |
715 * shot at to moving out of line of sight. | |
716 */ | |
717 | |
718 if (tp->t_wasshot && tp->t_stats.s_intel > 5 && | |
719 ce(hero, *ee)) | |
720 { | |
721 /* Move out of line of sight */ | |
722 if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL)) | |
723 { | |
724 if (flee) | |
725 thisdist -= SHOTPENALTY; | |
726 else | |
727 thisdist += SHOTPENALTY; | |
728 } | |
729 | |
730 /* | |
731 * But do we want to leave | |
732 * the room? | |
733 */ | |
734 else if (rer && rer == ree && ch == DOOR) | |
735 thisdist += DOORPENALTY; | |
736 } | |
737 | |
738 /* | |
739 * Don't move to the last position if | |
740 * we can help it | |
741 */ | |
742 | |
743 if (ce(tryp, tp->t_oldpos)) | |
744 dist_to_old = thisdist; | |
745 else if ((flee && (thisdist > dist)) || | |
746 (!flee && (thisdist < dist))) | |
747 { | |
748 tp->t_nxtpos = tryp; | |
749 dist = thisdist; | |
750 } | |
751 } | |
752 } | |
753 | |
754 /* | |
755 * If we are running from the player and he is in our way, go | |
756 * ahead and slug him. | |
757 */ | |
758 | |
759 if (next_player && DISTANCE(*er,*ee) < dist && | |
760 step_ok(tp->t_chasee->t_pos.y, tp->t_chasee->t_pos.x, NOMONST, tp)) | |
761 { | |
762 tp->t_nxtpos = tp->t_chasee->t_pos; /* Okay to hit player */ | |
763 return(FALSE); | |
764 } | |
765 | |
766 | |
767 /* | |
768 * If we can't get closer to the player (if that's our goal) | |
769 * because other monsters are in the way, just stay put | |
770 */ | |
771 | |
772 if (!flee && ce(hero, *ee) && monst_dist < INT_MAX && | |
773 DISTANCE(*er, hero) < dist) | |
774 tp->t_nxtpos = *er; | |
775 | |
776 /* Do we want to go back to the last position? */ | |
777 else if (dist_to_old != INT_MIN && /* It is possible to move back */ | |
778 ((flee && dist == 0) || /* No other possible moves */ | |
779 (!flee && dist == INT_MAX))) | |
780 { | |
781 /* Do we move back or just stay put (default)? */ | |
782 | |
783 dist = DISTANCE(*er,*ee); /* Current distance */ | |
784 | |
785 if (!flee || (flee && (dist_to_old > dist))) | |
786 tp->t_nxtpos = tp->t_oldpos; | |
787 } | |
788 } | |
789 | |
790 /* Make sure we have the real distance now */ | |
791 dist = DISTANCE(tp->t_nxtpos, *ee); | |
792 | |
793 /* Mark monsters in a wall */ | |
794 | |
795 switch(mvinch(tp->t_nxtpos.y, tp->t_nxtpos.x)) | |
796 { | |
797 case WALL: | |
798 case '-': | |
799 case '|': | |
800 turn_on(*tp, ISINWALL); | |
801 break; | |
802 default: | |
803 turn_off(*tp, ISINWALL); | |
804 } | |
805 | |
806 if (off(*tp, ISFLEE) && | |
807 !(!SAME_POS((tp->t_chasee->t_pos),hero) || off(player, ISINWALL) || on(*tp, CANINWALL))) | |
808 return(dist != 0); | |
809 else /* May actually hit here from a confused move */ | |
810 return(!ce(tp->t_nxtpos, hero)); | |
811 } | |
812 | |
813 /* | |
814 roomin(coord *cp) | |
815 | |
816 Find what room some coordinates are in. | |
817 NULL means they aren't in any room. | |
818 */ | |
819 | |
820 struct room * | |
821 roomin(coord cp) | |
822 { | |
823 struct room *rp; | |
824 int i; | |
825 | |
826 for (i = 0; i < MAXROOMS; i++) | |
827 { | |
828 rp = &rooms[i]; | |
829 | |
830 if ((cp.x <= (rp->r_pos.x + (rp->r_max.x - 1))) && | |
831 (cp.y <= (rp->r_pos.y + (rp->r_max.y - 1))) && | |
832 (cp.x >= rp->r_pos.x) && | |
833 (cp.y >= rp->r_pos.y)) | |
834 { | |
835 return(rp); | |
836 } | |
837 } | |
838 | |
839 return(NULL); | |
840 } | |
841 | |
842 /* | |
843 * find_mons: Find the monster from his corrdinates | |
844 */ | |
845 | |
846 struct linked_list * | |
847 find_mons(int y, int x) | |
848 { | |
849 struct linked_list *item; | |
850 | |
851 for (item = mlist; item != NULL; item = next(item)) | |
852 { | |
853 struct thing *th = THINGPTR(item); | |
854 | |
855 if (th->t_pos.y == y && th->t_pos.x == x) | |
856 return item; | |
857 } | |
858 return NULL; | |
859 } | |
860 | |
861 /* | |
862 * Find an unfriendly monster around us to hit | |
863 */ | |
864 | |
865 struct linked_list * | |
866 f_mons_a(int y, int x, int hit_bad) | |
867 { | |
868 int row, col; | |
869 struct linked_list *item; | |
870 struct thing *tp; | |
871 | |
872 for (row = x - 1; row <= x + 1; row++) | |
873 for (col = y - 1; col <= y + 1; col++) | |
874 if (row == x && col == y) | |
875 continue; | |
876 else if (col > 0 && row > 0 && | |
877 isalpha(mvwinch(mw, col, row)) && | |
878 ((item = find_mons(col, row)) != NULL)) | |
879 { | |
880 tp = THINGPTR(item); | |
881 if ((good_monster(*tp) && !hit_bad) || | |
882 (!good_monster(*tp) && hit_bad)) | |
883 return (item); | |
884 } | |
885 | |
886 return (NULL); | |
887 } | |
888 | |
889 | |
890 /* | |
891 diag_ok() | |
892 Check to see if the move is legal if it is diagonal | |
893 */ | |
894 | |
895 int | |
896 diag_ok(coord *sp, coord *ep, struct thing *flgptr) | |
897 { | |
898 if (ep->x == sp->x || ep->y == sp->y) | |
899 return TRUE; | |
900 | |
901 return (step_ok(ep->y, sp->x, MONSTOK, flgptr) && | |
902 step_ok(sp->y, ep->x, MONSTOK, flgptr)); | |
903 } | |
904 | |
905 /* | |
906 cansee() | |
907 returns true if the hero can see a certain coordinate. | |
908 */ | |
909 | |
910 int | |
911 cansee(int y, int x) | |
912 { | |
913 struct room *rer; | |
914 coord tp; | |
915 | |
916 if (on(player, ISBLIND)) | |
917 return FALSE; | |
918 | |
919 tp.y = y; | |
920 tp.x = x; | |
921 rer = roomin(tp); | |
922 | |
923 /* | |
924 * We can only see if the hero in the same room as the coordinate and | |
925 * the room is lit or if it is close. | |
926 */ | |
927 | |
928 return ((rer != NULL && | |
929 rer == roomin(hero) && | |
930 (!(rer->r_flags & ISDARK) || (rer->r_flags & HASFIRE)) && | |
931 (levtype != MAZELEV || /* Maze level needs direct line */ | |
932 maze_view(tp.y, tp.x))) || | |
933 DISTANCE(tp,hero) < see_dist); | |
934 } | |
935 | |
936 coord * | |
937 find_shoot(struct thing *tp, coord *dir) | |
938 { | |
939 struct room *rtp; | |
940 int ulx, uly, xmx, ymx, xmon, ymon, tpx, tpy, row, col; | |
941 struct linked_list *mon; | |
942 struct thing *ick; | |
943 | |
944 rtp = roomin(tp->t_pos); /* Find room of chaser */ | |
945 | |
946 if (rtp == NULL) | |
947 return NULL; | |
948 | |
949 ulx = rtp->r_pos.x; | |
950 uly = rtp->r_pos.y; | |
951 xmx = rtp->r_max.x; | |
952 ymx = rtp->r_max.y; | |
953 | |
954 tpx = tp->t_pos.x; | |
955 tpy = tp->t_pos.y; | |
956 | |
957 for (col = ulx; col < (ulx + xmx); col++) | |
958 for (row = uly; row < (uly + ymx); row++) | |
959 { | |
960 if (row > 0 && col > 0 && isalpha(mvwinch(mw, row, col))) | |
961 { | |
962 mon = find_mons(row, col); | |
963 | |
964 if (mon) | |
965 { | |
966 ick = THINGPTR(mon); | |
967 xmon = ick->t_pos.x; | |
968 ymon = ick->t_pos.y; | |
969 | |
970 if (!(good_monster(*ick))) | |
971 { | |
972 if (straight_shot(tpy, tpx, ymon, xmon, dir)) | |
973 return(dir); | |
974 } | |
975 } | |
976 } | |
977 } | |
978 | |
979 return(NULL); | |
980 } | |
981 | |
982 /* | |
983 can_shoot() | |
984 determines if the monster (er) has a direct line of shot at the | |
985 player (ee). If so, it returns the direction in which to shoot. | |
986 */ | |
987 | |
988 coord * | |
989 can_shoot(coord *er, coord *ee, coord *dir) | |
990 { | |
991 int ery, erx, eey, eex; | |
992 | |
993 /* Make sure we are chasing the player */ | |
994 | |
995 if (!ce((*ee), hero)) | |
996 return(NULL); | |
997 | |
998 /* They must be in the same room */ | |
999 | |
1000 if (roomin(*er) != roomin(hero)) | |
1001 return(NULL); | |
1002 | |
1003 ery = er->y; | |
1004 erx = er->x; | |
1005 eey = ee->y; | |
1006 eex = ee->x; | |
1007 | |
1008 /* Will shoot unless next to player, then 80% prob will fight */ | |
1009 | |
1010 if ((DISTANCE(*er,*ee) < 4) && (rnd(100) < 80)) | |
1011 return(NULL); | |
1012 | |
1013 /* Do we have a straight shot? */ | |
1014 | |
1015 if (!straight_shot(ery, erx, eey, eex, dir)) | |
1016 return(NULL); | |
1017 else | |
1018 return(dir); | |
1019 } | |
1020 | |
1021 /* | |
1022 straight_shot() | |
1023 See if there is a straight line of sight between the two | |
1024 given coordinates. If shooting is not NULL, it is a pointer to a | |
1025 structure which should be filled with the direction to shoot (if | |
1026 there is a line of sight). If shooting, monsters get in the way. | |
1027 Otherwise, they do not. | |
1028 */ | |
1029 | |
1030 int | |
1031 straight_shot(int ery, int erx, int eey, int eex, coord *dir) | |
1032 { | |
1033 int dy, dx; /* Deltas */ | |
1034 int ch; | |
1035 | |
1036 /* Does the monster have a straight shot at player */ | |
1037 | |
1038 if ((ery != eey) && (erx != eex) && | |
1039 (abs(ery - eey) != abs(erx - eex))) | |
1040 return (FALSE); | |
1041 | |
1042 /* Get the direction to shoot */ | |
1043 | |
1044 if (eey > ery) | |
1045 dy = 1; | |
1046 else if (eey == ery) | |
1047 dy = 0; | |
1048 else | |
1049 dy = -1; | |
1050 | |
1051 if (eex > erx) | |
1052 dx = 1; | |
1053 else if (eex == erx) | |
1054 dx = 0; | |
1055 else | |
1056 dx = -1; | |
1057 | |
1058 /* Make sure we have free area all the way to the player */ | |
1059 | |
1060 ery += dy; | |
1061 erx += dx; | |
1062 | |
1063 while ((ery != eey) || (erx != eex)) | |
1064 { | |
1065 switch(ch = winat(ery, erx)) | |
1066 { | |
1067 case '|': | |
1068 case '-': | |
1069 case WALL: | |
1070 case DOOR: | |
1071 case SECRETDOOR: | |
1072 return(FALSE); | |
1073 default: | |
1074 if (dir && isalpha(ch)) | |
1075 return(FALSE); | |
1076 } | |
1077 | |
1078 ery += dy; | |
1079 erx += dx; | |
1080 } | |
1081 | |
1082 if (dir) | |
1083 { /* If we are shooting -- put in the directions */ | |
1084 dir->y = dy; | |
1085 dir->x = dx; | |
1086 } | |
1087 | |
1088 return(TRUE); | |
1089 } | |
1090 | |
1091 /* | |
1092 get_hurl | |
1093 returns the weapon that the monster will "throw" if it has one | |
1094 */ | |
1095 | |
1096 struct linked_list * | |
1097 get_hurl(struct thing *tp) | |
1098 { | |
1099 struct linked_list *arrow, *bolt, *rock, *silverarrow, *fbbolt; | |
1100 struct linked_list *bullet, *firearrow, *dart, *dagger, *shuriken; | |
1101 struct linked_list *oil, *grenade; | |
1102 | |
1103 struct linked_list *pitem; | |
1104 int bow = FALSE, crossbow = FALSE, sling = FALSE, footbow = FALSE; | |
1105 | |
1106 /* Don't point to anything to begin with */ | |
1107 | |
1108 arrow = bolt = rock = silverarrow = fbbolt = NULL; | |
1109 bullet = firearrow = dart = dagger = shuriken = NULL; | |
1110 oil = grenade = NULL; | |
1111 | |
1112 for (pitem = tp->t_pack; pitem != NULL; pitem = next(pitem)) | |
1113 if ((OBJPTR(pitem))->o_type == WEAPON) | |
1114 switch ((OBJPTR(pitem))->o_which) | |
1115 { | |
1116 case BOW:bow = TRUE; break; | |
1117 case CROSSBOW:crossbow = TRUE; break; | |
1118 case SLING:sling = TRUE; break; | |
1119 case FOOTBOW:footbow = TRUE; break; | |
1120 case ROCK:rock = pitem; break; | |
1121 case ARROW:arrow = pitem; break; | |
1122 case SILVERARROW:silverarrow = pitem; break; | |
1123 case BOLT:bolt = pitem; break; | |
1124 case FBBOLT:fbbolt = pitem; break; | |
1125 case BULLET:bullet = pitem; break; | |
1126 case FLAMEARROW:firearrow = pitem; break; | |
1127 case DART:dart = pitem; break; | |
1128 case DAGGER:dagger = pitem; break; | |
1129 case SHURIKEN:shuriken = pitem; break; | |
1130 case MOLOTOV:oil = pitem; break; | |
1131 case GRENADE:shuriken = pitem; break; | |
1132 } | |
1133 | |
1134 if (bow && silverarrow) | |
1135 return(silverarrow); | |
1136 | |
1137 if (crossbow && bolt) | |
1138 return(bolt); | |
1139 | |
1140 if (bow && firearrow) | |
1141 return(firearrow); | |
1142 | |
1143 if (off(*tp, ISCHARMED) && oil) | |
1144 return(oil); | |
1145 | |
1146 if (off(*tp, ISCHARMED) && grenade) | |
1147 return(grenade); | |
1148 | |
1149 if (footbow && fbbolt) | |
1150 return(fbbolt); | |
1151 | |
1152 if (bow && arrow) | |
1153 return(arrow); | |
1154 | |
1155 if (sling && bullet) | |
1156 return(bullet); | |
1157 | |
1158 if (sling && rock) | |
1159 return(rock); | |
1160 | |
1161 if (shuriken) | |
1162 return(shuriken); | |
1163 | |
1164 if (dagger) | |
1165 return(dagger); | |
1166 | |
1167 if (silverarrow) | |
1168 return(silverarrow); | |
1169 | |
1170 if (firearrow) | |
1171 return(firearrow); | |
1172 | |
1173 if (fbbolt) | |
1174 return(fbbolt); | |
1175 | |
1176 if (bolt) | |
1177 return(bolt); | |
1178 | |
1179 if (bullet) | |
1180 return(bullet); | |
1181 | |
1182 if (dart) | |
1183 return(dart); | |
1184 | |
1185 if (rock) | |
1186 return(rock); | |
1187 | |
1188 return(NULL); | |
1189 } | |
1190 | |
1191 /* | |
1192 pick_weap() | |
1193 returns the biggest weapon that the monster will wield if it | |
1194 has a non-launching or non-missile weapon returns NULL if no weapon, or | |
1195 bare hands is better | |
1196 */ | |
1197 | |
1198 struct object * | |
1199 pick_weap(struct thing *tp) | |
1200 { | |
1201 int weap_dam = maxdamage(tp->t_stats.s_dmg); | |
1202 struct object *ret_obj = NULL; | |
1203 struct linked_list *pitem; | |
1204 | |
1205 if (on(*tp, CANWIELD)) | |
1206 { | |
1207 for (pitem = tp->t_pack; pitem != NULL; pitem = next(pitem)) | |
1208 { | |
1209 struct object *obj = OBJPTR(pitem); | |
1210 | |
1211 if (obj->o_type != WEAPON && !(obj->o_flags&(ISLAUNCHER|ISMISL)) && | |
1212 maxdamage(obj->o_damage) > weap_dam) | |
1213 { | |
1214 weap_dam = maxdamage(obj->o_damage); | |
1215 ret_obj = obj; | |
1216 } | |
1217 } | |
1218 } | |
1219 | |
1220 return (ret_obj); | |
1221 } | |
1222 | |
1223 /* | |
1224 canblink() | |
1225 checks if the monster can teleport (blink). If so, it will try | |
1226 to blink the monster next to the player. | |
1227 */ | |
1228 | |
1229 int | |
1230 can_blink(struct thing *tp) | |
1231 { | |
1232 int y, x, index = 9; | |
1233 coord tryp; /* To hold the coordinates for use in diag_ok */ | |
1234 int spots[9], found_one = FALSE; | |
1235 | |
1236 /* | |
1237 * First, can the monster even blink? And if so, there is only a 30% | |
1238 * chance that it will do so. And it won't blink if it is running. | |
1239 */ | |
1240 | |
1241 if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) || | |
1242 on(*tp, ISFLEE) || | |
1243 (on(*tp, ISSLOW) && off(*tp, ISHASTE) && !(tp->t_turn)) || | |
1244 (rnd(10) < 9)) | |
1245 return (FALSE); | |
1246 | |
1247 /* Initialize the spots as illegal */ | |
1248 | |
1249 do | |
1250 { | |
1251 spots[--index] = FALSE; | |
1252 } | |
1253 while (index > 0); | |
1254 | |
1255 /* Find a suitable spot next to the player */ | |
1256 | |
1257 for (y = hero.y - 1; y < hero.y + 2; y++) | |
1258 for (x = hero.x - 1; x < hero.x + 2; x++, index++) | |
1259 { | |
1260 /* | |
1261 * Make sure x coordinate is in range and that we are | |
1262 * not at the player's position | |
1263 */ | |
1264 | |
1265 if (x < 0 || x >= COLS || index == 4) | |
1266 continue; | |
1267 | |
1268 /* Is it OK to move there? */ | |
1269 | |
1270 if (!step_ok(y, x, NOMONST, tp)) | |
1271 spots[index] = FALSE; | |
1272 else | |
1273 { | |
1274 | |
1275 /* | |
1276 * OK, we can go here. But don't go there if | |
1277 * monster can't get at player from there | |
1278 */ | |
1279 | |
1280 tryp.y = y; | |
1281 tryp.x = x; | |
1282 if (diag_ok(&tryp, &hero, tp)) | |
1283 { | |
1284 spots[index] = TRUE; | |
1285 found_one = TRUE; | |
1286 } | |
1287 } | |
1288 } | |
1289 | |
1290 /* If we found one, go to it */ | |
1291 | |
1292 if (found_one) | |
1293 { | |
1294 /* Find a legal spot */ | |
1295 | |
1296 while (spots[index = rnd(9)] == FALSE) | |
1297 continue; | |
1298 | |
1299 /* Get the coordinates */ | |
1300 | |
1301 y = hero.y + (index / 3) - 1; | |
1302 x = hero.x + (index % 3) - 1; | |
1303 | |
1304 /* Move the monster from the old space */ | |
1305 | |
1306 mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch); | |
1307 | |
1308 /* Move it to the new space */ | |
1309 | |
1310 tp->t_oldch = CCHAR( mvwinch(cw, y, x) ); | |
1311 | |
1312 if (cansee(y, x) && | |
1313 off(*tp, ISINWALL) && | |
1314 ((off(*tp, ISINVIS) && | |
1315 (off(*tp, ISSHADOW) || rnd(100) < 10)) || on(player, CANSEE)) && | |
1316 off(*tp, CANSURPRISE)) | |
1317 mvwaddch(cw, y, x, tp->t_type); | |
1318 | |
1319 mvwaddch(mw, tp->t_pos.y,tp->t_pos.x,' '); /*Clear old position */ | |
1320 mvwaddch(mw, y, x, tp->t_type); | |
1321 tp->t_pos.y = y; | |
1322 tp->t_pos.x = x; | |
1323 } | |
1324 | |
1325 return (found_one); | |
1326 } |