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 /*---------------------------------------------------------------------------*/
46 static struct game_draw gd;
48 static float timer = 0.0f; /* Clock time */
49 static int status = GAME_NONE; /* Outcome of the game */
50 static int coins = 0; /* Collected coins */
52 static int ups; /* Updates per second */
53 static int first_update; /* First update flag */
54 static int curr_ball; /* Current ball index */
59 } version; /* Current map version */
62 * Neverball <= 1.5.1 does not send explicit tilt axes, rotation
63 * happens directly around view vectors. So for compatibility if at
64 * the time of receiving tilt angles we have not yet received the tilt
65 * axes, we use the view vectors.
68 static int got_tilt_axes;
70 /*---------------------------------------------------------------------------*/
72 static void game_run_cmd(const union cmd *cmd)
76 struct s_vary *vary = &gd.file.vary;
86 case CMD_END_OF_UPDATE:
96 /* Compute gravity for particle effects. */
98 if (status == GAME_GOAL)
99 game_tilt_grav(v, GRAVITY_UP, &gd.tilt);
101 game_tilt_grav(v, GRAVITY_DN, &gd.tilt);
103 /* Step particle, goal and jump effects. */
107 dt = 1.0f / (float) ups;
109 if (gd.goal_e && gd.goal_k < 1.0f)
116 if (1.0f < gd.jump_dt)
126 /* Allocate a new ball and mark it as the current ball. */
128 if ((up = realloc(vary->uv, sizeof (*up) * (vary->uc + 1))))
131 curr_ball = vary->uc;
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:
171 game_tilt_axes(&gd.tilt, gd.view.e);
173 gd.tilt.rx = cmd->tiltangles.x;
174 gd.tilt.rz = cmd->tiltangles.z;
178 /* Play the sound. */
181 audio_play(cmd->sound.n, cmd->sound.a);
186 timer = cmd->timer.t;
190 status = cmd->status.t;
194 coins = cmd->coins.n;
208 vary->bv[cmd->bodypath.bi].pi = cmd->bodypath.pi;
212 vary->bv[cmd->bodytime.bi].t = cmd->bodytime.t;
217 * Enable the goal and make sure it's fully visible if
218 * this is the first update.
224 gd.goal_k = first_update ? 1.0f : 0.0f;
229 vary->xv[cmd->swchenter.xi].e = 1;
232 case CMD_SWCH_TOGGLE:
233 vary->xv[cmd->swchtoggle.xi].f = !vary->xv[cmd->swchtoggle.xi].f;
237 vary->xv[cmd->swchexit.xi].e = 0;
240 case CMD_UPDATES_PER_SECOND:
244 case CMD_BALL_RADIUS:
245 vary->uv[curr_ball].r = cmd->ballradius.r;
248 case CMD_CLEAR_ITEMS:
257 case CMD_CLEAR_BALLS:
266 case CMD_BALL_POSITION:
267 up = vary->uv + curr_ball;
269 v_cpy(up->p, cmd->ballpos.p);
273 up = vary->uv + curr_ball;
275 v_cpy(up->e[0], cmd->ballbasis.e[0]);
276 v_cpy(up->e[1], cmd->ballbasis.e[1]);
277 v_crs(up->e[2], up->e[0], up->e[1]);
280 case CMD_BALL_PEND_BASIS:
281 up = vary->uv + curr_ball;
283 v_cpy(up->E[0], cmd->ballpendbasis.E[0]);
284 v_cpy(up->E[1], cmd->ballpendbasis.E[1]);
285 v_crs(up->E[2], up->E[0], up->E[1]);
288 case CMD_VIEW_POSITION:
289 v_cpy(gd.view.p, cmd->viewpos.p);
292 case CMD_VIEW_CENTER:
293 v_cpy(gd.view.c, cmd->viewcenter.c);
297 v_cpy(gd.view.e[0], cmd->viewbasis.e[0]);
298 v_cpy(gd.view.e[1], cmd->viewbasis.e[1]);
299 v_crs(gd.view.e[2], gd.view.e[0], gd.view.e[1]);
302 case CMD_CURRENT_BALL:
303 curr_ball = cmd->currball.ui;
307 vary->pv[cmd->pathflag.pi].f = cmd->pathflag.f;
310 case CMD_STEP_SIMULATION:
312 * Simulate body motion.
314 * This is done on the client side due to replay file size
315 * concerns and isn't done as part of CMD_END_OF_UPDATE to
316 * match the server state as closely as possible. Body time
317 * is still synchronized with the server on a semi-regular
318 * basis and path indices are handled through CMD_BODY_PATH,
319 * thus this code doesn't need to be as sophisticated as
323 dt = cmd->stepsim.dt;
325 for (i = 0; i < vary->bc; i++)
327 struct v_body *bp = vary->bv + i;
328 struct v_path *pp = vary->pv + bp->pi;
330 if (bp->pi >= 0 && pp->f)
337 * Note a version (mis-)match between the loaded map and what
338 * the server has. (This doesn't actually load a map.)
341 game_compat_map = version.x == cmd->map.version.x;
346 v_cpy(gd.tilt.x, cmd->tiltaxes.x);
347 v_cpy(gd.tilt.z, cmd->tiltaxes.z);
356 void game_client_sync(fs_file demo_fp)
360 while ((cmdp = game_proxy_deq()))
363 cmd_put(demo_fp, cmdp);
371 /*---------------------------------------------------------------------------*/
373 int game_client_init(const char *file_name)
375 char *back_name = "", *grad_name = "";
384 if (!sol_load_full(&gd.file, file_name, config_get_d(CONFIG_SHADOW)))
385 return (gd.state = 0);
387 gd.reflective = sol_reflective(&gd.file.draw);
391 game_tilt_init(&gd.tilt);
393 /* Initialize jump and goal states. */
401 /* Initialise the level, background, particles, fade, and view. */
410 for (i = 0; i < gd.file.base.dc; i++)
412 char *k = gd.file.base.av + gd.file.base.dv[i].ai;
413 char *v = gd.file.base.av + gd.file.base.dv[i].aj;
415 if (strcmp(k, "back") == 0) back_name = v;
416 if (strcmp(k, "grad") == 0) grad_name = v;
418 if (strcmp(k, "version") == 0)
419 sscanf(v, "%d.%d", &version.x, &version.y);
423 * If the version of the loaded map is 1, assume we have a version
424 * match with the server. In this way 1.5.0 replays don't trigger
425 * bogus map compatibility warnings. Post-1.5.0 replays will have
426 * CMD_MAP override this.
429 game_compat_map = version.x == 1;
431 part_reset(GOAL_HEIGHT, JUMP_HEIGHT);
436 back_init(grad_name);
437 sol_load_full(&gd.back, back_name, 0);
442 void game_client_free(void)
447 sol_free_full(&gd.file);
448 sol_free_full(&gd.back);
454 /*---------------------------------------------------------------------------*/
456 void game_client_draw(int pose, float t)
458 game_draw(&gd, pose, t);
461 /*---------------------------------------------------------------------------*/
465 return (int) (timer * 100.f);
473 int curr_status(void)
478 /*---------------------------------------------------------------------------*/
481 void game_look(float phi, float theta)
483 gd.view.c[0] = gd.view.p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
484 gd.view.c[1] = gd.view.p[1] + fsinf(V_RAD(phi));
485 gd.view.c[2] = gd.view.p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
488 /*---------------------------------------------------------------------------*/
490 void game_kill_fade(void)
496 void game_step_fade(float dt)
498 if ((gd.fade_k < 1.0f && gd.fade_d > 0.0f) ||
499 (gd.fade_k > 0.0f && gd.fade_d < 0.0f))
500 gd.fade_k += gd.fade_d * dt;
502 if (gd.fade_k < 0.0f)
507 if (gd.fade_k > 1.0f)
514 void game_fade(float d)
519 /*---------------------------------------------------------------------------*/
521 void game_client_fly(float k)
523 game_view_fly(&gd.view, &gd.file.vary, k);
526 /*---------------------------------------------------------------------------*/