New play mode. be happy.
[neverball] / ball / game.c
1 /*   
2  * Copyright (C) 2003 Robert Kooima
3  *
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.
8  *
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.
13  */
14
15 #include <SDL.h>
16 #include <math.h>
17
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"
31
32 /*---------------------------------------------------------------------------*/
33
34 static int game_state = 0;
35
36 static struct s_file file;
37 static struct s_file back;
38
39 static float clock = 0.f;               /* Clock time                        */
40
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       */
45
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                     */
52
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;
58
59 static int   goal_e = 0;                /* Goal enabled flag                 */
60 static float goal_k = 0;                /* Goal animation                    */
61 static int   goal_s = 0;                /* Goal reached flag                 */
62 static int   swch_e = 1;                /* Switching enabled flag            */
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             */
69
70 /*---------------------------------------------------------------------------*/
71
72 static void view_init(void)
73 {
74     view_a  = 0.f;
75     view_ry = 0.f;
76
77     view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
78     view_dp  = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
79     view_dc  = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
80     view_dz  = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
81     view_k   = 1.0f;
82
83     view_c[0] = 0.f;
84     view_c[1] = view_dc;
85     view_c[2] = 0.f;
86
87     view_p[0] =     0.f;
88     view_p[1] = view_dp;
89     view_p[2] = view_dz;
90
91     view_e[0][0] = 1.f;
92     view_e[0][1] = 0.f;
93     view_e[0][2] = 0.f;
94     view_e[1][0] = 0.f;
95     view_e[1][1] = 1.f;
96     view_e[1][2] = 0.f;
97     view_e[2][0] = 0.f;
98     view_e[2][1] = 0.f;
99     view_e[2][2] = 1.f;
100 }
101
102 int game_init(const char *file_name,
103               const char *back_name,
104               const char *grad_name, int t, int e)
105 {
106     if (level_mode() == MODE_FREE)
107         clock = 0.f;
108     else
109         clock = (float) t / 100.f;
110
111     if (game_state)
112         game_free();
113
114     game_ix = 0.f;
115     game_iz = 0.f;
116     game_rx = 0.f;
117     game_rz = 0.f;
118
119     /* Initialize jump and goal states. */
120
121     jump_e = 1;
122     jump_b = 0;
123
124     goal_e = e ? 1    : 0;
125     goal_k = e ? 1.0f : 0.0f;
126     goal_s = 0;
127
128     /* Reset the hud. */
129
130     hud_ball_pulse(0.f);
131     hud_time_pulse(0.f);
132     hud_coin_pulse(0.f);
133
134     /* Initialise the level, background, particles, fade, and view. */
135
136     fade_k =  1.0f;
137     fade_d = -2.0f;
138
139     part_reset(GOAL_HEIGHT);
140     view_init();
141     back_init(grad_name, config_get_d(CONFIG_GEOMETRY));
142
143     if (sol_load(&back, config_data(back_name),
144                  config_get_d(CONFIG_TEXTURES), 0) &&
145         sol_load(&file, config_data(file_name),
146                  config_get_d(CONFIG_TEXTURES), config_get_d(CONFIG_SHADOW)))
147         return (game_state = 1);
148     else
149         return (game_state = 0);
150 }
151
152 void game_free(void)
153 {
154     if (game_state)
155     {
156         sol_free(&file);
157         sol_free(&back);
158         back_free();
159     }
160     game_state = 0;
161 }
162
163 /*---------------------------------------------------------------------------*/
164
165 int curr_clock(void)
166 {
167     return (int) (clock * 100.f);
168 }
169
170 char *curr_intro(void)
171 {
172     return (file.ac > 0) ? file.av : NULL;
173 }
174
175 /*---------------------------------------------------------------------------*/
176
177 static void game_draw_balls(const struct s_file *fp)
178 {
179     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
180     float M[16];
181
182     m_basis(M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
183
184     glPushMatrix();
185     {
186         glTranslatef(fp->uv[0].p[0],
187                      fp->uv[0].p[1] + BALL_FUDGE,
188                      fp->uv[0].p[2]);
189         glMultMatrixf(M);
190         glScalef(fp->uv[0].r,
191                  fp->uv[0].r,
192                  fp->uv[0].r);
193
194         glColor4fv(c);
195
196         ball_draw();
197     }
198     glPopMatrix();
199 }
200
201 static void game_draw_coins(const struct s_file *fp)
202 {
203     float r = 360.f * SDL_GetTicks() / 1000.f;
204     int ci;
205
206     coin_push();
207     {
208         for (ci = 0; ci < fp->cc; ci++)
209             if (fp->cv[ci].n > 0)
210             {
211                 glPushMatrix();
212                 {
213                     glTranslatef(fp->cv[ci].p[0],
214                                  fp->cv[ci].p[1],
215                                  fp->cv[ci].p[2]);
216                     glRotatef(r, 0.0f, 1.0f, 0.0f);
217                     coin_draw(fp->cv[ci].n, r);
218                 }
219                 glPopMatrix();
220             }
221     }
222     coin_pull();
223 }
224
225 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
226 {
227     int zi;
228
229     if (goal_e)
230         for (zi = 0; zi < fp->zc; zi++)
231         {
232             glPushMatrix();
233             {
234                 glTranslatef(fp->zv[zi].p[0],
235                              fp->zv[zi].p[1],
236                              fp->zv[zi].p[2]);
237
238                 part_draw_goal(rx, ry, fp->zv[zi].r, goal_k);
239
240                 glScalef(fp->zv[zi].r, goal_k, fp->zv[zi].r);
241                 goal_draw();
242             }
243             glPopMatrix();
244         }
245 }
246
247 static void game_draw_jumps(const struct s_file *fp)
248 {
249     int ji;
250
251     for (ji = 0; ji < fp->jc; ji++)
252     {
253         glPushMatrix();
254         {
255             glTranslatef(fp->jv[ji].p[0],
256                          fp->jv[ji].p[1],
257                          fp->jv[ji].p[2]);
258
259             glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
260             jump_draw();
261         }
262         glPopMatrix();
263     }
264 }
265
266 static void game_draw_swchs(const struct s_file *fp)
267 {
268     int xi;
269
270     for (xi = 0; xi < fp->xc; xi++)
271     {
272         glPushMatrix();
273         {
274             glTranslatef(fp->xv[xi].p[0],
275                          fp->xv[xi].p[1],
276                          fp->xv[xi].p[2]);
277
278             glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
279             swch_draw(fp->xv[xi].f);
280         }
281         glPopMatrix();
282     }
283 }
284
285 /*---------------------------------------------------------------------------*/
286
287 static void game_refl_all(int s)
288 {
289     const float *ball_p = file.uv->p;
290
291     glPushMatrix();
292     {
293         /* Rotate the environment about the position of the ball. */
294
295         glTranslatef(+ball_p[0], +ball_p[1], +ball_p[2]);
296         glRotatef(-game_rz, view_e[2][0], view_e[2][1], view_e[2][2]);
297         glRotatef(-game_rx, view_e[0][0], view_e[0][1], view_e[0][2]);
298         glTranslatef(-ball_p[0], -ball_p[1], -ball_p[2]);
299
300         /* Draw the floor. */
301
302         sol_refl(&file);
303     }
304     glPopMatrix();
305 }
306
307 /*---------------------------------------------------------------------------*/
308
309 static void game_draw_light(void)
310 {
311     const float light_p[2][4] = {
312         { -8.0f, +32.0f, -8.0f, 1.0f },
313         { +8.0f, +32.0f, +8.0f, 1.0f },
314     };
315     const float light_c[2][4] = {
316         { 1.0f, 0.8f, 0.8f, 1.0f },
317         { 0.8f, 1.0f, 0.8f, 1.0f },
318     };
319
320     /* Configure the lighting. */
321
322     glEnable(GL_LIGHT0);
323     glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
324     glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_c[0]);
325     glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
326
327     glEnable(GL_LIGHT1);
328     glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
329     glLightfv(GL_LIGHT1, GL_DIFFUSE,  light_c[1]);
330     glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
331 }
332
333 static void game_draw_back(int pose, int d, const float p[3])
334 {
335     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
336     float t = SDL_GetTicks() / 1000.f + 120.0f;
337
338     glPushMatrix();
339     {
340         if (d < 0)
341         {
342             glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
343             glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
344         }
345
346         glTranslatef(p[0], p[1], p[2]);
347         glColor4fv(c);
348
349         if (config_get_d(CONFIG_BACKGROUND))
350         {
351             /* Draw all background layers back to front. */
352
353             sol_back(&back, BACK_DIST, FAR_DIST, t);
354             back_draw(0);
355             sol_back(&back, 0, BACK_DIST, t);
356
357             /* Draw all foreground geometry in the background file. */
358
359             sol_draw(&back);
360         }
361         else back_draw(0);
362     }
363     glPopMatrix();
364 }
365
366 static void game_draw_fore(int pose, float rx, float ry, int d, const float p[3])
367 {
368     const float *ball_p = file.uv->p;
369     const float  ball_r = file.uv->r;
370     
371     glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT);
372     {
373         glPushMatrix();
374         {
375             /* Rotate the environment about the position of the ball. */
376
377             glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
378             glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
379             glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
380             glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
381
382             if (d < 0)
383             {
384                 GLdouble e[4];
385
386                 e[0] = +0;
387                 e[1] = +1;
388                 e[2] = +0;
389                 e[3] = -0.00001;
390
391                 glEnable(GL_CLIP_PLANE0);
392                 glClipPlane(GL_CLIP_PLANE0, e);
393             }
394
395             /* Draw the floor. */
396
397             sol_draw(&file);
398
399             if (config_get_d(CONFIG_SHADOW))
400             {
401                 shad_draw_set(ball_p, ball_r);
402                 sol_shad(&file);
403                 shad_draw_clr();
404             }
405
406             /* Draw the game elements. */
407
408             glEnable(GL_BLEND);
409             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
410
411             if (pose == 0)
412             {
413                 part_draw_coin(-rx * d, -ry);
414                 game_draw_coins(&file);
415                 game_draw_balls(&file);
416             }
417             game_draw_goals(&file, -rx * d, -ry);
418             game_draw_jumps(&file);
419             game_draw_swchs(&file);
420
421             glDisable(GL_CLIP_PLANE0);
422         }
423         glPopMatrix();
424     }
425     glPopAttrib();
426 }
427
428 void game_draw(int pose, float st)
429 {
430     float fov = view_fov;
431
432     if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
433
434     if (game_state)
435     {
436         config_push_persp(fov, 0.1f, FAR_DIST);
437         glPushMatrix();
438         {
439             float v[3], rx, ry;
440             float pup[3];
441             float pdn[3];
442
443             v_cpy(pup, view_p);
444             v_cpy(pdn, view_p);
445             pdn[1] = -pdn[1];
446
447             /* Compute and apply the view. */
448
449             v_sub(v, view_c, view_p);
450
451             rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
452             ry = V_DEG(fatan2f(+v[0], -v[2])) + st;
453
454             glTranslatef(0.f, 0.f, -v_len(v));
455             glRotatef(rx, 1.f, 0.f, 0.f);
456             glRotatef(ry, 0.f, 1.f, 0.f);
457             glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
458
459             if (config_get_d(CONFIG_REFLECTION))
460             {
461                 /* Draw the mirror only into the stencil buffer. */
462
463                 glDisable(GL_DEPTH_TEST);
464                 glEnable(GL_STENCIL_TEST);
465                 glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
466                 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
467                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
468
469                 game_refl_all(0);
470
471                 /* Draw the scene reflected into color and depth buffers. */
472
473                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
474                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
475                 glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
476                 glEnable(GL_DEPTH_TEST);
477
478                 glFrontFace(GL_CW);
479                 glPushMatrix();
480                 {
481                     glScalef(+1.f, -1.f, +1.f);
482
483                     game_draw_light();
484                     game_draw_back(pose,         -1, pdn);
485                     game_draw_fore(pose, rx, ry, -1, pdn);
486                 }
487                 glPopMatrix();
488                 glFrontFace(GL_CCW);
489
490                 glDisable(GL_STENCIL_TEST);
491             }
492
493             /* Draw the scene normally. */
494
495             game_draw_light();
496             game_refl_all(pose ? 0 : config_get_d(CONFIG_SHADOW));
497             game_draw_back(pose,         +1, pup);
498             game_draw_fore(pose, rx, ry, +1, pup);
499         }
500         glPopMatrix();
501         config_pop_matrix();
502
503         /* Draw the fade overlay. */
504
505         fade_draw(fade_k);
506     }
507 }
508
509 /*---------------------------------------------------------------------------*/
510
511 static void game_update_grav(float h[3], const float g[3])
512 {
513     struct s_file *fp = &file;
514
515     float x[3];
516     float y[3] = { 0.f, 1.f, 0.f };
517     float z[3];
518     float X[16];
519     float Z[16];
520     float M[16];
521
522     /* Compute the gravity vector from the given world rotations. */
523
524     v_sub(z, view_p, fp->uv->p);
525     v_crs(x, y, z);
526     v_crs(z, x, y);
527     v_nrm(x, x);
528     v_nrm(z, z);
529
530     m_rot (Z, z, V_RAD(game_rz));
531     m_rot (X, x, V_RAD(game_rx));
532     m_mult(M, Z, X);
533     m_vxfm(h, M, g);
534 }
535
536 static void game_update_view(float dt)
537 {
538     float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
539     float dx = view_ry * dt * 5.0f;
540     float k;
541
542     view_a += view_ry * dt * 90.f;
543
544     /* Center the view about the ball. */
545
546     v_cpy(view_c, file.uv->p);
547     v_inv(view_v, file.uv->v);
548
549     switch (config_get_d(CONFIG_CAMERA))
550     {
551     case 1: /* Camera 1:  Viewpoint chases the ball position. */
552
553         v_sub(view_e[2], view_p, view_c);
554         break;
555
556     case 2: /* Camera 2: View vector is given by view angle. */
557
558         view_e[2][0] = fsinf(V_RAD(view_a));
559         view_e[2][1] = 0.f;
560         view_e[2][2] = fcosf(V_RAD(view_a));
561
562         dx = 0.0f;
563
564         break;
565
566     default: /* Default: View vector approaches the ball velocity vector. */
567
568         k = v_dot(view_v, view_v);
569
570         v_sub(view_e[2], view_p, view_c);
571         v_mad(view_e[2], view_e[2], view_v, k * dt / 4);
572
573         break;
574     }
575
576     /* Orthonormalize the basis of the view in its new position. */
577
578     v_crs(view_e[0], view_e[1], view_e[2]);
579     v_crs(view_e[2], view_e[0], view_e[1]);
580     v_nrm(view_e[0], view_e[0]);
581     v_nrm(view_e[2], view_e[2]);
582
583     /* Compute the new view position. */
584
585     k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
586
587     view_k = view_k + (k - view_k) * dt;
588
589     if (view_k < 0.5) view_k = 0.5;
590
591     v_cpy(view_p, file.uv->p);
592     v_mad(view_p, view_p, view_e[0], dx      * view_k);
593     v_mad(view_p, view_p, view_e[1], view_dp * view_k);
594     v_mad(view_p, view_p, view_e[2], view_dz * view_k);
595
596     /* Compute the new view center. */
597
598     v_cpy(view_c, file.uv->p);
599     v_mad(view_c, view_c, view_e[1], dc);
600
601     /* Note the current view angle. */
602
603     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
604 }
605
606 static void game_update_time(float dt, int b)
607 {
608     int tick = (int) floor(clock);
609     int tock = (int) floor(clock * 2);
610
611     if (goal_e && goal_k < 1.0f)
612         goal_k += dt;
613
614    /* The ticking clock. */
615
616     if (b==2)
617         clock += dt;
618     else if (b)
619     {
620         if (clock < 600.f)
621             clock -= dt;
622         if (clock < 0.f)
623             clock = 0.f;
624
625         if (0 < tick && tick <= 10 && tick == (int) ceil(clock))
626         {
627             audio_play(AUD_TICK, 1.f);
628             hud_time_pulse(1.50);
629         }
630         else if (0 < tock && tock <= 10 && tock == (int) ceil(clock * 2))
631         {
632             audio_play(AUD_TOCK, 1.f);
633             hud_time_pulse(1.25);
634         }
635     }
636 }
637
638 static int game_update_state(void)
639 {
640     struct s_file *fp = &file;
641     float p[3];
642     float c[3];
643     int n, e = swch_e;
644
645     /* Test for a coin grab and a possible 1UP. */
646
647     if ((n = sol_coin_test(fp, p, COIN_RADIUS)) > 0)
648     {
649         coin_color(c, n);
650         part_burst(p, c);
651
652         if (level_score(n))
653             goal_e = 1;
654     }
655
656     /* Test for a switch. */
657
658     if ((swch_e = sol_swch_test(fp, swch_e, 0)) != e && e)
659         audio_play(AUD_SWITCH, 1.f);
660
661     /* Test for a jump. */
662
663     if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
664     {
665         jump_b  = 1;
666         jump_e  = 0;
667         jump_dt = 0.f;
668         
669         audio_play(AUD_JUMP, 1.f);
670     }
671     if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
672         jump_e = 1;
673
674     /* Test for a goal. */
675
676     if (goal_e && sol_goal_test(fp, p, 0))
677     {
678         if (!goal_s)
679         {
680             goal_s = 1;
681             audio_play(AUD_GOAL, 1.0f);
682         }
683         return GAME_GOAL;
684     }
685
686     /* Test for time-out. */
687
688     if (clock <= 0.f)
689         return GAME_TIME;
690
691     /* Test for fall-out. */
692
693     if (fp->uv[0].p[1] < fp->vv[0].p[1])
694         return GAME_FALL;
695
696     return GAME_NONE;
697 }
698
699 /*
700  * On  most  hardware, rendering  requires  much  more  computing power  than
701  * physics.  Since  physics takes less time  than graphics, it  make sense to
702  * detach  the physics update  time step  from the  graphics frame  rate.  By
703  * performing multiple physics updates for  each graphics update, we get away
704  * with higher quality physics with little impact on overall performance.
705  *
706  * Toward this  end, we establish a  baseline maximum physics  time step.  If
707  * the measured  frame time  exceeds this  maximum, we cut  the time  step in
708  * half, and  do two updates.  If THIS  time step exceeds the  maximum, we do
709  * four updates.  And  so on.  In this way, the physics  system is allowed to
710  * seek an optimal update rate independant of, yet in integral sync with, the
711  * graphics frame rate.
712  */
713
714 int game_step(const float g[3], float dt, int bt)
715 {
716     struct s_file *fp = &file;
717
718     float h[3];
719     float d = 0.f;
720     float b = 0.f;
721     float t;
722     int i, n = 1;
723
724     if (game_state)
725     {
726         t = dt;
727
728         /* Smooth jittery or discontinuous input. */
729
730         if (t < RESPONSE)
731         {
732             game_rx += (game_ix - game_rx) * t / RESPONSE;
733             game_rz += (game_iz - game_rz) * t / RESPONSE;
734         }
735         else
736         {
737             game_rx = game_ix;
738             game_rz = game_iz;
739         }
740
741         game_update_grav(h, g);
742         part_step(h, t);
743
744         if (jump_b)
745         {
746             jump_dt += t;
747
748             /* Handle a jump. */
749
750             if (0.5 < jump_dt)
751             {
752                 fp->uv[0].p[0] = jump_p[0];
753                 fp->uv[0].p[1] = jump_p[1];
754                 fp->uv[0].p[2] = jump_p[2];
755             }
756             if (1.f < jump_dt)
757                 jump_b = 0;
758         }
759         else
760         {
761             /* Run the sim. */
762
763             while (t > MAX_DT && n < MAX_DN)
764             {
765                 t /= 2;
766                 n *= 2;
767             }
768
769             for (i = 0; i < n; i++)
770                 if (b < (d = sol_step(fp, h, t, 0, NULL)))
771                     b = d;
772
773             /* Mix the sound of a ball bounce. */
774
775             if (b > 0.5)
776                 audio_play(AUD_BUMP, (b - 0.5f) * 2.0f);
777         }
778
779         game_step_fade(dt);
780         game_update_view(dt);
781         game_update_time(dt, bt);
782
783         return game_update_state();
784     }
785     return GAME_NONE;
786 }
787
788 /*---------------------------------------------------------------------------*/
789
790 void game_set_x(int k)
791 {
792     game_ix = -20.f * k / JOY_MAX;
793 }
794
795 void game_set_z(int k)
796 {
797     game_iz = +20.f * k / JOY_MAX;
798 }
799
800 void game_set_pos(int x, int y)
801 {
802     float bound = 20.f;
803
804     game_ix += 40.f * y / config_get_d(CONFIG_MOUSE_SENSE);
805     game_iz += 40.f * x / config_get_d(CONFIG_MOUSE_SENSE);
806
807     if (game_ix > +bound) game_ix = +bound;
808     if (game_ix < -bound) game_ix = -bound;
809     if (game_iz > +bound) game_iz = +bound;
810     if (game_iz < -bound) game_iz = -bound;
811 }
812
813 void game_set_rot(float r)
814 {
815     view_ry = r;
816 }
817
818 /*---------------------------------------------------------------------------*/
819
820 void game_set_fly(float k)
821 {
822     struct s_file *fp = &file;
823
824     float  x[3] = { 1.f, 0.f, 0.f };
825     float  y[3] = { 0.f, 1.f, 0.f };
826     float  z[3] = { 0.f, 0.f, 1.f };
827     float c0[3] = { 0.f, 0.f, 0.f };
828     float p0[3] = { 0.f, 0.f, 0.f };
829     float c1[3] = { 0.f, 0.f, 0.f };
830     float p1[3] = { 0.f, 0.f, 0.f };
831     float  v[3];
832
833     v_cpy(view_e[0], x);
834     v_cpy(view_e[1], y);
835     v_cpy(view_e[2], z);
836
837     /* k = 0.0 view is at the ball. */
838
839     if (fp->uc > 0)
840     {
841         v_cpy(c0, fp->uv[0].p);
842         v_cpy(p0, fp->uv[0].p);
843     }
844
845     v_mad(p0, p0, y, view_dp);
846     v_mad(p0, p0, z, view_dz);
847     v_mad(c0, c0, y, view_dc);
848
849     /* k = +1.0 view is s_view 0 */
850
851     if (k >= 0 && fp->wc > 0)
852     {
853         v_cpy(p1, fp->wv[0].p);
854         v_cpy(c1, fp->wv[0].q);
855     }
856
857     /* k = -1.0 view is s_view 1 */
858
859     if (k <= 0 && fp->wc > 1)
860     {
861         v_cpy(p1, fp->wv[1].p);
862         v_cpy(c1, fp->wv[1].q);
863     }
864
865     /* Interpolate the views. */
866
867     v_sub(v, p1, p0);
868     v_mad(view_p, p0, v, k * k);
869
870     v_sub(v, c1, c0);
871     v_mad(view_c, c0, v, k * k);
872
873     /* Orthonormalize the view basis. */
874
875     v_sub(view_e[2], view_p, view_c);
876     v_crs(view_e[0], view_e[1], view_e[2]);
877     v_crs(view_e[2], view_e[0], view_e[1]);
878     v_nrm(view_e[0], view_e[0]);
879     v_nrm(view_e[2], view_e[2]);
880 }
881
882 void game_look(float phi, float theta)
883 {
884     view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
885     view_c[1] = view_p[1] +                       fsinf(V_RAD(phi));
886     view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
887 }
888
889 /*---------------------------------------------------------------------------*/
890
891 void game_kill_fade(void)
892 {
893     fade_k = 0.0f;
894     fade_d = 0.0f;
895 }
896
897 void game_step_fade(float dt)
898 {
899     if ((fade_k < 1.0f && fade_d > 0.0f) ||
900         (fade_k > 0.0f && fade_d < 0.0f))
901         fade_k += fade_d * dt;
902
903     if (fade_k < 0.0f)
904     {
905         fade_k = 0.0f;
906         fade_d = 0.0f;
907     }
908     if (fade_k > 1.0f)
909     {
910         fade_k = 1.0f;
911         fade_d = 0.0f;
912     }
913 }
914
915 void game_fade(float d)
916 {
917     fade_d = d;
918 }
919
920 /*---------------------------------------------------------------------------*/
921
922 int put_game_state(FILE *fout)
923 {
924     if (game_state)
925     {
926         /* Write the view and tilt state. */
927
928         put_float(fout, &game_rx);
929         put_float(fout, &game_rz);
930         put_array(fout,  view_c, 3);
931         put_array(fout,  view_p, 3);
932
933         /* Write the game simulation state. */
934
935         put_file_state(fout, &file);
936
937         return 1;
938     }
939     return 0;
940 }
941
942 int get_game_state(FILE *fin)
943 {
944     if (game_state)
945     {
946         /* Read the view and tilt state. */
947
948         get_float(fin, &game_rx);
949         get_float(fin, &game_rz);
950         get_array(fin,  view_c, 3);
951         get_array(fin,  view_p, 3);
952
953         /* Read the game simulation state. */
954
955         get_file_state(fin, &file);
956
957         return (feof(fin) ? 0 : 1);
958     }
959     return 0;
960 }
961
962 /*---------------------------------------------------------------------------*/