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