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.
29 #include "solid_phys.h"
33 /*---------------------------------------------------------------------------*/
35 static int game_state = 0;
37 static struct s_file file;
38 static struct s_file back;
40 static int reflective; /* Reflective geometry used? */
42 static float timer = 0.f; /* Clock time */
43 static int timer_down = 1; /* Timer go up or down? */
45 static float game_rx; /* Floor rotation about X axis */
46 static float game_rz; /* Floor rotation about Z axis */
48 static float view_a; /* Ideal view rotation 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 reference frame */
60 static int coins = 0; /* Collected coins */
61 static int goal_e = 0; /* Goal enabled flag */
62 static float goal_k = 0; /* Goal animation */
63 static int jump_e = 1; /* Jumping enabled flag */
64 static int jump_b = 0; /* Jump-in-progress flag */
65 static float jump_dt; /* Jump duration */
66 static float jump_p[3]; /* Jump destination */
67 static float fade_k = 0.0; /* Fade in/out level */
68 static float fade_d = 0.0; /* Fade in/out direction */
70 /*---------------------------------------------------------------------------*/
73 * This is an abstraction of the game's input state. All input is
74 * encapsulated here, and all references to the input by the game are made
75 * here. This has the effect of homogenizing input for use in replay
76 * recording and playback.
79 * -32767 = -ANGLE_BOUND
80 * +32767 = +ANGLE_BOUND
83 * -32767 = -VIEWR_BOUND
84 * +32767 = +VIEWR_BOUND
96 static struct input input_current;
98 static void input_init(void)
106 static void input_set_x(float x)
108 if (x < -ANGLE_BOUND) x = -ANGLE_BOUND;
109 if (x > ANGLE_BOUND) x = ANGLE_BOUND;
111 input_current.x = (short) (32767.0f * x / ANGLE_BOUND);
114 static void input_set_z(float z)
116 if (z < -ANGLE_BOUND) z = -ANGLE_BOUND;
117 if (z > ANGLE_BOUND) z = ANGLE_BOUND;
119 input_current.z = (short) (32767.0f * z / ANGLE_BOUND);
122 static void input_set_r(float r)
124 if (r < -VIEWR_BOUND) r = -VIEWR_BOUND;
125 if (r > VIEWR_BOUND) r = VIEWR_BOUND;
127 input_current.r = (short) (32767.0f * r / VIEWR_BOUND);
130 static void input_set_c(int c)
132 input_current.c = (short) c;
135 static float input_get_x(void)
137 return ANGLE_BOUND * (float) input_current.x / 32767.0f;
140 static float input_get_z(void)
142 return ANGLE_BOUND * (float) input_current.z / 32767.0f;
145 static float input_get_r(void)
147 return VIEWR_BOUND * (float) input_current.r / 32767.0f;
150 static int input_get_c(void)
152 return (int) input_current.c;
155 int input_put(FILE *fout)
159 put_short(fout, &input_current.x);
160 put_short(fout, &input_current.z);
161 put_short(fout, &input_current.r);
162 put_short(fout, &input_current.c);
169 int input_get(FILE *fin)
173 get_short(fin, &input_current.x);
174 get_short(fin, &input_current.z);
175 get_short(fin, &input_current.r);
176 get_short(fin, &input_current.c);
178 return (feof(fin) ? 0 : 1);
183 /*---------------------------------------------------------------------------*/
185 static int grow = 0; /* Should the ball be changing size? */
186 static float grow_orig = 0; /* the original ball size */
187 static float grow_goal = 0; /* how big or small to get! */
188 static float grow_t = 0.0; /* timer for the ball to grow... */
189 static float grow_strt = 0; /* starting value for growth */
190 static int got_orig = 0; /* Do we know original ball size? */
192 #define GROW_TIME 0.5f /* sec for the ball to get to size. */
193 #define GROW_BIG 1.5f /* large factor */
194 #define GROW_SMALL 0.5f /* small factor */
196 static int grow_state = 0; /* Current state (values -1, 0, +1) */
198 static void grow_init(const struct s_file *fp, int type)
202 grow_orig = fp->uv->r;
203 grow_goal = grow_orig;
204 grow_strt = grow_orig;
211 if (type == ITEM_SHRINK)
219 audio_play(AUD_SHRINK, 1.f);
220 grow_goal = grow_orig * GROW_SMALL;
226 audio_play(AUD_SHRINK, 1.f);
227 grow_goal = grow_orig;
233 else if (type == ITEM_GROW)
238 audio_play(AUD_GROW, 1.f);
239 grow_goal = grow_orig;
245 audio_play(AUD_GROW, 1.f);
246 grow_goal = grow_orig * GROW_BIG;
259 grow_strt = fp->uv->r;
263 static void grow_step(const struct s_file *fp, float dt)
270 /* Calculate new size based on how long since you touched the coin... */
274 if (grow_t >= GROW_TIME)
280 dr = grow_strt + ((grow_goal-grow_strt) * (1.0f / (GROW_TIME / grow_t)));
282 /* No sinking through the floor! Keeps ball's bottom constant. */
284 fp->uv->p[1] += (dr - fp->uv->r);
288 /*---------------------------------------------------------------------------*/
290 static void view_init(void)
292 view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
293 view_dp = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
294 view_dc = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
295 view_dz = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
318 int game_init(const char *file_name, int t, int e)
320 char *back_name = NULL, *grad_name = NULL;
324 timer = (float) t / 100.f;
325 timer_down = (t > 0);
331 if (!sol_load_gl(&file, config_data(file_name),
332 config_get_d(CONFIG_TEXTURES),
333 config_get_d(CONFIG_SHADOW)))
334 return (game_state = 0);
336 reflective = sol_reflective(&file);
345 /* Initialize jump and goal states. */
351 goal_k = e ? 1.0f : 0.0f;
353 /* Initialise the level, background, particles, fade, and view. */
358 for (i = 0; i < file.dc; i++)
360 char *k = file.av + file.dv[i].ai;
361 char *v = file.av + file.dv[i].aj;
363 if (strcmp(k, "back") == 0) back_name = v;
364 if (strcmp(k, "grad") == 0) grad_name = v;
367 part_reset(GOAL_HEIGHT);
369 back_init(grad_name, config_get_d(CONFIG_GEOMETRY));
371 sol_load_gl(&back, config_data(back_name),
372 config_get_d(CONFIG_TEXTURES), 0);
374 /* Initialize ball size tracking... */
393 /*---------------------------------------------------------------------------*/
397 return (int) (timer * 100.f);
405 /*---------------------------------------------------------------------------*/
407 static void game_draw_balls(const struct s_file *fp,
408 const float *bill_M, float t)
410 float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
415 m_basis(ball_M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
416 m_basis(pend_M, fp->uv[0].E[0], fp->uv[0].E[1], fp->uv[0].E[2]);
418 glPushAttrib(GL_LIGHTING_BIT);
421 glTranslatef(fp->uv[0].p[0],
422 fp->uv[0].p[1] + BALL_FUDGE,
424 glScalef(fp->uv[0].r,
429 ball_draw(ball_M, pend_M, bill_M, t);
435 static void game_draw_items(const struct s_file *fp, float t)
440 glPushAttrib(GL_LIGHTING_BIT);
442 item_push(ITEM_COIN);
444 for (hi = 0; hi < fp->hc; hi++)
446 if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
450 glTranslatef(fp->hv[hi].p[0],
453 glRotatef(r, 0.0f, 1.0f, 0.0f);
454 item_draw(&fp->hv[hi], r);
461 item_push(ITEM_SHRINK);
463 for (hi = 0; hi < fp->hc; hi++)
465 if (fp->hv[hi].t == ITEM_SHRINK)
469 glTranslatef(fp->hv[hi].p[0],
472 glRotatef(r, 0.0f, 1.0f, 0.0f);
473 item_draw(&fp->hv[hi], r);
480 item_push(ITEM_GROW);
482 for (hi = 0; hi < fp->hc; hi++)
484 if (fp->hv[hi].t == ITEM_GROW)
488 glTranslatef(fp->hv[hi].p[0],
491 glRotatef(r, 0.0f, 1.0f, 0.0f);
492 item_draw(&fp->hv[hi], r);
502 static void game_draw_goals(const struct s_file *fp, const float *M, float t)
508 /* Draw the goal particles. */
510 glEnable(GL_TEXTURE_2D);
512 for (zi = 0; zi < fp->zc; zi++)
516 glTranslatef(fp->zv[zi].p[0],
520 part_draw_goal(M, fp->zv[zi].r, goal_k, t);
525 glDisable(GL_TEXTURE_2D);
527 /* Draw the goal column. */
529 for (zi = 0; zi < fp->zc; zi++)
533 glTranslatef(fp->zv[zi].p[0],
537 glScalef(fp->zv[zi].r,
548 static void game_draw_jumps(const struct s_file *fp)
552 for (ji = 0; ji < fp->jc; ji++)
556 glTranslatef(fp->jv[ji].p[0],
559 glScalef(fp->jv[ji].r,
569 static void game_draw_swchs(const struct s_file *fp)
573 for (xi = 0; xi < fp->xc; xi++)
580 glTranslatef(fp->xv[xi].p[0],
583 glScalef(fp->xv[xi].r,
587 swch_draw(fp->xv[xi].f, fp->xv[xi].e);
593 /*---------------------------------------------------------------------------*/
595 static void game_draw_tilt(int d)
597 const float *ball_p = file.uv->p;
599 /* Rotate the environment about the position of the ball. */
601 glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
602 glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
603 glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
604 glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
607 static void game_refl_all(void)
613 /* Draw the floor. */
620 /*---------------------------------------------------------------------------*/
622 static void game_draw_light(void)
624 const float light_p[2][4] = {
625 { -8.0f, +32.0f, -8.0f, 0.0f },
626 { +8.0f, +32.0f, +8.0f, 0.0f },
628 const float light_c[2][4] = {
629 { 1.0f, 0.8f, 0.8f, 1.0f },
630 { 0.8f, 1.0f, 0.8f, 1.0f },
633 /* Configure the lighting. */
636 glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
637 glLightfv(GL_LIGHT0, GL_DIFFUSE, light_c[0]);
638 glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
641 glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
642 glLightfv(GL_LIGHT1, GL_DIFFUSE, light_c[1]);
643 glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
646 static void game_draw_back(int pose, int d, float t)
652 glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
653 glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
656 glTranslatef(view_p[0], view_p[1] * d, view_p[2]);
658 if (config_get_d(CONFIG_BACKGROUND))
660 /* Draw all background layers back to front. */
662 sol_back(&back, BACK_DIST, FAR_DIST, t);
664 sol_back(&back, 0, BACK_DIST, t);
671 static void game_clip_refl(int d)
673 /* Fudge to eliminate the floor from reflection. */
675 GLdouble e[4], k = -0.00001;
682 glClipPlane(GL_CLIP_PLANE0, e);
685 static void game_clip_ball(int d, const float *p)
687 GLdouble r, c[3], pz[4], nz[4];
689 /* Compute the plane giving the front of the ball, as seen from view_p. */
695 pz[0] = view_p[0] - c[0];
696 pz[1] = view_p[1] - c[1];
697 pz[2] = view_p[2] - c[2];
699 r = sqrt(pz[0] * pz[0] + pz[1] * pz[1] + pz[2] * pz[2]);
704 pz[3] = -(pz[0] * c[0] +
708 /* Find the plane giving the back of the ball, as seen from view_p. */
715 /* Reflect these planes as necessary, and store them in the GL state. */
720 glClipPlane(GL_CLIP_PLANE1, nz);
721 glClipPlane(GL_CLIP_PLANE2, pz);
724 static void game_draw_fore(int pose, const float *M, int d, float t)
726 const float *ball_p = file.uv->p;
727 const float ball_r = file.uv->r;
731 /* Rotate the environment about the position of the ball. */
735 /* Compute clipping planes for reflection and ball facing. */
738 game_clip_ball(d, ball_p);
741 glEnable(GL_CLIP_PLANE0);
744 sol_draw(&file, 0, 1);
747 /* Draw the coins. */
749 game_draw_items(&file, t);
751 /* Draw the floor. */
753 sol_draw(&file, 0, 1);
755 /* Draw the ball shadow. */
757 if (d > 0 && config_get_d(CONFIG_SHADOW))
759 shad_draw_set(ball_p, ball_r);
766 game_draw_balls(&file, M, t);
769 /* Draw the particles and light columns. */
771 glEnable(GL_COLOR_MATERIAL);
772 glDisable(GL_LIGHTING);
773 glDepthMask(GL_FALSE);
775 glColor3f(1.0f, 1.0f, 1.0f);
777 sol_bill(&file, M, t);
778 part_draw_coin(M, t);
780 glDisable(GL_TEXTURE_2D);
782 game_draw_goals(&file, M, t);
783 game_draw_jumps(&file);
784 game_draw_swchs(&file);
786 glEnable(GL_TEXTURE_2D);
788 glColor3f(1.0f, 1.0f, 1.0f);
790 glDepthMask(GL_TRUE);
791 glEnable(GL_LIGHTING);
792 glDisable(GL_COLOR_MATERIAL);
795 glDisable(GL_CLIP_PLANE0);
800 void game_draw(int pose, float t)
802 float fov = view_fov;
804 if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
808 config_push_persp(fov, 0.1f, FAR_DIST);
811 float T[16], U[16], M[16], v[3];
813 /* Compute direct and reflected view bases. */
819 m_view(T, view_c, view_p, view_e[1]);
820 m_view(U, view_c, v, view_e[1]);
824 /* Apply current the view. */
826 v_sub(v, view_c, view_p);
828 glTranslatef(0.f, 0.f, -v_len(v));
830 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
832 if (reflective && config_get_d(CONFIG_REFLECTION))
834 glEnable(GL_STENCIL_TEST);
836 /* Draw the mirrors only into the stencil buffer. */
838 glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
839 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
840 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
841 glDepthMask(GL_FALSE);
845 glDepthMask(GL_TRUE);
846 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
847 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
848 glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
850 /* Draw the scene reflected into color and depth buffers. */
855 glScalef(+1.0f, -1.0f, +1.0f);
858 game_draw_back(pose, -1, t);
859 game_draw_fore(pose, U, -1, t);
864 glDisable(GL_STENCIL_TEST);
867 /* Draw the scene normally. */
871 game_draw_back(pose, +1, t);
872 game_draw_fore(pose, T, +1, t);
877 /* Draw the fade overlay. */
883 /*---------------------------------------------------------------------------*/
885 static void game_update_grav(float h[3], const float g[3])
888 float y[3] = { 0.0f, 1.0f, 0.0f };
894 /* Compute the gravity vector from the given world rotations. */
896 z[0] = fsinf(V_RAD(view_a));
898 z[2] = fcosf(V_RAD(view_a));
905 m_rot (Z, z, V_RAD(game_rz));
906 m_rot (X, x, V_RAD(game_rx));
911 static void game_update_view(float dt)
913 float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
914 float da = input_get_r() * dt * 90.0f;
917 float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f };
921 /* Center the view about the ball. */
923 v_cpy(view_c, file.uv->p);
924 v_inv(view_v, file.uv->v);
926 view_e[2][0] = fsinf(V_RAD(view_a));
928 view_e[2][2] = fcosf(V_RAD(view_a));
930 switch (input_get_c())
932 case 1: /* Camera 1: Viewpoint chases the ball position. */
934 v_sub(view_e[2], view_p, view_c);
938 case 2: /* Camera 2: View vector is given by view angle. */
942 default: /* Default: View vector approaches the ball velocity vector. */
944 v_mad(view_e[2], view_e[2], view_v, v_dot(view_v, view_v) * dt / 4);
949 /* Orthonormalize the new view reference frame. */
951 v_crs(view_e[0], view_e[1], view_e[2]);
952 v_crs(view_e[2], view_e[0], view_e[1]);
953 v_nrm(view_e[0], view_e[0]);
954 v_nrm(view_e[2], view_e[2]);
956 /* Compute the new view position. */
958 k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
960 view_k = view_k + (k - view_k) * dt;
962 if (view_k < 0.5) view_k = 0.5;
964 v_scl(v, view_e[1], view_dp * view_k);
965 v_mad(v, v, view_e[2], view_dz * view_k);
966 m_rot(M, Y, V_RAD(da));
967 m_vxfm(view_p, M, v);
968 v_add(view_p, view_p, file.uv->p);
970 /* Compute the new view center. */
972 v_cpy(view_c, file.uv->p);
973 v_mad(view_c, view_c, view_e[1], dc);
975 /* Note the current view angle. */
977 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
980 static void game_update_time(float dt, int b)
982 if (goal_e && goal_k < 1.0f)
985 /* The ticking clock. */
1000 static int game_update_state(int bt)
1002 struct s_file *fp = &file;
1009 /* Test for an item. */
1011 if (bt && (hp = sol_item_test(fp, p, ITEM_RADIUS)))
1016 grow_init(fp, hp->t);
1018 if (hp->t == ITEM_COIN)
1021 audio_play(AUD_COIN, 1.f);
1028 /* Test for a switch. */
1030 if (sol_swch_test(fp, 0))
1031 audio_play(AUD_SWITCH, 1.f);
1033 /* Test for a jump. */
1035 if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
1041 audio_play(AUD_JUMP, 1.f);
1043 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
1046 /* Test for a goal. */
1048 if (bt && goal_e && (zp = sol_goal_test(fp, p, 0)))
1050 audio_play(AUD_GOAL, 1.0f);
1054 /* Test for time-out. */
1056 if (bt && timer_down && timer <= 0.f)
1058 audio_play(AUD_TIME, 1.0f);
1062 /* Test for fall-out. */
1064 if (bt && fp->uv[0].p[1] < fp->vv[0].p[1])
1066 audio_play(AUD_FALL, 1.0f);
1073 int game_step(const float g[3], float dt, int bt)
1077 struct s_file *fp = &file;
1081 /* Smooth jittery or discontinuous input. */
1083 game_rx += (input_get_x() - game_rx) * dt / RESPONSE;
1084 game_rz += (input_get_z() - game_rz) * dt / RESPONSE;
1088 game_update_grav(h, g);
1095 /* Handle a jump. */
1099 fp->uv[0].p[0] = jump_p[0];
1100 fp->uv[0].p[1] = jump_p[1];
1101 fp->uv[0].p[2] = jump_p[2];
1110 float b = sol_step(fp, h, dt, 0, NULL);
1112 /* Mix the sound of a ball bounce. */
1116 float k = (b - 0.5f) * 2.0f;
1120 if (fp->uv->r > grow_orig) audio_play(AUD_BUMPL, k);
1121 else if (fp->uv->r < grow_orig) audio_play(AUD_BUMPS, k);
1122 else audio_play(AUD_BUMPM, k);
1124 else audio_play(AUD_BUMPM, k);
1129 game_update_view(dt);
1130 game_update_time(dt, bt);
1132 return game_update_state(bt);
1137 /*---------------------------------------------------------------------------*/
1139 void game_set_goal(void)
1141 audio_play(AUD_SWITCH, 1.0f);
1145 void game_clr_goal(void)
1150 /*---------------------------------------------------------------------------*/
1152 void game_set_x(int k)
1154 input_set_x(-ANGLE_BOUND * k / JOY_MAX);
1157 void game_set_z(int k)
1159 input_set_z(+ANGLE_BOUND * k / JOY_MAX);
1162 void game_set_ang(int x, int z)
1168 void game_set_pos(int x, int y)
1170 input_set_x(input_get_x() + 40.0f * y / config_get_d(CONFIG_MOUSE_SENSE));
1171 input_set_z(input_get_z() + 40.0f * x / config_get_d(CONFIG_MOUSE_SENSE));
1174 void game_set_cam(int c)
1179 void game_set_rot(float r)
1184 /*---------------------------------------------------------------------------*/
1186 void game_set_fly(float k)
1188 struct s_file *fp = &file;
1190 float x[3] = { 1.f, 0.f, 0.f };
1191 float y[3] = { 0.f, 1.f, 0.f };
1192 float z[3] = { 0.f, 0.f, 1.f };
1193 float c0[3] = { 0.f, 0.f, 0.f };
1194 float p0[3] = { 0.f, 0.f, 0.f };
1195 float c1[3] = { 0.f, 0.f, 0.f };
1196 float p1[3] = { 0.f, 0.f, 0.f };
1199 z[0] = fsinf(V_RAD(view_a));
1200 z[2] = fcosf(V_RAD(view_a));
1202 v_cpy(view_e[0], x);
1203 v_cpy(view_e[1], y);
1204 v_cpy(view_e[2], z);
1206 /* k = 0.0 view is at the ball. */
1210 v_cpy(c0, fp->uv[0].p);
1211 v_cpy(p0, fp->uv[0].p);
1214 v_mad(p0, p0, y, view_dp);
1215 v_mad(p0, p0, z, view_dz);
1216 v_mad(c0, c0, y, view_dc);
1218 /* k = +1.0 view is s_view 0 */
1220 if (k >= 0 && fp->wc > 0)
1222 v_cpy(p1, fp->wv[0].p);
1223 v_cpy(c1, fp->wv[0].q);
1226 /* k = -1.0 view is s_view 1 */
1228 if (k <= 0 && fp->wc > 1)
1230 v_cpy(p1, fp->wv[1].p);
1231 v_cpy(c1, fp->wv[1].q);
1234 /* Interpolate the views. */
1237 v_mad(view_p, p0, v, k * k);
1240 v_mad(view_c, c0, v, k * k);
1242 /* Orthonormalize the view basis. */
1244 v_sub(view_e[2], view_p, view_c);
1245 v_crs(view_e[0], view_e[1], view_e[2]);
1246 v_crs(view_e[2], view_e[0], view_e[1]);
1247 v_nrm(view_e[0], view_e[0]);
1248 v_nrm(view_e[2], view_e[2]);
1251 void game_look(float phi, float theta)
1253 view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
1254 view_c[1] = view_p[1] + fsinf(V_RAD(phi));
1255 view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
1258 /*---------------------------------------------------------------------------*/
1260 void game_kill_fade(void)
1266 void game_step_fade(float dt)
1268 if ((fade_k < 1.0f && fade_d > 0.0f) ||
1269 (fade_k > 0.0f && fade_d < 0.0f))
1270 fade_k += fade_d * dt;
1284 void game_fade(float d)
1289 /*---------------------------------------------------------------------------*/
1291 const char *status_to_str(int s)
1295 case GAME_NONE: return _("Aborted");
1296 case GAME_TIME: return _("Time-out");
1297 case GAME_GOAL: return _("Success");
1298 case GAME_FALL: return _("Fall-out");
1299 default: return _("Unknown");
1303 /*---------------------------------------------------------------------------*/