[net.sources.games] Spacewar version 2.0 for Sun-3's running 3.0 UNIX or later

jonab@SDC-CAMARILLO.ARPA.UUCP (03/04/87)

After a couple of months of beta-testing, I have decided to release
Spacewar upon the masses.  It will run only on Sun-3 machines with a
68881 chip running Sun UNIX 3.0 release or later.  For those who have
the beta test version already, this version no longer uses ethernet
broadcasts for screen updates and I have added asteroids as additional
obsticles to avoid.  Enjoy!

extract the sources with "sed -e '1,/^#/d' | sh".
------------------------------CUT HERE-----------------------------------
#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./spacewar.6`
then
echo "writting ./spacewar.6"
cat > ./spacewar.6 << '\Rogue\Monster\'
.\"	Copyright 1987 Jonathan Biggar
.\"
.\"	Permission is given to copy and distribute for non-profit purposes.
.\"
.TH SPACEWAR 6 "16 December 1986"
.SH NAME
spacewar \- multi-user science fiction combat game
.SH SYNOPSIS
.B /usr/games/spacewar
[ \fB-s\fIx\fR ]
.SH DESCRIPTION
.I Spacewar
is an arcade-like game inspired by the original game written for a PDP-1.
Each user controls a single ship on the screen.  The object of the game
is to shoot your opponents with your missiles while avoiding their missiles.
.PP
The display is divided into two areas:  the main window, which shows the
positions of all ships and missiles, and the score window, which shows the
names and scores of all players that are active.  When a player's ship is
destroyed, buttons will pop up in the score window to allow the
user to re-enter the game.
.PP
A ship is represented on the screen by an isosceles triangle, a missile by
a point.  Each user's ship will be highlighted on his screen by a stripe
down the middle of the ship.  Sometimes there will be a sun in the middle
of the screen, with gravity that either attracts or repels all other
objects.
.PP
The player maneuvers his ship with the mouse buttons:
.RS
.IP LEFT 8
rotates the ship counter-clockwise
.IP MIDDLE 8
fires the ship's thrusters
.IP RIGHT 8
rotates the ship counter-clockwise.
.RE
.PP
Striking the carriage return key on the keyboard will activate the ship's
shields.  The shields will protect the ship from destruction for two seconds,
after which the shields may not be re-activated for eight more seconds.
.PP
Striking the escape key will exit the game.
.PP
Striking any other key on the keyboard will fire one missile.  A ship may
only fire five missiles at a time.  A player will receive points for
hitting another ship:  ten percent of the difference in scores between the
target and firing players, but always at least one point.
.PP
A ship is destroyed by being hit by a missile, or running into the star or
another ship.  Running into the star or an asteroid will deduct one point
from the player's score.
.PP
.SH OPTIONS
.IP \fB\-c\fIx\fR
Set the shield activation key to \fIx\fP.
(\en, \er, and \eb are understood).
.SH DIAGNOSTICS
.SH FILES
/usr/games/lib/spaceward		spacewar daemon
.br
/usr/games/lib/spacewar.{dir,pag}	spacewar score file
.SH AUTHOR
Copyright 1987 Jonathan Biggar
.br
<jonab@SDC-CAMARILLO.ARPA> or <sdcrdcf!jonab>
.SH BUGS
Chews up too many cpu cycles--can only run on a sun-3 with a 68881, and
can't reliably run the game and the daemon on the same machine.
.PP
Can only support 3 or 4 users, although there is room for about 20.
.PP
Because it uses udp(4) broadcast packets to find the server ,
the game must run setuid root.
\Rogue\Monster\
else
  echo "will not over write ./spacewar.6"
fi
if [ `wc -c ./spacewar.6 | awk '{printf $1}'` -ne 2712 ]
then
echo `wc -c ./spacewar.6 | awk '{print "Got " $1 ", Expected " 2712}'`
fi
if `test ! -s ./README`
then
echo "writting ./README"
cat > ./README << '\Rogue\Monster\'





SPACEWAR Version 2.0

Copyright 1987 Jonathan Biggar

Permission is given to copy and distribute for non-profit purposes.

This game will only operate on a Sun-3 processor with a 68881 chip
and a 1152x900 monochrome or color monitor, running Sun 3.0 UNIX or later.

INSTALLATION INSTRUCTIONS:

1.	Unpack this kit in an empty directory and edit Makefile.
	Change the SCOREFILE, BYMACHINE, .c.o, and MANLOC options if you wish.

2.	run 'make install' as root.  This program must run setuid root
	because it uses udp broadcasts.

3..	Edit /etc/services (or update the yellow pages map) and add the
	following lines:

spacewar	700/udp
spaceward	701/udp

	the port numbers don't matter, as long as they are unique, and
	less than 1024.

4.	Pick a single machine on your ethernet to run the spacewar daemon.
	This machine MUST NOT be used to play the game also--spacewar
	is too much of a cpu hog.  Edit /etc/servers on this machine and
	add the line:

spaceward	udp	/usr/games/lib/spaceward

	Then restart the "inetd" process on that machine.

5.	Get a friend or two and each run /usr/games/spacewar on different
	workstations.

NOTE:

Only one spacewar server can run on an ethernet.  If you try to run
more than one, it will get VERY confusing.

If you have any questions or problems/bug reports, send them to me:

Jonathan Biggar
jonab@CAM.UNISYS.COM
	or
sdcrdcf!jonab
\Rogue\Monster\
else
  echo "will not over write ./README"
fi
if [ `wc -c ./README | awk '{printf $1}'` -ne 1372 ]
then
echo `wc -c ./README | awk '{print "Got " $1 ", Expected " 1372}'`
fi
if `test ! -s ./Makefile`
then
echo "writting ./Makefile"
cat > ./Makefile << '\Rogue\Monster\'
# dbm file to save scores in
SCOREFILE = /usr/games/lib/spacewar

# set to 1 if you want to keep scores in the format user@host.
# set to 0 if you want just the user name.
BYMACHINE = 1

# place to put the manual page
MANLOC = /usr/man/man6/spacewar.6

# if you are running release 3.2, use this .c.o definition
.c.o:
	cc -c $(CFLAGS) $< /usr/lib/f68881.il

# if you are not running release 3.2, use the following .c.o definition
#.c.o:
#	cc -S $(CFLAGS) $<
#	sed -f mathfix $*.s >tmp.s
#	as -20 -o $@ tmp.s
#	rm -f $*.s tmp.s

CFLAGS = -O -f68881 -DSCOREFILE=\"$(SCOREFILE)\" -DBYMACHINE=$(BYMACHINE)

SWOBJS = spacewar.o
SWLIBS= -lm -lsuntool -lsunwindow -lpixrect
SWDOBJS = spaceward.o
SWDLIBS = -lm -lsunwindow -ldbm
DISTFILES = spacewar.6 README Makefile spacewar.h spacewar.icon spacewar.c spaceward.c mathfix

all: spacewar spaceward

spacewar: $(SWOBJS)
	cc $(CFLAGS) -o spacewar $(SWOBJS) $(SWLIBS)

spaceward: $(SWDOBJS)
	cc $(CFLAGS) -o spaceward $(SWDOBJS) $(SWDLIBS)

score: score.c
	cc $(CFLAGS) -o score score.c

install: spacewar spaceward
	install -s -m 4711 -o root spacewar /usr/games
	install -s -o root spaceward /usr/games/lib
	cp spacewar.6 $(MANLOC)

installd: spaceward
	install -s -o root spaceward /usr/games/lib

spacewar.o: spacewar.h

spaceward.o: spacewar.h

kit: $(DISTFILES)
	shar -f swshar -c $(DISTFILES)

clean:
	/bin/rm -f core spacewar spaceward *.o kit *.s
\Rogue\Monster\
else
  echo "will not over write ./Makefile"
fi
if [ `wc -c ./Makefile | awk '{printf $1}'` -ne 1396 ]
then
echo `wc -c ./Makefile | awk '{print "Got " $1 ", Expected " 1396}'`
fi
if `test ! -s ./spacewar.h`
then
echo "writting ./spacewar.h"
cat > ./spacewar.h << '\Rogue\Monster\'
/*
 *	Copyright 1987, Jonathan Biggar
 *
 *	Permission is given to copy and distribute for non-profit purposes.
 *
 */

