Compare commits

...

14 commits

Author SHA1 Message Date
Marmare314 54a34151f2
Merge b9cb2d2e00 into bf5add67ec 2024-09-13 19:14:01 +02:00
Dennis Felsing bf5add67ec
Merge pull request #8940 from Robyt3/Client-Skin07-Settings-Refactoring
Rework 0.7 tee settings layout and code
2024-09-13 15:37:01 +00:00
Dennis Felsing a09ce576ac
Merge pull request #8907 from KebsCS/pr-hotreload-dummy
Fix dummy disconnecting on hot reload
2024-09-13 15:36:05 +00:00
Robert Müller 6af07a78b3 Rework 0.7 tee settings layout and code
- Show Player/Dummy tabs at the top instead of using checkbox for dummy settings like in regular skin settings.
- Show Basic/Custom tabs instead of using button to toggle custom skin settings.
- Move Skin directory and Refresh icon buttons to right side like in regular skin settings. Refreshing 0.7 skins and parts is not implemented yet though.
- Render 0.7 skin entries like entries of the regular skin list, i.e. with more space and with only four skins per row.
- Move the Random skin button next to the Custom colors checkbox and make it an icon button like in the regular skin settings.
- Move skin preview to the right side of the Player/Dummy/Basic/Custom tabs to reduce unused empty space.
- Remove most overlapping and unnecessarily dark backgrounds.
- Improve layout of skin part tabs by only using colors for the first and last buttons.
- Localize skin part tab names again but properly capitalize the names being shown in the UI.
2024-09-13 17:01:41 +02:00
KebsCS 1477b8c69e
Fix dummy disconnecting on hot reload 2024-09-13 16:53:06 +02:00
Dennis Felsing 8d431f8feb
Merge pull request #8934 from sjrc6/pr_freeze_stars
Add client setting to restore old freeze stars
2024-09-12 12:31:54 +00:00
Tater 80e2de13da Add client side freeze stars 2024-09-12 04:48:46 -05:00
Dennis Felsing 66fb5d5d7f
Merge pull request #8933 from Robyt3/UI-EditText-Searchable
Extract `CUi::DoEditBox_Search` function for quick search
2024-09-11 21:55:25 +00:00
Robert Müller d3f0c2a156 Extract CUi::DoEditBox_Search function for quick search
Reduce duplicate and inconsistent code for rendering quick search for the demo browser, ingame vote list, player flag, skin, skin 0.7 and asset search.

The quick search and exclude in the server browser are not refactored, as they have additional labels and different alignment, which would make a general function complicated.
2024-09-11 21:33:08 +02:00
Jupeyy d5e81ca78d
Merge pull request #8932 from Robyt3/Client-Network-Before-Graphics
Initialize client networking before graphics
2024-09-11 18:07:20 +00:00
Robert Müller 128ffd2313 Initialize client networking before graphics
Avoid Vulkan crash if the backend is destroyed immediately after being created.

Slightly decreases time of initial black screen before loading menu is rendered.
2024-09-11 19:34:31 +02:00
Dennis Felsing af1b32d296
Merge pull request #8930 from timakro/pr-fix-econ-listening-publicly
Fixes a bug where econ was exposed publicly when ec_bindaddr was set to localhost
2024-09-11 16:50:42 +00:00
Tim Schumacher 0ad1c08c22 Fixes a bug where econ was exposed publicly when ec_bindaddr was set to localhost
Also implements the original intention of 85f5e9c that is to disable
econ if ec_binaddr is invalid.
2024-09-11 18:28:07 +02:00
marmare314 b9cb2d2e00 Add Include command to automapper 2024-07-24 17:50:13 +02:00
20 changed files with 478 additions and 498 deletions

View file

@ -762,6 +762,8 @@ void CClient::DummyDisconnect(const char *pReason)
m_aReceivedSnapshots[1] = 0;
m_DummyConnected = false;
m_DummyConnecting = false;
m_DummyReconnectOnReload = false;
m_DummyDeactivateOnReconnect = false;
GameClient()->OnDummyDisconnect();
}
@ -1520,7 +1522,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
}
}
if(m_DummyConnected)
if(m_DummyConnected && !m_DummyReconnectOnReload)
{
DummyDisconnect(0);
}
@ -1649,9 +1651,25 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
}
}
}
else if(Conn == CONN_MAIN && (pPacket->m_Flags & NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_MAP_RELOAD)
{
if(m_DummyConnected)
{
m_DummyReconnectOnReload = true;
m_DummyDeactivateOnReconnect = g_Config.m_ClDummy == 0;
g_Config.m_ClDummy = 0;
}
else
m_DummyDeactivateOnReconnect = false;
}
else if(Conn == CONN_MAIN && (pPacket->m_Flags & NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_CON_READY)
{
GameClient()->OnConnected();
if(m_DummyReconnectOnReload)
{
m_DummySendConnInfo = true;
m_DummyReconnectOnReload = false;
}
}
else if(Conn == CONN_DUMMY && Msg == NETMSG_CON_READY)
{
@ -1659,7 +1677,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
m_DummyConnecting = false;
g_Config.m_ClDummy = 1;
Rcon("crashmeplx");
if(m_aRconAuthed[0])
if(m_aRconAuthed[0] && !m_aRconAuthed[1])
RconAuth(m_aRconUsername, m_aRconPassword);
}
else if(Msg == NETMSG_PING)
@ -2746,6 +2764,16 @@ void CClient::Update()
}
}
if(m_DummyDeactivateOnReconnect && g_Config.m_ClDummy == 1)
{
m_DummyDeactivateOnReconnect = false;
g_Config.m_ClDummy = 0;
}
else if(!m_DummyConnected && m_DummyDeactivateOnReconnect)
{
m_DummyDeactivateOnReconnect = false;
}
m_LastDummy = (bool)g_Config.m_ClDummy;
}
@ -2929,6 +2957,24 @@ void CClient::Run()
g_UuidManager.DebugDump();
}
#ifndef CONF_WEBASM
char aNetworkError[256];
if(!InitNetworkClient(aNetworkError, sizeof(aNetworkError)))
{
log_error("client", "%s", aNetworkError);
ShowMessageBox("Network Error", aNetworkError);
return;
}
#endif
if(!m_Http.Init(std::chrono::seconds{1}))
{
const char *pErrorMessage = "Failed to initialize the HTTP client.";
log_error("client", "%s", pErrorMessage);
ShowMessageBox("HTTP Error", pErrorMessage);
return;
}
// init graphics
m_pGraphics = CreateEngineGraphicsThreaded();
Kernel()->RegisterInterface(m_pGraphics); // IEngineGraphics
@ -2952,24 +2998,6 @@ void CClient::Run()
CVideo::Init();
#endif
#ifndef CONF_WEBASM
char aNetworkError[256];
if(!InitNetworkClient(aNetworkError, sizeof(aNetworkError)))
{
log_error("client", "%s", aNetworkError);
ShowMessageBox("Network Error", aNetworkError);
return;
}
#endif
if(!m_Http.Init(std::chrono::seconds{1}))
{
const char *pErrorMessage = "Failed to initialize the HTTP client.";
log_error("client", "%s", pErrorMessage);
ShowMessageBox("HTTP Error", pErrorMessage);
return;
}
// init text render
m_pTextRender = Kernel()->RequestInterface<IEngineTextRender>();
m_pTextRender->Init();

View file

@ -182,6 +182,8 @@ class CClient : public IClient, public CDemoPlayer::IListener
bool m_DummyConnecting = false;
bool m_DummyConnected = false;
float m_LastDummyConnectTime = 0.0f;
bool m_DummyReconnectOnReload = false;
bool m_DummyDeactivateOnReconnect = false;
// graphs
CGraph m_InputtimeMarginGraph;

View file

