Compare commits

...

7 commits

Author SHA1 Message Date
Dennis Felsing 5a4d8e26e6
Merge pull request #8941 from Robyt3/Client-Skin07-Dummy-Skin-Name
Fix dummy 0.7 tee skin name not being used, fix some unused config variables not being detected
2024-09-13 21:30:19 +00:00
Robert Müller ffabdee953 Fix some unused config variables not being detected
Unused config variables were not detected if they were the prefix of a longer config variable which is used.
2024-09-13 21:37:25 +02:00
Robert Müller 7e1881263d Fix dummy 0.7 tee skin name not being used
The dummy 0.7 skin name was not being updated and used, causing the wrong skin to be selected when switching between player and dummy settings.
2024-09-13 21:36:31 +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
10 changed files with 351 additions and 364 deletions

View file

@ -17,7 +17,7 @@ def parse_config_variables(lines):
return matches return matches
def generate_regex(variable_code): def generate_regex(variable_code):
return fr'(g_Config\.m_{variable_code}|Config\(\)->m_{variable_code}|m_pConfig->m_{variable_code})' return fr'(g_Config\.m_{variable_code}\b|Config\(\)->m_{variable_code}\b|m_pConfig->m_{variable_code}\b)'
def find_config_variables(config_variables): def find_config_variables(config_variables):
"""Returns the config variables which were not found.""" """Returns the config variables which were not found."""

View file

@ -762,6 +762,8 @@ void CClient::DummyDisconnect(const char *pReason)
m_aReceivedSnapshots[1] = 0; m_aReceivedSnapshots[1] = 0;
m_DummyConnected = false; m_DummyConnected = false;
m_DummyConnecting = false; m_DummyConnecting = false;
m_DummyReconnectOnReload = false;
m_DummyDeactivateOnReconnect = false;
GameClient()->OnDummyDisconnect(); 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); 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) else if(Conn == CONN_MAIN && (pPacket->m_Flags & NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_CON_READY)
{ {
GameClient()->OnConnected(); GameClient()->OnConnected();
if(m_DummyReconnectOnReload)
{
m_DummySendConnInfo = true;
m_DummyReconnectOnReload = false;
}
} }
else if(Conn == CONN_DUMMY && Msg == NETMSG_CON_READY) 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; m_DummyConnecting = false;
g_Config.m_ClDummy = 1; g_Config.m_ClDummy = 1;
Rcon("crashmeplx"); Rcon("crashmeplx");
if(m_aRconAuthed[0]) if(m_aRconAuthed[0] && !m_aRconAuthed[1])
RconAuth(m_aRconUsername, m_aRconPassword); RconAuth(m_aRconUsername, m_aRconPassword);
} }
else if(Msg == NETMSG_PING) 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; m_LastDummy = (bool)g_Config.m_ClDummy;
} }

View file

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

View file