#ifndef lint
static char *rcsh = "$header$ Copyright 1986 Jonathan Biggar";
#endif !lint

/*
 * $log$
 */

#define UPDATE_FREQ		100000	/* microsecs between updates */
#define ROT_SPEED		5	/* must divide 360 evenly */
#define MAX_VELOCITY		15	/* maximum speed */
#define MISSILE_VELOCITY	25	/* missile delta velocity */
#define KILL_RADIUS		10	/* radius of kill for missiles */
#define DIE_PERIOD		20	/* period a ship dies in */
#define MIS_P_PLAYER		5
#define SHIELD_TIME		20	/* time shield will protect */
#define SHIELD_RECHARGE		100	/* time between shield activations */
#define ASTEROID_MIN_PER	100	/* minimum time before new asteroid */
#define ASTEROID_MAX_PER	300	/* maximum time before new asteroid */

#define MAX_PACKET	(1500 - 20 - 8) /* for ip and udp headers */
#define MAX_OBJECTS	((MAX_PACKET - sizeof(struct header))/sizeof(Object))
#define OTHER_OBJECTS	10
#define MAX_PLAYERS	((MAX_OBJECTS-OTHER_OBJECTS)/(MIS_P_PLAYER+1))
#define MISSILE_START	(MAX_PLAYERS+OTHER_OBJECTS)
#define NUM_MISSILES	MAX_PLAYERS * MIS_P_PLAYER

#define S_X_MAX		1152
#define S_Y_MAX		900
#define BORDER		5
#define X_MAX		990
#define Y_MAX		(S_Y_MAX-BORDER*2)
#define P_X_MAX		(S_X_MAX-X_MAX-BORDER*3)
#define P_Y_MAX		Y_MAX

#define mysin(x)	sin_tab[x]
#define mycos(x)	cos_tab[x]

typedef struct object {
    u_char	type;
    u_char	flags;
    short	x_pos;
    short	y_pos;
    short	rot;
} Object;

/* object.type values */

#define SHIP_T		0
#define MISSILE_T	1
#define SUN_T		2
#define ASTEROID_T	3

/* objects.flags values */

#define ALIVE		0x1
#define DYING		0x2
#define THRUSTING	0x4
#define SHIELD		0x8

/* udp packet header */

struct header {
    u_char	type;
    u_char	ship_num;
};

/* header.type values */

#define HELLO		0
#define YOU_ARE		1
#define CONTROLS	2
#define DISPLAY		4
#define BYE		5
#define USERS		6

#define NAME_SIZE	40

typedef struct player {
    char	name[NAME_SIZE];
    int		score;
} Player;

typedef struct controls {
    struct header head;
    u_int	left:1;
    u_int	thrust:1;
    u_int	right:1;
    u_int	fire:1;
    u_int	shield:1;
    u_char	seq;
} Controls;
\Rogue\Monster\
else
  echo "will not over write ./spacewar.h"
fi
if [ `wc -c ./spacewar.h | awk '{printf $1}'` -ne 2216 ]
then
echo `wc -c ./spacewar.h | awk '{print "Got " $1 ", Expected " 2216}'`
fi
if `test ! -s ./spacewar.icon`
then
echo "writting ./spacewar.icon"
cat > ./spacewar.icon << '\Rogue\Monster\'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFE,0xFFFF,
	0xFFFF,0xFFFF,0xFFFE,0xFFFF,0xFFFF,0xFFFF,0xFFFD,0x7FFF,
	0xFFFF,0xFFFF,0xFFFD,0x7FFF,0xFFFF,0xFFFF,0xFFFD,0x7FFF,
	0xFFFF,0xFFFF,0xFFFD,0x7FFF,0xFFFF,0xFFFF,0xFFFB,0xBFFF,
	0xFFFF,0xFFFF,0xFFFB,0xBFFF,0xFFFF,0xFFFF,0xFFFB,0xBFFF,
	0xFFFF,0xFFFF,0xFFF7,0xDFFF,0xFFFF,0xFFFF,0xFFF6,0xDFFF,
	0xFFFF,0xFFFF,0xFFF6,0xDFFF,0xFFFF,0xFFFF,0xFFF5,0x5FFF,
	0xFFFF,0xFFFF,0xFFED,0x6FFF,0xFFFF,0xFFFF,0xFFED,0x6FFF,
	0xF3FF,0xFFFF,0xFFED,0x6FFF,0xF43F,0xFFFF,0xFFDB,0xB7FF,
	0xF7C3,0xFFFF,0xFFDB,0xB7FF,0xF7FC,0x3FFF,0xFFDB,0xB7FF,
	0xF7FF,0xC3FF,0x1FDB,0xB7FF,0xF000,0x007F,0x1FB7,0xDBFF,
	0xF7FF,0xE1FF,0x1FB7,0xDBFF,0xF7FE,0x1FFF,0xFFB7,0xDBFF,
	0xF7E1,0xFFFF,0xFF77,0xDDFF,0xF61F,0xFFFF,0xFF6F,0xEDFF,
	0xF1FF,0xFFFF,0xFF6F,0xEDFF,0xFFFF,0xFFFF,0xFF6F,0xEDFF,
	0xFFFF,0xFFFF,0xFEEF,0xEEFF,0xFFFF,0xFFFF,0xFEDF,0xF6FF,
	0xFFFF,0xFFFF,0xFEDF,0xF6FF,0xFFFF,0xFFFF,0xFDC0,0x077F,
	0xFFFF,0xFFFF,0xFDFF,0xFF7F,0xFFFF,0xFFFF,0xFDFF,0xFF7F,
	0xFFFF,0xFFFF,0xFDFF,0xFF7F,0xFFFF,0xFFFF,0xFBFF,0xFFBF,
	0xFFFF,0xFFFF,0xF800,0x003F,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF
\Rogue\Monster\
else
  echo "will not over write ./spacewar.icon"
fi
if [ `wc -c ./spacewar.icon | awk '{printf $1}'` -ne 1933 ]
then
echo `wc -c ./spacewar.icon | awk '{print "Got " $1 ", Expected " 1933}'`
fi
if `test ! -s ./spacewar.c`
then
echo "writting ./spacewar.c"
cat > ./spacewar.c << '\Rogue\Monster\'
/*
 *	Copyright 1987, Jonathan Biggar
 *
 *	Permission is given to copy and distribute for non-profit purposes.
 *
 */

#ifndef lint
static char *rcs = "$header$ Copyright 1987 Jonathan Biggar";
#endif !lint

/*
 * $log$
 */

#include <stdio.h>
#include <sys/types.h>
#include <suntool/sunview.h>
#include <suntool/canvas.h>
#include <suntool/panel.h>
#include <sunwindow/notify.h>
#include <math.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "spacewar.h"

#define	ITIMER_NULL	((struct itimerval *)0)

void			die();

char			shieldkey = '\r';

Frame			main_frame;
static short		icon_image[] = {
#include "spacewar.icon"
};
DEFINE_ICON_FROM_IMAGE(icon, icon_image);

Canvas			canvas;
Pixwin			*canvas_pw;
Panel			panel;
Panel_item		play_message;
Panel_item		play_yes;
Panel_item		play_no;
Panel_item		cp1;
Panel_item		cp2;
void			play_yes_no();
Panel_item		player_names[MAX_PLAYERS];
Panel_item		player_scores[MAX_PLAYERS];

struct singlecolor	foreground = {255, 255, 255};
struct singlecolor	background = {0, 0, 0};

double			deg_2_rad;
double			sin_tab[360];
double			cos_tab[360];

/* xy_point_list contains the vectors needed to draw a ship and a star */

