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