diff arogue5/chase.c @ 63:0ed67132cf10

Import Advanced Rogue 5.8 from the Roguelike Restoration Project (r1490)
author elwin
date Thu, 09 Aug 2012 22:58:48 +0000
parents
children 56e748983fa8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/arogue5/chase.c	Thu Aug 09 22:58:48 2012 +0000
@@ -0,0 +1,1008 @@
+/*
+ * Code for one object to chase another
+ *
+ * Advanced Rogue
+ * Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T
+ * 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 <ctype.h>
+#include <limits.h>
+#include "curses.h"
+#include "rogue.h"
+#define	MAXINT	INT_MAX
+#define	MININT	INT_MIN
+
+coord ch_ret;				/* Where chasing takes you */
+
+
+
+
+
+/*
+ * Canblink checks if the monster can teleport (blink).  If so, it will
+ * try to blink the monster next to the player.
+ */
+
+bool
+can_blink(tp)
+register struct thing *tp;
+{
+    register int y, x, index=9;
+    coord tryp;	/* To hold the coordinates for use in diag_ok */
+    bool spots[9], found_one=FALSE;
+
+    /*
+     * First, can the monster even blink?  And if so, there is only a 50%
+     * chance that it will do so.  And it won't blink if it is running or
+     * held.
+     */
+    if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) ||
+	on(*tp, ISFLEE) ||
+	(on(*tp, ISSLOW) && off(*tp, ISHASTE) && !(tp->t_turn)) ||
+	tp->t_no_move ||
+	(rnd(12) < 6)) return(FALSE);
+
+
+    /* Initialize the spots as illegal */
+    do {
+	spots[--index] = FALSE;
+    } while (index > 0);
+
+    /* Find a suitable spot next to the player */
+    for (y=hero.y-1; y<hero.y+2; y++)
+	for (x=hero.x-1; x<hero.x+2; x++, index++) {
+	    /* Make sure x coordinate is in range and that we are
+	     * not at the player's position
+	     */
+	    if (x<0 || x >= COLS || index == 4) continue;
+
+	    /* Is it OK to move there? */
+	    if (step_ok(y, x, NOMONST, tp) &&
+		(!isatrap(mvwinch(cw, y, x)) ||
+		  rnd(10) >= tp->t_stats.s_intel ||
+		  on(*tp, ISFLY))) {
+		/* OK, we can go here.  But don't go there if
+		 * monster can't get at player from there
+		 */
+		tryp.y = y;
+		tryp.x = x;
+		if (diag_ok(&tryp, &hero, tp)) {
+		    spots[index] = TRUE;
+		    found_one = TRUE;
+		}
+	    }
+	}
+
+    /* If we found one, go to it */
+    if (found_one) {
+	char rch;	/* What's really where the creatures moves to */
+
+	/* Find a legal spot */
+	while (spots[index=rnd(9)] == FALSE) continue;
+
+	/* Get the coordinates */
+	y = hero.y + (index/3) - 1;
+	x = hero.x + (index % 3) - 1;
+
+	/* Move the monster from the old space */
+	mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch);
+
+	/* Move it to the new space */
+	tp->t_oldch = CCHAR( mvwinch(cw, y, x) );
+
+	/* Display the creature if our hero can see it */
+	if (cansee(y, x) &&
+	    off(*tp, ISINWALL) &&
+	    !invisible(tp))
+	    mvwaddch(cw, y, x, tp->t_type);
+
+	/* Fix the monster window */
+	mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, ' '); /* Clear old position */
+	mvwaddch(mw, y, x, tp->t_type);
+
+	/* Record the new position */
+	tp->t_pos.y = y;
+	tp->t_pos.x = x;
+
+	/* If the monster is on a trap, trap it */
+	rch = CCHAR( mvinch(y, x) );
+	if (isatrap(rch)) {
+	    if (cansee(y, x)) tp->t_oldch = rch;
+	    be_trapped(tp, &(tp->t_pos));
+	}
+    }
+
+    return(found_one);
+}
+
+/* 
+ * Can_shoot determines if the monster (er) has a direct line of shot 
+ * at the player (ee).  If so, it returns the direction in which to shoot.
+ */
+
+coord *
+can_shoot(er, ee)
+register coord *er, *ee;
+{
+    static coord shoot_dir;
+
+    /* Make sure we are chasing the player */
+    if (!ce((*ee), hero)) return(NULL);
+
+    /* 
+     * They must be in the same room or very close (at door)
+     */
+    if (roomin(er) != roomin(&hero) && DISTANCE(er->y,er->x,ee->y,ee->x) > 1)
+	return(NULL);
+
+    /* Do we have a straight shot? */
+    if (!straight_shot(er->y, er->x, ee->y, ee->x, &shoot_dir)) return(NULL);
+    else return(&shoot_dir);
+}
+
+/*
+ * chase:
+ *	Find the spot for the chaser(er) to move closer to the
+ *	chasee(ee).  Returns TRUE if we want to keep on chasing later
+ *	FALSE if we reach the goal.
+ */
+
+chase(tp, ee, flee, mdead)
+register struct thing *tp;
+register coord *ee;
+bool flee; /* True if destination (ee) is player and monster is running away
+	    * or the player is in a wall and the monster can't get to it
+	    */
+bool *mdead;
+{
+    int damage, dist, thisdist, monst_dist = MAXINT; 
+    struct linked_list *weapon;
+    register coord *er = &tp->t_pos; 
+    coord *shoot_dir;
+    char ch, mch;
+    bool next_player = FALSE;
+
+    if (mdead != NULL)
+	*mdead = 0;
+
+    /* 
+     * set the distance from the chas(er) to the chas(ee) here and then
+     * we won't have to reset it unless the chas(er) moves (instead of shoots)
+     */
+    dist = DISTANCE(er->y, er->x, ee->y, ee->x);
+
+    /*
+     * If the thing is confused or it can't see the player,
+     * let it move randomly. 
+     */
+    if ((on(*tp, ISHUH) && rnd(10) < 8) ||
+	(on(player, ISINVIS) && off(*tp, CANSEE))) { /* Player is invisible */
+	/*
+	 * get a valid random move
+	 */
+	ch_ret = *rndmove(tp);
+	dist = DISTANCE(ch_ret.y, ch_ret.x, ee->y, ee->x);
+	/*
+	 * check to see if random move takes creature away from player
+	 * if it does then turn off ISHELD
+	 */
+	if (dist > 2) {
+	    if (on(*tp, DIDHOLD)) {
+		 turn_off(*tp, DIDHOLD);
+		 turn_on(*tp, CANHOLD);
+		 if (--hold_count == 0) 
+		     turn_off(player, ISHELD);
+	    }
+
+	    /* If monster was suffocating, stop it */
+	    if (on(*tp, DIDSUFFOCATE)) {
+		turn_off(*tp, DIDSUFFOCATE);
+		turn_on(*tp, CANSUFFOCATE);
+		extinguish(suffocate);
+	    }
+	}
+    }
+
+    /* If we can breathe, we may do so */
+    else if (on(*tp, CANBREATHE)		&&
+	     (dist < BOLT_LENGTH*BOLT_LENGTH)	&&
+	     (shoot_dir = can_shoot(er, ee))	&&
+	     !on(player, ISINWALL)		&&
+	     (rnd(100) < 75)) {
+		register char *breath = NULL;
+
+		damage = tp->t_stats.s_hpt;
+		/* Will it breathe at random */
+		if (on(*tp, CANBRANDOM)) {
+		    /* Turn off random breath */
+		    turn_off(*tp, CANBRANDOM);
+
+		    /* Select type of breath */
+		    switch (rnd(10)) {
+		    case 0: breath = "acid";
+			    turn_on(*tp, NOACID);
+		    when 1: breath = "flame";
+			    turn_on(*tp, NOFIRE);
+		    when 2: breath = "lightning bolt";
+			    turn_on(*tp, NOBOLT);
+		    when 3: breath = "chlorine gas";
+			    turn_on(*tp, NOGAS);
+		    when 4: breath = "ice";
+			    turn_on(*tp, NOCOLD);
+		    when 5: breath = "nerve gas";
+			    turn_on(*tp, NOPARALYZE);
+		    when 6: breath = "sleeping gas";
+			    turn_on(*tp, NOSLEEP);
+		    when 7: breath = "slow gas";
+			    turn_on(*tp, NOSLOW);
+		    when 8: breath = "confusion gas";
+			    turn_on(*tp, ISCLEAR);
+		    when 9: breath = "fear gas";
+			    turn_on(*tp, NOFEAR);
+		    }
+		}
+
+		/* Or can it breathe acid? */
+		else if (on(*tp, CANBACID)) {
+		    turn_off(*tp, CANBACID);
+		    breath = "acid";
+		}
+
+		/* Or can it breathe fire */
+		else if (on(*tp, CANBFIRE)) {
+		    turn_off(*tp, CANBFIRE);
+		    breath = "flame";
+		}
+
+		/* Or can it breathe electricity? */
+		else if (on(*tp, CANBBOLT)) {
+		    turn_off(*tp, CANBBOLT);
+		    breath = "lightning bolt";
+		}
+
+		/* Or can it breathe gas? */
+		else if (on(*tp, CANBGAS)) {
+		    turn_off(*tp, CANBGAS);
+		    breath = "chlorine gas";
+		}
+
+		/* Or can it breathe ice? */
+		else if (on(*tp, CANBICE)) {
+		    turn_off(*tp, CANBICE);
+		    breath = "ice";
+		}
+
+		else if (on(*tp, CANBPGAS)) {
+		    turn_off(*tp, CANBPGAS);
+		    breath = "nerve gas";
+		}
+
+		/* can it breathe sleeping gas */
+		else if (on(*tp, CANBSGAS)) {
+		    turn_off(*tp, CANBSGAS);
+		    breath = "sleeping gas";
+		}
+
+		/* can it breathe slow gas */
+		else if (on(*tp, CANBSLGAS)) {
+		    turn_off(*tp, CANBSLGAS);
+		    breath = "slow gas";
+		}
+		/* can it breathe confusion gas */
+		else if (on(*tp, CANBCGAS)) {
+		    turn_off(*tp, CANBCGAS);
+		    breath = "confusion gas";
+		}
+		/* can it breathe fear gas */
+		else {
+		    turn_off(*tp, CANBFGAS);
+		    breath = "fear gas";
+		}
+
+		/* Now breathe -- sets "monst_dead" if it kills someone */
+		*mdead = shoot_bolt(	tp, *er, *shoot_dir, FALSE, 
+				tp->t_index, breath, damage);
+
+		ch_ret = *er;
+		running = FALSE;
+		if (*mdead) return(TRUE);
+    }
+
+    /* We may shoot missiles if we can */
+    else if (on(*tp, CANMISSILE) 		&& 
+	    (shoot_dir = can_shoot(er, ee))	&&
+	    !on(player, ISINWALL)		&& 
+	    (rnd(100) < 75)) {
+	    static struct object missile =
+	    {
+		MISSILE, {0, 0}, "", 0, "", "0d4 " , NULL, 0, WS_MISSILE, 100, 1
+	    };
+
+	    sprintf(missile.o_hurldmg, "%dd4", tp->t_stats.s_lvl);
+	    do_motion(&missile, shoot_dir->y, shoot_dir->x, tp);
+	    hit_monster(unc(missile.o_pos), &missile, tp);
+	    turn_off(*tp, CANMISSILE);
+	    ch_ret = *er;
+	    running = FALSE;
+    }
+
+    /* We may use a sonic blast if we can */
+    else if (on(*tp, CANSONIC)			&& 
+	    (dist < BOLT_LENGTH*2)		&&
+	    (shoot_dir = can_shoot(er, ee))	&&
+	    !on(player, ISINWALL)		&& 
+	    (rnd(100) < 50)) {
+	    static struct object blast =
+	    {
+		MISSILE, {0, 0}, "", 0, "", "150" , NULL, 0, 0, 0, 0
+	    };
+
+	    turn_off(*tp, CANSONIC);
+	    do_motion(&blast, shoot_dir->y, shoot_dir->x, tp);
+	    damage = 150;
+	    if (save(VS_BREATH, &player, -3))
+		damage /= 2;
+	    msg ("The %s's sonic blast hits you", monsters[tp->t_index].m_name);
+	    if ((pstats.s_hpt -= damage) <= 0)
+		death(tp->t_index);
+	    ch_ret = *er;
+	    running = FALSE;
+    }
+    /*
+     * If we have a special magic item, we might use it.  We will restrict
+     * this options to uniques with relics for now.
+     */
+    else if (on(*tp, ISUNIQUE) && m_use_item(tp, er, ee)) {
+	    ch_ret = *er;
+	    running = FALSE;
+    }
+    /* 
+     * If we can shoot or throw something, we might do so.
+     * If next to player, then 80% prob will fight.
+     */
+    else if(on(*tp, CANSHOOT)			&&
+	    (shoot_dir = can_shoot(er, ee))	&&
+	    !on(player, ISINWALL)		&&
+	    (dist > 3 || (rnd(100) > 80))	&&
+	    (weapon = get_hurl(tp))) {
+		missile(shoot_dir->y, shoot_dir->x, weapon, tp);
+		ch_ret = *er;
+	}
+
+    /*
+     * Otherwise, find the empty spot next to the chaser that is
+     * closest to the chasee.
+     */
+    else {
+	register int ey, ex, x, y;
+	register struct room *rer, *ree;
+	int dist_to_old = MININT; /* Dist from goal to old position */
+
+	/* Get rooms */
+	rer = roomin(er);	/* Room the chasER (monster) is in */	
+	ree = roomin(ee);	/* Room the chasEE is in */
+
+	/*
+	 * This will eventually hold where we move to get closer
+	 * If we can't find an empty spot, we stay where we are.
+	 */
+	dist = flee ? 0 : MAXINT;
+	ch_ret = *er;
+
+	/* Are we at our goal already? */
+	if (!flee && ce(ch_ret, *ee)) return(FALSE);
+
+	ey = er->y + 1;
+	ex = er->x + 1;
+
+	/* Check all possible moves */
+	for (x = er->x - 1; x <= ex; x++) {
+	    if (x < 0 || x >= COLS) /* Don't try off the board */
+		continue;
+	    for (y = er->y - 1; y <= ey; y++) {
+		coord tryp;
+
+		if ((y < 1) || (y >= LINES - 2)) /* Don't try off the board */
+		    continue;
+
+		/* Don't try the player if not going after the player */
+		if ((flee || !ce(hero, *ee)) && x == hero.x && y == hero.y) {
+		    next_player = TRUE;
+		    continue;
+		}
+
+		tryp.x = x;
+		tryp.y = y;
+
+		/* Is there a monster on this spot closer to our goal?
+		 * Don't look in our spot or where we were.
+		 */
+		if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) &&
+		    isalpha(mch = CCHAR( mvwinch(mw, y, x) ) )) {
+		    int test_dist;
+
+		    test_dist = DISTANCE(y, x, ee->y, ee->x);
+		    if (test_dist <= 25 &&   /* Let's be fairly close */
+			test_dist < monst_dist) {
+			/* Could we really move there? */
+			mvwaddch(mw, y, x, ' '); /* Temporarily blank monst */
+			if (diag_ok(er, &tryp, tp)) monst_dist = test_dist;
+			mvwaddch(mw, y, x, mch); /* Restore monster */
+		    }
+		}
+
+		/* Can we move onto the spot? */	
+		if (!diag_ok(er, &tryp, tp)) continue;
+
+		ch = CCHAR( mvwinch(cw, y, x) );	/* Screen character */
+
+		/* Stepping on player is NOT okay if we are fleeing */
+		if (step_ok(y, x, NOMONST, tp) &&
+		    (off(*tp, ISFLEE) || ch != PLAYER))
+		{
+		    /*
+		     * If it is a trap, an intelligent monster may not
+		     * step on it (unless our hero is on top!)
+		     */
+		    if ((isatrap(ch))			&& 
+			(rnd(10) < tp->t_stats.s_intel) &&
+			(!on(*tp, ISFLY))		&&
+			(y != hero.y || x != hero.x)) 
+			    continue;
+
+		    /*
+		     * OK -- this place counts
+		     */
+		    thisdist = DISTANCE(y, x, ee->y, ee->x);
+
+		    /* Adjust distance if we are being shot at */
+		    if (tp->t_wasshot && tp->t_stats.s_intel > 5 &&
+			ce(hero, *ee)) {
+			/* Move out of line of sight */
+			if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL)) {
+			    if (flee) thisdist -= SHOTPENALTY;
+			    else thisdist += SHOTPENALTY;
+			}
+
+			/* But do we want to leave the room? */
+			else if (rer && rer == ree && ch == DOOR)
+			    thisdist += DOORPENALTY;
+		    }
+
+		    /* Don't move to the last position if we can help it
+		     * (unless out prey just moved there)
+		     */
+		    if (ce(tryp, tp->t_oldpos) && (flee || !ce(tryp, hero)))
+			dist_to_old = thisdist;
+
+		    else if ((flee && (thisdist > dist)) ||
+			(!flee && (thisdist < dist)))
+		    {
+			ch_ret = tryp;
+			dist = thisdist;
+		    }
+		}
+	    }
+	}
+
+	/* If we aren't trying to get the player, but he is in our way,
+	 * hit him (unless we have been turned)
+	 */
+	if (next_player && off(*tp, WASTURNED) &&
+	    ((flee && ce(ch_ret, *er)) ||
+	     (!flee && DISTANCE(er->y, er->x, ee->y, ee->x) < dist)) &&
+	    step_ok(tp->t_dest->y, tp->t_dest->x, NOMONST, tp)) {
+	    /* Okay to hit player */
+	    ch_ret = hero;
+	    return(FALSE);
+	}
+
+
+	/* If we can't get closer to the player (if that's our goal)
+	 * because other monsters are in the way, just stay put
+	 */
+	if (!flee && ce(hero, *ee) && monst_dist < MAXINT &&
+	    DISTANCE(er->y, er->x, hero.y, hero.x) < dist)
+		ch_ret = *er;
+
+	/* Do we want to go back to the last position? */
+	else if (dist_to_old != MININT &&      /* It is possible to move back */
+	    ((flee && dist == 0) ||	/* No other possible moves */
+	     (!flee && dist == MAXINT))) {
+	    /* Do we move back or just stay put (default)? */
+	    dist = DISTANCE(er->y, er->x, ee->y, ee->x); /* Current distance */
+	    if (!flee || (flee && (dist_to_old > dist))) ch_ret = tp->t_oldpos;
+	}
+    }
+
+    /* May actually hit here from a confused move */
+    return(!ce(ch_ret, hero));
+}
+
+/*
+ * do_chase:
+ *	Make one thing chase another.
+ */
+
+do_chase(th, flee)
+register struct thing *th;
+register bool flee; /* True if running away or player is inaccessible in wall */
+{
+    register struct room *rer, *ree,	/* room of chaser, room of chasee */
+			 *orig_rer,	/* Original room of chaser */
+			 *new_room;	/* new room of monster */
+    int dist = MININT;
+    int mindist = MAXINT, maxdist = MININT;
+    bool stoprun = FALSE,		/* TRUE means we are there */
+	 rundoor;			/* TRUE means run to a door */
+    bool mdead = 0;
+    char rch, sch;
+    coord *last_door=0,			/* Door we just came from */
+	   this;			/* Temporary destination for chaser */
+
+    /* Make sure the monster can move */
+    if (th->t_no_move != 0) {
+	th->t_no_move--;
+	return;
+    }
+
+    rer = roomin(&th->t_pos);	/* Find room of chaser */
+    ree = roomin(th->t_dest);	/* Find room of chasee */
+    orig_rer = rer;	/* Original room of chaser (including doors) */
+
+    /*
+     * We don't count monsters on doors as inside rooms for this routine
+     */
+    if ((sch = CCHAR( mvwinch(stdscr, th->t_pos.y, th->t_pos.x) )) == DOOR ||
+	sch == PASSAGE) {
+	rer = NULL;
+    }
+    this = *th->t_dest;
+
+    /*
+     * If we are not in a corridor and not a Xorn, then if we are running
+     * after the player, we run to a door if he is not in the same room.
+     * If we are fleeing, we run to a door if he IS in the same room.
+     * Note:  We don't bother with doors in mazes.
+     */
+    if (levtype != MAZELEV && rer != NULL && off(*th, CANINWALL)) {
+	if (flee) rundoor = (rer == ree);
+	else rundoor = (rer != ree);
+    }
+    else rundoor = FALSE;
+
+    if (rundoor) {
+	register struct linked_list *exitptr;	/* For looping through exits */
+	coord *exit;				/* A particular door */
+	int exity, exitx;			/* Door's coordinates */
+	char dch='\0';				/* Door character */
+
+	if (th->t_doorgoal)
+	    dch = CCHAR( mvwinch(stdscr, th->t_doorgoal->y, th->t_doorgoal->x) );
+	    
+	/* Do we have a valid goal? */
+	if ((dch == PASSAGE || dch == DOOR) &&  /* A real door */
+	    (!flee || !ce(*th->t_doorgoal, hero))) { /* Player should not
+						      * be at door if we are
+						      * running away
+						      */
+	    this = *th->t_doorgoal;
+	    dist = 0;	/* Indicate that we have our door */
+	}
+
+	/* Go through all the doors */
+	else for (exitptr = rer->r_exit; exitptr; exitptr = next(exitptr)) {
+	    exit = DOORPTR(exitptr);
+	    exity = exit->y;
+	    exitx = exit->x;
+
+	    /* Make sure it is a real door */
+	    dch = CCHAR( mvwinch(stdscr, exity, exitx) );
+	    if (dch == PASSAGE || dch == DOOR) {
+		/* Don't count a door if we are fleeing from the player and
+		 * he is standing on it
+		 */
+		if (flee && ce(*exit, hero)) continue;
+		
+		/* Were we just on this door? */
+		if (ce(*exit, th->t_oldpos)) last_door = exit;
+
+		else {
+		    dist = DISTANCE(th->t_dest->y, th->t_dest->x, exity, exitx);
+
+		    /* If fleeing, we want to maximize distance from door to
+		     * what we flee, and minimize distance from door to us.
+		     */
+		    if (flee)
+		       dist -= DISTANCE(th->t_pos.y, th->t_pos.x, exity, exitx);
+
+		    /* Maximize distance if fleeing, otherwise minimize it */
+		    if ((flee && (dist > maxdist)) ||
+			(!flee && (dist < mindist))) {
+			th->t_doorgoal = exit;	/* Use this door */
+			this = *exit;
+			mindist = maxdist = dist;
+		    }
+		}
+	    }
+	}
+
+	/* Could we not find a door? */
+	if (dist == MININT) {
+	    /* If we were on a door, go ahead and use it */
+	    if (last_door) {
+		th->t_doorgoal = last_door;
+		this = th->t_oldpos;
+		dist = 0;	/* Indicate that we found a door */
+	    }
+	    else th->t_doorgoal = NULL;	/* No more door goal */
+	}
+
+	/* Indicate that we do not want to flee from the door */
+	if (dist != MININT) flee = FALSE;
+    }
+    else th->t_doorgoal = 0;	/* Not going to any door */