version 0.7
[zeRace] / zeRace.c
index c88d6cf..9277dd3 100644 (file)
--- a/zeRace.c
+++ b/zeRace.c
@@ -1,5 +1,5 @@
 /*
- * zeRace 0.3, a funny retro racing game
+ * zeRace 0.7, a funny retro racing game
  * http://royale.zerezo.com/zerace/
  * 
  * Copyright (C) 2004  Antoine Jacquet <royale@zerezo.com>
@@ -33,6 +33,7 @@
 #include "car.h"
 #include "tracklist.h"
 #include "network.h"
+#include <sys/stat.h>
 
 /* configuration constants */
 #define COEFF 1
 #define WIDTH 1024
 #define HEIGHT 768
 #define MAXRECORDKEYS 9999
+#define NB_CARS 16
+
+/* some usefull colors */
+#define C_BLACK SDL_MapRGB(screen->format,0,0,0)
+#define C_WHITE SDL_MapRGB(screen->format,255,255,255)
+#define C_RED SDL_MapRGB(screen->format,255,0,0)
+#define C_ORANGE SDL_MapRGB(screen->format,255,200,0)
+#define C_YELLOW SDL_MapRGB(screen->format,255,255,100)
+#define T_BLACK SDL_MapRGB(cir->format,0,0,0)
 
 /* tracklist */
 struct _tracklist *tracklist;
@@ -57,7 +67,10 @@ struct _config
   SDLKey left;
   SDLKey right;
   int color;
-} config = {"anonymous","",0,0,1,SDLK_UP,SDLK_DOWN,SDLK_LEFT,SDLK_RIGHT,6};
+  SDLKey boss;
+  int bynight;
+  int internet;
+} config = {"anonymous","",0,0,1,SDLK_UP,SDLK_DOWN,SDLK_LEFT,SDLK_RIGHT,6,SDLK_b,0,1};
 
 /* full script for a lap */
 struct _record
@@ -69,7 +82,7 @@ struct _record
 
 /* display and all directions for the car */
 SDL_Surface *screen;
-SDL_Surface *cars[12][256];
+SDL_Surface *cars[NB_CARS][256];
 
 /* network stuff */
 UDPsocket udpsock=NULL;
@@ -81,7 +94,7 @@ int network_speed=1;
 void zeRace_read_config()
 {
   FILE *fic;
-  if ((fic=fopen("zeRace.cfg","rt"))==NULL)
+  if ((fic=fopen("zeRace.cfg","rb"))==NULL)
   {
     fprintf(stderr,"can't open config file \"zeRace.cfg\"\n");
     return;
@@ -95,7 +108,7 @@ void zeRace_read_config()
 void zeRace_save_config()
 {
   FILE *fic;
-  if ((fic=fopen("zeRace.cfg","wt"))==NULL)
+  if ((fic=fopen("zeRace.cfg","wb"))==NULL)
   {
     fprintf(stderr,"can't create config file \"zeRace.cfg\"\n");
     return;
@@ -130,17 +143,19 @@ void zeRace_check_version()
   char response[1024],*tmp,*version;
   int len,result;
   
+  if (!config.internet) return;
+  
   printf("checking version... ");
   fflush(stdout);
 
-  if(SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
+  if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
   {
     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
     return;
   }
   
   tcpsock=SDLNet_TCP_Open(&ip);
-  if(!tcpsock)
+  if (!tcpsock)
   {
     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
     return;
@@ -148,7 +163,7 @@ void zeRace_check_version()
 
   len=strlen(request);
   result=SDLNet_TCP_Send(tcpsock,request,len);
-  if(result<len)
+  if (result<len)
     fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
   else
     printf("done\n");
@@ -186,17 +201,19 @@ void zeRace_update_tracks()
   int len,result;
   FILE *fic;
   
+  if (!config.internet) return;
+  
   printf("checking version and updating tracks... ");
   fflush(stdout);
 
-  if(SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
+  if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
   {
     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
     return;
   }
   
   tcpsock=SDLNet_TCP_Open(&ip);
-  if(!tcpsock)
+  if (!tcpsock)
   {
     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
     return;
@@ -204,7 +221,7 @@ void zeRace_update_tracks()
 
   len=strlen(request);
   result=SDLNet_TCP_Send(tcpsock,request,len);
-  if(result<len)
+  if (result<len)
     fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
   else
   {
@@ -226,13 +243,75 @@ void zeRace_update_tracks()
 }
 
 
+/* download the file if it is missing */
+void zeRace_download_file(char *file)
+{
+  IPaddress ip;
+  TCPsocket tcpsock;
+  char request[1024];
+  char response[10240],*tmp;
+  int len,result;
+  FILE *fic;
+  struct stat buf;
+
+  if (!config.internet) return;
+  
+  if (stat(file,&buf)<0)
+  {
+    printf("downloading file \"%s\" : ",file);
+    fflush(stdout);
+    
+    if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
+    {
+      fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
+      return;
+    }
+    
+    tcpsock=SDLNet_TCP_Open(&ip);
+    if (!tcpsock)
+    {
+      fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
+      return;
+    }
+  
+    sprintf(request,
+      "GET /zerace/%s HTTP/1.0\n"
+      "Host: royale.zerezo.com\n"
+      "User-Agent: zeRace " VERSION "\n"
+      "\n",file);
+    len=strlen(request);
+    result=SDLNet_TCP_Send(tcpsock,request,len);
+    if (result<len)
+      fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
+    else
+    {
+      if ((fic=fopen(file,"wb"))==NULL)
+      {
+        fprintf(stderr,"can't create \"%s\" file\n",file);
+        zeRace_exit();
+      }
+      len=SDLNet_TCP_Recv(tcpsock,response,10240);
+      tmp=response;
+      while (*tmp!='\n' || *(tmp+1)!='\r') tmp++;
+      tmp+=3;
+      fwrite(tmp,1,len+response-tmp,fic);
+      while ((len=SDLNet_TCP_Recv(tcpsock,response,10240))) fwrite(response,1,len,fic);
+      fclose(fic);
+      printf("done\n");
+    }
+    
+    SDLNet_TCP_Close(tcpsock);
+  }
+}
+
+
 /* load the car sprite and rotate it for every angles */
 void zeRace_generate_cars()
 {
   int i,j;
   SDL_Surface *car;
   char temp[20]="sprites/carX.png";
-  for (i=0;i<12;i++)
+  for (i=0;i<NB_CARS;i++)
   {
     temp[11]='A'+i;
     /* load the car sprite */
@@ -242,7 +321,7 @@ void zeRace_generate_cars()
     {
       float x,y;
       float tcos,tsin;
-      if ((cars[i][j]=SDL_CreateRGBSurface(SDL_SWSURFACE,30,30,32,0x000000ff,0x0000ff00,0x00ff0000,0xff000000))==NULL)
+      if ((cars[i][j]=SDL_CreateRGBSurface(SDL_SWSURFACE,30,30,32,RMASK,GMASK,BMASK,AMASK))==NULL)
       {
         fprintf(stderr,"CreateRGBSurface failed: %s\n",SDL_GetError());
         zeRace_exit();
@@ -267,11 +346,27 @@ void zeRace_generate_cars()
 void zeRace_init()
 {
   int flags;
+  struct _tracklist *loopcheck;
   
   /* do a clean exit in case of emergency */
   signal(SIGINT,zeRace_exit);
   signal(SIGTERM,zeRace_exit);
 
+  /* initialize SDL */
+  if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0)
+  {
+    fprintf(stderr,"could not initialize SDL : %s\n",SDL_GetError());
+    zeRace_exit();
+  }
+  atexit(SDL_Quit);
+  
+  /* initialize SDLNet */
+  if (SDLNet_Init()==-1)
+  {
+    fprintf(stderr,"could not initialize SDLNet : %s\n",SDLNet_GetError());
+    zeRace_exit();
+  }
+
   /* read the user configuration file */
   zeRace_read_config();
 
@@ -284,21 +379,17 @@ void zeRace_init()
   /* get the list of local tracks */
   if (!zeRace_get_tracks(&tracklist)) zeRace_exit();
   
-  srand(time(NULL));
-  
-  if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0)
+  /* download missing files */
+  loopcheck=tracklist;
+  do
   {
-    fprintf(stderr,"could not initialize SDL : %s\n",SDL_GetError());
-    zeRace_exit();
-  }
-  atexit(SDL_Quit);
+    zeRace_download_file(tracklist->full);
+    zeRace_download_file(tracklist->function);
+    tracklist=tracklist->next;
+  } while (tracklist!=loopcheck);
+  
+  srand(time(NULL));
   
-  if(SDLNet_Init()==-1)
-  {
-    fprintf(stderr,"could not initialize SDLNet : %s\n",SDLNet_GetError());
-    zeRace_exit();
-  }
-
   packet=SDLNet_AllocPacket(1024);
   if (!packet)
   {
@@ -333,7 +424,7 @@ void zeRace_init()
 
 
 /* send the best time for this race to the web server */
-void zeRace_send_time(struct _record record/*float x,float y,float speed,float angle,int btime,char *bkeys*/)
+void zeRace_send_time(struct _record *record)
 {
   IPaddress ip;
   TCPsocket tcpsock;
@@ -356,28 +447,33 @@ void zeRace_send_time(struct _record record/*float x,float y,float speed,float a
   char *msg9="&bkeys=";
   int len,result;
   
+  if (!config.internet) return;
+  
+  /* if the best time is small enought to save all keys, send it */
+  if (record->time>=MAXRECORDKEYS) return;
+
   printf("sending time... ");
   fflush(stdout);
 
-  if(SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
+  if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
   {
     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
     return;
   }
   
   tcpsock=SDLNet_TCP_Open(&ip);
-  if(!tcpsock)
+  if (!tcpsock)
   {
     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
     return;
   }
 
-  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);
-  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);
+  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);
+  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);
   
   len=strlen(temp);
   result=SDLNet_TCP_Send(tcpsock,temp,len);
-  if(result<len)
+  if (result<len)
     fprintf(stderr,"SDLNet_TCP_Send: %s\n", SDLNet_GetError());
   else
     printf("done\n");
@@ -406,10 +502,35 @@ void print_time(int x,int y,int time)
 }
 
 
+/* car lights */
+void lights(int x,int y,int r,Uint32 pixel)
+{
+  putpixel(screen,x,y,pixel);
+  if (r>1)
+  {
+    putpixel(screen,x-1,y,pixel);
+    putpixel(screen,x+1,y,pixel);
+    putpixel(screen,x,y-1,pixel);
+    putpixel(screen,x,y+1,pixel);
+  }
+  if (r>2)
+  {
+    putpixel(screen,x-2,y,pixel);
+    putpixel(screen,x+2,y,pixel);
+    putpixel(screen,x,y-2,pixel);
+    putpixel(screen,x,y+2,pixel);
+    putpixel(screen,x-1,y-1,pixel);
+    putpixel(screen,x-1,y+1,pixel);
+    putpixel(screen,x+1,y-1,pixel);
+    putpixel(screen,x+1,y+1,pixel);
+  }
+}
+
+
 /* launch a new race */
 void zeRace_launch(int alltime,int go)
 {
-  SDL_Surface *cir,*fun;
+  SDL_Surface *cir,*fun,*hilight;
   SDL_Rect pos;
   SDL_Event event;
   int ku=0,kd=0,kl=0,kr=0,i;
@@ -421,15 +542,49 @@ void zeRace_launch(int alltime,int go)
   int lastack=alltime;
   struct _record net;
   struct _car oldnetpos[MAX_CLIENTS],newnetpos[MAX_CLIENTS];
+  int l=80,o=15;
+  
+  /* free memory */
+  void free_mem()
+  {
+    SDL_FreeSurface(cir);
+    SDL_FreeSurface(fun);
+    if (config.sound)
+    {
+      Mix_FreeMusic(light);
+      Mix_FreeMusic(engine);
+      Mix_FreeMusic(crash);
+      Mix_FreeMusic(slide);
+    }
+    return;
+  }
 
   cir=IMG_Load(tracklist->full);
+  /* dark the track if it is night */
+  if (config.bynight)
+  {
+    for (pos.x=0;pos.x<screen->w;pos.x++)
+      for (pos.y=0;pos.y<screen->h;pos.y++)
+      {
+        Uint32 c;
+        Uint8 r,g,b;
+        c=getpixel(cir,pos.x,pos.y);
+        SDL_GetRGB(c,cir->format,&r,&g,&b);
+        r*=0.3;
+        g*=0.3;
+        b*=0.3;
+        putpixel(cir,pos.x,pos.y,SDL_MapRGB(cir->format,r,g,b));
+      }
+  }
   fun=IMG_Load(tracklist->function);
+  hilight=IMG_Load("sprites/light.png");
   
   current.speed=car.speed=0;
   current.angle=car.angle=tracklist->a*2*M_PI/360;
   current.x=car.ox=car.x=tracklist->x;
   current.y=car.oy=car.y=tracklist->y;
   car.lastcheck=0;
+  car.lapflag=0;
   car.w=cars[0][0]->w;
   car.h=cars[0][0]->h;
   current.time=0;
@@ -483,13 +638,6 @@ void zeRace_launch(int alltime,int go)
           switch (event.key.keysym.sym)
           {
             case SDLK_ESCAPE:
-              /* free memory */
-              Mix_FreeMusic(light);
-              Mix_FreeMusic(engine);
-              Mix_FreeMusic(crash);
-              Mix_FreeMusic(slide);
-              /* if the best time is small enought to save all keys, send it */
-              if (best.time<MAXRECORDKEYS) zeRace_send_time(best);
               if (udpsock)
               {
                 print(screen,WIDTH/2-strlen("Disconnecting !")*5,HEIGHT/2-10,"Disconnecting !");
@@ -498,6 +646,8 @@ void zeRace_launch(int alltime,int go)
                 SDLNet_UDP_Send(udpsock,-1,packet);
                 SDL_Flip(screen);
               }
+              zeRace_send_time(&best);
+              free_mem();
               return;  
             default:
               i=event.key.keysym.sym;
@@ -505,6 +655,19 @@ void zeRace_launch(int alltime,int go)
               if (i==config.down) kd=1;
               if (i==config.left) kl=1;
               if (i==config.right) kr=1;
+              if (i==config.boss)
+              {
+                /* display the boss screen */
+                SDL_Surface *boss;
+                boss=IMG_Load("sprites/boss.png");
+                SDL_BlitSurface(boss,NULL,screen,NULL);
+                SDL_FreeSurface(boss);
+                SDL_Flip(screen);
+                /* and wait until the user press another key */
+                for (;;) if (SDL_PollEvent(&event)) { if (event.type==SDL_KEYDOWN) break; } else SDL_Delay(10);
+                SDL_BlitSurface(cir,NULL,screen,NULL);
+                SDL_Flip(screen);
+              }
               break;
           }
           break;
@@ -528,6 +691,9 @@ void zeRace_launch(int alltime,int go)
       net.keys[net.time+1]='\0';
     }
 
+    /* move the car */
+    move_car(&car,(ku<<3 | kd<<2 | kl<<1 | kr),fun);
+
     delay=DELAY;
     /* if we are in network mode */
     if (udpsock!=NULL)
@@ -535,11 +701,10 @@ void zeRace_launch(int alltime,int go)
       char *tmp;
       while (SDLNet_UDP_Recv(udpsock,packet)) if (strcmp(packet->data,"positions")==0)
       {
-        /*struct _car netcar;*/
         int servertime,clienttime,nb;
-        memcpy(&servertime,packet->data+strlen("positions")+1,sizeof(int));
-        memcpy(&clienttime,packet->data+strlen("positions")+1+sizeof(int),sizeof(int));
-        memcpy(&nb,packet->data+strlen("positions")+1+sizeof(int)+sizeof(int),sizeof(int));
+        servertime=SDLNet_Read32(packet->data+strlen("positions")+1);
+        clienttime=SDLNet_Read32(packet->data+strlen("positions")+1+4);
+        nb=SDLNet_Read16(packet->data+strlen("positions")+1+4+4);
         if (clienttime>lastack)
         {
           memcpy(net.keys,net.keys+clienttime-lastack,net.time+1);
@@ -547,19 +712,48 @@ void zeRace_launch(int alltime,int go)
           if (clienttime>servertime+5) delay+=DELAY;
           if (clienttime<servertime-5) delay-=DELAY;
           if (delay<0) delay=0;
-          /* printf("servertime = %d clienttime = %d lastack = %d nb = %d delay = %d net.time = %d\n",servertime,clienttime,lastack,nb,delay,net.time); */
           for (i=0;i<MAX_CLIENTS;i++) newnetpos[i].w=0;
-          memcpy(newnetpos,packet->data+strlen("positions")+1+sizeof(int)*3,sizeof(struct _car)*nb);
+          for (i=0;i<nb;i++)
+          {
+            newnetpos[i].w=newnetpos[i].h=30;
+            newnetpos[i].x=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14);
+            newnetpos[i].y=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2);
+            newnetpos[i].angle=(float)SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2+2)/1000;
+            newnetpos[i].color=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2+2+2);
+            newnetpos[i].lights_brake=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2+2+2+2);
+            newnetpos[i].lights_backwards=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2+2+2+2+2);
+            newnetpos[i].lights_warning=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2+2+2+2+2+2);
+          }
           lastack=clienttime;
         }
-      } else return;
+      }
+      else if (strcmp(packet->data,"collision")==0)
+      {
+        net.time=-1;
+        net.keys[0]='\0';
+        lastack=SDLNet_Read32(packet->data+strlen("collision")+1);
+        car.x=(float)SDLNet_Read32(packet->data+strlen("collision")+1+4)/65536-100;
+        car.y=(float)SDLNet_Read32(packet->data+strlen("collision")+1+4+4)/65536-100;
+        car.speed=(float)SDLNet_Read32(packet->data+strlen("collision")+1+4+4+4)/65536-100;
+        car.angle=(float)SDLNet_Read32(packet->data+strlen("collision")+1+4+4+4+4)/65536-100;
+      }
+      else /* end of this network race */
+      {
+        zeRace_send_time(&best);
+        free_mem();
+        return;
+      }
       if (strlen(net.keys)!=0)
       {
         tmp=packet->data;
         strcpy(tmp,"keys");
         tmp+=strlen(tmp)+1;
-        memcpy(tmp,&lastack,sizeof(int));
-        tmp+=sizeof(int);
+        SDLNet_Write32(lastack,tmp);
+        tmp+=4;
+        SDLNet_Write32(car.x,tmp);
+        tmp+=4;
+        SDLNet_Write32(car.y,tmp);
+        tmp+=4;
         strcpy(tmp,net.keys);
         tmp+=strlen(tmp)+1;
         packet->len=(void *)tmp-(void *)packet->data+10;
@@ -588,6 +782,25 @@ void zeRace_launch(int alltime,int go)
     pos.h=car.h;
     SDL_BlitSurface(cir,&pos,screen,&pos);
     
+    /* clear the lights */
+    if (config.bynight)
+    {
+      pos.x=car.ox-cos(car.angle)*l-sin(car.angle)*o;
+      pos.y=car.oy-sin(car.angle)*l+cos(car.angle)*o;
+      pos.x-=60;
+      pos.y-=60;
+      pos.w=120;
+      pos.h=120;
+      SDL_BlitSurface(cir,&pos,screen,&pos);
+      pos.x=car.ox-cos(car.angle)*l+sin(car.angle)*o;
+      pos.y=car.oy-sin(car.angle)*l-cos(car.angle)*o;
+      pos.x-=60;
+      pos.y-=60;
+      pos.w=120;
+      pos.h=120;
+      SDL_BlitSurface(cir,&pos,screen,&pos);
+    }
+
     /* display the network car at the new position */
     if (udpsock) for (i=0;i<MAX_CLIENTS;i++) if (newnetpos[i].w)
     {
@@ -596,6 +809,29 @@ void zeRace_launch(int alltime,int go)
       pos.w=car.w;
       pos.h=car.h;
       SDL_BlitSurface(cars[newnetpos[i].color][(unsigned char)(256*newnetpos[i].angle/2.0/M_PI)%256],NULL,screen,&pos);
+      
+      /* if the car is braking, display red lights */
+      if (newnetpos[i].lights_brake)
+      {
+        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);
+        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);
+      }
+      
+      /* if the car is going backwards, display white lights */
+      if (newnetpos[i].lights_backwards)
+      {
+        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);
+        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);
+      }
+      
+      /* if the car is stopped, then warning */
+      if (newnetpos[i].lights_warning && alltime/75%2)
+      {
+        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);
+        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);
+        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);
+        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);
+      }
     }
     
     /* display the car at the new position */
