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_rx; /* Floor rotation about X axis */
42 static float game_rz; /* Floor rotation about Z axis */
44 static float view_a; /* Ideal view rotation about Y axis */
45 static float view_dc; /* Ideal view distance above ball */
46 static float view_dp; /* Ideal view distance above ball */
47 static float view_dz; /* Ideal view distance behind ball */
48 static float view_fov; /* Field of view */
50 static float view_c[3]; /* Current view center */
51 static float view_v[3]; /* Current view vector */
52 static float view_p[3]; /* Current view position */
53 static float view_e[3][3]; /* Current view orientation */
56 static int coins = 0; /* Collected coins */
57 static int goal_c = 0; /* Goal coins remaining (0 = open) */
58 static float goal_k = 0; /* Goal animation */
59 static int jump_e = 1; /* Jumping enabled flag */
60 static int jump_b = 0; /* Jump-in-progress flag */
61 static float jump_dt; /* Jump duration */
62 static float jump_p[3]; /* Jump destination */
63 static float fade_k = 0.0; /* Fade in/out level */
64 static float fade_d = 0.0; /* Fade in/out direction */
66 /*---------------------------------------------------------------------------*/
69 * This is an abstraction of the game's input state. All input is
70 * encapsulated here, and all references to the input by the game are made
71 * here. This has the effect of homogenizing input for use in replay
72 * recording and playback.
75 * -32767 = -ANGLE_BOUND
76 * +32767 = +ANGLE_BOUND
79 * -32767 = -VIEWR_BOUND
80 * +32767 = +VIEWR_BOUND
92 static struct input input_current;
94 static void input_init(void)
102 static void input_set_x(float x)
104 if (x < -ANGLE_BOUND) x = -ANGLE_BOUND;
105 if (x > ANGLE_BOUND) x = ANGLE_BOUND;
107 input_current.x = (short) (32767.0f * x / ANGLE_BOUND);
110 static void input_set_z(float z)
112 if (z < -ANGLE_BOUND) z = -ANGLE_BOUND;
113 if (z > ANGLE_BOUND) z = ANGLE_BOUND;
115 input_current.z = (short) (32767.0f * z / ANGLE_BOUND);
118 static void input_set_r(float r)
120 if (r < -VIEWR_BOUND) r = -VIEWR_BOUND;
121 if (r > VIEWR_BOUND) r = VIEWR_BOUND;
123 input_current.r = (short) (32767.0f * r / VIEWR_BOUND);
126 static void input_set_c(int c)
128 input_current.c = (short) c;
131 static float input_get_x(void)
133 return ANGLE_BOUND * (float) input_current.x / 32767.0f;
136 static float input_get_z(void)
138 return ANGLE_BOUND * (float) input_current.z / 32767.0f;
141 static float input_get_r(void)
143 return VIEWR_BOUND * (float) input_current.r / 32767.0f;
146 static int input_get_c(void)
148 return (int) input_current.c;
151 int input_put(FILE *fout)
155 put_short(fout, &input_current.x);
156 put_short(fout, &input_current.z);
157 put_short(fout, &input_current.r);
158 put_short(fout, &input_current.c);
165 int input_get(FILE *fin)
169 get_short(fin, &input_current.x);
170 get_short(fin, &input_current.z);
171 get_short(fin, &input_current.r);
172 get_short(fin, &input_current.c);
174 return (feof(fin) ? 0 : 1);
179 /*---------------------------------------------------------------------------*/
181 static int grow = 0; /* Should the ball be changing size? */
182 static float grow_orig = 0; /* the original ball size */
183 static float grow_goal = 0; /* how big or small to get! */
184 static float grow_t = 0.0; /* timer for the ball to grow... */
185 static float grow_strt = 0; /* starting value for growth */
186 static int got_orig = 0; /* Do we know original ball size? */
188 #define GROW_TIME 0.5f /* sec for the ball to get to size. */
189 #define GROW_BIG 1.5f /* large factor */
190 #define GROW_SMALL 0.5f /* small factor */
192 static int grow_state = 0; /* Current state (values -1, 0, +1) */
194 static void grow_init(const struct s_file *fp, int type)
198 grow_orig = fp->uv->r;
199 grow_goal = grow_orig;
200 grow_strt = grow_orig;
207 if (type == ITEM_SHRINK)
209 audio_play(AUD_SHRINK, 1.f);
217 grow_goal = grow_orig * GROW_SMALL;
223 grow_goal = grow_orig;
229 else if (type == ITEM_GROW)
231 audio_play(AUD_GROW, 1.f);
236 grow_goal = grow_orig;
242 grow_goal = grow_orig * GROW_BIG;
255 grow_strt = fp->uv->r;
259 static void grow_step(const struct s_file *fp, float dt)
266 /* Calculate new size based on how long since you touched the coin... */
270 if (grow_t >= GROW_TIME)
276 dr = grow_strt + ((grow_goal-grow_strt) * (1.0f / (GROW_TIME / grow_t)));
278 /* No sinking through the floor! Keeps ball's bottom constant. */
280 fp->uv->p[1] += (dr - fp->uv->r);
284 /*---------------------------------------------------------------------------*/
286 static void view_init(void)
288 view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
289 view_dp = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
290 view_dc = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
291 view_dz = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
314 int game_init(const struct level *level, int t, int g)
316 clock = (float) t / 100.f;
317 clock_down = (t > 0);
323 if (!sol_load_gl(&file, config_data(level->file),
324 config_get_d(CONFIG_TEXTURES),
325 config_get_d(CONFIG_SHADOW)))
326 return (game_state = 0);
335 /* Initialize jump and goal states. */
341 goal_k = (g == 0) ? 1.0f : 0.0f;
343 /* Initialise the level, background, particles, fade, and view. */
348 part_reset(GOAL_HEIGHT);
350 back_init(level->grad, config_get_d(CONFIG_GEOMETRY));
352 sol_load_gl(&back, config_data(level->back),
353 config_get_d(CONFIG_TEXTURES), 0);
355 /* Initialize ball size tracking... */
374 /*---------------------------------------------------------------------------*/
378 return (int) (clock * 100.f);
391 /*---------------------------------------------------------------------------*/
393 static void game_draw_balls(const struct s_file *fp)
395 float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
398 m_basis(M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
400 glPushAttrib(GL_LIGHTING_BIT);
403 glTranslatef(fp->uv[0].p[0],
404 fp->uv[0].p[1] + BALL_FUDGE,
407 glScalef(fp->uv[0].r,
418 static void game_draw_items(const struct s_file *fp)
420 float r = 360.f * SDL_GetTicks() / 1000.f;
423 glPushAttrib(GL_LIGHTING_BIT);
425 item_push(ITEM_COIN);
427 for (hi = 0; hi < fp->hc; hi++)
429 if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
433 glTranslatef(fp->hv[hi].p[0],
436 glRotatef(r, 0.0f, 1.0f, 0.0f);
437 item_draw(&fp->hv[hi], r);
444 item_push(ITEM_SHRINK);
446 for (hi = 0; hi < fp->hc; hi++)
448 if (fp->hv[hi].t == ITEM_SHRINK)
452 glTranslatef(fp->hv[hi].p[0],
455 glRotatef(r, 0.0f, 1.0f, 0.0f);
456 item_draw(&fp->hv[hi], r);
463 item_push(ITEM_GROW);
465 for (hi = 0; hi < fp->hc; hi++)
467 if (fp->hv[hi].t == ITEM_GROW)
471 glTranslatef(fp->hv[hi].p[0],
474 glRotatef(r, 0.0f, 1.0f, 0.0f);
475 item_draw(&fp->hv[hi], r);
485 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
491 /* Draw the goal particles. */
493 glEnable(GL_TEXTURE_2D);
495 for (zi = 0; zi < fp->zc; zi++)
499 glTranslatef(fp->zv[zi].p[0],
503 part_draw_goal(rx, ry, fp->zv[zi].r, goal_k);
508 glDisable(GL_TEXTURE_2D);
510 /* Draw the goal column. */
512 for (zi = 0; zi < fp->zc; zi++)
516 glTranslatef(fp->zv[zi].p[0],
520 glScalef(fp->zv[zi].r,
531 static void game_draw_jumps(const struct s_file *fp)
535 for (ji = 0; ji < fp->jc; ji++)
539 glTranslatef(fp->jv[ji].p[0],
542 glScalef(fp->jv[ji].r,
552 static void game_draw_swchs(const struct s_file *fp)
556 for (xi = 0; xi < fp->xc; xi++)
563 glTranslatef(fp->xv[xi].p[0],
566 glScalef(fp->xv[xi].r,
570 swch_draw(fp->xv[xi].f, fp->xv[xi].e);
576 /*---------------------------------------------------------------------------*/
578 static void game_refl_all()
580 const float *ball_p = file.uv->p;
584 /* Rotate the environment about the position of the ball. */
586 glTranslatef(+ball_p[0], +ball_p[1], +ball_p[2]);
587 glRotatef(-game_rz, view_e[2][0], view_e[2][1], view_e[2][2]);
588 glRotatef(-game_rx, view_e[0][0], view_e[0][1], view_e[0][2]);
589 glTranslatef(-ball_p[0], -ball_p[1], -ball_p[2]);
591 /* Draw the floor. */
598 /*---------------------------------------------------------------------------*/
600 static void game_draw_light(void)
602 const float light_p[2][4] = {
603 { -8.0f, +32.0f, -8.0f, 0.0f },
604 { +8.0f, +32.0f, +8.0f, 0.0f },
606 const float light_c[2][4] = {
607 { 1.0f, 0.8f, 0.8f, 1.0f },
608 { 0.8f, 1.0f, 0.8f, 1.0f },
611 /* Configure the lighting. */
614 glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
615 glLightfv(GL_LIGHT0, GL_DIFFUSE, light_c[0]);
616 glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
619 glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
620 glLightfv(GL_LIGHT1, GL_DIFFUSE, light_c[1]);
621 glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
624 static void game_draw_back(int pose, int d, const float p[3])
626 float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
627 float t = SDL_GetTicks() / 1000.f + 120.0f;
633 glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
634 glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
637 glTranslatef(p[0], p[1], p[2]);
640 if (config_get_d(CONFIG_BACKGROUND))
642 /* Draw all background layers back to front. */
644 sol_back(&back, BACK_DIST, FAR_DIST, t);
646 sol_back(&back, 0, BACK_DIST, t);
648 /* Draw all foreground geometry in the background file. */
650 /* HACK: This is never used.
659 static void game_draw_fore(int pose, float rx,
660 float ry, int d, const float p[3])
662 const float *ball_p = file.uv->p;
663 const float ball_r = file.uv->r;
667 /* Rotate the environment about the position of the ball. */
669 glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
670 glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
671 glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
672 glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
683 glEnable(GL_CLIP_PLANE0);
684 glClipPlane(GL_CLIP_PLANE0, e);
688 sol_draw(&file, -rx * d, -ry);
691 /* Draw the coins. */
693 game_draw_items(&file);
695 /* Draw the floor. */
697 sol_draw(&file, -rx * d, -ry);
699 /* Draw the ball shadow. */
701 if (d > 0 && config_get_d(CONFIG_SHADOW))
703 shad_draw_set(ball_p, ball_r);
710 game_draw_balls(&file);
713 /* Draw the particles and light columns. */
715 glEnable(GL_COLOR_MATERIAL);
716 glDisable(GL_LIGHTING);
717 glDepthMask(GL_FALSE);
719 glColor3f(1.0f, 1.0f, 1.0f);
721 sol_bill(&file, -rx * d, -ry);
722 part_draw_coin(-rx * d, -ry);
724 glDisable(GL_TEXTURE_2D);
726 game_draw_goals(&file, -rx * d, -ry);
727 game_draw_jumps(&file);
728 game_draw_swchs(&file);
730 glEnable(GL_TEXTURE_2D);
732 glColor3f(1.0f, 1.0f, 1.0f);
734 glDepthMask(GL_TRUE);
735 glEnable(GL_LIGHTING);
736 glDisable(GL_COLOR_MATERIAL);
739 glDisable(GL_CLIP_PLANE0);
744 void game_draw(int pose, float st)
746 float fov = view_fov;
748 if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
752 config_push_persp(fov, 0.1f, FAR_DIST);
763 /* Compute and apply the view. */
765 v_sub(v, view_c, view_p);
767 rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
768 ry = V_DEG(fatan2f(+v[0], -v[2])) + st;
770 glTranslatef(0.f, 0.f, -v_len(v));
771 glRotatef(rx, 1.f, 0.f, 0.f);
772 glRotatef(ry, 0.f, 1.f, 0.f);
773 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
775 if (config_get_d(CONFIG_REFLECTION))
777 glEnable(GL_STENCIL_TEST);
779 /* Draw the mirrors only into the stencil buffer. */
781 glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
782 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
783 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
784 glDepthMask(GL_FALSE);
788 glDepthMask(GL_TRUE);
789 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
790 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
791 glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
793 /* Draw the scene reflected into color and depth buffers. */
798 glScalef(+1.f, -1.f, +1.f);
801 game_draw_back(pose, -1, pdn);
802 game_draw_fore(pose, rx, ry, -1, pdn);
807 glDisable(GL_STENCIL_TEST);
810 /* Draw the scene normally. */
814 game_draw_back(pose, +1, pup);
815 game_draw_fore(pose, rx, ry, +1, pup);
820 /* Draw the fade overlay. */
826 /*---------------------------------------------------------------------------*/
828 static void game_update_grav(float h[3], const float g[3])
831 float y[3] = { 0.f, 1.f, 0.f };
837 /* Compute the gravity vector from the given world rotations. */
839 z[0] = fsinf(V_RAD(view_a));
841 z[2] = fcosf(V_RAD(view_a));
848 m_rot (Z, z, V_RAD(game_rz));
849 m_rot (X, x, V_RAD(game_rx));
854 static void game_update_view(float dt)
856 float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
857 float da = input_get_r() * dt * 90.0f;
860 float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f };
864 /* Center the view about the ball. */
866 v_cpy(view_c, file.uv->p);
867 v_inv(view_v, file.uv->v);
869 view_e[2][0] = fsinf(V_RAD(view_a));
871 view_e[2][2] = fcosf(V_RAD(view_a));
873 switch (input_get_c())
875 case 1: /* Camera 1: Viewpoint chases the ball position. */
877 /* TODO: This camera no longer exists. */
881 case 2: /* Camera 2: View vector is given by view angle. */
885 default: /* Default: View vector approaches the ball velocity vector. */
887 v_mad(view_e[2], view_e[2], view_v, v_dot(view_v, view_v) * dt / 4);
892 /* Orthonormalize the basis of the view in its new position. */
894 v_crs(view_e[0], view_e[1], view_e[2]);
895 v_crs(view_e[2], view_e[0], view_e[1]);
896 v_nrm(view_e[0], view_e[0]);
897 v_nrm(view_e[2], view_e[2]);
899 /* Compute the new view position. */
901 k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
903 view_k = view_k + (k - view_k) * dt;
905 if (view_k < 0.5) view_k = 0.5;
907 v_scl(v, view_e[1], view_dp * view_k);
908 v_mad(v, v, view_e[2], view_dz * view_k);
909 m_rot(M, Y, V_RAD(da));
910 m_vxfm(view_p, M, v);
911 v_add(view_p, view_p, file.uv->p);
913 /* Compute the new view center. */
915 v_cpy(view_c, file.uv->p);
916 v_mad(view_c, view_c, view_e[1], dc);
918 /* Note the current view angle. */
920 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
923 static void game_update_time(float dt, int b)
925 if (goal_c == 0 && goal_k < 1.0f)
928 /* The ticking clock. */
943 static int game_update_state(int bt)
945 struct s_file *fp = &file;
952 /* Test for an item. */
954 if (bt && (hp = sol_item_test(fp, p, COIN_RADIUS)))
956 const char *sound = AUD_COIN;
961 grow_init(fp, hp->t);
963 if (hp->t == ITEM_COIN)
967 /* Check for goal open. */
979 audio_play(sound, 1.f);
981 /* Reset item type. */
986 /* Test for a switch. */
988 if (sol_swch_test(fp, 0))
989 audio_play(AUD_SWITCH, 1.f);
991 /* Test for a jump. */
993 if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
999 audio_play(AUD_JUMP, 1.f);
1001 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
1004 /* Test for a goal. */
1006 if (bt && goal_c == 0 && (zp = sol_goal_test(fp, p, 0)))
1008 audio_play(AUD_GOAL, 1.0f);
1012 /* Test for time-out. */
1014 if (bt && clock_down && clock <= 0.f)
1016 audio_play(AUD_TIME, 1.0f);
1020 /* Test for fall-out. */
1022 if (bt && fp->uv[0].p[1] < fp->vv[0].p[1])
1024 audio_play(AUD_FALL, 1.0f);
1031 int game_step(const float g[3], float dt, int bt)
1035 struct s_file *fp = &file;
1039 /* Smooth jittery or discontinuous input. */
1041 game_rx += (input_get_x() - game_rx) * dt / RESPONSE;
1042 game_rz += (input_get_z() - game_rz) * dt / RESPONSE;
1046 game_update_grav(h, g);
1053 /* Handle a jump. */
1057 fp->uv[0].p[0] = jump_p[0];
1058 fp->uv[0].p[1] = jump_p[1];
1059 fp->uv[0].p[2] = jump_p[2];
1068 float b = sol_step(fp, h, dt, 0, NULL);
1070 /* Mix the sound of a ball bounce. */
1074 float k = (b - 0.5f) * 2.0f;
1078 if (fp->uv->r > grow_orig) audio_play(AUD_BUMPL, k);
1079 else if (fp->uv->r < grow_orig) audio_play(AUD_BUMPS, k);
1080 else audio_play(AUD_BUMPM, k);
1082 else audio_play(AUD_BUMPM, k);
1087 game_update_view(dt);
1088 game_update_time(dt, bt);
1090 return game_update_state(bt);
1095 /*---------------------------------------------------------------------------*/
1097 void game_set_x(int k)
1099 input_set_x(-ANGLE_BOUND * k / JOY_MAX);
1102 void game_set_z(int k)
1104 input_set_z(+ANGLE_BOUND * k / JOY_MAX);
1107 void game_set_ang(int x, int z)
1113 void game_set_pos(int x, int y)
1115 input_set_x(input_get_x() + 40.0f * y / config_get_d(CONFIG_MOUSE_SENSE));
1116 input_set_z(input_get_z() + 40.0f * x / config_get_d(CONFIG_MOUSE_SENSE));
1119 void game_set_cam(int c)
1124 void game_set_rot(float r)
1129 /*---------------------------------------------------------------------------*/
1131 void game_set_fly(float k)
1133 struct s_file *fp = &file;
1135 float x[3] = { 1.f, 0.f, 0.f };
1136 float y[3] = { 0.f, 1.f, 0.f };
1137 float z[3] = { 0.f, 0.f, 1.f };
1138 float c0[3] = { 0.f, 0.f, 0.f };
1139 float p0[3] = { 0.f, 0.f, 0.f };
1140 float c1[3] = { 0.f, 0.f, 0.f };
1141 float p1[3] = { 0.f, 0.f, 0.f };
1144 z[0] = fsinf(V_RAD(view_a));
1145 z[2] = fcosf(V_RAD(view_a));
1147 v_cpy(view_e[0], x);
1148 v_cpy(view_e[1], y);
1149 v_cpy(view_e[2], z);
1151 /* k = 0.0 view is at the ball. */
1155 v_cpy(c0, fp->uv[0].p);
1156 v_cpy(p0, fp->uv[0].p);
1159 v_mad(p0, p0, y, view_dp);
1160 v_mad(p0, p0, z, view_dz);
1161 v_mad(c0, c0, y, view_dc);
1163 /* k = +1.0 view is s_view 0 */
1165 if (k >= 0 && fp->wc > 0)
1167 v_cpy(p1, fp->wv[0].p);
1168 v_cpy(c1, fp->wv[0].q);
1171 /* k = -1.0 view is s_view 1 */
1173 if (k <= 0 && fp->wc > 1)
1175 v_cpy(p1, fp->wv[1].p);
1176 v_cpy(c1, fp->wv[1].q);
1179 /* Interpolate the views. */
1182 v_mad(view_p, p0, v, k * k);
1185 v_mad(view_c, c0, v, k * k);
1187 /* Orthonormalize the view basis. */
1189 v_sub(view_e[2], view_p, view_c);
1190 v_crs(view_e[0], view_e[1], view_e[2]);
1191 v_crs(view_e[2], view_e[0], view_e[1]);
1192 v_nrm(view_e[0], view_e[0]);
1193 v_nrm(view_e[2], view_e[2]);
1196 void game_look(float phi, float theta)
1198 view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
1199 view_c[1] = view_p[1] + fsinf(V_RAD(phi));
1200 view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
1203 /*---------------------------------------------------------------------------*/
1205 void game_kill_fade(void)
1211 void game_step_fade(float dt)
1213 if ((fade_k < 1.0f && fade_d > 0.0f) ||
1214 (fade_k > 0.0f && fade_d < 0.0f))
1215 fade_k += fade_d * dt;
1229 void game_fade(float d)
1234 /*---------------------------------------------------------------------------*/