JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Some code cleanup.
[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 #undef DEBUG
23
24 extern int font_height;
25 void clearBuffer();
26
27 // includes {{{
28 #include "config.h"
29 #include "file.h"
30 #include "sound.h"
31
32 #include <math.h>
33 #include <SDL/SDL.h>
34 #include <SDL/SDL_image.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include "SFont.h"
42 // }}}
43 // constants {{{
44 // }}}
45 // macros {{{
46 #define CONDERROR(a) if((a)) {initerror = strdup(SDL_GetError());return 1;}
47 #define NULLERROR(a) CONDERROR((a) == NULL)
48 // }}}
49
50 // ************************************* STRUCTS
51 struct rock_struct {
52         // Array of black pixel coordinates. This is scanned 
53         // every frame to see if it's still black, and as
54         // soon as it isn't we BLOW UP
55         float x,y,xvel,yvel;
56         int active;
57         int dead;  // has been blown out of the way
58                    // to make room for a new ship appearing.
59         SDL_Surface *image;
60         int type_number;
61 }; 
62 struct black_point_struct {
63         int x,y;
64 };
65 struct bangdots {
66         // Bang dots have the same colour as shield dots.
67         // Bang dots get darker as they age.
68         // Some are coloured the same as the ex-ship.
69         float x,y,dx,dy;
70         Uint16 c; // when zero, use heatcolor[bangdotlife]
71         float life;     // When reduced to 0, set active = 0
72         int active;
73         float decay;// Amount by which to reduce life each time dot is drawn
74 };
75 struct enginedots {
76         // Engine dots stream out the back of the ship, getting darker as they go.
77         int active;
78         float x,y,dx,dy;
79         // The life of an engine dot 
80         // is a number starting at between 0 and 50 and counting backward.
81         float life;     // When reduced to 0, set active = 0
82 };
83 struct spacedot {
84         // Space dots are harmless background items
85         // All are active. When one falls off the edge, another is created at the start.
86         float x,y,dx;
87         Uint16 color;
88 };
89 // High score table {{{
90 struct highscore {
91         int score;
92         char *name;
93         int allocated;
94 } high[] = {
95         {13000,"Pad",0},
96         {12500,"Pad",0},
97         {6500,"Pad",0},
98         {5000,"Pad",0},
99         {3000,"Pad",0},
100         {2500,"Pad",0},
101         {2000,"Pad",0},
102         {1500,"Pad",0}
103 };
104 // }}}
105
106 // ************************************* VARS
107 // SDL_Surface global variables {{{
108 SDL_Surface 
109         *surf_screen,   // Screen
110         *surf_b_variations, // "variations" banner
111         *surf_b_on, // "on" banner
112         *surf_b_rockdodger, // "rockdodger" banner
113         *surf_b_game,   // Title element "game"
114         *surf_b_over,   // Title element "over"
115         *surf_ship,             // Spaceship element
116         *surf_life,     // Indicator of number of ships remaining
117         *surf_rock[NROCKS],     // THE ROCKS
118         *surf_deadrock[NROCKS], // THE DEAD ROCKS
119         *surf_font_big; // The big font
120 // }}}
121 // Structure global variables {{{
122 struct enginedots edot[MAXENGINEDOTS], *dotptr = edot;
123 struct rock_struct rock[MAXROCKS], *rockptr = rock;
124 struct black_point_struct black_point[MAXBLACKPOINTS], *blackptr = black_point;
125 struct bangdots bdot[MAXBANGDOTS], *bdotptr = bdot;
126 struct spacedot sdot[MAXSPACEDOTS];
127 // }}}
128 // Other global variables {{{
129 char topline[1024];
130 char *initerror = "";
131 char name[1024], debug1[1024];
132
133 float xship,yship = 240.0;      // X position, 0..XSIZE
134 float xvel,yvel;        // Change in X position per tick.
135 float rockrate,rockspeed;
136 float movementrate;
137 float yscroll;
138 float scrollvel;
139
140 int nships,score,initticks,ticks_since_last, last_ticks;
141 int gameover;
142 int countdown = 0;
143 int maneuver = 0;
144 int sound_flag = 1, music_flag = 0;
145 int tail_plume = 0; // display big engine at the back?
146 int friction = 0;       // should there be friction?
147 int scorerank;
148 float fadetimer = 0,faderate;
149
150 int pausedown = 0,paused = 0;
151
152 // bangdot start (bd1) and end (bd2) position:
153 int bd1 = 0, bd2 = 0;
154
155 int xoffset[NROCKS][MAXROCKHEIGHT];
156
157 enum states {
158         TITLE_PAGE,
159         GAMEPLAY,
160         DEAD_PAUSE,
161         GAME_OVER,
162         HIGH_SCORE_ENTRY,
163         HIGH_SCORE_DISPLAY,
164         DEMO
165 };
166 enum states state = TITLE_PAGE;
167 float state_timeout = 600.0;
168
169 const int fakesin[] = {0,1,0,-1};
170 const int fakecos[] = {1,0,-1,0};
171 #define NSEQUENCE 2
172 char *sequence[] = {
173         "Press SPACE to start",
174         "http://qualdan.com/vor/"
175 };
176
177 int bangdotlife, nbangdots;
178 Uint16 heatcolor[W*3];
179
180 char *data_dir;
181 extern char *optarg;
182 extern int optind, opterr, optopt;
183 // }}}
184
185 float dist_sq(float x1, float y1, float x2, float y2)
186 {
187         return (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
188 }
189
190 // ************************************* FUNCS
191
192 void read_high_score_table() {
193         FILE *f;
194         int i;
195         
196         f = open_score_file("r");
197         if(f) {
198                 // If the file exists, read from it
199                 for(i = 0; i<8; i++) {
200                         char s[1024];
201                         int highscore;
202                         if(fscanf (f, "%d %[^\n]", &highscore, s) != 2) {
203                                 break;
204                         }
205                         if(high[i].allocated) {
206                                 free(high[i].name);
207                         }
208                         high[i].name = strdup(s);
209                         high[i].score = highscore;
210                         high[i].allocated = 1;
211                 }
212                 fclose(f);
213         }
214 }
215
216 void write_high_score_table() {
217         FILE *f;
218         int i;
219         
220         f = open_score_file("w");
221         if(f) {
222                 // If the file exists, write to it
223                 for(i = 0; i<8; i++) {
224                         fprintf (f, "%d %s\n", high[i].score, high[i].name);
225                 }
226                 fclose(f);
227         }
228 }
229 void snprintscore(char *s, size_t n, int score) {
230         int min = score/60000;
231         int sec = score/1000%60;
232         int tenths = score%1000/100;
233         if(min) {
234                 snprintf(s, n, "%2d:%.2d.%d", min, sec, tenths);
235         } else {
236                 snprintf(s, n, " %2d.%d", sec, tenths);
237         }
238 }
239 float rnd() {
240         return (float)random()/(float)RAND_MAX;
241 }
242 void init_engine_dots() {
243         int i;
244         for(i = 0; i<MAXENGINEDOTS; i++) {
245                 edot[i].active = 0;
246         }
247 }
248 void init_space_dots() {
249         int i,intensity;
250         for(i = 0; i<MAXSPACEDOTS; i++) {
251                 float r;
252
253                 sdot[i].x = rnd()*(XSIZE-5);
254                 sdot[i].y = rnd()*(YSIZE-5);
255
256                 r = rnd()*rnd();
257
258                 sdot[i].dx = -r*4;
259                 // -1/((1-r) + .3);
260                 intensity = (int)(r*180 + 70);
261                 sdot[i].color = SDL_MapRGB(surf_screen->format,intensity,intensity,intensity);
262
263         }
264 }
265
266 void makebangdots(int xbang, int ybang, int xvel, int yvel, SDL_Surface *s, int power) {
267
268         // TODO - stop generating dots after a certain amount of time has passed, to cope with slower CPUs.
269         // TODO - generate and display dots in a circular buffer
270
271         int x,y,endcount;
272         Uint16 *rawpixel,c;
273         double theta,r;
274         int begin_generate;
275
276         begin_generate = SDL_GetTicks();
277
278         SDL_LockSurface(s);
279         rawpixel = (Uint16 *) s->pixels;
280
281         //for(n = 0; n <= power/2; n++) {
282
283         endcount = 0;
284         while (endcount<3) {
285                 for(x = 0; x<s->w; x++) {
286                         for(y = 0; y<s->h; y++) {
287                                 c = rawpixel[s->pitch/2*y + x];
288                                 if(c && c != SDL_MapRGB(s->format,0,255,0)) {
289
290                                         theta = rnd()*M_PI*2;
291
292                                         r = 1-(rnd()*rnd());
293
294                                         bdot[bd2].dx = (power/50.0)*45.0*cos(theta)*r + xvel;
295                                         bdot[bd2].dy = (power/50.0)*45.0*sin(theta)*r + yvel;
296                                         bdot[bd2].x = x + xbang;
297                                         bdot[bd2].y = y + ybang;
298
299                                         // Replace the last few bang dots with the pixels from the exploding object
300                                         bdot[bd2].c = (endcount>0)?c:0;
301                                         bdot[bd2].life = 100;
302                                         bdot[bd2].decay = rnd()*3 + 1;
303                                         bdot[bd2].active = 1;
304
305                                         bd2++;
306                                         bd2 %= MAXBANGDOTS;
307
308                                         // If the circular buffer is filled, who cares? They've had their chance.
309                                         //if(bd2 == bd1-1) goto exitloop;
310
311                                 }
312                         }
313                 }
314
315                 if(SDL_GetTicks() - begin_generate > 7) endcount++;
316         }
317
318         SDL_UnlockSurface(s);
319
320 }
321
322 void draw_bang_dots(SDL_Surface *s) {
323         int i;
324         int first_i, last_i;
325         Uint16 *rawpixel;
326         rawpixel = (Uint16 *) s->pixels;
327
328         first_i = -1;
329
330         for(i = bd1; (bd1 <= bd2)?(i<bd2):(i >= bd1 && i < bd2); last_i = ++i) {
331
332                 i %= MAXBANGDOTS;
333
334                 if(bdot[i].x <= 0 || bdot[i].x >= XSIZE || bdot[i].y <= 0 || bdot[i].y >= YSIZE) {
335                         // If the dot has drifted outside the perimeter, kill it
336                         bdot[i].active = 0;
337                 }
338
339                 if(bdot[i].active) {
340                         if(first_i < 0)
341                         first_i = i;
342                         //last_i = i + 1;
343                         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)];
344                         bdot[i].life -= bdot[i].decay;
345                         bdot[i].x += bdot[i].dx*movementrate;
346                         bdot[i].y += bdot[i].dy*movementrate + yscroll;
347
348                         if(bdot[i].life<0)
349                         bdot[i].active = 0;
350                 }
351         }
352
353         if(first_i >= 0) {
354                 bd1 = first_i;
355                 bd2 = last_i;
356         }
357         else {
358                 bd1 = 0;
359                 bd2 = 0;
360         }
361
362 }
363
364
365 void draw_space_dots(SDL_Surface *s) {
366         int i;
367         Uint16 *rawpixel;
368         rawpixel = (Uint16 *) s->pixels;
369
370         for(i = 0; i<MAXSPACEDOTS; i++) {
371                 if(sdot[i].y<0) {
372                         sdot[i].y = 0;
373                 }
374                 rawpixel[(int)(s->pitch/2*(int)sdot[i].y) + (int)(sdot[i].x)] = sdot[i].color;
375                 sdot[i].x += sdot[i].dx*movementrate;
376                 sdot[i].y += yscroll;
377                 if(sdot[i].y > YSIZE) {
378                         sdot[i].y -= YSIZE;
379                 } else if(sdot[i].y < 0) {
380                         sdot[i].y += YSIZE;
381                 }
382                 if(sdot[i].x<0) {
383                         sdot[i].x = XSIZE;
384                 }
385         }
386 }
387
388 void draw_engine_dots(SDL_Surface *s) {
389         int i;
390         Uint16 *rawpixel;
391         rawpixel = (Uint16 *) s->pixels;
392
393         for(i = 0; i<MAXENGINEDOTS; i++) {
394                 if(edot[i].active) {
395                         edot[i].x += edot[i].dx*movementrate;
396                         edot[i].y += edot[i].dy*movementrate + yscroll;
397                         if((edot[i].life -= movementrate*3)<0 || edot[i].y<0 || edot[i].y>YSIZE) {
398                                 edot[i].active = 0;
399                         } else if(edot[i].x<0 || edot[i].x>XSIZE) {
400                                 edot[i].active = 0;
401                         } else {
402                                 int heatindex;
403                                 heatindex = edot[i].life * 6;
404                                 //rawpixel[(int)(s->pitch/2*(int)(edot[i].y)) + (int)(edot[i].x)] = lifecolor[(int)(edot[i].life)];
405                                 rawpixel[(int)(s->pitch/2*(int)(edot[i].y)) + (int)(edot[i].x)] = heatindex>3*W ? heatcolor[3*W-1] : heatcolor[heatindex];
406                         }
407                 }
408         }
409 }
410
411 void create_engine_dots(int newdots) {
412         int i;
413         double theta,r,dx,dy;
414
415         if(!tail_plume) return;
416
417         if(state == GAMEPLAY) {
418                 for(i = 0; i<newdots*movementrate; i++) {
419                         if(dotptr->active == 0) {
420                                 theta = rnd()*M_PI*2;
421                                 r = rnd();
422                                 dx = cos(theta)*r;
423                                 dy = sin(theta)*r;
424
425                                 dotptr->active = 1;
426                                 dotptr->x = xship + surf_ship->w/2-14;
427                                 dotptr->y = yship + surf_ship->h/2 + (rnd()-0.5)*5-1;
428                                 dotptr->dx = 10*(dx-1.5) + xvel;
429                                 dotptr->dy = 1*dy + yvel;
430                                 dotptr->life = 45 + rnd(1)*5;
431
432                                 dotptr++;
433                                 if(dotptr-edot >= MAXENGINEDOTS) {
434                                         dotptr = edot;
435                                 }
436                         }
437                 }
438         }
439 }
440
441 void create_engine_dots2(int newdots, int m) {
442         int i;
443         double theta, theta2, dx, dy, adx, ady;
444
445         // Don't create fresh engine dots when
446         // the game is not being played and a demo is not beng shown
447         if(state != GAMEPLAY && state != DEMO) return;
448
449         for(i = 0; i<newdots; i++) {
450                 if(dotptr->active == 0) {
451                         theta = rnd()*M_PI*2;
452                         theta2 = rnd()*M_PI*2;
453
454                         dx = cos(theta) * fabs(cos(theta2));
455                         dy = sin(theta) * fabs(cos(theta2));
456                         adx = fabs(dx);
457                         ady = fabs(dy);
458
459
460                         dotptr->active = 1;
461                         dotptr->x = xship + surf_ship->w/2 + (rnd()-0.5)*3;
462                         dotptr->y = yship + surf_ship->h/2 + (rnd()-0.5)*3;
463
464                         switch(m) {
465                                 case 0:
466                                         dotptr->x -= 14;
467                                         dotptr->dx = -20*adx + xvel;
468                                         dotptr->dy = 2*dy + yvel;
469                                         dotptr->life = 60 * adx;
470                                 break;
471                                 case 1:
472                                         dotptr->dx = 2*dx + xvel;
473                                         dotptr->dy = -20*ady + yvel;
474                                         dotptr->life = 60 * ady;
475                                 break;
476                                 case 2:
477                                         dotptr->x += 14;
478                                         dotptr->dx = 20*adx + xvel;
479                                         dotptr->dy = 2*dy + yvel;
480                                         dotptr->life = 60 * adx;
481                                 break;
482                                 case 3:
483                                         dotptr->dx = 2*dx + xvel;
484                                         dotptr->dy = 20*ady + yvel;
485                                         dotptr->life = 60 * ady;
486                                 break;
487                         }
488                         dotptr++;
489                         if(dotptr-edot >= MAXENGINEDOTS) {
490                                 dotptr = edot;
491                         }
492                 }
493         }
494 }
495
496 void drawdots(SDL_Surface *s) {
497         int m, scorepos, n;
498
499         SDL_LockSurface(s);
500         // Draw the background stars aka space dots
501         draw_space_dots(s);
502
503         // Draw the score when playing the game or whn the game is freshly over
504         if(1 || state == GAMEPLAY || state == DEAD_PAUSE || state == GAME_OVER ) {
505                 SDL_UnlockSurface(s);
506
507                 scorepos = XSIZE-250;
508                 n = snprintf(topline, 50, "Time: ");
509                 snprintscore(topline + n, 50-n, score);
510                 PutString(s,scorepos,0,topline);
511
512                 SDL_LockSurface(s);
513         }
514
515         // Draw all the engine dots
516         draw_engine_dots(s);
517
518         // Create more engine dots comin out da back
519         if(!gameover)
520         create_engine_dots(200);
521
522         // Create engine dots out the side we're moving from
523         for(m = 0; m<4; m++) {
524                 if(maneuver & 1<<m) { // 'maneuver' is a bit field
525                         create_engine_dots2(80,m);
526                 }
527         }
528
529         // Draw all outstanding bang dots
530         //if(bangdotlife-- > 0) 
531         draw_bang_dots(s);
532
533         SDL_UnlockSurface(s);
534 }
535
536 int init(int fullscreen) {
537
538         int i,j;
539         SDL_Surface *temp;
540         Uint16 *raw_pixels;
541         Uint32 flag;
542
543         // Where are our data files?
544         if(!find_files()) exit(1);
545         read_high_score_table();
546
547         if(sound_flag) {
548                 // Initialize SDL with audio and video
549                 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
550                         sound_flag = 0;
551                         printf ("Can't open sound, starting without it\n");
552                         atexit(SDL_Quit);
553                 } else {
554                         atexit(SDL_Quit);
555                         atexit(SDL_CloseAudio);
556                         sound_flag = init_sound();
557                 }
558         } else {
559                 // Initialize with video only
560                 CONDERROR(SDL_Init(SDL_INIT_VIDEO) != 0);
561                 atexit(SDL_Quit);
562         }
563
564         play_tune(0);
565
566         // Attempt to get the required video size
567         flag = SDL_DOUBLEBUF | SDL_HWSURFACE;
568         if(fullscreen) flag |= SDL_FULLSCREEN;
569         surf_screen = SDL_SetVideoMode(XSIZE,YSIZE,16,flag);
570
571         // Set the title bar text
572         SDL_WM_SetCaption("Variations on Rockdodger", "VoR");
573
574         NULLERROR(surf_screen);
575
576         // Set the heat color from the range 0 (cold) to 300 (blue-white)
577         for(i = 0; i<W*3; i++) {
578                 heatcolor[i] = SDL_MapRGB(
579                         surf_screen->format,
580                         (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?
581                 );
582         }
583
584         // Load the banners
585         NULLERROR(temp = IMG_Load(load_file("banners/variations.png")));
586         NULLERROR(surf_b_variations = SDL_DisplayFormat(temp));
587
588         NULLERROR(temp = IMG_Load(load_file("banners/on.png")));
589         NULLERROR(surf_b_on = SDL_DisplayFormat(temp));
590
591         NULLERROR(temp = IMG_Load(load_file("banners/rockdodger.png")));
592         NULLERROR(surf_b_rockdodger = SDL_DisplayFormat(temp));
593
594         NULLERROR(temp = IMG_Load(load_file("banners/game.png")));
595         NULLERROR(surf_b_game = SDL_DisplayFormat(temp));
596
597         NULLERROR(temp = IMG_Load(load_file("banners/over.png")));
598         NULLERROR(surf_b_over = SDL_DisplayFormat(temp));
599
600         surf_font_big = IMG_Load(load_file(BIG_FONT_FILE));
601         InitFont(surf_font_big);
602
603         // Load the spaceship graphic.
604         NULLERROR(temp = IMG_Load(load_file("sprites/ship.png")));
605         NULLERROR(surf_ship = SDL_DisplayFormat(temp));
606
607         // Load the life indicator (small ship) graphic.
608         NULLERROR(temp = IMG_Load(load_file("indicators/life.png")));
609         NULLERROR(surf_life = SDL_DisplayFormat(temp));
610
611         // Create the array of black points;
612         SDL_LockSurface(surf_ship);
613         raw_pixels = (Uint16 *) surf_ship->pixels;
614         for(i = 0; i<surf_ship->w; i++) {
615                 for(j = 0; j<surf_ship->h; j++) {
616                         if(raw_pixels[j*(surf_ship->pitch)/2 + i] == 0) {
617                                 blackptr->x = i;
618                                 blackptr->y = j;
619                                 blackptr++;
620                         }
621                 }
622         }
623
624         SDL_UnlockSurface(surf_ship);
625
626         init_engine_dots();
627         init_space_dots();
628
629         // Load all our lovely rocks
630         for(i = 0; i<NROCKS; i++) {
631                 char a[100];
632
633                 sprintf(a,load_file("sprites/rock%d.png"),i);
634                 NULLERROR(temp = IMG_Load(a));
635                 NULLERROR(surf_rock[i] = SDL_DisplayFormat(temp));
636
637                 sprintf(a,load_file("sprites/deadrock%d.png"),i);
638                 NULLERROR(temp = IMG_Load(a));
639                 NULLERROR(surf_deadrock[i] = SDL_DisplayFormat(temp));
640         }
641
642         // Remove the mouse cursor
643 #ifdef SDL_DISABLE
644         SDL_ShowCursor(SDL_DISABLE);
645 #endif
646
647         return 0;
648 }
649 int draw() {
650         int i;
651         SDL_Rect src,dest;
652         struct black_point_struct *p;
653         Uint16 *raw_pixels;
654         int bang, offset, x;
655         char *text;
656         float fadegame,fadeover;
657
658         bang = 0;
659
660         src.x = 0;
661         src.y = 0;
662         dest.x = 0;
663         dest.y = 0;
664
665         // Draw a fully black background
666         SDL_FillRect(surf_screen,NULL,0);
667
668
669 #ifdef DEBUG
670         // DEBUG {{{
671         // Show the current state
672         switch (state) {
673                 case TITLE_PAGE:
674                         statedisplay = "title_page";
675                 break;
676                 case GAMEPLAY:
677                         statedisplay = "gameplay";
678                 break;
679                 case DEAD_PAUSE:
680                         statedisplay = "dead_pause";
681                 break;
682                 case GAME_OVER:
683                         statedisplay = "game_over";
684                 break;
685                 case HIGH_SCORE_ENTRY:
686                         statedisplay = "high_score_entry";
687                 break;
688                 case HIGH_SCORE_DISPLAY:
689                         statedisplay = "high_score_display";
690                 break;
691                 case DEMO:
692                         statedisplay = "demo";
693                 break;
694         }
695         snprintf(buf,1024, "mode = %s", statedisplay);
696         PutString(surf_screen,0,YSIZE-50,buf);
697         // }}}
698 #endif
699         
700         // Draw the background dots
701         drawdots(surf_screen);
702
703         // Draw ship
704         if(!gameover && (state == GAMEPLAY || state == DEMO) ) {
705                 src.w = surf_ship->w;
706                 src.h = surf_ship->h;
707                 dest.w = src.w;
708                 dest.h = src.h;
709                 dest.x = (int)xship;
710                 dest.y = (int)yship;
711                 SDL_BlitSurface(surf_ship,&src,surf_screen,&dest);
712         }
713
714         // Draw all the rocks, in all states
715         for(i = 0; i<MAXROCKS; i++) {
716                 if(rock[i].active) {
717
718                         src.w = rock[i].image->w;
719                         src.h = rock[i].image->h;
720                         dest.w = src.w;
721                         dest.h = src.h;
722                         dest.x = (int) rock[i].x;
723                         dest.y = (int) rock[i].y;
724
725                         // Draw the rock
726                         SDL_BlitSurface(rock[i].image,&src,surf_screen,&dest);
727
728                 }
729         }
730
731         // If it's game over, show the game over graphic in the dead centre
732         switch (state) {
733                 case GAME_OVER:
734                         if(fadetimer<3.0/faderate) {
735                                 fadegame = fadetimer/(3.0/faderate);
736                         } else {
737                                 fadegame = 1.0;
738                         }
739
740                         if(fadetimer<3.0/faderate) {
741                                 fadeover = 0.0;
742                         } else if(fadetimer<6.0/faderate) {
743                                 fadeover = ((3.0/faderate)-fadetimer)/(6.0/faderate);
744                         } else {
745                                 fadeover = 1.0;
746                         }
747
748                         src.w = surf_b_game->w;
749                         src.h = surf_b_game->h;
750                         dest.w = src.w;
751                         dest.h = src.h;
752                         dest.x = (XSIZE-src.w)/2;
753                         dest.y = (YSIZE-src.h)/2-40;
754                         SDL_SetAlpha(surf_b_game, SDL_SRCALPHA, (int)(fadegame*(200 + 55*cos(fadetimer += movementrate/1.0))));
755                         SDL_BlitSurface(surf_b_game,&src,surf_screen,&dest);
756
757                         src.w = surf_b_over->w;
758                         src.h = surf_b_over->h;
759                         dest.w = src.w;
760                         dest.h = src.h;
761                         dest.x = (XSIZE-src.w)/2;
762                         dest.y = (YSIZE-src.h)/2 + 40;
763                         SDL_SetAlpha(surf_b_over, SDL_SRCALPHA, (int)(fadeover*(200 + 55*sin(fadetimer))));
764                         SDL_BlitSurface(surf_b_over,&src,surf_screen,&dest);
765                 break;
766
767                 case TITLE_PAGE:
768
769                         src.w = surf_b_variations->w;
770                         src.h = surf_b_variations->h;
771                         dest.w = src.w;
772                         dest.h = src.h;
773                         dest.x = (XSIZE-src.w)/2 + cos(fadetimer/6.5)*10;
774                         dest.y = (YSIZE/2-src.h)/2 + sin(fadetimer/5.0)*10;
775                         SDL_SetAlpha(surf_b_variations, SDL_SRCALPHA, (int)(200 + 55*sin(fadetimer += movementrate/2.0)));
776                         SDL_BlitSurface(surf_b_variations,&src,surf_screen,&dest);
777
778                         src.w = surf_b_on->w;
779                         src.h = surf_b_on->h;
780                         dest.w = src.w;
781                         dest.h = src.h;
782                         dest.x = (XSIZE-src.w)/2 + cos((fadetimer + 1.0)/6.5)*10;
783                         dest.y = (YSIZE/2-src.h)/2 + surf_b_variations->h + 20 + sin((fadetimer + 1.0)/5.0)*10;
784                         SDL_SetAlpha(surf_b_on, SDL_SRCALPHA, (int)(200 + 55*sin(fadetimer-1.0)));
785                         SDL_BlitSurface(surf_b_on,&src,surf_screen,&dest);
786
787                         src.w = surf_b_rockdodger->w;
788                         src.h = surf_b_rockdodger->h;
789                         dest.w = src.w;
790                         dest.h = src.h;
791                         dest.x = (XSIZE-src.w)/2 + cos((fadetimer + 2.0)/6.5)*10;
792                         dest.y = (YSIZE/2-src.h)/2 + surf_b_variations->h + surf_b_on->h + 40 + sin((fadetimer + 2.0)/5)*10;
793                         SDL_SetAlpha(surf_b_rockdodger, SDL_SRCALPHA, (int)(200 + 55*sin(fadetimer-2.0)));
794                         SDL_BlitSurface(surf_b_rockdodger,&src,surf_screen,&dest);
795
796                         text = "Version " VERSION;
797                         x = (XSIZE-SFont_wide(text))/2 + sin(fadetimer/4.5)*10;
798                         PutString(surf_screen,x,YSIZE-50 + sin(fadetimer/2)*5,text);
799
800                         text = sequence[(int)(fadetimer/40)%NSEQUENCE];
801                         //text = "Press SPACE to start!";
802                         x = (XSIZE-SFont_wide(text))/2 + cos(fadetimer/4.5)*10;
803                         PutString(surf_screen,x,YSIZE-100 + cos(fadetimer/3)*5,text);
804                 break;
805
806                 case HIGH_SCORE_ENTRY:
807
808                         if(score >= high[7].score) {
809                                 play_tune(2);
810                                 if(SFont_Input (surf_screen, 330, 50 + (scorerank + 2)*font_height, 300, name)) {
811                                         // Insert name into high score table
812
813                                         // Lose the lowest name forever (loser!)
814                                         //if(high[7].allocated)
815                                         //      free(high[7].name);                     // THIS WAS CRASHING SO I REMOVED IT
816
817                                         // Insert new high score
818                                         high[scorerank].score = score;
819                                         high[scorerank].name = strdup(name);    // MEMORY NEVER FREED!
820                                         high[scorerank].allocated = 1;
821                         
822                                         // Set the global name string to "", ready for the next winner
823                                         name[0] = 0;
824                         
825                                         // Change state to briefly show high scores page
826                                         state = HIGH_SCORE_DISPLAY;
827                                         state_timeout = 200;
828
829                                         // Write the high score table to the file
830                                         write_high_score_table();
831                         
832                                         // Play the title page tune
833                                         play_tune(0);
834                                 }
835                         } else {
836                                 state = HIGH_SCORE_DISPLAY;
837                                 state_timeout = 400;
838                         }
839                 // FALL THROUGH
840
841                 case HIGH_SCORE_DISPLAY:
842                         // Display de list o high scores mon.
843                         PutString(surf_screen,180,50,"High scores");
844                         for(i = 0; i<8; i++) {
845                                 char s[1024];
846                                 sprintf(s, "#%1d",i + 1);
847                                 PutString(surf_screen, 150, 50 + (i + 2)*font_height,s);
848                                 snprintscore(s, 1024, high[i].score);
849                                 PutString(surf_screen, 200, 50 + (i + 2)*font_height,s);
850                                 sprintf(s, "%3s", high[i].name);
851                                 PutString(surf_screen, 330, 50 + (i + 2)*font_height,s);
852                         }
853
854         }
855
856         if(!gameover && state == GAMEPLAY) {
857                 SDL_LockSurface(surf_screen);
858                 raw_pixels = (Uint16 *) surf_screen->pixels;
859                 // Check that the black points on the ship are
860                 // still black, and not covered up by rocks.
861                 for(p = black_point; p<blackptr; p++) { 
862                         offset = surf_screen->pitch/2 * (p->y + (int)yship) + p->x + (int)xship;
863                         if(raw_pixels[offset]) {
864                                 // Set the bang flag
865                                 bang = 1;
866                         }
867                 }
868                 SDL_UnlockSurface(surf_screen);
869         }
870
871         // Draw all the little ships
872         if(state == GAMEPLAY || state == DEAD_PAUSE || state == GAME_OVER)
873         for(i = 0; i<nships-1; i++) {
874                 src.w = surf_life->w;
875                 src.h = surf_life->h;
876                 dest.w = src.w;
877                 dest.h = src.h;
878                 dest.x = (i + 1)*(src.w + 10);
879                 dest.y = 20;
880                 SDL_BlitSurface(surf_life,&src,surf_screen,&dest);
881         }
882
883
884         // Update the score
885         /*
886         n = SDL_GetTicks()-initticks;
887         if(score)
888         ticks_since_last = n-score;
889         score = n;
890         */
891
892         ticks_since_last = SDL_GetTicks()-last_ticks;
893         last_ticks = SDL_GetTicks();
894         if(ticks_since_last>200 || ticks_since_last<0) {
895                 movementrate = 0;
896         }
897         else {
898                 movementrate = ticks_since_last/50.0;
899                 if(state == GAMEPLAY) {
900                         score += ticks_since_last;
901                 }
902         }
903
904         // Update the surface
905         SDL_Flip(surf_screen);
906
907
908         return bang;
909 }
910
911 int gameloop() {
912         int i = 0;
913         Uint8 *keystate;
914
915
916         for(;;) {
917                 if(!paused) {
918                         // Count down the game loop timer, and change state when it gets to zero or less;
919
920                         if((state_timeout -= movementrate*3) < 0) {
921                                 switch(state) {
922                                         case DEAD_PAUSE:
923                                                 // Create a new ship and start all over again
924                                                 state = GAMEPLAY;
925                                                 play_tune(1);
926                                                 xship -= 50;
927                                         break;
928                                         case GAME_OVER:
929                                                 state = HIGH_SCORE_ENTRY;
930                                                 clearBuffer();
931                                                 name[0] = 0;
932                                                 state_timeout = 5.0e6;
933
934                                                 if(score >= high[7].score) {
935                                                         // Read the high score table from the storage file
936                                                         read_high_score_table();
937
938                                                         // Find ranking of this score, store as scorerank
939                                                         for(i = 0; i<8; i++) {
940                                                         if(high[i].score <= score) {
941                                                                 scorerank = i;
942                                                                 break;
943                                                         }
944                                                         }
945
946                                                         // Move all lower scores down a notch
947                                                         for(i = 7; i >= scorerank; i--)
948                                                         high[i] = high[i-1];
949
950                                                         // Insert blank high score
951                                                         high[scorerank].score = score;
952                                                         high[scorerank].name = "";
953                                                         high[scorerank].allocated = 0;
954                                                 }
955
956                                         break;
957                                         case HIGH_SCORE_DISPLAY:
958                                                 state = TITLE_PAGE;
959                                                 state_timeout = 500.0;
960                                         break;
961                                         case HIGH_SCORE_ENTRY:
962                                                 // state = TITLE_PAGE;
963                                                 // play_tune(1);
964                                                 // state_timeout = 100.0;
965                                         break;
966                                         case TITLE_PAGE:
967                                                 state = HIGH_SCORE_DISPLAY;
968                                                 state_timeout = 200.0;
969                                         break;
970                                 }
971                         } else {
972                                 if(state == DEAD_PAUSE) {
973                                         float blast_radius = BLAST_RADIUS * state_timeout / 20.0;
974                                         if(xship < 60) xship = 60;
975                                         for(i = 0; i<MAXROCKS; i++ ) {
976                                                 float dx, dy, n;
977                                                 if(rock[i].x <= 0) continue;
978                                                 dx = rock[i].x - xship;
979                                                 dy = rock[i].y - yship;
980                                                 n = sqrt(dx*dx + dy*dy);
981                                                 if(n < blast_radius) {
982                                                         n *= 20;
983                                                         rock[i].xvel += rockrate*(dx+30)/n;
984                                                         rock[i].yvel += rockrate*dy/n;
985                                                         rock[i].dead = 1;
986                                                 }
987                                         }
988                                 }
989                         }
990
991                         if(--countdown <= 0 && (rnd()*100.0<(rockrate += 0.025))) {
992                                 // Create a rock
993                                 rockptr++;
994                                 if(rockptr-rock >= MAXROCKS) {
995                                         rockptr = rock;
996                                 }
997                                 if(!rockptr->active) {
998                                         rockptr->x = (float)XSIZE;
999                                         rockptr->xvel = -(rockspeed)*(1 + rnd());
1000                                         rockptr->yvel = rnd()-0.5;
1001                                         rockptr->type_number = random() % NROCKS;
1002                                         rockptr->image = surf_rock[rockptr->type_number];// [random()%NROCKS];
1003                                         rockptr->active = 1;
1004                                         rockptr->y = rnd()*(YSIZE + rockptr->image->h);
1005                                 }
1006                                 if(movementrate>0.1) {
1007                                         countdown = (int)(ROCKRATE/movementrate);
1008                                 } else {
1009                                         countdown = 0;
1010                                 }
1011                         }
1012
1013                         // FRICTION?
1014                         if(friction) {
1015                                 xvel *= pow((double)0.9,(double)movementrate);
1016                                 yvel *= pow((double)0.9,(double)movementrate);
1017                                 // if(abs(xvel)<0.00001) xvel = 0;
1018                                 // if(abs(yvel)<0.00001) yvel = 0;
1019                         }
1020
1021                         // INERTIA
1022                         xship += xvel*movementrate;
1023                         yship += yvel*movementrate;
1024
1025                         // SCROLLING
1026                         yscroll = yship - (YSIZE / 2);
1027                         yscroll += yvel * 25;
1028                         yscroll /= -25;
1029                         yscroll = ((scrollvel * (12 - movementrate)) + (yscroll * movementrate)) / 12;
1030                         scrollvel = yscroll;
1031                         yscroll = yscroll*movementrate;
1032                         yship += yscroll;
1033                         
1034                         // Move all the rocks
1035                         for(i = 0; i < MAXROCKS; i++) {
1036                                 if(rock[i].active) {
1037                                         rock[i].x += rock[i].xvel*movementrate;
1038                                         rock[i].y += rock[i].yvel*movementrate + yscroll;
1039                                         if(rock[i].y > YSIZE || rock[i].y < -rock[i].image->h) {
1040                                                 if(rock[i].dead) {
1041                                                         rock[i].dead = 0;
1042                                                         rock[i].active = 0;
1043                                                 } else {
1044                                                         // wrap
1045                                                         rock[i].y = (YSIZE - rock[i].image->h) - rock[i].y;
1046                                                         rock[i].y += (rock[i].yvel*movementrate + yscroll) * 1.01;
1047                                                 }
1048                                         }
1049                                         if(rock[i].x < -rock[i].image->w || rock[i].x > XSIZE) {
1050                                                 rock[i].active = 0;
1051                                                 rock[i].dead = 0;
1052                                         }
1053                                 }
1054                         }
1055
1056
1057                         // BOUNCE X
1058                         if(xship<0 || xship>XSIZE-surf_ship->w) {
1059                                 // BOUNCE from left and right wall
1060                                 xship -= xvel*movementrate;
1061                                 xvel *= -0.99;
1062                         }
1063
1064                         // BOUNCE Y
1065                         if(yship<0 || yship>YSIZE-surf_ship->h) {
1066                                 // BOUNCE from top and bottom wall
1067                                 yship -= yvel;
1068                                 yvel *= -0.99;
1069                         }
1070
1071
1072                         if(draw() && state == GAMEPLAY) {
1073                                 // Play the explosion sound
1074                                 play_sound(0);
1075                                 makebangdots(xship,yship,xvel,yvel,surf_ship,30);
1076                                 if(--nships <= 0) {
1077                                         gameover = 1;
1078                                         state = GAME_OVER;
1079                                         state_timeout = 200.0;
1080                                         fadetimer = 0.0;
1081                                         faderate = movementrate;
1082                                 }
1083                                 else {
1084                                         state = DEAD_PAUSE;
1085                                         state_timeout = 20.0;
1086                                         xvel = 0;
1087                                         yvel = 0;
1088                                 }
1089                         }
1090
1091                         SDL_PumpEvents();
1092                         keystate = SDL_GetKeyState(NULL);
1093
1094                         if(state != HIGH_SCORE_ENTRY && (keystate[SDLK_q] || keystate[SDLK_ESCAPE])) {
1095                                 return 0;
1096                         }
1097
1098                         if(keystate[SDLK_SPACE] && (state == HIGH_SCORE_DISPLAY || state == TITLE_PAGE || state == DEMO)) {
1099
1100                                 for(i = 0; i<MAXROCKS; i++ ) {
1101                                         rock[i].active = 0;
1102                                         rock[i].dead = 0;
1103                                 }
1104
1105                                 rockrate = 54.0;
1106                                 rockspeed = 5.0;
1107
1108                                 nships = 4;
1109                                 score = 0;
1110
1111                                 state = GAMEPLAY;
1112                                 play_tune(1);
1113
1114                                 xvel = -1;
1115                                 gameover = 0;
1116                                 yvel = 0;
1117                                 xship = 0;
1118                                 yship = YSIZE/2;
1119
1120                         }
1121
1122                         maneuver = 0;
1123                 } else {
1124                         SDL_PumpEvents();
1125                         keystate = SDL_GetKeyState(NULL);
1126                 }
1127
1128                 if(state == GAMEPLAY) {
1129                         if(!gameover) {
1130
1131                                 if(!paused) {
1132                                         if(keystate[SDLK_UP] | keystate[SDLK_c])                { yvel -= 1.5*movementrate; maneuver |= 1<<3;}
1133                                         if(keystate[SDLK_DOWN] | keystate[SDLK_t])              { yvel += 1.5*movementrate; maneuver |= 1<<1;}
1134                                         if(keystate[SDLK_LEFT] | keystate[SDLK_h])              { xvel -= 1.5*movementrate; maneuver |= 1<<2;}
1135                                         if(keystate[SDLK_RIGHT] | keystate[SDLK_n])             { xvel += 1.5*movementrate; maneuver |= 1;}
1136                                         if(keystate[SDLK_3])            { SDL_SaveBMP(surf_screen, "snapshot.bmp"); }
1137                                 }
1138
1139                                 if(keystate[SDLK_p] | keystate[SDLK_s]) {
1140                                         if(!pausedown) {
1141                                                 paused = !paused;
1142                                                 if(paused) {
1143                                                         SDL_Rect src,dest;
1144                                                         src.w = surf_b_variations->w;
1145                                                         src.h = surf_b_variations->h;
1146                                                         dest.w = src.w;
1147                                                         dest.h = src.h;
1148                                                         dest.x = (XSIZE-src.w)/2;
1149                                                         dest.y = (YSIZE-src.h)/2;
1150                                                         SDL_BlitSurface(surf_b_variations,&src,surf_screen,&dest);
1151                                                         // Update the surface
1152                                                         SDL_Flip(surf_screen);
1153                                                 }
1154                                                 pausedown = 1;
1155                                         }
1156                                 } else {
1157                                         pausedown = 0;
1158                                 }
1159
1160                         }
1161                         else {
1162                                 paused = 0;
1163                                 pausedown = 0;
1164                         }
1165                 } else if(state == GAME_OVER) {
1166                         if(keystate[SDLK_SPACE]) {
1167                                 state_timeout = -1;
1168                         }
1169                 }
1170         }
1171 }
1172
1173 int
1174 main(int argc, char **argv) {
1175         int i, x, fullscreen;
1176
1177         fullscreen = 0;
1178         tail_plume = 0;
1179         friction = 0;
1180         sound_flag = 1;
1181         music_flag = 0;
1182
1183         while ((x = getopt(argc,argv,"efhmps")) >= 0) {
1184                 switch(x) {
1185                         case 'e': // engine
1186                                 tail_plume = 1;
1187                         break;
1188                         case 'f': // fullscreen
1189                                 fullscreen = 1;
1190                         break;
1191                         case 'h': // help
1192                                 printf("Variations on RockDodger\n"
1193                                        " -e big tail [E]ngine\n"
1194                                        " -f [F]ull screen\n"
1195                                        " -h this [H]elp message\n"
1196                                        " -m enable [M]usic\n"
1197                                        " -p original [P]hysics (friction)\n"
1198                                        " -s [S]ilent (no sound)\n");
1199                                 exit(0);
1200                         break;
1201                         case 'm': // music
1202                                 music_flag = 1;
1203                         case 'p': // physics
1204                                 friction = 1;
1205                         break;
1206                         case 's': // silent
1207                                 sound_flag = 0;
1208                                 music_flag = 0;
1209                         break;
1210                 }
1211         }
1212
1213         if(init(fullscreen)) {
1214                 printf ("ta: '%s'\n",initerror);
1215                 return 1;
1216         }
1217
1218         for(i = 0; i<MAXROCKS; i++) {
1219                 rock[i].active = 0;
1220                 rock[i].dead = 0;
1221         }
1222         rockrate = 54.0;
1223         rockspeed = 5.0;
1224         initticks = SDL_GetTicks();
1225         gameloop();
1226
1227         return 0;
1228 }