[net.sources] warp 6.0 distribution kit, finally

lwall@sdcrdcf.UUCP (Larry Wall) (08/13/83)

			WARP 6.0 DISTRIBUTION KIT

(The last line of the kit should say "end of warp distribution kit".  If
it doesn't, you haven't got the whole thing.  This file is ~123000 bytes.)

Installation procedure:

    1)	Decide what uid you want warp to setuid to (NOT root).  Choose a
	uid that nobody untrustworthy can log into, because warp uses
	only the setuid feature to protect its files--it does not encrypt
	anything, nor does it store anything in the user's directories
	(with the possible exception of the full name of the player).
	All of its files will be stored in the directory in which this
	distribution shell script is run, and will belong to the uid in
	force when the script is run.

    2)	Login to the uid you have chosen or created, and cd to a place where
	you don't mind creating a subdirectory, such as /usr/games.

    3)	Create a subdirectory called warplib, and cd to it.  If you already
	have a warplib, you may reuse it, because this shell script will
	create a subdirectory whose name will be the version number.

    4)	Move everything after the line of asterisks below to a shell script
	file in warplib ("dist" would be a safe name), and chmod 700 dist.
	(If your distribution came in more than one hunk, append the
	additional hunks to the end of dist.  Be sure delete the header
	down to the row of asterisks on any subsequent hunks as well as
	the first, or you may end up with peculiar-looking C.)

    5)	Execute dist, or whatever you have called it.  It may ask you a few
	questions, depending on what it finds or doesn't find on your system.
	If you aren't sure of an answer, it's worth stopping to find out.
	You MUST execute dist in order for things to get configured right.
	About 1.5 persons out of 100 will try to take it apart by hand and
	then wonder why WARPDIR isn't defined.  If dist doesn't run right
	because of a brain-damaged shell:

	--  if your shell doesn't understand # comments, delete them.

	--  if your shell doesn't like the syntax of something, say, a
	    conditional, delete the conditional and substitute the
	    intelligent thing.

	--  if the `pwd` doesn't insert the current directory name, put that
	    in, wherever `pwd` occurs.

	--  if all else fails, and you MUST tear it apart by hand, then
	    be sure to make all the proper substitutions in those parts
	    that do variable substitution.

    6)	Do a make.  (Dist will start it automatically if you tell it to.)

    7)	You should now have an executable file called warp.  Link or copy
	it to wherever it will be executable by everyone, such as
	/usr/games.  (Saying "make install" will attempt to put warp into
	/usr/games.)  Ensure that the setuid bit stays set and that the file
	still belongs to the uid you originally selected.  Ensure that the
	files in WARPDIR are NOT writeable by the world but that WARPDIR
	itself is u+rw o+rx and belongs to the correct uid.  Note that
	WARPDIR is a subdirectory of warplib.

    8)	Play warp.  If it doesn't produce reasonable star distributions
	(sometimes uniformly random, sometimes clumped, sometimes a
	predefined scenario) then perhaps your random number generator
	is not what warp expects.  Ensure that warp has a random number
	generator that produces 31 bits worth of integer, i.e. numbers
	from 0 to 2**31-1, or that you have defined the RAND16 flag.
	Any strange random number generators that produce 17..30 bits
	should work if RAND16 is defined, since higher bits are discarded.

    9)	The system administrator should feel free to edit the warp.news
	file, which is printed whenever anyone starts warp.

    10)	If you have to take heroic measures to get warp to work on your
	system, and if there are other systems in the wide wide world like
	yours, then drop me a line giving the gory details, and I will
	try to incorporate them in the distribution kit.

********************************************************************************
#! /bin/sh

wv='6.0'
echo "Beginning installation of warp version $wv into "`pwd`"/$wv."
echo ""
if mkdir $wv
then
    cd $wv
