2 * zeRace 0.7, a funny retro racing game
3 * http://royale.zerezo.com/zerace/
5 * Copyright (C) 2004 Antoine Jacquet <royale@zerezo.com>
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.
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.
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
28 #include <SDL_image.h>
30 #include <SDL_mixer.h>
31 #include <SDL_rotozoom.h>
34 #include "tracklist.h"
38 /* configuration constants */
43 #define MAXRECORDKEYS 9999
46 /* some usefull colors */
47 #define C_BLACK SDL_MapRGB(screen->format,0,0,0)
48 #define C_WHITE SDL_MapRGB(screen->format,255,255,255)
49 #define C_RED SDL_MapRGB(screen->format,255,0,0)
50 #define C_ORANGE SDL_MapRGB(screen->format,255,200,0)
51 #define C_YELLOW SDL_MapRGB(screen->format,255,255,100)
52 #define T_BLACK SDL_MapRGB(cir->format,0,0,0)
55 struct _tracklist *tracklist;
60 char pseudo[MAXLINELENGTH];
61 char url[MAXLINELENGTH];
73 } config = {"anonymous","",0,0,1,SDLK_UP,SDLK_DOWN,SDLK_LEFT,SDLK_RIGHT,6,SDLK_b,0,1};
75 /* full script for a lap */
78 float x,y,angle,speed;
79 char keys[MAXRECORDKEYS];
83 /* display and all directions for the car */
85 SDL_Surface *cars[NB_CARS][256];
88 UDPsocket udpsock=NULL;
93 /* read the user configuration file */
94 void zeRace_read_config()
97 if ((fic=fopen("zeRace.cfg","rb"))==NULL)
99 fprintf(stderr,"can't open config file \"zeRace.cfg\"\n");
102 fread(&config,sizeof(struct _config),1,fic);
107 /* save the user configuration file */
108 void zeRace_save_config()
111 if ((fic=fopen("zeRace.cfg","wb"))==NULL)
113 fprintf(stderr,"can't create config file \"zeRace.cfg\"\n");
116 fwrite(&config,sizeof(struct _config),1,fic);
121 /* exit the game and clean */
125 if (config.sound) Mix_CloseAudio();
128 zeRace_save_config();
133 /* check for a newer version online to warn the user */
134 void zeRace_check_version()
139 "GET /zerace/version.php HTTP/1.0\n"
140 "Host: royale.zerezo.com\n"
141 "User-Agent: zeRace " VERSION "\n"
143 char response[1024],*tmp,*version;
146 if (!config.internet) return;
148 printf("checking version... ");
151 if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
153 fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
157 tcpsock=SDLNet_TCP_Open(&ip);
160 fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
165 result=SDLNet_TCP_Send(tcpsock,request,len);
167 fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
171 len=SDLNet_TCP_Recv(tcpsock,response,1024);
173 fprintf(stderr,"SDLNet_TCP_Recv: %s\n",SDLNet_GetError());
177 while (*tmp++!='\0') if (strncmp(tmp,"version=",8)==0)
180 while (*tmp!='\n') tmp++;
182 if (strcmp(version,VERSION)>0) printf("new version available !\nhttp://royale.zerezo.com/zerace/\n");
186 SDLNet_TCP_Close(tcpsock);
190 /* get remote list of tracks */
191 void zeRace_update_tracks()
196 "GET /zerace/tracks.php HTTP/1.0\n"
197 "Host: royale.zerezo.com\n"
198 "User-Agent: zeRace " VERSION "\n"
200 char response[10240],*tmp;
204 if (!config.internet) return;
206 printf("checking version and updating tracks... ");
209 if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
211 fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
215 tcpsock=SDLNet_TCP_Open(&ip);
218 fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
223 result=SDLNet_TCP_Send(tcpsock,request,len);
225 fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
228 if ((fic=fopen("tracks/list.txt","wt"))==NULL)
230 fprintf(stderr,"can't create track list\n");
233 len=SDLNet_TCP_Recv(tcpsock,response,10240);
235 while (*tmp!='\n' || *(tmp+1)!='\r') tmp++;
237 fwrite(tmp,1,len+response-tmp,fic);
242 SDLNet_TCP_Close(tcpsock);
246 /* download the file if it is missing */
247 void zeRace_download_file(char *file)
252 char response[10240],*tmp;
257 if (!config.internet) return;
259 if (stat(file,&buf)<0)
261 printf("downloading file \"%s\" : ",file);
264 if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
266 fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
270 tcpsock=SDLNet_TCP_Open(&ip);
273 fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
278 "GET /zerace/%s HTTP/1.0\n"
279 "Host: royale.zerezo.com\n"
280 "User-Agent: zeRace " VERSION "\n"
283 result=SDLNet_TCP_Send(tcpsock,request,len);
285 fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
288 if ((fic=fopen(file,"wb"))==NULL)
290 fprintf(stderr,"can't create \"%s\" file\n",file);
293 len=SDLNet_TCP_Recv(tcpsock,response,10240);
295 while (*tmp!='\n' || *(tmp+1)!='\r') tmp++;
297 fwrite(tmp,1,len+response-tmp,fic);
298 while ((len=SDLNet_TCP_Recv(tcpsock,response,10240))) fwrite(response,1,len,fic);
303 SDLNet_TCP_Close(tcpsock);
308 /* load the car sprite and rotate it for every angles */
309 void zeRace_generate_cars()
313 char temp[20]="sprites/carX.png";
314 for (i=0;i<NB_CARS;i++)
317 /* load the car sprite */
319 /* and rotate it for all available angles */
324 if ((cars[i][j]=SDL_CreateRGBSurface(SDL_SWSURFACE,30,30,32,RMASK,GMASK,BMASK,AMASK))==NULL)
326 fprintf(stderr,"CreateRGBSurface failed: %s\n",SDL_GetError());
329 tcos=cos(2*M_PI*j/256);
330 tsin=sin(2*M_PI*j/256);
331 for (x=0;x<cars[i][j]->w;x++) for (y=0;y<cars[i][j]->h;y++)
334 x2=(x-cars[i][j]->w/2.0)*tcos+(y-cars[i][j]->h/2.0)*tsin+car->w/2.0;
335 y2=(x-cars[i][j]->w/2.0)*tsin-(y-cars[i][j]->h/2.0)*tcos+car->h/2.0;
336 if (x2>0 && x2<car->w && y2>0 && y2<car->h)
337 putpixel(cars[i][j],x,y,getpixel(car,x2,y2));
340 SDL_FreeSurface(car);
345 /* initialize the game */
349 struct _tracklist *loopcheck;
351 /* do a clean exit in case of emergency */
352 signal(SIGINT,zeRace_exit);
353 signal(SIGTERM,zeRace_exit);
356 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0)
358 fprintf(stderr,"could not initialize SDL : %s\n",SDL_GetError());
363 /* initialize SDLNet */
364 if (SDLNet_Init()==-1)
366 fprintf(stderr,"could not initialize SDLNet : %s\n",SDLNet_GetError());
370 /* read the user configuration file */
371 zeRace_read_config();
373 /* check for a newer available version */
374 zeRace_check_version();
376 /* update the list of tracks */
377 zeRace_update_tracks();
379 /* get the list of local tracks */
380 if (!zeRace_get_tracks(&tracklist)) zeRace_exit();
382 /* download missing files */
386 zeRace_download_file(tracklist->full);
387 zeRace_download_file(tracklist->function);
388 tracklist=tracklist->next;
389 } while (tracklist!=loopcheck);
393 packet=SDLNet_AllocPacket(1024);
396 fprintf(stderr,"SDLNet_AllocPacket: %s\n",SDLNet_GetError());
400 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
402 flags=SDL_HWSURFACE|SDL_ANYFORMAT;
403 if (config.fullscreen) flags|=SDL_FULLSCREEN;
405 if ((screen=SDL_SetVideoMode(WIDTH,HEIGHT,32,flags))==NULL)
407 fprintf(stderr,"could not create a surface : %s\n",SDL_GetError());
411 SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"),NULL);
412 SDL_WM_SetCaption("zeRace " VERSION,"zeRace " VERSION);
413 SDL_ShowCursor(SDL_DISABLE);
415 if (config.sound) if (Mix_OpenAudio(44100,MIX_DEFAULT_FORMAT,2,512)<0)
417 fprintf(stderr,"Mix_OpenAudio error\n");
421 /* pre-calculate car sprites */
422 zeRace_generate_cars();
426 /* send the best time for this race to the web server */
427 void zeRace_send_time(struct _record *record)
433 "POST /zerace/time.php HTTP/1.0\n"
434 "Host: royale.zerezo.com\n"
435 "User-Agent: zeRace " VERSION "\n"
436 "Content-Type: application/x-www-form-urlencoded\n"
437 "Content-Length: 99999\n"
441 char *msg3="&track=";
442 char *msg4="&btime=";
445 char *msg7="&speed=";
446 char *msg8="&angle=";
447 char *msg9="&bkeys=";
450 if (!config.internet) return;
452 /* if the best time is small enought to save all keys, send it */
453 if (record->time>=MAXRECORDKEYS) return;
455 printf("sending time... ");
458 if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
460 fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
464 tcpsock=SDLNet_TCP_Open(&ip);
467 fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
471 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);
472 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);
475 result=SDLNet_TCP_Send(tcpsock,temp,len);
477 fprintf(stderr,"SDLNet_TCP_Send: %s\n", SDLNet_GetError());
481 SDLNet_TCP_Close(tcpsock);
485 /* print a time for a lap */
486 void print_time(int x,int y,int time)
498 temp[4]=r%100/10+'0';
501 print(screen,x,y,temp);
506 void lights(int x,int y,int r,Uint32 pixel)
508 putpixel(screen,x,y,pixel);
511 putpixel(screen,x-1,y,pixel);
512 putpixel(screen,x+1,y,pixel);
513 putpixel(screen,x,y-1,pixel);
514 putpixel(screen,x,y+1,pixel);
518 putpixel(screen,x-2,y,pixel);
519 putpixel(screen,x+2,y,pixel);
520 putpixel(screen,x,y-2,pixel);
521 putpixel(screen,x,y+2,pixel);
522 putpixel(screen,x-1,y-1,pixel);
523 putpixel(screen,x-1,y+1,pixel);
524 putpixel(screen,x+1,y-1,pixel);
525 putpixel(screen,x+1,y+1,pixel);
530 /* launch a new race */
531 void zeRace_launch(int alltime,int go)
533 SDL_Surface *cir,*fun,*hilight;
536 int ku=0,kd=0,kl=0,kr=0,i;
538 struct _record current,best;
539 Mix_Music *light,*engine,*crash,*slide;
540 int lastsound_time=-999,lastsound=0;
544 struct _car oldnetpos[MAX_CLIENTS],newnetpos[MAX_CLIENTS];
550 SDL_FreeSurface(cir);
551 SDL_FreeSurface(fun);
554 Mix_FreeMusic(light);
555 Mix_FreeMusic(engine);
556 Mix_FreeMusic(crash);
557 Mix_FreeMusic(slide);
562 cir=IMG_Load(tracklist->full);
563 /* dark the track if it is night */
566 for (pos.x=0;pos.x<screen->w;pos.x++)
567 for (pos.y=0;pos.y<screen->h;pos.y++)
571 c=getpixel(cir,pos.x,pos.y);
572 SDL_GetRGB(c,cir->format,&r,&g,&b);
576 putpixel(cir,pos.x,pos.y,SDL_MapRGB(cir->format,r,g,b));
579 fun=IMG_Load(tracklist->function);
580 hilight=IMG_Load("sprites/light.png");
582 current.speed=car.speed=0;
583 current.angle=car.angle=tracklist->a*2*M_PI/360;
584 current.x=car.ox=car.x=tracklist->x;
585 current.y=car.oy=car.y=tracklist->y;
591 best.time=MAXRECORDKEYS;
593 memset(oldnetpos,0,MAX_CLIENTS*sizeof(struct _car));
594 memset(newnetpos,0,MAX_CLIENTS*sizeof(struct _car));
596 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")))
598 fprintf(stderr,"Mix_LoadMUS error\n");
602 /* startup countdown */
605 char startup[15]="sprites/?.png";
610 SDL_BlitSurface(cir,NULL,screen,NULL);
611 SDL_BlitSurface(cars[config.color][(unsigned char)(256*car.angle/2.0/M_PI)%256],NULL,screen,&pos);
614 temp=IMG_Load(startup);
615 pos.x=screen->w/2-temp->w/2;
616 pos.y=screen->h/2-temp->h/2;
617 SDL_BlitSurface(temp,NULL,screen,&pos);
618 SDL_FreeSurface(temp);
620 if (config.sound) if (i!=4) Mix_PlayMusic(light,1);
623 if (i!=-1) SDL_Delay(1000);
629 /* look for user interaction */
630 while (SDL_PollEvent(&event))
638 switch (event.key.keysym.sym)
643 print(screen,WIDTH/2-strlen("Disconnecting !")*5,HEIGHT/2-10,"Disconnecting !");
644 strcpy(packet->data,"disconnect");
645 packet->len=strlen(packet->data)+1;
646 SDLNet_UDP_Send(udpsock,-1,packet);
649 zeRace_send_time(&best);
653 i=event.key.keysym.sym;
654 if (i==config.up) ku=1;
655 if (i==config.down) kd=1;
656 if (i==config.left) kl=1;
657 if (i==config.right) kr=1;
660 /* display the boss screen */
662 boss=IMG_Load("sprites/boss.png");
663 SDL_BlitSurface(boss,NULL,screen,NULL);
664 SDL_FreeSurface(boss);
666 /* and wait until the user press another key */
667 for (;;) if (SDL_PollEvent(&event)) { if (event.type==SDL_KEYDOWN) break; } else SDL_Delay(10);
668 SDL_BlitSurface(cir,NULL,screen,NULL);
675 i=event.key.keysym.sym;
676 if (i==config.up) ku=0;
677 if (i==config.down) kd=0;
678 if (i==config.left) kl=0;
679 if (i==config.right) kr=0;
684 /* save pressed keys to validate best time */
685 if (current.time<MAXRECORDKEYS) current.keys[current.time]=(ku<<3 | kd<<2 | kl<<1 | kr)+'A';
686 current.keys[current.time+1]='\0';
687 /* and to send to server if needed */
690 net.keys[net.time]=(ku<<3 | kd<<2 | kl<<1 | kr)+'A';
691 net.keys[net.time+1]='\0';
695 move_car(&car,(ku<<3 | kd<<2 | kl<<1 | kr),fun);
698 /* if we are in network mode */
702 while (SDLNet_UDP_Recv(udpsock,packet)) if (strcmp(packet->data,"positions")==0)
704 int servertime,clienttime,nb;
705 servertime=SDLNet_Read32(packet->data+strlen("positions")+1);
706 clienttime=SDLNet_Read32(packet->data+strlen("positions")+1+4);
707 nb=SDLNet_Read16(packet->data+strlen("positions")+1+4+4);
708 if (clienttime>lastack)
710 memcpy(net.keys,net.keys+clienttime-lastack,net.time+1);
711 net.time-=clienttime-lastack;
712 if (clienttime>servertime+5) delay+=DELAY;
713 if (clienttime<servertime-5) delay-=DELAY;
714 if (delay<0) delay=0;
715 for (i=0;i<MAX_CLIENTS;i++) newnetpos[i].w=0;
718 newnetpos[i].w=newnetpos[i].h=30;
719 newnetpos[i].x=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14);
720 newnetpos[i].y=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2);
721 newnetpos[i].angle=(float)SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2+2)/1000;
722 newnetpos[i].color=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2+2+2);
723 newnetpos[i].lights_brake=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2+2+2+2);
724 newnetpos[i].lights_backwards=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2+2+2+2+2);
725 newnetpos[i].lights_warning=SDLNet_Read16(packet->data+strlen("positions")+1+4+4+2+i*14+2+2+2+2+2+2);
730 else if (strcmp(packet->data,"collision")==0)
734 lastack=SDLNet_Read32(packet->data+strlen("collision")+1);
735 car.x=(float)SDLNet_Read32(packet->data+strlen("collision")+1+4)/65536-100;
736 car.y=(float)SDLNet_Read32(packet->data+strlen("collision")+1+4+4)/65536-100;
737 car.speed=(float)SDLNet_Read32(packet->data+strlen("collision")+1+4+4+4)/65536-100;
738 car.angle=(float)SDLNet_Read32(packet->data+strlen("collision")+1+4+4+4+4)/65536-100;
740 else /* end of this network race */
742 zeRace_send_time(&best);
746 if (strlen(net.keys)!=0)
751 SDLNet_Write32(lastack,tmp);
753 SDLNet_Write32(car.x,tmp);
755 SDLNet_Write32(car.y,tmp);
757 strcpy(tmp,net.keys);
759 packet->len=(void *)tmp-(void *)packet->data+10;
760 if (net.time%network_speed==0) if (!SDLNet_UDP_Send(udpsock,-1,packet))
762 fprintf(stderr,"SDLNet_UDP_Send: %s\n",SDLNet_GetError());
768 /* clear the old network position */
769 if (udpsock) for (i=0;i<MAX_CLIENTS;i++) if (oldnetpos[i].w)
771 pos.x=oldnetpos[i].x-car.w/2;
772 pos.y=oldnetpos[i].y-car.h/2;
775 SDL_BlitSurface(cir,&pos,screen,&pos);
778 /* clear the old position */
779 pos.x=car.ox-car.w/2;
780 pos.y=car.oy-car.h/2;
783 SDL_BlitSurface(cir,&pos,screen,&pos);
785 /* clear the lights */
788 pos.x=car.ox-cos(car.angle)*l-sin(car.angle)*o;
789 pos.y=car.oy-sin(car.angle)*l+cos(car.angle)*o;
794 SDL_BlitSurface(cir,&pos,screen,&pos);
795 pos.x=car.ox-cos(car.angle)*l+sin(car.angle)*o;
796 pos.y=car.oy-sin(car.angle)*l-cos(car.angle)*o;
801 SDL_BlitSurface(cir,&pos,screen,&pos);
804 /* display the network car at the new position */
805 if (udpsock) for (i=0;i<MAX_CLIENTS;i++) if (newnetpos[i].w)
807 pos.x=newnetpos[i].x-car.w/2;
808 pos.y=newnetpos[i].y-car.h/2;
811 SDL_BlitSurface(cars[newnetpos[i].color][(unsigned char)(256*newnetpos[i].angle/2.0/M_PI)%256],NULL,screen,&pos);
813 /* if the car is braking, display red lights */
814 if (newnetpos[i].lights_brake)
816 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);
817 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);
820 /* if the car is going backwards, display white lights */
821 if (newnetpos[i].lights_backwards)
823 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);
824 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);
827 /* if the car is stopped, then warning */
828 if (newnetpos[i].lights_warning && alltime/75%2)
830 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);
831 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);
832 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);
833 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);
837 /* display the car at the new position */
842 SDL_BlitSurface(cars[config.color][(unsigned char)(256*car.angle/2.0/M_PI)%256],NULL,screen,&pos);
844 /* if the car is braking, display red lights */
845 if (car.lights_brake)
847 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);
848 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);
851 /* if the car is going backwards, display white lights */
852 if (car.lights_backwards)
854 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);
855 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);
858 /* if the car is stopped, then warning */
859 if (car.lights_warning && alltime/75%2)
861 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);
862 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);
863 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);
864 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);
867 /* display the lights */
870 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);
871 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);
872 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);
873 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);
874 pos.x=car.x-cos(car.angle)*l-sin(car.angle)*o;
875 pos.y=car.y-sin(car.angle)*l+cos(car.angle)*o;
880 SDL_BlitSurface(hilight,NULL,screen,&pos);
881 pos.x=car.x-cos(car.angle)*l+sin(car.angle)*o;
882 pos.y=car.y-sin(car.angle)*l-cos(car.angle)*o;
887 SDL_BlitSurface(hilight,NULL,screen,&pos);
893 for (i=0;i<MAX_CLIENTS;i++)
895 if (oldnetpos[i].w) SDL_UpdateRect(screen,oldnetpos[i].x-car.w/2,oldnetpos[i].y-car.h/2,car.w,car.h);
896 if (newnetpos[i].w) SDL_UpdateRect(screen,newnetpos[i].x-car.w/2,newnetpos[i].y-car.h/2,car.w,car.h);
898 memcpy(oldnetpos,newnetpos,MAX_CLIENTS*sizeof(struct _car));
900 SDL_UpdateRect(screen,car.ox-car.w/2,car.oy-car.h/2,car.w,car.h);
901 SDL_UpdateRect(screen,car.x-car.w/2,car.y-car.h/2,car.w,car.h);
903 /* update the lights by night */
906 pos.x=car.x-cos(car.angle)*l-sin(car.angle)*15;
907 pos.y=car.y-sin(car.angle)*l+cos(car.angle)*15;
912 if (pos.x<0) pos.x=0;
913 if (pos.y<0) pos.y=0;
914 if (pos.x+pos.w>screen->w) pos.w=screen->w-pos.x;
915 if (pos.y+pos.h>screen->h) pos.h=screen->h-pos.y;
916 SDL_UpdateRect(screen,pos.x,pos.y,pos.w,pos.h);
917 pos.x=car.x-cos(car.angle)*l+sin(car.angle)*15;
918 pos.y=car.y-sin(car.angle)*l-cos(car.angle)*15;
923 if (pos.x<0) pos.x=0;
924 if (pos.y<0) pos.y=0;
925 if (pos.x+pos.w>screen->w) pos.w=screen->w-pos.x;
926 if (pos.y+pos.h>screen->h) pos.h=screen->h-pos.y;
927 SDL_UpdateRect(screen,pos.x,pos.y,pos.w,pos.h);
930 memcpy(oldnetpos,newnetpos,MAX_CLIENTS*sizeof(struct _car));
932 /* play engine sound if no sound is currently playing */
933 if (lastsound_time+100<alltime)
936 lastsound_time=alltime;
937 if (config.sound) Mix_PlayMusic(engine,1);
940 /* if the car is fast or braking, it slides */
941 if ((kd && car.speed>0.5) || (car.speed>2.0 && !ku))
943 /* if the only sound is the engine, play the slide sound */
944 if (lastsound_time+100<alltime || lastsound<1)
947 lastsound_time=alltime;
948 if (config.sound) Mix_PlayMusic(slide,1)==-1;
951 /* display tires slide */
954 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);
955 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);
956 /* if we are braking the slide is larger */
959 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);
960 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);
966 if (car.crashflag && (lastsound_time+100<alltime || lastsound<2))
969 lastsound_time=alltime;
970 if (config.sound) Mix_PlayMusic(crash,1)==-1;
976 if (udpsock && net.time>MAX_LAG)
978 print(screen,WIDTH/2-strlen("Timeout !")*5,HEIGHT/2-10,"Timeout !");
980 zeRace_send_time(&best);
988 /* if we completed a lap */
990 printf("time = %d\"%d\n",current.time*DELAY/1000,current.time*DELAY%1000);
991 print(screen,0,0,"Last lap : ");
992 print_time(110,0,current.time);
993 SDL_UpdateRect(screen,0,0,200,19);
994 /* if it is the first turn of the best turn, save it */
995 if (best.time==-1 || current.time<best.time)
996 memcpy(&best,¤t,sizeof(struct _record));
997 /* reset turn variables */
1001 current.angle=car.angle;
1002 current.speed=car.speed;
1005 /* if we completed an incomplete lap */
1007 print(screen,0,0,"Last lap : CANCELED ");
1008 SDL_UpdateRect(screen,0,0,200,19);
1009 /* reset turn variables */
1013 /* if we miss a checkpoint */
1015 print(screen,0,0,"Checkpoint missed ! ");
1016 SDL_UpdateRect(screen,0,0,200,19);
1018 /* if we validate a missed checkpoint */
1020 print(screen,0,0,"Checkpoint missed OK");
1021 SDL_UpdateRect(screen,0,0,200,19);
1028 /* let the system breath */
1034 /* display a random splash screen at startup */
1035 void zeRace_splash()
1037 SDL_Surface *splash;
1039 char temp[20]="splashs/0.jpg";
1041 SDL_FillRect(screen,NULL,C_BLACK);
1042 temp[8]=rand()%5+'1';
1043 splash=IMG_Load(temp);
1044 pos.x=screen->w/2-splash->w/2-1;
1046 pos.y=screen->h/2-splash->h/2-1;
1048 SDL_FillRect(screen,&pos,C_WHITE);
1049 pos.x=screen->w/2-splash->w/2;
1050 pos.y=screen->h/2-splash->h/2;
1051 SDL_BlitSurface(splash,NULL,screen,&pos);
1052 print(screen,screen->w/2-strlen("zeRace " VERSION)*5,screen->h/2-splash->h/2-20,"zeRace " VERSION);
1053 SDL_FreeSurface(splash);
1059 /* menu loop to select track */
1066 SDL_Surface *full,*preview;
1068 SDL_FillRect(screen,NULL,C_BLACK);
1069 print(screen,WIDTH/2-28*5,HEIGHT/6,"* Please choose your race *");
1070 print(screen,WIDTH/2-strlen(tracklist->title)*5,5*HEIGHT/6-20,tracklist->title);
1071 print(screen,WIDTH/2-(strlen(tracklist->author)+strlen("Author : "))*5,5*HEIGHT/6+0,"Author : ");
1072 print(screen,WIDTH/2-(strlen(tracklist->author)-strlen("Author : "))*5,5*HEIGHT/6+0,tracklist->author);
1073 print(screen,WIDTH/2-( strlen("Version : ")+strlen(tracklist->version))*5,5*HEIGHT/6+20,"Version : ");
1074 print(screen,WIDTH/2-(-strlen("Version : ")+strlen(tracklist->version))*5,5*HEIGHT/6+20,tracklist->version);
1075 print(screen,WIDTH/2-( strlen("Best time : ")+6+strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40,"Best time : ");
1076 print_time (WIDTH/2-(-strlen("Best time : ")+6+strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40,tracklist->best_time);
1077 print(screen,WIDTH/2-(-strlen("Best time : ")-6+strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40," by ");
1078 print(screen,WIDTH/2-(-strlen("Best time : ")-6-strlen(" by ")+strlen(tracklist->best_pseudo))*5,5*HEIGHT/6+40,tracklist->best_pseudo);
1079 full=IMG_Load(tracklist->full);
1080 preview=(SDL_Surface *)zoomSurface(full,0.5,0.5,1);
1081 SDL_FreeSurface(full);
1082 pos.x=WIDTH/2-preview->w/2-1;
1084 pos.y=screen->h/2-preview->h/2-1;
1086 SDL_FillRect(screen,&pos,C_WHITE);
1087 pos.x=WIDTH/2-preview->w/2;
1088 pos.y=screen->h/2-preview->h/2;
1089 SDL_BlitSurface(preview,NULL,screen,&pos);
1090 SDL_FreeSurface(preview);
1097 while (SDL_PollEvent(&event))
1105 switch (event.key.keysym.sym)
1115 tracklist=tracklist->next;
1119 tracklist=tracklist->prev;
1134 void zeRace_top10(char *buf)
1138 SDL_FillRect(screen,NULL,C_BLACK);
1139 nb=SDLNet_Read16(buf);
1141 print(screen,WIDTH/2-16*5,HEIGHT/14,"* Race results *");
1144 print(screen,150,(i+3)*HEIGHT/14,buf);
1146 tmp=SDLNet_Read16(buf);
1149 pos.y=(i+3)*HEIGHT/14-8;
1150 SDL_BlitSurface(cars[tmp][0],NULL,screen,&pos);
1157 /* connect to a server */
1158 void zeRace_connect(char *host,int port)
1162 udpsock=SDLNet_UDP_Open(0);
1165 fprintf(stderr,"SDLNet_UDP_Open: %s\n",SDLNet_GetError());
1168 SDLNet_ResolveHost(&packet->address,host,port);
1170 strcpy(tmp,"connect");
1172 strcpy(tmp,config.pseudo);
1174 SDLNet_Write16(config.color,tmp);
1176 packet->len=(void *)tmp-(void *)packet->data;
1177 SDLNet_UDP_Send(udpsock,-1,packet);
1179 while (SDLNet_UDP_Recv(udpsock,packet) || lag<MAX_LAG)
1182 if (strcmp(tmp,"track")==0)
1184 struct _tracklist *loopcheck=tracklist;
1189 printf("server asked for track : %s\n",tmp);
1190 do if (strcmp(tracklist->name,tmp)==0) break; else tracklist=tracklist->next; while (tracklist!=loopcheck);
1191 if (strcmp(tracklist->name,tmp)!=0)
1193 fprintf(stderr,"unknown track : %s\n",tmp);
1197 time=SDLNet_Read32(tmp);
1199 network_speed=SDLNet_Read32(tmp);
1200 zeRace_launch(time,go);
1201 if (strcmp(packet->data,"finish")==0) zeRace_top10(packet->data+strlen(packet->data)+1);
1207 SDLNet_UDP_Close(udpsock);
1213 void zeRace_network()
1217 char server[MAXLINELENGTH]="localhost";
1219 #define NETWORK_OPTIONS 4
1223 SDL_FillRect(screen,NULL,C_BLACK);
1224 print(screen,380,HEIGHT/(NETWORK_OPTIONS+4)*(3+active),">");
1225 print(screen,WIDTH/2-18*5,HEIGHT/(NETWORK_OPTIONS+4),"* Network screen *");
1226 print(screen,400,HEIGHT/(NETWORK_OPTIONS+4)*3,"Server : ");
1227 print(screen,400+10*strlen("Server : "),HEIGHT/(NETWORK_OPTIONS+4)*3,server);
1228 print(screen,400,HEIGHT/(NETWORK_OPTIONS+4)*4,"Port : ");
1229 print(screen,400+10*strlen("Port : "),HEIGHT/(NETWORK_OPTIONS+4)*4,port);
1230 print(screen,400,HEIGHT/(NETWORK_OPTIONS+4)*(NETWORK_OPTIONS+1),"Connect");
1231 print(screen,400,HEIGHT/(NETWORK_OPTIONS+4)*(NETWORK_OPTIONS+2),"Back to main menu");
1238 while (SDL_PollEvent(&event))
1246 switch (event.key.keysym.sym)
1256 case 0: readstring(screen,400+10*strlen("Server : "),HEIGHT/(NETWORK_OPTIONS+4)*3,server,MAXLINELENGTH); break;
1257 case 1: readstring(screen,400+10*strlen("Port : "),HEIGHT/(NETWORK_OPTIONS+4)*4,port,5); break;;
1259 zeRace_connect(server,atoi(port));
1267 active--; if (active<0) active=NETWORK_OPTIONS-1;
1271 active++; if (active>NETWORK_OPTIONS-1) active=0;
1286 void zeRace_internet()
1291 "GET /zerace/servers.php HTTP/1.0\n"
1292 "Host: royale.zerezo.com\n"
1293 "User-Agent: zeRace " VERSION "\n"
1295 char response[10240],*tmp;
1299 char name[MAXLINELENGTH];
1305 #define INTERNET_OPTIONS 11
1307 if (!config.internet) return;
1309 printf("dowloading list of servers... ");
1312 if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
1314 fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
1318 tcpsock=SDLNet_TCP_Open(&ip);
1321 fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
1325 len=strlen(request);
1326 result=SDLNet_TCP_Send(tcpsock,request,len);
1328 fprintf(stderr,"SDLNet_TCP_Send: %s\n",SDLNet_GetError());
1331 len=SDLNet_TCP_Recv(tcpsock,response,10240);
1333 for (tmp=response;tmp<response+10240;tmp++) if (*tmp=='\n') *tmp='\0';
1335 while (*tmp!='\0' || *(tmp+1)!='\r') tmp++;
1339 strcpy(servers[i].name,tmp);
1341 strcpy(servers[i].ip,tmp);
1343 strcpy(servers[i].port,tmp);
1349 SDLNet_TCP_Close(tcpsock);
1354 SDL_FillRect(screen,NULL,C_BLACK);
1355 print(screen,380,HEIGHT/(INTERNET_OPTIONS+4)*(3+active),">");
1356 print(screen,WIDTH/2-19*5,HEIGHT/(INTERNET_OPTIONS+4),"* Internet screen *");
1358 print(screen,400,HEIGHT/(INTERNET_OPTIONS+4)*(i+3),servers[i].name);
1359 print(screen,400,HEIGHT/(INTERNET_OPTIONS+4)*(INTERNET_OPTIONS+2),"Back to main menu");
1366 while (SDL_PollEvent(&event))
1374 switch (event.key.keysym.sym)
1382 if (active==INTERNET_OPTIONS-1)
1385 zeRace_connect(servers[active].ip,atoi(servers[active].port));
1389 active--; if (active<0) active=INTERNET_OPTIONS-1;
1393 active++; if (active>INTERNET_OPTIONS-1) active=0;
1407 /* configuration screen */
1408 void zeRace_config()
1412 #define CONFIG_OPTIONS 14
1417 SDL_FillRect(screen,NULL,C_BLACK);
1418 print(screen,20,HEIGHT/(CONFIG_OPTIONS+4)*(3+active),">");
1419 print(screen,WIDTH/2-24*5,HEIGHT/(CONFIG_OPTIONS+4),"* Configuration screen *");
1420 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*3,"Pseudo : ");
1421 print(screen,40+10*strlen("Pseudo : "),HEIGHT/(CONFIG_OPTIONS+4)*3,config.pseudo);
1422 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*4,"Url : ");
1423 print(screen,40+10*strlen("Url : "),HEIGHT/(CONFIG_OPTIONS+4)*4,config.url);
1424 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*5,"Fullscreen : ");
1425 print(screen,40+10*strlen("Fullscreen : "),HEIGHT/(CONFIG_OPTIONS+4)*5,config.fullscreen?"Yes":"No");
1426 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*6,"Sound : ");
1427 print(screen,40+10*strlen("Sound : "),HEIGHT/(CONFIG_OPTIONS+4)*6,config.sound?"Yes":"No");
1428 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*7,"Tire : ");
1429 print(screen,40+10*strlen("Tire : "),HEIGHT/(CONFIG_OPTIONS+4)*7,config.tire?"Yes":"No");
1430 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*8,"Accelerate key : ");
1431 print(screen,40+10*strlen("Accelerate key : "),HEIGHT/(CONFIG_OPTIONS+4)*8,config.up?SDL_GetKeyName(config.up):"<press key>");
1432 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*9,"Brake key : ");
1433 print(screen,40+10*strlen("Brake key : "),HEIGHT/(CONFIG_OPTIONS+4)*9,config.down?SDL_GetKeyName(config.down):"<press key>");
1434 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*10,"Turn left key : ");
1435 print(screen,40+10*strlen("Turn left key : "),HEIGHT/(CONFIG_OPTIONS+4)*10,config.left?SDL_GetKeyName(config.left):"<press key>");
1436 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*11,"Turn right key : ");
1437 print(screen,40+10*strlen("Turn right key : "),HEIGHT/(CONFIG_OPTIONS+4)*11,config.right?SDL_GetKeyName(config.right):"<press key>");
1438 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*12,"Color : ");
1440 pos.y=HEIGHT/(CONFIG_OPTIONS+4)*12-7;
1441 SDL_BlitSurface(cars[config.color][0],NULL,screen,&pos);
1442 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*13,"Boss key : ");
1443 print(screen,40+10*strlen("Boss key : "),HEIGHT/(CONFIG_OPTIONS+4)*13,config.boss?SDL_GetKeyName(config.boss):"<press key>");
1444 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*14,"By night : ");
1445 print(screen,40+10*strlen("By night : "),HEIGHT/(CONFIG_OPTIONS+4)*14,config.bynight?"Yes":"No");
1446 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*15,"Internet : ");
1447 print(screen,40+10*strlen("Internet : "),HEIGHT/(CONFIG_OPTIONS+4)*15,config.internet?"Yes":"No");
1448 print(screen,40,HEIGHT/(CONFIG_OPTIONS+4)*(CONFIG_OPTIONS+2),"Back to main menu");
1456 while (SDL_PollEvent(&event)) switch (event.type)
1459 return event.key.keysym.sym;
1468 while (SDL_PollEvent(&event))
1476 switch (event.key.keysym.sym)
1486 case 0: readstring(screen,40+10*strlen("Pseudo : "),HEIGHT/(CONFIG_OPTIONS+4)*3,config.pseudo,MAXLINELENGTH); break;
1487 case 1: readstring(screen,40+10*strlen("Url : "),HEIGHT/(CONFIG_OPTIONS+4)*4,config.url,MAXLINELENGTH); break;;
1488 case 2: config.fullscreen=!config.fullscreen; break;
1489 case 3: config.sound=!config.sound; break;
1490 case 4: config.tire=!config.tire; break;
1491 case 5: config.up=0; update(); config.up=read_key(); break;
1492 case 6: config.down=0; update(); config.down=read_key(); break;
1493 case 7: config.left=0; update(); config.left=read_key(); break;
1494 case 8: config.right=0; update(); config.right=read_key(); break;
1496 if (event.key.keysym.sym==SDLK_LEFT) config.color--; else config.color++;
1497 if (config.color<0) config.color=NB_CARS-1;
1498 if (config.color>NB_CARS-1) config.color=0;
1500 case 10: config.boss=0; update(); config.boss=read_key(); break;
1501 case 11: config.bynight=!config.bynight; break;
1502 case 12: config.internet=!config.internet; break;
1509 active--; if (active<0) active=CONFIG_OPTIONS-1;
1513 active++; if (active>CONFIG_OPTIONS-1) active=0;
1533 #define MENU_OPTIONS 5
1538 SDL_FillRect(screen,NULL,C_BLACK);
1539 pos.x=WIDTH/2-logo->w/2;
1540 pos.y=HEIGHT/6-logo->h/2;
1541 SDL_BlitSurface(logo,NULL,screen,&pos);
1542 print(screen,650,HEIGHT/6+logo->h/3,"version " VERSION);
1543 print(screen,420,HEIGHT/(MENU_OPTIONS+4)*(3+active),">");
1544 print(screen,440,HEIGHT/(MENU_OPTIONS+4)*3,"Local game");
1545 print(screen,440,HEIGHT/(MENU_OPTIONS+4)*4,"Network game");
1546 print(screen,440,HEIGHT/(MENU_OPTIONS+4)*5,"Internet game");
1547 print(screen,440,HEIGHT/(MENU_OPTIONS+4)*6,"Configuration");
1548 print(screen,440,HEIGHT/(MENU_OPTIONS+4)*7,"Exit game");
1552 logo=IMG_Load("sprites/logo.jpg");
1556 while (SDL_PollEvent(&event))
1564 switch (event.key.keysym.sym)
1574 case 0: zeRace_local(); break;
1575 case 1: zeRace_network(); break;
1576 case 2: zeRace_internet(); break;
1577 case 3: zeRace_config(); break;
1583 active--; if (active<0) active=MENU_OPTIONS-1;
1587 active++; if (active>MENU_OPTIONS-1) active=0;
1602 int main(int argc,char *argv[])