@ -243,6 +243,7 @@ CServer::CServer()
} }
m_MapReload = false; m_MapReload = false;
m_SameMapReload = false;
m_ReloadedWhenEmpty = false; m_ReloadedWhenEmpty = false;
m_aCurrentMap[0] = '\0'; 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) void CServer::SendConnectionReady(int ClientId)
{ {
CMsgPacker Msg(NETMSG_CON_READY, true); CMsgPacker Msg(NETMSG_CON_READY, true);
@ -2553,12 +2560,13 @@ void CServer::ChangeMap(const char *pMap)
void CServer::ReloadMap() void CServer::ReloadMap()
{ {
m_MapReload = true; m_SameMapReload = true;
} }
int CServer::LoadMap(const char *pMapName) int CServer::LoadMap(const char *pMapName)
{ {
m_MapReload = false; m_MapReload = false;
m_SameMapReload = false;
char aBuf[IO_MAX_PATH_LENGTH]; char aBuf[IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName); str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName);
@ -2805,8 +2813,9 @@ int CServer::Run()
int NewTicks = 0; int NewTicks = 0;
// load new map // 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 // load map
if(LoadMap(Config()->m_SvMap)) if(LoadMap(Config()->m_SvMap))
{ {
@ -2831,6 +2840,9 @@ int CServer::Run()
if(m_aClients[ClientId].m_State <= CClient::STATE_AUTH) if(m_aClients[ClientId].m_State <= CClient::STATE_AUTH)
continue; continue;
if(SameMapReload)
SendMapReload(ClientId);
SendMap(ClientId); SendMap(ClientId);
bool HasPersistentData = m_aClients[ClientId].m_HasPersistentData; bool HasPersistentData = m_aClients[ClientId].m_HasPersistentData;
m_aClients[ClientId].Reset(); m_aClients[ClientId].Reset();
@ -3509,7 +3521,7 @@ void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser)
void CServer::ConMapReload(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) void CServer::ConLogout(IConsole::IResult *pResult, void *pUser)

View file

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

View file

@ -35,3 +35,4 @@ UUID(NETMSG_CHECKSUM_ERROR, "checksum-error@ddnet.tw")
UUID(NETMSG_REDIRECT, "redirect@ddnet.org") UUID(NETMSG_REDIRECT, "redirect@ddnet.org")
UUID(NETMSG_RCON_CMD_GROUP_START, "rcon-cmd-group-start@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_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; int m_SettingPlayerPage;
// 0.7 skins // 0.7 skins
bool m_CustomSkinMenu = false;
int m_TeePartSelected = protocol7::SKINPART_BODY; int m_TeePartSelected = protocol7::SKINPART_BODY;
const CSkins7::CSkin *m_pSelectedSkin = nullptr; const CSkins7::CSkin *m_pSelectedSkin = nullptr;
CLineInputBuffered<protocol7::MAX_SKIN_ARRAY_SIZE, protocol7::MAX_SKIN_LENGTH> m_SkinNameInput; CLineInputBuffered<protocol7::MAX_SKIN_ARRAY_SIZE, protocol7::MAX_SKIN_LENGTH> m_SkinNameInput;
@ -596,7 +597,6 @@ protected:
void RenderSettingsTee(CUIRect MainView); void RenderSettingsTee(CUIRect MainView);
void RenderSettingsTee7(CUIRect MainView); void RenderSettingsTee7(CUIRect MainView);
void RenderSettingsTeeCustom7(CUIRect MainView); void RenderSettingsTeeCustom7(CUIRect MainView);
void RenderSettingsTeeBasic7(CUIRect MainView);
void RenderSkinSelection7(CUIRect MainView); void RenderSkinSelection7(CUIRect MainView);
void RenderSkinPartSelection7(CUIRect MainView); void RenderSkinPartSelection7(CUIRect MainView);
void RenderSettingsControls(CUIRect MainView); void RenderSettingsControls(CUIRect MainView);

View file

@ -31,246 +31,62 @@
#include <vector> #include <vector>
using namespace FontIcons;
void CMenus::RenderSettingsTee7(CUIRect MainView) void CMenus::RenderSettingsTee7(CUIRect MainView)
{ {
static bool s_CustomSkinMenu = false; CUIRect SkinPreview, NormalSkinPreview, RedTeamSkinPreview, BlueTeamSkinPreview, Buttons, QuickSearch, DirectoryButton, RefreshButton, SaveDeleteButton, TabBars, TabBar, LeftTab, RightTab;
// static int s_PlayerCountry = 0; MainView.HSplitBottom(20.0f, &MainView, &Buttons);
// static char s_aPlayerName[64] = {0}; MainView.HSplitBottom(5.0f, &MainView, nullptr);
// static char s_aPlayerClan[64] = {0}; 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) TabBars.HSplitTop(20.0f, &TabBar, &TabBars);
// { TabBar.VSplitMid(&LeftTab, &RightTab);
// s_PlayerCountry = Config()->m_PlayerCountry; TabBars.HSplitTop(10.0f, nullptr, &TabBars);
// 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;
// }
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 static CButtonContainer s_PlayerTabButton;
MainView.HSplitBottom(40.0f, &MainView, &BottomView); if(DoButton_MenuTab(&s_PlayerTabButton, Localize("Player"), !m_Dummy, &LeftTab, IGraphics::CORNER_L, nullptr, nullptr, nullptr, nullptr, 4.0f))
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
{ {
CUIRect Top, Bottom, TeeLeft, TeeRight; m_Dummy = false;
Left.HSplitTop(SpacingH, 0, &Left);
Left.HSplitTop(SkinHeight * 2, &Top, &Left);
// split the menu in 2 parts
Top.HSplitMid(&Top, &Bottom, SpacingH);
// handle left
// 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];
int aColorVars[protocol7::NUM_SKINPARTS];
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);
apSkinPartsPtr[Part] = aSkinParts[Part];
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);
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
int SkinPart = m_pClient->m_Skins7.FindSkinPart(Part, apSkinPartsPtr[Part], false);
const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.GetSkinPart(Part, SkinPart);
if(aUCCVars[Part])
{
OwnSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkinPart->m_ColorTexture;
OwnSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = m_pClient->m_Skins7.GetColor(aColorVars[Part], Part == protocol7::SKINPART_MARKING);
}
else
{
OwnSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkinPart->m_OrgTexture;
OwnSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
}
}
// 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);
{
// 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 DeltaPosition = Ui()->MousePos() - TeePosition;
const float Distance = length(DeltaPosition);
const float InteractionDistance = 20.0f;
const vec2 TeeDirection = Distance < InteractionDistance ? normalize(vec2(DeltaPosition.x, maximum(DeltaPosition.y, 0.5f))) : normalize(DeltaPosition);
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))
{
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);
apSkinPartsPtr[Part] = aSkinParts[Part];
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);
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
int SkinPart = m_pClient->m_Skins7.FindSkinPart(Part, apSkinPartsPtr[Part], false);
const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.GetSkinPart(Part, SkinPart);
if(aUCCVars[Part])
{
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkinPart->m_ColorTexture;
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = m_pClient->m_Skins7.GetColor(aColorVars[Part], Part == protocol7::SKINPART_MARKING);
}
else
{
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkinPart->m_OrgTexture;
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
}
}
// 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++)
{
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;
}
RenderTools()->RenderTee(CAnimState::GetIdle(), &TeamSkinInfo, 0, vec2(1, 0), vec2(TeeLeft.x + TeeLeft.w / 2.0f, TeeLeft.y + TeeLeft.h / 2.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); static CButtonContainer s_DummyTabButton;
Ui()->DoLabel(&Label, Localize("Settings"), ButtonHeight * CUi::ms_FontmodHeight * 0.8f, TEXTALIGN_MC); if(DoButton_MenuTab(&s_DummyTabButton, Localize("Dummy"), m_Dummy, &RightTab, IGraphics::CORNER_R, nullptr, nullptr, nullptr, nullptr, 4.0f))
// Settings
{ {
CUIRect Top, Bottom, Dummy, DummyLabel; m_Dummy = true;
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); TabBars.HSplitTop(20.0f, &TabBar, &TabBars);
TabBar.VSplitMid(&LeftTab, &RightTab);
if(s_CustomSkinMenu) static CButtonContainer s_BasicTabButton;
RenderSettingsTeeCustom7(MainView); if(DoButton_MenuTab(&s_BasicTabButton, Localize("Basic"), !m_CustomSkinMenu, &LeftTab, IGraphics::CORNER_L, nullptr, nullptr, nullptr, nullptr, 4.0f))
else
RenderSettingsTeeBasic7(MainView);
// bottom buttons
if(s_CustomSkinMenu)
{ {
static CButtonContainer s_CustomSkinSaveButton; m_CustomSkinMenu = false;
if(DoButton_Menu(&s_CustomSkinSaveButton, Localize("Save"), 0, &ButtonLeft))
{
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))
{
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; static CButtonContainer s_CustomTabButton;
if(DoButton_Menu(&s_CustomSwitchButton, s_CustomSkinMenu ? Localize("Basic") : Localize("Custom"), 0, &ButtonRight)) if(DoButton_MenuTab(&s_CustomTabButton, Localize("Custom"), m_CustomSkinMenu, &RightTab, IGraphics::CORNER_R, nullptr, nullptr, nullptr, nullptr, 4.0f))
{ {
s_CustomSkinMenu = !s_CustomSkinMenu; m_CustomSkinMenu = true;
if(s_CustomSkinMenu && m_pSelectedSkin) if(m_CustomSkinMenu && m_pSelectedSkin)
{ {
if(m_pSelectedSkin->m_Flags & CSkins7::SKINFLAG_STANDARD) if(m_pSelectedSkin->m_Flags & CSkins7::SKINFLAG_STANDARD)
{ {
@ -282,6 +98,122 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
} }
} }
// validate skin parts for solo mode
char aSkinParts[protocol7::NUM_SKINPARTS][protocol7::MAX_SKIN_ARRAY_SIZE];
char *apSkinPartsPtr[protocol7::NUM_SKINPARTS];
int aUCCVars[protocol7::NUM_SKINPARTS];
int aColorVars[protocol7::NUM_SKINPARTS];
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);
apSkinPartsPtr[Part] = aSkinParts[Part];
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);
const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.GetSkinPart(Part, SkinPart);
if(aUCCVars[Part])
{
OwnSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkinPart->m_ColorTexture;
OwnSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = m_pClient->m_Skins7.GetColor(aColorVars[Part], Part == protocol7::SKINPART_MARKING);
}
else
{
OwnSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkinPart->m_OrgTexture;
OwnSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = ColorRGBA(1.0f, 1.0f, 1.0f, 1.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 = NormalSkinPreview.Center() + vec2(0.0f, 6.0f);
const vec2 DeltaPosition = Ui()->MousePos() - TeePosition;
const float Distance = length(DeltaPosition);
const float InteractionDistance = 20.0f;
const vec2 TeeDirection = Distance < InteractionDistance ? normalize(vec2(DeltaPosition.x, maximum(DeltaPosition.y, 0.5f))) : normalize(DeltaPosition);
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, &NormalSkinPreview))
{
m_pClient->m_Sounds.Play(CSounds::CHN_GUI, SOUND_PLAYER_SPAWN, 1.0f);
}
}
// validate skin parts for team game mode
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);
apSkinPartsPtr[Part] = aSkinParts[Part];
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);
const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.GetSkinPart(Part, SkinPart);
if(aUCCVars[Part])
{
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkinPart->m_ColorTexture;
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = m_pClient->m_Skins7.GetColor(aColorVars[Part], Part == protocol7::SKINPART_MARKING);
}
else
{
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkinPart->m_OrgTexture;
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = ColorRGBA(1.0f, 1.0f, 1.0f, 1.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++)
{
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), BlueTeamSkinPreview.Center() + vec2(0.0f, 6.0f));
if(m_CustomSkinMenu)
RenderSettingsTeeCustom7(MainView);
else
RenderSkinSelection7(MainView);
if(m_CustomSkinMenu)
{
static CButtonContainer s_CustomSkinSaveButton;
if(DoButton_Menu(&s_CustomSkinSaveButton, Localize("Save"), 0, &SaveDeleteButton))
{
m_Popup = POPUP_SAVE_SKIN;
m_SkinNameInput.SelectAll();
Ui()->SetActiveItem(&m_SkinNameInput);
}
}
else if(m_pSelectedSkin && (m_pSelectedSkin->m_Flags & CSkins7::SKINFLAG_STANDARD) == 0)
{
static CButtonContainer s_CustomSkinDeleteButton;
if(DoButton_Menu(&s_CustomSkinDeleteButton, Localize("Delete"), 0, &SaveDeleteButton) || Ui()->ConsumeHotkey(CUi::HOTKEY_DELETE))
{
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 CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString)); static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString));
if(Ui()->DoEditBox_Search(&s_SkinFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed())) if(Ui()->DoEditBox_Search(&s_SkinFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()))
{ {
@ -291,12 +223,25 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
static CButtonContainer s_DirectoryButton; static CButtonContainer s_DirectoryButton;
if(DoButton_Menu(&s_DirectoryButton, Localize("Skins directory"), 0, &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()->GetCompletePath(IStorage::TYPE_SAVE, "skins7", aBuf, sizeof(aBuf));
Storage()->CreateFolder("skins7", IStorage::TYPE_SAVE); Storage()->CreateFolder("skins7", IStorage::TYPE_SAVE);
Client()->ViewFile(aBuf); Client()->ViewFile(aBuf);
} }
GameClient()->m_Tooltips.DoToolTip(&s_DirectoryButton, &DirectoryButton, Localize("Open the directory to add custom skins")); 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() void CMenus::PopupConfirmDeleteSkin7()
@ -311,51 +256,34 @@ void CMenus::PopupConfirmDeleteSkin7()
m_pSelectedSkin = nullptr; m_pSelectedSkin = nullptr;
} }
void CMenus::RenderSettingsTeeBasic7(CUIRect MainView)
{
RenderSkinSelection7(MainView); // yes thats all here ^^
}
void CMenus::RenderSettingsTeeCustom7(CUIRect MainView) void CMenus::RenderSettingsTeeCustom7(CUIRect MainView)
{ {
CUIRect Label, Patterns, Button, Left, Right; CUIRect ButtonBar, SkinPartSelection, CustomColors;
// render skin preview background MainView.HSplitTop(20.0f, &ButtonBar, &MainView);
float SpacingH = 2.0f; MainView.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f), IGraphics::CORNER_B, 5.0f);
float SpacingW = 3.0f; MainView.VSplitMid(&SkinPartSelection, &CustomColors, 10.0f);
float ButtonHeight = 20.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); static CButtonContainer s_aSkinPartButtons[protocol7::NUM_SKINPARTS];
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];
for(int i = 0; i < protocol7::NUM_SKINPARTS; i++) for(int i = 0; i < protocol7::NUM_SKINPARTS; i++)
{ {
Patterns.VSplitLeft(ButtonWidth, &Button, &Patterns); CUIRect Button;
if(DoButton_MenuTab(&s_aPatternButtons[i], Localize(CSkins7::ms_apSkinPartNames[i], "skins"), m_TeePartSelected == i, &Button, IGraphics::CORNER_ALL)) 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; m_TeePartSelected = i;
} }
Patterns.VSplitLeft(SpacingW, 0, &Patterns);
} }
MainView.HSplitTop(SpacingH, 0, &MainView); RenderSkinPartSelection7(SkinPartSelection);
MainView.VSplitMid(&Left, &Right, SpacingW);
Right.Margin(5.0f, &Right);
RenderSkinPartSelection7(Left);
CUIRect CustomColorsButton;
Right.HSplitTop(20.0f, &CustomColorsButton, &Right);
int *pUseCustomColor = CSkins7::ms_apUCCVariables[(int)m_Dummy][m_TeePartSelected]; int *pUseCustomColor = CSkins7::ms_apUCCVariables[(int)m_Dummy][m_TeePartSelected];
if(DoButton_CheckBox(pUseCustomColor, Localize("Custom colors"), *pUseCustomColor, &CustomColorsButton)) if(DoButton_CheckBox(pUseCustomColor, Localize("Custom colors"), *pUseCustomColor, &CustomColorsButton))
@ -366,15 +294,31 @@ void CMenus::RenderSettingsTeeCustom7(CUIRect MainView)
if(*pUseCustomColor) if(*pUseCustomColor)
{ {
CUIRect CustomColors; CUIRect CustomColorScrollbars;
Right.HSplitTop(5.0f, nullptr, &Right); CustomColors.HSplitTop(5.0f, nullptr, &CustomColors);
Right.HSplitTop(95.0f, &CustomColors, &Right); 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(); 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);
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) void CMenus::RenderSkinSelection7(CUIRect MainView)
@ -389,76 +333,74 @@ void CMenus::RenderSkinSelection7(CUIRect MainView)
s_SkinCount = m_pClient->m_Skins7.Num(); s_SkinCount = m_pClient->m_Skins7.Num();
for(int i = 0; i < s_SkinCount; ++i) for(int i = 0; i < s_SkinCount; ++i)
{ {
const CSkins7::CSkin *s = m_pClient->m_Skins7.Get(i); 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))
if(g_Config.m_ClSkinFilterString[0] != '\0' && !str_utf8_find_nocase(s->m_aName, g_Config.m_ClSkinFilterString))
continue; continue;
// no special skins // 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_SkinListNeedsUpdate = false;
} }
m_pSelectedSkin = 0; m_pSelectedSkin = nullptr;
int s_OldSelected = -1; 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) for(int i = 0; i < (int)s_vpSkinList.size(); ++i)
{ {
const CSkins7::CSkin *s = s_vpSkinList[i]; const CSkins7::CSkin *pSkin = s_vpSkinList[i];
if(s == 0) if(pSkin == nullptr)
continue; continue;
if(!str_comp(s->m_aName, Config()->m_ClPlayer7Skin)) if(!str_comp(pSkin->m_aName, CSkins7::ms_apSkinNameVariables[m_Dummy]))
{ {
m_pSelectedSkin = s; m_pSelectedSkin = pSkin;
s_OldSelected = i; s_OldSelected = i;
} }
CListboxItem Item = s_ListBox.DoNextItem(&s_vpSkinList[i], s_OldSelected == i); const CListboxItem Item = s_ListBox.DoNextItem(&s_vpSkinList[i], s_OldSelected == i);
if(Item.m_Visible) 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++)
{ {
CTeeRenderInfo Info; if(pSkin->m_aUseCustomColors[Part])
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{ {
if(s->m_aUseCustomColors[Part]) 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);
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);
}
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_aColors[Part] = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
}
} }
else
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 Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkin->m_apParts[Part]->m_OrgTexture;
int TeeEmote = (Item.m_Selected && s_LastSelectionTime + 0.75f > Client()->LocalTime()) ? EMOTE_HAPPY : EMOTE_NORMAL; Info.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
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));
} }
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);
} }
Info.m_Size = 50.0f;
{
// interactive tee: tee is happy to be selected
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));
}
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(); const int NewSelected = s_ListBox.DoEnd();
if(NewSelected != -1 && NewSelected != s_OldSelected) if(NewSelected != -1 && NewSelected != s_OldSelected)
{ {
s_LastSelectionTime = Client()->LocalTime(); s_LastSelectionTime = Client()->GlobalTime();
m_pSelectedSkin = s_vpSkinList[NewSelected]; m_pSelectedSkin = s_vpSkinList[NewSelected];
str_copy(Config()->m_ClPlayer7Skin, m_pSelectedSkin->m_aName); str_copy(CSkins7::ms_apSkinNameVariables[m_Dummy], m_pSelectedSkin->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE);
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++) for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{ {
str_copy(CSkins7::ms_apSkinVariables[(int)m_Dummy][Part], m_pSelectedSkin->m_apParts[Part]->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE); str_copy(CSkins7::ms_apSkinVariables[(int)m_Dummy][Part], m_pSelectedSkin->m_apParts[Part]->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE);
@ -481,11 +423,11 @@ void CMenus::RenderSkinPartSelection7(CUIRect MainView)
s_paList[Part].clear(); s_paList[Part].clear();
for(int i = 0; i < m_pClient->m_Skins7.NumSkinPart(Part); ++i) 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 // 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);
} }
} }
} }
@ -494,71 +436,62 @@ void CMenus::RenderSkinPartSelection7(CUIRect MainView)
static int s_OldSelected = -1; static int s_OldSelected = -1;
s_ListBox.DoBegin(&MainView); 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) for(int i = 0; i < (int)s_paList[m_TeePartSelected].size(); ++i)
{ {
const CSkins7::CSkinPart *s = s_paList[m_TeePartSelected][i]; const CSkins7::CSkinPart *pPart = s_paList[m_TeePartSelected][i];
if(s == 0) if(pPart == nullptr)
continue; 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; s_OldSelected = i;
CListboxItem Item = s_ListBox.DoNextItem(&s_paList[m_TeePartSelected][i], 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++)
{ {
CTeeRenderInfo Info; int SkinPart = m_pClient->m_Skins7.FindSkinPart(j, CSkins7::ms_apSkinVariables[(int)m_Dummy][j], false);
for(int j = 0; j < protocol7::NUM_SKINPARTS; j++) const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.GetSkinPart(j, SkinPart);
if(*CSkins7::ms_apUCCVariables[(int)m_Dummy][j])
{ {
int SkinPart = m_pClient->m_Skins7.FindSkinPart(j, CSkins7::ms_apSkinVariables[(int)m_Dummy][j], false); Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[j] = m_TeePartSelected == j ? pPart->m_ColorTexture : pSkinPart->m_ColorTexture;
const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.GetSkinPart(j, SkinPart); 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);
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_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_Size = 50.0f; else
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);
if(m_TeePartSelected == protocol7::SKINPART_HANDS)
{ {
// RenderTools()->RenderTeeHand(&Info, TeePos, vec2(1.0f, 0.0f), -pi*0.5f, vec2(18, 0)); 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);
} }
int TeePartEmote = EMOTE_NORMAL;
if(m_TeePartSelected == protocol7::SKINPART_EYES)
{
float LocalTime = Client()->LocalTime();
TeePartEmote = (int)(LocalTime * 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);
} }
Info.m_Size = 50.0f;
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));
}
int TeePartEmote = EMOTE_NORMAL;
if(m_TeePartSelected == protocol7::SKINPART_EYES)
{
TeePartEmote = (int)(Client()->GlobalTime() * 0.5f) % NUM_EMOTES;
}
RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, TeePartEmote, vec2(1.0f, 0.0f), TeePos);
Ui()->DoLabel(&Label, pPart->m_aName, 12.0f, TEXTALIGN_MC);
} }
const int NewSelected = s_ListBox.DoEnd(); const int NewSelected = s_ListBox.DoEnd();
if(NewSelected != -1 && NewSelected != s_OldSelected) 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_paList[m_TeePartSelected][NewSelected]->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE);
str_copy(CSkins7::ms_apSkinVariables[(int)m_Dummy][m_TeePartSelected], s->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE); CSkins7::ms_apSkinNameVariables[m_Dummy][0] = '\0';
Config()->m_ClPlayer7Skin[0] = 0;
SetNeedSendInfo(); SetNeedSendInfo();
} }
s_OldSelected = NewSelected; s_OldSelected = NewSelected;

