ejb@think.ARPA (Erik Bailey) (05/04/86)
#---Cut here and place in it's own directory, then feed to Bourne shell---
# This is a shell archive. Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file". (Files
# unpacked will be owned by you and have default permissions.)
# This archive contains:
# cmd.c (7867 chars)
# init.c (18991 chars)
# main.c (8760 chars)
# scan.c (3505 chars)
# talk.c (7531 chars)
# view.c (6135 chars)
#
echo x - cmd.c
sed -e 's/^X//' > "cmd.c" << '//E*O*F cmd.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)cmd.c 1.2 4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include "war.h"
X
Xint testblast(); /* routine to test is blasting is ok */
Xint testfight(); /* routine to test if fighting is ok */
X
X/*
X * Read any command from us and set them for the object to work on.
X * This routine never blocks for input. Partially finished commands
X * will be delayed until they are complete. Such partial commands
X * can be edited. Command errors ring the terminal bell.
X */
Xdocommands()
X{
X register char *cp; /* character pointer */
X register int ch; /* current character */
X register int objchar; /* character for object */
X char buf[SCAN_SIZE+1]; /* input buffer */
X
X if (setjmp(ttyjmp) == SCAN_EOF) { /* done with commands */
X runcommands();
X sendinfo('r', 0, 0, 0);
X return;
X }
X objchar = scanchar(); /* get object char */
X if ((objchar == ' ') || (objchar == '\n') || (objchar == '\033'))
X scanabort();
X if (ch == '\014') {
X dpyredraw();
X scanabort();
X }
X cp = buf;
X while (1) { /* get commands for object */
X ch = scanchar();
X if ((ch == ' ') || (ch == '\n')) break;
X if (ch == '\033') scanabort();
X *cp++ = ch;
X }
X *cp = '\0';
X if (setcmds(findobject(objchar), buf))
X write(STDERR, "\7", 1);
X scanabort();
X}
X
X
X/*
X * Routine call by scanchar to read the next input character from
X * the terminal. Longjmps away if a character is not ready yet.
X */
Xttychar()
X{
X long n; /* char count */
X unsigned char ch; /* char to return */
X
X if (playing && (ioctl(STDIN, FIONREAD, &n) == 0) && (n <= 0)) {
X scaneof(); /* no char available now */
X }
X do {
X errno = 0;
X n = read(STDIN, &ch, 1); /* read one char */
X } while ((n < 0) && (errno == EINTR));
X return((int)(ch &= 0x7f));
X}
X
X
X
X/*
X * Execute one move's worth of commands for all of the user's objects.
X * Returns nonzero if something at all moved.
X */
Xruncommands()
X{
X register struct object *obj; /* current object */
X register int trymore; /* nonzero if try again */
X register int moved; /* something moved */
X
X for (obj = objects; obj < endobjects; obj++)
X obj->o_flags &= ~F_MOVED;
X moved = 0;
X do {
X trymore = 0;
X for (obj = objects; obj < endobjects; obj++) {
X trymore |= runobject(obj);
X }
X moved |= trymore;
X } while (trymore);
X return(moved);
X}
X
X
X/*
X * Attempt to run one move's worth of commands for an object.
X * Returns nonzero if a command was actually executed.
X * In addition, the object is flagged as having made it's move.
X */
Xrunobject(obj)
X register struct object *obj; /* object to execute */
X{
X register struct cmd *cmd; /* current command to execute */
X register struct cell *cell; /* current cell */
X register struct cell *newcell; /* new cell */
X register struct object *newobj; /* other object */
X int trying; /* trying to do commands */
X
X if ((obj->o_side != myside) || (obj->o_flags & F_MOVED)) return(0);
X cell = obj->o_cell;
X if (cell == NULL) return(0);
X trying = 0;
X for (cmd = obj->o_cmds; cmd->c_type; cmd++) {
X if (cmd->c_count <= 0) continue;
X newcell = NULL;
X switch (cmd->c_type) {
X case 'b': /* blast a wall */
X if (tryblast(obj, cmd->c_subtype)) return(1);
X break;
X case 'f': /* fight the enemy */
X if (tryfight(obj, cmd->c_subtype)) return(1);
X break;
X case 'h': /* move left */
X newcell = cell->c_left;
X break;
X case 'j': /* move down */
X newcell = cell->c_down;
X break;
X case 'k': /* move up */
X newcell = cell->c_up;
X break;
X case 'l': /* move right */
X newcell = cell->c_right;
X break;
X default:
X panic("running unknown command");
X }
X if ((newcell == NULL) || (newcell == &edge)) continue;
X newobj = newcell->c_obj;
X if (newobj) {
X trying |= ((newobj->o_flags & F_WALL) == 0);
X continue;
X }
X placeobject(obj, newcell->c_row, newcell->c_col);
X sendinfo('p', newcell->c_row, newcell->c_col, obj->o_id);
X obj->o_flags |= F_MOVED;
X if (cmd->c_count < INFINITY) cmd->c_count--;
X return(1);
X }
X return(0);
X}
X
X
X/*
X * Attempt to blast a wall in a given direction.
X * Returns nonzero if we actually tried to do it.
X */
Xtryblast(obj, dir)
X register struct object *obj; /* object doing fighting */
X{
X register struct cell *cc; /* current cell */
X
X if ((obj->o_flags & F_MOVED) || ((obj->o_flags & F_BLAST) == 0))
X return(0);
X cc = pickdir(obj->o_cell, dir, testblast);
X if (cc == NULL) return(0);
X obj->o_flags |= F_MOVED;
X obj = cc->c_obj;
X if ((obj->o_life >= 100) ||
X ((obj->o_life > 0) && (random() % obj->o_life)))
X return(1);
X removeobject(cc->c_row, cc->c_col);
X sendinfo('b', cc->c_row, cc->c_col, 0);
X return(1);
X}
X
X
X/* See if a location is worth blasting */
Xtestblast(cc)
X register struct cell *cc; /* cell to check */
X{
X return((cc != &edge) && cc->c_obj && (cc->c_obj->o_flags & F_WALL));
X}
X
X
X/*
X * Attempt to fight in a given certain direction.
X * Returns nonzero if we actually did fight.
X */
Xtryfight(obj, dir)
X register struct object *obj; /* object doing fighting */
X{
X register struct cell *cc; /* current cell */
X
X if ((obj->o_flags & F_MOVED) || ((obj->o_flags & F_FIGHT) == 0))
X return(0);
X cc = pickdir(obj->o_cell, dir, testfight);
X if (cc == NULL) return(0);
X obj->o_flags |= F_MOVED;
X if (random() % 2) return(1);
X sendinfo('h', cc->c_row, cc->c_col, 0);
X hitobject(cc->c_row, cc->c_col);
X return(1);
X}
X
X
X/* See if a cell is worth fighting */
Xtestfight(cc)
X struct cell *cc; /* cell to check */
X{
X register struct object *obj; /* object to fight */
X
X obj = cc->c_obj;
X if (obj == NULL) return(0);
X return(((obj->o_flags&(F_WALL|F_EDGE))== 0) && (obj->o_side != myside));
X}
X
X
X/*
X * Specify the given list of commands to the specified object.
X * Returns nonzero if a bad command is given or if too many are specified.
X * If the string is valid, any previous commands are cancelled and
X * the new ones are in effect.
X */
Xsetcmds(obj, str)
X register struct object *obj; /* current object */
X register char *str; /* command string */
X{
X register struct cmd *cmd; /* next command to store */
X register int count; /* count for command */
X register int gotcount; /* got a count */
X struct cmd newcmds[CMDS]; /* new commands */
X
X if ((obj == NULL) || (obj->o_side != myside)) return(1);
X cmd = newcmds;
X while (*str) {
X count = 0; /* read numeric argument */
X gotcount = 0;
X while ((*str >= '0') && (*str <= '9')) {
X count = (count * 10) + *str++ - '0';
X gotcount = 1;
X }
X if (count >= (INFINITY - 1)) count = (INFINITY - 1);
X if (gotcount == 0) count = INFINITY;
X if (count <= 0) return(1);
X switch (*str) {
X case 'b': /* blast in a direction */
X case 'f': /* fight in a direction */
X if (cmd >= &newcmds[CMDS-2]) return(1);
X cmd->c_type = *str++;
X switch (*str) { /* verify */
X case 'h': case 'l':
X case 'j': case 'k':
X case 'a': case 'n':
X cmd->c_subtype = *str++;
X cmd->c_count = INFINITY;
X cmd++;
X continue;
X }
X return(1);
X
X case 'h': /* move left small amount */
X case 'j': /* move down small amount */
X case 'k': /* move up small amount */
X case 'l': /* move right small amount */
X if (cmd >= &newcmds[CMDS-2]) return(1);
X if (gotcount == 0) count = 1;
X cmd->c_type = *str++;
X cmd->c_subtype = '\0';
X cmd->c_count = count;
X cmd++;
X continue;
X
X case 'H': /* move left large amount */
X case 'J': /* move down large amount */
X case 'K': /* move up large amount */
X case 'L': /* move right large amount */
X if (cmd >= &newcmds[CMDS-2]) return(1);
X cmd->c_type = *str++ - 'A' + 'a';
X cmd->c_subtype = '\0';
X cmd->c_count = count;
X cmd++;
X continue;
X
X default: /* illegal */
X return(1);
X }
X }
X cmd->c_type = 'f'; /* add default fight command */
X cmd->c_subtype = 'a';
X cmd->c_count = INFINITY;
X cmd++;
X cmd->c_type = '\0'; /* end command table */
X bcopy(newcmds, obj->o_cmds, sizeof(obj->o_cmds)); /* set cmds */
X return(0);
X}
//E*O*F cmd.c//
echo x - init.c
sed -e 's/^X//' > "init.c" << '//E*O*F init.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)init.c 1.2 4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include "war.h"
X
Xstatic int checkcount; /* counter for board checking */
Xstatic int didescape; /* did escape */
Xchar *ask(); /* routine to ask a question */
Xint getaskchar(); /* routine to get chars */
XFILE *opensetupfile(); /* open setup files */
X
X
X/*
X * Get commands from the user to initialize the board.
X */
Xuserinit()
X{
X register struct object *obj; /* object being placed */
X register int ch; /* current character */
X register char *str; /* answer for user's question */
X register int countone; /* command count defaulted to one */
X register int countinf; /* count defaulted to infinity */
X int gotcount; /* true if read a count */
X int row; /* row to place it on */
X int col; /* column to place it on */
X
X row = 0;
X col = 0;
X obj = NULL;
X newstat = 1;
X setjmp(ttyjmp); /* come back here for every command */
X if (newstat) viewstats();
X dpymove(row, col);
X dpyupdate();
X countone = 0;
X gotcount = 0;
X while (1) { /* read numeric argument if any */
X ch = scanchar();
X if ((ch < '0') || (ch > '9')) break;
X countone = (countone * 10) + ch - '0';
X gotcount = 1;
X }
X if (countone < 0) countone = 0;
X if (countone > 100) countone = 100;
X countinf = countone;
X if (gotcount == 0) { /* default values if necessary */
X countone = 1;
X countinf = 100;
X }
X switch (ch) {
X case '\033': /* ESCAPE - ignore command */
X break;
X case '\014': /* ^L - redraw screen */
X dpyredraw();
X break;
X case '\n': /* move to next line */
X row += countone;
X if (row > HOMEROW) row = HOMEROW;
X col = 0;
X break;
X case '-': /* move to previous line */
X row -= countone;
X if (row < 0) row = 0;
X col = 0;
X break;
X case '\t': /* move to next tab stop */
X while ((col < (COLS-1)) && (++col % 8)) ;
X break;
X case '^': /* move to beginning of row */
X col = 0;
X break;
X case '$': /* move to end of row */
X col = (COLS - 1);
X break;
X case 'h': /* move left */
X col -= countone;
X if (col < 0) col = 0;
X break;
X case 'j': /* move down */
X row += countone;
X if (row > HOMEROW) row = HOMEROW;
X break;
X case 'k': /* move up */
X row -= countone;
X if (row < 0) row = 0;
X break;
X case 'l': /* move right */
X case ' ':
X col += countone;
X if (col >= COLS) col = COLS - 1;
X break;
X case 'y': /* move to upper left */
X while (countone-- && (row > 0) && (col > 0)) {
X row--;
X col--;
X }
X break;
X case 'u': /* move to upper right */
X while (countone-- && (row > 0) && (col < COLS-1)) {
X row--;
X col++;
X }
X break;
X case 'b': /* move to lower left */
X while (countone-- && (row < HOMEROW) && (col > 0)) {
X row++;
X col--;
X }
X break;
X case 'n': /* move to lower right */
X while (countone-- && (row<HOMEROW) && (col<COLS-1)) {
X row++;
X col++;
X }
X break;
X case 'f': /* flip board left-right */
X flipboard();
X break;
X case 'p': /* place an object */
X obj = findobject(scanchar());
X /* proceed into next case */
X case '.': /* place same object */
X placeobject(obj, row, col);
X break;
X case 'x': /* delete an object */
X while (countone--) {
X removeobject(row, col);
X if (col >= (COLS-1)) break;
X col++;
X }
X break;
X case 'H': /* place lots left */
X placeline(obj, &row, &col, 0, -1, countinf);
X break;
X case 'J': /* place lots down */
X placeline(obj, &row, &col, 1, 0, countinf);
X break;
X case 'K': /* place lots up */
X placeline(obj, &row, &col, -1, 0, countinf);
X break;
X case 'L': /* place lots right */
X placeline(obj, &row, &col, 0, 1, countinf);
X break;
X case 'Y': /* place lots to upper left */
X placeline(obj, &row, &col, -1, -1, countinf);
X break;
X case 'U': /* place lots to upper right */
X placeline(obj, &row, &col, -1, 1, countinf);
X break;
X case 'B': /* place lots to lower left */
X placeline(obj, &row, &col, 1, -1, countinf);
X break;
X case 'N': /* place lots to lower right */
X placeline(obj, &row, &col, 1, 1, countinf);
X break;
X case 's': /* start to play */
X if (editflag || checksetup()) {
X beep();
X break;
X }
X if (yesno("Ready to start playing? ")) return;
X break;
X case 'q': /* want to quit */
X if (yesno("Want to quit? ")) quit(0);
X break;
X case 'w': /* write the setup */
X str = ask("Write setup named: ");
X if (str == NULL) break;
X if (writesetup(setupfile, str)) beep();
X break;
X case 'r': /* read a setup */
X str = ask("Read setup named: ");
X if (str == NULL) break;
X if (readsetup(setupfile, str)) beep();
X break;
X case 'c': /* clear board */
X if (yesno("Want to clear the board? ")) clearboard();
X break;
X default:
X beep();
X break;
X }
X scanabort(); /* go back up for next command */
X}
X
X
X/*
X * Ask a question and see if the answer is yes.
X * Returns nonzero if so.
X */
Xyesno(str)
X register char *str; /* string to ask, and answer */
X{
X str = ask(str);
X if (str == NULL) return(0);
X return((strcmp(str, "y") == 0) || (strcmp(str, "yes") == 0)
X || (strcmp(str, "Y") == 0) || (strcmp(str, "YES") == 0));
X}
X
X
X/*
X * Ask the player a question and get an answer.
X * Leading and trailing spaces are removed from the answer.
X * If an escape or a null answer is given, a null pointer is returned.
X */
Xchar *
Xask(str)
X{
X register char *cp; /* current character */
X static char buf[100]; /* buffer */
X
X didescape = 0;
X scanreset();
X dpywindow(-1, -1, 2, COLS);
X cp = buf;
X cp += dpyread(str, getaskchar, cp, sizeof(buf));
X if (cp < buf) cp = buf;
X *cp-- = '\0';
X while ((cp >= buf) && ((*cp == ' ') || (*cp == '\t'))) *cp-- = '\0';
X for (cp = buf; ((*cp == ' ') || (*cp == '\t')); cp++) ;
X dpyhome();
X dpyclrwindow();
X dpywindow(0, -1, 1, COLS);
X if (didescape || (*cp == '\0')) cp = NULL;
X return(cp);
X}
X
X
X/* Routine called by dpyread to get characters */
Xgetaskchar()
X{
X char ch;
X
X if ((read(STDIN, &ch, 1) != 1) || (ch == '\033')) {
X didescape = 1;
X return(-1);
X }
X if (ch == '\n') return(-1);
X return((int)(ch & 0x7f));
X}
X
X
X/*
X * Place a run of an object in a certain direction until it is illegal
X * or until the count runs out. The new row and column are returned
X * through pointers.
X */
Xplaceline(obj, rowptr, colptr, rowsign, colsign, count)
X register struct object *obj; /* object to place */
X unsigned int *rowptr; /* current row */
X unsigned int *colptr; /* current column */
X register int rowsign; /* changes to make in rows */
X register int colsign; /* changes to make in columns */
X{
X register unsigned int row; /* current row */
X register unsigned int col; /* current column */
X
X row = *rowptr;
X col = *colptr;
X while (count-- > 0) {
X if (placeobject(obj, row, col)) break;
X row += rowsign;
X col += colsign;
X if ((row > HOMEROW) || (col >= COLS)) {
X row -= rowsign;
X col -= colsign;
X break;
X }
X if (board[row][col].c_obj) break;
X }
X *rowptr = row;
X *colptr = col;
X}
X
X
X/*
X * Clear everything from the home area of the board.
X */
Xclearboard()
X{
X register struct cell *cc; /* current cell */
X
X for (cc = homecell; cc; cc = cc->c_next) {
X if (cc->c_obj) removeobject(cc->c_row, cc->c_col);
X }
X}
X
X
X/*
X * Flip the board left to right during the initialization phase
X */
Xflipboard()
X{
X register int row; /* current row */
X register struct cell *lc; /* left cell */
X register struct cell *rc; /* right cell */
X register struct object *lobj; /* left object */
X register struct object *robj; /* right object */
X
X for (row = 0; row <= HOMEROW; row++) {
X lc = &board[row][0];
X rc = &board[row][COLS-1];
X for (; lc < rc; lc = lc->c_right, rc = rc->c_left) {
X lobj = lc->c_obj;
X robj = rc->c_obj;
X if (lobj == robj) continue;
X lc->c_obj = robj;
X rc->c_obj = lobj;
X if (lobj && (lobj->o_max <= 1)) lobj->o_cell = rc;
X if (robj && (robj->o_max <= 1)) robj->o_cell = lc;
X if (lobj) dpyplace(lc->c_row, lc->c_col, ' ');
X if (robj) dpyplace(rc->c_row, rc->c_col, ' ');
X if (lobj) dpyplace(rc->c_row, rc->c_col, lobj->o_ownch);
X if (robj) dpyplace(lc->c_row, lc->c_col, robj->o_ownch);
X }
X }
X}
X
X
X/*
X * Check the setup to make sure we are using all the men we are supposed to.
X * Returns nonzero if some men are missing.
X */
Xchecksetup()
X{
X register struct object *obj; /* current object */
X
X for (obj = objects; obj < endobjects; obj++) {
X if (obj->o_side != myside) continue;
X if (obj->o_count < obj->o_min) return(1);
X }
X return(0);
X}
X
X
X/*
X * Determine if an object just placed on the board leaves a legal layout.
X * This means that each board location is reachable by some path which does
X * not need to cross objects which can be multiply-placed (such as walls).
X * Returns nonzero if the layout is illegal.
X */
Xcheckboard(nc)
X register struct cell *nc; /* new cell just placed */
X{
X register struct cell *cc; /* current cell */
X register struct cell *rc; /* beginning of row cell */
X register struct cell **ptr; /* pointer to adjacent cells */
X
X if ((nc == NULL) || (nc->c_obj == NULL)) panic("checkboard");
X if (((nc->c_obj->o_flags&F_WALL) == 0) || (nc->c_obj->o_side != myside))
X return(0);
X /*
X * See if the placing of the object is trivially known to be legal.
X * This is true if nothing surrounds the object, or if a single
X * object lies next to it which isn't surrounded. We must search
X * in all eight directions for this check.
X */
X cc = NULL;
X for (ptr = nc->c_dirs; ptr < &nc->c_dirs[8]; ptr++) {
X rc = *ptr;
X if ((rc->c_obj == NULL) || ((rc->c_obj->o_flags & F_WALL) == 0))
X continue;
X if (cc) goto hard;
X cc = rc;
X }
X if (cc == NULL) return(0);
X for (ptr = cc->c_dirs; ptr < &cc->c_dirs[8]; ptr++) {
X rc = *ptr;
X if ((rc->c_obj == NULL) || ((rc->c_obj->o_flags & F_WALL) == 0))
X return(0);
X }
X /*
X * At least two other objects lie next to the new one.
X * We must do the brute force check now.
X */
Xhard: rc = &board[HOMEROW+1][0];
X rc->c_checkcount = ++checkcount;
X checkboardloop(rc);
X for (rc = homecell; rc; rc = rc->c_next)
X if (rc->c_checkcount != checkcount) return(1);
X return(0);
X}
X
X
X/*
X * Recursive subroutine used for marking accessible locations on the board.
X * Call ourself for each new cell in the four orthagonal directions from
X * the given cell.
X */
Xcheckboardloop(cc)
X register struct cell *cc; /* current cell to check */
X{
X register struct cell *tc; /* temporary cell */
X register struct cell **ptr; /* cell pointer */
X register int count; /* count of cells to check */
X struct cell *celltab[4]; /* table of cells to check */
X
X while (1) {
X count = 0;
X for (ptr = cc->c_dirs; ptr < &cc->c_dirs[4]; ptr++) {
X tc = *ptr;
X if (tc->c_obj == &edgeobj) continue;
X if (tc->c_row > (HOMEROW + 1)) continue;
X if (tc->c_checkcount == checkcount) continue;
X tc->c_checkcount = checkcount;
X if (tc->c_obj && (tc->c_obj->o_flags & F_WALL))
X continue;
X celltab[count++] = tc;
X }
X if (count != 1) break;
X cc = celltab[0]; /* only one, no recursion needed */
X }
X while (count > 0) checkboardloop(celltab[--count]);
X}
X
X
X/*
X * Initialize the cells of the board
X */
Xboardinit()
X{
X register struct cell *cc; /* current cell */
X register int row; /* current row */
X register int col; /* current column */
X register int ch; /* character */
X
X cc = &edge;
X cc->c_row = -1;
X cc->c_col = -1;
X cc->c_up = cc;
X cc->c_down = cc;
X cc->c_left = cc;
X cc->c_right = cc;
X cc->c_ul = cc;
X cc->c_ur = cc;
X cc->c_ll = cc;
X cc->c_lr = cc;
X cc->c_obj = &edgeobj;
X edgeobj.o_ownch = '*';
X edgeobj.o_altch = '*';
X edgeobj.o_side = -1;
X edgeobj.o_flags = (F_WALL|F_EDGE);
X for (row = 0; row < ROWS; row++) {
X for (col = 0; col < COLS; col++) {
X cc = &board[row][col];
X cc->c_row = row;
X cc->c_col = col;
X cc->c_up = &board[row-1][col];
X cc->c_down = &board[row+1][col];
X cc->c_left = &board[row][col-1];
X cc->c_right = &board[row][col+1];
X cc->c_ul = &board[row-1][col-1];
X cc->c_ur = &board[row-1][col+1];
X cc->c_ll = &board[row+1][col-1];
X cc->c_lr = &board[row+1][col+1];
X cc->c_next = cc->c_left;
X cc->c_obj = NULL;
X }
X cc = &board[row][0];
X cc->c_left = &edge;
X cc->c_ul = &edge;
X cc->c_ll = &edge;
X cc->c_next = &board[row-1][COLS-1];
X cc = &board[row][COLS-1];
X cc->c_right = &edge;
X cc->c_ur = &edge;
X cc->c_lr = &edge;
X }
X for (col = 0; col < COLS; col++) {
X cc = &board[0][col];
X cc->c_up = &edge;
X cc->c_ul = &edge;
X cc->c_ur = &edge;
X cc = &board[ROWS-1][col];
X cc->c_down = &edge;
X cc->c_ll = &edge;
X cc->c_lr = &edge;
X }
X board[0][0].c_next = NULL;
X firstcell = &board[ROWS-1][COLS-1];
X homecell = &board[HOMEROW][COLS-1];
X /*
X * Initialize the looks of the board
X */
X for (row = 0; row < ROWS; row++) {
X ch = '|';
X if ((row == HOMEROW) || (row == (ROWS - HOMEROW - 1))) ch = '+';
X dpychar(ch);
X for (col = 0; col < COLS; col++) dpychar(' ');
X dpychar(ch);
X dpychar('\n');
X }
X dpywindow(0, -1, 1, COLS);
X}
X
X
X/*
X * Clear the board and read in a setup from a board file.
X * If a null board name is specified, a default one in the user's home
X * directory is used. Returns nonzero if the file cannot be found.
X */
Xreadsetup(setupfile, setupname)
X char *setupfile; /* filename for board setups */
X register char *setupname; /* name of setup */
X{
X register char *cp; /* current character */
X register int row; /* current row */
X register int col; /* current column */
X register FILE *fd; /* file handle */
X char buf[200]; /* line of the file */
X
X if (setupname == NULL) return(0);
X /*
X * Open file and search for setup name
X */
X fd = opensetupfile(setupfile, "r");
X if (fd == NULL) return(1);
X do {
X cp = buf;
X if (fgets(cp, sizeof(buf), fd) == NULL) {
X fclose(fd);
X return(1);
X }
X if (*cp++ != '"') continue;
X while (*cp && (*cp != '"') && (*cp != '\n')) cp++;
X *cp = '\0';
X } while (strcmp(setupname, &buf[1]));
X /*
X * Found the setup, clear the board and read it in
X */
X clearboard();
X for (row = 0; row <= HOMEROW; row++) {
X cp = buf;
X if (fgets(cp, sizeof(buf), fd) == NULL) break;
X if (*cp == '"') break;
X for (col = 0; (col < COLS) && *cp; col++, cp++) {
X if (*cp == '\n') break;
X if (*cp == '\t') col |= 7;
X if ((*cp <= ' ') || (*cp == 0177)) continue;
X placeobject(findobject(*cp), row, col);
X }
X }
X fclose(fd);
X return(0);
X}
X
X
X/*
X * Append the current setup to the specified file.
X * Existing setups by the same name are not removed (which is a bug).
X * This is to make the algorithm easy.
X */
Xwritesetup(setupfile, setupname)
X char *setupfile; /* filename for board setups */
X char *setupname; /* name of setup */
X{
X register struct cell *rc; /* cell at front of row */
X register struct cell *cc; /* current cell in row */
X register char *cp; /* current character */
X register FILE *fd; /* file handle */
X int error; /* error flag */
X char buf[COLS+2]; /* data to write */
X
X if (setupname == NULL) return(1);
X fd = opensetupfile(setupfile, "a");
X if (fd == NULL) return(1);
X fprintf(fd, "\"%s\"\n", setupname);
X for (rc = &board[0][0]; rc->c_row <= HOMEROW; rc = rc->c_down) {
X for (cc = rc, cp = buf; cc != &edge; cc = cc->c_right) {
X *cp++ = cc->c_obj ? cc->c_obj->o_ownch : ' ';
X }
X cp = &buf[COLS-1];
X while ((cp >= buf) && (*cp == ' ')) cp--;
X *++cp = '\n';
X *++cp = '\0';
X fputs(buf, fd);
X }
X error = ferror(fd);
X fclose(fd);
X return(error);
X}
X
X
X/*
X * Open the given setup file name, defaulting it if necessary.
X * Returns a stdio FILE pointer if successfull, or NULL on an error.
X */
XFILE *
Xopensetupfile(name, mode)
X register char *name; /* file name, or NULL for default */
X char *mode; /* mode to open file in */
X{
X char buf[200]; /* buffer for default name */
X
X if ((name == NULL) || (*name == '\0')) {
X name = (char *) getenv("HOME");
X if (name == NULL) return(NULL);
X sprintf(buf, "%s/%s", name, SETUPFILE);
X name = buf;
X }
X return(fopen(name, mode));
X}
X
X
X/*
X * Read in the parameters for the objects from the specified file.
X * If a null name is given, the default file is used.
X * Doesn't return if an error is encountered.
X */
Xreadobjects(name)
X register char *name; /* filename for objects */
X{
X register FILE *fd; /* file variable */
X register struct object *obj; /* current object */
X register int line; /* line number */
X register int err; /* error occurred */
X register int count; /* token count */
X char flags[21]; /* flag characters */
X char seen[3]; /* characters seen as */
X char eol[2]; /* end of line character */
X int life; /* life of the object */
X int view; /* viewing range of the object */
X int min; /* minimum number of objects */
X int max; /* maximum number of objects */
X char buf[200]; /* data buffer */
X char altname[200]; /* alternate name */
X
X if ((name == NULL) || (*name == '\0')) name = OBJECTFILE;
X fd = fopen(name, "r");
X if (fd == NULL) {
X if (index(name, '/') == 0) {
X sprintf(altname, "%s/%s", LIBDIR, name);
X name = altname;
X fd = fopen(name, "r");
X }
X if (fd == NULL) {
X perror(name);
X quit(1);
X }
X }
X for (endobjects = objects, err = 0, line = 1; ; line++) {
X fgets(buf, sizeof(buf), fd);
X if (feof(fd) || ferror(fd)) break;
X if (buf[0] == '#') continue; /* comment line */
X eol[0] = '#';
X count = sscanf(buf, "%20s%2s%d%d%d%d%1s\n",
X flags, seen, &life, &view, &min, &max, eol);
X if ((count < 6) || (seen[0] < ' ') || (seen[0] == 0177)
X || (view < 0) || (min < 0) || (max < min)
X || (eol[0] != '#')) {
X fprintf(stderr, "%s, line %d: bad object\n",
X name, line);
X err = 1;
X continue;
X }
X if (endobjects >= &objects[OBJS-1]) {
X fprintf(stderr, "%s: Too many objects\n", name);
X quit(1);
X }
X obj = endobjects++; /* allocate object */
X obj->o_side = 0;
X obj->o_id = (line * 2);
X obj->o_ownch = seen[0];
X obj->o_altch = seen[1];
X if ((obj->o_altch < ' ') || (obj->o_altch == 0177))
X obj->o_altch = ' ';
X obj->o_view = view;
X obj->o_life = life;
X obj->o_min = min;
X obj->o_max = max;
X obj->o_count = 0;
X obj->o_cell = NULL;
X if (setflags(obj, flags)) {
X fprintf(stderr, "%s, line %d: bad flag bits\n",
X name, line);
X err = 1;
X }
X setcmds(obj, ""); /* set to fight */
X if ((obj->o_life == 0) && (obj->o_flags & F_GOAL))
X obj->o_life = 1;
X if (obj->o_max > 1) {
X obj->o_flags |= F_IMMOB;
X obj->o_flags &= ~(F_FIGHT|F_BLAST);
X }
X *endobjects = *obj; /* duplicate for other side */
X endobjects->o_side = 1;
X endobjects->o_id++;
X endobjects++;
X }
X if (err) quit(1);
X if (ferror(fd)) {
X perror(name);
X quit(1);
X }
X fclose(fd);
X myside = 1;
X}
X
X
X/*
X * Parse the flag string for an object and set the appropriate flags.
X * Returns nonzero if they were illegal.
X */
Xsetflags(obj, str)
X register struct object *obj; /* object to set flags for */
X register char *str; /* string */
X{
X register int flags; /* flag bits */
X
X flags = 0;
X while (*str) switch (*str++) {
X case 'b': flags |= F_BLAST; break;
X case 'f': flags |= F_FIGHT; break;
X case 'g': flags |= F_GOAL; break;
X case 'i': flags |= F_IMMOB; break;
X case 't': flags |= F_TRANS; break;
X case 'v': flags |= F_VIS; break;
X case 'w': flags |= (F_WALL|F_IMMOB); break;
X case 'x': flags |= F_XRAY; break;
X case '-': break;
X default: return(1);
X }
X obj->o_flags = flags;
X return(0);
X}
//E*O*F init.c//
echo x - main.c
sed -e 's/^X//' > "main.c" << '//E*O*F main.c//'
Xstatic char *sccsid = "@(#)main.c 1.2 4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X
X/*
X * Two player war game played in mazes (by David I. Bell).
X * These war game sources are in the public domain, and can be copied
X * or used as desired, with the following restrictions:
X * 1. All copyright notices (and this notice) must be preserved.
X * 2. The war game sources (even if modified) cannot be sold for profit.
X * 3. If any sources are modified, a sentence must exist by the
X * copyright notice of each modified source file which says that
X * the file has been modified.
X */
X
X#include "war.h"
X
Xint sigint(); /* interrupt signal catcher */
X
Xmain(argc, argv)
X register int argc; /* argument count */
X register char **argv; /* argument name */
X{
X register char *errstr; /* reason for option error */
X
X while (argc-- > 1) {
X argv++;
X if (**argv != '-') { /* not option, must be name */
X if (enemyname) usage();
X enemyname = *argv;
X if ((argc > 1) && (argv[1][0] != '-')) {
X enemytty = *++argv;
X argc--;
X }
X continue;
X }
X errstr = NULL;
X switch (argv[0][1]) {
X case 'e': /* just editing */
X editflag = 1;
X break;
X case 'f': /* setup file */
X errstr = "setup file";
X setupfile = *++argv;
X argc--;
X break;
X case 'o': /* object file */
X errstr = "object file";
X objectfile = *++argv;
X argc--;
X break;
X case 's': /* setup name */
X errstr = "setup";
X setupname = *++argv;
X argc--;
X break;
X default: /* errors */
X usage();
X }
X if (errstr && ((*argv == NULL) || (**argv == '-'))) {
X fprintf(stderr, "missing %s name\n", errstr);
X exit(1);
X }
X }
X if ((enemyname == NULL) && (editflag == 0)) usage();
X if (editflag == 0) findplayer(enemyname, enemytty);
X signal(SIGINT, sigint);
X if (dpyinit(NULL, NULL)) exit(1);
X scaninit(ttychar, ttyjmp);
X boardinit(); /* build the board */
X if (readobjects(objectfile)) { /* read in objects */
X fprintf(stderr, "bad object file\n");
X exit(1);
X }
X if (readsetup(setupfile, setupname)) { /* read in setup */
X fprintf(stderr, "cannot get setup\n");
X exit(1);
X }
X userinit();
X dpymove(0, 0);
X talkinit(); /* set up talking with enemy */
X while (1) {
X readinfo(); /* get enemy's changes */
X view(); /* show current board */
X sleep(1); /* wait a bit */
X docommands(); /* read and execute commands */
X }
X}
X
X
X/*
X * Tell how to run the game and exit
X */
Xusage()
X{
X fprintf(stderr, "\
Xusage: war [-f setupfile] [-s setupname] [-o objectfile] username [ttyname]\n\
X or: war -e [-f setupfile] [-s setupname] [-o objectfile]\n");
X exit(1);
X}
X
X
X/*
X * Find the object of ours with the given character.
X * Lower case letters are converted to upper case if reasonable.
X * Returns NULL if the object cannot be found.
X */
Xstruct object *
Xfindobject(ch)
X{
X register struct object *obj; /* current object */
X
X for (obj = objects; obj < endobjects; obj++) {
X if ((obj->o_ownch == ch) && (obj->o_side == myside)
X && ((playing == 0) || obj->o_count))
X return(obj);
X }
X if ((ch >= 'a') && (ch <= 'z')) /* try upper case */
X return(findobject(ch - 'a' + 'A'));
X return(NULL);
X}
X
X
X/*
X * Find the object which has the given id.
X * Fails if the object could not be found.
X */
Xstruct object *
Xfindid(id)
X{
X register struct object *obj; /* found object */
X
X for (obj = objects; obj < endobjects; obj++) {
X if (obj->o_id == id)
X return(obj);
X }
X panic("unknown object");
X}
X
X
X/*
X * Place an object at a particular location. If the object is incapable
X * of being multiply placed, it is removed from its old location.
X * If another object is currently at this location, it is removed.
X * If the object can be multiply placed then a check is made to insure
X * that all multiply paced objects can be reached from the common area.
X * Returns nonzero if placement cannot be done.
X * The screen database is also updated.
X */
Xplaceobject(obj, row, col)
X register struct object *obj; /* object to place */
X unsigned int row; /* row to place object at */
X unsigned int col; /* column to place object at */
X{
X register struct cell *cc; /* cell location */
X register struct object *oldobj;/* old object at this location */
X
X if (obj == NULL) return(0);
X if ((row >= ROWS) || (col >= COLS)) panic("placeloc");
X /*
X * Remove any object already at this location
X */
X oldobj = board[row][col].c_obj;
X if (oldobj) {
X if (oldobj == obj) return(0); /* already here */
X removeobject(row, col);
X }
X /*
X * If we are elsewhere on the board, remove us from there
X */
X cc = obj->o_cell;
X if (cc) {
X if (cc->c_obj != obj) panic("badcell");
X removeobject(cc->c_row, cc->c_col);
X }
X /*
X * Put object at the current location
X */
X cc = &board[row][col];
X cc->c_obj = obj;
X cc->c_seen = ((obj->o_side == myside) || (obj->o_flags & F_VIS));
X if (obj->o_max <= 1) obj->o_cell = cc;
X if (cc->c_seen) dpyplace(row, col, objectchar(obj));
X obj->o_count++;
X adjustdata(obj, 1);
X /*
X * Verify that this placement is legal
X */
X if (obj->o_side != myside) return(0);
X if ((obj->o_count > obj->o_max) || checkboard(cc)) {
X removeobject(row, col);
X placeobject(oldobj, row, col);
X return(1);
X }
X return(0);
X}
X
X
X/*
X * Remove an object from the board at a given coordinate.
X * The screen database is also updated.
X */
Xremoveobject(row, col)
X unsigned int row; /* row to remove object from */
X unsigned int col; /* column to remove object from */
X{
X register struct cell *cc; /* cell location */
X register struct object *obj; /* object to remove */
X
X if ((row >= ROWS) || (col >= COLS)) panic("removeloc");
X cc = &board[row][col];
X obj = cc->c_obj;
X if (obj == NULL) return;
X adjustdata(obj, -1);
X obj->o_count--;
X obj->o_cell = NULL;
X cc->c_obj = NULL;
X dpyplace(row, col, ' ');
X}
X
X
X/*
X * Adjust the data counts as necessary when an object is added or removed.
X * Delta is 1 if the object is being added, and -1 if the object is
X * being removed.
X */
Xadjustdata(obj, delta)
X register struct object *obj; /* object being added/removed */
X register int delta; /* change to be made */
X{
X register struct data *dp; /* pointer to appropriate data */
X register int flags; /* flag bits */
X register int deltalife; /* total life change for object */
X
X dp = &hisdata;
X if (obj->o_side == myside) dp = &mydata;
X flags = obj->o_flags;
X deltalife = (obj->o_life * delta);
X if ((flags & F_IMMOB) == 0) { /* movable men */
X dp->d_movemen += delta;
X dp->d_movelife += deltalife;
X }
X if (flags & F_FIGHT) { /* fighting men */
X dp->d_fightmen += delta;
X dp->d_fightlife += deltalife;
X }
X if (flags & F_BLAST) { /* blasting men */
X dp->d_blastmen += delta;
X dp->d_blastlife += deltalife;
X }
X if (flags & F_GOAL) { /* goals */
X dp->d_goalmen += delta;
X dp->d_goallife += deltalife;
X }
X if ((flags & F_WALL) == 0) { /* total men */
X dp->d_totalmen += delta;
X dp->d_totallife += deltalife;
X }
X if (flags & F_WALL) { /* walls */
X dp->d_totalwalls += delta;
X }
X newstat = 1;
X}
X
X
X/*
X * Pick one of a specified set of directions from a cell.
X * Each applicable direction is tested using a supplied routine.
X * The routine returns nonzero if the cell in a direction is acceptable.
X * Returns the cell in the picked direction.
X */
Xstruct cell *
Xpickdir(cc, dir, routine)
X register struct cell *cc; /* cell to move from */
X int dir; /* direction to pick */
X int (*routine)(); /* routine to decide */
X{
X register struct cell *tc; /* test cell */
X register int count; /* number of enemies */
X register int i; /* index */
X struct cell *table[4]; /* possible directions */
X
X switch (dir) {
X case 'h': tc = cc->c_left; break;
X case 'j': tc = cc->c_down; break;
X case 'k': tc = cc->c_up; break;
X case 'l': tc = cc->c_right; break;
X case 'a': tc = NULL; break;
X default: return(NULL);
X }
X if (tc) { /* try specified direction */
X if (routine(tc) == 0) tc = NULL;
X return(tc);
X }
X for (count = 0, i = 0; i < 4; i++) { /* search all directions */
X tc = cc->c_dirs[i];
X if (routine(tc)) table[count++] = tc;
X }
X if (count == 0) return(NULL);
X if (count > 1) count = (random() % count) + 1;
X return(table[count - 1]);
X}
X
X
X/*
X * Remove one hit point from an object at a given location.
X * If it goes to zero, kill the object.
X */
Xhitobject(row, col)
X{
X register struct object *obj;
X
X obj = board[row][col].c_obj;
X if (obj == NULL) return;
X adjustdata(obj, -1);
X obj->o_life--;
X adjustdata(obj, 1);
X if (obj->o_life <= 0) removeobject(row, col);
X newstat = 1;
X}
X
X
X/*
X * Write a single bell to warn the user of an error
X */
Xbeep()
X{
X write(STDERR, "\7", 1);
X}
X
X
X/*
X * Fatal error routine
X */
Xpanic(str)
X char *str;
X{
X dpyclose();
X fprintf(stderr, "Fatal error: %s\n", str);
X exit(1);
X}
X
X
X/*
X * Reset the terminal modes and exit
X */
Xquit(status)
X{
X dpyclose();
X exit(status);
X}
X
X
X/*
X * Here on an interrupt to quit.
X */
Xsigint()
X{
X dpyclose();
X exit(0);
X}
//E*O*F main.c//
echo x - scan.c
sed -e 's/^X//' > "scan.c" << '//E*O*F scan.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)scan.c 1.1 4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X/* Module to read self-terminating input while allowing editing of the input */
X
X#include "war.h"
X
Xjmp_buf *scanjumpbuf; /* jump buffer to use in scanchar */
Xchar scanbuffer[SCAN_SIZE+1]; /* storage for characters */
Xchar *scanreadptr; /* current read pointer */
Xchar *scanwriteptr; /* current write pointer */
Xchar (*scanroutine)(); /* routine to read characters */
Xstatic char rubchar; /* erase letter character */
Xstatic char rubword; /* erase word character */
Xstatic char rubline; /* erase line character */
Xstatic char litchar; /* literal input */
X
X
X/*
X * Initialize for later calls to scanchar.
X */
Xscaninit(routine, jumpbuf)
X char (*routine)(); /* routine to get characters */
X jmp_buf *jumpbuf; /* jump buffer to use later */
X{
X struct sgttyb sgbuf; /* basic tty structure */
X struct ltchars ltbuf; /* local tty structure */
X
X scanroutine = routine; /* init static variables */
X scanjumpbuf = jumpbuf;
X scanwriteptr = scanbuffer;
X scanreadptr = scanbuffer;
X sgbuf.sg_erase = CERASE; /* set defaults in case ioctls fail */
X sgbuf.sg_kill = CKILL;
X ltbuf.t_werasc = CWERASE;
X ltbuf.t_lnextc = CLNEXT;
X ioctl(STDIN, TIOCGETP, &sgbuf); /* get and save editing characters */
X ioctl(STDIN, TIOCGLTC, <buf);
X rubchar = sgbuf.sg_erase;
X rubline = sgbuf.sg_kill;
X rubword = ltbuf.t_werasc;
X litchar = ltbuf.t_lnextc;
X}
X
X
X/*
X * Read the next input character. If it is an editing character,
X * abort the current context and longjmp back to the last setjmp.
X * NOTE: for proper results, the caller should not alter the global
X * state until the full command has been read in. This includes such
X * things as prompting for input or saving values. Otherwise, improper
X * results will occur if the user edits the command.
X */
Xscanchar()
X{
X register int ch; /* current character */
X
Xloop: if (scanreadptr < scanwriteptr) /* get saved char if have any */
X return(*scanreadptr++);
X ch = scanroutine() & 0x7f; /* get new character */
X if (ch == litchar) { /* literal input */
X ch = scanroutine() & 0x7f;
X goto store;
X }
X if (ch == rubchar) { /* character erase */
X if (scanwriteptr <= scanbuffer) {
X write(STDERR, "\007", 1);
X goto loop;
X }
X scanwriteptr--;
X scanreadptr = scanbuffer;
X longjmp(scanjumpbuf, SCAN_EDIT);
X }
X if (ch == rubword) { /* word erase */
X if (scanwriteptr <= scanbuffer) goto loop;
X while ((--scanwriteptr >= scanbuffer) &&
X ((*scanwriteptr == ' ') || (*scanwriteptr == '\t'))) ;
X scanwriteptr++;
X while ((--scanwriteptr >= scanbuffer) &&
X ((*scanwriteptr != ' ') && (*scanwriteptr != '\t'))) ;
X scanwriteptr++;
X scanreadptr = scanbuffer;
X longjmp(scanjumpbuf, SCAN_EDIT);
X }
X if (ch == rubline) { /* line erase */
X if (scanwriteptr <= scanbuffer) goto loop;
X scanwriteptr = scanbuffer;
X scanreadptr = scanbuffer;
X longjmp(scanjumpbuf, SCAN_EDIT);
X }
X
Xstore: if (scanwriteptr >= scanbuffer + SCAN_SIZE) {
X write(STDERR, "\007", 1);
X goto loop;
X }
X *scanwriteptr++ = ch;
X return(*scanreadptr++);
X}
X
X
X/* Abort reading of the current command */
Xscanabort()
X{
X scanreadptr = scanbuffer;
X scanwriteptr = scanbuffer;
X longjmp(scanjumpbuf, SCAN_ABORT);
X}
X
X
X/* Indicate no more characters ready yet */
Xscaneof()
X{
X scanreadptr = scanbuffer;
X longjmp(scanjumpbuf, SCAN_EOF);
X}
X
X
X/* Simply reset input and output pointers without longjmping */
Xscanreset()
X{
X scanreadptr = scanbuffer;
X scanwriteptr = scanbuffer;
X}
//E*O*F scan.c//
echo x - talk.c
sed -e 's/^X//' > "talk.c" << '//E*O*F talk.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)talk.c 1.1 4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/socket.h>
X#include <utmp.h>
X#include "war.h"
X
X#define NAMELEN (sizeof(ud.ut_name)) /* length of name field */
X#define LINELEN (sizeof(ud.ut_line)) /* length of line field */
X#define DEVLEN (sizeof(dev) - 1) /* length of device string */
X#define FULLEN (LINELEN + DEVLEN + 1) /* length of full tty name */
X#define INFO 20 /* number of messages */
X
Xchar utmp[] = "/etc/utmp"; /* logged in user file */
Xchar dev[] = "/dev/"; /* prefix for tty names */
Xint sd; /* descriptor for socket */
Xint serverflag; /* nonzero if we are the server */
Xstruct sockaddr addr; /* name of our socket */
Xint infocount; /* number of queued messages */
Xstruct info info[INFO]; /* messages queued for sending */
X
X
X/*
X * Set the socket address used for connecting to the other player.
X * We find the other player by his user name (and his tty name if necessary).
X * The address is generated using the device numbers of our two terminals.
X * Exits if an error is detected. If successful, saves the socket address
X * and remembers whether we are to be the server or the client.
X */
Xfindplayer(user, tty)
X register char *user; /* other player's user name */
X register char *tty; /* other player's tty name, or NULL */
X{
X register FILE *fd; /* file pointer */
X register char *shortname; /* short tty name */
X register int count; /* times user is logged in */
X int sawme; /* true if saw myself */
X int dev1; /* device of first tty */
X int dev2; /* device of second tty */
X struct stat sb; /* file status */
X struct utmp ud; /* utmp entry */
X char fullname[FULLEN]; /* full tty name */
X
X if (fstat(STDERR, &sb)) { /* we aren't on a tty */
X perror("stderr");
X exit(1);
X }
X if ((sb.st_mode & S_IFMT) != S_IFCHR) {
X fprintf(stderr, "stderr: not a character device\n");
X exit(1);
X }
X dev1 = sb.st_rdev;
X fd = fopen(utmp, "r");
X if (fd == NULL) {
X perror(utmp);
X exit(1);
X }
X strcpy(fullname, dev);
X shortname = &fullname[DEVLEN];
X shortname[LINELEN] = '\0';
X count = 0;
X sawme = 0;
X while (fread(&ud, sizeof(ud), 1, fd)) {
X if (ud.ut_name[0] == '\0') continue;
X if (strncmp(user, ud.ut_name, NAMELEN)) continue;
X strncpy(shortname, ud.ut_line, LINELEN);
X if (stat(fullname, &sb) < 0) continue;
X if ((sb.st_mode & S_IFMT) != S_IFCHR) continue;
X if (sb.st_rdev == dev1) { /* see if this is myself */
X sawme = 1;
X continue;
X }
X dev2 = sb.st_rdev;
X count++;
X if ((tty == NULL) || strcmp(tty, shortname)) continue;
X count = 1; /* found user on tty */
X break;
X }
X fclose(fd);
X if (count <= 0) {
X if (sawme)
X fprintf(stderr, "you are not logged in elsewhere\n");
X else
X fprintf(stderr, "%s is not logged in\n", user);
X exit(1);
X }
X if (tty && strcmp(tty, shortname)) {
X fprintf(stderr, "%s is not logged in on %s\n", user, tty);
X exit(1);
X }
X if (count > 1) {
X fprintf(stderr, "%s is logged in more than once\n", user);
X exit(1);
X }
X if (dev1 > dev2) { /* order the devices */
X count = dev1;
X dev1 = dev2;
X dev2 = count;
X serverflag = 1;
X }
X sprintf(addr.sa_data, "WAR.%d.%d", dev1, dev2);
X}
X
X
X
X/*
X * Initialize to talk to the other player.
X */
Xtalkinit()
X{
X if (chdir(GAMEDIR)) { /* go to right place */
X perror(GAMEDIR);
X exit(1);
X }
X dpymove(-1, 1);
X dpystr("Waiting for other player...");
X dpyhome();
X dpyupdate();
X if (serverflag) { /* we are the server */
X serverinit();
X dpymove(-1, 1);
X dpyclrwindow();
X sendboard();
X } else { /* we are the client */
X clientinit();
X dpymove(-1, 1);
X dpyclrwindow();
X readinfo();
X sendboard();
X }
X beep();
X playing = 1;
X newstat = 1;
X}
X
X
X/*
X * Here if we are the server process, to create the binding.
X * Wait for a connection from the client.
X */
Xserverinit()
X{
X register int s; /* socket descriptor */
X int dummylen; /* dummy length */
X struct sockaddr dummyaddr; /* name of our socket */
X
X s = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); /* create socket */
X if (s < 0) {
X perror("socket");
X exit(1);
X }
X unlink(addr.sa_data); /* remove binding name */
X if (bind(s, &addr, sizeof(addr)) < 0) { /* declare ourself */
X perror("bind");
X exit(1);
X }
X if (listen(s, 1) < 0) { /* allow one connection */
X perror("listen");
X exit(1);
X }
X dummylen = sizeof(dummyaddr);
X sd = accept(s, &dummyaddr, &dummylen);
X if (sd < 0) {
X perror("accept");
X exit(1);
X }
X close(s);
X}
X
X
X/*
X * Here if we are the client process, to connect to the binded address.
X * We just continuously try to connect to the address.
X */
Xclientinit()
X{
X sd = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); /* create socket */
X if (sd < 0) {
X perror("socket");
X exit(1);
X }
X while (connect(sd, &addr, sizeof(addr)) < 0) {
X if ((errno != ECONNREFUSED) && (errno != ENOENT)) {
X perror("connect");
X exit(1);
X }
X sleep(1);
X }
X}
X
X
X/*
X * Send the initial board layout
X */
Xsendboard()
X{
X register struct cell *cc; /* current cell within row */
X register struct object *obj; /* object at this location */
X
X for (cc = homecell; cc; cc = cc->c_next) {
X obj = cc->c_obj;
X if ((obj == NULL) || (obj->o_side != myside)) continue;
X sendinfo('p', cc->c_row, cc->c_col, obj->o_id);
X }
X sendinfo('r', 0, 0, 0);
X}
X
X
X/*
X * Send an information message to the other player.
X * Successive message are buffered up and sent together in one write.
X * All pending messages are flushed when an 'r' message type is used.
X */
Xsendinfo(type, row, col, id)
X register int type; /* message type */
X unsigned int row;
X unsigned int col;
X{
X register struct info *ip; /* pointer to current info block */
X
X if ((row >= ROWS) || (col >= COLS)) panic("badsend");
X ip = &info[infocount++];
X ip->i_type = type;
X ip->i_row = row;
X ip->i_col = col;
X ip->i_id = id;
X if ((type != 'r') && (infocount < INFO))
X return; /* wait till later */
X type = (infocount * sizeof(struct info));
X if (write(sd, info, type) != type) {
X dpyclose();
X fprintf(stderr, "other player quit\n");
X exit(0);
X }
X infocount = 0;
X}
X
X
X/*
X * Read and process commands from the other player until we are released
X * with a ready command. We transform incoming id's and row numbers.
X */
Xreadinfo()
X{
X register int row; /* row of interest */
X register int col; /* column of interest */
X register int id; /* id of object */
X register struct cell *cc; /* current cell */
X register struct object *obj; /* current object */
X struct info info; /* command being read */
X
X while (1) {
X id = read(sd, &info, sizeof(info));
X if (id <= 0) {
X dpyclose();
X fprintf(stderr, "other player quit\n");
X exit(0);
X }
X if (id != sizeof(info)) panic("short read from socket");
X id = info.i_id ^ 1; /* toggle id */
X row = (ROWS - 1) - info.i_row; /* and row number */
X col = info.i_col;
X if ((row >= ROWS) || (col >= COLS)) panic("bad coordinates");
X cc = &board[row][col];
X obj = cc->c_obj;
X
X switch (info.i_type) {
X
X case 'b': /* wall got blasted */
X if ((obj == NULL) || ((obj->o_flags & F_WALL) == 0))
X panic("blasting non-wall");
X removeobject(row, col);
X break;
X
X case 'h': /* man got hit */
X if ((obj == NULL) || (obj->o_side != myside))
X panic("missed me");
X hitobject(cc->c_row, cc->c_col);
X break;
X
X case 'p': /* object got placed or moved */
X if (obj) panic("nested objects");
X obj = findid(id);
X if (obj->o_side == myside) panic("read my own object");
X placeobject(obj, row, col);
X break;
X
X case 'r': /* ready for other player */
X return;
X
X default:
X panic("bad command read from opponent\n");
X }
X }
X}
//E*O*F talk.c//
echo x - view.c
sed -e 's/^X//' > "view.c" << '//E*O*F view.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)view.c 1.1 4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include "war.h"
X
X/*
X * See what is currently visible by all of our men.
X */
Xview()
X{
X register struct cell *cc; /* current cell */
X register struct cell *tc; /* test cell */
X register struct object *obj; /* object doing viewing */
X register int row; /* current row */
X register int col; /* current column */
X register int view; /* viewing distance */
X int objrow; /* current row of object */
X int objcol; /* current column of object */
X int minrow; /* minimum row for search */
X int maxrow; /* maximum row for search */
X int mincol; /* minimum column for search */
X int maxcol; /* maximum column for search */
X
X /*
X * Remove visibility of movable objects
X */
X for (obj = objects; obj < endobjects; obj++) {
X if (obj->o_side == myside) continue;
X if (obj->o_flags & (F_IMMOB|F_VIS)) continue;
X cc = obj->o_cell;
X if ((cc == NULL) || (cc->c_seen == 0)) continue;
X cc->c_seen = 0;
X dpyplace(cc->c_row, cc->c_col, ' ');
X }
X /*
X * See what is visible now
X */
X for (cc = firstcell; cc; cc = cc->c_next) {
X obj = cc->c_obj;
X if ((obj == NULL) || (obj->o_side != myside)) continue;
X view = obj->o_view;
X if (view <= 0) continue;
X objrow = cc->c_row;
X objcol = cc->c_col;
X minrow = objrow - view;
X maxrow = objrow + view;
X mincol = objcol - view;
X maxcol = objcol + view;
X if (minrow < 0) minrow = 0;
X if (maxrow >= ROWS) maxrow = ROWS - 1;
X if (mincol < 0) mincol = 0;
X if (maxcol >= COLS) maxcol = COLS - 1;
X for (row = minrow; row <= maxrow; row++) {
X col = mincol;
X tc = &board[row][col];
X for (; col <= maxcol; col++, tc = tc->c_right) {
X if (tc->c_obj == NULL) continue;
X if (tc->c_seen) continue;
X if (tc->c_obj->o_side == myside) continue;
X if (((obj->o_flags & F_XRAY) == 0)
X && obstruct(objcol, objrow, col, row))
X continue;
X tc->c_seen = 1;
X dpyplace(row, col, tc->c_obj->o_altch);
X }
X }
X }
X viewstats();
X dpyupdate();
X}
X
X
X/*
X * Given a rectangle specified by its opposite corners (a,b) and (c,d).
X * Check whether there exists any object in the rectangle which is within
X * a distance of 1/2 from the line segment connecting the two corners (the
X * corners themselves are not tested). The equation for those points (x,y)
X * which satisfy this distance requirement is:
X *
X * 4 * square((b-d)x + (c-a)y + (ad-bc)) <= square(b-d) + square(c-a).
X *
X * By precomputing the constant parts of this equation, it becomes:
X *
X * 4 * square(Ax + By + C) <= D.
X *
X * Returns nonzero if some object is within this distance.
X */
Xobstruct(a, b, c, d)
X{
X register int x; /* x coordinate of test point */
X register int y; /* y coordinate of test point */
X register int i; /* number being computed */
X register struct cell *cc; /* current cell being checked */
X struct cell *endc1; /* cell at first endpoint */
X struct cell *endc2; /* cell at last endpoint */
X int A; /* (b - d) */
X int B; /* (c - a) */
X int C; /* (ad - bc) */
X int D; /* (square(b-d) + square(c-a)) */
X int dy; /* delta for y coordinate */
X
X if (a > c) { /* put first point at left */
X i = c;
X c = a;
X a = i;
X }
X dy = 1; /* see if line rises or falls */
X if (d < b) dy = -1;
X A = b - d; /* compute constants */
X B = c - a;
X C = (a * d) - (b * c);
X D = (A * A) + (B * B);
X endc1 = &board[b][a]; /* get cells at endpoints */
X endc2 = &board[d][c];
X for (y = b; ; y += dy) {
X for (x = a, cc = &board[y][x]; x <= c; x++, cc = cc->c_right) {
X if (cc == endc2) return(0);
X if ((cc->c_obj == NULL) || (cc == endc1)) continue;
X if (cc->c_obj->o_flags & F_TRANS) continue;
X i = (A * x) + (B * y) + C;
X if ((i * i * 4) <= D) return(1);
X }
X }
X}
X
X
X/*
X * Update the status information if necessary
X */
Xviewstats()
X{
X
X if (newstat == 0) return;
X viewobjects();
X viewdata();
X dpywindow(0, -1, 1, INFOCOL - 2);
X newstat = 0;
X}
X
X
X/*
X * Give comparisons between our men and the enemy
X */
Xviewdata()
X{
X register struct data *md; /* pointer to my data */
X register struct data *hd; /* pointer to his data */
X
X dpywindow(DATAROW, -1, INFOCOL, -1);
X md = &mydata;
X hd = &hisdata;
X dpyprintf("STATISTIC YOU ENEMY\n");
X dpyprintf("fight men/life %2d/%-3d %2d/%d\n",
X md->d_fightmen, md->d_fightlife,
X hd->d_fightmen, hd->d_fightlife);
X dpyprintf("move men/life %2d/%-3d %2d/%d\n",
X md->d_movemen, md->d_movelife,
X hd->d_movemen, hd->d_movelife);
X dpyprintf("blast men/life %2d/%-3d %2d/%d\n",
X md->d_blastmen, md->d_blastlife,
X hd->d_blastmen, hd->d_blastlife);
X dpyprintf("goal men/life %2d/%-3d %2d/%d\n",
X md->d_goalmen, md->d_goallife,
X hd->d_goalmen, hd->d_goallife);
X dpyprintf("total men/life %2d/%-3d %2d/%d\n",
X md->d_totalmen, md->d_totallife,
X hd->d_totalmen, hd->d_totallife);
X dpyprintf("total walls %-3d %-3d\n",
X md->d_totalwalls, hd->d_totalwalls);
X dpyclrwindow();
X}
X
X
X/*
X * Give statistics on our objects
X */
Xviewobjects()
X{
X register struct object *obj; /* current object */
X register char *cp; /* current flag character */
X register int flags; /* current flags */
X char flagchars[20]; /* flags for object */
X char count[20]; /* value for counts */
X
X dpywindow(0, DATAROW - 2, INFOCOL, -1);
X dpystr("OBJ LIFE VIEW FLAGS COUNT\n");
X for (obj = objects; obj < endobjects; obj++) {
X if (obj->o_side != myside) continue;
X if (playing && (obj->o_count == 0)) continue;
X cp = flagchars;
X flags = obj->o_flags;
X if (flags & F_BLAST) *cp++ = 'b';
X if (flags & F_FIGHT) *cp++ = 'f';
X if (flags & F_GOAL) *cp++ = 'g';
X if (flags & F_IMMOB) *cp++ = 'i';
X if (flags & F_TRANS) *cp++ = 't';
X if (flags & F_VIS) *cp++ = 'v';
X if (flags & F_WALL) *cp++ = 'w';
X if (flags & F_XRAY) *cp++ = 'x';
X *cp = '\0';
X cp = count;
X if (playing)
X sprintf(cp, "%d ", obj->o_count);
X else if (obj->o_min == obj->o_max)
X sprintf(cp, "%d ", obj->o_min);
X else if (obj->o_max >= 1000)
X sprintf(cp, "%d-INF", obj->o_min);
X else
X sprintf(cp, "%d-%d", obj->o_min, obj->o_max);
X dpyprintf("%c %c %3d %4d %-7s%s\n",
X obj->o_ownch, obj->o_altch, obj->o_life,
X obj->o_view, flagchars, count);
X }
X dpyclrwindow();
X}
//E*O*F view.c//
echo done
--
Erik Bailey -- 7 Oak Knoll (USENET courtesy of
ihnp4!think!ejb Arlington, MA 02174 Thinking Machines Corp.
ejb@think.com (617) 643-0732 Cambridge, MA)
It takes thought to make a program that thinks.
But it takes work to make a program that works.