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

2625 lines
82 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
2011-08-31 11:56:04 +00:00
#include <algorithm>
2022-05-29 16:33:38 +00:00
#include <chrono>
2022-02-14 23:22:52 +00:00
#include <cmath>
2022-05-29 16:33:38 +00:00
#include <vector>
2010-05-29 07:25:38 +00:00
#include <base/math.h>
2020-09-22 16:02:03 +00:00
#include <base/system.h>
2010-05-29 07:25:38 +00:00
#include <base/vmath.h>
#include <engine/client.h>
2022-10-04 16:40:24 +00:00
#include <engine/config.h>
#include <engine/editor.h>
2011-03-23 12:06:35 +00:00
#include <engine/friends.h>
2010-05-29 07:25:38 +00:00
#include <engine/graphics.h>
#include <engine/keys.h>
#include <engine/serverbrowser.h>
2020-09-22 16:02:03 +00:00
#include <engine/shared/config.h>
2010-10-06 21:07:35 +00:00
#include <engine/storage.h>
#include <engine/textrender.h>
2009-06-13 17:18:06 +00:00
2010-05-29 07:25:38 +00:00
#include <game/generated/protocol.h>
#include <engine/client/updater.h>
#include <game/client/components/binds.h>
#include <game/client/components/console.h>
2020-09-18 16:45:42 +00:00
#include <game/client/components/menu_background.h>
#include <game/client/components/sounds.h>
2010-05-29 07:25:38 +00:00
#include <game/client/gameclient.h>
#include <game/client/ui_listbox.h>
2020-09-10 18:14:47 +00:00
#include <game/generated/client_data.h>
2010-05-29 07:25:38 +00:00
#include <game/localization.h>
#include "countryflags.h"
2011-03-23 12:06:35 +00:00
#include "menus.h"
2022-05-18 16:00:05 +00:00
using namespace std::chrono_literals;
ColorRGBA CMenus::ms_GuiColor;
ColorRGBA CMenus::ms_ColorTabbarInactiveOutgame;
ColorRGBA CMenus::ms_ColorTabbarActiveOutgame;
ColorRGBA CMenus::ms_ColorTabbarHoverOutgame;
ColorRGBA CMenus::ms_ColorTabbarInactive;
ColorRGBA CMenus::ms_ColorTabbarActive = ColorRGBA(0, 0, 0, 0.5f);
ColorRGBA CMenus::ms_ColorTabbarHover;
ColorRGBA CMenus::ms_ColorTabbarInactiveIngame;
ColorRGBA CMenus::ms_ColorTabbarActiveIngame;
ColorRGBA CMenus::ms_ColorTabbarHoverIngame;
2020-10-28 02:59:50 +00:00
SColorPicker CMenus::ms_ColorPicker;
2020-12-14 00:51:31 +00:00
bool CMenus::ms_ValueSelectorTextMode;
2020-10-28 02:59:50 +00:00
2010-05-29 07:25:38 +00:00
float CMenus::ms_ButtonHeight = 25.0f;
float CMenus::ms_ListheaderHeight = 17.0f;
2010-05-29 07:25:38 +00:00
IInput::CEvent CMenus::m_aInputEvents[MAX_INPUTEVENTS];
int CMenus::m_NumInputEvents;
2010-05-29 07:25:38 +00:00
CMenus::CMenus()
{
2010-05-29 07:25:38 +00:00
m_Popup = POPUP_NONE;
m_ActivePage = PAGE_INTERNET;
m_MenuPage = 0;
2010-05-29 07:25:38 +00:00
m_GamePage = PAGE_GAME;
m_JoinTutorial = false;
m_NeedRestartGraphics = false;
m_NeedRestartSound = false;
2010-05-29 07:25:38 +00:00
m_NeedSendinfo = false;
2014-04-28 13:19:57 +00:00
m_NeedSendDummyinfo = false;
2010-05-29 07:25:38 +00:00
m_MenuActive = true;
m_ShowStart = true;
2010-05-29 07:25:38 +00:00
m_NumInputEvents = 0;
2022-07-09 16:14:56 +00:00
str_copy(m_aCurrentDemoFolder, "demos");
m_aCallvoteReason[0] = 0;
2011-03-23 12:06:35 +00:00
m_FriendlistSelectedIndex = -1;
2014-08-23 15:48:04 +00:00
m_DemoPlayerState = DEMOPLAYER_NONE;
2015-08-28 18:44:07 +00:00
m_Dummy = false;
m_ServerProcess.Process = 0;
for(SUIAnimator &animator : m_aAnimatorsSettingsTab)
{
animator.m_YOffset = -2.5f;
animator.m_HOffset = 5.0f;
animator.m_WOffset = 5.0f;
2020-10-26 03:10:58 +00:00
animator.m_RepositionLabel = true;
}
for(SUIAnimator &animator : m_aAnimatorsBigPage)
{
animator.m_YOffset = -5.0f;
animator.m_HOffset = 5.0f;
}
for(SUIAnimator &animator : m_aAnimatorsSmallPage)
{
animator.m_YOffset = -2.5f;
animator.m_HOffset = 2.5f;
}
}
int CMenus::DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect, bool Active)
{
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GUIBUTTONS].m_Id);
Graphics()->QuadsBegin();
if(!Active)
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f);
2020-09-22 16:02:03 +00:00
RenderTools()->SelectSprite(Checked ? SPRITE_GUIBUTTON_ON : SPRITE_GUIBUTTON_OFF);
IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
if(UI()->HotItem() == pID && Active)
{
RenderTools()->SelectSprite(SPRITE_GUIBUTTON_HOVER);
QuadItem = IGraphics::CQuadItem(pRect->x, pRect->y, pRect->w, pRect->h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
}
Graphics()->QuadsEnd();
return Active ? UI()->DoButtonLogic(pID, Checked, pRect) : 0;
}
2022-07-16 13:32:06 +00:00
int CMenus::DoButton_Menu(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, const char *pImageName, int Corners, float r, float FontFactor, vec4 ColorHot, vec4 Color, int AlignVertically, bool CheckForActiveColorPicker)
{
CUIRect Text = *pRect;
bool MouseInsideColorPicker = false;
if(CheckForActiveColorPicker)
{
if(ms_ColorPicker.m_Active)
{
CUIRect PickerRect;
PickerRect.x = ms_ColorPicker.m_X;
PickerRect.y = ms_ColorPicker.m_Y;
PickerRect.w = ms_ColorPicker.ms_Width;
PickerRect.h = ms_ColorPicker.ms_Height;
MouseInsideColorPicker = UI()->MouseInside(&PickerRect);
}
}
if(!MouseInsideColorPicker)
2022-07-16 13:32:06 +00:00
Color.a *= UI()->ButtonColorMul(pButtonContainer);
pRect->Draw(Color, Corners, r);
if(pImageName)
{
CUIRect Image;
2020-09-10 18:14:47 +00:00
pRect->VSplitRight(pRect->h * 4.0f, &Text, &Image); // always correct ratio for image
// render image
const CMenuImage *pImage = FindMenuImage(pImageName);
if(pImage)
{
2022-07-16 13:32:06 +00:00
Graphics()->TextureSet(UI()->HotItem() == pButtonContainer ? pImage->m_OrgTexture : pImage->m_GreyTexture);
Graphics()->WrapClamp();
Graphics()->QuadsBegin();
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
IGraphics::CQuadItem QuadItem(Image.x, Image.y, Image.w, Image.h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
Graphics()->WrapNormal();
}
}
2020-09-10 18:14:47 +00:00
Text.HMargin(pRect->h >= 20.0f ? 2.0f : 1.0f, &Text);
Text.HMargin((Text.h * FontFactor) / 2.0f, &Text);
2022-03-11 16:34:48 +00:00
SLabelProperties Props;
Props.m_AlignVertically = AlignVertically;
UI()->DoLabel(&Text, pText, Text.h * CUI::ms_FontmodHeight, TEXTALIGN_CENTER, Props);
if(MouseInsideColorPicker)
return 0;
2022-07-16 13:32:06 +00:00
return UI()->DoButtonLogic(pButtonContainer, Checked, pRect);
}
void CMenus::DoButton_KeySelect(const void *pID, const char *pText, const CUIRect *pRect)
{
pRect->Draw(ColorRGBA(1, 1, 1, 0.5f * UI()->ButtonColorMul(pID)), IGraphics::CORNER_ALL, 5.0f);
CUIRect Temp;
pRect->HMargin(1.0f, &Temp);
UI()->DoLabel(&Temp, pText, Temp.h * CUI::ms_FontmodHeight, TEXTALIGN_CENTER);
}
2022-07-16 13:32:06 +00:00
int CMenus::DoButton_MenuTab(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, int Corners, SUIAnimator *pAnimator, const ColorRGBA *pDefaultColor, const ColorRGBA *pActiveColor, const ColorRGBA *pHoverColor, float EdgeRounding, int AlignVertically)
{
2022-05-13 18:33:29 +00:00
const bool MouseInside = UI()->MouseInside(pRect);
2020-10-26 01:10:55 +00:00
CUIRect Rect = *pRect;
if(pAnimator != NULL)
{
auto Time = time_get_nanoseconds();
2020-10-26 01:10:55 +00:00
2022-05-18 16:00:05 +00:00
if(pAnimator->m_Time + 100ms < Time)
2020-10-26 01:10:55 +00:00
{
pAnimator->m_Value = pAnimator->m_Active ? 1 : 0;
pAnimator->m_Time = Time;
}
pAnimator->m_Active = Checked || MouseInside;
if(pAnimator->m_Active)
2022-05-18 16:00:05 +00:00
pAnimator->m_Value = clamp<float>(pAnimator->m_Value + (Time - pAnimator->m_Time).count() / (double)std::chrono::nanoseconds(100ms).count(), 0, 1);
2020-10-26 01:10:55 +00:00
else
2022-05-18 16:00:05 +00:00
pAnimator->m_Value = clamp<float>(pAnimator->m_Value - (Time - pAnimator->m_Time).count() / (double)std::chrono::nanoseconds(100ms).count(), 0, 1);
2020-10-26 01:10:55 +00:00
Rect.w += pAnimator->m_Value * pAnimator->m_WOffset;
Rect.h += pAnimator->m_Value * pAnimator->m_HOffset;
Rect.x += pAnimator->m_Value * pAnimator->m_XOffset;
Rect.y += pAnimator->m_Value * pAnimator->m_YOffset;
2020-10-26 01:43:34 +00:00
pAnimator->m_Time = Time;
2020-10-26 01:10:55 +00:00
}
2009-10-27 14:38:53 +00:00
if(Checked)
{
ColorRGBA ColorMenuTab = ms_ColorTabbarActive;
if(pActiveColor)
ColorMenuTab = *pActiveColor;
2020-10-26 01:10:55 +00:00
Rect.Draw(ColorMenuTab, Corners, EdgeRounding);
}
else
{
2022-05-13 18:33:29 +00:00
if(MouseInside)
{
ColorRGBA HoverColorMenuTab = ms_ColorTabbarHover;
if(pHoverColor)
HoverColorMenuTab = *pHoverColor;
2020-10-26 01:10:55 +00:00
Rect.Draw(HoverColorMenuTab, Corners, EdgeRounding);
}
else
{
ColorRGBA ColorMenuTab = ms_ColorTabbarInactive;
if(pDefaultColor)
ColorMenuTab = *pDefaultColor;
2020-10-26 01:10:55 +00:00
Rect.Draw(ColorMenuTab, Corners, EdgeRounding);
}
}
2020-10-26 01:10:55 +00:00
if(pAnimator != NULL)
{
2020-10-26 03:10:58 +00:00
if(pAnimator->m_RepositionLabel)
{
Rect.x += Rect.w - pRect->w + Rect.x - pRect->x;
Rect.y += Rect.h - pRect->h + Rect.y - pRect->y;
}
2020-12-13 18:31:37 +00:00
if(!pAnimator->m_ScaleLabel)
{
Rect.w = pRect->w;
Rect.h = pRect->h;
}
}
2020-12-13 18:31:37 +00:00
2022-05-13 18:34:11 +00:00
CUIRect Temp;
Rect.HMargin(2.0f, &Temp);
2022-03-11 16:34:48 +00:00
SLabelProperties Props;
Props.m_AlignVertically = AlignVertically;
UI()->DoLabel(&Temp, pText, Temp.h * CUI::ms_FontmodHeight, TEXTALIGN_CENTER, Props);
2022-07-16 13:32:06 +00:00
return UI()->DoButtonLogic(pButtonContainer, Checked, pRect);
}
2010-05-29 07:25:38 +00:00
int CMenus::DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect)
{
if(Checked == 2)
pRect->Draw(ColorRGBA(1, 0.98f, 0.5f, 0.55f), IGraphics::CORNER_T, 5.0f);
else if(Checked)
pRect->Draw(ColorRGBA(1, 1, 1, 0.5f), IGraphics::CORNER_T, 5.0f);
2009-10-27 14:38:53 +00:00
CUIRect t;
pRect->VSplitLeft(5.0f, 0, &t);
UI()->DoLabel(&t, pText, pRect->h * CUI::ms_FontmodHeight, TEXTALIGN_LEFT);
return UI()->DoButtonLogic(pID, Checked, pRect);
}
2010-05-29 07:25:38 +00:00
int CMenus::DoButton_CheckBox_Common(const void *pID, const char *pText, const char *pBoxText, const CUIRect *pRect)
{
2009-10-27 14:38:53 +00:00
CUIRect c = *pRect;
CUIRect t = *pRect;
c.w = c.h;
t.x += c.w;
t.w -= c.w;
2009-10-27 14:38:53 +00:00
t.VSplitLeft(5.0f, 0, &t);
2009-10-27 14:38:53 +00:00
c.Margin(2.0f, &c);
c.Draw(ColorRGBA(1, 1, 1, 0.25f * UI()->ButtonColorMul(pID)), IGraphics::CORNER_ALL, 3.0f);
const bool Checkable = *pBoxText == 'X';
2022-03-11 16:34:48 +00:00
SLabelProperties Props;
Props.m_AlignVertically = 0;
if(Checkable)
{
2020-10-06 10:25:10 +00:00
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT);
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
2022-03-19 17:50:33 +00:00
UI()->DoLabel(&c, "\xEF\x80\x8D", c.h * CUI::ms_FontmodHeight, TEXTALIGN_CENTER, Props);
TextRender()->SetCurFont(NULL);
}
else
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&c, pBoxText, c.h * CUI::ms_FontmodHeight, TEXTALIGN_CENTER, Props);
TextRender()->SetRenderFlags(0);
UI()->DoLabel(&t, pText, c.h * CUI::ms_FontmodHeight, TEXTALIGN_LEFT);
return UI()->DoButtonLogic(pID, 0, pRect);
}
void CMenus::DoLaserPreview(const CUIRect *pRect, const ColorHSLA LaserOutlineColor, const ColorHSLA LaserInnerColor, const int LaserType)
{
ColorRGBA LaserRGB;
CUIRect Section = *pRect;
vec2 From = vec2(Section.x + 50.0f, Section.y + Section.h / 2.0f);
vec2 Pos = vec2(Section.x + Section.w - 10.0f, Section.y + Section.h / 2.0f);
Graphics()->BlendNormal();
Graphics()->TextureClear();
Graphics()->QuadsBegin();
LaserRGB = color_cast<ColorRGBA, ColorHSLA>(LaserOutlineColor);
ColorRGBA OuterColor(LaserRGB.r, LaserRGB.g, LaserRGB.b, 1.0f);
Graphics()->SetColor(LaserRGB.r, LaserRGB.g, LaserRGB.b, 1.0f);
2020-12-14 01:18:04 +00:00
vec2 Out = vec2(0.0f, -1.0f) * (3.15f);
IGraphics::CFreeformItem Freeform(From.x - Out.x, From.y - Out.y, From.x + Out.x, From.y + Out.y, Pos.x - Out.x, Pos.y - Out.y, Pos.x + Out.x, Pos.y + Out.y);
Graphics()->QuadsDrawFreeform(&Freeform, 1);
LaserRGB = color_cast<ColorRGBA, ColorHSLA>(LaserInnerColor);
ColorRGBA InnerColor(LaserRGB.r, LaserRGB.g, LaserRGB.b, 1.0f);
Out = vec2(0.0f, -1.0f) * (2.25f);
Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f);
Freeform = IGraphics::CFreeformItem(From.x - Out.x, From.y - Out.y, From.x + Out.x, From.y + Out.y, Pos.x - Out.x, Pos.y - Out.y, Pos.x + Out.x, Pos.y + Out.y);
Graphics()->QuadsDrawFreeform(&Freeform, 1);
Graphics()->QuadsEnd();
Graphics()->BlendNormal();
int SpriteIndex = time_get() % 3;
Graphics()->TextureSet(GameClient()->m_ParticlesSkin.m_aSpriteParticleSplat[SpriteIndex]);
Graphics()->QuadsBegin();
Graphics()->QuadsSetRotation(time_get());
Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f);
IGraphics::CQuadItem QuadItem(Pos.x, Pos.y, 24, 24);
Graphics()->QuadsDraw(&QuadItem, 1);
Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f);
QuadItem = IGraphics::CQuadItem(Pos.x, Pos.y, 20, 20);
Graphics()->QuadsDraw(&QuadItem, 1);
Graphics()->QuadsEnd();
switch(LaserType)
{
case LASERTYPE_RIFLE:
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteWeaponLaser);
RenderTools()->SelectSprite(SPRITE_WEAPON_LASER_BODY);
Graphics()->QuadsBegin();
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->DrawSprite(Section.x + 30.0f, Section.y + Section.h / 2.0f, 60.0f);
Graphics()->QuadsEnd();
break;
case LASERTYPE_SHOTGUN:
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteWeaponShotgun);
RenderTools()->SelectSprite(SPRITE_WEAPON_SHOTGUN_BODY);
Graphics()->QuadsBegin();
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->DrawSprite(Section.x + 30.0f, Section.y + Section.h / 2.0f, 60.0f);
Graphics()->QuadsEnd();
break;
default:
Graphics()->QuadsBegin();
Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f);
QuadItem = IGraphics::CQuadItem(From.x, From.y, 24, 24);
Graphics()->QuadsDraw(&QuadItem, 1);
Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f);
QuadItem = IGraphics::CQuadItem(From.x, From.y, 20, 20);
Graphics()->QuadsDraw(&QuadItem, 1);
Graphics()->QuadsEnd();
}
}
ColorHSLA CMenus::DoLine_ColorPicker(CButtonContainer *pResetID, const float LineSize, const float LabelSize, const float BottomMargin, CUIRect *pMainRect, const char *pText, unsigned int *pColorValue, const ColorRGBA DefaultColor, bool CheckBoxSpacing, int *pCheckBoxValue)
{
CUIRect Section, ColorPickerButton, ResetButton, Label;
pMainRect->HSplitTop(LineSize, &Section, pMainRect);
pMainRect->HSplitTop(BottomMargin, nullptr, pMainRect);
if(CheckBoxSpacing || pCheckBoxValue != nullptr)
{
CUIRect CheckBox;
Section.VSplitLeft(Section.h, &CheckBox, &Section);
if(pCheckBoxValue != nullptr)
{
CheckBox.Margin(2.0f, &CheckBox);
if(DoButton_CheckBox(pCheckBoxValue, "", *pCheckBoxValue, &CheckBox))
*pCheckBoxValue ^= 1;
}
}
Section.VSplitLeft(5.0f, nullptr, &Section);
Section.VSplitMid(&Label, &Section, Section.h);
Section.VSplitRight(60.0f, &Section, &ResetButton);
Section.VSplitRight(8.0f, &Section, nullptr);
Section.VSplitRight(Section.h, &Section, &ColorPickerButton);
UI()->DoLabel(&Label, pText, LabelSize, TEXTALIGN_LEFT);
ColorHSLA PickedColor = RenderHSLColorPicker(&ColorPickerButton, pColorValue, false);
ResetButton.HMargin(2.0f, &ResetButton);
if(DoButton_Menu(pResetID, Localize("Reset"), 0, &ResetButton, nullptr, IGraphics::CORNER_ALL, 8.0f, 0.0f, vec4(1, 1, 1, 0.5f), vec4(1, 1, 1, 0.25f), 1, true))
{
*pColorValue = color_cast<ColorHSLA>(DefaultColor).Pack(false);
}
return PickedColor;
}
int CMenus::DoButton_CheckBoxAutoVMarginAndSet(const void *pID, const char *pText, int *pValue, CUIRect *pRect, float VMargin)
{
CUIRect CheckBoxRect;
pRect->HSplitTop(VMargin, &CheckBoxRect, pRect);
int Logic = DoButton_CheckBox_Common(pID, pText, *pValue ? "X" : "", &CheckBoxRect);
if(Logic)
*pValue ^= 1;
return Logic;
}
2010-05-29 07:25:38 +00:00
int CMenus::DoButton_CheckBox(const void *pID, const char *pText, int Checked, const CUIRect *pRect)
{
2020-09-22 16:02:03 +00:00
return DoButton_CheckBox_Common(pID, pText, Checked ? "X" : "", pRect);
}
2010-05-29 07:25:38 +00:00
int CMenus::DoButton_CheckBox_Number(const void *pID, const char *pText, int Checked, const CUIRect *pRect)
{
2010-05-29 07:25:38 +00:00
char aBuf[16];
str_format(aBuf, sizeof(aBuf), "%d", Checked);
return DoButton_CheckBox_Common(pID, pText, aBuf, pRect);
}
int CMenus::DoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, bool UseScroll, int Current, int Min, int Max, int Step, float Scale, bool IsHex, float Round, ColorRGBA *pColor)
2020-12-14 00:51:31 +00:00
{
// logic
static float s_Value;
static char s_aNumStr[64];
static void *s_pLastTextpID = pID;
const bool Inside = UI()->MouseInside(pRect);
2020-12-14 00:51:31 +00:00
if(Inside)
UI()->SetHotItem(pID);
if(UI()->MouseButtonReleased(1) && UI()->HotItem() == pID)
{
s_pLastTextpID = pID;
2020-12-14 00:51:31 +00:00
ms_ValueSelectorTextMode = true;
if(IsHex)
str_format(s_aNumStr, sizeof(s_aNumStr), "%06X", Current);
2020-12-14 00:51:31 +00:00
else
str_format(s_aNumStr, sizeof(s_aNumStr), "%d", Current);
2020-12-14 00:51:31 +00:00
}
if(UI()->CheckActiveItem(pID))
2020-12-14 00:51:31 +00:00
{
if(!UI()->MouseButton(0))
{
//m_LockMouse = false;
UI()->SetActiveItem(nullptr);
2020-12-14 00:51:31 +00:00
ms_ValueSelectorTextMode = false;
}
}
if(ms_ValueSelectorTextMode && s_pLastTextpID == pID)
2020-12-14 00:51:31 +00:00
{
static float s_NumberBoxID = 0;
UI()->DoEditBox(&s_NumberBoxID, pRect, s_aNumStr, sizeof(s_aNumStr), 10.0f, &s_NumberBoxID, false, IGraphics::CORNER_ALL);
2020-12-14 00:51:31 +00:00
UI()->SetActiveItem(&s_NumberBoxID);
if(Input()->KeyIsPressed(KEY_RETURN) || Input()->KeyIsPressed(KEY_KP_ENTER) ||
((UI()->MouseButtonClicked(1) || UI()->MouseButtonClicked(0)) && !Inside))
{
if(IsHex)
Current = clamp(str_toint_base(s_aNumStr, 16), Min, Max);
2020-12-14 00:51:31 +00:00
else
Current = clamp(str_toint(s_aNumStr), Min, Max);
2020-12-14 00:51:31 +00:00
//m_LockMouse = false;
UI()->SetActiveItem(nullptr);
2020-12-14 00:51:31 +00:00
ms_ValueSelectorTextMode = false;
}
if(Input()->KeyIsPressed(KEY_ESCAPE))
{
//m_LockMouse = false;
UI()->SetActiveItem(nullptr);
2020-12-14 00:51:31 +00:00
ms_ValueSelectorTextMode = false;
}
}
else
{
if(UI()->CheckActiveItem(pID))
2020-12-14 00:51:31 +00:00
{
if(UseScroll)
{
if(UI()->MouseButton(0))
{
float delta = UI()->MouseDeltaX();
if(Input()->ShiftIsPressed())
2020-12-14 00:51:31 +00:00
s_Value += delta * 0.05f;
else
s_Value += delta;
if(absolute(s_Value) > Scale)
{
int Count = (int)(s_Value / Scale);
s_Value = std::fmod(s_Value, Scale);
2020-12-14 00:51:31 +00:00
Current += Step * Count;
Current = clamp(Current, Min, Max);
// Constrain to discrete steps
if(Count > 0)
Current = Current / Step * Step;
else
Current = std::ceil(Current / (float)Step) * Step;
2020-12-14 00:51:31 +00:00
}
}
}
}
else if(UI()->HotItem() == pID)
{
if(UI()->MouseButtonClicked(0))
{
//m_LockMouse = true;
s_Value = 0;
UI()->SetActiveItem(pID);
}
}
// render
char aBuf[128];
if(pLabel[0] != '\0')
{
if(IsHex)
str_format(aBuf, sizeof(aBuf), "%s #%06X", pLabel, Current);
else
str_format(aBuf, sizeof(aBuf), "%s %d", pLabel, Current);
}
else
{
if(IsHex)
str_format(aBuf, sizeof(aBuf), "#%06X", Current);
else
str_format(aBuf, sizeof(aBuf), "%d", Current);
}
pRect->Draw(*pColor, IGraphics::CORNER_ALL, Round);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(pRect, aBuf, 10, TEXTALIGN_CENTER);
2020-12-14 00:51:31 +00:00
}
return Current;
}
int CMenus::DoKeyReader(void *pID, const CUIRect *pRect, int Key, int ModifierCombination, int *pNewModifierCombination)
{
// process
2009-10-27 14:38:53 +00:00
static void *pGrabbedID = 0;
static bool MouseReleased = true;
static int s_ButtonUsed = 0;
const bool Inside = UI()->MouseHovered(pRect);
2009-10-27 14:38:53 +00:00
int NewKey = Key;
*pNewModifierCombination = ModifierCombination;
if(!UI()->MouseButton(0) && !UI()->MouseButton(1) && pGrabbedID == pID)
2009-10-27 14:38:53 +00:00
MouseReleased = true;
if(UI()->CheckActiveItem(pID))
{
2010-05-29 07:25:38 +00:00
if(m_Binder.m_GotKey)
{
// abort with escape key
if(m_Binder.m_Key.m_Key != KEY_ESCAPE)
{
NewKey = m_Binder.m_Key.m_Key;
*pNewModifierCombination = m_Binder.m_ModifierCombination;
}
2010-05-29 07:25:38 +00:00
m_Binder.m_GotKey = false;
UI()->SetActiveItem(nullptr);
2009-10-27 14:38:53 +00:00
MouseReleased = false;
pGrabbedID = pID;
}
if(s_ButtonUsed == 1 && !UI()->MouseButton(1))
{
if(Inside)
NewKey = 0;
UI()->SetActiveItem(nullptr);
}
}
2009-10-27 14:38:53 +00:00
else if(UI()->HotItem() == pID)
{
if(MouseReleased)
{
if(UI()->MouseButton(0))
{
m_Binder.m_TakeKey = true;
m_Binder.m_GotKey = false;
UI()->SetActiveItem(pID);
s_ButtonUsed = 0;
}
if(UI()->MouseButton(1))
{
UI()->SetActiveItem(pID);
s_ButtonUsed = 1;
}
}
}
2009-10-27 14:38:53 +00:00
if(Inside)
UI()->SetHotItem(pID);
// draw
if(UI()->CheckActiveItem(pID) && s_ButtonUsed == 0)
DoButton_KeySelect(pID, "???", pRect);
else if(NewKey == 0)
DoButton_KeySelect(pID, "", pRect);
else
{
char aBuf[64];
str_format(aBuf, sizeof(aBuf), "%s%s", CBinds::GetKeyBindModifiersName(*pNewModifierCombination), Input()->KeyName(NewKey));
DoButton_KeySelect(pID, aBuf, pRect);
}
2009-10-27 14:38:53 +00:00
return NewKey;
}
2010-05-29 07:25:38 +00:00
int CMenus::RenderMenubar(CUIRect r)
{
2010-05-29 07:25:38 +00:00
CUIRect Box = r;
CUIRect Button;
m_ActivePage = m_MenuPage;
2010-05-29 07:25:38 +00:00
int NewPage = -1;
2010-05-29 07:25:38 +00:00
if(Client()->State() != IClient::STATE_OFFLINE)
m_ActivePage = m_GamePage;
2010-05-29 07:25:38 +00:00
if(Client()->State() == IClient::STATE_OFFLINE)
{
Box.VSplitLeft(33.0f, &Button, &Box);
static CButtonContainer s_StartButton;
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
bool GotNewsOrUpdate = false;
#if defined(CONF_AUTOUPDATE)
int State = Updater()->GetCurrentState();
bool NeedUpdate = str_comp(Client()->LatestVersion(), "0");
if(State == IUpdater::CLEAN && NeedUpdate)
{
GotNewsOrUpdate = true;
}
#endif
GotNewsOrUpdate |= (bool)g_Config.m_UiUnreadNews;
ColorRGBA HomeButtonColorAlert(0, 1, 0, 0.25f);
ColorRGBA HomeButtonColorAlertHover(0, 1, 0, 0.5f);
ColorRGBA *pHomeButtonColor = NULL;
ColorRGBA *pHomeButtonColorHover = NULL;
const char *pHomeScreenButtonLabel = "\xEF\x80\x95";
if(GotNewsOrUpdate)
{
pHomeScreenButtonLabel = "\xEF\x87\xAA";
pHomeButtonColor = &HomeButtonColorAlert;
pHomeButtonColorHover = &HomeButtonColorAlertHover;
}
if(DoButton_MenuTab(&s_StartButton, pHomeScreenButtonLabel, false, &Button, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_HOME], pHomeButtonColor, pHomeButtonColor, pHomeButtonColorHover, 10.0f, 0))
{
m_ShowStart = true;
}
TextRender()->SetRenderFlags(0);
TextRender()->SetCurFont(NULL);
Box.VSplitLeft(10.0f, 0, &Box);
2020-09-05 21:43:39 +00:00
// offline menus
if(m_ActivePage == PAGE_NEWS)
{
2020-09-05 21:43:39 +00:00
Box.VSplitLeft(100.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_NewsButton;
if(DoButton_MenuTab(&s_NewsButton, Localize("News"), m_ActivePage == PAGE_NEWS, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_NEWS]))
2020-09-05 21:43:39 +00:00
{
NewPage = PAGE_NEWS;
}
}
2020-09-05 21:43:39 +00:00
else if(m_ActivePage == PAGE_DEMOS)
{
2020-09-05 21:43:39 +00:00
Box.VSplitLeft(100.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_DemosButton;
if(DoButton_MenuTab(&s_DemosButton, Localize("Demos"), m_ActivePage == PAGE_DEMOS, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_DEMOS]))
2020-09-05 21:43:39 +00:00
{
DemolistPopulate();
NewPage = PAGE_DEMOS;
}
}
2020-09-05 21:43:39 +00:00
else
{
2020-09-05 21:43:39 +00:00
Box.VSplitLeft(100.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_InternetButton;
if(DoButton_MenuTab(&s_InternetButton, Localize("Internet"), m_ActivePage == PAGE_INTERNET, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_INTERNET]))
2020-09-05 21:43:39 +00:00
{
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_INTERNET)
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
NewPage = PAGE_INTERNET;
}
2020-09-05 21:43:39 +00:00
Box.VSplitLeft(100.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_LanButton;
if(DoButton_MenuTab(&s_LanButton, Localize("LAN"), m_ActivePage == PAGE_LAN, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_LAN]))
{
2020-09-05 21:43:39 +00:00
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_LAN)
ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN);
NewPage = PAGE_LAN;
}
2014-09-13 14:36:25 +00:00
2020-09-05 21:43:39 +00:00
Box.VSplitLeft(100.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_FavoritesButton;
if(DoButton_MenuTab(&s_FavoritesButton, Localize("Favorites"), m_ActivePage == PAGE_FAVORITES, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_FAVORITES]))
{
2020-09-05 21:43:39 +00:00
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_FAVORITES)
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
NewPage = PAGE_FAVORITES;
}
Box.VSplitLeft(90.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_DDNetButton;
if(DoButton_MenuTab(&s_DDNetButton, "DDNet", m_ActivePage == PAGE_DDNET, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_DDNET]))
2020-09-05 21:43:39 +00:00
{
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_DDNET)
{
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_DDNET);
}
NewPage = PAGE_DDNET;
}
Box.VSplitLeft(90.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_KoGButton;
if(DoButton_MenuTab(&s_KoGButton, "KoG", m_ActivePage == PAGE_KOG, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_KOG]))
2020-09-05 21:43:39 +00:00
{
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_KOG)
{
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_KOG);
}
NewPage = PAGE_KOG;
}
}
}
else
{
2010-05-29 07:25:38 +00:00
// online menus
Box.VSplitLeft(90.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_GameButton;
if(DoButton_MenuTab(&s_GameButton, Localize("Game"), m_ActivePage == PAGE_GAME, &Button, IGraphics::CORNER_TL))
2010-05-29 07:25:38 +00:00
NewPage = PAGE_GAME;
Box.VSplitLeft(90.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_PlayersButton;
2020-09-22 16:02:03 +00:00
if(DoButton_MenuTab(&s_PlayersButton, Localize("Players"), m_ActivePage == PAGE_PLAYERS, &Button, 0))
NewPage = PAGE_PLAYERS;
Box.VSplitLeft(130.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ServerInfoButton;
2020-09-22 16:02:03 +00:00
if(DoButton_MenuTab(&s_ServerInfoButton, Localize("Server info"), m_ActivePage == PAGE_SERVER_INFO, &Button, 0))
2010-05-29 07:25:38 +00:00
NewPage = PAGE_SERVER_INFO;
Box.VSplitLeft(90.0f, &Button, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_NetworkButton;
2020-09-22 16:02:03 +00:00
if(DoButton_MenuTab(&s_NetworkButton, Localize("Browser"), m_ActivePage == PAGE_NETWORK, &Button, 0))
NewPage = PAGE_NETWORK;
{
2022-07-16 13:32:06 +00:00
static CButtonContainer s_GhostButton;
if(GameClient()->m_GameInfo.m_Race)
{
2020-09-05 21:43:39 +00:00
Box.VSplitLeft(90.0f, &Button, &Box);
2020-09-10 18:14:47 +00:00
if(DoButton_MenuTab(&s_GhostButton, Localize("Ghost"), m_ActivePage == PAGE_GHOST, &Button, 0))
NewPage = PAGE_GHOST;
}
}
2010-05-29 07:25:38 +00:00
Box.VSplitLeft(100.0f, &Button, &Box);
2010-05-29 07:25:38 +00:00
Box.VSplitLeft(4.0f, 0, &Box);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_CallVoteButton;
if(DoButton_MenuTab(&s_CallVoteButton, Localize("Call vote"), m_ActivePage == PAGE_CALLVOTE, &Button, IGraphics::CORNER_TR))
{
2010-05-29 07:25:38 +00:00
NewPage = PAGE_CALLVOTE;
m_ControlPageOpening = true;
}
}
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
2020-10-06 10:25:10 +00:00
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
Box.VSplitRight(33.0f, &Box, &Button);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_QuitButton;
ColorRGBA QuitColor(1, 0, 0, 0.5f);
if(DoButton_MenuTab(&s_QuitButton, "\xEF\x80\x91", 0, &Button, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_QUIT], NULL, NULL, &QuitColor, 10.0f, 0))
{
if(m_pClient->Editor()->HasUnsavedData() || (Client()->GetCurrentRaceTime() / 60 >= g_Config.m_ClConfirmQuitTime && g_Config.m_ClConfirmQuitTime >= 0))
{
m_Popup = POPUP_QUIT;
}
else
{
Client()->Quit();
}
}
2010-05-29 07:25:38 +00:00
Box.VSplitRight(10.0f, &Box, &Button);
Box.VSplitRight(33.0f, &Box, &Button);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_SettingsButton;
if(DoButton_MenuTab(&s_SettingsButton, "\xEF\x80\x93", m_ActivePage == PAGE_SETTINGS, &Button, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_SETTINGS], NULL, NULL, NULL, 10.0f, 0))
2010-05-29 07:25:38 +00:00
NewPage = PAGE_SETTINGS;
Box.VSplitRight(10.0f, &Box, &Button);
Box.VSplitRight(33.0f, &Box, &Button);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_EditorButton;
if(DoButton_MenuTab(&s_EditorButton, "\xEF\x81\x84", 0, &Button, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_EDITOR], NULL, NULL, NULL, 10.0f, 0))
{
g_Config.m_ClEditor = 1;
}
if(Client()->State() == IClient::STATE_OFFLINE)
{
Box.VSplitRight(10.0f, &Box, &Button);
Box.VSplitRight(33.0f, &Box, &Button);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_DemoButton;
if(DoButton_MenuTab(&s_DemoButton, "\xEE\x84\xB1", m_ActivePage == PAGE_DEMOS, &Button, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_DEMOBUTTON], NULL, NULL, NULL, 10.0f, 0))
NewPage = PAGE_DEMOS;
Box.VSplitRight(10.0f, &Box, &Button);
Box.VSplitRight(33.0f, &Box, &Button);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ServerButton;
if(DoButton_MenuTab(&s_ServerButton, "\xEF\x95\xBD", m_ActivePage == g_Config.m_UiPage, &Button, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_SERVER], NULL, NULL, NULL, 10.0f, 0))
NewPage = g_Config.m_UiPage;
}
TextRender()->SetRenderFlags(0);
TextRender()->SetCurFont(NULL);
2010-05-29 07:25:38 +00:00
if(NewPage != -1)
{
2010-05-29 07:25:38 +00:00
if(Client()->State() == IClient::STATE_OFFLINE)
SetMenuPage(NewPage);
else
2010-05-29 07:25:38 +00:00
m_GamePage = NewPage;
}
return 0;
}
void CMenus::RenderLoading(const char *pCaption, const char *pContent, int IncreaseCounter, bool RenderLoadingBar, bool RenderMenuBackgroundMap)
{
// TODO: not supported right now due to separate render thread
2022-05-18 16:00:05 +00:00
static std::chrono::nanoseconds LastLoadRender{0};
auto CurLoadRenderCount = m_LoadCurrent;
m_LoadCurrent += IncreaseCounter;
float Percent = CurLoadRenderCount / (float)m_LoadTotal;
// make sure that we don't render for each little thing we load
// because that will slow down loading if we have vsync
if(time_get_nanoseconds() - LastLoadRender < std::chrono::nanoseconds(1s) / 60l)
return;
LastLoadRender = time_get_nanoseconds();
// need up date this here to get correct
ms_GuiColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_UiColor, true));
CUIRect Screen = *UI()->Screen();
// some margin around the screen
Screen.Margin(10.0f, &Screen);
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
if(!RenderMenuBackgroundMap || !m_pBackground->Render())
2020-09-18 16:45:42 +00:00
{
RenderBackground();
}
CUIRect Box = Screen;
Box.Margin(150.0f, &Box);
2009-10-27 14:38:53 +00:00
Graphics()->BlendNormal();
Graphics()->TextureClear();
Box.Draw(ColorRGBA{0, 0, 0, 0.50f}, IGraphics::CORNER_ALL, 15.0f);
CUIRect Part;
Box.HSplitTop(20.f, &Part, &Box);
Box.HSplitTop(24.f, &Part, &Box);
Part.VMargin(20.f, &Part);
SLabelProperties Props;
Props.m_MaxWidth = (int)Part.w;
UI()->DoLabel(&Part, pCaption, 24.f, TEXTALIGN_CENTER);
Box.HSplitTop(20.f, &Part, &Box);
Box.HSplitTop(24.f, &Part, &Box);
Part.VMargin(20.f, &Part);
Props.m_MaxWidth = (int)Part.w;
UI()->DoLabel(&Part, pContent, 20.0f, TEXTALIGN_CENTER);
if(RenderLoadingBar)
Graphics()->DrawRect(Box.x + 40, Box.y + Box.h - 75, (Box.w - 80) * Percent, 25, ColorRGBA(1.0f, 1.0f, 1.0f, 0.75f), IGraphics::CORNER_ALL, 5.0f);
Client()->UpdateAndSwap();
}
2010-05-29 07:25:38 +00:00
void CMenus::RenderNews(CUIRect MainView)
{
g_Config.m_UiUnreadNews = false;
MainView.Draw(ms_ColorTabbarActive, IGraphics::CORNER_B, 10.0f);
2014-06-05 10:11:41 +00:00
MainView.HSplitTop(15.0f, 0, &MainView);
MainView.VSplitLeft(15.0f, 0, &MainView);
CUIRect Label;
const char *pStr = Client()->m_aNews;
char aLine[256];
while((pStr = str_next_token(pStr, "\n", aLine, sizeof(aLine))))
2014-06-05 10:11:41 +00:00
{
const int Len = str_length(aLine);
2020-09-22 16:02:03 +00:00
if(Len > 0 && aLine[0] == '|' && aLine[Len - 1] == '|')
2014-06-05 10:11:41 +00:00
{
MainView.HSplitTop(30.0f, &Label, &MainView);
2020-09-22 16:02:03 +00:00
aLine[Len - 1] = '\0';
UI()->DoLabel(&Label, aLine + 1, 20.0f, TEXTALIGN_LEFT);
2014-06-05 10:11:41 +00:00
}
else
{
MainView.HSplitTop(20.0f, &Label, &MainView);
UI()->DoLabel(&Label, aLine, 15.f, TEXTALIGN_LEFT);
2014-06-05 10:11:41 +00:00
}
}
}
2010-05-29 07:25:38 +00:00
void CMenus::OnInit()
{
2010-05-29 07:25:38 +00:00
if(g_Config.m_ClShowWelcome)
m_Popup = POPUP_LANGUAGE;
if(g_Config.m_ClSkipStartMenu)
m_ShowStart = false;
UI()->InitInputs(m_aInputEvents, &m_NumInputEvents);
2021-09-13 21:48:10 +00:00
m_RefreshButton.Init(UI(), -1);
m_ConnectButton.Init(UI(), -1);
2020-10-12 10:29:47 +00:00
Console()->Chain("add_favorite", ConchainServerbrowserUpdate, this);
Console()->Chain("remove_favorite", ConchainServerbrowserUpdate, this);
2011-06-26 15:10:13 +00:00
Console()->Chain("add_friend", ConchainFriendlistUpdate, this);
Console()->Chain("remove_friend", ConchainFriendlistUpdate, this);
2011-02-27 16:56:03 +00:00
Console()->Chain("snd_enable", ConchainUpdateMusicState, this);
Console()->Chain("snd_enable_music", ConchainUpdateMusicState, this);
Console()->Chain("cl_assets_entities", ConchainAssetsEntities, this);
Console()->Chain("cl_asset_game", ConchainAssetGame, this);
Console()->Chain("cl_asset_emoticons", ConchainAssetEmoticons, this);
Console()->Chain("cl_asset_particles", ConchainAssetParticles, this);
Console()->Chain("cl_asset_hud", ConchainAssetHud, this);
2022-06-14 17:28:51 +00:00
Console()->Chain("cl_asset_extras", ConchainAssetExtras, this);
m_TextureBlob = Graphics()->LoadTexture("blob.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
2011-02-27 16:56:03 +00:00
// setup load amount
const int NumMenuImages = 5;
2011-02-27 16:56:03 +00:00
m_LoadCurrent = 0;
m_LoadTotal = g_pData->m_NumImages + NumMenuImages + GameClient()->ComponentCount();
2011-02-27 16:56:03 +00:00
if(!g_Config.m_ClThreadsoundloading)
m_LoadTotal += g_pData->m_NumSounds;
m_IsInit = true;
// load menu images
m_vMenuImages.clear();
Storage()->ListDirectory(IStorage::TYPE_ALL, "menuimages", MenuImageScan, this);
}
2022-10-04 16:40:24 +00:00
void CMenus::OnConsoleInit()
{
auto *pConfigManager = Kernel()->RequestInterface<IConfigManager>();
if(pConfigManager != nullptr)
pConfigManager->RegisterCallback(CMenus::ConfigSaveCallback, this);
Console()->Register("add_favorite_skin", "s[skin_name]", CFGFLAG_CLIENT, Con_AddFavoriteSkin, this, "Add a skin as a favorite");
Console()->Register("remove_favorite_skin", "s[skin_name]", CFGFLAG_CLIENT, Con_RemFavoriteSkin, this, "Remove a skin from the favorites");
}
void CMenus::ConchainUpdateMusicState(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData);
auto *pSelf = (CMenus *)pUserData;
if(pResult->NumArguments())
pSelf->UpdateMusicState();
}
void CMenus::UpdateMusicState()
{
const bool ShouldPlay = Client()->State() == IClient::STATE_OFFLINE && g_Config.m_SndEnable && g_Config.m_SndMusic;
if(ShouldPlay && !m_pClient->m_Sounds.IsPlaying(SOUND_MENU))
m_pClient->m_Sounds.Enqueue(CSounds::CHN_MUSIC, SOUND_MENU);
else if(!ShouldPlay && m_pClient->m_Sounds.IsPlaying(SOUND_MENU))
m_pClient->m_Sounds.Stop(SOUND_MENU);
}
void CMenus::PopupMessage(const char *pTitle, const char *pMessage, const char *pButtonLabel, int NextPopup, FPopupButtonCallback pfnButtonCallback)
{
// reset active item
UI()->SetActiveItem(nullptr);
str_copy(m_aPopupTitle, pTitle);
str_copy(m_aPopupMessage, pMessage);
str_copy(m_aPopupButtons[BUTTON_CONFIRM].m_aLabel, pButtonLabel);
m_aPopupButtons[BUTTON_CONFIRM].m_NextPopup = NextPopup;
m_aPopupButtons[BUTTON_CONFIRM].m_pfnCallback = pfnButtonCallback;
2020-09-05 14:54:20 +00:00
m_Popup = POPUP_MESSAGE;
}
void CMenus::PopupConfirm(const char *pTitle, const char *pMessage, const char *pConfirmButtonLabel, const char *pCancelButtonLabel,
FPopupButtonCallback pfnConfirmButtonCallback, int ConfirmNextPopup, FPopupButtonCallback pfnCancelButtonCallback, int CancelNextPopup)
{
// reset active item
UI()->SetActiveItem(nullptr);
str_copy(m_aPopupTitle, pTitle);
str_copy(m_aPopupMessage, pMessage);
str_copy(m_aPopupButtons[BUTTON_CONFIRM].m_aLabel, pConfirmButtonLabel);
m_aPopupButtons[BUTTON_CONFIRM].m_NextPopup = ConfirmNextPopup;
m_aPopupButtons[BUTTON_CONFIRM].m_pfnCallback = pfnConfirmButtonCallback;
str_copy(m_aPopupButtons[BUTTON_CANCEL].m_aLabel, pCancelButtonLabel);
m_aPopupButtons[BUTTON_CANCEL].m_NextPopup = CancelNextPopup;
m_aPopupButtons[BUTTON_CANCEL].m_pfnCallback = pfnCancelButtonCallback;
m_Popup = POPUP_CONFIRM;
}
2022-05-18 16:00:05 +00:00
void CMenus::PopupWarning(const char *pTopic, const char *pBody, const char *pButton, std::chrono::nanoseconds Duration)
{
// no multiline support for console
std::string BodyStr = pBody;
while(BodyStr.find('\n') != std::string::npos)
BodyStr.replace(BodyStr.find('\n'), 1, " ");
dbg_msg(pTopic, "%s", BodyStr.c_str());
// reset active item
UI()->SetActiveItem(nullptr);
2022-07-09 16:14:56 +00:00
str_copy(m_aMessageTopic, pTopic);
str_copy(m_aMessageBody, pBody);
str_copy(m_aMessageButton, pButton);
m_Popup = POPUP_WARNING;
SetActive(true);
m_PopupWarningDuration = Duration;
m_PopupWarningLastTime = time_get_nanoseconds();
}
bool CMenus::CanDisplayWarning()
{
return m_Popup == POPUP_NONE;
}
2020-10-28 02:59:50 +00:00
void CMenus::RenderColorPicker()
{
if(UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
{
ms_ColorPicker.m_Active = false;
ms_ValueSelectorTextMode = false;
UI()->SetActiveItem(nullptr);
}
2020-10-28 02:59:50 +00:00
if(!ms_ColorPicker.m_Active)
return;
// First check if we should disable color picker
CUIRect PickerRect;
PickerRect.x = ms_ColorPicker.m_X;
PickerRect.y = ms_ColorPicker.m_Y;
PickerRect.w = ms_ColorPicker.ms_Width;
PickerRect.h = ms_ColorPicker.ms_Height;
2020-12-14 00:51:31 +00:00
if(UI()->MouseButtonClicked(0) && !UI()->MouseInside(&PickerRect) && !UI()->MouseInside(&ms_ColorPicker.m_AttachedRect))
2020-10-28 02:59:50 +00:00
{
ms_ColorPicker.m_Active = false;
2020-12-14 00:51:31 +00:00
ms_ValueSelectorTextMode = false;
UI()->SetActiveItem(nullptr);
2020-10-28 02:59:50 +00:00
return;
}
// Prevent activation of UI elements outside of active color picker
if(UI()->MouseInside(&PickerRect))
UI()->SetHotItem(&ms_ColorPicker);
2020-10-28 02:59:50 +00:00
// Render
2020-12-14 00:51:31 +00:00
ColorRGBA BackgroundColor(0.1f, 0.1f, 0.1f, 1.0f);
PickerRect.Draw(BackgroundColor, 0, 0);
2020-10-28 02:59:50 +00:00
CUIRect ColorsArea, HueArea, ValuesHitbox, BottomArea, HueRect, SatRect, ValueRect, HexRect, AlphaRect;
2020-10-28 02:59:50 +00:00
PickerRect.Margin(3, &ColorsArea);
2020-12-14 00:51:31 +00:00
ColorsArea.HSplitBottom(ms_ColorPicker.ms_Height - 140.0f, &ColorsArea, &ValuesHitbox);
2020-10-28 02:59:50 +00:00
ColorsArea.VSplitRight(20, &ColorsArea, &HueArea);
2020-12-14 00:51:31 +00:00
BottomArea = ValuesHitbox;
2020-10-28 02:59:50 +00:00
BottomArea.HSplitTop(3, 0x0, &BottomArea);
HueArea.VSplitLeft(3, 0x0, &HueArea);
BottomArea.HSplitTop(20, &HueRect, &BottomArea);
2020-12-14 00:51:31 +00:00
BottomArea.HSplitTop(3, 0x0, &BottomArea);
constexpr float ValuePadding = 5.0f;
const float HsvValueWidth = (HueRect.w - ValuePadding * 2) / 3.0f;
const float HexValueWidth = HsvValueWidth * 2 + ValuePadding;
2020-12-14 00:51:31 +00:00
HueRect.VSplitLeft(HsvValueWidth, &HueRect, &SatRect);
SatRect.VSplitLeft(ValuePadding, 0x0, &SatRect);
SatRect.VSplitLeft(HsvValueWidth, &SatRect, &ValueRect);
ValueRect.VSplitLeft(ValuePadding, 0x0, &ValueRect);
2020-12-14 00:51:31 +00:00
BottomArea.HSplitTop(20, &HexRect, &BottomArea);
HexRect.VSplitLeft(HexValueWidth, &HexRect, &AlphaRect);
AlphaRect.VSplitLeft(ValuePadding, 0x0, &AlphaRect);
2020-10-28 02:59:50 +00:00
2020-12-14 00:51:31 +00:00
if(UI()->MouseButtonReleased(1) && !UI()->MouseInside(&ValuesHitbox))
{
ms_ColorPicker.m_Active = false;
ms_ValueSelectorTextMode = false;
UI()->SetActiveItem(nullptr);
2020-12-14 00:51:31 +00:00
return;
}
ColorRGBA BlackColor(0, 0, 0, 0.5f);
HueArea.Draw(BlackColor, 0, 0);
2020-10-28 02:59:50 +00:00
HueArea.Margin(1, &HueArea);
ColorsArea.Draw(BlackColor, 0, 0);
2020-10-28 02:59:50 +00:00
ColorsArea.Margin(1, &ColorsArea);
2020-12-14 00:51:31 +00:00
ColorHSVA PickerColorHSV(ms_ColorPicker.m_HSVColor);
unsigned H = (unsigned)(PickerColorHSV.x * 255.0f);
unsigned S = (unsigned)(PickerColorHSV.y * 255.0f);
unsigned V = (unsigned)(PickerColorHSV.z * 255.0f);
2020-10-28 02:59:50 +00:00
// Color Area
vec4 TL = color_cast<ColorRGBA>(ColorHSVA(PickerColorHSV.x, 0.0f, 1.0f));
vec4 TR = color_cast<ColorRGBA>(ColorHSVA(PickerColorHSV.x, 1.0f, 1.0f));
vec4 BL = color_cast<ColorRGBA>(ColorHSVA(PickerColorHSV.x, 0.0f, 1.0f));
vec4 BR = color_cast<ColorRGBA>(ColorHSVA(PickerColorHSV.x, 1.0f, 1.0f));
2020-10-28 02:59:50 +00:00
ColorsArea.Draw4(TL, TR, BL, BR, IGraphics::CORNER_NONE, 0.0f);
2020-10-28 02:59:50 +00:00
TL = vec4(0.0f, 0.0f, 0.0f, 0.0f);
TR = vec4(0.0f, 0.0f, 0.0f, 0.0f);
BL = vec4(0.0f, 0.0f, 0.0f, 1.0f);
BR = vec4(0.0f, 0.0f, 0.0f, 1.0f);
ColorsArea.Draw4(TL, TR, BL, BR, IGraphics::CORNER_NONE, 0.0f);
2020-10-28 02:59:50 +00:00
// Hue Area
static const float s_aColorIndices[7][3] = {
{1.0f, 0.0f, 0.0f}, // red
{1.0f, 0.0f, 1.0f}, // magenta
{0.0f, 0.0f, 1.0f}, // blue
{0.0f, 1.0f, 1.0f}, // cyan
{0.0f, 1.0f, 0.0f}, // green
{1.0f, 1.0f, 0.0f}, // yellow
{1.0f, 0.0f, 0.0f} // red
};
float HuePickerOffset = HueArea.h / 6.0f;
CUIRect HuePartialArea = HueArea;
HuePartialArea.h = HuePickerOffset;
for(int j = 0; j < 6; j++)
{
TL = vec4(s_aColorIndices[j][0], s_aColorIndices[j][1], s_aColorIndices[j][2], 1.0f);
BL = vec4(s_aColorIndices[j + 1][0], s_aColorIndices[j + 1][1], s_aColorIndices[j + 1][2], 1.0f);
HuePartialArea.y = HueArea.y + HuePickerOffset * j;
HuePartialArea.Draw4(TL, TL, BL, BL, IGraphics::CORNER_NONE, 0.0f);
2020-10-28 02:59:50 +00:00
}
// Editboxes Area
2020-12-14 00:51:31 +00:00
ColorRGBA EditboxBackground(0, 0, 0, 0.4f);
static int s_aValueSelectorIds[4];
2020-12-14 00:51:31 +00:00
H = DoValueSelector(&s_aValueSelectorIds[0], &HueRect, "H:", true, H, 0, 255, 1, 1, false, 5.0f, &EditboxBackground);
S = DoValueSelector(&s_aValueSelectorIds[1], &SatRect, "S:", true, S, 0, 255, 1, 1, false, 5.0f, &EditboxBackground);
V = DoValueSelector(&s_aValueSelectorIds[2], &ValueRect, "V:", true, V, 0, 255, 1, 1, false, 5.0f, &EditboxBackground);
2020-10-28 02:59:50 +00:00
PickerColorHSV = ColorHSVA(H / 255.0f, S / 255.0f, V / 255.0f);
2020-10-28 02:59:50 +00:00
unsigned int Hex = color_cast<ColorRGBA>(PickerColorHSV).Pack(false);
unsigned int NewHex = DoValueSelector(&s_aValueSelectorIds[3], &HexRect, "HEX:", false, Hex, 0, 0xFFFFFF, 1, 1, true, 5.0f, &EditboxBackground);
2020-12-14 00:51:31 +00:00
if(Hex != NewHex)
PickerColorHSV = color_cast<ColorHSVA>(ColorRGBA(NewHex));
2020-12-14 00:51:31 +00:00
// TODO : ALPHA SUPPORT
UI()->DoLabel(&AlphaRect, "A: 255", 10, TEXTALIGN_CENTER);
AlphaRect.Draw(ColorRGBA(0, 0, 0, 0.65f), IGraphics::CORNER_ALL, 5.0f);
2020-10-28 02:59:50 +00:00
// Logic
float PickerX, PickerY;
static int s_ColorPickerId = 0;
static int s_HuePickerId = 0;
2020-12-14 01:49:24 +00:00
if(UI()->DoPickerLogic(&s_ColorPickerId, &ColorsArea, &PickerX, &PickerY))
2020-10-28 02:59:50 +00:00
{
2020-12-14 00:51:31 +00:00
PickerColorHSV.y = PickerX / ColorsArea.w;
PickerColorHSV.z = 1.0f - PickerY / ColorsArea.h;
2020-10-28 02:59:50 +00:00
}
if(UI()->DoPickerLogic(&s_HuePickerId, &HueArea, &PickerX, &PickerY))
2020-12-14 00:51:31 +00:00
PickerColorHSV.x = 1.0f - PickerY / HueArea.h;
2020-10-28 02:59:50 +00:00
// Marker Color Area
2020-12-14 00:51:31 +00:00
float MarkerX = ColorsArea.x + ColorsArea.w * PickerColorHSV.y;
float MarkerY = ColorsArea.y + ColorsArea.h * (1.0f - PickerColorHSV.z);
2020-10-28 02:59:50 +00:00
const float MarkerOutlineInd = PickerColorHSV.z > 0.5f ? 0.0f : 1.0f;
2020-10-28 02:59:50 +00:00
ColorRGBA MarkerOutline(MarkerOutlineInd, MarkerOutlineInd, MarkerOutlineInd, 1.0f);
2020-12-14 00:51:31 +00:00
Graphics()->TextureClear();
2020-10-28 02:59:50 +00:00
Graphics()->QuadsBegin();
Graphics()->SetColor(MarkerOutline);
Graphics()->DrawCircle(MarkerX, MarkerY, 4.5f, 32);
Graphics()->SetColor(color_cast<ColorRGBA>(PickerColorHSV));
Graphics()->DrawCircle(MarkerX, MarkerY, 3.5f, 32);
2020-10-28 02:59:50 +00:00
Graphics()->QuadsEnd();
// Marker Hue Area
CUIRect HueMarker;
HueArea.Margin(-2.5f, &HueMarker);
2020-12-14 00:51:31 +00:00
HueMarker.h = 6.5f;
HueMarker.y = (HueArea.y + HueArea.h * (1.0f - PickerColorHSV.x)) - HueMarker.h / 2.0f;
ColorRGBA HueMarkerColor = color_cast<ColorRGBA>(ColorHSVA(PickerColorHSV.x, 1, 1, 1));
const float HueMarkerOutlineColor = PickerColorHSV.x > 0.75f ? 1.0f : 0.0f;
ColorRGBA HueMarkerOutline(HueMarkerOutlineColor, HueMarkerOutlineColor, HueMarkerOutlineColor, 1);
HueMarker.Draw(HueMarkerOutline, IGraphics::CORNER_ALL, 1.2f);
2020-12-14 00:51:31 +00:00
HueMarker.Margin(1.2f, &HueMarker);
HueMarker.Draw(HueMarkerColor, IGraphics::CORNER_ALL, 1.2f);
2020-12-14 00:51:31 +00:00
ms_ColorPicker.m_HSVColor = PickerColorHSV.Pack(false);
*ms_ColorPicker.m_pColor = color_cast<ColorHSLA>(PickerColorHSV).Pack(false);
2020-10-28 02:59:50 +00:00
}
2010-05-29 07:25:38 +00:00
int CMenus::Render()
{
if(Client()->State() == IClient::STATE_DEMOPLAYBACK && m_Popup == POPUP_NONE)
return 0;
CUIRect Screen = *UI()->Screen();
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
UI()->ResetMouseSlow();
static int s_Frame = 0;
if(s_Frame == 0)
{
m_MenuPage = g_Config.m_UiPage;
s_Frame++;
}
else if(s_Frame == 1)
{
UpdateMusicState();
s_Frame++;
RefreshBrowserTab(g_Config.m_UiPage);
2010-05-29 07:25:38 +00:00
if(g_Config.m_UiPage == PAGE_INTERNET)
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
else if(g_Config.m_UiPage == PAGE_LAN)
ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN);
else if(g_Config.m_UiPage == PAGE_FAVORITES)
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
2014-09-13 14:36:25 +00:00
else if(g_Config.m_UiPage == PAGE_DDNET)
ServerBrowser()->Refresh(IServerBrowser::TYPE_DDNET);
else if(g_Config.m_UiPage == PAGE_KOG)
ServerBrowser()->Refresh(IServerBrowser::TYPE_KOG);
}
if(Client()->State() >= IClient::STATE_ONLINE)
{
2010-05-29 07:25:38 +00:00
ms_ColorTabbarInactive = ms_ColorTabbarInactiveIngame;
ms_ColorTabbarActive = ms_ColorTabbarActiveIngame;
ms_ColorTabbarHover = ms_ColorTabbarHoverIngame;
}
else
{
2020-09-18 16:45:42 +00:00
if(!m_pBackground->Render())
{
RenderBackground();
}
2010-05-29 07:25:38 +00:00
ms_ColorTabbarInactive = ms_ColorTabbarInactiveOutgame;
ms_ColorTabbarActive = ms_ColorTabbarActiveOutgame;
ms_ColorTabbarHover = ms_ColorTabbarHoverOutgame;
}
2010-05-29 07:25:38 +00:00
CUIRect TabBar;
CUIRect MainView;
// some margin around the screen
2010-05-29 07:25:38 +00:00
Screen.Margin(10.0f, &Screen);
static bool s_SoundCheck = false;
if(!s_SoundCheck && m_Popup == POPUP_NONE)
{
if(Client()->SoundInitFailed())
PopupMessage(Localize("Sound error"), Localize("The audio device couldn't be initialised."), Localize("Ok"));
s_SoundCheck = true;
}
2010-05-29 07:25:38 +00:00
if(m_Popup == POPUP_NONE)
{
if(m_JoinTutorial && !Client()->InfoTaskRunning() && !ServerBrowser()->IsGettingServerlist())
{
m_JoinTutorial = false;
const char *pAddr = ServerBrowser()->GetTutorialServer();
if(pAddr)
Client()->Connect(pAddr);
}
if(m_ShowStart && Client()->State() == IClient::STATE_OFFLINE)
2020-09-18 16:45:42 +00:00
{
m_pBackground->ChangePosition(CMenuBackground::POS_START);
RenderStartMenu(Screen);
2020-09-18 16:45:42 +00:00
}
else
{
Screen.HSplitTop(24.0f, &TabBar, &MainView);
if(Client()->State() == IClient::STATE_OFFLINE && UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
{
m_ShowStart = true;
}
// render news
if(m_MenuPage < PAGE_NEWS || m_MenuPage > PAGE_SETTINGS || (Client()->State() == IClient::STATE_OFFLINE && m_MenuPage >= PAGE_GAME && m_MenuPage <= PAGE_CALLVOTE))
{
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
SetMenuPage(PAGE_INTERNET);
}
// render current page
if(Client()->State() != IClient::STATE_OFFLINE)
{
if(m_GamePage == PAGE_GAME)
{
RenderGame(MainView);
RenderIngameHint();
}
else if(m_GamePage == PAGE_PLAYERS)
{
RenderPlayers(MainView);
}
else if(m_GamePage == PAGE_SERVER_INFO)
{
RenderServerInfo(MainView);
}
else if(m_GamePage == PAGE_NETWORK)
{
RenderInGameNetwork(MainView);
}
else if(m_GamePage == PAGE_GHOST)
{
RenderGhost(MainView);
}
else if(m_GamePage == PAGE_CALLVOTE)
{
RenderServerControl(MainView);
}
else if(m_GamePage == PAGE_SETTINGS)
{
RenderSettings(MainView);
}
}
else if(m_MenuPage == PAGE_NEWS)
2020-09-18 16:45:42 +00:00
{
m_pBackground->ChangePosition(CMenuBackground::POS_NEWS);
RenderNews(MainView);
2020-09-18 16:45:42 +00:00
}
else if(m_MenuPage == PAGE_INTERNET)
2020-09-18 16:45:42 +00:00
{
2020-09-26 07:37:35 +00:00
m_pBackground->ChangePosition(CMenuBackground::POS_BROWSER_INTERNET);
RenderServerbrowser(MainView);
2020-09-18 16:45:42 +00:00
}
else if(m_MenuPage == PAGE_LAN)
2020-09-18 16:45:42 +00:00
{
2020-09-26 07:37:35 +00:00
m_pBackground->ChangePosition(CMenuBackground::POS_BROWSER_LAN);
RenderServerbrowser(MainView);
2020-09-18 16:45:42 +00:00
}
else if(m_MenuPage == PAGE_DEMOS)
2020-09-18 16:45:42 +00:00
{
m_pBackground->ChangePosition(CMenuBackground::POS_DEMOS);
RenderDemoList(MainView);
2020-09-18 16:45:42 +00:00
}
else if(m_MenuPage == PAGE_FAVORITES)
2020-09-18 16:45:42 +00:00
{
2020-09-26 07:37:35 +00:00
m_pBackground->ChangePosition(CMenuBackground::POS_BROWSER_FAVORITES);
RenderServerbrowser(MainView);
2020-09-18 16:45:42 +00:00
}
else if(m_MenuPage == PAGE_DDNET)
2020-09-18 16:45:42 +00:00
{
2020-09-26 07:37:35 +00:00
m_pBackground->ChangePosition(CMenuBackground::POS_BROWSER_CUSTOM0);
RenderServerbrowser(MainView);
2020-09-18 16:45:42 +00:00
}
else if(m_MenuPage == PAGE_KOG)
2020-09-18 16:45:42 +00:00
{
2020-09-26 07:37:35 +00:00
m_pBackground->ChangePosition(CMenuBackground::POS_BROWSER_CUSTOM0 + 1);
RenderServerbrowser(MainView);
2020-09-18 16:45:42 +00:00
}
else if(m_MenuPage == PAGE_SETTINGS)
2010-05-29 07:25:38 +00:00
RenderSettings(MainView);
2020-10-28 02:59:50 +00:00
// do tab bar
RenderMenubar(TabBar);
}
}
else
{
// make sure that other windows doesn't do anything funnay!
2009-10-27 14:38:53 +00:00
//UI()->SetHotItem(0);
//UI()->SetActiveItem(nullptr);
char aBuf[1536];
2010-05-29 07:25:38 +00:00
const char *pTitle = "";
const char *pExtraText = "";
const char *pButtonText = "";
int ExtraAlign = 0;
2022-07-11 00:13:56 +00:00
bool UseIpLabel = false;
ColorRGBA BgColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f);
if(m_Popup == POPUP_MESSAGE || m_Popup == POPUP_CONFIRM)
{
pTitle = m_aPopupTitle;
pExtraText = m_aPopupMessage;
}
2010-05-29 07:25:38 +00:00
else if(m_Popup == POPUP_CONNECTING)
{
2010-05-29 07:25:38 +00:00
pTitle = Localize("Connecting to");
2022-07-11 00:13:56 +00:00
UseIpLabel = true;
2010-05-29 07:25:38 +00:00
pButtonText = Localize("Abort");
if(Client()->State() == IClient::STATE_CONNECTING && time_get() - Client()->StateStartTime() > time_freq())
{
int Connectivity = Client()->UdpConnectivity(Client()->ConnectNetTypes());
switch(Connectivity)
{
case IClient::CONNECTIVITY_UNKNOWN:
break;
case IClient::CONNECTIVITY_CHECKING:
2022-07-11 00:13:56 +00:00
pExtraText = Localize("Trying to determine UDP connectivity...");
break;
case IClient::CONNECTIVITY_UNREACHABLE:
2022-07-11 00:13:56 +00:00
pExtraText = Localize("UDP seems to be filtered.");
break;
case IClient::CONNECTIVITY_DIFFERING_UDP_TCP_IP_ADDRESSES:
2022-07-11 00:13:56 +00:00
pExtraText = Localize("UDP and TCP IP addresses seem to be different. Try disabling VPN, proxy or network accelerators.");
break;
case IClient::CONNECTIVITY_REACHABLE:
2022-07-11 00:13:56 +00:00
pExtraText = Localize("No answer from server yet.");
break;
}
}
else if(Client()->MapDownloadTotalsize() > 0)
{
2014-08-15 23:06:17 +00:00
str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("Downloading map"), Client()->MapDownloadName());
pTitle = aBuf;
2022-07-11 00:13:56 +00:00
UseIpLabel = false;
}
else if(Client()->State() == IClient::STATE_LOADING)
{
2022-07-11 00:13:56 +00:00
UseIpLabel = false;
if(Client()->LoadingStateDetail() == IClient::LOADING_STATE_DETAIL_INITIAL)
{
pTitle = Localize("Connected");
pExtraText = Localize("Getting game info");
}
else if(Client()->LoadingStateDetail() == IClient::LOADING_STATE_DETAIL_LOADING_MAP)
{
pTitle = Localize("Connected");
pExtraText = Localize("Loading map file from storage");
}
else if(Client()->LoadingStateDetail() == IClient::LOADING_STATE_DETAIL_SENDING_READY)
{
pTitle = Localize("Connected");
pExtraText = Localize("Requesting to join the game");
}
else if(Client()->LoadingStateDetail() == IClient::LOADING_STATE_DETAIL_GETTING_READY)
{
pTitle = Localize("Connected");
2022-07-14 20:36:06 +00:00
pExtraText = Localize("Sending initial client info");
}
}
}
else if(m_Popup == POPUP_DISCONNECTED)
{
2010-05-29 07:25:38 +00:00
pTitle = Localize("Disconnected");
pExtraText = Client()->ErrorString();
pButtonText = Localize("Ok");
if(Client()->m_ReconnectTime > 0)
{
str_format(aBuf, sizeof(aBuf), Localize("Reconnect in %d sec"), (int)((Client()->m_ReconnectTime - time_get()) / time_freq()));
pTitle = Client()->ErrorString();
pExtraText = aBuf;
pButtonText = Localize("Abort");
}
ExtraAlign = 0;
}
else if(m_Popup == POPUP_RENAME_DEMO)
{
pTitle = Localize("Rename demo");
}
2020-09-22 16:02:03 +00:00
#if defined(CONF_VIDEORECORDER)
else if(m_Popup == POPUP_RENDER_DEMO)
{
pTitle = Localize("Render demo");
}
2020-09-22 16:02:03 +00:00
#endif
2010-05-29 07:25:38 +00:00
else if(m_Popup == POPUP_PASSWORD)
{
2010-06-02 19:03:15 +00:00
pTitle = Localize("Password incorrect");
2010-05-29 07:25:38 +00:00
pButtonText = Localize("Try again");
}
2010-05-29 07:25:38 +00:00
else if(m_Popup == POPUP_QUIT)
{
2010-05-29 07:25:38 +00:00
pTitle = Localize("Quit");
pExtraText = Localize("Are you sure that you want to quit?");
}
2010-05-29 07:25:38 +00:00
else if(m_Popup == POPUP_FIRST_LAUNCH)
{
pTitle = Localize("Welcome to DDNet");
str_format(aBuf, sizeof(aBuf), "%s\n\n%s\n\n%s\n\n%s",
Localize("DDraceNetwork is a cooperative online game where the goal is for you and your group of tees to reach the finish line of the map. As a newcomer you should start on Novice servers, which host the easiest maps. Consider the ping to choose a server close to you."),
Localize("Use k key to kill (restart), q to pause and watch other players. See settings for other key binds."),
Localize("It's recommended that you check the settings to adjust them to your liking before joining a server."),
2020-11-05 08:32:14 +00:00
Localize("Please enter your nickname below."));
pExtraText = aBuf;
2010-05-29 07:25:38 +00:00
pButtonText = Localize("Ok");
ExtraAlign = -1;
}
else if(m_Popup == POPUP_POINTS)
{
pTitle = Localize("Existing Player");
if(Client()->m_Points > 50)
{
2020-11-05 08:32:14 +00:00
str_format(aBuf, sizeof(aBuf), Localize("Your nickname '%s' is already used (%d points). Do you still want to use it?"), Client()->PlayerName(), Client()->m_Points);
pExtraText = aBuf;
2022-07-11 00:13:56 +00:00
ExtraAlign = -1;
}
else if(Client()->m_Points >= 0)
{
m_Popup = POPUP_NONE;
}
else
{
pExtraText = Localize("Checking for existing player with your name");
}
}
else if(m_Popup == POPUP_WARNING)
{
BgColor = ColorRGBA(0.5f, 0.0f, 0.0f, 0.7f);
pTitle = m_aMessageTopic;
pExtraText = m_aMessageBody;
pButtonText = m_aMessageButton;
ExtraAlign = -1;
}
CUIRect Box, Part;
2010-05-29 07:25:38 +00:00
Box = Screen;
if(m_Popup != POPUP_FIRST_LAUNCH)
Box.Margin(150.0f, &Box);
// render the box
Box.Draw(BgColor, IGraphics::CORNER_ALL, 15.0f);
Box.HSplitTop(20.f, &Part, &Box);
Box.HSplitTop(24.f, &Part, &Box);
Part.VMargin(20.f, &Part);
2022-03-11 16:34:48 +00:00
SLabelProperties Props;
Props.m_MaxWidth = (int)Part.w;
2022-07-11 00:13:56 +00:00
if(TextRender()->TextWidth(24.f, pTitle, -1, -1.0f) > Part.w)
UI()->DoLabel(&Part, pTitle, 24.f, TEXTALIGN_LEFT, Props);
2015-03-29 12:20:34 +00:00
else
UI()->DoLabel(&Part, pTitle, 24.f, TEXTALIGN_CENTER);
2022-07-11 00:13:56 +00:00
Box.HSplitTop(20.f, &Part, &Box);
Box.HSplitTop(24.f, &Part, &Box);
Part.VMargin(20.f, &Part);
float FontSize = m_Popup == POPUP_FIRST_LAUNCH ? 16.0f : 20.f;
2022-07-11 00:13:56 +00:00
if(UseIpLabel)
{
UI()->DoLabel(&Part, Client()->ConnectAddressString(), FontSize, TEXTALIGN_CENTER);
Box.HSplitTop(20.f, &Part, &Box);
Box.HSplitTop(24.f, &Part, &Box);
}
2022-03-11 16:34:48 +00:00
Props.m_MaxWidth = (int)Part.w;
2010-05-29 07:25:38 +00:00
if(ExtraAlign == -1)
UI()->DoLabel(&Part, pExtraText, FontSize, TEXTALIGN_LEFT, Props);
else
{
if(TextRender()->TextWidth(FontSize, pExtraText, -1, -1.0f) > Part.w)
UI()->DoLabel(&Part, pExtraText, FontSize, TEXTALIGN_LEFT, Props);
else
UI()->DoLabel(&Part, pExtraText, FontSize, TEXTALIGN_CENTER);
}
if(m_Popup == POPUP_MESSAGE || m_Popup == POPUP_CONFIRM)
{
CUIRect ButtonBar;
Box.HSplitBottom(20.0f, &Box, nullptr);
Box.HSplitBottom(24.0f, &Box, &ButtonBar);
ButtonBar.VMargin(100.0f, &ButtonBar);
if(m_Popup == POPUP_MESSAGE)
{
static CButtonContainer s_ButtonConfirm;
if(DoButton_Menu(&s_ButtonConfirm, m_aPopupButtons[BUTTON_CONFIRM].m_aLabel, 0, &ButtonBar) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
{
m_Popup = m_aPopupButtons[BUTTON_CONFIRM].m_NextPopup;
(this->*m_aPopupButtons[BUTTON_CONFIRM].m_pfnCallback)();
}
}
else if(m_Popup == POPUP_CONFIRM)
{
CUIRect CancelButton, ConfirmButton;
ButtonBar.VSplitMid(&CancelButton, &ConfirmButton, 40.0f);
static CButtonContainer s_ButtonCancel;
if(DoButton_Menu(&s_ButtonCancel, m_aPopupButtons[BUTTON_CANCEL].m_aLabel, 0, &CancelButton) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
{
m_Popup = m_aPopupButtons[BUTTON_CANCEL].m_NextPopup;
(this->*m_aPopupButtons[BUTTON_CANCEL].m_pfnCallback)();
}
static CButtonContainer s_ButtonConfirm;
if(DoButton_Menu(&s_ButtonConfirm, m_aPopupButtons[BUTTON_CONFIRM].m_aLabel, 0, &ConfirmButton) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
{
m_Popup = m_aPopupButtons[BUTTON_CONFIRM].m_NextPopup;
(this->*m_aPopupButtons[BUTTON_CONFIRM].m_pfnCallback)();
}
}
}
else if(m_Popup == POPUP_QUIT)
{
2010-05-29 07:25:38 +00:00
CUIRect Yes, No;
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
// additional info
Box.VMargin(20.f, &Box);
if(m_pClient->Editor()->HasUnsavedData())
{
str_format(aBuf, sizeof(aBuf), "%s\n%s", Localize("There's an unsaved map in the editor, you might want to save it before you quit the game."), Localize("Quit anyway?"));
2022-03-11 16:34:48 +00:00
Props.m_MaxWidth = Part.w - 20.0f;
UI()->DoLabel(&Box, aBuf, 20.f, TEXTALIGN_LEFT, Props);
}
// buttons
2010-05-29 07:25:38 +00:00
Part.VMargin(80.0f, &Part);
Part.VSplitMid(&No, &Yes);
Yes.VMargin(20.0f, &Yes);
No.VMargin(20.0f, &No);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ButtonAbort;
if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
2010-05-29 07:25:38 +00:00
m_Popup = POPUP_NONE;
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ButtonTryAgain;
if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
{
m_Popup = POPUP_NONE;
2010-05-29 07:25:38 +00:00
Client()->Quit();
}
}
2010-05-29 07:25:38 +00:00
else if(m_Popup == POPUP_PASSWORD)
{
2010-05-29 07:25:38 +00:00
CUIRect Label, TextBox, TryAgain, Abort;
2010-05-29 07:25:38 +00:00
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VMargin(80.0f, &Part);
2010-05-29 07:25:38 +00:00
Part.VSplitMid(&Abort, &TryAgain);
2010-05-29 07:25:38 +00:00
TryAgain.VMargin(20.0f, &TryAgain);
Abort.VMargin(20.0f, &Abort);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ButtonAbort;
if(DoButton_Menu(&s_ButtonAbort, Localize("Abort"), 0, &Abort) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
2010-05-29 07:25:38 +00:00
m_Popup = POPUP_NONE;
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ButtonTryAgain;
if(DoButton_Menu(&s_ButtonTryAgain, Localize("Try again"), 0, &TryAgain) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
{
Client()->Connect(g_Config.m_UiServerAddress, g_Config.m_Password);
}
2010-05-29 07:25:38 +00:00
Box.HSplitBottom(60.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
2010-05-29 07:25:38 +00:00
Part.VSplitLeft(60.0f, 0, &Label);
Label.VSplitLeft(100.0f, 0, &TextBox);
TextBox.VSplitLeft(20.0f, 0, &TextBox);
TextBox.VSplitRight(60.0f, &TextBox, 0);
UI()->DoLabel(&Label, Localize("Password"), 18.0f, TEXTALIGN_LEFT);
static float s_Offset = 0.0f;
UI()->DoEditBox(&g_Config.m_Password, &TextBox, g_Config.m_Password, sizeof(g_Config.m_Password), 12.0f, &s_Offset, true);
}
2010-12-16 00:52:29 +00:00
else if(m_Popup == POPUP_CONNECTING)
{
Box = Screen;
Box.Margin(150.0f, &Box);
2010-12-16 00:52:29 +00:00
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VMargin(120.0f, &Part);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_Button;
if(DoButton_Menu(&s_Button, pButtonText, 0, &Part) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
2010-12-16 00:52:29 +00:00
{
Client()->Disconnect();
m_Popup = POPUP_NONE;
RefreshBrowserTab(g_Config.m_UiPage);
2010-12-16 00:52:29 +00:00
}
if(Client()->MapDownloadTotalsize() > 0)
{
2021-06-23 05:05:49 +00:00
int64_t Now = time_get();
2020-09-22 16:02:03 +00:00
if(Now - m_DownloadLastCheckTime >= time_freq())
2010-12-16 00:52:29 +00:00
{
if(m_DownloadLastCheckSize > Client()->MapDownloadAmount())
{
// map downloaded restarted
m_DownloadLastCheckSize = 0;
}
// update download speed
2020-09-22 16:02:03 +00:00
float Diff = (Client()->MapDownloadAmount() - m_DownloadLastCheckSize) / ((int)((Now - m_DownloadLastCheckTime) / time_freq()));
float StartDiff = m_DownloadLastCheckSize - 0.0f;
if(StartDiff + Diff > 0.0f)
m_DownloadSpeed = (Diff / (StartDiff + Diff)) * (Diff / 1.0f) + (StartDiff / (Diff + StartDiff)) * m_DownloadSpeed;
else
m_DownloadSpeed = 0.0f;
2010-12-16 00:52:29 +00:00
m_DownloadLastCheckTime = Now;
m_DownloadLastCheckSize = Client()->MapDownloadAmount();
}
Box.HSplitTop(64.f, 0, &Box);
Box.HSplitTop(24.f, &Part, &Box);
2020-09-22 16:02:03 +00:00
str_format(aBuf, sizeof(aBuf), "%d/%d KiB (%.1f KiB/s)", Client()->MapDownloadAmount() / 1024, Client()->MapDownloadTotalsize() / 1024, m_DownloadSpeed / 1024.0f);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&Part, aBuf, 20.f, TEXTALIGN_CENTER);
2010-12-16 00:52:29 +00:00
// time left
2020-09-22 16:02:03 +00:00
int TimeLeft = maximum(1, m_DownloadSpeed > 0.0f ? static_cast<int>((Client()->MapDownloadTotalsize() - Client()->MapDownloadAmount()) / m_DownloadSpeed) : 1);
2010-12-16 00:52:29 +00:00
if(TimeLeft >= 60)
{
TimeLeft /= 60;
str_format(aBuf, sizeof(aBuf), TimeLeft == 1 ? Localize("%i minute left") : Localize("%i minutes left"), TimeLeft);
2010-12-16 00:52:29 +00:00
}
else
{
str_format(aBuf, sizeof(aBuf), TimeLeft == 1 ? Localize("%i second left") : Localize("%i seconds left"), TimeLeft);
}
2010-12-16 00:52:29 +00:00
Box.HSplitTop(20.f, 0, &Box);
Box.HSplitTop(24.f, &Part, &Box);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&Part, aBuf, 20.f, TEXTALIGN_CENTER);
2010-12-16 00:52:29 +00:00
// progress bar
Box.HSplitTop(20.f, 0, &Box);
Box.HSplitTop(24.f, &Part, &Box);
Part.VMargin(40.0f, &Part);
Part.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, 5.0f);
2020-09-22 16:02:03 +00:00
Part.w = maximum(10.0f, (Part.w * Client()->MapDownloadAmount()) / Client()->MapDownloadTotalsize());
Part.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f), IGraphics::CORNER_ALL, 5.0f);
2010-12-16 00:52:29 +00:00
}
}
else if(m_Popup == POPUP_LANGUAGE)
{
CUIRect Button;
Screen.Margin(150.0f, &Box);
Box.HSplitTop(20.0f, nullptr, &Box);
Box.HSplitBottom(20.0f, &Box, nullptr);
Box.HSplitBottom(24.0f, &Box, &Button);
Box.HSplitBottom(20.0f, &Box, nullptr);
Box.VMargin(20.0f, &Box);
const bool Activated = RenderLanguageSelection(Box);
Button.VMargin(120.0f, &Button);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_Button;
if(DoButton_Menu(&s_Button, Localize("Ok"), 0, &Button) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER) || Activated)
m_Popup = POPUP_FIRST_LAUNCH;
}
else if(m_Popup == POPUP_COUNTRY)
{
CUIRect ButtonBar;
Screen.Margin(150.0f, &Box);
Box.HSplitTop(20.0f, nullptr, &Box);
Box.HSplitBottom(20.0f, &Box, nullptr);
Box.HSplitBottom(24.0f, &Box, &ButtonBar);
Box.HSplitBottom(20.0f, &Box, nullptr);
Box.VMargin(20.0f, &Box);
ButtonBar.VMargin(100.0f, &ButtonBar);
2011-08-11 08:59:14 +00:00
static int s_CurSelection = -2;
if(s_CurSelection == -2)
s_CurSelection = g_Config.m_BrFilterCountryIndex;
static CListBox s_ListBox;
int OldSelected = -1;
s_ListBox.DoStart(50.0f, m_pClient->m_CountryFlags.Num(), 10, 1, OldSelected, &Box);
for(size_t i = 0; i < m_pClient->m_CountryFlags.Num(); ++i)
{
2021-07-12 09:43:56 +00:00
const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_CountryFlags.GetByIndex(i);
if(pEntry->m_CountryCode == s_CurSelection)
OldSelected = i;
const CListboxItem Item = s_ListBox.DoNextItem(pEntry, OldSelected >= 0 && (size_t)OldSelected == i);
if(!Item.m_Visible)
continue;
CUIRect FlagRect, Label;
Item.m_Rect.Margin(5.0f, &FlagRect);
FlagRect.HSplitBottom(12.0f, &FlagRect, &Label);
Label.HSplitTop(2.0f, nullptr, &Label);
const float OldWidth = FlagRect.w;
FlagRect.w = FlagRect.h * 2.0f;
FlagRect.x += (OldWidth - FlagRect.w) / 2.0f;
ColorRGBA Color(1.0f, 1.0f, 1.0f, 1.0f);
m_pClient->m_CountryFlags.Render(pEntry->m_CountryCode, &Color, FlagRect.x, FlagRect.y, FlagRect.w, FlagRect.h);
SLabelProperties ItemLabelProps;
ItemLabelProps.m_AlignVertically = 0;
UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, TEXTALIGN_CENTER, ItemLabelProps);
}
const int NewSelected = s_ListBox.DoEnd();
if(OldSelected != NewSelected)
s_CurSelection = m_pClient->m_CountryFlags.GetByIndex(NewSelected)->m_CountryCode;
CUIRect CancelButton, OkButton;
ButtonBar.VSplitMid(&CancelButton, &OkButton, 40.0f);
static CButtonContainer s_CancelButton;
if(DoButton_Menu(&s_CancelButton, Localize("Cancel"), 0, &CancelButton) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
{
s_CurSelection = g_Config.m_BrFilterCountryIndex;
m_Popup = POPUP_NONE;
}
static CButtonContainer s_OkButton;
if(DoButton_Menu(&s_OkButton, Localize("Ok"), 0, &OkButton) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER) || s_ListBox.WasItemActivated())
{
g_Config.m_BrFilterCountryIndex = s_CurSelection;
Client()->ServerBrowserUpdate();
m_Popup = POPUP_NONE;
}
}
else if(m_Popup == POPUP_RENAME_DEMO)
{
CUIRect Label, TextBox, Ok, Abort;
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VMargin(80.0f, &Part);
Part.VSplitMid(&Abort, &Ok);
Ok.VMargin(20.0f, &Ok);
Abort.VMargin(20.0f, &Abort);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ButtonAbort;
if(DoButton_Menu(&s_ButtonAbort, Localize("Abort"), 0, &Abort) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
m_Popup = POPUP_NONE;
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ButtonOk;
if(DoButton_Menu(&s_ButtonOk, Localize("Ok"), 0, &Ok) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
{
m_Popup = POPUP_NONE;
// rename demo
if(m_DemolistSelectedIndex >= 0 && !m_DemolistSelectedIsDir)
{
char aBufOld[IO_MAX_PATH_LENGTH];
str_format(aBufOld, sizeof(aBufOld), "%s/%s", m_aCurrentDemoFolder, m_vDemos[m_DemolistSelectedIndex].m_aFilename);
char aBufNew[IO_MAX_PATH_LENGTH];
str_format(aBufNew, sizeof(aBufNew), "%s/%s", m_aCurrentDemoFolder, m_aCurrentDemoFile);
if(!str_endswith(aBufNew, ".demo"))
str_append(aBufNew, ".demo", sizeof(aBufNew));
if(Storage()->RenameFile(aBufOld, aBufNew, m_vDemos[m_DemolistSelectedIndex].m_StorageType))
{
DemolistPopulate();
DemolistOnUpdate(false);
}
else
PopupMessage(Localize("Error"), Localize("Unable to rename the demo"), Localize("Ok"));
}
}
Box.HSplitBottom(60.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VSplitLeft(60.0f, 0, &Label);
Label.VSplitLeft(120.0f, 0, &TextBox);
TextBox.VSplitLeft(20.0f, 0, &TextBox);
TextBox.VSplitRight(60.0f, &TextBox, 0);
UI()->DoLabel(&Label, Localize("New name:"), 18.0f, TEXTALIGN_LEFT);
static float s_Offset = 0.0f;
UI()->DoEditBox(&s_Offset, &TextBox, m_aCurrentDemoFile, sizeof(m_aCurrentDemoFile), 12.0f, &s_Offset);
}
2019-09-27 07:22:50 +00:00
#if defined(CONF_VIDEORECORDER)
else if(m_Popup == POPUP_RENDER_DEMO)
{
CUIRect Label, TextBox, Ok, Abort, IncSpeed, DecSpeed, Button;
Box.HSplitBottom(20.f, &Box, &Part);
#if defined(__ANDROID__)
Box.HSplitBottom(60.f, &Box, &Part);
#else
Box.HSplitBottom(24.f, &Box, &Part);
#endif
Part.VMargin(80.0f, &Part);
Part.VSplitMid(&Abort, &Ok);
Ok.VMargin(20.0f, &Ok);
Abort.VMargin(20.0f, &Abort);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ButtonAbort;
if(DoButton_Menu(&s_ButtonAbort, Localize("Abort"), 0, &Abort) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
m_Popup = POPUP_NONE;
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ButtonOk;
if(DoButton_Menu(&s_ButtonOk, Localize("Ok"), 0, &Ok) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
{
m_Popup = POPUP_NONE;
// name video
if(m_DemolistSelectedIndex >= 0 && !m_DemolistSelectedIsDir)
{
if(!str_endswith(m_aCurrentDemoFile, ".mp4"))
str_append(m_aCurrentDemoFile, ".mp4", sizeof(m_aCurrentDemoFile));
char aWholePath[IO_MAX_PATH_LENGTH];
2019-09-27 06:51:08 +00:00
// store new video filename to origin buffer
if(Storage()->FindFile(m_aCurrentDemoFile, "videos", IStorage::TYPE_ALL, aWholePath, sizeof(aWholePath)))
{
char aMessage[128 + IO_MAX_PATH_LENGTH];
str_format(aMessage, sizeof(aMessage), Localize("File '%s' already exists, do you want to overwrite it?"), m_aCurrentDemoFile);
PopupConfirm(Localize("Replace video"), aMessage, Localize("Yes"), Localize("No"), &CMenus::PopupConfirmDemoReplaceVideo, POPUP_NONE, &CMenus::DefaultButtonCallback, POPUP_RENDER_DEMO);
2019-09-27 06:51:08 +00:00
}
else
{
PopupConfirmDemoReplaceVideo();
2019-09-27 06:51:08 +00:00
}
}
}
Box.HSplitBottom(30.f, &Box, 0);
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(10.f, &Box, 0);
float ButtonSize = 20.0f;
Part.VSplitLeft(113.0f, 0, &Part);
2022-03-18 13:31:52 +00:00
Part.VSplitLeft(200.0f, &Button, &Part);
if(DoButton_CheckBox(&g_Config.m_ClVideoShowChat, Localize("Show chat"), g_Config.m_ClVideoShowChat, &Button))
g_Config.m_ClVideoShowChat ^= 1;
2022-03-18 13:31:52 +00:00
Part.VSplitLeft(32.0f, 0, &Part);
if(DoButton_CheckBox(&g_Config.m_ClVideoSndEnable, Localize("Use sounds"), g_Config.m_ClVideoSndEnable, &Part))
g_Config.m_ClVideoSndEnable ^= 1;
2019-09-28 04:18:38 +00:00
Box.HSplitBottom(20.f, &Box, &Part);
Part.VSplitLeft(60.0f, 0, &Part);
Part.VSplitLeft(60.0f, 0, &Label);
Part.VSplitMid(&IncSpeed, &DecSpeed);
IncSpeed.VMargin(20.0f, &IncSpeed);
DecSpeed.VMargin(20.0f, &DecSpeed);
Part.VSplitLeft(20.0f, &Button, &Part);
bool IncDemoSpeed = false, DecDemoSpeed = false;
// slowdown
Part.VSplitLeft(5.0f, 0, &Part);
Part.VSplitLeft(ButtonSize, &Button, &Part);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_SlowDownButton;
2022-08-19 05:05:02 +00:00
if(DoButton_FontIcon(&s_SlowDownButton, "\xEF\x81\x8A", 0, &Button, IGraphics::CORNER_ALL))
2019-09-28 04:18:38 +00:00
DecDemoSpeed = true;
// fastforward
Part.VSplitLeft(5.0f, 0, &Part);
Part.VSplitLeft(ButtonSize, &Button, &Part);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_FastForwardButton;
2022-08-19 05:05:02 +00:00
if(DoButton_FontIcon(&s_FastForwardButton, "\xEF\x81\x8E", 0, &Button, IGraphics::CORNER_ALL))
2019-09-28 04:18:38 +00:00
IncDemoSpeed = true;
// speed meter
Part.VSplitLeft(8.0f, 0, &Part);
2019-09-28 04:18:38 +00:00
char aBuffer[64];
str_format(aBuffer, sizeof(aBuffer), "%s: ×%g", Localize("Speed"), g_aSpeeds[m_Speed]);
UI()->DoLabel(&Part, aBuffer, 12.8f, TEXTALIGN_LEFT);
2019-09-28 04:18:38 +00:00
if(IncDemoSpeed)
m_Speed = clamp(m_Speed + 1, 0, (int)(g_DemoSpeeds - 1));
2019-09-28 04:18:38 +00:00
else if(DecDemoSpeed)
m_Speed = clamp(m_Speed - 1, 0, (int)(g_DemoSpeeds - 1));
2019-09-28 04:18:38 +00:00
2022-03-18 13:31:52 +00:00
Part.VSplitLeft(207.0f, 0, &Part);
if(DoButton_CheckBox(&g_Config.m_ClVideoShowhud, Localize("Show ingame HUD"), g_Config.m_ClVideoShowhud, &Part))
g_Config.m_ClVideoShowhud ^= 1;
2019-09-28 04:18:38 +00:00
Box.HSplitBottom(20.f, &Box, &Part);
#if defined(__ANDROID__)
Box.HSplitBottom(60.f, &Box, &Part);
#else
Box.HSplitBottom(24.f, &Box, &Part);
#endif
Part.VSplitLeft(60.0f, 0, &Label);
Label.VSplitLeft(120.0f, 0, &TextBox);
TextBox.VSplitLeft(20.0f, 0, &TextBox);
TextBox.VSplitRight(60.0f, &TextBox, 0);
UI()->DoLabel(&Label, Localize("Video name:"), 18.0f, TEXTALIGN_LEFT);
static float s_Offset = 0.0f;
UI()->DoEditBox(&s_Offset, &TextBox, m_aCurrentDemoFile, sizeof(m_aCurrentDemoFile), 12.0f, &s_Offset);
}
2019-09-27 07:22:50 +00:00
#endif
2010-05-29 07:25:38 +00:00
else if(m_Popup == POPUP_FIRST_LAUNCH)
{
CUIRect Label, TextBox, Skip, Join;
2010-05-29 07:25:38 +00:00
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VMargin(80.0f, &Part);
Part.VSplitMid(&Skip, &Join);
Skip.VMargin(20.0f, &Skip);
Join.VMargin(20.0f, &Join);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_JoinTutorialButton;
if(DoButton_Menu(&s_JoinTutorialButton, Localize("Join Tutorial Server"), 0, &Join) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
{
m_JoinTutorial = true;
Client()->RequestDDNetInfo();
m_Popup = g_Config.m_BrIndicateFinished ? POPUP_POINTS : POPUP_NONE;
}
2022-07-16 13:32:06 +00:00
static CButtonContainer s_SkipTutorialButton;
if(DoButton_Menu(&s_SkipTutorialButton, Localize("Skip Tutorial"), 0, &Skip) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
{
m_JoinTutorial = false;
Client()->RequestDDNetInfo();
m_Popup = g_Config.m_BrIndicateFinished ? POPUP_POINTS : POPUP_NONE;
}
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VSplitLeft(30.0f, 0, &Part);
str_format(aBuf, sizeof(aBuf), "%s\n(%s)",
Localize("Show DDNet map finishes in server browser"),
2022-08-30 08:09:06 +00:00
Localize("transmits your player name to info.ddnet.org"));
if(DoButton_CheckBox(&g_Config.m_BrIndicateFinished, aBuf, g_Config.m_BrIndicateFinished, &Part))
g_Config.m_BrIndicateFinished ^= 1;
Box.HSplitBottom(20.f, &Box, &Part);
2010-05-29 07:25:38 +00:00
Box.HSplitBottom(24.f, &Box, &Part);
2010-05-29 07:25:38 +00:00
Part.VSplitLeft(60.0f, 0, &Label);
Label.VSplitLeft(100.0f, 0, &TextBox);
TextBox.VSplitLeft(20.0f, 0, &TextBox);
TextBox.VSplitRight(60.0f, &TextBox, 0);
UI()->DoLabel(&Label, Localize("Nickname"), 16.0f, TEXTALIGN_LEFT);
static float s_Offset = 0.0f;
2022-03-13 18:09:33 +00:00
SUIExEditBoxProperties EditProps;
EditProps.m_pEmptyText = Client()->PlayerName();
UI()->DoEditBox(&g_Config.m_PlayerName, &TextBox, g_Config.m_PlayerName, sizeof(g_Config.m_PlayerName), 12.0f, &s_Offset, false, IGraphics::CORNER_ALL, EditProps);
}
else if(m_Popup == POPUP_POINTS)
{
CUIRect Yes, No;
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VMargin(80.0f, &Part);
Part.VSplitMid(&No, &Yes);
Yes.VMargin(20.0f, &Yes);
No.VMargin(20.0f, &No);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ButtonNo;
if(DoButton_Menu(&s_ButtonNo, Localize("No"), 0, &No) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
m_Popup = POPUP_FIRST_LAUNCH;
2022-07-16 13:32:06 +00:00
static CButtonContainer s_ButtonYes;
if(DoButton_Menu(&s_ButtonYes, Localize("Yes"), 0, &Yes) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
m_Popup = POPUP_NONE;
}
else if(m_Popup == POPUP_WARNING)
{
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VMargin(120.0f, &Part);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_Button;
if(DoButton_Menu(&s_Button, pButtonText, 0, &Part) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER) || (time_get_nanoseconds() - m_PopupWarningLastTime >= m_PopupWarningDuration))
{
m_Popup = POPUP_NONE;
SetActive(false);
}
}
else
{
2010-05-29 07:25:38 +00:00
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VMargin(120.0f, &Part);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_Button;
if(DoButton_Menu(&s_Button, pButtonText, 0, &Part) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
2016-05-07 14:12:23 +00:00
{
if(m_Popup == POPUP_DISCONNECTED && Client()->m_ReconnectTime > 0)
Client()->m_ReconnectTime = 0;
2010-05-29 07:25:38 +00:00
m_Popup = POPUP_NONE;
2016-05-07 14:12:23 +00:00
}
}
2013-03-16 14:30:40 +00:00
if(m_Popup == POPUP_NONE)
UI()->SetActiveItem(nullptr);
}
return 0;
}
#if defined(CONF_VIDEORECORDER)
void CMenus::PopupConfirmDemoReplaceVideo()
{
char aBuf[IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "%s/%s", m_aCurrentDemoFolder, m_vDemos[m_DemolistSelectedIndex].m_aFilename);
const char *pError = Client()->DemoPlayer_Render(aBuf, m_vDemos[m_DemolistSelectedIndex].m_StorageType, m_aCurrentDemoFile, m_Speed);
m_Speed = 4;
if(pError)
PopupMessage(Localize("Error"), str_comp(pError, "error loading demo") ? pError : Localize("Error loading demo"), Localize("Ok"));
}
#endif
void CMenus::RenderThemeSelection(CUIRect MainView)
2020-09-18 16:45:42 +00:00
{
const std::vector<CTheme> &vThemes = m_pBackground->GetThemes();
2020-09-18 16:45:42 +00:00
int SelectedTheme = -1;
for(int i = 0; i < (int)vThemes.size(); i++)
2020-09-18 16:45:42 +00:00
{
if(str_comp(vThemes[i].m_Name.c_str(), g_Config.m_ClMenuMap) == 0)
2020-09-18 16:45:42 +00:00
{
SelectedTheme = i;
break;
}
}
const int OldSelected = SelectedTheme;
2020-09-18 16:45:42 +00:00
static CListBox s_ListBox;
s_ListBox.DoHeader(&MainView, Localize("Theme"), 20.0f);
s_ListBox.DoStart(20.0f, vThemes.size(), 1, 3, SelectedTheme, nullptr, true);
2020-09-18 16:45:42 +00:00
for(int i = 0; i < (int)vThemes.size(); i++)
2020-09-18 16:45:42 +00:00
{
const CTheme &Theme = vThemes[i];
const CListboxItem Item = s_ListBox.DoNextItem(&Theme.m_Name, i == SelectedTheme);
2020-09-18 16:45:42 +00:00
if(!Item.m_Visible)
continue;
CUIRect Icon, Label;
Item.m_Rect.VSplitLeft(Item.m_Rect.h * 2.0f, &Icon, &Label);
2020-09-18 16:45:42 +00:00
// draw icon if it exists
if(Theme.m_IconTexture.IsValid())
2020-09-18 16:45:42 +00:00
{
Icon.VMargin(6.0f, &Icon);
Icon.HMargin(3.0f, &Icon);
Graphics()->TextureSet(Theme.m_IconTexture);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
IGraphics::CQuadItem QuadItem(Icon.x, Icon.y, Icon.w, Icon.h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
}
char aName[128];
if(Theme.m_Name.empty())
2022-07-09 16:14:56 +00:00
str_copy(aName, "(none)");
else if(str_comp(Theme.m_Name.c_str(), "auto") == 0)
2022-07-09 16:14:56 +00:00
str_copy(aName, "(seasons)");
else if(str_comp(Theme.m_Name.c_str(), "rand") == 0)
2022-07-09 16:14:56 +00:00
str_copy(aName, "(random)");
2020-09-18 16:45:42 +00:00
else if(Theme.m_HasDay && Theme.m_HasNight)
str_copy(aName, Theme.m_Name.c_str());
2020-09-18 16:45:42 +00:00
else if(Theme.m_HasDay && !Theme.m_HasNight)
str_format(aName, sizeof(aName), "%s (day)", Theme.m_Name.c_str());
2020-09-18 16:45:42 +00:00
else if(!Theme.m_HasDay && Theme.m_HasNight)
str_format(aName, sizeof(aName), "%s (night)", Theme.m_Name.c_str());
2020-09-18 16:45:42 +00:00
else // generic
str_copy(aName, Theme.m_Name.c_str());
2020-09-18 16:45:42 +00:00
SLabelProperties Props;
Props.m_AlignVertically = 0;
UI()->DoLabel(&Label, aName, 16.0f * CUI::ms_FontmodHeight, TEXTALIGN_LEFT, Props);
2020-09-18 16:45:42 +00:00
}
SelectedTheme = s_ListBox.DoEnd();
2020-09-18 16:45:42 +00:00
if(OldSelected != SelectedTheme)
2020-09-18 16:45:42 +00:00
{
const CTheme &Theme = vThemes[SelectedTheme];
str_copy(g_Config.m_ClMenuMap, Theme.m_Name.c_str());
m_pBackground->LoadMenuBackground(Theme.m_HasDay, Theme.m_HasNight);
2020-09-18 16:45:42 +00:00
}
}
2010-05-29 07:25:38 +00:00
void CMenus::SetActive(bool Active)
{
if(Active != m_MenuActive)
2020-12-16 04:55:41 +00:00
{
ms_ColorPicker.m_Active = false;
Input()->SetIMEState(Active);
2022-07-11 16:52:51 +00:00
UI()->SetHotItem(nullptr);
UI()->SetActiveItem(nullptr);
2020-12-16 04:55:41 +00:00
}
2010-05-29 07:25:38 +00:00
m_MenuActive = Active;
if(!m_MenuActive)
{
if(m_NeedSendinfo)
{
m_pClient->SendInfo(false);
m_NeedSendinfo = false;
}
2014-04-28 13:19:57 +00:00
if(m_NeedSendDummyinfo)
{
m_pClient->SendDummyInfo(false);
m_NeedSendDummyinfo = false;
}
if(Client()->State() == IClient::STATE_ONLINE)
{
m_pClient->OnRelease();
}
}
else if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
{
m_pClient->OnRelease();
}
}
2010-05-29 07:25:38 +00:00
void CMenus::OnReset()
{
}
2022-01-20 11:19:06 +00:00
void CMenus::OnShutdown()
{
KillServer();
}
bool CMenus::OnCursorMove(float x, float y, IInput::ECursorType CursorType)
{
2010-05-29 07:25:38 +00:00
if(!m_MenuActive)
return false;
UI()->ConvertMouseMove(&x, &y, CursorType);
2021-11-26 20:55:31 +00:00
m_MousePos.x = clamp(m_MousePos.x + x, 0.f, (float)Graphics()->WindowWidth());
m_MousePos.y = clamp(m_MousePos.y + y, 0.f, (float)Graphics()->WindowHeight());
return true;
}
bool CMenus::OnInput(IInput::CEvent Event)
{
2008-10-21 18:25:28 +00:00
// special handle esc and enter for popup purposes
if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_ESCAPE)
2008-08-27 20:23:50 +00:00
{
SetActive(!IsActive());
UI()->OnInput(Event);
return true;
2008-10-21 18:25:28 +00:00
}
2010-05-29 07:25:38 +00:00
if(IsActive())
2008-10-21 18:25:28 +00:00
{
2010-05-29 07:25:38 +00:00
if(m_NumInputEvents < MAX_INPUTEVENTS)
m_aInputEvents[m_NumInputEvents++] = Event;
UI()->OnInput(Event);
2008-08-27 20:23:50 +00:00
return true;
}
return false;
}
2010-05-29 07:25:38 +00:00
void CMenus::OnStateChange(int NewState, int OldState)
{
2010-05-29 07:25:38 +00:00
// reset active item
UI()->SetActiveItem(nullptr);
2010-05-29 07:25:38 +00:00
if(OldState == IClient::STATE_ONLINE || OldState == IClient::STATE_OFFLINE)
TextRender()->DeleteTextContainer(m_MotdTextContainerIndex);
2010-05-29 07:25:38 +00:00
if(NewState == IClient::STATE_OFFLINE)
{
2020-10-02 13:44:27 +00:00
if(OldState >= IClient::STATE_ONLINE && NewState < IClient::STATE_QUITTING)
UpdateMusicState();
2010-05-29 07:25:38 +00:00
m_Popup = POPUP_NONE;
if(Client()->ErrorString() && Client()->ErrorString()[0] != 0)
{
2010-05-29 07:25:38 +00:00
if(str_find(Client()->ErrorString(), "password"))
{
2010-05-29 07:25:38 +00:00
m_Popup = POPUP_PASSWORD;
UI()->SetHotItem(&g_Config.m_Password);
UI()->SetActiveItem(&g_Config.m_Password);
}
else
2010-05-29 07:25:38 +00:00
m_Popup = POPUP_DISCONNECTED;
}
}
else if(NewState == IClient::STATE_LOADING)
{
2010-05-29 07:25:38 +00:00
m_Popup = POPUP_CONNECTING;
2010-12-16 00:52:29 +00:00
m_DownloadLastCheckTime = time_get();
m_DownloadLastCheckSize = 0;
m_DownloadSpeed = 0.0f;
}
2010-05-29 07:25:38 +00:00
else if(NewState == IClient::STATE_CONNECTING)
{
2010-05-29 07:25:38 +00:00
m_Popup = POPUP_CONNECTING;
}
else if(NewState == IClient::STATE_ONLINE || NewState == IClient::STATE_DEMOPLAYBACK)
{
if(m_Popup != POPUP_WARNING)
{
m_Popup = POPUP_NONE;
SetActive(false);
}
}
}
void CMenus::OnWindowResize()
{
TextRender()->DeleteTextContainer(m_MotdTextContainerIndex);
}
2010-05-29 07:25:38 +00:00
void CMenus::OnRender()
{
UI()->StartCheck();
2010-05-29 07:25:38 +00:00
if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK)
SetActive(true);
2010-05-29 07:25:38 +00:00
if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
{
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
RenderDemoPlayer(*UI()->Screen());
}
if(Client()->State() == IClient::STATE_ONLINE && m_pClient->m_ServerMode == m_pClient->SERVERMODE_PUREMOD)
{
2010-05-29 07:25:38 +00:00
Client()->Disconnect();
SetActive(true);
PopupMessage(Localize("Disconnected"), Localize("The server is running a non-standard tuning on a pure game type."), Localize("Ok"));
}
2010-05-29 07:25:38 +00:00
if(!IsActive())
{
UI()->FinishCheck();
UI()->ClearHotkeys();
m_NumInputEvents = 0;
return;
}
// update colors
ms_GuiColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_UiColor, true));
2010-05-29 07:25:38 +00:00
ms_ColorTabbarInactiveOutgame = ColorRGBA(0, 0, 0, 0.25f);
ms_ColorTabbarActiveOutgame = ColorRGBA(0, 0, 0, 0.5f);
ms_ColorTabbarHoverOutgame = ColorRGBA(1, 1, 1, 0.25f);
2010-05-29 07:25:38 +00:00
float ColorIngameScaleI = 0.5f;
float ColorIngameAcaleA = 0.2f;
ms_ColorTabbarInactiveIngame = ColorRGBA(
ms_GuiColor.r * ColorIngameScaleI,
ms_GuiColor.g * ColorIngameScaleI,
ms_GuiColor.b * ColorIngameScaleI,
ms_GuiColor.a * 0.8f);
ms_ColorTabbarActiveIngame = ColorRGBA(
2020-09-22 16:02:03 +00:00
ms_GuiColor.r * ColorIngameAcaleA,
ms_GuiColor.g * ColorIngameAcaleA,
ms_GuiColor.b * ColorIngameAcaleA,
2010-05-29 07:25:38 +00:00
ms_GuiColor.a);
ms_ColorTabbarHoverIngame = ColorRGBA(1, 1, 1, 0.75f);
// update the ui
2010-05-29 07:25:38 +00:00
CUIRect *pScreen = UI()->Screen();
float mx = (m_MousePos.x / (float)Graphics()->WindowWidth()) * pScreen->w;
float my = (m_MousePos.y / (float)Graphics()->WindowHeight()) * pScreen->h;
UI()->Update(mx, my, mx * 3.0f, my * 3.0f);
Render();
2022-05-27 17:30:20 +00:00
RenderTools()->RenderCursor(vec2(mx, my), 24.0f);
2009-01-10 11:16:21 +00:00
// render debug information
2010-05-29 07:25:38 +00:00
if(g_Config.m_Debug)
{
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
2010-05-29 07:25:38 +00:00
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "%p %p %p", UI()->HotItem(), UI()->ActiveItem(), UI()->LastActiveItem());
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, 10, 10, 10, TEXTFLAG_RENDER);
TextRender()->TextEx(&Cursor, aBuf, -1);
}
UI()->FinishCheck();
UI()->ClearHotkeys();
2010-05-29 07:25:38 +00:00
m_NumInputEvents = 0;
}
2008-09-11 22:45:28 +00:00
2010-05-29 07:25:38 +00:00
void CMenus::RenderBackground()
2008-09-11 22:45:28 +00:00
{
2020-08-31 14:18:45 +00:00
Graphics()->BlendNormal();
2020-09-22 16:02:03 +00:00
float sw = 300 * Graphics()->ScreenAspect();
float sh = 300;
2009-10-27 14:38:53 +00:00
Graphics()->MapScreen(0, 0, sw, sh);
// render background color
Graphics()->TextureClear();
2009-10-27 14:38:53 +00:00
Graphics()->QuadsBegin();
2020-09-22 16:02:03 +00:00
ColorRGBA Bottom(ms_GuiColor.r, ms_GuiColor.g, ms_GuiColor.b, 1.0f);
ColorRGBA Top(ms_GuiColor.r, ms_GuiColor.g, ms_GuiColor.b, 1.0f);
IGraphics::CColorVertex Array[4] = {
IGraphics::CColorVertex(0, Top.r, Top.g, Top.b, Top.a),
IGraphics::CColorVertex(1, Top.r, Top.g, Top.b, Top.a),
IGraphics::CColorVertex(2, Bottom.r, Bottom.g, Bottom.b, Bottom.a),
IGraphics::CColorVertex(3, Bottom.r, Bottom.g, Bottom.b, Bottom.a)};
Graphics()->SetColorVertex(Array, 4);
IGraphics::CQuadItem QuadItem(0, 0, sw, sh);
Graphics()->QuadsDrawTL(&QuadItem, 1);
2009-10-27 14:38:53 +00:00
Graphics()->QuadsEnd();
// render the tiles
Graphics()->TextureClear();
2009-10-27 14:38:53 +00:00
Graphics()->QuadsBegin();
2020-09-22 16:02:03 +00:00
float Size = 15.0f;
float OffsetTime = std::fmod(LocalTime() * 0.15f, 2.0f);
2020-09-22 16:02:03 +00:00
for(int y = -2; y < (int)(sw / Size); y++)
for(int x = -2; x < (int)(sh / Size); x++)
{
Graphics()->SetColor(0, 0, 0, 0.045f);
QuadItem = IGraphics::CQuadItem((x - OffsetTime) * Size * 2 + (y & 1) * Size, (y + OffsetTime) * Size, Size, Size);
2020-09-22 16:02:03 +00:00
Graphics()->QuadsDrawTL(&QuadItem, 1);
}
2009-10-27 14:38:53 +00:00
Graphics()->QuadsEnd();
// render border fade
Graphics()->TextureSet(m_TextureBlob);
2009-10-27 14:38:53 +00:00
Graphics()->QuadsBegin();
2020-09-22 16:02:03 +00:00
Graphics()->SetColor(1, 1, 1, 1);
QuadItem = IGraphics::CQuadItem(-100, -100, sw + 200, sh + 200);
Graphics()->QuadsDrawTL(&QuadItem, 1);
2009-10-27 14:38:53 +00:00
Graphics()->QuadsEnd();
// restore screen
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
2008-09-11 22:45:28 +00:00
}
bool CMenus::CheckHotKey(int Key) const
{
return m_Popup == POPUP_NONE &&
!Input()->ShiftIsPressed() && !Input()->ModifierIsPressed() && // no modifier
2021-07-12 09:43:56 +00:00
Input()->KeyIsPressed(Key) && m_pClient->m_GameConsole.IsClosed();
}
int CMenus::DoButton_CheckBox_Tristate(const void *pID, const char *pText, TRISTATE Checked, const CUIRect *pRect)
{
switch(Checked)
{
case TRISTATE::NONE:
return DoButton_CheckBox_Common(pID, pText, "", pRect);
case TRISTATE::SOME:
return DoButton_CheckBox_Common(pID, pText, "O", pRect);
case TRISTATE::ALL:
return DoButton_CheckBox_Common(pID, pText, "X", pRect);
default:
dbg_assert(false, "invalid tristate");
}
dbg_break();
}
int CMenus::MenuImageScan(const char *pName, int IsDir, int DirType, void *pUser)
{
CMenus *pSelf = (CMenus *)pUser;
if(IsDir || !str_endswith(pName, ".png"))
return 0;
char aBuf[IO_MAX_PATH_LENGTH];
bool ImgExists = false;
for(const auto &Img : pSelf->m_vMenuImages)
{
str_format(aBuf, std::size(aBuf), "%s.png", Img.m_aName);
if(str_comp(aBuf, pName) == 0)
{
ImgExists = true;
break;
}
}
if(!ImgExists)
{
str_format(aBuf, sizeof(aBuf), "menuimages/%s", pName);
CImageInfo Info;
if(!pSelf->Graphics()->LoadPNG(&Info, aBuf, DirType))
{
str_format(aBuf, sizeof(aBuf), "failed to load menu image from %s", pName);
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf);
return 0;
}
CMenuImage MenuImage;
MenuImage.m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0);
unsigned char *pData = (unsigned char *)Info.m_pData;
//int Pitch = Info.m_Width*4;
// create colorless version
int Step = Info.m_Format == CImageInfo::FORMAT_RGBA ? 4 : 3;
// make the texture gray scale
for(int i = 0; i < Info.m_Width * Info.m_Height; i++)
{
int v = (pData[i * Step] + pData[i * Step + 1] + pData[i * Step + 2]) / 3;
pData[i * Step] = v;
pData[i * Step + 1] = v;
pData[i * Step + 2] = v;
}
MenuImage.m_GreyTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0);
pSelf->Graphics()->FreePNG(&Info);
// set menu image data
str_truncate(MenuImage.m_aName, sizeof(MenuImage.m_aName), pName, str_length(pName) - 4);
pSelf->m_vMenuImages.push_back(MenuImage);
pSelf->RenderLoading(Localize("Loading DDNet Client"), Localize("Loading menu images"), 1);
}
return 0;
}
const CMenus::CMenuImage *CMenus::FindMenuImage(const char *pName)
{
for(auto &Image : m_vMenuImages)
if(str_comp(Image.m_aName, pName) == 0)
return &Image;
return nullptr;
}
void CMenus::SetMenuPage(int NewPage)
{
m_MenuPage = NewPage;
if(NewPage >= PAGE_INTERNET && NewPage <= PAGE_KOG)
g_Config.m_UiPage = NewPage;
}
void CMenus::RefreshBrowserTab(int UiPage)
{
if(UiPage == PAGE_INTERNET)
2022-09-23 22:56:43 +00:00
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
else if(UiPage == PAGE_LAN)
ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN);
else if(UiPage == PAGE_FAVORITES)
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
else if(UiPage == PAGE_DDNET)
{
// start a new server list request
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_DDNET);
}
else if(UiPage == PAGE_KOG)
{
// start a new server list request
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_KOG);
}
}