1 /*	Xasteroids
2 	Copyright 1990 by Phil Goetz
3 	goetz@cs.buffalo.EDU
4 	Version 5, 9 Feb 93
5 
6 	Changes after version 5.0:
7 
8 		Improved cursor-hiding.
9 		Arrow keys.
10 		ANSI-C cleanups.
11 		Use usleep() instead of a delay loop, when available.
12 
13 	Changes from version 4.3:
14 
15 		High score script.
16 		Enemy moves towards you at higher levels.
17 
18 	Changes from version 3:
19 
20 		Better collision detection:  Actually checks for intersection
21 			of line segments if non-round objects are very close.
22 		Explosions!  (Thanks to Peter Phillips.)
23 		Fine rotation repeating detected using KeyRelease.
24 		Thrust indicator behind ship.  (Thanks to Peter Phillips.)
25 		Doesn't place ship in center of screen for new levels.
26 		Seeds random-number generator with time.
27 			(Thanks to Craig Smith.)
28 		Shields!  (Thanks to Peter Phillips.  I rewrote the bounce code
29 			& added the energy bar & other refinements.)
30 		Solved won't-take-input bug. (Thanks to Doug Merritt.)
31 		Detects failure of XOpenDisplay. (Thanks to Pat Ryan.)
32 
33 	Contributors:	Peter Phillips <pphillip@cs.ubc.ca>
34 			Pat Ryan <pat@jaameri.gsfc.nasa.gov>
35 			Craig Smith <csmith@cscs.UUCP>
36 			Doug Merritt <doug@netcom.com>
37 			Stephen McCamant <alias@mcs.com>
38 			Makefile by James Berg <berg@plains>
39 			Hi score script by Chris Moore <moore@src.bae.co.uk>
40 			Man page by David Partain <dpl@ida.liu.se>
41 */
42 #include <stdio.h>		/* For NULL	*/
43 #include <stdlib.h>		/* For malloc(), rand() etc */
44 #include <string.h>		/* For srtlen() */
45 #include <time.h>		/* For time() */
46 #include <X11/Xlib.h>
47 #include <X11/Xutil.h>
48 #include <X11/keysym.h>
49 #include <math.h>
50 
51 #ifdef HAS_USLEEP
52 #include <unistd.h>
53 #endif
54 
55 #ifndef __STDC__
56 define void
57 #endif
58 
59 #define rand(rndint) rand() /* ??? */
60 
61 /* Indexes for 1st dimension of obj	*/
62 /* The order they are in is important	*/
63 #define	AST	0
64 #define	ENEMY	96
65 #define ENEMYBUL 97
66 #define	FBUL	98
67 #define	LASTBUL	102
68 #define	SHIP	103
69 #define LASTOBJ 103	/* Must be ship!  See makeasts().	*/
70 
71 /* Shapes	*/
72 /* Order is important!  See collide().	*/
73 #define	ASTSHAPE1	0
74 #define ASTSHAPE2	1
75 #define ASTSHAPE3	2
76 #define ENBULSH		3
77 #define	BULSHAPE	4
78 #define	SHIPSHAPE	5
79 #define SHIPTHRSHAPE	6
80 #define	ENEMYSHAPE	7
81 #define LASTSHAPE	7
82 
83 /* Masses	*/
84 #define M_BIG	8.0
85 #define M_MED	4.0
86 #define M_SMALL	1.0
87 #define M_SHIP	1.5
88 #define M_ENEMY	1.0
89 #define M_BULLET 0.1
90 
91 /* Keys		*/
92 #define FIRE		XK_p
93 #define PAUSE		XK_Escape	/* escape	*/
94 #define SHIELD		XK_grave	/* ` */
95 #define THRUST		XK_o
96 
97 #define BMAX		300	/* Max particles in a "boom" + 1	*/
98 #define letheight	20	/* height of font	*/
99 #define	pi		3.1415926535897932384
100 #define SHIPSIZE	28
101 
102 typedef struct _Boom *Boom;
103 struct _Boom {Boom next; int dur, part; double bcoord[BMAX][2], bvec[BMAX][2];};
104 typedef struct {int shape, alive, time;
105 		double mass, x, y, xvel, yvel, rot, rotvel;} Objtype;
106 typedef struct {double angle; int length;} PolarPair;
107 typedef struct {double x, y, mag;} Vector;
108 
109 /* Global variables:	*/
110 Objtype	obj[SHIP+1];
111 /*	In shapes pairs, 1st # is radians, 2nd is length in pixels.
112 	Degrees: 0 ->, pi/2 down, pi <-, 3*pi/2 up
113 	IF YOU CHANGE THE SHAPES, you MUST change numpairs & shapesize
114 */
115 PolarPair shapes[LASTSHAPE+1][11] =
116 	{	{{0,0}, {3*pi/2,40}, {0,20}, {pi/4,28}, {pi/2,40}, /* just crossed 0-deg line */
117 		 {3*pi/4,28},{pi,40},{5*pi/4,28},{3*pi/2,40},{7*pi/4,28},{0,20}},
118 /*	hexagon if you prefer
119 		{{0,0}, {3*pi/2, 20}, {pi/6, 20}, {pi/2, 20},
120                  {5*pi/6, 20}, {7*pi/6, 20}, {3*pi/2, 20}, {11*pi/6, 20}},
121 */
122 		{{0,0}, {3*pi/2,20}, {0,10}, {pi/4,14}, {pi/2,20},
123 		 {3*pi/4,14},{pi,20},{5*pi/4,14},{3*pi/2,20},{7*pi/4,14},{0,10}},
124 		{{0,0}, {3*pi/2,10}, {0,5}, {pi/4,7}, {pi/2,10},
125 		 {3*pi/4,7},{pi,10},{5*pi/4,7},{3*pi/2,10},{7*pi/4,7},{0,5}},
126 		{{0,0}, {7*pi/4, 4}, {pi/4, 4}, {3*pi/4, 4}, {5*pi/4, 4}},
127 		{{0,0}, {0,10}},
128 		{{0,0}, {5*pi/4,28}, {0,20}, {pi/4,28},{3*pi/4,28},{pi,20},{7*pi/4,28}},	/* Ship */
129 		{{0,0}, {5*pi/4,28}, {0,20}, {pi/4,28},{3*pi/4,28},{pi,20},
130 		 {7*pi/4,28}, {3*pi/4, 7}, {9*pi/8, 13}, {15*pi/8, 13}},	/* Thrusting ship */
131 		{{0,0}, {pi,20},{7*pi/4,28},{pi/4,28},{pi,20}}
132 	};
133 Boom	blist = NULL;
134 double	drawscale = 1, speedscale = 1;
135 int	width, height,
136 	energy,		/* # of turns shield is good for	*/
137 	highscore = 0, level,
138 	nextbul = FBUL,			/* Obj# of next bullet fired	*/
139 	numasts, oldscore = 99,
140 	rndint = 73, ships, score,
141 	numpairs[LASTSHAPE+1]	= {11, 11, 11, 5, 2, 7, 10, 5},
142 	shapesize[LASTSHAPE+1]	= {44, 21, 10, 2, 1, SHIPSIZE+1, 35, 20},
143 	shield_on;
144 
145 void
initasts()146 initasts()
147 {	int i;
148 	extern Objtype obj[SHIP+1];
149 
150 	for (i = 0; i < LASTOBJ+1; i++)
151 	{	obj[i].rot = 0;
152 		obj[i].rotvel = 0;
153 	}
154 	for (i = 0; i < ENEMY; i++)
155 	{	obj[i].shape = ASTSHAPE1;
156 	}
157 	obj[SHIP].shape = SHIPSHAPE;
158 	obj[SHIP].mass = M_SHIP;
159 	obj[ENEMY].shape = ENEMYSHAPE;
160 	obj[ENEMY].mass = M_ENEMY;
161 	obj[ENEMYBUL].shape = ENBULSH;
162 	obj[ENEMYBUL].mass = M_BULLET;
163 	for (i = FBUL; i < LASTBUL+1; i++)
164 	{	obj[i].shape = BULSHAPE;
165 		obj[i].mass = M_BULLET;
166 }	}
167 
168 void
makeasts()169 makeasts()
170 {	int i;
171 	extern Objtype obj[SHIP+1];
172 	extern int level, numasts, rndint;
173 	extern double speedscale;
174 	unsigned char a;
175 
176 	for (i = 0; i < SHIP; i++)
177 		obj[i].alive = 0; /* Erase objs from last level except ship */
178 	for (i = ENEMYBUL; i < LASTBUL+1; i++)
179 		obj[i].time = 0;		/* No bullets in the air */
180 	for (i = 0; i < ((level > 8) ? 8 : level+4); i++)	/* Asteroids:	*/
181 	{	a = rand(rndint); a>>=1;	/* a = rand# from 0 to 127 */
182 		if (a > 63)
183 			obj[i].x = (double) a;
184 			else obj[i].x = (double) (width - a);
185 		a = rand(rndint); a>>=1;	/* Now the same for y	*/
186 		if (a >  63)
187 			obj[i].y = (double) a;
188 			else obj[i].y = (double) height - a;
189 		a = rand(rndint); a = 4 - (a>>5);
190 		obj[i].rot = (double) a;
191 		a = rand(rndint);
192 		obj[i].rotvel = ((double) a)/2048;
193 		a = rand(rndint);
194 		obj[i].xvel = cos((double) a);
195 		obj[i].yvel = sin((double) a);
196 		obj[i].shape = ASTSHAPE1;
197 		obj[i].mass = M_BIG;
198 		obj[i].alive = 1;
199 	}
200 	numasts = i;
201 }
202 
203 void
makeenemy()204 makeenemy()	/* Start an enemy ship	*/
205 {	extern Objtype obj[SHIP+1];
206 	extern int height, level, rndint;
207 	unsigned char c;
208 
209 	obj[ENEMY].alive = 1;
210 	obj[ENEMY].x = 0;
211 	obj[ENEMY].y = (double) height/4;
212 	c = rand(rndint); obj[ENEMY].y += (double) c; /* May put enemy outside window	*/
213 	obj[ENEMY].xvel = .4 + ((double) level)/3.;
214 	obj[ENEMY].yvel = 0;
215 	obj[ENEMY].rot = 0;
216 	obj[ENEMY].rotvel = 0;
217 }
218 
nextast()219 int nextast()	/* Find next unused asteroid object	*/
220 {	extern Objtype obj[SHIP+1];
221 	int i;
222 	for (i = 0; obj[i].alive; i++);	/* guaranteed to find one	*/
223 	return i;
224 }
225 
collide(i,j)226 int collide(i, j)	/* Returns non-zero if i collided with j	*/
227 			/* Ship must be j!  (See below)			*/
228 	int i, j;
229 {	extern Objtype obj[SHIP+1];
230 	extern int shapesize[LASTSHAPE+1];
231 	extern double drawscale;
232 	double	mi, mj,				/* Slopes of lines	*/
233 		ix1, ix2, iy1, iy2, jx1, jx2, jy1, jy2,	/* Endpoints	*/
234 		roti, rotj,
235 		xcross,	ycross,		/* coord of intersection	*/
236 		z;
237 	int	diff, xd, yd,
238 		a, b,
239 		shapei, shapej;
240 	xd = obj[i].x - obj[j].x;
241 	yd = obj[i].y - obj[j].y;
242 	diff = sqrt((double)(xd*xd + yd*yd));
243 	shapei = obj[i].shape; shapej = obj[j].shape;
244 	/* Note this will miss if drawscale is < 0	*/
245 	if (diff < (shapesize[shapei] + shapesize[shapej])*drawscale)
246 	{   /* If both are round objects, approximation is good */
247 	    if (shapei < SHIPSHAPE && shapej < SHIPSHAPE) return 1;
248 	    if (j == SHIP && shield_on) return 1;	/* Ship always j! */
249 	    roti = obj[i].rot; rotj = obj[j].rot;
250 	    ix1 = (double) obj[i].x; iy1 = (double) obj[i].y;
251 	    for (a = 1; a < numpairs[shapei]; a++)
252 	    {	ix2 = ix1 + (double) shapes[shapei][a].length * drawscale *
253 			cos(shapes[shapei][a].angle + roti);
254 		iy2 = iy1 + (double) shapes[shapei][a].length * drawscale *
255 			sin(shapes[shapei][a].angle + roti);
256 		if (ix1 == ix2)
257 		{	printf("\nif1 = if2"); return 1;} /* Easy way out */
258 		mi = (iy2-iy1)/(ix2-ix1);
259 		z = mi*ix1;
260 		jx1 = (double) obj[j].x; jy1 = (double) obj[j].y;
261 		for (b = 1; b < numpairs[shapej]; b++)
262 		{	jx2 = jx1 + (double) shapes[shapej][b].length *
263 				drawscale * cos(shapes[shapej][b].angle + rotj);
264 			jy2 = jy1 + (double) shapes[shapej][b].length *
265 				drawscale * sin(shapes[shapej][b].angle + rotj);
266 			if (jx1 == jx2)
267 			{	ycross = ix1 + (jx1-ix1)*mi;
268 				if ((jx1-ix1) * (ix2-jx1) >= 0 &&
269 					(ycross-jy1)*(jy2-ycross) >= 0)
270 					return 1;
271 				else jx2 += .0001;	/* Avoid divide by 0 */
272 			}
273 			mj = (jy2-jy1)/(jx2-jx1);
274 			if (mj == mi) goto Loopend;	/* Parallel lines */
275 			xcross = (iy1 - jy1 + mj*jx1 - z) / (mj - mi);
276 			if ((xcross-ix1) * (ix2-xcross) > 0
277 				&& (xcross-jx1) * (jx2-xcross) > 0) return 1;
278 Loopend:		jx1 = jx2; jy1 = jy2;
279 		}
280 		ix1 = ix2; iy1 = iy2;
281 	}   }
282 	return 0;
283 }
284 
285 void
blastpair(i,j)286 blastpair(i, j)		/* Generate random velocity vector v.	*/
287 	int i, j ;	/* Add v to i, -v to j.			*/
288 {	extern int rndint;
289 	extern Objtype obj[SHIP+1];
290 	unsigned char c;	/* for rand	*/
291 	double vx, vy;
292 	c = rand(rndint);
293 /*	c = 4 - c>>5;	if you need angles from -3 to 4		*/
294 	c >>= 2;		/* possibly save some time on sin/cos	*/
295 	vx = cos((double) c); vy = sin((double) c);
296 	obj[i].xvel = obj[i].xvel + vx;
297 	obj[i].yvel = obj[i].yvel + vy;
298 	obj[j].xvel = obj[j].xvel - vx;
299 	obj[j].yvel = obj[j].yvel - vy;
300 	obj[i].rotvel = obj[i].rotvel + .05;
301 	obj[j].rotvel = obj[j].rotvel - .05;
302 }
303 
304 /* dot product of 2 vectors	*/
305 #define dot(i,j)	(i.x*j.x + i.y*j.y)
306 /* rotational inertia (constant eliminated) of obj. i	*/
307 #define rotinert(i)	(double) (obj[i].mass*shapesize[obj[i].shape]*shapesize[obj[i].shape])
308 
309 /* cause two objects to collide elastically	*/
310 void
bounce(i,j)311 bounce(i, j)
312 int	i,j;
313 {
314 double	rotrat, temp;
315 extern	Objtype obj[SHIP+1];
316 Vector	vi, vj,		/* velocity of objs i, j		*/
317 	ij,		/* vector from center of i to center of j */
318 	vi_ij, vj_ij,	/* velocity of obj along vector ij	*/
319 	vipij, vjpij,	/* velocity perpendicular to ij		*/
320 	vi_ijf, vj_ijf;	/* post-collision velocity along ij	*/
321 
322 vi.x = obj[i].xvel; vi.y = obj[i].yvel;
323 vj.x = obj[j].xvel; vj.y = obj[j].yvel;
324 ij.x = obj[j].x - obj[i].x; ij.y = obj[j].y - obj[i].y;
325 ij.mag = sqrt(ij.x*ij.x + ij.y*ij.y);
326 /*
327 Calculate velocities projected onto ij;
328 	vi_ij = vi*cos(a) = (vi dot ij) / |ij|		*/
329 vi_ij.mag = dot(vi, ij) / ij.mag;
330 vi_ij.x = (ij.x * vi_ij.mag) / ij.mag;
331 vi_ij.y = (ij.y * vi_ij.mag) / ij.mag;
332 vj_ij.mag = dot(vj, ij) / ij.mag;
333 vj_ij.x = (ij.x * vj_ij.mag) / ij.mag;
334 vj_ij.y = (ij.y * vj_ij.mag) / ij.mag;
335 if (vi_ij.mag - vj_ij.mag < 0)	/* Objs moving away from each other -
336 	Since objs are round (at least when bouncing), this means
337 	they are moving away from each other already.	*/
338 	return;
339 /*
340 Calculate component of velocities perpendicular to ij:
341 	|vipij| = |vi|*sin(a) = |vi x ij| / |ij|
342 Same as
343 	|vipij| = |vi|*cos(pi/2 - a) = (vi dot (perp. to ij)) / |ij|	*/
344 temp = vi.y*ij.x - vi.x*ij.y;	/* - (cross product when 3rd coord is 0)*/
345 temp /= (ij.mag*ij.mag);
346 vipij.x = -ij.y*temp; vipij.y = ij.x*temp;
347 temp = (vj.x*ij.y - vj.y*ij.x) / (ij.mag*ij.mag);
348 vjpij.x = -ij.y*temp; vjpij.y = ij.x*temp;
349 /*
350 Calculate the linear elastic collision along ij:
351 	mass(i)*vi_ij + mass(j)*vj_ij = mass(i)*vi_ijf + mass(j)*vj_ijf
352 	vi_ij + vi_ijf = vj_ij + vj_ijf	(derived by dividing equation
353 	for conservation of kinetic energy by eq. for cons. of momentum) */
354 temp = obj[i].mass/obj[j].mass;
355 vj_ijf.x = (temp * (2*vi_ij.x - vj_ij.x) + vj_ij.x) / (1 + temp);
356 vj_ijf.y = (temp * (2*vi_ij.y - vj_ij.y) + vj_ij.y) / (1 + temp);
357 vi_ijf.x = vj_ijf.x + vj_ij.x - vi_ij.x;
358 vi_ijf.y = vj_ijf.y + vj_ij.y - vi_ij.y;
359 /*
360 Now, given vi_ijf and vj_ijf, add them to the perpendicular
361 	components to get final velocity vectors		*/
362 obj[i].xvel = vi_ijf.x + vipij.x;
363 obj[i].yvel = vi_ijf.y + vipij.y;
364 obj[j].xvel = vj_ijf.x + vjpij.x;
365 obj[j].yvel = vj_ijf.y + vjpij.y;
366 /*
367 Now calculate rotational velocity exchange	*/
368 rotrat = rotinert(i)/rotinert(j);
369 temp = rotrat * (2*obj[i].rotvel - obj[j].rotvel) / (1+rotrat);
370 obj[i].rotvel = temp + obj[j].rotvel - obj[i].rotvel;
371 obj[j].rotvel = temp;
372 }
373 
374 void
botline(disp,window,gc)375 botline(disp, window, gc)	/* Print status line text	*/
376 	Display *disp;
377 	Drawable window;
378 	GC gc;
379 {	extern int highscore, ships, score;
380 	char text[70];
381 	sprintf(text, "Ships:%2d   Score:%6d   Shield:        High:%6d",
382 		ships, score, highscore);
383 	XDrawImageString (disp, window, gc, 0, height+letheight,
384 			text, strlen(text));
385 }
386 
387 void
printss(disp,window,gc)388 printss(disp, window, gc)	/* Print ships and score	*/
389 	Display *disp;
390 	Drawable window;
391 	GC gc;
392 {	extern int height, highscore, oldscore, ships, score;
393 	extern Objtype obj[SHIP+1];	/* to kill ship	*/
394 	char sstring[30];
395 
396 	if (score != oldscore)
397 	{	if (score/10000 > oldscore/10000)
398 		{	ships++; botline(disp, window, gc);}
399 		if (score/10000 < oldscore/10000)
400 		{	ships--; botline(disp, window, gc);
401 			if (!ships) obj[SHIP].alive = 0;
402 		}
403 		if (score > highscore)	/* Separate if to avoid flashing */
404 		{	highscore = score;
405 			sprintf(sstring, "%6d", highscore);
406 			XDrawImageString (disp, window, gc, 460,
407 				height+letheight, sstring, strlen(sstring));
408 		}
409 		sprintf(sstring, "%6d", score);
410 		XDrawImageString (disp, window, gc, 170, height+letheight,
411 				sstring, strlen(sstring));
412 		oldscore = score;
413 	}
414 
415 	/* Draw shield energy bar	*/
416 	XFillRectangle(disp, window, gc, 340, height+8, energy>>1, 10);
417 	XClearArea(disp, window, 340+(energy>>1), height+8, 8, 10, False);
418 }
419 
420 void
upscore(killer,up)421 upscore(killer, up)	/* Only award score for things the player shot */
422 	int killer, up;
423 {	extern int score;
424 	if (killer != ENEMYBUL && killer != SHIP)
425 		score = score + up;
426 }
427 
428 /* boom, movebooms, drawbooms all by Peter Phillips */
429 void
boom(ob,particles,duration)430 boom(ob, particles, duration)
431 int ob;
432 int particles;
433 int duration;
434 { extern int rndint;
435   int i;
436   unsigned int r1, r2;
437   Boom b;
438   double x, y;
439   double angle, length;
440 
441   b = (Boom) malloc(sizeof(struct _Boom));
442   b->dur = duration;
443   b->part = particles;
444   x = obj[ob].x;
445   y = obj[ob].y;
446   for (i = 0; i < particles; i++) {
447     r1 = (rand(rndint) >> 2) % 100;
448     r2 = (rand(rndint) >> 2) % 7;
449 
450     b->bcoord[i][0] = x;
451     b->bcoord[i][1] = y;
452     angle = r1 * pi / 50.0;
453     length = 3 + r2;
454     b->bvec[i][0] = cos(angle) * length + obj[ob].xvel;
455     b->bvec[i][1] = sin(angle) * length + obj[ob].yvel;
456   }
457   b->next = blist;
458   blist = b;
459 }
460 
461 /* move the various booms that are active */
462 void
movebooms()463 movebooms()
464 {
465   int i;
466   Boom b, prevb;
467 
468   prevb = NULL;
469   b = blist;
470   while (b != NULL) {
471     b->dur--;
472     if (b->dur < 0) { /* delete this boom */
473       Boom temp;
474 
475       temp = b;
476       if (prevb == NULL) {
477         blist = b->next;
478       } else {
479         prevb->next = b->next;
480       }
481       b = b->next;
482       free(temp);
483     } else {  /* move boom, advance list */
484       for (i = 0; i < b->part; i++) {
485         b->bcoord[i][0] += b->bvec[i][0];
486         b->bcoord[i][1] += b->bvec[i][1];
487       }
488       prevb = b;
489       b = b->next;
490     }
491   }
492 }
493 
494 /* Draw the various booms */
495 void
drawbooms(disp,window,gc)496 drawbooms(disp, window, gc)
497      Display *disp;
498      Drawable window;
499      GC gc;
500 {
501   int i;
502   Boom b;
503   XPoint figure[BMAX];
504 
505   b = blist;
506   while (b != NULL) {
507     for (i = 0; i < b->part; i++) {
508       figure[i].x = (int) b->bcoord[i][0];
509       figure[i].y = (int) b->bcoord[i][1];
510     }
511     XDrawPoints(disp, window, gc, figure, b->part, CoordModeOrigin);
512     b = b->next;
513   }
514 }
515 
516 void
deletebooms()517 deletebooms()	/* delete all booms */
518 {	Boom b;
519 
520 	b = blist;
521 	while (b != NULL)
522 	{	b->dur = 0;
523 		b = b->next;
524 }	}
525 
526 void
killast(killer,i)527 killast(killer, i)
528 	int killer, i;		/* i = Asteroid # to kill	*/
529 {	extern Objtype obj[SHIP+1];
530 	extern int numasts;
531 	int k, na = 0, oldna;
532 
533 	if (obj[i].shape == ASTSHAPE1)
534 	{	na = nextast();		/* Could put 6 lines in a sub */
535 		obj[na].x = obj[i].x;
536 		obj[na].y = obj[i].y;
537 		obj[na].xvel = obj[i].xvel;
538 		obj[na].yvel = obj[i].yvel;
539 		obj[na].alive++;
540 		obj[na].shape = ASTSHAPE2;
541 		obj[na].mass = M_MED;
542 		obj[i].shape = ASTSHAPE2;
543 		obj[i].mass = M_MED;
544 		blastpair(i, na);
545 		boom(i, 30, 12);
546 		numasts++;
547 		if (numasts == 2)	/* Big asteroid was last remaining */
548 			upscore(killer, 25+level*2000);
549 		else	upscore(killer, 25);
550 	}
551 	else if (obj[i].shape == ASTSHAPE2)
552 	{
553 		for (k = 0; k < 3; k++)
554 		{	oldna = na;
555 			na = nextast();
556 			obj[na].x = obj[i].x;
557 			obj[na].y = obj[i].y;
558 			obj[na].xvel = obj[i].xvel;
559 			obj[na].yvel = obj[i].yvel;
560 			obj[na].alive++;
561 			obj[na].shape = ASTSHAPE3;
562 			obj[na].mass = M_SMALL;
563 			if (k == 1) blastpair(oldna,na);
564 		}
565 		obj[i].shape = ASTSHAPE3;
566 		obj[i].mass = M_SMALL;
567 		blastpair(na, i);
568 		boom(i, 20, 10);
569 		if (numasts == 1) upscore(killer, 500*level);
570 		numasts = numasts + 3;
571 		upscore(killer, 50);
572 	}
573 	else if (obj[i].shape == ASTSHAPE3)
574 	{	boom(i, 10, 8);
575 		obj[i].alive = 0; numasts--; upscore(killer, 100);}
576 	else	/* enemy {ship or bullet}	*/
577 	{	boom(i, 9, 7);
578 		obj[i].alive = 0; upscore(killer, 500);}
579 }
580 
581 void
moveobjs(crash)582 moveobjs(crash)
583 	int *crash;
584 {	extern Objtype obj[SHIP+1];
585 	extern int ships;
586 	extern double speedscale;
587 	int i, j;	/* Indexes	*/
588 	double *temp;
589 
590 	movebooms();
591 	for (i = 0; i < LASTOBJ+1; i++)
592 		if (obj[i].alive)
593 		{	temp = &obj[i].x;
594 			*temp = *temp + obj[i].xvel*speedscale;
595 			while (*temp < 0) *temp = *temp + (double) width;
596 			while (*temp > width) *temp = *temp - (double) width;
597 			temp = &obj[i].y;
598 			*temp = *temp + obj[i].yvel*speedscale;
599 			while (*temp < 0) *temp = *temp + height;
600 			while (*temp > height) *temp = *temp - height;
601 			obj[i].rot = obj[i].rot + obj[i].rotvel;
602 		}
603 	for (i = 0; i < FBUL; i++)
604 	    if (obj[i].alive)
605 	    {
606 		if (obj[SHIP].alive && collide(i, SHIP))
607 		{	if (shield_on) bounce(SHIP, i);
608 			else
609 			{	*crash = 2;
610 				ships--; obj[SHIP].alive = 0;
611 				killast(SHIP, i);
612 				continue;
613 		}	}
614 		for (j = ENEMYBUL; j < LASTBUL+1; j++)
615 			if (obj[j].alive && collide(i, j) &&
616 			   (j != ENEMYBUL || (i != ENEMYBUL && i != ENEMY)))
617 			{	obj[j].alive = 0;	/* Kill the bullet */
618 				/* Don't have 2 bullets kill same asteroid */
619 				if (obj[i].alive) killast(j,i);
620 			}
621 	    }
622 }
623 
624 void
fire()625 fire()
626 {	extern Objtype obj[SHIP+1];
627 	extern int width, nextbul;
628 	extern double drawscale, speedscale;
629 	double *shiprot, cosrot, sinrot;
630 
631 	obj[nextbul].alive++;
632 	shiprot = &obj[SHIP].rot;
633 	cosrot = cos(*shiprot); sinrot = sin(*shiprot);
634 	obj[nextbul].x = obj[SHIP].x + 20 * cosrot * drawscale;
635 	obj[nextbul].y = obj[SHIP].y + 20 * sinrot * drawscale;
636 	obj[nextbul].xvel = obj[SHIP].xvel + 10 * cosrot;
637 	obj[nextbul].yvel = obj[SHIP].yvel + 10 * sinrot;
638 	obj[nextbul].rot = *shiprot;
639 	obj[nextbul].time = width/(speedscale*11);	/* loops before bullet expires	*/
640 	nextbul++; if (nextbul == LASTBUL+1) nextbul = FBUL;
641 }
642 
643 void
hyper()644 hyper()
645 {	extern Objtype obj[SHIP+1];
646 	extern int width, height, rndint;
647 	unsigned char c;
648 	unsigned int i;
649 
650 	c = rand(rndint); i = c; i<<=2;	/* 0 - 1024	*/
651 	while (i > width) i -= width;
652 	obj[SHIP].x = (double) i;
653 	c = rand(rndint); i = c; i<<=2;	/* 0 - 1024	*/
654 	while (i > height) i -= height;
655 	obj[SHIP].y = (double) i;
656 }
657 
658 void
vdraw(disp,window,gc,shape,x,y,rot)659 vdraw(disp, window, gc, shape, x, y, rot)
660 	Display *disp;
661 	Drawable window;
662 	GC gc;
663 	int shape;
664 	double x, y, rot;
665 
666 {	int line;
667 	extern PolarPair shapes[LASTSHAPE+1][11];
668 	extern int numpairs[LASTSHAPE+1];
669 	extern double drawscale;
670 	XPoint figure[20];
671 	figure[0].x = (int) x; figure[0].y = (int) y;
672 	for (line=1; line < numpairs[shape]; line++)	/* 2 pairs = 1 line */
673 	{	figure[line].x  = (int) shapes[shape][line].length *
674 			cos(shapes[shape][line].angle + rot) * drawscale;
675 		figure[line].y  = (int) shapes[shape][line].length *
676 			sin(shapes[shape][line].angle + rot) * drawscale;
677 	}
678 	XDrawLines (disp, window, gc, figure, numpairs[shape], CoordModePrevious);
679 }
680 
681 int
main(argc,argv)682 main(argc, argv)
683 	int argc;
684 	char **argv;
685 {	Colormap cmap;
686 	Cursor cursor;
687 	Display *disp;
688 	Font font;
689 	GC gc, pmgc;
690 	KeySym key;
691 	Pixmap pixmap;
692 	Window window;
693 	XColor black, exact;
694 	XEvent event;
695 	XSizeHints hint;
696 	XWMHints wmhints;
697 	extern int width, height;
698 	int screen, depth;
699 	char text[30];
700 	unsigned long fg, bg;
701 
702 	extern double drawscale, speedscale;
703 	extern int level, numasts, rndint, ships, score, oldscore;
704 	extern Objtype obj[SHIP+1];
705 	unsigned char c;	/* for rand	*/
706 	double dx, dy, dist;
707 	int crashed, flashon, pause = 0, delay = 64,
708 		enemycount, counter, counterstart = 1,
709 		i,	/* index for drawing objs, counting bullets */
710 		r;	/* radius of shield circle	*/
711 
712 	disp = XOpenDisplay(0);
713 	if (disp == (Display *) NULL)
714 	{	fprintf(stderr, "Could not open display\n");
715 		exit(1);
716 	}
717 	screen = DefaultScreen(disp);
718 	bg = BlackPixel(disp, screen);
719 	fg = WhitePixel(disp, screen);
720 	hint.x = 150; hint.y = 200; hint.width = 550; hint.height = 550;
721 	hint.flags = PPosition | PSize;
722 	width = hint.width; height = hint.height-letheight-1;
723 	depth = DefaultDepth (disp, screen);
724 	window = XCreateSimpleWindow (disp, DefaultRootWindow(disp),
725 		hint.x, hint.y, hint.width, hint.height, 5, fg, bg);
726 	pixmap = XCreatePixmap (disp, window, width, height, depth);
727 	XSetStandardProperties (disp, window, "asteroids", "asteroids", None,
728 		argv, argc, &hint);
729 	gc = XCreateGC (disp, window, 0, 0);
730 	XSetGraphicsExposures(disp, gc, 0);	/* IMPORTANT!  If you do not
731 		specifically ask not to get Expose events, every XCopyArea
732 		will generate one, & the event queue will fill up.	*/
733 	font = XLoadFont(disp, "10x20\0");	/* If you don't have this
734 		font, try replacing it with 9x15\0	*/
735 	XSetFont(disp, gc, font);
736 	pmgc = XCreateGC (disp, window, 0, 0);
737 	XSetBackground (disp, gc, bg);
738 	XSetForeground (disp, gc, fg);
739 	XSetForeground (disp, pmgc, bg);  /* fg of pixmap is bg of window */
740 	wmhints.flags = InputHint;
741 	wmhints.input = True;
742 	XSetWMHints(disp, window, &wmhints);	/* Thanks to Doug Merritt */
743 	XSelectInput (disp, window,
744 		KeyPressMask | KeyReleaseMask | StructureNotifyMask);
745 	XMapRaised (disp, window);
746 
747 	/* Make cursor invisible. Just delete chunk if any error.	*/
748 	cmap = XDefaultColormap(disp, screen);
749 	XAllocNamedColor(disp, cmap, "Black", &exact, &black);
750 #if 0   /* Easy way, but can leave a black spot */
751 #include <X11/cursorfont.h>
752 	cursor = XCreateFontCursor(disp, XC_dot);
753 	XRecolorCursor(disp, cursor, &black, &black);
754 #else   /* Hard, good way */
755 	{	unsigned wd, ht;
756 		Pixmap pm, maskpm;
757 		GC cleargc;
758 		XQueryBestCursor(disp, window, 1, 1, &wd, &ht);
759 		pm = XCreatePixmap(disp, window, wd, ht, 1);
760 		maskpm = XCreatePixmap(disp, window, wd, ht, 1);
761 		cleargc = XCreateGC(disp, pm, 0, 0);
762 		XFillRectangle(disp, pm, cleargc, 0, 0, wd, ht);
763 		XFillRectangle(disp, maskpm, cleargc, 0, 0, wd, ht);
764 		cursor = XCreatePixmapCursor(disp, pm, maskpm,
765 						&black, &black, 1, 1);
766 		XFreePixmap(disp, pm);
767 		XFreePixmap(disp, maskpm);
768 		XFreeGC(disp, cleargc);
769 	}
770 #endif
771 	XDefineCursor(disp, window, cursor);
772 
773 	XFillRectangle (disp, pixmap, pmgc, 0, 0, width, height);
774 /*	Can delete next line if it causes trouble	*/
775 	srand((unsigned) time(0));	/* By Craig Smith	*/
776 	initasts();
777 Newgame:
778 	deletebooms();
779 	ships = 3;
780 	score = 0; oldscore = -1;
781 	for (level = 0; ;)
782 	{   if (level < 15) level++;
783 	    makeasts ();
784 Newship:    botline(disp, window, gc);
785 	    if (!obj[SHIP].alive)
786 	    {	obj[SHIP].x = width/2;
787 		obj[SHIP].y = height/2;
788 		obj[SHIP].xvel = 0;
789 		obj[SHIP].yvel = 0;
790 		obj[SHIP].rot = 3*pi/2;
791 		obj[SHIP].rotvel = 0;
792 		energy = 80;
793 		shield_on = 0;
794 	    }
795 	    obj[SHIP].alive = (ships > 0);
796 	    crashed = 0; flashon = 0; enemycount = 20;
797 	    counter = 0;
798 	    while (numasts)
799 	    {	for (i = FBUL; i < LASTBUL+1; i++)  /* Bullet timer */
800 		    if (obj[i].alive)
801 		    {	obj[i].time--;
802 			if (!obj[i].time) obj[i].alive = 0; /* Not --! */
803 		    }
804 		while (XEventsQueued(disp, QueuedAfterReading))
805 		{   XNextEvent(disp, &event);
806 		    switch (event.type)
807 		    {	case MappingNotify:
808 			    XRefreshKeyboardMapping ((XMappingEvent *)&event);
809 			    break;
810 			case ConfigureNotify:
811 			    width = event.xconfigure.width;
812 			    height = event.xconfigure.height-letheight-1;
813 			    XFreePixmap (disp, pixmap);
814 			    pixmap = XCreatePixmap (disp, window, width, height, depth);
815 			    XFillRectangle (disp, pixmap, pmgc, 0, 0, width, height);
816 			    botline(disp, window, gc);
817 			    break;
818 			case KeyPress:
819 			    key = XLookupKeysym ((XKeyEvent *)&event, 0);
820 			    if (!shield_on) switch (key)
821 			    {	case XK_Left: case XK_e:
822 				    obj[SHIP].rotvel = obj[SHIP].rotvel - .1; break;
823 				case XK_Right: case XK_r:
824 				    obj[SHIP].rotvel = obj[SHIP].rotvel + .1; break;
825 				case XK_w:
826 				    obj[SHIP].rot -= pi/4; break;
827 				case XK_t:
828 				    obj[SHIP].rot += pi/4; break;
829 				case XK_d:
830 				    obj[SHIP].rotvel = obj[SHIP].rotvel - .02; break;
831 				case XK_f:
832 				    obj[SHIP].rotvel = obj[SHIP].rotvel + .02; break;
833 				case XK_Up: case THRUST:
834 				    obj[SHIP].xvel += cos(obj[SHIP].rot);
835 				    obj[SHIP].yvel += sin(obj[SHIP].rot);
836 				    obj[SHIP].shape = SHIPTHRSHAPE;
837 				    break;
838 				case XK_Control_L: case XK_Control_R:
839 				case FIRE:
840 				    if (obj[SHIP].alive) fire(); break;
841 				case XK_space:
842 				    if (obj[SHIP].alive)
843 				    {	hyper(); flashon = 1;
844 /*				    NOT XSetForeground (disp, gc, bg);
845 	If you set the fg black, & print the highscore, it will effectively erase it.	*/
846 				        XSetForeground (disp, pmgc, fg);
847 					XFillRectangle (disp, pixmap, pmgc, 0, 0, width, height);
848 				    }
849 				    break;
850 				case XK_Down: case SHIELD:
851 				    if (energy)
852 				    {	shield_on = 1;
853 					obj[SHIP].shape = SHIPSHAPE;}
854 					break;
855 				case XK_period:	/* decrease delay	*/
856 				    if (delay > 1) delay >>=1; break;
857 				case XK_comma:	/* increase delay	*/
858 				    delay <<=1; break;
859 				case XK_m:	/* decrease drawscale - may go negative */
860 				    drawscale -= .1; break;
861 				case XK_n:	/* increase drawscale	*/
862 				    drawscale += .1; break;
863 				case XK_2:	/* increase speedscale	*/
864 				    speedscale += .1; break;
865 				case XK_1:	/* decrease speedscale	*/
866 				    speedscale -= .1; break;
867 				case XK_b:	/* increase moves/update */
868 				    counterstart++; break;
869 				case XK_v:	/* decrease moves/update */
870 				    if (counterstart > 1) counterstart--;
871 				    break;
872 				case PAUSE:	/* pause	*/
873 				    pause = 1 - pause; break;
874 				case XK_plus:	/* cheat	*/
875 				    ships++; botline(disp, window, gc); break;
876 				case XK_q:	/* quit		*/
877 				    if (event.xkey.state & ShiftMask)
878 					goto End;
879 				    break;
880 				case XK_s:	/* start new ship */
881 				    if (!obj[SHIP].alive) {
882 					if (ships < 1) goto Newgame;
883 					else goto Newship;
884 				    }
885 				    break;
886 			    }
887 			    break;
888 			case KeyRelease:
889 			    key = XLookupKeysym((XKeyEvent *)&event, 0);
890 			    switch (key)
891 			    {	case XK_Left: case XK_e:
892 				    obj[SHIP].rotvel = 0; break;
893 				case XK_Right: case XK_r:
894 				    obj[SHIP].rotvel = 0; break;
895 				case XK_Up: case THRUST:
896 				    obj[SHIP].shape = SHIPSHAPE;
897 				    break;
898 				case XK_Down: case SHIELD:
899 				    shield_on = 0; break;
900 			    }
901 /*			    break;		*/
902 		}   }
903 		if (!pause)
904 		{   moveobjs(&crashed);
905 		    if (ships) score--;	/* timer effect	*/
906 		    if (!counter)
907 		    {	counter = counterstart;	/* Restart counter */
908 			if (crashed == 2)
909 			{   crashed--; flashon++;
910 			    boom(SHIP, BMAX-1, 70);
911 			    XSetForeground (disp, pmgc, fg);
912 			    XFillRectangle (disp, pixmap, pmgc, 0, 0, width, height);
913 			    botline(disp, window, gc);
914 			}
915 			/* Write copyright notice	*/
916 			if (!ships)
917 			{   sprintf(text, "Xasteroids");
918 			    XDrawImageString (disp, pixmap, gc,
919 				width/2-50, height/2-2*letheight,
920 				text, strlen(text));
921 			    sprintf(text, "Copyright 1990 by Phil Goetz");
922 			    XDrawImageString (disp, pixmap, gc,
923 				width/2-140, height/2,
924 				text, strlen(text));
925 			    sprintf(text, "goetz@cs.buffalo.edu");
926 			    XDrawImageString (disp, pixmap, gc,
927 				width/2-100, height/2+2*letheight,
928 				text, strlen(text));
929 			}
930 			/*	Draw objects	*/
931 			for (i = 0; i <= LASTOBJ; i++)
932 			    if (obj[i].alive)
933 				vdraw(disp, pixmap, gc, obj[i].shape,
934 					obj[i].x, obj[i].y, obj[i].rot);
935 			if (shield_on && obj[SHIP].alive)
936 			{   r = abs((int) (drawscale*SHIPSIZE));
937 			    XDrawArc(disp, pixmap, gc,
938 				((int) obj[SHIP].x) - r,
939 				((int) obj[SHIP].y) - r,
940 				2*r, 2*r, 0, 360*64);
941 			    energy--;
942 			    if (!energy) shield_on = 0;
943 			}
944 			drawbooms(disp, pixmap, gc);
945 			/* update display:	*/
946 			XCopyArea(disp, pixmap, window, gc, 0, 0, width, height, 0, 0);
947 			printss(disp, window, gc);
948 			/* erase objects	*/
949 			XFillRectangle (disp, pixmap, pmgc, 0, 0, width, height);
950 			if (flashon)
951 			{   flashon--;
952 			    XSetForeground (disp, pmgc, bg);
953 			    XFillRectangle (disp, pixmap, pmgc, 0, 0, width, height);
954 			}
955 			XSync(disp, 0);
956 		    }
957 		    counter--;
958 		    i = (rand(rndint)>>8) & 255;
959 		    if (!obj[ENEMY].alive)
960 			{   if (i < level)
961 			    {	c = rand(rndint);
962 				if (c < level * 10) makeenemy();
963 			}   }
964 		    else
965 		    {	i += (obj[SHIP].y > obj[ENEMY].y)
966 				? level>>1 : -(level>>1);
967 			obj[ENEMY].yvel += (i>128+6*obj[ENEMY].yvel) ? .5 : -.5;
968 		    }
969 		    enemycount--; if (!enemycount)
970 		    {	enemycount = 100;
971 			if (obj[ENEMY].alive)
972 			{   obj[ENEMYBUL].alive++;
973 			    obj[ENEMYBUL].x = obj[ENEMY].x;
974 			    obj[ENEMYBUL].y = obj[ENEMY].y;
975 			    dx = obj[SHIP].x - obj[ENEMY].x;
976 			    dy = obj[SHIP].y - obj[ENEMY].y;
977 			    dist = sqrt(dx*dx + dy*dy);
978 			    obj[ENEMYBUL].xvel = 3*dx/dist;
979 			    obj[ENEMYBUL].yvel = 3*dy/dist;
980 			}
981 			else	obj[ENEMYBUL].alive = 0;
982 		    }
983 #ifdef HAS_USLEEP
984 		    usleep(delay);
985 #else
986 		    for (i = 0; i < delay; i++);
987 #endif
988 		}
989 	    }
990 	}
991 End:	printf("\nYour high score was %d\n", highscore);
992 	XFreeGC (disp, gc);
993 	XFreeGC (disp, pmgc);
994 	XFreePixmap (disp, pixmap);
995 	XDestroyWindow (disp, window);
996 	XCloseDisplay (disp);
997 	exit(0);
998 }
999