double			xy_point_list[][2] = {	/*0*/	10.0,	0.0,
						/*1*/	-10.0,	5.0,
						/*2*/	-10.0,	-5.0,
						/*3*/	-10.0,	0.0,
						/*4*/	-15.0,	0.0,
						/*5*/	10.0,	0.0,
						/*6*/	-10.0,	0.0,
						/*7*/	0.0,	10.0,
						/*8*/	0.0,	-10.0,
						/*9*/	8.66,	5.0,
						/*10*/	-8.66,	-5.0,
						/*11*/	5.0,	8.66,
						/*12*/	-5.0,	-8.66,
						/*13*/	-5.0,	8.66,
						/*14*/	5.0,	-8.66,
						/*15*/	-8.66,	5.0,
						/*16*/	8.66,	-5.0,
						/*17*/	20.0,	0.0,
						/*18*/	-15.0,	10.0,
						/*19*/	-15.0,	-10.0,
						/*20*/	10.0, 	0,
						/*21*/	7.0,	-5.0,
						/*22*/	2.0,	-7.0,
						/*23*/	0,	-10.0,
						/*24*/	-10.0,	-7.0,
						/*25*/	-9.0,	6.0,
						/*26*/	2.0,	10.0};
#define PL_SIZE		(sizeof(xy_point_list)/sizeof(double)/2)
double			rt_point_list[PL_SIZE][3];

Controls		controls = { {CONTROLS, 0}, 0, 0, 0, 0, 0, 0};
struct itimerval	control_timer = {0, UPDATE_FREQ, 0, UPDATE_FREQ};
int			control_count = 0;
int			last_ack = 0;
Notify_value		control_send();

int			ship_num = -1;
int			num_objects = 0;
Object			objects[MAX_OBJECTS];
int			die_count[MAX_PLAYERS];

int			sock;
struct sockaddr_in	my_addr;
struct sockaddr_in	serv_addr;

struct {
    struct header	head;
    char		name[NAME_SIZE];
}			hello_head = { HELLO, -1};
struct itimerval	hello_timer = {1, 0, 1, 0};
int			hello_count = 0;
Notify_value		hello();

void			dead();
void			event_proc();
Notify_value		net_input();

char			*getlogin();
struct in_addr		inet_makeaddr();

main(argc, argv)
int argc;
char **argv;
{
    struct servent	*se;
    struct hostent	*he;
    char 		hostname[32];
    register int	i;
    Cursor		cursor;
    char		*name;
    int			rows;
    int			cols;
    struct pixfont	*panel_font;
    struct pixfont	*default_font;

    /* parse arguments */
    for(argc--, argv++; argc > 0; argc--, argv++)
	if (argv[0][0] = '-')
	    switch (argv[0][1]) {
	    case 's':
		if (argv[0][2] == '\\')
		    switch(argv[0][3]) {
		    case 'r':
			shieldkey = '\r';
			break;
		    case 'n':
			shieldkey = '\n';
			break;
		    case 'b':
			shieldkey = '\b';
			break;
		    }
		else
		    shieldkey = argv[0][2];
		break;
	    default:
		die("Usage: spacewar [-s{key}]\n", 0);
		break;
	    }
	else
	    die("Usage: spacewar [-s{key}]\n", 0);
	    
    /* fill in sine and cosine tables for future reference and speed */
    
    deg_2_rad = acos(-1.0)/180.0;
    for (i = 0; i < 360; i++) {
	sin_tab[i] = sin(i*deg_2_rad);
	cos_tab[i] = cos(i*deg_2_rad);
    }
    
    /* pre-calculate angles for ship rotations */
    
    for (i = 0; i < PL_SIZE; i++) {
	
	rt_point_list[i][0] =
	  sqrt((xy_point_list[i][0] * xy_point_list[i][0] +
	  xy_point_list[i][1] * xy_point_list[i][1]));
	rt_point_list[i][1] = xy_point_list[i][1]/rt_point_list[i][0];
	rt_point_list[i][2] = xy_point_list[i][0]/rt_point_list[i][0];
    }

    /* setup my address and server address */
    
    if ((se = getservbyname("spacewar", "udp")) == NULL)
	die("Can't find spacewar service\n", 0);
    gethostname(hostname, sizeof(hostname));
    if ((he = gethostbyname(hostname)) == NULL)
	die("Can't find hostname?\n", 0);
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = se->s_port;
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
	die("Can't create socket:", 1);
    if(bind(sock, &my_addr, sizeof(my_addr)) == -1)
	die("Can't bind socket address:", 1);
    if((se = getservbyname("spaceward", "udp")) == NULL)
	die("Can't find spaceward service\n", 0);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = se->s_port;
    bcopy(he->h_addr, &serv_addr.sin_addr, he->h_length);
    serv_addr.sin_addr =
      inet_makeaddr(inet_netof(serv_addr.sin_addr),INADDR_ANY);
      
    /* set up my name */
    
    if ((name = getlogin()) != NULL)
	strcpy(hello_head.name, name);
    else
	strcpy(hello_head.name, "unknown");
    strcat(hello_head.name, "@");
    strcat(hello_head.name, hostname);

    /* invert the icon because we are reverse video */
    for (i = 0; i < sizeof(icon_image)/sizeof(short); i++)
	icon_image[i] ^= 0xFFFF;

    /* create main frame */
   
    main_frame = window_create(NULL, FRAME,
			       FRAME_SHOW_LABEL, FALSE,
			       WIN_HEIGHT, S_Y_MAX,
			       WIN_WIDTH, S_X_MAX,
			       WIN_X, 0,
			       WIN_Y, 0,
			       FRAME_FOREGROUND_COLOR, &foreground,
			       FRAME_BACKGROUND_COLOR, &background,
			       FRAME_INHERIT_COLORS, TRUE,
			       FRAME_SUBWINDOWS_ADJUSTABLE, FALSE,
			       FRAME_ICON, &icon,
			       0);

    /* set up canvas for the graphics */
    
    canvas = window_create(main_frame, CANVAS,
			   WIN_HEIGHT, Y_MAX,
			   WIN_WIDTH, X_MAX,
			   CANVAS_RETAINED, FALSE,
			   WIN_EVENT_PROC, event_proc,
			   WIN_CONSUME_KBD_EVENTS,
			       WIN_ASCII_EVENTS,
			       0,
			   WIN_CONSUME_PICK_EVENTS,
			       WIN_MOUSE_BUTTONS,
			       0,
			   0);
    win_set_kbd_focus((int)window_get(canvas, WIN_FD),
      (int)window_get(canvas, WIN_DEVICE_NUMBER));
    canvas_pw = canvas_pixwin(canvas);

    /* set up the panel on the right hand side */
    
    default_font = pf_default();
    panel_font = pf_open("/usr/lib/fonts/fixedwidthfonts/screen.r.7");
    panel = window_create(main_frame, PANEL,
			  PANEL_ACCEPT_KEYSTROKE, TRUE,
			  WIN_NOTIFY_EVENT_PROC, event_proc,
			  WIN_HEIGHT, P_Y_MAX,
			  WIN_WIDTH, P_X_MAX,
			  WIN_RIGHT_OF, canvas,
			  WIN_FONT, panel_font,
			  0);
    rows = (int)window_get(panel, WIN_ROWS);
    /* work around sunview bug */
    cols = (int)window_get(panel, WIN_WIDTH)/
      ((int)window_get(panel, WIN_COLUMN_WIDTH) +
      (int)window_get(panel, WIN_COLUMN_GAP));
    cp1 = panel_create_item(panel, PANEL_MESSAGE,
			     PANEL_LABEL_STRING, "Copyright 1987",
			     PANEL_ITEM_Y, ATTR_ROW(2),
			     PANEL_ITEM_X, ATTR_COL(cols/2-10),
			     PANEL_LABEL_FONT, default_font,
			     0);
    cp2 = panel_create_item(panel, PANEL_MESSAGE,
			     PANEL_LABEL_STRING, "Jonathan Biggar",
			     PANEL_ITEM_Y, ATTR_ROW(4),
			     PANEL_ITEM_X, ATTR_COL(cols/2-11),
			     PANEL_LABEL_FONT, default_font,
			     0);
    play_message = panel_create_item(panel, PANEL_MESSAGE,
				     PANEL_LABEL_STRING, "Play again?",
				     PANEL_ITEM_Y, ATTR_ROW(8),
				     PANEL_ITEM_X, ATTR_COL(cols/2-8),
				     PANEL_SHOW_ITEM, FALSE,
				     PANEL_LABEL_FONT, default_font,
				     0);
    play_yes = panel_create_item(panel, PANEL_BUTTON,
				 PANEL_LABEL_IMAGE,
				   panel_button_image(panel, "YES", 3,
				     default_font),
				 PANEL_ITEM_Y, ATTR_ROW(10),
				 PANEL_ITEM_X, ATTR_COL(cols/2-5),
				 PANEL_NOTIFY_PROC, play_yes_no,
				 PANEL_SHOW_ITEM, FALSE,
				 0);
    play_no = panel_create_item(panel, PANEL_BUTTON,
				PANEL_LABEL_IMAGE,
				  panel_button_image(panel, "NO", 3,
				    default_font),
				PANEL_ITEM_Y, ATTR_ROW(12),
				PANEL_ITEM_X, ATTR_COL(cols/2-5),
				PANEL_NOTIFY_PROC, play_yes_no,
				PANEL_SHOW_ITEM, FALSE,
				0);

    /* set up panel item for each player's name */
    
    for (i = 0; i < MAX_PLAYERS; i++) {
	player_scores[i] = panel_create_item(panel, PANEL_MESSAGE,
					     PANEL_LABEL_STRING, "",
					     PANEL_ITEM_X, ATTR_COL(1),
					     PANEL_ITEM_Y,
					       ATTR_ROW(rows-MAX_PLAYERS+i),
					     0);
	player_names[i] =
	  panel_create_item(panel, PANEL_MESSAGE,
				   PANEL_LABEL_STRING, "",
				   PANEL_ITEM_X, ATTR_COL(6),
				   PANEL_ITEM_Y,
				     ATTR_ROW(rows-MAX_PLAYERS+i),
				   0);
    }

    /* make cursor visible when in panel */
    
    cursor = window_get(panel, WIN_CURSOR);
    cursor_set(cursor, CURSOR_OP, PIX_SRC^PIX_DST, 0);
    window_set(panel, WIN_CURSOR, cursor, 0);

    /* start main processing */
    		  
    (void) notify_set_input_func(&sock, net_input, sock);
    (void) notify_set_itimer_func(&ship_num, hello, ITIMER_REAL,
	&hello_timer, ITIMER_NULL);
    window_main_loop(main_frame);
    exit(0);
}

