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