[net.sources.games] WAR Part 2 *REPOSTING*

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, &ltbuf);
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.