void draw_vector(ship, p1, p2, on, boom_ang, boom_x, boom_y)
Object		*ship;
register int	p1;
register int	p2;
int		on;
int		boom_ang;
int		boom_x;
int		boom_y;
{
    register double	tsin;
    register double	tcos;
    
    if (ship->rot + boom_ang >= 0) {
	tsin = mysin((ship->rot + boom_ang) % 360);
	tcos = mycos((ship->rot + boom_ang) % 360);
    } else {
	tsin = mysin((ship->rot + boom_ang) % 360 + 360);
	tcos = mycos((ship->rot + boom_ang) % 360 + 360);
    }
    pw_vector(canvas_pw,
      ship->x_pos + boom_x + (int)(rt_point_list[p1][0]*
        (tcos*rt_point_list[p1][2]-tsin*rt_point_list[p1][1])),
      ship->y_pos - boom_y - (int)(rt_point_list[p1][0]*
        (tcos*rt_point_list[p1][1]+tsin*rt_point_list[p1][2])),
      ship->x_pos + boom_x + (int)(rt_point_list[p2][0]*
        (tcos*rt_point_list[p2][2]-tsin*rt_point_list[p2][1])),
      ship->y_pos - boom_y - (int)(rt_point_list[p2][0]*
        (tcos*rt_point_list[p2][1]+tsin*rt_point_list[p2][2])),
      PIX_SRC, on);
}

void display_object(object, on, high, boom_count)
Object	*object;
int	on;
int	high;
int	boom_count;
{
    Rect	lock_rect;
    /* boom_size contains the angles to rotate the ship vectors by
     * to get a nice explosion
     */

    switch (object->type) {
    case SHIP_T:
	lock_rect.r_left = object->x_pos - 15;
	lock_rect.r_top = object->x_pos - 15;
	lock_rect.r_width = 30;
	lock_rect.r_height = 30;
	pw_lock(canvas_pw, &lock_rect);
	draw_vector(object, 0, 1, on, boom_count*15, 2*boom_count, 2*boom_count);
	draw_vector(object, 1, 2, on, boom_count*(-15), -2*boom_count, 0);
	draw_vector(object, 2, 0, on, boom_count*15, 2*boom_count, -2*boom_count);
	if (high)
	    draw_vector(object, 0, 3, on, boom_count*(-15), 0, 0);
	if (object->flags & THRUSTING)
	    draw_vector(object, 3, 4, on, 0, 0, 0);
	if (object->flags & SHIELD) {
	    draw_vector(object, 17, 18, on, 0, 0, 0);
	    draw_vector(object, 18, 19, on, 0, 0, 0);
	    draw_vector(object, 19, 17, on, 0, 0, 0);
	}
	pw_unlock(canvas_pw);
	break;
    case MISSILE_T:
	pw_rop(canvas_pw, object->x_pos-1, object->y_pos-1, 3, 3,
	       PIX_SRC|PIX_COLOR(on), (struct pixrect *)0, 0, 0);
	break;
    case SUN_T:
	lock_rect.r_left = object->x_pos - 15;
	lock_rect.r_top = object->x_pos - 15;
	lock_rect.r_width = 30;
	lock_rect.r_height = 30;
	pw_lock(canvas_pw, &lock_rect);
	draw_vector(object, 5, 6, on, 0, 0, 0);
	draw_vector(object, 7, 8, on, 0, 0, 0);
	draw_vector(object, 9, 10, on, 0, 0, 0);
	draw_vector(object, 11, 12, on, 0, 0, 0);
	draw_vector(object, 13, 14, on, 0, 0, 0);
	draw_vector(object, 15, 16, on, 0, 0, 0);
	pw_unlock(canvas_pw);
	break;
    case ASTEROID_T:
	lock_rect.r_left = object->x_pos - 15;
	lock_rect.r_top = object->x_pos - 15;
	lock_rect.r_width = 30;
	lock_rect.r_height = 30;
	pw_lock(canvas_pw, &lock_rect);
	draw_vector(object, 20, 21, on, 0, 0, 0);
	draw_vector(object, 21, 22, on, 0, 0, 0);
	draw_vector(object, 22, 23, on, 0, 0, 0);
	draw_vector(object, 23, 24, on, 0, 0, 0);
	draw_vector(object, 24, 25, on, 0, 0, 0);
	draw_vector(object, 25, 26, on, 0, 0, 0);
	draw_vector(object, 26, 20, on, 0, 0, 0);
	pw_unlock(canvas_pw);
	break;
    }
}

void event_proc(win, event, arg)
Window	win;
Event	*event;
caddr_t	arg;
{
    struct header	head;
    
    if (win == (Window)panel)
	window_default_event_proc(win, event, arg);
    if (event_is_ascii(event)) {
	if (event_id(event) == '\033') {
	    head.type = BYE;
	    head.ship_num = ship_num;
	    if (sendto(sock, &head, sizeof(head), 0,
	      &serv_addr, sizeof(serv_addr)) == -1)
		die("Can't send:", 1);
	} else if (event_id(event) == shieldkey)
	    controls.shield = 1;
	else
            controls.fire = 1;
    } else if (event_is_button(event))
	switch(event_id(event)) {
	case MS_LEFT:
	    controls.left = !event_is_up(event);
	    break;
	case MS_MIDDLE:
	    controls.thrust = !event_is_up(event);
	    break;
	case MS_RIGHT:
	    controls.right = !event_is_up(event);
	    break;
	}
    else
	return;
    if (ship_num == -1 || objects[ship_num].flags & DYING)
	return;
    controls.seq++;
    if (sendto(sock, &controls, sizeof(controls), 0,
      &serv_addr, sizeof(serv_addr)) == -1)
	die("Can't send:", 1);
    (void) notify_set_itimer_func(&controls, control_send, ITIMER_REAL,
      &control_timer, ITIMER_NULL);
}

