Replace strong level path from replay with a couple (name/version) of the level.
[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 "game.h"
21 #include "demo.h"
22 #include "levels.h"
23 #include "audio.h"
24 #include "solid.h"
25 #include "config.h"
26 #include "st_shared.h"
27 #include "util.h"
28
29 #include "st_demo.h"
30 #include "st_title.h"
31
32 /*---------------------------------------------------------------------------*/
33
34 #define DEMO_LINE 4
35 #define DEMO_STEP 8
36
37 static int first = 0;
38 static int total = 0;
39
40 static float replay_time;
41 static float global_time;
42
43 /*---------------------------------------------------------------------------*/
44
45 static int demo_action(int i)
46 {
47     audio_play(AUD_MENU, 1.0f);
48
49     switch (i)
50     {
51     case GUI_BACK:
52         return goto_state(&st_title);
53
54     case GUI_NEXT:
55         first += DEMO_STEP;
56         return goto_state(&st_demo);
57         break;
58
59     case GUI_PREV:
60         first -= DEMO_STEP;
61         return goto_state(&st_demo);
62         break;
63
64     default:
65         if (level_replay(demo_get(i)->filename))
66             demo_play_goto(0);
67             return goto_state(&st_demo_play);
68     }
69     return 1;
70 }
71
72 static void demo_replay(int id, int i)
73 {
74     int w = config_get_d(CONFIG_WIDTH);
75     int h = config_get_d(CONFIG_HEIGHT);
76     int jd;
77
78     char nam[MAXNAM + 3];
79
80     if ((jd = gui_vstack(id)))
81     {
82         gui_space(jd);
83         gui_image(jd, demo_get(i)->level->shot, w / 6, h / 6);
84
85         nam[MAXNAM - 1] = '\0';
86         strncpy(nam, demo_get(i)->name, MAXNAM);
87         if (nam[MAXNAM - 1] != '\0')
88         {
89             nam[MAXNAM - 2] = '.';
90             nam[MAXNAM - 1] = '.';
91             nam[MAXNAM + 0] = '.';
92             nam[MAXNAM + 1] = '\0';
93         }
94         gui_state(jd, nam, GUI_SML, i, 0);
95
96         gui_active(jd, i, 0);
97     }
98 }
99
100 static int name_id;
101 static int time_id;
102 static int coin_id;
103 static int date_id;
104 static int level_id;
105 static int mode_id;
106 static int state_id;
107 static int player_id;
108
109 /* Create a layout for some demo info.  If d is NULL, try to reserve enough
110  * space. */
111
112 static int gui_demo_status(int id, const struct demo *d)
113 {
114     char noname[MAXNAM];
115     char levelname[MAXSTR];
116     const char *mode, *state;
117     int i, j, k;
118     int jd, kd, ld, md;
119
120     if (d == NULL)
121     {
122         /* Build a long name */
123         memset(noname, 'M', MAXNAM - 1);
124         noname[MAXNAM - 1] = '\0';
125
126         /* Get a long mode */
127         mode = mode_to_str(0);
128         j = strlen(mode);
129         for (i = 1; i <= MODE_SINGLE; i++)
130         {
131             k = strlen(mode_to_str(i));
132             if (k > j)
133             {
134                 j = k;
135                 mode = mode_to_str(i);
136             }
137         }
138
139         /* Get a long state */
140         state = state_to_str(0);
141         j = strlen(state);
142         for (i = 1; i <= GAME_FALL; i++)
143         {
144             k = strlen(state_to_str(i));
145             if (k > j)
146             {
147                 j = k;
148                 state = state_to_str(i);
149             }
150         }
151
152         strcpy(levelname, "M");
153     }
154     else
155     {
156         mode = mode_to_str(d->mode);
157         state = state_to_str(d->state);
158         strcpy(levelname, _(d->level->set->name));
159         strcat(levelname, " - ");
160         strcat(levelname, d->level->repr);
161     }
162
163     if ((jd = gui_hstack(id)))
164     {
165         if ((kd = gui_vstack(jd)))
166         {
167             if ((ld = gui_harray(kd)))
168             {
169                 if ((md = gui_vstack(ld)))
170                 {
171                     player_id = gui_label(md, (d ? d->player : noname),
172                                           GUI_SML, GUI_RGT, 0, 0);
173                     coin_id = gui_count(md, (d ? d->coins : 100),
174                                         GUI_SML, GUI_RGT);
175                     state_id = gui_label(md, state, GUI_SML, GUI_RGT,
176                                          gui_red, gui_red);
177                 }
178                 if ((md = gui_vstack(ld)))
179                 {
180                     gui_label(md, _("Player"), GUI_SML, GUI_LFT,
181                               gui_wht, gui_wht);
182                     gui_label(md, _("Coins"), GUI_SML, GUI_LFT,
183                               gui_wht, gui_wht);
184                     gui_label(md, _("State"), GUI_SML, GUI_LFT,
185                               gui_wht, gui_wht);
186                 }
187                 if ((md = gui_vstack(ld)))
188                 {
189                     name_id = gui_label(md, (d ? d->name : noname),
190                                         GUI_SML, GUI_RGT, 0, 0);
191                     time_id = gui_clock(md, (d ? d->timer : 35000),
192                                         GUI_SML, GUI_RGT);
193                     mode_id = gui_label(md, mode, GUI_SML, GUI_RGT, 0, 0);
194                 }
195             }
196             level_id = gui_label(kd, (levelname), GUI_SML, GUI_RGT, 0, 0);
197             date_id = gui_label(kd, (d ? date_to_str(d->date) : "M"),
198                                 GUI_SML, GUI_RGT, 0, 0);
199         }
200         if ((kd = gui_vstack(jd)))
201         {
202             gui_label(kd, _("Replay"), GUI_SML, GUI_LFT, gui_wht, gui_wht);
203             gui_label(kd, _("Time"),   GUI_SML, GUI_LFT, gui_wht, gui_wht);
204             gui_label(kd, _("Mode"),   GUI_SML, GUI_LFT, gui_wht, gui_wht);
205             gui_label(kd, _("Level"),  GUI_SML, GUI_LFT, gui_wht, gui_wht);
206             gui_label(kd, _("Date"),   GUI_SML, GUI_LFT, gui_wht, gui_wht);
207         }
208         if (d && (d->state == GAME_GOAL || d->state == GAME_SPEC))
209             gui_set_color(state_id, gui_grn, gui_grn);
210     }
211     return jd;
212 }
213
214 static void gui_demo_update_status(int i)
215 {
216     const struct demo *d = demo_get(i);
217     char levelname[MAXSTR];
218     
219     strcpy(levelname, _(d->level->set->name));
220     strcat(levelname, " - ");
221     strcat(levelname, d->level->repr);
222
223     gui_set_label(name_id,   d->name);
224     gui_set_label(date_id,   date_to_str(d->date));
225     gui_set_label(level_id,  levelname);
226     gui_set_label(player_id, d->player);
227     gui_set_label(mode_id,   mode_to_str(d->mode));
228
229     if (d->state == GAME_GOAL || d->state == GAME_SPEC)
230         gui_set_color(state_id, gui_grn, gui_grn);
231     else
232         gui_set_color(state_id, gui_red, gui_red);
233
234     gui_set_label(state_id, state_to_str(d->state));
235     gui_set_count(coin_id, d->coins);
236     gui_set_clock(time_id, d->timer);
237 }
238
239 static int demo_enter(void)
240 {
241     int i, j;
242     int id, jd, kd;
243
244     id = gui_vstack(0);
245
246     if ((total = demo_scan()))
247     {
248         if ((jd = gui_hstack(id)))
249         {
250
251             gui_label(jd, _("Select Replay"), GUI_SML, GUI_ALL, 0,0);
252             gui_filler(jd);
253             gui_back_prev_next(jd, first > 0, first + DEMO_STEP < total);
254         }
255
256         if ((jd = gui_varray(id)))
257             for (i = first; i < first + DEMO_STEP ; i += DEMO_LINE)
258                 if ((kd = gui_harray(jd)))
259                 {
260                     for (j = i + DEMO_LINE - 1; j >= i; j--)
261                         if (j < total)
262                             demo_replay(kd, j);
263                         else
264                             gui_space(kd);
265                 }
266         gui_filler(id);
267         gui_demo_status(id, NULL);
268         gui_layout(id, 0, 0);
269         gui_demo_update_status(0);
270     }
271     else
272     {
273         gui_label(id, _("No Replays"), GUI_MED, GUI_ALL, 0,0);
274         gui_filler(id);
275         gui_start(id, _("Back"), GUI_SML, GUI_BACK, 0);
276         gui_layout(id, 0, 0);
277     }
278
279     audio_music_fade_to(0.5f, "bgm/inter.ogg");
280
281     return id;
282 }
283
284 static void demo_point(int id, int x, int y, int dx, int dy)
285 {
286     int jd = shared_point_basic(id, x, y);
287     int i  = gui_token(jd);
288
289     if (jd && i >= 0)
290         gui_demo_update_status(i);
291 }
292
293 static void demo_stick(int id, int a, int v)
294 {
295     int jd = shared_stick_basic(id, a, v);
296     int i  = gui_token(jd);
297
298     if (jd && i >= 0)
299         gui_demo_update_status(i);
300 }
301
302 static int demo_buttn(int b, int d)
303 {
304     if (d)
305     {
306         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b))
307             return demo_action(gui_token(gui_click()));
308         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
309             return demo_action(GUI_BACK);
310     }
311     return 1;
312 }
313
314 /*---------------------------------------------------------------------------*/
315
316 static int simple_play; /* play demo from command line */
317
318 void demo_play_goto(int simple)
319 {
320     simple_play = simple;
321 }
322
323 static int demo_play_enter(void)
324 {
325     int id;
326
327     if ((id = gui_vstack(0)))
328     {
329         gui_label(id, _("Replay"), GUI_LRG, GUI_ALL, gui_blu, gui_grn);
330         gui_layout(id, 0, 0);
331         gui_pulse(id, 1.2f);
332     }
333
334     global_time = -1.f;
335     replay_time =  0.f;
336
337     hud_update(0);
338
339     game_set_fly(0.f);
340
341     return id;
342 }
343
344 static void demo_play_paint(int id, float st)
345 {
346     game_draw(0, st);
347     hud_paint();
348
349     if (time_state() < 1.f)
350         gui_paint(id);
351 }
352
353 static void demo_play_timer(int id, float dt)
354 {
355     float t;
356
357     game_step_fade(dt);
358     gui_timer(id, dt);
359     audio_timer(dt);
360
361     global_time += dt;
362     hud_timer(dt);
363
364     /* Spin or skip depending on how fast the demo wants to run. */
365
366     while (replay_time < global_time)
367         if (demo_replay_step(&t))
368         {
369             replay_time += t;
370         }
371         else
372         {
373             goto_state(&st_demo_end);
374             break;
375         }
376 }
377
378 static int demo_play_buttn(int b, int d)
379 {
380     if (d)
381     {
382         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
383             return goto_state(&st_demo_end);
384     }
385     return 1;
386 }
387
388 /*---------------------------------------------------------------------------*/
389
390 #define DEMO_KEEP    0
391 #define DEMO_DEL     1
392 #define DEMO_QUIT    2
393 #define DEMO_REPLAY  3
394
395 static int demo_end_action(int i)
396 {
397     audio_play(AUD_MENU, 1.0f);
398
399     switch (i)
400     {
401     case DEMO_DEL:
402         return goto_state(&st_demo_del);
403     case DEMO_KEEP:
404         demo_replay_stop(0);
405         return goto_state(&st_demo);
406     case DEMO_QUIT:
407         demo_replay_stop(0);
408         return 0;
409     case DEMO_REPLAY:
410         demo_replay_stop(0);
411         level_replay(curr_demo_replay()->filename);
412         return goto_state(&st_demo_play);
413     }
414     return 1;
415 }
416
417 static int demo_end_enter(void)
418 {
419     int id, jd, kd;
420
421     if ((id = gui_vstack(0)))
422     {
423         kd = gui_label(id, _("Replay Ends"), GUI_LRG, GUI_ALL, gui_gry, gui_red);
424
425         if ((jd = gui_harray(id)))
426         {
427             gui_start(jd, _("Replay Again"), GUI_SML, DEMO_REPLAY, 0);
428
429             if (simple_play)
430                 gui_start(jd, _("OK"),       GUI_SML, DEMO_QUIT,   1);
431             else
432             {
433                 gui_start(jd, _("Keep"),     GUI_SML, DEMO_KEEP,   1);
434                 gui_state(jd, _("Delete"),   GUI_SML, DEMO_DEL,    0);
435             }
436         }
437
438         gui_filler(id);
439
440         if ((jd = gui_hstack(id)))
441         {
442             gui_filler(jd);
443             gui_demo_status(jd, curr_demo_replay());
444             gui_filler(jd);
445         }
446
447         gui_pulse(kd, 1.2f);
448         gui_layout(id, 0, 0);
449     }
450     audio_music_fade_out(2.0f);
451
452     return id;
453 }
454
455 static void demo_end_timer(int id, float dt)
456 {
457     float t;
458     float gg[3] = { 0.0f,  9.8f, 0.0f };
459     float gf[3] = { 0.0f, -9.8f, 0.0f };
460     int state = curr_demo_replay()->state;
461
462     if (time_state() < 2.f)
463     {
464         /* Continue demo in background for 2 seconds */
465         if (replay_time < global_time)
466         {
467             /* The demo is finished, let the ball go */
468             if (state != GAME_NONE)
469                 game_step((state == GAME_GOAL || state == GAME_SPEC) ? gg : gf,
470                           dt, NULL);
471         }
472         else
473         {
474             /* The demo is not finished, play it */
475             global_time += dt;
476
477             while (replay_time < global_time)
478                 if (demo_replay_step(&t))
479                     replay_time += t;
480                 else
481                     break;
482         }
483     }
484
485     gui_timer(id, dt);
486     audio_timer(dt);
487 }
488
489 static int demo_end_buttn(int b, int d)
490 {
491     if (d)
492     {
493         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b))
494             return demo_end_action(gui_token(gui_click()));
495         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
496             return demo_end_action(simple_play ? DEMO_QUIT : DEMO_KEEP);
497     }
498     return 1;
499 }
500
501 /*---------------------------------------------------------------------------*/
502
503 static int demo_del_action(int i)
504 {
505     audio_play(AUD_MENU, 1.0f);
506
507     demo_replay_stop(i == DEMO_DEL);
508     return goto_state(&st_demo);
509 }
510
511 static int demo_del_enter(void)
512 {
513     int id, jd, kd;
514
515     if ((id = gui_vstack(0)))
516     {
517         kd = gui_label(id, _("Delete Replay?"), GUI_MED, GUI_ALL, gui_red, gui_red);
518
519         if ((jd = gui_harray(id)))
520         {
521             gui_start(jd, _("No"),  GUI_SML, DEMO_KEEP, 1);
522             gui_state(jd, _("Yes"), GUI_SML, DEMO_DEL,  0);
523         }
524
525         gui_pulse(kd, 1.2f);
526         gui_layout(id, 0, 0);
527     }
528     audio_music_fade_out(2.0f);
529
530     return id;
531 }
532
533 static int demo_del_buttn(int b, int d)
534 {
535     if (d)
536     {
537         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b))
538             return demo_del_action(gui_token(gui_click()));
539         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
540             return demo_del_action(DEMO_KEEP);
541     }
542     return 1;
543 }
544
545 /*---------------------------------------------------------------------------*/
546
547 struct state st_demo = {
548     demo_enter,
549     shared_leave,
550     shared_paint,
551     shared_timer,
552     demo_point,
553     demo_stick,
554     shared_click,
555     NULL,
556     demo_buttn,
557     0
558 };
559
560 struct state st_demo_play = {
561     demo_play_enter,
562     shared_leave,
563     demo_play_paint,
564     demo_play_timer,
565     NULL,
566     NULL,
567     NULL,
568     NULL,
569     demo_play_buttn,
570     0
571 };
572
573 struct state st_demo_end = {
574     demo_end_enter,
575     shared_leave,
576     shared_paint,
577     demo_end_timer,
578     shared_point,
579     shared_stick,
580     shared_click,
581     NULL,
582     demo_end_buttn,
583     1, 0
584 };
585
586 struct state st_demo_del = {
587     demo_del_enter,
588     shared_leave,
589     shared_paint,
590     shared_timer,
591     shared_point,
592     shared_stick,
593     shared_click,
594     NULL,
595     demo_del_buttn,
596     1, 0
597 };