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