2020-09-18 16:45:42 +00:00
# include <base/system.h>
# include <algorithm>
# include <engine/graphics.h>
# include <engine/map.h>
# include <engine/shared/config.h>
# include <game/client/components/camera.h>
# include <game/client/components/mapimages.h>
# include <game/client/components/maplayers.h>
2021-07-12 09:29:59 +00:00
# include <game/layers.h>
# include <game/client/render.h>
2020-09-18 16:45:42 +00:00
# include "menu_background.h"
2020-09-26 19:41:58 +00:00
CMenuBackground : : CMenuBackground ( ) :
CBackground ( CMapLayers : : TYPE_FULL_DESIGN , false )
2020-09-18 16:45:42 +00:00
{
m_RotationCenter = vec2 ( 0.0f , 0.0f ) ;
m_AnimationStartPos = vec2 ( 0.0f , 0.0f ) ;
m_Camera . m_Center = vec2 ( 0.0f , 0.0f ) ;
m_Camera . m_PrevCenter = vec2 ( 0.0f , 0.0f ) ;
m_MenuCenter = vec2 ( 0.0f , 0.0f ) ;
2020-10-13 11:15:44 +00:00
m_ChangedPosition = false ;
2020-09-18 16:45:42 +00:00
ResetPositions ( ) ;
m_CurrentPosition = - 1 ;
m_MoveTime = 0.0f ;
m_IsInit = false ;
}
CBackgroundEngineMap * CMenuBackground : : CreateBGMap ( )
{
return new CMenuMap ;
}
void CMenuBackground : : OnInit ( )
{
2020-10-10 10:12:04 +00:00
m_pBackgroundMap = CreateBGMap ( ) ;
m_pMap = m_pBackgroundMap ;
2020-09-18 16:45:42 +00:00
m_IsInit = true ;
m_pImages - > m_pClient = GameClient ( ) ;
Kernel ( ) - > RegisterInterface < CMenuMap > ( ( CMenuMap * ) m_pBackgroundMap ) ;
if ( g_Config . m_ClMenuMap [ 0 ] ! = ' \0 ' )
LoadMenuBackground ( ) ;
m_Camera . m_pClient = GameClient ( ) ;
m_Camera . m_ZoomSet = false ;
m_Camera . m_ZoomSmoothingTarget = 0 ;
}
void CMenuBackground : : ResetPositions ( )
{
m_Positions [ POS_START ] = vec2 ( 500.0f , 500.0f ) ;
2020-09-26 07:37:35 +00:00
m_Positions [ POS_BROWSER_INTERNET ] = vec2 ( 1000.0f , 1000.0f ) ;
m_Positions [ POS_BROWSER_LAN ] = vec2 ( 1100.0f , 1000.0f ) ;
2020-09-18 16:45:42 +00:00
m_Positions [ POS_DEMOS ] = vec2 ( 900.0f , 100.0f ) ;
m_Positions [ POS_NEWS ] = vec2 ( 500.0f , 750.0f ) ;
2020-09-26 07:37:35 +00:00
m_Positions [ POS_BROWSER_FAVORITES ] = vec2 ( 1250.0f , 500.0f ) ;
2020-09-18 16:45:42 +00:00
m_Positions [ POS_SETTINGS_LANGUAGE ] = vec2 ( 500.0f , 1200.0f ) ;
m_Positions [ POS_SETTINGS_GENERAL ] = vec2 ( 500.0f , 1000.0f ) ;
m_Positions [ POS_SETTINGS_PLAYER ] = vec2 ( 600.0f , 1000.0f ) ;
m_Positions [ POS_SETTINGS_TEE ] = vec2 ( 700.0f , 1000.0f ) ;
m_Positions [ POS_SETTINGS_HUD ] = vec2 ( 200.0f , 1000.0f ) ;
m_Positions [ POS_SETTINGS_CONTROLS ] = vec2 ( 800.0f , 1000.0f ) ;
m_Positions [ POS_SETTINGS_GRAPHICS ] = vec2 ( 900.0f , 1000.0f ) ;
m_Positions [ POS_SETTINGS_SOUND ] = vec2 ( 1000.0f , 1000.0f ) ;
m_Positions [ POS_SETTINGS_DDNET ] = vec2 ( 1200.0f , 200.0f ) ;
2020-09-26 07:37:35 +00:00
m_Positions [ POS_SETTINGS_ASSETS ] = vec2 ( 500.0f , 500.0f ) ;
for ( int i = 0 ; i < POS_BROWSER_CUSTOM_NUM ; + + i )
2020-10-13 11:15:44 +00:00
m_Positions [ POS_BROWSER_CUSTOM0 + i ] = vec2 ( 500.0f + ( 75.0f * ( float ) i ) , 650.0f - ( 75.0f * ( float ) i ) ) ;
2020-09-26 07:37:35 +00:00
for ( int i = 0 ; i < POS_SETTINGS_RESERVED_NUM ; + + i )
m_Positions [ POS_SETTINGS_RESERVED0 + i ] = vec2 ( 0 , 0 ) ;
for ( int i = 0 ; i < POS_RESERVED_NUM ; + + i )
m_Positions [ POS_RESERVED0 + i ] = vec2 ( 0 , 0 ) ;
2020-09-18 16:45:42 +00:00
}
int CMenuBackground : : ThemeScan ( const char * pName , int IsDir , int DirType , void * pUser )
{
CMenuBackground * pSelf = ( CMenuBackground * ) pUser ;
const char * pSuffix = str_endswith ( pName , " .map " ) ;
if ( IsDir | | ! pSuffix )
return 0 ;
char aFullName [ 128 ] ;
char aThemeName [ 128 ] ;
str_truncate ( aFullName , sizeof ( aFullName ) , pName , pSuffix - pName ) ;
bool IsDay = false ;
bool IsNight = false ;
if ( ( pSuffix = str_endswith ( aFullName , " _day " ) ) )
{
str_truncate ( aThemeName , sizeof ( aThemeName ) , pName , pSuffix - aFullName ) ;
IsDay = true ;
}
else if ( ( pSuffix = str_endswith ( aFullName , " _night " ) ) )
{
str_truncate ( aThemeName , sizeof ( aThemeName ) , pName , pSuffix - aFullName ) ;
IsNight = true ;
}
else
str_copy ( aThemeName , aFullName , sizeof ( aThemeName ) ) ;
if ( str_comp ( aThemeName , " none " ) = = 0 | | str_comp ( aThemeName , " auto " ) = = 0 | | str_comp ( aThemeName , " rand " ) = = 0 ) // "none", "auto" and "rand" reserved, disallowed for maps
return 0 ;
// try to edit an existing theme
2020-10-26 14:14:07 +00:00
for ( auto & Theme : pSelf - > m_lThemes )
2020-09-18 16:45:42 +00:00
{
2020-10-26 14:14:07 +00:00
if ( str_comp ( Theme . m_Name , aThemeName ) = = 0 )
2020-09-18 16:45:42 +00:00
{
if ( IsDay )
2020-10-26 14:14:07 +00:00
Theme . m_HasDay = true ;
2020-09-18 16:45:42 +00:00
if ( IsNight )
2020-10-26 14:14:07 +00:00
Theme . m_HasNight = true ;
2020-09-18 16:45:42 +00:00
return 0 ;
}
}
// make new theme
CTheme Theme ( aThemeName , IsDay , IsNight ) ;
char aBuf [ 512 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " added theme %s from themes/%s " , aThemeName , pName ) ;
pSelf - > Console ( ) - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " game " , aBuf ) ;
pSelf - > m_lThemes . push_back ( Theme ) ;
2022-04-02 11:37:59 +00:00
int64_t TimeNow = time_get_microseconds ( ) ;
if ( TimeNow - pSelf - > m_ThemeScanStartTime > = 1000000 / 60 )
{
pSelf - > Client ( ) - > UpdateAndSwap ( ) ;
pSelf - > m_ThemeScanStartTime = TimeNow ;
}
2020-09-18 16:45:42 +00:00
return 0 ;
}
int CMenuBackground : : ThemeIconScan ( const char * pName , int IsDir , int DirType , void * pUser )
{
CMenuBackground * pSelf = ( CMenuBackground * ) pUser ;
const char * pSuffix = str_endswith ( pName , " .png " ) ;
if ( IsDir | | ! pSuffix )
return 0 ;
2022-04-02 11:37:59 +00:00
int64_t TimeNow = time_get_microseconds ( ) ;
if ( TimeNow - pSelf - > m_ThemeScanStartTime > = 1000000 / 60 )
{
pSelf - > Client ( ) - > UpdateAndSwap ( ) ;
pSelf - > m_ThemeScanStartTime = TimeNow ;
}
2020-09-18 16:45:42 +00:00
char aThemeName [ 128 ] ;
str_truncate ( aThemeName , sizeof ( aThemeName ) , pName , pSuffix - pName ) ;
// save icon for an existing theme
for ( CTheme & Theme : pSelf - > m_lThemes ) // bit slow but whatever
{
if ( str_comp ( Theme . m_Name , aThemeName ) = = 0 | | ( ! Theme . m_Name [ 0 ] & & str_comp ( aThemeName , " none " ) = = 0 ) )
{
2021-09-13 08:06:34 +00:00
char aBuf [ IO_MAX_PATH_LENGTH ] ;
2020-09-18 16:45:42 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " themes/%s " , pName ) ;
CImageInfo Info ;
if ( ! pSelf - > Graphics ( ) - > LoadPNG ( & Info , aBuf , DirType ) )
{
str_format ( aBuf , sizeof ( aBuf ) , " failed to load theme icon from %s " , pName ) ;
pSelf - > Console ( ) - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " game " , aBuf ) ;
return 0 ;
}
str_format ( aBuf , sizeof ( aBuf ) , " loaded theme icon %s " , pName ) ;
pSelf - > Console ( ) - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " game " , aBuf ) ;
Theme . m_IconTexture = pSelf - > Graphics ( ) - > LoadTextureRaw ( Info . m_Width , Info . m_Height , Info . m_Format , Info . m_pData , Info . m_Format , 0 ) ;
2020-11-25 12:05:53 +00:00
pSelf - > Graphics ( ) - > FreePNG ( & Info ) ;
2020-09-18 16:45:42 +00:00
return 0 ;
}
}
return 0 ; // no existing theme
}
void CMenuBackground : : LoadMenuBackground ( bool HasDayHint , bool HasNightHint )
{
if ( ! m_IsInit )
return ;
if ( m_Loaded & & m_pMap = = m_pBackgroundMap )
m_pMap - > Unload ( ) ;
m_Loaded = false ;
m_pMap = m_pBackgroundMap ;
m_pLayers = m_pBackgroundLayers ;
m_pImages = m_pBackgroundImages ;
ResetPositions ( ) ;
bool NeedImageLoading = false ;
str_copy ( m_aMapName , g_Config . m_ClMenuMap , sizeof ( m_aMapName ) ) ;
if ( g_Config . m_ClMenuMap [ 0 ] ! = ' \0 ' )
{
const char * pMenuMap = g_Config . m_ClMenuMap ;
if ( str_comp ( pMenuMap , " auto " ) = = 0 )
{
switch ( time_season ( ) )
{
case SEASON_SPRING :
pMenuMap = " heavens " ;
break ;
case SEASON_SUMMER :
pMenuMap = " jungle " ;
break ;
case SEASON_AUTUMN :
pMenuMap = " autumn " ;
break ;
case SEASON_WINTER :
pMenuMap = " winter " ;
2020-12-28 16:20:29 +00:00
break ;
case SEASON_NEWYEAR :
pMenuMap = " newyear " ;
2020-09-18 16:45:42 +00:00
break ;
}
}
else if ( str_comp ( pMenuMap , " rand " ) = = 0 )
{
//make sure to load themes
std : : vector < CTheme > & ThemesRef = GetThemes ( ) ;
int RandomThemeIndex = rand ( ) % ( ThemesRef . size ( ) - PREDEFINED_THEMES_COUNT ) ;
if ( RandomThemeIndex + PREDEFINED_THEMES_COUNT < ( int ) ThemesRef . size ( ) )
pMenuMap = ThemesRef [ RandomThemeIndex + PREDEFINED_THEMES_COUNT ] . m_Name . cstr ( ) ;
}
char aBuf [ 128 ] ;
const int HourOfTheDay = time_houroftheday ( ) ;
const bool IsDaytime = HourOfTheDay > = 6 & & HourOfTheDay < 18 ;
if ( ! m_Loaded & & ( ( HasDayHint & & IsDaytime ) | | ( HasNightHint & & ! IsDaytime ) ) )
{
str_format ( aBuf , sizeof ( aBuf ) , " themes/%s_%s.map " , pMenuMap , IsDaytime ? " day " : " night " ) ;
if ( m_pMap - > Load ( aBuf ) )
{
m_Loaded = true ;
}
}
if ( ! m_Loaded )
{
str_format ( aBuf , sizeof ( aBuf ) , " themes/%s.map " , pMenuMap ) ;
if ( m_pMap - > Load ( aBuf ) )
{
m_Loaded = true ;
}
}
if ( ! m_Loaded & & ( ( HasDayHint & & ! IsDaytime ) | | ( HasNightHint & & IsDaytime ) ) )
{
str_format ( aBuf , sizeof ( aBuf ) , " themes/%s_%s.map " , pMenuMap , IsDaytime ? " night " : " day " ) ;
if ( m_pMap - > Load ( aBuf ) )
{
m_Loaded = true ;
}
}
if ( m_Loaded )
{
m_pLayers - > InitBackground ( m_pMap ) ;
NeedImageLoading = true ;
CMapLayers : : OnMapLoad ( ) ;
if ( NeedImageLoading )
m_pImages - > LoadBackground ( m_pLayers , m_pMap ) ;
// look for custom positions
CMapItemLayerTilemap * pTLayer = m_pLayers - > GameLayer ( ) ;
if ( pTLayer )
{
int DataIndex = pTLayer - > m_Data ;
unsigned int Size = m_pLayers - > Map ( ) - > GetDataSize ( DataIndex ) ;
void * pTiles = m_pLayers - > Map ( ) - > GetData ( DataIndex ) ;
unsigned int TileSize = sizeof ( CTile ) ;
if ( Size > = pTLayer - > m_Width * pTLayer - > m_Height * TileSize )
{
int x = 0 ;
int y = 0 ;
for ( y = 0 ; y < pTLayer - > m_Height ; + + y )
{
for ( x = 0 ; x < pTLayer - > m_Width ; + + x )
{
unsigned char Index = ( ( CTile * ) pTiles ) [ y * pTLayer - > m_Width + x ] . m_Index ;
if ( Index > = TILE_CHECKPOINT_FIRST & & Index < = TILE_CHECKPOINT_LAST )
{
int ArrayIndex = clamp < int > ( ( Index - TILE_CHECKPOINT_FIRST ) , 0 , NUM_POS ) ;
m_Positions [ ArrayIndex ] = vec2 ( x * 32.0f + 16.0f , y * 32.0f + 16.0f ) ;
}
x + = ( ( CTile * ) pTiles ) [ y * pTLayer - > m_Width + x ] . m_Skip ;
}
}
}
}
}
m_LastLoad = time_get ( ) ;
}
}
void CMenuBackground : : OnMapLoad ( )
{
}
void CMenuBackground : : OnRender ( )
{
}
bool CMenuBackground : : Render ( )
{
if ( ! m_Loaded )
return false ;
if ( Client ( ) - > State ( ) > = IClient : : STATE_ONLINE )
return false ;
m_Camera . m_Zoom = 0.7f ;
static vec2 Dir = vec2 ( 1.0f , 0.0f ) ;
2020-10-13 11:15:44 +00:00
float DistToCenter = distance ( m_Camera . m_Center , m_RotationCenter ) ;
if ( ! m_ChangedPosition & & absolute ( DistToCenter - ( float ) g_Config . m_ClRotationRadius ) < = 0.5f )
2020-09-18 16:45:42 +00:00
{
// do little rotation
float RotPerTick = 360.0f / ( float ) g_Config . m_ClRotationSpeed * clamp ( Client ( ) - > RenderFrameTime ( ) , 0.0f , 0.1f ) ;
Dir = rotate ( Dir , RotPerTick ) ;
m_Camera . m_Center = m_RotationCenter + Dir * ( float ) g_Config . m_ClRotationRadius ;
}
else
{
// positions for the animation
2020-10-13 11:15:44 +00:00
vec2 DirToCenter ;
if ( DistToCenter > 0.5f )
DirToCenter = normalize ( m_AnimationStartPos - m_RotationCenter ) ;
else
DirToCenter = vec2 ( 1 , 0 ) ;
vec2 TargetPos = m_RotationCenter + DirToCenter * ( float ) g_Config . m_ClRotationRadius ;
2020-09-18 16:45:42 +00:00
float Distance = distance ( m_AnimationStartPos , TargetPos ) ;
2020-10-13 11:15:44 +00:00
if ( Distance > 0.001f )
Dir = normalize ( m_AnimationStartPos - TargetPos ) ;
else
Dir = vec2 ( 1 , 0 ) ;
2020-09-18 16:45:42 +00:00
// move time
m_MoveTime + = clamp ( Client ( ) - > RenderFrameTime ( ) , 0.0f , 0.1f ) * g_Config . m_ClCameraSpeed / 10.0f ;
float XVal = 1 - m_MoveTime ;
XVal = pow ( XVal , 7.0f ) ;
m_Camera . m_Center = TargetPos + Dir * ( XVal * Distance ) ;
2020-10-14 20:09:36 +00:00
if ( m_CurrentPosition < 0 )
{
m_AnimationStartPos = m_Camera . m_Center ;
m_MoveTime = 0.0f ;
}
2020-10-13 11:15:44 +00:00
m_ChangedPosition = false ;
2020-09-18 16:45:42 +00:00
}
CMapLayers : : OnRender ( ) ;
2020-10-14 20:09:36 +00:00
m_CurrentPosition = - 1 ;
2020-09-18 16:45:42 +00:00
return true ;
}
CCamera * CMenuBackground : : GetCurCamera ( )
{
return & m_Camera ;
}
void CMenuBackground : : ChangePosition ( int PositionNumber )
{
if ( PositionNumber ! = m_CurrentPosition )
{
if ( PositionNumber > = POS_START & & PositionNumber < NUM_POS )
{
m_CurrentPosition = PositionNumber ;
}
else
{
m_CurrentPosition = POS_START ;
}
2020-10-13 11:15:44 +00:00
m_ChangedPosition = true ;
2020-09-18 16:45:42 +00:00
}
m_AnimationStartPos = m_Camera . m_Center ;
m_RotationCenter = m_Positions [ m_CurrentPosition ] ;
m_MoveTime = 0.0f ;
}
std : : vector < CTheme > & CMenuBackground : : GetThemes ( )
{
2022-01-22 12:54:25 +00:00
if ( m_lThemes . empty ( ) ) // not loaded yet
2020-09-18 16:45:42 +00:00
{
// when adding more here, make sure to change the value of PREDEFINED_THEMES_COUNT too
2022-02-14 23:15:06 +00:00
m_lThemes . emplace_back ( " " , true , true ) ; // no theme
m_lThemes . emplace_back ( " auto " , true , true ) ; // auto theme
m_lThemes . emplace_back ( " rand " , true , true ) ; // random theme
2020-09-18 16:45:42 +00:00
2022-04-02 11:37:59 +00:00
m_ThemeScanStartTime = time_get_microseconds ( ) ;
2020-09-18 16:45:42 +00:00
Storage ( ) - > ListDirectory ( IStorage : : TYPE_ALL , " themes " , ThemeScan , ( CMenuBackground * ) this ) ;
Storage ( ) - > ListDirectory ( IStorage : : TYPE_ALL , " themes " , ThemeIconScan , ( CMenuBackground * ) this ) ;
std : : sort ( m_lThemes . begin ( ) + PREDEFINED_THEMES_COUNT , m_lThemes . end ( ) ) ;
}
return m_lThemes ;
}