1 /*
2  * menu.c - menu scene
3  * Copyright (C) 2008-2010  Alexandre Martins <alemartf(at)gmail(dot)com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <allegro.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <math.h>
25 #include "menu.h"
26 #include "quest.h"
27 #include "../core/quest.h"
28 #include "../core/scene.h"
29 #include "../core/storyboard.h"
30 #include "../core/global.h"
31 #include "../core/osspec.h"
32 #include "../core/v2d.h"
33 #include "../core/logfile.h"
34 #include "../core/video.h"
35 #include "../core/audio.h"
36 #include "../core/timer.h"
37 #include "../core/lang.h"
38 #include "../core/soundfactory.h"
39 #include "../entities/actor.h"
40 #include "../entities/font.h"
41 #include "../entities/background.h"
42 
43 
44 
45 /* private data */
46 #define MENU_MUSICFILE  "musics/title.ogg"
47 #define MENU_BGFILE     "themes/menu.bg"
48 
49 /* menu screens */
50 #define MENU_MAIN 0 /* main menu */
51 #define MENU_QUEST 1 /* custom quests */
52 static int menu_screen; /* SCREEN_* */
53 static input_t *input;
54 static scene_t *jump_to;
55 static bgtheme_t *bgtheme;
56 
57 
58 /* main menu */
59 #define MENU_MAXOPTIONS 5
60 static float start_time;
61 static int control_restored;
62 static char menu[MENU_MAXOPTIONS][32];
63 static int menuopt; /* current option */
64 static font_t *menufnt[MENU_MAXOPTIONS][2]; /* 0. white ; 1. yellow */
65 static actor_t *menufoot;
66 static int surge_entering;
67 static actor_t *surge, *surgebg, *gametitle;
68 static font_t *credit, *version;
69 static int quit;
70 
71 
72 /* quest menu */
73 #define MENU_QUESTSPERPAGE 14
74 static font_t *qstselect[2], *qstdetail;
75 static int qstmenuopt;
76 static int qstcount;
77 static font_t **qstfnt; /* vector of font_t* */
78 static quest_t **qstdata; /* vector of quest_t* */
79 
80 
81 
82 /* private functions */
83 static void select_option(int opt);
84 static void load_quest_list();
85 static void release_quest_list();
86 static int dirfill(const char *filename, int attrib, void *param);
87 static int dircount(const char *filename, int attrib, void *param);
88 static int sort_cmp(const void *a, const void *b);
89 static int qstmenuopt_getpage(int val);
90 static int qstmenuopt_getmaxpages();
91 static void game_start(quest_t *q);
92 
93 
94 
95 
96 
97 
98 /* public functions */
99 
100 /*
101  * menu_init()
102  * Initializes the menu
103  */
menu_init()104 void menu_init()
105 {
106     int i, j;
107 
108     /* initializing... */
109     quit = FALSE;
110     menu_screen = MENU_MAIN;
111     start_time = timer_get_ticks()*0.001;
112     control_restored = FALSE;
113     jump_to = NULL;
114     input = input_create_user();
115     input_ignore(input);
116     load_quest_list();
117     music_play( music_load(MENU_MUSICFILE) , INFINITY);
118 
119 
120     /* background init */
121     bgtheme = background_load(MENU_BGFILE);
122 
123 
124     /* main actors */
125     surge_entering = TRUE;
126 
127     surge = actor_create();
128     actor_change_animation(surge, sprite_get_animation("SD_TITLESURGE", 0));
129     surge->position.x = (VIDEO_SCREEN_W-actor_image(surge)->w)/2 + 5;
130     surge->position.y = -15;
131 
132     surgebg = actor_create();
133     actor_change_animation(surgebg, sprite_get_animation("SD_TITLEBG", 0));
134     surgebg->position.x = (VIDEO_SCREEN_W-actor_image(surgebg)->w)/2;
135     surgebg->position.y = surge->position.y+25;
136 
137     gametitle = actor_create();
138     actor_change_animation(gametitle, sprite_get_animation("SD_TITLEGAMENAME", 0));
139     gametitle->position.x = (VIDEO_SCREEN_W-actor_image(gametitle)->w)/2;
140     gametitle->position.y = surge->position.y+actor_image(surge)->h-9;
141 
142     credit = font_create(8);
143     credit->position = v2d_new(3, VIDEO_SCREEN_H-12);
144     font_set_text(credit, "%s   2008-2010", GAME_WEBSITE+7);
145     version = font_create(0);
146     version->position = v2d_new(VIDEO_SCREEN_W-75, 3);
147     font_set_text(version, "FREEWARE\n  V%d.%d.%d", GAME_VERSION, GAME_SUB_VERSION, GAME_WIP_VERSION);
148 
149     /* main menu */
150     menuopt = 0;
151     menufoot = actor_create();
152     actor_change_animation(menufoot, sprite_get_animation("SD_TITLEFOOT", 0));
153 
154     lang_getstring("MENU_1PGAME", menu[0], sizeof(menu[0]));
155     lang_getstring("MENU_TUTORIAL", menu[1], sizeof(menu[1]));
156     lang_getstring("MENU_CUSTOMQUESTS", menu[2], sizeof(menu[2]));
157     lang_getstring("MENU_OPTIONS", menu[3], sizeof(menu[3]));
158     lang_getstring("MENU_EXIT", menu[4], sizeof(menu[4]));
159 
160     for(i=0; i<2; i++) {
161         for(j=0; j<MENU_MAXOPTIONS; j++) {
162             menufnt[j][i] = font_create(i);
163             menufnt[j][i]->position = v2d_new(112, gametitle->position.y+65+10*j);
164             font_set_text(menufnt[j][i], menu[j]);
165         }
166     }
167 
168     /* quest menu */
169     qstselect[0] = font_create(8);
170     qstselect[0]->position = v2d_new(5, 3);
171 
172     qstselect[1] = font_create(8);
173     qstselect[1]->position = v2d_new(5, VIDEO_SCREEN_H-13);
174 
175     qstdetail = font_create(8);
176     qstdetail->position = v2d_new(5, 170);
177 
178 
179     /* fade in */
180     fadefx_in(image_rgb(0,0,0), 1.5);
181 }
182 
183 
184 /*
185  * menu_update()
186  * Updates the menu
187  */
menu_update()188 void menu_update()
189 {
190     int i;
191     float t = timer_get_ticks() * 0.001;
192 
193     /* game start */
194     if(jump_to != NULL && fadefx_over()) {
195         scenestack_pop();
196         scenestack_push(jump_to);
197         return;
198     }
199 
200     /* quit game */
201     if(quit && fadefx_over()) {
202         game_quit();
203         return;
204     }
205 
206     /* ignore/restore control */
207     if(t <= start_time + 2.0)
208         input_ignore(input);
209     else if(!control_restored) {
210         input_restore(input);
211         control_restored = TRUE;
212     }
213 
214     /* background movement */
215     background_update(bgtheme);
216 
217 
218     /* menu programming */
219     if(jump_to || quit)
220         return;
221 
222     switch(menu_screen) {
223 
224         /* ------ main menu ------ */
225         case MENU_MAIN:
226         {
227             /* surge & stuff */
228             if(surge_entering && actor_animation_finished(surge)) {
229                 surge_entering = FALSE;
230                 actor_change_animation(surge, sprite_get_animation("SD_TITLESURGE", 1));
231                 input_restore(input);
232             }
233             gametitle->visible = !surge_entering;
234 
235             /* current option */
236             menufoot->position.x = menufnt[menuopt][0]->position.x - 20 + 3*cos(2*PI * t);
237             menufoot->position.y = menufnt[menuopt][0]->position.y;
238 
239             if(input_button_pressed(input, IB_UP)) {
240                 sound_play( soundfactory_get("choose") );
241                 menuopt--;
242             }
243             if(input_button_pressed(input, IB_DOWN)) {
244                 sound_play( soundfactory_get("choose") );
245                 menuopt++;
246             }
247             menuopt = (menuopt%MENU_MAXOPTIONS + MENU_MAXOPTIONS) % MENU_MAXOPTIONS;
248 
249             if(input_button_pressed(input, IB_FIRE1) || input_button_pressed(input, IB_FIRE3)) {
250                 sound_play( soundfactory_get("select") );
251                 select_option(menuopt);
252                 return;
253             }
254 
255             break;
256         }
257 
258 
259 
260 
261 
262         /* ------ quest menu ----- */
263         case MENU_QUEST:
264         {
265             /* go back to the main menu */
266             if(input_button_pressed(input, IB_FIRE4)) {
267                 sound_play( soundfactory_get("return") );
268                 menu_screen = MENU_MAIN;
269             }
270 
271             /* font position */
272             for(i=0; i<qstcount; i++) {
273                 qstfnt[i]->position = v2d_new(30, 20 + 10*(i%MENU_QUESTSPERPAGE));
274                 qstfnt[i]->visible = (qstmenuopt_getpage(i) == qstmenuopt_getpage(qstmenuopt));
275             }
276 
277             /* selected option? */
278             menufoot->position.x = 10;
279             menufoot->position.y = qstfnt[qstmenuopt]->position.y;
280 
281             if(input_button_pressed(input, IB_UP)) {
282                 sound_play( soundfactory_get("choose") );
283                 qstmenuopt--;
284             }
285             if(input_button_pressed(input, IB_DOWN)) {
286                 sound_play( soundfactory_get("choose") );
287                 qstmenuopt++;
288             }
289             qstmenuopt = (qstmenuopt%qstcount + qstcount) % qstcount;
290 
291             /* quest details */
292             font_set_text(qstselect[0], lang_get("MENU_CQ_SELECT"), qstmenuopt_getpage(qstmenuopt), qstmenuopt_getmaxpages());
293             font_set_text(qstselect[1], lang_get("MENU_CQ_BACK"));
294             font_set_text(qstdetail, lang_get("MENU_CQ_INFO"), qstdata[qstmenuopt]->version, qstdata[qstmenuopt]->name, qstdata[qstmenuopt]->author, qstdata[qstmenuopt]->description);
295 
296             /* game start! */
297             if(input_button_pressed(input, IB_FIRE1) || input_button_pressed(input, IB_FIRE3)) {
298                 quest_t *clone = load_quest(qstdata[qstmenuopt]->file);
299                 sound_play( soundfactory_get("select") );
300                 game_start(clone);
301                 return;
302             }
303 
304             break;
305         }
306     }
307 }
308 
309 
310 /*
311  * menu_render()
312  * Renders the menu
313  */
menu_render()314 void menu_render()
315 {
316     int i;
317     v2d_t camera = v2d_new(VIDEO_SCREEN_W/2, VIDEO_SCREEN_H/2);
318 
319     /* don't draw anything. We're leaving... */
320     if(quit && fadefx_over())
321         return;
322 
323     /* background */
324     background_render_bg(bgtheme, camera);
325     background_render_fg(bgtheme, camera);
326 
327     /* rendering menus... :) */
328     switch(menu_screen) {
329         /* ----- main menu ----- */
330         case MENU_MAIN:
331         {
332             /* menu */
333             for(i=0; i<MENU_MAXOPTIONS; i++)
334                 font_render(menufnt[i][i == menuopt ? 1 : 0], camera);
335             actor_render(menufoot, camera);
336 
337             /* surge & stuff */
338             font_render(credit, camera);
339             font_render(version, camera);
340             actor_render(surgebg, camera);
341             if(surge_entering)
342                 image_clear(video_get_backbuffer(), image_rgb(0,0,0));
343             actor_render(surge, camera);
344             actor_render(gametitle, camera);
345             break;
346         }
347 
348         /* ----- quest menu ----- */
349         case MENU_QUEST:
350         {
351             /* quest details */
352             image_t *thumb = qstdata[qstmenuopt]->image;
353             font_render(qstdetail, camera);
354             image_blit(thumb, video_get_backbuffer(), 0, 0, VIDEO_SCREEN_W - thumb->w - 5, (int)qstfnt[0]->position.y, thumb->w, thumb->h);
355 
356             /* texts */
357             font_render(qstselect[0], camera);
358             font_render(qstselect[1], camera);
359             for(i=0; i<qstcount; i++)
360                 font_render(qstfnt[i], camera);
361             actor_render(menufoot, camera);
362             break;
363         }
364     }
365 }
366 
367 
368 
369 /*
370  * menu_release()
371  * Releases the menu
372  */
menu_release()373 void menu_release()
374 {
375     int i, j;
376 
377     /* no more music... */
378     music_stop();
379     music_unref(MENU_MUSICFILE);
380 
381     /* main menu stuff */
382     font_destroy(credit);
383     font_destroy(version);
384     for(i=0; i<2; i++) {
385         for(j=0; j<MENU_MAXOPTIONS; j++)
386             font_destroy(menufnt[j][i]);
387     }
388     actor_destroy(surgebg);
389     actor_destroy(gametitle);
390     actor_destroy(surge);
391 
392     /* quest menu */
393     font_destroy(qstselect[0]);
394     font_destroy(qstselect[1]);
395     font_destroy(qstdetail);
396 
397     /* background */
398     bgtheme = background_unload(bgtheme);
399 
400     /* misc */
401     actor_destroy(menufoot);
402     input_destroy(input);
403     release_quest_list();
404 }
405 
406 
407 
408 
409 
410 
411 
412 
413 /* private functions */
414 
415 
416 /* the player selected some option at the main menu */
select_option(int opt)417 void select_option(int opt)
418 {
419     char abs_path[1024];
420 
421     switch(opt) {
422         /* 1P GAME */
423         case 0:
424             resource_filepath(abs_path, "quests/default.qst", sizeof(abs_path), RESFP_READ);
425             game_start( load_quest(abs_path) );
426             return;
427 
428         /* TUTORIAL */
429         case 1:
430             resource_filepath(abs_path, "quests/tutorial.qst", sizeof(abs_path), RESFP_READ);
431             game_start( load_quest(abs_path) );
432             return;
433 
434         /* CUSTOM QUESTS */
435         case 2:
436             menu_screen = MENU_QUEST;
437             qstmenuopt = 0;
438             break;
439 
440         /* OPTIONS */
441         case 3:
442             jump_to = storyboard_get_scene(SCENE_OPTIONS);
443             fadefx_out(image_rgb(0,0,0), 0.5);
444             return;
445 
446         /* EXIT */
447         case 4:
448             quit = TRUE;
449             fadefx_out(image_rgb(0,0,0), 0.5);
450             return;
451     }
452 }
453 
454 
455 
456 /* reads the quest list from the quest/ folder */
load_quest_list()457 void load_quest_list()
458 {
459     int i, j, deny_flags = FA_DIREC | FA_LABEL, c = 0;
460     int max_paths;
461     char path[] = "quests/*.qst";
462     char abs_path[2][1024];
463 
464     logfile_message("load_quest_list()");
465 
466     /* official and $HOME quests */
467     absolute_filepath(abs_path[0], path, sizeof(abs_path[0]));
468     home_filepath(abs_path[1], path, sizeof(abs_path[1]));
469     max_paths = (strcmp(abs_path[0], abs_path[1]) == 0) ? 1 : 2;
470 
471     /* loading quest data */
472     qstcount = 0;
473     for(j=0; j<max_paths; j++)
474         for_each_file_ex(abs_path[j], 0, deny_flags, dircount, NULL);
475 
476     qstdata = mallocx(qstcount * sizeof(quest_t*));
477     for(j=0; j<max_paths; j++)
478         for_each_file_ex(abs_path[j], 0, deny_flags, dirfill, (void*)&c);
479     qsort(qstdata, qstcount, sizeof(quest_t*), sort_cmp);
480 
481     /* fatal error */
482     if(qstcount == 0)
483         fatal_error("FATAL ERROR: no quests found! Please reinstall the game.");
484     else
485         logfile_message("%d quests found.", qstcount);
486 
487     /* other stuff */
488     qstfnt = mallocx(qstcount * sizeof(font_t*));
489     for(i=0; i<qstcount; i++) {
490         qstfnt[i] = font_create(8);
491         font_set_text(qstfnt[i], "%2d %s", i+1, qstdata[i]->name);
492     }
493 }
494 
dirfill(const char * filename,int attrib,void * param)495 int dirfill(const char *filename, int attrib, void *param)
496 {
497     int *c = (int*)param;
498     qstdata[ (*c)++ ] = load_quest((char*)filename);
499     return 0;
500 }
501 
dircount(const char * filename,int attrib,void * param)502 int dircount(const char *filename, int attrib, void *param)
503 {
504     qstcount++;
505     return 0;
506 }
507 
508 /* releases the quest list */
release_quest_list()509 void release_quest_list()
510 {
511     int i;
512 
513     logfile_message("release_quest_list()");
514 
515     for(i=0; i<qstcount; i++) {
516         unload_quest(qstdata[i]);
517         font_destroy(qstfnt[i]);
518     }
519 
520     free(qstdata);
521     free(qstfnt);
522     qstcount = 0;
523 }
524 
525 
526 /* comparator */
sort_cmp(const void * a,const void * b)527 int sort_cmp(const void *a, const void *b)
528 {
529     quest_t *q[2] = { *((quest_t**)a), *((quest_t**)b) };
530     return (int)( file_time( (const char*)(q[1]->file) ) - file_time( (const char*)(q[0]->file) ) );
531 }
532 
533 
534 /* returns a page number... */
qstmenuopt_getpage(int val)535 int qstmenuopt_getpage(int val)
536 {
537     return val/MENU_QUESTSPERPAGE + 1;
538 }
539 
540 /* how many pages? */
qstmenuopt_getmaxpages()541 int qstmenuopt_getmaxpages()
542 {
543     return qstcount/MENU_QUESTSPERPAGE + ((qstcount%MENU_QUESTSPERPAGE == 0) ? 0 : 1);
544 }
545 
546 
547 /* closes the menu and starts the game. Call return after this. */
game_start(quest_t * q)548 void game_start(quest_t *q)
549 {
550     quest_run(q, FALSE);
551     jump_to = storyboard_get_scene(SCENE_QUEST);
552     input_ignore(input);
553     fadefx_out(image_rgb(0,0,0), 0.5);
554 }
555