Transform default game/locale data paths based on executable name
[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 "solid.h"
24 #include "config.h"
25 #include "st_shared.h"
26 #include "util.h"
27 #include "common.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
36 /*---------------------------------------------------------------------------*/
37
38 #define DEMO_LINE 4
39 #define DEMO_STEP 8
40
41 static int first = 0;
42 static int total = 0;
43
44 static int last_viewed = 0;
45
46 /*---------------------------------------------------------------------------*/
47
48 static int demo_action(int i)
49 {
50     audio_play(AUD_MENU, 1.0f);
51
52     switch (i)
53     {
54     case GUI_BACK:
55         return goto_state(&st_title);
56
57     case GUI_NEXT:
58         first += DEMO_STEP;
59         return goto_state(&st_demo);
60         break;
61
62     case GUI_PREV:
63         first -= DEMO_STEP;
64         return goto_state(&st_demo);
65         break;
66
67     case GUI_NULL:
68         return 1;
69         break;
70
71     default:
72         if (progress_replay(demo_get(i)->filename))
73         {
74             last_viewed = i;
75             demo_play_goto(0);
76             return goto_state(&st_demo_play);
77         }
78         break;
79     }
80     return 1;
81 }
82
83 /*---------------------------------------------------------------------------*/
84
85 static void demo_replay(int id, int i)
86 {
87     int w = config_get_d(CONFIG_WIDTH);
88     int h = config_get_d(CONFIG_HEIGHT);
89     int jd;
90
91     char nam[MAXNAM + 3];
92
93     if ((jd = gui_vstack(id)))
94     {
95         gui_space(jd);
96         gui_image(jd, demo_get(i)->shot, w / 6, h / 6);
97
98         nam[MAXNAM - 1] = '\0';
99         strncpy(nam, demo_get(i)->name, MAXNAM);
100         if (nam[MAXNAM - 1] != '\0')
101         {
102             nam[MAXNAM - 2] = '.';
103             nam[MAXNAM - 1] = '.';
104             nam[MAXNAM + 0] = '.';
105             nam[MAXNAM + 1] = '\0';
106         }
107         gui_state(jd, nam, GUI_SML, i, 0);
108
109         gui_active(jd, i, 0);
110     }
111 }
112
113 static int name_id;
114 static int time_id;
115 static int coin_id;
116 static int date_id;
117 static int status_id;
118 static int player_id;
119
120 static int gui_demo_status(int id, const struct demo *d)
121 {
122     char noname[MAXNAM];
123     const char *status;
124     int i, j, k;
125     int jd, kd, ld;
126
127     if (d == NULL)
128     {
129         /* Build a long name */
130         memset(noname, 'M', MAXNAM - 1);
131         noname[MAXNAM - 1] = '\0';
132
133         /* Get a long status */
134         status = status_to_str(0);
135         j = strlen(status);
136         for (i = 1; i <= GAME_FALL; i++)
137         {
138             k = strlen(status_to_str(i));
139             if (k > j)
140             {
141                 j = k;
142                 status = status_to_str(i);
143             }
144         }
145     }
146     else
147     {
148         status = status_to_str(d->status);
149     }
150
151     if ((jd = gui_hstack(id)))
152     {
153         gui_filler(jd);
154
155         if ((kd = gui_hstack(jd)))
156         {
157             if ((ld = gui_vstack(kd)))
158             {
159                 gui_filler(ld);
160
161                 time_id = gui_clock(ld, (d ? d->timer : 35000),
162                                     GUI_SML, GUI_NE);
163                 coin_id = gui_count(ld, (d ? d->coins : 100),
164                                     GUI_SML, 0);
165                 status_id = gui_label(ld, status, GUI_SML, GUI_SE,
166                                       gui_red, gui_red);
167
168                 if (d && d->status == GAME_GOAL)
169                     gui_set_color(status_id, gui_grn, gui_grn);
170
171                 gui_filler(ld);
172             }
173
174             if ((ld = gui_vstack(kd)))
175             {
176                 gui_filler(ld);
177
178                 gui_label(ld, _("Time"),  GUI_SML, GUI_NW,
179                           gui_wht, gui_wht);
180                 gui_label(ld, _("Coins"), GUI_SML, 0,
181                           gui_wht, gui_wht);
182                 gui_label(ld, _("Status"), GUI_SML, GUI_SW,
183                           gui_wht, gui_wht);
184
185                 gui_filler(ld);
186             }
187         }
188
189         gui_space(jd);
190
191         if ((kd = gui_vstack(jd)))
192         {
193             gui_filler(kd);
194
195             name_id   = gui_label(kd, (d ? d->name : noname),
196                                   GUI_SML, GUI_NE, 0, 0);
197             player_id = gui_label(kd, (d ? d->player : noname),
198                                   GUI_SML, 0,      0, 0);
199             date_id   = gui_label(kd, (d ? date_to_str(d->date) :
200                                        date_to_str(time(NULL))),
201                                   GUI_SML, GUI_SE, 0, 0);
202
203             gui_filler(kd);
204         }
205
206         if ((kd = gui_vstack(jd)))
207         {
208             gui_filler(kd);
209
210             gui_label(kd, _("Replay"), GUI_SML, GUI_NW, gui_wht, gui_wht);
211             gui_label(kd, _("Player"), GUI_SML, 0,      gui_wht, gui_wht);
212             gui_label(kd, _("Date"),   GUI_SML, GUI_SW, gui_wht, gui_wht);
213
214             gui_filler(kd);
215         }
216
217         gui_filler(jd);
218     }
219
220     return jd;
221 }
222
223 static void gui_demo_update_status(int i)
224 {
225     const struct demo *d;
226
227     if ((d = demo_get(i)) == NULL && (d = demo_get(0)) == NULL)
228         return;
229
230     gui_set_label(name_id,   d->name);
231     gui_set_label(date_id,   date_to_str(d->date));
232     gui_set_label(player_id, d->player);
233
234     if (d->status == GAME_GOAL)
235         gui_set_color(status_id, gui_grn, gui_grn);
236     else
237         gui_set_color(status_id, gui_red, gui_red);
238
239     gui_set_label(status_id, status_to_str(d->status));
240     gui_set_count(coin_id, d->coins);
241     gui_set_clock(time_id, d->timer);
242 }
243
244 /*---------------------------------------------------------------------------*/
245
246 static int demo_enter(void)
247 {
248     int i, j;
249     int id, jd, kd;
250
251     id = gui_vstack(0);
252
253     if ((total = demo_scan()))
254     {
255         if ((jd = gui_hstack(id)))
256         {
257
258             gui_label(jd, _("Select Replay"), GUI_SML, GUI_ALL, 0,0);
259             gui_filler(jd);
260             gui_navig(jd, first > 0, first + DEMO_STEP < total);
261         }
262
263         if ((jd = gui_varray(id)))
264             for (i = first; i < first + DEMO_STEP ; i += DEMO_LINE)
265                 if ((kd = gui_harray(jd)))
266                 {
267                     for (j = i + DEMO_LINE - 1; j >= i; j--)
268                         if (j < total)
269                             demo_replay(kd, j);
270                         else
271                             gui_space(kd);
272                 }
273         gui_filler(id);
274         gui_demo_status(id, NULL);
275         gui_layout(id, 0, 0);
276         gui_demo_update_status(last_viewed);
277     }
278     else
279     {
280         gui_label(id, _("No Replays"), GUI_MED, GUI_ALL, 0, 0);
281         gui_layout(id, 0, 0);
282     }
283
284     audio_music_fade_to(0.5f, "bgm/inter.ogg");
285
286     return id;
287 }
288
289 static void demo_timer(int id, float dt)
290 {
291     if (total == 0 && time_state() > 4.0f)
292         goto_state(&st_title);
293
294     gui_timer(id, dt);
295 }
296
297 static void demo_point(int id, int x, int y, int dx, int dy)
298 {
299     int jd = shared_point_basic(id, x, y);
300     int i  = gui_token(jd);
301
302     if (jd && i >= 0 && !GUI_ISMSK(i))
303         gui_demo_update_status(i);
304 }
305
306 static void demo_stick(int id, int a, int v)
307 {
308     int jd = shared_stick_basic(id, a, v);
309     int i  = gui_token(jd);
310
311     if (jd && i >= 0 && !GUI_ISMSK(i))
312         gui_demo_update_status(i);
313 }
314
315 static int demo_buttn(int b, int d)
316 {
317     if (d)
318     {
319         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b))
320             return demo_action(total ? gui_token(gui_click()) : GUI_BACK);
321         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
322             return demo_action(GUI_BACK);
323     }
324     return 1;
325 }
326
327 /*---------------------------------------------------------------------------*/
328
329 static int standalone;
330 static int demo_paused;
331 static int show_hud;
332
333 void demo_play_goto(int s)
334 {
335     standalone = s;
336 }
337
338 static int demo_play_enter(void)
339 {
340     int id;
341
342     if (demo_paused)
343     {
344         demo_paused = 0;
345         audio_music_fade_in(0.5f);
346         return 0;
347     }
348
349     if ((id = gui_vstack(0)))
350     {
351         gui_label(id, _("Replay"), GUI_LRG, GUI_ALL, gui_blu, gui_grn);
352         gui_layout(id, 0, 0);
353         gui_pulse(id, 1.2f);
354     }
355
356     show_hud = 1;
357     hud_update(0);
358     game_set_fly(0.f, game_client_file());
359     game_client_step(NULL);
360
361     return id;
362 }
363
364 static void demo_play_paint(int id, float t)
365 {
366     game_draw(0, t);
367
368     if (show_hud)
369         hud_paint();
370
371     if (time_state() < 1.f)
372         gui_paint(id);
373 }
374
375 static void demo_play_timer(int id, float dt)
376 {
377     game_step_fade(dt);
378     gui_timer(id, dt);
379     hud_timer(dt);
380
381     /*
382      * Introduce a one-second pause at the start of replay playback.  (One
383      * second is the time during which the "Replay" label is being displayed.)
384      * HACK ALERT!  "id == 0" means we got here from the pause screen, so no
385      * label has been created and there's no need to wait.
386      */
387
388     if (id != 0 && time_state() < 1.0f)
389         return;
390
391     /* Spin or skip depending on how fast the demo wants to run. */
392
393     if (!demo_replay_step(dt))
394     {
395         demo_paused = 0;
396         goto_state(&st_demo_end);
397     }
398     else
399         progress_step();
400 }
401
402 static int demo_play_keybd(int c, int d)
403 {
404     if (d)
405     {
406         if (config_tst_d(CONFIG_KEY_PAUSE, c))
407         {
408             demo_paused = 1;
409             return goto_state(&st_demo_end);
410         }
411
412         if (c == SDLK_F6)
413             show_hud = !show_hud;
414     }
415     return 1;
416 }
417
418 static int demo_play_buttn(int b, int d)
419 {
420     if (d)
421     {
422         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
423         {
424             if (config_tst_d(CONFIG_KEY_PAUSE, SDLK_ESCAPE))
425                 demo_paused = 1;
426
427             return goto_state(&st_demo_end);
428         }
429     }
430     return 1;
431 }
432
433 /*---------------------------------------------------------------------------*/
434
435 #define DEMO_KEEP      0
436 #define DEMO_DEL       1
437 #define DEMO_QUIT      2
438 #define DEMO_REPLAY    3
439 #define DEMO_CONTINUE  4
440
441 static int demo_end_action(int i)
442 {
443     audio_play(AUD_MENU, 1.0f);
444
445     switch (i)
446     {
447     case DEMO_DEL:
448         demo_paused = 0;
449         return goto_state(&st_demo_del);
450     case DEMO_KEEP:
451         demo_paused = 0;
452         demo_replay_stop(0);
453         return goto_state(&st_demo);
454     case DEMO_QUIT:
455         demo_replay_stop(0);
456         return 0;
457     case DEMO_REPLAY:
458         demo_replay_stop(0);
459         progress_replay(curr_demo_replay()->filename);
460         return goto_state(&st_demo_play);
461     case DEMO_CONTINUE:
462         return goto_state(&st_demo_play);
463     }
464     return 1;
465 }
466
467 static int demo_end_enter(void)
468 {
469     int id, jd, kd;
470
471     if ((id = gui_vstack(0)))
472     {
473         if (demo_paused)
474             kd = gui_label(id, _("Replay Paused"), GUI_LRG, GUI_ALL,
475                            gui_gry, gui_red);
476         else
477             kd = gui_label(id, _("Replay Ends"),   GUI_LRG, GUI_ALL,
478                            gui_gry, gui_red);
479
480         if ((jd = gui_harray(id)))
481         {
482             int start_id = 0;
483
484             if (standalone)
485             {
486                 start_id = gui_start(jd, _("Quit"), GUI_SML, DEMO_QUIT, 1);
487             }
488             else
489             {
490                 start_id = gui_start(jd, _("Keep"), GUI_SML, DEMO_KEEP, 1);
491                 gui_state(jd, _("Delete"), GUI_SML, DEMO_DEL, 0);
492             }
493
494             if (demo_paused)
495             {
496                 gui_start(jd, _("Continue"), GUI_SML, DEMO_CONTINUE, 1);
497                 gui_toggle(start_id);
498             }
499             else
500                 gui_state(jd, _("Repeat"),   GUI_SML, DEMO_REPLAY,   0);
501         }
502
503         gui_pulse(kd, 1.2f);
504         gui_layout(id, 0, 0);
505     }
506
507     audio_music_fade_out(demo_paused ? 0.2f : 2.0f);
508
509     return id;
510 }
511
512 static void demo_end_paint(int id, float t)
513 {
514     game_draw(0, t);
515     gui_paint(id);
516
517     if (demo_paused)
518         hud_paint();
519 }
520
521 static int demo_end_keybd(int c, int d)
522 {
523     if (d)
524     {
525         if (demo_paused && config_tst_d(CONFIG_KEY_PAUSE, c))
526             return demo_end_action(DEMO_CONTINUE);
527     }
528     return 1;
529 }
530
531 static int demo_end_buttn(int b, int d)
532 {
533     if (d)
534     {
535         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b))
536             return demo_end_action(gui_token(gui_click()));
537
538         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
539         {
540             if (demo_paused)
541                 return demo_end_action(DEMO_CONTINUE);
542             else
543                 return demo_end_action(standalone ? DEMO_QUIT : DEMO_KEEP);
544         }
545     }
546     return 1;
547 }
548
549 /*---------------------------------------------------------------------------*/
550
551 static int demo_del_action(int i)
552 {
553     audio_play(AUD_MENU, 1.0f);
554
555     demo_replay_stop(i == DEMO_DEL);
556     return goto_state(&st_demo);
557 }
558
559 static int demo_del_enter(void)
560 {
561     int id, jd, kd;
562
563     if ((id = gui_vstack(0)))
564     {
565         kd = gui_label(id, _("Delete Replay?"), GUI_MED, GUI_ALL, gui_red, gui_red);
566
567         if ((jd = gui_harray(id)))
568         {
569             gui_start(jd, _("No"),  GUI_SML, DEMO_KEEP, 1);
570             gui_state(jd, _("Yes"), GUI_SML, DEMO_DEL,  0);
571         }
572
573         gui_pulse(kd, 1.2f);
574         gui_layout(id, 0, 0);
575     }
576     audio_music_fade_out(2.0f);
577
578     return id;
579 }
580
581 static int demo_del_buttn(int b, int d)
582 {
583     if (d)
584     {
585         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b))
586             return demo_del_action(gui_token(gui_click()));
587         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
588             return demo_del_action(DEMO_KEEP);
589     }
590     return 1;
591 }
592
593 /*---------------------------------------------------------------------------*/
594
595 struct state st_demo = {
596     demo_enter,
597     shared_leave,
598     shared_paint,
599     demo_timer,
600     demo_point,
601     demo_stick,
602     shared_angle,
603     shared_click,
604     NULL,
605     demo_buttn,
606     1, 0
607 };
608
609 struct state st_demo_play = {
610     demo_play_enter,
611     shared_leave,
612     demo_play_paint,
613     demo_play_timer,
614     NULL,
615     NULL,
616     NULL,
617     NULL,
618     demo_play_keybd,
619     demo_play_buttn,
620     1, 0
621 };
622
623 struct state st_demo_end = {
624     demo_end_enter,
625     shared_leave,
626     demo_end_paint,
627     shared_timer,
628     shared_point,
629     shared_stick,
630     shared_angle,
631     shared_click,
632     demo_end_keybd,
633     demo_end_buttn,
634     1, 0
635 };
636
637 struct state st_demo_del = {
638     demo_del_enter,
639     shared_leave,
640     shared_paint,
641     shared_timer,
642     shared_point,
643     shared_stick,
644     shared_angle,
645     shared_click,
646     NULL,
647     demo_del_buttn,
648     1, 0
649 };