6459: Add random skin button r=def- a=HiRavie

Adds a button for generating a random skin in tee settings. The logic generates handsome tees more often than not.
Intentionally didn't add a console command so it's not possible to make annoying skin change binds with this.
![random_skin](https://user-images.githubusercontent.com/65019210/227658839-5e8f3a4b-4e7c-4561-90c9-21a267d61a3a.png)

## Checklist

- [x] Tested the change ingame
- [x] Provided screenshots if it is a visual change
- [x] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [x] Considered possible null pointers and out of bounds array indexing
- [x] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Ravie <65019210+HiRavie@users.noreply.github.com>
This commit is contained in:
bors[bot] 2023-03-25 23:08:15 +00:00 committed by GitHub
commit 214053ec9a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 8 deletions

View file

@ -105,6 +105,8 @@ class CMenus : public CComponent
void RefreshSkins(); void RefreshSkins();
void RandomSkin();
// new gui with gui elements // new gui with gui elements
template<typename T> template<typename T>
int DoButtonMenu(CUIElement &UIElement, const void *pID, T &&GetTextLambda, int Checked, const CUIRect *pRect, bool HintRequiresStringCheck, bool HintCanChangePositionOrSize = false, int Corners = IGraphics::CORNER_ALL, float r = 5.0f, float FontFactor = 0.0f, vec4 ColorHot = vec4(1.0f, 1.0f, 1.0f, 0.75f), vec4 Color = vec4(1, 1, 1, 0.5f), int AlignVertically = 1) int DoButtonMenu(CUIElement &UIElement, const void *pID, T &&GetTextLambda, int Checked, const CUIRect *pRect, bool HintRequiresStringCheck, bool HintCanChangePositionOrSize = false, int Corners = IGraphics::CORNER_ALL, float r = 5.0f, float FontFactor = 0.0f, vec4 ColorHot = vec4(1.0f, 1.0f, 1.0f, 0.75f), vec4 Color = vec4(1, 1, 1, 0.5f), int AlignVertically = 1)

View file

@ -438,6 +438,36 @@ void CMenus::RefreshSkins()
} }
} }
void CMenus::RandomSkin()
{
static const float s_aSchemes[] = {1.0f / 2.0f, 1.0f / 3.0f, 1.0f / -3.0f, 1.0f / 12.0f, 1.0f / -12.0f}; // complementary, triadic, analogous
float GoalSat = random_float(0.3f, 1.0f);
float MaxBodyLht = 1.0f - GoalSat * GoalSat; // max allowed lightness before we start losing saturation
ColorHSLA Body;
Body.h = random_float();
Body.l = random_float(0.0f, MaxBodyLht);
Body.s = clamp(GoalSat * GoalSat / (1.0f - Body.l), 0.0f, 1.0f);
ColorHSLA Feet;
Feet.h = std::fmod(Body.h + s_aSchemes[rand() % std::size(s_aSchemes)], 1.0f);
Feet.l = random_float();
Feet.s = clamp(GoalSat * GoalSat / (1.0f - Feet.l), 0.0f, 1.0f);
const char *pRandomSkinName = CSkins::VANILLA_SKINS[rand() % (std::size(CSkins::VANILLA_SKINS) - 2)]; // last 2 skins are x_ninja and x_spec
char *pSkinName = !m_Dummy ? g_Config.m_ClPlayerSkin : g_Config.m_ClDummySkin;
unsigned *pColorBody = !m_Dummy ? &g_Config.m_ClPlayerColorBody : &g_Config.m_ClDummyColorBody;
unsigned *pColorFeet = !m_Dummy ? &g_Config.m_ClPlayerColorFeet : &g_Config.m_ClDummyColorFeet;
mem_copy(pSkinName, pRandomSkinName, sizeof(g_Config.m_ClPlayerSkin));
*pColorBody = Body.Pack(false);
*pColorFeet = Feet.Pack(false);
SetNeedSendInfo();
}
void CMenus::Con_AddFavoriteSkin(IConsole::IResult *pResult, void *pUserData) void CMenus::Con_AddFavoriteSkin(IConsole::IResult *pResult, void *pUserData)
{ {
auto *pSelf = (CMenus *)pUserData; auto *pSelf = (CMenus *)pUserData;
@ -601,7 +631,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
OwnSkinInfo.m_Size = 50.0f; OwnSkinInfo.m_Size = 50.0f;
MainView.HSplitTop(50.0f, &Label, &MainView); MainView.HSplitTop(50.0f, &Label, &MainView);
Label.VSplitLeft(230.0f, &Label, 0); Label.VSplitLeft(260.0f, &Label, 0);
CAnimState *pIdleState = CAnimState::GetIdle(); CAnimState *pIdleState = CAnimState::GetIdle();
vec2 OffsetToMid; vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &OwnSkinInfo, OffsetToMid); RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &OwnSkinInfo, OffsetToMid);
@ -661,6 +691,8 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
RenderTools()->RenderTee(pIdleState, &OwnSkinInfo, CurrentEyeEmote, vec2(1, 0), vec2(EyesTee.x + 25.0f, EyesTee.y + EyesTee.h / 2.0f + OffsetToMid.y)); RenderTools()->RenderTee(pIdleState, &OwnSkinInfo, CurrentEyeEmote, vec2(1, 0), vec2(EyesTee.x + 25.0f, EyesTee.y + EyesTee.h / 2.0f + OffsetToMid.y));
} }
Label.VSplitRight(34.0f, &Label, &Button);
static float s_OffsetSkin = 0.0f; static float s_OffsetSkin = 0.0f;
static int s_ClearButton = 0; static int s_ClearButton = 0;
SUIExEditBoxProperties EditProps; SUIExEditBoxProperties EditProps;
@ -670,6 +702,22 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
SetNeedSendInfo(); SetNeedSendInfo();
} }
// random skin button
Button.VSplitRight(30.0f, 0, &Button);
static CButtonContainer s_RandomSkinButtonID;
static const char *s_apDice[] = {"\xef\x94\xa5", "\xef\x94\xa8", "\xef\x94\xa7", "\xef\x94\xa4", "\xef\x94\xa3", "\xef\x94\xa6"};
static int s_CurrentDie = rand() % std::size(s_apDice);
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_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_RandomSkinButtonID, s_apDice[s_CurrentDie], 1, &Button, nullptr, IGraphics::CORNER_ALL, 5.0f, -0.2f))
{
RandomSkin();
s_CurrentDie = rand() % std::size(s_apDice);
}
TextRender()->SetRenderFlags(0);
TextRender()->SetCurFont(nullptr);
GameClient()->m_Tooltips.DoToolTip(&s_RandomSkinButtonID, &Button, Localize("Create a random skin"));
// custom color selector // custom color selector
MainView.HSplitTop(20.0f + RenderEyesBelow * 25.0f, 0, &MainView); MainView.HSplitTop(20.0f + RenderEyesBelow * 25.0f, 0, &MainView);
MainView.HSplitTop(20.0f, &Button, &MainView); MainView.HSplitTop(20.0f, &Button, &MainView);
@ -1693,7 +1741,7 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
CUIRect Text; CUIRect Text;
MainView.HSplitTop(20.0f, 0, &MainView); MainView.HSplitTop(20.0f, 0, &MainView);
MainView.HSplitTop(20.0f, &Text, &MainView); MainView.HSplitTop(20.0f, &Text, &MainView);
//text.VSplitLeft(15.0f, 0, &text); // text.VSplitLeft(15.0f, 0, &text);
UI()->DoLabel(&Text, Localize("UI Color"), 14.0f, TEXTALIGN_LEFT); UI()->DoLabel(&Text, Localize("UI Color"), 14.0f, TEXTALIGN_LEFT);
CUIRect HSLBar = MainView; CUIRect HSLBar = MainView;
RenderHSLScrollbars(&HSLBar, &g_Config.m_UiColor, true); RenderHSLScrollbars(&HSLBar, &g_Config.m_UiColor, true);

