Mercurial > hg > early-roguelike
diff urogue/chase.c @ 256:c495a4f288c6
Import UltraRogue from the Roguelike Restoration Project (r1490)
author | John "Elwin" Edwards |
---|---|
date | Tue, 31 Jan 2017 19:56:04 -0500 |
parents | |
children | 0250220d8cdd |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/chase.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1326 @@ +/* + chase.c - Code for one creature to chase another + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + 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 <ctype.h> +#include <limits.h> +#include "rogue.h" + +/* + do_chase() + Make one thing chase another. +*/ + +void +do_chase(struct thing *th, int flee) +{ + struct room *rer; /* room of chaser */ + struct room *ree; /* room of chasee */ + struct room *old_room; /* old room of monster */ + struct room *new_room; /* new room of monster */ + + int i, mindist = INT_MAX, maxdist = INT_MIN, dist = INT_MIN; + + int last_door = -1; /* Door we just came from */ + int stoprun = FALSE; /* TRUE means we are there */ + int rundoor; /* TRUE means run to a door */ + int hit_bad = FALSE; /* TRUE means hit bad monster */ + int mon_attack; /* TRUE means find a monster to hit */ + + char sch; + struct linked_list *item; + coord this; /* Temporary destination for chaser */ + + if (!th->t_ischasing) + return; + + /* Make sure the monster can move */ + + if (th->t_no_move != 0) + { + th->t_no_move--; + return; + } + + /* + * Bad monsters check for a good monster to hit, friendly monsters + * check for a bad monster to hit. + */ + + mon_attack = FALSE; + + if (good_monster(*th)) + { + hit_bad = TRUE; + mon_attack = TRUE; + } + else if (on(*th, ISMEAN)) + { + hit_bad = FALSE; + mon_attack = TRUE; + } + + if (mon_attack) + { + struct linked_list *mon_to_hit; + + mon_to_hit = f_mons_a(th->t_pos.y, th->t_pos.x, hit_bad); + + if (mon_to_hit) + { + mon_mon_attack(th, mon_to_hit, pick_weap(th), NOTHROWN); + return; + } + } + + /* no nearby monster to hit */ + + rer = roomin(th->t_pos); /* Find room of chaser */ + ree = roomin(th->t_chasee->t_pos); /* Find room of chasee */ + + /* + * We don't count doors as inside rooms for this routine + */ + + if (mvwinch(stdscr, th->t_pos.y, th->t_pos.x) == DOOR) + rer = NULL; + + this = th->t_chasee->t_pos; + + /* + * If we are not in a corridor and not a phasing monster, 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. Phasing + * monsters don't need to look for doors. There are no doors in mazes + * and throne rooms. + */ + + if (levtype != MAZELEV && levtype != THRONE && rer != NULL && off(*th, CANINWALL)) + { + if (flee) + rundoor = (rer == ree); + else + rundoor = (rer != ree); + } + else + rundoor = FALSE; + + if (rundoor) + { + coord d_exit; /* A particular door */ + int exity, exitx; /* Door's coordinates */ + + if (th->t_doorgoal != -1) + { /* Do we already have the goal? */ + this = rer->r_exit[th->t_doorgoal]; + dist = 0; /* Indicate that we have our door */ + } + else + for (i = 0; i < rer->r_nexits; i++) + { /* Loop through doors */ + d_exit = rer->r_exit[i]; + exity = d_exit.y; + exitx = d_exit.x; + + /* Avoid secret doors */ + if (mvwinch(stdscr, exity, exitx) == DOOR) + { + /* Were we just on this door? */ + if (ce(d_exit, th->t_oldpos)) + last_door = i; + else + { + dist = DISTANCE(th->t_chasee->t_pos, d_exit); + + /* + * 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,d_exit); + + /* + * Maximize distance if + * fleeing, otherwise + * minimize it + */ + + if ((flee && (dist > maxdist)) || + (!flee && (dist < mindist))) + { + th->t_doorgoal = i; /* Use this door */ + this = d_exit; + mindist = maxdist = dist; + } + } + } + } + + /* Could we not find a door? */ + if (dist == INT_MIN) + { + /* If we were on a door, go ahead and use it */ + if (last_door != -1) + { + th->t_doorgoal = last_door; + this = th->t_oldpos; + dist = 0; /* Indicate that we found a door */ + } + } + + /* Indicate that we do not want to flee from the door */ + if (dist != INT_MIN) + flee = FALSE; + } + else + th->t_doorgoal = -1; /* Not going to any door */ + + /* + * 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, flee)) + { + if (ce(th->t_nxtpos, hero)) + { + /* merchants try to sell something */ + + if (on(*th, CANSELL)) + { + sell(th); + return; + } + else if (off(*th, ISFRIENDLY) && off(*th, ISCHARMED) + && (off(*th, CANFLY) || (on(*th, CANFLY) && rnd(2)))) + attack(th, pick_weap(th), FALSE); + return; + } + else if (on(*th, NOMOVE)) + stoprun = TRUE; + } + + if (!curr_mons) + return; /* Did monster get itself killed? */ + + if (on(*th, NOMOVE)) + return; + + /* If we have a scavenger, it can pick something up */ + + if ((item = find_obj(th->t_nxtpos.y, th->t_nxtpos.x)) != NULL) + { + struct linked_list *node, *top = item; + struct object *obt; + + while(top) + { + /* grab all objects that qualify */ + + struct object *obj = OBJPTR(item); + + obt = OBJPTR(top); + node = obt->next_obj; + + if (on(*th, ISSCAVENGE) || + ((on(*th, CANWIELD) || on(*th, CANSHOOT)) && + (obj->o_type == WEAPON || obj->o_type == ARMOR)) || + (on(*th, CANCAST) && is_magic(obj))) + { + rem_obj(top, FALSE); + attach(th->t_pack, top); + } + + top = node; + } + + light(&hero); + } + + mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch); + sch = CCHAR( mvwinch(cw, th->t_nxtpos.y, th->t_nxtpos.x) ); + + /* Get old and new room of monster */ + old_room = roomin(th->t_pos); + new_room = roomin(th->t_nxtpos); + + /* If the monster can illuminate rooms, check for a change */ + if (on(*th, HASFIRE)) + { + /* Is monster entering a room? */ + if (old_room != new_room && new_room != NULL) + { + new_room->r_flags |= HASFIRE; + new_room->r_fires++; + if (cansee(th->t_nxtpos.y, th->t_nxtpos.x) && new_room->r_fires==1) + light(&hero); + } + + /* Is monster leaving a room? */ + if (old_room != new_room && old_room != NULL) + { + if (--(old_room->r_fires) <= 0) + { + old_room->r_flags &= ~HASFIRE; + if (cansee(th->t_pos.y, th->t_pos.x)) + light(&th->t_pos); + } + } + } + + /* + * If monster is entering player's room and player can see it, stop + * the player's running. + */ + + if (new_room != old_room && new_room != NULL && + new_room == ree && cansee(th->t_nxtpos.y, th->t_nxtpos.x) && + (off(*th, ISINVIS) || (off(*th, ISSHADOW) || rnd(10) == 0) || + on(player, CANSEE)) && off(*th, CANSURPRISE)) + running = FALSE; + + if (rer != NULL && (rer->r_flags & ISDARK) && + !(rer->r_flags & HASFIRE) && sch == FLOOR && + DISTANCE(th->t_nxtpos, th->t_pos) < see_dist && + off(player, ISBLIND)) + th->t_oldch = ' '; + else + th->t_oldch = sch; + + if (cansee(th->t_nxtpos.y, th->t_nxtpos.x) && + off(*th, ISINWALL) && + ((off(*th, ISINVIS) && (off(*th, ISSHADOW) || rnd(100) < 10)) || + on(player, CANSEE)) && + off(*th, CANSURPRISE)) + mvwaddch(cw, th->t_nxtpos.y, th->t_nxtpos.x, th->t_type); + + mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' '); + mvwaddch(mw, th->t_nxtpos.y, th->t_nxtpos.x, th->t_type); + + /* Record monster's last position (if new one is different) */ + + if (!ce(th->t_nxtpos, th->t_pos)) + th->t_oldpos = th->t_pos; + + th->t_pos = th->t_nxtpos; /* Mark the monster's new position */ + + /* If the monster is on a trap, trap it */ + + sch = CCHAR(mvinch(th->t_nxtpos.y, th->t_nxtpos.x)); + + if (isatrap(sch)) + { + debug("Monster trapped by %c.", sch); + + if (cansee(th->t_nxtpos.y, th->t_nxtpos.x)) + th->t_oldch = sch; + + be_trapped(th, th->t_nxtpos); + } + + /* And stop running if need be */ + + if (stoprun && ce(th->t_pos, th->t_chasee->t_pos)) + { + th->t_ischasing = FALSE; + turn_off(*th, ISRUN); + } +} + +/* + chase_it() + Set a monster running after something or stop it from running (for + when it dies) +*/ + +void +chase_it(coord *runner, struct thing *th) +{ + struct linked_list *item; + struct thing *tp; + + /* If we couldn't find him, something is funny */ + + if ((item = find_mons(runner->y, runner->x)) == NULL) + { + debug("CHASER '%s'", unctrl(winat(runner->y, runner->x))); + return; + } + + tp = THINGPTR(item); + + /* Start the beastie running */ + + tp->t_ischasing = TRUE; + tp->t_chasee = th; + + turn_on(*tp, ISRUN); + turn_off(*tp, ISDISGUISE); + + return; +} + +/* + 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. +*/ + +int +chase(struct thing *tp, coord *ee, int flee) +{ + int x, y; + int dist, thisdist, monst_dist = INT_MAX; + struct linked_list *weapon; + coord *er = &tp->t_pos; + coord shoot; + coord *shootit_dir = NULL; + int ch; + char mch; + int next_player = FALSE; + + /* Take care of shooting directions */ + + if (on(*tp, CANBREATHE) || on(*tp, CANSHOOT) || on(*tp, CANCAST)) + { + if (good_monster(*tp)) + { + shootit_dir = find_shoot(tp, &shoot); /* find a mean monster */ + + if (wizard && shootit_dir) + msg("Found monster to attack towards (%d,%d).", + shootit_dir->x, shootit_dir->y); + } + else + shootit_dir = can_shoot(er, ee, &shoot); /* shoot hero */ + } + + /* + * If the thing is confused, let it move randomly. Some monsters are + * slightly confused all of the time. + */ + + if ((on(*tp, ISHUH) && rnd(10) < 8) || + ((on(*tp, ISINVIS) || on(*tp, ISSHADOW)) && rnd(100) < 20) || + (on(player, ISINVIS) && off(*tp, CANSEE))) + { /* Player is invisible */ + + /* get a valid random move */ + + tp->t_nxtpos = rndmove(tp); + + dist = DISTANCE(tp->t_nxtpos, *ee); + + if (on(*tp, ISHUH) && rnd(20) == 0) /* monster might lose confusion */ + turn_off(*tp, ISHUH); + + /* + * check to see if random move takes creature away from + * player if it does then turn off ISHELD + */ + + if (dist > 1 && on(*tp, DIDHOLD)) + { + turn_off(*tp, DIDHOLD); + turn_on(*tp, CANHOLD); + + if (--hold_count == 0) + turn_off(player, ISHELD); + } + } /* If we can breathe, we may do so */ + else if (on(*tp, CANBREATHE) && (shootit_dir) && (rnd(100) < 67) && + (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)) && + (DISTANCE(*er, *ee) < BOLT_LENGTH * BOLT_LENGTH)) + { + int chance; + char *breath; + + /* Will it breathe at random */ + + if (on(*tp, CANBRANDOM)) + { + if (rnd(level / 20) == 0 && tp->t_index != nummonst + 1 + && !(good_monster(*tp))) + turn_off(*tp, CANBRANDOM); + + /* Select type of breath */ + + chance = rnd(100); + + if (chance < 11) + breath = "acid"; + else if (chance < 22) + breath = "flame"; + else if (chance < 33) + breath = "lightning bolt"; + else if (chance < 44) + breath = "chlorine gas"; + else if (chance < 55) + breath = "ice"; + else if (chance < 66) + breath = "nerve gas"; + else if (chance < 77) + breath = "sleeping gas"; + else if (chance < 88) + breath = "slow gas"; + else + breath = "fear gas"; + } /* Or can it breathe acid? */ + else if (on(*tp, CANBACID)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBACID); + + breath = "acid"; + } /* Or can it breathe fire */ + else if (on(*tp, CANBFIRE)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBFIRE); + + breath = "flame"; + } /* Or can it breathe electricity? */ + else if (on(*tp, CANBBOLT)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBBOLT); + + breath = "lightning bolt"; + } /* Or can it breathe gas? */ + else if (on(*tp, CANBGAS)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBGAS); + + breath = "chlorine gas"; + } /* Or can it breathe ice? */ + else if (on(*tp, CANBICE)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBICE); + + breath = "ice"; + } + else if (on(*tp, CANBPGAS)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBPGAS); + + breath = "nerve gas"; + } + else if (on(*tp, CANBSGAS)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBSGAS); + + breath = "sleeping gas"; + } + else if (on(*tp, CANBSLGAS)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBSLGAS); + + breath = "slow gas"; + } + else + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBFGAS); + + breath = "fear gas"; + } + + shoot_bolt(tp, *er, *shootit_dir, (tp == THINGPTR(fam_ptr)), + tp->t_index, breath, roll(tp->t_stats.s_lvl, 6)); + + tp->t_nxtpos = *er; + + dist = DISTANCE(tp->t_nxtpos, *ee); + + if (!curr_mons) + return (TRUE); + } + else if (shootit_dir && on(*tp, CANCAST) && + (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6))) + { + /* + If we can cast spells we might do so - even if adjacent fleeing + monsters are restricted to certain spells + */ + + incant(tp, *shootit_dir); + tp->t_nxtpos = *er; + dist = DISTANCE(tp->t_nxtpos, *ee); + } + else if (shootit_dir && on(*tp, CANSHOOT)) + { + weapon = get_hurl(tp); + + if (weapon && + (off(*tp, ISFLEE) || rnd(DISTANCE(*er, *ee)) > 2) && + (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6))) + { + /* + Should we shoot or throw something? fleeing monsters + may to shoot anyway if far enough away + */ + + missile(shootit_dir->y, shootit_dir->x, weapon, tp); + tp->t_nxtpos = *er; + dist = DISTANCE(tp->t_nxtpos, *ee); + } + } + else + { + /* + Otherwise, find the empty spot next to the chaser that is closest + to the chasee. + */ + int ey, ex; + struct room *rer, *ree; + int dist_to_old = INT_MIN; /* Dist from goal to old position */ + + /* Get rooms */ + rer = roomin(*er); + ree = roomin(*ee); + + /* + * 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 : INT_MAX; + tp->t_nxtpos = *er; + + /* Are we at our goal already? */ + + if (!flee && ce(tp->t_nxtpos, *ee)) + return (FALSE); + + ey = er->y + 1; + ex = er->x + 1; + + for (x = er->x - 1; x <= ex; x++) + for (y = er->y - 1; y <= ey; y++) + { + coord tryp; /* test position */ + + /* Don't try off the screen */ + + if ((x < 0) || (x >= COLS) || (y < 1) || (y >= LINES - 2)) + continue; + + /* + * Don't try the player if not going after + * the player or he's disguised and monster is dumb + */ + + if (((off(*tp, ISFLEE) && !ce(hero, *ee)) || + (on(player, ISDISGUISE) && (rnd(tp->t_stats.s_lvl) < 6)) + || good_monster(*tp)) + && x == hero.x && y == hero.y) + 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(tryp,*ee); + if (test_dist <= 25 && /* Let's be fairly close */ + test_dist < monst_dist) + { + + /* Could we really move there? */ + + mvwaddch(mw, y, x, ' '); /* Temp blank monst */ + + if (diag_ok(er, &tryp, tp)) + monst_dist = test_dist; + + mvwaddch(mw, y, x, mch); /* Restore monster */ + } + } + + if (!diag_ok(er, &tryp, tp)) + continue; + + ch = mvwinch(cw, y, x); /* Screen character */ + + /* + * Stepping on player is NOT okay if we are + * fleeing + */ + + if (on(*tp, ISFLEE) && (ch == PLAYER)) + next_player = TRUE; + + 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)) + { + if (!(ch == RUSTTRAP) && + !(ch == FIRETRAP && on(*tp, NOFIRE)) && + rnd(10) < tp->t_stats.s_intel && + (y != hero.y || x != hero.x)) + continue; + } + + /* + * OK -- this place counts + */ + + thisdist = DISTANCE(tryp, *ee); + + /* + * Adjust distance if we are being + * shot at to moving out of line of sight. + */ + + 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 + */ + + if (ce(tryp, tp->t_oldpos)) + dist_to_old = thisdist; + else if ((flee && (thisdist > dist)) || + (!flee && (thisdist < dist))) + { + tp->t_nxtpos = tryp; + dist = thisdist; + } + } + } + + /* + * If we are running from the player and he is in our way, go + * ahead and slug him. + */ + + if (next_player && DISTANCE(*er,*ee) < dist && + step_ok(tp->t_chasee->t_pos.y, tp->t_chasee->t_pos.x, NOMONST, tp)) + { + tp->t_nxtpos = tp->t_chasee->t_pos; /* Okay to hit player */ + 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 < INT_MAX && + DISTANCE(*er, hero) < dist) + tp->t_nxtpos = *er; + + /* Do we want to go back to the last position? */ + else if (dist_to_old != INT_MIN && /* It is possible to move back */ + ((flee && dist == 0) || /* No other possible moves */ + (!flee && dist == INT_MAX))) + { + /* Do we move back or just stay put (default)? */ + + dist = DISTANCE(*er,*ee); /* Current distance */ + + if (!flee || (flee && (dist_to_old > dist))) + tp->t_nxtpos = tp->t_oldpos; + } + } + + /* Make sure we have the real distance now */ + dist = DISTANCE(tp->t_nxtpos, *ee); + + /* Mark monsters in a wall */ + + switch(mvinch(tp->t_nxtpos.y, tp->t_nxtpos.x)) + { + case WALL: + case '-': + case '|': + turn_on(*tp, ISINWALL); + break; + default: + turn_off(*tp, ISINWALL); + } + + if (off(*tp, ISFLEE) && + !(!SAME_POS((tp->t_chasee->t_pos),hero) || off(player, ISINWALL) || on(*tp, CANINWALL))) + return(dist != 0); + else /* May actually hit here from a confused move */ + return(!ce(tp->t_nxtpos, hero)); +} + +/* + roomin(coord *cp) + + Find what room some coordinates are in. + NULL means they aren't in any room. +*/ + +struct room * +roomin(coord cp) +{ + struct room *rp; + int i; + + for (i = 0; i < MAXROOMS; i++) + { + rp = &rooms[i]; + + if ((cp.x <= (rp->r_pos.x + (rp->r_max.x - 1))) && + (cp.y <= (rp->r_pos.y + (rp->r_max.y - 1))) && + (cp.x >= rp->r_pos.x) && + (cp.y >= rp->r_pos.y)) + { + return(rp); + } + } + + return(NULL); +} + +/* + * find_mons: Find the monster from his corrdinates + */ + +struct linked_list * +find_mons(int y, int x) +{ + struct linked_list *item; + + for (item = mlist; item != NULL; item = next(item)) + { + struct thing *th = THINGPTR(item); + + if (th->t_pos.y == y && th->t_pos.x == x) + return item; + } + return NULL; +} + +/* + * Find an unfriendly monster around us to hit + */ + +struct linked_list * +f_mons_a(int y, int x, int hit_bad) +{ + int row, col; + struct linked_list *item; + struct thing *tp; + + for (row = x - 1; row <= x + 1; row++) + for (col = y - 1; col <= y + 1; col++) + if (row == x && col == y) + continue; + else if (col > 0 && row > 0 && + isalpha(mvwinch(mw, col, row)) && + ((item = find_mons(col, row)) != NULL)) + { + tp = THINGPTR(item); + if ((good_monster(*tp) && !hit_bad) || + (!good_monster(*tp) && hit_bad)) + return (item); + } + + return (NULL); +} + + +/* + diag_ok() + Check to see if the move is legal if it is diagonal +*/ + +int +diag_ok(coord *sp, coord *ep, struct thing *flgptr) +{ + if (ep->x == sp->x || ep->y == sp->y) + return TRUE; + + return (step_ok(ep->y, sp->x, MONSTOK, flgptr) && + step_ok(sp->y, ep->x, MONSTOK, flgptr)); +} + +/* + cansee() + returns true if the hero can see a certain coordinate. +*/ + +int +cansee(int y, int x) +{ + struct room *rer; + coord tp; + + if (on(player, ISBLIND)) + return FALSE; + + tp.y = y; + tp.x = x; + rer = roomin(tp); + + /* + * We can only see if the hero in the same room as the coordinate and + * the room is lit or if it is close. + */ + + return ((rer != NULL && + rer == roomin(hero) && + (!(rer->r_flags & ISDARK) || (rer->r_flags & HASFIRE)) && + (levtype != MAZELEV || /* Maze level needs direct line */ + maze_view(tp.y, tp.x))) || + DISTANCE(tp,hero) < see_dist); +} + +coord * +find_shoot(struct thing *tp, coord *dir) +{ + struct room *rtp; + int ulx, uly, xmx, ymx, xmon, ymon, tpx, tpy, row, col; + struct linked_list *mon; + struct thing *ick; + + rtp = roomin(tp->t_pos); /* Find room of chaser */ + + if (rtp == NULL) + return NULL; + + ulx = rtp->r_pos.x; + uly = rtp->r_pos.y; + xmx = rtp->r_max.x; + ymx = rtp->r_max.y; + + tpx = tp->t_pos.x; + tpy = tp->t_pos.y; + + for (col = ulx; col < (ulx + xmx); col++) + for (row = uly; row < (uly + ymx); row++) + { + if (row > 0 && col > 0 && isalpha(mvwinch(mw, row, col))) + { + mon = find_mons(row, col); + + if (mon) + { + ick = THINGPTR(mon); + xmon = ick->t_pos.x; + ymon = ick->t_pos.y; + + if (!(good_monster(*ick))) + { + if (straight_shot(tpy, tpx, ymon, xmon, dir)) + return(dir); + } + } + } + } + + return(NULL); +} + +/* + 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(coord *er, coord *ee, coord *dir) +{ + int ery, erx, eey, eex; + + /* Make sure we are chasing the player */ + + if (!ce((*ee), hero)) + return(NULL); + + /* They must be in the same room */ + + if (roomin(*er) != roomin(hero)) + return(NULL); + + ery = er->y; + erx = er->x; + eey = ee->y; + eex = ee->x; + + /* Will shoot unless next to player, then 80% prob will fight */ + + if ((DISTANCE(*er,*ee) < 4) && (rnd(100) < 80)) + return(NULL); + + /* Do we have a straight shot? */ + + if (!straight_shot(ery, erx, eey, eex, dir)) + return(NULL); + else + return(dir); +} + +/* + straight_shot() + See if there is a straight line of sight between the two + given coordinates. If shooting is not NULL, it is a pointer to a + structure which should be filled with the direction to shoot (if + there is a line of sight). If shooting, monsters get in the way. + Otherwise, they do not. +*/ + +int +straight_shot(int ery, int erx, int eey, int eex, coord *dir) +{ + int dy, dx; /* Deltas */ + int ch; + + /* Does the monster have a straight shot at player */ + + if ((ery != eey) && (erx != eex) && + (abs(ery - eey) != abs(erx - eex))) + return (FALSE); + + /* Get the direction to shoot */ + + if (eey > ery) + dy = 1; + else if (eey == ery) + dy = 0; + else + dy = -1; + + if (eex > erx) + dx = 1; + else if (eex == erx) + dx = 0; + else + dx = -1; + + /* Make sure we have free area all the way to the player */ + + ery += dy; + erx += dx; + + while ((ery != eey) || (erx != eex)) + { + switch(ch = winat(ery, erx)) + { + case '|': + case '-': + case WALL: + case DOOR: + case SECRETDOOR: + return(FALSE); + default: + if (dir && isalpha(ch)) + return(FALSE); + } + + ery += dy; + erx += dx; + } + + if (dir) + { /* If we are shooting -- put in the directions */ + dir->y = dy; + dir->x = dx; + } + + return(TRUE); +} + +/* + get_hurl + returns the weapon that the monster will "throw" if it has one +*/ + +struct linked_list * +get_hurl(struct thing *tp) +{ + struct linked_list *arrow, *bolt, *rock, *silverarrow, *fbbolt; + struct linked_list *bullet, *firearrow, *dart, *dagger, *shuriken; + struct linked_list *oil, *grenade; + + struct linked_list *pitem; + int bow = FALSE, crossbow = FALSE, sling = FALSE, footbow = FALSE; + + /* Don't point to anything to begin with */ + + arrow = bolt = rock = silverarrow = fbbolt = NULL; + bullet = firearrow = dart = dagger = shuriken = NULL; + oil = grenade = NULL; + + for (pitem = tp->t_pack; pitem != NULL; pitem = next(pitem)) + if ((OBJPTR(pitem))->o_type == WEAPON) + switch ((OBJPTR(pitem))->o_which) + { + case BOW:bow = TRUE; break; + case CROSSBOW:crossbow = TRUE; break; + case SLING:sling = TRUE; break; + case FOOTBOW:footbow = TRUE; break; + case ROCK:rock = pitem; break; + case ARROW:arrow = pitem; break; + case SILVERARROW:silverarrow = pitem; break; + case BOLT:bolt = pitem; break; + case FBBOLT:fbbolt = pitem; break; + case BULLET:bullet = pitem; break; + case FLAMEARROW:firearrow = pitem; break; + case DART:dart = pitem; break; + case DAGGER:dagger = pitem; break; + case SHURIKEN:shuriken = pitem; break; + case MOLOTOV:oil = pitem; break; + case GRENADE:shuriken = pitem; break; + } + + if (bow && silverarrow) + return(silverarrow); + + if (crossbow && bolt) + return(bolt); + + if (bow && firearrow) + return(firearrow); + + if (off(*tp, ISCHARMED) && oil) + return(oil); + + if (off(*tp, ISCHARMED) && grenade) + return(grenade); + + if (footbow && fbbolt) + return(fbbolt); + + if (bow && arrow) + return(arrow); + + if (sling && bullet) + return(bullet); + + if (sling && rock) + return(rock); + + if (shuriken) + return(shuriken); + + if (dagger) + return(dagger); + + if (silverarrow) + return(silverarrow); + + if (firearrow) + return(firearrow); + + if (fbbolt) + return(fbbolt); + + if (bolt) + return(bolt); + + if (bullet) + return(bullet); + + if (dart) + return(dart); + + if (rock) + return(rock); + + return(NULL); +} + +/* + pick_weap() + returns the biggest weapon that the monster will wield if it + has a non-launching or non-missile weapon returns NULL if no weapon, or + bare hands is better +*/ + +struct object * +pick_weap(struct thing *tp) +{ + int weap_dam = maxdamage(tp->t_stats.s_dmg); + struct object *ret_obj = NULL; + struct linked_list *pitem; + + if (on(*tp, CANWIELD)) + { + for (pitem = tp->t_pack; pitem != NULL; pitem = next(pitem)) + { + struct object *obj = OBJPTR(pitem); + + if (obj->o_type != WEAPON && !(obj->o_flags&(ISLAUNCHER|ISMISL)) && + maxdamage(obj->o_damage) > weap_dam) + { + weap_dam = maxdamage(obj->o_damage); + ret_obj = obj; + } + } + } + + return (ret_obj); +} + +/* + canblink() + checks if the monster can teleport (blink). If so, it will try + to blink the monster next to the player. +*/ + +int +can_blink(struct thing *tp) +{ + int y, x, index = 9; + coord tryp; /* To hold the coordinates for use in diag_ok */ + int spots[9], found_one = FALSE; + + /* + * First, can the monster even blink? And if so, there is only a 30% + * chance that it will do so. And it won't blink if it is running. + */ + + if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) || + on(*tp, ISFLEE) || + (on(*tp, ISSLOW) && off(*tp, ISHASTE) && !(tp->t_turn)) || + (rnd(10) < 9)) + 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)) + spots[index] = FALSE; + else + { + + /* + * 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) + { + /* 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) ); + + if (cansee(y, x) && + off(*tp, ISINWALL) && + ((off(*tp, ISINVIS) && + (off(*tp, ISSHADOW) || rnd(100) < 10)) || on(player, CANSEE)) && + off(*tp, CANSURPRISE)) + mvwaddch(cw, y, x, tp->t_type); + + mvwaddch(mw, tp->t_pos.y,tp->t_pos.x,' '); /*Clear old position */ + mvwaddch(mw, y, x, tp->t_type); + tp->t_pos.y = y; + tp->t_pos.x = x; + } + + return (found_one); +}