else
    echo "Cannot create subdirectory $wv."
    if test -f $wv -o -d $wv
    then
	echo "Please rename the current $wv to something else and re-run."
    else
	if test ! -w .
	then
	    echo "You can't write this directory.  Please fix the problem and re-run."
	else
	    echo "I can't tell what's wrong.  Please fix the problem and re-run."
	fi
    fi
    exit 1
fi

if test -r /usr/lib/libjobs.a
then
    echo 'Job control found'
    jbs='-ljobs'
else
    echo 'Job control not found--subshell substituted'
    jbs=''
fi

if test -r /usr/lib/libnm.a
then
    echo 'nm library found'
    nm='-lnm'
else
    echo 'nm library not found--normal math library will have to do'
    nm=''
fi

if test -r /usr/include/sys/timeb.h
then
    echo 'ftime routine found'
    ftim='D'
else
    echo 'ftime routine not found--you may have to use -l at low baud rates'
    ftim='U'
fi

echo ""
echo "Please type y or n to the following questions:"

echo ""
echo "Some random number generators produce 16 bits, others 31 bits."
echo "(If yours does neither, pretend like it does 16 for the moment."
echo "Likewise if you can't find out easily, assume 16 bits and it should work.)"
echo -n "Does your random number generator produce exactly 31 bits like Berkeley's? "
read ans
if test "$ans" = y
then
    rnd='RAND31'
else
    rnd='RAND16'
fi

echo ""
echo "Does your passwd file keep full names in Berkeley format (name first"
echo -n "thing after ':')? "
read ans
if test "$ans" = y
then
    namalg=1
else
    echo ""
    echo "Does your passwd file keep full names in USG format (name sandwiched"
    echo -n "between a '-' and a '(')? "
    read ans
    if test "$ans" = y
    then
	namalg=2
    else
	echo "Full name will be kept in ~/.fullname"
	namalg=3
    fi
fi

if test "$namalg" != 3
then
    echo ""
    echo "The scoreboard can be kept with one score per login name, or one score"
    echo "per full name.  If users can change their full name, or if there is"
    echo "more than one person on your system with the same full name, you should"
    echo "keep the score by login name (the full name is still printed)."
    echo -n "Do you want the scoreboard kept by full name? "
    read ans
    if test "$ans" = y
    then
	fullnam='D'
    else
	fullnam='U'
    fi
else
    fullnam='U'
fi

# The make file
# (does variable, command substitution at installation time)

echo Installing Makefile
cat >Makefile <<!STUFFY!FUNK!
flags = -ltermlib $nm -lm $jbs -DWARPDIR=\"`pwd` -${ftim}FTIMER -D${rnd}
moreflags = -DFNAMEALG=${namalg} -${fullnam}BYFULLNAME

all: warp smap.0 smap.1 smap.2 smap.3 smap.4 smap.5 smap.6 smap.7 warp.6
	echo "Warp make finished"
warp: wtmp
	cat </dev/null >>warp.log
	cat </dev/null >>warp.top
	cat </dev/null >>warp.lowtop
	cat </dev/null >wart
	chmod 600 war?
	rm -f wart oldwarp
	cat </dev/null >>warp
# The above ritual is supposed to prevent the next line from blowing up
	mv warp oldwarp
	mv wtmp warp
	chmod 4711 warp
	chmod 000 oldwarp
wtmp: warp.c
	cc warp.c -o wtmp -n -O $(flags) $(moreflags)
# It is my belief that -n implies -i for those who need separate I and D.
# If this isn't true, put a -i above and tell me I'm all wet.
smap.0: smp.0 sm
	sm <smp.0 >smap.0
smap.1: smp.1 sm
	sm <smp.1 >smap.1
smap.2: smp.2 sm
	sm <smp.2 >smap.2
smap.3: smp.3 sm
	sm <smp.3 >smap.3
smap.4: smp.4 sm
	sm <smp.4 >smap.4
smap.5: smp.5 sm
	sm <smp.5 >smap.5
smap.6: smp.6 sm
	sm <smp.6 >smap.6
smap.7: smp.7 sm
	sm <smp.7 >smap.7
sm: sm.c
	cc sm.c -o sm
