1 | #include <base/system.h> |
2 | |
3 | #include <engine/shared/config.h> |
4 | #include <engine/storage.h> |
5 | #include <engine/textrender.h> |
6 | |
7 | #include <game/client/gameclient.h> |
8 | #include <game/client/ui_listbox.h> |
9 | #include <game/localization.h> |
10 | |
11 | #include "menus.h" |
12 | |
13 | #include <chrono> |
14 | |
15 | using namespace FontIcons; |
16 | using namespace std::chrono_literals; |
17 | |
18 | typedef std::function<void()> ; |
19 | |
20 | struct |
21 | { |
22 | void *; |
23 | TMenuAssetScanLoadedFunc ; |
24 | }; |
25 | |
26 | // IDs of the tabs in the Assets menu |
27 | enum |
28 | { |
29 | ASSETS_TAB_ENTITIES = 0, |
30 | ASSETS_TAB_GAME = 1, |
31 | ASSETS_TAB_EMOTICONS = 2, |
32 | ASSETS_TAB_PARTICLES = 3, |
33 | ASSETS_TAB_HUD = 4, |
34 | = 5, |
35 | NUMBER_OF_ASSETS_TABS = 6, |
36 | }; |
37 | |
38 | void CMenus::(SCustomEntities *pEntitiesItem, void *pUser) |
39 | { |
40 | auto *pRealUser = (SMenuAssetScanUser *)pUser; |
41 | auto *pThis = (CMenus *)pRealUser->m_pUser; |
42 | |
43 | char aPath[IO_MAX_PATH_LENGTH]; |
44 | if(str_comp(a: pEntitiesItem->m_aName, b: "default" ) == 0) |
45 | { |
46 | for(int i = 0; i < MAP_IMAGE_MOD_TYPE_COUNT; ++i) |
47 | { |
48 | str_format(buffer: aPath, buffer_size: sizeof(aPath), format: "editor/entities_clear/%s.png" , gs_apModEntitiesNames[i]); |
49 | pEntitiesItem->m_aImages[i].m_Texture = pThis->Graphics()->LoadTexture(pFilename: aPath, StorageType: IStorage::TYPE_ALL); |
50 | if(!pEntitiesItem->m_RenderTexture.IsValid() || pEntitiesItem->m_RenderTexture.IsNullTexture()) |
51 | pEntitiesItem->m_RenderTexture = pEntitiesItem->m_aImages[i].m_Texture; |
52 | } |
53 | } |
54 | else |
55 | { |
56 | for(int i = 0; i < MAP_IMAGE_MOD_TYPE_COUNT; ++i) |
57 | { |
58 | str_format(buffer: aPath, buffer_size: sizeof(aPath), format: "assets/entities/%s/%s.png" , pEntitiesItem->m_aName, gs_apModEntitiesNames[i]); |
59 | pEntitiesItem->m_aImages[i].m_Texture = pThis->Graphics()->LoadTexture(pFilename: aPath, StorageType: IStorage::TYPE_ALL); |
60 | if(pEntitiesItem->m_aImages[i].m_Texture.IsNullTexture()) |
61 | { |
62 | str_format(buffer: aPath, buffer_size: sizeof(aPath), format: "assets/entities/%s.png" , pEntitiesItem->m_aName); |
63 | pEntitiesItem->m_aImages[i].m_Texture = pThis->Graphics()->LoadTexture(pFilename: aPath, StorageType: IStorage::TYPE_ALL); |
64 | } |
65 | if(!pEntitiesItem->m_RenderTexture.IsValid() || pEntitiesItem->m_RenderTexture.IsNullTexture()) |
66 | pEntitiesItem->m_RenderTexture = pEntitiesItem->m_aImages[i].m_Texture; |
67 | } |
68 | } |
69 | } |
70 | |
71 | int CMenus::(const char *pName, int IsDir, int DirType, void *pUser) |
72 | { |
73 | auto *pRealUser = (SMenuAssetScanUser *)pUser; |
74 | auto *pThis = (CMenus *)pRealUser->m_pUser; |
75 | if(IsDir) |
76 | { |
77 | if(pName[0] == '.') |
78 | return 0; |
79 | |
80 | // default is reserved |
81 | if(str_comp(a: pName, b: "default" ) == 0) |
82 | return 0; |
83 | |
84 | SCustomEntities EntitiesItem; |
85 | str_copy(dst&: EntitiesItem.m_aName, src: pName); |
86 | CMenus::LoadEntities(pEntitiesItem: &EntitiesItem, pUser); |
87 | pThis->m_vEntitiesList.push_back(x: EntitiesItem); |
88 | } |
89 | else |
90 | { |
91 | if(str_endswith(str: pName, suffix: ".png" )) |
92 | { |
93 | char aName[IO_MAX_PATH_LENGTH]; |
94 | str_truncate(dst: aName, dst_size: sizeof(aName), src: pName, truncation_len: str_length(str: pName) - 4); |
95 | // default is reserved |
96 | if(str_comp(a: aName, b: "default" ) == 0) |
97 | return 0; |
98 | |
99 | SCustomEntities EntitiesItem; |
100 | str_copy(dst&: EntitiesItem.m_aName, src: aName); |
101 | CMenus::LoadEntities(pEntitiesItem: &EntitiesItem, pUser); |
102 | pThis->m_vEntitiesList.push_back(x: EntitiesItem); |
103 | } |
104 | } |
105 | |
106 | pRealUser->m_LoadedFunc(); |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | template<typename TName> |
112 | static void LoadAsset(TName *pAssetItem, const char *pAssetName, IGraphics *pGraphics) |
113 | { |
114 | char aPath[IO_MAX_PATH_LENGTH]; |
115 | if(str_comp(pAssetItem->m_aName, "default" ) == 0) |
116 | { |
117 | str_format(buffer: aPath, buffer_size: sizeof(aPath), format: "%s.png" , pAssetName); |
118 | pAssetItem->m_RenderTexture = pGraphics->LoadTexture(pFilename: aPath, StorageType: IStorage::TYPE_ALL); |
119 | } |
120 | else |
121 | { |
122 | str_format(aPath, sizeof(aPath), "assets/%s/%s.png" , pAssetName, pAssetItem->m_aName); |
123 | pAssetItem->m_RenderTexture = pGraphics->LoadTexture(pFilename: aPath, StorageType: IStorage::TYPE_ALL); |
124 | if(pAssetItem->m_RenderTexture.IsNullTexture()) |
125 | { |
126 | str_format(aPath, sizeof(aPath), "assets/%s/%s/%s.png" , pAssetName, pAssetItem->m_aName, pAssetName); |
127 | pAssetItem->m_RenderTexture = pGraphics->LoadTexture(pFilename: aPath, StorageType: IStorage::TYPE_ALL); |
128 | } |
129 | } |
130 | } |
131 | |
132 | template<typename TName> |
133 | static int AssetScan(const char *pName, int IsDir, int DirType, std::vector<TName> &vAssetList, const char *pAssetName, IGraphics *pGraphics, void *pUser) |
134 | { |
135 | auto *pRealUser = (SMenuAssetScanUser *)pUser; |
136 | if(IsDir) |
137 | { |
138 | if(pName[0] == '.') |
139 | return 0; |
140 | |
141 | // default is reserved |
142 | if(str_comp(a: pName, b: "default" ) == 0) |
143 | return 0; |
144 | |
145 | TName AssetItem; |
146 | str_copy(AssetItem.m_aName, pName); |
147 | LoadAsset(&AssetItem, pAssetName, pGraphics); |
148 | vAssetList.push_back(AssetItem); |
149 | } |
150 | else |
151 | { |
152 | if(str_endswith(str: pName, suffix: ".png" )) |
153 | { |
154 | char aName[IO_MAX_PATH_LENGTH]; |
155 | str_truncate(dst: aName, dst_size: sizeof(aName), src: pName, truncation_len: str_length(str: pName) - 4); |
156 | // default is reserved |
157 | if(str_comp(a: aName, b: "default" ) == 0) |
158 | return 0; |
159 | |
160 | TName AssetItem; |
161 | str_copy(AssetItem.m_aName, aName); |
162 | LoadAsset(&AssetItem, pAssetName, pGraphics); |
163 | vAssetList.push_back(AssetItem); |
164 | } |
165 | } |
166 | |
167 | pRealUser->m_LoadedFunc(); |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | int CMenus::(const char *pName, int IsDir, int DirType, void *pUser) |
173 | { |
174 | auto *pRealUser = (SMenuAssetScanUser *)pUser; |
175 | auto *pThis = (CMenus *)pRealUser->m_pUser; |
176 | IGraphics *pGraphics = pThis->Graphics(); |
177 | return AssetScan(pName, IsDir, DirType, vAssetList&: pThis->m_vGameList, pAssetName: "game" , pGraphics, pUser); |
178 | } |
179 | |
180 | int CMenus::(const char *pName, int IsDir, int DirType, void *pUser) |
181 | { |
182 | auto *pRealUser = (SMenuAssetScanUser *)pUser; |
183 | auto *pThis = (CMenus *)pRealUser->m_pUser; |
184 | IGraphics *pGraphics = pThis->Graphics(); |
185 | return AssetScan(pName, IsDir, DirType, vAssetList&: pThis->m_vEmoticonList, pAssetName: "emoticons" , pGraphics, pUser); |
186 | } |
187 | |
188 | int CMenus::ParticlesScan(const char *pName, int IsDir, int DirType, void *pUser) |
189 | { |
190 | auto *pRealUser = (SMenuAssetScanUser *)pUser; |
191 | auto *pThis = (CMenus *)pRealUser->m_pUser; |
192 | IGraphics *pGraphics = pThis->Graphics(); |
193 | return AssetScan(pName, IsDir, DirType, vAssetList&: pThis->m_vParticlesList, pAssetName: "particles" , pGraphics, pUser); |
194 | } |
195 | |
196 | int CMenus::(const char *pName, int IsDir, int DirType, void *pUser) |
197 | { |
198 | auto *pRealUser = (SMenuAssetScanUser *)pUser; |
199 | auto *pThis = (CMenus *)pRealUser->m_pUser; |
200 | IGraphics *pGraphics = pThis->Graphics(); |
201 | return AssetScan(pName, IsDir, DirType, vAssetList&: pThis->m_vHudList, pAssetName: "hud" , pGraphics, pUser); |
202 | } |
203 | |
204 | int CMenus::(const char *pName, int IsDir, int DirType, void *pUser) |
205 | { |
206 | auto *pRealUser = (SMenuAssetScanUser *)pUser; |
207 | auto *pThis = (CMenus *)pRealUser->m_pUser; |
208 | IGraphics *pGraphics = pThis->Graphics(); |
209 | return AssetScan(pName, IsDir, DirType, vAssetList&: pThis->m_vExtrasList, pAssetName: "extras" , pGraphics, pUser); |
210 | } |
211 | |
212 | static std::vector<const CMenus::SCustomEntities *> gs_vpSearchEntitiesList; |
213 | static std::vector<const CMenus::SCustomGame *> gs_vpSearchGamesList; |
214 | static std::vector<const CMenus::SCustomEmoticon *> gs_vpSearchEmoticonsList; |
215 | static std::vector<const CMenus::SCustomParticle *> gs_vpSearchParticlesList; |
216 | static std::vector<const CMenus::SCustomHud *> gs_vpSearchHudList; |
217 | static std::vector<const CMenus::SCustomExtras *> ; |
218 | |
219 | static bool gs_aInitCustomList[NUMBER_OF_ASSETS_TABS] = { |
220 | true, |
221 | }; |
222 | |
223 | static size_t gs_aCustomListSize[NUMBER_OF_ASSETS_TABS] = { |
224 | 0, |
225 | }; |
226 | |
227 | static CLineInputBuffered<64> s_aFilterInputs[NUMBER_OF_ASSETS_TABS]; |
228 | |
229 | static int s_CurCustomTab = ASSETS_TAB_ENTITIES; |
230 | |
231 | static const CMenus::SCustomItem *GetCustomItem(int CurTab, size_t Index) |
232 | { |
233 | if(CurTab == ASSETS_TAB_ENTITIES) |
234 | return gs_vpSearchEntitiesList[Index]; |
235 | else if(CurTab == ASSETS_TAB_GAME) |
236 | return gs_vpSearchGamesList[Index]; |
237 | else if(CurTab == ASSETS_TAB_EMOTICONS) |
238 | return gs_vpSearchEmoticonsList[Index]; |
239 | else if(CurTab == ASSETS_TAB_PARTICLES) |
240 | return gs_vpSearchParticlesList[Index]; |
241 | else if(CurTab == ASSETS_TAB_HUD) |
242 | return gs_vpSearchHudList[Index]; |
243 | else if(CurTab == ASSETS_TAB_EXTRAS) |
244 | return gs_vpSearchExtrasList[Index]; |
245 | |
246 | return NULL; |
247 | } |
248 | |
249 | template<typename TName> |
250 | void ClearAssetList(std::vector<TName> &vList, IGraphics *pGraphics) |
251 | { |
252 | for(size_t i = 0; i < vList.size(); ++i) |
253 | { |
254 | if(vList[i].m_RenderTexture.IsValid()) |
255 | pGraphics->UnloadTexture(pIndex: &(vList[i].m_RenderTexture)); |
256 | vList[i].m_RenderTexture = IGraphics::CTextureHandle(); |
257 | } |
258 | vList.clear(); |
259 | } |
260 | |
261 | void CMenus::(int CurTab) |
262 | { |
263 | if(CurTab == ASSETS_TAB_ENTITIES) |
264 | { |
265 | for(auto &Entity : m_vEntitiesList) |
266 | { |
267 | for(auto &Image : Entity.m_aImages) |
268 | { |
269 | if(Image.m_Texture.IsValid()) |
270 | Graphics()->UnloadTexture(pIndex: &Image.m_Texture); |
271 | Image.m_Texture = IGraphics::CTextureHandle(); |
272 | } |
273 | } |
274 | m_vEntitiesList.clear(); |
275 | |
276 | // reload current entities |
277 | m_pClient->m_MapImages.ChangeEntitiesPath(pPath: g_Config.m_ClAssetsEntities); |
278 | } |
279 | else if(CurTab == ASSETS_TAB_GAME) |
280 | { |
281 | ClearAssetList(vList&: m_vGameList, pGraphics: Graphics()); |
282 | |
283 | // reload current game skin |
284 | GameClient()->LoadGameSkin(pPath: g_Config.m_ClAssetGame); |
285 | } |
286 | else if(CurTab == ASSETS_TAB_EMOTICONS) |
287 | { |
288 | ClearAssetList(vList&: m_vEmoticonList, pGraphics: Graphics()); |
289 | |
290 | // reload current emoticons skin |
291 | GameClient()->LoadEmoticonsSkin(pPath: g_Config.m_ClAssetEmoticons); |
292 | } |
293 | else if(CurTab == ASSETS_TAB_PARTICLES) |
294 | { |
295 | ClearAssetList(vList&: m_vParticlesList, pGraphics: Graphics()); |
296 | |
297 | // reload current particles skin |
298 | GameClient()->LoadParticlesSkin(pPath: g_Config.m_ClAssetParticles); |
299 | } |
300 | else if(CurTab == ASSETS_TAB_HUD) |
301 | { |
302 | ClearAssetList(vList&: m_vHudList, pGraphics: Graphics()); |
303 | |
304 | // reload current hud skin |
305 | GameClient()->LoadHudSkin(pPath: g_Config.m_ClAssetHud); |
306 | } |
307 | else if(CurTab == ASSETS_TAB_EXTRAS) |
308 | { |
309 | ClearAssetList(vList&: m_vExtrasList, pGraphics: Graphics()); |
310 | |
311 | // reload current DDNet particles skin |
312 | GameClient()->LoadExtrasSkin(pPath: g_Config.m_ClAssetExtras); |
313 | } |
314 | gs_aInitCustomList[CurTab] = true; |
315 | } |
316 | |
317 | template<typename TName, typename TCaller> |
318 | void InitAssetList(std::vector<TName> &vAssetList, const char *pAssetPath, const char *pAssetName, FS_LISTDIR_CALLBACK pfnCallback, IGraphics *pGraphics, IStorage *pStorage, TCaller Caller) |
319 | { |
320 | if(vAssetList.empty()) |
321 | { |
322 | TName AssetItem; |
323 | str_copy(AssetItem.m_aName, "default" ); |
324 | LoadAsset(&AssetItem, pAssetName, pGraphics); |
325 | vAssetList.push_back(AssetItem); |
326 | |
327 | // load assets |
328 | pStorage->ListDirectory(Type: IStorage::TYPE_ALL, pPath: pAssetPath, pfnCallback, pUser: Caller); |
329 | std::sort(vAssetList.begin(), vAssetList.end()); |
330 | } |
331 | if(vAssetList.size() != gs_aCustomListSize[s_CurCustomTab]) |
332 | gs_aInitCustomList[s_CurCustomTab] = true; |
333 | } |
334 | |
335 | template<typename TName> |
336 | int InitSearchList(std::vector<const TName *> &vpSearchList, std::vector<TName> &vAssetList) |
337 | { |
338 | vpSearchList.clear(); |
339 | int ListSize = vAssetList.size(); |
340 | for(int i = 0; i < ListSize; ++i) |
341 | { |
342 | const TName *pAsset = &vAssetList[i]; |
343 | |
344 | // filter quick search |
345 | if(!s_aFilterInputs[s_CurCustomTab].IsEmpty() && !str_utf8_find_nocase(pAsset->m_aName, s_aFilterInputs[s_CurCustomTab].GetString())) |
346 | continue; |
347 | |
348 | vpSearchList.push_back(pAsset); |
349 | } |
350 | return vAssetList.size(); |
351 | } |
352 | |
353 | void CMenus::(CUIRect MainView) |
354 | { |
355 | CUIRect TabBar, CustomList, QuickSearch, QuickSearchClearButton, DirectoryButton, ReloadButton; |
356 | |
357 | MainView.HSplitTop(Cut: 20.0f, pTop: &TabBar, pBottom: &MainView); |
358 | const float TabWidth = TabBar.w / NUMBER_OF_ASSETS_TABS; |
359 | static CButtonContainer s_aPageTabs[NUMBER_OF_ASSETS_TABS] = {}; |
360 | const char *apTabNames[NUMBER_OF_ASSETS_TABS] = { |
361 | Localize(pStr: "Entities" ), |
362 | Localize(pStr: "Game" ), |
363 | Localize(pStr: "Emoticons" ), |
364 | Localize(pStr: "Particles" ), |
365 | Localize(pStr: "HUD" ), |
366 | Localize(pStr: "Extras" )}; |
367 | |
368 | for(int Tab = ASSETS_TAB_ENTITIES; Tab < NUMBER_OF_ASSETS_TABS; ++Tab) |
369 | { |
370 | CUIRect Button; |
371 | TabBar.VSplitLeft(Cut: TabWidth, pLeft: &Button, pRight: &TabBar); |
372 | const int Corners = Tab == ASSETS_TAB_ENTITIES ? IGraphics::CORNER_L : Tab == NUMBER_OF_ASSETS_TABS - 1 ? IGraphics::CORNER_R : IGraphics::CORNER_NONE; |
373 | if(DoButton_MenuTab(pButtonContainer: &s_aPageTabs[Tab], pText: apTabNames[Tab], Checked: s_CurCustomTab == Tab, pRect: &Button, Corners, pAnimator: nullptr, pDefaultColor: nullptr, pActiveColor: nullptr, pHoverColor: nullptr, EdgeRounding: 4.0f)) |
374 | { |
375 | s_CurCustomTab = Tab; |
376 | } |
377 | } |
378 | |
379 | auto LoadStartTime = time_get_nanoseconds(); |
380 | SMenuAssetScanUser User; |
381 | User.m_pUser = this; |
382 | User.m_LoadedFunc = [&]() { |
383 | if(time_get_nanoseconds() - LoadStartTime > 500ms) |
384 | RenderLoading(pCaption: Localize(pStr: "Loading assets" ), pContent: "" , IncreaseCounter: 0, RenderLoadingBar: false); |
385 | }; |
386 | if(s_CurCustomTab == ASSETS_TAB_ENTITIES) |
387 | { |
388 | if(m_vEntitiesList.empty()) |
389 | { |
390 | SCustomEntities EntitiesItem; |
391 | str_copy(dst&: EntitiesItem.m_aName, src: "default" ); |
392 | LoadEntities(pEntitiesItem: &EntitiesItem, pUser: &User); |
393 | m_vEntitiesList.push_back(x: EntitiesItem); |
394 | |
395 | // load entities |
396 | Storage()->ListDirectory(Type: IStorage::TYPE_ALL, pPath: "assets/entities" , pfnCallback: EntitiesScan, pUser: &User); |
397 | std::sort(first: m_vEntitiesList.begin(), last: m_vEntitiesList.end()); |
398 | } |
399 | if(m_vEntitiesList.size() != gs_aCustomListSize[s_CurCustomTab]) |
400 | gs_aInitCustomList[s_CurCustomTab] = true; |
401 | } |
402 | else if(s_CurCustomTab == ASSETS_TAB_GAME) |
403 | { |
404 | InitAssetList(vAssetList&: m_vGameList, pAssetPath: "assets/game" , pAssetName: "game" , pfnCallback: GameScan, pGraphics: Graphics(), pStorage: Storage(), Caller: &User); |
405 | } |
406 | else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS) |
407 | { |
408 | InitAssetList(vAssetList&: m_vEmoticonList, pAssetPath: "assets/emoticons" , pAssetName: "emoticons" , pfnCallback: EmoticonsScan, pGraphics: Graphics(), pStorage: Storage(), Caller: &User); |
409 | } |
410 | else if(s_CurCustomTab == ASSETS_TAB_PARTICLES) |
411 | { |
412 | InitAssetList(vAssetList&: m_vParticlesList, pAssetPath: "assets/particles" , pAssetName: "particles" , pfnCallback: ParticlesScan, pGraphics: Graphics(), pStorage: Storage(), Caller: &User); |
413 | } |
414 | else if(s_CurCustomTab == ASSETS_TAB_HUD) |
415 | { |
416 | InitAssetList(vAssetList&: m_vHudList, pAssetPath: "assets/hud" , pAssetName: "hud" , pfnCallback: HudScan, pGraphics: Graphics(), pStorage: Storage(), Caller: &User); |
417 | } |
418 | else if(s_CurCustomTab == ASSETS_TAB_EXTRAS) |
419 | { |
420 | InitAssetList(vAssetList&: m_vExtrasList, pAssetPath: "assets/extras" , pAssetName: "extras" , pfnCallback: ExtrasScan, pGraphics: Graphics(), pStorage: Storage(), Caller: &User); |
421 | } |
422 | |
423 | MainView.HSplitTop(Cut: 10.0f, pTop: 0, pBottom: &MainView); |
424 | |
425 | // skin selector |
426 | MainView.HSplitTop(Cut: MainView.h - 10.0f - ms_ButtonHeight, pTop: &CustomList, pBottom: &MainView); |
427 | if(gs_aInitCustomList[s_CurCustomTab]) |
428 | { |
429 | int ListSize = 0; |
430 | if(s_CurCustomTab == ASSETS_TAB_ENTITIES) |
431 | { |
432 | gs_vpSearchEntitiesList.clear(); |
433 | ListSize = m_vEntitiesList.size(); |
434 | for(int i = 0; i < ListSize; ++i) |
435 | { |
436 | const SCustomEntities *pEntity = &m_vEntitiesList[i]; |
437 | |
438 | // filter quick search |
439 | if(!s_aFilterInputs[s_CurCustomTab].IsEmpty() && !str_utf8_find_nocase(haystack: pEntity->m_aName, needle: s_aFilterInputs[s_CurCustomTab].GetString())) |
440 | continue; |
441 | |
442 | gs_vpSearchEntitiesList.push_back(x: pEntity); |
443 | } |
444 | } |
445 | else if(s_CurCustomTab == ASSETS_TAB_GAME) |
446 | { |
447 | ListSize = InitSearchList(vpSearchList&: gs_vpSearchGamesList, vAssetList&: m_vGameList); |
448 | } |
449 | else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS) |
450 | { |
451 | ListSize = InitSearchList(vpSearchList&: gs_vpSearchEmoticonsList, vAssetList&: m_vEmoticonList); |
452 | } |
453 | else if(s_CurCustomTab == ASSETS_TAB_PARTICLES) |
454 | { |
455 | ListSize = InitSearchList(vpSearchList&: gs_vpSearchParticlesList, vAssetList&: m_vParticlesList); |
456 | } |
457 | else if(s_CurCustomTab == ASSETS_TAB_HUD) |
458 | { |
459 | ListSize = InitSearchList(vpSearchList&: gs_vpSearchHudList, vAssetList&: m_vHudList); |
460 | } |
461 | else if(s_CurCustomTab == ASSETS_TAB_EXTRAS) |
462 | { |
463 | ListSize = InitSearchList(vpSearchList&: gs_vpSearchExtrasList, vAssetList&: m_vExtrasList); |
464 | } |
465 | gs_aInitCustomList[s_CurCustomTab] = false; |
466 | gs_aCustomListSize[s_CurCustomTab] = ListSize; |
467 | } |
468 | |
469 | int OldSelected = -1; |
470 | float Margin = 10; |
471 | float TextureWidth = 150; |
472 | float TextureHeight = 150; |
473 | |
474 | size_t SearchListSize = 0; |
475 | |
476 | if(s_CurCustomTab == ASSETS_TAB_ENTITIES) |
477 | { |
478 | SearchListSize = gs_vpSearchEntitiesList.size(); |
479 | } |
480 | else if(s_CurCustomTab == ASSETS_TAB_GAME) |
481 | { |
482 | SearchListSize = gs_vpSearchGamesList.size(); |
483 | TextureHeight = 75; |
484 | } |
485 | else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS) |
486 | { |
487 | SearchListSize = gs_vpSearchEmoticonsList.size(); |
488 | } |
489 | else if(s_CurCustomTab == ASSETS_TAB_PARTICLES) |
490 | { |
491 | SearchListSize = gs_vpSearchParticlesList.size(); |
492 | } |
493 | else if(s_CurCustomTab == ASSETS_TAB_HUD) |
494 | { |
495 | SearchListSize = gs_vpSearchHudList.size(); |
496 | } |
497 | else if(s_CurCustomTab == ASSETS_TAB_EXTRAS) |
498 | { |
499 | SearchListSize = gs_vpSearchExtrasList.size(); |
500 | } |
501 | |
502 | static CListBox s_ListBox; |
503 | s_ListBox.DoStart(RowHeight: TextureHeight + 15.0f + 10.0f + Margin, NumItems: SearchListSize, ItemsPerRow: CustomList.w / (Margin + TextureWidth), RowsPerScroll: 1, SelectedIndex: OldSelected, pRect: &CustomList, Background: false); |
504 | for(size_t i = 0; i < SearchListSize; ++i) |
505 | { |
506 | const SCustomItem *pItem = GetCustomItem(CurTab: s_CurCustomTab, Index: i); |
507 | if(pItem == NULL) |
508 | continue; |
509 | |
510 | if(s_CurCustomTab == ASSETS_TAB_ENTITIES) |
511 | { |
512 | if(str_comp(a: pItem->m_aName, b: g_Config.m_ClAssetsEntities) == 0) |
513 | OldSelected = i; |
514 | } |
515 | else if(s_CurCustomTab == ASSETS_TAB_GAME) |
516 | { |
517 | if(str_comp(a: pItem->m_aName, b: g_Config.m_ClAssetGame) == 0) |
518 | OldSelected = i; |
519 | } |
520 | else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS) |
521 | { |
522 | if(str_comp(a: pItem->m_aName, b: g_Config.m_ClAssetEmoticons) == 0) |
523 | OldSelected = i; |
524 | } |
525 | else if(s_CurCustomTab == ASSETS_TAB_PARTICLES) |
526 | { |
527 | if(str_comp(a: pItem->m_aName, b: g_Config.m_ClAssetParticles) == 0) |
528 | OldSelected = i; |
529 | } |
530 | else if(s_CurCustomTab == ASSETS_TAB_HUD) |
531 | { |
532 | if(str_comp(a: pItem->m_aName, b: g_Config.m_ClAssetHud) == 0) |
533 | OldSelected = i; |
534 | } |
535 | else if(s_CurCustomTab == ASSETS_TAB_EXTRAS) |
536 | { |
537 | if(str_comp(a: pItem->m_aName, b: g_Config.m_ClAssetExtras) == 0) |
538 | OldSelected = i; |
539 | } |
540 | |
541 | const CListboxItem Item = s_ListBox.DoNextItem(pId: pItem, Selected: OldSelected >= 0 && (size_t)OldSelected == i); |
542 | CUIRect ItemRect = Item.m_Rect; |
543 | ItemRect.Margin(Cut: Margin / 2, pOtherRect: &ItemRect); |
544 | if(!Item.m_Visible) |
545 | continue; |
546 | |
547 | CUIRect TextureRect; |
548 | ItemRect.HSplitTop(Cut: 15, pTop: &ItemRect, pBottom: &TextureRect); |
549 | TextureRect.HSplitTop(Cut: 10, NULL, pBottom: &TextureRect); |
550 | Ui()->DoLabel(pRect: &ItemRect, pText: pItem->m_aName, Size: ItemRect.h - 2, Align: TEXTALIGN_MC); |
551 | if(pItem->m_RenderTexture.IsValid()) |
552 | { |
553 | Graphics()->WrapClamp(); |
554 | Graphics()->TextureSet(Texture: pItem->m_RenderTexture); |
555 | Graphics()->QuadsBegin(); |
556 | Graphics()->SetColor(r: 1, g: 1, b: 1, a: 1); |
557 | IGraphics::CQuadItem QuadItem(TextureRect.x + (TextureRect.w - TextureWidth) / 2, TextureRect.y + (TextureRect.h - TextureHeight) / 2, TextureWidth, TextureHeight); |
558 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
559 | Graphics()->QuadsEnd(); |
560 | Graphics()->WrapNormal(); |
561 | } |
562 | } |
563 | |
564 | const int NewSelected = s_ListBox.DoEnd(); |
565 | if(OldSelected != NewSelected) |
566 | { |
567 | if(GetCustomItem(CurTab: s_CurCustomTab, Index: NewSelected)->m_aName[0] != '\0') |
568 | { |
569 | if(s_CurCustomTab == ASSETS_TAB_ENTITIES) |
570 | { |
571 | str_copy(dst&: g_Config.m_ClAssetsEntities, src: GetCustomItem(CurTab: s_CurCustomTab, Index: NewSelected)->m_aName); |
572 | m_pClient->m_MapImages.ChangeEntitiesPath(pPath: GetCustomItem(CurTab: s_CurCustomTab, Index: NewSelected)->m_aName); |
573 | } |
574 | else if(s_CurCustomTab == ASSETS_TAB_GAME) |
575 | { |
576 | str_copy(dst&: g_Config.m_ClAssetGame, src: GetCustomItem(CurTab: s_CurCustomTab, Index: NewSelected)->m_aName); |
577 | GameClient()->LoadGameSkin(pPath: g_Config.m_ClAssetGame); |
578 | } |
579 | else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS) |
580 | { |
581 | str_copy(dst&: g_Config.m_ClAssetEmoticons, src: GetCustomItem(CurTab: s_CurCustomTab, Index: NewSelected)->m_aName); |
582 | GameClient()->LoadEmoticonsSkin(pPath: g_Config.m_ClAssetEmoticons); |
583 | } |
584 | else if(s_CurCustomTab == ASSETS_TAB_PARTICLES) |
585 | { |
586 | str_copy(dst&: g_Config.m_ClAssetParticles, src: GetCustomItem(CurTab: s_CurCustomTab, Index: NewSelected)->m_aName); |
587 | GameClient()->LoadParticlesSkin(pPath: g_Config.m_ClAssetParticles); |
588 | } |
589 | else if(s_CurCustomTab == ASSETS_TAB_HUD) |
590 | { |
591 | str_copy(dst&: g_Config.m_ClAssetHud, src: GetCustomItem(CurTab: s_CurCustomTab, Index: NewSelected)->m_aName); |
592 | GameClient()->LoadHudSkin(pPath: g_Config.m_ClAssetHud); |
593 | } |
594 | else if(s_CurCustomTab == ASSETS_TAB_EXTRAS) |
595 | { |
596 | str_copy(dst&: g_Config.m_ClAssetExtras, src: GetCustomItem(CurTab: s_CurCustomTab, Index: NewSelected)->m_aName); |
597 | GameClient()->LoadExtrasSkin(pPath: g_Config.m_ClAssetExtras); |
598 | } |
599 | } |
600 | } |
601 | |
602 | // render quick search |
603 | { |
604 | MainView.HSplitBottom(Cut: ms_ButtonHeight, pTop: &MainView, pBottom: &QuickSearch); |
605 | QuickSearch.VSplitLeft(Cut: 240.0f, pLeft: &QuickSearch, pRight: &DirectoryButton); |
606 | QuickSearch.HSplitTop(Cut: 5.0f, pTop: 0, pBottom: &QuickSearch); |
607 | TextRender()->SetFontPreset(EFontPreset::ICON_FONT); |
608 | 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); |
609 | |
610 | Ui()->DoLabel(pRect: &QuickSearch, pText: FONT_ICON_MAGNIFYING_GLASS, Size: 14.0f, Align: TEXTALIGN_ML); |
611 | float SearchWidth = TextRender()->TextWidth(Size: 14.0f, pText: FONT_ICON_MAGNIFYING_GLASS, StrLength: -1, LineWidth: -1.0f); |
612 | TextRender()->SetRenderFlags(0); |
613 | TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); |
614 | QuickSearch.VSplitLeft(Cut: SearchWidth, pLeft: 0, pRight: &QuickSearch); |
615 | QuickSearch.VSplitLeft(Cut: 5.0f, pLeft: 0, pRight: &QuickSearch); |
616 | QuickSearch.VSplitLeft(Cut: QuickSearch.w - 10.0f, pLeft: &QuickSearch, pRight: &QuickSearchClearButton); |
617 | if(Input()->KeyPress(Key: KEY_F) && Input()->ModifierIsPressed()) |
618 | { |
619 | Ui()->SetActiveItem(&s_aFilterInputs[s_CurCustomTab]); |
620 | s_aFilterInputs[s_CurCustomTab].SelectAll(); |
621 | } |
622 | s_aFilterInputs[s_CurCustomTab].SetEmptyText(Localize(pStr: "Search" )); |
623 | if(Ui()->DoClearableEditBox(pLineInput: &s_aFilterInputs[s_CurCustomTab], pRect: &QuickSearch, FontSize: 14.0f)) |
624 | gs_aInitCustomList[s_CurCustomTab] = true; |
625 | } |
626 | |
627 | DirectoryButton.HSplitTop(Cut: 5.0f, pTop: 0, pBottom: &DirectoryButton); |
628 | DirectoryButton.VSplitRight(Cut: 175.0f, pLeft: 0, pRight: &DirectoryButton); |
629 | DirectoryButton.VSplitRight(Cut: 25.0f, pLeft: &DirectoryButton, pRight: &ReloadButton); |
630 | DirectoryButton.VSplitRight(Cut: 10.0f, pLeft: &DirectoryButton, pRight: 0); |
631 | static CButtonContainer s_AssetsDirId; |
632 | if(DoButton_Menu(pButtonContainer: &s_AssetsDirId, pText: Localize(pStr: "Assets directory" ), Checked: 0, pRect: &DirectoryButton)) |
633 | { |
634 | char aBuf[IO_MAX_PATH_LENGTH]; |
635 | char aBufFull[IO_MAX_PATH_LENGTH + 7]; |
636 | if(s_CurCustomTab == ASSETS_TAB_ENTITIES) |
637 | str_copy(dst&: aBufFull, src: "assets/entities" ); |
638 | else if(s_CurCustomTab == ASSETS_TAB_GAME) |
639 | str_copy(dst&: aBufFull, src: "assets/game" ); |
640 | else if(s_CurCustomTab == ASSETS_TAB_EMOTICONS) |
641 | str_copy(dst&: aBufFull, src: "assets/emoticons" ); |
642 | else if(s_CurCustomTab == ASSETS_TAB_PARTICLES) |
643 | str_copy(dst&: aBufFull, src: "assets/particles" ); |
644 | else if(s_CurCustomTab == ASSETS_TAB_HUD) |
645 | str_copy(dst&: aBufFull, src: "assets/hud" ); |
646 | else if(s_CurCustomTab == ASSETS_TAB_EXTRAS) |
647 | str_copy(dst&: aBufFull, src: "assets/extras" ); |
648 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, pDir: aBufFull, pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
649 | Storage()->CreateFolder(pFoldername: "assets" , Type: IStorage::TYPE_SAVE); |
650 | Storage()->CreateFolder(pFoldername: aBufFull, Type: IStorage::TYPE_SAVE); |
651 | Client()->ViewFile(pFilename: aBuf); |
652 | } |
653 | GameClient()->m_Tooltips.DoToolTip(pId: &s_AssetsDirId, pNearRect: &DirectoryButton, pText: Localize(pStr: "Open the directory to add custom assets" )); |
654 | |
655 | TextRender()->SetFontPreset(EFontPreset::ICON_FONT); |
656 | 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); |
657 | static CButtonContainer s_AssetsReloadBtnId; |
658 | if(DoButton_Menu(pButtonContainer: &s_AssetsReloadBtnId, pText: FONT_ICON_ARROW_ROTATE_RIGHT, Checked: 0, pRect: &ReloadButton) || Input()->KeyPress(Key: KEY_F5) || (Input()->KeyPress(Key: KEY_R) && Input()->ModifierIsPressed())) |
659 | { |
660 | ClearCustomItems(CurTab: s_CurCustomTab); |
661 | } |
662 | TextRender()->SetRenderFlags(0); |
663 | TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); |
664 | } |
665 | |
666 | void CMenus::(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) |
667 | { |
668 | CMenus *pThis = (CMenus *)pUserData; |
669 | if(pResult->NumArguments() == 1) |
670 | { |
671 | const char *pArg = pResult->GetString(Index: 0); |
672 | if(str_comp(a: pArg, b: g_Config.m_ClAssetsEntities) != 0) |
673 | { |
674 | pThis->m_pClient->m_MapImages.ChangeEntitiesPath(pPath: pArg); |
675 | } |
676 | } |
677 | |
678 | pfnCallback(pResult, pCallbackUserData); |
679 | } |
680 | |
681 | void CMenus::(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) |
682 | { |
683 | CMenus *pThis = (CMenus *)pUserData; |
684 | if(pResult->NumArguments() == 1) |
685 | { |
686 | const char *pArg = pResult->GetString(Index: 0); |
687 | if(str_comp(a: pArg, b: g_Config.m_ClAssetGame) != 0) |
688 | { |
689 | pThis->GameClient()->LoadGameSkin(pPath: pArg); |
690 | } |
691 | } |
692 | |
693 | pfnCallback(pResult, pCallbackUserData); |
694 | } |
695 | |
696 | void CMenus::ConchainAssetParticles(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) |
697 | { |
698 | CMenus *pThis = (CMenus *)pUserData; |
699 | if(pResult->NumArguments() == 1) |
700 | { |
701 | const char *pArg = pResult->GetString(Index: 0); |
702 | if(str_comp(a: pArg, b: g_Config.m_ClAssetParticles) != 0) |
703 | { |
704 | pThis->GameClient()->LoadParticlesSkin(pPath: pArg); |
705 | } |
706 | } |
707 | |
708 | pfnCallback(pResult, pCallbackUserData); |
709 | } |
710 | |
711 | void CMenus::(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) |
712 | { |
713 | CMenus *pThis = (CMenus *)pUserData; |
714 | if(pResult->NumArguments() == 1) |
715 | { |
716 | const char *pArg = pResult->GetString(Index: 0); |
717 | if(str_comp(a: pArg, b: g_Config.m_ClAssetEmoticons) != 0) |
718 | { |
719 | pThis->GameClient()->LoadEmoticonsSkin(pPath: pArg); |
720 | } |
721 | } |
722 | |
723 | pfnCallback(pResult, pCallbackUserData); |
724 | } |
725 | |
726 | void CMenus::(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) |
727 | { |
728 | CMenus *pThis = (CMenus *)pUserData; |
729 | if(pResult->NumArguments() == 1) |
730 | { |
731 | const char *pArg = pResult->GetString(Index: 0); |
732 | if(str_comp(a: pArg, b: g_Config.m_ClAssetHud) != 0) |
733 | { |
734 | pThis->GameClient()->LoadHudSkin(pPath: pArg); |
735 | } |
736 | } |
737 | |
738 | pfnCallback(pResult, pCallbackUserData); |
739 | } |
740 | |
741 | void CMenus::(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) |
742 | { |
743 | CMenus *pThis = (CMenus *)pUserData; |
744 | if(pResult->NumArguments() == 1) |
745 | { |
746 | const char *pArg = pResult->GetString(Index: 0); |
747 | if(str_comp(a: pArg, b: g_Config.m_ClAssetExtras) != 0) |
748 | { |
749 | pThis->GameClient()->LoadExtrasSkin(pPath: pArg); |
750 | } |
751 | } |
752 | |
753 | pfnCallback(pResult, pCallbackUserData); |
754 | } |
755 | |