@ -243,6 +243,7 @@ CServer::CServer()
}
m_MapReload = false;
m_SameMapReload = false;
m_ReloadedWhenEmpty = false;
m_aCurrentMap[0] = '\0';
@ -1269,6 +1270,12 @@ void CServer::SendMapData(int ClientId, int Chunk)
}
}
void CServer::SendMapReload(int ClientId)
{
CMsgPacker Msg(NETMSG_MAP_RELOAD, true);
SendMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_FLUSH, ClientId);
}
void CServer::SendConnectionReady(int ClientId)
{
CMsgPacker Msg(NETMSG_CON_READY, true);
@ -2553,12 +2560,13 @@ void CServer::ChangeMap(const char *pMap)
void CServer::ReloadMap()
{
m_MapReload = true;
m_SameMapReload = true;
}
int CServer::LoadMap(const char *pMapName)
{
m_MapReload = false;
m_SameMapReload = false;
char aBuf[IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName);
@ -2805,8 +2813,9 @@ int CServer::Run()
int NewTicks = 0;
// load new map
if(m_MapReload || m_CurrentGameTick >= MAX_TICK) // force reload to make sure the ticks stay within a valid range
if(m_MapReload || m_SameMapReload || m_CurrentGameTick >= MAX_TICK) // force reload to make sure the ticks stay within a valid range
{
const bool SameMapReload = m_SameMapReload;
// load map
if(LoadMap(Config()->m_SvMap))
{
@ -2831,6 +2840,9 @@ int CServer::Run()
if(m_aClients[ClientId].m_State <= CClient::STATE_AUTH)
continue;
if(SameMapReload)
SendMapReload(ClientId);
SendMap(ClientId);
bool HasPersistentData = m_aClients[ClientId].m_HasPersistentData;
m_aClients[ClientId].Reset();
@ -3509,7 +3521,7 @@ void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser)
void CServer::ConMapReload(IConsole::IResult *pResult, void *pUser)
{
((CServer *)pUser)->m_MapReload = true;
((CServer *)pUser)->ReloadMap();
}
void CServer::ConLogout(IConsole::IResult *pResult, void *pUser)

View file

@ -219,6 +219,7 @@ public:
int m_RunServer;
bool m_MapReload;
bool m_SameMapReload;
bool m_ReloadedWhenEmpty;
int m_RconClientId;
int m_RconAuthLevel;
@ -324,6 +325,7 @@ public:
void SendCapabilities(int ClientId);
void SendMap(int ClientId);
void SendMapData(int ClientId, int Chunk);
void SendMapReload(int ClientId);
void SendConnectionReady(int ClientId);
void SendRconLine(int ClientId, const char *pLine);
// Accepts -1 as ClientId to mean "all clients with at least auth level admin"

View file

@ -73,6 +73,7 @@ MACRO_CONFIG_INT(ClShowfps, cl_showfps, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE,
MACRO_CONFIG_INT(ClShowpred, cl_showpred, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ingame prediction time in milliseconds")
MACRO_CONFIG_INT(ClEyeWheel, cl_eye_wheel, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show eye wheel along together with emotes")
MACRO_CONFIG_INT(ClEyeDuration, cl_eye_duration, 999999, 1, 999999, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How long the eyes emotes last")
MACRO_CONFIG_INT(ClFreezeStars, cl_freeze_stars, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show old star particles for frozen tees")
MACRO_CONFIG_INT(ClAirjumpindicator, cl_airjumpindicator, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show the air jump indicator")
MACRO_CONFIG_INT(ClThreadsoundloading, cl_threadsoundloading, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Load sound files threaded")

View file

@ -70,18 +70,18 @@ void CEcon::Init(CConfig *pConfig, IConsole *pConsole, CNetBan *pNetBan)
}
NETADDR BindAddr;
if(g_Config.m_EcBindaddr[0] == '\0')
if(g_Config.m_EcBindaddr[0] && net_host_lookup(g_Config.m_EcBindaddr, &BindAddr, NETTYPE_ALL) == 0)
{
mem_zero(&BindAddr, sizeof(BindAddr));
// got bindaddr
BindAddr.port = g_Config.m_EcPort;
}
else if(net_host_lookup(g_Config.m_EcBindaddr, &BindAddr, NETTYPE_ALL) != 0)
else
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "The configured bindaddr '%s' cannot be resolved.", g_Config.m_Bindaddr);
str_format(aBuf, sizeof(aBuf), "The configured bindaddr '%s' cannot be resolved.", g_Config.m_EcBindaddr);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "econ", aBuf);
return;
}
BindAddr.type = NETTYPE_ALL;
BindAddr.port = g_Config.m_EcPort;
if(m_NetConsole.Open(BindAddr, pNetBan))
{

View file

@ -35,3 +35,4 @@ UUID(NETMSG_CHECKSUM_ERROR, "checksum-error@ddnet.tw")
UUID(NETMSG_REDIRECT, "redirect@ddnet.org")
UUID(NETMSG_RCON_CMD_GROUP_START, "rcon-cmd-group-start@ddnet.org")
UUID(NETMSG_RCON_CMD_GROUP_END, "rcon-cmd-group-end@ddnet.org")
UUID(NETMSG_MAP_RELOAD, "map-reload@ddnet.org")

View file

@ -248,6 +248,7 @@ protected:
int m_SettingPlayerPage;
// 0.7 skins
bool m_CustomSkinMenu = false;
int m_TeePartSelected = protocol7::SKINPART_BODY;
const CSkins7::CSkin *m_pSelectedSkin = nullptr;
CLineInputBuffered<protocol7::MAX_SKIN_ARRAY_SIZE, protocol7::MAX_SKIN_LENGTH> m_SkinNameInput;
@ -596,7 +597,6 @@ protected:
void RenderSettingsTee(CUIRect MainView);
void RenderSettingsTee7(CUIRect MainView);
void RenderSettingsTeeCustom7(CUIRect MainView);
void RenderSettingsTeeBasic7(CUIRect MainView);
void RenderSkinSelection7(CUIRect MainView);
void RenderSkinPartSelection7(CUIRect MainView);
void RenderSettingsControls(CUIRect MainView);

View file

@ -1397,22 +1397,10 @@ void CMenus::RenderDemoBrowserButtons(CUIRect ButtonsView, bool WasListboxItemAc
// quick search
{
SetIconMode(true);
CUIRect DemoSearch, SearchIcon;
CUIRect DemoSearch;
ButtonBarTop.VSplitLeft(ButtonBarBottom.h * 21.0f, &DemoSearch, &ButtonBarTop);
ButtonBarTop.VSplitLeft(ButtonBarTop.h / 2.0f, nullptr, &ButtonBarTop);
DemoSearch.VSplitLeft(TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS), &SearchIcon, &DemoSearch);
DemoSearch.VSplitLeft(5.0f, nullptr, &DemoSearch);
Ui()->DoLabel(&SearchIcon, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
SetIconMode(false);
m_DemoSearchInput.SetEmptyText(Localize("Search"));
if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
{
Ui()->SetActiveItem(&m_DemoSearchInput);
m_DemoSearchInput.SelectAll();
}
if(Ui()->DoClearableEditBox(&m_DemoSearchInput, &DemoSearch, 12.0f))
if(Ui()->DoEditBox_Search(&m_DemoSearchInput, &DemoSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()))
{
RefreshFilteredDemos();
DemolistOnUpdate(false);

View file

@ -683,26 +683,15 @@ void CMenus::RenderServerControl(CUIRect MainView)
// render quick search
CUIRect QuickSearch;
Bottom.VSplitLeft(5.0f, 0, &Bottom);
Bottom.VSplitLeft(5.0f, nullptr, &Bottom);
Bottom.VSplitLeft(250.0f, &QuickSearch, &Bottom);
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f);
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
QuickSearch.VSplitLeft(SearchWidth, 0, &QuickSearch);
QuickSearch.VSplitLeft(5.0f, 0, &QuickSearch);
if(m_ControlPageOpening || (Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed()))
if(m_ControlPageOpening)
{
Ui()->SetActiveItem(&m_FilterInput);
m_ControlPageOpening = false;
Ui()->SetActiveItem(&m_FilterInput);
m_FilterInput.SelectAll();
}
m_FilterInput.SetEmptyText(Localize("Search"));
Ui()->DoClearableEditBox(&m_FilterInput, &QuickSearch, 14.0f);
Ui()->DoEditBox_Search(&m_FilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed());
// call vote
Bottom.VSplitRight(10.0f, &Bottom, 0);

View file

@ -257,7 +257,7 @@ void CMenus::SetNeedSendInfo()
void CMenus::RenderSettingsPlayer(CUIRect MainView)
{
CUIRect TabBar, PlayerTab, DummyTab, ChangeInfo, QuickSearch, QuickSearchClearButton;
CUIRect TabBar, PlayerTab, DummyTab, ChangeInfo, QuickSearch;
MainView.HSplitTop(20.0f, &TabBar, &MainView);
TabBar.VSplitMid(&TabBar, &ChangeInfo, 20.f);
TabBar.VSplitMid(&PlayerTab, &DummyTab);
@ -340,7 +340,10 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView)
}
MainView.HSplitTop(10.0f, nullptr, &MainView);
MainView.HSplitBottom(25.0f, &MainView, &QuickSearch);
MainView.HSplitBottom(20.0f, &MainView, &QuickSearch);
MainView.HSplitBottom(5.0f, &MainView, nullptr);
QuickSearch.VSplitLeft(220.0f, &QuickSearch, nullptr);
int OldSelected = -1;
static CListBox s_ListBox;
s_ListBox.DoStart(48.0f, vpFilteredFlags.size(), 10, 3, OldSelected, &MainView);
@ -378,30 +381,7 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView)
SetNeedSendInfo();
}
// render quick search
QuickSearch.VSplitLeft(240.0f, &QuickSearch, nullptr);
QuickSearch.HSplitTop(5.0f, nullptr, &QuickSearch);
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f);
QuickSearch.VSplitLeft(SearchWidth - 1.5f, nullptr, &QuickSearch);
QuickSearch.VSplitLeft(5.0f, nullptr, &QuickSearch);
QuickSearch.VSplitLeft(QuickSearch.w - 10.0f, &QuickSearch, &QuickSearchClearButton);
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
{
Ui()->SetActiveItem(&s_FlagFilterInput);
s_FlagFilterInput.SelectAll();
}
s_FlagFilterInput.SetEmptyText(Localize("Search"));
Ui()->DoClearableEditBox(&s_FlagFilterInput, &QuickSearch, 14.0f);
Ui()->DoEditBox_Search(&s_FlagFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed());
}
struct CUISkin
@ -770,8 +750,8 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
CUIRect QuickSearch, DatabaseButton, DirectoryButton, RefreshButton;
MainView.HSplitBottom(20.0f, &MainView, &QuickSearch);
MainView.HSplitBottom(5.0f, &MainView, nullptr);
QuickSearch.VSplitLeft(240.0f, &QuickSearch, &DatabaseButton);
QuickSearch.VSplitRight(10.0f, &QuickSearch, nullptr);
QuickSearch.VSplitLeft(220.0f, &QuickSearch, &DatabaseButton);
DatabaseButton.VSplitLeft(10.0f, nullptr, &DatabaseButton);
DatabaseButton.VSplitLeft(150.0f, &DatabaseButton, &DirectoryButton);
DirectoryButton.VSplitRight(175.0f, nullptr, &DirectoryButton);
DirectoryButton.VSplitRight(25.0f, &DirectoryButton, &RefreshButton);
@ -904,23 +884,9 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
SetNeedSendInfo();
}
// Quick search
{
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f);
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
QuickSearch.VSplitLeft(SearchWidth + 5.0f, nullptr, &QuickSearch);
static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString));
if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
if(Ui()->DoEditBox_Search(&s_SkinFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()))
{
Ui()->SetActiveItem(&s_SkinFilterInput);
s_SkinFilterInput.SelectAll();
}
s_SkinFilterInput.SetEmptyText(Localize("Search"));
if(Ui()->DoClearableEditBox(&s_SkinFilterInput, &QuickSearch, 14.0f))
m_SkinListNeedsUpdate = true;
}

