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