mirror of
https://github.com/ddnet/ddnet.git
synced 2024-10-21 08:18:18 +00:00
6633e9af1d
Add a button to manually reload the entities background map instead of automatically reloading it when it's changed. As the background map was only reloaded every 10 seconds, sometimes changing the background map quickly had no effect. Improve the layout of the DDNet settings menu. Align labels and UI elements and reduce unused empty space.
419 lines
12 KiB
C++
419 lines
12 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;
|
|
|
|
std::array<vec2, CMenuBackground::NUM_POS> GenerateMenuBackgroundPositions()
|
|
{
|
|
std::array<vec2, CMenuBackground::NUM_POS> Positions;
|
|
|
|
Positions[CMenuBackground::POS_START] = vec2(500.0f, 500.0f);
|
|
Positions[CMenuBackground::POS_BROWSER_INTERNET] = vec2(1000.0f, 1000.0f);
|
|
Positions[CMenuBackground::POS_BROWSER_LAN] = vec2(1100.0f, 1000.0f);
|
|
Positions[CMenuBackground::POS_DEMOS] = vec2(900.0f, 100.0f);
|
|
Positions[CMenuBackground::POS_NEWS] = vec2(500.0f, 750.0f);
|
|
Positions[CMenuBackground::POS_BROWSER_FAVORITES] = vec2(1250.0f, 500.0f);
|
|
Positions[CMenuBackground::POS_SETTINGS_LANGUAGE] = vec2(500.0f, 1200.0f);
|
|
Positions[CMenuBackground::POS_SETTINGS_GENERAL] = vec2(500.0f, 1000.0f);
|
|
Positions[CMenuBackground::POS_SETTINGS_PLAYER] = vec2(600.0f, 1000.0f);
|
|
Positions[CMenuBackground::POS_SETTINGS_TEE] = vec2(700.0f, 1000.0f);
|
|
Positions[CMenuBackground::POS_SETTINGS_APPEARANCE] = vec2(200.0f, 1000.0f);
|
|
Positions[CMenuBackground::POS_SETTINGS_CONTROLS] = vec2(800.0f, 1000.0f);
|
|
Positions[CMenuBackground::POS_SETTINGS_GRAPHICS] = vec2(900.0f, 1000.0f);
|
|
Positions[CMenuBackground::POS_SETTINGS_SOUND] = vec2(1000.0f, 1000.0f);
|
|
Positions[CMenuBackground::POS_SETTINGS_DDNET] = vec2(1200.0f, 200.0f);
|
|
Positions[CMenuBackground::POS_SETTINGS_ASSETS] = vec2(500.0f, 500.0f);
|
|
for(int i = 0; i < CMenuBackground::POS_BROWSER_CUSTOM_NUM; ++i)
|
|
Positions[CMenuBackground::POS_BROWSER_CUSTOM0 + i] = vec2(500.0f + (75.0f * (float)i), 650.0f - (75.0f * (float)i));
|
|
for(int i = 0; i < CMenuBackground::POS_SETTINGS_RESERVED_NUM; ++i)
|
|
Positions[CMenuBackground::POS_SETTINGS_RESERVED0 + i] = vec2(0, 0);
|
|
for(int i = 0; i < CMenuBackground::POS_RESERVED_NUM; ++i)
|
|
Positions[CMenuBackground::POS_RESERVED0 + i] = vec2(0, 0);
|
|
|
|
return Positions;
|
|
}
|
|
|
|
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 = GenerateMenuBackgroundPositions();
|
|
}
|
|
|
|
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();
|
|
|
|
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
|
|
const std::vector<CTheme> &vThemesRef = GetThemes();
|
|
if(vThemesRef.size() > PREDEFINED_THEMES_COUNT)
|
|
{
|
|
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);
|
|
|
|
CMapLayers::OnMapLoad();
|
|
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)
|
|
{
|
|
for(int y = 0; y < pTLayer->m_Height; ++y)
|
|
{
|
|
for(int 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = std::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;
|
|
}
|