[net.sources] Warp for folks w/o FIONREAD

estes@tty3b.UUCP (08/24/83)

	I've spent the past few lunch hours hacking on Warp 6.0 to compensate
for the lack of "ioctl(fd,FIONREAD,&count)".  The following is a modified
source for warp.c.  Make sure that -DSPEC is added to Makefile.  I am assuming
that interested parties already have the other parts of the distribution kit.

	Other parts fixed:

	#defined index(s,c) to strchr(s,c) for those on UNIX 4.0 and 5.0.

	Changed velx and vely in OBJECT to int's because on a 3B20 char
	is unsigned, which causes all sorts of problems.

	Changed the fullname routine to look for quotes around the name,
	since that's how things are in our /etc/passwd.

	Put in new macros using ioctl() with termio.h instead of stty()/
	gtty() with sgtty.h.

	All changes are #ifdef'd and I suggest you peruse them.  Please
let me know about any other changes you find useful beyond these.

	Circumventing the FIONREAD problem produced a bit of a kluge,
but I couldn't come up with a better fix.  I cannot guarantee the results
and resume no responsibility for anything.

Ted Estes
Teletype Corp, Skokie, IL
{ihnp4, otuxa, we13}!tty3b!estes


**********************
static char	warpid[] = "Warp 6.0 (c) 1983 Larry Wall";

/*	warp -- a real-time space war program
 *	author: Larry Wall
 *	helpers: Jonathan and Mark Biggar
 *	special thanks to my sweetie Gloria who suggested the Planet Crusher
 *	and to Norman Azadian, who keeps asking embarrassing questions.
 *
 *	Thanks also to that wonderful parallel processor, Usenet.
 *
 *	Copyright 1983, Larry Wall
 *
 *	This program may be copied as long as this copyright notice is
 *	included, and as long as it is not being copied for purposes
 *	of profit.  If you want to modify this program in any way other
 *	than normal configuration changes, common decency would suggest
 *	that you also modify the name of the program so that my good name
 *	(what there is of it) is not impugned.  (Calling it something like
 *	"warpx" or "superwarp" would be fine.)  Also, give it another
 *	WARPDIR so that the scoreboards don't get confused.
 *
 * version 5.0  04/20/83
 *         5.1  05/05/83	various tidbits
 *	   5.2  05/12/83	VAX -> vax, ifdef'ed a SIGCONT
 *	   5.3  05/24/83	RCS
 *
 * $Log:	/ea/lwall/src/warp/warp.c%v $
 * Revision 6.0  83/08/08  17:09:26  lwall
 * New baseline version for net release.
 * 
 * Revision 5.5  83/08/01  10:59:56  lwall
 * Cloaking for the Enterprise.
 * Difficulty now goes to 99, and many activities depending on difficulty
 *     have been adjusted in frequency.
 * Simplified exit sequence, and reduced dependencies on control
 *     characters.  You needn't see the scoreboard if you don't want to.
 * Hitting i,w,c, or v switches to Enterprise.  Hitting p switches to Base.
 * Excessive use of q is not allowed.
 * Excessive use of D is not allowed.
 * Scoreboard may depend on either full name or login name.
 * Integrated scoreboard lister.  Login name now shows up on scoreboard.
 * "Hidden" startup options are now upper case.
 * Checks upon startup for no cursor movement, or screen too small.
 * Checks upon startup that WARPDIR is correctly protected, and that warp
 *     is running setuid.  As an additional bonus this prevents root from
 *     running warp, which mucks things up, UN*X be blessed.
 * All gets's turned into fgets's for safety.
 * Bonus Enterprises and Bases.
 * Escalating bonuses for saving Base and Enterprise.
 * Escalating Enterprise energy.
 * Turbolasers decrease with distance.
 * Really smart enemies can see through stars occasionally.
 * Occasional Tholian jackpot waves.  Tholians are a trifle nastier.
 * Choleric Gorns.
 * An O or o can miss seeing you.  Enemies can avoid a stationary O, o, or X.
 * Warp 3 enemies and other nastinesses are possible in massacre mode.
 * Enemies that decide to navigate when they see you can do other things than
 *     just come toward you.
 * Gorns occasionally launch a salvo for the fun of it.
 * Only star and enemy explosions can keep the round going now.
 * Bounces don't always go back to starting spot now.
 * Better full name processing.  USG quirks handled.  & substitution also
 *     handled now (whoever dreamed up that one must have been in the middle
 *     of the night before the morning after).
 * Catch ^D on fgets.
 * Version number printer.
 * Less signal catching during debugging.
 * 
 * Revision 5.4  83/06/24  09:28:38  lwall
 * 16 bit random number generators are now supported.
 * Made warp not blow up on a null save file.
 * Warp now prints E and B before the stars.
 * Fixed bug which caused torp count to get decremented even when no torp
 *     was launched because of an obstacle.
 * Put %<n>ld formats where appropriate.
 * Fixed E: 0  0 bug on refresh.
 * 
 * Revision 5.3  83/05/24  14:03:10  lwall
 * Starting RCS
 * 
 */

#include <stdio.h>
#include <signal.h>
#ifndef SPEC
#include <sgtty.h>
#else
#include <sys/termio.h>
#endif
#include <math.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>

#define bool char
#define TRUE (1)
#define FALSE (0)

/* machine dependent stuff starts here */

#define HAVETERMLIB 1
#define TCSIZE 256

#ifdef FTIMER
#include <sys/timeb.h>
#endif

/* WARPDIR must be readable and writeable by warp, but not by anyone who you
 * don't trust.  In other words, to set up warp so everyone can play and
 * no one can cheat, give warp a uid of its own and make warp setuid to
 * that uid.  WARPDIR must then NOT be made writeable by the world,
 * since no attempt is made to encrypt saved games or anything.
 * (It must be readable by the world, however, due to a strangeness in
 * access.)
 */
/* definition of WARPDIR comes from Makefile and must begin with " */

#define SAVEDIR WARPDIR/"
#define NEWSFILE WARPDIR/warp.news"
#define HELPFILE WARPDIR/warp.doc"
#define LOCKFILE WARPDIR/.warp.lock"
#define LOGFILE WARPDIR/warp.log"
#define SCOREBOARD WARPDIR/warp.top"
#define LSCOREBOARD WARPDIR/warp.lowtop"
#define TMPSCOREBOARD WARPDIR/warp.topnew"

#define PERMMAPS 8	/* how many starmaps are permanent */
#define MAPS 20		/* how many starmaps to choose from */
			/* (MAPS - PERMMAPS is # of half-gone universes) */
/*
 * Screen size info, minimum screen size is 23x40 (actually 24x80).
 * YSIZE and XSIZE should be relatively prime so that a torpedo launched
 * at an angle will eventually cover the whole screen.
 * To calculate a new position for something:
 * new_position = (current_position + delta + ?SIZE00) % ?SIZE
 * This allows for negative deltas of up to ?SIZE00 (% doesn't work right
 * on negative numbers).
 * ?SIZE01, etc. are fudges for efficiency--they already include a delta.
 */

#define XYSIZE 920
#define XYSIZEx4 3680

#define YSIZE   23
#define YSIZE00 2300
#define YSIZE01 2301
#define YSIZE99 2299

#define XSIZE   40
#define XSIZE00 4000
#define XSIZE01 4001
#define XSIZE99 3999
#define XSIZE02 4002
#define XSIZE98 3998
#define XSIZE03 4003
#define XSIZE97 3997
#define XSIZE08 4008
#define XSIZE92 3992

#define BREAKCH '\0'
#define FUNCTCH '\01'

#define ENTBOUNDARY 100000	/*  point boundary across which a new E is
					awarded */

#define BASEBOUNDARY 250000	/*  point boundary across which a new B is
					awarded */

char INTRCH = '\03';

/* if your rand() produces only 16 bits, define RAND16 */

#ifdef RAND16	/* 16 bits of rand()? */
#define RANDRAND 1073741824.0 /* that's 2**30 */
#define HALFRAND 0x8000 /* that's 2**15 */
unsigned rand();
#define myrand() (rand()&65535)
#define rand_mod(m) ((int)((double)myrand() / 65536.0 * ((double)(m))))
/* pick number in 0..m-1 */

#else		/* assume 31 bits */
#define RANDRAND 1152921504606846976.0 /* that's 2**60 */
#define HALFRAND 0x40000000 /* that's 2**30 */
long rand();
#define myrand() rand()
#define rand_mod(m) ((myrand() / 37) % (m)) /* pick number in 0..m-1 */
/*
 * The reason for the /37 above is that our random number generator yields
 * successive evens and odds, for some reason.
 */
#endif

    /* we get fractions of seconds from calling ftime on timebuf */

#ifdef FTIMER
struct timeb timebuf;
#define roundsleep(x) (ftime(&timebuf),sleep(timebuf.millitm > 500?x+1:x))
#else
#define roundsleep(x) sleep(x)
#endif

int charsperhalfsec;

long iocount;

struct stat filestat;

int real_y = -100, real_x = -100;

#ifdef FIONREAD
/* if you haven't got FIONREAD or a reasonable facsimile, you'll probably
 * have to root around in /dev/kmem.
 */
#define input_pending() (ioctl(0, FIONREAD, &iocount),(int)iocount)
#else

#include <setjmp.h>
int	(*saveint)();
jmp_buf	jmp;

input_pending(ch)
register char *ch;
{
register int num;
int	catch();

	saveint = signal(SIGALRM,catch);
	alarm(1);	/* set alarm */
	if (!setjmp(jmp))
		num = read(0,ch,1);
	else
		num = 0;
	alarm(0);	/* cancel any outstanding alarm */
	signal(SIGALRM,saveint);
	return(num);
}

catch()
{
	longjmp(jmp,1);
}
#endif

/* warp will still work without the following, but may get ahead at low speed */
#ifdef TIOCOUTQ		/* chars left in output queue */
#define output_pending() (ioctl(1, TIOCOUTQ, &iocount),iocount)
#endif

/* If some of the following look something like curses calls, it is because
 * warp used to use curses but doesn't now.  Warp was neither as efficient nor
 * as portable with curses, and since the program had to cheat on curses all
 * over the place anyway, we ripped it out.
 */
#define setimage(of,to) (mvaddch(of->posy+1,of->posx*2,of->image=(to)))

#define mvaddch(y,x,ch) (tmpchr=(ch), move((y),(x),&tmpchr))
#define addch(ch) (tmpchr=(ch), write(1,&tmpchr,1), real_x++)
#define mvaddc(y,x,ch) (move((y),(x),&(ch)))
#define addc(ch) (write(1,&(ch),1), real_x++)
#define addspace() (write(1," ",1), real_x++)
#define mvaddstr(y,x,s) (move((y),(x),(char*)0), tmpstr = (s), tmplen = strlen(tmpstr), write(1, tmpstr, tmplen), real_x += tmplen)

int tmplen;
char *tmpstr;
char tmpchr;

/* The following macros are like the pseudo-curses macros above, but do
 * certain amount of controlled output buffering.
 *
 * NOTE: a beg_qwrite()..end_qwrite() sequence must NOT contain a cursor
 * movement (move), because the move() routine uses beg_qwrite()..end_qwrite()
 * itself.
 */

#define beg_qwrite() (maxcmstring = cmbuffer)
#ifdef vax
#define qwrite() asm("movc3 _gfillen,_filler,*_maxcmstring"); maxcmstring += gfillen
#else
#define qwrite() (movc3(gfillen,filler,maxcmstring), maxcmstring += gfillen)
#endif
#define qaddc(ch) (*maxcmstring++ = (ch), real_x++)
#define qaddch(ch) (*maxcmstring++ = (ch), real_x++)
#define qaddspace() (*maxcmstring++ = ' ', real_x++)
#define end_qwrite() (write(1,cmbuffer,maxcmstring-cmbuffer))

#ifndef SPEC
struct sgttyb _tty;
int _tty_ch = 2, _res_flg;

/* terminal mode diddling routines */

#define raw()	 (_tty.sg_flags|=RAW, stty(_tty_ch,&_tty))
#define noraw()	 (_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty))
#define crmode() (_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty))
#define nocrmode() (_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty))
#define echo()	 (_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty))
#define noecho() (_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty))
#define nl()	 (_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty))
#define nonl()	 (_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty))
#define	savetty() (gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags)
#define	resetty() (_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty))

#else

struct termio _tty;
int	_tty_ch = 2, _res_iflg, _res_lflg;

#define raw()    (_tty.c_lflag &= ~(ICANON|ECHO|ISIG), _tty.c_iflag &= ~(BRKINT|IGNPAR|ICRNL|IXON),\
			       _tty.c_cc[4]=_tty.c_cc[5]=1, ioctl(_tty_ch,TCSETA,&_tty))
#define noraw()  (_tty.c_lflag |= (ICANON|ECHO|ISIG), _tty.c_iflag |= (BRKINT|IGNPAR|ICRNL|IXON),\
				_tty.c_cc[4]=4, _tty.c_cc[5]=0, ioctl(_tty_ch,TCSETA,&_tty))
#define crmode() (_tty.c_lflag &= ~(ICANON|ECHO), _tty.c_iflag |= (BRKINT|IGNPAR|ICRNL),\
				_tty.c_cc[4]=_tty.c_cc[5]=1, ioctl(_tty_ch,TCSETA,&_tty))
#define nocrmode() (_tty.c_lflag |= (ICANON|ECHO|ISIG), _tty.c_iflag |= (BRKINT|IGNPAR|ICRNL|IXON),\
				_tty.c_cc[4]=4, _tty.c_cc[5]=0, ioctl(_tty_ch,TCSETA,&_tty))
#define echo()   (_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA,&_tty))
#define noecho() (_tty.c_lflag &= ~ECHO, ioctl(_tty_ch, TCSETA,&_tty))
#define nl()     (_tty.c_oflag |= OCRNL, ioctl(_tty_ch, TCSETA,&_tty))
#define nonl()   (_tty.c_oflag &= ~OCRNL, ioctl(_tty_ch, TCSETA, &_tty))
#define savetty() (ioctl(_tty_ch, TCGETA, &_tty), _res_lflg = _tty.c_lflag, _res_iflg = _tty.c_iflag)
#define resetty() (_tty.c_lflag = _res_lflg, _tty.c_iflag = _res_iflg, _tty.c_cc[4]=4, _tty.c_cc[5]=0, ioctl(_tty_ch, TCSETA, &_tty))

#endif

/*
 * NOTE: if you don't have termlib you'll have to define these strings,
 *    the tputs routine, and the tgoto routine.
 * The tgoto routine simply produces a cursor addressing string for a given
 * x and y.  The 1st argument is a generic string to be interpreted.
 * If you are hardwiring it you might just ignore the 1st argument.
 * The tputs routine interprets any leading number as a padding factor, possibly
 * scaled by the number of lines (2nd argument), puts out the string (1st arg)
 * and the padding using the routine specified as the 3rd argument.
 */

