view srogue/chase.c @ 239:837044d2c362

Merge the GCC5 and build fix branches. This fixes all warnings produced by GCC 5, except the ones related to system functions. Those could be fixed by including the proper headers, but it would be better to replace the system-dependent code with functions from mdport.c.
author John "Elwin" Edwards
date Fri, 11 Mar 2016 19:47:52 -0500
parents 94a0d9dd5ce1
children e52a8a7ad4c5
line wrap: on
line source

/*
 * Code for one object to chase another
 *
 * @(#)chase.c	9.0	(rdk)	 7/17/84
 *
 * Super-Rogue
 * Copyright (C) 1984 Robert D. Kindelberger
 * All rights reserved.
 *
 * Based on "Rogue: Exploring the Dungeons of Doom"
 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
 * All rights reserved.
 *
 * See the file LICENSE.TXT for full copyright and licensing information.
 */

#include <stdlib.h>
#include "rogue.h"
#include "rogue.ext"

#define FARAWAY	32767
#define RDIST(a, b)	(DISTANCE((a)->y, (a)->x, (b).y, (b).x))

int do_chase(struct linked_list *mon);
int chase(struct thing *tp, struct coord *ee, bool runaway, bool dofight);

struct coord ch_ret;	/* Where chasing takes you */

/*
 * runners:
 *	Make all the running monsters move.
 */
void
runners(void)
{
	reg struct thing *tp;
	reg struct linked_list *mon,*nextmon;

	for (mon = mlist; mon != NULL; mon = nextmon) {
		tp = THINGPTR(mon);
		nextmon = next(mon);
		if (off(*tp, ISHELD) && on(*tp, ISRUN)) {
			if (tp->t_nomove > 0)
				if (--tp->t_nomove > 0)
					continue;
			if (on(*tp, ISHASTE))
				if (do_chase(mon) == -1)
					continue;
			if (off(*tp, ISSLOW) || tp->t_turn)
				if (do_chase(mon) == -1)
					continue;
			tp->t_turn ^= TRUE;
		}
	}
}


/*
 * do_chase:
 *	Make one thing chase another.
 */
