785d16ef2dac1fa1a92d2047415a8cb7665ab879
[neverball] / ball / game.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include <SDL.h>
16 #include <math.h>
17
18 #include "glext.h"
19 #include "game.h"
20 #include "vec3.h"
21 #include "geom.h"
22 #include "back.h"
23 #include "part.h"
24 #include "image.h"
25 #include "audio.h"
26 #include "solid_gl.h"
27 #include "config.h"
28 #include "binary.h"
29 #include "level.h"
30
31 /*---------------------------------------------------------------------------*/
32
33 static int game_state = 0;
34
35 static struct s_file file;
36 static struct s_file back;
37
38 static float clock      = 0.f;          /* Clock time                        */
39 static int   clock_down = 1;            /* Clock go up or down?              */
40
41 static float game_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 orientation          */
54 static float view_k;
55
56 static int   coins  = 0;                /* Collected coins                   */
57 static int   goal_c = 0;                /* Goal coins remaining (0 = open)   */
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         audio_play(AUD_SHRINK, 1.f);
210
211         switch (grow_state)
212         {
213         case -1:
214             break;
215
216         case  0:
217             grow_goal = grow_orig * GROW_SMALL;
218             grow_state = -1;
219             grow = 1;
220             break;
221
222         case +1:
223             grow_goal = grow_orig;
224             grow_state = 0;
225             grow = 1;
226             break;
227         }
228     }
229     else if (type == ITEM_GROW)
230     {
231         audio_play(AUD_GROW, 1.f);
232
233         switch (grow_state)
234         {
235         case -1:
236             grow_goal = grow_orig;
237             grow_state = 0;
238             grow = 1;
239             break;
240
241         case  0:
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 struct level *level, int t, int g)
315 {
316     clock      = (float) t / 100.f;
317     clock_down = (t > 0);
318     coins      = 0;
319
320     if (game_state)
321         game_free();
322
323     if (!sol_load_gl(&file, config_data(level->file),
324                      config_get_d(CONFIG_TEXTURES),
325                      config_get_d(CONFIG_SHADOW)))
326         return (game_state = 0);
327
328     game_state = 1;
329
330     input_init();
331
332     game_rx = 0.0f;
333     game_rz = 0.0f;
334
335     /* Initialize jump and goal states. */
336
337     jump_e = 1;
338     jump_b = 0;
339
340     goal_c = g;
341     goal_k = (g == 0) ? 1.0f : 0.0f;
342
343     /* Initialise the level, background, particles, fade, and view. */
344
345     fade_k =  1.0f;
346     fade_d = -2.0f;
347
348     part_reset(GOAL_HEIGHT);
349     view_init();
350     back_init(level->grad, config_get_d(CONFIG_GEOMETRY));
351
352     sol_load_gl(&back, config_data(level->back),
353                 config_get_d(CONFIG_TEXTURES), 0);
354
355     /* Initialize ball size tracking... */
356
357     got_orig = 0;
358     grow = 0;
359
360     return game_state;
361 }
362
363 void game_free(void)
364 {
365     if (game_state)
366     {
367         sol_free_gl(&file);
368         sol_free_gl(&back);
369         back_free();
370     }
371     game_state = 0;
372 }
373
374 /*---------------------------------------------------------------------------*/
375
376 int curr_clock(void)
377 {
378     return (int) (clock * 100.f);
379 }
380
381 int curr_coins(void)
382 {
383     return coins;
384 }
385
386 int curr_goal(void)
387 {
388     return goal_c;
389 }
390
391 /*---------------------------------------------------------------------------*/
392
393 static void game_draw_balls(const struct s_file *fp)
394 {
395     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
396     float M[16];
397
398     m_basis(M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
399
400     glPushAttrib(GL_LIGHTING_BIT);
401     glPushMatrix();
402     {
403         glTranslatef(fp->uv[0].p[0],
404                      fp->uv[0].p[1] + BALL_FUDGE,
405                      fp->uv[0].p[2]);
406         glMultMatrixf(M);
407         glScalef(fp->uv[0].r,
408                  fp->uv[0].r,
409                  fp->uv[0].r);
410
411         glColor4fv(c);
412         ball_draw();
413     }
414     glPopMatrix();
415     glPopAttrib();
416 }
417
418 static void game_draw_items(const struct s_file *fp)
419 {
420     float r = 360.f * SDL_GetTicks() / 1000.f;
421     int hi;
422
423     glPushAttrib(GL_LIGHTING_BIT);
424     {
425         item_push(ITEM_COIN);
426         {
427             for (hi = 0; hi < fp->hc; hi++)
428
429                 if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
430                 {
431                     glPushMatrix();
432                     {
433                         glTranslatef(fp->hv[hi].p[0],
434                                      fp->hv[hi].p[1],
435                                      fp->hv[hi].p[2]);
436                         glRotatef(r, 0.0f, 1.0f, 0.0f);
437                         item_draw(&fp->hv[hi], r);
438                     }
439                     glPopMatrix();
440                 }
441         }
442         item_pull();
443
444         item_push(ITEM_SHRINK);
445         {
446             for (hi = 0; hi < fp->hc; hi++)
447
448                 if (fp->hv[hi].t == ITEM_SHRINK)
449                 {
450                     glPushMatrix();
451                     {
452                         glTranslatef(fp->hv[hi].p[0],
453                                      fp->hv[hi].p[1],
454                                      fp->hv[hi].p[2]);
455                         glRotatef(r, 0.0f, 1.0f, 0.0f);
456                         item_draw(&fp->hv[hi], r);
457                     }
458                     glPopMatrix();
459                 }
460         }
461         item_pull();
462
463         item_push(ITEM_GROW);
464         {
465             for (hi = 0; hi < fp->hc; hi++)
466
467                 if (fp->hv[hi].t == ITEM_GROW)
468                 {
469                     glPushMatrix();
470                     {
471                         glTranslatef(fp->hv[hi].p[0],
472                                      fp->hv[hi].p[1],
473                                      fp->hv[hi].p[2]);
474                         glRotatef(r, 0.0f, 1.0f, 0.0f);
475                         item_draw(&fp->hv[hi], r);
476                     }
477                     glPopMatrix();
478                 }
479         }
480         item_pull();
481     }
482     glPopAttrib();
483 }
484
485 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
486 {
487     if (goal_c == 0)
488     {
489         int zi;
490
491         /* Draw the goal particles. */
492
493         glEnable(GL_TEXTURE_2D);
494         {
495             for (zi = 0; zi < fp->zc; zi++)
496             {
497                 glPushMatrix();
498                 {
499                     glTranslatef(fp->zv[zi].p[0],
500                                  fp->zv[zi].p[1],
501                                  fp->zv[zi].p[2]);
502                     
503                     part_draw_goal(rx, ry, fp->zv[zi].r, goal_k);
504                 }
505                 glPopMatrix();
506             }
507         }
508         glDisable(GL_TEXTURE_2D);
509
510         /* Draw the goal column. */
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                 glScalef(fp->zv[zi].r,
521                          goal_k,
522                          fp->zv[zi].r);
523
524                 goal_draw();
525             }
526             glPopMatrix();
527         }
528     }
529 }
530
531 static void game_draw_jumps(const struct s_file *fp)
532 {
533     int ji;
534
535     for (ji = 0; ji < fp->jc; ji++)
536     {
537         glPushMatrix();
538         {
539             glTranslatef(fp->jv[ji].p[0],
540                          fp->jv[ji].p[1],
541                          fp->jv[ji].p[2]);
542             glScalef(fp->jv[ji].r,
543                      1.0f,
544                      fp->jv[ji].r);
545
546             jump_draw(!jump_e);
547         }
548         glPopMatrix();
549     }
550 }
551
552 static void game_draw_swchs(const struct s_file *fp)
553 {
554     int xi;
555
556     for (xi = 0; xi < fp->xc; xi++)
557     {
558         if (fp->xv[xi].i)
559             continue;
560
561         glPushMatrix();
562         {
563             glTranslatef(fp->xv[xi].p[0],
564                          fp->xv[xi].p[1],
565                          fp->xv[xi].p[2]);
566             glScalef(fp->xv[xi].r,
567                      1.0f,
568                      fp->xv[xi].r);
569
570             swch_draw(fp->xv[xi].f, fp->xv[xi].e);
571         }
572         glPopMatrix();
573     }
574 }
575
576 /*---------------------------------------------------------------------------*/
577
578 static void game_refl_all()
579 {
580     const float *ball_p = file.uv->p;
581
582     glPushMatrix();
583     {
584         /* Rotate the environment about the position of the ball. */
585
586         glTranslatef(+ball_p[0], +ball_p[1], +ball_p[2]);
587         glRotatef(-game_rz, view_e[2][0], view_e[2][1], view_e[2][2]);
588         glRotatef(-game_rx, view_e[0][0], view_e[0][1], view_e[0][2]);
589         glTranslatef(-ball_p[0], -ball_p[1], -ball_p[2]);
590
591         /* Draw the floor. */
592
593         sol_refl(&file);
594     }
595     glPopMatrix();
596 }
597
598 /*---------------------------------------------------------------------------*/
599
600 static void game_draw_light(void)
601 {
602     const float light_p[2][4] = {
603         { -8.0f, +32.0f, -8.0f, 0.0f },
604         { +8.0f, +32.0f, +8.0f, 0.0f },
605     };
606     const float light_c[2][4] = {
607         { 1.0f, 0.8f, 0.8f, 1.0f },
608         { 0.8f, 1.0f, 0.8f, 1.0f },
609     };
610
611     /* Configure the lighting. */
612
613     glEnable(GL_LIGHT0);
614     glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
615     glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_c[0]);
616     glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
617
618     glEnable(GL_LIGHT1);
619     glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
620     glLightfv(GL_LIGHT1, GL_DIFFUSE,  light_c[1]);
621     glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
622 }
623
624 static void game_draw_back(int pose, int d, const float p[3])
625 {
626     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
627     float t = SDL_GetTicks() / 1000.f + 120.0f;
628
629     glPushMatrix();
630     {
631         if (d < 0)
632         {
633             glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
634             glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
635         }
636
637         glTranslatef(p[0], p[1], p[2]);
638         glColor4fv(c);
639
640         if (config_get_d(CONFIG_BACKGROUND))
641         {
642             /* Draw all background layers back to front. */
643
644             sol_back(&back, BACK_DIST, FAR_DIST,  t);
645             back_draw(0);
646             sol_back(&back,         0, BACK_DIST, t);
647
648             /* Draw all foreground geometry in the background file. */
649
650             /* HACK: This is never used.
651             sol_draw(&back);
652             */
653         }
654         else back_draw(0);
655     }
656     glPopMatrix();
657 }
658
659 static void game_draw_fore(int pose, float rx,
660                                      float ry, int d, const float p[3])
661 {
662     const float *ball_p = file.uv->p;
663     const float  ball_r = file.uv->r;
664
665     glPushMatrix();
666     {
667         /* Rotate the environment about the position of the ball. */
668
669         glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
670         glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
671         glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
672         glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
673
674         if (d < 0)
675         {
676             GLdouble e[4];
677
678             e[0] = +0;
679             e[1] = +1;
680             e[2] = +0;
681             e[3] = -0.00001;
682
683             glEnable(GL_CLIP_PLANE0);
684             glClipPlane(GL_CLIP_PLANE0, e);
685         }
686
687         if (pose)
688             sol_draw(&file, -rx * d, -ry);
689         else
690         {
691             /* Draw the coins. */
692
693             game_draw_items(&file);
694
695             /* Draw the floor. */
696
697             sol_draw(&file, -rx * d, -ry);
698
699             /* Draw the ball shadow. */
700
701             if (d > 0 && config_get_d(CONFIG_SHADOW))
702             {
703                 shad_draw_set(ball_p, ball_r);
704                 sol_shad(&file);
705                 shad_draw_clr();
706             }
707
708             /* Draw the ball. */
709
710             game_draw_balls(&file);
711         }
712
713         /* Draw the particles and light columns. */
714
715         glEnable(GL_COLOR_MATERIAL);
716         glDisable(GL_LIGHTING);
717         glDepthMask(GL_FALSE);
718         {
719             glColor3f(1.0f, 1.0f, 1.0f);
720
721             sol_bill(&file, -rx * d, -ry);
722             part_draw_coin(-rx * d, -ry);
723
724             glDisable(GL_TEXTURE_2D);
725             {
726                 game_draw_goals(&file, -rx * d, -ry);
727                 game_draw_jumps(&file);
728                 game_draw_swchs(&file);
729             }
730             glEnable(GL_TEXTURE_2D);
731
732             glColor3f(1.0f, 1.0f, 1.0f);
733         }
734         glDepthMask(GL_TRUE);
735         glEnable(GL_LIGHTING);
736         glDisable(GL_COLOR_MATERIAL);
737
738         if (d < 0)
739             glDisable(GL_CLIP_PLANE0);
740     }
741     glPopMatrix();
742 }
743
744 void game_draw(int pose, float st)
745 {
746     float fov = view_fov;
747
748     if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
749
750     if (game_state)
751     {
752         config_push_persp(fov, 0.1f, FAR_DIST);
753         glPushMatrix();
754         {
755             float v[3], rx, ry;
756             float pup[3];
757             float pdn[3];
758
759             v_cpy(pup, view_p);
760             v_cpy(pdn, view_p);
761             pdn[1] = -pdn[1];
762
763             /* Compute and apply the view. */
764
765             v_sub(v, view_c, view_p);
766
767             rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
768             ry = V_DEG(fatan2f(+v[0], -v[2])) + st;
769
770             glTranslatef(0.f, 0.f, -v_len(v));
771             glRotatef(rx, 1.f, 0.f, 0.f);
772             glRotatef(ry, 0.f, 1.f, 0.f);
773             glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
774
775             if (config_get_d(CONFIG_REFLECTION))
776             {
777                 glEnable(GL_STENCIL_TEST);
778                 {
779                     /* Draw the mirrors only into the stencil buffer. */
780
781                     glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
782                     glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
783                     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
784                     glDepthMask(GL_FALSE);
785
786                     game_refl_all();
787
788                     glDepthMask(GL_TRUE);
789                     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
790                     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
791                     glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
792
793                     /* Draw the scene reflected into color and depth buffers. */
794
795                     glFrontFace(GL_CW);
796                     glPushMatrix();
797                     {
798                         glScalef(+1.f, -1.f, +1.f);
799
800                         game_draw_light();
801                         game_draw_back(pose,         -1, pdn);
802                         game_draw_fore(pose, rx, ry, -1, pdn);
803                     }
804                     glPopMatrix();
805                     glFrontFace(GL_CCW);
806                 }
807                 glDisable(GL_STENCIL_TEST);
808             }
809
810             /* Draw the scene normally. */
811
812             game_draw_light();
813             game_refl_all();
814             game_draw_back(pose,         +1, pup);
815             game_draw_fore(pose, rx, ry, +1, pup);
816         }
817         glPopMatrix();
818         config_pop_matrix();
819
820         /* Draw the fade overlay. */
821
822         fade_draw(fade_k);
823     }
824 }
825
826 /*---------------------------------------------------------------------------*/
827
828 static void game_update_grav(float h[3], const float g[3])
829 {
830     float x[3];
831     float y[3] = { 0.f, 1.f, 0.f };
832     float z[3];
833     float X[16];
834     float Z[16];
835     float M[16];
836
837     /* Compute the gravity vector from the given world rotations. */
838
839     z[0] = fsinf(V_RAD(view_a));
840     z[1] = 0.0;
841     z[2] = fcosf(V_RAD(view_a));
842
843     v_crs(x, y, z);
844     v_crs(z, x, y);
845     v_nrm(x, x);
846     v_nrm(z, z);
847
848     m_rot (Z, z, V_RAD(game_rz));
849     m_rot (X, x, V_RAD(game_rx));
850     m_mult(M, Z, X);
851     m_vxfm(h, M, g);
852 }
853
854 static void game_update_view(float dt)
855 {
856     float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
857     float da = input_get_r() * dt * 90.0f;
858     float k;
859
860     float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f };
861
862     view_a += da;
863
864     /* Center the view about the ball. */
865
866     v_cpy(view_c, file.uv->p);
867     v_inv(view_v, file.uv->v);
868
869     view_e[2][0] = fsinf(V_RAD(view_a));
870     view_e[2][1] = 0.0;
871     view_e[2][2] = fcosf(V_RAD(view_a));
872
873     switch (input_get_c())
874     {
875     case 1: /* Camera 1: Viewpoint chases the ball position. */
876
877         /* TODO: This camera no longer exists. */
878
879         break;
880
881     case 2: /* Camera 2: View vector is given by view angle. */
882
883         break;
884
885     default: /* Default: View vector approaches the ball velocity vector. */
886
887         v_mad(view_e[2], view_e[2], view_v, v_dot(view_v, view_v) * dt / 4);
888
889         break;
890     }
891
892     /* Orthonormalize the basis of the view in its new position. */
893
894     v_crs(view_e[0], view_e[1], view_e[2]);
895     v_crs(view_e[2], view_e[0], view_e[1]);
896     v_nrm(view_e[0], view_e[0]);
897     v_nrm(view_e[2], view_e[2]);
898
899     /* Compute the new view position. */
900
901     k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
902
903     view_k = view_k + (k - view_k) * dt;
904
905     if (view_k < 0.5) view_k = 0.5;
906
907     v_scl(v,    view_e[1], view_dp * view_k);
908     v_mad(v, v, view_e[2], view_dz * view_k);
909     m_rot(M, Y, V_RAD(da));
910     m_vxfm(view_p, M, v);
911     v_add(view_p, view_p, file.uv->p);
912
913     /* Compute the new view center. */
914
915     v_cpy(view_c, file.uv->p);
916     v_mad(view_c, view_c, view_e[1], dc);
917
918     /* Note the current view angle. */
919
920     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
921 }
922
923 static void game_update_time(float dt, int b)
924 {
925     if (goal_c == 0 && goal_k < 1.0f)
926         goal_k += dt;
927
928    /* The ticking clock. */
929
930     if (b && clock_down)
931     {
932         if (clock < 600.f)
933             clock -= dt;
934         if (clock < 0.f)
935             clock = 0.f;
936     }
937     else if (b)
938     {
939         clock += dt;
940     }
941 }
942
943 static int game_update_state(int bt)
944 {
945     struct s_file *fp = &file;
946     struct s_goal *zp;
947     struct s_item *hp;
948
949     float p[3];
950     float c[3];
951
952     /* Test for an item. */
953
954     if (bt && (hp = sol_item_test(fp, p, COIN_RADIUS)))
955     {
956         const char *sound = AUD_COIN;
957
958         item_color(hp, c);
959         part_burst(p, c);
960
961         grow_init(fp, hp->t);
962
963         if (hp->t == ITEM_COIN)
964         {
965             coins += hp->n;
966
967             /* Check for goal open. */
968
969             if (goal_c > 0)
970             {
971                 goal_c -= hp->n;
972                 if (goal_c <= 0)
973                 {
974                     sound = AUD_SWITCH;
975                     goal_c = 0;
976                 }
977             }
978         }
979         audio_play(sound, 1.f);
980
981         /* Reset item type. */
982
983         hp->t = ITEM_NONE;
984     }
985
986     /* Test for a switch. */
987
988     if (sol_swch_test(fp, 0))
989         audio_play(AUD_SWITCH, 1.f);
990
991     /* Test for a jump. */
992
993     if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
994     {
995         jump_b  = 1;
996         jump_e  = 0;
997         jump_dt = 0.f;
998
999         audio_play(AUD_JUMP, 1.f);
1000     }
1001     if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
1002         jump_e = 1;
1003
1004     /* Test for a goal. */
1005
1006     if (bt && goal_c == 0 && (zp = sol_goal_test(fp, p, 0)))
1007     {
1008         audio_play(AUD_GOAL, 1.0f);
1009         return GAME_GOAL;
1010     }
1011
1012     /* Test for time-out. */
1013
1014     if (bt && clock_down && clock <= 0.f)
1015     {
1016         audio_play(AUD_TIME, 1.0f);
1017         return GAME_TIME;
1018     }
1019
1020     /* Test for fall-out. */
1021
1022     if (bt && fp->uv[0].p[1] < fp->vv[0].p[1])
1023     {
1024         audio_play(AUD_FALL, 1.0f);
1025         return GAME_FALL;
1026     }
1027
1028     return GAME_NONE;
1029 }
1030
1031 int game_step(const float g[3], float dt, int bt)
1032 {
1033     if (game_state)
1034     {
1035         struct s_file *fp = &file;
1036
1037         float h[3];
1038
1039         /* Smooth jittery or discontinuous input. */
1040
1041         game_rx += (input_get_x() - game_rx) * dt / RESPONSE;
1042         game_rz += (input_get_z() - game_rz) * dt / RESPONSE;
1043
1044         grow_step(fp, dt);
1045
1046         game_update_grav(h, g);
1047         part_step(h, dt);
1048
1049         if (jump_b)
1050         {
1051             jump_dt += dt;
1052
1053             /* Handle a jump. */
1054
1055             if (0.5f < jump_dt)
1056             {
1057                 fp->uv[0].p[0] = jump_p[0];
1058                 fp->uv[0].p[1] = jump_p[1];
1059                 fp->uv[0].p[2] = jump_p[2];
1060             }
1061             if (1.0f < jump_dt)
1062                 jump_b = 0;
1063         }
1064         else
1065         {
1066             /* Run the sim. */
1067
1068             float b = sol_step(fp, h, dt, 0, NULL);
1069
1070             /* Mix the sound of a ball bounce. */
1071
1072             if (b > 0.5f)
1073             {
1074                 float k = (b - 0.5f) * 2.0f;
1075
1076                 if (got_orig)
1077                 {
1078                     if      (fp->uv->r > grow_orig) audio_play(AUD_BUMPL, k);
1079                     else if (fp->uv->r < grow_orig) audio_play(AUD_BUMPS, k);
1080                     else                            audio_play(AUD_BUMPM, k);
1081                 }
1082                 else audio_play(AUD_BUMPM, k);
1083             }
1084         }
1085
1086         game_step_fade(dt);
1087         game_update_view(dt);
1088         game_update_time(dt, bt);
1089
1090         return game_update_state(bt);
1091     }
1092     return GAME_NONE;
1093 }
1094
1095 /*---------------------------------------------------------------------------*/
1096
1097 void game_set_x(int k)
1098 {
1099     input_set_x(-ANGLE_BOUND * k / JOY_MAX);
1100 }
1101
1102 void game_set_z(int k)
1103 {
1104     input_set_z(+ANGLE_BOUND * k / JOY_MAX);
1105 }
1106
1107 void game_set_ang(int x, int z)
1108 {
1109     input_set_x(x);
1110     input_set_z(z);
1111 }
1112
1113 void game_set_pos(int x, int y)
1114 {
1115     input_set_x(input_get_x() + 40.0f * y / config_get_d(CONFIG_MOUSE_SENSE));
1116     input_set_z(input_get_z() + 40.0f * x / config_get_d(CONFIG_MOUSE_SENSE));
1117 }
1118
1119 void game_set_cam(int c)
1120 {
1121     input_set_c(c);
1122 }
1123
1124 void game_set_rot(float r)
1125 {
1126     input_set_r(r);
1127 }
1128
1129 /*---------------------------------------------------------------------------*/
1130
1131 void game_set_fly(float k)
1132 {
1133     struct s_file *fp = &file;
1134
1135     float  x[3] = { 1.f, 0.f, 0.f };
1136     float  y[3] = { 0.f, 1.f, 0.f };
1137     float  z[3] = { 0.f, 0.f, 1.f };
1138     float c0[3] = { 0.f, 0.f, 0.f };
1139     float p0[3] = { 0.f, 0.f, 0.f };
1140     float c1[3] = { 0.f, 0.f, 0.f };
1141     float p1[3] = { 0.f, 0.f, 0.f };
1142     float  v[3];
1143
1144     z[0] = fsinf(V_RAD(view_a));
1145     z[2] = fcosf(V_RAD(view_a));
1146
1147     v_cpy(view_e[0], x);
1148     v_cpy(view_e[1], y);
1149     v_cpy(view_e[2], z);
1150
1151     /* k = 0.0 view is at the ball. */
1152
1153     if (fp->uc > 0)
1154     {
1155         v_cpy(c0, fp->uv[0].p);
1156         v_cpy(p0, fp->uv[0].p);
1157     }
1158
1159     v_mad(p0, p0, y, view_dp);
1160     v_mad(p0, p0, z, view_dz);
1161     v_mad(c0, c0, y, view_dc);
1162
1163     /* k = +1.0 view is s_view 0 */
1164
1165     if (k >= 0 && fp->wc > 0)
1166     {
1167         v_cpy(p1, fp->wv[0].p);
1168         v_cpy(c1, fp->wv[0].q);
1169     }
1170
1171     /* k = -1.0 view is s_view 1 */
1172
1173     if (k <= 0 && fp->wc > 1)
1174     {
1175         v_cpy(p1, fp->wv[1].p);
1176         v_cpy(c1, fp->wv[1].q);
1177     }
1178
1179     /* Interpolate the views. */
1180
1181     v_sub(v, p1, p0);
1182     v_mad(view_p, p0, v, k * k);
1183
1184     v_sub(v, c1, c0);
1185     v_mad(view_c, c0, v, k * k);
1186
1187     /* Orthonormalize the view basis. */
1188
1189     v_sub(view_e[2], view_p, view_c);
1190     v_crs(view_e[0], view_e[1], view_e[2]);
1191     v_crs(view_e[2], view_e[0], view_e[1]);
1192     v_nrm(view_e[0], view_e[0]);
1193     v_nrm(view_e[2], view_e[2]);
1194 }
1195
1196 void game_look(float phi, float theta)
1197 {
1198     view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
1199     view_c[1] = view_p[1] +                       fsinf(V_RAD(phi));
1200     view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
1201 }
1202
1203 /*---------------------------------------------------------------------------*/
1204
1205 void game_kill_fade(void)
1206 {
1207     fade_k = 0.0f;
1208     fade_d = 0.0f;
1209 }
1210
1211 void game_step_fade(float dt)
1212 {
1213     if ((fade_k < 1.0f && fade_d > 0.0f) ||
1214         (fade_k > 0.0f && fade_d < 0.0f))
1215         fade_k += fade_d * dt;
1216
1217     if (fade_k < 0.0f)
1218     {
1219         fade_k = 0.0f;
1220         fade_d = 0.0f;
1221     }
1222     if (fade_k > 1.0f)
1223     {
1224         fade_k = 1.0f;
1225         fade_d = 0.0f;
1226     }
1227 }
1228
1229 void game_fade(float d)
1230 {
1231     fade_d = d;
1232 }
1233
1234 /*---------------------------------------------------------------------------*/