Transform default game/locale data paths based on executable name
[neverball] / ball / game_client.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 #include <assert.h>
18
19 #include "glext.h"
20 #include "vec3.h"
21 #include "geom.h"
22 #include "item.h"
23 #include "back.h"
24 #include "part.h"
25 #include "ball.h"
26 #include "image.h"
27 #include "audio.h"
28 #include "solid_gl.h"
29 #include "config.h"
30 #include "video.h"
31
32 #include "game_client.h"
33 #include "game_common.h"
34 #include "game_proxy.h"
35
36 #include "cmd.h"
37
38 /*---------------------------------------------------------------------------*/
39
40 static int client_state = 0;
41
42 static struct s_file file;
43 static struct s_file back;
44
45 static int   reflective;                /* Reflective geometry used?         */
46
47 static float timer      = 0.f;          /* Clock time                        */
48
49 static int status = GAME_NONE;          /* Outcome of the game               */
50
51 static float game_rx;                   /* Floor rotation about X axis       */
52 static float game_rz;                   /* Floor rotation about Z axis       */
53
54 static float view_a;                    /* Ideal view rotation about Y axis  */
55 static float view_fov;                  /* Field of view                     */
56
57 static float view_c[3];                 /* Current view center               */
58 static float view_p[3];                 /* Current view position             */
59 static float view_e[3][3];              /* Current view reference frame      */
60
61 static int   coins  = 0;                /* Collected coins                   */
62 static int   goal_e = 0;                /* Goal enabled flag                 */
63 static float goal_k = 0;                /* Goal animation                    */
64
65 static int   jump_e = 1;                /* Jumping enabled flag              */
66 static int   jump_b = 0;                /* Jump-in-progress flag             */
67 static float jump_dt;                   /* Jump duration                     */
68
69 static float fade_k = 0.0;              /* Fade in/out level                 */
70 static float fade_d = 0.0;              /* Fade in/out direction             */
71
72 static int ups;                         /* Updates per second                */
73 static int first_update;                /* First update flag                 */
74 static int curr_ball;                   /* Current ball index                */
75
76 /*---------------------------------------------------------------------------*/
77
78 static void game_run_cmd(const union cmd *cmd)
79 {
80     static const float gup[] = { 0.0f, +9.8f, 0.0f };
81     static const float gdn[] = { 0.0f, -9.8f, 0.0f };
82
83     float f[3];
84
85     if (client_state)
86     {
87         struct s_item *hp;
88         struct s_ball *up;
89
90         float dt;
91         int i;
92
93         switch (cmd->type)
94         {
95         case CMD_END_OF_UPDATE:
96             if (first_update)
97             {
98                 first_update = 0;
99                 break;
100             }
101
102             /* Compute gravity for particle effects. */
103
104             if (status == GAME_GOAL)
105                 game_comp_grav(f, gup, view_a, game_rx, game_rz);
106             else
107                 game_comp_grav(f, gdn, view_a, game_rx, game_rz);
108
109             /* Step particle, goal and jump effects. */
110
111             if (ups > 0)
112             {
113                 dt = 1.0f / (float) ups;
114
115                 if (goal_e && goal_k < 1.0f)
116                     goal_k += dt;
117
118                 if (jump_b)
119                 {
120                     jump_dt += dt;
121
122                     if (1.0f < jump_dt)
123                         jump_b = 0;
124                 }
125
126                 part_step(f, dt);
127             }
128
129             break;
130
131         case CMD_MAKE_BALL:
132             /* Allocate a new ball and mark it as the current ball. */
133
134             if ((up = realloc(file.uv, sizeof (*up) * (file.uc + 1))))
135             {
136                 file.uv = up;
137                 curr_ball = file.uc;
138                 file.uc++;
139             }
140             break;
141
142         case CMD_MAKE_ITEM:
143             /* Allocate and initialise a new item. */
144
145             if ((hp = realloc(file.hv, sizeof (*hp) * (file.hc + 1))))
146             {
147                 struct s_item h;
148
149                 v_cpy(h.p, cmd->mkitem.p);
150
151                 h.t = cmd->mkitem.t;
152                 h.n = cmd->mkitem.n;
153
154                 file.hv          = hp;
155                 file.hv[file.hc] = h;
156                 file.hc++;
157             }
158
159             break;
160
161         case CMD_PICK_ITEM:
162             /* Set up particle effects and discard the item. */
163
164             assert(cmd->pkitem.hi < file.hc);
165
166             hp = &file.hv[cmd->pkitem.hi];
167
168             item_color(hp, f);
169             part_burst(hp->p, f);
170
171             hp->t = ITEM_NONE;
172
173             break;
174
175         case CMD_ROTATE:
176             game_rx = cmd->rotate.x;
177             game_rz = cmd->rotate.z;
178             break;
179
180         case CMD_SOUND:
181             /* Play the sound, then free its file name. */
182
183             if (cmd->sound.n)
184             {
185                 audio_play(cmd->sound.n, cmd->sound.a);
186
187                 /*
188                  * FIXME Command memory management should be done
189                  * elsewhere and done properly.
190                  */
191
192                 free(cmd->sound.n);
193             }
194             break;
195
196         case CMD_TIMER:
197             timer = cmd->timer.t;
198             break;
199
200         case CMD_STATUS:
201             status = cmd->status.t;
202             break;
203
204         case CMD_COINS:
205             coins = cmd->coins.n;
206             break;
207
208         case CMD_JUMP_ENTER:
209             jump_b  = 1;
210             jump_e  = 0;
211             jump_dt = 0.0f;
212             break;
213
214         case CMD_JUMP_EXIT:
215             jump_e = 1;
216             break;
217
218         case CMD_BODY_PATH:
219             file.bv[cmd->bodypath.bi].pi = cmd->bodypath.pi;
220             break;
221
222         case CMD_BODY_TIME:
223             file.bv[cmd->bodytime.bi].t = cmd->bodytime.t;
224             break;
225
226         case CMD_GOAL_OPEN:
227             /*
228              * Enable the goal and make sure it's fully visible if
229              * this is the first update.
230              */
231
232             if (!goal_e)
233             {
234                 goal_e = 1;
235                 goal_k = first_update ? 1.0f : 0.0f;
236             }
237             break;
238
239         case CMD_SWCH_ENTER:
240             file.xv[cmd->swchenter.xi].e = 1;
241             break;
242
243         case CMD_SWCH_TOGGLE:
244             file.xv[cmd->swchtoggle.xi].f = !file.xv[cmd->swchtoggle.xi].f;
245             break;
246
247         case CMD_SWCH_EXIT:
248             file.xv[cmd->swchexit.xi].e = 0;
249             break;
250
251         case CMD_UPDATES_PER_SECOND:
252             ups = cmd->ups.n;
253             break;
254
255         case CMD_BALL_RADIUS:
256             file.uv[curr_ball].r = cmd->ballradius.r;
257             break;
258
259         case CMD_CLEAR_ITEMS:
260             if (file.hv)
261             {
262                 free(file.hv);
263                 file.hv = NULL;
264             }
265             file.hc = 0;
266             break;
267
268         case CMD_CLEAR_BALLS:
269             if (file.uv)
270             {
271                 free(file.uv);
272                 file.uv = NULL;
273             }
274             file.uc = 0;
275             break;
276
277         case CMD_BALL_POSITION:
278             v_cpy(file.uv[curr_ball].p, cmd->ballpos.p);
279             break;
280
281         case CMD_BALL_BASIS:
282             v_cpy(file.uv[curr_ball].e[0], cmd->ballbasis.e[0]);
283             v_cpy(file.uv[curr_ball].e[1], cmd->ballbasis.e[1]);
284
285             v_crs(file.uv[curr_ball].e[2],
286                   file.uv[curr_ball].e[0],
287                   file.uv[curr_ball].e[1]);
288             break;
289
290         case CMD_BALL_PEND_BASIS:
291             v_cpy(file.uv[curr_ball].E[0], cmd->ballpendbasis.E[0]);
292             v_cpy(file.uv[curr_ball].E[1], cmd->ballpendbasis.E[1]);
293
294             v_crs(file.uv[curr_ball].E[2],
295                   file.uv[curr_ball].E[0],
296                   file.uv[curr_ball].E[1]);
297             break;
298
299         case CMD_VIEW_POSITION:
300             v_cpy(view_p, cmd->viewpos.p);
301             break;
302
303         case CMD_VIEW_CENTER:
304             v_cpy(view_c, cmd->viewcenter.c);
305             break;
306
307         case CMD_VIEW_BASIS:
308             v_cpy(view_e[0], cmd->viewbasis.e[0]);
309             v_cpy(view_e[1], cmd->viewbasis.e[1]);
310
311             v_crs(view_e[2], view_e[0], view_e[1]);
312
313             view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
314
315             break;
316
317         case CMD_CURRENT_BALL:
318             curr_ball = cmd->currball.ui;
319             break;
320
321         case CMD_PATH_FLAG:
322             file.pv[cmd->pathflag.pi].f = cmd->pathflag.f;
323             break;
324
325         case CMD_STEP_SIMULATION:
326             /*
327              * Simulate body motion.
328              *
329              * This is done on the client side due to replay file size
330              * concerns and isn't done as part of CMD_END_OF_UPDATE to
331              * match the server state as closely as possible.  Body
332              * time is still synchronised with the server on a
333              * semi-regular basis and path indices are handled through
334              * CMD_BODY_PATH, thus this code doesn't need to be as
335              * sophisticated as sol_body_step.
336              */
337
338             dt = cmd->stepsim.dt;
339
340             for (i = 0; i < file.bc; i++)
341             {
342                 struct s_body *bp = file.bv + i;
343                 struct s_path *pp = file.pv + bp->pi;
344
345                 if (bp->pi >= 0 && pp->f)
346                     bp->t += dt;
347             }
348             break;
349
350         case CMD_NONE:
351         case CMD_MAX:
352             break;
353         }
354     }
355 }
356
357 void game_client_step(FILE *demo_fp)
358 {
359     union cmd *cmdp;
360
361     while ((cmdp = game_proxy_deq()))
362     {
363         /*
364          * Note: cmd_put is called first here because game_run_cmd
365          * frees the filename of CMD_SOUND.
366          */
367
368         if (demo_fp)
369             cmd_put(demo_fp, cmdp);
370
371         game_run_cmd(cmdp);
372
373         free(cmdp);
374     }
375 }
376
377 /*---------------------------------------------------------------------------*/
378
379 int  game_client_init(const char *file_name)
380 {
381     char *back_name = NULL, *grad_name = NULL;
382     int i;
383
384     coins  = 0;
385     status = GAME_NONE;
386
387     if (client_state)
388         game_client_free();
389
390     if (!sol_load_gl(&file, config_data(file_name),
391                      config_get_d(CONFIG_TEXTURES),
392                      config_get_d(CONFIG_SHADOW)))
393         return (client_state = 0);
394
395     reflective = sol_reflective(&file);
396
397     client_state = 1;
398
399     game_rx = 0.0f;
400     game_rz = 0.0f;
401
402     /* Initialize jump and goal states. */
403
404     jump_e = 1;
405     jump_b = 0;
406
407     goal_e = 0;
408     goal_k = 0.0f;
409
410     /* Initialise the level, background, particles, fade, and view. */
411
412     fade_k =  1.0f;
413     fade_d = -2.0f;
414
415     for (i = 0; i < file.dc; i++)
416     {
417         char *k = file.av + file.dv[i].ai;
418         char *v = file.av + file.dv[i].aj;
419
420         if (strcmp(k, "back") == 0) back_name = v;
421         if (strcmp(k, "grad") == 0) grad_name = v;
422     }
423
424     part_reset(GOAL_HEIGHT, JUMP_HEIGHT);
425
426     view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
427
428     ups          = 0;
429     first_update = 1;
430
431     back_init(grad_name, config_get_d(CONFIG_GEOMETRY));
432     sol_load_gl(&back, config_data(back_name),
433                 config_get_d(CONFIG_TEXTURES), 0);
434
435     return client_state;
436 }
437
438 void game_client_free(void)
439 {
440     if (client_state)
441     {
442         game_proxy_clr();
443         sol_free_gl(&file);
444         sol_free_gl(&back);
445         back_free();
446     }
447     client_state = 0;
448 }
449
450 /*---------------------------------------------------------------------------*/
451
452 int curr_clock(void)
453 {
454     return (int) (timer * 100.f);
455 }
456
457 int curr_coins(void)
458 {
459     return coins;
460 }
461
462 int curr_status(void)
463 {
464     return status;
465 }
466
467 /*---------------------------------------------------------------------------*/
468
469 static void game_draw_balls(const struct s_file *fp,
470                             const float *bill_M, float t)
471 {
472     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
473
474     float ball_M[16];
475     float pend_M[16];
476
477     m_basis(ball_M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
478     m_basis(pend_M, fp->uv[0].E[0], fp->uv[0].E[1], fp->uv[0].E[2]);
479
480     glPushAttrib(GL_LIGHTING_BIT);
481     glPushMatrix();
482     {
483         glTranslatef(fp->uv[0].p[0],
484                      fp->uv[0].p[1] + BALL_FUDGE,
485                      fp->uv[0].p[2]);
486         glScalef(fp->uv[0].r,
487                  fp->uv[0].r,
488                  fp->uv[0].r);
489
490         glColor4fv(c);
491         ball_draw(ball_M, pend_M, bill_M, t);
492     }
493     glPopMatrix();
494     glPopAttrib();
495 }
496
497 static void game_draw_items(const struct s_file *fp, float t)
498 {
499     float r = 360.f * t;
500     int hi;
501
502     glPushAttrib(GL_LIGHTING_BIT);
503     {
504         item_push(ITEM_COIN);
505         {
506             for (hi = 0; hi < fp->hc; hi++)
507
508                 if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
509                 {
510                     glPushMatrix();
511                     {
512                         glTranslatef(fp->hv[hi].p[0],
513                                      fp->hv[hi].p[1],
514                                      fp->hv[hi].p[2]);
515                         glRotatef(r, 0.0f, 1.0f, 0.0f);
516                         item_draw(&fp->hv[hi], r);
517                     }
518                     glPopMatrix();
519                 }
520         }
521         item_pull();
522
523         item_push(ITEM_SHRINK);
524         {
525             for (hi = 0; hi < fp->hc; hi++)
526
527                 if (fp->hv[hi].t == ITEM_SHRINK)
528                 {
529                     glPushMatrix();
530                     {
531                         glTranslatef(fp->hv[hi].p[0],
532                                      fp->hv[hi].p[1],
533                                      fp->hv[hi].p[2]);
534                         glRotatef(r, 0.0f, 1.0f, 0.0f);
535                         item_draw(&fp->hv[hi], r);
536                     }
537                     glPopMatrix();
538                 }
539         }
540         item_pull();
541
542         item_push(ITEM_GROW);
543         {
544             for (hi = 0; hi < fp->hc; hi++)
545
546                 if (fp->hv[hi].t == ITEM_GROW)
547                 {
548                     glPushMatrix();
549                     {
550                         glTranslatef(fp->hv[hi].p[0],
551                                      fp->hv[hi].p[1],
552                                      fp->hv[hi].p[2]);
553                         glRotatef(r, 0.0f, 1.0f, 0.0f);
554                         item_draw(&fp->hv[hi], r);
555                     }
556                     glPopMatrix();
557                 }
558         }
559         item_pull();
560     }
561     glPopAttrib();
562 }
563
564 static void game_draw_goals(const struct s_file *fp, const float *M, float t)
565 {
566     if (goal_e)
567     {
568         int zi;
569
570         /* Draw the goal particles. */
571
572         glEnable(GL_TEXTURE_2D);
573         {
574             for (zi = 0; zi < fp->zc; zi++)
575             {
576                 glPushMatrix();
577                 {
578                     glTranslatef(fp->zv[zi].p[0],
579                                  fp->zv[zi].p[1],
580                                  fp->zv[zi].p[2]);
581
582                     part_draw_goal(M, fp->zv[zi].r, goal_k, t);
583                 }
584                 glPopMatrix();
585             }
586         }
587         glDisable(GL_TEXTURE_2D);
588
589         /* Draw the goal column. */
590
591         for (zi = 0; zi < fp->zc; zi++)
592         {
593             glPushMatrix();
594             {
595                 glTranslatef(fp->zv[zi].p[0],
596                              fp->zv[zi].p[1],
597                              fp->zv[zi].p[2]);
598
599                 glScalef(fp->zv[zi].r,
600                          goal_k,
601                          fp->zv[zi].r);
602
603                 goal_draw();
604             }
605             glPopMatrix();
606         }
607     }
608 }
609
610 static void game_draw_jumps(const struct s_file *fp, const float *M, float t)
611 {
612     int ji;
613
614     glEnable(GL_TEXTURE_2D);
615     {
616         for (ji = 0; ji < fp->jc; ji++)
617         {
618             glPushMatrix();
619             {
620                 glTranslatef(fp->jv[ji].p[0],
621                              fp->jv[ji].p[1],
622                              fp->jv[ji].p[2]);
623
624                 part_draw_jump(M, fp->jv[ji].r, 1.0f, t);
625             }
626             glPopMatrix();
627         }
628     }
629     glDisable(GL_TEXTURE_2D);
630
631     for (ji = 0; ji < fp->jc; ji++)
632     {
633         glPushMatrix();
634         {
635             glTranslatef(fp->jv[ji].p[0],
636                          fp->jv[ji].p[1],
637                          fp->jv[ji].p[2]);
638             glScalef(fp->jv[ji].r,
639                      1.0f,
640                      fp->jv[ji].r);
641
642             jump_draw(!jump_e);
643         }
644         glPopMatrix();
645     }
646 }
647
648 static void game_draw_swchs(const struct s_file *fp)
649 {
650     int xi;
651
652     for (xi = 0; xi < fp->xc; xi++)
653     {
654         if (fp->xv[xi].i)
655             continue;
656
657         glPushMatrix();
658         {
659             glTranslatef(fp->xv[xi].p[0],
660                          fp->xv[xi].p[1],
661                          fp->xv[xi].p[2]);
662             glScalef(fp->xv[xi].r,
663                      1.0f,
664                      fp->xv[xi].r);
665
666             swch_draw(fp->xv[xi].f, fp->xv[xi].e);
667         }
668         glPopMatrix();
669     }
670 }
671
672 /*---------------------------------------------------------------------------*/
673
674 static void game_draw_tilt(int d)
675 {
676     const float *ball_p = file.uv->p;
677
678     /* Rotate the environment about the position of the ball. */
679
680     glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
681     glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
682     glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
683     glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
684 }
685
686 static void game_refl_all(void)
687 {
688     glPushMatrix();
689     {
690         game_draw_tilt(1);
691
692         /* Draw the floor. */
693
694         sol_refl(&file);
695     }
696     glPopMatrix();
697 }
698
699 /*---------------------------------------------------------------------------*/
700
701 static void game_draw_light(void)
702 {
703     const float light_p[2][4] = {
704         { -8.0f, +32.0f, -8.0f, 0.0f },
705         { +8.0f, +32.0f, +8.0f, 0.0f },
706     };
707     const float light_c[2][4] = {
708         { 1.0f, 0.8f, 0.8f, 1.0f },
709         { 0.8f, 1.0f, 0.8f, 1.0f },
710     };
711
712     /* Configure the lighting. */
713
714     glEnable(GL_LIGHT0);
715     glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
716     glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_c[0]);
717     glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
718
719     glEnable(GL_LIGHT1);
720     glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
721     glLightfv(GL_LIGHT1, GL_DIFFUSE,  light_c[1]);
722     glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
723 }
724
725 static void game_draw_back(int pose, int d, float t)
726 {
727     glPushMatrix();
728     {
729         if (d < 0)
730         {
731             glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
732             glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
733         }
734
735         glTranslatef(view_p[0], view_p[1] * d, view_p[2]);
736
737         if (config_get_d(CONFIG_BACKGROUND))
738         {
739             /* Draw all background layers back to front. */
740
741             sol_back(&back, BACK_DIST, FAR_DIST,  t);
742             back_draw(0);
743             sol_back(&back,         0, BACK_DIST, t);
744         }
745         else back_draw(0);
746     }
747     glPopMatrix();
748 }
749
750 static void game_clip_refl(int d)
751 {
752     /* Fudge to eliminate the floor from reflection. */
753
754     GLdouble e[4], k = -0.00001;
755
756     e[0] = 0;
757     e[1] = 1;
758     e[2] = 0;
759     e[3] = k;
760
761     glClipPlane(GL_CLIP_PLANE0, e);
762 }
763
764 static void game_clip_ball(int d, const float *p)
765 {
766     GLdouble r, c[3], pz[4], nz[4];
767
768     /* Compute the plane giving the front of the ball, as seen from view_p. */
769
770     c[0] = p[0];
771     c[1] = p[1] * d;
772     c[2] = p[2];
773
774     pz[0] = view_p[0] - c[0];
775     pz[1] = view_p[1] - c[1];
776     pz[2] = view_p[2] - c[2];
777
778     r = sqrt(pz[0] * pz[0] + pz[1] * pz[1] + pz[2] * pz[2]);
779
780     pz[0] /= r;
781     pz[1] /= r;
782     pz[2] /= r;
783     pz[3] = -(pz[0] * c[0] +
784               pz[1] * c[1] +
785               pz[2] * c[2]);
786
787     /* Find the plane giving the back of the ball, as seen from view_p. */
788
789     nz[0] = -pz[0];
790     nz[1] = -pz[1];
791     nz[2] = -pz[2];
792     nz[3] = -pz[3];
793
794     /* Reflect these planes as necessary, and store them in the GL state. */
795
796     pz[1] *= d;
797     nz[1] *= d;
798
799     glClipPlane(GL_CLIP_PLANE1, nz);
800     glClipPlane(GL_CLIP_PLANE2, pz);
801 }
802
803 static void game_draw_fore(int pose, const float *M, int d, float t)
804 {
805     const float *ball_p = file.uv->p;
806     const float  ball_r = file.uv->r;
807
808     glPushMatrix();
809     {
810         /* Rotate the environment about the position of the ball. */
811
812         game_draw_tilt(d);
813
814         /* Compute clipping planes for reflection and ball facing. */
815
816         game_clip_refl(d);
817         game_clip_ball(d, ball_p);
818
819         if (d < 0)
820             glEnable(GL_CLIP_PLANE0);
821
822         if (pose)
823             sol_draw(&file, 0, 1);
824         else
825         {
826             /* Draw the coins. */
827
828             game_draw_items(&file, t);
829
830             /* Draw the floor. */
831
832             sol_draw(&file, 0, 1);
833
834             /* Draw the ball shadow. */
835
836             if (d > 0 && config_get_d(CONFIG_SHADOW))
837             {
838                 shad_draw_set(ball_p, ball_r);
839                 sol_shad(&file);
840                 shad_draw_clr();
841             }
842
843             /* Draw the ball. */
844
845             game_draw_balls(&file, M, t);
846         }
847
848         /* Draw the particles and light columns. */
849
850         glEnable(GL_COLOR_MATERIAL);
851         glDisable(GL_LIGHTING);
852         glDepthMask(GL_FALSE);
853         {
854             glColor3f(1.0f, 1.0f, 1.0f);
855
856             sol_bill(&file, M, t);
857             part_draw_coin(M, t);
858
859             glDisable(GL_TEXTURE_2D);
860             {
861                 game_draw_goals(&file, M, t);
862                 game_draw_jumps(&file, M, t);
863                 game_draw_swchs(&file);
864             }
865             glEnable(GL_TEXTURE_2D);
866
867             glColor3f(1.0f, 1.0f, 1.0f);
868         }
869         glDepthMask(GL_TRUE);
870         glEnable(GL_LIGHTING);
871         glDisable(GL_COLOR_MATERIAL);
872
873         if (d < 0)
874             glDisable(GL_CLIP_PLANE0);
875     }
876     glPopMatrix();
877 }
878
879 void game_draw(int pose, float t)
880 {
881     float fov = view_fov;
882
883     if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
884
885     if (client_state)
886     {
887         video_push_persp(fov, 0.1f, FAR_DIST);
888         glPushMatrix();
889         {
890             float T[16], U[16], M[16], v[3];
891
892             /* Compute direct and reflected view bases. */
893
894             v[0] = +view_p[0];
895             v[1] = -view_p[1];
896             v[2] = +view_p[2];
897
898             m_view(T, view_c, view_p, view_e[1]);
899             m_view(U, view_c, v,      view_e[1]);
900
901             m_xps(M, T);
902
903             /* Apply current the view. */
904
905             v_sub(v, view_c, view_p);
906
907             glTranslatef(0.f, 0.f, -v_len(v));
908             glMultMatrixf(M);
909             glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
910
911             if (reflective && config_get_d(CONFIG_REFLECTION))
912             {
913                 glEnable(GL_STENCIL_TEST);
914                 {
915                     /* Draw the mirrors only into the stencil buffer. */
916
917                     glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
918                     glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
919                     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
920                     glDepthMask(GL_FALSE);
921
922                     game_refl_all();
923
924                     glDepthMask(GL_TRUE);
925                     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
926                     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
927                     glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
928
929                     /* Draw the scene reflected into color and depth buffers. */
930
931                     glFrontFace(GL_CW);
932                     glPushMatrix();
933                     {
934                         glScalef(+1.0f, -1.0f, +1.0f);
935
936                         game_draw_light();
937                         game_draw_back(pose,    -1, t);
938                         game_draw_fore(pose, U, -1, t);
939                     }
940                     glPopMatrix();
941                     glFrontFace(GL_CCW);
942                 }
943                 glDisable(GL_STENCIL_TEST);
944             }
945
946             /* Draw the scene normally. */
947
948             game_draw_light();
949             game_refl_all();
950             game_draw_back(pose,    +1, t);
951             game_draw_fore(pose, T, +1, t);
952         }
953         glPopMatrix();
954         video_pop_matrix();
955
956         /* Draw the fade overlay. */
957
958         fade_draw(fade_k);
959     }
960 }
961
962 /*---------------------------------------------------------------------------*/
963
964 void game_look(float phi, float theta)
965 {
966     view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
967     view_c[1] = view_p[1] +                       fsinf(V_RAD(phi));
968     view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
969 }
970
971 /*---------------------------------------------------------------------------*/
972
973 void game_kill_fade(void)
974 {
975     fade_k = 0.0f;
976     fade_d = 0.0f;
977 }
978
979 void game_step_fade(float dt)
980 {
981     if ((fade_k < 1.0f && fade_d > 0.0f) ||
982         (fade_k > 0.0f && fade_d < 0.0f))
983         fade_k += fade_d * dt;
984
985     if (fade_k < 0.0f)
986     {
987         fade_k = 0.0f;
988         fade_d = 0.0f;
989     }
990     if (fade_k > 1.0f)
991     {
992         fade_k = 1.0f;
993         fade_d = 0.0f;
994     }
995 }
996
997 void game_fade(float d)
998 {
999     fade_d = d;
1000 }
1001
1002 /*---------------------------------------------------------------------------*/
1003
1004 const struct s_file *game_client_file(void)
1005 {
1006     return &file;
1007 }
1008
1009 /*---------------------------------------------------------------------------*/