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