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 /*---------------------------------------------------------------------------*/
33 static int game_state = 0;
35 static struct s_file file;
36 static struct s_file back;
38 static float clock = 0.f; /* Clock time */
39 static int clock_down = 1; /* Clock go up or down? */
41 static float game_ix; /* Input rotation about X axis */
42 static float game_iz; /* Input rotation about Z axis */
43 static float game_rx; /* Floor rotation about X axis */
44 static float game_rz; /* Floor rotation about Z axis */
46 static float view_a; /* Ideal view rotation about Y axis */
47 static float view_ry; /* Angular velocity about Y axis */
48 static float view_dc; /* Ideal view distance above ball */
49 static float view_dp; /* Ideal view distance above ball */
50 static float view_dz; /* Ideal view distance behind ball */
51 static float view_fov; /* Field of view */
53 static float view_c[3]; /* Current view center */
54 static float view_v[3]; /* Current view vector */
55 static float view_p[3]; /* Current view position */
56 static float view_e[3][3]; /* Current view orientation */
59 static int coins = 0; /* Collected coins */
60 static int goal_c = 0; /* Goal coins remaining (0 = open) */
61 static float goal_k = 0; /* Goal animation */
62 static int jump_e = 1; /* Jumping enabled flag */
63 static int jump_b = 0; /* Jump-in-progress flag */
64 static float jump_dt; /* Jump duration */
65 static float jump_p[3]; /* Jump destination */
66 static float fade_k = 0.0; /* Fade in/out level */
67 static float fade_d = 0.0; /* Fade in/out direction */
68 static int ball_b = 0; /* Is the ball a bonus ball? */
70 static int grow = 0; /* Should the ball be changing size? */
71 static float grow_orig = 0; /* the original ball size */
72 static float grow_goal = 0; /* how big or small to get! */
73 static float grow_t = 0.0; /* timer for the ball to grow... */
74 static float grow_strt = 0; /* starting value for growth */
75 static int got_orig = 0; /* Do we know original ball size? */
77 #define GROW_TIME 0.5f /* sec for the ball to get to size. */
78 #define GROW_BIG 1.5f /* large factor */
79 #define GROW_SMALL 0.5f /* small factor */
81 /*---------------------------------------------------------------------------*/
83 static void grow_set(const struct s_file *fp, int type)
87 grow_orig = fp->uv->r;
88 grow_goal = grow_orig;
89 grow_strt = grow_orig;
96 audio_play(AUD_SHRINK, 1.f);
98 if (grow_goal == grow_orig * GROW_SMALL)
100 else if (grow_goal == grow_orig * GROW_BIG)
102 grow_goal = grow_orig;
107 grow_goal = grow_orig * GROW_SMALL;
114 audio_play(AUD_GROW, 1.f);
116 if (grow_goal == grow_orig * GROW_BIG)
118 else if (grow_goal == grow_orig * GROW_SMALL)
121 grow_goal = grow_orig;
125 grow_goal = grow_orig * GROW_BIG;
138 grow_strt = fp->uv->r;
142 static void grow_ball(const struct s_file *fp, float dt)
146 /* Calculate new size based on how long since you touched the coin... */
150 if (grow_t >= GROW_TIME)
156 dr = grow_strt + ((grow_goal-grow_strt) * (1.0f / (GROW_TIME / grow_t)));
158 /* No sinking through the floor! Keeps ball's bottom constant. */
159 fp->uv->p[1] += (dr - fp->uv->r);
163 static void view_init(void)
168 view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
169 view_dp = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
170 view_dc = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
171 view_dz = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
193 int game_init(const struct level *level, int t, int g)
195 clock = (float) t / 100.f;
196 clock_down = (t > 0);
202 if (!sol_load_gl(&file, config_data(level->file),
203 config_get_d(CONFIG_TEXTURES),
204 config_get_d(CONFIG_SHADOW)))
205 return (game_state = 0);
214 /* Initialize jump and goal states. */
220 goal_k = (g == 0) ? 1.0f : 0.0f;
222 /* Initialise the level, background, particles, fade, and view. */
227 part_reset(GOAL_HEIGHT);
229 back_init(level->grad, config_get_d(CONFIG_GEOMETRY));
231 sol_load_gl(&back, config_data(level->back),
232 config_get_d(CONFIG_TEXTURES), 0);
234 /* Initialize ball size tracking... */
253 /*---------------------------------------------------------------------------*/
257 return (int) (clock * 100.f);
270 /*---------------------------------------------------------------------------*/
272 static void game_draw_balls(const struct s_file *fp)
274 float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
277 m_basis(M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
281 glTranslatef(fp->uv[0].p[0],
282 fp->uv[0].p[1] + BALL_FUDGE,
285 glScalef(fp->uv[0].r,
296 static void game_draw_items(const struct s_file *fp)
298 float r = 360.f * SDL_GetTicks() / 1000.f;
301 /* FIXME: Draw items of different types in one pass? */
303 item_push(ITEM_COIN);
305 for (hi = 0; hi < fp->hc; hi++)
307 if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
311 glTranslatef(fp->hv[hi].p[0],
314 glRotatef(r, 0.0f, 1.0f, 0.0f);
315 item_draw(&fp->hv[hi], r);
322 item_push(ITEM_SHRINK);
324 for (hi = 0; hi < fp->hc; hi++)
326 if (fp->hv[hi].t == ITEM_SHRINK)
330 glTranslatef(fp->hv[hi].p[0],
333 glRotatef(r, 0.0f, 1.0f, 0.0f);
334 item_draw(&fp->hv[hi], r);
341 item_push(ITEM_GROW);
343 for (hi = 0; hi < fp->hc; hi++)
345 if (fp->hv[hi].t == ITEM_GROW)
349 glTranslatef(fp->hv[hi].p[0],
352 glRotatef(r, 0.0f, 1.0f, 0.0f);
353 item_draw(&fp->hv[hi], r);
361 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
366 for (zi = 0; zi < fp->zc; zi++)
370 glTranslatef(fp->zv[zi].p[0],
374 part_draw_goal(rx, ry, fp->zv[zi].r, goal_k, fp->zv[zi].c);
376 glScalef(fp->zv[zi].r, goal_k, fp->zv[zi].r);
383 static void game_draw_jumps(const struct s_file *fp)
387 for (ji = 0; ji < fp->jc; ji++)
391 glTranslatef(fp->jv[ji].p[0],
395 glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
402 static void game_draw_swchs(const struct s_file *fp)
406 for (xi = 0; xi < fp->xc; xi++)
413 glTranslatef(fp->xv[xi].p[0],
417 glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
418 swch_draw(fp->xv[xi].f, fp->xv[xi].e);
424 /*---------------------------------------------------------------------------*/
426 static void game_refl_all(int s)
428 const float *ball_p = file.uv->p;
432 /* Rotate the environment about the position of the ball. */
434 glTranslatef(+ball_p[0], +ball_p[1], +ball_p[2]);
435 glRotatef(-game_rz, view_e[2][0], view_e[2][1], view_e[2][2]);
436 glRotatef(-game_rx, view_e[0][0], view_e[0][1], view_e[0][2]);
437 glTranslatef(-ball_p[0], -ball_p[1], -ball_p[2]);
439 /* Draw the floor. */
446 /*---------------------------------------------------------------------------*/
448 static void game_draw_light(void)
450 const float light_p[2][4] = {
451 { -8.0f, +32.0f, -8.0f, 1.0f },
452 { +8.0f, +32.0f, +8.0f, 1.0f },
454 const float light_c[2][4] = {
455 { 1.0f, 0.8f, 0.8f, 1.0f },
456 { 0.8f, 1.0f, 0.8f, 1.0f },
459 /* Configure the lighting. */
462 glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
463 glLightfv(GL_LIGHT0, GL_DIFFUSE, light_c[0]);
464 glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
467 glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
468 glLightfv(GL_LIGHT1, GL_DIFFUSE, light_c[1]);
469 glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
472 static void game_draw_back(int pose, int d, const float p[3])
474 float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
475 float t = SDL_GetTicks() / 1000.f + 120.0f;
481 glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
482 glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
485 glTranslatef(p[0], p[1], p[2]);
488 if (config_get_d(CONFIG_BACKGROUND))
490 /* Draw all background layers back to front. */
492 sol_back(&back, BACK_DIST, FAR_DIST, t);
494 sol_back(&back, 0, BACK_DIST, t);
496 /* Draw all foreground geometry in the background file. */
505 static void game_draw_fore(int pose, float rx, float ry, int d, const float p[3])
507 const float *ball_p = file.uv->p;
508 const float ball_r = file.uv->r;
510 glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT);
514 /* Rotate the environment about the position of the ball. */
516 glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
517 glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
518 glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
519 glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
530 glEnable(GL_CLIP_PLANE0);
531 glClipPlane(GL_CLIP_PLANE0, e);
534 /* Draw the floor. */
538 if (config_get_d(CONFIG_SHADOW))
540 shad_draw_set(ball_p, ball_r);
545 /* Draw the game elements. */
548 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
552 part_draw_coin(-rx * d, -ry);
553 game_draw_items(&file);
554 game_draw_balls(&file);
556 game_draw_goals(&file, -rx * d, -ry);
557 game_draw_jumps(&file);
558 game_draw_swchs(&file);
560 glDisable(GL_CLIP_PLANE0);
567 void game_draw(int pose, float st)
569 float fov = view_fov;
571 if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
575 config_push_persp(fov, 0.1f, FAR_DIST);
586 /* Compute and apply the view. */
588 v_sub(v, view_c, view_p);
590 rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
591 ry = V_DEG(fatan2f(+v[0], -v[2])) + st;
593 glTranslatef(0.f, 0.f, -v_len(v));
594 glRotatef(rx, 1.f, 0.f, 0.f);
595 glRotatef(ry, 0.f, 1.f, 0.f);
596 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
598 if (config_get_d(CONFIG_REFLECTION))
600 /* Draw the mirror only into the stencil buffer. */
602 glDisable(GL_DEPTH_TEST);
603 glEnable(GL_STENCIL_TEST);
604 glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
605 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
606 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
610 /* Draw the scene reflected into color and depth buffers. */
612 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
613 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
614 glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
615 glEnable(GL_DEPTH_TEST);
620 glScalef(+1.f, -1.f, +1.f);
623 game_draw_back(pose, -1, pdn);
624 game_draw_fore(pose, rx, ry, -1, pdn);
629 glDisable(GL_STENCIL_TEST);
632 /* Draw the scene normally. */
635 game_refl_all(pose ? 0 : config_get_d(CONFIG_SHADOW));
636 game_draw_back(pose, +1, pup);
637 game_draw_fore(pose, rx, ry, +1, pup);
642 /* Draw the fade overlay. */
648 /*---------------------------------------------------------------------------*/
650 static void game_update_grav(float h[3], const float g[3])
652 struct s_file *fp = &file;
655 float y[3] = { 0.f, 1.f, 0.f };
661 /* Compute the gravity vector from the given world rotations. */
663 v_sub(z, view_p, fp->uv->p);
669 m_rot (Z, z, V_RAD(game_rz));
670 m_rot (X, x, V_RAD(game_rx));
675 static void game_update_view(float dt)
677 float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
678 float dx = view_ry * dt * 5.0f;
681 view_a += view_ry * dt * 90.f;
683 /* Center the view about the ball. */
685 v_cpy(view_c, file.uv->p);
686 v_inv(view_v, file.uv->v);
688 switch (config_get_d(CONFIG_CAMERA))
690 case 1: /* Camera 1: Viewpoint chases the ball position. */
692 v_sub(view_e[2], view_p, view_c);
695 case 2: /* Camera 2: View vector is given by view angle. */
697 view_e[2][0] = fsinf(V_RAD(view_a));
699 view_e[2][2] = fcosf(V_RAD(view_a));
705 default: /* Default: View vector approaches the ball velocity vector. */
707 k = v_dot(view_v, view_v);
709 v_sub(view_e[2], view_p, view_c);
710 v_mad(view_e[2], view_e[2], view_v, k * dt / 4);
715 /* Orthonormalize the basis of the view in its new position. */
717 v_crs(view_e[0], view_e[1], view_e[2]);
718 v_crs(view_e[2], view_e[0], view_e[1]);
719 v_nrm(view_e[0], view_e[0]);
720 v_nrm(view_e[2], view_e[2]);
722 /* Compute the new view position. */
724 k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
726 view_k = view_k + (k - view_k) * dt;
728 if (view_k < 0.5) view_k = 0.5;
730 v_cpy(view_p, file.uv->p);
731 v_mad(view_p, view_p, view_e[0], dx * view_k);
732 v_mad(view_p, view_p, view_e[1], view_dp * view_k);
733 v_mad(view_p, view_p, view_e[2], view_dz * view_k);
735 /* Compute the new view center. */
737 v_cpy(view_c, file.uv->p);
738 v_mad(view_c, view_c, view_e[1], dc);
740 /* Note the current view angle. */
742 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
745 static void game_update_time(float dt, int b)
747 if (goal_c == 0 && goal_k < 1.0f)
750 /* The ticking clock. */
765 static int game_update_state(int *state_value)
767 struct s_file *fp = &file;
774 int bt = state_value != NULL;
776 /* Test for an item. */
777 if (bt && (hp = sol_item_test(fp, p, COIN_RADIUS)))
779 int sound = AUD_COIN;
786 if (hp->t == ITEM_COIN)
790 /* Check for goal open. */
801 audio_play(sound, 1.f);
803 /* Reset item type. */
807 /* Test for a switch. */
808 if (sol_swch_test(fp, 0))
809 audio_play(AUD_SWITCH, 1.f);
811 /* Test for a jump. */
813 if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
819 audio_play(AUD_JUMP, 1.f);
821 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
824 /* Test for a goal. */
826 if (bt && goal_c == 0 && (zp = sol_goal_test(fp, p, 0)))
828 *state_value = zp->s;
829 audio_play(AUD_GOAL, 1.0f);
830 return zp->c ? GAME_SPEC : GAME_GOAL;
833 /* Test for time-out. */
835 if (bt && clock_down && clock <= 0.f)
837 audio_play(AUD_TIME, 1.0f);
841 /* Test for fall-out. */
843 if (bt && fp->uv[0].p[1] < fp->vv[0].p[1])
845 audio_play(AUD_FALL, 1.0f);
853 * By performing multiple physics updates or skipping an update for a given
854 * graphics update, we get away with higher quality physics with little
855 * impact on overall performance. Toward this end, we establish a baseline
856 * maximum and minimum physics time step. If the measured frame time exceeds
857 * the maximum time step, we cut the time step in half, and do two updates.
858 * If THIS time step exceeds the maximum, we do four updates. And so on. On
859 * the other hand, if the frame time is lower than the minimum time step, we
860 * skip an update and do so until the accumulated frame time has reached this
861 * minimum. In this way, the physics system is allowed to seek an optimal
862 * update rate independent of, yet in integral sync with, the graphics frame
866 int game_step(const float g[3], float dt, int *state_value)
868 struct s_file *fp = &file;
880 /* Smooth jittery or discontinuous input. */
884 game_rx += (game_ix - game_rx) * t / RESPONSE;
885 game_rz += (game_iz - game_rz) * t / RESPONSE;
896 game_update_grav(h, g);
907 fp->uv[0].p[0] = jump_p[0];
908 fp->uv[0].p[1] = jump_p[1];
909 fp->uv[0].p[2] = jump_p[2];
916 static float accumulated_t = 0.f;
922 if (accumulated_t < MIN_DT)
932 while (t > MAX_DT && n < MAX_DN)
938 for (i = 0; i < n; i++)
939 if (b < (d = sol_step(fp, h, t, 0, NULL)))
942 /* Mix the sound of a ball bounce. */
946 float k = (b - 0.5f) * 2.0f;
950 if (fp->uv->r > grow_orig) audio_play(AUD_BUMPL, k);
951 else if (fp->uv->r < grow_orig) audio_play(AUD_BUMPS, k);
952 else audio_play(AUD_BUMPM, k);
954 else audio_play(AUD_BUMPM, k);
959 game_update_view(dt);
960 game_update_time(dt, state_value != NULL);
962 return game_update_state(state_value);
967 /*---------------------------------------------------------------------------*/
969 void game_no_aa(void)
971 float max = game_ix * game_ix + game_iz * game_iz;
972 if (max > ANGLE_BOUND * ANGLE_BOUND)
974 max = ANGLE_BOUND / sqrt(max);
980 void game_set_x(int k)
982 game_ix = -(ANGLE_BOUND) * k / JOY_MAX;
988 void game_set_z(int k)
990 game_iz = +ANGLE_BOUND * k / JOY_MAX;
996 void game_set_pos(int x, int y)
998 game_ix += 40.f * y / config_get_d(CONFIG_MOUSE_SENSE);
999 game_iz += 40.f * x / config_get_d(CONFIG_MOUSE_SENSE);
1004 if (game_ix > +ANGLE_BOUND) game_ix = +ANGLE_BOUND;
1005 if (game_ix < -ANGLE_BOUND) game_ix = -ANGLE_BOUND;
1006 if (game_iz > +ANGLE_BOUND) game_iz = +ANGLE_BOUND;
1007 if (game_iz < -ANGLE_BOUND) game_iz = -ANGLE_BOUND;
1011 void game_set_rot(float r)
1016 /*---------------------------------------------------------------------------*/
1018 void game_set_fly(float k)
1020 struct s_file *fp = &file;
1022 float x[3] = { 1.f, 0.f, 0.f };
1023 float y[3] = { 0.f, 1.f, 0.f };
1024 float z[3] = { 0.f, 0.f, 1.f };
1025 float c0[3] = { 0.f, 0.f, 0.f };
1026 float p0[3] = { 0.f, 0.f, 0.f };
1027 float c1[3] = { 0.f, 0.f, 0.f };
1028 float p1[3] = { 0.f, 0.f, 0.f };
1031 z[0] = fsinf(V_RAD(view_a));
1032 z[2] = fcosf(V_RAD(view_a));
1034 v_cpy(view_e[0], x);
1035 v_cpy(view_e[1], y);
1036 v_cpy(view_e[2], z);
1038 /* k = 0.0 view is at the ball. */
1042 v_cpy(c0, fp->uv[0].p);
1043 v_cpy(p0, fp->uv[0].p);
1046 v_mad(p0, p0, y, view_dp);
1047 v_mad(p0, p0, z, view_dz);
1048 v_mad(c0, c0, y, view_dc);
1050 /* k = +1.0 view is s_view 0 */
1052 if (k >= 0 && fp->wc > 0)
1054 v_cpy(p1, fp->wv[0].p);
1055 v_cpy(c1, fp->wv[0].q);
1058 /* k = -1.0 view is s_view 1 */
1060 if (k <= 0 && fp->wc > 1)
1062 v_cpy(p1, fp->wv[1].p);
1063 v_cpy(c1, fp->wv[1].q);
1066 /* Interpolate the views. */
1069 v_mad(view_p, p0, v, k * k);
1072 v_mad(view_c, c0, v, k * k);
1074 /* Orthonormalize the view basis. */
1076 v_sub(view_e[2], view_p, view_c);
1077 v_crs(view_e[0], view_e[1], view_e[2]);
1078 v_crs(view_e[2], view_e[0], view_e[1]);
1079 v_nrm(view_e[0], view_e[0]);
1080 v_nrm(view_e[2], view_e[2]);
1083 void game_look(float phi, float theta)
1085 view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
1086 view_c[1] = view_p[1] + fsinf(V_RAD(phi));
1087 view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
1090 /*---------------------------------------------------------------------------*/
1092 void game_kill_fade(void)
1098 void game_step_fade(float dt)
1100 if ((fade_k < 1.0f && fade_d > 0.0f) ||
1101 (fade_k > 0.0f && fade_d < 0.0f))
1102 fade_k += fade_d * dt;
1116 void game_fade(float d)
1121 /*---------------------------------------------------------------------------*/
1123 int put_game_state(FILE *fout)
1127 /* Write the view and tilt state. */
1129 put_float(fout, &game_rx);
1130 put_float(fout, &game_rz);
1131 put_array(fout, view_c, 3);
1132 put_array(fout, view_p, 3);
1134 /* Write the game simulation state. */
1136 put_file_state(fout, &file);
1143 int get_game_state(FILE *fin)
1147 /* Read the view and tilt state. */
1149 get_float(fin, &game_rx);
1150 get_float(fin, &game_rz);
1151 get_array(fin, view_c, 3);
1152 get_array(fin, view_p, 3);
1154 /* Read the game simulation state. */
1156 get_file_state(fin, &file);
1158 return (feof(fin) ? 0 : 1);
1163 /*---------------------------------------------------------------------------*/