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