[comp.sources.games] v01i073: xtrek - multiplayer space war game for X-windows, Part01/06

games-request@tekred.TEK.COM (07/02/87)

Submitted by: Chris Guthrie <chris%ic.Berkeley.EDU@ucbvax.berkeley.edu>
Comp.sources.games: Volume 1, Issue 73
Archive-name: xtrek/Part01

	[The following note is from the author.  (I sure wish I had
	 a system with X-windows to play all these neat games!)  -br]

	[[xtrek is a game based on the x windowing system.  Typically,
	 I've been distributing it through ftp, but I'm getting too
	 many requests from people out in uucp land, so I figure that
	 sources.games would be appropriate.  People will need to have
	 X and sysV shared memory to run the game.  We have it running
	 on Ultrix, Sun 3.2, and HP-UX 5.2 machines.]]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 6)."
# Contents:  README MANIFEST daemon.c rmove.c xtrek.6
# Wrapped by billr@tekred on Thu Jul  2 10:26:27 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(5259 characters\)
sed "s/^X//" >README <<'END_OF_README'
XThis explains quickly how to run xtrek on a ultrix,
XSun 3.20, or HP-UX 5.2 system.  Note that other
Xoperating systems will probably require fiddling.
XThere are notes for various operating systems below.
XThere is also a list of recent changes.
X
XIt is important that the system V shared memory
Xsegments are actually compiled into your kernel on
Xthe server machine.  The clients just need X.
X
XCreate an appropriate directory for it.  The defs.h
Xfile contains the paths to this directory.  If you
Xdon't want it in /usr/games/lib/xtrek, change this
Xfile.
X
XA "make install" will perform the following steps for you.
XIf you don't want xtrek and daemon to be suid to root
Xor you don't like the choice of directories, either
Xchange DESTDIR, XTREKDIR and XTREKOWNER in the Makefile
Xor perform these steps by hand:
X
X	Run make.  Install xtrek in /usr/games.  Robot and
X	daemon must be in /usr/games/lib/xtrek. Create the
X	files .motd, .scores, and .planets in
X	/usr/games/lib/xtrek.
X
X	Xtrek and the daemon must all be setuid
X	to someone who has write permission on these files.
X	Root is fine.  There are no real security holes in
X	the game.  If you want to avoid this, take out the
X	shared memory chmoding and make the dot files writable.
X
XIf you want to change constants in the game, it is
Xpretty well set up to allow tweaking.  Most of the
Xconstants are in defs.h.  Player constants were defined
Xin getship.c in plans for a future custom designed ship
Xoption.
X
Xnroff -me the document and put it somewhere useful.
X
XHP-UX notes:
X
XAlthought I have compiled and run this version of xtrek on
Xan HP-UX 5.2 system, there are some things to be aware of.
XFirst, my system was a 320.  If you have access to a 840 it
Xwould clearly be a better choice as a server.  It would also
Xprovide the wait3 function needed in some of the code.  I don't
Xknow how the wait's I've put into the code will work out.
X
XA 320 system, while being sufficient as a display for the game,
Xwould probably not work well as a server.
X
XSun notes:
X
XSome code was added to prevent people from holding down the
X't' key and generating fields of torps with the auto-repeat
Xmode.  This uses the timer for the xevents and it's not clear
Xthat the timer works everywhere.
X
XThere is one clear bug in the sun Xlib server that can make
Xtorpedoes occasionnally fail to fire.  If you want to fix this
Xbug, replace the line of code in libsun/events.c:
X
X   xe->vse_time = (se->ie_time.tv_usec/10000 + se->ie_time.tv_sec);
X
Xwith
X
X   xe->vse_time = (se->ie_time.tv_usec/10000 + se->ie_time.tv_sec*100);
X
XIt appears twice so get 'em both.  Note that this stuff is in the source
Xto libX.a for the suns.
X
XApollo Notes:
X
XWe don't have an apollo to test the code out, but the following lines
Xin the xlib code scare me:
X
X    /* this needs to be replaced with a "real" timestamp */
X    xe->vse_time += 1;
X
XI suspect that on an apollo, only one torp in ten will actually be fired.
XSee the Sun notes above for more information.
X
XUltrix notes:
X
XBefore running make, look for random.o.  I found
Xthat the ultrix library version wasn't very random,
Xso I substituted the 4.3 one which worked better.
XLicensing agreements being what they are, the
Xversion in this directory just links against
Xwhatever random.o is in libc.a. 
X
XIf you have the 4.3 random.o (or .c), just restore
Xthe commented-out reference to random.o in the Makefile
Xand it will link against your copy. If we ever find a
Xgood public domain rand() function, this problem will
Xgo away.
X
X
XChanges from version 3.0 to version 4.0:
X
XPlayers cannot jump teams once in.  They must quit and rejoin
Xto change teams.
X
XWhen a team loses its last planet, they cannot hold a coup for 30
Xminutes to an hour of play time.
X
XThere is an independent team that controls all planets with zero
Xarmies.
X
XFuel cannot go negative now.
X
XCertain deaths will force you to exit the game completely.
X
XSome colors have been changed.
X
XPlayers may not bomb their own planets.  (Their crews have wives and
Xchildren down there....)
X
XSince sys V compatibility seems to show up in most 4.3 based include
Xfiles, I've gone over to the sys V names of some things like
XSIGCHLD -> SIGCLD.
X
XFixed a bug with showstats that crashed in watch mode.
X
XThere have been a number of changes to 'improve' play.  Not
Xeveryone agrees that they are for the better.  Ships now
Xturn one warp faster.  Fuel is used by the engines -- you
Xdon't gain fuel at warp 5 anymore.
X
XEvents now go through info windows properly.
X
XThe special font effects occur in the info windows.
X
XThe space key will now also close all special windows.
X
XYou cannot repair and use transporters at the same time.
X
XThere are a number of optimizations.  Hardcoding the lengths
Xof planetnames reduced the number of calls to strlen.  The
Xstatus line is now drawn by hand to save calls to doprnt().
XAlso, the characters up on the map window for each ship are
Xstored in the ship's data structure to avoid more calls to
Xdoprnt().
X
XRobots are a little smarter and slightly more aggressive.  This
Xwas meant to stop people from darting in and out while taking
Xperiphery planets.
X
XOverheated weapons can't be wrapped around.
X
XPeople who hold down the 't' key to get a burst of torps are
Xin for a surprise.  Torps cannot be fired faster than one every
Xtenth of a second.
X
END_OF_README
if test 5259 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f MANIFEST -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"MANIFEST\"
else
echo shar: Extracting \"MANIFEST\" \(1374 characters\)
sed "s/^X//" >MANIFEST <<'END_OF_MANIFEST'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                  1	This shipping list
X Makefile                  4	
X README                    1	
X bitmaps.h                 2	
X clock.bitmap              5	
X colors.c                  5	
X coup.c                    5	
X daemon.c                  1	
X data.c                    5	
X data.h                    5	
X death.c                   5	
X defs.h                    5	
X detonate.c                3	
X dmessage.c                5	
X doc                       2	
X enter.c                   5	
X getship.c                 6	
X inform.c                  4	
X input.c                   3	
X interface.c               5	
X main.c                    4	
X newwin.c                  3	
X orbit.c                   5	
X phaser.c                  5	
X planetlist.c              5	
X planets.h                 5	
X playerlist.c              2	
X pstats.c                  4	
X redraw.c                  3	
X rmove.c                   1	
X robot.c                   5	
X scores.c                  5	
X sintab.c                  4	
X smessage.c                4	
X startdaemon.c             5	
X stats.c                   4	
X struct.h                  4	
X torp.c                    5	
X util.c                    5	
X war.c                     4	
X warning.c                 6	
X xtrek.6                   1	
END_OF_MANIFEST
if test 1374 -ne `wc -c <MANIFEST`; then
    echo shar: \"MANIFEST\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f daemon.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"daemon.c\"
