|
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); }