Annotation of early-roguelike/arogue7/chase.c, Revision 1.1.1.1
1.1 rubenllo 1: /*
2: * chase.c - Code for one object to chase another
3: *
4: * Advanced Rogue
5: * Copyright (C) 1984, 1985, 1986 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: /*
16: * Code for one object to chase another
17: *
18: */
19:
20: #include <ctype.h>
21: #include <limits.h>
22: #include <stdlib.h>
23: #include "curses.h"
24: #include "rogue.h"
25: #define MAXINT INT_MAX
26: #define MININT INT_MIN
27:
28: bool straight_shot(int ery, int erx, int eey, int eex, coord *shooting);
29:
30: /*
31: * Canblink checks if the monster can teleport (blink). If so, it will
32: * try to blink the monster next to the player.
33: */
34:
35: bool
36: can_blink(struct thing *tp)
37: {
38: register int y, x, index=9;
39: coord tryp; /* To hold the coordinates for use in diag_ok */
40: bool spots[9], found_one=FALSE;
41:
42: /*
43: * First, can the monster even blink? And if so, there is only a 50%
44: * chance that it will do so. And it won't blink if it is running or
45: * held.
46: */
47: if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) ||
48: on(*tp, ISFLEE) ||
49: tp->t_action == A_FREEZE ||
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 prey (ee). If so, it returns the direction in which to shoot.
128: */
129:
130: coord *
131: can_shoot(coord *er, coord *ee)
132: {
133: static coord shoot_dir;
134:
135: /*
136: * They must be in the same room or very close (at door)
137: */
138: if (roomin(er) != roomin(ee) && DISTANCE(er->y,er->x,ee->y,ee->x) > 1)
139: return(NULL);
140:
141: /* Do we have a straight shot? */
142: if (!straight_shot(er->y, er->x, ee->y, ee->x, &shoot_dir)) return(NULL);
143: else return(&shoot_dir);
144: }
145:
146: /*
147: * chase:
148: * Find the spot for the chaser(er) to move closer to the
149: * chasee(ee). Rer is the room of the chaser, and ree is the
150: * room of the creature being chased (chasee). Flee is true if
151: * destination (ee) is player and monster is running away
152: * or the player is in a wall and the monster can't get to it
153: */
154:
155: void
156: chase(struct thing *tp, coord *ee, struct room *rer, struct room *ree,
157: bool flee)
158: {
159: int dist, thisdist, monst_dist = MAXINT;
160: register coord *er = &tp->t_pos;
161: struct thing *prey; /* What we are chasing */
162: coord ch_ret; /* Where chasing takes you */
163: char ch, mch;
164: bool next_player = FALSE;
165:
166: /*
167: * set the distance from the chas(er) to the chas(ee) here and then
168: * we won't have to reset it unless the chas(er) moves (instead of shoots)
169: */
170: dist = DISTANCE(er->y, er->x, ee->y, ee->x);
171:
172: /*
173: * See if our destination is a monster or player. If so, make "prey" point
174: * to it.
175: */
176: if (ce(hero, *ee)) prey = &player; /* Is it the player? */
177: else if (tp->t_dest && ce(*(tp->t_dest), *ee)) { /* Is it a monster? */
178: struct linked_list *item;
179:
180: /* What is the monster we're chasing? */
181: item = find_mons(ee->y, ee->x);
182: if (item != NULL) prey = THINGPTR(item);
183: else prey = NULL;
184: }
185: else prey = NULL;
186:
187: /* We will use at least one movement period */
188: tp->t_no_move = movement(tp);
189: if (on(*tp, ISFLY)) /* If the creature is flying, speed it up */
190: tp->t_no_move /= 2;
191:
192: /*
193: * If the thing is confused or it can't see the player,
194: * let it move randomly.
195: */
196: if ((on(*tp, ISHUH) && rnd(10) < 8) ||
197: (prey && on(*prey, ISINVIS) && off(*tp, CANSEE))) { /* invisible prey */
198: /*
199: * get a valid random move
200: */
201: tp->t_newpos = *rndmove(tp);
202: dist = DISTANCE(tp->t_newpos.y, tp->t_newpos.x, ee->y, ee->x);
203: }
204:
205: /*
206: * Otherwise, find the empty spot next to the chaser that is
207: * closest to the chasee.
208: */
209: else {
210: register int ey, ex, x, y;
211: int dist_to_old = MININT; /* Dist from goal to old position */
212:
213: /*
214: * This will eventually hold where we move to get closer
215: * If we can't find an empty spot, we stay where we are.
216: */
217: dist = flee ? 0 : MAXINT;
218: ch_ret = *er;
219:
220: /* Are we at our goal already? */
221: if (!flee && ce(ch_ret, *ee)) {
222: turn_off(*tp, ISRUN); /* So stop running! */
223: return;
224: }
225:
226: ey = er->y + 1;
227: ex = er->x + 1;
228:
229: /* Check all possible moves */
230: for (x = er->x - 1; x <= ex; x++) {
231: if (x < 0 || x >= cols) /* Don't try off the board */
232: continue;
233: for (y = er->y - 1; y <= ey; y++) {
234: coord tryp;
235:
236: if ((y < 1) || (y >= lines - 2)) /* Don't try off the board */
237: continue;
238:
239: /* Don't try the player if not going after the player */
240: if ((flee || !ce(hero, *ee) || on(*tp, ISFRIENDLY)) &&
241: x == hero.x && y == hero.y) {
242: next_player = TRUE;
243: continue;
244: }
245:
246: tryp.x = x;
247: tryp.y = y;
248:
249: /* Is there a monster on this spot closer to our goal?
250: * Don't look in our spot or where we were.
251: */
252: if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) &&
253: isalpha(mch = CCHAR( mvwinch(mw, y, x) ) )) {
254: int test_dist;
255:
256: test_dist = DISTANCE(y, x, ee->y, ee->x);
257: if (test_dist <= 25 && /* Let's be fairly close */
258: test_dist < monst_dist) {
259: /* Could we really move there? */
260: mvwaddch(mw, y, x, ' '); /* Temporarily blank monst */
261: if (diag_ok(er, &tryp, tp)) monst_dist = test_dist;
262: mvwaddch(mw, y, x, mch); /* Restore monster */
263: }
264: }
265:
266: /* Can we move onto the spot? */
267: if (!diag_ok(er, &tryp, tp)) continue;
268:
269: ch = CCHAR( mvwinch(cw, y, x) ); /* Screen character */
270:
271: /*
272: * Stepping on player is NOT okay if we are fleeing.
273: * If we are friendly to the player and there is a monster
274: * in the way that is not of our race, it is okay to move
275: * there.
276: */
277: if (step_ok(y, x, FIGHTOK, tp) &&
278: (off(*tp, ISFLEE) || ch != PLAYER))
279: {
280: /*
281: * If it is a trap, an intelligent monster may not
282: * step on it (unless our hero is on top!)
283: */
284: if ((isatrap(ch)) &&
285: (rnd(10) < tp->t_stats.s_intel) &&
286: (!on(*tp, ISFLY)) &&
287: (y != hero.y || x != hero.x))
288: continue;
289:
290: /*
291: * OK -- this place counts
292: */
293: thisdist = DISTANCE(y, x, ee->y, ee->x);
294:
295: /* Adjust distance if we are being shot at */
296: if (tp->t_wasshot && tp->t_stats.s_intel > 5 &&
297: prey != NULL) {
298: /* Move out of line of sight */
299: if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL)) {
300: if (flee) thisdist -= SHOTPENALTY;
301: else thisdist += SHOTPENALTY;
302: }
303:
304: /* But do we want to leave the room? */
305: else if (rer && rer == ree && ch == DOOR)
306: thisdist += DOORPENALTY;
307: }
308:
309: /* Don't move to the last position if we can help it
310: * (unless out prey just moved there)
311: */
312: if (ce(tryp, tp->t_oldpos) && (flee || !ce(tryp, hero)))
313: dist_to_old = thisdist;
314:
315: else if ((flee && (thisdist > dist)) ||
316: (!flee && (thisdist < dist)))
317: {
318: ch_ret = tryp;
319: dist = thisdist;
320: }
321: }
322: }
323: }
324:
325: /* If we aren't trying to get the player, but he is in our way,
326: * hit him (unless we have been turned or are friendly). next_player
327: * being TRUE -> we are next to the player but don't want to hit him.
328: *
329: * If we are friendly to the player, following him, and standing next
330: * to him, we will try to help him out in battle.
331: */
332: if (next_player && off(*tp, WASTURNED)) {
333: if (off(*tp, ISFRIENDLY) &&
334: ((flee && ce(ch_ret, *er)) ||
335: (!flee && DISTANCE(er->y, er->x, ee->y, ee->x) < dist)) &&
336: step_ok(tp->t_dest->y, tp->t_dest->x, NOMONST, tp)) {
337: /* Okay to hit player */
338: debug("Switching to hero.");
339: tp->t_newpos = hero;
340: tp->t_action = A_MOVE;
341: return;
342: }
343: else if (on(*tp, ISFRIENDLY) && !flee && ce(*ee, hero)) {
344: /*
345: * Look all around the player. If there is a fightable
346: * creature next to both of us, hit it. Otherwise, if
347: * there is a fightable creature next to the player, try
348: * to move next to it.
349: */
350: dist = MAXINT;
351: for (x = hero.x - 1; x <= hero.x + 1; x++) {
352: if (x < 0 || x >= cols) /* Don't try off the board */
353: continue;
354: for (y = hero.y - 1; y <= hero.y + 1; y++) {
355: if ((y < 1) || (y >= lines - 2)) /* Stay on the board */
356: continue;
357:
358: /* Is there a fightable monster here? */
359: if (isalpha(mvwinch(mw, y, x)) &&
360: step_ok(y, x, FIGHTOK, tp) &&
361: off(*tp, ISSTONE)) {
362: thisdist = DISTANCE(er->y, er->x, y, x);
363: if (thisdist < dist) {
364: dist = thisdist;
365: ch_ret.y = y;
366: ch_ret.x = x;
367: }
368: }
369: }
370: }
371:
372: /* Are we next to a bad guy? */
373: if (dist <= 2) { /* Get him! */
374: tp->t_newpos = ch_ret;
375: tp->t_action = A_MOVE;
376: }
377:
378: /* Try to move to the bad guy */
379: else if (dist < MAXINT)
380: chase(tp, &ch_ret,
381: roomin(&tp->t_pos), roomin(&ch_ret), FALSE);
382:
383: else tp->t_action = A_NIL;
384:
385: return;
386: }
387: }
388:
389:
390: /*
391: * If we have decided that we can move onto a monster (we are
392: * friendly to the player, go to it.
393: */
394: if (!ce(ch_ret, *er) && isalpha(mvwinch(mw, ch_ret.y, ch_ret.x))) {
395: debug("Attack monster");
396: tp->t_newpos = ch_ret;
397: tp->t_action = A_MOVE;
398: return;
399: }
400:
401: /* If we can't get closer to the player (if that's our goal)
402: * because other monsters are in the way, just stay put
403: */
404: if (!flee && ce(hero, *ee) && monst_dist < MAXINT &&
405: DISTANCE(er->y, er->x, hero.y, hero.x) < dist) {
406: tp->t_action = A_NIL; /* do nothing for awhile */
407: return;
408: }
409:
410: /* Do we want to go back to the last position? */
411: else if (dist_to_old != MININT && /* It is possible to move back */
412: ((flee && dist == 0) || /* No other possible moves */
413: (!flee && dist == MAXINT))) {
414: /* Do we move back or just stay put (default)? */
415: dist = DISTANCE(er->y, er->x, ee->y, ee->x); /* Current distance */
416: if (!flee || (flee && (dist_to_old > dist))) ch_ret = tp->t_oldpos;
417: }
418:
419: /* Record the new destination */
420: tp->t_newpos = ch_ret;
421: }
422:
423: /*
424: * Do we want to fight or move? If our selected destination (ch_ret)
425: * is our hero, then we want to fight. Otherwise, we want to move.
426: */
427: if (ce(tp->t_newpos, hero)) {
428: /* Fight! (or sell) */
429: if (on(*tp, CANSELL)) {
430: tp->t_action = A_SELL;
431: tp->t_no_move += movement(tp); /* takes a little time to sell */
432: }
433: else {
434: tp->t_action = A_ATTACK;
435:
436: /*
437: * Try to find a weapon to wield. Wield_weap will return a
438: * projector if weapon is a projectile (eg. bow for arrow).
439: * If weapon is NULL (the case here), it will try to find
440: * a suitable weapon.
441: *
442: * Add in rest of time. Fight is
443: * movement() + weap_move() + FIGHTBASE
444: */
445: tp->t_using = wield_weap(NULL, tp);
446: if (tp->t_using == NULL)
447: tp->t_no_move += weap_move(tp, NULL);
448: else
449: tp->t_no_move += weap_move(tp, OBJPTR(tp->t_using));
450:
451: if (on(*tp, ISHASTE))
452: tp->t_no_move += FIGHTBASE/2;
453: else if (on(*tp, ISSLOW))
454: tp->t_no_move += FIGHTBASE*2;
455: else
456: tp->t_no_move += FIGHTBASE;
457: }
458: }
459: else {
460: /* Move */
461: tp->t_action = A_MOVE;
462:
463: /*
464: * Check if the creature is not next to the player. If it
465: * is not and has held or suffocated the player, then stop it!
466: * Note that this code should more appropriately appear in
467: * the area that actually moves the monster, but for now it
468: * is okay here because the player can't move while held or
469: * suffocating.
470: */
471: if (dist > 2) {
472: if (on(*tp, DIDHOLD)) {
473: turn_off(*tp, DIDHOLD);
474: turn_on(*tp, CANHOLD);
475: if (--hold_count == 0)
476: turn_off(player, ISHELD);
477: }
478:
479: /* If monster was suffocating, stop it */
480: if (on(*tp, DIDSUFFOCATE)) {
481: turn_off(*tp, DIDSUFFOCATE);
482: turn_on(*tp, CANSUFFOCATE);
483: extinguish(suffocate);
484: msg("You can breathe again.....Whew!");
485: }
486: }
487: }
488: }
489:
490: /*
491: * do_chase:
492: * Make one thing chase another.
493: */
494:
495: void
496: do_chase(struct thing *th)
497: {
498: register struct room *orig_rer, /* Original room of chaser */
499: *new_room; /* new room of monster */
500: char floor, rch, sch;
501: coord old_pos, /* Old position of monster */
502: ch_ret; /* Where we want to go */
503:
504: if (on(*th, NOMOVE)) return;
505:
506: ch_ret = th->t_newpos; /* Record our desired new position */
507:
508: /*
509: * Make sure we have an open spot (no other monster's gotten in our way,
510: * someone didn't just drop a scare monster there, our prey didn't just
511: * get there, etc.)
512: */
513: if (!step_ok(th->t_newpos.y, th->t_newpos.x, FIGHTOK, th)) {
514: /*
515: * Most monsters get upset now. Guardians are all friends,
516: * and we don't want to see 50 messages in a row!
517: */
518: if (th->t_stats.s_intel > 4 &&
519: off(*th, ISUNDEAD) &&
520: off(*th, ISGUARDIAN) &&
521: off(*th, AREMANY) &&
522: off(*th, ISHUH) &&
523: off(player, ISBLIND) &&
524: cansee(unc(th->t_pos)) &&
525: !invisible(th))
526: msg("%s motions angrily.", prname(monster_name(th), TRUE));
527: return;
528: }
529: else if (ce(th->t_newpos, hero) || /* Player just got in our way */
530: isalpha(mvwinch(mw, th->t_newpos.y, th->t_newpos.x))) {
531: bool fightplayer = ce(th->t_newpos, hero);
532:
533: /* If we were turned or are friendly, we just have to sit here! */
534: if (fightplayer && (on(*th, WASTURNED) || on(*th, ISFRIENDLY))) return;
535:
536: /* Do we want to sell something? */
537: if (fightplayer && on(*th, CANSELL)) {
538: th->t_action = A_SELL;
539: th->t_no_move += movement(th); /* takes a little time to sell */
540: return;
541: }
542:
543: /* Let's hit him */
544: th->t_action = A_ATTACK;
545:
546: /*
547: * Try to find a weapon to wield. Wield_weap will return a
548: * projector if weapon is a projectile (eg. bow for arrow).
549: * If weapon is NULL (the case here), it will try to find
550: * a suitable weapon.
551: */
552: th->t_using = wield_weap(NULL, th);
553: /*
554: * add in rest of time
555: */
556: if (th->t_using == NULL)
557: th->t_no_move += weap_move(th, NULL);
558: else
559: th->t_no_move += weap_move(th, OBJPTR(th->t_using));
560: if (on(*th, ISHASTE))
561: th->t_no_move += FIGHTBASE/2;
562: else if (on(*th, ISSLOW))
563: th->t_no_move += FIGHTBASE*2;
564: else
565: th->t_no_move += FIGHTBASE;
566: return;
567: }
568:
569: /*
570: * Blank out the old position and record the new position --
571: * the blanking must be done first in case the positions are the same.
572: */
573: mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' ');
574: mvwaddch(mw, ch_ret.y, ch_ret.x, th->t_type);
575:
576: /* Get new and old rooms of monster */
577: new_room = roomin(&ch_ret);
578: orig_rer = roomin(&th->t_pos);
579:
580: /* Store the critter's old position and update the current one */
581: old_pos = th->t_pos;
582: th->t_pos = ch_ret;
583: floor = (roomin(&ch_ret) == NULL) ? PASSAGE : FLOOR;
584:
585: /* If we have a scavenger, it can pick something up */
586: if (off(*th, ISGUARDIAN)) {
587: register struct linked_list *n_item, *o_item;
588: register int item_count = 0;
589: bool want_something = FALSE;
590:
591: while ((n_item = find_obj(ch_ret.y, ch_ret.x)) != NULL) {
592: register struct object *n_obj, *o_obj;
593: bool wants_it;
594:
595: /* Does this monster want anything? */
596: if (want_something == FALSE) {
597: if (on(*th, ISSCAVENGE) || on(*th, CARRYFOOD) ||
598: on(*th, CARRYGOLD) || on(*th, CARRYSCROLL) ||
599: on(*th, CARRYPOTION) || on(*th, CARRYRING) ||
600: on(*th, CARRYSTICK) || on(*th, CARRYMISC) ||
601: on(*th, CARRYWEAPON) || on(*th, CARRYARMOR) ||
602: on(*th, CARRYDAGGER)) {
603: want_something = TRUE;
604:
605: /*
606: * Blank the area. We have to do it only before the
607: * first item in case an item gets dropped in same
608: * place. We don't want to blank it out after it get
609: * dropped.
610: */
611: mvaddch(ch_ret.y, ch_ret.x, floor);
612:
613: /* Were we specifically after something here? */
614: if (ce(*th->t_dest, ch_ret)) {
615: /* If we're mean, we go after the hero */
616: if (on(*th, ISMEAN)) runto(th, &hero);
617:
618: /* Otherwise just go back to sleep */
619: else {
620: turn_off(*th, ISRUN);
621: th->t_dest = NULL;
622: }
623: }
624: }
625: else break;
626: }
627:
628: item_count++; /* Count the number of items */
629:
630: /*
631: * see if he's got one of this group already
632: */
633: o_item = NULL;
634: n_obj = OBJPTR(n_item);
635: detach(lvl_obj, n_item);
636:
637: /* See if he wants it */
638: if (n_obj->o_type == SCROLL && n_obj->o_which == S_SCARE &&
639: th->t_stats.s_intel < 16)
640: wants_it = FALSE; /* Most monsters don't want a scare monster */
641: else if (on(*th, ISSCAVENGE)) wants_it = TRUE;
642: else {
643: wants_it = FALSE; /* Default case */
644: switch (n_obj->o_type) {
645: case FOOD: if(on(*th, CARRYFOOD)) wants_it = TRUE;
646: when GOLD: if(on(*th, CARRYGOLD)) wants_it = TRUE;
647: when SCROLL:if(on(*th, CARRYSCROLL)) wants_it = TRUE;
648: when POTION:if(on(*th, CARRYPOTION)) wants_it = TRUE;
649: when RING: if(on(*th, CARRYRING)) wants_it = TRUE;
650: when STICK: if(on(*th, CARRYSTICK)) wants_it = TRUE;
651: when MM: if(on(*th, CARRYMISC)) wants_it = TRUE;
652: when ARMOR: if(on(*th, CARRYARMOR)) wants_it = TRUE;
653: when WEAPON:if(on(*th, CARRYWEAPON) ||
654: (on(*th,CARRYDAGGER)&&n_obj->o_which==DAGGER))
655: wants_it = TRUE;
656: }
657: }
658: /*
659: * The quartermaster doesn't sell cursed stuff so he won't
660: * pick it up
661: */
662: if (on(*th, CANSELL) && (n_obj->o_flags & ISCURSED))
663: wants_it = FALSE;
664:
665: /* If he doesn't want it, throw it away */
666: if (wants_it == FALSE) {
667: fall(n_item, FALSE);
668: continue;
669: }
670:
671: /* Otherwise, let's pick it up */
672: if (n_obj->o_group) {
673: for(o_item = th->t_pack; o_item != NULL; o_item = next(o_item)){
674: o_obj = OBJPTR(o_item);
675: if (o_obj->o_group == n_obj->o_group) {
676: o_obj->o_count += n_obj->o_count;
677: o_discard(n_item);
678: break;
679: }
680: }
681: }
682: if (o_item == NULL) { /* didn't find it */
683: attach(th->t_pack, n_item);
684: }
685: }
686:
687: /* If there was anything here, we may have to update the screen */
688: if (item_count) {
689: if (cansee(ch_ret.y, ch_ret.x))
690: mvwaddch(cw, ch_ret.y, ch_ret.x, mvinch(ch_ret.y, ch_ret.x));
691: updpack(TRUE, th); /* Update the monster's encumberance, too */
692: }
693: }
694:
695:
696: rch = CCHAR( mvwinch(stdscr, old_pos.y, old_pos.x) );
697: if (th->t_oldch == floor && rch != floor && !isatrap(rch))
698: mvwaddch(cw, old_pos.y, old_pos.x, rch);
699: else
700: mvwaddch(cw, old_pos.y, old_pos.x, th->t_oldch);
701: sch = CCHAR( mvwinch(cw, ch_ret.y, ch_ret.x) ); /* What player sees */
702: rch = CCHAR( mvwinch(stdscr, ch_ret.y, ch_ret.x) ); /* What's really there */
703:
704: /* If we have a tunneling monster, it may be making a tunnel */
705: if (on(*th, CANTUNNEL) &&
706: (rch == SECRETDOOR || rch == WALL || rch == '|' || rch == '-')) {
707: char nch; /* The new look to the tunnel */
708:
709: if (rch == WALL) nch = PASSAGE;
710: else if (levtype == MAZELEV) nch = FLOOR;
711: else nch = DOOR;
712: addch(nch);
713:
714: if (cansee(ch_ret.y, ch_ret.x)) sch = nch; /* Can player see this? */
715:
716: /* Does this make a new exit? */
717: if (rch == '|' || rch == '-') {
718: struct linked_list *newroom;
719: coord *exit;
720:
721: newroom = new_item(sizeof(coord));
722: exit = DOORPTR(newroom);
723: *exit = ch_ret;
724: attach(new_room->r_exit, newroom);
725: }
726: }
727:
728: /* Mark if the monster is inside a wall */
729: if (isrock(mvinch(ch_ret.y, ch_ret.x))) turn_on(*th, ISINWALL);
730: else turn_off(*th, ISINWALL);
731:
732: /* If the monster can illuminate rooms, check for a change */
733: if (on(*th, HASFIRE)) {
734: register struct linked_list *fire_item;
735:
736: /* Is monster entering a room? */
737: if (orig_rer != new_room && new_room != NULL) {
738: fire_item = creat_item(); /* Get an item-only structure */
739: ldata(fire_item) = (char *) th;
740:
741: attach(new_room->r_fires, fire_item);
742: new_room->r_flags |= HASFIRE;
743:
744: if (cansee(ch_ret.y, ch_ret.x) && next(new_room->r_fires) == NULL)
745: light(&hero);
746: }
747:
748: /* Is monster leaving a room? */
749: if (orig_rer != new_room && orig_rer != NULL) {
750: /* Find the bugger in the list and delete him */
751: for (fire_item = orig_rer->r_fires; fire_item != NULL;
752: fire_item = next(fire_item)) {
753: if (THINGPTR(fire_item) == th) { /* Found him! */
754: detach(orig_rer->r_fires, fire_item);
755: destroy_item(fire_item);
756: if (orig_rer->r_fires == NULL) {
757: orig_rer->r_flags &= ~HASFIRE;
758: if (cansee(old_pos.y, old_pos.x))
759: light(&old_pos);
760: }
761: break;
762: }
763: }
764: }
765: }
766:
767: /* If monster is entering player's room and player can see it,
768: * stop the player's running.
769: */
770: if (new_room != orig_rer && new_room != NULL &&
771: new_room == roomin(th->t_dest) && cansee(unc(ch_ret)) &&
772: (off(*th, ISINVIS) || on(player, CANSEE)) &&
773: (off(*th, ISSHADOW) || on(player, CANSEE)) &&
774: (off(*th, CANSURPRISE) || ISWEARING(R_ALERT))) {
775: running = FALSE;
776: if (fight_flush) md_flushinp();
777: }
778:
779: th->t_oldch = sch;
780:
781: /* Let's display those creatures that we can see. */
782: if (cansee(unc(ch_ret)) &&
783: off(*th, ISINWALL) &&
784: !invisible(th))
785: mvwaddch(cw, ch_ret.y, ch_ret.x, th->t_type);
786:
787: /* Record monster's last position (if new one is different) */
788: if (!ce(ch_ret, old_pos)) th->t_oldpos = old_pos;
789:
790: /* If the monster is on a trap, trap it */
791: sch = CCHAR( mvinch(ch_ret.y, ch_ret.x) );
792: if (isatrap(sch)) {
793: if (cansee(ch_ret.y, ch_ret.x)) th->t_oldch = sch;
794: be_trapped(th, &ch_ret);
795: }
796: }
797:
798:
799: /*
800: * Get_hurl returns the weapon that the monster will "throw" if he has one
801: */
802:
803: struct linked_list *
804: get_hurl(struct thing *tp)
805: {
806: struct linked_list *arrow=NULL, *bolt=NULL, *rock=NULL,
807: *spear = NULL, *dagger=NULL, *dart=NULL, *aklad=NULL;
808: register struct linked_list *pitem;
809: register struct object *obj;
810: bool bow=FALSE, crossbow=FALSE, sling=FALSE;
811:
812: for (pitem=tp->t_pack; pitem; pitem=next(pitem)) {
813: obj = OBJPTR(pitem);
814: if (obj->o_type == WEAPON)
815: switch (obj->o_which) {
816: case BOW: bow = TRUE;
817: when CROSSBOW: crossbow = TRUE;
818: when SLING: sling = TRUE;
819: when ROCK: rock = pitem;
820: when ARROW: arrow = pitem;
821: when BOLT: bolt = pitem;
822: when SPEAR: spear = pitem;
823: when DAGGER:
824: /* Don't throw the dagger if it's our last one */
825: if (obj->o_count > 1) dagger = pitem;
826: when DART: dart = pitem;
827: }
828: else if (obj->o_type == RELIC &&
829: obj->o_which == AXE_AKLAD)
830: aklad = pitem;
831: }
832:
833: /* Do we have that all-powerful Aklad Axe? */
834: if (aklad) return(aklad);
835:
836: /* Use crossbow bolt if possible */
837: if (crossbow && bolt) return(bolt);
838: if (bow && arrow) return(arrow);
839: if (spear) return(spear);
840: if (dagger) return(dagger);
841: if (sling && rock) return(rock);
842: if (dart) return(dart);
843: return(NULL);
844: }
845:
846: /*
847: * runto:
848: * Set a monster running after something
849: */
850:
851: void
852: runto(struct thing *runner, coord *spot)
853: {
854: if (on(*runner, ISSTONE))
855: return;
856:
857: /* If we are chasing a new creature, forget about thrown weapons */
858: if (runner->t_dest && !ce(*runner->t_dest, *spot)) runner->t_wasshot=FALSE;
859:
860: /*
861: * Start the beastie running
862: */
863: runner->t_dest = spot;
864: turn_on(*runner, ISRUN);
865: turn_off(*runner, ISDISGUISE);
866: }
867:
868:
869:
870: /*
871: * straight_shot:
872: * See if there is a straight line of sight between the two
873: * given coordinates. If shooting is not NULL, it is a pointer
874: * to a structure which should be filled with the direction
875: * to shoot (if there is a line of sight). If shooting, monsters
876: * get in the way. Otherwise, they do not.
877: */
878:
879: bool
880: straight_shot(int ery, int erx, int eey, int eex, coord *shooting)
881: {
882: register int dy, dx; /* Deltas */
883: char ch;
884:
885: /* Does the monster have a straight shot at prey */
886: if ((ery != eey) && (erx != eex) &&
887: (abs(ery - eey) != abs(erx - eex))) return(FALSE);
888:
889: /* Get the direction to shoot */
890: if (eey > ery) dy = 1;
891: else if (eey == ery) dy = 0;
892: else dy = -1;
893:
894: if (eex > erx) dx = 1;
895: else if (eex == erx) dx = 0;
896: else dx = -1;
897:
898: /* Make sure we have free area all the way to the player */
899: ery += dy;
900: erx += dx;
901: while ((ery != eey) || (erx != eex)) {
902: switch (ch = CCHAR( winat(ery, erx) )) {
903: case '|':
904: case '-':
905: case WALL:
906: case DOOR:
907: case SECRETDOOR:
908: case FOREST:
909: return(FALSE);
910: default:
911: if (shooting && isalpha(ch)) return(FALSE);
912: }
913: ery += dy;
914: erx += dx;
915: }
916:
917: if (shooting) { /* If we are shooting -- put in the directions */
918: shooting->y = dy;
919: shooting->x = dx;
920: }
921: return(TRUE);
922: }
CVSweb