1 /*
2 icbm3d.c
3
4 ICBM3D 0.4
5
6 A "Missile Command" clone, in Three Dimensions
7
8 by Bill Kendrick
9
10 New Breed Software
11 nbs@sonic.net
12 http://www.sonic.net/~nbs/unix/x/icbm3d/
13
14 With help from:
15 - Brian Mordecai (random comments and suggestions)
16 - Matt Hermes (random comments and suggestions)
17 - Roger Mamer ("make the screen flash!", and beloved sysadmin)
18 - Steve Becerra (targetter toggle option suggestion)
19 - Martin Green (growing cities idea)
20 - Robert Hamilton (code speed enhancements) <hamil@barbarian.tamu.edu>
21 - Luis Fernandes (XCreatePixmap correction) <elf@ee.ryerson.ca>
22 - Tim Rightnour (Better makefile) <root@garbled.net>
23
24 Based on Missile Command
25 by Dave Thuerer of Atari, 1981
26
27 March 2, 1998 - July 29, 1998
28 */
29
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <X11/Xlib.h>
36 #include <X11/Xutil.h>
37 #include <X11/cursorfont.h>
38 #include <sys/types.h>
39 #include <sys/time.h>
40 #include <math.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include "window.h"
45 #include "connect.h"
46 #include "hints.h"
47 #include "visual.h"
48 #include "gc.h"
49 #include "color.h"
50 #include "randnum.h"
51 #include "text.h"
52 #include "keydefs.h"
53 #include "language.h"
54
55
56 /* Use backbuffer? */
57
58 #define DOUBLE_BUFFER True
59
60
61 /* Drawing speed-ups: */
62
63 #define FASTDRAW_BY_DEFAULT 0
64 #define HALFFRAME_BY_DEFAULT 0
65 #define SIMPLE_CITIES_BY_DEFAULT 1
66
67
68 /* Speed controls: */
69
70 #define FRAMERATE 70000 /* fps = 100,000 / FRAMERATE */
71
72
73 /* Object definitions: */
74
75 #define OBJ_SMOKE 0
76 #define OBJ_MISSILE 1
77 #define OBJ_MISSILE_TARGETTER 2
78 #define OBJ_CITY 3
79 #define OBJ_BASE 4
80 #define OBJ_POINTER 5
81 #define OBJ_POINTER_SHADOW 6
82 #define OBJ_POINTER_EXCITED 7
83 #define OBJ_EXPLOSION 8
84 #define OBJ_GROUND 9
85 #define OBJ_SKY 10
86 #define OBJ_GROUND_HORZ 11
87 #define OBJ_FLAME 12
88 #define OBJ_BULLET 13
89 #define OBJ_BULLET_DEST 14
90 #define OBJ_DEBRIS 15
91 #define OBJ_LETTER 16
92 #define OBJ_DEFENSEBASE 17
93 #define OBJ_PLANE 18
94 #define NUM_OBJECTS 19
95
96 char * object_colors[NUM_OBJECTS] = {
97 "grey",
98 "yellow",
99 "dark grey",
100 "blue",
101 "brown",
102 "white",
103 "grey",
104 "red",
105 "red",
106 "dark green",
107 "navy blue",
108 "black",
109 "orange",
110 "purple",
111 "purple",
112 "red",
113 "cyan",
114 "green",
115 "cyan"};
116
117
118 /* Keys: */
119
120 #define HKEY_UP 0
121 #define HKEY_DOWN 1
122 #define HKEY_LEFT 2
123 #define HKEY_RIGHT 3
124 #define HKEY_AWAY 4
125 #define HKEY_TOWARDS 5
126
127 int key_holds[6];
128
129
130 /* Bonus mode definitions: */
131
132 #define BONUS_OFF 0
133 #define BONUS_BULLETS 1
134 #define BONUS_CITIES 2
135 #define BONUS_DONE 3
136
137
138 /* City/Defensebase chunk definitions: */
139
140 #define CHUNK_IS_CITY 0
141 #define CHUNK_IS_DEFENSEBASE 1
142
143
144 /* Global X-Window Variables: */
145
146 char server[512];
147 Display * display;
148 Colormap colormap;
149 Window window, root;
150 Pixmap backbuffer;
151 GC whitegc, blackgc;
152 GC colorgcs[NUM_OBJECTS], yougc;
153 XFontStruct * font;
154 int fh, screen, black, white, has_color;
155
156
157 /* Window size: */
158
159 #define HEIGHT 400
160 #define WIDTH 600
161
162
163 /* 3D controls: */
164
165 #define DISTANCE 400
166 #define ASPECT 200
167
168
169 /* Game area size: */
170
171 #define Y_HEIGHT 200
172 #define X_WIDTH 400
173 #define Z_DEPTH 400
174
175
176 /* Game area values: */
177
178 #define START_HEIGHT -200
179 #define SPLIT_HEIGHT 0
180
181
182 /* Max. objects: */
183
184 #define MAX_MISSILES 20
185 #define MAX_EXPLOSIONS 100
186 #define MAX_BULLETS 20
187 #define MAX_SMOKES 5000
188 #define MAX_DEBRISERS 16
189 #define MAX_CITYCHUNKS 128
190 #define MAX_DEBRIS 256
191 #define MAX_LETTERS 20
192 #define MAX_CITIES 16
193 #define MAX_DEFENSEBASES 4
194
195
196 /* Scoring: */
197
198 #define NEW_CITY_SCORE 10000
199
200
201 /* Typedefs: */
202
203 typedef struct player_type {
204 float x, y, z;
205 } player_type;
206
207 typedef struct city_type {
208 int alive, bonused;
209 float x, z;
210 float percent;
211 int height[4], shape[4];
212 } city_type;
213
214 typedef struct defensebase_type {
215 int alive, bonusing;
216 float x, z;
217 int bullets;
218 } defensebase_type;
219
220 typedef struct missile_type {
221 int alive;
222 float x, y, z;
223 float xm, ym, zm;
224 float destx, destz;
225 int splitter;
226 int spiraller;
227 } missile_type;
228
229 typedef struct bullet_type {
230 int alive;
231 float x, y, z;
232 float xm, ym, zm;
233 float destx, desty, destz;
234 } bullet_type;
235
236 typedef struct explosion_type {
237 int alive;
238 float x, y, z;
239 int size, sizem;
240 int users;
241 } explosion_type;
242
243 typedef struct smoke_type {
244 int alive;
245 float x, y, z;
246 int owner;
247 int attachtime;
248 } smoke_type;
249
250 typedef struct debris_type {
251 int alive;
252 float x, y, z;
253 float xm, ym, zm;
254 int time;
255 } debris_type;
256
257 typedef struct debriser_type {
258 int alive;
259 float x, y, z;
260 int time;
261 } debriser_type;
262
263 typedef struct citychunk_type {
264 int alive;
265 float x1, y1, z1, x2, y2, z2;
266 float xm1, ym1, zm1, xm2, ym2, zm2;
267 int is_defensechunk;
268 } citychunk_type;
269
270 typedef struct letter_type {
271 int alive;
272 int killatdest;
273 int wave;
274 float x, y, z;
275 float destx, desty, destz;
276 float xm, ym, zm;
277 char letter;
278 } letter_type;
279
280 typedef struct plane_type {
281 int alive, hasdropped;
282 float x, y, z;
283 float xm, ym, zm;
284 float wantx;
285 } plane_type;
286
287
288 /* Global game variables: */
289
290 int toggle, ttoggle, gameover, flashtime, flashcolors, num_missiles,
291 chance_of_spiral, chance_of_split, missile_view, missile_view_missile,
292 tries, show_targets, drawlinetoggle, fastdraw, how_many_coming, halfframe,
293 num_bullets, num_explosions, bonus_mode, bonus_time, last_num_missiles,
294 got_negative_time_padding, simple_cities, bullet_view,
295 bullet_view_bullet, wind, windtime, title_mode, title_timer, chance_of_plane;
296 float windxm, windzm;
297 player_type player;
298 missile_type missiles[MAX_MISSILES];
299 bullet_type bullets[MAX_BULLETS];
300 explosion_type explosions[MAX_EXPLOSIONS];
301 smoke_type smokes[MAX_SMOKES];
302 debris_type debris[MAX_DEBRIS];
303 debriser_type debrisers[MAX_DEBRISERS];
304 citychunk_type citychunks[MAX_CITYCHUNKS];
305 letter_type letters[MAX_LETTERS];
306 city_type cities[MAX_CITIES];
307 defensebase_type defensebases[MAX_DEFENSEBASES];
308 plane_type plane;
309 float anglex, angley, cos_anglex, sin_anglex, cos_angley, sin_angley;
310 int zoom;
311 int x_change, y_change, z_change;
312 int level, showingletters, paused, score, last_score;
313
314
315 /* Local function prototypes: */
316
317 void eventloop(void);
318 void setup(int argc, char * argv[]);
319 void Xsetup();
320 unsigned long MyAllocNamedColor(Display *display, Colormap colormap,
321 char* colorname, unsigned long default_color,
322 int has_color);
323 void initlevel();
324 void drawline3d(float x1, float y1, float z1, float x2, float y2, float z2,
325 GC color);
326 void drawpoint3d(float x, float y, float z2, GC color);
327 void addexplosion(float x, float y, float z, int whose);
328 void addsmoke(float x, float y, float z, int owner);
329 void addmissile(int which, int cansplit, float x, float y, float z,
330 int angle, int whichangle);
331 void addbullet(float x, float y, float z);
332 void removemissile(int which, int cause_explosion);
333 void recalculatetrig();
334 void domove(void);
335 void killcity(int which);
336 void killdefensebase(int which);
337 void adddebris(float x, float y, float z);
338 void adddebriser(float x, float y, float z);
339 void addcitychunks(float x, float y, float z, int what);
340 void turnonletters(char * str, int wavey, float wantz);
341 void turnoffletters(void);
342 void initgame(void);
343 void endoflevel(void);
344 int calc3d(float * sx, float * sy, float x, float y, float z);
345 float sign(float v);
346
347
348 /* ---- MAIN FUNCTION ---- */
349
main(int argc,char * argv[])350 int main(int argc, char * argv[])
351 {
352 /* Program setup: */
353
354 setup(argc, argv);
355 randinit();
356
357
358 /* Connect to the X Servers and draw the program's windows: */
359
360 Xsetup();
361
362
363 /* Run the main loop: */
364
365 eventloop();
366
367
368 /* Nice message! */
369
370 printf("\n\n\n\n\n\n");
371 printf("Thank you for playing ICBM3D!\n\n");
372
373 printf("If you have comments, suggestions, questions or donations,\n");
374 printf("send them to:\n\n");
375
376 printf("New Breed Software\n");
377 printf("c/o Bill Kendrick\n");
378 printf("619 Pole Line Road #249\n");
379 printf("Davis, CA 95616 USA\n\n");
380
381 printf("email: nbs@sonic.net\n");
382 printf("web: http://www.newbreedsoftware.com/\n\n");
383
384 if (got_negative_time_padding)
385 {
386 printf("NOTE: You may wish to run the game in HALF-FRAME\n");
387 printf(" and/or FASTDRAW mode on this machine, or\n");
388 printf(" run it on a faster server...\n\n");
389 }
390 }
391
392
393 /* --- MAIN EVENT LOOP --- */
394
eventloop(void)395 void eventloop(void)
396 {
397 int old_button, old_x, old_y, i, j, k, eventloopcounter, num_cities,
398 found, angle, any, wave_count, which_color;
399 long time_padding;
400 char string[128], temp[128];
401 XEvent event;
402 char key[1024];
403 KeySym keysym;
404 XComposeStatus composestatus;
405 struct timeval now, then;
406 int done, do_move, c;
407 float x1, y1, z1, x2, y2, z2, x3, y3, z3;
408
409
410 /* Init event loop variables: */
411
412 toggle = 0;
413 ttoggle = 0;
414 drawlinetoggle = 0;
415 fastdraw = FASTDRAW_BY_DEFAULT;
416 halfframe = HALFFRAME_BY_DEFAULT;
417 simple_cities = SIMPLE_CITIES_BY_DEFAULT;
418 got_negative_time_padding = 0;
419
420 done = 0;
421
422
423 /* Init Player variables: */
424
425 player.x = X_WIDTH / 2;
426 player.y = Y_HEIGHT / 2;
427 player.z = Z_DEPTH / 2;
428 old_button = -1;
429
430 missile_view = 0;
431 missile_view_missile = 0;
432 bullet_view = 0;
433 bullet_view_bullet = 0;
434 show_targets = 1;
435
436 anglex = 0;
437 angley = 0;
438 recalculatetrig();
439
440
441 /* Init level 1: */
442
443 initgame();
444
445 initlevel();
446 gameover = 1;
447 turnonletters("ICBM-3D", 2, -100);
448 title_mode = 1;
449 title_timer = 0;
450
451
452 /* -- MAIN LOOP -- */
453
454
455 do
456 {
457 /* Toggle our toggle switches: */
458
459 toggle = 1 - toggle;
460
461 if (toggle == 0)
462 ttoggle = 1 - ttoggle;
463
464 drawlinetoggle = toggle;
465
466 gettimeofday(&then, NULL);
467
468
469 x_change = 0;
470 y_change = 0;
471 z_change = 0;
472
473
474 /* Handle controls: */
475
476 for (eventloopcounter = 0; eventloopcounter < 500; eventloopcounter++)
477 {
478 if (XPending(display))
479 {
480 strcpy(key, "");
481
482 XNextEvent(display, &event);
483
484 if (event.type == KeyPress || event.type == KeyRelease)
485 {
486 /* Get the key's name: */
487
488 XLookupString(&event.xkey, key, 1, &keysym,
489 &composestatus);
490
491 if (XKeysymToString(keysym) != NULL)
492 strcpy(key, XKeysymToString(keysym));
493
494
495 /* Movement controls: */
496
497 if (strcasecmp(key, KEY_UP) == 0)
498 {
499 /* A - Up: */
500
501 key_holds[HKEY_UP] = event.type;
502 }
503 else if (strcasecmp(key, KEY_DOWN) == 0)
504 {
505 /* Z: - Down: */
506
507 key_holds[HKEY_DOWN] = event.type;
508 }
509 else if (strcasecmp(key, "KP Left") == 0 ||
510 strcasecmp(key, KEY_LEFT) == 0)
511 {
512 /* Left - Left: */
513
514 key_holds[HKEY_LEFT] = event.type;
515 }
516 else if (strcasecmp(key, "KP Right") == 0 ||
517 strcasecmp(key, KEY_RIGHT) == 0)
518 {
519 /* Right - Right: */
520
521 key_holds[HKEY_RIGHT] = event.type;
522 }
523 else if (strcasecmp(key, "KP Up") == 0 ||
524 strcasecmp(key, KEY_AWAY) == 0)
525 {
526 /* Up - Away: */
527
528 key_holds[HKEY_AWAY] = event.type;
529 }
530 else if (strcasecmp(key, "KP Down") == 0 ||
531 strcasecmp(key, KEY_TOWARDS) == 0)
532 {
533 /* Down - Towards: */
534
535 key_holds[HKEY_TOWARDS] = event.type;
536 }
537
538
539 if (event.type == KeyPress)
540 {
541 if (strcasecmp(key, KEY_QUIT) == 0)
542 {
543 /* Q: Quit: */
544
545 done = True;
546 }
547 else if (strcasecmp(key, KEY_ABORT) == 0)
548 {
549 /* X: Abort game: */
550
551 if (gameover == 0)
552 {
553 gameover = 1;
554
555 for (i = 0; i < MAX_CITIES; i++)
556 {
557 if (cities[i].alive)
558 killcity(i);
559 }
560
561 flashtime = 30;
562 flashcolors = 1;
563
564 turnonletters(LANG_GAMEOVER, 0, 0);
565 }
566 }
567 else if (strcasecmp(key, KEY_LEVEL_NEXT) == 0)
568 {
569 /* L: Change level (+1): */
570
571 if (gameover == 1)
572 {
573 level = level + 1;
574 if (level > 99)
575 level = 1;
576 }
577 }
578 else if (strcasecmp(key, KEY_LEVEL_PREV) == 0)
579 {
580 /* K: Change level (-1): */
581
582 if (gameover == 1)
583 {
584 level = level - 1;
585 if (level < 1)
586 level = 99;
587 }
588 }
589 else if (strcasecmp(key, KEY_TOG_TARGET) == 0)
590 {
591 /* T: Toggle missile targetting lines: */
592
593 show_targets = 1 - show_targets;
594 }
595 else if (strcasecmp(key, KEY_TOG_FASTDRAW) == 0)
596 {
597 /* F: Toggle Fast-Draw mode: */
598
599 fastdraw = 1 - fastdraw;
600 }
601 else if (strcasecmp(key, KEY_TOG_HALFFRAME) == 0)
602 {
603 /* H: Toggle Half-Frame mode: */
604
605 halfframe = 1 - halfframe;
606 }
607 else if (strcasecmp(key, KEY_TOG_CITY_STYLE) == 0)
608 {
609 /* C: Toggle Simple-Cities mode: */
610
611 simple_cities = 1 - simple_cities;
612 }
613 else if (strcasecmp(key, KEY_VIEW_MISSILE) == 0)
614 {
615 /* M: Switch to missile view: */
616
617 bullet_view = 0;
618 missile_view = 1 - missile_view;
619
620 if (missile_view == 1)
621 {
622 tries = 0;
623
624 while (missiles[missile_view_missile].alive == 0
625 && tries < MAX_MISSILES)
626 {
627 missile_view_missile =
628 missile_view_missile + 1;
629
630 if (missile_view_missile >= MAX_MISSILES)
631 missile_view_missile = 0;
632
633 tries++;
634 }
635
636 if (tries >= MAX_MISSILES)
637 {
638 missile_view = 0;
639 recalculatetrig();
640 }
641 }
642 else
643 recalculatetrig();
644 }
645 else if (strcasecmp(key, KEY_VIEW_BULLET) == 0 &&
646 1 == 0)
647 {
648 /* B: Switch to bullet view: */
649
650 missile_view = 0;
651 bullet_view = 1 - bullet_view;
652
653 if (bullet_view == 1)
654 {
655 tries = 0;
656
657 while (bullets[bullet_view_bullet].alive == 0 &&
658 tries < MAX_BULLETS)
659 {
660 bullet_view_bullet = bullet_view_bullet + 1;
661
662 if (bullet_view_bullet >= MAX_BULLETS)
663 bullet_view_bullet = 0;
664
665 tries++;
666 }
667
668 if (tries >= MAX_BULLETS)
669 {
670 bullet_view = 0;
671 recalculatetrig();
672 }
673 }
674 else
675 recalculatetrig();
676 }
677 else if (strcasecmp(key, KEY_VIEW_MISSILE_SELECT) == 0)
678 {
679 /* Tab: Select a missile to view from: */
680
681 tries = 0;
682
683 do
684 {
685 missile_view_missile = missile_view_missile + 1;
686
687 if (missile_view_missile >= MAX_MISSILES)
688 missile_view_missile = 0;
689
690 tries++;
691 }
692 while (missiles[missile_view_missile].alive == 0 &&
693 tries < MAX_MISSILES);
694
695 if (tries >= MAX_MISSILES)
696 {
697 missile_view = 0;
698 recalculatetrig();
699 }
700 }
701 else if (strcasecmp(key, KEY_PAUSE) == 0)
702 {
703 /* P: Pause / Unpause: */
704
705 if (paused == 0)
706 {
707 if (gameover == 0)
708 {
709 paused = 1;
710 turnonletters(LANG_PAUSED, 1, 0);
711 }
712 }
713 else
714 {
715 paused = 0;
716 turnoffletters();
717 }
718 }
719 else if (strcasecmp(key, KEY_FIRE) == 0)
720 {
721 title_mode = 0;
722
723 if (showingletters == 0 && last_num_missiles != 0)
724 {
725 /* Space: Fire: */
726
727 addbullet(player.x, player.y, player.z);
728 }
729 else
730 {
731 /* Space: Begin Level / Unpause: */
732
733 turnoffletters();
734 paused = 0;
735
736 if (gameover == 1)
737 {
738 gameover = 0;
739 initgame();
740 initlevel();
741 }
742 }
743 }
744 else if (strcasecmp(key, KEY_IN) == 0 ||
745 strcasecmp(key, KEY_OUT) == 0)
746 {
747 /* I/O - Zoom In/Out: */
748
749 i = 20;
750
751 if (strcasecmp(key, KEY_OUT) == 0)
752 i = -20;
753
754 zoom = zoom + i;
755
756 if (zoom < -100)
757 zoom = -100;
758 else if (zoom > 100)
759 zoom = 100;
760 }
761 }
762 }
763 else if (event.type == ButtonPress)
764 {
765 old_button = event.xbutton.button;
766 old_x = event.xbutton.x;
767 old_y = event.xbutton.y;
768 }
769 else if (event.type == ButtonRelease)
770 {
771 old_button = -1;
772 }
773 else if (event.type == MotionNotify)
774 {
775 /* Move pointer: */
776
777 if (old_button == Button1)
778 {
779 /* Move left/right, up/down: */
780
781 x_change = (event.xbutton.x - old_x) / 10;
782 y_change = (event.xbutton.y - old_y) / 10;
783
784 }
785 else if (old_button == Button2)
786 {
787 /* Move left/right, in/out: */
788
789 x_change = (event.xbutton.x - old_x) / 10;
790 z_change = -(event.xbutton.y - old_y) / 5;
791 }
792 else if (old_button == Button3)
793 {
794 /* Rotate angle: */
795
796 anglex = anglex + event.xbutton.x - old_x;
797
798 if (anglex < -30)
799 anglex = -30;
800
801 if (anglex > 30)
802 anglex = 30;
803
804 angley = angley + event.xbutton.y - old_y;
805
806 if (angley < -30)
807 angley = -30;
808
809 if (angley > 40)
810 angley = 40;
811
812 recalculatetrig();
813
814 old_x = event.xbutton.x;
815 old_y = event.xbutton.y;
816 }
817
818 domove();
819 }
820 }
821 }
822
823
824 if (key_holds[HKEY_UP] == KeyPress)
825 y_change = -10;
826 else if (key_holds[HKEY_DOWN] == KeyPress)
827 y_change = 10;
828
829 if (key_holds[HKEY_LEFT] == KeyPress)
830 x_change = -10;
831 else if (key_holds[HKEY_RIGHT] == KeyPress)
832 x_change = 10;
833
834 if (key_holds[HKEY_AWAY] == KeyPress)
835 z_change = 10;
836 else if (key_holds[HKEY_TOWARDS] == KeyPress)
837 z_change = -10;
838
839 domove();
840
841
842 if (paused == 0)
843 {
844 /* Handle bullets: */
845
846 num_bullets = 0;
847
848 for (i = 0; i < MAX_BULLETS; i++)
849 {
850 if (bullets[i].alive == 1)
851 {
852 num_bullets++;
853
854 bullets[i].x = bullets[i].x + bullets[i].xm;
855 bullets[i].y = bullets[i].y + bullets[i].ym;
856 bullets[i].z = bullets[i].z + bullets[i].zm;
857
858
859 /* "Go off" when we hit the right spot: */
860
861 if ((bullets[i].x >= bullets[i].destx - 5 &&
862 bullets[i].x <= bullets[i].destx + 5 &&
863 bullets[i].z >= bullets[i].destz - 5 &&
864 bullets[i].z <= bullets[i].destz + 5 &&
865 bullets[i].y <= bullets[i].desty) ||
866 bullets[i].y <= bullets[i].desty)
867 {
868 bullets[i].alive = 0;
869
870 addexplosion(bullets[i].destx,
871 bullets[i].desty,
872 bullets[i].destz, 1);
873 }
874 }
875 }
876
877
878 /* Handle debrisers: */
879
880 for (i = 0; i < MAX_DEBRISERS; i++)
881 {
882 if (debrisers[i].alive == 1)
883 {
884 if (randnum(500) < debrisers[i].time)
885 adddebris(debrisers[i].x + randnum(20) - 10,
886 debrisers[i].y,
887 debrisers[i].z + randnum(20) - 10);
888
889 debrisers[i].time--;
890
891 if (debrisers[i].time <= 0)
892 debrisers[i].alive = 0;
893 }
894 }
895
896
897 /* Handle debris: */
898
899 for (i = 0; i < MAX_DEBRIS; i++)
900 {
901 if (debris[i].alive == 1)
902 {
903 debris[i].x = debris[i].x + debris[i].xm;
904 debris[i].y = debris[i].y + debris[i].ym;
905 debris[i].z = debris[i].z + debris[i].zm;
906
907 if (debris[i].y > Y_HEIGHT)
908 {
909 debris[i].y = Y_HEIGHT;
910 debris[i].ym = - debris[i].ym / 4;
911 }
912
913 debris[i].ym = debris[i].ym + 0.5;
914
915 if (wind == 1)
916 {
917 debris[i].x = debris[i].x + windxm;
918 debris[i].z = debris[i].z + windzm;
919 }
920
921 debris[i].time--;
922
923 if (debris[i].time <= 0)
924 debris[i].alive = 0;
925 }
926 }
927
928
929 /* Handle cities: */
930
931 for (i = 0; i < MAX_CITIES; i++)
932 {
933 if (cities[i].percent < 1.0)
934 cities[i].percent = cities[i].percent + 0.05;
935 }
936
937
938 /* Handle city chunks: */
939
940 for (i = 0; i < MAX_CITYCHUNKS; i++)
941 {
942 if (citychunks[i].alive == 1)
943 {
944 citychunks[i].x1 = citychunks[i].x1 + citychunks[i].xm1;
945 citychunks[i].y1 = citychunks[i].y1 + citychunks[i].ym1;
946 citychunks[i].z1 = citychunks[i].z1 + citychunks[i].zm1;
947
948 citychunks[i].x2 = citychunks[i].x2 + citychunks[i].xm2;
949 citychunks[i].y2 = citychunks[i].y2 + citychunks[i].ym2;
950 citychunks[i].z2 = citychunks[i].z2 + citychunks[i].zm2;
951
952 if (citychunks[i].y1 > Y_HEIGHT ||
953 citychunks[i].y2 > Y_HEIGHT)
954 citychunks[i].alive = 0;
955
956 citychunks[i].ym1 = citychunks[i].ym1 + 0.3;
957 citychunks[i].ym2 = citychunks[i].ym2 + 0.3;
958 }
959 }
960
961
962 num_explosions = 0;
963
964 /* Handle explosions: */
965
966 for (i = 0; i < MAX_EXPLOSIONS; i++)
967 {
968 if (explosions[i].alive == 1)
969 {
970 num_explosions++;
971
972 if (toggle == 0)
973 {
974 explosions[i].size = explosions[i].size +
975 explosions[i].sizem;
976
977 if (explosions[i].size >= 20)
978 explosions[i].sizem = -1;
979
980 if (explosions[i].size <= 0)
981 explosions[i].alive = 0;
982 }
983 }
984 }
985
986
987 /* Handle missiles: */
988
989 for (i = 0; i < MAX_MISSILES; i++)
990 {
991 if (missiles[i].alive == 1)
992 {
993 num_missiles++;
994
995 if (toggle == 0)
996 {
997 /* Move missile: */
998
999 missiles[i].x = missiles[i].x + missiles[i].xm;
1000 missiles[i].y = missiles[i].y + missiles[i].ym;
1001 missiles[i].z = missiles[i].z + missiles[i].zm;
1002
1003
1004 /* Add some smoke: */
1005
1006 if (ttoggle == 0 && toggle == 0)
1007 addsmoke(missiles[i].x, missiles[i].y, missiles[i].z,
1008 i);
1009
1010
1011 if (missiles[i].y > Y_HEIGHT)
1012 {
1013 /* If it hit the ground, get rid of it: */
1014
1015 removemissile(i, 1);
1016 }
1017 else
1018 {
1019 /* Check for collissions with explosions: */
1020
1021 for (j = 0; j < MAX_EXPLOSIONS; j++)
1022 {
1023 x1 = explosions[j].x - explosions[j].size - 10;
1024 y1 = explosions[j].y - explosions[j].size - 10;
1025 z1 = explosions[j].z - explosions[j].size - 10;
1026
1027 x2 = explosions[j].x + explosions[j].size + 10;
1028 y2 = explosions[j].y + explosions[j].size + 10;
1029 z2 = explosions[j].z + explosions[j].size + 10;
1030
1031 if (missiles[i].x >= x1 && missiles[i].x <= x2 &&
1032 missiles[i].y >= y1 && missiles[i].y <= y2 &&
1033 missiles[i].z >= z1 && missiles[i].z <= z2 &&
1034 missiles[i].alive == 1 &&
1035 explosions[j].alive == 1)
1036 {
1037 removemissile(i, 1);
1038
1039 if (explosions[j].users == 1)
1040 score = score + Y_HEIGHT - missiles[i].y;
1041 }
1042 }
1043 }
1044
1045
1046 /* Control spiral: */
1047
1048 if (missiles[i].spiraller == 1)
1049 {
1050 if (missiles[i].x > missiles[i].destx)
1051 missiles[i].xm = missiles[i].xm - 0.125;
1052 else
1053 missiles[i].xm = missiles[i].xm + 0.125;
1054
1055 if (missiles[i].z > missiles[i].destz)
1056 missiles[i].zm = missiles[i].zm - 0.125;
1057 else
1058 missiles[i].zm = missiles[i].zm + 0.125;
1059 }
1060
1061
1062 /* Split missile: */
1063
1064 if (missiles[i].splitter == 1 &&
1065 missiles[i].y >= SPLIT_HEIGHT)
1066 {
1067 /* Turn off this missile (it gets replaced): */
1068
1069 removemissile(i, 0);
1070
1071
1072 /* Add a burst of 3 more missiles: */
1073
1074 angle = randnum(360);
1075
1076 for (j = 0; j < 3; j++)
1077 {
1078 /* Find a slot for a missile: */
1079
1080 found = -1;
1081
1082 for (k = 0; k < MAX_MISSILES && found == -1; k++)
1083 {
1084 if (missiles[k].alive == 0)
1085 found = k;
1086 }
1087
1088 if (found != -1)
1089 {
1090 addmissile(found, 0,
1091 missiles[i].x,
1092 missiles[i].y,
1093 missiles[i].z, angle, j);
1094 }
1095 }
1096 }
1097 }
1098 }
1099 else if (showingletters == 0)
1100 {
1101 /* Possibly create a new missile: */
1102
1103 if ((randnum(5000) <= (level / 5) || num_missiles == 0) &&
1104 how_many_coming > 0)
1105 {
1106 addmissile(i, 1, -1, -1, -1, 0, 0);
1107 num_missiles++;
1108 how_many_coming--;
1109 }
1110 }
1111 }
1112
1113
1114 /* Handle plane: */
1115
1116 if (plane.alive == 1)
1117 {
1118 /* Move plane: */
1119
1120 plane.x = plane.x + plane.xm;
1121 plane.y = plane.y + plane.ym;
1122 plane.z = plane.z + plane.zm;
1123
1124
1125 /* If it goes "off-screen", get rid of it 'naturally': */
1126
1127 if (plane.x < -X_WIDTH * 0.5 ||
1128 plane.x > X_WIDTH + X_WIDTH * 0.5)
1129 plane.alive = 0;
1130
1131
1132 /* If it's getting too low, make it slow down: */
1133
1134 if (plane.y > Y_HEIGHT / 2)
1135 plane.ym = plane.ym - 0.5;
1136
1137
1138 /* Get the **** out of here!? */
1139
1140 if (plane.hasdropped == 1)
1141 plane.ym = plane.ym - 0.25;
1142
1143
1144 /* See if we hit an explosion: */
1145
1146 for (i = 0; i < MAX_EXPLOSIONS; i++)
1147 {
1148 if (explosions[i].x >= plane.x - 20 &&
1149 explosions[i].x <= plane.x + 20 &&
1150 explosions[i].y >= plane.y - 20 &&
1151 explosions[i].y <= plane.y + 20 &&
1152 explosions[i].z >= plane.z - 20 &&
1153 explosions[i].z <= plane.z + 20)
1154 {
1155 addexplosion(plane.x, plane.y, plane.z, 0);
1156 plane.alive = 0;
1157 score = score + 200 + (300 * plane.hasdropped);
1158 }
1159 }
1160
1161
1162 /* Drop a missile: */
1163
1164 if (plane.hasdropped == 0 && plane.x >= 0 && plane.x <= X_WIDTH)
1165 {
1166 if ((plane.x >= plane.wantx && plane.xm > 0) ||
1167 (plane.x <= plane.wantx && plane.xm < 0))
1168 {
1169 found = -1;
1170
1171 for (k = 0; k < MAX_MISSILES && found == -1; k++)
1172 {
1173 if (missiles[k].alive == 0)
1174 found = k;
1175 }
1176
1177 if (found != -1)
1178 {
1179 addmissile(found, 1,
1180 plane.x, plane.y, plane.z, -1, -1);
1181 plane.hasdropped = 1;
1182 }
1183 }
1184 }
1185 }
1186 else
1187 {
1188 /* Randomly add a plane: */
1189
1190 if (gameover == 0 && showingletters == 0 &&
1191 bonus_mode == BONUS_OFF)
1192 {
1193 if (randnum(chance_of_plane) == 0)
1194 {
1195 /* Start from left or right, moving inwards; */
1196
1197 plane.alive = 1;
1198 plane.hasdropped = 0;
1199
1200 plane.x = -X_WIDTH;
1201 plane.xm = randnum(5) + 3;
1202
1203 if (randnum(10) < 5)
1204 {
1205 plane.x = -plane.x;
1206 plane.xm = -plane.xm;
1207 }
1208
1209 plane.x = plane.x + (X_WIDTH / 2);
1210
1211
1212 /* Pick a random spot at which to drop bombs: */
1213
1214 plane.wantx = randnum(X_WIDTH);
1215
1216
1217 /* Pick a random direction in the other two planes
1218 (no pun intended): */
1219
1220 plane.y = randnum(Y_HEIGHT / 2);
1221 plane.ym = (randnum(10) - 5) / 2;
1222
1223 plane.z = randnum(Z_DEPTH);
1224 plane.zm = (randnum(10) - 5) / 10;
1225 }
1226 }
1227 }
1228
1229
1230 /* All missiles gone? No more coming? Handle bonus!
1231 Then, go to next level or see if game is over yet: */
1232
1233 if (how_many_coming == 0 && num_missiles == 0 &&
1234 num_explosions == 0 && num_bullets == 0 && gameover == 0)
1235 {
1236 if (bonus_mode == BONUS_OFF)
1237 {
1238 bonus_mode++;
1239 }
1240 else if (bonus_mode == BONUS_BULLETS)
1241 {
1242 any = 0;
1243
1244 for (i = 0; i < MAX_DEFENSEBASES && any == 0; i++)
1245 {
1246 if (defensebases[i].bullets > 0)
1247 {
1248 any = 1;
1249
1250 if (defensebases[i].bullets > 5)
1251 {
1252 defensebases[i].bullets =
1253 defensebases[i].bullets - 5;
1254 score = score + 5;
1255 defensebases[i].bonusing = 1;
1256 }
1257 else
1258 {
1259 score = score + defensebases[i].bullets;
1260 defensebases[i].bullets = 0;
1261 defensebases[i].bonusing = 0;
1262 }
1263 }
1264 else
1265 defensebases[i].bonusing = 0;
1266 }
1267
1268 if (any == 0)
1269 bonus_mode++;
1270 }
1271 else if (bonus_mode == BONUS_CITIES)
1272 {
1273 any = 0;
1274
1275 for (i = 0; i < MAX_CITIES && any == 0; i++)
1276 {
1277 if (cities[i].bonused == 0 && cities[i].alive != 0)
1278 {
1279 any = 1;
1280
1281 cities[i].bonused = 1;
1282
1283 score = score + 100;
1284 }
1285 }
1286
1287 if (any == 0)
1288 bonus_mode++;
1289 }
1290 else if (bonus_mode == BONUS_DONE)
1291 {
1292 /* See if we add a new city: */
1293
1294 if (score / NEW_CITY_SCORE > last_score / NEW_CITY_SCORE &&
1295 num_cities < MAX_CITIES)
1296 {
1297 /* Find a dead spot to bring back to life: */
1298
1299 do
1300 {
1301 i = randnum(MAX_CITIES);
1302 }
1303 while (cities[i].alive == 1);
1304
1305
1306 /* Reincarnate it: */
1307
1308 cities[i].alive = 1;
1309 cities[i].percent = 0;
1310 cities[i].bonused = 0;
1311
1312 num_cities++;
1313 }
1314
1315
1316 if (num_cities == 0)
1317 {
1318 /* No more cities!? Later! */
1319
1320 if (gameover == 0)
1321 {
1322 gameover = 1;
1323
1324 flashtime = 30;
1325 flashcolors = 1;
1326
1327 turnonletters(LANG_GAMEOVER, 0, 0);
1328 }
1329 }
1330 else
1331 {
1332 /* Still some cities? Next level! */
1333
1334 endoflevel();
1335 }
1336 }
1337 }
1338
1339
1340 last_num_missiles = num_missiles;
1341
1342 num_missiles = 0;
1343 }
1344
1345
1346 /* Turn off missile view if the missile is gone! */
1347
1348 if (missile_view == 1)
1349 {
1350 if (missiles[missile_view_missile].alive == 0)
1351 missile_view = 0;
1352 }
1353
1354
1355 /* Turn off bullet view if the bullet is gone! */
1356
1357 /* if (bullet_view == 1)
1358 {
1359 if (bullets[bullet_view_bullet].alive == 0)
1360 bullet_view = 0;
1361 } */
1362
1363
1364 /* Clear window: */
1365
1366 if ((fastdraw == 0 || ttoggle == 0) &&
1367 (halfframe == 0 || drawlinetoggle == 0))
1368 {
1369 if (flashtime <= 0)
1370 {
1371 XFillRectangle(display, backbuffer, blackgc, 0, 0,
1372 WIDTH, HEIGHT);
1373 }
1374 else
1375 {
1376 if (ttoggle)
1377 {
1378 if (flashcolors == 0)
1379 XFillRectangle(display, backbuffer, whitegc, 0, 0,
1380 WIDTH, HEIGHT);
1381 else
1382 XFillRectangle(display, backbuffer,
1383 colorgcs[randnum(NUM_OBJECTS)],
1384 0, 0, WIDTH, HEIGHT);
1385 }
1386 else
1387 {
1388 XFillRectangle(display, backbuffer, blackgc, 0, 0,
1389 WIDTH, HEIGHT);
1390 }
1391 }
1392
1393 if (flashtime > 0)
1394 flashtime--;
1395
1396 /* Draw horizon: */
1397
1398 if (missile_view == 0 && flashtime <= 0)
1399 {
1400 if (calc3d(&x1, &y1, 0, 0, 50000))
1401 {
1402 XFillRectangle(display, backbuffer, colorgcs[OBJ_SKY],
1403 0, 0, WIDTH, y1);
1404
1405 XFillRectangle(display, backbuffer,
1406 colorgcs[OBJ_GROUND_HORZ],
1407 0, y1, WIDTH, HEIGHT - y1);
1408 }
1409 }
1410 }
1411
1412
1413 /* Draw ground: */
1414
1415 drawline3d(0, Y_HEIGHT, 0,
1416 X_WIDTH, Y_HEIGHT, 0, colorgcs[OBJ_GROUND]);
1417
1418 drawline3d(X_WIDTH, Y_HEIGHT, 0,
1419 X_WIDTH, Y_HEIGHT, Z_DEPTH, colorgcs[OBJ_GROUND]);
1420
1421 drawline3d(X_WIDTH, Y_HEIGHT, Z_DEPTH,
1422 0, Y_HEIGHT, Z_DEPTH, colorgcs[OBJ_GROUND]);
1423
1424 drawline3d(0, Y_HEIGHT, Z_DEPTH,
1425 0, Y_HEIGHT, 0, colorgcs[OBJ_GROUND]);
1426
1427
1428 /* Draw cities: */
1429
1430 num_cities = 0;
1431
1432 for (i = 0; i < MAX_CITIES; i++)
1433 {
1434 if (cities[i].alive == 1)
1435 {
1436 num_cities++;
1437
1438 x1 = cities[i].x - ((X_WIDTH / 12) * cities[i].percent);
1439 y1 = Y_HEIGHT;
1440 z1 = cities[i].z - ((Z_DEPTH / 12) * cities[i].percent);
1441
1442 x2 = cities[i].x + ((X_WIDTH / 12) * cities[i].percent);
1443 y2 = Y_HEIGHT;
1444 z2 = cities[i].z + ((Z_DEPTH / 12) * cities[i].percent);
1445
1446 x3 = cities[i].x;
1447 y3 = Y_HEIGHT;
1448 z3 = cities[i].z;
1449
1450 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_CITY]);
1451 drawline3d(x2, y1, z1, x2, y1, z2, colorgcs[OBJ_CITY]);
1452 drawline3d(x2, y1, z2, x1, y1, z2, colorgcs[OBJ_CITY]);
1453 drawline3d(x1, y1, z2, x1, y1, z1, colorgcs[OBJ_CITY]);
1454
1455 if (simple_cities == 0)
1456 {
1457 /* Nice looking, complicated cities: */
1458
1459 if (cities[i].bonused == 0)
1460 y3 = Y_HEIGHT - cities[i].height[0] * cities[i].percent;
1461 else
1462 y3 = Y_HEIGHT;
1463
1464 drawline3d(x1, y1, z3, x2, y1, z3, colorgcs[OBJ_CITY]);
1465 drawline3d(x3, y1, z1, x3, y1, z2, colorgcs[OBJ_CITY]);
1466
1467 if (cities[i].shape[0] == 0)
1468 {
1469 drawline3d(x1, y3, z1, x3, y3, z1, colorgcs[OBJ_CITY]);
1470 drawline3d(x3, y3, z1, x3, y3, z3, colorgcs[OBJ_CITY]);
1471 drawline3d(x3, y3, z3, x1, y3, z3, colorgcs[OBJ_CITY]);
1472 drawline3d(x1, y3, z3, x1, y3, z1, colorgcs[OBJ_CITY]);
1473
1474 drawline3d(x1, y3, z1, x1, y1, z1, colorgcs[OBJ_CITY]);
1475 drawline3d(x3, y3, z1, x3, y1, z1, colorgcs[OBJ_CITY]);
1476 drawline3d(x3, y3, z3, x3, y1, z3, colorgcs[OBJ_CITY]);
1477 drawline3d(x1, y3, z3, x1, y1, z3, colorgcs[OBJ_CITY]);
1478 }
1479 else
1480 {
1481 drawline3d(x1, y1, z1,
1482 (x1 + x3) / 2, y3, (z1 + z3) / 2,
1483 colorgcs[OBJ_CITY]);
1484 drawline3d(x3, y1, z1,
1485 (x1 + x3) / 2, y3, (z1 + z3) / 2,
1486 colorgcs[OBJ_CITY]);
1487 drawline3d(x3, y1, z3,
1488 (x1 + x3) / 2, y3, (z1 + z3) / 2,
1489 colorgcs[OBJ_CITY]);
1490 drawline3d(x1, y1, z3,
1491 (x1 + x3) / 2, y3, (z1 + z3) / 2,
1492 colorgcs[OBJ_CITY]);
1493 }
1494
1495 if (cities[i].bonused == 0)
1496 y3 = Y_HEIGHT - cities[i].height[1] * cities[i].percent;
1497 else
1498 y3 = Y_HEIGHT;
1499
1500 if (cities[i].shape[1] == 0)
1501 {
1502 drawline3d(x2, y3, z1, x3, y3, z1, colorgcs[OBJ_CITY]);
1503 drawline3d(x3, y3, z1, x3, y3, z3, colorgcs[OBJ_CITY]);
1504 drawline3d(x3, y3, z3, x2, y3, z3, colorgcs[OBJ_CITY]);
1505 drawline3d(x2, y3, z3, x2, y3, z1, colorgcs[OBJ_CITY]);
1506
1507 drawline3d(x2, y3, z1, x2, y1, z1, colorgcs[OBJ_CITY]);
1508 drawline3d(x3, y3, z1, x3, y1, z1, colorgcs[OBJ_CITY]);
1509 drawline3d(x3, y3, z3, x3, y1, z3, colorgcs[OBJ_CITY]);
1510 drawline3d(x2, y3, z3, x2, y1, z3, colorgcs[OBJ_CITY]);
1511 }
1512 else
1513 {
1514 drawline3d(x2, y1, z1,
1515 (x2 + x3) / 2, y3, (z1 + z3) / 2,
1516 colorgcs[OBJ_CITY]);
1517 drawline3d(x3, y1, z1,
1518 (x2 + x3) / 2, y3, (z1 + z3) / 2,
1519 colorgcs[OBJ_CITY]);
1520 drawline3d(x3, y1, z3,
1521 (x2 + x3) / 2, y3, (z1 + z3) / 2,
1522 colorgcs[OBJ_CITY]);
1523 drawline3d(x2, y1, z3,
1524 (x2 + x3) / 2, y3, (z1 + z3) / 2,
1525 colorgcs[OBJ_CITY]);
1526 }
1527
1528 if (cities[i].bonused == 0)
1529 y3 = Y_HEIGHT - cities[i].height[2] * cities[i].percent;
1530 else
1531 y3 = Y_HEIGHT;
1532
1533 if (cities[i].shape[2] == 0)
1534 {
1535 drawline3d(x1, y3, z2, x3, y3, z2, colorgcs[OBJ_CITY]);
1536 drawline3d(x3, y3, z2, x3, y3, z3, colorgcs[OBJ_CITY]);
1537 drawline3d(x3, y3, z3, x1, y3, z3, colorgcs[OBJ_CITY]);
1538 drawline3d(x1, y3, z3, x1, y3, z2, colorgcs[OBJ_CITY]);
1539
1540 drawline3d(x1, y3, z2, x1, y1, z2, colorgcs[OBJ_CITY]);
1541 drawline3d(x3, y3, z2, x3, y1, z2, colorgcs[OBJ_CITY]);
1542 drawline3d(x3, y3, z3, x3, y1, z3, colorgcs[OBJ_CITY]);
1543 drawline3d(x1, y3, z3, x1, y1, z3, colorgcs[OBJ_CITY]);
1544 }
1545 else
1546 {
1547 drawline3d(x1, y1, z2,
1548 (x1 + x3) / 2, y3, (z2 + z3) / 2,
1549 colorgcs[OBJ_CITY]);
1550 drawline3d(x3, y1, z2,
1551 (x1 + x3) / 2, y3, (z2 + z3) / 2,
1552 colorgcs[OBJ_CITY]);
1553 drawline3d(x3, y1, z3,
1554 (x1 + x3) / 2, y3, (z2 + z3) / 2,
1555 colorgcs[OBJ_CITY]);
1556 drawline3d(x1, y1, z3,
1557 (x1 + x3) / 2, y3, (z2 + z3) / 2,
1558 colorgcs[OBJ_CITY]);
1559 }
1560
1561 if (cities[i].bonused == 0)
1562 y3 = Y_HEIGHT - cities[i].height[3] * cities[i].percent;
1563 else
1564 y3 = Y_HEIGHT;
1565
1566 if (cities[i].shape[3] == 0)
1567 {
1568 drawline3d(x2, y3, z2, x3, y3, z2, colorgcs[OBJ_CITY]);
1569 drawline3d(x3, y3, z2, x3, y3, z3, colorgcs[OBJ_CITY]);
1570 drawline3d(x3, y3, z3, x2, y3, z3, colorgcs[OBJ_CITY]);
1571 drawline3d(x2, y3, z3, x2, y3, z2, colorgcs[OBJ_CITY]);
1572
1573 drawline3d(x2, y3, z2, x2, y1, z2, colorgcs[OBJ_CITY]);
1574 drawline3d(x3, y3, z2, x3, y1, z2, colorgcs[OBJ_CITY]);
1575 drawline3d(x3, y3, z3, x3, y1, z3, colorgcs[OBJ_CITY]);
1576 drawline3d(x2, y3, z3, x2, y1, z3, colorgcs[OBJ_CITY]);
1577 }
1578 else
1579 {
1580 drawline3d(x2, y1, z2,
1581 (x2 + x3) / 2, y3, (z2 + z3) / 2,
1582 colorgcs[OBJ_CITY]);
1583 drawline3d(x3, y1, z2,
1584 (x2 + x3) / 2, y3, (z2 + z3) / 2,
1585 colorgcs[OBJ_CITY]);
1586 drawline3d(x3, y1, z3,
1587 (x2 + x3) / 2, y3, (z2 + z3) / 2,
1588 colorgcs[OBJ_CITY]);
1589 drawline3d(x2, y1, z3,
1590 (x2 + x3) / 2, y3, (z2 + z3) / 2,
1591 colorgcs[OBJ_CITY]);
1592 }
1593 }
1594 else
1595 {
1596 /* Simple-looking cities: */
1597
1598 if (cities[i].bonused == 0)
1599 y3 = Y_HEIGHT - 40 * cities[i].percent;
1600 else
1601 y3 = Y_HEIGHT;
1602
1603 drawline3d(x1, y1, z1, x3, y3, z3, colorgcs[OBJ_CITY]);
1604 drawline3d(x2, y1, z1, x3, y3, z3, colorgcs[OBJ_CITY]);
1605 drawline3d(x2, y1, z2, x3, y3, z3, colorgcs[OBJ_CITY]);
1606 drawline3d(x1, y1, z2, x3, y3, z3, colorgcs[OBJ_CITY]);
1607 }
1608 }
1609 }
1610
1611
1612 /* Draw defense bases: */
1613
1614 for (i = 0; i < MAX_DEFENSEBASES; i++)
1615 {
1616 if (defensebases[i].alive == 1)
1617 {
1618 j = 16;
1619
1620 if (defensebases[i].bonusing == 1)
1621 j = 8;
1622
1623 x1 = defensebases[i].x - (X_WIDTH / j);
1624 y1 = Y_HEIGHT;
1625 z1 = defensebases[i].z - (Z_DEPTH / j);
1626
1627 x2 = defensebases[i].x + (X_WIDTH / j);
1628 y2 = Y_HEIGHT;
1629 z2 = defensebases[i].z + (Z_DEPTH / j);
1630
1631 x3 = defensebases[i].x;
1632 y3 = Y_HEIGHT - 30;
1633 z3 = defensebases[i].z;
1634
1635 if (defensebases[i].bonusing == 1)
1636 y3 = Y_HEIGHT - 60;
1637
1638 drawline3d(x1, y1, z1, x2, y1, z1,
1639 colorgcs[OBJ_DEFENSEBASE]);
1640 drawline3d(x2, y1, z1, x2, y1, z2,
1641 colorgcs[OBJ_DEFENSEBASE]);
1642 drawline3d(x2, y1, z2, x1, y1, z2,
1643 colorgcs[OBJ_DEFENSEBASE]);
1644 drawline3d(x1, y1, z2, x1, y1, z1,
1645 colorgcs[OBJ_DEFENSEBASE]);
1646
1647
1648 if (defensebases[i].bullets > 0)
1649 {
1650 /* Shape for active defense bases: */
1651
1652 drawline3d(x1, y3, z1, x3, y1, z1,
1653 colorgcs[OBJ_DEFENSEBASE]);
1654 drawline3d(x2, y3, z1, x3, y1, z1,
1655 colorgcs[OBJ_DEFENSEBASE]);
1656
1657 drawline3d(x1, y3, z2, x3, y1, z2,
1658 colorgcs[OBJ_DEFENSEBASE]);
1659 drawline3d(x2, y3, z2, x3, y1, z2,
1660 colorgcs[OBJ_DEFENSEBASE]);
1661
1662 drawline3d(x1, y3, z1, x1, y1, z3,
1663 colorgcs[OBJ_DEFENSEBASE]);
1664 drawline3d(x1, y3, z2, x1, y1, z3,
1665 colorgcs[OBJ_DEFENSEBASE]);
1666
1667 drawline3d(x2, y3, z1, x2, y1, z3,
1668 colorgcs[OBJ_DEFENSEBASE]);
1669 drawline3d(x2, y3, z2, x2, y1, z3,
1670 colorgcs[OBJ_DEFENSEBASE]);
1671
1672 drawline3d(x1, y3, z1, x3, y1, z3,
1673 colorgcs[OBJ_DEFENSEBASE]);
1674 drawline3d(x2, y3, z1, x3, y1, z3,
1675 colorgcs[OBJ_DEFENSEBASE]);
1676 drawline3d(x2, y3, z2, x3, y1, z3,
1677 colorgcs[OBJ_DEFENSEBASE]);
1678 drawline3d(x1, y3, z2, x3, y1, z3,
1679 colorgcs[OBJ_DEFENSEBASE]);
1680
1681 drawline3d(x1, y3, z1, x1, y1, z1,
1682 colorgcs[OBJ_DEFENSEBASE]);
1683 drawline3d(x2, y3, z1, x2, y1, z1,
1684 colorgcs[OBJ_DEFENSEBASE]);
1685 drawline3d(x2, y3, z2, x2, y1, z2,
1686 colorgcs[OBJ_DEFENSEBASE]);
1687 drawline3d(x1, y3, z2, x1, y1, z2,
1688 colorgcs[OBJ_DEFENSEBASE]);
1689 }
1690 else
1691 {
1692 /* Shape for empty defense bases: */
1693
1694 drawline3d(x1, y1, z1, x3, y3, z3,
1695 colorgcs[OBJ_DEFENSEBASE]);
1696 drawline3d(x2, y1, z1, x3, y3, z3,
1697 colorgcs[OBJ_DEFENSEBASE]);
1698 drawline3d(x2, y1, z2, x3, y3, z3,
1699 colorgcs[OBJ_DEFENSEBASE]);
1700 drawline3d(x1, y1, z2, x3, y3, z3,
1701 colorgcs[OBJ_DEFENSEBASE]);
1702 }
1703 }
1704 }
1705
1706
1707 /* Draw plane: */
1708
1709 if (plane.alive == 1)
1710 {
1711 x1 = plane.x + sign(plane.xm) * 20;
1712 y1 = plane.y - 10 - plane.ym * 2;
1713 z1 = plane.z - 10 - plane.zm * 2;
1714
1715 x3 = plane.x;
1716 y3 = plane.y;
1717 z3 = plane.z;
1718
1719 x2 = plane.x - sign(plane.xm) * 20;
1720 y2 = plane.y + 10 + plane.ym * 2;
1721 z2 = plane.z + 10 + plane.zm * 2;
1722
1723 drawline3d(x1, y3 + plane.ym, z3, x2, y3 - plane.ym, z3,
1724 colorgcs[OBJ_PLANE]);
1725
1726 drawline3d(x1, y3 + plane.ym, z3, x2, y3 - plane.ym, z2,
1727 colorgcs[OBJ_PLANE]);
1728 drawline3d(x1, y3 + plane.ym, z3, x2, y3 - plane.ym, z1,
1729 colorgcs[OBJ_PLANE]);
1730 drawline3d(x2, y3 - plane.ym, z2, x2, y3 - plane.ym, z1,
1731 colorgcs[OBJ_PLANE]);
1732
1733 drawline3d(x2, y3 - plane.ym, z3, x2, y1 + plane.ym, z3,
1734 colorgcs[OBJ_PLANE]);
1735 drawline3d(x1, y3 + plane.ym, z3, x2, y1 + plane.ym, z3,
1736 colorgcs[OBJ_PLANE]);
1737
1738 if (show_targets == 1 ||
1739 (player.x >= plane.x - 50 &&
1740 player.x <= plane.x + 50 &&
1741 player.z >= plane.z - 50 &&
1742 player.z <= plane.z + 50))
1743 {
1744 drawline3d(plane.x, Y_HEIGHT, plane.z,
1745 plane.x, plane.y, plane.z,
1746 colorgcs[OBJ_MISSILE_TARGETTER]);
1747 }
1748 }
1749
1750
1751 /* Draw pointer: */
1752
1753 if (gameover == 0)
1754 {
1755 /* White (normal) or red (near a plane or missile): */
1756
1757 which_color = OBJ_POINTER;
1758
1759 if (plane.alive == 1 &&
1760 player.x >= plane.x - 20 &&
1761 player.x <= plane.x + 20 &&
1762 player.y >= plane.y - 20 &&
1763 player.y <= plane.y + 20 &&
1764 player.z >= plane.z - 20 &&
1765 player.z <= plane.z + 20)
1766 {
1767 which_color = OBJ_POINTER_EXCITED;
1768 }
1769
1770 for (i = 0; i < MAX_MISSILES && which_color != OBJ_POINTER_EXCITED;
1771 i++)
1772 {
1773 if (missiles[i].alive == 1 &&
1774 player.x >= missiles[i].x - 20 &&
1775 player.x <= missiles[i].x + 20 &&
1776 player.y >= missiles[i].y - 20 &&
1777 player.y <= missiles[i].y + 20 &&
1778 player.z >= missiles[i].z - 20 &&
1779 player.z <= missiles[i].z + 20)
1780 {
1781 which_color = OBJ_POINTER_EXCITED;
1782 }
1783 }
1784
1785
1786 drawline3d(player.x - 10, player.y, player.z,
1787 player.x + 10, player.y, player.z, colorgcs[which_color]);
1788
1789 drawline3d(player.x, player.y - 10, player.z,
1790 player.x, player.y + 10, player.z, colorgcs[which_color]);
1791
1792 drawline3d(player.x, player.y, player.z - 10,
1793 player.x, player.y, player.z + 10, colorgcs[which_color]);
1794
1795
1796 /* Draw pointer "shadow": */
1797
1798 drawline3d(player.x, Y_HEIGHT, player.z - 10,
1799 player.x, Y_HEIGHT, player.z + 10,
1800 colorgcs[OBJ_POINTER_SHADOW]);
1801
1802 drawline3d(player.x - 10, Y_HEIGHT, player.z,
1803 player.x + 10, Y_HEIGHT, player.z,
1804 colorgcs[OBJ_POINTER_SHADOW]);
1805 }
1806
1807
1808 /* Draw missiles: */
1809
1810 for (i = 0; i < MAX_MISSILES; i++)
1811 {
1812 if (missiles[i].alive == 1)
1813 {
1814 drawline3d(missiles[i].x, missiles[i].y, missiles[i].z,
1815 missiles[i].x - missiles[i].xm,
1816 missiles[i].y - missiles[i].ym,
1817 missiles[i].z - missiles[i].zm,
1818 colorgcs[OBJ_MISSILE]);
1819
1820 if (show_targets == 1 ||
1821 (player.x >= missiles[i].x - 50 &&
1822 player.x <= missiles[i].x + 50 &&
1823 player.z >= missiles[i].z - 50 &&
1824 player.z <= missiles[i].z + 50))
1825 {
1826 drawline3d(missiles[i].x, Y_HEIGHT, missiles[i].z,
1827 missiles[i].x, missiles[i].y, missiles[i].z,
1828 colorgcs[OBJ_MISSILE_TARGETTER]);
1829 }
1830 }
1831 }
1832
1833
1834 /* Draw bullets: */
1835
1836 for (i = 0; i < MAX_BULLETS; i++)
1837 {
1838 if (bullets[i].alive == 1)
1839 {
1840 /* Draw destination target: */
1841
1842 drawline3d(bullets[i].destx - 5,
1843 bullets[i].desty - 5,
1844 bullets[i].destz,
1845 bullets[i].destx + 5,
1846 bullets[i].desty + 5,
1847 bullets[i].destz, colorgcs[OBJ_BULLET_DEST]);
1848
1849 drawline3d(bullets[i].destx + 5,
1850 bullets[i].desty - 5,
1851 bullets[i].destz,
1852 bullets[i].destx - 5,
1853 bullets[i].desty + 5,
1854 bullets[i].destz, colorgcs[OBJ_BULLET_DEST]);
1855
1856
1857 /* Draw "plasma"-y bullet: */
1858
1859 drawline3d(bullets[i].x - randnum(5),
1860 bullets[i].y - randnum(5),
1861 bullets[i].z - randnum(5),
1862 bullets[i].x + randnum(5),
1863 bullets[i].y - randnum(5),
1864 bullets[i].z - randnum(5),
1865 colorgcs[OBJ_BULLET]);
1866
1867 drawline3d(bullets[i].x - randnum(5),
1868 bullets[i].y - randnum(5),
1869 bullets[i].z - randnum(5),
1870 bullets[i].x - randnum(5),
1871 bullets[i].y + randnum(5),
1872 bullets[i].z - randnum(5),
1873 colorgcs[OBJ_BULLET]);
1874
1875 drawline3d(bullets[i].x - randnum(5),
1876 bullets[i].y - randnum(5),
1877 bullets[i].z - randnum(5),
1878 bullets[i].x - randnum(5),
1879 bullets[i].y - randnum(5),
1880 bullets[i].z + randnum(5),
1881 colorgcs[OBJ_BULLET]);
1882
1883 drawline3d(bullets[i].x - randnum(5),
1884 bullets[i].y + randnum(5),
1885 bullets[i].z - randnum(5),
1886 bullets[i].x - randnum(5),
1887 bullets[i].y - randnum(5),
1888 bullets[i].z + randnum(5),
1889 colorgcs[OBJ_BULLET]);
1890 }
1891 }
1892
1893
1894 /* Draw smoke: */
1895
1896 for (i = 0; i < MAX_SMOKES; i++)
1897 {
1898 if (smokes[i].alive == 1)
1899 {
1900 if (smokes[i].attachtime >= 0)
1901 {
1902 drawline3d(smokes[i].x, smokes[i].y, smokes[i].z,
1903 missiles[smokes[i].owner].x,
1904 missiles[smokes[i].owner].y,
1905 missiles[smokes[i].owner].z,
1906 colorgcs[OBJ_FLAME]);
1907
1908 smokes[i].attachtime--;
1909 }
1910 else
1911 {
1912 drawpoint3d(smokes[i].x, smokes[i].y, smokes[i].z,
1913 colorgcs[OBJ_SMOKE]);
1914 }
1915
1916 if (ttoggle == 0 && toggle == 0)
1917 {
1918 smokes[i].x = smokes[i].x + (randnum(3) - 1) / 2.0;
1919 smokes[i].y = smokes[i].y + (randnum(3) - 1) / 2.0;
1920 smokes[i].z = smokes[i].z + (randnum(3) - 1) / 2.0;
1921
1922 if (wind == 1 && smokes[i].attachtime <= 0)
1923 {
1924 smokes[i].x = smokes[i].x + windxm;
1925 smokes[i].z = smokes[i].z + windzm;
1926 }
1927 }
1928 }
1929 }
1930
1931
1932 /* Handle wind: */
1933
1934 if (wind == 1)
1935 {
1936 windtime--;
1937
1938 if (windtime <= 0)
1939 {
1940 windxm = (randnum(80) - 40) / 10.0;
1941 windzm = (randnum(80) - 40) / 10.0;
1942 windtime = randnum(100) + 100;
1943 }
1944 }
1945
1946
1947 /* Draw debris: */
1948
1949 for (i = 0; i < MAX_DEBRIS; i++)
1950 {
1951 if (debris[i].alive == 1)
1952 {
1953 drawpoint3d(debris[i].x, debris[i].y, debris[i].z,
1954 colorgcs[OBJ_DEBRIS]);
1955 }
1956 }
1957
1958
1959 /* Draw city chunks: */
1960
1961 for (i = 0; i < MAX_CITYCHUNKS; i++)
1962 {
1963 if (citychunks[i].alive == 1)
1964 {
1965 if (citychunks[i].is_defensechunk == CHUNK_IS_CITY)
1966 drawline3d(citychunks[i].x1, citychunks[i].y1,
1967 citychunks[i].z1,
1968 citychunks[i].x2, citychunks[i].y2,
1969 citychunks[i].z2,
1970 colorgcs[OBJ_CITY]);
1971 else
1972 drawline3d(citychunks[i].x1, citychunks[i].y1,
1973 citychunks[i].z1,
1974 citychunks[i].x2, citychunks[i].y2,
1975 citychunks[i].z2,
1976 colorgcs[OBJ_DEFENSEBASE]);
1977 }
1978 }
1979
1980
1981 /* Draw explosions: */
1982
1983 for (i = 0; i < MAX_EXPLOSIONS; i++)
1984 {
1985 if (explosions[i].alive == 1)
1986 {
1987 j = explosions[i].size;
1988
1989 x1 = explosions[i].x - j;
1990 x2 = explosions[i].x + j;
1991 x3 = explosions[i].x;
1992
1993 y1 = explosions[i].y - j;
1994 y2 = explosions[i].y + j;
1995 y3 = explosions[i].y;
1996
1997 z1 = explosions[i].z - j;
1998 z2 = explosions[i].z + j;
1999 z3 = explosions[i].z;
2000
2001
2002 drawline3d(x1, y3, z1, x2, y3, z1, colorgcs[OBJ_EXPLOSION]);
2003 drawline3d(x2, y3, z1, x2, y3, z2, colorgcs[OBJ_EXPLOSION]);
2004 drawline3d(x2, y3, z2, x1, y3, z2, colorgcs[OBJ_EXPLOSION]);
2005 drawline3d(x1, y3, z2, x1, y3, z1, colorgcs[OBJ_EXPLOSION]);
2006
2007 drawline3d(x1, y3, z1, x3, y1, z3, colorgcs[OBJ_EXPLOSION]);
2008 drawline3d(x2, y3, z1, x3, y1, z3, colorgcs[OBJ_EXPLOSION]);
2009 drawline3d(x2, y3, z2, x3, y1, z3, colorgcs[OBJ_EXPLOSION]);
2010 drawline3d(x1, y3, z2, x3, y1, z3, colorgcs[OBJ_EXPLOSION]);
2011
2012 drawline3d(x1, y3, z1, x3, y2, z3, colorgcs[OBJ_EXPLOSION]);
2013 drawline3d(x2, y3, z1, x3, y2, z3, colorgcs[OBJ_EXPLOSION]);
2014 drawline3d(x2, y3, z2, x3, y2, z3, colorgcs[OBJ_EXPLOSION]);
2015 drawline3d(x1, y3, z2, x3, y2, z3, colorgcs[OBJ_EXPLOSION]);
2016 }
2017 }
2018
2019
2020 /* Handle title effect: */
2021
2022 if (title_mode == 1)
2023 {
2024 title_timer++;
2025
2026 if (title_timer >= 100)
2027 {
2028 title_mode = 2;
2029
2030 for (i = 0; i < MAX_LETTERS; i++)
2031 {
2032 letters[i].destx = X_WIDTH / 2;
2033 letters[i].desty = Y_HEIGHT / 2;
2034 letters[i].destz = Z_DEPTH / 2;
2035 }
2036 }
2037 }
2038
2039
2040 /* Show letters: */
2041
2042 wave_count = (wave_count + 10) % 360;
2043
2044 for (i = 0; i < MAX_LETTERS; i++)
2045 {
2046 if (letters[i].alive == 1)
2047 {
2048 x3 = letters[i].x;
2049 y3 = letters[i].y;
2050 z3 = letters[i].z;
2051
2052
2053 /* Do wave effects: */
2054
2055 if (letters[i].wave == 1)
2056 {
2057 y3 = y3 + sin(M_PI * (i * 50 + wave_count) / 180) * 5;
2058 }
2059 else if (letters[i].wave == 2)
2060 {
2061 if (letters[i].y < Y_HEIGHT - 20 - 1 ||
2062 letters[i].y > Y_HEIGHT - 20 + 1 ||
2063 letters[i].ym > 0.125)
2064 {
2065 letters[i].ym = letters[i].ym + 0.25;
2066
2067 if (letters[i].y > Y_HEIGHT - 20)
2068 {
2069 letters[i].y = Y_HEIGHT - 20;
2070 letters[i].ym = -letters[i].ym / 4;
2071 }
2072 }
2073 else
2074 letters[i].ym = 0;
2075 }
2076
2077
2078 /* Do title effects: */
2079
2080 if (title_mode == 2)
2081 {
2082 if (letters[i].z > Z_DEPTH / 2)
2083 letters[i].zm = letters[i].zm - 0.5;
2084 else
2085 letters[i].zm = letters[i].zm + 0.5;
2086
2087 if (letters[i].y > Y_HEIGHT / 2)
2088 letters[i].ym = letters[i].ym - 0.5;
2089 else
2090 letters[i].ym = letters[i].ym + 0.5;
2091
2092 if (toggle == 1)
2093 adddebris(letters[i].x, letters[i].y, letters[i].z);
2094 }
2095
2096
2097 x1 = letters[i].x - 5;
2098 y1 = y3 - 5;
2099 z1 = letters[i].z;
2100
2101 x2 = letters[i].x + 5;
2102 y2 = y3 + 5;
2103 z2 = letters[i].z;
2104
2105
2106 c = toupper(letters[i].letter);
2107
2108
2109 /* Draw the letter shape: */
2110
2111 if (c == 'L')
2112 {
2113 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2114 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2115 }
2116 else if (c == '-')
2117 {
2118 drawline3d(x1, y3, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2119 }
2120 else if (c == 'C')
2121 {
2122 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2123 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2124 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2125 }
2126 else if (c == 'B')
2127 {
2128 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2129 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2130 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2131 drawline3d(x2, y1, z1, x1, y3, z1, colorgcs[OBJ_LETTER]);
2132 drawline3d(x2, y2, z1, x1, y3, z1, colorgcs[OBJ_LETTER]);
2133 }
2134 else if (c == 'I')
2135 {
2136 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2137 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2138 drawline3d(x3, y1, z1, x3, y2, z1, colorgcs[OBJ_LETTER]);
2139 }
2140 else if (c == 'E' || c == 'F')
2141 {
2142 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2143 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2144 drawline3d(x1, y3, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2145 if (c == 'E')
2146 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2147 }
2148 else if (c == 'V')
2149 {
2150 drawline3d(x1, y1, z1, x3, y2, z1, colorgcs[OBJ_LETTER]);
2151 drawline3d(x3, y2, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2152 }
2153 else if (c == 'G')
2154 {
2155 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2156 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2157 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2158 drawline3d(x2, y2, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2159 drawline3d(x2, y3, z1, x3, y3, z1, colorgcs[OBJ_LETTER]);
2160 }
2161 else if (c == 'A')
2162 {
2163 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2164 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2165 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2166 drawline3d(x1, y3, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2167 }
2168 else if (c == 'M')
2169 {
2170 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2171 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2172 drawline3d(x1, y1, z1, x3, y3, z1, colorgcs[OBJ_LETTER]);
2173 drawline3d(x2, y1, z1, x3, y3, z1, colorgcs[OBJ_LETTER]);
2174 }
2175 else if (c == 'R')
2176 {
2177 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2178 drawline3d(x2, y1, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2179 drawline3d(x2, y3, z1, x1, y3, z1, colorgcs[OBJ_LETTER]);
2180 drawline3d(x1, y3, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2181 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2182 }
2183 else if (c == 'P')
2184 {
2185 drawline3d(x1, y1, z1, x2, y1, z2, colorgcs[OBJ_LETTER]);
2186 drawline3d(x2, y1, z1, x2, y3, z2, colorgcs[OBJ_LETTER]);
2187 drawline3d(x2, y3, z1, x1, y3, z2, colorgcs[OBJ_LETTER]);
2188 drawline3d(x1, y1, z1, x1, y2, z2, colorgcs[OBJ_LETTER]);
2189 }
2190 else if (c == 'U')
2191 {
2192 drawline3d(x1, y1, z1, x1, y2, z2, colorgcs[OBJ_LETTER]);
2193 drawline3d(x1, y2, z1, x2, y2, z2, colorgcs[OBJ_LETTER]);
2194 drawline3d(x2, y1, z1, x2, y2, z2, colorgcs[OBJ_LETTER]);
2195 }
2196 else if (c == 'D')
2197 {
2198 drawline3d(x1, y1, z1, x1, y2, z2, colorgcs[OBJ_LETTER]);
2199 drawline3d(x1, y1, z1, x2, y3, z2, colorgcs[OBJ_LETTER]);
2200 drawline3d(x1, y2, z1, x2, y3, z2, colorgcs[OBJ_LETTER]);
2201 }
2202 else if (c == '0' || c == 'O' || c == 'Q')
2203 {
2204 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2205 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2206 drawline3d(x2, y2, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2207 drawline3d(x1, y2, z1, x1, y1, z1, colorgcs[OBJ_LETTER]);
2208
2209 if (c == 'Q')
2210 drawline3d(x3, y3, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2211 }
2212 else if (c == '1')
2213 {
2214 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2215 }
2216 else if (c == '2')
2217 {
2218 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2219 drawline3d(x2, y1, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2220 drawline3d(x2, y3, z1, x1, y3, z1, colorgcs[OBJ_LETTER]);
2221 drawline3d(x1, y3, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2222 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2223 }
2224 else if (c == '3')
2225 {
2226 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2227 drawline3d(x2, y1, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2228 drawline3d(x2, y3, z1, x3, y3, z1, colorgcs[OBJ_LETTER]);
2229 drawline3d(x2, y3, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2230 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2231 }
2232 else if (c == '4')
2233 {
2234 drawline3d(x1, y1, z1, x1, y3, z1, colorgcs[OBJ_LETTER]);
2235 drawline3d(x1, y3, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2236 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2237 }
2238 else if (c == '5' || c == 'S')
2239 {
2240 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2241 drawline3d(x1, y1, z1, x1, y3, z1, colorgcs[OBJ_LETTER]);
2242 drawline3d(x2, y3, z1, x1, y3, z1, colorgcs[OBJ_LETTER]);
2243 drawline3d(x2, y3, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2244 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2245 }
2246 else if (c == '6')
2247 {
2248 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2249 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2250 drawline3d(x2, y3, z1, x1, y3, z1, colorgcs[OBJ_LETTER]);
2251 drawline3d(x2, y3, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2252 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2253 }
2254 else if (c == '7')
2255 {
2256 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2257 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2258 }
2259 else if (c == '8')
2260 {
2261 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2262 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2263 drawline3d(x2, y2, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2264 drawline3d(x1, y2, z1, x1, y1, z1, colorgcs[OBJ_LETTER]);
2265 drawline3d(x1, y3, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2266 }
2267 else if (c == '9')
2268 {
2269 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2270 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2271 drawline3d(x2, y2, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2272 drawline3d(x1, y3, z1, x1, y1, z1, colorgcs[OBJ_LETTER]);
2273 drawline3d(x1, y3, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2274 }
2275 else if (c == 'H')
2276 {
2277 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2278 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2279 drawline3d(x1, y3, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2280 }
2281 else if (c == 'J')
2282 {
2283 drawline3d(x2, y1, z1, x2, y3, z1, colorgcs[OBJ_LETTER]);
2284 drawline3d(x2, y3, z1, x3, y2, z1, colorgcs[OBJ_LETTER]);
2285 drawline3d(x3, y2, z1, x1, y3, z1, colorgcs[OBJ_LETTER]);
2286 }
2287 else if (c == 'K')
2288 {
2289 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2290 drawline3d(x1, y3, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2291 drawline3d(x1, y3, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2292 }
2293 else if (c == 'N')
2294 {
2295 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2296 drawline3d(x1, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2297 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2298 }
2299 else if (c == 'T')
2300 {
2301 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2302 drawline3d(x3, y1, z1, x3, y2, z1, colorgcs[OBJ_LETTER]);
2303 }
2304 else if (c == 'W')
2305 {
2306 drawline3d(x1, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2307 drawline3d(x2, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2308 drawline3d(x1, y2, z1, x3, y3, z1, colorgcs[OBJ_LETTER]);
2309 drawline3d(x2, y2, z1, x3, y3, z1, colorgcs[OBJ_LETTER]);
2310 }
2311 else if (c == 'X')
2312 {
2313 drawline3d(x1, y1, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2314 drawline3d(x2, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2315 }
2316 else if (c == 'Y')
2317 {
2318 drawline3d(x1, y1, z1, x3, y3, z1, colorgcs[OBJ_LETTER]);
2319 drawline3d(x2, y1, z1, x3, y3, z1, colorgcs[OBJ_LETTER]);
2320 drawline3d(x3, y3, z1, x3, y2, z1, colorgcs[OBJ_LETTER]);
2321 }
2322 else if (c == 'Z')
2323 {
2324 drawline3d(x1, y1, z1, x2, y1, z1, colorgcs[OBJ_LETTER]);
2325 drawline3d(x2, y1, z1, x1, y2, z1, colorgcs[OBJ_LETTER]);
2326 drawline3d(x1, y2, z1, x2, y2, z1, colorgcs[OBJ_LETTER]);
2327 }
2328
2329
2330 /* Make letter stop moving when it hits its destination: */
2331
2332 if (letters[i].x >= letters[i].destx - 1 &&
2333 letters[i].x <= letters[i].destx + 1)
2334 letters[i].xm = 0;
2335
2336 if (letters[i].y >= letters[i].desty - 1 &&
2337 letters[i].y <= letters[i].desty + 1 && letters[i].wave != 2)
2338 letters[i].ym = 0;
2339
2340 if (letters[i].z >= letters[i].destz - 1 &&
2341 letters[i].z <= letters[i].destz + 1)
2342 letters[i].zm = 0;
2343
2344
2345 /* Kill when it hits its destination (if it's supposed to): */
2346
2347 if (letters[i].xm == 0 && letters[i].ym == 0 &&
2348 letters[i].zm == 0 && letters[i].killatdest == 1)
2349 letters[i].alive = 0;
2350
2351
2352 /* Move letter: */
2353
2354 letters[i].x = letters[i].x + letters[i].xm;
2355 letters[i].y = letters[i].y + letters[i].ym;
2356 letters[i].z = letters[i].z + letters[i].zm;
2357 }
2358 }
2359
2360
2361 /* Draw score, level, etc.: */
2362
2363 sprintf(temp, "%s: %d", LANG_LEVEL, level);
2364
2365 drawtext(display, backbuffer, colorgcs[OBJ_LETTER],
2366 0, fh, temp);
2367
2368 if (gameover == 1)
2369 {
2370 drawcenteredtext(display, backbuffer, colorgcs[randnum(NUM_OBJECTS)],
2371 0, WIDTH, (fh + 1) * 3,
2372 temp, font);
2373
2374 sprintf(temp, "[%s] %s", KEY_LEVEL_NEXT, LANG_SELECT_NEXT_LEVEL);
2375 drawcenteredtext(display, backbuffer, colorgcs[OBJ_LETTER],
2376 0, WIDTH, (fh + 1) * 5, temp, font);
2377
2378 sprintf(temp, "[%s] %s", KEY_LEVEL_PREV, LANG_SELECT_PREV_LEVEL);
2379 drawcenteredtext(display, backbuffer, colorgcs[OBJ_LETTER],
2380 0, WIDTH, (fh + 1) * 6, temp, font);
2381
2382 sprintf(temp, "[%s] %s, [%s] %s", KEY_FIRE, LANG_BEGIN,
2383 KEY_QUIT, LANG_QUIT);
2384 drawcenteredtext(display, backbuffer, colorgcs[OBJ_LETTER],
2385 0, WIDTH, (fh + 1) * 8, temp, font);
2386 }
2387
2388 sprintf(temp, "%s: %.5d", LANG_SCORE, score);
2389
2390 drawcenteredtext(display, backbuffer, colorgcs[OBJ_LETTER],
2391 0, WIDTH, fh, temp, font);
2392
2393 strcpy(temp, LANG_VIEW);
2394
2395 if (fastdraw == 1)
2396 strcat(temp, LANG_VIEW_FAST);
2397
2398 if (halfframe == 1)
2399 strcat(temp, LANG_VIEW_HALF);
2400
2401 if (missile_view == 1)
2402 strcat(temp, LANG_VIEW_MISSILE);
2403
2404 if (bullet_view == 1)
2405 strcat(temp, LANG_VIEW_BULLET);
2406
2407 if (strcmp(temp, LANG_VIEW) == 0)
2408 strcat(temp, LANG_VIEW_NORMAL);
2409
2410 drawtext(display, backbuffer, colorgcs[OBJ_LETTER],
2411 0, HEIGHT, temp);
2412
2413
2414 /* Draw AICBM Bullet info.: */
2415
2416 for (i = 0; i < MAX_DEFENSEBASES; i++)
2417 {
2418 if (calc3d(&x1, &y1,
2419 defensebases[i].x, Y_HEIGHT, defensebases[i].z))
2420 {
2421 sprintf(temp, "%d", defensebases[i].bullets);
2422
2423 drawtext(display, backbuffer, colorgcs[OBJ_LETTER],
2424 x1, y1, temp);
2425 }
2426 }
2427
2428
2429 /* Flush: */
2430
2431 if (DOUBLE_BUFFER == True)
2432 {
2433 XCopyArea(display, backbuffer, window,
2434 whitegc, 0, 0, WIDTH, HEIGHT, 0, 0);
2435 }
2436
2437 XSync(display, 0);
2438
2439
2440 /* Keep framerate exact: */
2441
2442 gettimeofday(&now, NULL);
2443
2444 time_padding = FRAMERATE - ((now.tv_sec - then.tv_sec) * 1000000 +
2445 (now.tv_usec - then.tv_usec));
2446
2447 if (time_padding > 0)
2448 usleep(time_padding);
2449 else
2450 got_negative_time_padding = 1;
2451 }
2452 while (done == False);
2453 }
2454
2455
2456 /* Program set-up (check usage, load data, etc.): */
2457
setup(int argc,char * argv[])2458 void setup(int argc, char * argv[])
2459 {
2460 FILE * fi;
2461 char temp[512], color[512], file[1024];
2462 int i, len, z, zc, cntl, want_length, sound_pos_tmp, which_sound;
2463 char c;
2464 struct timeval now, then;
2465
2466
2467 /* What level to start out at? (Default is 1, use -L# to set) */
2468
2469 level = 1;
2470
2471 if (argc >= 2)
2472 {
2473 if (argv[argc - 1][0] == '-' &&
2474 toupper(argv[argc - 1][1]) == 'L' &&
2475 isdigit(argv[argc - 1][2]))
2476 {
2477 level = atoi(argv[argc - 1] + 2);
2478
2479 if (level < 0)
2480 level = 0;
2481 else if (level > 99)
2482 level = 99;
2483
2484 argc--;
2485 }
2486 }
2487
2488 if (argc == 2)
2489 {
2490 if (strcmp(argv[1], "-version") == 0 ||
2491 strcmp(argv[1], "-v") == 0)
2492 {
2493 /* Check for "-version": */
2494
2495 printf("\nicbm3d version 0.4\n\n");
2496
2497 printf("by Bill Kendrick\n");
2498 printf("nbs@sonic.net\n");
2499 printf("http://www.sonic.net/~nbs/unix/x/icbm3d/\n\n");
2500 exit(0);
2501 }
2502 else if (strcmp(argv[1], "-help") == 0 ||
2503 strcmp(argv[1], "-h") == 0)
2504 {
2505 /* Check for "-help": */
2506
2507 fprintf(stderr, "\n");
2508 fprintf(stderr, "Usage: %s [display] | -help | -version | -lyrics\n",
2509 argv[0]);
2510 fprintf(stderr, "OBJECT:\n");
2511 fprintf(stderr, " Destroy ICBMs before they destroy the cities.\n");
2512 fprintf(stderr, "MOVEMENT:\n");
2513 fprintf(stderr, " Left-Click - Move up/down/right/left\n");
2514 fprintf(stderr, " Middle-Click - Move in/out/right/left\n");
2515 fprintf(stderr, " Right-Click - Change perspective\n");
2516 fprintf(stderr, "%7s/%7s - Move up/down\n", KEY_UP, KEY_DOWN);
2517 fprintf(stderr, "%7s/%7s - Move left/right\n", KEY_LEFT, KEY_RIGHT);
2518 fprintf(stderr, "%7s/%7s - Move in/out\n", KEY_AWAY, KEY_TOWARDS);
2519 fprintf(stderr, "%15s - Fire Anti-ICBM\n", KEY_FIRE);
2520 fprintf(stderr, "GAME CONTROLS:\n");
2521 fprintf(stderr, "%15s - Pause/Unpause\n", KEY_PAUSE);
2522 fprintf(stderr, "%7s/%7s - Select next/prev. level\n",
2523 KEY_LEVEL_NEXT, KEY_LEVEL_PREV);
2524 fprintf(stderr, "%15s - Abort game (game over)\n", KEY_ABORT);
2525 fprintf(stderr, "%15s - Quit\n", KEY_QUIT);
2526 fprintf(stderr, "GRAPHICS CONTROLS:\n");
2527 fprintf(stderr, "%7s/%7s - Zoom view in and out\n", KEY_IN, KEY_OUT);
2528 fprintf(stderr, "%15s - Toggle missile view on/off\n",
2529 KEY_VIEW_MISSILE);
2530 fprintf(stderr, "%15s - Choose missile (missile view)\n",
2531 KEY_VIEW_MISSILE_SELECT);
2532 fprintf(stderr, "%15s - Toggle targetting lines\n",
2533 KEY_TOG_TARGET);
2534 fprintf(stderr, "%15s - Toggle fastdraw mode\n",
2535 KEY_TOG_FASTDRAW);
2536 fprintf(stderr, "%15s - Toggle half-frame mode\n",
2537 KEY_TOG_HALFFRAME);
2538 fprintf(stderr, "%15s - Toggle simple city graphics\n",
2539 KEY_TOG_CITY_STYLE);
2540
2541 exit(0);
2542 }
2543 else if (strcmp(argv[1], "-lyrics") == 0 ||
2544 strcmp(argv[1], "-l") == 0)
2545 {
2546 printf("When you attend a funeral,\n");
2547 printf("It is sad to think that sooner o'\n");
2548 printf("Later those you love will do the same for you.\n");
2549 printf("And you may have thought it tragic,\n");
2550 printf("Not to mention other adjec-\n");
2551 printf("Tives, to think of all the weeping they will do.\n");
2552 printf("(But don't you worry.)\n");
2553 printf("\n");
2554 printf("No more ashes, no more sackcloth,\n");
2555 printf("And an arm band made of black cloth\n");
2556 printf("Will some day nevermore adorn a sleeve.\n");
2557 printf("For if the bomb that drops on you\n");
2558 printf("Gets your friends and neighbors too,\n");
2559 printf("There'll be nobody left behind to grieve.\n");
2560 printf("\n");
2561 printf("And we will all go together when we go.\n");
2562 printf("What a comforting fact that is to know.\n");
2563 printf("Universal bereavement,\n");
2564 printf("An inspiring achievement,\n");
2565 printf("Yes, we will all go together when we go.\n");
2566 printf("\n");
2567 printf("We will all go together when we go.\n");
2568 printf("All suffused with an incandescent glow.\n");
2569 printf("No one will have the endurance\n");
2570 printf("To collect on his insurance,\n");
2571 printf("Lloyd's of London will be loaded when they go.\n");
2572 printf("\n");
2573 printf("Oh we will all fry together when we fry.\n");
2574 printf("We'll be french fried potatoes by and by.\n");
2575 printf("There will be no more misery\n");
2576 printf("When the world is our rotisserie,\n");
2577 printf("Yes, we will all fry together when we fry.\n");
2578 printf("\n");
2579 printf("Down by the old maelstrom,\n");
2580 printf("There'll be a storm before the calm.\n");
2581 printf("\n");
2582 printf("And we will all bake together when we bake.\n");
2583 printf("There'll be nobody present at the wake.\n");
2584 printf("With complete participation\n");
2585 printf("In that grand incineration,\n");
2586 printf("Nearly three billion hunks of well-done steak.\n");
2587 printf("\n");
2588 printf("Oh we will all char together when we char.\n");
2589 printf("And let there be no moaning of the bar.\n");
2590 printf("Just sing out a Te Deum\n");
2591 printf("When you see that I.C.B.M.,\n");
2592 printf("And the party will be come-as-you-are.\n");
2593 printf("\n");
2594 printf("Oh, we will all burn together when we burn.\n");
2595 printf("There'll be no need to stand and wait your turn.\n");
2596 printf("When it's time for the fallout\n");
2597 printf("And Saint Peter calls us all out,\n");
2598 printf("We'll just drop our agendas and adjourn.\n");
2599 printf("\n");
2600 printf("You will all go directly to your respective Valhallas.\n");
2601 printf("Go directly, do not pass Go, do not collect two hundred dollars.\n");
2602 printf("\n");
2603 printf("And we will all go together when we go.\n");
2604 printf("Every Hottentot and every Eskimo.\n");
2605 printf("When the air becomes uranious,\n");
2606 printf("We will all go simultaneous.\n");
2607 printf("Yes, we all will go together\n");
2608 printf("When we all go together,\n");
2609 printf("Yes we all will go together when we go.\n");
2610 printf("\n");
2611 printf("TOM LEHRER\n");
2612
2613 exit(0);
2614 }
2615 }
2616
2617 if (argc == 1)
2618 {
2619 if (getenv("DISPLAY") != NULL)
2620 {
2621 strcpy(server, getenv("DISPLAY"));
2622 }
2623 else
2624 {
2625 fprintf(stderr, LANG_ERROR_NODISPLAY);
2626 exit(1);
2627 }
2628 }
2629 else if (argc == 2)
2630 {
2631 strcpy(server, argv[1]);
2632 }
2633 else
2634 {
2635 fprintf(stderr, "Usage: %s [display] | -help | -version | -lyrics\n",
2636 argv[0]);
2637 exit(1);
2638 }
2639
2640 randinit();
2641 }
2642
2643
2644 /* Setup the application: */
2645
Xsetup()2646 void Xsetup()
2647 {
2648 int i, tempint, ret, max_depth, num_avail_depths, which_max_depth;
2649 int * avail_depths;
2650 char title[1024], file[128];
2651 int status, temp_depth;
2652 Visual * temp_visual = CopyFromParent;
2653 FILE * fi;
2654 XGCValues gcvalues;
2655 XColor mycolor;
2656
2657
2658 /* Connect to display: */
2659
2660 fprintf(stderr, "%s: %s\n", LANG_CONNECTING, server);
2661
2662 display = ConnectToServer(server, &screen, &root);
2663 if (display == NULL)
2664 {
2665 perror(server);
2666 exit(1);
2667 }
2668
2669
2670 /* Load font: */
2671
2672 font = LoadFont(display, "variable", "fixed");
2673 fh = FontHeight(font);
2674
2675
2676 /* Get our primitve colors: */
2677
2678 black = BlackPixel(display, screen);
2679 white = WhitePixel(display, screen);
2680
2681
2682 /* Open window: */
2683
2684 window = OpenWindow(display, root, 10, 10, WIDTH, HEIGHT,
2685 CopyFromParent, black,
2686 (KeyPressMask | KeyReleaseMask | ButtonMotionMask |
2687 ButtonPressMask | ButtonReleaseMask),
2688 (Visual *)CopyFromParent);
2689
2690 if (DOUBLE_BUFFER == True)
2691 {
2692 backbuffer = XCreatePixmap(display, root, WIDTH, HEIGHT,
2693 DefaultDepthOfScreen(DefaultScreenOfDisplay(display)));
2694 }
2695 else
2696 backbuffer = window;
2697
2698 sprintf(title, "ICBM3D by Bill Kendrick");
2699 SetStandardHints(display, window, "ICBM3D", title, 10, 10,
2700 WIDTH, HEIGHT);
2701
2702
2703 /* Set up visual: */
2704
2705 has_color = 0;
2706
2707 if (SetUpVisual(display, screen, &temp_visual, &temp_depth))
2708 {
2709 if (!SetUpColormap(display, screen, window, temp_visual,
2710 &colormap))
2711 {
2712 fprintf(stderr, "Could not create a colormap!\n");
2713 }
2714 else
2715 has_color = 1;
2716 }
2717 else
2718 {
2719 fprintf(stderr, "Could not find a PseudoColor visual!\n");
2720 }
2721
2722
2723 /* Create Primitive GC's: */
2724
2725 whitegc = CreateGC(display, window, white, black);
2726
2727 blackgc = CreateGC(display, window, black, black);
2728
2729
2730 /* Create object GC's: */
2731
2732 for (i = 0; i < NUM_OBJECTS; i++)
2733 colorgcs[i] = CreateGC(display, window,
2734 MyAllocNamedColor(display, colormap,
2735 object_colors[i], white,
2736 has_color),
2737 black);
2738
2739
2740 /* Special GC "fixes": */
2741
2742 XSetLineAttributes(display, colorgcs[OBJ_MISSILE_TARGETTER], 1,
2743 LineOnOffDash, CapNotLast, JoinMiter);
2744
2745
2746 /* Put us up!: */
2747
2748 XMapWindow(display, window);
2749 XMapRaised(display, window);
2750 XFlush(display);
2751 }
2752
2753
2754 /* Allocate a color (or white): */
2755
MyAllocNamedColor(Display * display,Colormap colormap,char * colorname,unsigned long default_color,int has_color)2756 unsigned long MyAllocNamedColor(Display *display, Colormap colormap,
2757 char* colorname, unsigned long default_color,
2758 int has_color)
2759 {
2760 if (has_color == 0)
2761 return(default_color);
2762 else
2763 return(AllocNamedColor(display, colormap, colorname, default_color));
2764 }
2765
2766
2767 /* Init the level stuff: */
2768
initlevel(void)2769 void initlevel(void)
2770 {
2771 char temp[10];
2772 int i;
2773 float x1, z1;
2774
2775
2776 /* Erase everything: */
2777
2778 for (i = 0; i < MAX_MISSILES; i++)
2779 missiles[i].alive = 0;
2780
2781 for (i = 0; i < MAX_BULLETS; i++)
2782 bullets[i].alive = 0;
2783
2784 for (i = 0; i < MAX_EXPLOSIONS; i++)
2785 explosions[i].alive = 0;
2786
2787 for (i = 0; i < MAX_SMOKES; i++)
2788 smokes[i].alive = 0;
2789
2790 for (i = 0; i < MAX_DEBRIS; i++)
2791 debris[i].alive = 0;
2792
2793 for (i = 0; i < MAX_DEBRISERS; i++)
2794 debrisers[i].alive = 0;
2795
2796 for (i = 0; i < MAX_CITYCHUNKS; i++)
2797 citychunks[i].alive = 0;
2798
2799 plane.alive = 0;
2800
2801
2802 /* Set up letters to introduce the level: */
2803
2804 sprintf(temp, "%s %d", LANG_LEVEL, level);
2805
2806 turnonletters(temp, 0, 0);
2807
2808
2809 /* Turn off stuff: */
2810
2811 paused = 0;
2812 gameover = 0;
2813 flashtime = 0;
2814 num_missiles = 0;
2815 last_num_missiles = 0;
2816 how_many_coming = level * 1;
2817 bonus_mode = BONUS_OFF;
2818
2819
2820 /* Determine this level's statistics: */
2821
2822 chance_of_split = (100 - level);
2823
2824 if (level % 2 == 0)
2825 chance_of_spiral = 10;
2826 else
2827 chance_of_spiral = 10000;
2828
2829 if (level > 10)
2830 chance_of_plane = 100;
2831 else
2832 chance_of_plane = 10000;
2833
2834 if (level > 50)
2835 {
2836 wind = 1;
2837 windxm = (randnum(80) - 40) / 10.0;
2838 windzm = (randnum(80) - 40) / 10.0;
2839 windtime = randnum(100) + 100;
2840 }
2841 else
2842 wind = 0;
2843
2844
2845 /* Record our score: */
2846
2847 last_score = score;
2848
2849
2850 /* Init defense bases: */
2851
2852 x1 = 0;
2853 z1 = 0;
2854
2855 for (i = 0; i < MAX_DEFENSEBASES; i++)
2856 {
2857 defensebases[i].alive = 1;
2858 defensebases[i].bonusing = 0;
2859
2860 defensebases[i].x = x1;
2861 defensebases[i].z = z1;
2862
2863 defensebases[i].bullets = 99;
2864
2865 if (x1 == 0)
2866 {
2867 x1 = X_WIDTH;
2868 }
2869 else
2870 {
2871 x1 = 0;
2872 z1 = Z_DEPTH;
2873 }
2874 }
2875
2876
2877 /* Turn of "bonused"'s on cities: */
2878
2879 for (i = 0; i < MAX_CITIES; i++)
2880 cities[i].bonused = 0;
2881 }
2882
2883
2884 /* Draw a line: */
2885
drawline3d(float x1,float y1,float z1,float x2,float y2,float z2,GC color)2886 void drawline3d(float x1, float y1, float z1, float x2, float y2, float z2,
2887 GC color)
2888 {
2889 float sx1, sy1, sx2, sy2;
2890
2891
2892 if (fastdraw == 1)
2893 drawlinetoggle = 1 - drawlinetoggle;
2894
2895 if ((fastdraw == 0 && halfframe == 0) || drawlinetoggle == 0)
2896 {
2897 if (calc3d(&sx1, &sy1, x1, y1, z1) &&
2898 calc3d(&sx2, &sy2, x2, y2, z2))
2899 {
2900 /* Draw the line into the window: */
2901
2902 XDrawLine(display, backbuffer, color, sx1, sy1, sx2, sy2);
2903 }
2904 }
2905 }
2906
2907
2908 /* Draw point: */
2909
drawpoint3d(float x,float y,float z,GC color)2910 void drawpoint3d(float x, float y, float z, GC color)
2911 {
2912 float sx, sy;
2913
2914
2915 if (fastdraw == 1)
2916 drawlinetoggle = 1 - drawlinetoggle;
2917
2918 if ((fastdraw == 0 && halfframe == 0) || drawlinetoggle == 0)
2919 {
2920 if (calc3d(&sx, &sy, x, y, z))
2921 {
2922 /* Draw the point into the window: */
2923
2924 XDrawPoint(display, backbuffer, color, sx, sy);
2925 }
2926 }
2927 }
2928
2929
2930 /* Add an explosion: */
2931
addexplosion(float x,float y,float z,int whose)2932 void addexplosion(float x, float y, float z, int whose)
2933 {
2934 int i, found;
2935
2936
2937 found = -1;
2938
2939 for (i = 0; i < MAX_EXPLOSIONS; i++)
2940 {
2941 if (explosions[i].alive == 0)
2942 found = i;
2943 }
2944
2945 if (found != -1)
2946 {
2947 explosions[found].alive = 1;
2948 explosions[found].x = x;
2949 explosions[found].y = y;
2950 explosions[found].z = z;
2951 explosions[found].size = 1;
2952 explosions[found].sizem = 1;
2953 explosions[found].users = whose;
2954 }
2955
2956 for (i = 0; i < MAX_CITIES; i++)
2957 {
2958 if (cities[i].alive == 1)
2959 {
2960 if (x + 10 >= cities[i].x - X_WIDTH / 12 &&
2961 x - 10 <= cities[i].x + X_WIDTH / 12 &&
2962 y + 10 >= Y_HEIGHT - 10 &&
2963 z + 10 >= cities[i].z - Z_DEPTH / 12 &&
2964 z - 10 <= cities[i].z + Z_DEPTH / 12)
2965 {
2966 killcity(i);
2967 }
2968 }
2969 }
2970
2971
2972 for (i = 0; i < MAX_DEFENSEBASES; i++)
2973 {
2974 if (defensebases[i].alive == 1)
2975 {
2976 if (x + 10 >= defensebases[i].x - X_WIDTH / 12 &&
2977 x - 10 <= defensebases[i].x + X_WIDTH / 12 &&
2978 y + 10 >= Y_HEIGHT - 10 &&
2979 z + 10 >= defensebases[i].z - Z_DEPTH / 12 &&
2980 z - 10 <= defensebases[i].z + Z_DEPTH / 12)
2981 {
2982 killdefensebase(i);
2983 }
2984 }
2985 }
2986 }
2987
2988
2989 /* Add a smoke: */
2990
addsmoke(float x,float y,float z,int owner)2991 void addsmoke(float x, float y, float z, int owner)
2992 {
2993 int i, found;
2994
2995
2996 found = -1;
2997
2998 for (i = 0; i < MAX_SMOKES && found == -1; i++)
2999 {
3000 if (smokes[i].alive == 0)
3001 found = i;
3002 }
3003
3004 if (found != -1)
3005 {
3006 smokes[found].alive = 1;
3007 smokes[found].x = x;
3008 smokes[found].y = y;
3009 smokes[found].z = z;
3010 smokes[found].owner = owner;
3011 smokes[found].attachtime = randnum(5) + 20;
3012 }
3013 }
3014
3015
3016 /* Add a missile: */
3017
addmissile(int which,int cansplit,float x,float y,float z,int angle,int whichangle)3018 void addmissile(int which, int cansplit, float x, float y, float z,
3019 int angle, int whichangle)
3020 {
3021 int take_time;
3022 float destx, destz;
3023
3024
3025 missiles[which].alive = 1;
3026
3027 if (x == -1)
3028 {
3029 missiles[which].x = randnum(X_WIDTH);
3030 missiles[which].y = START_HEIGHT;
3031 missiles[which].z = randnum(Z_DEPTH);
3032 }
3033 else
3034 {
3035 missiles[which].x = x;
3036 missiles[which].y = y;
3037 missiles[which].z = z;
3038 }
3039
3040
3041 /* Pick a spot to aim for: */
3042
3043 missiles[which].destx = randnum(X_WIDTH);
3044 missiles[which].destz = randnum(Z_DEPTH);
3045
3046
3047 /* Pick a random amount of time to take to get there: */
3048
3049 take_time = randnum(150) + 300 - level * 2;
3050
3051
3052 /* Aim for it: */
3053
3054 missiles[which].xm = (missiles[which].destx - missiles[which].x) / take_time;
3055 missiles[which].ym = (Y_HEIGHT - missiles[which].y) / take_time;
3056 missiles[which].zm = (missiles[which].destz - missiles[which].z) / take_time;
3057
3058
3059 /* Does this missile split? */
3060
3061 if (randnum(chance_of_split) < 1 && cansplit == 1)
3062 missiles[which].splitter = 1;
3063 else
3064 missiles[which].splitter = 0;
3065
3066
3067 /* Does this missile spliral? */
3068
3069 if (randnum(chance_of_spiral) < 1)
3070 missiles[which].spiraller = 1;
3071 else
3072 missiles[which].spiraller = 0;
3073 }
3074
3075
3076 /* Add a bullet: */
3077
addbullet(float x,float y,float z)3078 void addbullet(float x, float y, float z)
3079 {
3080 int i, found, whichbase;
3081 float distance, this_dist;
3082
3083
3084 whichbase = -1;
3085 distance = 100000.0;
3086
3087 for (i = 0; i < MAX_DEFENSEBASES; i++)
3088 {
3089 if (defensebases[i].alive == 1 && defensebases[i].bullets > 0)
3090 {
3091 this_dist = sqrt(((defensebases[i].x - x) *
3092 (defensebases[i].x - x)) +
3093 ((defensebases[i].z - z) *
3094 (defensebases[i].z - z)));
3095
3096 if (this_dist < distance)
3097 {
3098 distance = this_dist;
3099 whichbase = i;
3100 }
3101 }
3102 }
3103
3104
3105 if (whichbase != -1)
3106 {
3107 found = -1;
3108
3109 for (i = 0; i < MAX_BULLETS && found == -1; i++)
3110 {
3111 if (bullets[i].alive == 0)
3112 found = i;
3113 }
3114
3115 if (found != -1)
3116 {
3117 bullets[found].alive = 1;
3118
3119
3120 /* Set it's starting position and destination: */
3121
3122 bullets[found].x = defensebases[whichbase].x;
3123 bullets[found].y = Y_HEIGHT;
3124 bullets[found].z = defensebases[whichbase].z;
3125
3126 bullets[found].destx = x;
3127 bullets[found].desty = y;
3128 bullets[found].destz = z;
3129
3130
3131 /* Determine it's speed and direction: */
3132
3133 distance = cbrt(((bullets[found].x - bullets[found].destx) *
3134 (bullets[found].x - bullets[found].destx)) +
3135 ((bullets[found].y - bullets[found].desty) *
3136 (bullets[found].y - bullets[found].desty)) +
3137 ((bullets[found].z - bullets[found].destz) *
3138 (bullets[found].z - bullets[found].destz))) / 5;
3139
3140 bullets[found].xm = (bullets[found].destx - bullets[found].x) /
3141 distance;
3142 bullets[found].ym = (bullets[found].desty - bullets[found].y) /
3143 distance;
3144 bullets[found].zm = (bullets[found].destz - bullets[found].z) /
3145 distance;
3146
3147
3148 /* Eat one of the base's bullets: */
3149
3150 defensebases[whichbase].bullets--;
3151 }
3152 }
3153 }
3154
3155
3156 /* Kill a missile (and it's smoke): */
3157
removemissile(int which,int cause_explosion)3158 void removemissile(int which, int cause_explosion)
3159 {
3160 int i;
3161
3162
3163 missiles[which].alive = 0;
3164
3165 for (i = 0; i < MAX_SMOKES; i++)
3166 {
3167 if (smokes[i].owner == which && smokes[i].alive == 1)
3168 {
3169 smokes[i].alive = 0;
3170 }
3171 }
3172
3173 if (cause_explosion == 1)
3174 addexplosion(missiles[which].x, missiles[which].y, missiles[which].z, 0);
3175 }
3176
3177
3178 /* Recalculate rotation trig values: */
3179
recalculatetrig()3180 void recalculatetrig()
3181 {
3182 cos_anglex = cos(M_PI * anglex / 180.0);
3183 sin_anglex = sin(M_PI * anglex / 180.0);
3184
3185 cos_angley = cos(M_PI * angley / 180.0);
3186 sin_angley = sin(M_PI * angley / 180.0);
3187 }
3188
3189
3190 /* Move pointer; keep it in bounds: */
3191
domove(void)3192 void domove(void)
3193 {
3194 if (paused == 0)
3195 {
3196 player.x = player.x + x_change;
3197 if (player.x < 0)
3198 player.x = 0;
3199 else if (player.x >= X_WIDTH)
3200 player.x = X_WIDTH - 1;
3201
3202 player.y = player.y + y_change;
3203 if (player.y < -100)
3204 player.y = -100;
3205 else if (player.y > Y_HEIGHT)
3206 player.y = Y_HEIGHT - 1;
3207
3208 player.z = player.z + z_change;
3209 if (player.z < 0)
3210 player.z = 0;
3211 else if (player.z > Z_DEPTH)
3212 player.z = Z_DEPTH - 1;
3213 }
3214 }
3215
3216
3217 /* Destroy a city: */
3218
killcity(int which)3219 void killcity(int which)
3220 {
3221 int i;
3222
3223
3224 cities[which].alive = 0;
3225
3226 for (i = 0; i < 8; i++)
3227 addcitychunks(cities[which].x, Y_HEIGHT, cities[which].z, CHUNK_IS_CITY);
3228
3229 adddebriser(cities[which].x, Y_HEIGHT, cities[which].z);
3230
3231 flashtime = 6;
3232 flashcolors = 0;
3233 }
3234
3235
3236 /* Destroy a city: */
3237
killdefensebase(int which)3238 void killdefensebase(int which)
3239 {
3240 int i;
3241
3242
3243 defensebases[which].alive = 0;
3244 defensebases[which].bullets = 0;
3245
3246 for (i = 0; i < 10; i++)
3247 addcitychunks(defensebases[which].x, Y_HEIGHT, defensebases[which].z,
3248 CHUNK_IS_DEFENSEBASE);
3249
3250 adddebriser(defensebases[which].x, Y_HEIGHT, defensebases[which].z);
3251
3252 flashtime = 6;
3253 flashcolors = 1;
3254 }
3255
3256
3257 /* Add some debris: */
3258
adddebris(float x,float y,float z)3259 void adddebris(float x, float y, float z)
3260 {
3261 int i, found;
3262
3263 found = -1;
3264
3265 for (i = 0; i < MAX_DEBRIS; i++)
3266 {
3267 if (debris[i].alive == 0)
3268 found = i;
3269 }
3270
3271 if (found != -1)
3272 {
3273 debris[found].alive = 1;
3274
3275 debris[found].x = x;
3276 debris[found].y = y;
3277 debris[found].z = z;
3278
3279 debris[found].xm = randnum(5) - 2;
3280 debris[found].ym = -randnum(5) - 5;
3281 debris[found].zm = randnum(5) - 2;
3282
3283 debris[found].time = 20 + randnum(10);
3284 }
3285 }
3286
3287
3288 /* Add a debrisers: */
3289
adddebriser(float x,float y,float z)3290 void adddebriser(float x, float y, float z)
3291 {
3292 int i, found;
3293
3294 found = -1;
3295
3296 for (i = 0; i < MAX_DEBRISERS; i++)
3297 {
3298 if (debrisers[i].alive == 0)
3299 found = i;
3300 }
3301
3302 if (found != -1)
3303 {
3304 debrisers[found].alive = 1;
3305
3306 debrisers[found].x = x;
3307 debrisers[found].y = y;
3308 debrisers[found].z = z;
3309
3310 debrisers[found].time = 500;
3311 }
3312 }
3313
3314
3315 /* Add a chunk of city flying around: */
3316
addcitychunks(float x,float y,float z,int what)3317 void addcitychunks(float x, float y, float z, int what)
3318 {
3319 int i, found;
3320
3321 found = -1;
3322
3323 for (i = 0; i < MAX_CITYCHUNKS; i++)
3324 {
3325 if (citychunks[i].alive == 0)
3326 found = i;
3327 }
3328
3329 if (found != -1)
3330 {
3331 citychunks[found].alive = 1;
3332 citychunks[found].is_defensechunk = what;
3333
3334 citychunks[found].x1 = x + randnum(10) - 5;
3335 citychunks[found].y1 = y;
3336 citychunks[found].z1 = z + randnum(10) - 5;
3337
3338 citychunks[found].xm1 = randnum(6) - 3;
3339 citychunks[found].ym1 = -randnum(3) - 10;
3340 citychunks[found].zm1 = randnum(6) - 3;
3341
3342 citychunks[found].x2 = x + randnum(10) - 5;
3343 citychunks[found].y2 = y - 20;
3344 citychunks[found].z2 = z + randnum(10) - 5;
3345
3346 citychunks[found].xm2 = randnum(6) - 3;
3347 citychunks[found].ym2 = -randnum(3) - 10;
3348 citychunks[found].zm2 = randnum(6) - 3;
3349 }
3350 }
3351
3352
3353
3354 /* Turns on letter graphics: */
3355
turnonletters(char * str,int wavey,float wantz)3356 void turnonletters(char * str, int wavey, float wantz)
3357 {
3358 int i, r;
3359
3360 showingletters = 1;
3361
3362 for (i = 0; i < MAX_LETTERS; i++)
3363 letters[i].alive = 0;
3364
3365 for (i = 0; i < strlen(str); i++)
3366 {
3367 letters[i].alive = 1;
3368 letters[i].letter = str[i];
3369
3370 letters[i].x = randnum(X_WIDTH);
3371 letters[i].y = randnum(Y_HEIGHT);
3372 letters[i].z = Z_DEPTH * 2 + randnum(50);
3373
3374 letters[i].destx = (X_WIDTH / 2) + (i - 3) * 20;
3375 letters[i].desty = Y_HEIGHT / 2;
3376 letters[i].destz = wantz;
3377
3378 r = randnum(5);
3379
3380 letters[i].xm = (letters[i].destx - letters[i].x) / (15 + i * 2 + r);
3381 letters[i].ym = (letters[i].desty - letters[i].y) / (15 + i * 2 + r);
3382 letters[i].zm = (letters[i].destz - letters[i].z) / (15 + i * 2 + r);
3383
3384 letters[i].killatdest = 0;
3385 letters[i].wave = wavey;
3386 }
3387 }
3388
3389
3390 /* Make letters fly away: */
3391
turnoffletters(void)3392 void turnoffletters(void)
3393 {
3394 int i;
3395
3396 for (i = 0; i < MAX_LETTERS; i++)
3397 {
3398 letters[i].destz = -200;
3399 letters[i].zm = (letters[i].destz - letters[i].z) / 20;
3400 letters[i].killatdest = 1;
3401 }
3402
3403 showingletters = 0;
3404 }
3405
3406
3407 /* Init game values: */
3408
initgame(void)3409 void initgame(void)
3410 {
3411 int i, j;
3412 float x1, z1;
3413
3414
3415 /* Init cities : */
3416
3417 x1 = X_WIDTH / 8;
3418 z1 = Z_DEPTH / 8;
3419
3420 for (i = 0; i < MAX_CITIES; i++)
3421 {
3422 cities[i].alive = 1;
3423 cities[i].percent = 0;
3424 cities[i].bonused = 0;
3425
3426 cities[i].x = x1;
3427 cities[i].z = z1;
3428
3429 for (j = 0; j < 4; j++)
3430 {
3431 cities[i].height[j] = randnum(40) + 10;
3432 cities[i].shape[j] = randnum(2);
3433 }
3434
3435 x1 = x1 + (X_WIDTH / 4);
3436 if (x1 >= X_WIDTH)
3437 {
3438 x1 = X_WIDTH / 8;
3439 z1 = z1 + (Z_DEPTH / 4);
3440 }
3441 }
3442
3443
3444 /* Init. score: */
3445
3446 score = 0;
3447 }
3448
3449
3450 /* End of level sequence: */
3451
endoflevel(void)3452 void endoflevel(void)
3453 {
3454 level++;
3455 initlevel();
3456 }
3457
3458
3459 /* Convert 3D to screen (2D): */
3460
calc3d(float * sx,float * sy,float x,float y,float z)3461 int calc3d(float * sx, float * sy, float x, float y, float z)
3462 {
3463 float xx, yy, zz;
3464 float cos_x, cos_y, sin_x, sin_y, hyp;
3465 int ok;
3466
3467
3468 ok = 0;
3469
3470 /* Calc. x,y,z based on your viewpoint: */
3471
3472 if (missile_view == 1)
3473 {
3474 /* Missile view: */
3475
3476 x = x - missiles[missile_view_missile].x -
3477 missiles[missile_view_missile].xm;
3478 y = y - missiles[missile_view_missile].y - DISTANCE + 80;
3479 z = z - missiles[missile_view_missile].z -
3480 missiles[missile_view_missile].zm;
3481
3482 xx = x;
3483 yy = -z;
3484 zz = y;
3485 }
3486 else if (bullet_view == 1)
3487 {
3488 /* Bullet view: */
3489
3490 x = x - bullets[bullet_view_bullet].x -
3491 bullets[bullet_view_bullet].xm * 2;
3492 y = y - bullets[bullet_view_bullet].y -
3493 bullets[bullet_view_bullet].ym * 2;
3494 z = z - bullets[bullet_view_bullet].z -
3495 bullets[bullet_view_bullet].zm * 2;
3496
3497 hyp = sqrt((bullets[bullet_view_bullet].ym *
3498 bullets[bullet_view_bullet].ym) +
3499 (bullets[bullet_view_bullet].xm *
3500 bullets[bullet_view_bullet].xm));
3501
3502 cos_x = bullets[bullet_view_bullet].xm / hyp;
3503 sin_x = bullets[bullet_view_bullet].zm / hyp;
3504
3505 cos_y = bullets[bullet_view_bullet].xm / hyp;
3506 sin_y = bullets[bullet_view_bullet].ym / hyp;
3507
3508 xx = x * cos_x - z * sin_x;
3509 zz = x * sin_x + z * cos_x;
3510
3511 yy = y * cos_y - zz * sin_y;
3512 zz = y * sin_y + zz * cos_y;
3513
3514 zz = zz - DISTANCE;
3515 }
3516 else
3517 {
3518 /* Normal (freeview) view: */
3519
3520 x = x - X_WIDTH / 2;
3521 y = y - Y_HEIGHT / 2;
3522 z = z - Z_DEPTH / 2;
3523
3524 xx = x * cos_anglex - z * sin_anglex;
3525 zz = x * sin_anglex + z * cos_anglex;
3526
3527 yy = y * cos_angley - zz * sin_angley;
3528 zz = y * sin_angley + zz * cos_angley;
3529 }
3530
3531
3532 zz = zz - zoom;
3533
3534 if (zz > -DISTANCE)
3535 {
3536 /* Convert (x,y,z) into (x,y) with a 3D look: */
3537
3538 *sx = xx / ((zz + DISTANCE) / ASPECT);
3539 *sy = yy / ((zz + DISTANCE) / ASPECT);
3540
3541
3542 /* Transpose (0,0) origin to the center of the window: */
3543
3544 *sx = *sx + WIDTH / 2;
3545 *sy = *sy + HEIGHT / 2;
3546
3547 ok = 1;
3548 }
3549
3550 return(ok);
3551 }
3552
3553
sign(float v)3554 float sign(float v)
3555 {
3556 if (v < 0.0)
3557 return(-1.0);
3558 else if (v > 0.0)
3559 return(1.0);
3560 else
3561 return(0.0);
3562 }
3563