Widespread minor changes to eliminate the use of SDL_GetTicks to
[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 "ball.h"
25 #include "image.h"
26 #include "audio.h"
27 #include "solid_gl.h"
28 #include "config.h"
29 #include "binary.h"
30 #include "level.h"
31
32 /*---------------------------------------------------------------------------*/
33
34 static int game_state = 0;
35
36 static struct s_file file;
37 static struct s_file back;
38
39 static float timer      = 0.f;          /* Clock time                        */
40 static int   timer_down = 1;            /* Timer go up or down?              */
41
42 static float game_rx;                   /* Floor rotation about X axis       */
43 static float game_rz;                   /* Floor rotation about Z axis       */
44
45 static float view_a;                    /* Ideal view rotation about Y axis  */
46 static float view_dc;                   /* Ideal view distance above ball    */
47 static float view_dp;                   /* Ideal view distance above ball    */
48 static float view_dz;                   /* Ideal view distance behind ball   */
49 static float view_fov;                  /* Field of view                     */
50
51 static float view_c[3];                 /* Current view center               */
52 static float view_v[3];                 /* Current view vector               */
53 static float view_p[3];                 /* Current view position             */
54 static float view_e[3][3];              /* Current view reference frame      */
55 static float view_k;
56
57 static int   coins  = 0;                /* Collected coins                   */
58 static int   goal_c = 0;                /* Goal coins remaining (0 = open)   */
59 static float goal_k = 0;                /* Goal animation                    */
60 static int   jump_e = 1;                /* Jumping enabled flag              */
61 static int   jump_b = 0;                /* Jump-in-progress flag             */
62 static float jump_dt;                   /* Jump duration                     */
63 static float jump_p[3];                 /* Jump destination                  */
64 static float fade_k = 0.0;              /* Fade in/out level                 */
65 static float fade_d = 0.0;              /* Fade in/out direction             */
66
67 /*---------------------------------------------------------------------------*/
68
69 /*
70  * This is an abstraction of the game's input state.  All input is
71  * encapsulated here, and all references to the input by the game are made
72  * here.  This has the effect of homogenizing input for use in replay
73  * recording and playback.
74  *
75  * x and z:
76  *     -32767 = -ANGLE_BOUND
77  *     +32767 = +ANGLE_BOUND
78  *
79  * r:
80  *     -32767 = -VIEWR_BOUND
81  *     +32767 = +VIEWR_BOUND
82  *     
83  */
84
85 struct input
86 {
87     short x;
88     short z;
89     short r;
90     short c;
91 };
92
93 static struct input input_current;
94
95 static void input_init(void)
96 {
97     input_current.x = 0;
98     input_current.z = 0;
99     input_current.r = 0;
100     input_current.c = 0;
101 }
102
103 static void input_set_x(float x)
104 {
105     if (x < -ANGLE_BOUND) x = -ANGLE_BOUND;
106     if (x >  ANGLE_BOUND) x =  ANGLE_BOUND;
107
108     input_current.x = (short) (32767.0f * x / ANGLE_BOUND);
109 }
110
111 static void input_set_z(float z)
112 {
113     if (z < -ANGLE_BOUND) z = -ANGLE_BOUND;
114     if (z >  ANGLE_BOUND) z =  ANGLE_BOUND;
115
116     input_current.z = (short) (32767.0f * z / ANGLE_BOUND);
117 }
118
119 static void input_set_r(float r)
120 {
121     if (r < -VIEWR_BOUND) r = -VIEWR_BOUND;
122     if (r >  VIEWR_BOUND) r =  VIEWR_BOUND;
123
124     input_current.r = (short) (32767.0f * r / VIEWR_BOUND);
125 }
126
127 static void input_set_c(int c)
128 {
129     input_current.c = (short) c;
130 }
131
132 static float input_get_x(void)
133 {
134     return ANGLE_BOUND * (float) input_current.x / 32767.0f;
135 }
136
137 static float input_get_z(void)
138 {
139     return ANGLE_BOUND * (float) input_current.z / 32767.0f;
140 }
141
142 static float input_get_r(void)
143 {
144     return VIEWR_BOUND * (float) input_current.r / 32767.0f;
145 }
146
147 static int input_get_c(void)
148 {
149     return (int) input_current.c;
150 }
151
152 int input_put(FILE *fout)
153 {
154     if (game_state)
155     {
156         put_short(fout, &input_current.x);
157         put_short(fout, &input_current.z);
158         put_short(fout, &input_current.r);
159         put_short(fout, &input_current.c);
160         
161         return 1;
162     }
163     return 0;
164 }
165
166 int input_get(FILE *fin)
167 {
168     if (game_state)
169     {
170         get_short(fin, &input_current.x);
171         get_short(fin, &input_current.z);
172         get_short(fin, &input_current.r);
173         get_short(fin, &input_current.c);
174
175         return (feof(fin) ? 0 : 1);
176     }
177     return 0;
178 }
179
180 /*---------------------------------------------------------------------------*/
181
182 static int   grow = 0;                  /* Should the ball be changing size? */
183 static float grow_orig = 0;             /* the original ball size            */
184 static float grow_goal = 0;             /* how big or small to get!          */
185 static float grow_t = 0.0;              /* timer for the ball to grow...     */
186 static float grow_strt = 0;             /* starting value for growth         */
187 static int   got_orig = 0;              /* Do we know original ball size?    */
188
189 #define GROW_TIME  0.5f                 /* sec for the ball to get to size.  */
190 #define GROW_BIG   1.5f                 /* large factor                      */
191 #define GROW_SMALL 0.5f                 /* small factor                      */
192
193 static int   grow_state = 0;            /* Current state (values -1, 0, +1)  */
194
195 static void grow_init(const struct s_file *fp, int type)
196 {
197     if (!got_orig)
198     {
199         grow_orig  = fp->uv->r;
200         grow_goal  = grow_orig;
201         grow_strt  = grow_orig;
202
203         grow_state = 0;
204
205         got_orig   = 1;
206     }
207
208     if (type == ITEM_SHRINK)
209     {
210         audio_play(AUD_SHRINK, 1.f);
211
212         switch (grow_state)
213         {
214         case -1:
215             break;
216
217         case  0:
218             grow_goal = grow_orig * GROW_SMALL;
219             grow_state = -1;
220             grow = 1;
221             break;
222
223         case +1:
224             grow_goal = grow_orig;
225             grow_state = 0;
226             grow = 1;
227             break;
228         }
229     }
230     else if (type == ITEM_GROW)
231     {
232         audio_play(AUD_GROW, 1.f);
233
234         switch (grow_state)
235         {
236         case -1:
237             grow_goal = grow_orig;
238             grow_state = 0;
239             grow = 1;
240             break;
241
242         case  0:
243             grow_goal = grow_orig * GROW_BIG;
244             grow_state = +1;
245             grow = 1;
246             break;
247
248         case +1:
249             break;
250         }
251     }
252
253     if (grow)
254     {
255         grow_t = 0.0;
256         grow_strt = fp->uv->r;
257     }
258 }
259
260 static void grow_step(const struct s_file *fp, float dt)
261 {
262     float dr;
263
264     if (!grow)
265         return;
266
267     /* Calculate new size based on how long since you touched the coin... */
268
269     grow_t += dt;
270
271     if (grow_t >= GROW_TIME)
272     {
273         grow = 0;
274         grow_t = GROW_TIME;
275     }
276
277     dr = grow_strt + ((grow_goal-grow_strt) * (1.0f / (GROW_TIME / grow_t)));
278
279     /* No sinking through the floor! Keeps ball's bottom constant. */
280
281     fp->uv->p[1] += (dr - fp->uv->r);
282     fp->uv->r     =  dr;
283 }
284
285 /*---------------------------------------------------------------------------*/
286
287 static void view_init(void)
288 {
289     view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
290     view_dp  = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
291     view_dc  = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
292     view_dz  = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
293     view_k   = 1.0f;
294     view_a   = 0.0f;
295
296     view_c[0] = 0.f;
297     view_c[1] = view_dc;
298     view_c[2] = 0.f;
299
300     view_p[0] =     0.f;
301     view_p[1] = view_dp;
302     view_p[2] = view_dz;
303
304     view_e[0][0] = 1.f;
305     view_e[0][1] = 0.f;
306     view_e[0][2] = 0.f;
307     view_e[1][0] = 0.f;
308     view_e[1][1] = 1.f;
309     view_e[1][2] = 0.f;
310     view_e[2][0] = 0.f;
311     view_e[2][1] = 0.f;
312     view_e[2][2] = 1.f;
313 }
314
315 int game_init(const struct level *level, int t, int g)
316 {
317     timer      = (float) t / 100.f;
318     timer_down = (t > 0);
319     coins      = 0;
320
321     if (game_state)
322         game_free();
323
324     if (!sol_load_gl(&file, config_data(level->file),
325                      config_get_d(CONFIG_TEXTURES),
326                      config_get_d(CONFIG_SHADOW)))
327         return (game_state = 0);
328
329     game_state = 1;
330
331     input_init();
332
333     game_rx = 0.0f;
334     game_rz = 0.0f;
335
336     /* Initialize jump and goal states. */
337
338     jump_e = 1;
339     jump_b = 0;
340
341     goal_c = g;
342     goal_k = (g == 0) ? 1.0f : 0.0f;
343
344     /* Initialise the level, background, particles, fade, and view. */
345
346     fade_k =  1.0f;
347     fade_d = -2.0f;
348
349     part_reset(GOAL_HEIGHT);
350     view_init();
351     back_init(level->grad, config_get_d(CONFIG_GEOMETRY));
352
353     sol_load_gl(&back, config_data(level->back),
354                 config_get_d(CONFIG_TEXTURES), 0);
355
356     /* Initialize ball size tracking... */
357
358     got_orig = 0;
359     grow = 0;
360
361     return game_state;
362 }
363
364 void game_free(void)
365 {
366     if (game_state)
367     {
368         sol_free_gl(&file);
369         sol_free_gl(&back);
370         back_free();
371     }
372     game_state = 0;
373 }
374
375 /*---------------------------------------------------------------------------*/
376
377 int curr_clock(void)
378 {
379     return (int) (timer * 100.f);
380 }
381
382 int curr_coins(void)
383 {
384     return coins;
385 }
386
387 int curr_goal(void)
388 {
389     return goal_c;
390 }
391
392 /*---------------------------------------------------------------------------*/
393
394 static void game_draw_balls(const struct s_file *fp,
395                             const float *bill_M, float t)
396 {
397     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
398
399     float ball_M[16];
400     float pend_M[16];
401
402     m_basis(ball_M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
403     m_basis(pend_M, fp->uv[0].E[0], fp->uv[0].E[1], fp->uv[0].E[2]);
404
405     glPushAttrib(GL_LIGHTING_BIT);
406     glPushMatrix();
407     {
408         glTranslatef(fp->uv[0].p[0],
409                      fp->uv[0].p[1] + BALL_FUDGE,
410                      fp->uv[0].p[2]);
411         glScalef(fp->uv[0].r,
412                  fp->uv[0].r,
413                  fp->uv[0].r);
414
415         glColor4fv(c);
416         ball_draw(ball_M, pend_M, bill_M, t);
417     }
418     glPopMatrix();
419     glPopAttrib();
420 }
421
422 static void game_draw_items(const struct s_file *fp, float t)
423 {
424     float r = 360.f * t;
425     int hi;
426
427     glPushAttrib(GL_LIGHTING_BIT);
428     {
429         item_push(ITEM_COIN);
430         {
431             for (hi = 0; hi < fp->hc; hi++)
432
433                 if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
434                 {
435                     glPushMatrix();
436                     {
437                         glTranslatef(fp->hv[hi].p[0],
438                                      fp->hv[hi].p[1],
439                                      fp->hv[hi].p[2]);
440                         glRotatef(r, 0.0f, 1.0f, 0.0f);
441                         item_draw(&fp->hv[hi], r);
442                     }
443                     glPopMatrix();
444                 }
445         }
446         item_pull();
447
448         item_push(ITEM_SHRINK);
449         {
450             for (hi = 0; hi < fp->hc; hi++)
451
452                 if (fp->hv[hi].t == ITEM_SHRINK)
453                 {
454                     glPushMatrix();
455                     {
456                         glTranslatef(fp->hv[hi].p[0],
457                                      fp->hv[hi].p[1],
458                                      fp->hv[hi].p[2]);
459                         glRotatef(r, 0.0f, 1.0f, 0.0f);
460                         item_draw(&fp->hv[hi], r);
461                     }
462                     glPopMatrix();
463                 }
464         }
465         item_pull();
466
467         item_push(ITEM_GROW);
468         {
469             for (hi = 0; hi < fp->hc; hi++)
470
471                 if (fp->hv[hi].t == ITEM_GROW)
472                 {
473                     glPushMatrix();
474                     {
475                         glTranslatef(fp->hv[hi].p[0],
476                                      fp->hv[hi].p[1],
477                                      fp->hv[hi].p[2]);
478                         glRotatef(r, 0.0f, 1.0f, 0.0f);
479                         item_draw(&fp->hv[hi], r);
480                     }
481                     glPopMatrix();
482                 }
483         }
484         item_pull();
485     }
486     glPopAttrib();
487 }
488
489 static void game_draw_goals(const struct s_file *fp, const float *M, float t)
490 {
491     if (goal_c == 0)
492     {
493         int zi;
494
495         /* Draw the goal particles. */
496
497         glEnable(GL_TEXTURE_2D);
498         {
499             for (zi = 0; zi < fp->zc; zi++)
500             {
501                 glPushMatrix();
502                 {
503                     glTranslatef(fp->zv[zi].p[0],
504                                  fp->zv[zi].p[1],
505                                  fp->zv[zi].p[2]);
506                     
507                     part_draw_goal(M, fp->zv[zi].r, goal_k, t);
508                 }
509                 glPopMatrix();
510             }
511         }
512         glDisable(GL_TEXTURE_2D);
513
514         /* Draw the goal column. */
515
516         for (zi = 0; zi < fp->zc; zi++)
517         {
518             glPushMatrix();
519             {
520                 glTranslatef(fp->zv[zi].p[0],
521                              fp->zv[zi].p[1],
522                              fp->zv[zi].p[2]);
523
524                 glScalef(fp->zv[zi].r,
525                          goal_k,
526                          fp->zv[zi].r);
527
528                 goal_draw();
529             }
530             glPopMatrix();
531         }
532     }
533 }
534
535 static void game_draw_jumps(const struct s_file *fp)
536 {
537     int ji;
538
539     for (ji = 0; ji < fp->jc; ji++)
540     {
541         glPushMatrix();
542         {
543             glTranslatef(fp->jv[ji].p[0],
544                          fp->jv[ji].p[1],
545                          fp->jv[ji].p[2]);
546             glScalef(fp->jv[ji].r,
547                      1.0f,
548                      fp->jv[ji].r);
549
550             jump_draw(!jump_e);
551         }
552         glPopMatrix();
553     }
554 }
555
556 static void game_draw_swchs(const struct s_file *fp)
557 {
558     int xi;
559
560     for (xi = 0; xi < fp->xc; xi++)
561     {
562         if (fp->xv[xi].i)
563             continue;
564
565         glPushMatrix();
566         {
567             glTranslatef(fp->xv[xi].p[0],
568                          fp->xv[xi].p[1],
569                          fp->xv[xi].p[2]);
570             glScalef(fp->xv[xi].r,
571                      1.0f,
572                      fp->xv[xi].r);
573
574             swch_draw(fp->xv[xi].f, fp->xv[xi].e);
575         }
576         glPopMatrix();
577     }
578 }
579
580 /*---------------------------------------------------------------------------*/
581
582 static void game_draw_tilt(int d)
583 {
584     const float *ball_p = file.uv->p;
585
586     /* Rotate the environment about the position of the ball. */
587
588     glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
589     glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
590     glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
591     glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
592 }
593
594 static void game_refl_all(void)
595 {
596     glPushMatrix();
597     {
598         game_draw_tilt(1);
599
600         /* Draw the floor. */
601
602         sol_refl(&file);
603     }
604     glPopMatrix();
605 }
606
607 /*---------------------------------------------------------------------------*/
608
609 static void game_draw_light(void)
610 {
611     const float light_p[2][4] = {
612         { -8.0f, +32.0f, -8.0f, 0.0f },
613         { +8.0f, +32.0f, +8.0f, 0.0f },
614     };
615     const float light_c[2][4] = {
616         { 1.0f, 0.8f, 0.8f, 1.0f },
617         { 0.8f, 1.0f, 0.8f, 1.0f },
618     };
619
620     /* Configure the lighting. */
621
622     glEnable(GL_LIGHT0);
623     glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
624     glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_c[0]);
625     glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
626
627     glEnable(GL_LIGHT1);
628     glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
629     glLightfv(GL_LIGHT1, GL_DIFFUSE,  light_c[1]);
630     glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
631 }
632
633 static void game_draw_back(int pose, int d, float t)
634 {
635     glPushMatrix();
636     {
637         if (d < 0)
638         {
639             glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
640             glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
641         }
642
643         glTranslatef(view_p[0], view_p[1] * d, view_p[2]);
644
645         if (config_get_d(CONFIG_BACKGROUND))
646         {
647             /* Draw all background layers back to front. */
648
649             sol_back(&back, BACK_DIST, FAR_DIST,  t);
650             back_draw(0);
651             sol_back(&back,         0, BACK_DIST, t);
652         }
653         else back_draw(0);
654     }
655     glPopMatrix();
656 }
657
658 static void game_clip_refl(int d)
659 {
660     /* Fudge to eliminate the floor from reflection. */
661
662     GLdouble e[4], k = -0.00001;
663
664     e[0] = 0;
665     e[1] = 1;
666     e[2] = 0;
667     e[3] = k;
668
669     glClipPlane(GL_CLIP_PLANE0, e);
670 }
671
672 static void game_clip_ball(int d, const float *p)
673 {
674     GLdouble r, c[3], pz[4], nz[4];
675
676     /* Compute the plane giving the front of the ball, as seen from view_p. */
677
678     c[0] = p[0];
679     c[1] = p[1] * d;
680     c[2] = p[2];
681
682     pz[0] = view_p[0] - c[0];
683     pz[1] = view_p[1] - c[1];
684     pz[2] = view_p[2] - c[2];
685
686     r = sqrt(pz[0] * pz[0] + pz[1] * pz[1] + pz[2] * pz[2]);
687
688     pz[0] /= r;
689     pz[1] /= r;
690     pz[2] /= r;
691     pz[3] = -(pz[0] * c[0] +
692               pz[1] * c[1] +
693               pz[2] * c[2]);
694
695     /* Find the plane giving the back of the ball, as seen from view_p. */
696
697     nz[0] = -pz[0];
698     nz[1] = -pz[1];
699     nz[2] = -pz[2];
700     nz[3] = -pz[3];
701
702     /* Reflect these planes as necessary, and store them in the GL state. */
703
704     pz[1] *= d;
705     nz[1] *= d;
706
707     glClipPlane(GL_CLIP_PLANE1, nz);
708     glClipPlane(GL_CLIP_PLANE2, pz);
709 }
710
711 static void game_draw_fore(int pose, const float *M, int d, float t)
712 {
713     const float *ball_p = file.uv->p;
714     const float  ball_r = file.uv->r;
715
716     glPushMatrix();
717     {
718         /* Rotate the environment about the position of the ball. */
719
720         game_draw_tilt(d);
721
722         /* Compute clipping planes for reflection and ball facing. */
723
724         game_clip_refl(d);
725         game_clip_ball(d, ball_p);
726
727         if (d < 0)
728             glEnable(GL_CLIP_PLANE0);
729
730         if (pose)
731             sol_draw(&file, 0, 1);
732         else
733         {
734             /* Draw the coins. */
735
736             game_draw_items(&file, t);
737
738             /* Draw the floor. */
739
740             sol_draw(&file, 0, 1);
741
742             /* Draw the ball shadow. */
743
744             if (d > 0 && config_get_d(CONFIG_SHADOW))
745             {
746                 shad_draw_set(ball_p, ball_r);
747                 sol_shad(&file);
748                 shad_draw_clr();
749             }
750
751             /* Draw the ball. */
752
753             game_draw_balls(&file, M, t);
754         }
755
756         /* Draw the particles and light columns. */
757
758         glEnable(GL_COLOR_MATERIAL);
759         glDisable(GL_LIGHTING);
760         glDepthMask(GL_FALSE);
761         {
762             glColor3f(1.0f, 1.0f, 1.0f);
763
764             sol_bill(&file, M, t);
765             part_draw_coin(M, t);
766
767             glDisable(GL_TEXTURE_2D);
768             {
769                 game_draw_goals(&file, M, t);
770                 game_draw_jumps(&file);
771                 game_draw_swchs(&file);
772             }
773             glEnable(GL_TEXTURE_2D);
774
775             glColor3f(1.0f, 1.0f, 1.0f);
776         }
777         glDepthMask(GL_TRUE);
778         glEnable(GL_LIGHTING);
779         glDisable(GL_COLOR_MATERIAL);
780
781         if (d < 0)
782             glDisable(GL_CLIP_PLANE0);
783     }
784     glPopMatrix();
785 }
786
787 void game_draw(int pose, float t)
788 {
789     float fov = view_fov;
790
791     if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
792
793     if (game_state)
794     {
795         config_push_persp(fov, 0.1f, FAR_DIST);
796         glPushMatrix();
797         {
798             float T[16], U[16], M[16], v[3];
799
800             /* Compute direct and reflected view bases. */
801
802             v[0] = +view_p[0];
803             v[1] = -view_p[1];
804             v[2] = +view_p[2];
805
806             m_view(T, view_c, view_p, view_e[1]);
807             m_view(U, view_c, v,      view_e[1]);
808
809             m_xps(M, T);
810
811             /* Apply current the view. */
812
813             v_sub(v, view_c, view_p);
814
815             glTranslatef(0.f, 0.f, -v_len(v));
816             glMultMatrixf(M);
817             glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
818
819             if (config_get_d(CONFIG_REFLECTION))
820             {
821                 glEnable(GL_STENCIL_TEST);
822                 {
823                     /* Draw the mirrors only into the stencil buffer. */
824
825                     glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
826                     glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
827                     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
828                     glDepthMask(GL_FALSE);
829
830                     game_refl_all();
831
832                     glDepthMask(GL_TRUE);
833                     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
834                     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
835                     glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
836
837                     /* Draw the scene reflected into color and depth buffers. */
838
839                     glFrontFace(GL_CW);
840                     glPushMatrix();
841                     {
842                         glScalef(+1.0f, -1.0f, +1.0f);
843
844                         game_draw_light();
845                         game_draw_back(pose,    -1, t);
846                         game_draw_fore(pose, U, -1, t);
847                     }
848                     glPopMatrix();
849                     glFrontFace(GL_CCW);
850                 }
851                 glDisable(GL_STENCIL_TEST);
852             }
853
854             /* Draw the scene normally. */
855
856             game_draw_light();
857             game_refl_all();
858             game_draw_back(pose,    +1, t);
859             game_draw_fore(pose, T, +1, t);
860         }
861         glPopMatrix();
862         config_pop_matrix();
863
864         /* Draw the fade overlay. */
865
866         fade_draw(fade_k);
867     }
868 }
869
870 /*---------------------------------------------------------------------------*/
871
872 static void game_update_grav(float h[3], const float g[3])
873 {
874     float x[3];
875     float y[3] = { 0.0f, 1.0f, 0.0f };
876     float z[3];
877     float X[16];
878     float Z[16];
879     float M[16];
880
881     /* Compute the gravity vector from the given world rotations. */
882
883     z[0] = fsinf(V_RAD(view_a));
884     z[1] = 0.0f;
885     z[2] = fcosf(V_RAD(view_a));
886
887     v_crs(x, y, z);
888     v_crs(z, x, y);
889     v_nrm(x, x);
890     v_nrm(z, z);
891
892     m_rot (Z, z, V_RAD(game_rz));
893     m_rot (X, x, V_RAD(game_rx));
894     m_mult(M, Z, X);
895     m_vxfm(h, M, g);
896 }
897
898 static void game_update_view(float dt)
899 {
900     float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
901     float da = input_get_r() * dt * 90.0f;
902     float k;
903
904     float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f };
905
906     view_a += da;
907
908     /* Center the view about the ball. */
909
910     v_cpy(view_c, file.uv->p);
911     v_inv(view_v, file.uv->v);
912
913     view_e[2][0] = fsinf(V_RAD(view_a));
914     view_e[2][1] = 0.0;
915     view_e[2][2] = fcosf(V_RAD(view_a));
916
917     switch (input_get_c())
918     {
919     case 1: /* Camera 1: Viewpoint chases the ball position. */
920
921         /* TODO: This camera no longer exists. */
922
923         break;
924
925     case 2: /* Camera 2: View vector is given by view angle. */
926
927         break;
928
929     default: /* Default: View vector approaches the ball velocity vector. */
930
931         v_mad(view_e[2], view_e[2], view_v, v_dot(view_v, view_v) * dt / 4);
932
933         break;
934     }
935
936     /* Orthonormalize the new view reference frame. */
937
938     v_crs(view_e[0], view_e[1], view_e[2]);
939     v_crs(view_e[2], view_e[0], view_e[1]);
940     v_nrm(view_e[0], view_e[0]);
941     v_nrm(view_e[2], view_e[2]);
942
943     /* Compute the new view position. */
944
945     k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
946
947     view_k = view_k + (k - view_k) * dt;
948
949     if (view_k < 0.5) view_k = 0.5;
950
951     v_scl(v,    view_e[1], view_dp * view_k);
952     v_mad(v, v, view_e[2], view_dz * view_k);
953     m_rot(M, Y, V_RAD(da));
954     m_vxfm(view_p, M, v);
955     v_add(view_p, view_p, file.uv->p);
956
957     /* Compute the new view center. */
958
959     v_cpy(view_c, file.uv->p);
960     v_mad(view_c, view_c, view_e[1], dc);
961
962     /* Note the current view angle. */
963
964     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
965 }
966
967 static void game_update_time(float dt, int b)
968 {
969     if (goal_c == 0 && goal_k < 1.0f)
970         goal_k += dt;
971
972    /* The ticking clock. */
973
974     if (b && timer_down)
975     {
976         if (timer < 600.f)
977             timer -= dt;
978         if (timer < 0.f)
979             timer = 0.f;
980     }
981     else if (b)
982     {
983         timer += dt;
984     }
985 }
986
987 static int game_update_state(int bt)
988 {
989     struct s_file *fp = &file;
990     struct s_goal *zp;
991     struct s_item *hp;
992
993     float p[3];
994     float c[3];
995
996     /* Test for an item. */
997
998     if (bt && (hp = sol_item_test(fp, p, COIN_RADIUS)))
999     {
1000         const char *sound = AUD_COIN;
1001
1002         item_color(hp, c);
1003         part_burst(p, c);
1004
1005         grow_init(fp, hp->t);
1006
1007         if (hp->t == ITEM_COIN)
1008         {
1009             coins += hp->n;
1010
1011             /* Check for goal open. */
1012
1013             if (goal_c > 0)
1014             {
1015                 goal_c -= hp->n;
1016                 if (goal_c <= 0)
1017                 {
1018                     sound = AUD_SWITCH;
1019                     goal_c = 0;
1020                 }
1021             }
1022         }
1023         audio_play(sound, 1.f);
1024
1025         /* Reset item type. */
1026
1027         hp->t = ITEM_NONE;
1028     }
1029
1030     /* Test for a switch. */
1031
1032     if (sol_swch_test(fp, 0))
1033         audio_play(AUD_SWITCH, 1.f);
1034
1035     /* Test for a jump. */
1036
1037     if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
1038     {
1039         jump_b  = 1;
1040         jump_e  = 0;
1041         jump_dt = 0.f;
1042
1043         audio_play(AUD_JUMP, 1.f);
1044     }
1045     if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
1046         jump_e = 1;
1047
1048     /* Test for a goal. */
1049
1050     if (bt && goal_c == 0 && (zp = sol_goal_test(fp, p, 0)))
1051     {
1052         audio_play(AUD_GOAL, 1.0f);
1053         return GAME_GOAL;
1054     }
1055
1056     /* Test for time-out. */
1057
1058     if (bt && timer_down && timer <= 0.f)
1059     {
1060         audio_play(AUD_TIME, 1.0f);
1061         return GAME_TIME;
1062     }
1063
1064     /* Test for fall-out. */
1065
1066     if (bt && fp->uv[0].p[1] < fp->vv[0].p[1])
1067     {
1068         audio_play(AUD_FALL, 1.0f);
1069         return GAME_FALL;
1070     }
1071
1072     return GAME_NONE;
1073 }
1074
1075 int game_step(const float g[3], float dt, int bt)
1076 {
1077     if (game_state)
1078     {
1079         struct s_file *fp = &file;
1080
1081         float h[3];
1082
1083         /* Smooth jittery or discontinuous input. */
1084
1085         game_rx += (input_get_x() - game_rx) * dt / RESPONSE;
1086         game_rz += (input_get_z() - game_rz) * dt / RESPONSE;
1087
1088         grow_step(fp, dt);
1089
1090         game_update_grav(h, g);
1091         part_step(h, dt);
1092
1093         if (jump_b)
1094         {
1095             jump_dt += dt;
1096
1097             /* Handle a jump. */
1098
1099             if (0.5f < jump_dt)
1100             {
1101                 fp->uv[0].p[0] = jump_p[0];
1102                 fp->uv[0].p[1] = jump_p[1];
1103                 fp->uv[0].p[2] = jump_p[2];
1104             }
1105             if (1.0f < jump_dt)
1106                 jump_b = 0;
1107         }
1108         else
1109         {
1110             /* Run the sim. */
1111
1112             float b = sol_step(fp, h, dt, 0, NULL);
1113
1114             /* Mix the sound of a ball bounce. */
1115
1116             if (b > 0.5f)
1117             {
1118                 float k = (b - 0.5f) * 2.0f;
1119
1120                 if (got_orig)
1121                 {
1122                     if      (fp->uv->r > grow_orig) audio_play(AUD_BUMPL, k);
1123                     else if (fp->uv->r < grow_orig) audio_play(AUD_BUMPS, k);
1124                     else                            audio_play(AUD_BUMPM, k);
1125                 }
1126                 else audio_play(AUD_BUMPM, k);
1127             }
1128         }
1129
1130         game_step_fade(dt);
1131         game_update_view(dt);
1132         game_update_time(dt, bt);
1133
1134         return game_update_state(bt);
1135     }
1136     return GAME_NONE;
1137 }
1138
1139 /*---------------------------------------------------------------------------*/
1140
1141 void game_set_x(int k)
1142 {
1143     input_set_x(-ANGLE_BOUND * k / JOY_MAX);
1144 }
1145
1146 void game_set_z(int k)
1147 {
1148     input_set_z(+ANGLE_BOUND * k / JOY_MAX);
1149 }
1150
1151 void game_set_ang(int x, int z)
1152 {
1153     input_set_x(x);
1154     input_set_z(z);
1155 }
1156
1157 void game_set_pos(int x, int y)
1158 {
1159     input_set_x(input_get_x() + 40.0f * y / config_get_d(CONFIG_MOUSE_SENSE));
1160     input_set_z(input_get_z() + 40.0f * x / config_get_d(CONFIG_MOUSE_SENSE));
1161 }
1162
1163 void game_set_cam(int c)
1164 {
1165     input_set_c(c);
1166 }
1167
1168 void game_set_rot(float r)
1169 {
1170     input_set_r(r);
1171 }
1172
1173 /*---------------------------------------------------------------------------*/
1174
1175 void game_set_fly(float k)
1176 {
1177     struct s_file *fp = &file;
1178
1179     float  x[3] = { 1.f, 0.f, 0.f };
1180     float  y[3] = { 0.f, 1.f, 0.f };
1181     float  z[3] = { 0.f, 0.f, 1.f };
1182     float c0[3] = { 0.f, 0.f, 0.f };
1183     float p0[3] = { 0.f, 0.f, 0.f };
1184     float c1[3] = { 0.f, 0.f, 0.f };
1185     float p1[3] = { 0.f, 0.f, 0.f };
1186     float  v[3];
1187
1188     z[0] = fsinf(V_RAD(view_a));
1189     z[2] = fcosf(V_RAD(view_a));
1190
1191     v_cpy(view_e[0], x);
1192     v_cpy(view_e[1], y);
1193     v_cpy(view_e[2], z);
1194
1195     /* k = 0.0 view is at the ball. */
1196
1197     if (fp->uc > 0)
1198     {
1199         v_cpy(c0, fp->uv[0].p);
1200         v_cpy(p0, fp->uv[0].p);
1201     }
1202
1203     v_mad(p0, p0, y, view_dp);
1204     v_mad(p0, p0, z, view_dz);
1205     v_mad(c0, c0, y, view_dc);
1206
1207     /* k = +1.0 view is s_view 0 */
1208
1209     if (k >= 0 && fp->wc > 0)
1210     {
1211         v_cpy(p1, fp->wv[0].p);
1212         v_cpy(c1, fp->wv[0].q);
1213     }
1214
1215     /* k = -1.0 view is s_view 1 */
1216
1217     if (k <= 0 && fp->wc > 1)
1218     {
1219         v_cpy(p1, fp->wv[1].p);
1220         v_cpy(c1, fp->wv[1].q);
1221     }
1222
1223     /* Interpolate the views. */
1224
1225     v_sub(v, p1, p0);
1226     v_mad(view_p, p0, v, k * k);
1227
1228     v_sub(v, c1, c0);
1229     v_mad(view_c, c0, v, k * k);
1230
1231     /* Orthonormalize the view basis. */
1232
1233     v_sub(view_e[2], view_p, view_c);
1234     v_crs(view_e[0], view_e[1], view_e[2]);
1235     v_crs(view_e[2], view_e[0], view_e[1]);
1236     v_nrm(view_e[0], view_e[0]);
1237     v_nrm(view_e[2], view_e[2]);
1238 }
1239
1240 void game_look(float phi, float theta)
1241 {
1242     view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
1243     view_c[1] = view_p[1] +                       fsinf(V_RAD(phi));
1244     view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
1245 }
1246
1247 /*---------------------------------------------------------------------------*/
1248
1249 void game_kill_fade(void)
1250 {
1251     fade_k = 0.0f;
1252     fade_d = 0.0f;
1253 }
1254
1255 void game_step_fade(float dt)
1256 {
1257     if ((fade_k < 1.0f && fade_d > 0.0f) ||
1258         (fade_k > 0.0f && fade_d < 0.0f))
1259         fade_k += fade_d * dt;
1260
1261     if (fade_k < 0.0f)
1262     {
1263         fade_k = 0.0f;
1264         fade_d = 0.0f;
1265     }
1266     if (fade_k > 1.0f)
1267     {
1268         fade_k = 1.0f;
1269         fade_d = 0.0f;
1270     }
1271 }
1272
1273 void game_fade(float d)
1274 {
1275     fade_d = d;
1276 }
1277
1278 /*---------------------------------------------------------------------------*/