else
echo shar: Extracting \"daemon.c\" \(29846 characters\)
sed "s/^X//" >daemon.c <<'END_OF_daemon.c'
X
X/*
X
X	Copyright (c) 1986 	Chris Guthrie
X
XPermission to use, copy, modify, and distribute this
Xsoftware and its documentation for any purpose and without
Xfee is hereby granted, provided that the above copyright
Xnotice appear in all copies and that both that copyright
Xnotice and this permission notice appear in supporting
Xdocumentation.  No representations are made about the
Xsuitability of this software for any purpose.  It is
Xprovided "as is" without express or implied warranty.
X
X*/
X
X#include <X/Xlib.h>
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/ipc.h>
X#include <sys/shm.h>
X#ifdef hpux
X#include <time.h>
X#else hpux
X#include <sys/time.h>
X#endif hpux
X#include <sys/file.h>
X#ifndef hpux
X#include <sys/wait.h>
X#endif hpux
X#include <sys/ioctl.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <math.h>
X#include "defs.h"
X#include "struct.h"
X#include "data.h"
X#include "planets.h"
X
X#ifdef hpux
X#define bcopy(from, to, length)		memcpy((to), (from), (length))
X#endif hpux
X
X#define fuse(X) ((ticks % (X)) == 0)
X/* Run the game */
X
Xlong random();
Xlong lseek();
Xstatic struct itimerval udt;
Xstatic int shmid;
X
Xstatic int debug = 0;
Xstatic int ticks = 0;
Xstatic int plfd;
X
Xjmp_buf env;
X
Xstatic int tcount[MAXTEAM + 1];
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X    register int i;
X    int	shmemKey = PKEY;
X    struct memory	*sharedMemory;
X    struct shmid_ds smbuf;
X    int x = 0;
X    int move();
X    int reaper();
X    int freemem();
X
X    if (argc > 1)
X	debug = 1;
X
X    srandom(getpid());
X    if (!debug) {
X	for (i = 0; i < NSIG; i++) {
X	    (void) signal(i, freemem);
X	}
X	detach();
X    }
X
X    /* Kill any existing segments */
X
X    if ((shmid = shmget(shmemKey, 0, 0)) >= 0) {
X	fprintf(stderr, "Killing existing segment\n");
X	shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0);
X    }
X
X    shmid = shmget(shmemKey, sizeof(struct memory), IPC_CREAT | 0777);
X    if (shmid < 0) {
X	perror("can't open shared memory");
X	exit (1);
X    }
X    /* Hose Ed's robots */
X    shmctl(shmid, IPC_STAT, &smbuf);
X    smbuf.shm_perm.uid = geteuid();
X    smbuf.shm_perm.mode = 0700;
X    shmctl(shmid, IPC_SET, &smbuf);
X
X    sharedMemory = (struct memory *) shmat(shmid, 0, 0);
X    if (sharedMemory == (struct memory *) -1) {
X	perror("shm attach");
X	exit (1);
X    }
X    players = sharedMemory->players;
X    torps = sharedMemory->torps;
X    status = sharedMemory->status;
X    planets = sharedMemory->planets;
X    phasers = sharedMemory->phasers;
X    mctl = sharedMemory->mctl;
X    messages = sharedMemory->messages;
X
X    for (i = 0; i < MAXPLAYER; i++)
X	players[i].p_status = PFREE;
X
X    plfd = open(PLFILE, O_RDWR, 0777);
X    if (plfd < 0) {
X	fprintf(stderr, "No planet file.  Restarting galaxy\n");
X	bcopy(pdata, planets, sizeof(pdata));
X	for (i = 0; i < 40; i++) {
X	    if (planets[i].pl_flags & PLHOME)
X		planets[i].pl_flags |= (PLREPAIR|PLFUEL);
X	    if (random() % 4 == 0)
X		planets[i].pl_flags |= PLREPAIR;
X	    if (random() % 2 == 0)
X		planets[i].pl_flags |= PLFUEL;
X	}
X    }
X    else {
X	if (read(plfd, (char *) planets, sizeof(pdata)) != sizeof(pdata)) {
X	    fprintf(stderr, "Planet file wrong size.  Restarting galaxy\n");
X	    bcopy(pdata, planets, sizeof(pdata));
X	    for (i = 0; i < 40; i++) {
X		if (planets[i].pl_flags & PLHOME)
X		    planets[i].pl_flags |= (PLREPAIR|PLFUEL);
X		if (random() % 4 == 0)
X		    planets[i].pl_flags |= PLREPAIR;
X		if (random() % 2 == 0)
X		    planets[i].pl_flags |= PLFUEL;
X	    }
X	}
X    }
X
X    status->active = 0;
X
X    (void) signal(SIGCLD, reaper);
X
X    (void) signal(SIGALRM, move);
X    udt.it_interval.tv_sec = 0;
X    udt.it_interval.tv_usec = UPDATE;
X    udt.it_value.tv_sec = 0;
X    udt.it_value.tv_usec = UPDATE;
X    (void) setitimer(ITIMER_REAL, &udt, (struct itimerval *) 0);
X
X    (void) setjmp(env);
X
X    while (1) {
X	pause();
X	if (debug) {
X	    if (!(++x % 50))
X		printf("Mark %d\n", x);
X	}
X    }
X}
X
Xdetach()
X{
X    int fd;
X
X#ifdef hpux
X    setpgrp();
X#else hpux
X    fd = open("/dev/tty", O_RDWR, 0);
X    if (fd < 0)
X	return;
X    (void) ioctl(fd, TIOCNOTTY, (char *) NULL);
X    (void) close(fd);
X#endif hpux
X}
X
X/* These specify how often special actions will take place in
X   UPDATE units (0.10 seconds, currently.)
X*/
X
X#define PLAYERFUSE	1
X#define TORPFUSE	1
X#define PHASERFUSE	1
X#define TEAMFUSE	5
X#define PLFIGHTFUSE	5
X#define BEAMFUSE	10
X#define SYNCFUSE	3000
X#define PLANETFUSE	600
X
X#define GHOSTTIME	(10 * 1000000 / UPDATE)	/* 10 secs */
X#define OUTFITTIME	(2 * AUTOQUIT * 1000000 / UPDATE) /* 2 * AQ secs */
X
Xnplayers = 0;
Xdietime = -1;
X
Xmove()
X{
X    if (++ticks == dietime)	/* no player for 1 minute. kill self */
X	freemem();
X    if (fuse(PLAYERFUSE)) {
X	udplayers();
X    }
X    if (fuse(TORPFUSE)) {
X	udtorps();
X    }
X    if (fuse(PHASERFUSE)) {
X	udphaser();
X    }
X    if (fuse(TEAMFUSE)) {
X	teamtimers();
X    }
X    if (fuse(PLFIGHTFUSE)) {
X	plfight();
X    }
X    if (fuse(BEAMFUSE)) {
X	beam();
X    }
X    if (fuse(SYNCFUSE)) {
X	save_planets();
X    }
X    if (fuse(PLANETFUSE)) {
X	udplanets();
X    }
X}
X
Xudplayers()
X{
X    register int i, k;
X    register struct player *j;
X    int maxspeed;
X
X    nplayers = 0;
X    tcount[FED] = tcount[ROM] = tcount[KLI] = tcount[ORI] = 0;
X    for (i = status->active = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
X	switch (j->p_status) {
X	    case POUTFIT:
X		if (++(j->p_ghostbuster) > OUTFITTIME)
X		    j->p_status = PFREE;
X		continue;
X	    case PFREE:
X		nplayers++;
X		j->p_ghostbuster = 0;	/* stop from hosing new players */
X		continue;
X	    case PDEAD:
X		if (--j->p_explode <= 0) { 	/* Ghost Buster */
X		    j->p_status = PFREE;
X		}
X		continue;
X	    case PEXPLODE:
X		j->p_updates++;
X		j->p_flags &= ~PFCLOAK;
X		if (j->p_explode == (10/PLAYERFUSE))
X		    blowup(j);		/* damage everyone else around */
X		if (--j->p_explode <= 0) {
X		    j->p_status = PDEAD;
X		    j->p_explode = 600/PLAYERFUSE; /* set ghost buster */
X		}
X		break;
X	    case PALIVE:
X		if (++(j->p_ghostbuster) > GHOSTTIME) {
X		    j->p_status = PEXPLODE;
X		    j->p_explode = 10/PLAYERFUSE;
X		    ghostmess(j);
X		    /* j->p_stats.st_losses++; */
X		    j->p_whydead = KGHOST;
X		    j->p_whodead = i;
X		}
X			
X		status->active += (1<<i);
X		tcount[j->p_team]++;
X		j->p_updates++;
X
X		/* cool weapons */
X		j->p_wtemp -= 2;
X		if (j->p_wtemp < 0)
X		    j->p_wtemp = 0;
X		if (j->p_flags & PFWEP) {
X		    if (--j->p_wtime <= 0)
X			j->p_flags &= ~PFWEP;
X		}
X		else if (j->p_wtemp > 1000) {
X		    if (!(random() % 40)) {
X			j->p_flags |= PFWEP;
X			j->p_wtime = ((random() % 150) + 100) / PLAYERFUSE;
X		    }
X		}
X		/* cool engine */
X		j->p_etemp -= 5;
X		if (j->p_etemp < 0)
X		    j->p_etemp = 0;
X		if (j->p_flags & PFENG) {
X		    if (--j->p_etime <= 0)
X			j->p_flags &= ~PFENG;
X		}
X		else if (j->p_etemp > 1000) {
X		    if (!(random() % 40)) {
X			j->p_flags |= PFENG;
X			j->p_etime = ((random() % 150) + 100) / PLAYERFUSE;
X			j->p_desspeed = 0;
X		    }
X		}
X
X		/* Add fuel */
X		if ((j->p_flags & PFORBIT) &&
X		    (planets[j->p_planet].pl_flags & PLFUEL) &&
X		    (!(planets[j->p_planet].pl_owner 
X			& (j->p_swar | j->p_hostile)))) {
X			    j->p_fuel += 8 * j->p_ship.s_recharge;
X		}
X		else
X		    j->p_fuel += 2 * j->p_ship.s_recharge;
X
X		if (j->p_fuel > j->p_ship.s_maxfuel)
X		    j->p_fuel = j->p_ship.s_maxfuel;
X		if (j->p_fuel < 0) {
X		    j->p_desspeed = 0;
X		    j->p_flags &= ~PFCLOAK;
X		}
X
X		/* repair shields */
X		if (j->p_shield < 100) {
X		    if ((j->p_flags & PFREPAIR) && (j->p_speed == 0)) {
X			j->p_subshield += j->p_ship.s_repair * 4;
X			if ((j->p_flags & PFORBIT) &&
X			    (planets[j->p_planet].pl_flags & PLREPAIR) &&
X			    (!(planets[j->p_planet].pl_owner 
X				& (j->p_swar | j->p_hostile)))) {
X				    j->p_subshield += j->p_ship.s_repair * 4;
X			}
X		    }
X		    else
X			j->p_subshield += j->p_ship.s_repair * 2;
X		    if (j->p_subshield / 1000) {
X			j->p_shield += j->p_subshield / 1000;
X			j->p_subshield %= 1000;
X		    }
X		    if (j->p_shield > 100) {
X			j->p_shield = 100;
X			j->p_subshield = 0;
X		    }
X		}
X
X		/* repair damage */
X		if (j->p_damage && !(j->p_flags & PFSHIELD)) {
X		    if ((j->p_flags & PFREPAIR) && (j->p_speed == 0)) {
X			j->p_subdamage += j->p_ship.s_repair * 2;
X			if ((j->p_flags & PFORBIT) &&
X			    (planets[j->p_planet].pl_flags & PLREPAIR) &&
X			    (!(planets[j->p_planet].pl_owner 
X				& (j->p_swar | j->p_hostile)))) {
X				    j->p_subdamage += j->p_ship.s_repair * 2;
X			}
X		    }
X		    else
X			j->p_subdamage += j->p_ship.s_repair;
X		    if (j->p_subdamage / 1000) {
X			j->p_damage -= j->p_subdamage / 1000;
X			j->p_subdamage %= 1000;
X		    }
X		    if (j->p_damage < 0) {
X			j->p_damage = 0;
X			j->p_subdamage = 0;
X		    }
X		}
X
X		/* Charge for cloaking */
X		if (j->p_flags & PFCLOAK) {
X		    if (j->p_fuel < j->p_ship.s_cloakcost) {
X			j->p_flags &= ~PFCLOAK;
X		    }
X		    else {
X			j->p_fuel -= j->p_ship.s_cloakcost;
X		    }
X		}
X
X		/* Move Player in orbit */
X		if (j->p_flags & PFORBIT) {
X		    j->p_dir += 2;
X		    j->p_desdir = j->p_dir;
X		    j->p_x = planets[j->p_planet].pl_x + ORBDIST
X			* Cos[(unsigned char) (j->p_dir - (unsigned char) 64)];
X		    j->p_y = planets[j->p_planet].pl_y + ORBDIST
X			* Sin[(unsigned char) (j->p_dir - (unsigned char) 64)];
X		}
X
X		/* Move player through space */
X		else {
X
X		    if (j->p_dir != j->p_desdir)
X			changedir(j);
X
X		    /* Alter speed */
X		    maxspeed = 10 - j->p_damage/10;
X		    if (j->p_desspeed > maxspeed)
X			j->p_desspeed = maxspeed;
X		    if (j->p_flags & PFENG)
X			j->p_desspeed = 0;
X
X		    if (j->p_desspeed > j->p_speed) {
X			j->p_subspeed += j->p_ship.s_accint;
X		    }
X		    if (j->p_desspeed < j->p_speed) {
X			j->p_subspeed -= j->p_ship.s_decint;
X		    }
X		    if (j->p_subspeed / 1000) {
X			j->p_speed += j->p_subspeed / 1000;
X			j->p_subspeed /= 1000;
X			if (j->p_speed < 0)
X			    j->p_speed = 0;
X			if (j->p_speed > j->p_ship.s_maxspeed)
X			    j->p_speed = j->p_ship.s_maxspeed;
X		    }
X		    /* Charge for speed */
X
X		    if (j->p_fuel < (j->p_ship.s_warpcost * j->p_speed)) {
X			j->p_desspeed = 0;
X		    }
X		    else {
X			j->p_fuel -= j->p_ship.s_warpcost * j->p_speed;
X			j->p_etemp += j->p_speed;
X		    }
X
X		    j->p_x += (double) j->p_speed * Cos[j->p_dir] * WARP1;
X		    j->p_y += (double) j->p_speed * Sin[j->p_dir] * WARP1;
X
X		    /* Bounce off the side of the galaxy */
X		    if (j->p_x < 0) {
X			j->p_x = -j->p_x;
X			if (j->p_dir == 192) 
X			    j->p_dir = j->p_desdir = 64;
X			else 
X			    j->p_dir = j->p_desdir = 64 - (j->p_dir - 192);
X		    }
X		    else if (j->p_x > GWIDTH) {
X			j->p_x = GWIDTH - (j->p_x - GWIDTH);
X			if (j->p_dir == 64) 
X			    j->p_dir = j->p_desdir = 192;
X			else 
X			    j->p_dir = j->p_desdir = 192 - (j->p_dir - 64);
X		    }
X		    if (j->p_y < 0) {
X			j->p_y = -j->p_y;
X			if (j->p_dir == 0) 
X			    j->p_dir = j->p_desdir = 128;
X			else 
X			    j->p_dir = j->p_desdir = 128 - j->p_dir;
X		    }
X		    else if (j->p_y > GWIDTH) {
X			j->p_y = GWIDTH - (j->p_y - GWIDTH);
X			if (j->p_dir == 128) 
X			    j->p_dir = j->p_desdir = 0;
X			else 
X			    j->p_dir = j->p_desdir = 0 - (j->p_dir - 128);
X		    }
X		}
X
X		/* Set player's alert status */
X#define YRANGE ((GWIDTH)/5)
X#define RRANGE ((GWIDTH)/10)
X		j->p_flags |= PFGREEN;
X		j->p_flags &= ~(PFRED|PFYELLOW);
X		for (k = 0; k < MAXPLAYER; k++) {
X		    int dx, dy, dist;
X		    if ((players[k].p_status != PALIVE) ||
X			((!((j->p_swar | j->p_hostile) & players[k].p_team)) &&
X			(!((players[k].p_swar | players[k].p_hostile) & 
X			j->p_team)))) {
X			    continue;
X		    }
X		    else if (j == &players[k]) {
X			continue;
X		    }
X		    else {
X			dx = j->p_x - players[k].p_x;
X			dy = j->p_y - players[k].p_y;
X			if (ABS(dx) > YRANGE || ABS(dy) > YRANGE)
X			    continue;
X			dist = dx * dx + dy * dy;
X			if (dist <  RRANGE * RRANGE) {
X			    j->p_flags |= PFRED;
X			    j->p_flags &= ~(PFGREEN|PFYELLOW);
X			}
X			else if ((dist <  YRANGE * YRANGE) &&
X			    (!(j->p_flags & PFRED))) {
X			    j->p_flags |= PFYELLOW;
X			    j->p_flags &= ~(PFGREEN|PFRED);
X			}
X		    }
X		}
X	    break;
X	} /* end switch */
X    }
X    if (nplayers == MAXPLAYER) {
X	if (dietime == -1)
X	    dietime = ticks + 600 / PLAYERFUSE;
X    }
X    else {
X	dietime = -1;
X    }
X}
Xchangedir(sp)
Xstruct player *sp;
X{
X    unsigned int ticks;
X
X    if (sp->p_speed == 0) {
X	sp->p_dir = sp->p_desdir;
X	sp->p_subdir = 0;
X    }
X    else {
X	sp->p_subdir += sp->p_ship.s_turns / (1 << sp->p_speed);
X	ticks = sp->p_subdir / 1000;
X	if (ticks) {
X	    if (ticks > ABS(sp->p_dir - sp->p_desdir))
X		sp->p_dir = sp->p_desdir;
X	    else if ((unsigned char) (sp->p_dir - sp->p_desdir) > 127)
X		sp->p_dir += ticks;
X	    else 
X		sp->p_dir -= ticks;
X	    sp->p_subdir %= 1000;
X	}
X    }
X}
X
Xudtorps()
X{
X    register int i;
X    register struct torp *j;
X
X    for (i = 0, j = &torps[i]; i < MAXPLAYER * MAXTORP; i++, j++) {
X	switch (j->t_status) {
X	    case TFREE:
X		continue;
X	    case TMOVE:
X	    case TSTRAIGHT:
X		j->t_x += (double) j->t_speed * Cos[j->t_dir] * WARP1;
X		if (j->t_x < 0) {
X		    j->t_x = 0;
X		    explode(j);
X		    break;
X		}
X		else if (j->t_x > GWIDTH) {
X		    j->t_x = GWIDTH;
X		    explode(j);
X		    break;
X		}
X		j->t_y += (double) j->t_speed * Sin[j->t_dir] * WARP1;
X		if (j->t_y < 0) {
X		    j->t_y = 0;
X		    explode(j);
X		    break;
X		}
X		else if (j->t_y > GWIDTH) {
X		    j->t_y = GWIDTH;
X		    explode(j);
X		    break;
X		}
X
X		/* Make sure that player torps wobble */
X		if (j->t_status == TMOVE)
X		    j->t_dir += (random() % 3) - 1;
X
X		if (j->t_fuse-- <= 0) {
X		    j->t_status = TFREE;
X		    players[j->t_owner].p_ntorp--;
X		}
X		else if (near(j)) {
X		    explode(j);
X		}
X		break;
X	    case TDET:
X		j->t_x += (double) j->t_speed * Cos[j->t_dir] * WARP1;
X		if (j->t_x < 0)
X		    j->t_x += GWIDTH;
X		else if (j->t_x > GWIDTH)
X		    j->t_x -= GWIDTH;
X		j->t_y += (double) j->t_speed * Sin[j->t_dir] * WARP1;
X		if (j->t_y < 0)
X		    j->t_y += GWIDTH;
X		else if (j->t_y > GWIDTH)
X		    j->t_y -= GWIDTH;
X		explode(j);
X		break;
X	    case TEXPLODE:
X		if (j->t_fuse-- <= 0) {
X		    j->t_status = TFREE;
X		    players[j->t_owner].p_ntorp--;
X		}
X		break;
X	    case TOFF:
X		j->t_status = TFREE;
X		players[j->t_owner].p_ntorp--;
X		break;
X	}
X    }
X}
X
X/* See if there is someone close enough to explode for */
Xnear(torp)
Xstruct torp *torp;
X{
X    register int i;
X    int dx, dy;
X    register struct player *j;
X
X    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
X	if (!(j->p_status == PALIVE))
X	    continue;
X	if (torp->t_owner == j->p_no)
X	    continue;
X	/* This is tricky.  If you aren't at war with the team,
X	   and that team is not at war with your team, continue.
X	*/
X	if ((!(torp->t_war & j->p_team)) &&
X	    (!(torp->t_team & (j->p_swar | j->p_hostile))))
X	        continue;
X	dx = torp->t_x - j->p_x;
X	dy = torp->t_y - j->p_y;
X	if (ABS(dx) > EXPDIST || ABS(dy) > EXPDIST)
X	    continue;
X	if (dx * dx + dy * dy < EXPDIST * EXPDIST)
X	    return 1;
X    }
X    return 0;
X}
X
X    
X
X/* Do damage to all surrounding players */
X
Xexplode(torp)
Xstruct torp *torp;
X{
X    register int i;
X    int dx, dy, dist;
X    int damage;
X    register struct player *j;
X
X    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
X	if (!(j->p_status == PALIVE))
X	    continue;
X	if (torp->t_owner == j->p_no)	/* This isn't realistic */
X	    continue;
X	dx = torp->t_x - j->p_x;
X	dy = torp->t_y - j->p_y;
X	if (ABS(dx) > DETDIST || ABS(dy) > DETDIST) /*XXX*/
X	    continue;
X	dist = dx * dx + dy * dy;
X	if (dist > DETDIST * DETDIST)
X	    continue;
X	if (dist > EXPDIST * EXPDIST) {
X	    damage = torp->t_damage * (DETDIST - sqrt((double) dist)) /
X		(DETDIST - EXPDIST);
X	}
X	else {
X	    damage = torp->t_damage;
X	}
X	if (damage > 0) {
X	    /* First, check to see if torp owner has started a war */
X	    if (players[torp->t_owner].p_hostile & j->p_team) {
X		players[torp->t_owner].p_swar |= j->p_team;
X	    }
X	    /* Note that if a player is at peace with the victim, then
X	       the torp has caused damage either accidently, or because
X	       the victim was at war with, or hostile to, the player.
X	       In either case, we don't consider the damage to be
X	       an act of war */
X
X	    if (j->p_flags & PFSHIELD) {
X		j->p_shield -= damage;
X		if (j->p_shield < 0) {
X		    j->p_damage -= j->p_shield;
X		    j->p_shield = 0;
X		}
X	    }
X	    else {
X		j->p_damage += damage;
X	    }
X	    if (j->p_damage >= 100) {
X		j->p_status = PEXPLODE;
X		j->p_explode = 10/PLAYERFUSE;
X		if (!(j->p_flags & PFPRACTR)) {
X		    players[torp->t_owner].p_kills += 1.0 + 
X			j->p_armies * 0.1 + j->p_kills * 0.1;
X		}
X		killmess(j, &players[torp->t_owner]);
X		j->p_stats.st_losses++;
X		j->p_whydead = KTORP;
X		j->p_whodead = torp->t_owner;
X	    }
X	}
X    }
X    torp->t_status = TEXPLODE;
X    torp->t_fuse = 10/TORPFUSE;
X}
X
Xudplanets()
X{
X    register int i;
X    register struct planet *l;
X
X    for (i = 0, l = &planets[i]; i < MAXPLANETS; i++, l++) {
X	if (l->pl_couptime)	/* Decrement coup counter one minute */
X	    l->pl_couptime--;
X	if (l->pl_armies == 0)
X	    continue;
X	if ((random() % 3000) < l->pl_armies)
X	    l->pl_armies -= (random() % l->pl_armies);
X	if ((l->pl_armies < 4) && ((random() % 20) == 0)) {
X	    l->pl_armies++;
X	    continue;
X	}
X	if ((random() % 10) == 0)
X	    l->pl_armies += (random() % 3) + 1;
X	    
X    }
X}
X
Xudphaser()
X{
X    register int i;
X    register struct phaser *j;
X    register struct player *victim;
X
X    for (i = 0, j = &phasers[i]; i < MAXPLAYER; i++, j++) {
X	switch (j->ph_status) {
X	    case PHFREE:
X		continue;
X	    case PHMISS:
X		if (j->ph_fuse-- == 1)
X		    j->ph_status = PHFREE;
X		break;
X	    case PHHIT:
X		if (j->ph_fuse-- == 10) {
X		    victim = &players[j->ph_target];
X
X		    /* start a war, if necessary */
X		    if (players[i].p_hostile & victim->p_team) {
X			players[i].p_swar |= victim->p_team;
X		    }
X
X		    if (victim->p_flags & PFSHIELD) {
X			victim->p_shield -= j->ph_damage;
X			if (victim->p_shield < 0) {
X			    victim->p_damage -= victim->p_shield;
X			    victim->p_shield = 0;
X			}
X		    }
X		    else {
X			victim->p_damage += j->ph_damage;
X		    }
X		    if (victim->p_damage >= 100) {
X			victim->p_status = PEXPLODE;
X			victim->p_explode = 10/PLAYERFUSE;
X			if (!(victim->p_flags & PFPRACTR)) {
X			    players[i].p_kills += 1.0 + 
X				victim->p_armies * 0.1 +
X				victim->p_kills * 0.1;
X			}
X			killmess(victim, &players[i]);
X			victim->p_stats.st_losses++;
X			victim->p_whydead = KPHASER;
X			victim->p_whodead = i;
X		    }
X		}
X		if (j->ph_fuse == 0)
X		    j->ph_status = PHFREE;
X		break;
X	}
X    }
X}
X
Xint pl_warning[MAXPLANETS];	/* To keep planets shut up for awhile */
Xint tm_robots[MAXTEAM + 1];		/* To limit the number of robots */
Xint tm_coup[MAXTEAM + 1];		/* To allow a coup */
X
Xteamtimers()
X{
X    register int i;
X    for (i = 0; i <= MAXTEAM; i++) {
X	if (tm_robots[i] > 0)
X	    tm_robots[i]--;
X	if (tm_coup[i] > 0)
X	    tm_coup[i]--;
X    }
X}
X
Xplfight()
X{
X    register int h, i;
X    register struct player *j;
X    register struct planet *l;
X    int dx, dy;
X    int damage;
X    int dist;
X    int rnd;
X    char buf[80];
X    char buf1[80];
X
X    for (h = 0, l = &planets[h]; h < MAXPLANETS; h++, l++) {
X	if (l->pl_flags & PLCOUP) {
X	    l->pl_flags &= ~PLCOUP;
X	    l->pl_owner = (l->pl_flags & ALLTEAM);
X	    l->pl_armies = 4;
X	}
X	l->pl_flags &= ~PLREDRAW;
X	if (pl_warning[h] > 0)
X	    pl_warning[h]--;
X    }
X    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
X	if (j->p_status == PALIVE) {
X
X	    /* Have planets do damage to nearby players.  Note that
X	       this is the most inefficient code in the game.  If
X	       one needs cycles, simply change the code to have
X	       planets only attack ships that are in orbit.   Thus
X	       you get rid of the following for statement 
X	    */
X	    for (h = 0, l = &planets[h]; h < MAXPLANETS; h++, l++) {
X		dx = ABS(l->pl_x - j->p_x);
X		dy = ABS(l->pl_y - j->p_y);
X		if (dx < 3 * PFIREDIST && dy < 3 * PFIREDIST)
X		    l->pl_flags |= PLREDRAW;
X		if (dx > PFIREDIST || dy > PFIREDIST)	/*XXX*/
X		    continue;
X		dist = (int) hypot((double) dx, (double) dy);
X		if (dist > PFIREDIST)
X		    continue;
X		if ((j->p_swar | j->p_hostile) & l->pl_owner) {
X		    if (l->pl_armies > 0)
X			damage = l->pl_armies / 10 + 2;
X		    else
X			damage = 0;
X		    if (damage > 0) {
X			if (j->p_flags & PFSHIELD) {
X			    j->p_shield -= damage;
X			    if (j->p_shield < 0) {
X				j->p_damage -= j->p_shield;
X				j->p_shield = 0;
X			    }
X			}
X			else {
X			    j->p_damage += damage;
X			}
X			if (j->p_damage >= 100) {
X			j->p_explode = 10/PLAYERFUSE;
X			j->p_status = PEXPLODE;
X			j->p_stats.st_losses++;
X			(void) sprintf(buf, "%s (%c%x) killed by %s (%c)",
X			    j->p_name,
X			    teamlet[j->p_team],
X			    j->p_no,
X			    l->pl_name,
X			    teamlet[l->pl_owner]);
X			pmessage(buf, 0, MALL, "GOD->ALL");
X			j->p_whydead = KPLANET;
X			j->p_whodead = h;
X			}
X		    }
X		}
X	    }	/* End planet damage */
X
X	    /* do bombing */
X	    if ((!(j->p_flags & PFORBIT)) || (!(j->p_flags & PFBOMB)))
X		continue;
X	    l = &planets[j->p_planet];
X	    if (j->p_team == l->pl_owner)
X		continue;
X	    if (!((j->p_swar | j->p_hostile) & l->pl_owner))
X		continue;
X	    if (l->pl_armies < 5)
X		continue;
X
X	    /* Warn owning team */
X	    if (pl_warning[h] <= 0) {
X		pl_warning[h] = 50/PLFIGHTFUSE; 
X		(void) sprintf(buf, "We are being attacked by %s %c%x.",
X			j->p_name,
X			teamlet[j->p_team],
X			j->p_no);
X		(void) sprintf(buf1, "%-3s->%-3s",
X		    l->pl_name, teamshort[l->pl_owner]);
X		pmessage(buf, l->pl_owner, MTEAM, buf1);
X	    }
X
X	    /* start the war (if necessary) */
X	    j->p_swar |= l->pl_owner;
X
X	    rnd = random() % 100;
X	    if (rnd < 50) {
X		continue;
X	    }
X	    else if (rnd < 80) {
X		l->pl_armies -= 1;
X		j->p_kills += 0.02;
X		j->p_stats.st_armsbomb++;
X	    }
X	    else if (rnd < 90) {
X		l->pl_armies -= 2;
X		j->p_kills += 0.04;
X		j->p_stats.st_armsbomb += 2;
X	    }
X	    else  {
X		l->pl_armies -= 3;
X		j->p_kills += 0.06;
X		j->p_stats.st_armsbomb += 3;
X	    }
X
X	    /* Send in a robot if there are no other defenders 
X		and the planet is in the team's home space */
X
X	    if ((tcount[l->pl_owner] == 0) && 
X		(l->pl_flags & l->pl_owner) &&
X		    tm_robots[l->pl_owner] == 0) {
X			rescue(l->pl_owner, j->p_kills);
X			tm_robots[l->pl_owner] = (1800 + 
X			    (random() % 1800)) /
X			    TEAMFUSE;
X	    }
X	}
X    }
X}
X
Xbeam()
X{
X    register int i;
X    register struct player *j;
X    register struct planet *l;
X    char buf[80];
X    char buf1[80];
X
X    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
X	if ((j->p_status != PALIVE) || (!(j->p_flags & PFORBIT)))
X	    continue;
X	l = &planets[j->p_planet];
X	if (j->p_flags & PFBEAMUP) {
X	    if (l->pl_armies < 5)
X		continue;
X	    if (j->p_armies == j->p_ship.s_maxarmies)
X		continue;
X	    /* XXX */
X	    if (j->p_armies == floor(j->p_kills * 2.0))
X		continue;
X	    if (j->p_team != l->pl_owner)
X		continue;
X	    if ((j->p_swar | j->p_hostile) & l->pl_owner)
X		continue;
X	    j->p_armies++;
X	    l->pl_armies--;
X	    continue;
X	}
X	if (j->p_flags & PFBEAMDOWN) {
X	    if (j->p_armies == 0)
X		continue;
X	    if ((!((j->p_swar | j->p_hostile) & l->pl_owner))
X		 && (j->p_team != l->pl_owner) && (l->pl_owner != NOBODY))
X		continue;
X	    if (j->p_team != l->pl_owner) {
X		j->p_armies--;
X		if (l->pl_armies) {
X		    int oldowner = l->pl_owner;
X
X		    /* start the war (if necessary) */
X		    j->p_swar |= l->pl_owner;
X
X		    l->pl_armies--;
X		    j->p_kills += 0.02;
X		    j->p_stats.st_armsbomb++;
X		    if (l->pl_armies == 0) {
X			/* Give planet to nobody */
X			(void) sprintf(buf, "%s destroyed by %s (%c%x)",
X			    l->pl_name,
X			    j->p_name,
X			    teamlet[j->p_team],
X			    j->p_no);
X			(void) sprintf(buf1, "%-3s->%-3s",
X			    l->pl_name, teamshort[l->pl_owner]);
X			pmessage(buf, l->pl_owner, MTEAM, buf1);
X			l->pl_owner = NOBODY;
X			checkgen(oldowner, j);
X		    }
X		}
X		else { 	/* planet taken over */
X		    l->pl_armies++;
X		    j->p_stats.st_planets++;
X		    j->p_kills += 0.25;
X		    (void) sprintf(buf, "%s taken over by %s (%c%x)",
X			l->pl_name,
X			j->p_name,
X			teamlet[j->p_team],
X			j->p_no);
X		    l->pl_owner = j->p_team;
X		    l->pl_info = j->p_team;
X		    checkwin(j);
X		    /* now tell new team */
X		    (void) sprintf(buf1, "%-3s->%-3s",
X			l->pl_name, teamshort[l->pl_owner]);
X		    pmessage(buf, l->pl_owner, MTEAM, buf1);
X		}
X	    }
X	    else {
X		j->p_armies--;
X		l->pl_armies++;
X	    }
X	}
X
X    }
X}
X
Xblowup(sh)
Xstruct player *sh;
X{
X    register int i;
X    int dx, dy, dist;
X    int damage;
X    register struct player *j;
X
X    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
X	if (j->p_status != PALIVE)
X	    continue;
X	if (sh == j)
X	    continue;
X	/* the following keeps people from blowing up on others */
X	me = sh;
X	if ((me->p_whydead == KQUIT) && (friendlyPlayer(j)))
X	    continue;
X	dx = sh->p_x - j->p_x;
X	dy = sh->p_y - j->p_y;
X	if (ABS(dx) > DETDIST || ABS(dy) > DETDIST)
X	    continue;
X	dist = dx * dx + dy * dy;
X	if (dist > DETDIST * DETDIST)
X	    continue;
X	if (dist > EXPDIST * EXPDIST) {
X	    damage = 100 * (DETDIST - sqrt((double) dist)) /
X		(DETDIST - EXPDIST);
X	}
X	else {
X	    damage = 100;
X	}
X	if (damage > 0) {
X	    if (j->p_flags & PFSHIELD) {
X		j->p_shield -= damage;
X		if (j->p_shield < 0) {
X		    j->p_damage -= j->p_shield;
X		    j->p_shield = 0;
X		}
X	    }
X	    else {
X		j->p_damage += damage;
X	    }
X	    if (j->p_damage >= 100) {
X		j->p_status = PEXPLODE;
X		j->p_explode = 10;
X		if (!(j->p_flags & PFPRACTR)) {
X		    sh->p_kills += 1.0 + 
X			j->p_armies * 0.1 + j->p_kills * 0.1;
X		}
X		killmess(j, sh);
X		j->p_stats.st_losses++;
X		j->p_whydead = KSHIP;
X		j->p_whodead = sh->p_no;
X	    }
X	}
X    }
X}
X
Xfreemem()
X{
X    register int i;
X    register struct player *j;
X
X    /* Blow players out of the game */
X    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
X	j->p_status = PDEAD;
X	j->p_whydead = KDAEMON;
X	j->p_ntorp = 0;
X    }
X    save_planets();
X    sleep(2);
X    shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0);
X    exit(0);
X}
X
Xsave_planets()
X{
X    if (plfd >= 0) {
X	(void) lseek(plfd, (long) 0, 0);
X	(void) write(plfd, (char *) planets, sizeof(pdata));
X    }
X}
X/* This function checks to see if a team has been genocided --
X   their last planet has been beamed down to zero.  It will set
X   a timer on their home planet that will prevent them from
X   couping for a random number of minutes.
X*/
Xcheckgen(loser, winner)
Xint loser;
Xstruct player *winner;
X{
X    register int i;
X    register struct planet *l;
X    struct planet *homep;
X
X    for (i = 0, l = &planets[i]; i < MAXPLANETS; i++, l++) {
X	if (l->pl_owner == loser)
X	    return;
X	if (((l->pl_flags & ALLTEAM) == loser) && (l->pl_flags & PLHOME))
X	    homep = l;		/* Keep track of his home planet for marking */
X    }
X
X    /* well, I guess they are losers.  Hose them now */
X    winner->p_stats.st_genocides++;
X    homep->pl_couptime = (18000 + (random() % 18000)) / PLANETFUSE;
X}
X
X
X/* This function is called when a planet has been taken over.
X   It checks all the planets to see if the victory conditions
X   are right.  If so, it blows everyone out of the game and
X   resets the galaxy
X*/
Xcheckwin(winner)
Xstruct player *winner;
X{
X    register int i, h;
X    register struct planet *l;
X    register struct player *j;
X    int team[MAXTEAM + 1];
X
X    for (i = 0; i < 4; i++)
X	team[1<<i] = 0;
X    
X    for (i = 0, l = &planets[i]; i < MAXPLANETS; i++, l++)
X	team[l->pl_owner]++;
X
X    for (i = 0; i < 4; i++) {
X	if (team[1<<i] >= VICTORY) {
X	    /* We have a winning team */
X	    for (h = 0, j = &players[0]; h < MAXPLAYER; h++, j++) {
X		j->p_status = PDEAD;
X		j->p_whydead = KWINNER;
X		j->p_whodead = winner->p_no;
X		j->p_ntorp = 0;
X	    }
X	    winner->p_stats.st_conqs++;
X	    bcopy(pdata, planets, sizeof(pdata));
X	    for (i = 0; i < 40; i++) {
X		if (random() % 4 == 0)
X		    planets[i].pl_flags |= PLREPAIR;
X		if (random() % 2 == 0)
X		    planets[i].pl_flags |= PLFUEL;
X	    }
X	    longjmp(env, 0);
X	}
X    }
X}
X
Xpmessage(str, recip, group, addr)
Xchar *str;
Xint recip;
Xint group;
Xchar *addr;
X{
X    struct message *cur;
X
X    if (++(mctl->mc_current) >= MAXMESSAGE)
X	mctl->mc_current = 0;
X    cur = &messages[mctl->mc_current];
X    cur->m_no = mctl->mc_current;
X    cur->m_flags = group;
X    cur->m_time = ticks;
X    cur->m_recpt = recip;
X    (void) sprintf(cur->m_data, "%s %s", addr, str);
X    cur->m_flags |= MVALID;
X}
X
Xghostmess(victim)
X	struct player	*victim;
X{
X    char 		buf[80];
X    static float	ghostkills = 0.0;
X
X    ghostkills += 1.0 + victim->p_armies * 0.1 + victim->p_kills * 0.1;
X    (void) sprintf(buf, "%s (%c%x) was kill %0.2f for the GhostBusters",
X	victim->p_name, teamlet[victim->p_team], victim->p_no, ghostkills);
X    pmessage(buf, 0, MALL, "GOD->ALL");
X}
X
Xkillmess(victim, killer)
Xstruct player *victim, *killer;
X{
X    char buf[80];
X
X    (void) sprintf(buf, "%s (%c%x) was kill %0.2f for %s (%c%x)",
X	victim->p_name,
X	teamlet[victim->p_team],
X	victim->p_no,
X	killer->p_kills,
X	killer->p_name,
X	teamlet[killer->p_team],
X	killer->p_no);
X    pmessage(buf, 0, MALL, "GOD->ALL");
X}
X
X/* Send in a robot to avenge the aggrieved team */
Xrescue(team, kills)
Xint team;
Xfloat kills;
X{
X    char *arg1;
X    char *arg2 = "-lX";
X
X    if (fork() == 0) {
X	(void) close(0);
X	(void) close(1);
X	(void) close(2);
X	(void) signal(SIGALRM, SIG_DFL);
X	switch (team) {
X	    case FED:
X		arg1 = "-Tf";
X		break;
X	    case ROM:
X		arg1 = "-Tr";
X		break;
X	    case KLI:
X		arg1 = "-Tk";
X		break;
X	    case ORI:
X		arg1 = "-To";
X		break;
X	}
X	if (kills > 9.0)
X	    kills = 9.0;
X	(void) sprintf(arg2, "-l%d", (int) kills);
X	execl(ROBOT, "robot", arg1, arg2, 0);
X	/* If we get here, we are hosed anyway */
X    }
X}
X
X#include <sys/resource.h>
X
X/* Don't fear the ... */
X
Xreaper(sig)
X{
X#ifdef hpux
X    wait((int *) 0);
X#else hpux
X    while (wait3((union wait *) 0, WNOHANG, (struct rusage *) 0) > 0)
X	;
X#endif hpux
X}
X
X#ifdef hpux
X
Xsrandom(foo)
Xint foo;
X{
X    rand(foo);
X}
X
Xrandom()
X{
X    return(rand());
X}
X
Xgetrusage(foo, buf)
Xint foo;
Xstruct rusage *buf;
X{
X    buf->ru_utime.tv_sec = 0;
X    buf->ru_stime.tv_sec = 0;
X}
X
X#include <sys/signal.h>
X
Xint (*
Xsignal(sig, funct))()
Xint sig;
Xint (*funct)();
X{
X    struct sigvec vec, oldvec;
X
X    sigvector(sig, 0, &vec);
X    vec.sv_handler = funct;
X    sigvector(sig, &vec, (struct sigvec *) 0);
X}
X
X#endif hpux
END_OF_daemon.c
if test 29846 -ne `wc -c <daemon.c`; then
    echo shar: \"daemon.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f rmove.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"rmove.c\"
