Correctly check switch test return value in Neverputt
[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 "config.h"
29 #include "video.h"
30
31 #include "solid_gl.h"
32 #include "solid_sim.h"
33 #include "solid_all.h"
34
35 /*---------------------------------------------------------------------------*/
36
37 static struct s_file file;
38 static int           ball;
39
40 static float view_a;                    /* Ideal view rotation about Y axis  */
41 static float view_m;
42 static float view_ry;                   /* Angular velocity about Y axis     */
43 static float view_dy;                   /* Ideal view distance above ball    */
44 static float view_dz;                   /* Ideal view distance behind ball   */
45
46 static float view_c[3];                 /* Current view center               */
47 static float view_v[3];                 /* Current view vector               */
48 static float view_p[3];                 /* Current view position             */
49 static float view_e[3][3];              /* Current view orientation          */
50
51 static float jump_e = 1;                /* Jumping enabled flag              */
52 static float jump_b = 0;                /* Jump-in-progress flag             */
53 static float jump_dt;                   /* Jump duration                     */
54 static float jump_p[3];                 /* Jump destination                  */
55
56 static float idle_t;                    /* Idling timeout                    */
57
58 /*---------------------------------------------------------------------------*/
59
60 static void view_init(void)
61 {
62     view_a  = 0.f;
63     view_m  = 0.f;
64     view_ry = 0.f;
65     view_dy = 3.f;
66     view_dz = 5.f;
67
68     view_c[0] = 0.f;
69     view_c[1] = 0.f;
70     view_c[2] = 0.f;
71
72     view_p[0] =     0.f;
73     view_p[1] = view_dy;
74     view_p[2] = view_dz;
75
76     view_e[0][0] = 1.f;
77     view_e[0][1] = 0.f;
78     view_e[0][2] = 0.f;
79     view_e[1][0] = 0.f;
80     view_e[1][1] = 1.f;
81     view_e[1][2] = 0.f;
82     view_e[2][0] = 0.f;
83     view_e[2][1] = 0.f;
84     view_e[2][2] = 1.f;
85 }
86
87 void game_init(const char *s)
88 {
89     int i;
90
91     jump_e = 1;
92     jump_b = 0;
93
94     idle_t = 1.0f;
95
96     view_init();
97     sol_load_gl(&file, s, config_get_d(CONFIG_SHADOW));
98     sol_init_sim(&file);
99
100     for (i = 0; i < file.dc; i++)
101     {
102         const char *k = file.av + file.dv[i].ai;
103         const char *v = file.av + file.dv[i].aj;
104
105         if (strcmp(k, "idle") == 0)
106         {
107             sscanf(v, "%f", &idle_t);
108
109             if (idle_t < 1.0f)
110                 idle_t = 1.0f;
111         }
112     }
113 }
114
115 void game_free(void)
116 {
117     sol_quit_sim();
118     sol_free_gl(&file);
119 }
120
121 /*---------------------------------------------------------------------------*/
122
123 static void game_draw_vect_prim(const struct s_file *fp, GLenum mode)
124 {
125     float p[3];
126     float x[3];
127     float z[3];
128     float r;
129
130     v_cpy(p, fp->uv[ball].p);
131     v_cpy(x, view_e[0]);
132     v_cpy(z, view_e[2]);
133
134     r = fp->uv[ball].r;
135
136     glBegin(mode);
137     {
138         glColor4f(1.0f, 1.0f, 0.5f, 0.5f);
139         glVertex3f(p[0] - x[0] * r,
140                    p[1] - x[1] * r,
141                    p[2] - x[2] * r);
142
143         glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
144         glVertex3f(p[0] + z[0] * view_m,
145                    p[1] + z[1] * view_m,
146                    p[2] + z[2] * view_m);
147
148         glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
149         glVertex3f(p[0] + x[0] * r,
150                    p[1] + x[1] * r,
151                    p[2] + x[2] * r);
152     }
153     glEnd();
154 }
155
156 static void game_draw_vect(const struct s_file *fp)
157 {
158     if (view_m > 0.f)
159     {
160         glPushAttrib(GL_TEXTURE_BIT);
161         glPushAttrib(GL_POLYGON_BIT);
162         glPushAttrib(GL_LIGHTING_BIT);
163         glPushAttrib(GL_DEPTH_BUFFER_BIT);
164         {
165             glEnable(GL_COLOR_MATERIAL);
166             glDisable(GL_LIGHTING);
167             glDisable(GL_TEXTURE_2D);
168             glDepthMask(GL_FALSE);
169
170             glEnable(GL_DEPTH_TEST);
171             game_draw_vect_prim(fp, GL_TRIANGLES);
172
173             glDisable(GL_DEPTH_TEST);
174             game_draw_vect_prim(fp, GL_LINE_STRIP);
175         }
176         glPopAttrib();
177         glPopAttrib();
178         glPopAttrib();
179         glPopAttrib();
180     }
181 }
182
183 static void game_draw_balls(const struct s_file *fp,
184                             const float *bill_M, float t)
185 {
186     static const GLfloat color[5][4] = {
187         { 1.0f, 1.0f, 1.0f, 0.7f },
188         { 1.0f, 0.0f, 0.0f, 1.0f },
189         { 0.0f, 1.0f, 0.0f, 1.0f },
190         { 0.0f, 0.0f, 1.0f, 1.0f },
191         { 1.0f, 1.0f, 0.0f, 1.0f },
192     };
193
194     int ui;
195
196     for (ui = curr_party(); ui > 0; ui--)
197     {
198         if (ui == ball)
199         {
200             float ball_M[16];
201             float pend_M[16];
202
203             m_basis(ball_M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
204             m_basis(pend_M, fp->uv[ui].E[0], fp->uv[ui].E[1], fp->uv[ui].E[2]);
205
206             glPushMatrix();
207             {
208                 glTranslatef(fp->uv[ui].p[0],
209                              fp->uv[ui].p[1] + BALL_FUDGE,
210                              fp->uv[ui].p[2]);
211                 glScalef(fp->uv[ui].r,
212                          fp->uv[ui].r,
213                          fp->uv[ui].r);
214
215                 glEnable(GL_COLOR_MATERIAL);
216                 glColor4fv(color[ui]);
217                 ball_draw(ball_M, pend_M, bill_M, t);
218                 glDisable(GL_COLOR_MATERIAL);
219             }
220             glPopMatrix();
221         }
222         else
223         {
224             glPushMatrix();
225             {
226                 glTranslatef(fp->uv[ui].p[0],
227                              fp->uv[ui].p[1] - fp->uv[ui].r + BALL_FUDGE,
228                              fp->uv[ui].p[2]);
229                 glScalef(fp->uv[ui].r,
230                          fp->uv[ui].r,
231                          fp->uv[ui].r);
232
233                 glColor4f(color[ui][0],
234                           color[ui][1],
235                           color[ui][2], 0.5f);
236
237                 mark_draw();
238             }
239             glPopMatrix();
240         }
241     }
242     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
243 }
244
245 static void game_draw_goals(const struct s_file *fp)
246 {
247     int zi;
248
249     for (zi = 0; zi < fp->zc; zi++)
250     {
251         glPushMatrix();
252         {
253             glTranslatef(fp->zv[zi].p[0],
254                          fp->zv[zi].p[1],
255                          fp->zv[zi].p[2]);
256             flag_draw();
257         }
258         glPopMatrix();
259     }
260 }
261
262 static void game_draw_jumps(const struct s_file *fp)
263 {
264     int ji;
265
266     for (ji = 0; ji < fp->jc; ji++)
267     {
268         glPushMatrix();
269         {
270             glTranslatef(fp->jv[ji].p[0],
271                          fp->jv[ji].p[1],
272                          fp->jv[ji].p[2]);
273
274             glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
275             jump_draw(!jump_e);
276         }
277         glPopMatrix();
278     }
279 }
280
281 static void game_draw_swchs(const struct s_file *fp)
282 {
283     int xi;
284
285     for (xi = 0; xi < fp->xc; xi++)
286     {
287         if (fp->xv[xi].i)
288             continue;
289
290         glPushMatrix();
291         {
292             glTranslatef(fp->xv[xi].p[0],
293                          fp->xv[xi].p[1],
294                          fp->xv[xi].p[2]);
295
296             glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
297             swch_draw(fp->xv[xi].f, fp->xv[xi].e);
298         }
299         glPopMatrix();
300     }
301 }
302
303 /*---------------------------------------------------------------------------*/
304
305 void game_draw(int pose, float t)
306 {
307     static const float a[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
308     static const float s[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
309     static const float e[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
310     static const float h[1] = { 0.0f };
311
312     const float light_p[4] = { 8.f, 32.f, 8.f, 1.f };
313
314     const struct s_file *fp = &file;
315
316     float fov = FOV;
317
318     if (jump_b) fov *= 2.0f * fabsf(jump_dt - 0.5f);
319
320     video_push_persp(fov, 0.1f, FAR_DIST);
321     glPushAttrib(GL_LIGHTING_BIT);
322     glPushMatrix();
323     {
324         float T[16], M[16], v[3], rx, ry;
325
326         m_view(T, view_c, view_p, view_e[1]);
327         m_xps(M, T);
328
329         v_sub(v, view_c, view_p);
330
331         rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
332         ry = V_DEG(fatan2f(+v[0], -v[2]));
333
334         glTranslatef(0.f, 0.f, -v_len(v));
335         glMultMatrixf(M);
336         glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
337
338         /* Center the skybox about the position of the camera. */
339
340         glPushMatrix();
341         {
342             glTranslatef(view_p[0], view_p[1], view_p[2]);
343             back_draw(0);
344         }
345         glPopMatrix();
346
347         glEnable(GL_LIGHT0);
348         glLightfv(GL_LIGHT0, GL_POSITION, light_p);
349
350         /* Draw the floor. */
351
352         sol_draw(fp, 0, 1);
353
354         if (config_get_d(CONFIG_SHADOW) && !pose)
355         {
356             shad_draw_set(fp->uv[ball].p, fp->uv[ball].r);
357             sol_shad(fp);
358             shad_draw_clr();
359         }
360
361         /* Draw the game elements. */
362
363         glEnable(GL_BLEND);
364         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
365
366         if (pose == 0)
367         {
368             game_draw_balls(fp, T, t);
369             game_draw_vect(fp);
370         }
371
372         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   a);
373         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  s);
374         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  e);
375         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, h);
376
377         game_draw_goals(fp);
378
379         glEnable(GL_COLOR_MATERIAL);
380         glDisable(GL_LIGHTING);
381         glDisable(GL_TEXTURE_2D);
382         glDepthMask(GL_FALSE);
383         {
384             game_draw_jumps(fp);
385             game_draw_swchs(fp);
386         }
387         glDepthMask(GL_TRUE);
388         glEnable(GL_TEXTURE_2D);
389         glEnable(GL_LIGHTING);
390         glDisable(GL_COLOR_MATERIAL);
391     }
392     glPopMatrix();
393     glPopAttrib();
394     video_pop_matrix();
395 }
396
397 /*---------------------------------------------------------------------------*/
398
399 void game_update_view(float dt)
400 {
401     const float y[3] = { 0.f, 1.f, 0.f };
402
403     float dy;
404     float dz;
405     float k;
406     float e[3];
407     float d[3];
408     float s = 2.f * dt;
409
410     /* Center the view about the ball. */
411
412     v_cpy(view_c, file.uv[ball].p);
413     v_inv(view_v, file.uv[ball].v);
414
415     switch (config_get_d(CONFIG_CAMERA))
416     {
417     case 2:
418         /* Camera 2: View vector is given by view angle. */
419
420         view_e[2][0] = fsinf(V_RAD(view_a));
421         view_e[2][1] = 0.f;
422         view_e[2][2] = fcosf(V_RAD(view_a));
423
424         s = 1.f;
425         break;
426
427     default:
428         /* View vector approaches the ball velocity vector. */
429
430         v_mad(e, view_v, y, v_dot(view_v, y));
431         v_inv(e, e);
432
433         k = v_dot(view_v, view_v);
434
435         v_sub(view_e[2], view_p, view_c);
436         v_mad(view_e[2], view_e[2], view_v, k * dt * 0.1f);
437     }
438
439     /* Orthonormalize the basis of the view in its new position. */
440
441     v_crs(view_e[0], view_e[1], view_e[2]);
442     v_crs(view_e[2], view_e[0], view_e[1]);
443     v_nrm(view_e[0], view_e[0]);
444     v_nrm(view_e[2], view_e[2]);
445
446     /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */
447
448     v_sub(d, view_p, view_c);
449
450     dy = v_dot(view_e[1], d);
451     dz = v_dot(view_e[2], d);
452
453     dy += (view_dy - dy) * s;
454     dz += (view_dz - dz) * s;
455
456     /* Compute the new view position. */
457
458     view_p[0] = view_p[1] = view_p[2] = 0.f;
459
460     v_mad(view_p, view_c, view_e[1], dy);
461     v_mad(view_p, view_p, view_e[2], dz);
462
463     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
464 }
465
466 static int game_update_state(float dt)
467 {
468     static float t = 0.f;
469
470     struct s_file *fp = &file;
471     float p[3];
472
473     if (dt > 0.f)
474         t += dt;
475     else
476         t = 0.f;
477
478     /* Test for a switch. */
479
480     if (sol_swch_test(fp, ball) == SWCH_TRIGGER)
481         audio_play(AUD_SWITCH, 1.f);
482
483     /* Test for a jump. */
484
485     if (jump_e == 1 && jump_b == 0 && (sol_jump_test(fp, jump_p, ball) ==
486                                        JUMP_TRIGGER))
487     {
488         jump_b  = 1;
489         jump_e  = 0;
490         jump_dt = 0.f;
491
492         audio_play(AUD_JUMP, 1.f);
493     }
494     if (jump_e == 0 && jump_b == 0 && (sol_jump_test(fp, jump_p, ball) ==
495                                        JUMP_OUTSIDE))
496     {
497         jump_e = 1;
498     }
499
500     /* Test for fall-out. */
501
502     if (fp->uv[ball].p[1] < -10.f)
503         return GAME_FALL;
504
505     /* Test for a goal or stop. */
506
507     if (t > 1.f && sol_goal_test(fp, p, ball))
508     {
509         t = 0.f;
510         return GAME_GOAL;
511     }
512
513     if (t > idle_t)
514     {
515         t = 0.f;
516         return GAME_STOP;
517     }
518
519     return GAME_NONE;
520 }
521
522 /*
523  * On  most  hardware, rendering  requires  much  more  computing power  than
524  * physics.  Since  physics takes less time  than graphics, it  make sense to
525  * detach  the physics update  time step  from the  graphics frame  rate.  By
526  * performing multiple physics updates for  each graphics update, we get away
527  * with higher quality physics with little impact on overall performance.
528  *
529  * Toward this  end, we establish a  baseline maximum physics  time step.  If
530  * the measured  frame time  exceeds this  maximum, we cut  the time  step in
531  * half, and  do two updates.  If THIS  time step exceeds the  maximum, we do
532  * four updates.  And  so on.  In this way, the physics  system is allowed to
533  * seek an optimal update rate independent of, yet in integral sync with, the
534  * graphics frame rate.
535  */
536
537 int game_step(const float g[3], float dt)
538 {
539     struct s_file *fp = &file;
540
541     static float s = 0.f;
542     static float t = 0.f;
543
544     float d = 0.f;
545     float b = 0.f;
546     float st = 0.f;
547     int i, n = 1, m = 0;
548
549     s = (7.f * s + dt) / 8.f;
550     t = s;
551
552     if (jump_b)
553     {
554         jump_dt += dt;
555
556         /* Handle a jump. */
557
558         if (0.5 < jump_dt)
559         {
560             fp->uv[ball].p[0] = jump_p[0];
561             fp->uv[ball].p[1] = jump_p[1];
562             fp->uv[ball].p[2] = jump_p[2];
563         }
564         if (1.f < jump_dt)
565             jump_b = 0;
566     }
567     else
568     {
569         /* Run the sim. */
570
571         while (t > MAX_DT && n < MAX_DN)
572         {
573             t /= 2;
574             n *= 2;
575         }
576
577         for (i = 0; i < n; i++)
578         {
579             d = sol_step(fp, g, t, ball, &m);
580
581             if (b < d)
582                 b = d;
583             if (m)
584                 st += t;
585         }
586
587         /* Mix the sound of a ball bounce. */
588
589         if (b > 0.5)
590             audio_play(AUD_BUMP, (float) (b - 0.5) * 2.0f);
591     }
592
593     game_update_view(dt);
594     return game_update_state(st);
595 }
596
597 void game_putt(void)
598 {
599     /*
600      * HACK: The BALL_FUDGE here  guarantees that a putt doesn't drive
601      * the ball  too directly down  toward a lump,  triggering rolling
602      * friction too early and stopping the ball prematurely.
603      */
604
605     file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
606     file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
607     file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
608
609     view_m = 0.f;
610 }
611
612 /*---------------------------------------------------------------------------*/
613
614 void game_set_rot(int d)
615 {
616     view_a += (float) (30.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
617 }
618
619 void game_clr_mag(void)
620 {
621     view_m = 1.f;
622 }
623
624 void game_set_mag(int d)
625 {
626     view_m -= (float) (1.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
627
628     if (view_m < 0.25)
629         view_m = 0.25;
630 }
631
632 void game_set_fly(float k)
633 {
634     struct s_file *fp = &file;
635
636     float  x[3] = { 1.f, 0.f, 0.f };
637     float  y[3] = { 0.f, 1.f, 0.f };
638     float  z[3] = { 0.f, 0.f, 1.f };
639     float c0[3] = { 0.f, 0.f, 0.f };
640     float p0[3] = { 0.f, 0.f, 0.f };
641     float c1[3] = { 0.f, 0.f, 0.f };
642     float p1[3] = { 0.f, 0.f, 0.f };
643     float  v[3];
644
645     v_cpy(view_e[0], x);
646     v_cpy(view_e[1], y);
647     v_sub(view_e[2], fp->uv[ball].p, fp->zv[0].p);
648
649     if (fabs(v_dot(view_e[1], view_e[2])) > 0.999)
650         v_cpy(view_e[2], z);
651
652     v_crs(view_e[0], view_e[1], view_e[2]);
653     v_crs(view_e[2], view_e[0], view_e[1]);
654
655     v_nrm(view_e[0], view_e[0]);
656     v_nrm(view_e[2], view_e[2]);
657
658     /* k = 0.0 view is at the ball. */
659
660     if (fp->uc > 0)
661     {
662         v_cpy(c0, fp->uv[ball].p);
663         v_cpy(p0, fp->uv[ball].p);
664     }
665
666     v_mad(p0, p0, view_e[1], view_dy);
667     v_mad(p0, p0, view_e[2], view_dz);
668
669     /* k = +1.0 view is s_view 0 */
670
671     if (k >= 0 && fp->wc > 0)
672     {
673         v_cpy(p1, fp->wv[0].p);
674         v_cpy(c1, fp->wv[0].q);
675     }
676
677     /* k = -1.0 view is s_view 1 */
678
679     if (k <= 0 && fp->wc > 1)
680     {
681         v_cpy(p1, fp->wv[1].p);
682         v_cpy(c1, fp->wv[1].q);
683     }
684
685     /* Interpolate the views. */
686
687     v_sub(v, p1, p0);
688     v_mad(view_p, p0, v, k * k);
689
690     v_sub(v, c1, c0);
691     v_mad(view_c, c0, v, k * k);
692
693     /* Orthonormalize the view basis. */
694
695     v_sub(view_e[2], view_p, view_c);
696     v_crs(view_e[0], view_e[1], view_e[2]);
697     v_crs(view_e[2], view_e[0], view_e[1]);
698     v_nrm(view_e[0], view_e[0]);
699     v_nrm(view_e[2], view_e[2]);
700
701     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
702 }
703
704 void game_ball(int i)
705 {
706     int ui;
707
708     ball = i;
709
710     jump_e = 1;
711     jump_b = 0;
712
713     for (ui = 0; ui < file.uc; ui++)
714     {
715         file.uv[ui].v[0] = 0.f;
716         file.uv[ui].v[1] = 0.f;
717         file.uv[ui].v[2] = 0.f;
718
719         file.uv[ui].w[0] = 0.f;
720         file.uv[ui].w[1] = 0.f;
721         file.uv[ui].w[2] = 0.f;
722     }
723 }
724
725 void game_get_pos(float p[3], float e[3][3])
726 {
727     v_cpy(p,    file.uv[ball].p);
728     v_cpy(e[0], file.uv[ball].e[0]);
729     v_cpy(e[1], file.uv[ball].e[1]);
730     v_cpy(e[2], file.uv[ball].e[2]);
731 }
732
733 void game_set_pos(float p[3], float e[3][3])
734 {
735     v_cpy(file.uv[ball].p,    p);
736     v_cpy(file.uv[ball].e[0], e[0]);
737     v_cpy(file.uv[ball].e[1], e[1]);
738     v_cpy(file.uv[ball].e[2], e[2]);
739 }
740
741 /*---------------------------------------------------------------------------*/
742