Notify_value control_send(me, which)
char *me;
int which;
{
    struct header	head;

    if (ship_num == -1 || objects[ship_num].flags & DYING)
	return;
    if (control_count++ > 20) {
	die("server not responding?\n", 0);
	head.type = BYE;
	head.ship_num = ship_num;
	if (sendto(sock, &head, sizeof(head), 0,
	  &serv_addr, sizeof(serv_addr)) == -1)
	    die("Can't send:", 1);
    }
    if (sendto(sock, &controls, sizeof(controls), 0,
      &serv_addr, sizeof(serv_addr)) == -1)
	die("Can't send:", 1);
}

Notify_value hello(me, which)
char *me;
int which;
{
    /* kludge to work around apparent Sunview bug */
    panel_paint(cp1, PANEL_CLEAR);
    panel_paint(cp2, PANEL_CLEAR);

    if (hello_count++ > 10)
	die("server not running?\n", 0);
    if (sendto(sock, &hello_head, sizeof(hello_head), 0,
      &serv_addr, sizeof(serv_addr)) == -1)
	die("Can't send:", 1);
    return NOTIFY_DONE;
}

char		input_buf[1500];

Notify_value net_input(me, fd)
char	*me;
int	fd;
{
    int			size;
    struct sockaddr_in	from_addr;
    int			from_len = sizeof(from_addr);
    struct header	*head;
    register int	i;
    register Object	*object;
    int			seq_diff;
    int			new_num_objects;

    size = recvfrom(sock, input_buf, sizeof(input_buf), 0,
      &from_addr, &from_len);
    if (size < 0)
	die("Can't read:", 1);
    if (size >= 2) {
	head = (struct header *)input_buf;
	switch (head->type) {
	/* tell me which ship i am */
	case YOU_ARE:
	    ship_num = head->ship_num;
	    controls.head.ship_num = ship_num;
	    controls.seq = 0;
	    last_ack = 0;
	    control_count = 0;
	    (void) notify_set_itimer_func(&controls, control_send,
	      ITIMER_REAL, ITIMER_NULL, ITIMER_NULL);
	    (void) notify_set_itimer_func(&ship_num, hello, ITIMER_REAL,
	      ITIMER_NULL, ITIMER_NULL);
	    serv_addr = from_addr;
	    break;
	/* update the screen */
	case DISPLAY:
	    /* acknowledged control information */
	    seq_diff = head->ship_num - last_ack;
	    if (seq_diff < 0)
		seq_diff += 256;
	    if (head->ship_num == controls.seq) {
		last_ack = head->ship_num;
		control_count = 0;
		(void) notify_set_itimer_func(&controls, control_send,
		  ITIMER_REAL, ITIMER_NULL, ITIMER_NULL);
		controls.fire = controls.shield = 0;
	    } else if (seq_diff > 0) {
		last_ack = head->ship_num;
		control_count = 0;
		(void) notify_set_itimer_func(&controls, control_send,
		  ITIMER_REAL, &control_timer, ITIMER_NULL);
	    }
	    new_num_objects = (size-sizeof(*head))/sizeof(Object);
	    /* display ships first */
	    for (i = 0, object = (Object *)(input_buf+sizeof(*head));
	      i < MAX_PLAYERS; i++, object++) {
	        if (objects[i].flags & ALIVE)
		    display_object(&objects[i], 0, i==ship_num, die_count[i]);
		if (object->flags & DYING && die_count[i] <= DIE_PERIOD)
		    die_count[i]++;
		else
		    die_count[i] = 0;
		if (object->flags & ALIVE)
		    display_object(object, 1, i == ship_num, die_count[i]);
	    }
	    /* display any extra objects */
	    for (i = MAX_PLAYERS; i < MAX_PLAYERS+OTHER_OBJECTS;
	      i++, object++) {
		if (objects[i].flags & ALIVE)
		    display_object(&objects[i], 0, 0, 0);
		if (object->flags & ALIVE)
		    display_object(object, 1, 0, 0);
	    }
	    /* undisplay old missile positions */
	    for (i = MISSILE_START; i < MISSILE_START+NUM_MISSILES &&
	      i < num_objects; i++) {
	        if (objects[i].flags & ALIVE)
		    display_object(&objects[i], 0, 0, 0);
		else if (i >= MISSILE_START)
		    break;
	    }
	    /* display new missile positions */
	    for (i = MISSILE_START; i < MISSILE_START+NUM_MISSILES &&
	      i < new_num_objects; i++, object++) {
		if (object->flags & ALIVE)
		    display_object(object, 1, 0, 0);
		else if (i >= MISSILE_START)
		    break;
	    }
	    num_objects = new_num_objects;
	    /* save objects to undisplay next update */
	    bcopy(input_buf+sizeof(*head), objects, num_objects*sizeof(Object));
	    if (ship_num != -1 && !(objects[ship_num].flags & ALIVE))
		dead();
	    break;
	/* list of active players */
	case USERS:
	    for (i = 0; i < MAX_PLAYERS; i++) {
		Player	*player = (Player *)(input_buf+
		  sizeof(*head)+sizeof(Player)*i);
		char	score_buf[10];
		
		panel_set(player_names[i],
		  PANEL_LABEL_STRING, &player->name[0], 0);
		if (player->name[0]) {
		    sprintf(score_buf, "%4d", player->score);
		    panel_set(player_scores[i],
		      PANEL_LABEL_STRING, score_buf, 0);
		} else
		    panel_set(player_scores[i], PANEL_LABEL_STRING, "", 0);
	    }
	    break;
	default:
	    break;
	}
    }
    return NOTIFY_DONE;
}

/* ask if the player wants to play again */
void dead()
{
    control_count = 0;
    (void) notify_set_itimer_func(&controls, control_send,
      ITIMER_REAL, ITIMER_NULL, ITIMER_NULL);
    panel_set(play_message, PANEL_SHOW_ITEM, TRUE, 0);
    panel_set(play_yes, PANEL_SHOW_ITEM, TRUE, 0);
    panel_set(play_no, PANEL_SHOW_ITEM, TRUE, 0);
    ship_num = -1;
}

void play_yes_no(item, event)
Panel_item	item;
Event		*event;
{
    if (item == play_no)
	exit(0);
    /* set up to play again */
    panel_set(play_message, PANEL_SHOW_ITEM, FALSE, 0);
    panel_set(play_yes, PANEL_SHOW_ITEM, FALSE, 0);
    panel_set(play_no, PANEL_SHOW_ITEM, FALSE, 0);
    pw_writebackground(canvas_pw, 0, 0, X_MAX, Y_MAX, PIX_SRC);
    hello_count = 0;
    (void) notify_set_itimer_func(&ship_num, hello, ITIMER_REAL,
	&hello_timer, ITIMER_NULL);
    controls.left = controls.right = controls.thrust =
      controls.fire = controls.shield = 0;
}

/* die because of some UNIX error */
void die(message, pperr)
char *message;
int pperr;
{
    fprintf(stderr, message);
    if (pperr)
	perror("spacewar");
    exit(1);
}
\Rogue\Monster\
else
  echo "will not over write ./spacewar.c"
fi
if [ `wc -c ./spacewar.c | awk '{printf $1}'` -ne 18976 ]
then
echo `wc -c ./spacewar.c | awk '{print "Got " $1 ", Expected " 18976}'`
fi
if `test ! -s ./spaceward.c`
then
echo "writting ./spaceward.c"
cat > ./spaceward.c << '\Rogue\Monster\'
/*
 *	Copyright 1987, Jonathan Biggar
 *
 *	Permission is given to copy and distribute for non-profit purposes.
 *
 */

#ifndef lint
static char *rcs = "$header$ Copyright 1986 Jonathan Biggar";
#endif !lint

/*
 * $log$
 */

#include <stdio.h>
#include <sys/types.h>
#include <math.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/uio.h>
#include <netdb.h>
#include <sunwindow/notify.h>