View file

@ -31,71 +31,74 @@
#include <vector>
using namespace FontIcons;
void CMenus::RenderSettingsTee7(CUIRect MainView)
{
static bool s_CustomSkinMenu = false;
// static int s_PlayerCountry = 0;
// static char s_aPlayerName[64] = {0};
// static char s_aPlayerClan[64] = {0};
CUIRect SkinPreview, NormalSkinPreview, RedTeamSkinPreview, BlueTeamSkinPreview, Buttons, QuickSearch, DirectoryButton, RefreshButton, SaveDeleteButton, TabBars, TabBar, LeftTab, RightTab;
MainView.HSplitBottom(20.0f, &MainView, &Buttons);
MainView.HSplitBottom(5.0f, &MainView, nullptr);
Buttons.VSplitRight(25.0f, &Buttons, &RefreshButton);
Buttons.VSplitRight(10.0f, &Buttons, nullptr);
Buttons.VSplitRight(140.0f, &Buttons, &DirectoryButton);
Buttons.VSplitLeft(220.0f, &QuickSearch, &Buttons);
Buttons.VSplitLeft(10.0f, nullptr, &Buttons);
Buttons.VSplitLeft(120.0f, &SaveDeleteButton, &Buttons);
MainView.HSplitTop(50.0f, &TabBars, &MainView);
MainView.HSplitTop(10.0f, nullptr, &MainView);
TabBars.VSplitMid(&TabBars, &SkinPreview, 20.0f);
// if(m_pClient->m_IdentityState < 0)
// {
// s_PlayerCountry = Config()->m_PlayerCountry;
// str_copy(s_aPlayerName, Config()->m_PlayerName, sizeof(s_aPlayerName));
// str_copy(s_aPlayerClan, Config()->m_PlayerClan, sizeof(s_aPlayerClan));
// m_pClient->m_IdentityState = 0;
// }
TabBars.HSplitTop(20.0f, &TabBar, &TabBars);
TabBar.VSplitMid(&LeftTab, &RightTab);
TabBars.HSplitTop(10.0f, nullptr, &TabBars);
CUIRect Label, TopView, BottomView, Left, Right;
SkinPreview.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f), IGraphics::CORNER_ALL, 5.0f);
SkinPreview.VMargin(10.0f, &SkinPreview);
SkinPreview.VSplitRight(50.0f, &SkinPreview, &BlueTeamSkinPreview);
SkinPreview.VSplitRight(10.0f, &SkinPreview, nullptr);
SkinPreview.VSplitRight(50.0f, &SkinPreview, &RedTeamSkinPreview);
SkinPreview.VSplitRight(10.0f, &SkinPreview, nullptr);
SkinPreview.VSplitRight(50.0f, &SkinPreview, &NormalSkinPreview);
SkinPreview.VSplitRight(10.0f, &SkinPreview, nullptr);
// cut view
MainView.HSplitBottom(40.0f, &MainView, &BottomView);
BottomView.HSplitTop(20.f, 0, &BottomView);
CUIRect QuickSearch, DirectoryButton, Buttons;
CUIRect ButtonLeft, ButtonMiddle, ButtonRight;
BottomView.VSplitMid(&QuickSearch, &Buttons, 10.0f);
QuickSearch.VSplitLeft(240.0f, &QuickSearch, &DirectoryButton);
QuickSearch.VSplitRight(10.0f, &QuickSearch, nullptr);
const float ButtonSize = Buttons.w / 3;
Buttons.VSplitLeft(ButtonSize, &ButtonLeft, &Buttons);
Buttons.VSplitLeft(ButtonSize, &ButtonMiddle, &Buttons);
Buttons.VSplitLeft(ButtonSize, &ButtonRight, &Buttons);
// render skin preview background
const float SpacingH = 2.0f;
const float SpacingW = 3.0f;
const float ButtonHeight = 20.0f;
const float SkinHeight = 50.0f;
const float BackgroundHeight = (ButtonHeight + SpacingH) + SkinHeight * 2;
MainView.HSplitTop(20.0f, 0, &MainView);
MainView.HSplitTop(BackgroundHeight, &TopView, &MainView);
TopView.VSplitMid(&Left, &Right, 3.0f);
Left.Draw(vec4(0.0f, 0.0f, 0.0f, 0.25f), 15, 5.0F);
Right.Draw(vec4(0.0f, 0.0f, 0.0f, 0.25f), 15, 5.0F);
Left.HSplitTop(ButtonHeight, &Label, &Left);
Ui()->DoLabel(&Label, Localize("Tee"), ButtonHeight * CUi::ms_FontmodHeight * 0.8f, TEXTALIGN_MC);
// Preview
static CButtonContainer s_PlayerTabButton;
if(DoButton_MenuTab(&s_PlayerTabButton, Localize("Player"), !m_Dummy, &LeftTab, IGraphics::CORNER_L, nullptr, nullptr, nullptr, nullptr, 4.0f))
{
CUIRect Top, Bottom, TeeLeft, TeeRight;
m_Dummy = false;
}
Left.HSplitTop(SpacingH, 0, &Left);
Left.HSplitTop(SkinHeight * 2, &Top, &Left);
static CButtonContainer s_DummyTabButton;
if(DoButton_MenuTab(&s_DummyTabButton, Localize("Dummy"), m_Dummy, &RightTab, IGraphics::CORNER_R, nullptr, nullptr, nullptr, nullptr, 4.0f))
{
m_Dummy = true;
}
// split the menu in 2 parts
Top.HSplitMid(&Top, &Bottom, SpacingH);
TabBars.HSplitTop(20.0f, &TabBar, &TabBars);
TabBar.VSplitMid(&LeftTab, &RightTab);
// handle left
static CButtonContainer s_BasicTabButton;
if(DoButton_MenuTab(&s_BasicTabButton, Localize("Basic"), !m_CustomSkinMenu, &LeftTab, IGraphics::CORNER_L, nullptr, nullptr, nullptr, nullptr, 4.0f))
{
m_CustomSkinMenu = false;
}
static CButtonContainer s_CustomTabButton;
if(DoButton_MenuTab(&s_CustomTabButton, Localize("Custom"), m_CustomSkinMenu, &RightTab, IGraphics::CORNER_R, nullptr, nullptr, nullptr, nullptr, 4.0f))
{
m_CustomSkinMenu = true;
if(m_CustomSkinMenu && m_pSelectedSkin)
{
if(m_pSelectedSkin->m_Flags & CSkins7::SKINFLAG_STANDARD)
{
m_SkinNameInput.Set("copy_");
m_SkinNameInput.Append(m_pSelectedSkin->m_aName);
}
else
m_SkinNameInput.Set(m_pSelectedSkin->m_aName);
}
}
// validate skin parts for solo mode
CTeeRenderInfo OwnSkinInfo;
OwnSkinInfo.m_Size = 50.0f;
char aSkinParts[protocol7::NUM_SKINPARTS][protocol7::MAX_SKIN_ARRAY_SIZE];
char *apSkinPartsPtr[protocol7::NUM_SKINPARTS];
int aUCCVars[protocol7::NUM_SKINPARTS];
@ -107,9 +110,10 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
aUCCVars[Part] = *CSkins7::ms_apUCCVariables[(int)m_Dummy][Part];
aColorVars[Part] = *CSkins7::ms_apColorVariables[(int)m_Dummy][Part];
}
m_pClient->m_Skins7.ValidateSkinParts(apSkinPartsPtr, aUCCVars, aColorVars, 0);
CTeeRenderInfo OwnSkinInfo;
OwnSkinInfo.m_Size = 50.0f;
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
int SkinPart = m_pClient->m_Skins7.FindSkinPart(Part, apSkinPartsPtr[Part], false);
@ -126,18 +130,13 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
}
}
// draw preview
Top.Draw(vec4(0.0f, 0.0f, 0.0f, 0.25f), 15, 5.0F);
Top.VSplitLeft(Top.w / 3.0f + SpacingW / 2.0f, &Label, &Top);
Label.y += 17.0f;
Ui()->DoLabel(&Label, Localize("Normal:"), ButtonHeight * CUi::ms_FontmodHeight * 0.8f, TEXTALIGN_CENTER);
Top.Draw(vec4(0.0f, 0.0f, 0.0f, 0.25f), 15, 5.0F);
char aBuf[128 + IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "%s:", Localize("Your skin"));
Ui()->DoLabel(&SkinPreview, aBuf, 14.0f, TEXTALIGN_ML);
{
// interactive tee: tee looking towards cursor, and it is happy when you touch it
const vec2 TeePosition = vec2(Top.x + Top.w / 2.0f, Top.y + Top.h / 2.0f + 6.0f);
const vec2 TeePosition = NormalSkinPreview.Center() + vec2(0.0f, 6.0f);
const vec2 DeltaPosition = Ui()->MousePos() - TeePosition;
const float Distance = length(DeltaPosition);
const float InteractionDistance = 20.0f;
@ -145,17 +144,13 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
const int TeeEmote = Distance < InteractionDistance ? EMOTE_HAPPY : EMOTE_NORMAL;
RenderTools()->RenderTee(CAnimState::GetIdle(), &OwnSkinInfo, TeeEmote, TeeDirection, TeePosition);
static char s_InteractiveTeeButtonId;
if(Distance < InteractionDistance && Ui()->DoButtonLogic(&s_InteractiveTeeButtonId, 0, &Top))
if(Distance < InteractionDistance && Ui()->DoButtonLogic(&s_InteractiveTeeButtonId, 0, &NormalSkinPreview))
{
m_pClient->m_Sounds.Play(CSounds::CHN_GUI, SOUND_PLAYER_SPAWN, 1.0f);
}
}
// handle right (team skins)
// validate skin parts for team game mode
CTeeRenderInfo TeamSkinInfo = OwnSkinInfo;
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
str_copy(aSkinParts[Part], CSkins7::ms_apSkinVariables[(int)m_Dummy][Part], protocol7::MAX_SKIN_ARRAY_SIZE);
@ -163,9 +158,9 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
aUCCVars[Part] = *CSkins7::ms_apUCCVariables[(int)m_Dummy][Part];
aColorVars[Part] = *CSkins7::ms_apColorVariables[(int)m_Dummy][Part];
}
m_pClient->m_Skins7.ValidateSkinParts(apSkinPartsPtr, aUCCVars, aColorVars, GAMEFLAG_TEAMS);
CTeeRenderInfo TeamSkinInfo = OwnSkinInfo;
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
int SkinPart = m_pClient->m_Skins7.FindSkinPart(Part, apSkinPartsPtr[Part], false);
@ -182,135 +177,71 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
}
}
// draw preview
Bottom.Draw(vec4(0.0f, 0.0f, 0.0f, 0.25f), 15, 5.0F);
Bottom.VSplitLeft(Bottom.w / 3.0f + SpacingW / 2.0f, &Label, &Bottom);
Label.y += 17.0f;
Ui()->DoLabel(&Label, Localize("Team:"), ButtonHeight * CUi::ms_FontmodHeight * 0.8f, TEXTALIGN_CENTER);
Bottom.VSplitMid(&TeeLeft, &TeeRight, SpacingW);
TeeLeft.Draw(vec4(0.0f, 0.0f, 0.0f, 0.25f), 15, 5.0F);
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = m_pClient->m_Skins7.GetTeamColor(aUCCVars[Part], aColorVars[Part], TEAM_RED, Part);
}
RenderTools()->RenderTee(CAnimState::GetIdle(), &TeamSkinInfo, 0, vec2(1, 0), RedTeamSkinPreview.Center() + vec2(0.0f, 6.0f));
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
ColorRGBA TeamColor = m_pClient->m_Skins7.GetTeamColor(aUCCVars[Part], aColorVars[Part], TEAM_RED, Part);
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = TeamColor;
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = m_pClient->m_Skins7.GetTeamColor(aUCCVars[Part], aColorVars[Part], TEAM_BLUE, Part);
}
RenderTools()->RenderTee(CAnimState::GetIdle(), &TeamSkinInfo, 0, vec2(1, 0), vec2(TeeLeft.x + TeeLeft.w / 2.0f, TeeLeft.y + TeeLeft.h / 2.0f + 6.0f));
RenderTools()->RenderTee(CAnimState::GetIdle(), &TeamSkinInfo, 0, vec2(-1, 0), BlueTeamSkinPreview.Center() + vec2(0.0f, 6.0f));
TeeRight.Draw(vec4(0.0f, 0.0f, 0.0f, 0.25f), 15, 5.0F);
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
ColorRGBA TeamColor = m_pClient->m_Skins7.GetTeamColor(aUCCVars[Part], aColorVars[Part], TEAM_BLUE, Part);
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = TeamColor;
}
RenderTools()->RenderTee(CAnimState::GetIdle(), &TeamSkinInfo, 0, vec2(-1, 0), vec2(TeeRight.x + TeeRight.w / 2.0f, TeeRight.y + TeeRight.h / 2.0f + 6.0f));
}
Right.HSplitTop(ButtonHeight, &Label, &Right);
Ui()->DoLabel(&Label, Localize("Settings"), ButtonHeight * CUi::ms_FontmodHeight * 0.8f, TEXTALIGN_MC);
// Settings
{
CUIRect Top, Bottom, Dummy, DummyLabel;
Right.HSplitTop(SpacingH, 0, &Right);
Right.HSplitMid(&Top, &Bottom, SpacingH);
Right.HSplitTop(20.0f, &Dummy, &Right);
Dummy.HSplitTop(20.0f, &DummyLabel, &Dummy);
if(DoButton_CheckBox(&m_Dummy, Localize("Dummy settings"), m_Dummy, &DummyLabel))
{
m_Dummy ^= 1;
}
GameClient()->m_Tooltips.DoToolTip(&m_Dummy, &DummyLabel, Localize("Toggle to edit your dummy settings"));
}
MainView.HSplitTop(10.0f, 0, &MainView);
if(s_CustomSkinMenu)
if(m_CustomSkinMenu)
RenderSettingsTeeCustom7(MainView);
else
RenderSettingsTeeBasic7(MainView);
RenderSkinSelection7(MainView);
// bottom buttons
if(s_CustomSkinMenu)
if(m_CustomSkinMenu)
{
static CButtonContainer s_CustomSkinSaveButton;
if(DoButton_Menu(&s_CustomSkinSaveButton, Localize("Save"), 0, &ButtonLeft))
if(DoButton_Menu(&s_CustomSkinSaveButton, Localize("Save"), 0, &SaveDeleteButton))
{
m_Popup = POPUP_SAVE_SKIN;
m_SkinNameInput.SelectAll();
Ui()->SetActiveItem(&m_SkinNameInput);
}
static CButtonContainer s_RandomizeSkinButton;
if(DoButton_Menu(&s_RandomizeSkinButton, Localize("Randomize"), 0, &ButtonMiddle))
{
m_pClient->m_Skins7.RandomizeSkin(m_Dummy);
Config()->m_ClPlayer7Skin[0] = 0;
SetNeedSendInfo();
}
}
else if(m_pSelectedSkin && (m_pSelectedSkin->m_Flags & CSkins7::SKINFLAG_STANDARD) == 0)
{
static CButtonContainer s_CustomSkinDeleteButton;
if(DoButton_Menu(&s_CustomSkinDeleteButton, Localize("Delete"), 0, &ButtonMiddle) || Ui()->ConsumeHotkey(CUi::HOTKEY_DELETE))
if(DoButton_Menu(&s_CustomSkinDeleteButton, Localize("Delete"), 0, &SaveDeleteButton) || Ui()->ConsumeHotkey(CUi::HOTKEY_DELETE))
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), Localize("Are you sure that you want to delete '%s'?"), m_pSelectedSkin->m_aName);
PopupConfirm(Localize("Delete skin"), aBuf, Localize("Yes"), Localize("No"), &CMenus::PopupConfirmDeleteSkin7);
}
}
static CButtonContainer s_CustomSwitchButton;
if(DoButton_Menu(&s_CustomSwitchButton, s_CustomSkinMenu ? Localize("Basic") : Localize("Custom"), 0, &ButtonRight))
{
s_CustomSkinMenu = !s_CustomSkinMenu;
if(s_CustomSkinMenu && m_pSelectedSkin)
{
if(m_pSelectedSkin->m_Flags & CSkins7::SKINFLAG_STANDARD)
{
m_SkinNameInput.Set("copy_");
m_SkinNameInput.Append(m_pSelectedSkin->m_aName);
}
else
m_SkinNameInput.Set(m_pSelectedSkin->m_aName);
}
}
// Quick search
{
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
Ui()->DoLabel(&QuickSearch, FontIcons::FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
float SearchWidth = TextRender()->TextWidth(14.0f, FontIcons::FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f);
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
QuickSearch.VSplitLeft(SearchWidth + 5.0f, nullptr, &QuickSearch);
static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString));
if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
if(Ui()->DoEditBox_Search(&s_SkinFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()))
{
Ui()->SetActiveItem(&s_SkinFilterInput);
s_SkinFilterInput.SelectAll();
}
s_SkinFilterInput.SetEmptyText(Localize("Search"));
if(Ui()->DoClearableEditBox(&s_SkinFilterInput, &QuickSearch, 14.0f))
m_SkinListNeedsUpdate = true;
}
static CButtonContainer s_DirectoryButton;
if(DoButton_Menu(&s_DirectoryButton, Localize("Skins directory"), 0, &DirectoryButton))
{
char aBuf[128 + IO_MAX_PATH_LENGTH];
Storage()->GetCompletePath(IStorage::TYPE_SAVE, "skins7", aBuf, sizeof(aBuf));
Storage()->CreateFolder("skins7", IStorage::TYPE_SAVE);
Client()->ViewFile(aBuf);
}
GameClient()->m_Tooltips.DoToolTip(&s_DirectoryButton, &DirectoryButton, Localize("Open the directory to add custom skins"));
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
static CButtonContainer s_SkinRefreshButton;
if(DoButton_Menu(&s_SkinRefreshButton, FONT_ICON_ARROW_ROTATE_RIGHT, 0, &RefreshButton) ||
(!Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed() && (Input()->KeyPress(KEY_F5) || (Input()->ModifierIsPressed() && Input()->KeyPress(KEY_R)))))
{
// reset render flags for possible loading screen
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
// TODO: m_pClient->RefreshSkins();
}
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
}
void CMenus::PopupConfirmDeleteSkin7()
@ -325,51 +256,34 @@ void CMenus::PopupConfirmDeleteSkin7()
m_pSelectedSkin = nullptr;
}
void CMenus::RenderSettingsTeeBasic7(CUIRect MainView)
{
RenderSkinSelection7(MainView); // yes thats all here ^^
}
void CMenus::RenderSettingsTeeCustom7(CUIRect MainView)
{
CUIRect Label, Patterns, Button, Left, Right;
CUIRect ButtonBar, SkinPartSelection, CustomColors;
// render skin preview background
float SpacingH = 2.0f;
float SpacingW = 3.0f;
float ButtonHeight = 20.0f;
MainView.HSplitTop(20.0f, &ButtonBar, &MainView);
MainView.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f), IGraphics::CORNER_B, 5.0f);
MainView.VSplitMid(&SkinPartSelection, &CustomColors, 10.0f);
CustomColors.Margin(5.0f, &CustomColors);
CUIRect CustomColorsButton, RandomSkinButton;
CustomColors.HSplitTop(20.0f, &CustomColorsButton, &CustomColors);
CustomColorsButton.VSplitRight(30.0f, &CustomColorsButton, &RandomSkinButton);
CustomColorsButton.VSplitRight(20.0f, &CustomColorsButton, nullptr);
MainView.Draw(vec4(0.0f, 0.0f, 0.0f, 0.25f), 15, 5.0F);
const float ButtonWidth = ButtonBar.w / protocol7::NUM_SKINPARTS;
MainView.HSplitTop(ButtonHeight, &Label, &MainView);
Ui()->DoLabel(&Label, Localize("Customize"), ButtonHeight * CUi::ms_FontmodHeight * 0.8f, TEXTALIGN_MC);
// skin part selection
MainView.HSplitTop(SpacingH, 0, &MainView);
MainView.HSplitTop(ButtonHeight, &Patterns, &MainView);
Patterns.Draw(vec4(0.0f, 0.0f, 0.0f, 0.25f), 15, 5.0F);
float ButtonWidth = (Patterns.w / 6.0f) - (SpacingW * 5.0) / 6.0f;
static CButtonContainer s_aPatternButtons[protocol7::NUM_SKINPARTS];
static CButtonContainer s_aSkinPartButtons[protocol7::NUM_SKINPARTS];
for(int i = 0; i < protocol7::NUM_SKINPARTS; i++)
{
Patterns.VSplitLeft(ButtonWidth, &Button, &Patterns);
if(DoButton_MenuTab(&s_aPatternButtons[i], Localize(CSkins7::ms_apSkinPartNames[i], "skins"), m_TeePartSelected == i, &Button, IGraphics::CORNER_ALL))
CUIRect Button;
ButtonBar.VSplitLeft(ButtonWidth, &Button, &ButtonBar);
const int Corners = i == 0 ? IGraphics::CORNER_TL : (i == (protocol7::NUM_SKINPARTS - 1) ? IGraphics::CORNER_TR : IGraphics::CORNER_NONE);
if(DoButton_MenuTab(&s_aSkinPartButtons[i], Localize(CSkins7::ms_apSkinPartNamesLocalized[i], "skins"), m_TeePartSelected == i, &Button, Corners, nullptr, nullptr, nullptr, nullptr, 4.0f))
{
m_TeePartSelected = i;
}
Patterns.VSplitLeft(SpacingW, 0, &Patterns);
}
MainView.HSplitTop(SpacingH, 0, &MainView);
MainView.VSplitMid(&Left, &Right, SpacingW);
Right.Margin(5.0f, &Right);
RenderSkinPartSelection7(Left);
CUIRect CustomColorsButton;
Right.HSplitTop(20.0f, &CustomColorsButton, &Right);
RenderSkinPartSelection7(SkinPartSelection);
int *pUseCustomColor = CSkins7::ms_apUCCVariables[(int)m_Dummy][m_TeePartSelected];
if(DoButton_CheckBox(pUseCustomColor, Localize("Custom colors"), *pUseCustomColor, &CustomColorsButton))
@ -380,15 +294,32 @@ void CMenus::RenderSettingsTeeCustom7(CUIRect MainView)
if(*pUseCustomColor)
{
CUIRect CustomColors;
Right.HSplitTop(5.0f, nullptr, &Right);
Right.HSplitTop(95.0f, &CustomColors, &Right);
CUIRect CustomColorScrollbars;
CustomColors.HSplitTop(5.0f, nullptr, &CustomColors);
CustomColors.HSplitTop(95.0f, &CustomColorScrollbars, &CustomColors);
if(RenderHslaScrollbars(&CustomColors, CSkins7::ms_apColorVariables[(int)m_Dummy][m_TeePartSelected], m_TeePartSelected == protocol7::SKINPART_MARKING, CSkins7::DARKEST_COLOR_LGT))
if(RenderHslaScrollbars(&CustomColorScrollbars, CSkins7::ms_apColorVariables[(int)m_Dummy][m_TeePartSelected], m_TeePartSelected == protocol7::SKINPART_MARKING, CSkins7::DARKEST_COLOR_LGT))
{
SetNeedSendInfo();
}
}
// Random skin button
static CButtonContainer s_RandomSkinButton;
static const char *s_apDice[] = {FONT_ICON_DICE_ONE, FONT_ICON_DICE_TWO, FONT_ICON_DICE_THREE, FONT_ICON_DICE_FOUR, FONT_ICON_DICE_FIVE, FONT_ICON_DICE_SIX};
static int s_CurrentDie = rand() % std::size(s_apDice);
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
if(DoButton_Menu(&s_RandomSkinButton, s_apDice[s_CurrentDie], 0, &RandomSkinButton, nullptr, IGraphics::CORNER_ALL, 5.0f, -0.2f))
{
m_pClient->m_Skins7.RandomizeSkin(m_Dummy);
Config()->m_ClPlayer7Skin[0] = '\0';
SetNeedSendInfo();
s_CurrentDie = rand() % std::size(s_apDice);
}
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
GameClient()->m_Tooltips.DoToolTip(&s_RandomSkinButton, &RandomSkinButton, Localize("Create a random skin"));
}
void CMenus::RenderSkinSelection7(CUIRect MainView)
@ -403,74 +334,72 @@ void CMenus::RenderSkinSelection7(CUIRect MainView)
s_SkinCount = m_pClient->m_Skins7.Num();
for(int i = 0; i < s_SkinCount; ++i)
{
const CSkins7::CSkin *s = m_pClient->m_Skins7.Get(i);
if(g_Config.m_ClSkinFilterString[0] != '\0' && !str_utf8_find_nocase(s->m_aName, g_Config.m_ClSkinFilterString))
const CSkins7::CSkin *pSkin = m_pClient->m_Skins7.Get(i);
if(g_Config.m_ClSkinFilterString[0] != '\0' && !str_utf8_find_nocase(pSkin->m_aName, g_Config.m_ClSkinFilterString))
continue;
// no special skins
if((s->m_Flags & CSkins7::SKINFLAG_SPECIAL) == 0)
if((pSkin->m_Flags & CSkins7::SKINFLAG_SPECIAL) == 0)
{
s_vpSkinList.emplace_back(s);
s_vpSkinList.emplace_back(pSkin);
}
}
m_SkinListNeedsUpdate = false;
}
m_pSelectedSkin = 0;
m_pSelectedSkin = nullptr;
int s_OldSelected = -1;
s_ListBox.DoStart(60.0f, s_vpSkinList.size(), 10, 1, s_OldSelected, &MainView);
s_ListBox.DoStart(50.0f, s_vpSkinList.size(), 4, 1, s_OldSelected, &MainView);
for(int i = 0; i < (int)s_vpSkinList.size(); ++i)
{
const CSkins7::CSkin *s = s_vpSkinList[i];
if(s == 0)
const CSkins7::CSkin *pSkin = s_vpSkinList[i];
if(pSkin == nullptr)
continue;
if(!str_comp(s->m_aName, Config()->m_ClPlayer7Skin))
if(!str_comp(pSkin->m_aName, Config()->m_ClPlayer7Skin))
{
m_pSelectedSkin = s;
m_pSelectedSkin = pSkin;
s_OldSelected = i;
}
CListboxItem Item = s_ListBox.DoNextItem(&s_vpSkinList[i], s_OldSelected == i);
if(Item.m_Visible)
{
const CListboxItem Item = s_ListBox.DoNextItem(&s_vpSkinList[i], s_OldSelected == i);
if(!Item.m_Visible)
continue;
CUIRect TeePreview, Label;
Item.m_Rect.VSplitLeft(60.0f, &TeePreview, &Label);
CTeeRenderInfo Info;
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
if(s->m_aUseCustomColors[Part])
if(pSkin->m_aUseCustomColors[Part])
{
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = s->m_apParts[Part]->m_ColorTexture;
Info.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = m_pClient->m_Skins7.GetColor(s->m_aPartColors[Part], Part == protocol7::SKINPART_MARKING);
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkin->m_apParts[Part]->m_ColorTexture;
Info.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = m_pClient->m_Skins7.GetColor(pSkin->m_aPartColors[Part], Part == protocol7::SKINPART_MARKING);
}
else
{
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = s->m_apParts[Part]->m_OrgTexture;
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkin->m_apParts[Part]->m_OrgTexture;
Info.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
}
}
Info.m_Size = 50.0f;
Item.m_Rect.HSplitTop(5.0f, 0, &Item.m_Rect); // some margin from the top
{
// interactive tee: tee is happy to be selected
int TeeEmote = (Item.m_Selected && s_LastSelectionTime + 0.75f > Client()->LocalTime()) ? EMOTE_HAPPY : EMOTE_NORMAL;
RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, TeeEmote, vec2(1.0f, 0.0f), vec2(Item.m_Rect.x + Item.m_Rect.w / 2, Item.m_Rect.y + Item.m_Rect.h / 2));
int TeeEmote = (Item.m_Selected && s_LastSelectionTime + 0.75f > Client()->GlobalTime()) ? EMOTE_HAPPY : EMOTE_NORMAL;
RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, TeeEmote, vec2(1.0f, 0.0f), TeePreview.Center() + vec2(0.0f, 6.0f));
}
CUIRect Label;
Item.m_Rect.Margin(5.0f, &Item.m_Rect);
Item.m_Rect.HSplitBottom(10.0f, &Item.m_Rect, &Label);
Ui()->DoLabel(&Label, s->m_aName, 10.0f, TEXTALIGN_MC);
}
SLabelProperties Props;
Props.m_MaxWidth = Label.w - 5.0f;
Ui()->DoLabel(&Label, pSkin->m_aName, 12.0f, TEXTALIGN_ML, Props);
}
const int NewSelected = s_ListBox.DoEnd();
if(NewSelected != -1 && NewSelected != s_OldSelected)
{
s_LastSelectionTime = Client()->LocalTime();
s_LastSelectionTime = Client()->GlobalTime();
m_pSelectedSkin = s_vpSkinList[NewSelected];
str_copy(Config()->m_ClPlayer7Skin, m_pSelectedSkin->m_aName);
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
@ -495,11 +424,11 @@ void CMenus::RenderSkinPartSelection7(CUIRect MainView)
s_paList[Part].clear();
for(int i = 0; i < m_pClient->m_Skins7.NumSkinPart(Part); ++i)
{
const CSkins7::CSkinPart *s = m_pClient->m_Skins7.GetSkinPart(Part, i);
const CSkins7::CSkinPart *pPart = m_pClient->m_Skins7.GetSkinPart(Part, i);
// no special skins
if((s->m_Flags & CSkins7::SKINFLAG_SPECIAL) == 0)
if((pPart->m_Flags & CSkins7::SKINFLAG_SPECIAL) == 0)
{
s_paList[Part].emplace_back(s);
s_paList[Part].emplace_back(pPart);
}
}
}
@ -508,19 +437,24 @@ void CMenus::RenderSkinPartSelection7(CUIRect MainView)
static int s_OldSelected = -1;
s_ListBox.DoBegin(&MainView);
s_ListBox.DoStart(60.0f, s_paList[m_TeePartSelected].size(), 5, 1, s_OldSelected);
s_ListBox.DoStart(72.0f, s_paList[m_TeePartSelected].size(), 4, 1, s_OldSelected, nullptr, false, IGraphics::CORNER_NONE, true);
for(int i = 0; i < (int)s_paList[m_TeePartSelected].size(); ++i)
{
const CSkins7::CSkinPart *s = s_paList[m_TeePartSelected][i];
if(s == 0)
const CSkins7::CSkinPart *pPart = s_paList[m_TeePartSelected][i];
if(pPart == nullptr)
continue;
if(!str_comp(s->m_aName, CSkins7::ms_apSkinVariables[(int)m_Dummy][m_TeePartSelected]))
if(!str_comp(pPart->m_aName, CSkins7::ms_apSkinVariables[(int)m_Dummy][m_TeePartSelected]))
s_OldSelected = i;
CListboxItem Item = s_ListBox.DoNextItem(&s_paList[m_TeePartSelected][i], s_OldSelected == i);
if(Item.m_Visible)
{
if(!Item.m_Visible)
continue;
CUIRect Label;
Item.m_Rect.Margin(5.0f, &Item.m_Rect);
Item.m_Rect.HSplitBottom(12.0f, &Item.m_Rect, &Label);
CTeeRenderInfo Info;
for(int j = 0; j < protocol7::NUM_SKINPARTS; j++)
{
@ -528,25 +462,18 @@ void CMenus::RenderSkinPartSelection7(CUIRect MainView)
const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.GetSkinPart(j, SkinPart);
if(*CSkins7::ms_apUCCVariables[(int)m_Dummy][j])
{
if(m_TeePartSelected == j)
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[j] = s->m_ColorTexture;
else
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[j] = pSkinPart->m_ColorTexture;
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[j] = m_TeePartSelected == j ? pPart->m_ColorTexture : pSkinPart->m_ColorTexture;
Info.m_aSixup[g_Config.m_ClDummy].m_aColors[j] = m_pClient->m_Skins7.GetColor(*CSkins7::ms_apColorVariables[(int)m_Dummy][j], j == protocol7::SKINPART_MARKING);
}
else
{
if(m_TeePartSelected == j)
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[j] = s->m_OrgTexture;
else
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[j] = pSkinPart->m_OrgTexture;
Info.m_aSixup[0].m_aColors[j] = vec4(1.0f, 1.0f, 1.0f, 1.0f);
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[j] = m_TeePartSelected == j ? pPart->m_OrgTexture : pSkinPart->m_OrgTexture;
Info.m_aSixup[0].m_aColors[j] = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
}
}
Info.m_Size = 50.0f;
Item.m_Rect.HSplitTop(5.0f, 0, &Item.m_Rect); // some margin from the top
const vec2 TeePos(Item.m_Rect.x + Item.m_Rect.w / 2, Item.m_Rect.y + Item.m_Rect.h / 2);
const vec2 TeePos = Item.m_Rect.Center() + vec2(0.0f, 6.0f);
if(m_TeePartSelected == protocol7::SKINPART_HANDS)
{
// RenderTools()->RenderTeeHand(&Info, TeePos, vec2(1.0f, 0.0f), -pi*0.5f, vec2(18, 0));
@ -554,25 +481,18 @@ void CMenus::RenderSkinPartSelection7(CUIRect MainView)
int TeePartEmote = EMOTE_NORMAL;
if(m_TeePartSelected == protocol7::SKINPART_EYES)
{
float LocalTime = Client()->LocalTime();
TeePartEmote = (int)(LocalTime * 0.5f) % NUM_EMOTES;
TeePartEmote = (int)(Client()->GlobalTime() * 0.5f) % NUM_EMOTES;
}
RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, TeePartEmote, vec2(1.0f, 0.0f), TeePos);
CUIRect Label;
Item.m_Rect.Margin(5.0f, &Item.m_Rect);
Item.m_Rect.HSplitBottom(10.0f, &Item.m_Rect, &Label);
Ui()->DoLabel(&Label, s->m_aName, 10.0f, TEXTALIGN_MC);
}
Ui()->DoLabel(&Label, pPart->m_aName, 12.0f, TEXTALIGN_MC);
}
const int NewSelected = s_ListBox.DoEnd();
if(NewSelected != -1 && NewSelected != s_OldSelected)
{
const CSkins7::CSkinPart *s = s_paList[m_TeePartSelected][NewSelected];
str_copy(CSkins7::ms_apSkinVariables[(int)m_Dummy][m_TeePartSelected], s->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE);
Config()->m_ClPlayer7Skin[0] = 0;
str_copy(CSkins7::ms_apSkinVariables[(int)m_Dummy][m_TeePartSelected], s_paList[m_TeePartSelected][NewSelected]->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE);
Config()->m_ClPlayer7Skin[0] = '\0';
SetNeedSendInfo();
}
s_OldSelected = NewSelected;

