Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10 5/3/83; site cmu-cs-cad.ARPA Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!godot!harvard!seismo!rochester!cmu-cs-pt!cmu-cs-cad!mlm From: mlm@cmu-cs-cad.ARPA (Michael Mauldin) Newsgroups: net.sources Subject: Rog-O-Matic XIV (part 01 of 10) Message-ID: <265@cmu-cs-cad.ARPA> Date: Fri, 1-Feb-85 11:25:23 EST Article-I.D.: cmu-cs-c.265 Posted: Fri Feb 1 11:25:23 1985 Date-Received: Sun, 3-Feb-85 08:47:46 EST Organization: Carnegie-Mellon University, CS/RI Lines: 1459 #!/bin/sh # # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ # @ Here is part of your new automatic Rogue player, Rog-O-Matic XIV! @ # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ # # [Note: this is a Beta-Test release of version XIV, and almost # certainly contains bugs. A new version will be made available # soon. If you experience any problems with this version, please # contact Michael Mauldin as soon as possible, so your input can be # included in the new release] # # Rog-O-Matic XIV is shipped via mail in pieces, files rgm14.01, rgm14.02, # ..., rgm14.nn. Each piece contains some number of smaller files. To # retrieve them, run each file through the shell 'sh', as follows: # # sh debug.c << '/' X/* X * debug.c: Rog-O-Matic XIV (CMU) Fri Dec 28 21:48:55 1984 - mlm X * Copyright (C) 1985 by A. Appel, G. Jacobson, L. Hamey, and M. Mauldin X * X * This file contains the code for the debugger. Rogomatic has one of X * the tensest internal debuggers around, because in the early days it X * had an incredible number of bugs, with no way to repeat an error X * (because Rogue uses a different dungeon each time). X */ X X# include X# include X# include "types.h" X# include "globals.h" X# include "install.h" X X/* X * Debugging wait loop: Handle the usual Rogomatic command chars, and also X * allows dumping the flags '^' command. Exits when a non-command char is X * typed. To use, just put a "dwait (type, "message");" wherever you need X * debugging messages, and hit a space or a cr to continue X */ X Xdwait (msgtype, f, a1, a2, a3, a4, a5, a6, a7, a8) Xchar *f; Xint msgtype, a1, a2, a3, a4, a5, a6, a7, a8; X{ char msg[128]; X int r, c; X X /* Build the actual message */ X sprintf (msg, f, a1, a2, a3, a4, a5, a6, a7, a8); X X /* Log the message if the error is severe enough */ X if (!replaying && (msgtype & (D_FATAL | D_ERROR | D_WARNING))) X { char errfn[128]; FILE *errfil; X X sprintf (errfn, "%s/error%s", RGMDIR, versionstr); X if ((errfil = wopen (errfn, "a")) != NULL) X { fprintf (errfil, "User %s, error type %d: %s\n\n", X getname(), msgtype, msg); X if (msgtype & (D_FATAL | D_ERROR)) X { printsnap (errfil); X summary (errfil, NEWLINE); X fprintf (errfil, "\f\n"); X } X fclose (errfil); X } X } X X if (msgtype & D_FATAL) X { extern jmp_buf commandtop; /* From play */ X saynow (msg); X playing = 0; X quitrogue ("fatal error trap", Gold, SAVED); X longjmp (commandtop); X } X X if (! debug (msgtype | D_INFORM)) /* If debugoff */ X { if (msgtype & D_SAY) /* Echo? */ X { saynow (msg); return (1); } /* Yes => win */ X return (0); /* No => lose */ X } X X if (*msg) { mvaddstr (0, 0, msg); clrtoeol (); } /* Write msg */ X if (noterm) return (1); /* Exit if no user */ X X /* Debugging loop, accept debugging commands from user */ X while (1) X { refresh (); X switch (fgetc (stdin)) X { case '?': X say ("i=inv, d=debug !=stf, @=mon, #=wls, $=id, ^=flg, &=chr"); X break; X case 'i': at (1,0); dumpinv (NULL); at (row, col); break; X case 'd': toggledebug (); break; X case 't': transparent = 1; break; X case '!': dumpstuff (); break; X case '@': dumpmonster (); break; X case '#': dumpwalls (); break; X case '^': promptforflags (); break; X case '&': X if (getscrpos ("char", &r, &c)) X saynow ("Char at %d,%d '%c'", r, c, screen[r][c]); X break; X case '(': dumpdatabase (); at (row, col); break; X case ')': markcycles (DOPRINT); at (row, col); break; X case '~': saynow ("Version %d, quit at %d", version, quitat); break; X case '/': dosnapshot (); break; X default: at (row, col); return (1); X } X } X} X X/* X * promptforflags: Prompt the user for a location and dump its flags. X */ X Xpromptforflags () X{ int r, c; X X if (getscrpos ("flags", &r, &c)) X { mvprintw (0, 0, "Flags for %d,%d ", r, c); X dumpflags (r, c); X clrtoeol (); X at (row, col); X } X} X X/* X * dumpflags: Create a message line for the scrmap flags of a particular X * square. Note that the fnames[] array must match the X * various flags defined in "types.h". X */ X Xchar *fnames[] = X{ "been", "cango", "door", "hall", "psd", "room", X "safe", "seen", "deadend", "stuff", "trap", "arrow", X "trapdor", "teltrap", "gastrap", "beartrap", "dartrap", "waterap", X "monster", "wall", "useless", "scarem", "stairs", "runok", X "boundry", "sleeper", "everclr" X}; X Xdumpflags (r, c) Xint r, c; X{ char **f; int b; X X printw (":"); X for (f=fnames, b=1; b<=EVERCLR; b = b * 2, f++) X if (scrmap[r][c] & b) X printw ("%s:", *f); X} X X/* X * Timehistory: print a time analysis of the game. X */ X Xtimehistory (f, sep) XFILE *f; Xchar sep; X{ register int i, j; X char s[2048]; X X timespent[0].timestamp = 0; X X sprintf (s, "Time Analysis: %s%c%c", X "othr hand fght rest move expl rung grop srch door total", X sep, sep); X X for (i=1; i<=MaxLevel; i++) X { sprintf (s, "%slevel %2d: ", s, i); X for (j = T_OTHER; j < T_LISTLEN; j++) X sprintf (s, "%s%5d", s, timespent[i].activity[j]); X sprintf (s, "%s%6d%c", X s, timespent[i].timestamp - timespent[i-1].timestamp, sep); X } X X if (f == NULL) X addstr (s); X else X fprintf (f, "%s", s); X} X X/* X * toggledebug: Set the value of the debugging word. X */ X Xtoggledebug () X{ char debugstr[100]; X int type = debugging & ~(D_FATAL | D_ERROR | D_WARNING); X X if (debugging == D_ALL) debugging = D_NORMAL; X else if (debugging == D_NORMAL) debugging = D_NORMAL | D_SEARCH; X else if (type == D_SEARCH) debugging = D_NORMAL | D_BATTLE; X else if (type == D_BATTLE) debugging = D_NORMAL | D_MESSAGE; X else if (type == D_MESSAGE) debugging = D_NORMAL | D_PACK; X else if (type == D_PACK) debugging = D_NORMAL | D_MONSTER; X else if (type == D_MONSTER) debugging = D_NORMAL | D_CONTROL; X else if (type == D_CONTROL) debugging = D_NORMAL | D_SCREEN; X else if (type == D_SCREEN) debugging = D_NORMAL | D_WARNING; X else if (!debug (D_INFORM)) debugging = D_NORMAL | D_WARNING | D_INFORM; X else debugging = D_ALL; X X strcpy (debugstr, "Debugging :"); X X if (debug(D_FATAL)) strcat (debugstr, "fatal:"); X if (debug(D_ERROR)) strcat (debugstr, "error:"); X if (debug(D_WARNING)) strcat (debugstr, "warn:"); X if (debug(D_INFORM)) strcat (debugstr, "info:"); X if (debug(D_SEARCH)) strcat (debugstr, "search:"); X if (debug(D_BATTLE)) strcat (debugstr, "battle:"); X if (debug(D_MESSAGE)) strcat (debugstr, "msg:"); X if (debug(D_PACK)) strcat (debugstr, "pack:"); X if (debug(D_CONTROL)) strcat (debugstr, "ctrl:"); X if (debug(D_SCREEN)) strcat (debugstr, "screen:"); X if (debug(D_MONSTER)) strcat (debugstr, "monster:"); X X saynow (debugstr); X} X X/* X * getscrpos: Prompt the user for an x,y coordinate on the screen. X */ X Xgetscrpos (msg, r, c) Xchar *msg; Xint *r, *c; X{ char buf[256]; X X saynow ("At %d,%d: enter 'row,col' for %s: ", atrow, atcol, msg); X X if (fgets (buf, 256, stdin)) X { sscanf (buf, "%d,%d", r, c); X if (*r>=1 && *r<23 && *c>=0 && *c<=79) X return (1); X else X say ("%d,%d is not on the screen!", *r, *c); X } X X at (row, col); X return (0); X} / echo 'x - strategy.c' sed 's/^X//' > strategy.c << '/' X/* X * strategy.c: Rog-O-Matic XIV (CMU) Thu Jan 31 20:51:06 1985 - mlm X * Copyright (C) 1985 by A. Appel, G. Jacobson, L. Hamey, and M. Mauldin X * X * This file contains all of the 'high level intelligence' of Rog-O-Matic. X */ X X# include X# include X# include X# include "types.h" X# include "globals.h" X# include "install.h" X X/* X * foughtmonster records whether we engaged in battle recently. This X * information is used to tell whether we should sit still, waiting for a X * confused monster to come back, or to go on about our business. X * DIDFIGHT is the number of turns to sit still after a battle. X */ X X# define DIDFIGHT 3 X Xextern int genericinit(), sleepvalue(); /* From explore.c */ X X/* X * strategize: Run through each rule until something fires. Return 1 if an X * action was taken, otherwise return 0 (and then play will read a command X * from the user). X */ X Xint strategize () X{ X dwait (D_CONTROL, "Strategizing..."); X X /* If replaying, instead of making an action, return the old one */ X if (replaying) return (replaycommand ()); X X /* Clear any messages we printed last turn */ X if (msgonscreen) { at (0,0); clrtoeol (); msgonscreen = 0; at (row,col); } X X X /* ----------------------- Production Rules --------------------------- */ X X X if (fightmonster ()) /* We are under attack! */ X return (1); X X if (fightinvisible ()) /* Claude Raines! */ X return (1); X X if (tomonster ()) /* Go play with the pretty monster */ X return (1); X X if (shootindark ()) /* Shoot arrows in dark rooms */ X return (1); X X if (handleweapon ()) /* Play with the nice sword */ X { dwait (D_BATTLE, "Switching to sword [1]"); return (1); } X X if (light ()) /* Fiat lux! Especially if we lost */ X return (1); /* a monster from view. */ X X if (dinnertime ()) /* Soups on! */ X return (1); X X /* X * These variables are short term memory. Slowed and X * cancelled are fuses which are disabled after a small X * number of turns. X */ X X lyinginwait = 0; /* No more monsters to wait for */ X if (foughtmonster) foughtmonster--; /* Turns since fought monster */ X if (slowed) slowed--; /* Turns since we slowed a monster */ X if (cancelled) cancelled--; /* Turns since we zapped 'cancel' */ X if (beingheld) beingheld--; /* Turns since held by a fungus */ X X /* ---- End of short term memory modification ---- */ X X if (dropjunk ()) /* Send it back */ X return (1); X X if (readscroll ()) /* Get out the reading glasses */ X return (1); /* Must come before handlearmor() */ X X if (handlearmor ()) /* Play dressup */ X return (1); X X if (quaffpotion ()) /* Glug glug glug ... */ X return (1); /* Must come before handlering() */ X X if (handlering ()) /* We are engaged! */ X return (1); X X if (blinded && grope (100)) /* Who turned out the lights */ X { display ("Blinded, groping..."); return (1); } X X if (aftermelee ()) /* Wait for lingering monsters */ X return (1); X X if (tostuff ()) /* Pick up the play pretty */ X return (1); X X if (restup ()) /* Yawn! */ X return (1); X X if (goupstairs (NOTRUNNING)) /* Up we go! Make sure that we get */ X return (1); /* a better rank on the board. */ X X if (trywand ()) /* Try to use a wand */ X return (1); X X if (gotowardsgoal ()) /* Keep on trucking */ X return (1); X X if (exploreroom ()) /* Search the room */ X return (1); X X if (archery ()) /* Try to position for fight */ X return (1); X X if (pickupafter ()) /* Look for stuff dropped by arched mon */ X return (1); X X if (plunge ()) /* Plunge mode */ X return (1); X X if (findarrow ()) /* Do we have an unitialized arrow? */ X return (1); X X if (findroom ()) /* Look for another room */ X return (1); X X /* X * 'attempt' records the number of times we have completely searched X * this level for secret doors. If attempt is greater than 0, then we X * have failed once to find the stairs and go down. If this happens X * three times, there could be amonster sleeping on the stairs. We set X * the SLEEPER bit for each square with a sleeping monster. Go find X * such a monster and kill it to see whether (s)he was on the stairs). X */ X X if (attempt > 4 && makemove (ATTACKSLEEP, genericinit, sleepvalue, REUSE)) X { display ("No stairs, attacking sleeping monster..."); X return (1); X } X X if (Level>1 && larder>0 && doorexplore ()) /* Grub around */ X return (1); X X if (godownstairs (NOTRUNNING)) /* Down we go! */ X return (1); X X if ((Level<2 || larder<1) && doorexplore()) /* Grub around anyway */ X return (1); X X /* X * If we think we are on the stairs, but arent, maybe they were moved X * (ie we were hallucinating when we saw them last time). X */ X X if (on (STAIRS) && (atrow != stairrow || atcol != staircol)) X { dwait (D_ERROR, "Stairs moved!"); findstairs (); return (1); } X X /* X * If we failed to find the stairs, explore each possible secret door X * another ten times. X */ X X while (attempt++ < MAXATTEMPTS) X { timestosearch += max (3, k_door / 5); X foundnew (); X if (doorexplore ()) return (1); X } X X /* X * Don't give up, start all over! X */ X X newlevel (); X display ("I would give up, but I am too stubborn, starting over..."); X return (grope (100)); X} X X/* X * fightmonster: looks for adjacent monsters. If found, it calls X * battlestations to prepare for battle otherwise hacks with the X * weapon already in hand. X */ X Xint fightmonster () X{ register int i, rr, cc, mdir = NONE, mbad = NONE, danger = 0; X int melee = 0, adjacent = 0, alertmonster = 0; X int wanddir = NONE, m = NONE, howmean; X char mon, monc = ':', *monster; X X /* Check for adjacent monsters */ X for (i = 0; i < mlistlen; i++) X { rr = mlist[i].mrow; cc = mlist[i].mcol; X if (max (abs (atrow-rr), abs (atcol-cc)) == 1) X { if (mlist[i].q != ASLEEP) X { if (mlist[i].q != HELD || Hp >= Hpmax || !havefood (1)) X { melee = 1; X if (mlist[i].q == AWAKE) alertmonster = 1; X } X } X } X } X X if (!melee) return (0); /* No one to fight */ X X /* Loop to find worst monster and tally danger & number adjacent */ X for (i = 0; i < mlistlen; i++) X { rr = mlist[i].mrow; cc = mlist[i].mcol; /* Monster position */ X X /* X * If the monster is adjacent and is either awake or X * we dont know yet whether he is asleep, but we havent X * see any alert monsters yet. X */ X X if (max (abs (atrow-rr), abs (atcol-cc)) == 1 && X (alertmonster ? mlist[i].q == AWAKE : X mlist[i].q != ASLEEP)) /* DR Utexas 26 Jan 84 */ X { mon = mlist[i].chr; /* Record the monster type */ X monster = monname (mon); /* Record the monster name */ X danger += maxhitchar(mon); /* Add to the danger */ X X /* If he is adjacent, add to the adj count */ X if (onrc (CANGO, rr, atcol) && onrc (CANGO, atrow, cc)) X { adjacent++; howmean = isholder (monster) ? 10000 : avghit(i); X X /* If he is adjacent and the worst monster yet, save him */ X if (howmean > mbad) X { wanddir = mdir = direc (rr-atrow, cc-atcol); X monc = mon; m = i; mbad = howmean; X } X } X X /* If we havent yet a line of sight, check this guy out */ X else if (wanddir == NONE) X { wanddir = direc (rr-atrow, cc-atcol); } X X /* Debugging breakpoint */ X dwait (D_BATTLE, "%c <%d,%d>, danger %d, worst %c(%d,%d), total %d", X screen[rr][cc], rr-atrow, cc-atcol, X danger, monc, mdir, mbad, adjacent); X } X } X X /* X * The following variables have now been set: X * X * monc: The letter of the worst monster we can hit X * mbad: Relative scale 0 to 26, how bad is (s)he X * mdir: Which direction to him/her X * danger: How many hit points can (s)he/they do this round? X * wanddir: Direction of worst monster, even if we cant move to it. X */ X X /* X * Check whether the battlestations expert has a suggested action. X */ X X monster = monname (monc); X if (battlestations (m, monster, mbad, danger, adjacent ? mdir : wanddir, X adjacent ? 1 : 2, alertmonster, max (1, adjacent))) X { foughtmonster = DIDFIGHT; return (1); } X X /* X * If we did not wait for him last turn, and he is not adjacent, X * let him move to us (otherwise, he gets to hits us first). X */ X X if (!lyinginwait && !adjacent) X { command (T_FIGHTING, "s"); X dwait (D_BATTLE, "Lying in wait..."); X lyinginwait = 1; X foughtmonster = DIDFIGHT; X return (1); X } X X /* If we are here but have no direction, there was a bug somewhere */ X if (mdir < 0) X { dwait (D_BATTLE, "Adjacent, but no direction known!"); X return (0); X } X X /* If we could die this round, tell the user about it */ X if (danger >= Hp) display ("In trouble..."); X X /* Well, nothing better than to hit the beast! Tell dwait about it */ X dwait (D_BATTLE, "Attacking %s(%d) direction %d (total danger %d)...", X monster, mbad, mdir, danger); X X /* Record the monster type */ X lastmonster = monc-'A'+1; X X /* Move towards the monster (this causes us to hit him) */ X rmove (1, mdir, T_FIGHTING); X lyinginwait = 0; X foughtmonster = DIDFIGHT; X return (1); X} X X/* X * tomonster: if we can see a monster (and either it is awake or we X * think we can beat it) then pick the worst one, call battlestations, X * and then call gotowards to move toward the monster. If the monster X * is an odd number of turns away, sit once to assure initiative before X * charging after him. Special case for sitting on a door. X */ X Xint tomonster () X{ register int i, dist, rr, cc, mdir = NONE, mbad = NONE; X int closest, which, danger = 0, adj = 0; X char monc = ':', monchar = ':', *monster; X X /* If no monsters, fail */ X if (mlistlen==0) X return (0); X X /* X * Loop through the monsters, 'which' and 'closest' record the index X * and distance of the closest monster worth fighting. X */ X X for (i = 0, which = NONE, closest = 999; i < mlistlen; i++) X { dist = max (abs (mlist[i].mrow - atrow), abs (mlist[i].mcol - atcol)); X monchar = mlist[i].chr; X X /* X * IF we are not using a magic arrow OR X * we want to wake this monster up AND we can beat him OR X * he is standing near something we want and we will have to X * fight him anywhay X * THEN consider fighting the monster. X * X * Don't pick fights with sleepers if cosmic. DR UTexas 25 Jan 84 X */ X X if (usingarrow || mlist[i].q == AWAKE || X (!cosmic && wanttowake (monchar) && X (avghit (i) <= 50 || (maxhit (i) + 50 - k_wake) < Hp)) || X (mlist[i].q == HELD && Hp >= Hpmax)) X { danger += maxhit(i); /* track total danger */ X adj++; /* count number of monsters */ X X /* If he is the closest monster, save his index and distance */ X if (dist < closest) X { closest = dist; which = i; monc = mlist[i].chr; mbad = avghit(i); } X X /* Or if he is meaner than another equally close monster, save him */ X else if (dist == closest && avghit(i) > avghit(which)) X { dwait (D_BATTLE, "Chasing %c(%d) rather than %c(%d) at distance %d.", X mlist[i].chr, avghit(i), mlist[which].chr, X avghit(which), dist); X X closest = dist; which = i; monc = mlist[i].chr; mbad = avghit(i); X } X } X } X X /* No monsters worth bothering, return failure */ X if (which < 0) return (0); X X /* Save the monsters location in registers */ X rr = mlist[which].mrow - atrow; cc = mlist[which].mcol - atcol; X X /* If the monster is on an exact diagonal, record direction */ X mdir = (rr==0 || cc==0 || abs(rr)==abs(cc)) ? direc (rr, cc) : -1; X X /* Get a string which names the monster */ X monster = monname (monc); X X /* If 'battlestations' has an action, use that action */ X if (battlestations (which, monster, mbad, danger, mdir, closest, 1, adj)) X return (1); X X /* If he is an odd number of squares away, lie in wait for him */ X if (closest&1 == 0 && !lyinginwait) X { command (T_FIGHTING, "s"); X dwait (D_BATTLE, "Waiting for monster an odd number of squares away..."); X lyinginwait = 1; X return (1); X } X X /* "We have him! Move toward him!" */ X if (gotowards (mlist[which].mrow, mlist[which].mcol, 0)) X { goalr = mlist[which].mrow; goalc = mlist[which].mcol; X lyinginwait = 0; X return (1); X } X X /* Could not find a path to the monster, record failure */ X return (0); X} X X/* X * wanttowake is true for monsters without special attacks, such that the X * expected damage from hits is a reasonable estimate of their vorpalness. X * Some monsters are included here because we want to shoot arrows at them. X */ X Xwanttowake(c) Xchar c; X{ char *monster = monname (c); X X if (missedstairs) X return (1); X X /* X * If monster sleeping but won't wake up when we move around him, X * return wanttowake as false. DR UTexas 09 Jan 84 X */ X X if (streq (monster, "centaur") || X streq (monster, "dragon") || X streq (monster, "floating eye") || X streq (monster, "ice monster") || X streq (monster, "leprechaun") || X streq (monster, "nymph") || X streq (monster, "wraith") || X streq (monster, "purple worm") ) X return (0); X X return (1); X} X X/* X * aftermelee: called when we have just fought a monster, assures X * that it wasn't just a confused monster that backed X * away and might get a hit on us if we move. Now only X * used when we lost a monster from view. X * X * Also rest if we are critically weak and have some food. X */ X Xaftermelee () X{ X if (foughtmonster > 0) X { lyinginwait = 1; X command (T_RESTING, "s"); X dwait (D_BATTLE, "Aftermelee: waiting for %d rounds.", foughtmonster); X return (1); X } X X /* If critically weak, rest up so traps won't kill us. DR Utexas */ X if (Hp < 6 && larder > 0) X { command (T_RESTING, "s"); X display ("Recovering from severe beating..."); X return (1); X } X X return (foughtmonster = 0); X} X X/* X * battlestations: X * X * We are going into battle. Can we think of anything better to X * to than simply hacking at him with our weapon? X */ X X# define die_in(n) (Hp/n < danger*50/(100-k_run)) X# define live_for(n) (! die_in(n)) X Xbattlestations (m, monster, mbad, danger, mdir, mdist, alert, adj) Xint m; /* Monster index */ Xchar *monster; /* What is it? */ Xint mbad; /* How bad is it? */ Xint danger; /* How many points damage per round? */ Xint mdir; /* Which direction (clear line of sight)? */ Xint mdist; /* How many turns until battle? */ Xint alert; /* Is he known to be awake? */ Xint adj; /* How many attackers are there? */ X{ int obj, turns; X static int stepback = 0; X X /* Ascertain whether we have a clear path to this monster */ X if (mdir != NONE && !checkcango (mdir, mdist)) X mdir = NONE; X X /* Number of turns is one less than distance (modified if we are hasted) */ X turns = hasted ? (mdist-1)*2 : (mdist-1); X X /* No point in wasting resources when we are invulnerable */ X if (on (SCAREM) && (turns > 0 || confused) && !streq(monster, "dragon")) X { command (T_RESTING, "s"); X display ("Resting on scare monster"); X dwait (D_BATTLE, "Battlestations: resting, on scaremonster."); X return (1); X } X X /* X * Take invisible stalkers into account into account, X * fightmonster() and tomonster() cant see stalkers. X */ X X if (beingstalked > 1000) { turns = 0; danger += 16; } X X /* Debugging breakpoint */ X dwait (D_BATTLE, X "Battlestations: %s(%d), total danger %d, dir %d, %d turns, %d adj.", X monster, mbad, danger, mdir, turns, adj); X X X /* X * Switch back to our mace or sword? X */ X X if (live_for (1) && turns < 2 && wielding (thrower) && handleweapon ()) X { dwait (D_BATTLE, "Switching to sword [2]"); return (1); } X X /* X * Dont waste magic when on a scare monster scroll X */ X X if (on (SCAREM) && !streq (monster, "dragon")) X { dwait (D_BATTLE, "Battlestations: hitting from scaremonster."); X return (0); X } X X /* X * If we were busy resting on the stairs and we see a monster, go down X * Go on down if about to be attacked by a monster with an effective X * magic attack. DR UTexas 25 Jan 84 X */ X X if (on(STAIRS) && ((Level>18 && Level<26) || exploredlevel) && !floating && X (die_in(5) || X ((seeawakemonster ("rattlesnake") || seeawakemonster ("giant ant")) && X (have (ring, "sustain strength") < 0)) || X ((seeawakemonster ("aquator") || seeawakemonster ("rust monster")) && X turns < 2 && willrust (currentarmor) && X wearing ("maintain armor") == NONE) || X seeawakemonster ("medusa") || seeawakemonster ("umber hulk"))) X { if (goupstairs (RUNNING) || godownstairs (RUNNING)) X return (1); X } X X /* X * Are healing potions worthwhile? X */ X X if (die_in (1) && Hpmax-Hp > 10 && turns > 0 && X ((obj = havenamed (potion, "extra healing")) != NONE || X (obj = havenamed (potion, "healing")) != NONE)) X return (quaff (obj)); X X /* X * Run away if we are sure of the direction and we are in trouble X * Dont try to run if a fungi has ahold of us. If we are confused, X * we will try other things, and we will decide to run later. X * If we are on a door, wait until the monster is on us (that way X * we can shoot arrows at him, if we want to). X * Don't run away from Dragons!!! They'll just flame you. X */ X X if (!confused && !beingheld && (!on(DOOR) || turns < 1) && X (!streq (monster, "dragon") || cosmic) && Hp+Explev < Hpmax && X ((die_in(1) || Hp <= danger + between (Level-10, 0, 10)) || chicken) && X runaway ()) X { display ("Run away! Run away!"); X darkdir = NONE; darkturns = 0; X return(1); } X X /* X * Be clever when facing multiple monsters? X */ X X if (adj > 1 && !confused && !beingheld && !on (STAIRS | DOOR) && X backtodoor (turns)) X return (1); X X /* X * stepback to see if he is awake. X */ X X if (!alert && !beingheld && !stepback && mdir != NONE && X turns == 0 && !on (DOOR | STAIRS)) X { int rdir = (mdir+4)%8; X if (onrc (CANGO | TRAP, atdrow(rdir), atdcol(rdir)) == CANGO) X { move1 (rdir); stepback = 7; return (1); } X } X X if (stepback) stepback--; /* Decrement turns until step back again */ X X /* X * Should we put on our ring of maintain armor? DR UTexas 19 Jan 84 X */ X X if (live_for (1) && currentarmor != NONE && X (leftring == NONE || rightring == NONE) && X (seemonster ("aquator") || seemonster ("rust monster")) && X willrust (currentarmor) && X wearing ("maintain armor") == NONE && X (obj = havenamed (ring, "maintain armor")) != NONE && X puton (obj)) X return (1); X X if (turns > 1 && live_for (2) && leftring != NONE && rightring != NONE && X (seemonster ("aquator") || seemonster ("rust monster")) && X wearing ("maintain armor") < 0 && X findring ("maintain armor")) X return (1); X X /* X * Should we put on our ring of sustain strength? DR UTexas 19 Jan 84 X */ X X if ((live_for (1) || turns > 0) && X (leftring == NONE || rightring == NONE) && X (seemonster ("giant ant") || seemonster ("rattlesnake")) && X wearing ("sustain strength") < 0 && X (obj = havenamed (ring, "sustain strength")) != NONE && X puton (obj)) X return (1); X X if ((live_for (2) || turns > 1) && X leftring != NONE && rightring != NONE && X (seemonster ("giant ant") || seemonster ("rattlesnake")) && X wearing ("sustain strength") < 0 && X findring ("sustain strength")) X return (1); X X /* X * Should we put on our ring of regeneration? Make sure we wont kill X * ourselves trying to do it, by checking how many turns it will take to X * get it on compared to the number of hits we can take. X */ X X /* Have a ring and a free hand, one turn */ X if (die_in (4) && (live_for (1) || turns > 0) && X (leftring == NONE || rightring == NONE) && X !(turns == 0 && (streq (monster, "rattlesnake") || X streq (monster, "giant ant"))) && X wearing ("regeneration") < 0 && X (obj = havenamed (ring, "regeneration")) != NONE && X puton (obj)) X return (1); X X /* Have a ring and both hands are full, takes two turns */ X if (die_in (4) && (live_for (2) || turns > 1) && X leftring != NONE && rightring != NONE && X wearing ("regeneration") < 0 && X findring ("regeneration")) X return (1); X X /* X * Haste ourselves? X */ X X if (!hasted && version > RV36B && (turns > 0 || live_for (1)) && X die_in (2) && (obj = havenamed (potion, "haste self")) != NONE && X quaff (obj)) X return (1); X X /* X * Confuse the poor beast? X */ X X if (die_in (2) && turns > 0 && !redhands && X ((obj = havenamed (scroll, "monster confusion")) != NONE)) X return (reads (obj)); X X /* X * Put them all to sleep? This does us little good, since we cant X * currently infer that we have a scroll of Hold Monster. But we X * will read scrolls of identify on the second one. Bug, this X * does not put them to sleep, it just holds them in place. X * We have a lot more programming to do here!!!! Fuzzy X */ X X if (die_in (1) && (obj = havenamed (scroll, "hold monster")) != NONE && X reads (obj)) X { holdmonsters (); X return (1); X } X X /* X * Drop a scare monster? X */ X X if (die_in (1) && !streq(monster, "dragon") && X (obj = havenamed (scroll, "scare monster")) != NONE && X drop (obj)) X { set (SCAREM); X droppedscare++; X return (1); X } X X /* X * Buy buy birdy! X */ X X if (die_in (1) && mdir != NONE && turns == 0 && X (obj = havewand ("teleport away")) != NONE && X point (obj, mdir)) X { if (streq (monster, "violet fungi")) beingheld = 0; X if (streq (monster, "venus flytrap")) beingheld = 0; X return (1); X } X X /* X * Eat dust, turkey! X */ X X if (die_in (1) && turns == 0 && X (obj = havenamed (scroll, "teleportation")) != NONE) X { beingheld = 0; X return (reads (obj)); X } X X /* X * The better part of valor... X */ X X if ((die_in (1) && turns == 0 || fainting ()) && quitforhonors ()) X return (1); X X /* X * If we trust our magic arrow, give it a whirl X */ X X if (!confused && cheat && usingarrow && goodarrow > 10 && turns == 0) X return (0); X X /* X * Try to protect our armor from Rusties. X */ X X if (!cursedarmor && currentarmor != NONE && X (seeawakemonster ("rust monster") || seeawakemonster ("aquator")) && X live_for (1) && X !(cosmic && Level < 8) && /* DR UTexas 25 Jan 84 */ X willrust (currentarmor) && X wearing ("maintain armor") == NONE && X takeoff ()) X { return (1); } X X /* X * Any life saving wands? X */ X X if (die_in (2) && Hp > 40 && turns < 3 && X !(streq (monster, "purple worm") || streq (monster, "jabberwock")) && X (obj = havewand ("drain life")) != NONE) X return (point (obj, 0)); X X if (mdir != NONE && die_in (2) && X (!cosmic || Level > 18) && /* DR UTexas 31 Jan 84 */ X (streq (monster, "dragon") || streq (monster, "purple worm") || X streq (monster, "jabberwock") || streq (monster, "medusa") || X streq (monster, "xorn") || streq (monster, "violet fungi") || X streq (monster, "griffin") || streq (monster, "venus flytrap") || X streq (monster, "umber hulk") || streq (monster, "black unicorn")) && X (obj = havewand ("polymorph")) != NONE) X return (point (obj, mdir)); X X /* X * Any life prolonging wands? X */ X X if ((die_in (1) || (turns == 0 && streq (monster, "floating eye")) || X (turns == 0 && streq (monster, "ice monster"))) && X mdir != NONE && mdist < 6 && !on(DOOR) && X ((obj = havewand ("fire")) != NONE && !streq(monster, "dragon") || X (obj = havewand ("cold")) != NONE || X (obj = havewand ("lightning")) != NONE)) X return (point (obj, mdir)); X X if (die_in (2) && mdir != NONE && !slowed && (turns>0 || live_for (2)) && X (obj = havewand ("slow monster")) != NONE && X (slowed = 5)) X return (point (obj, mdir)); X X if (mdir != NONE && !cancelled && turns == 0 && X (streq (monster, "wraith") || X streq (monster, "vampire") || X streq (monster, "floating eye") || X streq (monster, "ice monster") || X streq (monster, "leprechaun") || X streq (monster, "violet fungi") || X streq (monster, "venus flytrap")) && X (obj = havewand ("cancellation")) != NONE && X (cancelled = 10)) X { if (streq (monster, "violet fungi") || streq (monster, "venus flytrap")) X beingheld = 0; X return (point (obj, mdir)); X } X X if (((die_in (3) && live_for (1)) || X (turns == 0 && streq (monster, "floating eye")) || X (turns == 0 && streq (monster, "ice monster"))) && X mdir != NONE && X (((obj = havewand ("magic missile")) != NONE && turns > 0) || X ((obj = havewand ("striking")) != NONE && turns == 0))) X return (point (obj, mdir)); X X /* X * Since we have no directional things, we will try to run even though X * we are confused. Again, wait at door until the monster is on us. X * Don't run away from dragons, they'll just flame you!! X */ X X if (confused && !beingheld && (!on(DOOR) || turns < 1) && X ! streq (monster, "dragon") && X ((die_in (1) && Hp+Explev/2+3 < Hpmax) || chicken) && X runaway ()) X { display ("Run away! Run away!"); return(1); } X X /* X * We can live for a while, try to get to a position where we can run X * away if we really get into trouble. Dont run away from dragons, X * they'll just flame you!!! X */ X X if (!confused && !beingheld && ! streq (monster, "dragon") && X (mdir < 0 || turns < 5) && X (((adj > 1 || live_for (1)) && die_in (4) && !canrun ())) && X unpin ()) X { display ("Unpinning!!!"); return(1); } X X /* X * Light up the room if we are in combat. X */ X X if (turns > 0 && die_in (3) && lightroom ()) X return (1); X X /* X * We arent yet in danger and can shoot at the old monster. X */ X X if ((live_for (5) || turns > 1) && shootindark ()) X return (1); X X /* X * Try out an unknown wand? Try shooting unknown wands at X * rattlesnakes since they are such a pain. DR UTexas 19 Jan 84 X */ X X if (live_for (2) && (Level > 8 || streq (monster, "rattlesnake") || X streq (monster, "giant ant")) && X mdir != NONE && on(ROOM) && mdist < 6 && X ((obj = unknown (wand)) != NONE) && point (obj, mdir)) X { usesynch = 0; X /* zappedunknown = TRUE; */ /* DR UTexas 19 Jan 84 */ X return (1); X } X X /* X * Wait to see if he is really awake. X */ X X if (!alert && !lyinginwait && turns > 0) X { command (T_FIGHTING, "s"); X dwait (D_BATTLE, "Waiting to see if he is awake..."); X lyinginwait = 1; X return (1); X } X X /* X * Archery: try to move into a better position, and after that, try to X * shoot an arrow at the beast. Conserve arrows below SAVEARROWS. X */ X X if ((streq (monster, "leprechaun") || X streq (monster, "nymph") || X streq (monster, "floating eye") || X streq (monster, "ice monster") || X streq (monster, "giant ant") || X streq (monster, "rattlesnake") || X streq (monster, "wraith") || X streq (monster, "vampire") || X streq (monster, "centaur") || /* DR UTexas 21 Jan 84 */ X die_in (1+k_arch/20) || ammo > SAVEARROWS+5-k_arch/10) && X (obj = havemissile ()) != NONE) X { X /* Move into position */ X if ((!alert || mdir < 0) && turns > 0 && archmonster (m, 1)) X return (1); X X /* If in position */ X if (!on (HALL) && mdir != NONE && turns > 0) X { int bow; X X /* Wield the bow if we have time */ X if (!cursedweapon && !wielding (thrower) && turns > 4 && X (bow = havebow (1, NOPRINT)) != NONE && wield (bow)) X return (1); X X /* And shoot! */ X throw (obj, mdir); X return (1); X } X } X X /* X * Switch back to our mace or sword? X */ X X if (!cursedweapon && wielding (thrower) && handleweapon ()) X { dwait (D_BATTLE, "Switching to sword [3]"); return (1); } X X /* X * No bright ideas. Return and let the caller figure out what to do. X */ X X return (0); X} X X/* X * tostuff: if we see something to pick up, go to it. If our pack is full, X * try to drop our least useful item. If pack is still full, fail. X */ X Xint tostuff () X{ register int i, closest, dist, w, worst, worstval; X int which, wrow, wcol; X stuff what; X X /* If we don't see anything (or dont care), return failure */ X if (slistlen == 0 || Level == 1 && have (amulet) != NONE) return (0); X X /* X * Now find the closest thing to pick up. Dont consider things we have X * already dropped (those squares have the USELESS bit set), unless we X * have dropped a scroll of SCARE MONSTER, in which case we want our X * pack to be full. Dont be fooled by stairs when hallucinating. X * X * NOTE: Dont pick up the scaremonster scroll!!! MLM X */ X X for (i = 0, which = NONE, closest = 999; i < slistlen; i++) X { if (!onrc (USELESS, slist[i].srow, slist[i].scol) || X (droppedscare && objcount < maxobj && X !onrc (SCAREM, slist[i].srow, slist[i].scol))) X { dist = max (abs (slist[i].srow - atrow), abs (slist[i].scol - atcol)); X X /* Make junk look farther away, but not farther than infinity */ X if (onrc (USELESS, slist[i].srow, slist[i].scol)) dist += 500; X X /* If this is the closest item, save its distance and index */ X if (dist < closest) X { closest = dist; which = i; } X } X } X X /* Could not find anything worth picking up, return failure */ X if (which < 0) return (0); X X /* Found something, save its location and type in registers */ X what= slist[which].what; wrow= slist[which].srow; wcol= slist[which].scol; X X /* We can always pick up more gold */ X if (what == gold) return (gotowards (wrow, wcol, 0)); X X /* Have space in our pack, go get it */ X if (objcount < maxobj) return (gotowards (wrow, wcol, 0)); X X /* No space in pack and we cannot drop something here, fail */ X if (on (STUFF | DOOR | TRAP | STAIRS)) return (0); X X /* Must drop something, pick least valuable item to drop */ X for (worst = NONE, worstval = 9999, i = 0; i < invcount; i++) X { if (inven[i].count && !itemis (i, INUSE) && (w = worth (i)) < worstval) X { worst = i; worstval = w; } X X /* Once we have found a toally useless item, stop looking */ X if (worstval == 0) break; X } X X /* Found an item, drop it */ X if (worst != NONE) return (drop (worst)); X X /* Pack is full and we cant find something to drop, fail */ X return (0); X} X X/* X * fightinvisible: being hounded by unseen beasties, try something clever. X */ X X# define INVDAM (16) X# define INVPRES (INVHIT-100) X# define INVLURK (INVPRES-200) X Xfightinvisible () X{ char cmd[20]; register int dir, liberties = 0, lastdir, obj; X X /* Count down the time since we were last hit by a stalker */ X if (--beingstalked < 0) X { return (beingstalked=0); } X X /* If we are in real trouble, we might want to quit */ X if (beingstalked > INVPRES && Hp < INVDAM && quitforhonors ()) X { return (1); } X X /* Can we teleport out of here? */ X if (Hp < INVDAM && beingstalked > INVPRES && X (obj = havenamed (scroll, "teleport")) != NONE && reads (obj)) X { beingstalked = INVPRES-1; X return (1); } X X /* Can we quaff a potion of see invisible? */ X if ((obj = havenamed (potion, "see invisible")) != NONE && quaff (obj)) X { beingstalked = 0; return (1); } X X /* If we have some time, try putting on a ring of see invisible */ X if (Hp > (INVDAM * 3/2) && beingstalked > INVLURK && X findring ("see invisible")) X { return (1); } X X /* If we can bail out to the next level, do so */ X if (((beingstalked < INVPRES && Hp < (INVDAM * 2)) || X (beingstalked >= INVPRES && Hp < (INVDAM * 3))) && X godownstairs (RUNNING)) X { display ("Running like hell from an invisible stalker..."); X return (1); } X X /* Nothing worth doing, but he is around somewhere */ X if (beingstalked <= INVPRES) X return (0); X X /* Must fight him 'mano a mano', tell the user (who cant see him either) */ X display ("Fighting invisible stalker..."); X *cmd = '\0'; X X /* Record the monster type (for didhit and didmiss, see mess.c) */ X if (version < RV53A) X lastmonster = ('I'-'A'+1); X else X lastmonster = ('P'-'A'+1); X X /* Count how many orthogonal moves we can make */ X for (dir=0; dir<8; dir++) X if (atdrow(dir) > 0 && atdrow(dir) < 23 && X onrc(CANGO, atdrow(dir), atdcol(dir)) && X onrc(CANGO, atdrow(dir), atcol) && X onrc(CANGO, atrow, atdcol(dir))) X { liberties++; lastdir = dir; } X X /* If can only go two ways, then go back and forth (will hit) */ X if (liberties == 1 || liberties == 2) X sprintf (cmd, "%c%c", keydir[lastdir], keydir[(lastdir+4)&7]); X X /* Try to get away, usually gets to a square with only 2 liberties */ X else if (runaway ()) return (1); X X /* Else run two and then double back on him. If that will */ X /* not work, run in a circle (will hit one out of 4) */ X else X { for (dir=0; dir<8; dir += 2) X if ((onrc(CANGO, atdrow(dir), atdcol(dir))) && X (onrc(CANGO, atrow+2*deltr[dir], atcol+2*deltc[dir]))) X break; X X if (dir > 7) command (T_FIGHTING, "hjlk"); X else command (T_FIGHTING, "%c%c%c", keydir[dir], X keydir[dir], keydir[(dir+4)&7]); X } X X return (1); X} X X/* X * archery: Try to arch sleeping monsters. The 'mtokill' attr keeps track X * of how many arrows we want to be able to pump into a monster before we X * decide to wake him up. That means we must be that far away AND have X * that many missiles in our pack. This number can be modified by our hit X * and damage bonuses. X * X * Note: some monsters are to wimpy archery, and some too mean. MLM X */ X Xarchery () X{ register int m, mtk; X char *monster; X X for (m=0; m < mlistlen; m++) /* Find a sleeping monster */ X { monster = monname (mlist[m].chr); X X /* X * If he is not awake and either X * we are much stronger than he is or X * he is a known target for archery and X * we have enough arrows to wipe this dude out and X * we have food or he is a leprechaun and we arent hungry yet X * X * Then try calling archmonster to move to the right place. X */ X X if (mlist[m].q != AWAKE && gplushit != NONE && X !(mlist[m].q == HELD && Hp < Hpmax) && /* DR UTexas 26 Jan 84 */ X (maxhit(m) > Hp/3 || X streq (monster, "leprechaun") || X streq (monster, "nymph") || X streq (monster, "floating eye") || X streq (monster, "giant ant") || X streq (monster, "rattlesnake") || X streq (monster, "centaur") || X streq (monster, "ice monster")) && X ammo >= (mtk = monatt[mlist[m].chr-'A'].mtokill - gplushit) && X larder > 0 || X ((streq (monster, "leprechaun") && !hungry ()) || X streq (monster, "nympyh"))) X { dwait (D_BATTLE, "Arching at %c at (%d,%d)", X mlist[m].chr, mlist[m].mrow, mlist[m].mcol); X if (archmonster (m, mtk)) return (1); X dwait (D_BATTLE, "Archmonster failed in archery."); X } X } X X return (0); X} X X/* X * pickupafter: Go stand on square where the monster used to be. X * If (s)he left something behind (evens just arrows X * that missed) we will find it and pick it up. X * X * Bug: Sometimes goes the long way around and doesnt see things. X */ X Xpickupafter () X{ /* If no goal */ X if (agoalr < 0 || agoalc < 0) X return (0); X X /* If on goal */ X if (atrow == agoalr && atcol == agoalc) X { agoalr = agoalc = NONE; X return (0); X } X X /* Else go for it */ X return (gotowards (agoalr, agoalc, 0)); X} X X/* X * dropjunk: This doesnt just drop something. It destroys it. X * When an object is thrown diagonally into a corner, X * Rogue cant find a place to put it, and the object is X * removed from the game. Used to get rid of empty wands X * and staves. This way, we dont pick them up later, X * and mistake them for fresh wands. X */ X Xdropjunk () X{ int obj; X X if ((obj = haveuseless ()) != NONE && (gotocorner () || throw (obj, 7))) X return (1); X X return (0); X} X X/* X * quitforhonors: We are in mortal danger. Do we want to quit? X * X * Strategy: 'quitat' is the score to beat (set in setup); X * If we will beat it anyway, dont quit. If we X * wont beat it anyway, dont quit. If we will just X * beat the score by quiting, then do so. X * X * Assumes a 10 percent death tax. X */ X Xquitforhonors () X{ X if (Gold > quitat && (Gold-Gold/10) <= quitat) X { quitrogue ("quit (scoreboard)", Gold, 0); X return (1); X } X X return (0); X} / echo 'Part 01 of Rog-O-Matic XIV complete.' exit