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