WIP: Vibra support
[neverball] / ball / game_server.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 "vec3.h"
20 #include "item.h"
21 #include "config.h"
22 #include "binary.h"
23 #include "common.h"
24 #ifdef __MAEMO__
25 #include "maemo.h"
26 #endif
27
28 #include "solid_sim.h"
29 #include "solid_all.h"
30 #include "solid_cmd.h"
31
32 #include "game_common.h"
33 #include "game_server.h"
34 #include "game_proxy.h"
35
36 #include "cmd.h"
37
38 /*---------------------------------------------------------------------------*/
39
40 static int server_state = 0;
41
42 static struct s_vary vary;
43
44 static float timer      = 0.f;          /* Clock time                        */
45 static int   timer_down = 1;            /* Timer go up or down?              */
46
47 static int status = GAME_NONE;          /* Outcome of the game               */
48
49 static struct game_tilt tilt;           /* Floor rotation                    */
50 static struct game_view view;           /* Current view                      */
51
52 static float view_k;
53
54 static int   coins  = 0;                /* Collected coins                   */
55 static int   goal_e = 0;                /* Goal enabled flag                 */
56 static float goal_k = 0;                /* Goal animation                    */
57 static int   jump_e = 1;                /* Jumping enabled flag              */
58 static int   jump_b = 0;                /* Jump-in-progress flag             */
59 static float jump_dt;                   /* Jump duration                     */
60 static float jump_p[3];                 /* Jump destination                  */
61 static float jump_w[3];                 /* View destination                  */
62
63 /*---------------------------------------------------------------------------*/
64
65 /*
66  * This is an abstraction of the game's input state.  All input is
67  * encapsulated here, and all references to the input by the game are
68  * made here.
69  */
70
71 struct input
72 {
73     float x;
74     float z;
75     float r;
76     int   c;
77 };
78
79 static struct input input_current;
80
81 static void input_init(void)
82 {
83     input_current.x = 0;
84     input_current.z = 0;
85     input_current.r = 0;
86     input_current.c = 0;
87 }
88
89 static void input_set_x(float x)
90 {
91     if (x < -ANGLE_BOUND) x = -ANGLE_BOUND;
92     if (x >  ANGLE_BOUND) x =  ANGLE_BOUND;
93
94     input_current.x = x;
95 }
96
97 static void input_set_z(float z)
98 {
99     if (z < -ANGLE_BOUND) z = -ANGLE_BOUND;
100     if (z >  ANGLE_BOUND) z =  ANGLE_BOUND;
101
102     input_current.z = z;
103 }
104
105 static void input_set_r(float r)
106 {
107     if (r < -VIEWR_BOUND) r = -VIEWR_BOUND;
108     if (r >  VIEWR_BOUND) r =  VIEWR_BOUND;
109
110     input_current.r = r;
111 }
112
113 static void input_set_c(int c)
114 {
115     input_current.c = c;
116 }
117
118 static float input_get_x(void)
119 {
120     return input_current.x;
121 }
122
123 static float input_get_z(void)
124 {
125     return input_current.z;
126 }
127
128 static float input_get_r(void)
129 {
130     return input_current.r;
131 }
132
133 static int input_get_c(void)
134 {
135     return input_current.c;
136 }
137
138 /*---------------------------------------------------------------------------*/
139
140 /*
141  * Utility functions for preparing the "server" state and events for
142  * consumption by the "client".
143  */
144
145 static union cmd cmd;
146
147 static void game_cmd_map(const char *name, int ver_x, int ver_y)
148 {
149     cmd.type          = CMD_MAP;
150     cmd.map.name      = strdup(name);
151     cmd.map.version.x = ver_x;
152     cmd.map.version.y = ver_y;
153     game_proxy_enq(&cmd);
154 }
155
156 static void game_cmd_eou(void)
157 {
158     cmd.type = CMD_END_OF_UPDATE;
159     game_proxy_enq(&cmd);
160 }
161
162 static void game_cmd_ups(void)
163 {
164     cmd.type  = CMD_UPDATES_PER_SECOND;
165     cmd.ups.n = UPS;
166     game_proxy_enq(&cmd);
167 }
168
169 static void game_cmd_sound(const char *filename, float a)
170 {
171     cmd.type = CMD_SOUND;
172
173     cmd.sound.n = strdup(filename);
174     cmd.sound.a = a;
175
176     game_proxy_enq(&cmd);
177 }
178
179 #define audio_play(s, f) game_cmd_sound((s), (f))
180
181 static void game_cmd_goalopen(void)
182 {
183     cmd.type = CMD_GOAL_OPEN;
184     game_proxy_enq(&cmd);
185 }
186
187 static void game_cmd_updball(void)
188 {
189     cmd.type = CMD_BALL_POSITION;
190     v_cpy(cmd.ballpos.p, vary.uv[0].p);
191     game_proxy_enq(&cmd);
192
193     cmd.type = CMD_BALL_BASIS;
194     v_cpy(cmd.ballbasis.e[0], vary.uv[0].e[0]);
195     v_cpy(cmd.ballbasis.e[1], vary.uv[0].e[1]);
196     game_proxy_enq(&cmd);
197
198     cmd.type = CMD_BALL_PEND_BASIS;
199     v_cpy(cmd.ballpendbasis.E[0], vary.uv[0].E[0]);
200     v_cpy(cmd.ballpendbasis.E[1], vary.uv[0].E[1]);
201     game_proxy_enq(&cmd);
202 }
203
204 static void game_cmd_updview(void)
205 {
206     cmd.type = CMD_VIEW_POSITION;
207     v_cpy(cmd.viewpos.p, view.p);
208     game_proxy_enq(&cmd);
209
210     cmd.type = CMD_VIEW_CENTER;
211     v_cpy(cmd.viewcenter.c, view.c);
212     game_proxy_enq(&cmd);
213
214     cmd.type = CMD_VIEW_BASIS;
215     v_cpy(cmd.viewbasis.e[0], view.e[0]);
216     v_cpy(cmd.viewbasis.e[1], view.e[1]);
217     game_proxy_enq(&cmd);
218 }
219
220 static void game_cmd_ballradius(void)
221 {
222     cmd.type         = CMD_BALL_RADIUS;
223     cmd.ballradius.r = vary.uv[0].r;
224     game_proxy_enq(&cmd);
225 }
226
227 static void game_cmd_init_balls(void)
228 {
229     cmd.type = CMD_CLEAR_BALLS;
230     game_proxy_enq(&cmd);
231
232     cmd.type = CMD_MAKE_BALL;
233     game_proxy_enq(&cmd);
234
235     game_cmd_updball();
236     game_cmd_ballradius();
237 }
238
239 static void game_cmd_init_items(void)
240 {
241     int i;
242
243     cmd.type = CMD_CLEAR_ITEMS;
244     game_proxy_enq(&cmd);
245
246     for (i = 0; i < vary.hc; i++)
247     {
248         cmd.type = CMD_MAKE_ITEM;
249
250         v_cpy(cmd.mkitem.p, vary.hv[i].p);
251
252         cmd.mkitem.t = vary.hv[i].t;
253         cmd.mkitem.n = vary.hv[i].n;
254
255         game_proxy_enq(&cmd);
256     }
257 }
258
259 static void game_cmd_pkitem(int hi)
260 {
261     cmd.type      = CMD_PICK_ITEM;
262     cmd.pkitem.hi = hi;
263     game_proxy_enq(&cmd);
264 }
265
266 static void game_cmd_jump(int e)
267 {
268     cmd.type = e ? CMD_JUMP_ENTER : CMD_JUMP_EXIT;
269     game_proxy_enq(&cmd);
270 }
271
272 static void game_cmd_tiltangles(void)
273 {
274     cmd.type = CMD_TILT_ANGLES;
275
276     cmd.tiltangles.x = tilt.rx;
277     cmd.tiltangles.z = tilt.rz;
278
279     game_proxy_enq(&cmd);
280 }
281
282 static void game_cmd_tiltaxes(void)
283 {
284     cmd.type = CMD_TILT_AXES;
285
286     v_cpy(cmd.tiltaxes.x, tilt.x);
287     v_cpy(cmd.tiltaxes.z, tilt.z);
288
289     game_proxy_enq(&cmd);
290 }
291
292 static void game_cmd_timer(void)
293 {
294     cmd.type    = CMD_TIMER;
295     cmd.timer.t = timer;
296     game_proxy_enq(&cmd);
297 }
298
299 static void game_cmd_coins(void)
300 {
301     cmd.type    = CMD_COINS;
302     cmd.coins.n = coins;
303     game_proxy_enq(&cmd);
304 }
305
306 static void game_cmd_status(void)
307 {
308     cmd.type     = CMD_STATUS;
309     cmd.status.t = status;
310     game_proxy_enq(&cmd);
311 }
312
313 /*---------------------------------------------------------------------------*/
314
315 static int   grow = 0;                  /* Should the ball be changing size? */
316 static float grow_orig = 0;             /* the original ball size            */
317 static float grow_goal = 0;             /* how big or small to get!          */
318 static float grow_t = 0.0;              /* timer for the ball to grow...     */
319 static float grow_strt = 0;             /* starting value for growth         */
320 static int   got_orig = 0;              /* Do we know original ball size?    */
321
322 #define GROW_TIME  0.5f                 /* sec for the ball to get to size.  */
323 #define GROW_BIG   1.5f                 /* large factor                      */
324 #define GROW_SMALL 0.5f                 /* small factor                      */
325
326 static int   grow_state = 0;            /* Current state (values -1, 0, +1)  */
327
328 static void grow_init(const struct s_vary *vary, int type)
329 {
330     if (!got_orig)
331     {
332         grow_orig  = vary->uv->r;
333         grow_goal  = grow_orig;
334         grow_strt  = grow_orig;
335
336         grow_state = 0;
337
338         got_orig   = 1;
339     }
340
341     if (type == ITEM_SHRINK)
342     {
343         switch (grow_state)
344         {
345         case -1:
346             break;
347
348         case  0:
349             audio_play(AUD_SHRINK, 1.f);
350             grow_goal = grow_orig * GROW_SMALL;
351             grow_state = -1;
352             grow = 1;
353             break;
354
355         case +1:
356             audio_play(AUD_SHRINK, 1.f);
357             grow_goal = grow_orig;
358             grow_state = 0;
359             grow = 1;
360             break;
361         }
362     }
363     else if (type == ITEM_GROW)
364     {
365         switch (grow_state)
366         {
367         case -1:
368             audio_play(AUD_GROW, 1.f);
369             grow_goal = grow_orig;
370             grow_state = 0;
371             grow = 1;
372             break;
373
374         case  0:
375             audio_play(AUD_GROW, 1.f);
376             grow_goal = grow_orig * GROW_BIG;
377             grow_state = +1;
378             grow = 1;
379             break;
380
381         case +1:
382             break;
383         }
384     }
385
386     if (grow)
387     {
388         grow_t = 0.0;
389         grow_strt = vary->uv->r;
390     }
391 }
392
393 static void grow_step(const struct s_vary *vary, float dt)
394 {
395     float dr;
396
397     if (!grow)
398         return;
399
400     /* Calculate new size based on how long since you touched the coin... */
401
402     grow_t += dt;
403
404     if (grow_t >= GROW_TIME)
405     {
406         grow = 0;
407         grow_t = GROW_TIME;
408     }
409
410     dr = grow_strt + ((grow_goal-grow_strt) * (1.0f / (GROW_TIME / grow_t)));
411
412     /* No sinking through the floor! Keeps ball's bottom constant. */
413
414     vary->uv->p[1] += (dr - vary->uv->r);
415     vary->uv->r     =  dr;
416
417     game_cmd_ballradius();
418 }
419
420 /*---------------------------------------------------------------------------*/
421
422 static struct lockstep server_step;
423
424 int game_server_init(const char *file_name, int t, int e)
425 {
426     struct { int x, y; } version;
427     int i;
428
429     timer      = (float) t / 100.f;
430     timer_down = (t > 0);
431     coins      = 0;
432     status     = GAME_NONE;
433
434     game_server_free(file_name);
435
436     /* Load SOL data. */
437
438     if (!game_base_load(file_name))
439         return (server_state = 0);
440
441     if (!sol_load_vary(&vary, &game_base))
442     {
443         game_base_free(NULL);
444         return (server_state = 0);
445     }
446
447     server_state = 1;
448
449     /* Get SOL version. */
450
451     version.x = 0;
452     version.y = 0;
453
454     for (i = 0; i < vary.base->dc; i++)
455     {
456         char *k = vary.base->av + vary.base->dv[i].ai;
457         char *v = vary.base->av + vary.base->dv[i].aj;
458
459         if (strcmp(k, "version") == 0)
460             sscanf(v, "%d.%d", &version.x, &version.y);
461     }
462
463     input_init();
464
465     game_tilt_init(&tilt);
466
467     /* Initialize jump and goal states. */
468
469     jump_e = 1;
470     jump_b = 0;
471
472     goal_e = e ? 1    : 0;
473     goal_k = e ? 1.0f : 0.0f;
474
475     /* Initialize the view (and put it at the ball). */
476
477     game_view_fly(&view, &vary, 0.0f);
478     view_k = 1.0f;
479
480     /* Initialize ball size tracking. */
481
482     got_orig = 0;
483     grow = 0;
484
485     /* Initialize simulation. */
486
487     sol_init_sim(&vary);
488     sol_cmd_enq_func(game_proxy_enq);
489
490     /* Send initial update. */
491
492     game_cmd_map(file_name, version.x, version.y);
493     game_cmd_ups();
494     game_cmd_timer();
495
496     if (goal_e)
497         game_cmd_goalopen();
498
499     game_cmd_init_balls();
500     game_cmd_init_items();
501
502     game_cmd_updview();
503     game_cmd_eou();
504
505     /* Reset lockstep state. */
506
507     lockstep_clr(&server_step);
508
509     return server_state;
510 }
511
512 void game_server_free(const char *next)
513 {
514     if (server_state)
515     {
516         sol_quit_sim();
517         sol_free_vary(&vary);
518
519         game_base_free(next);
520
521         server_state = 0;
522     }
523 }
524
525 /*---------------------------------------------------------------------------*/
526
527 static void game_update_view(float dt)
528 {
529     float dc = view.dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
530     float da = input_get_r() * dt * 90.0f;
531     float k;
532
533     float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f };
534     float view_v[3];
535
536     /* Center the view about the ball. */
537
538     v_cpy(view.c, vary.uv->p);
539
540     view_v[0] = -vary.uv->v[0];
541     view_v[1] =  0.0f;
542     view_v[2] = -vary.uv->v[2];
543
544     switch (input_get_c())
545     {
546     case VIEW_LAZY: /* Viewpoint chases the ball position. */
547
548         v_sub(view.e[2], view.p, view.c);
549
550         break;
551
552     case VIEW_MANUAL:  /* View vector is given by view angle. */
553
554         view.e[2][0] = fsinf(V_RAD(view.a));
555         view.e[2][1] = 0.0;
556         view.e[2][2] = fcosf(V_RAD(view.a));
557
558         break;
559
560     case VIEW_CHASE: /* View vector approaches the ball velocity vector. */
561
562         v_sub(view.e[2], view.p, view.c);
563         v_nrm(view.e[2], view.e[2]);
564         v_mad(view.e[2], view.e[2], view_v, v_dot(view_v, view_v) * dt / 4);
565
566         break;
567     }
568
569     /* Apply manual rotation. */
570
571     m_rot(M, Y, V_RAD(da));
572     m_vxfm(v, M, view.e[2]);
573     v_cpy(view.e[2], v);
574
575     /* Orthonormalize the new view reference frame. */
576
577     v_crs(view.e[0], view.e[1], view.e[2]);
578     v_crs(view.e[2], view.e[0], view.e[1]);
579     v_nrm(view.e[0], view.e[0]);
580     v_nrm(view.e[2], view.e[2]);
581
582     /* Compute the new view position. */
583
584     k = 1.0f + v_dot(view.e[2], view_v) / 10.0f;
585
586     view_k = view_k + (k - view_k) * dt;
587
588     if (view_k < 0.5) view_k = 0.5;
589
590     v_scl(v,    view.e[1], view.dp * view_k);
591     v_mad(v, v, view.e[2], view.dz * view_k);
592     v_add(view.p, v, vary.uv->p);
593
594     /* Compute the new view center. */
595
596     v_cpy(view.c, vary.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     game_cmd_updview();
604 }
605
606 static void game_update_time(float dt, int b)
607 {
608     if (goal_e && goal_k < 1.0f)
609         goal_k += dt;
610
611    /* The ticking clock. */
612
613     if (b && timer_down)
614     {
615         if (timer < 600.f)
616             timer -= dt;
617         if (timer < 0.f)
618             timer = 0.f;
619     }
620     else if (b)
621     {
622         timer += dt;
623     }
624
625     if (b) game_cmd_timer();
626 }
627
628 static int game_update_state(int bt)
629 {
630     struct b_goal *zp;
631     int hi;
632
633     float p[3];
634
635     /* Test for an item. */
636
637     if (bt && (hi = sol_item_test(&vary, p, ITEM_RADIUS)) != -1)
638     {
639         struct v_item *hp = vary.hv + hi;
640
641         game_cmd_pkitem(hi);
642
643         grow_init(&vary, hp->t);
644
645         if (hp->t == ITEM_COIN)
646         {
647             coins += hp->n;
648             game_cmd_coins();
649         }
650
651         audio_play(AUD_COIN, 1.f);
652
653         /* Discard item. */
654
655         hp->t = ITEM_NONE;
656     }
657
658     /* Test for a switch. */
659
660     if (sol_swch_test(&vary, 0) == SWCH_TRIGGER)
661         audio_play(AUD_SWITCH, 1.f);
662
663     /* Test for a jump. */
664
665     if (jump_e == 1 && jump_b == 0 && (sol_jump_test(&vary, jump_p, 0) ==
666                                        JUMP_TRIGGER))
667     {
668         jump_b  = 1;
669         jump_e  = 0;
670         jump_dt = 0.f;
671
672         v_sub(jump_w, jump_p, vary.uv->p);
673         v_add(jump_w, view.p, jump_w);
674
675         audio_play(AUD_JUMP, 1.f);
676
677         game_cmd_jump(1);
678     }
679     if (jump_e == 0 && jump_b == 0 && (sol_jump_test(&vary, jump_p, 0) ==
680                                        JUMP_OUTSIDE))
681     {
682         jump_e = 1;
683         game_cmd_jump(0);
684     }
685
686     /* Test for a goal. */
687
688     if (bt && goal_e && (zp = sol_goal_test(&vary, p, 0)))
689     {
690         audio_play(AUD_GOAL, 1.0f);
691         return GAME_GOAL;
692     }
693
694     /* Test for time-out. */
695
696     if (bt && timer_down && timer <= 0.f)
697     {
698         audio_play(AUD_TIME, 1.0f);
699         return GAME_TIME;
700     }
701
702     /* Test for fall-out. */
703
704     if (bt && vary.uv[0].p[1] < vary.base->vv[0].p[1])
705     {
706         audio_play(AUD_FALL, 1.0f);
707         return GAME_FALL;
708     }
709
710     return GAME_NONE;
711 }
712
713 static int game_step(const float g[3], float dt, int bt)
714 {
715     if (server_state)
716     {
717         float h[3];
718
719         /* Smooth jittery or discontinuous input. */
720
721         tilt.rx += (input_get_x() - tilt.rx) * dt / RESPONSE;
722         tilt.rz += (input_get_z() - tilt.rz) * dt / RESPONSE;
723
724         game_tilt_axes(&tilt, view.e);
725
726         game_cmd_tiltaxes();
727         game_cmd_tiltangles();
728
729         grow_step(&vary, dt);
730
731         game_tilt_grav(h, g, &tilt);
732
733         if (jump_b)
734         {
735             jump_dt += dt;
736
737             /* Handle a jump. */
738
739             if (0.5f < jump_dt)
740             {
741                 v_cpy(vary.uv->p, jump_p);
742                 v_cpy(view.p,      jump_w);
743             }
744             if (1.0f < jump_dt)
745                 jump_b = 0;
746         }
747         else
748         {
749             /* Run the sim. */
750
751             float b = sol_step(&vary, h, dt, 0, NULL);
752
753             /* Mix the sound of a ball bounce. */
754
755             if (b > 0.5f)
756             {
757                 float k = (b - 0.5f) * 2.0f;
758
759                 if (got_orig)
760                 {
761                     if      (vary.uv->r > grow_orig) audio_play(AUD_BUMPL, k);
762                     else if (vary.uv->r < grow_orig) audio_play(AUD_BUMPS, k);
763                     else                             audio_play(AUD_BUMPM, k);
764                 }
765                 else audio_play(AUD_BUMPM, k);
766 #ifdef __MAEMO__
767                 maemo_vibra(k);
768 #endif
769             }
770         }
771
772         game_cmd_updball();
773
774         game_update_view(dt);
775         game_update_time(dt, bt);
776
777         return game_update_state(bt);
778     }
779     return GAME_NONE;
780 }
781
782 static void game_server_iter(float dt)
783 {
784     switch (status)
785     {
786     case GAME_GOAL: game_step(GRAVITY_UP, dt, 0); break;
787     case GAME_FALL: game_step(GRAVITY_DN, dt, 0); break;
788
789     case GAME_NONE:
790         if ((status = game_step(GRAVITY_DN, dt, 1)) != GAME_NONE)
791             game_cmd_status();
792         break;
793     }
794
795     game_cmd_eou();
796 }
797
798 static struct lockstep server_step = { game_server_iter, DT };
799
800 void game_server_step(float dt)
801 {
802     lockstep_run(&server_step, dt);
803 }
804
805 float game_server_blend(void)
806 {
807     return lockstep_blend(&server_step);
808 }
809
810 /*---------------------------------------------------------------------------*/
811
812 void game_set_goal(void)
813 {
814     audio_play(AUD_SWITCH, 1.0f);
815     goal_e = 1;
816
817     game_cmd_goalopen();
818 }
819
820 /*---------------------------------------------------------------------------*/
821
822 void game_set_x(float k)
823 {
824     input_set_x(-ANGLE_BOUND * k);
825 }
826
827 void game_set_z(float k)
828 {
829     input_set_z(+ANGLE_BOUND * k);
830 }
831
832 void game_set_ang(float x, float z)
833 {
834     input_set_x(x);
835     input_set_z(z);
836 }
837
838 void game_set_pos(int x, int y)
839 {
840     const float range = ANGLE_BOUND * 2;
841
842     input_set_x(input_get_x() + range * y / config_get_d(CONFIG_MOUSE_SENSE));
843     input_set_z(input_get_z() + range * x / config_get_d(CONFIG_MOUSE_SENSE));
844 }
845
846 void game_set_cam(int c)
847 {
848     input_set_c(c);
849 }
850
851 void game_set_rot(float r)
852 {
853     input_set_r(r);
854 }
855
856 /*---------------------------------------------------------------------------*/