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.
32 /*---------------------------------------------------------------------------*/
34 static int game_state = 0;
36 static struct s_file file;
37 static struct s_file back;
39 static float clock = 0.f; /* Clock time */
40 static int clock_down = 1; /* Clock go up or down? */
42 static float game_ix; /* Input rotation about X axis */
43 static float game_iz; /* Input rotation about Z axis */
44 static float game_rx; /* Floor rotation about X axis */
45 static float game_rz; /* Floor rotation about Z axis */
47 static float view_a; /* Ideal view rotation about Y axis */
48 static float view_ry; /* Angular velocity about Y axis */
49 static float view_dc; /* Ideal view distance above ball */
50 static float view_dp; /* Ideal view distance above ball */
51 static float view_dz; /* Ideal view distance behind ball */
52 static float view_fov; /* Field of view */
54 static float view_c[3]; /* Current view center */
55 static float view_v[3]; /* Current view vector */
56 static float view_p[3]; /* Current view position */
57 static float view_e[3][3]; /* Current view orientation */
60 static int goal_e = 0; /* Goal enabled flag */
61 static float goal_k = 0; /* Goal animation */
62 static int goal_s = 0; /* Goal reached flag */
63 static int swch_e = 1; /* Switching enabled flag */
64 static int jump_e = 1; /* Jumping enabled flag */
65 static int jump_b = 0; /* Jump-in-progress flag */
66 static float jump_dt; /* Jump duration */
67 static float jump_p[3]; /* Jump destination */
68 static float fade_k = 0.0; /* Fade in/out level */
69 static float fade_d = 0.0; /* Fade in/out direction */
71 /*---------------------------------------------------------------------------*/
73 static void view_init(void)
78 view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
79 view_dp = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
80 view_dc = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
81 view_dz = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
103 int game_init(const char *file_name,
104 const char *back_name,
105 const char *grad_name, int t, int e)
107 clock = (float) t / 100.f;
108 clock_down = (t > 0);
118 /* Initialize jump and goal states. */
124 goal_k = e ? 1.0f : 0.0f;
133 /* Initialise the level, background, particles, fade, and view. */
138 part_reset(GOAL_HEIGHT);
140 back_init(grad_name, config_get_d(CONFIG_GEOMETRY));
142 if (sol_load(&back, config_data(back_name),
143 config_get_d(CONFIG_TEXTURES), 0) &&
144 sol_load(&file, config_data(file_name),
145 config_get_d(CONFIG_TEXTURES), config_get_d(CONFIG_SHADOW)))
146 return (game_state = 1);
148 return (game_state = 0);
162 /*---------------------------------------------------------------------------*/
166 return (int) (clock * 100.f);
169 char *curr_intro(void)
171 return (file.ac > 0) ? file.av : NULL;
174 /*---------------------------------------------------------------------------*/
176 static void game_draw_balls(const struct s_file *fp)
178 float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
181 m_basis(M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
185 glTranslatef(fp->uv[0].p[0],
186 fp->uv[0].p[1] + BALL_FUDGE,
189 glScalef(fp->uv[0].r,
200 static void game_draw_coins(const struct s_file *fp)
202 float r = 360.f * SDL_GetTicks() / 1000.f;
207 for (ci = 0; ci < fp->cc; ci++)
208 if (fp->cv[ci].n > 0)
212 glTranslatef(fp->cv[ci].p[0],
215 glRotatef(r, 0.0f, 1.0f, 0.0f);
216 coin_draw(fp->cv[ci].n, r);
224 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
229 for (zi = 0; zi < fp->zc; zi++)
233 glTranslatef(fp->zv[zi].p[0],
237 part_draw_goal(rx, ry, fp->zv[zi].r, goal_k);
239 glScalef(fp->zv[zi].r, goal_k, fp->zv[zi].r);
246 static void game_draw_jumps(const struct s_file *fp)
250 for (ji = 0; ji < fp->jc; ji++)
254 glTranslatef(fp->jv[ji].p[0],
258 glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
265 static void game_draw_swchs(const struct s_file *fp)
269 for (xi = 0; xi < fp->xc; xi++)
273 glTranslatef(fp->xv[xi].p[0],
277 glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
278 swch_draw(fp->xv[xi].f);
284 /*---------------------------------------------------------------------------*/
286 static void game_refl_all(int s)
288 const float *ball_p = file.uv->p;
292 /* Rotate the environment about the position of the ball. */
294 glTranslatef(+ball_p[0], +ball_p[1], +ball_p[2]);
295 glRotatef(-game_rz, view_e[2][0], view_e[2][1], view_e[2][2]);
296 glRotatef(-game_rx, view_e[0][0], view_e[0][1], view_e[0][2]);
297 glTranslatef(-ball_p[0], -ball_p[1], -ball_p[2]);
299 /* Draw the floor. */
306 /*---------------------------------------------------------------------------*/
308 static void game_draw_light(void)
310 const float light_p[2][4] = {
311 { -8.0f, +32.0f, -8.0f, 1.0f },
312 { +8.0f, +32.0f, +8.0f, 1.0f },
314 const float light_c[2][4] = {
315 { 1.0f, 0.8f, 0.8f, 1.0f },
316 { 0.8f, 1.0f, 0.8f, 1.0f },
319 /* Configure the lighting. */
322 glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
323 glLightfv(GL_LIGHT0, GL_DIFFUSE, light_c[0]);
324 glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
327 glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
328 glLightfv(GL_LIGHT1, GL_DIFFUSE, light_c[1]);
329 glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
332 static void game_draw_back(int pose, int d, const float p[3])
334 float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
335 float t = SDL_GetTicks() / 1000.f + 120.0f;
341 glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
342 glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
345 glTranslatef(p[0], p[1], p[2]);
348 if (config_get_d(CONFIG_BACKGROUND))
350 /* Draw all background layers back to front. */
352 sol_back(&back, BACK_DIST, FAR_DIST, t);
354 sol_back(&back, 0, BACK_DIST, t);
356 /* Draw all foreground geometry in the background file. */
365 static void game_draw_fore(int pose, float rx, float ry, int d, const float p[3])
367 const float *ball_p = file.uv->p;
368 const float ball_r = file.uv->r;
370 glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT);
374 /* Rotate the environment about the position of the ball. */
376 glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
377 glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
378 glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
379 glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
390 glEnable(GL_CLIP_PLANE0);
391 glClipPlane(GL_CLIP_PLANE0, e);
394 /* Draw the floor. */
398 if (config_get_d(CONFIG_SHADOW))
400 shad_draw_set(ball_p, ball_r);
405 /* Draw the game elements. */
408 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
412 part_draw_coin(-rx * d, -ry);
413 game_draw_coins(&file);
414 game_draw_balls(&file);
416 game_draw_goals(&file, -rx * d, -ry);
417 game_draw_jumps(&file);
418 game_draw_swchs(&file);
420 glDisable(GL_CLIP_PLANE0);
427 void game_draw(int pose, float st)
429 float fov = view_fov;
431 if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
435 config_push_persp(fov, 0.1f, FAR_DIST);
446 /* Compute and apply the view. */
448 v_sub(v, view_c, view_p);
450 rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
451 ry = V_DEG(fatan2f(+v[0], -v[2])) + st;
453 glTranslatef(0.f, 0.f, -v_len(v));
454 glRotatef(rx, 1.f, 0.f, 0.f);
455 glRotatef(ry, 0.f, 1.f, 0.f);
456 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
458 if (config_get_d(CONFIG_REFLECTION))
460 /* Draw the mirror only into the stencil buffer. */
462 glDisable(GL_DEPTH_TEST);
463 glEnable(GL_STENCIL_TEST);
464 glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
465 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
466 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
470 /* Draw the scene reflected into color and depth buffers. */
472 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
473 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
474 glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
475 glEnable(GL_DEPTH_TEST);
480 glScalef(+1.f, -1.f, +1.f);
483 game_draw_back(pose, -1, pdn);
484 game_draw_fore(pose, rx, ry, -1, pdn);
489 glDisable(GL_STENCIL_TEST);
492 /* Draw the scene normally. */
495 game_refl_all(pose ? 0 : config_get_d(CONFIG_SHADOW));
496 game_draw_back(pose, +1, pup);
497 game_draw_fore(pose, rx, ry, +1, pup);
502 /* Draw the fade overlay. */
508 /*---------------------------------------------------------------------------*/
510 static void game_update_grav(float h[3], const float g[3])
512 struct s_file *fp = &file;
515 float y[3] = { 0.f, 1.f, 0.f };
521 /* Compute the gravity vector from the given world rotations. */
523 v_sub(z, view_p, fp->uv->p);
529 m_rot (Z, z, V_RAD(game_rz));
530 m_rot (X, x, V_RAD(game_rx));
535 static void game_update_view(float dt)
537 float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
538 float dx = view_ry * dt * 5.0f;
541 view_a += view_ry * dt * 90.f;
543 /* Center the view about the ball. */
545 v_cpy(view_c, file.uv->p);
546 v_inv(view_v, file.uv->v);
548 switch (config_get_d(CONFIG_CAMERA))
550 case 1: /* Camera 1: Viewpoint chases the ball position. */
552 v_sub(view_e[2], view_p, view_c);
555 case 2: /* Camera 2: View vector is given by view angle. */
557 view_e[2][0] = fsinf(V_RAD(view_a));
559 view_e[2][2] = fcosf(V_RAD(view_a));
565 default: /* Default: View vector approaches the ball velocity vector. */
567 k = v_dot(view_v, view_v);
569 v_sub(view_e[2], view_p, view_c);
570 v_mad(view_e[2], view_e[2], view_v, k * dt / 4);
575 /* Orthonormalize the basis of the view in its new position. */
577 v_crs(view_e[0], view_e[1], view_e[2]);
578 v_crs(view_e[2], view_e[0], view_e[1]);
579 v_nrm(view_e[0], view_e[0]);
580 v_nrm(view_e[2], view_e[2]);
582 /* Compute the new view position. */
584 k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
586 view_k = view_k + (k - view_k) * dt;
588 if (view_k < 0.5) view_k = 0.5;
590 v_cpy(view_p, file.uv->p);
591 v_mad(view_p, view_p, view_e[0], dx * view_k);
592 v_mad(view_p, view_p, view_e[1], view_dp * view_k);
593 v_mad(view_p, view_p, view_e[2], view_dz * view_k);
595 /* Compute the new view center. */
597 v_cpy(view_c, file.uv->p);
598 v_mad(view_c, view_c, view_e[1], dc);
600 /* Note the current view angle. */
602 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
605 static void game_update_time(float dt, int b)
607 int tick = (int) floor(clock);
608 int tock = (int) floor(clock * 2);
610 if (goal_e && goal_k < 1.0f)
613 /* The ticking clock. */
622 if (0 < tick && tick <= 10 && tick == (int) ceil(clock))
624 audio_play(AUD_TICK, 1.f);
625 hud_time_pulse(1.50);
627 else if (0 < tock && tock <= 10 && tock == (int) ceil(clock * 2))
629 audio_play(AUD_TOCK, 1.f);
630 hud_time_pulse(1.25);
639 static int game_update_state(int bt)
641 struct s_file *fp = &file;
646 /* Test for a coin grab. */
648 if (bt && (n = sol_coin_test(fp, p, COIN_RADIUS)) > 0)
657 /* Test for a switch. */
659 if ((swch_e = sol_swch_test(fp, swch_e, 0)) != e && e)
660 audio_play(AUD_SWITCH, 1.f);
662 /* Test for a jump. */
664 if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
670 audio_play(AUD_JUMP, 1.f);
672 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
675 /* Test for a goal. */
677 if (bt && goal_e && sol_goal_test(fp, p, 0))
682 audio_play(AUD_GOAL, 1.0f);
687 /* Test for time-out. */
689 if (bt && clock_down && clock <= 0.f)
692 /* Test for fall-out. */
694 if (bt && fp->uv[0].p[1] < fp->vv[0].p[1])
701 * On most hardware, rendering requires much more computing power than
702 * physics. Since physics takes less time than graphics, it make sense to
703 * detach the physics update time step from the graphics frame rate. By
704 * performing multiple physics updates for each graphics update, we get away
705 * with higher quality physics with little impact on overall performance.
707 * Toward this end, we establish a baseline maximum physics time step. If
708 * the measured frame time exceeds this maximum, we cut the time step in
709 * half, and do two updates. If THIS time step exceeds the maximum, we do
710 * four updates. And so on. In this way, the physics system is allowed to
711 * seek an optimal update rate independant of, yet in integral sync with, the
712 * graphics frame rate.
715 int game_step(const float g[3], float dt, int bt)
717 struct s_file *fp = &file;
729 /* Smooth jittery or discontinuous input. */
733 game_rx += (game_ix - game_rx) * t / RESPONSE;
734 game_rz += (game_iz - game_rz) * t / RESPONSE;
742 game_update_grav(h, g);
753 fp->uv[0].p[0] = jump_p[0];
754 fp->uv[0].p[1] = jump_p[1];
755 fp->uv[0].p[2] = jump_p[2];
764 while (t > MAX_DT && n < MAX_DN)
770 for (i = 0; i < n; i++)
771 if (b < (d = sol_step(fp, h, t, 0, NULL)))
774 /* Mix the sound of a ball bounce. */
777 audio_play(AUD_BUMP, (b - 0.5f) * 2.0f);
781 game_update_view(dt);
782 game_update_time(dt, bt);
784 return game_update_state(bt);
789 /*---------------------------------------------------------------------------*/
791 void game_no_aa(void)
793 float max = game_ix * game_ix + game_iz * game_iz;
794 if (max > ANGLE_BOUND * ANGLE_BOUND)
796 max = ANGLE_BOUND / sqrt(max);
802 void game_set_x(int k)
804 game_ix = -(ANGLE_BOUND) * k / JOY_MAX;
810 void game_set_z(int k)
812 game_iz = +ANGLE_BOUND * k / JOY_MAX;
818 void game_set_pos(int x, int y)
820 game_ix += 40.f * y / config_get_d(CONFIG_MOUSE_SENSE);
821 game_iz += 40.f * x / config_get_d(CONFIG_MOUSE_SENSE);
826 if (game_ix > +ANGLE_BOUND) game_ix = +ANGLE_BOUND;
827 if (game_ix < -ANGLE_BOUND) game_ix = -ANGLE_BOUND;
828 if (game_iz > +ANGLE_BOUND) game_iz = +ANGLE_BOUND;
829 if (game_iz < -ANGLE_BOUND) game_iz = -ANGLE_BOUND;
833 void game_set_rot(float r)
838 /*---------------------------------------------------------------------------*/
840 void game_set_fly(float k)
842 struct s_file *fp = &file;
844 float x[3] = { 1.f, 0.f, 0.f };
845 float y[3] = { 0.f, 1.f, 0.f };
846 float z[3] = { 0.f, 0.f, 1.f };
847 float c0[3] = { 0.f, 0.f, 0.f };
848 float p0[3] = { 0.f, 0.f, 0.f };
849 float c1[3] = { 0.f, 0.f, 0.f };
850 float p1[3] = { 0.f, 0.f, 0.f };
857 /* k = 0.0 view is at the ball. */
861 v_cpy(c0, fp->uv[0].p);
862 v_cpy(p0, fp->uv[0].p);
865 v_mad(p0, p0, y, view_dp);
866 v_mad(p0, p0, z, view_dz);
867 v_mad(c0, c0, y, view_dc);
869 /* k = +1.0 view is s_view 0 */
871 if (k >= 0 && fp->wc > 0)
873 v_cpy(p1, fp->wv[0].p);
874 v_cpy(c1, fp->wv[0].q);
877 /* k = -1.0 view is s_view 1 */
879 if (k <= 0 && fp->wc > 1)
881 v_cpy(p1, fp->wv[1].p);
882 v_cpy(c1, fp->wv[1].q);
885 /* Interpolate the views. */
888 v_mad(view_p, p0, v, k * k);
891 v_mad(view_c, c0, v, k * k);
893 /* Orthonormalize the view basis. */
895 v_sub(view_e[2], view_p, view_c);
896 v_crs(view_e[0], view_e[1], view_e[2]);
897 v_crs(view_e[2], view_e[0], view_e[1]);
898 v_nrm(view_e[0], view_e[0]);
899 v_nrm(view_e[2], view_e[2]);
902 void game_look(float phi, float theta)
904 view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
905 view_c[1] = view_p[1] + fsinf(V_RAD(phi));
906 view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
909 /*---------------------------------------------------------------------------*/
911 void game_kill_fade(void)
917 void game_step_fade(float dt)
919 if ((fade_k < 1.0f && fade_d > 0.0f) ||
920 (fade_k > 0.0f && fade_d < 0.0f))
921 fade_k += fade_d * dt;
935 void game_fade(float d)
940 /*---------------------------------------------------------------------------*/
942 int put_game_state(FILE *fout)
946 /* Write the view and tilt state. */
948 put_float(fout, &game_rx);
949 put_float(fout, &game_rz);
950 put_array(fout, view_c, 3);
951 put_array(fout, view_p, 3);
953 /* Write the game simulation state. */
955 put_file_state(fout, &file);
962 int get_game_state(FILE *fin)
966 /* Read the view and tilt state. */
968 get_float(fin, &game_rx);
969 get_float(fin, &game_rz);
970 get_array(fin, view_c, 3);
971 get_array(fin, view_p, 3);
973 /* Read the game simulation state. */
975 get_file_state(fin, &file);
977 return (feof(fin) ? 0 : 1);
982 /*---------------------------------------------------------------------------*/