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