else
echo shar: Extracting \"rmove.c\" \(14954 characters\)
sed "s/^X//" >rmove.c <<'END_OF_rmove.c'
X
X/*
X
X	Copyright (c) 1986 	Chris Guthrie
X
XPermission to use, copy, modify, and distribute this
Xsoftware and its documentation for any purpose and without
Xfee is hereby granted, provided that the above copyright
Xnotice appear in all copies and that both that copyright
Xnotice and this permission notice appear in supporting
Xdocumentation.  No representations are made about the
Xsuitability of this software for any purpose.  It is
Xprovided "as is" without express or implied warranty.
X
X*/
X
X#include <X/Xlib.h>
X#include <stdio.h>
X#include <signal.h>
X#include <math.h>
X#include "defs.h"
X#include "struct.h"
X#include "data.h"
X
X#define SIZEOF(s)		(sizeof (s) / sizeof (*(s)))
X#define AVOID_TIME		4
X#define AVOID_CLICKS		200
X
X#define NORMALIZE(d) 		((((d) % 256) + 256) % 256)
X
X#define E_INTRUDER	0x01
X#define E_TSHOT		0x02
X#define E_PSHOT 	0x04
X
Xstruct Enemy {
X    int e_info;
X    int e_dist;
X    unsigned char e_course;	/* course to enemy */
X    unsigned char e_tcourse;	/* torpedo intercept course to enemy */
X    unsigned int e_flags;
X};
X
Xint timer;
Xstatic int avoidTime;
Xstatic unsigned short tok;
X
Xextern int debug;
Xextern int hostile;
Xextern int sticky;
Xextern int practice;
X
Xunsigned char	getcourse();
X
Xrmove()
X{
X    register int i;
X    register int burst;
X    register int numHits, tDir;
X    int		avDir;
X    extern struct Enemy *get_nearest();
X    struct Enemy *enemy_buf;
X    struct player *enemy;
X    static int	clock = 0;
X    static int	avoid[2] = { -32, 32 };
X    int no_cloak;
X
X    clock++;
X    /* Check that I'm alive */
X    if (me->p_status == PEXPLODE) {
X	signal(SIGALRM, SIG_IGN);
X	while (me->p_status == PEXPLODE)
X	    ;
X	while (me->p_ntorp > 0)
X	    ;
X	me->p_status = PFREE;
X	exit(0);
X    }
X    if (me->p_status == PDEAD) {
X	signal(SIGALRM, SIG_IGN);
X	me->p_status = PFREE;
X	exit(0);
X    }
X
X    /* keep ghostbuster away */
X    me->p_ghostbuster = 0;
X
X    /* Find an enemy */
X
X    enemy_buf = get_nearest();
X
X    if (enemy_buf > 0) {			/* Someone to kill */
X	enemy = &players[enemy_buf->e_info];
X	timer = 0;
X	if (debug)
X	    fprintf(stderr, "%d) noticed %d\n", me->p_no, enemy->p_no);
X    }
X    else if (enemy_buf < 0) { /* no more players. wait 1 minute. */
X	if (!sticky) {
X	    if (timer == 0)
X		timer = me->p_updates + 600;
X	    if (me->p_updates >= timer) {
X		signal(SIGALRM, SIG_IGN);
X		me->p_status = PFREE;
X		exit(0);
X	    }
X	}
X	if (do_repair()) {
X	    return;
X	}
X	go_home(0);
X	if (debug)
X	    fprintf(stderr, "%d) No players in game.\n", me->p_no);
X	return;
X    }
X    else if (enemy_buf == 0) {	 /* no one hostile */
X	if (debug)
X	    fprintf(stderr, "%d) No hostile players in game.\n", me->p_no);
X	if (do_repair()) {
X	    return;
X	}
X	go_home(0);
X	timer = 0;
X	return;
X    }
X
X/* Algorithm:
X** We have an enemy.
X** First priority: shoot at target in range.
X** Second: Dodge torps.
X** Third: Get away if we are damaged.
X** Fourth: repair.
X** Fifth: attack.
X*/
X
X/*
X** If we are a practice robot, we will do all but the second.  One
X** will be modified to shoot poorly and not use phasers.
X**/
X    /* Fire weapons!!! */
X    /*
X    ** get_nearest() has already determined if torpedoes and phasers
X    ** will hit.  It has also determined the courses which torps and
X    ** phasers should be fired.  If so we will go ahead and shoot here.
X    ** We will lose repair and cloaking for the rest of this interrupt.
X    ** if we fire here.
X    */
X
X    if (practice) {
X	no_cloak = 1;
X	if (enemy_buf->e_flags & E_TSHOT) {
X	    if (debug)
X		fprintf(stderr, "%d) firing torps\n", me->p_no);
X	    for (burst = 0; (burst < 3) && (me->p_ntorp < MAXTORP); burst++) {
X		tok += 11;
X		ntorp(enemy_buf->e_tcourse, TMOVE, tok);
X	    }
X	}
X    }
X    else {
X	no_cloak = 0;
X	if (enemy_buf->e_flags & E_TSHOT) {
X	    if (debug)
X		fprintf(stderr, "%d) firing torps\n", me->p_no);
X	    for (burst = 0; (burst < 2) && (me->p_ntorp < MAXTORP); burst++) {
X		repair_off();
X		cloak_off();
X		tok += 11;
X		ntorp(enemy_buf->e_tcourse, TSTRAIGHT, tok);
X		no_cloak++;
X	    }
X	}
X	if (enemy_buf->e_flags & E_PSHOT) {
X	    if (debug)
X		fprintf(stderr, "%d) phaser firing\n", me->p_no);
X	    no_cloak++;
X	    repair_off();
X	    cloak_off();
X	    phaser(enemy_buf->e_course);
X	}
X    }
X
X    /* Avoid torps */
X    /*
X    ** This section of code allows robots to avoid torps.
X    ** Within a specific range they will check to see if
X    ** any of the 'closest' enemies torps will hit them.
X    ** If so, they will evade for four updates.
X    ** Evading is all they will do for this round, other than shooting.
X    */
X
X    if (!practice) {
X	if (enemy->p_ntorp < 5) {
X	    if ((enemy_buf->e_dist < 15000) || (avoidTime > 0)) {
X		numHits = projectDamage(enemy->p_no, &avDir);
X		if (debug) {
X		    fprintf(stderr, "%d hits expected from %d from dir = %d\n",
X			    numHits, enemy->p_no, avDir);
X		}
X		if (numHits == 0) {
X		    if (--avoidTime > 0) {	/* we may still be avoiding */
X			if (angdist(me->p_desdir, me->p_dir) > 64)
X			    me->p_desspeed = 3;
X			else
X			    me->p_desspeed = 5;
X			return;
X		    }
X		} else {
X		    /*
X		     * Actually avoid Torps
X		     */ 
X		    avoidTime = AVOID_TIME;
X		    tDir = avDir - me->p_dir;
X		    /* put into 0->255 range */
X		    tDir = NORMALIZE(tDir);
X		    if (debug)
X			fprintf(stderr, "mydir = %d avDir = %d tDir = %d q = %d\n",
X			    me->p_dir, avDir, tDir, tDir / 64);
X		    switch (tDir / 64) {
X		    case 0:
X		    case 1:
X			    me->p_desdir = NORMALIZE(avDir + 64);
X			    break;
X		    case 2:
X		    case 3:
X			    me->p_desdir = NORMALIZE(avDir - 64);
X			    break;
X		    }
X		    if (!no_cloak)
X			cloak_on();
X
X		    if (angdist(me->p_desdir, me->p_dir) > 64)
X			me->p_desspeed = 3;
X		    else
X			me->p_desspeed = 5;
X			
X		    shield_up();
X		    if (debug)
X			fprintf(stderr, "evading to dir = %d\n", me->p_desdir);
X		    return;
X		}
X	    }
X	}
X
X	/*
X	** Trying another scheme.
X	** Robot will keep track of the number of torps a player has
X	** launched.  If they are greater than say four, the robot will
X	** veer off immediately.  Seems more humanlike to me.
X	*/
X
X	else if (enemy_buf->e_dist < 15000) {
X	    if (--avoidTime > 0) {	/* we may still be avoiding */
X		if (angdist(me->p_desdir, me->p_dir) > 64)
X		    me->p_desspeed = 3;
X		else
X		    me->p_desspeed = 5;
X		return;
X	    }
X	    if (random() % 2) {
X		me->p_desdir = NORMALIZE(enemy_buf->e_course - 64);
X		avoidTime = AVOID_TIME;
X	    }
X	    else {
X		me->p_desdir = NORMALIZE(enemy_buf->e_course + 64);
X		avoidTime = AVOID_TIME;
X	    }
X	    if (angdist(me->p_desdir, me->p_dir) > 64)
X		me->p_desspeed = 3;
X	    else
X		me->p_desspeed = 5;
X	    shield_up();
X	    return;
X	}
X    }
X	    
X    /* Run away */
X    /*
X    ** The robot has taken damage.  He will now attempt to run away from
X    ** the closest player.  This obviously won't do him any good if there
X    ** is another player in the direction he wants to go.
X    ** Note that the robot will not run away if he dodged torps, above.
X    ** The robot will lower his shields in hopes of repairing some damage.
X    */
X
X    if (me->p_damage > 0 && enemy_buf->e_dist < 13000) {
X	if (me->p_etemp > 900)		/* 90% of 1000 */
X	    me->p_desspeed = 5;
X	else
X	    me->p_desspeed = 6;
X	if (!no_cloak)
X	    cloak_on();
X	repair_off();
X	shield_down();
X	me->p_desdir = enemy_buf->e_course - 128;
X	if (debug)
X	    fprintf(stderr, "%d(%d)(%d/%d) running from %c%d %16s damage (%d/%d) dist %d\n",
X		me->p_no,
X		(int) me->p_kills,
X		me->p_damage,
X		me->p_shield,
X		teamlet[enemy->p_team],
X		enemy->p_no,
X		enemy->p_login,
X		enemy->p_damage,
X		enemy->p_shield,
X		enemy_buf->e_dist);
X	return;
X    }
X
X    /* Repair if necessary (we are safe) */
X    /*
X    ** The robot is safely away from players.  It can now repair in peace.
X    ** It will try to do so now.
X    */
X
X    if (do_repair()) {
X	return;
X    }
X
X    /* Attack. */
X    /*
X    ** The robot has nothing to do.  It will check and see if the nearest
X    ** enemy fits any of its criterion for attack.  If it does, the robot
X    ** will speed in and deliver a punishing blow.  (Well, maybe)
X    */
X
X    if ((enemy_buf->e_flags & E_INTRUDER) || (enemy_buf->e_dist < 15000)
X	|| (hostile)) {
X	if ((!no_cloak) && (enemy_buf->e_dist < 10000))
X	    cloak_on();
X	shield_up();
X	if (debug)
X	    fprintf(stderr, "%d(%d)(%d/%d) attacking %c%d %16s damage (%d/%d) dist %d\n",
X		me->p_no,
X		(int) me->p_kills,
X		me->p_damage,
X		me->p_shield,
X		teamlet[enemy->p_team],
X		enemy->p_no,
X		enemy->p_login,
X		enemy->p_damage,
X		enemy->p_shield,
X		enemy_buf->e_dist);
X
X	if (enemy_buf->e_dist < 15000) {
X	    me->p_desdir = enemy_buf->e_course + 
X		    avoid[(clock / AVOID_CLICKS) % SIZEOF(avoid)];
X	    if (angdist(me->p_desdir, me->p_dir) > 64)
X		me->p_desspeed = 3;
X	    else
X		me->p_desspeed = 4;
X	}
X	else {
X	    me->p_desdir = enemy_buf->e_course;
X	    if (angdist(me->p_desdir, me->p_dir) > 64)
X		me->p_desspeed = 3;
X	    else if (me->p_etemp > 900)		/* 90% of 1000 */
X		me->p_desspeed = 5;
X	    else
X		me->p_desspeed = 6;
X	}
X    }
X    else {
X	go_home(enemy_buf);
X    }
X}
X
Xunsigned char
Xgetcourse(x, y)
Xint x, y;
X{
X	return((unsigned char) (atan2((double) (x - me->p_x),
X	    (double) (me->p_y - y)) / 3.14159 * 128.));
X}
X
Xstruct {
X    int x;
X    int y;
X} center[] = { {0, 0},
X		{GWIDTH / 4, GWIDTH * 3 / 4},		/* Fed */
X		{GWIDTH / 4, GWIDTH / 4},		/* Rom */
X		{0, 0},
X		{GWIDTH * 3 / 4, GWIDTH  / 4},		/* Kli */
X		{0, 0},
X		{0, 0},
X		{0, 0},
X		{GWIDTH * 3 / 4, GWIDTH * 3 / 4}};	/* Ori */
X
X/* This function means that the robot has nothing better to do.
X   If there are hostile players in the game, it will try to get
X   as close to them as it can, while staying in it's on space.
X   Otherwise, it will head to the center of its own space.
X*/
Xgo_home(ebuf)
Xstruct Enemy *ebuf;
X{
X    int x, y, speed;
X    double dx, dy;
X    int tdist;
X    struct player *j;
X
X    if (ebuf == 0) {  /* No enemies */
X	if (debug)
X	    fprintf(stderr, "%d) No enemies\n", me->p_no);
X	x = center[me->p_team].x;
X	y = center[me->p_team].y;
X    }
X    else {	/* Let's get near him */
X	j = &players[ebuf->e_info];
X	x = j->p_x;
X	y = j->p_y;
X	switch (me->p_team) {
X	    case FED:
X		if (x > (GWIDTH/2) - 5000)
X		    x = (GWIDTH/2) - 5000;
X		if (y < (GWIDTH/2) + 5000)
X		    y = (GWIDTH/2) + 5000;
X		break;
X	    case ROM:
X		if (x > (GWIDTH/2) - 5000)
X		    x = (GWIDTH/2) - 5000;
X		if (y > (GWIDTH/2) - 5000)
X		    y = (GWIDTH/2) - 5000;
X		break;
X	    case KLI:
X		if (x < (GWIDTH/2) + 5000)
X		    x = (GWIDTH/2) + 5000;
X		if (y > (GWIDTH/2) - 5000)
X		    y = (GWIDTH/2) - 5000;
X		break;
X	    case ORI:
X		if (x < (GWIDTH/2) + 5000)
X		    x = (GWIDTH/2) + 5000;
X		if (y < (GWIDTH/2) + 5000)
X		    y = (GWIDTH/2) + 5000;
X		break;
X	}
X    }
X    if (debug)
X	fprintf(stderr, "%d) moving towards (%d/%d)\n",
X	    me->p_no, x, y);
X
X    /* Note that I've decided that robots should never stop moving.
X    ** It makes them too easy to kill
X    */
X
X    me->p_desdir = getcourse(x, y);
X    if (angdist(me->p_desdir, me->p_dir) > 64)
X	me->p_desspeed = 3;
X    else if (me->p_etemp > 900)		/* 90% of 1000 */
X	me->p_desspeed = 5;
X    else {
X	dx = x - me->p_x;
X	dy = y - me->p_y;
X	me->p_desspeed = (hypot(dx, dy) / 5000) + 3;
X    }
X    cloak_off();
X}
X
XprojectDamage(eNum, dirP)
X	int	*dirP;
X{
X	register int		i, j, numHits = 0, mx, my, tx, ty, dx, dy;
X	double			tdx, tdy, mdx, mdy;
X	register struct torp	*t;
X
X	*dirP = 0;
X	for (i = 0, t = &torps[eNum * MAXTORP]; i < MAXTORP; i++, t++) {
X		if (t->t_status == TFREE)
X			continue;
X		tx = t->t_x; ty = t->t_y;
X		mx = me->p_x; my = me->p_y;
X		tdx = (double) t->t_speed * Cos[t->t_dir] * WARP1;
X		tdy = (double) t->t_speed * Sin[t->t_dir] * WARP1;
X		mdx = (double) me->p_speed * Cos[me->p_dir] * WARP1;
X		mdy = (double) me->p_speed * Sin[me->p_dir] * WARP1;
X		for (j = t->t_fuse; j > 0; j--) {
X			tx += tdx; ty += tdy;
X			mx += mdx; my += mdy;
X			dx = tx - mx; dy = ty - my;
X			if (ABS(dx) < EXPDIST && ABS(dy) < EXPDIST) {
X				numHits++;
X				*dirP += t->t_dir;
X				break;
X			}
X		}
X	}
X	if (numHits > 0)
X		*dirP /= numHits;
X	return (numHits);
X}
X
Xstruct Enemy ebuf;
X
Xstruct Enemy *
Xget_nearest()
X{
X    int pcount = 0;
X    register int i;
X    register struct player *j;
X    int intruder = 0;
X    int tdist;
X    double dx, dy;
X
X    /* Find an enemy */
X    ebuf.e_info = me->p_no;
X    ebuf.e_dist = GWIDTH + 1;
X
X    pcount = 0;  /* number of human players in game */
X
X    /* avoid dead slots, me, other robots (which aren't hostile) */
X    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
X	if ((j->p_status != PALIVE) || (j == me) ||
X	    ((j->p_flags & PFROBOT) && (!hostile)))
X	    continue;
X	else
X	    pcount++;	/* Other players in the game */
X	if (((j->p_swar | j->p_hostile) & me->p_team)
X		|| ((me->p_swar | me->p_hostile) & j->p_team)) {
X	    /* We have an enemy */
X	    /* Get his range */
X	    dx = j->p_x - me->p_x;
X	    dy = j->p_y - me->p_y;
X	    tdist = hypot(dx, dy);
X
X	    /* Check to see if ship is in our space. */
X	    switch (me->p_team) {
X		case FED:
X		    intruder = ((j->p_x < GWIDTH/2) && (j->p_y > GWIDTH/2));
X		    break;
X		case ROM:
X		    intruder = ((j->p_x < GWIDTH/2) && (j->p_y < GWIDTH/2));
X		    break;
X		case KLI:
X		    intruder = ((j->p_x > GWIDTH/2) && (j->p_y < GWIDTH/2));
X		    break;
X		case ORI:
X		    intruder = ((j->p_x > GWIDTH/2) && (j->p_y > GWIDTH/2));
X		    break;
X	    }
X
X	    if (tdist < ebuf.e_dist) {
X		ebuf.e_info = i;
X		ebuf.e_dist = tdist;
X		if (intruder)
X		    ebuf.e_flags |= E_INTRUDER;
X		else
X		    ebuf.e_flags &= ~(E_INTRUDER);
X	    }
X	}
X    }
X    if (pcount == 0)
X	return ((struct Enemy *) -1);	/* no players in game */
X    else if (ebuf.e_info == me->p_no)
X	return (0);			/* no hostile players in the game */
X    else {
X	j = &players[ebuf.e_info];
X
X	/* Get torpedo course to nearest enemy */
X	ebuf.e_flags &= ~(E_TSHOT);
X	for (i = 0; i < 50; i++) {
X	    double  he_x, he_y, area;
X
X	    he_x = j->p_x + Cos[j->p_dir] * j->p_speed * i * WARP1;
X	    he_y = j->p_y + Sin[j->p_dir] * j->p_speed * i * WARP1;
X	    area = i * me->p_ship.s_torpspeed * WARP1;
X	    if (hypot(he_x - me->p_x, he_y - me->p_y) < area) {
X		ebuf.e_flags |= E_TSHOT;
X		ebuf.e_tcourse = getcourse((int) he_x, (int) he_y);
X		break;
X	    }
X	}
X	/*
X	if (debug)
X	    fprintf(stderr, "torpedo course to enemy %d is %d (%d) - %s\n", 
X		    j->p_no,
X		    (int) ebuf.e_tcourse,
X		    (int) ebuf.e_tcourse * 360 / 256,
X		    (ebuf.e_flags & E_TSHOT) ? "aiming to hit" :
X		    "no hit possible");
X	*/
X
X	/* Get phaser shot status */
X	if (ebuf.e_dist < 5000)
X		ebuf.e_flags |= E_PSHOT;
X	else
X		ebuf.e_flags &= ~(E_PSHOT);
X
X	/* get course info */
X	ebuf.e_course = getcourse(j->p_x, j->p_y);
X	/*
X	if (debug)
X	    fprintf(stderr, "Set course to enemy is %d (%d)\n",
X		    (int)ebuf.e_course, 
X		    (int) ebuf.e_course * 360 / 256);
X	*/
X
X	return (&ebuf);
X    }
X}
X
Xdo_repair()
X{
X/* Repair if necessary (we are safe) */
X
X    if (me->p_damage > 0) {
X	me->p_desspeed = 0;
X	cloak_off();
X	shield_down();
X	me->p_desspeed = 0;
X	if (me->p_speed == 0)
X	    repair();
X	if (debug)
X	    fprintf(stderr, "%d) repairing damage at %d\n",
X		me->p_no,
X		me->p_damage);
X	return(1);
X    }
X    else {
X	return (0);
X    }
X}
END_OF_rmove.c
if test 14954 -ne `wc -c <rmove.c`; then
    echo shar: \"rmove.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f xtrek.6 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"xtrek.6\"
else
echo shar: Extracting \"xtrek.6\" \(472 characters\)
sed "s/^X//" >xtrek.6 <<'END_OF_xtrek.6'
X.TH XTREK 6 "19 Sept 1986"
X.SH NAME
Xxtrek \- X based multi-player space shoot 'em up game
X.SH SYNOPSIS
X.B xtrek
X[ monitor:0 ]
X.SH DESCRIPTION
X.I Xtrek
Xis a game based on shared memory and the X window environment.
XIt is thoroughly described in xtrek.doc which probably lives
Xin /usr/games/lib/xtrek/xtrek.doc.
X.SH OPTIONS
X.I Xtrek
Xwill get the monitor name from your environment unless
Xit is specified on the command line.
X.SH AUTHOR
XChris Guthrie (chris@ic.berkeley.edu)
END_OF_xtrek.6
if test 472 -ne `wc -c <xtrek.6`; then
    echo shar: \"xtrek.6\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 1 \(of 6\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit= P