#ifdef HAVETERMLIB
extern	char *BC;	/* backspace character */
char *ND;		/* non-destructive cursor right */
char *DO;		/* move cursor down one line */
extern	char *UP;	/* move cursor up one line */
char *CL;		/* home and clear screen */
char *CE;		/* clear to end of line */
char *CM;		/* cursor motion */
extern  char PC;	/* pad character for use by tputs() */
extern	short ospeed;	/* terminal output speed, for use by tputs() */
char *tcbuf;		/* temp area for "uncompiled" termcap entry */
char tcarea[TCSIZE];	/* area for "compiled" termcap strings */
int LINES, COLS;	/* size of screen */

/* define a few handy macros */

#define clear() (do_tc(CL,LINES),real_y=real_x=0)
#define erase_eol() do_tc(CE,1)

#else
  ????????		/* up to you */
#endif

/* setting a ??size to infinity forces cursor addressing in that direction */

int CMsize, BCsize = 1, DOsize = 1000, UPsize = 1000, NDsize = 1000;

void hangup_catcher();
#ifdef SIGTSTP
void cont_catcher();
#endif

extern int errno;

bool justonemoretime = TRUE, starspec = FALSE, klingspec = FALSE,
	apolspec = FALSE, crushspec = FALSE, romspec = FALSE, prespec = FALSE,
	tholspec = FALSE, gornspec = FALSE, keepgoing = TRUE,
	beginner = FALSE, massacre = FALSE, bombed_out, panic = FALSE,
	lowspeed = FALSE, debugging = FALSE, experimenting = FALSE,
	scorespec = FALSE, cloaking, cloaked, madgorns;

int inumstars, numstars, inumenemies, numenemies, inumroms, inumthols,
    inumapollos, numapollos, apolloflag, inumcrushes, numcrushes,
    inumgorns, numgorns, deados, smarts, ismarts = 0, numos = 0, numxes = 0,
    numents, numbases, inuminhab, numinhab, wave, cumsmarts, prescene = -1,
    oldstatus, oldetorp, oldbtorp, oldstrs, oldenemies, scandist,
    antibase, sm35, sm45, sm50, sm55, sm80, sm95, entmax, enemshields, super,
    whenok;

long totalscore, lastscore = 0, curscore, possiblescore,
    oldeenergy, oldbenergy, oldcurscore;

