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