2016-07-04 00:56:45 +02: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
2017-07-22 18:22:12 +02:00
* the Free Software Foundation ; either version 3 of the License , or
2016-07-04 00:56:45 +02:00
* ( 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
2017-07-22 18:22:12 +02:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2016-07-04 00:56:45 +02:00
*
* Also , ponies .
*/
2016-07-04 01:12:55 +02:00
# include "internal.h"
2017-09-09 00:11:43 +02:00
# include "3rdparty/valgrind.h"
2017-09-10 21:35:14 +02:00
# include <dlfcn.h>
2016-07-04 00:56:45 +02:00
2018-02-03 03:46:33 +01:00
SYMBOL_INTERNAL void SimpleCompositor ( struct Game * game , struct Gamestate * gamestates ) {
struct Gamestate * tmp = gamestates ;
2018-11-27 03:08:21 +01:00
ClearToColor ( game , al_map_rgb ( 0 , 0 , 0 ) ) ;
2018-02-03 03:46:33 +01:00
while ( tmp ) {
if ( ( tmp - > loaded ) & & ( tmp - > started ) ) {
2018-11-22 21:01:35 +01:00
al_draw_bitmap ( tmp - > fb , game - > _priv . clip_rect . x , game - > _priv . clip_rect . y , 0 ) ;
2018-02-03 03:46:33 +01:00
}
tmp = tmp - > next ;
}
2018-12-07 06:14:52 +01:00
if ( game - > _priv . loading . shown ) {
2018-11-22 21:01:35 +01:00
al_draw_bitmap ( game - > loading_fb , game - > _priv . clip_rect . x , game - > _priv . clip_rect . y , 0 ) ;
2018-11-22 04:53:51 +01:00
}
2018-02-03 03:46:33 +01:00
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void DrawGamestates ( struct Game * game ) {
2018-02-03 03:46:33 +01:00
if ( ! game - > handlers . compositor ) {
ClearScreen ( game ) ;
}
2017-09-10 21:35:14 +02:00
struct Gamestate * tmp = game - > _priv . gamestates ;
2018-02-03 03:39:30 +01:00
if ( game - > handlers . predraw ) {
2018-08-05 02:01:21 +02:00
game - > handlers . predraw ( game ) ;
2018-02-03 03:39:30 +01:00
}
2016-07-04 00:56:45 +02:00
while ( tmp ) {
if ( ( tmp - > loaded ) & & ( tmp - > started ) ) {
2016-08-16 18:41:50 +02:00
game - > _priv . current_gamestate = tmp ;
2018-02-03 03:46:33 +01:00
SetFramebufferAsTarget ( game ) ;
if ( game - > handlers . compositor ) { // don't clear when uncomposited
2018-11-22 21:01:35 +01:00
al_reset_clipping_rectangle ( ) ;
2018-02-03 03:46:33 +01:00
al_clear_to_color ( al_map_rgb ( 0 , 0 , 0 ) ) ; // even if everything is going to be redrawn, it optimizes tiled rendering
}
2018-08-05 02:01:21 +02:00
tmp - > api - > Gamestate_Draw ( game , tmp - > data ) ;
2018-02-03 03:46:33 +01:00
// TODO: save and restore more state for careless gamestating
2016-07-04 00:56:45 +02:00
}
tmp = tmp - > next ;
}
2018-02-03 03:46:33 +01:00
2018-11-22 04:53:51 +01:00
if ( game - > _priv . loading . inProgress ) {
// same as above, but for the loading gamestate
game - > _priv . current_gamestate = NULL ;
SetFramebufferAsTarget ( game ) ;
if ( game - > handlers . compositor ) {
2018-11-22 21:01:35 +01:00
al_reset_clipping_rectangle ( ) ;
2018-11-22 04:53:51 +01:00
al_clear_to_color ( al_map_rgb ( 0 , 0 , 0 ) ) ;
}
game - > _priv . loading . gamestate - > api - > Gamestate_Draw ( game , game - > _priv . loading . gamestate - > data ) ;
}
2018-11-22 21:01:35 +01:00
al_set_target_backbuffer ( game - > display ) ;
ALLEGRO_TRANSFORM t ;
// restore full resolution access to the whole screen
al_identity_transform ( & t ) ;
al_use_transform ( & t ) ;
al_reset_clipping_rectangle ( ) ;
if ( game - > handlers . compositor ) {
2018-02-03 03:46:33 +01:00
game - > handlers . compositor ( game , game - > _priv . gamestates ) ;
}
2018-11-22 21:01:35 +01:00
2018-02-03 03:39:30 +01:00
if ( game - > handlers . postdraw ) {
2018-08-05 02:01:21 +02:00
game - > handlers . postdraw ( game ) ;
2018-02-03 03:39:30 +01:00
}
2016-07-04 00:56:45 +02:00
}
2018-02-03 03:37:44 +01:00
SYMBOL_INTERNAL void LogicGamestates ( struct Game * game , double delta ) {
2017-09-10 21:35:14 +02:00
struct Gamestate * tmp = game - > _priv . gamestates ;
2018-02-03 03:39:30 +01:00
if ( game - > handlers . prelogic ) {
2018-08-05 02:01:21 +02:00
game - > handlers . prelogic ( game , delta ) ;
2018-02-03 03:39:30 +01:00
}
2016-07-04 00:56:45 +02:00
while ( tmp ) {
if ( ( tmp - > loaded ) & & ( tmp - > started ) & & ( ! tmp - > paused ) ) {
2016-08-16 18:41:50 +02:00
game - > _priv . current_gamestate = tmp ;
2018-08-05 02:01:21 +02:00
tmp - > api - > Gamestate_Logic ( game , tmp - > data , delta ) ;
2016-07-04 00:56:45 +02:00
}
tmp = tmp - > next ;
}
2018-02-03 03:39:30 +01:00
if ( game - > handlers . postlogic ) {
2018-08-05 02:01:21 +02:00
game - > handlers . postlogic ( game , delta ) ;
}
}
SYMBOL_INTERNAL void TickGamestates ( struct Game * game ) {
struct Gamestate * tmp = game - > _priv . gamestates ;
while ( tmp ) {
if ( ( tmp - > loaded ) & & ( tmp - > started ) & & ( ! tmp - > paused ) ) {
game - > _priv . current_gamestate = tmp ;
if ( tmp - > api - > Gamestate_Tick ) {
tmp - > api - > Gamestate_Tick ( game , tmp - > data ) ;
}
}
tmp = tmp - > next ;
2018-02-03 03:39:30 +01:00
}
2016-07-04 00:56:45 +02:00
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void ReloadGamestates ( struct Game * game ) {
struct Gamestate * tmp = game - > _priv . gamestates ;
2018-05-31 20:52:16 +02:00
ReloadShaders ( game , true ) ;
2016-11-11 19:38:26 +01:00
while ( tmp ) {
if ( tmp - > loaded ) {
game - > _priv . current_gamestate = tmp ;
2018-07-05 20:42:51 +02:00
if ( tmp - > api - > Gamestate_Reload ) {
2018-08-05 02:01:21 +02:00
tmp - > api - > Gamestate_Reload ( game , tmp - > data ) ;
2018-07-05 20:42:51 +02:00
}
2016-11-11 19:38:26 +01:00
}
tmp = tmp - > next ;
}
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void EventGamestates ( struct Game * game , ALLEGRO_EVENT * ev ) {
struct Gamestate * tmp = game - > _priv . gamestates ;
2016-07-04 00:56:45 +02:00
while ( tmp ) {
if ( ( tmp - > loaded ) & & ( tmp - > started ) & & ( ! tmp - > paused ) ) {
2016-08-16 18:41:50 +02:00
game - > _priv . current_gamestate = tmp ;
2018-08-05 02:01:21 +02:00
tmp - > api - > Gamestate_ProcessEvent ( game , tmp - > data , ev ) ;
2016-07-04 00:56:45 +02:00
}
tmp = tmp - > next ;
2016-11-09 00:40:13 +01:00
}
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void FreezeGamestates ( struct Game * game ) {
struct Gamestate * tmp = game - > _priv . gamestates ;
2016-11-09 00:40:13 +01:00
while ( tmp ) {
2016-11-11 19:38:26 +01:00
if ( tmp - > started & & ! tmp - > paused ) {
2016-11-09 00:40:13 +01:00
tmp - > frozen = true ;
2016-11-11 19:38:26 +01:00
PauseGamestate ( game , tmp - > name ) ;
2016-11-09 00:40:13 +01:00
}
tmp = tmp - > next ;
}
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void UnfreezeGamestates ( struct Game * game ) {
struct Gamestate * tmp = game - > _priv . gamestates ;
2016-11-09 00:40:13 +01:00
while ( tmp ) {
if ( tmp - > frozen ) {
ResumeGamestate ( game , tmp - > name ) ;
tmp - > frozen = false ;
}
tmp = tmp - > next ;
2016-07-04 00:56:45 +02:00
}
}
2018-02-03 03:46:33 +01:00
SYMBOL_INTERNAL void ResizeGamestates ( struct Game * game ) {
struct Gamestate * tmp = game - > _priv . gamestates ;
while ( tmp ) {
al_destroy_bitmap ( tmp - > fb ) ;
if ( game - > handlers . compositor ) {
tmp - > fb = CreateNotPreservedBitmap ( game - > _priv . clip_rect . w , game - > _priv . clip_rect . h ) ;
} else {
tmp - > fb = al_create_sub_bitmap ( al_get_backbuffer ( game - > display ) , game - > _priv . clip_rect . x , game - > _priv . clip_rect . y , game - > _priv . clip_rect . w , game - > _priv . clip_rect . h ) ;
}
tmp = tmp - > next ;
}
2018-11-22 04:53:51 +01:00
al_destroy_bitmap ( game - > loading_fb ) ;
if ( game - > handlers . compositor ) {
game - > loading_fb = CreateNotPreservedBitmap ( game - > _priv . clip_rect . w , game - > _priv . clip_rect . h ) ;
} else {
game - > loading_fb = al_create_sub_bitmap ( al_get_backbuffer ( game - > display ) , game - > _priv . clip_rect . x , game - > _priv . clip_rect . y , game - > _priv . clip_rect . w , game - > _priv . clip_rect . h ) ;
}
2018-02-03 03:46:33 +01:00
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void DrawConsole ( struct Game * game ) {
2018-03-20 23:49:22 +01:00
double game_time = al_get_time ( ) ;
2016-07-04 00:56:45 +02:00
if ( game - > _priv . showconsole ) {
al_set_target_backbuffer ( game - > display ) ;
ALLEGRO_TRANSFORM trans ;
al_identity_transform ( & trans ) ;
al_use_transform ( & trans ) ;
2018-07-18 01:23:02 +02:00
al_hold_bitmap_drawing ( true ) ;
2017-09-09 00:11:43 +02:00
int size = sizeof ( game - > _priv . console ) / sizeof ( game - > _priv . console [ 0 ] ) ;
2017-09-10 21:35:14 +02:00
for ( int i = 0 ; i < size ; i + + ) {
2018-11-22 21:01:35 +01:00
al_draw_filled_rectangle ( 0 , 0 , al_get_display_width ( game - > display ) , al_get_font_line_height ( game - > _priv . font_console ) * ( size - i ) , al_map_rgba ( 0 , 0 , 0 , 80 ) ) ;
2017-09-09 00:11:43 +02:00
}
int cur = game - > _priv . console_pos + size ;
2017-09-10 21:35:14 +02:00
for ( int i = 0 ; i < size ; i + + ) {
2017-09-09 00:11:43 +02:00
if ( cur > = size ) {
cur - = size ;
}
2018-11-22 21:01:35 +01:00
al_draw_text ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , ( int ) ( al_get_display_width ( game - > display ) * 0.005 ) , al_get_font_line_height ( game - > _priv . font_console ) * i , ALLEGRO_ALIGN_LEFT , game - > _priv . console [ cur ] ) ;
2017-09-09 00:11:43 +02:00
cur + + ;
}
2018-03-20 23:49:22 +01:00
char sfps [ 16 ] = { 0 } ;
2016-07-04 00:56:45 +02:00
snprintf ( sfps , 6 , " %.0f " , game - > _priv . fps_count . fps ) ;
2018-11-22 21:01:35 +01:00
DrawTextWithShadow ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , al_get_display_width ( game - > display ) , 0 , ALLEGRO_ALIGN_RIGHT , sfps ) ;
2018-03-20 23:49:22 +01:00
snprintf ( sfps , 16 , " %.2f ms " , 1000 * ( game_time - game - > _priv . fps_count . time ) ) ;
2018-11-22 21:01:35 +01:00
DrawTextWithShadow ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , al_get_display_width ( game - > display ) , al_get_font_line_height ( game - > _priv . font_console ) , ALLEGRO_ALIGN_RIGHT , sfps ) ;
2016-07-04 00:56:45 +02:00
2017-09-09 00:11:43 +02:00
DrawTimelines ( game ) ;
2018-07-18 01:23:02 +02:00
al_hold_bitmap_drawing ( false ) ;
al_use_transform ( & game - > projection ) ;
2017-09-09 00:11:43 +02:00
}
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 ;
2016-07-04 00:56:45 +02:00
}
2018-03-20 23:49:22 +01:00
game - > _priv . fps_count . time = game_time ;
2016-07-04 00:56:45 +02:00
game - > _priv . fps_count . frames_done + + ;
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void Console_Load ( struct Game * game ) {
2018-10-10 21:37:54 +02:00
game - > _priv . font_console = al_load_ttf_font ( GetDataFilePath ( game , " fonts/DejaVuSansMono.ttf " ) , ( int ) ( game - > _priv . clip_rect . h * 0.025 ) , 0 ) ;
2018-07-18 01:23:02 +02:00
if ( game - > _priv . clip_rect . h * 0.025 > = 16 ) {
game - > _priv . font_bsod = al_load_ttf_font ( GetDataFilePath ( game , " fonts/PerfectDOSVGA437.ttf " ) , 16 * ( ( game - > _priv . clip_rect . h > 1080 ) ? 2 : 1 ) , 0 ) ;
2016-07-04 00:56:45 +02:00
} else {
2018-10-10 21:37:54 +02:00
game - > _priv . font_bsod = al_load_ttf_font ( GetDataFilePath ( game , " fonts/DejaVuSansMono.ttf " ) , ( int ) ( game - > _priv . clip_rect . h * 0.025 ) , 0 ) ;
2016-07-04 00:56:45 +02:00
}
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void Console_Unload ( struct Game * game ) {
2017-09-09 00:11:43 +02:00
if ( game - > _priv . font_console ) {
al_destroy_font ( game - > _priv . font_console ) ;
}
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void * GamestateLoadingThread ( void * arg ) {
struct GamestateLoadingThreadData * data = arg ;
2017-09-09 00:11:43 +02:00
data - > game - > _priv . loading . inProgress = true ;
al_set_new_bitmap_flags ( data - > bitmap_flags ) ;
2018-08-05 02:01:21 +02:00
data - > gamestate - > data = data - > gamestate - > api - > Gamestate_Load ( data - > game , & GamestateProgress ) ;
2018-07-05 20:42:51 +02:00
if ( data - > game - > _priv . loading . progress ! = data - > gamestate - > progressCount ) {
2018-07-05 19:39:11 +02:00
PrintConsole ( data - > game , " [%s] WARNING: Gamestate_ProgressCount does not match the number of progress invokations (%d)! " , data - > gamestate - > name , data - > game - > _priv . loading . progress ) ;
if ( data - > game - > config . debug ) {
PrintConsole ( data - > game , " (sleeping for 3 seconds...) " ) ;
data - > game - > _priv . showconsole = true ;
al_rest ( 3.0 ) ;
}
}
2017-09-09 00:11:43 +02:00
data - > bitmap_flags = al_get_new_bitmap_flags ( ) ;
data - > game - > _priv . loading . inProgress = false ;
return NULL ;
2016-07-04 00:56:45 +02:00
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void * ScreenshotThread ( void * arg ) {
struct ScreenshotThreadData * data = arg ;
ALLEGRO_PATH * path = al_get_standard_path ( ALLEGRO_USER_DOCUMENTS_PATH ) ;
2017-09-09 00:42:57 +02:00
char filename [ 255 ] ;
snprintf ( filename , 255 , " %s_%ju_%ju.png " , data - > game - > name , ( uintmax_t ) time ( NULL ) , ( uintmax_t ) clock ( ) ) ;
al_set_path_filename ( path , filename ) ;
al_save_bitmap ( al_path_cstr ( path , ALLEGRO_NATIVE_PATH_SEP ) , data - > bitmap ) ;
PrintConsole ( data - > game , " Screenshot stored in %s " , al_path_cstr ( path , ALLEGRO_NATIVE_PATH_SEP ) ) ;
al_destroy_path ( path ) ;
al_destroy_bitmap ( data - > bitmap ) ;
free ( data ) ;
return NULL ;
}
2018-07-05 20:42:51 +02:00
SYMBOL_INTERNAL void CalculateProgress ( struct Game * game ) {
2017-09-10 21:35:14 +02:00
struct Gamestate * tmp = game - > _priv . loading . current ;
2018-07-05 20:42:51 +02:00
float progress = ( ( game - > _priv . loading . progress / ( float ) ( tmp - > progressCount + 1 ) ) / ( float ) game - > _priv . loading . toLoad ) + ( game - > _priv . loading . loaded / ( float ) game - > _priv . loading . toLoad ) ;
2017-09-10 21:35:14 +02:00
if ( game - > config . debug ) {
2018-07-05 20:42:51 +02:00
PrintConsole ( game , " [%s] Progress: %d%% (%d/%d) " , tmp - > name , ( int ) ( progress * 100 ) , game - > _priv . loading . progress , tmp - > progressCount + 1 ) ;
2017-09-10 21:35:14 +02:00
}
2018-07-05 20:42:51 +02:00
game - > loading_progress = progress ;
}
SYMBOL_INTERNAL void GamestateProgress ( struct Game * game ) {
game - > _priv . loading . progress + + ;
CalculateProgress ( game ) ;
2018-06-23 04:44:36 +02:00
# ifndef LIBSUPERDERPY_SINGLE_THREAD
2018-07-05 19:38:31 +02:00
// TODO: debounce thread synchronization to reduce overhead
2018-06-23 04:44:36 +02:00
al_lock_mutex ( game - > _priv . texture_sync_mutex ) ;
game - > _priv . texture_sync = true ;
while ( game - > _priv . texture_sync ) {
al_wait_cond ( game - > _priv . texture_sync_cond , game - > _priv . texture_sync_mutex ) ;
}
al_unlock_mutex ( game - > _priv . texture_sync_mutex ) ;
2018-07-04 06:55:12 +02:00
# else
2018-07-05 19:38:31 +02:00
al_convert_memory_bitmaps ( ) ;
2018-04-21 01:08:39 +02:00
double delta = al_get_time ( ) - game - > _priv . loading . time ;
2018-07-06 03:55:32 +02:00
if ( game - > _priv . loading . current - > showLoading ) {
2018-08-05 02:01:21 +02:00
game - > _priv . loading . gamestate - > api - > Gamestate_Logic ( game , game - > _priv . loading . gamestate - > data , delta ) ;
2018-11-22 04:53:51 +01:00
DrawGamestates ( game ) ;
2017-09-10 22:53:41 +02:00
}
2018-04-21 01:08:39 +02:00
game - > _priv . loading . time + = delta ;
2016-07-04 00:56:45 +02:00
DrawConsole ( game ) ;
2017-09-09 00:11:43 +02:00
al_flip_display ( ) ;
# endif
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL bool OpenGamestate ( struct Game * game , struct Gamestate * gamestate ) {
2017-09-09 00:11:43 +02:00
PrintConsole ( game , " Opening gamestate \" %s \" ... " , gamestate - > name ) ;
char libname [ 1024 ] ;
snprintf ( libname , 1024 , " libsuperderpy-%s-%s " LIBRARY_EXTENSION , game - > name , gamestate - > name ) ;
2017-09-20 18:11:29 +02:00
gamestate - > handle = dlopen ( AddGarbage ( game , GetLibraryPath ( game , libname ) ) , RTLD_NOW ) ;
2017-09-09 00:11:43 +02:00
if ( ! gamestate - > handle ) {
FatalError ( game , false , " Error while opening gamestate \" %s \" : %s " , gamestate - > name , dlerror ( ) ) ; // TODO: move out
return false ;
}
2018-04-16 01:06:58 +02:00
if ( game - > handlers . compositor ) {
gamestate - > fb = CreateNotPreservedBitmap ( game - > _priv . clip_rect . w , game - > _priv . clip_rect . h ) ;
} else {
gamestate - > fb = al_create_sub_bitmap ( al_get_backbuffer ( game - > display ) , game - > _priv . clip_rect . x , game - > _priv . clip_rect . y , game - > _priv . clip_rect . w , game - > _priv . clip_rect . h ) ;
}
2018-09-10 04:36:18 +02:00
gamestate - > open = true ;
2017-09-09 00:11:43 +02:00
return true ;
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL bool LinkGamestate ( struct Game * game , struct Gamestate * gamestate ) {
2018-07-19 01:39:53 +02:00
gamestate - > api = calloc ( 1 , sizeof ( struct Gamestate_API ) ) ;
2017-09-09 00:11:43 +02:00
2017-09-10 21:35:14 +02:00
# define GS_ERROR \
FatalError ( game , false , " Error on resolving gamestate's %s symbol: %s " , gamestate - > name , dlerror ( ) ) ; /* TODO: move out */ \
free ( gamestate - > api ) ; \
2017-09-09 00:11:43 +02:00
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 ; }
2018-07-05 20:42:51 +02:00
if ( ! ( gamestate - > api - > Gamestate_Unload = dlsym ( gamestate - > handle , " Gamestate_Unload " ) ) ) { GS_ERROR ; }
2017-09-09 00:11:43 +02:00
if ( ! ( gamestate - > api - > Gamestate_Start = dlsym ( gamestate - > handle , " Gamestate_Start " ) ) ) { GS_ERROR ; }
if ( ! ( gamestate - > api - > Gamestate_Stop = dlsym ( gamestate - > handle , " Gamestate_Stop " ) ) ) { GS_ERROR ; }
if ( ! ( gamestate - > api - > Gamestate_ProcessEvent = dlsym ( gamestate - > handle , " Gamestate_ProcessEvent " ) ) ) { GS_ERROR ; }
2018-07-05 20:42:51 +02:00
// optional
2018-08-05 02:01:21 +02:00
gamestate - > api - > Gamestate_Tick = dlsym ( gamestate - > handle , " Gamestate_Tick " ) ;
2018-07-05 20:55:32 +02:00
gamestate - > api - > Gamestate_PostLoad = dlsym ( gamestate - > handle , " Gamestate_PostLoad " ) ;
2018-07-05 20:42:51 +02:00
gamestate - > api - > Gamestate_Pause = dlsym ( gamestate - > handle , " Gamestate_Pause " ) ;
gamestate - > api - > Gamestate_Resume = dlsym ( gamestate - > handle , " Gamestate_Resume " ) ;
gamestate - > api - > Gamestate_Reload = dlsym ( gamestate - > handle , " Gamestate_Reload " ) ;
gamestate - > api - > Gamestate_ProgressCount = dlsym ( gamestate - > handle , " Gamestate_ProgressCount " ) ;
if ( gamestate - > api - > Gamestate_ProgressCount ) {
gamestate - > progressCount = * gamestate - > api - > Gamestate_ProgressCount ;
}
2017-09-09 00:11:43 +02:00
# undef GS_ERROR
return true ;
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL struct Gamestate * AllocateGamestate ( struct Game * game , const char * name ) {
struct Gamestate * tmp = malloc ( sizeof ( struct Gamestate ) ) ;
2017-09-09 00:11:43 +02:00
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 ;
2018-07-18 19:58:34 +02:00
tmp - > fromlib = true ;
2018-07-05 20:42:51 +02:00
tmp - > progressCount = 0 ;
2018-09-10 04:36:18 +02:00
tmp - > open = false ;
2018-11-01 23:56:37 +01:00
tmp - > fb = NULL ;
tmp - > showLoading = true ;
tmp - > data = NULL ;
2017-09-09 00:11:43 +02:00
return tmp ;
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void CloseGamestate ( struct Game * game , struct Gamestate * gamestate ) {
2018-09-10 04:36:18 +02:00
if ( ! gamestate - > open ) {
return ;
}
2017-09-09 00:11:43 +02:00
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 ) ;
2016-07-04 00:56:45 +02:00
}
2018-02-03 03:46:33 +01:00
al_destroy_bitmap ( gamestate - > fb ) ;
2016-07-04 00:56:45 +02:00
}
2016-08-15 04:37:27 +02:00
2018-08-03 05:02:44 +02:00
SYMBOL_INTERNAL struct List * AddToList ( struct List * list , void * data ) {
2016-08-16 18:01:12 +02:00
if ( ! list ) {
2018-08-03 05:02:44 +02:00
list = malloc ( sizeof ( struct List ) ) ;
2016-08-16 18:01:12 +02:00
list - > data = data ;
list - > next = NULL ;
2016-08-15 04:37:27 +02:00
} else {
2018-08-03 05:02:44 +02:00
struct List * elem = malloc ( sizeof ( struct List ) ) ;
2016-08-16 18:01:12 +02:00
elem - > next = list ;
elem - > data = data ;
list = elem ;
}
return list ;
}
2018-08-03 05:02:44 +02:00
static bool Identity ( struct List * elem , void * data ) {
2018-05-30 21:11:46 +02:00
return elem - > data = = data ;
}
2018-08-03 05:02:44 +02:00
SYMBOL_INTERNAL struct List * FindInList ( struct List * list , void * data , bool ( * identity ) ( struct List * elem , void * data ) ) {
struct List * tmp = list ;
2018-05-31 20:52:16 +02:00
if ( ! identity ) {
identity = Identity ;
}
while ( tmp ) {
if ( identity ( tmp , data ) ) {
return tmp ;
}
tmp = tmp - > next ;
}
return NULL ;
}
2018-08-03 05:02:44 +02:00
SYMBOL_INTERNAL void * RemoveFromList ( struct List * * list , void * data , bool ( * identity ) ( struct List * elem , void * data ) ) {
struct List * prev = NULL , * tmp = * list , * start ;
2016-08-16 18:01:12 +02:00
void * d = NULL ;
2018-05-30 21:11:46 +02:00
if ( ! identity ) {
identity = Identity ;
}
2016-08-16 18:01:12 +02:00
while ( tmp ) {
if ( identity ( tmp , data ) ) {
if ( prev ) {
prev - > next = tmp - > next ;
d = tmp - > data ;
free ( tmp ) ;
return d ;
}
2017-09-10 21:35:14 +02:00
start = tmp - > next ;
d = tmp - > data ;
free ( tmp ) ;
* list = start ;
return d ;
2016-08-16 18:01:12 +02:00
}
prev = tmp ;
tmp = tmp - > next ;
2016-08-15 04:37:27 +02:00
}
2016-08-16 18:01:12 +02:00
return NULL ;
}
2018-03-15 00:46:52 +01:00
// TODO: maybe make external?
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void * AddGarbage ( struct Game * game , void * data ) {
2016-08-16 18:01:12 +02:00
game - > _priv . garbage = AddToList ( game - > _priv . garbage , data ) ;
2016-08-15 04:37:27 +02:00
return data ;
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void ClearGarbage ( struct Game * game ) {
2018-08-03 05:02:44 +02:00
struct List * tmp ;
2016-08-15 04:37:27 +02:00
while ( game - > _priv . garbage ) {
free ( game - > _priv . garbage - > data ) ;
2016-08-16 18:01:12 +02:00
tmp = game - > _priv . garbage - > next ;
free ( game - > _priv . garbage ) ;
game - > _priv . garbage = tmp ;
2016-08-15 04:37:27 +02:00
}
}
2016-11-08 10:34:32 +01:00
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void AddTimeline ( struct Game * game , struct Timeline * timeline ) {
2017-08-07 02:26:36 +02:00
game - > _priv . timelines = AddToList ( game - > _priv . timelines , timeline ) ;
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void RemoveTimeline ( struct Game * game , struct Timeline * timeline ) {
2018-08-03 05:02:44 +02:00
struct List * tmp = game - > _priv . timelines ;
2017-08-07 02:26:36 +02:00
if ( tmp - > data = = timeline ) {
2018-08-03 05:02:44 +02:00
struct List * next = tmp - > next ;
2017-08-07 02:26:36 +02:00
free ( tmp ) ;
game - > _priv . timelines = next ;
return ;
}
while ( tmp - > next ) {
if ( tmp - > next - > data = = timeline ) {
2018-08-03 05:02:44 +02:00
struct List * next = tmp - > next - > next ;
2017-08-07 02:26:36 +02:00
free ( tmp - > next ) ;
tmp - > next = next ;
return ;
}
2017-08-07 15:59:12 +02:00
tmp = tmp - > next ;
2017-08-07 02:26:36 +02:00
}
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void ClearScreen ( struct Game * game ) {
2016-11-08 10:34:32 +01:00
al_set_target_backbuffer ( game - > display ) ;
2018-06-03 03:39:28 +02:00
al_reset_clipping_rectangle ( ) ;
2017-09-10 21:35:14 +02:00
al_clear_to_color ( al_map_rgb ( 0 , 0 , 0 ) ) ;
2018-11-27 03:06:43 +01:00
if ( game - > viewport_config . depth_buffer ) {
al_clear_depth_buffer ( 1.0 ) ;
}
2018-02-03 03:46:33 +01:00
al_set_clipping_rectangle ( game - > _priv . clip_rect . x , game - > _priv . clip_rect . y , game - > _priv . clip_rect . w , game - > _priv . clip_rect . h ) ;
2016-11-08 10:34:32 +01:00
}
2017-08-07 02:26:36 +02:00
2017-09-10 21:35:14 +02:00
static void DrawQueue ( struct Game * game , struct TM_Action * queue , int clipX , int clipY ) {
2017-08-07 02:26:36 +02:00
int pos = clipX ;
2017-09-10 21:35:14 +02:00
struct TM_Action * pom = queue ;
while ( pom ! = NULL ) {
2017-08-07 02:26:36 +02:00
int width = al_get_text_width ( game - > _priv . font_console , pom - > name ) ;
2018-07-18 01:23:02 +02:00
al_draw_filled_rectangle ( pos - ( 10 / 3200.0 ) * game - > _priv . clip_rect . w , clipY , pos + width + ( 10 / 3200.0 ) * game - > _priv . clip_rect . w , clipY + ( 60 / 1800.0 ) * game - > _priv . clip_rect . h , pom - > started ? al_map_rgba ( 255 , 255 , 255 , 192 ) : al_map_rgba ( 0 , 0 , 0 , 0 ) ) ;
al_draw_rectangle ( pos - ( 10 / 3200.0 ) * game - > _priv . clip_rect . w , clipY , pos + width + ( 10 / 3200.0 ) * game - > _priv . clip_rect . w , clipY + ( 60 / 1800.0 ) * game - > _priv . clip_rect . h , al_map_rgb ( 255 , 255 , 255 ) , 2 ) ;
2018-06-29 22:59:25 +02:00
al_draw_text ( game - > _priv . font_console , pom - > started ? al_map_rgb ( 0 , 0 , 0 ) : al_map_rgb ( 255 , 255 , 255 ) , pos , clipY , ALLEGRO_ALIGN_LEFT , pom - > name ) ;
2017-08-07 02:26:36 +02:00
if ( pom - > delay ) {
2018-07-18 01:23:02 +02:00
al_draw_textf ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , pos , clipY - ( 50 / 1800.0 ) * game - > _priv . clip_rect . h , ALLEGRO_ALIGN_LEFT , " %d " , ( int ) ( pom - > delay * 1000 ) ) ;
2017-08-07 02:26:36 +02:00
}
2018-06-30 02:52:06 +02:00
if ( strncmp ( pom - > name , " TM_RunInBackground " , 18 ) = = 0 ) { // FIXME: this is crappy way to detect queued background actions
2018-07-18 01:23:02 +02:00
al_draw_textf ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , pos , clipY - ( 50 / 1800.0 ) * game - > _priv . clip_rect . h , ALLEGRO_ALIGN_LEFT , " %s " , ( char * ) pom - > arguments - > next - > next - > value ) ;
2017-08-07 02:26:36 +02:00
}
2018-10-10 21:37:54 +02:00
pos + = width + ( int ) ( ( 20 / 3200.0 ) * game - > _priv . clip_rect . w ) ;
2017-08-07 02:26:36 +02:00
pom = pom - > next ;
}
}
2017-09-10 21:35:14 +02:00
static void DrawTimeline ( struct Game * game , struct Timeline * timeline , int pos ) {
2018-11-22 21:01:35 +01:00
al_draw_filled_rectangle ( 0 , al_get_display_height ( game - > display ) - ( 340 / 1800.0 ) * al_get_display_height ( game - > display ) * ( pos + 1 ) , al_get_display_width ( game - > display ) , al_get_display_height ( game - > display ) - ( 340 / 1800.0 ) * al_get_display_height ( game - > display ) * pos , al_map_rgba ( 0 , 0 , 0 , 92 ) ) ;
2017-08-07 02:26:36 +02:00
2018-11-22 21:01:35 +01:00
al_draw_textf ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , al_get_display_width ( game - > display ) / 2.0 , al_get_display_height ( game - > display ) - ( 340 / 1800.0 ) * al_get_display_height ( game - > display ) * ( pos + 1 ) + ( 10 / 1800.0 ) * al_get_display_height ( game - > display ) , ALLEGRO_ALIGN_CENTER , " Timeline: %s " , timeline - > name ) ;
2017-08-07 02:26:36 +02:00
2018-11-22 21:01:35 +01:00
DrawQueue ( game , timeline - > queue , ( int ) ( ( 25 / 3200.0 ) * al_get_display_width ( game - > display ) ) , al_get_display_height ( game - > display ) - ( int ) ( ( 220 / 1800.0 ) * al_get_display_height ( game - > display ) ) - ( int ) ( ( 340 / 1800.0 ) * al_get_display_height ( game - > display ) * pos ) ) ;
DrawQueue ( game , timeline - > background , ( int ) ( ( 25 / 3200.0 ) * al_get_display_width ( game - > display ) ) , al_get_display_height ( game - > display ) - ( int ) ( ( 100 / 1800.0 ) * al_get_display_height ( game - > display ) ) - ( int ) ( ( 340 / 1800.0 ) * al_get_display_height ( game - > display ) * pos ) ) ;
2017-08-07 02:26:36 +02:00
}
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL void DrawTimelines ( struct Game * game ) {
2017-09-09 00:11:43 +02:00
if ( ! game - > _priv . showtimeline ) {
2017-08-07 02:26:36 +02:00
return ;
}
2018-08-03 05:02:44 +02:00
struct List * tmp = game - > _priv . timelines ;
2017-09-10 21:35:14 +02:00
int i = 0 ;
2017-08-07 02:26:36 +02:00
while ( tmp ) {
DrawTimeline ( game , tmp - > data , i ) ;
i + + ;
tmp = tmp - > next ;
}
}
2017-09-20 18:11:29 +02:00
SYMBOL_INTERNAL char * GetLibraryPath ( struct Game * game , char * filename ) {
char * result = NULL ;
ALLEGRO_PATH * path = al_get_standard_path ( ALLEGRO_EXENAME_PATH ) ;
al_set_path_filename ( path , filename ) ;
if ( al_filename_exists ( al_path_cstr ( path , ALLEGRO_NATIVE_PATH_SEP ) ) ) {
result = strdup ( al_path_cstr ( path , ALLEGRO_NATIVE_PATH_SEP ) ) ;
} else {
al_append_path_component ( path , " gamestates " ) ;
if ( al_filename_exists ( al_path_cstr ( path , ALLEGRO_NATIVE_PATH_SEP ) ) ) {
result = strdup ( al_path_cstr ( path , ALLEGRO_NATIVE_PATH_SEP ) ) ;
} else {
result = strdup ( filename ) ;
}
}
al_destroy_path ( path ) ;
return result ;
}
2018-04-16 01:06:58 +02:00
SYMBOL_INTERNAL void PauseExecution ( struct Game * game ) {
2018-09-10 04:36:18 +02:00
if ( game - > _priv . paused ) {
return ;
}
2018-04-16 01:06:58 +02:00
game - > _priv . paused = true ;
2018-09-10 03:18:52 +02:00
al_stop_timer ( game - > _priv . timer ) ;
al_detach_voice ( game - > audio . v ) ;
FreezeGamestates ( game ) ;
PrintConsole ( game , " Engine halted. " ) ;
2018-04-16 01:06:58 +02:00
}
2018-08-09 00:42:53 +02:00
SYMBOL_INTERNAL void ReloadCode ( struct Game * game ) {
2018-05-31 20:52:16 +02:00
ReloadShaders ( game , true ) ;
2018-09-10 04:36:18 +02:00
PrintConsole ( game , " DEBUG: Reloading the gamestates... " ) ;
2018-04-16 01:06:58 +02:00
struct Gamestate * tmp = game - > _priv . gamestates ;
while ( tmp ) {
2018-09-10 04:36:18 +02:00
if ( tmp - > open & & tmp - > fromlib ) {
2018-07-18 19:58:34 +02:00
char * name = strdup ( tmp - > name ) ;
CloseGamestate ( game , tmp ) ;
tmp - > name = name ;
if ( OpenGamestate ( game , tmp ) & & LinkGamestate ( game , tmp ) & & tmp - > loaded ) {
if ( tmp - > api - > Gamestate_Reload ) {
2018-09-10 04:36:18 +02:00
PrintConsole ( game , " [%s] Reloading... " , tmp - > name ) ;
2018-07-18 19:58:34 +02:00
tmp - > api - > Gamestate_Reload ( game , tmp - > data ) ;
}
2018-07-19 01:39:53 +02:00
} else {
// live-reload failed
tmp - > loaded = false ;
2018-07-18 19:47:56 +02:00
}
2018-04-18 23:14:05 +02:00
}
2018-08-09 00:42:53 +02:00
tmp = tmp - > next ;
}
2018-09-10 04:36:18 +02:00
PrintConsole ( game , " DEBUG: Reloading done. " ) ;
2018-08-09 00:42:53 +02:00
}
2018-07-19 01:39:53 +02:00
2018-08-09 00:42:53 +02:00
SYMBOL_INTERNAL void ResumeExecution ( struct Game * game ) {
2018-09-10 04:36:18 +02:00
if ( ! game - > _priv . paused ) {
return ;
}
2018-09-10 03:18:52 +02:00
UnfreezeGamestates ( game ) ;
al_attach_mixer_to_voice ( game - > audio . mixer , game - > audio . v ) ;
al_resume_timer ( game - > _priv . timer ) ;
2018-04-16 01:06:58 +02:00
game - > _priv . paused = false ;
2018-09-10 03:18:52 +02:00
game - > _priv . timestamp = al_get_time ( ) ;
PrintConsole ( game , " Engine resumed. " ) ;
2018-04-16 01:06:58 +02:00
}
2018-07-13 18:38:02 +02:00
SYMBOL_INTERNAL char * GetGameName ( struct Game * game , const char * format ) {
char * result = malloc ( sizeof ( char ) * 255 ) ;
SUPPRESS_WARNING ( " -Wformat-nonliteral " )
snprintf ( result , 255 , format , game - > name ) ;
SUPPRESS_END
return AddGarbage ( game , result ) ;
}
2018-08-03 05:02:44 +02:00
static int HashString ( struct Game * game , const char * str ) {
unsigned long hash = 5381 ;
int c ;
while ( ( c = * str + + ) ) {
hash = ( ( hash < < 5 ) + hash ) + c ; /* hash * 33 + c */
}
//PrintConsole(game, "sum %d, bucket %d", hash, hash % LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS);
return hash % LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS ;
}
static bool RefCountIdentity ( struct List * elem , void * data ) {
struct RefCount * item = elem - > data ;
return strcmp ( data , item - > id ) = = 0 ;
}
SYMBOL_INTERNAL ALLEGRO_BITMAP * AddBitmap ( struct Game * game , char * filename ) {
int bucket = HashString ( game , filename ) ;
struct List * item = FindInList ( game - > _priv . bitmaps [ bucket ] , filename , RefCountIdentity ) ;
struct RefCount * rc ;
if ( item ) {
rc = item - > data ;
rc - > counter + + ;
} else {
rc = malloc ( sizeof ( struct RefCount ) ) ;
rc - > counter = 1 ;
rc - > id = strdup ( filename ) ;
rc - > data = al_load_bitmap ( GetDataFilePath ( game , filename ) ) ;
game - > _priv . bitmaps [ bucket ] = AddToList ( game - > _priv . bitmaps [ bucket ] , rc ) ;
}
return rc - > data ;
}
SYMBOL_INTERNAL void RemoveBitmap ( struct Game * game , char * filename ) {
int bucket = HashString ( game , filename ) ;
struct List * item = FindInList ( game - > _priv . bitmaps [ bucket ] , filename , RefCountIdentity ) ;
if ( item ) {
struct RefCount * rc = item - > data ;
rc - > counter - - ;
if ( rc - > counter = = 0 ) {
RemoveFromList ( & game - > _priv . bitmaps [ bucket ] , filename , RefCountIdentity ) ;
al_destroy_bitmap ( rc - > data ) ;
free ( rc - > id ) ;
free ( rc ) ;
}
} else {
PrintConsole ( game , " Tried to remove non-existent bitmap %s! " , filename ) ;
}
}