rogue

Ken Arnold's Rogue
git clone https://git.woozle.org/neale/rogue.git

Neale Pickett  ·  2011-10-13

netprot.c

  1/*
  2 * Code for one creature to chase another
  3 *
  4 * @(#)netprot.c	4.57 (Berkeley) 02/05/99
  5 */
  6
  7#include <curses.h>
  8#include "netprot.h"
  9
 10#define DRAGONSHOT  5	/* one chance in DRAGONSHOT that a dragon will flame */
 11
 12static coord Ch_ret;				/* Where chasing takes you */
 13
 14/*
 15 * runners:
 16 *	Make all the running monsters move.
 17 */
 18void
 19runners(void)
 20{
 21    THING *tp;
 22    THING *next;
 23    bool wastarget;
 24    static coord orig_pos;
 25
 26    for (tp = Mlist; tp != NULL; tp = next)
 27    {
 28	/* remember this in case the monster's "next" is changed */
 29	next = next(tp);
 30
 31	if (!on(*tp, ISHELD) && on(*tp, ISRUN))
 32	{
 33	    orig_pos = tp->t_pos;
 34	    wastarget = on(*tp, ISTARGET);
 35	    move_monst(tp);
 36	    if (on(*tp, ISFLY) && dist_cp(&Hero, &tp->t_pos) >= 3)
 37		move_monst(tp);
 38	    if (wastarget && !ce(orig_pos, tp->t_pos))
 39	    {
 40		tp->t_flags &= ~ISTARGET;
 41		To_death = FALSE;
 42	    }
 43	}
 44    }
 45    if (Has_hit)
 46    {
 47	endmsg();
 48	Has_hit = FALSE;
 49    }
 50}
 51
 52/*
 53 * move_monst:
 54 *	Execute a single turn of running for a monster
 55 */
 56void
 57move_monst(THING *tp)
 58{
 59    if (!on(*tp, ISSLOW) || tp->t_turn)
 60	do_chase(tp);
 61    if (on(*tp, ISHASTE))
 62	do_chase(tp);
 63    tp->t_turn ^= TRUE;
 64}
 65
 66/*
 67 * relocate:
 68 *	Make the monster's new location be the specified one, updating
 69 *	all the relevant state.
 70 */
 71void
 72relocate(THING *th, coord *new_loc)
 73{
 74    struct room *oroom;
 75
 76    if (!ce(*new_loc, th->t_pos))
 77    {
 78	mvaddch(th->t_pos.y, th->t_pos.x, th->t_oldch);
 79	th->t_room = roomin(new_loc);
 80	set_oldch(th, new_loc);
 81	oroom = th->t_room;
 82	moat(th->t_pos.y, th->t_pos.x) = NULL;
 83
 84	if (oroom != th->t_room)
 85	    th->t_dest = find_dest(th);
 86	th->t_pos = *new_loc;
 87	moat(new_loc->y, new_loc->x) = th;
 88    }
 89    move(new_loc->y, new_loc->x);
 90    if (see_monst(th))
 91	addch(th->t_disguise);
 92    else if (on(Player, SEEMONST))
 93    {
 94	standout();
 95	addch(th->t_type);
 96	standend();
 97    }
 98}
 99
