tennis.map: Texture tweak
[neverball] / ball / st_demo.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 <string.h>
16
17 #include "gui.h"
18 #include "hud.h"
19 #include "set.h"
20 #include "demo.h"
21 #include "progress.h"
22 #include "audio.h"
23 #include "config.h"
24 #include "util.h"
25 #include "common.h"
26 #include "demo_dir.h"
27 #include "speed.h"
28
29 #include "game_common.h"
30 #include "game_server.h"
31 #include "game_client.h"
32
33 #include "st_demo.h"
34 #include "st_title.h"
35 #include "st_shared.h"
36
37 /*---------------------------------------------------------------------------*/
38
39 #define DEMO_LINE 4
40 #define DEMO_STEP 8
41
42 static Array items;
43
44 static int first = 0;
45 static int total = 0;
46 static int last  = 0;
47
48 static int last_viewed = 0;
49
50 /*---------------------------------------------------------------------------*/
51
52 static int demo_action(int i)
53 {
54     audio_play(AUD_MENU, 1.0f);
55
56     switch (i)
57     {
58     case GUI_BACK:
59         return goto_state(&st_title);
60
61     case GUI_NEXT:
62         first += DEMO_STEP;
63         return goto_state(&st_demo);
64         break;
65
66     case GUI_PREV:
67         first -= DEMO_STEP;
68         return goto_state(&st_demo);
69         break;
70
71     case GUI_NULL:
72         return 1;
73         break;
74
75     default:
76         if (progress_replay(DIR_ITEM_GET(items, i)->path))
77         {
78             last_viewed = i;
79             demo_play_goto(0);
80             return goto_state(&st_demo_play);
81         }
82         break;
83     }
84     return 1;
85 }
86
87 /*---------------------------------------------------------------------------*/
88
89 static struct thumb
90 {
91     int item;
92     int shot;
93     int name;
94 } thumbs[DEMO_STEP];
95
96 static int gui_demo_thumbs(int id)
97 {
98     int w = config_get_d(CONFIG_WIDTH);
99     int h = config_get_d(CONFIG_HEIGHT);
100
101     int jd, kd, ld;
102     int i, j;
103
104     struct thumb *thumb;
105
106     if ((jd = gui_varray(id)))
107         for (i = first; i < first + DEMO_STEP; i += DEMO_LINE)
108             if ((kd = gui_harray(jd)))
109             {
110                 for (j = i + DEMO_LINE - 1; j >= i; j--)
111                 {
112                     thumb = &thumbs[j % DEMO_STEP];
113
114                     thumb->item = j;
115
116                     if (j < total)
117                     {
118                         if ((ld = gui_vstack(kd)))
119                         {
120                             gui_space(ld);
121
122                             thumb->shot = gui_image(ld, " ", w / 6, h / 6);
123                             thumb->name = gui_state(ld, " ", GUI_SML, j, 0);
124
125                             gui_set_trunc(thumb->name, TRUNC_TAIL);
126
127                             gui_active(ld, j, 0);
128                         }
129                     }
130                     else
131                     {
132                         gui_space(kd);
133
134                         thumb->shot = 0;
135                         thumb->name = 0;
136                     }
137                 }
138             }
139
140     return jd;
141 }
142
143 static void gui_demo_update_thumbs(void)
144 {
145     struct dir_item *item;
146     struct demo *demo;
147     int i;
148
149     for (i = 0; i < ARRAYSIZE(thumbs) && thumbs[i].shot && thumbs[i].name; i++)
150     {
151         item = DIR_ITEM_GET(items, thumbs[i].item);
152         demo = item->data;
153
154         gui_set_image(thumbs[i].shot, demo ? demo->shot : "");
155         gui_set_label(thumbs[i].name, demo ? demo->name : base_name(item->path));
156     }
157 }
158
159 static int name_id;
160 static int time_id;
161 static int coin_id;
162 static int date_id;
163 static int status_id;
164 static int player_id;
165
166 static int gui_demo_status(int id)
167 {
168     const char *status;
169     int jd, kd, ld;
170     int s;
171
172     /* Find the longest status string. */
173
174     for (status = "", s = GAME_NONE; s < GAME_MAX; s++)
175         if (strlen(status_to_str(s)) > strlen(status))
176             status = status_to_str(s);
177
178     /* Build info bar with dummy values. */
179
180     if ((jd = gui_hstack(id)))
181     {
182         gui_filler(jd);
183
184         if ((kd = gui_hstack(jd)))
185         {
186             if ((ld = gui_vstack(kd)))
187             {
188                 gui_filler(ld);
189
190                 time_id   = gui_clock(ld, 35000,  GUI_SML, GUI_NE);
191                 coin_id   = gui_count(ld, 100,    GUI_SML, 0);
192                 status_id = gui_label(ld, status, GUI_SML, GUI_SE,
193                                       gui_red, gui_red);
194
195                 gui_filler(ld);
196             }
197
198             if ((ld = gui_vstack(kd)))
199             {
200                 gui_filler(ld);
201
202                 gui_label(ld, _("Time"),   GUI_SML, GUI_NW, gui_wht, gui_wht);
203                 gui_label(ld, _("Coins"),  GUI_SML, 0,      gui_wht, gui_wht);
204                 gui_label(ld, _("Status"), GUI_SML, GUI_SW, gui_wht, gui_wht);
205
206                 gui_filler(ld);
207             }
208         }
209
210         gui_space(jd);
211
212         if ((kd = gui_vstack(jd)))
213         {
214             gui_filler(kd);
215
216             name_id   = gui_label(kd, " ", GUI_SML, GUI_NE, 0, 0);
217             player_id = gui_label(kd, " ", GUI_SML, 0,      0, 0);
218             date_id   = gui_label(kd, date_to_str(time(NULL)),
219                                   GUI_SML, GUI_SE, 0, 0);
220
221             gui_filler(kd);
222
223             gui_set_trunc(name_id,   TRUNC_TAIL);
224             gui_set_trunc(player_id, TRUNC_TAIL);
225         }
226
227         if ((kd = gui_vstack(jd)))
228         {
229             gui_filler(kd);
230
231             gui_label(kd, _("Replay"), GUI_SML, GUI_NW, gui_wht, gui_wht);
232             gui_label(kd, _("Player"), GUI_SML, 0,      gui_wht, gui_wht);
233             gui_label(kd, _("Date"),   GUI_SML, GUI_SW, gui_wht, gui_wht);
234
235             gui_filler(kd);
236         }
237
238         gui_filler(jd);
239     }
240
241     return jd;
242 }
243
244 static void gui_demo_update_status(int i)
245 {
246     const struct demo *d;
247
248     if (!total)
249         return;
250
251     d = DEMO_GET(items, i < total ? i : 0);
252
253     if (!d)
254         return;
255
256     gui_set_label(name_id,   d->name);
257     gui_set_label(date_id,   date_to_str(d->date));
258     gui_set_label(player_id, d->player);
259
260     if (d->status == GAME_GOAL)
261         gui_set_color(status_id, gui_grn, gui_grn);
262     else
263         gui_set_color(status_id, gui_red, gui_red);
264
265     gui_set_label(status_id, status_to_str(d->status));
266     gui_set_count(coin_id, d->coins);
267     gui_set_clock(time_id, d->timer);
268 }
269
270 /*---------------------------------------------------------------------------*/
271
272 static int demo_gui(void)
273 {
274     int id, jd;
275
276     id = gui_vstack(0);
277
278     if (total)
279     {
280         if ((jd = gui_hstack(id)))
281         {
282
283             gui_label(jd, _("Select Replay"), GUI_SML, GUI_ALL, 0,0);
284             gui_filler(jd);
285             gui_navig(jd, first > 0, first + DEMO_STEP < total);
286         }
287
288         gui_demo_thumbs(id);
289         gui_space(id);
290         gui_demo_status(id);
291
292         gui_layout(id, 0, 0);
293
294         gui_demo_update_thumbs();
295         gui_demo_update_status(last_viewed);
296     }
297     else
298     {
299         gui_label(id, _("No Replays"), GUI_MED, GUI_ALL, 0, 0);
300         gui_layout(id, 0, 0);
301     }
302
303     return id;
304 }
305
306 static int demo_enter(struct state *st, struct state *prev)
307 {
308     if (!items || (prev == &st_demo_del))
309     {
310         if (items)
311         {
312             demo_dir_free(items);
313             items = NULL;
314         }
315
316         items = demo_dir_scan();
317         total = array_len(items);
318     }
319
320     first       = first < total ? first : 0;
321     last        = MIN(first + DEMO_STEP - 1, total - 1);
322     last_viewed = MIN(MAX(first, last_viewed), last);
323
324     if (total)
325         demo_dir_load(items, first, last);
326
327     audio_music_fade_to(0.5f, "bgm/inter.ogg");
328
329     return demo_gui();
330 }
331
332 static void demo_leave(struct state *st, struct state *next, int id)
333 {
334     if (next == &st_title)
335     {
336         demo_dir_free(items);
337         items = NULL;
338     }
339
340     gui_delete(id);
341 }
342
343 static void demo_timer(int id, float dt)
344 {
345     if (total == 0 && time_state() > 4.0f)
346         goto_state(&st_title);
347
348     gui_timer(id, dt);
349 }
350
351 static void demo_point(int id, int x, int y, int dx, int dy)
352 {
353     int jd = shared_point_basic(id, x, y);
354     int i  = gui_token(jd);
355
356     if (jd && i >= 0 && !GUI_ISMSK(i))
357         gui_demo_update_status(i);
358 }
359
360 static void demo_stick(int id, int a, float v, int bump)
361 {
362     int jd = shared_stick_basic(id, a, v, bump);
363     int i  = gui_token(jd);
364
365     if (jd && i >= 0 && !GUI_ISMSK(i))
366         gui_demo_update_status(i);
367 }
368
369 static int demo_buttn(int b, int d)
370 {
371     if (d)
372     {
373         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b))
374             return demo_action(total ? gui_token(gui_click()) : GUI_BACK);
375         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
376             return demo_action(GUI_BACK);
377     }
378     return 1;
379 }
380
381 /*---------------------------------------------------------------------------*/
382
383 static int standalone;
384 static int demo_paused;
385 static int show_hud;
386 static int check_compat;
387 static int speed;
388
389 static float prelude;
390
391 void demo_play_goto(int s)
392 {
393     standalone   = s;
394     check_compat = 1;
395 }
396
397 static int demo_play_gui(void)
398 {
399     int id;
400
401     if ((id = gui_vstack(0)))
402     {
403         gui_label(id, _("Replay"), GUI_LRG, GUI_ALL, gui_blu, gui_grn);
404         gui_layout(id, 0, 0);
405         gui_pulse(id, 1.2f);
406     }
407
408     return id;
409 }
410
411 static int demo_play_enter(struct state *st, struct state *prev)
412 {
413     if (demo_paused)
414     {
415         demo_paused = 0;
416         prelude = 0;
417         audio_music_fade_in(0.5f);
418         return 0;
419     }
420
421     /*
422      * Post-1.5.1 replays include view data in the first update, this
423      * line is currently left in for compatibility with older replays.
424      */
425     game_client_fly(0.0f);
426
427     if (check_compat && !game_compat_map)
428     {
429         goto_state(&st_demo_compat);
430         return 0;
431     }
432
433     prelude = 1.0f;
434
435     speed = SPEED_NORMAL;
436     demo_speed_set(speed);
437
438     show_hud = 1;
439     hud_update(0);
440
441     return demo_play_gui();
442 }
443
444 static void demo_play_paint(int id, float t)
445 {
446     game_client_draw(0, t);
447
448     if (show_hud)
449         hud_paint();
450
451     if (time_state() < prelude)
452         gui_paint(id);
453 }
454
455 static void demo_play_timer(int id, float dt)
456 {
457     game_step_fade(dt);
458     gui_timer(id, dt);
459     hud_timer(dt);
460
461     /* Pause briefly before starting playback. */
462
463     if (time_state() < prelude)
464         return;
465
466     if (!demo_replay_step(dt))
467     {
468         demo_paused = 0;
469         goto_state(&st_demo_end);
470     }
471     else
472     {
473         progress_step();
474         game_client_blend(demo_replay_blend());
475     }
476 }
477
478 static void set_speed(int d)
479 {
480     if (d > 0) speed = SPEED_UP(speed);
481     if (d < 0) speed = SPEED_DN(speed);
482
483     demo_speed_set(speed);
484     hud_speed_pulse(speed);
485 }
486
487 static void demo_play_stick(int id, int a, float v, int bump)
488 {
489     if (!bump)
490         return;
491
492     if (config_tst_d(CONFIG_JOYSTICK_AXIS_Y, a))
493     {
494         if (v < 0) set_speed(+1);
495         if (v > 0) set_speed(-1);
496     }
497 }
498
499 static int demo_play_click(int b, int d)
500 {
501     if (d)
502     {
503         if (b == SDL_BUTTON_WHEELUP)   set_speed(+1);
504         if (b == SDL_BUTTON_WHEELDOWN) set_speed(-1);
505     }
506
507     return 1;
508 }
509
510 static int demo_play_keybd(int c, int d)
511 {
512     if (d)
513     {
514         if (config_tst_d(CONFIG_KEY_PAUSE, c))
515         {
516             demo_paused = 1;
517             return goto_state(&st_demo_end);
518         }
519
520         if (c == SDLK_F6)
521             show_hud = !show_hud;
522     }
523     return 1;
524 }
525
526 static int demo_play_buttn(int b, int d)
527 {
528     if (d)
529     {
530         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
531         {
532             if (config_tst_d(CONFIG_KEY_PAUSE, SDLK_ESCAPE))
533                 demo_paused = 1;
534
535             return goto_state(&st_demo_end);
536         }
537     }
538     return 1;
539 }
540
541 /*---------------------------------------------------------------------------*/
542
543 #define DEMO_KEEP      0
544 #define DEMO_DEL       1
545 #define DEMO_QUIT      2
546 #define DEMO_REPLAY    3
547 #define DEMO_CONTINUE  4
548
549 static int demo_end_action(int i)
550 {
551     audio_play(AUD_MENU, 1.0f);
552
553     switch (i)
554     {
555     case DEMO_DEL:
556         demo_paused = 0;
557         return goto_state(&st_demo_del);
558     case DEMO_KEEP:
559         demo_paused = 0;
560         demo_replay_stop(0);
561         return goto_state(&st_demo);
562     case DEMO_QUIT:
563         demo_replay_stop(0);
564         return 0;
565     case DEMO_REPLAY:
566         demo_replay_stop(0);
567         progress_replay(curr_demo());
568         return goto_state(&st_demo_play);
569     case DEMO_CONTINUE:
570         return goto_state(&st_demo_play);
571     }
572     return 1;
573 }
574
575 static int demo_end_gui(void)
576 {
577     int id, jd, kd;
578
579     if ((id = gui_vstack(0)))
580     {
581         if (demo_paused)
582             kd = gui_label(id, _("Replay Paused"), GUI_LRG, GUI_ALL,
583                            gui_gry, gui_red);
584         else
585             kd = gui_label(id, _("Replay Ends"),   GUI_LRG, GUI_ALL,
586                            gui_gry, gui_red);
587
588         if ((jd = gui_harray(id)))
589         {
590             int start_id = 0;
591
592             if (standalone)
593             {
594                 start_id = gui_start(jd, _("Quit"), GUI_SML, DEMO_QUIT, 1);
595             }
596             else
597             {
598                 start_id = gui_start(jd, _("Keep"), GUI_SML, DEMO_KEEP, 1);
599                 gui_state(jd, _("Delete"), GUI_SML, DEMO_DEL, 0);
600             }
601
602             if (demo_paused)
603             {
604                 gui_start(jd, _("Continue"), GUI_SML, DEMO_CONTINUE, 1);
605                 gui_toggle(start_id);
606             }
607             else
608                 gui_state(jd, _("Repeat"),   GUI_SML, DEMO_REPLAY,   0);
609         }
610
611         gui_pulse(kd, 1.2f);
612         gui_layout(id, 0, 0);
613     }
614
615     return id;
616 }
617
618 static int demo_end_enter(struct state *st, struct state *prev)
619 {
620     audio_music_fade_out(demo_paused ? 0.2f : 2.0f);
621
622     return demo_end_gui();
623 }
624
625 static void demo_end_paint(int id, float t)
626 {
627     game_client_draw(0, t);
628     gui_paint(id);
629
630     if (demo_paused)
631         hud_paint();
632 }
633
634 static int demo_end_keybd(int c, int d)
635 {
636     if (d)
637     {
638         if (demo_paused && config_tst_d(CONFIG_KEY_PAUSE, c))
639             return demo_end_action(DEMO_CONTINUE);
640     }
641     return 1;
642 }
643
644 static int demo_end_buttn(int b, int d)
645 {
646     if (d)
647     {
648         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b))
649             return demo_end_action(gui_token(gui_click()));
650
651         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
652         {
653             if (demo_paused)
654                 return demo_end_action(DEMO_CONTINUE);
655             else
656                 return demo_end_action(standalone ? DEMO_QUIT : DEMO_KEEP);
657         }
658     }
659     return 1;
660 }
661
662 /*---------------------------------------------------------------------------*/
663
664 static int demo_del_action(int i)
665 {
666     audio_play(AUD_MENU, 1.0f);
667     demo_replay_stop(i == DEMO_DEL);
668     return goto_state(&st_demo);
669 }
670
671 static int demo_del_gui(void)
672 {
673     int id, jd, kd;
674
675     if ((id = gui_vstack(0)))
676     {
677         kd = gui_label(id, _("Delete Replay?"), GUI_MED, GUI_ALL, gui_red, gui_red);
678
679         if ((jd = gui_harray(id)))
680         {
681             gui_start(jd, _("No"),  GUI_SML, DEMO_KEEP, 1);
682             gui_state(jd, _("Yes"), GUI_SML, DEMO_DEL,  0);
683         }
684
685         gui_pulse(kd, 1.2f);
686         gui_layout(id, 0, 0);
687     }
688
689     return id;
690 }
691
692 static int demo_del_enter(struct state *st, struct state *prev)
693 {
694     audio_music_fade_out(2.0f);
695
696     return demo_del_gui();
697 }
698
699 static int demo_del_buttn(int b, int d)
700 {
701     if (d)
702     {
703         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b))
704             return demo_del_action(gui_token(gui_click()));
705         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
706             return demo_del_action(DEMO_KEEP);
707     }
708     return 1;
709 }
710
711 /*---------------------------------------------------------------------------*/
712
713 static int demo_compat_gui(void)
714 {
715     int id;
716
717     if ((id = gui_vstack(0)))
718     {
719         gui_label(id, _("Warning!"), GUI_MED, GUI_ALL, 0, 0);
720         gui_space(id);
721         gui_multi(id, _("The current replay was recorded with a\\"
722                         "different (or unknown) version of this level.\\"
723                         "Be prepared to encounter visual errors.\\"),
724                   GUI_SML, GUI_ALL, gui_wht, gui_wht);
725
726         gui_layout(id, 0, 0);
727     }
728
729     return id;
730 }
731
732 static int demo_compat_enter(struct state *st, struct state *prev)
733 {
734     check_compat = 0;
735
736     return demo_compat_gui();
737 }
738
739 static void demo_compat_timer(int id, float dt)
740 {
741     game_step_fade(dt);
742     gui_timer(id, dt);
743 }
744
745 static int demo_compat_buttn(int b, int d)
746 {
747     if (d)
748     {
749         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b))
750             return goto_state(&st_demo_play);
751         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
752             return goto_state(&st_demo_end);
753     }
754     return 1;
755 }
756
757 /*---------------------------------------------------------------------------*/
758
759 struct state st_demo = {
760     demo_enter,
761     demo_leave,
762     shared_paint,
763     demo_timer,
764     demo_point,
765     demo_stick,
766     shared_angle,
767     shared_click,
768     NULL,
769     demo_buttn
770 };
771
772 struct state st_demo_play = {
773     demo_play_enter,
774     shared_leave,
775     demo_play_paint,
776     demo_play_timer,
777     NULL,
778     demo_play_stick,
779     NULL,
780     demo_play_click,
781     demo_play_keybd,
782     demo_play_buttn
783 };
784
785 struct state st_demo_end = {
786     demo_end_enter,
787     shared_leave,
788     demo_end_paint,
789     shared_timer,
790     shared_point,
791     shared_stick,
792     shared_angle,
793     shared_click,
794     demo_end_keybd,
795     demo_end_buttn
796 };
797
798 struct state st_demo_del = {
799     demo_del_enter,
800     shared_leave,
801     shared_paint,
802     shared_timer,
803     shared_point,
804     shared_stick,
805     shared_angle,
806     shared_click,
807     NULL,
808     demo_del_buttn
809 };
810
811 struct state st_demo_compat = {
812     demo_compat_enter,
813     shared_leave,
814     shared_paint,
815     demo_compat_timer,
816     shared_point,
817     shared_stick,
818     shared_angle,
819     shared_click,
820     NULL,
821     demo_compat_buttn
822 };