warp.6: warp.man
	nroff -man warp.man >warp.6
install:
	ln warp extralink
	mv extralink /usr/games/warp
# The above should conserve disk space if we are in same filesystem, but
# not blow up if we aren't.  Of course, if we can't write /usr/games...
!STUFFY!FUNK!

# The manual page

echo Installing warp.man
cat >warp.man <<\!STUFFY!FUNK!
.TH WARP 6
.SH NAME
warp - a real-time space war game
.SH SYNOPSIS
.B warp [options]
.SH DESCRIPTION
.I Warp
is a real-time space war game that requires skill and quick thinking.
"Real-time" in this context means that the enemies keep moving (and shooting)
even if you don't.
A unique feature of
.I warp
is that blast propagates; it is unhealthy to remain near things that are
in the process of blowing up.
If a given universe is above a critical density it may chain react.
Scoring is like many popular arcade games--there are multiple waves which
get harder and harder as you go along.
Nobody has ever maxed out the scoreboard without cheating.
.PP
Unlike many space-war games,
.I warp
is not simply a shooting gallery.
Along with phasers and photon torpedoes, you have tractor beams and a cloaking
device.
Skill in navigation is important.
It helps to be schizophrenic, because you must manage an Enterprise and a Base
simultaneously.
And enemies do not simply shoot back.
You can get tailed, snuck up upon, hemmed in, rammed, loved to death, and
eaten.
And if you should happen to get bored by the enemies (not bl**dy likely!),
you can always watch the interesting star patterns.
In fact, you have to, since your tactics will depend upon what kind of
universe you find yourself in.
.PP
.I Warp
is played in a double wraparound universe, i.e. the bottom is connected to the
top, and the right is connected to the left.
You need a crt with random cursor addressing and at least 24 lines by 80
columns.
For more information about about how to play, simply run
.I warp
and say "y" when it asks if you want to see the instructions.
There is also a single-page command summary that you can get while playing
by typing a "?".
.PP
Command line options include:
.TP 5
.B -b
Put
.I warp
into beginner mode.
Makes the difficulty increase more slowly, but penalizes you for it.
.TP 5
.B -d<n>
Sets the initial difficulty to
.BR n .
.TP 5
.B -l
Play a low-speed game.
Changes the basic cycle time from 1 second to 2 seconds.
This switch is automatically set at baud rates below 2400.
You may want to set it at higher speeds if your terminal cannot keep up
with the output.
(This should never happen on BSD systems, which have an IOCTL call to
determine output queue length.)
Because this makes the game easier, a separate scoreboard is kept for
low-speed games.
.TP 5
.B -s
Just prints out the scoreboards and saved games and then exits.
.TP 5
.B -v
Prints out the version number.
.TP 5
.B -x
Play an experimental game.
This causes
.I warp
to ignore any saved game, and disables the ability to save
the current game.
Thus you can play around with something or show
.I warp
to someone without jeopardizing a currently saved game.
.SH AUTHOR
Larry Wall <lwall@sdcrdcf.UUCP>
.SH FILES
~/.fullname, if full names aren't in /etc/passwd
.SH DIAGNOSTICS
Generally self-documenting, as they say.
.SH BUGS
Addicting.
At the end of a wave, all you have to do to keep going is hit a space.
You see the message "Hit space to continue" and automatically hit space.
About 2 seconds later you remember you wanted to go home, but by then
it's too late to escape without penalty.
.PP
You can't kill a backgrounded
.I warp
process directly, because it is running setuid.
You have to use the killer built in to
.IR warp .
.PP
There ought to be a space amoeba.
!STUFFY!FUNK!
# The help file

echo Installing warp.doc
cat >warp.doc <<\!STUFFY!FUNK!
Warp is a real-time space war game.  This means that the enemies will keep
playing even when you sit still.  Another peculiarity is that things which
blow up can damage other things around them.  Universes above a critical
density may chain react.

