Merge pull request #9208 from Robyt3/Client-Skin07-FindSkin-Cleanup

Refactor `CSkins7` accessor functions and 0.7 skin json parsing
This commit is contained in:
Dennis Felsing 2024-11-05 07:33:22 +00:00 committed by GitHub
commit c0ff4c35f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 224 additions and 217 deletions

View file

@ -116,8 +116,7 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
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);
const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.FindSkinPart(Part, apSkinPartsPtr[Part], false);
if(aUCCVars[Part])
{
OwnSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = pSkinPart->m_ColorTexture;
@ -160,21 +159,12 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
}
m_pClient->m_Skins7.ValidateSkinParts(apSkinPartsPtr, aUCCVars, aColorVars, GAMEFLAG_TEAMS);
CTeeRenderInfo TeamSkinInfo = OwnSkinInfo;
CTeeRenderInfo TeamSkinInfo;
TeamSkinInfo.m_Size = OwnSkinInfo.m_Size;
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);
}
const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.FindSkinPart(Part, apSkinPartsPtr[Part], false);
TeamSkinInfo.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = aUCCVars[Part] ? pSkinPart->m_ColorTexture : pSkinPart->m_OrgTexture;
}
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
@ -327,22 +317,21 @@ void CMenus::RenderSkinSelection7(CUIRect MainView)
static float s_LastSelectionTime = -10.0f;
static std::vector<const CSkins7::CSkin *> s_vpSkinList;
static CListBox s_ListBox;
static int s_SkinCount = 0;
if(m_SkinListNeedsUpdate || m_pClient->m_Skins7.Num() != s_SkinCount)
static size_t s_SkinCount = 0;
const std::vector<CSkins7::CSkin> &vCurrentSkins = GameClient()->m_Skins7.GetSkins();
if(m_SkinListNeedsUpdate || vCurrentSkins.size() != s_SkinCount)
{
s_vpSkinList.clear();
s_SkinCount = m_pClient->m_Skins7.Num();
for(int i = 0; i < s_SkinCount; ++i)
s_SkinCount = vCurrentSkins.size();
for(const CSkins7::CSkin &Skin : vCurrentSkins)
{
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((Skin.m_Flags & CSkins7::SKINFLAG_SPECIAL) != 0)
continue;
if(g_Config.m_ClSkinFilterString[0] != '\0' && !str_utf8_find_nocase(Skin.m_aName, g_Config.m_ClSkinFilterString))
continue;
// no special skins
if((pSkin->m_Flags & CSkins7::SKINFLAG_SPECIAL) == 0)
{
s_vpSkinList.emplace_back(pSkin);
}
s_vpSkinList.emplace_back(&Skin);
}
m_SkinListNeedsUpdate = false;
}
@ -416,24 +405,23 @@ void CMenus::RenderSkinPartSelection7(CUIRect MainView)
{
static std::vector<const CSkins7::CSkinPart *> s_paList[protocol7::NUM_SKINPARTS];
static CListBox s_ListBox;
static int s_aSkinPartCount[protocol7::NUM_SKINPARTS] = {0};
static size_t s_aSkinPartCount[protocol7::NUM_SKINPARTS] = {0};
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
if(m_SkinPartListNeedsUpdate || m_pClient->m_Skins7.NumSkinPart(Part) != s_aSkinPartCount[Part])
const std::vector<CSkins7::CSkinPart> &vCurrentSkinParts = GameClient()->m_Skins7.GetSkinParts(Part);
if(m_SkinPartListNeedsUpdate || vCurrentSkinParts.size() != s_aSkinPartCount[Part])
{
s_paList[Part].clear();
s_aSkinPartCount[Part] = m_pClient->m_Skins7.NumSkinPart(Part);
for(int i = 0; i < s_aSkinPartCount[Part]; ++i)
s_aSkinPartCount[Part] = vCurrentSkinParts.size();
for(const CSkins7::CSkinPart &SkinPart : vCurrentSkinParts)
{
const CSkins7::CSkinPart *pPart = m_pClient->m_Skins7.GetSkinPart(Part, i);
if(g_Config.m_ClSkinFilterString[0] != '\0' && !str_utf8_find_nocase(pPart->m_aName, g_Config.m_ClSkinFilterString))
if((SkinPart.m_Flags & CSkins7::SKINFLAG_SPECIAL) != 0)
continue;
// no special skins
if((pPart->m_Flags & CSkins7::SKINFLAG_SPECIAL) == 0)
{
s_paList[Part].emplace_back(pPart);
}
if(g_Config.m_ClSkinFilterString[0] != '\0' && !str_utf8_find_nocase(SkinPart.m_aName, g_Config.m_ClSkinFilterString))
continue;
s_paList[Part].emplace_back(&SkinPart);
}
}
}
@ -460,19 +448,18 @@ void CMenus::RenderSkinPartSelection7(CUIRect MainView)
Item.m_Rect.HSplitBottom(12.0f, &Item.m_Rect, &Label);
CTeeRenderInfo Info;
for(int j = 0; j < protocol7::NUM_SKINPARTS; j++)
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
int SkinPart = m_pClient->m_Skins7.FindSkinPart(j, CSkins7::ms_apSkinVariables[(int)m_Dummy][j], false);
const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.GetSkinPart(j, SkinPart);
if(*CSkins7::ms_apUCCVariables[(int)m_Dummy][j])
const CSkins7::CSkinPart *pSkinPart = m_pClient->m_Skins7.FindSkinPart(Part, CSkins7::ms_apSkinVariables[(int)m_Dummy][Part], false);
if(*CSkins7::ms_apUCCVariables[(int)m_Dummy][Part])
{
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);
Info.m_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = m_TeePartSelected == Part ? pPart->m_ColorTexture : pSkinPart->m_ColorTexture;
Info.m_aSixup[g_Config.m_ClDummy].m_aColors[Part] = m_pClient->m_Skins7.GetColor(*CSkins7::ms_apColorVariables[(int)m_Dummy][Part], Part == protocol7::SKINPART_MARKING);
}
else
{
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_aSixup[g_Config.m_ClDummy].m_aTextures[Part] = m_TeePartSelected == Part ? pPart->m_OrgTexture : pSkinPart->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;

View file

@ -48,11 +48,6 @@ int CSkins7::SkinPartScan(const char *pName, int IsDir, int DirType, void *pUser
return 0;
}
CSkinPart Part;
str_copy(Part.m_aName, pName, minimum<int>(PartNameSize + 1, sizeof(Part.m_aName)));
if(pSelf->FindSkinPart(pSelf->m_ScanningPart, Part.m_aName, true) != -1)
return 0;
char aFilename[IO_MAX_PATH_LENGTH];
str_format(aFilename, sizeof(aFilename), SKINS_DIR "/%s/%s", CSkins7::ms_apSkinPartNames[pSelf->m_ScanningPart], pName);
CImageInfo Info;
@ -68,6 +63,8 @@ int CSkins7::SkinPartScan(const char *pName, int IsDir, int DirType, void *pUser
return 0;
}
CSkinPart Part;
str_copy(Part.m_aName, pName, minimum<int>(PartNameSize + 1, sizeof(Part.m_aName)));
Part.m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Info, 0, aFilename);
Part.m_BloodColor = ColorRGBA(1.0f, 1.0f, 1.0f);
@ -131,14 +128,15 @@ int CSkins7::SkinScan(const char *pName, int IsDir, int DirType, void *pUser)
}
// init
CSkin Skin = pSelf->m_DummySkin;
int NameLength = str_length(pName);
str_copy(Skin.m_aName, pName, 1 + NameLength - str_length(".json"));
bool SpecialSkin = pName[0] == 'x' && pName[1] == '_';
CSkin Skin;
str_copy(Skin.m_aName, pName, 1 + str_length(pName) - str_length(".json"));
const bool SpecialSkin = pName[0] == 'x' && pName[1] == '_';
Skin.m_Flags = SpecialSkin ? SKINFLAG_SPECIAL : 0;
if(DirType != IStorage::TYPE_SAVE)
Skin.m_Flags |= SKINFLAG_STANDARD;
// parse json data
json_settings JsonSettings;
mem_zero(&JsonSettings, sizeof(JsonSettings));
json_settings JsonSettings{};
char aError[256];
json_value *pJsonData = json_parse_ex(&JsonSettings, static_cast<const json_char *>(pFileData), JsonFileSize, aError);
free(pFileData);
@ -151,64 +149,78 @@ int CSkins7::SkinScan(const char *pName, int IsDir, int DirType, void *pUser)
// extract data
const json_value &Start = (*pJsonData)["skin"];
if(Start.type == json_object)
if(Start.type != json_object)
{
for(int PartIndex = 0; PartIndex < protocol7::NUM_SKINPARTS; ++PartIndex)
log_error("skins7", "Failed to parse skin json file '%s': root must be an object", aFilename);
json_value_free(pJsonData);
return 0;
}
for(int PartIndex = 0; PartIndex < protocol7::NUM_SKINPARTS; ++PartIndex)
{
Skin.m_aUseCustomColors[PartIndex] = 0;
Skin.m_aPartColors[PartIndex] = (PartIndex == protocol7::SKINPART_MARKING ? 0xFF000000u : 0u) + 0x00FF80u;
const json_value &Part = Start[(const char *)ms_apSkinPartNames[PartIndex]];
if(Part.type == json_none)
{
const json_value &Part = Start[(const char *)ms_apSkinPartNames[PartIndex]];
if(Part.type != json_object)
Skin.m_apParts[PartIndex] = pSelf->FindDefaultSkinPart(PartIndex);
continue;
}
if(Part.type != json_object)
{
log_error("skins7", "Failed to parse skin json file '%s': attribute '%s' must specify an object", aFilename, ms_apSkinPartNames[PartIndex]);
json_value_free(pJsonData);
return 0;
}
// filename
const json_value &Filename = Part["filename"];
if(Filename.type == json_string)
{
Skin.m_apParts[PartIndex] = pSelf->FindSkinPart(PartIndex, (const char *)Filename, SpecialSkin);
}
else
{
log_error("skins7", "Failed to parse skin json file '%s': part '%s' attribute 'filename' must specify a string", aFilename, ms_apSkinPartNames[PartIndex]);
json_value_free(pJsonData);
return 0;
}
// use custom colors
bool UseCustomColors = false;
const json_value &Color = Part["custom_colors"];
if(Color.type == json_string)
UseCustomColors = str_comp((const char *)Color, "true") == 0;
else if(Color.type == json_boolean)
UseCustomColors = Color.u.boolean;
Skin.m_aUseCustomColors[PartIndex] = UseCustomColors;
// color components
if(!UseCustomColors)
continue;
for(int i = 0; i < NUM_COLOR_COMPONENTS; i++)
{
if(PartIndex != protocol7::SKINPART_MARKING && i == 3)
continue;
// filename
const json_value &Filename = Part["filename"];
if(Filename.type == json_string)
const json_value &Component = Part[(const char *)ms_apColorComponents[i]];
if(Component.type == json_integer)
{
int SkinPart = pSelf->FindSkinPart(PartIndex, (const char *)Filename, SpecialSkin);
if(SkinPart > -1)
Skin.m_apParts[PartIndex] = pSelf->GetSkinPart(PartIndex, SkinPart);
}
// use custom colors
bool UseCustomColors = false;
const json_value &Color = Part["custom_colors"];
if(Color.type == json_string)
UseCustomColors = str_comp((const char *)Color, "true") == 0;
else if(Color.type == json_boolean)
UseCustomColors = Color.u.boolean;
Skin.m_aUseCustomColors[PartIndex] = UseCustomColors;
// color components
if(!UseCustomColors)
continue;
for(int i = 0; i < NUM_COLOR_COMPONENTS; i++)
{
if(PartIndex != protocol7::SKINPART_MARKING && i == 3)
continue;
const json_value &Component = Part[(const char *)ms_apColorComponents[i]];
if(Component.type == json_integer)
switch(i)
{
switch(i)
{
case 0: Skin.m_aPartColors[PartIndex] = (Skin.m_aPartColors[PartIndex] & 0xFF00FFFF) | (Component.u.integer << 16); break;
case 1: Skin.m_aPartColors[PartIndex] = (Skin.m_aPartColors[PartIndex] & 0xFFFF00FF) | (Component.u.integer << 8); break;
case 2: Skin.m_aPartColors[PartIndex] = (Skin.m_aPartColors[PartIndex] & 0xFFFFFF00) | Component.u.integer; break;
case 3: Skin.m_aPartColors[PartIndex] = (Skin.m_aPartColors[PartIndex] & 0x00FFFFFF) | (Component.u.integer << 24); break;
}
case 0: Skin.m_aPartColors[PartIndex] = (Skin.m_aPartColors[PartIndex] & 0xFF00FFFFu) | (Component.u.integer << 16); break;
case 1: Skin.m_aPartColors[PartIndex] = (Skin.m_aPartColors[PartIndex] & 0xFFFF00FFu) | (Component.u.integer << 8); break;
case 2: Skin.m_aPartColors[PartIndex] = (Skin.m_aPartColors[PartIndex] & 0xFFFFFF00u) | Component.u.integer; break;
case 3: Skin.m_aPartColors[PartIndex] = (Skin.m_aPartColors[PartIndex] & 0x00FFFFFFu) | (Component.u.integer << 24); break;
}
}
}
}
// clean up
json_value_free(pJsonData);
// set skin data
Skin.m_Flags = SpecialSkin ? SKINFLAG_SPECIAL : 0;
if(DirType != IStorage::TYPE_SAVE)
Skin.m_Flags |= SKINFLAG_STANDARD;
if(pSelf->Config()->m_Debug)
{
log_trace("skins7", "Loaded skin '%s'", Skin.m_aName);
@ -218,11 +230,6 @@ int CSkins7::SkinScan(const char *pName, int IsDir, int DirType, void *pUser)
return 0;
}
int CSkins7::GetInitAmount() const
{
return protocol7::NUM_SKINPARTS * 5 + 8;
}
void CSkins7::OnInit()
{
int Dummy = 0;
@ -267,6 +274,8 @@ void CSkins7::OnInit()
ms_apColorVariables[Dummy][protocol7::SKINPART_FEET] = &Config()->m_ClDummy7ColorFeet;
ms_apColorVariables[Dummy][protocol7::SKINPART_EYES] = &Config()->m_ClDummy7ColorEyes;
InitPlaceholderSkinParts();
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
m_avSkinParts[Part].clear();
@ -282,56 +291,36 @@ void CSkins7::OnInit()
}
// load skin parts
char aBuf[64];
char aBuf[IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), SKINS_DIR "/%s", ms_apSkinPartNames[Part]);
m_ScanningPart = Part;
Storage()->ListDirectory(IStorage::TYPE_ALL, aBuf, SkinPartScan, this);
// add dummy skin part
if(m_avSkinParts[Part].empty())
{
CSkinPart DummySkinPart;
DummySkinPart.m_Flags = SKINFLAG_STANDARD;
str_copy(DummySkinPart.m_aName, "dummy");
DummySkinPart.m_BloodColor = vec3(1.0f, 1.0f, 1.0f);
m_avSkinParts[Part].emplace_back(DummySkinPart);
}
GameClient()->m_Menus.RenderLoading(Localize("Loading DDNet Client"), Localize("Loading skin files"), 0);
}
// create dummy skin
m_DummySkin.m_Flags = SKINFLAG_STANDARD;
str_copy(m_DummySkin.m_aName, "dummy");
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
int Default;
if(Part == protocol7::SKINPART_MARKING || Part == protocol7::SKINPART_DECORATION)
Default = FindSkinPart(Part, "", false);
else
Default = FindSkinPart(Part, "standard", false);
if(Default < 0)
Default = 0;
m_DummySkin.m_apParts[Part] = GetSkinPart(Part, Default);
m_DummySkin.m_aPartColors[Part] = Part == protocol7::SKINPART_MARKING ? (255 << 24) + 65408 : 65408;
m_DummySkin.m_aUseCustomColors[Part] = 0;
}
GameClient()->m_Menus.RenderLoading(Localize("Loading DDNet Client"), Localize("Loading skin files"), 0);
// load skins
m_vSkins.clear();
Storage()->ListDirectory(IStorage::TYPE_ALL, SKINS_DIR, SkinScan, this);
GameClient()->m_Menus.RenderLoading(Localize("Loading DDNet Client"), Localize("Loading skin files"), 0);
// add dummy skin
if(m_vSkins.empty())
m_vSkins.emplace_back(m_DummySkin);
LoadXmasHat();
LoadBotDecoration();
GameClient()->m_Menus.RenderLoading(Localize("Loading DDNet Client"), Localize("Loading skin files"), 0);
}
void CSkins7::InitPlaceholderSkinParts()
{
for(CSkinPart &SkinPart : m_aPlaceholderSkinParts)
{
SkinPart.m_Flags = SKINFLAG_STANDARD;
str_copy(SkinPart.m_aName, "dummy");
SkinPart.m_OrgTexture.Invalidate();
SkinPart.m_ColorTexture.Invalidate();
SkinPart.m_BloodColor = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
}
}
void CSkins7::LoadXmasHat()
{
const char *pFilename = SKINS_DIR "/xmas_hat.png";
@ -374,24 +363,26 @@ void CSkins7::LoadBotDecoration()
}
}
void CSkins7::AddSkin(const char *pSkinName, int Dummy)
void CSkins7::AddSkinFromConfigVariables(const char *pName, int Dummy)
{
CSkin Skin = m_DummySkin;
Skin.m_Flags = 0;
str_copy(Skin.m_aName, pSkinName, sizeof(Skin.m_aName));
auto OldSkin = std::find_if(m_vSkins.begin(), m_vSkins.end(), [pName](const CSkin &Skin) {
return str_comp(Skin.m_aName, pName) == 0;
});
if(OldSkin != m_vSkins.end())
{
m_vSkins.erase(OldSkin);
}
CSkin NewSkin;
NewSkin.m_Flags = 0;
str_copy(NewSkin.m_aName, pName);
for(int PartIndex = 0; PartIndex < protocol7::NUM_SKINPARTS; ++PartIndex)
{
int SkinPart = FindSkinPart(PartIndex, ms_apSkinVariables[Dummy][PartIndex], false);
if(SkinPart > -1)
Skin.m_apParts[PartIndex] = GetSkinPart(PartIndex, SkinPart);
Skin.m_aUseCustomColors[PartIndex] = *ms_apUCCVariables[Dummy][PartIndex];
Skin.m_aPartColors[PartIndex] = *ms_apColorVariables[Dummy][PartIndex];
NewSkin.m_apParts[PartIndex] = FindSkinPart(PartIndex, ms_apSkinVariables[Dummy][PartIndex], false);
NewSkin.m_aUseCustomColors[PartIndex] = *ms_apUCCVariables[Dummy][PartIndex];
NewSkin.m_aPartColors[PartIndex] = *ms_apColorVariables[Dummy][PartIndex];
}
int SkinIndex = Find(Skin.m_aName, false);
if(SkinIndex != -1)
m_vSkins[SkinIndex] = Skin;
else
m_vSkins.emplace_back(Skin);
m_vSkins.insert(std::lower_bound(m_vSkins.begin(), m_vSkins.end(), NewSkin), NewSkin);
}
bool CSkins7::RemoveSkin(const CSkin *pSkin)
@ -403,53 +394,60 @@ bool CSkins7::RemoveSkin(const CSkin *pSkin)
return false;
}
auto Position = std::find(m_vSkins.begin(), m_vSkins.end(), *pSkin);
m_vSkins.erase(Position);
auto FoundSkin = std::find(m_vSkins.begin(), m_vSkins.end(), *pSkin);
dbg_assert(FoundSkin != m_vSkins.end(), "Skin not found");
m_vSkins.erase(FoundSkin);
return true;
}
int CSkins7::Num()
const std::vector<CSkins7::CSkin> &CSkins7::GetSkins() const
{
return m_vSkins.size();
return m_vSkins;
}
int CSkins7::NumSkinPart(int Part)
const std::vector<CSkins7::CSkinPart> &CSkins7::GetSkinParts(int Part) const
{
return m_avSkinParts[Part].size();
return m_avSkinParts[Part];
}
const CSkins7::CSkin *CSkins7::Get(int Index)
const CSkins7::CSkinPart *CSkins7::FindSkinPartOrNullptr(int Part, const char *pName, bool AllowSpecialPart) const
{
return &m_vSkins[maximum((std::size_t)0, Index % m_vSkins.size())];
}
int CSkins7::Find(const char *pName, bool AllowSpecialSkin)
{
for(unsigned int i = 0; i < m_vSkins.size(); i++)
auto FoundPart = std::find_if(m_avSkinParts[Part].begin(), m_avSkinParts[Part].end(), [pName](const CSkinPart &SkinPart) {
return str_comp(SkinPart.m_aName, pName) == 0;
});
if(FoundPart == m_avSkinParts[Part].end())
{
if(str_comp(m_vSkins[i].m_aName, pName) == 0 && ((m_vSkins[i].m_Flags & SKINFLAG_SPECIAL) == 0 || AllowSpecialSkin))
return i;
return nullptr;
}
return -1;
}
const CSkins7::CSkinPart *CSkins7::GetSkinPart(int Part, int Index)
{
int Size = m_avSkinParts[Part].size();
return &m_avSkinParts[Part][maximum(0, Index % Size)];
}
int CSkins7::FindSkinPart(int Part, const char *pName, bool AllowSpecialPart)
{
for(unsigned int i = 0; i < m_avSkinParts[Part].size(); i++)
if((FoundPart->m_Flags & SKINFLAG_SPECIAL) != 0 && !AllowSpecialPart)
{
if(str_comp(m_avSkinParts[Part][i].m_aName, pName) == 0 && ((m_avSkinParts[Part][i].m_Flags & SKINFLAG_SPECIAL) == 0 || AllowSpecialPart))
return i;
return nullptr;
}
return -1;
return &*FoundPart;
}
void CSkins7::RandomizeSkin(int Dummy)
const CSkins7::CSkinPart *CSkins7::FindDefaultSkinPart(int Part) const
{
const char *pDefaultPartName = Part == protocol7::SKINPART_MARKING || Part == protocol7::SKINPART_DECORATION ? "" : "default";
const CSkinPart *pDefault = FindSkinPartOrNullptr(Part, pDefaultPartName, false);
if(pDefault != nullptr)
{
return pDefault;
}
return &m_aPlaceholderSkinParts[Part];
}
const CSkins7::CSkinPart *CSkins7::FindSkinPart(int Part, const char *pName, bool AllowSpecialPart) const
{
const CSkinPart *pSkinPart = FindSkinPartOrNullptr(Part, pName, AllowSpecialPart);
if(pSkinPart != nullptr)
{
return pSkinPart;
}
return FindDefaultSkinPart(Part);
}
void CSkins7::RandomizeSkin(int Dummy) const
{
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
@ -466,10 +464,23 @@ void CSkins7::RandomizeSkin(int Dummy)
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
const CSkinPart *pSkinPart = GetSkinPart(Part, rand() % NumSkinPart(Part));
while(pSkinPart->m_Flags & SKINFLAG_SPECIAL)
pSkinPart = GetSkinPart(Part, rand() % NumSkinPart(Part));
str_copy(ms_apSkinVariables[Dummy][Part], pSkinPart->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE);
std::vector<const CSkins7::CSkinPart *> vConsideredSkinParts;
for(const CSkinPart &SkinPart : GetSkinParts(Part))
{
if((SkinPart.m_Flags & CSkins7::SKINFLAG_SPECIAL) != 0)
continue;
vConsideredSkinParts.push_back(&SkinPart);
}
const CSkins7::CSkinPart *pRandomPart;
if(vConsideredSkinParts.empty())
{
pRandomPart = FindDefaultSkinPart(Part);
}
else
{
pRandomPart = vConsideredSkinParts[rand() % vConsideredSkinParts.size()];
}
str_copy(CSkins7::ms_apSkinVariables[Dummy][Part], pRandomPart->m_aName, protocol7::MAX_SKIN_ARRAY_SIZE);
}
ms_apSkinNameVariables[Dummy][0] = '\0';
@ -523,10 +534,13 @@ bool CSkins7::ValidateSkinParts(char *apPartNames[protocol7::NUM_SKINPARTS], int
return true;
}
bool CSkins7::SaveSkinfile(const char *pSaveSkinName, int Dummy)
bool CSkins7::SaveSkinfile(const char *pName, int Dummy)
{
const bool SpecialSkin = pName[0] == 'x' && pName[1] == '_';
dbg_assert(!SpecialSkin, "Cannot save special skins");
char aBuf[IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), SKINS_DIR "/%s.json", pSaveSkinName);
str_format(aBuf, sizeof(aBuf), SKINS_DIR "/%s.json", pName);
IOHANDLE File = Storage()->OpenFile(aBuf, IOFLAG_WRITE, IStorage::TYPE_SAVE);
if(!File)
return false;
@ -573,7 +587,6 @@ bool CSkins7::SaveSkinfile(const char *pSaveSkinName, int Dummy)
Writer.EndObject();
Writer.EndObject();
// add new skin to the skin list
AddSkin(pSaveSkinName, Dummy);
AddSkinFromConfigVariables(pName, Dummy);
return true;
}