#include "spacewar.h"

#define RANDOM(x)	(random() % x)

/* structure def for dbm package */

typedef struct {
    char	*dptr;
    int		dsize;
} datum;

datum		fetch(), firstkey(), nextkey();

/* internal info about objects not sent to clients */
typedef struct intobject {
    double	x_vel;
    double	y_vel;
    short	fuel;
    short	m_count;
    short	dying;
    short	shield;
} Intobject;

int			sock = 0;

int			client_count = 0;
struct sockaddr_in	clients[MAX_PLAYERS];
Player			players[MAX_PLAYERS];
struct header		player_header = {USERS, 0};
struct iovec		player_vec[2] = {(char *)&player_header,
					 sizeof(player_header),
					 (char *)players, sizeof(players)};
struct msghdr		player_packet = { 0,
					  sizeof(struct sockaddr_in),
					  player_vec, 2, 0, 0};

struct timeval		cycle_time = {0, 0};
unsigned long		cycle = 0;
struct itimerval	die_timer = {0, 0, 10, 0};
struct itimerval	update_timer = {0, UPDATE_FREQ, 0, UPDATE_FREQ};
struct header		update_header = {DISPLAY, 0};
Object			objects[MAX_OBJECTS];
Intobject		intobjects[MAX_OBJECTS];
Controls		controls[MAX_PLAYERS];
unsigned long		control_update[MAX_PLAYERS];
int			next_missile = MISSILE_START;
int			star;
int			gravity_sign;

struct iovec		update_vec[2] = {(char *)&update_header,
					 sizeof(update_header),
					 (char *)objects, sizeof(objects)};
struct msghdr		update_packet = { 0,
					  sizeof(struct sockaddr_in),
					  update_vec, 2, 0, 0};

double			deg_2_rad;
double			sin_tab[360];
double			cos_tab[360];

char			*score_file = SCOREFILE;
int			by_machine = BYMACHINE;

void			die();
Notify_value		update();
Notify_value		read_net();
int			hit();
void			send_names();

long			random();
char			*strcpy(), *index();
struct in_addr		inet_makeaddr();

main(argc, argv)
int argc;
char **argv;
{
    struct hostent	*he;
    struct servent	*se;
    char		hostname[32];
    register int	i;

#ifdef DEBUG
    struct sockaddr_in	my_addr;

    /* if debugging start server listening by hand */
    
    if ((se = getservbyname("spaceward", "udp")) == NULL)
	die();
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = se->s_port;
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
	die();
    if (bind(sock, &my_addr, sizeof(my_addr)) == -1)
	die();
#endif DEBUG

    /* find the broadcast address */
    
    gethostname(hostname, sizeof(hostname));
    if ((he = gethostbyname(hostname)) == NULL)
	die();
    if ((se = getservbyname("spacewar", "udp")) == NULL)
	die();

    /* fill in the sine and cosine tables for speed */
    
    deg_2_rad = acos(-1.0)/180.0;
    for (i = 0; i < 360; i++) {
	sin_tab[i] = sin(i*deg_2_rad);
	cos_tab[i] = cos(i*deg_2_rad);
    }
    
    /* set up the score file */
    
    if (dbminit(score_file) < 0) {
	char cmd_buf[100];
	
	sprintf(cmd_buf, "touch %s.dir %s.pag", SCOREFILE, SCOREFILE);
	system(cmd_buf);
	if (dbminit(score_file) < 0)
	    die();
    }
    
    /* randomize */
    
    srandom(time(0));
    
    /* start main processing */
    (void) notify_set_input_func(&sock, read_net, sock);
    (void) notify_set_itimer_func(die, die, ITIMER_REAL,
      &die_timer, (struct itimerval *)0);
    notify_start();
}

/* go away because of an error */
void die()
{
    char	buf[80];
    
    sprintf(buf, "echo %d %d / %d >>/usr/games/lib/spacewar.times",
	cycle_time.tv_sec, cycle_time.tv_usec, cycle);
    system(buf);
    exit(0);
}

/* decide on the initial configuration of the universe.  Done whenever
 * no players are active and one wants to join 
 */
void startup()
{
    int		i;

    for (i = MAX_PLAYERS; i < MISSILE_START; i++) {
	objects[i].flags &= ~ALIVE;
	intobjects[i].m_count = RANDOM(ASTEROID_MAX_PER);
    }
    if (star) {
	objects[star].flags &= ~ALIVE;
	star = 0;
    }
    if (RANDOM(2)) {
	star = MAX_PLAYERS;
	objects[star].type = SUN_T;
	objects[star].flags |= ALIVE;
	objects[star].x_pos = X_MAX/2;
	objects[star].y_pos = Y_MAX/2;
	objects[star].rot = 0;
	if (RANDOM(2))
	    gravity_sign = 1;
	else
	    gravity_sign = -1;
    }
    (void) notify_set_itimer_func(update, update, ITIMER_REAL,
      &update_timer, (struct itimerval *)0);
    (void) notify_set_itimer_func(die, die, ITIMER_REAL,
      (struct itimerval *)0, (struct itimerval *)0);
}

/* client's ship has been hit, set up for explosion time */
void die_ship(i)
int i;
{
    if (objects[i].flags & DYING)
	return;
    objects[i].flags |= DYING;
    intobjects[i].dying = DIE_PERIOD;
    intobjects[i].shield = 0;
    objects[i].flags &= ~SHIELD;
    controls[i].thrust = controls[i].fire = controls[i].shield = 0;
}

/* client's ship is dead, get rid of client and start shutdown timer if 
 * last client
 */
void kill_ship(i)
int i;
{
    datum	key, content;
    char	buf[40];
    char	*cp;

    if (by_machine)
	strcpy(buf, &players[i].name[0]);
    else {
	cp = index(&players[i].name[0], '@');
	if (cp != NULL)
	    strncpy(buf, &players[i].name[0],
	      cp-&players[i].name[0]+1);
    else
	strcpy(buf, "unknown");
    }
    key.dptr = buf;
    key.dsize = strlen(buf);
    content.dptr = (char *)&players[i].score;
    content.dsize = sizeof(players[i].score);
    store(key, content);
    objects[i].flags &= ~(ALIVE|DYING);
    players[i].name[0] = '\0';
    send_names();
    if (--client_count == 0) {
	(void) notify_set_itimer_func(update, update, ITIMER_REAL,
	  (struct itimerval *)0, (struct itimerval *)0);
	(void) notify_set_itimer_func(die, die, ITIMER_REAL,
	  &die_timer, (struct itimerval *)0);
    }
    
}

/* calculate the stars gravitational influence on an object */
void sun_gravity(object, intobject)
Object		*object;
Intobject	*intobject;
{
    int			tx = object->x_pos - objects[star].x_pos;
    int			ty = object->y_pos - objects[star].y_pos;
    double		dist = tx*tx+ty*ty;
    
    intobject->x_vel += gravity_sign * 4000*tx/pow(dist, 1.5);
    intobject->y_vel += -gravity_sign * 4000*ty/pow(dist, 1.5);
}

