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