http://slackware.bholcomb.com/slackware/slackware-11.0/source/xap/xscreensaver/xscree...
[xscreensaver] / hacks / penetrate.c
1 /* Copyright (c) 1999 Adam Miller adum@aya.yale.edu
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10
11  * penetrate simulates the arcade classic with the cities and the stuff
12  * shooting down from the sky and stuff. The computer plays against itself,
13  * desperately defending the forces of good against those thingies raining
14  * down. Bonus cities are awarded at ever-increasing intervals. Every five
15  * levels appears a bonus round. The computer player gets progressively
16  * more intelligent as the game progresses. Better aim, more economical with
17  * ammo, and better target selection. Points are in the bottom right, and
18  * high score is in the bottom left. Start with -smart to have the computer
19  * player skip the learning process.
20
21  Version: 0.2
22  -- fixed an AI bug that was keeping the computer player a tad weak
23  Version: 0.1
24  -- first release
25
26  */
27
28 #include "screenhack.h"
29
30 #define kSleepTime 10000 
31
32 #define font_height(font)               (font->ascent + font->descent)
33
34 #define kCityPause 500000
35 #define kLevelPause 1
36 #define SCORE_MISSILE 100
37 #define kFirstBonus 5000
38 #define kMinRate 30
39 #define kMaxRadius 100
40
41 typedef struct {
42   int alive;
43   int x, y;
44   int startx, starty;
45   int endx, endy;
46   int dcity;
47   float pos;
48   int enemies;
49   int jenis;
50   int splits;
51   XColor color;
52 } Missile;
53
54 typedef struct {
55   int alive;
56   int x, y, rad, oflaser;
57   int max, outgoing;
58   XColor color;
59 } Boom;
60
61 typedef struct {
62   int alive;
63   int x;
64   XColor color;
65 } City;
66
67 typedef struct {
68   int alive;
69   int x, y;
70   int startx, starty;
71   int endx, endy;
72   int oldx, oldy;
73   int oldx2, oldy2;
74   float velx, vely, fposx, fposy;
75   float lenMul;
76   XColor color;
77   int target;
78 } Laser;
79
80 #define kMaxMissiles 256
81 #define kMaxBooms 512
82 #define kMaxLasers 128
83 #define kBoomRad 40
84 #define kNumCities 5
85
86 #define kLaserLength 12
87
88 #define kMissileSpeed 0.003
89 #define kLaserSpeed (kMissileSpeed * 6)
90
91
92 struct state {
93   Display *dpy;
94   Window window;
95
96    XFontStruct *font, *scoreFont;
97    GC draw_gc, erase_gc, level_gc;
98    unsigned int default_fg_pixel;
99    XColor scoreColor;
100
101    int bgrowth;
102    int lrate, startlrate;
103    long loop;
104    long score, highscore;
105    long nextBonus;
106    int numBonus;
107    int bround;
108    long lastLaser;
109    int gamez;
110    int aim;
111    int econpersen;
112    int choosypersen;
113    int carefulpersen;
114    int smart;
115    Colormap cmap;
116
117    Missile missile[kMaxMissiles];
118    Boom boom[kMaxBooms];
119    City city[kNumCities];
120    Laser laser[kMaxLasers];
121    int blive[kNumCities];
122
123    int level, levMissiles, levFreq;
124
125    int draw_xlim, draw_ylim;
126    int draw_reset;
127 };
128
129
130 static void Explode(struct state *st, int x, int y, int max, XColor color, int oflaser)
131 {
132   int i;
133   Boom *m = 0;
134   for (i=0;i<kMaxBooms;i++)
135          if (!st->boom[i].alive) {
136                 m = &st->boom[i];
137                 break;
138          }
139   if (!m)
140          return;
141
142   m->alive = 1;
143   m->x = x;
144   m->y = y;
145   m->rad = 0;
146   if (max > kMaxRadius)
147          max = kMaxRadius;
148   m->max = max;
149   m->outgoing = 1;
150   m->color = color;
151   m->oflaser = oflaser;
152 }
153
154 static void launch (struct state *st, int xlim, int ylim, int src)
155 {
156   int i;
157   Missile *m = 0, *msrc;
158   for (i=0;i<kMaxMissiles;i++)
159          if (!st->missile[i].alive) {
160                 m = &st->missile[i];
161                 break;
162          }
163   if (!m)
164          return;
165
166   m->alive = 1;
167   m->startx = (random() % xlim);
168   m->starty = 0;
169   m->endy = ylim;
170   m->pos = 0.0;
171   m->jenis = random() % 360;
172   m->splits = 0;
173   if (m->jenis < 50) {
174     int j = ylim * 0.4;
175     if (j)
176          m->splits = random() % j;
177          if (m->splits < ylim * 0.08)
178                 m->splits = 0;
179   }
180
181   /* special if we're from another missile */
182   if (src >= 0) {
183          int dc = random() % (kNumCities - 1);
184          msrc = &st->missile[src];
185          if (dc == msrc->dcity)
186                 dc++;
187          m->dcity = dc;
188          m->startx = msrc->x;
189          m->starty = msrc->y;
190          if (m->starty > ylim * 0.4 || m->splits <= m->starty)
191                 m->splits = 0;  /* too far down already */
192          m->jenis = msrc->jenis;
193   }
194   else
195          m->dcity = random() % kNumCities;
196   m->endx = st->city[m->dcity].x + (random() % 20) - 10;
197   m->x = m->startx;
198   m->y = m->starty;
199   m->enemies = 0;
200
201   if (!mono_p) {
202          hsv_to_rgb (m->jenis, 1.0, 1.0,
203                                          &m->color.red, &m->color.green, &m->color.blue);
204          m->color.flags = DoRed | DoGreen | DoBlue;
205          if (!XAllocColor (st->dpy, st->cmap, &m->color)) {
206                 m->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
207                 m->color.red = m->color.green = m->color.blue = 0xFFFF;
208          }
209   }
210 }
211
212 #define kExpHelp 0.2
213 #define kSpeedDiff 3.5
214 #define kMaxToGround 0.75
215 static int fire(struct state *st, int xlim, int ylim)
216 {
217   int i, j, cnt = 0;
218   int dcity;
219   long dx, dy, ex, ey;
220   Missile *mis = 0;
221   Laser *m = 0;
222   int untargeted = 0;
223   int choosy = 0, economic = 0, careful = 0;
224   int suitor[kMaxMissiles];
225   int livecity = 0;
226   int ytargetmin = ylim * 0.75;
227   int deepest = 0;
228   int misnum = 0;
229
230   choosy = (random() % 100) < st->choosypersen;
231   economic = (random() % 100) < st->econpersen;
232   careful = (random() % 100) < st->carefulpersen;
233
234   /* count our cities */
235   for (i=0;i<kNumCities;i++)
236          livecity += st->city[i].alive;
237   if (livecity == 0)
238          return 1;  /* no guns */
239
240   for (i=0;i<kMaxLasers;i++)
241          if (!st->laser[i].alive) {
242                 m = &st->laser[i];
243                 break;
244          }
245   if (!m)
246          return 1;
247
248   /* if no missiles on target, no need to be choosy */
249   if (choosy) {
250          int choo = 0;
251          for (j=0;j<kMaxMissiles;j++) {
252                 mis = &st->missile[j];
253                 if (!mis->alive || (mis->y > ytargetmin))
254                   continue;
255                 if (st->city[mis->dcity].alive)
256                   choo++;
257          }
258          if (choo == 0)
259                 choosy = 0;
260   }
261
262   for (j=0;j<kMaxMissiles;j++) {
263          mis = &st->missile[j];
264          suitor[j] = 0;
265          if (!mis->alive || (mis->y > ytargetmin))
266                 continue;
267          if (choosy && (st->city[mis->dcity].alive == 0))
268                 continue;
269          ey = mis->starty + ((float) (mis->endy - mis->starty)) * (mis->pos + kExpHelp + (1.0 - mis->pos) / kSpeedDiff);
270          if (ey > ylim * kMaxToGround)
271                 continue;  /* too far down */
272          cnt++;
273          suitor[j] = 1;
274   }
275
276   /* count missiles that are on target and not being targeted */
277   if (choosy && economic)
278          for (j=0;j<kMaxMissiles;j++)
279                 if (suitor[j] && st->missile[j].enemies == 0)
280                   untargeted++;
281
282   if (economic)
283          for (j=0;j<kMaxMissiles;j++) {
284                 if (suitor[j] && cnt > 1)
285                   if (st->missile[j].enemies > 0)
286                          if (st->missile[j].enemies > 1 || untargeted == 0) {
287                                 suitor[j] = 0;
288                                 cnt--;
289                          }
290                 /* who's closest? biggest threat */
291                 if (suitor[j] && st->missile[j].y > deepest)
292                   deepest = st->missile[j].y;
293          }
294
295   if (deepest > 0 && careful) {
296          /* only target deepest missile */
297          cnt = 1;
298          for (j=0;j<kMaxMissiles;j++)
299                 if (suitor[j] && st->missile[j].y != deepest)
300                   suitor[j] = 0;
301   }
302
303   if (cnt == 0)
304          return 1;  /* no targets available */
305   cnt = random() % cnt;
306   for (j=0;j<kMaxMissiles;j++)
307          if (suitor[j])
308                 if (cnt-- == 0) {
309                   mis = &st->missile[j];
310                   misnum = j;
311                   break;
312                 }
313
314   if (mis == 0)
315          return 1;  /* shouldn't happen */
316
317   dcity = random() % livecity;
318   for (j=0;j<kNumCities;j++)
319          if (st->city[j].alive)
320                 if (dcity-- == 0) {
321                   dcity = j;
322                   break;
323                 }
324   m->startx = st->city[dcity].x;
325   m->starty = ylim;
326   ex = mis->startx + ((float) (mis->endx - mis->startx)) * (mis->pos + kExpHelp + (1.0 - mis->pos) / kSpeedDiff);
327   ey = mis->starty + ((float) (mis->endy - mis->starty)) * (mis->pos + kExpHelp + (1.0 - mis->pos) / kSpeedDiff);
328   m->endx = ex + random() % 16 - 8 + (random() % st->aim) - st->aim / 2;
329   m->endy = ey + random() % 16 - 8 + (random() % st->aim) - st->aim / 2;
330   if (ey > ylim * kMaxToGround)
331          return 0;  /* too far down */
332   mis->enemies++;
333   m->target = misnum;
334   m->x = m->startx;
335   m->y = m->starty;
336   m->oldx = -1;
337   m->oldy = -1;
338   m->oldx2 = -1;
339   m->oldy2 = -1;
340   m->fposx = m->x;
341   m->fposy = m->y;
342   dx = (m->endx - m->x);
343   dy = (m->endy - m->y);
344   m->velx = dx / 100.0;
345   m->vely = dy / 100.0;
346   m->alive = 1;
347   /* m->lenMul = (kLaserLength * kLaserLength) / (m->velx * m->velx + m->vely * m->vely); */
348   m->lenMul = -(kLaserLength / m->vely);
349
350   if (!mono_p) {
351          m->color.blue = 0x0000;
352          m->color.green = 0xFFFF;
353          m->color.red = 0xFFFF;
354          m->color.flags = DoRed | DoGreen | DoBlue;
355          if (!XAllocColor (st->dpy, st->cmap, &m->color)) {
356                 m->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
357                 m->color.red = m->color.green = m->color.blue = 0xFFFF;
358          }
359   }
360   return 1;
361 }
362
363 static void *
364 penetrate_init (Display *dpy, Window window)
365 {
366   struct state *st = (struct state *) calloc (1, sizeof(*st));
367   int i;
368   /*char *fontname =   "-*-new century schoolbook-*-r-*-*-*-380-*-*-*-*-*-*"; */
369   char *fontname =   "-*-courier-*-r-*-*-*-380-*-*-*-*-*-*";
370   XGCValues gcv;
371   XWindowAttributes xgwa;
372
373   st->dpy = dpy;
374   st->window = window;
375
376   XGetWindowAttributes (st->dpy, st->window, &xgwa);
377   st->cmap = xgwa.colormap;
378
379   st->lrate = 80;
380   st->nextBonus = kFirstBonus;
381   st->aim = 180;
382
383   st->smart = get_boolean_resource(st->dpy, "smart","Boolean");
384   st->bgrowth = get_integer_resource (st->dpy, "bgrowth", "Integer");
385   st->lrate = get_integer_resource (st->dpy, "lrate", "Integer");
386   if (st->bgrowth < 0) st->bgrowth = 2;
387   if (st->lrate < 0) st->lrate = 2;
388   st->startlrate = st->lrate;
389
390   if (!fontname || !*fontname)
391     fprintf (stderr, "%s: no font specified.\n", progname);
392   st->font = XLoadQueryFont(st->dpy, fontname);
393   if (!st->font)
394     fprintf (stderr, "%s: could not load font %s.\n", progname, fontname);
395
396   if (!(st->scoreFont = XLoadQueryFont(st->dpy, "-*-times-*-r-*-*-*-180-*-*-*-*-*-*")))
397          fprintf(stderr, "%s: Can't load Times font.", progname);
398
399   for (i = 0; i < kMaxMissiles; i++)
400     st->missile[i].alive = 0;
401
402   for (i = 0; i < kMaxLasers; i++)
403     st->laser[i].alive = 0;
404
405   for (i = 0; i < kMaxBooms; i++)
406     st->boom[i].alive = 0;
407
408   for (i = 0; i < kNumCities; i++) {
409          City *m = &st->city[i];
410     m->alive = 1;
411          m->color.red = m->color.green = m->color.blue = 0xFFFF;
412          m->color.blue = 0x1111; m->color.green = 0x8888;
413          m->color.flags = DoRed | DoGreen | DoBlue;
414          if (!XAllocColor (st->dpy, st->cmap, &m->color)) {
415                 m->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
416                 m->color.red = m->color.green = m->color.blue = 0xFFFF;
417          }
418   }
419
420   gcv.foreground = st->default_fg_pixel =
421     get_pixel_resource(st->dpy, st->cmap, "foreground", "Foreground");
422   gcv.font = st->scoreFont->fid;
423   st->draw_gc = XCreateGC(st->dpy, st->window, GCForeground | GCFont, &gcv);
424   gcv.font = st->font->fid;
425   st->level_gc = XCreateGC(st->dpy, st->window, GCForeground | GCFont, &gcv);
426   XSetForeground (st->dpy, st->level_gc, st->city[0].color.pixel);
427   gcv.foreground = get_pixel_resource(st->dpy, st->cmap, "background", "Background");
428   st->erase_gc = XCreateGC(st->dpy, st->window, GCForeground, &gcv);
429
430 # ifdef HAVE_COCOA
431   jwxyz_XSetAntiAliasing (st->dpy, st->erase_gc, False);
432   jwxyz_XSetAntiAliasing (st->dpy, st->draw_gc, False);
433 # endif
434
435
436   /* make a gray color for score */
437   if (!mono_p) {
438          st->scoreColor.red = st->scoreColor.green = st->scoreColor.blue = 0xAAAA;
439          st->scoreColor.flags = DoRed | DoGreen | DoBlue;
440          if (!XAllocColor (st->dpy, st->cmap, &st->scoreColor)) {
441                 st->scoreColor.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
442                 st->scoreColor.red = st->scoreColor.green = st->scoreColor.blue = 0xFFFF;
443          }
444   }
445
446   XClearWindow(st->dpy, st->window);
447   return st;
448 }
449
450 static void DrawScore(struct state *st, int xlim, int ylim)
451 {
452   char buf[16];
453   int width, height;
454   sprintf(buf, "%ld", st->score);
455   width = XTextWidth(st->scoreFont, buf, strlen(buf));
456   height = font_height(st->scoreFont);
457   XSetForeground (st->dpy, st->draw_gc, st->scoreColor.pixel);
458   XFillRectangle(st->dpy, st->window, st->erase_gc,
459                                   xlim - width - 6, ylim - height - 2, width + 6, height + 2);
460   XDrawString(st->dpy, st->window, st->draw_gc, xlim - width - 2, ylim - 2,
461                     buf, strlen(buf));
462
463   sprintf(buf, "%ld", st->highscore);
464   width = XTextWidth(st->scoreFont, buf, strlen(buf));
465   XFillRectangle(st->dpy, st->window, st->erase_gc,
466                                   4, ylim - height - 2, width + 4, height + 2);
467   XDrawString(st->dpy, st->window, st->draw_gc, 4, ylim - 2,
468                     buf, strlen(buf));
469 }
470
471 static void AddScore(struct state *st, int xlim, int ylim, long dif)
472 {
473   int i, sumlive = 0;
474   for (i=0;i<kNumCities;i++)
475          sumlive += st->city[i].alive;
476   if (sumlive == 0)
477          return;   /* no cities, not possible to score */
478
479   st->score += dif;
480   if (st->score > st->highscore)
481          st->highscore = st->score;
482   DrawScore(st, xlim, ylim);
483 }
484
485 static void DrawCity(struct state *st, int x, int y, XColor col)
486 {
487          XSetForeground (st->dpy, st->draw_gc, col.pixel);
488          XFillRectangle(st->dpy, st->window, st->draw_gc,
489                                   x - 30, y - 40, 60, 40);
490          XFillRectangle(st->dpy, st->window, st->draw_gc,
491                                                  x - 20, y - 50, 10, 10);
492          XFillRectangle(st->dpy, st->window, st->draw_gc,
493                                   x + 10, y - 50, 10, 10);
494 }
495
496 static void DrawCities(struct state *st, int xlim, int ylim)
497 {
498   int i, x;
499   for (i = 0; i < kNumCities; i++) {
500          City *m = &st->city[i];
501          if (!m->alive)
502                 continue;
503          x = (i + 1) * (xlim / (kNumCities + 1));
504          m->x = x;
505
506          DrawCity(st, x, ylim, m->color);
507   }
508 }
509
510 static void LoopMissiles(struct state *st, int xlim, int ylim)
511 {
512   int i, j, max = 0;
513   for (i = 0; i < kMaxMissiles; i++) {
514          int old_x, old_y;
515          Missile *m = &st->missile[i];
516          if (!m->alive)
517                 continue;
518          old_x = m->x;
519          old_y = m->y;
520          m->pos += kMissileSpeed;
521          m->x = m->startx + ((float) (m->endx - m->startx)) * m->pos;
522          m->y = m->starty + ((float) (m->endy - m->starty)) * m->pos;
523
524       /* erase old one */
525
526          XSetLineAttributes(st->dpy, st->draw_gc, 4, 0,0,0);
527     XSetForeground (st->dpy, st->draw_gc, m->color.pixel);
528          XDrawLine(st->dpy, st->window, st->draw_gc,
529                                   old_x, old_y, m->x, m->y);
530
531          /* maybe split off a new missile? */
532          if (m->splits && (m->y > m->splits)) {
533                 m->splits = 0;
534                 launch(st, xlim, ylim, i);
535          }
536          
537          if (m->y >= ylim) {
538                 m->alive = 0;
539                 if (st->city[m->dcity].alive) {
540                   st->city[m->dcity].alive = 0;
541                   Explode(st, m->x, m->y, kBoomRad * 2, m->color, 0);
542                 }
543          }
544
545          /* check hitting explosions */
546          for (j=0;j<kMaxBooms;j++) {
547                 Boom *b = &st->boom[j];
548                 if (!b->alive)
549                   continue;
550                 else {
551                   int dx = abs(m->x - b->x);
552                   int dy = abs(m->y - b->y);
553                   int r = b->rad + 2;
554                   if ((dx < r) && (dy < r))
555                          if (dx * dx + dy * dy < r * r) {
556                                 m->alive = 0;
557                                 max = b->max + st->bgrowth - kBoomRad;
558                                 AddScore(st, xlim, ylim, SCORE_MISSILE);
559                   }
560                 }
561          }
562
563          if (m->alive == 0) {
564                 float my_pos;
565                 /* we just died */
566                 Explode(st, m->x, m->y, kBoomRad + max, m->color, 0);
567                 XSetLineAttributes(st->dpy, st->erase_gc, 4, 0,0,0);
568                 /* In a perfect world, we could simply erase a line from
569                    (m->startx, m->starty) to (m->x, m->y). This is not a
570                    perfect world. */
571                 old_x = m->startx;
572                 old_y = m->starty;
573                 my_pos = kMissileSpeed;
574                 while (my_pos <= m->pos) {
575                         m->x = m->startx + ((float) (m->endx - m->startx)) * my_pos;
576                         m->y = m->starty + ((float) (m->endy - m->starty)) * my_pos;
577                         XDrawLine(st->dpy, st->window, st->erase_gc, old_x, old_y, m->x, m->y);
578                         old_x = m->x;
579                         old_y = m->y;
580                         my_pos += kMissileSpeed;
581                 }
582          }
583   }
584 }
585
586 static void LoopLasers(struct state *st, int xlim, int ylim)
587 {
588   int i, j, miny = ylim * 0.8;
589   int x, y;
590   for (i = 0; i < kMaxLasers; i++) {
591          Laser *m = &st->laser[i];
592          if (!m->alive)
593                 continue;
594
595          if (m->oldx != -1) {
596                  XSetLineAttributes(st->dpy, st->erase_gc, 2, 0,0,0);
597                  XDrawLine(st->dpy, st->window, st->erase_gc,
598                                   m->oldx2, m->oldy2, m->oldx, m->oldy);
599          }
600
601          m->fposx += m->velx;
602          m->fposy += m->vely;
603          m->x = m->fposx;
604          m->y = m->fposy;
605          
606          x = m->fposx + (-m->velx * m->lenMul);
607          y = m->fposy + (-m->vely * m->lenMul);
608
609          m->oldx = x;
610          m->oldy = y;
611
612          XSetLineAttributes(st->dpy, st->draw_gc, 2, 0,0,0);
613     XSetForeground (st->dpy, st->draw_gc, m->color.pixel);
614          XDrawLine(st->dpy, st->window, st->draw_gc,
615                                   m->x, m->y, x, y);
616
617          m->oldx2 = m->x;
618          m->oldy2 = m->y;
619          m->oldx = x;
620          m->oldy = y;
621          
622          if (m->y < m->endy) {
623                 m->alive = 0;
624          }
625
626          /* check hitting explosions */
627          if (m->y < miny)
628                 for (j=0;j<kMaxBooms;j++) {
629                   Boom *b = &st->boom[j];
630                   if (!b->alive)
631                          continue;
632                   else {
633                          int dx = abs(m->x - b->x);
634                          int dy = abs(m->y - b->y);
635                          int r = b->rad + 2;
636                          if (b->oflaser)
637                                 continue;
638                          if ((dx < r) && (dy < r))
639                                 if (dx * dx + dy * dy < r * r) {
640                                   m->alive = 0;
641                                   /* one less enemy on this missile -- it probably didn't make it */
642                                   if (st->missile[m->target].alive)
643                                          st->missile[m->target].enemies--;
644                                 }
645                   }
646                 }
647          
648          if (m->alive == 0) {
649                 /* we just died */
650                 XDrawLine(st->dpy, st->window, st->erase_gc,
651                                   m->x, m->y, x, y);
652                 Explode(st, m->x, m->y, kBoomRad, m->color, 1);
653          }
654   }
655 }
656
657 static void LoopBooms(struct state *st, int xlim, int ylim)
658 {
659   int i;
660   for (i = 0; i < kMaxBooms; i++) {
661          Boom *m = &st->boom[i];
662          if (!m->alive)
663                 continue;
664          
665          if (st->loop & 1) {
666                 if (m->outgoing) {
667                   m->rad++;
668                   if (m->rad >= m->max)
669                          m->outgoing = 0;
670                   XSetLineAttributes(st->dpy, st->draw_gc, 1, 0,0,0);
671                   XSetForeground (st->dpy, st->draw_gc, m->color.pixel);
672                   XDrawArc(st->dpy, st->window, st->draw_gc, m->x - m->rad, m->y - m->rad, m->rad * 2, m->rad * 2, 0, 360 * 64);
673                 }
674                 else {
675                   XSetLineAttributes(st->dpy, st->erase_gc, 1, 0,0,0);
676                   XDrawArc(st->dpy, st->window, st->erase_gc, m->x - m->rad, m->y - m->rad, m->rad * 2, m->rad * 2, 0, 360 * 64);
677                   m->rad--;
678                   if (m->rad <= 0)
679                          m->alive = 0;
680                 }
681          }
682   }
683 }
684
685
686 /* after they die, let's change a few things */
687 static void Improve(struct state *st)
688 {
689   if (st->smart)
690          return;
691   if (st->level > 20)
692          return;  /* no need, really */
693   st->aim -= 4;
694   if (st->level <= 2) st->aim -= 8;
695   if (st->level <= 5) st->aim -= 6;
696   if (st->gamez < 3)
697          st->aim -= 10;
698   st->carefulpersen += 6;
699   st->choosypersen += 4;
700   if (st->level <= 5) st->choosypersen += 3;
701   st->econpersen += 4;
702   st->lrate -= 2;
703   if (st->startlrate < kMinRate) {
704          if (st->lrate < st->startlrate)
705                 st->lrate = st->startlrate;
706   }
707   else {
708          if (st->lrate < kMinRate)
709                 st->lrate = kMinRate;
710   }
711   if (st->level <= 5) st->econpersen += 3;
712   if (st->aim < 1) st->aim = 1;
713   if (st->choosypersen > 100) st->choosypersen = 100;
714   if (st->carefulpersen > 100) st->carefulpersen = 100;
715   if (st->econpersen > 100) st->econpersen = 100;
716 }
717
718 static void NewLevel(struct state *st, int xlim, int ylim)
719 {
720   char buf[32];
721   int width, i, sumlive = 0;
722   int liv[kNumCities];
723   int freecity = 0;
724
725   if (st->level == 0) {
726          st->level++;
727          goto END_LEVEL;
728   }
729
730   /* check for a free city */
731   if (st->score >= st->nextBonus) {
732          st->numBonus++;
733          st->nextBonus += kFirstBonus * st->numBonus;
734          freecity = 1;
735   }
736
737   for (i=0;i<kNumCities;i++) {
738          if (st->bround)
739                 st->city[i].alive = st->blive[i];
740          liv[i] = st->city[i].alive;
741          sumlive += liv[i];
742          if (!st->bround)
743                 st->city[i].alive = 0;
744   }
745
746   /* print out screen */
747   XFillRectangle(st->dpy, st->window, st->erase_gc,
748                                   0, 0, xlim, ylim);
749   if (st->bround)
750          sprintf(buf, "Bonus Round Over");
751   else {
752          if (sumlive || freecity)
753                 sprintf(buf, "Level %d Cleared", st->level);
754          else
755                 sprintf(buf, "GAME OVER");
756   }
757   if (st->level > 0) {
758          width = XTextWidth(st->font, buf, strlen(buf));
759          XDrawString(st->dpy, st->window, st->level_gc, xlim / 2 - width / 2, ylim / 2 - font_height(st->font) / 2,
760                                          buf, strlen(buf));
761          XSync(st->dpy, False);
762          usleep(1000000);
763   }
764
765   if (!st->bround) {
766          if (sumlive || freecity) {
767                 int sumwidth;
768                 /* draw live cities */
769                 XFillRectangle(st->dpy, st->window, st->erase_gc,
770                                                         0, ylim - 100, xlim, 100);
771
772                 sprintf(buf, "X %ld", st->level * 100L);
773                 /* how much they get */
774                 sumwidth = XTextWidth(st->font, buf, strlen(buf));
775                 /* add width of city */
776                 sumwidth += 60;
777                 /* add spacer */
778                 sumwidth += 40;
779                 DrawCity(st, xlim / 2 - sumwidth / 2 + 30, ylim * 0.70, st->city[0].color);
780                 XDrawString(st->dpy, st->window, st->level_gc, xlim / 2 - sumwidth / 2 + 40 + 60, ylim * 0.7, buf, strlen(buf));
781                 for (i=0;i<kNumCities;i++) {
782                   if (liv[i]) {
783                          st->city[i].alive = 1;
784                          AddScore(st, xlim, ylim, 100 * st->level);
785                          DrawCities(st, xlim, ylim);
786                          XSync(st->dpy, False);
787                          usleep(kCityPause);
788                   }
789                 }
790          }
791          else {
792                 /* we're dead */
793                 usleep(3000000);
794
795                 /* start new */
796                 st->gamez++;
797                 Improve(st);
798                 for (i=0;i<kNumCities;i++)
799                   st->city[i].alive = 1;
800                 st->level = 0;
801                 st->loop = 1;
802                 st->score = 0;
803                 st->nextBonus = kFirstBonus;
804                 st->numBonus = 0;
805                 DrawCities(st, xlim, ylim);
806          }
807   }
808
809   /* do free city part */
810   if (freecity && sumlive < 5) {
811          int ncnt = random() % (5 - sumlive) + 1;
812          for (i=0;i<kNumCities;i++)
813                 if (!st->city[i].alive)
814                   if (!--ncnt)
815                          st->city[i].alive = 1;
816          strcpy(buf, "Bonus City");
817          width = XTextWidth(st->font, buf, strlen(buf));
818          XDrawString(st->dpy, st->window, st->level_gc, xlim / 2 - width / 2, ylim / 4, buf, strlen(buf));
819          DrawCities(st, xlim, ylim);
820          XSync(st->dpy, False);
821          usleep(1000000);
822   }
823
824   XFillRectangle(st->dpy, st->window, st->erase_gc,
825                                           0, 0, xlim, ylim - 100);
826   
827   if (!st->bround)
828          st->level++;
829   if (st->level == 1) {
830          st->nextBonus = kFirstBonus;
831   }
832
833   if (st->level > 3 && (st->level % 5 == 1)) {
834          if (st->bround) {
835                 st->bround = 0;
836                 DrawCities(st, xlim, ylim);
837          }
838          else {
839                 /* bonus round */
840                 st->bround = 1;
841                 st->levMissiles = 20 + st->level * 10;
842                 st->levFreq = 10;
843                 for (i=0;i<kNumCities;i++)
844                   st->blive[i] = st->city[i].alive;
845                 sprintf(buf, "Bonus Round");
846                 width = XTextWidth(st->font, buf, strlen(buf));
847                 XDrawString(st->dpy, st->window, st->level_gc, xlim / 2 - width / 2, ylim / 2 - font_height(st->font) / 2, buf, strlen(buf));
848                 XSync(st->dpy, False);
849                 usleep(1000000);
850                 XFillRectangle(st->dpy, st->window, st->erase_gc,
851                                                         0, 0, xlim, ylim - 100);
852          }
853   }
854
855  END_LEVEL: ;
856
857   if (!st->bround) {
858          st->levMissiles = 5 + st->level * 3;
859          if (st->level > 5)
860                 st->levMissiles += st->level * 5;
861          /*  levMissiles = 2; */
862          st->levFreq = 120 - st->level * 5;
863          if (st->levFreq < 30)
864                 st->levFreq = 30;
865   }
866
867   /* ready to fire */
868   st->lastLaser = 0;
869 }
870
871
872 static unsigned long
873 penetrate_draw (Display *dpy, Window window, void *closure)
874 {
875   struct state *st = (struct state *) closure;
876   XWindowAttributes xgwa;
877
878   if (st->draw_reset)
879     {
880       st->draw_reset = 0;
881       DrawCities(st, st->draw_xlim, st->draw_ylim);
882     }
883
884   XGetWindowAttributes(st->dpy, st->window, &xgwa);
885   st->draw_xlim = xgwa.width;
886   st->draw_ylim = xgwa.height;
887
888   /* see if just started */
889   if (st->loop == 0) {
890          if (st->smart) {
891                 st->choosypersen = st->econpersen = st->carefulpersen = 100;
892                 st->lrate = kMinRate; st->aim = 1;
893          }
894          NewLevel(st, st->draw_xlim, st->draw_ylim);
895          DrawScore(st, st->draw_xlim, st->draw_ylim);
896   }
897
898   st->loop++;
899
900   if (st->levMissiles == 0) {
901          /* see if anything's still on the screen, to know when to end level */
902          int i;
903          for (i=0;i<kMaxMissiles;i++)
904                 if (st->missile[i].alive)
905                   goto END_CHECK;
906          for (i=0;i<kMaxBooms;i++)
907                 if (st->boom[i].alive)
908                   goto END_CHECK;
909          for (i=0;i<kMaxLasers;i++)
910                 if (st->laser[i].alive)
911                   goto END_CHECK;
912          /* okay, nothing's alive, start end of level countdown */
913          usleep(kLevelPause*1000000);
914          NewLevel(st, st->draw_xlim, st->draw_ylim);
915          goto END;
916   END_CHECK: ;
917   }
918   else if ((random() % st->levFreq) == 0) {
919          launch(st, st->draw_xlim, st->draw_ylim, -1);
920          st->levMissiles--;
921   }
922
923   if (st->loop - st->lastLaser >= st->lrate) {
924          if (fire(st, st->draw_xlim, st->draw_ylim))
925                 st->lastLaser = st->loop;
926   }
927
928   if ((st->loop & 7) == 0)
929     st->draw_reset = 1;
930
931   LoopMissiles(st, st->draw_xlim, st->draw_ylim);
932   LoopLasers(st, st->draw_xlim, st->draw_ylim);
933   LoopBooms(st, st->draw_xlim, st->draw_ylim);
934
935  END:
936   return kSleepTime;
937 }
938
939 static void
940 penetrate_reshape (Display *dpy, Window window, void *closure, 
941                  unsigned int w, unsigned int h)
942 {
943 }
944
945 static Bool
946 penetrate_event (Display *dpy, Window window, void *closure, XEvent *event)
947 {
948   return False;
949 }
950
951 static void
952 penetrate_free (Display *dpy, Window window, void *closure)
953 {
954   struct state *st = (struct state *) closure;
955   free (st);
956 }
957
958
959 static const char *penetrate_defaults [] = {
960   ".background: black",
961   ".foreground: white",
962   "*bgrowth:    5",
963   "*lrate:      80",
964   "*smart:      False",
965   "*geometry:   800x500",
966   0
967 };
968
969 static XrmOptionDescRec penetrate_options [] = {
970   { "-bgrowth",         ".bgrowth",     XrmoptionSepArg, 0 },
971   { "-lrate",           ".lrate",       XrmoptionSepArg, 0 },
972   {"-smart",            ".smart",       XrmoptionNoArg, "True" },
973   { 0, 0, 0, 0 }
974 };
975
976 XSCREENSAVER_MODULE ("Penetrate", penetrate)