support of loading in separate thread; thread-safe console; various fixes from clang code model warnings

This commit is contained in:
Sebastian Krzyszkowiak 2017-09-09 00:11:43 +02:00
parent 4a2e2a3d25
commit 450e33b2e3
9 changed files with 285 additions and 212 deletions

View file

@ -71,6 +71,10 @@ if(EMSCRIPTEN)
endif() endif()
if(ANDROID OR EMSCRIPTEN)
add_definitions(-DLIBSUPERDERPY_SINGLE_THREAD=1)
endif()
find_package(Allegro5 REQUIRED) find_package(Allegro5 REQUIRED)
find_package(Allegro5Font REQUIRED) find_package(Allegro5Font REQUIRED)
find_package(Allegro5TTF REQUIRED) find_package(Allegro5TTF REQUIRED)

View file

@ -22,34 +22,22 @@
#include "utils.h" #include "utils.h"
#include "gamestate.h" #include "gamestate.h"
SYMBOL_INTERNAL struct Gamestate* AddNewGamestate(struct Game *game, const char* name) { static struct Gamestate* AddNewGamestate(struct Game *game, const char* name) {
struct Gamestate *tmp = game->_priv.gamestates; struct Gamestate *tmp = game->_priv.gamestates;
if (!tmp) { if (!tmp) {
game->_priv.gamestates = malloc(sizeof(struct Gamestate)); game->_priv.gamestates = AllocateGamestate(game, name);
tmp = game->_priv.gamestates; tmp = game->_priv.gamestates;
} else { } else {
while (tmp->next) { while (tmp->next) {
tmp = tmp->next; tmp = tmp->next;
} }
tmp->next = malloc(sizeof(struct Gamestate)); tmp->next = AllocateGamestate(game, name);
tmp = tmp->next; tmp = tmp->next;
} }
tmp->name = strdup(name);
tmp->handle = NULL;
tmp->loaded = false;
tmp->paused = false;
tmp->frozen = false;
tmp->started = false;
tmp->pending_load = false;
tmp->pending_start = false;
tmp->pending_stop = false;
tmp->pending_unload = false;
tmp->next = NULL;
tmp->api = NULL;
return tmp; return tmp;
} }
SYMBOL_INTERNAL struct Gamestate* FindGamestate(struct Game *game, const char* name) { static struct Gamestate* FindGamestate(struct Game *game, const char* name) {
struct Gamestate *tmp = game->_priv.gamestates; struct Gamestate *tmp = game->_priv.gamestates;
while (tmp) { while (tmp) {
if (!strcmp(name, tmp->name)) { if (!strcmp(name, tmp->name)) {

View file

@ -18,9 +18,11 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <dlfcn.h>
#include <allegro5/allegro_ttf.h> #include <allegro5/allegro_ttf.h>
#include "internal.h" #include "internal.h"
#include "libsuperderpy.h" #include "libsuperderpy.h"
#include "3rdparty/valgrind.h"
SYMBOL_INTERNAL void DrawGamestates(struct Game *game) { SYMBOL_INTERNAL void DrawGamestates(struct Game *game) {
ClearScreen(game); ClearScreen(game);
@ -99,22 +101,40 @@ SYMBOL_INTERNAL void DrawConsole(struct Game *game) {
al_get_clipping_rectangle(&clipX, &clipY, &clipWidth, &clipHeight); al_get_clipping_rectangle(&clipX, &clipY, &clipWidth, &clipHeight);
al_use_transform(&trans); al_use_transform(&trans);
al_draw_bitmap(game->_priv.console, clipX, clipY, 0); int width = (al_get_display_width(game->display) / game->viewport.width) * game->viewport.width;
double game_time = al_get_time(); if (!game->viewport.integer_scaling) {
if(game_time - game->_priv.fps_count.old_time >= 1.0) { width = (al_get_display_width(game->display) / (float)game->viewport.width) * game->viewport.width;
game->_priv.fps_count.fps = game->_priv.fps_count.frames_done / (game_time - game->_priv.fps_count.old_time);
game->_priv.fps_count.frames_done = 0;
game->_priv.fps_count.old_time = game_time;
} }
int size = sizeof(game->_priv.console) / sizeof(game->_priv.console[0]);
for (int i=0; i<size; i++) {
al_draw_filled_rectangle(clipX, clipY, clipX + width, clipY + al_get_font_line_height(game->_priv.font_console)*(size-i), al_map_rgba(0,0,0,80));
}
int cur = game->_priv.console_pos + size;
for (int i=0; i<size; i++) {
if (cur >= size) {
cur -= size;
}
al_draw_text(game->_priv.font_console, al_map_rgb(255,255,255), clipX + (int)(game->viewport.width*0.005), clipY + al_get_font_line_height(game->_priv.font_console)*i, ALLEGRO_ALIGN_LEFT, game->_priv.console[cur]);
cur++;
}
char sfps[6] = { }; char sfps[6] = { };
snprintf(sfps, 6, "%.0f", game->_priv.fps_count.fps); snprintf(sfps, 6, "%.0f", game->_priv.fps_count.fps);
DrawTextWithShadow(game->_priv.font_console, al_map_rgb(255,255,255), clipX + clipWidth, clipY, ALLEGRO_ALIGN_RIGHT, sfps); DrawTextWithShadow(game->_priv.font_console, al_map_rgb(255,255,255), clipX + clipWidth, clipY, ALLEGRO_ALIGN_RIGHT, sfps);
al_use_transform(&game->projection); al_use_transform(&game->projection);
DrawTimelines(game);
}
double game_time = al_get_time();
if (game_time - game->_priv.fps_count.old_time >= 1.0) {
game->_priv.fps_count.fps = game->_priv.fps_count.frames_done / (game_time - game->_priv.fps_count.old_time);
game->_priv.fps_count.frames_done = 0;
game->_priv.fps_count.old_time = game_time;
} }
game->_priv.fps_count.frames_done++; game->_priv.fps_count.frames_done++;
DrawTimelines(game);
} }
SYMBOL_INTERNAL void Console_Load(struct Game *game) { SYMBOL_INTERNAL void Console_Load(struct Game *game) {
@ -124,35 +144,106 @@ SYMBOL_INTERNAL void Console_Load(struct Game *game) {
} else { } else {
game->_priv.font_bsod = al_load_ttf_font(GetDataFilePath(game, "fonts/DejaVuSansMono.ttf"), al_get_display_height(game->display)*0.025,0 ); game->_priv.font_bsod = al_load_ttf_font(GetDataFilePath(game, "fonts/DejaVuSansMono.ttf"), al_get_display_height(game->display)*0.025,0 );
} }
int width = (al_get_display_width(game->display) / game->viewport.width) * game->viewport.width;
if (!game->viewport.integer_scaling) {
width = (al_get_display_width(game->display) / (float)game->viewport.width) * game->viewport.width;
}
game->_priv.console = CreateNotPreservedBitmap(width, al_get_font_line_height(game->_priv.font_console)*5);
game->_priv.console_tmp = CreateNotPreservedBitmap(width, al_get_font_line_height(game->_priv.font_console)*5);
al_set_target_bitmap(game->_priv.console);
al_clear_to_color(al_map_rgba(0,0,0,80));
al_set_target_bitmap(al_get_backbuffer(game->display));
} }
SYMBOL_INTERNAL void Console_Unload(struct Game *game) { SYMBOL_INTERNAL void Console_Unload(struct Game *game) {
al_destroy_font(game->_priv.font_console); if (game->_priv.font_console) {
al_destroy_bitmap(game->_priv.console); al_destroy_font(game->_priv.font_console);
al_destroy_bitmap(game->_priv.console_tmp); }
}
SYMBOL_INTERNAL void* GamestateLoadingThread(void *arg) {
struct GamestateLoadingThreadData *data = arg;
data->game->_priv.loading.inProgress = true;
al_set_new_bitmap_flags(data->bitmap_flags);
GamestateProgress(data->game);
data->gamestate->data = (*data->gamestate->api->Gamestate_Load)(data->game, &GamestateProgress);
data->bitmap_flags = al_get_new_bitmap_flags();
data->game->_priv.loading.inProgress = false;
return NULL;
} }
SYMBOL_INTERNAL void GamestateProgress(struct Game *game) { SYMBOL_INTERNAL void GamestateProgress(struct Game *game) {
struct Gamestate *tmp = game->_priv.tmp_gamestate.tmp; struct Gamestate *tmp = game->_priv.loading.current;
game->_priv.tmp_gamestate.p++; game->_priv.loading.progress++;
DrawGamestates(game);
float progressCount = *(tmp->api->Gamestate_ProgressCount) ? (float)*(tmp->api->Gamestate_ProgressCount) : 1; float progressCount = *(tmp->api->Gamestate_ProgressCount) ? (float)*(tmp->api->Gamestate_ProgressCount) : 1;
float progress = ((game->_priv.tmp_gamestate.p / progressCount) / (float)game->_priv.tmp_gamestate.toLoad) + (game->_priv.tmp_gamestate.loaded/(float)game->_priv.tmp_gamestate.toLoad); float progress = ((game->_priv.loading.progress / progressCount) / (float)game->_priv.loading.toLoad) + (game->_priv.loading.loaded/(float)game->_priv.loading.toLoad);
if (game->config.debug) PrintConsole(game, "[%s] Progress: %d% (%d/%d)", tmp->name, (int)(progress*100), game->_priv.tmp_gamestate.p, *(tmp->api->Gamestate_ProgressCount)); game->loading_progress = progress;
if (tmp->showLoading) (*game->_priv.loading.Draw)(game, game->_priv.loading.data, progress); if (game->config.debug) PrintConsole(game, "[%s] Progress: %d% (%d/%d)", tmp->name, (int)(progress*100), game->_priv.loading.progress, *(tmp->api->Gamestate_ProgressCount));
#ifdef LIBSUPERDERPY_SINGLE_THREAD
DrawGamestates(game);
if (tmp->showLoading) (*game->_priv.loading.gamestate->api->Gamestate_Draw)(game, game->_priv.loading.gamestate->data);
DrawConsole(game); DrawConsole(game);
if (al_get_time() - game->_priv.tmp_gamestate.t >= 1/60.0) { al_flip_display();
al_flip_display(); #endif
game->_priv.tmp_gamestate.t = al_get_time(); }
SYMBOL_INTERNAL bool OpenGamestate(struct Game *game, struct Gamestate *gamestate) {
PrintConsole(game, "Opening gamestate \"%s\"...", gamestate->name);
char libname[1024];
snprintf(libname, 1024, "libsuperderpy-%s-%s" LIBRARY_EXTENSION, game->name, gamestate->name);
gamestate->handle = dlopen(libname, RTLD_NOW);
if (!gamestate->handle) {
FatalError(game, false, "Error while opening gamestate \"%s\": %s", gamestate->name, dlerror()); // TODO: move out
return false;
}
return true;
}
SYMBOL_INTERNAL bool LinkGamestate(struct Game *game, struct Gamestate *gamestate) {
gamestate->api = malloc(sizeof(struct Gamestate_API));
#define GS_ERROR FatalError(game, false, "Error on resolving gamestate's %s symbol: %s", gamestate->name, dlerror()); /* TODO: move out */ \
free(gamestate->api); \
return false;
if (!(gamestate->api->Gamestate_Draw = dlsym(gamestate->handle, "Gamestate_Draw"))) { GS_ERROR; }
if (!(gamestate->api->Gamestate_Logic = dlsym(gamestate->handle, "Gamestate_Logic"))) { GS_ERROR; }
if (!(gamestate->api->Gamestate_Load = dlsym(gamestate->handle, "Gamestate_Load"))) { GS_ERROR; }
if (!(gamestate->api->Gamestate_Start = dlsym(gamestate->handle, "Gamestate_Start"))) { GS_ERROR; }
if (!(gamestate->api->Gamestate_Pause = dlsym(gamestate->handle, "Gamestate_Pause"))) { GS_ERROR; }
if (!(gamestate->api->Gamestate_Resume = dlsym(gamestate->handle, "Gamestate_Resume"))) { GS_ERROR; }
if (!(gamestate->api->Gamestate_Stop = dlsym(gamestate->handle, "Gamestate_Stop"))) { GS_ERROR; }
if (!(gamestate->api->Gamestate_Unload = dlsym(gamestate->handle, "Gamestate_Unload"))) { GS_ERROR; }
if (!(gamestate->api->Gamestate_ProcessEvent = dlsym(gamestate->handle, "Gamestate_ProcessEvent"))) { GS_ERROR; }
if (!(gamestate->api->Gamestate_Reload = dlsym(gamestate->handle, "Gamestate_Reload"))) { GS_ERROR; }
if (!(gamestate->api->Gamestate_ProgressCount = dlsym(gamestate->handle, "Gamestate_ProgressCount"))) { GS_ERROR; }
#undef GS_ERROR
return true;
}
SYMBOL_INTERNAL struct Gamestate* AllocateGamestate(struct Game *game, const char* name) {
struct Gamestate *tmp = malloc(sizeof(struct Gamestate));
tmp->name = strdup(name);
tmp->handle = NULL;
tmp->loaded = false;
tmp->paused = false;
tmp->frozen = false;
tmp->started = false;
tmp->pending_load = false;
tmp->pending_start = false;
tmp->pending_stop = false;
tmp->pending_unload = false;
tmp->next = NULL;
tmp->api = NULL;
return tmp;
}
SYMBOL_INTERNAL void CloseGamestate(struct Game *game, struct Gamestate *gamestate) {
if (gamestate->handle && !RUNNING_ON_VALGRIND) {
#ifndef LEAK_SANITIZER
PrintConsole(game, "Closing gamestate \"%s\"...", gamestate->name);
dlclose(gamestate->handle);
#endif
}
free(gamestate->name);
if (gamestate->api) {
free(gamestate->api);
} }
} }
@ -245,7 +336,7 @@ SYMBOL_INTERNAL void ClearScreen(struct Game *game) {
al_set_clipping_rectangle(clipX, clipY, clipWidth, clipHeight); al_set_clipping_rectangle(clipX, clipY, clipWidth, clipHeight);
} }
SYMBOL_INTERNAL void DrawQueue(struct Game *game, struct TM_Action* queue, int clipX, int clipY) { static void DrawQueue(struct Game *game, struct TM_Action* queue, int clipX, int clipY) {
int pos = clipX; int pos = clipX;
@ -270,7 +361,7 @@ SYMBOL_INTERNAL void DrawQueue(struct Game *game, struct TM_Action* queue, int c
} }
} }
SYMBOL_INTERNAL void DrawTimeline(struct Game *game, struct Timeline* timeline, int pos) { static void DrawTimeline(struct Game *game, struct Timeline* timeline, int pos) {
al_set_target_backbuffer(game->display); al_set_target_backbuffer(game->display);
ALLEGRO_TRANSFORM trans; ALLEGRO_TRANSFORM trans;
al_identity_transform(&trans); al_identity_transform(&trans);
@ -289,7 +380,7 @@ SYMBOL_INTERNAL void DrawTimeline(struct Game *game, struct Timeline* timeline,
} }
SYMBOL_INTERNAL void DrawTimelines(struct Game *game) { SYMBOL_INTERNAL void DrawTimelines(struct Game *game) {
if ((!game->_priv.showconsole) || (!game->_priv.showtimeline)) { if (!game->_priv.showtimeline) {
return; return;
} }
struct libsuperderpy_list *tmp = game->_priv.timelines; struct libsuperderpy_list *tmp = game->_priv.timelines;

View file

@ -42,6 +42,12 @@ struct libsuperderpy_list {
struct libsuperderpy_list *next; struct libsuperderpy_list *next;
}; };
struct GamestateLoadingThreadData {
struct Game *game;
struct Gamestate *gamestate;
int bitmap_flags;
};
void DrawGamestates(struct Game *game); void DrawGamestates(struct Game *game);
void LogicGamestates(struct Game *game); void LogicGamestates(struct Game *game);
void EventGamestates(struct Game *game, ALLEGRO_EVENT *ev); void EventGamestates(struct Game *game, ALLEGRO_EVENT *ev);
@ -51,12 +57,19 @@ void UnfreezeGamestates(struct Game *game);
void DrawConsole(struct Game *game); void DrawConsole(struct Game *game);
void Console_Load(struct Game *game); void Console_Load(struct Game *game);
void Console_Unload(struct Game *game); void Console_Unload(struct Game *game);
void* GamestateLoadingThread(void *arg);
void GamestateProgress(struct Game *game); void GamestateProgress(struct Game *game);
void* AddGarbage(struct Game *game, void* data); void* AddGarbage(struct Game *game, void* data);
void ClearGarbage(struct Game *game); void ClearGarbage(struct Game *game);
void ClearScreen(struct Game *game); void ClearScreen(struct Game *game);
struct libsuperderpy_list* AddToList(struct libsuperderpy_list *list, void* data);
struct libsuperderpy_list* RemoveFromList(struct libsuperderpy_list **list, bool (*identity)(struct libsuperderpy_list* elem, void* data), void* data);
void AddTimeline(struct Game *game, struct Timeline *timeline); void AddTimeline(struct Game *game, struct Timeline *timeline);
void RemoveTimeline(struct Game *game, struct Timeline *timeline); void RemoveTimeline(struct Game *game, struct Timeline *timeline);
void DrawTimelines(struct Game *game); void DrawTimelines(struct Game *game);
bool OpenGamestate(struct Game *game, struct Gamestate *gamestate);
bool LinkGamestate(struct Game *game, struct Gamestate *gamestate);
void CloseGamestate(struct Game *game, struct Gamestate *gamestate);
struct Gamestate* AllocateGamestate(struct Game *game, const char* name);
#endif #endif

View file

@ -34,7 +34,6 @@
#include <sys/param.h> #include <sys/param.h>
#include "internal.h" #include "internal.h"
#include "libsuperderpy.h" #include "libsuperderpy.h"
#include "3rdparty/valgrind.h"
#ifdef ALLEGRO_MACOSX #ifdef ALLEGRO_MACOSX
#include <mach-o/dyld.h> #include <mach-o/dyld.h>
#endif #endif
@ -67,15 +66,18 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char*
game->_priv.fps_count.fps = 0; game->_priv.fps_count.fps = 0;
game->_priv.fps_count.old_time = 0; game->_priv.fps_count.old_time = 0;
game->_priv.font_console = NULL;
game->_priv.font_bsod = NULL; game->_priv.font_bsod = NULL;
game->_priv.console = NULL; game->_priv.console_pos = 0;
game->_priv.console_tmp = NULL; for (unsigned int i=0; i<(sizeof(game->_priv.console)/sizeof(game->_priv.console[0])); i++) {
game->_priv.console[i][0] = '\0';
}
game->_priv.garbage = NULL; game->_priv.garbage = NULL;
game->_priv.timelines = NULL; game->_priv.timelines = NULL;
game->eventHandler = NULL; game->handlers.event = NULL;
game->destroyHandler = NULL; game->handlers.destroy = NULL;
game->config.fullscreen = atoi(GetConfigOptionDefault(game, "SuperDerpy", "fullscreen", "1")); game->config.fullscreen = atoi(GetConfigOptionDefault(game, "SuperDerpy", "fullscreen", "1"));
game->config.music = atoi(GetConfigOptionDefault(game, "SuperDerpy", "music", "10")); game->config.music = atoi(GetConfigOptionDefault(game, "SuperDerpy", "music", "10"));
@ -143,7 +145,7 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char*
al_install_joystick(); 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_flags(ALLEGRO_OPENGL_ES_PROFILE | ALLEGRO_PROGRAMMABLE_PIPELINE | (game->config.fullscreen ? ALLEGRO_FULLSCREEN_WINDOW : ALLEGRO_WINDOWED) | ALLEGRO_RESIZABLE | ALLEGRO_OPENGL);
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
al_set_new_display_flags((al_get_new_display_flags() | ALLEGRO_WINDOWED) ^ ALLEGRO_FULLSCREEN_WINDOW); al_set_new_display_flags((al_get_new_display_flags() | ALLEGRO_WINDOWED) ^ ALLEGRO_FULLSCREEN_WINDOW);
#endif #endif
@ -183,7 +185,7 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char*
if (game->config.fullscreen) al_hide_mouse_cursor(game->display); if (game->config.fullscreen) al_hide_mouse_cursor(game->display);
al_inhibit_screensaver(true); al_inhibit_screensaver(true);
al_set_new_bitmap_flags(ALLEGRO_MIN_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; game->_priv.gamestate_scheduled = false;
@ -197,9 +199,15 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char*
return NULL; return NULL;
} }
game->audio.v = al_create_voice(44100, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2); ALLEGRO_AUDIO_DEPTH depth = ALLEGRO_AUDIO_DEPTH_FLOAT32;
#ifdef ALLEGRO_ANDROID
depth = ALLEGRO_AUDIO_DEPTH_INT16;
#endif
game->audio.v = al_create_voice(44100, depth, ALLEGRO_CHANNEL_CONF_2);
if (!game->audio.v) { if (!game->audio.v) {
game->audio.v = al_create_voice(44100, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2); // fallback
depth = (depth == ALLEGRO_AUDIO_DEPTH_FLOAT32) ? ALLEGRO_AUDIO_DEPTH_INT16 : ALLEGRO_AUDIO_DEPTH_FLOAT32;
game->audio.v = al_create_voice(44100, depth, ALLEGRO_CHANNEL_CONF_2);
} }
game->audio.mixer = al_create_mixer(44100, ALLEGRO_AUDIO_DEPTH_FLOAT32, 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.fx = al_create_mixer(44100, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);
@ -220,11 +228,13 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char*
game->data = NULL; game->data = NULL;
game->shuttingdown = false; game->shutting_down = false;
game->restart = false; game->restart = false;
game->show_loading_on_launch = false; game->show_loading_on_launch = false;
game->loading_progress = 0;
return game; return game;
} }
@ -259,24 +269,13 @@ SYMBOL_EXPORT int libsuperderpy_run(struct Game *game) {
tmp = tmp->next; tmp = tmp->next;
} }
char libname[1024] = {}; game->_priv.loading.gamestate = AllocateGamestate(game, "loading");
snprintf(libname, 1024, "libsuperderpy-%s-loading" LIBRARY_EXTENSION, game->name); if (!OpenGamestate(game, game->_priv.loading.gamestate) || !LinkGamestate(game, game->_priv.loading.gamestate)) {
void *handle = dlopen(libname, RTLD_NOW); // TODO: support loading-less scenario
if (!handle) {
FatalError(game, true, "Error while initializing loading screen: %s", dlerror());
return 2; return 2;
} else {
#define GS_LOADINGERROR FatalError(game, true, "Error on resolving loading symbol: %s", dlerror()); return 3;
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; }
} }
game->_priv.loading.gamestate->data = (*game->_priv.loading.gamestate->api->Gamestate_Load)(game, NULL);
game->_priv.loading.data = (*game->_priv.loading.Load)(game); PrintConsole(game, "Loading screen registered.");
game->_priv.draw = true; game->_priv.draw = true;
@ -311,10 +310,11 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void *g) {
game->_priv.gamestate_scheduled = false; game->_priv.gamestate_scheduled = false;
struct Gamestate *tmp = game->_priv.gamestates; struct Gamestate *tmp = game->_priv.gamestates;
game->_priv.tmp_gamestate.toLoad = 0; game->_priv.loading.toLoad = 0;
game->_priv.tmp_gamestate.loaded = 0; game->_priv.loading.loaded = 0;
game->loading_progress = 0;
// TODO: support gamestate dependences // TODO: support gamestate dependences/ordering
while (tmp) { while (tmp) {
if (tmp->pending_stop) { if (tmp->pending_stop) {
PrintConsole(game, "Stopping gamestate \"%s\"...", tmp->name); PrintConsole(game, "Stopping gamestate \"%s\"...", tmp->name);
@ -324,14 +324,12 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void *g) {
tmp->pending_stop = false; tmp->pending_stop = false;
} }
if (tmp->pending_load) game->_priv.tmp_gamestate.toLoad++; if (tmp->pending_load) game->_priv.loading.toLoad++;
tmp=tmp->next; tmp=tmp->next;
} }
tmp = game->_priv.gamestates; tmp = game->_priv.gamestates;
game->_priv.tmp_gamestate.t = -1;
while (tmp) { while (tmp) {
if (tmp->pending_unload) { if (tmp->pending_unload) {
PrintConsole(game, "Unloading gamestate \"%s\"...", tmp->name); PrintConsole(game, "Unloading gamestate \"%s\"...", tmp->name);
@ -344,60 +342,48 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void *g) {
} }
if (tmp->pending_load) { if (tmp->pending_load) {
al_stop_timer(game->_priv.timer); 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 (!tmp->api) {
PrintConsole(game, "Opening gamestate \"%s\"...", tmp->name); if (!OpenGamestate(game, tmp) || !LinkGamestate(game, tmp)) {
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_load = false;
tmp->pending_start = false; tmp->pending_start = false;
} else { tmp->next = tmp;
continue;
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) { if (tmp->api) {
PrintConsole(game, "Loading gamestate \"%s\"...", tmp->name); PrintConsole(game, "Loading gamestate \"%s\"...", tmp->name);
game->_priv.tmp_gamestate.p = 0; game->_priv.loading.progress = -1;
DrawGamestates(game); game->_priv.loading.current = tmp;
if (tmp->showLoading) {
(*game->_priv.loading.Draw)(game, game->_priv.loading.data, game->_priv.tmp_gamestate.loaded/(float)game->_priv.tmp_gamestate.toLoad);
}
DrawConsole(game);
if (al_get_time() - game->_priv.tmp_gamestate.t >= 1/60.0) {
al_flip_display();
game->_priv.tmp_gamestate.t = al_get_time();
}
game->_priv.tmp_gamestate.tmp = tmp;
game->_priv.current_gamestate = tmp; game->_priv.current_gamestate = tmp;
tmp->data = (*tmp->api->Gamestate_Load)(game, &GamestateProgress);
game->_priv.tmp_gamestate.loaded++; struct GamestateLoadingThreadData data = { .game = game, .gamestate = tmp, .bitmap_flags = al_get_new_bitmap_flags() };
game->_priv.loading.inProgress = true;
#ifndef LIBSUPERDERPY_SINGLE_THREAD
al_run_detached_thread(GamestateLoadingThread, &data);
while (game->_priv.loading.inProgress) {
DrawGamestates(game);
if (tmp->showLoading) (*game->_priv.loading.gamestate->api->Gamestate_Draw)(game, game->_priv.loading.gamestate->data);
DrawConsole(game);
al_flip_display();
}
#else
GamestateLoadingThread(&data);
#endif
al_set_new_bitmap_flags(data.bitmap_flags);
// TODO: compile shaders
al_convert_memory_bitmaps();
game->_priv.loading.loaded++;
tmp->loaded = true; tmp->loaded = true;
tmp->pending_load = false; 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); al_resume_timer(game->_priv.timer);
} }
@ -430,6 +416,8 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void *g) {
break; break;
} }
al_convert_memory_bitmaps();
DrawGamestates(game); DrawGamestates(game);
DrawConsole(game); DrawConsole(game);
al_flip_display(); al_flip_display();
@ -445,8 +433,8 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void *g) {
al_wait_for_event(game->_priv.event_queue, &ev); al_wait_for_event(game->_priv.event_queue, &ev);
if (game->eventHandler) { if (game->handlers.event) {
if ((*game->eventHandler)(game, &ev)) { if ((*game->handlers.event)(game, &ev)) {
continue; continue;
} }
} }
@ -467,13 +455,11 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void *g) {
al_stop_timer(game->_priv.timer); al_stop_timer(game->_priv.timer);
al_detach_voice(game->audio.v); al_detach_voice(game->audio.v);
FreezeGamestates(game); FreezeGamestates(game);
if (game->_priv.console) Console_Unload(game);
al_acknowledge_drawing_halt(game->display); al_acknowledge_drawing_halt(game->display);
} }
else if(ev.type == ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING) { else if(ev.type == ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING) {
game->_priv.draw = true; game->_priv.draw = true;
al_acknowledge_drawing_resume(game->display); al_acknowledge_drawing_resume(game->display);
Console_Load(game);
PrintConsole(game, "Engine resumed."); PrintConsole(game, "Engine resumed.");
ReloadGamestates(game); ReloadGamestates(game);
UnfreezeGamestates(game); UnfreezeGamestates(game);
@ -529,7 +515,7 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void *g) {
} }
#ifndef __EMSCRIPTEN__ #ifndef __EMSCRIPTEN__
if (game->destroyHandler) { if (game->handlers.destroy) {
libsuperderpy_destroy(game); libsuperderpy_destroy(game);
} }
return 0; return 0;
@ -537,7 +523,7 @@ SYMBOL_INTERNAL void libsuperderpy_mainloop(void *g) {
} }
SYMBOL_EXPORT void libsuperderpy_destroy(struct Game *game) { SYMBOL_EXPORT void libsuperderpy_destroy(struct Game *game) {
game->shuttingdown = true; game->shutting_down = true;
ClearGarbage(game); ClearGarbage(game);
@ -555,41 +541,37 @@ SYMBOL_EXPORT void libsuperderpy_destroy(struct Game *game) {
(*tmp->api->Gamestate_Unload)(game, tmp->data); (*tmp->api->Gamestate_Unload)(game, tmp->data);
tmp->loaded = false; tmp->loaded = false;
} }
if (tmp->handle && !RUNNING_ON_VALGRIND) { CloseGamestate(game, tmp);
#ifndef LEAK_SANITIZER
PrintConsole(game, "Closing gamestate \"%s\"...", tmp->name);
dlclose(tmp->handle);
#endif
}
free(tmp->name);
if (tmp->api) {
free(tmp->api);
}
pom = tmp->next; pom = tmp->next;
free(tmp); free(tmp);
tmp=pom; tmp=pom;
} }
if (game->destroyHandler) { (*game->_priv.loading.gamestate->api->Gamestate_Unload)(game, game->_priv.loading.gamestate->data);
(*game->destroyHandler)(game); CloseGamestate(game, game->_priv.loading.gamestate);
free(game->_priv.loading.gamestate);
if (game->handlers.destroy) {
(*game->handlers.destroy)(game);
} }
ClearScreen(game); ClearScreen(game);
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
{ {
ALLEGRO_BITMAP *bmp = al_create_bitmap(320, 180); ALLEGRO_BITMAP *bmp = al_create_bitmap(320, 180);
al_set_target_bitmap(bmp); al_set_target_bitmap(bmp);
al_clear_to_color(al_map_rgb(0,0,0)); al_clear_to_color(al_map_rgb(0,0,0));
ALLEGRO_FONT *font = al_create_builtin_font(); ALLEGRO_FONT *font = al_create_builtin_font();
al_draw_text(font, al_map_rgb(228,127,59), 320/2, 180/2 - 8 - 6, ALLEGRO_ALIGN_CENTER, "It's now safe to turn off"); al_draw_text(font, al_map_rgb(228,127,59), 320/2, 180/2 - 8 - 6, ALLEGRO_ALIGN_CENTER, "It's now safe to turn off");
al_draw_text(font, al_map_rgb(228,127,59), 320/2, 180/2 - 8 + 6, ALLEGRO_ALIGN_CENTER, "your browser."); al_draw_text(font, al_map_rgb(228,127,59), 320/2, 180/2 - 8 + 6, ALLEGRO_ALIGN_CENTER, "your browser.");
al_set_target_backbuffer(game->display); al_set_target_backbuffer(game->display);
al_draw_scaled_bitmap(bmp, 0, 0, 320, 180, 0, -game->viewport.height*0.2, game->viewport.width, game->viewport.height*1.4, 0); al_draw_scaled_bitmap(bmp, 0, 0, 320, 180, 0, -game->viewport.height*0.2, game->viewport.width, game->viewport.height*1.4, 0);
al_flip_display(); al_flip_display();
al_destroy_bitmap(bmp); al_destroy_bitmap(bmp);
al_destroy_font(font); al_destroy_font(font);
} }
#endif #endif
PrintConsole(game, "Shutting down..."); PrintConsole(game, "Shutting down...");
DrawConsole(game); DrawConsole(game);
al_flip_display(); al_flip_display();
@ -597,7 +579,6 @@ SYMBOL_EXPORT void libsuperderpy_destroy(struct Game *game) {
free(game->_priv.garbage->data); free(game->_priv.garbage->data);
game->_priv.garbage = game->_priv.garbage->next; game->_priv.garbage = game->_priv.garbage->next;
} }
(*game->_priv.loading.Unload)(game, game->_priv.loading.data);
al_destroy_timer(game->_priv.timer); al_destroy_timer(game->_priv.timer);
Console_Unload(game); Console_Unload(game);
al_destroy_display(game->display); al_destroy_display(game->display);

