Merged progression and putt-collisions
[neverball] / putt / game.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERPUTT 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 "ball.h"
23 #include "back.h"
24 #include "hole.h"
25 #include "hud.h"
26 #include "image.h"
27 #include "audio.h"
28 #include "solid_gl.h"
29 #include "config.h"
30
31 /*---------------------------------------------------------------------------*/
32
33 static struct s_file file;
34 static int           ball;
35
36 static float view_a;                    /* Ideal view rotation about Y axis  */
37 static float view_m;
38 static float view_ry;                   /* Angular velocity about Y axis     */
39 static float view_dy;                   /* Ideal view distance above ball    */
40 static float view_dz;                   /* Ideal view distance behind ball   */
41
42 static float view_c[3];                 /* Current view center               */
43 static float view_v[3];                 /* Current view vector               */
44 static float view_p[3];                 /* Current view position             */
45 static float view_e[3][3];              /* Current view orientation          */
46
47 static float jump_e = 1;                /* Jumping enabled flag              */
48 static float jump_b = 0;                /* Jump-in-progress flag             */
49 static int   jump_u = 0;                /* Which ball is jumping?            */
50 static float jump_dt;                   /* Jump duration                     */
51 static float jump_p[3];                 /* Jump destination                  */
52
53 /*---------------------------------------------------------------------------*/
54
55 static void view_init(void)
56 {
57     view_a  = 0.f;
58     view_m  = 0.f;
59     view_ry = 0.f;
60     view_dy = 3.f;
61     view_dz = 5.f;
62
63     view_c[0] = 0.f;
64     view_c[1] = 0.f;
65     view_c[2] = 0.f;
66
67     view_p[0] =     0.f;
68     view_p[1] = view_dy;
69     view_p[2] = view_dz;
70
71     view_e[0][0] = 1.f;
72     view_e[0][1] = 0.f;
73     view_e[0][2] = 0.f;
74     view_e[1][0] = 0.f;
75     view_e[1][1] = 1.f;
76     view_e[1][2] = 0.f;
77     view_e[2][0] = 0.f;
78     view_e[2][1] = 0.f;
79     view_e[2][2] = 1.f;
80 }
81
82 void game_init(const char *s)
83 {
84     jump_e = 1;
85     jump_b = 0;
86     jump_u = 0;
87
88     view_init();
89     sol_load_gl(&file, config_data(s), config_get_d(CONFIG_TEXTURES),
90                                     config_get_d(CONFIG_SHADOW));
91 }
92
93 void game_free(void)
94 {
95     sol_free_gl(&file);
96 }
97
98 /*---------------------------------------------------------------------------*/
99
100 int game_check_balls(struct s_file *fp)
101 {
102     float z[3] = {0.0f, 0.0f, 0.0f};
103     int i, j;
104
105     for (i = 1; i < fp->uc && config_get_d(CONFIG_BALL_COLLISIONS); i++)
106     {
107         struct s_ball *up = fp->uv + i;
108
109        /*
110         * If a ball falls out, return the ball to the camera marker
111         * and reset the play state for fair play
112         */
113         if (i != ball && up->p[1] < -10.f && (up->p[1] > -199.9f || up->p[1] < -599.9f))
114         {
115             up->P = 0;
116             v_cpy(up->p, fp->uv->p);
117             v_cpy(up->v, z);
118             v_cpy(up->w, z);
119         }
120
121         if (i == ball && up->p[1] < -30.0f)
122         {
123             v_cpy(up->p, fp->uv->p);
124             v_cpy(up->v, z);
125             v_cpy(up->w, z);
126         }
127
128        /*
129         * If an OTHER ball stops in a hole, mark it as done
130         * and drop it -200.0 units to allow room for more balls
131         */
132         if (i != ball && !(v_len(up->v) > 0.0f))
133         {
134             const float *ball_p = up->p;
135             const float  ball_r = up->r;
136             int zi;
137             for (zi = 0; zi < fp->zc; zi++)
138             {
139                 float r[3];
140
141                 r[0] = ball_p[0] - fp->zv[zi].p[0];
142                 r[1] = ball_p[2] - fp->zv[zi].p[2];
143                 r[2] = 0;
144
145                 if (v_len(r) < fp->zv[zi].r * 1.1 - ball_r &&
146                     ball_p[1] > fp->zv[zi].p[1] &&
147                     ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2)
148                 {
149                     up->p[1] = -200.0f;
150                     v_cpy(up->v, z);
151                     v_cpy(up->w, z);
152                     return i;
153                 }
154             }
155         }
156
157        /*
158         * Check for intesecting balls.
159         * If there are any, reset the proper
160         * ball's play state
161         */
162         for (j = i + 1; j < fp->uc && config_get_d(CONFIG_BALL_COLLISIONS); j++)
163         {
164             struct s_ball *u2p = fp->uv + j;
165             float d[3];
166             v_sub(d, up->p, u2p->p);
167             if (v_len(up->v) > 0.005f || v_len(u2p->v) > 0.005f)
168                 continue;
169             if (v_len(d) < (fsqrtf((up->r + u2p->r) * (up->r + u2p->r))) * 1.0f && i != ball)
170                 up->P = 0;
171             else if (v_len(d) < (fsqrtf((up->r + u2p->r) * (up->r + u2p->r)) *  1.0f))
172                 u2p->P = 0;
173         }
174     }
175
176     for (i = 0; i < fp->yc; i++)
177     {
178         struct s_ball *yp = fp->yv + i;
179
180         if (yp->p[1] < -20.0f && yp->n)
181         {
182             v_cpy(yp->p, yp->O);
183             v_cpy(yp->v, z);
184             v_cpy(yp->w, z);
185         }
186
187         if (!(v_len(yp->v) > 0.0f))
188         {
189             const float *ball_p = yp->p;
190             const float  ball_r = yp->r;
191             int zi;
192             for (zi = 0; zi < fp->zc; zi++)
193             {
194                 float r[3];
195
196                 r[0] = ball_p[0] - fp->zv[zi].p[0];
197                 r[1] = ball_p[2] - fp->zv[zi].p[2];
198                 r[2] = 0;
199
200                 if (v_len(r) < fp->zv[zi].r * 1.1 - ball_r &&
201                     ball_p[1] > fp->zv[zi].p[1] &&
202                     ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2)
203                 {
204                     v_cpy(yp->p, yp->O);
205                     v_cpy(yp->v, z);
206                     v_cpy(yp->w, z);
207                 }
208             }
209         }
210     }
211
212     return 0;
213 }
214
215 static void game_draw_vect_prim(const struct s_file *fp, GLenum mode)
216 {
217     float p[3];
218     float x[3];
219     float z[3];
220     float r;
221
222     v_cpy(p, fp->uv[ball].p);
223     v_cpy(x, view_e[0]);
224     v_cpy(z, view_e[2]);
225
226     r = fp->uv[ball].r;
227
228     glBegin(mode);
229     {
230         glColor4f(1.0f, 1.0f, 0.5f, 0.5f);
231         glVertex3f(p[0] - x[0] * r,
232                    p[1] - x[1] * r,
233                    p[2] - x[2] * r);
234
235         glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
236         glVertex3f(p[0] + z[0] * view_m,
237                    p[1] + z[1] * view_m,
238                    p[2] + z[2] * view_m);
239
240         glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
241         glVertex3f(p[0] + x[0] * r,
242                    p[1] + x[1] * r,
243                    p[2] + x[2] * r);
244     }
245     glEnd();
246 }
247
248 static void game_draw_vect(const struct s_file *fp)
249 {
250     if (view_m > 0.f)
251     {
252         glPushAttrib(GL_TEXTURE_BIT);
253         glPushAttrib(GL_POLYGON_BIT);
254         glPushAttrib(GL_LIGHTING_BIT);
255         glPushAttrib(GL_DEPTH_BUFFER_BIT);
256         {
257             glEnable(GL_COLOR_MATERIAL);
258             glDisable(GL_LIGHTING);
259             glDisable(GL_TEXTURE_2D);
260             glDepthMask(GL_FALSE);
261
262             glEnable(GL_DEPTH_TEST);
263             game_draw_vect_prim(fp, GL_TRIANGLES);
264
265             glDisable(GL_DEPTH_TEST);
266             game_draw_vect_prim(fp, GL_LINE_STRIP);
267         }
268         glPopAttrib();
269         glPopAttrib();
270         glPopAttrib();
271         glPopAttrib();
272     }
273 }
274
275 static void game_draw_balls(const struct s_file *fp,
276                             const float *bill_M, float t)
277 {
278     static const GLfloat color[6][4] = {
279         { 1.0f, 1.0f, 1.0f, 0.7f },
280         { 1.0f, 0.0f, 0.0f, 1.0f },
281         { 0.0f, 1.0f, 0.0f, 1.0f },
282         { 0.0f, 0.0f, 1.0f, 1.0f },
283         { 1.0f, 1.0f, 0.0f, 1.0f },
284         { 0.1f, 0.1f, 0.1f, 1.0f },
285     };
286
287     int ui, yi;
288
289     for (yi = 0; yi < fp->yc; yi++)
290     {
291         float M[16];
292
293         if (!config_get_d(CONFIG_BALL_COLLISIONS) && fp->yv[yi].c)
294             continue;
295
296         m_basis(M, fp->yv[yi].e[0], fp->yv[yi].e[1], fp->yv[yi].e[2]);
297
298         glPushMatrix();
299         {
300             glTranslatef(fp->yv[yi].p[0],
301                          fp->yv[yi].p[1] + BALL_FUDGE,
302                          fp->yv[yi].p[2]);
303             glMultMatrixf(M);
304             glScalef(fp->yv[yi].r,
305                      fp->yv[yi].r,
306                      fp->yv[yi].r);
307
308             glColor4fv(color[5]);
309             oldball_draw(1);
310         }
311         glPopMatrix();
312     }
313
314     for (ui = curr_party(); ui > 0; ui--)
315     {
316         if (ui == ball || (config_get_d(CONFIG_BALL_COLLISIONS) && fp->uv[ui].P))
317         {
318             float M[16];
319
320             m_basis(M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
321
322             glPushMatrix();
323             {
324                 glTranslatef(fp->uv[ui].p[0],
325                              fp->uv[ui].p[1] + BALL_FUDGE,
326                              fp->uv[ui].p[2]);
327                 glMultMatrixf(M);
328                 glScalef(fp->uv[ui].r,
329                          fp->uv[ui].r,
330                          fp->uv[ui].r);
331
332                 glColor4fv(color[ui]);
333                 oldball_draw(0);
334             }
335             glPopMatrix();
336         }
337         else
338         {
339             glPushMatrix();
340             {
341                 glTranslatef(fp->uv[ui].p[0],
342                              fp->uv[ui].p[1] - fp->uv[ui].r + BALL_FUDGE,
343                              fp->uv[ui].p[2]);
344                 glScalef(fp->uv[ui].r,
345                          fp->uv[ui].r,
346                          fp->uv[ui].r);
347
348                 glColor4f(color[ui][0],
349                           color[ui][1],
350                           color[ui][2], 0.5f);
351
352                 mark_draw();
353             }
354             glPopMatrix();
355         }
356     }
357     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
358 }
359
360 static void game_draw_goals(const struct s_file *fp)
361 {
362     int zi;
363
364     for (zi = 0; zi < fp->zc; zi++)
365     {
366         glPushMatrix();
367         {
368             glTranslatef(fp->zv[zi].p[0],
369                          fp->zv[zi].p[1],
370                          fp->zv[zi].p[2]);
371             flag_draw();
372         }
373         glPopMatrix();
374     }
375 }
376
377 static void game_draw_jumps(const struct s_file *fp)
378 {
379     int ji;
380
381     for (ji = 0; ji < fp->jc; ji++)
382     {
383         glPushMatrix();
384         {
385             glTranslatef(fp->jv[ji].p[0],
386                          fp->jv[ji].p[1],
387                          fp->jv[ji].p[2]);
388
389             glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
390             jump_draw(!jump_e);
391         }
392         glPopMatrix();
393     }
394 }
395
396 static void game_draw_swchs(const struct s_file *fp)
397 {
398     int xi;
399
400     for (xi = 0; xi < fp->xc; xi++)
401     {
402         glPushMatrix();
403         {
404             glTranslatef(fp->xv[xi].p[0],
405                          fp->xv[xi].p[1],
406                          fp->xv[xi].p[2]);
407
408             glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
409             swch_draw(fp->xv[xi].f, fp->xv[xi].e);
410         }
411         glPopMatrix();
412     }
413 }
414
415 /*---------------------------------------------------------------------------*/
416
417 void game_draw(int pose, float t)
418 {
419     static const float a[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
420     static const float s[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
421     static const float e[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
422     static const float h[1] = { 0.0f };
423     
424     const float light_p[4] = { 8.f, 32.f, 8.f, 1.f };
425
426     const struct s_file *fp = &file;
427
428     float fov = FOV;
429
430     int i = 0;
431
432     if (config_get_d(CONFIG_BALL_COLLISIONS) && jump_b && jump_u != ball * 2)
433         fov /= 1.9f * fabsf(jump_dt - 0.5f);
434
435     else if (jump_b)
436         fov *= 2.0f * fabsf(jump_dt - 0.5f);
437
438     config_push_persp(fov, 0.1f, FAR_DIST);
439     glPushAttrib(GL_LIGHTING_BIT);
440     glPushMatrix();
441     {
442         float T[16], M[16], v[3], rx, ry;
443
444         m_view(T, view_c, view_p, view_e[1]);
445         m_xps(M, T);
446
447         v_sub(v, view_c, view_p);
448
449         rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
450         ry = V_DEG(fatan2f(+v[0], -v[2]));
451
452         glTranslatef(0.f, 0.f, -v_len(v));
453         glMultMatrixf(M);
454         glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
455
456         /* Center the skybox about the position of the camera. */
457
458         glPushMatrix();
459         {
460             glTranslatef(view_p[0], view_p[1], view_p[2]);
461             back_draw(0);
462         }
463         glPopMatrix();
464
465         glEnable(GL_LIGHT0);
466         glLightfv(GL_LIGHT0, GL_POSITION, light_p);
467
468         /* Draw the floor. */
469
470         sol_draw(fp, 0, 1);
471
472         if (config_get_d(CONFIG_SHADOW) && !pose)
473         {
474             for (i = 0; i < fp->yc; i++)
475             {
476                 shad_draw_set(fp->yv[i].p, fp->yv[i].r);
477                 sol_shad(fp);
478                 shad_draw_clr();
479             }
480
481             for (i = 0; i < fp->uc; i++)
482             {
483                 if (fp->uv[i].P)
484                 {
485                     shad_draw_set(fp->uv[i].p, fp->uv[i].r);
486                     sol_shad(fp);
487                     shad_draw_clr();
488                 }
489             }
490
491             shad_draw_set(fp->uv[ball].p, fp->uv[ball].r);
492             sol_shad(fp);
493             shad_draw_clr();
494         }
495
496         /* Draw the game elements. */
497
498         glEnable(GL_BLEND);
499         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
500
501         if (pose == 0)
502         {
503             game_draw_balls(fp, T, t);
504             game_draw_vect(fp);
505         }
506
507         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   a);
508         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  s);
509         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  e);
510         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, h);
511
512         game_draw_goals(fp);
513
514         glEnable(GL_COLOR_MATERIAL);
515         glDisable(GL_LIGHTING);
516         glDisable(GL_TEXTURE_2D);
517         glDepthMask(GL_FALSE);
518         {
519             game_draw_jumps(fp);
520             game_draw_swchs(fp);
521         }
522         glDepthMask(GL_TRUE);
523         glEnable(GL_TEXTURE_2D);
524         glEnable(GL_LIGHTING);
525         glDisable(GL_COLOR_MATERIAL);
526     }
527     glPopMatrix();
528     glPopAttrib();
529     config_pop_matrix();
530 }
531
532 /*---------------------------------------------------------------------------*/
533
534 void game_update_view(float dt)
535 {
536     const float y[3] = { 0.f, 1.f, 0.f };
537
538     float dy;
539     float dz;
540     float k;
541     float e[3];
542     float d[3];
543     float s = 2.f * dt;
544
545     /* Center the view about the ball. */
546
547     v_cpy(view_c, file.uv[ball].p);
548     v_inv(view_v, file.uv[ball].v);
549
550     switch (config_get_d(CONFIG_CAMERA))
551     {
552     case 2:
553         /* Camera 2: View vector is given by view angle. */
554
555         view_e[2][0] = fsinf(V_RAD(view_a));
556         view_e[2][1] = 0.f;
557         view_e[2][2] = fcosf(V_RAD(view_a));
558
559         s = 1.f;
560         break;
561
562     default:
563         /* View vector approaches the ball velocity vector. */
564
565         v_mad(e, view_v, y, v_dot(view_v, y));
566         v_inv(e, e);
567
568         k = v_dot(view_v, view_v);
569
570         v_sub(view_e[2], view_p, view_c);
571         v_mad(view_e[2], view_e[2], view_v, k * dt * 0.1f);
572     }
573
574     /* Orthonormalize the basis of the view in its new position. */
575
576     v_crs(view_e[0], view_e[1], view_e[2]);
577     v_crs(view_e[2], view_e[0], view_e[1]);
578     v_nrm(view_e[0], view_e[0]);
579     v_nrm(view_e[2], view_e[2]);
580
581     /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */
582
583     v_sub(d, view_p, view_c);
584
585     dy = v_dot(view_e[1], d);
586     dz = v_dot(view_e[2], d);
587
588     dy += (view_dy - dy) * s;
589     dz += (view_dz - dz) * s;
590
591     /* Compute the new view position. */
592
593     view_p[0] = view_p[1] = view_p[2] = 0.f;
594
595     v_mad(view_p, view_c, view_e[1], dy);
596     v_mad(view_p, view_p, view_e[2], dz);
597
598     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
599 }
600
601 static int game_update_state(float dt)
602 {
603     static float t = 0.f;
604
605     struct s_file *fp = &file;
606     float p[3];
607
608     int i;
609
610     if (dt > 0.f)
611         t += dt;
612     else
613         t = 0.f;
614
615     /* Test for a switch. */
616     if (sol_swch_test(fp))
617         audio_play(AUD_SWITCH, 1.f);
618
619     /* Test for a jump. */
620
621     if (config_get_d(CONFIG_BALL_COLLISIONS))
622     {
623         for (i = 1; i < curr_party() + 1; i++)
624         {
625             if (!jump_u && jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, i) == 1)
626             {
627                 jump_b  = 1;
628                 jump_e  = 0;
629                 jump_dt = 0.f;
630                 jump_u  = i * 2;
631
632                 audio_play(AUD_JUMP, 1.f);
633             }
634             if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, i) == 0)
635                 jump_e = 1;
636             if (!jump_b && jump_u && i == jump_u / 2 && sol_jump_test(fp, jump_p, i) == 0)
637                 jump_u = 0;
638         }
639
640         for (i = 0; i < fp->yc; i++)
641         {
642             if (!jump_u && jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 1)
643             {
644                 jump_b  = 1;
645                 jump_e  = 0;
646                 jump_dt = 0.f;
647                 jump_u  = i * 2 + 1;
648
649                 audio_play(AUD_JUMP, 1.f);
650             }
651             if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 0)
652                 jump_e = 1;
653             if (!jump_b && jump_u && i == jump_u / 2 && sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 0)
654                 jump_u = 0;
655         }
656     }
657     else
658     {
659         if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 1)
660         {
661             jump_b  = 1;
662             jump_e  = 0;
663             jump_dt = 0.f;
664
665             audio_play(AUD_JUMP, 1.f);
666         }
667         if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 0)
668             jump_e = 1;
669     }
670
671     /* Test for fall-out. */
672
673     if (fp->uv[ball].p[1] < -10.0f)
674         return GAME_FALL;
675
676     /* Test for a goal or stop. */
677
678     if (t > 1.0f)
679     {
680         t = 0.0f;
681
682         if (config_get_d(CONFIG_BALL_COLLISIONS))
683         {
684             switch (sol_goal_test(fp, p, ball))
685             {
686                 case 2:  /* The player's ball landed in the goal and the all of the other balls have stopped */
687                     t = 0.0f;
688                     return GAME_GOAL;
689                     break;
690                 case 1:  /* All balls have stopped */
691                     t = 0.0f;
692                     return GAME_STOP;
693                     break;
694                 case 0:  /* Game still running; there may be a ball that has not yet stopped */
695                     return GAME_NONE;
696                     break;
697                 default: /* Should never reach this */
698                     break;
699             }
700         }
701
702         else
703         {
704             if (sol_goal_test(fp, p, ball))
705                 return GAME_GOAL;
706             else
707                 return GAME_STOP;
708         }
709     }
710
711     return GAME_NONE;
712 }
713
714 void game_set_played(int b)
715 {
716     if (ball)
717         file.uv[ball].P = b;
718     if (!b)
719     {
720         file.uv[0].P = 0;
721         file.uv[1].P = 0;
722         file.uv[2].P = 0;
723         file.uv[3].P = 0;
724         file.uv[4].P = 0;
725     }
726 }
727
728 /*
729  * On  most  hardware, rendering  requires  much  more  computing power  than
730  * physics.  Since  physics takes less time  than graphics, it  make sense to
731  * detach  the physics update  time step  from the  graphics frame  rate.  By
732  * performing multiple physics updates for  each graphics update, we get away
733  * with higher quality physics with little impact on overall performance.
734  *
735  * Toward this  end, we establish a  baseline maximum physics  time step.  If
736  * the measured  frame time  exceeds this  maximum, we cut  the time  step in
737  * half, and  do two updates.  If THIS  time step exceeds the  maximum, we do
738  * four updates.  And  so on.  In this way, the physics  system is allowed to
739  * seek an optimal update rate independent of, yet in integral sync with, the
740  * graphics frame rate.
741  */
742
743 int game_step(const float g[3], float dt)
744 {
745     struct s_file *fp = &file;
746
747     static float s = 0.f;
748     static float t = 0.f;
749
750     float d = 0.f;
751     float b = 0.f;
752     float st = 0.f;
753     int i, n = 1, m = 0;
754
755     s = (7.f * s + dt) / 8.f;
756     t = s;
757
758     if (jump_b)
759     {
760         if (config_get_d(CONFIG_BALL_COLLISIONS))
761         {
762             jump_dt += dt;
763
764             /* Handle a jump. */
765
766             if (0.5 < jump_dt)
767             {
768                 if (jump_u % 2)
769                 {
770                     fp->yv[jump_u / 2].p[0] = jump_p[0];
771                     fp->yv[jump_u / 2].p[1] = jump_p[1];
772                     fp->yv[jump_u / 2].p[2] = jump_p[2];
773                 }
774
775                 else
776                 {
777                     fp->uv[jump_u / 2].p[0] = jump_p[0];
778                     fp->uv[jump_u / 2].p[1] = jump_p[1];
779                     fp->uv[jump_u / 2].p[2] = jump_p[2];
780                 }
781             }
782             if (1.f < jump_dt)
783             {
784                 jump_b = 0;
785             }
786         }
787
788         else
789         {
790             jump_dt += dt;
791
792             /* Handle a jump. */
793
794             if (0.5 < jump_dt)
795             {
796                 fp->uv[ball].p[0] = jump_p[0];
797                 fp->uv[ball].p[1] = jump_p[1];
798                 fp->uv[ball].p[2] = jump_p[2];
799             }
800             if (1.f < jump_dt)
801                 jump_b = 0;
802         }
803     }
804     else
805     {
806         /* Run the sim. */
807
808         while (t > MAX_DT && n < MAX_DN)
809         {
810             t /= 2;
811             n *= 2;
812         }
813
814         for (i = 0; i < n; i++)
815         {
816             int ball_in_hole = 0;
817
818             d = sol_step(fp, g, t, ball, &m);
819
820             if ((ball_in_hole = game_check_balls(fp)))
821                 hole_goal(ball_in_hole);
822
823             if (b < d)
824                 b = d;
825             if (m)
826                 st += t;
827         }
828
829         /* Mix the sound of a ball bounce. */
830
831         if (b > 0.5)
832             audio_play(AUD_BUMP, (float) (b - 0.5) * 2.0f);
833     }
834
835     game_update_view(dt);
836     return game_update_state(st);
837 }
838
839 void game_putt(void)
840 {
841     /*
842      * HACK: The BALL_FUDGE here  guarantees that a putt doesn't drive
843      * the ball  too directly down  toward a lump,  triggering rolling
844      * friction too early and stopping the ball prematurely.
845      */
846
847     if (config_get_d(CONFIG_BALL_COLLISIONS))
848     {
849         file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
850         file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
851         file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
852     }
853
854     else
855     {
856         file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
857         file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
858         file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
859     }
860
861     view_m = 0.f;
862 }
863
864 /*---------------------------------------------------------------------------*/
865
866 void game_set_rot(int d)
867 {
868     view_a += (float) (30.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
869 }
870
871 void game_clr_mag(void)
872 {
873     view_m = 1.f;
874 }
875
876 void game_set_mag(int d)
877 {
878     view_m -= (float) (1.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
879
880     if (view_m < 0.25)
881         view_m = 0.25;
882 }
883
884 void game_set_fly(float k)
885 {
886     struct s_file *fp = &file;
887
888     float  x[3] = { 1.f, 0.f, 0.f };
889     float  y[3] = { 0.f, 1.f, 0.f };
890     float  z[3] = { 0.f, 0.f, 1.f };
891     float c0[3] = { 0.f, 0.f, 0.f };
892     float p0[3] = { 0.f, 0.f, 0.f };
893     float c1[3] = { 0.f, 0.f, 0.f };
894     float p1[3] = { 0.f, 0.f, 0.f };
895     float  v[3];
896
897     v_cpy(view_e[0], x);
898     v_cpy(view_e[1], y);
899     v_sub(view_e[2], fp->uv[ball].p, fp->zv[0].p);
900
901     if (fabs(v_dot(view_e[1], view_e[2])) > 0.999)
902         v_cpy(view_e[2], z);
903
904     v_crs(view_e[0], view_e[1], view_e[2]);
905     v_crs(view_e[2], view_e[0], view_e[1]);
906
907     v_nrm(view_e[0], view_e[0]);
908     v_nrm(view_e[2], view_e[2]);
909
910     /* k = 0.0 view is at the ball. */
911
912     if (fp->uc > 0)
913     {
914         v_cpy(c0, fp->uv[ball].p);
915         v_cpy(p0, fp->uv[ball].p);
916     }
917
918     v_mad(p0, p0, view_e[1], view_dy);
919     v_mad(p0, p0, view_e[2], view_dz);
920
921     /* k = +1.0 view is s_view 0 */
922
923     if (k >= 0 && fp->wc > 0)
924     {
925         v_cpy(p1, fp->wv[0].p);
926         v_cpy(c1, fp->wv[0].q);
927     }
928
929     /* k = -1.0 view is s_view 1 */
930
931     if (k <= 0 && fp->wc > 1)
932     {
933         v_cpy(p1, fp->wv[1].p);
934         v_cpy(c1, fp->wv[1].q);
935     }
936
937     /* Interpolate the views. */
938
939     v_sub(v, p1, p0);
940     v_mad(view_p, p0, v, k * k);
941
942     v_sub(v, c1, c0);
943     v_mad(view_c, c0, v, k * k);
944
945     /* Orthonormalize the view basis. */
946
947     v_sub(view_e[2], view_p, view_c);
948     v_crs(view_e[0], view_e[1], view_e[2]);
949     v_crs(view_e[2], view_e[0], view_e[1]);
950     v_nrm(view_e[0], view_e[0]);
951     v_nrm(view_e[2], view_e[2]);
952
953     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
954 }
955
956 void game_ball(int i)
957 {
958     int ui;
959
960     ball = i;
961
962     jump_e = 1;
963     jump_b = 0;
964
965     for (ui = 0; ui < file.uc; ui++)
966     {
967         file.uv[ui].v[0] = 0.f;
968         file.uv[ui].v[1] = 0.f;
969         file.uv[ui].v[2] = 0.f;
970
971         file.uv[ui].w[0] = 0.f;
972         file.uv[ui].w[1] = 0.f;
973         file.uv[ui].w[2] = 0.f;
974     }
975 }
976
977 void game_get_pos(float p[3], float e[3][3])
978 {
979     v_cpy(p,    file.uv[ball].p);
980     v_cpy(e[0], file.uv[ball].e[0]);
981     v_cpy(e[1], file.uv[ball].e[1]);
982     v_cpy(e[2], file.uv[ball].e[2]);
983 }
984
985 void game_set_pos(float p[3], float e[3][3])
986 {
987     v_cpy(file.uv[ball].p,    p);
988     v_cpy(file.uv[ball].e[0], e[0]);
989     v_cpy(file.uv[ball].e[1], e[1]);
990     v_cpy(file.uv[ball].e[2], e[2]);
991 }
992
993 /*---------------------------------------------------------------------------*/
994