1 /***************************************************************************
2                           car.cpp  -  description
3                              -------------------
4     begin                : Mon Jan 3 2000
5     copyright            : (C) 2000 by Perdig
6     email                : perdig@linuxbr.com.br
7 
8     $Id: car.c,v 1.4 2000/11/25 14:48:53 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 "car.h"
21 #include "graphics.h"
22 #include "game.h"
23 #include "ai.h"
24 #include "config.h"
25 
26 #ifdef USE_SOUND
27 #include "sound.h"
28 #endif
29 
30 //#define OLD_INTERSECT
31 
32 carStruct blue_car;
33 extern Image red_carImg[4];
34 extern int SPRITE_SIZE;
35 smoke_struct smoke;
36 bool crashed;
37 
38 extern int mapResult;
39 extern aiStruct AI;
40 extern mapStruct *map;
41 extern bool ENDMAP;
42 
43 int check_player_car();
44 bool fitsY(carStruct *Car);
45 bool fitsX(carStruct *Car);
46 
47 
changeDir(carStruct * Car,mapStruct * Map,int direction)48 bool changeDir(carStruct *Car, mapStruct *Map, int direction) {
49 	Car->toGoDir = direction;
50 	Car->aiguess = -1;
51 	return true;
52 }
53 
turn(carStruct * Car,mapStruct * Map)54 bool turn(carStruct *Car, mapStruct *Map) {
55 	if (Car->aiguess >= 0) {
56 		if (isClean(Map, Car, Car->aiguess, 0)) {
57 			Car->direction = Car->aiguess;
58 			Car->toGoDir = Car->direction;
59 			Car->aiguess = -1;
60 			return true;
61 		}
62 	}
63 	if (Car->toGoDir != Car->direction) {
64 		// Wants to turn
65 		// Check if it should
66 		if (isClean(Map, Car, Car->toGoDir, 0)) {
67 			Car->direction = Car->toGoDir;
68 		}
69 	}
70 	return true;
71 }
72 
carAI(carStruct * Car,mapStruct * Map)73 bool carAI(carStruct *Car, mapStruct *Map) {
74 	int a, tx, ty;
75 	if (Car->ai >= 2)
76 		return false;
77 	if (fits(Car) && Car->ai <= 0) {
78 		for (a = 0; a < 5; a++) {
79 			if (a == 4) {
80 				// Well, I give up! Turn back
81 				changeDir(Car, Map, (Car->direction==1) ? 3 : abs(Car->direction -2));
82 				break;
83 			}
84 			if (abs(a - Car->direction) != 2 && isClean(Map, Car, a, 0)) {
85 				changeDir(Car, Map, a);
86 				break;
87 			}
88 		}
89 	} else {
90 		tx = Car->x / SPRITE_SIZE;
91 		ty = Car->y / SPRITE_SIZE;
92 		switch (Car->direction) {
93 			case UP:
94 				if (passable(tx, ty-1)) {
95 					changeDir(Car, Map, LEFT);
96 					Car->aiguess = UP;
97 				} else if (passable(tx+1, ty-1)) {
98 					changeDir(Car, Map, RIGHT);
99 					Car->aiguess = UP;
100 				} else if (Car->ai<=0) {
101 					changeDir(Car, Map, RIGHT);
102 				}
103 				break;
104 			case RIGHT:
105 				if (passable(tx+1, ty)) {
106 					changeDir(Car, Map, UP);
107 					Car->aiguess = RIGHT;
108 				} else if (passable(tx+1, ty+1)) {
109 					changeDir(Car, Map, DOWN);
110 					Car->aiguess = RIGHT;
111 				} else if (Car->ai<=0) {
112 					changeDir(Car, Map, DOWN);
113 				}
114 				break;
115 			case DOWN:
116 				if (passable(tx, ty+1)) {
117 					changeDir(Car, Map, LEFT);
118 					Car->aiguess = DOWN;
119 				} else if (passable(tx+1, ty+1)) {
120 					changeDir(Car, Map, RIGHT);
121 					Car->aiguess = DOWN;
122 				} else if (Car->ai<=0) {
123 					changeDir(Car, Map, LEFT);
124 				}
125 				break;
126 			case LEFT:
127 				if (passable(tx-1, ty)) {
128 					changeDir(Car, Map, UP);
129 					Car->aiguess = LEFT;
130 				} else if (passable(tx-1, ty+1)) {
131 					changeDir(Car, Map, DOWN);
132 					Car->aiguess = LEFT;
133 				} else if (Car->ai<=0) {
134 					changeDir(Car, Map, UP);
135 				}
136 				break;
137 		}
138 	}
139 	return true;
140 }
141 
142 	/* OLD STUFF (turn and carAI)
143 	static int a = 0;
144 	int oldDir;
145 	bool test;
146 	int oldX, oldY;
147 	static bool tryOld = true;
148 	// Check if it wants to turn
149 	if (Car->toGoDir != Car->direction) {
150 		if (isClean(Map, Car, Car->toGoDir, 0) && (fits(Car) || !restrictedArea(Map, Car)))
151 				// Turns!
152 				Car->direction = Car->toGoDir;
153 		// Check if it is clean to turn and if it fits in the sprite
154 		if (Car->toGoDir == LEFT || Car->toGoDir == RIGHT) {
155 			if (isClean(Map, Car, Car->toGoDir, 0) && (fitsY(Car) || !restrictedArea(Map, Car)))
156 				// Turns!
157 				Car->direction = Car->toGoDir;
158 		} else {
159 			if (isClean(Map, Car, Car->toGoDir, 0) && (fitsX(Car) || !restrictedArea(Map, Car)))
160 				// Turns!
161 				Car->direction = Car->toGoDir;
162 		}
163 	}
164 	// Check if it is clean to go in that direction
165 	if (!isClean(Map, Car, Car->direction, 0) && fits(Car)) {
166 		// It can't stop moving!!
167 		a = (a == 3) ? a = 0 : a + 1;
168 		if ((a == (Car->direction + 2) || a == (Car->direction - 2)) && tryOld) {
169 			tryOld = false;
170 			a = (a == 3) ? a = 0 : a + 1;
171 		}
172 		changeDir(Car, Map, a);
173 		return carAI(Car, Map);
174 	}
175 	tryOld = true;
176 	*/
177 
moveXY(int * x,int * y,int dir,int offset)178 void moveXY(int *x, int *y, int dir, int offset) {
179 	switch (dir) {
180 		case UP:
181 			*y-=offset;
182 			break;
183 		case RIGHT:
184 			*x+=offset;
185 			break;
186 		case DOWN:
187 			*y+=offset;
188 			break;
189 		case LEFT:
190 			*x-=offset;
191 			break;
192 	}
193 }
194 
move(carStruct * Car,mapStruct * Map)195 bool move(carStruct *Car, mapStruct *Map) {
196 	int a;
197 	if (check_player_car()!=0)
198 		return false;
199 	for (a = 0; a < Car->vel; a++) {
200 		turn(Car, Map);
201 		if (Car->ai >= 0 && check_player_car()!=0)
202 			return false;
203 		if (isClean(Map, Car, Car->direction, 0)) {
204 			// MOVE
205 			moveXY(&Car->x, &Car->y, Car->direction, 1);
206 		} else {
207 			// EXIT
208 			carAI(Car, Map);
209 			return false;
210 		}
211 	}
212 	return true;
213 }
214 	/* OLD move STUFF
215 	// Check if it can continue moving
216 	if (!isClean(Map, Car, Car->direction, 0)  && fits(Car)) {
217 		return false;
218 	}
219 	// Move
220 	switch (Car->direction) {
221 		case UP:
222   		// Move car
223 			Car->y -= Car->vel;
224 			break;
225 		case RIGHT:
226 			// Move car
227 			Car->x += Car->vel;
228 			break;
229 		case DOWN:
230 			// Move car
231 			Car->y += Car->vel;
232 			break;
233 		case LEFT:
234 			// Move car
235 			Car->x -= Car->vel;
236 			break;
237 		default:
238 			break;
239 	}
240 	return true;
241 	*/
242 
fitsX(carStruct * Car)243 bool fitsX(carStruct *Car) {
244 	carStruct car;
245 	car = *Car;
246 	car.y = toMap(toTile(Car->y));
247 	return fits(&car);
248 }
249 
fitsY(carStruct * Car)250 bool fitsY(carStruct *Car) {
251 	carStruct car;
252 	car = *Car;
253 	car.x = toMap(toTile(Car->x));
254 	return fits(&car);
255 }
256 
fits(carStruct * Car)257 bool fits(carStruct *Car) {
258 	int a, b;
259 	a = toTile(Car->x);
260 	a = toMap(a);
261 	b = toTile(Car->y);
262 	b = toMap(b);
263 	a -= Car->x;
264 	b -= Car->y;
265 	if (abs(a) < Car->vel && abs(b) < Car->vel) {
266 		Car->x += a;
267 		Car->y += b;
268 		return true;
269 	}	else
270 		return false;
271 }
272 
check(carStruct * Car,aiStruct * AI,mapStruct * Map)273 bool check(carStruct *Car, aiStruct *AI, mapStruct *Map) {
274 	int a;
275 	// Check is its a flag
276 		if (Map->data[(toTile(Car->y)*Map->width) + toTile(Car->x)] == FLAG) {
277 			getFlag(toTile(Car->x), toTile(Car->y));
278 			#ifdef USE_SOUND
279 			play_sound (SOUND_GETFLAG);
280 			#endif
281 		}
282 
283 #ifdef PLAYABLE
284 	// Check if got by a enemy
285 	for (a = 0; a < AI->number; a++) {
286 		if (AI->order[a] != CONFUSED && intersect(Car, &AI->bot[a])) {
287 			ldbg("AI x: %d\ty: %d", AI->bot[a].x, AI->bot[a].y);
288 			ldbg("Car x: %d\ty: %d", Car->x, Car->y);
289 			crashed = true;
290 			#ifdef USE_SOUND
291 			play_sound(SOUND_CRASH);
292 			#endif
293 			return true;
294 		}
295 	}
296 
297 	// Check if its in a pit pit
298 	if (Map->data[(toTile(Car->y)*Map->width) + toTile(Car->x)] == PIT) {
299 		crashed = true;
300 #ifdef USE_SOUND
301 		play_sound(SOUND_CRASH);
302 #endif
303 		return true;
304 	}
305 #endif
306 
307 	return false;
308 
309 }
310 
intersect(carStruct * Car,carStruct * AI)311 bool intersect(carStruct *Car, carStruct *AI) {
312 #ifdef OLD_INTERSECT
313 	return abs(AI->x - Car->x) < SPRITE_SIZE && abs(AI->y - Car->y) < SPRITE_SIZE;
314 #else
315 	return (toTile(Car->x) == toTile(AI->x)) && (toTile(Car->y) == toTile(AI->y));
316 #endif
317 }
318 
carCheck(aiStruct * ai,mapStruct * map)319 void carCheck(aiStruct *ai, mapStruct *map) {
320 	int a, b, value;
321 	// Now check the enemys
322 	for (a = 0; a < ai->number; a++) {
323 		value = map->data[toTile(ai->bot[a].y)*map->width+toTile(ai->bot[a].x)];
324 		// If just come out of confusion
325 		if (ai->order[a] == CONFUSED && ai->data[a] != 0) {
326 			// Already confused. Don't mess with it
327 			continue;
328 		} else if (ai->order[a] == CONFUSED && ai->data[a] == 0) {
329 			// Just got out of confusion -> Half second to think about life
330 			ai->order[a] = WANDER;
331 			ai->data[a] = -(REST_TIME);
332 			ai->bot[a].img[ai->bot[a].direction] = &red_carImg[ai->bot[a].direction];
333 		 	changeDir(&ai->bot[a], map, random() % 4);
334 			carAI(&ai->bot[a], map);
335 			continue;
336 		} else if (ai->data[a] < 0) {
337 			// data < 0 -> Ai unvulnerable
338 			// Increase the data
339 			ai->data[a]++;
340 			continue;
341 		}
342 		// Now check if it was got by another enemy or by smoke
343 		// Check if the car hits another
344 		// This really needs to be improved
345 		for (b = 0; b < ai->number; b++) {
346 			if (a == b)
347 				continue;
348 			if (intersect(&ai->bot[a], &ai->bot[b])) {
349 //				dbg("Confusing car %d (crashed)", a);
350 				ai->order[a] = ai->order[b] = CONFUSED;
351 				// Rest for 2 seconds
352 				ai->data[a] = ai->data[b] = (CONFUSED_TIME);
353 			}
354 		}
355 		// Now check for smoke
356 		for (b = 0; b < SMOKE_NUM; b++) {
357 			if (smoke.x[b] < 0 || smoke.y[b] < 0)
358 				continue;
359 			// Car must be really got by smoke:
360 			// abs(smoke.x - car.x) <= car.vel
361 			if (abs(smoke.x[b] - ai->bot[a].x) < ai->bot[a].vel && abs(smoke.y[b] - ai->bot[a].y) < ai->bot[a].vel) {
362 //				dbg("Confusing car %d (smoked)", a);
363 				ai->order[a] = CONFUSED;
364 				// Rest for 2 seconds
365 				ai->data[a] = (CONFUSED_TIME);
366 			}
367 		}
368 		// Now check for pit pits (smart AIS won't need to check for this)
369 		if (value == PIT) {
370 				ai->order[a] = CONFUSED;
371 				// Rest for 2 seconds
372 				ai->data[a] = (CONFUSED_TIME);
373 		}
374 	}
375 
376 	// Now update the smokes
377 	for (b = 0; b < SMOKE_NUM; b++) {
378 		if (smoke.x[b] >= 0 && smoke.y[b] >= 0)
379 			smoke.left[b]--;
380    	if (smoke.left[b] < 0) {
381 			smoke.x[b] = -1;
382 			smoke.y[b] = -1;
383 		}
384 	}
385 }
386 
restrictedArea(mapStruct * Map,carStruct * Car)387 bool restrictedArea(mapStruct *Map, carStruct *Car) {
388 	// Returns true if the area is restricted (must fit to make a turn)
389 	// Check if turning up/down or left/right (never restricted)
390 	int x, y;
391 	x = toTile(Car->x);
392 	y = toTile(Car->y);
393 	if (abs(Car->direction - Car->toGoDir) == 2)
394 		return false;
395   //if (isClean(Map, Car, Car->direction, -1) && isClean(Map, Car, Car->toGoDir, -1) && isClean(Map, Car, Car->toGoDir, 0))
396 	//	return false;
397 	return true;
398 }
399 
check_player_car()400 int check_player_car() {
401   if (check(&blue_car, &AI, map) || blue_car.vel <= 0) {
402 		// LOST THE GAME !!!
403   	mapResult = RESULT_LOSE;
404 		ENDMAP = true;
405 		ldbg("Ended map... lost!");
406 		return -1;
407   }
408   if (map->flagLeft <= 0 && map->flagNum != 0) {
409 		// WON THE GAME
410 		mapResult = RESULT_WIN;
411 		ldbg("Ended map... won!");
412 		ENDMAP = true;
413 		return 1;
414 	}
415 	return 0;
416 }
417