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