summaryrefslogtreecommitdiffstats
path: root/hacks/penetrate.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/penetrate.c')
-rw-r--r--hacks/penetrate.c977
1 files changed, 977 insertions, 0 deletions
diff --git a/hacks/penetrate.c b/hacks/penetrate.c
new file mode 100644
index 0000000..9148827
--- /dev/null
+++ b/hacks/penetrate.c
@@ -0,0 +1,977 @@
+/* Copyright (c) 1999 Adam Miller adum@aya.yale.edu
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+
+ * penetrate simulates the arcade classic with the cities and the stuff
+ * shooting down from the sky and stuff. The computer plays against itself,
+ * desperately defending the forces of good against those thingies raining
+ * down. Bonus cities are awarded at ever-increasing intervals. Every five
+ * levels appears a bonus round. The computer player gets progressively
+ * more intelligent as the game progresses. Better aim, more economical with
+ * ammo, and better target selection. Points are in the bottom right, and
+ * high score is in the bottom left. Start with -smart to have the computer
+ * player skip the learning process.
+
+ Version: 0.2
+ -- fixed an AI bug that was keeping the computer player a tad weak
+ Version: 0.1
+ -- first release
+
+ */
+
+#include "screenhack.h"
+
+#define kSleepTime 10000
+
+#define font_height(font) (font->ascent + font->descent)
+
+#define kCityPause 500000
+#define kLevelPause 1
+#define SCORE_MISSILE 100
+#define kFirstBonus 5000
+#define kMinRate 30
+#define kMaxRadius 100
+
+typedef struct {
+ int alive;
+ int x, y;
+ int startx, starty;
+ int endx, endy;
+ int dcity;
+ float pos;
+ int enemies;
+ int jenis;
+ int splits;
+ XColor color;
+} Missile;
+
+typedef struct {
+ int alive;
+ int x, y, rad, oflaser;
+ int max, outgoing;
+ XColor color;
+} Boom;
+
+typedef struct {
+ int alive;
+ int x;
+ XColor color;
+} City;
+
+typedef struct {
+ int alive;
+ int x, y;
+ int startx, starty;
+ int endx, endy;
+ int oldx, oldy;
+ int oldx2, oldy2;
+ float velx, vely, fposx, fposy;
+ float lenMul;
+ XColor color;
+ int target;
+} Laser;
+
+#define kMaxMissiles 256
+#define kMaxBooms 512
+#define kMaxLasers 128
+#define kBoomRad 40
+#define kNumCities 5
+
+#define kLaserLength 12
+
+#define kMissileSpeed 0.003
+#define kLaserSpeed (kMissileSpeed * 6)
+
+
+struct state {
+ Display *dpy;
+ Window window;
+
+ XFontStruct *font, *scoreFont;
+ GC draw_gc, erase_gc, level_gc;
+ unsigned int default_fg_pixel;
+ XColor scoreColor;
+
+ int bgrowth;
+ int lrate, startlrate;
+ long loop;
+ long score, highscore;
+ long nextBonus;
+ int numBonus;
+ int bround;
+ long lastLaser;
+ int gamez;
+ int aim;
+ int econpersen;
+ int choosypersen;
+ int carefulpersen;
+ int smart;
+ Colormap cmap;
+
+ Missile missile[kMaxMissiles];
+ Boom boom[kMaxBooms];
+ City city[kNumCities];
+ Laser laser[kMaxLasers];
+ int blive[kNumCities];
+
+ int level, levMissiles, levFreq;
+
+ int draw_xlim, draw_ylim;
+ int draw_reset;
+};
+
+
+static void Explode(struct state *st, int x, int y, int max, XColor color, int oflaser)
+{
+ int i;
+ Boom *m = 0;
+ for (i=0;i<kMaxBooms;i++)
+ if (!st->boom[i].alive) {
+ m = &st->boom[i];
+ break;
+ }
+ if (!m)
+ return;
+
+ m->alive = 1;
+ m->x = x;
+ m->y = y;
+ m->rad = 0;
+ if (max > kMaxRadius)
+ max = kMaxRadius;
+ m->max = max;
+ m->outgoing = 1;
+ m->color = color;
+ m->oflaser = oflaser;
+}
+
+static void launch (struct state *st, int xlim, int ylim, int src)
+{
+ int i;
+ Missile *m = 0, *msrc;
+ for (i=0;i<kMaxMissiles;i++)
+ if (!st->missile[i].alive) {
+ m = &st->missile[i];
+ break;
+ }
+ if (!m)
+ return;
+
+ m->alive = 1;
+ m->startx = (random() % xlim);
+ m->starty = 0;
+ m->endy = ylim;
+ m->pos = 0.0;
+ m->jenis = random() % 360;
+ m->splits = 0;
+ if (m->jenis < 50) {
+ int j = ylim * 0.4;
+ if (j) {
+ m->splits = random() % j;
+ if (m->splits < ylim * 0.08)
+ m->splits = 0;
+ }
+ }
+
+ /* special if we're from another missile */
+ if (src >= 0) {
+ int dc = random() % (kNumCities - 1);
+ msrc = &st->missile[src];
+ if (dc == msrc->dcity)
+ dc++;
+ m->dcity = dc;
+ m->startx = msrc->x;
+ m->starty = msrc->y;
+ if (m->starty > ylim * 0.4 || m->splits <= m->starty)
+ m->splits = 0; /* too far down already */
+ m->jenis = msrc->jenis;
+ }
+ else
+ m->dcity = random() % kNumCities;
+ m->endx = st->city[m->dcity].x + (random() % 20) - 10;
+ m->x = m->startx;
+ m->y = m->starty;
+ m->enemies = 0;
+
+ if (!mono_p) {
+ hsv_to_rgb (m->jenis, 1.0, 1.0,
+ &m->color.red, &m->color.green, &m->color.blue);
+ m->color.flags = DoRed | DoGreen | DoBlue;
+ if (!XAllocColor (st->dpy, st->cmap, &m->color)) {
+ m->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
+ m->color.red = m->color.green = m->color.blue = 0xFFFF;
+ }
+ }
+}
+
+#define kExpHelp 0.2
+#define kSpeedDiff 3.5
+#define kMaxToGround 0.75
+static int fire(struct state *st, int xlim, int ylim)
+{
+ int i, j, cnt = 0;
+ int dcity;
+ long dx, dy, ex, ey;
+ Missile *mis = 0;
+ Laser *m = 0;
+ int untargeted = 0;
+ int choosy = 0, economic = 0, careful = 0;
+ int suitor[kMaxMissiles];
+ int livecity = 0;
+ int ytargetmin = ylim * 0.75;
+ int deepest = 0;
+ int misnum = 0;
+
+ choosy = (random() % 100) < st->choosypersen;
+ economic = (random() % 100) < st->econpersen;
+ careful = (random() % 100) < st->carefulpersen;
+
+ /* count our cities */
+ for (i=0;i<kNumCities;i++)
+ livecity += st->city[i].alive;
+ if (livecity == 0)
+ return 1; /* no guns */
+
+ for (i=0;i<kMaxLasers;i++)
+ if (!st->laser[i].alive) {
+ m = &st->laser[i];
+ break;
+ }
+ if (!m)
+ return 1;
+
+ /* if no missiles on target, no need to be choosy */
+ if (choosy) {
+ int choo = 0;
+ for (j=0;j<kMaxMissiles;j++) {
+ mis = &st->missile[j];
+ if (!mis->alive || (mis->y > ytargetmin))
+ continue;
+ if (st->city[mis->dcity].alive)
+ choo++;
+ }
+ if (choo == 0)
+ choosy = 0;
+ }
+
+ for (j=0;j<kMaxMissiles;j++) {
+ mis = &st->missile[j];
+ suitor[j] = 0;
+ if (!mis->alive || (mis->y > ytargetmin))
+ continue;
+ if (choosy && (st->city[mis->dcity].alive == 0))
+ continue;
+ ey = mis->starty + ((float) (mis->endy - mis->starty)) * (mis->pos + kExpHelp + (1.0 - mis->pos) / kSpeedDiff);
+ if (ey > ylim * kMaxToGround)
+ continue; /* too far down */
+ cnt++;
+ suitor[j] = 1;
+ }
+
+ /* count missiles that are on target and not being targeted */
+ if (choosy && economic)
+ for (j=0;j<kMaxMissiles;j++)
+ if (suitor[j] && st->missile[j].enemies == 0)
+ untargeted++;
+
+ if (economic)
+ for (j=0;j<kMaxMissiles;j++) {
+ if (suitor[j] && cnt > 1)
+ if (st->missile[j].enemies > 0)
+ if (st->missile[j].enemies > 1 || untargeted == 0) {
+ suitor[j] = 0;
+ cnt--;
+ }
+ /* who's closest? biggest threat */
+ if (suitor[j] && st->missile[j].y > deepest)
+ deepest = st->missile[j].y;
+ }
+
+ if (deepest > 0 && careful) {
+ /* only target deepest missile */
+ cnt = 1;
+ for (j=0;j<kMaxMissiles;j++)
+ if (suitor[j] && st->missile[j].y != deepest)
+ suitor[j] = 0;
+ }
+
+ if (cnt == 0)
+ return 1; /* no targets available */
+ cnt = random() % cnt;
+ for (j=0;j<kMaxMissiles;j++)
+ if (suitor[j])
+ if (cnt-- == 0) {
+ mis = &st->missile[j];
+ misnum = j;
+ break;
+ }
+
+ if (mis == 0)
+ return 1; /* shouldn't happen */
+
+ dcity = random() % livecity;
+ for (j=0;j<kNumCities;j++)
+ if (st->city[j].alive)
+ if (dcity-- == 0) {
+ dcity = j;
+ break;
+ }
+ m->startx = st->city[dcity].x;
+ m->starty = ylim;
+ ex = mis->startx + ((float) (mis->endx - mis->startx)) * (mis->pos + kExpHelp + (1.0 - mis->pos) / kSpeedDiff);
+ ey = mis->starty + ((float) (mis->endy - mis->starty)) * (mis->pos + kExpHelp + (1.0 - mis->pos) / kSpeedDiff);
+ m->endx = ex + random() % 16 - 8 + (random() % st->aim) - st->aim / 2;
+ m->endy = ey + random() % 16 - 8 + (random() % st->aim) - st->aim / 2;
+ if (ey > ylim * kMaxToGround)
+ return 0; /* too far down */
+ mis->enemies++;
+ m->target = misnum;
+ m->x = m->startx;
+ m->y = m->starty;
+ m->oldx = -1;
+ m->oldy = -1;
+ m->oldx2 = -1;
+ m->oldy2 = -1;
+ m->fposx = m->x;
+ m->fposy = m->y;
+ dx = (m->endx - m->x);
+ dy = (m->endy - m->y);
+ m->velx = dx / 100.0;
+ m->vely = dy / 100.0;
+ m->alive = 1;
+ /* m->lenMul = (kLaserLength * kLaserLength) / (m->velx * m->velx + m->vely * m->vely); */
+ m->lenMul = -(kLaserLength / m->vely);
+
+ if (!mono_p) {
+ m->color.blue = 0x0000;
+ m->color.green = 0xFFFF;
+ m->color.red = 0xFFFF;
+ m->color.flags = DoRed | DoGreen | DoBlue;
+ if (!XAllocColor (st->dpy, st->cmap, &m->color)) {
+ m->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
+ m->color.red = m->color.green = m->color.blue = 0xFFFF;
+ }
+ }
+ return 1;
+}
+
+static void *
+penetrate_init (Display *dpy, Window window)
+{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
+ int i;
+ const char *levelfont = "-*-courier-*-r-*-*-*-380-*-*-*-*-*-*";
+ const char *scorefont = "-*-helvetica-*-r-*-*-*-180-*-*-*-*-*-*";
+ XGCValues gcv;
+ XWindowAttributes xgwa;
+
+ st->dpy = dpy;
+ st->window = window;
+
+ XGetWindowAttributes (st->dpy, st->window, &xgwa);
+ st->cmap = xgwa.colormap;
+
+ st->lrate = 80;
+ st->nextBonus = kFirstBonus;
+ st->aim = 180;
+
+ st->smart = get_boolean_resource(st->dpy, "smart","Boolean");
+ st->bgrowth = get_integer_resource (st->dpy, "bgrowth", "Integer");
+ st->lrate = get_integer_resource (st->dpy, "lrate", "Integer");
+ if (st->bgrowth < 0) st->bgrowth = 2;
+ if (st->lrate < 0) st->lrate = 2;
+ st->startlrate = st->lrate;
+
+ st->font = load_font_retry(st->dpy, levelfont);
+ if (!st->font) abort();
+
+ st->scoreFont = load_font_retry(st->dpy, scorefont);
+ if (!st->scoreFont) abort();
+
+ for (i = 0; i < kMaxMissiles; i++)
+ st->missile[i].alive = 0;
+
+ for (i = 0; i < kMaxLasers; i++)
+ st->laser[i].alive = 0;
+
+ for (i = 0; i < kMaxBooms; i++)
+ st->boom[i].alive = 0;
+
+ for (i = 0; i < kNumCities; i++) {
+ City *m = &st->city[i];
+ m->alive = 1;
+ m->color.red = m->color.green = m->color.blue = 0xFFFF;
+ m->color.blue = 0x1111; m->color.green = 0x8888;
+ m->color.flags = DoRed | DoGreen | DoBlue;
+ if (!XAllocColor (st->dpy, st->cmap, &m->color)) {
+ m->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
+ m->color.red = m->color.green = m->color.blue = 0xFFFF;
+ }
+ }
+
+ gcv.foreground = st->default_fg_pixel =
+ get_pixel_resource(st->dpy, st->cmap, "foreground", "Foreground");
+ gcv.font = st->scoreFont->fid;
+ st->draw_gc = XCreateGC(st->dpy, st->window, GCForeground | GCFont, &gcv);
+ gcv.font = st->font->fid;
+ st->level_gc = XCreateGC(st->dpy, st->window, GCForeground | GCFont, &gcv);
+ XSetForeground (st->dpy, st->level_gc, st->city[0].color.pixel);
+ gcv.foreground = get_pixel_resource(st->dpy, st->cmap, "background", "Background");
+ st->erase_gc = XCreateGC(st->dpy, st->window, GCForeground, &gcv);
+
+# ifdef HAVE_JWXYZ
+ jwxyz_XSetAntiAliasing (st->dpy, st->erase_gc, False);
+ jwxyz_XSetAntiAliasing (st->dpy, st->draw_gc, False);
+# endif
+
+
+ /* make a gray color for score */
+ if (!mono_p) {
+ st->scoreColor.red = st->scoreColor.green = st->scoreColor.blue = 0xAAAA;
+ st->scoreColor.flags = DoRed | DoGreen | DoBlue;
+ if (!XAllocColor (st->dpy, st->cmap, &st->scoreColor)) {
+ st->scoreColor.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
+ st->scoreColor.red = st->scoreColor.green = st->scoreColor.blue = 0xFFFF;
+ }
+ }
+
+ XClearWindow(st->dpy, st->window);
+ return st;
+}
+
+static void DrawScore(struct state *st, int xlim, int ylim)
+{
+ char buf[16];
+ int width, height;
+ sprintf(buf, "%ld", st->score);
+ width = XTextWidth(st->scoreFont, buf, strlen(buf));
+ height = font_height(st->scoreFont);
+ XSetForeground (st->dpy, st->draw_gc, st->scoreColor.pixel);
+ XFillRectangle(st->dpy, st->window, st->erase_gc,
+ xlim - width - 6, ylim - height - 2, width + 6, height + 2);
+ XDrawString(st->dpy, st->window, st->draw_gc, xlim - width - 2, ylim - 2,
+ buf, strlen(buf));
+
+ sprintf(buf, "%ld", st->highscore);
+ width = XTextWidth(st->scoreFont, buf, strlen(buf));
+ XFillRectangle(st->dpy, st->window, st->erase_gc,
+ 4, ylim - height - 2, width + 4, height + 2);
+ XDrawString(st->dpy, st->window, st->draw_gc, 4, ylim - 2,
+ buf, strlen(buf));
+}
+
+static void AddScore(struct state *st, int xlim, int ylim, long dif)
+{
+ int i, sumlive = 0;
+ for (i=0;i<kNumCities;i++)
+ sumlive += st->city[i].alive;
+ if (sumlive == 0)
+ return; /* no cities, not possible to score */
+
+ st->score += dif;
+ if (st->score > st->highscore)
+ st->highscore = st->score;
+ DrawScore(st, xlim, ylim);
+}
+
+static void DrawCity(struct state *st, int x, int y, XColor col)
+{
+ XSetForeground (st->dpy, st->draw_gc, col.pixel);
+ XFillRectangle(st->dpy, st->window, st->draw_gc,
+ x - 30, y - 40, 60, 40);
+ XFillRectangle(st->dpy, st->window, st->draw_gc,
+ x - 20, y - 50, 10, 10);
+ XFillRectangle(st->dpy, st->window, st->draw_gc,
+ x + 10, y - 50, 10, 10);
+}
+
+static void DrawCities(struct state *st, int xlim, int ylim)
+{
+ int i, x;
+ for (i = 0; i < kNumCities; i++) {
+ City *m = &st->city[i];
+ if (!m->alive)
+ continue;
+ x = (i + 1) * (xlim / (kNumCities + 1));
+ m->x = x;
+
+ DrawCity(st, x, ylim, m->color);
+ }
+}
+
+static void LoopMissiles(struct state *st, int xlim, int ylim)
+{
+ int i, j, max = 0;
+ for (i = 0; i < kMaxMissiles; i++) {
+ int old_x, old_y;
+ Missile *m = &st->missile[i];
+ if (!m->alive)
+ continue;
+ old_x = m->x;
+ old_y = m->y;
+ m->pos += kMissileSpeed;
+ m->x = m->startx + ((float) (m->endx - m->startx)) * m->pos;
+ m->y = m->starty + ((float) (m->endy - m->starty)) * m->pos;
+
+ /* erase old one */
+
+ XSetLineAttributes(st->dpy, st->draw_gc, 4, 0,0,0);
+ XSetForeground (st->dpy, st->draw_gc, m->color.pixel);
+ XDrawLine(st->dpy, st->window, st->draw_gc,
+ old_x, old_y, m->x, m->y);
+
+ /* maybe split off a new missile? */
+ if (m->splits && (m->y > m->splits)) {
+ m->splits = 0;
+ launch(st, xlim, ylim, i);
+ }
+
+ if (m->y >= ylim) {
+ m->alive = 0;
+ if (st->city[m->dcity].alive) {
+ st->city[m->dcity].alive = 0;
+ Explode(st, m->x, m->y, kBoomRad * 2, m->color, 0);
+ }
+ }
+
+ /* check hitting explosions */
+ for (j=0;j<kMaxBooms;j++) {
+ Boom *b = &st->boom[j];
+ if (!b->alive)
+ continue;
+ else {
+ int dx = abs(m->x - b->x);
+ int dy = abs(m->y - b->y);
+ int r = b->rad + 2;
+ if ((dx < r) && (dy < r))
+ if (dx * dx + dy * dy < r * r) {
+ m->alive = 0;
+ max = b->max + st->bgrowth - kBoomRad;
+ AddScore(st, xlim, ylim, SCORE_MISSILE);
+ }
+ }
+ }
+
+ if (m->alive == 0) {
+ float my_pos;
+ /* we just died */
+ Explode(st, m->x, m->y, kBoomRad + max, m->color, 0);
+ XSetLineAttributes(st->dpy, st->erase_gc, 4, 0,0,0);
+ /* In a perfect world, we could simply erase a line from
+ (m->startx, m->starty) to (m->x, m->y). This is not a
+ perfect world. */
+ old_x = m->startx;
+ old_y = m->starty;
+ my_pos = kMissileSpeed;
+ while (my_pos <= m->pos) {
+ m->x = m->startx + ((float) (m->endx - m->startx)) * my_pos;
+ m->y = m->starty + ((float) (m->endy - m->starty)) * my_pos;
+ XDrawLine(st->dpy, st->window, st->erase_gc, old_x, old_y, m->x, m->y);
+ old_x = m->x;
+ old_y = m->y;
+ my_pos += kMissileSpeed;
+ }
+ }
+ }
+}
+
+static void LoopLasers(struct state *st, int xlim, int ylim)
+{
+ int i, j, miny = ylim * 0.8;
+ int x, y;
+ for (i = 0; i < kMaxLasers; i++) {
+ Laser *m = &st->laser[i];
+ if (!m->alive)
+ continue;
+
+ if (m->oldx != -1) {
+ XSetLineAttributes(st->dpy, st->erase_gc, 2, 0,0,0);
+ XDrawLine(st->dpy, st->window, st->erase_gc,
+ m->oldx2, m->oldy2, m->oldx, m->oldy);
+ }
+
+ m->fposx += m->velx;
+ m->fposy += m->vely;
+ m->x = m->fposx;
+ m->y = m->fposy;
+
+ x = m->fposx + (-m->velx * m->lenMul);
+ y = m->fposy + (-m->vely * m->lenMul);
+
+ m->oldx = x;
+ m->oldy = y;
+
+ XSetLineAttributes(st->dpy, st->draw_gc, 2, 0,0,0);
+ XSetForeground (st->dpy, st->draw_gc, m->color.pixel);
+ XDrawLine(st->dpy, st->window, st->draw_gc,
+ m->x, m->y, x, y);
+
+ m->oldx2 = m->x;
+ m->oldy2 = m->y;
+ m->oldx = x;
+ m->oldy = y;
+
+ if (m->y < m->endy) {
+ m->alive = 0;
+ }
+
+ /* check hitting explosions */
+ if (m->y < miny)
+ for (j=0;j<kMaxBooms;j++) {
+ Boom *b = &st->boom[j];
+ if (!b->alive)
+ continue;
+ else {
+ int dx = abs(m->x - b->x);
+ int dy = abs(m->y - b->y);
+ int r = b->rad + 2;
+ if (b->oflaser)
+ continue;
+ if ((dx < r) && (dy < r))
+ if (dx * dx + dy * dy < r * r) {
+ m->alive = 0;
+ /* one less enemy on this missile -- it probably didn't make it */
+ if (st->missile[m->target].alive)
+ st->missile[m->target].enemies--;
+ }
+ }
+ }
+
+ if (m->alive == 0) {
+ /* we just died */
+ XDrawLine(st->dpy, st->window, st->erase_gc,
+ m->x, m->y, x, y);
+ Explode(st, m->x, m->y, kBoomRad, m->color, 1);
+ }
+ }
+}
+
+static void LoopBooms(struct state *st, int xlim, int ylim)
+{
+ int i;
+ for (i = 0; i < kMaxBooms; i++) {
+ Boom *m = &st->boom[i];
+ if (!m->alive)
+ continue;
+
+ if (st->loop & 1) {
+ if (m->outgoing) {
+ m->rad++;
+ if (m->rad >= m->max)
+ m->outgoing = 0;
+ XSetLineAttributes(st->dpy, st->draw_gc, 1, 0,0,0);
+ XSetForeground (st->dpy, st->draw_gc, m->color.pixel);
+ 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);
+ }
+ else {
+ XSetLineAttributes(st->dpy, st->erase_gc, 1, 0,0,0);
+ 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);
+ m->rad--;
+ if (m->rad <= 0)
+ m->alive = 0;
+ }
+ }
+ }
+}
+
+
+/* after they die, let's change a few things */
+static void Improve(struct state *st)
+{
+ if (st->smart)
+ return;
+ if (st->level > 20)
+ return; /* no need, really */
+ st->aim -= 4;
+ if (st->level <= 2) st->aim -= 8;
+ if (st->level <= 5) st->aim -= 6;
+ if (st->gamez < 3)
+ st->aim -= 10;
+ st->carefulpersen += 6;
+ st->choosypersen += 4;
+ if (st->level <= 5) st->choosypersen += 3;
+ st->econpersen += 4;
+ st->lrate -= 2;
+ if (st->startlrate < kMinRate) {
+ if (st->lrate < st->startlrate)
+ st->lrate = st->startlrate;
+ }
+ else {
+ if (st->lrate < kMinRate)
+ st->lrate = kMinRate;
+ }
+ if (st->level <= 5) st->econpersen += 3;
+ if (st->aim < 1) st->aim = 1;
+ if (st->choosypersen > 100) st->choosypersen = 100;
+ if (st->carefulpersen > 100) st->carefulpersen = 100;
+ if (st->econpersen > 100) st->econpersen = 100;
+}
+
+static void NewLevel(struct state *st, int xlim, int ylim)
+{
+ char buf[32];
+ int width, i, sumlive = 0;
+ int liv[kNumCities];
+ int freecity = 0;
+
+ if (st->level == 0) {
+ st->level++;
+ goto END_LEVEL;
+ }
+
+ /* check for a free city */
+ if (st->score >= st->nextBonus) {
+ st->numBonus++;
+ st->nextBonus += kFirstBonus * st->numBonus;
+ freecity = 1;
+ }
+
+ for (i=0;i<kNumCities;i++) {
+ if (st->bround)
+ st->city[i].alive = st->blive[i];
+ liv[i] = st->city[i].alive;
+ sumlive += liv[i];
+ if (!st->bround)
+ st->city[i].alive = 0;
+ }
+
+ /* print out screen */
+ XFillRectangle(st->dpy, st->window, st->erase_gc,
+ 0, 0, xlim, ylim);
+ if (st->bround)
+ sprintf(buf, "Bonus Round Over");
+ else {
+ if (sumlive || freecity)
+ sprintf(buf, "Level %d Cleared", st->level);
+ else
+ sprintf(buf, "GAME OVER");
+ }
+ if (st->level > 0) {
+ width = XTextWidth(st->font, buf, strlen(buf));
+ XDrawString(st->dpy, st->window, st->level_gc, xlim / 2 - width / 2, ylim / 2 - font_height(st->font) / 2,
+ buf, strlen(buf));
+ XSync(st->dpy, False);
+ usleep(1000000);
+ }
+
+ if (!st->bround) {
+ if (sumlive || freecity) {
+ int sumwidth;
+ /* draw live cities */
+ XFillRectangle(st->dpy, st->window, st->erase_gc,
+ 0, ylim - 100, xlim, 100);
+
+ sprintf(buf, "X %ld", st->level * 100L);
+ /* how much they get */
+ sumwidth = XTextWidth(st->font, buf, strlen(buf));
+ /* add width of city */
+ sumwidth += 60;
+ /* add spacer */
+ sumwidth += 40;
+ DrawCity(st, xlim / 2 - sumwidth / 2 + 30, ylim * 0.70, st->city[0].color);
+ XDrawString(st->dpy, st->window, st->level_gc, xlim / 2 - sumwidth / 2 + 40 + 60, ylim * 0.7, buf, strlen(buf));
+ for (i=0;i<kNumCities;i++) {
+ if (liv[i]) {
+ st->city[i].alive = 1;
+ AddScore(st, xlim, ylim, 100 * st->level);
+ DrawCities(st, xlim, ylim);
+ XSync(st->dpy, False);
+ usleep(kCityPause);
+ }
+ }
+ }
+ else {
+ /* we're dead */
+ usleep(3000000);
+
+ /* start new */
+ st->gamez++;
+ Improve(st);
+ for (i=0;i<kNumCities;i++)
+ st->city[i].alive = 1;
+ st->level = 0;
+ st->loop = 1;
+ st->score = 0;
+ st->nextBonus = kFirstBonus;
+ st->numBonus = 0;
+ DrawCities(st, xlim, ylim);
+ }
+ }
+
+ /* do free city part */
+ if (freecity && sumlive < 5) {
+ int ncnt = random() % (5 - sumlive) + 1;
+ for (i=0;i<kNumCities;i++)
+ if (!st->city[i].alive)
+ if (!--ncnt)
+ st->city[i].alive = 1;
+ strcpy(buf, "Bonus City");
+ width = XTextWidth(st->font, buf, strlen(buf));
+ XDrawString(st->dpy, st->window, st->level_gc, xlim / 2 - width / 2, ylim / 4, buf, strlen(buf));
+ DrawCities(st, xlim, ylim);
+ XSync(st->dpy, False);
+ usleep(1000000);
+ }
+
+ XFillRectangle(st->dpy, st->window, st->erase_gc,
+ 0, 0, xlim, ylim - 100);
+
+ if (!st->bround)
+ st->level++;
+ if (st->level == 1) {
+ st->nextBonus = kFirstBonus;
+ }
+
+ if (st->level > 3 && (st->level % 5 == 1)) {
+ if (st->bround) {
+ st->bround = 0;
+ DrawCities(st, xlim, ylim);
+ }
+ else {
+ /* bonus round */
+ st->bround = 1;
+ st->levMissiles = 20 + st->level * 10;
+ st->levFreq = 10;
+ for (i=0;i<kNumCities;i++)
+ st->blive[i] = st->city[i].alive;
+ sprintf(buf, "Bonus Round");
+ width = XTextWidth(st->font, buf, strlen(buf));
+ XDrawString(st->dpy, st->window, st->level_gc, xlim / 2 - width / 2, ylim / 2 - font_height(st->font) / 2, buf, strlen(buf));
+ XSync(st->dpy, False);
+ usleep(1000000);
+ XFillRectangle(st->dpy, st->window, st->erase_gc,
+ 0, 0, xlim, ylim - 100);
+ }
+ }
+
+ END_LEVEL: ;
+
+ if (!st->bround) {
+ st->levMissiles = 5 + st->level * 3;
+ if (st->level > 5)
+ st->levMissiles += st->level * 5;
+ /* levMissiles = 2; */
+ st->levFreq = 120 - st->level * 5;
+ if (st->levFreq < 30)
+ st->levFreq = 30;
+ }
+
+ /* ready to fire */
+ st->lastLaser = 0;
+}
+
+
+static unsigned long
+penetrate_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ XWindowAttributes xgwa;
+
+ if (st->draw_reset)
+ {
+ st->draw_reset = 0;
+ DrawCities(st, st->draw_xlim, st->draw_ylim);
+ }
+
+ XGetWindowAttributes(st->dpy, st->window, &xgwa);
+ st->draw_xlim = xgwa.width;
+ st->draw_ylim = xgwa.height;
+
+ /* see if just started */
+ if (st->loop == 0) {
+ if (st->smart) {
+ st->choosypersen = st->econpersen = st->carefulpersen = 100;
+ st->lrate = kMinRate; st->aim = 1;
+ }
+ NewLevel(st, st->draw_xlim, st->draw_ylim);
+ DrawScore(st, st->draw_xlim, st->draw_ylim);
+ }
+
+ st->loop++;
+
+ if (st->levMissiles == 0) {
+ /* see if anything's still on the screen, to know when to end level */
+ int i;
+ for (i=0;i<kMaxMissiles;i++)
+ if (st->missile[i].alive)
+ goto END_CHECK;
+ for (i=0;i<kMaxBooms;i++)
+ if (st->boom[i].alive)
+ goto END_CHECK;
+ for (i=0;i<kMaxLasers;i++)
+ if (st->laser[i].alive)
+ goto END_CHECK;
+ /* okay, nothing's alive, start end of level countdown */
+ usleep(kLevelPause*1000000);
+ NewLevel(st, st->draw_xlim, st->draw_ylim);
+ goto END;
+ END_CHECK: ;
+ }
+ else if ((random() % st->levFreq) == 0) {
+ launch(st, st->draw_xlim, st->draw_ylim, -1);
+ st->levMissiles--;
+ }
+
+ if (st->loop - st->lastLaser >= st->lrate) {
+ if (fire(st, st->draw_xlim, st->draw_ylim))
+ st->lastLaser = st->loop;
+ }
+
+ if ((st->loop & 7) == 0)
+ st->draw_reset = 1;
+
+ LoopMissiles(st, st->draw_xlim, st->draw_ylim);
+ LoopLasers(st, st->draw_xlim, st->draw_ylim);
+ LoopBooms(st, st->draw_xlim, st->draw_ylim);
+
+ END:
+ return kSleepTime;
+}
+
+static void
+penetrate_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+ XClearWindow (dpy, window);
+}
+
+static Bool
+penetrate_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ return False;
+}
+
+static void
+penetrate_free (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ free (st);
+}
+
+
+static const char *penetrate_defaults [] = {
+ ".lowrez: true",
+ ".background: black",
+ ".foreground: white",
+ "*fpsTop: true",
+ "*fpsSolid: true",
+ "*bgrowth: 5",
+ "*lrate: 80",
+ "*smart: False",
+ 0
+};
+
+static XrmOptionDescRec penetrate_options [] = {
+ { "-bgrowth", ".bgrowth", XrmoptionSepArg, 0 },
+ { "-lrate", ".lrate", XrmoptionSepArg, 0 },
+ {"-smart", ".smart", XrmoptionNoArg, "True" },
+ { 0, 0, 0, 0 }
+};
+
+XSCREENSAVER_MODULE ("Penetrate", penetrate)