1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ |
2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ |
3 | |
4 | #include <algorithm> |
5 | #include <chrono> |
6 | #include <cmath> |
7 | #include <vector> |
8 | |
9 | #include <base/math.h> |
10 | #include <base/system.h> |
11 | #include <base/vmath.h> |
12 | |
13 | #include <engine/client.h> |
14 | #include <engine/config.h> |
15 | #include <engine/editor.h> |
16 | #include <engine/friends.h> |
17 | #include <engine/graphics.h> |
18 | #include <engine/keys.h> |
19 | #include <engine/serverbrowser.h> |
20 | #include <engine/shared/config.h> |
21 | #include <engine/storage.h> |
22 | #include <engine/textrender.h> |
23 | |
24 | #include <game/generated/protocol.h> |
25 | |
26 | #include <engine/client/updater.h> |
27 | |
28 | #include <game/client/components/binds.h> |
29 | #include <game/client/components/console.h> |
30 | #include <game/client/components/menu_background.h> |
31 | #include <game/client/components/sounds.h> |
32 | #include <game/client/gameclient.h> |
33 | #include <game/client/ui_listbox.h> |
34 | #include <game/generated/client_data.h> |
35 | #include <game/localization.h> |
36 | |
37 | #include "menus.h" |
38 | |
39 | using namespace FontIcons; |
40 | using namespace std::chrono_literals; |
41 | |
42 | ColorRGBA CMenus::; |
43 | ColorRGBA CMenus::; |
44 | ColorRGBA CMenus::; |
45 | ColorRGBA CMenus::; |
46 | ColorRGBA CMenus::; |
47 | ColorRGBA CMenus:: = ColorRGBA(0, 0, 0, 0.5f); |
48 | ColorRGBA CMenus::; |
49 | ColorRGBA CMenus::; |
50 | ColorRGBA CMenus::; |
51 | ColorRGBA CMenus::; |
52 | |
53 | float CMenus:: = 25.0f; |
54 | float CMenus:: = 17.0f; |
55 | |
56 | CMenus::() |
57 | { |
58 | m_Popup = POPUP_NONE; |
59 | m_MenuPage = 0; |
60 | m_GamePage = PAGE_GAME; |
61 | |
62 | m_NeedRestartGraphics = false; |
63 | m_NeedRestartSound = false; |
64 | m_NeedSendinfo = false; |
65 | m_NeedSendDummyinfo = false; |
66 | m_MenuActive = true; |
67 | m_ShowStart = true; |
68 | |
69 | str_copy(dst&: m_aCurrentDemoFolder, src: "demos" ); |
70 | m_DemolistStorageType = IStorage::TYPE_ALL; |
71 | |
72 | m_DemoPlayerState = DEMOPLAYER_NONE; |
73 | m_Dummy = false; |
74 | |
75 | m_ServerProcess.m_Process = INVALID_PROCESS; |
76 | |
77 | for(SUIAnimator &animator : m_aAnimatorsSettingsTab) |
78 | { |
79 | animator.m_YOffset = -2.5f; |
80 | animator.m_HOffset = 5.0f; |
81 | animator.m_WOffset = 5.0f; |
82 | animator.m_RepositionLabel = true; |
83 | } |
84 | |
85 | for(SUIAnimator &animator : m_aAnimatorsBigPage) |
86 | { |
87 | animator.m_YOffset = -5.0f; |
88 | animator.m_HOffset = 5.0f; |
89 | } |
90 | |
91 | for(SUIAnimator &animator : m_aAnimatorsSmallPage) |
92 | { |
93 | animator.m_YOffset = -2.5f; |
94 | animator.m_HOffset = 2.5f; |
95 | } |
96 | |
97 | m_PasswordInput.SetBuffer(pStr: g_Config.m_Password, MaxSize: sizeof(g_Config.m_Password)); |
98 | m_PasswordInput.SetHidden(true); |
99 | } |
100 | |
101 | int CMenus::(const void *pId, int Checked, const CUIRect *pRect, bool Active) |
102 | { |
103 | Graphics()->TextureSet(Texture: g_pData->m_aImages[IMAGE_GUIBUTTONS].m_Id); |
104 | Graphics()->QuadsBegin(); |
105 | if(!Active) |
106 | Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 0.5f); |
107 | RenderTools()->SelectSprite(Id: Checked ? SPRITE_GUIBUTTON_ON : SPRITE_GUIBUTTON_OFF); |
108 | IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h); |
109 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
110 | if(Ui()->HotItem() == pId && Active) |
111 | { |
112 | RenderTools()->SelectSprite(Id: SPRITE_GUIBUTTON_HOVER); |
113 | QuadItem = IGraphics::CQuadItem(pRect->x, pRect->y, pRect->w, pRect->h); |
114 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
115 | } |
116 | Graphics()->QuadsEnd(); |
117 | |
118 | return Active ? Ui()->DoButtonLogic(pId, Checked, pRect) : 0; |
119 | } |
120 | |
121 | int CMenus::(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, const char *pImageName, int Corners, float Rounding, float FontFactor, ColorRGBA Color) |
122 | { |
123 | CUIRect Text = *pRect; |
124 | |
125 | if(Checked) |
126 | Color = ColorRGBA(0.6f, 0.6f, 0.6f, 0.5f); |
127 | Color.a *= Ui()->ButtonColorMul(pId: pButtonContainer); |
128 | |
129 | pRect->Draw(Color, Corners, Rounding); |
130 | |
131 | if(pImageName) |
132 | { |
133 | CUIRect Image; |
134 | pRect->VSplitRight(Cut: pRect->h * 4.0f, pLeft: &Text, pRight: &Image); // always correct ratio for image |
135 | |
136 | // render image |
137 | const CMenuImage *pImage = FindMenuImage(pName: pImageName); |
138 | if(pImage) |
139 | { |
140 | Graphics()->TextureSet(Texture: Ui()->HotItem() == pButtonContainer ? pImage->m_OrgTexture : pImage->m_GreyTexture); |
141 | Graphics()->WrapClamp(); |
142 | Graphics()->QuadsBegin(); |
143 | Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f); |
144 | IGraphics::CQuadItem QuadItem(Image.x, Image.y, Image.w, Image.h); |
145 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
146 | Graphics()->QuadsEnd(); |
147 | Graphics()->WrapNormal(); |
148 | } |
149 | } |
150 | |
151 | Text.HMargin(Cut: pRect->h >= 20.0f ? 2.0f : 1.0f, pOtherRect: &Text); |
152 | Text.HMargin(Cut: (Text.h * FontFactor) / 2.0f, pOtherRect: &Text); |
153 | Ui()->DoLabel(pRect: &Text, pText, Size: Text.h * CUi::ms_FontmodHeight, Align: TEXTALIGN_MC); |
154 | |
155 | return Ui()->DoButtonLogic(pId: pButtonContainer, Checked, pRect); |
156 | } |
157 | |
158 | int CMenus::(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, int Corners, SUIAnimator *pAnimator, const ColorRGBA *pDefaultColor, const ColorRGBA *pActiveColor, const ColorRGBA *pHoverColor, float EdgeRounding, const SCommunityIcon *) |
159 | { |
160 | const bool MouseInside = Ui()->HotItem() == pButtonContainer; |
161 | CUIRect Rect = *pRect; |
162 | |
163 | if(pAnimator != NULL) |
164 | { |
165 | auto Time = time_get_nanoseconds(); |
166 | |
167 | if(pAnimator->m_Time + 100ms < Time) |
168 | { |
169 | pAnimator->m_Value = pAnimator->m_Active ? 1 : 0; |
170 | pAnimator->m_Time = Time; |
171 | } |
172 | |
173 | pAnimator->m_Active = Checked || MouseInside; |
174 | |
175 | if(pAnimator->m_Active) |
176 | pAnimator->m_Value = clamp<float>(val: pAnimator->m_Value + (Time - pAnimator->m_Time).count() / (double)std::chrono::nanoseconds(100ms).count(), lo: 0, hi: 1); |
177 | else |
178 | pAnimator->m_Value = clamp<float>(val: pAnimator->m_Value - (Time - pAnimator->m_Time).count() / (double)std::chrono::nanoseconds(100ms).count(), lo: 0, hi: 1); |
179 | |
180 | Rect.w += pAnimator->m_Value * pAnimator->m_WOffset; |
181 | Rect.h += pAnimator->m_Value * pAnimator->m_HOffset; |
182 | Rect.x += pAnimator->m_Value * pAnimator->m_XOffset; |
183 | Rect.y += pAnimator->m_Value * pAnimator->m_YOffset; |
184 | |
185 | pAnimator->m_Time = Time; |
186 | } |
187 | |
188 | if(Checked) |
189 | { |
190 | ColorRGBA = ms_ColorTabbarActive; |
191 | if(pActiveColor) |
192 | ColorMenuTab = *pActiveColor; |
193 | |
194 | Rect.Draw(Color: ColorMenuTab, Corners, Rounding: EdgeRounding); |
195 | } |
196 | else |
197 | { |
198 | if(MouseInside) |
199 | { |
200 | ColorRGBA = ms_ColorTabbarHover; |
201 | if(pHoverColor) |
202 | HoverColorMenuTab = *pHoverColor; |
203 | |
204 | Rect.Draw(Color: HoverColorMenuTab, Corners, Rounding: EdgeRounding); |
205 | } |
206 | else |
207 | { |
208 | ColorRGBA = ms_ColorTabbarInactive; |
209 | if(pDefaultColor) |
210 | ColorMenuTab = *pDefaultColor; |
211 | |
212 | Rect.Draw(Color: ColorMenuTab, Corners, Rounding: EdgeRounding); |
213 | } |
214 | } |
215 | |
216 | if(pAnimator != NULL) |
217 | { |
218 | if(pAnimator->m_RepositionLabel) |
219 | { |
220 | Rect.x += Rect.w - pRect->w + Rect.x - pRect->x; |
221 | Rect.y += Rect.h - pRect->h + Rect.y - pRect->y; |
222 | } |
223 | |
224 | if(!pAnimator->m_ScaleLabel) |
225 | { |
226 | Rect.w = pRect->w; |
227 | Rect.h = pRect->h; |
228 | } |
229 | } |
230 | |
231 | if(pCommunityIcon) |
232 | { |
233 | CUIRect ; |
234 | Rect.Margin(Cut: 2.0f, pOtherRect: &CommunityIcon); |
235 | RenderCommunityIcon(pIcon: pCommunityIcon, Rect: CommunityIcon, Active: true); |
236 | } |
237 | else |
238 | { |
239 | CUIRect Label; |
240 | Rect.HMargin(Cut: 2.0f, pOtherRect: &Label); |
241 | Ui()->DoLabel(pRect: &Label, pText, Size: Label.h * CUi::ms_FontmodHeight, Align: TEXTALIGN_MC); |
242 | } |
243 | |
244 | return Ui()->DoButtonLogic(pId: pButtonContainer, Checked, pRect); |
245 | } |
246 | |
247 | int CMenus::(const void *pId, const char *pText, int Checked, const CUIRect *pRect) |
248 | { |
249 | if(Checked == 2) |
250 | pRect->Draw(Color: ColorRGBA(1, 0.98f, 0.5f, 0.55f), Corners: IGraphics::CORNER_T, Rounding: 5.0f); |
251 | else if(Checked) |
252 | pRect->Draw(Color: ColorRGBA(1, 1, 1, 0.5f), Corners: IGraphics::CORNER_T, Rounding: 5.0f); |
253 | |
254 | CUIRect Temp; |
255 | pRect->VSplitLeft(Cut: 5.0f, pLeft: nullptr, pRight: &Temp); |
256 | Ui()->DoLabel(pRect: &Temp, pText, Size: pRect->h * CUi::ms_FontmodHeight, Align: TEXTALIGN_ML); |
257 | return Ui()->DoButtonLogic(pId, Checked, pRect); |
258 | } |
259 | |
260 | int CMenus::(const void *pButtonId, const void *pParentId, bool Checked, const CUIRect *pRect) |
261 | { |
262 | if(Checked || (pParentId != nullptr && Ui()->HotItem() == pParentId) || Ui()->HotItem() == pButtonId) |
263 | { |
264 | TextRender()->SetFontPreset(EFontPreset::ICON_FONT); |
265 | 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); |
266 | const float Alpha = Ui()->HotItem() == pButtonId ? 0.2f : 0.0f; |
267 | TextRender()->TextColor(rgb: Checked ? ColorRGBA(1.0f, 0.85f, 0.3f, 0.8f + Alpha) : ColorRGBA(0.5f, 0.5f, 0.5f, 0.8f + Alpha)); |
268 | SLabelProperties Props; |
269 | Props.m_MaxWidth = pRect->w; |
270 | Ui()->DoLabel(pRect, pText: FONT_ICON_STAR, Size: 12.0f, Align: TEXTALIGN_MC, LabelProps: Props); |
271 | TextRender()->TextColor(rgb: TextRender()->DefaultTextColor()); |
272 | TextRender()->SetRenderFlags(0); |
273 | TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); |
274 | } |
275 | return Ui()->DoButtonLogic(pId: pButtonId, Checked: 0, pRect); |
276 | } |
277 | |
278 | int CMenus::(const void *pId, const char *pText, const char *pBoxText, const CUIRect *pRect) |
279 | { |
280 | CUIRect Box, Label; |
281 | pRect->VSplitLeft(Cut: pRect->h, pLeft: &Box, pRight: &Label); |
282 | Label.VSplitLeft(Cut: 5.0f, pLeft: nullptr, pRight: &Label); |
283 | |
284 | Box.Margin(Cut: 2.0f, pOtherRect: &Box); |
285 | Box.Draw(Color: ColorRGBA(1, 1, 1, 0.25f * Ui()->ButtonColorMul(pId)), Corners: IGraphics::CORNER_ALL, Rounding: 3.0f); |
286 | |
287 | const bool Checkable = *pBoxText == 'X'; |
288 | if(Checkable) |
289 | { |
290 | 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); |
291 | TextRender()->SetFontPreset(EFontPreset::ICON_FONT); |
292 | Ui()->DoLabel(pRect: &Box, pText: FONT_ICON_XMARK, Size: Box.h * CUi::ms_FontmodHeight, Align: TEXTALIGN_MC); |
293 | TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); |
294 | } |
295 | else |
296 | Ui()->DoLabel(pRect: &Box, pText: pBoxText, Size: Box.h * CUi::ms_FontmodHeight, Align: TEXTALIGN_MC); |
297 | |
298 | TextRender()->SetRenderFlags(0); |
299 | Ui()->DoLabel(pRect: &Label, pText, Size: Box.h * CUi::ms_FontmodHeight, Align: TEXTALIGN_ML); |
300 | |
301 | return Ui()->DoButtonLogic(pId, Checked: 0, pRect); |
302 | } |
303 | |
304 | void CMenus::(const CUIRect *pRect, const ColorHSLA LaserOutlineColor, const ColorHSLA LaserInnerColor, const int LaserType) |
305 | { |
306 | ColorRGBA LaserRGB; |
307 | CUIRect Section = *pRect; |
308 | vec2 From = vec2(Section.x + 50.0f, Section.y + Section.h / 2.0f); |
309 | vec2 Pos = vec2(Section.x + Section.w - 10.0f, Section.y + Section.h / 2.0f); |
310 | |
311 | Graphics()->BlendNormal(); |
312 | Graphics()->TextureClear(); |
313 | Graphics()->QuadsBegin(); |
314 | |
315 | LaserRGB = color_cast<ColorRGBA, ColorHSLA>(hsl: LaserOutlineColor); |
316 | ColorRGBA OuterColor(LaserRGB.r, LaserRGB.g, LaserRGB.b, 1.0f); |
317 | Graphics()->SetColor(r: LaserRGB.r, g: LaserRGB.g, b: LaserRGB.b, a: 1.0f); |
318 | vec2 Out = vec2(0.0f, -1.0f) * (3.15f); |
319 | |
320 | 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); |
321 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
322 | |
323 | LaserRGB = color_cast<ColorRGBA, ColorHSLA>(hsl: LaserInnerColor); |
324 | ColorRGBA InnerColor(LaserRGB.r, LaserRGB.g, LaserRGB.b, 1.0f); |
325 | Out = vec2(0.0f, -1.0f) * (2.25f); |
326 | Graphics()->SetColor(r: InnerColor.r, g: InnerColor.g, b: InnerColor.b, a: 1.0f); |
327 | |
328 | 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); |
329 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
330 | Graphics()->QuadsEnd(); |
331 | |
332 | Graphics()->BlendNormal(); |
333 | int SpriteIndex = time_get() % 3; |
334 | Graphics()->TextureSet(Texture: GameClient()->m_ParticlesSkin.m_aSpriteParticleSplat[SpriteIndex]); |
335 | Graphics()->QuadsBegin(); |
336 | Graphics()->QuadsSetRotation(Angle: time_get()); |
337 | Graphics()->SetColor(r: OuterColor.r, g: OuterColor.g, b: OuterColor.b, a: 1.0f); |
338 | IGraphics::CQuadItem QuadItem(Pos.x, Pos.y, 24, 24); |
339 | Graphics()->QuadsDraw(pArray: &QuadItem, Num: 1); |
340 | Graphics()->SetColor(r: InnerColor.r, g: InnerColor.g, b: InnerColor.b, a: 1.0f); |
341 | QuadItem = IGraphics::CQuadItem(Pos.x, Pos.y, 20, 20); |
342 | Graphics()->QuadsDraw(pArray: &QuadItem, Num: 1); |
343 | Graphics()->QuadsEnd(); |
344 | |
345 | switch(LaserType) |
346 | { |
347 | case LASERTYPE_RIFLE: |
348 | Graphics()->TextureSet(Texture: GameClient()->m_GameSkin.m_SpriteWeaponLaser); |
349 | RenderTools()->SelectSprite(Id: SPRITE_WEAPON_LASER_BODY); |
350 | Graphics()->QuadsBegin(); |
351 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
352 | RenderTools()->DrawSprite(x: Section.x + 30.0f, y: Section.y + Section.h / 2.0f, Size: 60.0f); |
353 | Graphics()->QuadsEnd(); |
354 | break; |
355 | case LASERTYPE_SHOTGUN: |
356 | Graphics()->TextureSet(Texture: GameClient()->m_GameSkin.m_SpriteWeaponShotgun); |
357 | RenderTools()->SelectSprite(Id: SPRITE_WEAPON_SHOTGUN_BODY); |
358 | Graphics()->QuadsBegin(); |
359 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
360 | RenderTools()->DrawSprite(x: Section.x + 30.0f, y: Section.y + Section.h / 2.0f, Size: 60.0f); |
361 | Graphics()->QuadsEnd(); |
362 | break; |
363 | default: |
364 | Graphics()->QuadsBegin(); |
365 | Graphics()->SetColor(r: OuterColor.r, g: OuterColor.g, b: OuterColor.b, a: 1.0f); |
366 | QuadItem = IGraphics::CQuadItem(From.x, From.y, 24, 24); |
367 | Graphics()->QuadsDraw(pArray: &QuadItem, Num: 1); |
368 | Graphics()->SetColor(r: InnerColor.r, g: InnerColor.g, b: InnerColor.b, a: 1.0f); |
369 | QuadItem = IGraphics::CQuadItem(From.x, From.y, 20, 20); |
370 | Graphics()->QuadsDraw(pArray: &QuadItem, Num: 1); |
371 | Graphics()->QuadsEnd(); |
372 | } |
373 | } |
374 | |
375 | ColorHSLA CMenus::(CButtonContainer *pResetId, const float LineSize, const float LabelSize, const float BottomMargin, CUIRect *pMainRect, const char *pText, unsigned int *pColorValue, const ColorRGBA DefaultColor, bool CheckBoxSpacing, int *pCheckBoxValue, bool Alpha) |
376 | { |
377 | CUIRect Section, ColorPickerButton, ResetButton, Label; |
378 | |
379 | pMainRect->HSplitTop(Cut: LineSize, pTop: &Section, pBottom: pMainRect); |
380 | pMainRect->HSplitTop(Cut: BottomMargin, pTop: nullptr, pBottom: pMainRect); |
381 | |
382 | Section.VSplitRight(Cut: 60.0f, pLeft: &Section, pRight: &ResetButton); |
383 | Section.VSplitRight(Cut: 8.0f, pLeft: &Section, pRight: nullptr); |
384 | Section.VSplitRight(Cut: Section.h, pLeft: &Section, pRight: &ColorPickerButton); |
385 | Section.VSplitRight(Cut: 8.0f, pLeft: &Label, pRight: nullptr); |
386 | |
387 | if(pCheckBoxValue != nullptr) |
388 | { |
389 | Label.Margin(Cut: 2.0f, pOtherRect: &Label); |
390 | if(DoButton_CheckBox(pId: pCheckBoxValue, pText, Checked: *pCheckBoxValue, pRect: &Label)) |
391 | *pCheckBoxValue ^= 1; |
392 | } |
393 | else if(CheckBoxSpacing) |
394 | { |
395 | Label.VSplitLeft(Cut: Label.h + 5.0f, pLeft: nullptr, pRight: &Label); |
396 | } |
397 | if(pCheckBoxValue == nullptr) |
398 | { |
399 | Ui()->DoLabel(pRect: &Label, pText, Size: LabelSize, Align: TEXTALIGN_ML); |
400 | } |
401 | |
402 | const ColorHSLA PickedColor = DoButton_ColorPicker(pRect: &ColorPickerButton, pHslaColor: pColorValue, Alpha); |
403 | |
404 | ResetButton.HMargin(Cut: 2.0f, pOtherRect: &ResetButton); |
405 | if(DoButton_Menu(pButtonContainer: pResetId, pText: Localize(pStr: "Reset" ), Checked: 0, pRect: &ResetButton, pImageName: nullptr, Corners: IGraphics::CORNER_ALL, Rounding: 4.0f, FontFactor: 0.1f, Color: ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f))) |
406 | { |
407 | *pColorValue = color_cast<ColorHSLA>(rgb: DefaultColor).Pack(Alpha); |
408 | } |
409 | |
410 | return PickedColor; |
411 | } |
412 | |
413 | ColorHSLA CMenus::(const CUIRect *pRect, unsigned int *pHslaColor, bool Alpha) |
414 | { |
415 | ColorHSLA HslaColor = ColorHSLA(*pHslaColor, Alpha); |
416 | |
417 | ColorRGBA Outline = ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f); |
418 | Outline.a *= Ui()->ButtonColorMul(pId: pHslaColor); |
419 | |
420 | CUIRect Rect; |
421 | pRect->Margin(Cut: 3.0f, pOtherRect: &Rect); |
422 | |
423 | pRect->Draw(Color: Outline, Corners: IGraphics::CORNER_ALL, Rounding: 4.0f); |
424 | Rect.Draw(Color: color_cast<ColorRGBA>(hsl: HslaColor), Corners: IGraphics::CORNER_ALL, Rounding: 4.0f); |
425 | |
426 | static CUi::SColorPickerPopupContext ; |
427 | if(Ui()->DoButtonLogic(pId: pHslaColor, Checked: 0, pRect)) |
428 | { |
429 | s_ColorPickerPopupContext.m_pHslaColor = pHslaColor; |
430 | s_ColorPickerPopupContext.m_HslaColor = HslaColor; |
431 | s_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(hsl: HslaColor); |
432 | s_ColorPickerPopupContext.m_RgbaColor = color_cast<ColorRGBA>(hsv: s_ColorPickerPopupContext.m_HsvaColor); |
433 | s_ColorPickerPopupContext.m_Alpha = Alpha; |
434 | Ui()->ShowPopupColorPicker(X: Ui()->MouseX(), Y: Ui()->MouseY(), pContext: &s_ColorPickerPopupContext); |
435 | } |
436 | else if(Ui()->IsPopupOpen(pId: &s_ColorPickerPopupContext) && s_ColorPickerPopupContext.m_pHslaColor == pHslaColor) |
437 | { |
438 | HslaColor = color_cast<ColorHSLA>(hsv: s_ColorPickerPopupContext.m_HsvaColor); |
439 | } |
440 | |
441 | return HslaColor; |
442 | } |
443 | |
444 | int CMenus::DoButton_CheckBoxAutoVMarginAndSet(const void *pId, const char *pText, int *pValue, CUIRect *pRect, float VMargin) |
445 | { |
446 | CUIRect CheckBoxRect; |
447 | pRect->HSplitTop(Cut: VMargin, pTop: &CheckBoxRect, pBottom: pRect); |
448 | |
449 | int Logic = DoButton_CheckBox_Common(pId, pText, pBoxText: *pValue ? "X" : "" , pRect: &CheckBoxRect); |
450 | |
451 | if(Logic) |
452 | *pValue ^= 1; |
453 | |
454 | return Logic; |
455 | } |
456 | |
457 | int CMenus::(const void *pId, const char *pText, int Checked, const CUIRect *pRect) |
458 | { |
459 | return DoButton_CheckBox_Common(pId, pText, pBoxText: Checked ? "X" : "" , pRect); |
460 | } |
461 | |
462 | int CMenus::(const void *pId, const char *pText, int Checked, const CUIRect *pRect) |
463 | { |
464 | char aBuf[16]; |
465 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d" , Checked); |
466 | return DoButton_CheckBox_Common(pId, pText, pBoxText: aBuf, pRect); |
467 | } |
468 | |
469 | int CMenus::(const void *pId, const CUIRect *pRect, int Key, int ModifierCombination, int *pNewModifierCombination) |
470 | { |
471 | int NewKey = Key; |
472 | *pNewModifierCombination = ModifierCombination; |
473 | |
474 | const int ButtonResult = Ui()->DoButtonLogic(pId, Checked: 0, pRect); |
475 | if(ButtonResult == 1) |
476 | { |
477 | m_Binder.m_pKeyReaderId = pId; |
478 | m_Binder.m_TakeKey = true; |
479 | m_Binder.m_GotKey = false; |
480 | } |
481 | else if(ButtonResult == 2) |
482 | { |
483 | NewKey = 0; |
484 | *pNewModifierCombination = CBinds::MODIFIER_NONE; |
485 | } |
486 | |
487 | if(m_Binder.m_pKeyReaderId == pId && m_Binder.m_GotKey) |
488 | { |
489 | // abort with escape key |
490 | if(m_Binder.m_Key.m_Key != KEY_ESCAPE) |
491 | { |
492 | NewKey = m_Binder.m_Key.m_Key; |
493 | *pNewModifierCombination = m_Binder.m_ModifierCombination; |
494 | } |
495 | m_Binder.m_pKeyReaderId = nullptr; |
496 | m_Binder.m_GotKey = false; |
497 | Ui()->SetActiveItem(nullptr); |
498 | } |
499 | |
500 | char aBuf[64]; |
501 | if(m_Binder.m_pKeyReaderId == pId && m_Binder.m_TakeKey) |
502 | str_copy(dst&: aBuf, src: Localize(pStr: "Press a key…" )); |
503 | else if(NewKey == 0) |
504 | aBuf[0] = '\0'; |
505 | else |
506 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s%s" , CBinds::GetKeyBindModifiersName(ModifierCombination: *pNewModifierCombination), Input()->KeyName(Key: NewKey)); |
507 | |
508 | const ColorRGBA Color = m_Binder.m_pKeyReaderId == pId && m_Binder.m_TakeKey ? ColorRGBA(0.0f, 1.0f, 0.0f, 0.4f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f * Ui()->ButtonColorMul(pId)); |
509 | pRect->Draw(Color, Corners: IGraphics::CORNER_ALL, Rounding: 5.0f); |
510 | CUIRect Temp; |
511 | pRect->HMargin(Cut: 1.0f, pOtherRect: &Temp); |
512 | Ui()->DoLabel(pRect: &Temp, pText: aBuf, Size: Temp.h * CUi::ms_FontmodHeight, Align: TEXTALIGN_MC); |
513 | |
514 | return NewKey; |
515 | } |
516 | |
517 | void CMenus::(CUIRect Box, IClient::EClientState ClientState) |
518 | { |
519 | CUIRect Button; |
520 | |
521 | int NewPage = -1; |
522 | int ActivePage = -1; |
523 | if(ClientState == IClient::STATE_OFFLINE) |
524 | { |
525 | ActivePage = m_MenuPage; |
526 | } |
527 | else if(ClientState == IClient::STATE_ONLINE) |
528 | { |
529 | ActivePage = m_GamePage; |
530 | } |
531 | else |
532 | { |
533 | dbg_assert(false, "Client state invalid for RenderMenubar" ); |
534 | } |
535 | |
536 | // First render buttons aligned from right side so remaining |
537 | // width is known when rendering buttons from left side. |
538 | TextRender()->SetFontPreset(EFontPreset::ICON_FONT); |
539 | 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); |
540 | |
541 | Box.VSplitRight(Cut: 33.0f, pLeft: &Box, pRight: &Button); |
542 | static CButtonContainer s_QuitButton; |
543 | ColorRGBA QuitColor(1, 0, 0, 0.5f); |
544 | if(DoButton_MenuTab(pButtonContainer: &s_QuitButton, pText: FONT_ICON_POWER_OFF, Checked: 0, pRect: &Button, Corners: IGraphics::CORNER_T, pAnimator: &m_aAnimatorsSmallPage[SMALL_TAB_QUIT], pDefaultColor: nullptr, pActiveColor: nullptr, pHoverColor: &QuitColor, EdgeRounding: 10.0f)) |
545 | { |
546 | if(m_pClient->Editor()->HasUnsavedData() || (Client()->GetCurrentRaceTime() / 60 >= g_Config.m_ClConfirmQuitTime && g_Config.m_ClConfirmQuitTime >= 0)) |
547 | { |
548 | m_Popup = POPUP_QUIT; |
549 | } |
550 | else |
551 | { |
552 | Client()->Quit(); |
553 | } |
554 | } |
555 | GameClient()->m_Tooltips.DoToolTip(pId: &s_QuitButton, pNearRect: &Button, pText: Localize(pStr: "Quit" )); |
556 | |
557 | Box.VSplitRight(Cut: 10.0f, pLeft: &Box, pRight: nullptr); |
558 | Box.VSplitRight(Cut: 33.0f, pLeft: &Box, pRight: &Button); |
559 | static CButtonContainer s_SettingsButton; |
560 | if(DoButton_MenuTab(pButtonContainer: &s_SettingsButton, pText: FONT_ICON_GEAR, Checked: ActivePage == PAGE_SETTINGS, pRect: &Button, Corners: IGraphics::CORNER_T, pAnimator: &m_aAnimatorsSmallPage[SMALL_TAB_SETTINGS])) |
561 | { |
562 | NewPage = PAGE_SETTINGS; |
563 | } |
564 | GameClient()->m_Tooltips.DoToolTip(pId: &s_SettingsButton, pNearRect: &Button, pText: Localize(pStr: "Settings" )); |
565 | |
566 | Box.VSplitRight(Cut: 10.0f, pLeft: &Box, pRight: nullptr); |
567 | Box.VSplitRight(Cut: 33.0f, pLeft: &Box, pRight: &Button); |
568 | static CButtonContainer s_EditorButton; |
569 | if(DoButton_MenuTab(pButtonContainer: &s_EditorButton, pText: FONT_ICON_PEN_TO_SQUARE, Checked: 0, pRect: &Button, Corners: IGraphics::CORNER_T, pAnimator: &m_aAnimatorsSmallPage[SMALL_TAB_EDITOR])) |
570 | { |
571 | g_Config.m_ClEditor = 1; |
572 | } |
573 | GameClient()->m_Tooltips.DoToolTip(pId: &s_EditorButton, pNearRect: &Button, pText: Localize(pStr: "Editor" )); |
574 | |
575 | if(ClientState == IClient::STATE_OFFLINE) |
576 | { |
577 | Box.VSplitRight(Cut: 10.0f, pLeft: &Box, pRight: nullptr); |
578 | Box.VSplitRight(Cut: 33.0f, pLeft: &Box, pRight: &Button); |
579 | static CButtonContainer s_DemoButton; |
580 | if(DoButton_MenuTab(pButtonContainer: &s_DemoButton, pText: FONT_ICON_CLAPPERBOARD, Checked: ActivePage == PAGE_DEMOS, pRect: &Button, Corners: IGraphics::CORNER_T, pAnimator: &m_aAnimatorsSmallPage[SMALL_TAB_DEMOBUTTON])) |
581 | { |
582 | NewPage = PAGE_DEMOS; |
583 | } |
584 | GameClient()->m_Tooltips.DoToolTip(pId: &s_DemoButton, pNearRect: &Button, pText: Localize(pStr: "Demos" )); |
585 | } |
586 | |
587 | Box.VSplitRight(Cut: 10.0f, pLeft: &Box, pRight: nullptr); |
588 | |
589 | TextRender()->SetRenderFlags(0); |
590 | TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); |
591 | |
592 | if(ClientState == IClient::STATE_OFFLINE) |
593 | { |
594 | Box.VSplitLeft(Cut: 33.0f, pLeft: &Button, pRight: &Box); |
595 | |
596 | TextRender()->SetFontPreset(EFontPreset::ICON_FONT); |
597 | 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); |
598 | |
599 | bool GotNewsOrUpdate = false; |
600 | |
601 | #if defined(CONF_AUTOUPDATE) |
602 | int State = Updater()->GetCurrentState(); |
603 | bool NeedUpdate = str_comp(a: Client()->LatestVersion(), b: "0" ); |
604 | if(State == IUpdater::CLEAN && NeedUpdate) |
605 | { |
606 | GotNewsOrUpdate = true; |
607 | } |
608 | #endif |
609 | |
610 | GotNewsOrUpdate |= (bool)g_Config.m_UiUnreadNews; |
611 | |
612 | ColorRGBA HomeButtonColorAlert(0, 1, 0, 0.25f); |
613 | ColorRGBA HomeButtonColorAlertHover(0, 1, 0, 0.5f); |
614 | ColorRGBA *pHomeButtonColor = nullptr; |
615 | ColorRGBA *pHomeButtonColorHover = nullptr; |
616 | |
617 | const char *pHomeScreenButtonLabel = FONT_ICON_HOUSE; |
618 | if(GotNewsOrUpdate) |
619 | { |
620 | pHomeScreenButtonLabel = FONT_ICON_NEWSPAPER; |
621 | pHomeButtonColor = &HomeButtonColorAlert; |
622 | pHomeButtonColorHover = &HomeButtonColorAlertHover; |
623 | } |
624 | |
625 | static CButtonContainer s_StartButton; |
626 | if(DoButton_MenuTab(pButtonContainer: &s_StartButton, pText: pHomeScreenButtonLabel, Checked: false, pRect: &Button, Corners: IGraphics::CORNER_T, pAnimator: &m_aAnimatorsSmallPage[SMALL_TAB_HOME], pDefaultColor: pHomeButtonColor, pActiveColor: pHomeButtonColor, pHoverColor: pHomeButtonColorHover, EdgeRounding: 10.0f)) |
627 | { |
628 | m_ShowStart = true; |
629 | } |
630 | GameClient()->m_Tooltips.DoToolTip(pId: &s_StartButton, pNearRect: &Button, pText: Localize(pStr: "Main menu" )); |
631 | |
632 | const float BrowserButtonWidth = 75.0f; |
633 | Box.VSplitLeft(Cut: 10.0f, pLeft: nullptr, pRight: &Box); |
634 | Box.VSplitLeft(Cut: BrowserButtonWidth, pLeft: &Button, pRight: &Box); |
635 | static CButtonContainer s_InternetButton; |
636 | if(DoButton_MenuTab(pButtonContainer: &s_InternetButton, pText: FONT_ICON_EARTH_AMERICAS, Checked: ActivePage == PAGE_INTERNET, pRect: &Button, Corners: IGraphics::CORNER_T, pAnimator: &m_aAnimatorsBigPage[BIG_TAB_INTERNET])) |
637 | { |
638 | NewPage = PAGE_INTERNET; |
639 | } |
640 | GameClient()->m_Tooltips.DoToolTip(pId: &s_InternetButton, pNearRect: &Button, pText: Localize(pStr: "Internet" )); |
641 | |
642 | Box.VSplitLeft(Cut: BrowserButtonWidth, pLeft: &Button, pRight: &Box); |
643 | static CButtonContainer s_LanButton; |
644 | if(DoButton_MenuTab(pButtonContainer: &s_LanButton, pText: FONT_ICON_NETWORK_WIRED, Checked: ActivePage == PAGE_LAN, pRect: &Button, Corners: IGraphics::CORNER_T, pAnimator: &m_aAnimatorsBigPage[BIG_TAB_LAN])) |
645 | { |
646 | NewPage = PAGE_LAN; |
647 | } |
648 | GameClient()->m_Tooltips.DoToolTip(pId: &s_LanButton, pNearRect: &Button, pText: Localize(pStr: "LAN" )); |
649 | |
650 | Box.VSplitLeft(Cut: BrowserButtonWidth, pLeft: &Button, pRight: &Box); |
651 | static CButtonContainer s_FavoritesButton; |
652 | if(DoButton_MenuTab(pButtonContainer: &s_FavoritesButton, pText: FONT_ICON_STAR, Checked: ActivePage == PAGE_FAVORITES, pRect: &Button, Corners: IGraphics::CORNER_T, pAnimator: &m_aAnimatorsBigPage[BIG_TAB_FAVORITES])) |
653 | { |
654 | NewPage = PAGE_FAVORITES; |
655 | } |
656 | GameClient()->m_Tooltips.DoToolTip(pId: &s_FavoritesButton, pNearRect: &Button, pText: Localize(pStr: "Favorites" )); |
657 | |
658 | size_t = 0; |
659 | static CButtonContainer [5]; |
660 | static_assert(std::size(s_aFavoriteCommunityButtons) == (size_t)PAGE_FAVORITE_COMMUNITY_5 - PAGE_FAVORITE_COMMUNITY_1 + 1); |
661 | static_assert(std::size(s_aFavoriteCommunityButtons) == (size_t)BIT_TAB_FAVORITE_COMMUNITY_5 - BIT_TAB_FAVORITE_COMMUNITY_1 + 1); |
662 | static_assert(std::size(s_aFavoriteCommunityButtons) == (size_t)IServerBrowser::TYPE_FAVORITE_COMMUNITY_5 - IServerBrowser::TYPE_FAVORITE_COMMUNITY_1 + 1); |
663 | for(const CCommunity * : ServerBrowser()->FavoriteCommunities()) |
664 | { |
665 | if(Box.w < BrowserButtonWidth) |
666 | break; |
667 | Box.VSplitLeft(Cut: BrowserButtonWidth, pLeft: &Button, pRight: &Box); |
668 | const int Page = PAGE_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex; |
669 | if(DoButton_MenuTab(pButtonContainer: &s_aFavoriteCommunityButtons[FavoriteCommunityIndex], pText: FONT_ICON_ELLIPSIS, Checked: ActivePage == Page, pRect: &Button, Corners: IGraphics::CORNER_T, pAnimator: &m_aAnimatorsBigPage[BIT_TAB_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex], pDefaultColor: nullptr, pActiveColor: nullptr, pHoverColor: nullptr, EdgeRounding: 10.0f, pCommunityIcon: FindCommunityIcon(pCommunityId: pCommunity->Id()))) |
670 | { |
671 | NewPage = Page; |
672 | } |
673 | GameClient()->m_Tooltips.DoToolTip(pId: &s_aFavoriteCommunityButtons[FavoriteCommunityIndex], pNearRect: &Button, pText: pCommunity->Name()); |
674 | |
675 | ++FavoriteCommunityIndex; |
676 | if(FavoriteCommunityIndex >= std::size(s_aFavoriteCommunityButtons)) |
677 | break; |
678 | } |
679 | |
680 | TextRender()->SetRenderFlags(0); |
681 | TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); |
682 | } |
683 | else |
684 | { |
685 | // online menus |
686 | Box.VSplitLeft(Cut: 90.0f, pLeft: &Button, pRight: &Box); |
687 | static CButtonContainer s_GameButton; |
688 | if(DoButton_MenuTab(pButtonContainer: &s_GameButton, pText: Localize(pStr: "Game" ), Checked: ActivePage == PAGE_GAME, pRect: &Button, Corners: IGraphics::CORNER_TL)) |
689 | NewPage = PAGE_GAME; |
690 | |
691 | Box.VSplitLeft(Cut: 90.0f, pLeft: &Button, pRight: &Box); |
692 | static CButtonContainer s_PlayersButton; |
693 | if(DoButton_MenuTab(pButtonContainer: &s_PlayersButton, pText: Localize(pStr: "Players" ), Checked: ActivePage == PAGE_PLAYERS, pRect: &Button, Corners: IGraphics::CORNER_NONE)) |
694 | NewPage = PAGE_PLAYERS; |
695 | |
696 | Box.VSplitLeft(Cut: 130.0f, pLeft: &Button, pRight: &Box); |
697 | static CButtonContainer s_ServerInfoButton; |
698 | if(DoButton_MenuTab(pButtonContainer: &s_ServerInfoButton, pText: Localize(pStr: "Server info" ), Checked: ActivePage == PAGE_SERVER_INFO, pRect: &Button, Corners: IGraphics::CORNER_NONE)) |
699 | NewPage = PAGE_SERVER_INFO; |
700 | |
701 | Box.VSplitLeft(Cut: 90.0f, pLeft: &Button, pRight: &Box); |
702 | static CButtonContainer s_NetworkButton; |
703 | if(DoButton_MenuTab(pButtonContainer: &s_NetworkButton, pText: Localize(pStr: "Browser" ), Checked: ActivePage == PAGE_NETWORK, pRect: &Button, Corners: IGraphics::CORNER_NONE)) |
704 | NewPage = PAGE_NETWORK; |
705 | |
706 | if(GameClient()->m_GameInfo.m_Race) |
707 | { |
708 | Box.VSplitLeft(Cut: 90.0f, pLeft: &Button, pRight: &Box); |
709 | static CButtonContainer s_GhostButton; |
710 | if(DoButton_MenuTab(pButtonContainer: &s_GhostButton, pText: Localize(pStr: "Ghost" ), Checked: ActivePage == PAGE_GHOST, pRect: &Button, Corners: IGraphics::CORNER_NONE)) |
711 | NewPage = PAGE_GHOST; |
712 | } |
713 | |
714 | Box.VSplitLeft(Cut: 100.0f, pLeft: &Button, pRight: &Box); |
715 | Box.VSplitLeft(Cut: 4.0f, pLeft: nullptr, pRight: &Box); |
716 | static CButtonContainer s_CallVoteButton; |
717 | if(DoButton_MenuTab(pButtonContainer: &s_CallVoteButton, pText: Localize(pStr: "Call vote" ), Checked: ActivePage == PAGE_CALLVOTE, pRect: &Button, Corners: IGraphics::CORNER_TR)) |
718 | { |
719 | NewPage = PAGE_CALLVOTE; |
720 | m_ControlPageOpening = true; |
721 | } |
722 | } |
723 | |
724 | if(NewPage != -1) |
725 | { |
726 | if(ClientState == IClient::STATE_OFFLINE) |
727 | SetMenuPage(NewPage); |
728 | else |
729 | m_GamePage = NewPage; |
730 | } |
731 | } |
732 | |
733 | void CMenus::(const char *pCaption, const char *pContent, int IncreaseCounter, bool RenderLoadingBar, bool ) |
734 | { |
735 | // TODO: not supported right now due to separate render thread |
736 | |
737 | const int CurLoadRenderCount = m_LoadingState.m_Current; |
738 | m_LoadingState.m_Current += IncreaseCounter; |
739 | |
740 | // make sure that we don't render for each little thing we load |
741 | // because that will slow down loading if we have vsync |
742 | const std::chrono::nanoseconds Now = time_get_nanoseconds(); |
743 | if(Now - m_LoadingState.m_LastRender < std::chrono::nanoseconds(1s) / 60l) |
744 | return; |
745 | |
746 | m_LoadingState.m_LastRender = Now; |
747 | |
748 | // need up date this here to get correct |
749 | ms_GuiColor = color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_UiColor, true)); |
750 | |
751 | Ui()->MapScreen(); |
752 | |
753 | if(!RenderMenuBackgroundMap || !GameClient()->m_MenuBackground.Render()) |
754 | { |
755 | RenderBackground(); |
756 | } |
757 | |
758 | CUIRect Box; |
759 | Ui()->Screen()->Margin(Cut: 160.0f, pOtherRect: &Box); |
760 | |
761 | Graphics()->BlendNormal(); |
762 | Graphics()->TextureClear(); |
763 | Box.Draw(Color: ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f), Corners: IGraphics::CORNER_ALL, Rounding: 15.0f); |
764 | Box.Margin(Cut: 20.0f, pOtherRect: &Box); |
765 | |
766 | CUIRect Label; |
767 | Box.HSplitTop(Cut: 24.0f, pTop: &Label, pBottom: &Box); |
768 | Ui()->DoLabel(pRect: &Label, pText: pCaption, Size: 24.0f, Align: TEXTALIGN_MC); |
769 | |
770 | Box.HSplitTop(Cut: 20.0f, pTop: nullptr, pBottom: &Box); |
771 | Box.HSplitTop(Cut: 24.0f, pTop: &Label, pBottom: &Box); |
772 | Ui()->DoLabel(pRect: &Label, pText: pContent, Size: 20.0f, Align: TEXTALIGN_MC); |
773 | |
774 | if(RenderLoadingBar) |
775 | { |
776 | CUIRect ProgressBar; |
777 | Box.HSplitBottom(Cut: 30.0f, pTop: &Box, pBottom: nullptr); |
778 | Box.HSplitBottom(Cut: 25.0f, pTop: &Box, pBottom: &ProgressBar); |
779 | ProgressBar.VMargin(Cut: 20.0f, pOtherRect: &ProgressBar); |
780 | Ui()->RenderProgressBar(ProgressBar, Progress: CurLoadRenderCount / (float)m_LoadingState.m_Total); |
781 | } |
782 | |
783 | Client()->UpdateAndSwap(); |
784 | } |
785 | |
786 | void CMenus::(CUIRect MainView) |
787 | { |
788 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_NEWS); |
789 | |
790 | g_Config.m_UiUnreadNews = false; |
791 | |
792 | MainView.Draw(Color: ms_ColorTabbarActive, Corners: IGraphics::CORNER_B, Rounding: 10.0f); |
793 | |
794 | MainView.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &MainView); |
795 | MainView.VSplitLeft(Cut: 15.0f, pLeft: nullptr, pRight: &MainView); |
796 | |
797 | CUIRect Label; |
798 | |
799 | const char *pStr = Client()->News(); |
800 | char aLine[256]; |
801 | while((pStr = str_next_token(str: pStr, delim: "\n" , buffer: aLine, buffer_size: sizeof(aLine)))) |
802 | { |
803 | const int Len = str_length(str: aLine); |
804 | if(Len > 0 && aLine[0] == '|' && aLine[Len - 1] == '|') |
805 | { |
806 | MainView.HSplitTop(Cut: 30.0f, pTop: &Label, pBottom: &MainView); |
807 | aLine[Len - 1] = '\0'; |
808 | Ui()->DoLabel(pRect: &Label, pText: aLine + 1, Size: 20.0f, Align: TEXTALIGN_ML); |
809 | } |
810 | else |
811 | { |
812 | MainView.HSplitTop(Cut: 20.0f, pTop: &Label, pBottom: &MainView); |
813 | Ui()->DoLabel(pRect: &Label, pText: aLine, Size: 15.f, Align: TEXTALIGN_ML); |
814 | } |
815 | } |
816 | } |
817 | |
818 | void CMenus::() |
819 | { |
820 | if(g_Config.m_ClShowWelcome) |
821 | { |
822 | m_Popup = POPUP_LANGUAGE; |
823 | m_CreateDefaultFavoriteCommunities = true; |
824 | } |
825 | |
826 | if(g_Config.m_UiPage >= PAGE_FAVORITE_COMMUNITY_1 && g_Config.m_UiPage <= PAGE_FAVORITE_COMMUNITY_5 && |
827 | (size_t)(g_Config.m_UiPage - PAGE_FAVORITE_COMMUNITY_1) >= ServerBrowser()->FavoriteCommunities().size()) |
828 | { |
829 | // Reset page to internet when there is no favorite community for this page. |
830 | g_Config.m_UiPage = PAGE_INTERNET; |
831 | } |
832 | |
833 | if(g_Config.m_ClSkipStartMenu) |
834 | { |
835 | m_ShowStart = false; |
836 | } |
837 | |
838 | SetMenuPage(g_Config.m_UiPage); |
839 | |
840 | m_RefreshButton.Init(pUI: Ui(), RequestedRectCount: -1); |
841 | m_ConnectButton.Init(pUI: Ui(), RequestedRectCount: -1); |
842 | |
843 | Console()->Chain(pName: "add_favorite" , pfnChainFunc: ConchainFavoritesUpdate, pUser: this); |
844 | Console()->Chain(pName: "remove_favorite" , pfnChainFunc: ConchainFavoritesUpdate, pUser: this); |
845 | Console()->Chain(pName: "add_friend" , pfnChainFunc: ConchainFriendlistUpdate, pUser: this); |
846 | Console()->Chain(pName: "remove_friend" , pfnChainFunc: ConchainFriendlistUpdate, pUser: this); |
847 | |
848 | Console()->Chain(pName: "add_excluded_community" , pfnChainFunc: ConchainCommunitiesUpdate, pUser: this); |
849 | Console()->Chain(pName: "remove_excluded_community" , pfnChainFunc: ConchainCommunitiesUpdate, pUser: this); |
850 | Console()->Chain(pName: "add_excluded_country" , pfnChainFunc: ConchainCommunitiesUpdate, pUser: this); |
851 | Console()->Chain(pName: "remove_excluded_country" , pfnChainFunc: ConchainCommunitiesUpdate, pUser: this); |
852 | Console()->Chain(pName: "add_excluded_type" , pfnChainFunc: ConchainCommunitiesUpdate, pUser: this); |
853 | Console()->Chain(pName: "remove_excluded_type" , pfnChainFunc: ConchainCommunitiesUpdate, pUser: this); |
854 | |
855 | Console()->Chain(pName: "ui_page" , pfnChainFunc: ConchainUiPageUpdate, pUser: this); |
856 | |
857 | Console()->Chain(pName: "snd_enable" , pfnChainFunc: ConchainUpdateMusicState, pUser: this); |
858 | Console()->Chain(pName: "snd_enable_music" , pfnChainFunc: ConchainUpdateMusicState, pUser: this); |
859 | Console()->Chain(pName: "cl_background_entities" , pfnChainFunc: ConchainBackgroundEntities, pUser: this); |
860 | |
861 | Console()->Chain(pName: "cl_assets_entities" , pfnChainFunc: ConchainAssetsEntities, pUser: this); |
862 | Console()->Chain(pName: "cl_asset_game" , pfnChainFunc: ConchainAssetGame, pUser: this); |
863 | Console()->Chain(pName: "cl_asset_emoticons" , pfnChainFunc: ConchainAssetEmoticons, pUser: this); |
864 | Console()->Chain(pName: "cl_asset_particles" , pfnChainFunc: ConchainAssetParticles, pUser: this); |
865 | Console()->Chain(pName: "cl_asset_hud" , pfnChainFunc: ConchainAssetHud, pUser: this); |
866 | Console()->Chain(pName: "cl_asset_extras" , pfnChainFunc: ConchainAssetExtras, pUser: this); |
867 | |
868 | m_TextureBlob = Graphics()->LoadTexture(pFilename: "blob.png" , StorageType: IStorage::TYPE_ALL); |
869 | |
870 | // setup load amount |
871 | const int = 5; |
872 | m_LoadingState.m_Current = 0; |
873 | m_LoadingState.m_Total = g_pData->m_NumImages + NumMenuImages + GameClient()->ComponentCount(); |
874 | if(!g_Config.m_ClThreadsoundloading) |
875 | m_LoadingState.m_Total += g_pData->m_NumSounds; |
876 | |
877 | m_IsInit = true; |
878 | |
879 | // load menu images |
880 | m_vMenuImages.clear(); |
881 | Storage()->ListDirectory(Type: IStorage::TYPE_ALL, pPath: "menuimages" , pfnCallback: MenuImageScan, pUser: this); |
882 | |
883 | // load community icons |
884 | m_vCommunityIcons.clear(); |
885 | Storage()->ListDirectory(Type: IStorage::TYPE_ALL, pPath: "communityicons" , pfnCallback: CommunityIconScan, pUser: this); |
886 | } |
887 | |
888 | void CMenus::() |
889 | { |
890 | ConfigManager()->RegisterCallback(pfnFunc: CMenus::ConfigSaveCallback, pUserData: this); |
891 | Console()->Register(pName: "add_favorite_skin" , pParams: "s[skin_name]" , Flags: CFGFLAG_CLIENT, pfnFunc: Con_AddFavoriteSkin, pUser: this, pHelp: "Add a skin as a favorite" ); |
892 | Console()->Register(pName: "remove_favorite_skin" , pParams: "s[skin_name]" , Flags: CFGFLAG_CLIENT, pfnFunc: Con_RemFavoriteSkin, pUser: this, pHelp: "Remove a skin from the favorites" ); |
893 | } |
894 | |
895 | void CMenus::(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) |
896 | { |
897 | pfnCallback(pResult, pCallbackUserData); |
898 | if(pResult->NumArguments()) |
899 | { |
900 | CMenus *pSelf = (CMenus *)pUserData; |
901 | pSelf->UpdateBackgroundEntities(); |
902 | } |
903 | } |
904 | |
905 | void CMenus::() |
906 | { |
907 | if(str_comp(a: g_Config.m_ClBackgroundEntities, b: m_pClient->m_Background.MapName()) != 0) |
908 | m_pClient->m_Background.LoadBackground(); |
909 | } |
910 | |
911 | void CMenus::(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) |
912 | { |
913 | pfnCallback(pResult, pCallbackUserData); |
914 | auto *pSelf = (CMenus *)pUserData; |
915 | if(pResult->NumArguments()) |
916 | pSelf->UpdateMusicState(); |
917 | } |
918 | |
919 | void CMenus::() |
920 | { |
921 | const bool ShouldPlay = Client()->State() == IClient::STATE_OFFLINE && g_Config.m_SndEnable && g_Config.m_SndMusic; |
922 | if(ShouldPlay && !m_pClient->m_Sounds.IsPlaying(SetId: SOUND_MENU)) |
923 | m_pClient->m_Sounds.Enqueue(Channel: CSounds::CHN_MUSIC, SetId: SOUND_MENU); |
924 | else if(!ShouldPlay && m_pClient->m_Sounds.IsPlaying(SetId: SOUND_MENU)) |
925 | m_pClient->m_Sounds.Stop(SetId: SOUND_MENU); |
926 | } |
927 | |
928 | void CMenus::(const char *pTitle, const char *pMessage, const char *pButtonLabel, int , FPopupButtonCallback pfnButtonCallback) |
929 | { |
930 | // reset active item |
931 | Ui()->SetActiveItem(nullptr); |
932 | |
933 | str_copy(dst&: m_aPopupTitle, src: pTitle); |
934 | str_copy(dst&: m_aPopupMessage, src: pMessage); |
935 | str_copy(dst&: m_aPopupButtons[BUTTON_CONFIRM].m_aLabel, src: pButtonLabel); |
936 | m_aPopupButtons[BUTTON_CONFIRM].m_NextPopup = NextPopup; |
937 | m_aPopupButtons[BUTTON_CONFIRM].m_pfnCallback = pfnButtonCallback; |
938 | m_Popup = POPUP_MESSAGE; |
939 | } |
940 | |
941 | void CMenus::(const char *pTitle, const char *pMessage, const char *pConfirmButtonLabel, const char *pCancelButtonLabel, |
942 | FPopupButtonCallback pfnConfirmButtonCallback, int , FPopupButtonCallback pfnCancelButtonCallback, int ) |
943 | { |
944 | // reset active item |
945 | Ui()->SetActiveItem(nullptr); |
946 | |
947 | str_copy(dst&: m_aPopupTitle, src: pTitle); |
948 | str_copy(dst&: m_aPopupMessage, src: pMessage); |
949 | str_copy(dst&: m_aPopupButtons[BUTTON_CONFIRM].m_aLabel, src: pConfirmButtonLabel); |
950 | m_aPopupButtons[BUTTON_CONFIRM].m_NextPopup = ConfirmNextPopup; |
951 | m_aPopupButtons[BUTTON_CONFIRM].m_pfnCallback = pfnConfirmButtonCallback; |
952 | str_copy(dst&: m_aPopupButtons[BUTTON_CANCEL].m_aLabel, src: pCancelButtonLabel); |
953 | m_aPopupButtons[BUTTON_CANCEL].m_NextPopup = CancelNextPopup; |
954 | m_aPopupButtons[BUTTON_CANCEL].m_pfnCallback = pfnCancelButtonCallback; |
955 | m_Popup = POPUP_CONFIRM; |
956 | } |
957 | |
958 | void CMenus::(const char *pTopic, const char *pBody, const char *pButton, std::chrono::nanoseconds Duration) |
959 | { |
960 | // no multiline support for console |
961 | std::string BodyStr = pBody; |
962 | while(BodyStr.find(c: '\n') != std::string::npos) |
963 | BodyStr.replace(pos: BodyStr.find(c: '\n'), n1: 1, s: " " ); |
964 | dbg_msg(sys: pTopic, fmt: "%s" , BodyStr.c_str()); |
965 | |
966 | // reset active item |
967 | Ui()->SetActiveItem(nullptr); |
968 | |
969 | str_copy(dst&: m_aMessageTopic, src: pTopic); |
970 | str_copy(dst&: m_aMessageBody, src: pBody); |
971 | str_copy(dst&: m_aMessageButton, src: pButton); |
972 | m_Popup = POPUP_WARNING; |
973 | SetActive(true); |
974 | |
975 | m_PopupWarningDuration = Duration; |
976 | m_PopupWarningLastTime = time_get_nanoseconds(); |
977 | } |
978 | |
979 | bool CMenus::CanDisplayWarning() const |
980 | { |
981 | return m_Popup == POPUP_NONE; |
982 | } |
983 | |
984 | void CMenus::() |
985 | { |
986 | Ui()->MapScreen(); |
987 | Ui()->ResetMouseSlow(); |
988 | |
989 | static int s_Frame = 0; |
990 | if(s_Frame == 0) |
991 | { |
992 | RefreshBrowserTab(Force: true); |
993 | s_Frame++; |
994 | } |
995 | else if(s_Frame == 1) |
996 | { |
997 | UpdateMusicState(); |
998 | s_Frame++; |
999 | } |
1000 | else |
1001 | { |
1002 | UpdateCommunityIcons(); |
1003 | } |
1004 | |
1005 | if(ServerBrowser()->DDNetInfoAvailable()) |
1006 | { |
1007 | // Initially add DDNet as favorite community and select its tab. |
1008 | // This must be delayed until the DDNet info is available. |
1009 | if(m_CreateDefaultFavoriteCommunities) |
1010 | { |
1011 | m_CreateDefaultFavoriteCommunities = false; |
1012 | if(ServerBrowser()->Community(pCommunityId: IServerBrowser::COMMUNITY_DDNET) != nullptr) |
1013 | { |
1014 | ServerBrowser()->FavoriteCommunitiesFilter().Clear(); |
1015 | ServerBrowser()->FavoriteCommunitiesFilter().Add(pElement: IServerBrowser::COMMUNITY_DDNET); |
1016 | SetMenuPage(PAGE_FAVORITE_COMMUNITY_1); |
1017 | ServerBrowser()->Refresh(Type: IServerBrowser::TYPE_FAVORITE_COMMUNITY_1); |
1018 | } |
1019 | } |
1020 | |
1021 | if(m_JoinTutorial && m_Popup == POPUP_NONE && !ServerBrowser()->IsGettingServerlist()) |
1022 | { |
1023 | m_JoinTutorial = false; |
1024 | // This is only reached on first launch, when the DDNet community tab has been created and |
1025 | // activated by default, so the server info for the tutorial server should be available. |
1026 | const char *pAddr = ServerBrowser()->GetTutorialServer(); |
1027 | if(pAddr) |
1028 | { |
1029 | Client()->Connect(pAddress: pAddr); |
1030 | } |
1031 | } |
1032 | } |
1033 | |
1034 | // Determine the client state once before rendering because it can change |
1035 | // while rendering which causes frames with broken user interface. |
1036 | const IClient::EClientState ClientState = Client()->State(); |
1037 | |
1038 | if(ClientState == IClient::STATE_ONLINE || ClientState == IClient::STATE_DEMOPLAYBACK) |
1039 | { |
1040 | ms_ColorTabbarInactive = ms_ColorTabbarInactiveIngame; |
1041 | ms_ColorTabbarActive = ms_ColorTabbarActiveIngame; |
1042 | ms_ColorTabbarHover = ms_ColorTabbarHoverIngame; |
1043 | } |
1044 | else |
1045 | { |
1046 | if(!GameClient()->m_MenuBackground.Render()) |
1047 | { |
1048 | RenderBackground(); |
1049 | } |
1050 | ms_ColorTabbarInactive = ms_ColorTabbarInactiveOutgame; |
1051 | ms_ColorTabbarActive = ms_ColorTabbarActiveOutgame; |
1052 | ms_ColorTabbarHover = ms_ColorTabbarHoverOutgame; |
1053 | } |
1054 | |
1055 | CUIRect Screen = *Ui()->Screen(); |
1056 | if(Client()->State() != IClient::STATE_DEMOPLAYBACK || m_Popup != POPUP_NONE) |
1057 | { |
1058 | Screen.Margin(Cut: 10.0f, pOtherRect: &Screen); |
1059 | } |
1060 | |
1061 | switch(ClientState) |
1062 | { |
1063 | case IClient::STATE_QUITTING: |
1064 | case IClient::STATE_RESTARTING: |
1065 | // Render nothing except menu background. This should not happen for more than one frame. |
1066 | return; |
1067 | |
1068 | case IClient::STATE_CONNECTING: |
1069 | RenderPopupConnecting(Screen); |
1070 | break; |
1071 | |
1072 | case IClient::STATE_LOADING: |
1073 | RenderPopupLoading(Screen); |
1074 | break; |
1075 | |
1076 | case IClient::STATE_OFFLINE: |
1077 | if(m_Popup != POPUP_NONE) |
1078 | { |
1079 | RenderPopupFullscreen(Screen); |
1080 | } |
1081 | else if(m_ShowStart) |
1082 | { |
1083 | RenderStartMenu(MainView: Screen); |
1084 | } |
1085 | else |
1086 | { |
1087 | CUIRect TabBar, MainView; |
1088 | Screen.HSplitTop(Cut: 24.0f, pTop: &TabBar, pBottom: &MainView); |
1089 | |
1090 | if(m_MenuPage == PAGE_NEWS) |
1091 | { |
1092 | RenderNews(MainView); |
1093 | } |
1094 | else if(m_MenuPage >= PAGE_INTERNET && m_MenuPage <= PAGE_FAVORITE_COMMUNITY_5) |
1095 | { |
1096 | RenderServerbrowser(MainView); |
1097 | } |
1098 | else if(m_MenuPage == PAGE_DEMOS) |
1099 | { |
1100 | RenderDemoBrowser(MainView); |
1101 | } |
1102 | else if(m_MenuPage == PAGE_SETTINGS) |
1103 | { |
1104 | RenderSettings(MainView); |
1105 | } |
1106 | else |
1107 | { |
1108 | dbg_assert(false, "m_MenuPage invalid" ); |
1109 | } |
1110 | |
1111 | RenderMenubar(Box: TabBar, ClientState); |
1112 | } |
1113 | break; |
1114 | |
1115 | case IClient::STATE_ONLINE: |
1116 | if(m_Popup != POPUP_NONE) |
1117 | { |
1118 | RenderPopupFullscreen(Screen); |
1119 | } |
1120 | else |
1121 | { |
1122 | CUIRect TabBar, MainView; |
1123 | Screen.HSplitTop(Cut: 24.0f, pTop: &TabBar, pBottom: &MainView); |
1124 | |
1125 | if(m_GamePage == PAGE_GAME) |
1126 | { |
1127 | RenderGame(MainView); |
1128 | RenderIngameHint(); |
1129 | } |
1130 | else if(m_GamePage == PAGE_PLAYERS) |
1131 | { |
1132 | RenderPlayers(MainView); |
1133 | } |
1134 | else if(m_GamePage == PAGE_SERVER_INFO) |
1135 | { |
1136 | RenderServerInfo(MainView); |
1137 | } |
1138 | else if(m_GamePage == PAGE_NETWORK) |
1139 | { |
1140 | RenderInGameNetwork(MainView); |
1141 | } |
1142 | else if(m_GamePage == PAGE_GHOST) |
1143 | { |
1144 | RenderGhost(MainView); |
1145 | } |
1146 | else if(m_GamePage == PAGE_CALLVOTE) |
1147 | { |
1148 | RenderServerControl(MainView); |
1149 | } |
1150 | else if(m_GamePage == PAGE_SETTINGS) |
1151 | { |
1152 | RenderSettings(MainView); |
1153 | } |
1154 | else |
1155 | { |
1156 | dbg_assert(false, "m_GamePage invalid" ); |
1157 | } |
1158 | |
1159 | RenderMenubar(Box: TabBar, ClientState); |
1160 | } |
1161 | break; |
1162 | |
1163 | case IClient::STATE_DEMOPLAYBACK: |
1164 | if(m_Popup != POPUP_NONE) |
1165 | { |
1166 | RenderPopupFullscreen(Screen); |
1167 | } |
1168 | else |
1169 | { |
1170 | RenderDemoPlayer(MainView: Screen); |
1171 | } |
1172 | break; |
1173 | } |
1174 | |
1175 | Ui()->RenderPopupMenus(); |
1176 | |
1177 | // Prevent UI elements from being hovered while a key reader is active |
1178 | if(m_Binder.m_TakeKey) |
1179 | { |
1180 | Ui()->SetHotItem(nullptr); |
1181 | } |
1182 | |
1183 | // Handle this escape hotkey after popup menus |
1184 | if(!m_ShowStart && ClientState == IClient::STATE_OFFLINE && Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
1185 | { |
1186 | m_ShowStart = true; |
1187 | } |
1188 | } |
1189 | |
1190 | void CMenus::(CUIRect Screen) |
1191 | { |
1192 | char aBuf[1536]; |
1193 | const char *pTitle = "" ; |
1194 | const char * = "" ; |
1195 | const char *pButtonText = "" ; |
1196 | bool TopAlign = false; |
1197 | |
1198 | ColorRGBA BgColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f); |
1199 | if(m_Popup == POPUP_MESSAGE || m_Popup == POPUP_CONFIRM) |
1200 | { |
1201 | pTitle = m_aPopupTitle; |
1202 | pExtraText = m_aPopupMessage; |
1203 | TopAlign = true; |
1204 | } |
1205 | else if(m_Popup == POPUP_DISCONNECTED) |
1206 | { |
1207 | pTitle = Localize(pStr: "Disconnected" ); |
1208 | pExtraText = Client()->ErrorString(); |
1209 | pButtonText = Localize(pStr: "Ok" ); |
1210 | if(Client()->ReconnectTime() > 0) |
1211 | { |
1212 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: Localize(pStr: "Reconnect in %d sec" ), (int)((Client()->ReconnectTime() - time_get()) / time_freq())); |
1213 | pTitle = Client()->ErrorString(); |
1214 | pExtraText = aBuf; |
1215 | pButtonText = Localize(pStr: "Abort" ); |
1216 | } |
1217 | } |
1218 | else if(m_Popup == POPUP_RENAME_DEMO) |
1219 | { |
1220 | dbg_assert(m_DemolistSelectedIndex >= 0, "m_DemolistSelectedIndex invalid for POPUP_RENAME_DEMO" ); |
1221 | pTitle = m_vpFilteredDemos[m_DemolistSelectedIndex]->m_IsDir ? Localize(pStr: "Rename folder" ) : Localize(pStr: "Rename demo" ); |
1222 | } |
1223 | #if defined(CONF_VIDEORECORDER) |
1224 | else if(m_Popup == POPUP_RENDER_DEMO) |
1225 | { |
1226 | pTitle = Localize(pStr: "Render demo" ); |
1227 | } |
1228 | else if(m_Popup == POPUP_RENDER_DONE) |
1229 | { |
1230 | pTitle = Localize(pStr: "Render complete" ); |
1231 | } |
1232 | #endif |
1233 | else if(m_Popup == POPUP_PASSWORD) |
1234 | { |
1235 | pTitle = Localize(pStr: "Password incorrect" ); |
1236 | pButtonText = Localize(pStr: "Try again" ); |
1237 | } |
1238 | else if(m_Popup == POPUP_RESTART) |
1239 | { |
1240 | pTitle = Localize(pStr: "Restart" ); |
1241 | pExtraText = Localize(pStr: "Are you sure that you want to restart?" ); |
1242 | } |
1243 | else if(m_Popup == POPUP_QUIT) |
1244 | { |
1245 | pTitle = Localize(pStr: "Quit" ); |
1246 | pExtraText = Localize(pStr: "Are you sure that you want to quit?" ); |
1247 | } |
1248 | else if(m_Popup == POPUP_FIRST_LAUNCH) |
1249 | { |
1250 | pTitle = Localize(pStr: "Welcome to DDNet" ); |
1251 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s\n\n%s\n\n%s\n\n%s" , |
1252 | Localize(pStr: "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." ), |
1253 | Localize(pStr: "Use k key to kill (restart), q to pause and watch other players. See settings for other key binds." ), |
1254 | Localize(pStr: "It's recommended that you check the settings to adjust them to your liking before joining a server." ), |
1255 | Localize(pStr: "Please enter your nickname below." )); |
1256 | pExtraText = aBuf; |
1257 | pButtonText = Localize(pStr: "Ok" ); |
1258 | TopAlign = true; |
1259 | } |
1260 | else if(m_Popup == POPUP_POINTS) |
1261 | { |
1262 | pTitle = Localize(pStr: "Existing Player" ); |
1263 | if(Client()->Points() > 50) |
1264 | { |
1265 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: Localize(pStr: "Your nickname '%s' is already used (%d points). Do you still want to use it?" ), Client()->PlayerName(), Client()->Points()); |
1266 | pExtraText = aBuf; |
1267 | TopAlign = true; |
1268 | } |
1269 | else if(Client()->Points() >= 0) |
1270 | { |
1271 | m_Popup = POPUP_NONE; |
1272 | } |
1273 | else |
1274 | { |
1275 | pExtraText = Localize(pStr: "Checking for existing player with your name" ); |
1276 | } |
1277 | } |
1278 | else if(m_Popup == POPUP_WARNING) |
1279 | { |
1280 | BgColor = ColorRGBA(0.5f, 0.0f, 0.0f, 0.7f); |
1281 | pTitle = m_aMessageTopic; |
1282 | pExtraText = m_aMessageBody; |
1283 | pButtonText = m_aMessageButton; |
1284 | TopAlign = true; |
1285 | } |
1286 | |
1287 | CUIRect Box, Part; |
1288 | Box = Screen; |
1289 | if(m_Popup != POPUP_FIRST_LAUNCH) |
1290 | Box.Margin(Cut: 150.0f, pOtherRect: &Box); |
1291 | |
1292 | // render the box |
1293 | Box.Draw(Color: BgColor, Corners: IGraphics::CORNER_ALL, Rounding: 15.0f); |
1294 | |
1295 | Box.HSplitTop(Cut: 20.f, pTop: &Part, pBottom: &Box); |
1296 | Box.HSplitTop(Cut: 24.f, pTop: &Part, pBottom: &Box); |
1297 | Part.VMargin(Cut: 20.f, pOtherRect: &Part); |
1298 | SLabelProperties Props; |
1299 | Props.m_MaxWidth = (int)Part.w; |
1300 | |
1301 | if(TextRender()->TextWidth(Size: 24.f, pText: pTitle, StrLength: -1, LineWidth: -1.0f) > Part.w) |
1302 | Ui()->DoLabel(pRect: &Part, pText: pTitle, Size: 24.f, Align: TEXTALIGN_ML, LabelProps: Props); |
1303 | else |
1304 | Ui()->DoLabel(pRect: &Part, pText: pTitle, Size: 24.f, Align: TEXTALIGN_MC); |
1305 | |
1306 | Box.HSplitTop(Cut: 20.f, pTop: &Part, pBottom: &Box); |
1307 | Box.HSplitTop(Cut: 24.f, pTop: &Part, pBottom: &Box); |
1308 | Part.VMargin(Cut: 20.f, pOtherRect: &Part); |
1309 | |
1310 | float FontSize = m_Popup == POPUP_FIRST_LAUNCH ? 16.0f : 20.f; |
1311 | |
1312 | Props.m_MaxWidth = (int)Part.w; |
1313 | if(TopAlign) |
1314 | Ui()->DoLabel(pRect: &Part, pText: pExtraText, Size: FontSize, Align: TEXTALIGN_TL, LabelProps: Props); |
1315 | else if(TextRender()->TextWidth(Size: FontSize, pText: pExtraText, StrLength: -1, LineWidth: -1.0f) > Part.w) |
1316 | Ui()->DoLabel(pRect: &Part, pText: pExtraText, Size: FontSize, Align: TEXTALIGN_ML, LabelProps: Props); |
1317 | else |
1318 | Ui()->DoLabel(pRect: &Part, pText: pExtraText, Size: FontSize, Align: TEXTALIGN_MC); |
1319 | |
1320 | if(m_Popup == POPUP_MESSAGE || m_Popup == POPUP_CONFIRM) |
1321 | { |
1322 | CUIRect ButtonBar; |
1323 | Box.HSplitBottom(Cut: 20.0f, pTop: &Box, pBottom: nullptr); |
1324 | Box.HSplitBottom(Cut: 24.0f, pTop: &Box, pBottom: &ButtonBar); |
1325 | ButtonBar.VMargin(Cut: 100.0f, pOtherRect: &ButtonBar); |
1326 | |
1327 | if(m_Popup == POPUP_MESSAGE) |
1328 | { |
1329 | static CButtonContainer s_ButtonConfirm; |
1330 | if(DoButton_Menu(pButtonContainer: &s_ButtonConfirm, pText: m_aPopupButtons[BUTTON_CONFIRM].m_aLabel, Checked: 0, pRect: &ButtonBar) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER)) |
1331 | { |
1332 | m_Popup = m_aPopupButtons[BUTTON_CONFIRM].m_NextPopup; |
1333 | (this->*m_aPopupButtons[BUTTON_CONFIRM].m_pfnCallback)(); |
1334 | } |
1335 | } |
1336 | else if(m_Popup == POPUP_CONFIRM) |
1337 | { |
1338 | CUIRect CancelButton, ConfirmButton; |
1339 | ButtonBar.VSplitMid(pLeft: &CancelButton, pRight: &ConfirmButton, Spacing: 40.0f); |
1340 | |
1341 | static CButtonContainer s_ButtonCancel; |
1342 | if(DoButton_Menu(pButtonContainer: &s_ButtonCancel, pText: m_aPopupButtons[BUTTON_CANCEL].m_aLabel, Checked: 0, pRect: &CancelButton) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
1343 | { |
1344 | m_Popup = m_aPopupButtons[BUTTON_CANCEL].m_NextPopup; |
1345 | (this->*m_aPopupButtons[BUTTON_CANCEL].m_pfnCallback)(); |
1346 | } |
1347 | |
1348 | static CButtonContainer s_ButtonConfirm; |
1349 | if(DoButton_Menu(pButtonContainer: &s_ButtonConfirm, pText: m_aPopupButtons[BUTTON_CONFIRM].m_aLabel, Checked: 0, pRect: &ConfirmButton) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER)) |
1350 | { |
1351 | m_Popup = m_aPopupButtons[BUTTON_CONFIRM].m_NextPopup; |
1352 | (this->*m_aPopupButtons[BUTTON_CONFIRM].m_pfnCallback)(); |
1353 | } |
1354 | } |
1355 | } |
1356 | else if(m_Popup == POPUP_QUIT || m_Popup == POPUP_RESTART) |
1357 | { |
1358 | CUIRect Yes, No; |
1359 | Box.HSplitBottom(Cut: 20.f, pTop: &Box, pBottom: &Part); |
1360 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1361 | |
1362 | // additional info |
1363 | Box.VMargin(Cut: 20.f, pOtherRect: &Box); |
1364 | if(m_pClient->Editor()->HasUnsavedData()) |
1365 | { |
1366 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s\n\n%s" , Localize(pStr: "There's an unsaved map in the editor, you might want to save it." ), Localize(pStr: "Continue anyway?" )); |
1367 | Props.m_MaxWidth = Part.w - 20.0f; |
1368 | Ui()->DoLabel(pRect: &Box, pText: aBuf, Size: 20.f, Align: TEXTALIGN_ML, LabelProps: Props); |
1369 | } |
1370 | |
1371 | // buttons |
1372 | Part.VMargin(Cut: 80.0f, pOtherRect: &Part); |
1373 | Part.VSplitMid(pLeft: &No, pRight: &Yes); |
1374 | Yes.VMargin(Cut: 20.0f, pOtherRect: &Yes); |
1375 | No.VMargin(Cut: 20.0f, pOtherRect: &No); |
1376 | |
1377 | static CButtonContainer s_ButtonAbort; |
1378 | if(DoButton_Menu(pButtonContainer: &s_ButtonAbort, pText: Localize(pStr: "No" ), Checked: 0, pRect: &No) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
1379 | m_Popup = POPUP_NONE; |
1380 | |
1381 | static CButtonContainer s_ButtonTryAgain; |
1382 | if(DoButton_Menu(pButtonContainer: &s_ButtonTryAgain, pText: Localize(pStr: "Yes" ), Checked: 0, pRect: &Yes) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER)) |
1383 | { |
1384 | if(m_Popup == POPUP_RESTART) |
1385 | { |
1386 | m_Popup = POPUP_NONE; |
1387 | Client()->Restart(); |
1388 | } |
1389 | else |
1390 | { |
1391 | m_Popup = POPUP_NONE; |
1392 | Client()->Quit(); |
1393 | } |
1394 | } |
1395 | } |
1396 | else if(m_Popup == POPUP_PASSWORD) |
1397 | { |
1398 | CUIRect Label, TextBox, TryAgain, Abort; |
1399 | |
1400 | Box.HSplitBottom(Cut: 20.f, pTop: &Box, pBottom: &Part); |
1401 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1402 | Part.VMargin(Cut: 80.0f, pOtherRect: &Part); |
1403 | |
1404 | Part.VSplitMid(pLeft: &Abort, pRight: &TryAgain); |
1405 | |
1406 | TryAgain.VMargin(Cut: 20.0f, pOtherRect: &TryAgain); |
1407 | Abort.VMargin(Cut: 20.0f, pOtherRect: &Abort); |
1408 | |
1409 | static CButtonContainer s_ButtonAbort; |
1410 | if(DoButton_Menu(pButtonContainer: &s_ButtonAbort, pText: Localize(pStr: "Abort" ), Checked: 0, pRect: &Abort) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
1411 | m_Popup = POPUP_NONE; |
1412 | |
1413 | static CButtonContainer s_ButtonTryAgain; |
1414 | if(DoButton_Menu(pButtonContainer: &s_ButtonTryAgain, pText: Localize(pStr: "Try again" ), Checked: 0, pRect: &TryAgain) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER)) |
1415 | { |
1416 | char aAddr[NETADDR_MAXSTRSIZE]; |
1417 | net_addr_str(addr: &Client()->ServerAddress(), string: aAddr, max_length: sizeof(aAddr), add_port: true); |
1418 | Client()->Connect(pAddress: aAddr, pPassword: g_Config.m_Password); |
1419 | } |
1420 | |
1421 | Box.HSplitBottom(Cut: 60.f, pTop: &Box, pBottom: &Part); |
1422 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1423 | |
1424 | Part.VSplitLeft(Cut: 60.0f, pLeft: 0, pRight: &Label); |
1425 | Label.VSplitLeft(Cut: 100.0f, pLeft: 0, pRight: &TextBox); |
1426 | TextBox.VSplitLeft(Cut: 20.0f, pLeft: 0, pRight: &TextBox); |
1427 | TextBox.VSplitRight(Cut: 60.0f, pLeft: &TextBox, pRight: 0); |
1428 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Password" ), Size: 18.0f, Align: TEXTALIGN_ML); |
1429 | Ui()->DoClearableEditBox(pLineInput: &m_PasswordInput, pRect: &TextBox, FontSize: 12.0f); |
1430 | } |
1431 | else if(m_Popup == POPUP_LANGUAGE) |
1432 | { |
1433 | CUIRect Button; |
1434 | Screen.Margin(Cut: 150.0f, pOtherRect: &Box); |
1435 | Box.HSplitTop(Cut: 20.0f, pTop: nullptr, pBottom: &Box); |
1436 | Box.HSplitBottom(Cut: 20.0f, pTop: &Box, pBottom: nullptr); |
1437 | Box.HSplitBottom(Cut: 24.0f, pTop: &Box, pBottom: &Button); |
1438 | Box.HSplitBottom(Cut: 20.0f, pTop: &Box, pBottom: nullptr); |
1439 | Box.VMargin(Cut: 20.0f, pOtherRect: &Box); |
1440 | const bool Activated = RenderLanguageSelection(MainView: Box); |
1441 | Button.VMargin(Cut: 120.0f, pOtherRect: &Button); |
1442 | |
1443 | static CButtonContainer s_Button; |
1444 | if(DoButton_Menu(pButtonContainer: &s_Button, pText: Localize(pStr: "Ok" ), Checked: 0, pRect: &Button) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER) || Activated) |
1445 | m_Popup = POPUP_FIRST_LAUNCH; |
1446 | } |
1447 | else if(m_Popup == POPUP_RENAME_DEMO) |
1448 | { |
1449 | CUIRect Label, TextBox, Ok, Abort; |
1450 | |
1451 | Box.HSplitBottom(Cut: 20.f, pTop: &Box, pBottom: &Part); |
1452 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1453 | Part.VMargin(Cut: 80.0f, pOtherRect: &Part); |
1454 | |
1455 | Part.VSplitMid(pLeft: &Abort, pRight: &Ok); |
1456 | |
1457 | Ok.VMargin(Cut: 20.0f, pOtherRect: &Ok); |
1458 | Abort.VMargin(Cut: 20.0f, pOtherRect: &Abort); |
1459 | |
1460 | static CButtonContainer s_ButtonAbort; |
1461 | if(DoButton_Menu(pButtonContainer: &s_ButtonAbort, pText: Localize(pStr: "Abort" ), Checked: 0, pRect: &Abort) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
1462 | m_Popup = POPUP_NONE; |
1463 | |
1464 | static CButtonContainer s_ButtonOk; |
1465 | if(DoButton_Menu(pButtonContainer: &s_ButtonOk, pText: Localize(pStr: "Ok" ), Checked: 0, pRect: &Ok) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER)) |
1466 | { |
1467 | m_Popup = POPUP_NONE; |
1468 | // rename demo |
1469 | char aBufOld[IO_MAX_PATH_LENGTH]; |
1470 | str_format(buffer: aBufOld, buffer_size: sizeof(aBufOld), format: "%s/%s" , m_aCurrentDemoFolder, m_vpFilteredDemos[m_DemolistSelectedIndex]->m_aFilename); |
1471 | char aBufNew[IO_MAX_PATH_LENGTH]; |
1472 | str_format(buffer: aBufNew, buffer_size: sizeof(aBufNew), format: "%s/%s" , m_aCurrentDemoFolder, m_DemoRenameInput.GetString()); |
1473 | if(!m_vpFilteredDemos[m_DemolistSelectedIndex]->m_IsDir && !str_endswith(str: aBufNew, suffix: ".demo" )) |
1474 | str_append(dst&: aBufNew, src: ".demo" ); |
1475 | |
1476 | if(Storage()->FileExists(pFilename: aBufNew, Type: m_vpFilteredDemos[m_DemolistSelectedIndex]->m_StorageType)) |
1477 | { |
1478 | PopupMessage(pTitle: Localize(pStr: "Error" ), pMessage: Localize(pStr: "A demo with this name already exists" ), pButtonLabel: Localize(pStr: "Ok" ), NextPopup: POPUP_RENAME_DEMO); |
1479 | } |
1480 | else if(Storage()->FolderExists(pFilename: aBufNew, Type: m_vpFilteredDemos[m_DemolistSelectedIndex]->m_StorageType)) |
1481 | { |
1482 | PopupMessage(pTitle: Localize(pStr: "Error" ), pMessage: Localize(pStr: "A folder with this name already exists" ), pButtonLabel: Localize(pStr: "Ok" ), NextPopup: POPUP_RENAME_DEMO); |
1483 | } |
1484 | else if(Storage()->RenameFile(pOldFilename: aBufOld, pNewFilename: aBufNew, Type: m_vpFilteredDemos[m_DemolistSelectedIndex]->m_StorageType)) |
1485 | { |
1486 | str_copy(dst&: m_aCurrentDemoSelectionName, src: m_DemoRenameInput.GetString()); |
1487 | if(!m_vpFilteredDemos[m_DemolistSelectedIndex]->m_IsDir) |
1488 | fs_split_file_extension(filename: m_DemoRenameInput.GetString(), name: m_aCurrentDemoSelectionName, name_size: sizeof(m_aCurrentDemoSelectionName)); |
1489 | DemolistPopulate(); |
1490 | DemolistOnUpdate(Reset: false); |
1491 | } |
1492 | else |
1493 | { |
1494 | PopupMessage(pTitle: Localize(pStr: "Error" ), pMessage: m_vpFilteredDemos[m_DemolistSelectedIndex]->m_IsDir ? Localize(pStr: "Unable to rename the folder" ) : Localize(pStr: "Unable to rename the demo" ), pButtonLabel: Localize(pStr: "Ok" ), NextPopup: POPUP_RENAME_DEMO); |
1495 | } |
1496 | } |
1497 | |
1498 | Box.HSplitBottom(Cut: 60.f, pTop: &Box, pBottom: &Part); |
1499 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1500 | |
1501 | Part.VSplitLeft(Cut: 60.0f, pLeft: 0, pRight: &Label); |
1502 | Label.VSplitLeft(Cut: 120.0f, pLeft: 0, pRight: &TextBox); |
1503 | TextBox.VSplitLeft(Cut: 20.0f, pLeft: 0, pRight: &TextBox); |
1504 | TextBox.VSplitRight(Cut: 60.0f, pLeft: &TextBox, pRight: 0); |
1505 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "New name:" ), Size: 18.0f, Align: TEXTALIGN_ML); |
1506 | Ui()->DoEditBox(pLineInput: &m_DemoRenameInput, pRect: &TextBox, FontSize: 12.0f); |
1507 | } |
1508 | #if defined(CONF_VIDEORECORDER) |
1509 | else if(m_Popup == POPUP_RENDER_DEMO) |
1510 | { |
1511 | CUIRect Row, Ok, Abort; |
1512 | Box.VMargin(Cut: 60.0f, pOtherRect: &Box); |
1513 | Box.HMargin(Cut: 20.0f, pOtherRect: &Box); |
1514 | Box.HSplitBottom(Cut: 24.0f, pTop: &Box, pBottom: &Row); |
1515 | Box.HSplitBottom(Cut: 40.0f, pTop: &Box, pBottom: nullptr); |
1516 | Row.VMargin(Cut: 40.0f, pOtherRect: &Row); |
1517 | Row.VSplitMid(pLeft: &Abort, pRight: &Ok, Spacing: 40.0f); |
1518 | |
1519 | static CButtonContainer s_ButtonAbort; |
1520 | if(DoButton_Menu(pButtonContainer: &s_ButtonAbort, pText: Localize(pStr: "Abort" ), Checked: 0, pRect: &Abort) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
1521 | { |
1522 | m_DemoRenderInput.Clear(); |
1523 | m_Popup = POPUP_NONE; |
1524 | } |
1525 | |
1526 | static CButtonContainer s_ButtonOk; |
1527 | if(DoButton_Menu(pButtonContainer: &s_ButtonOk, pText: Localize(pStr: "Ok" ), Checked: 0, pRect: &Ok) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER)) |
1528 | { |
1529 | m_Popup = POPUP_NONE; |
1530 | // render video |
1531 | char aVideoPath[IO_MAX_PATH_LENGTH]; |
1532 | str_format(buffer: aVideoPath, buffer_size: sizeof(aVideoPath), format: "videos/%s" , m_DemoRenderInput.GetString()); |
1533 | if(!str_endswith(str: aVideoPath, suffix: ".mp4" )) |
1534 | str_append(dst&: aVideoPath, src: ".mp4" ); |
1535 | if(Storage()->FolderExists(pFilename: aVideoPath, Type: IStorage::TYPE_SAVE)) |
1536 | { |
1537 | PopupMessage(pTitle: Localize(pStr: "Error" ), pMessage: Localize(pStr: "A folder with this name already exists" ), pButtonLabel: Localize(pStr: "Ok" ), NextPopup: POPUP_RENDER_DEMO); |
1538 | } |
1539 | else if(Storage()->FileExists(pFilename: aVideoPath, Type: IStorage::TYPE_SAVE)) |
1540 | { |
1541 | char aMessage[128 + IO_MAX_PATH_LENGTH]; |
1542 | str_format(buffer: aMessage, buffer_size: sizeof(aMessage), format: Localize(pStr: "File '%s' already exists, do you want to overwrite it?" ), m_DemoRenderInput.GetString()); |
1543 | PopupConfirm(pTitle: Localize(pStr: "Replace video" ), pMessage: aMessage, pConfirmButtonLabel: Localize(pStr: "Yes" ), pCancelButtonLabel: Localize(pStr: "No" ), pfnConfirmButtonCallback: &CMenus::PopupConfirmDemoReplaceVideo, ConfirmNextPopup: POPUP_NONE, pfnCancelButtonCallback: &CMenus::DefaultButtonCallback, CancelNextPopup: POPUP_RENDER_DEMO); |
1544 | } |
1545 | else |
1546 | { |
1547 | PopupConfirmDemoReplaceVideo(); |
1548 | } |
1549 | } |
1550 | |
1551 | CUIRect ShowChatCheckbox, UseSoundsCheckbox; |
1552 | Box.HSplitBottom(Cut: 20.0f, pTop: &Box, pBottom: &Row); |
1553 | Box.HSplitBottom(Cut: 10.0f, pTop: &Box, pBottom: nullptr); |
1554 | Row.VSplitMid(pLeft: &ShowChatCheckbox, pRight: &UseSoundsCheckbox, Spacing: 20.0f); |
1555 | |
1556 | if(DoButton_CheckBox(pId: &g_Config.m_ClVideoShowChat, pText: Localize(pStr: "Show chat" ), Checked: g_Config.m_ClVideoShowChat, pRect: &ShowChatCheckbox)) |
1557 | g_Config.m_ClVideoShowChat ^= 1; |
1558 | |
1559 | if(DoButton_CheckBox(pId: &g_Config.m_ClVideoSndEnable, pText: Localize(pStr: "Use sounds" ), Checked: g_Config.m_ClVideoSndEnable, pRect: &UseSoundsCheckbox)) |
1560 | g_Config.m_ClVideoSndEnable ^= 1; |
1561 | |
1562 | CUIRect ShowHudButton; |
1563 | Box.HSplitBottom(Cut: 20.0f, pTop: &Box, pBottom: &Row); |
1564 | Row.VSplitMid(pLeft: &Row, pRight: &ShowHudButton, Spacing: 20.0f); |
1565 | |
1566 | if(DoButton_CheckBox(pId: &g_Config.m_ClVideoShowhud, pText: Localize(pStr: "Show ingame HUD" ), Checked: g_Config.m_ClVideoShowhud, pRect: &ShowHudButton)) |
1567 | g_Config.m_ClVideoShowhud ^= 1; |
1568 | |
1569 | // slowdown |
1570 | CUIRect SlowDownButton; |
1571 | Row.VSplitLeft(Cut: 20.0f, pLeft: &SlowDownButton, pRight: &Row); |
1572 | Row.VSplitLeft(Cut: 5.0f, pLeft: nullptr, pRight: &Row); |
1573 | static CButtonContainer s_SlowDownButton; |
1574 | if(DoButton_FontIcon(pButtonContainer: &s_SlowDownButton, pText: FONT_ICON_BACKWARD, Checked: 0, pRect: &SlowDownButton, Corners: IGraphics::CORNER_ALL)) |
1575 | m_Speed = clamp(val: m_Speed - 1, lo: 0, hi: (int)(g_DemoSpeeds - 1)); |
1576 | |
1577 | // paused |
1578 | CUIRect PausedButton; |
1579 | Row.VSplitLeft(Cut: 20.0f, pLeft: &PausedButton, pRight: &Row); |
1580 | Row.VSplitLeft(Cut: 5.0f, pLeft: nullptr, pRight: &Row); |
1581 | static CButtonContainer s_PausedButton; |
1582 | if(DoButton_FontIcon(pButtonContainer: &s_PausedButton, pText: FONT_ICON_PAUSE, Checked: 0, pRect: &PausedButton, Corners: IGraphics::CORNER_ALL)) |
1583 | m_StartPaused ^= 1; |
1584 | |
1585 | // fastforward |
1586 | CUIRect FastForwardButton; |
1587 | Row.VSplitLeft(Cut: 20.0f, pLeft: &FastForwardButton, pRight: &Row); |
1588 | Row.VSplitLeft(Cut: 8.0f, pLeft: nullptr, pRight: &Row); |
1589 | static CButtonContainer s_FastForwardButton; |
1590 | if(DoButton_FontIcon(pButtonContainer: &s_FastForwardButton, pText: FONT_ICON_FORWARD, Checked: 0, pRect: &FastForwardButton, Corners: IGraphics::CORNER_ALL)) |
1591 | m_Speed = clamp(val: m_Speed + 1, lo: 0, hi: (int)(g_DemoSpeeds - 1)); |
1592 | |
1593 | // speed meter |
1594 | char aBuffer[128]; |
1595 | const char *pPaused = m_StartPaused ? Localize(pStr: "(paused)" ) : "" ; |
1596 | str_format(buffer: aBuffer, buffer_size: sizeof(aBuffer), format: "%s: ×%g %s" , Localize(pStr: "Speed" ), g_aSpeeds[m_Speed], pPaused); |
1597 | Ui()->DoLabel(pRect: &Row, pText: aBuffer, Size: 12.8f, Align: TEXTALIGN_ML); |
1598 | Box.HSplitBottom(Cut: 16.0f, pTop: &Box, pBottom: nullptr); |
1599 | Box.HSplitBottom(Cut: 24.0f, pTop: &Box, pBottom: &Row); |
1600 | |
1601 | CUIRect Label, TextBox; |
1602 | Row.VSplitLeft(Cut: 110.0f, pLeft: &Label, pRight: &TextBox); |
1603 | TextBox.VSplitLeft(Cut: 10.0f, pLeft: nullptr, pRight: &TextBox); |
1604 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Video name:" ), Size: 12.8f, Align: TEXTALIGN_ML); |
1605 | Ui()->DoEditBox(pLineInput: &m_DemoRenderInput, pRect: &TextBox, FontSize: 12.8f); |
1606 | } |
1607 | else if(m_Popup == POPUP_RENDER_DONE) |
1608 | { |
1609 | CUIRect Ok, OpenFolder; |
1610 | |
1611 | char aFilePath[IO_MAX_PATH_LENGTH]; |
1612 | char aSaveFolder[IO_MAX_PATH_LENGTH]; |
1613 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, pDir: "videos" , pBuffer: aSaveFolder, BufferSize: sizeof(aSaveFolder)); |
1614 | str_format(buffer: aFilePath, buffer_size: sizeof(aFilePath), format: "%s/%s.mp4" , aSaveFolder, m_DemoRenderInput.GetString()); |
1615 | |
1616 | Box.HSplitBottom(Cut: 20.f, pTop: &Box, pBottom: &Part); |
1617 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1618 | Part.VMargin(Cut: 80.0f, pOtherRect: &Part); |
1619 | |
1620 | Part.VSplitMid(pLeft: &OpenFolder, pRight: &Ok); |
1621 | |
1622 | Ok.VMargin(Cut: 20.0f, pOtherRect: &Ok); |
1623 | OpenFolder.VMargin(Cut: 20.0f, pOtherRect: &OpenFolder); |
1624 | |
1625 | static CButtonContainer s_ButtonOpenFolder; |
1626 | if(DoButton_Menu(pButtonContainer: &s_ButtonOpenFolder, pText: Localize(pStr: "Videos directory" ), Checked: 0, pRect: &OpenFolder)) |
1627 | { |
1628 | Client()->ViewFile(pFilename: aSaveFolder); |
1629 | } |
1630 | |
1631 | static CButtonContainer s_ButtonOk; |
1632 | if(DoButton_Menu(pButtonContainer: &s_ButtonOk, pText: Localize(pStr: "Ok" ), Checked: 0, pRect: &Ok) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER)) |
1633 | { |
1634 | m_Popup = POPUP_NONE; |
1635 | m_DemoRenderInput.Clear(); |
1636 | } |
1637 | |
1638 | Box.HSplitBottom(Cut: 160.f, pTop: &Box, pBottom: &Part); |
1639 | Part.VMargin(Cut: 20.0f, pOtherRect: &Part); |
1640 | |
1641 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: Localize(pStr: "Video was saved to '%s'" ), aFilePath); |
1642 | |
1643 | SLabelProperties MessageProps; |
1644 | MessageProps.m_MaxWidth = (int)Part.w; |
1645 | Ui()->DoLabel(pRect: &Part, pText: aBuf, Size: 18.0f, Align: TEXTALIGN_TL, LabelProps: MessageProps); |
1646 | } |
1647 | #endif |
1648 | else if(m_Popup == POPUP_FIRST_LAUNCH) |
1649 | { |
1650 | CUIRect Label, TextBox, Skip, Join; |
1651 | |
1652 | Box.HSplitBottom(Cut: 20.f, pTop: &Box, pBottom: &Part); |
1653 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1654 | Part.VMargin(Cut: 80.0f, pOtherRect: &Part); |
1655 | Part.VSplitMid(pLeft: &Skip, pRight: &Join); |
1656 | Skip.VMargin(Cut: 20.0f, pOtherRect: &Skip); |
1657 | Join.VMargin(Cut: 20.0f, pOtherRect: &Join); |
1658 | |
1659 | static CButtonContainer s_JoinTutorialButton; |
1660 | if(DoButton_Menu(pButtonContainer: &s_JoinTutorialButton, pText: Localize(pStr: "Join Tutorial Server" ), Checked: 0, pRect: &Join) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER)) |
1661 | { |
1662 | m_JoinTutorial = true; |
1663 | Client()->RequestDDNetInfo(); |
1664 | m_Popup = g_Config.m_BrIndicateFinished ? POPUP_POINTS : POPUP_NONE; |
1665 | } |
1666 | |
1667 | static CButtonContainer s_SkipTutorialButton; |
1668 | if(DoButton_Menu(pButtonContainer: &s_SkipTutorialButton, pText: Localize(pStr: "Skip Tutorial" ), Checked: 0, pRect: &Skip) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
1669 | { |
1670 | m_JoinTutorial = false; |
1671 | Client()->RequestDDNetInfo(); |
1672 | m_Popup = g_Config.m_BrIndicateFinished ? POPUP_POINTS : POPUP_NONE; |
1673 | } |
1674 | |
1675 | Box.HSplitBottom(Cut: 20.f, pTop: &Box, pBottom: &Part); |
1676 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1677 | |
1678 | Part.VSplitLeft(Cut: 30.0f, pLeft: 0, pRight: &Part); |
1679 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s\n(%s)" , |
1680 | Localize(pStr: "Show DDNet map finishes in server browser" ), |
1681 | Localize(pStr: "transmits your player name to info.ddnet.org" )); |
1682 | |
1683 | if(DoButton_CheckBox(pId: &g_Config.m_BrIndicateFinished, pText: aBuf, Checked: g_Config.m_BrIndicateFinished, pRect: &Part)) |
1684 | g_Config.m_BrIndicateFinished ^= 1; |
1685 | |
1686 | Box.HSplitBottom(Cut: 20.f, pTop: &Box, pBottom: &Part); |
1687 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1688 | |
1689 | Part.VSplitLeft(Cut: 60.0f, pLeft: 0, pRight: &Label); |
1690 | Label.VSplitLeft(Cut: 100.0f, pLeft: 0, pRight: &TextBox); |
1691 | TextBox.VSplitLeft(Cut: 20.0f, pLeft: 0, pRight: &TextBox); |
1692 | TextBox.VSplitRight(Cut: 60.0f, pLeft: &TextBox, pRight: 0); |
1693 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Nickname" ), Size: 16.0f, Align: TEXTALIGN_ML); |
1694 | static CLineInput s_PlayerNameInput(g_Config.m_PlayerName, sizeof(g_Config.m_PlayerName)); |
1695 | s_PlayerNameInput.SetEmptyText(Client()->PlayerName()); |
1696 | Ui()->DoEditBox(pLineInput: &s_PlayerNameInput, pRect: &TextBox, FontSize: 12.0f); |
1697 | } |
1698 | else if(m_Popup == POPUP_POINTS) |
1699 | { |
1700 | CUIRect Yes, No; |
1701 | |
1702 | Box.HSplitBottom(Cut: 20.f, pTop: &Box, pBottom: &Part); |
1703 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1704 | Part.VMargin(Cut: 80.0f, pOtherRect: &Part); |
1705 | |
1706 | Part.VSplitMid(pLeft: &No, pRight: &Yes); |
1707 | |
1708 | Yes.VMargin(Cut: 20.0f, pOtherRect: &Yes); |
1709 | No.VMargin(Cut: 20.0f, pOtherRect: &No); |
1710 | |
1711 | static CButtonContainer s_ButtonNo; |
1712 | if(DoButton_Menu(pButtonContainer: &s_ButtonNo, pText: Localize(pStr: "No" ), Checked: 0, pRect: &No) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
1713 | m_Popup = POPUP_FIRST_LAUNCH; |
1714 | |
1715 | static CButtonContainer s_ButtonYes; |
1716 | if(DoButton_Menu(pButtonContainer: &s_ButtonYes, pText: Localize(pStr: "Yes" ), Checked: 0, pRect: &Yes) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER)) |
1717 | m_Popup = POPUP_NONE; |
1718 | } |
1719 | else if(m_Popup == POPUP_WARNING) |
1720 | { |
1721 | Box.HSplitBottom(Cut: 20.f, pTop: &Box, pBottom: &Part); |
1722 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1723 | Part.VMargin(Cut: 120.0f, pOtherRect: &Part); |
1724 | |
1725 | static CButtonContainer s_Button; |
1726 | if(DoButton_Menu(pButtonContainer: &s_Button, pText: pButtonText, Checked: 0, pRect: &Part) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER) || (m_PopupWarningDuration > 0s && time_get_nanoseconds() - m_PopupWarningLastTime >= m_PopupWarningDuration)) |
1727 | { |
1728 | m_Popup = POPUP_NONE; |
1729 | SetActive(false); |
1730 | } |
1731 | } |
1732 | else |
1733 | { |
1734 | Box.HSplitBottom(Cut: 20.f, pTop: &Box, pBottom: &Part); |
1735 | Box.HSplitBottom(Cut: 24.f, pTop: &Box, pBottom: &Part); |
1736 | Part.VMargin(Cut: 120.0f, pOtherRect: &Part); |
1737 | |
1738 | static CButtonContainer s_Button; |
1739 | if(DoButton_Menu(pButtonContainer: &s_Button, pText: pButtonText, Checked: 0, pRect: &Part) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ENTER)) |
1740 | { |
1741 | if(m_Popup == POPUP_DISCONNECTED && Client()->ReconnectTime() > 0) |
1742 | Client()->SetReconnectTime(0); |
1743 | m_Popup = POPUP_NONE; |
1744 | } |
1745 | } |
1746 | |
1747 | if(m_Popup == POPUP_NONE) |
1748 | Ui()->SetActiveItem(nullptr); |
1749 | } |
1750 | |
1751 | void CMenus::(CUIRect Screen) |
1752 | { |
1753 | const float FontSize = 20.0f; |
1754 | |
1755 | CUIRect Box, Label; |
1756 | Screen.Margin(Cut: 150.0f, pOtherRect: &Box); |
1757 | Box.Draw(Color: ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f), Corners: IGraphics::CORNER_ALL, Rounding: 15.0f); |
1758 | Box.Margin(Cut: 20.0f, pOtherRect: &Box); |
1759 | |
1760 | Box.HSplitTop(Cut: 24.0f, pTop: &Label, pBottom: &Box); |
1761 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Connecting to" ), Size: 24.0f, Align: TEXTALIGN_MC); |
1762 | |
1763 | Box.HSplitTop(Cut: 20.0f, pTop: nullptr, pBottom: &Box); |
1764 | Box.HSplitTop(Cut: 24.0f, pTop: &Label, pBottom: &Box); |
1765 | SLabelProperties Props; |
1766 | Props.m_MaxWidth = Label.w; |
1767 | Props.m_EllipsisAtEnd = true; |
1768 | Ui()->DoLabel(pRect: &Label, pText: Client()->ConnectAddressString(), Size: FontSize, Align: TEXTALIGN_MC, LabelProps: Props); |
1769 | |
1770 | if(time_get() - Client()->StateStartTime() > time_freq()) |
1771 | { |
1772 | const char *pConnectivityLabel = "" ; |
1773 | switch(Client()->UdpConnectivity(NetType: Client()->ConnectNetTypes())) |
1774 | { |
1775 | case IClient::CONNECTIVITY_UNKNOWN: |
1776 | break; |
1777 | case IClient::CONNECTIVITY_CHECKING: |
1778 | pConnectivityLabel = Localize(pStr: "Trying to determine UDP connectivity…" ); |
1779 | break; |
1780 | case IClient::CONNECTIVITY_UNREACHABLE: |
1781 | pConnectivityLabel = Localize(pStr: "UDP seems to be filtered." ); |
1782 | break; |
1783 | case IClient::CONNECTIVITY_DIFFERING_UDP_TCP_IP_ADDRESSES: |
1784 | pConnectivityLabel = Localize(pStr: "UDP and TCP IP addresses seem to be different. Try disabling VPN, proxy or network accelerators." ); |
1785 | break; |
1786 | case IClient::CONNECTIVITY_REACHABLE: |
1787 | pConnectivityLabel = Localize(pStr: "No answer from server yet." ); |
1788 | break; |
1789 | } |
1790 | if(pConnectivityLabel[0] != '\0') |
1791 | { |
1792 | Box.HSplitTop(Cut: 20.0f, pTop: nullptr, pBottom: &Box); |
1793 | Box.HSplitTop(Cut: 24.0f, pTop: &Label, pBottom: &Box); |
1794 | SLabelProperties ConnectivityLabelProps; |
1795 | ConnectivityLabelProps.m_MaxWidth = Label.w; |
1796 | if(TextRender()->TextWidth(Size: FontSize, pText: pConnectivityLabel) > Label.w) |
1797 | Ui()->DoLabel(pRect: &Label, pText: pConnectivityLabel, Size: FontSize, Align: TEXTALIGN_ML, LabelProps: ConnectivityLabelProps); |
1798 | else |
1799 | Ui()->DoLabel(pRect: &Label, pText: pConnectivityLabel, Size: FontSize, Align: TEXTALIGN_MC); |
1800 | } |
1801 | } |
1802 | |
1803 | CUIRect Button; |
1804 | Box.HSplitBottom(Cut: 24.0f, pTop: &Box, pBottom: &Button); |
1805 | Button.VMargin(Cut: 100.0f, pOtherRect: &Button); |
1806 | |
1807 | static CButtonContainer s_Button; |
1808 | if(DoButton_Menu(pButtonContainer: &s_Button, pText: Localize(pStr: "Abort" ), Checked: 0, pRect: &Button) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
1809 | { |
1810 | Client()->Disconnect(); |
1811 | Ui()->SetActiveItem(nullptr); |
1812 | RefreshBrowserTab(Force: true); |
1813 | } |
1814 | } |
1815 | |
1816 | void CMenus::(CUIRect Screen) |
1817 | { |
1818 | char aTitle[256]; |
1819 | char aLabel1[128]; |
1820 | char aLabel2[128]; |
1821 | if(Client()->MapDownloadTotalsize() > 0) |
1822 | { |
1823 | const int64_t Now = time_get(); |
1824 | if(Now - m_DownloadLastCheckTime >= time_freq()) |
1825 | { |
1826 | if(m_DownloadLastCheckSize > Client()->MapDownloadAmount()) |
1827 | { |
1828 | // map downloaded restarted |
1829 | m_DownloadLastCheckSize = 0; |
1830 | } |
1831 | |
1832 | // update download speed |
1833 | const float Diff = (Client()->MapDownloadAmount() - m_DownloadLastCheckSize) / ((int)((Now - m_DownloadLastCheckTime) / time_freq())); |
1834 | const float StartDiff = m_DownloadLastCheckSize - 0.0f; |
1835 | if(StartDiff + Diff > 0.0f) |
1836 | m_DownloadSpeed = (Diff / (StartDiff + Diff)) * (Diff / 1.0f) + (StartDiff / (Diff + StartDiff)) * m_DownloadSpeed; |
1837 | else |
1838 | m_DownloadSpeed = 0.0f; |
1839 | m_DownloadLastCheckTime = Now; |
1840 | m_DownloadLastCheckSize = Client()->MapDownloadAmount(); |
1841 | } |
1842 | |
1843 | str_format(buffer: aTitle, buffer_size: sizeof(aTitle), format: "%s: %s" , Localize(pStr: "Downloading map" ), Client()->MapDownloadName()); |
1844 | |
1845 | str_format(buffer: aLabel1, buffer_size: sizeof(aLabel1), format: Localize(pStr: "%d/%d KiB (%.1f KiB/s)" ), Client()->MapDownloadAmount() / 1024, Client()->MapDownloadTotalsize() / 1024, m_DownloadSpeed / 1024.0f); |
1846 | |
1847 | const int SecondsLeft = maximum(a: 1, b: m_DownloadSpeed > 0.0f ? static_cast<int>((Client()->MapDownloadTotalsize() - Client()->MapDownloadAmount()) / m_DownloadSpeed) : 1); |
1848 | const int MinutesLeft = SecondsLeft / 60; |
1849 | if(MinutesLeft > 0) |
1850 | { |
1851 | str_format(buffer: aLabel2, buffer_size: sizeof(aLabel2), format: MinutesLeft == 1 ? Localize(pStr: "%i minute left" ) : Localize(pStr: "%i minutes left" ), MinutesLeft); |
1852 | } |
1853 | else |
1854 | { |
1855 | str_format(buffer: aLabel2, buffer_size: sizeof(aLabel2), format: SecondsLeft == 1 ? Localize(pStr: "%i second left" ) : Localize(pStr: "%i seconds left" ), SecondsLeft); |
1856 | } |
1857 | } |
1858 | else |
1859 | { |
1860 | str_copy(dst&: aTitle, src: Localize(pStr: "Connected" )); |
1861 | switch(Client()->LoadingStateDetail()) |
1862 | { |
1863 | case IClient::LOADING_STATE_DETAIL_INITIAL: |
1864 | str_copy(dst&: aLabel1, src: Localize(pStr: "Getting game info" )); |
1865 | break; |
1866 | case IClient::LOADING_STATE_DETAIL_LOADING_MAP: |
1867 | str_copy(dst&: aLabel1, src: Localize(pStr: "Loading map file from storage" )); |
1868 | break; |
1869 | case IClient::LOADING_STATE_DETAIL_LOADING_DEMO: |
1870 | str_copy(dst&: aLabel1, src: Localize(pStr: "Loading demo file from storage" )); |
1871 | break; |
1872 | case IClient::LOADING_STATE_DETAIL_SENDING_READY: |
1873 | str_copy(dst&: aLabel1, src: Localize(pStr: "Requesting to join the game" )); |
1874 | break; |
1875 | case IClient::LOADING_STATE_DETAIL_GETTING_READY: |
1876 | str_copy(dst&: aLabel1, src: Localize(pStr: "Sending initial client info" )); |
1877 | break; |
1878 | default: |
1879 | dbg_assert(false, "Invalid loading state for RenderPopupLoading" ); |
1880 | break; |
1881 | } |
1882 | aLabel2[0] = '\0'; |
1883 | } |
1884 | |
1885 | const float FontSize = 20.0f; |
1886 | |
1887 | CUIRect Box, Label; |
1888 | Screen.Margin(Cut: 150.0f, pOtherRect: &Box); |
1889 | Box.Draw(Color: ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f), Corners: IGraphics::CORNER_ALL, Rounding: 15.0f); |
1890 | Box.Margin(Cut: 20.0f, pOtherRect: &Box); |
1891 | |
1892 | Box.HSplitTop(Cut: 24.0f, pTop: &Label, pBottom: &Box); |
1893 | Ui()->DoLabel(pRect: &Label, pText: aTitle, Size: 24.0f, Align: TEXTALIGN_MC); |
1894 | |
1895 | Box.HSplitTop(Cut: 20.0f, pTop: nullptr, pBottom: &Box); |
1896 | Box.HSplitTop(Cut: 24.0f, pTop: &Label, pBottom: &Box); |
1897 | Ui()->DoLabel(pRect: &Label, pText: aLabel1, Size: FontSize, Align: TEXTALIGN_MC); |
1898 | |
1899 | if(aLabel2[0] != '\0') |
1900 | { |
1901 | Box.HSplitTop(Cut: 20.0f, pTop: nullptr, pBottom: &Box); |
1902 | Box.HSplitTop(Cut: 24.0f, pTop: &Label, pBottom: &Box); |
1903 | SLabelProperties ; |
1904 | ExtraTextProps.m_MaxWidth = Label.w; |
1905 | if(TextRender()->TextWidth(Size: FontSize, pText: aLabel2) > Label.w) |
1906 | Ui()->DoLabel(pRect: &Label, pText: aLabel2, Size: FontSize, Align: TEXTALIGN_ML, LabelProps: ExtraTextProps); |
1907 | else |
1908 | Ui()->DoLabel(pRect: &Label, pText: aLabel2, Size: FontSize, Align: TEXTALIGN_MC); |
1909 | } |
1910 | |
1911 | if(Client()->MapDownloadTotalsize() > 0) |
1912 | { |
1913 | CUIRect ProgressBar; |
1914 | Box.HSplitTop(Cut: 20.0f, pTop: nullptr, pBottom: &Box); |
1915 | Box.HSplitTop(Cut: 24.0f, pTop: &ProgressBar, pBottom: &Box); |
1916 | ProgressBar.VMargin(Cut: 20.0f, pOtherRect: &ProgressBar); |
1917 | Ui()->RenderProgressBar(ProgressBar, Progress: Client()->MapDownloadAmount() / (float)Client()->MapDownloadTotalsize()); |
1918 | } |
1919 | |
1920 | CUIRect Button; |
1921 | Box.HSplitBottom(Cut: 24.0f, pTop: &Box, pBottom: &Button); |
1922 | Button.VMargin(Cut: 100.0f, pOtherRect: &Button); |
1923 | |
1924 | static CButtonContainer s_Button; |
1925 | if(DoButton_Menu(pButtonContainer: &s_Button, pText: Localize(pStr: "Abort" ), Checked: 0, pRect: &Button) || Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
1926 | { |
1927 | Client()->Disconnect(); |
1928 | Ui()->SetActiveItem(nullptr); |
1929 | RefreshBrowserTab(Force: true); |
1930 | } |
1931 | } |
1932 | |
1933 | #if defined(CONF_VIDEORECORDER) |
1934 | void CMenus::() |
1935 | { |
1936 | char aBuf[IO_MAX_PATH_LENGTH]; |
1937 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s/%s.demo" , m_aCurrentDemoFolder, m_aCurrentDemoSelectionName); |
1938 | char aVideoName[IO_MAX_PATH_LENGTH]; |
1939 | str_copy(dst&: aVideoName, src: m_DemoRenderInput.GetString()); |
1940 | const char *pError = Client()->DemoPlayer_Render(pFilename: aBuf, StorageType: m_DemolistStorageType, pVideoName: aVideoName, SpeedIndex: m_Speed, StartPaused: m_StartPaused); |
1941 | m_Speed = 4; |
1942 | m_StartPaused = false; |
1943 | m_LastPauseChange = -1.0f; |
1944 | m_LastSpeedChange = -1.0f; |
1945 | if(pError) |
1946 | { |
1947 | m_DemoRenderInput.Clear(); |
1948 | PopupMessage(pTitle: Localize(pStr: "Error loading demo" ), pMessage: pError, pButtonLabel: Localize(pStr: "Ok" )); |
1949 | } |
1950 | } |
1951 | #endif |
1952 | |
1953 | void CMenus::(CUIRect MainView) |
1954 | { |
1955 | const std::vector<CTheme> &vThemes = GameClient()->m_MenuBackground.GetThemes(); |
1956 | |
1957 | int SelectedTheme = -1; |
1958 | for(int i = 0; i < (int)vThemes.size(); i++) |
1959 | { |
1960 | if(str_comp(a: vThemes[i].m_Name.c_str(), b: g_Config.m_ClMenuMap) == 0) |
1961 | { |
1962 | SelectedTheme = i; |
1963 | break; |
1964 | } |
1965 | } |
1966 | const int OldSelected = SelectedTheme; |
1967 | |
1968 | static CListBox s_ListBox; |
1969 | s_ListBox.DoHeader(pRect: &MainView, pTitle: Localize(pStr: "Theme" ), HeaderHeight: 20.0f); |
1970 | s_ListBox.DoStart(RowHeight: 20.0f, NumItems: vThemes.size(), ItemsPerRow: 1, RowsPerScroll: 3, SelectedIndex: SelectedTheme); |
1971 | |
1972 | for(int i = 0; i < (int)vThemes.size(); i++) |
1973 | { |
1974 | const CTheme &Theme = vThemes[i]; |
1975 | const CListboxItem Item = s_ListBox.DoNextItem(pId: &Theme.m_Name, Selected: i == SelectedTheme); |
1976 | |
1977 | if(!Item.m_Visible) |
1978 | continue; |
1979 | |
1980 | CUIRect Icon, Label; |
1981 | Item.m_Rect.VSplitLeft(Cut: Item.m_Rect.h * 2.0f, pLeft: &Icon, pRight: &Label); |
1982 | |
1983 | // draw icon if it exists |
1984 | if(Theme.m_IconTexture.IsValid()) |
1985 | { |
1986 | Icon.VMargin(Cut: 6.0f, pOtherRect: &Icon); |
1987 | Icon.HMargin(Cut: 3.0f, pOtherRect: &Icon); |
1988 | Graphics()->TextureSet(Texture: Theme.m_IconTexture); |
1989 | Graphics()->QuadsBegin(); |
1990 | Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f); |
1991 | IGraphics::CQuadItem QuadItem(Icon.x, Icon.y, Icon.w, Icon.h); |
1992 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
1993 | Graphics()->QuadsEnd(); |
1994 | } |
1995 | |
1996 | char aName[128]; |
1997 | if(Theme.m_Name.empty()) |
1998 | str_copy(dst&: aName, src: "(none)" ); |
1999 | else if(str_comp(a: Theme.m_Name.c_str(), b: "auto" ) == 0) |
2000 | str_copy(dst&: aName, src: "(seasons)" ); |
2001 | else if(str_comp(a: Theme.m_Name.c_str(), b: "rand" ) == 0) |
2002 | str_copy(dst&: aName, src: "(random)" ); |
2003 | else if(Theme.m_HasDay && Theme.m_HasNight) |
2004 | str_copy(dst&: aName, src: Theme.m_Name.c_str()); |
2005 | else if(Theme.m_HasDay && !Theme.m_HasNight) |
2006 | str_format(buffer: aName, buffer_size: sizeof(aName), format: "%s (day)" , Theme.m_Name.c_str()); |
2007 | else if(!Theme.m_HasDay && Theme.m_HasNight) |
2008 | str_format(buffer: aName, buffer_size: sizeof(aName), format: "%s (night)" , Theme.m_Name.c_str()); |
2009 | else // generic |
2010 | str_copy(dst&: aName, src: Theme.m_Name.c_str()); |
2011 | |
2012 | Ui()->DoLabel(pRect: &Label, pText: aName, Size: 16.0f * CUi::ms_FontmodHeight, Align: TEXTALIGN_ML); |
2013 | } |
2014 | |
2015 | SelectedTheme = s_ListBox.DoEnd(); |
2016 | |
2017 | if(OldSelected != SelectedTheme) |
2018 | { |
2019 | const CTheme &Theme = vThemes[SelectedTheme]; |
2020 | str_copy(dst&: g_Config.m_ClMenuMap, src: Theme.m_Name.c_str()); |
2021 | GameClient()->m_MenuBackground.LoadMenuBackground(HasDayHint: Theme.m_HasDay, HasNightHint: Theme.m_HasNight); |
2022 | } |
2023 | } |
2024 | |
2025 | void CMenus::(bool Active) |
2026 | { |
2027 | if(Active != m_MenuActive) |
2028 | { |
2029 | Ui()->SetHotItem(nullptr); |
2030 | Ui()->SetActiveItem(nullptr); |
2031 | } |
2032 | m_MenuActive = Active; |
2033 | if(!m_MenuActive) |
2034 | { |
2035 | if(m_NeedSendinfo) |
2036 | { |
2037 | m_pClient->SendInfo(Start: false); |
2038 | m_NeedSendinfo = false; |
2039 | } |
2040 | |
2041 | if(m_NeedSendDummyinfo) |
2042 | { |
2043 | m_pClient->SendDummyInfo(Start: false); |
2044 | m_NeedSendDummyinfo = false; |
2045 | } |
2046 | |
2047 | if(Client()->State() == IClient::STATE_ONLINE) |
2048 | { |
2049 | m_pClient->OnRelease(); |
2050 | } |
2051 | } |
2052 | else if(Client()->State() == IClient::STATE_DEMOPLAYBACK) |
2053 | { |
2054 | m_pClient->OnRelease(); |
2055 | } |
2056 | } |
2057 | |
2058 | void CMenus::() |
2059 | { |
2060 | } |
2061 | |
2062 | void CMenus::() |
2063 | { |
2064 | KillServer(); |
2065 | m_CommunityIconLoadJobs.clear(); |
2066 | m_CommunityIconDownloadJobs.clear(); |
2067 | } |
2068 | |
2069 | bool CMenus::(float x, float y, IInput::ECursorType CursorType) |
2070 | { |
2071 | if(!m_MenuActive) |
2072 | return false; |
2073 | |
2074 | Ui()->ConvertMouseMove(pX: &x, pY: &y, CursorType); |
2075 | Ui()->OnCursorMove(X: x, Y: y); |
2076 | |
2077 | return true; |
2078 | } |
2079 | |
2080 | bool CMenus::(const IInput::CEvent &Event) |
2081 | { |
2082 | // Escape key is always handled to activate/deactivate menu |
2083 | if((Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_ESCAPE) || IsActive()) |
2084 | { |
2085 | Ui()->OnInput(Event); |
2086 | return true; |
2087 | } |
2088 | return false; |
2089 | } |
2090 | |
2091 | void CMenus::(int NewState, int OldState) |
2092 | { |
2093 | // reset active item |
2094 | Ui()->SetActiveItem(nullptr); |
2095 | |
2096 | if(OldState == IClient::STATE_ONLINE || OldState == IClient::STATE_OFFLINE) |
2097 | TextRender()->DeleteTextContainer(TextContainerIndex&: m_MotdTextContainerIndex); |
2098 | |
2099 | if(NewState == IClient::STATE_OFFLINE) |
2100 | { |
2101 | if(OldState >= IClient::STATE_ONLINE && NewState < IClient::STATE_QUITTING) |
2102 | UpdateMusicState(); |
2103 | m_Popup = POPUP_NONE; |
2104 | if(Client()->ErrorString() && Client()->ErrorString()[0] != 0) |
2105 | { |
2106 | if(str_find(haystack: Client()->ErrorString(), needle: "password" )) |
2107 | { |
2108 | m_Popup = POPUP_PASSWORD; |
2109 | m_PasswordInput.SelectAll(); |
2110 | Ui()->SetActiveItem(&m_PasswordInput); |
2111 | } |
2112 | else |
2113 | m_Popup = POPUP_DISCONNECTED; |
2114 | } |
2115 | } |
2116 | else if(NewState == IClient::STATE_LOADING) |
2117 | { |
2118 | m_DownloadLastCheckTime = time_get(); |
2119 | m_DownloadLastCheckSize = 0; |
2120 | m_DownloadSpeed = 0.0f; |
2121 | } |
2122 | else if(NewState == IClient::STATE_ONLINE || NewState == IClient::STATE_DEMOPLAYBACK) |
2123 | { |
2124 | if(m_Popup != POPUP_WARNING) |
2125 | { |
2126 | m_Popup = POPUP_NONE; |
2127 | SetActive(false); |
2128 | } |
2129 | } |
2130 | } |
2131 | |
2132 | void CMenus::() |
2133 | { |
2134 | TextRender()->DeleteTextContainer(TextContainerIndex&: m_MotdTextContainerIndex); |
2135 | } |
2136 | |
2137 | void CMenus::() |
2138 | { |
2139 | Ui()->StartCheck(); |
2140 | |
2141 | if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK) |
2142 | SetActive(true); |
2143 | |
2144 | if(Client()->State() == IClient::STATE_ONLINE && m_pClient->m_ServerMode == CGameClient::SERVERMODE_PUREMOD) |
2145 | { |
2146 | Client()->Disconnect(); |
2147 | SetActive(true); |
2148 | PopupMessage(pTitle: Localize(pStr: "Disconnected" ), pMessage: Localize(pStr: "The server is running a non-standard tuning on a pure game type." ), pButtonLabel: Localize(pStr: "Ok" )); |
2149 | } |
2150 | |
2151 | if(!IsActive()) |
2152 | { |
2153 | if(Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
2154 | { |
2155 | SetActive(true); |
2156 | } |
2157 | else if(Client()->State() != IClient::STATE_DEMOPLAYBACK) |
2158 | { |
2159 | Ui()->FinishCheck(); |
2160 | Ui()->ClearHotkeys(); |
2161 | return; |
2162 | } |
2163 | } |
2164 | |
2165 | UpdateColors(); |
2166 | |
2167 | Ui()->Update(); |
2168 | |
2169 | Render(); |
2170 | |
2171 | if(IsActive()) |
2172 | { |
2173 | RenderTools()->RenderCursor(Center: Ui()->MousePos(), Size: 24.0f); |
2174 | } |
2175 | |
2176 | // render debug information |
2177 | if(g_Config.m_Debug) |
2178 | Ui()->DebugRender(); |
2179 | |
2180 | if(Ui()->ConsumeHotkey(Hotkey: CUi::HOTKEY_ESCAPE)) |
2181 | SetActive(false); |
2182 | |
2183 | Ui()->FinishCheck(); |
2184 | Ui()->ClearHotkeys(); |
2185 | } |
2186 | |
2187 | void CMenus::() |
2188 | { |
2189 | ms_GuiColor = color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_UiColor, true)); |
2190 | |
2191 | ms_ColorTabbarInactiveOutgame = ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f); |
2192 | ms_ColorTabbarActiveOutgame = ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f); |
2193 | ms_ColorTabbarHoverOutgame = ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f); |
2194 | |
2195 | const float ColorIngameScaleI = 0.5f; |
2196 | const float ColorIngameAcaleA = 0.2f; |
2197 | |
2198 | ms_ColorTabbarInactiveIngame = ColorRGBA( |
2199 | ms_GuiColor.r * ColorIngameScaleI, |
2200 | ms_GuiColor.g * ColorIngameScaleI, |
2201 | ms_GuiColor.b * ColorIngameScaleI, |
2202 | ms_GuiColor.a * 0.8f); |
2203 | |
2204 | ms_ColorTabbarActiveIngame = ColorRGBA( |
2205 | ms_GuiColor.r * ColorIngameAcaleA, |
2206 | ms_GuiColor.g * ColorIngameAcaleA, |
2207 | ms_GuiColor.b * ColorIngameAcaleA, |
2208 | ms_GuiColor.a); |
2209 | |
2210 | ms_ColorTabbarHoverIngame = ColorRGBA(1.0f, 1.0f, 1.0f, 0.75f); |
2211 | } |
2212 | |
2213 | void CMenus::() |
2214 | { |
2215 | Graphics()->BlendNormal(); |
2216 | |
2217 | const float ScreenHeight = 300.0f; |
2218 | const float ScreenWidth = ScreenHeight * Graphics()->ScreenAspect(); |
2219 | Graphics()->MapScreen(TopLeftX: 0.0f, TopLeftY: 0.0f, BottomRightX: ScreenWidth, BottomRightY: ScreenHeight); |
2220 | |
2221 | // render background color |
2222 | Graphics()->TextureClear(); |
2223 | Graphics()->QuadsBegin(); |
2224 | Graphics()->SetColor(ms_GuiColor.WithAlpha(alpha: 1.0f)); |
2225 | const IGraphics::CQuadItem BackgroundQuadItem = IGraphics::CQuadItem(0, 0, ScreenWidth, ScreenHeight); |
2226 | Graphics()->QuadsDrawTL(pArray: &BackgroundQuadItem, Num: 1); |
2227 | Graphics()->QuadsEnd(); |
2228 | |
2229 | // render the tiles |
2230 | Graphics()->TextureClear(); |
2231 | Graphics()->QuadsBegin(); |
2232 | Graphics()->SetColor(r: 0.0f, g: 0.0f, b: 0.0f, a: 0.045f); |
2233 | const float Size = 15.0f; |
2234 | const float OffsetTime = std::fmod(x: LocalTime() * 0.15f, y: 2.0f); |
2235 | IGraphics::CQuadItem aCheckerItems[64]; |
2236 | size_t NumCheckerItems = 0; |
2237 | for(int y = -2; y < (int)(ScreenWidth / Size); y++) |
2238 | { |
2239 | for(int x = -2; x < (int)(ScreenHeight / Size); x++) |
2240 | { |
2241 | aCheckerItems[NumCheckerItems] = IGraphics::CQuadItem((x - OffsetTime) * Size * 2 + (y & 1) * Size, (y + OffsetTime) * Size, Size, Size); |
2242 | NumCheckerItems++; |
2243 | if(NumCheckerItems == std::size(aCheckerItems)) |
2244 | { |
2245 | Graphics()->QuadsDrawTL(pArray: aCheckerItems, Num: NumCheckerItems); |
2246 | NumCheckerItems = 0; |
2247 | } |
2248 | } |
2249 | } |
2250 | if(NumCheckerItems != 0) |
2251 | Graphics()->QuadsDrawTL(pArray: aCheckerItems, Num: NumCheckerItems); |
2252 | Graphics()->QuadsEnd(); |
2253 | |
2254 | // render border fade |
2255 | Graphics()->TextureSet(Texture: m_TextureBlob); |
2256 | Graphics()->QuadsBegin(); |
2257 | Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f); |
2258 | const IGraphics::CQuadItem BlobQuadItem = IGraphics::CQuadItem(-100, -100, ScreenWidth + 200, ScreenHeight + 200); |
2259 | Graphics()->QuadsDrawTL(pArray: &BlobQuadItem, Num: 1); |
2260 | Graphics()->QuadsEnd(); |
2261 | |
2262 | // restore screen |
2263 | Ui()->MapScreen(); |
2264 | } |
2265 | |
2266 | bool CMenus::(int Key) const |
2267 | { |
2268 | return m_Popup == POPUP_NONE && |
2269 | !Input()->ShiftIsPressed() && !Input()->ModifierIsPressed() && // no modifier |
2270 | Input()->KeyIsPressed(Key) && m_pClient->m_GameConsole.IsClosed(); |
2271 | } |
2272 | |
2273 | int CMenus::(const void *pId, const char *pText, TRISTATE Checked, const CUIRect *pRect) |
2274 | { |
2275 | switch(Checked) |
2276 | { |
2277 | case TRISTATE::NONE: |
2278 | return DoButton_CheckBox_Common(pId, pText, pBoxText: "" , pRect); |
2279 | case TRISTATE::SOME: |
2280 | return DoButton_CheckBox_Common(pId, pText, pBoxText: "O" , pRect); |
2281 | case TRISTATE::ALL: |
2282 | return DoButton_CheckBox_Common(pId, pText, pBoxText: "X" , pRect); |
2283 | default: |
2284 | dbg_assert(false, "invalid tristate" ); |
2285 | } |
2286 | dbg_break(); |
2287 | } |
2288 | |
2289 | int CMenus::(const char *pName, int IsDir, int DirType, void *pUser) |
2290 | { |
2291 | const char *pExtension = ".png" ; |
2292 | CMenuImage ; |
2293 | CMenus *pSelf = static_cast<CMenus *>(pUser); |
2294 | if(IsDir || !str_endswith(str: pName, suffix: pExtension) || str_length(str: pName) - str_length(str: pExtension) >= (int)sizeof(MenuImage.m_aName)) |
2295 | return 0; |
2296 | |
2297 | char aPath[IO_MAX_PATH_LENGTH]; |
2298 | str_format(buffer: aPath, buffer_size: sizeof(aPath), format: "menuimages/%s" , pName); |
2299 | |
2300 | CImageInfo Info; |
2301 | if(!pSelf->Graphics()->LoadPng(Image&: Info, pFilename: aPath, StorageType: DirType)) |
2302 | { |
2303 | char aError[IO_MAX_PATH_LENGTH + 64]; |
2304 | str_format(buffer: aError, buffer_size: sizeof(aError), format: "Failed to load menu image from '%s'" , aPath); |
2305 | pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "menus" , pStr: aError); |
2306 | return 0; |
2307 | } |
2308 | if(Info.m_Format != CImageInfo::FORMAT_RGBA) |
2309 | { |
2310 | Info.Free(); |
2311 | char aError[IO_MAX_PATH_LENGTH + 64]; |
2312 | str_format(buffer: aError, buffer_size: sizeof(aError), format: "Failed to load menu image from '%s': must be an RGBA image" , aPath); |
2313 | pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "menus" , pStr: aError); |
2314 | return 0; |
2315 | } |
2316 | |
2317 | MenuImage.m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Image: Info, Flags: 0, pTexName: aPath); |
2318 | |
2319 | // create gray scale version |
2320 | unsigned char *pData = static_cast<unsigned char *>(Info.m_pData); |
2321 | const size_t Step = Info.PixelSize(); |
2322 | for(size_t i = 0; i < Info.m_Width * Info.m_Height; i++) |
2323 | { |
2324 | int v = (pData[i * Step] + pData[i * Step + 1] + pData[i * Step + 2]) / 3; |
2325 | pData[i * Step] = v; |
2326 | pData[i * Step + 1] = v; |
2327 | pData[i * Step + 2] = v; |
2328 | } |
2329 | MenuImage.m_GreyTexture = pSelf->Graphics()->LoadTextureRawMove(Image&: Info, Flags: 0, pTexName: aPath); |
2330 | |
2331 | str_truncate(dst: MenuImage.m_aName, dst_size: sizeof(MenuImage.m_aName), src: pName, truncation_len: str_length(str: pName) - str_length(str: pExtension)); |
2332 | pSelf->m_vMenuImages.push_back(x: MenuImage); |
2333 | |
2334 | pSelf->RenderLoading(pCaption: Localize(pStr: "Loading DDNet Client" ), pContent: Localize(pStr: "Loading menu images" ), IncreaseCounter: 1); |
2335 | |
2336 | return 0; |
2337 | } |
2338 | |
2339 | const CMenus::CMenuImage *CMenus::(const char *pName) |
2340 | { |
2341 | for(auto &Image : m_vMenuImages) |
2342 | if(str_comp(a: Image.m_aName, b: pName) == 0) |
2343 | return &Image; |
2344 | return nullptr; |
2345 | } |
2346 | |
2347 | void CMenus::(int NewPage) |
2348 | { |
2349 | const int OldPage = m_MenuPage; |
2350 | m_MenuPage = NewPage; |
2351 | if(NewPage >= PAGE_INTERNET && NewPage <= PAGE_FAVORITE_COMMUNITY_5) |
2352 | { |
2353 | g_Config.m_UiPage = NewPage; |
2354 | bool ForceRefresh = false; |
2355 | if(m_ForceRefreshLanPage) |
2356 | { |
2357 | ForceRefresh = NewPage == PAGE_LAN; |
2358 | m_ForceRefreshLanPage = false; |
2359 | } |
2360 | if(OldPage != NewPage || ForceRefresh) |
2361 | { |
2362 | RefreshBrowserTab(Force: ForceRefresh); |
2363 | } |
2364 | } |
2365 | } |
2366 | |
2367 | void CMenus::(bool Force) |
2368 | { |
2369 | if(g_Config.m_UiPage == PAGE_INTERNET) |
2370 | { |
2371 | if(Force || ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_INTERNET) |
2372 | { |
2373 | if(Force || ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN) |
2374 | { |
2375 | Client()->RequestDDNetInfo(); |
2376 | } |
2377 | ServerBrowser()->Refresh(Type: IServerBrowser::TYPE_INTERNET); |
2378 | UpdateCommunityCache(Force: true); |
2379 | } |
2380 | } |
2381 | else if(g_Config.m_UiPage == PAGE_LAN) |
2382 | { |
2383 | if(Force || ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_LAN) |
2384 | { |
2385 | ServerBrowser()->Refresh(Type: IServerBrowser::TYPE_LAN); |
2386 | UpdateCommunityCache(Force: true); |
2387 | } |
2388 | } |
2389 | else if(g_Config.m_UiPage == PAGE_FAVORITES) |
2390 | { |
2391 | if(Force || ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_FAVORITES) |
2392 | { |
2393 | if(Force || ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN) |
2394 | { |
2395 | Client()->RequestDDNetInfo(); |
2396 | } |
2397 | ServerBrowser()->Refresh(Type: IServerBrowser::TYPE_FAVORITES); |
2398 | UpdateCommunityCache(Force: true); |
2399 | } |
2400 | } |
2401 | else if(g_Config.m_UiPage >= PAGE_FAVORITE_COMMUNITY_1 && g_Config.m_UiPage <= PAGE_FAVORITE_COMMUNITY_5) |
2402 | { |
2403 | const int BrowserType = g_Config.m_UiPage - PAGE_FAVORITE_COMMUNITY_1 + IServerBrowser::TYPE_FAVORITE_COMMUNITY_1; |
2404 | if(Force || ServerBrowser()->GetCurrentType() != BrowserType) |
2405 | { |
2406 | if(Force || ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN) |
2407 | { |
2408 | Client()->RequestDDNetInfo(); |
2409 | } |
2410 | ServerBrowser()->Refresh(Type: BrowserType); |
2411 | UpdateCommunityCache(Force: true); |
2412 | } |
2413 | } |
2414 | } |
2415 | |