mirror of
https://gitlab.com/dosowisko.net/libsuperderpy.git
synced 2025-02-12 16:14:23 +01:00
Refactor the engine's main loop.
This commit is contained in:
parent
c5fcba57cb
commit
3e7d9812a3
9 changed files with 446 additions and 345 deletions
|
@ -85,23 +85,25 @@ if(EMSCRIPTEN)
|
||||||
set(CMAKE_EXECUTABLE_SUFFIX ".bc")
|
set(CMAKE_EXECUTABLE_SUFFIX ".bc")
|
||||||
set(CMAKE_SHARED_LIBRARY_SUFFIX ".so")
|
set(CMAKE_SHARED_LIBRARY_SUFFIX ".so")
|
||||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s SIDE_MODULE=1")
|
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s SIDE_MODULE=1")
|
||||||
set(EMSCRIPTEN_USE_FLAGS -s USE_SDL=2 -s USE_FREETYPE=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_OGG=1 -s USE_VORBIS=1 -s FULL_ES2=1)
|
set(EMSCRIPTEN_FLAGS -s USE_SDL=2 -s USE_FREETYPE=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_OGG=1 -s USE_VORBIS=1 -s FULL_ES2=1 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s NO_EXIT_RUNTIME=0 -s PRECISE_F32=1)
|
||||||
|
|
||||||
set(LIBSUPERDERPY_EMSCRIPTEN_MODE "asm.js" CACHE STRING "Emscripten compilation mode (JavaScript or WebAssembly)")
|
set(LIBSUPERDERPY_EMSCRIPTEN_MODE "asm.js" CACHE STRING "Emscripten compilation mode (JavaScript or WebAssembly)")
|
||||||
set_property(CACHE LIBSUPERDERPY_EMSCRIPTEN_MODE PROPERTY STRINGS "asm.js;wasm")
|
set_property(CACHE LIBSUPERDERPY_EMSCRIPTEN_MODE PROPERTY STRINGS "asm.js;wasm")
|
||||||
if("${LIBSUPERDERPY_EMSCRIPTEN_MODE}" STREQUAL "wasm")
|
if("${LIBSUPERDERPY_EMSCRIPTEN_MODE}" STREQUAL "wasm")
|
||||||
# https://github.com/kripken/emscripten/issues/5436
|
# https://github.com/kripken/emscripten/issues/5436
|
||||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -s WASM=1")
|
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -s WASM=1")
|
||||||
set(EMSCRIPTEN_USE_FLAGS ${EMSCRIPTEN_USE_FLAGS} -s WASM=1)
|
set(EMSCRIPTEN_FLAGS ${EMSCRIPTEN_FLAGS} -s WASM=1)
|
||||||
set(CMAKE_SHARED_MODULE_SUFFIX ".wasm")
|
set(CMAKE_SHARED_MODULE_SUFFIX ".wasm")
|
||||||
add_definitions(-DLIBSUPERDERPY_WASM=1)
|
add_definitions(-DLIBSUPERDERPY_WASM=1)
|
||||||
else()
|
else()
|
||||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -s WASM=0")
|
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -s WASM=0")
|
||||||
set(EMSCRIPTEN_USE_FLAGS ${EMSCRIPTEN_USE_FLAGS} -s WASM=0)
|
set(EMSCRIPTEN_FLAGS ${EMSCRIPTEN_FLAGS} -s WASM=0)
|
||||||
set(CMAKE_SHARED_MODULE_SUFFIX ".js")
|
set(CMAKE_SHARED_MODULE_SUFFIX ".js")
|
||||||
endif()
|
endif()
|
||||||
set(LIBSUPERDERPY_USE_WEBGL2 "no" CACHE STRING "Use WebGL 2 context")
|
|
||||||
|
set(LIBSUPERDERPY_USE_WEBGL2 NO CACHE BOOL "Use WebGL 2 context")
|
||||||
if(LIBSUPERDERPY_USE_WEBGL2)
|
if(LIBSUPERDERPY_USE_WEBGL2)
|
||||||
set(EMSCRIPTEN_USE_FLAGS ${EMSCRIPTEN_USE_FLAGS} -s USE_WEBGL2=1)
|
set(EMSCRIPTEN_FLAGS ${EMSCRIPTEN_FLAGS} -s USE_WEBGL2=1)
|
||||||
endif(LIBSUPERDERPY_USE_WEBGL2)
|
endif(LIBSUPERDERPY_USE_WEBGL2)
|
||||||
|
|
||||||
if(CMAKE_INSTALL_PREFIX MATCHES "/usr/local")
|
if(CMAKE_INSTALL_PREFIX MATCHES "/usr/local")
|
||||||
|
@ -246,7 +248,7 @@ endif()
|
||||||
add_custom_target(${LIBSUPERDERPY_GAMENAME}_js
|
add_custom_target(${LIBSUPERDERPY_GAMENAME}_js
|
||||||
DEPENDS ${LIBSUPERDERPY_GAMENAME}_install ${LIBSUPERDERPY_GAMENAME}_flac_to_ogg
|
DEPENDS ${LIBSUPERDERPY_GAMENAME}_install ${LIBSUPERDERPY_GAMENAME}_flac_to_ogg
|
||||||
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/${LIBSUPERDERPY_GAMENAME}"
|
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/${LIBSUPERDERPY_GAMENAME}"
|
||||||
COMMAND "${CMAKE_C_COMPILER}" ${CFLAGS_LIST} ../${LIBSUPERDERPY_GAMENAME}${CMAKE_EXECUTABLE_SUFFIX} ../libsuperderpy${CMAKE_SHARED_LIBRARY_SUFFIX} ../libsuperderpy-${LIBSUPERDERPY_GAMENAME}${CMAKE_SHARED_LIBRARY_SUFFIX} ${ALLEGRO5_LIBRARIES} ${ALLEGRO5_FONT_LIBRARIES} ${ALLEGRO5_TTF_LIBRARIES} ${ALLEGRO5_PRIMITIVES_LIBRARIES} ${ALLEGRO5_AUDIO_LIBRARIES} ${ALLEGRO5_ACODEC_LIBRARIES} ${ALLEGRO5_VIDEO_LIBRARIES} ${ALLEGRO5_IMAGE_LIBRARIES} ${ALLEGRO5_COLOR_LIBRARIES} ${EMSCRIPTEN_USE_FLAGS} -s MAIN_MODULE=1 -s TOTAL_MEMORY=${EMSCRIPTEN_TOTAL_MEMORY_BYTES} -o ${LIBSUPERDERPY_GAMENAME}.html --preload-file data --preload-file gamestates@/
|
COMMAND "${CMAKE_C_COMPILER}" ${CFLAGS_LIST} ../${LIBSUPERDERPY_GAMENAME}${CMAKE_EXECUTABLE_SUFFIX} ../libsuperderpy${CMAKE_SHARED_LIBRARY_SUFFIX} ../libsuperderpy-${LIBSUPERDERPY_GAMENAME}${CMAKE_SHARED_LIBRARY_SUFFIX} ${ALLEGRO5_LIBRARIES} ${ALLEGRO5_FONT_LIBRARIES} ${ALLEGRO5_TTF_LIBRARIES} ${ALLEGRO5_PRIMITIVES_LIBRARIES} ${ALLEGRO5_AUDIO_LIBRARIES} ${ALLEGRO5_ACODEC_LIBRARIES} ${ALLEGRO5_VIDEO_LIBRARIES} ${ALLEGRO5_IMAGE_LIBRARIES} ${ALLEGRO5_COLOR_LIBRARIES} ${EMSCRIPTEN_FLAGS} -s MAIN_MODULE=1 -s TOTAL_MEMORY=${EMSCRIPTEN_TOTAL_MEMORY_BYTES} -o ${LIBSUPERDERPY_GAMENAME}.html --preload-file data --preload-file gamestates@/
|
||||||
COMMAND rm -rf data gamestates
|
COMMAND rm -rf data gamestates
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,6 +5,7 @@ SET(SRC_LIST
|
||||||
timeline.c
|
timeline.c
|
||||||
gamestate.c
|
gamestate.c
|
||||||
libsuperderpy.c
|
libsuperderpy.c
|
||||||
|
mainloop.c
|
||||||
character.c
|
character.c
|
||||||
internal.c
|
internal.c
|
||||||
tween.c
|
tween.c
|
||||||
|
|
|
@ -76,7 +76,6 @@ SYMBOL_EXPORT void LoadGamestate(struct Game* game, const char* name) {
|
||||||
gs->showLoading = true;
|
gs->showLoading = true;
|
||||||
}
|
}
|
||||||
PrintConsole(game, "Gamestate \"%s\" marked to be LOADED.", name);
|
PrintConsole(game, "Gamestate \"%s\" marked to be LOADED.", name);
|
||||||
game->_priv.gamestate_scheduled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SYMBOL_EXPORT void UnloadGamestate(struct Game* game, const char* name) {
|
SYMBOL_EXPORT void UnloadGamestate(struct Game* game, const char* name) {
|
||||||
|
@ -98,7 +97,6 @@ SYMBOL_EXPORT void UnloadGamestate(struct Game* game, const char* name) {
|
||||||
PrintConsole(game, "Tried to unload nonexisitent gamestate \"%s\"", name);
|
PrintConsole(game, "Tried to unload nonexisitent gamestate \"%s\"", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
game->_priv.gamestate_scheduled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SYMBOL_EXPORT void StartGamestate(struct Game* game, const char* name) {
|
SYMBOL_EXPORT void StartGamestate(struct Game* game, const char* name) {
|
||||||
|
@ -118,7 +116,6 @@ SYMBOL_EXPORT void StartGamestate(struct Game* game, const char* name) {
|
||||||
LoadGamestate(game, name);
|
LoadGamestate(game, name);
|
||||||
return StartGamestate(game, name);
|
return StartGamestate(game, name);
|
||||||
}
|
}
|
||||||
game->_priv.gamestate_scheduled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SYMBOL_EXPORT void StopGamestate(struct Game* game, const char* name) {
|
SYMBOL_EXPORT void StopGamestate(struct Game* game, const char* name) {
|
||||||
|
@ -139,7 +136,6 @@ SYMBOL_EXPORT void StopGamestate(struct Game* game, const char* name) {
|
||||||
PrintConsole(game, "Tried to stop nonexisitent gamestate \"%s\"", name);
|
PrintConsole(game, "Tried to stop nonexisitent gamestate \"%s\"", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
game->_priv.gamestate_scheduled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SYMBOL_EXPORT void PauseGamestate(struct Game* game, const char* name) {
|
SYMBOL_EXPORT void PauseGamestate(struct Game* game, const char* name) {
|
||||||
|
|
|
@ -76,4 +76,20 @@ void StopCurrentGamestate(struct Game* game);
|
||||||
void PauseCurrentGamestate(struct Game* game);
|
void PauseCurrentGamestate(struct Game* game);
|
||||||
void UnloadCurrentGamestate(struct Game* game);
|
void UnloadCurrentGamestate(struct Game* game);
|
||||||
|
|
||||||
|
// Gamestate API
|
||||||
|
|
||||||
|
extern int Gamestate_ProgressCount;
|
||||||
|
void Gamestate_Draw(struct Game* game, struct GamestateResources* data);
|
||||||
|
void Gamestate_Logic(struct Game* game, struct GamestateResources* data, double delta);
|
||||||
|
void Gamestate_Tick(struct Game* game, struct GamestateResources* data);
|
||||||
|
void* Gamestate_Load(struct Game* game, void (*progress)(struct Game*));
|
||||||
|
void Gamestate_PostLoad(struct Game* game, struct GamestateResources* data);
|
||||||
|
void Gamestate_Start(struct Game* game, struct GamestateResources* data);
|
||||||
|
void Gamestate_Pause(struct Game* game, struct GamestateResources* data);
|
||||||
|
void Gamestate_Resume(struct Game* game, struct GamestateResources* data);
|
||||||
|
void Gamestate_Stop(struct Game* game, struct GamestateResources* data);
|
||||||
|
void Gamestate_Unload(struct Game* game, struct GamestateResources* data);
|
||||||
|
void Gamestate_ProcessEvent(struct Game* game, struct GamestateResources* data, ALLEGRO_EVENT* ev);
|
||||||
|
void Gamestate_Reload(struct Game* game, struct GamestateResources* data);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -542,15 +542,11 @@ SYMBOL_INTERNAL char* GetLibraryPath(struct Game* game, char* filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SYMBOL_INTERNAL void PauseExecution(struct Game* game) {
|
SYMBOL_INTERNAL void PauseExecution(struct Game* game) {
|
||||||
struct Gamestate* tmp = game->_priv.gamestates;
|
|
||||||
while (tmp) {
|
|
||||||
if (!tmp->paused && tmp->loaded && tmp->started && tmp->api->Gamestate_Pause) {
|
|
||||||
tmp->api->Gamestate_Pause(game, tmp->data);
|
|
||||||
}
|
|
||||||
tmp = tmp->next;
|
|
||||||
}
|
|
||||||
game->_priv.paused = true;
|
game->_priv.paused = true;
|
||||||
PrintConsole(game, "DEBUG: game execution paused.");
|
al_stop_timer(game->_priv.timer);
|
||||||
|
al_detach_voice(game->audio.v);
|
||||||
|
FreezeGamestates(game);
|
||||||
|
PrintConsole(game, "Engine halted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
SYMBOL_INTERNAL void ReloadCode(struct Game* game) {
|
SYMBOL_INTERNAL void ReloadCode(struct Game* game) {
|
||||||
|
@ -576,15 +572,12 @@ SYMBOL_INTERNAL void ReloadCode(struct Game* game) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SYMBOL_INTERNAL void ResumeExecution(struct Game* game) {
|
SYMBOL_INTERNAL void ResumeExecution(struct Game* game) {
|
||||||
struct Gamestate* tmp = game->_priv.gamestates;
|
UnfreezeGamestates(game);
|
||||||
while (tmp) {
|
al_attach_mixer_to_voice(game->audio.mixer, game->audio.v);
|
||||||
if (!tmp->paused && tmp->loaded && tmp->started && tmp->api->Gamestate_Resume) {
|
al_resume_timer(game->_priv.timer);
|
||||||
tmp->api->Gamestate_Resume(game, tmp->data);
|
|
||||||
}
|
|
||||||
tmp = tmp->next;
|
|
||||||
}
|
|
||||||
game->_priv.paused = false;
|
game->_priv.paused = false;
|
||||||
PrintConsole(game, "DEBUG: game execution resumed.");
|
game->_priv.timestamp = al_get_time();
|
||||||
|
PrintConsole(game, "Engine resumed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
SYMBOL_INTERNAL char* GetGameName(struct Game* game, const char* format) {
|
SYMBOL_INTERNAL char* GetGameName(struct Game* game, const char* format) {
|
||||||
|
|
|
@ -167,14 +167,19 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char*
|
||||||
game->joystick = al_install_joystick();
|
game->joystick = al_install_joystick();
|
||||||
}
|
}
|
||||||
|
|
||||||
int fullscreen = ALLEGRO_FULLSCREEN_WINDOW;
|
int windowMode = ALLEGRO_FULLSCREEN_WINDOW;
|
||||||
#ifdef ALLEGRO_ANDROID
|
#ifdef ALLEGRO_ANDROID
|
||||||
fullscreen |= ALLEGRO_FRAMELESS;
|
windowMode |= ALLEGRO_FRAMELESS;
|
||||||
#endif
|
#endif
|
||||||
al_set_new_display_flags((game->config.fullscreen ? (fullscreen) : ALLEGRO_WINDOWED) | ALLEGRO_RESIZABLE | ALLEGRO_OPENGL | ALLEGRO_PROGRAMMABLE_PIPELINE);
|
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
al_set_new_display_flags((al_get_new_display_flags() | ALLEGRO_WINDOWED) ^ ALLEGRO_FULLSCREEN_WINDOW);
|
windowMode = ALLEGRO_WINDOWED;
|
||||||
#endif
|
#endif
|
||||||
|
if (!game->config.fullscreen) {
|
||||||
|
windowMode = ALLEGRO_WINDOWED;
|
||||||
|
}
|
||||||
|
windowMode |= ALLEGRO_RESIZABLE;
|
||||||
|
|
||||||
|
al_set_new_display_flags(windowMode | ALLEGRO_OPENGL | ALLEGRO_PROGRAMMABLE_PIPELINE);
|
||||||
al_set_new_display_option(ALLEGRO_VSYNC, 2 - strtol(GetConfigOptionDefault(game, "SuperDerpy", "vsync", "1"), NULL, 10), ALLEGRO_SUGGEST);
|
al_set_new_display_option(ALLEGRO_VSYNC, 2 - strtol(GetConfigOptionDefault(game, "SuperDerpy", "vsync", "1"), NULL, 10), ALLEGRO_SUGGEST);
|
||||||
|
|
||||||
#ifdef LIBSUPERDERPY_ORIENTATION_LANDSCAPE
|
#ifdef LIBSUPERDERPY_ORIENTATION_LANDSCAPE
|
||||||
|
@ -244,7 +249,6 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char*
|
||||||
al_add_new_bitmap_flag(ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR);
|
al_add_new_bitmap_flag(ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR);
|
||||||
|
|
||||||
game->_priv.gamestates = NULL;
|
game->_priv.gamestates = NULL;
|
||||||
game->_priv.gamestate_scheduled = false;
|
|
||||||
|
|
||||||
al_init_user_event_source(&(game->event_source));
|
al_init_user_event_source(&(game->event_source));
|
||||||
|
|
||||||
|
@ -295,7 +299,7 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char*
|
||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
||||||
SYMBOL_EXPORT int libsuperderpy_run(struct Game* game) {
|
SYMBOL_EXPORT int libsuperderpy_start(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_display_event_source(game->display));
|
||||||
al_register_event_source(game->_priv.event_queue, al_get_keyboard_event_source());
|
al_register_event_source(game->_priv.event_queue, al_get_keyboard_event_source());
|
||||||
if (game->mouse) {
|
if (game->mouse) {
|
||||||
|
@ -341,308 +345,32 @@ SYMBOL_EXPORT int libsuperderpy_run(struct Game* game) {
|
||||||
PrintConsole(game, "Loading screen registered.");
|
PrintConsole(game, "Loading screen registered.");
|
||||||
|
|
||||||
game->_priv.timestamp = al_get_time();
|
game->_priv.timestamp = al_get_time();
|
||||||
game->_priv.draw = true;
|
game->_priv.paused = false;
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
void libsuperderpy_mainloop(void* game);
|
|
||||||
emscripten_set_main_loop_arg(libsuperderpy_mainloop, game, 0, true);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SYMBOL_INTERNAL void libsuperderpy_mainloop_exit(struct Game* game) {
|
#ifdef __EMSCRIPTEN__
|
||||||
|
SYMBOL_INTERNAL void libsuperderpy_emscripten_mainloop(void* game) {
|
||||||
|
if (!libsuperderpy_mainloop(game)) {
|
||||||
libsuperderpy_destroy(game);
|
libsuperderpy_destroy(game);
|
||||||
free(game);
|
free(game);
|
||||||
printf("Halted.\n");
|
printf("Halted.\n");
|
||||||
emscripten_cancel_main_loop();
|
emscripten_cancel_main_loop();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
SYMBOL_INTERNAL void libsuperderpy_mainloop(void* g) {
|
|
||||||
struct Game* game = (struct Game*)g;
|
|
||||||
#endif
|
#endif
|
||||||
do {
|
|
||||||
ClearGarbage(game);
|
|
||||||
|
|
||||||
// TODO: split mainloop to functions to make it readable
|
SYMBOL_EXPORT int libsuperderpy_run(struct Game* game) {
|
||||||
ALLEGRO_EVENT ev;
|
int ret = libsuperderpy_start(game);
|
||||||
if (game->_priv.draw && ((al_is_event_queue_empty(game->_priv.event_queue)) || (game->_priv.gamestate_scheduled))) {
|
if (ret) {
|
||||||
game->_priv.gamestate_scheduled = false;
|
return ret;
|
||||||
struct Gamestate* tmp = game->_priv.gamestates;
|
|
||||||
|
|
||||||
game->_priv.loading.toLoad = 0;
|
|
||||||
game->_priv.loading.loaded = 0;
|
|
||||||
game->loading_progress = 0;
|
|
||||||
|
|
||||||
// TODO: support gamestate dependences/ordering
|
|
||||||
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);
|
|
||||||
tmp->started = false;
|
|
||||||
tmp->pending_stop = false;
|
|
||||||
PrintConsole(game, "Gamestate \"%s\" stopped successfully.", tmp->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tmp->pending_load) { game->_priv.loading.toLoad++; }
|
|
||||||
tmp = tmp->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = game->_priv.gamestates;
|
|
||||||
|
|
||||||
while (tmp) {
|
|
||||||
if (tmp->pending_unload) {
|
|
||||||
PrintConsole(game, "Unloading gamestate \"%s\"...", tmp->name);
|
|
||||||
al_stop_timer(game->_priv.timer);
|
|
||||||
tmp->loaded = false;
|
|
||||||
tmp->pending_unload = false;
|
|
||||||
game->_priv.current_gamestate = tmp;
|
|
||||||
(*tmp->api->Gamestate_Unload)(game, tmp->data);
|
|
||||||
al_resume_timer(game->_priv.timer);
|
|
||||||
PrintConsole(game, "Gamestate \"%s\" unloaded successfully.", tmp->name);
|
|
||||||
}
|
|
||||||
if (tmp->pending_load) {
|
|
||||||
al_stop_timer(game->_priv.timer);
|
|
||||||
if (tmp->showLoading) {
|
|
||||||
(*game->_priv.loading.gamestate->api->Gamestate_Start)(game, game->_priv.loading.gamestate->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tmp->api) {
|
|
||||||
if (!OpenGamestate(game, tmp) || !LinkGamestate(game, tmp)) {
|
|
||||||
tmp->pending_load = false;
|
|
||||||
tmp->pending_start = false;
|
|
||||||
tmp->next = tmp;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tmp->api) {
|
|
||||||
PrintConsole(game, "Loading gamestate \"%s\"...", tmp->name);
|
|
||||||
game->_priv.loading.progress = 0;
|
|
||||||
|
|
||||||
game->_priv.loading.current = tmp;
|
|
||||||
game->_priv.current_gamestate = tmp;
|
|
||||||
|
|
||||||
struct GamestateLoadingThreadData data = {.game = game, .gamestate = tmp, .bitmap_flags = al_get_new_bitmap_flags()};
|
|
||||||
game->_priv.loading.inProgress = true;
|
|
||||||
double time = al_get_time();
|
|
||||||
game->_priv.loading.time = time;
|
|
||||||
|
|
||||||
CalculateProgress(game);
|
|
||||||
#ifndef LIBSUPERDERPY_SINGLE_THREAD
|
|
||||||
al_run_detached_thread(GamestateLoadingThread, &data);
|
|
||||||
while (game->_priv.loading.inProgress) {
|
|
||||||
DrawGamestates(game);
|
|
||||||
al_set_target_backbuffer(game->display);
|
|
||||||
double delta = al_get_time() - game->_priv.loading.time;
|
|
||||||
if (tmp->showLoading) {
|
|
||||||
(*game->_priv.loading.gamestate->api->Gamestate_Logic)(game, game->_priv.loading.gamestate->data, delta);
|
|
||||||
(*game->_priv.loading.gamestate->api->Gamestate_Draw)(game, game->_priv.loading.gamestate->data);
|
|
||||||
if (game->handlers.postdraw) {
|
|
||||||
game->handlers.postdraw(game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
game->_priv.loading.time += delta;
|
|
||||||
game->time += delta; // TODO: ability to disable passing time during loading
|
|
||||||
if (game->_priv.texture_sync) {
|
|
||||||
al_convert_memory_bitmaps();
|
|
||||||
game->_priv.texture_sync = false;
|
|
||||||
al_signal_cond(game->_priv.texture_sync_cond);
|
|
||||||
game->_priv.loading.time = al_get_time();
|
|
||||||
}
|
|
||||||
DrawConsole(game);
|
|
||||||
al_flip_display();
|
|
||||||
}
|
}
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
emscripten_set_main_loop_arg(libsuperderpy_emscripten_mainloop, game, 0, true);
|
||||||
|
return 0;
|
||||||
#else
|
#else
|
||||||
GamestateLoadingThread(&data);
|
while (libsuperderpy_mainloop(game)) {};
|
||||||
al_convert_memory_bitmaps();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
al_set_new_bitmap_flags(data.bitmap_flags);
|
|
||||||
|
|
||||||
if (tmp->api->Gamestate_PostLoad) {
|
|
||||||
PrintConsole(game, "[%s] Post-loading...", tmp->name);
|
|
||||||
tmp->api->Gamestate_PostLoad(game, tmp->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
game->_priv.loading.progress++;
|
|
||||||
CalculateProgress(game);
|
|
||||||
PrintConsole(game, "Gamestate \"%s\" loaded successfully in %f seconds.", tmp->name, al_get_time() - time);
|
|
||||||
game->_priv.loading.loaded++;
|
|
||||||
|
|
||||||
tmp->loaded = true;
|
|
||||||
tmp->pending_load = false;
|
|
||||||
}
|
|
||||||
if (tmp->showLoading) {
|
|
||||||
(*game->_priv.loading.gamestate->api->Gamestate_Stop)(game, game->_priv.loading.gamestate->data);
|
|
||||||
}
|
|
||||||
al_resume_timer(game->_priv.timer);
|
|
||||||
game->_priv.timestamp = al_get_time();
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = tmp->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game->_priv.loading.loaded) {
|
|
||||||
ReloadShaders(game, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool gameActive = false;
|
|
||||||
tmp = game->_priv.gamestates;
|
|
||||||
|
|
||||||
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;
|
|
||||||
tmp->started = true;
|
|
||||||
tmp->pending_start = false;
|
|
||||||
(*tmp->api->Gamestate_Start)(game, tmp->data);
|
|
||||||
al_resume_timer(game->_priv.timer);
|
|
||||||
game->_priv.timestamp = al_get_time();
|
|
||||||
PrintConsole(game, "Gamestate \"%s\" started successfully.", tmp->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tmp->started) || (tmp->pending_start) || (tmp->pending_load)) {
|
|
||||||
gameActive = true;
|
|
||||||
}
|
|
||||||
tmp = tmp->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gameActive) {
|
|
||||||
PrintConsole(game, "No gamestates left, exiting...");
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
libsuperderpy_mainloop_exit(game);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
al_convert_memory_bitmaps();
|
|
||||||
|
|
||||||
double delta = al_get_time() - game->_priv.timestamp;
|
|
||||||
game->_priv.timestamp += delta;
|
|
||||||
delta *= ALLEGRO_BPS_TO_SECS(al_get_timer_speed(game->_priv.timer) / (1 / 60.f));
|
|
||||||
game->time += delta;
|
|
||||||
if (!game->_priv.paused) {
|
|
||||||
LogicGamestates(game, delta);
|
|
||||||
DrawGamestates(game);
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawConsole(game);
|
|
||||||
al_flip_display();
|
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} else {
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
if (al_is_event_queue_empty(game->_priv.event_queue)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
al_wait_for_event(game->_priv.event_queue, &ev);
|
|
||||||
|
|
||||||
if (game->handlers.event) {
|
|
||||||
if ((*game->handlers.event)(game, &ev)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ev.type == ALLEGRO_EVENT_TIMER) && (ev.timer.source == game->_priv.timer)) {
|
|
||||||
TickGamestates(game);
|
|
||||||
} else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
libsuperderpy_mainloop_exit(game);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
} else if (ev.type == ALLEGRO_EVENT_DISPLAY_HALT_DRAWING) {
|
|
||||||
PrintConsole(game, "Engine halted.");
|
|
||||||
game->_priv.draw = false;
|
|
||||||
al_stop_timer(game->_priv.timer);
|
|
||||||
al_detach_voice(game->audio.v);
|
|
||||||
FreezeGamestates(game);
|
|
||||||
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);
|
|
||||||
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);
|
|
||||||
} else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE) {
|
|
||||||
al_acknowledge_resize(game->display);
|
|
||||||
SetupViewport(game, game->viewport_config);
|
|
||||||
} else if ((game->config.debug) && (game->_priv.debug.autopause) && (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT)) {
|
|
||||||
PauseExecution(game);
|
|
||||||
} else if ((game->config.debug) && (game->_priv.debug.autopause) && (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN)) {
|
|
||||||
if (game->_priv.debug.livereload) {
|
|
||||||
ReloadCode(game);
|
|
||||||
}
|
|
||||||
ResumeExecution(game);
|
|
||||||
}
|
|
||||||
#ifdef ALLEGRO_ANDROID
|
|
||||||
else if ((ev.type == ALLEGRO_EVENT_KEY_CHAR) && ((ev.keyboard.keycode == ALLEGRO_KEY_MENU) || (ev.keyboard.keycode == ALLEGRO_KEY_TILDE) || (ev.keyboard.keycode == ALLEGRO_KEY_BACKQUOTE))) {
|
|
||||||
#else
|
|
||||||
else if ((ev.type == ALLEGRO_EVENT_KEY_CHAR) && ((ev.keyboard.keycode == ALLEGRO_KEY_TILDE) || (ev.keyboard.keycode == ALLEGRO_KEY_BACKQUOTE))) {
|
|
||||||
#endif
|
|
||||||
game->_priv.showconsole = !game->_priv.showconsole;
|
|
||||||
if ((ev.keyboard.modifiers & ALLEGRO_KEYMOD_CTRL) && (game->config.debug)) {
|
|
||||||
game->_priv.showtimeline = game->_priv.showconsole;
|
|
||||||
}
|
|
||||||
} else if ((ev.type == ALLEGRO_EVENT_KEY_DOWN) && (game->config.debug) && (ev.keyboard.keycode == ALLEGRO_KEY_F1)) {
|
|
||||||
if (!game->_priv.paused) {
|
|
||||||
PauseExecution(game);
|
|
||||||
} else {
|
|
||||||
ReloadCode(game);
|
|
||||||
ResumeExecution(game);
|
|
||||||
}
|
|
||||||
} else if ((ev.type == ALLEGRO_EVENT_KEY_DOWN) && (game->config.debug) && (ev.keyboard.keycode == ALLEGRO_KEY_F9)) {
|
|
||||||
al_set_timer_speed(game->_priv.timer, ALLEGRO_BPS_TO_SECS(60.0));
|
|
||||||
game->_priv.showconsole = true;
|
|
||||||
PrintConsole(game, "DEBUG: Gameplay speed: 1.00x");
|
|
||||||
} 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)) {
|
|
||||||
DrawGamestates(game);
|
|
||||||
int flags = al_get_new_bitmap_flags();
|
|
||||||
al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
|
|
||||||
ALLEGRO_BITMAP* bitmap = al_create_bitmap(al_get_display_width(game->display), al_get_display_height(game->display));
|
|
||||||
al_set_new_bitmap_flags(flags);
|
|
||||||
ALLEGRO_BITMAP* target = al_get_target_bitmap();
|
|
||||||
al_set_target_bitmap(bitmap);
|
|
||||||
al_draw_bitmap(al_get_backbuffer(game->display), 0, 0, 0);
|
|
||||||
al_set_target_bitmap(target);
|
|
||||||
PrintConsole(game, "Screenshot made! Storing...");
|
|
||||||
|
|
||||||
struct ScreenshotThreadData* data = malloc(sizeof(struct ScreenshotThreadData));
|
|
||||||
data->game = game;
|
|
||||||
data->bitmap = bitmap;
|
|
||||||
#ifndef LIBSUPERDERPY_SINGLE_THREAD
|
|
||||||
al_run_detached_thread(ScreenshotThread, data);
|
|
||||||
#else
|
|
||||||
ScreenshotThread(data);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
EventGamestates(game, &ev);
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
#ifndef __EMSCRIPTEN__
|
|
||||||
if (game->handlers.destroy) {
|
|
||||||
libsuperderpy_destroy(game);
|
libsuperderpy_destroy(game);
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -715,7 +443,7 @@ SYMBOL_EXPORT void libsuperderpy_destroy(struct Game* game) {
|
||||||
al_destroy_mixer(game->audio.fx);
|
al_destroy_mixer(game->audio.fx);
|
||||||
al_destroy_mixer(game->audio.music);
|
al_destroy_mixer(game->audio.music);
|
||||||
al_destroy_mixer(game->audio.mixer);
|
al_destroy_mixer(game->audio.mixer);
|
||||||
al_destroy_voice(game->audio.v);
|
al_destroy_voice(game->audio.v); // FIXME: doesn't seem to work in Chromium under Emscripten
|
||||||
al_destroy_cond(game->_priv.texture_sync_cond);
|
al_destroy_cond(game->_priv.texture_sync_cond);
|
||||||
al_destroy_mutex(game->_priv.texture_sync_mutex);
|
al_destroy_mutex(game->_priv.texture_sync_mutex);
|
||||||
al_uninstall_audio();
|
al_uninstall_audio();
|
||||||
|
|
|
@ -49,6 +49,7 @@ struct GamestateResources;
|
||||||
#include "character.h"
|
#include "character.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "gamestate.h"
|
#include "gamestate.h"
|
||||||
|
#include "mainloop.h"
|
||||||
#include "maths.h"
|
#include "maths.h"
|
||||||
#include "shader.h"
|
#include "shader.h"
|
||||||
#include "timeline.h"
|
#include "timeline.h"
|
||||||
|
@ -100,7 +101,6 @@ struct Game {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct Gamestate* gamestates; /*!< List of known gamestates. */
|
struct Gamestate* gamestates; /*!< List of known gamestates. */
|
||||||
bool gamestate_scheduled; /*!< Whether there's some gamestate lifecycle management work to do. */
|
|
||||||
ALLEGRO_FONT* font_console; /*!< Font used in game console. */
|
ALLEGRO_FONT* font_console; /*!< Font used in game console. */
|
||||||
ALLEGRO_FONT* font_bsod; /*!< Font used in Blue Screens of Derp. */
|
ALLEGRO_FONT* font_bsod; /*!< Font used in Blue Screens of Derp. */
|
||||||
char console[5][1024];
|
char console[5][1024];
|
||||||
|
@ -135,8 +135,6 @@ struct Game {
|
||||||
|
|
||||||
struct List *garbage, *timelines, *shaders, *bitmaps[LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS];
|
struct List *garbage, *timelines, *shaders, *bitmaps[LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS];
|
||||||
|
|
||||||
bool draw;
|
|
||||||
|
|
||||||
double timestamp;
|
double timestamp;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -188,20 +186,8 @@ struct Game {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Game* libsuperderpy_init(int argc, char** argv, const char* name, struct Viewport viewport);
|
struct Game* libsuperderpy_init(int argc, char** argv, const char* name, struct Viewport viewport);
|
||||||
|
int libsuperderpy_start(struct Game* game);
|
||||||
int libsuperderpy_run(struct Game* game);
|
int libsuperderpy_run(struct Game* game);
|
||||||
void libsuperderpy_destroy(struct Game* game);
|
void libsuperderpy_destroy(struct Game* game);
|
||||||
|
|
||||||
extern int Gamestate_ProgressCount;
|
|
||||||
void Gamestate_ProcessEvent(struct Game* game, struct GamestateResources* data, ALLEGRO_EVENT* ev);
|
|
||||||
void Gamestate_Logic(struct Game* game, struct GamestateResources* data, double delta);
|
|
||||||
void Gamestate_Draw(struct Game* game, struct GamestateResources* data);
|
|
||||||
void* Gamestate_Load(struct Game* game, void (*progress)(struct Game*));
|
|
||||||
void Gamestate_PostLoad(struct Game* game, struct GamestateResources* data);
|
|
||||||
void Gamestate_Unload(struct Game* game, struct GamestateResources* data);
|
|
||||||
void Gamestate_Start(struct Game* game, struct GamestateResources* data);
|
|
||||||
void Gamestate_Stop(struct Game* game, struct GamestateResources* data);
|
|
||||||
void Gamestate_Reload(struct Game* game, struct GamestateResources* data);
|
|
||||||
void Gamestate_Pause(struct Game* game, struct GamestateResources* data);
|
|
||||||
void Gamestate_Resume(struct Game* game, struct GamestateResources* data);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
351
src/mainloop.c
Normal file
351
src/mainloop.c
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
/*! \file mainloop.c
|
||||||
|
* \brief Mainloop handling.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Also, ponies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
#include "libsuperderpy.h"
|
||||||
|
|
||||||
|
static inline void HandleEvent(struct Game* game, ALLEGRO_EVENT* ev) {
|
||||||
|
switch (ev->type) {
|
||||||
|
case ALLEGRO_EVENT_TIMER:
|
||||||
|
if (ev->timer.source == game->_priv.timer) {
|
||||||
|
TickGamestates(game);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ALLEGRO_EVENT_DISPLAY_HALT_DRAWING:
|
||||||
|
PauseExecution(game);
|
||||||
|
al_acknowledge_drawing_halt(game->display);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING:
|
||||||
|
al_acknowledge_drawing_resume(game->display);
|
||||||
|
ReloadGamestates(game);
|
||||||
|
ResumeExecution(game);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ALLEGRO_EVENT_DISPLAY_RESIZE:
|
||||||
|
al_acknowledge_resize(game->display);
|
||||||
|
SetupViewport(game, game->viewport_config);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ALLEGRO_EVENT_KEY_DOWN:
|
||||||
|
#ifdef ALLEGRO_ANDROID
|
||||||
|
if ((ev->keyboard.keycode == ALLEGRO_KEY_MENU) || (ev->keyboard.keycode == ALLEGRO_KEY_TILDE) || (ev->keyboard.keycode == ALLEGRO_KEY_BACKQUOTE)) {
|
||||||
|
#else
|
||||||
|
if ((ev->keyboard.keycode == ALLEGRO_KEY_TILDE) || (ev->keyboard.keycode == ALLEGRO_KEY_BACKQUOTE)) {
|
||||||
|
#endif
|
||||||
|
game->_priv.showconsole = !game->_priv.showconsole;
|
||||||
|
if ((ev->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL) && (game->config.debug)) {
|
||||||
|
game->_priv.showtimeline = game->_priv.showconsole;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev->keyboard.keycode == ALLEGRO_KEY_F12) {
|
||||||
|
DrawGamestates(game);
|
||||||
|
int flags = al_get_new_bitmap_flags();
|
||||||
|
al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
|
||||||
|
ALLEGRO_BITMAP* bitmap = al_create_bitmap(al_get_display_width(game->display), al_get_display_height(game->display));
|
||||||
|
al_set_new_bitmap_flags(flags);
|
||||||
|
ALLEGRO_BITMAP* target = al_get_target_bitmap();
|
||||||
|
al_set_target_bitmap(bitmap);
|
||||||
|
al_draw_bitmap(al_get_backbuffer(game->display), 0, 0, 0);
|
||||||
|
al_set_target_bitmap(target);
|
||||||
|
PrintConsole(game, "Screenshot made! Storing...");
|
||||||
|
|
||||||
|
struct ScreenshotThreadData* data = malloc(sizeof(struct ScreenshotThreadData));
|
||||||
|
data->game = game;
|
||||||
|
data->bitmap = bitmap;
|
||||||
|
#ifndef LIBSUPERDERPY_SINGLE_THREAD
|
||||||
|
al_run_detached_thread(ScreenshotThread, data);
|
||||||
|
#else
|
||||||
|
ScreenshotThread(data);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void HandleDebugEvent(struct Game* game, ALLEGRO_EVENT* ev) {
|
||||||
|
switch (ev->type) {
|
||||||
|
case ALLEGRO_EVENT_DISPLAY_SWITCH_OUT:
|
||||||
|
if (game->_priv.debug.autopause) {
|
||||||
|
PrintConsole(game, "DEBUG: autopause");
|
||||||
|
PauseExecution(game);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ALLEGRO_EVENT_DISPLAY_SWITCH_IN:
|
||||||
|
if (game->_priv.debug.autopause) {
|
||||||
|
if (game->_priv.debug.livereload) {
|
||||||
|
ReloadCode(game);
|
||||||
|
}
|
||||||
|
ResumeExecution(game);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ALLEGRO_EVENT_KEY_DOWN:
|
||||||
|
|
||||||
|
switch (ev->keyboard.keycode) {
|
||||||
|
case ALLEGRO_KEY_F1:
|
||||||
|
if (!game->_priv.paused) {
|
||||||
|
PauseExecution(game);
|
||||||
|
} else {
|
||||||
|
ReloadCode(game);
|
||||||
|
ResumeExecution(game);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ALLEGRO_KEY_F9:
|
||||||
|
al_set_timer_speed(game->_priv.timer, ALLEGRO_BPS_TO_SECS(60.0));
|
||||||
|
game->_priv.showconsole = true;
|
||||||
|
PrintConsole(game, "DEBUG: Gameplay speed: 1.00x");
|
||||||
|
break;
|
||||||
|
case 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);
|
||||||
|
} break;
|
||||||
|
case 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);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool MainloopTick(struct Game* game) {
|
||||||
|
if (game->_priv.paused) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Gamestate* tmp = game->_priv.gamestates;
|
||||||
|
|
||||||
|
game->_priv.loading.toLoad = 0;
|
||||||
|
game->_priv.loading.loaded = 0;
|
||||||
|
game->loading_progress = 0;
|
||||||
|
|
||||||
|
// TODO: support gamestate dependences/ordering
|
||||||
|
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);
|
||||||
|
tmp->started = false;
|
||||||
|
tmp->pending_stop = false;
|
||||||
|
PrintConsole(game, "Gamestate \"%s\" stopped successfully.", tmp->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp->pending_load) { game->_priv.loading.toLoad++; }
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = game->_priv.gamestates;
|
||||||
|
|
||||||
|
while (tmp) {
|
||||||
|
if (tmp->pending_unload) {
|
||||||
|
PrintConsole(game, "Unloading gamestate \"%s\"...", tmp->name);
|
||||||
|
al_stop_timer(game->_priv.timer);
|
||||||
|
tmp->loaded = false;
|
||||||
|
tmp->pending_unload = false;
|
||||||
|
game->_priv.current_gamestate = tmp;
|
||||||
|
(*tmp->api->Gamestate_Unload)(game, tmp->data);
|
||||||
|
al_resume_timer(game->_priv.timer);
|
||||||
|
PrintConsole(game, "Gamestate \"%s\" unloaded successfully.", tmp->name);
|
||||||
|
}
|
||||||
|
if (tmp->pending_load) {
|
||||||
|
al_stop_timer(game->_priv.timer);
|
||||||
|
if (tmp->showLoading) {
|
||||||
|
(*game->_priv.loading.gamestate->api->Gamestate_Start)(game, game->_priv.loading.gamestate->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tmp->api) {
|
||||||
|
if (!OpenGamestate(game, tmp) || !LinkGamestate(game, tmp)) {
|
||||||
|
tmp->pending_load = false;
|
||||||
|
tmp->pending_start = false;
|
||||||
|
tmp->next = tmp;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tmp->api) {
|
||||||
|
PrintConsole(game, "Loading gamestate \"%s\"...", tmp->name);
|
||||||
|
game->_priv.loading.progress = 0;
|
||||||
|
|
||||||
|
game->_priv.loading.current = tmp;
|
||||||
|
game->_priv.current_gamestate = tmp;
|
||||||
|
|
||||||
|
struct GamestateLoadingThreadData data = {.game = game, .gamestate = tmp, .bitmap_flags = al_get_new_bitmap_flags()};
|
||||||
|
game->_priv.loading.inProgress = true;
|
||||||
|
double time = al_get_time();
|
||||||
|
game->_priv.loading.time = time;
|
||||||
|
|
||||||
|
CalculateProgress(game);
|
||||||
|
#ifndef LIBSUPERDERPY_SINGLE_THREAD
|
||||||
|
al_run_detached_thread(GamestateLoadingThread, &data);
|
||||||
|
while (game->_priv.loading.inProgress) {
|
||||||
|
DrawGamestates(game);
|
||||||
|
al_set_target_backbuffer(game->display);
|
||||||
|
double delta = al_get_time() - game->_priv.loading.time;
|
||||||
|
if (tmp->showLoading) {
|
||||||
|
(*game->_priv.loading.gamestate->api->Gamestate_Logic)(game, game->_priv.loading.gamestate->data, delta);
|
||||||
|
(*game->_priv.loading.gamestate->api->Gamestate_Draw)(game, game->_priv.loading.gamestate->data);
|
||||||
|
if (game->handlers.postdraw) {
|
||||||
|
game->handlers.postdraw(game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
game->_priv.loading.time += delta;
|
||||||
|
game->time += delta; // TODO: ability to disable passing time during loading
|
||||||
|
if (game->_priv.texture_sync) {
|
||||||
|
al_convert_memory_bitmaps();
|
||||||
|
game->_priv.texture_sync = false;
|
||||||
|
al_signal_cond(game->_priv.texture_sync_cond);
|
||||||
|
game->_priv.loading.time = al_get_time();
|
||||||
|
}
|
||||||
|
DrawConsole(game);
|
||||||
|
al_flip_display();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
GamestateLoadingThread(&data);
|
||||||
|
al_convert_memory_bitmaps();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
al_set_new_bitmap_flags(data.bitmap_flags);
|
||||||
|
|
||||||
|
if (tmp->api->Gamestate_PostLoad) {
|
||||||
|
PrintConsole(game, "[%s] Post-loading...", tmp->name);
|
||||||
|
tmp->api->Gamestate_PostLoad(game, tmp->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
game->_priv.loading.progress++;
|
||||||
|
CalculateProgress(game);
|
||||||
|
PrintConsole(game, "Gamestate \"%s\" loaded successfully in %f seconds.", tmp->name, al_get_time() - time);
|
||||||
|
game->_priv.loading.loaded++;
|
||||||
|
|
||||||
|
tmp->loaded = true;
|
||||||
|
tmp->pending_load = false;
|
||||||
|
}
|
||||||
|
if (tmp->showLoading) {
|
||||||
|
(*game->_priv.loading.gamestate->api->Gamestate_Stop)(game, game->_priv.loading.gamestate->data);
|
||||||
|
}
|
||||||
|
al_resume_timer(game->_priv.timer);
|
||||||
|
game->_priv.timestamp = al_get_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game->_priv.loading.loaded) {
|
||||||
|
ReloadShaders(game, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gameActive = false;
|
||||||
|
tmp = game->_priv.gamestates;
|
||||||
|
|
||||||
|
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;
|
||||||
|
tmp->started = true;
|
||||||
|
tmp->pending_start = false;
|
||||||
|
(*tmp->api->Gamestate_Start)(game, tmp->data);
|
||||||
|
al_resume_timer(game->_priv.timer);
|
||||||
|
game->_priv.timestamp = al_get_time();
|
||||||
|
PrintConsole(game, "Gamestate \"%s\" started successfully.", tmp->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tmp->started) || (tmp->pending_start) || (tmp->pending_load)) {
|
||||||
|
gameActive = true;
|
||||||
|
}
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gameActive) {
|
||||||
|
PrintConsole(game, "No gamestates left, exiting...");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
al_convert_memory_bitmaps();
|
||||||
|
|
||||||
|
double delta = al_get_time() - game->_priv.timestamp;
|
||||||
|
game->_priv.timestamp += delta;
|
||||||
|
delta *= ALLEGRO_BPS_TO_SECS(al_get_timer_speed(game->_priv.timer) / (1 / 60.f));
|
||||||
|
game->time += delta;
|
||||||
|
|
||||||
|
LogicGamestates(game, delta);
|
||||||
|
DrawGamestates(game);
|
||||||
|
|
||||||
|
DrawConsole(game);
|
||||||
|
al_flip_display();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool MainloopEvents(struct Game* game) {
|
||||||
|
do {
|
||||||
|
ALLEGRO_EVENT ev;
|
||||||
|
|
||||||
|
if (game->_priv.paused) {
|
||||||
|
// there's no frame flipping when paused, so avoid pointless busylooping
|
||||||
|
al_wait_for_event(game->_priv.event_queue, &ev);
|
||||||
|
} else if (!al_get_next_event(game->_priv.event_queue, &ev)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game->handlers.event) {
|
||||||
|
if ((*game->handlers.event)(game, &ev)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
|
||||||
|
EventGamestates(game, &ev);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleEvent(game, &ev);
|
||||||
|
|
||||||
|
if (game->config.debug) {
|
||||||
|
HandleDebugEvent(game, &ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventGamestates(game, &ev);
|
||||||
|
|
||||||
|
} while (!al_is_event_queue_empty(game->_priv.event_queue));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYMBOL_EXPORT bool libsuperderpy_mainloop(struct Game* game) {
|
||||||
|
ClearGarbage(game);
|
||||||
|
return MainloopEvents(game) && MainloopTick(game);
|
||||||
|
}
|
28
src/mainloop.h
Normal file
28
src/mainloop.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*! \file mainloop.h
|
||||||
|
* \brief Mainloop handling.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBSUPERDERPY_MAINLOOP_H
|
||||||
|
#define LIBSUPERDERPY_MAINLOOP_H
|
||||||
|
|
||||||
|
#include "libsuperderpy.h"
|
||||||
|
|
||||||
|
bool libsuperderpy_mainloop(struct Game* game);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue