Remove duplicate macro definition.
[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 size)
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 (size == 50)
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 (size == 150)
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     /* Get the initial orientation angle */
156     if (file.uc > 0)
157         view_a  = file.uv->a - 90.f; /* angle is in the sol */
158     else
159         view_a  = 0.f; /* default is north :) */
160
161     view_ry = 0.f;
162
163     view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
164     view_dp  = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
165     view_dc  = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
166     view_dz  = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
167     view_k   = 1.0f;
168
169     view_c[0] = 0.f;
170     view_c[1] = view_dc;
171     view_c[2] = 0.f;
172
173     view_p[0] =     0.f;
174     view_p[1] = view_dp;
175     view_p[2] = view_dz;
176
177     view_e[0][0] = 1.f;
178     view_e[0][1] = 0.f;
179     view_e[0][2] = 0.f;
180     view_e[1][0] = 0.f;
181     view_e[1][1] = 1.f;
182     view_e[1][2] = 0.f;
183     view_e[2][0] = 0.f;
184     view_e[2][1] = 0.f;
185     view_e[2][2] = 1.f;
186 }
187
188 int game_init(const struct level *level, int t, int g)
189 {
190     clock      = (float) t / 100.f;
191     clock_down = (t > 0);
192     coins      = 0;
193
194     if (game_state)
195         game_free();
196
197     if (!sol_load_gl(&file, level->file, config_get_d(CONFIG_TEXTURES),
198                      config_get_d(CONFIG_SHADOW)))
199         return (game_state = 0);
200
201     game_state = 1;
202
203     game_ix = 0.f;
204     game_iz = 0.f;
205     game_rx = 0.f;
206     game_rz = 0.f;
207
208     drawball = 1;
209
210     /* Initialize jump and goal states. */
211
212     jump_e = 1;
213     jump_b = 0;
214
215     goal_c = g;
216     goal_k = (g == 0) ? 1.0f : 0.0f;
217
218     ball_b = level->is_bonus;
219
220     /* Initialise the level, background, particles, fade, and view. */
221
222     fade_k =  1.0f;
223     fade_d = -2.0f;
224
225     part_reset(GOAL_HEIGHT);
226     view_init();
227     back_init(level->grad, config_get_d(CONFIG_GEOMETRY));
228
229     sol_load_gl(&back, config_data(level->back),
230                 config_get_d(CONFIG_TEXTURES), 0);
231
232     /* Initialize ball size tracking... */
233
234     got_orig = 0;
235
236     return game_state;
237 }
238
239 void game_free(void)
240 {
241     if (game_state)
242     {
243         sol_free_gl(&file);
244         sol_free_gl(&back);
245         back_free();
246     }
247     game_state = 0;
248 }
249
250 /*---------------------------------------------------------------------------*/
251
252 int curr_clock(void)
253 {
254     return (int) (clock * 100.f);
255 }
256
257 int curr_coins(void)
258 {
259     return coins;
260 }
261
262 int curr_goal(void)
263 {
264     return goal_c;
265 }
266
267 /*---------------------------------------------------------------------------*/
268
269 static void game_draw_balls(const struct s_file *fp)
270 {
271     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
272     float M[16];
273
274     m_basis(M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
275
276     glPushMatrix();
277     {
278         glTranslatef(fp->uv[0].p[0],
279                      fp->uv[0].p[1] + BALL_FUDGE,
280                      fp->uv[0].p[2]);
281         glMultMatrixf(M);
282         glScalef(fp->uv[0].r,
283                  fp->uv[0].r,
284                  fp->uv[0].r);
285
286         glColor4fv(c);
287
288         ball_draw(ball_b);
289     }
290     glPopMatrix();
291 }
292
293 static void game_draw_coins(const struct s_file *fp)
294 {
295     float r = 360.f * SDL_GetTicks() / 1000.f;
296     int ci;
297
298     coin_push();
299     coin_push_text(0); /* Regular coins. */
300     {
301         for (ci = 0; ci < fp->cc; ci++)
302
303             if (fp->cv[ci].n > 0 && fp->cv[ci].n < 50)
304             {
305                 glPushMatrix();
306                 {
307                     glTranslatef(fp->cv[ci].p[0],
308                                  fp->cv[ci].p[1],
309                                  fp->cv[ci].p[2]);
310                     glRotatef(r, 0.0f, 1.0f, 0.0f);
311                     coin_draw(fp->cv[ci].n, r);
312                 }
313                 glPopMatrix();
314             }
315     }
316     coin_pull();
317
318     /* FIXME: there has got to be a better way than three seperate loops,  once
319      * for each texture, but someone else is going to have to do it! */
320
321     coin_push();
322     coin_push_text(50); /* Any shrink coins? */
323     {
324         for (ci = 0; ci < fp->cc; ci++)
325
326             if (fp->cv[ci].n == 50)
327             {
328                 glPushMatrix();
329                 {
330                     glTranslatef(fp->cv[ci].p[0],
331                                  fp->cv[ci].p[1],
332                                  fp->cv[ci].p[2]);
333                     glRotatef(r, 0.0f, 1.0f, 0.0f);
334                     coin_draw(fp->cv[ci].n, r);
335                 }
336                 glPopMatrix();
337             }
338     }
339     coin_pull();
340
341     coin_push();
342     coin_push_text(150); /* Any grow coins? */
343     {
344         for (ci = 0; ci < fp->cc; ci++)
345
346             if (fp->cv[ci].n == 150)
347             {
348                 glPushMatrix();
349                 {
350                     glTranslatef(fp->cv[ci].p[0],
351                                  fp->cv[ci].p[1],
352                                  fp->cv[ci].p[2]);
353                     glRotatef(r, 0.0f, 1.0f, 0.0f);
354                     coin_draw(fp->cv[ci].n, r);
355                 }
356                 glPopMatrix();
357             }
358     }
359     coin_pull();
360 }
361
362 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
363 {
364     int zi;
365
366     if (goal_c == 0)
367         for (zi = 0; zi < fp->zc; zi++)
368         {
369             glPushMatrix();
370             {
371                 glTranslatef(fp->zv[zi].p[0],
372                              fp->zv[zi].p[1],
373                              fp->zv[zi].p[2]);
374
375                 part_draw_goal(rx, ry, fp->zv[zi].r, goal_k, fp->zv[zi].c);
376
377                 glScalef(fp->zv[zi].r, goal_k, fp->zv[zi].r);
378                 goal_draw();
379             }
380             glPopMatrix();
381         }
382 }
383
384 static void game_draw_jumps(const struct s_file *fp)
385 {
386     int ji;
387
388     for (ji = 0; ji < fp->jc; ji++)
389     {
390         glPushMatrix();
391         {
392             glTranslatef(fp->jv[ji].p[0],
393                          fp->jv[ji].p[1],
394                          fp->jv[ji].p[2]);
395
396             glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
397             jump_draw(!jump_e);
398         }
399         glPopMatrix();
400     }
401 }
402
403 static void game_draw_swchs(const struct s_file *fp)
404 {
405     int xi;
406
407     for (xi = 0; xi < fp->xc; xi++)
408     {
409         if (fp->xv[xi].i)
410             continue;
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) && drawball)
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_coins(&file);
554                 if (drawball)
555                     game_draw_balls(&file);
556             }
557             game_draw_goals(&file, -rx * d, -ry);
558             game_draw_jumps(&file);
559             game_draw_swchs(&file);
560
561             glDisable(GL_CLIP_PLANE0);
562         }
563         glPopMatrix();
564     }
565     glPopAttrib();
566 }
567
568 void game_draw(int pose, float st)
569 {
570     float fov = view_fov;
571
572     if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
573
574     if (game_state)
575     {
576         config_push_persp(fov, 0.1f, FAR_DIST);
577         glPushMatrix();
578         {
579             float v[3], rx, ry;
580             float pup[3];
581             float pdn[3];
582
583             v_cpy(pup, view_p);
584             v_cpy(pdn, view_p);
585             pdn[1] = -pdn[1];
586
587             /* Compute and apply the view. */
588
589             v_sub(v, view_c, view_p);
590
591             rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
592             ry = V_DEG(fatan2f(+v[0], -v[2])) + st;
593
594             glTranslatef(0.f, 0.f, -v_len(v));
595             glRotatef(rx, 1.f, 0.f, 0.f);
596             glRotatef(ry, 0.f, 1.f, 0.f);
597             glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
598
599             if (config_get_d(CONFIG_REFLECTION))
600             {
601                 /* Draw the mirror only into the stencil buffer. */
602
603                 glDisable(GL_DEPTH_TEST);
604                 glEnable(GL_STENCIL_TEST);
605                 glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
606                 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
607                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
608
609                 game_refl_all(0);
610
611                 /* Draw the scene reflected into color and depth buffers. */
612
613                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
614                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
615                 glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
616                 glEnable(GL_DEPTH_TEST);
617
618                 glFrontFace(GL_CW);
619                 glPushMatrix();
620                 {
621                     glScalef(+1.f, -1.f, +1.f);
622
623                     game_draw_light();
624                     game_draw_back(pose,         -1, pdn);
625                     game_draw_fore(pose, rx, ry, -1, pdn);
626                 }
627                 glPopMatrix();
628                 glFrontFace(GL_CCW);
629
630                 glDisable(GL_STENCIL_TEST);
631             }
632
633             /* Draw the scene normally. */
634
635             game_draw_light();
636             game_refl_all(pose ? 0 : config_get_d(CONFIG_SHADOW));
637             game_draw_back(pose,         +1, pup);
638             game_draw_fore(pose, rx, ry, +1, pup);
639         }
640         glPopMatrix();
641         config_pop_matrix();
642
643         /* Draw the fade overlay. */
644
645         fade_draw(fade_k);
646     }
647 }
648
649 /*---------------------------------------------------------------------------*/
650
651 static void game_update_grav(float h[3], const float g[3])
652 {
653     struct s_file *fp = &file;
654
655     float x[3];
656     float y[3] = { 0.f, 1.f, 0.f };
657     float z[3];
658     float X[16];
659     float Z[16];
660     float M[16];
661
662     /* Compute the gravity vector from the given world rotations. */
663
664     v_sub(z, view_p, fp->uv->p);
665     v_crs(x, y, z);
666     v_crs(z, x, y);
667     v_nrm(x, x);
668     v_nrm(z, z);
669
670     m_rot (Z, z, V_RAD(game_rz));
671     m_rot (X, x, V_RAD(game_rx));
672     m_mult(M, Z, X);
673     m_vxfm(h, M, g);
674 }
675
676 static void game_update_view(float dt)
677 {
678     float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
679     float dx = view_ry * dt * 5.0f;
680     float k;
681
682     view_a += view_ry * dt * 90.f;
683
684     /* Center the view about the ball. */
685
686     v_cpy(view_c, file.uv->p);
687     v_inv(view_v, file.uv->v);
688
689     switch (config_get_d(CONFIG_CAMERA))
690     {
691     case 1: /* Camera 1:  Viewpoint chases the ball position. */
692
693         v_sub(view_e[2], view_p, view_c);
694         break;
695
696     case 2: /* Camera 2: View vector is given by view angle. */
697
698         view_e[2][0] = fsinf(V_RAD(view_a));
699         view_e[2][1] = 0.f;
700         view_e[2][2] = fcosf(V_RAD(view_a));
701
702         dx = 0.0f;
703
704         break;
705
706     default: /* Default: View vector approaches the ball velocity vector. */
707
708         k = v_dot(view_v, view_v);
709
710         v_sub(view_e[2], view_p, view_c);
711         v_mad(view_e[2], view_e[2], view_v, k * dt / 4);
712
713         break;
714     }
715
716     /* Orthonormalize the basis of the view in its new position. */
717
718     v_crs(view_e[0], view_e[1], view_e[2]);
719     v_crs(view_e[2], view_e[0], view_e[1]);
720     v_nrm(view_e[0], view_e[0]);
721     v_nrm(view_e[2], view_e[2]);
722
723     /* Compute the new view position. */
724
725     k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
726
727     view_k = view_k + (k - view_k) * dt;
728
729     if (view_k < 0.5) view_k = 0.5;
730
731     v_cpy(view_p, file.uv->p);
732     v_mad(view_p, view_p, view_e[0], dx      * view_k);
733     v_mad(view_p, view_p, view_e[1], view_dp * view_k);
734     v_mad(view_p, view_p, view_e[2], view_dz * view_k);
735
736     /* Compute the new view center. */
737
738     v_cpy(view_c, file.uv->p);
739     v_mad(view_c, view_c, view_e[1], dc);
740
741     /* Note the current view angle. */
742
743     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
744 }
745
746 static void game_update_time(float dt, int b)
747 {
748     if (goal_c == 0 && goal_k < 1.0f)
749         goal_k += dt;
750
751    /* The ticking clock. */
752
753     if (b && clock_down)
754     {
755         if (clock < 600.f)
756             clock -= dt;
757         if (clock < 0.f)
758             clock = 0.f;
759     }
760     else if (b)
761     {
762         clock += dt;
763     }
764 }
765
766 static int game_update_state(int *state_value)
767 {
768     struct s_file *fp = &file;
769     float p[3];
770     float c[3];
771     int bt = state_value != NULL;
772     int n;
773     struct s_goal *g;
774
775     /* Test for a coin grab. */
776
777     if (bt && (n = sol_coin_test(fp, p, COIN_RADIUS)) > 0)
778     {
779         coin_color(c, n);
780         part_burst(p, c);
781
782         /* Add coins if regular, change radius if not. */
783
784         if (n <= 10)
785             coins += n;
786         else
787         {
788             grow_set(fp, n);
789             n = 0;
790         }
791
792         /* Check for goal open. */
793         if (goal_c > 0)
794         {
795             goal_c = goal_c - n;
796             if (goal_c <= 0)
797             {
798                 audio_play(AUD_SWITCH, 1.f);
799                 goal_c = 0;
800             }
801             else
802                 audio_play(AUD_COIN, 1.f);
803         }
804         else
805             audio_play(AUD_COIN, 1.f);
806     }
807
808     /* Test for a switch. */
809     if (sol_swch_test(fp, 0))
810         audio_play(AUD_SWITCH, 1.f);
811
812     /* Test for a jump. */
813
814     if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
815     {
816         jump_b  = 1;
817         jump_e  = 0;
818         jump_dt = 0.f;
819
820         audio_play(AUD_JUMP, 1.f);
821     }
822     if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
823         jump_e = 1;
824
825     /* Test for a goal. */
826
827     if (bt && goal_c == 0 && (g = sol_goal_test(fp, p, 0)))
828     {
829         *state_value = g->s;
830         audio_play(AUD_GOAL, 1.0f);
831         return g->c ? GAME_SPEC : GAME_GOAL;
832     }
833
834     /* Test for time-out. */
835
836     if (bt && clock_down && clock <= 0.f)
837     {
838         const GLfloat *p = fp->uv->p;
839         const GLfloat c[5] = {1.0f, 1.0f, 0.0f, 0.0f, 1.0f};
840         part_burst(p, c);
841         part_burst(p, c+1);
842         part_burst(p, c+2);
843         part_burst(p, c);
844         part_burst(p, c+1);
845         part_burst(p, c+2);
846         drawball = 0;
847         audio_play(AUD_TIME, 1.0f);
848         return GAME_TIME;
849     }
850
851     /* Test for fall-out. */
852
853     if (bt && fp->uv[0].p[1] < fp->vv[0].p[1])
854     {
855         audio_play(AUD_FALL, 1.0f);
856         return GAME_FALL;
857     }
858
859     return GAME_NONE;
860 }
861
862 /*
863  * On  most  hardware, rendering  requires  much  more  computing power  than
864  * physics.  Since  physics takes less time  than graphics, it  make sense to
865  * detach  the physics update  time step  from the  graphics frame  rate.  By
866  * performing multiple physics updates for  each graphics update, we get away
867  * with higher quality physics with little impact on overall performance.
868  *
869  * Toward this  end, we establish a  baseline maximum physics  time step.  If
870  * the measured  frame time  exceeds this  maximum, we cut  the time  step in
871  * half, and  do two updates.  If THIS  time step exceeds the  maximum, we do
872  * four updates.  And  so on.  In this way, the physics  system is allowed to
873  * seek an optimal update rate independant of, yet in integral sync with, the
874  * graphics frame rate.
875  */
876
877 int game_step(const float g[3], float dt, int *state_value)
878 {
879     struct s_file *fp = &file;
880
881     float h[3];
882     float d = 0.f;
883     float b = 0.f;
884     float t;
885     int i, n = 1;
886
887     if (game_state)
888     {
889         t = dt;
890
891         /* Smooth jittery or discontinuous input. */
892
893         if (t < RESPONSE)
894         {
895             game_rx += (game_ix - game_rx) * t / RESPONSE;
896             game_rz += (game_iz - game_rz) * t / RESPONSE;
897         }
898         else
899         {
900             game_rx = game_ix;
901             game_rz = game_iz;
902         }
903
904         if (grow)
905             grow_ball(fp,dt);
906
907         game_update_grav(h, g);
908         part_step(h, t);
909
910         if (!drawball)
911                 /* nothing */;
912         else if (jump_b)
913         {
914             jump_dt += t;
915
916             /* Handle a jump. */
917
918             if (0.5 < jump_dt)
919             {
920                 fp->uv[0].p[0] = jump_p[0];
921                 fp->uv[0].p[1] = jump_p[1];
922                 fp->uv[0].p[2] = jump_p[2];
923             }
924             if (1.f < jump_dt)
925                 jump_b = 0;
926         }
927         else
928         {
929             /* Run the sim. */
930
931             while (t > MAX_DT && n < MAX_DN)
932             {
933                 t /= 2;
934                 n *= 2;
935             }
936
937             for (i = 0; i < n; i++)
938                 if (b < (d = sol_step(fp, h, t, 0, NULL)))
939                     b = d;
940
941             /* Mix the sound of a ball bounce. */
942
943             if (b > 0.5)
944                 audio_play(AUD_BUMP, (b - 0.5f) * 2.0f);
945         }
946
947         game_step_fade(dt);
948         game_update_view(dt);
949         game_update_time(dt, state_value != NULL);
950
951         return game_update_state(state_value);
952     }
953     return GAME_NONE;
954 }
955
956 /*---------------------------------------------------------------------------*/
957
958 void game_no_aa(void)
959 {
960     float max = game_ix * game_ix + game_iz * game_iz;
961     if (max > ANGLE_BOUND * ANGLE_BOUND)
962     {
963         max = ANGLE_BOUND / sqrt(max);
964         game_ix *= max;
965         game_iz *= max;
966     }
967 }
968
969 void game_set_x(int k)
970 {
971     game_ix = -(ANGLE_BOUND) * k / JOY_MAX;
972 #if NO_AA
973     game_no_aa();
974 #endif
975 }
976
977 void game_set_z(int k)
978 {
979     game_iz = +ANGLE_BOUND * k / JOY_MAX;
980 #if NO_AA
981     game_no_aa();
982 #endif
983 }
984
985 void game_set_pos(int x, int y)
986 {
987     game_ix += 40.f * y / config_get_d(CONFIG_MOUSE_SENSE);
988     game_iz += 40.f * x / config_get_d(CONFIG_MOUSE_SENSE);
989
990 #if NO_AA
991     game_no_aa();
992 #else
993     if (game_ix > +ANGLE_BOUND) game_ix = +ANGLE_BOUND;
994     if (game_ix < -ANGLE_BOUND) game_ix = -ANGLE_BOUND;
995     if (game_iz > +ANGLE_BOUND) game_iz = +ANGLE_BOUND;
996     if (game_iz < -ANGLE_BOUND) game_iz = -ANGLE_BOUND;
997 #endif
998 }
999
1000 void game_set_rot(float r)
1001 {
1002     view_ry = r;
1003 }
1004
1005 /*---------------------------------------------------------------------------*/
1006
1007 void game_set_fly(float k)
1008 {
1009     struct s_file *fp = &file;
1010
1011     float  x[3] = { 1.f, 0.f, 0.f };
1012     float  y[3] = { 0.f, 1.f, 0.f };
1013     float  z[3] = { 0.f, 0.f, 1.f };
1014     float c0[3] = { 0.f, 0.f, 0.f };
1015     float p0[3] = { 0.f, 0.f, 0.f };
1016     float c1[3] = { 0.f, 0.f, 0.f };
1017     float p1[3] = { 0.f, 0.f, 0.f };
1018     float  v[3];
1019
1020     z[0] = fsinf(V_RAD(view_a));
1021     z[2] = fcosf(V_RAD(view_a));
1022
1023     v_cpy(view_e[0], x);
1024     v_cpy(view_e[1], y);
1025     v_cpy(view_e[2], z);
1026
1027     /* k = 0.0 view is at the ball. */
1028
1029     if (fp->uc > 0)
1030     {
1031         v_cpy(c0, fp->uv[0].p);
1032         v_cpy(p0, fp->uv[0].p);
1033     }
1034
1035     v_mad(p0, p0, y, view_dp);
1036     v_mad(p0, p0, z, view_dz);
1037     v_mad(c0, c0, y, view_dc);
1038
1039     /* k = +1.0 view is s_view 0 */
1040
1041     if (k >= 0 && fp->wc > 0)
1042     {
1043         v_cpy(p1, fp->wv[0].p);
1044         v_cpy(c1, fp->wv[0].q);
1045     }
1046
1047     /* k = -1.0 view is s_view 1 */
1048
1049     if (k <= 0 && fp->wc > 1)
1050     {
1051         v_cpy(p1, fp->wv[1].p);
1052         v_cpy(c1, fp->wv[1].q);
1053     }
1054
1055     /* Interpolate the views. */
1056
1057     v_sub(v, p1, p0);
1058     v_mad(view_p, p0, v, k * k);
1059
1060     v_sub(v, c1, c0);
1061     v_mad(view_c, c0, v, k * k);
1062
1063     /* Orthonormalize the view basis. */
1064
1065     v_sub(view_e[2], view_p, view_c);
1066     v_crs(view_e[0], view_e[1], view_e[2]);
1067     v_crs(view_e[2], view_e[0], view_e[1]);
1068     v_nrm(view_e[0], view_e[0]);
1069     v_nrm(view_e[2], view_e[2]);
1070 }
1071
1072 void game_look(float phi, float theta)
1073 {
1074     view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
1075     view_c[1] = view_p[1] +                       fsinf(V_RAD(phi));
1076     view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
1077 }
1078
1079 /*---------------------------------------------------------------------------*/
1080
1081 void game_kill_fade(void)
1082 {
1083     fade_k = 0.0f;
1084     fade_d = 0.0f;
1085 }
1086
1087 void game_step_fade(float dt)
1088 {
1089     if ((fade_k < 1.0f && fade_d > 0.0f) ||
1090         (fade_k > 0.0f && fade_d < 0.0f))
1091         fade_k += fade_d * dt;
1092
1093     if (fade_k < 0.0f)
1094     {
1095         fade_k = 0.0f;
1096         fade_d = 0.0f;
1097     }
1098     if (fade_k > 1.0f)
1099     {
1100         fade_k = 1.0f;
1101         fade_d = 0.0f;
1102     }
1103 }
1104
1105 void game_fade(float d)
1106 {
1107     fade_d = d;
1108 }
1109
1110 /*---------------------------------------------------------------------------*/
1111
1112 int put_game_state(FILE *fout)
1113 {
1114     if (game_state)
1115     {
1116         /* Write the view and tilt state. */
1117
1118         put_float(fout, &game_rx);
1119         put_float(fout, &game_rz);
1120         put_array(fout,  view_c, 3);
1121         put_array(fout,  view_p, 3);
1122
1123         /* Write the game simulation state. */
1124
1125         put_file_state(fout, &file);
1126
1127         return 1;
1128     }
1129     return 0;
1130 }
1131
1132 int get_game_state(FILE *fin)
1133 {
1134     if (game_state)
1135     {
1136         /* Read the view and tilt state. */
1137
1138         get_float(fin, &game_rx);
1139         get_float(fin, &game_rz);
1140         get_array(fin,  view_c, 3);
1141         get_array(fin,  view_p, 3);
1142
1143         /* Read the game simulation state. */
1144
1145         get_file_state(fin, &file);
1146
1147         return (feof(fin) ? 0 : 1);
1148     }
1149     return 0;
1150 }
1151
1152 /*---------------------------------------------------------------------------*/