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