Applied my "bidirectional dt scaling" patch. Accordingly, the game can
[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 "image.h"
25 #include "audio.h"
26 #include "solid_gl.h"
27 #include "config.h"
28 #include "binary.h"
29 #include "level.h"
30
31 /*---------------------------------------------------------------------------*/
32
33 static int game_state = 0;
34
35 static struct s_file file;
36 static struct s_file back;
37
38 static float clock      = 0.f;          /* Clock time                        */
39 static int   clock_down = 1;            /* Clock go up or down?              */
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   coins  = 0;                /* Collected coins                   */
60 static int   goal_c = 0;                /* Goal coins remaining (0 = open)   */
61 static float goal_k = 0;                /* Goal animation                    */
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             */
68 static int   ball_b = 0;                /* Is the ball a bonus ball?         */
69
70 static int   grow = 0;                  /* Should the ball be changing size? */
71 static float grow_orig = 0;             /* the original ball size            */
72 static float grow_goal = 0;             /* how big or small to get!          */
73 static float grow_t = 0.0;              /* timer for the ball to grow...     */
74 static float grow_strt = 0;             /* starting value for growth         */
75 static int   got_orig = 0;              /* Do we know original ball size?    */
76
77 #define GROW_TIME  0.5f                 /* sec for the ball to get to size.  */
78 #define GROW_BIG   1.5f                 /* large factor                      */
79 #define GROW_SMALL 0.5f                 /* small factor                      */
80
81 /*---------------------------------------------------------------------------*/
82
83 static void grow_set(const struct s_file *fp, int type)
84 {
85     if (!got_orig)
86     {
87         grow_orig = fp->uv->r;
88         grow_goal = grow_orig;
89         grow_strt = grow_orig;
90         got_orig  = 1;
91     }
92
93     switch (type)
94     {
95     case ITEM_SHRINK:
96         audio_play(AUD_SHRINK, 1.f);
97
98         if (grow_goal == grow_orig * GROW_SMALL)
99             return;
100         else if (grow_goal == grow_orig * GROW_BIG)
101         {
102             grow_goal = grow_orig;
103             grow = 1;
104         }
105         else
106         {
107             grow_goal = grow_orig * GROW_SMALL;
108             grow = 1;
109         }
110
111         break;
112
113     case ITEM_GROW:
114         audio_play(AUD_GROW, 1.f);
115
116         if (grow_goal == grow_orig * GROW_BIG)
117             return;
118         else if (grow_goal == grow_orig * GROW_SMALL)
119         {
120             grow = 1;
121             grow_goal = grow_orig;
122         }
123         else
124         {
125             grow_goal = grow_orig * GROW_BIG;
126             grow = 1;
127         }
128
129         break;
130
131     default:
132         break;
133     }
134
135     if (grow)
136     {
137         grow_t = 0.0;
138         grow_strt = fp->uv->r;
139     }
140 }
141
142 static void grow_ball(const struct s_file *fp, float dt)
143 {
144     float dr;
145
146     /* Calculate new size based on how long since you touched the coin... */
147
148     grow_t += dt;
149
150     if (grow_t >= GROW_TIME)
151     {
152         grow = 0;
153         grow_t = GROW_TIME;
154     }
155
156     dr = grow_strt + ((grow_goal-grow_strt) * (1.0f / (GROW_TIME / grow_t)));
157
158     /* No sinking through the floor! Keeps ball's bottom constant. */
159     fp->uv->p[1] += (dr - fp->uv->r); 
160     fp->uv->r = dr;
161 }
162
163 static void view_init(void)
164 {
165     view_a  = 0.f;
166     view_ry = 0.f;
167
168     view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
169     view_dp  = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
170     view_dc  = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
171     view_dz  = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
172     view_k   = 1.0f;
173
174     view_c[0] = 0.f;
175     view_c[1] = view_dc;
176     view_c[2] = 0.f;
177
178     view_p[0] =     0.f;
179     view_p[1] = view_dp;
180     view_p[2] = view_dz;
181
182     view_e[0][0] = 1.f;
183     view_e[0][1] = 0.f;
184     view_e[0][2] = 0.f;
185     view_e[1][0] = 0.f;
186     view_e[1][1] = 1.f;
187     view_e[1][2] = 0.f;
188     view_e[2][0] = 0.f;
189     view_e[2][1] = 0.f;
190     view_e[2][2] = 1.f;
191 }
192
193 int game_init(const struct level *level, int t, int g)
194 {
195     clock      = (float) t / 100.f;
196     clock_down = (t > 0);
197     coins      = 0;
198
199     if (game_state)
200         game_free();
201
202     if (!sol_load_gl(&file, config_data(level->file),
203                      config_get_d(CONFIG_TEXTURES),
204                      config_get_d(CONFIG_SHADOW)))
205         return (game_state = 0);
206
207     game_state = 1;
208
209     game_ix = 0.f;
210     game_iz = 0.f;
211     game_rx = 0.f;
212     game_rz = 0.f;
213
214     /* Initialize jump and goal states. */
215
216     jump_e = 1;
217     jump_b = 0;
218
219     goal_c = g;
220     goal_k = (g == 0) ? 1.0f : 0.0f;
221
222     /* Initialise the level, background, particles, fade, and view. */
223
224     fade_k =  1.0f;
225     fade_d = -2.0f;
226
227     part_reset(GOAL_HEIGHT);
228     view_init();
229     back_init(level->grad, config_get_d(CONFIG_GEOMETRY));
230
231     sol_load_gl(&back, config_data(level->back),
232                 config_get_d(CONFIG_TEXTURES), 0);
233
234     /* Initialize ball size tracking... */
235
236     got_orig = 0;
237     grow = 0;
238
239     return game_state;
240 }
241
242 void game_free(void)
243 {
244     if (game_state)
245     {
246         sol_free_gl(&file);
247         sol_free_gl(&back);
248         back_free();
249     }
250     game_state = 0;
251 }
252
253 /*---------------------------------------------------------------------------*/
254
255 int curr_clock(void)
256 {
257     return (int) (clock * 100.f);
258 }
259
260 int curr_coins(void)
261 {
262     return coins;
263 }
264
265 int curr_goal(void)
266 {
267     return goal_c;
268 }
269
270 /*---------------------------------------------------------------------------*/
271
272 static void game_draw_balls(const struct s_file *fp)
273 {
274     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
275     float M[16];
276
277     m_basis(M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
278
279     glPushMatrix();
280     {
281         glTranslatef(fp->uv[0].p[0],
282                      fp->uv[0].p[1] + BALL_FUDGE,
283                      fp->uv[0].p[2]);
284         glMultMatrixf(M);
285         glScalef(fp->uv[0].r,
286                  fp->uv[0].r,
287                  fp->uv[0].r);
288
289         glColor4fv(c);
290
291         ball_draw(ball_b);
292     }
293     glPopMatrix();
294 }
295
296 static void game_draw_items(const struct s_file *fp)
297 {
298     float r = 360.f * SDL_GetTicks() / 1000.f;
299     int hi;
300
301     /* FIXME:  Draw items of different types in one pass? */
302
303     item_push(ITEM_COIN);
304     {
305         for (hi = 0; hi < fp->hc; hi++)
306
307             if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
308             {
309                 glPushMatrix();
310                 {
311                     glTranslatef(fp->hv[hi].p[0],
312                                  fp->hv[hi].p[1],
313                                  fp->hv[hi].p[2]);
314                     glRotatef(r, 0.0f, 1.0f, 0.0f);
315                     item_draw(&fp->hv[hi], r);
316                 }
317                 glPopMatrix();
318             }
319     }
320     item_pull();
321
322     item_push(ITEM_SHRINK);
323     {
324         for (hi = 0; hi < fp->hc; hi++)
325
326             if (fp->hv[hi].t == ITEM_SHRINK)
327             {
328                 glPushMatrix();
329                 {
330                     glTranslatef(fp->hv[hi].p[0],
331                                  fp->hv[hi].p[1],
332                                  fp->hv[hi].p[2]);
333                     glRotatef(r, 0.0f, 1.0f, 0.0f);
334                     item_draw(&fp->hv[hi], r);
335                 }
336                 glPopMatrix();
337             }
338     }
339     item_pull();
340
341     item_push(ITEM_GROW);
342     {
343         for (hi = 0; hi < fp->hc; hi++)
344
345             if (fp->hv[hi].t == ITEM_GROW)
346             {
347                 glPushMatrix();
348                 {
349                     glTranslatef(fp->hv[hi].p[0],
350                                  fp->hv[hi].p[1],
351                                  fp->hv[hi].p[2]);
352                     glRotatef(r, 0.0f, 1.0f, 0.0f);
353                     item_draw(&fp->hv[hi], r);
354                 }
355                 glPopMatrix();
356             }
357     }
358     item_pull();
359 }
360
361 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
362 {
363     int zi;
364
365     if (goal_c == 0)
366         for (zi = 0; zi < fp->zc; zi++)
367         {
368             glPushMatrix();
369             {
370                 glTranslatef(fp->zv[zi].p[0],
371                              fp->zv[zi].p[1],
372                              fp->zv[zi].p[2]);
373
374                 part_draw_goal(rx, ry, fp->zv[zi].r, goal_k, fp->zv[zi].c);
375
376                 glScalef(fp->zv[zi].r, goal_k, fp->zv[zi].r);
377                 goal_draw();
378             }
379             glPopMatrix();
380         }
381 }
382
383 static void game_draw_jumps(const struct s_file *fp)
384 {
385     int ji;
386
387     for (ji = 0; ji < fp->jc; ji++)
388     {
389         glPushMatrix();
390         {
391             glTranslatef(fp->jv[ji].p[0],
392                          fp->jv[ji].p[1],
393                          fp->jv[ji].p[2]);
394
395             glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
396             jump_draw(!jump_e);
397         }
398         glPopMatrix();
399     }
400 }
401
402 static void game_draw_swchs(const struct s_file *fp)
403 {
404     int xi;
405
406     for (xi = 0; xi < fp->xc; xi++)
407     {
408         if (fp->xv[xi].i)
409             continue;
410
411         glPushMatrix();
412         {
413             glTranslatef(fp->xv[xi].p[0],
414                          fp->xv[xi].p[1],
415                          fp->xv[xi].p[2]);
416
417             glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
418             swch_draw(fp->xv[xi].f, fp->xv[xi].e);
419         }
420         glPopMatrix();
421     }
422 }
423
424 /*---------------------------------------------------------------------------*/
425
426 static void game_refl_all(int s)
427 {
428     const float *ball_p = file.uv->p;
429
430     glPushMatrix();
431     {
432         /* Rotate the environment about the position of the ball. */
433
434         glTranslatef(+ball_p[0], +ball_p[1], +ball_p[2]);
435         glRotatef(-game_rz, view_e[2][0], view_e[2][1], view_e[2][2]);
436         glRotatef(-game_rx, view_e[0][0], view_e[0][1], view_e[0][2]);
437         glTranslatef(-ball_p[0], -ball_p[1], -ball_p[2]);
438
439         /* Draw the floor. */
440
441         sol_refl(&file);
442     }
443     glPopMatrix();
444 }
445
446 /*---------------------------------------------------------------------------*/
447
448 static void game_draw_light(void)
449 {
450     const float light_p[2][4] = {
451         { -8.0f, +32.0f, -8.0f, 1.0f },
452         { +8.0f, +32.0f, +8.0f, 1.0f },
453     };
454     const float light_c[2][4] = {
455         { 1.0f, 0.8f, 0.8f, 1.0f },
456         { 0.8f, 1.0f, 0.8f, 1.0f },
457     };
458
459     /* Configure the lighting. */
460
461     glEnable(GL_LIGHT0);
462     glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
463     glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_c[0]);
464     glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
465
466     glEnable(GL_LIGHT1);
467     glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
468     glLightfv(GL_LIGHT1, GL_DIFFUSE,  light_c[1]);
469     glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
470 }
471
472 static void game_draw_back(int pose, int d, const float p[3])
473 {
474     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
475     float t = SDL_GetTicks() / 1000.f + 120.0f;
476
477     glPushMatrix();
478     {
479         if (d < 0)
480         {
481             glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
482             glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
483         }
484
485         glTranslatef(p[0], p[1], p[2]);
486         glColor4fv(c);
487
488         if (config_get_d(CONFIG_BACKGROUND))
489         {
490             /* Draw all background layers back to front. */
491
492             sol_back(&back, BACK_DIST, FAR_DIST, t);
493             back_draw(0);
494             sol_back(&back, 0, BACK_DIST, t);
495
496             /* Draw all foreground geometry in the background file. */
497
498             sol_draw(&back);
499         }
500         else back_draw(0);
501     }
502     glPopMatrix();
503 }
504
505 static void game_draw_fore(int pose, float rx, float ry, int d, const float p[3])
506 {
507     const float *ball_p = file.uv->p;
508     const float  ball_r = file.uv->r;
509
510     glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT);
511     {
512         glPushMatrix();
513         {
514             /* Rotate the environment about the position of the ball. */
515
516             glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
517             glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
518             glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
519             glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
520
521             if (d < 0)
522             {
523                 GLdouble e[4];
524
525                 e[0] = +0;
526                 e[1] = +1;
527                 e[2] = +0;
528                 e[3] = -0.00001;
529
530                 glEnable(GL_CLIP_PLANE0);
531                 glClipPlane(GL_CLIP_PLANE0, e);
532             }
533
534             /* Draw the floor. */
535
536             sol_draw(&file);
537
538             if (config_get_d(CONFIG_SHADOW))
539             {
540                 shad_draw_set(ball_p, ball_r);
541                 sol_shad(&file);
542                 shad_draw_clr();
543             }
544
545             /* Draw the game elements. */
546
547             glEnable(GL_BLEND);
548             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
549
550             if (pose == 0)
551             {
552                 part_draw_coin(-rx * d, -ry);
553                 game_draw_items(&file);
554                 game_draw_balls(&file);
555             }
556             game_draw_goals(&file, -rx * d, -ry);
557             game_draw_jumps(&file);
558             game_draw_swchs(&file);
559
560             glDisable(GL_CLIP_PLANE0);
561         }
562         glPopMatrix();
563     }
564     glPopAttrib();
565 }
566
567 void game_draw(int pose, float st)
568 {
569     float fov = view_fov;
570
571     if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
572
573     if (game_state)
574     {
575         config_push_persp(fov, 0.1f, FAR_DIST);
576         glPushMatrix();
577         {
578             float v[3], rx, ry;
579             float pup[3];
580             float pdn[3];
581
582             v_cpy(pup, view_p);
583             v_cpy(pdn, view_p);
584             pdn[1] = -pdn[1];
585
586             /* Compute and apply the view. */
587
588             v_sub(v, view_c, view_p);
589
590             rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
591             ry = V_DEG(fatan2f(+v[0], -v[2])) + st;
592
593             glTranslatef(0.f, 0.f, -v_len(v));
594             glRotatef(rx, 1.f, 0.f, 0.f);
595             glRotatef(ry, 0.f, 1.f, 0.f);
596             glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
597
598             if (config_get_d(CONFIG_REFLECTION))
599             {
600                 /* Draw the mirror only into the stencil buffer. */
601
602                 glDisable(GL_DEPTH_TEST);
603                 glEnable(GL_STENCIL_TEST);
604                 glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
605                 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
606                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
607
608                 game_refl_all(0);
609
610                 /* Draw the scene reflected into color and depth buffers. */
611
612                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
613                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
614                 glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
615                 glEnable(GL_DEPTH_TEST);
616
617                 glFrontFace(GL_CW);
618                 glPushMatrix();
619                 {
620                     glScalef(+1.f, -1.f, +1.f);
621
622                     game_draw_light();
623                     game_draw_back(pose,         -1, pdn);
624                     game_draw_fore(pose, rx, ry, -1, pdn);
625                 }
626                 glPopMatrix();
627                 glFrontFace(GL_CCW);
628
629                 glDisable(GL_STENCIL_TEST);
630             }
631
632             /* Draw the scene normally. */
633
634             game_draw_light();
635             game_refl_all(pose ? 0 : config_get_d(CONFIG_SHADOW));
636             game_draw_back(pose,         +1, pup);
637             game_draw_fore(pose, rx, ry, +1, pup);
638         }
639         glPopMatrix();
640         config_pop_matrix();
641
642         /* Draw the fade overlay. */
643
644         fade_draw(fade_k);
645     }
646 }
647
648 /*---------------------------------------------------------------------------*/
649
650 static void game_update_grav(float h[3], const float g[3])
651 {
652     struct s_file *fp = &file;
653
654     float x[3];
655     float y[3] = { 0.f, 1.f, 0.f };
656     float z[3];
657     float X[16];
658     float Z[16];
659     float M[16];
660
661     /* Compute the gravity vector from the given world rotations. */
662
663     v_sub(z, view_p, fp->uv->p);
664     v_crs(x, y, z);
665     v_crs(z, x, y);
666     v_nrm(x, x);
667     v_nrm(z, z);
668
669     m_rot (Z, z, V_RAD(game_rz));
670     m_rot (X, x, V_RAD(game_rx));
671     m_mult(M, Z, X);
672     m_vxfm(h, M, g);
673 }
674
675 static void game_update_view(float dt)
676 {
677     float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
678     float dx = view_ry * dt * 5.0f;
679     float k;
680
681     view_a += view_ry * dt * 90.f;
682
683     /* Center the view about the ball. */
684
685     v_cpy(view_c, file.uv->p);
686     v_inv(view_v, file.uv->v);
687
688     switch (config_get_d(CONFIG_CAMERA))
689     {
690     case 1: /* Camera 1:  Viewpoint chases the ball position. */
691
692         v_sub(view_e[2], view_p, view_c);
693         break;
694
695     case 2: /* Camera 2: View vector is given by view angle. */
696
697         view_e[2][0] = fsinf(V_RAD(view_a));
698         view_e[2][1] = 0.f;
699         view_e[2][2] = fcosf(V_RAD(view_a));
700
701         dx = 0.0f;
702
703         break;
704
705     default: /* Default: View vector approaches the ball velocity vector. */
706
707         k = v_dot(view_v, view_v);
708
709         v_sub(view_e[2], view_p, view_c);
710         v_mad(view_e[2], view_e[2], view_v, k * dt / 4);
711
712         break;
713     }
714
715     /* Orthonormalize the basis of the view in its new position. */
716
717     v_crs(view_e[0], view_e[1], view_e[2]);
718     v_crs(view_e[2], view_e[0], view_e[1]);
719     v_nrm(view_e[0], view_e[0]);
720     v_nrm(view_e[2], view_e[2]);
721
722     /* Compute the new view position. */
723
724     k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
725
726     view_k = view_k + (k - view_k) * dt;
727
728     if (view_k < 0.5) view_k = 0.5;
729
730     v_cpy(view_p, file.uv->p);
731     v_mad(view_p, view_p, view_e[0], dx      * view_k);
732     v_mad(view_p, view_p, view_e[1], view_dp * view_k);
733     v_mad(view_p, view_p, view_e[2], view_dz * view_k);
734
735     /* Compute the new view center. */
736
737     v_cpy(view_c, file.uv->p);
738     v_mad(view_c, view_c, view_e[1], dc);
739
740     /* Note the current view angle. */
741
742     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
743 }
744
745 static void game_update_time(float dt, int b)
746 {
747     if (goal_c == 0 && goal_k < 1.0f)
748         goal_k += dt;
749
750    /* The ticking clock. */
751
752     if (b && clock_down)
753     {
754         if (clock < 600.f)
755             clock -= dt;
756         if (clock < 0.f)
757             clock = 0.f;
758     }
759     else if (b)
760     {
761         clock += dt;
762     }
763 }
764
765 static int game_update_state(int *state_value)
766 {
767     struct s_file *fp = &file;
768     struct s_goal *zp;
769     struct s_item *hp;
770
771     float p[3];
772     float c[3];
773
774     int bt = state_value != NULL;
775
776     /* Test for an item. */
777     if (bt && (hp = sol_item_test(fp, p, COIN_RADIUS)))
778     {
779         int sound = AUD_COIN;
780
781         item_color(hp, c);
782         part_burst(p, c);
783
784         grow_set(fp, hp->t);
785
786         if (hp->t == ITEM_COIN)
787         {
788             coins += hp->n;
789
790             /* Check for goal open. */
791             if (goal_c > 0)
792             {
793                 goal_c -= hp->n;
794                 if (goal_c <= 0)
795                 {
796                     sound = AUD_SWITCH;
797                     goal_c = 0;
798                 }
799             }
800         }
801         audio_play(sound, 1.f);
802
803         /* Reset item type. */
804         hp->t = ITEM_NONE;
805     }
806
807     /* Test for a switch. */
808     if (sol_swch_test(fp, 0))
809         audio_play(AUD_SWITCH, 1.f);
810
811     /* Test for a jump. */
812
813     if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
814     {
815         jump_b  = 1;
816         jump_e  = 0;
817         jump_dt = 0.f;
818
819         audio_play(AUD_JUMP, 1.f);
820     }
821     if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
822         jump_e = 1;
823
824     /* Test for a goal. */
825
826     if (bt && goal_c == 0 && (zp = sol_goal_test(fp, p, 0)))
827     {
828         *state_value = zp->s;
829         audio_play(AUD_GOAL, 1.0f);
830         return zp->c ? GAME_SPEC : GAME_GOAL;
831     }
832
833     /* Test for time-out. */
834
835     if (bt && clock_down && clock <= 0.f)
836     {
837         audio_play(AUD_TIME, 1.0f);
838         return GAME_TIME;
839     }
840
841     /* Test for fall-out. */
842
843     if (bt && fp->uv[0].p[1] < fp->vv[0].p[1])
844     {
845         audio_play(AUD_FALL, 1.0f);
846         return GAME_FALL;
847     }
848
849     return GAME_NONE;
850 }
851
852 /*
853  * By performing multiple physics updates or skipping an update for  a  given
854  * graphics update, we get away  with  higher  quality  physics  with  little
855  * impact on overall performance.  Toward this end, we establish  a  baseline
856  * maximum and minimum physics time step.  If the measured frame time exceeds
857  * the maximum time step, we cut the time step in half, and do  two  updates.
858  * If THIS time step exceeds the maximum, we do four updates.  And so on.  On
859  * the other hand, if the frame time is lower than the minimum time step,  we
860  * skip an update and do so until the accumulated frame time has reached this
861  * minimum.  In this way, the physics system is allowed to  seek  an  optimal
862  * update rate independent of, yet in integral sync with, the graphics  frame
863  * rate.
864  */
865
866 int game_step(const float g[3], float dt, int *state_value)
867 {
868     struct s_file *fp = &file;
869
870     float h[3];
871     float d = 0.f;
872     float b = 0.f;
873     float t;
874     int i, n = 1;
875
876     if (game_state)
877     {
878         t = dt;
879
880         /* Smooth jittery or discontinuous input. */
881
882         if (t < RESPONSE)
883         {
884             game_rx += (game_ix - game_rx) * t / RESPONSE;
885             game_rz += (game_iz - game_rz) * t / RESPONSE;
886         }
887         else
888         {
889             game_rx = game_ix;
890             game_rz = game_iz;
891         }
892
893         if (grow)
894             grow_ball(fp, dt);
895
896         game_update_grav(h, g);
897         part_step(h, t);
898
899         if (jump_b)
900         {
901             jump_dt += t;
902
903             /* Handle a jump. */
904
905             if (0.5 < jump_dt)
906             {
907                 fp->uv[0].p[0] = jump_p[0];
908                 fp->uv[0].p[1] = jump_p[1];
909                 fp->uv[0].p[2] = jump_p[2];
910             }
911             if (1.f < jump_dt)
912                 jump_b = 0;
913         }
914         else
915         {
916             static float accumulated_t = 0.f;
917
918             /* Run the sim. */
919
920             accumulated_t += t;
921
922             if (accumulated_t < MIN_DT)
923             {
924                 n = 0;
925             }
926             else
927             {
928                 t = accumulated_t;
929                 accumulated_t = 0.f;
930             }
931
932             while (t > MAX_DT && n < MAX_DN)
933             {
934                 t /= 2;
935                 n *= 2;
936             }
937
938             for (i = 0; i < n; i++)
939                 if (b < (d = sol_step(fp, h, t, 0, NULL)))
940                     b = d;
941
942             /* Mix the sound of a ball bounce. */
943
944             if (b > 0.5)
945             {
946                 float k = (b - 0.5f) * 2.0f;
947                 
948                 if (got_orig)
949                 {
950                     if      (fp->uv->r > grow_orig) audio_play(AUD_BUMPL, k);
951                     else if (fp->uv->r < grow_orig) audio_play(AUD_BUMPS, k);
952                     else                            audio_play(AUD_BUMPM, k);
953                 }
954                 else audio_play(AUD_BUMPM, k);
955             }
956         }
957
958         game_step_fade(dt);
959         game_update_view(dt);
960         game_update_time(dt, state_value != NULL);
961
962         return game_update_state(state_value);
963     }
964     return GAME_NONE;
965 }
966
967 /*---------------------------------------------------------------------------*/
968
969 void game_no_aa(void)
970 {
971     float max = game_ix * game_ix + game_iz * game_iz;
972     if (max > ANGLE_BOUND * ANGLE_BOUND)
973     {
974         max = ANGLE_BOUND / sqrt(max);
975         game_ix *= max;
976         game_iz *= max;
977     }
978 }
979
980 void game_set_x(int k)
981 {
982     game_ix = -(ANGLE_BOUND) * k / JOY_MAX;
983 #if NO_AA
984     game_no_aa();
985 #endif
986 }
987
988 void game_set_z(int k)
989 {
990     game_iz = +ANGLE_BOUND * k / JOY_MAX;
991 #if NO_AA
992     game_no_aa();
993 #endif
994 }
995
996 void game_set_pos(int x, int y)
997 {
998     game_ix += 40.f * y / config_get_d(CONFIG_MOUSE_SENSE);
999     game_iz += 40.f * x / config_get_d(CONFIG_MOUSE_SENSE);
1000
1001 #if NO_AA
1002     game_no_aa();
1003 #else
1004     if (game_ix > +ANGLE_BOUND) game_ix = +ANGLE_BOUND;
1005     if (game_ix < -ANGLE_BOUND) game_ix = -ANGLE_BOUND;
1006     if (game_iz > +ANGLE_BOUND) game_iz = +ANGLE_BOUND;
1007     if (game_iz < -ANGLE_BOUND) game_iz = -ANGLE_BOUND;
1008 #endif
1009 }
1010
1011 void game_set_rot(float r)
1012 {
1013     view_ry = r;
1014 }
1015
1016 /*---------------------------------------------------------------------------*/
1017
1018 void game_set_fly(float k)
1019 {
1020     struct s_file *fp = &file;
1021
1022     float  x[3] = { 1.f, 0.f, 0.f };
1023     float  y[3] = { 0.f, 1.f, 0.f };
1024     float  z[3] = { 0.f, 0.f, 1.f };
1025     float c0[3] = { 0.f, 0.f, 0.f };
1026     float p0[3] = { 0.f, 0.f, 0.f };
1027     float c1[3] = { 0.f, 0.f, 0.f };
1028     float p1[3] = { 0.f, 0.f, 0.f };
1029     float  v[3];
1030
1031     z[0] = fsinf(V_RAD(view_a));
1032     z[2] = fcosf(V_RAD(view_a));
1033
1034     v_cpy(view_e[0], x);
1035     v_cpy(view_e[1], y);
1036     v_cpy(view_e[2], z);
1037
1038     /* k = 0.0 view is at the ball. */
1039
1040     if (fp->uc > 0)
1041     {
1042         v_cpy(c0, fp->uv[0].p);
1043         v_cpy(p0, fp->uv[0].p);
1044     }
1045
1046     v_mad(p0, p0, y, view_dp);
1047     v_mad(p0, p0, z, view_dz);
1048     v_mad(c0, c0, y, view_dc);
1049
1050     /* k = +1.0 view is s_view 0 */
1051
1052     if (k >= 0 && fp->wc > 0)
1053     {
1054         v_cpy(p1, fp->wv[0].p);
1055         v_cpy(c1, fp->wv[0].q);
1056     }
1057
1058     /* k = -1.0 view is s_view 1 */
1059
1060     if (k <= 0 && fp->wc > 1)
1061     {
1062         v_cpy(p1, fp->wv[1].p);
1063         v_cpy(c1, fp->wv[1].q);
1064     }
1065
1066     /* Interpolate the views. */
1067
1068     v_sub(v, p1, p0);
1069     v_mad(view_p, p0, v, k * k);
1070
1071     v_sub(v, c1, c0);
1072     v_mad(view_c, c0, v, k * k);
1073
1074     /* Orthonormalize the view basis. */
1075
1076     v_sub(view_e[2], view_p, view_c);
1077     v_crs(view_e[0], view_e[1], view_e[2]);
1078     v_crs(view_e[2], view_e[0], view_e[1]);
1079     v_nrm(view_e[0], view_e[0]);
1080     v_nrm(view_e[2], view_e[2]);
1081 }
1082
1083 void game_look(float phi, float theta)
1084 {
1085     view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
1086     view_c[1] = view_p[1] +                       fsinf(V_RAD(phi));
1087     view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
1088 }
1089
1090 /*---------------------------------------------------------------------------*/
1091
1092 void game_kill_fade(void)
1093 {
1094     fade_k = 0.0f;
1095     fade_d = 0.0f;
1096 }
1097
1098 void game_step_fade(float dt)
1099 {
1100     if ((fade_k < 1.0f && fade_d > 0.0f) ||
1101         (fade_k > 0.0f && fade_d < 0.0f))
1102         fade_k += fade_d * dt;
1103
1104     if (fade_k < 0.0f)
1105     {
1106         fade_k = 0.0f;
1107         fade_d = 0.0f;
1108     }
1109     if (fade_k > 1.0f)
1110     {
1111         fade_k = 1.0f;
1112         fade_d = 0.0f;
1113     }
1114 }
1115
1116 void game_fade(float d)
1117 {
1118     fade_d = d;
1119 }
1120
1121 /*---------------------------------------------------------------------------*/
1122
1123 int put_game_state(FILE *fout)
1124 {
1125     if (game_state)
1126     {
1127         /* Write the view and tilt state. */
1128
1129         put_float(fout, &game_rx);
1130         put_float(fout, &game_rz);
1131         put_array(fout,  view_c, 3);
1132         put_array(fout,  view_p, 3);
1133
1134         /* Write the game simulation state. */
1135
1136         put_file_state(fout, &file);
1137
1138         return 1;
1139     }
1140     return 0;
1141 }
1142
1143 int get_game_state(FILE *fin)
1144 {
1145     if (game_state)
1146     {
1147         /* Read the view and tilt state. */
1148
1149         get_float(fin, &game_rx);
1150         get_float(fin, &game_rz);
1151         get_array(fin,  view_c, 3);
1152         get_array(fin,  view_p, 3);
1153
1154         /* Read the game simulation state. */
1155
1156         get_file_state(fin, &file);
1157
1158         return (feof(fin) ? 0 : 1);
1159     }
1160     return 0;
1161 }
1162
1163 /*---------------------------------------------------------------------------*/