ddnet/src/game/client/components/skins.cpp

232 lines
5.5 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (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. */
2008-03-17 01:41:11 +00:00
#include <math.h>
#include <base/system.h>
2010-05-29 07:25:38 +00:00
#include <base/math.h>
2010-05-29 07:25:38 +00:00
#include <engine/graphics.h>
#include <engine/storage.h>
#include <engine/shared/config.h>
2010-05-29 07:25:38 +00:00
#include "skins.h"
2018-07-24 15:26:39 +00:00
static const char *VANILLA_SKINS[] = {"bluekitty", "bluestripe", "brownbear",
"cammo", "cammostripes", "coala", "default", "limekitty",
"pinky", "redbopp", "redstripe", "saddo", "toptri",
"twinbop", "twintri", "warpaint", "x_ninja"};
2018-07-24 15:26:39 +00:00
static bool IsVanillaSkin(const char *pName)
{
2018-07-24 15:26:39 +00:00
for(unsigned int i = 0; i < sizeof(VANILLA_SKINS) / sizeof(VANILLA_SKINS[0]); i++)
{
2018-07-24 15:26:39 +00:00
if(str_comp(pName, VANILLA_SKINS[i]) == 0)
{
2018-07-24 15:26:39 +00:00
return true;
}
}
2018-07-24 15:26:39 +00:00
return false;
}
2018-07-24 15:26:39 +00:00
int CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser)
{
2010-05-29 07:25:38 +00:00
CSkins *pSelf = (CSkins *)pUser;
2014-11-25 19:13:40 +00:00
if(IsDir || !str_endswith(pName, ".png"))
return 0;
char aNameWithoutPng[128];
str_copy(aNameWithoutPng, pName, sizeof(aNameWithoutPng));
aNameWithoutPng[str_length(aNameWithoutPng) - 4] = 0;
2018-07-24 15:26:39 +00:00
2014-11-25 19:13:40 +00:00
// Don't add duplicate skins (one from user's config directory, other from
// client itself)
for(int i = 0; i < pSelf->Num(); i++)
{
2018-07-24 15:26:39 +00:00
const char *pExName = pSelf->Get(i)->m_aName;
if(str_comp(pExName, aNameWithoutPng) == 0)
2014-11-25 19:13:40 +00:00
return 0;
}
2010-05-29 07:25:38 +00:00
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "skins/%s", pName);
CImageInfo Info;
2010-10-06 21:07:35 +00:00
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_IsVanilla = IsVanillaSkin(aNameWithoutPng);
2018-07-25 21:36:52 +00:00
Skin.m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0);
2010-05-29 07:25:38 +00:00
int BodySize = 96; // body size
if (BodySize > Info.m_Height)
return 0;
2010-05-29 07:25:38 +00:00
unsigned char *d = (unsigned char *)Info.m_pData;
int Pitch = Info.m_Width*4;
2008-03-17 01:41:11 +00:00
// dig out blood color
{
2010-05-29 07:25:38 +00:00
int aColors[3] = {0};
for(int y = 0; y < BodySize; y++)
for(int x = 0; x < BodySize; x++)
2008-03-17 01:41:11 +00:00
{
2010-05-29 07:25:38 +00:00
if(d[y*Pitch+x*4+3] > 128)
2008-03-17 01:41:11 +00:00
{
2010-05-29 07:25:38 +00:00
aColors[0] += d[y*Pitch+x*4+0];
aColors[1] += d[y*Pitch+x*4+1];
aColors[2] += d[y*Pitch+x*4+2];
2008-03-17 01:41:11 +00:00
}
}
2019-04-26 12:06:32 +00:00
Skin.m_BloodColor = ColorRGBA(normalize(vec3(aColors[0], aColors[1], aColors[2])));
2008-03-17 01:41:11 +00:00
}
2008-03-17 01:41:11 +00:00
// create colorless version
2010-05-29 07:25:38 +00:00
int Step = Info.m_Format == CImageInfo::FORMAT_RGBA ? 4 : 3;
// make the texture gray scale
2010-05-29 07:25:38 +00:00
for(int i = 0; i < Info.m_Width*Info.m_Height; i++)
{
2010-05-29 07:25:38 +00:00
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;
}
2008-03-17 01:41:11 +00:00
2011-02-13 12:58:59 +00:00
int Freq[256] = {0};
int OrgWeight = 0;
int NewWeight = 192;
2011-02-13 12:58:59 +00:00
// find most common frequence
for(int y = 0; y < BodySize; y++)
for(int x = 0; x < BodySize; x++)
2007-11-26 22:26:33 +00:00
{
2011-02-13 12:58:59 +00:00
if(d[y*Pitch+x*4+3] > 128)
Freq[d[y*Pitch+x*4]]++;
2007-11-26 22:26:33 +00:00
}
2011-02-13 12:58:59 +00:00
for(int i = 1; i < 256; i++)
{
if(Freq[OrgWeight] < Freq[i])
OrgWeight = i;
2007-11-26 22:26:33 +00:00
}
2011-02-13 12:58:59 +00:00
// 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, aNameWithoutPng, sizeof(Skin.m_aName));
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;
}
2011-02-13 12:58:59 +00:00
void CSkins::OnInit()
{
2018-12-23 21:53:10 +00:00
m_EventSkinPrefix[0] = '\0';
if(g_Config.m_Events)
{
time_t rawtime;
struct tm* timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
if(timeinfo->tm_mon == 11 && timeinfo->tm_mday >= 24 && timeinfo->tm_mday <= 26)
{ // Christmas
str_copy(m_EventSkinPrefix, "santa", sizeof(m_EventSkinPrefix));
}
}
// load skins
m_aSkins.clear();
2010-05-29 07:25:38 +00:00
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_IsVanilla = true;
DummySkin.m_OrgTexture = -1;
DummySkin.m_ColorTexture = -1;
str_copy(DummySkin.m_aName, "dummy", sizeof(DummySkin.m_aName));
2019-04-26 12:06:32 +00:00
DummySkin.m_BloodColor = ColorRGBA(1.0f, 1.0f, 1.0f);
m_aSkins.add(DummySkin);
}
}
2010-05-29 07:25:38 +00:00
int CSkins::Num()
{
return m_aSkins.size();
}
2010-05-29 07:25:38 +00:00
const CSkins::CSkin *CSkins::Get(int Index)
{
2018-07-25 20:41:24 +00:00
if(Index < 0)
{
Index = Find("default");
if (Index < 0)
Index = 0;
}
return &m_aSkins[Index % m_aSkins.size()];
}
2018-07-24 15:26:39 +00:00
int CSkins::Find(const char *pName) const
{
2018-12-23 21:53:10 +00:00
const char *pSkinPrefix = m_EventSkinPrefix[0] ? m_EventSkinPrefix : g_Config.m_ClSkinPrefix;
if(g_Config.m_ClVanillaSkinsOnly && !IsVanillaSkin(pName))
{
return -1;
}
2018-12-23 21:53:10 +00:00
else if(pSkinPrefix)
{
char aBuf[64];
2018-12-23 21:53:10 +00:00
str_format(aBuf, sizeof(aBuf), "%s_%s", pSkinPrefix, pName);
// If we find something, use it, otherwise fall back to normal skins.
int Result = FindImpl(aBuf);
2018-08-21 16:29:25 +00:00
if(Result != -1)
2018-08-13 18:54:01 +00:00
{
return Result;
}
}
2018-07-24 15:26:39 +00:00
return FindImpl(pName);
}
int CSkins::FindImpl(const char *pName) const
{
for(int i = 0; i < m_aSkins.size(); i++)
{
2010-05-29 07:25:38 +00:00
if(str_comp(m_aSkins[i].m_aName, pName) == 0)
2018-07-24 15:26:39 +00:00
{
return i;
2018-07-24 15:26:39 +00:00
}
}
return -1;
}