diff srogue/chase.c @ 36:2128c7dc8a40

Import Super-Rogue 9.0 from the Roguelike Restoration Project (r1490)
author elwin
date Thu, 25 Nov 2010 12:21:41 +0000
parents
children 94a0d9dd5ce1
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srogue/chase.c	Thu Nov 25 12:21:41 2010 +0000
@@ -0,0 +1,486 @@
+/*
+ * 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 "rogue.h"
+#include "rogue.ext"
+
+#define FARAWAY	32767
+#define RDIST(a, b)	(DISTANCE((a)->y, (a)->x, (b).y, (b).x))
+
+struct coord ch_ret;	/* Where chasing takes you */
+
+/*
+ * runners:
+ *	Make all the running monsters move.
+ */
+runners()
+{
+	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.
+ */
+do_chase(mon)
+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.
+ */
+chase(tp, ee, runaway, dofight)
+struct thing *tp;
+struct coord *ee;
+bool runaway, 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
+ */
+runto(runner, spot)
+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(cp)
+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(y, x)
+int y, 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
+ */
+diag_ok(sp, ep)
+struct coord *sp, *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.
+ */
+cansee(y, x)
+int y, 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;
+}