Import from neverball-1.4.0.tar.gz
[neverball-archive.git] / ball / game.c
blobaa829dd5b87ac4604a2d13ecd20567d9c83c4a64
1 /*
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.
15 #include <SDL.h>
16 #include <math.h>
18 #include "glext.h"
19 #include "game.h"
20 #include "vec3.h"
21 #include "geom.h"
22 #include "back.h"
23 #include "part.h"
24 #include "hud.h"
25 #include "image.h"
26 #include "audio.h"
27 #include "solid.h"
28 #include "level.h"
29 #include "config.h"
30 #include "binary.h"
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 */
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 */
57 static float view_k;
59 static int goal_e = 0; /* Goal enabled flag */
60 static float goal_k = 0; /* Goal animation */
61 static int swch_e = 1; /* Switching enabled flag */
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 */
69 /*---------------------------------------------------------------------------*/
71 static void view_init(void)
73 view_a = 0.f;
74 view_ry = 0.f;
76 view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
77 view_dp = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
78 view_dc = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
79 view_dz = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
80 view_k = 1.0f;
82 view_c[0] = 0.f;
83 view_c[1] = view_dc;
84 view_c[2] = 0.f;
86 view_p[0] = 0.f;
87 view_p[1] = view_dp;
88 view_p[2] = view_dz;
90 view_e[0][0] = 1.f;
91 view_e[0][1] = 0.f;
92 view_e[0][2] = 0.f;
93 view_e[1][0] = 0.f;
94 view_e[1][1] = 1.f;
95 view_e[1][2] = 0.f;
96 view_e[2][0] = 0.f;
97 view_e[2][1] = 0.f;
98 view_e[2][2] = 1.f;
101 int game_init(const char *file_name,
102 const char *back_name,
103 const char *grad_name, int t, int e)
105 clock = (float) t / 100.f;
107 if (game_state)
108 game_free();
110 game_ix = 0.f;
111 game_iz = 0.f;
112 game_rx = 0.f;
113 game_rz = 0.f;
115 /* Initialize jump and goal states. */
117 jump_e = 1;
118 jump_b = 0;
120 goal_e = e ? 1 : 0;
121 goal_k = e ? 1.0f : 0.0f;
123 /* Reset the hud. */
125 hud_ball_pulse(0.f);
126 hud_time_pulse(0.f);
127 hud_coin_pulse(0.f);
129 /* Initialise the level, background, particles, fade, and view. */
131 fade_k = 1.0f;
132 fade_d = -2.0f;
134 part_reset(GOAL_HEIGHT);
135 view_init();
136 back_init(grad_name, config_get_d(CONFIG_GEOMETRY));
138 if (sol_load(&back, config_data(back_name),
139 config_get_d(CONFIG_TEXTURES), 0) &&
140 sol_load(&file, config_data(file_name),
141 config_get_d(CONFIG_TEXTURES), config_get_d(CONFIG_SHADOW)))
142 return (game_state = 1);
143 else
144 return (game_state = 0);
147 void game_free(void)
149 if (game_state)
151 sol_free(&file);
152 sol_free(&back);
153 back_free();
155 game_state = 0;
158 /*---------------------------------------------------------------------------*/
160 int curr_clock(void)
162 return (int) (clock * 100.f);
165 char *curr_intro(void)
167 return (file.ac > 0) ? file.av : NULL;
170 /*---------------------------------------------------------------------------*/
172 static void game_draw_balls(const struct s_file *fp)
174 float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
175 float M[16];
177 m_basis(M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
179 glPushMatrix();
181 glTranslatef(fp->uv[0].p[0],
182 fp->uv[0].p[1] + BALL_FUDGE,
183 fp->uv[0].p[2]);
184 glMultMatrixf(M);
185 glScalef(fp->uv[0].r,
186 fp->uv[0].r,
187 fp->uv[0].r);
189 glColor4fv(c);
191 ball_draw();
193 glPopMatrix();
196 static void game_draw_coins(const struct s_file *fp)
198 float r = 360.f * SDL_GetTicks() / 1000.f;
199 int ci;
201 coin_push();
203 for (ci = 0; ci < fp->cc; ci++)
204 if (fp->cv[ci].n > 0)
206 glPushMatrix();
208 glTranslatef(fp->cv[ci].p[0],
209 fp->cv[ci].p[1],
210 fp->cv[ci].p[2]);
211 glRotatef(r, 0.0f, 1.0f, 0.0f);
212 coin_draw(fp->cv[ci].n, r);
214 glPopMatrix();
217 coin_pull();
220 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
222 int zi;
224 if (goal_e)
225 for (zi = 0; zi < fp->zc; zi++)
227 glPushMatrix();
229 glTranslatef(fp->zv[zi].p[0],
230 fp->zv[zi].p[1],
231 fp->zv[zi].p[2]);
233 part_draw_goal(rx, ry, fp->zv[zi].r, goal_k);
235 glScalef(fp->zv[zi].r, goal_k, fp->zv[zi].r);
236 goal_draw();
238 glPopMatrix();
242 static void game_draw_jumps(const struct s_file *fp)
244 int ji;
246 for (ji = 0; ji < fp->jc; ji++)
248 glPushMatrix();
250 glTranslatef(fp->jv[ji].p[0],
251 fp->jv[ji].p[1],
252 fp->jv[ji].p[2]);
254 glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
255 jump_draw();
257 glPopMatrix();
261 static void game_draw_swchs(const struct s_file *fp)
263 int xi;
265 for (xi = 0; xi < fp->xc; xi++)
267 glPushMatrix();
269 glTranslatef(fp->xv[xi].p[0],
270 fp->xv[xi].p[1],
271 fp->xv[xi].p[2]);
273 glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
274 swch_draw(fp->xv[xi].f);
276 glPopMatrix();
280 /*---------------------------------------------------------------------------*/
282 static void game_refl_all(int s)
284 const float *ball_p = file.uv->p;
286 glPushMatrix();
288 /* Rotate the environment about the position of the ball. */
290 glTranslatef(+ball_p[0], +ball_p[1], +ball_p[2]);
291 glRotatef(-game_rz, view_e[2][0], view_e[2][1], view_e[2][2]);
292 glRotatef(-game_rx, view_e[0][0], view_e[0][1], view_e[0][2]);
293 glTranslatef(-ball_p[0], -ball_p[1], -ball_p[2]);
295 /* Draw the floor. */
297 sol_refl(&file);
299 glPopMatrix();
302 /*---------------------------------------------------------------------------*/
304 static void game_draw_light(void)
306 const float light_p[2][4] = {
307 { -8.0f, +32.0f, -8.0f, 1.0f },
308 { +8.0f, +32.0f, +8.0f, 1.0f },
310 const float light_c[2][4] = {
311 { 1.0f, 0.8f, 0.8f, 1.0f },
312 { 0.8f, 1.0f, 0.8f, 1.0f },
315 /* Configure the lighting. */
317 glEnable(GL_LIGHT0);
318 glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
319 glLightfv(GL_LIGHT0, GL_DIFFUSE, light_c[0]);
320 glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
322 glEnable(GL_LIGHT1);
323 glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
324 glLightfv(GL_LIGHT1, GL_DIFFUSE, light_c[1]);
325 glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
328 static void game_draw_back(int pose, int d, const float p[3])
330 float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
331 float t = SDL_GetTicks() / 1000.f + 120.0f;
333 glPushMatrix();
335 if (d < 0)
337 glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
338 glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
341 glTranslatef(p[0], p[1], p[2]);
342 glColor4fv(c);
344 if (config_get_d(CONFIG_BACKGROUND))
346 /* Draw all background layers back to front. */
348 sol_back(&back, BACK_DIST, FAR_DIST, t);
349 back_draw(0);
350 sol_back(&back, 0, BACK_DIST, t);
352 /* Draw all foreground geometry in the background file. */
354 sol_draw(&back);
356 else back_draw(0);
358 glPopMatrix();
361 static void game_draw_fore(int pose, float rx, float ry, int d, const float p[3])
363 const float *ball_p = file.uv->p;
364 const float ball_r = file.uv->r;
366 glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT);
368 glPushMatrix();
370 /* Rotate the environment about the position of the ball. */
372 glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
373 glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
374 glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
375 glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
377 if (d < 0)
379 GLdouble e[4];
381 e[0] = +0;
382 e[1] = +1;
383 e[2] = +0;
384 e[3] = -0.00001;
386 glEnable(GL_CLIP_PLANE0);
387 glClipPlane(GL_CLIP_PLANE0, e);
390 /* Draw the floor. */
392 sol_draw(&file);
394 if (config_get_d(CONFIG_SHADOW))
396 shad_draw_set(ball_p, ball_r);
397 sol_shad(&file);
398 shad_draw_clr();
401 /* Draw the game elements. */
403 glEnable(GL_BLEND);
404 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
406 if (pose == 0)
408 part_draw_coin(-rx * d, -ry);
409 game_draw_coins(&file);
410 game_draw_balls(&file);
412 game_draw_goals(&file, -rx * d, -ry);
413 game_draw_jumps(&file);
414 game_draw_swchs(&file);
416 glDisable(GL_CLIP_PLANE0);
418 glPopMatrix();
420 glPopAttrib();
423 void game_draw(int pose, float st)
425 float fov = view_fov;
427 if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
429 if (game_state)
431 config_push_persp(fov, 0.1f, FAR_DIST);
432 glPushMatrix();
434 float v[3], rx, ry;
435 float pup[3];
436 float pdn[3];
438 v_cpy(pup, view_p);
439 v_cpy(pdn, view_p);
440 pdn[1] = -pdn[1];
442 /* Compute and apply the view. */
444 v_sub(v, view_c, view_p);
446 rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
447 ry = V_DEG(fatan2f(+v[0], -v[2])) + st;
449 glTranslatef(0.f, 0.f, -v_len(v));
450 glRotatef(rx, 1.f, 0.f, 0.f);
451 glRotatef(ry, 0.f, 1.f, 0.f);
452 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
454 if (config_get_d(CONFIG_REFLECTION))
456 /* Draw the mirror only into the stencil buffer. */
458 glDisable(GL_DEPTH_TEST);
459 glEnable(GL_STENCIL_TEST);
460 glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
461 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
462 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
464 game_refl_all(0);
466 /* Draw the scene reflected into color and depth buffers. */
468 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
469 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
470 glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
471 glEnable(GL_DEPTH_TEST);
473 glFrontFace(GL_CW);
474 glPushMatrix();
476 glScalef(+1.f, -1.f, +1.f);
478 game_draw_light();
479 game_draw_back(pose, -1, pdn);
480 game_draw_fore(pose, rx, ry, -1, pdn);
482 glPopMatrix();
483 glFrontFace(GL_CCW);
485 glDisable(GL_STENCIL_TEST);
488 /* Draw the scene normally. */
490 game_draw_light();
491 game_refl_all(pose ? 0 : config_get_d(CONFIG_SHADOW));
492 game_draw_back(pose, +1, pup);
493 game_draw_fore(pose, rx, ry, +1, pup);
495 glPopMatrix();
496 config_pop_matrix();
498 /* Draw the fade overlay. */
500 fade_draw(fade_k);
504 /*---------------------------------------------------------------------------*/
506 static void game_update_grav(float h[3], const float g[3])
508 struct s_file *fp = &file;
510 float x[3];
511 float y[3] = { 0.f, 1.f, 0.f };
512 float z[3];
513 float X[16];
514 float Z[16];
515 float M[16];
517 /* Compute the gravity vector from the given world rotations. */
519 v_sub(z, view_p, fp->uv->p);
520 v_crs(x, y, z);
521 v_crs(z, x, y);
522 v_nrm(x, x);
523 v_nrm(z, z);
525 m_rot (Z, z, V_RAD(game_rz));
526 m_rot (X, x, V_RAD(game_rx));
527 m_mult(M, Z, X);
528 m_vxfm(h, M, g);
531 static void game_update_view(float dt)
533 float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
534 float dx = view_ry * dt * 5.0f;
535 float k;
537 view_a += view_ry * dt * 90.f;
539 /* Center the view about the ball. */
541 v_cpy(view_c, file.uv->p);
542 v_inv(view_v, file.uv->v);
544 switch (config_get_d(CONFIG_CAMERA))
546 case 1: /* Camera 1: Viewpoint chases the ball position. */
548 v_sub(view_e[2], view_p, view_c);
549 break;
551 case 2: /* Camera 2: View vector is given by view angle. */
553 view_e[2][0] = fsinf(V_RAD(view_a));
554 view_e[2][1] = 0.f;
555 view_e[2][2] = fcosf(V_RAD(view_a));
557 dx = 0.0f;
559 break;
561 default: /* Default: View vector approaches the ball velocity vector. */
563 k = v_dot(view_v, view_v);
565 v_sub(view_e[2], view_p, view_c);
566 v_mad(view_e[2], view_e[2], view_v, k * dt / 4);
568 break;
571 /* Orthonormalize the basis of the view in its new position. */
573 v_crs(view_e[0], view_e[1], view_e[2]);
574 v_crs(view_e[2], view_e[0], view_e[1]);
575 v_nrm(view_e[0], view_e[0]);
576 v_nrm(view_e[2], view_e[2]);
578 /* Compute the new view position. */
580 k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
582 view_k = view_k + (k - view_k) * dt;
584 if (view_k < 0.5) view_k = 0.5;
586 v_cpy(view_p, file.uv->p);
587 v_mad(view_p, view_p, view_e[0], dx * view_k);
588 v_mad(view_p, view_p, view_e[1], view_dp * view_k);
589 v_mad(view_p, view_p, view_e[2], view_dz * view_k);
591 /* Compute the new view center. */
593 v_cpy(view_c, file.uv->p);
594 v_mad(view_c, view_c, view_e[1], dc);
596 /* Note the current view angle. */
598 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
601 static void game_update_time(float dt, int b)
603 int tick = (int) floor(clock);
604 int tock = (int) floor(clock * 2);
606 if (goal_e && goal_k < 1.0f)
607 goal_k += dt;
609 /* The ticking clock. */
611 if (b)
613 if (clock < 600.f)
614 clock -= dt;
615 if (clock < 0.f)
616 clock = 0.f;
618 if (0 < tick && tick <= 10 && tick == (int) ceil(clock))
620 audio_play(AUD_TICK, 1.f);
621 hud_time_pulse(1.50);
623 else if (0 < tock && tock <= 10 && tock == (int) ceil(clock * 2))
625 audio_play(AUD_TOCK, 1.f);
626 hud_time_pulse(1.25);
631 static int game_update_state(void)
633 struct s_file *fp = &file;
634 float p[3];
635 float c[3];
636 int n, e = swch_e;
638 /* Test for a coin grab and a possible 1UP. */
640 if ((n = sol_coin_test(fp, p, COIN_RADIUS)) > 0)
642 coin_color(c, n);
643 part_burst(p, c);
645 if (level_score(n))
646 goal_e = 1;
649 /* Test for a switch. */
651 if ((swch_e = sol_swch_test(fp, swch_e, 0)) != e && e)
652 audio_play(AUD_SWITCH, 1.f);
654 /* Test for a jump. */
656 if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
658 jump_b = 1;
659 jump_e = 0;
660 jump_dt = 0.f;
662 audio_play(AUD_JUMP, 1.f);
664 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
665 jump_e = 1;
667 /* Test for a goal. */
669 if (goal_e && sol_goal_test(fp, p, 0))
670 return GAME_GOAL;
672 /* Test for time-out. */
674 if (clock <= 0.f)
675 return GAME_TIME;
677 /* Test for fall-out. */
679 if (fp->uv[0].p[1] < fp->vv[0].p[1])
680 return GAME_FALL;
682 return GAME_NONE;
686 * On most hardware, rendering requires much more computing power than
687 * physics. Since physics takes less time than graphics, it make sense to
688 * detach the physics update time step from the graphics frame rate. By
689 * performing multiple physics updates for each graphics update, we get away
690 * with higher quality physics with little impact on overall performance.
692 * Toward this end, we establish a baseline maximum physics time step. If
693 * the measured frame time exceeds this maximum, we cut the time step in
694 * half, and do two updates. If THIS time step exceeds the maximum, we do
695 * four updates. And so on. In this way, the physics system is allowed to
696 * seek an optimal update rate independant of, yet in integral sync with, the
697 * graphics frame rate.
700 int game_step(const float g[3], float dt, int bt)
702 struct s_file *fp = &file;
704 float h[3];
705 float d = 0.f;
706 float b = 0.f;
707 float t;
708 int i, n = 1;
710 if (game_state)
712 t = dt;
714 /* Smooth jittery or discontinuous input. */
716 if (t < RESPONSE)
718 game_rx += (game_ix - game_rx) * t / RESPONSE;
719 game_rz += (game_iz - game_rz) * t / RESPONSE;
721 else
723 game_rx = game_ix;
724 game_rz = game_iz;
727 game_update_grav(h, g);
728 part_step(h, t);
730 if (jump_b)
732 jump_dt += t;
734 /* Handle a jump. */
736 if (0.5 < jump_dt)
738 fp->uv[0].p[0] = jump_p[0];
739 fp->uv[0].p[1] = jump_p[1];
740 fp->uv[0].p[2] = jump_p[2];
742 if (1.f < jump_dt)
743 jump_b = 0;
745 else
747 /* Run the sim. */
749 while (t > MAX_DT && n < MAX_DN)
751 t /= 2;
752 n *= 2;
755 for (i = 0; i < n; i++)
756 if (b < (d = sol_step(fp, h, t, 0, NULL)))
757 b = d;
759 /* Mix the sound of a ball bounce. */
761 if (b > 0.5)
762 audio_play(AUD_BUMP, (b - 0.5f) * 2.0f);
765 game_step_fade(dt);
766 game_update_view(dt);
767 game_update_time(dt, bt);
769 return game_update_state();
771 return GAME_NONE;
774 /*---------------------------------------------------------------------------*/
776 void game_set_x(int k)
778 game_ix = -20.f * k / JOY_MAX;
781 void game_set_z(int k)
783 game_iz = +20.f * k / JOY_MAX;
786 void game_set_pos(int x, int y)
788 float bound = 20.f;
790 game_ix += 40.f * y / config_get_d(CONFIG_MOUSE_SENSE);
791 game_iz += 40.f * x / config_get_d(CONFIG_MOUSE_SENSE);
793 if (game_ix > +bound) game_ix = +bound;
794 if (game_ix < -bound) game_ix = -bound;
795 if (game_iz > +bound) game_iz = +bound;
796 if (game_iz < -bound) game_iz = -bound;
799 void game_set_rot(float r)
801 view_ry = r;
804 /*---------------------------------------------------------------------------*/
806 void game_set_fly(float k)
808 struct s_file *fp = &file;
810 float x[3] = { 1.f, 0.f, 0.f };
811 float y[3] = { 0.f, 1.f, 0.f };
812 float z[3] = { 0.f, 0.f, 1.f };
813 float c0[3] = { 0.f, 0.f, 0.f };
814 float p0[3] = { 0.f, 0.f, 0.f };
815 float c1[3] = { 0.f, 0.f, 0.f };
816 float p1[3] = { 0.f, 0.f, 0.f };
817 float v[3];
819 v_cpy(view_e[0], x);
820 v_cpy(view_e[1], y);
821 v_cpy(view_e[2], z);
823 /* k = 0.0 view is at the ball. */
825 if (fp->uc > 0)
827 v_cpy(c0, fp->uv[0].p);
828 v_cpy(p0, fp->uv[0].p);
831 v_mad(p0, p0, y, view_dp);
832 v_mad(p0, p0, z, view_dz);
833 v_mad(c0, c0, y, view_dc);
835 /* k = +1.0 view is s_view 0 */
837 if (k >= 0 && fp->wc > 0)
839 v_cpy(p1, fp->wv[0].p);
840 v_cpy(c1, fp->wv[0].q);
843 /* k = -1.0 view is s_view 1 */
845 if (k <= 0 && fp->wc > 1)
847 v_cpy(p1, fp->wv[1].p);
848 v_cpy(c1, fp->wv[1].q);
851 /* Interpolate the views. */
853 v_sub(v, p1, p0);
854 v_mad(view_p, p0, v, k * k);
856 v_sub(v, c1, c0);
857 v_mad(view_c, c0, v, k * k);
859 /* Orthonormalize the view basis. */
861 v_sub(view_e[2], view_p, view_c);
862 v_crs(view_e[0], view_e[1], view_e[2]);
863 v_crs(view_e[2], view_e[0], view_e[1]);
864 v_nrm(view_e[0], view_e[0]);
865 v_nrm(view_e[2], view_e[2]);
868 void game_look(float phi, float theta)
870 view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
871 view_c[1] = view_p[1] + fsinf(V_RAD(phi));
872 view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
875 /*---------------------------------------------------------------------------*/
877 void game_kill_fade(void)
879 fade_k = 0.0f;
880 fade_d = 0.0f;
883 void game_step_fade(float dt)
885 if ((fade_k < 1.0f && fade_d > 0.0f) ||
886 (fade_k > 0.0f && fade_d < 0.0f))
887 fade_k += fade_d * dt;
889 if (fade_k < 0.0f)
891 fade_k = 0.0f;
892 fade_d = 0.0f;
894 if (fade_k > 1.0f)
896 fade_k = 1.0f;
897 fade_d = 0.0f;
901 void game_fade(float d)
903 fade_d = d;
906 /*---------------------------------------------------------------------------*/
908 int put_game_state(FILE *fout)
910 if (game_state)
912 /* Write the view and tilt state. */
914 put_float(fout, &game_rx);
915 put_float(fout, &game_rz);
916 put_array(fout, view_c, 3);
917 put_array(fout, view_p, 3);
919 /* Write the game simulation state. */
921 put_file_state(fout, &file);
923 return 1;
925 return 0;
928 int get_game_state(FILE *fin)
930 if (game_state)
932 /* Read the view and tilt state. */
934 get_float(fin, &game_rx);
935 get_float(fin, &game_rz);
936 get_array(fin, view_c, 3);
937 get_array(fin, view_p, 3);
939 /* Read the game simulation state. */
941 get_file_state(fin, &file);
943 return (feof(fin) ? 0 : 1);
945 return 0;
948 /*---------------------------------------------------------------------------*/