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