Fix accidental switch/teleporter behavior changes
[neverball] / ball / game_server.c
index 6680002..18a8f58 100644 (file)
 
 #include "vec3.h"
 #include "item.h"
-#include "solid_phys.h"
 #include "config.h"
 #include "binary.h"
 #include "common.h"
 
+#include "solid_sim.h"
+#include "solid_all.h"
+#include "solid_cmd.h"
+
 #include "game_common.h"
 #include "game_server.h"
 #include "game_proxy.h"
@@ -40,18 +43,9 @@ static int   timer_down = 1;            /* Timer go up or down?              */
 
 static int status = GAME_NONE;          /* Outcome of the game               */
 
-static float game_rx;                   /* Floor rotation about X axis       */
-static float game_rz;                   /* Floor rotation about Z axis       */
-
-static float view_a;                    /* Ideal view rotation about Y axis  */
-static float view_dc;                   /* Ideal view distance above ball    */
-static float view_dp;                   /* Ideal view distance above ball    */
-static float view_dz;                   /* Ideal view distance behind ball   */
+static struct game_tilt tilt;           /* Floor rotation                    */
+static struct game_view view;           /* Current view                      */
 
-static float view_c[3];                 /* Current view center               */
-static float view_v[3];                 /* Current view vector               */
-static float view_p[3];                 /* Current view position             */
-static float view_e[3][3];              /* Current view reference frame      */
 static float view_k;
 
 static int   coins  = 0;                /* Collected coins                   */
@@ -61,6 +55,7 @@ static int   jump_e = 1;                /* Jumping enabled flag              */
 static int   jump_b = 0;                /* Jump-in-progress flag             */
 static float jump_dt;                   /* Jump duration                     */
 static float jump_p[3];                 /* Jump destination                  */
+static float jump_w[3];                 /* View destination                  */
 
 /*---------------------------------------------------------------------------*/
 
@@ -149,7 +144,7 @@ static int input_get_c(void)
     return (int) input_current.c;
 }
 
