JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Refactored Makefile
[vor.git] / main.c
1 /* Variations on RockDodger
2  * Space Rocks copyright (C) 2001 Paul Holt <pad@pcholt.com>
3  *
4  * Project fork 2004, Jason Woofenden and Joshua Grams.
5  * (a whole bunch of modifications and project rename)
6
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; either version 2 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21
22 #ifdef DEBUG
23 #include "debug.h"
24 #endif
25
26 #include "config.h"
27 #include "file.h"
28 #include "globals.h"
29 #include "rocks.h"
30 #include "score.h"
31 #include "shape.h"
32 #include "sound.h"
33
34 #include <argp.h>
35 #include <math.h>
36 #include <SDL/SDL.h>
37 #include <SDL/SDL_image.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #include "SFont.h"
44
45 // ************************************* VARS
46 // SDL_Surface global variables
47 SDL_Surface 
48         *surf_screen,   // Screen
49         *surf_b_variations, // "variations" banner
50         *surf_b_on, // "on" banner
51         *surf_b_rockdodger, // "rockdodger" banner
52         *surf_b_game,   // Title element "game"
53         *surf_b_over,   // Title element "over"
54         *surf_ship,             // Spaceship element
55         *surf_life,     // Indicator of number of ships remaining
56         *surf_rock[NROCKS],     // THE ROCKS
57         *surf_font_big; // The big font
58         
59
60 SFont_Font *g_font;
61
62 // Structure global variables
63 struct enginedots edot[MAXENGINEDOTS], *dotptr = edot;
64 struct bangdots bdot[MAXBANGDOTS], *bdotptr = bdot;
65 struct spacedot sdot[MAXSPACEDOTS];
66
67 // Other global variables
68 char topline[1024];
69 char *initerror = "";
70
71 // Command-line argument parsing
72 int opt_fullscreen;
73 int opt_sound;
74 int opt_music;
75 float opt_gamespeed;
76 int opt_tail_engine;
77 int opt_friction;
78
79 const char *argp_program_version = "Variations on Rockdodger " VERSION;
80 const char *argp_program_bug_address = "<josh@qualdan.com>";
81 static char doc[] = "VoR: Dodge the rocks until you die.";
82 static struct argp_option opts[] = {
83         {0, 0, 0, 0, "Basic Options:", 0},
84         {"full-screen", 'f', 0, 0, "", 0},
85         {"music", 'm', 0, 0, "Enable music", 0},
86         {"silent", 's', 0, 0, "Turn off explosion sounds", 0},
87         {0, 0, 0, 0, "Gameplay Options:", 1},
88         {"game-speed", 'g', "N%", 0, "Game speed [50-100%]", 1},
89         {"engine", 'e', 0, 0, "Display large tail plume", 1},
90         {"old-physics", 'p', 0, 0, "Original physics (i.e. friction).", 1},
91         {0}
92 };
93 error_t parse_opt(int, char*, struct argp_state *);
94 static struct argp argp = { opts, &parse_opt, 0, doc };
95
96
97 struct shape shipshape;
98 float shipx = XSIZE/2, shipy = YSIZE/2; // X position, 0..XSIZE
99 float shipdx = 7.5, shipdy = 0.0;       // Change in X position per tick.
100 float screendx = 7.5, screendy = 0.0;
101 float xscroll, yscroll;
102 float gamerate;  // this controls the speed of everything that moves.
103
104 float bangx, bangy, bangdx, bangdy;
105
106 int nships,score,ticks_since_last,last_ticks;
107 int gameover;
108 int maneuver = 0;
109
110 float fadetimer = 0, faderate;
111
112 int pausedown = 0, paused = 0;
113
114 // bangdot start (bd1) and end (bd2) position:
115 int bd1 = 0, bd2 = 0;
116
117 enum states {
118         TITLE_PAGE,
119         GAMEPLAY,
120         DEAD_PAUSE,
121         GAME_OVER,
122         HIGH_SCORE_ENTRY,
123         HIGH_SCORE_DISPLAY
124 };
125 enum states state = TITLE_PAGE;
126 float state_timeout = 600.0;
127
128 #define NSEQUENCE 2
129 char *sequence[] = {
130         "Press SPACE to start",
131         "http://herkamire.com/jason/vor"
132 };
133
134 int bangdotlife, nbangdots;
135 Uint16 heatcolor[W*3];
136
137 char *data_dir;
138 extern char *optarg;
139 extern int optind, opterr, optopt;
140
141 // ************************************* FUNCS
142
143 void
144 init_opts(void)
145 {
146         opt_fullscreen = 0;
147         opt_sound = 1;
148         opt_music = 0;
149         opt_gamespeed = 1.00; // Run game at full speed.
150         // These switch back to the old gameplay and are off by default.
151         opt_tail_engine = 0;
152         opt_friction = 0;
153 }
154
155 error_t
156 parse_opt(int key, char *arg, struct argp_state *state)
157 {
158         int i;
159
160         switch(key) {
161                 case 'f': opt_fullscreen = 1; break;
162                 case 'm': opt_music = 1; break;
163                 case 's': opt_sound = 0; opt_music = 0; break;
164                 case 'g': sscanf(arg, "%d%%", &i);
165                                   if(i < 50) i = 50; else if(i > 100) i = 100;
166                                   opt_gamespeed = (float)i / 100;
167                                   break;
168                 case 'e': opt_tail_engine = 1; break;
169                 case 'p': opt_friction = 1; break;
170                 default: break;
171         }
172         return 0;
173 }
174
175 float
176 rnd() {
177         return (float)random()/(float)RAND_MAX;
178 }
179
180 void
181 init_engine_dots() {
182         int i;
183         for(i = 0; i<MAXENGINEDOTS; i++) {
184                 edot[i].active = 0;
185         }
186 }
187
188 void
189 init_space_dots() {
190         int i,b;
191         for(i = 0; i<MAXSPACEDOTS; i++) {
192                 sdot[i].x = rnd()*(XSIZE-5);
193                 sdot[i].y = rnd()*(YSIZE-5);
194                 sdot[i].z = MAXDUSTDEPTH*sqrt(rnd());
195                 b = (MAXDUSTDEPTH - sdot[i].z) * 255.0 / MAXDUSTDEPTH;
196                 sdot[i].color = SDL_MapRGB(surf_screen->format, b, b, b);
197         }
198 }
199
200 void
201 make_bang_dots(int xbang, int ybang, int dx, int dy, SDL_Surface *s, int power) {
202
203         // TODO - stop generating dots after a certain amount of time has passed, to cope with slower CPUs.
204         // TODO - generate and display dots in a circular buffer
205
206         int x,y,endcount;
207         Uint16 *rawpixel,c;
208         double theta,r;
209         int begin_generate;
210
211         begin_generate = SDL_GetTicks();
212
213         SDL_LockSurface(s);
214         rawpixel = (Uint16 *) s->pixels;
215
216         //for(n = 0; n <= power/2; n++) {
217
218         endcount = 0;
219         while (endcount<3) {
220                 for(x = 0; x<s->w; x++) {
221                         for(y = 0; y<s->h; y++) {
222                                 c = rawpixel[s->pitch/2*y + x];
223                                 if(c && c != s->format->colorkey) {
224
225                                         theta = rnd()*M_PI*2;
226
227                                         r = 1-(rnd()*rnd());
228
229                                         bdot[bd2].dx = (power/50.0)*45.0*cos(theta)*r + dx;
230                                         bdot[bd2].dy = (power/50.0)*45.0*sin(theta)*r + dy;
231                                         bdot[bd2].x = x + xbang;
232                                         bdot[bd2].y = y + ybang;
233
234                                         // Replace the last few bang dots with the pixels from the exploding object
235                                         bdot[bd2].c = (endcount>0)?c:0;
236                                         bdot[bd2].life = 100;
237                                         bdot[bd2].decay = rnd()*3 + 1;
238                                         bdot[bd2].active = 1;
239
240                                         bd2++;
241                                         bd2 %= MAXBANGDOTS;
242
243                                         // If the circular buffer is filled, who cares? They've had their chance.
244                                         //if(bd2 == bd1-1) goto exitloop;
245
246                                 }
247                         }
248                 }
249
250                 if(SDL_GetTicks() - begin_generate > 7) endcount++;
251         }
252
253         SDL_UnlockSurface(s);
254
255 }
256
257 void
258 draw_bang_dots(SDL_Surface *s) {
259         int i;
260         int first_i, last_i;
261         Uint16 *rawpixel;
262         rawpixel = (Uint16 *) s->pixels;
263
264         first_i = -1;
265         last_i = 0;
266
267         for(i = bd1; (bd1 <= bd2)?(i<bd2):(i >= bd1 && i < bd2); last_i = ++i) {
268
269                 i %= MAXBANGDOTS;
270
271                 if(bdot[i].x <= 0 || bdot[i].x >= XSIZE || bdot[i].y <= 0 || bdot[i].y >= YSIZE) {
272                         // If the dot has drifted outside the perimeter, kill it
273                         bdot[i].active = 0;
274                 }
275
276                 if(bdot[i].active) {
277                         if(first_i < 0)
278                         first_i = i;
279                         //last_i = i + 1;
280                         rawpixel[(int)(s->pitch/2*(int)(bdot[i].y)) + (int)(bdot[i].x)] = bdot[i].c ? bdot[i].c : heatcolor[(int)(bdot[i].life*3)];
281                         bdot[i].life -= bdot[i].decay;
282                         bdot[i].x += bdot[i].dx*gamerate - xscroll;
283                         bdot[i].y += bdot[i].dy*gamerate - yscroll;
284
285                         if(bdot[i].life<0)
286                         bdot[i].active = 0;
287                 }
288         }
289
290         if(first_i >= 0) {
291                 bd1 = first_i;
292                 bd2 = last_i;
293         }
294         else {
295                 bd1 = 0;
296                 bd2 = 0;
297         }
298
299 }
300
301
302 void
303 draw_space_dots(SDL_Surface *s) {
304         int i;
305         Uint16 *rawpixel;
306         rawpixel = (Uint16 *) s->pixels;
307
308         for(i = 0; i<MAXSPACEDOTS; i++) {
309                 if(sdot[i].y<0) {
310                         sdot[i].y = 0;
311                 }
312                 rawpixel[(int)(s->pitch/2*(int)sdot[i].y) + (int)(sdot[i].x)] = sdot[i].color;
313                 sdot[i].x -= xscroll / (1.3 + sdot[i].z);
314                 sdot[i].y -= yscroll / (1.3 + sdot[i].z);
315                 if(sdot[i].y >= XSIZE) sdot[i].x -= XSIZE;
316                 else if(sdot[i].x < 0) sdot[i].x = XSIZE-1;
317                 if(sdot[i].y > YSIZE) sdot[i].y -= YSIZE;
318                 else if(sdot[i].y < 0) sdot[i].y += YSIZE-1;
319         }
320 }
321
322 void
323 draw_engine_dots(SDL_Surface *s) {
324         int i;
325         Uint16 *rawpixel;
326         rawpixel = (Uint16 *) s->pixels;
327
328         for(i = 0; i<MAXENGINEDOTS; i++) {
329                 if(edot[i].active) {
330                         edot[i].x += edot[i].dx*gamerate - xscroll;
331                         edot[i].y += edot[i].dy*gamerate - yscroll;
332                         if((edot[i].life -= gamerate*3)<0 || edot[i].y<0 || edot[i].y>YSIZE) {
333                                 edot[i].active = 0;
334                         } else if(edot[i].x<0 || edot[i].x>XSIZE) {
335                                 edot[i].active = 0;
336                         } else {
337                                 int heatindex;
338                                 heatindex = edot[i].life * 6;
339                                 //rawpixel[(int)(s->pitch/2*(int)(edot[i].y)) + (int)(edot[i].x)] = lifecolor[(int)(edot[i].life)];
340                                 rawpixel[(int)(s->pitch/2*(int)(edot[i].y)) + (int)(edot[i].x)] = heatindex>3*W ? heatcolor[3*W-1] : heatcolor[heatindex];
341                         }
342                 }
343         }
344 }
345
346 void
347 create_engine_dots(int newdots) {
348         int i;
349         double theta,r,dx,dy;
350
351         if(!opt_tail_engine) return;
352
353         if(state == GAMEPLAY) {
354                 for(i = 0; i<newdots*gamerate; i++) {
355                         if(dotptr->active == 0) {
356                                 theta = rnd()*M_PI*2;
357                                 r = rnd();
358                                 dx = cos(theta)*r;
359                                 dy = sin(theta)*r;
360
361                                 dotptr->active = 1;
362                                 dotptr->x = shipx + surf_ship->w/2-14;
363                                 dotptr->y = shipy + surf_ship->h/2 + (rnd()-0.5)*5-1;
364                                 dotptr->dx = 10*(dx-1.5) + shipdx;
365                                 dotptr->dy = 1*dy + shipdy;
366                                 dotptr->life = 45 + rnd(1)*5;
367
368                                 dotptr++;
369                                 if(dotptr-edot >= MAXENGINEDOTS) {
370                                         dotptr = edot;
371                                 }
372                         }
373                 }
374         }
375 }
376
377 void
378 create_engine_dots2(int newdots, int m) {
379         int i;
380         double theta, theta2, dx, dy, adx, ady;
381
382         // Don't create fresh engine dots when
383         // the game is not being played and a demo is not beng shown
384         if(state != GAMEPLAY) return;
385
386         for(i = 0; i<newdots; i++) {
387                 if(dotptr->active == 0) {
388                         theta = rnd()*M_PI*2;
389                         theta2 = rnd()*M_PI*2;
390
391                         dx = cos(theta) * fabs(cos(theta2));
392                         dy = sin(theta) * fabs(cos(theta2));
393                         adx = fabs(dx);
394                         ady = fabs(dy);
395
396
397                         dotptr->active = 1;
398                         dotptr->x = shipx + surf_ship->w/2 + (rnd()-0.5)*3;
399                         dotptr->y = shipy + surf_ship->h/2 + (rnd()-0.5)*3;
400
401                         switch(m) {
402                                 case 0:
403                                         dotptr->x -= 14;
404                                         dotptr->dx = -20*adx + shipdx;
405                                         dotptr->dy = 2*dy + shipdy;
406                                         dotptr->life = 60 * adx;
407                                 break;
408                                 case 1:
409                                         dotptr->dx = 2*dx + shipdx;
410                                         dotptr->dy = -20*ady + shipdy;
411                                         dotptr->life = 60 * ady;
412                                 break;
413                                 case 2:
414                                         dotptr->x += 14;
415                                         dotptr->dx = 20*adx + shipdx;
416                                         dotptr->dy = 2*dy + shipdy;
417                                         dotptr->life = 60 * adx;
418                                 break;
419                                 case 3:
420                                         dotptr->dx = 2*dx + shipdx;
421                                         dotptr->dy = 20*ady + shipdy;
422                                         dotptr->life = 60 * ady;
423                                 break;
424                         }
425                         dotptr++;
426                         if(dotptr-edot >= MAXENGINEDOTS) {
427                                 dotptr = edot;
428                         }
429                 }
430         }
431 }
432
433 void
434 drawdots(SDL_Surface *s) {
435         int m;
436
437         // Create more engine dots comin' out da back
438         if(!gameover) create_engine_dots(200);
439
440         // Create engine dots out the side we're moving from
441         for(m = 0; m<4; m++) {
442                 if(maneuver & 1<<m) { // 'maneuver' is a bit field
443                         create_engine_dots2(80,m);
444                 }
445         }
446
447         SDL_LockSurface(s);
448         draw_space_dots(s);
449         draw_engine_dots(s);
450         draw_bang_dots(s);
451         SDL_UnlockSurface(s);
452 }
453
454 int
455 init(void) {
456
457         int i;
458         SDL_Surface *temp;
459         Uint32 flag;
460
461         // Where are our data files?
462         if(!find_files()) exit(1);
463         read_high_score_table();
464
465         if(opt_sound) {
466                 // Initialize SDL with audio and video
467                 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
468                         opt_sound = 0;
469                         printf ("Can't open sound, starting without it\n");
470                         atexit(SDL_Quit);
471                 } else {
472                         atexit(SDL_Quit);
473                         atexit(SDL_CloseAudio);
474                         opt_sound = init_sound();
475                 }
476         } else {
477                 // Initialize with video only
478                 CONDERROR(SDL_Init(SDL_INIT_VIDEO) != 0);
479                 atexit(SDL_Quit);
480         }
481
482         play_tune(0);
483
484         // Attempt to get the required video size
485         flag = SDL_DOUBLEBUF | SDL_HWSURFACE;
486         if(opt_fullscreen) flag |= SDL_FULLSCREEN;
487         surf_screen = SDL_SetVideoMode(XSIZE,YSIZE,16,flag);
488
489         // Set the title bar text
490         SDL_WM_SetCaption("Variations on Rockdodger", "VoR");
491
492         NULLERROR(surf_screen);
493
494         // Set the heat color from the range 0 (cold) to 300 (blue-white)
495         for(i = 0; i<W*3; i++) {
496                 heatcolor[i] = SDL_MapRGB(
497                         surf_screen->format,
498                         (i<W)?(i*M/W):(M),(i<W)?0:(i<2*W)?((i-W)*M/W):M,(i<2*W)?0:((i-W)*M/W) // Got that?
499                 );
500         }
501
502         // Load the banners
503         NULLERROR(temp = IMG_Load(add_path("banners/variations.png")));
504         NULLERROR(surf_b_variations = SDL_DisplayFormat(temp));
505
506         NULLERROR(temp = IMG_Load(add_path("banners/on.png")));
507         NULLERROR(surf_b_on = SDL_DisplayFormat(temp));
508
509         NULLERROR(temp = IMG_Load(add_path("banners/rockdodger.png")));
510         NULLERROR(surf_b_rockdodger = SDL_DisplayFormat(temp));
511
512         NULLERROR(temp = IMG_Load(add_path("banners/game.png")));
513         NULLERROR(surf_b_game = SDL_DisplayFormat(temp));
514
515         NULLERROR(temp = IMG_Load(add_path("banners/over.png")));
516         NULLERROR(surf_b_over = SDL_DisplayFormat(temp));
517
518         surf_font_big = IMG_Load(add_path(BIG_FONT_FILE));
519         g_font = SFont_InitFont(surf_font_big);
520
521         // Load the spaceship graphic.
522         NULLERROR(temp = IMG_Load(add_path("sprites/ship.png")));
523         NULLERROR(surf_ship = SDL_DisplayFormat(temp));
524         get_shape(surf_ship, &shipshape);
525
526         // Load the life indicator (small ship) graphic.
527         NULLERROR(temp = IMG_Load(add_path("indicators/life.png")));
528         NULLERROR(surf_life = SDL_DisplayFormat(temp));
529
530         init_engine_dots();
531         init_space_dots();
532
533         init_rocks();
534
535         // Remove the mouse cursor
536 #ifdef SDL_DISABLE
537         SDL_ShowCursor(SDL_DISABLE);
538 #endif
539
540         return 0;
541 }
542
543 int
544 draw() {
545         int i;
546         SDL_Rect dest;
547         int bang, x;
548         char *text;
549         float fadegame,fadeover;
550
551         bang = 0;
552
553         // Draw a fully black background
554         SDL_FillRect(surf_screen,NULL,0);
555
556         // Draw the background dots
557         drawdots(surf_screen);
558
559         // Draw ship
560         if(!gameover && state == GAMEPLAY ) {
561                 dest.x = shipx;
562                 dest.y = shipy;
563                 SDL_BlitSurface(surf_ship,NULL,surf_screen,&dest);
564         }
565
566         draw_rocks();
567
568         // Draw the life indicators.
569         if(state == GAMEPLAY || state == DEAD_PAUSE || state == GAME_OVER)
570         for(i = 0; i<nships-1; i++) {
571                 dest.x = (i + 1)*(surf_life->w + 10);
572                 dest.y = 20;
573                 SDL_BlitSurface(surf_life, NULL, surf_screen, &dest);
574         }
575
576         // Draw the score
577         snprintscore_line(topline, 50, score);
578         SFont_Write(surf_screen, g_font, XSIZE-250, 0, topline);
579
580         // If it's game over, show the game over graphic in the dead centre
581         switch (state) {
582                 case GAME_OVER:
583                         if(fadetimer<3.0/faderate) {
584                                 fadegame = fadetimer/(3.0/faderate);
585                         } else {
586                                 fadegame = 1.0;
587                         }
588
589                         if(fadetimer<3.0/faderate) {
590                                 fadeover = 0.0;
591                         } else if(fadetimer<6.0/faderate) {
592                                 fadeover = ((3.0/faderate)-fadetimer)/(6.0/faderate);
593                         } else {
594                                 fadeover = 1.0;
595                         }
596
597                         dest.x = (XSIZE-surf_b_game->w)/2;
598                         dest.y = (YSIZE-surf_b_game->h)/2-40;
599                         SDL_SetAlpha(surf_b_game, SDL_SRCALPHA, (int)(fadegame*(200 + 55*cos(fadetimer += gamerate/1.0))));
600                         SDL_BlitSurface(surf_b_game,NULL,surf_screen,&dest);
601
602                         dest.x = (XSIZE-surf_b_over->w)/2;
603                         dest.y = (YSIZE-surf_b_over->h)/2 + 40;
604                         SDL_SetAlpha(surf_b_over, SDL_SRCALPHA, (int)(fadeover*(200 + 55*sin(fadetimer))));
605                         SDL_BlitSurface(surf_b_over,NULL,surf_screen,&dest);
606                 break;
607
608                 case TITLE_PAGE:
609
610                         dest.x = (XSIZE-surf_b_variations->w)/2 + cos(fadetimer/6.5)*10;
611                         dest.y = (YSIZE/2-surf_b_variations->h)/2 + sin(fadetimer/5.0)*10;
612                         SDL_SetAlpha(surf_b_variations, SDL_SRCALPHA, (int)(200 + 55*sin(fadetimer += gamerate/2.0)));
613                         SDL_BlitSurface(surf_b_variations,NULL,surf_screen,&dest);
614
615                         dest.x = (XSIZE-surf_b_on->w)/2 + cos((fadetimer + 1.0)/6.5)*10;
616                         dest.y = (YSIZE/2-surf_b_on->h)/2 + surf_b_variations->h + 20 + sin((fadetimer + 1.0)/5.0)*10;
617                         SDL_SetAlpha(surf_b_on, SDL_SRCALPHA, (int)(200 + 55*sin(fadetimer-1.0)));
618                         SDL_BlitSurface(surf_b_on,NULL,surf_screen,&dest);
619
620                         dest.x = (XSIZE-surf_b_rockdodger->w)/2 + cos((fadetimer + 2.0)/6.5)*10;
621                         dest.y = (YSIZE/2-surf_b_rockdodger->h)/2 + surf_b_variations->h + surf_b_on->h + 40 + sin((fadetimer + 2.0)/5)*10;
622                         SDL_SetAlpha(surf_b_rockdodger, SDL_SRCALPHA, (int)(200 + 55*sin(fadetimer-2.0)));
623                         SDL_BlitSurface(surf_b_rockdodger,NULL,surf_screen,&dest);
624
625                         text = "Version " VERSION;
626                         x = (XSIZE-SFont_TextWidth(g_font,text))/2 + sin(fadetimer/4.5)*10;
627                         SFont_Write(surf_screen,g_font,x,YSIZE-50 + sin(fadetimer/2)*5,text);
628
629                         text = sequence[(int)(fadetimer/40)%NSEQUENCE];
630                         //text = "Press SPACE to start!";
631                         x = (XSIZE-SFont_TextWidth(g_font,text))/2 + cos(fadetimer/4.5)*10;
632                         SFont_Write(surf_screen,g_font,x,YSIZE-100 + cos(fadetimer/3)*5,text);
633                 break;
634
635                 case HIGH_SCORE_ENTRY:
636                         play_tune(2);
637                         if(!process_score_input()) {  // done inputting name
638
639                                 // Change state to briefly show high scores page
640                                 state = HIGH_SCORE_DISPLAY;
641                                 state_timeout = 200;
642
643                                 // Write the high score table to the file
644                                 write_high_score_table();
645                 
646                                 // Play the title page tune
647                                 play_tune(0);
648                         }
649                 // FALL THROUGH TO
650                 case HIGH_SCORE_DISPLAY:
651                         // Display de list o high scores mon.
652                         display_scores(surf_screen, 150,50);
653                         break;
654                 case GAMEPLAY:
655                 case DEAD_PAUSE:
656                         ; // no action necessary
657         }
658
659         if(!gameover && state == GAMEPLAY) {
660                 bang = hit_rocks(shipx, shipy, &shipshape);
661         }
662
663         ticks_since_last = SDL_GetTicks()-last_ticks;
664         last_ticks = SDL_GetTicks();
665         if(ticks_since_last>200 || ticks_since_last<0) {
666                 gamerate = 0;
667         }
668         else {
669                 gamerate = opt_gamespeed*ticks_since_last/50.0;
670                 if(state == GAMEPLAY) {
671                         score += ticks_since_last;
672                 }
673         }
674
675         // Update the surface
676         SDL_Flip(surf_screen);
677
678
679         return bang;
680 }
681
682 int
683 gameloop() {
684         Uint8 *keystate;
685         float tmp;
686
687
688         for(;;) {
689                 if(!paused) {
690                         // Count down the game loop timer, and change state when it gets to zero or less;
691
692                         if((state_timeout -= gamerate*3) < 0) {
693                                 switch(state) {
694                                         case DEAD_PAUSE:
695                                                 // Create a new ship and start all over again
696                                                 state = GAMEPLAY;
697                                                 play_tune(1);
698                                                 break;
699                                         case GAME_OVER:
700                                                 state = HIGH_SCORE_ENTRY;
701                                                 state_timeout = 5.0e6;
702                                                 if(new_high_score(score)) {
703                                                         SDL_Event e;
704                                                         SDL_EnableUNICODE(1);
705                                                         while(SDL_PollEvent(&e))
706                                                                 ;
707                                                 } else {
708                                                         state = HIGH_SCORE_DISPLAY;
709                                                         state_timeout = 400;
710                                                 }
711                                                 break;
712                                         case HIGH_SCORE_DISPLAY:
713                                                 state = TITLE_PAGE;
714                                                 state_timeout = 500.0;
715                                                 break;
716                                         case HIGH_SCORE_ENTRY:
717                                                 // state = TITLE_PAGE;
718                                                 // play_tune(1);
719                                                 // state_timeout = 100.0;
720                                                 break;
721                                         case TITLE_PAGE:
722                                                 state = HIGH_SCORE_DISPLAY;
723                                                 state_timeout = 200.0;
724                                                 break;
725                                         case GAMEPLAY:
726                                                 ; // no action necessary
727                                 }
728                         } else {
729                                 if(state == DEAD_PAUSE) {
730                                         float blast_radius;
731                                         int fixonly;
732
733                                         if(state_timeout < DEAD_PAUSE_LENGTH - 20.0) {
734                                                 blast_radius = BLAST_RADIUS * 1.3;
735                                                 fixonly = 1;
736                                         } else {
737                                                 blast_radius = BLAST_RADIUS * (DEAD_PAUSE_LENGTH - state_timeout) / 20.0;
738                                                 fixonly = 0;
739                                         }
740                                         blast_rocks(bangx, bangy, blast_radius, fixonly);
741
742                                         if(bangx < 60) bangx = 60;
743                                 }
744                         }
745
746                         new_rocks();
747
748                         // FRICTION?
749                         if(opt_friction) {
750                                 shipdx *= pow((double)0.9,(double)gamerate);
751                                 shipdy *= pow((double)0.9,(double)gamerate);
752                         }
753
754                         // INERTIA
755                         shipx += shipdx*gamerate;
756                         shipy += shipdy*gamerate;
757
758                         // SCROLLING
759                         tmp = shipy - (YSIZE / 2);
760                         tmp += shipdy * 25;
761                         tmp /= -25;
762                         tmp = ((screendy * (gamerate - 12)) + (tmp * gamerate)) / 12;
763                         screendy = -tmp;
764                         tmp = shipx - (XSIZE / 3);
765                         tmp += shipdx * 25;
766                         tmp /= -25;
767                         tmp = ((screendx * (gamerate - 12)) + (tmp * gamerate)) / 12;
768                         screendx = -tmp;
769                         if(screendx < 7.5) screendx=7.5;
770
771                         xscroll = screendx * gamerate;
772                         yscroll = screendy * gamerate;
773                         shipx -= xscroll;
774                         shipy -= yscroll;
775
776                         // move bang center
777                         bangx += bangdx*gamerate - xscroll;
778                         bangy += bangdy*gamerate - yscroll;
779
780                         move_rocks();
781
782
783                         // BOUNCE X
784                         if(shipx<0 || shipx>XSIZE-surf_ship->w) {
785                                 // BOUNCE from left and right wall
786                                 shipx -= (shipdx-screendx)*gamerate;
787                                 shipdx = 2*screendx-shipdx;
788                         }
789
790                         // BOUNCE Y
791                         if(shipy<0 || shipy>YSIZE-surf_ship->h) {
792                                 // BOUNCE from top and bottom wall
793                                 shipy -= (shipdy-screendy)*gamerate;
794                                 shipdy = 2*screendy-shipdy;
795                         }
796
797
798                         if(draw() && state == GAMEPLAY) {
799                                 // Died
800                                 play_sound(0); // Play the explosion sound
801                                 bangx = shipx; bangy = shipy; bangdx = shipdx; bangdy = shipdy;
802                                 make_bang_dots(shipx,shipy,shipdx,shipdy,surf_ship,30);
803                                 shipdx *= 0.5; shipdy *= 0.5;
804                                 if(--nships <= 0) {
805                                         state = GAME_OVER;
806                                         gameover = 1;
807                                         shipdx = 8; shipdy = 0;
808                                         state_timeout = 200.0;
809                                         fadetimer = 0.0;
810                                         faderate = gamerate;
811                                 }
812                                 else {
813                                         state = DEAD_PAUSE;
814                                         state_timeout = DEAD_PAUSE_LENGTH;
815                                 }
816                         }
817
818                         SDL_PumpEvents();
819                         keystate = SDL_GetKeyState(NULL);
820
821                         // new game
822                         if(keystate[SDLK_SPACE] && (state == HIGH_SCORE_DISPLAY || state == TITLE_PAGE)) {
823
824                                 reset_rocks();
825
826                                 nships = 4;
827                                 score = 0;
828
829                                 state = GAMEPLAY;
830                                 play_tune(1);
831
832                                 gameover = 0;
833                                 shipx = XSIZE/2.2; shipy = YSIZE/2;
834                                 shipdx = screendx; shipdy = screendy;
835                         }
836
837                         maneuver = 0;
838                 } else {
839                         SDL_PumpEvents();
840                         keystate = SDL_GetKeyState(NULL);
841                 }
842
843                 if(state == GAMEPLAY) {
844                         if(!gameover) {
845
846                                 if(!paused) {
847                                         if(keystate[SDLK_UP] | keystate[SDLK_c])                { shipdy -= 1.5*gamerate; maneuver |= 1<<3;}
848                                         if(keystate[SDLK_DOWN] | keystate[SDLK_t])              { shipdy += 1.5*gamerate; maneuver |= 1<<1;}
849                                         if(keystate[SDLK_LEFT] | keystate[SDLK_h])              { shipdx -= 1.5*gamerate; maneuver |= 1<<2;}
850                                         if(keystate[SDLK_RIGHT] | keystate[SDLK_n])             { shipdx += 1.5*gamerate; maneuver |= 1;}
851                                         if(keystate[SDLK_3])            { SDL_SaveBMP(surf_screen, "snapshot.bmp"); }
852                                 }
853
854                                 if(keystate[SDLK_p] | keystate[SDLK_s]) {
855                                         if(!pausedown) {
856                                                 paused = !paused;
857                                                 pausedown = 1;
858                                         }
859                                 } else {
860                                         pausedown = 0;
861                                 }
862
863                         }
864                         else {
865                                 paused = 0;
866                                 pausedown = 0;
867                         }
868                 } else if(state == GAME_OVER) {
869                         if(keystate[SDLK_SPACE]) {
870                                 state_timeout = -1;
871                         }
872                 }
873
874                 if(state != HIGH_SCORE_ENTRY && (keystate[SDLK_q] || keystate[SDLK_ESCAPE])) {
875                         return 0;
876                 }
877
878         }
879 }
880
881 int
882 main(int argc, char **argv) {
883         init_opts();
884
885         argp_parse(&argp, argc, argv, 0, 0, 0);
886
887         if(init()) {
888                 printf ("ta: '%s'\n",initerror);
889                 return 1;
890         }
891
892         reset_rocks();
893         gameloop();
894
895         return 0;
896 }