Annotation of early-roguelike/urogue/chase.c, Revision 1.1.1.1
1.1 rubenllo 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: }
CVSweb