/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include #include #include #include #include #include #include "skins.h" static const char* s_aaVanillaSkins[] = {"bluekitty.png", "bluestripe.png", "brownbear.png", "cammo.png", "cammostripes.png", "coala.png", "default.png", "limekitty.png", "pinky.png", "redbopp.png", "redstripe.png", "saddo.png", "toptri.png", "twinbop.png", "twintri.png", "warpaint.png", "x_ninja.png"}; int CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser) { if(g_Config.m_ClVanillaSkinsOnly) { bool Found = false; for(unsigned int i = 0; i < sizeof(s_aaVanillaSkins) / sizeof(s_aaVanillaSkins[0]); i++) { if(str_comp(pName, s_aaVanillaSkins[i]) == 0) { Found = true; break; } } if(!Found) return 0; } CSkins *pSelf = (CSkins *)pUser; int l = str_length(pName); if(l < 4 || IsDir || str_comp(pName+l-4, ".png") != 0) return 0; // Don't add duplicate skins (one from user's config directory, other from // client itself) for(int i = 0; i < pSelf->Num(); i++) { const char* pExName = pSelf->Get(i)->m_aName; if(str_comp_num(pExName, pName, l-4) == 0 && str_length(pExName) == l-4) return 0; } char aBuf[512]; str_format(aBuf, sizeof(aBuf), "skins/%s", pName); CImageInfo Info; if(!pSelf->Graphics()->LoadPNG(&Info, aBuf, DirType)) { str_format(aBuf, sizeof(aBuf), "failed to load skin from %s", pName); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf); return 0; } CSkin Skin; Skin.m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); int BodySize = 96; // body size if (BodySize > Info.m_Height) return 0; unsigned char *d = (unsigned char *)Info.m_pData; int Pitch = Info.m_Width*4; // dig out blood color { int aColors[3] = {0}; for(int y = 0; y < BodySize; y++) for(int x = 0; x < BodySize; x++) { if(d[y*Pitch+x*4+3] > 128) { aColors[0] += d[y*Pitch+x*4+0]; aColors[1] += d[y*Pitch+x*4+1]; aColors[2] += d[y*Pitch+x*4+2]; } } Skin.m_BloodColor = normalize(vec3(aColors[0], aColors[1], aColors[2])); } // create colorless version int Step = Info.m_Format == CImageInfo::FORMAT_RGBA ? 4 : 3; // make the texture gray scale for(int i = 0; i < Info.m_Width*Info.m_Height; i++) { int v = (d[i*Step]+d[i*Step+1]+d[i*Step+2])/3; d[i*Step] = v; d[i*Step+1] = v; d[i*Step+2] = v; } int Freq[256] = {0}; int OrgWeight = 0; int NewWeight = 192; // find most common frequence for(int y = 0; y < BodySize; y++) for(int x = 0; x < BodySize; x++) { if(d[y*Pitch+x*4+3] > 128) Freq[d[y*Pitch+x*4]]++; } for(int i = 1; i < 256; i++) { if(Freq[OrgWeight] < Freq[i]) OrgWeight = i; } // reorder int InvOrgWeight = 255-OrgWeight; int InvNewWeight = 255-NewWeight; for(int y = 0; y < BodySize; y++) for(int x = 0; x < BodySize; x++) { int v = d[y*Pitch+x*4]; if(v <= OrgWeight) v = (int)(((v/(float)OrgWeight) * NewWeight)); else v = (int)(((v-OrgWeight)/(float)InvOrgWeight)*InvNewWeight + NewWeight); d[y*Pitch+x*4] = v; d[y*Pitch+x*4+1] = v; d[y*Pitch+x*4+2] = v; } Skin.m_ColorTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); free(Info.m_pData); // set skin data str_copy(Skin.m_aName, pName, min((int)sizeof(Skin.m_aName),l-3)); if(g_Config.m_Debug) { str_format(aBuf, sizeof(aBuf), "load skin %s", Skin.m_aName); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf); } pSelf->m_aSkins.add(Skin); return 0; } void CSkins::OnInit() { // load skins m_aSkins.clear(); Storage()->ListDirectory(IStorage::TYPE_ALL, "skins", SkinScan, this); if(!m_aSkins.size()) { Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gameclient", "failed to load skins. folder='skins/'"); CSkin DummySkin; DummySkin.m_OrgTexture = -1; DummySkin.m_ColorTexture = -1; str_copy(DummySkin.m_aName, "dummy", sizeof(DummySkin.m_aName)); DummySkin.m_BloodColor = vec3(1.0f, 1.0f, 1.0f); m_aSkins.add(DummySkin); } } int CSkins::Num() { return m_aSkins.size(); } const CSkins::CSkin *CSkins::Get(int Index) { return &m_aSkins[max(0, Index%m_aSkins.size())]; } int CSkins::Find(const char *pName) { char aBuf[24]; if(g_Config.m_ClKittySkins) { for(unsigned int i = 0; i < sizeof(s_aaVanillaSkins) / sizeof(s_aaVanillaSkins[0]); i++) { // index 0 and 7 are bluekitty and limekitty if(i != 0 && i != 7 && str_comp_num(pName, s_aaVanillaSkins[i], str_length(s_aaVanillaSkins[i])-4) == 0) { str_format(aBuf, sizeof(aBuf), "kitty_%s", pName); pName = aBuf; break; } } } for(int i = 0; i < m_aSkins.size(); i++) { if(str_comp(m_aSkins[i].m_aName, pName) == 0) return i; } return -1; } vec3 CSkins::GetColorV3(int v) { return HslToRgb(vec3(((v>>16)&0xff)/255.0f, ((v>>8)&0xff)/255.0f, 0.5f+(v&0xff)/255.0f*0.5f)); } vec4 CSkins::GetColorV4(int v) { vec3 r = GetColorV3(v); return vec4(r.r, r.g, r.b, 1.0f); }