/* update the universe */
Notify_value update(me, which)
char *me;
int which;
{
    register int	i;
    register int	j;
    int			missile_offset = 0;
    register Object	*object;
    register Intobject	*intobject;
    struct timeval	start, stop;
    int			last_ship;

    gettimeofday(&start, 0);
    cycle++;
    for (i = 0, object = &objects[0], intobject = &intobjects[0];
      i < MAX_PLAYERS; i++, object++, intobject++)
	/* update the positions of each ship */
	if (object->flags & ALIVE) {
	    last_ship = i;
	    if (cycle - control_update[i] > 1000) {
		kill_ship(i);
		continue;
	    }
	    if (intobject->shield > 0) {
		intobject->shield--;
		if (intobject->shield < SHIELD_RECHARGE - SHIELD_TIME)
		    object->flags &= ~SHIELD;
	    }
	    if (controls[i].shield && intobject->shield == 0) {
		intobject->shield = SHIELD_RECHARGE;
		object->flags |= SHIELD;
	    }
	    controls[i].shield = 0;
	    if (controls[i].left) {
		object->rot += ROT_SPEED;
		object->rot %= 360;
	    }
	    if (controls[i].right) {
		object->rot += 360 - ROT_SPEED;
		object->rot %= 360;
	    }
	    /* check for collisions */
	    for (j = i+1; j < MAX_PLAYERS+OTHER_OBJECTS; j++) {
		int	die_bonus;
		if (!(objects[j].flags & ALIVE))
		    continue;
		switch (objects[j].type) {
		case SHIP_T:
		    die_bonus = ((object->flags&DYING)?
		      DIE_PERIOD-intobject->dying:0) +
		      ((objects[j].flags&DYING)?
		      DIE_PERIOD-intobjects[j].dying:0);
		    if (hit(objects[j].x_pos, objects[j].y_pos,
			  object->x_pos, object->y_pos,
			  (int)(object->x_pos + intobject->x_vel),
			  (int)(object->y_pos - intobject->y_vel),
			  KILL_RADIUS+die_bonus*2)) {
			if (!(object->flags & SHIELD))
			    die_ship(i);
			if (!(objects[j].flags & SHIELD))
			    die_ship(j);
		    }
		    break;
		case SUN_T:
		    if (!(object->flags & SHIELD) &&
		      hit(objects[j].x_pos, objects[j].y_pos,
		      object->x_pos, object->y_pos,
		      (int)(object->x_pos+intobject->x_vel),
		      (int)(object->y_pos-intobject->y_vel), 3)) {
			intobject->x_vel = intobject->y_vel = 0;
			if (!(object->flags & DYING) && players[i].score > 0)
			    players[i].score--;
			die_ship(i);
		    }
		    break;
		case ASTEROID_T:
		    if (!(object->flags & SHIELD) &&
		      hit(objects[j].x_pos, objects[j].y_pos,
		      object->x_pos, object->y_pos,
		      (int)(object->x_pos+intobject->x_vel),
		      (int)(object->y_pos-intobject->y_vel), KILL_RADIUS*2)) {
			if (!(object->flags & DYING) && players[i].score > 0)
			    players[i].score--;
			die_ship(i);
		    }
		    break;
		}
	    }
	    /* move the ship */
	    object->x_pos += (int)intobject->x_vel + X_MAX;
	    object->y_pos += -(int)intobject->y_vel + Y_MAX;
	    object->x_pos %= X_MAX;
	    object->y_pos %= Y_MAX;
	    if (star && !(object->flags & SHIELD))
		sun_gravity(object, intobject);
	    /* if ship finished exploding, kill it */
	    if (object->flags & DYING && !--intobject->dying) {
		kill_ship(i);
		continue;
	    }
	    object->flags &= ~THRUSTING;
	    if (controls[i].thrust) {
		register double		hypot1, hypot2;
		register double		tx, ty;
		
		/* limit thrust to a maximum velocity */
		hypot1 = sqrt(intobject->x_vel*intobject->x_vel+
			      intobject->y_vel*intobject->y_vel);
		tx = intobject->x_vel + mycos(object->rot);
		ty = intobject->y_vel + mysin(object->rot);
		hypot2 = sqrt(tx*tx+ty*ty);
		if (hypot2 <= MAX_VELOCITY || hypot2 <= hypot1) {
		    intobject->x_vel = tx;
		    intobject->y_vel = ty;
		} else {
		    intobject->x_vel = hypot1*tx/hypot2;
		    intobject->y_vel = hypot1*ty/hypot2;
		}
		object->flags |= THRUSTING;
	    }
	    if (controls[i].fire && intobject->m_count < MIS_P_PLAYER) {
		objects[next_missile] = *object;
		objects[next_missile].type = MISSILE_T;
		intobjects[next_missile].fuel = Y_MAX/MISSILE_VELOCITY-1;
		intobjects[next_missile].m_count = i;
		intobjects[next_missile].x_vel = intobject->x_vel +
		  MISSILE_VELOCITY * mycos(object->rot);
		intobjects[next_missile].y_vel = intobject->y_vel +
		  MISSILE_VELOCITY * mysin(object->rot);
		next_missile++;
		intobject->m_count++;
	    }
	    controls[i].fire = 0;
	} else {
	    intobject->dying--;
	}
    for (i = MAX_PLAYERS, object = &objects[i], intobject= &intobjects[i];
      i < MISSILE_START; i++, object++, intobject++)
	if (object->type == SUN_T && object->flags & ALIVE) {
	    objects[star].rot += 30;
	    objects[star].rot %= 360;
	} else {
	    if (object->flags & ALIVE) {
		object->x_pos += intobject->x_vel;
		object->y_pos -= intobject->y_vel;
		object->rot += intobject->fuel + 360;
		object->rot %= 360;
		if (object->x_pos < 0 || object->x_pos >= X_MAX ||
		  object->y_pos < 0 || object->y_pos >= Y_MAX) {
		    object->flags &= ~ALIVE;
		    intobject->m_count =
		      RANDOM(ASTEROID_MAX_PER-ASTEROID_MIN_PER) +
		      ASTEROID_MIN_PER;
		    continue;
		}
		if (star)
		    sun_gravity(object, intobject);
	    } else if (--intobject->m_count == 0) {
		object->type = ASTEROID_T;
		object->flags |= ALIVE;
		object->rot = 0;
		intobject->fuel = RANDOM(30) - 15;
		switch (RANDOM(4)) {
		case 0:
		    object->x_pos = 0;
		    object->y_pos = RANDOM(Y_MAX);
		    intobject->x_vel = RANDOM(10)+1;
		    intobject->y_vel = -RANDOM(10)+5;
		    break;
		case 1:
		    object->x_pos = X_MAX-1;
		    object->y_pos = RANDOM(Y_MAX);
		    intobject->x_vel = -RANDOM(10)+1;
		    intobject->y_vel = -RANDOM(10)+5;
		    break;
		case 2:
		    object->y_pos = 0;
		    object->x_pos = RANDOM(X_MAX);
		    intobject->y_vel = -RANDOM(10)+1;
		    intobject->x_vel = RANDOM(10)-5;
		    break;
		case 3:
		    object->y_pos = Y_MAX-1;
		    object->x_pos = RANDOM(X_MAX);
		    intobject->y_vel = RANDOM(10)+1;
		    intobject->x_vel = RANDOM(10)-5;
		    break;
		}
	    }
	}
    for (i = MISSILE_START, object = &objects[i], intobject = &intobjects[i];
      i < MISSILE_START+NUM_MISSILES; i++, object++, intobject++)
        /* update each missile */
	if (object->flags & ALIVE) {
	    if (--intobject->fuel > 0) {
		for (j = 0; j < MAX_PLAYERS+OTHER_OBJECTS; j++)
		    if (objects[j].flags & ALIVE &&
		        (intobject->m_count != j ||
		        intobject->fuel != Y_MAX/MISSILE_VELOCITY - 2) &&
			hit(objects[j].x_pos, objects[j].y_pos,
		            object->x_pos, object->y_pos,
		            (int)(object->x_pos + intobject->x_vel),
		            (int)(object->y_pos - intobject->y_vel),
		            KILL_RADIUS)) {
			object->flags &= ~ALIVE;
			if (objects[j].type != SHIP_T)
			    continue;
			if (intobject->m_count != j &&
			  !(objects[j].flags & (DYING|SHIELD)))
			    if (players[intobject->m_count].score >
			      players[j].score)
				players[intobject->m_count].score++;
			    else
				players[intobject->m_count].score += 
				  (players[j].score-
				   players[intobject->m_count].score)/10+1;
			if (!(objects[j].flags & SHIELD))
			    die_ship(j);
		    }
		/* move the missile */
		object->x_pos += (int)intobject->x_vel + X_MAX;
		object->y_pos += -(int)intobject->y_vel + Y_MAX;
		object->x_pos %= X_MAX;
		object->y_pos %= Y_MAX;
		if (star)
		    sun_gravity(object, intobject);
		/* if the missile hit something, remove it */
		if (!(object->flags & ALIVE)) {
		    intobjects[intobject->m_count].m_count--;
		    missile_offset++;
		} else if (missile_offset > 0) {
		    /* compact the missile list */
		    objects[i-missile_offset] = *object;
		    intobjects[i-missile_offset] = *intobject;
		    object->flags &= ~ALIVE;
		}
	    /* missile ran out of fuel */
	    } else {
	        object->flags &= ~ALIVE;
		intobjects[intobject->m_count].m_count--;
		missile_offset++;
	    }
	/* end of missile list */
	} else {
	    next_missile = i - missile_offset;
	    break;
	}
    update_vec[1].iov_len = (&objects[next_missile]-objects)*sizeof(Object);
    for (i = 0; i <= last_ship; i++)
	if (objects[i].flags & ALIVE || intobjects[i].dying > -300) {
	    update_packet.msg_name = (caddr_t) &clients[i];
	    update_header.ship_num = controls[i].seq;
	    if (sendmsg(sock, &update_packet, 0) == -1)
		die();
	}
    gettimeofday(&stop, 0);
    if (start.tv_sec == stop.tv_sec)
	cycle_time.tv_usec += stop.tv_usec - start.tv_usec;
    else
	cycle_time.tv_usec += 1000000 + stop.tv_usec - start.tv_usec;
    if (cycle_time.tv_usec > 1000000) {
	cycle_time.tv_usec -= 1000000;
	cycle_time.tv_sec += 1;
    }
    return NOTIFY_DONE;
}