View file

@ -9,6 +9,7 @@
#include <engine/graphics.h> #include <engine/graphics.h>
#include <engine/shared/config.h> #include <engine/shared/config.h>
#include <engine/shared/jsonwriter.h> #include <engine/shared/jsonwriter.h>
#include <engine/shared/localization.h>
#include <engine/shared/protocol7.h> #include <engine/shared/protocol7.h>
#include <engine/storage.h> #include <engine/storage.h>
@ -19,8 +20,10 @@
#include "skins7.h" #include "skins7.h"
const char *const CSkins7::ms_apSkinPartNames[protocol7::NUM_SKINPARTS] = {"body", "marking", "decoration", "hands", "feet", "eyes"}; 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"}; const char *const CSkins7::ms_apColorComponents[NUM_COLOR_COMPONENTS] = {"hue", "sat", "lgt", "alp"};
char *CSkins7::ms_apSkinNameVariables[NUM_DUMMIES] = {0};
char *CSkins7::ms_apSkinVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS] = {{0}}; char *CSkins7::ms_apSkinVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS] = {{0}};
int *CSkins7::ms_apUCCVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS] = {{0}}; int *CSkins7::ms_apUCCVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS] = {{0}};
int unsigned *CSkins7::ms_apColorVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS] = {{0}}; int unsigned *CSkins7::ms_apColorVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS] = {{0}};
@ -229,6 +232,7 @@ int CSkins7::GetInitAmount() const
void CSkins7::OnInit() void CSkins7::OnInit()
{ {
int Dummy = 0; int Dummy = 0;
ms_apSkinNameVariables[Dummy] = Config()->m_ClPlayer7Skin;
ms_apSkinVariables[Dummy][protocol7::SKINPART_BODY] = Config()->m_ClPlayer7SkinBody; ms_apSkinVariables[Dummy][protocol7::SKINPART_BODY] = Config()->m_ClPlayer7SkinBody;
ms_apSkinVariables[Dummy][protocol7::SKINPART_MARKING] = Config()->m_ClPlayer7SkinMarking; ms_apSkinVariables[Dummy][protocol7::SKINPART_MARKING] = Config()->m_ClPlayer7SkinMarking;
ms_apSkinVariables[Dummy][protocol7::SKINPART_DECORATION] = Config()->m_ClPlayer7SkinDecoration; ms_apSkinVariables[Dummy][protocol7::SKINPART_DECORATION] = Config()->m_ClPlayer7SkinDecoration;
@ -249,6 +253,7 @@ void CSkins7::OnInit()
ms_apColorVariables[Dummy][protocol7::SKINPART_EYES] = &Config()->m_ClPlayer7ColorEyes; ms_apColorVariables[Dummy][protocol7::SKINPART_EYES] = &Config()->m_ClPlayer7ColorEyes;
Dummy = 1; Dummy = 1;
ms_apSkinNameVariables[Dummy] = Config()->m_ClDummy7Skin;
ms_apSkinVariables[Dummy][protocol7::SKINPART_BODY] = Config()->m_ClDummy7SkinBody; ms_apSkinVariables[Dummy][protocol7::SKINPART_BODY] = Config()->m_ClDummy7SkinBody;
ms_apSkinVariables[Dummy][protocol7::SKINPART_MARKING] = Config()->m_ClDummy7SkinMarking; ms_apSkinVariables[Dummy][protocol7::SKINPART_MARKING] = Config()->m_ClDummy7SkinMarking;
ms_apSkinVariables[Dummy][protocol7::SKINPART_DECORATION] = Config()->m_ClDummy7SkinDecoration; ms_apSkinVariables[Dummy][protocol7::SKINPART_DECORATION] = Config()->m_ClDummy7SkinDecoration;
@ -461,17 +466,19 @@ void CSkins7::RandomizeSkin(int Dummy)
if(Part == protocol7::SKINPART_MARKING) if(Part == protocol7::SKINPART_MARKING)
Alp = rand() % 255; Alp = rand() % 255;
int ColorVariable = (Alp << 24) | (Hue << 16) | (Sat << 8) | Lgt; int ColorVariable = (Alp << 24) | (Hue << 16) | (Sat << 8) | Lgt;
*CSkins7::ms_apUCCVariables[Dummy][Part] = true; *ms_apUCCVariables[Dummy][Part] = true;
*CSkins7::ms_apColorVariables[Dummy][Part] = ColorVariable; *ms_apColorVariables[Dummy][Part] = ColorVariable;
} }
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++) for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{ {
const CSkins7::CSkinPart *pSkinPart = GetSkinPart(Part, rand() % NumSkinPart(Part)); const CSkinPart *pSkinPart = GetSkinPart(Part, rand() % NumSkinPart(Part));
while(pSkinPart->m_Flags & CSkins7::SKINFLAG_SPECIAL) while(pSkinPart->m_Flags & SKINFLAG_SPECIAL)
pSkinPart = GetSkinPart(Part, rand() % NumSkinPart(Part)); pSkinPart = GetSkinPart(Part, rand() % NumSkinPart(Part));
mem_copy(CSkins7::ms_apSkinVariables[Dummy][Part], pSkinPart->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE); str_copy(ms_apSkinVariables[Dummy][Part], pSkinPart->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE);
} }
ms_apSkinNameVariables[Dummy][0] = '\0';
} }
ColorRGBA CSkins7::GetColor(int Value, bool UseAlpha) const ColorRGBA CSkins7::GetColor(int Value, bool UseAlpha) const

View file

@ -52,8 +52,10 @@ public:
}; };
static const char *const ms_apSkinPartNames[protocol7::NUM_SKINPARTS]; 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 const char *const ms_apColorComponents[NUM_COLOR_COMPONENTS];
static char *ms_apSkinNameVariables[NUM_DUMMIES];
static char *ms_apSkinVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS]; static char *ms_apSkinVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS];
static int *ms_apUCCVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS]; // use custom color static int *ms_apUCCVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS]; // use custom color
static unsigned int *ms_apColorVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS]; static unsigned int *ms_apColorVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS];