1 #include <math.h>
2 #include <float.h>
3 
4 #include "video/video.h"
5 #include "game/game.h"
6 #include "audio/audio.h"
7 #include "Nebu_scripting.h"
8 
9 /*! \fn int processEvent(GameEvent* e)
10   handle events, e.g.
11   left/right/crash/stop
12 
13   frees e
14 */
15 
processEvent(GameEvent * e)16 int processEvent(GameEvent* e) {
17   int value = 0;
18 
19 #ifdef RECORD
20   if(game2->mode == GAME_SINGLE_RECORD) {
21     writeEvent(e);
22   }
23 #endif
24   switch(e->type) {
25   case EVENT_TURN_LEFT:
26     doTurn(e, TURN_LEFT);
27     break;
28   case EVENT_TURN_RIGHT:
29     doTurn(e, TURN_RIGHT);
30     break;
31   case EVENT_CRASH:
32     displayMessage(TO_CONSOLE, "player %d crashed", e->player + 1);
33     doCrashPlayer(e);
34     break;
35   case EVENT_STOP:
36     // displayMessage(TO_STDOUT, "game stopped");
37 #ifdef RECORD
38     if(game2->mode == GAME_SINGLE_RECORD) {
39       stopRecording();
40       game2->mode = GAME_SINGLE;
41     } else if(game2->mode == GAME_PLAY) {
42       stopPlaying();
43       game2->mode = GAME_SINGLE;
44     }
45 #endif
46     if(e->player<PLAYERS && game->player[e->player].ai->active != AI_NONE) {
47       game->winner = e->player;
48       displayMessage(TO_CONSOLE, "winner: %d", game->winner + 1);
49     } else {
50       game->winner = -2;
51       displayMessage(TO_CONSOLE, "everyone died! no one wins!");
52     }
53 		SystemExitLoop(RETURN_GAME_END);
54     game->pauseflag = PAUSE_GAME_FINISHED;
55     value = 1;
56     break;
57   }
58   free(e);
59   return value;
60 }
61 
crashTestPlayers(int i,const segment2 * movement)62 int crashTestPlayers(int i, const segment2 *movement) {
63 	int j, k;
64 	int crash = 0;
65 	Data *data = game->player[i].data;
66 	segment2 *current = data->trails + data->trailOffset;
67 	// debug: only test player0 against himself
68 	// j = 0;
69 	// if(i == 0) {
70 	for(j = 0; j < game->players; j++) {
71 		int crash = 0;
72 
73 		if(game->player[j].data->trail_height < TRAIL_HEIGHT)
74 			continue;
75 
76 		for(k = 0; k < game->player[j].data->trailOffset + 1; k++) {
77 			segment2 *wall;
78 			vec2 v;
79 			float t1, t2;
80 
81 			if(j == i && k >= game->player[j].data->trailOffset - 1)
82 				break;
83 
84 			wall = game->player[j].data->trails + k;
85 
86 			if(segment2_Intersect(&v, &t1, &t2, movement, wall)) {
87 #if 0
88 				printf("(%.2f, %.2f), (%.2f, %.2f), %.2f, %.2f\n",
89 							 data->posx, data->posy,
90 							 v.v[0], v.v[1],
91 							 t1, t2);
92 #endif
93 				if(t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) {
94 					current->vDirection.v[0] = v.v[0] - current->vStart.v[0];
95 					current->vDirection.v[1] = v.v[1] - current->vStart.v[1];
96 					createEvent(i, EVENT_CRASH);
97 					crash = 1;
98 					break;
99 				}
100 			}
101 		}
102 		if(crash)
103 			break;
104 	}
105 	return crash;
106 }
107 
crashTestWalls(int i,const segment2 * movement)108 int crashTestWalls(int i, const segment2 *movement) {
109 	int j;
110 	vec2 v;
111 	float t1, t2;
112 	int crash = 0;
113 
114 	Data *data = game->player[i].data;
115 	segment2 *current = data->trails + data->trailOffset;
116 
117 	for(j = 0; j < 4; j++) {
118 		if(segment2_Intersect(&v, &t1, &t2, current, walls + j)) {
119 			if(t1 >= 0 && t1 < 1 && t2 >= 0 && t2 < 1) {
120 				current->vDirection.v[0] = v.v[0] - current->vStart.v[0];
121 				current->vDirection.v[1] = v.v[1] - current->vStart.v[1];
122 				createEvent(i, EVENT_CRASH);
123 				crash = 1;
124 				break;
125 			}
126 		}
127 	}
128 	return crash;
129 }
130 
applyBooster(int player,int dt)131 int applyBooster(int player, int dt) {
132 	Data *data = game->player[player].data;
133 	if(data->booster > 0 && data->boost_enabled) {
134 		float boost = getSettingf("booster_use") * dt / 1000.0f;
135 		if(boost > data->booster) {
136 			boost = data->booster;
137 			data->boost_enabled = 0;
138 		}
139 		data->speed += boost;
140 		data->booster -= boost;
141 		return 1;
142 	}
143 	else {
144 		float booster_max = getSettingf("booster_max");
145 		if(data->booster < booster_max) {
146 			data->booster += getSettingf("booster_regenerate") * dt / 1000.0f;
147 			if(data->booster > booster_max)
148 				data->booster = booster_max;
149 		}
150 		return 0;
151 	}
152 }
153 
applyDecceleration(int player,int dt,float factor)154 void applyDecceleration(int player, int dt, float factor) {
155 	Data *data = game->player[player].data;
156 	if(data->speed > game2->rules.speed) {
157 		float decrease = factor * dt / 1000.0f;
158 		data->speed -= decrease;
159 		if(data->speed < game2->rules.speed)
160 			data->speed = game2->rules.speed;
161 	}
162 }
163 
applyWallAcceleration(int player,int dt)164 int applyWallAcceleration(int player, int dt) {
165 	// find distance to enemy walls left & right
166 	enum { eLeft, eRight, eMax };
167 	segment2 segments[eMax];
168 
169 	Data *data = game->player[player].data;
170 	int dirLeft = (data->dir + 3) % 4;
171 	int dirRight = (data->dir + 1) % 4;
172 
173 	float left, right;
174 
175 	float x, y;
176 	vec2 vPos;
177 
178 	int i, j;
179 
180 	getPositionFromIndex(&x, &y, player);
181 	vPos.v[0] = x;
182 	vPos.v[1] = y;
183 
184 	for(i = 0; i < eMax; i++) {
185 		vec2Copy(&segments[i].vStart, &vPos);
186 	}
187 
188 	segments[eLeft].vDirection.v[0] = dirsX[dirLeft];
189 	segments[eLeft].vDirection.v[1] = dirsY[dirLeft];
190 	segments[eRight].vDirection.v[0] = dirsX[dirRight];
191 	segments[eRight].vDirection.v[1] = dirsY[dirRight];
192 
193 	left = FLT_MAX;
194 	right = FLT_MAX;
195 
196 	for(i = 0; i < game->players; i++) {
197 		segment2 *wall = game->player[i].data->trails;
198 
199 		if(i == player)
200 			continue;
201 		if(game->player[i].data->trail_height < TRAIL_HEIGHT)
202 			continue;
203 
204 		for(j = 0; j < game->player[i].data->trailOffset + 1; j++) {
205 			float t1, t2;
206 			vec2 v;
207 			if(segment2_Intersect(&v, &t1, &t2, segments + eLeft, wall) &&
208 				 t1 > 0 && t1 < left && t2 >= 0 && t2 <= 1)
209 				left = t1;
210 			if(segment2_Intersect(&v, &t1, &t2, segments + eRight, wall) &&
211 				 t1 > 0 && t1 < right && t2 >= 0 && t2 <= 1)
212 				right = t1;
213 			wall++;
214 		}
215 	}
216 
217 	{
218 		float accell_limit = getSettingf("wall_accel_limit");
219 		if(left < accell_limit || right < accell_limit) {
220 			float boost = getSettingf("wall_accel_use") * dt / 1000.0f;
221 			data->speed += boost;
222 			return 1;
223 		} else {
224 			return 0;
225 		}
226 	}
227 }
228 
229 /*! \fn static list* doMovement(int mode, int dt)
230   do physics, create CRASH and STOP events
231 */
232 
doMovement(int mode,int dt)233 List* doMovement(int mode, int dt) {
234   int i;
235   List *l = NULL;
236 
237   for(i = 0; i < game->players; i++) {
238 		Data *data = game->player[i].data;
239 		PlayerVisual *pV = gPlayerVisuals + i;
240     if(data->speed > 0) { /* still alive */
241 			float fs;
242 			float t;
243 
244 			// speed boost:
245 			float deccel = 0;
246 			if(getSettingf("wall_accel_on") == 1) {
247 				if(!applyWallAcceleration(i, dt)) {
248 					deccel = getSettingf("wall_accel_decrease");
249 				}
250 				else {
251 					deccel = -1; // forbid deacceleration for booster
252 				}
253 			}
254 			if(getSettingf("booster_on") == 1) {
255 				if(!applyBooster(i, dt) && deccel != -1) {
256 					float d = getSettingf("booster_decrease");
257 					deccel = d > deccel ? d : deccel;
258 				} else {
259 					deccel = -1;
260 				}
261 			}
262 			if(deccel > 0)
263 				applyDecceleration(i, dt, deccel);
264 
265 			// if(i == 0)
266 			// printf("speed: %.2f, boost: %.2f\n", data->speed, data->booster);
267 
268       fs = 1.0f - SPEED_OZ_FACTOR + SPEED_OZ_FACTOR *
269 				cosf(i * PI / 4.0f +
270 						(game2->time.current % SPEED_OZ_FREQ) *
271 						2.0f * PI / SPEED_OZ_FREQ);
272 
273       t = dt / 100.0f * data->speed * fs;
274 
275 			{
276 				segment2 *current = data->trails + data->trailOffset;
277 				segment2 movement;
278 				int crash = 0;
279 				float x, y;
280 
281 				getPositionFromData(&x, &y, data);
282 				movement.vStart.v[0] = x;
283 				movement.vStart.v[1] = y;
284 				movement.vDirection.v[0] = t * dirsX[data->dir];
285 				movement.vDirection.v[1] = t * dirsY[data->dir];
286 
287 				current->vDirection.v[0] += t * dirsX[data->dir];
288 				current->vDirection.v[1] += t * dirsY[data->dir];
289 
290 				crash = crash || crashTestPlayers(i, &movement);
291 				crash = crash || crashTestWalls(i, &movement);
292 			}
293     } else { /* already crashed */
294       if(game2->rules.eraseCrashed == 1 && data->trail_height > 0)
295 				data->trail_height -= (dt * TRAIL_HEIGHT) / 1000.0f;
296       if(pV->exp_radius < EXP_RADIUS_MAX)
297 				pV->exp_radius += dt * EXP_RADIUS_DELTA;
298       else if (data->speed == SPEED_CRASHED) {
299 				int winner = -1;
300 
301 				data->speed = SPEED_GONE;
302 				game->running--;
303 				if(game->running <= 1) { /* all dead, find survivor */
304 					int i, maxSpeed = SPEED_GONE;
305 					/* create winner event */
306 					for(i = 0; i < game->players; i++) {
307 						if(game->player[i].data->speed >= maxSpeed) {
308 							winner = i;
309 							maxSpeed = game->player[i].data->speed;
310 						}
311 					}
312 					if(mode) {
313             createEvent(winner, EVENT_STOP);
314 						/* a stop event is the last event that happens */
315 						return l;
316 					}
317 				}
318       }
319     }
320   }
321   return l;
322 }
323 
324 /*! \fn void idleGame( void )
325   game loop:
326   run ai, process events, do physics, process events again,
327   do camera movement
328 */
329 
Game_Idle(void)330 void Game_Idle(void) {
331   List *l;
332   List *p;
333   int i;
334   int dt;
335   int t;
336 
337 	switch(game2->mode) {
338 	case GAME_SINGLE:
339 #ifdef RECORD
340 	case GAME_SINGLE_RECORD:
341 #endif
342 		/* check for fast finish */
343 
344 		if (gSettingsCache.fast_finish == 1) {
345 			int factors[4] = { 4, 6, 12, 25 };
346 			int threshold[4] = { 0, 300, 600, 800 };
347 			int factor = 1;
348 			for(i = 0; i < 4; i++) {
349 				if(game2->rules.grid_size > threshold[i])
350 					factor = factors[i];
351 			}
352 			for (i = 0; i < game->players; i++) {
353 				if (game->player[i].ai->active != AI_COMPUTER &&
354 						gPlayerVisuals[i].exp_radius < EXP_RADIUS_MAX) {
355 					factor = 1;
356 				}
357 			}
358 			dt = game2->time.dt * factor;
359 		} else {
360 			dt = game2->time.dt;
361 		}
362 
363 		while(dt > 0) {
364 			if(dt > PHYSICS_RATE) t = PHYSICS_RATE;
365 			else t = dt;
366 
367 			/* run AI */
368 			for(i = 0; i < game->players; i++)
369 				if(game->player[i].ai != NULL)
370 					if(game->player[i].ai->active == AI_COMPUTER &&
371 						 PLAYER_IS_ACTIVE(&game->player[i])) {
372 						doComputer(i, 0);
373 					}
374 
375 			/* process any outstanding events (turns, etc) */
376 			for(p = &(game2->events); p->next != NULL; p = p->next) {
377 				if(processEvent((GameEvent*) p->data)) return;
378 			}
379 
380 			/* free events */
381 			p = game2->events.next;
382 			while(p != NULL) {
383 				l = p;
384 				p = p->next;
385 				free(l);
386 			}
387 			game2->events.next = NULL;
388 
389 			l = doMovement(1, t); /* this can generate new events */
390 			if(l != NULL) {
391 				for(p = l; p->next != NULL; p = p->next) {
392 					if(processEvent((GameEvent*) p->data));
393 				}
394 
395 			}
396 			/* free list  */
397 			p = l;
398 			while(p != NULL) {
399 				l = p;
400 				p = p->next;
401 				free(l);
402 			}
403 			dt -= PHYSICS_RATE;
404 		}
405 		break;
406 #ifdef RECORD
407 	case GAME_PLAY_NETWORK:
408 		/* fall through to GAME_PLAY */
409 	case GAME_PLAY:
410 		getEvents();
411 		l = doMovement(0, game2->time.dt); /* this won't generate new events */
412 		if(l != NULL) {
413 			fprintf(stderr, "something is seriously wrong - ignoring events\n");
414 		}
415 		break;
416 #endif /* RECORD */
417 	}
418 
419 	doCameraMovement();
420 	doRecognizerMovement();
421 }
422 
423 /*! \fn void createEvent(int player, event_type_e eventType)
424   helper function to create an event struct and insert it into the
425   global event queue
426 */
427 
createEvent(int player,event_type_e eventType)428 void createEvent(int player, event_type_e eventType) {
429   GameEvent *e;
430   List *p = &(game2->events);
431 
432   /* move to the end of the event list */
433   while (p->next)
434     p = p->next;
435 
436   /* TODO: handle failed malloc */
437   e = (GameEvent*) malloc(sizeof(GameEvent));
438   p->data = e;
439   p->next = (List*) malloc(sizeof(List));
440   p->next->next = NULL;
441   e->type = eventType;
442 	getPositionFromIndex(&e->x, &e->y, player);
443   e->player = player;
444   e->timestamp = game2->time.current;
445 }
446