View file

@ -17,12 +17,7 @@
#include "skins.h" #include "skins.h"
static const char *VANILLA_SKINS[] = {"bluekitty", "bluestripe", "brownbear", bool CSkins::IsVanillaSkin(const char *pName)
"cammo", "cammostripes", "coala", "default", "limekitty",
"pinky", "redbopp", "redstripe", "saddo", "toptri",
"twinbop", "twintri", "warpaint", "x_ninja", "x_spec"};
static bool IsVanillaSkin(const char *pName)
{ {
return std::any_of(std::begin(VANILLA_SKINS), std::end(VANILLA_SKINS), [pName](const char *pVanillaSkin) { return str_comp(pName, pVanillaSkin) == 0; }); return std::any_of(std::begin(VANILLA_SKINS), std::end(VANILLA_SKINS), [pName](const char *pVanillaSkin) { return str_comp(pName, pVanillaSkin) == 0; });
} }

View file

@ -69,6 +69,13 @@ public:
bool IsDownloadingSkins() { return m_DownloadingSkins; } bool IsDownloadingSkins() { return m_DownloadingSkins; }
static bool IsVanillaSkin(const char *pName);
constexpr static const char *VANILLA_SKINS[] = {"bluekitty", "bluestripe", "brownbear",
"cammo", "cammostripes", "coala", "default", "limekitty",
"pinky", "redbopp", "redstripe", "saddo", "toptri",
"twinbop", "twintri", "warpaint", "x_ninja", "x_spec"};
private: private:
std::unordered_map<std::string_view, std::unique_ptr<CSkin>> m_Skins; std::unordered_map<std::string_view, std::unique_ptr<CSkin>> m_Skins;
std::unordered_map<std::string_view, std::unique_ptr<CDownloadSkin>> m_DownloadSkins; std::unordered_map<std::string_view, std::unique_ptr<CDownloadSkin>> m_DownloadSkins;