view arogue5/chase.c @ 159:44a0fce4b168

arogue7, xrogue: fix configure's wizardmode and limitscore options. WIZARD and LIMITSCORE, when set by options to './configure', are no longer overridden in mach_dep.h.
author John "Elwin" Edwards
date Fri, 05 Jun 2015 13:57:38 -0400
parents 0ed67132cf10
children 56e748983fa8
line wrap: on
line source

/*
 * 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;