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