int
do_chase(struct linked_list *mon)
{
	reg struct thing *th;
	reg struct room *rer, *ree, *rxx;
	reg int mindist, i, dist;
	struct stats *st;
	bool stoprun = FALSE, ondoor = FALSE, link = FALSE;
	char runaway, dofight, wound, sch, ch;
	struct coord this;
	struct trap *trp;

	th = THINGPTR(mon);
	wound = th->t_flags & ISWOUND;
	if (wound)
		mindist = 0;
	else
		mindist = FARAWAY;
	runaway = wound;
	dofight = !runaway;
	rer = th->t_room;
	if (th->t_type == 'V') {
		if (rer != NULL && !rf_on(rer, ISDARK)) {
			/*
			 * Vampires can't stand the light
			 */
			if (cansee(th->t_pos.y, th->t_pos.x))
				msg("The vampire vaporizes into thin air !");
			killed(mon, FALSE);
			return(-1);
		}
	}
	ree = roomin(th->t_dest);	/* room of chasee */
	this = *th->t_dest;
	/*
	 * If the object of our desire is in a different
	 * room, then run to the door nearest to our goal.
	 */
	if (mvinch(th->t_pos.y, th->t_pos.x) == DOOR)
		ondoor = TRUE;
	rxx = NULL;
	if (rer != NULL || ree != NULL) {
		/*
		 * Monster not in room, hero in room. Run to closest door
		 * in hero's room if not wounded. Run away if wounded.
		 */
		if (rer == NULL && ree != NULL) {
			if (!wound)
				rxx = ree;
		}
		/*
		 * Monster in a room, hero not in room. If on a door,
		 * then use closest distance. If not on a door, then
		 * run to closest door in monsters room.
		 */
		else if (rer != NULL && ree == NULL) {
			if (!ondoor) {
				rxx = rer;
				if (wound)
					runaway = FALSE;
			}
		}
		/*
		 * Both hero and monster in a DIFFERENT room. Set flag to
		 * check for links between the monster's and hero's rooms.
		 * If no links are found, then the closest door in the
		 * monster's room is used.
		 */
		else if (rer != ree) {
			if (!wound) {
				link = TRUE;
				if (ondoor)
					rxx = ree;	/* if on door, run to heros room */
				else
					rxx = rer;	/* else to nearest door this room */
			}
		}
		/*
		 * Both hero and monster in same room. If monster is
		 * wounded, find the best door to run to.
		 */
		else if (wound) {
			struct coord *ex;
			int poss, mdtd, hdtd, ghdtd, nx, gx = 0, best;

			best = ghdtd = -FARAWAY;
			for (nx = 0; nx < ree->r_nexits; nx++) {
				ex = &ree->r_exit[nx];
				if (mvinch(ex->y, ex->x) == SECRETDOOR)
					continue;
				gx += 1;
				mdtd = abs(th->t_pos.y - ex->y) + abs(th->t_pos.x - ex->x);
				hdtd = abs(hero.y - ex->y) + abs(hero.x - ex->x);
				poss = hdtd - mdtd;				/* possible move */
				if (poss > best) {
					best = poss;
					this = *ex;
				}
				else if (poss == best && hdtd > ghdtd) {
					ghdtd = hdtd;
					best = poss;
					this = *ex;
				}
			}
			runaway = FALSE;		/* go for target */
			if (best < 1)
				dofight = TRUE;		/* fight if we must */
			mdtd = (gx <= 1 && best < 1);
			if (ondoor || mdtd) {
				this = hero;
				runaway = TRUE;
				if (!mdtd)
					dofight = FALSE;
			}
		}
		if (rxx != NULL) {
			for (i = 0; i < rxx->r_nexits; i += 1) {
				dist = RDIST(th->t_dest, rxx->r_exit[i]);
				if (link && rxx->r_ptr[i] == ree)
					dist = -1;
				if ((!wound && dist < mindist) ||
				    (wound && dist > mindist)) {
					this = rxx->r_exit[i];
					mindist = dist;
				}
			}
		}
	}
	else if (DISTANCE(hero.y, hero.x, th->t_pos.y, th->t_pos.x) <= 3)
		dofight = TRUE;
	/*
	 * this now contains what we want to run to this time
	 * so we run to it.  If we hit it we either want to
	 * fight it or stop running.
	 */
	if (chase(th, &this, runaway, dofight) == FIGHT) {
		return( attack(th) );
	}
	else if ((th->t_flags & (ISSTUCK | ISPARA)))
		return(0);				/* if paralyzed or stuck */
	if ((trp = trap_at(ch_ret.y, ch_ret.x)) != NULL) {
		ch = be_trapped(&ch_ret, th);
		if (ch == GONER || nlmove) {
			if (ch == GONER)
				remove_monster(&th->t_pos, mon);
			nlmove = FALSE;
			return((ch == GONER) ? -1 : 0);
		}
	}
	if (pl_off(ISBLIND))
		mvwaddch(cw,th->t_pos.y,th->t_pos.x,th->t_oldch);
	sch = mvwinch(cw, ch_ret.y, ch_ret.x);
	if (rer != NULL && rf_on(rer,ISDARK) && sch == FLOOR &&
	  DISTANCE(ch_ret.y,ch_ret.x,th->t_pos.y,th->t_pos.x) < 3 &&
	  pl_off(ISBLIND))
		th->t_oldch = ' ';
	else
		th->t_oldch = sch;
	if (cansee(unc(ch_ret)) && off(*th, ISINVIS))
		mvwaddch(cw, ch_ret.y, ch_ret.x, th->t_type);
	mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' ');
	mvwaddch(mw, ch_ret.y, ch_ret.x, th->t_type);
	th->t_oldpos = th->t_pos;
	th->t_pos = ch_ret;
	th->t_room = roomin(&ch_ret);
	i = 5;
	if (th->t_flags & ISREGEN)
		i = 40;
	st = &th->t_stats;
	if (rnd(100) < i) {
		if (++st->s_hpt > st->s_maxhp)
			st->s_hpt = st->s_maxhp;
		if (!monhurt(th))
			th->t_flags &= ~ISWOUND;
	}
	if (stoprun && ce(th->t_pos, *(th->t_dest)))
		th->t_flags &= ~ISRUN;
	return CHASE;
}


/*
 * chase:
 *	Find the spot for the chaser to move closer to the
 *	chasee.  Returns TRUE if we want to keep on chasing
 *	later FALSE if we reach the goal.
 */
