1 /* Copyright (c) 1999 Adam Miller adum@aya.yale.edu
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
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.
22 -- fixed an AI bug that was keeping the computer player a tad weak
28 #include "screenhack.h"
30 #define kSleepTime 10000
32 #define font_height(font) (font->ascent + font->descent)
34 #define kCityPause 500000
36 #define SCORE_MISSILE 100
37 #define kFirstBonus 5000
39 #define kMaxRadius 100
56 int x, y, rad, oflaser;
74 float velx, vely, fposx, fposy;
80 #define kMaxMissiles 256
82 #define kMaxLasers 128
86 #define kLaserLength 12
88 #define kMissileSpeed 0.003
89 #define kLaserSpeed (kMissileSpeed * 6)
96 XftFont *font, *scoreFont;
97 XftColor xft_fg, xft_level_fg;
100 unsigned int default_fg_pixel;
104 int lrate, startlrate;
106 long score, highscore;
119 Missile missile[kMaxMissiles];
120 Boom boom[kMaxBooms];
121 City city[kNumCities];
122 Laser laser[kMaxLasers];
123 int blive[kNumCities];
125 int level, levMissiles, levFreq;
127 int draw_xlim, draw_ylim;
133 static void Explode(struct state *st, int x, int y, int max, XColor color, int oflaser)
137 for (i=0;i<kMaxBooms;i++)
138 if (!st->boom[i].alive) {
149 if (max > kMaxRadius)
154 m->oflaser = oflaser;
157 static void launch (struct state *st, int xlim, int ylim, int src)
160 Missile *m = 0, *msrc;
161 for (i=0;i<kMaxMissiles;i++)
162 if (!st->missile[i].alive) {
170 m->startx = (random() % xlim);
174 m->jenis = random() % 360;
179 m->splits = random() % j;
180 if (m->splits < ylim * 0.08)
185 /* special if we're from another missile */
187 int dc = random() % (kNumCities - 1);
188 msrc = &st->missile[src];
189 if (dc == msrc->dcity)
194 if (m->starty > ylim * 0.4 || m->splits <= m->starty)
195 m->splits = 0; /* too far down already */
196 m->jenis = msrc->jenis;
199 m->dcity = random() % kNumCities;
200 m->endx = st->city[m->dcity].x + (random() % 20) - 10;
206 hsv_to_rgb (m->jenis, 1.0, 1.0,
207 &m->color.red, &m->color.green, &m->color.blue);
208 m->color.flags = DoRed | DoGreen | DoBlue;
209 if (!XAllocColor (st->dpy, st->cmap, &m->color)) {
210 m->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
211 m->color.red = m->color.green = m->color.blue = 0xFFFF;
217 #define kSpeedDiff 3.5
218 #define kMaxToGround 0.75
219 static int fire(struct state *st, int xlim, int ylim)
227 int choosy = 0, economic = 0, careful = 0;
228 int suitor[kMaxMissiles];
230 int ytargetmin = ylim * 0.75;
234 choosy = (random() % 100) < st->choosypersen;
235 economic = (random() % 100) < st->econpersen;
236 careful = (random() % 100) < st->carefulpersen;
238 /* count our cities */
239 for (i=0;i<kNumCities;i++)
240 livecity += st->city[i].alive;
242 return 1; /* no guns */
244 for (i=0;i<kMaxLasers;i++)
245 if (!st->laser[i].alive) {
252 /* if no missiles on target, no need to be choosy */
255 for (j=0;j<kMaxMissiles;j++) {
256 mis = &st->missile[j];
257 if (!mis->alive || (mis->y > ytargetmin))
259 if (st->city[mis->dcity].alive)
266 for (j=0;j<kMaxMissiles;j++) {
267 mis = &st->missile[j];
269 if (!mis->alive || (mis->y > ytargetmin))
271 if (choosy && (st->city[mis->dcity].alive == 0))
273 ey = mis->starty + ((float) (mis->endy - mis->starty)) * (mis->pos + kExpHelp + (1.0 - mis->pos) / kSpeedDiff);
274 if (ey > ylim * kMaxToGround)
275 continue; /* too far down */
280 /* count missiles that are on target and not being targeted */
281 if (choosy && economic)
282 for (j=0;j<kMaxMissiles;j++)
283 if (suitor[j] && st->missile[j].enemies == 0)
287 for (j=0;j<kMaxMissiles;j++) {
288 if (suitor[j] && cnt > 1)
289 if (st->missile[j].enemies > 0)
290 if (st->missile[j].enemies > 1 || untargeted == 0) {
294 /* who's closest? biggest threat */
295 if (suitor[j] && st->missile[j].y > deepest)
296 deepest = st->missile[j].y;
299 if (deepest > 0 && careful) {
300 /* only target deepest missile */
302 for (j=0;j<kMaxMissiles;j++)
303 if (suitor[j] && st->missile[j].y != deepest)
308 return 1; /* no targets available */
309 cnt = random() % cnt;
310 for (j=0;j<kMaxMissiles;j++)
313 mis = &st->missile[j];
319 return 1; /* shouldn't happen */
321 dcity = random() % livecity;
322 for (j=0;j<kNumCities;j++)
323 if (st->city[j].alive)
328 m->startx = st->city[dcity].x;
330 ex = mis->startx + ((float) (mis->endx - mis->startx)) * (mis->pos + kExpHelp + (1.0 - mis->pos) / kSpeedDiff);
331 ey = mis->starty + ((float) (mis->endy - mis->starty)) * (mis->pos + kExpHelp + (1.0 - mis->pos) / kSpeedDiff);
332 m->endx = ex + random() % 16 - 8 + (random() % st->aim) - st->aim / 2;
333 m->endy = ey + random() % 16 - 8 + (random() % st->aim) - st->aim / 2;
334 if (ey > ylim * kMaxToGround)
335 return 0; /* too far down */
346 dx = (m->endx - m->x);
347 dy = (m->endy - m->y);
348 m->velx = dx / 100.0;
349 m->vely = dy / 100.0;
351 /* m->lenMul = (kLaserLength * kLaserLength) / (m->velx * m->velx + m->vely * m->vely); */
352 m->lenMul = -(kLaserLength / m->vely);
355 m->color.blue = 0x0000;
356 m->color.green = 0xFFFF;
357 m->color.red = 0xFFFF;
358 m->color.flags = DoRed | DoGreen | DoBlue;
359 if (!XAllocColor (st->dpy, st->cmap, &m->color)) {
360 m->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
361 m->color.red = m->color.green = m->color.blue = 0xFFFF;
368 penetrate_init (Display *dpy, Window window)
370 struct state *st = (struct state *) calloc (1, sizeof(*st));
372 const char *levelfont = "monospace 38";
373 const char *scorefont = "sans-serif 18";
375 XWindowAttributes xgwa;
381 XGetWindowAttributes (st->dpy, st->window, &xgwa);
382 st->cmap = xgwa.colormap;
385 if (xgwa.width > 2560 || xgwa.height > 2560)
386 st->pscale *= 3; /* Retina displays */
389 st->nextBonus = kFirstBonus;
392 st->smart = get_boolean_resource(st->dpy, "smart","Boolean");
393 st->bgrowth = get_integer_resource (st->dpy, "bgrowth", "Integer");
394 st->lrate = get_integer_resource (st->dpy, "lrate", "Integer");
395 if (st->bgrowth < 0) st->bgrowth = 2;
396 if (st->lrate < 0) st->lrate = 2;
397 st->startlrate = st->lrate;
399 st->font = load_xft_font_retry(st->dpy, screen_number (xgwa.screen),
401 if (!st->font) abort();
403 st->scoreFont = load_xft_font_retry(st->dpy, screen_number (xgwa.screen),
405 if (!st->scoreFont) abort();
407 for (i = 0; i < kMaxMissiles; i++)
408 st->missile[i].alive = 0;
410 for (i = 0; i < kMaxLasers; i++)
411 st->laser[i].alive = 0;
413 for (i = 0; i < kMaxBooms; i++)
414 st->boom[i].alive = 0;
416 for (i = 0; i < kNumCities; i++) {
417 City *m = &st->city[i];
419 m->color.red = m->color.green = m->color.blue = 0xFFFF;
420 m->color.blue = 0x1111; m->color.green = 0x8888;
421 m->color.flags = DoRed | DoGreen | DoBlue;
422 if (!XAllocColor (st->dpy, st->cmap, &m->color)) {
423 m->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
424 m->color.red = m->color.green = m->color.blue = 0xFFFF;
428 gcv.foreground = st->default_fg_pixel =
429 get_pixel_resource(st->dpy, st->cmap, "foreground", "Foreground");
430 st->draw_gc = XCreateGC(st->dpy, st->window, GCForeground, &gcv);
431 gcv.foreground = get_pixel_resource(st->dpy, st->cmap, "background", "Background");
432 st->erase_gc = XCreateGC(st->dpy, st->window, GCForeground, &gcv);
433 s = get_string_resource (st->dpy, "foreground", "Foreground");
434 if (!s) s = strdup ("white");
435 XftColorAllocName (st->dpy, xgwa.visual, xgwa.colormap, s, &st->xft_fg);
438 /* Level color was st->city[0].color which is hardcoded as: */
440 XftColorAllocName (st->dpy, xgwa.visual, xgwa.colormap, s,
442 st->xftdraw = XftDrawCreate (st->dpy, window, xgwa.visual, xgwa.colormap);
445 jwxyz_XSetAntiAliasing (st->dpy, st->erase_gc, False);
446 jwxyz_XSetAntiAliasing (st->dpy, st->draw_gc, False);
450 /* make a gray color for score */
452 st->scoreColor.red = st->scoreColor.green = st->scoreColor.blue = 0xAAAA;
453 st->scoreColor.flags = DoRed | DoGreen | DoBlue;
454 if (!XAllocColor (st->dpy, st->cmap, &st->scoreColor)) {
455 st->scoreColor.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
456 st->scoreColor.red = st->scoreColor.green = st->scoreColor.blue = 0xFFFF;
460 XClearWindow(st->dpy, st->window);
464 static void DrawScore(struct state *st, int xlim, int ylim)
469 sprintf(buf, "%ld", st->score);
470 XftTextExtentsUtf8 (st->dpy, st->scoreFont, (FcChar8 *) buf,
471 strlen(buf), &overall);
472 width = overall.xOff;
473 height = font_height(st->scoreFont);
474 XSetForeground (st->dpy, st->draw_gc, st->scoreColor.pixel);
475 XFillRectangle(st->dpy, st->window, st->erase_gc,
476 xlim - width - 6, ylim - height - 2, width + 6, height + 2);
477 XftDrawStringUtf8 (st->xftdraw, &st->xft_fg, st->scoreFont,
478 xlim - width - 2, ylim - 2,
479 (FcChar8 *) buf, strlen(buf));
481 sprintf(buf, "%ld", st->highscore);
482 XftTextExtentsUtf8 (st->dpy, st->scoreFont, (FcChar8 *) buf,
483 strlen(buf), &overall);
484 width = overall.xOff;
485 XFillRectangle(st->dpy, st->window, st->erase_gc,
486 4, ylim - height - 2, width + 4, height + 2);
487 XftDrawStringUtf8 (st->xftdraw, &st->xft_fg, st->scoreFont,
489 (FcChar8 *) buf, strlen(buf));
492 static void AddScore(struct state *st, int xlim, int ylim, long dif)
495 for (i=0;i<kNumCities;i++)
496 sumlive += st->city[i].alive;
498 return; /* no cities, not possible to score */
501 if (st->score > st->highscore)
502 st->highscore = st->score;
503 DrawScore(st, xlim, ylim);
506 static void DrawCity(struct state *st, int x, int y, XColor col)
508 XSetForeground (st->dpy, st->draw_gc, col.pixel);
509 XFillRectangle(st->dpy, st->window, st->draw_gc,
510 x - 30 * st->pscale, y - 40 * st->pscale,
511 60 * st->pscale, 40 * st->pscale);
512 XFillRectangle(st->dpy, st->window, st->draw_gc,
513 x - 20 * st->pscale, y - 50 * st->pscale,
514 10 * st->pscale, 10 * st->pscale);
515 XFillRectangle(st->dpy, st->window, st->draw_gc,
516 x + 10 * st->pscale, y - 50 * st->pscale,
517 10 * st->pscale, 10 * st->pscale);
520 static void DrawCities(struct state *st, int xlim, int ylim)
523 for (i = 0; i < kNumCities; i++) {
524 City *m = &st->city[i];
527 x = (i + 1) * (xlim / (kNumCities + 1));
530 DrawCity(st, x, ylim, m->color);
534 static void LoopMissiles(struct state *st, int xlim, int ylim)
537 for (i = 0; i < kMaxMissiles; i++) {
539 Missile *m = &st->missile[i];
544 m->pos += kMissileSpeed;
545 m->x = m->startx + ((float) (m->endx - m->startx)) * m->pos;
546 m->y = m->starty + ((float) (m->endy - m->starty)) * m->pos;
550 XSetLineAttributes(st->dpy, st->draw_gc, 4*st->pscale, 0,0,0);
551 XSetForeground (st->dpy, st->draw_gc, m->color.pixel);
552 XDrawLine(st->dpy, st->window, st->draw_gc,
553 old_x, old_y, m->x, m->y);
555 /* maybe split off a new missile? */
556 if (m->splits && (m->y > m->splits)) {
558 launch(st, xlim, ylim, i);
563 if (st->city[m->dcity].alive) {
564 st->city[m->dcity].alive = 0;
565 Explode(st, m->x, m->y, kBoomRad * 2, m->color, 0);
569 /* check hitting explosions */
570 for (j=0;j<kMaxBooms;j++) {
571 Boom *b = &st->boom[j];
575 int dx = abs(m->x - b->x);
576 int dy = abs(m->y - b->y);
577 int r = b->rad + 2 * st->pscale;
578 if ((dx < r) && (dy < r))
579 if (dx * dx + dy * dy < r * r) {
581 max = b->max + st->bgrowth - kBoomRad;
582 AddScore(st, xlim, ylim, SCORE_MISSILE);
590 Explode(st, m->x, m->y, kBoomRad + max, m->color, 0);
591 XSetLineAttributes(st->dpy, st->erase_gc, 4*st->pscale, 0,0,0);
592 /* In a perfect world, we could simply erase a line from
593 (m->startx, m->starty) to (m->x, m->y). This is not a
597 my_pos = kMissileSpeed;
598 while (my_pos <= m->pos) {
599 m->x = m->startx + ((float) (m->endx - m->startx)) * my_pos;
600 m->y = m->starty + ((float) (m->endy - m->starty)) * my_pos;
601 XDrawLine(st->dpy, st->window, st->erase_gc, old_x, old_y, m->x, m->y);
604 my_pos += kMissileSpeed;
610 static void LoopLasers(struct state *st, int xlim, int ylim)
612 int i, j, miny = ylim * 0.8;
614 for (i = 0; i < kMaxLasers; i++) {
615 Laser *m = &st->laser[i];
620 XSetLineAttributes(st->dpy, st->erase_gc, 2*st->pscale, 0,0,0);
621 XDrawLine(st->dpy, st->window, st->erase_gc,
622 m->oldx2, m->oldy2, m->oldx, m->oldy);
630 x = m->fposx + (-m->velx * m->lenMul);
631 y = m->fposy + (-m->vely * m->lenMul);
636 XSetLineAttributes(st->dpy, st->draw_gc, 2*st->pscale, 0,0,0);
637 XSetForeground (st->dpy, st->draw_gc, m->color.pixel);
638 XDrawLine(st->dpy, st->window, st->draw_gc,
646 if (m->y < m->endy) {
650 /* check hitting explosions */
652 for (j=0;j<kMaxBooms;j++) {
653 Boom *b = &st->boom[j];
657 int dx = abs(m->x - b->x);
658 int dy = abs(m->y - b->y);
659 int r = b->rad + 2 * st->pscale;
662 if ((dx < r) && (dy < r))
663 if (dx * dx + dy * dy < r * r) {
665 /* one less enemy on this missile -- it probably didn't make it */
666 if (st->missile[m->target].alive)
667 st->missile[m->target].enemies--;
674 XDrawLine(st->dpy, st->window, st->erase_gc,
676 Explode(st, m->x, m->y, kBoomRad, m->color, 1);
681 static void LoopBooms(struct state *st, int xlim, int ylim)
684 for (i = 0; i < kMaxBooms; i++) {
685 Boom *m = &st->boom[i];
692 if (m->rad >= m->max)
694 XSetLineAttributes(st->dpy, st->draw_gc, 1*st->pscale, 0,0,0);
695 XSetForeground (st->dpy, st->draw_gc, m->color.pixel);
696 XDrawArc(st->dpy, st->window, st->draw_gc,
697 m->x - m->rad * st->pscale,
698 m->y - m->rad * st->pscale,
699 m->rad * 2 * st->pscale,
700 m->rad * 2 * st->pscale,
704 XSetLineAttributes(st->dpy, st->erase_gc, 1*st->pscale, 0,0,0);
705 XDrawArc(st->dpy, st->window, st->erase_gc,
706 m->x - m->rad * st->pscale,
707 m->y - m->rad * st->pscale,
708 m->rad * 2 * st->pscale,
709 m->rad * 2 * st->pscale,
720 /* after they die, let's change a few things */
721 static void Improve(struct state *st)
726 return; /* no need, really */
728 if (st->level <= 2) st->aim -= 8;
729 if (st->level <= 5) st->aim -= 6;
732 st->carefulpersen += 6;
733 st->choosypersen += 4;
734 if (st->level <= 5) st->choosypersen += 3;
737 if (st->startlrate < kMinRate) {
738 if (st->lrate < st->startlrate)
739 st->lrate = st->startlrate;
742 if (st->lrate < kMinRate)
743 st->lrate = kMinRate;
745 if (st->level <= 5) st->econpersen += 3;
746 if (st->aim < 1) st->aim = 1;
747 if (st->choosypersen > 100) st->choosypersen = 100;
748 if (st->carefulpersen > 100) st->carefulpersen = 100;
749 if (st->econpersen > 100) st->econpersen = 100;
752 static void NewLevel(struct state *st, int xlim, int ylim)
755 int width, i, sumlive = 0;
760 if (st->level == 0) {
765 /* check for a free city */
766 if (st->score >= st->nextBonus) {
768 st->nextBonus += kFirstBonus * st->numBonus;
772 for (i=0;i<kNumCities;i++) {
774 st->city[i].alive = st->blive[i];
775 liv[i] = st->city[i].alive;
778 st->city[i].alive = 0;
781 /* print out screen */
782 XFillRectangle(st->dpy, st->window, st->erase_gc,
785 sprintf(buf, "Bonus Round Over");
787 if (sumlive || freecity)
788 sprintf(buf, "Level %d Cleared", st->level);
790 sprintf(buf, "GAME OVER");
793 XftTextExtentsUtf8 (st->dpy, st->font, (FcChar8 *) buf,
794 strlen(buf), &overall);
795 width = overall.xOff;
796 XftDrawStringUtf8 (st->xftdraw, &st->xft_level_fg, st->font,
797 xlim / 2 - width / 2, ylim / 2 - font_height(st->font) / 2,
798 (FcChar8 *) buf, strlen(buf));
799 XSync(st->dpy, False);
804 if (sumlive || freecity) {
806 /* draw live cities */
807 XFillRectangle(st->dpy, st->window, st->erase_gc,
808 0, ylim - 100 * st->pscale, xlim, 100 * st->pscale);
810 sprintf(buf, "X %ld", st->level * 100L);
811 /* how much they get */
812 XftTextExtentsUtf8 (st->dpy, st->font, (FcChar8 *) buf,
813 strlen(buf), &overall);
814 sumwidth = overall.xOff;
815 /* add width of city */
819 DrawCity(st, xlim / 2 - sumwidth / 2 + 30, ylim * 0.70, st->city[0].color);
820 XftDrawStringUtf8 (st->xftdraw, &st->xft_level_fg, st->font,
821 xlim / 2 - sumwidth / 2 + 40 + 60,
823 (FcChar8 *) buf, strlen(buf));
824 for (i=0;i<kNumCities;i++) {
826 st->city[i].alive = 1;
827 AddScore(st, xlim, ylim, 100 * st->level);
828 DrawCities(st, xlim, ylim);
829 XSync(st->dpy, False);
841 for (i=0;i<kNumCities;i++)
842 st->city[i].alive = 1;
846 st->nextBonus = kFirstBonus;
848 DrawCities(st, xlim, ylim);
852 /* do free city part */
853 if (freecity && sumlive < 5) {
854 int ncnt = random() % (5 - sumlive) + 1;
855 for (i=0;i<kNumCities;i++)
856 if (!st->city[i].alive)
858 st->city[i].alive = 1;
859 strcpy(buf, "Bonus City");
860 XftTextExtentsUtf8 (st->dpy, st->font, (FcChar8 *) buf,
861 strlen(buf), &overall);
862 width = overall.xOff;
863 XftDrawStringUtf8 (st->xftdraw, &st->xft_level_fg, st->font,
864 xlim / 2 - width / 2, ylim / 4,
865 (FcChar8 *) buf, strlen(buf));
866 DrawCities(st, xlim, ylim);
867 XSync(st->dpy, False);
871 XFillRectangle(st->dpy, st->window, st->erase_gc,
872 0, 0, xlim, ylim - 100 * st->pscale);
876 if (st->level == 1) {
877 st->nextBonus = kFirstBonus;
880 if (st->level > 3 && (st->level % 5 == 1)) {
883 DrawCities(st, xlim, ylim);
888 st->levMissiles = 20 + st->level * 10;
890 for (i=0;i<kNumCities;i++)
891 st->blive[i] = st->city[i].alive;
892 sprintf(buf, "Bonus Round");
893 XftTextExtentsUtf8 (st->dpy, st->font, (FcChar8 *) buf,
894 strlen(buf), &overall);
895 width = overall.xOff;
896 XftDrawStringUtf8 (st->xftdraw, &st->xft_level_fg, st->font,
897 xlim / 2 - width / 2,
898 ylim / 2 - font_height(st->font) / 2,
899 (FcChar8 *) buf, strlen(buf));
900 XSync(st->dpy, False);
902 XFillRectangle(st->dpy, st->window, st->erase_gc,
903 0, 0, xlim, ylim - 100 * st->pscale);
910 st->levMissiles = 5 + st->level * 3;
912 st->levMissiles += st->level * 5;
913 /* levMissiles = 2; */
914 st->levFreq = 120 - st->level * 5;
915 if (st->levFreq < 30)
925 penetrate_draw (Display *dpy, Window window, void *closure)
927 struct state *st = (struct state *) closure;
928 XWindowAttributes xgwa;
933 DrawCities(st, st->draw_xlim, st->draw_ylim);
936 XGetWindowAttributes(st->dpy, st->window, &xgwa);
937 st->draw_xlim = xgwa.width;
938 st->draw_ylim = xgwa.height;
940 /* see if just started */
943 st->choosypersen = st->econpersen = st->carefulpersen = 100;
944 st->lrate = kMinRate; st->aim = 1;
946 NewLevel(st, st->draw_xlim, st->draw_ylim);
947 DrawScore(st, st->draw_xlim, st->draw_ylim);
952 if (st->levMissiles == 0) {
953 /* see if anything's still on the screen, to know when to end level */
955 for (i=0;i<kMaxMissiles;i++)
956 if (st->missile[i].alive)
958 for (i=0;i<kMaxBooms;i++)
959 if (st->boom[i].alive)
961 for (i=0;i<kMaxLasers;i++)
962 if (st->laser[i].alive)
964 /* okay, nothing's alive, start end of level countdown */
965 usleep(kLevelPause*1000000);
966 NewLevel(st, st->draw_xlim, st->draw_ylim);
970 else if ((random() % st->levFreq) == 0) {
971 launch(st, st->draw_xlim, st->draw_ylim, -1);
975 if (st->loop - st->lastLaser >= st->lrate) {
976 if (fire(st, st->draw_xlim, st->draw_ylim))
977 st->lastLaser = st->loop;
980 if ((st->loop & 7) == 0)
983 LoopMissiles(st, st->draw_xlim, st->draw_ylim);
984 LoopLasers(st, st->draw_xlim, st->draw_ylim);
985 LoopBooms(st, st->draw_xlim, st->draw_ylim);
992 penetrate_reshape (Display *dpy, Window window, void *closure,
993 unsigned int w, unsigned int h)
995 XClearWindow (dpy, window);
999 penetrate_event (Display *dpy, Window window, void *closure, XEvent *event)
1005 penetrate_free (Display *dpy, Window window, void *closure)
1007 struct state *st = (struct state *) closure;
1008 XFreeGC (dpy, st->draw_gc);
1009 XFreeGC (dpy, st->erase_gc);
1010 XftFontClose (st->dpy, st->font);
1011 XftFontClose (st->dpy, st->scoreFont);
1012 XftDrawDestroy (st->xftdraw);
1017 static const char *penetrate_defaults [] = {
1018 /* ".lowrez: true", */
1019 ".background: black",
1020 ".foreground: white",
1029 static XrmOptionDescRec penetrate_options [] = {
1030 { "-bgrowth", ".bgrowth", XrmoptionSepArg, 0 },
1031 { "-lrate", ".lrate", XrmoptionSepArg, 0 },
1032 {"-smart", ".smart", XrmoptionNoArg, "True" },
1036 XSCREENSAVER_MODULE ("Penetrate", penetrate)