The game starts at difficulty 1, and gets more difficult with each
succeeding wave, up to difficulty 99.  You're not likely to get that far.
(Invoking warp with a -b switch causes the difficulty to increase more
slowly, but games count only a tenth as much.)  The game starts with
5 Enterprises and 3 Bases, and you get more for surviving long enough.
The game is over when you run out of Enterprises and Bases.

The object of the game is to get as many points as possible.  This is done
by destroying as many enemies as possible.  This is not a trivial task.
Each wave starts with one Enterprise and one Base, and continues until
either both the Enterprise and Base are destroyed, or all the enemies
(including any homing torpedoes) are destroyed.  It is possible to abort a
wave, but you will be penalized for it.  The game may be saved between waves.

A -x switch causes any saved game to be ignored, and causes the new game
not to be saveable.  Hence it is possible to run test games without
invalidating a currently saved game.

The game is played in a 23 x 40 double wrap-around universe.  Everybody
(both you and the enemies) gets the chance to move once every second,
unless a -l (low-speed) switch was given or you are under 2400 baud, in
which case it's every two seconds.  The following symbols are displayed:

	E,e         Enterprise with/without shields
	C,c         Cloaked Enterprise with/without shields
	B,b         Base with/without shields
	K           Klingon
	R           Romulan
		    Romulan with cloaking device!
	G           Gorn
	T           Tholian
	A           Apollo
	<           Planet crusher
	+           Friendly torpedo
	x,X         Hostile torpedo
	o,O         Homing torpedo
	*           Star
	@           Inhabited star
	|,-,/,\     Web

The following keys control the DIRECTION of your various actions:

	h or 4          left
	j or 2          down
	k or 8          up
	l or 6          right
	b or 1          down and left
	n or 3          down and right
	y or 7          up and left
	u or 9          up and right

(You will note that the letters are the same as rogue directions, and the
numbers are for use with a keypad.) By themselves, these keys move either
the Enterprise or the Base, whichever is the current vessel.  When shifted,
they fire photon torpedoes in the specified direction from the current
vessel.  When used with either the CTRL key or the FUNCT key, phasers
(turbo-lasers for the Base) are fired in the specified direction.  (CTRL
won't work with numbers, and FUNCT probably doesn't exist on non-TVI
terminals.)  When preceded by an 'a', an attractor beam is fired in the
specified direction, and when preceded by an 'r', a repulsor beam is fired.

These keys have special functions:

	del or %        fire photon torpedoes in every (reasonable) direction
	s               stop all friendly torpedoes
	S or 0          stop the Enterprise when in warp mode
	d               destruct all friendly torpedoes (quite useful)
	D               destruct the current vessel (commit suicide)
	i               switch to Enterprise and put into impulse mode
	w               switch to Enterprise and put into warp mode
	c               switch to Enterprise and put into cloaking mode
	v               switch to Enterprise and put into visible mode
	p		switch to Base (not very mnemonic, but 'b' is taken)
	o               switch from Enterprise to Base, or vice versa

	^R              refresh the screen
	^Z              suspend the game (on a bsd system)
	q               asks if you want to exit this wave (will not work
			    within 10 cycles of previous q command)
	Q		exit this game
	?               display a summary of these commands

Unrecognized keystrokes are ignored.  IF YOU FORGET ALL THE OTHER COMMANDS,
REMEMBER "?".

Commands for moving the Enterprise may operate in one of two ways.  If it
is in impulse mode, movement commands affect the position of the ship;
if it is in warp mode, movement commands affect the velocity instead.
The Base always moves in impulse mode.  Since multiple commands may be
entered in one turn (if you can type fast enough), it is possible to jump
over things even in impulse mode.  In a crowded universe this may be the
only way to go.

(Actually, motion commands always change the velocity--the actual motion
does not occur until the next turn.  Impulse mode simply causes the
velocity to be zeroed out at the end of every turn.  Phaser commands, on
the other hand, are executed immediately.  If you want to move and fire a
phaser, you must wait for the motion to actually occur before typing the
phaser command, or the phaser fires from your old position.  This is a
feature, not a bug, and is intended to reflect reality.  Really.)

