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