@@ -605,6 +841,52 @@ void zeRace_launch(int alltime,int go)
     pos.h=car.h;
     SDL_BlitSurface(cars[config.color][(unsigned char)(256*car.angle/2.0/M_PI)%256],NULL,screen,&pos);
     
+    /* if the car is braking, display red lights */
+    if (car.lights_brake)
+    {
+      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);
+      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);
+    }
+    
+    /* if the car is going backwards, display white lights */
+    if (car.lights_backwards)
+    {
+      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);
+      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);
+    }
+    
+    /* if the car is stopped, then warning */
+    if (car.lights_warning && alltime/75%2)
+    {
+      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);
+      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);
+      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);
+      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);
+    }
+    
+    /* display the lights */
+    if (config.bynight)
+    {
+      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);
+      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);
+      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);
+      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);
+      pos.x=car.x-cos(car.angle)*l-sin(car.angle)*o;
+      pos.y=car.y-sin(car.angle)*l+cos(car.angle)*o;
+      pos.x-=50;
+      pos.y-=50;
+      pos.w=100;
+      pos.h=100;
+      SDL_BlitSurface(hilight,NULL,screen,&pos);
+      pos.x=car.x-cos(car.angle)*l+sin(car.angle)*o;
+      pos.y=car.y-sin(car.angle)*l-cos(car.angle)*o;
+      pos.x-=50;
+      pos.y-=50;
+      pos.w=100;
+      pos.h=100;
+      SDL_BlitSurface(hilight,NULL,screen,&pos);
+    }
+    
     /* update display */
     if (udpsock)
     {
@@ -617,12 +899,36 @@ void zeRace_launch(int alltime,int go)
     }
     SDL_UpdateRect(screen,car.ox-car.w/2,car.oy-car.h/2,car.w,car.h);
     SDL_UpdateRect(screen,car.x-car.w/2,car.y-car.h/2,car.w,car.h);