If multiple torpedo launching commands are given in a turn, a single torpedo
is launched with extra velocity.  You can thus launch photon torpedoes over
objects in the way, and get them where you want them quickly.  This feature
works well with the destruct button.

NOTE:  Phasers destroy the target by blasting the projected next location of
the object hit.  This means that if the object hit, be it Klingon, Romulan or
Enterprise, changes velocity in the same turn, it can elude the effect of
the phaser!  (Note that this also means that if you phaser a Klingon or
torpedo that is about to ram you, you will be phasered as well as he/she/it.
This can be embarrassing, not to mention deadly.)  Smart players move
immediately upon phasering something at short range, or whenever they
think they might get phasered (in other words, most of the time).

Objects with larger mass can bounce objects with smaller mass out of the way.
In a crowded universe the bouncee can bounce quite a way before finding an
empty place to land.  If you let the Tholians fill up the universe with web,
so that there is no place to bounce to, the Tholians win that wave.

The status line across the top gives the current mode, the number of
points accumulated this wave, the Enterprise's energy and torpedoes, the
Base's energy and torpedoes, the number of stars, the number of enemies,
and the stardate.  You will note that nice things happen to your energy levels
when you put the Enterprise next to the Base, or the Base next to some stars.

An object is destroyed when its energy goes negative, either from a direct
hit, or from the blast of the previous turn's explosions.  Enemies and
stars start with random amounts of energy.  High energy enemies can go warp
2.  A Romulan with sufficient energy maintains a cloaking device.  Tholians
spin web, Gorns shoot homing torpedoes, and the Planet Crusher munches
anything in its way, even Apollo.  Apollo won't let you go unless you kill
him, but he loves you very much and beefs up your shields considerably.
Both Apollo and the Planet Crusher recharge themselves, so you must hit
them hard in a single turn to do them in. (Yes, the Planet Crusher must be
shot in the mouth--he can only die of gluttony--and he blasts out of his
mouth when he dies.)  Tholian web may be crossed only by coasting across it
in warp mode, or by blasting it (but web blasts extend twice as far as
normal blasts, so keep your distance).

Note that because of the size of the Base's turbo-lasers (the Base does not
have phasers) they cannot shoot anything next to the Base.  (This is why the
Death Star died!)  In part, this is to protect the Enterprise.  It also lets
you shoot over one adjacent star.  The Enterprise's phasers will shoot over
a arbitrary number of adjacent, contiguous stars, including inhabited ones.
Phasers die away with distance, so don't expect them to kill everything with
one blow.

While the Enterprise's shields are up (when it is displayed as "E" rather
than "e"), hits on it count only a fifth as much (or even less if you are
moving in warp mode).  The shields are automatically maintained as long as
there are more than 500 units of energy for the Enterprise.  The Base also
has shields, which stay up as long as it has at least 1000 units of energy.

You get points for destroying enemies and hostile torpedoes.  At the end of
a wave, you also get bonus points for saving stars, saving the Enterprise
and Base, and for having an efficiency rating higher that 0.8.  You get
NEGATIVE bonus points for letting inhabited stars get destroyed, and for
giving up.  Bonuses tend to be scaled by the ratio of the number of points
you got over the number of points you could have got.  If you think you are
done with a wave, but it won't quit, there may be Gorn torpedoes that you
haven't destroyed--you must make the universe safe for posterity, you know.

When you have used up your Enterprises and Bases (or quit), your score will
be posted to the scoreboard.  You may see the scoreboard outside of the game
simply by giving the command "warp -s".
!STUFFY!FUNK!

# A sample warp news file.  Feel free to edit.

echo Installing warp.news
cat >warp.news <<\!STUFFY!FUNK!
			  ***  WARP NEWS  ***

Welcome to warp!  Please send any gripes, comments, fantastic ideas, etc.
to lwall@sdcrdcf.uucp (Larry Wall).
!STUFFY!FUNK!

