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