-
+    
+    /* update the lights by night */
+    if (config.bynight)
+    {
+      pos.x=car.x-cos(car.angle)*l-sin(car.angle)*15;
+      pos.y=car.y-sin(car.angle)*l+cos(car.angle)*15;
+      pos.x-=60;
+      pos.y-=60;
+      pos.w=120;
+      pos.h=120;
+      if (pos.x<0) pos.x=0;
+      if (pos.y<0) pos.y=0;
+      if (pos.x+pos.w>screen->w) pos.w=screen->w-pos.x;
+      if (pos.y+pos.h>screen->h) pos.h=screen->h-pos.y;
+      SDL_UpdateRect(screen,pos.x,pos.y,pos.w,pos.h);
+      pos.x=car.x-cos(car.angle)*l+sin(car.angle)*15;
+      pos.y=car.y-sin(car.angle)*l-cos(car.angle)*15;
+      pos.x-=60;
+      pos.y-=60;
+      pos.w=120;
+      pos.h=120;
+      if (pos.x<0) pos.x=0;
+      if (pos.y<0) pos.y=0;
+      if (pos.x+pos.w>screen->w) pos.w=screen->w-pos.x;
+      if (pos.y+pos.h>screen->h) pos.h=screen->h-pos.y;
+      SDL_UpdateRect(screen,pos.x,pos.y,pos.w,pos.h);
+    }
+    
     memcpy(oldnetpos,newnetpos,MAX_CLIENTS*sizeof(struct _car));
     
