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 "libsuperderpy.h"
# include <allegro5/allegro_ttf.h>
# include <dlfcn.h>
2018-02-03 03:46:33 +01:00
# include <math.h>
2017-09-10 21:35:14 +02:00
# include <stdio.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 ;
al_clear_to_color ( al_map_rgb ( 0 , 0 , 0 ) ) ;
while ( tmp ) {
if ( ( tmp - > loaded ) & & ( tmp - > started ) ) {
al_draw_bitmap ( tmp - > fb , 0 , 0 , 0 ) ;
}
tmp = tmp - > next ;
}
}
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 ) {
( * game - > handlers . predraw ) ( game ) ;
}
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
al_clear_to_color ( al_map_rgb ( 0 , 0 , 0 ) ) ; // even if everything is going to be redrawn, it optimizes tiled rendering
}
2016-08-20 03:02:39 +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
if ( game - > handlers . compositor ) {
ALLEGRO_TRANSFORM t ;
al_set_target_backbuffer ( game - > display ) ;
ClearScreen ( game ) ;
al_identity_transform ( & t ) ;
/* double factor = (sin(al_get_time()) / 2.0 + 1.0) * 2;
al_translate_transform ( & t , - game - > _priv . clip_rect . w / factor , - game - > _priv . clip_rect . h / factor ) ;
al_scale_transform ( & t , factor , factor ) ;
al_translate_transform ( & t , game - > _priv . clip_rect . w / factor , game - > _priv . clip_rect . h / factor ) ; */
al_translate_transform ( & t , game - > _priv . clip_rect . x , game - > _priv . clip_rect . y ) ;
al_use_transform ( & t ) ;
game - > handlers . compositor ( game , game - > _priv . gamestates ) ;
}
2018-02-03 03:39:30 +01:00
if ( game - > handlers . postdraw ) {
( * game - > handlers . postdraw ) ( game ) ;
}
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 ) {
( * game - > handlers . prelogic ) ( game , delta ) ;
}
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-02-03 03:37:44 +01: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 ) {
( * game - > handlers . postlogic ) ( game , delta ) ;
}
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 ) {
( * tmp - > api - > Gamestate_Reload ) ( game , tmp - > data ) ;
}
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 ;
2016-08-20 03:02:39 +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 ;
}
}
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 ) ;
int clipX , clipY , clipWidth , clipHeight ;
al_get_clipping_rectangle ( & clipX , & clipY , & clipWidth , & clipHeight ) ;
al_use_transform ( & trans ) ;
2018-02-03 03:44:49 +01:00
al_hold_bitmap_drawing ( true ) ;
2016-07-04 00:56:45 +02:00
2017-09-09 00:11:43 +02:00
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 ;
2016-07-04 00:56:45 +02:00
}
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 + + ) {
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 ) ) ;
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 ;
}
2017-09-10 21:35:14 +02:00
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 ] ) ;
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 ) ;
2017-09-10 21:35:14 +02:00
DrawTextWithShadow ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , clipX + clipWidth , clipY , 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 ) ) ;
DrawTextWithShadow ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , clipX + clipWidth , clipY + al_get_font_line_height ( game - > _priv . font_console ) , ALLEGRO_ALIGN_RIGHT , sfps ) ;
2016-07-04 00:56:45 +02:00
al_use_transform ( & game - > projection ) ;
2017-09-09 00:11:43 +02:00
DrawTimelines ( game ) ;
}
2018-02-03 03:44:49 +01:00
al_hold_bitmap_drawing ( false ) ;
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 ) {
game - > _priv . font_console = al_load_ttf_font ( GetDataFilePath ( game , " fonts/DejaVuSansMono.ttf " ) , al_get_display_height ( game - > display ) * 0.025 , 0 ) ;
if ( al_get_display_height ( game - > display ) * 0.025 > = 16 ) {
game - > _priv . font_bsod = al_load_ttf_font ( GetDataFilePath ( game , " fonts/PerfectDOSVGA437.ttf " ) , 16 * ( ( al_get_display_height ( game - > display ) > 1080 ) ? 2 : 1 ) , 0 ) ;
2016-07-04 00:56:45 +02:00
} else {
2017-09-10 21:35:14 +02:00
game - > _priv . font_bsod = al_load_ttf_font ( GetDataFilePath ( game , " fonts/DejaVuSansMono.ttf " ) , al_get_display_height ( game - > display ) * 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 ) ;
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 ( ) ;
2017-09-09 00:11:43 +02:00
DrawGamestates ( game ) ;
2018-04-21 01:08:39 +02:00
double delta = al_get_time ( ) - game - > _priv . loading . time ;
2017-09-10 22:53:41 +02:00
if ( tmp - > showLoading ) {
2018-04-21 01:08:39 +02:00
( * game - > _priv . loading . gamestate - > api - > Gamestate_Logic ) ( game , game - > _priv . loading . gamestate - > data , delta ) ;
2017-09-10 22:53:41 +02:00
( * game - > _priv . loading . gamestate - > api - > Gamestate_Draw ) ( game , game - > _priv . loading . gamestate - > data ) ;
}
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 ) ;
}
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 ) {
2017-09-09 00:11:43 +02:00
gamestate - > api = malloc ( sizeof ( struct Gamestate_API ) ) ;
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-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-05 20:42:51 +02:00
tmp - > progressCount = 0 ;
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 ) {
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
2017-09-10 21:35:14 +02:00
SYMBOL_INTERNAL struct libsuperderpy_list * AddToList ( struct libsuperderpy_list * list , void * data ) {
2016-08-16 18:01:12 +02:00
if ( ! list ) {
list = malloc ( sizeof ( struct libsuperderpy_list ) ) ;
list - > data = data ;
list - > next = NULL ;
2016-08-15 04:37:27 +02:00
} else {
2017-09-10 21:35:14 +02:00
struct libsuperderpy_list * elem = malloc ( sizeof ( struct libsuperderpy_list ) ) ;
2016-08-16 18:01:12 +02:00
elem - > next = list ;
elem - > data = data ;
list = elem ;
}
return list ;
}
2018-05-30 21:11:46 +02:00
static bool Identity ( struct libsuperderpy_list * elem , void * data ) {
return elem - > data = = data ;
}
2018-05-31 20:52:16 +02:00
SYMBOL_INTERNAL struct libsuperderpy_list * FindInList ( struct libsuperderpy_list * list , void * data , bool ( * identity ) ( struct libsuperderpy_list * elem , void * data ) ) {
struct libsuperderpy_list * tmp = list ;
if ( ! identity ) {
identity = Identity ;
}
while ( tmp ) {
if ( identity ( tmp , data ) ) {
return tmp ;
}
tmp = tmp - > next ;
}
return NULL ;
}
SYMBOL_INTERNAL void * RemoveFromList ( struct libsuperderpy_list * * list , void * data , bool ( * identity ) ( struct libsuperderpy_list * elem , void * data ) ) {
2017-08-16 01:47:07 +02:00
struct libsuperderpy_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 ) {
struct libsuperderpy_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 ) {
struct libsuperderpy_list * tmp = game - > _priv . timelines ;
2017-08-07 02:26:36 +02:00
if ( tmp - > data = = timeline ) {
2017-09-10 21:35:14 +02:00
struct libsuperderpy_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 ) {
2017-09-10 21:35:14 +02:00
struct libsuperderpy_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-02-09 03:34:50 +01:00
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-06-29 22:59:25 +02:00
al_draw_filled_rectangle ( pos - ( 10 / 3200.0 ) * al_get_display_width ( game - > display ) , clipY , pos + width + ( 10 / 3200.0 ) * al_get_display_width ( game - > display ) , clipY + ( 60 / 1800.0 ) * al_get_display_height ( game - > display ) , pom - > started ? al_map_rgba ( 255 , 255 , 255 , 192 ) : al_map_rgba ( 0 , 0 , 0 , 0 ) ) ;
2017-09-10 21:35:14 +02:00
al_draw_rectangle ( pos - ( 10 / 3200.0 ) * al_get_display_width ( game - > display ) , clipY , pos + width + ( 10 / 3200.0 ) * al_get_display_width ( game - > display ) , clipY + ( 60 / 1800.0 ) * al_get_display_height ( game - > display ) , 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-06-30 01:27:28 +02:00
al_draw_textf ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , pos , clipY - ( 50 / 1800.0 ) * al_get_display_height ( game - > display ) , 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
2017-09-10 21:35:14 +02:00
al_draw_textf ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , pos , clipY - ( 50 / 1800.0 ) * al_get_display_height ( game - > display ) , ALLEGRO_ALIGN_LEFT , " %s " , ( char * ) pom - > arguments - > next - > next - > value ) ;
2017-08-07 02:26:36 +02:00
}
2017-09-10 21:35:14 +02:00
pos + = width + ( 20 / 3200.0 ) * al_get_display_width ( game - > display ) ;
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-06-28 04:38:33 +02:00
int clipX = game - > _priv . clip_rect . x , clipY = game - > _priv . clip_rect . y , clipWidth = game - > _priv . clip_rect . w , clipHeight = game - > _priv . clip_rect . h ;
2017-08-07 02:26:36 +02:00
2017-09-10 21:35:14 +02:00
al_draw_filled_rectangle ( clipX , clipY + clipHeight - ( 340 / 1800.0 ) * al_get_display_height ( game - > display ) * ( pos + 1 ) , clipX + clipWidth , clipY + clipHeight - ( 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-07-04 19:08:39 +02:00
al_draw_textf ( game - > _priv . font_console , al_map_rgb ( 255 , 255 , 255 ) , clipX + clipWidth / 2.0 , clipY + clipHeight - ( 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
2017-09-10 21:35:14 +02:00
DrawQueue ( game , timeline - > queue , clipX + ( 25 / 3200.0 ) * al_get_display_width ( game - > display ) , clipY + clipHeight - ( 220 / 1800.0 ) * al_get_display_height ( game - > display ) - ( 340 / 1800.0 ) * al_get_display_height ( game - > display ) * pos ) ;
DrawQueue ( game , timeline - > background , clipX + ( 25 / 3200.0 ) * al_get_display_width ( game - > display ) , clipY + clipHeight - ( 100 / 1800.0 ) * al_get_display_height ( game - > display ) - ( 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 ;
}
2017-09-10 21:35:14 +02:00
struct libsuperderpy_list * tmp = game - > _priv . timelines ;
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 ) {
game - > _priv . paused = true ;
PrintConsole ( game , " DEBUG: game execution paused. " ) ;
}
SYMBOL_INTERNAL void ResumeExecution ( struct Game * game ) {
2018-05-31 20:52:16 +02:00
ReloadShaders ( game , true ) ;
2018-04-16 01:06:58 +02:00
PrintConsole ( game , " DEBUG: reloading the gamestates... " ) ;
struct Gamestate * tmp = game - > _priv . gamestates ;
while ( tmp ) {
char * name = strdup ( tmp - > name ) ;
CloseGamestate ( game , tmp ) ;
tmp - > name = name ;
2018-04-18 23:14:05 +02:00
if ( OpenGamestate ( game , tmp ) & & LinkGamestate ( game , tmp ) ) {
2018-07-05 20:42:51 +02:00
if ( tmp - > api - > Gamestate_Reload ) {
tmp - > api - > Gamestate_Reload ( game , tmp - > data ) ;
}
2018-04-18 23:14:05 +02:00
}
2018-04-16 01:06:58 +02:00
tmp = tmp - > next ;
}
game - > _priv . paused = false ;
PrintConsole ( game , " DEBUG: game execution resumed. " ) ;
}