version 0.4
[zeRace] / zeRace.c
1 /*
2  * zeRace 0.4, 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 #include "car.h"
34 #include "tracklist.h"
35 #include "network.h"
36 #include <sys/stat.h>
37
38 /* configuration constants */
39 #define COEFF 1
40 #define DELAY 7
41 #define WIDTH 1024
42 #define HEIGHT 768
43 #define MAXRECORDKEYS 9999
44
45 /* tracklist */
46 struct _tracklist *tracklist;
47
48 /* user setup */
49 struct _config
50 {
51   char pseudo[MAXLINELENGTH];
52   char url[MAXLINELENGTH];
53   int fullscreen;
54   int sound;
55   int tire;
56   SDLKey up;
57   SDLKey down;
58   SDLKey left;
59   SDLKey right;
60   int color;
61   SDLKey boss;
62 } config = {"anonymous","",0,0,1,SDLK_UP,SDLK_DOWN,SDLK_LEFT,SDLK_RIGHT,6,SDLK_b};
63
64 /* full script for a lap */
65 struct _record
66 {
67   float x,y,angle,speed;
68   char keys[MAXRECORDKEYS];
69   int time;
70 };
71
72 /* display and all directions for the car */
73 SDL_Surface *screen;
74 SDL_Surface *cars[12][256];
75
76 /* network stuff */
77 UDPsocket udpsock=NULL;
78 UDPpacket *packet;
79 int network_speed=1;
80
81
82 /* read the user configuration file */
83 void zeRace_read_config()
84 {
85   FILE *fic;
86   if ((fic=fopen("zeRace.cfg","rt"))==NULL)
87   {
88     fprintf(stderr,"can't open config file \"zeRace.cfg\"\n");
89     return;
90   }
91   fread(&config,sizeof(struct _config),1,fic);
92   fclose(fic);
93 }
94
95
96 /* save the user configuration file */
97 void zeRace_save_config()
98 {
99   FILE *fic;
100   if ((fic=fopen("zeRace.cfg","wt"))==NULL)
101   {
102     fprintf(stderr,"can't create config file \"zeRace.cfg\"\n");
103     return;
104   }
105   fwrite(&config,sizeof(struct _config),1,fic);
106   fclose(fic);
107 }
108
109
110 /* exit the game and clean */
111 void zeRace_exit()
112 {
113   printf("quit\n");
114   if (config.sound) Mix_CloseAudio();
115   SDLNet_Quit();
116   SDL_Quit();
117   zeRace_save_config();
118   exit(0);
119 }
120
121
122 /* check for a newer version online to warn the user */
123 void zeRace_check_version()
124 {
125   IPaddress ip;
126   TCPsocket tcpsock;
127   char *request=
128     "GET /zerace/version.php HTTP/1.0\n"
129     "Host: royale.zerezo.com\n"
130     "User-Agent: zeRace " VERSION "\n"
131     "\n";
132   char response[1024],*tmp,*version;
133   int len,result;
134   
135   printf("checking version... ");
136   fflush(stdout);
137
138   if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
139   {
140     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
141     return;
142   }
143   
144   tcpsock=SDLNet_TCP_Open(&ip);
145   if (!tcpsock)
146   {
147     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
148     return;
149   }
150
151   len=strlen(request);
152   result=SDLNet_TCP_Send(tcpsock,request,len);
153   if (result<len)
154     fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
155   else
156     printf("done\n");
157   
158   len=SDLNet_TCP_Recv(tcpsock,response,1024);
159   if (len<0)
160     fprintf(stderr,"SDLNet_TCP_Recv: %s\n",SDLNet_GetError());
161   else
162   {
163     tmp=response;
164     while (*tmp++!='\0') if (strncmp(tmp,"version=",8)==0)
165     {
166       version=tmp+8;
167       while (*tmp!='\n') tmp++;
168       *tmp='\0';
169       if (strcmp(version,VERSION)>0) printf("new version available !\nhttp://royale.zerezo.com/zerace/\n");
170     }
171   }
172
173   SDLNet_TCP_Close(tcpsock);
174 }
175
176
177 /* get remote list of tracks */
178 void zeRace_update_tracks()
179 {
180   IPaddress ip;
181   TCPsocket tcpsock;
182   char *request=
183     "GET /zerace/tracks.php HTTP/1.0\n"
184     "Host: royale.zerezo.com\n"
185     "User-Agent: zeRace " VERSION "\n"
186     "\n";
187   char response[10240],*tmp;
188   int len,result;
189   FILE *fic;
190   
191   printf("checking version and updating tracks... ");
192   fflush(stdout);
193
194   if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
195   {
196     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
197     return;
198   }
199   
200   tcpsock=SDLNet_TCP_Open(&ip);
201   if (!tcpsock)
202   {
203     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
204     return;
205   }
206
207   len=strlen(request);
208   result=SDLNet_TCP_Send(tcpsock,request,len);
209   if (result<len)
210     fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
211   else
212   {
213     if ((fic=fopen("tracks/list.txt","wt"))==NULL)
214     {
215       fprintf(stderr,"can't create track list\n");
216       zeRace_exit();
217     }
218     len=SDLNet_TCP_Recv(tcpsock,response,10240);
219     tmp=response;
220     while (*tmp!='\n' || *(tmp+1)!='\r') tmp++;
221     tmp+=3;
222     fwrite(tmp,1,len+response-tmp,fic);
223     fclose(fic);
224     printf("done\n");
225   }
226   
227   SDLNet_TCP_Close(tcpsock);
228 }
229
230
231 /* download the file if it is missing */
232 void zeRace_download_file(char *file)
233 {
234   IPaddress ip;
235   TCPsocket tcpsock;
236   char request[1024];
237   char response[10240],*tmp;
238   int len,result;
239   FILE *fic;
240   struct stat buf;
241
242   if (stat(file,&buf)<0)
243   {
244     printf("downloading file \"%s\" : ",file);
245     fflush(stdout);
246     
247     if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
248     {
249       fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
250       return;
251     }
252     
253     tcpsock=SDLNet_TCP_Open(&ip);
254     if (!tcpsock)
255     {
256       fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
257       return;
258     }
259   
260     sprintf(request,
261       "GET /zerace/%s HTTP/1.0\n"
262       "Host: royale.zerezo.com\n"
263       "User-Agent: zeRace " VERSION "\n"
264       "\n",file);
265     len=strlen(request);
266     result=SDLNet_TCP_Send(tcpsock,request,len);
267     if (result<len)
268       fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
269     else
270     {
271       if ((fic=fopen(file,"wt"))==NULL)
272       {
273         fprintf(stderr,"can't create \"%s\" file\n",file);
274         zeRace_exit();
275       }
276       len=SDLNet_TCP_Recv(tcpsock,response,10240);
277       tmp=response;
278       while (*tmp!='\n' || *(tmp+1)!='\r') tmp++;
279       tmp+=3;
280       fwrite(tmp,1,len+response-tmp,fic);
281       while ((len=SDLNet_TCP_Recv(tcpsock,response,10240))) fwrite(response,1,len,fic);
282       fclose(fic);
283       printf("done\n");
284     }
285     
286     SDLNet_TCP_Close(tcpsock);
287   }
288 }
289
290
291 /* load the car sprite and rotate it for every angles */
292 void zeRace_generate_cars()
293 {
294   int i,j;
295   SDL_Surface *car;
296   char temp[20]="sprites/carX.png";
297   for (i=0;i<12;i++)
298   {
299     temp[11]='A'+i;
300     /* load the car sprite */
301     car=IMG_Load(temp);
302     /* and rotate it for all available angles */
303     for (j=0;j<256;j++)
304     {
305       float x,y;
306       float tcos,tsin;
307       if ((cars[i][j]=SDL_CreateRGBSurface(SDL_SWSURFACE,30,30,32,0xff<<RSHIFT,0xff<<GSHIFT,0xff<<BSHIFT,0xff<<ASHIFT))==NULL)
308       {
309         fprintf(stderr,"CreateRGBSurface failed: %s\n",SDL_GetError());
310         zeRace_exit();
311       };
312       tcos=cos(2*M_PI*j/256);
313       tsin=sin(2*M_PI*j/256);
314       for (x=0;x<cars[i][j]->w;x++) for (y=0;y<cars[i][j]->h;y++)
315       {
316         int x2,y2;
317         x2=(x-cars[i][j]->w/2.0)*tcos+(y-cars[i][j]->h/2.0)*tsin+car->w/2.0;
318         y2=(x-cars[i][j]->w/2.0)*tsin-(y-cars[i][j]->h/2.0)*tcos+car->h/2.0;
319         if (x2>0 && x2<car->w && y2>0 && y2<car->h)
320           putpixel(cars[i][j],x,y,getpixel(car,x2,y2));
321       }
322     }
323     SDL_FreeSurface(car);
324   }
325 }
326
327
328 /* initialize the game */
329 void zeRace_init()
330 {
331   int flags;
332   struct _tracklist *loopcheck;
333   
334   /* do a clean exit in case of emergency */
335   signal(SIGINT,zeRace_exit);
336   signal(SIGTERM,zeRace_exit);
337
338   /* read the user configuration file */
339   zeRace_read_config();
340
341   /* check for a newer available version */
342   zeRace_check_version();
343   
344   /* update the list of tracks */
345   zeRace_update_tracks();
346   
347   /* get the list of local tracks */
348   if (!zeRace_get_tracks(&tracklist)) zeRace_exit();
349   
350   /* download missing files */
351   loopcheck=tracklist;
352   while (tracklist->next!=loopcheck)
353   {
354     zeRace_download_file(tracklist->full);
355     zeRace_download_file(tracklist->function);
356     tracklist=tracklist->next;
357   }
358   
359   srand(time(NULL));
360   
361   if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0)
362   {
363     fprintf(stderr,"could not initialize SDL : %s\n",SDL_GetError());
364     zeRace_exit();
365   }
366   atexit(SDL_Quit);
367   
368   if (SDLNet_Init()==-1)
369   {
370     fprintf(stderr,"could not initialize SDLNet : %s\n",SDLNet_GetError());
371     zeRace_exit();
372   }
373
374   packet=SDLNet_AllocPacket(1024);
375   if (!packet)
376   {
377     fprintf(stderr,"SDLNet_AllocPacket: %s\n",SDLNet_GetError());
378     zeRace_exit();
379   }
380
381   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
382
383   flags=SDL_HWSURFACE|SDL_ANYFORMAT;
384   if (config.fullscreen) flags|=SDL_FULLSCREEN;
385   
386   if ((screen=SDL_SetVideoMode(WIDTH,HEIGHT,32,flags))==NULL)
387   {
388     fprintf(stderr,"could not create a surface : %s\n",SDL_GetError());
389     zeRace_exit();
390   }
391   
392   SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"),NULL);
393   SDL_WM_SetCaption("zeRace " VERSION,"zeRace " VERSION);
394   SDL_ShowCursor(SDL_DISABLE);
395   
396   if (config.sound) if (Mix_OpenAudio(44100,MIX_DEFAULT_FORMAT,2,512)<0)
397   {
398     fprintf(stderr,"Mix_OpenAudio error\n");
399     zeRace_exit();
400   }
401
402   /* pre-calculate car sprites */
403   zeRace_generate_cars();
404 }
405
406
407 /* send the best time for this race to the web server */
408 void zeRace_send_time(struct _record record)
409 {
410   IPaddress ip;
411   TCPsocket tcpsock;
412   char *temp;
413   char *msg1=
414     "POST /zerace/time.php HTTP/1.0\n"
415     "Host: royale.zerezo.com\n"
416     "User-Agent: zeRace " VERSION "\n"
417     "Content-Type: application/x-www-form-urlencoded\n"
418     "Content-Length: 99999\n"
419     "\n"
420     "pseudo=";
421   char *msg2="&url=";
422   char *msg3="&track=";
423   char *msg4="&btime=";
424   char *msg5="&x=";
425   char *msg6="&y=";
426   char *msg7="&speed=";
427   char *msg8="&angle=";
428   char *msg9="&bkeys=";
429   int len,result;
430   
431   /* if the best time is small enought to save all keys, send it */
432   if (record.time>=MAXRECORDKEYS) return;
433
434   printf("sending time... ");
435   fflush(stdout);
436
437   if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
438   {
439     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
440     return;
441   }
442   
443   tcpsock=SDLNet_TCP_Open(&ip);
444   if (!tcpsock)
445   {
446     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
447     return;
448   }
449
450   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(record.keys)+100);
451   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,record.time,msg5,record.x,msg6,record.y,msg7,record.speed,msg8,record.angle,msg9,record.keys);
452   
453   len=strlen(temp);
454   result=SDLNet_TCP_Send(tcpsock,temp,len);
455   if (result<len)
456     fprintf(stderr,"SDLNet_TCP_Send: %s\n", SDLNet_GetError());
457   else
458     printf("done\n");
459
460   SDLNet_TCP_Close(tcpsock);
461 }
462
463
464 /* print a time for a lap */
465 void print_time(int x,int y,int time)
466 {
467   char temp[20];
468   int q,r;
469   time*=DELAY;
470   q=(time/1000);
471   r=(time%1000);
472   if (q>100) return;
473   temp[0]=q/10+'0';
474   temp[1]=q%10+'0';
475   temp[2]='"';
476   temp[3]=r/100+'0';
477   temp[4]=r%100/10+'0';
478   temp[5]=r%10+'0';
479   temp[6]='\0';
480   print(screen,x,y,temp);
481 }
482
483
484 /* launch a new race */
485 void zeRace_launch(int alltime,int go)
486 {
487   SDL_Surface *cir,*fun;
488   SDL_Rect pos;
489   SDL_Event event;
490   int ku=0,kd=0,kl=0,kr=0,i;
491   struct _car car;
492   struct _record current,best;
493   Mix_Music *light,*engine,*crash,*slide;
494   int lastsound_time=-999,lastsound=0;
495   int delay=DELAY;
496   int lastack=alltime;
497   struct _record net;
498   struct _car oldnetpos[MAX_CLIENTS],newnetpos[MAX_CLIENTS];
499     
500   /* free memory */
501   void free_mem()
502   {
503     SDL_FreeSurface(cir);
504     SDL_FreeSurface(fun);
505     if (config.sound)
506     {
507       Mix_FreeMusic(light);
508       Mix_FreeMusic(engine);
509       Mix_FreeMusic(crash);
510       Mix_FreeMusic(slide);
511     }
512     return;
513   }
514
515   cir=IMG_Load(tracklist->full);
516   fun=IMG_Load(tracklist->function);
517   
518   current.speed=car.speed=0;
519   current.angle=car.angle=tracklist->a*2*M_PI/360;
520   current.x=car.ox=car.x=tracklist->x;
521   current.y=car.oy=car.y=tracklist->y;
522   car.lastcheck=0;
523   car.w=cars[0][0]->w;
524   car.h=cars[0][0]->h;
525   current.time=0;
526   best.time=MAXRECORDKEYS;
527   net.time=0;
528   memset(oldnetpos,0,MAX_CLIENTS*sizeof(struct _car));
529   memset(newnetpos,0,MAX_CLIENTS*sizeof(struct _car));
530
531   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")))
532   {
533     fprintf(stderr,"Mix_LoadMUS error\n");
534     zeRace_exit();
535   }
536   
537   /* startup countdown */
538   for (i=4;i>=-1;i--)
539   {
540     char startup[15]="sprites/?.png";
541     SDL_Surface *temp;
542     startup[8]='0'+i;
543     pos.x=car.x-car.w/2;
544     pos.y=car.y-car.h/2;
545     SDL_BlitSurface(cir,NULL,screen,NULL);
546     SDL_BlitSurface(cars[config.color][(unsigned char)(256*car.angle/2.0/M_PI)%256],NULL,screen,&pos);
547     if (i!=4 && i!=-1)
548     {
549       temp=IMG_Load(startup);
550       pos.x=screen->w/2-temp->w/2;
551       pos.y=screen->h/2-temp->h/2;
552       SDL_BlitSurface(temp,NULL,screen,&pos);
553       SDL_FreeSurface(temp);
554     }
555     if (config.sound) if (i!=4) Mix_PlayMusic(light,1);
556     SDL_Flip(screen);
557     if (!go) break;
558     if (i!=-1) SDL_Delay(1000);
559   }
560   
561   /* main loop */
562   for (;;)
563   {
564     /* look for user interaction */
565     while (SDL_PollEvent(&event))
566     {
567       switch (event.type)
568       {
569         case SDL_QUIT:
570           zeRace_exit();
571           break;
572         case SDL_KEYDOWN:
573           switch (event.key.keysym.sym)
574           {
575             case SDLK_ESCAPE:
576               if (udpsock)
577               {
578                 print(screen,WIDTH/2-strlen("Disconnecting !")*5,HEIGHT/2-10,"Disconnecting !");
579                 strcpy(packet->data,"disconnect");
580                 packet->len=strlen(packet->data)+1;
581                 SDLNet_UDP_Send(udpsock,-1,packet);
582                 SDL_Flip(screen);
583               }
584               zeRace_send_time(best);
585               free_mem();
586               return;  
587             default:
588               i=event.key.keysym.sym;
589               if (i==config.up) ku=1;
590               if (i==config.down) kd=1;
591               if (i==config.left) kl=1;
592               if (i==config.right) kr=1;
593               if (i==config.boss)
594               {
595                 /* display the boss screen */
596                 SDL_Surface *boss;
597                 boss=IMG_Load("sprites/boss.png");
598                 SDL_BlitSurface(boss,NULL,screen,NULL);
599                 SDL_FreeSurface(boss);
600                 SDL_Flip(screen);
601                 /* and wait until the user press another key */
602                 for (;;) if (SDL_PollEvent(&event)) { if (event.type==SDL_KEYDOWN) break; } else SDL_Delay(10);
603                 SDL_BlitSurface(cir,NULL,screen,NULL);
604                 SDL_Flip(screen);
605               }
606               break;
607           }
608           break;
609         case SDL_KEYUP:
610           i=event.key.keysym.sym;
611           if (i==config.up) ku=0;
612           if (i==config.down) kd=0;
613           if (i==config.left) kl=0;
614           if (i==config.right) kr=0;
615           break;
616       }
617     }
618     
619     /* save pressed keys to validate best time */
620     if (current.time<MAXRECORDKEYS) current.keys[current.time]=(ku<<3 | kd<<2 | kl<<1 | kr)+'A';
621     current.keys[current.time+1]='\0';
622     /* and to send to server if needed */
623     if (udpsock)
624     {
625       net.keys[net.time]=(ku<<3 | kd<<2 | kl<<1 | kr)+'A';
626       net.keys[net.time+1]='\0';
627     }
628
629     delay=DELAY;
630     /* if we are in network mode */
631     if (udpsock!=NULL)
632     {
633       char *tmp;
634       while (SDLNet_UDP_Recv(udpsock,packet)) if (strcmp(packet->data,"positions")==0)
635       {
636         int servertime,clienttime,nb;
637         servertime=SDLNet_Read32(packet->data+strlen("positions")+1);
638         clienttime=SDLNet_Read32(packet->data+strlen("positions")+1+4);
639         nb=SDLNet_Read16(packet->data+strlen("positions")+1+4+4);
640         if (clienttime>lastack)
641         {
642           memcpy(net.keys,net.keys+clienttime-lastack,net.time+1);
643           net.time-=clienttime-lastack;
644           if (clienttime>servertime+5) delay+=DELAY;
645           if (clienttime<servertime-5) delay-=DELAY;
646           if (delay<0) delay=0;
647           for (i=0;i<MAX_CLIENTS;i++) newnetpos[i].w=0;
648           for (i=0;i<nb;i++)
649           {
650             newnetpos[i].w=newnetpos[i].h=30;
651             newnetpos[i].x=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*8);
652             newnetpos[i].y=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*8+2);
653             newnetpos[i].angle=(float)SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*8+2+2)/1000;
654             newnetpos[i].color=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*8+2+2+2);
655           }
656           lastack=clienttime;
657         }
658       }
659       else /* end of this network race */
660       {
661         zeRace_send_time(best);
662         free_mem();
663         return;
664       }
665       if (strlen(net.keys)!=0)
666       {
667         tmp=packet->data;
668         strcpy(tmp,"keys");
669         tmp+=strlen(tmp)+1;
670         SDLNet_Write32(lastack,tmp);
671         tmp+=4;
672         strcpy(tmp,net.keys);
673         tmp+=strlen(tmp)+1;
674         packet->len=(void *)tmp-(void *)packet->data+10;
675         if (net.time%network_speed==0) if (!SDLNet_UDP_Send(udpsock,-1,packet))
676         {
677           fprintf(stderr,"SDLNet_UDP_Send: %s\n",SDLNet_GetError());
678           exit(2);
679         };
680       }
681     }
682
683     /* clear the old network position */
684     if (udpsock) for (i=0;i<MAX_CLIENTS;i++) if (oldnetpos[i].w)
685     {
686       pos.x=oldnetpos[i].x-car.w/2;
687       pos.y=oldnetpos[i].y-car.h/2;
688       pos.w=car.w;
689       pos.h=car.h;
690       SDL_BlitSurface(cir,&pos,screen,&pos);
691     }
692     
693     /* clear the old position */
694     pos.x=car.ox-car.w/2;
695     pos.y=car.oy-car.h/2;
696     pos.w=car.w;
697     pos.h=car.h;
698     SDL_BlitSurface(cir,&pos,screen,&pos);
699     
700     /* display the network car at the new position */
701     if (udpsock) for (i=0;i<MAX_CLIENTS;i++) if (newnetpos[i].w)
702     {
703       pos.x=newnetpos[i].x-car.w/2;
704       pos.y=newnetpos[i].y-car.h/2;
705       pos.w=car.w;
706       pos.h=car.h;
707       SDL_BlitSurface(cars[newnetpos[i].color][(unsigned char)(256*newnetpos[i].angle/2.0/M_PI)%256],NULL,screen,&pos);
708     }
709     
710     /* display the car at the new position */
711     pos.x=car.x-car.w/2;
712     pos.y=car.y-car.h/2;
713     pos.w=car.w;
714     pos.h=car.h;
715     SDL_BlitSurface(cars[config.color][(unsigned char)(256*car.angle/2.0/M_PI)%256],NULL,screen,&pos);
716     
717     /* update display */
718     if (udpsock)
719     {
720       for (i=0;i<MAX_CLIENTS;i++)
721       {
722         if (oldnetpos[i].w) SDL_UpdateRect(screen,oldnetpos[i].x-car.w/2,oldnetpos[i].y-car.h/2,car.w,car.h);
723         if (newnetpos[i].w) SDL_UpdateRect(screen,newnetpos[i].x-car.w/2,newnetpos[i].y-car.h/2,car.w,car.h);
724       }
725       memcpy(oldnetpos,newnetpos,MAX_CLIENTS*sizeof(struct _car));
726     }
727     SDL_UpdateRect(screen,car.ox-car.w/2,car.oy-car.h/2,car.w,car.h);
728     SDL_UpdateRect(screen,car.x-car.w/2,car.y-car.h/2,car.w,car.h);
729
730     memcpy(oldnetpos,newnetpos,MAX_CLIENTS*sizeof(struct _car));
731     
732     /* move the car */
733     move_car(&car,(ku<<3 | kd<<2 | kl<<1 | kr),fun);
734
735     /* play engine sound if no sound is currently playing */
736     if (lastsound_time+100<alltime)
737     {
738       lastsound=0;
739       lastsound_time=alltime;
740       if (config.sound) Mix_PlayMusic(engine,1);
741     }
742     
743     /* if the car is fast or braking, it slides */
744     if ((kd && car.speed>0.5) || (car.speed>2.0 && !ku))
745     {
746       /* if the only sound is the engine, play the slide sound */
747       if (lastsound_time+100<alltime || lastsound<1)
748       {
749         lastsound=1;
750         lastsound_time=alltime;
751         if (config.sound) Mix_PlayMusic(slide,1)==-1;
752       }
753
754       /* display tires slide */
755       if (config.tire)
756       {
757         putpixel(cir,car.x+cos(car.angle)*car.w/3-sin(car.angle)*4,car.y+sin(car.angle)*car.h/3+cos(car.angle)*4,0);
758         putpixel(cir,car.x+cos(car.angle)*car.w/3+sin(car.angle)*4,car.y+sin(car.angle)*car.h/3-cos(car.angle)*4,0);
759         /* if we are braking the slide is larger */
760         if (kd)
761         {
762           putpixel(cir,car.x+cos(car.angle)*car.w/3-sin(car.angle)*3,car.y+sin(car.angle)*car.h/3+cos(car.angle)*3,0);
763           putpixel(cir,car.x+cos(car.angle)*car.w/3+sin(car.angle)*3,car.y+sin(car.angle)*car.h/3-cos(car.angle)*3,0);
764         }
765       }
766     }
767    
768     /* if we crashed */
769     if (car.crashflag && (lastsound_time+100<alltime || lastsound<2))
770     {
771       lastsound=2;
772       lastsound_time=alltime;
773       if (config.sound) Mix_PlayMusic(crash,1)==-1;
774     }
775    
776     /* game time */
777     current.time++;
778     net.time++;
779     if (udpsock && net.time>MAX_LAG)
780     {
781       print(screen,WIDTH/2-strlen("Timeout !")*5,HEIGHT/2-10,"Timeout !");
782       SDL_Flip(screen);
783       zeRace_send_time(best);
784       free_mem();
785       return;
786     }
787     alltime++;
788
789     /* if we completed a lap */
790     if (car.lapflag)
791     {
792       printf("time = %d\"%d\n",current.time*DELAY/1000,current.time*DELAY%1000);
793       print(screen,0,0,"Last lap : ");
794       print_time(110,0,current.time);
795       SDL_UpdateRect(screen,0,0,170,19);
796       /* if it is the first turn of the best turn, save it */
797       if (best.time==-1 || current.time<best.time)
798         memcpy(&best,&current,sizeof(struct _record));
799       /* reset turn variables */
800       current.time=0;
801       current.x=car.x;
802       current.y=car.y;
803       current.angle=car.angle;
804       current.speed=car.speed;
805     }
806     
807     /* let the system breath */
808     SDL_Delay(delay);
809   }
810 }
811
812
813 /* display a random splash screen at startup */
814 void zeRace_splash()
815 {
816   SDL_Surface *splash;
817   SDL_Rect pos;
818   char temp[20]="splashs/0.jpg";
819   
820   SDL_FillRect(screen,NULL,0x000000);
821   temp[8]=rand()%3+'1';
822   splash=IMG_Load(temp);
823   pos.x=screen->w/2-splash->w/2-1;
824   pos.w=splash->w+2;
825   pos.y=screen->h/2-splash->h/2-1;
826   pos.h=splash->h+2;
827   SDL_FillRect(screen,&pos,0xffffff);
828   pos.x=screen->w/2-splash->w/2;
829   pos.y=screen->h/2-splash->h/2;
830   SDL_BlitSurface(splash,NULL,screen,&pos);
831   print(screen,screen->w/2-strlen("zeRace " VERSION)*5,screen->h/2-splash->h/2-20,"zeRace " VERSION);
832   SDL_FreeSurface(splash);
833   SDL_Flip(screen);
834   SDL_Delay(2000);
835 }
836
837
838 /* menu loop to select track */
839 void zeRace_local()
840 {
841   SDL_Event event;
842   
843   void update()
844   {
845     SDL_Surface *full,*preview;
846     SDL_Rect pos;
847     SDL_FillRect(screen,NULL,0x000000);
848     print(screen,WIDTH/2-28*5,HEIGHT/6,"* Please choose your race *");
849     print(screen,WIDTH/2-strlen(tracklist->title)*5,5*HEIGHT/6-20,tracklist->title);
850     print(screen,WIDTH/2-(strlen(tracklist->author)+strlen("Author : "))*5,5*HEIGHT/6+0,"Author : ");
851     print(screen,WIDTH/2-(strlen(tracklist->author)-strlen("Author : "))*5,5*HEIGHT/6+0,tracklist->author);
852     print(screen,WIDTH/2-( strlen("Version : ")+strlen(tracklist->version))*5,5*HEIGHT/6+20,"Version : ");
853     print(screen,WIDTH/2-(-strlen("Version : ")+strlen(tracklist->version))*5,5*HEIGHT/6+20,tracklist->version);
854     print(screen,WIDTH/2-( strlen("Best time : ")+6+strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40,"Best time : ");
855     print_time  (WIDTH/2-(-strlen("Best time : ")+6+strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40,tracklist->best_time);
856     print(screen,WIDTH/2-(-strlen("Best time : ")-6+strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40," by ");
857     print(screen,WIDTH/2-(-strlen("Best time : ")-6-strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40,tracklist->best_pseudo);
858     full=IMG_Load(tracklist->full);
859     preview=(SDL_Surface *)zoomSurface(full,0.5,0.5,1);
860     SDL_FreeSurface(full);
861     pos.x=WIDTH/2-preview->w/2-1;
862     pos.w=preview->w+2;
863     pos.y=screen->h/2-preview->h/2-1;
864     pos.h=preview->h+2;
865     SDL_FillRect(screen,&pos,0xffffff);
866     pos.x=WIDTH/2-preview->w/2;
867     pos.y=screen->h/2-preview->h/2;
868     SDL_BlitSurface(preview,NULL,screen,&pos);
869     SDL_FreeSurface(preview);
870     SDL_Flip(screen);
871   }
872   
873   update();
874   for (;;)
875   {
876     while (SDL_PollEvent(&event))
877     {
878       switch (event.type)
879       {
880         case SDL_QUIT:
881           zeRace_exit();
882           break;
883         case SDL_KEYDOWN:
884           switch (event.key.keysym.sym)
885           {
886             case SDLK_ESCAPE:
887               return;
888             case SDLK_RETURN:
889             case SDLK_SPACE:
890               zeRace_launch(0,1);
891               update();
892               break;
893             case SDLK_LEFT:
894               tracklist=tracklist->next;
895               update();
896               break;
897             case SDLK_RIGHT:
898               tracklist=tracklist->prev;
899               update();
900               break;
901             default:
902               break;
903           }
904           break;
905       }
906     }
907     SDL_Delay(10);
908   }
909 }
910
911
912 /* top 10 screen */
913 void zeRace_top10(char *buf)
914 {
915   int i,nb,tmp;
916   SDL_Rect pos;
917   SDL_FillRect(screen,NULL,0x000000);
918   nb=SDLNet_Read16(buf);
919   buf+=2;
920   print(screen,WIDTH/2-16*5,HEIGHT/14,"* Race results *");
921   for (i=0;i<nb;i++)
922   {
923     print(screen,150,(i+3)*HEIGHT/14,buf);
924     buf+=strlen(buf)+1;
925     tmp=SDLNet_Read16(buf);
926     buf+=2;
927     pos.x=110;
928     pos.y=(i+3)*HEIGHT/14-8;
929     SDL_BlitSurface(cars[tmp][0],NULL,screen,&pos);
930   }
931   SDL_Flip(screen);
932   SDL_Delay(5000);
933 }
934
935
936 /* connect to a server */
937 void zeRace_connect(char *host,int port)
938 {
939   char *tmp;
940   int lag=0;
941   udpsock=SDLNet_UDP_Open(0);
942   if (udpsock==NULL)
943   {
944     fprintf(stderr,"SDLNet_UDP_Open: %s\n",SDLNet_GetError());
945     zeRace_exit();
946   }
947   SDLNet_ResolveHost(&packet->address,host,port);
948   tmp=packet->data;
949   strcpy(tmp,"connect");
950   tmp+=strlen(tmp)+1;
951   strcpy(tmp,config.pseudo);
952   tmp+=strlen(tmp)+1;
953   SDLNet_Write16(config.color,tmp);
954   tmp+=2;
955   packet->len=(void *)tmp-(void *)packet->data;
956   SDLNet_UDP_Send(udpsock,-1,packet);
957   /* network loop */
958   while (SDLNet_UDP_Recv(udpsock,packet) || lag<MAX_LAG)
959   {
960     tmp=packet->data;
961     if (strcmp(tmp,"track")==0)
962     {
963       struct _tracklist *loopcheck=tracklist;
964       int time;
965       char go;
966       tmp+=strlen(tmp)+1;
967       go=*tmp++;
968       printf("server asked for track : %s\n",tmp);
969       while (tracklist->next!=loopcheck) if (strcmp(tracklist->name,tmp)==0) break; else tracklist=tracklist->next;
970       if (strcmp(tracklist->name,tmp)!=0)
971       {
972         fprintf(stderr,"unknown track : %s\n",tmp);
973         zeRace_exit();
974       }
975       tmp+=strlen(tmp)+1;
976       time=SDLNet_Read32(tmp);
977       tmp+=4;
978       network_speed=SDLNet_Read32(tmp);
979       zeRace_launch(time,go);
980       if (strcmp(packet->data,"finish")==0) zeRace_top10(packet->data+strlen(packet->data)+1);
981       lag=0;
982     }
983     SDL_Delay(7);
984     lag++;
985   }
986   SDLNet_UDP_Close(udpsock);
987   udpsock=NULL;
988 }
989
990
991 /* network game */
992 void zeRace_network()
993 {
994   SDL_Event event;
995   int active=0;
996   char server[MAXLINELENGTH]="localhost";
997   char port[6]=PORT;
998   #define NETWORK_OPTIONS 4
999
1000   void update()
1001   {
1002     SDL_FillRect(screen,NULL,0x000000);
1003     print(screen,380,HEIGHT/(NETWORK_OPTIONS+4)*(3+active),">");
1004     print(screen,WIDTH/2-18*5,HEIGHT/(NETWORK_OPTIONS+4),"* Network screen *");
1005     print(screen,400,HEIGHT/(NETWORK_OPTIONS+4)*3,"Server : ");
1006     print(screen,400+10*strlen("Server : "),HEIGHT/(NETWORK_OPTIONS+4)*3,server);
1007     print(screen,400,HEIGHT/(NETWORK_OPTIONS+4)*4,"Port : ");
1008     print(screen,400+10*strlen("Port : "),HEIGHT/(NETWORK_OPTIONS+4)*4,port);
1009     print(screen,400,HEIGHT/(NETWORK_OPTIONS+4)*(NETWORK_OPTIONS+1),"Connect");
1010     print(screen,400,HEIGHT/(NETWORK_OPTIONS+4)*(NETWORK_OPTIONS+2),"Back to main menu");
1011     SDL_Flip(screen);
1012   }
1013
1014   update();
1015   for (;;)
1016   {
1017     while (SDL_PollEvent(&event))
1018     {
1019       switch (event.type)
1020       {
1021         case SDL_QUIT:
1022           zeRace_exit();
1023           break;
1024         case SDL_KEYDOWN:
1025           switch (event.key.keysym.sym)
1026           {
1027             case SDLK_ESCAPE:
1028               return;
1029             case SDLK_RETURN:
1030             case SDLK_SPACE:
1031             case SDLK_LEFT:
1032             case SDLK_RIGHT:
1033               switch (active)
1034               {
1035                 case 0: readstring(screen,400+10*strlen("Server : "),HEIGHT/(NETWORK_OPTIONS+4)*3,server,MAXLINELENGTH); break;
1036                 case 1: readstring(screen,400+10*strlen("Port : "),HEIGHT/(NETWORK_OPTIONS+4)*4,port,5); break;;
1037                 case 2:
1038                   zeRace_connect(server,atoi(port));
1039                   break;
1040                 case 3:
1041                   return;
1042               }
1043               update();
1044               break;
1045             case SDLK_UP:
1046               active--; if (active<0) active=NETWORK_OPTIONS-1;
1047               update();
1048               break;
1049             case SDLK_DOWN:
1050               active++; if (active>NETWORK_OPTIONS-1) active=0;
1051               update();
1052               break;
1053             default:
1054               break;
1055           }
1056           break;
1057       }
1058     }
1059     SDL_Delay(10);
1060   }
1061 }
1062
1063
1064 /* internet game */
1065 void zeRace_internet()
1066 {
1067   IPaddress ip;
1068   TCPsocket tcpsock;
1069   char *request=
1070     "GET /zerace/servers.php HTTP/1.0\n"
1071     "Host: royale.zerezo.com\n"
1072     "User-Agent: zeRace " VERSION "\n"
1073     "\n";
1074   char response[10240],*tmp;
1075   int len,result,i;
1076   struct _server
1077   {
1078     char name[MAXLINELENGTH];
1079     char ip[16];
1080     char port[6];
1081   } servers[10];
1082   SDL_Event event;
1083   int active=0;
1084   #define INTERNET_OPTIONS 11
1085   
1086   printf("dowloading list of servers... ");
1087   fflush(stdout);
1088
1089   if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
1090   {
1091     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
1092     return;
1093   }
1094   
1095   tcpsock=SDLNet_TCP_Open(&ip);
1096   if (!tcpsock)
1097   {
1098     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
1099     return;
1100   }
1101
1102   len=strlen(request);
1103   result=SDLNet_TCP_Send(tcpsock,request,len);
1104   if (result<len)
1105     fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
1106   else
1107   {
1108     len=SDLNet_TCP_Recv(tcpsock,response,10240);
1109     tmp=response;
1110     for (tmp=response;tmp<response+10240;tmp++) if (*tmp=='\n') *tmp='\0';
1111     tmp=response;
1112     while (*tmp!='\0' || *(tmp+1)!='\r') tmp++;
1113     tmp+=3;
1114     for (i=0;i<10;i++)
1115     {
1116       strcpy(servers[i].name,tmp);
1117       tmp+=strlen(tmp)+1;
1118       strcpy(servers[i].ip,tmp);
1119       tmp+=strlen(tmp)+1;
1120       strcpy(servers[i].port,tmp);
1121       tmp+=strlen(tmp)+1;
1122     }
1123     printf("done\n");
1124   }
1125   
1126   SDLNet_TCP_Close(tcpsock);
1127   
1128   void update()
1129   {
1130     int i;
1131     SDL_FillRect(screen,NULL,0x000000);
1132     print(screen,380,HEIGHT/(INTERNET_OPTIONS+4)*(3+active),">");
1133     print(screen,WIDTH/2-19*5,HEIGHT/(INTERNET_OPTIONS+4),"* Internet screen *");
1134     for (i=0;i<10;i++)
1135       print(screen,400,HEIGHT/(INTERNET_OPTIONS+4)*(i+3),servers[i].name);
1136     print(screen,400,HEIGHT/(INTERNET_OPTIONS+4)*(INTERNET_OPTIONS+2),"Back to main menu");
1137     SDL_Flip(screen);
1138   }
1139
1140   update();
1141   for (;;)
1142   {
1143     while (SDL_PollEvent(&event))
1144     {
1145       switch (event.type)
1146       {
1147         case SDL_QUIT:
1148           zeRace_exit();
1149           break;
1150         case SDL_KEYDOWN:
1151           switch (event.key.keysym.sym)
1152           {
1153             case SDLK_ESCAPE:
1154               return;
1155             case SDLK_RETURN:
1156             case SDLK_SPACE:
1157             case SDLK_LEFT:
1158             case SDLK_RIGHT:
1159               if (active==INTERNET_OPTIONS-1)
1160                 return;
1161               else
1162                 zeRace_connect(servers[active].ip,atoi(servers[active].port));
1163               update();
1164               break;
1165             case SDLK_UP:
1166               active--; if (active<0) active=INTERNET_OPTIONS-1;
1167               update();
1168               break;
1169             case SDLK_DOWN:
1170               active++; if (active>INTERNET_OPTIONS-1) active=0;
1171               update();
1172               break;
1173             default:
1174               break;
1175           }
1176           break;
1177       }
1178     }
1179     SDL_Delay(10);
1180   }
1181 }
1182
1183
1184 /* configuration screen */
1185 void zeRace_config()
1186 {
1187   SDL_Event event;
1188   int active=0;
1189   #define CONFIG_OPTIONS 12
1190   
1191   void update()
1192   {
1193     SDL_Rect pos;
1194     SDL_FillRect(screen,NULL,0x000000);
1195     print(screen,20,HEIGHT/(CONFIG_OPTIONS+4)*(3+active),">");
1196     print(screen,WIDTH/2-24*5,HEIGHT/(CONFIG_OPTIONS+4),"* Configuration screen *");
1197     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*3,"Pseudo : ");
1198     print(screen,40+10*strlen("Pseudo : "),HEIGHT/(CONFIG_OPTIONS+4)*3,config.pseudo);
1199     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*4,"Url : ");
1200     print(screen,40+10*strlen("Url : "),HEIGHT/(CONFIG_OPTIONS+4)*4,config.url);
1201     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*5,"Fullscreen : ");
1202     print(screen,40+10*strlen("Fullscreen : "),HEIGHT/(CONFIG_OPTIONS+4)*5,config.fullscreen?"Yes":"No");
1203     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*6,"Sound : ");
1204     print(screen,40+10*strlen("Sound : "),HEIGHT/(CONFIG_OPTIONS+4)*6,config.sound?"Yes":"No");
1205     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*7,"Tire : ");
1206     print(screen,40+10*strlen("Tire : "),HEIGHT/(CONFIG_OPTIONS+4)*7,config.tire?"Yes":"No");
1207     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*8,"Accelerate key : ");
1208     print(screen,40+10*strlen("Accelerate key : "),HEIGHT/(CONFIG_OPTIONS+4)*8,config.up?SDL_GetKeyName(config.up):"<press key>");
1209     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*9,"Brake key : ");
1210     print(screen,40+10*strlen("Brake key : "),HEIGHT/(CONFIG_OPTIONS+4)*9,config.down?SDL_GetKeyName(config.down):"<press key>");
1211     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*10,"Turn left key : ");
1212     print(screen,40+10*strlen("Turn left key : "),HEIGHT/(CONFIG_OPTIONS+4)*10,config.left?SDL_GetKeyName(config.left):"<press key>");
1213     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*11,"Turn right key : ");
1214     print(screen,40+10*strlen("Turn right key : "),HEIGHT/(CONFIG_OPTIONS+4)*11,config.right?SDL_GetKeyName(config.right):"<press key>");
1215     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*12,"Color : ");
1216     pos.x=123;
1217     pos.y=HEIGHT/(CONFIG_OPTIONS+4)*12-7;
1218     SDL_BlitSurface(cars[config.color][0],NULL,screen,&pos);
1219     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*13,"Boss key : ");
1220     print(screen,40+10*strlen("Boss key : "),HEIGHT/(CONFIG_OPTIONS+4)*13,config.boss?SDL_GetKeyName(config.boss):"<press key>");
1221     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*(CONFIG_OPTIONS+2),"Back to main menu");
1222     SDL_Flip(screen);
1223   }
1224
1225   int read_key()
1226   {
1227     for (;;)
1228     {
1229       while (SDL_PollEvent(&event)) switch (event.type)
1230       {
1231         case SDL_KEYDOWN:
1232           return event.key.keysym.sym;
1233       }
1234       SDL_Delay(10);
1235     }
1236   }
1237   
1238   update();
1239   for (;;)
1240   {
1241     while (SDL_PollEvent(&event))
1242     {
1243       switch (event.type)
1244       {
1245         case SDL_QUIT:
1246           zeRace_exit();
1247           break;
1248         case SDL_KEYDOWN:
1249           switch (event.key.keysym.sym)
1250           {
1251             case SDLK_ESCAPE:
1252               return;
1253             case SDLK_RETURN:
1254             case SDLK_SPACE:
1255             case SDLK_LEFT:
1256             case SDLK_RIGHT:
1257               switch (active)
1258               {
1259                 case 0: readstring(screen,40+10*strlen("Pseudo : "),HEIGHT/(CONFIG_OPTIONS+4)*3,config.pseudo,MAXLINELENGTH); break;
1260                 case 1: readstring(screen,40+10*strlen("Url : "),HEIGHT/(CONFIG_OPTIONS+4)*4,config.url,MAXLINELENGTH); break;;
1261                 case 2: config.fullscreen=!config.fullscreen; break;
1262                 case 3: config.sound=!config.sound; break;
1263                 case 4: config.tire=!config.tire; break;
1264                 case 5: config.up=0; update(); config.up=read_key(); break;
1265                 case 6: config.down=0; update(); config.down=read_key(); break;
1266                 case 7: config.left=0; update(); config.left=read_key(); break;
1267                 case 8: config.right=0; update(); config.right=read_key(); break;
1268                 case 9:
1269                   if (event.key.keysym.sym==SDLK_LEFT) config.color--; else config.color++;
1270                   if (config.color<0) config.color=11;
1271                   if (config.color>11) config.color=0;
1272                   break;
1273                 case 10: config.boss=0; update(); config.boss=read_key(); break;
1274                 case 11:
1275                   return;
1276               }
1277               update();
1278               break;
1279             case SDLK_UP:
1280               active--; if (active<0) active=CONFIG_OPTIONS-1;
1281               update();
1282               break;
1283             case SDLK_DOWN:
1284               active++; if (active>CONFIG_OPTIONS-1) active=0;
1285               update();
1286               break;
1287             default:
1288               break;
1289           }
1290           break;
1291       }
1292     }
1293     SDL_Delay(10);
1294   }
1295 }
1296
1297
1298 /* main menu */
1299 void zeRace_menu()
1300 {
1301   SDL_Event event;
1302   int active=0;
1303   SDL_Surface *logo;
1304   #define MENU_OPTIONS 5
1305   
1306   void update()
1307   {
1308     SDL_Rect pos;
1309     SDL_FillRect(screen,NULL,0x000000);
1310     pos.x=WIDTH/2-logo->w/2;
1311     pos.y=HEIGHT/6-logo->h/2;
1312     SDL_BlitSurface(logo,NULL,screen,&pos);
1313     print(screen,650,HEIGHT/6+logo->h/3,"version " VERSION);
1314     print(screen,420,HEIGHT/(MENU_OPTIONS+4)*(3+active),">");
1315     print(screen,440,HEIGHT/(MENU_OPTIONS+4)*3,"Local game");
1316     print(screen,440,HEIGHT/(MENU_OPTIONS+4)*4,"Network game");
1317     print(screen,440,HEIGHT/(MENU_OPTIONS+4)*5,"Internet game");
1318     print(screen,440,HEIGHT/(MENU_OPTIONS+4)*6,"Configuration");
1319     print(screen,440,HEIGHT/(MENU_OPTIONS+4)*7,"Exit game");
1320     SDL_Flip(screen);
1321   }
1322
1323   logo=IMG_Load("sprites/logo.jpg");
1324   update();
1325   for (;;)
1326   {
1327     while (SDL_PollEvent(&event))
1328     {
1329       switch (event.type)
1330       {
1331         case SDL_QUIT:
1332           zeRace_exit();
1333           break;
1334         case SDL_KEYDOWN:
1335           switch (event.key.keysym.sym)
1336           {
1337             case SDLK_ESCAPE:
1338               return;
1339             case SDLK_RETURN:
1340             case SDLK_SPACE:
1341             case SDLK_LEFT:
1342             case SDLK_RIGHT:
1343               switch (active)
1344               {
1345                 case 0: zeRace_local(); break;
1346                 case 1: zeRace_network(); break;
1347                 case 2: zeRace_internet(); break;
1348                 case 3: zeRace_config(); break;
1349                 case 4: return;
1350               }
1351               update();
1352               break;
1353             case SDLK_UP:
1354               active--; if (active<0) active=MENU_OPTIONS-1;
1355               update();
1356               break;
1357             case SDLK_DOWN:
1358               active++; if (active>MENU_OPTIONS-1) active=0;
1359               update();
1360               break;
1361             default:
1362               break;
1363           }
1364           break;
1365       }
1366     }
1367     SDL_Delay(10);
1368   }
1369 }
1370
1371
1372 /* main program */
1373 int main(int argc,char *argv[])
1374 {
1375   zeRace_init();
1376   zeRace_splash();
1377   zeRace_menu();
1378   zeRace_exit();
1379   return 0;
1380 }