-    /* move the car */
-    move_car(&car,(ku<<3 | kd<<2 | kl<<1 | kr),fun);
-
     /* play engine sound if no sound is currently playing */
     if (lastsound_time+100<alltime)
     {
@@ -645,13 +951,13 @@ void zeRace_launch(int alltime,int go)
       /* display tires slide */
       if (config.tire)
       {
-        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);
-        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);
+        putpixel(cir,car.x+cos(car.angle)*car.w/3-sin(car.angle)*4,car.y+sin(car.angle)*car.h/3+cos(car.angle)*4,T_BLACK);
+        putpixel(cir,car.x+cos(car.angle)*car.w/3+sin(car.angle)*4,car.y+sin(car.angle)*car.h/3-cos(car.angle)*4,T_BLACK);
         /* if we are braking the slide is larger */
         if (kd)
         {
-          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);
-          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);
+          putpixel(cir,car.x+cos(car.angle)*car.w/3-sin(car.angle)*3,car.y+sin(car.angle)*car.h/3+cos(car.angle)*3,T_BLACK);
+          putpixel(cir,car.x+cos(car.angle)*car.w/3+sin(car.angle)*3,car.y+sin(car.angle)*car.h/3-cos(car.angle)*3,T_BLACK);
         }
       }
     }
@@ -664,26 +970,6 @@ void zeRace_launch(int alltime,int go)
       if (config.sound) Mix_PlayMusic(crash,1)==-1;
     }
    
