1 /*
2   vectoroids.c
3 
4   An asteroid shooting game with vector graphics.
5   Based on "Agendaroids."
6 
7   by Bill Kendrick
8   bill@newbreedsoftware.com
9   http://www.newbreedsoftware.com/vectoroids/
10 
11   November 30, 2001 - April 20, 2002
12 */
13 
14 #define VER_VERSION "1.1.0"
15 #define VER_DATE "2002.04.20"
16 
17 #ifndef EMBEDDED
18 #define STATE_FORMAT_VERSION "2001.12.01"
19 #else
20 #define STATE_FORMAT_VERSION "2001.12.01e"
21 #endif
22 
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <SDL.h>
28 #include <SDL_image.h>
29 #ifndef NOSOUND
30 #include <SDL_mixer.h>
31 #endif
32 
33 
34 #ifndef DATA_PREFIX
35 #define DATA_PREFIX "data/"
36 #endif
37 
38 
39 /* Constraints: */
40 
41 #ifndef EMBEDDED
42   #define NUM_BULLETS 2
43 #else
44   #define NUM_BULLETS 3
45 #endif
46 
47 #ifndef EMBEDDED
48   #define NUM_ASTEROIDS 20
49   #define NUM_BITS 50
50 #else
51   #define NUM_ASTEROIDS 15
52   #define NUM_BITS 25
53 #endif
54 
55 #define AST_SIDES 6
56 #ifndef EMBEDDED
57   #define AST_RADIUS 10
58   #define SHIP_RADIUS 20
59 #else
60   #define AST_RADIUS 7
61   #define SHIP_RADIUS 12
62 #endif
63 
64 #define ZOOM_START 40
65 #define ONEUP_SCORE 10000
66 #define FPS 50
67 
68 #ifndef EMBEDDED
69   #define WIDTH 480
70   #define HEIGHT 480
71 #else
72   #define WIDTH 240
73   #define HEIGHT 320
74 #endif
75 
76 
77 
78 enum { FALSE, TRUE };
79 
80 #define LEFT_EDGE   0x0001
81 #define RIGHT_EDGE  0x0002
82 #define TOP_EDGE    0x0004
83 #define BOTTOM_EDGE 0x0008
84 
85 
86 /* Types: */
87 
88 typedef struct letter_type {
89   int x, y;
90   int xm, ym;
91 } letter_type;
92 
93 typedef struct bullet_type {
94   int timer;
95   int x, y;
96   int xm, ym;
97 } bullet_type;
98 
99 typedef struct shape_type {
100   int radius;
101   int angle;
102 } shape_type;
103 
104 typedef struct asteroid_type {
105   int alive, size;
106   int x, y;
107   int xm, ym;
108   int angle, angle_m;
109   shape_type shape[AST_SIDES];
110 } asteroid_type;
111 
112 typedef struct bit_type {
113   int timer;
114   int x, y;
115   int xm, ym;
116 } bit_type;
117 
118 typedef struct color_type {
119   Uint8 r;
120   Uint8 g;
121   Uint8 b;
122 } color_type;
123 
124 
125 /* Data: */
126 
127 enum {
128   SND_BULLET,
129   SND_AST1,
130   SND_AST2,
131   SND_AST3,
132   SND_AST4,
133   SND_THRUST,
134   SND_EXPLODE,
135   SND_GAMEOVER,
136   SND_EXTRALIFE,
137   NUM_SOUNDS
138 };
139 
140 char * sound_names[NUM_SOUNDS] = {
141   DATA_PREFIX "sounds/bullet.wav",
142   DATA_PREFIX "sounds/ast1.wav",
143   DATA_PREFIX "sounds/ast2.wav",
144   DATA_PREFIX "sounds/ast3.wav",
145   DATA_PREFIX "sounds/ast4.wav",
146   DATA_PREFIX "sounds/thrust.wav",
147   DATA_PREFIX "sounds/explode.wav",
148   DATA_PREFIX "sounds/gameover.wav",
149   DATA_PREFIX "sounds/extralife.wav"
150 };
151 
152 #define CHAN_THRUST 0
153 
154 char * mus_game_name = DATA_PREFIX "music/decision.s3m";
155 
156 
157 #ifdef JOY_YES
158 #define JOY_A 0
159 #define JOY_B 1
160 #define JOY_X 0
161 #define JOY_Y 1
162 #endif
163 
164 
165 /* Globals: */
166 
167 SDL_Surface * screen, * bkgd;
168 #ifndef NOSOUND
169 Mix_Chunk * sounds[NUM_SOUNDS];
170 Mix_Music * game_music;
171 #endif
172 #ifdef JOY_YES
173 SDL_Joystick *js;
174 #endif
175 bullet_type bullets[NUM_BULLETS];
176 asteroid_type asteroids[NUM_ASTEROIDS];
177 bit_type bits[NUM_BITS];
178 int use_sound, use_joystick, fullscreen, text_zoom;
179 char zoom_str[24];
180 int x, y, xm, ym, angle;
181 int player_alive, player_die_timer;
182 int lives, score, high, level, game_pending;
183 
184 
185 /* Trig junk:  (thanks to Atari BASIC for this) */
186 
187 int trig[12] = {
188   1024,
189   1014,
190   984,
191   935,
192   868,
193   784,
194   685,
195   572,
196   448,
197   316,
198   117,
199   0
200 };
201 
202 
203 /* Characters: */
204 
205 int char_vectors[36][5][4] = {
206   {
207     /* 0 */
208     { 0, 0, 1, 0 },
209     { 1, 0, 1, 2 },
210     { 1, 2, 0, 2 },
211     { 0, 2, 0, 0 },
212     { -1, -1, -1, -1 }
213   },
214 
215   {
216     /* 1 */
217     { 1, 0, 1, 2 },
218     { -1, -1, -1, -1 },
219     { -1, -1, -1, -1 },
220     { -1, -1, -1, -1 },
221     { -1, -1, -1, -1 }
222   },
223 
224   {
225     /* 2 */
226     { 1, 0, 0, 0 },
227     { 1, 0, 1, 1 },
228     { 0, 1, 1, 1 },
229     { 0, 1, 0, 2 },
230     { 1, 2, 0, 2 },
231   },
232 
233   {
234     /* 3 */
235     { 0, 0, 1, 0 },
236     { 1, 0, 1, 2 },
237     { 0, 1, 1, 1 },
238     { 0, 2, 1, 2 },
239     { -1, -1, -1, -1 }
240   },
241 
242   {
243     /* 4 */
244     { 1, 0, 1, 2 },
245     { 0, 0, 0, 1 },
246     { 0, 1, 1, 1 },
247     { -1, -1, -1, -1 },
248     { -1, -1, -1, -1 }
249   },
250 
251   {
252     /* 5 */
253     { 1, 0, 0, 0 },
254     { 0, 0, 0, 1 },
255     { 0, 1, 1, 1 },
256     { 1, 1, 1, 2 },
257     { 1, 2, 0, 2 }
258   },
259 
260   {
261     /* 6 */
262     { 1, 0, 0, 0 },
263     { 0, 0, 0, 2 },
264     { 0, 2, 1, 2 },
265     { 1, 2, 1, 1 },
266     { 1, 1, 0, 1 }
267   },
268 
269   {
270     /* 7 */
271     { 0, 0, 1, 0 },
272     { 1, 0, 1, 2 },
273     { -1, -1, -1, -1 },
274     { -1, -1, -1, -1 },
275     { -1, -1, -1, -1 }
276   },
277 
278   {
279     /* 8 */
280     { 0, 0, 1, 0 },
281     { 0, 0, 0, 2 },
282     { 1, 0, 1, 2 },
283     { 0, 2, 1, 2 },
284     { 0, 1, 1, 1 }
285   },
286 
287   {
288     /* 9 */
289     { 1, 0, 1, 2 },
290     { 0, 0, 1, 0 },
291     { 0, 0, 0, 1 },
292     { 0, 1, 1, 1 },
293     { -1, -1, -1, -1 }
294   },
295 
296   {
297     /* A */
298     { 0, 2, 0, 1 },
299     { 0, 1, 1, 0 },
300     { 1, 0, 1, 2 },
301     { 0, 1, 1, 1 },
302     { -1, -1, -1, -1 }
303   },
304 
305   {
306     /* B */
307     { 0, 2, 0, 0 },
308     { 0, 0, 1, 0 },
309     { 1, 0, 0, 1 },
310     { 0, 1, 1, 2 },
311     { 1, 2, 0, 2 }
312   },
313 
314   {
315     /* C */
316     { 1, 0, 0, 0 },
317     { 0, 0, 0, 2 },
318     { 0, 2, 1, 2 },
319     { -1, -1, -1, -1 },
320     { -1, -1, -1, -1 }
321   },
322 
323   {
324     /* D */
325     { 0, 0, 1, 1 },
326     { 1, 1, 0, 2 },
327     { 0, 2, 0, 0 },
328     { -1, -1, -1, -1 },
329     { -1, -1, -1, -1 }
330   },
331 
332   {
333     /* E */
334     { 1, 0, 0, 0 },
335     { 0, 0, 0, 2 },
336     { 0, 2, 1, 2 },
337     { 0, 1, 1, 1 },
338     { -1, -1, -1, -1 }
339   },
340 
341   {
342     /* F */
343     { 1, 0, 0, 0 },
344     { 0, 0, 0, 2 },
345     { 0, 1, 1, 1 },
346     { -1, -1, -1, -1 },
347     { -1, -1, -1, -1 }
348   },
349 
350   {
351     /* G */
352     { 1, 0, 0, 0 },
353     { 0, 0, 0, 2 },
354     { 0, 2, 1, 2 },
355     { 1, 2, 1, 1 },
356     { -1, -1, -1, -1 }
357   },
358 
359   {
360     /* H */
361     { 0, 0, 0, 2 },
362     { 1, 0, 1, 2 },
363     { 0, 1, 1, 1 },
364     { -1, -1, -1, -1 },
365     { -1, -1, -1, -1 }
366   },
367 
368   {
369     /* I */
370     { 1, 0, 1, 2 },
371     { -1, -1, -1, -1 },
372     { -1, -1, -1, -1 },
373     { -1, -1, -1, -1 },
374     { -1, -1, -1, -1 }
375   },
376 
377   {
378     /* J */
379     { 1, 0, 1, 2 },
380     { 1, 2, 0, 2 },
381     { 0, 2, 0, 1 },
382     { -1, -1, -1, -1 },
383     { -1, -1, -1, -1 }
384   },
385 
386   {
387     /* K */
388     { 0, 0, 0, 2 },
389     { 1, 0, 0, 1 },
390     { 0, 1, 1, 2 },
391     { -1, -1, -1, -1 },
392     { -1, -1, -1, -1 }
393   },
394 
395   {
396     /* L */
397     { 0, 0, 0, 2 },
398     { 0, 2, 1, 2 },
399     { -1, -1, -1, -1 },
400     { -1, -1, -1, -1 },
401     { -1, -1, -1, -1 }
402   },
403 
404   {
405     /* M */
406     { 0, 0, 0, 2 },
407     { 1, 0, 1, 2 },
408     { 0, 0, 1, 1 },
409     { 0, 1, 1, 0 },
410     { -1, -1, -1, -1 }
411   },
412 
413   {
414     /* N */
415     { 0, 2, 0, 0 },
416     { 0, 0, 1, 2 },
417     { 1, 2, 1, 0 },
418     { -1, -1, -1, -1 },
419     { -1, -1, -1, -1 }
420   },
421 
422   {
423     /* O */
424     { 0, 0, 1, 0 },
425     { 1, 0, 1, 2 },
426     { 1, 2, 0, 2 },
427     { 0, 2, 0, 0 },
428     { -1, -1, -1, -1 }
429   },
430 
431   {
432     /* P */
433     { 0, 2, 0, 0 },
434     { 0, 0, 1, 0 },
435     { 1, 0, 1, 1 },
436     { 1, 1, 0, 1 },
437     { -1, -1, -1, -1 }
438   },
439 
440   {
441     /* Q */
442     { 0, 0, 1, 0 },
443     { 1, 0, 1, 2 },
444     { 1, 2, 0, 2 },
445     { 0, 2, 0, 0 },
446     { 0, 1, 1, 2 }
447   },
448 
449   {
450     /* R */
451     { 0, 2, 0, 0 },
452     { 0, 0, 1, 0 },
453     { 1, 0, 1, 1 },
454     { 1, 1, 0, 1 },
455     { 0, 1, 1, 2 }
456   },
457 
458   {
459     /* S */
460     { 1, 0, 0, 0 },
461     { 0, 0, 0, 1 },
462     { 0, 1, 1, 1 },
463     { 1, 1, 1, 2 },
464     { 1, 2, 0, 2 }
465   },
466 
467   {
468     /* T */
469     { 0, 0, 1, 0 },
470     { 1, 0, 1, 2 },
471     { -1, -1, -1, -1 },
472     { -1, -1, -1, -1 },
473     { -1, -1, -1, -1 }
474   },
475 
476   {
477     /* U */
478     { 0, 0, 0, 2 },
479     { 0, 2, 1, 2 },
480     { 1, 2, 1, 0 },
481     { -1, -1, -1, -1 },
482     { -1, -1, -1, -1 }
483   },
484 
485   {
486     /* V */
487     { 0, 0, 0, 1 },
488     { 0, 1, 1, 2 },
489     { 1, 2, 1, 0 },
490     { -1, -1, -1, -1 },
491     { -1, -1, -1, -1 }
492   },
493 
494   {
495     /* W */
496     { 0, 0, 0, 2 },
497     { 1, 0, 1, 2 },
498     { 0, 1, 1, 2 },
499     { 0, 2, 1, 1 },
500     { -1, -1, -1, -1 }
501   },
502 
503   {
504     /* X */
505     { 0, 0, 1, 2 },
506     { 0, 2, 1, 0 },
507     { -1, -1, -1, -1 },
508     { -1, -1, -1, -1 },
509     { -1, -1, -1, -1 }
510   },
511 
512   {
513     /* Y */
514     { 0, 0, 1, 1 },
515     { 1, 0, 1, 2 },
516     { -1, -1, -1, -1 },
517     { -1, -1, -1, -1 },
518     { -1, -1, -1, -1 }
519   },
520 
521   {
522     /* Z */
523     { 0, 0, 1, 0 },
524     { 1, 0, 0, 2 },
525     { 0, 2, 1, 2 },
526     { -1, -1, -1, -1 },
527     { -1, -1, -1, -1 }
528   }
529 };
530 
531 
532 
533 /* Local function prototypes: */
534 
535 int title(void);
536 int game(void);
537 void finish(void);
538 void setup(int argc, char * argv[]);
539 void seticon(void);
540 int fast_cos(int v);
541 int fast_sin(int v);
542 void draw_line(int x1, int y1, color_type c1,
543 	       int x2, int y2, color_type c2);
544 int clip(int * x1, int * y1, int * x2, int * y2);
545 color_type mkcolor(int r, int g, int b);
546 void sdl_drawline(int x1, int y1, color_type c1,
547 		  int x2, int y2, color_type c2);
548 unsigned char encode(float x, float y);
549 void drawvertline(int x, int y1, color_type c1,
550                   int y2, color_type c2);
551 void putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel);
552 void draw_segment(int r1, int a1,
553 		  color_type c1,
554 		  int r2, int a2,
555 		  color_type c2,
556 		  int cx, int cy, int ang);
557 void add_bullet(int x, int y, int a, int xm, int ym);
558 void add_asteroid(int x, int y, int xm, int ym, int size);
559 void add_bit(int x, int y, int xm, int ym);
560 void draw_asteroid(int size, int x, int y, int angle, shape_type * shape);
561 void playsound(int snd);
562 void hurt_asteroid(int j, int xm, int ym, int exp_size);
563 void add_score(int amount);
564 void draw_char(char c, int x, int y, int r, color_type cl);
565 void draw_text(char * str, int x, int y, int s, color_type c);
566 void draw_thick_line(int x1, int y1, color_type c1,
567 		     int x2, int y2, color_type c2);
568 void reset_level(void);
569 void show_version(void);
570 void show_usage(FILE * f, char * prg);
571 SDL_Surface * set_vid_mode(unsigned flags);
572 void draw_centered_text(char * str, int y, int s, color_type c);
573 
574 
575 /* --- MAIN --- */
576 
main(int argc,char * argv[])577 int main(int argc, char * argv[])
578 {
579   int done;
580   FILE * fi;
581   char statefile[256], buf[256];
582 
583 
584   setup(argc, argv);
585 
586 
587   /* Set defaults: */
588 
589   score = 0;
590   high = 0;
591   game_pending = 0;
592 
593 
594   /* Load state from disk: */
595 
596 #ifndef _WIN32
597   /* snprintf(statefile, sizeof(statefile), "%s/.vectoroids-state",
598 	   getenv("HOME")); */
599   sprintf(statefile, "%s/.vectoroids-state",
600 	   getenv("HOME"));
601 #else
602   sprintf(statefile, "vectoroids-state.dat");
603 #endif
604 
605   fi = fopen(statefile, "r");
606   if (fi != NULL)
607   {
608     /* Skip comment line: */
609 
610     fgets(buf, sizeof(buf), fi);
611 
612 
613     /* Grab statefile version: */
614 
615     fgets(buf, sizeof(buf), fi);
616     buf[strlen(buf) - 1] = '\0';
617 
618     if (strcmp(buf, STATE_FORMAT_VERSION) != 0)
619     {
620       fprintf(stderr, "Vectoroids state file format has been updated.\n"
621 		      "Old game state is unreadable.  Sorry!\n");
622     }
623     else
624     {
625       game_pending = fgetc(fi);
626       lives = fgetc(fi);
627       level = fgetc(fi);
628       player_alive = fgetc(fi);
629       player_die_timer = fgetc(fi);
630       fread(&score, sizeof(int), 1, fi);
631       fread(&high, sizeof(int), 1, fi);
632       fread(&x, sizeof(int), 1, fi);
633       fread(&y, sizeof(int), 1, fi);
634       fread(&xm, sizeof(int), 1, fi);
635       fread(&ym, sizeof(int), 1, fi);
636       fread(&angle, sizeof(int), 1, fi);
637       fread(bullets, sizeof(bullet_type), NUM_BULLETS, fi);
638       fread(asteroids, sizeof(asteroid_type), NUM_ASTEROIDS, fi);
639       fread(bits, sizeof(bit_type), NUM_BITS, fi);
640     }
641 
642     fclose(fi);
643   }
644 
645 
646 
647   /* Main app loop! */
648 
649   do
650   {
651     done = title();
652 
653     if (!done)
654     {
655       done = game();
656     }
657   }
658   while (!done);
659 
660 
661   /* Save state: */
662 
663   fi = fopen(statefile, "w");
664   if (fi == NULL)
665   {
666     perror(statefile);
667   }
668   else
669   {
670     fprintf(fi, "Vectoroids State File\n");
671     fprintf(fi, "%s\n", STATE_FORMAT_VERSION);
672 
673     fputc(game_pending, fi);
674     fputc(lives, fi);
675     fputc(level, fi);
676     fputc(player_alive, fi);
677     fputc(player_die_timer, fi);
678     fwrite(&score, sizeof(int), 1, fi);
679     fwrite(&high, sizeof(int), 1, fi);
680     fwrite(&x, sizeof(int), 1, fi);
681     fwrite(&y, sizeof(int), 1, fi);
682     fwrite(&xm, sizeof(int), 1, fi);
683     fwrite(&ym, sizeof(int), 1, fi);
684     fwrite(&angle, sizeof(int), 1, fi);
685     fwrite(bullets, sizeof(bullet_type), NUM_BULLETS, fi);
686     fwrite(asteroids, sizeof(asteroid_type), NUM_ASTEROIDS, fi);
687     fwrite(bits, sizeof(bit_type), NUM_BITS, fi);
688 
689     fclose(fi);
690   }
691 
692   finish();
693 
694   return(0);
695 }
696 
697 
698 /* Title screen: */
699 
title(void)700 int title(void)
701 {
702   int done, quit;
703   int i, snapped, angle, size, counter, x, y, xm, ym, z1, z2, z3;
704   SDL_Event event;
705   SDLKey key;
706   Uint32 now_time, last_time;
707   char * titlestr = "VECTOROIDS";
708   char str[20];
709   letter_type letters[11];
710 
711 
712   /* Reset letters: */
713 
714   snapped = 0;
715 
716   for (i = 0; i < strlen(titlestr); i++)
717   {
718     letters[i].x = (rand() % WIDTH);
719     letters[i].y = (rand() % HEIGHT);
720     letters[i].xm = 0;
721     letters[i].ym = 0;
722   }
723 
724   x = (rand() % WIDTH);
725   y = (rand() % HEIGHT);
726   xm = (rand() % 4) + 2;
727   ym = (rand() % 10) - 5;
728 
729   counter = 0;
730   angle = 0;
731   size = 40;
732 
733   done = 0;
734   quit = 0;
735 
736   do
737   {
738     last_time = SDL_GetTicks();
739 
740     counter++;
741 
742 
743     /* Rotate rock: */
744 
745     angle = ((angle + 2) % 360);
746 
747 
748     /* Make rock grow: */
749 
750     if ((counter % 3) == 0)
751     {
752       if (size > 1)
753         size--;
754     }
755 
756 
757     /* Move rock: */
758 
759     x = x + xm;
760 
761     if (x >= WIDTH)
762       x = x - WIDTH;
763 
764     y = y + ym;
765 
766     if (y >= HEIGHT)
767       y = y - HEIGHT;
768     else if (y < 0)
769       y = y + HEIGHT;
770 
771 
772     /* Handle events: */
773 
774     while (SDL_PollEvent(&event) > 0)
775     {
776       if (event.type == SDL_QUIT)
777       {
778 	done = 1;
779 	quit = 1;
780       }
781       else if (event.type == SDL_KEYDOWN)
782       {
783         key = event.key.keysym.sym;
784 
785 	if (key == SDLK_SPACE)
786         {
787 	  done = 1;
788 	}
789 	else if (key == SDLK_ESCAPE)
790 	{
791 	  done = 1;
792 	  quit = 1;
793 	}
794       }
795 #ifdef JOY_YES
796       else if (event.type == SDL_JOYBUTTONDOWN)
797 	{
798 	  done = 1;
799 	}
800 #endif
801       else if (event.type == SDL_MOUSEBUTTONDOWN)
802 	{
803 	  if (event.button.x >= (WIDTH - 50) / 2 &&
804 	      event.button.x <= (WIDTH + 50) / 2 &&
805 	      event.button.y >= 180 && event.button.y <= 195)
806 	    {
807 	      /* Start! */
808 
809 	      game_pending = 0;
810   	      done = 1;
811 	    }
812 	  else if (event.button.x >= (WIDTH - 80) / 2 &&
813 		   event.button.x <= (WIDTH + 80) / 2 &&
814 	           event.button.y >= 200 && event.button.y <= 215 &&
815 		   game_pending)
816 	    {
817 	      done = 1;
818 	    }
819 	}
820     }
821 
822 
823     /* Move title characters: */
824 
825     if (snapped < strlen(titlestr))
826     {
827       for (i = 0; i < strlen(titlestr); i++)
828       {
829         letters[i].x = letters[i].x + letters[i].xm;
830         letters[i].y = letters[i].y + letters[i].ym;
831 
832 
833         /* Home in on final spot! */
834 
835         if (letters[i].x > ((WIDTH - (strlen(titlestr) * 14)) / 2 +
836 			    (i * 14)) &&
837 	    letters[i].xm > -4)
838   	  letters[i].xm--;
839         else if (letters[i].x < ((WIDTH - (strlen(titlestr) * 14)) / 2 +
840 				 (i * 14)) &&
841 		 letters[i].xm < 4)
842   	  letters[i].xm++;
843 
844         if (letters[i].y > 100 && letters[i].ym > -4)
845           letters[i].ym--;
846         else if (letters[i].y < 100 && letters[i].ym < 4)
847           letters[i].ym++;
848 
849 
850         /* Snap into place: */
851 
852         if (letters[i].x >= ((WIDTH - (strlen(titlestr) * 14)) / 2 +
853 			     (i * 14)) - 8 &&
854             letters[i].x <= ((WIDTH - (strlen(titlestr) * 14)) / 2 +
855 			     (i * 14)) + 8 &&
856   	    letters[i].y >= 92 &&
857 	    letters[i].y <= 108 &&
858 	    (letters[i].xm != 0 ||
859 	     letters[i].ym != 0))
860         {
861   	  letters[i].x = ((WIDTH - (strlen(titlestr) * 14)) / 2 + (i * 14));
862    	  letters[i].xm = 0;
863 
864  	  letters[i].y = 100;
865           letters[i].ym = 0;
866 
867 	  snapped++;
868         }
869       }
870     }
871 
872 
873     /* Draw screen: */
874 
875     /* (Erase first) */
876 
877     SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
878 
879 
880     /* (Title) */
881 
882     if (snapped != strlen(titlestr))
883       {
884 	for (i = 0; i < strlen(titlestr); i++)
885 	  {
886 	    draw_char(titlestr[i], letters[i].x, letters[i].y, 10,
887 		      mkcolor(255, 255, 255));
888 	  }
889       }
890     else
891       {
892 	for (i = 0; i < strlen(titlestr); i++)
893 	  {
894 	    z1 = (i + counter) % 255;
895 	    z2 = ((i + counter + 128) * 2) % 255;
896 	    z3 = ((i + counter) * 5) % 255;
897 
898 	    draw_char(titlestr[i], letters[i].x, letters[i].y, 10,
899 		      mkcolor(z1, z2, z3));
900 	  }
901       }
902 
903 
904     /* (Credits) */
905 
906     if (snapped == strlen(titlestr))
907     {
908       draw_centered_text("BY BILL KENDRICK", 140, 5,
909 		mkcolor(128, 128,128));
910       draw_centered_text("NEW BREED SOFTWARE", 155, 5,
911 		mkcolor(96, 96, 96));
912 
913       sprintf(str, "HIGH %.6d", high);
914       draw_text(str, (WIDTH - 110) / 2, 5, 5, mkcolor(128, 255, 255));
915       draw_text(str, (WIDTH - 110) / 2 + 1, 6, 5, mkcolor(128, 255, 255));
916 
917       if (score != 0 && (score != high || (counter % 20) < 10))
918       {
919 	if (game_pending == 0)
920           sprintf(str, "LAST %.6d", score);
921 	else
922           sprintf(str, "SCR  %.6d", score);
923         draw_text(str, (WIDTH - 110) / 2, 25, 5, mkcolor(128, 128, 255));
924         draw_text(str, (WIDTH - 110) / 2 + 1, 26, 5, mkcolor(128, 128, 255));
925       }
926     }
927 
928 
929     draw_text("START", (WIDTH - 50) / 2, 180, 5, mkcolor(0, 255, 0));
930 
931     if (game_pending)
932       draw_text("CONTINUE", (WIDTH - 80) / 2, 200, 5, mkcolor(0, 255, 0));
933 
934 
935     /* (Giant rock) */
936 
937     draw_segment(40 / size, 0, mkcolor(255, 255, 255),
938 		 30 / size, 30, mkcolor(255, 255, 255),
939 		 x, y, angle);
940     draw_segment(30 / size, 30, mkcolor(255, 255, 255),
941 		 40 / size, 55, mkcolor(255, 255, 255),
942 		 x, y, angle);
943     draw_segment(40 / size, 55, mkcolor(255, 255, 255),
944 		 25 / size, 90, mkcolor(255, 255, 255),
945 		 x, y, angle);
946     draw_segment(25 / size, 90, mkcolor(255, 255, 255),
947 		 40 / size, 120, mkcolor(255, 255, 255),
948 		 x, y, angle);
949     draw_segment(40 / size, 120, mkcolor(255, 255, 255),
950 		 35 / size, 130, mkcolor(255, 255, 255),
951 		 x, y, angle);
952     draw_segment(35 / size, 130, mkcolor(255, 255, 255),
953 		 40 / size, 160, mkcolor(255, 255, 255),
954 		 x, y, angle);
955     draw_segment(40 / size, 160, mkcolor(255, 255, 255),
956 		 30 / size, 200, mkcolor(255, 255, 255),
957 		 x, y, angle);
958     draw_segment(30 / size, 200, mkcolor(255, 255, 255),
959 		 45 / size, 220, mkcolor(255, 255, 255),
960 		 x, y, angle);
961     draw_segment(45 / size, 220, mkcolor(255, 255, 255),
962 		 25 / size, 265, mkcolor(255, 255, 255),
963 		 x, y, angle);
964     draw_segment(25 / size, 265, mkcolor(255, 255, 255),
965 		 30 / size, 300, mkcolor(255, 255, 255),
966 		 x, y, angle);
967     draw_segment(30 / size, 300, mkcolor(255, 255, 255),
968 		 45 / size, 335, mkcolor(255, 255, 255),
969 		 x, y, angle);
970     draw_segment(45 / size, 335, mkcolor(255, 255, 255),
971 		 40 / size, 0, mkcolor(255, 255, 255),
972 		 x, y, angle);
973 
974 
975     /* Flush and pause! */
976 
977     SDL_Flip(screen);
978 
979     now_time = SDL_GetTicks();
980 
981     if (now_time < last_time + (1000 / FPS))
982       {
983 	SDL_Delay(last_time + 1000 / FPS - now_time);
984       }
985   }
986   while (!done);
987 
988   return(quit);
989 }
990 
991 
992 
993 /* --- GAME --- */
994 
game(void)995 int game(void)
996 {
997   int done, quit, counter;
998   int i, j;
999   int num_asteroids_alive;
1000   SDL_Event event;
1001   SDLKey key;
1002   int left_pressed, right_pressed, up_pressed, shift_pressed;
1003   char str[10];
1004   Uint32 now_time, last_time;
1005 
1006 
1007   done = 0;
1008   quit = 0;
1009   counter = 0;
1010 
1011   left_pressed = 0;
1012   right_pressed = 0;
1013   up_pressed = 0;
1014   shift_pressed = 0;
1015 
1016   if (game_pending == 0)
1017   {
1018     lives = 3;
1019     score = 0;
1020 
1021     player_alive = 1;
1022     player_die_timer = 0;
1023     angle = 90;
1024     x = (WIDTH / 2) << 4;
1025     y = (HEIGHT / 2) << 4;
1026     xm = 0;
1027     ym = 0;
1028 
1029     level = 1;
1030     reset_level();
1031   }
1032 
1033   game_pending = 1;
1034 
1035 
1036   /* Hide mouse cursor: */
1037 
1038   if (fullscreen)
1039     SDL_ShowCursor(0);
1040 
1041 
1042   /* Play music: */
1043 
1044 #ifndef NOSOUND
1045       if (use_sound)
1046 	{
1047 	  if (!Mix_PlayingMusic())
1048 	    Mix_PlayMusic(game_music, -1);
1049 	}
1050 #endif
1051 
1052 
1053   do
1054     {
1055       last_time = SDL_GetTicks();
1056       counter++;
1057 
1058 
1059       /* Handle events: */
1060 
1061       while (SDL_PollEvent(&event) > 0)
1062 	{
1063 	  if (event.type == SDL_QUIT)
1064 	    {
1065 	      /* Quit! */
1066 
1067 	      done = 1;
1068 	      quit = 1;
1069 	    }
1070 	  else if (event.type == SDL_KEYDOWN ||
1071 		   event.type == SDL_KEYUP)
1072 	    {
1073 	      key = event.key.keysym.sym;
1074 
1075 	      if (event.type == SDL_KEYDOWN)
1076 		{
1077 		  if (key == SDLK_ESCAPE)
1078 		  {
1079 	            /* Return to menu! */
1080 
1081 	            done = 1;
1082 		  }
1083 
1084 
1085 		  /* Key press... */
1086 
1087 		  if (key == SDLK_RIGHT)
1088 		    {
1089 		      /* Rotate CW */
1090 
1091   		      left_pressed = 0;
1092 		      right_pressed = 1;
1093 		    }
1094 		  else if (key == SDLK_LEFT)
1095 		    {
1096 		      /* Rotate CCW */
1097 
1098 		      left_pressed = 1;
1099 		      right_pressed = 0;
1100 		    }
1101 		  else if (key == SDLK_UP)
1102 		    {
1103 		      /* Thrust! */
1104 
1105 		      up_pressed = 1;
1106 		    }
1107 		  else if ((key == SDLK_SPACE) &&
1108 		           player_alive)
1109 		    {
1110 		      /* Fire a bullet! */
1111 
1112 		      add_bullet(x >> 4, y >> 4, angle, xm, ym);
1113 		    }
1114 
1115 		  if (key == SDLK_LSHIFT ||
1116 		      key == SDLK_RSHIFT)
1117 		    {
1118 		      /* Respawn now (if applicable) */
1119 
1120 		      shift_pressed = 1;
1121 		    }
1122 		}
1123 	      else if (event.type == SDL_KEYUP)
1124 		{
1125 		  /* Key release... */
1126 
1127 		  if (key == SDLK_RIGHT)
1128 		    {
1129 		      right_pressed = 0;
1130 		    }
1131 		  else if (key == SDLK_LEFT)
1132 		    {
1133                       left_pressed = 0;
1134 		    }
1135 		  else if (key == SDLK_UP)
1136 		    {
1137 		      up_pressed = 0;
1138 		    }
1139 
1140 		  if (key == SDLK_LSHIFT ||
1141 		      key == SDLK_RSHIFT)
1142 		    {
1143 		      /* Respawn now (if applicable) */
1144 
1145 		      shift_pressed = 0;
1146 		    }
1147 		}
1148 	    }
1149 #ifdef JOY_YES
1150 	  else if (event.type == SDL_JOYBUTTONDOWN &&
1151 		   player_alive)
1152 	    {
1153 	      if (event.jbutton.button == JOY_B)
1154 		{
1155 		  /* Fire a bullet! */
1156 
1157 		  add_bullet(x >> 4, y >> 4, angle, xm, ym);
1158 		}
1159 	      else if (event.jbutton.button == JOY_A)
1160 		{
1161 		  /* Thrust: */
1162 
1163 		  up_pressed = 1;
1164 		}
1165 	      else
1166 		{
1167 		  shift_pressed = 1;
1168 		}
1169 	    }
1170 	  else if (event.type == SDL_JOYBUTTONUP)
1171 	    {
1172 	      if (event.jbutton.button == JOY_A)
1173 		{
1174 		  /* Stop thrust: */
1175 
1176 		  up_pressed = 0;
1177 		}
1178 	      else if (event.jbutton.button != JOY_B)
1179 		{
1180 		  shift_pressed = 0;
1181 		}
1182 	    }
1183 	  else if (event.type == SDL_JOYAXISMOTION)
1184 	    {
1185 	      if (event.jaxis.axis == JOY_X)
1186 		{
1187 		  if (event.jaxis.value < -256)
1188 		    {
1189 		      left_pressed = 1;
1190 		      right_pressed = 0;
1191 		    }
1192 		  else if (event.jaxis.value > 256)
1193 		    {
1194 		      left_pressed = 0;
1195 		      right_pressed = 1;
1196 		    }
1197 		  else
1198 		    {
1199 		      left_pressed = 0;
1200 		      right_pressed = 0;
1201 		    }
1202 		}
1203 	    }
1204 #endif
1205 	}
1206 
1207 
1208       /* Rotate ship: */
1209 
1210       if (right_pressed)
1211 	{
1212 	  angle = angle - 8;
1213 	  if (angle < 0)
1214 	    angle = angle + 360;
1215 	}
1216       else if (left_pressed)
1217 	{
1218 	  angle = angle + 8;
1219 	  if (angle >= 360)
1220 	    angle = angle - 360;
1221 	}
1222 
1223 
1224       /* Thrust ship: */
1225 
1226       if (up_pressed && player_alive)
1227 	{
1228 	  /* Move forward: */
1229 
1230 	  xm = xm + ((fast_cos(angle >> 3) * 3) >> 10);
1231 	  ym = ym - ((fast_sin(angle >> 3) * 3) >> 10);
1232 
1233 
1234 	  /* Start thruster sound: */
1235 #ifndef NOSOUND
1236 	  if (use_sound)
1237 	    {
1238 	      if (!Mix_Playing(CHAN_THRUST))
1239 		{
1240 #ifndef EMBEDDED
1241 		  Mix_PlayChannel(CHAN_THRUST, sounds[SND_THRUST], -1);
1242 #else
1243 		  Mix_PlayChannel(-1, sounds[SND_THRUST], 0);
1244 #endif
1245 		}
1246 	    }
1247 #endif
1248 	}
1249       else
1250         {
1251 	  /* Slow down (unrealistic, but.. feh!) */
1252 
1253           if ((counter % 20) == 0)
1254 	  {
1255             xm = (xm * 7) / 8;
1256 	    ym = (ym * 7) / 8;
1257 	  }
1258 
1259 
1260 	  /* Stop thruster sound: */
1261 
1262 #ifndef NOSOUND
1263 	  if (use_sound)
1264 	    {
1265 	      if (Mix_Playing(CHAN_THRUST))
1266 		{
1267 #ifndef EMBEDDED
1268 		  Mix_HaltChannel(CHAN_THRUST);
1269 #endif
1270 		}
1271 	    }
1272 #endif
1273 	}
1274 
1275 
1276       /* Handle player death: */
1277 
1278       if (player_alive == 0)
1279 	{
1280 	  player_die_timer--;
1281 
1282 	  if (player_die_timer <= 0)
1283 	    {
1284 	      if (lives > 0)
1285 	      {
1286 	        /* Reset player: */
1287 
1288   	        player_die_timer = 0;
1289 	        angle = 90;
1290 	        x = (WIDTH / 2) << 4;
1291 	        y = (HEIGHT / 2) << 4;
1292 	        xm = 0;
1293 	        ym = 0;
1294 
1295 
1296 	        /* Only bring player back when it's alright to! */
1297 
1298 	        player_alive = 1;
1299 
1300 	        if (!shift_pressed)
1301 		{
1302 	          for (i = 0; i < NUM_ASTEROIDS && player_alive; i++)
1303 	    	    {
1304 	 	      if (asteroids[i].alive)
1305 		        {
1306 		          if (asteroids[i].x >= (x >> 4) - (WIDTH / 5) &&
1307 			      asteroids[i].x <= (x >> 4) + (WIDTH / 5) &&
1308 			      asteroids[i].y >= (y >> 4) - (HEIGHT / 5) &&
1309 	 		      asteroids[i].y <= (y >> 4) + (HEIGHT / 5))
1310 			    {
1311 			      /* If any asteroid is too close for comfort,
1312 			         don't bring ship back yet! */
1313 
1314 			      player_alive = 0;
1315 			    }
1316 		        }
1317 	 	    }
1318 		}
1319 	      }
1320 	      else
1321 	      {
1322 	        done = 1;
1323 		game_pending = 0;
1324 	      }
1325 	    }
1326 	}
1327 
1328 
1329       /* Erase screen: */
1330 
1331       SDL_BlitSurface(bkgd, NULL, screen, NULL);
1332 
1333 
1334       /* Move ship: */
1335 
1336       x = x + xm;
1337       y = y + ym;
1338 
1339 
1340       /* Wrap ship around edges of screen: */
1341 
1342       if (x >= (WIDTH << 4))
1343 	x = x - (WIDTH << 4);
1344       else if (x < 0)
1345 	x = x + (WIDTH << 4);
1346 
1347       if (y >= (HEIGHT << 4))
1348 	y = y - (HEIGHT << 4);
1349       else if (y < 0)
1350 	    y = y + (HEIGHT << 4);
1351 
1352 
1353       /* Move bullets: */
1354 
1355       for (i = 0; i < NUM_BULLETS; i++)
1356 	{
1357 	  if (bullets[i].timer >= 0)
1358 	    {
1359 	      /* Bullet wears out: */
1360 
1361 	      bullets[i].timer--;
1362 
1363 
1364 	      /* Move bullet: */
1365 
1366 	      bullets[i].x = bullets[i].x + bullets[i].xm;
1367 	      bullets[i].y = bullets[i].y + bullets[i].ym;
1368 
1369 
1370 	      /* Wrap bullet around edges of screen: */
1371 
1372 	      if (bullets[i].x >= WIDTH)
1373 		bullets[i].x = bullets[i].x - WIDTH;
1374 	      else if (bullets[i].x < 0)
1375 		bullets[i].x = bullets[i].x + WIDTH;
1376 
1377 	      if (bullets[i].y >= HEIGHT)
1378 		bullets[i].y = bullets[i].y - HEIGHT;
1379 	      else if (bullets[i].y < 0)
1380 		bullets[i].y = bullets[i].y + HEIGHT;
1381 
1382 
1383 	      /* Check for collision with any asteroids! */
1384 
1385 	      for (j = 0; j < NUM_ASTEROIDS; j++)
1386 		{
1387 		  if (bullets[i].timer > 0 && asteroids[j].alive)
1388 		    {
1389 		      if ((bullets[i].x + 5 >=
1390 			   asteroids[j].x - asteroids[j].size * AST_RADIUS) &&
1391 			  (bullets[i].x - 5<=
1392 			   asteroids[j].x + asteroids[j].size * AST_RADIUS) &&
1393 			  (bullets[i].y + 5 >=
1394 			   asteroids[j].y - asteroids[j].size * AST_RADIUS) &&
1395 			  (bullets[i].y - 5 <=
1396 			   asteroids[j].y + asteroids[j].size * AST_RADIUS))
1397 			{
1398 			  /* Remove bullet! */
1399 
1400 			  bullets[i].timer = 0;
1401 
1402 
1403 			  hurt_asteroid(j, bullets[i].xm, bullets[i].ym,
1404 					asteroids[j].size * 3);
1405 			}
1406 		    }
1407 		}
1408 	    }
1409 	}
1410 
1411 
1412       /* Move asteroids: */
1413 
1414       num_asteroids_alive = 0;
1415 
1416       for (i = 0; i < NUM_ASTEROIDS; i++)
1417 	{
1418 	  if (asteroids[i].alive)
1419 	    {
1420 	      num_asteroids_alive++;
1421 
1422 	      /* Move asteroid: */
1423 
1424 	      if ((counter % 4) == 0)
1425 		{
1426 		  asteroids[i].x = asteroids[i].x + asteroids[i].xm;
1427 		  asteroids[i].y = asteroids[i].y + asteroids[i].ym;
1428 		}
1429 
1430 
1431 	      /* Wrap asteroid around edges of screen: */
1432 
1433 	      if (asteroids[i].x >= WIDTH)
1434 		asteroids[i].x = asteroids[i].x - WIDTH;
1435 	      else if (asteroids[i].x < 0)
1436 		asteroids[i].x = asteroids[i].x + WIDTH;
1437 
1438 	      if (asteroids[i].y >= HEIGHT)
1439 		asteroids[i].y = asteroids[i].y - HEIGHT;
1440 	      else if (asteroids[i].y < 0)
1441 		asteroids[i].y = asteroids[i].y + HEIGHT;
1442 
1443 
1444 	      /* Rotate asteroid: */
1445 
1446 	      asteroids[i].angle = (asteroids[i].angle +
1447 				    asteroids[i].angle_m);
1448 
1449 
1450 	      /* Wrap rotation angle... */
1451 
1452 	      if (asteroids[i].angle < 0)
1453 		asteroids[i].angle = asteroids[i].angle + 360;
1454 	      else if (asteroids[i].angle >= 360)
1455 		asteroids[i].angle = asteroids[i].angle - 360;
1456 
1457 
1458 	      /* See if we collided with the player: */
1459 
1460 	      if (asteroids[i].x >= (x >> 4) - SHIP_RADIUS &&
1461 		  asteroids[i].x <= (x >> 4) + SHIP_RADIUS &&
1462 		  asteroids[i].y >= (y >> 4) - SHIP_RADIUS &&
1463 		  asteroids[i].y <= (y >> 4) + SHIP_RADIUS &&
1464 		  player_alive)
1465 		{
1466 		  hurt_asteroid(i, xm >> 4, ym >> 4, NUM_BITS);
1467 
1468 		  player_alive = 0;
1469 		  player_die_timer = 30;
1470 
1471 		  playsound(SND_EXPLODE);
1472 
1473 		  /* Stop thruster sound: */
1474 
1475 #ifndef NOSOUND
1476 		  if (use_sound)
1477 		    {
1478 		      if (Mix_Playing(CHAN_THRUST))
1479 			{
1480 #ifndef EMBEDDED
1481 			  Mix_HaltChannel(CHAN_THRUST);
1482 #endif
1483 			}
1484 		    }
1485 #endif
1486 
1487 		  lives--;
1488 
1489 		  if (lives == 0)
1490 		  {
1491 #ifndef NOSOUND
1492 		    if (use_sound)
1493 		      {
1494 			playsound(SND_GAMEOVER);
1495 			playsound(SND_GAMEOVER);
1496 			playsound(SND_GAMEOVER);
1497 			/* Mix_PlayChannel(CHAN_THRUST,
1498 			   sounds[SND_GAMEOVER], 0); */
1499 		      }
1500 #endif
1501 	            player_die_timer = 100;
1502 		  }
1503 		}
1504 	    }
1505 	}
1506 
1507 
1508       /* Move bits: */
1509 
1510       for (i = 0; i < NUM_BITS; i++)
1511 	{
1512 	  if (bits[i].timer > 0)
1513 	    {
1514 	      /* Countdown bit's lifespan: */
1515 
1516 	      bits[i].timer--;
1517 
1518 
1519 	      /* Move the bit: */
1520 
1521 	      bits[i].x = bits[i].x + bits[i].xm;
1522 	      bits[i].y = bits[i].y + bits[i].ym;
1523 
1524 
1525 	      /* Wrap bit around edges of screen: */
1526 
1527 	      if (bits[i].x >= WIDTH)
1528 		bits[i].x = bits[i].x - WIDTH;
1529 	      else if (bits[i].x < 0)
1530 		bits[i].x = bits[i].x + WIDTH;
1531 
1532 	      if (bits[i].y >= HEIGHT)
1533 		bits[i].y = bits[i].y - HEIGHT;
1534 	      else if (bits[i].y < 0)
1535 		bits[i].y = bits[i].y + HEIGHT;
1536 	    }
1537 	}
1538 
1539 
1540       /* Draw ship: */
1541 
1542       if (player_alive)
1543 	{
1544 	  draw_segment(SHIP_RADIUS, 0, mkcolor(128, 128, 255),
1545 		       SHIP_RADIUS / 2, 135, mkcolor(0, 0, 192),
1546 		       x >> 4, y >> 4,
1547 		       angle);
1548 
1549 	  draw_segment(SHIP_RADIUS / 2, 135, mkcolor(0, 0, 192),
1550 		       0, 0, mkcolor(64, 64, 230),
1551 		       x >> 4, y >> 4,
1552 		       angle);
1553 
1554 	  draw_segment(0, 0, mkcolor(64, 64, 230),
1555 		       SHIP_RADIUS / 2, 225, mkcolor(0, 0, 192),
1556 		       x >> 4, y >> 4,
1557 		       angle);
1558 
1559 	  draw_segment(SHIP_RADIUS / 2, 225, mkcolor(0, 0, 192),
1560 		       SHIP_RADIUS, 0, mkcolor(128, 128, 255),
1561 		       x >> 4, y >> 4,
1562 		       angle);
1563 
1564 
1565 	  /* Draw flame: */
1566 
1567 	  if (up_pressed)
1568 	    {
1569 #ifndef EMBEDDED
1570 	      draw_segment(0, 0, mkcolor(255, 255, 255),
1571 			   (rand() % 20), 180, mkcolor(255, 0, 0),
1572 			   x >> 4, y >> 4,
1573 			   angle);
1574 #else
1575 	      i = (rand() % 128) + 128;
1576 
1577 	      draw_segment(0, 0, mkcolor(255, i, i),
1578 			   (rand() % 20), 180, mkcolor(255, i, i),
1579 			   x >> 4, y >> 4,
1580 			   angle);
1581 #endif
1582 	    }
1583 	}
1584 
1585 
1586       /* Draw bullets: */
1587 
1588       for (i = 0; i < NUM_BULLETS; i++)
1589 	{
1590 	  if (bullets[i].timer >= 0)
1591 	    {
1592 	      draw_line(bullets[i].x - (rand() % 3) - bullets[i].xm * 2,
1593 			bullets[i].y - (rand() % 3) - bullets[i].ym * 2,
1594 			mkcolor((rand() % 3) * 128,
1595 				(rand() % 3) * 128,
1596 				(rand() % 3) * 128),
1597 			bullets[i].x + (rand() % 3) - bullets[i].xm * 2,
1598 			bullets[i].y + (rand() % 3) - bullets[i].ym * 2,
1599 			mkcolor((rand() % 3) * 128,
1600 				(rand() % 3) * 128,
1601 				(rand() % 3) * 128));
1602 
1603 	      draw_line(bullets[i].x + (rand() % 3) - bullets[i].xm * 2,
1604 			bullets[i].y - (rand() % 3) - bullets[i].ym * 2,
1605 			mkcolor((rand() % 3) * 128,
1606 				(rand() % 3) * 128,
1607 				(rand() % 3) * 128),
1608 			bullets[i].x - (rand() % 3) - bullets[i].xm * 2,
1609 			bullets[i].y + (rand() % 3) - bullets[i].ym * 2,
1610 			mkcolor((rand() % 3) * 128,
1611 				(rand() % 3) * 128,
1612 				(rand() % 3) * 128));
1613 
1614 
1615 
1616 	      draw_thick_line(bullets[i].x - (rand() % 5),
1617 			      bullets[i].y - (rand() % 5),
1618 			      mkcolor((rand() % 3) * 128 + 64,
1619 				      (rand() % 3) * 128 + 64,
1620 				      (rand() % 3) * 128 + 64),
1621 			      bullets[i].x + (rand() % 5),
1622 			      bullets[i].y + (rand() % 5),
1623 			      mkcolor((rand() % 3) * 128 + 64,
1624 				      (rand() % 3) * 128 + 64,
1625 				      (rand() % 3) * 128 + 64));
1626 
1627 	      draw_thick_line(bullets[i].x + (rand() % 5),
1628 			      bullets[i].y - (rand() % 5),
1629 			      mkcolor((rand() % 3) * 128 + 64,
1630 				      (rand() % 3) * 128 + 64,
1631 				      (rand() % 3) * 128 + 64),
1632 			      bullets[i].x - (rand() % 5),
1633 			      bullets[i].y + (rand() % 5),
1634 			      mkcolor((rand() % 3) * 128 + 64,
1635 				      (rand() % 3) * 128 + 64,
1636 				      (rand() % 3) * 128 + 64));
1637 	    }
1638 	}
1639 
1640 
1641       /* Draw asteroids: */
1642 
1643       for (i = 0; i < NUM_ASTEROIDS; i++)
1644 	{
1645 	  if (asteroids[i].alive)
1646 	    {
1647 	      draw_asteroid(asteroids[i].size,
1648 			    asteroids[i].x, asteroids[i].y,
1649 			    asteroids[i].angle,
1650 			    asteroids[i].shape);
1651 	    }
1652 	}
1653 
1654 
1655       /* Draw bits: */
1656 
1657       for (i = 0; i < NUM_BITS; i++)
1658 	{
1659 	  if (bits[i].timer > 0)
1660 	    {
1661 	      draw_line(bits[i].x, bits[i].y, mkcolor(255, 255, 255),
1662 	                bits[i].x + bits[i].xm,
1663 		       	bits[i].y + bits[i].ym, mkcolor(255, 255, 255));
1664 	    }
1665 	}
1666 
1667 
1668       /* Draw score: */
1669 
1670 #ifndef EMBEDDED
1671       sprintf(str, "%.6d", score);
1672       draw_text(str, 3, 3, 14, mkcolor(255, 255, 255));
1673       draw_text(str, 4, 4, 14, mkcolor(255, 255, 255));
1674 #else
1675       sprintf(str, "%.6d", score);
1676       draw_text(str, 3, 3, 10, mkcolor(255, 255, 255));
1677       draw_text(str, 4, 4, 10, mkcolor(255, 255, 255));
1678 #endif
1679 
1680 
1681       /* Level: */
1682 
1683 #ifndef EMBEDDED
1684       sprintf(str, "%d", level);
1685       draw_text(str, (WIDTH - 14) / 2, 3, 14, mkcolor(255, 255, 255));
1686       draw_text(str, (WIDTH - 14) / 2 + 1, 4, 14, mkcolor(255, 255, 255));
1687 #else
1688       sprintf(str, "%d", level);
1689       draw_text(str, (WIDTH - 14) / 2, 3, 10, mkcolor(255, 255, 255));
1690       draw_text(str, (WIDTH - 14) / 2 + 1, 4, 10, mkcolor(255, 255, 255));
1691 #endif
1692 
1693 
1694       /* Draw lives: */
1695 
1696       for (i = 0; i < lives; i++)
1697 	{
1698 	  draw_segment(16, 0, mkcolor(255, 255, 255),
1699 		       4, 135, mkcolor(255, 255, 255),
1700 		       WIDTH - 10 - i * 10, 20,
1701 		       90);
1702 
1703 	  draw_segment(8, 135, mkcolor(255, 255, 255),
1704 		       0, 0, mkcolor(255, 255, 255),
1705 		       WIDTH - 10 - i * 10, 20,
1706 		       90);
1707 
1708 	  draw_segment(0, 0, mkcolor(255, 255, 255),
1709 		       8, 225, mkcolor(255, 255, 255),
1710 		       WIDTH - 10 - i * 10, 20,
1711 		       90);
1712 
1713 	  draw_segment(8, 225, mkcolor(255, 255, 255),
1714 		       16, 0, mkcolor(255, 255, 255),
1715 		       WIDTH - 10 - i * 10, 20,
1716 		       90);
1717 	}
1718 
1719 
1720       if (player_die_timer > 0)
1721 	{
1722 	  if (player_die_timer > 30)
1723 	    j = 30;
1724 	  else
1725 	    j = player_die_timer;
1726 
1727 	  draw_segment((16 * j) / 30, 0, mkcolor(255, 255, 255),
1728 		       (4 * j) / 30, 135, mkcolor(255, 255, 255),
1729 		       WIDTH - 10 - i * 10, 20,
1730 		       90);
1731 
1732 	  draw_segment((8 * j) / 30, 135, mkcolor(255, 255, 255),
1733 		       0, 0, mkcolor(255, 255, 255),
1734 		       WIDTH - 10 - i * 10, 20,
1735 		       90);
1736 
1737 	  draw_segment(0, 0, mkcolor(255, 255, 255),
1738 		       (8 * j) / 30, 225, mkcolor(255, 255, 255),
1739 		       WIDTH - 10 - i * 10, 20,
1740 		       90);
1741 
1742 	  draw_segment((8 * j) / 30, 225, mkcolor(255, 255, 255),
1743 		       (16 * j) / 30, 0, mkcolor(255, 255, 255),
1744 		       WIDTH - 10 - i * 10, 20,
1745 		       90);
1746 
1747 	}
1748 
1749 
1750       /* Zooming level effect: */
1751 
1752       if (text_zoom > 0)
1753 	{
1754 	  if ((counter % 2) == 0)
1755 	    text_zoom--;
1756 
1757 #ifndef EMBEDDED
1758 	  draw_text(zoom_str, (WIDTH - (strlen(zoom_str) * text_zoom)) / 2,
1759 		    (HEIGHT - text_zoom) / 2,
1760 		    text_zoom, mkcolor(text_zoom * (256 / ZOOM_START), 0, 0));
1761 #else
1762 	  draw_text(zoom_str, (WIDTH - (strlen(zoom_str) * text_zoom)) / 2,
1763 		    (HEIGHT - text_zoom) / 2,
1764 		    text_zoom, mkcolor(text_zoom * (256 / ZOOM_START), 128, 128));
1765 #endif
1766 	}
1767 
1768 
1769       /* Game over? */
1770 
1771       if (player_alive == 0 && lives == 0)
1772       {
1773 	if (player_die_timer > 14)
1774 	{
1775 	  draw_text("GAME OVER",
1776 	            (WIDTH - 9 * player_die_timer) / 2,
1777 	  	    (HEIGHT - player_die_timer) / 2,
1778 		    player_die_timer,
1779 		    mkcolor(rand() % 255,
1780 			    rand() % 255,
1781 			    rand() % 255));
1782 	}
1783 	else
1784 	{
1785 	  draw_text("GAME OVER",
1786 	            (WIDTH - 9 * 14) / 2,
1787                     (HEIGHT - 14) / 2,
1788                     14,
1789 		    mkcolor(255, 255, 255));
1790 
1791 	}
1792       }
1793 
1794 
1795       /* Go to next level? */
1796 
1797       if (num_asteroids_alive == 0)
1798 	{
1799 	  level++;
1800 
1801 	  reset_level();
1802 	}
1803 
1804 
1805       /* Flush and pause! */
1806 
1807       SDL_Flip(screen);
1808 
1809       now_time = SDL_GetTicks();
1810 
1811       if (now_time < last_time + (1000 / FPS))
1812 	{
1813 	  SDL_Delay(last_time + 1000 / FPS - now_time);
1814 	}
1815     }
1816   while (!done);
1817 
1818 
1819   /* Record, if a high score: */
1820 
1821   if (score >= high)
1822   {
1823     high = score;
1824   }
1825 
1826 
1827   /* Display mouse cursor: */
1828 
1829   if (fullscreen)
1830     SDL_ShowCursor(1);
1831 
1832 
1833   return(quit);
1834 }
1835 
1836 
finish(void)1837 void finish(void)
1838 {
1839   SDL_Quit();
1840 }
1841 
1842 
setup(int argc,char * argv[])1843 void setup(int argc, char * argv[])
1844 {
1845   int i;
1846   SDL_Surface * tmp;
1847 
1848 
1849   /* Options: */
1850 
1851   score = 0;
1852   use_sound = TRUE;
1853   fullscreen = FALSE;
1854 
1855 
1856   /* Check command-line options: */
1857 
1858   for (i = 1; i < argc; i++)
1859     {
1860       if (strcmp(argv[i], "--fullscreen") == 0 ||
1861 	  strcmp(argv[i], "-f") == 0)
1862 	{
1863 	  fullscreen = TRUE;
1864 	}
1865       else if (strcmp(argv[i], "--nosound") == 0 ||
1866 	       strcmp(argv[i], "-q") == 0)
1867 	{
1868 	  use_sound = FALSE;
1869 	}
1870       else if (strcmp(argv[i], "--help") == 0 ||
1871 	       strcmp(argv[i], "-h") == 0)
1872 	{
1873 	  show_version();
1874 
1875 	  printf("\n"
1876 		 "Programming: Bill Kendrick, New Breed Software - bill@newbreedsoftware.com\n"
1877 		 "Music:       Mike Faltiss (Hadji/Digital Music Kings) - deadchannel@hotmail.com\n"
1878 		 "\n"
1879 		 "Keyboard controls:\n"
1880 		 "  Left/Right - Rotate ship\n"
1881 		 "  Up         - Thrust engines\n"
1882 		 "  Space      - Fire weapons\n"
1883 		 "  Shift      - Respawn after death (or wait)\n"
1884 		 "  Escape     - Return to title screen\n"
1885 		 "\n"
1886 		 "Joystick controls:\n"
1887 		 "  Left/Right - Rotate ship\n"
1888 		 "  Fire-A     - Thrust engines\n"
1889 		 "  Fire-B     - Fire weapons\n"
1890 		 "\n"
1891 		 "Run with \"--usage\" for command-line options...\n"
1892 		 "Run with \"--copying\" for copying information...\n"
1893 		 "\n");
1894 
1895 	  exit(0);
1896 	}
1897       else if (strcmp(argv[i], "--version") == 0 ||
1898 	       strcmp(argv[i], "-v") == 0)
1899 	{
1900 	  show_version();
1901 	  printf("State format file version " STATE_FORMAT_VERSION "\n");
1902 	  exit(0);
1903 	}
1904       else if (strcmp(argv[i], "--copying") == 0 ||
1905 	       strcmp(argv[i], "-c") == 0)
1906 	{
1907 	  show_version();
1908 	  printf("\n"
1909 		 "This program is free software; you can redistribute it\n"
1910 		 "and/or modify it under the terms of the GNU General Public\n"
1911 		 "License as published by the Free Software Foundation;\n"
1912 		 "either version 2 of the License, or (at your option) any\n"
1913 		 "later version.\n"
1914 		 "\n"
1915 		 "This program is distributed in the hope that it will be\n"
1916 		 "useful and entertaining, but WITHOUT ANY WARRANTY; without\n"
1917 		 "even the implied warranty of MERCHANTABILITY or FITNESS\n"
1918 		 "FOR A PARTICULAR PURPOSE.  See the GNU General Public\n"
1919 		 "License for more details.\n"
1920 		 "\n");
1921 	  printf("You should have received a copy of the GNU General Public\n"
1922 		 "License along with this program; if not, write to the Free\n"
1923 		 "Software Foundation, Inc., 59 Temple Place, Suite 330,\n"
1924 		 "Boston, MA  02111-1307  USA\n"
1925 		 "\n");
1926 	  exit(0);
1927 	}
1928       else if (strcmp(argv[i], "--usage") == 0 ||
1929 	       strcmp(argv[i], "-u") == 0)
1930 	{
1931 	  show_usage(stdout, argv[0]);
1932 	  exit(0);
1933 	}
1934       else
1935 	{
1936 	  show_usage(stderr, argv[0]);
1937 	  exit(1);
1938 	}
1939     }
1940 
1941 
1942   /* Seed random number generator: */
1943 
1944   srand(SDL_GetTicks());
1945 
1946 
1947   /* Init SDL video: */
1948 
1949   if (SDL_Init(SDL_INIT_VIDEO) < 0)
1950     {
1951       fprintf(stderr,
1952               "\nError: I could not initialize video!\n"
1953               "The Simple DirectMedia error that occured was:\n"
1954               "%s\n\n", SDL_GetError());
1955       exit(1);
1956     }
1957 
1958 
1959   /* Init joysticks: */
1960 
1961 #ifdef JOY_YES
1962   use_joystick = 1;
1963 
1964   if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1965     {
1966       fprintf(stderr,
1967               "\nWarning: I could not initialize joystick.\n"
1968               "The Simple DirectMedia error that occured was:\n"
1969               "%s\n\n", SDL_GetError());
1970 
1971       use_joystick = 0;
1972     }
1973   else
1974     {
1975       /* Look for joysticks: */
1976 
1977       if (SDL_NumJoysticks() <= 0)
1978         {
1979           fprintf(stderr,
1980                   "\nWarning: No joysticks available.\n");
1981 
1982           use_joystick = 0;
1983         }
1984       else
1985 	{
1986           /* Open joystick: */
1987 
1988           js = SDL_JoystickOpen(0);
1989 
1990           if (js == NULL)
1991             {
1992               fprintf(stderr,
1993                       "\nWarning: Could not open joystick 1.\n"
1994                       "The Simple DirectMedia error that occured was:\n"
1995                       "%s\n\n", SDL_GetError());
1996 
1997               use_joystick = 0;
1998             }
1999 	  else
2000 	    {
2001               /* Check for proper stick configuration: */
2002 
2003               if (SDL_JoystickNumAxes(js) < 2)
2004                 {
2005                   fprintf(stderr,
2006                           "\nWarning: Joystick doesn't have enough axes!\n");
2007 
2008                   use_joystick = 0;
2009                 }
2010               else
2011                 {
2012                   if (SDL_JoystickNumButtons(js) < 2)
2013                     {
2014                       fprintf(stderr,
2015                               "\nWarning: Joystick doesn't have enough "
2016                               "buttons!\n");
2017 
2018                       use_joystick = 0;
2019                     }
2020                 }
2021 	    }
2022 	}
2023     }
2024 #else
2025   use_joystick = 0;
2026 #endif
2027 
2028 
2029   /* Open window: */
2030 
2031   if (fullscreen)
2032     {
2033       screen = set_vid_mode(SDL_FULLSCREEN | SDL_HWSURFACE);
2034 
2035       if (screen == NULL)
2036         {
2037           fprintf(stderr,
2038                   "\nWarning: I could not set up fullscreen video for "
2039                   "%dx%d mode.\n"
2040                   "The Simple DirectMedia error that occured was:\n"
2041                   "%s\n\n", WIDTH, HEIGHT, SDL_GetError());
2042           fullscreen = 0;
2043         }
2044     }
2045 
2046   if (!fullscreen)
2047     {
2048       screen = set_vid_mode(0);
2049 
2050       if (screen == NULL)
2051 	{
2052 	  fprintf(stderr,
2053 		  "\nError: I could not open the display.\n"
2054 		  "The Simple DirectMedia error that occured was:\n"
2055 		  "%s\n\n", SDL_GetError());
2056 	  exit(1);
2057 	}
2058     }
2059 
2060 
2061   /* Load background image: */
2062 
2063 #ifndef EMBEDDED
2064   tmp = IMG_Load(DATA_PREFIX "images/redspot.jpg");
2065 
2066   if (tmp == NULL)
2067     {
2068       fprintf(stderr,
2069 	      "\nError: I could not open the background image:\n"
2070 	      DATA_PREFIX "images/redspot.jpg\n"
2071 	      "The Simple DirectMedia error that occured was:\n"
2072 	      "%s\n\n", SDL_GetError());
2073       exit(1);
2074     }
2075 
2076   bkgd = SDL_DisplayFormat(tmp);
2077   if (bkgd == NULL)
2078     {
2079       fprintf(stderr,
2080 	      "\nError: I couldn't convert the background image"
2081 	      "to the display format!\n"
2082 	      "The Simple DirectMedia error that occured was:\n"
2083 	      "%s\n\n", SDL_GetError());
2084       exit(1);
2085     }
2086 
2087   SDL_FreeSurface(tmp);
2088 
2089 #else
2090 
2091   tmp = SDL_LoadBMP(DATA_PREFIX "images/redspot-e.bmp");
2092 
2093   if (tmp == NULL)
2094     {
2095       fprintf(stderr,
2096 	      "\nError: I could not open the background image:\n"
2097 	      DATA_PREFIX "images/redspot-e.bmp\n"
2098 	      "The Simple DirectMedia error that occured was:\n"
2099 	      "%s\n\n", SDL_GetError());
2100       exit(1);
2101     }
2102 
2103   bkgd = SDL_DisplayFormat(tmp);
2104   if (bkgd == NULL)
2105     {
2106       fprintf(stderr,
2107 	      "\nError: I couldn't convert the background image"
2108 	      "to the display format!\n"
2109 	      "The Simple DirectMedia error that occured was:\n"
2110 	      "%s\n\n", SDL_GetError());
2111       exit(1);
2112     }
2113 
2114   SDL_FreeSurface(tmp);
2115 #endif
2116 
2117 
2118 #ifndef NOSOUND
2119   /* Init sound: */
2120 
2121   if (use_sound)
2122     {
2123       if (Mix_OpenAudio(22050, AUDIO_S16, 2, 512) < 0)
2124 	{
2125 	  fprintf(stderr,
2126                   "\nWarning: I could not set up audio for 22050 Hz "
2127                   "16-bit stereo.\n"
2128                   "The Simple DirectMedia error that occured was:\n"
2129                   "%s\n\n", SDL_GetError());
2130           use_sound = FALSE;
2131 	}
2132     }
2133 
2134 
2135   /* Load sound files: */
2136 
2137   if (use_sound)
2138     {
2139       for (i = 0; i < NUM_SOUNDS; i++)
2140 	{
2141 	  sounds[i] = Mix_LoadWAV(sound_names[i]);
2142           if (sounds[i] == NULL)
2143             {
2144               fprintf(stderr,
2145                       "\nError: I could not load the sound file:\n"
2146                       "%s\n"
2147                       "The Simple DirectMedia error that occured was:\n"
2148                       "%s\n\n", sound_names[i], SDL_GetError());
2149               exit(1);
2150             }
2151 	}
2152 
2153 
2154       game_music = Mix_LoadMUS(mus_game_name);
2155       if (game_music == NULL)
2156 	{
2157 	  fprintf(stderr,
2158 		  "\nError: I could not load the music file:\n"
2159 		  "%s\n"
2160 		  "The Simple DirectMedia error that occured was:\n"
2161 		  "%s\n\n", mus_game_name, SDL_GetError());
2162 	  exit(1);
2163 	}
2164     }
2165 #endif
2166 
2167 
2168   seticon();
2169   SDL_WM_SetCaption("Vectoroids", "Vectoroids");
2170 }
2171 
2172 
2173 /* Set the window's icon: */
2174 
seticon(void)2175 void seticon(void)
2176 {
2177 #ifndef EMBEDDED
2178   int masklen;
2179   Uint8 * mask;
2180   SDL_Surface * icon;
2181 
2182 
2183   /* Load icon into a surface: */
2184 
2185   icon = IMG_Load(DATA_PREFIX "images/icon.png");
2186   if (icon == NULL)
2187     {
2188       fprintf(stderr,
2189               "\nError: I could not load the icon image: %s\n"
2190               "The Simple DirectMedia error that occured was:\n"
2191               "%s\n\n", DATA_PREFIX "images/icon.png", SDL_GetError());
2192       exit(1);
2193     }
2194 
2195 
2196   /* Create mask: */
2197 
2198   masklen = (((icon -> w) + 7) / 8) * (icon -> h);
2199   mask = malloc(masklen * sizeof(Uint8));
2200   memset(mask, 0xFF, masklen);
2201 
2202 
2203   /* Set icon: */
2204 
2205   SDL_WM_SetIcon(icon, mask);
2206 
2207 
2208   /* Free icon surface & mask: */
2209 
2210   free(mask);
2211   SDL_FreeSurface(icon);
2212 #endif
2213 }
2214 
2215 
2216 /* Fast approximate-integer, table-based cosine! Whee! */
2217 
fast_cos(int angle)2218 int fast_cos(int angle)
2219 {
2220   angle = (angle % 45);
2221 
2222   if (angle < 12)
2223     return(trig[angle]);
2224   else if (angle < 23)
2225     return(-trig[10 - (angle - 12)]);
2226   else if (angle < 34)
2227     return(-trig[angle - 22]);
2228   else
2229     return(trig[45 - angle]);
2230 }
2231 
2232 
2233 /* Sine based on fast cosine... */
2234 
fast_sin(int angle)2235 int fast_sin(int angle)
2236 {
2237   return(- fast_cos((angle + 11) % 45));
2238 }
2239 
2240 
2241 /* Draw a line: */
2242 
draw_line(int x1,int y1,color_type c1,int x2,int y2,color_type c2)2243 void draw_line(int x1, int y1, color_type c1,
2244 	       int x2, int y2, color_type c2)
2245 {
2246   sdl_drawline(x1, y1, c1, x2, y2, c2);
2247 
2248   if (x1 < 0 || x2 < 0)
2249     {
2250       sdl_drawline(x1 + WIDTH, y1, c1, x2 + WIDTH, y2, c2);
2251     }
2252   else if (x1 >= WIDTH || x2 >= WIDTH)
2253     {
2254       sdl_drawline(x1 - WIDTH, y1, c1, x2 - WIDTH, y2, c2);
2255     }
2256 
2257   if (y1 < 0 || y2 < 0)
2258     {
2259       sdl_drawline(x1, y1 + HEIGHT, c1, x2, y2 + HEIGHT, c2);
2260     }
2261   else if (y1 >= HEIGHT || y2 >= HEIGHT)
2262     {
2263       sdl_drawline(x1, y1 - HEIGHT, c1, x2, y2 - HEIGHT, c2);
2264     }
2265 }
2266 
2267 
2268 /* Create a color_type struct out of RGB values: */
2269 
mkcolor(int r,int g,int b)2270 color_type mkcolor(int r, int g, int b)
2271 {
2272   color_type c;
2273 
2274   if (r > 255)
2275     r = 255;
2276   if (g > 255)
2277     g = 255;
2278   if (b > 255)
2279     b = 255;
2280 
2281   c.r = (Uint8) r;
2282   c.g = (Uint8) g;
2283   c.b = (Uint8) b;
2284 
2285   return c;
2286 }
2287 
2288 
2289 /* Draw a line on an SDL surface: */
2290 
sdl_drawline(int x1,int y1,color_type c1,int x2,int y2,color_type c2)2291 void sdl_drawline(int x1, int y1, color_type c1,
2292 		  int x2, int y2, color_type c2)
2293 {
2294   int dx, dy;
2295 #ifndef EMBEDDED
2296   float cr, cg, cb, rd, gd, bd;
2297 #endif
2298   float m, b;
2299 
2300 
2301   if (clip(&x1, &y1, &x2, &y2))
2302     {
2303       dx = x2 - x1;
2304       dy = y2 - y1;
2305 
2306       if (dx != 0)
2307         {
2308           m = ((float) dy) / ((float) dx);
2309           b = y1 - m * x1;
2310 
2311           if (x2 >= x1)
2312             dx = 1;
2313           else
2314             dx = -1;
2315 
2316 #ifndef EMBEDDED
2317           cr = c1.r;
2318           cg = c1.g;
2319           cb = c1.b;
2320 
2321           rd = (float) (c2.r - c1.r) / (float) (x2 - x1) * dx;
2322           gd = (float) (c2.g - c1.g) / (float) (x2 - x1) * dx;
2323           bd = (float) (c2.b - c1.b) / (float) (x2 - x1) * dx;
2324 #endif
2325 
2326           while (x1 != x2)
2327             {
2328               y1 = m * x1 + b;
2329               y2 = m * (x1 + dx) + b;
2330 
2331 #ifndef EMBEDDED
2332               drawvertline(x1, y1, mkcolor(cr, cg, cb),
2333                            y2, mkcolor(cr + rd, cg + gd, cb + bd));
2334 #else
2335               drawvertline(x1, y1, mkcolor(c1.r, c1.g, c1.b),
2336                            y2, mkcolor(c1.r, c1.g, c1.b));
2337 #endif
2338 
2339               x1 = x1 + dx;
2340 
2341 
2342 #ifndef EMBEDDED
2343               cr = cr + rd;
2344               cg = cg + gd;
2345               cb = cb + bd;
2346 #endif
2347             }
2348         }
2349       else
2350         drawvertline(x1, y1, c1, y2, c2);
2351     }
2352 }
2353 
2354 
2355 /* Clip lines to window: */
2356 
clip(int * x1,int * y1,int * x2,int * y2)2357 int clip(int * x1, int * y1, int * x2, int * y2)
2358 {
2359 #ifndef EMBEDDED
2360 
2361   float fx1, fx2, fy1, fy2, tmp;
2362   float m;
2363   unsigned char code1, code2;
2364   int done, draw, swapped;
2365   unsigned char ctmp;
2366   fx1 = (float) *x1;
2367   fy1 = (float) *y1;
2368   fx2 = (float) *x2;
2369   fy2 = (float) *y2;
2370 
2371 
2372   done = FALSE;
2373   draw = FALSE;
2374   m = 0;
2375   swapped = FALSE;
2376 
2377 
2378   while (!done)
2379     {
2380       code1 = encode(fx1, fy1);
2381       code2 = encode(fx2, fy2);
2382 
2383       if (!(code1 | code2))
2384         {
2385           done = TRUE;
2386           draw = TRUE;
2387         }
2388       else if (code1 & code2)
2389         {
2390           done = TRUE;
2391         }
2392       else
2393         {
2394           if (!code1)
2395             {
2396               swapped = TRUE;
2397               tmp = fx1;
2398               fx1 = fx2;
2399               fx2 = tmp;
2400 
2401               tmp = fy1;
2402               fy1 = fy2;
2403               fy2 = tmp;
2404 
2405               ctmp = code1;
2406               code1 = code2;
2407               code2 = ctmp;
2408             }
2409 
2410 
2411           if (fx2 != fx1)
2412             m = (fy2 - fy1) / (fx2 - fx1);
2413           else
2414             m = 1;
2415 
2416           if (code1 & LEFT_EDGE)
2417             {
2418               fy1 += ((0 - (fx1)) * m);
2419               fx1 = 0;
2420             }
2421           else if (code1 & RIGHT_EDGE)
2422             {
2423               fy1 += (((WIDTH - 1) - (fx1)) * m);
2424               fx1 = (WIDTH - 1);
2425             }
2426           else if (code1 & TOP_EDGE)
2427             {
2428               if (fx2 != fx1)
2429                 fx1 += ((0 - (fy1)) / m);
2430               fy1 = 0;
2431             }
2432           else if (code1 & BOTTOM_EDGE)
2433             {
2434               if (fx2 != fx1)
2435                 fx1 += (((HEIGHT - 1) - (fy1)) / m);
2436               fy1 = (HEIGHT - 1);
2437             }
2438         }
2439     }
2440 
2441 
2442   if (swapped)
2443     {
2444       tmp = fx1;
2445       fx1 = fx2;
2446       fx2 = tmp;
2447 
2448       tmp = fy1;
2449       fy1 = fy2;
2450       fy2 = tmp;
2451     }
2452 
2453 
2454   *x1 = (int) fx1;
2455   *y1 = (int) fy1;
2456   *x2 = (int) fx2;
2457   *y2 = (int) fy2;
2458 
2459   return(draw);
2460 #else
2461 
2462   if (*x1 < 0 || *x1 >= WIDTH ||
2463       *y1 < 0 || *y1 >= HEIGHT ||
2464       *x2 < 0 || *x2 >= WIDTH ||
2465       *y2 < 0 || *y2 >= HEIGHT)
2466     return FALSE;
2467   else
2468     return TRUE;
2469 
2470 
2471 #endif
2472 }
2473 
2474 
2475 /* Where does this line clip? */
2476 
encode(float x,float y)2477 unsigned char encode(float x, float y)
2478 {
2479   unsigned char code;
2480 
2481   code = 0x00;
2482 
2483   if (x < 0.0)
2484     code = code | LEFT_EDGE;
2485   else if (x >= (float) WIDTH)
2486     code = code | RIGHT_EDGE;
2487 
2488   if (y < 0.0)
2489     code = code | TOP_EDGE;
2490   else if (y >= (float) HEIGHT)
2491     code = code | BOTTOM_EDGE;
2492 
2493   return code;
2494 }
2495 
2496 
2497 /* Draw a verticle line: */
2498 
drawvertline(int x,int y1,color_type c1,int y2,color_type c2)2499 void drawvertline(int x, int y1, color_type c1,
2500                   int y2, color_type c2)
2501 {
2502   int tmp, dy;
2503 #ifndef EMBEDDED
2504   float cr, cg, cb, rd, gd, bd;
2505 #else
2506   int cr, cg, cb;
2507 #endif
2508 
2509   if (y1 > y2)
2510     {
2511       tmp = y1;
2512       y1 = y2;
2513       y2 = tmp;
2514 
2515 #ifndef EMBEDDED
2516       tmp = c1.r;
2517       c1.r = c2.r;
2518       c2.r = tmp;
2519 
2520       tmp = c1.g;
2521       c1.g = c2.g;
2522       c2.g = tmp;
2523 
2524       tmp = c1.b;
2525       c1.b = c2.b;
2526       c2.b = tmp;
2527 #endif
2528     }
2529 
2530   cr = c1.r;
2531   cg = c1.g;
2532   cb = c1.b;
2533 
2534 #ifndef EMBEDDED
2535   if (y1 != y2)
2536     {
2537       rd = (float) (c2.r - c1.r) / (float) (y2 - y1);
2538       gd = (float) (c2.g - c1.g) / (float) (y2 - y1);
2539       bd = (float) (c2.b - c1.b) / (float) (y2 - y1);
2540     }
2541   else
2542     {
2543       rd = 0;
2544       gd = 0;
2545       bd = 0;
2546     }
2547 #endif
2548 
2549   for (dy = y1; dy <= y2; dy++)
2550     {
2551       putpixel(screen, x + 1, dy + 1, SDL_MapRGB(screen->format, 0, 0, 0));
2552 
2553       putpixel(screen, x, dy, SDL_MapRGB(screen->format,
2554                                          (Uint8) cr,
2555                                          (Uint8) cg,
2556                                          (Uint8) cb));
2557 
2558 #ifndef EMBEDDED
2559       cr = cr + rd;
2560       cg = cg + gd;
2561       cb = cb + bd;
2562 #endif
2563     }
2564 }
2565 
2566 
2567 /* Draw a single pixel into the surface: */
2568 
putpixel(SDL_Surface * surface,int x,int y,Uint32 pixel)2569 void putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel)
2570 {
2571   int bpp;
2572   Uint8 * p;
2573 
2574 
2575   /* Assuming the X/Y values are within the bounds of this surface... */
2576 
2577   if (x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT)
2578     {
2579       /* Determine bytes-per-pixel for the surface in question: */
2580 
2581       bpp = surface->format->BytesPerPixel;
2582 
2583 
2584       /* Set a pointer to the exact location in memory of the pixel
2585          in question: */
2586 
2587       p = (((Uint8 *) surface->pixels) +       /* Start at beginning of RAM */
2588 	   (y * surface->pitch) +  /* Go down Y lines */
2589 	   (x * bpp));             /* Go in X pixels */
2590 
2591 
2592       /* Set the (correctly-sized) piece of data in the surface's RAM
2593          to the pixel value sent in: */
2594 
2595       if (bpp == 1)
2596         *p = pixel;
2597       else if (bpp == 2)
2598         *(Uint16 *)p = pixel;
2599       else if (bpp == 3)
2600         {
2601           if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
2602             {
2603               p[0] = (pixel >> 16) & 0xff;
2604               p[1] = (pixel >> 8) & 0xff;
2605               p[2] = pixel & 0xff;
2606             }
2607           else
2608             {
2609               p[0] = pixel & 0xff;
2610               p[1] = (pixel >> 8) & 0xff;
2611               p[2] = (pixel >> 16) & 0xff;
2612             }
2613         }
2614       else if (bpp == 4)
2615         {
2616           *(Uint32 *)p = pixel;
2617         }
2618     }
2619 }
2620 
2621 
2622 
2623 /* Draw a line segment, rotated around a center point: */
2624 
draw_segment(int r1,int a1,color_type c1,int r2,int a2,color_type c2,int cx,int cy,int a)2625 void draw_segment(int r1, int a1,
2626 		  color_type c1,
2627 		  int r2, int a2,
2628 		  color_type c2,
2629 		  int cx, int cy, int a)
2630 {
2631   draw_line(((fast_cos((a1 + a) >> 3) * r1) >> 10) + cx,
2632 	    cy - ((fast_sin((a1 + a) >> 3) * r1) >> 10),
2633 	    c1,
2634 	    ((fast_cos((a2 + a) >> 3) * r2) >> 10) + cx,
2635 	    cy - ((fast_sin((a2 + a) >> 3) * r2) >> 10),
2636 	    c2);
2637 }
2638 
2639 
2640 /* Add a bullet: */
2641 
add_bullet(int x,int y,int a,int xm,int ym)2642 void add_bullet(int x, int y, int a, int xm, int ym)
2643 {
2644   int i, found;
2645 
2646   found = -1;
2647 
2648   for (i = 0; i < NUM_BULLETS && found == -1; i++)
2649     {
2650       if (bullets[i].timer <= 0)
2651 	found = i;
2652     }
2653 
2654   if (found != -1)
2655     {
2656 #ifndef EMBEDDED
2657       bullets[found].timer = 50;
2658 #else
2659       bullets[found].timer = 30;
2660 #endif
2661 
2662       bullets[found].x = x;
2663       bullets[found].y = y;
2664 
2665       bullets[found].xm = ((fast_cos(a >> 3) * 5) >> 10) + (xm >> 4);
2666       bullets[found].ym = - ((fast_sin(a >> 3) * 5) >> 10) + (ym >> 4);
2667 
2668 
2669       playsound(SND_BULLET);
2670     }
2671 }
2672 
2673 
2674 /* Add an asteroid: */
2675 
add_asteroid(int x,int y,int xm,int ym,int size)2676 void add_asteroid(int x, int y, int xm, int ym, int size)
2677 {
2678   int i, found;
2679 
2680 
2681   /* Find a slot: */
2682 
2683   found = -1;
2684 
2685   for (i = 0; i < NUM_ASTEROIDS && found == -1; i++)
2686     {
2687       if (asteroids[i].alive == 0)
2688 	found = i;
2689     }
2690 
2691 
2692   /* Hack: No asteroids should be stationary! */
2693 
2694   while (xm == 0)
2695     {
2696       xm = (rand() % 3) - 1;
2697     }
2698 
2699 
2700   if (found != -1)
2701     {
2702       asteroids[found].alive = 1;
2703 
2704       asteroids[found].x = x;
2705       asteroids[found].y = y;
2706       asteroids[found].xm = xm;
2707       asteroids[found].ym = ym;
2708 
2709       asteroids[found].angle = (rand() % 360);
2710       asteroids[found].angle_m = (rand() % 6) - 3;
2711 
2712       asteroids[found].size = size;
2713 
2714       for (i = 0; i < AST_SIDES; i++)
2715 	{
2716 	  asteroids[found].shape[i].radius = (rand() % 3);
2717 	  asteroids[found].shape[i].angle = i * 60 + (rand() % 40);
2718 	}
2719     }
2720 }
2721 
2722 
2723 /* Add a bit: */
2724 
add_bit(int x,int y,int xm,int ym)2725 void add_bit(int x, int y, int xm, int ym)
2726 {
2727   int i, found;
2728 
2729   found = -1;
2730 
2731   for (i = 0; i < NUM_BITS && found == -1; i++)
2732     {
2733       if (bits[i].timer <= 0)
2734 	found = i;
2735     }
2736 
2737 
2738   if (found != -1)
2739     {
2740       bits[found].timer = 16;
2741 
2742       bits[found].x = x;
2743       bits[found].y = y;
2744       bits[found].xm = xm;
2745       bits[found].ym = ym;
2746     }
2747 }
2748 
2749 
2750 /* Draw an asteroid: */
2751 
draw_asteroid(int size,int x,int y,int angle,shape_type * shape)2752 void draw_asteroid(int size, int x, int y, int angle, shape_type * shape)
2753 {
2754   int i, b1, b2;
2755   int div;
2756 
2757 #ifndef EMBEDDED
2758   div = 240;
2759 #else
2760   div = 120;
2761 #endif
2762 
2763   for (i = 0; i < AST_SIDES - 1; i++)
2764     {
2765       b1 = (((shape[i].angle + angle) % 180) * 255) / div;
2766       b2 = (((shape[i + 1].angle + angle) % 180) * 255) / div;
2767 
2768       draw_segment((size * (AST_RADIUS - shape[i].radius)),
2769 		   shape[i].angle, mkcolor(b1, b1, b1),
2770 		   (size * (AST_RADIUS - shape[i + 1].radius)),
2771 		   shape[i + 1].angle, mkcolor(b2, b2, b2),
2772 		   x, y,
2773 		   angle);
2774     }
2775 
2776   b1 = (((shape[AST_SIDES - 1].angle + angle) % 180) * 255) / div;
2777   b2 = (((shape[0].angle + angle) % 180) * 255) / div;
2778 
2779   draw_segment((size * (AST_RADIUS - shape[AST_SIDES - 1].radius)),
2780 	       shape[AST_SIDES - 1].angle, mkcolor(b1, b1, b1),
2781 	       (size * (AST_RADIUS - shape[0].radius)),
2782 	       shape[0].angle, mkcolor(b2, b2, b2),
2783 	       x, y,
2784 	       angle);
2785 }
2786 
2787 
2788 /* Queue a sound! */
2789 
playsound(int snd)2790 void playsound(int snd)
2791 {
2792   int which, i;
2793 
2794 #ifndef NOSOUND
2795   if (use_sound)
2796     {
2797 #ifdef EMBEDDED
2798       which = -1;
2799 #else
2800       which = (rand() % 3) + CHAN_THRUST;
2801       for (i = CHAN_THRUST; i < 4; i++)
2802 	{
2803 	  if (!Mix_Playing(i))
2804 	    which = i;
2805 	}
2806 #endif
2807 
2808       Mix_PlayChannel(which, sounds[snd], 0);
2809     }
2810 #endif
2811 }
2812 
2813 
2814 /* Break an asteroid and add an explosion: */
2815 
hurt_asteroid(int j,int xm,int ym,int exp_size)2816 void hurt_asteroid(int j, int xm, int ym, int exp_size)
2817 {
2818   int k;
2819 
2820   add_score(100 / (asteroids[j].size + 1));
2821 
2822   if (asteroids[j].size > 1)
2823     {
2824       /* Break the rock into two smaller ones! */
2825 
2826       add_asteroid(asteroids[j].x,
2827 		   asteroids[j].y,
2828 		   ((asteroids[j].xm + xm) / 2),
2829 		   (asteroids[j].ym + ym),
2830 		   asteroids[j].size - 1);
2831 
2832       add_asteroid(asteroids[j].x,
2833 		   asteroids[j].y,
2834 		   (asteroids[j].xm + xm),
2835 		   ((asteroids[j].ym + ym) / 2),
2836 		   asteroids[j].size - 1);
2837     }
2838 
2839 
2840   /* Make the original go away: */
2841 
2842   asteroids[j].alive = 0;
2843 
2844 
2845   /* Add explosion: */
2846 
2847   playsound(SND_AST1 + (asteroids[j].size) - 1);
2848 
2849   for (k = 0; k < exp_size; k++)
2850     {
2851       add_bit((asteroids[j].x -
2852 	       (asteroids[j].size * AST_RADIUS) +
2853 	       (rand() % (AST_RADIUS * 2))),
2854 	      (asteroids[j].y -
2855 	       (asteroids[j].size * AST_RADIUS) +
2856 	       (rand() % (AST_RADIUS * 2))),
2857 	      ((rand() % (asteroids[j].size * 3)) -
2858 	       (asteroids[j].size) +
2859 	       ((xm + asteroids[j].xm) / 3)),
2860 	      ((rand() % (asteroids[j].size * 3)) -
2861 	       (asteroids[j].size) +
2862 	       ((ym + asteroids[j].ym) / 3)));
2863     }
2864 }
2865 
2866 
2867 /* Increment score: */
2868 
add_score(int amount)2869 void add_score(int amount)
2870 {
2871   /* See if they deserve a new life: */
2872 
2873   if (score / ONEUP_SCORE < (score + amount) / ONEUP_SCORE)
2874   {
2875     lives++;
2876     strcpy(zoom_str, "EXTRA LIFE");
2877     text_zoom = ZOOM_START;
2878     playsound(SND_EXTRALIFE);
2879   }
2880 
2881 
2882 
2883   /* Add to score: */
2884 
2885   score = score + amount;
2886 }
2887 
2888 
2889 /* Draw a character: */
2890 
draw_char(char c,int x,int y,int r,color_type cl)2891 void draw_char(char c, int x, int y, int r, color_type cl)
2892 {
2893   int i, v;
2894 
2895   /* Which vector is this character? */
2896 
2897   v = -1;
2898   if (c >= '0' && c <= '9')
2899     v = (c - '0');
2900   else if (c >= 'A' && c <= 'Z')
2901     v = (c - 'A') + 10;
2902 
2903 
2904   if (v != -1)
2905     {
2906       for (i = 0; i < 5; i++)
2907 	{
2908 	  if (char_vectors[v][i][0] != -1)
2909 	    {
2910 	      draw_line(x + (char_vectors[v][i][0] * r),
2911 			y + (char_vectors[v][i][1] * r),
2912 			cl,
2913 			x + (char_vectors[v][i][2] * r),
2914 			y + (char_vectors[v][i][3] * r),
2915 			cl);
2916 	    }
2917 	}
2918     }
2919 }
2920 
2921 
draw_text(char * str,int x,int y,int s,color_type c)2922 void draw_text(char * str, int x, int y, int s, color_type c)
2923 {
2924   int i;
2925 
2926   for (i = 0; i < strlen(str); i++)
2927     draw_char(str[i], i * (s + 3) + x, y, s, c);
2928 }
2929 
2930 
draw_thick_line(int x1,int y1,color_type c1,int x2,int y2,color_type c2)2931 void draw_thick_line(int x1, int y1, color_type c1,
2932 		     int x2, int y2, color_type c2)
2933 {
2934   draw_line(x1, y1, c1, x2, y2, c2);
2935   draw_line(x1 + 1, y1 + 1, c1, x2 + 1, y2 + 1, c2);
2936 }
2937 
2938 
reset_level(void)2939 void reset_level(void)
2940 {
2941   int i;
2942 
2943 
2944   for (i = 0; i < NUM_BULLETS; i++)
2945     bullets[i].timer = 0;
2946 
2947   for (i = 0; i < NUM_ASTEROIDS; i++)
2948     asteroids[i].alive = 0;
2949 
2950   for (i = 0; i < NUM_BITS; i++)
2951     bits[i].timer = 0;
2952 
2953   for (i = 0; i < (level + 1) && i < 10; i++)
2954     {
2955 #ifndef EMBEDDED
2956       add_asteroid(/* x */ (rand() % 40) + ((WIDTH - 40) * (rand() % 2)),
2957 		   /* y */ (rand() % HEIGHT),
2958 		   /* xm */ (rand() % 9) - 4,
2959 		   /* ym */ ((rand() % 9) - 4) * 4,
2960 		   /* size */ (rand() % 3) + 2);
2961 #else
2962       add_asteroid(/* x */ (rand() % WIDTH),
2963 		   /* y */ (rand() % 40) + ((HEIGHT - 40) * (rand() % 2)),
2964 		   /* xm */ ((rand() % 9) - 4) * 4,
2965 		   /* ym */ (rand() % 9) - 4,
2966 		   /* size */ (rand() % 3) + 2);
2967 #endif
2968     }
2969 
2970 
2971   sprintf(zoom_str, "LEVEL %d", level);
2972 
2973   text_zoom = ZOOM_START;
2974 }
2975 
2976 
2977 /* Show program version: */
2978 
show_version(void)2979 void show_version(void)
2980 {
2981   printf("Vectoroids - Version " VER_VERSION " (" VER_DATE ")\n");
2982 }
2983 
2984 
2985 /* Show usage display: */
2986 
show_usage(FILE * f,char * prg)2987 void show_usage(FILE * f, char * prg)
2988 {
2989   fprintf(f, "Usage: %s {--help | --usage | --version | --copying }\n"
2990              "       %s [--fullscreen] [--nosound]\n\n", prg, prg);
2991 }
2992 
2993 
2994 /* Set video mode: */
2995 /* Contributed to "Defendguin" by Mattias Engdegard <f91-men@nada.kth.se> */
2996 
set_vid_mode(unsigned flags)2997 SDL_Surface * set_vid_mode(unsigned flags)
2998 {
2999   /* Prefer 16bpp, but also prefer native modes to emulated 16bpp. */
3000 
3001   int depth;
3002 
3003   depth = SDL_VideoModeOK(WIDTH, HEIGHT, 16, flags);
3004   return depth ? SDL_SetVideoMode(WIDTH, HEIGHT, depth, flags) : NULL;
3005 }
3006 
3007 
3008 /* Draw text, centered horizontally: */
3009 
draw_centered_text(char * str,int y,int s,color_type c)3010 void draw_centered_text(char * str, int y, int s, color_type c)
3011 {
3012   draw_text(str, (WIDTH - strlen(str) * (s + 3)) / 2, y, s, c);
3013 }
3014 
3015