1 #include <allegro5/allegro.h>
2 #ifdef ALLEGRO_ANDROID
3 #include <allegro5/allegro_android.h>
4 #endif
5 #include "global.h"
6 #include "credits.h"
7 #include "fps.h"
8 #include "framework.h"
9 #include "game.h"
10 #include "gamepad.h"
11 #include "gamestate.h"
12 #include "keyboard.h"
13 #include "mouse.h"
14 #include "menus.h"
15 #include "screenshot.h"
16 #include "transition.h"
17 
18 static int closed = false;
19 
20 /* name of the configuration file for storing demo-specific settings. */
21 #define DEMO_CFG "demo.cfg"
22 
23 static int timer = 0;
24 
25 /* Screenshot module; for taking screenshots. */
26 static SCREENSHOT *screenshot = NULL;
27 
28 /* Status of the F12 key; used for taking screenshots. */
29 static int F12 = 0;
30 
31 /* Framerate counter module. */
32 static FPS *fps = NULL;
33 
34 /*
35    Static array of gamestates. Only state_count states are actually initialized.
36    The current_state variable points to the currently active state and
37    last_state points to the previous state. This required to do smooth
38    transitions between states.
39 */
40 static GAMESTATE state[DEMO_MAX_GAMESTATES];
41 static int current_state = 0, last_state;
42 static int state_count = 0;
43 
44 ALLEGRO_EVENT_QUEUE *event_queue;
45 
46 /*
47    Module for performing smooth state transition animations.
48 */
49 static TRANSITION *transition = NULL;
50 
51 
52 
drop_build_config_dir(ALLEGRO_PATH * path)53 static void drop_build_config_dir(ALLEGRO_PATH *path)
54 {
55    const char *s = al_get_path_tail(path);
56    if (s) {
57       if (0 == strcmp(s, "Debug")
58          || 0 == strcmp(s, "Release")
59          || 0 == strcmp(s, "RelWithDebInfo")
60          || 0 == strcmp(s, "Profile"))
61       {
62          al_drop_path_tail(path);
63       }
64    }
65 }
66 
67 
init_framework(void)68 int init_framework(void)
69 {
70    int error = DEMO_OK;
71    int c;
72    ALLEGRO_PATH *path;
73 
74    /* Attempt to initialize Allegro. */
75    if (!al_init()) {
76       return DEMO_ERROR_ALLEGRO;
77    }
78 
79    al_init_image_addon();
80    al_init_primitives_addon();
81    al_init_font_addon();
82 
83    /* Construct aboslute path for the configuration file. */
84    al_set_app_name("Allegro Skater Demo");
85    al_set_org_name("");
86    path = al_get_standard_path(ALLEGRO_USER_SETTINGS_PATH);
87    al_make_directory(al_path_cstr(path, '/'));
88    al_set_path_filename(path, DEMO_CFG);
89    strncpy(config_path, al_path_cstr(path, '/'), DEMO_PATH_LENGTH);
90    al_destroy_path(path);
91 
92    /* Construct absolute path for the datafile containing game menu data. */
93 #ifdef ALLEGRO_ANDROID
94    al_android_set_apk_file_interface();
95    strncpy(data_path, "/data", DEMO_PATH_LENGTH);
96    (void)drop_build_config_dir;
97 #else
98    path = al_get_standard_path(ALLEGRO_RESOURCES_PATH);
99    al_set_path_filename(path, "");
100    drop_build_config_dir(path);
101    al_append_path_component(path, "data");
102    strncpy(data_path, al_path_cstr(path, '/'), DEMO_PATH_LENGTH);
103    al_destroy_path(path);
104 #endif
105 
106    /* Read configuration file. */
107    read_global_config(config_path);
108 
109    /* Set window title and install close icon callback on platforms that
110       support this. */
111    al_set_window_title(screen, "Allegro Demo Game");
112 
113    /* Install the Allegro sound and music submodule. Note that this function
114       failing is not considered a fatal error so no error checking is
115       required. If this call fails, the game will still run, but with no
116       sound or music (or both). */
117    al_install_audio();
118    al_init_acodec_addon();
119    al_reserve_samples(8);
120 
121    event_queue = al_create_event_queue();
122 
123    /* Attempt to set the gfx mode. */
124    if ((error = change_gfx_mode()) != DEMO_OK) {
125       fprintf(stderr, "Error: %s\n", demo_error(error));
126       return error;
127    }
128 
129    /* Attempt to install the Allegro keyboard submodule. */
130    if (!al_install_keyboard()) {
131       fprintf(stderr, "Error installing keyboard: %s\n",
132                       demo_error(DEMO_ERROR_ALLEGRO));
133       return DEMO_ERROR_ALLEGRO;
134    }
135 
136    /* Attempt to install the joystick submodule. Note: no need to check
137       the return value as joystick isn't really required! */
138    al_install_joystick();
139 
140    if (al_install_touch_input())
141       al_register_event_source(event_queue, al_get_touch_input_event_source());
142 
143    al_install_mouse();
144 
145    al_register_event_source(event_queue, al_get_keyboard_event_source());
146    al_register_event_source(event_queue, al_get_mouse_event_source());
147    al_register_event_source(event_queue, al_get_joystick_event_source());
148 
149    /* Create a timer. */
150    {
151       ALLEGRO_TIMER *t = al_create_timer(1.0 / logic_framerate);
152       al_register_event_source(event_queue, al_get_timer_event_source(t));
153       al_start_timer(t);
154    }
155 
156    /* Seed the random number generator. */
157    srand((unsigned)time(NULL));
158 
159    /* Create the screenshot module. Screenshots will be saved in TGA format
160       and named SHOTxxxx.TGA. */
161    screenshot = create_screenshot("SHOT", "TGA");
162 
163    /* Create the frame rate counter module. */
164    fps = create_fps(logic_framerate);
165 
166    /* Initialize the game state array. */
167    c = DEMO_MAX_GAMESTATES;
168    while (c--) {
169       state[c].deinit = state[c].init = NULL;
170       state[c].id = state[c].update = NULL;
171       state[c].draw = NULL;
172    }
173 
174    /* Create all the game states/screens/pages. New screens may be added
175       here as required. */
176    create_main_menu(&state[0]);
177    create_options_menu(&state[1]);
178    create_gfx_menu(&state[2]);
179    create_sound_menu(&state[3]);
180    create_misc_menu(&state[4]);
181    create_controls_menu(&state[5]);
182    create_intro(&state[6]);
183    create_about_menu(&state[7]);
184    create_new_game(&state[8]);
185    create_continue_game(&state[9]);
186    create_success_menu(&state[10]);
187 
188    state_count = 11;            /* demo game has 11 screens */
189    current_state = 6;           /* the game will start with screen #7 - the intro */
190 
191    /* Create the keyboard and gamepad controller modules. */
192    controller[DEMO_CONTROLLER_KEYBOARD] =
193       create_keyboard_controller(config_path);
194    controller[DEMO_CONTROLLER_GAMEPAD] =
195       create_gamepad_controller(config_path);
196 
197    /* Initialize the module for displaying credits. */
198    init_credits();
199 
200    return error;
201 }
202 
203 
204 /*
205    Draws the current state or transition animation to the backbuffer
206    and flips the page.
207 
208    Parameters:
209       none
210 
211    Returns:
212       none
213 */
draw_framework(void)214 static void draw_framework(void)
215 {
216 
217    /* Draw either the current state or the transition animation if we're
218       in between states. */
219    if (transition) {
220       draw_transition(transition);
221    } else {
222       /* DEMO_STATE_EXIT is a special case and doesn't eqate to and actual
223          state that can be drawn. */
224       if (current_state != DEMO_STATE_EXIT) {
225          state[current_state].draw();
226       }
227    }
228 
229    /* Draw the current framerate if required. */
230    if (display_framerate) {
231       draw_fps(fps, plain_font, screen_width, 0, al_map_rgb(255, 255, 255), "%d FPS");
232    }
233 
234    al_flip_display();
235 }
236 
237 
run_framework(void)238 void run_framework(void)
239 {
240    int done = false;            /* will become true when we're ready to close */
241    int need_to_redraw = 1;      /* do we need to draw the next frame? */
242    int next_state = current_state;
243    int i;
244    bool background_mode = false;
245    bool paused = false;
246 
247    /* Initialize the first game screen. */
248    state[current_state].init();
249 
250    /* Create a transition animation so that the first screen doesn't just
251       pop up but comes in a nice animation that lasts 0.3 seconds. */
252    transition = create_transition(NULL, &state[current_state], 0.3f);
253 
254    timer = 0;
255 
256    /* Do the main loop; until we're not done. */
257    while (!done) {
258 
259       ALLEGRO_EVENT event;
260 
261       al_wait_for_event(event_queue, &event);
262       switch (event.type) {
263 
264          case ALLEGRO_EVENT_MOUSE_AXES:
265             mouse_handle_event(&event);
266             break;
267 
268          case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
269             mouse_handle_event(&event);
270             break;
271 
272          case ALLEGRO_EVENT_MOUSE_BUTTON_UP:
273             mouse_handle_event(&event);
274             break;
275 
276          case ALLEGRO_EVENT_KEY_DOWN:
277             keyboard_event(&event);
278             break;
279 
280          case ALLEGRO_EVENT_KEY_CHAR:
281             keyboard_event(&event);
282             break;
283 
284          case ALLEGRO_EVENT_KEY_UP:
285             keyboard_event(&event);
286             break;
287 
288          case ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN:
289             gamepad_event(&event);
290             break;
291 
292          case ALLEGRO_EVENT_JOYSTICK_BUTTON_UP:
293             gamepad_event(&event);
294             break;
295 
296          case ALLEGRO_EVENT_JOYSTICK_AXIS:
297             gamepad_event(&event);
298             break;
299 
300          case ALLEGRO_EVENT_TOUCH_BEGIN:
301             gamepad_event(&event);
302             break;
303          case ALLEGRO_EVENT_TOUCH_END:
304               gamepad_event(&event);
305             break;
306 
307          case ALLEGRO_EVENT_DISPLAY_CLOSE:
308             closed = true;
309             break;
310 
311          case ALLEGRO_EVENT_TIMER:
312             if (!paused)
313                timer++;
314             break;
315 
316          case ALLEGRO_EVENT_DISPLAY_ORIENTATION:
317             if (event.display.orientation == ALLEGRO_DISPLAY_ORIENTATION_90_DEGREES ||
318                 event.display.orientation == ALLEGRO_DISPLAY_ORIENTATION_270_DEGREES)
319                screen_orientation = event.display.orientation;
320             break;
321 
322          case ALLEGRO_EVENT_DISPLAY_RESIZE:
323             al_acknowledge_resize(screen);
324             screen_width = al_get_display_width(screen);
325             screen_height = al_get_display_height(screen);
326             if (fullscreen == 0) {
327                window_width = screen_width;
328                window_height = screen_height;
329             }
330             break;
331 
332          case ALLEGRO_EVENT_DISPLAY_HALT_DRAWING:
333             background_mode = true;
334             al_acknowledge_drawing_halt(screen);
335             break;
336 
337          case ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING:
338             background_mode = false;
339             break;
340 
341          case ALLEGRO_EVENT_DISPLAY_SWITCH_OUT:
342             paused = true;
343             break;
344 
345          case ALLEGRO_EVENT_DISPLAY_SWITCH_IN:
346             paused = false;
347             break;
348 
349       }
350 
351       if (!al_is_event_queue_empty(event_queue)) continue;
352 
353       /* Check if the timer has ticked. */
354       while (timer > 0) {
355          --timer;
356 
357          /* See if the user pressed F12 to see if the user wants to take
358             a screenshot. */
359          if (key_pressed(ALLEGRO_KEY_F12)) {
360             /* See if the F12 key was already pressed before. */
361             if (F12 == 0) {
362                /* The user just pressed F12 (it wasn't pressed before), so
363                   remember this and take a screenshot! */
364                F12 = 1;
365                take_screenshot(screenshot);
366             }
367          } else {
368             /* Remember for later that F12 is not pressed. */
369             F12 = 0;
370          }
371 
372          /* Do one frame of logic. If we're in between states, then update
373             the transition animation module, otherwise update the current
374             game state. */
375          if (transition) {
376             /* Advance the transition animation. If it returns non-zero, it
377                means the animation finished playing. */
378             if (update_transition(transition) == 0) {
379                /* Destroy the animation, we're done with it. */
380                destroy_transition(transition);
381                transition = NULL;
382 
383                /* Complete the transition to the new state by deiniting the
384                   previous one. */
385                if (state[last_state].deinit) {
386                   state[last_state].deinit();
387                }
388 
389                /* Stop the main loop if there is no more game screens. */
390                if (current_state == DEMO_STATE_EXIT) {
391                   done = true;
392                   break;
393                }
394             }
395          }
396          /* We're not in between states, so update the current one. */
397          else {
398             /* Update the current state. It returns the ID of the
399                next state. */
400 
401             next_state = state[current_state].update();
402 
403             /* Did the current state just close the game? */
404             if (next_state == DEMO_STATE_EXIT) {
405                /* Create a transition so the game doesn't just close but
406                   instead goes out in a nice animation. */
407                transition =
408                   create_transition(&state[current_state], NULL, 0.3f);
409                last_state = current_state;
410                current_state = next_state;
411                break;
412             }
413             /* If the next state is different then the current one, then
414                start a transition. */
415             else if (next_state != state[current_state].id()) {
416                last_state = current_state;
417 
418                /* Find the index of the next state in the state array.
419                   Note that ID of a state is not the same as its index in
420                   the array! */
421                for (i = 0; i < state_count; i++) {
422                   /* Did we find it? */
423                   if (state[i].id() == next_state) {
424                      /* We found the new state. Initialize it and start the
425                         transition animation. */
426                      state[i].init();
427                      transition =
428                         create_transition(&state[current_state], &state[i],
429                                           0.3f);
430                      current_state = i;
431                      break;
432                   }
433                }
434             }
435          }
436 
437          /* We just did one logic frame so we assume we will need to update
438             the visuals to reflect the changes this logic frame made. */
439          need_to_redraw = 1;
440 
441          /* Let the framerate counter know that one logic frame was run. */
442          fps_tick(fps);
443 
444          keyboard_tick();
445          mouse_tick();
446       }
447 
448       /* In case a frame of logic has just been run or the user wants
449          unlimited framerate, we must redraw the screen. */
450       if (need_to_redraw == 1 && !background_mode) {
451          /* Actually do the drawing. */
452          draw_framework();
453 
454          /* Make sure we don't draw too many times. */
455          need_to_redraw = 0;
456 
457          /* Let the framerate counter know that we just drew a frame. */
458          fps_frame(fps);
459       }
460 
461       /* Check if the user pressed the close icon. */
462       done = done || closed;
463 
464    }
465 }
466 
467 
shutdown_framework(void)468 void shutdown_framework(void)
469 {
470    al_destroy_event_queue(event_queue);
471 
472    /* Save the configuration settings. */
473    write_global_config(config_path);
474 
475    /* Destroy the screenshot module. */
476    destroy_screenshot(screenshot);
477 
478    /* Destroy the framerate counter module. */
479    destroy_fps(fps);
480 
481    /* Destroy controller modules. */
482    destroy_vcontroller(controller[DEMO_CONTROLLER_KEYBOARD], config_path);
483    destroy_vcontroller(controller[DEMO_CONTROLLER_GAMEPAD], config_path);
484 
485    /* Destroy the game states/screens. */
486    destroy_game();
487 
488    /* Get rid of all the game data. */
489    unload_data();
490 
491    /* Destroy the credits module. */
492    destroy_credits();
493 
494    /* Destroy the transition module. */
495    if (transition) {
496       destroy_transition(transition);
497    }
498 }
499