libsuperderpy/src/libsuperderpy.c

557 lines
19 KiB
C
Raw Normal View History

2012-12-23 14:29:54 +01:00
/*! \file main.c
* \brief Main file of SuperDerpy engine.
2012-02-28 13:09:12 +01:00
*
2012-12-23 14:29:54 +01:00
* Contains basic functions shared by all views.
2012-02-28 13:09:12 +01:00
*/
2012-03-04 13:32:42 +01:00
/*
* Copyright (c) Sebastian Krzyszkowiak <dos@dosowisko.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Also, ponies.
2012-03-04 13:32:42 +01:00
*/
2016-11-06 03:10:43 +01:00
#ifdef LIBSUPERDERPY_MOUSE_EMULATION
#define ALLEGRO_UNSTABLE
#endif
2012-02-15 23:57:06 +01:00
#include <stdio.h>
#include <math.h>
#include <locale.h>
#include <dlfcn.h>
2016-07-03 20:34:31 +02:00
#include <unistd.h>
2016-07-05 01:30:06 +02:00
#include <libgen.h>
2016-09-08 00:32:21 +02:00
#include <sys/param.h>
#include "internal.h"
#include "libsuperderpy.h"
#include "3rdparty/valgrind.h"
2016-07-02 23:23:08 +02:00
#ifdef ALLEGRO_MACOSX
#include <mach-o/dyld.h>
#endif
2012-02-15 23:57:06 +01:00
2016-09-08 00:32:21 +02:00
SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char* name, struct Viewport viewport) {
2016-06-27 21:21:59 +02:00
struct Game *game = calloc(1, sizeof(struct Game));
game->name = name;
2016-08-23 02:13:15 +02:00
game->viewport_config = viewport;
2012-07-03 23:44:03 +02:00
2016-07-02 23:23:08 +02:00
#ifdef ALLEGRO_MACOSX
2016-09-08 00:32:21 +02:00
getcwd(game->_priv.cwd, MAXPATHLEN);
2016-07-02 23:23:08 +02:00
char exe_path[MAXPATHLEN];
uint32_t size = sizeof(exe_path);
_NSGetExecutablePath(exe_path, &size);
2016-07-05 01:30:06 +02:00
chdir(dirname(exe_path));
2016-07-02 23:23:08 +02:00
#endif
2012-09-03 02:25:32 +02:00
if(!al_init()) {
fprintf(stderr, "failed to initialize allegro!\n");
return NULL;
2012-09-03 02:25:32 +02:00
}
InitConfig(game);
2012-02-16 12:48:48 +01:00
game->_priv.fps_count.frames_done = 0;
game->_priv.fps_count.fps = 0;
game->_priv.fps_count.old_time = 0;
2012-12-23 14:29:54 +01:00
game->_priv.font_bsod = NULL;
game->_priv.console = NULL;
game->_priv.console_tmp = NULL;
game->_priv.garbage = NULL;
game->eventHandler = NULL;
game->config.fullscreen = atoi(GetConfigOptionDefault(game, "SuperDerpy", "fullscreen", "1"));
game->config.music = atoi(GetConfigOptionDefault(game, "SuperDerpy", "music", "10"));
game->config.voice = atoi(GetConfigOptionDefault(game, "SuperDerpy", "voice", "10"));
game->config.fx = atoi(GetConfigOptionDefault(game, "SuperDerpy", "fx", "10"));
game->config.debug = atoi(GetConfigOptionDefault(game, "SuperDerpy", "debug", "0"));
game->config.width = atoi(GetConfigOptionDefault(game, "SuperDerpy", "width", "1280"));
if (game->config.width<320) game->config.width=320;
game->config.height = atoi(GetConfigOptionDefault(game, "SuperDerpy", "height", "720"));
if (game->config.height<180) game->config.height=180;
2016-08-16 18:01:12 +02:00
game->_priv.showconsole = game->config.debug;
2012-02-25 22:26:31 +01:00
if(!al_init_image_addon()) {
fprintf(stderr, "failed to initialize image addon!\n");
2012-04-14 22:26:33 +02:00
/*al_show_native_message_box(display, "Error", "Error", "Failed to initialize al_init_image_addon!",
2016-07-02 23:23:08 +02:00
NULL, ALLEGRO_MESSAGEBOX_ERROR);*/
return NULL;
2012-02-25 22:26:31 +01:00
}
2012-02-15 23:57:06 +01:00
2012-02-25 22:26:31 +01:00
if(!al_init_acodec_addon()){
fprintf(stderr, "failed to initialize audio codecs!\n");
return NULL;
2012-02-25 22:26:31 +01:00
}
2012-02-19 19:17:04 +01:00
2012-02-25 22:26:31 +01:00
if(!al_install_audio()){
fprintf(stderr, "failed to initialize audio!\n");
return NULL;
2012-02-25 22:26:31 +01:00
}
2012-02-15 23:57:06 +01:00
2012-02-25 22:26:31 +01:00
if(!al_install_keyboard()){
fprintf(stderr, "failed to initialize keyboard!\n");
return NULL;
2012-02-25 22:26:31 +01:00
}
2012-02-15 23:57:06 +01:00
2012-05-09 10:58:45 +02:00
if(!al_init_primitives_addon()){
fprintf(stderr, "failed to initialize primitives!\n");
return NULL;
2012-05-09 10:58:45 +02:00
}
2016-06-27 21:21:59 +02:00
2016-07-02 23:23:08 +02:00
if(!al_install_mouse()) {
fprintf(stderr, "failed to initialize the mouse!\n");
return NULL;
2016-07-02 23:23:08 +02:00
}
2012-02-25 22:26:31 +01:00
al_init_font_addon();
2012-02-15 23:57:06 +01:00
2012-02-25 22:26:31 +01:00
if(!al_init_ttf_addon()){
fprintf(stderr, "failed to initialize fonts!\n");
return NULL;
2012-02-25 22:26:31 +01:00
}
2012-02-16 12:48:48 +01:00
game->touch = false;
if (!atoi(GetConfigOptionDefault(game, "SuperDerpy", "disableTouch", "0"))) {
game->touch = al_install_touch_input();
}
2016-11-06 03:10:43 +01:00
#ifdef LIBSUPERDERPY_MOUSE_EMULATION
if (game->touch) {
al_set_mouse_emulation_mode(ALLEGRO_MOUSE_EMULATION_TRANSPARENT);
}
2016-11-06 03:10:43 +01:00
#endif
2016-12-05 23:56:41 +01:00
al_install_joystick();
al_set_new_display_flags(ALLEGRO_PROGRAMMABLE_PIPELINE | (game->config.fullscreen ? ALLEGRO_FULLSCREEN_WINDOW : ALLEGRO_WINDOWED) | ALLEGRO_RESIZABLE | ALLEGRO_OPENGL ); // TODO: make ALLEGRO_PROGRAMMABLE_PIPELINE game-optional
al_set_new_display_option(ALLEGRO_VSYNC, 2-atoi(GetConfigOptionDefault(game, "SuperDerpy", "vsync", "1")), ALLEGRO_SUGGEST);
al_set_new_display_option(ALLEGRO_OPENGL, atoi(GetConfigOptionDefault(game, "SuperDerpy", "opengl", "1")), ALLEGRO_SUGGEST);
2017-06-26 03:18:54 +02:00
#ifdef LIBSUPERDERPY_ORIENTATION_LANDSCAPE
al_set_new_display_option(ALLEGRO_SUPPORTED_ORIENTATIONS, ALLEGRO_DISPLAY_ORIENTATION_LANDSCAPE, ALLEGRO_SUGGEST);
#elif defined(LIBSUPERDERPY_ORIENTATION_PORTRAIT)
al_set_new_display_option(ALLEGRO_SUPPORTED_ORIENTATIONS, ALLEGRO_DISPLAY_ORIENTATION_PORTRAIT, ALLEGRO_SUGGEST);
#endif
2016-06-27 21:20:02 +02:00
#ifdef ALLEGRO_WINDOWS
al_set_new_window_position(20, 40); // workaround nasty Windows bug with window being created off-screen
#endif
2016-08-23 02:13:15 +02:00
al_set_new_window_title(game->name);
game->display = al_create_display(game->config.width, game->config.height);
if(!game->display) {
2012-02-25 22:26:31 +01:00
fprintf(stderr, "failed to create display!\n");
return NULL;
2012-02-25 22:26:31 +01:00
}
2016-11-06 03:10:43 +01:00
#ifdef ALLEGRO_ANDROID
al_android_set_apk_file_interface();
al_android_set_apk_fs_interface();
#endif
2016-08-23 02:13:15 +02:00
SetupViewport(game, viewport);
2012-12-28 02:55:52 +01:00
PrintConsole(game, "Viewport %dx%d", game->viewport.width, game->viewport.height);
2012-12-28 02:55:52 +01:00
ALLEGRO_BITMAP *icon = al_load_bitmap(GetDataFilePath(game, GetGameName(game, "icons/%s.png")));
al_set_display_icon(game->display, icon);
al_destroy_bitmap(icon);
if (game->config.fullscreen) al_hide_mouse_cursor(game->display);
2016-07-02 23:23:08 +02:00
al_inhibit_screensaver(true);
2016-06-27 21:20:02 +02:00
al_set_new_bitmap_flags(ALLEGRO_MIN_LINEAR);
game->_priv.gamestates = NULL;
game->_priv.gamestate_scheduled = false;
2016-08-29 22:47:55 +02:00
al_init_user_event_source(&(game->event_source));
game->_priv.event_queue = al_create_event_queue();
if(!game->_priv.event_queue) {
FatalError(game, true, "Failed to create event queue.");
al_destroy_display(game->display);
return NULL;
2012-02-25 22:26:31 +01:00
}
2012-02-15 23:57:06 +01:00
game->audio.v = al_create_voice(44100, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2);
game->audio.mixer = al_create_mixer(44100, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);
game->audio.fx = al_create_mixer(44100, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);
game->audio.music = al_create_mixer(44100, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);
game->audio.voice = al_create_mixer(44100, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);
al_attach_mixer_to_voice(game->audio.mixer, game->audio.v);
al_attach_mixer_to_mixer(game->audio.fx, game->audio.mixer);
al_attach_mixer_to_mixer(game->audio.music, game->audio.mixer);
al_attach_mixer_to_mixer(game->audio.voice, game->audio.mixer);
al_set_mixer_gain(game->audio.fx, game->config.fx/10.0);
al_set_mixer_gain(game->audio.music, game->config.music/10.0);
al_set_mixer_gain(game->audio.voice, game->config.voice/10.0);
setlocale(LC_NUMERIC, "C");
2016-06-27 21:20:02 +02:00
game->_priv.argc = argc;
game->_priv.argv = argv;
game->data = NULL;
game->shuttingdown = false;
game->restart = false;
game->show_loading_on_launch = false;
return game;
}
2012-04-09 16:41:10 +02:00
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT int libsuperderpy_run(struct Game *game) {
al_register_event_source(game->_priv.event_queue, al_get_display_event_source(game->display));
al_register_event_source(game->_priv.event_queue, al_get_mouse_event_source());
al_register_event_source(game->_priv.event_queue, al_get_keyboard_event_source());
2016-12-05 23:56:41 +01:00
al_register_event_source(game->_priv.event_queue, al_get_joystick_event_source());
if (game->touch) {
al_register_event_source(game->_priv.event_queue, al_get_touch_input_event_source());
2016-11-06 03:10:43 +01:00
#ifdef LIBSUPERDERPY_MOUSE_EMULATION
al_register_event_source(game->_priv.event_queue, al_get_touch_input_mouse_emulation_event_source());
2016-11-06 03:10:43 +01:00
#endif
}
2016-08-29 22:47:55 +02:00
al_register_event_source(game->_priv.event_queue, &(game->event_source));
2012-04-09 16:41:10 +02:00
al_clear_to_color(al_map_rgb(0,0,0));
game->_priv.timer = al_create_timer(ALLEGRO_BPS_TO_SECS(60)); // logic timer
if(!game->_priv.timer) {
FatalError(game, true, "Failed to create logic timer.");
return 1;
}
al_register_event_source(game->_priv.event_queue, al_get_timer_event_source(game->_priv.timer));
al_flip_display();
al_start_timer(game->_priv.timer);
struct Gamestate *tmp = game->_priv.gamestates;
while (tmp) {
// don't show loading screen on init if requested
tmp->showLoading = game->show_loading_on_launch;
tmp = tmp->next;
}
2012-12-28 03:42:59 +01:00
char libname[1024] = {};
snprintf(libname, 1024, "libsuperderpy-%s-loading" LIBRARY_EXTENSION, game->name);
void *handle = dlopen(libname, RTLD_NOW);
2012-12-28 03:42:59 +01:00
if (!handle) {
2016-07-05 01:30:06 +02:00
FatalError(game, true, "Error while initializing loading screen: %s", dlerror());
return 2;
2012-12-28 03:42:59 +01:00
} else {
#define GS_LOADINGERROR FatalError(game, true, "Error on resolving loading symbol: %s", dlerror()); return 3;
2012-12-28 03:42:59 +01:00
if (!(game->_priv.loading.Draw = dlsym(handle, "Draw"))) { GS_LOADINGERROR; }
if (!(game->_priv.loading.Load = dlsym(handle, "Load"))) { GS_LOADINGERROR; }
if (!(game->_priv.loading.Start = dlsym(handle, "Start"))) { GS_LOADINGERROR; }
if (!(game->_priv.loading.Stop = dlsym(handle, "Stop"))) { GS_LOADINGERROR; }
if (!(game->_priv.loading.Unload = dlsym(handle, "Unload"))) { GS_LOADINGERROR; }
2012-12-28 03:42:59 +01:00
}
game->_priv.loading.data = (*game->_priv.loading.Load)(game);
2012-12-28 03:42:59 +01:00
bool redraw = false;
2016-11-06 03:10:43 +01:00
game->_priv.draw = true;
2012-02-25 22:26:31 +01:00
while(1) {
ClearGarbage(game);
2016-09-08 00:32:21 +02:00
// TODO: split mainloop to functions to make it readable
2012-02-25 22:26:31 +01:00
ALLEGRO_EVENT ev;
if (game->_priv.draw && ((redraw && al_is_event_queue_empty(game->_priv.event_queue)) || (game->_priv.gamestate_scheduled))) {
game->_priv.gamestate_scheduled = false;
struct Gamestate *tmp = game->_priv.gamestates;
2016-08-13 18:01:46 +02:00
game->_priv.tmp_gamestate.toLoad = 0;
game->_priv.tmp_gamestate.loaded = 0;
2016-09-08 00:32:21 +02:00
// TODO: support gamestate dependences
while (tmp) {
if (tmp->pending_stop) {
PrintConsole(game, "Stopping gamestate \"%s\"...", tmp->name);
game->_priv.current_gamestate = tmp;
(*tmp->api->Gamestate_Stop)(game, tmp->data);
2012-12-26 20:02:40 +01:00
tmp->started = false;
tmp->pending_stop = false;
2012-12-27 21:05:28 +01:00
}
if (tmp->pending_load) game->_priv.tmp_gamestate.toLoad++;
2012-12-27 21:05:28 +01:00
tmp=tmp->next;
}
tmp = game->_priv.gamestates;
game->_priv.tmp_gamestate.t = -1;
2016-06-27 21:21:59 +02:00
2012-12-27 21:05:28 +01:00
while (tmp) {
if (tmp->pending_unload) {
PrintConsole(game, "Unloading gamestate \"%s\"...", tmp->name);
al_stop_timer(game->_priv.timer);
2012-12-26 20:02:40 +01:00
tmp->loaded = false;
tmp->pending_unload = false;
game->_priv.current_gamestate = tmp;
(*tmp->api->Gamestate_Unload)(game, tmp->data);
2016-08-21 21:58:47 +02:00
al_resume_timer(game->_priv.timer);
}
if (tmp->pending_load) {
al_stop_timer(game->_priv.timer);
2012-12-28 02:55:52 +01:00
if (!tmp->api) {
PrintConsole(game, "Opening gamestate \"%s\"...", tmp->name);
char libname[1024];
snprintf(libname, 1024, "libsuperderpy-%s-%s" LIBRARY_EXTENSION, game->name, tmp->name);
tmp->handle = dlopen(libname,RTLD_NOW);
if (!tmp->handle) {
FatalError(game, false, "Error while opening gamestate \"%s\": %s", tmp->name, dlerror());
tmp->pending_load = false;
tmp->pending_start = false;
} else {
tmp->api = malloc(sizeof(struct Gamestate_API));
#define GS_ERROR FatalError(game, false, "Error on resolving gamestate symbol: %s", dlerror()); tmp->pending_load = false; tmp->pending_start = false; tmp=tmp->next; continue;
if (!(tmp->api->Gamestate_Draw = dlsym(tmp->handle, "Gamestate_Draw"))) { GS_ERROR; }
if (!(tmp->api->Gamestate_Logic = dlsym(tmp->handle, "Gamestate_Logic"))) { GS_ERROR; }
if (!(tmp->api->Gamestate_Load = dlsym(tmp->handle, "Gamestate_Load"))) { GS_ERROR; }
if (!(tmp->api->Gamestate_Start = dlsym(tmp->handle, "Gamestate_Start"))) { GS_ERROR; }
if (!(tmp->api->Gamestate_Pause = dlsym(tmp->handle, "Gamestate_Pause"))) { GS_ERROR; }
if (!(tmp->api->Gamestate_Resume = dlsym(tmp->handle, "Gamestate_Resume"))) { GS_ERROR; }
if (!(tmp->api->Gamestate_Stop = dlsym(tmp->handle, "Gamestate_Stop"))) { GS_ERROR; }
if (!(tmp->api->Gamestate_Unload = dlsym(tmp->handle, "Gamestate_Unload"))) { GS_ERROR; }
if (!(tmp->api->Gamestate_ProcessEvent = dlsym(tmp->handle, "Gamestate_ProcessEvent"))) { GS_ERROR; }
if (!(tmp->api->Gamestate_Reload = dlsym(tmp->handle, "Gamestate_Reload"))) { GS_ERROR; }
if (!(tmp->api->Gamestate_ProgressCount = dlsym(tmp->handle, "Gamestate_ProgressCount"))) { GS_ERROR; }
}
}
if (tmp->api) {
PrintConsole(game, "Loading gamestate \"%s\"...", tmp->name);
game->_priv.tmp_gamestate.p = 0;
DrawGamestates(game);
2015-03-15 05:38:15 +01:00
if (tmp->showLoading) {
(*game->_priv.loading.Draw)(game, game->_priv.loading.data, game->_priv.tmp_gamestate.loaded/(float)game->_priv.tmp_gamestate.toLoad);
2015-03-15 05:38:15 +01:00
}
DrawConsole(game);
if (al_get_time() - game->_priv.tmp_gamestate.t >= 1/60.0) {
2016-06-27 21:21:59 +02:00
al_flip_display();
game->_priv.tmp_gamestate.t = al_get_time();
2016-06-27 21:21:59 +02:00
}
game->_priv.tmp_gamestate.tmp = tmp;
game->_priv.current_gamestate = tmp;
tmp->data = (*tmp->api->Gamestate_Load)(game, &GamestateProgress);
game->_priv.tmp_gamestate.loaded++;
tmp->loaded = true;
tmp->pending_load = false;
}
2016-08-21 21:58:47 +02:00
al_resume_timer(game->_priv.timer);
}
2012-12-27 21:05:28 +01:00
tmp=tmp->next;
}
bool gameActive = false;
tmp=game->_priv.gamestates;
2012-12-27 21:05:28 +01:00
while (tmp) {
if ((tmp->pending_start) && (tmp->loaded)) {
PrintConsole(game, "Starting gamestate \"%s\"...", tmp->name);
al_stop_timer(game->_priv.timer);
game->_priv.current_gamestate = tmp;
2012-12-27 21:05:28 +01:00
tmp->started = true;
tmp->pending_start = false;
(*tmp->api->Gamestate_Start)(game, tmp->data);
al_resume_timer(game->_priv.timer);
2012-12-27 21:05:28 +01:00
}
2012-12-27 21:05:28 +01:00
if ((tmp->started) || (tmp->pending_start) || (tmp->pending_load)) gameActive = true;
tmp=tmp->next;
}
if (!gameActive) {
PrintConsole(game, "No gamestates left, exiting...");
break;
}
DrawGamestates(game);
DrawConsole(game);
2012-02-25 22:19:12 +01:00
al_flip_display();
redraw = false;
2012-09-26 03:19:10 +02:00
} else {
al_wait_for_event(game->_priv.event_queue, &ev);
if (game->eventHandler) {
if ((*game->eventHandler)(game, &ev)) {
continue;
}
}
if ((ev.type == ALLEGRO_EVENT_TIMER) && (ev.timer.source == game->_priv.timer)) {
LogicGamestates(game);
redraw = true;
2012-09-26 03:19:10 +02:00
}
else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
break;
}
2016-11-06 03:10:43 +01:00
else if(ev.type == ALLEGRO_EVENT_DISPLAY_HALT_DRAWING) {
PrintConsole(game, "Engine halted.");
2016-11-06 03:10:43 +01:00
game->_priv.draw = false;
al_stop_timer(game->_priv.timer);
2016-11-06 03:10:43 +01:00
al_detach_voice(game->audio.v);
FreezeGamestates(game);
if (game->_priv.console) Console_Unload(game);
2016-11-06 03:10:43 +01:00
al_acknowledge_drawing_halt(game->display);
}
else if(ev.type == ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING) {
game->_priv.draw = true;
al_acknowledge_drawing_resume(game->display);
Console_Load(game);
PrintConsole(game, "Engine resumed.");
ReloadGamestates(game);
UnfreezeGamestates(game);
al_attach_mixer_to_voice(game->audio.mixer, game->audio.v);
al_resume_timer(game->_priv.timer);
2016-11-06 03:10:43 +01:00
}
else if(ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE) {
al_acknowledge_resize(game->display);
SetupViewport(game, game->viewport_config);
}
#ifdef ALLEGRO_ANDROID
2016-11-07 01:45:12 +01:00
else if ((ev.type == ALLEGRO_EVENT_KEY_DOWN) && (ev.keyboard.keycode == ALLEGRO_KEY_MENU)) {
2016-06-27 21:20:02 +02:00
#else
else if ((ev.type == ALLEGRO_EVENT_KEY_DOWN) && ((ev.keyboard.keycode == ALLEGRO_KEY_TILDE) || (ev.keyboard.keycode == ALLEGRO_KEY_BACKQUOTE))) {
2016-06-27 21:20:02 +02:00
#endif
game->_priv.showconsole = !game->_priv.showconsole;
}
else if ((ev.type == ALLEGRO_EVENT_KEY_DOWN) && (game->config.debug) && (ev.keyboard.keycode == ALLEGRO_KEY_F1)) {
int i;
for (i=0; i<512; i++) {
LogicGamestates(game);
2012-09-26 03:19:10 +02:00
}
game->_priv.showconsole = true;
PrintConsole(game, "DEBUG: 512 frames skipped...");
} else if ((ev.type == ALLEGRO_EVENT_KEY_DOWN) && (game->config.debug) && (ev.keyboard.keycode == ALLEGRO_KEY_F10)) {
double speed = ALLEGRO_BPS_TO_SECS(al_get_timer_speed(game->_priv.timer)); // inverting
speed -= 10;
if (speed<10) speed = 10;
al_set_timer_speed(game->_priv.timer, ALLEGRO_BPS_TO_SECS(speed));
game->_priv.showconsole = true;
PrintConsole(game, "DEBUG: Gameplay speed: %.2fx", speed/60.0);
} else if ((ev.type == ALLEGRO_EVENT_KEY_DOWN) && (game->config.debug) && (ev.keyboard.keycode == ALLEGRO_KEY_F11)) {
double speed = ALLEGRO_BPS_TO_SECS(al_get_timer_speed(game->_priv.timer)); // inverting
speed += 10;
if (speed>600) speed = 600;
al_set_timer_speed(game->_priv.timer, ALLEGRO_BPS_TO_SECS(speed));
game->_priv.showconsole = true;
PrintConsole(game, "DEBUG: Gameplay speed: %.2fx", speed/60.0);
} else if ((ev.type == ALLEGRO_EVENT_KEY_DOWN) && (ev.keyboard.keycode == ALLEGRO_KEY_F12)) {
ALLEGRO_PATH *path = al_get_standard_path(ALLEGRO_USER_DOCUMENTS_PATH);
char filename[255] = { };
snprintf(filename, 255, "%s_%lld_%ld.png", game->name, (long long)time(NULL), clock());
al_set_path_filename(path, filename);
al_save_bitmap(al_path_cstr(path, ALLEGRO_NATIVE_PATH_SEP), al_get_backbuffer(game->display));
PrintConsole(game, "Screenshot stored in %s", al_path_cstr(path, ALLEGRO_NATIVE_PATH_SEP));
al_destroy_path(path);
2016-07-02 23:23:08 +02:00
}
EventGamestates(game, &ev);
2012-02-25 22:26:31 +01:00
}
}
return 0;
}
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT void libsuperderpy_destroy(struct Game *game) {
game->shuttingdown = true;
2016-08-16 18:01:12 +02:00
ClearGarbage(game);
// in case of restart
2016-08-16 18:01:12 +02:00
struct Gamestate *tmp = game->_priv.gamestates, *pom;
while (tmp) {
if (tmp->started) {
PrintConsole(game, "Stopping gamestate \"%s\"...", tmp->name);
game->_priv.current_gamestate = tmp;
(*tmp->api->Gamestate_Stop)(game, tmp->data);
tmp->started = false;
}
if (tmp->loaded) {
PrintConsole(game, "Unloading gamestate \"%s\"...", tmp->name);
game->_priv.current_gamestate = tmp;
(*tmp->api->Gamestate_Unload)(game, tmp->data);
tmp->loaded = false;
}
if (tmp->handle && !RUNNING_ON_VALGRIND) {
PrintConsole(game, "Closing gamestate \"%s\"...", tmp->name);
dlclose(tmp->handle);
}
2016-08-16 18:01:12 +02:00
free(tmp->name);
if (tmp->api) {
free(tmp->api);
}
2016-08-16 18:01:12 +02:00
pom = tmp->next;
free(tmp);
tmp=pom;
}
ClearScreen(game);
PrintConsole(game, "Shutting down...");
DrawConsole(game);
2012-02-17 13:25:06 +01:00
al_flip_display();
2016-08-16 18:01:12 +02:00
while (game->_priv.garbage) {
free(game->_priv.garbage->data);
game->_priv.garbage = game->_priv.garbage->next;
}
(*game->_priv.loading.Unload)(game, game->_priv.loading.data);
al_destroy_timer(game->_priv.timer);
Console_Unload(game);
al_destroy_display(game->display);
2016-08-29 22:47:55 +02:00
al_destroy_user_event_source(&(game->event_source));
al_destroy_event_queue(game->_priv.event_queue);
al_destroy_mixer(game->audio.fx);
al_destroy_mixer(game->audio.music);
al_destroy_mixer(game->audio.mixer);
al_destroy_voice(game->audio.v);
2012-02-25 22:26:31 +01:00
al_uninstall_audio();
DeinitConfig(game);
al_uninstall_system();
al_shutdown_ttf_addon();
al_shutdown_font_addon();
char** argv = game->_priv.argv;
2016-08-16 18:01:12 +02:00
bool restart = game->restart;
free(game);
2016-08-16 18:01:12 +02:00
if (restart) {
2016-09-08 00:32:21 +02:00
#ifdef ALLEGRO_MACOSX
chdir(game->_priv.cwd);
#endif
execv(argv[0], argv); // FIXME: on OSX there's chdir called which might break it
}
2012-02-15 23:57:06 +01:00
}