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