-int input_put(FILE *fout)
+int input_put(fs_file fout)
 {
     if (server_state)
     {
@@ -163,7 +158,7 @@ int input_put(FILE *fout)
     return 0;
 }
 
-int input_get(FILE *fin)
+int input_get(fs_file fin)
 {
     if (server_state)
     {
@@ -172,7 +167,7 @@ int input_get(FILE *fin)
         get_short(fin, &input_current.r);
         get_short(fin, &input_current.c);
 
-        return (feof(fin) ? 0 : 1);
+        return (fs_eof(fin) ? 0 : 1);
     }
     return 0;
 }
@@ -186,6 +181,15 @@ int input_get(FILE *fin)
 
 static union cmd cmd;
 
+static void game_cmd_map(const char *name, int ver_x, int ver_y)
+{
+    cmd.type          = CMD_MAP;
+    cmd.map.name      = strdup(name);
+    cmd.map.version.x = ver_x;
+    cmd.map.version.y = ver_y;
+    game_proxy_enq(&cmd);
+}
+
 static void game_cmd_eou(void)
 {
     cmd.type = CMD_END_OF_UPDATE;
@@ -220,7 +224,7 @@ static void game_cmd_goalopen(void)
 static void game_cmd_updball(void)
 {
     cmd.type = CMD_BALL_POSITION;
-    memcpy(cmd.ballpos.p, file.uv[0].p, sizeof (float) * 3);
+    v_cpy(cmd.ballpos.p, file.uv[0].p);
     game_proxy_enq(&cmd);
 
     cmd.type = CMD_BALL_BASIS;
@@ -237,16 +241,16 @@ static void game_cmd_updball(void)
 static void game_cmd_updview(void)
 {
     cmd.type = CMD_VIEW_POSITION;
-    memcpy(cmd.viewpos.p, view_p, sizeof (float) * 3);
+    v_cpy(cmd.viewpos.p, view.p);
     game_proxy_enq(&cmd);
 
     cmd.type = CMD_VIEW_CENTER;
-    memcpy(cmd.viewcenter.c, view_c, sizeof (float) * 3);
+    v_cpy(cmd.viewcenter.c, view.c);
     game_proxy_enq(&cmd);
 
     cmd.type = CMD_VIEW_BASIS;
-    v_cpy(cmd.viewbasis.e[0], view_e[0]);
-    v_cpy(cmd.viewbasis.e[1], view_e[1]);
+    v_cpy(cmd.viewbasis.e[0], view.e[0]);
+    v_cpy(cmd.viewbasis.e[1], view.e[1]);
     game_proxy_enq(&cmd);
 }
 
@@ -302,12 +306,22 @@ static void game_cmd_jump(int e)
     game_proxy_enq(&cmd);
 }
 
-static void game_cmd_rotate(void)
+static void game_cmd_tiltangles(void)
 {
-    cmd.type = CMD_ROTATE;
+    cmd.type = CMD_TILT_ANGLES;
 
-    cmd.rotate.x = game_rx;
-    cmd.rotate.z = game_rz;
+    cmd.tiltangles.x = tilt.rx;
+    cmd.tiltangles.z = tilt.rz;
+
+    game_proxy_enq(&cmd);
+}
+
+static void game_cmd_tiltaxes(void)
+{
+    cmd.type = CMD_TILT_AXES;
+
+    v_cpy(cmd.tiltaxes.x, tilt.x);
+    v_cpy(cmd.tiltaxes.z, tilt.z);
 
     game_proxy_enq(&cmd);
 }
@@ -442,35 +456,17 @@ static void grow_step(const struct s_file *fp, float dt)
 
 /*---------------------------------------------------------------------------*/
 
-static void view_init(void)
-{
-    view_dp  = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
-    view_dc  = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
-    view_dz  = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
-    view_k   = 1.0f;
-    view_a   = 0.0f;
-
-    view_c[0] = 0.f;
-    view_c[1] = view_dc;
-    view_c[2] = 0.f;
-
-    view_p[0] =     0.f;
-    view_p[1] = view_dp;
-    view_p[2] = view_dz;
-
-    view_e[0][0] = 1.f;
-    view_e[0][1] = 0.f;
-    view_e[0][2] = 0.f;
-    view_e[1][0] = 0.f;
-    view_e[1][1] = 1.f;
-    view_e[1][2] = 0.f;
-    view_e[2][0] = 0.f;
-    view_e[2][1] = 0.f;
-    view_e[2][2] = 1.f;
-}
+static struct lockstep server_step;
 
 int game_server_init(const char *file_name, int t, int e)
 {
+    struct
+    {
+        int x, y;
+    } version;
+
+    int i;
+
     timer      = (float) t / 100.f;
     timer_down = (t > 0);
     coins      = 0;
@@ -479,15 +475,26 @@ int game_server_init(const char *file_name, int t, int e)
     if (server_state)
         game_server_free();
 
-    if (!sol_load_only_file(&file, config_data(file_name)))
+    if (!sol_load_only_file(&file, file_name))
         return (server_state = 0);
 
     server_state = 1;
 
+    version.x = 0;
+    version.y = 0;
+
+    for (i = 0; i < file.dc; i++)
+    {
+        char *k = file.av + file.dv[i].ai;
+        char *v = file.av + file.dv[i].aj;
+
+        if (strcmp(k, "version") == 0)
+            sscanf(v, "%d.%d", &version.x, &version.y);
+    }
+
     input_init();
 
-    game_rx = 0.0f;
-    game_rz = 0.0f;
+    game_tilt_init(&tilt);
 
     /* Initialize jump and goal states. */
 
@@ -499,17 +506,24 @@ int game_server_init(const char *file_name, int t, int e)
 
     /* Initialize the view. */
 
-    view_init();
+    game_view_init(&view);
+
+    view_k = 1.0f;
 
     /* Initialize ball size tracking... */
 
     got_orig = 0;
     grow = 0;
 
+    /* Initialize simulation. */
+
+    sol_init_sim(&file);
+
     sol_cmd_enq_func(game_proxy_enq);
 
     /* Queue client commands. */
 
+    game_cmd_map(file_name, version.x, version.y);
     game_cmd_ups();
     game_cmd_timer();
 
@@ -517,7 +531,8 @@ int game_server_init(const char *file_name, int t, int e)
 
     game_cmd_init_balls();
     game_cmd_init_items();
-    game_cmd_eou();
+
+    lockstep_clr(&server_step);
 
     return server_state;
 }
@@ -526,6 +541,7 @@ void game_server_free(void)
 {
     if (server_state)
     {
+        sol_quit_sim();
         sol_free(&file);
         server_state = 0;
     }
@@ -535,38 +551,42 @@ void game_server_free(void)
 
 static void game_update_view(float dt)
 {
-    float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
+    float dc = view.dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
     float da = input_get_r() * dt * 90.0f;
     float k;
 
     float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f };
+    float view_v[3];
 
     /* Center the view about the ball. */
 
-    v_cpy(view_c, file.uv->p);
-    v_inv(view_v, file.uv->v);
+    v_cpy(view.c, file.uv->p);
+
+    view_v[0] = -file.uv->v[0];
+    view_v[1] =  0.0f;
+    view_v[2] = -file.uv->v[2];
 
     switch (input_get_c())
     {
-    case 1: /* Camera 1: Viewpoint chases the ball position. */
+    case VIEW_LAZY: /* Viewpoint chases the ball position. */
 
-        v_sub(view_e[2], view_p, view_c);
+        v_sub(view.e[2], view.p, view.c);
 
         break;
 
-    case 2: /* Camera 2: View vector is given by view angle. */
+    case VIEW_MANUAL:  /* View vector is given by view angle. */
 
-        view_e[2][0] = fsinf(V_RAD(view_a));
-        view_e[2][1] = 0.0;
-        view_e[2][2] = fcosf(V_RAD(view_a));
+        view.e[2][0] = fsinf(V_RAD(view.a));
+        view.e[2][1] = 0.0;
+        view.e[2][2] = fcosf(V_RAD(view.a));
 
         break;
 
-    default: /* Default: View vector approaches the ball velocity vector. */
+    case VIEW_CHASE: /* View vector approaches the ball velocity vector. */
 
-        v_sub(view_e[2], view_p, view_c);
-        v_nrm(view_e[2], view_e[2]);
-        v_mad(view_e[2], view_e[2], view_v, v_dot(view_v, view_v) * dt / 4);
+        v_sub(view.e[2], view.p, view.c);
+        v_nrm(view.e[2], view.e[2]);
+        v_mad(view.e[2], view.e[2], view_v, v_dot(view_v, view_v) * dt / 4);
 
         break;
     }
@@ -574,35 +594,36 @@ static void game_update_view(float dt)
     /* Apply manual rotation. */
 
     m_rot(M, Y, V_RAD(da));
-    m_vxfm(view_e[2], M, view_e[2]);
+    m_vxfm(v, M, view.e[2]);
+    v_cpy(view.e[2], v);
 
     /* Orthonormalize the new view reference frame. */
 
-    v_crs(view_e[0], view_e[1], view_e[2]);
-    v_crs(view_e[2], view_e[0], view_e[1]);
-    v_nrm(view_e[0], view_e[0]);
-    v_nrm(view_e[2], view_e[2]);
+    v_crs(view.e[0], view.e[1], view.e[2]);
+    v_crs(view.e[2], view.e[0], view.e[1]);
+    v_nrm(view.e[0], view.e[0]);
+    v_nrm(view.e[2], view.e[2]);
 
     /* Compute the new view position. */
 
-    k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
+    k = 1.0f + v_dot(view.e[2], view_v) / 10.0f;
 
     view_k = view_k + (k - view_k) * dt;
 
     if (view_k < 0.5) view_k = 0.5;
 
-    v_scl(v,    view_e[1], view_dp * view_k);
-    v_mad(v, v, view_e[2], view_dz * view_k);
-    v_add(view_p, v, file.uv->p);
+    v_scl(v,    view.e[1], view.dp * view_k);
+    v_mad(v, v, view.e[2], view.dz * view_k);
+    v_add(view.p, v, file.uv->p);
 
     /* Compute the new view center. */
 
-    v_cpy(view_c, file.uv->p);
-    v_mad(view_c, view_c, view_e[1], dc);
+    v_cpy(view.c, file.uv->p);
+    v_mad(view.c, view.c, view.e[1], dc);
 
     /* Note the current view angle. */
 
-    view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
+    view.a = V_DEG(fatan2f(view.e[2][0], view.e[2][2]));
 
     game_cmd_updview();
 }
@@ -662,22 +683,27 @@ static int game_update_state(int bt)
 
     /* Test for a switch. */
 
-    if (sol_swch_test(fp, 0))
+    if (sol_swch_test(fp, 0) == SWCH_TRIGGER)
         audio_play(AUD_SWITCH, 1.f);
 
     /* Test for a jump. */
 
-    if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
+    if (jump_e == 1 && jump_b == 0 && (sol_jump_test(fp, jump_p, 0) ==
+                                       JUMP_TRIGGER))
     {
         jump_b  = 1;
         jump_e  = 0;
         jump_dt = 0.f;
 
+        v_sub(jump_w, jump_p, fp->uv->p);
+        v_add(jump_w, view.p, jump_w);
+
         audio_play(AUD_JUMP, 1.f);
 
         game_cmd_jump(1);
     }
-    if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
+    if (jump_e == 0 && jump_b == 0 && (sol_jump_test(fp, jump_p, 0) ==
+                                       JUMP_OUTSIDE))
     {
         jump_e = 1;
         game_cmd_jump(0);
@@ -720,14 +746,17 @@ static int game_step(const float g[3], float dt, int bt)
 
         /* Smooth jittery or discontinuous input. */
 
-        game_rx += (input_get_x() - game_rx) * dt / RESPONSE;
-        game_rz += (input_get_z() - game_rz) * dt / RESPONSE;
+        tilt.rx += (input_get_x() - tilt.rx) * dt / RESPONSE;
+        tilt.rz += (input_get_z() - tilt.rz) * dt / RESPONSE;
 
-        game_cmd_rotate();
+        game_tilt_axes(&tilt, view.e);
+
+        game_cmd_tiltaxes();
+        game_cmd_tiltangles();
 
         grow_step(fp, dt);
 
-        game_comp_grav(h, g, view_a, game_rx, game_rz);
+        game_tilt_grav(h, g, &tilt);
 
         if (jump_b)
         {
@@ -737,9 +766,8 @@ static int game_step(const float g[3], float dt, int bt)
 
             if (0.5f < jump_dt)
             {
-                fp->uv[0].p[0] = jump_p[0];
-                fp->uv[0].p[1] = jump_p[1];
-                fp->uv[0].p[2] = jump_p[2];
+                v_cpy(fp->uv->p, jump_p);
+                v_cpy(view.p,    jump_w);
             }
             if (1.0f < jump_dt)
                 jump_b = 0;
@@ -776,18 +804,15 @@ static int game_step(const float g[3], float dt, int bt)
     return GAME_NONE;
 }
 
-void game_server_step(float dt)
+static void game_server_iter(float dt)
 {
-    static const float gup[] = { 0.0f, +9.8f, 0.0f };
-    static const float gdn[] = { 0.0f, -9.8f, 0.0f };
-
     switch (status)
     {
-    case GAME_GOAL: game_step(gup, dt, 0); break;
-    case GAME_FALL: game_step(gdn, dt, 0); break;
+    case GAME_GOAL: game_step(GRAVITY_UP, dt, 0); break;
+    case GAME_FALL: game_step(GRAVITY_DN, dt, 0); break;
 
     case GAME_NONE:
-        if ((status = game_step(gdn, dt, 1)) != GAME_NONE)
+        if ((status = game_step(GRAVITY_DN, dt, 1)) != GAME_NONE)
             game_cmd_status();
         break;
     }
@@ -795,6 +820,13 @@ void game_server_step(float dt)
     game_cmd_eou();
 }
 
+static struct lockstep server_step = { game_server_iter, DT };
+
+void game_server_step(float dt)
+{
+    lockstep_run(&server_step, dt);
+}
+
 /*---------------------------------------------------------------------------*/
 
 void game_set_goal(void)
@@ -830,8 +862,10 @@ void game_set_ang(int x, int z)
 
 void game_set_pos(int x, int y)
 {
-    input_set_x(input_get_x() + 40.0f * y / config_get_d(CONFIG_MOUSE_SENSE));
-    input_set_z(input_get_z() + 40.0f * x / config_get_d(CONFIG_MOUSE_SENSE));
+    const float range = ANGLE_BOUND * 2;
+
+    input_set_x(input_get_x() + range * y / config_get_d(CONFIG_MOUSE_SENSE));
+    input_set_z(input_get_z() + range * x / config_get_d(CONFIG_MOUSE_SENSE));
 }
 
 void game_set_cam(int c)
@@ -844,72 +878,9 @@ void game_set_rot(float r)
     input_set_r(r);
 }
 
-void game_set_fly(float k, const struct s_file *fp)
+void game_server_fly(float k)
 {
-    float  x[3] = { 1.f, 0.f, 0.f };
-    float  y[3] = { 0.f, 1.f, 0.f };
-    float  z[3] = { 0.f, 0.f, 1.f };
-    float c0[3] = { 0.f, 0.f, 0.f };
-    float p0[3] = { 0.f, 0.f, 0.f };
-    float c1[3] = { 0.f, 0.f, 0.f };
-    float p1[3] = { 0.f, 0.f, 0.f };
-    float  v[3];
-
-    if (!fp) fp = &file;
-
-    view_init();
-
-    z[0] = fsinf(V_RAD(view_a));
-    z[2] = fcosf(V_RAD(view_a));
-
-    v_cpy(view_e[0], x);
-    v_cpy(view_e[1], y);
-    v_cpy(view_e[2], z);
-
-    /* k = 0.0 view is at the ball. */
-
-    if (fp->uc > 0)
-    {
-        v_cpy(c0, fp->uv[0].p);
-        v_cpy(p0, fp->uv[0].p);
-    }
-
-    v_mad(p0, p0, y, view_dp);
-    v_mad(p0, p0, z, view_dz);
-    v_mad(c0, c0, y, view_dc);
-
-    /* k = +1.0 view is s_view 0 */
-
-    if (k >= 0 && fp->wc > 0)
-    {
-        v_cpy(p1, fp->wv[0].p);
-        v_cpy(c1, fp->wv[0].q);
-    }
-
-    /* k = -1.0 view is s_view 1 */
-
-    if (k <= 0 && fp->wc > 1)
-    {
-        v_cpy(p1, fp->wv[1].p);
-        v_cpy(c1, fp->wv[1].q);
-    }
-
-    /* Interpolate the views. */
-
-    v_sub(v, p1, p0);
-    v_mad(view_p, p0, v, k * k);
-
-    v_sub(v, c1, c0);
-    v_mad(view_c, c0, v, k * k);
-
-    /* Orthonormalize the view basis. */
-
-    v_sub(view_e[2], view_p, view_c);
-    v_crs(view_e[0], view_e[1], view_e[2]);
-    v_crs(view_e[2], view_e[0], view_e[1]);
-    v_nrm(view_e[0], view_e[0]);
-    v_nrm(view_e[2], view_e[2]);
-
+    game_view_fly(&view, &file, k);
     game_cmd_updview();
 }