View file

@ -352,7 +352,7 @@ int InitSearchList(std::vector<const TName *> &vpSearchList, std::vector<TName>
void CMenus::RenderSettingsCustom(CUIRect MainView)
{
CUIRect TabBar, CustomList, QuickSearch, QuickSearchClearButton, DirectoryButton, ReloadButton;
CUIRect TabBar, CustomList, QuickSearch, DirectoryButton, ReloadButton;
MainView.HSplitTop(20.0f, &TabBar, &MainView);
const float TabWidth = TabBar.w / NUMBER_OF_ASSETS_TABS;
@ -599,28 +599,12 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
}
}
// render quick search
{
// Quick search
MainView.HSplitBottom(ms_ButtonHeight, &MainView, &QuickSearch);
QuickSearch.VSplitLeft(240.0f, &QuickSearch, &DirectoryButton);
QuickSearch.HSplitTop(5.0f, 0, &QuickSearch);
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f);
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
QuickSearch.VSplitLeft(SearchWidth, 0, &QuickSearch);
QuickSearch.VSplitLeft(5.0f, 0, &QuickSearch);
QuickSearch.VSplitLeft(QuickSearch.w - 10.0f, &QuickSearch, &QuickSearchClearButton);
if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
QuickSearch.VSplitLeft(220.0f, &QuickSearch, &DirectoryButton);
QuickSearch.HSplitTop(5.0f, nullptr, &QuickSearch);
if(Ui()->DoEditBox_Search(&s_aFilterInputs[s_CurCustomTab], &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()))
{
Ui()->SetActiveItem(&s_aFilterInputs[s_CurCustomTab]);
s_aFilterInputs[s_CurCustomTab].SelectAll();
}
s_aFilterInputs[s_CurCustomTab].SetEmptyText(Localize("Search"));
if(Ui()->DoClearableEditBox(&s_aFilterInputs[s_CurCustomTab], &QuickSearch, 14.0f))
gs_aInitCustomList[s_CurCustomTab] = true;
}

