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