100/*
101 * do_chase:
102 *	Make one thing chase another.
103 */
104void
105do_chase(THING *th)
106{
107    coord *cp;
108    struct room *rer, *ree;	/* room of chaser, room of chasee */
109    int mindist = 32767, curdist;
110    bool stoprun = FALSE;	/* TRUE means we are there */
111    bool door;
112    THING *obj;
113    static coord this;			/* Temporary destination for chaser */
114
115    rer = th->t_room;		/* Find room of chaser */
116    if (on(*th, ISGREED) && rer->r_goldval == 0)
117	th->t_dest = &Hero;	/* If gold has been taken, run after hero */
118    if (th->t_dest == &Hero)	/* Find room of chasee */
119	ree = Proom;
120    else
121	ree = roomin(th->t_dest);
122    /*
123     * We don't count doors as inside rooms for this routine
124     */
125    door = (chat(th->t_pos.y, th->t_pos.x) == DOOR);
126    /*
127     * If the object of our desire is in a different room,
128     * and we are not in a corridor, run to the door nearest to
129     * our goal.
130     */
131over:
132    if (rer != ree)
133    {
134	for (cp = rer->r_exit; cp < &rer->r_exit[rer->r_nexits]; cp++)
135	{
136	    curdist = dist_cp(th->t_dest, cp);
137	    if (curdist < mindist)
138	    {
139		this = *cp;
140		mindist = curdist;
141	    }
142	}
143	if (door)
144	{
145	    rer = &Passages[flat(th->t_pos.y, th->t_pos.x) & F_PNUM];
146	    door = FALSE;
147	    goto over;
148	}
149    }
150    else
151    {
152	this = *th->t_dest;
153	/*
154	 * For dragons check and see if (a) the hero is on a straight
155	 * line from it, and (b) that it is within shooting distance,
156	 * but outside of striking range.
157	 */
158	if (th->t_type == 'D' && (th->t_pos.y == Hero.y || th->t_pos.x == Hero.x
159	    || abs(th->t_pos.y - Hero.y) == abs(th->t_pos.x - Hero.x))
160	    && dist_cp(&th->t_pos, &Hero) <= BOLT_LENGTH * BOLT_LENGTH
161	    && !on(*th, ISCANC) && rnd(DRAGONSHOT) == 0)
162	{
163	    Delta.y = sign(Hero.y - th->t_pos.y);
164	    Delta.x = sign(Hero.x - th->t_pos.x);
165	    if (Has_hit)
166		endmsg();
167	    fire_bolt(&th->t_pos, &Delta, "flame");
168	    Running = FALSE;
169	    Count = 0;
170	    Quiet = 0;
171	    if (To_death && !on(*th, ISTARGET))
172	    {
173		To_death = FALSE;
174		Kamikaze = FALSE;
175	    }
176	    return;
177	}
178    }
179    /*
180     * This now contains what we want to run to this time
181     * so we run to it.  If we hit it we either want to fight it
182     * or stop running
183     */
184    if (!chase(th, &this))
185    {
186	if (ce(this, Hero))
187	{
188	    attack(th);
189	    return;
190	}
191	else if (ce(this, *th->t_dest))
192	{
193	    for (obj = Lvl_obj; obj != NULL; obj = next(obj))
194		if (th->t_dest == &obj->o_pos)
195		{
196		    detach(Lvl_obj, obj);
197		    attach(th->t_pack, obj);
198		    chat(obj->o_pos.y, obj->o_pos.x) =
199			(th->t_room->r_flags & ISGONE) ? PASSAGE : FLOOR;
200		    th->t_dest = find_dest(th);
201		    break;
202		}
203	    if (th->t_type != 'F')
204		stoprun = TRUE;
205	}
206    }
207    else
208    {
209	if (th->t_type == 'F')
210	    return;
211    }
212    relocate(th, &Ch_ret);
213    /*
214     * And stop running if need be
215     */
216    if (stoprun && ce(th->t_pos, *(th->t_dest)))
217	th->t_flags &= ~ISRUN;
218}
219
220/*
221 * set_oldch:
222 *	Set the oldch character for the monster
223 */
224void
225set_oldch(THING *tp, coord *cp)
226{
227    char sch;
228
229    if (ce(tp->t_pos, *cp))
230	return;
231
232    sch = tp->t_oldch;
233    tp->t_oldch = mvinch(cp->y, cp->x);
234    if (!on(Player, ISBLIND))
235	if ((sch == FLOOR || tp->t_oldch == FLOOR) &&
236	    (tp->t_room->r_flags & ISDARK))
237		tp->t_oldch = ' ';
238	else if (dist_cp(cp, &Hero) <= LAMPDIST && See_floor)
239	    tp->t_oldch = chat(cp->y, cp->x);
240}
241
242/*
243 * see_monst:
244 *	Return TRUE if the hero can see the monster
245 */
246bool
247see_monst(THING *mp)
248{
249    int y, x;
250
251    if (on(Player, ISBLIND))
252	return FALSE;
253    if (on(*mp, ISINVIS) && !on(Player, CANSEE))
254	return FALSE;
255    y = mp->t_pos.y;
256    x = mp->t_pos.x;
257    if (dist(y, x, Hero.y, Hero.x) < LAMPDIST)
258    {
259	if (y != Hero.y && x != Hero.x &&
260	    !step_ok(chat(y, Hero.x)) && !step_ok(chat(Hero.y, x)))
261		return FALSE;
262	return TRUE;
263    }
264    if (mp->t_room != Proom)
265	return FALSE;
266    return (!(mp->t_room->r_flags & ISDARK));
267}
268
269/*
270 * runto:
271 *	Set a monster running after the hero.
272 */
273void
274runto(coord *runner)
275{
276    THING *tp;
277
278    /*
279     * If we couldn't find him, something is funny
280     */
281#ifdef MASTER
282    if ((tp = moat(runner->y, runner->x)) == NULL)
283	msg("couldn't find monster in runto at (%d,%d)", runner->y, runner->x);
284#else
285    tp = moat(runner->y, runner->x);
286#endif
287    /*
288     * Start the beastie running
289     */
290    tp->t_flags |= ISRUN;
291    tp->t_flags &= ~ISHELD;
292    tp->t_dest = find_dest(tp);
293}
294
295/*
296 * chase:
297 *	Find the spot for the chaser(er) to move closer to the
298 *	chasee(ee).  Returns TRUE if we want to keep on chasing later
299 *	FALSE if we reach the goal.
300 */
301bool
302chase(THING *tp, coord *ee)
303{
304    THING *obj;
305    int x, y;
306    int curdist, thisdist;
307    coord *er = &tp->t_pos;
308    char ch;
309    int plcnt = 1;
310    static coord tryp;
311
312    /*
313     * If the thing is confused, let it move randomly. Invisible
314     * Stalkers are slightly confused all of the time, and bats are
315     * quite confused all the time
316     */
317    if ((on(*tp, ISHUH) && rnd(5) != 0) || (tp->t_type == 'P' && rnd(5) == 0)
318	|| (tp->t_type == 'B' && rnd(2) == 0))
319    {
320	/*
321	 * get a valid random move
322	 */
323	Ch_ret = *rndmove(tp);
324	curdist = dist_cp(&Ch_ret, ee);
325	/*
326	 * Small chance that it will become un-confused 
327	 */
328	if (rnd(20) == 0)
329	    tp->t_flags &= ~ISHUH;
330    }
331    /*
332     * Otherwise, find the empty spot next to the chaser that is
333     * closest to the chasee.
334     */
335    else
336    {
337	int ey, ex;
338	/*
339	 * This will eventually hold where we move to get closer
340	 * If we can't find an empty spot, we stay where we are.
341	 */
342	curdist = dist_cp(er, ee);
343	Ch_ret = *er;
344
345	ey = er->y + 1;
346	if (ey >= NUMLINES - 1)
347	    ey = NUMLINES - 2;
348	ex = er->x + 1;
349	if (ex >= NUMCOLS)
350	    ex = NUMCOLS - 1;
351
352	for (x = er->x - 1; x <= ex; x++)
353	{
354	    if (x < 0)
355		continue;
356	    tryp.x = x;
357	    for (y = er->y - 1; y <= ey; y++)
358	    {
359		tryp.y = y;
360		if (!diag_ok(er, &tryp))
361		    continue;
362		ch = winat(y, x);
363		if (step_ok(ch))
364		{
365		    /*
366		     * If it is a scroll, it might be a scare monster scroll
367		     * so we need to look it up to see what type it is.
368		     */
369		    if (ch == SCROLL)
370		    {
371			for (obj = Lvl_obj; obj != NULL; obj = next(obj))
372			{
373			    if (y == obj->o_pos.y && x == obj->o_pos.x)
374				break;
375			}
376			if (obj != NULL && obj->o_which == S_SCARE)
377			    continue;
378		    }
379		    /*
380		     * It can also be a Xeroc, which we shouldn't step on
381		     */
382		    if ((obj = moat(y, x)) != NULL && obj->t_type == 'X')
383			continue;
384		    /*
385		     * If we didn't find any scrolls at this place or it
386		     * wasn't a scare scroll, then this place counts
387		     */
388		    thisdist = dist(y, x, ee->y, ee->x);
389		    if (thisdist < curdist)
390		    {
391			plcnt = 1;
392			Ch_ret = tryp;
393			curdist = thisdist;
394		    }
395		    else if (thisdist == curdist && rnd(++plcnt) == 0)
396		    {
397			Ch_ret = tryp;
398			curdist = thisdist;
399		    }
400		}
401	    }
402	}
403    }
404    return (curdist != 0 && !ce(Ch_ret, Hero));
405}
406
407/*
408 * roomin:
409 *	Find what room some coordinates are in. NULL means they aren't
410 *	in any room.
411 */
412struct room *
413roomin(coord *cp)
414{
415    struct room *rp;
416    char *fp;
417
418
419    fp = &flat(cp->y, cp->x);
420    if (*fp & F_PASS)
421	return &Passages[*fp & F_PNUM];
422
423    for (rp = Rooms; rp < &Rooms[MAXROOMS]; rp++)
424	if (cp->x <= rp->r_pos.x + rp->r_max.x && rp->r_pos.x <= cp->x
425	 && cp->y <= rp->r_pos.y + rp->r_max.y && rp->r_pos.y <= cp->y)
426	    return rp;
427
428    msg("in some bizarre place (%d, %d)", unc(*cp));
429#ifdef MASTER
430    abort();
431    /* NOTREACHED */
432#else
433    return NULL;
434#endif
435}
436
437/*
438 * diag_ok:
439 *	Check to see if the move is legal if it is diagonal
440 */
441bool
442diag_ok(coord *sp, coord *ep)
443{
444    if (ep->x < 0 || ep->x >= NUMCOLS || ep->y <= 0 || ep->y >= NUMLINES - 1)
445	return FALSE;
446    if (ep->x == sp->x || ep->y == sp->y)
447	return TRUE;
448    return (step_ok(chat(ep->y, sp->x)) && step_ok(chat(sp->y, ep->x)));
449}
450
451/*
452 * cansee:
453 *	Returns true if the hero can see a certain coordinate.
454 */
455bool
456cansee(int y, int x)
457{
458    struct room *rer;
459    static coord tp;
460
461    if (on(Player, ISBLIND))
462	return FALSE;
463    if (dist(y, x, Hero.y, Hero.x) < LAMPDIST)
464    {
465	if (flat(y, x) & F_PASS)
466	    if (y != Hero.y && x != Hero.x &&
467		!step_ok(chat(y, Hero.x)) && !step_ok(chat(Hero.y, x)))
468		    return FALSE;
469	return TRUE;
470    }
471    /*
472     * We can only see if the hero in the same room as
473     * the coordinate and the room is lit or if it is close.
474     */
475    tp.y = y;
476    tp.x = x;
477    return ((rer = roomin(&tp)) == Proom && !(rer->r_flags & ISDARK));
478}
479
480/*
481 * find_dest:
482 *	find the proper destination for the monster
483 */
484coord *
485find_dest(THING *tp)
486{
487    THING *obj;
488    int prob;
489
490    if ((prob = Monsters[tp->t_type - 'A'].m_carry) <= 0 || tp->t_room == Proom
491	|| see_monst(tp))
492	    return &Hero;
493    for (obj = Lvl_obj; obj != NULL; obj = next(obj))
494    {
495	if (obj->o_type == SCROLL && obj->o_which == S_SCARE)
496	    continue;
497	if (roomin(&obj->o_pos) == tp->t_room && rnd(100) < prob)
498	{
499	    for (tp = Mlist; tp != NULL; tp = next(tp))
500		if (tp->t_dest == &obj->o_pos)
501		    break;
502	    if (tp == NULL)
503		return &obj->o_pos;
504	}
505    }
506    return &Hero;
507}
508
509/*
510 * dist:
511 *	Calculate the "distance" between to points.  Actually,
512 *	this calculates d^2, not d, but that's good enough for
513 *	our purposes, since it's only used comparitively.
514 */
515int
516dist(int y1, int x1, int y2, int x2)
517{
518    return ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
519}
520
521/*
522 * dist_cp:
523 *	Call dist() with appropriate arguments for coord pointers
524 */
525int
526dist_cp(coord *c1, coord *c2)
527{
528    return dist(c1->y, c1->x, c2->y, c2->x);
529}