View file

@ -9,6 +9,7 @@
#include <engine/graphics.h>
#include <engine/shared/config.h>
#include <engine/shared/jsonwriter.h>
#include <engine/shared/localization.h>
#include <engine/shared/protocol7.h>
#include <engine/storage.h>
@ -19,6 +20,7 @@
#include "skins7.h"
const char *const CSkins7::ms_apSkinPartNames[protocol7::NUM_SKINPARTS] = {"body", "marking", "decoration", "hands", "feet", "eyes"};
const char *const CSkins7::ms_apSkinPartNamesLocalized[protocol7::NUM_SKINPARTS] = {Localizable("Body", "skins"), Localizable("Marking", "skins"), Localizable("Decoration", "skins"), Localizable("Hands", "skins"), Localizable("Feet", "skins"), Localizable("Eyes", "skins")};
const char *const CSkins7::ms_apColorComponents[NUM_COLOR_COMPONENTS] = {"hue", "sat", "lgt", "alp"};
char *CSkins7::ms_apSkinVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS] = {{0}};

View file

@ -52,6 +52,7 @@ public:
};
static const char *const ms_apSkinPartNames[protocol7::NUM_SKINPARTS];
static const char *const ms_apSkinPartNamesLocalized[protocol7::NUM_SKINPARTS];
static const char *const ms_apColorComponents[NUM_COLOR_COMPONENTS];
static char *ms_apSkinVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS];

View file

@ -2071,6 +2071,32 @@ void CGameClient::OnNewSnapshot()
m_Effects.AirJump(Pos, Alpha);
}
if(g_Config.m_ClFreezeStars && !m_SuppressEvents)
{
for(auto &Character : m_Snap.m_aCharacters)
{
if(Character.m_Active && Character.m_HasExtendedData && Character.m_PrevExtendedData)
{
int FreezeTimeNow = Character.m_ExtendedData.m_FreezeEnd - Client()->GameTick(g_Config.m_ClDummy);
int FreezeTimePrev = Character.m_PrevExtendedData->m_FreezeEnd - Client()->PrevGameTick(g_Config.m_ClDummy);
vec2 Pos = vec2(Character.m_Cur.m_X, Character.m_Cur.m_Y);
int StarsNow = (FreezeTimeNow + 1) / Client()->GameTickSpeed();
int StarsPrev = (FreezeTimePrev + 1) / Client()->GameTickSpeed();
if(StarsNow < StarsPrev || (StarsPrev == 0 && StarsNow > 0))
{
int Amount = StarsNow + 1;
float Mid = 3 * pi / 2;
float Min = Mid - pi / 3;
float Max = Mid + pi / 3;
for(int j = 0; j < Amount; j++)
{
float Angle = mix(Min, Max, (j + 1) / (float)(Amount + 2));
m_Effects.DamageIndicator(Pos, direction(Angle));
}
}
}
}
}
if(m_Snap.m_LocalClientId != m_PrevLocalId)
m_PredictedDummyId = m_PrevLocalId;
m_PrevLocalId = m_Snap.m_LocalClientId;

View file

@ -1004,6 +1004,25 @@ bool CUi::DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float
return ReturnValue;
}
bool CUi::DoEditBox_Search(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, bool HotkeyEnabled)
{
CUIRect QuickSearch = *pRect;
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, FontSize, TEXTALIGN_ML);
const float SearchWidth = TextRender()->TextWidth(FontSize, FONT_ICON_MAGNIFYING_GLASS);
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
QuickSearch.VSplitLeft(SearchWidth + 5.0f, nullptr, &QuickSearch);
if(HotkeyEnabled && Input()->ModifierIsPressed() && Input()->KeyPress(KEY_F))
{
SetActiveItem(pLineInput);
pLineInput->SelectAll();
}
pLineInput->SetEmptyText(Localize("Search"));
return DoClearableEditBox(pLineInput, &QuickSearch, FontSize);
}
int CUi::DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pId, const std::function<const char *()> &GetTextLambda, const CUIRect *pRect, const SMenuButtonProperties &Props)
{
CUIRect Text = *pRect, DropDownIcon;

View file

@ -603,6 +603,24 @@ public:
*/
bool DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const std::vector<STextColorSplit> &vColorSplits = {});
/**
* Creates an input field with a search icon and a clear [x] button attached to it.
* The input will have default text "Search" and the hotkey Ctrl+F can be used to activate the input.
*
* @see DoEditBox
*
* @param pLineInput This pointer will be stored and written to on next user input.
* So you can not pass in a pointer that goes out of scope such as a local variable.
* Pass in either a member variable of the current class or a static variable.
* For example ```static CLineInputBuffered<IO_MAX_PATH_LENGTH> s_MyInput;```
* @param pRect the UI rect it will attach to
* @param FontSize Size of the font (`10.0f`, `12.0f` and `14.0f` are commonly used here)
* @param HotkeyEnabled Whether the hotkey to enable this editbox is currently enabled.
*
* @return true if the value of the input field changed since the last call.
*/
bool DoEditBox_Search(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, bool HotkeyEnabled);
int DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pId, const std::function<const char *()> &GetTextLambda, const CUIRect *pRect, const SMenuButtonProperties &Props = {});
// only used for popup menus
int DoButton_PopupMenu(CButtonContainer *pButtonContainer, const char *pText, const CUIRect *pRect, float Size, int Align, float Padding = 0.0f, bool TransparentInactive = false, bool Enabled = true);

