1 #include <allegro.h>
2 #ifdef DEMO_USE_ALLEGRO_GL
3 #include <alleggl.h>
4 #endif
5 #include "../include/credits.h"
6 #include "../include/fps.h"
7 #include "../include/framewk.h"
8 #include "../include/game.h"
9 #include "../include/gamepad.h"
10 #include "../include/global.h"
11 #include "../include/gmestate.h"
12 #include "../include/keyboard.h"
13 #include "../include/menus.h"
14 #include "../include/scrshot.h"
15 #include "../include/transitn.h"
16 #include "../include/updtedvr.h"
17 
18 /*
19    The callback function for the close icon on platforms that support it.
20    The value of the close variable is regularly tested in the main loop
21    to determine if the user clicked the close icon.
22 */
23 static int closed = FALSE;
closehook(void)24 static void closehook(void)
25 {
26    closed = TRUE;
27 }
28 
29 /* name of the configuration file for storing demo-specific settings. */
30 #define DEMO_CFG "demo.cfg"
31 
32 /*
33    Timer callback. Installed with the frequency defined in the framework
34    configuration file. The value of the timer variable is regularly tested
35    in the main loop to determine when to run a frame of game logic.
36 */
37 static volatile int timer = 0;
timer_f(void)38 static void timer_f(void)
39 {
40    timer++;
41 } END_OF_STATIC_FUNCTION(timer_f);
42 
43 /* Screenshot module; for taking screenshots. */
44 static SCREENSHOT *screenshot = NULL;
45 
46 /* Status of the F12 key; used for taking screenshots. */
47 static int F12 = 0;
48 
49 /* Framerate counter module. */
50 static FPS *fps = NULL;
51 
52 /*
53    Static array of gamestates. Only state_count states are actually initialized.
54    The current_state variable points to the currently active state and
55    last_state points to the previous state. This required to do smooth
56    transitions between states.
57 */
58 static GAMESTATE state[DEMO_MAX_GAMESTATES];
59 static int current_state = 0, last_state;
60 static int state_count = 0;
61 
62 /*
63    Module for performing smooth state transition animations.
64 */
65 static TRANSITION *transition = NULL;
66 
67 
init_framework(void)68 int init_framework(void)
69 {
70    int error = DEMO_OK;
71    int c;
72 
73    /* Attempt to initialize Allegro. */
74    if (allegro_init() != 0) {
75       return DEMO_ERROR_ALLEGRO;
76    }
77 
78 #ifdef DEMO_USE_ALLEGRO_GL
79    /* Attempt to initialize AllegroGL. */
80    if (install_allegro_gl() != 0) {
81       return DEMO_ERROR_ALLEGRO;
82    }
83 #endif
84 
85    /* Construct aboslute path for the configuration file. */
86    get_executable_name(config_path, DEMO_PATH_LENGTH);
87    replace_filename(config_path, config_path, DEMO_CFG, DEMO_PATH_LENGTH);
88 
89    /* Construct aboslute path for the datafile containing game menu data. */
90    get_executable_name(data_path, DEMO_PATH_LENGTH);
91    replace_filename(data_path, data_path, "demo.dat#menu.dat",
92                     DEMO_PATH_LENGTH);
93 
94    /* Read configuration file. */
95    read_config(config_path);
96 
97    /* Set window title and install close icon callback on platforms that
98       support this. */
99    set_window_title("Allegro Demo Game");
100    set_close_button_callback(&closehook);
101 
102    /* Make sure the game will continue to run in the background when the
103       user tabs away. Note: this should be made configurable! */
104    set_display_switch_mode(SWITCH_BACKGROUND);
105 
106    /* Install the Allegro sound and music submodule. Note that this function
107       failing is not considered a fatal error so no error checking is
108       required. If this call fails, the game will still run, but with no
109       sound or music (or both). */
110    install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL);
111    set_volume(sound_volume * 25, music_volume * 25);
112 
113    /* Attempt to set the gfx mode. */
114    if ((error = change_gfx_mode()) != DEMO_OK) {
115       set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
116       allegro_message("Error: %s\n", demo_error(error));
117       return error;
118    }
119 
120    /* Attempt to install the Allegro keyboard submodule. */
121    if (install_keyboard() != 0) {
122       set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
123       allegro_message("Error installing keyboard: %s\n",
124                       demo_error(DEMO_ERROR_ALLEGRO));
125       return DEMO_ERROR_ALLEGRO;
126    }
127 
128    /* Attempt to install the joystick submodule. Note: no need to check
129       the return value as joystick isn't really required! */
130    install_joystick(JOY_TYPE_AUTODETECT);
131 
132    /* Install the timer submodule and install the timer interrupt callback. */
133    install_timer();
134    LOCK_VARIABLE(timer);
135    LOCK_FUNCTION(timer_f);
136    install_int_ex(timer_f, BPS_TO_TIMER(logic_framerate));
137 
138    /* Seed the random number generator. */
139    srand((unsigned)time(NULL));
140 
141    /* Create the screenshot module. Screenshots will be saved in TGA format
142       and named SHOTxxxx.TGA. */
143    screenshot = create_screenshot("SHOT", "TGA");
144 
145    /* Create the frame rate counter module. */
146    fps = create_fps(logic_framerate);
147 
148    /* Initialize the game state array. */
149    c = DEMO_MAX_GAMESTATES;
150    while (c--) {
151       state[c].deinit = state[c].init = NULL;
152       state[c].id = state[c].update = NULL;
153       state[c].draw = NULL;
154    }
155 
156    /* Create all the game states/screens/pages. New screens may be added
157       here as required. */
158    create_main_menu(&state[0]);
159    create_options_menu(&state[1]);
160    create_gfx_menu(&state[2]);
161    create_sound_menu(&state[3]);
162    create_misc_menu(&state[4]);
163    create_controls_menu(&state[5]);
164    create_intro(&state[6]);
165    create_about_menu(&state[7]);
166    create_new_game(&state[8]);
167    create_continue_game(&state[9]);
168    create_success_menu(&state[10]);
169 
170    state_count = 11;            /* demo game has 11 screens */
171    current_state = 6;           /* the game will start with screen #7 - the intro */
172 
173    /* Create the keyboard and gamepad controller modules. */
174    controller[DEMO_CONTROLLER_KEYBOARD] =
175       create_keyboard_controller(config_path);
176    controller[DEMO_CONTROLLER_GAMEPAD] =
177       create_gamepad_controller(config_path);
178 
179    /* Initialize the module for displaying credits. */
180    init_credits();
181 
182    return error;
183 }
184 
185 
186 /*
187    Draws the current state or transition animation to the backbuffer
188    and flips the page.
189 
190    Parameters:
191       none
192 
193    Returns:
194       none
195 */
draw_framework(void)196 void draw_framework(void)
197 {
198    /* Before drawing to the backbuffer we must acquire it - just in case. */
199    acquire_bitmap(update_driver.get_canvas());
200 
201    /* Draw either the current state or the transition animation if we're
202       in between states. */
203    if (transition) {
204       draw_transition(update_driver.get_canvas(), transition);
205    } else {
206       /* DEMO_STATE_EXIT is a special case and doesn't eqate to and actual
207          state that can be drawn. */
208       if (current_state != DEMO_STATE_EXIT) {
209          state[current_state].draw(update_driver.get_canvas());
210       }
211    }
212 
213    /* Draw the current framerate if required. */
214    if (display_framerate) {
215       draw_fps(fps, update_driver.get_canvas(), font, 0, 0,
216                makecol(255, 255, 255), "%d FPS");
217    }
218 
219    /* Release the previously acquired canvas bitmap (backbuffer). */
220    release_bitmap(update_driver.get_canvas());
221 
222    /* Finally make the update driver flip the page so it becomes visible on
223       the screen, however it may go about actually doing it. */
224    update_driver.draw();
225 }
226 
227 
run_framework(void)228 void run_framework(void)
229 {
230    int done = FALSE;            /* will become TRUE when we're ready to close */
231    int need_to_redraw = 1;      /* do we need to draw the next frame? */
232    int next_state = current_state;
233    int i;
234 
235    /* Initialize the first game screen. */
236    state[current_state].init();
237 
238    /* Create a transition animation so that the first screen doesn't just
239       pop up but comes in a nice animation that lasts 0.3 seconds. */
240    transition = create_transition(NULL, &state[current_state], 0.3f);
241 
242    /* Initialize the timer variable - just in case. */
243    timer = 0;
244 
245    /* Do the main loop; until we're not done. */
246    while (!done) {
247       /* Check if the timer has ticked. */
248       while (timer > 0) {
249          --timer;
250 
251          /* See if the user pressed F12 to see if the user wants to take
252             a screenshot. */
253          if (key[KEY_F12]) {
254             /* See if the F12 key was already pressed before. */
255             if (F12 == 0) {
256                /* The user just pressed F12 (it wasn't pressed before), so
257                   remember this and take a screenshot! */
258                F12 = 1;
259                take_screenshot(screenshot, update_driver.get_canvas());
260             }
261          } else {
262             /* Remember for later that F12 is not pressed. */
263             F12 = 0;
264          }
265 
266          /* Do one frame of logic. If we're in between states, then update
267             the transition animation module, otherwise update the current
268             game state. */
269          if (transition) {
270             /* Advance the transition animation. If it returns non-zero, it
271                means the animation finished playing. */
272             if (update_transition(transition) == 0) {
273                /* Destroy the animation, we're done with it. */
274                destroy_transition(transition);
275                transition = NULL;
276 
277                /* Complete the transition to the new state by deiniting the
278                   previous one. */
279                if (state[last_state].deinit) {
280                   state[last_state].deinit();
281                }
282 
283                /* Stop the main loop if there is no more game screens. */
284                if (current_state == DEMO_STATE_EXIT) {
285                   done = TRUE;
286                   break;
287                }
288             }
289          }
290          /* We're not in between states, so update the current one. */
291          else {
292             /* Update the current state. It returns the ID of the
293                next state. */
294             next_state = state[current_state].update();
295 
296             /* Did the current state just close the game? */
297             if (next_state == DEMO_STATE_EXIT) {
298                /* Create a transition so the game doesn't just close but
299                   instead goes out in a nice animation. */
300                transition =
301                   create_transition(&state[current_state], NULL, 0.3f);
302                last_state = current_state;
303                current_state = next_state;
304                break;
305             }
306             /* If the next state is different then the current one, then
307                start a transition. */
308             else if (next_state != state[current_state].id()) {
309                last_state = current_state;
310 
311                /* Find the index of the next state in the state array.
312                   Note that ID of a state is not the same as its index in
313                   the array! */
314                for (i = 0; i < state_count; i++) {
315                   /* Did we find it? */
316                   if (state[i].id() == next_state) {
317                      /* We found the new state. Initialize it and start the
318                         transition animation. */
319                      state[i].init();
320                      transition =
321                         create_transition(&state[current_state], &state[i],
322                                           0.3f);
323                      current_state = i;
324                      break;
325                   }
326                }
327             }
328          }
329 
330          /* We just did one logic frame so we assume we will need to update
331             the visuals to reflect the changes this logic frame made. */
332          need_to_redraw = 1;
333 
334          /* Let the framerate counter know that one logic frame was run. */
335          fps_tick(fps);
336       }
337 
338       /* Make sure the game doesn't freeze in case the computer isn't fast
339          enough to run the logic as fast as we want it to. Note that this
340          will skip logic frames so all time in the game will be stretched.
341          Note also that this is very unlikely to happen. */
342       while (timer > max_frame_skip) {
343          fps_tick(fps);
344          --timer;
345       }
346 
347       /* In case a frame of logic has just been run or the user wants
348          unlimited framerate, we must redraw the screen. */
349       if (need_to_redraw == 1 || limit_framerate == 0) {
350          /* Actually do the drawing. */
351          draw_framework();
352 
353          /* Make sure we don't draw too many times. */
354          need_to_redraw = 0;
355 
356          /* Let the framerate counter know that we just drew a frame. */
357          fps_frame(fps);
358       }
359 
360       /* Check if the user pressed the close icon. */
361       done = done || closed;
362 
363       /* If power saving is enabled, let go of the CPU until we need it
364          again. This is done by calling rest() and passing it 1. */
365       if (reduce_cpu_usage) {
366          while (timer == 0) {
367             rest(1);
368          }
369       }
370    }
371 }
372 
373 
shutdown_framework(void)374 void shutdown_framework(void)
375 {
376    /* Uninstall the time callback function. */
377    remove_int(timer_f);
378 
379    /* Save the configuration settings. */
380    write_config(config_path);
381 
382    /* Destroy the screenshot module. */
383    destroy_screenshot(screenshot);
384 
385    /* Destroy the framerate counter module. */
386    destroy_fps(fps);
387 
388    /* Destroy controller modules. */
389    destroy_vcontroller(controller[DEMO_CONTROLLER_KEYBOARD], config_path);
390    destroy_vcontroller(controller[DEMO_CONTROLLER_GAMEPAD], config_path);
391 
392    /* Destroy the game states/screens. */
393    destroy_game();
394 
395    /* Get rid of all the game data. */
396    unload_data();
397 
398    /* Destroy the credits module. */
399    destroy_credits();
400 
401    /* Destroy the transition module. */
402    if (transition) {
403       destroy_transition(transition);
404    }
405 
406    /* Destroy the screen update driver. */
407    update_driver.destroy();
408 }
409