-    /* if we completed a lap */
-    if (car.lapflag)
-    {
-      printf("time = %d\"%d\n",current.time*DELAY/1000,current.time*DELAY%1000);
-      print(screen,0,0,"Last lap : ");
-      print_time(110,0,current.time);
-      SDL_UpdateRect(screen,0,0,170,19);
-      /* if it is the first turn of the best turn, save it */
-      if (best.time==-1 || current.time<best.time)
-        memcpy(&best,&current,sizeof(struct _record));
-      /* reset turn variables */
-      current.time=0;
-      current.x=car.x;
-      current.y=car.y;
-      current.angle=car.angle;
-      current.speed=car.speed;
-    }
-    
-    /* let the system breath */
-    SDL_Delay(delay);
     /* game time */
     current.time++;
     net.time++;
@@ -691,9 +977,56 @@ void zeRace_launch(int alltime,int go)
     {
       print(screen,WIDTH/2-strlen("Timeout !")*5,HEIGHT/2-10,"Timeout !");
       SDL_Flip(screen);
+      zeRace_send_time(&best);
+      free_mem();
       return;
     }
     alltime++;
+
+    switch (car.lapflag)
+    {
+      /* if we completed a lap */
+      case 1:
+        printf("time = %d\"%d\n",current.time*DELAY/1000,current.time*DELAY%1000);
+        print(screen,0,0,"Last lap :          ");
+        print_time(110,0,current.time);
+        SDL_UpdateRect(screen,0,0,200,19);
+        /* if it is the first turn of the best turn, save it */
+        if (best.time==-1 || current.time<best.time)
+          memcpy(&best,&current,sizeof(struct _record));
+        /* reset turn variables */
+        current.time=0;
+        current.x=car.x;
+        current.y=car.y;
+        current.angle=car.angle;
+        current.speed=car.speed;
+        car.lapflag=0;
+        break;
+      /* if we completed an incomplete lap */
+      case 2:
+        print(screen,0,0,"Last lap : CANCELED ");
+        SDL_UpdateRect(screen,0,0,200,19);
+        /* reset turn variables */
+        current.time=0;
+        car.lapflag=0;
+        break;
+      /* if we miss a checkpoint */
+      case 3:
+        print(screen,0,0,"Checkpoint missed ! ");
+        SDL_UpdateRect(screen,0,0,200,19);
+        break;
+      /* if we validate a missed checkpoint */
+      case 4:
+        print(screen,0,0,"Checkpoint missed OK");
+        SDL_UpdateRect(screen,0,0,200,19);
+        break;
+      /* nothing */
+      default:
+        break;
+    }
+    
+    /* let the system breath */
+    SDL_Delay(delay);
   }
 }
 
@@ -705,21 +1038,21 @@ void zeRace_splash()
   SDL_Rect pos;
   char temp[20]="splashs/0.jpg";
   
