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