|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T s
Length: 36627 (0x8f13)
Types: TextFile
Names: »strategy.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/General/Rog-O-Matic/strategy.c«
/*
* strategy.c: Rog-O-Matic XIV (CMU) Thu Jan 31 20:51:06 1985 - mlm
* Copyright (C) 1985 by A. Appel, G. Jacobson, L. Hamey, and M. Mauldin
*
* This file contains all of the 'high level intelligence' of Rog-O-Matic.
*/
# include <stdio.h>
# include <ctype.h>
# include <curses.h>
# include "types.h"
# include "globals.h"
# include "install.h"
/*
* foughtmonster records whether we engaged in battle recently. This
* information is used to tell whether we should sit still, waiting for a
* confused monster to come back, or to go on about our business.
* DIDFIGHT is the number of turns to sit still after a battle.
*/
# define DIDFIGHT 3
extern int genericinit(), sleepvalue(); /* From explore.c */
/*
* strategize: Run through each rule until something fires. Return 1 if an
* action was taken, otherwise return 0 (and then play will read a command
* from the user).
*/
int strategize ()
{
dwait (D_CONTROL, "Strategizing...");
/* If replaying, instead of making an action, return the old one */
if (replaying) return (replaycommand ());
/* Clear any messages we printed last turn */
if (msgonscreen) { at (0,0); clrtoeol (); msgonscreen = 0; at (row,col); }
/* ----------------------- Production Rules --------------------------- */
if (fightmonster ()) /* We are under attack! */
return (1);
if (fightinvisible ()) /* Claude Raines! */
return (1);
if (tomonster ()) /* Go play with the pretty monster */
return (1);
if (shootindark ()) /* Shoot arrows in dark rooms */
return (1);
if (handleweapon ()) /* Play with the nice sword */
{ dwait (D_BATTLE, "Switching to sword [1]"); return (1); }
if (light ()) /* Fiat lux! Especially if we lost */
return (1); /* a monster from view. */
if (dinnertime ()) /* Soups on! */
return (1);
/*
* These variables are short term memory. Slowed and
* cancelled are fuses which are disabled after a small
* number of turns.
*/
lyinginwait = 0; /* No more monsters to wait for */
if (foughtmonster) foughtmonster--; /* Turns since fought monster */
if (slowed) slowed--; /* Turns since we slowed a monster */
if (cancelled) cancelled--; /* Turns since we zapped 'cancel' */
if (beingheld) beingheld--; /* Turns since held by a fungus */
/* ---- End of short term memory modification ---- */
if (dropjunk ()) /* Send it back */
return (1);
if (readscroll ()) /* Get out the reading glasses */
return (1); /* Must come before handlearmor() */
if (handlearmor ()) /* Play dressup */
return (1);
if (quaffpotion ()) /* Glug glug glug ... */
return (1); /* Must come before handlering() */
if (handlering ()) /* We are engaged! */
return (1);
if (blinded && grope (100)) /* Who turned out the lights */
{ display ("Blinded, groping..."); return (1); }
if (aftermelee ()) /* Wait for lingering monsters */
return (1);
if (tostuff ()) /* Pick up the play pretty */
return (1);
if (restup ()) /* Yawn! */
return (1);
if (goupstairs (NOTRUNNING)) /* Up we go! Make sure that we get */
return (1); /* a better rank on the board. */
if (trywand ()) /* Try to use a wand */
return (1);
if (gotowardsgoal ()) /* Keep on trucking */
return (1);
if (exploreroom ()) /* Search the room */
return (1);
if (archery ()) /* Try to position for fight */
return (1);
if (pickupafter ()) /* Look for stuff dropped by arched mon */
return (1);
if (plunge ()) /* Plunge mode */
return (1);
if (findarrow ()) /* Do we have an unitialized arrow? */
return (1);
if (findroom ()) /* Look for another room */
return (1);
/*
* 'attempt' records the number of times we have completely searched
* this level for secret doors. If attempt is greater than 0, then we
* have failed once to find the stairs and go down. If this happens
* three times, there could be amonster sleeping on the stairs. We set
* the SLEEPER bit for each square with a sleeping monster. Go find
* such a monster and kill it to see whether (s)he was on the stairs).
*/
if (attempt > 4 && makemove (ATTACKSLEEP, genericinit, sleepvalue, REUSE))
{ display ("No stairs, attacking sleeping monster...");
return (1);
}
if (Level>1 && larder>0 && doorexplore ()) /* Grub around */
return (1);
if (godownstairs (NOTRUNNING)) /* Down we go! */
return (1);
if ((Level<2 || larder<1) && doorexplore()) /* Grub around anyway */
return (1);
/*
* If we think we are on the stairs, but arent, maybe they were moved
* (ie we were hallucinating when we saw them last time).
*/
if (on (STAIRS) && (atrow != stairrow || atcol != staircol))
{ dwait (D_ERROR, "Stairs moved!"); findstairs (); return (1); }
/*
* If we failed to find the stairs, explore each possible secret door
* another ten times.
*/
while (attempt++ < MAXATTEMPTS)
{ timestosearch += max (3, k_door / 5);
foundnew ();
if (doorexplore ()) return (1);
}
/*
* Don't give up, start all over!
*/
newlevel ();
display ("I would give up, but I am too stubborn, starting over...");
return (grope (100));
}
/*
* fightmonster: looks for adjacent monsters. If found, it calls
* battlestations to prepare for battle otherwise hacks with the
* weapon already in hand.
*/
int fightmonster ()
{ register int i, rr, cc, mdir = NONE, mbad = NONE, danger = 0;
int melee = 0, adjacent = 0, alertmonster = 0;
int wanddir = NONE, m = NONE, howmean;
char mon, monc = ':', *monster;
/* Check for adjacent monsters */
for (i = 0; i < mlistlen; i++)
{ rr = mlist[i].mrow; cc = mlist[i].mcol;
if (max (abs (atrow-rr), abs (atcol-cc)) == 1)
{ if (mlist[i].q != ASLEEP)
{ if (mlist[i].q != HELD || Hp >= Hpmax || !havefood (1))
{ melee = 1;
if (mlist[i].q == AWAKE) alertmonster = 1;
}
}
}
}
if (!melee) return (0); /* No one to fight */
/* Loop to find worst monster and tally danger & number adjacent */
for (i = 0; i < mlistlen; i++)
{ rr = mlist[i].mrow; cc = mlist[i].mcol; /* Monster position */
/*
* If the monster is adjacent and is either awake or
* we dont know yet whether he is asleep, but we havent
* see any alert monsters yet.
*/
if (max (abs (atrow-rr), abs (atcol-cc)) == 1 &&
(alertmonster ? mlist[i].q == AWAKE :
mlist[i].q != ASLEEP)) /* DR Utexas 26 Jan 84 */
{ mon = mlist[i].chr; /* Record the monster type */
monster = monname (mon); /* Record the monster name */
danger += maxhitchar(mon); /* Add to the danger */
/* If he is adjacent, add to the adj count */
if (onrc (CANGO, rr, atcol) && onrc (CANGO, atrow, cc))
{ adjacent++; howmean = isholder (monster) ? 10000 : avghit(i);
/* If he is adjacent and the worst monster yet, save him */
if (howmean > mbad)
{ wanddir = mdir = direc (rr-atrow, cc-atcol);
monc = mon; m = i; mbad = howmean;
}
}
/* If we havent yet a line of sight, check this guy out */
else if (wanddir == NONE)
{ wanddir = direc (rr-atrow, cc-atcol); }
/* Debugging breakpoint */
dwait (D_BATTLE, "%c <%d,%d>, danger %d, worst %c(%d,%d), total %d",
screen[rr][cc], rr-atrow, cc-atcol,
danger, monc, mdir, mbad, adjacent);
}
}
/*
* The following variables have now been set:
*
* monc: The letter of the worst monster we can hit
* mbad: Relative scale 0 to 26, how bad is (s)he
* mdir: Which direction to him/her
* danger: How many hit points can (s)he/they do this round?
* wanddir: Direction of worst monster, even if we cant move to it.
*/
/*
* Check whether the battlestations expert has a suggested action.
*/
monster = monname (monc);
if (battlestations (m, monster, mbad, danger, adjacent ? mdir : wanddir,
adjacent ? 1 : 2, alertmonster, max (1, adjacent)))
{ foughtmonster = DIDFIGHT; return (1); }
/*
* If we did not wait for him last turn, and he is not adjacent,
* let him move to us (otherwise, he gets to hits us first).
*/
if (!lyinginwait && !adjacent)
{ command (T_FIGHTING, "s");
dwait (D_BATTLE, "Lying in wait...");
lyinginwait = 1;
foughtmonster = DIDFIGHT;
return (1);
}
/* If we are here but have no direction, there was a bug somewhere */
if (mdir < 0)
{ dwait (D_BATTLE, "Adjacent, but no direction known!");
return (0);
}
/* If we could die this round, tell the user about it */
if (danger >= Hp) display ("In trouble...");
/* Well, nothing better than to hit the beast! Tell dwait about it */
dwait (D_BATTLE, "Attacking %s(%d) direction %d (total danger %d)...",
monster, mbad, mdir, danger);
/* Record the monster type */
lastmonster = monc-'A'+1;
/* Move towards the monster (this causes us to hit him) */
rmove (1, mdir, T_FIGHTING);
lyinginwait = 0;
foughtmonster = DIDFIGHT;
return (1);
}
/*
* tomonster: if we can see a monster (and either it is awake or we
* think we can beat it) then pick the worst one, call battlestations,
* and then call gotowards to move toward the monster. If the monster
* is an odd number of turns away, sit once to assure initiative before
* charging after him. Special case for sitting on a door.
*/
int tomonster ()
{ register int i, dist, rr, cc, mdir = NONE, mbad = NONE;
int closest, which, danger = 0, adj = 0;
char monc = ':', monchar = ':', *monster;
/* If no monsters, fail */
if (mlistlen==0)
return (0);
/*
* Loop through the monsters, 'which' and 'closest' record the index
* and distance of the closest monster worth fighting.
*/
for (i = 0, which = NONE, closest = 999; i < mlistlen; i++)
{ dist = max (abs (mlist[i].mrow - atrow), abs (mlist[i].mcol - atcol));
monchar = mlist[i].chr;
/*
* IF we are not using a magic arrow OR
* we want to wake this monster up AND we can beat him OR
* he is standing near something we want and we will have to
* fight him anywhay
* THEN consider fighting the monster.
*
* Don't pick fights with sleepers if cosmic. DR UTexas 25 Jan 84
*/
if (usingarrow || mlist[i].q == AWAKE ||
(!cosmic && wanttowake (monchar) &&
(avghit (i) <= 50 || (maxhit (i) + 50 - k_wake) < Hp)) ||
(mlist[i].q == HELD && Hp >= Hpmax))
{ danger += maxhit(i); /* track total danger */
adj++; /* count number of monsters */
/* If he is the closest monster, save his index and distance */
if (dist < closest)
{ closest = dist; which = i; monc = mlist[i].chr; mbad = avghit(i); }
/* Or if he is meaner than another equally close monster, save him */
else if (dist == closest && avghit(i) > avghit(which))
{ dwait (D_BATTLE, "Chasing %c(%d) rather than %c(%d) at distance %d.",
mlist[i].chr, avghit(i), mlist[which].chr,
avghit(which), dist);
closest = dist; which = i; monc = mlist[i].chr; mbad = avghit(i);
}
}
}
/* No monsters worth bothering, return failure */
if (which < 0) return (0);
/* Save the monsters location in registers */
rr = mlist[which].mrow - atrow; cc = mlist[which].mcol - atcol;
/* If the monster is on an exact diagonal, record direction */
mdir = (rr==0 || cc==0 || abs(rr)==abs(cc)) ? direc (rr, cc) : -1;
/* Get a string which names the monster */
monster = monname (monc);
/* If 'battlestations' has an action, use that action */
if (battlestations (which, monster, mbad, danger, mdir, closest, 1, adj))
return (1);
/* If he is an odd number of squares away, lie in wait for him */
if (closest&1 == 0 && !lyinginwait)
{ command (T_FIGHTING, "s");
dwait (D_BATTLE, "Waiting for monster an odd number of squares away...");
lyinginwait = 1;
return (1);
}
/* "We have him! Move toward him!" */
if (gotowards (mlist[which].mrow, mlist[which].mcol, 0))
{ goalr = mlist[which].mrow; goalc = mlist[which].mcol;
lyinginwait = 0;
return (1);
}
/* Could not find a path to the monster, record failure */
return (0);
}
/*
* wanttowake is true for monsters without special attacks, such that the
* expected damage from hits is a reasonable estimate of their vorpalness.
* Some monsters are included here because we want to shoot arrows at them.
*/
wanttowake(c)
char c;
{ char *monster = monname (c);
if (missedstairs)
return (1);
/*
* If monster sleeping but won't wake up when we move around him,
* return wanttowake as false. DR UTexas 09 Jan 84
*/
if (streq (monster, "centaur") ||
streq (monster, "dragon") ||
streq (monster, "floating eye") ||
streq (monster, "ice monster") ||
streq (monster, "leprechaun") ||
streq (monster, "nymph") ||
streq (monster, "wraith") ||
streq (monster, "purple worm") )
return (0);
return (1);
}
/*
* aftermelee: called when we have just fought a monster, assures
* that it wasn't just a confused monster that backed
* away and might get a hit on us if we move. Now only
* used when we lost a monster from view.
*
* Also rest if we are critically weak and have some food.
*/
aftermelee ()
{
if (foughtmonster > 0)
{ lyinginwait = 1;
command (T_RESTING, "s");
dwait (D_BATTLE, "Aftermelee: waiting for %d rounds.", foughtmonster);
return (1);
}
/* If critically weak, rest up so traps won't kill us. DR Utexas */
if (Hp < 6 && larder > 0)
{ command (T_RESTING, "s");
display ("Recovering from severe beating...");
return (1);
}
return (foughtmonster = 0);
}
/*
* battlestations:
*
* We are going into battle. Can we think of anything better to
* to than simply hacking at him with our weapon?
*/
# define die_in(n) (Hp/n < danger*50/(100-k_run))
# define live_for(n) (! die_in(n))
battlestations (m, monster, mbad, danger, mdir, mdist, alert, adj)
int m; /* Monster index */
char *monster; /* What is it? */
int mbad; /* How bad is it? */
int danger; /* How many points damage per round? */
int mdir; /* Which direction (clear line of sight)? */
int mdist; /* How many turns until battle? */
int alert; /* Is he known to be awake? */
int adj; /* How many attackers are there? */
{ int obj, turns;
static int stepback = 0;
/* Ascertain whether we have a clear path to this monster */
if (mdir != NONE && !checkcango (mdir, mdist))
mdir = NONE;
/* Number of turns is one less than distance (modified if we are hasted) */
turns = hasted ? (mdist-1)*2 : (mdist-1);
/* No point in wasting resources when we are invulnerable */
if (on (SCAREM) && (turns > 0 || confused) && !streq(monster, "dragon"))
{ command (T_RESTING, "s");
display ("Resting on scare monster");
dwait (D_BATTLE, "Battlestations: resting, on scaremonster.");
return (1);
}
/*
* Take invisible stalkers into account into account,
* fightmonster() and tomonster() cant see stalkers.
*/
if (beingstalked > 1000) { turns = 0; danger += 16; }
/* Debugging breakpoint */
dwait (D_BATTLE,
"Battlestations: %s(%d), total danger %d, dir %d, %d turns, %d adj.",
monster, mbad, danger, mdir, turns, adj);
/*
* Switch back to our mace or sword?
*/
if (live_for (1) && turns < 2 && wielding (thrower) && handleweapon ())
{ dwait (D_BATTLE, "Switching to sword [2]"); return (1); }
/*
* Dont waste magic when on a scare monster scroll
*/
if (on (SCAREM) && !streq (monster, "dragon"))
{ dwait (D_BATTLE, "Battlestations: hitting from scaremonster.");
return (0);
}
/*
* If we were busy resting on the stairs and we see a monster, go down
* Go on down if about to be attacked by a monster with an effective
* magic attack. DR UTexas 25 Jan 84
*/
if (on(STAIRS) && ((Level>18 && Level<26) || exploredlevel) && !floating &&
(die_in(5) ||
((seeawakemonster ("rattlesnake") || seeawakemonster ("giant ant")) &&
(have (ring, "sustain strength") < 0)) ||
((seeawakemonster ("aquator") || seeawakemonster ("rust monster")) &&
turns < 2 && willrust (currentarmor) &&
wearing ("maintain armor") == NONE) ||
seeawakemonster ("medusa") || seeawakemonster ("umber hulk")))
{ if (goupstairs (RUNNING) || godownstairs (RUNNING))
return (1);
}
/*
* Are healing potions worthwhile?
*/
if (die_in (1) && Hpmax-Hp > 10 && turns > 0 &&
((obj = havenamed (potion, "extra healing")) != NONE ||
(obj = havenamed (potion, "healing")) != NONE))
return (quaff (obj));
/*
* Run away if we are sure of the direction and we are in trouble
* Dont try to run if a fungi has ahold of us. If we are confused,
* we will try other things, and we will decide to run later.
* If we are on a door, wait until the monster is on us (that way
* we can shoot arrows at him, if we want to).
* Don't run away from Dragons!!! They'll just flame you.
*/
if (!confused && !beingheld && (!on(DOOR) || turns < 1) &&
(!streq (monster, "dragon") || cosmic) && Hp+Explev < Hpmax &&
((die_in(1) || Hp <= danger + between (Level-10, 0, 10)) || chicken) &&
runaway ())
{ display ("Run away! Run away!");
darkdir = NONE; darkturns = 0;
return(1); }
/*
* Be clever when facing multiple monsters?
*/
if (adj > 1 && !confused && !beingheld && !on (STAIRS | DOOR) &&
backtodoor (turns))
return (1);
/*
* stepback to see if he is awake.
*/
if (!alert && !beingheld && !stepback && mdir != NONE &&
turns == 0 && !on (DOOR | STAIRS))
{ int rdir = (mdir+4)%8;
if (onrc (CANGO | TRAP, atdrow(rdir), atdcol(rdir)) == CANGO)
{ move1 (rdir); stepback = 7; return (1); }
}
if (stepback) stepback--; /* Decrement turns until step back again */
/*
* Should we put on our ring of maintain armor? DR UTexas 19 Jan 84
*/
if (live_for (1) && currentarmor != NONE &&
(leftring == NONE || rightring == NONE) &&
(seemonster ("aquator") || seemonster ("rust monster")) &&
willrust (currentarmor) &&
wearing ("maintain armor") == NONE &&
(obj = havenamed (ring, "maintain armor")) != NONE &&
puton (obj))
return (1);
if (turns > 1 && live_for (2) && leftring != NONE && rightring != NONE &&
(seemonster ("aquator") || seemonster ("rust monster")) &&
wearing ("maintain armor") < 0 &&
findring ("maintain armor"))
return (1);
/*
* Should we put on our ring of sustain strength? DR UTexas 19 Jan 84
*/
if ((live_for (1) || turns > 0) &&
(leftring == NONE || rightring == NONE) &&
(seemonster ("giant ant") || seemonster ("rattlesnake")) &&
wearing ("sustain strength") < 0 &&
(obj = havenamed (ring, "sustain strength")) != NONE &&
puton (obj))
return (1);
if ((live_for (2) || turns > 1) &&
leftring != NONE && rightring != NONE &&
(seemonster ("giant ant") || seemonster ("rattlesnake")) &&
wearing ("sustain strength") < 0 &&
findring ("sustain strength"))
return (1);
/*
* Should we put on our ring of regeneration? Make sure we wont kill
* ourselves trying to do it, by checking how many turns it will take to
* get it on compared to the number of hits we can take.
*/
/* Have a ring and a free hand, one turn */
if (die_in (4) && (live_for (1) || turns > 0) &&
(leftring == NONE || rightring == NONE) &&
!(turns == 0 && (streq (monster, "rattlesnake") ||
streq (monster, "giant ant"))) &&
wearing ("regeneration") < 0 &&
(obj = havenamed (ring, "regeneration")) != NONE &&
puton (obj))
return (1);
/* Have a ring and both hands are full, takes two turns */
if (die_in (4) && (live_for (2) || turns > 1) &&
leftring != NONE && rightring != NONE &&
wearing ("regeneration") < 0 &&
findring ("regeneration"))
return (1);
/*
* Haste ourselves?
*/
if (!hasted && version > RV36B && (turns > 0 || live_for (1)) &&
die_in (2) && (obj = havenamed (potion, "haste self")) != NONE &&
quaff (obj))
return (1);
/*
* Confuse the poor beast?
*/
if (die_in (2) && turns > 0 && !redhands &&
((obj = havenamed (scroll, "monster confusion")) != NONE))
return (reads (obj));
/*
* Put them all to sleep? This does us little good, since we cant
* currently infer that we have a scroll of Hold Monster. But we
* will read scrolls of identify on the second one. Bug, this
* does not put them to sleep, it just holds them in place.
* We have a lot more programming to do here!!!! Fuzzy
*/
if (die_in (1) && (obj = havenamed (scroll, "hold monster")) != NONE &&
reads (obj))
{ holdmonsters ();
return (1);
}
/*
* Drop a scare monster?
*/
if (die_in (1) && !streq(monster, "dragon") &&
(obj = havenamed (scroll, "scare monster")) != NONE &&
drop (obj))
{ set (SCAREM);
droppedscare++;
return (1);
}
/*
* Buy buy birdy!
*/
if (die_in (1) && mdir != NONE && turns == 0 &&
(obj = havewand ("teleport away")) != NONE &&
point (obj, mdir))
{ if (streq (monster, "violet fungi")) beingheld = 0;
if (streq (monster, "venus flytrap")) beingheld = 0;
return (1);
}
/*
* Eat dust, turkey!
*/
if (die_in (1) && turns == 0 &&
(obj = havenamed (scroll, "teleportation")) != NONE)
{ beingheld = 0;
return (reads (obj));
}
/*
* The better part of valor...
*/
if ((die_in (1) && turns == 0 || fainting ()) && quitforhonors ())
return (1);
/*
* If we trust our magic arrow, give it a whirl
*/
if (!confused && cheat && usingarrow && goodarrow > 10 && turns == 0)
return (0);
/*
* Try to protect our armor from Rusties.
*/
if (!cursedarmor && currentarmor != NONE &&
(seeawakemonster ("rust monster") || seeawakemonster ("aquator")) &&
live_for (1) &&
!(cosmic && Level < 8) && /* DR UTexas 25 Jan 84 */
willrust (currentarmor) &&
wearing ("maintain armor") == NONE &&
takeoff ())
{ return (1); }
/*
* Any life saving wands?
*/
if (die_in (2) && Hp > 40 && turns < 3 &&
!(streq (monster, "purple worm") || streq (monster, "jabberwock")) &&
(obj = havewand ("drain life")) != NONE)
return (point (obj, 0));
if (mdir != NONE && die_in (2) &&
(!cosmic || Level > 18) && /* DR UTexas 31 Jan 84 */
(streq (monster, "dragon") || streq (monster, "purple worm") ||
streq (monster, "jabberwock") || streq (monster, "medusa") ||
streq (monster, "xorn") || streq (monster, "violet fungi") ||
streq (monster, "griffin") || streq (monster, "venus flytrap") ||
streq (monster, "umber hulk") || streq (monster, "black unicorn")) &&
(obj = havewand ("polymorph")) != NONE)
return (point (obj, mdir));
/*
* Any life prolonging wands?
*/
if ((die_in (1) || (turns == 0 && streq (monster, "floating eye")) ||
(turns == 0 && streq (monster, "ice monster"))) &&
mdir != NONE && mdist < 6 && !on(DOOR) &&
((obj = havewand ("fire")) != NONE && !streq(monster, "dragon") ||
(obj = havewand ("cold")) != NONE ||
(obj = havewand ("lightning")) != NONE))
return (point (obj, mdir));
if (die_in (2) && mdir != NONE && !slowed && (turns>0 || live_for (2)) &&
(obj = havewand ("slow monster")) != NONE &&
(slowed = 5))
return (point (obj, mdir));
if (mdir != NONE && !cancelled && turns == 0 &&
(streq (monster, "wraith") ||
streq (monster, "vampire") ||
streq (monster, "floating eye") ||
streq (monster, "ice monster") ||
streq (monster, "leprechaun") ||
streq (monster, "violet fungi") ||
streq (monster, "venus flytrap")) &&
(obj = havewand ("cancellation")) != NONE &&
(cancelled = 10))
{ if (streq (monster, "violet fungi") || streq (monster, "venus flytrap"))
beingheld = 0;
return (point (obj, mdir));
}
if (((die_in (3) && live_for (1)) ||
(turns == 0 && streq (monster, "floating eye")) ||
(turns == 0 && streq (monster, "ice monster"))) &&
mdir != NONE &&
(((obj = havewand ("magic missile")) != NONE && turns > 0) ||
((obj = havewand ("striking")) != NONE && turns == 0)))
return (point (obj, mdir));
/*
* Since we have no directional things, we will try to run even though
* we are confused. Again, wait at door until the monster is on us.
* Don't run away from dragons, they'll just flame you!!
*/
if (confused && !beingheld && (!on(DOOR) || turns < 1) &&
! streq (monster, "dragon") &&
((die_in (1) && Hp+Explev/2+3 < Hpmax) || chicken) &&
runaway ())
{ display ("Run away! Run away!"); return(1); }
/*
* We can live for a while, try to get to a position where we can run
* away if we really get into trouble. Dont run away from dragons,
* they'll just flame you!!!
*/
if (!confused && !beingheld && ! streq (monster, "dragon") &&
(mdir < 0 || turns < 5) &&
(((adj > 1 || live_for (1)) && die_in (4) && !canrun ())) &&
unpin ())
{ display ("Unpinning!!!"); return(1); }
/*
* Light up the room if we are in combat.
*/
if (turns > 0 && die_in (3) && lightroom ())
return (1);
/*
* We arent yet in danger and can shoot at the old monster.
*/
if ((live_for (5) || turns > 1) && shootindark ())
return (1);
/*
* Try out an unknown wand? Try shooting unknown wands at
* rattlesnakes since they are such a pain. DR UTexas 19 Jan 84
*/
if (live_for (2) && (Level > 8 || streq (monster, "rattlesnake") ||
streq (monster, "giant ant")) &&
mdir != NONE && on(ROOM) && mdist < 6 &&
((obj = unknown (wand)) != NONE) && point (obj, mdir))
{ usesynch = 0;
/* zappedunknown = TRUE; */ /* DR UTexas 19 Jan 84 */
return (1);
}
/*
* Wait to see if he is really awake.
*/
if (!alert && !lyinginwait && turns > 0)
{ command (T_FIGHTING, "s");
dwait (D_BATTLE, "Waiting to see if he is awake...");
lyinginwait = 1;
return (1);
}
/*
* Archery: try to move into a better position, and after that, try to
* shoot an arrow at the beast. Conserve arrows below SAVEARROWS.
*/
if ((streq (monster, "leprechaun") ||
streq (monster, "nymph") ||
streq (monster, "floating eye") ||
streq (monster, "ice monster") ||
streq (monster, "giant ant") ||
streq (monster, "rattlesnake") ||
streq (monster, "wraith") ||
streq (monster, "vampire") ||
streq (monster, "centaur") || /* DR UTexas 21 Jan 84 */
die_in (1+k_arch/20) || ammo > SAVEARROWS+5-k_arch/10) &&
(obj = havemissile ()) != NONE)
{
/* Move into position */
if ((!alert || mdir < 0) && turns > 0 && archmonster (m, 1))
return (1);
/* If in position */
if (!on (HALL) && mdir != NONE && turns > 0)
{ int bow;
/* Wield the bow if we have time */
if (!cursedweapon && !wielding (thrower) && turns > 4 &&
(bow = havebow (1, NOPRINT)) != NONE && wield (bow))
return (1);
/* And shoot! */
throw (obj, mdir);
return (1);
}
}
/*
* Switch back to our mace or sword?
*/
if (!cursedweapon && wielding (thrower) && handleweapon ())
{ dwait (D_BATTLE, "Switching to sword [3]"); return (1); }
/*
* No bright ideas. Return and let the caller figure out what to do.
*/
return (0);
}
/*
* tostuff: if we see something to pick up, go to it. If our pack is full,
* try to drop our least useful item. If pack is still full, fail.
*/
int tostuff ()
{ register int i, closest, dist, w, worst, worstval;
int which, wrow, wcol;
stuff what;
/* If we don't see anything (or dont care), return failure */
if (slistlen == 0 || Level == 1 && have (amulet) != NONE) return (0);
/*
* Now find the closest thing to pick up. Dont consider things we have
* already dropped (those squares have the USELESS bit set), unless we
* have dropped a scroll of SCARE MONSTER, in which case we want our
* pack to be full. Dont be fooled by stairs when hallucinating.
*
* NOTE: Dont pick up the scaremonster scroll!!! MLM
*/
for (i = 0, which = NONE, closest = 999; i < slistlen; i++)
{ if (!onrc (USELESS, slist[i].srow, slist[i].scol) ||
(droppedscare && objcount < maxobj &&
!onrc (SCAREM, slist[i].srow, slist[i].scol)))
{ dist = max (abs (slist[i].srow - atrow), abs (slist[i].scol - atcol));
/* Make junk look farther away, but not farther than infinity */
if (onrc (USELESS, slist[i].srow, slist[i].scol)) dist += 500;
/* If this is the closest item, save its distance and index */
if (dist < closest)
{ closest = dist; which = i; }
}
}
/* Could not find anything worth picking up, return failure */
if (which < 0) return (0);
/* Found something, save its location and type in registers */
what= slist[which].what; wrow= slist[which].srow; wcol= slist[which].scol;
/* We can always pick up more gold */
if (what == gold) return (gotowards (wrow, wcol, 0));
/* Have space in our pack, go get it */
if (objcount < maxobj) return (gotowards (wrow, wcol, 0));
/* No space in pack and we cannot drop something here, fail */
if (on (STUFF | DOOR | TRAP | STAIRS)) return (0);
/* Must drop something, pick least valuable item to drop */
for (worst = NONE, worstval = 9999, i = 0; i < invcount; i++)
{ if (inven[i].count && !itemis (i, INUSE) && (w = worth (i)) < worstval)
{ worst = i; worstval = w; }
/* Once we have found a toally useless item, stop looking */
if (worstval == 0) break;
}
/* Found an item, drop it */
if (worst != NONE) return (drop (worst));
/* Pack is full and we cant find something to drop, fail */
return (0);
}
/*
* fightinvisible: being hounded by unseen beasties, try something clever.
*/
# define INVDAM (16)
# define INVPRES (INVHIT-100)
# define INVLURK (INVPRES-200)
fightinvisible ()
{ char cmd[20]; register int dir, liberties = 0, lastdir, obj;
/* Count down the time since we were last hit by a stalker */
if (--beingstalked < 0)
{ return (beingstalked=0); }
/* If we are in real trouble, we might want to quit */
if (beingstalked > INVPRES && Hp < INVDAM && quitforhonors ())
{ return (1); }
/* Can we teleport out of here? */
if (Hp < INVDAM && beingstalked > INVPRES &&
(obj = havenamed (scroll, "teleport")) != NONE && reads (obj))
{ beingstalked = INVPRES-1;
return (1); }
/* Can we quaff a potion of see invisible? */
if ((obj = havenamed (potion, "see invisible")) != NONE && quaff (obj))
{ beingstalked = 0; return (1); }
/* If we have some time, try putting on a ring of see invisible */
if (Hp > (INVDAM * 3/2) && beingstalked > INVLURK &&
findring ("see invisible"))
{ return (1); }
/* If we can bail out to the next level, do so */
if (((beingstalked < INVPRES && Hp < (INVDAM * 2)) ||
(beingstalked >= INVPRES && Hp < (INVDAM * 3))) &&
godownstairs (RUNNING))
{ display ("Running like hell from an invisible stalker...");
return (1); }
/* Nothing worth doing, but he is around somewhere */
if (beingstalked <= INVPRES)
return (0);
/* Must fight him 'mano a mano', tell the user (who cant see him either) */
display ("Fighting invisible stalker...");
*cmd = '\0';
/* Record the monster type (for didhit and didmiss, see mess.c) */
if (version < RV53A)
lastmonster = ('I'-'A'+1);
else
lastmonster = ('P'-'A'+1);
/* Count how many orthogonal moves we can make */
for (dir=0; dir<8; dir++)
if (atdrow(dir) > 0 && atdrow(dir) < 23 &&
onrc(CANGO, atdrow(dir), atdcol(dir)) &&
onrc(CANGO, atdrow(dir), atcol) &&
onrc(CANGO, atrow, atdcol(dir)))
{ liberties++; lastdir = dir; }
/* If can only go two ways, then go back and forth (will hit) */
if (liberties == 1 || liberties == 2)
sprintf (cmd, "%c%c", keydir[lastdir], keydir[(lastdir+4)&7]);
/* Try to get away, usually gets to a square with only 2 liberties */
else if (runaway ()) return (1);
/* Else run two and then double back on him. If that will */
/* not work, run in a circle (will hit one out of 4) */
else
{ for (dir=0; dir<8; dir += 2)
if ((onrc(CANGO, atdrow(dir), atdcol(dir))) &&
(onrc(CANGO, atrow+2*deltr[dir], atcol+2*deltc[dir])))
break;
if (dir > 7) command (T_FIGHTING, "hjlk");
else command (T_FIGHTING, "%c%c%c", keydir[dir],
keydir[dir], keydir[(dir+4)&7]);
}
return (1);
}
/*
* archery: Try to arch sleeping monsters. The 'mtokill' attr keeps track
* of how many arrows we want to be able to pump into a monster before we
* decide to wake him up. That means we must be that far away AND have
* that many missiles in our pack. This number can be modified by our hit
* and damage bonuses.
*
* Note: some monsters are to wimpy archery, and some too mean. MLM
*/
archery ()
{ register int m, mtk;
char *monster;
for (m=0; m < mlistlen; m++) /* Find a sleeping monster */
{ monster = monname (mlist[m].chr);
/*
* If he is not awake and either
* we are much stronger than he is or
* he is a known target for archery and
* we have enough arrows to wipe this dude out and
* we have food or he is a leprechaun and we arent hungry yet
*
* Then try calling archmonster to move to the right place.
*/
if (mlist[m].q != AWAKE && gplushit != NONE &&
!(mlist[m].q == HELD && Hp < Hpmax) && /* DR UTexas 26 Jan 84 */
(maxhit(m) > Hp/3 ||
streq (monster, "leprechaun") ||
streq (monster, "nymph") ||
streq (monster, "floating eye") ||
streq (monster, "giant ant") ||
streq (monster, "rattlesnake") ||
streq (monster, "centaur") ||
streq (monster, "ice monster")) &&
ammo >= (mtk = monatt[mlist[m].chr-'A'].mtokill - gplushit) &&
larder > 0 ||
((streq (monster, "leprechaun") && !hungry ()) ||
streq (monster, "nympyh")))
{ dwait (D_BATTLE, "Arching at %c at (%d,%d)",
mlist[m].chr, mlist[m].mrow, mlist[m].mcol);
if (archmonster (m, mtk)) return (1);
dwait (D_BATTLE, "Archmonster failed in archery.");
}
}
return (0);
}
/*
* pickupafter: Go stand on square where the monster used to be.
* If (s)he left something behind (evens just arrows
* that missed) we will find it and pick it up.
*
* Bug: Sometimes goes the long way around and doesnt see things.
*/
pickupafter ()
{ /* If no goal */
if (agoalr < 0 || agoalc < 0)
return (0);
/* If on goal */
if (atrow == agoalr && atcol == agoalc)
{ agoalr = agoalc = NONE;
return (0);
}
/* Else go for it */
return (gotowards (agoalr, agoalc, 0));
}
/*
* dropjunk: This doesnt just drop something. It destroys it.
* When an object is thrown diagonally into a corner,
* Rogue cant find a place to put it, and the object is
* removed from the game. Used to get rid of empty wands
* and staves. This way, we dont pick them up later,
* and mistake them for fresh wands.
*/
dropjunk ()
{ int obj;
if ((obj = haveuseless ()) != NONE && (gotocorner () || throw (obj, 7)))
return (1);
return (0);
}
/*
* quitforhonors: We are in mortal danger. Do we want to quit?
*
* Strategy: 'quitat' is the score to beat (set in setup);
* If we will beat it anyway, dont quit. If we
* wont beat it anyway, dont quit. If we will just
* beat the score by quiting, then do so.
*
* Assumes a 10 percent death tax.
*/
quitforhonors ()
{
if (Gold > quitat && (Gold-Gold/10) <= quitat)
{ quitrogue ("quit (scoreboard)", Gold, 0);
return (1);
}
return (0);
}