-  SDL_FillRect(screen,NULL,0x000000);
-  temp[8]=rand()%3+'1';
+  SDL_FillRect(screen,NULL,C_BLACK);
+  temp[8]=rand()%5+'1';
   splash=IMG_Load(temp);
   pos.x=screen->w/2-splash->w/2-1;
   pos.w=splash->w+2;
   pos.y=screen->h/2-splash->h/2-1;
   pos.h=splash->h+2;
-  SDL_FillRect(screen,&pos,0xffffff);
+  SDL_FillRect(screen,&pos,C_WHITE);
   pos.x=screen->w/2-splash->w/2;
   pos.y=screen->h/2-splash->h/2;
   SDL_BlitSurface(splash,NULL,screen,&pos);
   print(screen,screen->w/2-strlen("zeRace " VERSION)*5,screen->h/2-splash->h/2-20,"zeRace " VERSION);
   SDL_FreeSurface(splash);
   SDL_Flip(screen);
-  /*SDL_Delay(2000);*/
+  SDL_Delay(2000);
 }
 
 
@@ -732,7 +1065,7 @@ void zeRace_local()
   {
     SDL_Surface *full,*preview;
     SDL_Rect pos;
-    SDL_FillRect(screen,NULL,0x000000);
+    SDL_FillRect(screen,NULL,C_BLACK);
     print(screen,WIDTH/2-28*5,HEIGHT/6,"* Please choose your race *");
     print(screen,WIDTH/2-strlen(tracklist->title)*5,5*HEIGHT/6-20,tracklist->title);
     print(screen,WIDTH/2-(strlen(tracklist->author)+strlen("Author : "))*5,5*HEIGHT/6+0,"Author : ");
@@ -750,7 +1083,7 @@ void zeRace_local()
     pos.w=preview->w+2;
     pos.y=screen->h/2-preview->h/2-1;
     pos.h=preview->h+2;
-    SDL_FillRect(screen,&pos,0xffffff);
+    SDL_FillRect(screen,&pos,C_WHITE);
     pos.x=WIDTH/2-preview->w/2;
     pos.y=screen->h/2-preview->h/2;
     SDL_BlitSurface(preview,NULL,screen,&pos);
@@ -802,16 +1135,16 @@ void zeRace_top10(char *buf)
 {
   int i,nb,tmp;
   SDL_Rect pos;
-  SDL_FillRect(screen,NULL,0x000000);
-  memcpy(&nb,buf,sizeof(int));
-  buf+=sizeof(int);
+  SDL_FillRect(screen,NULL,C_BLACK);
+  nb=SDLNet_Read16(buf);
+  buf+=2;
   print(screen,WIDTH/2-16*5,HEIGHT/14,"* Race results *");
   for (i=0;i<nb;i++)
   {
     print(screen,150,(i+3)*HEIGHT/14,buf);
     buf+=strlen(buf)+1;
-    memcpy(&tmp,buf,sizeof(int));
-    buf+=sizeof(int);
+    tmp=SDLNet_Read16(buf);
+    buf+=2;
     pos.x=110;
     pos.y=(i+3)*HEIGHT/14-8;
     SDL_BlitSurface(cars[tmp][0],NULL,screen,&pos);
@@ -838,8 +1171,8 @@ void zeRace_connect(char *host,int port)
   tmp+=strlen(tmp)+1;
   strcpy(tmp,config.pseudo);
   tmp+=strlen(tmp)+1;
-  memcpy(tmp,&config.color,sizeof(int));
-  tmp+=sizeof(int);
+  SDLNet_Write16(config.color,tmp);
+  tmp+=2;
   packet->len=(void *)tmp-(void *)packet->data;
   SDLNet_UDP_Send(udpsock,-1,packet);
   /* network loop */
@@ -854,16 +1187,16 @@ void zeRace_connect(char *host,int port)
       tmp+=strlen(tmp)+1;
       go=*tmp++;
       printf("server asked for track : %s\n",tmp);
-      while (tracklist->next!=loopcheck) if (strcmp(tracklist->name,tmp)==0) break; else tracklist=tracklist->next;
+      do if (strcmp(tracklist->name,tmp)==0) break; else tracklist=tracklist->next; while (tracklist!=loopcheck);
       if (strcmp(tracklist->name,tmp)!=0)
       {
         fprintf(stderr,"unknown track : %s\n",tmp);
         zeRace_exit();
       }
       tmp+=strlen(tmp)+1;
-      memcpy(&time,tmp,sizeof(int));
-      tmp+=sizeof(int);
-      memcpy(&network_speed,tmp,sizeof(int));
+      time=SDLNet_Read32(tmp);
+      tmp+=4;
+      network_speed=SDLNet_Read32(tmp);
       zeRace_launch(time,go);
       if (strcmp(packet->data,"finish")==0) zeRace_top10(packet->data+strlen(packet->data)+1);
       lag=0;
@@ -887,7 +1220,7 @@ void zeRace_network()
 
   void update()
   {
-    SDL_FillRect(screen,NULL,0x000000);
+    SDL_FillRect(screen,NULL,C_BLACK);
     print(screen,380,HEIGHT/(NETWORK_OPTIONS+4)*(3+active),">");
     print(screen,WIDTH/2-18*5,HEIGHT/(NETWORK_OPTIONS+4),"* Network screen *");
     print(screen,400,HEIGHT/(NETWORK_OPTIONS+4)*3,"Server : ");
@@ -971,17 +1304,19 @@ void zeRace_internet()
   int active=0;
   #define INTERNET_OPTIONS 11
   
+  if (!config.internet) return;
+  
   printf("dowloading list of servers... ");
   fflush(stdout);
 
-  if(SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
+  if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
   {
     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
     return;
   }
   
   tcpsock=SDLNet_TCP_Open(&ip);
-  if(!tcpsock)
+  if (!tcpsock)
   {
     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
     return;
@@ -989,7 +1324,7 @@ void zeRace_internet()
 
   len=strlen(request);
   result=SDLNet_TCP_Send(tcpsock,request,len);
-  if(result<len)
+  if (result<len)
     fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
   else
   {
@@ -999,7 +1334,6 @@ void zeRace_internet()
     tmp=response;
     while (*tmp!='\0' || *(tmp+1)!='\r') tmp++;
     tmp+=3;
-    printf("%s\n",tmp);
     for (i=0;i<10;i++)
     {
       strcpy(servers[i].name,tmp);
@@ -1017,7 +1351,7 @@ void zeRace_internet()
   void update()
   {
     int i;
-    SDL_FillRect(screen,NULL,0x000000);
+    SDL_FillRect(screen,NULL,C_BLACK);
     print(screen,380,HEIGHT/(INTERNET_OPTIONS+4)*(3+active),">");
     print(screen,WIDTH/2-19*5,HEIGHT/(INTERNET_OPTIONS+4),"* Internet screen *");
     for (i=0;i<10;i++)
@@ -1075,12 +1409,12 @@ void zeRace_config()
 {
   SDL_Event event;
   int active=0;
-  #define CONFIG_OPTIONS 11
+  #define CONFIG_OPTIONS 14
   
   void update()
   {
     SDL_Rect pos;
-    SDL_FillRect(screen,NULL,0x000000);
+    SDL_FillRect(screen,NULL,C_BLACK);
     print(screen,20,HEIGHT/(CONFIG_OPTIONS+4)*(3+active),">");
     print(screen,WIDTH/2-24*5,HEIGHT/(CONFIG_OPTIONS+4),"* Configuration screen *");
     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*3,"Pseudo : ");
@@ -1105,6 +1439,12 @@ void zeRace_config()
     pos.x=123;
     pos.y=HEIGHT/(CONFIG_OPTIONS+4)*12-7;
     SDL_BlitSurface(cars[config.color][0],NULL,screen,&pos);
+    print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*13,"Boss key : ");
+    print(screen,40+10*strlen("Boss key : "),HEIGHT/(CONFIG_OPTIONS+4)*13,config.boss?SDL_GetKeyName(config.boss):"<press key>");
+    print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*14,"By night : ");
+    print(screen,40+10*strlen("By night : "),HEIGHT/(CONFIG_OPTIONS+4)*14,config.bynight?"Yes":"No");
+    print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*15,"Internet : ");
+    print(screen,40+10*strlen("Internet : "),HEIGHT/(CONFIG_OPTIONS+4)*15,config.internet?"Yes":"No");
     print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*(CONFIG_OPTIONS+2),"Back to main menu");
     SDL_Flip(screen);
   }
@@ -1154,10 +1494,13 @@ void zeRace_config()
                 case 8: config.right=0; update(); config.right=read_key(); break;
                 case 9:
                   if (event.key.keysym.sym==SDLK_LEFT) config.color--; else config.color++;
-                  if (config.color<0) config.color=11;
-                  if (config.color>11) config.color=0;
+                  if (config.color<0) config.color=NB_CARS-1;
+                  if (config.color>NB_CARS-1) config.color=0;
                   break;
-                case 10:
+                case 10: config.boss=0; update(); config.boss=read_key(); break;
+                case 11: config.bynight=!config.bynight; break;
+                case 12: config.internet=!config.internet; break;
+                case 13:
                   return;
               }
               update();
@@ -1192,7 +1535,7 @@ void zeRace_menu()
   void update()
   {
     SDL_Rect pos;
-    SDL_FillRect(screen,NULL,0x000000);
+    SDL_FillRect(screen,NULL,C_BLACK);
     pos.x=WIDTH/2-logo->w/2;
     pos.y=HEIGHT/6-logo->h/2;
     SDL_BlitSurface(logo,NULL,screen,&pos);