diff --git a/src/engine/textrender.h b/src/engine/textrender.h index 911a99a7977..352621e8eb4 100644 --- a/src/engine/textrender.h +++ b/src/engine/textrender.h @@ -79,6 +79,8 @@ MAYBE_UNUSED static const char *FONT_ICON_FLAG_CHECKERED = "\xEF\x84\x9E"; MAYBE_UNUSED static const char *FONT_ICON_BAN = "\xEF\x81\x9E"; MAYBE_UNUSED static const char *FONT_ICON_CIRCLE_CHEVRON_DOWN = "\xEF\x84\xBA"; MAYBE_UNUSED static const char *FONT_ICON_KEY = "\xEF\x82\x84"; +MAYBE_UNUSED static const char *FONT_ICON_USERS = "\xEF\x83\x80"; +MAYBE_UNUSED static const char *FONT_ICON_COMMENT = "\xEF\x81\xB5"; MAYBE_UNUSED static const char *FONT_ICON_SQUARE_MINUS = "\xEF\x85\x86"; MAYBE_UNUSED static const char *FONT_ICON_SQUARE_PLUS = "\xEF\x83\xBE"; MAYBE_UNUSED static const char *FONT_ICON_SORT_UP = "\xEF\x83\x9E"; diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 254a462bb0c..aa34df1dabd 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -849,8 +849,10 @@ class CMenus : public CComponent // found in menus_tclient.cpp void RenderSettingsTClient(CUIRect MainView); void RenderSettingsProfiles(CUIRect MainView); - void RenderDevSkin(vec2 RenderPos, float Size, const char *pSkinName, const char *pBackupSkin, bool CustomColors, int FeetColor, int BodyColor, int Emote, bool Rainbow); + void RenderSettingsWarList(CUIRect MainView); + void RenderDevSkin(vec2 RenderPos, float Size, const char *pSkinName, const char *pBackupSkin, bool CustomColors, int FeetColor, int BodyColor, int Emote, bool Rainbow); + void RenderFontIcon(const CUIRect Rect, const char *pText, float Size, int Align); ColorHSLA RenderHSLColorPicker(const CUIRect *pRect, unsigned int *pColor, bool Alpha); bool RenderHslaScrollbars(CUIRect *pRect, unsigned int *pColor, bool Alpha, float DarkestLight); diff --git a/src/game/client/components/tclient/menus_tclient.cpp b/src/game/client/components/tclient/menus_tclient.cpp index 5dd61ef640f..3f5057f86f0 100644 --- a/src/game/client/components/tclient/menus_tclient.cpp +++ b/src/game/client/components/tclient/menus_tclient.cpp @@ -69,6 +69,8 @@ static bool s_StartedTime = false; const float LineSize = 20.0f; const float ColorPickerLineSize = 25.0f; const float HeadlineFontSize = 20.0f; +const float StandardFontSize = 14.0f; + const float HeadlineHeight = HeadlineFontSize + 0.0f; const float Margin = 10.0f; const float MarginSmall = 5.0f; @@ -726,6 +728,11 @@ void CMenus::RenderSettingsTClient(CUIRect MainView) DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClResetBindWheelMouse, Localize("Reset position of mouse when opening bindwheel"), &g_Config.m_ClResetBindWheelMouse, &Button, LineSize); } + if(s_CurCustomTab == TCLIENT_TAB_WARLIST) + { + RenderSettingsWarList(MainView); + } + if(s_CurCustomTab == TCLIENT_TAB_INFO) { MainView.HSplitTop(MarginSmall, nullptr, &MainView); @@ -819,6 +826,254 @@ void CMenus::RenderSettingsTClient(CUIRect MainView) } } +void CMenus::RenderSettingsWarList(CUIRect MainView) +{ + CUIRect RightView, LeftView, Column1, Column2, Column3, Column4, Button, ButtonL, ButtonR, Label; + + MainView.HSplitTop(MarginSmall, nullptr, &MainView); + MainView.VSplitMid(&LeftView, &RightView, MarginBetweenViews); + LeftView.VSplitLeft(MarginSmall, nullptr, &LeftView); + RightView.VSplitRight(MarginSmall, &RightView, nullptr); + + // WAR LIST will have 4 columns + // [War entries] - [Entry Editing] - [Group Types] - [Recent Players] + // [Group Editing] + + // putting this here so it can be updated by the entry list + static char s_aEntryName[MAX_NAME_LENGTH]; + static char s_aEntryClan[MAX_CLAN_LENGTH]; + static char s_aEntryReason[MAX_WARLIST_REASON_LENGTH]; + static int s_IsClan = 1; + static int s_IsName = 0; + + LeftView.VSplitMid(&Column1, &Column2, Margin); + RightView.VSplitMid(&Column3, &Column4, Margin); + + // ======WAR ENTRIES====== + Column1.HSplitTop(HeadlineHeight, &Label, &Column1); + Ui()->DoLabel(&Label, Localize("War Entries"), HeadlineFontSize, TEXTALIGN_ML); + Column1.HSplitTop(MarginSmall, nullptr, &Column1); + CUIRect EntriesSearch; + + Column1.HSplitBottom(25.0f, &Column1, &EntriesSearch); + EntriesSearch.HSplitTop(MarginSmall, nullptr, &EntriesSearch); + + static CWarEntry *pSelectedEntry = nullptr; + static CWarType *pSelectedType = nullptr; + + // Filter the list + static CLineInputBuffered<128> s_EntriesFilterInput; + std::vector vpFilteredEntries; + for(size_t i = 0; i < GameClient()->m_WarList.m_WarEntries.size(); ++i) + { + CWarEntry *pEntry = &GameClient()->m_WarList.m_WarEntries[i]; + bool Matches = false; + if(str_find_nocase(pEntry->m_aName, s_EntriesFilterInput.GetString())) + Matches = true; + if(str_find_nocase(pEntry->m_aClan, s_EntriesFilterInput.GetString())) + Matches = true; + if(str_find_nocase(pEntry->m_pWarType->m_aWarName, s_EntriesFilterInput.GetString())) + Matches = true; + if(Matches) + vpFilteredEntries.push_back(pEntry); + } + + int SelectedOld = -1; + static CListBox s_EntriesListBox; + s_EntriesListBox.DoStart(35.0f, vpFilteredEntries.size(), 1, 2, SelectedOld, &Column1); + + static std::vector s_vItemIds; + static std::vector s_vToolTipIds; + static std::vector s_vDeleteButtons; + + const int MaxEntries = GameClient()->m_WarList.m_WarEntries.size(); + s_vItemIds.resize(MaxEntries); + s_vDeleteButtons.resize(MaxEntries); + s_vToolTipIds.resize(MaxEntries); + + for(size_t i = 0; i < vpFilteredEntries.size(); i++) + { + CWarEntry *pEntry = vpFilteredEntries[i]; + + // idk why it wants this, it was complaining + if(!pEntry) + continue; + + if(pSelectedEntry && pEntry == pSelectedEntry) + SelectedOld = i; + + const CListboxItem Item = s_EntriesListBox.DoNextItem(&s_vItemIds[i], SelectedOld >= 0 && (size_t)SelectedOld == i); + if(!Item.m_Visible) + continue; + + CUIRect EntryRect, DeleteButton, EntryTypeRect, WarType, ToolTip; + Item.m_Rect.Margin(0.0f, &EntryRect); + EntryRect.VSplitLeft(26.0f, &DeleteButton, &EntryRect); + DeleteButton.HMargin(7.5f, &DeleteButton); + DeleteButton.VSplitLeft(MarginSmall, nullptr, &DeleteButton); + DeleteButton.VSplitRight(MarginExtraSmall, &DeleteButton, nullptr); + + if(DoButton_FontIcon(&s_vDeleteButtons[i], FONT_ICON_TRASH, 0, &DeleteButton, IGraphics::CORNER_ALL)) + GameClient()->m_WarList.RemoveWarEntry(pEntry); + + bool IsClan = false; + char aBuf[32]; + if(str_comp(pEntry->m_aClan, "") != 0) + { + str_copy(aBuf, pEntry->m_aClan); + IsClan = true; + } + else + { + str_copy(aBuf, pEntry->m_aName); + } + EntryRect.VSplitLeft(35.0f, &EntryTypeRect, &EntryRect); + + if(IsClan) + { + RenderFontIcon(EntryTypeRect, FONT_ICON_USERS, 18.0f, TEXTALIGN_MC); + } + else + { + // TODO: stop misusing this function + // TODO: render the real skin with skin remembering component (to be added) + RenderDevSkin(EntryTypeRect.Center(), 35.0f, "defualt", "default", false, 0, 0, 0, false); + } + + if(str_comp(pEntry->m_aReason, "") != 0) + { + EntryRect.VSplitRight(20.0f, &EntryRect, &ToolTip); + RenderFontIcon(ToolTip, FONT_ICON_COMMENT, 18.0f, TEXTALIGN_MC); + GameClient()->m_Tooltips.DoToolTip(&s_vItemIds[i], &ToolTip, pEntry->m_aReason); + GameClient()->m_Tooltips.SetFadeTime(&s_vItemIds[i], 0.0f); + } + + EntryRect.HMargin(MarginExtraSmall, &EntryRect); + EntryRect.HSplitMid(&EntryRect, &WarType, MarginSmall); + + Ui()->DoLabel(&EntryRect, aBuf, StandardFontSize, TEXTALIGN_ML); + TextRender()->TextColor(pEntry->m_pWarType->m_Color); + Ui()->DoLabel(&WarType, pEntry->m_pWarType->m_aWarName, StandardFontSize, TEXTALIGN_ML); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + } + const int NewSelected = s_EntriesListBox.DoEnd(); + if(SelectedOld != NewSelected || (NewSelected >= 0 && Ui()->HotItem() == &s_vItemIds[NewSelected] && Ui()->MouseButtonClicked(0))) + { + pSelectedEntry = vpFilteredEntries[NewSelected]; + if(!Ui()->LastMouseButton(1) && !Ui()->LastMouseButton(2)) + { + str_copy(s_aEntryName, pSelectedEntry->m_aName); + str_copy(s_aEntryClan, pSelectedEntry->m_aClan); + str_copy(s_aEntryReason, pSelectedEntry->m_aReason); + if(str_comp(pSelectedEntry->m_aClan, "") != 0) + { + s_IsName = 0; + s_IsClan = 1; + } + else + { + s_IsName = 1; + s_IsClan = 0; + } + pSelectedType = pSelectedEntry->m_pWarType; + } + } + + Ui()->DoEditBox_Search(&s_EntriesFilterInput, &EntriesSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()); + + // ======WAR ENTRY EDITING====== + + Column2.HSplitTop(HeadlineHeight, nullptr, &Column2); + Column2.HSplitTop(MarginSmall, nullptr, &Column2); + Column2.HSplitTop(HeadlineFontSize, &Button, &Column2); + + Button.VSplitMid(&ButtonL, &ButtonR, MarginSmall); + static CLineInput s_NameInput; + s_NameInput.SetBuffer(s_aEntryName, sizeof(s_aEntryName)); + s_NameInput.SetEmptyText("Name"); + if(s_IsName) + Ui()->DoEditBox(&s_NameInput, &ButtonL, 12.0f); + else + { + ButtonL.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), 15, 3.0f); + Ui()->ClipEnable(&ButtonL); + ButtonL.VMargin(2.0f, &ButtonL); + const STextBoundingBox BoundingBox = s_NameInput.Render(&ButtonL, 12.0f, TEXTALIGN_ML, false, -1.0f, 0.0f); + Ui()->ClipDisable(); + } + + static CLineInput s_ClanInput; + s_ClanInput.SetBuffer(s_aEntryClan, sizeof(s_aEntryClan)); + s_ClanInput.SetEmptyText("Clan"); + if(s_IsClan) + Ui()->DoEditBox(&s_ClanInput, &ButtonR, 12.0f); + else + { + ButtonR.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), 15, 3.0f); + Ui()->ClipEnable(&ButtonR); + ButtonR.VMargin(2.0f, &ButtonR); + const STextBoundingBox BoundingBox = s_ClanInput.Render(&ButtonR, 12.0f, TEXTALIGN_ML, false, -1.0f, 0.0f); + Ui()->ClipDisable(); + } + Column2.HSplitTop(MarginSmall, nullptr, &Column2); + Column2.HSplitTop(HeadlineFontSize, &Button, &Column2); + static CLineInput s_ReasonInput; + s_ReasonInput.SetBuffer(s_aEntryReason, sizeof(s_aEntryReason)); + s_ReasonInput.SetEmptyText("Reason"); + Ui()->DoEditBox(&s_ReasonInput, &Button, 12.0f); + + Column2.HSplitTop(MarginSmall, nullptr, &Column2); + Column2.HSplitTop(LineSize, &Button, &Column2); + Button.VSplitMid(&ButtonL, &ButtonR, MarginSmall); + static unsigned char s_NameRadio, s_ClanRadio; + if(DoButton_CheckBox_Common(&s_NameRadio, "Name", s_IsName ? "X" : "", &ButtonL)) + { + s_IsName = 1; + s_IsClan = 0; + } + if(DoButton_CheckBox_Common(&s_ClanRadio, "Clan", s_IsClan ? "X" : "", &ButtonR)) + { + s_IsName = 0; + s_IsClan = 1; + } + if(!s_IsName) + str_copy(s_aEntryName, ""); + if(!s_IsClan) + str_copy(s_aEntryClan, ""); + + static CButtonContainer s_AddButton, s_OverrideButton; + + Column2.HSplitTop(MarginSmall, nullptr, &Column2); + Column2.HSplitTop(LineSize, &Button, &Column2); + Button.VSplitMid(&ButtonL, &ButtonR, MarginSmall); + + if(DoButton_Menu(&s_OverrideButton, Localize("Override Entry"), 0, &ButtonL) && pSelectedEntry) + { + if(pSelectedEntry && pSelectedType) + { + str_copy(pSelectedEntry->m_aName, s_aEntryName); + str_copy(pSelectedEntry->m_aClan, s_aEntryClan); + str_copy(pSelectedEntry->m_aReason, s_aEntryReason); + pSelectedEntry->m_pWarType = pSelectedType; + } + } + if(DoButton_Menu(&s_AddButton, Localize("Add Entry"), 0, &ButtonR)) + { + if(pSelectedType) + GameClient()->m_WarList.AddWarEntry(s_aEntryName, s_aEntryClan, s_aEntryReason, pSelectedType->m_aWarName); + } + Column2.HSplitTop(MarginSmall, nullptr, &Column2); + Column2.HSplitTop(HeadlineFontSize + MarginSmall, &Button, &Column2); + if(pSelectedType) + { + float Shade = 0.0f; + Button.Draw(ColorRGBA(Shade, Shade, Shade, 0.25f), 15, 3.0f); + TextRender()->TextColor(pSelectedType->m_Color); + Ui()->DoLabel(&Button, pSelectedType->m_aWarName, HeadlineFontSize, TEXTALIGN_MC); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + } +} + void CMenus::RenderSettingsProfiles(CUIRect MainView) { CUIRect Label, LabelMid, Section, LabelRight; @@ -1253,7 +1508,7 @@ void CMenus::RenderDevSkin(vec2 RenderPos, float Size, const char *pSkinName, co SkinInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); SkinInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); } - if (Rainbow) + if(Rainbow) { ColorRGBA Col = color_cast(ColorHSLA(DefTick, 1.0f, 0.5f)); SkinInfo.m_ColorBody = Col; @@ -1266,3 +1521,12 @@ void CMenus::RenderDevSkin(vec2 RenderPos, float Size, const char *pSkinName, co vec2 TeeRenderPos(RenderPos.x, RenderPos.y + OffsetToMid.y); RenderTools()->RenderTee(pIdleState, &SkinInfo, Emote, vec2(1.0f, 0.0f), TeeRenderPos); } + +void CMenus::RenderFontIcon(const CUIRect Rect, const char *pText, float Size, int Align) +{ + 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); + Ui()->DoLabel(&Rect, pText, Size, Align); + TextRender()->SetRenderFlags(0); + TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); +} diff --git a/src/game/client/components/tclient/warlist.cpp b/src/game/client/components/tclient/warlist.cpp index a5bff05615d..88b8675ec83 100644 --- a/src/game/client/components/tclient/warlist.cpp +++ b/src/game/client/components/tclient/warlist.cpp @@ -118,8 +118,8 @@ void CWarList::UpsertWarType(int Index, const char *pType, ColorRGBA Color) if(Index >= 0 && Index < static_cast(m_WarTypes.size())) { - str_copy(m_WarTypes[Index].m_aWarType, pType); - m_WarTypes[Index].m_Color = Color; + str_copy(m_WarTypes[Index]->m_aWarName, pType); + m_WarTypes[Index]->m_Color = Color; } else { @@ -129,7 +129,16 @@ void CWarList::UpsertWarType(int Index, const char *pType, ColorRGBA Color) void CWarList::AddWarEntry(const char *pName, const char *pClan, const char *pReason, const char *pType) { + if(str_comp(pName, "") == 0 && str_comp(pClan, "") == 0) + return; + CWarType *WarType = FindWarType(pType); + if(WarType == &m_WarTypeNone) + { + AddWarType(pType, ColorRGBA(0, 0, 1, 1)); + WarType = FindWarType(pType); + } + CWarEntry Entry(WarType); str_copy(Entry.m_aReason, pReason); @@ -137,21 +146,19 @@ void CWarList::AddWarEntry(const char *pName, const char *pClan, const char *pRe str_copy(Entry.m_aClan, pClan); else if(str_comp(pName, "") != 0) str_copy(Entry.m_aName, pName); - else - return; m_WarEntries.push_back(Entry); } void CWarList::AddWarType(const char *pType, ColorRGBA Color) { - if(str_comp(pType, "none")) + if(str_comp(pType, "none") == 0) return; CWarType *Type = FindWarType(pType); - if(*Type == m_WarTypeNone) + if(Type == &m_WarTypeNone) { - CWarType NewType(pType, Color); + CWarType *NewType = new CWarType(pType, Color); m_WarTypes.push_back(NewType); } else @@ -169,20 +176,30 @@ void CWarList::RemoveWarEntry(const char *pName, const char *pClan, const char * m_WarEntries.erase(it); } + +void CWarList::RemoveWarEntry(CWarEntry *Entry) +{ + auto it = std::find_if(m_WarEntries.begin(), m_WarEntries.end(), + [Entry](const CWarEntry &WarEntry) {return &WarEntry == Entry;}); + if(it != m_WarEntries.end()) + m_WarEntries.erase(it); +} void CWarList::RemoveWarType(const char *pType) { CWarType Type(pType); - auto it = std::find(m_WarTypes.begin(), m_WarTypes.end(), Type); + + auto it = std::find_if(m_WarTypes.begin(), m_WarTypes.end(), + [&Type](CWarType *warTypePtr) { return *warTypePtr == Type; }); if(it != m_WarTypes.end()) { // Don't remove default war types - if(!it->m_Removable) + if(!(*it)->m_Removable) return; // Find all war entries and set them to None if they are using this type for(CWarEntry &Entry : m_WarEntries) { - if(*Entry.m_pWarType == *it) + if(*Entry.m_pWarType == **it) { Entry.m_pWarType = &m_WarTypeNone; } @@ -194,9 +211,10 @@ void CWarList::RemoveWarType(const char *pType) CWarType *CWarList::FindWarType(const char *pType) { CWarType Type(pType); - auto it = std::find(m_WarTypes.begin(), m_WarTypes.end(), Type); + auto it = std::find_if(m_WarTypes.begin(), m_WarTypes.end(), + [&Type](CWarType *warTypePtr) {return *warTypePtr == Type;}); if(it != m_WarTypes.end()) - return &(*it); + return *it; else return &m_WarTypeNone; } @@ -292,14 +310,14 @@ void CWarList::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserDat char aBuf[1024]; for(int i = 0; i < static_cast(pThis->m_WarTypes.size()); i++) { - CWarType &WarType = pThis->m_WarTypes[i]; + CWarType &WarType = *pThis->m_WarTypes[i]; // Imported wartypes don't get saved if(WarType.m_Imported) continue; char aEscapeType[MAX_WARLIST_TYPE_LENGTH * 2]; - EscapeParam(aEscapeType, WarType.m_aWarType, sizeof(aEscapeType)); + EscapeParam(aEscapeType, WarType.m_aWarName, sizeof(aEscapeType)); ColorHSLA Color = color_cast(WarType.m_Color); str_format(aBuf, sizeof(aBuf), "update_war_type %d \"%s\" %d", i, aEscapeType, Color.Pack(false)); @@ -315,7 +333,7 @@ void CWarList::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserDat char aEscapeName[MAX_NAME_LENGTH * 2]; char aEscapeClan[MAX_CLAN_LENGTH * 2]; char aEscapeReason[MAX_WARLIST_REASON_LENGTH * 2]; - EscapeParam(aEscapeType, Entry.m_pWarType->m_aWarType, sizeof(aEscapeType)); + EscapeParam(aEscapeType, Entry.m_pWarType->m_aWarName, sizeof(aEscapeType)); EscapeParam(aEscapeName, Entry.m_aName, sizeof(aEscapeName)); EscapeParam(aEscapeClan, Entry.m_aClan, sizeof(aEscapeClan)); EscapeParam(aEscapeReason, Entry.m_aReason, sizeof(aEscapeReason)); diff --git a/src/game/client/components/tclient/warlist.h b/src/game/client/components/tclient/warlist.h index beb0f286938..57e3dabb30f 100644 --- a/src/game/client/components/tclient/warlist.h +++ b/src/game/client/components/tclient/warlist.h @@ -18,14 +18,15 @@ enum class CWarType { public: - char m_aWarType[MAX_WARLIST_TYPE_LENGTH] = ""; + // Intentionally named redundantly because it was confusing otherwise + char m_aWarName[MAX_WARLIST_TYPE_LENGTH] = ""; ColorRGBA m_Color = ColorRGBA(1, 1, 1, 1); bool m_Removable = true; bool m_Imported = false; CWarType(const char *pName, ColorRGBA Color = ColorRGBA(1, 1, 1, 1), bool Removable = true, bool IsImport = false) { - str_copy(m_aWarType, pName); + str_copy(m_aWarName, pName); m_Color = Color; m_Removable = Removable; m_Imported = IsImport; @@ -33,7 +34,7 @@ class CWarType bool operator==(const CWarType &Other) const { - return str_comp(m_aWarType, Other.m_aWarType) == 0; + return str_comp(m_aWarName, Other.m_aWarName) == 0; } }; @@ -135,8 +136,10 @@ class CWarList : public CComponent CWarType m_WarTypeNone = CWarType("none", ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f), false); // duplicate war types are NOT allowed - std::vector m_WarTypes = {CWarType("war", ColorRGBA(1.0f, 0.2f, 0.2f, 1.0f), false), CWarType("team", ColorRGBA(0.0f, 0.9f, 0.2f, 1.0f), false)}; - + std::vector m_WarTypes = { + new CWarType("enemy", ColorRGBA(1.0f, 0.2f, 0.2f, 1.0f), false), + new CWarType("team", ColorRGBA(0.0f, 0.9f, 0.2f, 1.0f), false) + }; // TODO: add a special backend command that allows for changing the names of the default war types // Duplicate war entries ARE allowed @@ -163,6 +166,8 @@ class CWarList : public CComponent void RemoveWarEntry(const char *pName, const char *pClan, const char *pType); void RemoveWarType(const char *pType); + void RemoveWarEntry(CWarEntry *Entry); + void RemoveWarEntry(int Index); void RemoveWarType(int Index); diff --git a/src/game/client/components/tooltips.cpp b/src/game/client/components/tooltips.cpp index ed1cbba9f7f..fc04a24e01a 100644 --- a/src/game/client/components/tooltips.cpp +++ b/src/game/client/components/tooltips.cpp @@ -26,6 +26,17 @@ inline void CTooltips::ClearActiveTooltip() m_PreviousTooltip.reset(); } +// TClient +void CTooltips::SetFadeTime(const void *pId, float Time) +{ + uintptr_t Id = reinterpret_cast(pId); + const auto it = m_Tooltips.find(Id); + if(it != m_Tooltips.end()) + { + it->second.m_FadeTime = Time; + } +} + void CTooltips::DoToolTip(const void *pId, const CUIRect *pNearRect, const char *pText, float WidthHint) { uintptr_t Id = reinterpret_cast(pId); @@ -75,7 +86,10 @@ void CTooltips::OnRender() m_PreviousTooltip.emplace(Tooltip); // Delay tooltip until 1 second passed. Start fade-in in the last 0.25 seconds. - constexpr float SecondsBeforeFadeIn = 0.75f; + float SecondsBeforeFadeIn = 0.75f; + + SecondsBeforeFadeIn = Tooltip.m_FadeTime; + const float SecondsSinceActivation = (time_get() - m_HoverTime) / (float)time_freq(); if(SecondsSinceActivation < SecondsBeforeFadeIn) return; diff --git a/src/game/client/components/tooltips.h b/src/game/client/components/tooltips.h index b2b98b363b0..d8c144f13fb 100644 --- a/src/game/client/components/tooltips.h +++ b/src/game/client/components/tooltips.h @@ -16,6 +16,7 @@ struct CTooltip const char *m_pText; float m_WidthHint; bool m_OnScreen; // used to know if the tooltip should be rendered. + float m_FadeTime = 0.75; }; /** @@ -54,6 +55,9 @@ class CTooltips : public CComponent */ void DoToolTip(const void *pId, const CUIRect *pNearRect, const char *pText, float WidthHint = -1.0f); + // TClient + void SetFadeTime(const void *pId, float Time); + virtual void OnReset() override; virtual void OnRender() override; }; diff --git a/src/game/client/ui.h b/src/game/client/ui.h index 2f52c156b06..94bba962876 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -483,6 +483,7 @@ class CUi float MouseWorldY() const { return m_MouseWorldPos.y; } vec2 UpdatedMousePos() const { return m_UpdatedMousePos; } vec2 UpdatedMouseDelta() const { return m_UpdatedMouseDelta; } + int LastMouseButton(int Index) const { return (m_LastMouseButtons >> Index) & 1; } // TClient int MouseButton(int Index) const { return (m_MouseButtons >> Index) & 1; } int MouseButtonClicked(int Index) const { return MouseButton(Index) && !((m_LastMouseButtons >> Index) & 1); } bool CheckMouseLock()