Mercurial > hg > early-roguelike
diff xrogue/actions.c @ 133:e6179860cb76
Import XRogue 8.0 from the Roguelike Restoration Project (r1490)
author | John "Elwin" Edwards |
---|---|
date | Tue, 21 Apr 2015 08:55:20 -0400 |
parents | |
children | f54901b9c39b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xrogue/actions.c Tue Apr 21 08:55:20 2015 -0400 @@ -0,0 +1,1074 @@ +/* + actions.c - functions for dealing with monster actions + + XRogue: Expeditions into the Dungeons of Doom + Copyright (C) 1991 Robert Pietkivitch + All rights reserved. + + Based on "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 <curses.h> +#include <limits.h> +#include "rogue.h" + +int mf_count = 0; /* move_free counter - see actions.c(m_act()) */ +int mf_jmpcnt = 0; /* move_free counter for # of jumps */ + +/* + * Did we disrupt a spell? + */ +dsrpt_monster(tp, always, see_him) +register struct thing *tp; +bool always, see_him; +{ + switch (tp->t_action) { + case A_SUMMON: + case A_MISSILE: + case A_SLOW: + tp->t_action = A_NIL; /* Just make the old fellow start over again */ + tp->t_no_move = movement(tp); + tp->t_using = NULL; /* Just to be on the safe side */ + turn_on(*tp, WASDISRUPTED); + if (see_him) + msg("%s's spell has been disrupted.",prname(monster_name(tp),TRUE)); + /* + * maybe choose something else to do next time since player + * is disrupting us + */ + tp->t_summon *= 2; + tp->t_cast /= 2; + return; + } + + /* We may want to disrupt other actions, too */ + if (always) { + tp->t_action = A_NIL; /* Just make the old fellow start over again */ + tp->t_no_move = movement(tp); + tp->t_using = NULL;/* Just to be on the safe side */ + } +} + +dsrpt_player() +{ + int which, action; + struct linked_list *item; + struct object *obj; + + action = player.t_action; + which = player.t_selection; + + switch (action) { + case C_CAST: /* Did we disrupt a spell? */ + case C_PRAY: + case C_CHANT: + { + msg("Your %s was disrupted!", action == C_CAST ? "spell" : "prayer"); + + /* Charge him 1/4 anyway */ + if (action == C_CAST) + spell_power += magic_spells[which].s_cost / 4; + else if (action == C_PRAY) + pray_time += cleric_spells[which].s_cost / 4; + else if (action == C_CHANT) + chant_time += druid_spells[which].s_cost / 4; + } + when C_COUNT: /* counting of gold? */ + { + if (purse > 0) { + msg("Your gold goes flying everywhere!"); + do { + item = spec_item(GOLD, NULL, NULL, NULL); + obj = OBJPTR(item); + obj->o_count = min(purse, rnd(20)+1); + purse -= obj->o_count; + obj->o_pos = hero; + fall(item, FALSE); + } while (purse > 0 && rnd(25) != 1); + } + } + when C_EAT: + msg("Ack! You gag on your food for a moment. "); + del_pack(player.t_using); + + when A_PICKUP: + msg("You drop what you are picking up! "); + + when C_SEARCH: /* searching for traps and secret doors... */ + msg("Ouch! You decide to stop searching. "); + count = 0; /* don't search again */ + + when C_SETTRAP: + msg("Ouch! You can't set a trap right now. "); + + when A_NIL: + default: + return; + } + player.t_no_move = movement(&player); /* disoriented for a while */ + player.t_action = A_NIL; + player.t_selection = 0; +} + +/* + * m_act: + * If the critter isn't doing anything, choose an action for it. + * Otherwise, let it perform its chosen action. + */ + +m_act(tp) +register struct thing *tp; +{ + struct object *obj; + bool flee; /* Are we scared? */ + + /* What are we planning to do? */ + switch (tp->t_action) { + default: + /* An unknown action! */ + msg("Unknown monster action (%d)", tp->t_action); + + /* Fall through */ + + case A_NIL: + /* If the monster is fairly intelligent and about to die, it + * may turn tail and run. But if we are a FRIENDLY creature + * in the hero's service, don't run. + */ + if (off(*tp, ISFLEE) && + tp->t_stats.s_hpt < tp->maxstats.s_hpt && + tp->t_stats.s_hpt < max(10, tp->maxstats.s_hpt/6) && + (off(*tp, ISFRIENDLY) || tp->t_dest != &hero) && + rnd(25) < tp->t_stats.s_intel) { + turn_on(*tp, ISFLEE); + + /* It is okay to turn tail */ + tp->t_oldpos = tp->t_pos; + } + + /* Should the monster run away? */ + flee = on(*tp, ISFLEE) || + ((tp->t_dest == &hero) && on(player, ISINWALL) && + off(*tp, CANINWALL)); + + m_select(tp, flee); /* Select an action */ + return; + + when A_ATTACK: + /* + * We're trying to attack the player or monster at t_newpos + * if the prey moved, do nothing + */ + obj = tp->t_using ? OBJPTR(tp->t_using) : NULL; + if (ce(tp->t_newpos, hero)) { + attack(tp, obj, FALSE); + } + else if (mvwinch(mw, tp->t_newpos.y, tp->t_newpos.x) && + step_ok(tp->t_newpos.y, tp->t_newpos.x, FIGHTOK, tp)) { + skirmish(tp, &tp->t_newpos, obj, FALSE); + } + + when A_SELL: + /* Is the quartermaster still next to us? */ + if (ce(tp->t_newpos, hero)) sell(tp); + + /* The quartermaster moved away */ + else if (off(player, ISBLIND) && cansee(unc(tp->t_pos)) && + (off(*tp, ISINVIS) || on(player, CANSEE)) && + (off(*tp, ISSHADOW) || on(player, CANSEE)) && + (off(*tp, CANSURPRISE) || ISWEARING(R_ALERT)) && + (rnd(12) < 4)) + msg("%s grunts with frustration",prname(monster_name(tp),TRUE)); + + when A_MOVE: + /* Let's try to move */ + do_chase(tp); + + /* If t_no_move > 0, we found that we have to fight! */ + if (tp->t_no_move > 0) return; + + when A_BREATHE: + /* Breathe on the critter */ + m_breathe(tp); + + when A_SLOW: + /* make him move slower */ + add_slow(); + turn_off(*tp, CANSLOW); + + when A_MISSILE: + /* Start up a magic missile spell */ + m_spell(tp); + + when A_SONIC: + /* Let out a sonic blast! */ + m_sonic(tp); + + when A_THROW: + /* We're throwing something (like an arrow) */ + missile(tp->t_newpos.y, tp->t_newpos.x, tp->t_using, tp); + + when A_SUMMON: + /* We're summoning help */ + m_summon(tp); + + when A_USERELIC: + /* Use our relic */ + m_use_relic(tp); + + when A_USEWAND: + /* use the wand we have */ + m_use_wand(tp); + } + + /* Can we in fact move? (we might have solidified in solid rock) */ + if (!step_ok(hero.y, hero.x, NOMONST, &player)) { + + if (move_free > 1) goto jump_over; /* avoid messages */ + if (mf_count > 2) goto jump_over; /* limit messages */ + + if (pstats.s_hpt < 1) { + pstats.s_hpt = -1; + msg("You have merged into the surroundings! --More--"); + wait_for(' '); + death(D_PETRIFY); + } + else { + mf_count += 1; /* count number of times we are here */ + pstats.s_hpt -= rnd(2)+1; + if (pstats.s_hpt < 1) { + pstats.s_hpt = -1; + msg("You have merged into the surroundings! --More--"); + wait_for(' '); + death(D_PETRIFY); + } + } + switch (rnd(51)) { + case 0: msg("Arrrggghhhhh!! "); + when 5: msg("You can't move! "); + when 10: msg("You motion angrily! "); + when 15: msg("You feel so weird! "); + when 20: msg("If only you could phase. "); + when 25: msg("The rock maggots are closing in! "); + when 30: msg("You wrench and wrench and wrench... "); + when 35: msg("You wish you could teleport out of here! "); + when 40: msg("Your feel your life force ebbing away... "); + when 45: msg("You partially regain your senses. "); + when 50: msg("The rock maggots have found you!!! "); + otherwise: pstats.s_hpt -= rnd(4)+1; + } + if (pstats.s_hpt < 1) { + pstats.s_hpt = -1; + msg("You lose the urge to live... --More--"); + wait_for(' '); + death(D_PETRIFY); + } + jump_over: + mf_jmpcnt++; /* count this jump */ + if (mf_jmpcnt > 9) { /* take a few turns, then reset it */ + mf_jmpcnt = 0; + mf_count = 0; + } + } + + /* No action now */ + tp->t_action = A_NIL; + tp->t_using = NULL; +} + +/* + * m_breathe: + * Breathe in the chosen direction. + */ + +m_breathe(tp) +register struct thing *tp; +{ + register int damage; + register char *breath = NULL; + + damage = tp->t_stats.s_hpt; + turn_off(*tp, CANSURPRISE); + + /* 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 */ + shoot_bolt(tp, tp->t_pos, tp->t_newpos, FALSE, + tp->t_index, breath, damage); + + running = FALSE; + if (fight_flush) flushinp(); +} + +/* + * m_select: + * Select an action for the monster. + */ + +m_select(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 */ + int dist = INT_MIN; + int mindist = INT_MAX, maxdist = INT_MIN; + bool rundoor; /* TRUE means run to a door */ + char sch; + coord *last_door=0, /* Door we just came from */ + this; /* Temporary destination for chaser */ + + rer = roomin(&th->t_pos); /* Find room of chaser */ + ree = roomin(th->t_dest); /* Find room of chasee */ + + /* First see if we want to use an ability or weapon */ + if (m_use_it(th, flee, rer, ree)) return; + + /* + * We don't count monsters on doors as inside rooms here because when + * a monster is in a room and the player is not in that room, the + * monster looks for the best door out. If we counted doors as part + * of the room, the monster would already be on the best door out; + * so he would never move. + */ + if ((sch = mvwinch(stdscr, th->t_pos.y, th->t_pos.x)) == DOOR || + sch == SECRETDOOR || sch == PASSAGE) { + rer = NULL; + } + this = *th->t_dest; + + /* + * If we are in a room heading for the player and the player is not + * in the room with us, we run to the "best" door. + * If we are in a room fleeing from the player, then we run to the + * "best" door if he IS in the same room. + * + * Note: We don't bother with doors in mazes or if we can walk + * through walls. + */ + if (rer != NULL && levtype != MAZELEV && 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 */ + *entrance; /* Place just inside doorway */ + int exity, exitx; /* Door's coordinates */ + char dch='\0'; /* Door character */ + + if ((th->t_doorgoal.x != -1) && (th->t_doorgoal.y != -1)) + dch = 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, *th->t_dest))) { /* Prey should not + * be at door if + * we are running + * away + */ + /* Make sure the player is not in the doorway, either */ + entrance = doorway(rer, &th->t_doorgoal); + if (!flee || entrance == NULL || !ce(*entrance, *th->t_dest)) { + 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 = mvwinch(stdscr, exity, exitx); + if (dch == PASSAGE || dch == DOOR) { + /* Don't count a door if we are fleeing from someone and + * he is standing on it. Also, don't count it if he is + * standing in the doorway. + */ + if (flee) { + if (ce(*exit, *th->t_dest)) continue; + + entrance = doorway(rer, exit); + if (entrance != NULL && ce(*entrance, *th->t_dest)) + 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 == INT_MIN) { + /* 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.x = th->t_doorgoal.y = -1; /* No more door goal */ + } + + /* Indicate that we do not want to flee from the door */ + if (dist != INT_MIN) flee = FALSE; + } + else th->t_doorgoal.x = th->t_doorgoal.y = -1; /* Not going to any door */ + + /* Now select someplace to go and start the action */ + chase(th, &this, rer, ree, flee); +} + +/* + * m_sonic: + * The monster is sounding a sonic blast. + */ + +m_sonic(tp) +register struct thing *tp; +{ + register int damage; + struct object blast = + { + MISSILE, {0, 0}, 0, "", "150" , NULL, 0, 0, 0, 0 + }; + + turn_off(*tp, CANSONIC); + turn_off(*tp, CANSURPRISE); + do_motion(&blast, tp->t_newpos.y, tp->t_newpos.x, tp); + damage = rnd(61)+40; + if (save(VS_BREATH, &player, -3)) + damage /= 2; + msg ("%s's ultra-sonic blast hits you", prname(monster_name(tp), TRUE)); + if ((pstats.s_hpt -= damage) <= 0) { + pstats.s_hpt = -1; + death(tp->t_index); + } + running = FALSE; + if (fight_flush) flushinp(); + dsrpt_player(); +} + +/* + * m_spell: + * The monster casts a spell. Currently this is limited to + * magic missile. + */ +m_spell(tp) +register struct thing *tp; +{ + 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, tp->t_newpos.y, tp->t_newpos.x, tp); + hit_monster(unc(missile.o_pos), &missile, tp); + turn_off(*tp, CANMISSILE); + turn_off(*tp, CANSURPRISE); + + running = FALSE; + if (fight_flush) flushinp(); +} + +/* + * m_summon: + * Summon aid. + */ + +m_summon(tp) +register struct thing *tp; +{ + register char *helpname, *mname; + int fail, numsum; + register int which, i; + + /* Let's make sure our prey is still here */ + if (!cansee(unc(tp->t_pos)) || fallpos(&hero, FALSE, 2) == NULL) return; + + /* + * Non-uniques can only summon once. Uniques get fewer + * creatures with each successive summoning. Also, the + * probability of summoning goes down + */ + if (off(*tp, ISUNIQUE)) + turn_off(*tp, CANSUMMON); + + turn_off(*tp, CANSURPRISE); + mname = monster_name(tp); + helpname = monsters[tp->t_index].m_typesum; + which = findmindex(helpname); + + if ((off(*tp, ISINVIS) || on(player, CANSEE)) && + (off(*tp, ISSHADOW) || on(player, CANSEE)) && + (off(*tp, CANSURPRISE) || ISWEARING(R_ALERT))) { + if (monsters[which].m_normal == FALSE) { /* genocided? */ + msg("%s appears dismayed", prname(mname, TRUE)); + monsters[tp->t_index].m_numsum = 0; + } + else { + msg("%s summons %ss for help", prname(mname, TRUE), helpname); + } + } + else { + if (monsters[which].m_normal == FALSE) /* genocided? */ + monsters[tp->t_index].m_numsum = 0; + else { + msg("%ss seem to appear from nowhere!", helpname); + } + } + numsum = monsters[tp->t_index].m_numsum; + if (numsum && on(*tp, ISUNIQUE)) { /* UNIQUEs summon less each time */ + monsters[tp->t_index].m_numsum--; + tp->t_summon *= 2; /* cut probability in half */ + } + + /* + * try to make all the creatures around player but remember + * if unsuccessful + */ + for (i=0, fail=0; i<numsum; i++) { + if (!creat_mons(&player, which, FALSE)) + fail++; /* remember the failures */ + } + + /* + * try once again to make the buggers + */ + for (i=0; i<fail; i++) + creat_mons(tp, which, FALSE); + + /* Now let the poor fellow see all the trouble */ + light(&hero); + turn_on(*tp, HASSUMMONED); +} + +/* + * m_use_it: + * See if the monster (tp) has anything useful it can do + * (ie. an ability or a weapon) other than just move. + */ + +bool +m_use_it(tp, flee, rer, ree) +register struct thing *tp; +bool flee; +register struct room *rer, *ree; +{ + int dist; + register coord *ee = tp->t_dest, *er = &tp->t_pos; + coord *shoot_dir = NULL; + coord straight_dir; + int straight_shot = FALSE; + struct thing *prey; + bool dest_player; /* Are we after the player? */ + + /* + * If we are fleeing, there's a chance, depending on our + * intelligence, that we'll just run in terror. + */ + if (flee && rnd(25) >= tp->t_stats.s_intel) return(FALSE); + + /* + * Make sure that we have a living destination, and record whether + * it is the player. + */ + if (ee != NULL) { + if (ce(*ee, hero)) { + dest_player = TRUE; + prey = &player; + } + else { + struct linked_list *item; + + dest_player = FALSE; + + /* What is the monster we're chasing? */ + item = find_mons(ee->y, ee->x); + if (item != NULL) prey = THINGPTR(item); + else return(FALSE); + } + } + else return(FALSE); + + /* + * If we are friendly to the hero, we don't do anything. + */ + if (on(*tp, ISFRIENDLY) && dest_player) return(FALSE); + + /* + * Also, for now, if our prey is in a wall, we won't do + * anything. The prey must be in the same room as we are OR + * we must have a straight shot at him. Note that + * shoot_dir must get set before rer is checked so + * that we get a valid value. + */ + + if (can_shoot(er, ee, &straight_dir) == 0) + shoot_dir = &straight_dir; + else + shoot_dir = NULL; + + if (on(*prey, ISINWALL) || + ( (shoot_dir == NULL) && (rer == NULL || rer != ree))) + return(FALSE); + + /* + * If we can't see the prey then forget it + */ + if (on(*prey, ISINVIS) && off(*tp, CANSEE)) + return(FALSE); + + /* How far are we from our prey? */ + dist = DISTANCE(er->y, er->x, ee->y, ee->x); + + /* + * Shall we summon aid so we don't have to get our hands dirty? + * For now, we will only summon aid against the player. + * We'll wait until he's within 2 dots of a missile length. + */ + if (on(*tp, CANSUMMON) && dest_player && + dist < (BOLT_LENGTH+2)*(BOLT_LENGTH+2) && + rnd(tp->t_summon) < tp->t_stats.s_lvl && + monsters[tp->t_index].m_numsum > 0 && + fallpos(&hero, FALSE, 2) != NULL) { + tp->t_action = A_SUMMON; /* We're going to summon help */ + tp->t_no_move = movement(tp); /* It takes time! */ + return(TRUE); + } + + /* + * If the creature can cast a slow spell and if the prey is within + * 2 dots of a missile fire, then see whether we will cast it. + * if next to player, lessen chance because we don't like being + * disrupted + */ + if (on(*tp, CANSLOW) && dest_player && + dist < (BOLT_LENGTH+5)*(BOLT_LENGTH+5) && + rnd(100) < (dist > 3 ? tp->t_cast : tp->t_cast/2)) { + tp->t_action = A_SLOW; /* We're going to slow him */ + tp->t_no_move = 3 * movement(tp); /* Takes time! */ + debug("casting slow spell!"); + return(TRUE); + } + + /* + * If we have a special magic item, we might use it. We will restrict + * this options to uniques with relics and creatures with wands for now. + * Also check for the quartermaster. Don't want him shooting wands.... + */ + if ((on(*tp, ISUNIQUE) || on(*tp, CARRYSTICK)) && + off(*tp, CANSELL) && dest_player && + m_use_pack(tp, ee, dist, shoot_dir)) { + return(TRUE); + } + + /* From now on, we must have a direct shot at the prey */ + if (!straight_shot) return(FALSE); + + /* We may use a sonic blast if we can, only on the player */ + if (on(*tp, CANSONIC) && + dest_player && + (dist < BOLT_LENGTH*2) && + (rnd(100) < tp->t_breathe)) { + tp->t_newpos = *shoot_dir; /* Save the direction */ + tp->t_action = A_SONIC; /* We're going to sonic blast */ + tp->t_no_move = 2 * movement(tp); /* Takes 2 movement periods */ + } + + /* If we can breathe, we may do so */ + else if (on(*tp, CANBREATHE) && + (dist < BOLT_LENGTH*BOLT_LENGTH) && + (rnd(100) < tp->t_breathe)) { + tp->t_newpos = *shoot_dir; /* Save the direction */ + tp->t_action = A_BREATHE; /* We're going to breathe */ + tp->t_no_move = movement(tp); /* It takes 1 movement period */ + } + + /* + * We may shoot missiles if we can + * if next to player, lessen chance so we don't get disrupted as often + */ + else if (on(*tp,CANMISSILE) && + rnd(100) < (dist > 3 ? tp->t_cast : tp->t_cast/2)){ + tp->t_newpos = *shoot_dir; /* Save the direction */ + tp->t_action = A_MISSILE; /* We're going to shoot MM's */ + tp->t_no_move = 3 * movement(tp); /* Takes time! */ + } + + /* + * If we can shoot or throw something, we might do so. + * If next to player, then forget it + */ + else if ((on(*tp,CANSHOOT) || on(*tp,CARRYWEAPON) || + on(*tp,CARRYDAGGER) || on(*tp, CARRYAXE)) && + dist > 3 && + off(*tp, CANSELL) && + (get_hurl(tp) != NULL)) { + tp->t_newpos = *shoot_dir; /* Save the direction */ + tp->t_action = A_THROW; /* We're going to throw something */ + tp->t_using = get_hurl(tp); /* Save our weapon */ + tp->t_no_move = 2 * movement(tp); /* Takes 2 movement periods */ + } + + /* We couldn't find anything to do */ + else return(FALSE); + + return(TRUE); + +} + +reap() +{ + _t_free_list(&rlist); +} + +/* + * runners: + * Make all the awake monsters try to do something. + */ + +runners(segments) +int segments; /* Number of segments since last called */ +{ + register struct linked_list *item; + register struct thing *tp = NULL; + register int min_time = 20; /* Minimum time until a monster can act */ + + /* + * loop thru the list of running (wandering) monsters and see what + * each one will do this time. + * + * Note: the special case that one of this buggers kills another. + * if this happens than we have to see if the monster killed + * himself or someone else. In case its himself we have to get next + * one immediately. If it wasn't we have to get next one at very + * end in case he killed the next one. + */ + for (item = mlist; item != NULL; item = item->l_next) + { + tp = THINGPTR(item); + turn_on(*tp, NEEDSTOACT); + } + + for(;;) + { + for (item = mlist; item != NULL; item = item->l_next) + { + tp = THINGPTR(item); + + if (on(*tp, NEEDSTOACT)) + break; + } + + if (item == NULL) + break; + + turn_off(*tp, NEEDSTOACT); + + /* If we are not awake, just skip us */ + + if (off(*tp, ISRUN) && off(*tp, ISHELD)) + continue; + + /* See if it's our turn */ + + tp->t_no_move -= segments; + + if (tp->t_no_move > 0) + { + if (tp->t_no_move < min_time) min_time = tp->t_no_move; + continue; + } + + /* If we were frozen, we're moving now */ + + if (tp->t_action == A_FREEZE) + tp->t_action = A_NIL; + + if (on(*tp, ISHELD)) + { + /* Make sure the action and using are nil */ + + tp->t_action = A_NIL; + tp->t_using = NULL; + + /* Can we break free? */ + + if (rnd(tp->t_stats.s_lvl) > 11) + { + turn_off(*tp, ISHELD); + + runto(tp, &hero); + + if (cansee(tp->t_pos.y, tp->t_pos.x)) + msg("%s breaks free from the hold spell", + prname(monster_name(tp), TRUE)); + } + else /* Too bad -- try again later */ + tp->t_no_move = movement(tp); + } + + /* Heal the creature if it's not in the middle of some action */ + + if (tp->t_action == A_NIL) + doctor(tp); + + while (off(*tp, ISELSEWHERE) && + off(*tp, ISDEAD) && + tp->t_no_move <= 0 && + off(*tp, ISHELD) && + on(*tp, ISRUN) ) + { + /* Let's act (or choose an action if t_action = A_NIL) */ + + m_act(tp); + } + + if ( off(*tp,ISELSEWHERE) && off(*tp,ISDEAD) ) + { + if (tp->t_no_move < min_time) + min_time = tp->t_no_move; + + if (tp->t_quiet < 0) + tp->t_quiet = 0; + } + } + + return(min_time); +} + +/* + * See if a monster has some magic it can use. Return TRUE if so. + * Only care about relics and wands for now. + */ +bool +m_use_pack(monster, defend_pos, dist, shoot_dir) +register struct thing *monster; +coord *defend_pos; +register int dist; +register coord *shoot_dir; +{ + register struct object *obj; + register struct linked_list *pitem, *relic, *stick; + register int units = -1; + + relic = stick = NULL; + + for (pitem=monster->t_pack; pitem; pitem=next(pitem)) { + obj = OBJPTR(pitem); + if (obj->o_flags & ISCURSED) continue; + if (obj->o_type == RELIC) { + switch (obj->o_which) { + case MING_STAFF: + if (shoot_dir != NULL) { + units = 2; /* Use 2 time units */ + relic = pitem; + } + + when EMORI_CLOAK: + if (obj->o_charges != 0 && + shoot_dir != NULL) { + units = 2; /* Use 2 time units */ + relic = pitem; + } + + when ASMO_ROD: + /* The bolt must be able to reach the defendant */ + if (shoot_dir != NULL && + dist < BOLT_LENGTH * BOLT_LENGTH) { + units = 2; /* Use 2 time units */ + relic = pitem; + } + + when BRIAN_MANDOLIN: + /* The defendant must be the player and within 4 spaces */ + if (ce(*defend_pos, hero) && + dist < 25 && + player.t_action != A_FREEZE) { + units = 4; + relic = pitem; + } + + when GERYON_HORN: + /* The defendant must be the player and within 5 spaces */ + if (ce(*defend_pos, hero) && + dist < 25 && + (off(player,ISFLEE)|| player.t_dest!=&monster->t_pos)) { + units = 3; + relic = pitem; + } + } + } + if (obj->o_type == STICK) { + if (obj->o_charges < 1) continue; + switch(obj->o_which) { + case WS_ELECT: + case WS_FIRE: + case WS_COLD: + /* The bolt must be able to reach the defendant */ + if (shoot_dir != NULL && + dist < BOLT_LENGTH * BOLT_LENGTH) { + units = 3; + stick = pitem; + } + + when WS_MISSILE: + case WS_SLOW_M: + case WS_CONFMON: + case WS_PARALYZE: + case WS_MDEG: + case WS_FEAR: + if (shoot_dir != NULL) { + units = 3; + stick = pitem; + } + + otherwise: + break; + } + } + } + + /* use relics in preference to all others */ + if (relic) debug("chance to use relic = %d%%", monster->t_artifact); + if (stick) debug("chance to use stick = %d%%", monster->t_wand); + if (relic && rnd(100) < monster->t_artifact) { + monster->t_action = A_USERELIC; + pitem = relic; + } + else if (stick && rnd(100) < monster->t_wand) { + /* + * see if the monster will use the wand + */ + pitem = stick; + monster->t_action = A_USEWAND; + } + else { + return(FALSE); + } + + monster->t_no_move = units * movement(monster); + monster->t_using = pitem; + monster->t_newpos = *shoot_dir; + return(TRUE); +} +