1 /*
2  * Code	for one	creature to chase another
3  *
4  * chase.c	1.32	(A.I. Design) 12/12/84
5  */
6 
7 #include "rogue.h"
8 #include "curses.h"
9 
10 #define	DRAGONSHOT  5	/* one chance in DRAGONSHOT that a dragon will flame */
11 
12 coord ch_ret;			/* Where chasing takes	you */
13 
14 /*
15  * runners:
16  *	Make all the running monsters move.
17  */
18 runners()
19 {
20     register THING *tp;
21 	register int dist;
22 
23 	for	(tp = mlist; tp	!= NULL; tp = next(tp)) {
24 		if (!on(*tp, ISHELD) && on(*tp, ISRUN)) {
25 			dist = DISTANCE(hero.y, hero.x, tp->t_pos.y, tp->t_pos.x);
26 			if	(!(on(*tp, ISSLOW) || (tp->t_type == 'S' && dist > 3)) || tp->t_turn)
27 				do_chase(tp);
28 			if (on(*tp, ISHASTE))
29 				do_chase(tp);
30 			dist = DISTANCE(hero.y, hero.x, tp->t_pos.y, tp->t_pos.x);
31 			if (on(*tp, ISFLY) && dist > 3)
32 				do_chase(tp);
33 			tp->t_turn ^= TRUE;
34 		}
35 	}
36 }
37 
38 /*
39  * do_chase:
40  *	Make one thing chase another.
41  */
42 do_chase(th)
43 THING *th;
44 {
45     int	mindist	= 32767, i, dist;
46     byte sch;
47     bool door;
48     register THING *obj;
49     struct room	*oroom;
50     register struct room	*rer, *ree;	/* room of chaser, room of chasee */
51     coord this;				/* Temporary	destination for	chaser */
52 
53     rer	= th->t_room;		/* Find room of chaser */
54     if (on(*th,	ISGREED) && rer->r_goldval == 0)
55 		th->t_dest = &hero;	/*	If gold	has been taken,	run after hero */
56     ree	= proom;
57     if (th->t_dest != &hero)	/*	Find room of chasee */
58 		ree = roomin(th->t_dest);
59     if (ree == NULL)
60     	return;
61     /*
62      * We don't	count doors as inside rooms for	this routine
63      */
64     door = (chat(th->t_pos.y, th->t_pos.x) == DOOR);
65 
66 
67     /*
68      * If the object of	our desire is in a different room,
69      * and we are not in a maze, run to	the door nearest to
70      * our goal.
71      */
72 over:
73     if (rer != ree && (rer->r_flags & ISMAZE) == 0)
74     {
75 		for (i	= 0; i < rer->r_nexits;	i++) {	/*	loop through doors */
76 		    dist = DISTANCE(th->t_dest->y, th->t_dest->x,rer->r_exit[i].y, rer->r_exit[i].x);
77 		    if	(dist <	mindist) {
78 				this = rer->r_exit[i];
79 				mindist = dist;
80 		    }
81 		}
82 		if (door) {
83 		    rer = &passages[flat(th->t_pos.y, th->t_pos.x) & F_PNUM];
84 		    door = FALSE;
85 		    goto over;
86 		}
87     } else {
88 		this =	*th->t_dest;
89 		/*
90 		 * For	monsters which can fire	bolts at the poor hero,	we check to
91 		 * see	if (a) the hero	in on a	straight line from it, and (b) that
92 		 * it is within shooting distance, but	outside	of striking range.
93 		 */
94 		if ((th->t_type == 'D' || th->t_type == 'I')
95 		    &&	(th->t_pos.y ==	hero.y || th->t_pos.x == hero.x
96 			 || abs(th->t_pos.y - hero.y) == abs(th->t_pos.x - hero.x))
97 		    &&	((dist=DISTANCE(th->t_pos.y, th->t_pos.x, hero.y, hero.x)) > 2
98 			 && dist <= BOLT_LENGTH	* BOLT_LENGTH)
99 		    &&	!on(*th, ISCANC) && rnd(DRAGONSHOT) == 0)
100 		{
101 		    running = FALSE;
102 		    delta.y = sign(hero.y - th->t_pos.y);
103 		    delta.x = sign(hero.x - th->t_pos.x);
104 		    fire_bolt(&th->t_pos,&delta,th->t_type == 'D' ? "flame" : "frost");
105 		    return;
106 		}
107     }
108     /*
109      * This now	contains what we want to run to	this time
110      * so we run to it.	 If we hit it we either	want to	fight it
111      * or stop running
112      */
113     chase(th, &this);
114     if (ce(ch_ret, hero)) {
115 		attack(th);
116 		return;
117     } else if (ce(ch_ret,	*th->t_dest)) {
118 		for (obj = lvl_obj; obj != NULL; obj =	next(obj))
119 		    if	(th->t_dest == &obj->o_pos) {
120 		    	byte oldchar;
121 
122 				detach(lvl_obj, obj);
123 				attach(th->t_pack, obj);
124 				oldchar = chat(obj->o_pos.y, obj->o_pos.x) =
125 			    (th->t_room->r_flags & ISGONE) ? PASSAGE : FLOOR;
126 				if (cansee(obj->o_pos.y, obj->o_pos.x))
127 				    mvaddch(obj->o_pos.y, obj->o_pos.x, oldchar);
128 				th->t_dest = find_dest(th);
129 				break;
130 		    }
131     }
132     if (th->t_type == 'F')
133 		return;
134     /*
135      * If the chasing thing moved, update the screen
136      */
137     if (th->t_oldch != '@') {
138     	if	(th->t_oldch ==	' ' && cansee(th->t_pos.y, th->t_pos.x)
139 		       && _level[INDEX(th->t_pos.y,th->t_pos.x)] == FLOOR)
140 		    mvaddch(th->t_pos.y, th->t_pos.x, FLOOR);
141     	else if (th->t_oldch == FLOOR && !cansee(th->t_pos.y, th->t_pos.x)
142 				&& !on(player, SEEMONST))
143 		    mvaddch(th->t_pos.y, th->t_pos.x, ' ');
144 		else
145 		    mvaddch(th->t_pos.y, th->t_pos.x, th->t_oldch);
146     }
147     oroom = th->t_room;
148     if (!ce(ch_ret, th->t_pos))
149     {
150 	if ((th->t_room = roomin(&ch_ret)) == NULL) {
151 	    th->t_room	= oroom;
152 	    return;
153 	}
154 	if (oroom != th->t_room)
155 	    th->t_dest	= find_dest(th);
156 	th->t_pos = ch_ret;
157     }
158  
159     if (see_monst(th)) {
160 	if (flat(ch_ret.y,ch_ret.x) & F_PASS)
161 		standout();
162 	th->t_oldch = mvinch(ch_ret.y, ch_ret.x);
163 	mvaddch(ch_ret.y, ch_ret.x, th->t_disguise);
164     }
165     else if (on(player,	SEEMONST)) 
166     {
167 	standout();
168 	th->t_oldch = mvinch(ch_ret.y, ch_ret.x);
169 	mvaddch(ch_ret.y, ch_ret.x, th->t_type);
170     }
171     else
172     	th->t_oldch = '@';
173     if (th->t_oldch == FLOOR &&	oroom->r_flags & ISDARK)
174 	th->t_oldch = ' ';
175     standend();
176 }
177 
178 /*
179  * see_monst:
180  *	Return TRUE if the hero can see the monster
181  */
182 see_monst(mp)
183 register THING *mp;
184 {
185     if (on(player, ISBLIND))
186 	return	FALSE;
187     if (on(*mp,	ISINVIS) && !on(player,	CANSEE))
188 	return	FALSE;
189     if (DISTANCE(mp->t_pos.y, mp->t_pos.x, hero.y, hero.x) >= LAMPDIST &&
190 	((mp->t_room != proom || (mp->t_room->r_flags & ISDARK) ||
191 		(mp->t_room->r_flags & ISMAZE))))
192 	    return FALSE;
193     /*
194      * If we are seeing	the enemy of a vorpally	enchanted weapon for the first
195      * time, give the player a hint as to what that weapon is good for.
196      */
197     if (cur_weapon != NULL && mp->t_type == cur_weapon->o_enemy
198 	&& ((cur_weapon->o_flags & DIDFLASH) == 0))
199     {
200 	cur_weapon->o_flags |=	DIDFLASH;
201 	msg(flash, w_names[cur_weapon->o_which], terse	|| expert ? "" : intense);
202     }
203     return TRUE;
204 }
205 
206 /*
207  * start_run:
208  *	Set a monster running after something or stop it from running
209  *	(for	when it	dies)
210  */
211 start_run(runner)
212 register coord *runner;
213 {
214     register THING *tp;
215 
216     /*
217      * If we couldn't find him,	something is funny
218      */
219     tp = moat(runner->y, runner->x);
220     if (tp != NULL) {
221 	    /*
222     	 *	Start the beastie running
223      	 */
224 	    tp->t_flags |= ISRUN;
225     	tp->t_flags &= ~ISHELD;
226 	    tp->t_dest	= find_dest(tp);
227 	}
228 #ifdef DEBUG
229 	else
230 		debug("start_run: moat == NULL ???");
231 #endif DEBUG
232 }
233 
234 /*
235  * chase:
236  *	Find	the spot for the chaser(er) to move closer to the
237  *	chasee(ee).	Returns	TRUE if	we want	to keep	on chasing later
238  *	FALSE if we reach the goal.
239  */
240 chase(tp, ee)
241 THING *tp;
242 coord *ee;
243 {
244     register int	x, y;
245     int	dist, thisdist;
246     register THING *obj;
247     coord *er;
248     byte ch;
249     int	plcnt =	1;
250 
251     er = &tp->t_pos;
252     /*
253      * If the thing is confused, let it	move randomly. Phantoms
254      * are slightly confused all of the	time, and bats are
255      * quite confused all the time
256      */
257     if ((on(*tp, ISHUH)	&& rnd(5) != 0)	|| (tp->t_type == 'P' && rnd(5)	== 0)
258 		|| (tp->t_type	== 'B' && rnd(2) == 0))
259     {
260 	/*
261 	 * get	a valid	random move
262 	 */
263 	rndmove(tp,&ch_ret);
264 	dist =	DISTANCE(ch_ret.y, ch_ret.x, ee->y, ee->x);
265 	/*
266 	 * Small chance that it will become un-confused 
267 	 */
268 	if (rnd(30) ==	17)
269 	    tp->t_flags &= ~ISHUH;
270     }
271     /*
272      * Otherwise, find the empty spot next to the chaser that is
273      * closest to the chasee.
274      */
275     else
276     {
277 	register int ey, ex;
278 	/*
279 	 * This will eventually hold where we move to get closer
280 	 * If we can't	find an	empty spot, we stay where we are.
281 	 */
282 	dist =	DISTANCE(er->y,	er->x, ee->y, ee->x);
283 	ch_ret	= *er;
284 
285 	ey = er->y + 1;
286 	ex = er->x + 1;
287 	for (x	= er->x	- 1; x <= ex; x++)
288 	{
289 	    for (y = er->y - 1; y <= ey; y++)
290 	    {
291 		coord	tryp;
292 
293 		tryp.x = x;
294 		tryp.y = y;
295 		if (offmap(y,	x) || !diag_ok(er, &tryp))
296 		    continue;
297 		ch = winat(y,	x);
298 		if (step_ok(ch))
299 		{
300 		    /*
301 		     * If it is a scroll, it might be	a scare	monster	scroll
302 		     * so we need to look it up to see what type it is.
303 		     */
304 		    if (ch ==	SCROLL)
305 		    {
306 			for (obj = lvl_obj; obj != NULL; obj	= next(obj))
307 			{
308 			    if (y ==	obj->o_pos.y &&	x == obj->o_pos.x)
309 				break;
310 			}
311 			if (obj != NULL && obj->o_which == S_SCARE)
312 			    continue;
313 		    }
314 		    /*
315 		     * If we didn't find any scrolls at this place or	it
316 		     * wasn't	a scare	scroll,	then this place	counts
317 		     */
318 		    thisdist = DISTANCE(y, x,	ee->y, ee->x);
319 		    if (thisdist < dist)
320 		    {
321 			plcnt = 1;
322 			ch_ret = tryp;
323 			dist	= thisdist;
324 		    }
325 		    else if (thisdist	== dist	&& rnd(++plcnt)	== 0)
326 		    {
327 			ch_ret = tryp;
328 			dist	= thisdist;
329 		    }
330 		}
331 	    }
332 	}
333     }
334 }
335 
336 /*
337  * roomin:
338  *	Find	what room some coordinates are in. NULL	means they aren't
339  *	in any room.
340  */
341 struct room *
342 roomin(cp)
343 register coord *cp;
344 {
345     register struct room *rp;
346     register byte *fp;
347 
348     for	(rp = rooms; rp	<= &rooms[MAXROOMS-1]; rp++)
349 	if (cp->x < rp->r_pos.x + rp->r_max.x && rp->r_pos.x <= cp->x
350 	 && cp->y < rp->r_pos.y + rp->r_max.y && rp->r_pos.y <= cp->y)
351 	    return rp;
352     fp = &flat(cp->y, cp->x);
353     if (*fp & F_PASS)
354 	return	&passages[*fp &	F_PNUM];
355 #ifdef DEBUG
356 	debug("in some bizarre place (%d, %d)", unc(*cp));
357 #endif DEBUG
358     bailout++;
359     return NULL;
360 }
361 
362 /*
363  * diag_ok:
364  *	Check to see	if the move is legal if	it is diagonal
365  */
366 diag_ok(sp, ep)
367 register coord *sp, *ep;
368 {
369     if (ep->x == sp->x || ep->y	== sp->y)
370 		return	TRUE;
371     return (step_ok(chat(ep->y,	sp->x))	&& step_ok(chat(sp->y, ep->x)));
372 }
373 
374 /*
375  * cansee:
376  *	Returns true	if the hero can	see a certain coordinate.
377  */
378 cansee(y, x)
379 register int y,	x;
380 {
381     register struct room *rer;
382     coord tp;
383 
384     if (on(player, ISBLIND))
385 		return	FALSE;
386     if (DISTANCE(y, x, hero.y, hero.x) < LAMPDIST)
387 		return	TRUE;
388     /*
389      * We can only see if the hero in the same room as
390      * the coordinate and the room is lit or if	it is close.
391      */
392     tp.y = y;
393     tp.x = x;
394     rer	= roomin(&tp);
395     return (rer	== proom && !(rer->r_flags & ISDARK));
396 }
397 
398 /*
399  * find_dest:
400  *	find	the proper destination for the monster
401  */
402 coord *
403 find_dest(tp)
404 register THING *tp;
405 {
406     register THING *obj;
407     register register int prob;
408     register struct room *rp;
409 
410     if ((prob =	monsters[tp->t_type - 'A'].m_carry) <= 0 || tp->t_room == proom
411 	|| see_monst(tp))
412 	    return &hero;
413     rp = tp->t_room;
414     for	(obj = lvl_obj;	obj != NULL; obj = next(obj))
415     {
416 	if (obj->o_type == SCROLL && obj->o_which == S_SCARE)
417 	    continue;
418 	if (roomin(&obj->o_pos) == rp && rnd(100) < prob)
419 	{
420 	    for (tp = mlist; tp != NULL; tp = next(tp))
421 		if (tp->t_dest == &obj->o_pos)
422 		    break;
423 	    if	(tp == NULL)
424 		return &obj->o_pos;
425 	}
426     }
427     return &hero;
428 }
429 