View file

@ -2,16 +2,19 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_CLIENT_COMPONENTS_SKINS7_H
#define GAME_CLIENT_COMPONENTS_SKINS7_H
#include <base/color.h>
#include <base/vmath.h>
#include <engine/client/enums.h>
#include <engine/graphics.h>
#include <game/client/component.h>
#include <vector>
#include <game/client/component.h>
#include <game/generated/protocol.h>
#include <game/generated/protocol7.h>
#include <vector>
class CSkins7 : public CComponent
{
public:
@ -26,8 +29,9 @@ public:
HAT_OFFSET_SIDE = 2,
};
struct CSkinPart
class CSkinPart
{
public:
int m_Flags;
char m_aName[24];
IGraphics::CTextureHandle m_OrgTexture;
@ -37,13 +41,14 @@ public:
bool operator<(const CSkinPart &Other) { return str_comp_nocase(m_aName, Other.m_aName) < 0; }
};
struct CSkin
class CSkin
{
public:
int m_Flags;
char m_aName[24];
const CSkinPart *m_apParts[protocol7::NUM_SKINPARTS];
int m_aPartColors[protocol7::NUM_SKINPARTS];
int m_aUseCustomColors[protocol7::NUM_SKINPARTS];
unsigned m_aPartColors[protocol7::NUM_SKINPARTS];
bool operator<(const CSkin &Other) const { return str_comp_nocase(m_aName, Other.m_aName) < 0; }
bool operator==(const CSkin &Other) const { return !str_comp(m_aName, Other.m_aName); }
@ -56,23 +61,17 @@ public:
static char *ms_apSkinNameVariables[NUM_DUMMIES];
static char *ms_apSkinVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS];
static int *ms_apUCCVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS]; // use custom color
static unsigned int *ms_apColorVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS];
IGraphics::CTextureHandle m_XmasHatTexture;
IGraphics::CTextureHandle m_BotTexture;
static unsigned *ms_apColorVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS];
int GetInitAmount() const;
int Sizeof() const override { return sizeof(*this); }
void OnInit() override;
void AddSkin(const char *pSkinName, int Dummy);
bool RemoveSkin(const CSkin *pSkin);
int Num();
int NumSkinPart(int Part);
const CSkin *Get(int Index);
int Find(const char *pName, bool AllowSpecialSkin);
const CSkinPart *GetSkinPart(int Part, int Index);
int FindSkinPart(int Part, const char *pName, bool AllowSpecialPart);
void RandomizeSkin(int Dummy);
const std::vector<CSkin> &GetSkins() const;
const std::vector<CSkinPart> &GetSkinParts(int Part) const;
const CSkinPart *FindSkinPartOrNullptr(int Part, const char *pName, bool AllowSpecialPart) const;
const CSkinPart *FindDefaultSkinPart(int Part) const;
const CSkinPart *FindSkinPart(int Part, const char *pName, bool AllowSpecialPart) const;
void RandomizeSkin(int Dummy) const;
ColorRGBA GetColor(int Value, bool UseAlpha) const;
ColorRGBA GetTeamColor(int UseCustomColors, int PartColor, int Team, int Part) const;
@ -80,21 +79,30 @@ public:
// returns true if everything was valid and nothing changed
bool ValidateSkinParts(char *apPartNames[protocol7::NUM_SKINPARTS], int *pUseCustomColors, int *pPartColors, int GameFlags) const;
bool SaveSkinfile(const char *pSaveSkinName, int Dummy);
bool SaveSkinfile(const char *pName, int Dummy);
bool RemoveSkin(const CSkin *pSkin);
virtual int Sizeof() const override { return sizeof(*this); }
IGraphics::CTextureHandle XmasHatTexture() const { return m_XmasHatTexture; }
IGraphics::CTextureHandle BotDecorationTexture() const { return m_BotTexture; }
private:
int m_ScanningPart;
std::vector<CSkinPart> m_avSkinParts[protocol7::NUM_SKINPARTS];
CSkinPart m_aPlaceholderSkinParts[protocol7::NUM_SKINPARTS];
std::vector<CSkin> m_vSkins;
CSkin m_DummySkin;
IGraphics::CTextureHandle m_XmasHatTexture;
IGraphics::CTextureHandle m_BotTexture;
static int SkinPartScan(const char *pName, int IsDir, int DirType, void *pUser);
static int SkinScan(const char *pName, int IsDir, int DirType, void *pUser);
void InitPlaceholderSkinParts();
void LoadXmasHat();
void LoadBotDecoration();
void AddSkinFromConfigVariables(const char *pName, int Dummy);
};
#endif

View file

@ -94,7 +94,7 @@ void CGameClient::ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId, int Con
if(time_season() == SEASON_XMAS)
{
pClient->m_SkinInfo.m_aSixup[Conn].m_HatTexture = m_Skins7.m_XmasHatTexture;
pClient->m_SkinInfo.m_aSixup[Conn].m_HatTexture = m_Skins7.XmasHatTexture();
pClient->m_SkinInfo.m_aSixup[Conn].m_HatSpriteIndex = ClientId % CSkins7::HAT_NUM;
}
else
@ -102,8 +102,7 @@ void CGameClient::ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId, int Con
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
{
int Id = m_Skins7.FindSkinPart(Part, pClient->m_aSixup[Conn].m_aaSkinPartNames[Part], false);
const CSkins7::CSkinPart *pSkinPart = m_Skins7.GetSkinPart(Part, Id);
const CSkins7::CSkinPart *pSkinPart = m_Skins7.FindSkinPart(Part, pClient->m_aSixup[Conn].m_aaSkinPartNames[Part], false);
if(pClient->m_aSixup[Conn].m_aUseCustomColors[Part])
{
pClient->m_SkinInfo.m_aSixup[Conn].m_aTextures[Part] = pSkinPart->m_ColorTexture;