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