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