#include "geom.h"
#include "back.h"
#include "part.h"
+#include "ball.h"
#include "image.h"
#include "audio.h"
#include "solid_gl.h"
static struct s_file file;
static struct s_file back;
-static float clock = 0.f; /* Clock time */
-static int clock_down = 1; /* Clock go up or down? */
+static float timer = 0.f; /* Clock time */
+static int timer_down = 1; /* Timer go up or down? */
-static float game_ix; /* Input rotation about X axis */
-static float game_iz; /* Input rotation about Z axis */
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_ry; /* Angular velocity 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 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 orientation */
+static float view_e[3][3]; /* Current view reference frame */
static float view_k;
static int coins = 0; /* Collected coins */
static float jump_p[3]; /* Jump destination */
static float fade_k = 0.0; /* Fade in/out level */
static float fade_d = 0.0; /* Fade in/out direction */
-static int drawball = 1; /* Should the ball be drawn? */
-static int ball_b = 0; /* Is the ball a bonus ball? */
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * This is an abstraction of the game's input state. All input is
+ * encapsulated here, and all references to the input by the game are made
+ * here. This has the effect of homogenizing input for use in replay
+ * recording and playback.
+ *
+ * x and z:
+ * -32767 = -ANGLE_BOUND
+ * +32767 = +ANGLE_BOUND
+ *
+ * r:
+ * -32767 = -VIEWR_BOUND
+ * +32767 = +VIEWR_BOUND
+ *
+ */
+
+struct input
+{
+ short x;
+ short z;
+ short r;
+ short c;
+};
+
+static struct input input_current;
+
+static void input_init(void)
+{
+ input_current.x = 0;
+ input_current.z = 0;
+ input_current.r = 0;
+ input_current.c = 0;
+}
+
+static void input_set_x(float x)
+{
+ if (x < -ANGLE_BOUND) x = -ANGLE_BOUND;
+ if (x > ANGLE_BOUND) x = ANGLE_BOUND;
+
+ input_current.x = (short) (32767.0f * x / ANGLE_BOUND);
+}
+
+static void input_set_z(float z)
+{
+ if (z < -ANGLE_BOUND) z = -ANGLE_BOUND;
+ if (z > ANGLE_BOUND) z = ANGLE_BOUND;
+
+ input_current.z = (short) (32767.0f * z / ANGLE_BOUND);
+}
+
+static void input_set_r(float r)
+{
+ if (r < -VIEWR_BOUND) r = -VIEWR_BOUND;
+ if (r > VIEWR_BOUND) r = VIEWR_BOUND;
+
+ input_current.r = (short) (32767.0f * r / VIEWR_BOUND);
+}
+
+static void input_set_c(int c)
+{
+ input_current.c = (short) c;
+}
+
+static float input_get_x(void)
+{
+ return ANGLE_BOUND * (float) input_current.x / 32767.0f;
+}
+
+static float input_get_z(void)
+{
+ return ANGLE_BOUND * (float) input_current.z / 32767.0f;
+}
+
+static float input_get_r(void)
+{
+ return VIEWR_BOUND * (float) input_current.r / 32767.0f;
+}
+
+static int input_get_c(void)
+{
+ return (int) input_current.c;
+}
+
+int input_put(FILE *fout)
+{
+ if (game_state)
+ {
+ put_short(fout, &input_current.x);
+ put_short(fout, &input_current.z);
+ put_short(fout, &input_current.r);
+ put_short(fout, &input_current.c);
+
+ return 1;
+ }
+ return 0;
+}
+
+int input_get(FILE *fin)
+{
+ if (game_state)
+ {
+ get_short(fin, &input_current.x);
+ get_short(fin, &input_current.z);
+ get_short(fin, &input_current.r);
+ get_short(fin, &input_current.c);
+
+ return (feof(fin) ? 0 : 1);
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
static int grow = 0; /* Should the ball be changing size? */
static float grow_orig = 0; /* the original ball size */
static float grow_goal = 0; /* how big or small to get! */
-const float grow_time = 0.5f; /* sec for the ball to get to size. */
static float grow_t = 0.0; /* timer for the ball to grow... */
static float grow_strt = 0; /* starting value for growth */
-const float grow_big = 1.5f; /* large factor */
-const float grow_small= 0.5f; /* small factor */
static int got_orig = 0; /* Do we know original ball size? */
+#define GROW_TIME 0.5f /* sec for the ball to get to size. */
+#define GROW_BIG 1.5f /* large factor */
+#define GROW_SMALL 0.5f /* small factor */
-/*---------------------------------------------------------------------------*/
+static int grow_state = 0; /* Current state (values -1, 0, +1) */
-static void grow_set(const struct s_file *fp, int size)
-{
+static void grow_init(const struct s_file *fp, int type)
+{
if (!got_orig)
{
- grow_orig = fp->uv->r;
- grow_goal = grow_orig;
- grow_strt = grow_orig;
- got_orig = 1;
+ grow_orig = fp->uv->r;
+ grow_goal = grow_orig;
+ grow_strt = grow_orig;
+
+ grow_state = 0;
+
+ got_orig = 1;
}
- if (size == 50)
+ if (type == ITEM_SHRINK)
{
- if (grow_goal == grow_orig * grow_small) return; /*already small!*/
- else if (grow_goal == grow_orig * grow_big) /* big, let's set it to normal.*/
+ audio_play(AUD_SHRINK, 1.f);
+
+ switch (grow_state)
{
+ case -1:
+ break;
+
+ case 0:
+ grow_goal = grow_orig * GROW_SMALL;
+ grow_state = -1;
grow = 1;
+ break;
+
+ case +1:
grow_goal = grow_orig;
- }
- else /*must be normal sized.*/
- {
- grow_goal = grow_orig * grow_small;
+ grow_state = 0;
grow = 1;
+ break;
}
- }/* done with 50% size coin */
- if (size == 150)
+ }
+ else if (type == ITEM_GROW)
{
- if (grow_goal == grow_orig * grow_big) return; /*already big!*/
- else if (grow_goal == grow_orig * grow_small) /* small, let's set it to normal.*/
+ audio_play(AUD_GROW, 1.f);
+
+ switch (grow_state)
{
- grow = 1;
+ case -1:
grow_goal = grow_orig;
- }
- else /*must be normal sized.*/
- {
- grow_goal = grow_orig * grow_big;
+ grow_state = 0;
grow = 1;
+ break;
+
+ case 0:
+ grow_goal = grow_orig * GROW_BIG;
+ grow_state = +1;
+ grow = 1;
+ break;
+
+ case +1:
+ break;
}
- }/* done with 150% size coin */
+ }
if (grow)
{
}
}
-static void grow_ball(const struct s_file *fp, float dt)
+static void grow_step(const struct s_file *fp, float dt)
{
float dr;
- /*calculate new size based on how long since you touched the coin...*/
+ if (!grow)
+ return;
+
+ /* Calculate new size based on how long since you touched the coin... */
+
grow_t += dt;
- if (grow_t >= grow_time)
+
+ if (grow_t >= GROW_TIME)
{
grow = 0;
- grow_t = grow_time;
+ grow_t = GROW_TIME;
}
- dr = grow_strt + ((grow_goal-grow_strt) * (1.0f / (grow_time / grow_t)));
+ dr = grow_strt + ((grow_goal-grow_strt) * (1.0f / (GROW_TIME / grow_t)));
+
+ /* No sinking through the floor! Keeps ball's bottom constant. */
- fp->uv->p[1] += (dr - fp->uv->r); /*No sinking through the floor! keeps ball's bottom constant.*/
- fp->uv->r = dr;
+ fp->uv->p[1] += (dr - fp->uv->r);
+ fp->uv->r = dr;
}
+/*---------------------------------------------------------------------------*/
+
static void view_init(void)
{
- /* Get the initial orientation angle */
- if (file.uc > 0)
- view_a = file.uv->a - 90.f; /* angle is in the sol */
- else
- view_a = 0.f; /* default is north :) */
-
- view_ry = 0.f;
-
view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
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;
int game_init(const struct level *level, int t, int g)
{
- clock = (float) t / 100.f;
- clock_down = (t > 0);
+ timer = (float) t / 100.f;
+ timer_down = (t > 0);
coins = 0;
if (game_state)
game_free();
- if (!sol_load_gl(&file, level->file, config_get_d(CONFIG_TEXTURES),
+ if (!sol_load_gl(&file, config_data(level->file),
+ config_get_d(CONFIG_TEXTURES),
config_get_d(CONFIG_SHADOW)))
return (game_state = 0);
game_state = 1;
- game_ix = 0.f;
- game_iz = 0.f;
- game_rx = 0.f;
- game_rz = 0.f;
+ input_init();
- drawball = 1;
+ game_rx = 0.0f;
+ game_rz = 0.0f;
/* Initialize jump and goal states. */
goal_c = g;
goal_k = (g == 0) ? 1.0f : 0.0f;
- ball_b = level->is_bonus;
-
/* Initialise the level, background, particles, fade, and view. */
fade_k = 1.0f;
/* Initialize ball size tracking... */
got_orig = 0;
+ grow = 0;
return game_state;
}
int curr_clock(void)
{
- return (int) (clock * 100.f);
+ return (int) (timer * 100.f);
}
int curr_coins(void)
/*---------------------------------------------------------------------------*/
-static void game_draw_balls(const struct s_file *fp)
+static void game_draw_balls(const struct s_file *fp,
+ const float *bill_M, float t)
{
float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
- float M[16];
- m_basis(M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
+ float ball_M[16];
+ float pend_M[16];
+
+ m_basis(ball_M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
+ m_basis(pend_M, fp->uv[0].E[0], fp->uv[0].E[1], fp->uv[0].E[2]);
+ glPushAttrib(GL_LIGHTING_BIT);
glPushMatrix();
{
glTranslatef(fp->uv[0].p[0],
fp->uv[0].p[1] + BALL_FUDGE,
fp->uv[0].p[2]);
- glMultMatrixf(M);
glScalef(fp->uv[0].r,
fp->uv[0].r,
fp->uv[0].r);
glColor4fv(c);
-
- ball_draw(ball_b);
+ ball_draw(ball_M, pend_M, bill_M, t);
}
glPopMatrix();
+ glPopAttrib();
}
-static void game_draw_coins(const struct s_file *fp)
+static void game_draw_items(const struct s_file *fp, float t)
{
- float r = 360.f * SDL_GetTicks() / 1000.f;
- int ci;
+ float r = 360.f * t;
+ int hi;
- coin_push();
- coin_push_text(0); /*regular coins*/
+ glPushAttrib(GL_LIGHTING_BIT);
{
- for (ci = 0; ci < fp->cc; ci++)
+ item_push(ITEM_COIN);
+ {
+ for (hi = 0; hi < fp->hc; hi++)
- if (fp->cv[ci].n > 0 && fp->cv[ci].n < 50)
- {
- glPushMatrix();
+ if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
{
- glTranslatef(fp->cv[ci].p[0],
- fp->cv[ci].p[1],
- fp->cv[ci].p[2]);
- glRotatef(r, 0.0f, 1.0f, 0.0f);
- coin_draw(fp->cv[ci].n, r);
+ glPushMatrix();
+ {
+ glTranslatef(fp->hv[hi].p[0],
+ fp->hv[hi].p[1],
+ fp->hv[hi].p[2]);
+ glRotatef(r, 0.0f, 1.0f, 0.0f);
+ item_draw(&fp->hv[hi], r);
+ }
+ glPopMatrix();
}
- glPopMatrix();
- }
- }
- coin_pull();
+ }
+ item_pull();
- /*there has got to be a better way than three seperate loops,*/
- /*once for each texture, but someone else is going to have to do it!*/
- coin_push();
- coin_push_text(50); /*any shrink coins?*/
- {
- for (ci = 0; ci < fp->cc; ci++)
+ item_push(ITEM_SHRINK);
+ {
+ for (hi = 0; hi < fp->hc; hi++)
- if (fp->cv[ci].n == 50)
- {
- glPushMatrix();
+ if (fp->hv[hi].t == ITEM_SHRINK)
{
- glTranslatef(fp->cv[ci].p[0],
- fp->cv[ci].p[1],
- fp->cv[ci].p[2]);
- glRotatef(r, 0.0f, 1.0f, 0.0f);
- coin_draw(fp->cv[ci].n, r);
+ glPushMatrix();
+ {
+ glTranslatef(fp->hv[hi].p[0],
+ fp->hv[hi].p[1],
+ fp->hv[hi].p[2]);
+ glRotatef(r, 0.0f, 1.0f, 0.0f);
+ item_draw(&fp->hv[hi], r);
+ }
+ glPopMatrix();
}
- glPopMatrix();
- }
+ }
+ item_pull();
+
+ item_push(ITEM_GROW);
+ {
+ for (hi = 0; hi < fp->hc; hi++)
+
+ if (fp->hv[hi].t == ITEM_GROW)
+ {
+ glPushMatrix();
+ {
+ glTranslatef(fp->hv[hi].p[0],
+ fp->hv[hi].p[1],
+ fp->hv[hi].p[2]);
+ glRotatef(r, 0.0f, 1.0f, 0.0f);
+ item_draw(&fp->hv[hi], r);
+ }
+ glPopMatrix();
+ }
+ }
+ item_pull();
}
- coin_pull();
+ glPopAttrib();
+}
- coin_push();
- coin_push_text(150); /*any grow coins?*/
+static void game_draw_goals(const struct s_file *fp, const float *M, float t)
+{
+ if (goal_c == 0)
{
- for (ci = 0; ci < fp->cc; ci++)
+ int zi;
+
+ /* Draw the goal particles. */
- if (fp->cv[ci].n == 150)
+ glEnable(GL_TEXTURE_2D);
+ {
+ for (zi = 0; zi < fp->zc; zi++)
{
glPushMatrix();
{
- glTranslatef(fp->cv[ci].p[0],
- fp->cv[ci].p[1],
- fp->cv[ci].p[2]);
- glRotatef(r, 0.0f, 1.0f, 0.0f);
- coin_draw(fp->cv[ci].n, r);
+ glTranslatef(fp->zv[zi].p[0],
+ fp->zv[zi].p[1],
+ fp->zv[zi].p[2]);
+
+ part_draw_goal(M, fp->zv[zi].r, goal_k, t);
}
glPopMatrix();
}
- }
- coin_pull();
-}
+ }
+ glDisable(GL_TEXTURE_2D);
-static void game_draw_goals(const struct s_file *fp, float rx, float ry)
-{
- int zi;
+ /* Draw the goal column. */
- if (goal_c == 0)
for (zi = 0; zi < fp->zc; zi++)
{
glPushMatrix();
fp->zv[zi].p[1],
fp->zv[zi].p[2]);
- part_draw_goal(rx, ry, fp->zv[zi].r, goal_k, fp->zv[zi].c);
+ glScalef(fp->zv[zi].r,
+ goal_k,
+ fp->zv[zi].r);
- glScalef(fp->zv[zi].r, goal_k, fp->zv[zi].r);
goal_draw();
}
glPopMatrix();
}
+ }
}
static void game_draw_jumps(const struct s_file *fp)
glTranslatef(fp->jv[ji].p[0],
fp->jv[ji].p[1],
fp->jv[ji].p[2]);
+ glScalef(fp->jv[ji].r,
+ 1.0f,
+ fp->jv[ji].r);
- glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
jump_draw(!jump_e);
}
glPopMatrix();
{
if (fp->xv[xi].i)
continue;
+
glPushMatrix();
{
glTranslatef(fp->xv[xi].p[0],
fp->xv[xi].p[1],
fp->xv[xi].p[2]);
+ glScalef(fp->xv[xi].r,
+ 1.0f,
+ fp->xv[xi].r);
- glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
swch_draw(fp->xv[xi].f, fp->xv[xi].e);
}
glPopMatrix();
/*---------------------------------------------------------------------------*/
-static void game_refl_all(int s)
+static void game_draw_tilt(int d)
{
const float *ball_p = file.uv->p;
+ /* Rotate the environment about the position of the ball. */
+
+ glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
+ glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
+ glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
+ glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
+}
+
+static void game_refl_all(void)
+{
glPushMatrix();
{
- /* Rotate the environment about the position of the ball. */
-
- glTranslatef(+ball_p[0], +ball_p[1], +ball_p[2]);
- glRotatef(-game_rz, view_e[2][0], view_e[2][1], view_e[2][2]);
- glRotatef(-game_rx, view_e[0][0], view_e[0][1], view_e[0][2]);
- glTranslatef(-ball_p[0], -ball_p[1], -ball_p[2]);
+ game_draw_tilt(1);
/* Draw the floor. */
static void game_draw_light(void)
{
const float light_p[2][4] = {
- { -8.0f, +32.0f, -8.0f, 1.0f },
- { +8.0f, +32.0f, +8.0f, 1.0f },
+ { -8.0f, +32.0f, -8.0f, 0.0f },
+ { +8.0f, +32.0f, +8.0f, 0.0f },
};
const float light_c[2][4] = {
{ 1.0f, 0.8f, 0.8f, 1.0f },
glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
}
-static void game_draw_back(int pose, int d, const float p[3])
+static void game_draw_back(int pose, int d, float t)
{
- float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
- float t = SDL_GetTicks() / 1000.f + 120.0f;
-
glPushMatrix();
{
if (d < 0)
glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
}
- glTranslatef(p[0], p[1], p[2]);
- glColor4fv(c);
+ glTranslatef(view_p[0], view_p[1] * d, view_p[2]);
if (config_get_d(CONFIG_BACKGROUND))
{
/* Draw all background layers back to front. */
- sol_back(&back, BACK_DIST, FAR_DIST, t);
+ sol_back(&back, BACK_DIST, FAR_DIST, t);
back_draw(0);
- sol_back(&back, 0, BACK_DIST, t);
-
- /* Draw all foreground geometry in the background file. */
-
- sol_draw(&back);
+ sol_back(&back, 0, BACK_DIST, t);
}
else back_draw(0);
}
glPopMatrix();
}
-static void game_draw_fore(int pose, float rx, float ry, int d, const float p[3])
+static void game_clip_refl(int d)
+{
+ /* Fudge to eliminate the floor from reflection. */
+
+ GLdouble e[4], k = -0.00001;
+
+ e[0] = 0;
+ e[1] = 1;
+ e[2] = 0;
+ e[3] = k;
+
+ glClipPlane(GL_CLIP_PLANE0, e);
+}
+
+static void game_clip_ball(int d, const float *p)
+{
+ GLdouble r, c[3], pz[4], nz[4];
+
+ /* Compute the plane giving the front of the ball, as seen from view_p. */
+
+ c[0] = p[0];
+ c[1] = p[1] * d;
+ c[2] = p[2];
+
+ pz[0] = view_p[0] - c[0];
+ pz[1] = view_p[1] - c[1];
+ pz[2] = view_p[2] - c[2];
+
+ r = sqrt(pz[0] * pz[0] + pz[1] * pz[1] + pz[2] * pz[2]);
+
+ pz[0] /= r;
+ pz[1] /= r;
+ pz[2] /= r;
+ pz[3] = -(pz[0] * c[0] +
+ pz[1] * c[1] +
+ pz[2] * c[2]);
+
+ /* Find the plane giving the back of the ball, as seen from view_p. */
+
+ nz[0] = -pz[0];
+ nz[1] = -pz[1];
+ nz[2] = -pz[2];
+ nz[3] = -pz[3];
+
+ /* Reflect these planes as necessary, and store them in the GL state. */
+
+ pz[1] *= d;
+ nz[1] *= d;
+
+ glClipPlane(GL_CLIP_PLANE1, nz);
+ glClipPlane(GL_CLIP_PLANE2, pz);
+}
+
+static void game_draw_fore(int pose, const float *M, int d, float t)
{
const float *ball_p = file.uv->p;
const float ball_r = file.uv->r;
- glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT);
+ glPushMatrix();
{
- glPushMatrix();
- {
- /* Rotate the environment about the position of the ball. */
+ /* Rotate the environment about the position of the ball. */
- glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
- glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
- glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
- glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
+ game_draw_tilt(d);
- if (d < 0)
- {
- GLdouble e[4];
+ /* Compute clipping planes for reflection and ball facing. */
- e[0] = +0;
- e[1] = +1;
- e[2] = +0;
- e[3] = -0.00001;
+ game_clip_refl(d);
+ game_clip_ball(d, ball_p);
- glEnable(GL_CLIP_PLANE0);
- glClipPlane(GL_CLIP_PLANE0, e);
- }
+ if (d < 0)
+ glEnable(GL_CLIP_PLANE0);
+
+ if (pose)
+ sol_draw(&file, 0, 1);
+ else
+ {
+ /* Draw the coins. */
+
+ game_draw_items(&file, t);
/* Draw the floor. */
- sol_draw(&file);
+ sol_draw(&file, 0, 1);
+
+ /* Draw the ball shadow. */
- if (config_get_d(CONFIG_SHADOW) && drawball)
+ if (d > 0 && config_get_d(CONFIG_SHADOW))
{
shad_draw_set(ball_p, ball_r);
sol_shad(&file);
shad_draw_clr();
}
- /* Draw the game elements. */
+ /* Draw the ball. */
+
+ game_draw_balls(&file, M, t);
+ }
+
+ /* Draw the particles and light columns. */
+
+ glEnable(GL_COLOR_MATERIAL);
+ glDisable(GL_LIGHTING);
+ glDepthMask(GL_FALSE);
+ {
+ glColor3f(1.0f, 1.0f, 1.0f);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ sol_bill(&file, M, t);
+ part_draw_coin(M, t);
- if (pose == 0)
+ glDisable(GL_TEXTURE_2D);
{
- part_draw_coin(-rx * d, -ry);
- game_draw_coins(&file);
- if (drawball)
- game_draw_balls(&file);
+ game_draw_goals(&file, M, t);
+ game_draw_jumps(&file);
+ game_draw_swchs(&file);
}
- game_draw_goals(&file, -rx * d, -ry);
- game_draw_jumps(&file);
- game_draw_swchs(&file);
+ glEnable(GL_TEXTURE_2D);
- glDisable(GL_CLIP_PLANE0);
+ glColor3f(1.0f, 1.0f, 1.0f);
}
- glPopMatrix();
+ glDepthMask(GL_TRUE);
+ glEnable(GL_LIGHTING);
+ glDisable(GL_COLOR_MATERIAL);
+
+ if (d < 0)
+ glDisable(GL_CLIP_PLANE0);
}
- glPopAttrib();
+ glPopMatrix();
}
-void game_draw(int pose, float st)
+void game_draw(int pose, float t)
{
float fov = view_fov;
config_push_persp(fov, 0.1f, FAR_DIST);
glPushMatrix();
{
- float v[3], rx, ry;
- float pup[3];
- float pdn[3];
+ float T[16], U[16], M[16], v[3];
- v_cpy(pup, view_p);
- v_cpy(pdn, view_p);
- pdn[1] = -pdn[1];
+ /* Compute direct and reflected view bases. */
- /* Compute and apply the view. */
+ v[0] = +view_p[0];
+ v[1] = -view_p[1];
+ v[2] = +view_p[2];
- v_sub(v, view_c, view_p);
+ m_view(T, view_c, view_p, view_e[1]);
+ m_view(U, view_c, v, view_e[1]);
- rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
- ry = V_DEG(fatan2f(+v[0], -v[2])) + st;
+ m_xps(M, T);
+
+ /* Apply current the view. */
+
+ v_sub(v, view_c, view_p);
glTranslatef(0.f, 0.f, -v_len(v));
- glRotatef(rx, 1.f, 0.f, 0.f);
- glRotatef(ry, 0.f, 1.f, 0.f);
+ glMultMatrixf(M);
glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
if (config_get_d(CONFIG_REFLECTION))
{
- /* Draw the mirror only into the stencil buffer. */
-
- glDisable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
- glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
- glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ {
+ /* Draw the mirrors only into the stencil buffer. */
- game_refl_all(0);
+ glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ glDepthMask(GL_FALSE);
- /* Draw the scene reflected into color and depth buffers. */
+ game_refl_all();
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
- glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
- glEnable(GL_DEPTH_TEST);
+ glDepthMask(GL_TRUE);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
- glFrontFace(GL_CW);
- glPushMatrix();
- {
- glScalef(+1.f, -1.f, +1.f);
+ /* Draw the scene reflected into color and depth buffers. */
- game_draw_light();
- game_draw_back(pose, -1, pdn);
- game_draw_fore(pose, rx, ry, -1, pdn);
- }
- glPopMatrix();
- glFrontFace(GL_CCW);
+ glFrontFace(GL_CW);
+ glPushMatrix();
+ {
+ glScalef(+1.0f, -1.0f, +1.0f);
+ game_draw_light();
+ game_draw_back(pose, -1, t);
+ game_draw_fore(pose, U, -1, t);
+ }
+ glPopMatrix();
+ glFrontFace(GL_CCW);
+ }
glDisable(GL_STENCIL_TEST);
}
/* Draw the scene normally. */
game_draw_light();
- game_refl_all(pose ? 0 : config_get_d(CONFIG_SHADOW));
- game_draw_back(pose, +1, pup);
- game_draw_fore(pose, rx, ry, +1, pup);
+ game_refl_all();
+ game_draw_back(pose, +1, t);
+ game_draw_fore(pose, T, +1, t);
}
glPopMatrix();
config_pop_matrix();
static void game_update_grav(float h[3], const float g[3])
{
- struct s_file *fp = &file;
-
float x[3];
- float y[3] = { 0.f, 1.f, 0.f };
+ float y[3] = { 0.0f, 1.0f, 0.0f };
float z[3];
float X[16];
float Z[16];
/* Compute the gravity vector from the given world rotations. */
- v_sub(z, view_p, fp->uv->p);
+ z[0] = fsinf(V_RAD(view_a));
+ z[1] = 0.0f;
+ z[2] = fcosf(V_RAD(view_a));
+
v_crs(x, y, z);
v_crs(z, x, y);
v_nrm(x, x);
static void game_update_view(float dt)
{
float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
- float dx = view_ry * dt * 5.0f;
+ float da = input_get_r() * dt * 90.0f;
float k;
- view_a += view_ry * dt * 90.f;
+ float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f };
+
+ view_a += da;
/* Center the view about the ball. */
v_cpy(view_c, file.uv->p);
v_inv(view_v, file.uv->v);
- switch (config_get_d(CONFIG_CAMERA))
+ view_e[2][0] = fsinf(V_RAD(view_a));
+ view_e[2][1] = 0.0;
+ view_e[2][2] = fcosf(V_RAD(view_a));
+
+ switch (input_get_c())
{
- case 1: /* Camera 1: Viewpoint chases the ball position. */
+ case 1: /* Camera 1: Viewpoint chases the ball position. */
+
+ /* TODO: This camera no longer exists. */
- v_sub(view_e[2], view_p, view_c);
break;
case 2: /* Camera 2: View vector is given by view angle. */
- view_e[2][0] = fsinf(V_RAD(view_a));
- view_e[2][1] = 0.f;
- view_e[2][2] = fcosf(V_RAD(view_a));
-
- dx = 0.0f;
-
break;
default: /* Default: View vector approaches the ball velocity vector. */
- k = v_dot(view_v, view_v);
-
- v_sub(view_e[2], view_p, view_c);
- v_mad(view_e[2], view_e[2], view_v, k * dt / 4);
+ v_mad(view_e[2], view_e[2], view_v, v_dot(view_v, view_v) * dt / 4);
break;
}
- /* Orthonormalize the basis of the view in its new position. */
+ /* 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]);
if (view_k < 0.5) view_k = 0.5;
- v_cpy(view_p, file.uv->p);
- v_mad(view_p, view_p, view_e[0], dx * view_k);
- v_mad(view_p, view_p, view_e[1], view_dp * view_k);
- v_mad(view_p, view_p, view_e[2], view_dz * view_k);
+ v_scl(v, view_e[1], view_dp * view_k);
+ v_mad(v, v, view_e[2], view_dz * view_k);
+ m_rot(M, Y, V_RAD(da));
+ m_vxfm(view_p, M, v);
+ v_add(view_p, view_p, file.uv->p);
/* Compute the new view center. */
/* The ticking clock. */
- if (b && clock_down)
+ if (b && timer_down)
{
- if (clock < 600.f)
- clock -= dt;
- if (clock < 0.f)
- clock = 0.f;
+ if (timer < 600.f)
+ timer -= dt;
+ if (timer < 0.f)
+ timer = 0.f;
}
else if (b)
{
- clock += dt;
+ timer += dt;
}
}
-static int game_update_state(int *state_value)
+static int game_update_state(int bt)
{
struct s_file *fp = &file;
+ struct s_goal *zp;
+ struct s_item *hp;
+
float p[3];
float c[3];
- int bt = state_value != NULL;
- int n;
- struct s_goal *g;
- /* Test for a coin grab. */
+ /* Test for an item. */
- if (bt && (n = sol_coin_test(fp, p, COIN_RADIUS)) > 0)
+ if (bt && (hp = sol_item_test(fp, p, COIN_RADIUS)))
{
- coin_color(c, n);
+ const char *sound = AUD_COIN;
+
+ item_color(hp, c);
part_burst(p, c);
- /*add coins if regular, change radius if not.*/
- if (n <= 10)
- coins += n;
- else
- {
- grow_set(fp, n); /*only 50 and 150 will produce results.*/
- n = 0;
- }
+ grow_init(fp, hp->t);
- /* Check for goal open. */
- if (goal_c > 0)
+ if (hp->t == ITEM_COIN)
{
- goal_c = goal_c - n;
- if (goal_c <= 0)
+ coins += hp->n;
+
+ /* Check for goal open. */
+
+ if (goal_c > 0)
{
- audio_play(AUD_SWITCH, 1.f);
- goal_c = 0;
+ goal_c -= hp->n;
+ if (goal_c <= 0)
+ {
+ sound = AUD_SWITCH;
+ goal_c = 0;
+ }
}
- else
- audio_play(AUD_COIN, 1.f);
}
- else
- audio_play(AUD_COIN, 1.f);
+ audio_play(sound, 1.f);
+
+ /* Reset item type. */
+
+ hp->t = ITEM_NONE;
}
/* Test for a switch. */
+
if (sol_swch_test(fp, 0))
audio_play(AUD_SWITCH, 1.f);
/* Test for a goal. */
- if (bt && goal_c == 0 && (g = sol_goal_test(fp, p, 0)))
+ if (bt && goal_c == 0 && (zp = sol_goal_test(fp, p, 0)))
{
- *state_value = g->s;
audio_play(AUD_GOAL, 1.0f);
- return g->c ? GAME_SPEC : GAME_GOAL;
+ return GAME_GOAL;
}
/* Test for time-out. */
- if (bt && clock_down && clock <= 0.f)
+ if (bt && timer_down && timer <= 0.f)
{
- const GLfloat *p = fp->uv->p;
- const GLfloat c[5] = {1.0f, 1.0f, 0.0f, 0.0f, 1.0f};
- part_burst(p, c);
- part_burst(p, c+1);
- part_burst(p, c+2);
- part_burst(p, c);
- part_burst(p, c+1);
- part_burst(p, c+2);
- drawball = 0;
audio_play(AUD_TIME, 1.0f);
return GAME_TIME;
}
return GAME_NONE;
}
-/*
- * On most hardware, rendering requires much more computing power than
- * physics. Since physics takes less time than graphics, it make sense to
- * detach the physics update time step from the graphics frame rate. By
- * performing multiple physics updates for each graphics update, we get away
- * with higher quality physics with little impact on overall performance.
- *
- * Toward this end, we establish a baseline maximum physics time step. If
- * the measured frame time exceeds this maximum, we cut the time step in
- * half, and do two updates. If THIS time step exceeds the maximum, we do
- * four updates. And so on. In this way, the physics system is allowed to
- * seek an optimal update rate independant of, yet in integral sync with, the
- * graphics frame rate.
- */
-
-int game_step(const float g[3], float dt, int *state_value)
+int game_step(const float g[3], float dt, int bt)
{
- struct s_file *fp = &file;
-
- float h[3];
- float d = 0.f;
- float b = 0.f;
- float t;
- int i, n = 1;
-
if (game_state)
{
- t = dt;
+ struct s_file *fp = &file;
+
+ float h[3];
/* Smooth jittery or discontinuous input. */
- if (t < RESPONSE)
- {
- game_rx += (game_ix - game_rx) * t / RESPONSE;
- game_rz += (game_iz - game_rz) * t / RESPONSE;
- }
- else
- {
- game_rx = game_ix;
- game_rz = game_iz;
- }
+ game_rx += (input_get_x() - game_rx) * dt / RESPONSE;
+ game_rz += (input_get_z() - game_rz) * dt / RESPONSE;
- if (grow) grow_ball(fp,dt);
+ grow_step(fp, dt);
game_update_grav(h, g);
- part_step(h, t);
+ part_step(h, dt);
- if (!drawball)
- /* nothing */;
- else if (jump_b)
+ if (jump_b)
{
- jump_dt += t;
+ jump_dt += dt;
/* Handle a jump. */
- if (0.5 < jump_dt)
+ 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];
}
- if (1.f < jump_dt)
+ if (1.0f < jump_dt)
jump_b = 0;
}
else
{
/* Run the sim. */
- while (t > MAX_DT && n < MAX_DN)
- {
- t /= 2;
- n *= 2;
- }
-
- for (i = 0; i < n; i++)
- if (b < (d = sol_step(fp, h, t, 0, NULL)))
- b = d;
+ float b = sol_step(fp, h, dt, 0, NULL);
/* Mix the sound of a ball bounce. */
- if (b > 0.5)
- audio_play(AUD_BUMP, (b - 0.5f) * 2.0f);
+ if (b > 0.5f)
+ {
+ float k = (b - 0.5f) * 2.0f;
+
+ if (got_orig)
+ {
+ if (fp->uv->r > grow_orig) audio_play(AUD_BUMPL, k);
+ else if (fp->uv->r < grow_orig) audio_play(AUD_BUMPS, k);
+ else audio_play(AUD_BUMPM, k);
+ }
+ else audio_play(AUD_BUMPM, k);
+ }
}
game_step_fade(dt);
game_update_view(dt);
- game_update_time(dt, state_value != NULL);
+ game_update_time(dt, bt);
- return game_update_state(state_value);
+ return game_update_state(bt);
}
return GAME_NONE;
}
/*---------------------------------------------------------------------------*/
-void game_no_aa(void)
+void game_set_x(int k)
{
- float max = game_ix * game_ix + game_iz * game_iz;
- if (max > ANGLE_BOUND * ANGLE_BOUND)
- {
- max = ANGLE_BOUND / sqrt(max);
- game_ix *= max;
- game_iz *= max;
- }
+ input_set_x(-ANGLE_BOUND * k / JOY_MAX);
}
-void game_set_x(int k)
+void game_set_z(int k)
{
- game_ix = -(ANGLE_BOUND) * k / JOY_MAX;
-#if NO_AA
- game_no_aa();
-#endif
+ input_set_z(+ANGLE_BOUND * k / JOY_MAX);
}
-void game_set_z(int k)
+void game_set_ang(int x, int z)
{
- game_iz = +ANGLE_BOUND * k / JOY_MAX;
-#if NO_AA
- game_no_aa();
-#endif
+ input_set_x(x);
+ input_set_z(z);
}
void game_set_pos(int x, int y)
{
- game_ix += 40.f * y / config_get_d(CONFIG_MOUSE_SENSE);
- game_iz += 40.f * x / config_get_d(CONFIG_MOUSE_SENSE);
-
-#if NO_AA
- game_no_aa();
-#else
- if (game_ix > +ANGLE_BOUND) game_ix = +ANGLE_BOUND;
- if (game_ix < -ANGLE_BOUND) game_ix = -ANGLE_BOUND;
- if (game_iz > +ANGLE_BOUND) game_iz = +ANGLE_BOUND;
- if (game_iz < -ANGLE_BOUND) game_iz = -ANGLE_BOUND;
-#endif
+ 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));
+}
+
+void game_set_cam(int c)
+{
+ input_set_c(c);
}
void game_set_rot(float r)
{
- view_ry = r;
+ input_set_r(r);
}
/*---------------------------------------------------------------------------*/
}
/*---------------------------------------------------------------------------*/
-
-int put_game_state(FILE *fout)
-{
- if (game_state)
- {
- /* Write the view and tilt state. */
-
- put_float(fout, &game_rx);
- put_float(fout, &game_rz);
- put_array(fout, view_c, 3);
- put_array(fout, view_p, 3);
-
- /* Write the game simulation state. */
-
- put_file_state(fout, &file);
-
- return 1;
- }
- return 0;
-}
-
-int get_game_state(FILE *fin)
-{
- if (game_state)
- {
- /* Read the view and tilt state. */
-
- get_float(fin, &game_rx);
- get_float(fin, &game_rz);
- get_array(fin, view_c, 3);
- get_array(fin, view_p, 3);
-
- /* Read the game simulation state. */
-
- get_file_state(fin, &file);
-
- return (feof(fin) ? 0 : 1);
- }
- return 0;
-}
-
-/*---------------------------------------------------------------------------*/