Mercurial > hg > early-roguelike
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 */