View file

@ -87,8 +87,8 @@ struct Game {
bool gamestate_scheduled; /*!< Whether there's some gamestate lifecycle management work to do. */ 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. */
ALLEGRO_BITMAP *console; /*!< Bitmap with game console. */ char console[5][1024];
ALLEGRO_BITMAP *console_tmp; /*!< Bitmap used for drawing game console. */ unsigned int console_pos;
ALLEGRO_EVENT_QUEUE *event_queue; /*!< Main event queue. */ ALLEGRO_EVENT_QUEUE *event_queue; /*!< Main event queue. */
ALLEGRO_TIMER *timer; /*!< Main LPS (logic) timer. */ ALLEGRO_TIMER *timer; /*!< Main LPS (logic) timer. */
bool showconsole; /*!< If true, game console is rendered on screen. */ bool showconsole; /*!< If true, game console is rendered on screen. */
@ -101,25 +101,16 @@ struct Game {
ALLEGRO_CONFIG *config; /*!< Configuration file interface. */ ALLEGRO_CONFIG *config; /*!< Configuration file interface. */
struct {
void (*Draw)(struct Game *game, void* data, float p);
void* (*Load)(struct Game *game);
void (*Start)(struct Game *game, void* data);
void (*Stop)(struct Game *game, void* data);
void (*Unload)(struct Game *game, void* data);
void* data;
} loading; /*!< Interface for accessing loading screen functions. */
int argc; int argc;
char** argv; char** argv;
struct { struct {
int p; struct Gamestate *gamestate;
struct Gamestate *tmp; struct Gamestate *current;
double t; int progress;
int loaded, toLoad; int loaded, toLoad;
} tmp_gamestate; bool inProgress;
} loading;
struct Gamestate *current_gamestate; struct Gamestate *current_gamestate;
@ -133,7 +124,7 @@ struct Game {
} _priv; /*!< Private resources. Do not use in gamestates! */ } _priv; /*!< Private resources. Do not use in gamestates! */
bool shuttingdown; /*!< If true then shut down of the game is pending. */ bool shutting_down; /*!< If true then shut down of the game is pending. */
bool restart; /*!< If true then restart of the game is pending. */ bool restart; /*!< If true then restart of the game is pending. */
bool touch; bool touch;
@ -143,8 +134,12 @@ struct Game {
ALLEGRO_EVENT_SOURCE event_source; ALLEGRO_EVENT_SOURCE event_source;
bool (*eventHandler)(struct Game *game, ALLEGRO_EVENT *ev); float loading_progress;
void (*destroyHandler)(struct Game *game);
struct {
bool (*event)(struct Game *game, ALLEGRO_EVENT *ev);
void (*destroy)(struct Game *game);
} handlers;
LIBSUPERDERPY_DATA_TYPE *data; LIBSUPERDERPY_DATA_TYPE *data;
@ -154,4 +149,17 @@ struct Game* libsuperderpy_init(int argc, char **argv, const char* name, struct
int libsuperderpy_run(struct Game* game); int libsuperderpy_run(struct Game* game);
void libsuperderpy_destroy(struct Game* game); void libsuperderpy_destroy(struct Game* game);
struct GamestateResources;
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);
void Gamestate_Draw(struct Game *game, struct GamestateResources *data);
void* Gamestate_Load(struct Game *game, void (*progress)(struct Game*));
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

View file

@ -128,7 +128,7 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline) {
} }
} }
SYMBOL_INTERNAL void PauseTimers(struct Timeline* timeline, bool pause) { static void PauseTimers(struct Timeline* timeline, bool pause) {
if (timeline->queue) { if (timeline->queue) {
if (timeline->queue->timer) { if (timeline->queue->timer) {
if (pause) { if (pause) {
@ -147,7 +147,7 @@ SYMBOL_INTERNAL void PauseTimers(struct Timeline* timeline, bool pause) {
} }
} }
SYMBOL_EXPORT void TM_Propagate(struct Timeline* timeline, enum TM_ActionState action) { static void TM_Propagate(struct Timeline* timeline, enum TM_ActionState action) {
if (timeline->queue) { if (timeline->queue) {
if ((*timeline->queue->function) && (timeline->queue->active)) { if ((*timeline->queue->function) && (timeline->queue->active)) {
(*timeline->queue->function)(timeline->game, timeline->queue, action); (*timeline->queue->function)(timeline->game, timeline->queue, action);
@ -273,7 +273,7 @@ SYMBOL_EXPORT struct TM_Action* TM_AddBackgroundAction(struct Timeline* timeline
} }
/*! \brief Predefined action used by TM_AddQueuedBackgroundAction */ /*! \brief Predefined action used by TM_AddQueuedBackgroundAction */
SYMBOL_INTERNAL bool runinbackground(struct Game* game, struct TM_Action* action, enum TM_ActionState state) { static bool runinbackground(struct Game* game, struct TM_Action* action, enum TM_ActionState state) {
int* delay = (int*) TM_GetArg(action->arguments, 1); int* delay = (int*) TM_GetArg(action->arguments, 1);
char* name = (char*) TM_GetArg(action->arguments, 2); char* name = (char*) TM_GetArg(action->arguments, 2);
struct Timeline *timeline = (struct Timeline*) TM_GetArg(action->arguments, 3); struct Timeline *timeline = (struct Timeline*) TM_GetArg(action->arguments, 3);

View file

@ -187,20 +187,12 @@ SYMBOL_EXPORT ALLEGRO_BITMAP* LoadScaledBitmap(struct Game *game, char* filename
SYMBOL_EXPORT void FatalError(struct Game *game, bool fatal, char* format, ...) { SYMBOL_EXPORT void FatalError(struct Game *game, bool fatal, char* format, ...) {
char text[1024] = {}; char text[1024] = {};
if (!game->_priv.console) { PrintConsole(game, "Fatal Error, displaying Blue Screen of Derp...");
va_list vl; va_list vl;
va_start(vl, format); va_start(vl, format);
vsnprintf(text, 1024, format, vl); vsnprintf(text, 1024, format, vl);
va_end(vl); va_end(vl);
printf("%s\n", text); PrintConsole(game, text);
} else {
PrintConsole(game, "Fatal Error, displaying Blue Screen of Derp...");
va_list vl;
va_start(vl, format);
vsnprintf(text, 1024, format, vl);
va_end(vl);
PrintConsole(game, text);
}
ALLEGRO_TRANSFORM trans; ALLEGRO_TRANSFORM trans;
al_identity_transform(&trans); al_identity_transform(&trans);
@ -270,7 +262,7 @@ SYMBOL_EXPORT void FatalError(struct Game *game, bool fatal, char* format, ...)
al_use_transform(&game->projection); al_use_transform(&game->projection);
} }
SYMBOL_INTERNAL void TestPath(char* filename, char* subpath, char** result) { static void TestPath(char* filename, char* subpath, char** result) {
if (*result) return; //already found if (*result) return; //already found
ALLEGRO_PATH *tail = al_create_path(filename); ALLEGRO_PATH *tail = al_create_path(filename);
ALLEGRO_PATH *path = al_get_standard_path(ALLEGRO_RESOURCES_PATH); ALLEGRO_PATH *path = al_get_standard_path(ALLEGRO_RESOURCES_PATH);
@ -293,7 +285,7 @@ SYMBOL_EXPORT char* GetGameName(struct Game *game, char* format) {
} }
SYMBOL_INTERNAL char* TestDataFilePath(struct Game *game, char* filename) { static char* TestDataFilePath(struct Game *game, char* filename) {
char *result = NULL; char *result = NULL;
if (al_filename_exists(filename)) { if (al_filename_exists(filename)) {
@ -339,11 +331,11 @@ SYMBOL_EXPORT char* GetDataFilePath(struct Game *game, char* filename) {
char* file = AddGarbage(game, strdup(filename)); char* file = AddGarbage(game, strdup(filename));
char* sub = strstr(file, ".flac"); char* sub = strstr(file, ".flac");
if (sub) { if (sub) {
sub[0] = '.'; sub[0] = '.';
sub[1] = 'o'; sub[1] = 'o';
sub[2] = 'g'; sub[2] = 'g';
sub[3] = 'g'; sub[3] = 'g';
sub[4] = 0; sub[4] = 0;
} }
result = TestDataFilePath(game, file); result = TestDataFilePath(game, file);
if (result) { if (result) {
@ -368,8 +360,8 @@ ALLEGRO_DEBUG_CHANNEL("libsuperderpy")
SYMBOL_EXPORT void PrintConsole(struct Game *game, char* format, ...) { SYMBOL_EXPORT void PrintConsole(struct Game *game, char* format, ...) {
va_list vl; va_list vl;
va_start(vl, format); va_start(vl, format);
char text[1024] = {}; char* text = game->_priv.console[game->_priv.console_pos];
vsnprintf(text, 1024, format, vl); vsnprintf(text, (sizeof(game->_priv.console[0])/sizeof(game->_priv.console[0][0])), format, vl);
va_end(vl); va_end(vl);
ALLEGRO_DEBUG("%s", text); ALLEGRO_DEBUG("%s", text);
#ifndef __EMSCRIPTEN__ #ifndef __EMSCRIPTEN__
@ -379,18 +371,11 @@ SYMBOL_EXPORT void PrintConsole(struct Game *game, char* format, ...) {
printf("%s\n", text); printf("%s\n", text);
fflush(stdout); fflush(stdout);
} }
if (!game->_priv.draw) return; game->_priv.console_pos++;
if (!game->_priv.console) return; if (game->_priv.console_pos >= (sizeof(game->_priv.console)/sizeof(game->_priv.console[0]))) {
if ((!game->config.debug) && (!game->_priv.showconsole)) return; game->_priv.console_pos = 0;
ALLEGRO_BITMAP *target = al_get_target_bitmap(); }
al_set_target_bitmap(game->_priv.console_tmp); return;
al_clear_to_color(al_map_rgba(0,0,0,80));
al_draw_bitmap_region(game->_priv.console, 0, (int)(al_get_bitmap_height(game->_priv.console)*0.2), al_get_bitmap_width(game->_priv.console), (int)(al_get_bitmap_height(game->_priv.console)*0.8), 0, 0, 0);
al_draw_text(game->_priv.font_console, al_map_rgb(255,255,255), (int)(game->viewport.width*0.005), (int)(al_get_bitmap_height(game->_priv.console)*0.81), ALLEGRO_ALIGN_LEFT, text);
al_set_target_bitmap(game->_priv.console);
al_clear_to_color(al_map_rgba(0,0,0,0));
al_draw_bitmap(game->_priv.console_tmp, 0, 0, 0);
al_set_target_bitmap(target);
} }
SYMBOL_EXPORT void SetupViewport(struct Game *game, struct Viewport config) { SYMBOL_EXPORT void SetupViewport(struct Game *game, struct Viewport config) {
@ -418,6 +403,9 @@ SYMBOL_EXPORT void SetupViewport(struct Game *game, struct Viewport config) {
} }
if (game->viewport.integer_scaling) { if (game->viewport.integer_scaling) {
resolution = floor(resolution); resolution = floor(resolution);
if (floor(resolution) == 0) {
resolution = 1;
}
} }
if ((!atoi(GetConfigOptionDefault(game, "SuperDerpy", "downscale", "1"))) && (resolution < 1)) { if ((!atoi(GetConfigOptionDefault(game, "SuperDerpy", "downscale", "1"))) && (resolution < 1)) {
resolution = 1; resolution = 1;
@ -425,9 +413,6 @@ SYMBOL_EXPORT void SetupViewport(struct Game *game, struct Viewport config) {
if (!atoi(GetConfigOptionDefault(game, "SuperDerpy", "scaling", "1"))) { if (!atoi(GetConfigOptionDefault(game, "SuperDerpy", "scaling", "1"))) {
resolution = 1; resolution = 1;
} }
if (resolution == 0) {
resolution = 1;
}
int clipWidth = game->viewport.width * resolution; int clipWidth = game->viewport.width * resolution;
int clipHeight = game->viewport.height * resolution; int clipHeight = game->viewport.height * resolution;
@ -440,7 +425,7 @@ SYMBOL_EXPORT void SetupViewport(struct Game *game, struct Viewport config) {
al_build_transform(&game->projection, 0, 0, al_get_display_width(game->display) / (float)game->viewport.width, al_get_display_height(game->display) / (float)game->viewport.height, 0.0f); al_build_transform(&game->projection, 0, 0, al_get_display_width(game->display) / (float)game->viewport.width, al_get_display_height(game->display) / (float)game->viewport.height, 0.0f);
} }
al_use_transform(&game->projection); al_use_transform(&game->projection);
if (game->_priv.console) Console_Unload(game); Console_Unload(game);
Console_Load(game); Console_Load(game);
} }

View file

@ -51,6 +51,9 @@ void DrawTextWithShadow(ALLEGRO_FONT *font, ALLEGRO_COLOR color, float x, float
int DrawWrappedText(ALLEGRO_FONT *font, ALLEGRO_COLOR color, float x, float y, int width, int flags, char const *text); int DrawWrappedText(ALLEGRO_FONT *font, ALLEGRO_COLOR color, float x, float y, int width, int flags, char const *text);
int DrawWrappedTextWithShadow(ALLEGRO_FONT *font, ALLEGRO_COLOR color, float x, float y, int width, int flags, char const *text); int DrawWrappedTextWithShadow(ALLEGRO_FONT *font, ALLEGRO_COLOR color, float x, float y, int width, int flags, char const *text);
ALLEGRO_COLOR InterpolateColor(ALLEGRO_COLOR c1, ALLEGRO_COLOR c2, float frac);
void ScaleBitmap(ALLEGRO_BITMAP* source, int width, int height);
/*! \brief Loads bitmap into memory and scales it with software linear filtering. */ /*! \brief Loads bitmap into memory and scales it with software linear filtering. */
ALLEGRO_BITMAP* LoadScaledBitmap(struct Game *game, char* filename, int width, int height); ALLEGRO_BITMAP* LoadScaledBitmap(struct Game *game, char* filename, int width, int height);