# A program to translate starmaps from one form to another

echo Installing sm.c
cat >sm.c <<\!STUFFY!FUNK!
#include <stdio.h>
#include <ctype.h>

main()
{
    char screen[23][90], buf[10];
    register int y, x;
    int tmpy, tmpx;

    for (x=0; x<79; x++)
	screen[0][x] = ' ';
    screen[0][79] = '\0';
    
    fgets(screen[0],90,stdin);
    if (isdigit(screen[0][0])) {
	int numstars = atoi(screen[0]);

	for (y=0; y<23; y++) {
	    for (x=0; x<79; x++)
		screen[y][x] = ' ';
	    screen[y][79] = '\0';
	}
	
	for ( ; numstars; numstars--) {
	    scanf("%d %d\n",&tmpy,&tmpx);
	    y = tmpy;
	    x = tmpx;
	    screen[y][x+x] = '*';
	}

	for (y=0; y<23; y++) {
	    printf("%s\n",screen[y]);
	}
    }
    else {
	register int numstars = 0;

	for (y=1; y<23; y++) {
	    for (x=0; x<79; x++)
		screen[y][x] = ' ';
	    screen[y][79] = '\0';
	}
	
	for (y=1; y<23; y++) {
	    fgets(screen[y],90,stdin);
	}

	for (y=0; y<23; y++) {
	    for (x=0; x<80; x += 2) {
		if (screen[y][x] == '*') {
		    numstars++;
		}
		else if (screen[y][x] == '\t' || screen[y][x+1] == '\t') {
		    fprintf(stderr,"Cannot have tabs in starmap--please expand.\n");
		    exit(1);
		}
	    }
	}

	printf("%d\n",numstars);

	for (y=0; y<23; y++) {
	    for (x=0; x<80; x += 2) {
		if (screen[y][x] == '*') {
		    printf("%d %d\n",y,x/2);
		}
	    }
	}
    }
    exit(0);
}
!STUFFY!FUNK!

# A scenario to encourage warp mode

echo Installing smp.0
cat >smp.0 <<\!STUFFY!FUNK!
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
!STUFFY!FUNK!

# Another scenario to encourage warp mode

echo Installing smp.1
cat >smp.1 <<\!STUFFY!FUNK!
  *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *
  *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *
  *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  
  *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *
  *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *
  *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *
!STUFFY!FUNK!

# A shooting gallery scenario

echo Installing smp.2
cat >smp.2 <<\!STUFFY!FUNK!
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   * * * *   *                       
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   * * * *   *                       
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
*   * * * *   *                       
!STUFFY!FUNK!

# A superfortress scenario

echo Installing smp.3
cat >smp.3 <<\!STUFFY!FUNK!
        * * * *        
    * * *     * * *    
  * * *   * *   * * *  
  * *   * * * *   * *  
* *   * *     * *   * *
*   * *   * *   * *   *
*   * *   * *   * *   *
* *   * *     * *   * *
  * *   * * * *   * * 
  * * *   * *   * * * 
    * * *     * * * 
        * * * * 











!STUFFY!FUNK!

# block world

echo Installing smp.4
cat >smp.4 <<\!STUFFY!FUNK!
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *

* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *

* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *

* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *

* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *
* * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *   * * * *

!STUFFY!FUNK!

# A double fortress scenario

echo Installing smp.5
cat >smp.5 <<\!STUFFY!FUNK!
* * *
*   *
* * *








                                        * * *
                                        *   *
                                        * * *









!STUFFY!FUNK!

# Eternal strait

echo Installing smp.6
cat >smp.6 <<\!STUFFY!FUNK!
      * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
              * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * *               * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * *             * * * * * * * * * * *           * * * * * * * * * * * *
* * * * * * * * *         * * * * * * * *                   * * * * * * * * * *
* * * * * * * * * *       * * * * * * *         * * *       * * * * * * * * * *
* * * * * * * * * *       * * * * * *       * * * * * * *     * * * * * * * * *
* * * * * * * * *         * * * * * *     * * * * * * * *       * * * * * * * *
* * * * * * * * *       * * * * * * *   * * * * * * * * *       * * * * * * * *
* * * * * * * *       * * * * * * * *   * * * * * * * * * *         * * * * * *
* * * * * *         * * * * * * * * *   * * * * * * * * * * *       * * * * * *
* * * * *       * * * * * * * * * * *   * * * * * * * * * * * *     * * * * * *
* * * * *     * * * * * * * *       *   * * * * * * * * * * * *     * * * * * *
* * * * *     * * * * * * *         *   * * * * * * * * * * *       * * * * * *
* * * * *     * * * * * *           *   * * * * * * * * * *       * * * * * * *
* * * * * *     * * * *     * * *       * * * * * * * * * *     * * * * * * * *
* * * * * * *             * * * *       * * * * * * * * *         * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * *       * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * *       * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * *       * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * *           * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *                   * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
!STUFFY!FUNK!

# Eternal isthmus

echo Installing smp.7
cat >smp.7 <<\!STUFFY!FUNK!
* * *                                                                     * * *
* * * * * * *
      * * * * * * *
            * * * * * *                       * * * * *
                  * * * *                 * * * * * * * * *
                    * * *               * * * *       * * *
                    * * *             * * *               * *
                  * * * *             * *                 * * *
                  * * *               *                   * * *
                * * *                 *                     * * * *
            * * * *                   *                       * * *
          * * *                       *                         * *
          * *                 * * *   *                         * *
          * *               * * * *   *                       * * *
          * *             * * * * *   *                     * * *
            * *         * *       * * *                     * *
              * * * * * *         * * *                   * * * *
                                                        * * *
                                                        * * *
                                                        * * *
                                                        * * * * *
                                                          * * * * * * * * *
                                                                          * * *
!STUFFY!FUNK!

# The program itself

echo "Installing warp.c (the biggie)"
cat >warp.c <<\!STUFFY!FUNK!
static char warpid[] = "@(#)$Header: /ea/lwall/src/warp/warp.c%v 6.0 83/08/08 17:09:26 lwall Exp $";

/*	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>
#include <sgtty.h>
#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;

/* 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)

/* 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))

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))

/*
 * 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
char *BC;		/* backspace character */
char *ND;		/* non-destructive cursor right */
char *DO;		/* move cursor down one line */
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() */
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];

char *index(), *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();
    ospeed = _tty.sg_ospeed;

    /* 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
    if (tmpaddr = index(s, '-'))
	s = tmpaddr+1;
    strncpy(realname, s, 24);
    realname[24] = '\0';
    if (s = index(realname, '('))
	*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 */
    sigignore(SIGTTOU);

    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;
    char velx, vely;
    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:
    while (count = input_pending()) {
	for (i=0; i<count; i++) {
	    if (read(0, &ch[i], 1) < 0)
		hangup_catcher();
	    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;
		case 's':
		    clear();
		    while (!input_pending())
			sleep(1);
		    rewrite();
		    roundsleep(3);
		    ctrla = FALSE;
		    goto top;
#ifdef SIGTSTP
		case 'z':
		    clear();
		    mytstp();
		    sleep(4);
		    ctrla = FALSE;
		    goto top;
#endif
		default:
		    break;
		}
		ctrla = FALSE;
	    }
	    else {
		switch (ch[i]) {
		case 'Z':
		    clear();
		    mytstp();
		    sleep(4);
		    goto top;
		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()
{
    if (input_pending())
	if (read(0, spbuf, sizeof(spbuf)) < 0)
	    hangup_catcher();
}
!STUFFY!FUNK!

echo -n "Would you like me to start the make? "
read ans
if test "$ans" = y
then
    echo "Make output will be found in file" `pwd`"/make.log, eventually."
    make 2>&1 make.log &
else
    echo "You will need to do a make in directory" `pwd`
fi
# end of warp distribution kit