int
chase(struct thing *tp, struct coord *ee, bool runaway, bool dofight)
{
	reg int x, y, ch;
	reg int dist, thisdist, closest;
	reg struct coord *er = &tp->t_pos;
	struct coord try, closecoord;
	int numsteps, onscare;

	/*
	 * If the thing is confused, let it move randomly.
	 */
	ch = CHASE;
	onscare = FALSE;
	if (on(*tp, ISHUH)) {
		ch_ret = *rndmove(tp);
		dist = DISTANCE(hero.y, hero.x, ch_ret.y, ch_ret.x);
		if (rnd(1000) < 5)
			tp->t_flags &= ~ISHUH;
		if (dist == 0)
			ch = FIGHT;
	}
	else {
		/*
		 * Otherwise, find the the best spot to run to
		 * in order to get to your goal.
		 */
		numsteps = 0;
		if (runaway)
			closest = 0;
		else
			closest = FARAWAY;
		ch_ret = *er;
		closecoord = tp->t_oldpos;
		for (y = er->y - 1; y <= er->y + 1; y += 1) {
			for (x = er->x - 1; x <= er->x + 1; x += 1) {
				if (!cordok(y, x))
					continue;
				try.x = x;
				try.y = y;
				if (!diag_ok(er, &try))
					continue;
				ch = winat(y, x);
				if (step_ok(ch)) {
					struct trap *trp;

					if (isatrap(ch)) {
						trp = trap_at(y, x);
						if (trp != NULL && off(*tp, ISHUH)) {
							/*
							 * Dont run over found traps unless
							 * the hero is standing on it. If confused,
							 * then he can run into them.
							 */
							if (trp->tr_flags & ISFOUND) {
								if (trp->tr_type == POOL && rnd(100) < 80)
									continue;
								else if (y != hero.y || x != hero.x)
									continue;
							}
						}
					}
					/*
					 * Check for scare monster scrolls.
					 */
					if (ch == SCROLL) {
						struct linked_list *item;

						item = find_obj(y, x);
						if (item != NULL)
							if ((OBJPTR(item))->o_which == S_SCARE) {
								if (ce(hero, try))
									onscare = TRUE;
								continue;
							}
					}
					/*
					 * Vampires will not run into a lit room.
					 */
					if (tp->t_type == 'V') {
						struct room *lr;

						lr = roomin(&try);
						if (lr != NULL && !rf_on(lr, ISDARK))
							continue;
					}
					/*
					 * This is a valid place to step
					 */
					if (y == hero.y && x == hero.x) {
						if (dofight) {
							ch_ret = try;	/* if fighting */
							return FIGHT;	/* hit hero */
						}
						else
							continue;
					}
					thisdist = DISTANCE(y, x, ee->y, ee->x);
					if (thisdist <= 0) {
						ch_ret = try;	/* got here but */
						return CHASE;	/* dont fight */
					}
					numsteps += 1;
					if ((!runaway && thisdist < closest) ||
						(runaway && thisdist > closest)) {
						/*
						 * dont count the monsters last position as
						 * the closest spot, unless running away and
						 * in the same room.
						 */
						if (!ce(try, tp->t_oldpos) || (runaway
						  && player.t_room == tp->t_room
						  && tp->t_room != NULL)) {
							closest = thisdist;
							closecoord = try;
						}
					}
				}
			}
		}
		/*
		 * If dead end, then go back from whence you came.
		 * Otherwise, pick the closest of the remaining spots.
		 */
		if (numsteps > 0)			/* move to best spot */
			ch_ret = closecoord;
		else {						/* nowhere to go */
			if (DISTANCE(tp->t_pos.y, tp->t_pos.x, hero.y, hero.x) < 2)
				if (!onscare)
					ch_ret = hero;
		}
		if (ce(hero, ch_ret))
			ch = FIGHT;
	}
	return ch;
}


/*
 * runto:
 *	Set a monster running after something
 */
void
runto(struct coord *runner, struct coord *spot)
{
	reg struct linked_list *item;
	reg struct thing *tp;

	if ((item = find_mons(runner->y, runner->x)) == NULL)
		return;
	tp = THINGPTR(item);
	if (tp->t_flags & ISPARA)
		return;
	tp->t_dest = spot;
	tp->t_flags |= ISRUN;
	tp->t_flags &= ~ISHELD;
}


/*
 * roomin:
 *	Find what room some coordinates are in.
 *	NULL means they aren't in any room.
 */
struct room *
roomin(struct coord *cp)
{
	reg struct room *rp;

	if (cordok(cp->y, cp->x)) {
		for (rp = rooms; rp < &rooms[MAXROOMS]; rp += 1)
			if (inroom(rp, cp))
				return rp;
	}
	return NULL;
}


/*
 * find_mons:
 *	Find the monster from his coordinates
 */
struct linked_list *
find_mons(int y, int x)
{
	reg struct linked_list *item;
	reg struct thing *th;

	for (item = mlist; item != NULL; item = next(item)) {
		th = THINGPTR(item);
		if (th->t_pos.y == y && th->t_pos.x == x)
			return item;
	}
	return NULL;
}


/*
 * diag_ok:
 *	Check to see if the move is legal if it is diagonal
 */
bool
diag_ok(struct coord *sp, struct coord *ep)
{
	if (ep->x == sp->x || ep->y == sp->y)
		return TRUE;
	if (step_ok(mvinch(ep->y,sp->x)) && step_ok(mvinch(sp->y,ep->x)))
		return TRUE;
	return FALSE;
}


/*
 * cansee:
 *	returns true if the hero can see a certain coordinate.
 */
bool
cansee(int y, int x)
{
	reg struct room *rer;
	struct coord tp;

	if (pl_on(ISBLIND))
		return FALSE;
	/*
	 * We can only see if the hero in the same room as
	 * the coordinate and the room is lit or if it is close.
	 */
	if (DISTANCE(y, x, hero.y, hero.x) < 3)
		return TRUE;
	tp.y = y;
	tp.x = x;
	rer = roomin(&tp);
	if (rer != NULL && levtype != MAZELEV)
		if (rer == player.t_room && !rf_on(rer,ISDARK))
			return TRUE;
	return FALSE;
}