/* this macro calculates the distance between two points, taking into account
 * wrap-around 
 */

#define DISTANCE(x1, x2, y1, y2, r) \
    delx = abs(x1 - x2); \
    dely = abs(y1 - y2); \
    if (delx > X_MAX/2) \
	delx = X_MAX - delx; \
    if (dely > Y_MAX/2) \
	dely = Y_MAX - dely; \
    r = delx * delx + dely * dely;

/* see if a moving object hit another one:
 * stationary object at sx, sy,
 * moving object moving from mx1, my1, to mx2, my2,
 * kill radius is kill
 */
int hit(sx, sy, mx1, my1, mx2, my2, kill)
int sx, sy, mx1, my1, mx2, my2, kill;
{
    register int	a2, b2, c2;
    register int	delx, dely;
    register double	a;
    register double	b;
    double		range1;
    double		range2;
    
    DISTANCE(sx, mx1, sy, my1, a2);
    DISTANCE(mx1, mx2, my1, my2, b2);
    DISTANCE(mx2, sx, my2, sy, c2);
    if (a2 == 0 || c2 == 0)
	return 1;
    a = sqrt((double)a2);
    b = sqrt((double)b2);
    range2 = (double)(a2 + b2 - c2) / (2 * a * b);
    range1 = a*sqrt(1-range2*range2);
    range2 *= a;
    return range1 < kill && range2 > 0 && range2 < b;
}

char	input_buf[1500];

Notify_value read_net(me, fd)
char	*me;
int	fd;
{
    int			size;
    struct sockaddr_in	from_addr;
    int			from_size = sizeof(from_addr);
    struct header	*head;
    register int	i;
    
    size = recvfrom(sock, input_buf, sizeof(input_buf), 0,
      &from_addr, &from_size);
    if (size < 0)
	die();
    if (size >= 2) {
	head = (struct header *)input_buf;
	switch (head->type) {
	/* someone wants to play */
	case HELLO:
	    for (i = 0; i < MAX_PLAYERS; i++) {
		struct header	you_are;
		datum		key, content;
		char		*cp;
		char		buf[40];
		
		if ((objects[i].flags & ALIVE) &&
		  bcmp(&from_addr, &clients[i], sizeof(from_addr)))
		    continue;
		if (!(objects[i].flags & ALIVE) && ++client_count == 1)
		    startup();
		clients[i] = from_addr;
		you_are.type = YOU_ARE;
		you_are.ship_num = i;
		if (sendto(sock, &you_are, 2, 0,
		  &clients[i], sizeof(clients[i])) == -1)
		    die();
		strcpy(&players[i].name[0], input_buf+sizeof(*head));
		if (by_machine)
		    strcpy(buf, &players[i].name[0]);
		else {
		    cp = index(&players[i].name[0], '@');
		    if (cp != NULL)
			strncpy(buf, &players[i].name[0],
			  cp-&players[i].name[0]+1);
		    else
			strcpy(buf, "unknown");
		}
		key.dptr = buf;
		key.dsize = strlen(buf);
		content = fetch(key);
		if (content.dptr != 0)
		    players[i].score = *(int *)content.dptr;
		else
		    players[i].score = 0;
		send_names();
		if (objects[i].flags & ALIVE)
		    break;
		/* initialize his ship */
		objects[i].flags |= ALIVE;
		objects[i].flags &= ~DYING;
		objects[i].x_pos = RANDOM(X_MAX);
		objects[i].y_pos = RANDOM(Y_MAX);
		objects[i].rot = RANDOM(360/ROT_SPEED)*ROT_SPEED;
		controls[i].left = controls[i].right = controls[i].thrust
		  = controls[i].fire = 0;
		intobjects[i].x_vel = intobjects[i].y_vel = 0;
		control_update[i] = cycle;
		break;
	    }
	    break;
	/* someone is maneuvering */
	case CONTROLS:
	    {
		u_char		fire = controls[head->ship_num].fire;
		u_char		shield = controls[head->ship_num].shield;
		int		seq_dif = ((Controls *)input_buf)->seq -
					  controls[head->ship_num].seq;

		if (seq_dif < 0)
		    seq_dif += 256;
		if (!(objects[head->ship_num].flags & DYING) && seq_dif > 0)
		    controls[head->ship_num] = *(Controls *)input_buf;
		controls[head->ship_num].fire |= fire;
		controls[head->ship_num].shield |= shield;
		control_update[head->ship_num] = cycle;
	    }
	    break;
	/* someone is going away */
	case BYE:
	    objects[head->ship_num].flags |= DYING;
	    intobjects[head->ship_num].dying = 1;
	    break;
	default:
	    break;
	}
    }
    return NOTIFY_DONE;
}

/* update the list of players */
void send_names()
{
    int i;

    for (i = 0; i < MAX_PLAYERS; i++)
	if (objects[i].flags & ALIVE) {
	    player_packet.msg_name = (caddr_t) &clients[i];
	    if (sendmsg(sock, &player_packet, 0) == -1)
        	die();
	}
}
\Rogue\Monster\
else
  echo "will not over write ./spaceward.c"
fi
if [ `wc -c ./spaceward.c | awk '{printf $1}'` -ne 18646 ]
then
echo `wc -c ./spaceward.c | awk '{print "Got " $1 ", Expected " 18646}'`
fi
if `test ! -s ./mathfix`
then
echo "writting ./mathfix"
cat > ./mathfix << '\Rogue\Monster\'
s/jbsr	_fabs/fabsd	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_acos/facosd	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_asin/fasind	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_atan/fatand	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_cos/fcosd	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_cosh/fcoshd	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_exp/fetoxd	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_exp1/fetoxm1d	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_log/fmoved	sp@,fp0\
	fcmps	#0r0.5,fp0\
	fjle	1f\
	fsubl	#1,fp0\
	flognp1x fp0,fp0\
	bras	2f\
1:	flognx	fp0,fp0\
2:	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_log1/flognp1	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_log10/flog10d	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_log2/flog2	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_pow10/ftentox	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_pow2/ftwotox	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_sin/fsind	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_sinh/fsinhd	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_sqrt/fsqrtd	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_tan/ftand	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
s/jbsr	_tanh/ftanhd	sp@,fp0\
	fmoved	fp0,sp@\
	movl	sp@,d0\
	movl	sp@(4),d1/
\Rogue\Monster\
else
  echo "will not over write ./mathfix"
fi
if [ `wc -c ./mathfix | awk '{printf $1}'` -ne 1555 ]
then
echo `wc -c ./mathfix | awk '{print "Got " $1 ", Expected " 1555}'`
fi
echo "Finished archive 1 of 1"
exit