diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d194867d4d..73f7335100a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2407,14 +2407,16 @@ if(CLIENT) components/statboard.h components/tooltips.cpp components/tooltips.h - components/tclient/bindwheel.cpp - components/tclient/bindwheel.h components/tclient/outlines.cpp components/tclient/outlines.h - components/tclient/rainbow.cpp - components/tclient/rainbow.h + components/tclient/bindwheel.cpp + components/tclient/bindwheel.h + components/tclient/menus_tclient.cpp + components/tclient/menus_tclient.h components/tclient/player_indicator.cpp components/tclient/player_indicator.h + components/tclient/rainbow.cpp + components/tclient/rainbow.h components/tclient/skinprofiles.cpp components/tclient/skinprofiles.h components/tclient/tater.cpp diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 2201342f0eb..ad48beb0c7a 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -839,6 +839,8 @@ class CMenus : public CComponent // found in menus_settings.cpp void RenderSettingsDDNet(CUIRect MainView); void RenderSettingsAppearance(CUIRect MainView); + + // found in menus_tclient.cpp void RenderSettingsTClient(CUIRect MainView); void RenderSettingsProfiles(CUIRect MainView); diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 959fe3b2393..7d42ffe7dc5 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -3213,1083 +3212,6 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) } } -void CMenus::OpenTClientDiscord() -{ - if(!open_link("https://discord.gg/fBvhH93Bt6")) - PopupWarning(Localize("Open TClient Discord"), Localize("Failed to open the TClient Discord in your browser"), Localize("Aww"), std::chrono::nanoseconds(0)); -} - -enum -{ - TCLIENT_TAB_SETTINGS = 0, - TCLIENT_TAB_BINDWHEEL = 1, - TCLIENT_TAB_DISCORD = 2, - NUMBER_OF_TCLIENT_TABS = 3 -}; - -bool CMenus::DoSliderWithScaledValue(const void *pId, int *pOption, const CUIRect *pRect, const char *pStr, int Min, int Max, int Scale, const IScrollbarScale *pScale, unsigned Flags, const char *pSuffix) -{ - const bool NoClampValue = Flags & CUi::SCROLLBAR_OPTION_NOCLAMPVALUE; - - int Value = *pOption; - Min /= Scale; - Max /= Scale; - // Allow adjustment of slider options when ctrl is pressed (to avoid scrolling, or accidently adjusting the value) - int Increment = std::max(1, (Max - Min) / 35); - if(Input()->ModifierIsPressed() && Input()->KeyPress(KEY_MOUSE_WHEEL_UP) && Ui()->MouseInside(pRect)) - { - Value += Increment; - Value = clamp(Value, Min, Max); - } - if(Input()->ModifierIsPressed() && Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN) && Ui()->MouseInside(pRect)) - { - Value -= Increment; - Value = clamp(Value, Min, Max); - } - - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "%s: %i%s", pStr, Value * Scale, pSuffix); - - if(NoClampValue) - { - // clamp the value internally for the scrollbar - Value = clamp(Value, Min, Max); - } - - CUIRect Label, ScrollBar; - pRect->VSplitMid(&Label, &ScrollBar, minimum(10.0f, pRect->w * 0.05f)); - - const float FontSize = Label.h * CUi::ms_FontmodHeight * 0.8f; - Ui()->DoLabel(&Label, aBuf, FontSize, TEXTALIGN_ML); - - Value = pScale->ToAbsolute(Ui()->DoScrollbarH(pId, &ScrollBar, pScale->ToRelative(Value, Min, Max)), Min, Max); - if(NoClampValue && ((Value == Min && *pOption < Min) || (Value == Max && *pOption > Max))) - { - Value = *pOption; - } - - if(*pOption != Value) - { - *pOption = Value; - return true; - } - return false; -} - -void CMenus::RenderSettingsTClient(CUIRect MainView) -{ - static int s_CurCustomTab = 0; - - const float LineSize = 20.0f; - const float ColorPickerLineSize = 25.0f; - const float HeadlineFontSize = 20.0f; - const float HeadlineHeight = HeadlineFontSize + 0.0f; - const float Margin = 10.0f; - const float MarginSmall = 5.0f; - const float MarginExtraSmall = 2.5f; - const float MarginBetweenSections = 30.0f; - const float MarginBetweenViews = 30.0f; - - const float ColorPickerLabelSize = 13.0f; - const float ColorPickerLineSpacing = 5.0f; - - CUIRect TabBar, Column, LeftView, RightView, Button, Label; - - MainView.HSplitTop(LineSize, &TabBar, &MainView); - const float TabWidth = TabBar.w / NUMBER_OF_TCLIENT_TABS; - static CButtonContainer s_aPageTabs[NUMBER_OF_TCLIENT_TABS] = {}; - const char *apTabNames[] = { - Localize("Settings"), - Localize("Bindwheel"), - Localize("Discord")}; - - for(int Tab = 0; Tab < NUMBER_OF_TCLIENT_TABS; ++Tab) - { - TabBar.VSplitLeft(TabWidth, &Button, &TabBar); - const int Corners = Tab == 0 ? IGraphics::CORNER_L : Tab == NUMBER_OF_TCLIENT_TABS - 1 ? IGraphics::CORNER_R : IGraphics::CORNER_NONE; - if(DoButton_MenuTab(&s_aPageTabs[Tab], apTabNames[Tab], s_CurCustomTab == Tab, &Button, Corners, nullptr, nullptr, nullptr, nullptr, 4.0f)) - { - if(Tab == TCLIENT_TAB_DISCORD) - PopupConfirm(Localize("Open TClient Discord"), Localize("Click open to open the TClient Discord invite in your browser"), Localize("Open"), Localize("Cancel"), &CMenus::OpenTClientDiscord); - else - s_CurCustomTab = Tab; - break; - } - } - - MainView.HSplitTop(MarginSmall, nullptr, &MainView); - - if(s_CurCustomTab == TCLIENT_TAB_SETTINGS) - { - static CScrollRegion s_ScrollRegion; - vec2 ScrollOffset(0.0f, 0.0f); - CScrollRegionParams ScrollParams; - ScrollParams.m_ScrollUnit = 120.0f; - ScrollParams.m_Flags = CScrollRegionParams::FLAG_CONTENT_STATIC_WIDTH; - ScrollParams.m_ScrollbarMargin = 5.0f; - s_ScrollRegion.Begin(&MainView, &ScrollOffset, &ScrollParams); - - static std::vector s_SectionBoxes; - static vec2 s_PrevScrollOffset(0.0f, 0.0f); - - MainView.y += ScrollOffset.y; - - MainView.VSplitRight(5.0f, &MainView, nullptr); // Padding for scrollbar - MainView.VSplitLeft(5.0f, nullptr, &MainView); // Padding for scrollbar - - MainView.VSplitMid(&LeftView, &RightView, MarginBetweenViews); - LeftView.VSplitLeft(MarginSmall, nullptr, &LeftView); - RightView.VSplitRight(MarginSmall, &RightView, nullptr); - - //RightView.VSplitRight(10.0f, &RightView, nullptr); - for(CUIRect &Section : s_SectionBoxes) - { - float Padding = MarginBetweenViews * 0.6666f; - Section.w += Padding; - Section.h += Padding; - Section.x -= Padding * 0.5f; - Section.y -= Padding * 0.5f; - Section.y -= s_PrevScrollOffset.y - ScrollOffset.y; - float Shade = 0.0f; - Section.Draw(ColorRGBA(Shade, Shade, Shade, 0.25f), IGraphics::CORNER_ALL, 10.0f); - } - s_PrevScrollOffset = ScrollOffset; - s_SectionBoxes.clear(); - - // ***** LeftView ***** // - Column = LeftView; - - // ***** Visual Miscellaneous ***** // - Column.HSplitTop(Margin, nullptr, &Column); - s_SectionBoxes.push_back(Column); - Column.HSplitTop(HeadlineHeight, &Label, &Column); - Ui()->DoLabel(&Label, Localize("Visual"), HeadlineFontSize, TEXTALIGN_ML); - Column.HSplitTop(MarginSmall, nullptr, &Column); - - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClFreezeUpdateFix, Localize("Update tee skin faster after being frozen"), &g_Config.m_ClFreezeUpdateFix, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClPingNameCircle, Localize("Show ping colored circle before names"), &g_Config.m_ClPingNameCircle, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRenderNameplateSpec, Localize("Hide nameplates in spec"), &g_Config.m_ClRenderNameplateSpec, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowSkinName, Localize("Show skin names in nameplate"), &g_Config.m_ClShowSkinName, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClFreezeStars, Localize("Freeze Stars"), &g_Config.m_ClFreezeStars, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClColorFreeze, Localize("Color Frozen Tee Skins"), &g_Config.m_ClColorFreeze, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClHammerRotatesWithCursor, Localize("Make hammer rotate with cursor"), &g_Config.m_ClHammerRotatesWithCursor, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClWhiteFeet, Localize("Render all custom colored feet as white feet skin"), &g_Config.m_ClWhiteFeet, &Column, LineSize); - CUIRect FeetBox; - Column.HSplitTop(LineSize + MarginExtraSmall, &FeetBox, &Column); - if(g_Config.m_ClWhiteFeet) - { - FeetBox.HSplitTop(MarginExtraSmall, nullptr, &FeetBox); - FeetBox.VSplitMid(&FeetBox, nullptr); - static CLineInput s_WhiteFeet(g_Config.m_ClWhiteFeetSkin, sizeof(g_Config.m_ClWhiteFeetSkin)); - s_WhiteFeet.SetEmptyText("x_ninja"); - Ui()->DoEditBox(&s_WhiteFeet, &FeetBox, 12.0f); - } - Column.HSplitTop(MarginExtraSmall, nullptr, &Column); - - s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; - - // ***** Input ***** // - Column.HSplitTop(MarginBetweenSections, nullptr, &Column); - s_SectionBoxes.push_back(Column); - Column.HSplitTop(HeadlineHeight, &Label, &Column); - Ui()->DoLabel(&Label, Localize("Input"), HeadlineFontSize, TEXTALIGN_ML); - Column.HSplitTop(MarginSmall, nullptr, &Column); - - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClFastInput, Localize("Fast Inputs (-20ms input delay)"), &g_Config.m_ClFastInput, &Column, LineSize); - - Column.HSplitTop(MarginSmall, nullptr, &Column); - if(g_Config.m_ClFastInput) - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClFastInputOthers, Localize("Extra tick other tees (increases other tees latency, \nmakes dragging slightly easier when using fast input)"), &g_Config.m_ClFastInputOthers, &Column, LineSize); - else - Column.HSplitTop(LineSize, nullptr, &Column); - // A little extra spacing because these are multi line - Column.HSplitTop(MarginSmall, nullptr, &Column); - - Column.HSplitTop(MarginSmall, nullptr, &Column); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOldMouseZoom, Localize("Old Mouse Precision (fixes precision at low zoom levels, \nbreaks /tc, /telecursor while zoomed)"), &g_Config.m_ClOldMouseZoom, &Column, LineSize); - Column.HSplitTop(MarginSmall, nullptr, &Column); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClImproveMousePrecision, Localize("Improve mouse precision by scaling sent max distance to 1000"), &g_Config.m_ClImproveMousePrecision, &Column, LineSize); - s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; - - // ***** Anti Latency Tools ***** // - Column.HSplitTop(MarginBetweenSections, nullptr, &Column); - s_SectionBoxes.push_back(Column); - Column.HSplitTop(HeadlineHeight, &Label, &Column); - Ui()->DoLabel(&Label, Localize("Anti Latency Tools"), HeadlineFontSize, TEXTALIGN_ML); - Column.HSplitTop(MarginSmall, nullptr, &Column); - - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClPredictionMargin, &g_Config.m_ClPredictionMargin, &Button, Localize("Prediction Margin"), 10, 25, &CUi::ms_LinearScrollbarScale, 0, "ms"); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRemoveAnti, Localize("Remove prediction & antiping in freeze"), &g_Config.m_ClRemoveAnti, &Column, LineSize); - if(g_Config.m_ClRemoveAnti) - { - if(g_Config.m_ClUnfreezeLagDelayTicks < g_Config.m_ClUnfreezeLagTicks) - g_Config.m_ClUnfreezeLagDelayTicks = g_Config.m_ClUnfreezeLagTicks; - Column.HSplitTop(LineSize, &Button, &Column); - DoSliderWithScaledValue(&g_Config.m_ClUnfreezeLagTicks, &g_Config.m_ClUnfreezeLagTicks, &Button, Localize("Amount"), 100, 300, 20, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_NOCLAMPVALUE, "ms"); - Column.HSplitTop(LineSize, &Button, &Column); - DoSliderWithScaledValue(&g_Config.m_ClUnfreezeLagDelayTicks, &g_Config.m_ClUnfreezeLagDelayTicks, &Button, Localize("Delay"), 100, 3000, 20, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_NOCLAMPVALUE, "ms"); - } - else - Column.HSplitTop(LineSize * 2, nullptr, &Column); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClUnpredOthersInFreeze, Localize("Dont predict other players if you are frozen"), &g_Config.m_ClUnpredOthersInFreeze, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClPredMarginInFreeze, Localize("Adjust your prediction margin while frozen"), &g_Config.m_ClPredMarginInFreeze, &Column, LineSize); - Column.HSplitTop(LineSize, &Button, &Column); - if(g_Config.m_ClPredMarginInFreeze) - Ui()->DoScrollbarOption(&g_Config.m_ClPredMarginInFreezeAmount, &g_Config.m_ClPredMarginInFreezeAmount, &Button, Localize("Frozen Margin"), 0, 100, &CUi::ms_LinearScrollbarScale, 0, "ms"); - s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; - - // ***** Other ***** // - Column.HSplitTop(MarginBetweenSections, nullptr, &Column); - s_SectionBoxes.push_back(Column); - Column.HSplitTop(HeadlineHeight, &Label, &Column); - Ui()->DoLabel(&Label, Localize("Other"), HeadlineFontSize, TEXTALIGN_ML); - Column.HSplitTop(MarginSmall, nullptr, &Column); - - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRunOnJoinConsole, Localize("Run cl_run_on_join as console command"), &g_Config.m_ClRunOnJoinConsole, &Column, LineSize); - Column.HSplitTop(LineSize, &Button, &Column); - if(g_Config.m_ClRunOnJoinConsole) - { - DoSliderWithScaledValue(&g_Config.m_ClRunOnJoinDelay, &g_Config.m_ClRunOnJoinDelay, &Button, Localize("Delay"), 140, 2000, 20, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_NOCLAMPVALUE, "ms"); - } - CUIRect ButtonVerify, EnableVerifySection; - Column.HSplitTop(LineSize, &EnableVerifySection, &Column); - EnableVerifySection.VSplitMid(&EnableVerifySection, &ButtonVerify); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClAutoVerify, Localize("Auto Verify"), &g_Config.m_ClAutoVerify, &EnableVerifySection, LineSize); - static CButtonContainer s_VerifyButton; - if(DoButton_Menu(&s_VerifyButton, Localize("Manual Verify"), 0, &ButtonVerify, 0, IGraphics::CORNER_ALL)) - { - if(!open_link("https://ger10.ddnet.org/")) - dbg_msg("menus", "couldn't open link"); - } - Column.HSplitTop(MarginExtraSmall, nullptr, &Column); - s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; - - // ***** Player Indicator ***** // - Column.HSplitTop(MarginBetweenSections, nullptr, &Column); - s_SectionBoxes.push_back(Column); - Column.HSplitTop(HeadlineHeight, &Label, &Column); - Ui()->DoLabel(&Label, Localize("Player Indicator"), HeadlineFontSize, TEXTALIGN_ML); - Column.HSplitTop(MarginSmall, nullptr, &Column); - - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClPlayerIndicator, Localize("Show any enabled Indicators"), &g_Config.m_ClPlayerIndicator, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClPlayerIndicatorFreeze, Localize("Show only freeze Players"), &g_Config.m_ClPlayerIndicatorFreeze, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClIndicatorTeamOnly, Localize("Only show after joining a team"), &g_Config.m_ClIndicatorTeamOnly, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClIndicatorTees, Localize("Render tiny tees instead of circles"), &g_Config.m_ClIndicatorTees, &Column, LineSize); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorRadius, &g_Config.m_ClIndicatorRadius, &Button, Localize("Indicator size"), 1, 16); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorOpacity, &g_Config.m_ClIndicatorOpacity, &Button, Localize("Indicator opacity"), 0, 100); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClIndicatorVariableDistance, Localize("Change indicator offset based on distance to other tees"), &g_Config.m_ClIndicatorVariableDistance, &Column, LineSize); - if(g_Config.m_ClIndicatorVariableDistance) - { - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorOffset, &g_Config.m_ClIndicatorOffset, &Button, Localize("Indicator min offset"), 16, 200); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorOffsetMax, &g_Config.m_ClIndicatorOffsetMax, &Button, Localize("Indicator max offset"), 16, 200); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorMaxDistance, &g_Config.m_ClIndicatorMaxDistance, &Button, Localize("Indicator max distance"), 500, 7000); - } - else - { - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorOffset, &g_Config.m_ClIndicatorOffset, &Button, Localize("Indicator offset"), 16, 200); - Column.HSplitTop(LineSize * 2, nullptr, &Column); - } - static CButtonContainer IndicatorAliveColorID, IndicatorDeadColorID, IndicatorSavedColorID; - DoLine_ColorPicker(&IndicatorAliveColorID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Indicator alive color"), &g_Config.m_ClIndicatorAlive, ColorRGBA(0.0f, 0.0f, 0.0f), false); - DoLine_ColorPicker(&IndicatorDeadColorID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Indicator dead color"), &g_Config.m_ClIndicatorFreeze, ColorRGBA(0.0f, 0.0f, 0.0f), false); - DoLine_ColorPicker(&IndicatorSavedColorID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Indicator save color"), &g_Config.m_ClIndicatorSaved, ColorRGBA(0.0f, 0.0f, 0.0f), false); - s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; - - // ***** RightView ***** // - LeftView = Column; - Column = RightView; - - // ***** HUD ***** // - Column.HSplitTop(Margin, nullptr, &Column); - s_SectionBoxes.push_back(Column); - Column.HSplitTop(HeadlineHeight, &Label, &Column); - Ui()->DoLabel(&Label, Localize("HUD"), HeadlineFontSize, TEXTALIGN_ML); - Column.HSplitTop(MarginSmall, nullptr, &Column); - - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowCenterLines, Localize("Show screen center"), &g_Config.m_ClShowCenterLines, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClMiniDebug, Localize("Show Position and angle (Mini debug)"), &g_Config.m_ClMiniDebug, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRenderCursorSpec, Localize("Show your cursor when in free spectate"), &g_Config.m_ClRenderCursorSpec, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNotifyWhenLast, Localize("Show when you are the last alive"), &g_Config.m_ClNotifyWhenLast, &Column, LineSize); - CUIRect NotificationConfig; - Column.HSplitTop(LineSize + MarginSmall, &NotificationConfig, &Column); - if(g_Config.m_ClNotifyWhenLast) - { - NotificationConfig.VSplitMid(&Button, &NotificationConfig); - static CLineInput s_LastInput(g_Config.m_ClNotifyWhenLastText, sizeof(g_Config.m_ClNotifyWhenLastText)); - s_LastInput.SetEmptyText(Localize("Last!")); - Button.HSplitTop(MarginSmall, nullptr, &Button); - Ui()->DoEditBox(&s_LastInput, &Button, 12.0f); - static CButtonContainer s_ClientNotifyWhenLastColor; - DoLine_ColorPicker(&s_ClientNotifyWhenLastColor, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &NotificationConfig, "", &g_Config.m_ClNotifyWhenLastColor, ColorRGBA(1.0f, 1.0f, 1.0f), false); - } - Column.HSplitTop(MarginExtraSmall, nullptr, &Column); - s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; - - // ***** Frozen Tee Display ***** // - Column.HSplitTop(MarginBetweenSections, nullptr, &Column); - s_SectionBoxes.push_back(Column); - Column.HSplitTop(HeadlineHeight, &Label, &Column); - Ui()->DoLabel(&Label, Localize("Frozen Tee Display"), HeadlineFontSize, TEXTALIGN_ML); - Column.HSplitTop(MarginSmall, nullptr, &Column); - - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowFrozenHud, Localize("Show frozen tee display"), &g_Config.m_ClShowFrozenHud, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowFrozenHudSkins, Localize("Use skins instead of ninja tees"), &g_Config.m_ClShowFrozenHudSkins, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClFrozenHudTeamOnly, Localize("Only show after joining a team"), &g_Config.m_ClFrozenHudTeamOnly, &Column, LineSize); - - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClFrozenMaxRows, &g_Config.m_ClFrozenMaxRows, &Button, Localize("Max Rows"), 1, 6); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClFrozenHudTeeSize, &g_Config.m_ClFrozenHudTeeSize, &Button, Localize("Tee Size"), 8, 27); - - { - CUIRect CheckBoxRect, CheckBoxRect2; - Column.HSplitTop(LineSize, &CheckBoxRect, &Column); - Column.HSplitTop(LineSize, &CheckBoxRect2, &Column); - if(DoButton_CheckBox(&g_Config.m_ClShowFrozenText, Localize("Tees Left Alive Text"), g_Config.m_ClShowFrozenText >= 1, &CheckBoxRect)) - g_Config.m_ClShowFrozenText = g_Config.m_ClShowFrozenText >= 1 ? 0 : 1; - - if(g_Config.m_ClShowFrozenText) - { - static int s_CountFrozenText = 0; - if(DoButton_CheckBox(&s_CountFrozenText, Localize("Count Frozen Tees"), g_Config.m_ClShowFrozenText == 2, &CheckBoxRect2)) - g_Config.m_ClShowFrozenText = g_Config.m_ClShowFrozenText != 2 ? 2 : 1; - } - } - s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; - - // ***** Tile Outlines ***** // - Column.HSplitTop(MarginBetweenSections, nullptr, &Column); - s_SectionBoxes.push_back(Column); - Column.HSplitTop(HeadlineHeight, &Label, &Column); - Ui()->DoLabel(&Label, Localize("Tile Outlines"), HeadlineFontSize, TEXTALIGN_ML); - Column.HSplitTop(MarginSmall, nullptr, &Column); - - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutline, Localize("Show any enabled outlines"), &g_Config.m_ClOutline, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutlineEntities, Localize("Only show outlines in entities"), &g_Config.m_ClOutlineEntities, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutlineFreeze, Localize("Outline freeze & deep"), &g_Config.m_ClOutlineFreeze, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutlineSolid, Localize("Outline walls"), &g_Config.m_ClOutlineSolid, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutlineTele, Localize("Outline teleporter"), &g_Config.m_ClOutlineTele, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutlineUnFreeze, Localize("Outline unfreeze & undeep"), &g_Config.m_ClOutlineUnFreeze, &Column, LineSize); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClOutlineWidth, &g_Config.m_ClOutlineWidth, &Button, Localize("Outline Width"), 1, 16); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClOutlineAlpha, &g_Config.m_ClOutlineAlpha, &Button, Localize("Outline Alpha"), 0, 100); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClOutlineAlphaSolid, &g_Config.m_ClOutlineAlphaSolid, &Button, Localize("Outline Alpha (walls)"), 0, 100); - static CButtonContainer OutlineColorFreezeID, OutlineColorSolidID, OutlineColorTeleID, OutlineColorUnfreezeID; - DoLine_ColorPicker(&OutlineColorFreezeID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Freeze Outline Color"), &g_Config.m_ClOutlineColorFreeze, ColorRGBA(0.0f, 0.0f, 0.0f), false); - DoLine_ColorPicker(&OutlineColorSolidID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Walls Outline Color"), &g_Config.m_ClOutlineColorSolid, ColorRGBA(0.0f, 0.0f, 0.0f), false); - DoLine_ColorPicker(&OutlineColorTeleID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Teleporter Outline Color"), &g_Config.m_ClOutlineColorTele, ColorRGBA(0.0f, 0.0f, 0.0f), false); - DoLine_ColorPicker(&OutlineColorUnfreezeID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Unfreeze Outline Color"), &g_Config.m_ClOutlineColorUnfreeze, ColorRGBA(0.0f, 0.0f, 0.0f), false); - s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; - - // ***** Ghost Tools ***** // - Column.HSplitTop(MarginBetweenSections, nullptr, &Column); - s_SectionBoxes.push_back(Column); - Column.HSplitTop(HeadlineHeight, &Label, &Column); - Ui()->DoLabel(&Label, Localize("Ghost Tools"), HeadlineFontSize, TEXTALIGN_ML); - Column.HSplitTop(MarginSmall, nullptr, &Column); - - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowOthersGhosts, Localize("Show unpredicted ghosts for other players"), &g_Config.m_ClShowOthersGhosts, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClSwapGhosts, Localize("Swap ghosts and normal players"), &g_Config.m_ClSwapGhosts, &Column, LineSize); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClPredGhostsAlpha, &g_Config.m_ClPredGhostsAlpha, &Button, Localize("Predicted Alpha"), 0, 100, &CUi::ms_LinearScrollbarScale, 0, "%"); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClUnpredGhostsAlpha, &g_Config.m_ClUnpredGhostsAlpha, &Button, Localize("Unpredicted Alpha"), 0, 100, &CUi::ms_LinearScrollbarScale, 0, "%"); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClHideFrozenGhosts, Localize("Hide ghosts of frozen players"), &g_Config.m_ClHideFrozenGhosts, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRenderGhostAsCircle, Localize("Render ghosts as circles"), &g_Config.m_ClRenderGhostAsCircle, &Column, LineSize); - - static CKeyInfo Key = CKeyInfo{"Toggle Ghosts Key", "toggle tc_show_others_ghosts 0 1", 0, 0}; - Key.m_ModifierCombination = Key.m_KeyId = 0; - for(int Mod = 0; Mod < CBinds::MODIFIER_COMBINATION_COUNT; Mod++) - { - for(int KeyId = 0; KeyId < KEY_LAST; KeyId++) - { - const char *pBind = m_pClient->m_Binds.Get(KeyId, Mod); - if(!pBind[0]) - continue; - - if(str_comp(pBind, Key.m_pCommand) == 0) - { - Key.m_KeyId = KeyId; - Key.m_ModifierCombination = Mod; - break; - } - } - } - - CUIRect KeyButton, KeyLabel; - Column.HSplitTop(LineSize, &KeyButton, &Column); - KeyButton.VSplitMid(&KeyLabel, &KeyButton); - char aBuf[64]; - str_format(aBuf, sizeof(aBuf), "%s:", Localize(Key.m_pName)); - Ui()->DoLabel(&KeyLabel, aBuf, 12.0f, TEXTALIGN_ML); - int OldId = Key.m_KeyId, OldModifierCombination = Key.m_ModifierCombination, NewModifierCombination; - int NewId = DoKeyReader(&Key, &KeyButton, OldId, OldModifierCombination, &NewModifierCombination); - if(NewId != OldId || NewModifierCombination != OldModifierCombination) - { - if(OldId != 0 || NewId == 0) - m_pClient->m_Binds.Bind(OldId, "", false, OldModifierCombination); - if(NewId != 0) - m_pClient->m_Binds.Bind(NewId, Key.m_pCommand, false, NewModifierCombination); - } - Column.HSplitTop(MarginExtraSmall, nullptr, &Column); - s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; - - // ***** Rainbow ***** // - Column.HSplitTop(MarginBetweenSections, nullptr, &Column); - s_SectionBoxes.push_back(Column); - Column.HSplitTop(HeadlineHeight, &Label, &Column); - Ui()->DoLabel(&Label, Localize("Rainbow"), HeadlineFontSize, TEXTALIGN_ML); - Column.HSplitTop(MarginSmall, nullptr, &Column); - - static std::vector s_DropDownNames = {Localize("Rainbow"), Localize("Pulse"), Localize("Black"), Localize("Random")}; - static CUi::SDropDownState s_RainbowDropDownState; - static CScrollRegion s_RainbowDropDownScrollRegion; - s_RainbowDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_RainbowDropDownScrollRegion; - int RainbowSelectedOld = g_Config.m_ClRainbowMode - 1; - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRainbow, Localize("Rainbow"), &g_Config.m_ClRainbow, &Column, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRainbowOthers, Localize("Rainbow Others"), &g_Config.m_ClRainbowOthers, &Column, LineSize); - Column.HSplitTop(MarginExtraSmall, nullptr, &Column); - CUIRect DropDownRect; - Column.HSplitTop(LineSize, &DropDownRect, &Column); - const int RainbowSelectedNew = Ui()->DoDropDown(&DropDownRect, RainbowSelectedOld, s_DropDownNames.data(), s_DropDownNames.size(), s_RainbowDropDownState); - if(RainbowSelectedOld != RainbowSelectedNew) - { - g_Config.m_ClRainbowMode = RainbowSelectedNew + 1; - RainbowSelectedOld = RainbowSelectedNew; - dbg_msg("rainbow", "rainbow mode changed to %d", g_Config.m_ClRainbowMode); - } - Column.HSplitTop(MarginExtraSmall, nullptr, &Column); - Column.HSplitTop(LineSize, &Button, &Column); - Ui()->DoScrollbarOption(&g_Config.m_ClRainbowSpeed, &g_Config.m_ClRainbowSpeed, &Button, Localize("Rainbow speed"), 0, 5000, &CUi::ms_LogarithmicScrollbarScale, 0, "%"); - Column.HSplitTop(MarginExtraSmall, nullptr, &Column); - s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; - - Column.HSplitTop(MarginSmall, nullptr, &Column); - // ***** END OF PAGE 1 SETTINGS ***** // - RightView = Column; - - // Scroll - CUIRect ScrollRegion; - ScrollRegion.x = MainView.x; - ScrollRegion.y = maximum(LeftView.y, RightView.y) + MarginSmall * 2.0f; - ScrollRegion.w = MainView.w; - ScrollRegion.h = 0.0f; - s_ScrollRegion.AddRect(ScrollRegion); - s_ScrollRegion.End(); - - } - - if(s_CurCustomTab == TCLIENT_TAB_BINDWHEEL) - { - MainView.HSplitTop(MarginBetweenSections, nullptr, &MainView); - MainView.VSplitLeft(MainView.w / 2.1f, &LeftView, &RightView); - - const float Radius = minimum(RightView.w, RightView.h) / 2.0f; - vec2 Pos{RightView.x + RightView.w / 2.0f, RightView.y + RightView.h / 2.0f}; - // Draw Circle - Graphics()->TextureClear(); - Graphics()->QuadsBegin(); - Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.3f); - Graphics()->DrawCircle(Pos.x, Pos.y, Radius, 64); - Graphics()->QuadsEnd(); - - static char s_aBindName[BINDWHEEL_MAX_NAME]; - static char s_aBindCommand[BINDWHEEL_MAX_CMD]; - - static int s_SelectedBindIndex = -1; - int HoveringIndex = -1; - - float MouseDist = distance(Pos, Ui()->MousePos()); - if(MouseDist < Radius && MouseDist > Radius * 0.25f) - { - int SegmentCount = GameClient()->m_Bindwheel.m_vBinds.size(); - float SegmentAngle = 2 * pi / SegmentCount; - - float HoveringAngle = angle(Ui()->MousePos() - Pos) + SegmentAngle / 2; - if(HoveringAngle < 0.0f) - HoveringAngle += 2.0f * pi; - - HoveringIndex = (int)(HoveringAngle / (2 * pi) * SegmentCount); - if(Ui()->MouseButtonClicked(0)) - { - s_SelectedBindIndex = HoveringIndex; - str_copy(s_aBindName, GameClient()->m_Bindwheel.m_vBinds[HoveringIndex].m_aName); - str_copy(s_aBindCommand, GameClient()->m_Bindwheel.m_vBinds[HoveringIndex].m_aCommand); - } - else if(Ui()->MouseButtonClicked(1) && s_SelectedBindIndex >= 0 && HoveringIndex >= 0 && HoveringIndex != s_SelectedBindIndex) - { - CBindWheel::SBind BindA = GameClient()->m_Bindwheel.m_vBinds[s_SelectedBindIndex]; - CBindWheel::SBind BindB = GameClient()->m_Bindwheel.m_vBinds[HoveringIndex]; - str_copy(GameClient()->m_Bindwheel.m_vBinds[s_SelectedBindIndex].m_aName, BindB.m_aName); - str_copy(GameClient()->m_Bindwheel.m_vBinds[s_SelectedBindIndex].m_aCommand, BindB.m_aCommand); - str_copy(GameClient()->m_Bindwheel.m_vBinds[HoveringIndex].m_aName, BindA.m_aName); - str_copy(GameClient()->m_Bindwheel.m_vBinds[HoveringIndex].m_aCommand, BindA.m_aCommand); - } - else if(Ui()->MouseButtonClicked(2)) - { - s_SelectedBindIndex = HoveringIndex; - } - - } - else if(MouseDist < Radius && Ui()->MouseButtonClicked(0)) - { - s_SelectedBindIndex = -1; - str_copy(s_aBindName, ""); - str_copy(s_aBindCommand, ""); - } - - const float Theta = pi * 2.0f / GameClient()->m_Bindwheel.m_vBinds.size(); - for(int i = 0; i < static_cast(GameClient()->m_Bindwheel.m_vBinds.size()); i++) - { - float FontSize = 12.0f; - if(i == s_SelectedBindIndex) - { - FontSize = 20.0f; - TextRender()->TextColor(ColorRGBA(0.5f, 1.0f, 0.75f, 1.0f)); - } - else if(i == HoveringIndex) - FontSize = 14.0f; - - const CBindWheel::SBind Bind = GameClient()->m_Bindwheel.m_vBinds[i]; - const float Angle = Theta * i; - vec2 TextPos = direction(Angle); - TextPos *= Radius * 0.75f; - - float Width = TextRender()->TextWidth(FontSize, Bind.m_aName); - TextPos += Pos; - TextPos.x -= Width / 2.0f; - TextRender()->Text(TextPos.x, TextPos.y, FontSize, Bind.m_aName); - TextRender()->TextColor(TextRender()->DefaultTextColor()); - } - - LeftView.HSplitTop(LineSize, &Button, &LeftView); - Button.VSplitLeft(100.0f, &Label, &Button); - Ui()->DoLabel(&Label, Localize("Name:"), 14.0f, TEXTALIGN_ML); - static CLineInput s_NameInput; - s_NameInput.SetBuffer(s_aBindName, sizeof(s_aBindName)); - s_NameInput.SetEmptyText("Name"); - Ui()->DoEditBox(&s_NameInput, &Button, 12.0f); - - LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); - LeftView.HSplitTop(LineSize, &Button, &LeftView); - Button.VSplitLeft(100.0f, &Label, &Button); - Ui()->DoLabel(&Label, Localize("Command:"), 14.0f, TEXTALIGN_ML); - static CLineInput s_BindInput; - s_BindInput.SetBuffer(s_aBindCommand, sizeof(s_aBindCommand)); - s_BindInput.SetEmptyText(Localize("Command")); - Ui()->DoEditBox(&s_BindInput, &Button, 12.0f); - - static CButtonContainer s_AddButton, s_RemoveButton, s_OverrideButton; - - LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); - LeftView.HSplitTop(LineSize, &Button, &LeftView); - if(DoButton_Menu(&s_OverrideButton, Localize("Override Selected"), 0, &Button) && s_SelectedBindIndex >= 0) - { - CBindWheel::SBind TempBind; - if(str_length(s_aBindName) == 0) - str_copy(TempBind.m_aName, "*"); - else - str_copy(TempBind.m_aName, s_aBindName); - - str_copy(GameClient()->m_Bindwheel.m_vBinds[s_SelectedBindIndex].m_aName, TempBind.m_aName); - str_copy(GameClient()->m_Bindwheel.m_vBinds[s_SelectedBindIndex].m_aCommand, s_aBindCommand); - } - LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); - LeftView.HSplitTop(LineSize, &Button, &LeftView); - CUIRect ButtonAdd, ButtonRemove; - Button.VSplitMid(&ButtonRemove, &ButtonAdd, MarginSmall); - if(DoButton_Menu(&s_AddButton, Localize("Add Bind"), 0, &ButtonAdd)) - { - CBindWheel::SBind TempBind; - if(str_length(s_aBindName) == 0) - str_copy(TempBind.m_aName, "*"); - else - str_copy(TempBind.m_aName, s_aBindName); - - GameClient()->m_Bindwheel.AddBind(TempBind.m_aName, s_aBindCommand); - s_SelectedBindIndex = static_cast(GameClient()->m_Bindwheel.m_vBinds.size()) - 1; - } - if(DoButton_Menu(&s_RemoveButton, Localize("Remove Bind"), 0, &ButtonRemove) && s_SelectedBindIndex >= 0) - { - GameClient()->m_Bindwheel.RemoveBind(s_SelectedBindIndex); - s_SelectedBindIndex = -1; - } - - LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); - LeftView.HSplitTop(LineSize, &Label, &LeftView); - Ui()->DoLabel(&Label, Localize("Use left mouse to select"), 14.0f, TEXTALIGN_ML); - LeftView.HSplitTop(LineSize, &Label, &LeftView); - Ui()->DoLabel(&Label, Localize("Use right mouse to swap with selected"), 14.0f, TEXTALIGN_ML); - LeftView.HSplitTop(LineSize, &Label, &LeftView); - Ui()->DoLabel(&Label, Localize("Use middle mouse select without copy"), 14.0f, TEXTALIGN_ML); - - // Do Settings Key - CKeyInfo Key = CKeyInfo{"Bind Wheel Key", "+bindwheel", 0, 0}; - for(int Mod = 0; Mod < CBinds::MODIFIER_COMBINATION_COUNT; Mod++) - { - for(int KeyId = 0; KeyId < KEY_LAST; KeyId++) - { - const char *pBind = m_pClient->m_Binds.Get(KeyId, Mod); - if(!pBind[0]) - continue; - - if(str_comp(pBind, Key.m_pCommand) == 0) - { - Key.m_KeyId = KeyId; - Key.m_ModifierCombination = Mod; - break; - } - } - } - - CUIRect KeyLabel; - LeftView.HSplitBottom(LineSize, &LeftView, &Button); - Button.VSplitLeft(120.0f, &KeyLabel, &Button); - Button.VSplitLeft(100.0f, &Button, nullptr); - char aBuf[64]; - str_format(aBuf, sizeof(aBuf), "%s:", Localize((const char *)Key.m_pName)); - - Ui()->DoLabel(&KeyLabel, aBuf, 14.0f, TEXTALIGN_ML); - int OldId = Key.m_KeyId, OldModifierCombination = Key.m_ModifierCombination, NewModifierCombination; - int NewId = DoKeyReader((void *)&Key.m_pName, &Button, OldId, OldModifierCombination, &NewModifierCombination); - if(NewId != OldId || NewModifierCombination != OldModifierCombination) - { - if(OldId != 0 || NewId == 0) - m_pClient->m_Binds.Bind(OldId, "", false, OldModifierCombination); - if(NewId != 0) - m_pClient->m_Binds.Bind(NewId, Key.m_pCommand, false, NewModifierCombination); - } - LeftView.HSplitBottom(LineSize, &LeftView, &Button); - - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClResetBindWheelMouse, Localize("Reset position of mouse when opening bindwheel"), &g_Config.m_ClResetBindWheelMouse, &Button, LineSize); - } -} - -void CMenus::RenderSettingsProfiles(CUIRect MainView) -{ - CUIRect Label, LabelMid, Section, LabelRight; - static int s_SelectedProfile = -1; - - const float LineSize = 20.0f; - const float MarginSmall = 5.0f; - const float FontSize = 14.0f; - - char *pSkinName = g_Config.m_ClPlayerSkin; - int *pUseCustomColor = &g_Config.m_ClPlayerUseCustomColor; - unsigned *pColorBody = &g_Config.m_ClPlayerColorBody; - unsigned *pColorFeet = &g_Config.m_ClPlayerColorFeet; - int CurrentFlag = m_Dummy ? g_Config.m_ClDummyCountry : g_Config.m_PlayerCountry; - - if(m_Dummy) - { - pSkinName = g_Config.m_ClDummySkin; - pUseCustomColor = &g_Config.m_ClDummyUseCustomColor; - pColorBody = &g_Config.m_ClDummyColorBody; - pColorFeet = &g_Config.m_ClDummyColorFeet; - } - - // skin info - CTeeRenderInfo OwnSkinInfo; - const CSkin *pSkin = m_pClient->m_Skins.Find(pSkinName); - OwnSkinInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; - OwnSkinInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; - OwnSkinInfo.m_SkinMetrics = pSkin->m_Metrics; - OwnSkinInfo.m_CustomColoredSkin = *pUseCustomColor; - if(*pUseCustomColor) - { - OwnSkinInfo.m_ColorBody = color_cast(ColorHSLA(*pColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); - OwnSkinInfo.m_ColorFeet = color_cast(ColorHSLA(*pColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); - } - else - { - OwnSkinInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); - OwnSkinInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); - } - OwnSkinInfo.m_Size = 50.0f; - - //======YOUR PROFILE====== - char aTempBuf[256]; - str_format(aTempBuf, sizeof(aTempBuf), "%s:", Localize("Your profile")); - MainView.HSplitTop(LineSize, &Label, &MainView); - Ui()->DoLabel(&Label, aTempBuf, FontSize, TEXTALIGN_ML); - MainView.HSplitTop(MarginSmall, nullptr, &MainView); - - MainView.HSplitTop(50.0f, &Label, &MainView); - Label.VSplitLeft(250.0f, &Label, &LabelMid); - const CAnimState *pIdleState = CAnimState::GetIdle(); - vec2 OffsetToMid; - CRenderTools::GetRenderTeeOffsetToRenderedTee(pIdleState, &OwnSkinInfo, OffsetToMid); - vec2 TeeRenderPos(Label.x + LineSize, Label.y + Label.h / 2.0f + OffsetToMid.y); - int Emote = m_Dummy ? g_Config.m_ClDummyDefaultEyes : g_Config.m_ClPlayerDefaultEyes; - RenderTools()->RenderTee(pIdleState, &OwnSkinInfo, Emote, vec2(1.0f, 0.0f), TeeRenderPos); - - char aName[64]; - char aClan[64]; - str_format(aName, sizeof(aName), "%s", m_Dummy ? g_Config.m_ClDummyName : g_Config.m_PlayerName); - str_format(aClan, sizeof(aClan), "%s", m_Dummy ? g_Config.m_ClDummyClan : g_Config.m_PlayerClan); - - CUIRect FlagRect; - Label.VSplitLeft(90.0f, &FlagRect, &Label); - - Label.HSplitTop(LineSize, &Section, &Label); - str_format(aTempBuf, sizeof(aTempBuf), Localize("Name: %s"), aName); - Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); - - Label.HSplitTop(LineSize, &Section, &Label); - str_format(aTempBuf, sizeof(aTempBuf), Localize("Clan: %s"), aClan); - Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); - - Label.HSplitTop(LineSize, &Section, &Label); - str_format(aTempBuf, sizeof(aTempBuf), Localize("Skin: %s"), pSkinName); - Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); - - FlagRect.VSplitRight(50.0f, nullptr, &FlagRect); - FlagRect.HSplitBottom(25.0f, nullptr, &FlagRect); - FlagRect.y -= 10.0f; - ColorRGBA Color(1.0f, 1.0f, 1.0f, 1.0f); - m_pClient->m_CountryFlags.Render(m_Dummy ? g_Config.m_ClDummyCountry : g_Config.m_PlayerCountry, Color, FlagRect.x, FlagRect.y, FlagRect.w, FlagRect.h); - - bool DoSkin = g_Config.m_ClApplyProfileSkin; - bool DoColors = g_Config.m_ClApplyProfileColors; - bool DoEmote = g_Config.m_ClApplyProfileEmote; - bool DoName = g_Config.m_ClApplyProfileName; - bool DoClan = g_Config.m_ClApplyProfileClan; - bool DoFlag = g_Config.m_ClApplyProfileFlag; - - //======AFTER LOAD====== - if(s_SelectedProfile != -1 && s_SelectedProfile < (int)GameClient()->m_SkinProfiles.m_Profiles.size()) - { - CProfile LoadProfile = GameClient()->m_SkinProfiles.m_Profiles[s_SelectedProfile]; - MainView.HSplitTop(LineSize, nullptr, &MainView); - MainView.HSplitTop(10.0f, &Label, &MainView); - str_format(aTempBuf, sizeof(aTempBuf), "%s:", Localize("After Load")); - Ui()->DoLabel(&Label, aTempBuf, FontSize, TEXTALIGN_ML); - - MainView.HSplitTop(50.0f, &Label, &MainView); - Label.VSplitLeft(250.0f, &Label, nullptr); - - if(DoSkin && strlen(LoadProfile.SkinName) != 0) - { - const CSkin *pLoadSkin = m_pClient->m_Skins.Find(LoadProfile.SkinName); - OwnSkinInfo.m_OriginalRenderSkin = pLoadSkin->m_OriginalSkin; - OwnSkinInfo.m_ColorableRenderSkin = pLoadSkin->m_ColorableSkin; - OwnSkinInfo.m_SkinMetrics = pLoadSkin->m_Metrics; - } - if(*pUseCustomColor && DoColors && LoadProfile.BodyColor != -1 && LoadProfile.FeetColor != -1) - { - OwnSkinInfo.m_ColorBody = color_cast(ColorHSLA(LoadProfile.BodyColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); - OwnSkinInfo.m_ColorFeet = color_cast(ColorHSLA(LoadProfile.FeetColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); - } - - CRenderTools::GetRenderTeeOffsetToRenderedTee(pIdleState, &OwnSkinInfo, OffsetToMid); - TeeRenderPos = vec2(Label.x + LineSize, Label.y + Label.h / 2.0f + OffsetToMid.y); - int LoadEmote = Emote; - if(DoEmote && LoadProfile.Emote != -1) - LoadEmote = LoadProfile.Emote; - RenderTools()->RenderTee(pIdleState, &OwnSkinInfo, LoadEmote, vec2(1.0f, 0.0f), TeeRenderPos); - - if(DoName && strlen(LoadProfile.Name) != 0) - str_format(aName, sizeof(aName), "%s", LoadProfile.Name); - if(DoClan && strlen(LoadProfile.Clan) != 0) - str_format(aClan, sizeof(aClan), "%s", LoadProfile.Clan); - - Label.VSplitLeft(90.0f, &FlagRect, &Label); - - Label.HSplitTop(LineSize, &Section, &Label); - str_format(aTempBuf, sizeof(aTempBuf), Localize("Name: %s"), aName); - Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); - - Label.HSplitTop(LineSize, &Section, &Label); - str_format(aTempBuf, sizeof(aTempBuf), Localize("Clan: %s"), aClan); - Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); - - Label.HSplitTop(LineSize, &Section, &Label); - str_format(aTempBuf, sizeof(aTempBuf), Localize("Skin: %s"), (DoSkin && strlen(LoadProfile.SkinName) != 0) ? LoadProfile.SkinName : pSkinName); - Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); - - FlagRect.VSplitRight(50.0f, nullptr, &FlagRect); - FlagRect.HSplitBottom(25.0f, nullptr, &FlagRect); - FlagRect.y -= 10.0f; - int RenderFlag = m_Dummy ? g_Config.m_ClDummyCountry : g_Config.m_PlayerCountry; - if(DoFlag && LoadProfile.CountryFlag != -2) - RenderFlag = LoadProfile.CountryFlag; - m_pClient->m_CountryFlags.Render(RenderFlag, Color, FlagRect.x, FlagRect.y, FlagRect.w, FlagRect.h); - - str_format(aName, sizeof(aName), "%s", m_Dummy ? g_Config.m_ClDummyName : g_Config.m_PlayerName); - str_format(aClan, sizeof(aClan), "%s", m_Dummy ? g_Config.m_ClDummyClan : g_Config.m_PlayerClan); - } - else - { - MainView.HSplitTop(80.0f, nullptr, &MainView); - } - - //===BUTTONS AND CHECK BOX=== - CUIRect DummyCheck, CustomCheck; - MainView.HSplitTop(30.0f, &DummyCheck, nullptr); - DummyCheck.HSplitTop(13.0f, nullptr, &DummyCheck); - - DummyCheck.VSplitLeft(100.0f, &DummyCheck, &CustomCheck); - CustomCheck.VSplitLeft(150.0f, &CustomCheck, nullptr); - - DoButton_CheckBoxAutoVMarginAndSet(&m_Dummy, Localize("Dummy"), (int *)&m_Dummy, &DummyCheck, LineSize); - - static int s_CustomColorID = 0; - CustomCheck.HSplitTop(LineSize, &CustomCheck, nullptr); - - if(DoButton_CheckBox(&s_CustomColorID, Localize("Custom colors"), *pUseCustomColor, &CustomCheck)) - { - *pUseCustomColor = *pUseCustomColor ? 0 : 1; - SetNeedSendInfo(); - } - - LabelMid.VSplitLeft(20.0f, nullptr, &LabelMid); - LabelMid.VSplitLeft(160.0f, &LabelMid, &LabelRight); - - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileSkin, Localize("Save/Load Skin"), &g_Config.m_ClApplyProfileSkin, &LabelMid, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileColors, Localize("Save/Load Colors"), &g_Config.m_ClApplyProfileColors, &LabelMid, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileEmote, Localize("Save/Load Emote"), &g_Config.m_ClApplyProfileEmote, &LabelMid, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileName, Localize("Save/Load Name"), &g_Config.m_ClApplyProfileName, &LabelMid, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileClan, Localize("Save/Load Clan"), &g_Config.m_ClApplyProfileClan, &LabelMid, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileFlag, Localize("Save/Load Flag"), &g_Config.m_ClApplyProfileFlag, &LabelMid, LineSize); - - CUIRect Button; - LabelRight.VSplitLeft(150.0f, &LabelRight, nullptr); - - LabelRight.HSplitTop(30.0f, &Button, &LabelRight); - static CButtonContainer s_LoadButton; - - if(DoButton_Menu(&s_LoadButton, Localize("Load"), 0, &Button)) - { - if(s_SelectedProfile != -1 && s_SelectedProfile < (int)GameClient()->m_SkinProfiles.m_Profiles.size()) - { - CProfile LoadProfile = GameClient()->m_SkinProfiles.m_Profiles[s_SelectedProfile]; - if(!m_Dummy) - { - if(DoSkin && strlen(LoadProfile.SkinName) != 0) - str_copy(g_Config.m_ClPlayerSkin, LoadProfile.SkinName, sizeof(g_Config.m_ClPlayerSkin)); - if(DoColors && LoadProfile.BodyColor != -1 && LoadProfile.FeetColor != -1) - { - g_Config.m_ClPlayerColorBody = LoadProfile.BodyColor; - g_Config.m_ClPlayerColorFeet = LoadProfile.FeetColor; - } - if(DoEmote && LoadProfile.Emote != -1) - g_Config.m_ClPlayerDefaultEyes = LoadProfile.Emote; - if(DoName && strlen(LoadProfile.Name) != 0) - str_copy(g_Config.m_PlayerName, LoadProfile.Name, sizeof(g_Config.m_PlayerName)); - if(DoClan && strlen(LoadProfile.Clan) != 0) - str_copy(g_Config.m_PlayerClan, LoadProfile.Clan, sizeof(g_Config.m_PlayerClan)); - if(DoFlag && LoadProfile.CountryFlag != -2) - g_Config.m_PlayerCountry = LoadProfile.CountryFlag; - } - else - { - if(DoSkin && strlen(LoadProfile.SkinName) != 0) - str_copy(g_Config.m_ClDummySkin, LoadProfile.SkinName, sizeof(g_Config.m_ClDummySkin)); - if(DoColors && LoadProfile.BodyColor != -1 && LoadProfile.FeetColor != -1) - { - g_Config.m_ClDummyColorBody = LoadProfile.BodyColor; - g_Config.m_ClDummyColorFeet = LoadProfile.FeetColor; - } - if(DoEmote && LoadProfile.Emote != -1) - g_Config.m_ClDummyDefaultEyes = LoadProfile.Emote; - if(DoName && strlen(LoadProfile.Name) != 0) - str_copy(g_Config.m_ClDummyName, LoadProfile.Name, sizeof(g_Config.m_ClDummyName)); - if(DoClan && strlen(LoadProfile.Clan) != 0) - str_copy(g_Config.m_ClDummyClan, LoadProfile.Clan, sizeof(g_Config.m_ClDummyClan)); - if(DoFlag && LoadProfile.CountryFlag != -2) - g_Config.m_ClDummyCountry = LoadProfile.CountryFlag; - } - } - SetNeedSendInfo(); - } - LabelRight.HSplitTop(5.0f, nullptr, &LabelRight); - - LabelRight.HSplitTop(30.0f, &Button, &LabelRight); - static CButtonContainer s_SaveButton; - if(DoButton_Menu(&s_SaveButton, Localize("Save"), 0, &Button)) - { - GameClient()->m_SkinProfiles.AddProfile( - DoColors ? *pColorBody : -1, - DoColors ? *pColorFeet : -1, - DoFlag ? CurrentFlag : -2, - DoEmote ? Emote : -1, - DoSkin ? pSkinName : "", - DoName ? aName : "", - DoClan ? aClan : ""); - GameClient()->m_SkinProfiles.SaveProfiles(); - } - LabelRight.HSplitTop(5.0f, nullptr, &LabelRight); - - static int s_AllowDelete; - DoButton_CheckBoxAutoVMarginAndSet(&s_AllowDelete, Localizable("Enable Deleting"), &s_AllowDelete, &LabelRight, LineSize); - LabelRight.HSplitTop(5.0f, nullptr, &LabelRight); - - if(s_AllowDelete) - { - LabelRight.HSplitTop(28.0f, &Button, &LabelRight); - static CButtonContainer s_DeleteButton; - if(DoButton_Menu(&s_DeleteButton, Localize("Delete"), 0, &Button)) - { - if(s_SelectedProfile != -1 && s_SelectedProfile < (int)GameClient()->m_SkinProfiles.m_Profiles.size()) - { - GameClient()->m_SkinProfiles.m_Profiles.erase(GameClient()->m_SkinProfiles.m_Profiles.begin() + s_SelectedProfile); - GameClient()->m_SkinProfiles.SaveProfiles(); - } - } - LabelRight.HSplitTop(5.0f, nullptr, &LabelRight); - - LabelRight.HSplitTop(28.0f, &Button, &LabelRight); - static CButtonContainer s_OverrideButton; - if(DoButton_Menu(&s_OverrideButton, Localize("Override"), 0, &Button)) - { - if(s_SelectedProfile != -1 && s_SelectedProfile < (int)GameClient()->m_SkinProfiles.m_Profiles.size()) - { - GameClient()->m_SkinProfiles.m_Profiles[s_SelectedProfile] = CProfile( - DoColors ? *pColorBody : -1, - DoColors ? *pColorFeet : -1, - DoFlag ? CurrentFlag : -2, - DoEmote ? Emote : -1, - DoSkin ? pSkinName : "", - DoName ? aName : "", - DoClan ? aClan : ""); - GameClient()->m_SkinProfiles.SaveProfiles(); - } - } - } - - //---RENDER THE SELECTOR--- - CUIRect FileButton; - CUIRect SelectorRect; - MainView.HSplitTop(50.0f, nullptr, &SelectorRect); - SelectorRect.HSplitBottom(LineSize, &SelectorRect, &FileButton); - SelectorRect.HSplitBottom(MarginSmall, &SelectorRect, nullptr); - std::vector *pProfileList = &GameClient()->m_SkinProfiles.m_Profiles; - - static CListBox s_ListBox; - s_ListBox.DoStart(50.0f, pProfileList->size(), 4, 3, s_SelectedProfile, &SelectorRect, true); - - static bool s_Indexs[1024]; - - for(size_t i = 0; i < pProfileList->size(); ++i) - { - CProfile CurrentProfile = GameClient()->m_SkinProfiles.m_Profiles[i]; - - char RenderSkin[24]; - if(strlen(CurrentProfile.SkinName) == 0) - str_copy(RenderSkin, pSkinName, sizeof(RenderSkin)); - else - str_copy(RenderSkin, CurrentProfile.SkinName, sizeof(RenderSkin)); - - const CSkin *pSkinToBeDraw = m_pClient->m_Skins.Find(RenderSkin); - - CListboxItem Item = s_ListBox.DoNextItem(&s_Indexs[i], s_SelectedProfile >= 0 && (size_t)s_SelectedProfile == i); - - if(!Item.m_Visible) - continue; - - if(Item.m_Visible) - { - CTeeRenderInfo Info; - Info.m_ColorBody = color_cast(ColorHSLA(CurrentProfile.BodyColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); - Info.m_ColorFeet = color_cast(ColorHSLA(CurrentProfile.FeetColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); - Info.m_CustomColoredSkin = true; - Info.m_OriginalRenderSkin = pSkinToBeDraw->m_OriginalSkin; - Info.m_ColorableRenderSkin = pSkinToBeDraw->m_ColorableSkin; - Info.m_SkinMetrics = pSkinToBeDraw->m_Metrics; - Info.m_Size = 50.0f; - if(CurrentProfile.BodyColor == -1 && CurrentProfile.FeetColor == -1) - { - Info.m_CustomColoredSkin = m_Dummy ? g_Config.m_ClDummyUseCustomColor : g_Config.m_ClPlayerUseCustomColor; - Info.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); - Info.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); - } - - CRenderTools::GetRenderTeeOffsetToRenderedTee(pIdleState, &Info, OffsetToMid); - - int RenderEmote = CurrentProfile.Emote == -1 ? Emote : CurrentProfile.Emote; - TeeRenderPos = vec2(Item.m_Rect.x + 30.0f, Item.m_Rect.y + Item.m_Rect.h / 2.0f + OffsetToMid.y); - - Item.m_Rect.VSplitLeft(60.0f, nullptr, &Item.m_Rect); - CUIRect PlayerRect, ClanRect, FeetColorSquare, BodyColorSquare; - - Item.m_Rect.VSplitLeft(60.0f, nullptr, &BodyColorSquare); // Delete this maybe - - Item.m_Rect.VSplitRight(60.0f, &BodyColorSquare, &FlagRect); - BodyColorSquare.x -= 11.0f; - BodyColorSquare.VSplitLeft(10.0f, &BodyColorSquare, nullptr); - BodyColorSquare.HSplitMid(&BodyColorSquare, &FeetColorSquare); - BodyColorSquare.HSplitMid(nullptr, &BodyColorSquare); - FeetColorSquare.HSplitMid(&FeetColorSquare, nullptr); - FlagRect.HSplitBottom(10.0f, &FlagRect, nullptr); - FlagRect.HSplitTop(10.0f, nullptr, &FlagRect); - - Item.m_Rect.HSplitMid(&PlayerRect, &ClanRect); - - SLabelProperties Props; - Props.m_MaxWidth = Item.m_Rect.w; - if(CurrentProfile.CountryFlag != -2) - m_pClient->m_CountryFlags.Render(CurrentProfile.CountryFlag, Color, FlagRect.x, FlagRect.y, FlagRect.w, FlagRect.h); - - if(CurrentProfile.BodyColor != -1 && CurrentProfile.FeetColor != -1) - { - ColorRGBA BodyColor = color_cast(ColorHSLA(CurrentProfile.BodyColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); - ColorRGBA FeetColor = color_cast(ColorHSLA(CurrentProfile.FeetColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); - - Graphics()->TextureClear(); - Graphics()->QuadsBegin(); - Graphics()->SetColor(BodyColor.r, BodyColor.g, BodyColor.b, 1.0f); - IGraphics::CQuadItem Quads[2]; - Quads[0] = IGraphics::CQuadItem(BodyColorSquare.x, BodyColorSquare.y, BodyColorSquare.w, BodyColorSquare.h); - Graphics()->QuadsDrawTL(&Quads[0], 1); - Graphics()->SetColor(FeetColor.r, FeetColor.g, FeetColor.b, 1.0f); - Quads[1] = IGraphics::CQuadItem(FeetColorSquare.x, FeetColorSquare.y, FeetColorSquare.w, FeetColorSquare.h); - Graphics()->QuadsDrawTL(&Quads[1], 1); - Graphics()->QuadsEnd(); - } - RenderTools()->RenderTee(pIdleState, &Info, RenderEmote, vec2(1.0f, 0.0f), TeeRenderPos); - - if(strlen(CurrentProfile.Name) == 0 && strlen(CurrentProfile.Clan) == 0) - { - PlayerRect = Item.m_Rect; - PlayerRect.y += MarginSmall; - Ui()->DoLabel(&PlayerRect, CurrentProfile.SkinName, FontSize, TEXTALIGN_ML, Props); - } - else - { - Ui()->DoLabel(&PlayerRect, CurrentProfile.Name, FontSize, TEXTALIGN_ML, Props); - Item.m_Rect.HSplitTop(LineSize, nullptr, &Item.m_Rect); - Props.m_MaxWidth = Item.m_Rect.w; - Ui()->DoLabel(&ClanRect, CurrentProfile.Clan, FontSize, TEXTALIGN_ML, Props); - } - } - } - - const int NewSelected = s_ListBox.DoEnd(); - if(s_SelectedProfile != NewSelected) - { - s_SelectedProfile = NewSelected; - } - static CButtonContainer s_ProfilesFile; - FileButton.VSplitLeft(130.0f, &FileButton, nullptr); - if(DoButton_Menu(&s_ProfilesFile, Localize("Profiles file"), 0, &FileButton)) - { - Storage()->GetCompletePath(IStorage::TYPE_SAVE, PROFILES_FILE, aTempBuf, sizeof(aTempBuf)); - if(!open_file(aTempBuf)) - { - dbg_msg("menus", "couldn't open file"); - } - } -} - void CMenus::RenderSettingsDDNet(CUIRect MainView) { CUIRect Button, Left, Right, LeftLeft, Label; diff --git a/src/game/client/components/tclient/menus_tclient.cpp b/src/game/client/components/tclient/menus_tclient.cpp new file mode 100644 index 00000000000..d8a9130776c --- /dev/null +++ b/src/game/client/components/tclient/menus_tclient.cpp @@ -0,0 +1,1125 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../binds.h" +#include "../countryflags.h" +#include "../menus.h" +#include "../skins.h" + +#include +#include +#include +#include +#include +#include + +void CMenus::OpenTClientDiscord() +{ + if(!open_link("https://discord.gg/fBvhH93Bt6")) + PopupWarning(Localize("Open TClient Discord"), Localize("Failed to open the TClient Discord in your browser"), Localize("Aww"), std::chrono::nanoseconds(0)); +} + +enum +{ + TCLIENT_TAB_SETTINGS = 0, + TCLIENT_TAB_BINDWHEEL = 1, + TCLIENT_TAB_DISCORD = 2, + NUMBER_OF_TCLIENT_TABS = 3 +}; + +typedef struct +{ + const char *m_pName; + const char *m_pCommand; + int m_KeyId; + int m_ModifierCombination; +} CKeyInfo; + +using namespace FontIcons; + +bool CMenus::DoSliderWithScaledValue(const void *pId, int *pOption, const CUIRect *pRect, const char *pStr, int Min, int Max, int Scale, const IScrollbarScale *pScale, unsigned Flags, const char *pSuffix) +{ + const bool NoClampValue = Flags & CUi::SCROLLBAR_OPTION_NOCLAMPVALUE; + + int Value = *pOption; + Min /= Scale; + Max /= Scale; + // Allow adjustment of slider options when ctrl is pressed (to avoid scrolling, or accidently adjusting the value) + int Increment = std::max(1, (Max - Min) / 35); + if(Input()->ModifierIsPressed() && Input()->KeyPress(KEY_MOUSE_WHEEL_UP) && Ui()->MouseInside(pRect)) + { + Value += Increment; + Value = clamp(Value, Min, Max); + } + if(Input()->ModifierIsPressed() && Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN) && Ui()->MouseInside(pRect)) + { + Value -= Increment; + Value = clamp(Value, Min, Max); + } + + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "%s: %i%s", pStr, Value * Scale, pSuffix); + + if(NoClampValue) + { + // clamp the value internally for the scrollbar + Value = clamp(Value, Min, Max); + } + + CUIRect Label, ScrollBar; + pRect->VSplitMid(&Label, &ScrollBar, minimum(10.0f, pRect->w * 0.05f)); + + const float FontSize = Label.h * CUi::ms_FontmodHeight * 0.8f; + Ui()->DoLabel(&Label, aBuf, FontSize, TEXTALIGN_ML); + + Value = pScale->ToAbsolute(Ui()->DoScrollbarH(pId, &ScrollBar, pScale->ToRelative(Value, Min, Max)), Min, Max); + if(NoClampValue && ((Value == Min && *pOption < Min) || (Value == Max && *pOption > Max))) + { + Value = *pOption; + } + + if(*pOption != Value) + { + *pOption = Value; + return true; + } + return false; +} + +void CMenus::RenderSettingsTClient(CUIRect MainView) +{ + static int s_CurCustomTab = 0; + + const float LineSize = 20.0f; + const float ColorPickerLineSize = 25.0f; + const float HeadlineFontSize = 20.0f; + const float HeadlineHeight = HeadlineFontSize + 0.0f; + const float Margin = 10.0f; + const float MarginSmall = 5.0f; + const float MarginExtraSmall = 2.5f; + const float MarginBetweenSections = 30.0f; + const float MarginBetweenViews = 30.0f; + + const float ColorPickerLabelSize = 13.0f; + const float ColorPickerLineSpacing = 5.0f; + + CUIRect TabBar, Column, LeftView, RightView, Button, Label; + + MainView.HSplitTop(LineSize, &TabBar, &MainView); + const float TabWidth = TabBar.w / NUMBER_OF_TCLIENT_TABS; + static CButtonContainer s_aPageTabs[NUMBER_OF_TCLIENT_TABS] = {}; + const char *apTabNames[] = { + Localize("Settings"), + Localize("Bindwheel"), + Localize("Discord")}; + + for(int Tab = 0; Tab < NUMBER_OF_TCLIENT_TABS; ++Tab) + { + TabBar.VSplitLeft(TabWidth, &Button, &TabBar); + const int Corners = Tab == 0 ? IGraphics::CORNER_L : Tab == NUMBER_OF_TCLIENT_TABS - 1 ? IGraphics::CORNER_R : + IGraphics::CORNER_NONE; + if(DoButton_MenuTab(&s_aPageTabs[Tab], apTabNames[Tab], s_CurCustomTab == Tab, &Button, Corners, nullptr, nullptr, nullptr, nullptr, 4.0f)) + { + if(Tab == TCLIENT_TAB_DISCORD) + PopupConfirm(Localize("Open TClient Discord"), Localize("Click open to open the TClient Discord invite in your browser"), Localize("Open"), Localize("Cancel"), &CMenus::OpenTClientDiscord); + else + s_CurCustomTab = Tab; + break; + } + } + + MainView.HSplitTop(MarginSmall, nullptr, &MainView); + + if(s_CurCustomTab == TCLIENT_TAB_SETTINGS) + { + static CScrollRegion s_ScrollRegion; + vec2 ScrollOffset(0.0f, 0.0f); + CScrollRegionParams ScrollParams; + ScrollParams.m_ScrollUnit = 120.0f; + ScrollParams.m_Flags = CScrollRegionParams::FLAG_CONTENT_STATIC_WIDTH; + ScrollParams.m_ScrollbarMargin = 5.0f; + s_ScrollRegion.Begin(&MainView, &ScrollOffset, &ScrollParams); + + static std::vector s_SectionBoxes; + static vec2 s_PrevScrollOffset(0.0f, 0.0f); + + MainView.y += ScrollOffset.y; + + MainView.VSplitRight(5.0f, &MainView, nullptr); // Padding for scrollbar + MainView.VSplitLeft(5.0f, nullptr, &MainView); // Padding for scrollbar + + MainView.VSplitMid(&LeftView, &RightView, MarginBetweenViews); + LeftView.VSplitLeft(MarginSmall, nullptr, &LeftView); + RightView.VSplitRight(MarginSmall, &RightView, nullptr); + + // RightView.VSplitRight(10.0f, &RightView, nullptr); + for(CUIRect &Section : s_SectionBoxes) + { + float Padding = MarginBetweenViews * 0.6666f; + Section.w += Padding; + Section.h += Padding; + Section.x -= Padding * 0.5f; + Section.y -= Padding * 0.5f; + Section.y -= s_PrevScrollOffset.y - ScrollOffset.y; + float Shade = 0.0f; + Section.Draw(ColorRGBA(Shade, Shade, Shade, 0.25f), IGraphics::CORNER_ALL, 10.0f); + } + s_PrevScrollOffset = ScrollOffset; + s_SectionBoxes.clear(); + + // ***** LeftView ***** // + Column = LeftView; + + // ***** Visual Miscellaneous ***** // + Column.HSplitTop(Margin, nullptr, &Column); + s_SectionBoxes.push_back(Column); + Column.HSplitTop(HeadlineHeight, &Label, &Column); + Ui()->DoLabel(&Label, Localize("Visual"), HeadlineFontSize, TEXTALIGN_ML); + Column.HSplitTop(MarginSmall, nullptr, &Column); + + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClFreezeUpdateFix, Localize("Update tee skin faster after being frozen"), &g_Config.m_ClFreezeUpdateFix, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClPingNameCircle, Localize("Show ping colored circle before names"), &g_Config.m_ClPingNameCircle, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRenderNameplateSpec, Localize("Hide nameplates in spec"), &g_Config.m_ClRenderNameplateSpec, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowSkinName, Localize("Show skin names in nameplate"), &g_Config.m_ClShowSkinName, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClFreezeStars, Localize("Freeze Stars"), &g_Config.m_ClFreezeStars, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClColorFreeze, Localize("Color Frozen Tee Skins"), &g_Config.m_ClColorFreeze, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClHammerRotatesWithCursor, Localize("Make hammer rotate with cursor"), &g_Config.m_ClHammerRotatesWithCursor, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClWhiteFeet, Localize("Render all custom colored feet as white feet skin"), &g_Config.m_ClWhiteFeet, &Column, LineSize); + CUIRect FeetBox; + Column.HSplitTop(LineSize + MarginExtraSmall, &FeetBox, &Column); + if(g_Config.m_ClWhiteFeet) + { + FeetBox.HSplitTop(MarginExtraSmall, nullptr, &FeetBox); + FeetBox.VSplitMid(&FeetBox, nullptr); + static CLineInput s_WhiteFeet(g_Config.m_ClWhiteFeetSkin, sizeof(g_Config.m_ClWhiteFeetSkin)); + s_WhiteFeet.SetEmptyText("x_ninja"); + Ui()->DoEditBox(&s_WhiteFeet, &FeetBox, 12.0f); + } + Column.HSplitTop(MarginExtraSmall, nullptr, &Column); + + s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; + + // ***** Input ***** // + Column.HSplitTop(MarginBetweenSections, nullptr, &Column); + s_SectionBoxes.push_back(Column); + Column.HSplitTop(HeadlineHeight, &Label, &Column); + Ui()->DoLabel(&Label, Localize("Input"), HeadlineFontSize, TEXTALIGN_ML); + Column.HSplitTop(MarginSmall, nullptr, &Column); + + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClFastInput, Localize("Fast Inputs (-20ms input delay)"), &g_Config.m_ClFastInput, &Column, LineSize); + + Column.HSplitTop(MarginSmall, nullptr, &Column); + if(g_Config.m_ClFastInput) + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClFastInputOthers, Localize("Extra tick other tees (increases other tees latency, \nmakes dragging slightly easier when using fast input)"), &g_Config.m_ClFastInputOthers, &Column, LineSize); + else + Column.HSplitTop(LineSize, nullptr, &Column); + // A little extra spacing because these are multi line + Column.HSplitTop(MarginSmall, nullptr, &Column); + + Column.HSplitTop(MarginSmall, nullptr, &Column); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOldMouseZoom, Localize("Old Mouse Precision (fixes precision at low zoom levels, \nbreaks /tc, /telecursor while zoomed)"), &g_Config.m_ClOldMouseZoom, &Column, LineSize); + Column.HSplitTop(MarginSmall, nullptr, &Column); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClImproveMousePrecision, Localize("Improve mouse precision by scaling sent max distance to 1000"), &g_Config.m_ClImproveMousePrecision, &Column, LineSize); + s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; + + // ***** Anti Latency Tools ***** // + Column.HSplitTop(MarginBetweenSections, nullptr, &Column); + s_SectionBoxes.push_back(Column); + Column.HSplitTop(HeadlineHeight, &Label, &Column); + Ui()->DoLabel(&Label, Localize("Anti Latency Tools"), HeadlineFontSize, TEXTALIGN_ML); + Column.HSplitTop(MarginSmall, nullptr, &Column); + + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClPredictionMargin, &g_Config.m_ClPredictionMargin, &Button, Localize("Prediction Margin"), 10, 25, &CUi::ms_LinearScrollbarScale, 0, "ms"); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRemoveAnti, Localize("Remove prediction & antiping in freeze"), &g_Config.m_ClRemoveAnti, &Column, LineSize); + if(g_Config.m_ClRemoveAnti) + { + if(g_Config.m_ClUnfreezeLagDelayTicks < g_Config.m_ClUnfreezeLagTicks) + g_Config.m_ClUnfreezeLagDelayTicks = g_Config.m_ClUnfreezeLagTicks; + Column.HSplitTop(LineSize, &Button, &Column); + DoSliderWithScaledValue(&g_Config.m_ClUnfreezeLagTicks, &g_Config.m_ClUnfreezeLagTicks, &Button, Localize("Amount"), 100, 300, 20, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_NOCLAMPVALUE, "ms"); + Column.HSplitTop(LineSize, &Button, &Column); + DoSliderWithScaledValue(&g_Config.m_ClUnfreezeLagDelayTicks, &g_Config.m_ClUnfreezeLagDelayTicks, &Button, Localize("Delay"), 100, 3000, 20, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_NOCLAMPVALUE, "ms"); + } + else + Column.HSplitTop(LineSize * 2, nullptr, &Column); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClUnpredOthersInFreeze, Localize("Dont predict other players if you are frozen"), &g_Config.m_ClUnpredOthersInFreeze, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClPredMarginInFreeze, Localize("Adjust your prediction margin while frozen"), &g_Config.m_ClPredMarginInFreeze, &Column, LineSize); + Column.HSplitTop(LineSize, &Button, &Column); + if(g_Config.m_ClPredMarginInFreeze) + Ui()->DoScrollbarOption(&g_Config.m_ClPredMarginInFreezeAmount, &g_Config.m_ClPredMarginInFreezeAmount, &Button, Localize("Frozen Margin"), 0, 100, &CUi::ms_LinearScrollbarScale, 0, "ms"); + s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; + + // ***** Other ***** // + Column.HSplitTop(MarginBetweenSections, nullptr, &Column); + s_SectionBoxes.push_back(Column); + Column.HSplitTop(HeadlineHeight, &Label, &Column); + Ui()->DoLabel(&Label, Localize("Other"), HeadlineFontSize, TEXTALIGN_ML); + Column.HSplitTop(MarginSmall, nullptr, &Column); + + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRunOnJoinConsole, Localize("Run cl_run_on_join as console command"), &g_Config.m_ClRunOnJoinConsole, &Column, LineSize); + Column.HSplitTop(LineSize, &Button, &Column); + if(g_Config.m_ClRunOnJoinConsole) + { + DoSliderWithScaledValue(&g_Config.m_ClRunOnJoinDelay, &g_Config.m_ClRunOnJoinDelay, &Button, Localize("Delay"), 140, 2000, 20, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_NOCLAMPVALUE, "ms"); + } + CUIRect ButtonVerify, EnableVerifySection; + Column.HSplitTop(LineSize, &EnableVerifySection, &Column); + EnableVerifySection.VSplitMid(&EnableVerifySection, &ButtonVerify); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClAutoVerify, Localize("Auto Verify"), &g_Config.m_ClAutoVerify, &EnableVerifySection, LineSize); + static CButtonContainer s_VerifyButton; + if(DoButton_Menu(&s_VerifyButton, Localize("Manual Verify"), 0, &ButtonVerify, 0, IGraphics::CORNER_ALL)) + { + if(!open_link("https://ger10.ddnet.org/")) + dbg_msg("menus", "couldn't open link"); + } + Column.HSplitTop(MarginExtraSmall, nullptr, &Column); + s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; + + // ***** Player Indicator ***** // + Column.HSplitTop(MarginBetweenSections, nullptr, &Column); + s_SectionBoxes.push_back(Column); + Column.HSplitTop(HeadlineHeight, &Label, &Column); + Ui()->DoLabel(&Label, Localize("Player Indicator"), HeadlineFontSize, TEXTALIGN_ML); + Column.HSplitTop(MarginSmall, nullptr, &Column); + + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClPlayerIndicator, Localize("Show any enabled Indicators"), &g_Config.m_ClPlayerIndicator, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClPlayerIndicatorFreeze, Localize("Show only freeze Players"), &g_Config.m_ClPlayerIndicatorFreeze, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClIndicatorTeamOnly, Localize("Only show after joining a team"), &g_Config.m_ClIndicatorTeamOnly, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClIndicatorTees, Localize("Render tiny tees instead of circles"), &g_Config.m_ClIndicatorTees, &Column, LineSize); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorRadius, &g_Config.m_ClIndicatorRadius, &Button, Localize("Indicator size"), 1, 16); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorOpacity, &g_Config.m_ClIndicatorOpacity, &Button, Localize("Indicator opacity"), 0, 100); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClIndicatorVariableDistance, Localize("Change indicator offset based on distance to other tees"), &g_Config.m_ClIndicatorVariableDistance, &Column, LineSize); + if(g_Config.m_ClIndicatorVariableDistance) + { + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorOffset, &g_Config.m_ClIndicatorOffset, &Button, Localize("Indicator min offset"), 16, 200); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorOffsetMax, &g_Config.m_ClIndicatorOffsetMax, &Button, Localize("Indicator max offset"), 16, 200); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorMaxDistance, &g_Config.m_ClIndicatorMaxDistance, &Button, Localize("Indicator max distance"), 500, 7000); + } + else + { + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClIndicatorOffset, &g_Config.m_ClIndicatorOffset, &Button, Localize("Indicator offset"), 16, 200); + Column.HSplitTop(LineSize * 2, nullptr, &Column); + } + static CButtonContainer IndicatorAliveColorID, IndicatorDeadColorID, IndicatorSavedColorID; + DoLine_ColorPicker(&IndicatorAliveColorID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Indicator alive color"), &g_Config.m_ClIndicatorAlive, ColorRGBA(0.0f, 0.0f, 0.0f), false); + DoLine_ColorPicker(&IndicatorDeadColorID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Indicator dead color"), &g_Config.m_ClIndicatorFreeze, ColorRGBA(0.0f, 0.0f, 0.0f), false); + DoLine_ColorPicker(&IndicatorSavedColorID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Indicator save color"), &g_Config.m_ClIndicatorSaved, ColorRGBA(0.0f, 0.0f, 0.0f), false); + s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; + + // ***** RightView ***** // + LeftView = Column; + Column = RightView; + + // ***** HUD ***** // + Column.HSplitTop(Margin, nullptr, &Column); + s_SectionBoxes.push_back(Column); + Column.HSplitTop(HeadlineHeight, &Label, &Column); + Ui()->DoLabel(&Label, Localize("HUD"), HeadlineFontSize, TEXTALIGN_ML); + Column.HSplitTop(MarginSmall, nullptr, &Column); + + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowCenterLines, Localize("Show screen center"), &g_Config.m_ClShowCenterLines, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClMiniDebug, Localize("Show Position and angle (Mini debug)"), &g_Config.m_ClMiniDebug, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRenderCursorSpec, Localize("Show your cursor when in free spectate"), &g_Config.m_ClRenderCursorSpec, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNotifyWhenLast, Localize("Show when you are the last alive"), &g_Config.m_ClNotifyWhenLast, &Column, LineSize); + CUIRect NotificationConfig; + Column.HSplitTop(LineSize + MarginSmall, &NotificationConfig, &Column); + if(g_Config.m_ClNotifyWhenLast) + { + NotificationConfig.VSplitMid(&Button, &NotificationConfig); + static CLineInput s_LastInput(g_Config.m_ClNotifyWhenLastText, sizeof(g_Config.m_ClNotifyWhenLastText)); + s_LastInput.SetEmptyText(Localize("Last!")); + Button.HSplitTop(MarginSmall, nullptr, &Button); + Ui()->DoEditBox(&s_LastInput, &Button, 12.0f); + static CButtonContainer s_ClientNotifyWhenLastColor; + DoLine_ColorPicker(&s_ClientNotifyWhenLastColor, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &NotificationConfig, "", &g_Config.m_ClNotifyWhenLastColor, ColorRGBA(1.0f, 1.0f, 1.0f), false); + } + Column.HSplitTop(MarginExtraSmall, nullptr, &Column); + s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; + + // ***** Frozen Tee Display ***** // + Column.HSplitTop(MarginBetweenSections, nullptr, &Column); + s_SectionBoxes.push_back(Column); + Column.HSplitTop(HeadlineHeight, &Label, &Column); + Ui()->DoLabel(&Label, Localize("Frozen Tee Display"), HeadlineFontSize, TEXTALIGN_ML); + Column.HSplitTop(MarginSmall, nullptr, &Column); + + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowFrozenHud, Localize("Show frozen tee display"), &g_Config.m_ClShowFrozenHud, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowFrozenHudSkins, Localize("Use skins instead of ninja tees"), &g_Config.m_ClShowFrozenHudSkins, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClFrozenHudTeamOnly, Localize("Only show after joining a team"), &g_Config.m_ClFrozenHudTeamOnly, &Column, LineSize); + + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClFrozenMaxRows, &g_Config.m_ClFrozenMaxRows, &Button, Localize("Max Rows"), 1, 6); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClFrozenHudTeeSize, &g_Config.m_ClFrozenHudTeeSize, &Button, Localize("Tee Size"), 8, 27); + + { + CUIRect CheckBoxRect, CheckBoxRect2; + Column.HSplitTop(LineSize, &CheckBoxRect, &Column); + Column.HSplitTop(LineSize, &CheckBoxRect2, &Column); + if(DoButton_CheckBox(&g_Config.m_ClShowFrozenText, Localize("Tees Left Alive Text"), g_Config.m_ClShowFrozenText >= 1, &CheckBoxRect)) + g_Config.m_ClShowFrozenText = g_Config.m_ClShowFrozenText >= 1 ? 0 : 1; + + if(g_Config.m_ClShowFrozenText) + { + static int s_CountFrozenText = 0; + if(DoButton_CheckBox(&s_CountFrozenText, Localize("Count Frozen Tees"), g_Config.m_ClShowFrozenText == 2, &CheckBoxRect2)) + g_Config.m_ClShowFrozenText = g_Config.m_ClShowFrozenText != 2 ? 2 : 1; + } + } + s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; + + // ***** Tile Outlines ***** // + Column.HSplitTop(MarginBetweenSections, nullptr, &Column); + s_SectionBoxes.push_back(Column); + Column.HSplitTop(HeadlineHeight, &Label, &Column); + Ui()->DoLabel(&Label, Localize("Tile Outlines"), HeadlineFontSize, TEXTALIGN_ML); + Column.HSplitTop(MarginSmall, nullptr, &Column); + + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutline, Localize("Show any enabled outlines"), &g_Config.m_ClOutline, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutlineEntities, Localize("Only show outlines in entities"), &g_Config.m_ClOutlineEntities, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutlineFreeze, Localize("Outline freeze & deep"), &g_Config.m_ClOutlineFreeze, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutlineSolid, Localize("Outline walls"), &g_Config.m_ClOutlineSolid, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutlineTele, Localize("Outline teleporter"), &g_Config.m_ClOutlineTele, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClOutlineUnFreeze, Localize("Outline unfreeze & undeep"), &g_Config.m_ClOutlineUnFreeze, &Column, LineSize); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClOutlineWidth, &g_Config.m_ClOutlineWidth, &Button, Localize("Outline Width"), 1, 16); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClOutlineAlpha, &g_Config.m_ClOutlineAlpha, &Button, Localize("Outline Alpha"), 0, 100); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClOutlineAlphaSolid, &g_Config.m_ClOutlineAlphaSolid, &Button, Localize("Outline Alpha (walls)"), 0, 100); + static CButtonContainer OutlineColorFreezeID, OutlineColorSolidID, OutlineColorTeleID, OutlineColorUnfreezeID; + DoLine_ColorPicker(&OutlineColorFreezeID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Freeze Outline Color"), &g_Config.m_ClOutlineColorFreeze, ColorRGBA(0.0f, 0.0f, 0.0f), false); + DoLine_ColorPicker(&OutlineColorSolidID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Walls Outline Color"), &g_Config.m_ClOutlineColorSolid, ColorRGBA(0.0f, 0.0f, 0.0f), false); + DoLine_ColorPicker(&OutlineColorTeleID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Teleporter Outline Color"), &g_Config.m_ClOutlineColorTele, ColorRGBA(0.0f, 0.0f, 0.0f), false); + DoLine_ColorPicker(&OutlineColorUnfreezeID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Column, Localize("Unfreeze Outline Color"), &g_Config.m_ClOutlineColorUnfreeze, ColorRGBA(0.0f, 0.0f, 0.0f), false); + s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; + + // ***** Ghost Tools ***** // + Column.HSplitTop(MarginBetweenSections, nullptr, &Column); + s_SectionBoxes.push_back(Column); + Column.HSplitTop(HeadlineHeight, &Label, &Column); + Ui()->DoLabel(&Label, Localize("Ghost Tools"), HeadlineFontSize, TEXTALIGN_ML); + Column.HSplitTop(MarginSmall, nullptr, &Column); + + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowOthersGhosts, Localize("Show unpredicted ghosts for other players"), &g_Config.m_ClShowOthersGhosts, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClSwapGhosts, Localize("Swap ghosts and normal players"), &g_Config.m_ClSwapGhosts, &Column, LineSize); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClPredGhostsAlpha, &g_Config.m_ClPredGhostsAlpha, &Button, Localize("Predicted Alpha"), 0, 100, &CUi::ms_LinearScrollbarScale, 0, "%"); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClUnpredGhostsAlpha, &g_Config.m_ClUnpredGhostsAlpha, &Button, Localize("Unpredicted Alpha"), 0, 100, &CUi::ms_LinearScrollbarScale, 0, "%"); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClHideFrozenGhosts, Localize("Hide ghosts of frozen players"), &g_Config.m_ClHideFrozenGhosts, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRenderGhostAsCircle, Localize("Render ghosts as circles"), &g_Config.m_ClRenderGhostAsCircle, &Column, LineSize); + + static CKeyInfo Key = CKeyInfo{"Toggle Ghosts Key", "toggle tc_show_others_ghosts 0 1", 0, 0}; + Key.m_ModifierCombination = Key.m_KeyId = 0; + for(int Mod = 0; Mod < CBinds::MODIFIER_COMBINATION_COUNT; Mod++) + { + for(int KeyId = 0; KeyId < KEY_LAST; KeyId++) + { + const char *pBind = m_pClient->m_Binds.Get(KeyId, Mod); + if(!pBind[0]) + continue; + + if(str_comp(pBind, Key.m_pCommand) == 0) + { + Key.m_KeyId = KeyId; + Key.m_ModifierCombination = Mod; + break; + } + } + } + + CUIRect KeyButton, KeyLabel; + Column.HSplitTop(LineSize, &KeyButton, &Column); + KeyButton.VSplitMid(&KeyLabel, &KeyButton); + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%s:", Localize(Key.m_pName)); + Ui()->DoLabel(&KeyLabel, aBuf, 12.0f, TEXTALIGN_ML); + int OldId = Key.m_KeyId, OldModifierCombination = Key.m_ModifierCombination, NewModifierCombination; + int NewId = DoKeyReader(&Key, &KeyButton, OldId, OldModifierCombination, &NewModifierCombination); + if(NewId != OldId || NewModifierCombination != OldModifierCombination) + { + if(OldId != 0 || NewId == 0) + m_pClient->m_Binds.Bind(OldId, "", false, OldModifierCombination); + if(NewId != 0) + m_pClient->m_Binds.Bind(NewId, Key.m_pCommand, false, NewModifierCombination); + } + Column.HSplitTop(MarginExtraSmall, nullptr, &Column); + s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; + + // ***** Rainbow ***** // + Column.HSplitTop(MarginBetweenSections, nullptr, &Column); + s_SectionBoxes.push_back(Column); + Column.HSplitTop(HeadlineHeight, &Label, &Column); + Ui()->DoLabel(&Label, Localize("Rainbow"), HeadlineFontSize, TEXTALIGN_ML); + Column.HSplitTop(MarginSmall, nullptr, &Column); + + static std::vector s_DropDownNames = {Localize("Rainbow"), Localize("Pulse"), Localize("Black"), Localize("Random")}; + static CUi::SDropDownState s_RainbowDropDownState; + static CScrollRegion s_RainbowDropDownScrollRegion; + s_RainbowDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_RainbowDropDownScrollRegion; + int RainbowSelectedOld = g_Config.m_ClRainbowMode - 1; + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRainbow, Localize("Rainbow"), &g_Config.m_ClRainbow, &Column, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClRainbowOthers, Localize("Rainbow Others"), &g_Config.m_ClRainbowOthers, &Column, LineSize); + Column.HSplitTop(MarginExtraSmall, nullptr, &Column); + CUIRect DropDownRect; + Column.HSplitTop(LineSize, &DropDownRect, &Column); + const int RainbowSelectedNew = Ui()->DoDropDown(&DropDownRect, RainbowSelectedOld, s_DropDownNames.data(), s_DropDownNames.size(), s_RainbowDropDownState); + if(RainbowSelectedOld != RainbowSelectedNew) + { + g_Config.m_ClRainbowMode = RainbowSelectedNew + 1; + RainbowSelectedOld = RainbowSelectedNew; + dbg_msg("rainbow", "rainbow mode changed to %d", g_Config.m_ClRainbowMode); + } + Column.HSplitTop(MarginExtraSmall, nullptr, &Column); + Column.HSplitTop(LineSize, &Button, &Column); + Ui()->DoScrollbarOption(&g_Config.m_ClRainbowSpeed, &g_Config.m_ClRainbowSpeed, &Button, Localize("Rainbow speed"), 0, 5000, &CUi::ms_LogarithmicScrollbarScale, 0, "%"); + Column.HSplitTop(MarginExtraSmall, nullptr, &Column); + s_SectionBoxes.back().h = Column.y - s_SectionBoxes.back().y; + + Column.HSplitTop(MarginSmall, nullptr, &Column); + // ***** END OF PAGE 1 SETTINGS ***** // + RightView = Column; + + // Scroll + CUIRect ScrollRegion; + ScrollRegion.x = MainView.x; + ScrollRegion.y = maximum(LeftView.y, RightView.y) + MarginSmall * 2.0f; + ScrollRegion.w = MainView.w; + ScrollRegion.h = 0.0f; + s_ScrollRegion.AddRect(ScrollRegion); + s_ScrollRegion.End(); + } + + if(s_CurCustomTab == TCLIENT_TAB_BINDWHEEL) + { + MainView.HSplitTop(MarginBetweenSections, nullptr, &MainView); + MainView.VSplitLeft(MainView.w / 2.1f, &LeftView, &RightView); + + const float Radius = minimum(RightView.w, RightView.h) / 2.0f; + vec2 Pos{RightView.x + RightView.w / 2.0f, RightView.y + RightView.h / 2.0f}; + // Draw Circle + Graphics()->TextureClear(); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.3f); + Graphics()->DrawCircle(Pos.x, Pos.y, Radius, 64); + Graphics()->QuadsEnd(); + + static char s_aBindName[BINDWHEEL_MAX_NAME]; + static char s_aBindCommand[BINDWHEEL_MAX_CMD]; + + static int s_SelectedBindIndex = -1; + int HoveringIndex = -1; + + float MouseDist = distance(Pos, Ui()->MousePos()); + if(MouseDist < Radius && MouseDist > Radius * 0.25f) + { + int SegmentCount = GameClient()->m_Bindwheel.m_vBinds.size(); + float SegmentAngle = 2 * pi / SegmentCount; + + float HoveringAngle = angle(Ui()->MousePos() - Pos) + SegmentAngle / 2; + if(HoveringAngle < 0.0f) + HoveringAngle += 2.0f * pi; + + HoveringIndex = (int)(HoveringAngle / (2 * pi) * SegmentCount); + if(Ui()->MouseButtonClicked(0)) + { + s_SelectedBindIndex = HoveringIndex; + str_copy(s_aBindName, GameClient()->m_Bindwheel.m_vBinds[HoveringIndex].m_aName); + str_copy(s_aBindCommand, GameClient()->m_Bindwheel.m_vBinds[HoveringIndex].m_aCommand); + } + else if(Ui()->MouseButtonClicked(1) && s_SelectedBindIndex >= 0 && HoveringIndex >= 0 && HoveringIndex != s_SelectedBindIndex) + { + CBindWheel::SBind BindA = GameClient()->m_Bindwheel.m_vBinds[s_SelectedBindIndex]; + CBindWheel::SBind BindB = GameClient()->m_Bindwheel.m_vBinds[HoveringIndex]; + str_copy(GameClient()->m_Bindwheel.m_vBinds[s_SelectedBindIndex].m_aName, BindB.m_aName); + str_copy(GameClient()->m_Bindwheel.m_vBinds[s_SelectedBindIndex].m_aCommand, BindB.m_aCommand); + str_copy(GameClient()->m_Bindwheel.m_vBinds[HoveringIndex].m_aName, BindA.m_aName); + str_copy(GameClient()->m_Bindwheel.m_vBinds[HoveringIndex].m_aCommand, BindA.m_aCommand); + } + else if(Ui()->MouseButtonClicked(2)) + { + s_SelectedBindIndex = HoveringIndex; + } + } + else if(MouseDist < Radius && Ui()->MouseButtonClicked(0)) + { + s_SelectedBindIndex = -1; + str_copy(s_aBindName, ""); + str_copy(s_aBindCommand, ""); + } + + const float Theta = pi * 2.0f / GameClient()->m_Bindwheel.m_vBinds.size(); + for(int i = 0; i < static_cast(GameClient()->m_Bindwheel.m_vBinds.size()); i++) + { + float FontSize = 12.0f; + if(i == s_SelectedBindIndex) + { + FontSize = 20.0f; + TextRender()->TextColor(ColorRGBA(0.5f, 1.0f, 0.75f, 1.0f)); + } + else if(i == HoveringIndex) + FontSize = 14.0f; + + const CBindWheel::SBind Bind = GameClient()->m_Bindwheel.m_vBinds[i]; + const float Angle = Theta * i; + vec2 TextPos = direction(Angle); + TextPos *= Radius * 0.75f; + + float Width = TextRender()->TextWidth(FontSize, Bind.m_aName); + TextPos += Pos; + TextPos.x -= Width / 2.0f; + TextRender()->Text(TextPos.x, TextPos.y, FontSize, Bind.m_aName); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + } + + LeftView.HSplitTop(LineSize, &Button, &LeftView); + Button.VSplitLeft(100.0f, &Label, &Button); + Ui()->DoLabel(&Label, Localize("Name:"), 14.0f, TEXTALIGN_ML); + static CLineInput s_NameInput; + s_NameInput.SetBuffer(s_aBindName, sizeof(s_aBindName)); + s_NameInput.SetEmptyText("Name"); + Ui()->DoEditBox(&s_NameInput, &Button, 12.0f); + + LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); + LeftView.HSplitTop(LineSize, &Button, &LeftView); + Button.VSplitLeft(100.0f, &Label, &Button); + Ui()->DoLabel(&Label, Localize("Command:"), 14.0f, TEXTALIGN_ML); + static CLineInput s_BindInput; + s_BindInput.SetBuffer(s_aBindCommand, sizeof(s_aBindCommand)); + s_BindInput.SetEmptyText(Localize("Command")); + Ui()->DoEditBox(&s_BindInput, &Button, 12.0f); + + static CButtonContainer s_AddButton, s_RemoveButton, s_OverrideButton; + + LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); + LeftView.HSplitTop(LineSize, &Button, &LeftView); + if(DoButton_Menu(&s_OverrideButton, Localize("Override Selected"), 0, &Button) && s_SelectedBindIndex >= 0) + { + CBindWheel::SBind TempBind; + if(str_length(s_aBindName) == 0) + str_copy(TempBind.m_aName, "*"); + else + str_copy(TempBind.m_aName, s_aBindName); + + str_copy(GameClient()->m_Bindwheel.m_vBinds[s_SelectedBindIndex].m_aName, TempBind.m_aName); + str_copy(GameClient()->m_Bindwheel.m_vBinds[s_SelectedBindIndex].m_aCommand, s_aBindCommand); + } + LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); + LeftView.HSplitTop(LineSize, &Button, &LeftView); + CUIRect ButtonAdd, ButtonRemove; + Button.VSplitMid(&ButtonRemove, &ButtonAdd, MarginSmall); + if(DoButton_Menu(&s_AddButton, Localize("Add Bind"), 0, &ButtonAdd)) + { + CBindWheel::SBind TempBind; + if(str_length(s_aBindName) == 0) + str_copy(TempBind.m_aName, "*"); + else + str_copy(TempBind.m_aName, s_aBindName); + + GameClient()->m_Bindwheel.AddBind(TempBind.m_aName, s_aBindCommand); + s_SelectedBindIndex = static_cast(GameClient()->m_Bindwheel.m_vBinds.size()) - 1; + } + if(DoButton_Menu(&s_RemoveButton, Localize("Remove Bind"), 0, &ButtonRemove) && s_SelectedBindIndex >= 0) + { + GameClient()->m_Bindwheel.RemoveBind(s_SelectedBindIndex); + s_SelectedBindIndex = -1; + } + + LeftView.HSplitTop(MarginSmall, nullptr, &LeftView); + LeftView.HSplitTop(LineSize, &Label, &LeftView); + Ui()->DoLabel(&Label, Localize("Use left mouse to select"), 14.0f, TEXTALIGN_ML); + LeftView.HSplitTop(LineSize, &Label, &LeftView); + Ui()->DoLabel(&Label, Localize("Use right mouse to swap with selected"), 14.0f, TEXTALIGN_ML); + LeftView.HSplitTop(LineSize, &Label, &LeftView); + Ui()->DoLabel(&Label, Localize("Use middle mouse select without copy"), 14.0f, TEXTALIGN_ML); + + // Do Settings Key + CKeyInfo Key = CKeyInfo{"Bind Wheel Key", "+bindwheel", 0, 0}; + for(int Mod = 0; Mod < CBinds::MODIFIER_COMBINATION_COUNT; Mod++) + { + for(int KeyId = 0; KeyId < KEY_LAST; KeyId++) + { + const char *pBind = m_pClient->m_Binds.Get(KeyId, Mod); + if(!pBind[0]) + continue; + + if(str_comp(pBind, Key.m_pCommand) == 0) + { + Key.m_KeyId = KeyId; + Key.m_ModifierCombination = Mod; + break; + } + } + } + + CUIRect KeyLabel; + LeftView.HSplitBottom(LineSize, &LeftView, &Button); + Button.VSplitLeft(120.0f, &KeyLabel, &Button); + Button.VSplitLeft(100.0f, &Button, nullptr); + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%s:", Localize((const char *)Key.m_pName)); + + Ui()->DoLabel(&KeyLabel, aBuf, 14.0f, TEXTALIGN_ML); + int OldId = Key.m_KeyId, OldModifierCombination = Key.m_ModifierCombination, NewModifierCombination; + int NewId = DoKeyReader((void *)&Key.m_pName, &Button, OldId, OldModifierCombination, &NewModifierCombination); + if(NewId != OldId || NewModifierCombination != OldModifierCombination) + { + if(OldId != 0 || NewId == 0) + m_pClient->m_Binds.Bind(OldId, "", false, OldModifierCombination); + if(NewId != 0) + m_pClient->m_Binds.Bind(NewId, Key.m_pCommand, false, NewModifierCombination); + } + LeftView.HSplitBottom(LineSize, &LeftView, &Button); + + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClResetBindWheelMouse, Localize("Reset position of mouse when opening bindwheel"), &g_Config.m_ClResetBindWheelMouse, &Button, LineSize); + } +} + +void CMenus::RenderSettingsProfiles(CUIRect MainView) +{ + CUIRect Label, LabelMid, Section, LabelRight; + static int s_SelectedProfile = -1; + + const float LineSize = 20.0f; + const float MarginSmall = 5.0f; + const float FontSize = 14.0f; + + char *pSkinName = g_Config.m_ClPlayerSkin; + int *pUseCustomColor = &g_Config.m_ClPlayerUseCustomColor; + unsigned *pColorBody = &g_Config.m_ClPlayerColorBody; + unsigned *pColorFeet = &g_Config.m_ClPlayerColorFeet; + int CurrentFlag = m_Dummy ? g_Config.m_ClDummyCountry : g_Config.m_PlayerCountry; + + if(m_Dummy) + { + pSkinName = g_Config.m_ClDummySkin; + pUseCustomColor = &g_Config.m_ClDummyUseCustomColor; + pColorBody = &g_Config.m_ClDummyColorBody; + pColorFeet = &g_Config.m_ClDummyColorFeet; + } + + // skin info + CTeeRenderInfo OwnSkinInfo; + const CSkin *pSkin = m_pClient->m_Skins.Find(pSkinName); + OwnSkinInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; + OwnSkinInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; + OwnSkinInfo.m_SkinMetrics = pSkin->m_Metrics; + OwnSkinInfo.m_CustomColoredSkin = *pUseCustomColor; + if(*pUseCustomColor) + { + OwnSkinInfo.m_ColorBody = color_cast(ColorHSLA(*pColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); + OwnSkinInfo.m_ColorFeet = color_cast(ColorHSLA(*pColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); + } + else + { + OwnSkinInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); + OwnSkinInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); + } + OwnSkinInfo.m_Size = 50.0f; + + //======YOUR PROFILE====== + char aTempBuf[256]; + str_format(aTempBuf, sizeof(aTempBuf), "%s:", Localize("Your profile")); + MainView.HSplitTop(LineSize, &Label, &MainView); + Ui()->DoLabel(&Label, aTempBuf, FontSize, TEXTALIGN_ML); + MainView.HSplitTop(MarginSmall, nullptr, &MainView); + + MainView.HSplitTop(50.0f, &Label, &MainView); + Label.VSplitLeft(250.0f, &Label, &LabelMid); + const CAnimState *pIdleState = CAnimState::GetIdle(); + vec2 OffsetToMid; + CRenderTools::GetRenderTeeOffsetToRenderedTee(pIdleState, &OwnSkinInfo, OffsetToMid); + vec2 TeeRenderPos(Label.x + LineSize, Label.y + Label.h / 2.0f + OffsetToMid.y); + int Emote = m_Dummy ? g_Config.m_ClDummyDefaultEyes : g_Config.m_ClPlayerDefaultEyes; + RenderTools()->RenderTee(pIdleState, &OwnSkinInfo, Emote, vec2(1.0f, 0.0f), TeeRenderPos); + + char aName[64]; + char aClan[64]; + str_format(aName, sizeof(aName), "%s", m_Dummy ? g_Config.m_ClDummyName : g_Config.m_PlayerName); + str_format(aClan, sizeof(aClan), "%s", m_Dummy ? g_Config.m_ClDummyClan : g_Config.m_PlayerClan); + + CUIRect FlagRect; + Label.VSplitLeft(90.0f, &FlagRect, &Label); + + Label.HSplitTop(LineSize, &Section, &Label); + str_format(aTempBuf, sizeof(aTempBuf), Localize("Name: %s"), aName); + Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); + + Label.HSplitTop(LineSize, &Section, &Label); + str_format(aTempBuf, sizeof(aTempBuf), Localize("Clan: %s"), aClan); + Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); + + Label.HSplitTop(LineSize, &Section, &Label); + str_format(aTempBuf, sizeof(aTempBuf), Localize("Skin: %s"), pSkinName); + Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); + + FlagRect.VSplitRight(50.0f, nullptr, &FlagRect); + FlagRect.HSplitBottom(25.0f, nullptr, &FlagRect); + FlagRect.y -= 10.0f; + ColorRGBA Color(1.0f, 1.0f, 1.0f, 1.0f); + m_pClient->m_CountryFlags.Render(m_Dummy ? g_Config.m_ClDummyCountry : g_Config.m_PlayerCountry, Color, FlagRect.x, FlagRect.y, FlagRect.w, FlagRect.h); + + bool DoSkin = g_Config.m_ClApplyProfileSkin; + bool DoColors = g_Config.m_ClApplyProfileColors; + bool DoEmote = g_Config.m_ClApplyProfileEmote; + bool DoName = g_Config.m_ClApplyProfileName; + bool DoClan = g_Config.m_ClApplyProfileClan; + bool DoFlag = g_Config.m_ClApplyProfileFlag; + + //======AFTER LOAD====== + if(s_SelectedProfile != -1 && s_SelectedProfile < (int)GameClient()->m_SkinProfiles.m_Profiles.size()) + { + CProfile LoadProfile = GameClient()->m_SkinProfiles.m_Profiles[s_SelectedProfile]; + MainView.HSplitTop(LineSize, nullptr, &MainView); + MainView.HSplitTop(10.0f, &Label, &MainView); + str_format(aTempBuf, sizeof(aTempBuf), "%s:", Localize("After Load")); + Ui()->DoLabel(&Label, aTempBuf, FontSize, TEXTALIGN_ML); + + MainView.HSplitTop(50.0f, &Label, &MainView); + Label.VSplitLeft(250.0f, &Label, nullptr); + + if(DoSkin && strlen(LoadProfile.SkinName) != 0) + { + const CSkin *pLoadSkin = m_pClient->m_Skins.Find(LoadProfile.SkinName); + OwnSkinInfo.m_OriginalRenderSkin = pLoadSkin->m_OriginalSkin; + OwnSkinInfo.m_ColorableRenderSkin = pLoadSkin->m_ColorableSkin; + OwnSkinInfo.m_SkinMetrics = pLoadSkin->m_Metrics; + } + if(*pUseCustomColor && DoColors && LoadProfile.BodyColor != -1 && LoadProfile.FeetColor != -1) + { + OwnSkinInfo.m_ColorBody = color_cast(ColorHSLA(LoadProfile.BodyColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); + OwnSkinInfo.m_ColorFeet = color_cast(ColorHSLA(LoadProfile.FeetColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); + } + + CRenderTools::GetRenderTeeOffsetToRenderedTee(pIdleState, &OwnSkinInfo, OffsetToMid); + TeeRenderPos = vec2(Label.x + LineSize, Label.y + Label.h / 2.0f + OffsetToMid.y); + int LoadEmote = Emote; + if(DoEmote && LoadProfile.Emote != -1) + LoadEmote = LoadProfile.Emote; + RenderTools()->RenderTee(pIdleState, &OwnSkinInfo, LoadEmote, vec2(1.0f, 0.0f), TeeRenderPos); + + if(DoName && strlen(LoadProfile.Name) != 0) + str_format(aName, sizeof(aName), "%s", LoadProfile.Name); + if(DoClan && strlen(LoadProfile.Clan) != 0) + str_format(aClan, sizeof(aClan), "%s", LoadProfile.Clan); + + Label.VSplitLeft(90.0f, &FlagRect, &Label); + + Label.HSplitTop(LineSize, &Section, &Label); + str_format(aTempBuf, sizeof(aTempBuf), Localize("Name: %s"), aName); + Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); + + Label.HSplitTop(LineSize, &Section, &Label); + str_format(aTempBuf, sizeof(aTempBuf), Localize("Clan: %s"), aClan); + Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); + + Label.HSplitTop(LineSize, &Section, &Label); + str_format(aTempBuf, sizeof(aTempBuf), Localize("Skin: %s"), (DoSkin && strlen(LoadProfile.SkinName) != 0) ? LoadProfile.SkinName : pSkinName); + Ui()->DoLabel(&Section, aTempBuf, FontSize, TEXTALIGN_ML); + + FlagRect.VSplitRight(50.0f, nullptr, &FlagRect); + FlagRect.HSplitBottom(25.0f, nullptr, &FlagRect); + FlagRect.y -= 10.0f; + int RenderFlag = m_Dummy ? g_Config.m_ClDummyCountry : g_Config.m_PlayerCountry; + if(DoFlag && LoadProfile.CountryFlag != -2) + RenderFlag = LoadProfile.CountryFlag; + m_pClient->m_CountryFlags.Render(RenderFlag, Color, FlagRect.x, FlagRect.y, FlagRect.w, FlagRect.h); + + str_format(aName, sizeof(aName), "%s", m_Dummy ? g_Config.m_ClDummyName : g_Config.m_PlayerName); + str_format(aClan, sizeof(aClan), "%s", m_Dummy ? g_Config.m_ClDummyClan : g_Config.m_PlayerClan); + } + else + { + MainView.HSplitTop(80.0f, nullptr, &MainView); + } + + //===BUTTONS AND CHECK BOX=== + CUIRect DummyCheck, CustomCheck; + MainView.HSplitTop(30.0f, &DummyCheck, nullptr); + DummyCheck.HSplitTop(13.0f, nullptr, &DummyCheck); + + DummyCheck.VSplitLeft(100.0f, &DummyCheck, &CustomCheck); + CustomCheck.VSplitLeft(150.0f, &CustomCheck, nullptr); + + DoButton_CheckBoxAutoVMarginAndSet(&m_Dummy, Localize("Dummy"), (int *)&m_Dummy, &DummyCheck, LineSize); + + static int s_CustomColorID = 0; + CustomCheck.HSplitTop(LineSize, &CustomCheck, nullptr); + + if(DoButton_CheckBox(&s_CustomColorID, Localize("Custom colors"), *pUseCustomColor, &CustomCheck)) + { + *pUseCustomColor = *pUseCustomColor ? 0 : 1; + SetNeedSendInfo(); + } + + LabelMid.VSplitLeft(20.0f, nullptr, &LabelMid); + LabelMid.VSplitLeft(160.0f, &LabelMid, &LabelRight); + + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileSkin, Localize("Save/Load Skin"), &g_Config.m_ClApplyProfileSkin, &LabelMid, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileColors, Localize("Save/Load Colors"), &g_Config.m_ClApplyProfileColors, &LabelMid, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileEmote, Localize("Save/Load Emote"), &g_Config.m_ClApplyProfileEmote, &LabelMid, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileName, Localize("Save/Load Name"), &g_Config.m_ClApplyProfileName, &LabelMid, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileClan, Localize("Save/Load Clan"), &g_Config.m_ClApplyProfileClan, &LabelMid, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClApplyProfileFlag, Localize("Save/Load Flag"), &g_Config.m_ClApplyProfileFlag, &LabelMid, LineSize); + + CUIRect Button; + LabelRight.VSplitLeft(150.0f, &LabelRight, nullptr); + + LabelRight.HSplitTop(30.0f, &Button, &LabelRight); + static CButtonContainer s_LoadButton; + + if(DoButton_Menu(&s_LoadButton, Localize("Load"), 0, &Button)) + { + if(s_SelectedProfile != -1 && s_SelectedProfile < (int)GameClient()->m_SkinProfiles.m_Profiles.size()) + { + CProfile LoadProfile = GameClient()->m_SkinProfiles.m_Profiles[s_SelectedProfile]; + if(!m_Dummy) + { + if(DoSkin && strlen(LoadProfile.SkinName) != 0) + str_copy(g_Config.m_ClPlayerSkin, LoadProfile.SkinName, sizeof(g_Config.m_ClPlayerSkin)); + if(DoColors && LoadProfile.BodyColor != -1 && LoadProfile.FeetColor != -1) + { + g_Config.m_ClPlayerColorBody = LoadProfile.BodyColor; + g_Config.m_ClPlayerColorFeet = LoadProfile.FeetColor; + } + if(DoEmote && LoadProfile.Emote != -1) + g_Config.m_ClPlayerDefaultEyes = LoadProfile.Emote; + if(DoName && strlen(LoadProfile.Name) != 0) + str_copy(g_Config.m_PlayerName, LoadProfile.Name, sizeof(g_Config.m_PlayerName)); + if(DoClan && strlen(LoadProfile.Clan) != 0) + str_copy(g_Config.m_PlayerClan, LoadProfile.Clan, sizeof(g_Config.m_PlayerClan)); + if(DoFlag && LoadProfile.CountryFlag != -2) + g_Config.m_PlayerCountry = LoadProfile.CountryFlag; + } + else + { + if(DoSkin && strlen(LoadProfile.SkinName) != 0) + str_copy(g_Config.m_ClDummySkin, LoadProfile.SkinName, sizeof(g_Config.m_ClDummySkin)); + if(DoColors && LoadProfile.BodyColor != -1 && LoadProfile.FeetColor != -1) + { + g_Config.m_ClDummyColorBody = LoadProfile.BodyColor; + g_Config.m_ClDummyColorFeet = LoadProfile.FeetColor; + } + if(DoEmote && LoadProfile.Emote != -1) + g_Config.m_ClDummyDefaultEyes = LoadProfile.Emote; + if(DoName && strlen(LoadProfile.Name) != 0) + str_copy(g_Config.m_ClDummyName, LoadProfile.Name, sizeof(g_Config.m_ClDummyName)); + if(DoClan && strlen(LoadProfile.Clan) != 0) + str_copy(g_Config.m_ClDummyClan, LoadProfile.Clan, sizeof(g_Config.m_ClDummyClan)); + if(DoFlag && LoadProfile.CountryFlag != -2) + g_Config.m_ClDummyCountry = LoadProfile.CountryFlag; + } + } + SetNeedSendInfo(); + } + LabelRight.HSplitTop(5.0f, nullptr, &LabelRight); + + LabelRight.HSplitTop(30.0f, &Button, &LabelRight); + static CButtonContainer s_SaveButton; + if(DoButton_Menu(&s_SaveButton, Localize("Save"), 0, &Button)) + { + GameClient()->m_SkinProfiles.AddProfile( + DoColors ? *pColorBody : -1, + DoColors ? *pColorFeet : -1, + DoFlag ? CurrentFlag : -2, + DoEmote ? Emote : -1, + DoSkin ? pSkinName : "", + DoName ? aName : "", + DoClan ? aClan : ""); + GameClient()->m_SkinProfiles.SaveProfiles(); + } + LabelRight.HSplitTop(5.0f, nullptr, &LabelRight); + + static int s_AllowDelete; + DoButton_CheckBoxAutoVMarginAndSet(&s_AllowDelete, Localizable("Enable Deleting"), &s_AllowDelete, &LabelRight, LineSize); + LabelRight.HSplitTop(5.0f, nullptr, &LabelRight); + + if(s_AllowDelete) + { + LabelRight.HSplitTop(28.0f, &Button, &LabelRight); + static CButtonContainer s_DeleteButton; + if(DoButton_Menu(&s_DeleteButton, Localize("Delete"), 0, &Button)) + { + if(s_SelectedProfile != -1 && s_SelectedProfile < (int)GameClient()->m_SkinProfiles.m_Profiles.size()) + { + GameClient()->m_SkinProfiles.m_Profiles.erase(GameClient()->m_SkinProfiles.m_Profiles.begin() + s_SelectedProfile); + GameClient()->m_SkinProfiles.SaveProfiles(); + } + } + LabelRight.HSplitTop(5.0f, nullptr, &LabelRight); + + LabelRight.HSplitTop(28.0f, &Button, &LabelRight); + static CButtonContainer s_OverrideButton; + if(DoButton_Menu(&s_OverrideButton, Localize("Override"), 0, &Button)) + { + if(s_SelectedProfile != -1 && s_SelectedProfile < (int)GameClient()->m_SkinProfiles.m_Profiles.size()) + { + GameClient()->m_SkinProfiles.m_Profiles[s_SelectedProfile] = CProfile( + DoColors ? *pColorBody : -1, + DoColors ? *pColorFeet : -1, + DoFlag ? CurrentFlag : -2, + DoEmote ? Emote : -1, + DoSkin ? pSkinName : "", + DoName ? aName : "", + DoClan ? aClan : ""); + GameClient()->m_SkinProfiles.SaveProfiles(); + } + } + } + + //---RENDER THE SELECTOR--- + CUIRect FileButton; + CUIRect SelectorRect; + MainView.HSplitTop(50.0f, nullptr, &SelectorRect); + SelectorRect.HSplitBottom(LineSize, &SelectorRect, &FileButton); + SelectorRect.HSplitBottom(MarginSmall, &SelectorRect, nullptr); + std::vector *pProfileList = &GameClient()->m_SkinProfiles.m_Profiles; + + static CListBox s_ListBox; + s_ListBox.DoStart(50.0f, pProfileList->size(), 4, 3, s_SelectedProfile, &SelectorRect, true); + + static bool s_Indexs[1024]; + + for(size_t i = 0; i < pProfileList->size(); ++i) + { + CProfile CurrentProfile = GameClient()->m_SkinProfiles.m_Profiles[i]; + + char RenderSkin[24]; + if(strlen(CurrentProfile.SkinName) == 0) + str_copy(RenderSkin, pSkinName, sizeof(RenderSkin)); + else + str_copy(RenderSkin, CurrentProfile.SkinName, sizeof(RenderSkin)); + + const CSkin *pSkinToBeDraw = m_pClient->m_Skins.Find(RenderSkin); + + CListboxItem Item = s_ListBox.DoNextItem(&s_Indexs[i], s_SelectedProfile >= 0 && (size_t)s_SelectedProfile == i); + + if(!Item.m_Visible) + continue; + + if(Item.m_Visible) + { + CTeeRenderInfo Info; + Info.m_ColorBody = color_cast(ColorHSLA(CurrentProfile.BodyColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); + Info.m_ColorFeet = color_cast(ColorHSLA(CurrentProfile.FeetColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); + Info.m_CustomColoredSkin = true; + Info.m_OriginalRenderSkin = pSkinToBeDraw->m_OriginalSkin; + Info.m_ColorableRenderSkin = pSkinToBeDraw->m_ColorableSkin; + Info.m_SkinMetrics = pSkinToBeDraw->m_Metrics; + Info.m_Size = 50.0f; + if(CurrentProfile.BodyColor == -1 && CurrentProfile.FeetColor == -1) + { + Info.m_CustomColoredSkin = m_Dummy ? g_Config.m_ClDummyUseCustomColor : g_Config.m_ClPlayerUseCustomColor; + Info.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); + Info.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); + } + + CRenderTools::GetRenderTeeOffsetToRenderedTee(pIdleState, &Info, OffsetToMid); + + int RenderEmote = CurrentProfile.Emote == -1 ? Emote : CurrentProfile.Emote; + TeeRenderPos = vec2(Item.m_Rect.x + 30.0f, Item.m_Rect.y + Item.m_Rect.h / 2.0f + OffsetToMid.y); + + Item.m_Rect.VSplitLeft(60.0f, nullptr, &Item.m_Rect); + CUIRect PlayerRect, ClanRect, FeetColorSquare, BodyColorSquare; + + Item.m_Rect.VSplitLeft(60.0f, nullptr, &BodyColorSquare); // Delete this maybe + + Item.m_Rect.VSplitRight(60.0f, &BodyColorSquare, &FlagRect); + BodyColorSquare.x -= 11.0f; + BodyColorSquare.VSplitLeft(10.0f, &BodyColorSquare, nullptr); + BodyColorSquare.HSplitMid(&BodyColorSquare, &FeetColorSquare); + BodyColorSquare.HSplitMid(nullptr, &BodyColorSquare); + FeetColorSquare.HSplitMid(&FeetColorSquare, nullptr); + FlagRect.HSplitBottom(10.0f, &FlagRect, nullptr); + FlagRect.HSplitTop(10.0f, nullptr, &FlagRect); + + Item.m_Rect.HSplitMid(&PlayerRect, &ClanRect); + + SLabelProperties Props; + Props.m_MaxWidth = Item.m_Rect.w; + if(CurrentProfile.CountryFlag != -2) + m_pClient->m_CountryFlags.Render(CurrentProfile.CountryFlag, Color, FlagRect.x, FlagRect.y, FlagRect.w, FlagRect.h); + + if(CurrentProfile.BodyColor != -1 && CurrentProfile.FeetColor != -1) + { + ColorRGBA BodyColor = color_cast(ColorHSLA(CurrentProfile.BodyColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); + ColorRGBA FeetColor = color_cast(ColorHSLA(CurrentProfile.FeetColor).UnclampLighting(ColorHSLA::DARKEST_LGT)); + + Graphics()->TextureClear(); + Graphics()->QuadsBegin(); + Graphics()->SetColor(BodyColor.r, BodyColor.g, BodyColor.b, 1.0f); + IGraphics::CQuadItem Quads[2]; + Quads[0] = IGraphics::CQuadItem(BodyColorSquare.x, BodyColorSquare.y, BodyColorSquare.w, BodyColorSquare.h); + Graphics()->QuadsDrawTL(&Quads[0], 1); + Graphics()->SetColor(FeetColor.r, FeetColor.g, FeetColor.b, 1.0f); + Quads[1] = IGraphics::CQuadItem(FeetColorSquare.x, FeetColorSquare.y, FeetColorSquare.w, FeetColorSquare.h); + Graphics()->QuadsDrawTL(&Quads[1], 1); + Graphics()->QuadsEnd(); + } + RenderTools()->RenderTee(pIdleState, &Info, RenderEmote, vec2(1.0f, 0.0f), TeeRenderPos); + + if(strlen(CurrentProfile.Name) == 0 && strlen(CurrentProfile.Clan) == 0) + { + PlayerRect = Item.m_Rect; + PlayerRect.y += MarginSmall; + Ui()->DoLabel(&PlayerRect, CurrentProfile.SkinName, FontSize, TEXTALIGN_ML, Props); + } + else + { + Ui()->DoLabel(&PlayerRect, CurrentProfile.Name, FontSize, TEXTALIGN_ML, Props); + Item.m_Rect.HSplitTop(LineSize, nullptr, &Item.m_Rect); + Props.m_MaxWidth = Item.m_Rect.w; + Ui()->DoLabel(&ClanRect, CurrentProfile.Clan, FontSize, TEXTALIGN_ML, Props); + } + } + } + + const int NewSelected = s_ListBox.DoEnd(); + if(s_SelectedProfile != NewSelected) + { + s_SelectedProfile = NewSelected; + } + static CButtonContainer s_ProfilesFile; + FileButton.VSplitLeft(130.0f, &FileButton, nullptr); + if(DoButton_Menu(&s_ProfilesFile, Localize("Profiles file"), 0, &FileButton)) + { + Storage()->GetCompletePath(IStorage::TYPE_SAVE, PROFILES_FILE, aTempBuf, sizeof(aTempBuf)); + if(!open_file(aTempBuf)) + { + dbg_msg("menus", "couldn't open file"); + } + } +} diff --git a/src/game/client/components/tclient/menus_tclient.h b/src/game/client/components/tclient/menus_tclient.h new file mode 100644 index 00000000000..e69de29bb2d