1 /***************************************************************************
2 main.c - description
3 -------------------
4 begin : Sat Jan 1 2000
5 copyright : (C) 2000 by Perdig
6 email : perdig@linuxbr.com.br
7
8 $Id: main.c,v 1.20 2001/01/01 18:49:16 perdig Exp $
9 ***************************************************************************/
10
11 /***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 ***************************************************************************/
19
20 #include <stdio.h>
21 #include "graphics.h"
22 #include "effects.h"
23 #include "game.h"
24 #include "map.h"
25 #include "car.h"
26 #include "ai.h"
27 #include "menu.h"
28 #include "level.h"
29 #include "score.h"
30
31 #ifdef USE_SOUND
32 #include "sound.h"
33 #endif
34
35 #define MAPNUM 0
36 #define FILENAME "default.map"
37 // If defined, negative delays will make the FRAME_LEN reduce
38 //#define REARRANGE_FPS
39 #define FRAME_LEN (long)((long)1000000 / (long)FPS)
40 #define FUEL_CONSUME 1
41 #define FUELPTS_SPEEDHACK 100
42
43 #define draw_screen() do { \
44 update_offset(map, &blue_car); \
45 draw_map(map); \
46 clean_info_bar(); \
47 draw_car(&blue_car); \
48 draw_ai(&AI); \
49 draw_smoke(); \
50 draw_info_bar(); \
51 draw_radar(FULL, 0); } while (0)
52
53
54 #include <sys/time.h>
55 struct timeval st, rt;
56
57 // Function declarations
58 long time_diff(struct timeval *rt);
59 long delay(long i);
60 void mapLose();
61 void mapWin();
62 int levelWin();
63 void fuelPts();
64 int check_player_car();
65 int initRoutines();
66 int mainGameLoop();
67 void goodbyefolks();
68 void cleanLevel();
69 char *findDir();
70
71 int posX = 0, posY = 0;
72
73 extern carStruct blue_car;
74 extern carStruct red_car;
75 extern mapStruct *map;
76 extern aiStruct AI;
77 int actualFuel;
78 extern smoke_struct smoke;
79 extern bool crashed;
80 extern blend_cache_t blend_cache;
81 levelStruct level;
82
83 bool BYE = false;
84 bool QUIT = false;
85 bool USERQUIT = false;
86 bool CLEAN = true;
87 bool ENDMAP = false;
88
89 int mapNumber = MAPNUM;
90 int mapResult = 3;
91 int cnt = 0;
92
93 int lives;
94 char livesString[10];
95 char mapString[15];
96 char *hiString;
97
98 scoreStruct score;
99
100 extern time_t startime;
101
102
103
main(int argc,char * argv[])104 int main (int argc, char *argv[]) {
105 // int cnt = 0, pause = 0;
106 // char *levelFile;
107 char *dir;
108 bool EXITPROGRAM = false;
109 int cmd;
110
111 // Initialize the Display
112 dbg("Initializing display...");
113 if (!init_display("")) {
114 hdbg("can't open display");
115 return -1;
116 }
117
118 dbg("ok");
119
120 // Go to the right directories
121 dir = (char *)malloc(256);
122 findDir(dir);
123
124 dbg("Working directory is %s", dir);
125
126 dbg("Loading records from ");
127 loadRecords("hiscore");
128 dbg("ok");
129
130 #ifdef USE_SOUND
131 sound_init ();
132 dbg("Sound initialized");
133 sound_load ("rally.mod");
134 sound_switch ();
135 sound_select (0);
136 sound_volume(64);
137 sound_start();
138 #endif
139
140 atexit (goodbyefolks);
141
142 init_screen();
143 initIntro();
144 introAnim();
145 //
146 // Init blend cache
147 blend_cache.color.red = blend_cache.color.blue = blend_cache.color.green = 0;
148 blend_cache.factor = 60;
149 init_blend_cache(&blend_cache);
150
151 while (!EXITPROGRAM) {
152 cmd = get_user_option();
153 switch(cmd) {
154 case PLAY:
155 while (initRoutines() == 0);
156 break;
157 case EXIT:
158 EXITPROGRAM = true;
159 break;
160 case HISCORES:
161 displayHiscores(-1, &level, true);
162 break;
163 }
164 }
165
166 goodbyefolks();
167
168 free_blend_cache(&blend_cache);
169
170 return 0;
171 }
172
initRoutines()173 int initRoutines() {
174 char *levelFile;
175 struct splash_struct splScr;
176
177 levelFile = chooseLevel();
178 if (!levelFile)
179 return -1;
180 dbg("Level file: %s", levelFile);
181
182 sprintf(splScr.txt, "Loading %s...", levelFile);
183 splScr.n = 9;
184 splScr.mode = 0;
185 splash(splScr, levelFile);
186
187 // Load the level
188 if (!loadLevel(&level, levelFile)) {
189 hdbg("Can't load level!");
190 //initRoutines();
191 return -1;
192 }
193 lives = level.lives;
194
195 dbg("Level loaded");
196
197 sprintf(splScr.txt, "Loading %s tileset...", level.tileset);
198 splScr.n = 15 + (level.pics - 5);
199 splScr.mode = 1;
200 splash(splScr, levelFile);
201
202 // Load the Pixmaps
203 if (load_images() < 0) {
204 hdbg("Can't load images");
205 //initRoutines();
206 return -1;
207 }
208
209 dbg("Images loaded");
210
211 mapNumber = 0;
212
213 if (level.lives <= 0 && (mapNumber = chooseMap(level.number)) < 0) {
214 //initRoutines();
215 return -1;
216 }
217
218
219 if (lives >= 0)
220 sprintf(livesString, "Lives: %d", lives);
221 else
222 sprintf(livesString, "No Lives!");
223
224 // Init score
225 sprintf(score.string, "%.*u", SCORE_DIGITS - 1, 0);
226 score.value = 0;
227 score.bonus = 1;
228 score.num_extra = 1;
229 score.current_extra = level.extra;
230
231 startime = time(0);
232
233 cleanScr();
234 while (mainGameLoop() == 0);
235 cleanLevel();
236
237 return 0;
238 }
239
mainGameLoop()240 int mainGameLoop() {
241 long int pause = 0;
242
243 ENDMAP = false;
244 mapResult = 3;
245
246 if (createMap(&level, mapNumber) < 0) {
247 hdbg("Can't load map!");
248 return -1;
249 }
250
251 dbg("Map %d Loaded", MAPNUM);
252
253 ldbg("Flag num: %d", map->flagNum);
254
255 blue_car.x = toMap(map->startX);
256 blue_car.y = toMap(map->startY);
257 blue_car.direction = map->startDir;
258 blue_car.aiguess = -1;
259 blue_car.toGoDir = map->startDir;
260 blue_car.vel = map->carVel;
261 for (cnt = 0; cnt < SMOKE_NUM; cnt++) {
262 smoke.x[cnt] = -1;
263 smoke.y[cnt] = -1;
264 smoke.left[cnt] = 0;
265 smoke.current = 0;
266 }
267 update_offset(map, &blue_car);
268 createAI(&AI, map);
269
270 gettimeofday(&rt, NULL);
271
272 // Starting random number generator
273 srandom(rt.tv_usec);
274
275
276 #ifdef USE_SOUND
277 sound_switch ();
278 sound_select(0);
279 sound_volume(0);
280 sound_start();
281 #endif
282
283 cleanScr();
284
285 // Get hiscore
286 hiString = getRecord(&level);
287
288 sprintf(mapString, "Map: %3d of %3d", mapNumber+1, level.number);
289 // Starting sequence
290 for (cnt = 0; cnt < 3; cnt++) {
291 wait_time(1000000);
292 draw_screen();
293 draw_light(cnt);
294 #ifdef USE_SOUND
295 play_sound (SOUND_LIGHT);
296 #endif
297 graphics_sync();
298 }
299
300 QUIT = false;
301 USERQUIT = false;
302
303 #ifdef USE_SOUND
304 sound_select(0);
305 sound_volume(level.volume);
306 #endif
307
308 // REAL GAME LOOP!
309
310 gettimeofday(&st, NULL);
311 cnt = 0;
312 sprintf(mapString, "Map: %3d of %3d", mapNumber+1, level.number);
313 while (!ENDMAP) {
314 gettimeofday(&rt, NULL);
315 // Playing the game!
316 graphics_sync();
317 cnt++;
318 /* Control car routines start */
319 process_input(check_key());
320 // Move player car
321 move(&blue_car, map);
322 // Check for disasters (AI crash AI or AI crash smoke)
323 // Also update the smokes
324 carCheck(&AI, map);
325 // AI take their reactions
326 orderAI(&AI, map);
327 moveAI(&AI, map);
328 /* Control car routines end */
329
330 /* Drawing routines start */
331 update_offset(map, &blue_car);
332 //cleanScr();
333 draw_map(map);
334 draw_car(&blue_car);
335 draw_ai(&AI);
336 draw_smoke();
337 clean_info_bar();
338 draw_info_bar();
339
340 if (map->radarmode != FUELSONAR)
341 draw_radar(map->radarmode, map->radarrange);
342 else
343 draw_radar(SONAR, map->radarrange * (actualFuel<0?0:actualFuel) / map->startFuel);
344
345 // Draw the lights for 1 second)
346 if (cnt < FPS)
347 draw_light(2);
348 /* Drawing routines end */
349
350 // check_player_car();
351
352
353 actualFuel-=FUEL_CONSUME;
354 if (actualFuel < 0) {
355 blue_car.vel = map->carVel + (actualFuel/map->fuelImpact);
356 }
357
358 delay(FRAME_LEN - time_diff(&rt));
359 }
360
361 pause = time_diff(&st);
362 // Ok, ended map!! Now lets do stuff
363 ldbg("Ended map... doing stuff");
364 dbg("Number of cycles: %d", cnt);
365 dbg("Expected frame len: %ld", FRAME_LEN);
366 dbg("Real frame len: %ld", pause/cnt);
367
368 if (mapResult == RESULT_WIN) {
369 // WON THE GAME
370 #ifdef USE_SOUND
371 sound_volume(0);
372 #endif
373 draw_screen();
374 graphics_sync();
375
376 ldbg("Won game");
377 sound_switch (); /* Switch to load in the right slot */
378 mapWin();
379 // Fool the fps counter
380 gettimeofday(&rt, NULL);
381 } else if (mapResult == RESULT_LOSE) {
382 // LOST THE GAME !!!
383 #ifdef USE_SOUND
384 sound_volume(0);
385 #endif
386 draw_screen();
387 graphics_sync();
388 wait_time(1000000);
389 crashed = false;
390 // Fool the fps counter
391 gettimeofday(&rt, NULL);
392 ldbg("Lost game");
393 sound_switch (); /* Switch to load in the right slot */
394 mapLose();
395 } else if (mapResult == RESULT_QUIT) {
396 // QUITED the game
397 #ifdef USE_SOUND
398 sound_volume(0);
399 #endif
400 crashed = false;
401 gettimeofday(&rt, NULL);
402 }
403
404
405 // If its the case, quit the game
406 if (level.lives <= 0 && (mapNumber = chooseMap(level.number))<0) {
407 return -1;
408 }
409
410 // Check if the game is over
411 if (level.lives > 0 && (lives < 0 || mapResult == RESULT_QUIT)) {
412 sound_stop ();
413 sound_switch ();
414 dbg ("Add the quit tune here");
415 sound_volume (0);
416 sound_start ();
417 endScreen(&level, score.value, GAME_OVER);
418 return -1;
419 }
420 if (mapNumber >= level.number) {
421 endScreen(&level, levelWin(), VICTORY);
422 dbg ("Add the victory tune here");
423 return -1;
424 }
425 //mainGameLoop(); /* CM: Recursive function ?!? */
426 return 0;
427 }
428
cleanLevel()429 void cleanLevel() {
430
431 dbg("Ending frame len: %ld", FRAME_LEN);
432 dbg("Playing time: %d seconds", cnt / FPS);
433
434 destroyAI();
435 free(level.map);
436 level.number = 0;
437 level.map = NULL;
438 fclose(level.file);
439 level.file = NULL;
440
441 }
442
goodbyefolks()443 void goodbyefolks() {
444
445 static int once = 0;
446
447 if (once)
448 return;
449
450 once++;
451
452 #ifdef USE_SOUND
453 sound_stop();
454 dbg("Sound deinitialized");
455 sound_deinit();
456 #endif
457 close_display();
458 saveRecords("hiscore");
459
460 return;
461 }
462
463 #ifdef USE_SOUND
play_sound(int i)464 void play_sound (int i)
465 {
466 sound_play (i, 60, -1);
467 }
468 #endif
469
470
delay(long i)471 long delay(long i)
472 {
473 struct timeval timeout;
474 if (i > 0) {
475 timeout.tv_usec = i % (long) 1000000;
476 timeout.tv_sec = i / (long) 1000000;
477 select(0, NULL, NULL, NULL, &timeout);
478 }
479 return i;
480 }
481
time_diff(struct timeval * ot)482 long time_diff(struct timeval *ot)
483 {
484 long diff;
485 struct timeval nt;
486
487 gettimeofday(&nt, NULL);
488 diff = ((long)1000000*(long)(nt.tv_sec-ot->tv_sec))+(long)(nt.tv_usec-ot->tv_usec);
489 return diff;
490 }
491
mapLose()492 void mapLose() {
493 if (map->bonus) {
494 mapNumber++;
495 } else if (level.lives > 0) {
496 lives--;
497 sprintf(livesString, "Lives: %d", lives);
498 }
499 score.last = 0;
500 }
501
levelWin()502 int levelWin() {
503 int oldscore = score.value;
504 // Add livesbonus to score
505 if (level.livesbonus < 0 && (lives-level.lives) > 0)
506 add_score(&score, &level, (lives-level.lives)*(-1)*level.livesbonus);
507 else if (level.livesbonus > 0)
508 add_score(&score, &level, lives * level.livesbonus);
509 return oldscore;
510 }
511
512
mapWin()513 void mapWin() {
514 // Adjust the score
515 fuelPts();
516 score.last = 0;
517 score.timer = 0;
518 // Won the game! Next map!
519 if (level.lives > 0)
520 mapNumber++;
521 }
522
fuelPts()523 void fuelPts() {
524 int a = 0;
525 if (map->fuelpts == 0)
526 return;
527 // The actual thing to is:
528 // if (map->fuelpts > 0)
529 // score.value += (actualFuel / map->fuelpts);
530 // else
531 // score.value += abs(actualFuel * map->fuelpts);
532 // But lets add some nice effects
533 while (actualFuel > 0) {
534 a++;
535 actualFuel--;
536 if (map->fuelpts > 0 && a % map->fuelpts == 0) // Fuel for points
537 add_score(&score, &level, 1);
538 else if (map->fuelpts < 0) // Points for fuel
539 add_score(&score, &level, -map->fuelpts); // Remenber that fuelpts is negative
540 if (a % (map->startFuel/FUELPTS_SPEEDHACK) == 0) {
541 draw_screen();
542 graphics_sync();
543 }
544 }
545 draw_screen();
546 graphics_sync();
547 }
548
findDir(char * buf)549 char *findDir(char *buf) {
550 bool foreign = false;
551 int a = 0;
552 // Look for maps subdirectory
553 foreign = chdir("maps");
554 chdir("..");
555 // Look for sounds subdirectory
556 foreign = chdir("sounds");
557 chdir("..");
558 // Look for images subdirectory
559 foreign = chdir("images");
560 chdir("..");
561 if (foreign)
562 a = chdir(GAMEDIR);
563 if (a < 0)
564 hdbg("Can't find the game data anywhere! Expect serious problems");
565 return getcwd(buf, 256);
566 }
567
568