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