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