mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-11 02:28:18 +00:00
415 lines
11 KiB
C++
415 lines
11 KiB
C++
#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>
|
|
|
|
#include <game/layers.h>
|
|
#include <game/mapitems.h>
|
|
|
|
#include "menu_background.h"
|
|
|
|
#include <chrono>
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
CMenuBackground::CMenuBackground() :
|
|
CBackground(CMapLayers::TYPE_FULL_DESIGN, false)
|
|
{
|
|
m_ChangedPosition = false;
|
|
|
|
ResetPositions();
|
|
|
|
m_CurrentPosition = -1;
|
|
m_MoveTime = 0.0f;
|
|
|
|
m_IsInit = false;
|
|
}
|
|
|
|
CBackgroundEngineMap *CMenuBackground::CreateBGMap()
|
|
{
|
|
return new CMenuMap;
|
|
}
|
|
|
|
void CMenuBackground::OnInit()
|
|
{
|
|
m_pBackgroundMap = CreateBGMap();
|
|
m_pMap = m_pBackgroundMap;
|
|
|
|
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_aPositions[POS_START] = vec2(500.0f, 500.0f);
|
|
m_aPositions[POS_BROWSER_INTERNET] = vec2(1000.0f, 1000.0f);
|
|
m_aPositions[POS_BROWSER_LAN] = vec2(1100.0f, 1000.0f);
|
|
m_aPositions[POS_DEMOS] = vec2(900.0f, 100.0f);
|
|
m_aPositions[POS_NEWS] = vec2(500.0f, 750.0f);
|
|
m_aPositions[POS_BROWSER_FAVORITES] = vec2(1250.0f, 500.0f);
|
|
m_aPositions[POS_SETTINGS_LANGUAGE] = vec2(500.0f, 1200.0f);
|
|
m_aPositions[POS_SETTINGS_GENERAL] = vec2(500.0f, 1000.0f);
|
|
m_aPositions[POS_SETTINGS_PLAYER] = vec2(600.0f, 1000.0f);
|
|
m_aPositions[POS_SETTINGS_TEE] = vec2(700.0f, 1000.0f);
|
|
m_aPositions[POS_SETTINGS_APPEARANCE] = vec2(200.0f, 1000.0f);
|
|
m_aPositions[POS_SETTINGS_CONTROLS] = vec2(800.0f, 1000.0f);
|
|
m_aPositions[POS_SETTINGS_GRAPHICS] = vec2(900.0f, 1000.0f);
|
|
m_aPositions[POS_SETTINGS_SOUND] = vec2(1000.0f, 1000.0f);
|
|
m_aPositions[POS_SETTINGS_DDNET] = vec2(1200.0f, 200.0f);
|
|
m_aPositions[POS_SETTINGS_ASSETS] = vec2(500.0f, 500.0f);
|
|
for(int i = 0; i < POS_BROWSER_CUSTOM_NUM; ++i)
|
|
m_aPositions[POS_BROWSER_CUSTOM0 + i] = vec2(500.0f + (75.0f * (float)i), 650.0f - (75.0f * (float)i));
|
|
for(int i = 0; i < POS_SETTINGS_RESERVED_NUM; ++i)
|
|
m_aPositions[POS_SETTINGS_RESERVED0 + i] = vec2(0, 0);
|
|
for(int i = 0; i < POS_RESERVED_NUM; ++i)
|
|
m_aPositions[POS_RESERVED0 + i] = vec2(0, 0);
|
|
}
|
|
|
|
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);
|
|
|
|
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
|
|
for(auto &Theme : pSelf->m_vThemes)
|
|
{
|
|
if(str_comp(Theme.m_Name.c_str(), aThemeName) == 0)
|
|
{
|
|
if(IsDay)
|
|
Theme.m_HasDay = true;
|
|
if(IsNight)
|
|
Theme.m_HasNight = true;
|
|
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_vThemes.push_back(Theme);
|
|
auto TimeNow = time_get_nanoseconds();
|
|
if(TimeNow - pSelf->m_ThemeScanStartTime >= std::chrono::nanoseconds(1s) / 60)
|
|
{
|
|
pSelf->Client()->UpdateAndSwap();
|
|
pSelf->m_ThemeScanStartTime = TimeNow;
|
|
}
|
|
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;
|
|
|
|
auto TimeNow = time_get_nanoseconds();
|
|
if(TimeNow - pSelf->m_ThemeScanStartTime >= std::chrono::nanoseconds(1s) / 60)
|
|
{
|
|
pSelf->Client()->UpdateAndSwap();
|
|
pSelf->m_ThemeScanStartTime = TimeNow;
|
|
}
|
|
|
|
char aThemeName[128];
|
|
str_truncate(aThemeName, sizeof(aThemeName), pName, pSuffix - pName);
|
|
|
|
// save icon for an existing theme
|
|
for(CTheme &Theme : pSelf->m_vThemes) // bit slow but whatever
|
|
{
|
|
if(str_comp(Theme.m_Name.c_str(), aThemeName) == 0 || (Theme.m_Name.empty() && str_comp(aThemeName, "none") == 0))
|
|
{
|
|
char aBuf[IO_MAX_PATH_LENGTH];
|
|
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);
|
|
pSelf->Graphics()->FreePNG(&Info);
|
|
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);
|
|
|
|
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";
|
|
break;
|
|
case SEASON_NEWYEAR:
|
|
pMenuMap = "newyear";
|
|
break;
|
|
}
|
|
}
|
|
else if(str_comp(pMenuMap, "rand") == 0)
|
|
{
|
|
//make sure to load themes
|
|
std::vector<CTheme> &vThemesRef = GetThemes();
|
|
int RandomThemeIndex = rand() % (vThemesRef.size() - PREDEFINED_THEMES_COUNT);
|
|
if(RandomThemeIndex + PREDEFINED_THEMES_COUNT < (int)vThemesRef.size())
|
|
pMenuMap = vThemesRef[RandomThemeIndex + PREDEFINED_THEMES_COUNT].m_Name.c_str();
|
|
}
|
|
|
|
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_TIME_CHECKPOINT_FIRST && Index <= TILE_TIME_CHECKPOINT_LAST)
|
|
{
|
|
int ArrayIndex = clamp<int>((Index - TILE_TIME_CHECKPOINT_FIRST), 0, NUM_POS);
|
|
m_aPositions[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);
|
|
|
|
float DistToCenter = distance(m_Camera.m_Center, m_RotationCenter);
|
|
if(!m_ChangedPosition && absolute(DistToCenter - (float)g_Config.m_ClRotationRadius) <= 0.5f)
|
|
{
|
|
// 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
|
|
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;
|
|
float Distance = distance(m_AnimationStartPos, TargetPos);
|
|
if(Distance > 0.001f)
|
|
Dir = normalize(m_AnimationStartPos - TargetPos);
|
|
else
|
|
Dir = vec2(1, 0);
|
|
|
|
// 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);
|
|
if(m_CurrentPosition < 0)
|
|
{
|
|
m_AnimationStartPos = m_Camera.m_Center;
|
|
m_MoveTime = 0.0f;
|
|
}
|
|
|
|
m_ChangedPosition = false;
|
|
}
|
|
|
|
CMapLayers::OnRender();
|
|
|
|
m_CurrentPosition = -1;
|
|
|
|
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;
|
|
}
|
|
|
|
m_ChangedPosition = true;
|
|
}
|
|
m_AnimationStartPos = m_Camera.m_Center;
|
|
m_RotationCenter = m_aPositions[m_CurrentPosition];
|
|
m_MoveTime = 0.0f;
|
|
}
|
|
|
|
std::vector<CTheme> &CMenuBackground::GetThemes()
|
|
{
|
|
if(m_vThemes.empty()) // not loaded yet
|
|
{
|
|
// when adding more here, make sure to change the value of PREDEFINED_THEMES_COUNT too
|
|
m_vThemes.emplace_back("", true, true); // no theme
|
|
m_vThemes.emplace_back("auto", true, true); // auto theme
|
|
m_vThemes.emplace_back("rand", true, true); // random theme
|
|
|
|
m_ThemeScanStartTime = time_get_nanoseconds();
|
|
Storage()->ListDirectory(IStorage::TYPE_ALL, "themes", ThemeScan, (CMenuBackground *)this);
|
|
Storage()->ListDirectory(IStorage::TYPE_ALL, "themes", ThemeIconScan, (CMenuBackground *)this);
|
|
|
|
std::sort(m_vThemes.begin() + PREDEFINED_THEMES_COUNT, m_vThemes.end());
|
|
}
|
|
return m_vThemes;
|
|
}
|