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 04 of 10) Message-ID: <267@cmu-cs-cad.ARPA> Date: Fri, 1-Feb-85 11:28:35 EST Article-I.D.: cmu-cs-c.267 Posted: Fri Feb 1 11:28:35 1985 Date-Received: Sun, 3-Feb-85 09:08:27 EST Organization: Carnegie-Mellon University, CS/RI Lines: 1656 #!/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 io.c << '/' X/* X * io.c: Rog-O-Matic XIV (CMU) Thu Jan 31 18:19:29 1985 - mlm X * Copyright (C) 1985 by A. Appel, G. Jacobson, L. Hamey, and M. Mauldin X * X * This file contains all of the functions which deal with the real world. X */ X X# include X# include X X# include "install.h" X X# ifdef BSD41 X# include X# else X# include X# endif X X# include "types.h" X# include "globals.h" X# include "termtokens.h" X X# define READ 0 X X/* X * Charonscreen returns the current character on the screen (using X * curses(3)). This macro is based on the winch(win) macro. X */ X# define charonscreen(X,Y) (stdscr->_y[X][Y]) X Xchar *month[] = X{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; X Xstatic char screen00 = ' '; X X/* Constants */ X X# define SENDQ 256 X X/* The command queue */ X Xchar queue[SENDQ]; /* To Rogue */ Xint head = 0, tail = 0; X X/* X * Getrogue: Sensory interface. X * X * Handle grungy low level terminal I/O. Getrogue reads tokens from the X * Rogue process and interprets them, making the screen array an image of X * the rogue level. Getrogue returns when the string waitstr has been read X * and either the cursor is on the Rogue '@' or some other condition X * implies that we have synchronized with Rogue. X */ X Xgetrogue (waitstr, onat) Xchar *waitstr; /* String to synchronize with */ Xint onat; /* 0 ==> Wait for waitstr X 1 ==> Cursor on @ sufficient X 2 ==> [1] + send ';' when ever X we eat a --More-- message */ X{ int botprinted = 0, wasmapped = didreadmap, r, c, pending (); X register int i, j; X char ch, *s, *m, *q, *d, *call, getroguetoken(); X int *doors; X static moved = 0; X X domonster(); /* LGCH */ X X newdoors = doorlist; /* no new doors found yet */ X atrow0 = atrow; atcol0 = atcol; /* Save our current posistion */ X s = waitstr; /* FSM to check for the wait msg */ X m = "More--"; /* FSM to check for '--More--' */ X call = "Call it:"; /* FSM to check for 'Call it:' */ X q = "(* for list): "; /* FSM to check for prompt */ X d = ")______"; /* FSM to check for tombstone grass */ X X if (moved) /* If we moved last time, put any */ X { sleepmonster (); moved = 0; } /* Old monsters to sleep */ X X /* While we have not reached the end of the Rogue input, read */ X /* characters from Rogue and figure out what they mean. */ X while ((*s) || X ((!hasted || version != RV36A) && onat && screen[row][col] != '@')) X { ch = getroguetoken (); X X /* If message ends in "(* for list): ", call terpmes */ X if (ch == *q) { if (*++q == 0) terpmes (); } X else q = "(* for list): "; X X /* Rogomatic now keys off of the grass under the Tombstone to */ X /* detect that it has been killed. This was done because the */ X /* "Press return" prompt only happens if there is a score file */ X /* Available on that system. Hopefully the grass is the same */ X /* in all versions of Rogue! */ X if (ch == *d) { if (0 == *++d) { addch (ch); deadrogue (); return;} } X else d = ")_______"; X X /* If the message has a more, strip it off and call terpmes */ X if (ch == *m) X { if (*++m == 0) X { /* More than 50 messages since last command ==> start logging */ X if (++morecount > 50 && !logging) X { toggleecho (); dwait (D_WARNING, "Started logging --More-- loop."); } X X /* More than 100 messages since last command ==> infinite loop */ X if (++morecount > 100) dwait (D_FATAL, "Caught in --More-- loop."); X X /* Send a space (and possibly a semicolon) to clear the message */ X if (onat == 2) sendnow (" ;"); X else sendnow (" "); X X /* Clear the --More-- of the end of the message */ X for (i = col - 7; i < col; screen[0][i++] = ' '); X X terpmes (); /* Interpret the message */ X X /* This code gets rid of the "Studded leather arm" bug */ X /* But it causes other problems. MLM */ X /* sprintf (&screen[0][col - 7], "--More--"); */ X } X } X else m = "More--"; X X /* If the message is 'Call it:', cancel the request */ X if (ch == *call) X { if (*++call == 0) X { /* Send an escape (and possibly a semicolon) to clear the message */ X if (onat == 2) sendnow ("%c;", ESC); X else sendnow ("%c", ESC); X } X } X else call = "Call it:"; X X /* Check to see whether we have read the synchronization string */ X if (*s) { if (ch == *s) s++; else s = waitstr; } X X /* Now figure out what the token means */ X switch (ch) X { case BS_TOK: X col--; X break; X X case CE_TOK: X if (row && row < 23) X for (i = col; i < 80; i++) X { updatepos (' ', row, i); X screen[row][i] = ' '; X } X else X for (i = col; i < 80; i++) X screen[row][i] = ' '; X X if (row) { at (row, col); clrtoeol (); } X else if (col == 0) screen00 = ' '; X break; X X case CL_TOK: X clearscreen (); X break; X X case CM_TOK: X screen00 = screen[0][0]; X break; X X case CR_TOK: X /* Handle missing '--more--' between inventories MLM 24-Jun-83 */ X if (row==0 && screen[0][1]==')' && screen[0][col-1] != '-') X terpmes (); X col = 0; X break; X X case DO_TOK: X row++; X break; X X case ER_TOK: X break; X X case LF_TOK: X row++; X col = 0; X break; X X case ND_TOK: X col++; X break; X X case SE_TOK: X revvideo = 0; X standend (); X break; X X case SO_TOK: X revvideo = 1; X standout (); X break; X X case TA_TOK: X col = 8 * (1 + col / 8); X break; X X case EOF: X if (interrupted) return; X if (!replaying || !logdigested) { playing = 0; return; } X saynow ("End of game log, type 'Q' to exit."); X return; X break; X X case UP_TOK: X row--; X break; X X default: X if (ch < ' ') X { saynow ("Unknown character '\\%o'--more--", ch); X waitforspace (); X } X else if (row) X { at (row, col); X if (!emacs && !terse) addch (ch); X if (row == 23) botprinted = 1; X else updatepos (ch, row, col); X } X else if (col == 0) X { screen00 = screen[0][0]; } X else if (col == 1 && ch == 'l' && screen[0][0] == 'I') X { screen[0][0] = screen00; X if (screen00 != ' ') terpmes (); X screen[0][0] = 'I'; X } X screen[row][col++] = ch; X break; X } X } X X if (botprinted) terpbot (); X X if (atrow != atrow0 || atcol != atcol0) X { updateat (); /* Changed position, record the move */ X moved = 1; /* Indicate that we moved */ X wakemonster (8); /* Wake up adjacent mean monsters */ X currentrectangle(); /* Keep current rectangle up to date. LGCH */ X } X X if (!usesynch && !pending ()) X { usesynch = 1; X lastobj = NONE; X resetinv(); X } X X if (version < RV53A && checkrange && !pending ()) X { command (T_OTHER, "Iz"); checkrange = 0; } X X donemonster (); /* LGCH */ X X /* If mapping status has changed */ X if (wasmapped != didreadmap) X { dwait (D_CONTROL | D_SEARCH, "wasmapped: %d didreadmap: %d", X wasmapped, didreadmap); X X mapinfer (); X } X X if (didreadmap != Level) X { doors = doorlist; X while (doors != newdoors) X { r = *doors++; c = *doors++; X dwait (D_INFORM, "new door at %d, %d", r, c); X inferhall (r, c); X } X } X X if (!blinded) X for (i = atrow-1; i <= atrow+1; i++) /* For blanks around the */ X for (j = atcol-1; j <= atcol+1; j++) /* rogue... */ X if (seerc(' ',i,j) && onrc(CANGO,i,j)) /* CANGO+BLANK impossible */ X { unsetrc (CANGO | SAFE, i, j); /* Infer cant go and... */ X setnewgoal (); /* invalidate the map. */ X } X X at (row, col); X if (!emacs && !terse) refresh (); X} X X/* X * terpbot: Read the Rogue status line and set the various status X * variables. This routine depends on the value of version to decide what X * the status line looks like. X */ X Xterpbot () X{ char sstr[30], modeline[256]; X int oldlev = Level, oldgold = Gold, oldhp = Hp, Str18 = 0; X extern int geneid; X register int i, oldstr = Str, oldAc = Ac, oldExp = Explev; X X /* Since we use scanf to read this field, it must not be left blank */ X if (screen[23][78] == ' ') screen[23][78] = 'X'; X X /* Read the bottom line, there are three versions of the status line */ X if (version < RV52A) /* Rogue 3.6, Rogue 4.7? */ X { sscanf (screen[23], X " Level: %d Gold: %d Hp: %d(%d) Str: %s Ac: %d Exp: %d/%d %s", X &Level, &Gold, &Hp, &Hpmax, sstr, &Ac, &Explev, &Exp, Ms); X sscanf (sstr, "%d/%d", &Str, &Str18); X Str = Str * 100 + Str18; X if (Str > Strmax) Strmax = Str; X } X else if (version < RV53A) /* Rogue 5.2 (versions A and B) */ X { sscanf (screen[23], X " Level: %d Gold: %d Hp: %d(%d) Str: %d(%d) Ac: %d Exp: %d/%d %s", X &Level, &Gold, &Hp, &Hpmax, &Str, &Strmax, &Ac, &Explev, &Exp, Ms); X X Str = Str * 100; Strmax = Strmax * 100; X } X else /* Rogue 5.3 (and beyond???) */ X { sscanf (screen[23], X " Level: %d Gold: %d Hp: %d(%d) Str: %d(%d) Arm: %d Exp: %d/%d %s", X &Level, &Gold, &Hp, &Hpmax, &Str, &Strmax, &Ac, &Explev, &Exp, Ms); X X Str = Str * 100; Strmax = Strmax * 100; Ac = 10 - Ac; X } X X /* Monitor changes in some variables */ X if (screen[23][78] == 'X') screen[23][78] = ' '; /* Restore blank */ X if (oldlev != Level) newlevel (); X if (Level > MaxLevel) MaxLevel = Level; X if (oldgold < Gold) deletestuff (atrow, atcol); X if (oldhp < Hp) newring = 1; X X lastdamage = max (0, oldhp - Hp); X X /* X * Insert code here to monitor changes in attributes due to special X * attacks MLM October 26, 1983. X */ X X setbonuses (); X X /* X * If in special output modes, generate output line X */ X X if ((oldlev != Level || oldgold != Gold || oldstr != Str || X oldAc != Ac || oldExp != Explev)) X { X /* Stuff the new values into the argument space (for ps command) */ X sprintf (modeline, "Rgm %d: Id%d L%d %d %d(%d) s%d a%d e%d ", X rogpid, geneid, Level, Gold, Hp, Hpmax, Str / 100, 10-Ac, Explev); X modeline[arglen-1] = '\0'; X strcpy (parmstr, modeline); X X /* Handle Emacs and Terse mode */ X if (emacs || terse) X { /* Skip backward over blanks and nulls */ X for (i = 79; screen[23][i] == ' ' || screen[23][i] == '\0'; i--); X screen[23][++i] = '\0'; X X if (emacs) X { sprintf (modeline, " %s (%%b)", screen[23]); X if (strlen (modeline) > 72) sprintf (modeline, " %s", screen[23]); X fprintf (realstdout, "%s", modeline); X fflush (realstdout); X } X else if (terse && oldlev != Level) X { fprintf (realstdout, "%s\n", screen[23]); X fflush (realstdout); X } X } X } X} X X/* X * dumpwalls: Dump the current screen map X */ X Xdumpwalls () X{ register int r, c, S; X char ch; X X printexplored (); X X for (r = 1; r < 23; r++) X { for (c = 0; c < 80; c++) X { S=scrmap[r][c]; X ch = (ARROW&S) ? 'a' : X (TELTRAP&S) ? 't' : X (TRAPDOR&S) ? 'v' : X (GASTRAP&S) ? 'g' : X (BEARTRP&S) ? 'b' : X (DARTRAP&S) ? 's' : X (WATERAP&S) ? 'w' : X (TRAP&S) ? '^' : X (STAIRS&S) ? '>' : X (RUNOK&S) ? '%' : X ((DOOR+BEEN&S)==DOOR+BEEN) ? 'D' : X (DOOR&S) ? 'd' : X ((BOUNDARY+BEEN&S)==BOUNDARY+BEEN) ? 'B' : X ((ROOM+BEEN&S)==ROOM+BEEN) ? 'R' : X (BEEN&S) ? ':' : X (HALL&S) ? '#' : X ((BOUNDARY+WALL&S)==BOUNDARY+WALL) ? 'W' : X (BOUNDARY&S) ? 'b' : X (ROOM&S) ? 'r' : X (CANGO&S) ? '.' : X (WALL&S) ? 'W' : X (S) ? 'X' : '\0'; X if (ch) mvaddch (r, c, ch); X } X } X X at (row, col); X} X X/* X * sendnow: Send a string to the Rogue process. X */ X Xsendnow (f, a1, a2, a3, a4) Xchar *f; Xint a1, a2, a3, a4; X{ char cmd[128]; X register char *s = cmd; X X sprintf (cmd, f, a1, a2, a3, a4); X X while (*s) sendcnow (*s++); X} X X/* X * sendcnow: send a character to the Rogue process. This routine also does X * the logging of characters in echo mode. X */ X Xsendcnow (c) Xchar c; X{ if (replaying) return; X if (logging) X { if (cecho) X { fprintf (fecho, "\nC: \"%c", c); cecho = !cecho; } X else X fprintf (fecho, "%c", c); X } X fprintf (trogue, "%c", c); X} X X/* X * send: add a string to the queue of commands to be sent to Rogue. The X * commands are sent one at a time by the resend routine. X */ X X# define bump(p,sizeq) (p)=((p)+1)%sizeq X Xsend (f, a1, a2, a3, a4) Xchar *f; Xint a1, a2, a3, a4; X{ char cmd[128]; X register char *s = cmd; X X sprintf (s, f, a1, a2, a3, a4); X X for (; *s; bump (tail, SENDQ)) X queue[tail] = *(s++); X X /* Appends null, so resend will treat as a unit */ X queue[tail] = '\0'; X bump (tail, SENDQ); X} X X/* X * resend: Send next block of characters from the queue X */ X Xresend () X{ register char *l=lastcmd; /* Ptr into last command */ X X morecount = 0; /* Clear message count */ X if (head == tail) return (0); /* Fail if no commands */ X X /* Send all queued characters until the next queued NULL */ X while (queue[head]) X { sendcnow (*l++ = queue[head]); bump (head, SENDQ); } X bump (head, SENDQ); X *l = '\0'; X X return (1); /* Return success */ X} X X/* X * pending: Return true if there is a command in the queue to be sent to X * Rogue. X */ X Xpending () X{ return (head != tail); X} X X/* X * getroguetoken: get a command from Rogue (either a character or a X * cursor motion sequence). X */ X Xchar getroguetoken () X{ char ch; X char getlogtoken(); X X if (replaying) X return (getlogtoken()); X X ch = GETROGUECHAR; X X /* Convert escape sequences into tokens (negative numbers) */ X if (ch == ESC) X { switch (ch = GETROGUECHAR) X X { case CE_CHR: ch = CE_TOK; break; X case CL_CHR: ch = CL_TOK; break; X case CM_CHR: ch = CM_TOK; break; X case DO_CHR: ch = DO_TOK; break; X case ND_CHR: ch = ND_TOK; break; X case SE_CHR: ch = SE_TOK; break; X case SO_CHR: ch = SO_TOK; break; X case UP_CHR: ch = UP_TOK; break; X default: saynow ("Unknown sequence ESC-%s --More--", unctrl(ch)); X waitforspace (); X ch = ER_TOK; X } X } X X /* Get arguments for cursor addressing */ X if ((int) ch == CM_TOK) X { row = (int) GETROGUECHAR - 32; col = (int) GETROGUECHAR - 32; } X X /* Log the tokens */ X if (logging) X { if (!cecho) { fprintf (fecho, "\"\nR: "); cecho = !cecho; } X if (ISPRT (ch)) fprintf (fecho, "%c", ch); X else switch (ch) X { case BS_TOK: fprintf (fecho, "{bs}"); break; X case CE_TOK: fprintf (fecho, "{ce}"); break; X case CL_TOK: fprintf (fecho, "{ff}"); break; X case CM_TOK: fprintf (fecho, "{cm(%d,%d)}", row, col); break; X case CR_TOK: fprintf (fecho, "{cr}"); break; X case DO_TOK: fprintf (fecho, "{do}"); break; X case LF_TOK: fprintf (fecho, "{nl}"); break; X case ND_TOK: fprintf (fecho, "{nd}"); break; X case SE_TOK: fprintf (fecho, "{se}"); break; X case SO_TOK: fprintf (fecho, "{so}"); break; X case TA_TOK: fprintf (fecho, "{ta}"); break; X case UP_TOK: fprintf (fecho, "{up}"); break; X case ER_TOK: fprintf (fecho, "{ERRESC}", ch); break; X default: fprintf (fecho, "{ERR%o}", ch); X ch = ER_TOK; X } X fflush (fecho); X } X X return (ch); X} X X/* X * at: move the cursor. Now just a call to move(); X */ X Xat (r, c) Xint r, c; X{ move (r, c); X} X X/* X * deadrogue: Called when we have been killed, it reads the tombstone X * to see how much we had when we died and who killed us. It then X * calls quitrogue to handle the termination handshaking and log the X * game. X */ X X# define GOLDROW 15 X# define KILLROW 17 X# define TOMBCOL 19 X Xdeadrogue () X{ int mh; X char *killer, *killend; X X printw ("\n\nOops..."); X refresh (); X X sscanf (&screen[GOLDROW][TOMBCOL], "%18d", &Gold); X X killer = &screen[KILLROW][TOMBCOL]; X killend = killer+17; X while (*killer==' ') ++killer; X while (*killend==' ') *(killend--) = '\0'; X X /* Record the death blow if killed by a monster */ X if ((mh = findmonster (killer)) != NONE) X { addprob (&monhist[mh].theyhit, SUCCESS); X addstat (&monhist[mh].damage, Hp); X } X X quitrogue (killer, Gold, DIED); X} X X/* X * quitrogue: we are going to quit. Log the game and send a \n to X * the Rogue process, then wait for it to die before returning. X */ X Xquitrogue (reason, gold, terminationtype) Xchar *reason; /* A reason string for the summary line */ Xint gold; /* What is the final score */ Xint terminationtype; /* SAVED, FINSISHED, or DIED */ X{ struct tm *localtime(), *ts; X long clock; X char *k, *r; X X /* Save the killer and score */ X for (k=ourkiller, r=reason; *r && *r != ' '; ++k, ++r) *k = *r; X *k = '\0'; X ourscore = gold; X X /* Dont need to make up any more commands */ X if (!replaying || !logdigested) X playing = 0; X X /* Now get the current time, so we can date the score */ X clock = time(&clock); X ts = localtime(&clock); X X /* Build a summary line */ X sprintf (sumline, "%3s %2d, %4d %-8.8s %7d%s%-17.17s %3d %3d ", X month[ts -> tm_mon], ts -> tm_mday, 1900 + ts -> tm_year, X getname (), gold, cheat ? "*" : " ", reason, MaxLevel, Hpmax); X X if (Str % 100) X sprintf (sumline, "%s%2d.%2d", sumline, Str/100, Str%100); X else X sprintf (sumline, "%s %2d ", sumline, Str/100); X X sprintf (sumline, "%s %2d %2d/%-6d %d", X sumline, Ac, Explev, Exp, ltm.gamecnt); X X /* Now write the summary line to the log file */ X at (23, 0); clrtoeol (); refresh (); X X /* 22 is index of score in sumline */ X if (!replaying) X add_score (sumline, versionstr, (terse || emacs || noterm)); X X /* Restore interrupt status */ X reset_int (); X X /* Set the termination message based on the termination method */ X if (stlmatch (reason, "total winner")) X termination = "victorius"; X else if (stlmatch (reason, "user typing quit")) X termination = "abortivus"; X else if (stlmatch (reason, "gave up")) X termination = "inops consilii"; X else if (stlmatch (reason, "quit (scoreboard)")) X termination = "callidus"; X else if (stlmatch (reason, "saved")) X termination = "suspendus"; X X /* Send the requisite handshaking to Rogue */ X if (terminationtype == DIED) X sendnow ("\n"); X else if (terminationtype == FINISHED) X sendnow ("Qy\n"); X else X sendnow ("Syy"); /* Must send two yesses, R5.2 MLM */ X X /* Wait for Rogue to die */ X wait (0); X} X X/* X * waitfor: snarf characters from Rogue until a string is found. X * The characters are echoed to the users screen. X * X * The string must not contain a valid prefix of itself X * internally. X * X * MLM 8/27/82 X */ X Xwaitfor (mess) Xchar *mess; X{ register char *m = mess; X X while (*m) X { if (getroguetoken () == *m) m++; X else m = mess; X } X} X X/* X * say: Display a messsage on the top line. Restore cursor to Rogue. X */ X Xsay (s, a1, a2, a3, a4, a5, a6, a7, a8) Xchar *s; Xint a1, a2, a3, a4, a5, a6, a7, a8; X{ char buf[BUFSIZ], *b; X X if (!emacs && !terse) X { sprintf (buf, s, a1, a2, a3, a4, a5, a6, a7, a8); X at (0,0); X for (b=buf; *b; b++) printw ("%s", unctrl (*b)); X clrtoeol (); X at (row, col); X } X} X X/* X * saynow: Display a messsage on the top line. Restore cursor to Rogue, X * and refresh the screen. X */ X Xsaynow (s, a1, a2, a3, a4, a5, a6, a7, a8) Xchar *s; Xint a1, a2, a3, a4, a5, a6, a7, a8; X{ if (!emacs && !terse) X { say (s, a1, a2, a3, a4, a5, a6, a7, a8); X refresh (); X } X} X X/* X * waitforspace: Wait for the user to type a space. X * Be sure to interpret a snapshot command, if given. X */ X Xwaitforspace () X{ char ch; X X refresh (); X X if (!noterm) X while ((ch = fgetc (stdin)) != ' ') X if (ch == '/') dosnapshot (); X X at (row, col); X} X X/* X * givehelp: Each time a ? is pressed, this routine prints the next X * help message in a sequence of help messages. Nexthelp is an X */ X Xchar *nexthelp[] = X{ "Rgm commands: t=toggle run mode, e=logging, i=inventory, -=status [?]", X "Rgm commands: =singlestep, `=summary, /=snapshot, R=replay [?]", X "Rogue cmds: S=Save, Q=Quit, h j k l H J K L b n u y N B U Y f s < > [?]", X "Wizard: d=debug, !=show items, @=show monsters, #=show level flags [?]", X "Wizard: ~=version, ^=bowrank, %%=armorrank, $=weaponrank, ==ringrank [?]", X "Wizard: (=database, )=cycles, +=possible secret doors, :=chicken [?]", X "Wizard: [=weapstat, r=resetinv, &=object count, *=toggle blind [?]", X "Wizard: C=toggle cosmic, M=mazedoor, m=monster, A=attempt, {=flags", X NULL X}; X Xchar **helpline = nexthelp; X Xgivehelp () X{ X if (*helpline == NULL) helpline = nexthelp; X saynow (*helpline++); X} X X/* X * pauserogue: Wait for the user to type a space and then redraw the X * screen. Now uses the stored image and passes it to X * curses rather than sending a form feed to Rogue. MLM X */ X Xpauserogue () X{ X at (23, 0); X addstr ("--press space to continue--"); X clrtoeol (); X refresh (); X X waitforspace (); X X redrawscreen (); X} X X/* X * getrogver: Read the output of the Rogue version command X * and set version. RV36B = 362 (3.6 with wands) X * and RV52A = 521 (5.2). Note that RV36A is X * infered when we send a "//" command to identify X * wands. X */ X Xgetrogver () X{ char *vstr = versionstr, ch; X X if (replaying) /* Use default version */ X { sprintf (versionstr, DEFVER); } X X else /* Execute the version command */ X { sendnow ("v"); X waitfor ("ersion "); X X while ((ch = getroguetoken ()) != ' ') *(vstr++) = ch; X *--vstr = '\0'; X } X X if (stlmatch (versionstr, "3.6")) version = RV36B; X else if (stlmatch (versionstr, "5.2")) version = RV52A; X else if (stlmatch (versionstr, "5.3")) version = RV53A; X else saynow ("What a strange version of Rogue! "); X} X X/* X * charsavail: How many characters are there at the terminal? If any X * characters are found, 'noterm' is reset, since there is obviously X * a terminal around if the user is typing at us. X */ X Xcharsavail () X{ long n; X int retc; X X if (retc = ioctl (READ, FIONREAD, &n)) X { saynow ("Ioctl returns %d, n=%ld.\n", retc, n); X n=0; X } X X if (n > 0) noterm = 0; X return ((int) n); X} X X/* X * redrawscreen: Make the users screen look like the Rogue screen (screen). X */ X Xredrawscreen () X{ register int i, j; X char ch; X X clear (); X X for (i = 1; i < 24; i++) for (j = 0; j < 80; j++) X if ((ch = screen[i][j]) > ' ') mvaddch(i, j, ch); X X at (row, col); X X refresh (); X} X X/* X * toggleecho: toggle the I/O echo feature. If first time, open the X * roguelog file. X */ X Xtoggleecho () X{ if (replaying) return; X logging = !logging; X if (logging) X { if ((fecho = wopen (ROGUELOG, "w")) == NULL) X { logging = !logging; X saynow ("can't open %s", ROGUELOG); X } X else X { fprintf (fecho, "Rogomatic Game Log\n\n"); X saynow ("Logging to file %s", ROGUELOG); X cecho = 1; X } X } X else X { if (cecho) X fprintf (fecho, "\n"); X else X fprintf (fecho, "\"\n"); X fclose (fecho); X X if (playing) saynow ("File %s closed", ROGUELOG); X } X if (playing) X { at (row, col); refresh (); } X} X X/* X * clearsendqueue: Throw away queued Rogue commands. X */ X Xclearsendqueue () X{ head = tail; X} X X/* X * startreplay: Open the log file to replay. X */ X Xstartreplay (logfile, logfilename) XFILE **logfile; Xchar *logfilename; X{ if ((*logfile = fopen (logfilename, "r")) == NULL) X { fprintf (stderr, "Can't open '%s'.\n", logfilename); X exit(1); X } X} X X/* X * putn: Put 'n' copies of character 'c' on file 'f'. X */ X Xputn (c, f, n) Xregister char c; Xregister FILE *f; Xregister int n; X{ X while (n--) X putc (c, f); X} X X/* X * printsnap: print a snapshot to file f. X */ X Xprintsnap (f) XFILE *f; X{ register int i, j, length; X struct tm *localtime(), *ts; X char *statusline(); X long clock; X X /* Now get the current time, so we can date the snapshot */ X clock = time(&clock); X ts = localtime(&clock); X X /* Print snapshot timestamp */ X fprintf (f, "\nSnapshot taken on %s %d, %d at %02d:%02d:%02d:\n\n", X month[ts -> tm_mon], ts -> tm_mday, 1900 + ts -> tm_year, X ts -> tm_hour, ts -> tm_min, ts -> tm_sec); X X /* Print the current map */ X putn ('-', f, 79); X fprintf (f, "\n"); X for (i = 0; i < 24; i++) X { for (length = 79; length >= 0 && charonscreen(i,length) == ' '; length--); X for (j=0; j <= length; j++) fprintf (f, "%c", charonscreen(i,j)); X fprintf (f, "\n"); X } X putn ('-', f, 79); X X /* Print status variables */ X fprintf (f, "\n\n%s\n\n", statusline ()); X X /* Print the inventory */ X X dumpinv (f); X fprintf (f, "\n"); X putn ('-', f, 79); X fprintf (f, "\n"); X} X X/* X * getlogtoken: routine to retrieve a rogue token from the log file. X * This allows us to replay a game with all the diagnostic commands of X * Rog-O-Matic at our disposal. LGCH. X */ X Xchar getlogtoken() X{ int acceptline; X char ch = GETLOGCHAR; X char ch1, ch2, dig; X X while (ch == NEWLINE) X { acceptline = 0; X if ((ch = GETLOGCHAR) == 'R') X if ((ch = GETLOGCHAR) == ':') X if ((ch = GETLOGCHAR) == ' ') X { ch = GETLOGCHAR; X acceptline = 1; X } X if (!acceptline) X while ((int) ch != NEWLINE && (int) ch != EOF) X ch = GETLOGCHAR; X } X X if (ch == '{') X { ch1 = GETLOGCHAR; X ch2 = GETLOGCHAR; X ch = GETLOGCHAR; /* Ignore the closing '}' */ X switch (ch1) X { case 'b': ch = BS_TOK; break; X case 'c': X switch (ch2) X { case 'e': ch = CE_TOK; break; X case 'm': X ch = CM_TOK; X row = 0; X while ((dig = GETLOGCHAR) != ',') X { row = row * 10 + dig - '0'; X } X col = 0; X while ((dig = GETLOGCHAR) != ')') X { col = col * 10 + dig - '0'; } X GETLOGCHAR; /* Ignore '}' */ X break; X case 'r': ch = CR_TOK; X } X break; X case 'd': ch = DO_TOK; break; X case 'f': ch = CL_TOK; break; X X case 'n': X if (ch2 == 'l') X ch = LF_TOK; X else X ch = ND_TOK; X break; X case 's': X if (ch2 == 'e') X ch = SE_TOK; X else X ch = SO_TOK; X break; X case 't': ch = TA_TOK; break; X case 'u': ch = UP_TOK; break; X case 'E': X while (GETLOGCHAR != '}') X ; X ch = ER_TOK; X break; X } X } X return (ch); X} X X/* X * getoldcommand: retrieve the old command from a logfile we are replaying. X */ X Xgetoldcommand (s) Xregister char *s; X{ register int charcount = 0; X char ch = ' ', term = '"', *startpat = "\nC: "; X X while (*startpat && (int) ch != EOF) X { if ((ch = GETLOGCHAR) != *(startpat++)) startpat = "\nC: "; } X X if ((int) ch != EOF) X { term = ch = GETLOGCHAR; X while ((ch = GETLOGCHAR) != term && (int) ch != EOF && charcount++ < 128) X { *(s++) = ch; X } X } X X *s = '\0'; X} X X/* X * dosnapshot: add a snapshot to the SHAPSHOT file. X */ X Xdosnapshot () X{ X if ((snapshot = wopen (SNAPSHOT, "a")) == NULL) X saynow ("Cannot write file %s.", SNAPSHOT); X else X { printsnap (snapshot); X fclose (snapshot); X saynow ("Snapshot added to %s.", SNAPSHOT); X } X} X X/* X * clearscreen: Done whenever a {ff} is sent by Rogue. This code is X * separate so it can be called from replay(), since there is an implicit X * formfeed not recorded in the log file. MLM X */ X Xclearscreen () X{ register int i, j; X X row = col = 0; X clear (); X screen00 = ' '; X for (i = 0; i < 24; i++) X for (j = 0; j < 80; j++) X { screen[i][j] = ' '; X unsetrc (STUFF, i, j); X } X initstufflist (); X mlistlen = 0; /* initmonsterlist (); temp hack MLM */ X} X X/* X * statusline: Write all about our current status into a string. X * Returns a pointer to a static area. MLM X */ X Xchar * Xstatusline () X{ static char staticarea[256]; X register char *s=staticarea; X X sprintf (s, "Status: "); X X if (aggravated) strcat (s, "aggravated, "); X if (beingheld) strcat (s, "being held, "); X if (blinded) strcat (s, "blind, "); X if (confused) strcat (s, "confused, "); X if (cosmic) strcat (s, "cosmic, "); X if (cursedarmor) strcat (s, "cursed armor, "); X if (cursedweapon) strcat (s, "cursed weapon, "); X if (doublehasted) strcat (s, "perm hasted, "); X if (droppedscare) strcat (s, "dropped scare, "); X if (floating) strcat (s, "floating, "); X if (hasted) strcat (s, "hasted, "); X if (protected) strcat (s, "protected, "); X if (redhands) strcat (s, "red hands, "); X if (Level == didreadmap) strcat (s, "mapped, "); X X if (*genocided) sprintf (s, "genocided '%s', ", s, genocided); X X sprintf (s, "%s%d food%s, %d missile%s, %d turn%s, (%d,%d %d,%d) bonus", X s, larder, plural(larder), ammo, plural(ammo), turns, X plural(turns), gplushit, gplusdam, wplushit, wplusdam); X X return (s); X} / echo 'x - pack.c' sed 's/^X//' > pack.c << '/' X/* X * pack.c: Rog-O-Matic XIV (CMU) Thu Jan 31 15:04:23 1985 - mlm X * Copyright (C) 1985 by A. Appel, G. Jacobson, L. Hamey, and M. Mauldin X * X * This file contains functions which mess with Rog-O-Matics pack X */ X X# include X# include "types.h" X# include "globals.h" X Xstatic char *stuffmess [] = { X "strange object", "food", "potion", "scroll", X "wand", "ring", "hitter", "thrower", X "missile", "armor", "amulet", "gold", X "none" }; X X/* X * itemstr: print the inventory message for a single item. X */ X Xchar *itemstr (i) Xregister int i; X{ static char ispace[128]; X register char *item = ispace; X X if (i < 0 || i >= MAXINV) X { sprintf (item, "%d out of bounds", i); } X else if (inven[i].count < 1) X { sprintf (item, "%c) nothing", LETTER(i)); } X else X { sprintf (item, "%c) %4d %d*%s:", LETTER(i), worth(i), X inven[i].count, stuffmess[(int)inven[i].type]); X X if (inven[i].phit != UNKNOWN && inven[i].pdam == UNKNOWN) X sprintf (item, "%s (%d)", item, inven[i].phit); X else if (inven[i].phit != UNKNOWN) X sprintf (item, "%s (%d,%d)", item, inven[i].phit, inven[i].pdam); X X if (inven[i].charges != UNKNOWN) X sprintf (item, "%s [%d]", item, inven[i].charges); X X sprintf (item, "%s %s%s%s%s%s%s%s%s%s.", /* DR UTexas */ X item, inven[i].str, X (itemis (i, KNOWN) ? "" : ", unknown"), X (used (inven[i].str) ? ", tried" : ""), X (itemis (i, CURSED) ? ", cursed" : ""), X (itemis (i, UNCURSED) ? ", uncursed" : ""), X (itemis (i, ENCHANTED) ? ", enchanted" : ""), X (itemis (i, PROTECTED) ? ", protected" : ""), X (itemis (i, WORTHLESS) ? ", useless" : ""), X (!itemis (i, INUSE) ? "" : X (inven[i].type == armor || inven[i].type == ring) ? X ", being worn" : ", in hand")); X } X X return (item); X} X X/* X * dumpinv: print the inventory. calls itemstr. X */ X Xdumpinv (f) Xregister FILE *f; X{ register int i; X X if (f == NULL) X at (1,0); X X for (i=0; i= MAXINV) return; X inven[pos].count = 0; X inven[pos].str[0] = '\0'; X inven[pos].phit = UNKNOWN; X inven[pos].pdam = UNKNOWN; X inven[pos].charges = UNKNOWN; X forget (pos, (KNOWN | CURSED | ENCHANTED | PROTECTED | UNCURSED | X INUSE | WORTHLESS)); X X} X X/* X * rollpackup: We have deleted an item, move up the objects behind it in X * the pack. X */ X Xrollpackup (pos) Xregister int pos; X{ register char *savebuf; X register int i; X X if (version >= RV53A) return; X X if (pos < currentarmor) currentarmor--; X else if (pos == currentarmor) currentarmor = NONE; X X if (pos < currentweapon) currentweapon--; X else if (pos == currentweapon) currentweapon = NONE; X X if (pos < leftring) leftring--; X else if (pos == leftring) leftring = NONE; X X if (pos < rightring) rightring--; X else if (pos == rightring) rightring = NONE; X X savebuf = inven[pos].str; X for (i=pos; i+1= RV53A) return; X X savebuf = inven[invcount].str; X for (i=invcount; i>pos; --i) X { inven[i] = inven[i-1]; X if (i-1 == currentarmor) currentarmor++; X if (i-1 == currentweapon) currentweapon++; X if (i-1 == leftring) leftring++; X if (i-1 == rightring) rightring++; X } X inven[pos].str = savebuf; X X if (++invcount > MAXINV) X usesynch = 0; X} X X/* X * resetinv: send an inventory command. The actual work is done by X * doresetinv, which is called by a demon in the command handler. X */ X Xresetinv() X{ X if (!replaying) command (T_OTHER, "i"); X} X X/* X * doresetinv: reset the inventory. DR UTexas 01/05/84 X */ X Xdoresetinv () X{ int i; X static char space[MAXINV][80]; X X usesynch = 1; X checkrange = 0; X X for(i=0; i= RV53A) invcount = MAXINV; X} X X/* X * inventory: parse an item message. X */ X X# define xtr(w,b,e,k) {what=(w);xbeg=mess+(b);xend=mend-(e);xknow|=(k);} X Xinventory (msgstart, msgend) Xchar *msgstart, *msgend; X{ register char *p, *q, *mess = msgstart, *mend = msgend; X char objname[100], *realname(); X int n, ipos, xknow = 0, newitem = 0, inuse = 0; X int plushit = UNKNOWN, plusdam = UNKNOWN, charges = UNKNOWN; X stuff what; X char *xbeg, *xend; X X xbeg = xend = ""; X dwait (D_PACK, "inventory: message %s", mess); X X /* Rip surrounding garbage from the message */ X X if (mess[1] == ')') X { ipos= DIGIT(*mess); mess+=3;} X else X { ipos= DIGIT(mend[-2]); mend -= 4; X deletestuff (atrow, atcol); X unsetrc (USELESS, atrow, atcol); X newitem = 1; } X X if (ISDIGIT(*mess)) X { n=atoi(mess); mess += 2+(n>9); } X else X { n=1; X if (*mess == 'a') mess++; /* Eat the determiner A/An/The */ X if (*mess == 'n') mess++; X if (*mess == 't') mess++; X if (*mess == 'h') mess++; X if (*mess == 'e') mess++; X if (*mess == ' ') mess++; } /* Eat the space after the determiner */ X X /* Read the plus to hit */ X if (*mess=='+' || *mess=='-') X { plushit = atoi(mess++); X while (ISDIGIT (*mess)) mess++; X xknow = KNOWN;} X X /* Eat any comma separating two modifiers */ X if (*mess==',') mess++; X X /* Read the plus damage */ X if (*mess=='+' || *mess=='-') X { plusdam = atoi(mess++); X while (ISDIGIT (*mess)) mess++; X xknow = KNOWN;} X X while (*mess==' ') mess++; /* Eat any separating spaces */ X while (mend[-1]==' ') mend--; /* Remove trailing blanks */ X while (mend[-1]=='.') mend--; /* Remove trailing periods */ X X /* Read any parenthesized strings at the end of the message */ X while (mend[-1]==')') X { while (*--mend != '(') ; /* on exit mend -> '(' */ X if (stlmatch(mend,"(being worn)") ) X { currentarmor = ipos; inuse = INUSE; } X else if (stlmatch(mend,"(weapon in hand)") ) X { currentweapon = ipos; inuse = INUSE; } X else if (stlmatch(mend,"(on left hand)") ) X { leftring = ipos; inuse = INUSE; } X else if (stlmatch(mend,"(on right hand)") ) X { rightring = ipos; inuse = INUSE; } X X while (mend[-1]==' ') mend--; X } X X /* Read the charges on a wand (or armor class or ring bonus) */ X if (mend[-1] == ']') X { while (*--mend != '['); /* on exit mend -> '[' */ X if (mend[1] == '+') charges = atoi(mend+2); X else charges = atoi(mend+1); X xknow = KNOWN; X } X X /* Undo plurals by removing trailing 's' */ X while (mend[-1] == ' ') mend--; X if (mend[-1]=='s') mend--; X X /* Now find what we have picked up: */ X if (stlmatch(mend-4,"food")) {what=food;xknow=KNOWN;} X else if (stlmatch(mess,"amulet")) xtr(amulet,0,0,KNOWN) X else if (stlmatch(mess,"potion of ")) xtr(potion,10,0,KNOWN) X else if (stlmatch(mess,"potions of ")) xtr(potion,11,0,KNOWN) X else if (stlmatch(mess,"scroll of ")) xtr(scroll,10,0,KNOWN) X else if (stlmatch(mess,"scrolls of ")) xtr(scroll,11,0,KNOWN) X else if (stlmatch(mess,"staff of ")) xtr(wand,9,0,KNOWN) X else if (stlmatch(mess,"wand of ")) xtr(wand,8,0,KNOWN) X else if (stlmatch(mess,"ring of ")) xtr(ring,8,0,KNOWN) X else if (stlmatch(mend-4,"mail")) xtr(armor,0,0,0) X else if (stlmatch(mend-6,"potion")) xtr(potion,0,7,0) X else if (stlmatch(mess,"scroll titled '")) xtr(scroll,15,1,0) X else if (stlmatch(mess,"scrolls titled '")) xtr(scroll,16,1,0) X else if (stlmatch(mend-5,"staff")) xtr(wand,0,6,0) X else if (stlmatch(mend-4,"wand")) xtr(wand,0,5,0) X else if (stlmatch(mend-4,"ring")) xtr(ring,0,5,0) X else if (stlmatch(mess,"apricot")) xtr(food,0,0,KNOWN) X else if (stlmatch(mend-5,"sword")) xtr(hitter,0,0,0) X else if (stlmatch(mend-4,"mace")) xtr(hitter,0,0,0) X else if (stlmatch(mend-6,"dagger")) xtr(missile,0,0,0) X else if (stlmatch(mend-5,"spear")) xtr(missile,0,0,0) X else if (stlmatch(mend-5,"armor")) xtr(armor,0,0,0) X else if (stlmatch(mend-3,"arm")) xtr(armor,0,0,0) X else if (stlmatch(mend-3,"bow")) xtr(thrower,0,0,0) X else if (stlmatch(mend-5,"sling")) xtr(thrower,0,0,0) X else if (stlmatch(mend-5,"arrow")) xtr(missile,0,0,0) X else if (stlmatch(mend-4,"dart")) xtr(missile,0,0,0) X else if (stlmatch(mend-4,"rock")) xtr(missile,0,0,0) X else if (stlmatch(mend-4,"bolt")) xtr(missile,0,0,0) X else if (stlmatch(mend-8,"shuriken")) xtr(missile,0,0,0) X else xtr(strange,0,0,0) X X /* Copy the name of the object into a string */ X X for (p = objname, q = xbeg; q < xend; p++, q++) *p = *q; X *p = '\0'; X X dwait (D_PACK, "inv: %s '%s', hit %d, dam %d, chg %d, knw %d", X stuffmess[(int) what], objname, plushit, plusdam, charges, xknow); X X /* Ring bonus is printed differently in Rogue 5.3 */ X if (version >= RV53A && what == ring && charges != UNKNOWN) X { plushit = charges; charges = UNKNOWN; } X X /* If the name of the object matches something in the database, */ X /* slap the real name into the slot and mark it as known */ X X if ((what == potion || what == scroll || what == wand) && !xknow) X { char *dbname = realname (objname); X if (*dbname) X { strcpy (objname, dbname); X xknow = KNOWN; X if (newitem) X { at (0,0); X X if (n == 1) printw ("a "); X else printw ("%d ", n); X X printw ("%s%s of %s (%c)", X what == potion ? "potion" : X what == scroll ? "scroll" : X what == ring ? "ring" : X "wand", X (n == 1) ? "" : "s", X objname, X LETTER(ipos)); X X clrtoeol (); X at (row, col); X refresh (); X } X else X say (msgstart); X } X } X X /* If new item, record the change */ X if (newitem && what == armor) X newarmor = 1; X else if (newitem && what == ring) X newring = 1; X else if (newitem && what == food) X { newring = 1; lastfoodlevel = Level; } X else if (newitem && (what == hitter || what == missile || what == wand)) X newweapon = 1; X X /* If the object is an old object, set its count, else allocate */ X /* a new object and roll the other objects down */ X X if (n > 1 && ipos < invcount && inven[ipos].type == what && X n == inven[ipos].count+1 && X stlmatch(objname, inven[ipos].str) && X inven[ipos].phit == plushit && X inven[ipos].pdam == plusdam) X inven[ipos].count = n; X X /* New item, in older Rogues, open up a spot in the pack */ X else X { if (version < RV53A) rollpackdown (ipos); X X /* X * Use retained info to determine cursed attributes when identifying X * or protected status for armor. DR UTexas 01/05/84 X */ X X if (inven[ipos].type == what && stlmatch (objname, inven[ipos].str)) X { if (xknow != itemis (ipos, KNOWN) && X !itemis (ipos, (UNCURSED | ENCHANTED)) && X ((plushit != UNKNOWN && plushit < 0) || X (plusdam != UNKNOWN && plusdam < 0))) X remember (ipos, CURSED); X if (newitem || what != armor ) forget (ipos, PROTECTED); X } X X inven[ipos].type = what; X inven[ipos].count = n; X inven[ipos].phit = plushit; X inven[ipos].pdam = plusdam; X inven[ipos].charges = charges; X remember (ipos, inuse | xknow); X if (!xknow) ++urocnt; X } X X /* Forget enchanted status if item known. DR UTexas 31 Jan 84 */ X if (itemis (ipos, KNOWN)) forget (ipos, ENCHANTED); X X /* Set the name of the object */ X if (inven[ipos].str != NULL) X strcpy (inven[ipos].str, objname); X else if (!replaying) X dwait (D_ERROR, "terpmess: null inven[%d].str, invcount %d.", X ipos, invcount); X X /* Set cursed attribute for weapon and armor */ X if (cursedarmor && ipos == currentarmor) remember (ipos, CURSED); X if (cursedweapon && ipos == currentweapon) remember (ipos, CURSED); X X /* Keep track of whether we are wielding a trap arrow */ X if (ipos == currentweapon) usingarrow = (what == missile); X X countpack (); X X /* If we picked up a useless thing, note that fact */ X if (newitem && on (USELESS)) remember (ipos, WORTHLESS); X else if (newitem) forget (ipos, WORTHLESS); X X checkrange = 1; X} X X/* X * countpack: Count objects, missiles, and food in the pack. X */ X Xcountpack () X{ register int i, cnt; X X for (objcount=0, larder=0, ammo=0, i=0; i