version 0.2
[zeRace] / zeRace.c
1 /*
2  * zeRace 0.2, a funny retro racing game
3  * http://royale.zerezo.com/zerace/
4  * 
5  * Copyright (C) 2004  Antoine Jacquet <royale@zerezo.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your 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 #include <stdlib.h>
23 #include <time.h>
24 #include <signal.h>
25 #include <math.h>
26 #include <string.h>
27 #include <SDL.h>
28 #include <SDL_image.h>
29 #include <SDL_net.h>
30 #include <SDL_mixer.h>
31 #include <SDL_rotozoom.h>
32 #include "sdl.h"
33
34 /* configuration constants */
35 #define COEFF 1
36 #define DELAY 7
37 #define MAXLINELENGTH 80
38 #define VERSION "0.2"
39 #define WIDTH 1024
40 #define HEIGHT 768
41
42 /* tracklist : double chained list */
43 struct _tracklist
44 {
45   char *name;
46   char *title;
47   char *author;
48   char *version;
49   char *full;
50   char *function;
51   int x,y,a;
52   int best_time;
53   char *best_pseudo;
54   struct _tracklist *prev;
55   struct _tracklist *next;
56 } *tracklist=NULL;
57
58 /* user setup */
59 struct _config
60 {
61   char pseudo[MAXLINELENGTH];
62   char url[MAXLINELENGTH];
63   int fullscreen;
64   int sound;
65   int tire;
66   SDLKey up;
67   SDLKey down;
68   SDLKey left;
69   SDLKey right;
70   char color;
71 } config = {"anonymous","",0,0,1,SDLK_UP,SDLK_DOWN,SDLK_LEFT,SDLK_RIGHT,6};
72
73 /* display and all directions for the car */
74 SDL_Surface *screen;
75 SDL_Surface *cars[256];
76
77
78 /* read the user configuration file */
79 void zeRace_read_config()
80 {
81   FILE *fic;
82   if ((fic=fopen("zeRace.cfg","rt"))==NULL)
83   {
84     fprintf(stderr,"can't open config file \"zeRace.cfg\"\n");
85     return;
86   }
87   fread(&config,sizeof(struct _config),1,fic);
88   fclose(fic);
89 }
90
91
92 /* save the user configuration file */
93 void zeRace_save_config()
94 {
95   FILE *fic;
96   if ((fic=fopen("zeRace.cfg","wt"))==NULL)
97   {
98     fprintf(stderr,"can't create config file \"zeRace.cfg\"\n");
99     return;
100   }
101   fwrite(&config,sizeof(struct _config),1,fic);
102   fclose(fic);
103 }
104
105
106 /* exit the game and clean */
107 void zeRace_exit()
108 {
109   printf("quit\n");
110   if (config.sound) Mix_CloseAudio();
111   SDLNet_Quit();
112   SDL_Quit();
113   zeRace_save_config();
114   exit(0);
115 }
116
117
118 /* get available local tracks */
119 void zeRace_get_tracks()
120 {
121   FILE *fic;
122   char line[MAXLINELENGTH];
123   struct _tracklist *tmp=NULL,*first=NULL;
124
125   if ((fic=fopen("tracks/list.txt","rt"))==NULL)
126   {
127     fprintf(stderr,"can't open track list\n");
128     zeRace_exit();
129   }
130   while (!feof(fic))
131   {
132     tmp=(struct _tracklist *)malloc(sizeof(struct _tracklist));
133     if (first==NULL) first=tmp;
134     fgets(line,MAXLINELENGTH,fic);
135     tmp->name=(char *)malloc(strlen(line)+1);
136     strcpy(tmp->name,line);
137     tmp->name[strlen(tmp->name)-1]='\0';
138     fgets(line,MAXLINELENGTH,fic);
139     tmp->title=(char *)malloc(strlen(line)+1);
140     strcpy(tmp->title,line);
141     fgets(line,MAXLINELENGTH,fic);
142     tmp->author=(char *)malloc(strlen(line)+1);
143     strcpy(tmp->author,line);
144     fgets(line,MAXLINELENGTH,fic);
145     tmp->version=(char *)malloc(strlen(line)+1);
146     strcpy(tmp->version,line);
147     fgets(line,MAXLINELENGTH,fic);
148     tmp->x=atoi(line);
149     fgets(line,MAXLINELENGTH,fic);
150     tmp->y=atoi(line);
151     fgets(line,MAXLINELENGTH,fic);
152     tmp->a=atoi(line);
153     fgets(line,MAXLINELENGTH,fic);
154     tmp->best_time=atoi(line);
155     fgets(line,MAXLINELENGTH,fic);
156     tmp->best_pseudo=(char *)malloc(strlen(line)+1);
157     strcpy(tmp->best_pseudo,line);
158     tmp->full=(char *)malloc(strlen(line)+20);
159     sprintf(tmp->full,"tracks/%s.png",tmp->name);
160     tmp->function=(char *)malloc(strlen(line)+30);
161     sprintf(tmp->function,"tracks/%s_function.png",tmp->name);
162     tmp->prev=tracklist;
163     if (tmp->prev) tmp->prev->next=tmp;
164     tracklist=tmp;
165     /* skip one line */
166     fgets(line,MAXLINELENGTH,fic);
167   }
168   fclose(fic);
169   if (!tmp) { fprintf(stderr,"no circuits found !\n"); zeRace_exit(); }
170   while (tmp->prev) tmp=tmp->prev;
171   tmp->prev=tracklist;
172   tracklist->next=tmp;
173 }
174
175
176 /* check for a newer version online to warn the user */
177 void zeRace_check_version()
178 {
179   IPaddress ip;
180   TCPsocket tcpsock;
181   char *request=
182     "GET /zerace/version.php HTTP/1.0\n"
183     "Host: royale.zerezo.com\n"
184     "User-Agent: zeRace " VERSION "\n"
185     "\n";
186   char response[1024],*tmp,*version;
187   int len,result;
188   
189   printf("checking version... ");
190   fflush(stdout);
191
192   if(SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
193   {
194     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
195     return;
196   }
197   
198   tcpsock=SDLNet_TCP_Open(&ip);
199   if(!tcpsock)
200   {
201     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
202     return;
203   }
204
205   len=strlen(request);
206   result=SDLNet_TCP_Send(tcpsock,request,len);
207   if(result<len)
208     fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
209   else
210     printf("done\n");
211   
212   len=SDLNet_TCP_Recv(tcpsock,response,1024);
213   if (len<0)
214     fprintf(stderr,"SDLNet_TCP_Recv: %s\n",SDLNet_GetError());
215   else
216   {
217     tmp=response;
218     while (*tmp++!='\0') if (strncmp(tmp,"version=",8)==0)
219     {
220       version=tmp+8;
221       while (*tmp!='\n') tmp++;
222       *tmp='\0';
223       if (strcmp(version,VERSION)>0) printf("new version available !\nhttp://royale.zerezo.com/zerace/\n");
224     }
225   }
226
227   SDLNet_TCP_Close(tcpsock);
228 }
229
230
231 /* get remote list of tracks */
232 void zeRace_update_tracks()
233 {
234   IPaddress ip;
235   TCPsocket tcpsock;
236   char *request=
237     "GET /zerace/tracks.php HTTP/1.0\n"
238     "Host: royale.zerezo.com\n"
239     "User-Agent: zeRace " VERSION "\n"
240     "\n";
241   char response[10240],*tmp;
242   int len,result;
243   FILE *fic;
244   
245   printf("checking version and updating tracks... ");
246   fflush(stdout);
247
248   if(SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
249   {
250     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
251     return;
252   }
253   
254   tcpsock=SDLNet_TCP_Open(&ip);
255   if(!tcpsock)
256   {
257     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
258     return;
259   }
260
261   len=strlen(request);
262   result=SDLNet_TCP_Send(tcpsock,request,len);
263   if(result<len)
264     fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
265   else
266   {
267     if ((fic=fopen("tracks/list.txt","wt"))==NULL)
268     {
269       fprintf(stderr,"can't create track list\n");
270       zeRace_exit();
271     }
272     len=SDLNet_TCP_Recv(tcpsock,response,10240);
273     tmp=response;
274     while (*tmp!='\n' || *(tmp+1)!='\r') tmp++;
275     tmp+=3;
276     fwrite(tmp,1,len+response-tmp,fic);
277     fclose(fic);
278     printf("done\n");
279   }
280   
281   SDLNet_TCP_Close(tcpsock);
282 }
283
284
285 /* load the car sprite and rotate it for every angles */
286 void zeRace_generate_cars()
287 {
288   int i;
289   SDL_Surface *car;
290   char temp[20]="sprites/carX.png";
291   temp[11]='A'+config.color;
292   /* load the car sprite */
293   car=IMG_Load(temp);
294   /* and rotate it for all available angles */
295   for (i=0;i<256;i++)
296   {
297     float x,y;
298     float tcos,tsin;
299     tcos=cos(2*M_PI*i/256);
300     tsin=sin(2*M_PI*i/256);
301     for (x=0;x<cars[i]->w;x++) for (y=0;y<cars[i]->h;y++)
302     {
303       int x2,y2;
304       x2=(x-cars[i]->w/2.0)*tcos+(y-cars[i]->h/2.0)*tsin+car->w/2.0;
305       y2=(x-cars[i]->w/2.0)*tsin-(y-cars[i]->h/2.0)*tcos+car->h/2.0;
306       if (x2>0 && x2<car->w && y2>0 && y2<car->h)
307         putpixel(cars[i],x,y,getpixel(car,x2,y2));
308     }
309   }
310   SDL_FreeSurface(car);
311 }
312
313
314 /* initialize the game */
315 void zeRace_init()
316 {
317   int flags,i;
318   
319   /* do a clean exit in case of emergency */
320   signal(SIGINT,zeRace_exit);
321   signal(SIGTERM,zeRace_exit);
322
323   /* read the user configuration file */
324   zeRace_read_config();
325
326   /* check for a newer available version */
327   zeRace_check_version();
328   
329   /* update the list of tracks */
330   zeRace_update_tracks();
331   
332   /* get the list of local tracks */
333   zeRace_get_tracks();
334   
335   srand(time(NULL));
336   
337   if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0)
338   {
339     fprintf(stderr,"could not initialize SDL : %s\n",SDL_GetError());
340     zeRace_exit();
341   }
342   atexit(SDL_Quit);
343   
344   if(SDLNet_Init()==-1)
345   {
346     fprintf(stderr,"could not initialize SDLNet : %s\n",SDLNet_GetError());
347     zeRace_exit();
348   }
349
350   flags=SDL_HWSURFACE|SDL_ANYFORMAT;
351   if (config.fullscreen) flags|=SDL_FULLSCREEN;
352   
353   if ((screen=SDL_SetVideoMode(WIDTH,HEIGHT,32,flags))==NULL)
354   {
355     fprintf(stderr,"could not create a surface : %s\n",SDL_GetError());
356     zeRace_exit();
357   }
358   
359   SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"),NULL);
360   SDL_WM_SetCaption("zeRace " VERSION,"zeRace " VERSION);
361   SDL_ShowCursor(SDL_DISABLE);
362   
363   if (config.sound) if (Mix_OpenAudio(44100,MIX_DEFAULT_FORMAT,2,512)<0)
364   {
365     fprintf(stderr,"Mix_OpenAudio error\n");
366     zeRace_exit();
367   }
368
369   /* allocate memory for car sprites */
370   for (i=0;i<256;i++) if ((cars[i]=SDL_CreateRGBSurface(SDL_SWSURFACE,30,30,32,0x000000ff,0x0000ff00,0x00ff0000,0xff000000))==NULL)
371   {
372     fprintf(stderr,"CreateRGBSurface failed: %s\n",SDL_GetError());
373     zeRace_exit();
374   };
375   zeRace_generate_cars();
376 }
377
378
379 /* send the best time for this race to the web server */
380 void zeRace_send_time(float x,float y,float speed,float angle,int btime,char *bkeys)
381 {
382   IPaddress ip;
383   TCPsocket tcpsock;
384   char *temp;
385   char *msg1=
386     "POST /zerace/time.php HTTP/1.0\n"
387     "Host: royale.zerezo.com\n"
388     "User-Agent: zeRace " VERSION "\n"
389     "Content-Type: application/x-www-form-urlencoded\n"
390     "Content-Length: 99999\n"
391     "\n"
392     "pseudo=";
393   char *msg2="&url=";
394   char *msg3="&track=";
395   char *msg4="&btime=";
396   char *msg5="&x=";
397   char *msg6="&y=";
398   char *msg7="&speed=";
399   char *msg8="&angle=";
400   char *msg9="&bkeys=";
401   int len,result;
402   
403   printf("sending time... ");
404   fflush(stdout);
405
406   if(SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
407   {
408     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
409     return;
410   }
411   
412   tcpsock=SDLNet_TCP_Open(&ip);
413   if(!tcpsock)
414   {
415     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
416     return;
417   }
418
419   temp=(char *)malloc(strlen(msg1)+strlen(config.pseudo)+strlen(msg2)+strlen(config.url)+strlen(msg3)+strlen(tracklist->name)+strlen(msg4)+10+strlen(msg5)+10+strlen(msg6)+10+strlen(msg7)+10+strlen(msg8)+10+strlen(msg9)+strlen(bkeys)+100);
420   sprintf(temp,"%s%s%s%s%s%s%s%d%s%f%s%f%s%f%s%f%s%s\n",msg1,config.pseudo,msg2,config.url,msg3,tracklist->name,msg4,btime,msg5,x,msg6,y,msg7,speed,msg8,angle,msg9,bkeys);
421   
422   len=strlen(temp);
423   result=SDLNet_TCP_Send(tcpsock,temp,len);
424   if(result<len)
425     fprintf(stderr,"SDLNet_TCP_Send: %s\n", SDLNet_GetError());
426   else
427     printf("done\n");
428
429   SDLNet_TCP_Close(tcpsock);
430 }
431
432
433 /* print a time for a lap */
434 void print_time(int x,int y,int time)
435 {
436   char temp[20];
437   int q,r;
438   time*=DELAY;
439   q=(time/1000);
440   r=(time%1000);
441   if (q>100) return;
442   temp[0]=q/10+'0';
443   temp[1]=q%10+'0';
444   temp[2]='"';
445   temp[3]=r/100+'0';
446   temp[4]=r%100/10+'0';
447   temp[5]=r%10+'0';
448   temp[6]='\0';
449   print(screen,x,y,temp);
450 }
451
452
453 /* launch a new race */
454 void zeRace_launch()
455 {
456   SDL_Surface *cir,*fun;
457   SDL_Rect pos,size;
458   SDL_Event event;
459   int ku=0,kd=0,kl=0,kr=0;
460   int i,time=0,lastcheck=0,btime=10000;
461   float ox,oy;
462   float x,y,angle,speed;
463   float sx,sy,sangle,sspeed;
464   float bx,by,bangle,bspeed;
465   int c,r,g,b;
466   char keys[10000];
467   char bkeys[10000];
468   Mix_Music *light,*engine,*crash,*slide;
469   int lastsound_time=-999,alltime=0,lastsound=0;
470
471   cir=IMG_Load(tracklist->full);
472   fun=IMG_Load(tracklist->function);
473   
474   sspeed=speed=0;
475   sangle=angle=(tracklist->a*2*M_PI/360);
476   sx=x=tracklist->x;
477   sy=y=tracklist->y;
478   lastcheck=0;
479   time=0;
480
481   if (config.sound) if (!(light=Mix_LoadMUS("sounds/light.wav")) || !(engine=Mix_LoadMUS("sounds/engine.wav")) || !(crash=Mix_LoadMUS("sounds/crash.wav")) || !(slide=Mix_LoadMUS("sounds/slide.wav")))
482   {
483     fprintf(stderr,"Mix_LoadMUS error\n");
484     zeRace_exit();
485   }
486   
487   /* startup countdown */
488   for (i=4;i>=-1;i--)
489   {
490     char startup[15]="sprites/?.png";
491     SDL_Surface *temp;
492     startup[8]='0'+i;
493     pos.x=x;
494     pos.y=y;
495     SDL_BlitSurface(cir,NULL,screen,NULL);
496     SDL_BlitSurface(cars[(unsigned char)(256*angle/2.0/M_PI)%256],NULL,screen,&pos);
497     if (i!=4 && i!=-1)
498     {
499       temp=IMG_Load(startup);
500       pos.x=screen->w/2-temp->w/2;
501       pos.y=screen->h/2-temp->h/2;
502       SDL_BlitSurface(temp,NULL,screen,&pos);
503       SDL_FreeSurface(temp);
504     }
505     if (config.sound) if (i!=4) Mix_PlayMusic(light,1);
506     SDL_Flip(screen);
507     if (i!=-1) SDL_Delay(1000);
508   }
509   
510   /* main loop */
511   for (;;)
512   {
513     /* clear the old position */
514     size.w=cars[0]->w;
515     size.h=cars[0]->h;
516     pos.x=x;
517     pos.y=y;
518     SDL_BlitSurface(cir,&pos,screen,&pos);
519     
520     /* save the old position and compute the new one */
521     ox=x;
522     oy=y;
523     speed*=0.995;
524     x=x-cos(angle)*speed;
525     y=y-sin(angle)*speed;
526     
527     /* collision with the border of the screen */
528     if (x<0 || x>screen->w-cars[0]->w || y<0 || y>screen->h-cars[0]->h)
529     {
530       x=ox;
531       y=oy;
532       speed=0;
533     }
534     
535     /* display the car at the new position and update display */
536     pos.x=x;
537     pos.y=y;
538     i=(unsigned char)(256*angle/2.0/M_PI)%256;
539     SDL_BlitSurface(cars[i],NULL,screen,&pos);
540     SDL_UpdateRect(screen,ox,oy,cars[i]->w,cars[i]->h);
541     SDL_UpdateRect(screen,x,y,cars[i]->w,cars[i]->h);
542     
543     /* accelerate, brake and turn depending on the pressed keys */
544     if (kl) { if (speed<0) angle+=0.01*(255-b)/255*COEFF; else angle-=0.01*(255-b)/255*COEFF; }
545     if (kr) { if (speed<0) angle-=0.01*(255-b)/255*COEFF; else angle+=0.01*(255-b)/255*COEFF; }
546     if (ku) speed+=0.01*2*COEFF;
547     if (kd) speed-=0.01*COEFF;
548
549     /* play engine sound if no sound is currently playing */
550     if (lastsound_time+100<alltime)
551     {
552       lastsound=0;
553       lastsound_time=alltime;
554       if (config.sound) Mix_PlayMusic(engine,1);
555     }
556     
557     /* if the car is fast or braking, it slides */
558     if ((kd && speed>0.5) || (speed>2.0 && !ku))
559     {
560       /* if the only sound is the engine, play the slide sound */
561       if (lastsound_time+100<alltime || lastsound<1)
562       {
563         lastsound=1;
564         lastsound_time=alltime;
565         if (config.sound) Mix_PlayMusic(slide,1)==-1;
566       }
567
568       /* display tires slide */
569       if (config.tire)
570       {
571         putpixel(cir,x+cars[i]->w/2+cos(angle)*cars[i]->w/3-sin(angle)*2,y+cars[i]->h/2+sin(angle)*cars[i]->h/3+cos(angle)*2,0);
572         putpixel(cir,x+cars[i]->w/2+cos(angle)*cars[i]->w/3+sin(angle)*5,y+cars[i]->h/2+sin(angle)*cars[i]->h/3-cos(angle)*5,0);
573         /* if we are braking the slide is larger */
574         if (kd)
575         {
576           putpixel(cir,x+cars[i]->w/2+cos(angle)*cars[i]->w/3-sin(angle)*3,y+cars[i]->h/2+sin(angle)*cars[i]->h/3+cos(angle)*3,0);
577           putpixel(cir,x+cars[i]->w/2+cos(angle)*cars[i]->w/3+sin(angle)*4,y+cars[i]->h/2+sin(angle)*cars[i]->h/3-cos(angle)*4,0);
578         }
579       }
580     }
581    
582     /* get the pixel color under the center of car in the function map */
583     c=getpixel(fun,x+cars[i]->w/2,y+cars[i]->h/2);
584     /* red layer (checkpoints) */
585     r=(c    )&0xff;
586     /* green layer (road quality) */
587     g=(c>>8 )&0xff;
588     /* blue layer (unused) */
589     b=(c>>16)&0xff;
590
591     /* if it is a wall we move back to the last position */
592     if (g==0)
593     {
594       x=ox;
595       y=oy;
596       /* play the crash sound */
597       if (lastsound_time+100<alltime || lastsound<2)
598       {
599         lastsound=2;
600         lastsound_time=alltime;
601         if (config.sound) Mix_PlayMusic(crash,1)==-1;
602       }
603     }
604     /* update the speed depending on the road quality */
605     speed-=speed*(255-g)/1000;
606    
607     /* if we are on the next checkpoint, validate it (no missing allowed) */
608     if (r/8==lastcheck+1) lastcheck++;
609     /* if we validate all and start over, we complete a turn */
610     if (r/8==0 && lastcheck==31)
611     {
612       printf("time = %d\"%d\n",time*DELAY/1000,time*DELAY%1000);
613       print(screen,0,0,"Last lap : ");
614       print_time(110,0,time);
615       SDL_UpdateRect(screen,0,0,170,19);
616       /* if it is the first turn of the best turn, save it */
617       if (btime==-1 || time<btime)
618       {
619         btime=time;
620         bx=sx;
621         by=sy;
622         bangle=sangle;
623         bspeed=sspeed;
624         keys[time]='\0';
625         memcpy(bkeys,keys,btime);
626       }
627       /* reset turn variables */
628       lastcheck=0;
629       time=0;
630       sx=x;
631       sy=y;
632       sangle=angle;
633       sspeed=speed;
634     }
635     
636     /* look for user interaction */
637     while (SDL_PollEvent(&event))
638     {
639       switch (event.type)
640       {
641         case SDL_QUIT:
642           zeRace_exit();
643           break;
644         case SDL_KEYDOWN:
645           switch (event.key.keysym.sym)
646           {
647             case SDLK_ESCAPE:
648               /* free memory */
649               Mix_FreeMusic(light);
650               Mix_FreeMusic(engine);
651               Mix_FreeMusic(crash);
652               Mix_FreeMusic(slide);
653               /* if the best time is small enought to save all keys, send it */
654               if (btime<10000) zeRace_send_time(bx,by,bspeed,bangle,btime,bkeys);
655               return;  
656             default:
657               i=event.key.keysym.sym;
658               if (i==config.up) ku=1;
659               if (i==config.down) kd=1;
660               if (i==config.left) kl=1;
661               if (i==config.right) kr=1;
662               break;
663           }
664           break;
665         case SDL_KEYUP:
666           i=event.key.keysym.sym;
667           if (i==config.up) ku=0;
668           if (i==config.down) kd=0;
669           if (i==config.left) kl=0;
670           if (i==config.right) kr=0;
671           break;
672       }
673     }
674     
675     /* let the system breath */
676     SDL_Delay(DELAY);
677     /* save pressed keys to validate best time */
678     if (time<btime) keys[time]=(ku<<3 | kd<<2 | kl<<1 | kr)+'A';
679     /* game time */
680     time++;
681     alltime++;
682   }
683 }
684
685
686 /* display a random splash screen at startup */
687 void zeRace_splash()
688 {
689   SDL_Surface *splash;
690   SDL_Rect pos;
691   char temp[20]="splashs/0.jpg";
692   
693   SDL_FillRect(screen,NULL,0x000000);
694   temp[8]=rand()%3+'1';
695   splash=IMG_Load(temp);
696   pos.x=screen->w/2-splash->w/2-1;
697   pos.w=splash->w+2;
698   pos.y=screen->h/2-splash->h/2-1;
699   pos.h=splash->h+2;
700   SDL_FillRect(screen,&pos,0xffffff);
701   pos.x=screen->w/2-splash->w/2;
702   pos.y=screen->h/2-splash->h/2;
703   SDL_BlitSurface(splash,NULL,screen,&pos);
704   print(screen,screen->w/2-strlen("zeRace " VERSION)*5,screen->h/2-splash->h/2-20,"zeRace " VERSION);
705   SDL_FreeSurface(splash);
706   SDL_Flip(screen);
707   SDL_Delay(2000);
708 }
709
710
711 /* menu loop to select track */
712 void zeRace_select_track()
713 {
714   SDL_Event event;
715   
716   void update()
717   {
718     SDL_Surface *full,*preview;
719     SDL_Rect pos;
720     SDL_FillRect(screen,NULL,0x000000);
721     print(screen,WIDTH/2-28*5,HEIGHT/6,"* Please choose your race *");
722     print(screen,WIDTH/2-strlen(tracklist->title)*5,5*HEIGHT/6-20,tracklist->title);
723     print(screen,WIDTH/2-(strlen(tracklist->author)+strlen("Author : "))*5,5*HEIGHT/6+0,"Author : ");
724     print(screen,WIDTH/2-(strlen(tracklist->author)-strlen("Author : "))*5,5*HEIGHT/6+0,tracklist->author);
725     print(screen,WIDTH/2-( strlen("Version : ")+strlen(tracklist->version))*5,5*HEIGHT/6+20,"Version : ");
726     print(screen,WIDTH/2-(-strlen("Version : ")+strlen(tracklist->version))*5,5*HEIGHT/6+20,tracklist->version);
727     print(screen,WIDTH/2-( strlen("Best time : ")+6+strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40,"Best time : ");
728     print_time  (WIDTH/2-(-strlen("Best time : ")+6+strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40,tracklist->best_time);
729     print(screen,WIDTH/2-(-strlen("Best time : ")-6+strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40," by ");
730     print(screen,WIDTH/2-(-strlen("Best time : ")-6-strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40,tracklist->best_pseudo);
731     full=IMG_Load(tracklist->full);
732     preview=(SDL_Surface *)zoomSurface(full,0.5,0.5,1);
733     SDL_FreeSurface(full);
734     pos.x=WIDTH/2-preview->w/2-1;
735     pos.w=preview->w+2;
736     pos.y=screen->h/2-preview->h/2-1;
737     pos.h=preview->h+2;
738     SDL_FillRect(screen,&pos,0xffffff);
739     pos.x=WIDTH/2-preview->w/2;
740     pos.y=screen->h/2-preview->h/2;
741     SDL_BlitSurface(preview,NULL,screen,&pos);
742     SDL_FreeSurface(preview);
743     SDL_Flip(screen);
744   }
745   
746   update();
747   for (;;)
748   {
749     while (SDL_PollEvent(&event))
750     {
751       switch (event.type)
752       {
753         case SDL_QUIT:
754           zeRace_exit();
755           break;
756         case SDL_KEYDOWN:
757           switch (event.key.keysym.sym)
758           {
759             case SDLK_ESCAPE:
760               return;
761             case SDLK_RETURN:
762             case SDLK_SPACE:
763               zeRace_launch();
764               update();
765               break;
766             case SDLK_LEFT:
767               tracklist=tracklist->next;
768               update();
769               break;
770             case SDLK_RIGHT:
771               tracklist=tracklist->prev;
772               update();
773               break;
774             default:
775               break;
776           }
777           break;
778       }
779     }
780     SDL_Delay(10);
781   }
782 }
783
784
785 /* configuration screen */
786 void zeRace_config()
787 {
788   SDL_Event event;
789   int active=0;
790   #define CONFIG_OPTIONS 11
791   
792   void update()
793   {
794     SDL_Rect pos;
795     SDL_FillRect(screen,NULL,0x000000);
796     print(screen,20,HEIGHT/(CONFIG_OPTIONS+4)*(3+active),">");
797     print(screen,WIDTH/2-24*5,HEIGHT/(CONFIG_OPTIONS+4),"* Configuration screen *");
798     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*3,"Pseudo : ");
799     print(screen,40+10*strlen("Pseudo : "),HEIGHT/(CONFIG_OPTIONS+4)*3,config.pseudo);
800     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*4,"Url : ");
801     print(screen,40+10*strlen("Url : "),HEIGHT/(CONFIG_OPTIONS+4)*4,config.url);
802     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*5,"Fullscreen : ");
803     print(screen,40+10*strlen("Fullscreen : "),HEIGHT/(CONFIG_OPTIONS+4)*5,config.fullscreen?"Yes":"No");
804     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*6,"Sound : ");
805     print(screen,40+10*strlen("Sound : "),HEIGHT/(CONFIG_OPTIONS+4)*6,config.sound?"Yes":"No");
806     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*7,"Tire : ");
807     print(screen,40+10*strlen("Tire : "),HEIGHT/(CONFIG_OPTIONS+4)*7,config.tire?"Yes":"No");
808     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*8,"Accelerate key : ");
809     print(screen,40+10*strlen("Accelerate key : "),HEIGHT/(CONFIG_OPTIONS+4)*8,config.up?SDL_GetKeyName(config.up):"<press key>");
810     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*9,"Brake key : ");
811     print(screen,40+10*strlen("Brake key : "),HEIGHT/(CONFIG_OPTIONS+4)*9,config.down?SDL_GetKeyName(config.down):"<press key>");
812     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*10,"Turn left key : ");
813     print(screen,40+10*strlen("Turn left key : "),HEIGHT/(CONFIG_OPTIONS+4)*10,config.left?SDL_GetKeyName(config.left):"<press key>");
814     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*11,"Turn right key : ");
815     print(screen,40+10*strlen("Turn right key : "),HEIGHT/(CONFIG_OPTIONS+4)*11,config.right?SDL_GetKeyName(config.right):"<press key>");
816     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*12,"Color : ");
817     pos.x=123;
818     pos.y=HEIGHT/(CONFIG_OPTIONS+4)*12-7;
819     SDL_BlitSurface(cars[0],NULL,screen,&pos);
820     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*(CONFIG_OPTIONS+2),"Back to main menu");
821     SDL_Flip(screen);
822   }
823
824   int read_key()
825   {
826     for (;;)
827     {
828       while (SDL_PollEvent(&event)) switch (event.type)
829       {
830         case SDL_KEYDOWN:
831           return event.key.keysym.sym;
832       }
833       SDL_Delay(10);
834     }
835   }
836   
837   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
838   update();
839   for (;;)
840   {
841     while (SDL_PollEvent(&event))
842     {
843       switch (event.type)
844       {
845         case SDL_QUIT:
846           zeRace_exit();
847           break;
848         case SDL_KEYDOWN:
849           switch (event.key.keysym.sym)
850           {
851             case SDLK_ESCAPE:
852               SDL_EnableKeyRepeat(0,0);
853               return;
854             case SDLK_RETURN:
855             case SDLK_SPACE:
856             case SDLK_LEFT:
857             case SDLK_RIGHT:
858               switch (active)
859               {
860                 case 0: readstring(screen,40+10*strlen("Pseudo : "),HEIGHT/(CONFIG_OPTIONS+4)*3,config.pseudo,MAXLINELENGTH); break;
861                 case 1: readstring(screen,40+10*strlen("Url : "),HEIGHT/(CONFIG_OPTIONS+4)*4,config.url,MAXLINELENGTH); break;;
862                 case 2: config.fullscreen=!config.fullscreen; break;
863                 case 3: config.sound=!config.sound; break;
864                 case 4: config.tire=!config.tire; break;
865                 case 5: config.up=0; update(); config.up=read_key(); break;
866                 case 6: config.down=0; update(); config.down=read_key(); break;
867                 case 7: config.left=0; update(); config.left=read_key(); break;
868                 case 8: config.right=0; update(); config.right=read_key(); break;
869                 case 9:
870                   if (event.key.keysym.sym==SDLK_LEFT) config.color--; else config.color++;
871                   if (config.color<0) config.color=11;
872                   if (config.color>11) config.color=0;
873                   zeRace_generate_cars();
874                   break;
875                 case 10:
876                   SDL_EnableKeyRepeat(0,0);
877                   return;
878               }
879               update();
880               break;
881             case SDLK_UP:
882               active--; if (active<0) active=CONFIG_OPTIONS-1;
883               update();
884               break;
885             case SDLK_DOWN:
886               active++; if (active>CONFIG_OPTIONS-1) active=0;
887               update();
888               break;
889             default:
890               break;
891           }
892           break;
893       }
894     }
895     SDL_Delay(10);
896   }
897 }
898
899
900 /* main menu */
901 void zeRace_menu()
902 {
903   SDL_Event event;
904   int active=0;
905   SDL_Surface *logo;
906   #define MENU_OPTIONS 3
907   
908   void update()
909   {
910     SDL_Rect pos;
911     SDL_FillRect(screen,NULL,0x000000);
912     pos.x=WIDTH/2-logo->w/2;
913     pos.y=HEIGHT/6-logo->h/2;
914     SDL_BlitSurface(logo,NULL,screen,&pos);
915     print(screen,650,HEIGHT/(MENU_OPTIONS+4)*2,"version " VERSION);
916     print(screen,420,HEIGHT/(MENU_OPTIONS+4)*(3+active),">");
917     print(screen,440,HEIGHT/(MENU_OPTIONS+4)*3,"Local game");
918     /* print(screen,440,HEIGHT/(MENU_OPTIONS+4)*4,"Network game"); */
919     print(screen,440,HEIGHT/(MENU_OPTIONS+4)*4,"Configuration");
920     print(screen,440,HEIGHT/(MENU_OPTIONS+4)*5,"Exit game");
921     SDL_Flip(screen);
922   }
923
924   logo=IMG_Load("sprites/logo.jpg");
925   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
926   update();
927   for (;;)
928   {
929     while (SDL_PollEvent(&event))
930     {
931       switch (event.type)
932       {
933         case SDL_QUIT:
934           zeRace_exit();
935           break;
936         case SDL_KEYDOWN:
937           switch (event.key.keysym.sym)
938           {
939             case SDLK_ESCAPE:
940               SDL_EnableKeyRepeat(0,0);
941               return;
942             case SDLK_RETURN:
943             case SDLK_SPACE:
944             case SDLK_LEFT:
945             case SDLK_RIGHT:
946               switch (active)
947               {
948                 case 0: zeRace_select_track(); break;
949                 case 1: zeRace_config(); break;
950                 case 2: return;
951               }
952               update();
953               break;
954             case SDLK_UP:
955               active--; if (active<0) active=MENU_OPTIONS-1;
956               update();
957               break;
958             case SDLK_DOWN:
959               active++; if (active>MENU_OPTIONS-1) active=0;
960               update();
961               break;
962             default:
963               break;
964           }
965           break;
966       }
967     }
968     SDL_Delay(10);
969   }
970 }
971
972
973 /* main program */
974 int main(int argc,char *argv[])
975 {
976   zeRace_init();
977   zeRace_splash();
978   zeRace_menu();
979   zeRace_exit();
980   return 0;
981 }