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