diff options
Diffstat (limited to 'hacks/penetrate.c')
-rw-r--r-- | hacks/penetrate.c | 977 |
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) |