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

2827 lines
86 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>
#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>
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_EscapePressed = false;
m_EnterPressed = false;
m_DeletePressed = false;
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-06-16 11:29:18 +00:00
m_DoubleClickIndex = -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;
m_ServerProcess.Initialized = false;
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;
}
int CMenus::DoButton_Menu(const void *pID, 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)
2021-11-26 20:55:31 +00:00
Color.a *= UI()->ButtonColorMul(pID);
RenderTools()->DrawUIRect(pRect, 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)
{
Graphics()->TextureSet(UI()->HotItem() == pID ? 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;
return UI()->DoButtonLogic(pID, Checked, pRect);
}
2010-05-29 07:25:38 +00:00
void CMenus::DoButton_KeySelect(const void *pID, const char *pText, int Checked, const CUIRect *pRect)
{
2021-11-26 20:55:31 +00:00
RenderTools()->DrawUIRect(pRect, ColorRGBA(1, 1, 1, 0.5f * UI()->ButtonColorMul(pID)), CUI::CORNER_ALL, 5.0f);
CUIRect Temp;
pRect->HMargin(1.0f, &Temp);
UI()->DoLabel(&Temp, pText, Temp.h * CUI::ms_FontmodHeight, TEXTALIGN_CENTER);
}
2020-10-26 01:10:55 +00:00
int CMenus::DoButton_MenuTab(const void *pID, 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
RenderTools()->DrawUIRect(&Rect, 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
RenderTools()->DrawUIRect(&Rect, HoverColorMenuTab, Corners, EdgeRounding);
}
else
{
ColorRGBA ColorMenuTab = ms_ColorTabbarInactive;
if(pDefaultColor)
ColorMenuTab = *pDefaultColor;
2020-10-26 01:10:55 +00:00
RenderTools()->DrawUIRect(&Rect, 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);
return UI()->DoButtonLogic(pID, 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)
2020-09-22 16:02:03 +00:00
RenderTools()->DrawUIRect(pRect, ColorRGBA(1, 0.98f, 0.5f, 0.55f), CUI::CORNER_T, 5.0f);
else if(Checked)
2020-09-22 16:02:03 +00:00
RenderTools()->DrawUIRect(pRect, ColorRGBA(1, 1, 1, 0.5f), CUI::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);
2021-11-26 20:55:31 +00:00
RenderTools()->DrawUIRect(&c, ColorRGBA(1, 1, 1, 0.25f * UI()->ButtonColorMul(pID)), CUI::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)
{
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();
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteWeaponLaser);
Graphics()->QuadsBegin();
RenderTools()->SelectSprite(SPRITE_WEAPON_LASER_BODY);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->DrawSprite(Section.x + 30.0f, Section.y + Section.h / 2.0f, 60.0f);
Graphics()->QuadsEnd();
}
ColorHSLA CMenus::DoLine_ColorPicker(int *pResetID, const float LineSize, const float WantedPickerPosition, const float LabelSize, const float BottomMargin, CUIRect *pMainRect, const char *pText, unsigned int *pColorValue, const ColorRGBA DefaultColor, bool CheckBoxSpacing, bool UseCheckBox, int *pCheckBoxValue)
{
CUIRect Section, Button, Label;
pMainRect->HSplitTop(LineSize, &Section, pMainRect);
pMainRect->HSplitTop(BottomMargin, 0x0, pMainRect);
float SectionWidth = Section.w;
if(CheckBoxSpacing || UseCheckBox)
Section.VSplitLeft(20.0f, &Button, &Section);
if(UseCheckBox)
{
CUIRect CheckBox;
Button.Margin(2.0f, &CheckBox);
if(DoButton_CheckBox(pCheckBoxValue, "", *pCheckBoxValue, &CheckBox))
*pCheckBoxValue ^= 1;
}
Section.VSplitLeft(5.0f, 0x0, &Section);
float LabelWidth = TextRender()->TextWidth(0, 14.0f, pText, -1, -1.0f);
Section.VSplitLeft(LabelWidth, &Label, &Section);
UI()->DoLabel(&Label, pText, LabelSize, TEXTALIGN_LEFT);
float Cut = WantedPickerPosition - (SectionWidth - Section.w);
if(Cut < 5)
Cut = 5.0f;
Section.VSplitLeft(Cut, 0x0, &Section);
Section.VSplitLeft(LineSize, &Button, &Section);
ColorHSLA PickedColor = RenderHSLColorPicker(&Button, pColorValue, false);
Section.VSplitLeft(7.5f, 0x0, &Section);
Section.VSplitLeft(55.0f, &Button, &Section);
Button.HSplitTop(2.0f, 0x0, &Button);
Button.HSplitBottom(2.0f, &Button, 0x0);
if(DoButton_Menu(pResetID, Localize("Reset"), 0, &Button, 0, CUI::CORNER_ALL, 8.0f, 0, vec4(1, 1, 1, 0.5f), vec4(1, 1, 1, 0.25f), 1, true))
{
ColorHSLA HSL = color_cast<ColorHSLA>(DefaultColor);
*pColorValue = HSL.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;
UIEx()->DoEditBox(&s_NumberBoxID, pRect, s_aNumStr, sizeof(s_aNumStr), 10.0f, &s_NumberBoxID, false, CUI::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()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT))
s_Value += delta * 0.05f;
else
s_Value += delta;
if(absolute(s_Value) > Scale)
{
int Count = (int)(s_Value / Scale);
s_Value = fmod(s_Value, Scale);
Current += Step * Count;
Current = clamp(Current, Min, Max);
// Constrain to discrete steps
if(Count > 0)
Current = Current / Step * Step;
else
Current = round_ceil(Current / (float)Step) * Step;
}
}
}
}
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);
}
RenderTools()->DrawUIRect(pRect, *pColor, CUI::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)
2009-10-27 14:38:53 +00:00
DoButton_KeySelect(pID, "???", 0, pRect);
else
{
if(Key)
{
char aBuf[64];
if(*pNewModifierCombination)
str_format(aBuf, sizeof(aBuf), "%s%s", CBinds::GetKeyBindModifiersName(*pNewModifierCombination), Input()->KeyName(Key));
else
str_format(aBuf, sizeof(aBuf), "%s", Input()->KeyName(Key));
DoButton_KeySelect(pID, aBuf, 0, pRect);
}
else
DoButton_KeySelect(pID, "", 0, 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);
2020-09-10 18:14:47 +00:00
static int s_StartButton = 0;
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);
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;
2022-03-19 17:50:33 +00:00
const char *pHomeScreenButtonLabel = "\xEF\x80\x95";
if(GotNewsOrUpdate)
{
2022-03-19 17:50:33 +00:00
pHomeScreenButtonLabel = "\xEF\x87\xAA";
pHomeButtonColor = &HomeButtonColorAlert;
pHomeButtonColorHover = &HomeButtonColorAlertHover;
}
if(DoButton_MenuTab(&s_StartButton, pHomeScreenButtonLabel, false, &Button, CUI::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_HOME], pHomeButtonColor, pHomeButtonColor, pHomeButtonColorHover, 10.0f, 0))
2014-06-16 11:29:18 +00:00
{
2020-09-05 21:43:39 +00:00
m_ShowStart = true;
m_DoubleClickIndex = -1;
}
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);
2020-09-10 18:14:47 +00:00
static int s_NewsButton = 0;
if(DoButton_MenuTab(&s_NewsButton, Localize("News"), m_ActivePage == PAGE_NEWS, &Button, CUI::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_NEWS]))
2020-09-05 21:43:39 +00:00
{
NewPage = PAGE_NEWS;
m_DoubleClickIndex = -1;
}
}
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);
2020-09-10 18:14:47 +00:00
static int s_DemosButton = 0;
if(DoButton_MenuTab(&s_DemosButton, Localize("Demos"), m_ActivePage == PAGE_DEMOS, &Button, CUI::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_DEMOS]))
2020-09-05 21:43:39 +00:00
{
DemolistPopulate();
NewPage = PAGE_DEMOS;
m_DoubleClickIndex = -1;
}
}
2020-09-05 21:43:39 +00:00
else
{
2020-09-05 21:43:39 +00:00
Box.VSplitLeft(100.0f, &Button, &Box);
2020-09-10 18:14:47 +00:00
static int s_InternetButton = 0;
if(DoButton_MenuTab(&s_InternetButton, Localize("Internet"), m_ActivePage == PAGE_INTERNET, &Button, CUI::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;
m_DoubleClickIndex = -1;
}
2020-09-05 21:43:39 +00:00
Box.VSplitLeft(100.0f, &Button, &Box);
2020-09-10 18:14:47 +00:00
static int s_LanButton = 0;
if(DoButton_MenuTab(&s_LanButton, Localize("LAN"), m_ActivePage == PAGE_LAN, &Button, CUI::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;
m_DoubleClickIndex = -1;
}
2014-09-13 14:36:25 +00:00
2020-09-05 21:43:39 +00:00
Box.VSplitLeft(100.0f, &Button, &Box);
2020-09-10 18:14:47 +00:00
static int s_FavoritesButton = 0;
if(DoButton_MenuTab(&s_FavoritesButton, Localize("Favorites"), m_ActivePage == PAGE_FAVORITES, &Button, CUI::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;
m_DoubleClickIndex = -1;
}
Box.VSplitLeft(90.0f, &Button, &Box);
2020-09-10 18:14:47 +00:00
static int s_DDNetButton = 0;
if(DoButton_MenuTab(&s_DDNetButton, "DDNet", m_ActivePage == PAGE_DDNET, &Button, CUI::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;
m_DoubleClickIndex = -1;
}
Box.VSplitLeft(90.0f, &Button, &Box);
2020-09-10 18:14:47 +00:00
static int s_KoGButton = 0;
if(DoButton_MenuTab(&s_KoGButton, "KoG", m_ActivePage == PAGE_KOG, &Button, CUI::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;
m_DoubleClickIndex = -1;
}
}
}
else
{
2010-05-29 07:25:38 +00:00
// online menus
Box.VSplitLeft(90.0f, &Button, &Box);
2020-09-22 16:02:03 +00:00
static int s_GameButton = 0;
if(DoButton_MenuTab(&s_GameButton, Localize("Game"), m_ActivePage == PAGE_GAME, &Button, CUI::CORNER_TL))
2010-05-29 07:25:38 +00:00
NewPage = PAGE_GAME;
Box.VSplitLeft(90.0f, &Button, &Box);
2020-09-22 16:02:03 +00:00
static int s_PlayersButton = 0;
if(DoButton_MenuTab(&s_PlayersButton, Localize("Players"), m_ActivePage == PAGE_PLAYERS, &Button, 0))
NewPage = PAGE_PLAYERS;
Box.VSplitLeft(130.0f, &Button, &Box);
2020-09-22 16:02:03 +00:00
static int s_ServerInfoButton = 0;
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);
2020-09-22 16:02:03 +00:00
static int s_NetworkButton = 0;
if(DoButton_MenuTab(&s_NetworkButton, Localize("Browser"), m_ActivePage == PAGE_NETWORK, &Button, 0))
NewPage = PAGE_NETWORK;
{
2019-07-18 15:56:01 +00:00
static int s_GhostButton = 0;
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);
2020-09-22 16:02:03 +00:00
static int s_CallVoteButton = 0;
if(DoButton_MenuTab(&s_CallVoteButton, Localize("Call vote"), m_ActivePage == PAGE_CALLVOTE, &Button, CUI::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);
static int s_QuitButton = 0;
ColorRGBA QuitColor(1, 0, 0, 0.5f);
2022-03-19 17:50:33 +00:00
if(DoButton_MenuTab(&s_QuitButton, "\xEF\x80\x91", 0, &Button, CUI::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);
2020-09-22 16:02:03 +00:00
static int s_SettingsButton = 0;
2022-03-19 17:50:33 +00:00
if(DoButton_MenuTab(&s_SettingsButton, "\xEF\x80\x93", m_ActivePage == PAGE_SETTINGS, &Button, CUI::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);
2020-09-22 16:02:03 +00:00
static int s_EditorButton = 0;
2022-03-19 17:50:33 +00:00
if(DoButton_MenuTab(&s_EditorButton, "\xEF\x81\x84", 0, &Button, CUI::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);
static int s_DemoButton = 0;
2022-03-19 17:50:33 +00:00
if(DoButton_MenuTab(&s_DemoButton, "\xEE\x84\xB1", m_ActivePage == PAGE_DEMOS, &Button, CUI::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);
static int s_ServerButton = 0;
2022-03-19 17:50:33 +00:00
if(DoButton_MenuTab(&s_ServerButton, "\xEF\x95\xBD", m_ActivePage == g_Config.m_UiPage, &Button, CUI::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();
RenderTools()->DrawUIRect(&Box, ColorRGBA{0, 0, 0, 0.50f}, CUI::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()->TextureClear();
Graphics()->QuadsBegin();
Graphics()->SetColor(1, 1, 1, 0.75f);
RenderTools()->DrawRoundRect(Box.x + 40, Box.y + Box.h - 75, (Box.w - 80) * Percent, 25, 5.0f);
Graphics()->QuadsEnd();
}
Client()->UpdateAndSwap();
}
2010-05-29 07:25:38 +00:00
void CMenus::RenderNews(CUIRect MainView)
{
g_Config.m_UiUnreadNews = false;
RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::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;
2021-12-03 18:54:22 +00:00
UIEx()->Init(UI(), Kernel(), RenderTools(), 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);
}
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);
}
2010-05-29 07:25:38 +00:00
void CMenus::PopupMessage(const char *pTopic, const char *pBody, const char *pButton)
{
// 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);
2020-09-05 14:54:20 +00:00
m_Popup = POPUP_MESSAGE;
}
2022-05-18 16:00:05 +00:00
void CMenus::PopupWarning(const char *pTopic, const char *pBody, const char *pButton, std::chrono::nanoseconds Duration)
{
dbg_msg(pTopic, "%s", pBody);
// 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(m_EscapePressed)
{
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;
}
// Render
2020-12-14 00:51:31 +00:00
ColorRGBA BackgroundColor(0.1f, 0.1f, 0.1f, 1.0f);
2020-10-28 02:59:50 +00:00
RenderTools()->DrawUIRect(&PickerRect, BackgroundColor, 0, 0);
2020-12-14 00:51:31 +00:00
CUIRect ColorsArea, HueArea, ValuesHitbox, BottomArea, HSVHRect, HSVSRect, HSVVRect, 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);
2020-12-14 00:51:31 +00:00
BottomArea.HSplitTop(20, &HSVHRect, &BottomArea);
BottomArea.HSplitTop(3, 0x0, &BottomArea);
constexpr float ValuePadding = 5.0f;
const float HSVValueWidth = (HSVHRect.w - ValuePadding * 2) / 3.0f;
const float HEXValueWidth = HSVValueWidth * 2 + ValuePadding;
HSVHRect.VSplitLeft(HSVValueWidth, &HSVHRect, &HSVSRect);
HSVSRect.VSplitLeft(ValuePadding, 0x0, &HSVSRect);
HSVSRect.VSplitLeft(HSVValueWidth, &HSVSRect, &HSVVRect);
HSVVRect.VSplitLeft(ValuePadding, 0x0, &HSVVRect);
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);
RenderTools()->DrawUIRect(&HueArea, BlackColor, 0, 0);
2020-10-28 02:59:50 +00:00
HueArea.Margin(1, &HueArea);
2020-12-14 00:51:31 +00:00
RenderTools()->DrawUIRect(&ColorsArea, BlackColor, 0, 0);
2020-10-28 02:59:50 +00:00
ColorsArea.Margin(1, &ColorsArea);
2020-12-14 00:51:31 +00:00
unsigned int H = ms_ColorPicker.m_HSVColor / (1 << 16);
unsigned int S = (ms_ColorPicker.m_HSVColor - (H << 16)) / (1 << 8);
unsigned int V = ms_ColorPicker.m_HSVColor % (1 << 8);
ColorHSVA PickerColorHSV(ms_ColorPicker.m_HSVColor);
2020-10-28 02:59:50 +00:00
// Color Area
ColorRGBA rgb;
2020-12-14 00:51:31 +00:00
rgb = color_cast<ColorRGBA, ColorHSVA>(ColorHSVA(PickerColorHSV.x, 0.0f, 1.0f));
2020-10-28 02:59:50 +00:00
vec4 TL(rgb.r, rgb.g, rgb.b, 1.0f);
2020-12-14 00:51:31 +00:00
rgb = color_cast<ColorRGBA, ColorHSVA>(ColorHSVA(PickerColorHSV.x, 1.0f, 1.0f));
2020-10-28 02:59:50 +00:00
vec4 TR(rgb.r, rgb.g, rgb.b, 1.0f);
2020-12-14 00:51:31 +00:00
rgb = color_cast<ColorRGBA, ColorHSVA>(ColorHSVA(PickerColorHSV.x, 0.0f, 1.0f));
2020-10-28 02:59:50 +00:00
vec4 BL(rgb.r, rgb.g, rgb.b, 1.0f);
2020-12-14 00:51:31 +00:00
rgb = color_cast<ColorRGBA, ColorHSVA>(ColorHSVA(PickerColorHSV.x, 1.0f, 1.0f));
2020-10-28 02:59:50 +00:00
vec4 BR(rgb.r, rgb.g, rgb.b, 1.0f);
RenderTools()->DrawUIRect4NoRounding(&ColorsArea, TL, TR, BL, BR);
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);
RenderTools()->DrawUIRect4NoRounding(&ColorsArea, TL, TR, BL, BR);
// 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;
RenderTools()->DrawUIRect4NoRounding(&HuePartialArea, TL, TL, BL, BL);
}
2020-12-14 00:51:31 +00:00
//Editboxes Area
ColorRGBA EditboxBackground(0, 0, 0, 0.4f);
static int RGBRID = 0;
static int RGBGID = 0;
static int RGBBID = 0;
H = DoValueSelector(&RGBRID, &HSVHRect, "H:", true, H, 0, 255, 1, 1, false, 5.0f, &EditboxBackground);
S = DoValueSelector(&RGBGID, &HSVSRect, "S:", true, S, 0, 255, 1, 1, false, 5.0f, &EditboxBackground);
V = DoValueSelector(&RGBBID, &HSVVRect, "V:", true, V, 0, 255, 1, 1, false, 5.0f, &EditboxBackground);
PickerColorHSV = ColorHSVA((H << 16) + (S << 8) + V);
2020-10-28 02:59:50 +00:00
2020-12-14 00:51:31 +00:00
unsigned int HEX = color_cast<ColorRGBA, ColorHSVA>(PickerColorHSV).Pack(false);
static int HEXID = 0;
2020-10-28 02:59:50 +00:00
2020-12-14 00:51:31 +00:00
unsigned int NEWHEX = DoValueSelector(&HEXID, &HEXRect, "HEX:", false, HEX, 0, 0xFFFFFF, 1, 1, true, 5.0f, &EditboxBackground);
if(HEX != NEWHEX)
PickerColorHSV = color_cast<ColorHSVA, ColorRGBA>(NEWHEX);
// TODO : ALPHA SUPPORT
2020-12-14 01:18:04 +00:00
//static int ALPHAID = 0;
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&ALPHARect, "A: 255", 10, TEXTALIGN_CENTER);
2020-12-14 00:51:31 +00:00
RenderTools()->DrawUIRect(&ALPHARect, ColorRGBA(0, 0, 0, 0.65f), CUI::CORNER_ALL, 5.0f);
2020-10-28 02:59:50 +00:00
// Logic
float PickerX, PickerY;
2020-12-14 01:49:24 +00:00
static int ColorPickerID = 0;
static int HuePickerID = 0;
if(UI()->DoPickerLogic(&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
}
2020-12-14 01:49:24 +00:00
if(UI()->DoPickerLogic(&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
2020-12-14 00:51:31 +00:00
int 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);
2020-12-14 00:51:31 +00:00
RenderTools()->DrawCircle(MarkerX, MarkerY, 4.5f, 32);
Graphics()->SetColor(color_cast<ColorRGBA, ColorHSVA>(PickerColorHSV));
RenderTools()->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;
2020-12-14 00:51:31 +00:00
ColorRGBA HueMarkerColor = color_cast<ColorRGBA, ColorHSVA>(ColorHSVA(PickerColorHSV.x, 1, 1, 1));
const float HMOColor = PickerColorHSV.x > 0.75f ? 1.0f : 0.0f;
ColorRGBA HueMarkerOutline(HMOColor, HMOColor, HMOColor, 1);
2020-12-14 00:51:31 +00:00
RenderTools()->DrawUIRect(&HueMarker, HueMarkerOutline, CUI::CORNER_ALL, 1.2f);
HueMarker.Margin(1.2f, &HueMarker);
RenderTools()->DrawUIRect(&HueMarker, HueMarkerColor, CUI::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, ColorHSVA>(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();
2021-12-03 18:54:22 +00:00
UIEx()->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++;
m_DoubleClickIndex = -1;
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())
m_Popup = POPUP_SOUNDERROR;
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 && m_EscapePressed)
{
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);
m_DoubleClickIndex = -1;
}
// render current page
if(Client()->State() != IClient::STATE_OFFLINE)
{
if(m_GamePage == PAGE_GAME)
RenderGame(MainView);
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;
ColorRGBA BgColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f);
2010-05-29 07:25:38 +00:00
if(m_Popup == POPUP_MESSAGE)
{
2010-05-29 07:25:38 +00:00
pTitle = m_aMessageTopic;
pExtraText = m_aMessageBody;
pButtonText = m_aMessageButton;
}
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");
pExtraText = Client()->ConnectAddressString();
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()->ServerAddress().type);
const char *pMessage = nullptr;
switch(Connectivity)
{
case IClient::CONNECTIVITY_UNKNOWN:
break;
case IClient::CONNECTIVITY_CHECKING:
pMessage = Localize("Trying to determine UDP connectivity...");
break;
case IClient::CONNECTIVITY_UNREACHABLE:
pMessage = Localize("UDP seems to be filtered.");
break;
case IClient::CONNECTIVITY_DIFFERING_UDP_TCP_IP_ADDRESSES:
pMessage = Localize("UDP and TCP IP addresses seem to be different. Try disabling VPN, proxy or network accelerators.");
break;
case IClient::CONNECTIVITY_REACHABLE:
pMessage = Localize("No answer from server yet.");
break;
}
if(pMessage)
{
str_format(aBuf, sizeof(aBuf), "%s\n\n%s", Client()->ConnectAddressString(), pMessage);
pExtraText = aBuf;
}
}
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;
2010-12-16 00:52:29 +00:00
pExtraText = "";
}
else if(Client()->State() == IClient::STATE_LOADING)
{
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");
pExtraText = Localize("Sending intial 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;
}
2010-05-29 07:25:38 +00:00
else if(m_Popup == POPUP_PURE)
{
2010-05-29 07:25:38 +00:00
pTitle = Localize("Disconnected");
pExtraText = Localize("The server is running a non-standard tuning on a pure game type.");
pButtonText = Localize("Ok");
ExtraAlign = -1;
}
else if(m_Popup == POPUP_DELETE_DEMO)
{
pTitle = Localize("Delete demo");
pExtraText = Localize("Are you sure that you want to delete the demo?");
ExtraAlign = -1;
}
else if(m_Popup == POPUP_RENAME_DEMO)
{
pTitle = Localize("Rename demo");
pExtraText = "";
ExtraAlign = -1;
}
2020-09-22 16:02:03 +00:00
#if defined(CONF_VIDEORECORDER)
else if(m_Popup == POPUP_RENDER_DEMO)
{
pTitle = Localize("Render demo");
pExtraText = "";
ExtraAlign = -1;
}
2019-09-27 06:51:08 +00:00
else if(m_Popup == POPUP_REPLACE_VIDEO)
{
pTitle = Localize("Replace video");
2019-11-03 03:20:24 +00:00
pExtraText = Localize("File already exists, do you want to overwrite it?");
2019-09-27 06:51:08 +00:00
ExtraAlign = -1;
}
2020-09-22 16:02:03 +00:00
#endif
2011-03-23 12:06:35 +00:00
else if(m_Popup == POPUP_REMOVE_FRIEND)
{
pTitle = Localize("Remove friend");
pExtraText = Localize("Are you sure that you want to remove the player from your friends list?");
ExtraAlign = -1;
}
else if(m_Popup == POPUP_SOUNDERROR)
{
pTitle = Localize("Sound error");
pExtraText = Localize("The audio device couldn't be initialised.");
pButtonText = Localize("Ok");
ExtraAlign = -1;
}
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");
pExtraText = "";
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?");
ExtraAlign = -1;
}
else if(m_Popup == POPUP_DISCONNECT)
{
pTitle = Localize("Disconnect");
pExtraText = Localize("Are you sure that you want to disconnect?");
ExtraAlign = -1;
}
else if(m_Popup == POPUP_DISCONNECT_DUMMY)
{
pTitle = Localize("Disconnect Dummy");
pExtraText = Localize("Are you sure that you want to disconnect your dummy?");
ExtraAlign = -1;
}
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;
}
else if(Client()->m_Points >= 0)
{
m_Popup = POPUP_NONE;
}
else
{
pExtraText = Localize("Checking for existing player with your name");
}
ExtraAlign = -1;
}
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;
}
else if(m_Popup == POPUP_SWITCH_SERVER)
{
pTitle = Localize("Disconnect");
pExtraText = Localize("Are you sure that you want to disconnect and switch to a different server?");
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
RenderTools()->DrawUIRect(&Box, BgColor, CUI::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;
if(TextRender()->TextWidth(0, 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);
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-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(0, FontSize, pExtraText, -1, -1.0f) > Part.w)
UI()->DoLabel(&Part, pExtraText, FontSize, TEXTALIGN_LEFT, Props);
else
UI()->DoLabel(&Part, pExtraText, FontSize, TEXTALIGN_CENTER);
}
2010-05-29 07:25:38 +00:00
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);
2010-05-29 07:25:38 +00:00
static int s_ButtonAbort = 0;
if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || m_EscapePressed)
m_Popup = POPUP_NONE;
2010-05-29 07:25:38 +00:00
static int s_ButtonTryAgain = 0;
if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed)
{
m_Popup = POPUP_NONE;
2010-05-29 07:25:38 +00:00
Client()->Quit();
}
}
2014-07-08 19:04:04 +00:00
else if(m_Popup == POPUP_DISCONNECT)
{
CUIRect Yes, No;
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
// buttons
Part.VMargin(80.0f, &Part);
Part.VSplitMid(&No, &Yes);
Yes.VMargin(20.0f, &Yes);
No.VMargin(20.0f, &No);
static int s_ButtonAbort = 0;
if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || m_EscapePressed)
m_Popup = POPUP_NONE;
static int s_ButtonTryAgain = 0;
if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed)
Client()->Disconnect();
}
else if(m_Popup == POPUP_DISCONNECT_DUMMY)
{
CUIRect Yes, No;
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
// buttons
Part.VMargin(80.0f, &Part);
Part.VSplitMid(&No, &Yes);
Yes.VMargin(20.0f, &Yes);
No.VMargin(20.0f, &No);
static int s_ButtonAbort = 0;
if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || m_EscapePressed)
m_Popup = POPUP_NONE;
static int s_ButtonTryAgain = 0;
if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed)
{
Client()->DummyDisconnect(0);
m_Popup = POPUP_NONE;
SetActive(false);
}
}
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);
2010-05-29 07:25:38 +00:00
static int s_ButtonAbort = 0;
if(DoButton_Menu(&s_ButtonAbort, Localize("Abort"), 0, &Abort) || m_EscapePressed)
m_Popup = POPUP_NONE;
2010-05-29 07:25:38 +00:00
static int s_ButtonTryAgain = 0;
if(DoButton_Menu(&s_ButtonTryAgain, Localize("Try again"), 0, &TryAgain) || m_EnterPressed)
{
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;
UIEx()->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);
static int s_Button = 0;
if(DoButton_Menu(&s_Button, pButtonText, 0, &Part) || m_EscapePressed || (m_EnterPressed && m_Popup != POPUP_CONNECTING))
2010-12-16 00:52:29 +00:00
{
Client()->Disconnect();
m_Popup = POPUP_NONE;
}
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);
RenderTools()->DrawUIRect(&Part, ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), CUI::CORNER_ALL, 5.0f);
2020-09-22 16:02:03 +00:00
Part.w = maximum(10.0f, (Part.w * Client()->MapDownloadAmount()) / Client()->MapDownloadTotalsize());
RenderTools()->DrawUIRect(&Part, ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f), CUI::CORNER_ALL, 5.0f);
2010-12-16 00:52:29 +00:00
}
}
else if(m_Popup == POPUP_LANGUAGE)
{
Box = Screen;
Box.Margin(150.0f, &Box);
Box.HSplitTop(20.f, &Part, &Box);
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Box.HSplitBottom(20.f, &Box, 0);
Box.VMargin(20.0f, &Box);
RenderLanguageSelection(Box);
Part.VMargin(120.0f, &Part);
static int s_Button = 0;
if(DoButton_Menu(&s_Button, Localize("Ok"), 0, &Part) || m_EscapePressed || m_EnterPressed)
m_Popup = POPUP_FIRST_LAUNCH;
}
else if(m_Popup == POPUP_COUNTRY)
{
Box = Screen;
Box.Margin(150.0f, &Box);
Box.HSplitTop(20.f, &Part, &Box);
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Box.HSplitBottom(20.f, &Box, 0);
Box.VMargin(20.0f, &Box);
2011-08-11 08:59:14 +00:00
static int CurSelection = -2;
if(CurSelection == -2)
CurSelection = g_Config.m_BrFilterCountryIndex;
static float s_ScrollValue = 0.0f;
int OldSelected = -1;
2021-07-12 09:43:56 +00:00
UiDoListboxStart(&s_ScrollValue, &Box, 50.0f, Localize("Country / Region"), "", m_pClient->m_CountryFlags.Num(), 6, OldSelected, s_ScrollValue);
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 == CurSelection)
OldSelected = i;
CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected >= 0 && (size_t)OldSelected == i);
if(Item.m_Visible)
{
CUIRect Label;
Item.m_Rect.Margin(5.0f, &Item.m_Rect);
Item.m_Rect.HSplitBottom(10.0f, &Item.m_Rect, &Label);
float OldWidth = Item.m_Rect.w;
2020-09-22 16:02:03 +00:00
Item.m_Rect.w = Item.m_Rect.h * 2;
Item.m_Rect.x += (OldWidth - Item.m_Rect.w) / 2.0f;
ColorRGBA Color(1.0f, 1.0f, 1.0f, 1.0f);
2021-07-12 09:43:56 +00:00
m_pClient->m_CountryFlags.Render(pEntry->m_CountryCode, &Color, Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h);
UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, TEXTALIGN_CENTER);
}
}
const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0);
if(OldSelected != NewSelected)
CurSelection = m_pClient->m_CountryFlags.GetByIndex(NewSelected)->m_CountryCode;
Part.VMargin(120.0f, &Part);
static int s_Button = 0;
if(DoButton_Menu(&s_Button, Localize("Ok"), 0, &Part) || m_EnterPressed)
{
g_Config.m_BrFilterCountryIndex = CurSelection;
Client()->ServerBrowserUpdate();
m_Popup = POPUP_NONE;
}
if(m_EscapePressed)
{
CurSelection = g_Config.m_BrFilterCountryIndex;
m_Popup = POPUP_NONE;
}
}
else if(m_Popup == POPUP_DELETE_DEMO)
{
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);
static int s_ButtonAbort = 0;
if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || m_EscapePressed)
m_Popup = POPUP_NONE;
static int s_ButtonTryAgain = 0;
if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed)
{
m_Popup = POPUP_NONE;
// delete demo
if(m_DemolistSelectedIndex >= 0 && !m_DemolistSelectedIsDir)
{
str_format(aBuf, sizeof(aBuf), "%s/%s", m_aCurrentDemoFolder, m_vDemos[m_DemolistSelectedIndex].m_aFilename);
if(Storage()->RemoveFile(aBuf, m_vDemos[m_DemolistSelectedIndex].m_StorageType))
{
DemolistPopulate();
DemolistOnUpdate(false);
}
else
PopupMessage(Localize("Error"), Localize("Unable to delete the demo"), Localize("Ok"));
}
}
}
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);
static int s_ButtonAbort = 0;
if(DoButton_Menu(&s_ButtonAbort, Localize("Abort"), 0, &Abort) || m_EscapePressed)
m_Popup = POPUP_NONE;
static int s_ButtonOk = 0;
if(DoButton_Menu(&s_ButtonOk, Localize("Ok"), 0, &Ok) || m_EnterPressed)
{
m_Popup = POPUP_NONE;
// rename demo
if(m_DemolistSelectedIndex >= 0 && !m_DemolistSelectedIsDir)
{
char aBufOld[512];
str_format(aBufOld, sizeof(aBufOld), "%s/%s", m_aCurrentDemoFolder, m_vDemos[m_DemolistSelectedIndex].m_aFilename);
int Length = str_length(m_aCurrentDemoFile);
char aBufNew[512];
2020-09-22 16:02:03 +00:00
if(Length <= 4 || m_aCurrentDemoFile[Length - 5] != '.' || str_comp_nocase(m_aCurrentDemoFile + Length - 4, "demo"))
str_format(aBufNew, sizeof(aBufNew), "%s/%s.demo", m_aCurrentDemoFolder, m_aCurrentDemoFile);
else
str_format(aBufNew, sizeof(aBufNew), "%s/%s", m_aCurrentDemoFolder, m_aCurrentDemoFile);
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;
UIEx()->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)
{
2022-03-18 13:31:52 +00:00
CUIRect Label, TextBox, Ok, Abort, IncSpeed, DecSpeed, Button, Buttons;
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);
2022-03-18 13:31:52 +00:00
Part.HSplitBottom(20.0f, &Part, &Buttons);
Buttons.VSplitMid(&Abort, &Ok);
Ok.VMargin(20.0f, &Ok);
Abort.VMargin(20.0f, &Abort);
static int s_ButtonAbort = 0;
if(DoButton_Menu(&s_ButtonAbort, Localize("Abort"), 0, &Abort) || m_EscapePressed)
m_Popup = POPUP_NONE;
static int s_ButtonOk = 0;
if(DoButton_Menu(&s_ButtonOk, Localize("Ok"), 0, &Ok) || m_EnterPressed)
{
m_Popup = POPUP_NONE;
// name video
if(m_DemolistSelectedIndex >= 0 && !m_DemolistSelectedIsDir)
{
char aBufOld[512];
str_format(aBufOld, sizeof(aBufOld), "%s/%s", m_aCurrentDemoFolder, m_vDemos[m_DemolistSelectedIndex].m_aFilename);
int Length = str_length(m_aCurrentDemoFile);
char aBufNew[512];
2020-09-22 16:02:03 +00:00
if(Length <= 3 || m_aCurrentDemoFile[Length - 4] != '.' || str_comp_nocase(m_aCurrentDemoFile + Length - 3, "mp4"))
str_format(aBufNew, sizeof(aBufNew), "%s.mp4", m_aCurrentDemoFile);
else
str_format(aBufNew, sizeof(aBufNew), "%s", m_aCurrentDemoFile);
2019-09-27 06:51:08 +00:00
char aWholePath[1024];
// store new video filename to origin buffer
2022-07-09 16:14:56 +00:00
str_copy(m_aCurrentDemoFile, aBufNew);
2019-09-27 06:51:08 +00:00
if(Storage()->FindFile(m_aCurrentDemoFile, "videos", IStorage::TYPE_ALL, aWholePath, sizeof(aWholePath)))
{
PopupMessage(Localize("Error"), Localize("Destination file already exist"), Localize("Ok"));
m_Popup = POPUP_REPLACE_VIDEO;
}
else
{
const char *pError = Client()->DemoPlayer_Render(aBufOld, m_vDemos[m_DemolistSelectedIndex].m_StorageType, m_aCurrentDemoFile, m_Speed);
2019-09-28 04:18:38 +00:00
m_Speed = 4;
2019-09-27 06:51:08 +00:00
//Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "demo_render_path", aWholePath);
if(pError)
PopupMessage(Localize("Error"), str_comp(pError, "error loading demo") ? pError : Localize("Error loading demo"), Localize("Ok"));
}
}
}
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);
2019-09-28 04:18:38 +00:00
static int s_SlowDownButton = 0;
if(DoButton_Sprite(&s_SlowDownButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_SLOWER, 0, &Button, CUI::CORNER_ALL))
DecDemoSpeed = true;
// fastforward
Part.VSplitLeft(5.0f, 0, &Part);
Part.VSplitLeft(ButtonSize, &Button, &Part);
2019-09-28 04:18:38 +00:00
static int s_FastForwardButton = 0;
if(DoButton_Sprite(&s_FastForwardButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_FASTER, 0, &Button, CUI::CORNER_ALL))
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;
UIEx()->DoEditBox(&s_Offset, &TextBox, m_aCurrentDemoFile, sizeof(m_aCurrentDemoFile), 12.0f, &s_Offset);
}
2019-09-27 06:51:08 +00:00
else if(m_Popup == POPUP_REPLACE_VIDEO)
{
CUIRect Yes, No;
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(&No, &Yes);
Yes.VMargin(20.0f, &Yes);
No.VMargin(20.0f, &No);
static int s_ButtonAbort = 0;
if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || m_EscapePressed)
m_Popup = POPUP_RENDER_DEMO;
static int s_ButtonTryAgain = 0;
if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed)
{
m_Popup = POPUP_NONE;
// render video
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);
2019-09-28 04:18:38 +00:00
m_Speed = 4;
2019-09-27 06:51:08 +00:00
if(pError)
PopupMessage(Localize("Error"), str_comp(pError, "error loading demo") ? pError : Localize("Error loading demo"), Localize("Ok"));
}
}
2019-09-27 07:22:50 +00:00
#endif
2011-03-23 12:06:35 +00:00
else if(m_Popup == POPUP_REMOVE_FRIEND)
{
CUIRect Yes, No;
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VMargin(80.0f, &Part);
2011-03-23 12:06:35 +00:00
Part.VSplitMid(&No, &Yes);
2011-03-23 12:06:35 +00:00
Yes.VMargin(20.0f, &Yes);
No.VMargin(20.0f, &No);
static int s_ButtonAbort = 0;
if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || m_EscapePressed)
m_Popup = POPUP_NONE;
static int s_ButtonTryAgain = 0;
if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed)
{
m_Popup = POPUP_NONE;
// remove friend
if(m_FriendlistSelectedIndex >= 0)
{
m_pClient->Friends()->RemoveFriend(m_vFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName,
m_vFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aClan);
2011-06-26 15:10:13 +00:00
FriendlistOnUpdate();
2011-03-23 12:06:35 +00:00
Client()->ServerBrowserUpdate();
}
}
}
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);
static int s_JoinTutorialButton = 0;
if(DoButton_Menu(&s_JoinTutorialButton, Localize("Join Tutorial Server"), 0, &Join) || m_EnterPressed)
{
m_JoinTutorial = true;
Client()->RequestDDNetInfo();
m_Popup = g_Config.m_BrIndicateFinished ? POPUP_POINTS : POPUP_NONE;
}
static int s_SkipTutorialButton = 0;
if(DoButton_Menu(&s_SkipTutorialButton, Localize("Skip Tutorial"), 0, &Skip) || m_EscapePressed)
{
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"),
Localize("transmits your player name to info2.ddnet.tw"));
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();
UIEx()->DoEditBox(&g_Config.m_PlayerName, &TextBox, g_Config.m_PlayerName, sizeof(g_Config.m_PlayerName), 12.0f, &s_Offset, false, CUI::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);
static int s_ButtonNo = 0;
if(DoButton_Menu(&s_ButtonNo, Localize("No"), 0, &No) || m_EscapePressed)
m_Popup = POPUP_FIRST_LAUNCH;
static int s_ButtonYes = 0;
if(DoButton_Menu(&s_ButtonYes, Localize("Yes"), 0, &Yes) || m_EnterPressed)
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);
static int s_Button = 0;
if(DoButton_Menu(&s_Button, pButtonText, 0, &Part) || m_EscapePressed || m_EnterPressed || (time_get_nanoseconds() - m_PopupWarningLastTime >= m_PopupWarningDuration))
{
m_Popup = POPUP_NONE;
SetActive(false);
}
}
else if(m_Popup == POPUP_SWITCH_SERVER)
{
CUIRect Yes, No;
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
// buttons
Part.VMargin(80.0f, &Part);
Part.VSplitMid(&No, &Yes);
Yes.VMargin(20.0f, &Yes);
No.VMargin(20.0f, &No);
static int s_ButtonAbort = 0;
if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || m_EscapePressed)
{
m_Popup = POPUP_NONE;
m_aNextServer[0] = '\0';
}
static int s_ButtonTryAgain = 0;
if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed)
Client()->Connect(m_aNextServer);
}
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);
2010-05-29 07:25:38 +00:00
static int s_Button = 0;
if(DoButton_Menu(&s_Button, pButtonText, 0, &Part) || m_EscapePressed || m_EnterPressed)
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;
}
2020-09-18 16:45:42 +00:00
void CMenus::RenderThemeSelection(CUIRect MainView, bool Header)
{
2022-06-15 17:34:41 +00:00
std::vector<CTheme> &vThemesRef = m_pBackground->GetThemes();
2020-09-18 16:45:42 +00:00
int SelectedTheme = -1;
2022-06-15 17:34:41 +00:00
for(int i = 0; i < (int)vThemesRef.size(); i++)
2020-09-18 16:45:42 +00:00
{
2022-06-15 17:34:41 +00:00
if(str_comp(vThemesRef[i].m_Name.c_str(), g_Config.m_ClMenuMap) == 0)
2020-09-18 16:45:42 +00:00
{
SelectedTheme = i;
break;
}
}
static int s_ListBox = 0;
static float s_ScrollValue = 0.0f;
2022-06-15 17:34:41 +00:00
UiDoListboxStart(&s_ListBox, &MainView, 26.0f, Localize("Theme"), "", vThemesRef.size(), 1, -1, s_ScrollValue);
2020-09-18 16:45:42 +00:00
2022-06-15 17:34:41 +00:00
for(int i = 0; i < (int)vThemesRef.size(); i++)
2020-09-18 16:45:42 +00:00
{
2022-06-15 17:34:41 +00:00
CListboxItem Item = UiDoListboxNextItem(&vThemesRef[i].m_Name, i == SelectedTheme);
2020-09-18 16:45:42 +00:00
2022-06-15 17:34:41 +00:00
CTheme &Theme = vThemesRef[i];
2020-09-18 16:45:42 +00:00
if(!Item.m_Visible)
continue;
CUIRect Icon;
Item.m_Rect.VSplitLeft(Item.m_Rect.h * 2.0f, &Icon, &Item.m_Rect);
// 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_format(aName, sizeof(aName), "%s", 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_format(aName, sizeof(aName), "%s", Theme.m_Name.c_str());
2020-09-18 16:45:42 +00:00
UI()->DoLabel(&Item.m_Rect, aName, 16 * CUI::ms_FontmodHeight, TEXTALIGN_LEFT);
2020-09-18 16:45:42 +00:00
}
bool ItemActive = false;
int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0, &ItemActive);
if(ItemActive && NewSelected != SelectedTheme)
{
2022-06-15 17:34:41 +00:00
str_format(g_Config.m_ClMenuMap, sizeof(g_Config.m_ClMenuMap), "%s", vThemesRef[NewSelected].m_Name.c_str());
m_pBackground->LoadMenuBackground(vThemesRef[NewSelected].m_HasDay, vThemesRef[NewSelected].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);
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;
UIEx()->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;
}
2010-05-29 07:25:38 +00:00
bool CMenus::OnInput(IInput::CEvent e)
{
2008-10-21 18:25:28 +00:00
// special handle esc and enter for popup purposes
2020-09-22 16:02:03 +00:00
if(e.m_Flags & IInput::FLAG_PRESS)
2008-08-27 20:23:50 +00:00
{
2010-05-29 07:25:38 +00:00
if(e.m_Key == KEY_ESCAPE)
{
2010-05-29 07:25:38 +00:00
m_EscapePressed = true;
if(m_Popup == POPUP_NONE)
SetActive(!IsActive());
2008-10-21 18:25:28 +00:00
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
{
2020-09-22 16:02:03 +00:00
if(e.m_Flags & IInput::FLAG_PRESS)
{
// special for popups
if(e.m_Key == KEY_RETURN || e.m_Key == KEY_KP_ENTER)
m_EnterPressed = true;
else if(e.m_Key == KEY_DELETE)
m_DeletePressed = true;
}
2010-05-29 07:25:38 +00:00
if(m_NumInputEvents < MAX_INPUTEVENTS)
m_aInputEvents[m_NumInputEvents++] = e;
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(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);
}
}
}
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);
m_Popup = POPUP_PURE;
}
2010-05-29 07:25:38 +00:00
if(!IsActive())
{
2010-05-29 07:25:38 +00:00
m_EscapePressed = false;
m_EnterPressed = false;
m_DeletePressed = false;
2010-05-29 07:25:38 +00:00
m_NumInputEvents = 0;
UI()->FinishCheck();
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();
2010-05-29 07:25:38 +00:00
m_EscapePressed = false;
m_EnterPressed = false;
m_DeletePressed = false;
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 = fmod(LocalTime() * 0.15f, 2.0f);
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 &&
2021-12-19 22:42:34 +00:00
!Input()->KeyIsPressed(KEY_LSHIFT) && !Input()->KeyIsPressed(KEY_RSHIFT) && !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;
}
bool CMenus::HandleListInputs(const CUIRect &View, float &ScrollValue, const float ScrollAmount, int *pScrollOffset, const float ElemHeight, int &SelectedIndex, const int NumElems)
{
2021-04-20 16:54:32 +00:00
if(NumElems == 0)
{
ScrollValue = 0;
SelectedIndex = 0;
return false;
}
int NewIndex = -1;
2020-12-16 06:03:32 +00:00
int Num = (int)(View.h / ElemHeight);
int ScrollNum = maximum(NumElems - Num, 0);
if(ScrollNum > 0)
{
if(pScrollOffset && *pScrollOffset >= 0)
{
ScrollValue = (float)(*pScrollOffset) / ScrollNum;
*pScrollOffset = -1;
}
if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP) && UI()->MouseInside(&View))
ScrollValue -= 3.0f / ScrollNum;
if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN) && UI()->MouseInside(&View))
ScrollValue += 3.0f / ScrollNum;
}
2020-11-21 17:30:24 +00:00
ScrollValue = clamp(ScrollValue, 0.0f, 1.0f);
2021-04-20 16:54:32 +00:00
SelectedIndex = clamp(SelectedIndex, 0, NumElems - 1);
for(int i = 0; i < m_NumInputEvents; i++)
{
if(m_aInputEvents[i].m_Flags & IInput::FLAG_PRESS)
{
2021-07-26 06:34:31 +00:00
if(UI()->LastActiveItem() == &g_Config.m_UiServerAddress)
return false;
else if(m_aInputEvents[i].m_Key == KEY_DOWN)
NewIndex = minimum(SelectedIndex + 1, NumElems - 1);
else if(m_aInputEvents[i].m_Key == KEY_UP)
NewIndex = maximum(SelectedIndex - 1, 0);
else if(m_aInputEvents[i].m_Key == KEY_PAGEUP)
NewIndex = maximum(SelectedIndex - 25, 0);
else if(m_aInputEvents[i].m_Key == KEY_PAGEDOWN)
NewIndex = minimum(SelectedIndex + 25, NumElems - 1);
else if(m_aInputEvents[i].m_Key == KEY_HOME)
NewIndex = 0;
else if(m_aInputEvents[i].m_Key == KEY_END)
NewIndex = NumElems - 1;
}
if(NewIndex > -1 && NewIndex < NumElems)
{
//scroll
float IndexY = View.y - ScrollValue * ScrollNum * ElemHeight + NewIndex * ElemHeight;
int Scroll = View.y > IndexY ? -1 : View.y + View.h < IndexY + ElemHeight ? 1 : 0;
if(Scroll)
{
if(Scroll < 0)
{
int NumScrolls = (View.y - IndexY + ElemHeight - 1.0f) / ElemHeight;
ScrollValue -= (1.0f / ScrollNum) * NumScrolls;
}
else
{
int NumScrolls = (IndexY + ElemHeight - (View.y + View.h) + ElemHeight - 1.0f) / ElemHeight;
ScrollValue += (1.0f / ScrollNum) * NumScrolls;
}
}
SelectedIndex = NewIndex;
}
}
return NewIndex != -1;
2022-04-18 07:55:50 +00:00
}