2 * Copyright (C) 2003 Robert Kooima
4 * NEVERBALL is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
31 #include "solid_draw.h"
33 #include "game_client.h"
34 #include "game_common.h"
35 #include "game_proxy.h"
36 #include "game_draw.h"
40 /*---------------------------------------------------------------------------*/
42 int game_compat_map; /* Client/server map compat flag */
44 /*---------------------------------------------------------------------------*/
49 static struct game_draw gd;
50 static struct game_lerp gl;
52 static float timer = 0.0f; /* Clock time */
53 static int status = GAME_NONE; /* Outcome of the game */
54 static int coins = 0; /* Collected coins */
56 static struct cmd_state cs; /* Command state */
61 } version; /* Current map version */
63 /*---------------------------------------------------------------------------*/
65 static void game_run_cmd(const union cmd *cmd)
69 struct game_view *view = &gl.view[CURR];
70 struct game_tilt *tilt = &gl.tilt[CURR];
72 struct s_vary *vary = &gd.vary;
86 case CMD_END_OF_UPDATE:
93 /* Hack to sync state before the next update. */
94 game_lerp_apply(&gl, &gd);
99 /* Compute gravity for particle effects. */
101 if (status == GAME_GOAL)
102 game_tilt_grav(v, GRAVITY_UP, tilt);
104 game_tilt_grav(v, GRAVITY_DN, tilt);
106 /* Step particle, goal and jump effects. */
112 if (gd.goal_e && gl.goal_k[CURR] < 1.0f)
113 gl.goal_k[CURR] += dt;
117 gl.jump_dt[CURR] += dt;
119 if (1.0f < gl.jump_dt[PREV])
129 /* Allocate a new ball and mark it as the current ball. */
131 if (sol_lerp_cmd(&gl.lerp, &cs, cmd))
132 cs.curr_ball = gl.lerp.uc - 1;
137 /* Allocate and initialise a new item. */
139 if ((hp = realloc(vary->hv, sizeof (*hp) * (vary->hc + 1))))
143 v_cpy(h.p, cmd->mkitem.p);
149 vary->hv[vary->hc] = h;
156 /* Set up particle effects and discard the item. */
158 assert(cmd->pkitem.hi < vary->hc);
160 hp = vary->hv + cmd->pkitem.hi;
163 part_burst(hp->p, v);
169 case CMD_TILT_ANGLES:
170 if (!cs.got_tilt_axes)
173 * Neverball <= 1.5.1 does not send explicit tilt
174 * axes, rotation happens directly around view
175 * vectors. So for compatibility if at the time of
176 * receiving tilt angles we have not yet received the
177 * tilt axes, we use the view vectors.
180 game_tilt_axes(tilt, view->e);
183 tilt->rx = cmd->tiltangles.x;
184 tilt->rz = cmd->tiltangles.z;
188 /* Play the sound. */
191 audio_play(cmd->sound.n, cmd->sound.a);
196 timer = cmd->timer.t;
200 status = cmd->status.t;
204 coins = cmd->coins.n;
210 gl.jump_dt[PREV] = 0.0f;
211 gl.jump_dt[CURR] = 0.0f;
219 sol_lerp_cmd(&gl.lerp, &cs, cmd);
223 sol_lerp_cmd(&gl.lerp, &cs, cmd);
228 * Enable the goal and make sure it's fully visible if
229 * this is the first update.
235 gl.goal_k[CURR] = cs.first_update ? 1.0f : 0.0f;
240 vary->xv[cmd->swchenter.xi].e = 1;
243 case CMD_SWCH_TOGGLE:
244 vary->xv[cmd->swchtoggle.xi].f = !vary->xv[cmd->swchtoggle.xi].f;
248 vary->xv[cmd->swchexit.xi].e = 0;
251 case CMD_UPDATES_PER_SECOND:
255 case CMD_BALL_RADIUS:
256 sol_lerp_cmd(&gl.lerp, &cs, cmd);
259 case CMD_CLEAR_ITEMS:
268 case CMD_CLEAR_BALLS:
269 sol_lerp_cmd(&gl.lerp, &cs, cmd);
272 case CMD_BALL_POSITION:
273 sol_lerp_cmd(&gl.lerp, &cs, cmd);
277 sol_lerp_cmd(&gl.lerp, &cs, cmd);
280 case CMD_BALL_PEND_BASIS:
281 sol_lerp_cmd(&gl.lerp, &cs, cmd);
284 case CMD_VIEW_POSITION:
285 v_cpy(view->p, cmd->viewpos.p);
288 case CMD_VIEW_CENTER:
289 v_cpy(view->c, cmd->viewcenter.c);
293 v_cpy(view->e[0], cmd->viewbasis.e[0]);
294 v_cpy(view->e[1], cmd->viewbasis.e[1]);
295 v_crs(view->e[2], view->e[0], view->e[1]);
298 case CMD_CURRENT_BALL:
299 cs.curr_ball = cmd->currball.ui;
303 vary->pv[cmd->pathflag.pi].f = cmd->pathflag.f;
306 case CMD_STEP_SIMULATION:
307 sol_lerp_cmd(&gl.lerp, &cs, cmd);
312 * Note a version (mis-)match between the loaded map and what
313 * the server has. (This doesn't actually load a map.)
315 game_compat_map = version.x == cmd->map.version.x;
319 cs.got_tilt_axes = 1;
320 v_cpy(tilt->x, cmd->tiltaxes.x);
321 v_cpy(tilt->z, cmd->tiltaxes.z);
331 void game_client_sync(fs_file demo_fp)
335 while ((cmdp = game_proxy_deq()))
338 cmd_put(demo_fp, cmdp);
346 /*---------------------------------------------------------------------------*/
348 int game_client_init(const char *file_name)
350 char *back_name = "", *grad_name = "";
356 game_client_free(file_name);
360 if (!game_base_load(file_name))
361 return (gd.state = 0);
363 if (!sol_load_vary(&gd.vary, &game_base))
365 game_base_free(NULL);
366 return (gd.state = 0);
369 if (!sol_load_draw(&gd.draw, &gd.vary, config_get_d(CONFIG_SHADOW)))
371 sol_free_vary(&gd.vary);
372 game_base_free(NULL);
373 return (gd.state = 0);
378 /* Initialize game state. */
380 game_tilt_init(&gd.tilt);
381 game_view_init(&gd.view);
390 /* Initialize interpolation. */
392 game_lerp_init(&gl, &gd);
394 /* Initialize fade. */
399 /* Load level info. */
404 for (i = 0; i < gd.vary.base->dc; i++)
406 char *k = gd.vary.base->av + gd.vary.base->dv[i].ai;
407 char *v = gd.vary.base->av + gd.vary.base->dv[i].aj;
409 if (strcmp(k, "back") == 0) back_name = v;
410 if (strcmp(k, "grad") == 0) grad_name = v;
412 if (strcmp(k, "version") == 0)
413 sscanf(v, "%d.%d", &version.x, &version.y);
417 * If the version of the loaded map is 1, assume we have a version
418 * match with the server. In this way 1.5.0 replays don't trigger
419 * bogus map compatibility warnings. Post-1.5.0 replays will have
420 * CMD_MAP override this.
423 game_compat_map = version.x == 1;
425 /* Initialize particles. */
427 part_reset(GOAL_HEIGHT, JUMP_HEIGHT);
429 /* Initialize command state. */
433 /* Initialize background. */
435 back_init(grad_name);
436 sol_load_full(&gd.back, back_name, 0);
441 void game_client_free(const char *next)
449 sol_free_draw(&gd.draw);
450 sol_free_vary(&gd.vary);
452 game_base_free(next);
454 sol_free_full(&gd.back);
460 /*---------------------------------------------------------------------------*/
462 void game_client_blend(float a)
467 void game_client_draw(int pose, float t)
469 game_lerp_apply(&gl, &gd);
470 game_draw(&gd, pose, t);
473 /*---------------------------------------------------------------------------*/
477 return (int) (timer * 100.f);
485 int curr_status(void)
490 /*---------------------------------------------------------------------------*/
492 void game_look(float phi, float theta)
494 struct game_view *view = &gl.view[CURR];
496 view->c[0] = view->p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
497 view->c[1] = view->p[1] + fsinf(V_RAD(phi));
498 view->c[2] = view->p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
500 gl.view[PREV] = gl.view[CURR];
503 /*---------------------------------------------------------------------------*/
505 void game_kill_fade(void)
511 void game_step_fade(float dt)
513 if ((gd.fade_k < 1.0f && gd.fade_d > 0.0f) ||
514 (gd.fade_k > 0.0f && gd.fade_d < 0.0f))
515 gd.fade_k += gd.fade_d * dt;
517 if (gd.fade_k < 0.0f)
522 if (gd.fade_k > 1.0f)
529 void game_fade(float d)
534 /*---------------------------------------------------------------------------*/
536 void game_client_fly(float k)
538 game_view_fly(&gl.view[CURR], &gd.vary, k);
540 gl.view[PREV] = gl.view[CURR];
543 /*---------------------------------------------------------------------------*/