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