View file

@ -45,7 +45,7 @@ CAutoMapper::CAutoMapper(CEditor *pEditor)
OnInit(pEditor);
}
void CAutoMapper::Load(const char *pTileName)
void CAutoMapper::Load(const char *pTileName, bool Include)
{
char aPath[IO_MAX_PATH_LENGTH];
str_format(aPath, sizeof(aPath), "editor/automap/%s.rules", pTileName);
@ -53,6 +53,9 @@ void CAutoMapper::Load(const char *pTileName)
if(!LineReader.OpenFile(Storage()->OpenFile(aPath, IOFLAG_READ, IStorage::TYPE_ALL)))
{
char aBuf[IO_MAX_PATH_LENGTH + 32];
if(Include)
str_format(aBuf, sizeof(aBuf), "failed to include %s", aPath);
else
str_format(aBuf, sizeof(aBuf), "failed to load %s", aPath);
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor/automap", aBuf);
return;
@ -331,6 +334,20 @@ void CAutoMapper::Load(const char *pTileName)
{
pCurrentRun->m_AutomapCopy = false;
}
else if(str_startswith(pLine, "Include "))
{
pLine += str_length("Include ");
char aName[512];
str_copy(aName, pLine, minimum<int>(sizeof(aName), str_length(pLine) + 1));
if(str_comp(aName, pTileName) != 0)
Load(aName, true);
pCurrentConf = nullptr;
pCurrentRun = nullptr;
pCurrentIndex = nullptr;
}
}
}
@ -383,9 +400,13 @@ void CAutoMapper::Load(const char *pTileName)
}
char aBuf[IO_MAX_PATH_LENGTH + 16];
if(Include)
str_format(aBuf, sizeof(aBuf), "included %s", aPath);
else
str_format(aBuf, sizeof(aBuf), "loaded %s", aPath);
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor/automap", aBuf);
if(!Include)
m_FileLoaded = true;
}

View file

@ -59,7 +59,7 @@ class CAutoMapper : public CEditorComponent
public:
explicit CAutoMapper(CEditor *pEditor);
void Load(const char *pTileName);
void Load(const char *pTileName, bool Include = false);
void ProceedLocalized(class CLayerTiles *pLayer, int ConfigId, int Seed = 0, int X = 0, int Y = 0, int Width = -1, int Height = -1);
void Proceed(class CLayerTiles *pLayer, int ConfigId, int Seed = 0, int SeedOffsetX = 0, int SeedOffsetY = 0);