/* 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;iboom[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;imissile[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;icity[i].alive; if (livecity == 0) return 1; /* no guns */ for (i=0;ilaser[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;jmissile[j]; if (!mis->alive || (mis->y > ytargetmin)) continue; if (st->city[mis->dcity].alive) choo++; } if (choo == 0) choosy = 0; } for (j=0;jmissile[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;jmissile[j].enemies == 0) untargeted++; if (economic) for (j=0;j 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;jmissile[j].y != deepest) suitor[j] = 0; } if (cnt == 0) return 1; /* no targets available */ cnt = random() % cnt; for (j=0;jmissile[j]; misnum = j; break; } if (mis == 0) return 1; /* shouldn't happen */ dcity = random() % livecity; for (j=0;jcity[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;icity[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;jboom[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;jboom[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;ibround) 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;icity[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;icity[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;icity[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;iblive[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;imissile[i].alive) goto END_CHECK; for (i=0;iboom[i].alive) goto END_CHECK; for (i=0;ilaser[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; XFreeGC (dpy, st->draw_gc); XFreeGC (dpy, st->erase_gc); XFreeGC (dpy, st->level_gc); XFreeFont (dpy, st->font); XFreeFont (dpy, st->scoreFont); 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)