|
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 i
Length: 29494 (0x7336) Types: TextFile Names: »io.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Rog-O-Matic/io.c«
/* * io.c: Rog-O-Matic XIV (CMU) Thu Jan 31 18:19:29 1985 - mlm * Copyright (C) 1985 by A. Appel, G. Jacobson, L. Hamey, and M. Mauldin * * This file contains all of the functions which deal with the real world. */ # include <curses.h> # include <ctype.h> # include "install.h" # ifdef BSD41 # include <time.h> # else # include <sys/time.h> # endif # include "types.h" # include "globals.h" # include "termtokens.h" # define READ 0 /* * Charonscreen returns the current character on the screen (using * curses(3)). This macro is based on the winch(win) macro. */ # define charonscreen(X,Y) (stdscr->_y[X][Y]) char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char screen00 = ' '; /* Constants */ # define SENDQ 256 /* The command queue */ char queue[SENDQ]; /* To Rogue */ int head = 0, tail = 0; /* * Getrogue: Sensory interface. * * Handle grungy low level terminal I/O. Getrogue reads tokens from the * Rogue process and interprets them, making the screen array an image of * the rogue level. Getrogue returns when the string waitstr has been read * and either the cursor is on the Rogue '@' or some other condition * implies that we have synchronized with Rogue. */ getrogue (waitstr, onat) char *waitstr; /* String to synchronize with */ int onat; /* 0 ==> Wait for waitstr 1 ==> Cursor on @ sufficient 2 ==> [1] + send ';' when ever we eat a --More-- message */ { int botprinted = 0, wasmapped = didreadmap, r, c, pending (); register int i, j; char ch, *s, *m, *q, *d, *call, getroguetoken(); int *doors; static moved = 0; domonster(); /* LGCH */ newdoors = doorlist; /* no new doors found yet */ atrow0 = atrow; atcol0 = atcol; /* Save our current posistion */ s = waitstr; /* FSM to check for the wait msg */ m = "More--"; /* FSM to check for '--More--' */ call = "Call it:"; /* FSM to check for 'Call it:' */ q = "(* for list): "; /* FSM to check for prompt */ d = ")______"; /* FSM to check for tombstone grass */ if (moved) /* If we moved last time, put any */ { sleepmonster (); moved = 0; } /* Old monsters to sleep */ /* While we have not reached the end of the Rogue input, read */ /* characters from Rogue and figure out what they mean. */ while ((*s) || ((!hasted || version != RV36A) && onat && screen[row][col] != '@')) { ch = getroguetoken (); /* If message ends in "(* for list): ", call terpmes */ if (ch == *q) { if (*++q == 0) terpmes (); } else q = "(* for list): "; /* Rogomatic now keys off of the grass under the Tombstone to */ /* detect that it has been killed. This was done because the */ /* "Press return" prompt only happens if there is a score file */ /* Available on that system. Hopefully the grass is the same */ /* in all versions of Rogue! */ if (ch == *d) { if (0 == *++d) { addch (ch); deadrogue (); return;} } else d = ")_______"; /* If the message has a more, strip it off and call terpmes */ if (ch == *m) { if (*++m == 0) { /* More than 50 messages since last command ==> start logging */ if (++morecount > 50 && !logging) { toggleecho (); dwait (D_WARNING, "Started logging --More-- loop."); } /* More than 100 messages since last command ==> infinite loop */ if (++morecount > 100) dwait (D_FATAL, "Caught in --More-- loop."); /* Send a space (and possibly a semicolon) to clear the message */ if (onat == 2) sendnow (" ;"); else sendnow (" "); /* Clear the --More-- of the end of the message */ for (i = col - 7; i < col; screen[0][i++] = ' '); terpmes (); /* Interpret the message */ /* This code gets rid of the "Studded leather arm" bug */ /* But it causes other problems. MLM */ /* sprintf (&screen[0][col - 7], "--More--"); */ } } else m = "More--"; /* If the message is 'Call it:', cancel the request */ if (ch == *call) { if (*++call == 0) { /* Send an escape (and possibly a semicolon) to clear the message */ if (onat == 2) sendnow ("%c;", ESC); else sendnow ("%c", ESC); } } else call = "Call it:"; /* Check to see whether we have read the synchronization string */ if (*s) { if (ch == *s) s++; else s = waitstr; } /* Now figure out what the token means */ switch (ch) { case BS_TOK: col--; break; case CE_TOK: if (row && row < 23) for (i = col; i < 80; i++) { updatepos (' ', row, i); screen[row][i] = ' '; } else for (i = col; i < 80; i++) screen[row][i] = ' '; if (row) { at (row, col); clrtoeol (); } else if (col == 0) screen00 = ' '; break; case CL_TOK: clearscreen (); break; case CM_TOK: screen00 = screen[0][0]; break; case CR_TOK: /* Handle missing '--more--' between inventories MLM 24-Jun-83 */ if (row==0 && screen[0][1]==')' && screen[0][col-1] != '-') terpmes (); col = 0; break; case DO_TOK: row++; break; case ER_TOK: break; case LF_TOK: row++; col = 0; break; case ND_TOK: col++; break; case SE_TOK: revvideo = 0; standend (); break; case SO_TOK: revvideo = 1; standout (); break; case TA_TOK: col = 8 * (1 + col / 8); break; case EOF: if (interrupted) return; if (!replaying || !logdigested) { playing = 0; return; } saynow ("End of game log, type 'Q' to exit."); return; break; case UP_TOK: row--; break; default: if (ch < ' ') { saynow ("Unknown character '\\%o'--more--", ch); waitforspace (); } else if (row) { at (row, col); if (!emacs && !terse) addch (ch); if (row == 23) botprinted = 1; else updatepos (ch, row, col); } else if (col == 0) { screen00 = screen[0][0]; } else if (col == 1 && ch == 'l' && screen[0][0] == 'I') { screen[0][0] = screen00; if (screen00 != ' ') terpmes (); screen[0][0] = 'I'; } screen[row][col++] = ch; break; } } if (botprinted) terpbot (); if (atrow != atrow0 || atcol != atcol0) { updateat (); /* Changed position, record the move */ moved = 1; /* Indicate that we moved */ wakemonster (8); /* Wake up adjacent mean monsters */ currentrectangle(); /* Keep current rectangle up to date. LGCH */ } if (!usesynch && !pending ()) { usesynch = 1; lastobj = NONE; resetinv(); } if (version < RV53A && checkrange && !pending ()) { command (T_OTHER, "Iz"); checkrange = 0; } donemonster (); /* LGCH */ /* If mapping status has changed */ if (wasmapped != didreadmap) { dwait (D_CONTROL | D_SEARCH, "wasmapped: %d didreadmap: %d", wasmapped, didreadmap); mapinfer (); } if (didreadmap != Level) { doors = doorlist; while (doors != newdoors) { r = *doors++; c = *doors++; dwait (D_INFORM, "new door at %d, %d", r, c); inferhall (r, c); } } if (!blinded) for (i = atrow-1; i <= atrow+1; i++) /* For blanks around the */ for (j = atcol-1; j <= atcol+1; j++) /* rogue... */ if (seerc(' ',i,j) && onrc(CANGO,i,j)) /* CANGO+BLANK impossible */ { unsetrc (CANGO | SAFE, i, j); /* Infer cant go and... */ setnewgoal (); /* invalidate the map. */ } at (row, col); if (!emacs && !terse) refresh (); } /* * terpbot: Read the Rogue status line and set the various status * variables. This routine depends on the value of version to decide what * the status line looks like. */ terpbot () { char sstr[30], modeline[256]; int oldlev = Level, oldgold = Gold, oldhp = Hp, Str18 = 0; extern int geneid; register int i, oldstr = Str, oldAc = Ac, oldExp = Explev; /* Since we use scanf to read this field, it must not be left blank */ if (screen[23][78] == ' ') screen[23][78] = 'X'; /* Read the bottom line, there are three versions of the status line */ if (version < RV52A) /* Rogue 3.6, Rogue 4.7? */ { sscanf (screen[23], " Level: %d Gold: %d Hp: %d(%d) Str: %s Ac: %d Exp: %d/%d %s", &Level, &Gold, &Hp, &Hpmax, sstr, &Ac, &Explev, &Exp, Ms); sscanf (sstr, "%d/%d", &Str, &Str18); Str = Str * 100 + Str18; if (Str > Strmax) Strmax = Str; } else if (version < RV53A) /* Rogue 5.2 (versions A and B) */ { sscanf (screen[23], " Level: %d Gold: %d Hp: %d(%d) Str: %d(%d) Ac: %d Exp: %d/%d %s", &Level, &Gold, &Hp, &Hpmax, &Str, &Strmax, &Ac, &Explev, &Exp, Ms); Str = Str * 100; Strmax = Strmax * 100; } else /* Rogue 5.3 (and beyond???) */ { sscanf (screen[23], " Level: %d Gold: %d Hp: %d(%d) Str: %d(%d) Arm: %d Exp: %d/%d %s", &Level, &Gold, &Hp, &Hpmax, &Str, &Strmax, &Ac, &Explev, &Exp, Ms); Str = Str * 100; Strmax = Strmax * 100; Ac = 10 - Ac; } /* Monitor changes in some variables */ if (screen[23][78] == 'X') screen[23][78] = ' '; /* Restore blank */ if (oldlev != Level) newlevel (); if (Level > MaxLevel) MaxLevel = Level; if (oldgold < Gold) deletestuff (atrow, atcol); if (oldhp < Hp) newring = 1; lastdamage = max (0, oldhp - Hp); /* * Insert code here to monitor changes in attributes due to special * attacks MLM October 26, 1983. */ setbonuses (); /* * If in special output modes, generate output line */ if ((oldlev != Level || oldgold != Gold || oldstr != Str || oldAc != Ac || oldExp != Explev)) { /* Stuff the new values into the argument space (for ps command) */ sprintf (modeline, "Rgm %d: Id%d L%d %d %d(%d) s%d a%d e%d ", rogpid, geneid, Level, Gold, Hp, Hpmax, Str / 100, 10-Ac, Explev); modeline[arglen-1] = '\0'; strcpy (parmstr, modeline); /* Handle Emacs and Terse mode */ if (emacs || terse) { /* Skip backward over blanks and nulls */ for (i = 79; screen[23][i] == ' ' || screen[23][i] == '\0'; i--); screen[23][++i] = '\0'; if (emacs) { sprintf (modeline, " %s (%%b)", screen[23]); if (strlen (modeline) > 72) sprintf (modeline, " %s", screen[23]); fprintf (realstdout, "%s", modeline); fflush (realstdout); } else if (terse && oldlev != Level) { fprintf (realstdout, "%s\n", screen[23]); fflush (realstdout); } } } } /* * dumpwalls: Dump the current screen map */ dumpwalls () { register int r, c, S; char ch; printexplored (); for (r = 1; r < 23; r++) { for (c = 0; c < 80; c++) { S=scrmap[r][c]; ch = (ARROW&S) ? 'a' : (TELTRAP&S) ? 't' : (TRAPDOR&S) ? 'v' : (GASTRAP&S) ? 'g' : (BEARTRP&S) ? 'b' : (DARTRAP&S) ? 's' : (WATERAP&S) ? 'w' : (TRAP&S) ? '^' : (STAIRS&S) ? '>' : (RUNOK&S) ? '%' : ((DOOR+BEEN&S)==DOOR+BEEN) ? 'D' : (DOOR&S) ? 'd' : ((BOUNDARY+BEEN&S)==BOUNDARY+BEEN) ? 'B' : ((ROOM+BEEN&S)==ROOM+BEEN) ? 'R' : (BEEN&S) ? ':' : (HALL&S) ? '#' : ((BOUNDARY+WALL&S)==BOUNDARY+WALL) ? 'W' : (BOUNDARY&S) ? 'b' : (ROOM&S) ? 'r' : (CANGO&S) ? '.' : (WALL&S) ? 'W' : (S) ? 'X' : '\0'; if (ch) mvaddch (r, c, ch); } } at (row, col); } /* * sendnow: Send a string to the Rogue process. */ sendnow (f, a1, a2, a3, a4) char *f; int a1, a2, a3, a4; { char cmd[128]; register char *s = cmd; sprintf (cmd, f, a1, a2, a3, a4); while (*s) sendcnow (*s++); } /* * sendcnow: send a character to the Rogue process. This routine also does * the logging of characters in echo mode. */ sendcnow (c) char c; { if (replaying) return; if (logging) { if (cecho) { fprintf (fecho, "\nC: \"%c", c); cecho = !cecho; } else fprintf (fecho, "%c", c); } fprintf (trogue, "%c", c); } /* * send: add a string to the queue of commands to be sent to Rogue. The * commands are sent one at a time by the resend routine. */ # define bump(p,sizeq) (p)=((p)+1)%sizeq send (f, a1, a2, a3, a4) char *f; int a1, a2, a3, a4; { char cmd[128]; register char *s = cmd; sprintf (s, f, a1, a2, a3, a4); for (; *s; bump (tail, SENDQ)) queue[tail] = *(s++); /* Appends null, so resend will treat as a unit */ queue[tail] = '\0'; bump (tail, SENDQ); } /* * resend: Send next block of characters from the queue */ resend () { register char *l=lastcmd; /* Ptr into last command */ morecount = 0; /* Clear message count */ if (head == tail) return (0); /* Fail if no commands */ /* Send all queued characters until the next queued NULL */ while (queue[head]) { sendcnow (*l++ = queue[head]); bump (head, SENDQ); } bump (head, SENDQ); *l = '\0'; return (1); /* Return success */ } /* * pending: Return true if there is a command in the queue to be sent to * Rogue. */ pending () { return (head != tail); } /* * getroguetoken: get a command from Rogue (either a character or a * cursor motion sequence). */ char getroguetoken () { char ch; char getlogtoken(); if (replaying) return (getlogtoken()); ch = GETROGUECHAR; /* Convert escape sequences into tokens (negative numbers) */ if (ch == ESC) { switch (ch = GETROGUECHAR) { case CE_CHR: ch = CE_TOK; break; case CL_CHR: ch = CL_TOK; break; case CM_CHR: ch = CM_TOK; break; case DO_CHR: ch = DO_TOK; break; case ND_CHR: ch = ND_TOK; break; case SE_CHR: ch = SE_TOK; break; case SO_CHR: ch = SO_TOK; break; case UP_CHR: ch = UP_TOK; break; default: saynow ("Unknown sequence ESC-%s --More--", unctrl(ch)); waitforspace (); ch = ER_TOK; } } /* Get arguments for cursor addressing */ if ((int) ch == CM_TOK) { row = (int) GETROGUECHAR - 32; col = (int) GETROGUECHAR - 32; } /* Log the tokens */ if (logging) { if (!cecho) { fprintf (fecho, "\"\nR: "); cecho = !cecho; } if (ISPRT (ch)) fprintf (fecho, "%c", ch); else switch (ch) { case BS_TOK: fprintf (fecho, "{bs}"); break; case CE_TOK: fprintf (fecho, "{ce}"); break; case CL_TOK: fprintf (fecho, "{ff}"); break; case CM_TOK: fprintf (fecho, "{cm(%d,%d)}", row, col); break; case CR_TOK: fprintf (fecho, "{cr}"); break; case DO_TOK: fprintf (fecho, "{do}"); break; case LF_TOK: fprintf (fecho, "{nl}"); break; case ND_TOK: fprintf (fecho, "{nd}"); break; case SE_TOK: fprintf (fecho, "{se}"); break; case SO_TOK: fprintf (fecho, "{so}"); break; case TA_TOK: fprintf (fecho, "{ta}"); break; case UP_TOK: fprintf (fecho, "{up}"); break; case ER_TOK: fprintf (fecho, "{ERRESC}", ch); break; default: fprintf (fecho, "{ERR%o}", ch); ch = ER_TOK; } fflush (fecho); } return (ch); } /* * at: move the cursor. Now just a call to move(); */ at (r, c) int r, c; { move (r, c); } /* * deadrogue: Called when we have been killed, it reads the tombstone * to see how much we had when we died and who killed us. It then * calls quitrogue to handle the termination handshaking and log the * game. */ # define GOLDROW 15 # define KILLROW 17 # define TOMBCOL 19 deadrogue () { int mh; char *killer, *killend; printw ("\n\nOops..."); refresh (); sscanf (&screen[GOLDROW][TOMBCOL], "%18d", &Gold); killer = &screen[KILLROW][TOMBCOL]; killend = killer+17; while (*killer==' ') ++killer; while (*killend==' ') *(killend--) = '\0'; /* Record the death blow if killed by a monster */ if ((mh = findmonster (killer)) != NONE) { addprob (&monhist[mh].theyhit, SUCCESS); addstat (&monhist[mh].damage, Hp); } quitrogue (killer, Gold, DIED); } /* * quitrogue: we are going to quit. Log the game and send a \n to * the Rogue process, then wait for it to die before returning. */ quitrogue (reason, gold, terminationtype) char *reason; /* A reason string for the summary line */ int gold; /* What is the final score */ int terminationtype; /* SAVED, FINSISHED, or DIED */ { struct tm *localtime(), *ts; long clock; char *k, *r; /* Save the killer and score */ for (k=ourkiller, r=reason; *r && *r != ' '; ++k, ++r) *k = *r; *k = '\0'; ourscore = gold; /* Dont need to make up any more commands */ if (!replaying || !logdigested) playing = 0; /* Now get the current time, so we can date the score */ clock = time(&clock); ts = localtime(&clock); /* Build a summary line */ sprintf (sumline, "%3s %2d, %4d %-8.8s %7d%s%-17.17s %3d %3d ", month[ts -> tm_mon], ts -> tm_mday, 1900 + ts -> tm_year, getname (), gold, cheat ? "*" : " ", reason, MaxLevel, Hpmax); if (Str % 100) sprintf (sumline, "%s%2d.%2d", sumline, Str/100, Str%100); else sprintf (sumline, "%s %2d ", sumline, Str/100); sprintf (sumline, "%s %2d %2d/%-6d %d", sumline, Ac, Explev, Exp, ltm.gamecnt); /* Now write the summary line to the log file */ at (23, 0); clrtoeol (); refresh (); /* 22 is index of score in sumline */ if (!replaying) add_score (sumline, versionstr, (terse || emacs || noterm)); /* Restore interrupt status */ reset_int (); /* Set the termination message based on the termination method */ if (stlmatch (reason, "total winner")) termination = "victorius"; else if (stlmatch (reason, "user typing quit")) termination = "abortivus"; else if (stlmatch (reason, "gave up")) termination = "inops consilii"; else if (stlmatch (reason, "quit (scoreboard)")) termination = "callidus"; else if (stlmatch (reason, "saved")) termination = "suspendus"; /* Send the requisite handshaking to Rogue */ if (terminationtype == DIED) sendnow ("\n"); else if (terminationtype == FINISHED) sendnow ("Qy\n"); else sendnow ("Syy"); /* Must send two yesses, R5.2 MLM */ /* Wait for Rogue to die */ wait (0); } /* * waitfor: snarf characters from Rogue until a string is found. * The characters are echoed to the users screen. * * The string must not contain a valid prefix of itself * internally. * * MLM 8/27/82 */ waitfor (mess) char *mess; { register char *m = mess; while (*m) { if (getroguetoken () == *m) m++; else m = mess; } } /* * say: Display a messsage on the top line. Restore cursor to Rogue. */ say (s, a1, a2, a3, a4, a5, a6, a7, a8) char *s; int a1, a2, a3, a4, a5, a6, a7, a8; { char buf[BUFSIZ], *b; if (!emacs && !terse) { sprintf (buf, s, a1, a2, a3, a4, a5, a6, a7, a8); at (0,0); for (b=buf; *b; b++) printw ("%s", unctrl (*b)); clrtoeol (); at (row, col); } } /* * saynow: Display a messsage on the top line. Restore cursor to Rogue, * and refresh the screen. */ saynow (s, a1, a2, a3, a4, a5, a6, a7, a8) char *s; int a1, a2, a3, a4, a5, a6, a7, a8; { if (!emacs && !terse) { say (s, a1, a2, a3, a4, a5, a6, a7, a8); refresh (); } } /* * waitforspace: Wait for the user to type a space. * Be sure to interpret a snapshot command, if given. */ waitforspace () { char ch; refresh (); if (!noterm) while ((ch = fgetc (stdin)) != ' ') if (ch == '/') dosnapshot (); at (row, col); } /* * givehelp: Each time a ? is pressed, this routine prints the next * help message in a sequence of help messages. Nexthelp is an */ char *nexthelp[] = { "Rgm commands: t=toggle run mode, e=logging, i=inventory, -=status [?]", "Rgm commands: <ret>=singlestep, `=summary, /=snapshot, R=replay [?]", "Rogue cmds: S=Save, Q=Quit, h j k l H J K L b n u y N B U Y f s < > [?]", "Wizard: d=debug, !=show items, @=show monsters, #=show level flags [?]", "Wizard: ~=version, ^=bowrank, %%=armorrank, $=weaponrank, ==ringrank [?]", "Wizard: (=database, )=cycles, +=possible secret doors, :=chicken [?]", "Wizard: [=weapstat, r=resetinv, &=object count, *=toggle blind [?]", "Wizard: C=toggle cosmic, M=mazedoor, m=monster, A=attempt, {=flags", NULL }; char **helpline = nexthelp; givehelp () { if (*helpline == NULL) helpline = nexthelp; saynow (*helpline++); } /* * pauserogue: Wait for the user to type a space and then redraw the * screen. Now uses the stored image and passes it to * curses rather than sending a form feed to Rogue. MLM */ pauserogue () { at (23, 0); addstr ("--press space to continue--"); clrtoeol (); refresh (); waitforspace (); redrawscreen (); } /* * getrogver: Read the output of the Rogue version command * and set version. RV36B = 362 (3.6 with wands) * and RV52A = 521 (5.2). Note that RV36A is * infered when we send a "//" command to identify * wands. */ getrogver () { char *vstr = versionstr, ch; if (replaying) /* Use default version */ { sprintf (versionstr, DEFVER); } else /* Execute the version command */ { sendnow ("v"); waitfor ("ersion "); while ((ch = getroguetoken ()) != ' ') *(vstr++) = ch; *--vstr = '\0'; } if (stlmatch (versionstr, "3.6")) version = RV36B; else if (stlmatch (versionstr, "5.2")) version = RV52A; else if (stlmatch (versionstr, "5.3")) version = RV53A; else saynow ("What a strange version of Rogue! "); } /* * charsavail: How many characters are there at the terminal? If any * characters are found, 'noterm' is reset, since there is obviously * a terminal around if the user is typing at us. */ charsavail () { long n; int retc; if (retc = ioctl (READ, FIONREAD, &n)) { saynow ("Ioctl returns %d, n=%ld.\n", retc, n); n=0; } if (n > 0) noterm = 0; return ((int) n); } /* * redrawscreen: Make the users screen look like the Rogue screen (screen). */ redrawscreen () { register int i, j; char ch; clear (); for (i = 1; i < 24; i++) for (j = 0; j < 80; j++) if ((ch = screen[i][j]) > ' ') mvaddch(i, j, ch); at (row, col); refresh (); } /* * toggleecho: toggle the I/O echo feature. If first time, open the * roguelog file. */ toggleecho () { if (replaying) return; logging = !logging; if (logging) { if ((fecho = wopen (ROGUELOG, "w")) == NULL) { logging = !logging; saynow ("can't open %s", ROGUELOG); } else { fprintf (fecho, "Rogomatic Game Log\n\n"); saynow ("Logging to file %s", ROGUELOG); cecho = 1; } } else { if (cecho) fprintf (fecho, "\n"); else fprintf (fecho, "\"\n"); fclose (fecho); if (playing) saynow ("File %s closed", ROGUELOG); } if (playing) { at (row, col); refresh (); } } /* * clearsendqueue: Throw away queued Rogue commands. */ clearsendqueue () { head = tail; } /* * startreplay: Open the log file to replay. */ startreplay (logfile, logfilename) FILE **logfile; char *logfilename; { if ((*logfile = fopen (logfilename, "r")) == NULL) { fprintf (stderr, "Can't open '%s'.\n", logfilename); exit(1); } } /* * putn: Put 'n' copies of character 'c' on file 'f'. */ putn (c, f, n) register char c; register FILE *f; register int n; { while (n--) putc (c, f); } /* * printsnap: print a snapshot to file f. */ printsnap (f) FILE *f; { register int i, j, length; struct tm *localtime(), *ts; char *statusline(); long clock; /* Now get the current time, so we can date the snapshot */ clock = time(&clock); ts = localtime(&clock); /* Print snapshot timestamp */ fprintf (f, "\nSnapshot taken on %s %d, %d at %02d:%02d:%02d:\n\n", month[ts -> tm_mon], ts -> tm_mday, 1900 + ts -> tm_year, ts -> tm_hour, ts -> tm_min, ts -> tm_sec); /* Print the current map */ putn ('-', f, 79); fprintf (f, "\n"); for (i = 0; i < 24; i++) { for (length = 79; length >= 0 && charonscreen(i,length) == ' '; length--); for (j=0; j <= length; j++) fprintf (f, "%c", charonscreen(i,j)); fprintf (f, "\n"); } putn ('-', f, 79); /* Print status variables */ fprintf (f, "\n\n%s\n\n", statusline ()); /* Print the inventory */ dumpinv (f); fprintf (f, "\n"); putn ('-', f, 79); fprintf (f, "\n"); } /* * getlogtoken: routine to retrieve a rogue token from the log file. * This allows us to replay a game with all the diagnostic commands of * Rog-O-Matic at our disposal. LGCH. */ char getlogtoken() { int acceptline; char ch = GETLOGCHAR; char ch1, ch2, dig; while (ch == NEWLINE) { acceptline = 0; if ((ch = GETLOGCHAR) == 'R') if ((ch = GETLOGCHAR) == ':') if ((ch = GETLOGCHAR) == ' ') { ch = GETLOGCHAR; acceptline = 1; } if (!acceptline) while ((int) ch != NEWLINE && (int) ch != EOF) ch = GETLOGCHAR; } if (ch == '{') { ch1 = GETLOGCHAR; ch2 = GETLOGCHAR; ch = GETLOGCHAR; /* Ignore the closing '}' */ switch (ch1) { case 'b': ch = BS_TOK; break; case 'c': switch (ch2) { case 'e': ch = CE_TOK; break; case 'm': ch = CM_TOK; row = 0; while ((dig = GETLOGCHAR) != ',') { row = row * 10 + dig - '0'; } col = 0; while ((dig = GETLOGCHAR) != ')') { col = col * 10 + dig - '0'; } GETLOGCHAR; /* Ignore '}' */ break; case 'r': ch = CR_TOK; } break; case 'd': ch = DO_TOK; break; case 'f': ch = CL_TOK; break; case 'n': if (ch2 == 'l') ch = LF_TOK; else ch = ND_TOK; break; case 's': if (ch2 == 'e') ch = SE_TOK; else ch = SO_TOK; break; case 't': ch = TA_TOK; break; case 'u': ch = UP_TOK; break; case 'E': while (GETLOGCHAR != '}') ; ch = ER_TOK; break; } } return (ch); } /* * getoldcommand: retrieve the old command from a logfile we are replaying. */ getoldcommand (s) register char *s; { register int charcount = 0; char ch = ' ', term = '"', *startpat = "\nC: "; while (*startpat && (int) ch != EOF) { if ((ch = GETLOGCHAR) != *(startpat++)) startpat = "\nC: "; } if ((int) ch != EOF) { term = ch = GETLOGCHAR; while ((ch = GETLOGCHAR) != term && (int) ch != EOF && charcount++ < 128) { *(s++) = ch; } } *s = '\0'; } /* * dosnapshot: add a snapshot to the SHAPSHOT file. */ dosnapshot () { if ((snapshot = wopen (SNAPSHOT, "a")) == NULL) saynow ("Cannot write file %s.", SNAPSHOT); else { printsnap (snapshot); fclose (snapshot); saynow ("Snapshot added to %s.", SNAPSHOT); } } /* * clearscreen: Done whenever a {ff} is sent by Rogue. This code is * separate so it can be called from replay(), since there is an implicit * formfeed not recorded in the log file. MLM */ clearscreen () { register int i, j; row = col = 0; clear (); screen00 = ' '; for (i = 0; i < 24; i++) for (j = 0; j < 80; j++) { screen[i][j] = ' '; unsetrc (STUFF, i, j); } initstufflist (); mlistlen = 0; /* initmonsterlist (); temp hack MLM */ } /* * statusline: Write all about our current status into a string. * Returns a pointer to a static area. MLM */ char * statusline () { static char staticarea[256]; register char *s=staticarea; sprintf (s, "Status: "); if (aggravated) strcat (s, "aggravated, "); if (beingheld) strcat (s, "being held, "); if (blinded) strcat (s, "blind, "); if (confused) strcat (s, "confused, "); if (cosmic) strcat (s, "cosmic, "); if (cursedarmor) strcat (s, "cursed armor, "); if (cursedweapon) strcat (s, "cursed weapon, "); if (doublehasted) strcat (s, "perm hasted, "); if (droppedscare) strcat (s, "dropped scare, "); if (floating) strcat (s, "floating, "); if (hasted) strcat (s, "hasted, "); if (protected) strcat (s, "protected, "); if (redhands) strcat (s, "red hands, "); if (Level == didreadmap) strcat (s, "mapped, "); if (*genocided) sprintf (s, "genocided '%s', ", s, genocided); sprintf (s, "%s%d food%s, %d missile%s, %d turn%s, (%d,%d %d,%d) bonus", s, larder, plural(larder), ammo, plural(ammo), turns, plural(turns), gplushit, gplusdam, wplushit, wplusdam); return (s); }