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#ifndef GAME_CLIENT_COMPONENTS_MENUS_H
4#define GAME_CLIENT_COMPONENTS_MENUS_H
5
6#include <base/bytes.h>
7#include <base/types.h>
8#include <base/vmath.h>
9
10#include <engine/console.h>
11#include <engine/demo.h>
12#include <engine/friends.h>
13#include <engine/serverbrowser.h>
14#include <engine/shared/config.h>
15#include <engine/textrender.h>
16
17#include <game/client/component.h>
18#include <game/client/components/community_icons.h>
19#include <game/client/components/mapimages.h>
20#include <game/client/components/menus_ingame_touch_controls.h>
21#include <game/client/components/menus_settings_controls.h>
22#include <game/client/components/menus_start.h>
23#include <game/client/components/skins7.h>
24#include <game/client/lineinput.h>
25#include <game/client/ui.h>
26#include <game/voting.h>
27
28#include <chrono>
29#include <deque>
30#include <optional>
31#include <vector>
32
33class CMenus : public CComponent
34{
35 static ColorRGBA ms_GuiColor;
36 static ColorRGBA ms_ColorTabbarInactiveOutgame;
37 static ColorRGBA ms_ColorTabbarActiveOutgame;
38 static ColorRGBA ms_ColorTabbarHoverOutgame;
39 static ColorRGBA ms_ColorTabbarInactiveIngame;
40 static ColorRGBA ms_ColorTabbarActiveIngame;
41 static ColorRGBA ms_ColorTabbarHoverIngame;
42 static ColorRGBA ms_ColorTabbarInactive;
43 static ColorRGBA ms_ColorTabbarActive;
44 static ColorRGBA ms_ColorTabbarHover;
45
46public:
47 int DoButton_Toggle(const void *pId, int Checked, const CUIRect *pRect, bool Active, unsigned Flags = BUTTONFLAG_LEFT);
48 int DoButton_Menu(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, unsigned Flags = BUTTONFLAG_LEFT, const char *pImageName = nullptr, int Corners = IGraphics::CORNER_ALL, float Rounding = 5.0f, float FontFactor = 0.0f, ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f));
49 int DoButton_MenuTab(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, int Corners, SUIAnimator *pAnimator = nullptr, const ColorRGBA *pDefaultColor = nullptr, const ColorRGBA *pActiveColor = nullptr, const ColorRGBA *pHoverColor = nullptr, float EdgeRounding = 10.0f, const CCommunityIcon *pCommunityIcon = nullptr);
50
51 int DoButton_CheckBox_Common(const void *pId, const char *pText, const char *pBoxText, const CUIRect *pRect, unsigned Flags);
52 int DoButton_CheckBox(const void *pId, const char *pText, int Checked, const CUIRect *pRect);
53 int DoButton_CheckBoxAutoVMarginAndSet(const void *pId, const char *pText, int *pValue, CUIRect *pRect, float VMargin);
54 int DoButton_CheckBox_Number(const void *pId, const char *pText, int Checked, const CUIRect *pRect);
55
56 bool DoLine_RadioMenu(CUIRect &View, const char *pLabel, std::vector<CButtonContainer> &vButtonContainers, const std::vector<const char *> &vLabels, const std::vector<int> &vValues, int &Value);
57
58private:
59 CUi::SColorPickerPopupContext m_ColorPickerPopupContext;
60 ColorHSLA DoLine_ColorPicker(CButtonContainer *pResetId, float LineSize, float LabelSize, float BottomMargin, CUIRect *pMainRect, const char *pText, unsigned int *pColorValue, ColorRGBA DefaultColor, bool CheckBoxSpacing = true, int *pCheckBoxValue = nullptr, bool Alpha = false);
61 ColorHSLA DoButton_ColorPicker(const CUIRect *pRect, unsigned int *pHslaColor, bool Alpha);
62
63 void DoLaserPreview(const CUIRect *pRect, ColorHSLA OutlineColor, ColorHSLA InnerColor, int LaserType);
64 int DoButton_GridHeader(const void *pId, const char *pText, int Checked, const CUIRect *pRect, int Align = TEXTALIGN_ML);
65 int DoButton_Favorite(const void *pButtonId, const void *pParentId, bool Checked, const CUIRect *pRect);
66
67 bool m_SkinListScrollToSelected = false;
68 std::optional<std::chrono::nanoseconds> m_SkinList7LastRefreshTime;
69 std::optional<std::chrono::nanoseconds> m_SkinPartsList7LastRefreshTime;
70
71 int m_DirectionQuadContainerIndex;
72
73 // menus_settings_assets.cpp
74public:
75 struct SCustomItem
76 {
77 IGraphics::CTextureHandle m_RenderTexture;
78
79 char m_aName[50];
80
81 bool operator<(const SCustomItem &Other) const { return str_comp(a: m_aName, b: Other.m_aName) < 0; }
82 };
83
84 struct SCustomEntities : public SCustomItem
85 {
86 struct SEntitiesImage
87 {
88 IGraphics::CTextureHandle m_Texture;
89 };
90 SEntitiesImage m_aImages[MAP_IMAGE_MOD_TYPE_COUNT];
91 };
92
93 struct SCustomGame : public SCustomItem
94 {
95 };
96
97 struct SCustomEmoticon : public SCustomItem
98 {
99 };
100
101 struct SCustomParticle : public SCustomItem
102 {
103 };
104
105 struct SCustomHud : public SCustomItem
106 {
107 };
108
109 struct SCustomExtras : public SCustomItem
110 {
111 };
112
113protected:
114 std::vector<SCustomEntities> m_vEntitiesList;
115 std::vector<SCustomGame> m_vGameList;
116 std::vector<SCustomEmoticon> m_vEmoticonList;
117 std::vector<SCustomParticle> m_vParticlesList;
118 std::vector<SCustomHud> m_vHudList;
119 std::vector<SCustomExtras> m_vExtrasList;
120
121 bool m_IsInit = false;
122
123 static void LoadEntities(struct SCustomEntities *pEntitiesItem, void *pUser);
124 static int EntitiesScan(const char *pName, int IsDir, int DirType, void *pUser);
125
126 static int GameScan(const char *pName, int IsDir, int DirType, void *pUser);
127 static int EmoticonsScan(const char *pName, int IsDir, int DirType, void *pUser);
128 static int ParticlesScan(const char *pName, int IsDir, int DirType, void *pUser);
129 static int HudScan(const char *pName, int IsDir, int DirType, void *pUser);
130 static int ExtrasScan(const char *pName, int IsDir, int DirType, void *pUser);
131
132 static void ConchainAssetsEntities(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
133 static void ConchainAssetGame(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
134 static void ConchainAssetParticles(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
135 static void ConchainAssetEmoticons(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
136 static void ConchainAssetHud(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
137 static void ConchainAssetExtras(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
138
139 void ClearCustomItems(int CurTab);
140
141 int m_MenuPage;
142 int m_GamePage;
143 int m_Popup;
144 bool m_ShowStart;
145 bool m_MenuActive;
146
147 bool m_DummyNamePlatePreview = false;
148
149 class CJoinTutorial
150 {
151 public:
152 bool m_Queued = false;
153 enum class EStatus
154 {
155 REFRESHING,
156 SERVER_LIST_ERROR,
157 NO_TUTORIAL_AVAILABLE,
158 };
159 EStatus m_Status = EStatus::REFRESHING;
160 bool m_TryRefresh = false;
161 bool m_TriedRefresh = false;
162 enum class ELocalServerState
163 {
164 NOT_TRIED,
165 TRY,
166 WAITING_STOP,
167 WAITING_START,
168 };
169 ELocalServerState m_LocalServerState = ELocalServerState::NOT_TRIED;
170 std::chrono::nanoseconds m_StateChange = std::chrono::nanoseconds(0);
171 };
172 CJoinTutorial m_JoinTutorial;
173
174 bool m_CreateDefaultFavoriteCommunities = false;
175 bool m_ForceRefreshLanPage = false;
176
177 char m_aNextServer[256];
178
179 // images
180 struct CMenuImage
181 {
182 char m_aName[64];
183 IGraphics::CTextureHandle m_OrgTexture;
184 IGraphics::CTextureHandle m_GreyTexture;
185 };
186 std::vector<CMenuImage> m_vMenuImages;
187 static int MenuImageScan(const char *pName, int IsDir, int DirType, void *pUser);
188 const CMenuImage *FindMenuImage(const char *pName);
189
190 // loading
191 class CLoadingState
192 {
193 public:
194 std::chrono::nanoseconds m_LastRender{0};
195 int m_Current;
196 int m_Total;
197 };
198 CLoadingState m_LoadingState;
199
200 //
201 char m_aMessageTopic[512];
202 char m_aMessageBody[512];
203 char m_aMessageButton[512];
204
205 CUIElement m_RefreshButton;
206 CUIElement m_ConnectButton;
207
208 // generic popups
209 typedef void (CMenus::*FPopupButtonCallback)();
210 void DefaultButtonCallback()
211 {
212 // do nothing
213 }
214 enum
215 {
216 BUTTON_CONFIRM = 0, // confirm / yes / close / ok
217 BUTTON_CANCEL, // cancel / no
218 NUM_BUTTONS
219 };
220 char m_aPopupTitle[128];
221 char m_aPopupMessage[IO_MAX_PATH_LENGTH + 256];
222 struct
223 {
224 char m_aLabel[64];
225 int m_NextPopup;
226 FPopupButtonCallback m_pfnCallback;
227 } m_aPopupButtons[NUM_BUTTONS];
228
229 void PopupMessage(const char *pTitle, const char *pMessage,
230 const char *pButtonLabel, int NextPopup = POPUP_NONE, FPopupButtonCallback pfnButtonCallback = &CMenus::DefaultButtonCallback);
231 void PopupConfirm(const char *pTitle, const char *pMessage,
232 const char *pConfirmButtonLabel, const char *pCancelButtonLabel,
233 FPopupButtonCallback pfnConfirmButtonCallback = &CMenus::DefaultButtonCallback, int ConfirmNextPopup = POPUP_NONE,
234 FPopupButtonCallback pfnCancelButtonCallback = &CMenus::DefaultButtonCallback, int CancelNextPopup = POPUP_NONE);
235
236 // some settings
237 static float ms_ButtonHeight;
238 static float ms_ListheaderHeight;
239 static float ms_ListitemAdditionalHeight;
240
241 // for settings
242 bool m_NeedRestartGraphics;
243 bool m_NeedRestartSound;
244 bool m_NeedRestartUpdate;
245 bool m_NeedSendinfo;
246 bool m_NeedSendDummyinfo;
247 int m_SettingPlayerPage;
248
249 // 0.7 skins
250 bool m_CustomSkinMenu = false;
251 int m_TeePartSelected = protocol7::SKINPART_BODY;
252 const CSkins7::CSkin *m_pSelectedSkin = nullptr;
253 CLineInputBuffered<protocol7::MAX_SKIN_ARRAY_SIZE, protocol7::MAX_SKIN_LENGTH> m_SkinNameInput;
254 bool m_SkinPartListNeedsUpdate = false;
255 void PopupConfirmDeleteSkin7();
256
257 // for map download popup
258 int64_t m_DownloadLastCheckTime;
259 int m_DownloadLastCheckSize;
260 float m_DownloadSpeed;
261
262 // for password popup
263 CLineInput m_PasswordInput;
264
265 // for call vote
266 int m_CallvoteSelectedOption;
267 int m_CallvoteSelectedPlayer;
268 CLineInputBuffered<VOTE_REASON_LENGTH> m_CallvoteReasonInput;
269 CLineInputBuffered<64> m_FilterInput;
270 bool m_ControlPageOpening;
271
272 // demo
273 enum
274 {
275 SORT_DEMONAME = 0,
276 SORT_MARKERS,
277 SORT_LENGTH,
278 SORT_DATE,
279 };
280
281 class CDemoItem
282 {
283 public:
284 char m_aFilename[IO_MAX_PATH_LENGTH];
285 char m_aName[IO_MAX_PATH_LENGTH];
286 bool m_IsDir;
287 bool m_IsLink;
288 int m_StorageType;
289 time_t m_Date;
290 int64_t m_Size;
291
292 bool m_InfosLoaded;
293 bool m_Valid;
294 CDemoHeader m_Info;
295 CTimelineMarkers m_TimelineMarkers;
296 CMapInfo m_MapInfo;
297
298 int NumMarkers() const
299 {
300 return std::clamp<int>(val: bytes_be_to_uint(bytes: m_TimelineMarkers.m_aNumTimelineMarkers), lo: 0, hi: MAX_TIMELINE_MARKERS);
301 }
302
303 int Length() const
304 {
305 return bytes_be_to_uint(bytes: m_Info.m_aLength);
306 }
307
308 unsigned MapSize() const
309 {
310 return bytes_be_to_uint(bytes: m_Info.m_aMapSize);
311 }
312
313 bool operator<(const CDemoItem &Other) const
314 {
315 if(!str_comp(a: m_aFilename, b: ".."))
316 return true;
317 if(!str_comp(a: Other.m_aFilename, b: ".."))
318 return false;
319 if(m_IsDir && !Other.m_IsDir)
320 return true;
321 if(!m_IsDir && Other.m_IsDir)
322 return false;
323
324 const CDemoItem &Left = g_Config.m_BrDemoSortOrder ? Other : *this;
325 const CDemoItem &Right = g_Config.m_BrDemoSortOrder ? *this : Other;
326
327 if(g_Config.m_BrDemoSort == SORT_DEMONAME)
328 return str_comp_filenames(a: Left.m_aFilename, b: Right.m_aFilename) < 0;
329 if(g_Config.m_BrDemoSort == SORT_DATE)
330 return Left.m_Date < Right.m_Date;
331
332 if(!Other.m_InfosLoaded)
333 return m_InfosLoaded;
334 if(!m_InfosLoaded)
335 return !Other.m_InfosLoaded;
336
337 if(g_Config.m_BrDemoSort == SORT_MARKERS)
338 return Left.NumMarkers() < Right.NumMarkers();
339 if(g_Config.m_BrDemoSort == SORT_LENGTH)
340 return Left.Length() < Right.Length();
341
342 // Unknown sort
343 return true;
344 }
345 };
346
347 char m_aCurrentDemoFolder[IO_MAX_PATH_LENGTH];
348 char m_aCurrentDemoSelectionName[IO_MAX_PATH_LENGTH];
349 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoRenameInput;
350 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoSliceInput;
351 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoSearchInput;
352#if defined(CONF_VIDEORECORDER)
353 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoRenderInput;
354#endif
355 int m_DemolistSelectedIndex;
356 bool m_DemolistSelectedReveal = false;
357 int m_DemolistStorageType;
358 bool m_DemolistMultipleStorages = false;
359 int m_Speed = 4;
360 bool m_StartPaused = false;
361
362 std::chrono::nanoseconds m_DemoPopulateStartTime{0};
363
364 void DemolistOnUpdate(bool Reset);
365 static int DemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser);
366
367 // friends
368 class CFriendItem
369 {
370 char m_aName[MAX_NAME_LENGTH];
371 char m_aClan[MAX_CLAN_LENGTH];
372 const CServerInfo *m_pServerInfo;
373 int m_FriendState;
374 bool m_IsPlayer;
375 bool m_IsAfk;
376 // skin info 0.6
377 char m_aSkin[MAX_SKIN_LENGTH];
378 bool m_CustomSkinColors;
379 int m_CustomSkinColorBody;
380 int m_CustomSkinColorFeet;
381 // skin info 0.7
382 char m_aaSkin7[protocol7::NUM_SKINPARTS][protocol7::MAX_SKIN_LENGTH];
383 bool m_aUseCustomSkinColor7[protocol7::NUM_SKINPARTS];
384 int m_aCustomSkinColor7[protocol7::NUM_SKINPARTS];
385
386 public:
387 CFriendItem(const CFriendInfo *pFriendInfo) :
388 m_pServerInfo(nullptr),
389 m_IsPlayer(false),
390 m_IsAfk(false),
391 m_CustomSkinColors(false),
392 m_CustomSkinColorBody(0),
393 m_CustomSkinColorFeet(0)
394 {
395 str_copy(dst&: m_aName, src: pFriendInfo->m_aName);
396 str_copy(dst&: m_aClan, src: pFriendInfo->m_aClan);
397 m_FriendState = m_aName[0] == '\0' ? IFriends::FRIEND_CLAN : IFriends::FRIEND_PLAYER;
398 m_aSkin[0] = '\0';
399 for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
400 {
401 m_aaSkin7[Part][0] = '\0';
402 m_aUseCustomSkinColor7[Part] = false;
403 m_aCustomSkinColor7[Part] = 0;
404 }
405 }
406 CFriendItem(const CServerInfo::CClient &CurrentClient, const CServerInfo *pServerInfo) :
407 m_pServerInfo(pServerInfo),
408 m_FriendState(CurrentClient.m_FriendState),
409 m_IsPlayer(CurrentClient.m_Player),
410 m_IsAfk(CurrentClient.m_Afk),
411 m_CustomSkinColors(CurrentClient.m_CustomSkinColors),
412 m_CustomSkinColorBody(CurrentClient.m_CustomSkinColorBody),
413 m_CustomSkinColorFeet(CurrentClient.m_CustomSkinColorFeet)
414 {
415 str_copy(dst&: m_aName, src: CurrentClient.m_aName);
416 str_copy(dst&: m_aClan, src: CurrentClient.m_aClan);
417 str_copy(dst&: m_aSkin, src: CurrentClient.m_aSkin);
418 for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
419 {
420 str_copy(dst&: m_aaSkin7[Part], src: CurrentClient.m_aaSkin7[Part]);
421 m_aUseCustomSkinColor7[Part] = CurrentClient.m_aUseCustomSkinColor7[Part];
422 m_aCustomSkinColor7[Part] = CurrentClient.m_aCustomSkinColor7[Part];
423 }
424 }
425
426 const char *Name() const { return m_aName; }
427 const char *Clan() const { return m_aClan; }
428 const CServerInfo *ServerInfo() const { return m_pServerInfo; }
429 int FriendState() const { return m_FriendState; }
430 bool IsPlayer() const { return m_IsPlayer; }
431 bool IsAfk() const { return m_IsAfk; }
432 // 0.6 skin
433 const char *Skin() const { return m_aSkin; }
434 bool CustomSkinColors() const { return m_CustomSkinColors; }
435 int CustomSkinColorBody() const { return m_CustomSkinColorBody; }
436 int CustomSkinColorFeet() const { return m_CustomSkinColorFeet; }
437 // 0.7 skin
438 const char *Skin7(int Part) const { return m_aaSkin7[Part]; }
439 bool UseCustomSkinColor7(int Part) const { return m_aUseCustomSkinColor7[Part]; }
440 int CustomSkinColor7(int Part) const { return m_aCustomSkinColor7[Part]; }
441
442 const void *ListItemId() const { return &m_aName; }
443 const void *RemoveButtonId() const { return &m_FriendState; }
444 const void *CommunityTooltipId() const { return &m_IsPlayer; }
445 const void *SkinTooltipId() const { return &m_aSkin; }
446
447 bool operator<(const CFriendItem &Other) const
448 {
449 const int Result = str_comp_nocase(a: m_aName, b: Other.m_aName);
450 return Result < 0 || (Result == 0 && str_comp_nocase(a: m_aClan, b: Other.m_aClan) < 0);
451 }
452 };
453
454 enum
455 {
456 FRIEND_PLAYER_ON = 0,
457 FRIEND_CLAN_ON,
458 FRIEND_OFF,
459 NUM_FRIEND_TYPES
460 };
461 std::vector<CFriendItem> m_avFriends[NUM_FRIEND_TYPES];
462 const CFriendItem *m_pRemoveFriend = nullptr;
463
464 // found in menus.cpp
465 void Render();
466 void RenderPopupFullscreen(CUIRect Screen);
467 void RenderPopupConnecting(CUIRect Screen);
468 void RenderPopupLoading(CUIRect Screen);
469#if defined(CONF_VIDEORECORDER)
470 void PopupConfirmDemoReplaceVideo();
471#endif
472 void RenderMenubar(CUIRect Box, IClient::EClientState ClientState);
473 void RenderNews(CUIRect MainView);
474 static void ConchainBackgroundEntities(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
475 static void ConchainUpdateMusicState(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
476 void UpdateMusicState();
477
478 // found in menus_demo.cpp
479 vec2 m_DemoControlsPositionOffset = vec2(0.0f, 0.0f);
480 bool m_PausedBeforeSeeking;
481 float m_PrevSeekAmount;
482 float m_LastPauseChange = -1.0f;
483 float m_LastSpeedChange = -1.0f;
484 static constexpr int DEFAULT_SKIP_DURATION_INDEX = 3;
485 int m_SkipDurationIndex = DEFAULT_SKIP_DURATION_INDEX;
486 static bool DemoFilterChat(const void *pData, int Size, void *pUser);
487 bool FetchHeader(CDemoItem &Item);
488 void FetchAllHeaders();
489 void HandleDemoSeeking(float PositionToSeek, float TimeToSeek);
490 void RenderDemoPlayer(CUIRect MainView);
491 void RenderDemoPlayerSliceSavePopup(CUIRect MainView);
492 bool m_DemoBrowserListInitialized = false;
493 void RenderDemoBrowser(CUIRect MainView);
494 void RenderDemoBrowserList(CUIRect ListView, bool &WasListboxItemActivated);
495 void RenderDemoBrowserDetails(CUIRect DetailsView);
496 void RenderDemoBrowserButtons(CUIRect ButtonsView, bool WasListboxItemActivated);
497 void PopupConfirmPlayDemo();
498 void PopupConfirmDeleteDemo();
499 void PopupConfirmDeleteFolder();
500 static void ConchainDemoPlay(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
501 static void ConchainDemoSpeed(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
502
503 // found in menus_ingame.cpp
504 STextContainerIndex m_MotdTextContainerIndex;
505 void RenderGame(CUIRect MainView);
506 void PopupConfirmDisconnect();
507 void PopupConfirmDisconnectDummy();
508 void PopupConfirmDiscardTouchControlsChanges();
509 void PopupConfirmResetTouchControls();
510 void PopupConfirmImportTouchControlsClipboard();
511 void PopupConfirmDeleteButton();
512 void PopupCancelDeselectButton();
513 void PopupConfirmSelectedNotVisible();
514 void PopupConfirmChangeSelectedButton();
515 void PopupCancelChangeSelectedButton();
516 void PopupConfirmTurnOffEditor();
517 void PopupConfirmOpenWiki();
518 void RenderPlayers(CUIRect MainView);
519 void RenderServerInfo(CUIRect MainView);
520 void RenderServerInfoMotd(CUIRect Motd);
521 void RenderServerControl(CUIRect MainView);
522 bool RenderServerControlKick(CUIRect MainView, bool FilterSpectators, bool UpdateScroll);
523 bool RenderServerControlServer(CUIRect MainView, bool UpdateScroll);
524 void RenderIngameHint();
525
526 // found in menus_browser.cpp
527 int m_SelectedIndex;
528 bool m_ServerBrowserShouldRevealSelection;
529 std::vector<CUIElement *> m_avpServerBrowserUiElements[IServerBrowser::NUM_TYPES];
530 void RenderServerbrowserServerList(CUIRect View, bool &WasListboxItemActivated);
531 void RenderServerbrowserStatusBox(CUIRect StatusBox, bool WasListboxItemActivated);
532 void Connect(const char *pAddress);
533 void PopupConfirmSwitchServer();
534 void RenderServerbrowserFilters(CUIRect View);
535 void ResetServerbrowserFilters();
536 void RenderServerbrowserDDNetFilter(CUIRect View,
537 IFilterList &Filter,
538 float ItemHeight, int MaxItems, int ItemsPerRow,
539 CScrollRegion &ScrollRegion, std::vector<unsigned char> &vItemIds,
540 bool UpdateCommunityCacheOnChange,
541 const std::function<const char *(int ItemIndex)> &GetItemName,
542 const std::function<void(int ItemIndex, CUIRect Item, const void *pItemId, bool Active)> &RenderItem);
543 void RenderServerbrowserCommunitiesFilter(CUIRect View);
544 void RenderServerbrowserCountriesFilter(CUIRect View);
545 void RenderServerbrowserTypesFilter(CUIRect View);
546 struct SPopupCountrySelectionContext
547 {
548 CMenus *m_pMenus;
549 int m_Selection;
550 bool m_New;
551 };
552 static CUi::EPopupMenuFunctionResult PopupCountrySelection(void *pContext, CUIRect View, bool Active);
553 void RenderServerbrowserInfo(CUIRect View);
554 void RenderServerbrowserInfoScoreboard(CUIRect View, const CServerInfo *pSelectedServer);
555 void RenderServerbrowserFriends(CUIRect View);
556 void FriendlistOnUpdate();
557 void PopupConfirmRemoveFriend();
558 void RenderServerbrowserTabBar(CUIRect TabBar);
559 void RenderServerbrowserToolBox(CUIRect ToolBox);
560 void RenderServerbrowser(CUIRect MainView);
561 template<typename F>
562 bool PrintHighlighted(const char *pName, F &&PrintFn);
563 CTeeRenderInfo GetTeeRenderInfo(vec2 Size, const char *pSkinName, bool CustomSkinColors, int CustomSkinColorBody, int CustomSkinColorFeet) const;
564 static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
565 static void ConchainFavoritesUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
566 static void ConchainCommunitiesUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
567 static void ConchainUiPageUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
568 void UpdateCommunityCache(bool Force);
569
570 // found in menus_settings.cpp
571 void RenderLanguageSettings(CUIRect MainView);
572 bool RenderLanguageSelection(CUIRect MainView);
573 void RenderThemeSelection(CUIRect MainView);
574 void RenderSettingsGeneral(CUIRect MainView);
575 void RenderSettingsPlayer(CUIRect MainView);
576 void RenderSettingsTee(CUIRect MainView);
577 void RenderSettingsTee7(CUIRect MainView);
578 void RenderSettingsTeeCustom7(CUIRect MainView);
579 void RenderSkinSelection7(CUIRect MainView);
580 void RenderSkinPartSelection7(CUIRect MainView);
581 void RenderSettingsGraphics(CUIRect MainView);
582 void RenderSettingsSound(CUIRect MainView);
583 void RenderSettings(CUIRect MainView);
584 void RenderSettingsCustom(CUIRect MainView);
585
586 // found in menus_settings_controls.cpp
587 // TODO: Change PopupConfirm to avoid using a function pointer to a CMenus
588 // member function, to move this function to CMenusSettingsControls
589 void ResetSettingsControls();
590
591 std::vector<CButtonContainer> m_vButtonContainersNamePlateShow = {{}, {}, {}, {}};
592 std::vector<CButtonContainer> m_vButtonContainersNamePlateKeyPresses = {{}, {}, {}, {}};
593
594 class CMapListItem
595 {
596 public:
597 char m_aFilename[IO_MAX_PATH_LENGTH];
598 bool m_IsDirectory;
599 };
600 class CPopupMapPickerContext
601 {
602 public:
603 std::vector<CMapListItem> m_vMaps;
604 char m_aCurrentMapFolder[IO_MAX_PATH_LENGTH] = "";
605 static int MapListFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser);
606 void MapListPopulate();
607 CMenus *m_pMenus;
608 int m_Selection;
609 };
610
611 static bool CompareFilenameAscending(const CMapListItem Lhs, const CMapListItem Rhs)
612 {
613 if(str_comp(a: Lhs.m_aFilename, b: "..") == 0)
614 return true;
615 if(str_comp(a: Rhs.m_aFilename, b: "..") == 0)
616 return false;
617 if(Lhs.m_IsDirectory != Rhs.m_IsDirectory)
618 return Lhs.m_IsDirectory;
619 return str_comp_filenames(a: Lhs.m_aFilename, b: Rhs.m_aFilename) < 0;
620 }
621
622 static CUi::EPopupMenuFunctionResult PopupMapPicker(void *pContext, CUIRect View, bool Active);
623
624 void SetNeedSendInfo();
625 void UpdateColors();
626
627 IGraphics::CTextureHandle m_TextureBlob;
628
629public:
630 void RenderBackground();
631
632 CMenus();
633 int Sizeof() const override { return sizeof(*this); }
634
635 void RenderLoading(const char *pCaption, const char *pContent, int IncreaseCounter);
636 void FinishLoading();
637
638 bool IsInit() const { return m_IsInit; }
639
640 bool IsActive() const { return m_MenuActive; }
641 void SetActive(bool Active);
642
643 void OnInterfacesInit(CGameClient *pClient) override;
644 void OnInit() override;
645
646 void OnStateChange(int NewState, int OldState) override;
647 void OnWindowResize() override;
648 void OnReset() override;
649 void OnRender() override;
650 bool OnInput(const IInput::CEvent &Event) override;
651 bool OnCursorMove(float x, float y, IInput::ECursorType CursorType) override;
652 void OnShutdown() override;
653
654 enum
655 {
656 PAGE_NEWS = 1,
657 PAGE_GAME,
658 PAGE_PLAYERS,
659 PAGE_SERVER_INFO,
660 PAGE_CALLVOTE,
661 PAGE_INTERNET,
662 PAGE_LAN,
663 PAGE_FAVORITES,
664 PAGE_FAVORITE_COMMUNITY_1,
665 PAGE_FAVORITE_COMMUNITY_2,
666 PAGE_FAVORITE_COMMUNITY_3,
667 PAGE_FAVORITE_COMMUNITY_4,
668 PAGE_FAVORITE_COMMUNITY_5,
669 PAGE_DEMOS,
670 PAGE_SETTINGS,
671 PAGE_NETWORK,
672 PAGE_GHOST,
673
674 PAGE_LENGTH,
675 };
676
677 enum
678 {
679 SETTINGS_LANGUAGE = 0,
680 SETTINGS_GENERAL,
681 SETTINGS_PLAYER,
682 SETTINGS_TEE,
683 SETTINGS_APPEARANCE,
684 SETTINGS_CONTROLS,
685 SETTINGS_GRAPHICS,
686 SETTINGS_SOUND,
687 SETTINGS_DDNET,
688 SETTINGS_ASSETS,
689
690 SETTINGS_LENGTH,
691 };
692
693 enum
694 {
695 BIG_TAB_NEWS = 0,
696 BIG_TAB_INTERNET,
697 BIG_TAB_LAN,
698 BIG_TAB_FAVORITES,
699 BIT_TAB_FAVORITE_COMMUNITY_1,
700 BIT_TAB_FAVORITE_COMMUNITY_2,
701 BIT_TAB_FAVORITE_COMMUNITY_3,
702 BIT_TAB_FAVORITE_COMMUNITY_4,
703 BIT_TAB_FAVORITE_COMMUNITY_5,
704 BIG_TAB_DEMOS,
705
706 BIG_TAB_LENGTH,
707 };
708
709 enum
710 {
711 SMALL_TAB_HOME = 0,
712 SMALL_TAB_QUIT,
713 SMALL_TAB_SETTINGS,
714 SMALL_TAB_EDITOR,
715 SMALL_TAB_DEMOBUTTON,
716 SMALL_TAB_SERVER,
717 SMALL_TAB_BROWSER_FILTER,
718 SMALL_TAB_BROWSER_INFO,
719 SMALL_TAB_BROWSER_FRIENDS,
720
721 SMALL_TAB_LENGTH,
722 };
723
724 SUIAnimator m_aAnimatorsBigPage[BIG_TAB_LENGTH];
725 SUIAnimator m_aAnimatorsSmallPage[SMALL_TAB_LENGTH];
726 SUIAnimator m_aAnimatorsSettingsTab[SETTINGS_LENGTH];
727
728 // DDRace
729 int DoButton_CheckBox_Tristate(const void *pId, const char *pText, TRISTATE Checked, const CUIRect *pRect);
730 std::vector<CDemoItem> m_vDemos;
731 std::vector<CDemoItem *> m_vpFilteredDemos;
732 void DemolistPopulate();
733 void RefreshFilteredDemos();
734 void DemoSeekTick(IDemoPlayer::ETickOffset TickOffset);
735 bool m_Dummy;
736
737 const char *GetCurrentDemoFolder() const { return m_aCurrentDemoFolder; }
738
739 // Ghost
740 struct CGhostItem
741 {
742 char m_aFilename[IO_MAX_PATH_LENGTH];
743 char m_aPlayer[MAX_NAME_LENGTH];
744
745 bool m_Failed;
746 int m_Time;
747 int m_Slot;
748 bool m_Own;
749 time_t m_Date;
750
751 CGhostItem() :
752 m_Slot(-1), m_Own(false) { m_aFilename[0] = 0; }
753
754 bool operator<(const CGhostItem &Other) const { return m_Time < Other.m_Time; }
755
756 bool Active() const { return m_Slot != -1; }
757 bool HasFile() const { return m_aFilename[0]; }
758 };
759
760 enum
761 {
762 GHOST_SORT_NONE = -1,
763 GHOST_SORT_NAME,
764 GHOST_SORT_TIME,
765 GHOST_SORT_DATE,
766 };
767
768 std::vector<CGhostItem> m_vGhosts;
769
770 std::chrono::nanoseconds m_GhostPopulateStartTime{0};
771
772 void GhostlistPopulate();
773 CGhostItem *GetOwnGhost();
774 void UpdateOwnGhost(CGhostItem Item);
775 void DeleteGhostItem(int Index);
776 void SortGhostlist();
777
778 bool CanDisplayWarning() const;
779
780 void PopupWarning(const char *pTopic, const char *pBody, const char *pButton, std::chrono::nanoseconds Duration);
781
782 std::chrono::nanoseconds m_PopupWarningLastTime;
783 std::chrono::nanoseconds m_PopupWarningDuration;
784
785 int m_DemoPlayerState;
786
787 enum
788 {
789 POPUP_NONE = 0,
790 POPUP_MESSAGE, // generic message popup (one button)
791 POPUP_CONFIRM, // generic confirmation popup (two buttons)
792 POPUP_FIRST_LAUNCH,
793 POPUP_JOIN_TUTORIAL,
794 POPUP_POINTS,
795 POPUP_DISCONNECTED,
796 POPUP_LANGUAGE,
797 POPUP_RENAME_DEMO,
798 POPUP_RENDER_DEMO,
799 POPUP_RENDER_DONE,
800 POPUP_PASSWORD,
801 POPUP_QUIT,
802 POPUP_RESTART,
803 POPUP_WARNING,
804 POPUP_SAVE_SKIN,
805 };
806
807 enum
808 {
809 // demo player states
810 DEMOPLAYER_NONE = 0,
811 DEMOPLAYER_SLICE_SAVE,
812 };
813
814 void SetMenuPage(int NewPage);
815 void RefreshBrowserTab(bool Force);
816 void ForceRefreshLanPage();
817 void SetShowStart(bool ShowStart);
818 void ShowQuitPopup();
819 void JoinTutorial();
820
821private:
822 CCommunityIcons m_CommunityIcons;
823 CMenusIngameTouchControls m_MenusIngameTouchControls;
824 friend CMenusIngameTouchControls;
825 CMenusSettingsControls m_MenusSettingsControls;
826 friend CMenusSettingsControls;
827 CMenusStart m_MenusStart;
828
829 static int GhostlistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser);
830
831 // found in menus_ingame.cpp
832 void RenderInGameNetwork(CUIRect MainView);
833 void RenderGhost(CUIRect MainView);
834
835 // found in menus_settings.cpp
836 void RenderSettingsDDNet(CUIRect MainView);
837 void RenderSettingsAppearance(CUIRect MainView);
838 bool RenderHslaScrollbars(CUIRect *pRect, unsigned int *pColor, bool Alpha, float DarkestLight);
839};
840#endif
841