char filler[] = {0,'\b',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
     *bsptr = filler+1;

int tractor = 0;

char *homedir, *maxcmstring, cmbuffer[512], spbuf[512];

char loginname[9], realname[25];

#ifdef SPEC
#define	index(s,c)	strchr(s,c)
char *strchr();
#else
char *index();
#endif

char *ttyname(), *malloc(), *ctime(), *strcpy(), *sprintf();
char *getenv(), cmstore(), *tgoto();
int comp_tc();

char savefilename[40];

char term[12];

char gfillen = 25;

#ifdef BYFULLNAME
#define COMPOFF 0
#define COMPNAME realname
#define COMPLEN 24
#else
#define COMPOFF 24
#define COMPNAME longlognam
#define COMPLEN 8
char longlognam[9];
#endif

main(argc,argv)
int argc;
char *argv[];
{
    char tmp, *s, *tmpaddr;

    int i;

    FILE *savfil, *tmpfil;

#ifndef RAND16
    for (i=100; i; i--)
	if (rand() >= 65536)
	    goto rand_ok;
    printf("Recompile with RAND16 defined.\n");
    exit(1);
rand_ok:
#endif
    while (--argc > 0 && (*++argv)[0] == '-')
	for (s = argv[0]+1; *s != '\0'; s++)
	    switch (*s) {
	    case 'A':
		apolspec = TRUE;
		beginner = TRUE;
		break;
	    case 'b':
		beginner = TRUE;
		break;
	    case 'C':
		crushspec = TRUE;
		beginner = TRUE;
		break;
	    case 'D':
		debugging = TRUE;
		break;
	    case 'd':
		s++;
		if (*s == '=') s++;
		ismarts = atoi(s);
		if (ismarts <= 0)
		    ismarts = 1;
		if (ismarts > 99)
		    ismarts = 99;
		if (ismarts > 40)
		    massacre = TRUE;
		s += strlen(s)-1;
		break;
	    case 'E':
		klingspec = TRUE;
		beginner = TRUE;
		s++;
		if (*s == '=') s++;
		inumenemies = atoi(s);
		s += strlen(s)-1;
		break;
	    case 'G':
		gornspec = TRUE;
		beginner = TRUE;
		break;
	    case 'l':
		lowspeed = TRUE;
		break;
	    case 'P':
		prespec = TRUE;
		beginner = TRUE;
		s++;
		if (*s == '=') s++;
		if (*s)
		    prescene = atoi(s);
		else
		    prescene = -1;
		s += strlen(s)-1;
		break;
	    case 'R':
		romspec = TRUE;
		beginner = TRUE;
		break;
	    case 'S':
		starspec = TRUE;
		beginner = TRUE;
		s++;
		if (*s == '=') s++;
		inumstars = atoi(s);
		s += strlen(s)-1;
		break;
	    case 's':
		scorespec = TRUE;
		break;
	    case 'T':
		tholspec = TRUE;
		beginner = TRUE;
		break;
	    case 'x':
		experimenting = TRUE;
		break;
	    case 'v':
		printf("%s\n",warpid);
		break;
	    default:
		fprintf(stderr,"warp: illegal option %c\n", *s);
		fprintf(stderr, "Usage: warp -dn -b -x -v -s\n");
		exit(1);
	    }
    if (argc != 0) {
	fprintf(stderr, "Usage: warp -dn -b -x -v -s\n");
	exit(1);
    }

    savetty();
#ifdef SPEC
    ospeed = _tty.c_cflag & CBAUD;
#else
    ospeed = _tty.sg_ospeed;
#endif

    /* get all that good termcap stuff */

    tcbuf = malloc(1024);		/* make place for termcap entry */
    tgetent(tcbuf,getenv("TERM"));	/* get termcap entry */
    tmpaddr = tcarea;			/* set up strange tgetstr pointer */
    tgetstr("pc",&tmpaddr);		/* get pad character */
    PC = *tcarea;			/* get it where tputs wants it */
    if (!tgetflag("bs")) {		/* is backspace not used? */
	BC = tmpaddr;			/* find out what is */
	tgetstr("bc",&tmpaddr);
    }
    else
	BC = "\b";			/* make a backspace handy */
    ND = tmpaddr;			/* non-destructive cursor right */
    tgetstr("nd",&tmpaddr);
    if (tmpaddr == ND)
	*tmpaddr++ = '\0';
    UP = tmpaddr;			/* move up a line */
    tgetstr("up",&tmpaddr);
    if (tmpaddr == UP)
	*tmpaddr++ = '\0';
    DO = tmpaddr;			/* move down a line */
    tgetstr("do",&tmpaddr);
    if (tmpaddr == DO)
	*tmpaddr++ = '\0';
    if (!*DO) {
	DO = tmpaddr;			/* move down a line */
	tgetstr("nl",&tmpaddr);
	if (tmpaddr == DO)
	    *tmpaddr++ = '\0';
    }
    CL = tmpaddr;			/* get clear string */
    tgetstr("cl",&tmpaddr);
    if (tmpaddr == CL)
	*tmpaddr++ = '\0';
    CE = tmpaddr;			/* clear to end of line string */
    tgetstr("ce",&tmpaddr);
    if (tmpaddr == CE)
	*tmpaddr++ = '\0';
    CM = tmpaddr;			/* cursor motion */
    tgetstr("cm",&tmpaddr);
    if (tmpaddr == CM)
	*tmpaddr++ = '\0';
    LINES = tgetnum("li");		/* lines per page */
    COLS = tgetnum("co");		/* columns on page */
    if (!LINES)
	LINES = 24;
    if (!COLS)
	COLS = 80;
    free(tcbuf);			/* recover 1024 bytes */

    BCsize = comp_tc(bsptr,BC,1);
    BC = bsptr;
    if (!*CM || !BCsize)
	no_can_do("dumb");
    if (LINES < 24 || COLS < 80)
	no_can_do("puny");

    if (!*ND)				/* not defined? */
	NDsize = 1000;			/* force cursor addressing */
    else {
	NDsize = comp_tc(cmbuffer,ND,1);
	ND = malloc((unsigned)NDsize);
	movc3(NDsize,cmbuffer,ND);
	if (debugging) {
	    int scr;

	    printf("ND");
	    for (scr=0; scr<NDsize; scr++)
		printf(" %d",ND[scr]);
	    printf("\n");
	}
    }

    if (!*UP)				/* not defined? */
	UPsize = 1000;			/* force cursor addressing */
    else {
	UPsize = comp_tc(cmbuffer,UP,1);
	UP = malloc((unsigned)UPsize);
	movc3(UPsize,cmbuffer,UP);
	if (debugging) {
	    int scr;

	    printf("UP");
	    for (scr=0; scr<UPsize; scr++)
		printf(" %d",UP[scr]);
	    printf("\n");
	}
    }

    if (!*DO) {				/* not defined? */
	DO = "\n";			/* assume a newline */
	DOsize = 1;
    }
    else {
	DOsize = comp_tc(cmbuffer,DO,1);
	DO = malloc((unsigned)DOsize);
	movc3(DOsize,cmbuffer,DO);
	if (debugging) {
	    int scr;

	    printf("DO");
	    for (scr=0; scr<DOsize; scr++)
		printf(" %d",DO[scr]);
	    printf("\n");
	}
    }
    if (debugging)
	fgets(cmbuffer,sizeof(cmbuffer),stdin);

    CMsize = comp_tc(cmbuffer,tgoto(CM,10,10),0);
    if (PC != '\0') {
	char *p;

	for (p=filler+sizeof(filler)-1;!*p;--p)
	    *p = PC;
    }
    charsperhalfsec = ospeed >= B9600 ? 480 :
		      ospeed == B4800 ? 240 :
		      ospeed == B2400 ? 120 :
		      ospeed == B1200 ? 60 :
		      ospeed == B600 ? 30 :
	      /* speed is 300 (?) */   15;

    gfillen = ospeed >= B9600 ? sizeof(filler) :
	      ospeed == B4800 ? 13 :
	      ospeed == B2400 ? 7 :
	      ospeed == B1200 ? 4 :
				1+BCsize;
    if (ospeed < B2400)
	lowspeed = TRUE;

    umask(022);     /* mustn't rely on incoming umask--could be 033 which */
		    /* would disable people from running wscore */

    strcpy(term,ttyname(2));

    if (stat(SAVEDIR,&filestat)) {
	printf("Cannot access %s\n",SAVEDIR);
	exit(1);
    }
    if (filestat.st_uid != geteuid()) {
	getpw(filestat.st_uid, spbuf);
	s = index(spbuf, ':');
	*s = '\0';
	printf("Warp will not run right without being setuid to %s.\n",spbuf);
	exit(1);
    }
    if ((filestat.st_mode & 0605) != 0605) {
	printf("%s is not protected correctly (must be u+rw o+rx).\n",SAVEDIR);
	exit(1);
    }
    
    getpw(getuid(), spbuf);
    s = index(spbuf, ':');        /* find end of login name */
    *s = '\0';
    strncpy(loginname, spbuf, 8);
    loginname[8] = '\0';

#ifndef BYFULLNAME
    strcpy(longlognam, loginname);
    for (i=strlen(longlognam); i<8; i++)
	longlognam[i] = ' ';	/* make sure it is 8 long for strncmp */
    longlognam[8] = '\0';
#endif
    
    if (scorespec)
	wscore();

    if (!CMsize)			/* no cursor addressing? */
	no_can_do("dumb");

    /* get home directory */

    homedir = getenv("HOME");
    if (homedir == NULL)
	homedir = getenv("LOGDIR");

#if FNAMEALG == 1 || FNAMEALG == 2
    s = index(s+1, ':')+1;        /* skip password */
    s = index(s, ':')+1;        /* skip uid */
    s = index(s, ':')+1;        /* skip gid */
    spbuf[index(s, ':')-spbuf] = '\0';
#if FNAMEALG == 1
    for (tmpaddr=realname; *s && tmpaddr < realname+24; s++,tmpaddr++) {
	if (*s == '&') {
	    *tmpaddr++ = islower(*loginname)?toupper(*loginname):*loginname;
	    strcpy(tmpaddr,loginname+1);
	    tmpaddr += strlen(tmpaddr)-1;
	}
	else
	    *tmpaddr = *s;
    }
    *tmpaddr = '\0';
    realname[24] = '\0';
    if (s = index(realname, ','))
	*s = '\0';
#else
#if FNAMEALG == 2
#ifdef SPEC
    if (tmpaddr = index(s, '"'))
#else
    if (tmpaddr = index(s, '-'))
#endif
	s = tmpaddr+1;
    strncpy(realname, s, 24);
    realname[24] = '\0';
#ifdef SPEC
    if (tmpaddr = index(s, '"'))
#else
    if (s = index(realname, '('))
#endif
	*s = '\0';
#endif
#endif
#else
#if FNAMEALG == 3    
    sprintf(cmbuffer,"%s/.fullname",homedir);
    if ((tmpfil = fopen(cmbuffer,"r")) == NULL) {
	printf("What is your name? ");
	fgets(spbuf,sizeof(spbuf),stdin);
	if (fork())
	    wait(0);
	else {
	    setuid(getuid());
	    if ((tmpfil = fopen(cmbuffer,"w")) == NULL)
		exit(1);
	    fprintf(tmpfil, "%s\n", spbuf);
	    fclose(tmpfil);
	    exit(0);
	}
    }
    else {
	fgets(spbuf,100,tmpfil);
	spbuf[strlen(spbuf)-1] = '\0';
	fclose(tmpfil);
    }
    strncpy(realname, spbuf, 24);
    realname[24] = '\0';
#else
    /* or substitute your favorite fullname algorithm here */
#endif
#endif
    for (i=strlen(realname); i<24; i++)
	realname[i] = ' ';	/* make sure it is 24 long for strncmp */

    sprintf(savefilename, "%ssave.%s", SAVEDIR, loginname);

    savfil = experimenting ? NULL : fopen(savefilename,"r");
    if (savfil != NULL && fgets(spbuf,100,savfil) != NULL) {
	char tmpbuf[80];

	spbuf[strlen(spbuf)-1] = '\0';
	if (fgets(tmpbuf,80,savfil) != NULL) {
	    int processnum;

	    tmpbuf[strlen(tmpbuf)-1] = '\0';
	    printf("You seem to have left a game %s.\n",tmpbuf+9);
	    s = index(tmpbuf+9, ',');
	    *s = '\0';
	    processnum = atoi(s+11);
	    if (kill(processnum, SIGINT)) {
					/* does process not exist? */
					/* (warp ignores SIGINT) */
		printf(
"\nThat process does not seem to exist anymore, so you'll have to start the\n");
		printf(
"last wave over.\n\n");
		printf(
"                      [press return to continue]");
		if (fgets(tmpbuf,sizeof(tmpbuf),stdin) == NULL) {
		    putchar('\n');
		    exit(1);
		}
	    }
	    else {
		if (strcmp(term+8,tmpbuf+23)) {
		    printf(
"That is not your current terminal--you are on %s.\n", term+5);
		    printf("\nYour options:\n");
		    printf("   1) Exit and find the terminal it's running on\n");
		}
		else {
		    printf("\nYour options:\n");
		    printf("   1) Exit and try to foreground it\n");
		}
		printf("   2) Let me terminate the other game\n\n");
		printf("What do you want to do? ");
		if (fgets(tmpbuf,sizeof(tmpbuf),stdin) == NULL) {
		    putchar('\n');
		    exit(1);
		}
		if (tmpbuf[0] == '1') {
		    printf(
"If you don't succeed, come back and do option 2 instead.  Good luck.\n");
		    exit(0);
		}
		printf(
"Ok, hang on a few moments \n");
		fclose(savfil);
		if (kill(processnum, SIGHUP)) {
		    printf("Unable to kill process #%d!\n",processnum);
		    roundsleep(2);
		}
		else {
#ifdef SIGCONT
		    kill(processnum, SIGCONT);
#endif
		    for (i=15; i; --i) {
			sleep(1);
			if (kill(processnum,SIGINT))
					/* does process not exist? */
					/* (warp ignores SIGINT) */
			    break;
		    }
		}
		savfil = fopen(savefilename,"r");
		if (savfil != NULL) {
		    fgets(spbuf,100,savfil);
		}
	    }
	}
    }
    else
	savfil = NULL;
    if (savfil == NULL) {
	totalscore = smarts = cumsmarts = wave = 0;
	numents = 5;
	numbases = 3;
    }
    else {
	totalscore = atoi(spbuf+9);
	smarts = atoi(spbuf+20);
	cumsmarts = atoi(spbuf+24);
	numents = atoi(spbuf+30);
	numbases = atoi(spbuf+33);
	wave = atoi(spbuf+36);
	apolspec = (spbuf[40] == 'a');
	beginner   = (spbuf[41] == 'b');
	crushspec  = (spbuf[42] == 'c');
	gornspec   = (spbuf[43] == 'g');
	massacre   = (spbuf[44] == 'm');
	romspec    = (spbuf[45] == 'r');
	tholspec   = (spbuf[46] == 't');
	lowspeed   = (spbuf[47] == 'l') || lowspeed;
	fclose(savfil);
    }

    if (!ismarts) {
	char buf[10], cmd_buf[80];

	ismarts = 1;
	clear();
	page(NEWSFILE,FALSE);
	if (smarts) {
	    printf("\nSaved game: SCORE DIFF CUMDIFF ENTERPRISES BASES WAVE");
	    printf("\n          %7ld  %2d   %4d        %1d        %1d   %3d",
		totalscore,smarts,cumsmarts,numents,numbases,wave);
	}
re_ask:
	printf("\nWould you like instructions? ");
	if (fgets(buf,sizeof(buf),stdin) == NULL) {
	    putchar('\n');
	    exit(1);
	}
	if (buf[0] == 'v') {
	    printf("%s\n",warpid);
	    goto re_ask;
	}
	if (buf[0] == 'Y' || buf[0] == 'y') {
	    page(HELPFILE,FALSE);
	    printf("\nWould you like to play easy games for a while? ");
	    if (fgets(buf,sizeof(buf),stdin) == NULL) {
		putchar('\n');
		exit(0);
	    }
	    if (buf[0] == 'Y' || buf[0] == 'y') {
		beginner = TRUE;
		lowspeed = TRUE;
	    }
	}
    }
    if (!smarts)
	smarts = ismarts;

#ifndef SIGTSTP
#define sigignore(sig) signal(sig,SIG_IGN)
#define sigset(sig,what) signal(sig,what)
#endif

    sigignore(SIGINT);  /* for inquiry of existence via kill call */
#ifdef SIGTTOU
    sigignore(SIGTTOU);
#endif

    sigset(SIGHUP, hangup_catcher);
    if (!debugging) {
	sigset(SIGQUIT, hangup_catcher);
	sigset(SIGILL, hangup_catcher);
	sigset(SIGFPE, hangup_catcher);
	sigset(SIGBUS, hangup_catcher);
	sigset(SIGSEGV, hangup_catcher);
	sigset(SIGSYS, hangup_catcher);
	sigset(SIGTERM, hangup_catcher);
    }
#ifdef SIGTSTP
    sigset(SIGXCPU, hangup_catcher);
    sigset(SIGCONT, cont_catcher);
#endif

    raw();
    noecho();
    nonl();
    if (totalscore) {
	clear();
	mvaddstr(12,25,"*** restoring saved game ***");
	roundsleep(1);
    }
    srand(getpid());

    do {
	for (keepgoing = TRUE;;) {
	    if (!experimenting) {
		if ((savfil = fopen(savefilename,"w")) == NULL) {
		    resetty();
		    printf("Can't open savefile\n");
		    exit(1);
		}
		fprintf(savfil,
		    "%-8s %10ld, %2d,%5d,%2d,%2d,%3d %c%c%c%c%c%c%c%c\n",
		    loginname, totalscore, smarts, cumsmarts,
		    numents, numbases, wave,
		    apolspec ? 'a' : ' ',
		    beginner   ? 'b' : ' ',
		    crushspec  ? 'c' : ' ',
		    gornspec   ? 'g' : ' ',
		    massacre   ? 'm' : ' ',
		    romspec    ? 'r' : ' ',
		    tholspec   ? 't' : ' ',
		    lowspeed   ? 'l' : ' '
		);
		fprintf(savfil,"         running on %s, process #%d\n",
		    term+5,getpid());
		fclose(savfil);
	    }

	    lastscore = totalscore;
	    initialize();
	    play();
	    cumsmarts += smarts;
	    wavescore();
	    if (!numents && !numbases)
		keepgoing = FALSE;
	  if (!keepgoing) break;
	    do {
		if (experimenting) {
		    mvaddstr(23,15,
		      "      [Hit space to continue, 'q' to quit]       ");
		}
		else {
		    mvaddstr(23,15,
		      "[Hit space to continue, 's' to save, 'q' to quit]");
		}
		sleep(1);
		eat_typeahead();
		if (read(0, &tmp, 1) < 0)
		    hangup_catcher();
		tmp &= 0177;
		if (tmp == BREAKCH || tmp == INTRCH) {
		    mvaddstr(23,15,
		      "                                                 ");
		    mvaddstr(23,33,
		      "Really quit? ");
		    if (read(0, &tmp, 1) < 0)
			hangup_catcher();
		    tmp &= 0177;
		    if (tmp == 'y' || tmp == 'Y')
			tmp = 'q';
		    else
			tmp = 1;
		}
	    } while (tmp != ' ' && tmp != 'q' && tmp != INTRCH &&
		tmp != 's' && tmp != BREAKCH);
	  if (tmp != ' ' && tmp != 's') break;
	    if (!beginner && smarts < 20)
		smarts += 4;
	    else if (!beginner && smarts < 35)
		smarts += 2;
	    else if (smarts < 99)
		smarts++;
	  if (tmp == 's') save_game();
	}
	score();

	smarts = ismarts;
	totalscore = cumsmarts = wave = 0;
	numents = 5;
	numbases = 3;
	apolspec = FALSE;
	beginner   = FALSE;
	crushspec  = FALSE;
	gornspec   = FALSE;
	massacre   = (ismarts >= 40);
	romspec    = FALSE;
	tholspec   = FALSE;

    } while (justonemoretime);

    if (!experimenting)
	unlink(savefilename);

    clear();
    resetty();
}

#define Root 0
#define Base 1
#define Enterprise 2
#define Star 3
#define Torp 4
#define Enemy 5
#define Web 6
#define Crusher 7

typedef struct object {
    char posx, posy;
#ifdef u3b
    int velx, vely;
#else
    char velx, vely;
#endif
    struct object *next, *prev, *contend;
    long energy;
    long mass;
    char type;
    char image;
    char strategy;
} OBJECT;

OBJECT root = {0, 0, 0, 0, &root, &root, 0, 0, 0, Root, '?', 0};

OBJECT free_root = {0, 0, 0, 0, &free_root, &free_root, 0, 0, 0, Root, '?', 0};

OBJECT *ent, *base, *enemies, *movers, *realapollo, *make_object();

OBJECT *occupant[YSIZE][XSIZE];

int finish = 0;

long blast[YSIZE][XSIZE];
bool blasted, xblasted[XSIZE], yblasted[YSIZE];

char    bangy[YSIZE*XSIZE],
	bangx[YSIZE*XSIZE],
	bangs[YSIZE*XSIZE],
	c = ' ';

long    bangm[YSIZE*XSIZE];

int xx[20], yy[20];

int nxtbang;
bool banging;

int etorp, btorp;

int timer;

int status, entmode;

int evely, evelx, bvely, bvelx;

OBJECT *isatorp[2][3][3];

int aretorps;

char cmstore(ch)
register char ch;
{
    *maxcmstring++ = ch;
}

initialize()
{
    long e;
    int yoff, xoff, ypred, xpred;
    register int i, x, y, dist, ydist, xdist;
    char ch;
    FILE *mapfp = NULL;
    bool tmptholspec;
    static char *distname[] =
	{" #"," -"," \\"," /",
	 " |"," *"," `"," '"};

    whenok = 10000;
    cloaking = madgorns = FALSE;
    deados = 0;
    curscore = possiblescore = 0L;
    if (smarts >= 90)
	massacre = TRUE;
    scandist = (massacre?20:15);
    antibase = (smarts>60?1:(smarts>40?2:(smarts>25?4:100)));
    sm35 = (smarts>35?35:smarts);
    sm45 = (smarts>45?45:smarts);
    sm50 = (smarts>50?50:smarts);
    sm55 = (smarts>55?55:smarts);
    sm80 = (smarts>80?80:smarts);
    sm95 = (smarts>95?95:smarts);
    super = (smarts>50?smarts-50:0);
    enemshields = 1 + super/5;
    entmax = (smarts>=75?4000:(smarts>=50?3000:(smarts>=40?2500:2000)));

    clear();
    while (root.next != &root) {
	root.next = root.next->next;
	free_object(root.next->prev);
    }
    root.prev = &root;
    enemies = movers = NULL;
    numos = numxes = 0;
#if defined(vax) && XYSIZEx4 == 3680
    asm("movc5 $0,_occupant,$0,$3680,_occupant");
    asm("movc5 $0,_blast,$0,$3680,_blast");	/* 3680 = XYSIZEx4 */
#else
    for (y=0;y<YSIZE;y++)
	for (x=0;x<XSIZE;x++) {
	    occupant[y][x] = 0;
	    blast[y][x] = 0;
	}
#endif
    for (y=0; y<YSIZE; y++)
	yblasted[y] = FALSE;
    for (x=0; x<XSIZE; x++)
	xblasted[x] = FALSE;
    blasted = FALSE;
    if (!starspec)
	if (smarts < 15)
	    inumstars = 50 + rand_mod(50);
	else
	    inumstars = exdis(800)+ rand_mod(100) + 1;
    tmptholspec = (inumstars < 450 && ! rand_mod(90-sm80));
    if (!klingspec) {
	inumenemies = rand_mod((smarts+1)/2) + 1;
	if (massacre || tmptholspec)
	    inumenemies += 10;
    }
    if (inumenemies+inumstars > YSIZE*XSIZE-20)
	inumstars = YSIZE*XSIZE-20 - inumenemies;
    if (inumstars < 0) {
	inumenemies += inumstars;
	inumstars = 0;
    }
    if (inumenemies < 0)
	inumenemies = 0;
    numstars = inumstars;
    inuminhab = numinhab = 0;
    inumroms = inumthols = inumgorns = 0;
    numapollos = apolspec || massacre ? 1 :
       ((!numstars || rand_mod(2) || smarts < 10) ? 0 : 1);
    inumapollos = apolloflag = 0;
    realapollo = NULL;
    inumcrushes = numcrushes =
	crushspec||massacre?1:(rand_mod(2000) < inumstars);
    inumenemies += inumcrushes;
    numenemies = inumenemies;

    /* do stars */

    if (prespec)
	dist = 4;
    else if (numstars > 550)
	dist = 0;
    else
	dist = rand_mod(starspec||smarts<=5?3:5);
    if (debugging) {
	real_y = real_x = -100;
	putchar('\n');
    }
    switch (dist) {
    case 0:				/* uniform random */
	ydist = xdist = 0;
	if (debugging)
	    printf(" R\n");
	break;
    case 1: case 2:	/* clumped, maybe skewed, maybe superposed */
	ydist = rand_mod(4);
	xdist = rand_mod(2);
	if (debugging)
	    printf("%s\n\r",distname[ydist+4*xdist]);
	yoff = rand_mod(YSIZE);
	xoff = rand_mod(XSIZE);
	dist = (dist==2 ? numstars/2 : 0);
	break;
    case 3: case 4:			/* predefined or residual */
	if (debugging)
	    printf(" P\n");
	dist = 0;
	sprintf(spbuf,"%ssmap.%d",SAVEDIR,
	    (prescene>=0?prescene:rand_mod(MAPS)) );
	if ((mapfp = fopen(spbuf,"r")) != NULL &&
	    fgets(spbuf,10,mapfp) != NULL ) {
	    inumstars = numstars = atoi(spbuf);
	    if (inumenemies+inumstars > YSIZE*XSIZE-20)
		inumstars = numstars = YSIZE*XSIZE-20 - inumenemies;
	    ydist = rand_mod(2) + 4;	/* flip y axis? */
	    xdist = rand_mod(2) + 4;	/* flip x axis? */
	    yoff = rand_mod(YSIZE);	/* how much to shift y */
	    xoff = rand_mod(XSIZE);	/* how much to shift x */
	}
	else
	    ydist = xdist = 0;
	break;
    }
    for (i = 1; i <= numstars; i++) {
	if (dist && i == dist) {	/* flip to another skewing? */
	    ydist = rand_mod(4);
	    xdist = rand_mod(2);
	    if (debugging)
		printf("%s\n",distname[ydist+4*xdist]);
	    yoff = rand_mod(YSIZE);
	    xoff = rand_mod(XSIZE);
	    dist = 0;
	}
	do {				/* until an open spot found */
	    switch (xdist) {
	    case 0:
		x = rand_mod(XSIZE);	/* pick from 0..39, uniform */
		break;
	    case 1: case 2: case 3:
		x = (int)((((double)(myrand()-HALFRAND)) *
		           ((double)(myrand()-HALFRAND))/RANDRAND)
			  * 20.0) + xoff;	/* pick from -20..20, clumped */
		break;
	    case 4:
		if (fscanf(mapfp,"%d %d\n",&ypred,&xpred) == EOF)
		    ydist = xdist = 0;
		x = xpred + xoff;
		break;
	    case 5:
		if (fscanf(mapfp,"%d %d\n",&ypred,&xpred) == EOF)
		    ydist = xdist = 0;
		x = -xpred + xoff;
		break;
	    }
	    switch (ydist) {
	    case 0:
		y = rand_mod(YSIZE);
		break;
	    case 1:
		y = (int)((((double)(myrand()-HALFRAND)) *
		           ((double)(myrand()-HALFRAND))/RANDRAND)
			  * 12.0) + yoff;	/* pick from -12..12, clumped */
		break;
	    case 2:
		y = (int)((((double)(myrand()-HALFRAND)) *
		           ((double)(myrand()-HALFRAND))/RANDRAND)
			  * 12.0) + yoff + x*YSIZE/XSIZE;
				 		/* clumped & skewed */
		break;
	    case 3:
		y = (int)((((double)(myrand()-HALFRAND)) *
		           ((double)(myrand()-HALFRAND))/RANDRAND)
			  * 12.0) + yoff - x*YSIZE/XSIZE;
						/* clumped & skewed */
		break;
	    case 4:
		y = ypred + yoff;
		break;
	    case 5:
		y = -ypred + yoff;
		break;
	    }
	    while (x<0) x += XSIZE00;
	    while (y<0) y += YSIZE00;
	    x %= XSIZE;
	    y %= YSIZE;
	} while (occupant[y][x]);
	e = rand_mod(32768);
	if (e<32000-super*150)
	    ch = '*';
	else {
	    ch = '@';
	    inuminhab = ++numinhab;
	}
	make_object(Star,ch,y,x,0,0,e,e/4,&root);
    }
    if (mapfp != NULL)
	fclose(mapfp);
    if (numcrushes) {
	do {
	    x = rand_mod(XSIZE);
	    y = rand_mod(YSIZE);
	} while (occupant[y][x]);
	movers = make_object(Crusher,'<',y,x,0,1,32767L,32768,&root);
	possiblescore += 10000;
    }
    if (numents) {
	do {
	    x = rand_mod(XSIZE);
	    y = rand_mod(YSIZE);
	} while (occupant[y][x]);
	e = entmax;
	ent = make_object(Enterprise,'E',y,x,0,0,e,e/2,&root);
	if (!movers)
	    movers = ent;
    }
    if (numbases) {
	do {
	    x = rand_mod(XSIZE);
	    y = rand_mod(YSIZE);
	} while (occupant[y][x]);
	e = 10000;
	base = make_object(Base, 'B',y,x,0,0,e,e/4,&root);
	if (!movers)
	    movers = base;
    }
    if (rand_mod(27-sm50/2) && !romspec && !gornspec)
	dist = 27-sm50/2;
    else
	dist = rand_mod(4) + 1;
    for (i = 1+inumcrushes; i <= numenemies; i++) {
	do {
	    x = rand_mod(XSIZE);
	    y = rand_mod(YSIZE);
	} while (occupant[y][x]);
	if (rand_mod(dist)) {
	    if (!tholspec && !tmptholspec && rand_mod((inumstars*3)/sm50+2))
		ch = 'K';
	    else {
		ch = 'T';
		inumthols++;
	    }
	}
	else {
	    if (romspec == gornspec)
		e = 50;
	    else if (gornspec)
		e = 10;
	    else
		e = 90;
	    if (rand_mod(100) < e) {
		ch = 'R';
		inumroms++;
	    }
	    else {
		ch = 'G';
		inumgorns++;
	    }
	}
	if (possiblescore > ENTBOUNDARY - 10000)
	    e = (ENTBOUNDARY - possiblescore) / 5;
	else
	    e = 250 + (sm50-1) * 30 * 20 / numenemies+1;
	e = exdis((int)e) + e - exdis((int)e);
	make_object(Enemy,ch,y,x,0,0,
	    e + rand_mod(super*200+2) + 10000*massacre,e/4,&root);
	e /= 4;
	switch (ch) {
	case 'K':
	    possiblescore += e;
	    break;
	case 'T':
	    possiblescore += e*3/2;
	    break;
	case 'G':
	    possiblescore += e*2;
	    break;
	case 'R':
	    possiblescore += e*3;
	    break;
	}
	if (!enemies)
	    enemies = occupant[y][x];
	if (!movers)
	    movers = occupant[y][x];
    }
    numgorns = inumgorns;
    if (!movers)
	movers = &root;
    if (!enemies)
	enemies = &root;
    if (ent)
	mvaddch(ent->posy+1, ent->posx*2, ent->image);
    if (base)
	mvaddch(base->posy+1, base->posx*2, base->image);
    sleep(2);
    {
	register OBJECT *curobj;

	for (curobj = root.next; curobj != &root; curobj = curobj->next) {
	    mvaddch(curobj->posy+1, curobj->posx*2, curobj->image);
	}
    }

    for (i=0;i<2;i++) for (y=0;y<3;y++) for (x=0;x<3;x++) 
    isatorp[i][y][x]=0;

    timer = 10000;
    finish = 0;
    bombed_out = FALSE;
    if (ent)
	entmode = status = 0;
    else
	if (base)
	    status = 2;
	else
	    status = 3;

    sprintf(spbuf,
    "%-4s%9ld  E: %4d %2d B: %5d %3d Stars: %-3d Enemies: %-3d Stardate%5d.%1d",
	"   ", 0, 0, 0, 0, 0, 0, 0, timer/10, timer%10);
    mvaddstr(0,0,spbuf);
    oldeenergy = oldbenergy = oldcurscore = 
    oldstatus = oldetorp = oldbtorp = oldstrs = oldenemies = -1;
					/* force everything to fill in */
    btorp = 500;
    etorp = 50;
}

play()
{
    bool done = FALSE;
    register OBJECT *curobj, *to;
    register int i, x, y;

    display_status();
#ifdef TIOCOUTQ
    while (output_pending() > charsperhalfsec)
	sleep(1);			 /* allow buffers to empty */
#endif
    sleep(3);
    do {
	timer++;
	nxtbang = 0;
	banging = FALSE;
	display_status();
#ifdef TIOCOUTQ
	while (output_pending() > charsperhalfsec)
	    sleep(1);
#endif
	if (lowspeed)
	    roundsleep(2);
	else
	    roundsleep(1);
	if (ent) {
	    evely = ent->vely;
	    evelx = ent->velx;
	    if (cloaking && ent->energy >= 250)
		ent->energy -= ent->energy/35;
	    else
		cloaking = FALSE;
	    cloaked = cloaking;
	}
	if (base) {
	    bvely = base->vely;
	    bvelx = base->velx;
	}
	get_commands(&done);
	if (done)
	    break;
	klingon_smarts();
	apolloflag = 0;
	if (ent) {
	    if (numapollos) {
		if (numstars) {
		    if (realapollo) {
			if (lookfor(realapollo->posy,realapollo->posx,
			    Enterprise)) {
			    apolloflag = 1;
			}
		    }
		    else if (lookfor(root.next->posy,root.next->posx,
			Enterprise)) {
			apolloflag = 1;
			realapollo = root.next;
			mvaddch(realapollo->posy+1,realapollo->posx*2,
			    'A');
			realapollo->image = 'A';
			realapollo->mass = 6000;
			inumapollos = 1;
			numenemies++;
			inumenemies++;
			possiblescore += 5000;
		    }
		    if (apolloflag) {
			if (blast[realapollo->posy][realapollo->posx] <= 32000)
			    evely = evelx = 0;
			realapollo->energy = 32000;
		    }
		}
		else
		    numapollos = 0;
	    }
	    ent->vely = evely;
	    ent->velx = evelx;
	}
	if (base) {
	    if (numapollos) {
		if (numstars) {
		    if (realapollo) {
			if (lookfor(realapollo->posy,realapollo->posx,
			    Base)) {
			    apolloflag |= 2;
			}
		    }
		    else if (lookfor(root.next->posy,root.next->posx,
			Base)) {
			apolloflag |= 2;
			realapollo = root.next;
			mvaddch(realapollo->posy+1,realapollo->posx*2,
			    'A');
			realapollo->image = 'A';
			realapollo->mass = 6000;
			inumapollos = 1;
			numenemies++;
			inumenemies++;
			possiblescore += 5000;
		    }
		    if (apolloflag & 2) {
			if (blast[realapollo->posy][realapollo->posx] <= 32000)
			    bvely = bvelx = 0;
			realapollo->energy = 32000;
		    }
		}
		else
		    numapollos = 0;
	    }
	    base->vely = bvely;
	    base->velx = bvelx;
	}
	if (aretorps) {
	    aretorps = 0;
	    for (i=0;i<2;i++) for (y=0;y<3;y++) for (x=0;x<3;x++) {
		if (curobj = isatorp[i][y][x]) {
		    to = occupant[(curobj->posy+curobj->vely+YSIZE00)%YSIZE]
				 [(curobj->posx+curobj->velx+XSIZE00)%XSIZE];
		    if (to && !to->vely && !to->velx) {
			unmake_object(curobj);
			if (i)
			    btorp++;
			else
			    etorp++;
		    }
		    isatorp[i][y][x]=0;
		}
	    }
	}
	move_universe();
	if (finish) {
	    finish--;
	    if (!finish && (!(numenemies || numos) || (!ent && !base))) {
		done = TRUE;
		timer -= 5;
	    }
	}
	else if (!banging && (!(numenemies || numos) || (!ent && !base)))
	    finish = 5;
    } while (!done);
}

wavescore()
{
    double power, effectscore, starscore, pi_over_2;
    long bonuses;
    long tmp;
    FILE *mapfp;

    clear();
    pi_over_2 = 3.14159265 / 2.0;
    power = pow((double)inumenemies+     /* total number of enemies */
			inumroms*2+      /* count roms 3 times */
			inumgorns+       /* count gorns 2 times */
			inumthols+       /* count thols 2 times */
			inumapollos*4+   /* count apollo 5 times */
			inumcrushes*3    /* count crushers 4 times */
	    , 0.50) *                    /* skew it a little */
	    (double)smarts;              /* average energy and intelligence */
    if (inumstars < 350 && inumenemies > 5)
	    power += (350.0 - (double)inumstars) * ((double)inumenemies - 5.0);
    if (inumstars > 850 && inumenemies > 2)
	    power += ((double)inumstars - 850.0) * ((double)inumenemies - 2.0);
    effectscore = ((double)curscore / possiblescore) *
	atan2(power, (double) timer - 9999.0) / pi_over_2;
    if (inumstars)
	starscore = (double) numstars / (double) inumstars;
    else
	starscore = 1.0;
    wave++;
    sprintf(spbuf,"Wave = %d, Difficulty = %d, cumulative difficulty = %d",
	 wave, smarts, cumsmarts);
    mvaddstr(1, 13+(smarts<10), spbuf);
    mvaddstr( 4, 68, " BONUS");
    sprintf(spbuf,"Efficiency rating:       %1.8f (diff=%0.2f,time=%d)",
	 effectscore, power, timer - 9999);
    mvaddstr( 5,5, spbuf);
    if (effectscore < 0.8)
	bonuses = tmp = 0;
    else
	bonuses = tmp = (long) ((effectscore-0.8) * smarts * 1000);
    sprintf(spbuf, "%6ld", tmp);
    mvaddstr( 5, 68, spbuf);
    sprintf(spbuf,"Star save ratio:         %1.8f (%d/%d)",
	starscore, numstars, inumstars);
    mvaddstr( 6,5, spbuf);
    bonuses += tmp = (long) (((double)curscore / possiblescore) *
	(starscore*starscore) * smarts * 20);
    sprintf(spbuf, "%6ld", tmp);
    mvaddstr( 6, 68, spbuf);
    sprintf(spbuf, "Inhabited stars destroyed:    %5d", inuminhab-numinhab);
    mvaddstr( 7,5, spbuf);
    bonuses += tmp = (long) (inuminhab-numinhab) * -500;
    sprintf(spbuf, "%6ld", tmp);
    mvaddstr( 7, 68, spbuf);
    if (bombed_out) {
	mvaddstr( 8,5, "   For running away from reality:");
	bonuses += tmp = (long) -possiblescore/2;
	sprintf(spbuf, "%6ld", tmp);
	mvaddstr( 8, 68,  spbuf);
    }
    sprintf(spbuf, "Enterprise: %-9s%5d remaining",
	ent?"saved":"destroyed", numents);
    mvaddstr( 9,5, spbuf);
    bonuses += tmp = ent && !bombed_out ? (smarts+1)*15 : 0;
    sprintf(spbuf, "%6ld", tmp);
    mvaddstr( 9, 68, spbuf);
    sprintf(spbuf, "Base: %-9s      %5d remaining",
	base?"saved":"destroyed", numbases);
    mvaddstr(10,5, spbuf);
    bonuses += tmp = base && !bombed_out ? (smarts+1)*10 : 0;
    sprintf(spbuf, "%6ld", tmp);
    mvaddstr(10, 68,  spbuf);
    if (beginner) {
	mvaddstr(12,19, "(Special games count only a tenth as much)");
	curscore /= 10;
	bonuses /= 10;
    }
    sprintf(spbuf, "Previous point total:%10ld",lastscore);
    mvaddstr(15,24, spbuf);
    sprintf(spbuf, "Points this round:   %10ld",curscore);
    mvaddstr(16,24, spbuf);
    sprintf(spbuf, "Bonuses:             %10ld",bonuses);
    mvaddstr(17,24, spbuf);
    totalscore = lastscore + curscore + bonuses;
    sprintf(spbuf, "New point total:     %10ld",totalscore);
    mvaddstr(18,24, spbuf);
    if (lastscore / ENTBOUNDARY < totalscore / ENTBOUNDARY) {
	mvaddstr(9,42,"+ 1 new");
	numents++;
    }
    else if (lastscore / ENTBOUNDARY > totalscore / ENTBOUNDARY) {
	mvaddstr(9,42,"- 1 obsolete");
	if (numents)
	    numents--;
    }
    if (lastscore / BASEBOUNDARY < totalscore / BASEBOUNDARY) {
	mvaddstr(10,42,"+ 1 new");
	numbases++;
    }
    else if (lastscore / BASEBOUNDARY > totalscore / BASEBOUNDARY) {
	mvaddstr(10,42,"- 1 obsolete");
	if (numbases)
	    numbases--;
    }
    if (starscore < 0.8 && inumstars > 200 && numstars > 50) {
	sprintf(spbuf, "%ssmap.%d",SAVEDIR,rand_mod(MAPS-PERMMAPS)+PERMMAPS);
	if ((mapfp = fopen(spbuf,"w")) != NULL) {
	    register OBJECT *obj;

	    fprintf(mapfp,"%d\n",numstars);
	    for (obj = root.next; obj != &root; obj = obj->next) {
		if (obj->type == Star) {
		    fprintf(mapfp,"%d %d\n",obj->posy,obj->posx);
		}
	    }
	    fclose(mapfp);
	}
    }
}

score()
{
    char tmp, buf[100], *retval, cdate[30];
    register FILE *logfd, *outfd;
    register int i;
    long nowtime, time();

    for (i=0; link(LOGFILE, LOCKFILE) == -1 && i<10; i++)
	sleep(1);
    nowtime = time((long *)0);
    strcpy(cdate,ctime(&nowtime));
    if ((logfd = fopen(LOGFILE,"a")) != NULL) {
	fprintf(logfd,
	    "%-24s%-9s%7ld%c%2d %4d %s",
	    realname, loginname, totalscore, c,smarts, cumsmarts, cdate);
	fclose(logfd);
    }
    strcpy(cdate+11,cdate+20);
    if (access(lowspeed?LSCOREBOARD:SCOREBOARD,0)) {
	if ((logfd = fopen(lowspeed?LSCOREBOARD:SCOREBOARD,"w")) != NULL)
	    fclose(logfd);
    }
    if ((logfd = fopen(lowspeed?LSCOREBOARD:SCOREBOARD,"r")) != NULL &&
	(outfd = fopen(TMPSCOREBOARD,"w")) != NULL) {
	for (i=0; i<20; i++) {
	    if ((retval = fgets(buf, 100, logfd)) == NULL)
		break;
	    if (atoi(buf+32) < totalscore)
		break;
	    if (!strncmp(buf+COMPOFF,COMPNAME,COMPLEN)) {
		i = 100;
		break;
	    }
	    fprintf(outfd, "%s", buf);
	}
	if (i == 100) {
	    mvaddstr(20,21, "You did not better your previous score");
	    fclose(outfd);
	    unlink(TMPSCOREBOARD);
	}
	else if (i < 20) {
	    fprintf(outfd, "%-24s%-8s%8ld%c %2d    %4d    %s",
		realname, loginname, totalscore, c,smarts, cumsmarts, cdate);
	    i++;
	    sprintf(spbuf, "    Congratulations--you've placed %d%s",
	      i, i==1?"st":(i==2?"nd":(i==3?"rd":"th")));
	    if (retval != NULL) {
		if (strncmp(buf+COMPOFF,COMPNAME,COMPLEN)) {
		    fprintf(outfd, "%s", buf);
		    i++;
		}
		else
		    strcpy(spbuf,"Congratulations--you've bettered your score");
		while (i<20) {
		    if (fgets(buf, 100, logfd) == NULL)
			break;
		    if (strncmp(buf+COMPOFF,COMPNAME,COMPLEN)) {
			fprintf(outfd, "%s", buf);
			i++;
		    }
		}
	    }
	    mvaddstr(20,19, spbuf);
	    fclose(logfd);
	    fclose(outfd);
	    unlink(lowspeed?LSCOREBOARD:SCOREBOARD);
	    link(TMPSCOREBOARD,
		 lowspeed?LSCOREBOARD:SCOREBOARD);
	    unlink(TMPSCOREBOARD);
	    logfd = fopen(lowspeed?LSCOREBOARD:SCOREBOARD,"r");
	}
	else {
	    mvaddstr(20,22,"You did not place within the top 20");
	    fclose(outfd);
	}
    }
    else {
	sprintf(spbuf,"(Cannot access %s file, error %d)",
	    (logfd==NULL?"log":"tmp"),errno);
	mvaddstr(20,22,spbuf);
    }
    move(23,0,(char*)0);
    erase_eol();
    mvaddstr(23,11,
	"[Hit space for scoreboard, 'r' for new game, 'q' to quit]");
    unlink(LOCKFILE);
    eat_typeahead();
    do {
	if (read(0, &tmp, 1) < 0)
	    hangup_catcher();
	tmp &= 0177;
    } while (tmp != ' ' && tmp != 'r' && tmp != INTRCH && tmp != BREAKCH &&
	tmp != 'q' && tmp != 'Q');
    if (tmp == 'q' || tmp == 'Q' || tmp == 'r') {
	justonemoretime = (tmp == 'r');
	if (logfd != NULL)
	    fclose(logfd);
    }
    else {
	clear();
	if (logfd != NULL) {
	    fseek(logfd, 0, 0);
	    if (lowspeed)
		mvaddstr(0,28,"TOP (LOW-SPEED) WARPISTS");
	    else
		mvaddstr(0,33,"TOP WARPISTS");
	    mvaddstr(2,0,"RANK  WHO                     AKA        SCORE DIFF  CUMDIFF  WHEN");
	    for (i=1; i<=20; i++) {
		if (fgets(buf, 100, logfd) == NULL)
		    break;
		buf[strlen(buf)-1] = '\0';
		sprintf(spbuf, " %2d   %s", i, buf);
		mvaddstr(i+2,0, spbuf);
	    }
	    fclose(logfd);
	}
	roundsleep(1);
	mvaddstr(23,25,"Would you like to play again?");
	eat_typeahead();
	do {
	    if (read(0, &tmp, 1) < 0)
		hangup_catcher();
	    tmp &= 0177;
	} while (tmp != 'n' && tmp != 'N' && tmp != 'y' && tmp != INTRCH &&
	    tmp != 'Y' && tmp != ' ' && tmp != '\n' && tmp != '\r' &&
	    tmp != BREAKCH);
	if (tmp == 'n' || tmp == 'N' || tmp == INTRCH || tmp == BREAKCH)
	    justonemoretime = FALSE;
    }
}

save_game()
{
    FILE *savfil;

    if (experimenting)
	return;
    if ((savfil = fopen(savefilename,"w")) == NULL) {
	resetty();
	printf("Cannot save game\n");
	exit(1);
    }
    fprintf(savfil, "%-8s %10ld, %2d,%5d,%2d,%2d,%3d %c%c%c%c%c%c%c%c\n",
	loginname, totalscore, smarts, cumsmarts, numents, numbases, wave,
	apolspec ? 'a' : ' ',
	beginner   ? 'b' : ' ',
	crushspec  ? 'c' : ' ',
	gornspec   ? 'g' : ' ',
	massacre   ? 'm' : ' ',
	romspec    ? 'r' : ' ',
	tholspec   ? 't' : ' ',
	lowspeed   ? 'l' : ' '
    );
    fclose(savfil);
    resetty();
    if (panic)
	exit(0);
    clear();
    exit(0);
}

void hangup_catcher()
{
    panic++;
    if (panic >= 2) {
	if (panic >= 3)
	    exit(1);
	chdir(SAVEDIR);
	kill(0,SIGIOT);
    }
    save_game();
    exit(0);
}

exdis(maxnum)
int maxnum;
{
    double temp, temp2;

    temp = (double) maxnum;
    temp2 = (double) myrand();
#ifdef RAND16
    return (int) exp(temp2 * log(temp)/0xffff);
#else
    return (int) exp(temp2 * log(temp)/0x7fffffff);
#endif
				       /*maxint*/
}

display_status()
{
    register int tmp;
    static char *status_names[] = {"Impl", "Warp", "Base", "****" };

    if (oldstatus != status) {
	sprintf(spbuf,"%-4s",status_names[status]);
	mvaddstr(0,0, spbuf);
	oldstatus = status;
    }
    if (curscore != oldcurscore) {
	sprintf(spbuf,"%9ld",curscore);
	mvaddstr(0,4, spbuf);
	oldcurscore = curscore;
    }
    if (ent) {
	if (ent->energy != oldeenergy) {
	    oldeenergy = ent->energy;
	    sprintf(spbuf,"%4d",oldeenergy);
	    mvaddstr(0,18, spbuf);
	}
	if (etorp != oldetorp) {
	    sprintf(spbuf,"%2d",etorp);
	    mvaddstr(0,23, spbuf);
	    oldetorp = etorp;
	}
    }
    else {
	if (etorp >= 0) {
	    etorp = -1;
	    mvaddstr(0,18,"*******");
	}
    }
    if (base) {
	if (base->energy != oldbenergy) {
	    oldbenergy = base->energy;
	    sprintf(spbuf,"%5d",oldbenergy);
	    mvaddstr(0,29, spbuf);
	}
	if (btorp != oldbtorp) {
	    sprintf(spbuf,"%3d",btorp);
	    mvaddstr(0,35, spbuf);
	    oldbtorp = btorp;
	}
    }
    else {
	if (btorp >= 0) {
	    btorp = -1;
	    mvaddstr(0,29,"*********");
	}
    }
    if (numstars != oldstrs) {
	sprintf(spbuf,"%-3d",numstars);
	mvaddstr(0,46, spbuf);
	oldstrs = numstars;
    }
    if (numenemies != oldenemies) {
	sprintf(spbuf,"%-3d",numenemies);
	mvaddstr(0,59, spbuf);
	oldenemies = numenemies;
    }
    if (tmp = timer%10) {
	sprintf(spbuf,"%1d",tmp);
	mvaddstr(0,77, spbuf);
    }
    else {
	sprintf(spbuf,"%4d.%1d",timer/10,tmp);
	mvaddstr(0,72, spbuf);
    }
}

do_direction(dy,dx)
int dy, dx;
{
    register int decr;

    if (status < 2) {
	if (cloaking) {
	    char ch;
	    
	    cloaked = FALSE;
	    ch = (ent->energy >= 500?'E':'e');
	    if (ch != ent->image) {
		setimage(ent, ch);
	    }
	}
	decr = 5+abs(evely)+abs(evelx)+tractor*tractor;
	if (ent->energy >= decr) {
	    ent->energy -= decr;
	    if (tractor) {
		if (tract(ent,dy,dx,tractor)) {
		    evely += tractor*dy;
		    evelx += tractor*dx;
		}
	    }
	    else {
		evely += dy;
		evelx += dx;
	    }
	    if (inumthols &&
	      occupant[(ent->posy+evely+YSIZE00)%YSIZE]
		      [(ent->posx+evelx+XSIZE00)%XSIZE]->type == Web)
		evely = evelx = 0;
	}
    }
    else if (status == 2) {
	decr = 500+abs(bvely)*5+abs(bvelx)*5+tractor*tractor*100;
	if (base->energy >= decr) {
	    base->energy -= decr;
	    if (tractor) {
		if (tract(base,dy,dx,tractor)) {
		    bvely += tractor*dy;
		    bvelx += tractor*dx;
		}
	    }
	    else {
		bvely += dy;
		bvelx += dx;
	    }
	    if (inumthols &&
	      occupant[(base->posy+bvely+YSIZE00)%YSIZE]
		      [(base->posx+bvelx+XSIZE00)%XSIZE]->type == Web)
		bvely = bvelx = 0;
	}
    }
    tractor = 0;
}

ctrl_direction(dy,dx)
int dy, dx;
{
    if (status < 2) {
	if (cloaking) {
	    char ch;
	    
	    cloaked = FALSE;
	    ch = (ent->energy >= 500?'E':'e');
	    if (ch != ent->image) {
		setimage(ent, ch);
	    }
	}
	fire_phaser(ent, dy, dx);
    }
    else if (status == 2)
	fire_phaser(base, dy, dx);
}

shift_direction(dy,dx)
int dy, dx;
{
    if (status < 2) {
	if (cloaking) {
	    char ch;
	    
	    cloaked = FALSE;
	    ch = (ent->energy >= 500?'E':'e');
	    if (ch != ent->image) {
		setimage(ent, ch);
	    }
	}
	fire_torp(ent, dy, dx);
    }
    else if (status == 2)
	fire_torp(base, dy, dx);
}

get_commands(done)
bool *done;
{
    char ch[80];
    register int i,count;
    register bool ctrla = FALSE;
    char numdestructs = 0;

top:
#ifdef FIONREAD
    while (count = input_pending()) {
	for (i=0; i<count; i++) {
	    if (read(0, &ch[i], 1) < 0)
		hangup_catcher();
#else
    while (count = input_pending(ch)) {
	if (count < 0)
		hangup_catcher();
	for (i=0; i<count; i++) {
#endif
	    ch[i] &= 0177;
	    if (ch[i] == 'Q') {
		bombed_out = TRUE;
		*done = TRUE;
		keepgoing = FALSE;
		return;
	    }
	    if (ch[i] == 'q' || ch[i] == BREAKCH || ch[i] == INTRCH) {
		int x;
		static char quest[] = "Do you wish to escape from reality? ";

		if (timer >= whenok) {
		    mvaddstr(12,22,quest);
		    do {
			if (read(0, &ch[i], 1) < 0)
			    hangup_catcher();
			ch[i] &= 0177;
		    } while (ch[i] != 'y' && ch[i] != 'n');
		    if (ch[i] == 'y') {
			bombed_out = TRUE;
			*done = TRUE;
			return;
		    }
		    else {
			for (x=11; x<=28; x++) {
			    mvaddch(12,x*2,
				occupant[11][x]?occupant[11][x]->image:' ');
			    addspace();
			}
			roundsleep(2);
			whenok = timer + 10;
			goto top;
		    }
		}
		else {
		    write(1,"\07",1);
		    goto top;
		}
	    }
	}
	for (i=0; i<count; i++) {
	    if (ctrla) {
		switch (ch[i]) {
		case '1': case 'b':
		    ctrl_direction(1, -1);
		    break;
		case '2': case 'j':
		    ctrl_direction(1, 0);
		    break;
		case '3': case 'n':
		    ctrl_direction(1, 1);
		    break;
		case '4': case 'h':
		    ctrl_direction(0, -1);
		    break;
		case '6': case 'l':
		    ctrl_direction(0, 1);
		    break;
		case '7': case 'y':
		    ctrl_direction(-1, -1);
		    break;
		case '8': case 'k':
		    ctrl_direction(-1, 0);
		    break;
		case '9': case 'u':
		    ctrl_direction(-1, 1);
		    break;
		case 'r':
		    rewrite();
		    roundsleep(3);
		    ctrla = FALSE;
		    goto top;
#ifdef FIONREAD
		case 's':
		    clear();
		    while (!input_pending())
			sleep(1);
		    rewrite();
		    roundsleep(3);
		    ctrla = FALSE;
		    goto top;
#endif
#ifdef SIGTSTP
		case 'z':
		    clear();
		    mytstp();
		    sleep(4);
		    ctrla = FALSE;
		    goto top;
#endif
		default:
		    break;
		}
		ctrla = FALSE;
	    }
	    else {
		switch (ch[i]) {
#ifdef SIGTSTP
		case 'Z':
		    clear();
		    mytstp();
		    sleep(4);
		    goto top;
#endif
		case 'i':
		    if (ent) {
			entmode = 0;
			status = 0;
		    }
		    break;
		case 'w':
		    if (ent) {
			entmode = 1;
			status = 1;
		    }
		    break;
		case 'p':
		    if (base) {
			status = 2;
		    }
		    break;
		case 'o':
		    if (status < 2) {
			if (base)
			    status = 2;
		    }
		    else if (status == 2) {
			if (ent)
			    status = entmode;
		    }
		    break;
		case 'v':
		    if (ent) {
			status = entmode;
		    }
		    cloaking=FALSE;
		    cloaked=FALSE;
		    break;
		case 'c':
		    if (ent) {
			status = entmode;
			if (ent->energy >= 250)
			    cloaking = TRUE;
		    }
		    break;
		case 'D':
		    if (status < 2) {
			if (++numdestructs <= 2)
			    make_blast(evely*2+ent->posy,evelx*2+ent->posx,
				15000L, 3);
			ent->energy /= 2;
		    }
		    else if (status == 2) {
			if (++numdestructs <= 2)
			    make_blast(base->posy, base->posx, 15000L, 5);
		    }
		    break;
		case 'd':
		    {
			register OBJECT *obj;
			int x, y;

			for (obj = root.prev;
			  obj != &root;
			  obj = obj->prev) {
			    if (obj->image == '+') {
				blast[y=(obj->posy+obj->vely+YSIZE00)%YSIZE]
				     [x=(obj->posx+obj->velx+XSIZE00)%XSIZE]
				     += 1;
				yblasted[y] = TRUE;
				xblasted[x] = TRUE;
				blasted = TRUE;
				obj->mass = (massacre?3000:4000);
			    }
			}
		    }
		    break;
		case 's':
		    { register OBJECT *obj;
			for (obj = root.prev;
			  obj->type == Torp || obj->type == Web ||
			  obj->type == Star;
			  obj = obj->prev) {
			    if (obj->image == '+')
				obj->vely = obj->velx = 0;
			}
		    }
		    break;
		case '\001':
		    ctrla = TRUE;
		    break;
		case '\002':
		case '\003':
		case '\004':
		case '\005':
		case '\006':
		case '\007':
		case '\010':
		case '\011':
		case '\012':
		case '\013':
		case '\014':
		case '\015':
		case '\016':
		case '\017':
		case '\020':
		case '\021':
		case '\022':
		case '\023':
		case '\024':
		case '\025':
		case '\026':
		case '\027':
		case '\030':
		case '\031':
		case '\032':
		    ch[i] += 96;
		    i--;
		    ctrla = TRUE;
		    break;
		case '\033':
		    tractor = 0;
		    break;
		case 'a':
		    tractor++;
		    break;
		case 'r':
		    tractor--;
		    break;
		case '1': case 'b':
		    do_direction(1,-1);
		    break;
		case '2': case 'j':
		    do_direction(1,0);
		    break;
		case '3': case 'n':
		    do_direction(1,1);
		    break;
		case '4': case 'h':
		    do_direction(0,-1);
		    break;
		case '6': case 'l':
		    do_direction(0,1);
		    break;
		case '7': case 'y':
		    do_direction(-1,-1);
		    break;
		case '8': case 'k':
		    do_direction(-1,0);
		    break;
		case '9': case 'u':
		    do_direction(-1,1);
		    break;
		case '0': case 'S':
		    if (status < 2) {
			evely = 0;
			evelx = 0;
		    }
		    break;
		case '-':
		    if (status < 2 && ent->energy >= 10) {
			evely *= -1;
			evelx *= -1;
			ent->energy -= 10;
		    }
		    break;
		case '%': case '\177': case '_':
		    shift_direction(0, -1);
		    shift_direction(0, 1);
		    shift_direction(-1, 0);
		    shift_direction(1, 0);
		    shift_direction(-1, -1);
		    shift_direction(-1, 1);
		    shift_direction(1, -1);
		    shift_direction(1, 1);
		    break;
		case '!': case 'B':
		    shift_direction(1, -1);
		    break;
		case '@': case 'J':
		    shift_direction(1, 0);
		    break;
		case '#': case 'N':
		    shift_direction(1, 1);
		    break;
		case '$': case 'H':
		    shift_direction(0, -1);
		    break;
		case '^': case 'L':
		    shift_direction(0, 1);
		    break;
		case '&': case 'Y':
		    shift_direction(-1, -1);
		    break;
		case '*': case 'K':
		    shift_direction(-1, 0);
		    break;
		case '(': case 'U':
		    shift_direction(-1, 1);
		    break;
		case '?':
		    helper();
		    roundsleep(3);
		    goto top;
		default:
		    break;
		}
	    }
	}
    }
}

klingon_smarts()
{
    register OBJECT *curkl,*obj;
    register int prob, count, y, x;

    if (numcrushes && movers->type == Crusher) {
	movers->vely += (rand_mod(222) - 111) / 100;
	if (!(rand_mod(100))) {
	    setimage(movers, (movers->velx *= -1) < 0 ? '>' : '<');
	}
    }
    for (curkl = enemies; curkl->type == Enemy; curkl = curkl->next) {
	if (curkl->image == 'R' && (curkl->energy > 300 || massacre)) {
	    setimage(curkl, ' ');
	}
	if (madgorns)
	    prob = 3;
	else if (curkl->vely || curkl->velx)
	    prob = massacre?10:20;
	else
	    prob = 4;
	count = 11;
	while (--count &&
	       (!(rand_mod(prob)) ||
	        (obj = occupant[y=(curkl->posy+curkl->vely+YSIZE00)%YSIZE]
		               [x=(curkl->posx+curkl->velx+XSIZE00)%XSIZE]) &&
		(obj->type == Star ||
		 ((rand_mod(100) <= smarts) &&
		  !obj->vely && !obj->velx &&
		  (obj->image == 'o' ||
		   obj->image == 'O' ||
		   obj->image == 'X'
		  )
		 ) ||
		 (obj->type == Web &&
		  (curkl->image != 'T' ||
		   (count > 5 && obj->image ==
		    (curkl->vely?
		     (curkl->velx?
		      (curkl->velx==curkl->vely?
		       '\\'
		      :
		       '/'
		      )
		     :
		      '|'
		     )
		    :
		     '-'
		    )
		   )
		  )
		 )
	        )
	       )
	      ) {
	    if (massacre && curkl->image != 'T') {
		curkl->vely = rand_mod(7) - 3;
		curkl->velx = rand_mod(7) - 3;
	    }
	    else if (curkl->energy >= 2500 && curkl->image != 'T') {
		curkl->vely = rand_mod(5) - 2;
		curkl->velx = rand_mod(5) - 2;
	    }
	    else {
		curkl->vely = rand_mod(3) - 1;
		curkl->velx = rand_mod(3) - 1;
	    }
	}
	if (count != 10) {
	    if (curkl->image == ' ') {
		setimage(curkl, 'R');
	    }
	    if (!count) {
		curkl->vely = 0;
		curkl->velx = 0;
	    }
	}
	if (curkl->image == 'G' && (base||ent) &&
	    !rand_mod((103-smarts)*50) ) {
	    int xxx,yyy;

	    for (xxx = -1; xxx<=1; xxx++)
		for (yyy = -1; yyy<=1; yyy++)
		    if ((xxx||yyy) && rand_mod(2))
			fire_torp(curkl,yyy,xxx);
	}
	else if (curkl->image == 'T' && (curkl->velx || curkl->vely)) {
	    make_object(Web,
            curkl->vely?
	     (curkl->velx?
	      (curkl->velx==curkl->vely?
	       '\\'
	      :
	       '/'
	      )
	     :
	      '|'
	     )
	    :
	     '-',
	    curkl->posy,curkl->posx,0,0,32767L,32767L,&root);
	    if (obj && obj->type == Web) {
		unmake_object(obj);
		occupant[y][x] = 0;
	    }
	}
    }
    /* klingon fighting */
    attack(base);
    if (ent && (!cloaked || ent->image=='E' || ent->image=='e'))
	attack(ent);
}

move_universe()
{
    register OBJECT *curobj;
    register int x, y;
    register OBJECT *temp;
    OBJECT *thenext;

    for (curobj = movers; curobj != &root; curobj = curobj->next) {
	x = curobj->posx;
	y = curobj->posy;
	if (curobj == occupant[y][x]) {
	    occupant[y][x] = 0;
	}
	else if (curobj->type != Torp && curobj->type != Web) {
	    resetty();
	    abort();
	}
    }
    for (curobj = movers; curobj != &root; curobj = thenext) {
	thenext = curobj->next;
	if (curobj->vely || curobj->velx) {
	    y = curobj->posy;
	    x = curobj->posx;
	    if (curobj->image != ' ' &&
		(!occupant[y][x] || occupant[y][x]->image==' ') ) {
		move(y+1, x*2, " ");
	    }
	    y = (y + curobj->vely + YSIZE00) % YSIZE;
	    x = (x + curobj->velx + XSIZE00) % XSIZE;
	    if (occupant[y][x]->type != Star || curobj->type != Torp ||
	      (curobj->image == '+' || curobj->image == 'x')) {
		curobj->posy = y;
		curobj->posx = x;
	    }
	    else {
		if (curobj->image == '0') {
		    curobj->vely = rand_mod(3)-1;
		    curobj->velx = rand_mod(3)-1;
		}
		else
		    curobj->vely = curobj->velx = 0;
		y = curobj->posy;
		x = curobj->posx;
	    }
	}
	else {
	    y = curobj->posy;
	    x = curobj->posx;
	    if (curobj->type == Web ||
		curobj->type == Star ||
		curobj->type == Torp) {
		curobj->strategy = 0;
		curobj->next->prev = curobj->prev;
		curobj->prev->next = curobj->next;
		curobj->prev = movers->prev;
		curobj->next = movers;
		movers->prev->next = curobj;
		movers->prev = curobj;
	    }
	}
	if (temp = occupant[y][x]) {
	    if (!temp->contend) {
		if (temp->type == Torp) {
		    if (temp->image == '+')
			blast[y][x] += 1250;
		    else if (temp->image == 'o' && (base||ent))
			blast[y][x] += 500+super*20;
		    else if (temp->image == 'O' && (base||ent))
			blast[y][x] += 5000+super*100;
		}
	    }
	    if (curobj->type != Enemy || temp->type != Enemy)
		blast[y][x] += rand_mod(751)+1;
	    else
		blast[y][x] += 10;
	    yblasted[y] = TRUE;
	    xblasted[x] = TRUE;
	    blasted = TRUE;
	    curobj->contend = temp;
	    occupant[y][x] = curobj;
	    if (curobj->type == Crusher)
		blast[y][x] += 100000;
	    else if (curobj->type == Torp) {
		if (curobj->image == '+')
		    blast[y][x] += 1250;
		else if (curobj->image == 'o')
		    blast[y][x] += 500+super*20;
		else if (curobj->image == 'O')
		    blast[y][x] += 5000+super*100;
	    }
	}
	else {
	    occupant[y][x] = curobj;
	    if (curobj->image != ' ' &&
	        (curobj->velx || curobj->vely ||
		 curobj->type == Torp || curobj->type == Web) ) {
		mvaddc(y+1, x*2, curobj->image);
	    }
	    if (curobj->type == Crusher) {
		blast[y][x] += 100000;
		yblasted[y] = TRUE;
		xblasted[x] = TRUE;
		blasted = TRUE;
	    }
	}
    }
    if (blasted) {
	int minxblast = -1, maxxblast = -2;
	long tmpblast;

	blasted = FALSE;
	for (x=0; x<XSIZE; x++) {
	    if (xblasted[x]) {
		xblasted[x] = FALSE;
		maxxblast = x;
		if (minxblast < 0)
		    minxblast = x;
	    }
	}
	for (y=0; y<YSIZE; y++) {
	    if (yblasted[y]) {
		yblasted[y] = FALSE;
		for (x=minxblast; x<=maxxblast; x++) {
		    if (tmpblast = blast[y][x]) {
			register OBJECT *biggie = 0;

			blast[y][x] = 0;
			if (temp = occupant[y][x]) {
			    if (tmpblast < 100000)
				make_plink(y,x);
			    for ( ;temp;
			      temp = curobj->contend,curobj->contend = 0){
				curobj = temp;
				if (curobj == ent &&
				   (ent->energy > 500 || apolloflag & 1))
				    curobj->energy -= tmpblast /
				       ((apolloflag & 1)?
					20: 5+abs(ent->velx)+abs(ent->vely));
				else if (curobj == base &&
				   (base->energy > 1000 || apolloflag & 2))
				    curobj->energy -= tmpblast /
				       ((apolloflag & 2)?20:5);
				else if (curobj->type == Crusher) {
				    if (tmpblast > 132767)
					curobj->energy -= (tmpblast - 100000);
				    else {
					curobj->energy += (tmpblast - 100000);
					if (curobj->energy > 32767)
					    curobj->energy = 32767;
				    }
				}
				else if (curobj->type == Enemy)
				    curobj->energy -= tmpblast / enemshields;
				else
				    curobj->energy -= tmpblast;
				if (curobj->energy < 0) {
				    if (tmpblast < 100000 &&
					curobj->image != 'G')
					make_blast(y,x,curobj->mass,
					   (curobj->type==Web?2:1));
				    else if (apolloflag && curobj->image == 'A')
					make_blast(y,x,8192L,1);
				    else if (curobj->type == Crusher) {
					int i;

					make_blast(y,(x+XSIZE00)%XSIZE,10000L,0);
					if (curobj->image == '<') {
					    for (i=XSIZE00; i<=XSIZE01; i++)
						make_blast(y,(x+i)%XSIZE,
						    10000L,0);
					    for (i=XSIZE00; i<=XSIZE02; i++)
						make_blast(y,(x+i)%XSIZE,
						    10000L,0);
					    make_blast(y,(x+XSIZE03)%XSIZE,
						10000L,1);
					    for (i=XSIZE00; i<=XSIZE08; i++)
						make_blast(y,(x+i)%XSIZE,
						    10000L,0);
					}
					else {
					    for (i=XSIZE00; i>=XSIZE99; i--)
						make_blast(y,(x+i)%XSIZE,
						    10000L,0);
					    for (i=XSIZE00; i>=XSIZE98; i--)
						make_blast(y,(x+i)%XSIZE,
						    10000L,0);
					    make_blast(y,(x+XSIZE97)%XSIZE,
						10000L,1);
					    for (i=XSIZE00; i>=XSIZE92; i--)
						make_blast(y,(x+i)%XSIZE,
						    10000L,0);
					}
				    }
				    switch (curobj->image) {
				    case 'A':
					numapollos = apolloflag = 0;
					numstars--;
					numenemies--;
					curscore += 5000;
					deados = 0;
					break;
				    case 'E': case 'e': case 'C': case 'c':
					ent = 0;
					numents--;
					if (base)
					    status = 2;
					else
					    status = 3;
					deados = 0;
					break;
				    case 'B': case 'b':
					base = 0;
					numbases--;
					if (ent)
					    status = entmode;
					else
					    status = 3;
					deados = 0;
					break;
				    case '<': case '>':
					numenemies--;
					numcrushes = 0;
					curscore += 10000;
					if (curobj == enemies)
					    enemies = curobj->next;
					deados = 0;
					break;
				    case 'K':
					numenemies--;
					curscore += curobj->mass;
					if (curobj == enemies)
					    enemies = curobj->next;
					deados = 0;
					break;
				    case 'T':
					numenemies--;
					curscore += curobj->mass*3/2;
					if (curobj == enemies)
					    enemies = curobj->next;
					deados = 0;
					break;
				    case 'R': case ' ':
					numenemies--;
					curscore += curobj->mass*3;
					if (curobj == enemies)
					    enemies = curobj->next;
					deados = 0;
					break;
				    case 'G':
					numenemies--;
					numgorns--;
					if (madgorns)
					    curscore += curobj->mass/2;
					else
					    curscore += curobj->mass*2;
					if (curobj == enemies)
					    enemies = curobj->next;
					{
					    int xxx,yyy;

					    for (xxx = -1; xxx<=1; xxx++)
						for (yyy = -1; yyy<=1; yyy++)
						    if (rand_mod(2+massacre))
							fire_torp(curobj,
							    yyy,xxx);
					}
					deados = 0;
					break;
				    case '*':
					banging = TRUE;
					numstars--;
					break;
				    case '@':
					banging = TRUE;
					numstars--;
					numinhab--;
					break;
				    case '|': case '-': case '/': case '\\':
					banging = TRUE;
					deados = 0;
					break;
				    case 'x':
					curscore += 10;
					deados = 0;
					break;
				    case 'X':
					curscore += 100;
					numxes--;
					deados = 0;
					break;
				    case '0':
					curscore += 35;
					numos--;
					deados += 3;
					break;
				    case 'o':
					curscore += 100;
					numos--;
					deados++;
					break;
				    case 'O':
					curscore += 200;
					numos--;
					deados += 2;
					break;
				    }
				    unmake_object(curobj);
				}
				else {
				    if (!biggie)
					biggie = curobj;
				    else {
					if (biggie->mass > curobj->mass)
					    bounce(curobj);
					else {
					    bounce(biggie);
					    biggie = curobj;
					}
				    }
				}
			    }
			    if (biggie) {
				occupant[y][x] = biggie;
				mvaddch(y+1,x*2, biggie->image);
			    }
			    else {
				occupant[y][x] = 0;
				mvaddch(y+1, x*2, ' ');
			    }
			}
		    }
		}
	    }
	}
    }
    do_bangs();
    if (numcrushes && movers->type == Crusher)
	movers->vely = 0;
    if (curobj = base) {
	char ch;

	curobj->velx = 0;
	curobj->vely = 0;
	curobj->energy += 25*lookaround(curobj->posy,curobj->posx,Star);
	if (curobj->energy > 10000)
	    curobj->energy = 10000;
	if (curobj->energy >= 1000)
	    ch = 'B';
	else
	    ch = 'b';
	if (ch != curobj->image) {
	    setimage(curobj, ch);
	}
    }
    if (curobj = ent) {
	char ch;

	if (entmode == 0) {
	    curobj->velx = 0;
	    curobj->vely = 0;
	}
	if (base && !cloaking && !curobj->velx && !curobj->vely &&
	  lookfor(curobj->posy,curobj->posx,Base)) {
	    int tmp;

	    tmp = (int) (base->energy - 1000 < entmax - curobj->energy ?
		         base->energy - 1000 : entmax - curobj->energy);
	    if (tmp < 0)
		tmp = 0;
	    curobj->energy += tmp;
	    base->energy -= tmp;
	    tmp = (btorp < 50 - etorp ?
		   btorp : 50 - etorp);
	    etorp += tmp;
	    btorp -= tmp;
	}
	if (curobj->energy >= 500)
	    ch = cloaked?'C':'E';
	else
	    ch = cloaked?'c':'e';
	if (ch != curobj->image) {
	    setimage(curobj, ch);
	}
    }
}

lookaround(y, x, what)
register int y, x;
register char what;
{
    register count=0, xp, xm;

    if (occupant[y][xp=(x+XSIZE01)%XSIZE]->type == what)  /* 0, 1 */
	count++;
    if (occupant[y][xm=(x+XSIZE99)%XSIZE]->type == what)  /* 0, -1 */
	count++;
    if (occupant[y=(y+YSIZE99)%YSIZE][xp]->type == what)  /* -1, 1 */
	count++;
    if (occupant[y][x]->type == what)                     /* -1, 0 */
	count++;
    if (occupant[y][xm]->type == what)                    /* -1, -1 */
	count++;
    if (occupant[y=(y+2)%YSIZE][xp]->type == what)        /* 1, 1 */
	count++;
    if (occupant[y][x]->type == what)                     /* 1, 0 */
	count++;
    if (occupant[y][xm]->type == what)                    /* 1, -1 */
	count++;
    return (count);
}

lookfor(y, x, what)
register int y, x;
register char what;
{
    register int xp, xm;

    if (occupant[y][xp=(x+XSIZE01)%XSIZE]->type == what ||  /* 0, 1 */
        occupant[y][xm=(x+XSIZE99)%XSIZE]->type == what ||  /* 0, -1 */
        occupant[y=(y+YSIZE99)%YSIZE][xp]->type == what ||  /* -1, 1 */
        occupant[y][x]->type == what                    ||  /* -1, 0 */
        occupant[y][xm]->type == what                   ||  /* -1, -1 */
        occupant[y=(y+2)%YSIZE][xp]->type == what       ||  /* 1, 1 */
        occupant[y][x]->type == what                    ||  /* 1, 0 */
        occupant[y][xm]->type == what)                      /* 1, -1 */
	return(1);
    return (0);
}

make_plink(y,x)
int x,y;
{
    move(y+1,x*2,(char*)0);
    beg_qwrite();
    *filler = '@';
    qwrite();
    if (occupant[y][x])
	qaddc(occupant[y][x]->image);
    else
	qaddspace();
    end_qwrite();
}

make_blast(y,x,mass,size)
int x,y,size;
long mass;
{
    bangy[nxtbang] = y;
    bangx[nxtbang] = x;
    bangm[nxtbang] = mass;
    bangs[nxtbang++] = size;
    move(y+1,x*2,(char*)0);
    beg_qwrite();
    *filler = '@';
    qwrite();
    *filler = '#';
    qwrite();
    *filler = '@';
    qwrite();
    *filler = '#';
    qwrite();
    *filler = '@';
    qwrite();
    if (occupant[y][x])
	qaddc(occupant[y][x]->image);
    else
	qaddspace();
    end_qwrite();
}

do_bangs()
{
    register int x, y, i, j;

    /* read blast list and update blast array */
    for (i=0; i<nxtbang; i++) {
	if (bangm[i] != 32767)
	    bangm[i] *= 4;
	for (y=bangy[i]-bangs[i],x=bangx[i]-bangs[i],j=bangs[i]<<1;j>=0;
	  y++,x++,--j) {
	    yblasted[yy[j] = (y+YSIZE00) % YSIZE] = TRUE;
	    xblasted[xx[j] = (x+XSIZE00) % XSIZE] = TRUE;
	}
	blasted = TRUE;
	for (y=bangs[i]<<1;y>=0;--y) {
	    for (x=bangs[i]<<1;x>=0;--x) {
		if (bangm[i] != 32767 ||
		  occupant[yy[y]][xx[x]]->type != Web)
		    blast[yy[y]][xx[x]] += bangm[i];
	    }
	}
    }
}

sgn(x)
int x;
{
    return x ? (x>0 ? 1 : -1) : 0;
}

bounce(obj)
register OBJECT *obj;
{
    register int x, y, count=0;

    y = (obj->posy - sgn(obj->vely) + YSIZE00) % YSIZE;
    x = (obj->posx - sgn(obj->velx) + XSIZE00) % XSIZE;
    while (occupant[y][x]) {
	y = (y + rand_mod(3) - 1 + YSIZE00) % YSIZE;
	x = (x + rand_mod(3) - 1 + XSIZE00) % XSIZE;
	if (++count > 10000) {     /* if universe full, get out of it fast */
	    unmake_object(obj);
	    if (ent) unmake_object(ent);
	    if (base) unmake_object(base);
	    finish = 1;
	    return;
	}
    }
    obj->posy = y;
    obj->posx = x;
    obj->vely = 0;
    obj->velx = 0;
    occupant[y][x] = obj;
    mvaddc(y+1, x*2, obj->image);
}

OBJECT *
make_object(typ, img, py, px, vy, vx, energ, mas, where)
char typ;
char img;
int px, py, vx, vy;
long energ, mas;
OBJECT *where;
{
    register OBJECT *obj;

    if (free_root.next == &free_root)
	obj = (OBJECT *) malloc(sizeof(root));
    else {
	obj = free_root.next;
	free_root.next = obj->next;
	obj->next->prev = &free_root;
    }
    obj->type = typ;
    obj->image = img;
    obj->next = where;
    obj->prev = where->prev;
    where->prev = obj;
    obj->prev->next = obj;
    obj->velx = vx;
    obj->vely = vy;
    obj->contend = 0;
    obj->strategy = 0;
    obj->posx = px;
    obj->posy = py;
    if (typ != Torp && typ != Web) {
	occupant[py][px] = obj;
    }
    obj->energy = energ;
    obj->mass = mas;
    return(obj);
}

unmake_object(curobj)
register OBJECT *curobj;
{
    curobj->prev->next = curobj->next;
    curobj->next->prev = curobj->prev;
    if (curobj == movers) {
	movers = curobj->next;
    }
    free_object(curobj);
}

free_object(curobj)
register OBJECT *curobj;
{
    curobj->next = free_root.next;
    curobj->prev = &free_root;
    free_root.next->prev = curobj;
    free_root.next = curobj;
}

fire_torp(from, ydir, xdir)
register OBJECT *from;
register int ydir, xdir;
{
    register OBJECT *to;

    if (from->type == Enemy ||
       (from == ent && etorp > 0) ||
       (from == base && btorp > 0)) {
	to = occupant[(from->posy+from->vely+ydir+YSIZE00)%YSIZE]
		     [(from->posx+from->velx+xdir+XSIZE00)%XSIZE];
	if (from->type != Enemy || !to || to->vely || to->velx) {
	    if (from->type != Enemy &&
		 (to = isatorp[from==base][ydir+1][xdir+1])) {
		to->vely += ydir;
		to->velx += xdir;
	    }
	    else {
		if (from == ent) {
		    to = make_object(Torp, '+', from->posy,from->posx,
			from->vely+ydir,from->velx+xdir, 0L, 1L,&root);
		    to->strategy = 1;
		    aretorps++;
		    isatorp[0][ydir+1][xdir+1] = to;
		    etorp--;
		}
		else if (from == base) {
		    to = make_object(Torp, '+', from->posy,from->posx,
			from->vely+ydir,from->velx+xdir, 0L, 1L,&root);
		    to->strategy = 1;
		    aretorps++;
		    isatorp[1][ydir+1][xdir+1] = to;
		    btorp--;
		}
		else if (from->image == 'G') {
		    numos++;
		    to = make_object(Torp, 'o', from->posy,from->posx,
			from->vely+ydir,from->velx+xdir, 100L, 1L,&root);
		    to->strategy = 1;
		    if (madgorns) {
			possiblescore += 35;
			to->image = '0';
			to->mass = 2000;
			to->energy = 2000;
		    }
		    else if (rand_mod(120)+10 > smarts)
			possiblescore += 100;
		    else {
			possiblescore += 200;
			to->image = 'O';
		    }
		}
		else {
		    to = make_object(Torp, 'x', from->posy,from->posx,
			from->vely+ydir,from->velx+xdir, 0L, 1L,&root);
		    to->strategy = 1;
		    if (rand_mod(140)+10 > smarts)
			possiblescore += 10;
		    else {
			possiblescore += 100;
			to->image = 'X';
			to->mass = 1000+super*20;
			numxes++;
		    }
		}
	    }
	}
    }
}

attack(attackee)
OBJECT *attackee;
{
    register int dx, dy, curx, cury, prob;
    register OBJECT *obj;
    bool torps, webnear;
    bool thru_stars;

    if (attackee) {
	for (dx= -1; dx<=1 ; dx++) {
	    for (dy= -1; dy<=1; dy++) {
		if (dx||dy) {
		    cury = attackee->posy;
		    curx = attackee->posx;
		    torps = webnear = thru_stars = FALSE;
		    for (prob = scandist;prob;prob--) {
			cury = (cury + dy + YSIZE00) % YSIZE;
			curx = (curx + dx + XSIZE00) % XSIZE;
			if (obj = occupant[cury][curx]) {
			    switch (obj->image) {
			    case 'K': case 'R': case ' ':
				if (rand_mod(51 - sm50) <= prob) {
				    switch (obj->strategy||thru_stars?0:
					  rand_mod(ent?4:2)) {
				    case 1: case 2:
					if (-dy + attackee->vely == obj->vely
					 && -dx + attackee->velx == obj->velx)
					    fire_torp(obj,
					     -dy + attackee->vely,
					     -dx + attackee->velx);
					else
					    fire_torp(obj,
					     -dy + attackee->vely - obj->vely,
					     -dx + attackee->velx - obj->velx);
					if (obj->image == ' ')
					    setimage(obj, 'R');
					break;
				    case 3: {
					int newspeed =
					    rand_mod(prob<5&&smarts>70?4:3)-1;
					
					obj->vely = -dy * newspeed;
					obj->velx = -dx * newspeed;
					if (newspeed >= 0 &&
					    !rand_mod(82-sm80)) {
					    obj->vely += attackee->vely;
					    obj->velx += attackee->velx;
					}
					break;
				    }
				    case 0:
					if (!torps && obj->energy > 1000) {
					    fire_phaser(obj, -dy, -dx);
					    if (smarts > 40 &&
					       (scandist-prob > 5
						|| attackee==base) &&
					       (massacre || obj->strategy ||
					        rand_mod(2)))
						while (rand_mod(2))
						    fire_phaser(obj, -dy, -dx);
					    if (obj->image == ' ')
						setimage(obj, 'R');
					}
					if (obj->strategy) {
					    obj->velx = obj->vely = 0;
					    if (obj->energy < 1000 ||
						  bvely || bvelx)
						obj->strategy = 0;
					}
					else if ((attackee==base||cloaking) &&
						 scandist-prob > 5 &&
					         !(rand_mod(
						   ent?antibase*2:antibase)) )
					    obj->strategy = 1;
					break;
				    }
				}
				goto bombout;
			    case 'G':
				if (thru_stars && obj->strategy < 7)
				    goto bombout;
				if (obj->strategy) {
				    if (madgorns || !rand_mod(4)) {
					obj->vely = attackee->vely;
					obj->velx = attackee->velx;
				    }
				    obj->strategy += (!torps && deados > 10);
				    if (obj->strategy > 4)
					madgorns = TRUE;
				    if (!torps && obj->strategy > 5) {
					do {
					    fire_phaser(obj, -dy, -dx);
					} while (rand_mod(2));
				    }
				}
				else if (numgorns >= numenemies-1 &&
				    deados > 15+numgorns*5)
				    obj->strategy = 1;
				if (madgorns || rand_mod(51 - sm50) <= prob) {
				    if (-dy + attackee->vely == obj->vely
				     && -dx + attackee->velx == obj->velx)
					fire_torp(obj,
					 -dy + attackee->vely,
					 -dx + attackee->velx);
				    else
					fire_torp(obj,
					 -dy + attackee->vely - obj->vely,
					 -dx + attackee->velx - obj->velx);
				}
				goto bombout;
			    case 'T':
				if (thru_stars)
				    goto bombout;
				if (massacre || smarts > 80 || madgorns)
				    webnear += rand_mod(2);
				if (webnear && scandist-prob > 5) {
				    if (massacre || rand_mod(50) < super) {
					if (!torps && obj->energy > 1000) {
					    fire_phaser(obj, -dy, -dx);
					    while (!rand_mod(57-sm55))
						fire_phaser(obj, -dy, -dx);
					}
				    }
				}
				goto bombout;
			    case 'C': case 'c':
				if (thru_stars)
				    goto bombout;
				break;
			    case '+':
				torps = FALSE;
				thru_stars = FALSE;
				break;
			    case '|': case '-': case '/': case '\\':
				if (thru_stars)
				    goto bombout;
				webnear = (scandist-prob < 3);
				torps = FALSE;
				break;
			    case 'x':
				if (thru_stars)
				    goto bombout;
				torps = TRUE;
				break;
			    case 'o': case 'O': case '0':
				if (thru_stars)
				    goto bombout;
				torps = TRUE;
				if (rand_mod(99+3*scandist) < smarts+3*prob) {
				    obj->vely = -dy + attackee->vely;
				    obj->velx = -dx + attackee->velx;
				    if (!obj->strategy) {  /* not a mover? */
					obj->strategy = 1;
					obj->prev->next = obj->next;
					obj->next->prev = obj->prev;
					root.prev->next = obj;
					obj->prev = root.prev;
					root.prev = obj;
					obj->next = &root;
				    }
				}
				if (obj->image != '0')
				    break;
			    /* DROP THROUGH! */
			    case 'X':
				torps = TRUE;
				if (thru_stars)
				    goto bombout;
				if (prob == scandist) {
				    int y, x;

				    blast[y=(obj->posy+obj->vely+YSIZE00)%YSIZE]
					 [x=(obj->posx+obj->velx+XSIZE00)%XSIZE]
				      += (obj->image == '0' ? 2000 : 200);
				    yblasted[y] = TRUE;
				    xblasted[x] = TRUE;
				    blasted = TRUE;
				}
				break;
			    case '*': case '@':
				if (!thru_stars)
				    if (rand_mod(97-sm95))
					goto bombout;
				    else
					thru_stars = TRUE;
				break;
			    default:
				goto bombout;
			    }
			}
			else {
			    if (thru_stars)
				goto bombout;
			}	
		    }
bombout:            ; /* end of loop */
		}
	    }
	}
    }
}

fire_phaser(obj, dy, dx)
OBJECT *obj;
int dy, dx;
{
    register int y, x, skipping, size=5000;
    int decr = 50, oldy, oldx;
    static char curchar[] = "@* ";

    if (obj == ent)
	decr = 100;
    else if (obj == base) {
	decr = 1000;
	size = 200;
    }
    if (!dy)
	curchar[2] = '-';
    else if (!dx)
	curchar[2] = '!';
    else if (dy == dx)
	curchar[2] = '\\';
    else
	curchar[2] = '/';
    if (obj->energy >= decr) {
	obj->energy -= decr;
	for (
	  /* initialize */
	  skipping = (obj != base),
	  y = (obj->posy+(obj==base?dy*2:dy)+YSIZE00)%YSIZE,
	  x = (obj->posx+(obj==base?dx*2:dx)+XSIZE00)%XSIZE;
	  /* while */
	  size && (!occupant[y][x]||(skipping && occupant[y][x]->type==Star));
	  /* at end of loop */
	  y = (y+dy+YSIZE00) % YSIZE,
	  x = (x+dx+XSIZE00) % XSIZE,
	  size = size * 3 / 4 ) {
	    move(y+1,x*2,(char*)0);
	    beg_qwrite();
	    if (obj == base || obj->image == 'T') {
		*filler = '@';
		qwrite();
		*filler = '#';
		qwrite();
		*filler = '~';
		qwrite();
		*filler = '%';
		qwrite();
		*filler = ':';
		qwrite();
		*filler = '@';
	    }
	    else {
		*filler = size >= 500 ?
			  *curchar : (size >= 50 ?
				     curchar[1] :
				     curchar[2]);
	    }
	    qwrite();
	    if (occupant[y][x])
		qaddc(occupant[y][x]->image);
	    else {
		qaddspace();
		if (skipping)
		    skipping = 0;
	    }
	    end_qwrite();
	}
	if (size) {
	    if (occupant[y][x]->type != Crusher ||
	        (dy==0 && dx==-(occupant[y][x]->velx))
	       ) {
		char img = occupant[y][x]->image;

		move(y+1,x*2,(char*)0);
		beg_qwrite();
		if (img == ' ') {
		    occupant[y][x]->image = 'R';
		    occupant[y][x]->strategy = 0;
		    *filler = 'R';
		    qwrite();
		    qwrite();
		}
		else if (img == 'C' || img == 'c') {
		    cloaked = 0;
		    img += 2;
		    occupant[y][x]->image = img;
		    *filler = img;
		    qwrite();
		    qwrite();
		}
		else if (img == 'K' && size > 50)
		    occupant[y][x]->strategy = 0;
		*filler = '@';
		qwrite();
		*filler = '#';
		qwrite();
		*filler = '@';
		qwrite();
		*filler = '#';
		qwrite();
		*filler = '@';
		qwrite();
		qaddc(img);
		end_qwrite();
		oldy = y;
		oldx = x;
		y = (occupant[oldy][oldx]->posy + occupant[oldy][oldx]->vely +
			YSIZE00) % YSIZE;
		x = (occupant[oldy][oldx]->posx + occupant[oldy][oldx]->velx +
			XSIZE00) % XSIZE;
		if (occupant[y][x]->type == Star) {
		    y = occupant[oldy][oldx]->posy;
		    x = occupant[oldy][oldx]->posx;
		}
		if (obj==base)
		    blast[y][x] += size>50 ? 15000 : (size>15 ? 1500 : 150);
		else if (obj==ent)
		    blast[y][x] += size*4;
		else if (obj->image=='T')
		    blast[y][x] += 15000;
		else
		    blast[y][x] += size*smarts/25;
		yblasted[y] = TRUE;
		xblasted[x] = TRUE;
		blasted = TRUE;
	    }
	    else if (occupant[y][x]->type == Crusher &&
	      !dy && dx==occupant[y][x]->velx) {
		occupant[y][x]->image =
		    (occupant[y][x]->velx *= -1) < 0 ? '>' : '<';
	    }
	}
    }
}

tract(obj, dy, dx, to_or_fro)
OBJECT *obj;
int dy, dx;
{
    register int y, x, size=10;
    static char ch;
    OBJECT *tractee;

    if (!dy)
	ch = '|';
    else if (!dx)
	ch = '-';
    else if (dy == dx)
	ch = '/';
    else
	ch = '\\';
    {
	for (
	  y = (obj->posy+dy+YSIZE00)%YSIZE,
	  x = (obj->posx+dx+XSIZE00)%XSIZE;
	  size && (!occupant[y][x]);
	  y = (y+dy+YSIZE00) % YSIZE, x = (x+dx+XSIZE00) % XSIZE, size--) {
	    move(y+1,x*2,(char*)0);
	    beg_qwrite();
	    *filler = ch;
	    qwrite();
	    qwrite();
	    qaddspace();
	    end_qwrite();
	}
	tractee = occupant[y][x];
	if (size) {
	    if (tractee->type != Web &&
		(tractee->mass < obj->mass * 5 ||
		 (tractee->type == Crusher && !dx) ) ) {
		if (tractee == ent) {
		    evely -= dy * to_or_fro;
		    evelx -= dx * to_or_fro;
		}
		else if (tractee == base) {
		    bvely -= dy * to_or_fro;
		    bvelx -= dx * to_or_fro;
		}
		else {
		    tractee->vely -= dy * to_or_fro;
		    tractee->velx -= dx * to_or_fro;
		}
		if (tractee->type == Torp ||
		    tractee->type == Star) {
		    if (!tractee->strategy) {  /* not a mover? */
			tractee->strategy = 1;
			tractee->prev->next = tractee->next;
			tractee->next->prev = tractee->prev;
			root.prev->next = tractee;
			tractee->prev = root.prev;
			root.prev = tractee;
			tractee->next = &root;
		    }
		}
	    }
	    else if (tractee->type == Crusher && !dy && dx==tractee->velx) {
		setimage(tractee, (tractee->velx *= -1) < 0 ? '>' : '<');
	    }
	    if (tractee->mass * 5 > obj->mass)
		return(1);
	}
    }
    return(0);
}

no_can_do(what)
char *what;
{
    noraw();
    fprintf(stderr,"Sorry, your terminal is too %s to play warp.\n",what);
    exit(1);
}

do_tc(s,l)
char *s;
int l;
{
    beg_qwrite();
    tputs(s,l,cmstore);
    end_qwrite();
}

int
comp_tc(dest,s,l)
char *dest;
char *s;
int l;
{
    maxcmstring = dest;
    tputs(s,l,cmstore);
    return(maxcmstring-dest);
}

helper()
{
    clear();
    mvaddstr(0,4,"h or 4          left");
    mvaddstr(1,4,"j or 2          down                Use with SHIFT to fire torpedoes.");
    mvaddstr(2,4,"k or 8          up                  Use with CTRL or FUNCT to fire");
    mvaddstr(3,4,"l or 6          right                   phasers or turbolasers.");
    mvaddstr(4,4,"b or 1          down and left       Use preceded by 'a' or 'r' for");
    mvaddstr(5,4,"n or 3          down and right          attractors or repulsors.");
    mvaddstr(6,4,"y or 7          up and left         Use normally for E or B motion.");
    mvaddstr(7,4,"u or 9          up and right");
    mvaddstr(8,4,"");
    mvaddstr(9,4,"del or %        fire photon torpedoes in every (reasonable) direction.");
    mvaddstr(10,4,"s               stop all torpedoes.");
    mvaddstr(11,4,"S or 0          stop the Enterprise when in warp mode.");
    mvaddstr(12,4,"d               destruct all torpedoes (quite useful).");
    mvaddstr(13,4,"D               destruct the current vessel (commit suicide).");
    mvaddstr(14,4,"i/w             switch to Enterprise & put into impulse/warp mode.");
    mvaddstr(15,4,"c/v             switch to Enterprise & make cloaked/visible.");
    mvaddstr(16,4,"p               switch to Base.");
    mvaddstr(17,4,"o               toggle to other vessel (from E to B, or vice versa.)");
    mvaddstr(18,4,"");
    mvaddstr(19,4,"^R      refresh the screen.              ^Z      suspend the game.");
    mvaddstr(20,4,"q       exit this round (if you haven't typed q within 10 cycles).");
    mvaddstr(21,4,"Q       exit this game.");
    mvaddstr(22,4,"");
    mvaddstr(23,4,"                   [Hit space to continue]");
    do {
	if (read(0, spbuf, 1) < 0)
	    hangup_catcher();
	*spbuf &= 0177;
    } while (*spbuf != ' ');
    rewrite();
    
}

rewrite()
{
    register int x, y;

    clear();
    for (y=0; y<YSIZE; y++) {
	for (x=0; x<XSIZE; x++) {
	    if (occupant[y][x]) {
		mvaddc(y+1,x*2,occupant[y][x]->image);
	    }
	}
    }
    sprintf(spbuf,
    "%-4s%9ld  E: %4d %2d B: %5d %3d Stars: %-3d Enemies: %-3d Stardate%5d.%1d",
	"   ", 0L, 0, 0, 0, 0, 0, 0, timer/10, timer%10);
    mvaddstr(0,0,spbuf);
    oldeenergy = oldbenergy = oldcurscore =
    oldstatus = oldetorp = oldbtorp = oldstrs = oldenemies = -1;
					/* force everything to fill in */
    if (!ent)
	etorp = 0;
    if (!base)
	btorp = 0;
    display_status();
}

#ifdef SIGTSTP
void cont_catcher()
{
    savetty();
    raw();
    noecho();
    nonl();
}

mytstp()
{
    resetty();
#ifdef SIGTSTP
    kill(0,SIGTSTP);
#else
    if (fork())
	wait(0);
    else {
	char *shell = getenv("SHELL");

	setuid(getuid());
	if (!*shell)
	    shell = "/bin/sh";
	execl(shell,shell,0);
	exit(1);
    }
#endif
    rewrite();
}
#endif

move(y, x, chadd)
int y, x;
char *chadd;
{
    register int ydist, xdist;
    register int i;
    register char *s;

    ydist = y - real_y;
    xdist = x - real_x;
    i = ydist * (ydist < 0 ? -UPsize : DOsize) +
        xdist * (xdist < 0 ? -BCsize : NDsize);
    beg_qwrite();
    if (i <= CMsize) {
	if (ydist < 0)
	    for (; ydist; ydist++)
		for (i=UPsize,s=UP; i; i--)
		    qaddch(*s++);
	else
	    for (; ydist; ydist--)
		for (i=DOsize,s=DO; i; i--)
		    qaddch(*s++);
	if (xdist < 0)
	    for (; xdist; xdist++)
		for (i=BCsize,s=BC; i; i--)
		    qaddch(*s++);
	else
	    for (; xdist; xdist--)
		for (i=NDsize,s=ND; i; i--)
		    qaddch(*s++);
    }
    else {
	tputs(tgoto(CM,x,y),0,cmstore);
    }
    real_y = y;
    real_x = x;
    if (chadd) {
	qaddch(*chadd);
    }
    if (maxcmstring != cmbuffer)
	end_qwrite();
}

movc3(len,src,dest)
#ifdef vax
char *dest, *src;
int len;
{
    asm("movc3 4(ap),*8(ap),*12(ap)");
}
#else
register char *dest, *src;
register int len;
{
    for (; len; len--) {
	*dest++ = *src++;
    }
}
#endif

/* print out a file, stopping at form feeds */

page(filename,num)
char *filename;
bool num;
{
    FILE *tmpfp = fopen(filename,"r");
    int linenum = 1;

    if (tmpfp != NULL) {
	while (fgets(spbuf,sizeof(spbuf),tmpfp) != NULL) {
	    if (*spbuf == '\f') {
		printf("[Hit return to continue] ");
		fgets(spbuf,sizeof(spbuf),stdin);
	    }
	    else {
		if (num)
		    printf("%3d   %s",linenum++,spbuf);
		else
		    printf("%s",spbuf);
	    }
	}
	fclose(tmpfp);
    }
}

wscore()
{
    clear();
    printf("                             TOP WARPISTS\n\n");
    printf("RANK  WHO                     AKA        SCORE DIFF  CUMDIFF  WHEN\n");
    page(SCOREBOARD,TRUE);
    printf("                       [hit return to continue]");
    fgets(spbuf,sizeof(spbuf),stdin);
    clear();
    printf("                       TOP (LOW-SPEED) WARPISTS\n\n");
    printf("RANK  WHO                     AKA        SCORE DIFF  CUMDIFF  WHEN\n");
    page(LSCOREBOARD,TRUE);
    printf("                       [hit return to continue]");
    fgets(spbuf,sizeof(spbuf),stdin);
    clear();
    printf("          GAMES SAVED OR IN PROGRESS\n\n");
    printf("WHO           SCORE  DF   CDF  E  B  WV  FLAGS\n");
    sprintf(spbuf,"/bin/cat %ssave.*",SAVEDIR);
    execl("/bin/sh", "sh", "-c", spbuf, 0);
    exit(1);
}

eat_typeahead()
{
#ifdef FIONREAD
    if (input_pending())
	if (read(0, spbuf, sizeof(spbuf)) < 0)
	    hangup_catcher();
#else
	if (input_pending(spbuf) < 0)
		hangup_catcher();
#endif
}