From 3ab4c52dd8ee2bb7c80c37ec1df9d63464ba1880 Mon Sep 17 00:00:00 2001 From: Samuele Radici <68398653+k-i-o@users.noreply.github.com> Date: Mon, 14 Aug 2023 14:57:08 +0200 Subject: [PATCH 001/126] Update gameclient.cpp I fixed the camera, when you were in multiview and you exited the pause mode the camera remained so dezoomed --- src/game/client/gameclient.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index aeead0a24..a160b911c 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -639,8 +639,10 @@ void CGameClient::UpdatePositions() if(!m_MultiViewActivated && m_MultiView.m_IsInit) ResetMultiView(); - else if(!m_Snap.m_SpecInfo.m_Active) + else if(!m_Snap.m_SpecInfo.m_Active){ + m_Camera.SetZoom(std::pow(0.866025f, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); m_MultiViewPersonalZoom = 0; + } UpdateRenderedCharacters(); } @@ -3739,6 +3741,7 @@ float CGameClient::MapValue(float MaxValue, float MinValue, float MaxRange, floa void CGameClient::ResetMultiView() { + m_Camera.SetZoom(std::pow(0.866025f, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); m_MultiViewPersonalZoom = 0; m_MultiViewActivated = false; m_MultiView.m_Solo = false; From 05704652745531e6977dcb4cc69cf1ef8d8423bb Mon Sep 17 00:00:00 2001 From: Samuele Radici <68398653+k-i-o@users.noreply.github.com> Date: Mon, 14 Aug 2023 17:21:03 +0000 Subject: [PATCH 002/126] . --- src/game/client/components/camera.cpp | 1 - src/game/client/components/camera.h | 2 ++ src/game/client/gameclient.cpp | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp index 839486b9b..5bb311fe7 100644 --- a/src/game/client/components/camera.cpp +++ b/src/game/client/components/camera.cpp @@ -13,7 +13,6 @@ #include -const float ZoomStep = 0.866025f; CCamera::CCamera() { diff --git a/src/game/client/components/camera.h b/src/game/client/components/camera.h index 9cfc8bf8d..0449ca673 100644 --- a/src/game/client/components/camera.h +++ b/src/game/client/components/camera.h @@ -37,6 +37,8 @@ class CCamera : public CComponent float MaxZoomLevel(); public: + static constexpr const float ZoomStep = 0.866025f; + vec2 m_Center; bool m_ZoomSet; bool m_Zooming; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index a160b911c..2313d6727 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -639,8 +639,9 @@ void CGameClient::UpdatePositions() if(!m_MultiViewActivated && m_MultiView.m_IsInit) ResetMultiView(); - else if(!m_Snap.m_SpecInfo.m_Active){ - m_Camera.SetZoom(std::pow(0.866025f, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); + else if(!m_Snap.m_SpecInfo.m_Active) + { + m_Camera.SetZoom(std::pow(m_Camera.ZoomStep, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); m_MultiViewPersonalZoom = 0; } From 4e1fbd71c8c2765777f664b54fa2e409a9fe55c2 Mon Sep 17 00:00:00 2001 From: Samuele Radici <68398653+k-i-o@users.noreply.github.com> Date: Thu, 17 Aug 2023 00:32:44 +0000 Subject: [PATCH 003/126] Fixed, now my pc is alive xd --- src/game/client/components/camera.cpp | 1 - src/game/client/components/camera.h | 2 +- src/game/client/gameclient.cpp | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp index 5bb311fe7..2a74b1b56 100644 --- a/src/game/client/components/camera.cpp +++ b/src/game/client/components/camera.cpp @@ -10,7 +10,6 @@ #include "camera.h" #include "controls.h" - #include diff --git a/src/game/client/components/camera.h b/src/game/client/components/camera.h index 0449ca673..ae8798d86 100644 --- a/src/game/client/components/camera.h +++ b/src/game/client/components/camera.h @@ -37,7 +37,7 @@ class CCamera : public CComponent float MaxZoomLevel(); public: - static constexpr const float ZoomStep = 0.866025f; + static constexpr float ZOOM_STEP = 0.866025f; vec2 m_Center; bool m_ZoomSet; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 2313d6727..19319b0de 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -641,7 +641,7 @@ void CGameClient::UpdatePositions() ResetMultiView(); else if(!m_Snap.m_SpecInfo.m_Active) { - m_Camera.SetZoom(std::pow(m_Camera.ZoomStep, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); + m_Camera.SetZoom(std::pow(CCamera::ZOOM_STEP, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); m_MultiViewPersonalZoom = 0; } From 5e923adcc9517bd595d910824f1ba376c751d641 Mon Sep 17 00:00:00 2001 From: dobrykafe <121701317+dobrykafe@users.noreply.github.com> Date: Sat, 26 Aug 2023 18:14:13 +0200 Subject: [PATCH 004/126] fix map drag and drop --- src/engine/client/client.cpp | 7 ++----- src/engine/editor.h | 1 + src/game/editor/editor.h | 5 +++++ src/game/editor/io.cpp | 21 +++++++++++++++++++++ src/game/editor/popups.cpp | 27 +++++++++++++++++++++++---- 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index eaac7b945..960162c07 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -3120,11 +3120,8 @@ void CClient::Run() // handle pending map edits if(m_aCmdEditMap[0]) { - int Result = m_pEditor->Load(m_aCmdEditMap, IStorage::TYPE_ALL_OR_ABSOLUTE); - if(Result) - g_Config.m_ClEditor = true; - else - dbg_msg("editor", "editing passed map file '%s' failed", m_aCmdEditMap); + g_Config.m_ClEditor = true; + m_pEditor->HandleMapDrop(m_aCmdEditMap, IStorage::TYPE_ALL_OR_ABSOLUTE); m_aCmdEditMap[0] = 0; } diff --git a/src/engine/editor.h b/src/engine/editor.h index 290e81898..46d1d4e36 100644 --- a/src/engine/editor.h +++ b/src/engine/editor.h @@ -15,6 +15,7 @@ public: virtual void OnActivate() = 0; virtual void OnWindowResize() = 0; virtual bool HasUnsavedData() const = 0; + virtual void HandleMapDrop(const char *pFilename, int StorageType) = 0; virtual bool Load(const char *pFilename, int StorageType) = 0; virtual bool Save(const char *pFilename) = 0; virtual void UpdateMentions() = 0; diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index b32dfbb0a..8ddf3839a 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -523,6 +523,7 @@ public: // io bool Save(const char *pFilename); bool Load(const char *pFilename, int StorageType, const std::function &ErrorHandler); + void HandleMapDrop(const char *pFilename, int StorageType); void PerformSanityChecks(const std::function &ErrorHandler); // DDRace @@ -867,6 +868,7 @@ public: m_BrushColorEnabled = true; m_aFileName[0] = '\0'; + m_aFileNamePending[0] = '\0'; m_aFileSaveName[0] = '\0'; m_ValidSaveFilename = false; @@ -982,6 +984,7 @@ public: void Reset(bool CreateDefault = true); bool Save(const char *pFilename) override; bool Load(const char *pFilename, int StorageType) override; + void HandleMapDrop(const char *pFilename, int StorageType) override; bool Append(const char *pFilename, int StorageType); void LoadCurrentMap(); void Render(); @@ -1040,6 +1043,7 @@ public: bool m_BrushColorEnabled; char m_aFileName[IO_MAX_PATH_LENGTH]; + char m_aFileNamePending[IO_MAX_PATH_LENGTH]; char m_aFileSaveName[IO_MAX_PATH_LENGTH]; bool m_ValidSaveFilename; @@ -1048,6 +1052,7 @@ public: POPEVENT_EXIT = 0, POPEVENT_LOAD, POPEVENT_LOADCURRENT, + POPEVENT_LOADDROP, POPEVENT_NEW, POPEVENT_SAVE, POPEVENT_SAVE_COPY, diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index a5516e18a..a490a1952 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -426,6 +426,27 @@ bool CEditorMap::Save(const char *pFileName) return true; } +void CEditor::HandleMapDrop(const char *pFileName, int StorageType) +{ + m_Map.HandleMapDrop(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); +} + +void CEditorMap::HandleMapDrop(const char *pFileName, int StorageType) +{ + if(m_pEditor->HasUnsavedData()) + { + str_copy(m_pEditor->m_aFileNamePending, pFileName); + m_pEditor->m_PopupEventType = CEditor::POPEVENT_LOADDROP; + m_pEditor->m_PopupEventActivated = true; + } + else + { + int Result = m_pEditor->Load(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); + if(!Result) + dbg_msg("editor", "editing passed map file '%s' failed", pFileName); + } +} + bool CEditor::Load(const char *pFileName, int StorageType) { const auto &&ErrorHandler = [this](const char *pErrorMessage) { diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 188a57dfe..0c2234d1f 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1721,7 +1721,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, pTitle = "Exit the editor"; pMessage = "The map contains unsaved data, you might want to save it before you exit the editor.\n\nContinue anyway?"; } - else if(pEditor->m_PopupEventType == POPEVENT_LOAD || pEditor->m_PopupEventType == POPEVENT_LOADCURRENT) + else if(pEditor->m_PopupEventType == POPEVENT_LOAD || pEditor->m_PopupEventType == POPEVENT_LOADCURRENT || pEditor->m_PopupEventType == POPEVENT_LOADDROP) { pTitle = "Load map"; pMessage = "The map contains unsaved data, you might want to save it before you load a new map.\n\nContinue anyway?"; @@ -1786,10 +1786,22 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, if(pEditor->m_PopupEventType != POPEVENT_LARGELAYER && pEditor->m_PopupEventType != POPEVENT_PREVENTUNUSEDTILES && pEditor->m_PopupEventType != POPEVENT_IMAGEDIV16 && pEditor->m_PopupEventType != POPEVENT_IMAGE_MAX) { static int s_CancelButton = 0; - if(pEditor->DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, nullptr)) + if(pEditor->m_PopupEventType == POPEVENT_LOADDROP) { - pEditor->m_PopupEventWasActivated = false; - return CUI::POPUP_CLOSE_CURRENT; + if(pEditor->DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, nullptr)) + { + pEditor->m_aFileNamePending[0] = 0; + pEditor->m_PopupEventWasActivated = false; + return CUI::POPUP_CLOSE_CURRENT; + } + } + else + { + if(pEditor->DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, nullptr)) + { + pEditor->m_PopupEventWasActivated = false; + return CUI::POPUP_CLOSE_CURRENT; + } } } @@ -1809,6 +1821,13 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, { pEditor->LoadCurrentMap(); } + else if(pEditor->m_PopupEventType == POPEVENT_LOADDROP) + { + int Result = pEditor->Load(pEditor->m_aFileNamePending, IStorage::TYPE_ALL_OR_ABSOLUTE); + if(!Result) + dbg_msg("editor", "editing passed map file '%s' failed", pEditor->m_aFileNamePending); + pEditor->m_aFileNamePending[0] = 0; + } else if(pEditor->m_PopupEventType == POPEVENT_NEW) { pEditor->Reset(); From 8957ae99bdbf99d718af27b562cb152e5ea77dcc Mon Sep 17 00:00:00 2001 From: dobrykafe <121701317+dobrykafe@users.noreply.github.com> Date: Sat, 26 Aug 2023 19:44:53 +0200 Subject: [PATCH 005/126] remove whitespace --- src/game/editor/io.cpp | 2 +- src/game/editor/popups.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index a490a1952..e04930f02 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -432,7 +432,7 @@ void CEditor::HandleMapDrop(const char *pFileName, int StorageType) } void CEditorMap::HandleMapDrop(const char *pFileName, int StorageType) -{ +{ if(m_pEditor->HasUnsavedData()) { str_copy(m_pEditor->m_aFileNamePending, pFileName); diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 0c2234d1f..ad81fc11f 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1822,12 +1822,12 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, pEditor->LoadCurrentMap(); } else if(pEditor->m_PopupEventType == POPEVENT_LOADDROP) - { + { int Result = pEditor->Load(pEditor->m_aFileNamePending, IStorage::TYPE_ALL_OR_ABSOLUTE); if(!Result) dbg_msg("editor", "editing passed map file '%s' failed", pEditor->m_aFileNamePending); pEditor->m_aFileNamePending[0] = 0; - } + } else if(pEditor->m_PopupEventType == POPEVENT_NEW) { pEditor->Reset(); From cb29ad2b4f7182ce4abc70daa5ca913b1784457d Mon Sep 17 00:00:00 2001 From: dobrykafe <121701317+dobrykafe@users.noreply.github.com> Date: Sat, 26 Aug 2023 22:13:13 +0200 Subject: [PATCH 006/126] dont switch to editor if loading fails --- src/engine/client/client.cpp | 7 +++++-- src/engine/editor.h | 2 +- src/game/editor/editor.h | 4 ++-- src/game/editor/io.cpp | 11 +++++------ 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 960162c07..1d940f381 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -3120,8 +3120,11 @@ void CClient::Run() // handle pending map edits if(m_aCmdEditMap[0]) { - g_Config.m_ClEditor = true; - m_pEditor->HandleMapDrop(m_aCmdEditMap, IStorage::TYPE_ALL_OR_ABSOLUTE); + int Result = m_pEditor->HandleMapDrop(m_aCmdEditMap, IStorage::TYPE_ALL_OR_ABSOLUTE); + if(Result) + g_Config.m_ClEditor = true; + else + dbg_msg("editor", "editing passed map file '%s' failed", m_aCmdEditMap); m_aCmdEditMap[0] = 0; } diff --git a/src/engine/editor.h b/src/engine/editor.h index 46d1d4e36..9acefc5d4 100644 --- a/src/engine/editor.h +++ b/src/engine/editor.h @@ -15,7 +15,7 @@ public: virtual void OnActivate() = 0; virtual void OnWindowResize() = 0; virtual bool HasUnsavedData() const = 0; - virtual void HandleMapDrop(const char *pFilename, int StorageType) = 0; + virtual bool HandleMapDrop(const char *pFilename, int StorageType) = 0; virtual bool Load(const char *pFilename, int StorageType) = 0; virtual bool Save(const char *pFilename) = 0; virtual void UpdateMentions() = 0; diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 8ddf3839a..5e498df9c 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -523,7 +523,7 @@ public: // io bool Save(const char *pFilename); bool Load(const char *pFilename, int StorageType, const std::function &ErrorHandler); - void HandleMapDrop(const char *pFilename, int StorageType); + bool HandleMapDrop(const char *pFilename, int StorageType); void PerformSanityChecks(const std::function &ErrorHandler); // DDRace @@ -984,7 +984,7 @@ public: void Reset(bool CreateDefault = true); bool Save(const char *pFilename) override; bool Load(const char *pFilename, int StorageType) override; - void HandleMapDrop(const char *pFilename, int StorageType) override; + bool HandleMapDrop(const char *pFilename, int StorageType) override; bool Append(const char *pFilename, int StorageType); void LoadCurrentMap(); void Render(); diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index e04930f02..4f530852d 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -426,24 +426,23 @@ bool CEditorMap::Save(const char *pFileName) return true; } -void CEditor::HandleMapDrop(const char *pFileName, int StorageType) +bool CEditor::HandleMapDrop(const char *pFileName, int StorageType) { - m_Map.HandleMapDrop(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); + return m_Map.HandleMapDrop(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); } -void CEditorMap::HandleMapDrop(const char *pFileName, int StorageType) +bool CEditorMap::HandleMapDrop(const char *pFileName, int StorageType) { if(m_pEditor->HasUnsavedData()) { str_copy(m_pEditor->m_aFileNamePending, pFileName); m_pEditor->m_PopupEventType = CEditor::POPEVENT_LOADDROP; m_pEditor->m_PopupEventActivated = true; + return true; } else { - int Result = m_pEditor->Load(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); - if(!Result) - dbg_msg("editor", "editing passed map file '%s' failed", pFileName); + return m_pEditor->Load(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); } } From 758601c310cd68a1c17ff15151b0e1d3d3922c6e Mon Sep 17 00:00:00 2001 From: dobrykafe <121701317+dobrykafe@users.noreply.github.com> Date: Sat, 26 Aug 2023 22:28:19 +0200 Subject: [PATCH 007/126] remove duplicate code --- src/game/editor/popups.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index ad81fc11f..0856859a0 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1786,22 +1786,12 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, if(pEditor->m_PopupEventType != POPEVENT_LARGELAYER && pEditor->m_PopupEventType != POPEVENT_PREVENTUNUSEDTILES && pEditor->m_PopupEventType != POPEVENT_IMAGEDIV16 && pEditor->m_PopupEventType != POPEVENT_IMAGE_MAX) { static int s_CancelButton = 0; - if(pEditor->m_PopupEventType == POPEVENT_LOADDROP) + if(pEditor->DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, nullptr)) { - if(pEditor->DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, nullptr)) - { + if(pEditor->m_PopupEventType == POPEVENT_LOADDROP) pEditor->m_aFileNamePending[0] = 0; - pEditor->m_PopupEventWasActivated = false; - return CUI::POPUP_CLOSE_CURRENT; - } - } - else - { - if(pEditor->DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, nullptr)) - { - pEditor->m_PopupEventWasActivated = false; - return CUI::POPUP_CLOSE_CURRENT; - } + pEditor->m_PopupEventWasActivated = false; + return CUI::POPUP_CLOSE_CURRENT; } } From b4e0e49ccb4bf654797d11157fa5e9fd16d37ebc Mon Sep 17 00:00:00 2001 From: JuraIBOZO <142632373+JuraIBOZO@users.noreply.github.com> Date: Sun, 27 Aug 2023 12:02:55 +0300 Subject: [PATCH 008/126] Update serbian.txt Update Serbian translation (by Jurai!) --- data/languages/serbian.txt | 107 +++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt index 565b6ae6c..c457cfe65 100644 --- a/data/languages/serbian.txt +++ b/data/languages/serbian.txt @@ -1517,168 +1517,169 @@ Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.c [Graphics error] Could not initialize the given graphics backend, reverting to the default backend now. -== +== Није било могуће иницијализовати дати графички позадину, сада се враћамо на подразумевану позадину. [Graphics error] Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. == +Није било могуће иницијализовати дати графички позадину, највероватније зато што нисте инсталирали драјвер за интегрисану графичку картицу. Could not save downloaded map. Try manually deleting this file: %s -== +== Није могуће сачувати преузету мапу. Покушајте ручно обрисати овај фајл: %s. Initializing components == Иницијализација компоненти Quitting. Please wait… -== Излазим. Молим вас да почекате… +== Излазим. Молим вас да почекате... Restarting. Please wait… -== Поновно покрећем се. Молим вас да почекате… +== Поновно покрећем се. Молим вас да почекате... Multi-View -== +== Мулти-поглед Rename folder -== +== Преименујте фасциклу A demo with this name already exists -== +== Демо са овим именом већ постоји. A folder with this name already exists -== +== Фасцикла са овим именом већ постоји. Unable to rename the folder -== +== Није могуће преименовати фасциклу. File '%s' already exists, do you want to overwrite it? -== +== Датотека '%s' већ постоји, желите ли да је препишете? (paused) -== +== (паузирано) transmits your player name to info.ddnet.org -== +== преноси ваше играчко име на info.ddnet.org Copy info -== +== Копирај информације No server selected -== +== Није изабран сервер Online players (%d) -== +== Играчи на мрежи (%d) Online clanmates (%d) -== +== Чланови клана на мрежи (%d) [friends (server browser)] Offline (%d) -== +== Изван мреже (%d) Click to select server. Double click to join your friend. -== +== Кликните да бисте изабрали сервер. Дупли клик за придруживање пријатељу. Click to remove this player from your friends list. -== +== Кликните да бисте уклонили овог играча са листе пријатеља. Click to remove this clan from your friends list. -== +== Кликните да бисте уклонили овај клан са листе пријатеља. None -== +== Ништа Are you sure that you want to remove the player '%s' from your friends list? -== +== Да ли сте сигурни да желите да уклоните играча '%s' са листе пријатеља? Are you sure that you want to remove the clan '%s' from your friends list? -== +== Да ли сте сигурни да желите да уклоните клан '%s' са листе пријатеља? Add Clan -== +== Додај клан Play the current demo -== +== Пусти тренутни демо запис Pause the current demo -== +== Паузирај тренутни демо запис Stop the current demo -== +== Заустави тренутни демо запис Go back one tick -== +== Иди назад један корак Go forward one tick -== +== Иди напред један корак Slow down the demo -== +== Успори демо запис Speed up the demo -== +== Убрзај демо запис Mark the beginning of a cut (right click to reset) -== +== Обележи почетак сечења (десни клик за ресетовање) Mark the end of a cut (right click to reset) -== +== Обележи крај сечења (десни клик за ресетовање) Export cut as a separate demo -== +== Извоз сечења као засебног демо записа Go back one marker -== +== Иди назад један маркер Go forward one marker -== +== Иди напред један маркер Close the demo player -== +== Затвори репродуктор демо записа Toggle keyboard shortcuts -== +== Пребациванје тастатурних пречица Export demo cut -== +== Извоз демо сечења Cut interval -== +== Интервал сечења Cut length -== +== Дужина сечења All combined -== +== Све комбиновано Folder Link -== +== Веза до фасцикле Open the directory that contains the demo files -== +== Отвори директоријум који садржи демо фајлове Are you sure that you want to delete the folder '%s'? -== +== Да ли сте сигурни да желите да обришете фасциклу '%s'? Are you sure that you want to delete the demo '%s'? -== +== Да ли сте сигурни да желите да обришете демо запис '%s'? Delete folder -== +== Обриши фасциклу Unable to delete the demo '%s' -== +== Није могуће обрисати демо запис '%s' Unable to delete the folder '%s'. Make sure it's empty first. -== +== Није могуће обрисати фасциклу '%s'. Проверите прво да ли је празна. Menu opened. Press Esc key again to close menu. -== +== Мени је отворен. Поново притисните тастер Esc да бисте затворили мени. Save power by lowering refresh rate (higher input latency) -== +== Уштедите струју смањивањем стопе освежавања (већа улазна латенција) Open the settings file -== +== Отворите датотеку са подешавањима Open the directory that contains the configuration and user files == Отворите директоријум који садржи конфигурационе и корисничке датотеке. @@ -1747,4 +1748,4 @@ Open the directory to add custom assets == Отворите директоријум за додавање прилагођених ресурса Moved ingame -== +== Померено у игри From 669f3845359228fa38e1f231d19f92bf03150621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 27 Aug 2023 11:25:16 +0200 Subject: [PATCH 009/126] Update data/languages/serbian.txt --- data/languages/serbian.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt index c457cfe65..72a309e08 100644 --- a/data/languages/serbian.txt +++ b/data/languages/serbian.txt @@ -1521,8 +1521,7 @@ Could not initialize the given graphics backend, reverting to the default backen [Graphics error] Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. -== -Није било могуће иницијализовати дати графички позадину, највероватније зато што нисте инсталирали драјвер за интегрисану графичку картицу. +== Није било могуће иницијализовати дати графички позадину, највероватније зато што нисте инсталирали драјвер за интегрисану графичку картицу. Could not save downloaded map. Try manually deleting this file: %s == Није могуће сачувати преузету мапу. Покушајте ручно обрисати овај фајл: %s. From 605a93787eca69ce41beb2a2303880776825fada Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Sun, 27 Aug 2023 11:38:15 +0200 Subject: [PATCH 010/126] improve code style in proofmode, mapgrid and mapview --- src/game/editor/editor.cpp | 2 +- src/game/editor/map_grid.cpp | 9 ++++----- src/game/editor/map_view.cpp | 12 ++++++------ src/game/editor/map_view.h | 2 +- src/game/editor/proof_mode.cpp | 28 ++++++++++++++-------------- 5 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index bff0bfd14..74a765d74 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -140,7 +140,7 @@ void CLayerGroup::Mapping(float *pPoints) m_pMap->m_pEditor->RenderTools()->MapScreenToWorld( m_pMap->m_pEditor->MapView()->GetWorldOffset().x, m_pMap->m_pEditor->MapView()->GetWorldOffset().y, m_ParallaxX, m_ParallaxY, ParallaxZoom, m_OffsetX, m_OffsetY, - m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->MapView()->WorldZoom(), pPoints); + m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->MapView()->GetWorldZoom(), pPoints); pPoints[0] += m_pMap->m_pEditor->MapView()->GetEditorOffset().x; pPoints[1] += m_pMap->m_pEditor->MapView()->GetEditorOffset().y; diff --git a/src/game/editor/map_grid.cpp b/src/game/editor/map_grid.cpp index 7bcfb075e..f1babeb9c 100644 --- a/src/game/editor/map_grid.cpp +++ b/src/game/editor/map_grid.cpp @@ -23,8 +23,7 @@ void CMapGrid::OnRender(CUIRect View) float aGroupPoints[4]; pGroup->Mapping(aGroupPoints); - float w = UI()->Screen()->w; - float h = UI()->Screen()->h; + const CUIRect *pScreen = UI()->Screen(); int LineDistance = GridLineDistance(); @@ -36,14 +35,14 @@ void CMapGrid::OnRender(CUIRect View) Graphics()->TextureClear(); Graphics()->LinesBegin(); - for(int i = 0; i < (int)w; i++) + for(int i = 0; i < (int)pScreen->w; i++) { if((i + YGridOffset) % m_GridFactor == 0) Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f); else Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f); - IGraphics::CLineItem Line = IGraphics::CLineItem(LineDistance * XOffset, LineDistance * i + LineDistance * YOffset, w + aGroupPoints[2], LineDistance * i + LineDistance * YOffset); + IGraphics::CLineItem Line = IGraphics::CLineItem(LineDistance * XOffset, LineDistance * i + LineDistance * YOffset, pScreen->w + aGroupPoints[2], LineDistance * i + LineDistance * YOffset); Graphics()->LinesDraw(&Line, 1); if((i + XGridOffset) % m_GridFactor == 0) @@ -51,7 +50,7 @@ void CMapGrid::OnRender(CUIRect View) else Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f); - Line = IGraphics::CLineItem(LineDistance * i + LineDistance * XOffset, LineDistance * YOffset, LineDistance * i + LineDistance * XOffset, h + aGroupPoints[3]); + Line = IGraphics::CLineItem(LineDistance * i + LineDistance * XOffset, LineDistance * YOffset, LineDistance * i + LineDistance * XOffset, pScreen->h + aGroupPoints[3]); Graphics()->LinesDraw(&Line, 1); } Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); diff --git a/src/game/editor/map_view.cpp b/src/game/editor/map_view.cpp index 1c08c05e9..35bdd8c03 100644 --- a/src/game/editor/map_view.cpp +++ b/src/game/editor/map_view.cpp @@ -66,14 +66,14 @@ void CMapView::RenderGroupBorder() float w, h; pLayer->GetSize(&w, &h); - IGraphics::CLineItem Array[4] = { + IGraphics::CLineItem aArray[4] = { IGraphics::CLineItem(0, 0, w, 0), IGraphics::CLineItem(w, 0, w, h), IGraphics::CLineItem(w, h, 0, h), IGraphics::CLineItem(0, h, 0, 0)}; Graphics()->TextureClear(); Graphics()->LinesBegin(); - Graphics()->LinesDraw(Array, 4); + Graphics()->LinesDraw(aArray, std::size(aArray)); Graphics()->LinesEnd(); } } @@ -121,11 +121,11 @@ void CMapView::RenderMap() } } - std::shared_ptr pT = std::static_pointer_cast(Editor()->GetSelectedLayerType(0, LAYERTYPE_TILES)); - if(Editor()->m_ShowTileInfo != CEditor::SHOW_TILE_OFF && pT && pT->m_Visible && m_Zoom.GetValue() <= 300.0f) + std::shared_ptr pSelectedTilesLayer = std::static_pointer_cast(Editor()->GetSelectedLayerType(0, LAYERTYPE_TILES)); + if(Editor()->m_ShowTileInfo != CEditor::SHOW_TILE_OFF && pSelectedTilesLayer && pSelectedTilesLayer->m_Visible && m_Zoom.GetValue() <= 300.0f) { Editor()->GetSelectedGroup()->MapScreen(); - pT->ShowInfo(); + pSelectedTilesLayer->ShowInfo(); } } @@ -230,7 +230,7 @@ vec2 CMapView::GetEditorOffset() const return m_EditorOffset; } -float CMapView::WorldZoom() const +float CMapView::GetWorldZoom() const { return m_WorldZoom; } diff --git a/src/game/editor/map_view.h b/src/game/editor/map_view.h index 65a5d5118..5da4db236 100644 --- a/src/game/editor/map_view.h +++ b/src/game/editor/map_view.h @@ -38,7 +38,7 @@ public: bool m_ShowPicker; // TODO: make private - float WorldZoom() const; + float GetWorldZoom() const; void OffsetWorld(vec2 Offset); void OffsetEditor(vec2 Offset); diff --git a/src/game/editor/proof_mode.cpp b/src/game/editor/proof_mode.cpp index 5f957a499..779668835 100644 --- a/src/game/editor/proof_mode.cpp +++ b/src/game/editor/proof_mode.cpp @@ -119,28 +119,28 @@ void CProofMode::RenderScreenSizes() if(i == 0) { - IGraphics::CLineItem Array[2] = { + IGraphics::CLineItem aArray[2] = { IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[2], aPoints[1]), IGraphics::CLineItem(aPoints[0], aPoints[3], aPoints[2], aPoints[3])}; - Graphics()->LinesDraw(Array, 2); + Graphics()->LinesDraw(aArray, std::size(aArray)); } if(i != 0) { - IGraphics::CLineItem Array[4] = { + IGraphics::CLineItem aArray[4] = { IGraphics::CLineItem(aPoints[0], aPoints[1], aLastPoints[0], aLastPoints[1]), IGraphics::CLineItem(aPoints[2], aPoints[1], aLastPoints[2], aLastPoints[1]), IGraphics::CLineItem(aPoints[0], aPoints[3], aLastPoints[0], aLastPoints[3]), IGraphics::CLineItem(aPoints[2], aPoints[3], aLastPoints[2], aLastPoints[3])}; - Graphics()->LinesDraw(Array, 4); + Graphics()->LinesDraw(aArray, std::size(aArray)); } if(i == NumSteps) { - IGraphics::CLineItem Array[2] = { + IGraphics::CLineItem aArray[2] = { IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[0], aPoints[3]), IGraphics::CLineItem(aPoints[2], aPoints[1], aPoints[2], aPoints[3])}; - Graphics()->LinesDraw(Array, 2); + Graphics()->LinesDraw(aArray, std::size(aArray)); } mem_copy(aLastPoints, aPoints, sizeof(aPoints)); @@ -166,12 +166,12 @@ void CProofMode::RenderScreenSizes() r.w = aPoints[2] - aPoints[0]; r.h = aPoints[3] - aPoints[1]; - IGraphics::CLineItem Array[4] = { + IGraphics::CLineItem aArray[4] = { IGraphics::CLineItem(r.x, r.y, r.x + r.w, r.y), IGraphics::CLineItem(r.x + r.w, r.y, r.x + r.w, r.y + r.h), IGraphics::CLineItem(r.x + r.w, r.y + r.h, r.x, r.y + r.h), IGraphics::CLineItem(r.x, r.y + r.h, r.x, r.y)}; - Graphics()->LinesDraw(Array, 4); + Graphics()->LinesDraw(aArray, std::size(aArray)); Graphics()->SetColor(0, 1, 0, 1); } } @@ -188,16 +188,16 @@ void CProofMode::RenderScreenSizes() { Graphics()->SetColor(0, 1, 0, 0.3f); - std::set indices; + std::set Indices; for(int i = 0; i < (int)m_vMenuBackgroundPositions.size(); i++) - indices.insert(i); + Indices.insert(i); - while(!indices.empty()) + while(!Indices.empty()) { - int i = *indices.begin(); - indices.erase(i); + int i = *Indices.begin(); + Indices.erase(i); for(int k : m_vMenuBackgroundCollisions.at(i)) - indices.erase(k); + Indices.erase(k); vec2 Pos = m_vMenuBackgroundPositions[i]; Pos += WorldOffset - m_vMenuBackgroundPositions[m_CurrentMenuProofIndex]; From e19b1e4da62355b96a9669debbeced239ed1ac5b Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Sun, 27 Aug 2023 11:18:10 +0200 Subject: [PATCH 011/126] extract code from editor mapitems into separate files --- CMakeLists.txt | 19 +- src/game/editor/editor.cpp | 380 ------ src/game/editor/mapitems/image.cpp | 40 + src/game/editor/mapitems/layer_front.cpp | 47 + src/game/editor/{ => mapitems}/layer_game.cpp | 2 +- src/game/editor/mapitems/layer_group.cpp | 155 +++ .../editor/{ => mapitems}/layer_quads.cpp | 7 +- .../editor/{ => mapitems}/layer_sounds.cpp | 3 +- src/game/editor/mapitems/layer_speedup.cpp | 244 ++++ src/game/editor/mapitems/layer_switch.cpp | 270 +++++ src/game/editor/mapitems/layer_tele.cpp | 247 ++++ .../editor/{ => mapitems}/layer_tiles.cpp | 1035 +---------------- src/game/editor/mapitems/layer_tune.cpp | 218 ++++ src/game/editor/mapitems/map.cpp | 179 +++ src/game/editor/mapitems/sound.cpp | 10 + 15 files changed, 1429 insertions(+), 1427 deletions(-) create mode 100644 src/game/editor/mapitems/image.cpp create mode 100644 src/game/editor/mapitems/layer_front.cpp rename src/game/editor/{ => mapitems}/layer_game.cpp (98%) create mode 100644 src/game/editor/mapitems/layer_group.cpp rename src/game/editor/{ => mapitems}/layer_quads.cpp (98%) rename src/game/editor/{ => mapitems}/layer_sounds.cpp (99%) create mode 100644 src/game/editor/mapitems/layer_speedup.cpp create mode 100644 src/game/editor/mapitems/layer_switch.cpp create mode 100644 src/game/editor/mapitems/layer_tele.cpp rename src/game/editor/{ => mapitems}/layer_tiles.cpp (50%) create mode 100644 src/game/editor/mapitems/layer_tune.cpp create mode 100644 src/game/editor/mapitems/map.cpp create mode 100644 src/game/editor/mapitems/sound.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e18f2634..9af9c71af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2303,7 +2303,7 @@ if(CLIENT) ui_scrollregion.cpp ui_scrollregion.h ) - set_src(GAME_EDITOR GLOB src/game/editor + set_src(GAME_EDITOR GLOB_RECURSE src/game/editor auto_map.cpp auto_map.h component.cpp @@ -2312,14 +2312,23 @@ if(CLIENT) editor.h explanations.cpp io.cpp - layer_game.cpp - layer_quads.cpp - layer_sounds.cpp - layer_tiles.cpp map_grid.cpp map_grid.h map_view.cpp map_view.h + mapitems/image.cpp + mapitems/layer_front.cpp + mapitems/layer_game.cpp + mapitems/layer_group.cpp + mapitems/layer_quads.cpp + mapitems/layer_sounds.cpp + mapitems/layer_speedup.cpp + mapitems/layer_switch.cpp + mapitems/layer_tele.cpp + mapitems/layer_tiles.cpp + mapitems/layer_tune.cpp + mapitems/map.cpp + mapitems/sound.cpp popups.cpp proof_mode.cpp proof_mode.h diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 74a765d74..9b0b9421c 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -87,206 +87,6 @@ enum BUTTON_CONTEXT = 1, }; -CEditorImage::~CEditorImage() -{ - m_pEditor->Graphics()->UnloadTexture(&m_Texture); - free(m_pData); - m_pData = nullptr; -} - -CEditorSound::~CEditorSound() -{ - m_pEditor->Sound()->UnloadSample(m_SoundID); - free(m_pData); - m_pData = nullptr; -} - -CLayerGroup::CLayerGroup() -{ - m_vpLayers.clear(); - m_aName[0] = 0; - m_Visible = true; - m_Collapse = false; - m_GameGroup = false; - m_OffsetX = 0; - m_OffsetY = 0; - m_ParallaxX = 100; - m_ParallaxY = 100; - m_CustomParallaxZoom = 0; - m_ParallaxZoom = 100; - - m_UseClipping = 0; - m_ClipX = 0; - m_ClipY = 0; - m_ClipW = 0; - m_ClipH = 0; -} - -CLayerGroup::~CLayerGroup() -{ - m_vpLayers.clear(); -} - -void CLayerGroup::Convert(CUIRect *pRect) -{ - pRect->x += m_OffsetX; - pRect->y += m_OffsetY; -} - -void CLayerGroup::Mapping(float *pPoints) -{ - float ParallaxZoom = m_pMap->m_pEditor->m_PreviewZoom ? m_ParallaxZoom : 100.0f; - - m_pMap->m_pEditor->RenderTools()->MapScreenToWorld( - m_pMap->m_pEditor->MapView()->GetWorldOffset().x, m_pMap->m_pEditor->MapView()->GetWorldOffset().y, - m_ParallaxX, m_ParallaxY, ParallaxZoom, m_OffsetX, m_OffsetY, - m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->MapView()->GetWorldZoom(), pPoints); - - pPoints[0] += m_pMap->m_pEditor->MapView()->GetEditorOffset().x; - pPoints[1] += m_pMap->m_pEditor->MapView()->GetEditorOffset().y; - pPoints[2] += m_pMap->m_pEditor->MapView()->GetEditorOffset().x; - pPoints[3] += m_pMap->m_pEditor->MapView()->GetEditorOffset().y; -} - -void CLayerGroup::MapScreen() -{ - float aPoints[4]; - Mapping(aPoints); - m_pMap->m_pEditor->Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]); -} - -void CLayerGroup::Render() -{ - MapScreen(); - IGraphics *pGraphics = m_pMap->m_pEditor->Graphics(); - - if(m_UseClipping) - { - float aPoints[4]; - m_pMap->m_pGameGroup->Mapping(aPoints); - float x0 = (m_ClipX - aPoints[0]) / (aPoints[2] - aPoints[0]); - float y0 = (m_ClipY - aPoints[1]) / (aPoints[3] - aPoints[1]); - float x1 = ((m_ClipX + m_ClipW) - aPoints[0]) / (aPoints[2] - aPoints[0]); - float y1 = ((m_ClipY + m_ClipH) - aPoints[1]) / (aPoints[3] - aPoints[1]); - - pGraphics->ClipEnable((int)(x0 * pGraphics->ScreenWidth()), (int)(y0 * pGraphics->ScreenHeight()), - (int)((x1 - x0) * pGraphics->ScreenWidth()), (int)((y1 - y0) * pGraphics->ScreenHeight())); - } - - for(auto &pLayer : m_vpLayers) - { - if(pLayer->m_Visible) - { - if(pLayer->m_Type == LAYERTYPE_TILES) - { - std::shared_ptr pTiles = std::static_pointer_cast(pLayer); - if(pTiles->m_Game || pTiles->m_Front || pTiles->m_Tele || pTiles->m_Speedup || pTiles->m_Tune || pTiles->m_Switch) - continue; - } - if(m_pMap->m_pEditor->m_ShowDetail || !(pLayer->m_Flags & LAYERFLAG_DETAIL)) - pLayer->Render(); - } - } - - for(auto &pLayer : m_vpLayers) - { - if(pLayer->m_Visible && pLayer->m_Type == LAYERTYPE_TILES && pLayer != m_pMap->m_pGameLayer && pLayer != m_pMap->m_pFrontLayer && pLayer != m_pMap->m_pTeleLayer && pLayer != m_pMap->m_pSpeedupLayer && pLayer != m_pMap->m_pSwitchLayer && pLayer != m_pMap->m_pTuneLayer) - { - std::shared_ptr pTiles = std::static_pointer_cast(pLayer); - if(pTiles->m_Game || pTiles->m_Front || pTiles->m_Tele || pTiles->m_Speedup || pTiles->m_Tune || pTiles->m_Switch) - { - pLayer->Render(); - } - } - } - - if(m_UseClipping) - pGraphics->ClipDisable(); -} - -void CLayerGroup::AddLayer(const std::shared_ptr &pLayer) -{ - m_pMap->OnModify(); - m_vpLayers.push_back(pLayer); -} - -void CLayerGroup::DeleteLayer(int Index) -{ - if(Index < 0 || Index >= (int)m_vpLayers.size()) - return; - m_vpLayers.erase(m_vpLayers.begin() + Index); - m_pMap->OnModify(); -} - -void CLayerGroup::DuplicateLayer(int Index) -{ - if(Index < 0 || Index >= (int)m_vpLayers.size()) - return; - - std::shared_ptr pDup = m_vpLayers[Index]->Duplicate(); - m_vpLayers.insert(m_vpLayers.begin() + Index + 1, pDup); - - m_pMap->OnModify(); -} - -void CLayerGroup::GetSize(float *pWidth, float *pHeight) const -{ - *pWidth = 0; - *pHeight = 0; - for(const auto &pLayer : m_vpLayers) - { - float lw, lh; - pLayer->GetSize(&lw, &lh); - *pWidth = maximum(*pWidth, lw); - *pHeight = maximum(*pHeight, lh); - } -} - -int CLayerGroup::SwapLayers(int Index0, int Index1) -{ - if(Index0 < 0 || Index0 >= (int)m_vpLayers.size()) - return Index0; - if(Index1 < 0 || Index1 >= (int)m_vpLayers.size()) - return Index0; - if(Index0 == Index1) - return Index0; - m_pMap->OnModify(); - std::swap(m_vpLayers[Index0], m_vpLayers[Index1]); - return Index1; -} - -void CEditorImage::AnalyseTileFlags() -{ - mem_zero(m_aTileFlags, sizeof(m_aTileFlags)); - - int tw = m_Width / 16; // tilesizes - int th = m_Height / 16; - if(tw == th && m_Format == CImageInfo::FORMAT_RGBA) - { - unsigned char *pPixelData = (unsigned char *)m_pData; - - int TileID = 0; - for(int ty = 0; ty < 16; ty++) - for(int tx = 0; tx < 16; tx++, TileID++) - { - bool Opaque = true; - for(int x = 0; x < tw; x++) - for(int y = 0; y < th; y++) - { - int p = (ty * tw + y) * m_Width + tx * tw + x; - if(pPixelData[p * 4 + 3] < 250) - { - Opaque = false; - break; - } - } - - if(Opaque) - m_aTileFlags[TileID] |= TILEFLAG_OPAQUE; - } - } -} - void CEditor::EnvelopeEval(int TimeOffsetMillis, int Env, ColorRGBA &Channels, void *pUser) { CEditor *pThis = (CEditor *)pUser; @@ -7625,154 +7425,6 @@ void CEditor::Reset(bool CreateDefault) m_SettingsCommandInput.Clear(); } -void CEditorMap::OnModify() -{ - m_Modified = true; - m_ModifiedAuto = true; - m_LastModifiedTime = m_pEditor->Client()->GlobalTime(); -} - -void CEditorMap::DeleteEnvelope(int Index) -{ - if(Index < 0 || Index >= (int)m_vpEnvelopes.size()) - return; - - OnModify(); - - VisitEnvelopeReferences([Index](int &ElementIndex) { - if(ElementIndex == Index) - ElementIndex = -1; - else if(ElementIndex > Index) - ElementIndex--; - }); - - m_vpEnvelopes.erase(m_vpEnvelopes.begin() + Index); -} - -void CEditorMap::SwapEnvelopes(int Index0, int Index1) -{ - if(Index0 < 0 || Index0 >= (int)m_vpEnvelopes.size()) - return; - if(Index1 < 0 || Index1 >= (int)m_vpEnvelopes.size()) - return; - if(Index0 == Index1) - return; - - OnModify(); - - VisitEnvelopeReferences([Index0, Index1](int &ElementIndex) { - if(ElementIndex == Index0) - ElementIndex = Index1; - else if(ElementIndex == Index1) - ElementIndex = Index0; - }); - - std::swap(m_vpEnvelopes[Index0], m_vpEnvelopes[Index1]); -} - -template -void CEditorMap::VisitEnvelopeReferences(F &&Visitor) -{ - for(auto &pGroup : m_vpGroups) - { - for(auto &pLayer : pGroup->m_vpLayers) - { - if(pLayer->m_Type == LAYERTYPE_QUADS) - { - std::shared_ptr pLayerQuads = std::static_pointer_cast(pLayer); - for(auto &Quad : pLayerQuads->m_vQuads) - { - Visitor(Quad.m_PosEnv); - Visitor(Quad.m_ColorEnv); - } - } - else if(pLayer->m_Type == LAYERTYPE_TILES) - { - std::shared_ptr pLayerTiles = std::static_pointer_cast(pLayer); - Visitor(pLayerTiles->m_ColorEnv); - } - else if(pLayer->m_Type == LAYERTYPE_SOUNDS) - { - std::shared_ptr pLayerSounds = std::static_pointer_cast(pLayer); - for(auto &Source : pLayerSounds->m_vSources) - { - Visitor(Source.m_PosEnv); - Visitor(Source.m_SoundEnv); - } - } - } - } -} - -void CEditorMap::MakeGameLayer(const std::shared_ptr &pLayer) -{ - m_pGameLayer = std::static_pointer_cast(pLayer); - m_pGameLayer->m_pEditor = m_pEditor; -} - -void CEditorMap::MakeGameGroup(std::shared_ptr pGroup) -{ - m_pGameGroup = std::move(pGroup); - m_pGameGroup->m_GameGroup = true; - str_copy(m_pGameGroup->m_aName, "Game"); -} - -void CEditorMap::Clean() -{ - m_vpGroups.clear(); - m_vpEnvelopes.clear(); - m_vpImages.clear(); - m_vpSounds.clear(); - - m_MapInfo.Reset(); - m_MapInfoTmp.Reset(); - - m_vSettings.clear(); - - m_pGameLayer = nullptr; - m_pGameGroup = nullptr; - - m_Modified = false; - m_ModifiedAuto = false; - - m_pTeleLayer = nullptr; - m_pSpeedupLayer = nullptr; - m_pFrontLayer = nullptr; - m_pSwitchLayer = nullptr; - m_pTuneLayer = nullptr; -} - -void CEditorMap::CreateDefault(IGraphics::CTextureHandle EntitiesTexture) -{ - // add background - std::shared_ptr pGroup = NewGroup(); - pGroup->m_ParallaxX = 0; - pGroup->m_ParallaxY = 0; - pGroup->m_CustomParallaxZoom = 0; - pGroup->m_ParallaxZoom = 0; - std::shared_ptr pLayer = std::make_shared(); - pLayer->m_pEditor = m_pEditor; - CQuad *pQuad = pLayer->NewQuad(0, 0, 1600, 1200); - pQuad->m_aColors[0].r = pQuad->m_aColors[1].r = 94; - pQuad->m_aColors[0].g = pQuad->m_aColors[1].g = 132; - pQuad->m_aColors[0].b = pQuad->m_aColors[1].b = 174; - pQuad->m_aColors[2].r = pQuad->m_aColors[3].r = 204; - pQuad->m_aColors[2].g = pQuad->m_aColors[3].g = 232; - pQuad->m_aColors[2].b = pQuad->m_aColors[3].b = 255; - pGroup->AddLayer(pLayer); - - // add game layer and reset front, tele, speedup, tune and switch layer pointers - MakeGameGroup(NewGroup()); - MakeGameLayer(std::make_shared(50, 50)); - m_pGameGroup->AddLayer(m_pGameLayer); - - m_pFrontLayer = nullptr; - m_pTeleLayer = nullptr; - m_pSpeedupLayer = nullptr; - m_pSwitchLayer = nullptr; - m_pTuneLayer = nullptr; -} - int CEditor::GetTextureUsageFlag() { return Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; @@ -8173,35 +7825,3 @@ void CEditor::LoadCurrentMap() } IEditor *CreateEditor() { return new CEditor; } - -// DDRace - -void CEditorMap::MakeTeleLayer(const std::shared_ptr &pLayer) -{ - m_pTeleLayer = std::static_pointer_cast(pLayer); - m_pTeleLayer->m_pEditor = m_pEditor; -} - -void CEditorMap::MakeSpeedupLayer(const std::shared_ptr &pLayer) -{ - m_pSpeedupLayer = std::static_pointer_cast(pLayer); - m_pSpeedupLayer->m_pEditor = m_pEditor; -} - -void CEditorMap::MakeFrontLayer(const std::shared_ptr &pLayer) -{ - m_pFrontLayer = std::static_pointer_cast(pLayer); - m_pFrontLayer->m_pEditor = m_pEditor; -} - -void CEditorMap::MakeSwitchLayer(const std::shared_ptr &pLayer) -{ - m_pSwitchLayer = std::static_pointer_cast(pLayer); - m_pSwitchLayer->m_pEditor = m_pEditor; -} - -void CEditorMap::MakeTuneLayer(const std::shared_ptr &pLayer) -{ - m_pTuneLayer = std::static_pointer_cast(pLayer); - m_pTuneLayer->m_pEditor = m_pEditor; -} diff --git a/src/game/editor/mapitems/image.cpp b/src/game/editor/mapitems/image.cpp new file mode 100644 index 000000000..a29e5371d --- /dev/null +++ b/src/game/editor/mapitems/image.cpp @@ -0,0 +1,40 @@ +#include + +CEditorImage::~CEditorImage() +{ + m_pEditor->Graphics()->UnloadTexture(&m_Texture); + free(m_pData); + m_pData = nullptr; +} + +void CEditorImage::AnalyseTileFlags() +{ + mem_zero(m_aTileFlags, sizeof(m_aTileFlags)); + + int tw = m_Width / 16; // tilesizes + int th = m_Height / 16; + if(tw == th && m_Format == CImageInfo::FORMAT_RGBA) + { + unsigned char *pPixelData = (unsigned char *)m_pData; + + int TileID = 0; + for(int ty = 0; ty < 16; ty++) + for(int tx = 0; tx < 16; tx++, TileID++) + { + bool Opaque = true; + for(int x = 0; x < tw; x++) + for(int y = 0; y < th; y++) + { + int p = (ty * tw + y) * m_Width + tx * tw + x; + if(pPixelData[p * 4 + 3] < 250) + { + Opaque = false; + break; + } + } + + if(Opaque) + m_aTileFlags[TileID] |= TILEFLAG_OPAQUE; + } + } +} diff --git a/src/game/editor/mapitems/layer_front.cpp b/src/game/editor/mapitems/layer_front.cpp new file mode 100644 index 000000000..01ae277b8 --- /dev/null +++ b/src/game/editor/mapitems/layer_front.cpp @@ -0,0 +1,47 @@ +#include + +CLayerFront::CLayerFront(int w, int h) : + CLayerTiles(w, h) +{ + str_copy(m_aName, "Front"); + m_Front = 1; +} + +void CLayerFront::SetTile(int x, int y, CTile Tile) +{ + if(Tile.m_Index == TILE_THROUGH_CUT) + { + CTile nohook = {TILE_NOHOOK}; + m_pEditor->m_Map.m_pGameLayer->CLayerTiles::SetTile(x, y, nohook); // NOLINT(bugprone-parent-virtual-call) + } + else if(Tile.m_Index == TILE_AIR && CLayerTiles::GetTile(x, y).m_Index == TILE_THROUGH_CUT) + { + CTile air = {TILE_AIR}; + m_pEditor->m_Map.m_pGameLayer->CLayerTiles::SetTile(x, y, air); // NOLINT(bugprone-parent-virtual-call) + } + if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidFrontTile(Tile.m_Index)) + { + CLayerTiles::SetTile(x, y, Tile); + } + else + { + CTile air = {TILE_AIR}; + CLayerTiles::SetTile(x, y, air); + if(!m_pEditor->m_PreventUnusedTilesWasWarned) + { + m_pEditor->m_PopupEventType = CEditor::POPEVENT_PREVENTUNUSEDTILES; + m_pEditor->m_PopupEventActivated = true; + m_pEditor->m_PreventUnusedTilesWasWarned = true; + } + } +} + +void CLayerFront::Resize(int NewW, int NewH) +{ + // resize tile data + CLayerTiles::Resize(NewW, NewH); + + // resize gamelayer too + if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) + m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); +} diff --git a/src/game/editor/layer_game.cpp b/src/game/editor/mapitems/layer_game.cpp similarity index 98% rename from src/game/editor/layer_game.cpp rename to src/game/editor/mapitems/layer_game.cpp index a77afc58e..fd9f82e47 100644 --- a/src/game/editor/layer_game.cpp +++ b/src/game/editor/mapitems/layer_game.cpp @@ -1,6 +1,6 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ -#include "editor.h" +#include CLayerGame::CLayerGame(int w, int h) : CLayerTiles(w, h) diff --git a/src/game/editor/mapitems/layer_group.cpp b/src/game/editor/mapitems/layer_group.cpp new file mode 100644 index 000000000..2f01d77eb --- /dev/null +++ b/src/game/editor/mapitems/layer_group.cpp @@ -0,0 +1,155 @@ +#include + +CLayerGroup::CLayerGroup() +{ + m_vpLayers.clear(); + m_aName[0] = 0; + m_Visible = true; + m_Collapse = false; + m_GameGroup = false; + m_OffsetX = 0; + m_OffsetY = 0; + m_ParallaxX = 100; + m_ParallaxY = 100; + m_CustomParallaxZoom = 0; + m_ParallaxZoom = 100; + + m_UseClipping = 0; + m_ClipX = 0; + m_ClipY = 0; + m_ClipW = 0; + m_ClipH = 0; +} + +CLayerGroup::~CLayerGroup() +{ + m_vpLayers.clear(); +} + +void CLayerGroup::Convert(CUIRect *pRect) +{ + pRect->x += m_OffsetX; + pRect->y += m_OffsetY; +} + +void CLayerGroup::Mapping(float *pPoints) +{ + float ParallaxZoom = m_pMap->m_pEditor->m_PreviewZoom ? m_ParallaxZoom : 100.0f; + + m_pMap->m_pEditor->RenderTools()->MapScreenToWorld( + m_pMap->m_pEditor->MapView()->GetWorldOffset().x, m_pMap->m_pEditor->MapView()->GetWorldOffset().y, + m_ParallaxX, m_ParallaxY, ParallaxZoom, m_OffsetX, m_OffsetY, + m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->MapView()->GetWorldZoom(), pPoints); + + pPoints[0] += m_pMap->m_pEditor->MapView()->GetEditorOffset().x; + pPoints[1] += m_pMap->m_pEditor->MapView()->GetEditorOffset().y; + pPoints[2] += m_pMap->m_pEditor->MapView()->GetEditorOffset().x; + pPoints[3] += m_pMap->m_pEditor->MapView()->GetEditorOffset().y; +} + +void CLayerGroup::MapScreen() +{ + float aPoints[4]; + Mapping(aPoints); + m_pMap->m_pEditor->Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]); +} + +void CLayerGroup::Render() +{ + MapScreen(); + IGraphics *pGraphics = m_pMap->m_pEditor->Graphics(); + + if(m_UseClipping) + { + float aPoints[4]; + m_pMap->m_pGameGroup->Mapping(aPoints); + float x0 = (m_ClipX - aPoints[0]) / (aPoints[2] - aPoints[0]); + float y0 = (m_ClipY - aPoints[1]) / (aPoints[3] - aPoints[1]); + float x1 = ((m_ClipX + m_ClipW) - aPoints[0]) / (aPoints[2] - aPoints[0]); + float y1 = ((m_ClipY + m_ClipH) - aPoints[1]) / (aPoints[3] - aPoints[1]); + + pGraphics->ClipEnable((int)(x0 * pGraphics->ScreenWidth()), (int)(y0 * pGraphics->ScreenHeight()), + (int)((x1 - x0) * pGraphics->ScreenWidth()), (int)((y1 - y0) * pGraphics->ScreenHeight())); + } + + for(auto &pLayer : m_vpLayers) + { + if(pLayer->m_Visible) + { + if(pLayer->m_Type == LAYERTYPE_TILES) + { + std::shared_ptr pTiles = std::static_pointer_cast(pLayer); + if(pTiles->m_Game || pTiles->m_Front || pTiles->m_Tele || pTiles->m_Speedup || pTiles->m_Tune || pTiles->m_Switch) + continue; + } + if(m_pMap->m_pEditor->m_ShowDetail || !(pLayer->m_Flags & LAYERFLAG_DETAIL)) + pLayer->Render(); + } + } + + for(auto &pLayer : m_vpLayers) + { + if(pLayer->m_Visible && pLayer->m_Type == LAYERTYPE_TILES && pLayer != m_pMap->m_pGameLayer && pLayer != m_pMap->m_pFrontLayer && pLayer != m_pMap->m_pTeleLayer && pLayer != m_pMap->m_pSpeedupLayer && pLayer != m_pMap->m_pSwitchLayer && pLayer != m_pMap->m_pTuneLayer) + { + std::shared_ptr pTiles = std::static_pointer_cast(pLayer); + if(pTiles->m_Game || pTiles->m_Front || pTiles->m_Tele || pTiles->m_Speedup || pTiles->m_Tune || pTiles->m_Switch) + { + pLayer->Render(); + } + } + } + + if(m_UseClipping) + pGraphics->ClipDisable(); +} + +void CLayerGroup::AddLayer(const std::shared_ptr &pLayer) +{ + m_pMap->OnModify(); + m_vpLayers.push_back(pLayer); +} + +void CLayerGroup::DeleteLayer(int Index) +{ + if(Index < 0 || Index >= (int)m_vpLayers.size()) + return; + m_vpLayers.erase(m_vpLayers.begin() + Index); + m_pMap->OnModify(); +} + +void CLayerGroup::DuplicateLayer(int Index) +{ + if(Index < 0 || Index >= (int)m_vpLayers.size()) + return; + + std::shared_ptr pDup = m_vpLayers[Index]->Duplicate(); + m_vpLayers.insert(m_vpLayers.begin() + Index + 1, pDup); + + m_pMap->OnModify(); +} + +void CLayerGroup::GetSize(float *pWidth, float *pHeight) const +{ + *pWidth = 0; + *pHeight = 0; + for(const auto &pLayer : m_vpLayers) + { + float lw, lh; + pLayer->GetSize(&lw, &lh); + *pWidth = maximum(*pWidth, lw); + *pHeight = maximum(*pHeight, lh); + } +} + +int CLayerGroup::SwapLayers(int Index0, int Index1) +{ + if(Index0 < 0 || Index0 >= (int)m_vpLayers.size()) + return Index0; + if(Index1 < 0 || Index1 >= (int)m_vpLayers.size()) + return Index0; + if(Index0 == Index1) + return Index0; + m_pMap->OnModify(); + std::swap(m_vpLayers[Index0], m_vpLayers[Index1]); + return Index1; +} diff --git a/src/game/editor/layer_quads.cpp b/src/game/editor/mapitems/layer_quads.cpp similarity index 98% rename from src/game/editor/layer_quads.cpp rename to src/game/editor/mapitems/layer_quads.cpp index 0a362b0fa..485ba9327 100644 --- a/src/game/editor/layer_quads.cpp +++ b/src/game/editor/mapitems/layer_quads.cpp @@ -1,11 +1,6 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ -#include - -#include - -#include "editor.h" -#include +#include CLayerQuads::CLayerQuads() { diff --git a/src/game/editor/layer_sounds.cpp b/src/game/editor/mapitems/layer_sounds.cpp similarity index 99% rename from src/game/editor/layer_sounds.cpp rename to src/game/editor/mapitems/layer_sounds.cpp index 57e5dd40e..9a8d8547c 100644 --- a/src/game/editor/layer_sounds.cpp +++ b/src/game/editor/mapitems/layer_sounds.cpp @@ -1,8 +1,7 @@ +#include #include -#include "editor.h" - static const float s_SourceVisualSize = 32.0f; CLayerSounds::CLayerSounds() diff --git a/src/game/editor/mapitems/layer_speedup.cpp b/src/game/editor/mapitems/layer_speedup.cpp new file mode 100644 index 000000000..0c2e2e715 --- /dev/null +++ b/src/game/editor/mapitems/layer_speedup.cpp @@ -0,0 +1,244 @@ +#include + +CLayerSpeedup::CLayerSpeedup(int w, int h) : + CLayerTiles(w, h) +{ + str_copy(m_aName, "Speedup"); + m_Speedup = 1; + + m_pSpeedupTile = new CSpeedupTile[w * h]; + mem_zero(m_pSpeedupTile, (size_t)w * h * sizeof(CSpeedupTile)); +} + +CLayerSpeedup::~CLayerSpeedup() +{ + delete[] m_pSpeedupTile; +} + +void CLayerSpeedup::Resize(int NewW, int NewH) +{ + // resize speedup data + CSpeedupTile *pNewSpeedupData = new CSpeedupTile[NewW * NewH]; + mem_zero(pNewSpeedupData, (size_t)NewW * NewH * sizeof(CSpeedupTile)); + + // copy old data + for(int y = 0; y < minimum(NewH, m_Height); y++) + mem_copy(&pNewSpeedupData[y * NewW], &m_pSpeedupTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CSpeedupTile)); + + // replace old + delete[] m_pSpeedupTile; + m_pSpeedupTile = pNewSpeedupData; + + // resize tile data + CLayerTiles::Resize(NewW, NewH); + + // resize gamelayer too + if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) + m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); +} + +void CLayerSpeedup::Shift(int Direction) +{ + CLayerTiles::Shift(Direction); + ShiftImpl(m_pSpeedupTile, Direction, m_pEditor->m_ShiftBy); +} + +bool CLayerSpeedup::IsEmpty(const std::shared_ptr &pLayer) +{ + for(int y = 0; y < pLayer->m_Height; y++) + for(int x = 0; x < pLayer->m_Width; x++) + if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidSpeedupTile(pLayer->GetTile(x, y).m_Index)) + return false; + + return true; +} + +void CLayerSpeedup::BrushDraw(std::shared_ptr pBrush, float wx, float wy) +{ + if(m_Readonly) + return; + + std::shared_ptr pSpeedupLayer = std::static_pointer_cast(pBrush); + int sx = ConvertX(wx); + int sy = ConvertY(wy); + if(str_comp(pSpeedupLayer->m_aFileName, m_pEditor->m_aFileName)) + { + m_pEditor->m_SpeedupAngle = pSpeedupLayer->m_SpeedupAngle; + m_pEditor->m_SpeedupForce = pSpeedupLayer->m_SpeedupForce; + m_pEditor->m_SpeedupMaxSpeed = pSpeedupLayer->m_SpeedupMaxSpeed; + } + + bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pSpeedupLayer); + + for(int y = 0; y < pSpeedupLayer->m_Height; y++) + for(int x = 0; x < pSpeedupLayer->m_Width; x++) + { + int fx = x + sx; + int fy = y + sy; + + if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(!Destructive && GetTile(fx, fy).m_Index) + continue; + + if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidSpeedupTile(pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index)) && pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index != TILE_AIR) + { + if(m_pEditor->m_SpeedupAngle != pSpeedupLayer->m_SpeedupAngle || m_pEditor->m_SpeedupForce != pSpeedupLayer->m_SpeedupForce || m_pEditor->m_SpeedupMaxSpeed != pSpeedupLayer->m_SpeedupMaxSpeed) + { + m_pSpeedupTile[fy * m_Width + fx].m_Force = m_pEditor->m_SpeedupForce; + m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed; + m_pSpeedupTile[fy * m_Width + fx].m_Angle = m_pEditor->m_SpeedupAngle; + m_pSpeedupTile[fy * m_Width + fx].m_Type = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; + m_pTiles[fy * m_Width + fx].m_Index = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; + } + else if(pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_Force) + { + m_pSpeedupTile[fy * m_Width + fx].m_Force = pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_Force; + m_pSpeedupTile[fy * m_Width + fx].m_Angle = pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_Angle; + m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_MaxSpeed; + m_pSpeedupTile[fy * m_Width + fx].m_Type = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; + m_pTiles[fy * m_Width + fx].m_Index = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; + } + else if(m_pEditor->m_SpeedupForce) + { + m_pSpeedupTile[fy * m_Width + fx].m_Force = m_pEditor->m_SpeedupForce; + m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed; + m_pSpeedupTile[fy * m_Width + fx].m_Angle = m_pEditor->m_SpeedupAngle; + m_pSpeedupTile[fy * m_Width + fx].m_Type = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; + m_pTiles[fy * m_Width + fx].m_Index = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; + } + else + { + m_pSpeedupTile[fy * m_Width + fx].m_Force = 0; + m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = 0; + m_pSpeedupTile[fy * m_Width + fx].m_Angle = 0; + m_pSpeedupTile[fy * m_Width + fx].m_Type = 0; + m_pTiles[fy * m_Width + fx].m_Index = 0; + } + } + else + { + m_pSpeedupTile[fy * m_Width + fx].m_Force = 0; + m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = 0; + m_pSpeedupTile[fy * m_Width + fx].m_Angle = 0; + m_pSpeedupTile[fy * m_Width + fx].m_Type = 0; + m_pTiles[fy * m_Width + fx].m_Index = 0; + } + } + FlagModified(sx, sy, pSpeedupLayer->m_Width, pSpeedupLayer->m_Height); +} + +void CLayerSpeedup::BrushFlipX() +{ + CLayerTiles::BrushFlipX(); + BrushFlipXImpl(m_pSpeedupTile); +} + +void CLayerSpeedup::BrushFlipY() +{ + CLayerTiles::BrushFlipY(); + BrushFlipYImpl(m_pSpeedupTile); +} + +void CLayerSpeedup::BrushRotate(float Amount) +{ + int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270° + if(Rotation < 0) + Rotation += 4; + + if(Rotation == 1 || Rotation == 3) + { + // 90° rotation + CSpeedupTile *pTempData1 = new CSpeedupTile[m_Width * m_Height]; + CTile *pTempData2 = new CTile[m_Width * m_Height]; + mem_copy(pTempData1, m_pSpeedupTile, (size_t)m_Width * m_Height * sizeof(CSpeedupTile)); + mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile)); + CSpeedupTile *pDst1 = m_pSpeedupTile; + CTile *pDst2 = m_pTiles; + for(int x = 0; x < m_Width; ++x) + for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2) + { + *pDst1 = pTempData1[y * m_Width + x]; + *pDst2 = pTempData2[y * m_Width + x]; + } + + std::swap(m_Width, m_Height); + delete[] pTempData1; + delete[] pTempData2; + } + + if(Rotation == 2 || Rotation == 3) + { + BrushFlipX(); + BrushFlipY(); + } +} + +void CLayerSpeedup::FillSelection(bool Empty, std::shared_ptr pBrush, CUIRect Rect) +{ + if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) + return; + + Snap(&Rect); // corrects Rect; no need of <= + + Snap(&Rect); + + int sx = ConvertX(Rect.x); + int sy = ConvertY(Rect.y); + int w = ConvertX(Rect.w); + int h = ConvertY(Rect.h); + + std::shared_ptr pLt = std::static_pointer_cast(pBrush); + + bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); + + for(int y = 0; y < h; y++) + { + for(int x = 0; x < w; x++) + { + int fx = x + sx; + int fy = y + sy; + + if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(!Destructive && GetTile(fx, fy).m_Index) + continue; + + const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height); + const int TgtIndex = fy * m_Width + fx; + + if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidSpeedupTile((pLt->m_pTiles[SrcIndex]).m_Index))) // no speed up tile chosen: reset + { + m_pTiles[TgtIndex].m_Index = 0; + m_pSpeedupTile[TgtIndex].m_Force = 0; + m_pSpeedupTile[TgtIndex].m_Angle = 0; + } + else + { + m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex]; + if(pLt->m_Speedup && m_pTiles[TgtIndex].m_Index > 0) + { + m_pSpeedupTile[TgtIndex].m_Type = m_pTiles[TgtIndex].m_Index; + + if((pLt->m_pSpeedupTile[SrcIndex].m_Force == 0 && m_pEditor->m_SpeedupForce) || m_pEditor->m_SpeedupForce != pLt->m_SpeedupForce) + m_pSpeedupTile[TgtIndex].m_Force = m_pEditor->m_SpeedupForce; + else + m_pSpeedupTile[TgtIndex].m_Force = pLt->m_pSpeedupTile[SrcIndex].m_Force; + + if((pLt->m_pSpeedupTile[SrcIndex].m_Angle == 0 && m_pEditor->m_SpeedupAngle) || m_pEditor->m_SpeedupAngle != pLt->m_SpeedupAngle) + m_pSpeedupTile[TgtIndex].m_Angle = m_pEditor->m_SpeedupAngle; + else + m_pSpeedupTile[TgtIndex].m_Angle = pLt->m_pSpeedupTile[SrcIndex].m_Angle; + + if((pLt->m_pSpeedupTile[SrcIndex].m_MaxSpeed == 0 && m_pEditor->m_SpeedupMaxSpeed) || m_pEditor->m_SpeedupMaxSpeed != pLt->m_SpeedupMaxSpeed) + m_pSpeedupTile[TgtIndex].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed; + else + m_pSpeedupTile[TgtIndex].m_MaxSpeed = pLt->m_pSpeedupTile[SrcIndex].m_MaxSpeed; + } + } + } + } + FlagModified(sx, sy, w, h); +} diff --git a/src/game/editor/mapitems/layer_switch.cpp b/src/game/editor/mapitems/layer_switch.cpp new file mode 100644 index 000000000..1d0d978a4 --- /dev/null +++ b/src/game/editor/mapitems/layer_switch.cpp @@ -0,0 +1,270 @@ +#include + +CLayerSwitch::CLayerSwitch(int w, int h) : + CLayerTiles(w, h) +{ + str_copy(m_aName, "Switch"); + m_Switch = 1; + + m_pSwitchTile = new CSwitchTile[w * h]; + mem_zero(m_pSwitchTile, (size_t)w * h * sizeof(CSwitchTile)); +} + +CLayerSwitch::~CLayerSwitch() +{ + delete[] m_pSwitchTile; +} + +void CLayerSwitch::Resize(int NewW, int NewH) +{ + // resize switch data + CSwitchTile *pNewSwitchData = new CSwitchTile[NewW * NewH]; + mem_zero(pNewSwitchData, (size_t)NewW * NewH * sizeof(CSwitchTile)); + + // copy old data + for(int y = 0; y < minimum(NewH, m_Height); y++) + mem_copy(&pNewSwitchData[y * NewW], &m_pSwitchTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CSwitchTile)); + + // replace old + delete[] m_pSwitchTile; + m_pSwitchTile = pNewSwitchData; + + // resize tile data + CLayerTiles::Resize(NewW, NewH); + + // resize gamelayer too + if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) + m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); +} + +void CLayerSwitch::Shift(int Direction) +{ + CLayerTiles::Shift(Direction); + ShiftImpl(m_pSwitchTile, Direction, m_pEditor->m_ShiftBy); +} + +bool CLayerSwitch::IsEmpty(const std::shared_ptr &pLayer) +{ + for(int y = 0; y < pLayer->m_Height; y++) + for(int x = 0; x < pLayer->m_Width; x++) + if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidSwitchTile(pLayer->GetTile(x, y).m_Index)) + return false; + + return true; +} + +void CLayerSwitch::BrushDraw(std::shared_ptr pBrush, float wx, float wy) +{ + if(m_Readonly) + return; + + std::shared_ptr pSwitchLayer = std::static_pointer_cast(pBrush); + int sx = ConvertX(wx); + int sy = ConvertY(wy); + if(str_comp(pSwitchLayer->m_aFileName, m_pEditor->m_aFileName)) + { + m_pEditor->m_SwitchNum = pSwitchLayer->m_SwitchNumber; + m_pEditor->m_SwitchDelay = pSwitchLayer->m_SwitchDelay; + } + + bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pSwitchLayer); + + for(int y = 0; y < pSwitchLayer->m_Height; y++) + for(int x = 0; x < pSwitchLayer->m_Width; x++) + { + int fx = x + sx; + int fy = y + sy; + + if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(!Destructive && GetTile(fx, fy).m_Index) + continue; + + if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidSwitchTile(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index)) && pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index != TILE_AIR) + { + if(m_pEditor->m_SwitchNum != pSwitchLayer->m_SwitchNumber || m_pEditor->m_SwitchDelay != pSwitchLayer->m_SwitchDelay) + { + m_pSwitchTile[fy * m_Width + fx].m_Number = m_pEditor->m_SwitchNum; + m_pSwitchTile[fy * m_Width + fx].m_Delay = m_pEditor->m_SwitchDelay; + } + else if(pSwitchLayer->m_pSwitchTile[y * pSwitchLayer->m_Width + x].m_Number) + { + m_pSwitchTile[fy * m_Width + fx].m_Number = pSwitchLayer->m_pSwitchTile[y * pSwitchLayer->m_Width + x].m_Number; + m_pSwitchTile[fy * m_Width + fx].m_Delay = pSwitchLayer->m_pSwitchTile[y * pSwitchLayer->m_Width + x].m_Delay; + } + else + { + m_pSwitchTile[fy * m_Width + fx].m_Number = m_pEditor->m_SwitchNum; + m_pSwitchTile[fy * m_Width + fx].m_Delay = m_pEditor->m_SwitchDelay; + } + + m_pSwitchTile[fy * m_Width + fx].m_Type = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index; + m_pSwitchTile[fy * m_Width + fx].m_Flags = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Flags; + m_pTiles[fy * m_Width + fx].m_Index = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index; + m_pTiles[fy * m_Width + fx].m_Flags = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Flags; + + if(!IsSwitchTileFlagsUsed(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index)) + { + m_pSwitchTile[fy * m_Width + fx].m_Flags = 0; + } + if(!IsSwitchTileNumberUsed(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index)) + { + m_pSwitchTile[fy * m_Width + fx].m_Number = 0; + } + if(!IsSwitchTileDelayUsed(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index)) + { + m_pSwitchTile[fy * m_Width + fx].m_Delay = 0; + } + } + else + { + m_pSwitchTile[fy * m_Width + fx].m_Number = 0; + m_pSwitchTile[fy * m_Width + fx].m_Type = 0; + m_pSwitchTile[fy * m_Width + fx].m_Flags = 0; + m_pSwitchTile[fy * m_Width + fx].m_Delay = 0; + m_pTiles[fy * m_Width + fx].m_Index = 0; + } + } + FlagModified(sx, sy, pSwitchLayer->m_Width, pSwitchLayer->m_Height); +} + +void CLayerSwitch::BrushFlipX() +{ + CLayerTiles::BrushFlipX(); + BrushFlipXImpl(m_pSwitchTile); +} + +void CLayerSwitch::BrushFlipY() +{ + CLayerTiles::BrushFlipY(); + BrushFlipYImpl(m_pSwitchTile); +} + +void CLayerSwitch::BrushRotate(float Amount) +{ + int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270° + if(Rotation < 0) + Rotation += 4; + + if(Rotation == 1 || Rotation == 3) + { + // 90° rotation + CSwitchTile *pTempData1 = new CSwitchTile[m_Width * m_Height]; + CTile *pTempData2 = new CTile[m_Width * m_Height]; + mem_copy(pTempData1, m_pSwitchTile, (size_t)m_Width * m_Height * sizeof(CSwitchTile)); + mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile)); + CSwitchTile *pDst1 = m_pSwitchTile; + CTile *pDst2 = m_pTiles; + for(int x = 0; x < m_Width; ++x) + for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2) + { + *pDst1 = pTempData1[y * m_Width + x]; + *pDst2 = pTempData2[y * m_Width + x]; + if(IsRotatableTile(pDst2->m_Index)) + { + if(pDst2->m_Flags & TILEFLAG_ROTATE) + pDst2->m_Flags ^= (TILEFLAG_YFLIP | TILEFLAG_XFLIP); + pDst2->m_Flags ^= TILEFLAG_ROTATE; + } + } + + std::swap(m_Width, m_Height); + delete[] pTempData1; + delete[] pTempData2; + } + + if(Rotation == 2 || Rotation == 3) + { + BrushFlipX(); + BrushFlipY(); + } +} + +void CLayerSwitch::FillSelection(bool Empty, std::shared_ptr pBrush, CUIRect Rect) +{ + if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) + return; + + Snap(&Rect); // corrects Rect; no need of <= + + Snap(&Rect); + + int sx = ConvertX(Rect.x); + int sy = ConvertY(Rect.y); + int w = ConvertX(Rect.w); + int h = ConvertY(Rect.h); + + std::shared_ptr pLt = std::static_pointer_cast(pBrush); + + bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); + + for(int y = 0; y < h; y++) + { + for(int x = 0; x < w; x++) + { + int fx = x + sx; + int fy = y + sy; + + if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(!Destructive && GetTile(fx, fy).m_Index) + continue; + + const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height); + const int TgtIndex = fy * m_Width + fx; + + if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidSwitchTile((pLt->m_pTiles[SrcIndex]).m_Index))) + { + m_pTiles[TgtIndex].m_Index = 0; + m_pSwitchTile[TgtIndex].m_Type = 0; + m_pSwitchTile[TgtIndex].m_Number = 0; + m_pSwitchTile[TgtIndex].m_Delay = 0; + } + else + { + m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex]; + m_pSwitchTile[TgtIndex].m_Type = m_pTiles[TgtIndex].m_Index; + if(pLt->m_Switch && m_pTiles[TgtIndex].m_Index > 0) + { + if(!IsSwitchTileNumberUsed(m_pSwitchTile[TgtIndex].m_Type)) + m_pSwitchTile[TgtIndex].m_Number = 0; + else if(pLt->m_pSwitchTile[SrcIndex].m_Number == 0 || m_pEditor->m_SwitchNum != pLt->m_SwitchNumber) + m_pSwitchTile[TgtIndex].m_Number = m_pEditor->m_SwitchNum; + else + m_pSwitchTile[TgtIndex].m_Number = pLt->m_pSwitchTile[SrcIndex].m_Number; + + if(!IsSwitchTileDelayUsed(m_pSwitchTile[TgtIndex].m_Type)) + m_pSwitchTile[TgtIndex].m_Delay = 0; + else if(pLt->m_pSwitchTile[SrcIndex].m_Delay == 0 || m_pEditor->m_SwitchDelay != pLt->m_SwitchDelay) + m_pSwitchTile[TgtIndex].m_Delay = m_pEditor->m_SwitchDelay; + else + m_pSwitchTile[TgtIndex].m_Delay = pLt->m_pSwitchTile[SrcIndex].m_Delay; + + if(!IsSwitchTileFlagsUsed(m_pSwitchTile[TgtIndex].m_Type)) + m_pSwitchTile[TgtIndex].m_Flags = 0; + else + m_pSwitchTile[TgtIndex].m_Flags = pLt->m_pSwitchTile[SrcIndex].m_Flags; + } + } + } + } + FlagModified(sx, sy, w, h); +} + +bool CLayerSwitch::ContainsElementWithId(int Id) +{ + for(int y = 0; y < m_Height; ++y) + { + for(int x = 0; x < m_Width; ++x) + { + if(IsSwitchTileNumberUsed(m_pSwitchTile[y * m_Width + x].m_Type) && m_pSwitchTile[y * m_Width + x].m_Number == Id) + { + return true; + } + } + } + + return false; +} diff --git a/src/game/editor/mapitems/layer_tele.cpp b/src/game/editor/mapitems/layer_tele.cpp new file mode 100644 index 000000000..56afbce78 --- /dev/null +++ b/src/game/editor/mapitems/layer_tele.cpp @@ -0,0 +1,247 @@ +#include + +CLayerTele::CLayerTele(int w, int h) : + CLayerTiles(w, h) +{ + str_copy(m_aName, "Tele"); + m_Tele = 1; + + m_pTeleTile = new CTeleTile[w * h]; + mem_zero(m_pTeleTile, (size_t)w * h * sizeof(CTeleTile)); +} + +CLayerTele::~CLayerTele() +{ + delete[] m_pTeleTile; +} + +void CLayerTele::Resize(int NewW, int NewH) +{ + // resize tele data + CTeleTile *pNewTeleData = new CTeleTile[NewW * NewH]; + mem_zero(pNewTeleData, (size_t)NewW * NewH * sizeof(CTeleTile)); + + // copy old data + for(int y = 0; y < minimum(NewH, m_Height); y++) + mem_copy(&pNewTeleData[y * NewW], &m_pTeleTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CTeleTile)); + + // replace old + delete[] m_pTeleTile; + m_pTeleTile = pNewTeleData; + + // resize tile data + CLayerTiles::Resize(NewW, NewH); + + // resize gamelayer too + if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) + m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); +} + +void CLayerTele::Shift(int Direction) +{ + CLayerTiles::Shift(Direction); + ShiftImpl(m_pTeleTile, Direction, m_pEditor->m_ShiftBy); +} + +bool CLayerTele::IsEmpty(const std::shared_ptr &pLayer) +{ + for(int y = 0; y < pLayer->m_Height; y++) + for(int x = 0; x < pLayer->m_Width; x++) + if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidTeleTile(pLayer->GetTile(x, y).m_Index)) + return false; + + return true; +} + +void CLayerTele::BrushDraw(std::shared_ptr pBrush, float wx, float wy) +{ + if(m_Readonly) + return; + + std::shared_ptr pTeleLayer = std::static_pointer_cast(pBrush); + int sx = ConvertX(wx); + int sy = ConvertY(wy); + if(str_comp(pTeleLayer->m_aFileName, m_pEditor->m_aFileName)) + m_pEditor->m_TeleNumber = pTeleLayer->m_TeleNum; + + bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pTeleLayer); + + for(int y = 0; y < pTeleLayer->m_Height; y++) + for(int x = 0; x < pTeleLayer->m_Width; x++) + { + int fx = x + sx; + int fy = y + sy; + + if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(!Destructive && GetTile(fx, fy).m_Index) + continue; + + if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidTeleTile(pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index)) && pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index != TILE_AIR) + { + if(!IsTeleTileNumberUsed(pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index)) + { + // Tele tile number is unused. Set a known value which is not 0, + // as tiles with number 0 would be ignored by previous versions. + m_pTeleTile[fy * m_Width + fx].m_Number = 255; + } + else if(m_pEditor->m_TeleNumber != pTeleLayer->m_TeleNum) + { + m_pTeleTile[fy * m_Width + fx].m_Number = m_pEditor->m_TeleNumber; + } + else if(pTeleLayer->m_pTeleTile[y * pTeleLayer->m_Width + x].m_Number) + { + m_pTeleTile[fy * m_Width + fx].m_Number = pTeleLayer->m_pTeleTile[y * pTeleLayer->m_Width + x].m_Number; + } + else + { + if(!m_pEditor->m_TeleNumber) + { + m_pTeleTile[fy * m_Width + fx].m_Number = 0; + m_pTeleTile[fy * m_Width + fx].m_Type = 0; + m_pTiles[fy * m_Width + fx].m_Index = 0; + continue; + } + else + m_pTeleTile[fy * m_Width + fx].m_Number = m_pEditor->m_TeleNumber; + } + + m_pTeleTile[fy * m_Width + fx].m_Type = pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index; + m_pTiles[fy * m_Width + fx].m_Index = pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index; + } + else + { + m_pTeleTile[fy * m_Width + fx].m_Number = 0; + m_pTeleTile[fy * m_Width + fx].m_Type = 0; + m_pTiles[fy * m_Width + fx].m_Index = 0; + } + } + FlagModified(sx, sy, pTeleLayer->m_Width, pTeleLayer->m_Height); +} + +void CLayerTele::BrushFlipX() +{ + CLayerTiles::BrushFlipX(); + BrushFlipXImpl(m_pTeleTile); +} + +void CLayerTele::BrushFlipY() +{ + CLayerTiles::BrushFlipY(); + BrushFlipYImpl(m_pTeleTile); +} + +void CLayerTele::BrushRotate(float Amount) +{ + int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270° + if(Rotation < 0) + Rotation += 4; + + if(Rotation == 1 || Rotation == 3) + { + // 90° rotation + CTeleTile *pTempData1 = new CTeleTile[m_Width * m_Height]; + CTile *pTempData2 = new CTile[m_Width * m_Height]; + mem_copy(pTempData1, m_pTeleTile, (size_t)m_Width * m_Height * sizeof(CTeleTile)); + mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile)); + CTeleTile *pDst1 = m_pTeleTile; + CTile *pDst2 = m_pTiles; + for(int x = 0; x < m_Width; ++x) + for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2) + { + *pDst1 = pTempData1[y * m_Width + x]; + *pDst2 = pTempData2[y * m_Width + x]; + } + + std::swap(m_Width, m_Height); + delete[] pTempData1; + delete[] pTempData2; + } + + if(Rotation == 2 || Rotation == 3) + { + BrushFlipX(); + BrushFlipY(); + } +} + +void CLayerTele::FillSelection(bool Empty, std::shared_ptr pBrush, CUIRect Rect) +{ + if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) + return; + + Snap(&Rect); // corrects Rect; no need of <= + + Snap(&Rect); + + int sx = ConvertX(Rect.x); + int sy = ConvertY(Rect.y); + int w = ConvertX(Rect.w); + int h = ConvertY(Rect.h); + + std::shared_ptr pLt = std::static_pointer_cast(pBrush); + + bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); + + for(int y = 0; y < h; y++) + { + for(int x = 0; x < w; x++) + { + int fx = x + sx; + int fy = y + sy; + + if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(!Destructive && GetTile(fx, fy).m_Index) + continue; + + const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height); + const int TgtIndex = fy * m_Width + fx; + + if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidTeleTile((pLt->m_pTiles[SrcIndex]).m_Index))) + { + m_pTiles[TgtIndex].m_Index = 0; + m_pTeleTile[TgtIndex].m_Type = 0; + m_pTeleTile[TgtIndex].m_Number = 0; + } + else + { + m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex]; + if(pLt->m_Tele && m_pTiles[TgtIndex].m_Index > 0) + { + m_pTeleTile[TgtIndex].m_Type = m_pTiles[TgtIndex].m_Index; + + if(!IsTeleTileNumberUsed(m_pTeleTile[TgtIndex].m_Type)) + { + // Tele tile number is unused. Set a known value which is not 0, + // as tiles with number 0 would be ignored by previous versions. + m_pTeleTile[TgtIndex].m_Number = 255; + } + else if((pLt->m_pTeleTile[SrcIndex].m_Number == 0 && m_pEditor->m_TeleNumber) || m_pEditor->m_TeleNumber != pLt->m_TeleNum) + m_pTeleTile[TgtIndex].m_Number = m_pEditor->m_TeleNumber; + else + m_pTeleTile[TgtIndex].m_Number = pLt->m_pTeleTile[SrcIndex].m_Number; + } + } + } + } + FlagModified(sx, sy, w, h); +} + +bool CLayerTele::ContainsElementWithId(int Id) +{ + for(int y = 0; y < m_Height; ++y) + { + for(int x = 0; x < m_Width; ++x) + { + if(IsTeleTileNumberUsed(m_pTeleTile[y * m_Width + x].m_Type) && m_pTeleTile[y * m_Width + x].m_Number == Id) + { + return true; + } + } + } + + return false; +} diff --git a/src/game/editor/layer_tiles.cpp b/src/game/editor/mapitems/layer_tiles.cpp similarity index 50% rename from src/game/editor/layer_tiles.cpp rename to src/game/editor/mapitems/layer_tiles.cpp index 9c56b43f6..bcd8bc0b5 100644 --- a/src/game/editor/layer_tiles.cpp +++ b/src/game/editor/mapitems/layer_tiles.cpp @@ -1,17 +1,9 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ -#include +#include -#include -#include -#include -#include - -#include "editor.h" -#include - -#include #include +#include CLayerTiles::CLayerTiles(int w, int h) { @@ -1089,1026 +1081,3 @@ void CLayerTiles::ModifyEnvelopeIndex(FIndexModifyFunction Func) { Func(&m_ColorEnv); } - -CLayerTele::CLayerTele(int w, int h) : - CLayerTiles(w, h) -{ - str_copy(m_aName, "Tele"); - m_Tele = 1; - - m_pTeleTile = new CTeleTile[w * h]; - mem_zero(m_pTeleTile, (size_t)w * h * sizeof(CTeleTile)); -} - -CLayerTele::~CLayerTele() -{ - delete[] m_pTeleTile; -} - -void CLayerTele::Resize(int NewW, int NewH) -{ - // resize tele data - CTeleTile *pNewTeleData = new CTeleTile[NewW * NewH]; - mem_zero(pNewTeleData, (size_t)NewW * NewH * sizeof(CTeleTile)); - - // copy old data - for(int y = 0; y < minimum(NewH, m_Height); y++) - mem_copy(&pNewTeleData[y * NewW], &m_pTeleTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CTeleTile)); - - // replace old - delete[] m_pTeleTile; - m_pTeleTile = pNewTeleData; - - // resize tile data - CLayerTiles::Resize(NewW, NewH); - - // resize gamelayer too - if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) - m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); -} - -void CLayerTele::Shift(int Direction) -{ - CLayerTiles::Shift(Direction); - ShiftImpl(m_pTeleTile, Direction, m_pEditor->m_ShiftBy); -} - -bool CLayerTele::IsEmpty(const std::shared_ptr &pLayer) -{ - for(int y = 0; y < pLayer->m_Height; y++) - for(int x = 0; x < pLayer->m_Width; x++) - if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidTeleTile(pLayer->GetTile(x, y).m_Index)) - return false; - - return true; -} - -void CLayerTele::BrushDraw(std::shared_ptr pBrush, float wx, float wy) -{ - if(m_Readonly) - return; - - std::shared_ptr pTeleLayer = std::static_pointer_cast(pBrush); - int sx = ConvertX(wx); - int sy = ConvertY(wy); - if(str_comp(pTeleLayer->m_aFileName, m_pEditor->m_aFileName)) - m_pEditor->m_TeleNumber = pTeleLayer->m_TeleNum; - - bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pTeleLayer); - - for(int y = 0; y < pTeleLayer->m_Height; y++) - for(int x = 0; x < pTeleLayer->m_Width; x++) - { - int fx = x + sx; - int fy = y + sy; - - if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) - continue; - - if(!Destructive && GetTile(fx, fy).m_Index) - continue; - - if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidTeleTile(pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index)) && pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index != TILE_AIR) - { - if(!IsTeleTileNumberUsed(pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index)) - { - // Tele tile number is unused. Set a known value which is not 0, - // as tiles with number 0 would be ignored by previous versions. - m_pTeleTile[fy * m_Width + fx].m_Number = 255; - } - else if(m_pEditor->m_TeleNumber != pTeleLayer->m_TeleNum) - { - m_pTeleTile[fy * m_Width + fx].m_Number = m_pEditor->m_TeleNumber; - } - else if(pTeleLayer->m_pTeleTile[y * pTeleLayer->m_Width + x].m_Number) - { - m_pTeleTile[fy * m_Width + fx].m_Number = pTeleLayer->m_pTeleTile[y * pTeleLayer->m_Width + x].m_Number; - } - else - { - if(!m_pEditor->m_TeleNumber) - { - m_pTeleTile[fy * m_Width + fx].m_Number = 0; - m_pTeleTile[fy * m_Width + fx].m_Type = 0; - m_pTiles[fy * m_Width + fx].m_Index = 0; - continue; - } - else - m_pTeleTile[fy * m_Width + fx].m_Number = m_pEditor->m_TeleNumber; - } - - m_pTeleTile[fy * m_Width + fx].m_Type = pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index; - m_pTiles[fy * m_Width + fx].m_Index = pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index; - } - else - { - m_pTeleTile[fy * m_Width + fx].m_Number = 0; - m_pTeleTile[fy * m_Width + fx].m_Type = 0; - m_pTiles[fy * m_Width + fx].m_Index = 0; - } - } - FlagModified(sx, sy, pTeleLayer->m_Width, pTeleLayer->m_Height); -} - -void CLayerTele::BrushFlipX() -{ - CLayerTiles::BrushFlipX(); - BrushFlipXImpl(m_pTeleTile); -} - -void CLayerTele::BrushFlipY() -{ - CLayerTiles::BrushFlipY(); - BrushFlipYImpl(m_pTeleTile); -} - -void CLayerTele::BrushRotate(float Amount) -{ - int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270° - if(Rotation < 0) - Rotation += 4; - - if(Rotation == 1 || Rotation == 3) - { - // 90° rotation - CTeleTile *pTempData1 = new CTeleTile[m_Width * m_Height]; - CTile *pTempData2 = new CTile[m_Width * m_Height]; - mem_copy(pTempData1, m_pTeleTile, (size_t)m_Width * m_Height * sizeof(CTeleTile)); - mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile)); - CTeleTile *pDst1 = m_pTeleTile; - CTile *pDst2 = m_pTiles; - for(int x = 0; x < m_Width; ++x) - for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2) - { - *pDst1 = pTempData1[y * m_Width + x]; - *pDst2 = pTempData2[y * m_Width + x]; - } - - std::swap(m_Width, m_Height); - delete[] pTempData1; - delete[] pTempData2; - } - - if(Rotation == 2 || Rotation == 3) - { - BrushFlipX(); - BrushFlipY(); - } -} - -void CLayerTele::FillSelection(bool Empty, std::shared_ptr pBrush, CUIRect Rect) -{ - if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) - return; - - Snap(&Rect); // corrects Rect; no need of <= - - Snap(&Rect); - - int sx = ConvertX(Rect.x); - int sy = ConvertY(Rect.y); - int w = ConvertX(Rect.w); - int h = ConvertY(Rect.h); - - std::shared_ptr pLt = std::static_pointer_cast(pBrush); - - bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); - - for(int y = 0; y < h; y++) - { - for(int x = 0; x < w; x++) - { - int fx = x + sx; - int fy = y + sy; - - if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) - continue; - - if(!Destructive && GetTile(fx, fy).m_Index) - continue; - - const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height); - const int TgtIndex = fy * m_Width + fx; - - if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidTeleTile((pLt->m_pTiles[SrcIndex]).m_Index))) - { - m_pTiles[TgtIndex].m_Index = 0; - m_pTeleTile[TgtIndex].m_Type = 0; - m_pTeleTile[TgtIndex].m_Number = 0; - } - else - { - m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex]; - if(pLt->m_Tele && m_pTiles[TgtIndex].m_Index > 0) - { - m_pTeleTile[TgtIndex].m_Type = m_pTiles[TgtIndex].m_Index; - - if(!IsTeleTileNumberUsed(m_pTeleTile[TgtIndex].m_Type)) - { - // Tele tile number is unused. Set a known value which is not 0, - // as tiles with number 0 would be ignored by previous versions. - m_pTeleTile[TgtIndex].m_Number = 255; - } - else if((pLt->m_pTeleTile[SrcIndex].m_Number == 0 && m_pEditor->m_TeleNumber) || m_pEditor->m_TeleNumber != pLt->m_TeleNum) - m_pTeleTile[TgtIndex].m_Number = m_pEditor->m_TeleNumber; - else - m_pTeleTile[TgtIndex].m_Number = pLt->m_pTeleTile[SrcIndex].m_Number; - } - } - } - } - FlagModified(sx, sy, w, h); -} - -bool CLayerTele::ContainsElementWithId(int Id) -{ - for(int y = 0; y < m_Height; ++y) - { - for(int x = 0; x < m_Width; ++x) - { - if(IsTeleTileNumberUsed(m_pTeleTile[y * m_Width + x].m_Type) && m_pTeleTile[y * m_Width + x].m_Number == Id) - { - return true; - } - } - } - - return false; -} - -CLayerSpeedup::CLayerSpeedup(int w, int h) : - CLayerTiles(w, h) -{ - str_copy(m_aName, "Speedup"); - m_Speedup = 1; - - m_pSpeedupTile = new CSpeedupTile[w * h]; - mem_zero(m_pSpeedupTile, (size_t)w * h * sizeof(CSpeedupTile)); -} - -CLayerSpeedup::~CLayerSpeedup() -{ - delete[] m_pSpeedupTile; -} - -void CLayerSpeedup::Resize(int NewW, int NewH) -{ - // resize speedup data - CSpeedupTile *pNewSpeedupData = new CSpeedupTile[NewW * NewH]; - mem_zero(pNewSpeedupData, (size_t)NewW * NewH * sizeof(CSpeedupTile)); - - // copy old data - for(int y = 0; y < minimum(NewH, m_Height); y++) - mem_copy(&pNewSpeedupData[y * NewW], &m_pSpeedupTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CSpeedupTile)); - - // replace old - delete[] m_pSpeedupTile; - m_pSpeedupTile = pNewSpeedupData; - - // resize tile data - CLayerTiles::Resize(NewW, NewH); - - // resize gamelayer too - if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) - m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); -} - -void CLayerSpeedup::Shift(int Direction) -{ - CLayerTiles::Shift(Direction); - ShiftImpl(m_pSpeedupTile, Direction, m_pEditor->m_ShiftBy); -} - -bool CLayerSpeedup::IsEmpty(const std::shared_ptr &pLayer) -{ - for(int y = 0; y < pLayer->m_Height; y++) - for(int x = 0; x < pLayer->m_Width; x++) - if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidSpeedupTile(pLayer->GetTile(x, y).m_Index)) - return false; - - return true; -} - -void CLayerSpeedup::BrushDraw(std::shared_ptr pBrush, float wx, float wy) -{ - if(m_Readonly) - return; - - std::shared_ptr pSpeedupLayer = std::static_pointer_cast(pBrush); - int sx = ConvertX(wx); - int sy = ConvertY(wy); - if(str_comp(pSpeedupLayer->m_aFileName, m_pEditor->m_aFileName)) - { - m_pEditor->m_SpeedupAngle = pSpeedupLayer->m_SpeedupAngle; - m_pEditor->m_SpeedupForce = pSpeedupLayer->m_SpeedupForce; - m_pEditor->m_SpeedupMaxSpeed = pSpeedupLayer->m_SpeedupMaxSpeed; - } - - bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pSpeedupLayer); - - for(int y = 0; y < pSpeedupLayer->m_Height; y++) - for(int x = 0; x < pSpeedupLayer->m_Width; x++) - { - int fx = x + sx; - int fy = y + sy; - - if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) - continue; - - if(!Destructive && GetTile(fx, fy).m_Index) - continue; - - if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidSpeedupTile(pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index)) && pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index != TILE_AIR) - { - if(m_pEditor->m_SpeedupAngle != pSpeedupLayer->m_SpeedupAngle || m_pEditor->m_SpeedupForce != pSpeedupLayer->m_SpeedupForce || m_pEditor->m_SpeedupMaxSpeed != pSpeedupLayer->m_SpeedupMaxSpeed) - { - m_pSpeedupTile[fy * m_Width + fx].m_Force = m_pEditor->m_SpeedupForce; - m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed; - m_pSpeedupTile[fy * m_Width + fx].m_Angle = m_pEditor->m_SpeedupAngle; - m_pSpeedupTile[fy * m_Width + fx].m_Type = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; - m_pTiles[fy * m_Width + fx].m_Index = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; - } - else if(pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_Force) - { - m_pSpeedupTile[fy * m_Width + fx].m_Force = pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_Force; - m_pSpeedupTile[fy * m_Width + fx].m_Angle = pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_Angle; - m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_MaxSpeed; - m_pSpeedupTile[fy * m_Width + fx].m_Type = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; - m_pTiles[fy * m_Width + fx].m_Index = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; - } - else if(m_pEditor->m_SpeedupForce) - { - m_pSpeedupTile[fy * m_Width + fx].m_Force = m_pEditor->m_SpeedupForce; - m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed; - m_pSpeedupTile[fy * m_Width + fx].m_Angle = m_pEditor->m_SpeedupAngle; - m_pSpeedupTile[fy * m_Width + fx].m_Type = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; - m_pTiles[fy * m_Width + fx].m_Index = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index; - } - else - { - m_pSpeedupTile[fy * m_Width + fx].m_Force = 0; - m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = 0; - m_pSpeedupTile[fy * m_Width + fx].m_Angle = 0; - m_pSpeedupTile[fy * m_Width + fx].m_Type = 0; - m_pTiles[fy * m_Width + fx].m_Index = 0; - } - } - else - { - m_pSpeedupTile[fy * m_Width + fx].m_Force = 0; - m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = 0; - m_pSpeedupTile[fy * m_Width + fx].m_Angle = 0; - m_pSpeedupTile[fy * m_Width + fx].m_Type = 0; - m_pTiles[fy * m_Width + fx].m_Index = 0; - } - } - FlagModified(sx, sy, pSpeedupLayer->m_Width, pSpeedupLayer->m_Height); -} - -void CLayerSpeedup::BrushFlipX() -{ - CLayerTiles::BrushFlipX(); - BrushFlipXImpl(m_pSpeedupTile); -} - -void CLayerSpeedup::BrushFlipY() -{ - CLayerTiles::BrushFlipY(); - BrushFlipYImpl(m_pSpeedupTile); -} - -void CLayerSpeedup::BrushRotate(float Amount) -{ - int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270° - if(Rotation < 0) - Rotation += 4; - - if(Rotation == 1 || Rotation == 3) - { - // 90° rotation - CSpeedupTile *pTempData1 = new CSpeedupTile[m_Width * m_Height]; - CTile *pTempData2 = new CTile[m_Width * m_Height]; - mem_copy(pTempData1, m_pSpeedupTile, (size_t)m_Width * m_Height * sizeof(CSpeedupTile)); - mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile)); - CSpeedupTile *pDst1 = m_pSpeedupTile; - CTile *pDst2 = m_pTiles; - for(int x = 0; x < m_Width; ++x) - for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2) - { - *pDst1 = pTempData1[y * m_Width + x]; - *pDst2 = pTempData2[y * m_Width + x]; - } - - std::swap(m_Width, m_Height); - delete[] pTempData1; - delete[] pTempData2; - } - - if(Rotation == 2 || Rotation == 3) - { - BrushFlipX(); - BrushFlipY(); - } -} - -void CLayerSpeedup::FillSelection(bool Empty, std::shared_ptr pBrush, CUIRect Rect) -{ - if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) - return; - - Snap(&Rect); // corrects Rect; no need of <= - - Snap(&Rect); - - int sx = ConvertX(Rect.x); - int sy = ConvertY(Rect.y); - int w = ConvertX(Rect.w); - int h = ConvertY(Rect.h); - - std::shared_ptr pLt = std::static_pointer_cast(pBrush); - - bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); - - for(int y = 0; y < h; y++) - { - for(int x = 0; x < w; x++) - { - int fx = x + sx; - int fy = y + sy; - - if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) - continue; - - if(!Destructive && GetTile(fx, fy).m_Index) - continue; - - const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height); - const int TgtIndex = fy * m_Width + fx; - - if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidSpeedupTile((pLt->m_pTiles[SrcIndex]).m_Index))) // no speed up tile chosen: reset - { - m_pTiles[TgtIndex].m_Index = 0; - m_pSpeedupTile[TgtIndex].m_Force = 0; - m_pSpeedupTile[TgtIndex].m_Angle = 0; - } - else - { - m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex]; - if(pLt->m_Speedup && m_pTiles[TgtIndex].m_Index > 0) - { - m_pSpeedupTile[TgtIndex].m_Type = m_pTiles[TgtIndex].m_Index; - - if((pLt->m_pSpeedupTile[SrcIndex].m_Force == 0 && m_pEditor->m_SpeedupForce) || m_pEditor->m_SpeedupForce != pLt->m_SpeedupForce) - m_pSpeedupTile[TgtIndex].m_Force = m_pEditor->m_SpeedupForce; - else - m_pSpeedupTile[TgtIndex].m_Force = pLt->m_pSpeedupTile[SrcIndex].m_Force; - - if((pLt->m_pSpeedupTile[SrcIndex].m_Angle == 0 && m_pEditor->m_SpeedupAngle) || m_pEditor->m_SpeedupAngle != pLt->m_SpeedupAngle) - m_pSpeedupTile[TgtIndex].m_Angle = m_pEditor->m_SpeedupAngle; - else - m_pSpeedupTile[TgtIndex].m_Angle = pLt->m_pSpeedupTile[SrcIndex].m_Angle; - - if((pLt->m_pSpeedupTile[SrcIndex].m_MaxSpeed == 0 && m_pEditor->m_SpeedupMaxSpeed) || m_pEditor->m_SpeedupMaxSpeed != pLt->m_SpeedupMaxSpeed) - m_pSpeedupTile[TgtIndex].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed; - else - m_pSpeedupTile[TgtIndex].m_MaxSpeed = pLt->m_pSpeedupTile[SrcIndex].m_MaxSpeed; - } - } - } - } - FlagModified(sx, sy, w, h); -} - -CLayerFront::CLayerFront(int w, int h) : - CLayerTiles(w, h) -{ - str_copy(m_aName, "Front"); - m_Front = 1; -} - -void CLayerFront::SetTile(int x, int y, CTile Tile) -{ - if(Tile.m_Index == TILE_THROUGH_CUT) - { - CTile nohook = {TILE_NOHOOK}; - m_pEditor->m_Map.m_pGameLayer->CLayerTiles::SetTile(x, y, nohook); // NOLINT(bugprone-parent-virtual-call) - } - else if(Tile.m_Index == TILE_AIR && CLayerTiles::GetTile(x, y).m_Index == TILE_THROUGH_CUT) - { - CTile air = {TILE_AIR}; - m_pEditor->m_Map.m_pGameLayer->CLayerTiles::SetTile(x, y, air); // NOLINT(bugprone-parent-virtual-call) - } - if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidFrontTile(Tile.m_Index)) - { - CLayerTiles::SetTile(x, y, Tile); - } - else - { - CTile air = {TILE_AIR}; - CLayerTiles::SetTile(x, y, air); - if(!m_pEditor->m_PreventUnusedTilesWasWarned) - { - m_pEditor->m_PopupEventType = CEditor::POPEVENT_PREVENTUNUSEDTILES; - m_pEditor->m_PopupEventActivated = true; - m_pEditor->m_PreventUnusedTilesWasWarned = true; - } - } -} - -void CLayerFront::Resize(int NewW, int NewH) -{ - // resize tile data - CLayerTiles::Resize(NewW, NewH); - - // resize gamelayer too - if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) - m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); -} - -CLayerSwitch::CLayerSwitch(int w, int h) : - CLayerTiles(w, h) -{ - str_copy(m_aName, "Switch"); - m_Switch = 1; - - m_pSwitchTile = new CSwitchTile[w * h]; - mem_zero(m_pSwitchTile, (size_t)w * h * sizeof(CSwitchTile)); -} - -CLayerSwitch::~CLayerSwitch() -{ - delete[] m_pSwitchTile; -} - -void CLayerSwitch::Resize(int NewW, int NewH) -{ - // resize switch data - CSwitchTile *pNewSwitchData = new CSwitchTile[NewW * NewH]; - mem_zero(pNewSwitchData, (size_t)NewW * NewH * sizeof(CSwitchTile)); - - // copy old data - for(int y = 0; y < minimum(NewH, m_Height); y++) - mem_copy(&pNewSwitchData[y * NewW], &m_pSwitchTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CSwitchTile)); - - // replace old - delete[] m_pSwitchTile; - m_pSwitchTile = pNewSwitchData; - - // resize tile data - CLayerTiles::Resize(NewW, NewH); - - // resize gamelayer too - if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) - m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); -} - -void CLayerSwitch::Shift(int Direction) -{ - CLayerTiles::Shift(Direction); - ShiftImpl(m_pSwitchTile, Direction, m_pEditor->m_ShiftBy); -} - -bool CLayerSwitch::IsEmpty(const std::shared_ptr &pLayer) -{ - for(int y = 0; y < pLayer->m_Height; y++) - for(int x = 0; x < pLayer->m_Width; x++) - if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidSwitchTile(pLayer->GetTile(x, y).m_Index)) - return false; - - return true; -} - -void CLayerSwitch::BrushDraw(std::shared_ptr pBrush, float wx, float wy) -{ - if(m_Readonly) - return; - - std::shared_ptr pSwitchLayer = std::static_pointer_cast(pBrush); - int sx = ConvertX(wx); - int sy = ConvertY(wy); - if(str_comp(pSwitchLayer->m_aFileName, m_pEditor->m_aFileName)) - { - m_pEditor->m_SwitchNum = pSwitchLayer->m_SwitchNumber; - m_pEditor->m_SwitchDelay = pSwitchLayer->m_SwitchDelay; - } - - bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pSwitchLayer); - - for(int y = 0; y < pSwitchLayer->m_Height; y++) - for(int x = 0; x < pSwitchLayer->m_Width; x++) - { - int fx = x + sx; - int fy = y + sy; - - if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) - continue; - - if(!Destructive && GetTile(fx, fy).m_Index) - continue; - - if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidSwitchTile(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index)) && pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index != TILE_AIR) - { - if(m_pEditor->m_SwitchNum != pSwitchLayer->m_SwitchNumber || m_pEditor->m_SwitchDelay != pSwitchLayer->m_SwitchDelay) - { - m_pSwitchTile[fy * m_Width + fx].m_Number = m_pEditor->m_SwitchNum; - m_pSwitchTile[fy * m_Width + fx].m_Delay = m_pEditor->m_SwitchDelay; - } - else if(pSwitchLayer->m_pSwitchTile[y * pSwitchLayer->m_Width + x].m_Number) - { - m_pSwitchTile[fy * m_Width + fx].m_Number = pSwitchLayer->m_pSwitchTile[y * pSwitchLayer->m_Width + x].m_Number; - m_pSwitchTile[fy * m_Width + fx].m_Delay = pSwitchLayer->m_pSwitchTile[y * pSwitchLayer->m_Width + x].m_Delay; - } - else - { - m_pSwitchTile[fy * m_Width + fx].m_Number = m_pEditor->m_SwitchNum; - m_pSwitchTile[fy * m_Width + fx].m_Delay = m_pEditor->m_SwitchDelay; - } - - m_pSwitchTile[fy * m_Width + fx].m_Type = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index; - m_pSwitchTile[fy * m_Width + fx].m_Flags = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Flags; - m_pTiles[fy * m_Width + fx].m_Index = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index; - m_pTiles[fy * m_Width + fx].m_Flags = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Flags; - - if(!IsSwitchTileFlagsUsed(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index)) - { - m_pSwitchTile[fy * m_Width + fx].m_Flags = 0; - } - if(!IsSwitchTileNumberUsed(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index)) - { - m_pSwitchTile[fy * m_Width + fx].m_Number = 0; - } - if(!IsSwitchTileDelayUsed(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index)) - { - m_pSwitchTile[fy * m_Width + fx].m_Delay = 0; - } - } - else - { - m_pSwitchTile[fy * m_Width + fx].m_Number = 0; - m_pSwitchTile[fy * m_Width + fx].m_Type = 0; - m_pSwitchTile[fy * m_Width + fx].m_Flags = 0; - m_pSwitchTile[fy * m_Width + fx].m_Delay = 0; - m_pTiles[fy * m_Width + fx].m_Index = 0; - } - } - FlagModified(sx, sy, pSwitchLayer->m_Width, pSwitchLayer->m_Height); -} - -void CLayerSwitch::BrushFlipX() -{ - CLayerTiles::BrushFlipX(); - BrushFlipXImpl(m_pSwitchTile); -} - -void CLayerSwitch::BrushFlipY() -{ - CLayerTiles::BrushFlipY(); - BrushFlipYImpl(m_pSwitchTile); -} - -void CLayerSwitch::BrushRotate(float Amount) -{ - int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270° - if(Rotation < 0) - Rotation += 4; - - if(Rotation == 1 || Rotation == 3) - { - // 90° rotation - CSwitchTile *pTempData1 = new CSwitchTile[m_Width * m_Height]; - CTile *pTempData2 = new CTile[m_Width * m_Height]; - mem_copy(pTempData1, m_pSwitchTile, (size_t)m_Width * m_Height * sizeof(CSwitchTile)); - mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile)); - CSwitchTile *pDst1 = m_pSwitchTile; - CTile *pDst2 = m_pTiles; - for(int x = 0; x < m_Width; ++x) - for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2) - { - *pDst1 = pTempData1[y * m_Width + x]; - *pDst2 = pTempData2[y * m_Width + x]; - if(IsRotatableTile(pDst2->m_Index)) - { - if(pDst2->m_Flags & TILEFLAG_ROTATE) - pDst2->m_Flags ^= (TILEFLAG_YFLIP | TILEFLAG_XFLIP); - pDst2->m_Flags ^= TILEFLAG_ROTATE; - } - } - - std::swap(m_Width, m_Height); - delete[] pTempData1; - delete[] pTempData2; - } - - if(Rotation == 2 || Rotation == 3) - { - BrushFlipX(); - BrushFlipY(); - } -} - -void CLayerSwitch::FillSelection(bool Empty, std::shared_ptr pBrush, CUIRect Rect) -{ - if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) - return; - - Snap(&Rect); // corrects Rect; no need of <= - - Snap(&Rect); - - int sx = ConvertX(Rect.x); - int sy = ConvertY(Rect.y); - int w = ConvertX(Rect.w); - int h = ConvertY(Rect.h); - - std::shared_ptr pLt = std::static_pointer_cast(pBrush); - - bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); - - for(int y = 0; y < h; y++) - { - for(int x = 0; x < w; x++) - { - int fx = x + sx; - int fy = y + sy; - - if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) - continue; - - if(!Destructive && GetTile(fx, fy).m_Index) - continue; - - const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height); - const int TgtIndex = fy * m_Width + fx; - - if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidSwitchTile((pLt->m_pTiles[SrcIndex]).m_Index))) - { - m_pTiles[TgtIndex].m_Index = 0; - m_pSwitchTile[TgtIndex].m_Type = 0; - m_pSwitchTile[TgtIndex].m_Number = 0; - m_pSwitchTile[TgtIndex].m_Delay = 0; - } - else - { - m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex]; - m_pSwitchTile[TgtIndex].m_Type = m_pTiles[TgtIndex].m_Index; - if(pLt->m_Switch && m_pTiles[TgtIndex].m_Index > 0) - { - if(!IsSwitchTileNumberUsed(m_pSwitchTile[TgtIndex].m_Type)) - m_pSwitchTile[TgtIndex].m_Number = 0; - else if(pLt->m_pSwitchTile[SrcIndex].m_Number == 0 || m_pEditor->m_SwitchNum != pLt->m_SwitchNumber) - m_pSwitchTile[TgtIndex].m_Number = m_pEditor->m_SwitchNum; - else - m_pSwitchTile[TgtIndex].m_Number = pLt->m_pSwitchTile[SrcIndex].m_Number; - - if(!IsSwitchTileDelayUsed(m_pSwitchTile[TgtIndex].m_Type)) - m_pSwitchTile[TgtIndex].m_Delay = 0; - else if(pLt->m_pSwitchTile[SrcIndex].m_Delay == 0 || m_pEditor->m_SwitchDelay != pLt->m_SwitchDelay) - m_pSwitchTile[TgtIndex].m_Delay = m_pEditor->m_SwitchDelay; - else - m_pSwitchTile[TgtIndex].m_Delay = pLt->m_pSwitchTile[SrcIndex].m_Delay; - - if(!IsSwitchTileFlagsUsed(m_pSwitchTile[TgtIndex].m_Type)) - m_pSwitchTile[TgtIndex].m_Flags = 0; - else - m_pSwitchTile[TgtIndex].m_Flags = pLt->m_pSwitchTile[SrcIndex].m_Flags; - } - } - } - } - FlagModified(sx, sy, w, h); -} - -bool CLayerSwitch::ContainsElementWithId(int Id) -{ - for(int y = 0; y < m_Height; ++y) - { - for(int x = 0; x < m_Width; ++x) - { - if(IsSwitchTileNumberUsed(m_pSwitchTile[y * m_Width + x].m_Type) && m_pSwitchTile[y * m_Width + x].m_Number == Id) - { - return true; - } - } - } - - return false; -} - -//------------------------------------------------------ - -CLayerTune::CLayerTune(int w, int h) : - CLayerTiles(w, h) -{ - str_copy(m_aName, "Tune"); - m_Tune = 1; - - m_pTuneTile = new CTuneTile[w * h]; - mem_zero(m_pTuneTile, (size_t)w * h * sizeof(CTuneTile)); -} - -CLayerTune::~CLayerTune() -{ - delete[] m_pTuneTile; -} - -void CLayerTune::Resize(int NewW, int NewH) -{ - // resize Tune data - CTuneTile *pNewTuneData = new CTuneTile[NewW * NewH]; - mem_zero(pNewTuneData, (size_t)NewW * NewH * sizeof(CTuneTile)); - - // copy old data - for(int y = 0; y < minimum(NewH, m_Height); y++) - mem_copy(&pNewTuneData[y * NewW], &m_pTuneTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CTuneTile)); - - // replace old - delete[] m_pTuneTile; - m_pTuneTile = pNewTuneData; - - // resize tile data - CLayerTiles::Resize(NewW, NewH); - - // resize gamelayer too - if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) - m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); -} - -void CLayerTune::Shift(int Direction) -{ - CLayerTiles::Shift(Direction); - ShiftImpl(m_pTuneTile, Direction, m_pEditor->m_ShiftBy); -} - -bool CLayerTune::IsEmpty(const std::shared_ptr &pLayer) -{ - for(int y = 0; y < pLayer->m_Height; y++) - for(int x = 0; x < pLayer->m_Width; x++) - if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidTuneTile(pLayer->GetTile(x, y).m_Index)) - return false; - - return true; -} - -void CLayerTune::BrushDraw(std::shared_ptr pBrush, float wx, float wy) -{ - if(m_Readonly) - return; - - std::shared_ptr pTuneLayer = std::static_pointer_cast(pBrush); - int sx = ConvertX(wx); - int sy = ConvertY(wy); - if(str_comp(pTuneLayer->m_aFileName, m_pEditor->m_aFileName)) - { - m_pEditor->m_TuningNum = pTuneLayer->m_TuningNumber; - } - - bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pTuneLayer); - - for(int y = 0; y < pTuneLayer->m_Height; y++) - for(int x = 0; x < pTuneLayer->m_Width; x++) - { - int fx = x + sx; - int fy = y + sy; - - if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) - continue; - - if(!Destructive && GetTile(fx, fy).m_Index) - continue; - - if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidTuneTile(pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index)) && pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index != TILE_AIR) - { - if(m_pEditor->m_TuningNum != pTuneLayer->m_TuningNumber) - { - m_pTuneTile[fy * m_Width + fx].m_Number = m_pEditor->m_TuningNum; - } - else if(pTuneLayer->m_pTuneTile[y * pTuneLayer->m_Width + x].m_Number) - m_pTuneTile[fy * m_Width + fx].m_Number = pTuneLayer->m_pTuneTile[y * pTuneLayer->m_Width + x].m_Number; - else - { - if(!m_pEditor->m_TuningNum) - { - m_pTuneTile[fy * m_Width + fx].m_Number = 0; - m_pTuneTile[fy * m_Width + fx].m_Type = 0; - m_pTiles[fy * m_Width + fx].m_Index = 0; - continue; - } - else - m_pTuneTile[fy * m_Width + fx].m_Number = m_pEditor->m_TuningNum; - } - - m_pTuneTile[fy * m_Width + fx].m_Type = pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index; - m_pTiles[fy * m_Width + fx].m_Index = pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index; - } - else - { - m_pTuneTile[fy * m_Width + fx].m_Number = 0; - m_pTuneTile[fy * m_Width + fx].m_Type = 0; - m_pTiles[fy * m_Width + fx].m_Index = 0; - } - } - FlagModified(sx, sy, pTuneLayer->m_Width, pTuneLayer->m_Height); -} - -void CLayerTune::BrushFlipX() -{ - CLayerTiles::BrushFlipX(); - BrushFlipXImpl(m_pTuneTile); -} - -void CLayerTune::BrushFlipY() -{ - CLayerTiles::BrushFlipY(); - BrushFlipYImpl(m_pTuneTile); -} - -void CLayerTune::BrushRotate(float Amount) -{ - int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270° - if(Rotation < 0) - Rotation += 4; - - if(Rotation == 1 || Rotation == 3) - { - // 90° rotation - CTuneTile *pTempData1 = new CTuneTile[m_Width * m_Height]; - CTile *pTempData2 = new CTile[m_Width * m_Height]; - mem_copy(pTempData1, m_pTuneTile, (size_t)m_Width * m_Height * sizeof(CTuneTile)); - mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile)); - CTuneTile *pDst1 = m_pTuneTile; - CTile *pDst2 = m_pTiles; - for(int x = 0; x < m_Width; ++x) - for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2) - { - *pDst1 = pTempData1[y * m_Width + x]; - *pDst2 = pTempData2[y * m_Width + x]; - } - - std::swap(m_Width, m_Height); - delete[] pTempData1; - delete[] pTempData2; - } - - if(Rotation == 2 || Rotation == 3) - { - BrushFlipX(); - BrushFlipY(); - } -} - -void CLayerTune::FillSelection(bool Empty, std::shared_ptr pBrush, CUIRect Rect) -{ - if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) - return; - - Snap(&Rect); // corrects Rect; no need of <= - - int sx = ConvertX(Rect.x); - int sy = ConvertY(Rect.y); - int w = ConvertX(Rect.w); - int h = ConvertY(Rect.h); - - std::shared_ptr pLt = std::static_pointer_cast(pBrush); - - bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); - - for(int y = 0; y < h; y++) - { - for(int x = 0; x < w; x++) - { - int fx = x + sx; - int fy = y + sy; - - if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) - continue; - - if(!Destructive && GetTile(fx, fy).m_Index) - continue; - - const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height); - const int TgtIndex = fy * m_Width + fx; - - if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidTuneTile((pLt->m_pTiles[SrcIndex]).m_Index))) - { - m_pTiles[TgtIndex].m_Index = 0; - m_pTuneTile[TgtIndex].m_Type = 0; - m_pTuneTile[TgtIndex].m_Number = 0; - } - else - { - m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex]; - if(pLt->m_Tune && m_pTiles[TgtIndex].m_Index > 0) - { - m_pTuneTile[TgtIndex].m_Type = m_pTiles[fy * m_Width + fx].m_Index; - - if((pLt->m_pTuneTile[SrcIndex].m_Number == 0 && m_pEditor->m_TuningNum) || m_pEditor->m_TuningNum != pLt->m_TuningNumber) - m_pTuneTile[TgtIndex].m_Number = m_pEditor->m_TuningNum; - else - m_pTuneTile[TgtIndex].m_Number = pLt->m_pTuneTile[SrcIndex].m_Number; - } - } - } - } - - FlagModified(sx, sy, w, h); -} diff --git a/src/game/editor/mapitems/layer_tune.cpp b/src/game/editor/mapitems/layer_tune.cpp new file mode 100644 index 000000000..13c8ee17b --- /dev/null +++ b/src/game/editor/mapitems/layer_tune.cpp @@ -0,0 +1,218 @@ +#include + +CLayerTune::CLayerTune(int w, int h) : + CLayerTiles(w, h) +{ + str_copy(m_aName, "Tune"); + m_Tune = 1; + + m_pTuneTile = new CTuneTile[w * h]; + mem_zero(m_pTuneTile, (size_t)w * h * sizeof(CTuneTile)); +} + +CLayerTune::~CLayerTune() +{ + delete[] m_pTuneTile; +} + +void CLayerTune::Resize(int NewW, int NewH) +{ + // resize Tune data + CTuneTile *pNewTuneData = new CTuneTile[NewW * NewH]; + mem_zero(pNewTuneData, (size_t)NewW * NewH * sizeof(CTuneTile)); + + // copy old data + for(int y = 0; y < minimum(NewH, m_Height); y++) + mem_copy(&pNewTuneData[y * NewW], &m_pTuneTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CTuneTile)); + + // replace old + delete[] m_pTuneTile; + m_pTuneTile = pNewTuneData; + + // resize tile data + CLayerTiles::Resize(NewW, NewH); + + // resize gamelayer too + if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) + m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); +} + +void CLayerTune::Shift(int Direction) +{ + CLayerTiles::Shift(Direction); + ShiftImpl(m_pTuneTile, Direction, m_pEditor->m_ShiftBy); +} + +bool CLayerTune::IsEmpty(const std::shared_ptr &pLayer) +{ + for(int y = 0; y < pLayer->m_Height; y++) + for(int x = 0; x < pLayer->m_Width; x++) + if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidTuneTile(pLayer->GetTile(x, y).m_Index)) + return false; + + return true; +} + +void CLayerTune::BrushDraw(std::shared_ptr pBrush, float wx, float wy) +{ + if(m_Readonly) + return; + + std::shared_ptr pTuneLayer = std::static_pointer_cast(pBrush); + int sx = ConvertX(wx); + int sy = ConvertY(wy); + if(str_comp(pTuneLayer->m_aFileName, m_pEditor->m_aFileName)) + { + m_pEditor->m_TuningNum = pTuneLayer->m_TuningNumber; + } + + bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pTuneLayer); + + for(int y = 0; y < pTuneLayer->m_Height; y++) + for(int x = 0; x < pTuneLayer->m_Width; x++) + { + int fx = x + sx; + int fy = y + sy; + + if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(!Destructive && GetTile(fx, fy).m_Index) + continue; + + if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidTuneTile(pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index)) && pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index != TILE_AIR) + { + if(m_pEditor->m_TuningNum != pTuneLayer->m_TuningNumber) + { + m_pTuneTile[fy * m_Width + fx].m_Number = m_pEditor->m_TuningNum; + } + else if(pTuneLayer->m_pTuneTile[y * pTuneLayer->m_Width + x].m_Number) + m_pTuneTile[fy * m_Width + fx].m_Number = pTuneLayer->m_pTuneTile[y * pTuneLayer->m_Width + x].m_Number; + else + { + if(!m_pEditor->m_TuningNum) + { + m_pTuneTile[fy * m_Width + fx].m_Number = 0; + m_pTuneTile[fy * m_Width + fx].m_Type = 0; + m_pTiles[fy * m_Width + fx].m_Index = 0; + continue; + } + else + m_pTuneTile[fy * m_Width + fx].m_Number = m_pEditor->m_TuningNum; + } + + m_pTuneTile[fy * m_Width + fx].m_Type = pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index; + m_pTiles[fy * m_Width + fx].m_Index = pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index; + } + else + { + m_pTuneTile[fy * m_Width + fx].m_Number = 0; + m_pTuneTile[fy * m_Width + fx].m_Type = 0; + m_pTiles[fy * m_Width + fx].m_Index = 0; + } + } + FlagModified(sx, sy, pTuneLayer->m_Width, pTuneLayer->m_Height); +} + +void CLayerTune::BrushFlipX() +{ + CLayerTiles::BrushFlipX(); + BrushFlipXImpl(m_pTuneTile); +} + +void CLayerTune::BrushFlipY() +{ + CLayerTiles::BrushFlipY(); + BrushFlipYImpl(m_pTuneTile); +} + +void CLayerTune::BrushRotate(float Amount) +{ + int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270° + if(Rotation < 0) + Rotation += 4; + + if(Rotation == 1 || Rotation == 3) + { + // 90° rotation + CTuneTile *pTempData1 = new CTuneTile[m_Width * m_Height]; + CTile *pTempData2 = new CTile[m_Width * m_Height]; + mem_copy(pTempData1, m_pTuneTile, (size_t)m_Width * m_Height * sizeof(CTuneTile)); + mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile)); + CTuneTile *pDst1 = m_pTuneTile; + CTile *pDst2 = m_pTiles; + for(int x = 0; x < m_Width; ++x) + for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2) + { + *pDst1 = pTempData1[y * m_Width + x]; + *pDst2 = pTempData2[y * m_Width + x]; + } + + std::swap(m_Width, m_Height); + delete[] pTempData1; + delete[] pTempData2; + } + + if(Rotation == 2 || Rotation == 3) + { + BrushFlipX(); + BrushFlipY(); + } +} + +void CLayerTune::FillSelection(bool Empty, std::shared_ptr pBrush, CUIRect Rect) +{ + if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) + return; + + Snap(&Rect); // corrects Rect; no need of <= + + int sx = ConvertX(Rect.x); + int sy = ConvertY(Rect.y); + int w = ConvertX(Rect.w); + int h = ConvertY(Rect.h); + + std::shared_ptr pLt = std::static_pointer_cast(pBrush); + + bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); + + for(int y = 0; y < h; y++) + { + for(int x = 0; x < w; x++) + { + int fx = x + sx; + int fy = y + sy; + + if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(!Destructive && GetTile(fx, fy).m_Index) + continue; + + const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height); + const int TgtIndex = fy * m_Width + fx; + + if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidTuneTile((pLt->m_pTiles[SrcIndex]).m_Index))) + { + m_pTiles[TgtIndex].m_Index = 0; + m_pTuneTile[TgtIndex].m_Type = 0; + m_pTuneTile[TgtIndex].m_Number = 0; + } + else + { + m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex]; + if(pLt->m_Tune && m_pTiles[TgtIndex].m_Index > 0) + { + m_pTuneTile[TgtIndex].m_Type = m_pTiles[fy * m_Width + fx].m_Index; + + if((pLt->m_pTuneTile[SrcIndex].m_Number == 0 && m_pEditor->m_TuningNum) || m_pEditor->m_TuningNum != pLt->m_TuningNumber) + m_pTuneTile[TgtIndex].m_Number = m_pEditor->m_TuningNum; + else + m_pTuneTile[TgtIndex].m_Number = pLt->m_pTuneTile[SrcIndex].m_Number; + } + } + } + } + + FlagModified(sx, sy, w, h); +} diff --git a/src/game/editor/mapitems/map.cpp b/src/game/editor/mapitems/map.cpp new file mode 100644 index 000000000..29e683223 --- /dev/null +++ b/src/game/editor/mapitems/map.cpp @@ -0,0 +1,179 @@ +#include + +void CEditorMap::OnModify() +{ + m_Modified = true; + m_ModifiedAuto = true; + m_LastModifiedTime = m_pEditor->Client()->GlobalTime(); +} + +void CEditorMap::DeleteEnvelope(int Index) +{ + if(Index < 0 || Index >= (int)m_vpEnvelopes.size()) + return; + + OnModify(); + + VisitEnvelopeReferences([Index](int &ElementIndex) { + if(ElementIndex == Index) + ElementIndex = -1; + else if(ElementIndex > Index) + ElementIndex--; + }); + + m_vpEnvelopes.erase(m_vpEnvelopes.begin() + Index); +} + +void CEditorMap::SwapEnvelopes(int Index0, int Index1) +{ + if(Index0 < 0 || Index0 >= (int)m_vpEnvelopes.size()) + return; + if(Index1 < 0 || Index1 >= (int)m_vpEnvelopes.size()) + return; + if(Index0 == Index1) + return; + + OnModify(); + + VisitEnvelopeReferences([Index0, Index1](int &ElementIndex) { + if(ElementIndex == Index0) + ElementIndex = Index1; + else if(ElementIndex == Index1) + ElementIndex = Index0; + }); + + std::swap(m_vpEnvelopes[Index0], m_vpEnvelopes[Index1]); +} + +template +void CEditorMap::VisitEnvelopeReferences(F &&Visitor) +{ + for(auto &pGroup : m_vpGroups) + { + for(auto &pLayer : pGroup->m_vpLayers) + { + if(pLayer->m_Type == LAYERTYPE_QUADS) + { + std::shared_ptr pLayerQuads = std::static_pointer_cast(pLayer); + for(auto &Quad : pLayerQuads->m_vQuads) + { + Visitor(Quad.m_PosEnv); + Visitor(Quad.m_ColorEnv); + } + } + else if(pLayer->m_Type == LAYERTYPE_TILES) + { + std::shared_ptr pLayerTiles = std::static_pointer_cast(pLayer); + Visitor(pLayerTiles->m_ColorEnv); + } + else if(pLayer->m_Type == LAYERTYPE_SOUNDS) + { + std::shared_ptr pLayerSounds = std::static_pointer_cast(pLayer); + for(auto &Source : pLayerSounds->m_vSources) + { + Visitor(Source.m_PosEnv); + Visitor(Source.m_SoundEnv); + } + } + } + } +} + +void CEditorMap::MakeGameLayer(const std::shared_ptr &pLayer) +{ + m_pGameLayer = std::static_pointer_cast(pLayer); + m_pGameLayer->m_pEditor = m_pEditor; +} + +void CEditorMap::MakeGameGroup(std::shared_ptr pGroup) +{ + m_pGameGroup = std::move(pGroup); + m_pGameGroup->m_GameGroup = true; + str_copy(m_pGameGroup->m_aName, "Game"); +} + +void CEditorMap::Clean() +{ + m_vpGroups.clear(); + m_vpEnvelopes.clear(); + m_vpImages.clear(); + m_vpSounds.clear(); + + m_MapInfo.Reset(); + m_MapInfoTmp.Reset(); + + m_vSettings.clear(); + + m_pGameLayer = nullptr; + m_pGameGroup = nullptr; + + m_Modified = false; + m_ModifiedAuto = false; + + m_pTeleLayer = nullptr; + m_pSpeedupLayer = nullptr; + m_pFrontLayer = nullptr; + m_pSwitchLayer = nullptr; + m_pTuneLayer = nullptr; +} + +void CEditorMap::CreateDefault(IGraphics::CTextureHandle EntitiesTexture) +{ + // add background + std::shared_ptr pGroup = NewGroup(); + pGroup->m_ParallaxX = 0; + pGroup->m_ParallaxY = 0; + pGroup->m_CustomParallaxZoom = 0; + pGroup->m_ParallaxZoom = 0; + std::shared_ptr pLayer = std::make_shared(); + pLayer->m_pEditor = m_pEditor; + CQuad *pQuad = pLayer->NewQuad(0, 0, 1600, 1200); + pQuad->m_aColors[0].r = pQuad->m_aColors[1].r = 94; + pQuad->m_aColors[0].g = pQuad->m_aColors[1].g = 132; + pQuad->m_aColors[0].b = pQuad->m_aColors[1].b = 174; + pQuad->m_aColors[2].r = pQuad->m_aColors[3].r = 204; + pQuad->m_aColors[2].g = pQuad->m_aColors[3].g = 232; + pQuad->m_aColors[2].b = pQuad->m_aColors[3].b = 255; + pGroup->AddLayer(pLayer); + + // add game layer and reset front, tele, speedup, tune and switch layer pointers + MakeGameGroup(NewGroup()); + MakeGameLayer(std::make_shared(50, 50)); + m_pGameGroup->AddLayer(m_pGameLayer); + + m_pFrontLayer = nullptr; + m_pTeleLayer = nullptr; + m_pSpeedupLayer = nullptr; + m_pSwitchLayer = nullptr; + m_pTuneLayer = nullptr; +} + +void CEditorMap::MakeTeleLayer(const std::shared_ptr &pLayer) +{ + m_pTeleLayer = std::static_pointer_cast(pLayer); + m_pTeleLayer->m_pEditor = m_pEditor; +} + +void CEditorMap::MakeSpeedupLayer(const std::shared_ptr &pLayer) +{ + m_pSpeedupLayer = std::static_pointer_cast(pLayer); + m_pSpeedupLayer->m_pEditor = m_pEditor; +} + +void CEditorMap::MakeFrontLayer(const std::shared_ptr &pLayer) +{ + m_pFrontLayer = std::static_pointer_cast(pLayer); + m_pFrontLayer->m_pEditor = m_pEditor; +} + +void CEditorMap::MakeSwitchLayer(const std::shared_ptr &pLayer) +{ + m_pSwitchLayer = std::static_pointer_cast(pLayer); + m_pSwitchLayer->m_pEditor = m_pEditor; +} + +void CEditorMap::MakeTuneLayer(const std::shared_ptr &pLayer) +{ + m_pTuneLayer = std::static_pointer_cast(pLayer); + m_pTuneLayer->m_pEditor = m_pEditor; +} diff --git a/src/game/editor/mapitems/sound.cpp b/src/game/editor/mapitems/sound.cpp new file mode 100644 index 000000000..2ee5bb36e --- /dev/null +++ b/src/game/editor/mapitems/sound.cpp @@ -0,0 +1,10 @@ +#include + +#include + +CEditorSound::~CEditorSound() +{ + m_pEditor->Sound()->UnloadSample(m_SoundID); + free(m_pData); + m_pData = nullptr; +} From 946be50807c3bca6044749bda4b6514f295b24ba Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Sat, 12 Aug 2023 12:13:27 +0200 Subject: [PATCH 012/126] Add tileart tool to editor --- CMakeLists.txt | 1 + src/game/editor/editor.cpp | 2 +- src/game/editor/editor.h | 11 +- src/game/editor/popups.cpp | 41 +++++- src/game/editor/tileart.cpp | 264 ++++++++++++++++++++++++++++++++++++ 5 files changed, 316 insertions(+), 3 deletions(-) create mode 100644 src/game/editor/tileart.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e18f2634..53fed51b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2325,6 +2325,7 @@ if(CLIENT) proof_mode.h smooth_value.cpp smooth_value.h + tileart.cpp ) set(GAME_GENERATED_CLIENT src/game/generated/checksum.cpp diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index bff0bfd14..4283d79fb 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -7208,7 +7208,7 @@ void CEditor::RenderMenubar(CUIRect MenuBar) if(DoButton_Menu(&s_ToolsButton, "Tools", 0, &ToolsButton, 0, nullptr)) { static SPopupMenuId s_PopupMenuToolsId; - UI()->DoPopupMenu(&s_PopupMenuToolsId, ToolsButton.x, ToolsButton.y + ToolsButton.h - 1.0f, 200.0f, 50.0f, this, PopupMenuTools, PopupProperties); + UI()->DoPopupMenu(&s_PopupMenuToolsId, ToolsButton.x, ToolsButton.y + ToolsButton.h - 1.0f, 200.0f, 64.0f, this, PopupMenuTools, PopupProperties); } MenuBar.VSplitLeft(5.0f, nullptr, &MenuBar); diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 2773657b6..29ec69a34 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -1046,7 +1046,10 @@ public: POPEVENT_PREVENTUNUSEDTILES, POPEVENT_IMAGEDIV16, POPEVENT_IMAGE_MAX, - POPEVENT_PLACE_BORDER_TILES + POPEVENT_PLACE_BORDER_TILES, + POPEVENT_PIXELART_BIG_IMAGE, + POPEVENT_PIXELART_MANY_COLORS, + POPEVENT_PIXELART_TOO_MANY_COLORS }; int m_PopupEventType; @@ -1265,6 +1268,11 @@ public: CLineInputBuffered<256> m_SettingsCommandInput; + CImageInfo m_TileartImageInfo; + char m_aTileartFilename[IO_MAX_PATH_LENGTH]; + void AddTileart(); + void TileartCheckColors(); + void PlaceBorderTiles(); void UpdateTooltip(const void *pID, const CUIRect *pRect, const char *pToolTip); @@ -1327,6 +1335,7 @@ public: static bool CallbackAppendMap(const char *pFileName, int StorageType, void *pUser); static bool CallbackSaveMap(const char *pFileName, int StorageType, void *pUser); static bool CallbackSaveCopyMap(const char *pFileName, int StorageType, void *pUser); + static bool CallbackAddTileart(const char *pFilepath, int StorageType, void *pUser); void PopupSelectImageInvoke(int Current, float x, float y); int PopupSelectImageResult(); diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 0b9ac96b1..e175cdede 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -198,6 +198,15 @@ CUI::EPopupMenuFunctionResult CEditor::PopupMenuTools(void *pContext, CUIRect Vi pEditor->UI()->DoPopupMenu(&s_PopupGotoId, Slot.x, Slot.y + Slot.h, 120, 52, pEditor, PopupGoto); } + static int s_TileartButton = 0; + View.HSplitTop(2.0f, nullptr, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_TileartButton, "Add tileart", 0, &Slot, 0, "Generate tileart from image")) + { + pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add tileart", "Open", "mapres", false, CallbackAddTileart, pEditor); + return CUI::POPUP_CLOSE_CURRENT; + } + return CUI::POPUP_KEEP_OPEN; } @@ -1764,6 +1773,21 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, pTitle = "Place border tiles"; pMessage = "This is going to overwrite any existing tiles around the edges of the layer.\n\nContinue?"; } + else if(pEditor->m_PopupEventType == POPEVENT_PIXELART_BIG_IMAGE) + { + pTitle = "Big image"; + pMessage = "The selected image is big. Converting it to tileart may take some time.\n\nContinue anyway?"; + } + else if(pEditor->m_PopupEventType == POPEVENT_PIXELART_MANY_COLORS) + { + pTitle = "Many colors"; + pMessage = "The selected image contains many colors, which will lead to a big mapfile. You may want to consider reducing the number of colors.\n\nContinue anyway?"; + } + else if(pEditor->m_PopupEventType == POPEVENT_PIXELART_TOO_MANY_COLORS) + { + pTitle = "Too many colors"; + pMessage = "The client only supports 64 images but more would be needed to add the selected image as tileart."; + } else { dbg_assert(false, "m_PopupEventType invalid"); @@ -1786,12 +1810,19 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, // button bar ButtonBar.VSplitLeft(110.0f, &Button, &ButtonBar); - if(pEditor->m_PopupEventType != POPEVENT_LARGELAYER && pEditor->m_PopupEventType != POPEVENT_PREVENTUNUSEDTILES && pEditor->m_PopupEventType != POPEVENT_IMAGEDIV16 && pEditor->m_PopupEventType != POPEVENT_IMAGE_MAX) + if(pEditor->m_PopupEventType != POPEVENT_LARGELAYER && pEditor->m_PopupEventType != POPEVENT_PREVENTUNUSEDTILES && pEditor->m_PopupEventType != POPEVENT_IMAGEDIV16 && pEditor->m_PopupEventType != POPEVENT_IMAGE_MAX && pEditor->m_PopupEventType != POPEVENT_PIXELART_TOO_MANY_COLORS) { static int s_CancelButton = 0; if(pEditor->DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, nullptr)) { pEditor->m_PopupEventWasActivated = false; + + if(pEditor->m_PopupEventType == POPEVENT_PIXELART_BIG_IMAGE || pEditor->m_PopupEventType == POPEVENT_PIXELART_MANY_COLORS) + { + free(pEditor->m_TileartImageInfo.m_pData); + pEditor->m_TileartImageInfo.m_pData = nullptr; + } + return CUI::POPUP_CLOSE_CURRENT; } } @@ -1831,6 +1862,14 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, { pEditor->PlaceBorderTiles(); } + else if(pEditor->m_PopupEventType == POPEVENT_PIXELART_BIG_IMAGE) + { + pEditor->TileartCheckColors(); + } + else if(pEditor->m_PopupEventType == POPEVENT_PIXELART_MANY_COLORS) + { + pEditor->AddTileart(); + } pEditor->m_PopupEventWasActivated = false; return CUI::POPUP_CLOSE_CURRENT; } diff --git a/src/game/editor/tileart.cpp b/src/game/editor/tileart.cpp new file mode 100644 index 000000000..0f33c8f50 --- /dev/null +++ b/src/game/editor/tileart.cpp @@ -0,0 +1,264 @@ +#include "editor.h" + +#include + +bool operator<(const ColorRGBA &Left, const ColorRGBA &Right) +{ + if(Left.r != Right.r) + return Left.r < Right.r; + else if(Left.g != Right.g) + return Left.g < Right.g; + else if(Left.b != Right.b) + return Left.b < Right.b; + else + return Left.a < Right.a; +} + +static int GetNumColorChannels(const CImageInfo &Image) +{ + switch(Image.m_Format) + { + case CImageInfo::FORMAT_RGB: + return 3; + case CImageInfo::FORMAT_SINGLE_COMPONENT: + return 1; + default: + return 4; + } +} + +static ColorRGBA GetPixelColor(const CImageInfo &Image, int x, int y) +{ + uint8_t *pData = static_cast(Image.m_pData); + int NumColorChannels = GetNumColorChannels(Image); + int PixelStartIndex = x * NumColorChannels + (Image.m_Width * NumColorChannels * y); + + ColorRGBA Color = {255, 255, 255, 255}; + if(NumColorChannels == 1) + { + Color.a = pData[PixelStartIndex]; + } + else + { + Color.r = pData[PixelStartIndex + 0]; + Color.g = pData[PixelStartIndex + 1]; + Color.b = pData[PixelStartIndex + 2]; + + if(NumColorChannels == 4) + Color.a = pData[PixelStartIndex + 3]; + } + + return Color; +} + +static void SetPixelColor(CImageInfo *pImage, int x, int y, ColorRGBA Color) +{ + uint8_t *pData = static_cast(pImage->m_pData); + int NumColorChannels = GetNumColorChannels(*pImage); + int PixelStartIndex = x * NumColorChannels + (pImage->m_Width * NumColorChannels * y); + + if(NumColorChannels == 1) + { + pData[PixelStartIndex] = Color.a; + } + else + { + pData[PixelStartIndex + 0] = Color.r; + pData[PixelStartIndex + 1] = Color.g; + pData[PixelStartIndex + 2] = Color.b; + + if(NumColorChannels == 4) + pData[PixelStartIndex + 3] = Color.a; + } +} + +static std::vector GetUniqueColors(const CImageInfo &Image) +{ + std::set ColorSet; + std::vector vUniqueColors; + for(int x = 0; x < Image.m_Width; x++) + { + for(int y = 0; y < Image.m_Height; y++) + { + ColorRGBA Color = GetPixelColor(Image, x, y); + if(Color.a > 0 && ColorSet.insert(Color).second) + vUniqueColors.push_back(Color); + } + } + std::sort(vUniqueColors.begin(), vUniqueColors.end()); + + return vUniqueColors; +} + +constexpr int NumTilesRow = 16; +constexpr int NumTilesColumn = 16; +constexpr int NumTiles = NumTilesRow * NumTilesColumn; +constexpr int TileSize = 64; + +static int GetColorIndex(const std::array &ColorGroup, ColorRGBA Color) +{ + std::array::const_iterator Iterator = std::find(ColorGroup.begin(), ColorGroup.end(), Color); + if(Iterator == ColorGroup.end()) + return 0; + return Iterator - ColorGroup.begin(); +} + +static std::vector> GroupColors(const std::vector &vColors) +{ + std::vector> vaColorGroups; + + for(size_t i = 0; i < vColors.size(); i += NumTiles - 1) + { + auto &Group = vaColorGroups.emplace_back(); + std::copy_n(vColors.begin() + i, std::min(NumTiles - 1, vColors.size() - i), Group.begin() + 1); + } + + return vaColorGroups; +} + +static void SetColorTile(CImageInfo *pImage, int x, int y, ColorRGBA Color) +{ + for(int i = 0; i < TileSize; i++) + { + for(int j = 0; j < TileSize; j++) + SetPixelColor(pImage, x * TileSize + i, y * TileSize + j, Color); + } +} + +static CImageInfo ColorGroupToImage(const std::array &aColorGroup) +{ + CImageInfo Image; + Image.m_Width = NumTilesRow * TileSize; + Image.m_Height = NumTilesColumn * TileSize; + Image.m_Format = CImageInfo::FORMAT_RGBA; + + uint8_t *pData = static_cast(malloc(static_cast(Image.m_Width) * Image.m_Height * 4 * sizeof(uint8_t))); + Image.m_pData = pData; + + for(int y = 0; y < NumTilesColumn; y++) + { + for(int x = 0; x < NumTilesRow; x++) + { + int ColorIndex = x + NumTilesRow * y; + SetColorTile(&Image, x, y, aColorGroup[ColorIndex]); + } + } + + return Image; +} + +static std::vector ColorGroupsToImages(const std::vector> &vaColorGroups) +{ + std::vector vImages; + vImages.reserve(vaColorGroups.size()); + for(const auto &ColorGroup : vaColorGroups) + vImages.push_back(ColorGroupToImage(ColorGroup)); + + return vImages; +} + +static std::shared_ptr ImageInfoToEditorImage(CEditor *pEditor, const CImageInfo &Image, const char *pName) +{ + std::shared_ptr pEditorImage = std::make_shared(pEditor); + pEditorImage->m_Width = Image.m_Width; + pEditorImage->m_Height = Image.m_Height; + pEditorImage->m_Format = Image.m_Format; + pEditorImage->m_pData = Image.m_pData; + + int TextureLoadFlag = pEditor->Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; + pEditorImage->m_Texture = pEditor->Graphics()->LoadTextureRaw(Image.m_Width, Image.m_Height, Image.m_Format, Image.m_pData, CImageInfo::FORMAT_AUTO, TextureLoadFlag, pName); + pEditorImage->m_External = 0; + str_copy(pEditorImage->m_aName, pName); + + return pEditorImage; +} + +static std::shared_ptr AddLayerWithImage(CEditor *pEditor, const std::shared_ptr &pGroup, int Width, int Height, const CImageInfo &Image, const char *pName) +{ + std::shared_ptr pEditorImage = ImageInfoToEditorImage(pEditor, Image, pName); + pEditor->m_Map.m_vpImages.push_back(pEditorImage); + + std::shared_ptr pLayer = std::make_shared(Width, Height); + str_copy(pLayer->m_aName, pName); + pLayer->m_pEditor = pEditor; + pLayer->m_Image = pEditor->m_Map.m_vpImages.size() - 1; + pGroup->AddLayer(pLayer); + + return pLayer; +} + +static void SetTilelayerIndices(const std::shared_ptr &pLayer, const std::array &aColorGroup, const CImageInfo &Image) +{ + for(int x = 0; x < pLayer->m_Width; x++) + { + for(int y = 0; y < pLayer->m_Height; y++) + pLayer->m_pTiles[x + y * pLayer->m_Width].m_Index = GetColorIndex(aColorGroup, GetPixelColor(Image, x, y)); + } +} + +void CEditor::AddTileart() +{ + std::shared_ptr pGroup = m_Map.NewGroup(); + str_copy(pGroup->m_aName, m_aTileartFilename); + + auto vUniqueColors = GetUniqueColors(m_TileartImageInfo); + auto vaColorGroups = GroupColors(vUniqueColors); + auto vColorImages = ColorGroupsToImages(vaColorGroups); + char aImageName[IO_MAX_PATH_LENGTH]; + for(size_t i = 0; i < vColorImages.size(); i++) + { + str_format(aImageName, sizeof(aImageName), "%s %" PRIzu, m_aTileartFilename, i + 1); + std::shared_ptr pLayer = AddLayerWithImage(this, pGroup, m_TileartImageInfo.m_Width, m_TileartImageInfo.m_Height, vColorImages[i], aImageName); + SetTilelayerIndices(pLayer, vaColorGroups[i], m_TileartImageInfo); + } + SortImages(); + + free(m_TileartImageInfo.m_pData); + m_TileartImageInfo.m_pData = nullptr; + m_Map.OnModify(); + m_Dialog = DIALOG_NONE; +} + +void CEditor::TileartCheckColors() +{ + auto vUniqueColors = GetUniqueColors(m_TileartImageInfo); + int NumColorGroups = std::ceil(vUniqueColors.size() / 255.0f); + if(m_Map.m_vpImages.size() + NumColorGroups >= 64) + { + m_PopupEventType = CEditor::POPEVENT_PIXELART_TOO_MANY_COLORS; + m_PopupEventActivated = true; + free(m_TileartImageInfo.m_pData); + m_TileartImageInfo.m_pData = nullptr; + } + else if(NumColorGroups > 1) + { + m_PopupEventType = CEditor::POPEVENT_PIXELART_MANY_COLORS; + m_PopupEventActivated = true; + } + else + AddTileart(); +} + +bool CEditor::CallbackAddTileart(const char *pFilepath, int StorageType, void *pUser) +{ + CEditor *pEditor = (CEditor *)pUser; + + if(!pEditor->Graphics()->LoadPNG(&pEditor->m_TileartImageInfo, pFilepath, StorageType)) + { + pEditor->ShowFileDialogError("Failed to load image from file '%s'.", pFilepath); + return false; + } + + IStorage::StripPathAndExtension(pFilepath, pEditor->m_aTileartFilename, sizeof(pEditor->m_aTileartFilename)); + if(pEditor->m_TileartImageInfo.m_Width * pEditor->m_TileartImageInfo.m_Height > 10'000) + { + pEditor->m_PopupEventType = CEditor::POPEVENT_PIXELART_BIG_IMAGE; + pEditor->m_PopupEventActivated = true; + return false; + } + else + { + pEditor->TileartCheckColors(); + return false; + } +} From 330d1ebaf60cebe1aecdf90a25e80d6375bda4c6 Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Fri, 25 Aug 2023 14:52:37 +0200 Subject: [PATCH 013/126] Add possibility to persist game data past map changes --- src/engine/server.h | 9 +++++++-- src/engine/server/server.cpp | 10 ++++++---- src/engine/server/server.h | 1 + src/game/server/gamecontext.cpp | 8 ++++++-- src/game/server/gamecontext.h | 9 +++++++-- 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/engine/server.h b/src/engine/server.h index 9983e84b1..e3ae46f30 100644 --- a/src/engine/server.h +++ b/src/engine/server.h @@ -277,10 +277,14 @@ class IGameServer : public IInterface MACRO_INTERFACE("gameserver", 0) protected: public: - virtual void OnInit() = 0; + // `pPersistentData` may be null if this is the first time `IGameServer` + // is instantiated. + virtual void OnInit(const void *pPersistentData) = 0; virtual void OnConsoleInit() = 0; virtual void OnMapChange(char *pNewMapName, int MapNameSize) = 0; - virtual void OnShutdown() = 0; + // `pPersistentData` may be null if this is the last time `IGameServer` + // is destroyed. + virtual void OnShutdown(void *pPersistentData) = 0; virtual void OnTick() = 0; virtual void OnPreSnap() = 0; @@ -315,6 +319,7 @@ public: virtual bool IsClientReady(int ClientID) const = 0; virtual bool IsClientPlayer(int ClientID) const = 0; + virtual int PersistentDataSize() const = 0; virtual int PersistentClientDataSize() const = 0; virtual CUuid GameUuid() const = 0; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 99d3fc4d0..9bda9978d 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -368,6 +368,7 @@ CServer::~CServer() free(Client.m_pPersistentData); } } + free(m_pPersistentData); delete m_pRegister; delete m_pConnectionPool; @@ -2636,6 +2637,7 @@ int CServer::Run() Client.m_pPersistentData = malloc(Size); } } + m_pPersistentData = malloc(GameServer()->PersistentDataSize()); // load map if(!LoadMap(Config()->m_SvMap)) @@ -2704,7 +2706,7 @@ int CServer::Run() Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); Antibot()->Init(); - GameServer()->OnInit(); + GameServer()->OnInit(nullptr); if(ErrorShutdown()) { m_RunServer = STOPPING; @@ -2762,7 +2764,7 @@ int CServer::Run() } } - GameServer()->OnShutdown(); + GameServer()->OnShutdown(m_pPersistentData); for(int ClientID = 0; ClientID < MAX_CLIENTS; ClientID++) { @@ -2780,7 +2782,7 @@ int CServer::Run() m_CurrentGameTick = MIN_TICK; m_ServerInfoFirstRequest = 0; Kernel()->ReregisterInterface(GameServer()); - GameServer()->OnInit(); + GameServer()->OnInit(m_pPersistentData); if(ErrorShutdown()) { break; @@ -2989,7 +2991,7 @@ int CServer::Run() m_Fifo.Shutdown(); - GameServer()->OnShutdown(); + GameServer()->OnShutdown(nullptr); m_pMap->Unload(); DbPool()->OnShutdown(); diff --git a/src/engine/server/server.h b/src/engine/server/server.h index e1d915bea..3d0e3e873 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -248,6 +248,7 @@ public: int m_RconAuthLevel; int m_PrintCBIndex; char m_aShutdownReason[128]; + void *m_pPersistentData; enum { diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index f13c32519..4c1c75c08 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -3369,8 +3369,10 @@ void CGameContext::OnConsoleInit() #include } -void CGameContext::OnInit() +void CGameContext::OnInit(const void *pPersistentData) { + const CPersistentData *pPersistent = (const CPersistentData *)pPersistentData; + m_pServer = Kernel()->RequestInterface(); m_pConfig = Kernel()->RequestInterface()->Values(); m_pConsole = Kernel()->RequestInterface(); @@ -3813,8 +3815,10 @@ void CGameContext::OnMapChange(char *pNewMapName, int MapNameSize) str_copy(m_aDeleteTempfile, aTemp, sizeof(m_aDeleteTempfile)); } -void CGameContext::OnShutdown() +void CGameContext::OnShutdown(void *pPersistentData) { + CPersistentData *pPersistent = (CPersistentData *)pPersistentData; + Antibot()->RoundEnd(); if(m_TeeHistorianActive) diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index f8fd9d150..bbba67c20 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -139,6 +139,10 @@ class CGameContext : public IGameServer void AddVote(const char *pDescription, const char *pCommand); static int MapScan(const char *pName, int IsDir, int DirType, void *pUserData); + struct CPersistentData + { + }; + struct CPersistentClientData { bool m_IsSpectator; @@ -269,10 +273,10 @@ public: void LoadMapSettings(); // engine events - void OnInit() override; + void OnInit(const void *pPersistentData) override; void OnConsoleInit() override; void OnMapChange(char *pNewMapName, int MapNameSize) override; - void OnShutdown() override; + void OnShutdown(void *pPersistentData) override; void OnTick() override; void OnPreSnap() override; @@ -299,6 +303,7 @@ public: bool IsClientReady(int ClientID) const override; bool IsClientPlayer(int ClientID) const override; + int PersistentDataSize() const override { return sizeof(CPersistentData); } int PersistentClientDataSize() const override { return sizeof(CPersistentClientData); } CUuid GameUuid() const override; From cbdb3b1d2eceecfff96391d4d019a2a9b064b8f4 Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Fri, 25 Aug 2023 16:29:12 +0200 Subject: [PATCH 014/126] Record previous game ID in teehistorian This allows to trace a complete server execution. --- src/game/server/gamecontext.cpp | 16 ++++++++++++++++ src/game/server/gamecontext.h | 1 + src/game/server/teehistorian.cpp | 15 +++++++++++++++ src/game/server/teehistorian.h | 3 +++ src/test/teehistorian.cpp | 26 +++++++++++++++++++++++++- 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 4c1c75c08..144e4eaf7 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -3545,6 +3545,17 @@ void CGameContext::OnInit(const void *pPersistentData) GameInfo.m_MapSha256 = MapSha256; GameInfo.m_MapCrc = MapCrc; + if(pPersistent) + { + GameInfo.m_HavePrevGameUuid = true; + GameInfo.m_PrevGameUuid = pPersistent->m_PrevGameUuid; + } + else + { + GameInfo.m_HavePrevGameUuid = false; + mem_zero(&GameInfo.m_PrevGameUuid, sizeof(GameInfo.m_PrevGameUuid)); + } + m_TeeHistorian.Reset(&GameInfo, TeeHistorianWrite, this); for(int i = 0; i < MAX_CLIENTS; i++) @@ -3819,6 +3830,11 @@ void CGameContext::OnShutdown(void *pPersistentData) { CPersistentData *pPersistent = (CPersistentData *)pPersistentData; + if(pPersistent) + { + pPersistent->m_PrevGameUuid = m_GameUuid; + } + Antibot()->RoundEnd(); if(m_TeeHistorianActive) diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index bbba67c20..fa3f69512 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -141,6 +141,7 @@ class CGameContext : public IGameServer struct CPersistentData { + CUuid m_PrevGameUuid; }; struct CPersistentClientData diff --git a/src/game/server/teehistorian.cpp b/src/game/server/teehistorian.cpp index fd09aa046..d3cd863e3 100644 --- a/src/game/server/teehistorian.cpp +++ b/src/game/server/teehistorian.cpp @@ -1,5 +1,6 @@ #include "teehistorian.h" +#include #include #include #include @@ -81,6 +82,18 @@ void CTeeHistorian::WriteHeader(const CGameInfo *pGameInfo) str_timestamp_ex(pGameInfo->m_StartTime, aStartTime, sizeof(aStartTime), "%Y-%m-%dT%H:%M:%S%z"); sha256_str(pGameInfo->m_MapSha256, aMapSha256, sizeof(aMapSha256)); + char aPrevGameUuid[UUID_MAXSTRSIZE]; + char aPrevGameUuidJson[64]; + if(pGameInfo->m_HavePrevGameUuid) + { + FormatUuid(pGameInfo->m_PrevGameUuid, aPrevGameUuid, sizeof(aPrevGameUuid)); + str_format(aPrevGameUuidJson, sizeof(aPrevGameUuidJson), "\"%s\"", aPrevGameUuid); + } + else + { + str_copy(aPrevGameUuidJson, "null"); + } + char aCommentBuffer[128]; char aServerVersionBuffer[128]; char aStartTimeBuffer[128]; @@ -100,6 +113,7 @@ void CTeeHistorian::WriteHeader(const CGameInfo *pGameInfo) "\"version\":\"%s\"," "\"version_minor\":\"%s\"," "\"game_uuid\":\"%s\"," + "\"prev_game_uuid\":%s," "\"server_version\":\"%s\"," "\"start_time\":\"%s\"," "\"server_name\":\"%s\"," @@ -115,6 +129,7 @@ void CTeeHistorian::WriteHeader(const CGameInfo *pGameInfo) TEEHISTORIAN_VERSION, TEEHISTORIAN_VERSION_MINOR, aGameUuid, + aPrevGameUuidJson, E(aServerVersionBuffer, pGameInfo->m_pServerVersion), E(aStartTimeBuffer, aStartTime), E(aServerNameBuffer, pGameInfo->m_pServerName), diff --git a/src/game/server/teehistorian.h b/src/game/server/teehistorian.h index ffbfe3422..d4bc8d027 100644 --- a/src/game/server/teehistorian.h +++ b/src/game/server/teehistorian.h @@ -33,6 +33,9 @@ public: SHA256_DIGEST m_MapSha256; int m_MapCrc; + bool m_HavePrevGameUuid; + CUuid m_PrevGameUuid; + CConfig *m_pConfig; CTuningParams *m_pTuning; CUuidManager *m_pUuids; diff --git a/src/test/teehistorian.cpp b/src/test/teehistorian.cpp index 48caa680a..fcaad9c08 100644 --- a/src/test/teehistorian.cpp +++ b/src/test/teehistorian.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,9 @@ protected: m_GameInfo.m_MapSha256 = Sha256; m_GameInfo.m_MapCrc = 0xeceaf25c; + m_GameInfo.m_HavePrevGameUuid = false; + mem_zero(&m_GameInfo.m_PrevGameUuid, sizeof(m_GameInfo.m_PrevGameUuid)); + m_GameInfo.m_pConfig = &m_Config; m_GameInfo.m_pTuning = &m_Tuning; m_GameInfo.m_pUuids = &m_UuidManager; @@ -101,7 +105,7 @@ protected: void Expect(const unsigned char *pOutput, size_t OutputSize) { static CUuid TEEHISTORIAN_UUID = CalculateUuid("teehistorian@ddnet.tw"); - static const char PREFIX1[] = "{\"comment\":\"teehistorian@ddnet.tw\",\"version\":\"2\",\"version_minor\":\"5\",\"game_uuid\":\"a1eb7182-796e-3b3e-941d-38ca71b2a4a8\",\"server_version\":\"DDNet test\",\"start_time\":\""; + static const char PREFIX1[] = "{\"comment\":\"teehistorian@ddnet.tw\",\"version\":\"2\",\"version_minor\":\"5\",\"game_uuid\":\"a1eb7182-796e-3b3e-941d-38ca71b2a4a8\",\"prev_game_uuid\":null,\"server_version\":\"DDNet test\",\"start_time\":\""; static const char PREFIX2[] = "\",\"server_name\":\"server name\",\"server_port\":\"8303\",\"game_type\":\"game type\",\"map_name\":\"Kobra 3 Solo\",\"map_size\":\"903514\",\"map_sha256\":\"0123456789012345678901234567890123456789012345678901234567890123\",\"map_crc\":\"eceaf25c\",\"prng_description\":\"test-prng:02468ace\",\"config\":{},\"tuning\":{},\"uuids\":["; static const char PREFIX3[] = "]}"; @@ -833,3 +837,23 @@ TEST_F(TeeHistorian, AntibotEmptyMessage) m_TH.RecordAntibot("🤖", 4); Expect(EXPECTED, sizeof(EXPECTED)); } + +TEST_F(TeeHistorian, PrevGameUuid) +{ + m_GameInfo.m_HavePrevGameUuid = true; + CUuid PrevGameUuid = {{ + // fe19c218-f555-4002-a273-126c59ccc17a + 0xfe, 0x19, 0xc2, 0x18, 0xf5, 0x55, 0x40, 0x02, + 0xa2, 0x73, 0x12, 0x6c, 0x59, 0xcc, 0xc1, 0x7a, + // + }}; + m_GameInfo.m_PrevGameUuid = PrevGameUuid; + Reset(&m_GameInfo); + Finish(); + json_value *pJson = json_parse((const char *)m_vBuffer.data() + 16, -1); + ASSERT_TRUE(pJson); + const json_value &JsonPrevGameUuid = (*pJson)["prev_game_uuid"]; + ASSERT_EQ(JsonPrevGameUuid.type, json_string); + EXPECT_STREQ(JsonPrevGameUuid, "fe19c218-f555-4002-a273-126c59ccc17a"); + json_value_free(pJson); +} From a48d14fac0236cb18ed8b41b55519aa13ecdf7bc Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Sun, 27 Aug 2023 05:20:53 +0200 Subject: [PATCH 015/126] teehistorian: Omit `"prev_game_uuid"` instead of setting it to `null` --- src/game/server/teehistorian.cpp | 8 ++++---- src/test/teehistorian.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/game/server/teehistorian.cpp b/src/game/server/teehistorian.cpp index d3cd863e3..d0bc838db 100644 --- a/src/game/server/teehistorian.cpp +++ b/src/game/server/teehistorian.cpp @@ -9,7 +9,7 @@ static const char TEEHISTORIAN_NAME[] = "teehistorian@ddnet.tw"; static const CUuid TEEHISTORIAN_UUID = CalculateUuid(TEEHISTORIAN_NAME); static const char TEEHISTORIAN_VERSION[] = "2"; -static const char TEEHISTORIAN_VERSION_MINOR[] = "5"; +static const char TEEHISTORIAN_VERSION_MINOR[] = "6"; #define UUID(id, name) static const CUuid UUID_##id = CalculateUuid(name); #include @@ -87,11 +87,11 @@ void CTeeHistorian::WriteHeader(const CGameInfo *pGameInfo) if(pGameInfo->m_HavePrevGameUuid) { FormatUuid(pGameInfo->m_PrevGameUuid, aPrevGameUuid, sizeof(aPrevGameUuid)); - str_format(aPrevGameUuidJson, sizeof(aPrevGameUuidJson), "\"%s\"", aPrevGameUuid); + str_format(aPrevGameUuidJson, sizeof(aPrevGameUuidJson), "\"prev_game_uuid\":\"%s\",", aPrevGameUuid); } else { - str_copy(aPrevGameUuidJson, "null"); + aPrevGameUuidJson[0] = 0; } char aCommentBuffer[128]; @@ -113,7 +113,7 @@ void CTeeHistorian::WriteHeader(const CGameInfo *pGameInfo) "\"version\":\"%s\"," "\"version_minor\":\"%s\"," "\"game_uuid\":\"%s\"," - "\"prev_game_uuid\":%s," + "%s" "\"server_version\":\"%s\"," "\"start_time\":\"%s\"," "\"server_name\":\"%s\"," diff --git a/src/test/teehistorian.cpp b/src/test/teehistorian.cpp index fcaad9c08..9e88e7e0f 100644 --- a/src/test/teehistorian.cpp +++ b/src/test/teehistorian.cpp @@ -105,7 +105,7 @@ protected: void Expect(const unsigned char *pOutput, size_t OutputSize) { static CUuid TEEHISTORIAN_UUID = CalculateUuid("teehistorian@ddnet.tw"); - static const char PREFIX1[] = "{\"comment\":\"teehistorian@ddnet.tw\",\"version\":\"2\",\"version_minor\":\"5\",\"game_uuid\":\"a1eb7182-796e-3b3e-941d-38ca71b2a4a8\",\"prev_game_uuid\":null,\"server_version\":\"DDNet test\",\"start_time\":\""; + static const char PREFIX1[] = "{\"comment\":\"teehistorian@ddnet.tw\",\"version\":\"2\",\"version_minor\":\"6\",\"game_uuid\":\"a1eb7182-796e-3b3e-941d-38ca71b2a4a8\",\"server_version\":\"DDNet test\",\"start_time\":\""; static const char PREFIX2[] = "\",\"server_name\":\"server name\",\"server_port\":\"8303\",\"game_type\":\"game type\",\"map_name\":\"Kobra 3 Solo\",\"map_size\":\"903514\",\"map_sha256\":\"0123456789012345678901234567890123456789012345678901234567890123\",\"map_crc\":\"eceaf25c\",\"prng_description\":\"test-prng:02468ace\",\"config\":{},\"tuning\":{},\"uuids\":["; static const char PREFIX3[] = "]}"; From 70bf739b6f14dec39b227d8f470bbb456134b738 Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Sun, 27 Aug 2023 17:35:13 +0200 Subject: [PATCH 016/126] Notify antibot of round start after initializing teehistorian This allows the antibot to start pushing data into teehistorian immediately. --- src/game/server/gamecontext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index f13c32519..b7f6d810c 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -3377,7 +3377,6 @@ void CGameContext::OnInit() m_pEngine = Kernel()->RequestInterface(); m_pStorage = Kernel()->RequestInterface(); m_pAntibot = Kernel()->RequestInterface(); - m_pAntibot->RoundStart(this); m_World.SetGameServer(this); m_Events.SetGameServer(this); @@ -3566,6 +3565,8 @@ void CGameContext::OnInit() if(GIT_SHORTREV_HASH) Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "git-revision", GIT_SHORTREV_HASH); + m_pAntibot->RoundStart(this); + #ifdef CONF_DEBUG if(g_Config.m_DbgDummies) { From 2b07832b9ea9c9db9480a45388255e9afac0a092 Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 27 Aug 2023 21:23:55 +0200 Subject: [PATCH 017/126] Don't register to ipv6 when ipv4only is enabled. --- src/engine/server/register.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/server/register.cpp b/src/engine/server/register.cpp index b5f8f9362..4da5627b1 100644 --- a/src/engine/server/register.cpp +++ b/src/engine/server/register.cpp @@ -257,6 +257,9 @@ void CRegister::ConchainOnConfigChange(IConsole::IResult *pResult, void *pUserDa void CRegister::CProtocol::SendRegister() { + if(g_Config.m_SvIpv4Only && (m_Protocol == PROTOCOL_TW6_IPV6 || m_Protocol == PROTOCOL_TW7_IPV6) ) + return; + int64_t Now = time_get(); int64_t Freq = time_freq(); From 8f73a9ea8cb9dc50e47b80ca45e29fcd63fc14da Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 27 Aug 2023 21:40:32 +0200 Subject: [PATCH 018/126] Fix style --- src/engine/server/register.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/server/register.cpp b/src/engine/server/register.cpp index 4da5627b1..cd2732621 100644 --- a/src/engine/server/register.cpp +++ b/src/engine/server/register.cpp @@ -257,7 +257,7 @@ void CRegister::ConchainOnConfigChange(IConsole::IResult *pResult, void *pUserDa void CRegister::CProtocol::SendRegister() { - if(g_Config.m_SvIpv4Only && (m_Protocol == PROTOCOL_TW6_IPV6 || m_Protocol == PROTOCOL_TW7_IPV6) ) + if(g_Config.m_SvIpv4Only && (m_Protocol == PROTOCOL_TW6_IPV6 || m_Protocol == PROTOCOL_TW7_IPV6)) return; int64_t Now = time_get(); From 6705cccb7244a32673cf264efae565f7f256ce53 Mon Sep 17 00:00:00 2001 From: Ravie <65019210+HiRavie@users.noreply.github.com> Date: Mon, 28 Aug 2023 16:19:41 +0200 Subject: [PATCH 019/126] Fix all envelopes being saved as bezier --- src/game/editor/io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 836776a4d..c3537b5d2 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -364,7 +364,7 @@ bool CEditorMap::Save(const char *pFileName) // save points m_pEditor->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "editor", "saving envelope points"); - bool BezierUsed = true; + bool BezierUsed = false; for(const auto &pEnvelope : m_vpEnvelopes) { for(const auto &Point : pEnvelope->m_vPoints) From 2dcc0b9db3829860996c062f3adb65251a44a6eb Mon Sep 17 00:00:00 2001 From: furo Date: Mon, 28 Aug 2023 21:11:12 +0200 Subject: [PATCH 020/126] Fix demo sorting --- src/game/client/components/menus_demo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 776cc958d..9f110c8a6 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -1182,7 +1182,7 @@ void CMenus::RenderDemoList(CUIRect MainView) } // Don't rescan in order to keep fetched headers, just resort - std::stable_sort(m_vpFilteredDemos.begin(), m_vpFilteredDemos.end()); + std::stable_sort(m_vDemos.begin(), m_vDemos.end()); DemolistOnUpdate(false); } } From 28fda865c56eac2742f8d037a0a20644eb6d373c Mon Sep 17 00:00:00 2001 From: furo Date: Mon, 28 Aug 2023 21:47:48 +0200 Subject: [PATCH 021/126] Keep selection after sort --- src/game/client/components/menus_demo.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 9f110c8a6..661531eec 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -913,6 +913,8 @@ void CMenus::DemolistOnUpdate(bool Reset) { bool Found = false; int SelectedIndex = -1; + RefreshFilteredDemos(); + // search for selected index for(auto &Item : m_vpFilteredDemos) { @@ -924,7 +926,6 @@ void CMenus::DemolistOnUpdate(bool Reset) break; } } - RefreshFilteredDemos(); if(Found) m_DemolistSelectedIndex = SelectedIndex; From b24b11f48e22b1dddd50f47f3557c0ab8987ff28 Mon Sep 17 00:00:00 2001 From: furo Date: Mon, 28 Aug 2023 22:45:53 +0200 Subject: [PATCH 022/126] Move sv_ipv4only check to OnConfigChange --- src/engine/server/register.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/server/register.cpp b/src/engine/server/register.cpp index cd2732621..74aaf7df5 100644 --- a/src/engine/server/register.cpp +++ b/src/engine/server/register.cpp @@ -257,9 +257,6 @@ void CRegister::ConchainOnConfigChange(IConsole::IResult *pResult, void *pUserDa void CRegister::CProtocol::SendRegister() { - if(g_Config.m_SvIpv4Only && (m_Protocol == PROTOCOL_TW6_IPV6 || m_Protocol == PROTOCOL_TW7_IPV6)) - return; - int64_t Now = time_get(); int64_t Freq = time_freq(); @@ -603,6 +600,11 @@ void CRegister::OnConfigChange() m_aProtocolEnabled[PROTOCOL_TW7_IPV6] = false; m_aProtocolEnabled[PROTOCOL_TW7_IPV4] = false; } + if(m_pConfig->m_SvIpv4Only) + { + m_aProtocolEnabled[PROTOCOL_TW6_IPV6] = false; + m_aProtocolEnabled[PROTOCOL_TW7_IPV6] = false; + } m_NumExtraHeaders = 0; const char *pRegisterExtra = m_pConfig->m_SvRegisterExtra; char aHeader[128]; From d7d635881b1defd91e3996a757b8080f06c45f7e Mon Sep 17 00:00:00 2001 From: Samuele Radici <68398653+k-i-o@users.noreply.github.com> Date: Tue, 29 Aug 2023 10:09:55 +0000 Subject: [PATCH 023/126] I found out he didn't save himself... --- src/game/client/components/camera.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp index 2a74b1b56..a8bb07030 100644 --- a/src/game/client/components/camera.cpp +++ b/src/game/client/components/camera.cpp @@ -193,7 +193,7 @@ void CCamera::OnConsoleInit() void CCamera::OnReset() { - m_Zoom = std::pow(ZoomStep, g_Config.m_ClDefaultZoom - 10); + m_Zoom = std::pow(CCamera::ZOOM_STEP, g_Config.m_ClDefaultZoom - 10); m_Zooming = false; } @@ -202,7 +202,7 @@ void CCamera::ConZoomPlus(IConsole::IResult *pResult, void *pUserData) CCamera *pSelf = (CCamera *)pUserData; if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || pSelf->GameClient()->m_GameInfo.m_AllowZoom || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK) { - pSelf->ScaleZoom(ZoomStep); + pSelf->ScaleZoom(CCamera::ZOOM_STEP); if(pSelf->GameClient()->m_MultiViewActivated) pSelf->GameClient()->m_MultiViewPersonalZoom++; @@ -213,7 +213,7 @@ void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData) CCamera *pSelf = (CCamera *)pUserData; if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || pSelf->GameClient()->m_GameInfo.m_AllowZoom || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK) { - pSelf->ScaleZoom(1 / ZoomStep); + pSelf->ScaleZoom(1 / CCamera::ZOOM_STEP); if(pSelf->GameClient()->m_MultiViewActivated) pSelf->GameClient()->m_MultiViewPersonalZoom--; @@ -223,7 +223,7 @@ void CCamera::ConZoom(IConsole::IResult *pResult, void *pUserData) { CCamera *pSelf = (CCamera *)pUserData; float TargetLevel = pResult->NumArguments() ? pResult->GetFloat(0) : g_Config.m_ClDefaultZoom; - pSelf->ChangeZoom(std::pow(ZoomStep, TargetLevel - 10), pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active && pSelf->GameClient()->m_MultiViewActivated ? g_Config.m_ClMultiViewZoomSmoothness : g_Config.m_ClSmoothZoomTime); + pSelf->ChangeZoom(std::pow(CCamera::ZOOM_STEP, TargetLevel - 10), pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active && pSelf->GameClient()->m_MultiViewActivated ? g_Config.m_ClMultiViewZoomSmoothness : g_Config.m_ClSmoothZoomTime); if(pSelf->GameClient()->m_MultiViewActivated) pSelf->GameClient()->m_MultiViewPersonalZoom = 0; From 65347287440a21446f8df41af3eb8e3f3ec6563b Mon Sep 17 00:00:00 2001 From: Samuele Radici <68398653+k-i-o@users.noreply.github.com> Date: Tue, 29 Aug 2023 11:01:58 +0000 Subject: [PATCH 024/126] fix style --- src/game/client/components/camera.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp index a8bb07030..18275c48d 100644 --- a/src/game/client/components/camera.cpp +++ b/src/game/client/components/camera.cpp @@ -12,7 +12,6 @@ #include "controls.h" #include - CCamera::CCamera() { m_CamType = CAMTYPE_UNDEFINED; From 73f68c73f46b1e3dfb1351fa13742c16d50bf12f Mon Sep 17 00:00:00 2001 From: Samuele Radici <68398653+k-i-o@users.noreply.github.com> Date: Tue, 29 Aug 2023 13:52:31 +0000 Subject: [PATCH 025/126] done i hope --- src/game/client/gameclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 19319b0de..4a7dc0b10 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -3742,7 +3742,7 @@ float CGameClient::MapValue(float MaxValue, float MinValue, float MaxRange, floa void CGameClient::ResetMultiView() { - m_Camera.SetZoom(std::pow(0.866025f, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); + m_Camera.SetZoom(std::pow(CCamera::ZOOM_STEP, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); m_MultiViewPersonalZoom = 0; m_MultiViewActivated = false; m_MultiView.m_Solo = false; From b7773d2b4c2642345b9da2acff90521f0bbcd03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 29 Aug 2023 18:01:09 +0200 Subject: [PATCH 026/126] Fix lineinput selection getting desynchronized from cursor When the text cursor/selection mode is set to calculate, values of `-1` are used when the selection is empty. These values were not being handled anymore due to a regression from #7028. This was causing the selection to be set to the last position instead, which was causing text to subsequently be inserted there instead of at the cursor position. An assertion is added to ensure that the selection cannot be desynchronized from the cursor position anymore. Closes #7099. --- src/game/client/lineinput.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp index 9f7d3e050..ac44c8574 100644 --- a/src/game/client/lineinput.cpp +++ b/src/game/client/lineinput.cpp @@ -159,6 +159,7 @@ void CLineInput::SetCursorOffset(size_t Offset) void CLineInput::SetSelection(size_t Start, size_t End) { + dbg_assert(m_CursorPos == Start || m_CursorPos == End, "Selection and cursor offset got desynchronized"); if(Start > End) std::swap(Start, End); m_SelectionStart = clamp(Start, 0, m_Len); @@ -472,12 +473,12 @@ STextBoundingBox CLineInput::Render(const CUIRect *pRect, float FontSize, int Al TextRender()->TextEx(&Cursor, pDisplayStr); } - if(Cursor.m_CursorMode == TEXT_CURSOR_CURSOR_MODE_CALCULATE) + if(Cursor.m_CursorMode == TEXT_CURSOR_CURSOR_MODE_CALCULATE && Cursor.m_CursorCharacter >= 0) { const size_t NewCursorOffset = str_utf8_offset_chars_to_bytes(pDisplayStr, Cursor.m_CursorCharacter); SetCursorOffset(OffsetFromDisplayToActual(NewCursorOffset)); } - if(Cursor.m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_CALCULATE) + if(Cursor.m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_CALCULATE && Cursor.m_SelectionStart >= 0 && Cursor.m_SelectionEnd >= 0) { const size_t NewSelectionStart = str_utf8_offset_chars_to_bytes(pDisplayStr, Cursor.m_SelectionStart); const size_t NewSelectionEnd = str_utf8_offset_chars_to_bytes(pDisplayStr, Cursor.m_SelectionEnd); From 95b1c7dc2b7aa3b1cb42c8a2ef70f3d0d56be76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 29 Aug 2023 22:05:12 +0200 Subject: [PATCH 027/126] Hide `client is not online` log message except when using `record` The log message is otherwise shown multiple times when starting the client. Now it's only shown when the `record` command is used manually, i.e. not for automatically recorded demos anymore. --- src/engine/client.h | 2 +- src/engine/client/client.cpp | 11 +++++++---- src/engine/client/client.h | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/engine/client.h b/src/engine/client.h index 33a8d2cc4..7d75fea3a 100644 --- a/src/engine/client.h +++ b/src/engine/client.h @@ -167,7 +167,7 @@ public: #if defined(CONF_VIDEORECORDER) virtual const char *DemoPlayer_Render(const char *pFilename, int StorageType, const char *pVideoName, int SpeedIndex, bool StartPaused = false) = 0; #endif - virtual void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder) = 0; + virtual void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose = false) = 0; virtual void DemoRecorder_HandleAutoStart() = 0; virtual void DemoRecorder_Stop(int Recorder, bool RemoveFile = false) = 0; virtual class IDemoRecorder *DemoRecorder(int Recorder) = 0; diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 1d940f381..9470c65a8 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -3957,10 +3957,13 @@ void CClient::Con_DemoSpeed(IConsole::IResult *pResult, void *pUserData) pSelf->m_DemoPlayer.SetSpeed(pResult->GetFloat(0)); } -void CClient::DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder) +void CClient::DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose) { if(State() != IClient::STATE_ONLINE) - m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demorec/record", "client is not online"); + { + if(Verbose) + m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demorec/record", "client is not online"); + } else { char aFilename[IO_MAX_PATH_LENGTH]; @@ -4038,9 +4041,9 @@ void CClient::Con_Record(IConsole::IResult *pResult, void *pUserData) { CClient *pSelf = (CClient *)pUserData; if(pResult->NumArguments()) - pSelf->DemoRecorder_Start(pResult->GetString(0), false, RECORDER_MANUAL); + pSelf->DemoRecorder_Start(pResult->GetString(0), false, RECORDER_MANUAL, true); else - pSelf->DemoRecorder_Start(pSelf->m_aCurrentMap, true, RECORDER_MANUAL); + pSelf->DemoRecorder_Start(pSelf->m_aCurrentMap, true, RECORDER_MANUAL, true); } void CClient::Con_StopRecord(IConsole::IResult *pResult, void *pUserData) diff --git a/src/engine/client/client.h b/src/engine/client/client.h index a57dd5fce..6578e5ae3 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -482,7 +482,7 @@ public: void RegisterCommands(); const char *DemoPlayer_Play(const char *pFilename, int StorageType) override; - void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder) override; + void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose = false) override; void DemoRecorder_HandleAutoStart() override; void DemoRecorder_StartReplayRecorder(); void DemoRecorder_Stop(int Recorder, bool RemoveFile = false) override; From a1ea2f1ff1ecfd7a955632a01a9c6f5aa5c611e9 Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Mon, 28 Aug 2023 17:47:19 +0200 Subject: [PATCH 028/126] Split up io.cpp --- CMakeLists.txt | 2 +- src/game/editor/editor.cpp | 93 ++++++++++++++ src/game/editor/mapitems/map.cpp | 15 +++ .../editor/{io.cpp => mapitems/map_io.cpp} | 113 +----------------- 4 files changed, 111 insertions(+), 112 deletions(-) rename src/game/editor/{io.cpp => mapitems/map_io.cpp} (92%) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd51b267c..8f364d3b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2311,7 +2311,6 @@ if(CLIENT) editor.cpp editor.h explanations.cpp - io.cpp map_grid.cpp map_grid.h map_view.cpp @@ -2328,6 +2327,7 @@ if(CLIENT) mapitems/layer_tiles.cpp mapitems/layer_tune.cpp mapitems/map.cpp + mapitems/map_io.cpp mapitems/sound.cpp popups.cpp proof_mode.cpp diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 05d53c0cb..e54f79659 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -7824,4 +7824,97 @@ void CEditor::LoadCurrentMap() MapView()->SetWorldOffset(Center); } +bool CEditor::Save(const char *pFilename) +{ + // Check if file with this name is already being saved at the moment + if(std::any_of(std::begin(m_WriterFinishJobs), std::end(m_WriterFinishJobs), [pFilename](const std::shared_ptr &Job) { return str_comp(pFilename, Job->GetRealFileName()) == 0; })) + return false; + + return m_Map.Save(pFilename); +} + +bool CEditor::HandleMapDrop(const char *pFileName, int StorageType) +{ + return m_Map.HandleMapDrop(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); +} + +bool CEditor::Load(const char *pFileName, int StorageType) +{ + const auto &&ErrorHandler = [this](const char *pErrorMessage) { + ShowFileDialogError("%s", pErrorMessage); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "editor/load", pErrorMessage); + }; + + Reset(); + bool Result = m_Map.Load(pFileName, StorageType, std::move(ErrorHandler)); + if(Result) + { + str_copy(m_aFileName, pFileName); + SortImages(); + SelectGameLayer(); + MapView()->OnMapLoad(); + } + else + { + m_aFileName[0] = 0; + Reset(); + } + return Result; +} + +bool CEditor::Append(const char *pFileName, int StorageType) +{ + CEditorMap NewMap; + NewMap.m_pEditor = this; + + const auto &&ErrorHandler = [this](const char *pErrorMessage) { + ShowFileDialogError("%s", pErrorMessage); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "editor/append", pErrorMessage); + }; + if(!NewMap.Load(pFileName, StorageType, std::move(ErrorHandler))) + return false; + + // modify indices + static const auto &&s_ModifyAddIndex = [](int AddAmount) { + return [AddAmount](int *pIndex) { + if(*pIndex >= 0) + *pIndex += AddAmount; + }; + }; + NewMap.ModifyImageIndex(s_ModifyAddIndex(m_Map.m_vpImages.size())); + NewMap.ModifySoundIndex(s_ModifyAddIndex(m_Map.m_vpSounds.size())); + NewMap.ModifyEnvelopeIndex(s_ModifyAddIndex(m_Map.m_vpEnvelopes.size())); + + // transfer images + for(const auto &pImage : NewMap.m_vpImages) + m_Map.m_vpImages.push_back(pImage); + NewMap.m_vpImages.clear(); + + // transfer sounds + for(const auto &pSound : NewMap.m_vpSounds) + m_Map.m_vpSounds.push_back(pSound); + NewMap.m_vpSounds.clear(); + + // transfer envelopes + for(const auto &pEnvelope : NewMap.m_vpEnvelopes) + m_Map.m_vpEnvelopes.push_back(pEnvelope); + NewMap.m_vpEnvelopes.clear(); + + // transfer groups + for(const auto &pGroup : NewMap.m_vpGroups) + { + if(pGroup != NewMap.m_pGameGroup) + { + pGroup->m_pMap = &m_Map; + m_Map.m_vpGroups.push_back(pGroup); + } + } + NewMap.m_vpGroups.clear(); + + SortImages(); + + // all done \o/ + return true; +} + IEditor *CreateEditor() { return new CEditor; } diff --git a/src/game/editor/mapitems/map.cpp b/src/game/editor/mapitems/map.cpp index 29e683223..5bc5338bc 100644 --- a/src/game/editor/mapitems/map.cpp +++ b/src/game/editor/mapitems/map.cpp @@ -177,3 +177,18 @@ void CEditorMap::MakeTuneLayer(const std::shared_ptr &pLayer) m_pTuneLayer = std::static_pointer_cast(pLayer); m_pTuneLayer->m_pEditor = m_pEditor; } + +bool CEditorMap::HandleMapDrop(const char *pFileName, int StorageType) +{ + if(m_pEditor->HasUnsavedData()) + { + str_copy(m_pEditor->m_aFileNamePending, pFileName); + m_pEditor->m_PopupEventType = CEditor::POPEVENT_LOADDROP; + m_pEditor->m_PopupEventActivated = true; + return true; + } + else + { + return m_pEditor->Load(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); + } +} diff --git a/src/game/editor/io.cpp b/src/game/editor/mapitems/map_io.cpp similarity index 92% rename from src/game/editor/io.cpp rename to src/game/editor/mapitems/map_io.cpp index 503c97a48..f0962f913 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/mapitems/map_io.cpp @@ -1,6 +1,5 @@ -/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ -/* If you are missing that file, acquire a complete release at teeworlds.com. */ -#include "editor.h" +#include + #include #include #include @@ -31,15 +30,6 @@ struct CSoundSource_DEPRECATED int m_SoundEnvOffset; }; -bool CEditor::Save(const char *pFilename) -{ - // Check if file with this name is already being saved at the moment - if(std::any_of(std::begin(m_WriterFinishJobs), std::end(m_WriterFinishJobs), [pFilename](const std::shared_ptr &Job) { return str_comp(pFilename, Job->GetRealFileName()) == 0; })) - return false; - - return m_Map.Save(pFilename); -} - bool CEditorMap::Save(const char *pFileName) { char aFileNameTmp[IO_MAX_PATH_LENGTH]; @@ -426,50 +416,6 @@ bool CEditorMap::Save(const char *pFileName) return true; } -bool CEditor::HandleMapDrop(const char *pFileName, int StorageType) -{ - return m_Map.HandleMapDrop(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); -} - -bool CEditorMap::HandleMapDrop(const char *pFileName, int StorageType) -{ - if(m_pEditor->HasUnsavedData()) - { - str_copy(m_pEditor->m_aFileNamePending, pFileName); - m_pEditor->m_PopupEventType = CEditor::POPEVENT_LOADDROP; - m_pEditor->m_PopupEventActivated = true; - return true; - } - else - { - return m_pEditor->Load(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); - } -} - -bool CEditor::Load(const char *pFileName, int StorageType) -{ - const auto &&ErrorHandler = [this](const char *pErrorMessage) { - ShowFileDialogError("%s", pErrorMessage); - Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "editor/load", pErrorMessage); - }; - - Reset(); - bool Result = m_Map.Load(pFileName, StorageType, std::move(ErrorHandler)); - if(Result) - { - str_copy(m_aFileName, pFileName); - SortImages(); - SelectGameLayer(); - MapView()->OnMapLoad(); - } - else - { - m_aFileName[0] = 0; - Reset(); - } - return Result; -} - bool CEditorMap::Load(const char *pFileName, int StorageType, const std::function &ErrorHandler) { CDataFileReader DataFile; @@ -1064,58 +1010,3 @@ void CEditorMap::PerformSanityChecks(const std::functionPrint(IConsole::OUTPUT_LEVEL_STANDARD, "editor/append", pErrorMessage); - }; - if(!NewMap.Load(pFileName, StorageType, std::move(ErrorHandler))) - return false; - - // modify indices - static const auto &&s_ModifyAddIndex = [](int AddAmount) { - return [AddAmount](int *pIndex) { - if(*pIndex >= 0) - *pIndex += AddAmount; - }; - }; - NewMap.ModifyImageIndex(s_ModifyAddIndex(m_Map.m_vpImages.size())); - NewMap.ModifySoundIndex(s_ModifyAddIndex(m_Map.m_vpSounds.size())); - NewMap.ModifyEnvelopeIndex(s_ModifyAddIndex(m_Map.m_vpEnvelopes.size())); - - // transfer images - for(const auto &pImage : NewMap.m_vpImages) - m_Map.m_vpImages.push_back(pImage); - NewMap.m_vpImages.clear(); - - // transfer sounds - for(const auto &pSound : NewMap.m_vpSounds) - m_Map.m_vpSounds.push_back(pSound); - NewMap.m_vpSounds.clear(); - - // transfer envelopes - for(const auto &pEnvelope : NewMap.m_vpEnvelopes) - m_Map.m_vpEnvelopes.push_back(pEnvelope); - NewMap.m_vpEnvelopes.clear(); - - // transfer groups - for(const auto &pGroup : NewMap.m_vpGroups) - { - if(pGroup != NewMap.m_pGameGroup) - { - pGroup->m_pMap = &m_Map; - m_Map.m_vpGroups.push_back(pGroup); - } - } - NewMap.m_vpGroups.clear(); - - SortImages(); - - // all done \o/ - return true; -} From 5f60d68e8bed35284e2d7f1ad259b0eecf137372 Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Mon, 28 Aug 2023 17:55:29 +0200 Subject: [PATCH 029/126] extract `CEditorImage` into separate header --- CMakeLists.txt | 1 + src/game/editor/editor.cpp | 2 ++ src/game/editor/editor.h | 29 +----------------- src/game/editor/mapitems/image.cpp | 2 ++ src/game/editor/mapitems/image.h | 39 ++++++++++++++++++++++++ src/game/editor/mapitems/layer_quads.cpp | 2 ++ src/game/editor/mapitems/layer_tiles.cpp | 2 ++ src/game/editor/mapitems/map.cpp | 2 ++ src/game/editor/mapitems/map_io.cpp | 2 ++ src/game/editor/popups.cpp | 1 + src/game/editor/tileart.cpp | 2 ++ 11 files changed, 56 insertions(+), 28 deletions(-) create mode 100644 src/game/editor/mapitems/image.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f364d3b5..bf3edad33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2316,6 +2316,7 @@ if(CLIENT) map_view.cpp map_view.h mapitems/image.cpp + mapitems/image.h mapitems/layer_front.cpp mapitems/layer_game.cpp mapitems/layer_group.cpp diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index e54f79659..6a5dd13c2 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -31,6 +31,8 @@ #include #include +#include + #include "auto_map.h" #include "editor.h" diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index f1b382f90..a43b4957a 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -329,34 +329,7 @@ public: } }; -class CEditorImage : public CImageInfo -{ -public: - CEditor *m_pEditor; - - CEditorImage(CEditor *pEditor) : - m_AutoMapper(pEditor) - { - m_pEditor = pEditor; - m_aName[0] = 0; - m_Texture.Invalidate(); - m_External = 0; - m_Width = 0; - m_Height = 0; - m_pData = nullptr; - m_Format = 0; - } - - ~CEditorImage(); - - void AnalyseTileFlags(); - - IGraphics::CTextureHandle m_Texture; - int m_External; - char m_aName[IO_MAX_PATH_LENGTH]; - unsigned char m_aTileFlags[256]; - class CAutoMapper m_AutoMapper; -}; +class CEditorImage; class CEditorSound { diff --git a/src/game/editor/mapitems/image.cpp b/src/game/editor/mapitems/image.cpp index a29e5371d..365c6687d 100644 --- a/src/game/editor/mapitems/image.cpp +++ b/src/game/editor/mapitems/image.cpp @@ -1,3 +1,5 @@ +#include "image.h" + #include CEditorImage::~CEditorImage() diff --git a/src/game/editor/mapitems/image.h b/src/game/editor/mapitems/image.h new file mode 100644 index 000000000..77a845a87 --- /dev/null +++ b/src/game/editor/mapitems/image.h @@ -0,0 +1,39 @@ +#ifndef GAME_EDITOR_MAPITEMS_IMAGE_H +#define GAME_EDITOR_MAPITEMS_IMAGE_H + +#include + +#include + +class CEditor; + +class CEditorImage : public CImageInfo +{ +public: + CEditor *m_pEditor; + + CEditorImage(CEditor *pEditor) : + m_AutoMapper(pEditor) + { + m_pEditor = pEditor; + m_aName[0] = 0; + m_Texture.Invalidate(); + m_External = 0; + m_Width = 0; + m_Height = 0; + m_pData = nullptr; + m_Format = 0; + } + + ~CEditorImage(); + + void AnalyseTileFlags(); + + IGraphics::CTextureHandle m_Texture; + int m_External; + char m_aName[IO_MAX_PATH_LENGTH]; + unsigned char m_aTileFlags[256]; + class CAutoMapper m_AutoMapper; +}; + +#endif diff --git a/src/game/editor/mapitems/layer_quads.cpp b/src/game/editor/mapitems/layer_quads.cpp index 485ba9327..b16eeba4a 100644 --- a/src/game/editor/mapitems/layer_quads.cpp +++ b/src/game/editor/mapitems/layer_quads.cpp @@ -2,6 +2,8 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include +#include "image.h" + CLayerQuads::CLayerQuads() { m_Type = LAYERTYPE_QUADS; diff --git a/src/game/editor/mapitems/layer_tiles.cpp b/src/game/editor/mapitems/layer_tiles.cpp index bcd8bc0b5..bf722c109 100644 --- a/src/game/editor/mapitems/layer_tiles.cpp +++ b/src/game/editor/mapitems/layer_tiles.cpp @@ -5,6 +5,8 @@ #include #include +#include "image.h" + CLayerTiles::CLayerTiles(int w, int h) { m_Type = LAYERTYPE_TILES; diff --git a/src/game/editor/mapitems/map.cpp b/src/game/editor/mapitems/map.cpp index 5bc5338bc..996d081e2 100644 --- a/src/game/editor/mapitems/map.cpp +++ b/src/game/editor/mapitems/map.cpp @@ -1,5 +1,7 @@ #include +#include "image.h" + void CEditorMap::OnModify() { m_Modified = true; diff --git a/src/game/editor/mapitems/map_io.cpp b/src/game/editor/mapitems/map_io.cpp index f0962f913..d2dce83f9 100644 --- a/src/game/editor/mapitems/map_io.cpp +++ b/src/game/editor/mapitems/map_io.cpp @@ -11,6 +11,8 @@ #include #include +#include "image.h" + template static int MakeVersion(int i, const T &v) { diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 9d60c65ca..d5fad78fe 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "editor.h" diff --git a/src/game/editor/tileart.cpp b/src/game/editor/tileart.cpp index 0f33c8f50..85a37a7bb 100644 --- a/src/game/editor/tileart.cpp +++ b/src/game/editor/tileart.cpp @@ -1,5 +1,7 @@ #include "editor.h" +#include + #include bool operator<(const ColorRGBA &Left, const ColorRGBA &Right) From d68029a25275f504542a08b353b6965d6b9f9ae9 Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:02:09 +0200 Subject: [PATCH 030/126] extract `CEditorSound` into separate header file --- CMakeLists.txt | 1 + src/game/editor/editor.cpp | 1 + src/game/editor/editor.h | 25 +--------------------- src/game/editor/mapitems/map_io.cpp | 1 + src/game/editor/mapitems/sound.cpp | 4 +++- src/game/editor/mapitems/sound.h | 32 +++++++++++++++++++++++++++++ src/game/editor/popups.cpp | 1 + 7 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 src/game/editor/mapitems/sound.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bf3edad33..c38e2b8f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2330,6 +2330,7 @@ if(CLIENT) mapitems/map.cpp mapitems/map_io.cpp mapitems/sound.cpp + mapitems/sound.h popups.cpp proof_mode.cpp proof_mode.h diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 6a5dd13c2..db6c97443 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -32,6 +32,7 @@ #include #include +#include #include "auto_map.h" #include "editor.h" diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index a43b4957a..ccd36e57f 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -330,30 +330,7 @@ public: }; class CEditorImage; - -class CEditorSound -{ -public: - CEditor *m_pEditor; - - CEditorSound(CEditor *pEditor) - { - m_pEditor = pEditor; - m_aName[0] = 0; - m_SoundID = 0; - - m_pData = nullptr; - m_DataSize = 0; - } - - ~CEditorSound(); - - int m_SoundID; - char m_aName[IO_MAX_PATH_LENGTH]; - - void *m_pData; - unsigned m_DataSize; -}; +class CEditorSound; class CEditorMap { diff --git a/src/game/editor/mapitems/map_io.cpp b/src/game/editor/mapitems/map_io.cpp index d2dce83f9..092431d2b 100644 --- a/src/game/editor/mapitems/map_io.cpp +++ b/src/game/editor/mapitems/map_io.cpp @@ -12,6 +12,7 @@ #include #include "image.h" +#include "sound.h" template static int MakeVersion(int i, const T &v) diff --git a/src/game/editor/mapitems/sound.cpp b/src/game/editor/mapitems/sound.cpp index 2ee5bb36e..cdf7e025e 100644 --- a/src/game/editor/mapitems/sound.cpp +++ b/src/game/editor/mapitems/sound.cpp @@ -1,7 +1,9 @@ -#include +#include "sound.h" #include +#include + CEditorSound::~CEditorSound() { m_pEditor->Sound()->UnloadSample(m_SoundID); diff --git a/src/game/editor/mapitems/sound.h b/src/game/editor/mapitems/sound.h new file mode 100644 index 000000000..6d3b00ed3 --- /dev/null +++ b/src/game/editor/mapitems/sound.h @@ -0,0 +1,32 @@ +#ifndef GAME_EDITOR_MAPITEMS_SOUND_H +#define GAME_EDITOR_MAPITEMS_SOUND_H + +#include + +class CEditor; + +class CEditorSound +{ +public: + CEditor *m_pEditor; + + CEditorSound(CEditor *pEditor) + { + m_pEditor = pEditor; + m_aName[0] = 0; + m_SoundID = 0; + + m_pData = nullptr; + m_DataSize = 0; + } + + ~CEditorSound(); + + int m_SoundID; + char m_aName[IO_MAX_PATH_LENGTH]; + + void *m_pData; + unsigned m_DataSize; +}; + +#endif diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index d5fad78fe..ca7a7f300 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "editor.h" From c3a07dd9774c80ececcffb16dd2c5476c15675f5 Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:07:28 +0200 Subject: [PATCH 031/126] let `CEditorSound` inherit `CEditorComponent` --- src/game/editor/mapitems/map_io.cpp | 1 - src/game/editor/mapitems/sound.cpp | 7 +++++-- src/game/editor/mapitems/sound.h | 25 +++++++------------------ 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/game/editor/mapitems/map_io.cpp b/src/game/editor/mapitems/map_io.cpp index 092431d2b..31c95e0c7 100644 --- a/src/game/editor/mapitems/map_io.cpp +++ b/src/game/editor/mapitems/map_io.cpp @@ -549,7 +549,6 @@ bool CEditorMap::Load(const char *pFileName, int StorageType, const std::functio // copy base info std::shared_ptr pSound = std::make_shared(m_pEditor); - if(pItem->m_External) { char aBuf[IO_MAX_PATH_LENGTH]; diff --git a/src/game/editor/mapitems/sound.cpp b/src/game/editor/mapitems/sound.cpp index cdf7e025e..6465f915b 100644 --- a/src/game/editor/mapitems/sound.cpp +++ b/src/game/editor/mapitems/sound.cpp @@ -2,11 +2,14 @@ #include -#include +CEditorSound::CEditorSound(CEditor *pEditor) +{ + Init(pEditor); +} CEditorSound::~CEditorSound() { - m_pEditor->Sound()->UnloadSample(m_SoundID); + Sound()->UnloadSample(m_SoundID); free(m_pData); m_pData = nullptr; } diff --git a/src/game/editor/mapitems/sound.h b/src/game/editor/mapitems/sound.h index 6d3b00ed3..681f9ad30 100644 --- a/src/game/editor/mapitems/sound.h +++ b/src/game/editor/mapitems/sound.h @@ -3,30 +3,19 @@ #include -class CEditor; +#include -class CEditorSound +class CEditorSound : public CEditorComponent { public: - CEditor *m_pEditor; - - CEditorSound(CEditor *pEditor) - { - m_pEditor = pEditor; - m_aName[0] = 0; - m_SoundID = 0; - - m_pData = nullptr; - m_DataSize = 0; - } - + explicit CEditorSound(CEditor *pEditor); ~CEditorSound(); - int m_SoundID; - char m_aName[IO_MAX_PATH_LENGTH]; + int m_SoundID = 0; + char m_aName[IO_MAX_PATH_LENGTH] = ""; - void *m_pData; - unsigned m_DataSize; + void *m_pData = nullptr; + unsigned m_DataSize = 0; }; #endif From 38ad2e956a4dba25c64185c88094ae0ef90ed52e Mon Sep 17 00:00:00 2001 From: furo Date: Tue, 29 Aug 2023 23:49:03 +0200 Subject: [PATCH 032/126] Update Swedish Translations --- data/languages/swedish.txt | 373 +++++++++++++++++++------------------ 1 file changed, 187 insertions(+), 186 deletions(-) diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt index 11c16d3ab..12a7e5f87 100644 --- a/data/languages/swedish.txt +++ b/data/languages/swedish.txt @@ -7,6 +7,7 @@ # 3edcxzaq1 2020-06-25 00:00:00 # cur.ie 2020-09-28 00:00:00 # simpygirl 2022-02-20 00:00:00 +# furo 2023-08-29 00:00:00 ##### /authors ##### ##### translated strings ##### @@ -207,7 +208,7 @@ Hook == Hook Invalid Demo -== Ogiltigt deo +== Ogiltig demo Join blue == Spela i blått @@ -343,7 +344,7 @@ Rename demo == Byt namn på demo Reset filter -== Återställ filter +== Nollställ filter Score == Poäng @@ -415,7 +416,7 @@ Strict gametype filter == Strikt speltypsfilter Sudden Death -== Plötslig död +== Sudden Death Switch weapon on pickup == Byt vapen vid anskaffning @@ -433,7 +434,7 @@ The server is running a non-standard tuning on a pure game type. == Denna server kör inte standardinställningar på en reserverad speltyp. There's an unsaved map in the editor, you might want to save it before you quit the game. -== Det finns en osparad bana i redigeraden, du kanske vill spara den innan du avslutar. +== Det finns en osparad bana i redigeraren, du kanske vill spara den innan du avslutar. Time limit == Tidsbegränsning @@ -550,7 +551,7 @@ Size: == Storlek: Reset to defaults -== Återställ till standard +== Nollställ till standard Quit anyway? == Avsluta i alla fall? @@ -607,7 +608,7 @@ Show kill messages == Visa döds meddelanden Reset -== Återställ +== Nollställ DDNet == DDNet @@ -643,7 +644,7 @@ Reconnect in %d sec == Återkopplar om %d sekunder Successfully saved the replay! -== Lyckades med att spara repris +== Lyckades med att spara repris! Save ghost == Spara spöken @@ -676,13 +677,13 @@ Show votes window after voting == Visa röstnings fönster efter röstning DDNet Client needs to be restarted to complete update! -== DDNet Klienten behöves startas om för att genomföra updateringen! +== DDNet Klienten behövs startas om för att genomföra uppdateringen! Kill == Dö Personal best: -== Personligs bästa: +== Personligt bästa: Show DDNet map finishes in server browser == Visa DDNet bana avklarningar i server bläddraren @@ -706,7 +707,7 @@ Render == Rendera Are you sure that you want to disconnect? -== Är du säker att du vill koppla ifrån? +== Är du säker på att du vill koppla ifrån? Grabs == Grabs @@ -808,7 +809,7 @@ Date == Datum Show other players' hook collision lines -== Visa andra spelares hook kollision linor +== Visa andra spelares hook kollisions linjer Fetch Info == Hämta Info @@ -874,7 +875,7 @@ Show only chat messages from friends == Visa bara chatt meddelanden från vänner DDNet Client updated! -== DDNet Klienten updaterades! +== DDNet Klienten uppdaterades! Converse == Konversera @@ -916,7 +917,7 @@ Best == Bäst Updating... -== Updaterar... +== Uppdaterar... Clan plates size == Klanskylt storlek @@ -925,7 +926,7 @@ Size == Storlek Save the best demo of each race -== Spara the bästa demon av varje race +== Spara den bästa demot av varje race Frags == Frags @@ -997,7 +998,7 @@ Show names in chat in team colors == Visa namn in chatten med lagets färger Update now -== Updatera nu +== Uppdatera nu Toggle ghost == Växla spöke @@ -1009,7 +1010,7 @@ Team message == Lag meddelande File already exists, do you want to overwrite it? -== Filen finns redan, vill du skriv över den? +== Filen finns redan, vill du skriva över den? Hook collisions == Hook kollisioner @@ -1069,7 +1070,7 @@ Server executable not found, can't run server == Server exekveringsfil hittades ej, kan ej starta servern Editor -== Editor +== Redigeraren [Start menu] Play @@ -1091,7 +1092,7 @@ Saving ddnet-settings.cfg failed == Det gick inte att spara ddnet-settings.cfg The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs. -== Bredden på texturen %s är inte delbar med %d, eller höjden är inte delbar med %d, vilket kan orsaka visuella buggar. +== Bredden på texturen %s är inte delbar med %d, eller är höjden inte delbar med %d, vilket kan orsaka visuella buggar. Debug mode enabled. Press Ctrl+Shift+D to disable debug mode. == Felsökningsläge är aktiverad, Klicka Ctrl+Shift+D för att stänga av felsökningsläge. @@ -1106,7 +1107,7 @@ Checking for existing player with your name == Söker efter en befintlig spelare med ditt namn Are you sure that you want to disconnect and switch to a different server? -== Är du säker att du vill koppla ifrån och byta server? +== Är du säker på att du vill koppla ifrån och byta server? Theme == Tema @@ -1223,7 +1224,7 @@ Entities == Entities Emoticons -== Emoticons +== Känsloikoner Particles == Partiklar @@ -1239,510 +1240,510 @@ https://ddnet.org/discord [Graphics error] Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. -== +== Misslyckades under uppstart. Testa att ändra gfx_backend till OpenGL eller Vulkan i settings_ddnet.cfg i konfig nappen och försök igen. [Graphics error] Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution. -== +== Slut på VRAM. Testa att ta bort assets (skins, entities, etc.), speciellt dem i hög upplösning. [Graphics error] An error during command recording occurred. Try to update your GPU drivers. -== +== Ett fel uppstod under "command recording". Testa att uppdatera ditt grafikkorts drivrutiner. [Graphics error] A render command failed. Try to update your GPU drivers. -== +== Ett "render command" misslyckades. Testa att uppdatera ditt grafikkorts drivrutiner. [Graphics error] Submitting the render commands failed. Try to update your GPU drivers. -== +== Inskickning av "render commands" misslyckades. Testa att uppdatera ditt grafikkorts drivrutiner. [Graphics error] Failed to swap framebuffers. Try to update your GPU drivers. -== +== Misslyckades att "swap framebuffers". Testa att uppdatera ditt grafikkorts drivrutiner. [Graphics error] Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. -== +== Okänt fel. Testa att ändra gfx_backend till OpenGL eller Vulkan i settings_ddnet.cfg i konfig nappen och försök igen. [Graphics error] Could not initialize the given graphics backend, reverting to the default backend now. -== +== Kunde inte starta den angivna grafikbackend, återgår till standard backend nu. [Graphics error] Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. -== +== Kunde inte starta den angivna grafikbackend, detta beror någ på att du inte har installerad drivrutinerna för ditt integrerade grafikkort. Could not save downloaded map. Try manually deleting this file: %s -== +== Kunde inte spara nedladdade bana. Testa att manuellt ta bort denna fil: %s The format of texture %s is not RGBA which will cause visual bugs. -== +== Formatet av texturn %s är inte RGBA, vilket kommer att orsaka visuella buggar. Preparing demo playback -== +== Förbreder för demo uppspelning Connected -== +== Ansluten Loading map file from storage -== +== Laddar bana fil från lagring Why are you slowmo replaying to read this? -== +== Varför försöker du att läsa detta? Initializing components -== +== Initierar komponenter Initializing assets -== +== Initierar assets Initializing map logic -== +== Initierar bana logik Sending initial client info -== +== Skickar första klient info Quitting. Please wait… -== +== Lämnar. Vänligen vänta… Restarting. Please wait… -== +== Startar om. Vänligen vänta… Position: -== +== Position Speed: -== +== Hastighet: Angle: -== +== Vinkel: Multi-View -== +== Multi-Vy Team %d -== +== Lag %d Uploading map data to GPU -== +== Laddar upp bana data till grafikkortet Trying to determine UDP connectivity... -== +== Försöker att bestämma UDP anslutning... UDP seems to be filtered. -== +== UDP verkar vara filtrerad. UDP and TCP IP addresses seem to be different. Try disabling VPN, proxy or network accelerators. -== +== UDP och TCP IP adresser verkar vara olika. Testa att stänga av VPN, proxy eller nätverksacceleratorer. No answer from server yet. -== +== Inget svar från servern än. Getting game info -== +== Hämtar spel info Requesting to join the game -== +== Begär att få ansluta till spelet. Rename folder -== +== Döp om mapp. A demo with this name already exists -== +== Ett demo med detta namn finns redan A folder with this name already exists -== +== En mapp finns redan med detta namn Unable to rename the folder -== +== Misslyckades att döpa om mappen File '%s' already exists, do you want to overwrite it? -== +== Fil '%s' finns redan, vill du skriva över den? (paused) -== +== (pausad) Join Tutorial Server -== +== Anslut till Tutorial Skip Tutorial -== +== Skippa Tutorial Loading menu images -== +== Laddar meny bilder Copy info -== +== Kopiera info No server selected -== +== Ingen server vald Online players (%d) -== +== Online spelare (%d) Online clanmates (%d) -== +== Online klanmedlemmar (%d) [friends (server browser)] Offline (%d) -== +== Offline (%d) Click to select server. Double click to join your friend. -== +== Klicka för att välja server. Dubbel klicka för att ansluta till din kamrat. Click to remove this player from your friends list. -== +== Klicka för att ta bort denna spelare från din kompis lista. Click to remove this clan from your friends list. -== +== Klicka för att ta bort denna klan från din kompis lista. None -== +== Ingen Are you sure that you want to remove the player '%s' from your friends list? -== +== Är du säker på att du vill ta bort spelare '%s' från din kompis lista? Are you sure that you want to remove the clan '%s' from your friends list? -== +== Är du säker på att du vill ta bort klanen '%s' från din kompis lista? Add Clan -== +== Lägg till klan Play the current demo -== +== Spela demo Pause the current demo -== +== Pausa demo Stop the current demo -== +== Stoppa demo Go back one tick -== +== Gå tillbaka en tick Go forward one tick -== +== Gå framåt en tick Slow down the demo -== +== Sakta ner demot Speed up the demo -== +== Snabba up demot Mark the beginning of a cut (right click to reset) -== +== Markera start av snittet (höger klicka för att nollställa) Mark the end of a cut (right click to reset) -== +== Markera slutet av snittet (höger klicka för att nollställa) Export cut as a separate demo -== +== Exportera snitt till en seperat demo fil Go back one marker -== +== Gå tillbaka en markör Go forward one marker -== +== Gå framåt en markör Close the demo player -== +== Stäng demo spelaren Toggle keyboard shortcuts -== +== Växla kortkommandon Export demo cut -== +== Exportera demo snitt Cut interval -== +== Snitt interval Cut length -== +== Snitt längd Loading demo files -== +== Laddar demo filer All combined -== +== Alla kombinerade Folder Link -== +== Mapp länk Open the directory that contains the demo files -== +== Öppna mappen som innehåller demo filerna Are you sure that you want to delete the folder '%s'? -== +== Är du säker på att du vill ta bort mappen '%s'? Are you sure that you want to delete the demo '%s'? -== +== Är du säker på att du vill ta bort demot '%s'? Delete folder -== +== Ta bort mapp Unable to delete the demo '%s' -== +== Misslyckades att ta bort demot '%s' Unable to delete the folder '%s'. Make sure it's empty first. -== +== Misslyckades att ta bort mappen '%s'. Den måste vara tom först. Loading ghost files -== +== Laddar spök filer Menu opened. Press Esc key again to close menu. -== +== Meny öppnad. Klicka Esc igen för att stänga menyn. Save power by lowering refresh rate (higher input latency) -== +== Spara batteri genom att sänka uppdateringsfrekvensen (högre inmatnings latens) Open the settings file -== +== Öppna inställningsfil Open the directory that contains the configuration and user files -== +== Öppna mappen som innehåller konfigurationen och användarfiler Open the directory to add custom themes -== +== Öppna mappen för att lägga till egna teman Loading skin files -== +== Laddar skin filer Toggle to edit your dummy settings -== +== Växla till att ändra dina dummy inställningar Download community skins -== +== Ladda ner community skins Choose default eyes when joining a server -== +== Välj standard ögon när du ansluter till en server Create a random skin -== +== Skapa ett slumpad skin Open the directory to add custom skins -== +== Öppna mappen för att lägga till egna skins Enable controller -== +== Aktivera kontroller Controller -== +== Kontroller Ingame controller mode -== +== Kontroller läge under spel [Ingame controller mode] Relative -== +== Relativ [Ingame controller mode] Absolute -== +== Absolut Ingame controller sens. -== +== Kontroller känslighet i spelet. UI controller sens. -== +== Kontroller känslighet i menyer. Controller jitter tolerance -== +== Kontroller skaka tolerans No controller found. Plug in a controller. -== +== Ingen kontroller hittad. Anslut en kontroller. Axis -== +== Axel Status -== +== Status Aim bind -== +== Aim bind Mouse -== +== Mus Ingame mouse sens. -== +== Mus känslighet i spelet. UI mouse sens. -== +== Mus känslighet i menyer. Reset controls -== +== Nollställ kontrollerna Are you sure that you want to reset the controls to their defaults? -== +== Är du säker på att du vill nollställa kontrollerna till standard? Cancel -== +== Avbryt Allows maps to render with more detail -== +== Tillåt banor att visa mer detaljer Renderer -== +== Renderer default -== +== standard custom -== +== egna Graphics card -== +== Grafikkort auto -== +== auto Appearance -== +== Utseende Name Plate -== +== Namnskylt Hook Collisions -== +== Hook kollisioner Show health, shields and ammo -== +== Visa hälsa, sköldar och ammunition DDRace HUD -== +== DDRace HUD Show client IDs in scoreboard -== +== Visa klient ID i poänglista Show DDRace HUD -== +== Visa DDRace HUD Show jumps indicator -== +== Visa hopp indikator Show dummy actions -== +== Visa dummy actions Show player position -== +== Visa spelarens position Show player speed -== +== Visa spelarens hastighet Show player target angle -== +== Visa spelarens vinkel Show freeze bars -== +== Visa freeze bars Opacity of freeze bars inside freeze -== +== Opacitet av freeze bars i freeze Show hook strength indicator -== +== Visa hook styrka indikator Hook collision line -== +== Hook kollisions linje Hook collision line width -== +== Hook kollisions linje bredd Hook collision line opacity -== +== Hook kollisions linje opacitet Colors of the hook collision line, in case of a possible collision with: -== +== Färger av hook kollisions linje, ifall det finns en kollision med: Your movements are not taken into account when calculating the line colors -== +== Dina rörelser tas inte med i beräkningen av linjefärgerna Nothing hookable -== +== Inget hookable Something hookable -== +== Något hookable A Tee -== +== En Tee Normal Color -== +== Normal Färg Highlight Color -== +== Betonad Färg Weapons -== +== Vapen Rifle Laser Outline Color -== +== Gevär Laser Kontur Färg Rifle Laser Inner Color -== +== Gevär Laser Inre Färg Shotgun Laser Outline Color -== +== Hagelgevär Laser Kontur Färg Shotgun Laser Inner Color -== +== Hagelgevär Inre Färg Door Laser Outline Color -== +== Dörr Laser Kontur Färg Door Laser Inner Color -== +== Dörr Laser Inre Färg Freeze Laser Outline Color -== +== Freeze Laser Kontur Färg Freeze Laser Inner Color -== +== Freeze Laser Inre Färg Set all to Rifle -== +== Sätt alla till Gevär When you cross the start line, show a ghost tee replicating the movements of your best time -== +== När du passera start linjen, visa en spök tee som visar rörelsen av din bästa tid Opacity -== +== Opacitet Adjust the opacity of entities belonging to other teams, such as tees and nameplates -== +== Justera opaciteten av entities som tillhör ett annat lag, som t.ex. tees och namnskyltar Quads are used for background decoration -== +== Quads används till bakgrunds decorationer Tries to predict other entities to give a feel of low latency -== +== Försöker att predicera andra entities för att ge känslan av låg latens. Unregister protocol and file extensions -== +== Avregistera protokoll och filtillägg Extras -== +== Extras Loading assets -== +== Laddar assets Open the directory to add custom assets -== +== Öppna mappen för att lägga till egna assets Tutorial -== +== Tutorial Can't find a Tutorial server -== +== Kunde inte hitta en Tutorial server Loading race demo files -== +== Laddar race demo filer Super -== +== Super Loading sound files -== +== Laddar ljud filer Moved ingame -== +== Förflyttades i spelet From ca306b9007d805d70e2d16b92e76765ef3999c62 Mon Sep 17 00:00:00 2001 From: h-kaan <105508453+h-kaan@users.noreply.github.com> Date: Wed, 30 Aug 2023 01:43:50 +0300 Subject: [PATCH 033/126] `kesim -> kesit` & improvements --- data/languages/turkish.txt | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/data/languages/turkish.txt b/data/languages/turkish.txt index bfaced9e3..2fa19dc87 100644 --- a/data/languages/turkish.txt +++ b/data/languages/turkish.txt @@ -1130,13 +1130,13 @@ Debug mode enabled. Press Ctrl+Shift+D to disable debug mode. == Hata ayıklama modu etkinleştirildi. Devre dışı bırakmak için Ctrl+Shift+D tuşlarına basın. Position: -== Pozisyon +== Pozisyon: Speed: -== Hız +== Hız: Angle: -== Açı +== Açı: Multi-View == Çoklu İzle @@ -1154,7 +1154,7 @@ UDP seems to be filtered. == UDP filtrelenmiş görünüyor. UDP and TCP IP addresses seem to be different. Try disabling VPN, proxy or network accelerators. -== UPD ve TCP IP adresleri farklı görünüyor. Proxy, VPN veya ağ hızlandırıcılarını devre dışı bırakmayı deneyin. +== UDP ve TCP IP adresleri farklı görünüyor. Proxy, VPN veya ağ hızlandırıcılarını devre dışı bırakmayı deneyin. No answer from server yet. == Henüz sunucudan cevap alınamadı. @@ -1172,7 +1172,7 @@ Existing Player == Var olan oyuncu Your nickname '%s' is already used (%d points). Do you still want to use it? -== Takma adınız '%s' başkası tarafından kullanılıyor (%d points). Yine de kullanmak istiyor musunuz? +== Takma adınız '%s' başkası tarafından kullanılıyor (%d puan). Yine de kullanmak istiyor musunuz? Checking for existing player with your name == Adınıza sahip diğer oyuncular aranıyor @@ -1238,7 +1238,7 @@ Getting server list from master server == %2$d sunucunun %1$d tanesi %d players -== %d oyuncular +== %d oyuncu %d player == %d oyuncu @@ -1308,13 +1308,13 @@ Speed up the demo == Demoyu hızlandır Mark the beginning of a cut (right click to reset) -== Bir kesimin başlangıcını işaretleyin (sıfırlamak için sağ tıkla) +== Bir kesitin başlangıcını işaretleyin (sıfırlamak için sağ tıkla) Mark the end of a cut (right click to reset) -== Bir kesimin bitişini işaretleyin (sıfırlamak için sağ tıkla) +== Bir kesitin bitişini işaretleyin (sıfırlamak için sağ tıkla) Export cut as a separate demo -== Kesimi ayrı bir demo olarak dışarı aktar +== Kesiti ayrı bir demo olarak dışarı aktar Go back one marker == Bir işaret geri git @@ -1329,13 +1329,13 @@ Toggle keyboard shortcuts == Klavye kısayollarını kullan Export demo cut -== Kesimi dışarı aktar +== Kesiti dışarı aktar Cut interval -== Kesim aralığı +== Kesit aralığı Cut length -== Kesim uzunluğu +== Kesit uzunluğu Loading demo files == Demo dosyaları yükleniyor @@ -1472,10 +1472,10 @@ Mouse == Fare Ingame mouse sens. -== Oyun içi fare hassasiyeti +== Oyun hassasiyeti UI mouse sens. -== Arayüz fare hassasiyeti +== Arayüz hassasiyeti Reset controls == Kontrolleri sıfırla @@ -1556,7 +1556,7 @@ Show DDRace HUD == DDRace HUD göster Show jumps indicator -== Zıplama göstergesini göster +== Kaç zıplama kaldığını göster Show dummy actions == Dummy aksiyonlarını göster From cf3f90f62d5ee4653b348d56c2c13cfc277d5476 Mon Sep 17 00:00:00 2001 From: Kaan <105508453+h-kaan@users.noreply.github.com> Date: Wed, 30 Aug 2023 01:53:11 +0300 Subject: [PATCH 034/126] add the modification date --- data/languages/turkish.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/data/languages/turkish.txt b/data/languages/turkish.txt index 2fa19dc87..6784b7da0 100644 --- a/data/languages/turkish.txt +++ b/data/languages/turkish.txt @@ -8,7 +8,8 @@ # Learath2 2012-01-01 22:54:29 # ardadem 2020-08-20 00:00:00 # ardadem 2020-08-22 00:00:00 -# h-kaan 2023-08-23 15:57:36 +# h-kaan 2023-08-23 15:57:36 +# h-kaan 2023-08-30 01:52:04 ##### /authors ##### ##### translated strings ##### @@ -1749,4 +1750,4 @@ Loading sound files == Ses dosyaları yükleniyor Moved ingame -== Hareket edildi \ No newline at end of file +== Hareket edildi From 0a290bc50163600425c5aa6e4e5062489bc4e36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 22 Aug 2023 20:51:01 +0200 Subject: [PATCH 035/126] Remove unused enum literals --- src/engine/serverbrowser.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index 28ccf2e45..5a25e08c5 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -131,13 +131,6 @@ public: TYPE_DDNET = 4, TYPE_KOG = 5, - SET_MASTER_ADD = 1, - SET_FAV_ADD, - SET_DDNET_ADD, - SET_KOG_ADD, - SET_TOKEN, - SET_HTTPINFO, - NETWORK_DDNET = 0, NETWORK_KOG = 1, NUM_NETWORKS, From bc82a450c35f7155a563479a136183550f8f68f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 30 Aug 2023 17:49:28 +0200 Subject: [PATCH 036/126] Remove `CLocConstString` This makes it more obvious where text is localized. This class was also broken for localized strings with context, since the member variable `m_ContextHash` was uninitialized. --- src/game/client/components/menus.cpp | 4 +- src/game/client/components/menus.h | 2 +- src/game/client/components/menus_browser.cpp | 45 +++++++++---------- src/game/client/components/menus_demo.cpp | 4 +- src/game/client/components/menus_ingame.cpp | 8 ++-- src/game/client/components/menus_settings.cpp | 13 +++--- src/game/localization.cpp | 25 ----------- src/game/localization.h | 26 ----------- 8 files changed, 37 insertions(+), 90 deletions(-) diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index f88b0489f..68d0e017c 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -448,10 +448,10 @@ int CMenus::DoButton_CheckBox_Number(const void *pID, const char *pText, int Che return DoButton_CheckBox_Common(pID, pText, aBuf, pRect); } -int CMenus::DoKeyReader(void *pID, const CUIRect *pRect, int Key, int ModifierCombination, int *pNewModifierCombination) +int CMenus::DoKeyReader(const void *pID, const CUIRect *pRect, int Key, int ModifierCombination, int *pNewModifierCombination) { // process - static void *pGrabbedID = 0; + static const void *pGrabbedID = 0; static bool MouseReleased = true; static int s_ButtonUsed = 0; const bool Inside = UI()->MouseHovered(pRect); diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index b51313b3e..dcf154d65 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -71,7 +71,7 @@ class CMenus : public CComponent int DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect); void DoButton_KeySelect(const void *pID, const char *pText, const CUIRect *pRect); - int DoKeyReader(void *pID, const CUIRect *pRect, int Key, int ModifierCombination, int *pNewModifierCombination); + int DoKeyReader(const void *pID, const CUIRect *pRect, int Key, int ModifierCombination, int *pNewModifierCombination); void DoSettingsControlsButtons(int Start, int Stop, CUIRect View); diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index e5dff168b..2a5a3e82f 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -70,7 +70,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) { int m_ID; int m_Sort; - CLocConstString m_Caption; + const char *m_pCaption; int m_Direction; float m_Width; CUIRect m_Rect; @@ -90,17 +90,17 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) COL_VERSION, }; - CColumn s_aCols[] = { - {-1, -1, " ", -1, 2.0f, {0}, {0}}, - {COL_FLAG_LOCK, -1, " ", -1, 14.0f, {0}, {0}}, - {COL_FLAG_FAV, -1, " ", -1, 14.0f, {0}, {0}}, - {COL_FLAG_OFFICIAL, -1, " ", -1, 14.0f, {0}, {0}}, - {COL_NAME, IServerBrowser::SORT_NAME, "Name", 0, 50.0f, {0}, {0}}, // Localize - these strings are localized within CLocConstString + static CColumn s_aCols[] = { + {-1, -1, "", -1, 2.0f, {0}, {0}}, + {COL_FLAG_LOCK, -1, "", -1, 14.0f, {0}, {0}}, + {COL_FLAG_FAV, -1, "", -1, 14.0f, {0}, {0}}, + {COL_FLAG_OFFICIAL, -1, "", -1, 14.0f, {0}, {0}}, + {COL_NAME, IServerBrowser::SORT_NAME, Localizable("Name"), 0, 50.0f, {0}, {0}}, {COL_GAMETYPE, IServerBrowser::SORT_GAMETYPE, Localizable("Type"), 1, 50.0f, {0}, {0}}, - {COL_MAP, IServerBrowser::SORT_MAP, "Map", 1, 120.0f + (Headers.w - 480) / 8, {0}, {0}}, - {COL_PLAYERS, IServerBrowser::SORT_NUMPLAYERS, "Players", 1, 85.0f, {0}, {0}}, - {-1, -1, " ", 1, 10.0f, {0}, {0}}, - {COL_PING, IServerBrowser::SORT_PING, "Ping", 1, 40.0f, {0}, {0}}, + {COL_MAP, IServerBrowser::SORT_MAP, Localizable("Map"), 1, 120.0f + (Headers.w - 480) / 8, {0}, {0}}, + {COL_PLAYERS, IServerBrowser::SORT_NUMPLAYERS, Localizable("Players"), 1, 85.0f, {0}, {0}}, + {-1, -1, "", 1, 10.0f, {0}, {0}}, + {COL_PING, IServerBrowser::SORT_PING, Localizable("Ping"), 1, 40.0f, {0}, {0}}, }; int NumCols = std::size(s_aCols); @@ -143,7 +143,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) if(PlayersOrPing && g_Config.m_BrSortOrder == 2 && (s_aCols[i].m_Sort == IServerBrowser::SORT_NUMPLAYERS || s_aCols[i].m_Sort == IServerBrowser::SORT_PING)) Checked = 2; - if(DoButton_GridHeader(s_aCols[i].m_Caption, Localize(s_aCols[i].m_Caption), Checked, &s_aCols[i].m_Rect)) + if(DoButton_GridHeader(&s_aCols[i].m_ID, Localize(s_aCols[i].m_pCaption), Checked, &s_aCols[i].m_Rect)) { if(s_aCols[i].m_Sort != -1) { @@ -1066,12 +1066,6 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) { ServerDetails.Margin(5.0f, &ServerDetails); - CUIRect Row; - static CLocConstString s_aLabels[] = { - "Version", // Localize - these strings are localized within CLocConstString - "Game type", - "Ping"}; - // copy info button { CUIRect Button; @@ -1122,21 +1116,24 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) } } - CUIRect LeftColumn, RightColumn; + CUIRect LeftColumn, RightColumn, Row; ServerDetails.VSplitLeft(80.0f, &LeftColumn, &RightColumn); - for(auto &Label : s_aLabels) - { - LeftColumn.HSplitTop(15.0f, &Row, &LeftColumn); - UI()->DoLabel(&Row, Label, FontSize, TEXTALIGN_ML); - } + LeftColumn.HSplitTop(15.0f, &Row, &LeftColumn); + UI()->DoLabel(&Row, Localize("Version"), FontSize, TEXTALIGN_ML); RightColumn.HSplitTop(15.0f, &Row, &RightColumn); UI()->DoLabel(&Row, pSelectedServer->m_aVersion, FontSize, TEXTALIGN_ML); + LeftColumn.HSplitTop(15.0f, &Row, &LeftColumn); + UI()->DoLabel(&Row, Localize("Game type"), FontSize, TEXTALIGN_ML); + RightColumn.HSplitTop(15.0f, &Row, &RightColumn); UI()->DoLabel(&Row, pSelectedServer->m_aGameType, FontSize, TEXTALIGN_ML); + LeftColumn.HSplitTop(15.0f, &Row, &LeftColumn); + UI()->DoLabel(&Row, Localize("Ping"), FontSize, TEXTALIGN_ML); + char aTemp[16]; FormatServerbrowserPing(aTemp, sizeof(aTemp), pSelectedServer); RightColumn.HSplitTop(15.0f, &Row, &RightColumn); diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 661531eec..3e36b7ddd 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -1113,7 +1113,7 @@ void CMenus::RenderDemoList(CUIRect MainView) { int m_ID; int m_Sort; - CLocConstString m_Caption; + const char *m_pCaption; int m_Direction; float m_Width; CUIRect m_Rect; @@ -1171,7 +1171,7 @@ void CMenus::RenderDemoList(CUIRect MainView) // do headers for(auto &Col : s_aCols) { - if(DoButton_GridHeader(&Col.m_ID, Col.m_Caption, g_Config.m_BrDemoSort == Col.m_Sort, &Col.m_Rect)) + if(DoButton_GridHeader(&Col.m_ID, Localize(Col.m_pCaption), g_Config.m_BrDemoSort == Col.m_Sort, &Col.m_Rect)) { if(Col.m_Sort != -1) { diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 0d3c0b914..5c074c95e 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -988,7 +988,7 @@ void CMenus::RenderGhost(CUIRect MainView) struct CColumn { - CLocConstString m_Caption; + const char *m_pCaption; int m_Id; float m_Width; CUIRect m_Rect; @@ -1003,8 +1003,8 @@ void CMenus::RenderGhost(CUIRect MainView) }; static CColumn s_aCols[] = { - {" ", -1, 2.0f, {0}, {0}}, - {" ", COL_ACTIVE, 30.0f, {0}, {0}}, + {"", -1, 2.0f, {0}, {0}}, + {"", COL_ACTIVE, 30.0f, {0}, {0}}, {Localizable("Name"), COL_NAME, 300.0f, {0}, {0}}, {Localizable("Time"), COL_TIME, 200.0f, {0}, {0}}, }; @@ -1022,7 +1022,7 @@ void CMenus::RenderGhost(CUIRect MainView) // do headers for(int i = 0; i < NumCols; i++) - DoButton_GridHeader(s_aCols[i].m_Caption, Localize(s_aCols[i].m_Caption), 0, &s_aCols[i].m_Rect); + DoButton_GridHeader(&s_aCols[i].m_Id, Localize(s_aCols[i].m_pCaption), 0, &s_aCols[i].m_Rect); View.Draw(ColorRGBA(0, 0, 0, 0.15f), 0, 0); diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 740fd39c7..80e2f86aa 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -940,7 +940,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) typedef struct { - CLocConstString m_Name; + const char *m_pName; const char *m_pCommand; int m_KeyId; int m_ModifierCombination; @@ -948,7 +948,7 @@ typedef struct static CKeyInfo gs_aKeys[] = { - {Localizable("Move left"), "+left", 0, 0}, // Localize - these strings are localized within CLocConstString + {Localizable("Move left"), "+left", 0, 0}, {Localizable("Move right"), "+right", 0, 0}, {Localizable("Jump"), "+jump", 0, 0}, {Localizable("Fire"), "+fire", 0, 0}, @@ -1003,23 +1003,24 @@ void CMenus::DoSettingsControlsButtons(int Start, int Stop, CUIRect View) { for(int i = Start; i < Stop; i++) { - CKeyInfo &Key = gs_aKeys[i]; + const CKeyInfo &Key = gs_aKeys[i]; + CUIRect Button, Label; View.HSplitTop(20.0f, &Button, &View); Button.VSplitLeft(135.0f, &Label, &Button); char aBuf[64]; - str_format(aBuf, sizeof(aBuf), "%s:", Localize((const char *)Key.m_Name)); + str_format(aBuf, sizeof(aBuf), "%s:", Localize(Key.m_pName)); UI()->DoLabel(&Label, aBuf, 13.0f, TEXTALIGN_ML); int OldId = Key.m_KeyId, OldModifierCombination = Key.m_ModifierCombination, NewModifierCombination; - int NewId = DoKeyReader((void *)&Key.m_Name, &Button, OldId, OldModifierCombination, &NewModifierCombination); + int NewId = DoKeyReader(&Key.m_KeyId, &Button, OldId, OldModifierCombination, &NewModifierCombination); if(NewId != OldId || NewModifierCombination != OldModifierCombination) { if(OldId != 0 || NewId == 0) m_pClient->m_Binds.Bind(OldId, "", false, OldModifierCombination); if(NewId != 0) - m_pClient->m_Binds.Bind(NewId, gs_aKeys[i].m_pCommand, false, NewModifierCombination); + m_pClient->m_Binds.Bind(NewId, Key.m_pCommand, false, NewModifierCombination); } View.HSplitTop(2.0f, 0, &View); diff --git a/src/game/localization.cpp b/src/game/localization.cpp index e8b8850a6..1b3feb33b 100644 --- a/src/game/localization.cpp +++ b/src/game/localization.cpp @@ -13,28 +13,6 @@ const char *Localize(const char *pStr, const char *pContext) return pNewStr ? pNewStr : pStr; } -CLocConstString::CLocConstString(const char *pStr, const char *pContext) -{ - m_pDefaultStr = pStr; - m_Hash = str_quickhash(m_pDefaultStr); - m_Version = -1; -} - -void CLocConstString::Reload() -{ - m_Version = g_Localization.Version(); - const char *pNewStr = g_Localization.FindString(m_Hash, m_ContextHash); - m_pCurrentStr = pNewStr; - if(!m_pCurrentStr) - m_pCurrentStr = m_pDefaultStr; -} - -CLocalizationDatabase::CLocalizationDatabase() -{ - m_VersionCounter = 0; - m_CurrentVersion = 0; -} - void CLocalizationDatabase::LoadIndexfile(IStorage *pStorage, IConsole *pConsole) { m_vLanguages.clear(); @@ -214,7 +192,6 @@ bool CLocalizationDatabase::Load(const char *pFilename, IStorage *pStorage, ICon { m_vStrings.clear(); m_StringsHeap.Reset(); - m_CurrentVersion = 0; return true; } @@ -281,8 +258,6 @@ bool CLocalizationDatabase::Load(const char *pFilename, IStorage *pStorage, ICon } io_close(IoHandle); std::sort(m_vStrings.begin(), m_vStrings.end()); - - m_CurrentVersion = ++m_VersionCounter; return true; } diff --git a/src/game/localization.h b/src/game/localization.h index b6d3173c5..6bd02bf95 100644 --- a/src/game/localization.h +++ b/src/game/localization.h @@ -48,46 +48,20 @@ class CLocalizationDatabase std::vector m_vLanguages; std::vector m_vStrings; CHeap m_StringsHeap; - int m_VersionCounter; - int m_CurrentVersion; public: - CLocalizationDatabase(); - void LoadIndexfile(class IStorage *pStorage, class IConsole *pConsole); const std::vector &Languages() const { return m_vLanguages; } void SelectDefaultLanguage(class IConsole *pConsole, char *pFilename, size_t Length) const; bool Load(const char *pFilename, class IStorage *pStorage, class IConsole *pConsole); - int Version() const { return m_CurrentVersion; } - void AddString(const char *pOrgStr, const char *pNewStr, const char *pContext); const char *FindString(unsigned Hash, unsigned ContextHash) const; }; extern CLocalizationDatabase g_Localization; -class CLocConstString -{ - const char *m_pDefaultStr; - const char *m_pCurrentStr; - unsigned m_Hash; - unsigned m_ContextHash; - int m_Version; - -public: - CLocConstString(const char *pStr, const char *pContext = ""); - void Reload(); - - inline operator const char *() - { - if(m_Version != g_Localization.Version()) - Reload(); - return m_pCurrentStr; - } -}; - extern const char *Localize(const char *pStr, const char *pContext = "") GNUC_ATTRIBUTE((format_arg(1))); #endif From 31a80c976b64a41af3ccf28dfb98fb95f3a4dd97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 28 Aug 2023 18:18:01 +0200 Subject: [PATCH 037/126] Remove `gfx_finish` config variable This config variable does not have any use case right now and only causes the rendering to be slowed down. --- src/engine/client/backend/opengl/backend_opengl.cpp | 8 -------- src/engine/client/backend/opengl/backend_opengl.h | 1 - src/engine/client/backend/vulkan/backend_vulkan.cpp | 9 +-------- src/engine/client/graphics_threaded.cpp | 6 ------ src/engine/client/graphics_threaded.h | 7 ------- src/engine/shared/config_variables.h | 1 - 6 files changed, 1 insertion(+), 31 deletions(-) diff --git a/src/engine/client/backend/opengl/backend_opengl.cpp b/src/engine/client/backend/opengl/backend_opengl.cpp index dc31e349c..cfa0aab88 100644 --- a/src/engine/client/backend/opengl/backend_opengl.cpp +++ b/src/engine/client/backend/opengl/backend_opengl.cpp @@ -42,11 +42,6 @@ void CCommandProcessorFragment_OpenGL::Cmd_Update_Viewport(const CCommandBuffer: glViewport(pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height); } -void CCommandProcessorFragment_OpenGL::Cmd_Finish(const CCommandBuffer::SCommand_Finish *pCommand) -{ - glFinish(); -} - int CCommandProcessorFragment_OpenGL::TexFormatToOpenGLFormat(int TexFormat) { if(TexFormat == CCommandBuffer::TEXFORMAT_RGBA) @@ -1102,9 +1097,6 @@ ERunCommandReturnTypes CCommandProcessorFragment_OpenGL::RunCommand(const CComma case CCommandBuffer::CMD_UPDATE_VIEWPORT: Cmd_Update_Viewport(static_cast(pBaseCommand)); break; - case CCommandBuffer::CMD_FINISH: - Cmd_Finish(static_cast(pBaseCommand)); - break; case CCommandBuffer::CMD_CREATE_BUFFER_OBJECT: Cmd_CreateBufferObject(static_cast(pBaseCommand)); break; case CCommandBuffer::CMD_UPDATE_BUFFER_OBJECT: Cmd_UpdateBufferObject(static_cast(pBaseCommand)); break; diff --git a/src/engine/client/backend/opengl/backend_opengl.h b/src/engine/client/backend/opengl/backend_opengl.h index c21f27479..b841d4289 100644 --- a/src/engine/client/backend/opengl/backend_opengl.h +++ b/src/engine/client/backend/opengl/backend_opengl.h @@ -102,7 +102,6 @@ protected: virtual void Cmd_Screenshot(const CCommandBuffer::SCommand_TrySwapAndScreenshot *pCommand); virtual void Cmd_Update_Viewport(const CCommandBuffer::SCommand_Update_Viewport *pCommand); - virtual void Cmd_Finish(const CCommandBuffer::SCommand_Finish *pCommand); virtual void Cmd_CreateBufferObject(const CCommandBuffer::SCommand_CreateBufferObject *pCommand) { dbg_assert(false, "Call of unsupported Cmd_CreateBufferObject"); } virtual void Cmd_RecreateBufferObject(const CCommandBuffer::SCommand_RecreateBufferObject *pCommand) { dbg_assert(false, "Call of unsupported Cmd_RecreateBufferObject"); } diff --git a/src/engine/client/backend/vulkan/backend_vulkan.cpp b/src/engine/client/backend/vulkan/backend_vulkan.cpp index b5c10d5d7..e5214b327 100644 --- a/src/engine/client/backend/vulkan/backend_vulkan.cpp +++ b/src/engine/client/backend/vulkan/backend_vulkan.cpp @@ -1282,7 +1282,6 @@ protected: m_aCommandCallbacks[CommandBufferCMDOff(CCommandBuffer::CMD_RENDER_QUAD_CONTAINER_SPRITE_MULTIPLE)] = {true, [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_RenderQuadContainerAsSpriteMultiple_FillExecuteBuffer(ExecBuffer, static_cast(pBaseCommand)); }, [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_RenderQuadContainerAsSpriteMultiple(static_cast(pBaseCommand), ExecBuffer); }}; m_aCommandCallbacks[CommandBufferCMDOff(CCommandBuffer::CMD_SWAP)] = {false, [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Swap(static_cast(pBaseCommand)); }}; - m_aCommandCallbacks[CommandBufferCMDOff(CCommandBuffer::CMD_FINISH)] = {false, [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Finish(static_cast(pBaseCommand)); }}; m_aCommandCallbacks[CommandBufferCMDOff(CCommandBuffer::CMD_VSYNC)] = {false, [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_VSync(static_cast(pBaseCommand)); }}; m_aCommandCallbacks[CommandBufferCMDOff(CCommandBuffer::CMD_MULTISAMPLING)] = {false, [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_MultiSampling(static_cast(pBaseCommand)); }}; @@ -6467,7 +6466,7 @@ public: Buffer.m_pRawCommand = pBaseCommand; Buffer.m_ThreadIndex = 0; - if(m_CurCommandInPipe + 1 == m_CommandsInPipe && Buffer.m_Command != CCommandBuffer::CMD_FINISH) + if(m_CurCommandInPipe + 1 == m_CommandsInPipe) { m_LastCommandsInPipeThreadIndex = std::numeric_limits::max(); } @@ -6921,12 +6920,6 @@ public: return true; } - [[nodiscard]] bool Cmd_Finish(const CCommandBuffer::SCommand_Finish *pCommand) - { - // just ignore it with vulkan - return true; - } - [[nodiscard]] bool Cmd_Swap(const CCommandBuffer::SCommand_Swap *pCommand) { return NextFrame(); diff --git a/src/engine/client/graphics_threaded.cpp b/src/engine/client/graphics_threaded.cpp index 91263817e..cad8e8b9c 100644 --- a/src/engine/client/graphics_threaded.cpp +++ b/src/engine/client/graphics_threaded.cpp @@ -2939,12 +2939,6 @@ void CGraphics_Threaded::Swap() AddCmd(Cmd); } - if(g_Config.m_GfxFinish) - { - CCommandBuffer::SCommand_Finish Cmd; - AddCmd(Cmd); - } - // kick the command buffer KickCommandBuffer(); // TODO: Remove when https://github.com/libsdl-org/SDL/issues/5203 is fixed diff --git a/src/engine/client/graphics_threaded.h b/src/engine/client/graphics_threaded.h index 807a203ef..ed2ee85b8 100644 --- a/src/engine/client/graphics_threaded.h +++ b/src/engine/client/graphics_threaded.h @@ -126,7 +126,6 @@ public: // swap CMD_SWAP, - CMD_FINISH, // misc CMD_MULTISAMPLING, @@ -495,12 +494,6 @@ public: SCommand(CMD_SWAP) {} }; - struct SCommand_Finish : public SCommand - { - SCommand_Finish() : - SCommand(CMD_FINISH) {} - }; - struct SCommand_VSync : public SCommand { SCommand_VSync() : diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 2a2f05f10..61926296e 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -123,7 +123,6 @@ MACRO_CONFIG_INT(GfxDisplayAllVideoModes, gfx_display_all_video_modes, 0, 0, 1, MACRO_CONFIG_INT(GfxHighDetail, gfx_high_detail, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "High detail") MACRO_CONFIG_INT(GfxFsaaSamples, gfx_fsaa_samples, 0, 0, 64, CFGFLAG_SAVE | CFGFLAG_CLIENT, "FSAA Samples") MACRO_CONFIG_INT(GfxRefreshRate, gfx_refresh_rate, 0, 0, 10000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Screen refresh rate") -MACRO_CONFIG_INT(GfxFinish, gfx_finish, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(GfxBackgroundRender, gfx_backgroundrender, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Render graphics when window is in background") MACRO_CONFIG_INT(GfxTextOverlay, gfx_text_overlay, 10, 1, 100, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Stop rendering textoverlay in editor or with entities: high value = less details = more speed") MACRO_CONFIG_INT(GfxAsyncRenderOld, gfx_asyncrender_old, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Do rendering async from the the update") From 1028cedee333799d050b3419ca6f8d0c7406b00e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 29 Aug 2023 20:14:22 +0200 Subject: [PATCH 038/126] Remove `dbg_hitch` config variable This config variable was only settable in the server console but only read in the client, so it was effectively unusable. It also has no use case right now. --- src/engine/client/client.cpp | 6 ------ src/engine/shared/config_variables.h | 1 - 2 files changed, 7 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 9470c65a8..9dbf35d43 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -3376,12 +3376,6 @@ void CClient::Run() else LastTime = Now; - if(g_Config.m_DbgHitch) - { - std::this_thread::sleep_for(g_Config.m_DbgHitch * 1ms); - g_Config.m_DbgHitch = 0; - } - // update local and global time m_LocalTime = (time_get() - m_LocalStartTime) / (float)time_freq(); m_GlobalTime = (time_get() - m_GlobalStartTime) / (float)time_freq(); diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 61926296e..0d6cdecd9 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -193,7 +193,6 @@ MACRO_CONFIG_INT(EcOutputLevel, ec_output_level, 1, 0, 2, CFGFLAG_ECON, "Adjusts MACRO_CONFIG_INT(Debug, debug, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SERVER, "Debug mode") MACRO_CONFIG_INT(DbgCurl, dbg_curl, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SERVER, "Debug curl") MACRO_CONFIG_INT(DbgGraphs, dbg_graphs, 0, 0, 1, CFGFLAG_CLIENT, "Performance graphs") -MACRO_CONFIG_INT(DbgHitch, dbg_hitch, 0, 0, 0, CFGFLAG_SERVER, "Hitch warnings") MACRO_CONFIG_INT(DbgGfx, dbg_gfx, 0, 0, 4, CFGFLAG_CLIENT, "Show graphic library warnings and errors, if the GPU supports it (0: none, 1: minimal, 2: affects performance, 3: verbose, 4: all)") #ifdef CONF_DEBUG MACRO_CONFIG_INT(DbgStress, dbg_stress, 0, 0, 0, CFGFLAG_CLIENT | CFGFLAG_SERVER, "Stress systems (Debug build only)") From 943f98dc92add84d2961a391563a0465026f6ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 29 Aug 2023 20:22:39 +0200 Subject: [PATCH 039/126] Improve config variable descriptions, add missing descriptions --- src/engine/shared/config_variables.h | 8 ++++---- src/game/variables.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 0d6cdecd9..2cb189876 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -24,7 +24,7 @@ MACRO_CONFIG_INT(ConsoleEnableColors, console_enable_colors, 1, 0, 1, CFGFLAG_SA MACRO_CONFIG_INT(ClSaveSettings, cl_save_settings, 1, 0, 1, CFGFLAG_CLIENT, "Write the settings file on exit") MACRO_CONFIG_INT(ClRefreshRate, cl_refresh_rate, 0, 0, 10000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Refresh rate for updating the game (in Hz)") MACRO_CONFIG_INT(ClRefreshRateInactive, cl_refresh_rate_inactive, 120, 0, 10000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Refresh rate for updating the game when the window is inactive (in Hz)") -MACRO_CONFIG_INT(ClEditor, cl_editor, 0, 0, 1, CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(ClEditor, cl_editor, 0, 0, 1, CFGFLAG_CLIENT, "Open the map editor") MACRO_CONFIG_INT(ClEditorDilate, cl_editor_dilate, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Automatically dilates embedded images") MACRO_CONFIG_STR(ClSkinFilterString, cl_skin_filter_string, 25, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Skin filtering string") @@ -89,7 +89,7 @@ MACRO_CONFIG_INT(SndGameSoundVolume, snd_game_volume, 30, 0, 100, CFGFLAG_SAVE | MACRO_CONFIG_INT(SndMapSoundVolume, snd_ambient_volume, 30, 0, 100, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Map Sound sound volume") MACRO_CONFIG_INT(SndBackgroundMusicVolume, snd_background_music_volume, 30, 0, 100, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Background music sound volume") -MACRO_CONFIG_INT(SndNonactiveMute, snd_nonactive_mute, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(SndNonactiveMute, snd_nonactive_mute, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Mute sounds when window is not active") MACRO_CONFIG_INT(SndGame, snd_game, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Enable game sounds") MACRO_CONFIG_INT(SndGun, snd_gun, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Enable gun sound") MACRO_CONFIG_INT(SndLongPain, snd_long_pain, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Enable long pain sound (used when shooting in freeze)") @@ -121,11 +121,11 @@ MACRO_CONFIG_INT(GfxColorDepth, gfx_color_depth, 24, 16, 24, CFGFLAG_SAVE | CFGF MACRO_CONFIG_INT(GfxVsync, gfx_vsync, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Vertical sync (may cause delay)") MACRO_CONFIG_INT(GfxDisplayAllVideoModes, gfx_display_all_video_modes, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show all video modes") MACRO_CONFIG_INT(GfxHighDetail, gfx_high_detail, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "High detail") -MACRO_CONFIG_INT(GfxFsaaSamples, gfx_fsaa_samples, 0, 0, 64, CFGFLAG_SAVE | CFGFLAG_CLIENT, "FSAA Samples") +MACRO_CONFIG_INT(GfxFsaaSamples, gfx_fsaa_samples, 0, 0, 64, CFGFLAG_SAVE | CFGFLAG_CLIENT, "FSAA samples (may cause delay)") MACRO_CONFIG_INT(GfxRefreshRate, gfx_refresh_rate, 0, 0, 10000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Screen refresh rate") MACRO_CONFIG_INT(GfxBackgroundRender, gfx_backgroundrender, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Render graphics when window is in background") MACRO_CONFIG_INT(GfxTextOverlay, gfx_text_overlay, 10, 1, 100, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Stop rendering textoverlay in editor or with entities: high value = less details = more speed") -MACRO_CONFIG_INT(GfxAsyncRenderOld, gfx_asyncrender_old, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Do rendering async from the the update") +MACRO_CONFIG_INT(GfxAsyncRenderOld, gfx_asyncrender_old, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "During an update cycle, skip the render cycle, if the render cycle would need to wait for the previous render cycle to finish") MACRO_CONFIG_INT(GfxQuadAsTriangle, gfx_quad_as_triangle, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Render quads as triangles (fixes quad coloring on some GPUs)") MACRO_CONFIG_INT(InpMousesens, inp_mousesens, 200, 1, 100000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Mouse sensitivity") diff --git a/src/game/variables.h b/src/game/variables.h index 4cefb6bc6..f65cea0ab 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -70,7 +70,7 @@ MACRO_CONFIG_INT(ClShowpred, cl_showpred, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE MACRO_CONFIG_INT(ClEyeWheel, cl_eye_wheel, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show eye wheel along together with emotes") MACRO_CONFIG_INT(ClEyeDuration, cl_eye_duration, 999999, 1, 999999, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How long the eyes emotes last") -MACRO_CONFIG_INT(ClAirjumpindicator, cl_airjumpindicator, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "") +MACRO_CONFIG_INT(ClAirjumpindicator, cl_airjumpindicator, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show the air jump indicator") MACRO_CONFIG_INT(ClThreadsoundloading, cl_threadsoundloading, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Load sound files threaded") MACRO_CONFIG_INT(ClWarningTeambalance, cl_warning_teambalance, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Warn about team balance") @@ -98,9 +98,9 @@ MACRO_CONFIG_INT(EdAutosaveMax, ed_autosave_max, 10, 0, 1000, CFGFLAG_CLIENT | C MACRO_CONFIG_INT(EdSmoothZoomTime, ed_smooth_zoom_time, 250, 0, 5000, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Time of smooth zoom animation in the editor in ms (0 for off)") MACRO_CONFIG_INT(EdLimitMaxZoomLevel, ed_limit_max_zoom_level, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Specifies, if zooming in the editor should be limited or not (0 = no limit)") MACRO_CONFIG_INT(EdZoomTarget, ed_zoom_target, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Zoom to the current mouse target") -MACRO_CONFIG_INT(EdShowkeys, ed_showkeys, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "") +MACRO_CONFIG_INT(EdShowkeys, ed_showkeys, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show pressed keys") -MACRO_CONFIG_INT(ClShowWelcome, cl_show_welcome, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "") +MACRO_CONFIG_INT(ClShowWelcome, cl_show_welcome, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show welcome message indicating the first launch of the client") MACRO_CONFIG_INT(ClMotdTime, cl_motd_time, 10, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How long to show the server message of the day") // http map download From 98b3fe460a7bdbc6d1a6c6d9f09cd97d5e57ba7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 29 Aug 2023 20:26:59 +0200 Subject: [PATCH 040/126] Fix typo in internal name of `cl_assets_entities` config variable The name used in config is unchanged. --- src/engine/shared/config_variables.h | 2 +- src/game/client/components/mapimages.cpp | 4 ++-- src/game/client/components/menus_settings_assets.cpp | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 2cb189876..a45ea0de6 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -40,7 +40,7 @@ MACRO_CONFIG_INT(ClPrintBroadcasts, cl_print_broadcasts, 1, 0, 1, CFGFLAG_CLIENT MACRO_CONFIG_INT(ClPrintMotd, cl_print_motd, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Print motd to console") MACRO_CONFIG_INT(ClFriendsIgnoreClan, cl_friends_ignore_clan, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Ignore clan tag when searching for friends") -MACRO_CONFIG_STR(ClAssetsEntites, cl_assets_entities, 50, "default", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The asset/assets for entities") +MACRO_CONFIG_STR(ClAssetsEntities, cl_assets_entities, 50, "default", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The asset/assets for entities") MACRO_CONFIG_STR(ClAssetGame, cl_asset_game, 50, "default", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The asset for game") MACRO_CONFIG_STR(ClAssetEmoticons, cl_asset_emoticons, 50, "default", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The asset for emoticons") MACRO_CONFIG_STR(ClAssetParticles, cl_asset_particles, 50, "default", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The asset for particles") diff --git a/src/game/client/components/mapimages.cpp b/src/game/client/components/mapimages.cpp index 0076eb88e..3ee792eef 100644 --- a/src/game/client/components/mapimages.cpp +++ b/src/game/client/components/mapimages.cpp @@ -46,11 +46,11 @@ void CMapImages::OnInit() { InitOverlayTextures(); - if(str_comp(g_Config.m_ClAssetsEntites, "default") == 0) + if(str_comp(g_Config.m_ClAssetsEntities, "default") == 0) str_copy(m_aEntitiesPath, "editor/entities_clear"); else { - str_format(m_aEntitiesPath, sizeof(m_aEntitiesPath), "assets/entities/%s", g_Config.m_ClAssetsEntites); + str_format(m_aEntitiesPath, sizeof(m_aEntitiesPath), "assets/entities/%s", g_Config.m_ClAssetsEntities); } } diff --git a/src/game/client/components/menus_settings_assets.cpp b/src/game/client/components/menus_settings_assets.cpp index e5f328917..b8a2a9960 100644 --- a/src/game/client/components/menus_settings_assets.cpp +++ b/src/game/client/components/menus_settings_assets.cpp @@ -309,7 +309,7 @@ void CMenus::ClearCustomItems(int CurTab) m_vEntitiesList.clear(); // reload current entities - m_pClient->m_MapImages.ChangeEntitiesPath(g_Config.m_ClAssetsEntites); + m_pClient->m_MapImages.ChangeEntitiesPath(g_Config.m_ClAssetsEntities); } else if(CurTab == ASSETS_TAB_GAME) { @@ -545,7 +545,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView) if(s_CurCustomTab == ASSETS_TAB_ENTITIES) { - if(str_comp(pItem->m_aName, g_Config.m_ClAssetsEntites) == 0) + if(str_comp(pItem->m_aName, g_Config.m_ClAssetsEntities) == 0) OldSelected = i; } else if(s_CurCustomTab == ASSETS_TAB_GAME) @@ -604,7 +604,7 @@ void CMenus::RenderSettingsCustom(CUIRect MainView) { if(s_CurCustomTab == ASSETS_TAB_ENTITIES) { - str_copy(g_Config.m_ClAssetsEntites, GetCustomItem(s_CurCustomTab, NewSelected)->m_aName); + str_copy(g_Config.m_ClAssetsEntities, GetCustomItem(s_CurCustomTab, NewSelected)->m_aName); m_pClient->m_MapImages.ChangeEntitiesPath(GetCustomItem(s_CurCustomTab, NewSelected)->m_aName); } else if(s_CurCustomTab == ASSETS_TAB_GAME) @@ -708,7 +708,7 @@ void CMenus::ConchainAssetsEntities(IConsole::IResult *pResult, void *pUserData, if(pResult->NumArguments() == 1) { const char *pArg = pResult->GetString(0); - if(str_comp(pArg, g_Config.m_ClAssetsEntites) != 0) + if(str_comp(pArg, g_Config.m_ClAssetsEntities) != 0) { pThis->m_pClient->m_MapImages.ChangeEntitiesPath(pArg); } From cbc1044b98f5a670a8ea72bbf84a3fbefae0d3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 30 Aug 2023 21:57:00 +0200 Subject: [PATCH 041/126] Fix some menus tooltips not working anymore For some tooltips, the associated UI element ID was not being set as hot item, which is required for tooltips. The tooltip for the "Dummy settings" checkbox was only present on the tee settings page but not on the player settings page. Closes #7107. --- src/game/client/components/menus_settings.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 80e2f86aa..bd0e8e7c9 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -323,6 +323,7 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) { m_Dummy ^= 1; } + GameClient()->m_Tooltips.DoToolTip(&m_Dummy, &Dummy, Localize("Toggle to edit your dummy settings")); // country flag selector MainView.HSplitTop(20.0f, 0, &MainView); @@ -615,7 +616,6 @@ void CMenus::RenderSettingsTee(CUIRect MainView) EyesLabel.HSplitTop(50.0f, &EyesLabel, &Eyes); static CButtonContainer s_aEyeButtons[6]; - static int s_aEyesToolTip[6]; for(int CurrentEyeEmote = 0; CurrentEyeEmote < 6; CurrentEyeEmote++) { EyesLabel.VSplitLeft(10.0f, 0, &EyesLabel); @@ -642,7 +642,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) GameClient()->m_Emoticon.EyeEmote(CurrentEyeEmote); } } - GameClient()->m_Tooltips.DoToolTip(&s_aEyesToolTip[CurrentEyeEmote], &EyesTee, Localize("Choose default eyes when joining a server")); + GameClient()->m_Tooltips.DoToolTip(&s_aEyeButtons[CurrentEyeEmote], &EyesTee, Localize("Choose default eyes when joining a server")); RenderTools()->RenderTee(pIdleState, &OwnSkinInfo, CurrentEyeEmote, vec2(1, 0), vec2(EyesTee.x + 25.0f, EyesTee.y + EyesTee.h / 2.0f + OffsetToMid.y)); } @@ -2841,6 +2841,7 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) Section.HSplitTop(LineSize, &Label, &Section); UI()->DoLabel(&Label, Localize("Colors of the hook collision line, in case of a possible collision with:"), 13.0f, TEXTALIGN_ML); + UI()->DoButtonLogic(&s_HookCollToolTip, 0, &Label); // Just for the tooltip, result ignored GameClient()->m_Tooltips.DoToolTip(&s_HookCollToolTip, &Label, Localize("Your movements are not taken into account when calculating the line colors")); DoLine_ColorPicker(&s_HookCollNoCollResetID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Nothing hookable"), &g_Config.m_ClHookCollColorNoColl, ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f), false); DoLine_ColorPicker(&s_HookCollHookableCollResetID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Something hookable"), &g_Config.m_ClHookCollColorHookableColl, ColorRGBA(130.0f / 255.0f, 232.0f / 255.0f, 160.0f / 255.0f, 1.0f), false); From ba09a3a30d2043df4100fbc18ca5342621b0b2f2 Mon Sep 17 00:00:00 2001 From: Dennis Felsing Date: Thu, 31 Aug 2023 10:26:11 +0200 Subject: [PATCH 042/126] Update country flags by JuralBOZO (fixes #7090) --- data/countryflags/GP.png | Bin 1058 -> 6424 bytes data/countryflags/MN.png | Bin 1580 -> 1965 bytes data/countryflags/MQ.png | Bin 2860 -> 1637 bytes data/countryflags/MW.png | Bin 2240 -> 2008 bytes data/countryflags/PY.png | Bin 1470 -> 1794 bytes data/countryflags/RE.png | Bin 1656 -> 4960 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/countryflags/GP.png b/data/countryflags/GP.png index bcd3c24785f985aabdbadd34507e7677de97d39e..33a02c06c0d4b16e106a5b0561f3f9581c543672 100644 GIT binary patch literal 6424 zcmV+z8RzDSP) zd32W5z3)GJKhrxTZwLt?OopI>f(QtRb)IT%i?_8}d)uq$oSsu%mU>!mTkE>5+Io81 zrM5lCUaxKSoO^BcTx-=TiXslJ;!qGpR6rTSlmtQu$vZ#e-uI6uabOTKk+_!MwcbVY z@MQ0u{rm3Uw10a?Z~$C%(M1P(d@$(Vy!;6U4q+Gqh{JvVsi&UW@0mG(*153sswtFIusgmNxpLa8g97mNv^u~3YKlChqTwHKF^={98nYz z1VO)tHuM@mXvn7c>ikt)eff)k;PurF-2L!8$dm`i!zeo3aQ!0AKVv;}r|sbN)e~5` zWfUZX!RgO88DE>{mLEOJ#Bm+``lk^|DyQScpgXhk=Hea3p1{3Q$ zQM;4Oow|+Bp0gIpcJJHL11EeK4g?4cjv1Y0;?aA+Gf;tKdR-giC$>W{zzRxeaBXJH z+)7O{PoxBtOlHp9K}FIBHCQ$W=FDjVSHv1$31-dO9+OV=f^}L!R2neh!%z`GXo%!{ z6P~g|Y$Kw_=$k_0*kQaPwSaH{h8Y@hTS|~l1|WKbEies*)gy>V#-H8@W?u*mN_J}% zfe;$odjIzW-tSL~_&p^K?ei!XbpP%C2XW*HAT$&cj5HLL0EGm*b4l_=2ZTl%n)Y&% z_Vx_8HmOiD$_Wugr*HjfkVS{OgpcD{;G4AU%0SpX;YLJ9TZ%5_kGyuBJi1G{W*PbzvQ)tf5kOdyhw9%EgLqB1}6kVv3kd7+BS{l($BobvIlNw@0_{U3DFn9V^M!1Ue zPFc@GzrTw$O`~bwRSAiJrp=>h&!u_vp1V0`?nXwG6(`Tw%GbX1DwGc37;1mOReukT zECIB@vLljq0IrEv5`4JsJ5R7{XBsc(;A63J(|B&WZV{Fdp+kxGO;RGnNd&!%o3$XB z^sse=@=bIosZ6_E|Lv!Ee#Nm+q1e(=L&gla=E~=w3`$9GEm8>|(@=d{bfytOC{Rj3 zXfS)mHZDAG4bQ)MG*m~t{Ms@6{Uysde#%aCV8rkFAdQ1`Hb}yUBTWD^BHNMX$rq=? zSfABf#_;3YE~YwD;@HW%d8e(8?4Bg!#Fv zz0XEk5v?sF*}SQq8Pi+1^`7QRPv$vbOPRacN|YWHyuCi zv2#~74I^{Z)#i{XMWNvE*Ja1^!iq`QV>-G2{?k~tZah?$KWyT31W}oN?a)9H;K-+f zwE#21H6oV2)d+TgmGF7@?RrYSg_8(L8v(8oU}#=k)d+S7w(eW5UJFQhw1x()Z%qUz zU{As3-L0c2Z#Id1i4Yo{DU&TEczI0&RCb43eRaCEfJ~XDTnch2s4TN~Yb~DAY~NLd zjwIV!D`_a_3v@m^2E6=|;ozOSme`g@jO?0dfq~_(< zr!Z@33tu{GEhkNF;cv^QfK>7So6-Kl*HIk%4>E>+Gbofaj_Krw-+PL)W^9B&Flkx` zZ>+0lWpACnLqW%0`PnBm@%?L`V!V@wghE&*Z{KhU9ldq>4hNAWWrckIs=qS-(`#@_ zf+^E>an*)b_@93`gHRnL>v#lKKMaKrurDIX>|^@>so92 zv|DyiXfP$ro7}>p#j|OgvYW>i&)~TirlJf*%brXh?fyqVNk&zbP-UA37tiLbbJp`u z*L{uMdn#$qCn$Q>{%*}dY<~}iu@BIP$+JVAePa@|S8>|On~|1c;ZrjRDvOZv2R8X7 zAVhQDl9?pE1V8xhlaxG*dmlOl>aww~G0@32GorOK%_Gm8$eiPLlBn?6wL8P|Rg++3 zAvOd1LV3W!Lcf9Cuo_qbh-BY>E~R0F;~(B{lz_AWIypE+AsHy=1uw0i#G6fx zFfszi0)2qhI1sRakr5kn)hv4L1e!bQp(X;u*`IJa0__5cgSDm^z)>Esp}rJ_rqgMx zR}MPp!y1S%6hbN@-yFPZpq|c@z_J3gmPC<^eXzkSZ|&Y8j0}aT#6i3Al!vcAy4G)3 zL+giHKHMQJU%q_5X9KDMZqG05J39p^CD2-6^f3i{sP=UT!sz}w0+kR5H%`Io0EET+ z{SFc#A|;Vh9dMFLTGOr+hjK%&VcLL=XFv(CN2WzzXH5$*hzlm-#WDc1ue*mMb-dyy zM+939v=8PuMPhnLVX+MgtSJ#jTI0T9B5aLv1u~^Mq%?Yqg9P4@)^wn72h!>7`*8XO zfsR%AVSajkS3}3q-tq}9mIB{U*pcW}Tl;-9NLwRQ3K4-wX)+gkpkotTb(tFrH~^W@ z)PAo-xI-eOMu&pTd>j90fl(bVRjt4nsj0lgBMJm6Co#q;YQ9w_ zQW7MLxZFk5XcBV+tO*JdkUTY{>Z>mKCt?p>R6xyjC9KA1-#wWDLrLp{27QB#At69d zsoxjZrPx)e`04psE?tx6#xHd-zGN^mC+Rd4%P09fH`T?pG-hnZF_l1SNQQ#dqkVQ) zs8~ygc%`P!4lpMvbO7#ZgNpeswcjn_cNvsdTGV{MMBNVy_?rx@qa()LlE+AE%<+oU zc>$R(yGR>UR$$avp(da+sVSS9(12GOJ<^^;MIa=EZIV$p z6_7QW@;esA=MvQ2QsAgRcA?q?YLB4yJ7p>^2#8uGnF~GArv<3plDg}Pq)rZy2{7vw zb+;A}DahTQ00p9w*jzLy^Z+bbUu&{j59#5`At8XNV0$Lyr3RNOPjcDYX(sQn__uTO ze0f=lKhG_p(r`kX&CAm~cBd7OPb-nP6t7J5XsQYM?WuWY?X+pC3Mn5OV%KZ5hSJLx zvQpFdVkfoNmMA=D6K*j`pB|98#6!CRRS<|oJnh;SV1V|(8m};GHQI+!KQ2;no=>pN zAjnH9|BJ_jgF2aW)+zpf(#^p>FpIBf|T5CgMg>&561M}^zayEpu#4=|Hl=kI#iwn|OI@_0_cRxA z6rlr1PK`*NTqJBZ$o?sTkKQ# zw!se`Gr908k1Eqel?1vV`WHB>IDa5ajlaRfnxGhcUml}c(f%(L_#2GF7Ji^LC9TP6 zHLNaF7_vM-B%sn0EO@1oqNT_t6cxVY?_aB6TUAJHQL?KxBA-_5vLc?D?9%Q;)D;XW zi;{#VSf32JeU`(d*^2EadHig%$D@Cg&>_*qehR+_D)tl1YQ^~fn~6OCjpg#k`8Gc}3SVpNU6cj+ zyr$+)og_{SC@gU(J#FEoA;`tj4;cPJ{~(6GB#ij5&rw|l&m7~@mX4U$?C>8G3w-hI z6g7F1=Jf$D9_6xkgkp~CuzF;`^`jgX%__5Hg3FY59JaJULzg1yY25zboN{1j5NDMM zDxm3yX|nexkWwL&8et8>4(1WSupofYP;fM-ZFd;qN}4kffA?ydPDk;`)B<${gRi`k z;^@6LH-5T{Z?CPSBdz(TD;0CTq{x3IfSf?(!F3tDS^^O#o4Ouw$UknAXb8{lEA(xLDG?I zo8vM5w2;Q*)xg{TeV|-{HC}Pl16hm_3V)+Xd8J8ykxkSg<9dUS5rz!`deC!qfB??k znqYHHz%S3r^N-8Z%-rR$Gp)F^CCToR;#c!>)C7_f8Z_T%5QBQb!+QuJH7_9CBiVIB z1>Tz`!UoWYp}%gU*BEvL=z;G3yfr4^a~qPJ((JH3Q)I#O71Y?^eY41~#~O?sEvOo! zkR5`kw+Dy%0AWM0O|tWbN`j3BBGL#GWMc699t;dy0tldJD^A{SV=6&gB4W-4hfrzm zn_lFk-=%4u5z;X^FKv&>af$u?q!lOz0`pyL-QviBzu$-$p} z?F*d7h=x1!jJvTwa-u>K7pkD&uS5N~U|4H_16Ip)r{0S%S`_|bp_M>nVC8vc z$E1P0qiJb_jz*7hH~1L$rD=OpGTJvthJwJ*1X82KhY2wp-GgCEfJ0Tw$fQOmz!(!T z>PID#vjhB%21bP*w3#*x*wPNSJfT_BqBz<&sCEQXjw;h+!-#^Uwk#Q!GZ~*Zs4h#A zfxuJ(zlRjvI(I;i_w)n9k^qs`bbUm@gpR-(r%0XWlbRb66`<{h6&M*!&4LoDG&obM zOig3G$A;Y*qj&Bm>QjFxB#d(w*CX`k1|#Tpa+ zm980+Vkn6)ED5lm-QNeofS^T^{m%rs`ylcJDwK@-_iS9g?K0p8It&4A>kVFXBaY9S zOqdt)`FeQiK9h_oaJnN9p}|t1gupX3uA$jBLeW$ck+T(AK)q*B*C{!<#i6bsiH6V< z8+HUZ+$!0gl&cP6LpyRok<@7cg+&f#ox*O62BdHki0l6>O=6Eh*L^m>P6k*{7<_St zWZr1Ox}9K(xXQ`su3mO6jU5S+0VqSzkx*2G0!Il957v(gnUFOZlQ)Ql&}|%61n6~@ zYy{5KkcuyOSQ8`KZ>%8q7n||F%ORZD%If}P2Q?re+$$g<$Xwua<#7fVe!GH~?SN<3 z_>7T)nov--;!#HeJQGsBz*3M;K-N)Idy?wBKq+Xdib9{(>T!g#ILCfX1wWp#@v2LW-4|qjws7;q4Up z#Lx;KhEX2iP=I)}m;EZ)z&Nm>3UW0`%u}Oph-hYZN1|F5#~?*!lGg;dY74Xu`ap^Pc2;=KzF3 zT-agm^L4i_4EG8|Qe%u%6#in<{M8Y3+?FEqwK7&?gpp8`mRLmX5=>3ORw$`CXN$vv z<(1SGB`e3fI2vZQI!GyK84)t7Y>+9yo~nq6ofgp%J_UR*3^@S?;&hCVKnPvGPqO!p z6sjaJ>m!`w1G2wQl3#42Bk;Ex@mb@`&tOA1;QJ00VYFLUaqn1tX!6Y0b#1%j6zNki9?AeQprSZ<=)eXA(_JhW1~j z5J`wSBrV^p#BY_P&I~BMWYcz^R1 z3wwxAu;W`m&fc8h%qE97#|QD>tO$mW@L>oD5NSv04%Rn57JJJDx+u^Jq@y8>SAYIb4$_IWL)an- zwi?JpyvmD9t@wh@;U=j;!k`o2h^+dLf_OjN!-($>5Zz9kFu>nne7Ka$V;ICnM-Br< zfMT)8_fDIQ)|xO3@jQ<(3<<*!Jxor*euDimUGR@ijW#+r~_wAlN!CoB^rLbtxqI@9m|8U)P*D-XtqUIU_Xq4-( zzn-qR;tGZ?M??t#ELgCBiKojiznr1Vm9hs={o1MJ2E1|#U4MDyl}tPWkddk-8X90HJa>aTkAcmyCTRZpGB9sx+a@Y6;BuJ}q&Vt(J@rH|cT z&!J)iz@_w;y?ggkC={B5Bmv+uJUq;VYbSu%*%y@%u=2EX$aTtDDO79#JB-c}0X7>Q z^oQQ6h719KfPdaDYXD%2!k>DZtO0=K20r=`07gFoz_A|zVC64G0Kk_z^&^1Dads)> zZ#1NgegqIZ!$$4T8l(uolREklK-)tG8gkbQFMqxI=tls9KR3{LxRzq)*@Tt9UBQ() z`Vm0%WE+#upti@2!UhmjztRT*wBBQ!4q)()1|j9INq;}}BY=T#8=3rRVyD}LTlN2` zx5*no>>L{hi)Ay)&%FS2UlW?go>ex3_GoCOSg-;gl? zcJ**&3U|<2gM&#YPhrv#K=^P6jb~X8wA^mWI^}LKBxS5rWCZng@+v}H95=_q@p63s zVJAN)pb%7GLRWBBDzMjs{9}ClYy4Fj;Rs;=t$*h13%@TKRj1q4hNNgdfdsCffW_9sPZvI6@JCDS6vKrgQ3lW=;8jIj!{1Q5Mm}ViA}{zHA_X?Ftev z_7DJnh?I;V0DebF89@MB6#kNP>PGsfR|69s>e@|QW^qy?j)*uGXRWwxdLD;T)2>~ zz4qGae{Bj%PGttb#9wpGH6n;d01Fl@$OpRm>Z>&>9s&FdD5I8f%?YyY00000Ne4wv IM6N<$f;N!y;{X5v diff --git a/data/countryflags/MN.png b/data/countryflags/MN.png index 2ef72106d2ceb59f0cc11b6dae87bb0c38517dc4..976c44c4d8ffbe4d1dc6ac1bbad32a388652cebe 100644 GIT binary patch delta 1952 zcmV;R2VeNC46P54BYy{UNklU5F%C6~}+Ks(YrVzjk}RHqNRsAb!MvKA31= zeF#|-ge-y(UjmAvm7q4~B+Y-PJX9{`Z`7@42^Hv400=&Yan+`Q4B^d2>qt z&{_kigTDXLrAxQ9W%_tMDG6eXAqav)pC=W-2x6@z2m*{Thdx)|1W+gx_}=0h{W{%& z@I|O*r+DPlIW{LtAU2sVRvwk0$>o=R%yiVnr~`Z9zVPi2PGgKAilRfGEA;>)h}N1g z46*j`g7^AxJ%1?)5{m7c@gWt!NMS^%8Phl8Ln?rgqGb)eKIb{907itawKVMEv;JF2 z1uz1L2u*7SGUJvCUkpLq!opCArhe{gS$EI1t6euf|`(2 z1bfi227QzTAX<@~3(3qyh)#A&b=ZkSXb*UL+pPcuynno9>Ju%dzr2E1u#nJn{wOf$ z-VcriAd#Z*=?3`+nq=l83J*4rDD?u6Oz#Jm0uWFWLiy1uYSJPBls{WV<p$K_y^5mwK?kKUC60F4)?QJJ*+C)NAG zu>iy>N`IfJk^fW!0Ne9v{^2;v8($G5+55qv03`4@{)JWMpS*Unt@KD0|A+6V_Un1n zt1R%ifq)x?ZU-|ys)Gy32bzcjB?rTMQ|IEY0R&Ox7KI6%en;{IvQn%XafM?(W z5Ut34w9D+bZp0b?1T-$p(0jeW^jB8M%mzq1PDS#k8tC;&sy{nH?jvo=pWlEG@L~NY z3-tb-KQc3gp;)R$6FPGJ+YH_VSn$f4{D1v*&_E8lf6Y^Weuj-_=9&J|3dbH>1E79! zj_S|n3E#?5I=xQ*fjaa6AH351kr@>Xi}4|;5MYUWvL+sPSOo&n)f{hs_X9MZp8=rr zN-6H)+aV*|`do&0hL91aYh$-R48{jXNuU_T+_@$4A8&#NiQ*8TzHT84omWfPz<>E- zrM?o0(6NSwgUO!#a90e-$i!ZTPlWXG6XHa5D__g|+i1 zQNED7w?qDuO>E1jak+wR`-oP|e)EQlOX~|pgq~OevGhcS((v23@c>)Pz-P}cQ~B!K zbYCr^JjmVGrt?af`o+rsUktJatbdMQ2Zi#Gs7u=#Cak&nd;kHW5it60+~feEqIjy# z^w*XFX#92-FJ~w|yvgw|u3#G;5*|G16A_5I5cR+WgEc+?9yV;TX(FuA*n}St(N7$e zgB7LI)i~~3nEl2QrO#FA{j132JspY&0nIBa2^&IQHloDsSU5vA@3yH6pZ(W&?%j04A~tUHs9)O$gA6;LSX_4|lO$pUgrJ zCE(9SpdffXzwaiIuC+9+A+!eZQ^OwyCm}#h2=(90(YaD4dryz{OUG&cej4%+y_KVO zVJ_Y(b_+&?y3wqg$Vu(|D1YK?7#LogWbOP(a`$$aIlGLPw={k=%f>ScM9UMXTS7o! zEE`r6q%pp)I2#5|eXN5&9U*Ox>T~ZwWd*aDA$u~w%iDJh1A_pnVZ35ybo1@u#MxBv z?715hPHjS@c8WY6kQLg0tkC^?9spyYT8F@n#<+Uaa1#PNq59liw0|xikLUciZL7eW zw1n4larF>rw4l>~tBv-cv%Ah##YKg_g#oOBY<^e>6D2fQf5Un+)&%bY%G8pz0 mz?~W{iXx&YN*N75g8UDs=h~A^a+(AH00006*HewWhnP>dTntYn;BPCn*42WMpLQ4>&qVX3w5Yr0gRJV=VNZD5oy8yeic1MMo->*9=9Dj|^y2EDLv}qK05e1L} zEL98&x`+Zu0XC=><_2{U1&{*lw3#2&MHN5_utG60msdt2Hygd+@qrCMpX)sfGw=`^C>`djDe~z0;v7dPv@lo?zE3%0rYk#e@Q|8G9PMw z@uT7u1yTUUR)5y|`4k}U6dmPu(5pTVpzKKnH9rLq8D&ZVcC%bD_!c1l3ZsgmeCIN%Y77hhw2{#atL5~KmP&{Sr(;%I`U4{X-*&ou-R_o z4m`J6fQh2(G!$N`Au`yM0@$onOfdu~e_n}=|HUzy^M5=jxN_)Q_NlMPT>7#U~@BLF+% z$NLb_xqlt%7WId0OeKo9Cy?390$bkLY+4xfzD*w$g{yq!)mSJgLN+djYDxa_9Qt!Def#FMCQs zbDo!_fDsRh?^J1y&{{C!S|fNqnA8HSW!=8e0<=OEKvWJ^`AGng0c;rHO@K^R;Ufj0 z7Jr`Ae$UW-co1MGwR*+G0+#AY0cb3z^LwK9KYvmSuwApTN-;6d9Y85S{Sunc?V0ND z0!bwR1$=|*1bu&&b>$p@2z6?gaA(kg2r%QjHJJA0a!mbfF{XUH0FytMi%IYOjfrpn z5#yadck>q}y-WN}x7+*^tCbkr~RI}1UyHz5Bk9eF3{`z^qXZ&qXKr;D6Gj(_79jDP*eaNt3d0?;Oq*6Rwd z)lmANiiS;Il)tFZ3gJEpKwU1aqq~E{vj7|jkbh=;U};Mj9sZ>?fzpRmI;H&qZGQ?t zt7wbaI4cE6HUTO>i@&}(KUND^{#+3C%lyuDrH?Bv0U8TD*zu5`eHl`K#1jzk*9NM*tRlZUm45P~P9L+ULyn>AFaN z3pq24_^M#N9dc~}!s{vUQULDKz{oHYWsfS(4?{F%dZ{O%+nvXRdt0$uxeS$p&Yu8M z09F+g9d06Lrir2(RAf&z;tC~~&Qf?3Kng%zf9Zp5Q+U6MmFEVf03sd@Y=18DAhID9 zn?CXKDu5J#%E60nP0aRd6s)*3h-D{p+XF}es3x%D)u4;K=L1;#pbu;A_haF3m1hCu zBrrGKpd)~6S5Te@nX6N==|e9TjZo=>N&$G!0;=$Sd+_uX&KJu~OdJ-f|O zIyYyTGXTKNpx4B*oWrJ)&t-9dUua^9lNqZE1657<2zzi?qcW)gxKijcnal$K@iQCa zwI-9vU@$~PM1+NfMMg%d)#}jDP>aPf=|*-z#gffo$hCM-84J#s!H;H*Mx#_JgMxxa zWz1V4J?R3=wElQ$G`JQY5D@SRWu6j>C0oM4^~Ipn^iD|s?Mjwv{i)I@P#z7A>PJy# z3}vQV$tQ#&f$e;q?HmJ6M1Vr=TcLd1m9%R8iLz+G`XxqiP-}N1!I@}KYy`&*peza; z4hKhc;A%WL7X!+pK#?9C(%N0genPQ|?N(yqmY9&^24uem*&Sw=DTYLJhLDi?(6l#A zc~OvoIE~{J{uX7%rObJZo<+hLB;kb<3Y=gtoR(pv6rGeZ&rv3spxh|}C!vCb3<>3s zpb7RW1~D0wp?^x{gHpOrLO++#!xDN-EVrVh4}p6?T+Wse_^4FgFQL1|RErokiD0J? zR*PUfin(K`RECl=bU;dXh{<0?uvG**gz%sQSIAHShW1LRW)XZWg55$`D}swrtX&8X zOK>k4dRGMFP|OuWho$l^A$drGw~5IYQuz}W#nhODo{}=1Qn>}iD`b>6h6xDFE>)bv zXkQXSnXCt8RuI$-3Sy9;FxiBZslaFt3O+-bl?27Gvtlv|XAxr`$!Ij{^?H>`6&xHK z7#OJ2>B7Uq*)O!*r7)MJTi?V+#eMj{2DqK3*uYNVv`)V?697To$K*6*RIFl`;+a9C ziceMlQZ%&8Q#HNdblD1Z-nS?Bczw3EzGtiC_Wi1g)UtZPjfxm0uRbqAxRtk`w=nO- zho*-q7E?g>S6PFbLVJ6Dp1+`asNEy7HhIpe%loW5c|lpV^YRXe^e?*HY<#1`t+Vfk zJ1etFT(RLFM(XDw^K&n3w>!EvHeFWHts{3?gJWuKadd-sL-X;ow)4vN=Hxovs{TN~ zCqMbwkQ1Y5VESLjuUt?+coV^|nSOtN$=vlb`n`?Cavy72@wt=hj~Bf-Dr&a$&wV;; z4j8+ayz2y5hOcgl^-S8UhHVKmlJ@NM2U<>1fRt+%TN`M9uz`~!E~p23q&(o+f0&Bo z2V58A_*(ITc1|;|tq)YI^8J9mq1KHRT4j)+k2}}t!F|A#nIILFDSv64F*@rgba;^}-UI4dOkx%^JYJRCWbQfeMQV@vS$-CO=*KHs6TYS*L6 zOUtWcKIsiniuKobTpL^>?VafqL-mAY+q<*Uk9IkxA|9vhx8M8TbxsWB7^qlDMq=7H zPnetZ{lsgRnaJY&0m*cSh)O5h9JA!RwwR(s>+E$OIBdxy#668QJ)5@`rl+#PqdB;+ zWRaI&wNeGDf*%*h9ZR%Qprcq~e966v+z#)0yeI#_ z>?hx*I3$gSZcm>*Ims)0owU5><{Ocwp zTZa0doKHCgr$=;|w&+d22`=xe(O5?kGWU9tLyk6n7r)$o<68E4;ec*|z;+uWj4j*tYFtZQHSJn`;F3_wMn2yB?yk^&V;(d-!)KWRJ&1Q2>AjU%u}+ zR#~2aUBER2CZ?gI`w<@eW$f_@BqUrR@a*fiiQ`0gbNl#viUU z`~(7k2KvS1AZ&6tgKKFup!f6-4C0T4QR9XL@?IT+ZlJuT6BI@`rZ7#3pX2~Qk2-Rp z2sF4ts_MH*uEs?bz-#4|wnABMq}Rf5Mcm7Kzmx!Q`dS$jl2zD*&5mdBj*j=Zr~=;C z_eE#VBTz_a`7F~mB)?qQ_lu460PwD!k3gEps=t~VJg)ix^LHFpCaeU+OPq(^??!`5 zGXf-?%7;Q&4whASBK~+T`2c>N6E0=|5WoWAqda}qhm_m~_{XPW$?l`Ai&j>YAA$JLhQ2ua+q104`7%31q3Cgu@rmY^ z(<}N8uB244CCrcsb;7dhbF)Cv0YGnQEcpsja~sgr^BC%c&*T$xSH1WDYL8c>2mt2u zeO{N;E*isTZb=KGj$||Jvz;nB5=h`iAIaw20;|&zFk^g*<-RM+IfL5X#b!5rJL9u?7GeJ<4+b;ZyYW4PZE7X_%hh2y4Hq zN_D@&`y~JXlAoBPIS5Wp=OhgYC-a~zoCqGhSj>=#txo6YL72Dw5dRF9(3>o=giy;) z+kXuJHhYp!=fgYtF6;xZ<8Dqpl!fEq#8Y{24!wyr)+aG_eG(%98$$ax1UjAA1~c{O%$cR|dc^-86d1Z}TF#0b|^43MIki{Nl&i zVQTj_zWTG33;=2F3TD$sDEr1e09Xo%S~uFC!AQco{lB^&(^XN>U+AN;iWUH}j9#8R zd4j5{DpXchYO3QM0J1!O{20~M)ikEm03b_GPmf?o2?0Qsfq?d#|Et*VBSI>Eqr%Zl4}hA<5Q6V;MJeZ@s+U?zBO~kmqv~<05nnVoZ%Ni zG_>5~8$rVU!2Hry#GlB);vI3qLWmIno2(L$o>_ofDLKM9bMuRF@Wf5n`yPYV98Vbl zDldKT{dyXnFMtZ0K~y(Y^!4?T#Bo6}b8Uzy06Z(-Sj+O116)thP)quZ^_X3 z=B|g3kyXG2guy}|(WU@ZeeEZ67%cLIU*uVomX+}t@b+(3G5|)S7Cx78E44r{PDyDQ z9DI+7nF9z)uXhVNh0N?i;oRf~^EZXb9AGrc7b)3v;lkw=6mv0E8MUaZfOA*Ugmd1! zn}aPjiFo_>m-l?heL+N9dp8#kA#oQ}7f$Q|V2&5gw{mw30U?)xn&$ljCvFLgNb(dM zdjV_q#p0LgZd_1N1KJz_i330YR{dSU4Zz**Rap!Af~JgB+z95D1RNA@lwARJcao_Ao?8E zo5!)`imy$a@cS$ed}d%T$|}It>xgikva)glnYkZ}wnX5UneL2MB!_tS4{OL(fI#{j zgdET2iSp2d3wZrbHvg=KN$0RM#EPBK^rSHd0LdO(y~<4g%*9mPNzFxJvGDH#IL=;5 zWpj%&1Ze_*!=7C($4H9k`B-NjH)N3*0TK>hrx#(PWjqNV!#~2}FXDt!iN*Hte+>W{ zj**FzN#1`z)VUYVQ&3n+K$FA3#`6fD4||*sz$4@oqW51Ruz4KH$}5--qz3?g7Yx{yMHzp#KLeI$Wq%LDMCwyh8)I9>a7raQLUCJB*&_4Zgq0P>K~xC;d8kn`M5 z$;DdpSZK}j;?$7GQL4}j zAmbkE6!60o7kW5Dzv?*&ZVs}B+aJKsN&^5a^+vOmOLx9En?3>|RHw|hhfdz&EM8Pq z!0SJolk^FEW;o5sD?(VpMfNtN!2w?X%@MC^_@Hn7YDwnI3IMa$hhp)z2uxiS$WoEx zR?+VVV7mHosw+c27H$bA-{JXL5fCVnr~>Hq!uQ|&2Pz%_$h(5+^-hAU2)DD?F7#;M_D0Q?_J!$#=JtRskPV+Ul_(Syu_D4=ZoH|BfJ_kQlZ=l*ifx%cFzARnB; zX8-_z`}wLubkyqXfxx=>3Mtm;0H%caD1p{t!5dvciTBia0zgMGwz88708rVcz%Xx( zM&sw_=kM>YR4M}k0=&Gu6beN|M1*c$rBZo#c(}Q_xwyE%00;qqC5#9lsuzvvO2zac ziY*0ALiKkdoEAcqit4u!qEpN@&4ef#;UuAYGypt+>Tg5XS6QatVf^#Bu#qsy$M_wD zX(dxY13?A|1<;727_SyLE@7_eV43D(ydoz51ZMODF3iJ>&M*Z%q*)Hb@CwWHuNdzb zX0#vGOJ^9wA>3qCKbv7Fh3RK;K_w%AA`vKWRJaN!m@=}cj8A<7s>`ZHTP%(fU~OC#tc3BnD8 zNdl_pPGvR`CXYFm?o_OcB`#p{%b06?X}C27VS(^P!epFdsl|AOOggwa;qgJbRhv)kGaDoocQbU45SAV|(ns4rK*6oQN z@ZFjM07U;cgLjfH?E(P!o}bDyY>(IA&&xZ$3l{Xuk;K^jsJJ+D{eAnQ8qP$VJzwds z4F6L@W&UgNbVzVX_)xW8XXwzE9BD;a!fL_A?K_IEIMm4hJ3s$5uI=&1Z28NPC&SHd zneA_*zC_~zliIJa4imtyM{=WajBCeRIA`j$|65D@!}@FO$GpjCDc?fq9y4%Y&hE5v z_kv>mwRuQg#Fy`W=aNOIFDDmGng6Knd%C(iU~SLvPZ>ua4W^xcDml35m&lEZy1=cH zyBxx=KpUhR&%S+SlTtM^wYK8=QeaA1MwX`9{t4H8eQ}Vs`grYtLbOTteC?2HL?e?x ze>>NTRQ<-ihez82$IN?026@{1 z_51cJ$Ga9RvvPA{gA%vrFgGqF$`5xuf0C)w(S%nW;_Cob=2r9Fu~D?cJd}D2`FbUsW%OCU0vd zo_l}N-de;=Mq_VHf*ScpOc1^2UCwG3Vx;58)m2TqcIHuJ<-$C4F6rgxitC@=Gp)eI z`&C64AJkZ-^Gv3}2Mx|oIRl{TPCQ8?g|)r1N%t4I$BTWFndt=!({CJN1aQq%clzgi z!5!^mm!uaVJ+et-^}S`5-UrsFPpHuI*g=n9%@Kk&VT+N1(SNJw7H4m+{Briqd_ar?_@ky<-Mx+0l!}7Bg57E-jINh%k eaS?vIH5$N5mm@lV)5FlxZ}9UDQnf0hkNzJ$P*wo| delta 2239 zcmV;w2tfDP55N(S8Gi-<007d~e}4b~2!}~TK~#7F?9(*?#6T2;;dQWygRqY0bhXIF76FEz2sg zYrQ;xooKumtP76gl*3aD0D4?0N2eG7j0G*LbD3fQuqiaTOn)^17;-hM(+mLSf?f_! zH2~;x1p(mwBm;mxSEvR6OYz|SlmdVOSE&X7tAqEG3IK+JiU3|MZ|@p_Bg@+8*tU^m z+=^`{W6#9NAjvSc?PQF#ZL3^0w(VJ$xj6rSzN+4z>O+aAR``P8kAlEj0%hqE_NUpQ2QxJ}g(&L1KyR~A!B7i0A_+%FuZ&x(S_-*?h8Gpaq@vIQxZu1Ty1wsF|b(fIk zZo@X2bE~ixM~BUzGVpcl$9_eCN4FbUWjJB)hN~$VxHvx%*Eba6T+pIFh;XNV3$Cup z#A%mMeA~7g$Hp4*ZO2|w01|*<<5p=SWW6ENtW)-QHUfYZRC zR=4nJvt+Ez%*KYr%dpJL4_hm$5E~GT)lqYh9h-n4yQx^>?u~?yaKs0P!E4kw+0R%W zBkwOvSuOho$*Zu^FAzJbYh}&F?p|;kHj4WeH-FQ>A(+*F(8HK^3DBuiC###EpC2+Z zGSH((4-^*{!{6T@vuDr7)TvVu8XAg`BS*@-E?v5C6zkl%v)Is~L*-)2fddC3B_#!u zCr`$=1O^78XV0E;e8!9!7&K@QReM<3x{+u5^c;qLA(bH|Py zD{8^Dh71{k6)RR?>(;FpHf)&I6hIMxa(_N|?p%@Y?%lh?!@~pR<>dnYv}x01PFPqN ze*XDqJZSs%*I$vJpD*v3IB}worm(P3z8}?P;lhQ8iHV^aXpI0$0X#iD#Y;;{N-%u* za2Sn70wrGi-FM&NVYlCZ|6Otiu4yuvFkrv{stNM}h7YYNfFi*3>C>@!^JXL_CVxV& z*AsN!Iwi03_#1D$fk)l0T)Dz9B1B?-z%{8Tsi~==5Wc>?S|fl`0D_;Mo{p@nEXn-) z_wSF_Y$nf-M;ze8?MWeka+-; zK-CA(8Ua}U^RpS3TU%RESy_psq$I{|9=8Yqm`f~PyjVVi3cw^YAt8Ze(-Hxc0#K5f zr85C!*Nz@*i4efv0878{@No7Ns4QAi07U>FA0P3~s;Vk1S+az+JG**p?SDRT64<+U zZ`j${vA@92VL8aY02P6+X^8+z0VYhCfP#Vo_U*-6%>eD*y&E@f+z`8Y^QPFXTerk+ z-@c7IckYPYy?a;e+i$h{Z4MKM4z2%a@5t;@r{P^)~>#pwU{%4!5|c@; zOLbv5(P|$+5rEx4a|4LUAibI8ATJW}^70~hJi|w?{=X$8foBfE@4~q}6-@cBudkQ) zb?w@f>nj4>ivVN%$AA1e<~If-Jw{@L+lYs3SDUgC|Qm>`74g~B1o0m-?^@Lc4HgscQaCPgBrE(fDMMkBpAo$K&D z);8@D!1)pR`y+6+_34j4CKC*hoG&&X#1=k{v82GdOn%PwYdgItDEf*;AvOr zTLk<^w#9pw2&l`RUV)?+QV{b@i~wKsPO$)7{Xq?U+WnC6dL~NWErUz5D_Z|a7@syD zG=1DG@2&l?PJh;#yL+BBfK~)BZgG6#A1m#Zbuw1GUqLk>xbk=NCoKrt7J~e@3$Xsx z4Vb@Yfy^QBCGVEPy~PuAcFiM6tO2qjfa6YQi~V4f`j}_qxKF@25@hjuN`C0JaIAeP z6~Ws=sTO>l3SupQ6#*Fcb&Zz);g}mx{<%+}IWh;}Xn&6LB!iU!Rs=AZo+E!z}A^=N0bC=K7OcB7e%?Y{&1)gmXKzY3)0AsyryG?n$ zA^?r|9Z1cZ1Rc3M1CIJXMZ<|uU{|yKb9Uc8B#s@Gd;Qyio_y?h4H(!PP&u{<$ N002ovPDHLkV1gGPiPz28Nh!U*^VvCiW5mu9Cec%7Ok@(?IvU! z9H#`krMPT$*=*B8>7j?Er7YyoQwybsvV}qmdnq)>Qd%04Krh{x(BL?vn>Z=4*F}pO z*4+kNN3t#19!aD5qj|4~tc|;IvT;2cjimX3E@JrJXWs97-+%mh@&wp|bUMA&*Uv(3 z=U^Y_sMTr!ffqgg+O=y>eb?b&K0YpD+cuh}?KqAvKocT_K-YCl)7)_^#{}r@?d7Nc z_`s=fKF{I8KnlY!FbreIvHTUF2{BC*UDpvp>^PRIYu{PgC#e2VVeB zQ}5~rUjWbIM)iX)fM;=|`oZD-0HV6{ElNI5s=sUto_~4or`Nf$92H=9`b{UpwJoc< zLAU;#-98^?9UIG00bY9VqLbm;5>3;n<@1D|x7+8fw;UU6oo^F4iN|hsA?Mb?%l(-ZH9)1IC}J` z>v`F>O@FaibY!FFc4#XBu3o)Lu~=kyc$l7^9v(eYf=ib!kxr+%b?X+{Y?i^n!Mf&V+cs-!Yp(YF&9wXqAcSCQYKozuA?D`h2%_-v zYlHm$llS>}ZJy4~PG&N*{OsZ{sPzI8CMG5r9v)_DYO3xN)2O9?ot77%P$&=#1}T@z z+<*A%4PJgV#jkJtmR$ZZUp{=CN~OX#N8aGVcYeqZe)SH6ue^e)s#unVq9_y!g}UX| z$fbXsmKR`od6|KM0V?woz4;)z#I8Pd>E43J{4zP!t8zG?|~D zXHRzosq6gZ!TaoveFH7@9^pM-M>Q(kpT5ifju@q~N+c3NN=aW|pJTabn#SVdqJJkU zf1Q>WAQp?Uu&_WPkszPXQCL{w{OGs&-Ge{#&r%Qj4qw3TO7Zr)-zO64r^8k-Oq1^J zZgROCu~=-!Iarp(%E}7)e7>P4d|T1-Eg%|=V%s+T{r!Y`ds$q}^Ty#}v<{2k-1#G; z|9PFe_dnp{kFvb`&d*6i;usZ!u79pBgb+ld(f?~)+qS7zs~aCDf6wP6EiZtgD4aTV zipj}I(&;p@SPWg)`Ci8uOVL&2E`|P+U*+wueV2iFf~8W z8`Cs5K3<;m&9syN>yz;5|IXPUnM@K2g}8nDHph=2CmaqF3WeC$8AVFTz<+_gbaZx7 zC>E$xDojsL6AFb$CX=6g5-5adstNxrEg^tqS(M9VN~O{>C*kzz(~OUgGd@1fv17;R z>FHtDu3bne2?PQglQ=s&OMibqBO@a$E-t$Km$*+;3$T7k^}LDS%-Gl%4<9~cW@d&! zAV4CKKnQ`NDCF~bq?DXEaesnTD&<1<-k@m(&@_#;wY6=ZRi{!ZQmGV6OG~V*tWYkO z35UZRIdX(}JYJ`4y-U*x;M9$jcs$;g6aHBo72y1j|K?=4r?6`#rmkXJ?rxfBWgHdY z^0j|E8SZ}(7SvRbwa-7c#d)pUxmeD&fSx*a6k&m-K~Mt0`i#-0cz^#^u&}^VK{nqt zb~E5@0i=Lh*`NG6;%xzJ4TOKfS5LevfNem{TTjkf5pN10tS3u9-gsu-%6Lxzzwf(> z_XP0!zN>gk0Kf0Mj&}s``+h_4h5&xwZy4SX!0-DFqwxaRy5IL3NaOYcNb#idYZ`vv zZyb#gz&8A4-y^syfNWp&p2Ix>{HpgP^%p?YKCl%nu=bp2jf&pRH4Gwe+b!{{<@3 VYV8Z@6E6S&002ovPDHLkV1l+_e3}3N delta 1453 zcmV;e1ycHg4!#SJBYy>jNklc*;V+g4oLwvA0~+wQ8i z?Q@g4cJxQ{o#%ODpLhO2l3fvizY!fBoe_{b5aHqBX^7IvKX#o@1t20KB2cYbwKPQO zg#;)dL`Fuk@~c*@nuaKy2rs}{bsK>pPNfnQ6vWEsPyk|w#D9Vn&!GUsAvG&(mYDM> z0C75%6p1;n0uZ|;me|av0K_phYgRm`0ub9J79;?Tel7(dc1kRG6@WOQ&PG440uZ|- zmb?l;ocww8^C|$bTg{SJ0fJ^yjYi# zNr(q4*W=8zIe+OQ&k8`veD2-52f19H9!P+!o*98`whgY!F{oy-(WOt)Cjt|x>&Vp z6>>5^e*D0lJ9kI`p)+dKC>R(RU}yYr$ZLe-s;4y+HGd3o&BqKEEo^WjCa{K)9Sdc@l<(hET-D<7QP`$U-e3uh$0mVu#~F@&w#!-5J*cogs@f zfwDt)czSxWDM9GGe*KyiN&*O&&!0cT(a{mxhb7~BMF+@hdSh+Jt|)3!6b^w7Y)uf< zAPDCdEq}$$=n%-FjBze@1hloaasU2(2$hc?KSH5UkN`qQDwV?D-yf=GQIN-&BB^2( zh&9A0s#^vvhfK!ol&e^^c{|EFmql{hMvzsv$IUQbbZg%MW5$dTY$2*tDp{E%fWV25 zkH?@cU2!AW4)XdnA(n_CRu`jo@>$d!Gy{Xi^?!p`*CXhh&=cw&>bTRbJ+23sVAFs& z)URJ3*`1d!U;e2~59i zBJf6N1Dw-w#F5w}CV*H=468sZNcU_(DS!J?C~jLEE8=5u!Q2~{T`FgE5y%RE`}Qp> zJsrooB!Dnsmt3~We)HxHyY!}(!47v8_8EvBhECY+5Qu}r$HK|a3EHmOF!nQsbFeek z3?Gg|zMA^HlIIu@PK{%bc}KmK!3=5 z{rVNJUcF-b_J7)2Z`!m8dU|@;+o=yy^z0$kvccZ)dN|a+CysU-fc>?aA;riAQVmP& zYtR}_PEKs}XQd<(K=`mxe)sO(bp6l<3>W|%9UUy})(1O%LsBL9{f{rC>Xz8yRtf97 z^@XLSCF<0vlV{5(0l-et(pmhUV1K{pF*i47Ke&W;9kHrqN32U_RJ%^75fj5M`;#Y6 z&a1_f0N=iS%kp0O^5x5zFku2(w{8s|A0N26xuHpuCKx$#B>SB~-YlI2VDtQ+e{GuQ zAOTkFye$Yzw<$1X=}}Bvc#udX&k7*&Tbcd%E`!K@DnxFRKrna`U zd3+RQn#aFYT~*zkgph<0895{($2v0LC?Kx02qPj2vnryTiaV?V5^w-HoZ-+}8CF0L zS#jkaMqtE|nUR4Hmmrs1qa4aAqMYFlBs{NzWA%TP|-JOoZ@AK(&>V4m?_pN%4 z_xC*STipU>pmXQWWfhkP_gWg?Ky7JzWI?N_W=aY>mJ!|~ie0%6-Wx~^CFJU0}e z3c_(5bX~``?FyeOW(ts+n#!U@i;IsnBaQ$4cpQINIT*{4kUg>LsbVlJXxVrxvxeVC z$=R##8(Y}^O>2}SF$vQKk6`l5!_(;3c^p0^SX|e>{exJRg<%*K?k8@S>r;XZ?lO$# zw{Ky>!YLd*T@RlUuima6q5y^ARBT7YBStL9c03e@(-13pY*AVHh}$Q{leie$8!~eZ<@m577SZWvB+kV9XVQ02FG~(9P81GR;h*P^*S;)pRO8 za2!w#c<}CJ%pLJSnZnmJVkUfuu>zbA+)`$Ft*nDg9?*rCyNyDTLl{;pd%Z>oU>Ma1 zYnjDGs+NIaRIUOTCV1sAFLxWon*+K~E9>ATZEPF9{r={d2_Ist0KHz{%F>Pfux;O^ z9f<~gI!xf*A)RSd_bXH#+)4-_1py2*1y#S`0aQH|(@eRBfGT!yT?p&YsNR>%7}AM8 z9VQ@(m(J~$?PJ06Uol|HqL}s{Qw7L9TbD6&X7K#Pwe0-5eVJ{V-@cAnFSp~Vw)3!T zuo65a2!LjI39A0$vXJUWHM|Lzzhy_r-cxPoGwbDcG{1daneBFd-JU+LFJt`N;hZ`D zUj)#astnX!;>j^2q+iQGHKHgK$P+{Tr#Sx=E7_jujskFfLWwsdXt9?y1|M2+gl zirf7Br%P#AcNcFgdXv>VA4B$l7!MV7ArP)sd{`JxkJ}XBI1$T#sKs9x(QPQTvc4}> zpI10rb$1GrAbGk)bEp~)|Sh&RwpKeLw)ruX>GeE4Y&hIN6F z*UCJ|r2btQ*?k1E97c~v&Om_kp$lyKe6UtrLO|UFSq?F>$8aY1?^;UuhUMqOPjl!w zelxR{4@Niq_%0T{ZD1P(u^(6oR7&w>Do9k=e`y`dfG`QSuE;esY_-PfMJ6W1_q{xnYJ z)g#3lxhEGR-qsJ5qySM!o=C*7>)ZPnFl7b5nX`byC+{e(Z`NQlGl%_xZtdn^JCV3K z77zu<4_$~#@h;@Fm(mHzP%5x9JvQ& zAraOONw6F&0E!H*>tX4}UToU^D7_z=&eLt*qFPEG>8WQK-TPPEb^B%}eK3K%{G0Gr za8XGRKvmOmT@e=uhGUZk)(p5Q;}k=mdWBA{=MnlFb$ zSU*&<2Pg?afD{D+;Y_A18O^g3HnD1FcOYUSom$Uf-tY%$b;rgE=L`e^0$MtTm4<1h z64Em&qyUZ^5q_(G*~pyX?Jo%5^{{G3cb@tECZ;YKLm-?4?5z|WH z{-(>BGx7l*Zt)Si3HCKCDhUD@hJtD+XoiA$jSE3`(Yk%dmP?sC@&WE|x*Wqw;e+)r z();x-%wL&hUs@`ai)<)sOJmR~_j4#wWZwe>$hm z*NwRQ>+mSH8N$-?uKj}*K+;YDWg)uS_{D)%418-D9qwI1zs|4HtNldox_uMBTksBh zzHg5I3Pei)Y)2rZ)c{w(azMF)DY!*M@6PPqjCrO%Kgs@#qh}g3<8R|wxveXv<-uQp z=)`qwZ2cUN*!A%$;Q=m&B7=*>Up989*XvuDwQL*>YJbW6QTNm1!I_xOmG>DqE))c_ zCEfiq6-w{Y?Jw%D>W}_Lt0DHjZAeZ{e?-x&h)9!@4WMHu7<-_$~UBhbrX( zN+P=2SQyIU%|DN2+4gSq@BA9S?e#n@n|#8Ag;O|vzE=4J(S<-b4FnJ_qX3!#H8W2# zs>iQsf6ro8?dZXbzl~=9u_h=IMOI(2BtoKzUrAI+4^R>wF_J$xnA@0P@4nB#x0Z8T zt^F)`r42v->8f(Kp1HvJPzL9N8D-bI5u$hhPgk?xl{RG8+E323ZrY+stzRPcW=diMISKj&CXiU>v+y(#_&IfNU^IWC{ zimWrF>+g8D<%cX;H-JU!ULdUcDx8J97?nZ%N}|epfQ#W(z_fhK`uj`tow%B;YI#h3 zu^SERd=*(#DrzSPP!Oy|VX#&)VRVUDeWSYjnEK+=)Tow6--)Z4`S-z?R_ulESQ?gg zhC~y;l8C7X(9Ot=6}a$DdGB#VbhYtBUOh(7ox#0#tmoN|llknchgh+#E4Us+5ekDf z5Qt>xYzGi*9=m@Dt$(tLc`ILH>%Ir@6lEr34ad?j)swikR$S&5{VD6+EpZ%6#8d&= zG+U2ucu{p90nLYLr4+rqShkE|$yk19yx6t}+mSGBq4-9Zw36umf&gLN%b(VF`ld7LDjhsB?y!fmPY`|<06R`9*>P8>qwG@BANK) zFn&3NSJCkM!}ygDes2iBw*bjKg{_`P5(A|8RJ^i^;tk-J^H5|9uVkV~7K&sb$vTRx zB8e&iGTE$FeaUg*Xq5z6yqe%Irc=IcKMwiHg5ti-8v?VOlhhcaz9Rb}) zBWzShH+`sDDyp7_s;3avd<3*i0$Mt%nuV%WC#0oN5Uj>`C+m}vrediP-!A(@96EU` z^=p1lv+UiZ`oj2q1^E4WRPz;5&6iJ#SH@?+ax3`@Z6Q@;tQgC{rJovKIJ^R820elu->Iy6HjFvk2>15i3>G2y1@Ark}7L zd0tP`(+KM+Xj&?&;YT(7s74xwrC?bKrsc!5BpgS=v_)*EeAiD2{vZlqdLqY-SawsY1|7JkZmrVQZ6E|V$qb~5kdCYh@y=wnaGlkB&tXh;Nc9C zdmgVRh@xot6ct5Q@p=pKD*^nnPKsB@=MCdkRQz%Qinjnovhc|Uifo~HbYxjW@`RB* z1|BiC?jxoO@a*{Qgmph5!-r-0v28!5<;AdMOjE{kN)w{!)EXsFrN|!_NFZg@LFDFN zoQ!cDJ3_7~v01={um`fw9b~{i#?a=_#{fhpCvW%&!#DJ%)sYW)=kC`yl#z|kDIxH( zNTypfZinJUsIX%fZR6H)Y!SE_*S-0YYc7f6>nhQgl&s9)k{#X;1JL3e)FoVzAQxBXKJSzxMMW;9N)nF=0iAAy*@sx zQocqYZm3cUa7C-f<&kgyOph<8k*S`kaMm>X`FnF$)A-yj7TuY{>g+B!g7|~+231A@ zq6;TwS-tLo+J#5>k6n{#mAk6AJXVNsgEI`+F^0R3Z(>gKq2#95!<%4k5D!!d1t@Fb z1R_>1x(=NV{F$fszD=ff28hptzEN(_Dt8r)^1k3tO>qW4Vg4E=VD;b+G;GPn6Kj-=O1c%%?3B)tM$yjRW?K*XWb zfe-1vcRCqb#pn8KMYO+`xvOcEw~Iwh2eR_ECvmBI-XN(JpyY0u=v-g7D6VgHoeV#g zaDfc%4E=YE1|*xE>aC6BKxM7+US2JRS5 z%j2Iiw|Uj@21%&^wvmr+hY)e;bYKbH_fDg_mY0-*FNq7>U=0oPcCol=4y&`fVkL1F zG3gY*GD8HcJn97x^W3gUw9H)tB&XGv2N%fH&eDI!SnfH#k$KIBawxriQbr0%r2x94 z;pnG%^xHqtJRbKg5a9-S4BRo6mM1nar`b@BRkAmz z6a_Fv@VE{=_RXTtw$YgCX*7P=!jB@#4O$*sOT)9f_@L<^R%SnmRY~3;p^t{S0)!=~ zU+@i6S9M|N`WG266WQE}%+qTfx^%S^XpCcz3GxPU^#GB%-loUCISkl78YO&+Tp;n^gOU7*P75t^ zKcP|HZWcEk#L5JCgSdDA7f@xWU$CFIRz1PU_0QuJS^W*+myhT}vek>Y!7H0{s2yi- z5OWU@jq7{uok_o)V{cgfbs#G2dve#&FmDfwo93`0yDM%SB84);&x-tbwClQ9mW8hC zSeAupnz)tm^I-oxxat(bwr$%kDT|p0a9y|f7cDKz!m_NG{=VG}#0O<6z_kj_G)+v? eydn51i2nyVGxK7~$83QB0000_p delta 1640 zcmV-u2ABEZCio1HBYy@zNkl3lDKglgAsL2LB^XrtZ_HLl+Pzf12$FJE!wJxDeui8p}L z^~2bIFwkfAsw|K5y6_do+?x((-iN>sjZKy}I}I%1;s-yN_+SF~T>K=yssC&$NVcYk zHvqpA@8P?LEq?|8JKNUF@VL4+FGA912>>u!asO*6Xmxd2A806)xm zJV;ohf6r6#2N1pQJ|`bJ!&?A&loZbBKm8qLi$8!-6J~PqeOZ360Dr_>y(fqOOQ&y5c`uD#;(c%xF z%fc<3e54Kk0Sri4M*7;1qu)FN*x0#6#sF&Y9su$80rI`v+Arz=T+eCXc}Rhb0r<0| z@f$$pYl#G(`@f-J^9Z2+2K)z;l`H(ofB3Zo>3_F=0uNwKH?5h|(L4J9rtlZQa;v#)d1QR~Z@ySV*isq3ZX%J5k%45mL>q)3K*S^& zXk0nzS!ltcF>V08_LU%3zH*up#hOn5!@3kj(Ydq2c=Goo{Z6cWL^@h(UI84g`fJ_+ zcz?4b87}|H2U`eQEGJgJe1aUMnoj^;Gga`}(-I};jW5eO{pIgU{GDj|iYW>dYF+_! z=wAxu>PB0ZXWEN@_1PMF>@7*Gctkl}i89SEfVB<(LaDkFz%rkKdoS#A6o2on9W1&0 z4}>fscGPp5z7Z1h4B$zCEeh3}@$Cyk%zp|gwA@w(`W+|^{aISG|BhcJAy_(xViGs) zn_U34MpZCKK5{1hXM9=0W3~&(#>r5i`35kfbwPsz`0j22e$y3#5|5)^I#P=A%sYT9 z(M~AQV0!c}NxXQ~OcfsSGOqRJ96;z~Ii$H7E+5%a_YWe4E2k<^dUn^kG=PEKi+`a! zqwvbt23%+7Jmump8_(D{^~%SyLdMGQwk-p!B#yBhxK1PZdv-SlRe*qko7`RWa~X#z2yUfkY_-e*MY`K^P6-L6Dun@_(@M z?@LiWN0%lANfJW?DAu@G7DDP2G>ZItndBpjJsqG>wL<0wxdAW?lZEd87%u;A2KgM< zDllMcfv?BB5L5t1%IYs)!}1_o`5eB^#EPN;{APt4H~j;6AMpaB(!b92x*_#=ffz zxal*1?*Euv4h^9CZ`#R!W@P!uj#4I$>N9Yo0t5DzCSegn1GrLw6R+V$ zJtT5KLMGG|^X7IYZe`+TCIWx9082|tu|jSDNNj8@L`Ft(zuJ_-0|W+u;v*s=#2}sq m5EB!l3lJV2E>Q6_fPVpW!_{)NkN#!=0000 Date: Thu, 31 Aug 2023 17:41:41 +0200 Subject: [PATCH 043/126] Fix chat history disappearing with `cl_showchat 1` Closes #7115. --- src/game/client/components/chat.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index 708ac04a1..e5629c197 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -830,13 +830,9 @@ void CChat::OnPrepareLines() float ScreenRatio = Graphics()->ScreenAspect(); - bool IsScoreBoardOpen = m_pClient->m_Scoreboard.Active() && (ScreenRatio > 1.7f); // only assume scoreboard when screen ratio is widescreen(something around 16:9) - - bool ForceRecreate = IsScoreBoardOpen != m_PrevScoreBoardShowed; - bool ShowLargeArea = m_Show || g_Config.m_ClShowChat == 2; - - ForceRecreate |= ShowLargeArea != m_PrevShowChat; - + const bool IsScoreBoardOpen = m_pClient->m_Scoreboard.Active() && (ScreenRatio > 1.7f); // only assume scoreboard when screen ratio is widescreen(something around 16:9) + const bool ShowLargeArea = m_Show || (m_Mode != MODE_NONE && g_Config.m_ClShowChat == 1) || g_Config.m_ClShowChat == 2; + const bool ForceRecreate = IsScoreBoardOpen != m_PrevScoreBoardShowed || ShowLargeArea != m_PrevShowChat; m_PrevScoreBoardShowed = IsScoreBoardOpen; m_PrevShowChat = ShowLargeArea; From cfcc2ff9bc75117657ce2e3f4fbf2a1b054cb721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 31 Aug 2023 20:08:46 +0200 Subject: [PATCH 044/126] Remove unused include --- src/game/client/components/chat.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index e5629c197..1f872a76b 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -11,12 +11,10 @@ #include #include -#include - -#include #include #include #include +#include #include #include "chat.h" From ddb9cdd25140d57ca155d3d8bbcd21fd08955fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 31 Aug 2023 20:10:13 +0200 Subject: [PATCH 045/126] Use `DefaultTextColor`/`DefaultTextOutlineColor` --- src/game/client/components/chat.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index 1f872a76b..ead69b10b 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -1031,7 +1031,7 @@ void CChat::OnPrepareLines() TextRender()->UploadTextContainer(m_aLines[r].m_TextContainerIndex); } - TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + TextRender()->TextColor(TextRender()->DefaultTextColor()); } void CChat::OnRender() @@ -1180,9 +1180,9 @@ void CChat::OnRender() RenderTools()->RenderTee(pIdleState, &RenderInfo, EMOTE_NORMAL, vec2(1, 0.1f), TeeRenderPos, Blend); } - ColorRGBA TextOutline(0.f, 0.f, 0.f, 0.3f * Blend); - ColorRGBA Text(1.f, 1.f, 1.f, Blend); - TextRender()->RenderTextContainer(m_aLines[r].m_TextContainerIndex, Text, TextOutline, 0, (y + RealMsgPaddingY / 2.0f) - m_aLines[r].m_TextYOffset); + const ColorRGBA TextColor = TextRender()->DefaultTextColor().WithMultipliedAlpha(Blend); + const ColorRGBA TextOutlineColor = TextRender()->DefaultTextOutlineColor().WithMultipliedAlpha(Blend); + TextRender()->RenderTextContainer(m_aLines[r].m_TextContainerIndex, TextColor, TextOutlineColor, 0, (y + RealMsgPaddingY / 2.0f) - m_aLines[r].m_TextYOffset); } } } From 87a0461eb66eb8c495cc657089108fea53453a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 31 Aug 2023 21:16:51 +0200 Subject: [PATCH 046/126] Disable server browser hotkeys when popup menu is open Closes #7088. --- src/game/client/components/menus_browser.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 2a5a3e82f..190545c2a 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -178,7 +178,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) UI()->DoLabel(&MsgBox, Localize("No servers match your filter criteria"), 16.0f, TEXTALIGN_MC); } - if(UI()->ConsumeHotkey(CUI::HOTKEY_TAB)) + if(!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_TAB)) { const int Direction = Input()->ShiftIsPressed() ? -1 : 1; g_Config.m_UiToolboxPage = (g_Config.m_UiToolboxPage + 3 + Direction) % 3; @@ -493,7 +493,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) QuickSearch.VSplitLeft(5.0f, 0, &QuickSearch); static CLineInput s_FilterInput(g_Config.m_BrFilterString, sizeof(g_Config.m_BrFilterString)); - if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed()) + if(!UI()->IsPopupOpen() && Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed()) { UI()->SetActiveItem(&s_FilterInput); s_FilterInput.SelectAll(); @@ -520,7 +520,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) QuickExclude.VSplitLeft(5.0f, 0, &QuickExclude); static CLineInput s_ExcludeInput(g_Config.m_BrExcludeString, sizeof(g_Config.m_BrExcludeString)); - if(Input()->KeyPress(KEY_X) && Input()->ShiftIsPressed() && Input()->ModifierIsPressed()) + if(!UI()->IsPopupOpen() && Input()->KeyPress(KEY_X) && Input()->ShiftIsPressed() && Input()->ModifierIsPressed()) UI()->SetActiveItem(&s_ExcludeInput); if(UI()->DoClearableEditBox(&s_ExcludeInput, &QuickExclude, 12.0f)) Client()->ServerBrowserUpdate(); @@ -576,7 +576,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) Props.m_UseIconFont = true; static CButtonContainer s_RefreshButton; - if(UI()->DoButton_Menu(m_RefreshButton, &s_RefreshButton, RefreshLabelFunc, &ButtonRefresh, Props) || Input()->KeyPress(KEY_F5) || (Input()->KeyPress(KEY_R) && Input()->ModifierIsPressed())) + if(UI()->DoButton_Menu(m_RefreshButton, &s_RefreshButton, RefreshLabelFunc, &ButtonRefresh, Props) || (!UI()->IsPopupOpen() && (Input()->KeyPress(KEY_F5) || (Input()->KeyPress(KEY_R) && Input()->ModifierIsPressed())))) { RefreshBrowserTab(g_Config.m_UiPage); } @@ -591,7 +591,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) Props.m_Color = ColorRGBA(0.5f, 1.0f, 0.5f, 0.5f); static CButtonContainer s_ConnectButton; - if(UI()->DoButton_Menu(m_ConnectButton, &s_ConnectButton, ConnectLabelFunc, &ButtonConnect, Props) || s_ListBox.WasItemActivated() || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER)) + if(UI()->DoButton_Menu(m_ConnectButton, &s_ConnectButton, ConnectLabelFunc, &ButtonConnect, Props) || s_ListBox.WasItemActivated() || (!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))) { Connect(g_Config.m_UiServerAddress); } From a4232198edc8f7135dc2fac0e55b4ab33be62297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 7 Jul 2023 22:37:59 +0200 Subject: [PATCH 047/126] Add `CUIRect::Center` convenience function --- src/game/client/ui_rect.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/game/client/ui_rect.h b/src/game/client/ui_rect.h index 5fb964e65..18143d1fd 100644 --- a/src/game/client/ui_rect.h +++ b/src/game/client/ui_rect.h @@ -113,6 +113,8 @@ public: void Draw(ColorRGBA Color, int Corners, float Rounding) const; void Draw4(ColorRGBA ColorTopLeft, ColorRGBA ColorTopRight, ColorRGBA ColorBottomLeft, ColorRGBA ColorBottomRight, int Corners, float Rounding) const; + + vec2 Center() const { return vec2(x + w / 2.0f, y + h / 2.0f); } }; #endif From 147561e2816b4e91948a2263c87e78743b3b2756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 15 Jul 2023 19:43:11 +0200 Subject: [PATCH 048/126] Add `CUI::RenderProgressSpinner` function --- src/game/client/ui.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++ src/game/client/ui.h | 10 +++++++++ 2 files changed, 59 insertions(+) diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 14dc6a752..7edb5210f 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -1279,6 +1279,55 @@ void CUI::DoScrollbarOption(const void *pID, int *pOption, const CUIRect *pRect, *pOption = Value; } +void CUI::RenderProgressSpinner(vec2 Center, float OuterRadius, const SProgressSpinnerProperties &Props) +{ + static float s_SpinnerOffset = 0.0f; + static float s_LastRender = Client()->LocalTime(); + s_SpinnerOffset += (Client()->LocalTime() - s_LastRender) * 1.5f; + s_SpinnerOffset = std::fmod(s_SpinnerOffset, 1.0f); + + Graphics()->TextureClear(); + Graphics()->QuadsBegin(); + + // The filled and unfilled segments need to begin at the same angle offset + // or the differences in pixel alignment will make the filled segments flicker. + const float SegmentsAngle = 2.0f * pi / Props.m_Segments; + const float InnerRadius = OuterRadius * 0.75f; + const float AngleOffset = -0.5f * pi; + Graphics()->SetColor(Props.m_Color.WithMultipliedAlpha(0.5f)); + for(int i = 0; i < Props.m_Segments; ++i) + { + const float Angle1 = AngleOffset + i * SegmentsAngle; + const float Angle2 = AngleOffset + (i + 1) * SegmentsAngle; + IGraphics::CFreeformItem Item = IGraphics::CFreeformItem( + Center.x + std::cos(Angle1) * InnerRadius, Center.y + std::sin(Angle1) * InnerRadius, + Center.x + std::cos(Angle2) * InnerRadius, Center.y + std::sin(Angle2) * InnerRadius, + Center.x + std::cos(Angle1) * OuterRadius, Center.y + std::sin(Angle1) * OuterRadius, + Center.x + std::cos(Angle2) * OuterRadius, Center.y + std::sin(Angle2) * OuterRadius); + Graphics()->QuadsDrawFreeform(&Item, 1); + } + + const float FilledRatio = Props.m_Progress < 0.0f ? 0.333f : Props.m_Progress; + const int FilledSegmentOffset = Props.m_Progress < 0.0f ? round_to_int(s_SpinnerOffset * Props.m_Segments) : 0; + const int FilledNumSegments = minimum(Props.m_Segments * FilledRatio + (Props.m_Progress < 0.0f ? 0 : 1), Props.m_Segments); + Graphics()->SetColor(Props.m_Color); + for(int i = 0; i < FilledNumSegments; ++i) + { + const float Angle1 = AngleOffset + (i + FilledSegmentOffset) * SegmentsAngle; + const float Angle2 = AngleOffset + ((i + 1 == FilledNumSegments && Props.m_Progress >= 0.0f) ? (2.0f * pi * Props.m_Progress) : ((i + FilledSegmentOffset + 1) * SegmentsAngle)); + IGraphics::CFreeformItem Item = IGraphics::CFreeformItem( + Center.x + std::cos(Angle1) * InnerRadius, Center.y + std::sin(Angle1) * InnerRadius, + Center.x + std::cos(Angle2) * InnerRadius, Center.y + std::sin(Angle2) * InnerRadius, + Center.x + std::cos(Angle1) * OuterRadius, Center.y + std::sin(Angle1) * OuterRadius, + Center.x + std::cos(Angle2) * OuterRadius, Center.y + std::sin(Angle2) * OuterRadius); + Graphics()->QuadsDrawFreeform(&Item, 1); + } + + Graphics()->QuadsEnd(); + + s_LastRender = Client()->LocalTime(); +} + void CUI::DoPopupMenu(const SPopupMenuId *pID, int X, int Y, int Width, int Height, void *pContext, FPopupMenuFunction pfnFunc, const SPopupMenuProperties &Props) { constexpr float Margin = SPopupMenu::POPUP_BORDER + SPopupMenu::POPUP_MARGIN; diff --git a/src/game/client/ui.h b/src/game/client/ui.h index 5a5b51bcb..eca56fd7b 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -238,6 +238,13 @@ struct SValueSelectorProperties ColorRGBA m_Color = ColorRGBA(0.0f, 0.0f, 0.0f, 0.4f); }; +struct SProgressSpinnerProperties +{ + float m_Progress = -1.0f; // between 0.0f and 1.0f, or negative for indeterminate progress + ColorRGBA m_Color = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); + int m_Segments = 64; +}; + /** * Type safe UI ID for popup menus. */ @@ -511,6 +518,9 @@ public: float DoScrollbarH(const void *pID, const CUIRect *pRect, float Current, const ColorRGBA *pColorInner = nullptr); void DoScrollbarOption(const void *pID, int *pOption, const CUIRect *pRect, const char *pStr, int Min, int Max, const IScrollbarScale *pScale = &ms_LinearScrollbarScale, unsigned Flags = 0u, const char *pSuffix = ""); + // progress spinner + void RenderProgressSpinner(vec2 Center, float OuterRadius, const SProgressSpinnerProperties &Props = {}); + // popup menu void DoPopupMenu(const SPopupMenuId *pID, int X, int Y, int Width, int Height, void *pContext, FPopupMenuFunction pfnFunc, const SPopupMenuProperties &Props = {}); void RenderPopupMenus(); From 07fd8e6712c74ea5dd3ff7d3dc7ec4045a4abce3 Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:22:01 +0200 Subject: [PATCH 049/126] let `CAutoMapper` inherit `CEditorComponent` --- src/game/editor/auto_map.cpp | 13 +++++++------ src/game/editor/auto_map.h | 11 ++++++----- src/game/editor/mapitems/image.h | 3 +-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/game/editor/auto_map.cpp b/src/game/editor/auto_map.cpp index d339a2601..bca98c498 100644 --- a/src/game/editor/auto_map.cpp +++ b/src/game/editor/auto_map.cpp @@ -5,8 +5,10 @@ #include #include +#include + #include "auto_map.h" -#include "editor.h" +#include "editor.h" // TODO: only needs CLayerTiles // Based on triple32inc from https://github.com/skeeto/hash-prospector/tree/79a6074062a84907df6e45b756134b74e2956760 static uint32_t HashUInt32(uint32_t Num) @@ -39,15 +41,14 @@ static int HashLocation(uint32_t Seed, uint32_t Run, uint32_t Rule, uint32_t X, CAutoMapper::CAutoMapper(CEditor *pEditor) { - m_pEditor = pEditor; - m_FileLoaded = false; + Init(pEditor); } void CAutoMapper::Load(const char *pTileName) { char aPath[IO_MAX_PATH_LENGTH]; str_format(aPath, sizeof(aPath), "editor/%s.rules", pTileName); - IOHANDLE RulesFile = m_pEditor->Storage()->OpenFile(aPath, IOFLAG_READ | IOFLAG_SKIP_BOM, IStorage::TYPE_ALL); + IOHANDLE RulesFile = Storage()->OpenFile(aPath, IOFLAG_READ | IOFLAG_SKIP_BOM, IStorage::TYPE_ALL); if(!RulesFile) return; @@ -362,7 +363,7 @@ void CAutoMapper::Load(const char *pTileName) char aBuf[IO_MAX_PATH_LENGTH + 16]; str_format(aBuf, sizeof(aBuf), "loaded %s", aPath); - m_pEditor->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor/automap", aBuf); + Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor/automap", aBuf); m_FileLoaded = true; } @@ -470,7 +471,7 @@ void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID, int Seed, int SeedO for(int x = 0; x < pLayer->m_Width; x++) { CTile *pTile = &(pLayer->m_pTiles[y * pLayer->m_Width + x]); - m_pEditor->m_Map.OnModify(); + Editor()->m_Map.OnModify(); for(size_t i = 0; i < pRun->m_vIndexRules.size(); ++i) { diff --git a/src/game/editor/auto_map.h b/src/game/editor/auto_map.h index b5fd6cc4d..4e998af72 100644 --- a/src/game/editor/auto_map.h +++ b/src/game/editor/auto_map.h @@ -3,7 +3,9 @@ #include -class CAutoMapper +#include "component.h" + +class CAutoMapper : public CEditorComponent { struct CIndexInfo { @@ -55,7 +57,7 @@ class CAutoMapper }; public: - CAutoMapper(class CEditor *pEditor); + explicit CAutoMapper(CEditor *pEditor); void Load(const char *pTileName); void ProceedLocalized(class CLayerTiles *pLayer, int ConfigID, int Seed = 0, int X = 0, int Y = 0, int Width = -1, int Height = -1); @@ -67,9 +69,8 @@ public: bool IsLoaded() const { return m_FileLoaded; } private: - std::vector m_vConfigs; - class CEditor *m_pEditor; - bool m_FileLoaded; + std::vector m_vConfigs = {}; + bool m_FileLoaded = false; }; #endif diff --git a/src/game/editor/mapitems/image.h b/src/game/editor/mapitems/image.h index 77a845a87..b34edba79 100644 --- a/src/game/editor/mapitems/image.h +++ b/src/game/editor/mapitems/image.h @@ -12,8 +12,7 @@ class CEditorImage : public CImageInfo public: CEditor *m_pEditor; - CEditorImage(CEditor *pEditor) : - m_AutoMapper(pEditor) + CEditorImage(CEditor *pEditor): m_AutoMapper(pEditor) { m_pEditor = pEditor; m_aName[0] = 0; From 12d0608dfd2f852574141c87cef9b23122b46bca Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:36:37 +0200 Subject: [PATCH 050/126] let `CEditorImage` inherit `CEditorComponent` --- src/engine/graphics.h | 8 ++++---- src/game/editor/mapitems/image.cpp | 18 ++++++++++++++++-- src/game/editor/mapitems/image.h | 27 +++++++-------------------- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/engine/graphics.h b/src/engine/graphics.h index 77a9da40b..5ed0fcab2 100644 --- a/src/engine/graphics.h +++ b/src/engine/graphics.h @@ -76,19 +76,19 @@ public: /* Variable: width Contains the width of the image */ - int m_Width; + int m_Width = 0; /* Variable: height Contains the height of the image */ - int m_Height; + int m_Height = 0; /* Variable: format Contains the format of the image. See for more information. */ - int m_Format; + int m_Format = FORMAT_RGB; /* Variable: data Pointer to the image data. */ - void *m_pData; + void *m_pData = nullptr; }; /* diff --git a/src/game/editor/mapitems/image.cpp b/src/game/editor/mapitems/image.cpp index 365c6687d..514a1cadf 100644 --- a/src/game/editor/mapitems/image.cpp +++ b/src/game/editor/mapitems/image.cpp @@ -1,14 +1,28 @@ #include "image.h" -#include +#include + +CEditorImage::CEditorImage(CEditor *pEditor) : + m_AutoMapper(pEditor) +{ + Init(pEditor); + m_Texture.Invalidate(); +} CEditorImage::~CEditorImage() { - m_pEditor->Graphics()->UnloadTexture(&m_Texture); + Graphics()->UnloadTexture(&m_Texture); free(m_pData); m_pData = nullptr; } +void CEditorImage::Init(CEditor *pEditor) +{ + CEditorComponent::Init(pEditor); + RegisterSubComponent(m_AutoMapper); + InitSubComponents(); +} + void CEditorImage::AnalyseTileFlags() { mem_zero(m_aTileFlags, sizeof(m_aTileFlags)); diff --git a/src/game/editor/mapitems/image.h b/src/game/editor/mapitems/image.h index b34edba79..cffbd6c7e 100644 --- a/src/game/editor/mapitems/image.h +++ b/src/game/editor/mapitems/image.h @@ -4,35 +4,22 @@ #include #include +#include -class CEditor; - -class CEditorImage : public CImageInfo +class CEditorImage : public CImageInfo, public CEditorComponent { public: - CEditor *m_pEditor; - - CEditorImage(CEditor *pEditor): m_AutoMapper(pEditor) - { - m_pEditor = pEditor; - m_aName[0] = 0; - m_Texture.Invalidate(); - m_External = 0; - m_Width = 0; - m_Height = 0; - m_pData = nullptr; - m_Format = 0; - } - + explicit CEditorImage(CEditor *pEditor); ~CEditorImage(); + void Init(CEditor *pEditor) override; void AnalyseTileFlags(); IGraphics::CTextureHandle m_Texture; - int m_External; - char m_aName[IO_MAX_PATH_LENGTH]; + int m_External = 0; + char m_aName[IO_MAX_PATH_LENGTH] = ""; unsigned char m_aTileFlags[256]; - class CAutoMapper m_AutoMapper; + CAutoMapper m_AutoMapper; }; #endif From 59df1d86d4430b47ae0db8c1d9a45a546b1abc43 Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Tue, 29 Aug 2023 22:03:11 +0200 Subject: [PATCH 051/126] move `CEditorMap::HandleMapDrop` to `CEditor` --- src/game/editor/editor.cpp | 12 +++++++++++- src/game/editor/editor.h | 1 - src/game/editor/mapitems/map.cpp | 15 --------------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index db6c97443..f8320c5d1 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -7838,7 +7838,17 @@ bool CEditor::Save(const char *pFilename) bool CEditor::HandleMapDrop(const char *pFileName, int StorageType) { - return m_Map.HandleMapDrop(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); + if(HasUnsavedData()) + { + str_copy(m_aFileNamePending, pFileName); + m_PopupEventType = CEditor::POPEVENT_LOADDROP; + m_PopupEventActivated = true; + return true; + } + else + { + return Load(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); + } } bool CEditor::Load(const char *pFileName, int StorageType) diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index ccd36e57f..851f62f0b 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -473,7 +473,6 @@ public: // io bool Save(const char *pFilename); bool Load(const char *pFilename, int StorageType, const std::function &ErrorHandler); - bool HandleMapDrop(const char *pFilename, int StorageType); void PerformSanityChecks(const std::function &ErrorHandler); // DDRace diff --git a/src/game/editor/mapitems/map.cpp b/src/game/editor/mapitems/map.cpp index 996d081e2..48c58acd1 100644 --- a/src/game/editor/mapitems/map.cpp +++ b/src/game/editor/mapitems/map.cpp @@ -179,18 +179,3 @@ void CEditorMap::MakeTuneLayer(const std::shared_ptr &pLayer) m_pTuneLayer = std::static_pointer_cast(pLayer); m_pTuneLayer->m_pEditor = m_pEditor; } - -bool CEditorMap::HandleMapDrop(const char *pFileName, int StorageType) -{ - if(m_pEditor->HasUnsavedData()) - { - str_copy(m_pEditor->m_aFileNamePending, pFileName); - m_pEditor->m_PopupEventType = CEditor::POPEVENT_LOADDROP; - m_pEditor->m_PopupEventActivated = true; - return true; - } - else - { - return m_pEditor->Load(pFileName, IStorage::TYPE_ALL_OR_ABSOLUTE); - } -} From eac1dc45330bc54fa8ead42d08a480e39e286d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 15 Jul 2023 19:43:58 +0200 Subject: [PATCH 052/126] Show progress spinner while saving map in editor --- src/game/editor/editor.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 05d53c0cb..86e82189d 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -7348,10 +7348,17 @@ void CEditor::RenderSavingIndicator(CUIRect View) if(m_WriterFinishJobs.empty()) return; + const char *pText = "Saving…"; + const float FontSize = 24.0f; + UI()->MapScreen(); - CUIRect Label; - View.Margin(20.0f, &Label); - UI()->DoLabel(&Label, "Saving…", 24.0f, TEXTALIGN_BR); + CUIRect Label, Spinner; + View.Margin(20.0f, &View); + View.HSplitBottom(FontSize, nullptr, &View); + View.VSplitRight(TextRender()->TextWidth(FontSize, pText) + 2.0f, &Spinner, &Label); + Spinner.VSplitRight(Spinner.h, nullptr, &Spinner); + UI()->DoLabel(&Label, pText, FontSize, TEXTALIGN_MR); + UI()->RenderProgressSpinner(Spinner.Center(), 8.0f); } void CEditor::FreeDynamicPopupMenus() From 8001b6fde90984b08433b1cd9012b28b44ea634b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 1 Sep 2023 21:31:33 +0200 Subject: [PATCH 053/126] Update displayed country codes of flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the country codes which are displayed for the flags in the UI. This only changes the names which are displayed in the UI but not how flags are communicated between server and client. - Use ISO 3166-2 subdivision codes: - England: XEN → GB-ENG - Northern Ireland: XNI → GB-NIR - Scotland: XSC → GB-SCT - Wales: XWA → GB-WLS - Catalonia: XCA → ES-CT - Galicia: XGL → ES-GA - Use ISO 3166/MA exceptional reservation code: - European Union: XEU → EU - Move South Sudan (SS) to official codes, as it was officially assigned in the year 2011. Closes #7071. --- CMakeLists.txt | 14 +++++------ data/countryflags/{XCA.png => ES-CT.png} | Bin data/countryflags/{XGL.png => ES-GA.png} | Bin data/countryflags/{XEU.png => EU.png} | Bin data/countryflags/{XEN.png => GB-ENG.png} | Bin data/countryflags/{XNI.png => GB-NIR.png} | Bin data/countryflags/{XSC.png => GB-SCT.png} | Bin data/countryflags/{XWA.png => GB-WLS.png} | Bin data/countryflags/index.txt | 27 ++++++++++++---------- 9 files changed, 22 insertions(+), 19 deletions(-) rename data/countryflags/{XCA.png => ES-CT.png} (100%) rename data/countryflags/{XGL.png => ES-GA.png} (100%) rename data/countryflags/{XEU.png => EU.png} (100%) rename data/countryflags/{XEN.png => GB-ENG.png} (100%) rename data/countryflags/{XNI.png => GB-NIR.png} (100%) rename data/countryflags/{XSC.png => GB-SCT.png} (100%) rename data/countryflags/{XWA.png => GB-WLS.png} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c38e2b8f6..193d5f76d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1222,8 +1222,11 @@ set(EXPECTED_DATA countryflags/EG.png countryflags/EH.png countryflags/ER.png + countryflags/ES-CT.png + countryflags/ES-GA.png countryflags/ES.png countryflags/ET.png + countryflags/EU.png countryflags/FI.png countryflags/FJ.png countryflags/FK.png @@ -1231,6 +1234,10 @@ set(EXPECTED_DATA countryflags/FO.png countryflags/FR.png countryflags/GA.png + countryflags/GB-ENG.png + countryflags/GB-NIR.png + countryflags/GB-SCT.png + countryflags/GB-WLS.png countryflags/GB.png countryflags/GD.png countryflags/GE.png @@ -1396,13 +1403,6 @@ set(EXPECTED_DATA countryflags/VU.png countryflags/WF.png countryflags/WS.png - countryflags/XCA.png - countryflags/XEN.png - countryflags/XEU.png - countryflags/XGL.png - countryflags/XNI.png - countryflags/XSC.png - countryflags/XWA.png countryflags/YE.png countryflags/ZA.png countryflags/ZM.png diff --git a/data/countryflags/XCA.png b/data/countryflags/ES-CT.png similarity index 100% rename from data/countryflags/XCA.png rename to data/countryflags/ES-CT.png diff --git a/data/countryflags/XGL.png b/data/countryflags/ES-GA.png similarity index 100% rename from data/countryflags/XGL.png rename to data/countryflags/ES-GA.png diff --git a/data/countryflags/XEU.png b/data/countryflags/EU.png similarity index 100% rename from data/countryflags/XEU.png rename to data/countryflags/EU.png diff --git a/data/countryflags/XEN.png b/data/countryflags/GB-ENG.png similarity index 100% rename from data/countryflags/XEN.png rename to data/countryflags/GB-ENG.png diff --git a/data/countryflags/XNI.png b/data/countryflags/GB-NIR.png similarity index 100% rename from data/countryflags/XNI.png rename to data/countryflags/GB-NIR.png diff --git a/data/countryflags/XSC.png b/data/countryflags/GB-SCT.png similarity index 100% rename from data/countryflags/XSC.png rename to data/countryflags/GB-SCT.png diff --git a/data/countryflags/XWA.png b/data/countryflags/GB-WLS.png similarity index 100% rename from data/countryflags/XWA.png rename to data/countryflags/GB-WLS.png diff --git a/data/countryflags/index.txt b/data/countryflags/index.txt index 8af04d58e..6ea9d4e12 100644 --- a/data/countryflags/index.txt +++ b/data/countryflags/index.txt @@ -6,30 +6,30 @@ default == -1 -XEN +##### ISO 3166-2 subdivisions ##### + +GB-ENG == 901 -XNI +GB-NIR == 902 -XSC +GB-SCT == 903 -XWA +GB-WLS == 904 -XEU -== 905 - -XCA +ES-CT == 906 -XGL +ES-GA == 907 -#south sudan, non official code# -SS -== 737 +##### ISO 3166/MA exceptional reservations ##### + +EU +== 905 ##### ISO 3166-1 based ##### @@ -648,6 +648,9 @@ SB SO == 706 +SS +== 737 + ZA == 710 From 16bc489afb7448f41cb421d3abc4e8e8db7415a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 19 Aug 2023 10:36:29 +0200 Subject: [PATCH 054/126] Move editor automap `.rules` files to separate subfolder For better organization of the `data/editor` folder. --- CMakeLists.txt | 32 +- data/editor/{ => automap}/basic_freeze.rules | 0 data/editor/{ => automap}/ddmax_freeze.rules | 0 data/editor/{ => automap}/ddnet_tiles.rules | 0 data/editor/{ => automap}/ddnet_walls.rules | 0 data/editor/{ => automap}/desert_main.rules | 0 data/editor/{ => automap}/fadeout.rules | 0 data/editor/{ => automap}/generic_clear.rules | 896 ++-- .../{ => automap}/generic_unhookable.rules | 3556 +++++++------- .../generic_unhookable_0.7.rules | 4276 ++++++++--------- data/editor/{ => automap}/grass_main.rules | 0 .../editor/{ => automap}/grass_main_0.7.rules | 0 data/editor/{ => automap}/jungle_main.rules | 0 .../{ => automap}/jungle_midground.rules | 2936 +++++------ data/editor/{ => automap}/round_tiles.rules | 0 data/editor/{ => automap}/water.rules | 0 data/editor/{ => automap}/winter_main.rules | 0 src/game/editor/auto_map.cpp | 7 +- 18 files changed, 5854 insertions(+), 5849 deletions(-) rename data/editor/{ => automap}/basic_freeze.rules (100%) rename data/editor/{ => automap}/ddmax_freeze.rules (100%) rename data/editor/{ => automap}/ddnet_tiles.rules (100%) rename data/editor/{ => automap}/ddnet_walls.rules (100%) rename data/editor/{ => automap}/desert_main.rules (100%) rename data/editor/{ => automap}/fadeout.rules (100%) rename data/editor/{ => automap}/generic_clear.rules (91%) rename data/editor/{ => automap}/generic_unhookable.rules (91%) rename data/editor/{ => automap}/generic_unhookable_0.7.rules (92%) rename data/editor/{ => automap}/grass_main.rules (100%) rename data/editor/{ => automap}/grass_main_0.7.rules (100%) rename data/editor/{ => automap}/jungle_main.rules (100%) rename data/editor/{ => automap}/jungle_midground.rules (93%) rename data/editor/{ => automap}/round_tiles.rules (100%) rename data/editor/{ => automap}/water.rules (100%) rename data/editor/{ => automap}/winter_main.rules (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 193d5f76d..5461196e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1411,14 +1411,25 @@ set(EXPECTED_DATA countryflags/index.txt debug_font.png editor/audio_source.png + editor/automap/basic_freeze.rules + editor/automap/ddmax_freeze.rules + editor/automap/ddnet_tiles.rules + editor/automap/ddnet_walls.rules + editor/automap/desert_main.rules + editor/automap/fadeout.rules + editor/automap/generic_clear.rules + editor/automap/generic_unhookable.rules + editor/automap/generic_unhookable_0.7.rules + editor/automap/grass_main.rules + editor/automap/grass_main_0.7.rules + editor/automap/jungle_main.rules + editor/automap/jungle_midground.rules + editor/automap/round_tiles.rules + editor/automap/water.rules + editor/automap/winter_main.rules editor/background.png - editor/basic_freeze.rules editor/checker.png editor/cursor.png - editor/ddmax_freeze.rules - editor/ddnet_tiles.rules - editor/ddnet_walls.rules - editor/desert_main.rules editor/entities/DDNet.png editor/entities/FNG.png editor/entities/Race.png @@ -1431,23 +1442,12 @@ set(EXPECTED_DATA editor/entities_clear/fng.png editor/entities_clear/race.png editor/entities_clear/vanilla.png - editor/fadeout.rules editor/front.png - editor/generic_clear.rules - editor/generic_unhookable.rules - editor/generic_unhookable_0.7.rules - editor/grass_main.rules - editor/grass_main_0.7.rules - editor/jungle_main.rules - editor/jungle_midground.rules - editor/round_tiles.rules editor/speed_arrow.png editor/speedup.png editor/switch.png editor/tele.png editor/tune.png - editor/water.rules - editor/winter_main.rules emoticons.png extras.png fonts/DejaVuSans.ttf diff --git a/data/editor/basic_freeze.rules b/data/editor/automap/basic_freeze.rules similarity index 100% rename from data/editor/basic_freeze.rules rename to data/editor/automap/basic_freeze.rules diff --git a/data/editor/ddmax_freeze.rules b/data/editor/automap/ddmax_freeze.rules similarity index 100% rename from data/editor/ddmax_freeze.rules rename to data/editor/automap/ddmax_freeze.rules diff --git a/data/editor/ddnet_tiles.rules b/data/editor/automap/ddnet_tiles.rules similarity index 100% rename from data/editor/ddnet_tiles.rules rename to data/editor/automap/ddnet_tiles.rules diff --git a/data/editor/ddnet_walls.rules b/data/editor/automap/ddnet_walls.rules similarity index 100% rename from data/editor/ddnet_walls.rules rename to data/editor/automap/ddnet_walls.rules diff --git a/data/editor/desert_main.rules b/data/editor/automap/desert_main.rules similarity index 100% rename from data/editor/desert_main.rules rename to data/editor/automap/desert_main.rules diff --git a/data/editor/fadeout.rules b/data/editor/automap/fadeout.rules similarity index 100% rename from data/editor/fadeout.rules rename to data/editor/automap/fadeout.rules diff --git a/data/editor/generic_clear.rules b/data/editor/automap/generic_clear.rules similarity index 91% rename from data/editor/generic_clear.rules rename to data/editor/automap/generic_clear.rules index 4dd8849b3..033fb5bf4 100644 --- a/data/editor/generic_clear.rules +++ b/data/editor/automap/generic_clear.rules @@ -1,448 +1,448 @@ -[Random Blocks] - -Index 1 - -Index 1 XFLIP -Random 40 - -Index 1 YFLIP -Random 40 - -Index 1 YFLIP XFLIP -Random 40 - -Index 2 -Random 40 - -Index 2 XFLIP -Random 40 - -Index 2 YFLIP -Random 40 - -Index 2 YFLIP XFLIP -Random 40 - -Index 3 XFLIP -Random 40 - -Index 3 YFLIP -Random 40 - -Index 3 YFLIP XFLIP -Random 40 - -Index 4 -Random 40 - -Index 4 XFLIP -Random 40 - -Index 4 YFLIP -Random 40 - -Index 4 YFLIP XFLIP -Random 40 - -Index 5 -Random 40 - -Index 5 XFLIP -Random 40 - -Index 5 YFLIP -Random 40 - -Index 5 YFLIP XFLIP -Random 40 - -Index 6 -Random 40 - -Index 6 XFLIP -Random 40 - -Index 6 YFLIP -Random 40 - -Index 6 YFLIP XFLIP -Random 40 - -Index 64 -Random 40 - -Index 64 XFLIP -Random 40 - -Index 64 YFLIP -Random 40 - -Index 64 YFLIP XFLIP -Random 40 - -Index 65 -Random 40 - -Index 65 XFLIP -Random 40 - -Index 65 YFLIP -Random 40 - -Index 65 YFLIP XFLIP -Random 40 - -Index 66 -Random 40 - -Index 66 XFLIP -Random 40 - -Index 66 YFLIP -Random 40 - -Index 66 YFLIP XFLIP -Random 40 - -Index 67 -Random 40 - -Index 67 XFLIP -Random 40 - -Index 67 YFLIP -Random 40 - -Index 67 YFLIP XFLIP -Random 40 - -#random 2x2 -Index 19 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 100 - - -#random 3x3 -Index 16 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos 0 2 FULL -Pos 1 2 FULL -Pos 2 2 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 3 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 100 - - -NewRun - -#Remove overlaps -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos -2 -2 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos -1 -2 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 0 -2 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 1 -2 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 2 -2 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos -2 -1 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos -1 -1 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 0 -1 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 1 -1 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 2 -1 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos -2 0 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos -1 0 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 1 0 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 2 0 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos -2 1 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos -1 1 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 0 1 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 1 1 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 2 1 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos -2 2 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos -1 2 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 0 2 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 1 2 INDEX 16 OR 19 - -Index 1 -Pos 0 0 INDEX 16 OR 19 -Pos 2 2 INDEX 16 OR 19 - -NewRun - -#Fill tiles -Index 20 -Pos -1 0 INDEX 19 -Index 35 -Pos 0 -1 INDEX 19 -Index 36 -Pos -1 -1 INDEX 19 - -Index 17 -Pos -1 0 INDEX 16 -Index 18 -Pos -2 0 INDEX 16 -Index 32 -Pos 0 -1 INDEX 16 -Index 33 -Pos -1 -1 INDEX 16 -Index 34 -Pos -2 -1 INDEX 16 -Index 48 -Pos 0 -2 INDEX 16 -Index 49 -Pos -1 -2 INDEX 16 -Index 50 -Pos -2 -2 INDEX 16 - - - -[Random small Blocks] - -Index 1 - -Index 1 XFLIP -Random 40 - -Index 1 YFLIP -Random 40 - -Index 1 YFLIP XFLIP -Random 40 - -Index 2 -Random 40 - -Index 2 XFLIP -Random 40 - -Index 2 YFLIP -Random 40 - -Index 2 YFLIP XFLIP -Random 40 - -Index 3 XFLIP -Random 40 - -Index 3 YFLIP -Random 40 - -Index 3 YFLIP XFLIP -Random 40 - -Index 4 -Random 40 - -Index 4 XFLIP -Random 40 - -Index 4 YFLIP -Random 40 - -Index 4 YFLIP XFLIP -Random 40 - -Index 5 -Random 40 - -Index 5 XFLIP -Random 40 - -Index 5 YFLIP -Random 40 - -Index 5 YFLIP XFLIP -Random 40 - -Index 6 -Random 40 - -Index 6 XFLIP -Random 40 - -Index 6 YFLIP -Random 40 - -Index 6 YFLIP XFLIP -Random 40 - -Index 64 -Random 40 - -Index 64 XFLIP -Random 40 - -Index 64 YFLIP -Random 40 - -Index 64 YFLIP XFLIP -Random 40 - -Index 65 -Random 40 - -Index 65 XFLIP -Random 40 - -Index 65 YFLIP -Random 40 - -Index 65 YFLIP XFLIP -Random 40 - -Index 66 -Random 40 - -Index 66 XFLIP -Random 40 - -Index 66 YFLIP -Random 40 - -Index 66 YFLIP XFLIP -Random 40 - -Index 67 -Random 40 - -Index 67 XFLIP -Random 40 - -Index 67 YFLIP -Random 40 - -Index 67 YFLIP XFLIP -Random 40 - - - -[Random Decoration] - -Index 7 - -Index 7 XFLIP -Random 18 - -Index 7 YFLIP -Random 18 - -Index 7 YFLIP XFLIP -Random 18 - -Index 21 -Random 18 - -Index 21 XFLIP -Random 18 - -Index 21 YFLIP -Random 18 - -Index 21 YFLIP XFLIP -Random 18 - -Index 23 -Random 18 - -Index 23 XFLIP -Random 18 - -Index 23 YFLIP -Random 18 - -Index 23 YFLIP XFLIP -Random 18 - -Index 51 -Random 18 - -Index 51 XFLIP -Random 18 - -Index 51 YFLIP -Random 18 - -Index 51 YFLIP XFLIP -Random 18 - -Index 68 -Random 18 +[Random Blocks] + +Index 1 + +Index 1 XFLIP +Random 40 + +Index 1 YFLIP +Random 40 + +Index 1 YFLIP XFLIP +Random 40 + +Index 2 +Random 40 + +Index 2 XFLIP +Random 40 + +Index 2 YFLIP +Random 40 + +Index 2 YFLIP XFLIP +Random 40 + +Index 3 XFLIP +Random 40 + +Index 3 YFLIP +Random 40 + +Index 3 YFLIP XFLIP +Random 40 + +Index 4 +Random 40 + +Index 4 XFLIP +Random 40 + +Index 4 YFLIP +Random 40 + +Index 4 YFLIP XFLIP +Random 40 + +Index 5 +Random 40 + +Index 5 XFLIP +Random 40 + +Index 5 YFLIP +Random 40 + +Index 5 YFLIP XFLIP +Random 40 + +Index 6 +Random 40 + +Index 6 XFLIP +Random 40 + +Index 6 YFLIP +Random 40 + +Index 6 YFLIP XFLIP +Random 40 + +Index 64 +Random 40 + +Index 64 XFLIP +Random 40 + +Index 64 YFLIP +Random 40 + +Index 64 YFLIP XFLIP +Random 40 + +Index 65 +Random 40 + +Index 65 XFLIP +Random 40 + +Index 65 YFLIP +Random 40 + +Index 65 YFLIP XFLIP +Random 40 + +Index 66 +Random 40 + +Index 66 XFLIP +Random 40 + +Index 66 YFLIP +Random 40 + +Index 66 YFLIP XFLIP +Random 40 + +Index 67 +Random 40 + +Index 67 XFLIP +Random 40 + +Index 67 YFLIP +Random 40 + +Index 67 YFLIP XFLIP +Random 40 + +#random 2x2 +Index 19 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 100 + + +#random 3x3 +Index 16 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos 0 2 FULL +Pos 1 2 FULL +Pos 2 2 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 3 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 100 + + +NewRun + +#Remove overlaps +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos -2 -2 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos -1 -2 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 0 -2 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 1 -2 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 2 -2 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos -2 -1 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos -1 -1 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 0 -1 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 1 -1 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 2 -1 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos -2 0 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos -1 0 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 1 0 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 2 0 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos -2 1 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos -1 1 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 0 1 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 1 1 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 2 1 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos -2 2 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos -1 2 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 0 2 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 1 2 INDEX 16 OR 19 + +Index 1 +Pos 0 0 INDEX 16 OR 19 +Pos 2 2 INDEX 16 OR 19 + +NewRun + +#Fill tiles +Index 20 +Pos -1 0 INDEX 19 +Index 35 +Pos 0 -1 INDEX 19 +Index 36 +Pos -1 -1 INDEX 19 + +Index 17 +Pos -1 0 INDEX 16 +Index 18 +Pos -2 0 INDEX 16 +Index 32 +Pos 0 -1 INDEX 16 +Index 33 +Pos -1 -1 INDEX 16 +Index 34 +Pos -2 -1 INDEX 16 +Index 48 +Pos 0 -2 INDEX 16 +Index 49 +Pos -1 -2 INDEX 16 +Index 50 +Pos -2 -2 INDEX 16 + + + +[Random small Blocks] + +Index 1 + +Index 1 XFLIP +Random 40 + +Index 1 YFLIP +Random 40 + +Index 1 YFLIP XFLIP +Random 40 + +Index 2 +Random 40 + +Index 2 XFLIP +Random 40 + +Index 2 YFLIP +Random 40 + +Index 2 YFLIP XFLIP +Random 40 + +Index 3 XFLIP +Random 40 + +Index 3 YFLIP +Random 40 + +Index 3 YFLIP XFLIP +Random 40 + +Index 4 +Random 40 + +Index 4 XFLIP +Random 40 + +Index 4 YFLIP +Random 40 + +Index 4 YFLIP XFLIP +Random 40 + +Index 5 +Random 40 + +Index 5 XFLIP +Random 40 + +Index 5 YFLIP +Random 40 + +Index 5 YFLIP XFLIP +Random 40 + +Index 6 +Random 40 + +Index 6 XFLIP +Random 40 + +Index 6 YFLIP +Random 40 + +Index 6 YFLIP XFLIP +Random 40 + +Index 64 +Random 40 + +Index 64 XFLIP +Random 40 + +Index 64 YFLIP +Random 40 + +Index 64 YFLIP XFLIP +Random 40 + +Index 65 +Random 40 + +Index 65 XFLIP +Random 40 + +Index 65 YFLIP +Random 40 + +Index 65 YFLIP XFLIP +Random 40 + +Index 66 +Random 40 + +Index 66 XFLIP +Random 40 + +Index 66 YFLIP +Random 40 + +Index 66 YFLIP XFLIP +Random 40 + +Index 67 +Random 40 + +Index 67 XFLIP +Random 40 + +Index 67 YFLIP +Random 40 + +Index 67 YFLIP XFLIP +Random 40 + + + +[Random Decoration] + +Index 7 + +Index 7 XFLIP +Random 18 + +Index 7 YFLIP +Random 18 + +Index 7 YFLIP XFLIP +Random 18 + +Index 21 +Random 18 + +Index 21 XFLIP +Random 18 + +Index 21 YFLIP +Random 18 + +Index 21 YFLIP XFLIP +Random 18 + +Index 23 +Random 18 + +Index 23 XFLIP +Random 18 + +Index 23 YFLIP +Random 18 + +Index 23 YFLIP XFLIP +Random 18 + +Index 51 +Random 18 + +Index 51 XFLIP +Random 18 + +Index 51 YFLIP +Random 18 + +Index 51 YFLIP XFLIP +Random 18 + +Index 68 +Random 18 diff --git a/data/editor/generic_unhookable.rules b/data/editor/automap/generic_unhookable.rules similarity index 91% rename from data/editor/generic_unhookable.rules rename to data/editor/automap/generic_unhookable.rules index e6673d9cd..a32b9677a 100644 --- a/data/editor/generic_unhookable.rules +++ b/data/editor/automap/generic_unhookable.rules @@ -1,1778 +1,1778 @@ -[Random Silver] - -Index 16 - -Index 1 -Random 32 - -Index 1 XFLIP -Random 32 - -Index 1 YFLIP -Random 32 - -Index 1 YFLIP XFLIP -Random 32 - -Index 2 -Random 32 - -Index 2 XFLIP -Random 32 - -Index 2 YFLIP -Random 32 - -Index 2 YFLIP XFLIP -Random 32 - -Index 16 XFLIP -Random 32 - -Index 16 YFLIP -Random 32 - -Index 16 YFLIP XFLIP -Random 32 - -Index 17 -Random 32 - -Index 17 XFLIP -Random 32 - -Index 17 YFLIP -Random 32 - -Index 17 YFLIP XFLIP -Random 32 - -Index 18 -Random 32 - -Index 18 XFLIP -Random 32 - -Index 18 YFLIP -Random 32 - -Index 18 YFLIP XFLIP -Random 32 - -Index 32 -Random 32 - -Index 32 XFLIP -Random 32 - -Index 32 YFLIP -Random 32 - -Index 32 YFLIP XFLIP -Random 32 - -Index 33 -Random 32 - -Index 33 XFLIP -Random 32 - -Index 33 YFLIP -Random 32 - -Index 33 YFLIP XFLIP -Random 32 - -Index 34 -Random 32 - -Index 34 XFLIP -Random 32 - -Index 34 YFLIP -Random 32 - -Index 34 YFLIP XFLIP -Random 32 - -#random 2x2 -Index 3 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -Index 5 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -#random 3x3 -Index 80 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos 0 2 FULL -Pos 1 2 FULL -Pos 2 2 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 3 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 75 - -#random 3x2 -Index 67 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 75 - -NewRun - -#Remove overlaps -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos -2 -2 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos -1 -2 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 0 -2 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 1 -2 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 2 -2 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos -2 -1 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos -1 -1 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 0 -1 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 1 -1 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 2 -1 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos -2 0 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos -1 0 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 1 0 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 2 0 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos -2 1 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos -1 1 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 0 1 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 1 1 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 2 1 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos -2 2 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos -1 2 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 0 2 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 1 2 INDEX 3 OR 5 OR 80 OR 67 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 -Pos 2 2 INDEX 3 OR 5 OR 80 OR 67 - -NewRun - -#Fill tiles -Index 4 -Pos -1 0 INDEX 3 -Index 19 -Pos 0 -1 INDEX 3 -Index 20 -Pos -1 -1 INDEX 3 - -Index 6 -Pos -1 0 INDEX 5 -Index 21 -Pos 0 -1 INDEX 5 -Index 22 -Pos -1 -1 INDEX 5 - -Index 81 -Pos -1 0 INDEX 80 -Index 82 -Pos -2 0 INDEX 80 -Index 96 -Pos 0 -1 INDEX 80 -Index 97 -Pos -1 -1 INDEX 80 -Index 98 -Pos -2 -1 INDEX 80 -Index 112 -Pos 0 -2 INDEX 80 -Index 113 -Pos -1 -2 INDEX 80 -Index 114 -Pos -2 -2 INDEX 80 - -Index 68 -Pos -1 0 INDEX 67 -Index 69 -Pos -2 0 INDEX 67 -Index 83 -Pos 0 -1 INDEX 67 -Index 84 -Pos -1 -1 INDEX 67 -Index 85 -Pos -2 -1 INDEX 67 - - - -[Random Gold] - -Index 23 - -Index 8 -Random 32 - -Index 8 XFLIP -Random 32 - -Index 8 YFLIP -Random 32 - -Index 8 YFLIP XFLIP -Random 32 - -Index 9 -Random 32 - -Index 9 XFLIP -Random 32 - -Index 9 YFLIP -Random 32 - -Index 9 YFLIP XFLIP -Random 32 - -Index 23 XFLIP -Random 32 - -Index 23 YFLIP -Random 32 - -Index 23 YFLIP XFLIP -Random 32 - -Index 24 -Random 32 - -Index 24 XFLIP -Random 32 - -Index 24 YFLIP -Random 32 - -Index 24 YFLIP XFLIP -Random 32 - -Index 25 -Random 32 - -Index 25 XFLIP -Random 32 - -Index 25 YFLIP -Random 32 - -Index 25 YFLIP XFLIP -Random 32 - -Index 39 -Random 32 - -Index 39 XFLIP -Random 32 - -Index 39 YFLIP -Random 32 - -Index 39 YFLIP XFLIP -Random 32 - -Index 40 -Random 32 - -Index 40 XFLIP -Random 32 - -Index 40 YFLIP -Random 32 - -Index 40 YFLIP XFLIP -Random 32 - -Index 41 -Random 32 - -Index 41 XFLIP -Random 32 - -Index 41 YFLIP -Random 32 - -Index 41 YFLIP XFLIP -Random 32 - -#random 2x2 -Index 10 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -Index 12 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -#random 3x3 -Index 87 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos 0 2 FULL -Pos 1 2 FULL -Pos 2 2 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 3 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 75 - -#random 3x2 -Index 74 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 75 - -NewRun - -#Remove overlaps -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos -2 -2 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos -1 -2 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 0 -2 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 1 -2 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 2 -2 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos -2 -1 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos -1 -1 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 0 -1 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 1 -1 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 2 -1 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos -2 0 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos -1 0 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 1 0 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 2 0 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos -2 1 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos -1 1 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 0 1 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 1 1 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 2 1 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos -2 2 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos -1 2 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 0 2 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 1 2 INDEX 10 OR 12 OR 87 OR 74 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 -Pos 2 2 INDEX 10 OR 12 OR 87 OR 74 - -NewRun - -#Fill tiles -Index 11 -Pos -1 0 INDEX 10 -Index 26 -Pos 0 -1 INDEX 10 -Index 27 -Pos -1 -1 INDEX 10 - -Index 13 -Pos -1 0 INDEX 12 -Index 28 -Pos 0 -1 INDEX 12 -Index 29 -Pos -1 -1 INDEX 12 - -Index 88 -Pos -1 0 INDEX 87 -Index 89 -Pos -2 0 INDEX 87 -Index 103 -Pos 0 -1 INDEX 87 -Index 104 -Pos -1 -1 INDEX 87 -Index 105 -Pos -2 -1 INDEX 87 -Index 119 -Pos 0 -2 INDEX 87 -Index 120 -Pos -1 -2 INDEX 87 -Index 121 -Pos -2 -2 INDEX 87 - -Index 75 -Pos -1 0 INDEX 74 -Index 76 -Pos -2 0 INDEX 74 -Index 90 -Pos 0 -1 INDEX 74 -Index 91 -Pos -1 -1 INDEX 74 -Index 92 -Pos -2 -1 INDEX 74 - - - -[Random Bronze] - -Index 144 - -Index 129 -Random 32 - -Index 129 XFLIP -Random 32 - -Index 129 YFLIP -Random 32 - -Index 129 YFLIP XFLIP -Random 32 - -Index 130 -Random 32 - -Index 130 XFLIP -Random 32 - -Index 130 YFLIP -Random 32 - -Index 130 YFLIP XFLIP -Random 32 - -Index 144 XFLIP -Random 32 - -Index 144 YFLIP -Random 32 - -Index 144 YFLIP XFLIP -Random 32 - -Index 145 -Random 32 - -Index 145 XFLIP -Random 32 - -Index 145 YFLIP -Random 32 - -Index 145 YFLIP XFLIP -Random 32 - -Index 146 -Random 32 - -Index 146 XFLIP -Random 32 - -Index 146 YFLIP -Random 32 - -Index 146 YFLIP XFLIP -Random 32 - -Index 160 -Random 32 - -Index 160 XFLIP -Random 32 - -Index 160 YFLIP -Random 32 - -Index 160 YFLIP XFLIP -Random 32 - -Index 161 -Random 32 - -Index 161 XFLIP -Random 32 - -Index 161 YFLIP -Random 32 - -Index 161 YFLIP XFLIP -Random 32 - -Index 162 -Random 32 - -Index 162 XFLIP -Random 32 - -Index 162 YFLIP -Random 32 - -Index 162 YFLIP XFLIP -Random 32 - -#random 2x2 -Index 131 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -Index 133 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -#random 3x3 -Index 208 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos 0 2 FULL -Pos 1 2 FULL -Pos 2 2 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 3 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 75 - -#random 3x2 -Index 195 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 75 - -NewRun - -#Remove overlaps -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos -2 -2 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos -1 -2 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 0 -2 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 1 -2 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 2 -2 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos -2 -1 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos -1 -1 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 0 -1 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 1 -1 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 2 -1 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos -2 0 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos -1 0 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 1 0 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 2 0 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos -2 1 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos -1 1 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 0 1 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 1 1 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 2 1 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos -2 2 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos -1 2 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 0 2 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 1 2 INDEX 131 OR 133 OR 208 OR 195 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 -Pos 2 2 INDEX 131 OR 133 OR 208 OR 195 - -NewRun - -#Fill tiles -Index 132 -Pos -1 0 INDEX 131 -Index 147 -Pos 0 -1 INDEX 131 -Index 148 -Pos -1 -1 INDEX 131 - -Index 134 -Pos -1 0 INDEX 133 -Index 149 -Pos 0 -1 INDEX 133 -Index 150 -Pos -1 -1 INDEX 133 - -Index 209 -Pos -1 0 INDEX 208 -Index 210 -Pos -2 0 INDEX 208 -Index 224 -Pos 0 -1 INDEX 208 -Index 225 -Pos -1 -1 INDEX 208 -Index 226 -Pos -2 -1 INDEX 208 -Index 240 -Pos 0 -2 INDEX 208 -Index 241 -Pos -1 -2 INDEX 208 -Index 242 -Pos -2 -2 INDEX 208 - -Index 196 -Pos -1 0 INDEX 195 -Index 197 -Pos -2 0 INDEX 195 -Index 211 -Pos 0 -1 INDEX 195 -Index 212 -Pos -1 -1 INDEX 195 -Index 213 -Pos -2 -1 INDEX 195 - - - -[Silver/Gold-Mix] - -#Silver -Index 16 - -Index 1 -Random 64 - -Index 1 XFLIP -Random 64 - -Index 1 YFLIP -Random 64 - -Index 1 YFLIP XFLIP -Random 64 - -Index 2 -Random 64 - -Index 2 XFLIP -Random 64 - -Index 2 YFLIP -Random 64 - -Index 2 YFLIP XFLIP -Random 64 - -Index 16 XFLIP -Random 64 - -Index 16 YFLIP -Random 64 - -Index 16 YFLIP XFLIP -Random 64 - -Index 17 -Random 64 - -Index 17 XFLIP -Random 64 - -Index 17 YFLIP -Random 64 - -Index 17 YFLIP XFLIP -Random 64 - -Index 18 -Random 64 - -Index 18 XFLIP -Random 64 - -Index 18 YFLIP -Random 64 - -Index 18 YFLIP XFLIP -Random 64 - -Index 32 -Random 64 - -Index 32 XFLIP -Random 64 - -Index 32 YFLIP -Random 64 - -Index 32 YFLIP XFLIP -Random 64 - -Index 33 -Random 64 - -Index 33 XFLIP -Random 64 - -Index 33 YFLIP -Random 64 - -Index 33 YFLIP XFLIP -Random 64 - -Index 34 -Random 64 - -Index 34 XFLIP -Random 64 - -Index 34 YFLIP -Random 64 - -Index 34 YFLIP XFLIP -Random 64 - -#Gold - -Index 8 -Random 64 - -Index 8 XFLIP -Random 64 - -Index 8 YFLIP -Random 64 - -Index 8 YFLIP XFLIP -Random 64 - -Index 9 -Random 64 - -Index 9 XFLIP -Random 64 - -Index 9 YFLIP -Random 64 - -Index 9 YFLIP XFLIP -Random 64 - -Index 23 -Random 64 - -Index 23 XFLIP -Random 64 - -Index 23 YFLIP -Random 64 - -Index 23 YFLIP XFLIP -Random 64 - -Index 24 -Random 64 - -Index 24 XFLIP -Random 64 - -Index 24 YFLIP -Random 64 - -Index 24 YFLIP XFLIP -Random 64 - -Index 25 -Random 64 - -Index 25 XFLIP -Random 64 - -Index 25 YFLIP -Random 64 - -Index 25 YFLIP XFLIP -Random 64 - -Index 39 -Random 64 - -Index 39 XFLIP -Random 64 - -Index 39 YFLIP -Random 64 - -Index 39 YFLIP XFLIP -Random 64 - -Index 40 -Random 64 - -Index 40 XFLIP -Random 64 - -Index 40 YFLIP -Random 64 - -Index 40 YFLIP XFLIP -Random 64 - -Index 41 -Random 64 - -Index 41 XFLIP -Random 64 - -Index 41 YFLIP -Random 64 - -Index 41 YFLIP XFLIP -Random 64 - - - -[Copper/Silver-Mix] - -#Copper -Index 144 - -Index 129 -Random 64 - -Index 129 XFLIP -Random 64 - -Index 129 YFLIP -Random 64 - -Index 129 YFLIP XFLIP -Random 64 - -Index 130 -Random 64 - -Index 130 XFLIP -Random 64 - -Index 130 YFLIP -Random 64 - -Index 130 YFLIP XFLIP -Random 64 - -Index 144 XFLIP -Random 64 - -Index 144 YFLIP -Random 64 - -Index 144 YFLIP XFLIP -Random 64 - -Index 145 -Random 64 - -Index 145 XFLIP -Random 64 - -Index 145 YFLIP -Random 64 - -Index 145 YFLIP XFLIP -Random 64 - -Index 146 -Random 64 - -Index 146 XFLIP -Random 64 - -Index 146 YFLIP -Random 64 - -Index 146 YFLIP XFLIP -Random 64 - -Index 160 -Random 64 - -Index 160 XFLIP -Random 64 - -Index 160 YFLIP -Random 64 - -Index 160 YFLIP XFLIP -Random 64 - -Index 161 -Random 64 - -Index 161 XFLIP -Random 64 - -Index 161 YFLIP -Random 64 - -Index 161 YFLIP XFLIP -Random 64 - -Index 162 -Random 64 - -Index 162 XFLIP -Random 64 - -Index 162 YFLIP -Random 64 - -Index 162 YFLIP XFLIP -Random 64 - -#Silver - -Index 1 -Random 64 - -Index 1 XFLIP -Random 64 - -Index 1 YFLIP -Random 64 - -Index 1 YFLIP XFLIP -Random 64 - -Index 2 -Random 64 - -Index 2 XFLIP -Random 64 - -Index 2 YFLIP -Random 64 - -Index 2 YFLIP XFLIP -Random 64 - -Index 16 -Random 64 - -Index 16 XFLIP -Random 64 - -Index 16 YFLIP -Random 64 - -Index 16 YFLIP XFLIP -Random 64 - -Index 17 -Random 64 - -Index 17 XFLIP -Random 64 - -Index 17 YFLIP -Random 64 - -Index 17 YFLIP XFLIP -Random 64 - -Index 18 -Random 64 - -Index 18 XFLIP -Random 64 - -Index 18 YFLIP -Random 64 - -Index 18 YFLIP XFLIP -Random 64 - -Index 32 -Random 64 - -Index 32 XFLIP -Random 64 - -Index 32 YFLIP -Random 64 - -Index 32 YFLIP XFLIP -Random 64 - -Index 33 -Random 64 - -Index 33 XFLIP -Random 64 - -Index 33 YFLIP -Random 64 - -Index 33 YFLIP XFLIP -Random 64 - -Index 34 -Random 64 - -Index 34 XFLIP -Random 64 - -Index 34 YFLIP -Random 64 - -Index 34 YFLIP XFLIP -Random 64 - - - -[Gold/Copper-Mix] - -#Gold -Index 23 - -Index 8 -Random 64 - -Index 8 XFLIP -Random 64 - -Index 8 YFLIP -Random 64 - -Index 8 YFLIP XFLIP -Random 64 - -Index 9 -Random 64 - -Index 9 XFLIP -Random 64 - -Index 9 YFLIP -Random 64 - -Index 9 YFLIP XFLIP -Random 64 - -Index 23 XFLIP -Random 64 - -Index 23 YFLIP -Random 64 - -Index 23 YFLIP XFLIP -Random 64 - -Index 24 -Random 64 - -Index 24 XFLIP -Random 64 - -Index 24 YFLIP -Random 64 - -Index 24 YFLIP XFLIP -Random 64 - -Index 25 -Random 64 - -Index 25 XFLIP -Random 64 - -Index 25 YFLIP -Random 64 - -Index 25 YFLIP XFLIP -Random 64 - -Index 39 -Random 64 - -Index 39 XFLIP -Random 64 - -Index 39 YFLIP -Random 64 - -Index 39 YFLIP XFLIP -Random 64 - -Index 40 -Random 64 - -Index 40 XFLIP -Random 64 - -Index 40 YFLIP -Random 64 - -Index 40 YFLIP XFLIP -Random 64 - -Index 41 -Random 64 - -Index 41 XFLIP -Random 64 - -Index 41 YFLIP -Random 64 - -Index 41 YFLIP XFLIP -Random 64 - -#Copper - -Index 129 -Random 64 - -Index 129 XFLIP -Random 64 - -Index 129 YFLIP -Random 64 - -Index 129 YFLIP XFLIP -Random 64 - -Index 130 -Random 64 - -Index 130 XFLIP -Random 64 - -Index 130 YFLIP -Random 64 - -Index 130 YFLIP XFLIP -Random 64 - -Index 144 -Random 64 - -Index 144 XFLIP -Random 64 - -Index 144 YFLIP -Random 64 - -Index 144 YFLIP XFLIP -Random 64 - -Index 145 -Random 64 - -Index 145 XFLIP -Random 64 - -Index 145 YFLIP -Random 64 - -Index 145 YFLIP XFLIP -Random 64 - -Index 146 -Random 64 - -Index 146 XFLIP -Random 64 - -Index 146 YFLIP -Random 64 - -Index 146 YFLIP XFLIP -Random 64 - -Index 160 -Random 64 - -Index 160 XFLIP -Random 64 - -Index 160 YFLIP -Random 64 - -Index 160 YFLIP XFLIP -Random 64 - -Index 161 -Random 64 - -Index 161 XFLIP -Random 64 - -Index 161 YFLIP -Random 64 - -Index 161 YFLIP XFLIP -Random 64 - -Index 162 -Random 64 - -Index 162 XFLIP -Random 64 - -Index 162 YFLIP -Random 64 - -Index 162 YFLIP XFLIP -Random 64 - - - -[Mix All] - -#Silver -Index 16 - -Index 1 -Random 96 - -Index 1 XFLIP -Random 96 - -Index 1 YFLIP -Random 96 - -Index 1 YFLIP XFLIP -Random 96 - -Index 2 -Random 96 - -Index 2 XFLIP -Random 96 - -Index 2 YFLIP -Random 96 - -Index 2 YFLIP XFLIP -Random 96 - -Index 16 XFLIP -Random 96 - -Index 16 YFLIP -Random 96 - -Index 16 YFLIP XFLIP -Random 96 - -Index 17 -Random 96 - -Index 17 XFLIP -Random 96 - -Index 17 YFLIP -Random 96 - -Index 17 YFLIP XFLIP -Random 96 - -Index 18 -Random 96 - -Index 18 XFLIP -Random 96 - -Index 18 YFLIP -Random 96 - -Index 18 YFLIP XFLIP -Random 96 - -Index 32 -Random 96 - -Index 32 XFLIP -Random 96 - -Index 32 YFLIP -Random 96 - -Index 32 YFLIP XFLIP -Random 96 - -Index 33 -Random 96 - -Index 33 XFLIP -Random 96 - -Index 33 YFLIP -Random 96 - -Index 33 YFLIP XFLIP -Random 96 - -Index 34 -Random 96 - -Index 34 XFLIP -Random 96 - -Index 34 YFLIP -Random 96 - -Index 34 YFLIP XFLIP -Random 96 - -#Gold - -Index 8 -Random 96 - -Index 8 XFLIP -Random 96 - -Index 8 YFLIP -Random 96 - -Index 8 YFLIP XFLIP -Random 96 - -Index 9 -Random 96 - -Index 9 XFLIP -Random 96 - -Index 9 YFLIP -Random 96 - -Index 9 YFLIP XFLIP -Random 96 - -Index 23 -Random 96 - -Index 23 XFLIP -Random 96 - -Index 23 YFLIP -Random 96 - -Index 23 YFLIP XFLIP -Random 96 - -Index 24 -Random 96 - -Index 24 XFLIP -Random 96 - -Index 24 YFLIP -Random 96 - -Index 24 YFLIP XFLIP -Random 96 - -Index 25 -Random 96 - -Index 25 XFLIP -Random 96 - -Index 25 YFLIP -Random 96 - -Index 25 YFLIP XFLIP -Random 96 - -Index 39 -Random 96 - -Index 39 XFLIP -Random 96 - -Index 39 YFLIP -Random 96 - -Index 39 YFLIP XFLIP -Random 96 - -Index 40 -Random 96 - -Index 40 XFLIP -Random 96 - -Index 40 YFLIP -Random 96 - -Index 40 YFLIP XFLIP -Random 96 - -Index 41 -Random 96 - -Index 41 XFLIP -Random 96 - -Index 41 YFLIP -Random 96 - -Index 41 YFLIP XFLIP -Random 96 - -#Copper - -Index 129 -Random 96 - -Index 129 XFLIP -Random 96 - -Index 129 YFLIP -Random 96 - -Index 129 YFLIP XFLIP -Random 96 - -Index 130 -Random 96 - -Index 130 XFLIP -Random 96 - -Index 130 YFLIP -Random 96 - -Index 130 YFLIP XFLIP -Random 96 - -Index 144 -Random 96 - -Index 144 XFLIP -Random 96 - -Index 144 YFLIP -Random 96 - -Index 144 YFLIP XFLIP -Random 96 - -Index 145 -Random 96 - -Index 145 XFLIP -Random 96 - -Index 145 YFLIP -Random 96 - -Index 145 YFLIP XFLIP -Random 96 - -Index 146 -Random 96 - -Index 146 XFLIP -Random 96 - -Index 146 YFLIP -Random 96 - -Index 146 YFLIP XFLIP -Random 96 - -Index 160 -Random 96 - -Index 160 XFLIP -Random 96 - -Index 160 YFLIP -Random 96 - -Index 160 YFLIP XFLIP -Random 96 - -Index 161 -Random 96 - -Index 161 XFLIP -Random 96 - -Index 161 YFLIP -Random 96 - -Index 161 YFLIP XFLIP -Random 96 - -Index 162 -Random 96 - -Index 162 XFLIP -Random 96 - -Index 162 YFLIP -Random 96 - -Index 162 YFLIP XFLIP -Random 96 +[Random Silver] + +Index 16 + +Index 1 +Random 32 + +Index 1 XFLIP +Random 32 + +Index 1 YFLIP +Random 32 + +Index 1 YFLIP XFLIP +Random 32 + +Index 2 +Random 32 + +Index 2 XFLIP +Random 32 + +Index 2 YFLIP +Random 32 + +Index 2 YFLIP XFLIP +Random 32 + +Index 16 XFLIP +Random 32 + +Index 16 YFLIP +Random 32 + +Index 16 YFLIP XFLIP +Random 32 + +Index 17 +Random 32 + +Index 17 XFLIP +Random 32 + +Index 17 YFLIP +Random 32 + +Index 17 YFLIP XFLIP +Random 32 + +Index 18 +Random 32 + +Index 18 XFLIP +Random 32 + +Index 18 YFLIP +Random 32 + +Index 18 YFLIP XFLIP +Random 32 + +Index 32 +Random 32 + +Index 32 XFLIP +Random 32 + +Index 32 YFLIP +Random 32 + +Index 32 YFLIP XFLIP +Random 32 + +Index 33 +Random 32 + +Index 33 XFLIP +Random 32 + +Index 33 YFLIP +Random 32 + +Index 33 YFLIP XFLIP +Random 32 + +Index 34 +Random 32 + +Index 34 XFLIP +Random 32 + +Index 34 YFLIP +Random 32 + +Index 34 YFLIP XFLIP +Random 32 + +#random 2x2 +Index 3 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +Index 5 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +#random 3x3 +Index 80 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos 0 2 FULL +Pos 1 2 FULL +Pos 2 2 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 3 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 75 + +#random 3x2 +Index 67 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 75 + +NewRun + +#Remove overlaps +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos -2 -2 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos -1 -2 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 0 -2 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 1 -2 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 2 -2 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos -2 -1 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos -1 -1 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 0 -1 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 1 -1 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 2 -1 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos -2 0 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos -1 0 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 1 0 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 2 0 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos -2 1 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos -1 1 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 0 1 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 1 1 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 2 1 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos -2 2 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos -1 2 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 0 2 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 1 2 INDEX 3 OR 5 OR 80 OR 67 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 +Pos 2 2 INDEX 3 OR 5 OR 80 OR 67 + +NewRun + +#Fill tiles +Index 4 +Pos -1 0 INDEX 3 +Index 19 +Pos 0 -1 INDEX 3 +Index 20 +Pos -1 -1 INDEX 3 + +Index 6 +Pos -1 0 INDEX 5 +Index 21 +Pos 0 -1 INDEX 5 +Index 22 +Pos -1 -1 INDEX 5 + +Index 81 +Pos -1 0 INDEX 80 +Index 82 +Pos -2 0 INDEX 80 +Index 96 +Pos 0 -1 INDEX 80 +Index 97 +Pos -1 -1 INDEX 80 +Index 98 +Pos -2 -1 INDEX 80 +Index 112 +Pos 0 -2 INDEX 80 +Index 113 +Pos -1 -2 INDEX 80 +Index 114 +Pos -2 -2 INDEX 80 + +Index 68 +Pos -1 0 INDEX 67 +Index 69 +Pos -2 0 INDEX 67 +Index 83 +Pos 0 -1 INDEX 67 +Index 84 +Pos -1 -1 INDEX 67 +Index 85 +Pos -2 -1 INDEX 67 + + + +[Random Gold] + +Index 23 + +Index 8 +Random 32 + +Index 8 XFLIP +Random 32 + +Index 8 YFLIP +Random 32 + +Index 8 YFLIP XFLIP +Random 32 + +Index 9 +Random 32 + +Index 9 XFLIP +Random 32 + +Index 9 YFLIP +Random 32 + +Index 9 YFLIP XFLIP +Random 32 + +Index 23 XFLIP +Random 32 + +Index 23 YFLIP +Random 32 + +Index 23 YFLIP XFLIP +Random 32 + +Index 24 +Random 32 + +Index 24 XFLIP +Random 32 + +Index 24 YFLIP +Random 32 + +Index 24 YFLIP XFLIP +Random 32 + +Index 25 +Random 32 + +Index 25 XFLIP +Random 32 + +Index 25 YFLIP +Random 32 + +Index 25 YFLIP XFLIP +Random 32 + +Index 39 +Random 32 + +Index 39 XFLIP +Random 32 + +Index 39 YFLIP +Random 32 + +Index 39 YFLIP XFLIP +Random 32 + +Index 40 +Random 32 + +Index 40 XFLIP +Random 32 + +Index 40 YFLIP +Random 32 + +Index 40 YFLIP XFLIP +Random 32 + +Index 41 +Random 32 + +Index 41 XFLIP +Random 32 + +Index 41 YFLIP +Random 32 + +Index 41 YFLIP XFLIP +Random 32 + +#random 2x2 +Index 10 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +Index 12 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +#random 3x3 +Index 87 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos 0 2 FULL +Pos 1 2 FULL +Pos 2 2 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 3 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 75 + +#random 3x2 +Index 74 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 75 + +NewRun + +#Remove overlaps +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos -2 -2 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos -1 -2 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 0 -2 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 1 -2 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 2 -2 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos -2 -1 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos -1 -1 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 0 -1 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 1 -1 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 2 -1 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos -2 0 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos -1 0 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 1 0 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 2 0 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos -2 1 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos -1 1 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 0 1 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 1 1 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 2 1 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos -2 2 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos -1 2 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 0 2 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 1 2 INDEX 10 OR 12 OR 87 OR 74 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 +Pos 2 2 INDEX 10 OR 12 OR 87 OR 74 + +NewRun + +#Fill tiles +Index 11 +Pos -1 0 INDEX 10 +Index 26 +Pos 0 -1 INDEX 10 +Index 27 +Pos -1 -1 INDEX 10 + +Index 13 +Pos -1 0 INDEX 12 +Index 28 +Pos 0 -1 INDEX 12 +Index 29 +Pos -1 -1 INDEX 12 + +Index 88 +Pos -1 0 INDEX 87 +Index 89 +Pos -2 0 INDEX 87 +Index 103 +Pos 0 -1 INDEX 87 +Index 104 +Pos -1 -1 INDEX 87 +Index 105 +Pos -2 -1 INDEX 87 +Index 119 +Pos 0 -2 INDEX 87 +Index 120 +Pos -1 -2 INDEX 87 +Index 121 +Pos -2 -2 INDEX 87 + +Index 75 +Pos -1 0 INDEX 74 +Index 76 +Pos -2 0 INDEX 74 +Index 90 +Pos 0 -1 INDEX 74 +Index 91 +Pos -1 -1 INDEX 74 +Index 92 +Pos -2 -1 INDEX 74 + + + +[Random Bronze] + +Index 144 + +Index 129 +Random 32 + +Index 129 XFLIP +Random 32 + +Index 129 YFLIP +Random 32 + +Index 129 YFLIP XFLIP +Random 32 + +Index 130 +Random 32 + +Index 130 XFLIP +Random 32 + +Index 130 YFLIP +Random 32 + +Index 130 YFLIP XFLIP +Random 32 + +Index 144 XFLIP +Random 32 + +Index 144 YFLIP +Random 32 + +Index 144 YFLIP XFLIP +Random 32 + +Index 145 +Random 32 + +Index 145 XFLIP +Random 32 + +Index 145 YFLIP +Random 32 + +Index 145 YFLIP XFLIP +Random 32 + +Index 146 +Random 32 + +Index 146 XFLIP +Random 32 + +Index 146 YFLIP +Random 32 + +Index 146 YFLIP XFLIP +Random 32 + +Index 160 +Random 32 + +Index 160 XFLIP +Random 32 + +Index 160 YFLIP +Random 32 + +Index 160 YFLIP XFLIP +Random 32 + +Index 161 +Random 32 + +Index 161 XFLIP +Random 32 + +Index 161 YFLIP +Random 32 + +Index 161 YFLIP XFLIP +Random 32 + +Index 162 +Random 32 + +Index 162 XFLIP +Random 32 + +Index 162 YFLIP +Random 32 + +Index 162 YFLIP XFLIP +Random 32 + +#random 2x2 +Index 131 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +Index 133 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +#random 3x3 +Index 208 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos 0 2 FULL +Pos 1 2 FULL +Pos 2 2 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 3 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 75 + +#random 3x2 +Index 195 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 75 + +NewRun + +#Remove overlaps +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos -2 -2 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos -1 -2 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 0 -2 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 1 -2 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 2 -2 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos -2 -1 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos -1 -1 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 0 -1 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 1 -1 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 2 -1 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos -2 0 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos -1 0 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 1 0 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 2 0 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos -2 1 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos -1 1 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 0 1 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 1 1 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 2 1 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos -2 2 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos -1 2 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 0 2 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 1 2 INDEX 131 OR 133 OR 208 OR 195 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 +Pos 2 2 INDEX 131 OR 133 OR 208 OR 195 + +NewRun + +#Fill tiles +Index 132 +Pos -1 0 INDEX 131 +Index 147 +Pos 0 -1 INDEX 131 +Index 148 +Pos -1 -1 INDEX 131 + +Index 134 +Pos -1 0 INDEX 133 +Index 149 +Pos 0 -1 INDEX 133 +Index 150 +Pos -1 -1 INDEX 133 + +Index 209 +Pos -1 0 INDEX 208 +Index 210 +Pos -2 0 INDEX 208 +Index 224 +Pos 0 -1 INDEX 208 +Index 225 +Pos -1 -1 INDEX 208 +Index 226 +Pos -2 -1 INDEX 208 +Index 240 +Pos 0 -2 INDEX 208 +Index 241 +Pos -1 -2 INDEX 208 +Index 242 +Pos -2 -2 INDEX 208 + +Index 196 +Pos -1 0 INDEX 195 +Index 197 +Pos -2 0 INDEX 195 +Index 211 +Pos 0 -1 INDEX 195 +Index 212 +Pos -1 -1 INDEX 195 +Index 213 +Pos -2 -1 INDEX 195 + + + +[Silver/Gold-Mix] + +#Silver +Index 16 + +Index 1 +Random 64 + +Index 1 XFLIP +Random 64 + +Index 1 YFLIP +Random 64 + +Index 1 YFLIP XFLIP +Random 64 + +Index 2 +Random 64 + +Index 2 XFLIP +Random 64 + +Index 2 YFLIP +Random 64 + +Index 2 YFLIP XFLIP +Random 64 + +Index 16 XFLIP +Random 64 + +Index 16 YFLIP +Random 64 + +Index 16 YFLIP XFLIP +Random 64 + +Index 17 +Random 64 + +Index 17 XFLIP +Random 64 + +Index 17 YFLIP +Random 64 + +Index 17 YFLIP XFLIP +Random 64 + +Index 18 +Random 64 + +Index 18 XFLIP +Random 64 + +Index 18 YFLIP +Random 64 + +Index 18 YFLIP XFLIP +Random 64 + +Index 32 +Random 64 + +Index 32 XFLIP +Random 64 + +Index 32 YFLIP +Random 64 + +Index 32 YFLIP XFLIP +Random 64 + +Index 33 +Random 64 + +Index 33 XFLIP +Random 64 + +Index 33 YFLIP +Random 64 + +Index 33 YFLIP XFLIP +Random 64 + +Index 34 +Random 64 + +Index 34 XFLIP +Random 64 + +Index 34 YFLIP +Random 64 + +Index 34 YFLIP XFLIP +Random 64 + +#Gold + +Index 8 +Random 64 + +Index 8 XFLIP +Random 64 + +Index 8 YFLIP +Random 64 + +Index 8 YFLIP XFLIP +Random 64 + +Index 9 +Random 64 + +Index 9 XFLIP +Random 64 + +Index 9 YFLIP +Random 64 + +Index 9 YFLIP XFLIP +Random 64 + +Index 23 +Random 64 + +Index 23 XFLIP +Random 64 + +Index 23 YFLIP +Random 64 + +Index 23 YFLIP XFLIP +Random 64 + +Index 24 +Random 64 + +Index 24 XFLIP +Random 64 + +Index 24 YFLIP +Random 64 + +Index 24 YFLIP XFLIP +Random 64 + +Index 25 +Random 64 + +Index 25 XFLIP +Random 64 + +Index 25 YFLIP +Random 64 + +Index 25 YFLIP XFLIP +Random 64 + +Index 39 +Random 64 + +Index 39 XFLIP +Random 64 + +Index 39 YFLIP +Random 64 + +Index 39 YFLIP XFLIP +Random 64 + +Index 40 +Random 64 + +Index 40 XFLIP +Random 64 + +Index 40 YFLIP +Random 64 + +Index 40 YFLIP XFLIP +Random 64 + +Index 41 +Random 64 + +Index 41 XFLIP +Random 64 + +Index 41 YFLIP +Random 64 + +Index 41 YFLIP XFLIP +Random 64 + + + +[Copper/Silver-Mix] + +#Copper +Index 144 + +Index 129 +Random 64 + +Index 129 XFLIP +Random 64 + +Index 129 YFLIP +Random 64 + +Index 129 YFLIP XFLIP +Random 64 + +Index 130 +Random 64 + +Index 130 XFLIP +Random 64 + +Index 130 YFLIP +Random 64 + +Index 130 YFLIP XFLIP +Random 64 + +Index 144 XFLIP +Random 64 + +Index 144 YFLIP +Random 64 + +Index 144 YFLIP XFLIP +Random 64 + +Index 145 +Random 64 + +Index 145 XFLIP +Random 64 + +Index 145 YFLIP +Random 64 + +Index 145 YFLIP XFLIP +Random 64 + +Index 146 +Random 64 + +Index 146 XFLIP +Random 64 + +Index 146 YFLIP +Random 64 + +Index 146 YFLIP XFLIP +Random 64 + +Index 160 +Random 64 + +Index 160 XFLIP +Random 64 + +Index 160 YFLIP +Random 64 + +Index 160 YFLIP XFLIP +Random 64 + +Index 161 +Random 64 + +Index 161 XFLIP +Random 64 + +Index 161 YFLIP +Random 64 + +Index 161 YFLIP XFLIP +Random 64 + +Index 162 +Random 64 + +Index 162 XFLIP +Random 64 + +Index 162 YFLIP +Random 64 + +Index 162 YFLIP XFLIP +Random 64 + +#Silver + +Index 1 +Random 64 + +Index 1 XFLIP +Random 64 + +Index 1 YFLIP +Random 64 + +Index 1 YFLIP XFLIP +Random 64 + +Index 2 +Random 64 + +Index 2 XFLIP +Random 64 + +Index 2 YFLIP +Random 64 + +Index 2 YFLIP XFLIP +Random 64 + +Index 16 +Random 64 + +Index 16 XFLIP +Random 64 + +Index 16 YFLIP +Random 64 + +Index 16 YFLIP XFLIP +Random 64 + +Index 17 +Random 64 + +Index 17 XFLIP +Random 64 + +Index 17 YFLIP +Random 64 + +Index 17 YFLIP XFLIP +Random 64 + +Index 18 +Random 64 + +Index 18 XFLIP +Random 64 + +Index 18 YFLIP +Random 64 + +Index 18 YFLIP XFLIP +Random 64 + +Index 32 +Random 64 + +Index 32 XFLIP +Random 64 + +Index 32 YFLIP +Random 64 + +Index 32 YFLIP XFLIP +Random 64 + +Index 33 +Random 64 + +Index 33 XFLIP +Random 64 + +Index 33 YFLIP +Random 64 + +Index 33 YFLIP XFLIP +Random 64 + +Index 34 +Random 64 + +Index 34 XFLIP +Random 64 + +Index 34 YFLIP +Random 64 + +Index 34 YFLIP XFLIP +Random 64 + + + +[Gold/Copper-Mix] + +#Gold +Index 23 + +Index 8 +Random 64 + +Index 8 XFLIP +Random 64 + +Index 8 YFLIP +Random 64 + +Index 8 YFLIP XFLIP +Random 64 + +Index 9 +Random 64 + +Index 9 XFLIP +Random 64 + +Index 9 YFLIP +Random 64 + +Index 9 YFLIP XFLIP +Random 64 + +Index 23 XFLIP +Random 64 + +Index 23 YFLIP +Random 64 + +Index 23 YFLIP XFLIP +Random 64 + +Index 24 +Random 64 + +Index 24 XFLIP +Random 64 + +Index 24 YFLIP +Random 64 + +Index 24 YFLIP XFLIP +Random 64 + +Index 25 +Random 64 + +Index 25 XFLIP +Random 64 + +Index 25 YFLIP +Random 64 + +Index 25 YFLIP XFLIP +Random 64 + +Index 39 +Random 64 + +Index 39 XFLIP +Random 64 + +Index 39 YFLIP +Random 64 + +Index 39 YFLIP XFLIP +Random 64 + +Index 40 +Random 64 + +Index 40 XFLIP +Random 64 + +Index 40 YFLIP +Random 64 + +Index 40 YFLIP XFLIP +Random 64 + +Index 41 +Random 64 + +Index 41 XFLIP +Random 64 + +Index 41 YFLIP +Random 64 + +Index 41 YFLIP XFLIP +Random 64 + +#Copper + +Index 129 +Random 64 + +Index 129 XFLIP +Random 64 + +Index 129 YFLIP +Random 64 + +Index 129 YFLIP XFLIP +Random 64 + +Index 130 +Random 64 + +Index 130 XFLIP +Random 64 + +Index 130 YFLIP +Random 64 + +Index 130 YFLIP XFLIP +Random 64 + +Index 144 +Random 64 + +Index 144 XFLIP +Random 64 + +Index 144 YFLIP +Random 64 + +Index 144 YFLIP XFLIP +Random 64 + +Index 145 +Random 64 + +Index 145 XFLIP +Random 64 + +Index 145 YFLIP +Random 64 + +Index 145 YFLIP XFLIP +Random 64 + +Index 146 +Random 64 + +Index 146 XFLIP +Random 64 + +Index 146 YFLIP +Random 64 + +Index 146 YFLIP XFLIP +Random 64 + +Index 160 +Random 64 + +Index 160 XFLIP +Random 64 + +Index 160 YFLIP +Random 64 + +Index 160 YFLIP XFLIP +Random 64 + +Index 161 +Random 64 + +Index 161 XFLIP +Random 64 + +Index 161 YFLIP +Random 64 + +Index 161 YFLIP XFLIP +Random 64 + +Index 162 +Random 64 + +Index 162 XFLIP +Random 64 + +Index 162 YFLIP +Random 64 + +Index 162 YFLIP XFLIP +Random 64 + + + +[Mix All] + +#Silver +Index 16 + +Index 1 +Random 96 + +Index 1 XFLIP +Random 96 + +Index 1 YFLIP +Random 96 + +Index 1 YFLIP XFLIP +Random 96 + +Index 2 +Random 96 + +Index 2 XFLIP +Random 96 + +Index 2 YFLIP +Random 96 + +Index 2 YFLIP XFLIP +Random 96 + +Index 16 XFLIP +Random 96 + +Index 16 YFLIP +Random 96 + +Index 16 YFLIP XFLIP +Random 96 + +Index 17 +Random 96 + +Index 17 XFLIP +Random 96 + +Index 17 YFLIP +Random 96 + +Index 17 YFLIP XFLIP +Random 96 + +Index 18 +Random 96 + +Index 18 XFLIP +Random 96 + +Index 18 YFLIP +Random 96 + +Index 18 YFLIP XFLIP +Random 96 + +Index 32 +Random 96 + +Index 32 XFLIP +Random 96 + +Index 32 YFLIP +Random 96 + +Index 32 YFLIP XFLIP +Random 96 + +Index 33 +Random 96 + +Index 33 XFLIP +Random 96 + +Index 33 YFLIP +Random 96 + +Index 33 YFLIP XFLIP +Random 96 + +Index 34 +Random 96 + +Index 34 XFLIP +Random 96 + +Index 34 YFLIP +Random 96 + +Index 34 YFLIP XFLIP +Random 96 + +#Gold + +Index 8 +Random 96 + +Index 8 XFLIP +Random 96 + +Index 8 YFLIP +Random 96 + +Index 8 YFLIP XFLIP +Random 96 + +Index 9 +Random 96 + +Index 9 XFLIP +Random 96 + +Index 9 YFLIP +Random 96 + +Index 9 YFLIP XFLIP +Random 96 + +Index 23 +Random 96 + +Index 23 XFLIP +Random 96 + +Index 23 YFLIP +Random 96 + +Index 23 YFLIP XFLIP +Random 96 + +Index 24 +Random 96 + +Index 24 XFLIP +Random 96 + +Index 24 YFLIP +Random 96 + +Index 24 YFLIP XFLIP +Random 96 + +Index 25 +Random 96 + +Index 25 XFLIP +Random 96 + +Index 25 YFLIP +Random 96 + +Index 25 YFLIP XFLIP +Random 96 + +Index 39 +Random 96 + +Index 39 XFLIP +Random 96 + +Index 39 YFLIP +Random 96 + +Index 39 YFLIP XFLIP +Random 96 + +Index 40 +Random 96 + +Index 40 XFLIP +Random 96 + +Index 40 YFLIP +Random 96 + +Index 40 YFLIP XFLIP +Random 96 + +Index 41 +Random 96 + +Index 41 XFLIP +Random 96 + +Index 41 YFLIP +Random 96 + +Index 41 YFLIP XFLIP +Random 96 + +#Copper + +Index 129 +Random 96 + +Index 129 XFLIP +Random 96 + +Index 129 YFLIP +Random 96 + +Index 129 YFLIP XFLIP +Random 96 + +Index 130 +Random 96 + +Index 130 XFLIP +Random 96 + +Index 130 YFLIP +Random 96 + +Index 130 YFLIP XFLIP +Random 96 + +Index 144 +Random 96 + +Index 144 XFLIP +Random 96 + +Index 144 YFLIP +Random 96 + +Index 144 YFLIP XFLIP +Random 96 + +Index 145 +Random 96 + +Index 145 XFLIP +Random 96 + +Index 145 YFLIP +Random 96 + +Index 145 YFLIP XFLIP +Random 96 + +Index 146 +Random 96 + +Index 146 XFLIP +Random 96 + +Index 146 YFLIP +Random 96 + +Index 146 YFLIP XFLIP +Random 96 + +Index 160 +Random 96 + +Index 160 XFLIP +Random 96 + +Index 160 YFLIP +Random 96 + +Index 160 YFLIP XFLIP +Random 96 + +Index 161 +Random 96 + +Index 161 XFLIP +Random 96 + +Index 161 YFLIP +Random 96 + +Index 161 YFLIP XFLIP +Random 96 + +Index 162 +Random 96 + +Index 162 XFLIP +Random 96 + +Index 162 YFLIP +Random 96 + +Index 162 YFLIP XFLIP +Random 96 diff --git a/data/editor/generic_unhookable_0.7.rules b/data/editor/automap/generic_unhookable_0.7.rules similarity index 92% rename from data/editor/generic_unhookable_0.7.rules rename to data/editor/automap/generic_unhookable_0.7.rules index d7e6d6237..8e6335a88 100644 --- a/data/editor/generic_unhookable_0.7.rules +++ b/data/editor/automap/generic_unhookable_0.7.rules @@ -1,2138 +1,2138 @@ -[Random Silver] - -Index 16 - -Index 1 -Random 40 - -Index 1 XFLIP -Random 40 - -Index 1 YFLIP -Random 40 - -Index 1 YFLIP XFLIP -Random 40 - -Index 2 -Random 40 - -Index 2 XFLIP -Random 40 - -Index 2 YFLIP -Random 40 - -Index 2 YFLIP XFLIP -Random 40 - -Index 16 XFLIP -Random 40 - -Index 16 YFLIP -Random 40 - -Index 16 YFLIP XFLIP -Random 40 - -Index 17 -Random 40 - -Index 17 XFLIP -Random 40 - -Index 17 YFLIP -Random 40 - -Index 17 YFLIP XFLIP -Random 40 - -Index 18 -Random 40 - -Index 18 XFLIP -Random 40 - -Index 18 YFLIP -Random 40 - -Index 18 YFLIP XFLIP -Random 40 - -Index 32 -Random 40 - -Index 32 XFLIP -Random 40 - -Index 32 YFLIP -Random 40 - -Index 32 YFLIP XFLIP -Random 40 - -Index 33 -Random 40 - -Index 33 XFLIP -Random 40 - -Index 33 YFLIP -Random 40 - -Index 33 YFLIP XFLIP -Random 40 - -Index 34 -Random 40 - -Index 34 XFLIP -Random 40 - -Index 34 YFLIP -Random 40 - -Index 34 YFLIP XFLIP -Random 40 - -Index 38 -Random 40 - -Index 38 XFLIP -Random 40 - -Index 38 YFLIP -Random 40 - -Index 38 YFLIP XFLIP -Random 40 - -Index 54 -Random 40 - -Index 54 XFLIP -Random 40 - -Index 54 YFLIP -Random 40 - -Index 54 YFLIP XFLIP -Random 40 - -#random 2x2 -Index 3 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -Index 5 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -#random 3x3 -Index 80 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos 0 2 FULL -Pos 1 2 FULL -Pos 2 2 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 3 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 75 - -#random 3x2 -Index 67 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 100 - -Index 99 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 100 - -NewRun - -#Remove overlaps -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos -2 -2 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos -1 -2 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 0 -2 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 1 -2 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 2 -2 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos -2 -1 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos -1 -1 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 0 -1 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 1 -1 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 2 -1 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos -2 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos -1 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 1 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 2 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos -2 1 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos -1 1 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 0 1 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 1 1 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 2 1 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos -2 2 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos -1 2 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 0 2 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 1 2 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -Index 16 -Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 -Pos 2 2 INDEX 3 OR 5 OR 80 OR 67 OR 99 - -NewRun - -#Fill tiles -Index 4 -Pos -1 0 INDEX 3 -Index 19 -Pos 0 -1 INDEX 3 -Index 20 -Pos -1 -1 INDEX 3 - -Index 6 -Pos -1 0 INDEX 5 -Index 21 -Pos 0 -1 INDEX 5 -Index 22 -Pos -1 -1 INDEX 5 - -Index 81 -Pos -1 0 INDEX 80 -Index 82 -Pos -2 0 INDEX 80 -Index 96 -Pos 0 -1 INDEX 80 -Index 97 -Pos -1 -1 INDEX 80 -Index 98 -Pos -2 -1 INDEX 80 -Index 112 -Pos 0 -2 INDEX 80 -Index 113 -Pos -1 -2 INDEX 80 -Index 114 -Pos -2 -2 INDEX 80 - -Index 68 -Pos -1 0 INDEX 67 -Index 69 -Pos -2 0 INDEX 67 -Index 83 -Pos 0 -1 INDEX 67 -Index 84 -Pos -1 -1 INDEX 67 -Index 85 -Pos -2 -1 INDEX 67 - -Index 100 -Pos -1 0 INDEX 99 -Index 101 -Pos -2 0 INDEX 99 -Index 115 -Pos 0 -1 INDEX 99 -Index 116 -Pos -1 -1 INDEX 99 -Index 117 -Pos -2 -1 INDEX 99 - - - -[Random Gold] - -Index 23 - -Index 8 -Random 40 - -Index 8 XFLIP -Random 40 - -Index 8 YFLIP -Random 40 - -Index 8 YFLIP XFLIP -Random 40 - -Index 9 -Random 40 - -Index 9 XFLIP -Random 40 - -Index 9 YFLIP -Random 40 - -Index 9 YFLIP XFLIP -Random 40 - -Index 23 XFLIP -Random 40 - -Index 23 YFLIP -Random 40 - -Index 23 YFLIP XFLIP -Random 40 - -Index 24 -Random 40 - -Index 24 XFLIP -Random 40 - -Index 24 YFLIP -Random 40 - -Index 24 YFLIP XFLIP -Random 40 - -Index 25 -Random 40 - -Index 25 XFLIP -Random 40 - -Index 25 YFLIP -Random 40 - -Index 25 YFLIP XFLIP -Random 40 - -Index 39 -Random 40 - -Index 39 XFLIP -Random 40 - -Index 39 YFLIP -Random 40 - -Index 39 YFLIP XFLIP -Random 40 - -Index 40 -Random 40 - -Index 40 XFLIP -Random 40 - -Index 40 YFLIP -Random 40 - -Index 40 YFLIP XFLIP -Random 40 - -Index 41 -Random 40 - -Index 41 XFLIP -Random 40 - -Index 41 YFLIP -Random 40 - -Index 41 YFLIP XFLIP -Random 40 - -Index 45 -Random 40 - -Index 45 XFLIP -Random 40 - -Index 45 YFLIP -Random 40 - -Index 45 YFLIP XFLIP -Random 40 - -Index 61 -Random 40 - -Index 61 XFLIP -Random 40 - -Index 61 YFLIP -Random 40 - -Index 61 YFLIP XFLIP -Random 40 - -#random 2x2 -Index 10 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -Index 12 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -#random 3x3 -Index 87 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos 0 2 FULL -Pos 1 2 FULL -Pos 2 2 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 3 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 75 - -#random 3x2 -Index 74 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 100 - -Index 106 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 100 - -NewRun - -#Remove overlaps -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos -2 -2 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos -1 -2 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 0 -2 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 1 -2 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 2 -2 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos -2 -1 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos -1 -1 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 0 -1 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 1 -1 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 2 -1 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos -2 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos -1 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 1 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 2 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos -2 1 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos -1 1 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 0 1 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 1 1 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 2 1 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos -2 2 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos -1 2 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 0 2 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 1 2 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -Index 23 -Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 -Pos 2 2 INDEX 10 OR 12 OR 87 OR 74 OR 106 - -NewRun - -#Fill tiles -Index 11 -Pos -1 0 INDEX 10 -Index 26 -Pos 0 -1 INDEX 10 -Index 27 -Pos -1 -1 INDEX 10 - -Index 13 -Pos -1 0 INDEX 12 -Index 28 -Pos 0 -1 INDEX 12 -Index 29 -Pos -1 -1 INDEX 12 - -Index 88 -Pos -1 0 INDEX 87 -Index 89 -Pos -2 0 INDEX 87 -Index 103 -Pos 0 -1 INDEX 87 -Index 104 -Pos -1 -1 INDEX 87 -Index 105 -Pos -2 -1 INDEX 87 -Index 119 -Pos 0 -2 INDEX 87 -Index 120 -Pos -1 -2 INDEX 87 -Index 121 -Pos -2 -2 INDEX 87 - -Index 75 -Pos -1 0 INDEX 74 -Index 76 -Pos -2 0 INDEX 74 -Index 90 -Pos 0 -1 INDEX 74 -Index 91 -Pos -1 -1 INDEX 74 -Index 92 -Pos -2 -1 INDEX 74 - -Index 107 -Pos -1 0 INDEX 106 -Index 108 -Pos -2 0 INDEX 106 -Index 122 -Pos 0 -1 INDEX 106 -Index 123 -Pos -1 -1 INDEX 106 -Index 124 -Pos -2 -1 INDEX 106 - - - -[Random Bronze] - -Index 144 - -Index 129 -Random 40 - -Index 129 XFLIP -Random 40 - -Index 129 YFLIP -Random 40 - -Index 129 YFLIP XFLIP -Random 40 - -Index 130 -Random 40 - -Index 130 XFLIP -Random 40 - -Index 130 YFLIP -Random 40 - -Index 130 YFLIP XFLIP -Random 40 - -Index 144 XFLIP -Random 40 - -Index 144 YFLIP -Random 40 - -Index 144 YFLIP XFLIP -Random 40 - -Index 145 -Random 40 - -Index 145 XFLIP -Random 40 - -Index 145 YFLIP -Random 40 - -Index 145 YFLIP XFLIP -Random 40 - -Index 146 -Random 40 - -Index 146 XFLIP -Random 40 - -Index 146 YFLIP -Random 40 - -Index 146 YFLIP XFLIP -Random 40 - -Index 160 -Random 40 - -Index 160 XFLIP -Random 40 - -Index 160 YFLIP -Random 40 - -Index 160 YFLIP XFLIP -Random 40 - -Index 161 -Random 40 - -Index 161 XFLIP -Random 40 - -Index 161 YFLIP -Random 40 - -Index 161 YFLIP XFLIP -Random 40 - -Index 162 -Random 40 - -Index 162 XFLIP -Random 40 - -Index 162 YFLIP -Random 40 - -Index 162 YFLIP XFLIP -Random 40 - -Index 166 -Random 40 - -Index 166 XFLIP -Random 40 - -Index 166 YFLIP -Random 40 - -Index 166 YFLIP XFLIP -Random 40 - -Index 182 -Random 40 - -Index 182 XFLIP -Random 40 - -Index 182 YFLIP -Random 40 - -Index 182 YFLIP XFLIP -Random 40 - -#random 2x2 -Index 131 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -Index 133 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 2 0 NOTINDEX -1 -Random 50 - -#random 3x3 -Index 208 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos 0 2 FULL -Pos 1 2 FULL -Pos 2 2 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 3 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 75 - -#random 3x2 -Index 195 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 100 - -Index 227 -Pos 0 0 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos -1 0 NOTINDEX -1 -Pos 0 -1 NOTINDEX -1 -Pos 0 2 NOTINDEX -1 -Pos 3 0 NOTINDEX -1 -Random 100 - -NewRun - -#Remove overlaps -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos -2 -2 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos -1 -2 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 0 -2 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 1 -2 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 2 -2 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos -2 -1 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos -1 -1 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 0 -1 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 1 -1 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 2 -1 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos -2 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos -1 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 1 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 2 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos -2 1 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos -1 1 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 0 1 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 1 1 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 2 1 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos -2 2 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos -1 2 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 0 2 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 1 2 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -Index 144 -Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 -Pos 2 2 INDEX 131 OR 133 OR 208 OR 195 OR 227 - -NewRun - -#Fill tiles -Index 132 -Pos -1 0 INDEX 131 -Index 147 -Pos 0 -1 INDEX 131 -Index 148 -Pos -1 -1 INDEX 131 - -Index 134 -Pos -1 0 INDEX 133 -Index 149 -Pos 0 -1 INDEX 133 -Index 150 -Pos -1 -1 INDEX 133 - -Index 209 -Pos -1 0 INDEX 208 -Index 210 -Pos -2 0 INDEX 208 -Index 224 -Pos 0 -1 INDEX 208 -Index 225 -Pos -1 -1 INDEX 208 -Index 226 -Pos -2 -1 INDEX 208 -Index 240 -Pos 0 -2 INDEX 208 -Index 241 -Pos -1 -2 INDEX 208 -Index 242 -Pos -2 -2 INDEX 208 - -Index 196 -Pos -1 0 INDEX 195 -Index 197 -Pos -2 0 INDEX 195 -Index 211 -Pos 0 -1 INDEX 195 -Index 212 -Pos -1 -1 INDEX 195 -Index 213 -Pos -2 -1 INDEX 195 - -Index 228 -Pos -1 0 INDEX 227 -Index 229 -Pos -2 0 INDEX 227 -Index 243 -Pos 0 -1 INDEX 227 -Index 244 -Pos -1 -1 INDEX 227 -Index 245 -Pos -2 -1 INDEX 227 - - - -[Silver/Gold-Mix] - -#Silver -Index 16 - -Index 1 -Random 80 - -Index 1 XFLIP -Random 80 - -Index 1 YFLIP -Random 80 - -Index 1 YFLIP XFLIP -Random 80 - -Index 2 -Random 80 - -Index 2 XFLIP -Random 80 - -Index 2 YFLIP -Random 80 - -Index 2 YFLIP XFLIP -Random 80 - -Index 16 XFLIP -Random 80 - -Index 16 YFLIP -Random 80 - -Index 16 YFLIP XFLIP -Random 80 - -Index 17 -Random 80 - -Index 17 XFLIP -Random 80 - -Index 17 YFLIP -Random 80 - -Index 17 YFLIP XFLIP -Random 80 - -Index 18 -Random 80 - -Index 18 XFLIP -Random 80 - -Index 18 YFLIP -Random 80 - -Index 18 YFLIP XFLIP -Random 80 - -Index 32 -Random 80 - -Index 32 XFLIP -Random 80 - -Index 32 YFLIP -Random 80 - -Index 32 YFLIP XFLIP -Random 80 - -Index 33 -Random 80 - -Index 33 XFLIP -Random 80 - -Index 33 YFLIP -Random 80 - -Index 33 YFLIP XFLIP -Random 80 - -Index 34 -Random 80 - -Index 34 XFLIP -Random 80 - -Index 34 YFLIP -Random 80 - -Index 34 YFLIP XFLIP -Random 80 - -Index 38 -Random 80 - -Index 38 XFLIP -Random 80 - -Index 38 YFLIP -Random 80 - -Index 38 YFLIP XFLIP -Random 80 - -Index 54 -Random 80 - -Index 54 XFLIP -Random 80 - -Index 54 YFLIP -Random 80 - -Index 54 YFLIP XFLIP -Random 80 - -#Gold - -Index 8 -Random 80 - -Index 8 XFLIP -Random 80 - -Index 8 YFLIP -Random 80 - -Index 8 YFLIP XFLIP -Random 80 - -Index 9 -Random 80 - -Index 9 XFLIP -Random 80 - -Index 9 YFLIP -Random 80 - -Index 9 YFLIP XFLIP -Random 80 - -Index 23 -Random 80 - -Index 23 XFLIP -Random 80 - -Index 23 YFLIP -Random 80 - -Index 23 YFLIP XFLIP -Random 80 - -Index 24 -Random 80 - -Index 24 XFLIP -Random 80 - -Index 24 YFLIP -Random 80 - -Index 24 YFLIP XFLIP -Random 80 - -Index 25 -Random 80 - -Index 25 XFLIP -Random 80 - -Index 25 YFLIP -Random 80 - -Index 25 YFLIP XFLIP -Random 80 - -Index 39 -Random 80 - -Index 39 XFLIP -Random 80 - -Index 39 YFLIP -Random 80 - -Index 39 YFLIP XFLIP -Random 80 - -Index 40 -Random 80 - -Index 40 XFLIP -Random 80 - -Index 40 YFLIP -Random 80 - -Index 40 YFLIP XFLIP -Random 80 - -Index 41 -Random 80 - -Index 41 XFLIP -Random 80 - -Index 41 YFLIP -Random 80 - -Index 41 YFLIP XFLIP -Random 80 - -Index 45 -Random 80 - -Index 45 XFLIP -Random 80 - -Index 45 YFLIP -Random 80 - -Index 45 YFLIP XFLIP -Random 80 - -Index 61 -Random 80 - -Index 61 XFLIP -Random 80 - -Index 61 YFLIP -Random 80 - -Index 61 YFLIP XFLIP -Random 80 - - - -[Copper/Silver-Mix] - -#Copper -Index 144 - -Index 129 -Random 80 - -Index 129 XFLIP -Random 80 - -Index 129 YFLIP -Random 80 - -Index 129 YFLIP XFLIP -Random 80 - -Index 130 -Random 80 - -Index 130 XFLIP -Random 80 - -Index 130 YFLIP -Random 80 - -Index 130 YFLIP XFLIP -Random 80 - -Index 144 XFLIP -Random 80 - -Index 144 YFLIP -Random 80 - -Index 144 YFLIP XFLIP -Random 80 - -Index 145 -Random 80 - -Index 145 XFLIP -Random 80 - -Index 145 YFLIP -Random 80 - -Index 145 YFLIP XFLIP -Random 80 - -Index 146 -Random 80 - -Index 146 XFLIP -Random 80 - -Index 146 YFLIP -Random 80 - -Index 146 YFLIP XFLIP -Random 80 - -Index 160 -Random 80 - -Index 160 XFLIP -Random 80 - -Index 160 YFLIP -Random 80 - -Index 160 YFLIP XFLIP -Random 80 - -Index 161 -Random 80 - -Index 161 XFLIP -Random 80 - -Index 161 YFLIP -Random 80 - -Index 161 YFLIP XFLIP -Random 80 - -Index 162 -Random 80 - -Index 162 XFLIP -Random 80 - -Index 162 YFLIP -Random 80 - -Index 162 YFLIP XFLIP -Random 80 - -Index 166 -Random 80 - -Index 166 XFLIP -Random 80 - -Index 166 YFLIP -Random 80 - -Index 166 YFLIP XFLIP -Random 80 - -Index 182 -Random 80 - -Index 182 XFLIP -Random 80 - -Index 182 YFLIP -Random 80 - -Index 182 YFLIP XFLIP -Random 80 - -#Silver - -Index 1 -Random 80 - -Index 1 XFLIP -Random 80 - -Index 1 YFLIP -Random 80 - -Index 1 YFLIP XFLIP -Random 80 - -Index 2 -Random 80 - -Index 2 XFLIP -Random 80 - -Index 2 YFLIP -Random 80 - -Index 2 YFLIP XFLIP -Random 80 - -Index 16 -Random 80 - -Index 16 XFLIP -Random 80 - -Index 16 YFLIP -Random 80 - -Index 16 YFLIP XFLIP -Random 80 - -Index 17 -Random 80 - -Index 17 XFLIP -Random 80 - -Index 17 YFLIP -Random 80 - -Index 17 YFLIP XFLIP -Random 80 - -Index 18 -Random 80 - -Index 18 XFLIP -Random 80 - -Index 18 YFLIP -Random 80 - -Index 18 YFLIP XFLIP -Random 80 - -Index 32 -Random 80 - -Index 32 XFLIP -Random 80 - -Index 32 YFLIP -Random 80 - -Index 32 YFLIP XFLIP -Random 80 - -Index 33 -Random 80 - -Index 33 XFLIP -Random 80 - -Index 33 YFLIP -Random 80 - -Index 33 YFLIP XFLIP -Random 80 - -Index 34 -Random 80 - -Index 34 XFLIP -Random 80 - -Index 34 YFLIP -Random 80 - -Index 34 YFLIP XFLIP -Random 80 - -Index 38 -Random 80 - -Index 38 XFLIP -Random 80 - -Index 38 YFLIP -Random 80 - -Index 38 YFLIP XFLIP -Random 80 - -Index 54 -Random 80 - -Index 54 XFLIP -Random 80 - -Index 54 YFLIP -Random 80 - -Index 54 YFLIP XFLIP -Random 80 - - - -[Gold/Copper-Mix] - -#Gold -Index 23 - -Index 8 -Random 80 - -Index 8 XFLIP -Random 80 - -Index 8 YFLIP -Random 80 - -Index 8 YFLIP XFLIP -Random 80 - -Index 9 -Random 80 - -Index 9 XFLIP -Random 80 - -Index 9 YFLIP -Random 80 - -Index 9 YFLIP XFLIP -Random 80 - -Index 23 XFLIP -Random 80 - -Index 23 YFLIP -Random 80 - -Index 23 YFLIP XFLIP -Random 80 - -Index 24 -Random 80 - -Index 24 XFLIP -Random 80 - -Index 24 YFLIP -Random 80 - -Index 24 YFLIP XFLIP -Random 80 - -Index 25 -Random 80 - -Index 25 XFLIP -Random 80 - -Index 25 YFLIP -Random 80 - -Index 25 YFLIP XFLIP -Random 80 - -Index 39 -Random 80 - -Index 39 XFLIP -Random 80 - -Index 39 YFLIP -Random 80 - -Index 39 YFLIP XFLIP -Random 80 - -Index 40 -Random 80 - -Index 40 XFLIP -Random 80 - -Index 40 YFLIP -Random 80 - -Index 40 YFLIP XFLIP -Random 80 - -Index 41 -Random 80 - -Index 41 XFLIP -Random 80 - -Index 41 YFLIP -Random 80 - -Index 41 YFLIP XFLIP -Random 80 - -Index 45 -Random 80 - -Index 45 XFLIP -Random 80 - -Index 45 YFLIP -Random 80 - -Index 45 YFLIP XFLIP -Random 80 - -Index 61 -Random 80 - -Index 61 XFLIP -Random 80 - -Index 61 YFLIP -Random 80 - -Index 61 YFLIP XFLIP -Random 80 - -#Copper - -Index 129 -Random 80 - -Index 129 XFLIP -Random 80 - -Index 129 YFLIP -Random 80 - -Index 129 YFLIP XFLIP -Random 80 - -Index 130 -Random 80 - -Index 130 XFLIP -Random 80 - -Index 130 YFLIP -Random 80 - -Index 130 YFLIP XFLIP -Random 80 - -Index 144 -Random 80 - -Index 144 XFLIP -Random 80 - -Index 144 YFLIP -Random 80 - -Index 144 YFLIP XFLIP -Random 80 - -Index 145 -Random 80 - -Index 145 XFLIP -Random 80 - -Index 145 YFLIP -Random 80 - -Index 145 YFLIP XFLIP -Random 80 - -Index 146 -Random 80 - -Index 146 XFLIP -Random 80 - -Index 146 YFLIP -Random 80 - -Index 146 YFLIP XFLIP -Random 80 - -Index 160 -Random 80 - -Index 160 XFLIP -Random 80 - -Index 160 YFLIP -Random 80 - -Index 160 YFLIP XFLIP -Random 80 - -Index 161 -Random 80 - -Index 161 XFLIP -Random 80 - -Index 161 YFLIP -Random 80 - -Index 161 YFLIP XFLIP -Random 80 - -Index 162 -Random 80 - -Index 162 XFLIP -Random 80 - -Index 162 YFLIP -Random 80 - -Index 162 YFLIP XFLIP -Random 80 - -Index 166 -Random 80 - -Index 166 XFLIP -Random 80 - -Index 166 YFLIP -Random 80 - -Index 166 YFLIP XFLIP -Random 80 - -Index 182 -Random 80 - -Index 182 XFLIP -Random 80 - -Index 182 YFLIP -Random 80 - -Index 182 YFLIP XFLIP -Random 80 - - - -[Mix All] - -#Silver -Index 16 - -Index 1 -Random 120 - -Index 1 XFLIP -Random 120 - -Index 1 YFLIP -Random 120 - -Index 1 YFLIP XFLIP -Random 120 - -Index 2 -Random 120 - -Index 2 XFLIP -Random 120 - -Index 2 YFLIP -Random 120 - -Index 2 YFLIP XFLIP -Random 120 - -Index 16 XFLIP -Random 120 - -Index 16 YFLIP -Random 120 - -Index 16 YFLIP XFLIP -Random 120 - -Index 17 -Random 120 - -Index 17 XFLIP -Random 120 - -Index 17 YFLIP -Random 120 - -Index 17 YFLIP XFLIP -Random 120 - -Index 18 -Random 120 - -Index 18 XFLIP -Random 120 - -Index 18 YFLIP -Random 120 - -Index 18 YFLIP XFLIP -Random 120 - -Index 32 -Random 120 - -Index 32 XFLIP -Random 120 - -Index 32 YFLIP -Random 120 - -Index 32 YFLIP XFLIP -Random 120 - -Index 33 -Random 120 - -Index 33 XFLIP -Random 120 - -Index 33 YFLIP -Random 120 - -Index 33 YFLIP XFLIP -Random 120 - -Index 34 -Random 120 - -Index 34 XFLIP -Random 120 - -Index 34 YFLIP -Random 120 - -Index 34 YFLIP XFLIP -Random 120 - -Index 38 -Random 120 - -Index 38 XFLIP -Random 120 - -Index 38 YFLIP -Random 120 - -Index 38 YFLIP XFLIP -Random 120 - -Index 54 -Random 120 - -Index 54 XFLIP -Random 120 - -Index 54 YFLIP -Random 120 - -Index 54 YFLIP XFLIP -Random 120 - -#Gold - -Index 8 -Random 120 - -Index 8 XFLIP -Random 120 - -Index 8 YFLIP -Random 120 - -Index 8 YFLIP XFLIP -Random 120 - -Index 9 -Random 120 - -Index 9 XFLIP -Random 120 - -Index 9 YFLIP -Random 120 - -Index 9 YFLIP XFLIP -Random 120 - -Index 23 -Random 120 - -Index 23 XFLIP -Random 120 - -Index 23 YFLIP -Random 120 - -Index 23 YFLIP XFLIP -Random 120 - -Index 24 -Random 120 - -Index 24 XFLIP -Random 120 - -Index 24 YFLIP -Random 120 - -Index 24 YFLIP XFLIP -Random 120 - -Index 25 -Random 120 - -Index 25 XFLIP -Random 120 - -Index 25 YFLIP -Random 120 - -Index 25 YFLIP XFLIP -Random 120 - -Index 39 -Random 120 - -Index 39 XFLIP -Random 120 - -Index 39 YFLIP -Random 120 - -Index 39 YFLIP XFLIP -Random 120 - -Index 40 -Random 120 - -Index 40 XFLIP -Random 120 - -Index 40 YFLIP -Random 120 - -Index 40 YFLIP XFLIP -Random 120 - -Index 41 -Random 120 - -Index 41 XFLIP -Random 120 - -Index 41 YFLIP -Random 120 - -Index 41 YFLIP XFLIP -Random 120 - -Index 45 -Random 120 - -Index 45 XFLIP -Random 120 - -Index 45 YFLIP -Random 120 - -Index 45 YFLIP XFLIP -Random 120 - -Index 61 -Random 120 - -Index 61 XFLIP -Random 120 - -Index 61 YFLIP -Random 120 - -Index 61 YFLIP XFLIP -Random 120 - -#Copper - -Index 129 -Random 120 - -Index 129 XFLIP -Random 120 - -Index 129 YFLIP -Random 120 - -Index 129 YFLIP XFLIP -Random 120 - -Index 130 -Random 120 - -Index 130 XFLIP -Random 120 - -Index 130 YFLIP -Random 120 - -Index 130 YFLIP XFLIP -Random 120 - -Index 144 -Random 120 - -Index 144 XFLIP -Random 120 - -Index 144 YFLIP -Random 120 - -Index 144 YFLIP XFLIP -Random 120 - -Index 145 -Random 120 - -Index 145 XFLIP -Random 120 - -Index 145 YFLIP -Random 120 - -Index 145 YFLIP XFLIP -Random 120 - -Index 146 -Random 120 - -Index 146 XFLIP -Random 120 - -Index 146 YFLIP -Random 120 - -Index 146 YFLIP XFLIP -Random 120 - -Index 160 -Random 120 - -Index 160 XFLIP -Random 120 - -Index 160 YFLIP -Random 120 - -Index 160 YFLIP XFLIP -Random 120 - -Index 161 -Random 120 - -Index 161 XFLIP -Random 120 - -Index 161 YFLIP -Random 120 - -Index 161 YFLIP XFLIP -Random 120 - -Index 162 -Random 120 - -Index 162 XFLIP -Random 120 - -Index 162 YFLIP -Random 120 - -Index 162 YFLIP XFLIP -Random 120 - -Index 166 -Random 120 - -Index 166 XFLIP -Random 120 - -Index 166 YFLIP -Random 120 - -Index 166 YFLIP XFLIP -Random 120 - -Index 182 -Random 120 - -Index 182 XFLIP -Random 120 - -Index 182 YFLIP -Random 120 - -Index 182 YFLIP XFLIP -Random 120 +[Random Silver] + +Index 16 + +Index 1 +Random 40 + +Index 1 XFLIP +Random 40 + +Index 1 YFLIP +Random 40 + +Index 1 YFLIP XFLIP +Random 40 + +Index 2 +Random 40 + +Index 2 XFLIP +Random 40 + +Index 2 YFLIP +Random 40 + +Index 2 YFLIP XFLIP +Random 40 + +Index 16 XFLIP +Random 40 + +Index 16 YFLIP +Random 40 + +Index 16 YFLIP XFLIP +Random 40 + +Index 17 +Random 40 + +Index 17 XFLIP +Random 40 + +Index 17 YFLIP +Random 40 + +Index 17 YFLIP XFLIP +Random 40 + +Index 18 +Random 40 + +Index 18 XFLIP +Random 40 + +Index 18 YFLIP +Random 40 + +Index 18 YFLIP XFLIP +Random 40 + +Index 32 +Random 40 + +Index 32 XFLIP +Random 40 + +Index 32 YFLIP +Random 40 + +Index 32 YFLIP XFLIP +Random 40 + +Index 33 +Random 40 + +Index 33 XFLIP +Random 40 + +Index 33 YFLIP +Random 40 + +Index 33 YFLIP XFLIP +Random 40 + +Index 34 +Random 40 + +Index 34 XFLIP +Random 40 + +Index 34 YFLIP +Random 40 + +Index 34 YFLIP XFLIP +Random 40 + +Index 38 +Random 40 + +Index 38 XFLIP +Random 40 + +Index 38 YFLIP +Random 40 + +Index 38 YFLIP XFLIP +Random 40 + +Index 54 +Random 40 + +Index 54 XFLIP +Random 40 + +Index 54 YFLIP +Random 40 + +Index 54 YFLIP XFLIP +Random 40 + +#random 2x2 +Index 3 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +Index 5 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +#random 3x3 +Index 80 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos 0 2 FULL +Pos 1 2 FULL +Pos 2 2 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 3 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 75 + +#random 3x2 +Index 67 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 100 + +Index 99 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 100 + +NewRun + +#Remove overlaps +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos -2 -2 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos -1 -2 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 0 -2 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 1 -2 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 2 -2 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos -2 -1 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos -1 -1 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 0 -1 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 1 -1 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 2 -1 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos -2 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos -1 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 1 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 2 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos -2 1 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos -1 1 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 0 1 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 1 1 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 2 1 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos -2 2 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos -1 2 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 0 2 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 1 2 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +Index 16 +Pos 0 0 INDEX 3 OR 5 OR 80 OR 67 OR 99 +Pos 2 2 INDEX 3 OR 5 OR 80 OR 67 OR 99 + +NewRun + +#Fill tiles +Index 4 +Pos -1 0 INDEX 3 +Index 19 +Pos 0 -1 INDEX 3 +Index 20 +Pos -1 -1 INDEX 3 + +Index 6 +Pos -1 0 INDEX 5 +Index 21 +Pos 0 -1 INDEX 5 +Index 22 +Pos -1 -1 INDEX 5 + +Index 81 +Pos -1 0 INDEX 80 +Index 82 +Pos -2 0 INDEX 80 +Index 96 +Pos 0 -1 INDEX 80 +Index 97 +Pos -1 -1 INDEX 80 +Index 98 +Pos -2 -1 INDEX 80 +Index 112 +Pos 0 -2 INDEX 80 +Index 113 +Pos -1 -2 INDEX 80 +Index 114 +Pos -2 -2 INDEX 80 + +Index 68 +Pos -1 0 INDEX 67 +Index 69 +Pos -2 0 INDEX 67 +Index 83 +Pos 0 -1 INDEX 67 +Index 84 +Pos -1 -1 INDEX 67 +Index 85 +Pos -2 -1 INDEX 67 + +Index 100 +Pos -1 0 INDEX 99 +Index 101 +Pos -2 0 INDEX 99 +Index 115 +Pos 0 -1 INDEX 99 +Index 116 +Pos -1 -1 INDEX 99 +Index 117 +Pos -2 -1 INDEX 99 + + + +[Random Gold] + +Index 23 + +Index 8 +Random 40 + +Index 8 XFLIP +Random 40 + +Index 8 YFLIP +Random 40 + +Index 8 YFLIP XFLIP +Random 40 + +Index 9 +Random 40 + +Index 9 XFLIP +Random 40 + +Index 9 YFLIP +Random 40 + +Index 9 YFLIP XFLIP +Random 40 + +Index 23 XFLIP +Random 40 + +Index 23 YFLIP +Random 40 + +Index 23 YFLIP XFLIP +Random 40 + +Index 24 +Random 40 + +Index 24 XFLIP +Random 40 + +Index 24 YFLIP +Random 40 + +Index 24 YFLIP XFLIP +Random 40 + +Index 25 +Random 40 + +Index 25 XFLIP +Random 40 + +Index 25 YFLIP +Random 40 + +Index 25 YFLIP XFLIP +Random 40 + +Index 39 +Random 40 + +Index 39 XFLIP +Random 40 + +Index 39 YFLIP +Random 40 + +Index 39 YFLIP XFLIP +Random 40 + +Index 40 +Random 40 + +Index 40 XFLIP +Random 40 + +Index 40 YFLIP +Random 40 + +Index 40 YFLIP XFLIP +Random 40 + +Index 41 +Random 40 + +Index 41 XFLIP +Random 40 + +Index 41 YFLIP +Random 40 + +Index 41 YFLIP XFLIP +Random 40 + +Index 45 +Random 40 + +Index 45 XFLIP +Random 40 + +Index 45 YFLIP +Random 40 + +Index 45 YFLIP XFLIP +Random 40 + +Index 61 +Random 40 + +Index 61 XFLIP +Random 40 + +Index 61 YFLIP +Random 40 + +Index 61 YFLIP XFLIP +Random 40 + +#random 2x2 +Index 10 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +Index 12 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +#random 3x3 +Index 87 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos 0 2 FULL +Pos 1 2 FULL +Pos 2 2 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 3 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 75 + +#random 3x2 +Index 74 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 100 + +Index 106 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 100 + +NewRun + +#Remove overlaps +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos -2 -2 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos -1 -2 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 0 -2 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 1 -2 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 2 -2 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos -2 -1 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos -1 -1 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 0 -1 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 1 -1 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 2 -1 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos -2 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos -1 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 1 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 2 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos -2 1 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos -1 1 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 0 1 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 1 1 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 2 1 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos -2 2 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos -1 2 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 0 2 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 1 2 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +Index 23 +Pos 0 0 INDEX 10 OR 12 OR 87 OR 74 OR 106 +Pos 2 2 INDEX 10 OR 12 OR 87 OR 74 OR 106 + +NewRun + +#Fill tiles +Index 11 +Pos -1 0 INDEX 10 +Index 26 +Pos 0 -1 INDEX 10 +Index 27 +Pos -1 -1 INDEX 10 + +Index 13 +Pos -1 0 INDEX 12 +Index 28 +Pos 0 -1 INDEX 12 +Index 29 +Pos -1 -1 INDEX 12 + +Index 88 +Pos -1 0 INDEX 87 +Index 89 +Pos -2 0 INDEX 87 +Index 103 +Pos 0 -1 INDEX 87 +Index 104 +Pos -1 -1 INDEX 87 +Index 105 +Pos -2 -1 INDEX 87 +Index 119 +Pos 0 -2 INDEX 87 +Index 120 +Pos -1 -2 INDEX 87 +Index 121 +Pos -2 -2 INDEX 87 + +Index 75 +Pos -1 0 INDEX 74 +Index 76 +Pos -2 0 INDEX 74 +Index 90 +Pos 0 -1 INDEX 74 +Index 91 +Pos -1 -1 INDEX 74 +Index 92 +Pos -2 -1 INDEX 74 + +Index 107 +Pos -1 0 INDEX 106 +Index 108 +Pos -2 0 INDEX 106 +Index 122 +Pos 0 -1 INDEX 106 +Index 123 +Pos -1 -1 INDEX 106 +Index 124 +Pos -2 -1 INDEX 106 + + + +[Random Bronze] + +Index 144 + +Index 129 +Random 40 + +Index 129 XFLIP +Random 40 + +Index 129 YFLIP +Random 40 + +Index 129 YFLIP XFLIP +Random 40 + +Index 130 +Random 40 + +Index 130 XFLIP +Random 40 + +Index 130 YFLIP +Random 40 + +Index 130 YFLIP XFLIP +Random 40 + +Index 144 XFLIP +Random 40 + +Index 144 YFLIP +Random 40 + +Index 144 YFLIP XFLIP +Random 40 + +Index 145 +Random 40 + +Index 145 XFLIP +Random 40 + +Index 145 YFLIP +Random 40 + +Index 145 YFLIP XFLIP +Random 40 + +Index 146 +Random 40 + +Index 146 XFLIP +Random 40 + +Index 146 YFLIP +Random 40 + +Index 146 YFLIP XFLIP +Random 40 + +Index 160 +Random 40 + +Index 160 XFLIP +Random 40 + +Index 160 YFLIP +Random 40 + +Index 160 YFLIP XFLIP +Random 40 + +Index 161 +Random 40 + +Index 161 XFLIP +Random 40 + +Index 161 YFLIP +Random 40 + +Index 161 YFLIP XFLIP +Random 40 + +Index 162 +Random 40 + +Index 162 XFLIP +Random 40 + +Index 162 YFLIP +Random 40 + +Index 162 YFLIP XFLIP +Random 40 + +Index 166 +Random 40 + +Index 166 XFLIP +Random 40 + +Index 166 YFLIP +Random 40 + +Index 166 YFLIP XFLIP +Random 40 + +Index 182 +Random 40 + +Index 182 XFLIP +Random 40 + +Index 182 YFLIP +Random 40 + +Index 182 YFLIP XFLIP +Random 40 + +#random 2x2 +Index 131 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +Index 133 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 2 0 NOTINDEX -1 +Random 50 + +#random 3x3 +Index 208 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos 0 2 FULL +Pos 1 2 FULL +Pos 2 2 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 3 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 75 + +#random 3x2 +Index 195 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 100 + +Index 227 +Pos 0 0 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos -1 0 NOTINDEX -1 +Pos 0 -1 NOTINDEX -1 +Pos 0 2 NOTINDEX -1 +Pos 3 0 NOTINDEX -1 +Random 100 + +NewRun + +#Remove overlaps +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos -2 -2 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos -1 -2 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 0 -2 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 1 -2 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 2 -2 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos -2 -1 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos -1 -1 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 0 -1 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 1 -1 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 2 -1 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos -2 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos -1 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 1 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 2 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos -2 1 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos -1 1 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 0 1 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 1 1 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 2 1 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos -2 2 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos -1 2 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 0 2 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 1 2 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +Index 144 +Pos 0 0 INDEX 131 OR 133 OR 208 OR 195 OR 227 +Pos 2 2 INDEX 131 OR 133 OR 208 OR 195 OR 227 + +NewRun + +#Fill tiles +Index 132 +Pos -1 0 INDEX 131 +Index 147 +Pos 0 -1 INDEX 131 +Index 148 +Pos -1 -1 INDEX 131 + +Index 134 +Pos -1 0 INDEX 133 +Index 149 +Pos 0 -1 INDEX 133 +Index 150 +Pos -1 -1 INDEX 133 + +Index 209 +Pos -1 0 INDEX 208 +Index 210 +Pos -2 0 INDEX 208 +Index 224 +Pos 0 -1 INDEX 208 +Index 225 +Pos -1 -1 INDEX 208 +Index 226 +Pos -2 -1 INDEX 208 +Index 240 +Pos 0 -2 INDEX 208 +Index 241 +Pos -1 -2 INDEX 208 +Index 242 +Pos -2 -2 INDEX 208 + +Index 196 +Pos -1 0 INDEX 195 +Index 197 +Pos -2 0 INDEX 195 +Index 211 +Pos 0 -1 INDEX 195 +Index 212 +Pos -1 -1 INDEX 195 +Index 213 +Pos -2 -1 INDEX 195 + +Index 228 +Pos -1 0 INDEX 227 +Index 229 +Pos -2 0 INDEX 227 +Index 243 +Pos 0 -1 INDEX 227 +Index 244 +Pos -1 -1 INDEX 227 +Index 245 +Pos -2 -1 INDEX 227 + + + +[Silver/Gold-Mix] + +#Silver +Index 16 + +Index 1 +Random 80 + +Index 1 XFLIP +Random 80 + +Index 1 YFLIP +Random 80 + +Index 1 YFLIP XFLIP +Random 80 + +Index 2 +Random 80 + +Index 2 XFLIP +Random 80 + +Index 2 YFLIP +Random 80 + +Index 2 YFLIP XFLIP +Random 80 + +Index 16 XFLIP +Random 80 + +Index 16 YFLIP +Random 80 + +Index 16 YFLIP XFLIP +Random 80 + +Index 17 +Random 80 + +Index 17 XFLIP +Random 80 + +Index 17 YFLIP +Random 80 + +Index 17 YFLIP XFLIP +Random 80 + +Index 18 +Random 80 + +Index 18 XFLIP +Random 80 + +Index 18 YFLIP +Random 80 + +Index 18 YFLIP XFLIP +Random 80 + +Index 32 +Random 80 + +Index 32 XFLIP +Random 80 + +Index 32 YFLIP +Random 80 + +Index 32 YFLIP XFLIP +Random 80 + +Index 33 +Random 80 + +Index 33 XFLIP +Random 80 + +Index 33 YFLIP +Random 80 + +Index 33 YFLIP XFLIP +Random 80 + +Index 34 +Random 80 + +Index 34 XFLIP +Random 80 + +Index 34 YFLIP +Random 80 + +Index 34 YFLIP XFLIP +Random 80 + +Index 38 +Random 80 + +Index 38 XFLIP +Random 80 + +Index 38 YFLIP +Random 80 + +Index 38 YFLIP XFLIP +Random 80 + +Index 54 +Random 80 + +Index 54 XFLIP +Random 80 + +Index 54 YFLIP +Random 80 + +Index 54 YFLIP XFLIP +Random 80 + +#Gold + +Index 8 +Random 80 + +Index 8 XFLIP +Random 80 + +Index 8 YFLIP +Random 80 + +Index 8 YFLIP XFLIP +Random 80 + +Index 9 +Random 80 + +Index 9 XFLIP +Random 80 + +Index 9 YFLIP +Random 80 + +Index 9 YFLIP XFLIP +Random 80 + +Index 23 +Random 80 + +Index 23 XFLIP +Random 80 + +Index 23 YFLIP +Random 80 + +Index 23 YFLIP XFLIP +Random 80 + +Index 24 +Random 80 + +Index 24 XFLIP +Random 80 + +Index 24 YFLIP +Random 80 + +Index 24 YFLIP XFLIP +Random 80 + +Index 25 +Random 80 + +Index 25 XFLIP +Random 80 + +Index 25 YFLIP +Random 80 + +Index 25 YFLIP XFLIP +Random 80 + +Index 39 +Random 80 + +Index 39 XFLIP +Random 80 + +Index 39 YFLIP +Random 80 + +Index 39 YFLIP XFLIP +Random 80 + +Index 40 +Random 80 + +Index 40 XFLIP +Random 80 + +Index 40 YFLIP +Random 80 + +Index 40 YFLIP XFLIP +Random 80 + +Index 41 +Random 80 + +Index 41 XFLIP +Random 80 + +Index 41 YFLIP +Random 80 + +Index 41 YFLIP XFLIP +Random 80 + +Index 45 +Random 80 + +Index 45 XFLIP +Random 80 + +Index 45 YFLIP +Random 80 + +Index 45 YFLIP XFLIP +Random 80 + +Index 61 +Random 80 + +Index 61 XFLIP +Random 80 + +Index 61 YFLIP +Random 80 + +Index 61 YFLIP XFLIP +Random 80 + + + +[Copper/Silver-Mix] + +#Copper +Index 144 + +Index 129 +Random 80 + +Index 129 XFLIP +Random 80 + +Index 129 YFLIP +Random 80 + +Index 129 YFLIP XFLIP +Random 80 + +Index 130 +Random 80 + +Index 130 XFLIP +Random 80 + +Index 130 YFLIP +Random 80 + +Index 130 YFLIP XFLIP +Random 80 + +Index 144 XFLIP +Random 80 + +Index 144 YFLIP +Random 80 + +Index 144 YFLIP XFLIP +Random 80 + +Index 145 +Random 80 + +Index 145 XFLIP +Random 80 + +Index 145 YFLIP +Random 80 + +Index 145 YFLIP XFLIP +Random 80 + +Index 146 +Random 80 + +Index 146 XFLIP +Random 80 + +Index 146 YFLIP +Random 80 + +Index 146 YFLIP XFLIP +Random 80 + +Index 160 +Random 80 + +Index 160 XFLIP +Random 80 + +Index 160 YFLIP +Random 80 + +Index 160 YFLIP XFLIP +Random 80 + +Index 161 +Random 80 + +Index 161 XFLIP +Random 80 + +Index 161 YFLIP +Random 80 + +Index 161 YFLIP XFLIP +Random 80 + +Index 162 +Random 80 + +Index 162 XFLIP +Random 80 + +Index 162 YFLIP +Random 80 + +Index 162 YFLIP XFLIP +Random 80 + +Index 166 +Random 80 + +Index 166 XFLIP +Random 80 + +Index 166 YFLIP +Random 80 + +Index 166 YFLIP XFLIP +Random 80 + +Index 182 +Random 80 + +Index 182 XFLIP +Random 80 + +Index 182 YFLIP +Random 80 + +Index 182 YFLIP XFLIP +Random 80 + +#Silver + +Index 1 +Random 80 + +Index 1 XFLIP +Random 80 + +Index 1 YFLIP +Random 80 + +Index 1 YFLIP XFLIP +Random 80 + +Index 2 +Random 80 + +Index 2 XFLIP +Random 80 + +Index 2 YFLIP +Random 80 + +Index 2 YFLIP XFLIP +Random 80 + +Index 16 +Random 80 + +Index 16 XFLIP +Random 80 + +Index 16 YFLIP +Random 80 + +Index 16 YFLIP XFLIP +Random 80 + +Index 17 +Random 80 + +Index 17 XFLIP +Random 80 + +Index 17 YFLIP +Random 80 + +Index 17 YFLIP XFLIP +Random 80 + +Index 18 +Random 80 + +Index 18 XFLIP +Random 80 + +Index 18 YFLIP +Random 80 + +Index 18 YFLIP XFLIP +Random 80 + +Index 32 +Random 80 + +Index 32 XFLIP +Random 80 + +Index 32 YFLIP +Random 80 + +Index 32 YFLIP XFLIP +Random 80 + +Index 33 +Random 80 + +Index 33 XFLIP +Random 80 + +Index 33 YFLIP +Random 80 + +Index 33 YFLIP XFLIP +Random 80 + +Index 34 +Random 80 + +Index 34 XFLIP +Random 80 + +Index 34 YFLIP +Random 80 + +Index 34 YFLIP XFLIP +Random 80 + +Index 38 +Random 80 + +Index 38 XFLIP +Random 80 + +Index 38 YFLIP +Random 80 + +Index 38 YFLIP XFLIP +Random 80 + +Index 54 +Random 80 + +Index 54 XFLIP +Random 80 + +Index 54 YFLIP +Random 80 + +Index 54 YFLIP XFLIP +Random 80 + + + +[Gold/Copper-Mix] + +#Gold +Index 23 + +Index 8 +Random 80 + +Index 8 XFLIP +Random 80 + +Index 8 YFLIP +Random 80 + +Index 8 YFLIP XFLIP +Random 80 + +Index 9 +Random 80 + +Index 9 XFLIP +Random 80 + +Index 9 YFLIP +Random 80 + +Index 9 YFLIP XFLIP +Random 80 + +Index 23 XFLIP +Random 80 + +Index 23 YFLIP +Random 80 + +Index 23 YFLIP XFLIP +Random 80 + +Index 24 +Random 80 + +Index 24 XFLIP +Random 80 + +Index 24 YFLIP +Random 80 + +Index 24 YFLIP XFLIP +Random 80 + +Index 25 +Random 80 + +Index 25 XFLIP +Random 80 + +Index 25 YFLIP +Random 80 + +Index 25 YFLIP XFLIP +Random 80 + +Index 39 +Random 80 + +Index 39 XFLIP +Random 80 + +Index 39 YFLIP +Random 80 + +Index 39 YFLIP XFLIP +Random 80 + +Index 40 +Random 80 + +Index 40 XFLIP +Random 80 + +Index 40 YFLIP +Random 80 + +Index 40 YFLIP XFLIP +Random 80 + +Index 41 +Random 80 + +Index 41 XFLIP +Random 80 + +Index 41 YFLIP +Random 80 + +Index 41 YFLIP XFLIP +Random 80 + +Index 45 +Random 80 + +Index 45 XFLIP +Random 80 + +Index 45 YFLIP +Random 80 + +Index 45 YFLIP XFLIP +Random 80 + +Index 61 +Random 80 + +Index 61 XFLIP +Random 80 + +Index 61 YFLIP +Random 80 + +Index 61 YFLIP XFLIP +Random 80 + +#Copper + +Index 129 +Random 80 + +Index 129 XFLIP +Random 80 + +Index 129 YFLIP +Random 80 + +Index 129 YFLIP XFLIP +Random 80 + +Index 130 +Random 80 + +Index 130 XFLIP +Random 80 + +Index 130 YFLIP +Random 80 + +Index 130 YFLIP XFLIP +Random 80 + +Index 144 +Random 80 + +Index 144 XFLIP +Random 80 + +Index 144 YFLIP +Random 80 + +Index 144 YFLIP XFLIP +Random 80 + +Index 145 +Random 80 + +Index 145 XFLIP +Random 80 + +Index 145 YFLIP +Random 80 + +Index 145 YFLIP XFLIP +Random 80 + +Index 146 +Random 80 + +Index 146 XFLIP +Random 80 + +Index 146 YFLIP +Random 80 + +Index 146 YFLIP XFLIP +Random 80 + +Index 160 +Random 80 + +Index 160 XFLIP +Random 80 + +Index 160 YFLIP +Random 80 + +Index 160 YFLIP XFLIP +Random 80 + +Index 161 +Random 80 + +Index 161 XFLIP +Random 80 + +Index 161 YFLIP +Random 80 + +Index 161 YFLIP XFLIP +Random 80 + +Index 162 +Random 80 + +Index 162 XFLIP +Random 80 + +Index 162 YFLIP +Random 80 + +Index 162 YFLIP XFLIP +Random 80 + +Index 166 +Random 80 + +Index 166 XFLIP +Random 80 + +Index 166 YFLIP +Random 80 + +Index 166 YFLIP XFLIP +Random 80 + +Index 182 +Random 80 + +Index 182 XFLIP +Random 80 + +Index 182 YFLIP +Random 80 + +Index 182 YFLIP XFLIP +Random 80 + + + +[Mix All] + +#Silver +Index 16 + +Index 1 +Random 120 + +Index 1 XFLIP +Random 120 + +Index 1 YFLIP +Random 120 + +Index 1 YFLIP XFLIP +Random 120 + +Index 2 +Random 120 + +Index 2 XFLIP +Random 120 + +Index 2 YFLIP +Random 120 + +Index 2 YFLIP XFLIP +Random 120 + +Index 16 XFLIP +Random 120 + +Index 16 YFLIP +Random 120 + +Index 16 YFLIP XFLIP +Random 120 + +Index 17 +Random 120 + +Index 17 XFLIP +Random 120 + +Index 17 YFLIP +Random 120 + +Index 17 YFLIP XFLIP +Random 120 + +Index 18 +Random 120 + +Index 18 XFLIP +Random 120 + +Index 18 YFLIP +Random 120 + +Index 18 YFLIP XFLIP +Random 120 + +Index 32 +Random 120 + +Index 32 XFLIP +Random 120 + +Index 32 YFLIP +Random 120 + +Index 32 YFLIP XFLIP +Random 120 + +Index 33 +Random 120 + +Index 33 XFLIP +Random 120 + +Index 33 YFLIP +Random 120 + +Index 33 YFLIP XFLIP +Random 120 + +Index 34 +Random 120 + +Index 34 XFLIP +Random 120 + +Index 34 YFLIP +Random 120 + +Index 34 YFLIP XFLIP +Random 120 + +Index 38 +Random 120 + +Index 38 XFLIP +Random 120 + +Index 38 YFLIP +Random 120 + +Index 38 YFLIP XFLIP +Random 120 + +Index 54 +Random 120 + +Index 54 XFLIP +Random 120 + +Index 54 YFLIP +Random 120 + +Index 54 YFLIP XFLIP +Random 120 + +#Gold + +Index 8 +Random 120 + +Index 8 XFLIP +Random 120 + +Index 8 YFLIP +Random 120 + +Index 8 YFLIP XFLIP +Random 120 + +Index 9 +Random 120 + +Index 9 XFLIP +Random 120 + +Index 9 YFLIP +Random 120 + +Index 9 YFLIP XFLIP +Random 120 + +Index 23 +Random 120 + +Index 23 XFLIP +Random 120 + +Index 23 YFLIP +Random 120 + +Index 23 YFLIP XFLIP +Random 120 + +Index 24 +Random 120 + +Index 24 XFLIP +Random 120 + +Index 24 YFLIP +Random 120 + +Index 24 YFLIP XFLIP +Random 120 + +Index 25 +Random 120 + +Index 25 XFLIP +Random 120 + +Index 25 YFLIP +Random 120 + +Index 25 YFLIP XFLIP +Random 120 + +Index 39 +Random 120 + +Index 39 XFLIP +Random 120 + +Index 39 YFLIP +Random 120 + +Index 39 YFLIP XFLIP +Random 120 + +Index 40 +Random 120 + +Index 40 XFLIP +Random 120 + +Index 40 YFLIP +Random 120 + +Index 40 YFLIP XFLIP +Random 120 + +Index 41 +Random 120 + +Index 41 XFLIP +Random 120 + +Index 41 YFLIP +Random 120 + +Index 41 YFLIP XFLIP +Random 120 + +Index 45 +Random 120 + +Index 45 XFLIP +Random 120 + +Index 45 YFLIP +Random 120 + +Index 45 YFLIP XFLIP +Random 120 + +Index 61 +Random 120 + +Index 61 XFLIP +Random 120 + +Index 61 YFLIP +Random 120 + +Index 61 YFLIP XFLIP +Random 120 + +#Copper + +Index 129 +Random 120 + +Index 129 XFLIP +Random 120 + +Index 129 YFLIP +Random 120 + +Index 129 YFLIP XFLIP +Random 120 + +Index 130 +Random 120 + +Index 130 XFLIP +Random 120 + +Index 130 YFLIP +Random 120 + +Index 130 YFLIP XFLIP +Random 120 + +Index 144 +Random 120 + +Index 144 XFLIP +Random 120 + +Index 144 YFLIP +Random 120 + +Index 144 YFLIP XFLIP +Random 120 + +Index 145 +Random 120 + +Index 145 XFLIP +Random 120 + +Index 145 YFLIP +Random 120 + +Index 145 YFLIP XFLIP +Random 120 + +Index 146 +Random 120 + +Index 146 XFLIP +Random 120 + +Index 146 YFLIP +Random 120 + +Index 146 YFLIP XFLIP +Random 120 + +Index 160 +Random 120 + +Index 160 XFLIP +Random 120 + +Index 160 YFLIP +Random 120 + +Index 160 YFLIP XFLIP +Random 120 + +Index 161 +Random 120 + +Index 161 XFLIP +Random 120 + +Index 161 YFLIP +Random 120 + +Index 161 YFLIP XFLIP +Random 120 + +Index 162 +Random 120 + +Index 162 XFLIP +Random 120 + +Index 162 YFLIP +Random 120 + +Index 162 YFLIP XFLIP +Random 120 + +Index 166 +Random 120 + +Index 166 XFLIP +Random 120 + +Index 166 YFLIP +Random 120 + +Index 166 YFLIP XFLIP +Random 120 + +Index 182 +Random 120 + +Index 182 XFLIP +Random 120 + +Index 182 YFLIP +Random 120 + +Index 182 YFLIP XFLIP +Random 120 diff --git a/data/editor/grass_main.rules b/data/editor/automap/grass_main.rules similarity index 100% rename from data/editor/grass_main.rules rename to data/editor/automap/grass_main.rules diff --git a/data/editor/grass_main_0.7.rules b/data/editor/automap/grass_main_0.7.rules similarity index 100% rename from data/editor/grass_main_0.7.rules rename to data/editor/automap/grass_main_0.7.rules diff --git a/data/editor/jungle_main.rules b/data/editor/automap/jungle_main.rules similarity index 100% rename from data/editor/jungle_main.rules rename to data/editor/automap/jungle_main.rules diff --git a/data/editor/jungle_midground.rules b/data/editor/automap/jungle_midground.rules similarity index 93% rename from data/editor/jungle_midground.rules rename to data/editor/automap/jungle_midground.rules index 30303553c..4dadbafc4 100644 --- a/data/editor/jungle_midground.rules +++ b/data/editor/automap/jungle_midground.rules @@ -1,1468 +1,1468 @@ -[Raw] - -#clear -Index 0 -Pos 0 0 INDEX 211 OR 214 OR 217 OR 218 OR 219 OR 224 - -NewRun - -Index 35 - - -[Small] - -#clear -Index 0 -Pos 0 0 INDEX 211 OR 214 OR 217 OR 218 OR 219 OR 224 - -NewRun - -Index 35 - -#top -Index 200 -Pos 0 -1 EMPTY - -#right -Index 200 ROTATE -Pos 1 0 EMPTY - -#bottom -Index 200 XFLIP YFLIP -Pos 0 1 EMPTY - -#left -Index 200 ROTATE XFLIP YFLIP -Pos -1 0 EMPTY - -#corner top-right -Index 199 -Pos 0 -1 EMPTY -Pos 1 0 EMPTY - -#corner top-left -Index 199 ROTATE XFLIP YFLIP -Pos 0 -1 EMPTY -Pos -1 0 EMPTY - -#corner bottom-left -Index 199 XFLIP YFLIP -Pos 0 1 EMPTY -Pos -1 0 EMPTY - -#corner bottom-right -Index 199 ROTATE -Pos 0 1 EMPTY -Pos 1 0 EMPTY - -#inside corner top-right -Index 215 XFLIP YFLIP -Pos -1 1 EMPTY -Pos -1 0 FULL -Pos 0 1 FULL - -#inside corner top-left -Index 215 ROTATE -Pos 1 1 EMPTY -Pos 1 0 FULL -Pos 0 1 FULL - -#inside corner bottom-left -Index 215 -Pos 1 -1 EMPTY -Pos 1 0 FULL -Pos 0 -1 FULL - -#inside corner bottom-right -Index 215 ROTATE XFLIP YFLIP -Pos -1 -1 EMPTY -Pos -1 0 FULL -Pos 0 -1 FULL - -NewRun - -#corner top-left 3x3 -Index 160 -Pos 0 0 INDEX 199 -Pos 1 -1 EMPTY -Pos 2 -1 EMPTY -Pos -1 1 EMPTY -Pos -1 2 EMPTY -Pos 1 0 FULL -Pos 2 0 FULL -Pos 3 0 FULL -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos 3 1 FULL -Pos 0 2 FULL -Pos 1 2 FULL -Pos 2 2 FULL -Pos 0 3 FULL -Pos 1 3 FULL - -NewRun - -#corner top-right 3x3 -Index 160 ROTATE -Pos 0 0 INDEX 199 -Pos -1 -1 EMPTY -Pos -2 -1 EMPTY -Pos 1 1 EMPTY -Pos 1 2 EMPTY -Pos -1 0 FULL -Pos -2 0 FULL -Pos -3 0 FULL -Pos 0 1 FULL -Pos -1 1 FULL -Pos -2 1 FULL -Pos -3 1 FULL -Pos 0 2 FULL -Pos -1 2 FULL -Pos -2 2 FULL -Pos 0 3 FULL -Pos -1 3 FULL -Pos -4 0 NOTINDEX 160 -Pos -3 0 NOTINDEX 160 - -NewRun - -#corner bottom-left 3x3 -Index 160 ROTATE XFLIP YFLIP -Pos 0 0 INDEX 199 -Pos 1 1 EMPTY -Pos 2 1 EMPTY -Pos -1 -1 EMPTY -Pos -1 -2 EMPTY -Pos 1 0 FULL -Pos 2 0 FULL -Pos 3 0 FULL -Pos 0 -1 FULL -Pos 1 -1 FULL -Pos 2 -1 FULL -Pos 3 -1 FULL -Pos 0 -2 FULL -Pos 1 -2 FULL -Pos 2 -2 FULL -Pos 0 -3 FULL -Pos 1 -3 FULL -Pos 0 -4 NOTINDEX 160 -Pos 0 -3 NOTINDEX 160 - -NewRun - -#corner bottom-right 3x3 -Index 160 XFLIP YFLIP -Pos 0 0 INDEX 199 -Pos -1 1 EMPTY -Pos -2 1 EMPTY -Pos 1 -1 EMPTY -Pos 1 -2 EMPTY -Pos -1 0 FULL -Pos -2 0 FULL -Pos -3 0 FULL -Pos 0 -1 FULL -Pos -1 -1 FULL -Pos -2 -1 FULL -Pos -3 -1 FULL -Pos 0 -2 FULL -Pos -1 -2 FULL -Pos -2 -2 FULL -Pos 0 -3 FULL -Pos -1 -3 FULL -Pos -4 0 NOTINDEX 160 -Pos -3 0 NOTINDEX 160 -Pos 0 -4 NOTINDEX 160 -Pos 0 -3 NOTINDEX 160 - -NewRun - -#fill top-left -Index 161 -Pos -1 0 INDEX 160 -Pos 1 0 FULL -Pos 0 -1 EMPTY - -Index 162 -Pos -2 0 INDEX 160 -Pos -1 0 FULL -Pos 0 -1 EMPTY - -Index 176 -Pos 0 -1 INDEX 160 -Pos 0 1 FULL -Pos -1 0 EMPTY - -Index 177 -Pos -1 -1 INDEX 160 -Pos 0 -1 FULL -Pos -1 0 FULL - -Index 192 -Pos 0 -2 INDEX 160 -Pos 0 -1 FULL -Pos -1 0 EMPTY - -#fill top-right -Index 176 ROTATE -Pos 1 0 INDEX 160 -Pos -1 0 FULL -Pos 0 -1 EMPTY - -Index 192 ROTATE -Pos 2 0 INDEX 160 -Pos 1 0 FULL -Pos 0 -1 EMPTY - -Index 161 ROTATE -Pos 0 -1 INDEX 160 -Pos 0 1 FULL -Pos 1 0 EMPTY - -Index 177 ROTATE -Pos 1 -1 INDEX 160 -Pos 0 -1 FULL -Pos 1 0 FULL - -Index 162 ROTATE -Pos 0 -2 INDEX 160 -Pos 0 -1 FULL -Pos 1 0 EMPTY - -#fill bottom-left -Index 176 ROTATE XFLIP YFLIP -Pos -1 0 INDEX 160 -Pos 1 0 FULL -Pos 0 1 EMPTY - -Index 192 ROTATE XFLIP YFLIP -Pos -2 0 INDEX 160 -Pos -1 0 FULL -Pos 0 1 EMPTY - -Index 161 ROTATE XFLIP YFLIP -Pos 0 1 INDEX 160 -Pos 0 -1 FULL -Pos -1 0 EMPTY - -Index 177 ROTATE XFLIP YFLIP -Pos -1 1 INDEX 160 -Pos 0 1 FULL -Pos -1 0 FULL - -Index 162 ROTATE XFLIP YFLIP -Pos 0 2 INDEX 160 -Pos 0 1 FULL -Pos -1 0 EMPTY - -#fill bottom-right -Index 161 XFLIP YFLIP -Pos 1 0 INDEX 160 -Pos -1 0 FULL -Pos 0 1 EMPTY - -Index 162 XFLIP YFLIP -Pos 2 0 INDEX 160 -Pos 1 0 FULL -Pos 0 1 EMPTY - -Index 176 XFLIP YFLIP -Pos 0 1 INDEX 160 -Pos 0 -1 FULL -Pos 1 0 EMPTY - -Index 177 XFLIP YFLIP -Pos 1 1 INDEX 160 -Pos 0 1 FULL -Pos 1 0 FULL - -Index 192 XFLIP YFLIP -Pos 0 2 INDEX 160 -Pos 0 1 FULL -Pos 1 0 EMPTY - -NewRun - -#corner top-right 4x3 -Index 165 -Pos 0 0 INDEX 199 -Pos 1 1 INDEX 199 -Pos -1 -1 EMPTY -Pos -2 -1 EMPTY -Pos -1 0 FULL -Pos -2 0 FULL -Pos -3 0 FULL -Pos -3 1 FULL -Pos -2 1 FULL -Pos -1 1 FULL -Pos 0 1 FULL -Pos -1 2 FULL -Pos 0 2 FULL -Pos 1 2 FULL -Pos 2 2 EMPTY -Pos 0 3 FULL -Pos 1 3 FULL -Pos -2 0 INDEX 200 -Pos 1 2 INDEX 200 - -NewRun - -#corner top-left 4x3 -Index 165 XFLIP -Pos 0 0 INDEX 199 -Pos -1 1 INDEX 199 -Pos 1 -1 EMPTY -Pos 2 -1 EMPTY -Pos 1 0 FULL -Pos 2 0 FULL -Pos 3 0 FULL -Pos 3 1 FULL -Pos 2 1 FULL -Pos 1 1 FULL -Pos 0 1 FULL -Pos 1 2 FULL -Pos 0 2 FULL -Pos -1 2 FULL -Pos -2 2 EMPTY -Pos 0 3 FULL -Pos -1 3 FULL -Pos 2 0 INDEX 200 -Pos -1 2 INDEX 200 -Pos 4 0 NOTINDEX 165 -Pos 3 0 NOTINDEX 165 - -NewRun - -#corner bottom-left 4x3 -Index 165 XFLIP YFLIP -Pos 0 0 INDEX 199 -Pos -1 -1 INDEX 199 -Pos 1 1 EMPTY -Pos 2 1 EMPTY -Pos 1 0 FULL -Pos 2 0 FULL -Pos 3 0 FULL -Pos 3 -1 FULL -Pos 2 -1 FULL -Pos 1 -1 FULL -Pos 0 -1 FULL -Pos 1 -2 FULL -Pos 0 -2 FULL -Pos -1 -2 FULL -Pos -2 -2 EMPTY -Pos 0 -3 FULL -Pos -1 -3 FULL -Pos 2 0 INDEX 200 -Pos -1 -2 INDEX 200 -Pos 0 -4 NOTINDEX 165 - -NewRun - -#corner bottom-right 4x3 -Index 165 YFLIP -Pos 0 0 INDEX 199 -Pos 1 -1 INDEX 199 -Pos -1 1 EMPTY -Pos -2 1 EMPTY -Pos -1 0 FULL -Pos -2 0 FULL -Pos -3 0 FULL -Pos -3 -1 FULL -Pos -2 -1 FULL -Pos -1 -1 FULL -Pos 0 -1 FULL -Pos -1 -2 FULL -Pos 0 -2 FULL -Pos 1 -2 FULL -Pos 2 -2 EMPTY -Pos 0 -3 FULL -Pos 1 -3 FULL -Pos -2 0 INDEX 200 -Pos 1 -2 INDEX 200 -Pos -4 0 NOTINDEX 165 -Pos -3 0 NOTINDEX 165 -Pos 0 -4 NOTINDEX 165 - -NewRun - -#fill top-right 4x3 -Index 164 -Pos 1 0 INDEX 165 -Pos 0 -1 EMPTY -Pos -1 0 FULL - -Index 163 -Pos 2 0 INDEX 165 -Pos 0 -1 EMPTY -Pos -1 0 FULL - -Index 181 -Pos 0 -1 INDEX 165 -Pos 1 -1 EMPTY -Pos 1 0 FULL - -Index 182 -Pos -1 -1 INDEX 165 -Pos -1 0 FULL -Pos 1 0 EMPTY - -Index 198 -Pos -1 -2 INDEX 165 -Pos 0 -1 FULL -Pos 1 0 EMPTY - -#fill top-left 4x3 -Index 164 XFLIP -Pos -1 0 INDEX 165 -Pos 0 -1 EMPTY -Pos 1 0 FULL - -Index 163 XFLIP -Pos -2 0 INDEX 165 -Pos 0 -1 EMPTY -Pos 1 0 FULL - -Index 181 XFLIP -Pos 0 -1 INDEX 165 -Pos -1 -1 EMPTY -Pos -1 0 FULL - -Index 182 XFLIP -Pos 1 -1 INDEX 165 -Pos 1 0 FULL -Pos -1 0 EMPTY - -Index 198 XFLIP -Pos 1 -2 INDEX 165 -Pos 0 -1 FULL -Pos -1 0 EMPTY - -#fill bottom-left 4x3 -Index 164 XFLIP YFLIP -Pos -1 0 INDEX 165 -Pos 0 1 EMPTY -Pos 1 0 FULL - -Index 163 XFLIP YFLIP -Pos -2 0 INDEX 165 -Pos 0 1 EMPTY -Pos 1 0 FULL - -Index 181 XFLIP YFLIP -Pos 0 1 INDEX 165 -Pos -1 1 EMPTY -Pos -1 0 FULL - -Index 182 XFLIP YFLIP -Pos 1 1 INDEX 165 -Pos 1 0 FULL -Pos -1 0 EMPTY - -Index 198 XFLIP YFLIP -Pos 1 2 INDEX 165 -Pos 0 1 FULL -Pos -1 0 EMPTY - -#fill bottom-right 4x3 -Index 164 YFLIP -Pos 1 0 INDEX 165 -Pos 0 1 EMPTY -Pos -1 0 FULL - -Index 163 YFLIP -Pos 2 0 INDEX 165 -Pos 0 1 EMPTY -Pos -1 0 FULL - -Index 181 YFLIP -Pos 0 1 INDEX 165 -Pos 1 1 EMPTY -Pos 1 0 FULL - -Index 182 YFLIP -Pos -1 1 INDEX 165 -Pos -1 0 FULL -Pos 1 0 EMPTY - -Index 198 YFLIP -Pos -1 2 INDEX 165 -Pos 0 1 FULL -Pos 1 0 EMPTY - -NewRun - -#corner top-left 3x4 -Index 165 ROTATE XFLIP YFLIP -Pos 0 0 INDEX 199 -Pos 1 -1 INDEX 199 -Pos 2 -2 EMPTY -Pos 2 -1 FULL -Pos 3 -1 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 3 0 FULL -Pos -1 1 EMPTY -Pos 0 1 FULL -Pos 1 1 FULL -Pos 2 1 FULL -Pos -1 2 EMPTY -Pos 0 2 FULL -Pos 1 2 FULL -Pos 0 3 FULL -Pos 1 3 FULL -Pos 0 2 INDEX 200 -Pos 2 -1 INDEX 200 - -NewRun - -#corner top-right 3x4 -Index 165 ROTATE XFLIP -Pos 0 0 INDEX 199 -Pos -1 -1 INDEX 199 -Pos -2 -2 EMPTY -Pos -2 -1 FULL -Pos -3 -1 FULL -Pos -1 0 FULL -Pos -2 0 FULL -Pos -3 0 FULL -Pos 1 1 EMPTY -Pos 0 1 FULL -Pos -1 1 FULL -Pos -2 1 FULL -Pos 1 2 EMPTY -Pos 0 2 FULL -Pos -1 2 FULL -Pos 0 3 FULL -Pos -1 3 FULL -Pos 0 2 INDEX 200 -Pos -2 -1 INDEX 200 - -NewRun - -#corner bottom-right 3x4 -Index 165 ROTATE -Pos 0 0 INDEX 199 -Pos -1 1 INDEX 199 -Pos -2 2 EMPTY -Pos -2 1 FULL -Pos -3 1 FULL -Pos -1 0 FULL -Pos -2 0 FULL -Pos -3 0 FULL -Pos 1 -1 EMPTY -Pos 0 -1 FULL -Pos -1 -1 FULL -Pos -2 -1 FULL -Pos 1 -2 EMPTY -Pos 0 -2 FULL -Pos -1 -2 FULL -Pos 0 -3 FULL -Pos -1 -3 FULL -Pos 0 -2 INDEX 200 -Pos -2 1 INDEX 200 -Pos 0 -4 NOTINDEX 165 -Pos 0 -3 NOTINDEX 165 - -NewRun - -#corner bottom-left 3x4 -Index 165 ROTATE YFLIP -Pos 0 0 INDEX 199 -Pos 1 1 INDEX 199 -Pos 2 2 EMPTY -Pos 2 1 FULL -Pos 3 1 FULL -Pos 1 0 FULL -Pos 2 0 FULL -Pos 3 0 FULL -Pos -1 -1 EMPTY -Pos 0 -1 FULL -Pos 1 -1 FULL -Pos 2 -1 FULL -Pos -1 -2 EMPTY -Pos 0 -2 FULL -Pos 1 -2 FULL -Pos 0 -3 FULL -Pos 1 -3 FULL -Pos 0 -2 INDEX 200 -Pos 2 1 INDEX 200 -Pos 0 -4 NOTINDEX 165 -Pos 0 -3 NOTINDEX 165 - -NewRun - -#fill top-left 3x4 -Index 164 ROTATE XFLIP YFLIP -Pos 0 -1 INDEX 165 -Pos -1 0 EMPTY -Pos 0 1 FULL - -Index 163 ROTATE XFLIP YFLIP -Pos 0 -2 INDEX 165 -Pos -1 0 EMPTY -Pos 0 1 FULL - -Index 181 ROTATE XFLIP YFLIP -Pos -1 0 INDEX 165 -Pos -1 -1 EMPTY -Pos 0 -1 FULL - -Index 182 ROTATE XFLIP YFLIP -Pos -1 1 INDEX 165 -Pos 0 1 FULL -Pos 0 -1 EMPTY - -Index 198 ROTATE XFLIP YFLIP -Pos -2 1 INDEX 165 -Pos -1 0 FULL -Pos 0 -1 EMPTY - -#fill top-right 3x4 -Index 164 ROTATE XFLIP -Pos 0 -1 INDEX 165 -Pos 1 0 EMPTY -Pos 0 1 FULL - -Index 163 ROTATE XFLIP -Pos 0 -2 INDEX 165 -Pos 1 0 EMPTY -Pos 0 1 FULL - -Index 181 ROTATE XFLIP -Pos 1 0 INDEX 165 -Pos 1 -1 EMPTY -Pos 0 -1 FULL - -Index 182 ROTATE XFLIP -Pos 1 1 INDEX 165 -Pos 0 1 FULL -Pos 0 -1 EMPTY - -Index 198 ROTATE XFLIP -Pos 2 1 INDEX 165 -Pos 1 0 FULL -Pos 0 -1 EMPTY - -#fill bottom-right 3x4 -Index 164 ROTATE -Pos 0 1 INDEX 165 -Pos 1 0 EMPTY -Pos 0 -1 FULL - -Index 163 ROTATE -Pos 0 2 INDEX 165 -Pos 1 0 EMPTY -Pos 0 -1 FULL - -Index 181 ROTATE -Pos 1 0 INDEX 165 -Pos 1 1 EMPTY -Pos 0 1 FULL - -Index 182 ROTATE -Pos 1 -1 INDEX 165 -Pos 0 -1 FULL -Pos 0 1 EMPTY - -Index 198 ROTATE -Pos 2 -1 INDEX 165 -Pos 1 0 FULL -Pos 0 1 EMPTY - -#fill bottom-left 3x4 -Index 164 ROTATE YFLIP -Pos 0 1 INDEX 165 -Pos -1 0 EMPTY -Pos 0 -1 FULL - -Index 163 ROTATE YFLIP -Pos 0 2 INDEX 165 -Pos -1 0 EMPTY -Pos 0 -1 FULL - -Index 181 ROTATE YFLIP -Pos -1 0 INDEX 165 -Pos -1 1 EMPTY -Pos 0 1 FULL - -Index 182 ROTATE YFLIP -Pos -1 -1 INDEX 165 -Pos 0 -1 FULL -Pos 0 1 EMPTY - -Index 198 ROTATE YFLIP -Pos -2 -1 INDEX 165 -Pos -1 0 FULL -Pos 0 1 EMPTY - -NewRun - -#dead end top -Index 227 XFLIP YFLIP -Pos -1 0 INDEX 215 -Pos 0 0 INDEX 200 -Pos 1 0 INDEX 215 -Pos -1 1 INDEX 200 -Pos 0 1 EMPTY -Pos 1 1 INDEX 200 - -#dead end right -Index 227 ROTATE XFLIP YFLIP -Pos 0 -1 INDEX 215 -Pos 0 0 INDEX 200 -Pos 0 1 INDEX 215 -Pos -1 -1 INDEX 200 -Pos -1 0 EMPTY -Pos -1 1 INDEX 200 - -#dead end bottom -Index 227 -Pos -1 0 INDEX 215 -Pos 0 0 INDEX 200 -Pos 1 0 INDEX 215 -Pos -1 -1 INDEX 200 -Pos 0 -1 EMPTY -Pos 1 -1 INDEX 200 - -#dead end left -Index 227 ROTATE -Pos 0 -1 INDEX 215 -Pos 0 0 INDEX 200 -Pos 0 1 INDEX 215 -Pos 1 -1 INDEX 200 -Pos 1 0 EMPTY -Pos 1 1 INDEX 200 - -NewRun - -#fill dead end top -Index 226 XFLIP YFLIP -Pos -1 0 INDEX 227 -Pos 0 1 INDEX 200 -Pos 1 0 FULL -Pos 0 -1 FULL - -Index 228 XFLIP YFLIP -Pos 1 0 INDEX 227 -Pos 0 1 INDEX 200 -Pos -1 0 FULL -Pos 0 1 FULL - -Index 210 XFLIP YFLIP -Pos -1 -1 INDEX 227 -Pos -1 0 EMPTY - -Index 211 XFLIP YFLIP -Pos 0 0 EMPTY -Pos 0 -1 INDEX 227 -Pos 0 1 EMPTY - -Index 212 XFLIP YFLIP -Pos 1 -1 INDEX 227 -Pos 1 0 EMPTY - -#fill dead end right -Index 226 ROTATE XFLIP YFLIP -Pos 0 -1 INDEX 227 -Pos -1 0 INDEX 200 -Pos 0 1 FULL -Pos 1 0 FULL - -Index 228 ROTATE XFLIP YFLIP -Pos 0 1 INDEX 227 -Pos -1 0 INDEX 200 -Pos 0 -1 FULL -Pos 1 0 FULL - -Index 210 ROTATE XFLIP YFLIP -Pos 1 -1 INDEX 227 -Pos 0 -1 EMPTY - -Index 211 ROTATE XFLIP YFLIP -Pos 0 0 EMPTY -Pos 1 0 INDEX 227 -Pos -1 0 EMPTY - -Index 212 ROTATE XFLIP YFLIP -Pos 1 1 INDEX 227 -Pos 0 1 EMPTY - -#fill dead end bottom -Index 226 -Pos 1 0 INDEX 227 -Pos 0 -1 INDEX 200 -Pos -1 0 FULL -Pos 0 1 FULL - -Index 228 -Pos -1 0 INDEX 227 -Pos 0 -1 INDEX 200 -Pos 1 0 FULL -Pos 0 1 FULL - -Index 210 -Pos 1 1 INDEX 227 -Pos 1 0 EMPTY - -Index 211 -Pos 0 0 EMPTY -Pos 0 1 INDEX 227 -Pos 0 -1 EMPTY - -Index 212 -Pos -1 1 INDEX 227 -Pos -1 0 EMPTY - -#fill dead end left -Index 226 ROTATE -Pos 0 1 INDEX 227 -Pos 1 0 INDEX 200 -Pos 0 -1 FULL -Pos -1 0 FULL - -Index 228 ROTATE -Pos 0 -1 INDEX 227 -Pos 1 0 INDEX 200 -Pos 0 1 FULL -Pos -1 0 FULL - -Index 210 ROTATE -Pos -1 1 INDEX 227 -Pos 0 1 EMPTY - -Index 211 ROTATE -Pos 0 0 EMPTY -Pos -1 0 INDEX 227 -Pos 1 0 EMPTY - -Index 212 ROTATE -Pos -1 -1 INDEX 227 -Pos 0 -1 EMPTY - -NewRun - -#s-curve bottom-left 1 -Index 209 XFLIP YFLIP -Pos 0 0 INDEX 215 -Pos 0 -1 INDEX 200 -Pos 1 0 INDEX 199 -Pos 1 -1 EMPTY - -#s-curve bottom-left 1 alt -Index 229 -Pos 0 0 INDEX 215 -Pos 0 -1 INDEX 200 -Pos 1 0 INDEX 199 -Pos 1 -1 EMPTY -Random 2 - -NewRun - -#s-curve bottom-left 2 -Index 209 ROTATE YFLIP -Pos 0 0 INDEX 215 -Pos 1 0 INDEX 200 -Pos 0 -1 INDEX 199 -Pos 1 -1 EMPTY - -#s-curve bottom-left 2 alt -Index 229 ROTATE XFLIP -Pos 0 0 INDEX 215 -Pos 1 0 INDEX 200 -Pos 0 -1 INDEX 199 -Pos 1 -1 EMPTY -Random 2 - -NewRun - -#s-curve top-left 1 -Index 209 XFLIP -Pos 0 0 INDEX 215 -Pos 0 1 INDEX 200 -Pos 1 0 INDEX 199 -Pos 1 1 EMPTY -Pos 0 2 NOTINDEX 209 OR 229 - -#s-curve top-left 1 alt -Index 229 YFLIP -Pos 0 0 INDEX 215 -Pos 0 1 INDEX 200 -Pos 1 0 INDEX 199 -Pos 1 1 EMPTY -Pos 0 2 NOTINDEX 209 OR 229 -Random 2 - -NewRun - -#s-curve top-left 2 -Index 209 ROTATE XFLIP YFLIP -Pos 0 0 INDEX 215 -Pos 1 0 INDEX 200 -Pos 0 1 INDEX 199 -Pos 1 1 EMPTY - -#s-curve top-left 2 alt -Index 229 ROTATE -Pos 0 0 INDEX 215 -Pos 1 0 INDEX 200 -Pos 0 1 INDEX 199 -Pos 1 1 EMPTY -Random 2 - -NewRun - -#s-curve top-right 1 -Index 209 -Pos 0 0 INDEX 215 -Pos 0 1 INDEX 200 -Pos -1 0 INDEX 199 -Pos -1 1 EMPTY - -#s-curve top-right 1 alt -Index 229 XFLIP YFLIP -Pos 0 0 INDEX 215 -Pos 0 1 INDEX 200 -Pos -1 0 INDEX 199 -Pos -1 1 EMPTY -Random 2 - -NewRun - -#s-curve top-right 2 -Index 209 ROTATE XFLIP -Pos 0 0 INDEX 215 -Pos -1 0 INDEX 200 -Pos 0 1 INDEX 199 -Pos -1 1 EMPTY -Pos -2 0 NOTINDEX 209 OR 229 - -#s-curve top-right 2 alt -Index 229 ROTATE YFLIP -Pos 0 0 INDEX 215 -Pos -1 0 INDEX 200 -Pos 0 1 INDEX 199 -Pos -1 1 EMPTY -Pos -2 0 NOTINDEX 209 OR 229 -Random 2 - -NewRun - -#s-curve bottom-right 1 -Index 209 YFLIP -Pos 0 0 INDEX 215 -Pos 0 -1 INDEX 200 -Pos -1 0 INDEX 199 -Pos -1 -1 EMPTY -Pos 0 -2 NOTINDEX 209 OR 229 - -#s-curve bottom-right 1 alt -Index 229 XFLIP -Pos 0 0 INDEX 215 -Pos 0 -1 INDEX 200 -Pos -1 0 INDEX 199 -Pos -1 -1 EMPTY -Pos 0 -2 NOTINDEX 209 OR 229 -Random 2 - -NewRun - -#s-curve bottom-right 2 -Index 209 ROTATE -Pos 0 0 INDEX 215 -Pos -1 0 INDEX 200 -Pos 0 -1 INDEX 199 -Pos -1 -1 EMPTY -Pos -2 0 NOTINDEX 209 OR 229 - -#s-curve bottom-right 2 alt -Index 229 ROTATE XFLIP YFLIP -Pos 0 0 INDEX 215 -Pos -1 0 INDEX 200 -Pos 0 -1 INDEX 199 -Pos -1 -1 EMPTY -Pos -2 0 NOTINDEX 209 OR 229 -Random 2 - -NewRun - -#fill s-curve bottom-left 1 -Index 225 XFLIP YFLIP -Pos 0 0 INDEX 200 -Pos 0 1 INDEX 209 -Pos 1 0 EMPTY - -Index 208 XFLIP YFLIP -Pos 0 0 INDEX 199 -Pos -1 0 INDEX 209 -Pos 0 -1 EMPTY - -Index 224 XFLIP YFLIP -Pos 0 0 EMPTY -Pos -1 1 INDEX 209 -Pos -1 0 INDEX 200 - -#fill s-curve bottom-left 1 alt -Index 213 -Pos 0 0 INDEX 200 -Pos 0 1 INDEX 229 -Pos 1 0 EMPTY - -Index 230 -Pos 0 0 INDEX 199 -Pos -1 0 INDEX 229 -Pos 0 -1 EMPTY - -Index 214 -Pos 0 0 EMPTY -Pos -1 1 INDEX 229 -Pos -1 0 INDEX 200 - -#fill s-curve bottom-left 2 -Index 225 ROTATE YFLIP -Pos 0 0 INDEX 200 -Pos -1 0 INDEX 209 -Pos 0 -1 EMPTY - -Index 208 ROTATE YFLIP -Pos 0 0 INDEX 199 -Pos 0 1 INDEX 209 -Pos 1 0 EMPTY - -Index 224 ROTATE YFLIP -Pos 0 0 EMPTY -Pos -1 1 INDEX 209 -Pos 0 1 INDEX 200 - -#fill s-curve bottom-left 2 alt -Index 213 ROTATE XFLIP -Pos 0 0 INDEX 200 -Pos -1 0 INDEX 229 -Pos 0 -1 EMPTY - -Index 230 ROTATE XFLIP -Pos 0 0 INDEX 199 -Pos 0 1 INDEX 229 -Pos 1 0 EMPTY - -Index 214 ROTATE XFLIP -Pos 0 0 EMPTY -Pos -1 1 INDEX 229 -Pos 0 1 INDEX 200 - -#fill s-curve top-left 1 -Index 225 XFLIP -Pos 0 0 INDEX 200 -Pos 0 -1 INDEX 209 -Pos 1 0 EMPTY - -Index 208 XFLIP -Pos 0 0 INDEX 199 -Pos -1 0 INDEX 209 -Pos 0 1 EMPTY - -Index 224 XFLIP -Pos 0 0 EMPTY -Pos -1 -1 INDEX 209 -Pos -1 0 INDEX 200 - -#fill s-curve top-left 1 alt -Index 213 YFLIP -Pos 0 0 INDEX 200 -Pos 0 -1 INDEX 229 -Pos 1 0 EMPTY - -Index 230 YFLIP -Pos 0 0 INDEX 199 -Pos -1 0 INDEX 229 -Pos 0 1 EMPTY - -Index 214 YFLIP -Pos 0 0 EMPTY -Pos -1 -1 INDEX 229 -Pos -1 0 INDEX 200 - -#fill s-curve top-left 2 -Index 225 ROTATE XFLIP YFLIP -Pos 0 0 INDEX 200 -Pos -1 0 INDEX 209 -Pos 0 1 EMPTY - -Index 208 ROTATE XFLIP YFLIP -Pos 0 0 INDEX 199 -Pos 0 -1 INDEX 209 -Pos 1 0 EMPTY - -Index 224 ROTATE XFLIP YFLIP -Pos 0 0 EMPTY -Pos -1 -1 INDEX 209 -Pos 0 -1 INDEX 200 - -#fill s-curve top-left 2 alt -Index 213 ROTATE -Pos 0 0 INDEX 200 -Pos -1 0 INDEX 229 -Pos 0 1 EMPTY - -Index 230 ROTATE -Pos 0 0 INDEX 199 -Pos 0 -1 INDEX 229 -Pos 1 0 EMPTY - -Index 214 ROTATE -Pos 0 0 EMPTY -Pos -1 -1 INDEX 229 -Pos 0 -1 INDEX 200 - -#fill s-curve top-right 1 -Index 225 -Pos 0 0 INDEX 200 -Pos 0 -1 INDEX 209 -Pos -1 0 EMPTY - -Index 208 -Pos 0 0 INDEX 199 -Pos 1 0 INDEX 209 -Pos 0 1 EMPTY - -Index 224 -Pos 0 0 EMPTY -Pos 1 -1 INDEX 209 -Pos 1 0 INDEX 200 - -#fill s-curve top-right 1 alt -Index 213 XFLIP YFLIP -Pos 0 0 INDEX 200 -Pos 0 -1 INDEX 229 -Pos -1 0 EMPTY - -Index 230 XFLIP YFLIP -Pos 0 0 INDEX 199 -Pos 1 0 INDEX 229 -Pos 0 1 EMPTY - -Index 214 XFLIP YFLIP -Pos 0 0 EMPTY -Pos 1 -1 INDEX 229 -Pos 1 0 INDEX 200 - -#fill s-curve top-right 2 -Index 225 ROTATE XFLIP -Pos 0 0 INDEX 200 -Pos 1 0 INDEX 209 -Pos 0 1 EMPTY - -Index 208 ROTATE XFLIP -Pos 0 0 INDEX 199 -Pos 0 -1 INDEX 209 -Pos -1 0 EMPTY - -Index 224 ROTATE XFLIP -Pos 0 0 EMPTY -Pos 1 -1 INDEX 209 -Pos 0 -1 INDEX 200 - -#fill s-curve top-right 2 alt -Index 213 ROTATE YFLIP -Pos 0 0 INDEX 200 -Pos 1 0 INDEX 229 -Pos 0 1 EMPTY - -Index 230 ROTATE YFLIP -Pos 0 0 INDEX 199 -Pos 0 -1 INDEX 229 -Pos -1 0 EMPTY - -Index 214 ROTATE YFLIP -Pos 0 0 EMPTY -Pos 1 -1 INDEX 229 -Pos 0 -1 INDEX 200 - -#fill s-curve bottom-right 1 -Index 225 YFLIP -Pos 0 0 INDEX 200 -Pos 0 1 INDEX 209 -Pos -1 0 EMPTY - -Index 208 YFLIP -Pos 0 0 INDEX 199 -Pos 1 0 INDEX 209 -Pos 0 -1 EMPTY - -Index 224 YFLIP -Pos 0 0 EMPTY -Pos 1 1 INDEX 209 -Pos 1 0 INDEX 200 - -#fill s-curve bottom-right 1 alt -Index 213 XFLIP -Pos 0 0 INDEX 200 -Pos 0 1 INDEX 229 -Pos -1 0 EMPTY - -Index 230 XFLIP -Pos 0 0 INDEX 199 -Pos 1 0 INDEX 229 -Pos 0 -1 EMPTY - -Index 214 XFLIP -Pos 0 0 EMPTY -Pos 1 1 INDEX 229 -Pos 1 0 INDEX 200 - -#fill s-curve bottom-right 2 -Index 225 ROTATE -Pos 0 0 INDEX 200 -Pos 1 0 INDEX 209 -Pos 0 -1 EMPTY - -Index 208 ROTATE -Pos 0 0 INDEX 199 -Pos 0 1 INDEX 209 -Pos -1 0 EMPTY - -Index 224 ROTATE -Pos 0 0 EMPTY -Pos 1 1 INDEX 209 -Pos 0 1 INDEX 200 - -#fill s-curve bottom-right 2 alt -Index 213 ROTATE XFLIP YFLIP -Pos 0 0 INDEX 200 -Pos 1 0 INDEX 229 -Pos 0 -1 EMPTY - -Index 230 ROTATE XFLIP YFLIP -Pos 0 0 INDEX 199 -Pos 0 1 INDEX 229 -Pos -1 0 EMPTY - -Index 214 ROTATE XFLIP YFLIP -Pos 0 0 EMPTY -Pos 1 1 INDEX 229 -Pos 0 1 INDEX 200 - - -NewRun - -#top long -Index 233 -Pos 0 0 INDEX 200 -Pos 0 -1 EMPTY -Pos 0 -2 EMPTY -Pos -1 0 FULL -Pos 1 0 INDEX 200 -Pos 2 0 INDEX 200 -Random 5 - -#right long -Index 233 ROTATE -Pos 0 0 INDEX 200 -Pos 1 0 EMPTY -Pos 2 0 EMPTY -Pos 0 -1 FULL -Pos 0 1 INDEX 200 -Pos 0 2 INDEX 200 -Random 5 - -#bottom long -Index 233 XFLIP YFLIP -Pos 0 0 INDEX 200 -Pos 0 1 EMPTY -Pos 0 2 EMPTY -Pos 1 0 FULL -Pos -1 0 INDEX 200 -Pos -2 0 INDEX 200 -Random 5 - -#left long -Index 233 ROTATE XFLIP YFLIP -Pos 0 0 INDEX 200 -Pos -1 0 EMPTY -Pos -2 0 EMPTY -Pos 0 1 FULL -Pos 0 -1 INDEX 200 -Pos 0 -2 INDEX 200 -Random 5 - - -NewRun - -#top overlap -Index 200 -Pos 0 0 233 -Pos 0 -1 EMPTY -Pos 0 0 233 -Pos -1 0 INDEX 233 - -#top overlap 2 -Index 200 -Pos 0 0 233 -Pos 0 -1 EMPTY -Pos 0 0 INDEX 233 -Pos -2 0 INDEX 233 - -#right overlap -Index 200 ROTATE -Pos 0 0 233 -Pos 1 0 EMPTY -Pos 0 0 INDEX 233 -Pos 0 -1 INDEX 233 - -#right overlap 2 -Index 200 ROTATE -Pos 0 0 233 -Pos 1 0 EMPTY -Pos 0 0 INDEX 233 -Pos 0 -2 INDEX 233 - -#bottom overlap -Index 200 XFLIP YFLIP -Pos 0 0 233 -Pos 0 1 EMPTY -Pos 0 0 INDEX 233 -Pos 1 0 INDEX 233 - -#bottom overlap 2 -Index 200 XFLIP YFLIP -Pos 0 0 233 -Pos 0 1 EMPTY -Pos 0 0 INDEX 233 -Pos 2 0 INDEX 233 - -#left overlap -Index 200 ROTATE XFLIP YFLIP -Pos 0 0 233 -Pos -1 0 EMPTY -Pos 0 0 INDEX 233 -Pos 0 1 INDEX 233 - -#left overlap 2 -Index 200 ROTATE XFLIP YFLIP -Pos 0 0 233 -Pos -1 0 EMPTY -Pos 0 0 INDEX 233 -Pos 0 2 INDEX 233 - -NewRun - -#fill top long 1 -Index 234 -Pos -1 0 INDEX 233 -Pos 0 -1 EMPTY - -#fill top long 2 -Index 235 -Pos -2 0 INDEX 233 -Pos -1 0 INDEX 200 -Pos 0 -1 EMPTY -Pos 1 0 FULL - -#fill right long 1 -Index 234 ROTATE -Pos 0 -1 INDEX 233 -Pos 1 0 EMPTY - -#fill right long 2 -Index 235 ROTATE -Pos 0 -2 INDEX 233 -Pos 0 -1 INDEX 200 -Pos 1 0 EMPTY -Pos 0 1 FULL - -#fill bottom long 1 -Index 234 XFLIP YFLIP -Pos 1 0 INDEX 233 -Pos 0 1 EMPTY - -#fill bottom long 2 -Index 235 XFLIP YFLIP -Pos 2 0 INDEX 233 -Pos 1 0 INDEX 200 -Pos 0 1 EMPTY -Pos -1 0 FULL - -#fill left long 1 -Index 234 ROTATE XFLIP YFLIP -Pos 0 1 INDEX 233 -Pos -1 0 EMPTY - -#fill left long 2 -Index 235 ROTATE XFLIP YFLIP -Pos 0 2 INDEX 233 -Pos 0 1 INDEX 200 -Pos -1 0 EMPTY -Pos 0 -1 FULL - -NewRun - -#fill top long 3 -Index 217 -Pos 0 0 EMPTY -Pos 0 1 INDEX 233 -Pos 0 2 FULL - -#fill top long 4 -Index 218 -Pos 0 0 EMPTY -Pos 0 1 INDEX 234 -Pos 0 2 FULL - -#fill top long 5 -Index 219 -Pos 0 0 EMPTY -Pos 0 1 INDEX 235 -Pos 0 2 FULL - -#fill right long 3 -Index 217 ROTATE -Pos 0 0 EMPTY -Pos -1 0 INDEX 233 -Pos -2 0 FULL - -#fill right long 4 -Index 218 ROTATE -Pos 0 0 EMPTY -Pos -1 0 INDEX 234 -Pos -2 0 FULL - -#fill right long 5 -Index 219 ROTATE -Pos 0 0 EMPTY -Pos -1 0 INDEX 235 -Pos -2 0 FULL - -#fill bottom long 3 -Index 217 XFLIP YFLIP -Pos 0 0 EMPTY -Pos 0 -1 INDEX 233 -Pos 0 -2 FULL - -#fill bottom long 4 -Index 218 XFLIP YFLIP -Pos 0 0 EMPTY -Pos 0 -1 INDEX 234 -Pos 0 -2 FULL - -#fill bottom long 5 -Index 219 XFLIP YFLIP -Pos 0 0 EMPTY -Pos 0 -1 INDEX 235 -Pos 0 -2 FULL - -#fill left long 3 -Index 217 ROTATE XFLIP YFLIP -Pos 0 0 EMPTY -Pos 1 0 INDEX 233 -Pos 2 0 FULL - -#fill left long 4 -Index 218 ROTATE XFLIP YFLIP -Pos 0 0 EMPTY -Pos 1 0 INDEX 234 -Pos 2 0 FULL - -#fill left long 5 -Index 219 ROTATE XFLIP YFLIP -Pos 0 0 EMPTY -Pos 1 0 INDEX 235 -Pos 2 0 FULL +[Raw] + +#clear +Index 0 +Pos 0 0 INDEX 211 OR 214 OR 217 OR 218 OR 219 OR 224 + +NewRun + +Index 35 + + +[Small] + +#clear +Index 0 +Pos 0 0 INDEX 211 OR 214 OR 217 OR 218 OR 219 OR 224 + +NewRun + +Index 35 + +#top +Index 200 +Pos 0 -1 EMPTY + +#right +Index 200 ROTATE +Pos 1 0 EMPTY + +#bottom +Index 200 XFLIP YFLIP +Pos 0 1 EMPTY + +#left +Index 200 ROTATE XFLIP YFLIP +Pos -1 0 EMPTY + +#corner top-right +Index 199 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +#corner top-left +Index 199 ROTATE XFLIP YFLIP +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-left +Index 199 XFLIP YFLIP +Pos 0 1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-right +Index 199 ROTATE +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +#inside corner top-right +Index 215 XFLIP YFLIP +Pos -1 1 EMPTY +Pos -1 0 FULL +Pos 0 1 FULL + +#inside corner top-left +Index 215 ROTATE +Pos 1 1 EMPTY +Pos 1 0 FULL +Pos 0 1 FULL + +#inside corner bottom-left +Index 215 +Pos 1 -1 EMPTY +Pos 1 0 FULL +Pos 0 -1 FULL + +#inside corner bottom-right +Index 215 ROTATE XFLIP YFLIP +Pos -1 -1 EMPTY +Pos -1 0 FULL +Pos 0 -1 FULL + +NewRun + +#corner top-left 3x3 +Index 160 +Pos 0 0 INDEX 199 +Pos 1 -1 EMPTY +Pos 2 -1 EMPTY +Pos -1 1 EMPTY +Pos -1 2 EMPTY +Pos 1 0 FULL +Pos 2 0 FULL +Pos 3 0 FULL +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos 3 1 FULL +Pos 0 2 FULL +Pos 1 2 FULL +Pos 2 2 FULL +Pos 0 3 FULL +Pos 1 3 FULL + +NewRun + +#corner top-right 3x3 +Index 160 ROTATE +Pos 0 0 INDEX 199 +Pos -1 -1 EMPTY +Pos -2 -1 EMPTY +Pos 1 1 EMPTY +Pos 1 2 EMPTY +Pos -1 0 FULL +Pos -2 0 FULL +Pos -3 0 FULL +Pos 0 1 FULL +Pos -1 1 FULL +Pos -2 1 FULL +Pos -3 1 FULL +Pos 0 2 FULL +Pos -1 2 FULL +Pos -2 2 FULL +Pos 0 3 FULL +Pos -1 3 FULL +Pos -4 0 NOTINDEX 160 +Pos -3 0 NOTINDEX 160 + +NewRun + +#corner bottom-left 3x3 +Index 160 ROTATE XFLIP YFLIP +Pos 0 0 INDEX 199 +Pos 1 1 EMPTY +Pos 2 1 EMPTY +Pos -1 -1 EMPTY +Pos -1 -2 EMPTY +Pos 1 0 FULL +Pos 2 0 FULL +Pos 3 0 FULL +Pos 0 -1 FULL +Pos 1 -1 FULL +Pos 2 -1 FULL +Pos 3 -1 FULL +Pos 0 -2 FULL +Pos 1 -2 FULL +Pos 2 -2 FULL +Pos 0 -3 FULL +Pos 1 -3 FULL +Pos 0 -4 NOTINDEX 160 +Pos 0 -3 NOTINDEX 160 + +NewRun + +#corner bottom-right 3x3 +Index 160 XFLIP YFLIP +Pos 0 0 INDEX 199 +Pos -1 1 EMPTY +Pos -2 1 EMPTY +Pos 1 -1 EMPTY +Pos 1 -2 EMPTY +Pos -1 0 FULL +Pos -2 0 FULL +Pos -3 0 FULL +Pos 0 -1 FULL +Pos -1 -1 FULL +Pos -2 -1 FULL +Pos -3 -1 FULL +Pos 0 -2 FULL +Pos -1 -2 FULL +Pos -2 -2 FULL +Pos 0 -3 FULL +Pos -1 -3 FULL +Pos -4 0 NOTINDEX 160 +Pos -3 0 NOTINDEX 160 +Pos 0 -4 NOTINDEX 160 +Pos 0 -3 NOTINDEX 160 + +NewRun + +#fill top-left +Index 161 +Pos -1 0 INDEX 160 +Pos 1 0 FULL +Pos 0 -1 EMPTY + +Index 162 +Pos -2 0 INDEX 160 +Pos -1 0 FULL +Pos 0 -1 EMPTY + +Index 176 +Pos 0 -1 INDEX 160 +Pos 0 1 FULL +Pos -1 0 EMPTY + +Index 177 +Pos -1 -1 INDEX 160 +Pos 0 -1 FULL +Pos -1 0 FULL + +Index 192 +Pos 0 -2 INDEX 160 +Pos 0 -1 FULL +Pos -1 0 EMPTY + +#fill top-right +Index 176 ROTATE +Pos 1 0 INDEX 160 +Pos -1 0 FULL +Pos 0 -1 EMPTY + +Index 192 ROTATE +Pos 2 0 INDEX 160 +Pos 1 0 FULL +Pos 0 -1 EMPTY + +Index 161 ROTATE +Pos 0 -1 INDEX 160 +Pos 0 1 FULL +Pos 1 0 EMPTY + +Index 177 ROTATE +Pos 1 -1 INDEX 160 +Pos 0 -1 FULL +Pos 1 0 FULL + +Index 162 ROTATE +Pos 0 -2 INDEX 160 +Pos 0 -1 FULL +Pos 1 0 EMPTY + +#fill bottom-left +Index 176 ROTATE XFLIP YFLIP +Pos -1 0 INDEX 160 +Pos 1 0 FULL +Pos 0 1 EMPTY + +Index 192 ROTATE XFLIP YFLIP +Pos -2 0 INDEX 160 +Pos -1 0 FULL +Pos 0 1 EMPTY + +Index 161 ROTATE XFLIP YFLIP +Pos 0 1 INDEX 160 +Pos 0 -1 FULL +Pos -1 0 EMPTY + +Index 177 ROTATE XFLIP YFLIP +Pos -1 1 INDEX 160 +Pos 0 1 FULL +Pos -1 0 FULL + +Index 162 ROTATE XFLIP YFLIP +Pos 0 2 INDEX 160 +Pos 0 1 FULL +Pos -1 0 EMPTY + +#fill bottom-right +Index 161 XFLIP YFLIP +Pos 1 0 INDEX 160 +Pos -1 0 FULL +Pos 0 1 EMPTY + +Index 162 XFLIP YFLIP +Pos 2 0 INDEX 160 +Pos 1 0 FULL +Pos 0 1 EMPTY + +Index 176 XFLIP YFLIP +Pos 0 1 INDEX 160 +Pos 0 -1 FULL +Pos 1 0 EMPTY + +Index 177 XFLIP YFLIP +Pos 1 1 INDEX 160 +Pos 0 1 FULL +Pos 1 0 FULL + +Index 192 XFLIP YFLIP +Pos 0 2 INDEX 160 +Pos 0 1 FULL +Pos 1 0 EMPTY + +NewRun + +#corner top-right 4x3 +Index 165 +Pos 0 0 INDEX 199 +Pos 1 1 INDEX 199 +Pos -1 -1 EMPTY +Pos -2 -1 EMPTY +Pos -1 0 FULL +Pos -2 0 FULL +Pos -3 0 FULL +Pos -3 1 FULL +Pos -2 1 FULL +Pos -1 1 FULL +Pos 0 1 FULL +Pos -1 2 FULL +Pos 0 2 FULL +Pos 1 2 FULL +Pos 2 2 EMPTY +Pos 0 3 FULL +Pos 1 3 FULL +Pos -2 0 INDEX 200 +Pos 1 2 INDEX 200 + +NewRun + +#corner top-left 4x3 +Index 165 XFLIP +Pos 0 0 INDEX 199 +Pos -1 1 INDEX 199 +Pos 1 -1 EMPTY +Pos 2 -1 EMPTY +Pos 1 0 FULL +Pos 2 0 FULL +Pos 3 0 FULL +Pos 3 1 FULL +Pos 2 1 FULL +Pos 1 1 FULL +Pos 0 1 FULL +Pos 1 2 FULL +Pos 0 2 FULL +Pos -1 2 FULL +Pos -2 2 EMPTY +Pos 0 3 FULL +Pos -1 3 FULL +Pos 2 0 INDEX 200 +Pos -1 2 INDEX 200 +Pos 4 0 NOTINDEX 165 +Pos 3 0 NOTINDEX 165 + +NewRun + +#corner bottom-left 4x3 +Index 165 XFLIP YFLIP +Pos 0 0 INDEX 199 +Pos -1 -1 INDEX 199 +Pos 1 1 EMPTY +Pos 2 1 EMPTY +Pos 1 0 FULL +Pos 2 0 FULL +Pos 3 0 FULL +Pos 3 -1 FULL +Pos 2 -1 FULL +Pos 1 -1 FULL +Pos 0 -1 FULL +Pos 1 -2 FULL +Pos 0 -2 FULL +Pos -1 -2 FULL +Pos -2 -2 EMPTY +Pos 0 -3 FULL +Pos -1 -3 FULL +Pos 2 0 INDEX 200 +Pos -1 -2 INDEX 200 +Pos 0 -4 NOTINDEX 165 + +NewRun + +#corner bottom-right 4x3 +Index 165 YFLIP +Pos 0 0 INDEX 199 +Pos 1 -1 INDEX 199 +Pos -1 1 EMPTY +Pos -2 1 EMPTY +Pos -1 0 FULL +Pos -2 0 FULL +Pos -3 0 FULL +Pos -3 -1 FULL +Pos -2 -1 FULL +Pos -1 -1 FULL +Pos 0 -1 FULL +Pos -1 -2 FULL +Pos 0 -2 FULL +Pos 1 -2 FULL +Pos 2 -2 EMPTY +Pos 0 -3 FULL +Pos 1 -3 FULL +Pos -2 0 INDEX 200 +Pos 1 -2 INDEX 200 +Pos -4 0 NOTINDEX 165 +Pos -3 0 NOTINDEX 165 +Pos 0 -4 NOTINDEX 165 + +NewRun + +#fill top-right 4x3 +Index 164 +Pos 1 0 INDEX 165 +Pos 0 -1 EMPTY +Pos -1 0 FULL + +Index 163 +Pos 2 0 INDEX 165 +Pos 0 -1 EMPTY +Pos -1 0 FULL + +Index 181 +Pos 0 -1 INDEX 165 +Pos 1 -1 EMPTY +Pos 1 0 FULL + +Index 182 +Pos -1 -1 INDEX 165 +Pos -1 0 FULL +Pos 1 0 EMPTY + +Index 198 +Pos -1 -2 INDEX 165 +Pos 0 -1 FULL +Pos 1 0 EMPTY + +#fill top-left 4x3 +Index 164 XFLIP +Pos -1 0 INDEX 165 +Pos 0 -1 EMPTY +Pos 1 0 FULL + +Index 163 XFLIP +Pos -2 0 INDEX 165 +Pos 0 -1 EMPTY +Pos 1 0 FULL + +Index 181 XFLIP +Pos 0 -1 INDEX 165 +Pos -1 -1 EMPTY +Pos -1 0 FULL + +Index 182 XFLIP +Pos 1 -1 INDEX 165 +Pos 1 0 FULL +Pos -1 0 EMPTY + +Index 198 XFLIP +Pos 1 -2 INDEX 165 +Pos 0 -1 FULL +Pos -1 0 EMPTY + +#fill bottom-left 4x3 +Index 164 XFLIP YFLIP +Pos -1 0 INDEX 165 +Pos 0 1 EMPTY +Pos 1 0 FULL + +Index 163 XFLIP YFLIP +Pos -2 0 INDEX 165 +Pos 0 1 EMPTY +Pos 1 0 FULL + +Index 181 XFLIP YFLIP +Pos 0 1 INDEX 165 +Pos -1 1 EMPTY +Pos -1 0 FULL + +Index 182 XFLIP YFLIP +Pos 1 1 INDEX 165 +Pos 1 0 FULL +Pos -1 0 EMPTY + +Index 198 XFLIP YFLIP +Pos 1 2 INDEX 165 +Pos 0 1 FULL +Pos -1 0 EMPTY + +#fill bottom-right 4x3 +Index 164 YFLIP +Pos 1 0 INDEX 165 +Pos 0 1 EMPTY +Pos -1 0 FULL + +Index 163 YFLIP +Pos 2 0 INDEX 165 +Pos 0 1 EMPTY +Pos -1 0 FULL + +Index 181 YFLIP +Pos 0 1 INDEX 165 +Pos 1 1 EMPTY +Pos 1 0 FULL + +Index 182 YFLIP +Pos -1 1 INDEX 165 +Pos -1 0 FULL +Pos 1 0 EMPTY + +Index 198 YFLIP +Pos -1 2 INDEX 165 +Pos 0 1 FULL +Pos 1 0 EMPTY + +NewRun + +#corner top-left 3x4 +Index 165 ROTATE XFLIP YFLIP +Pos 0 0 INDEX 199 +Pos 1 -1 INDEX 199 +Pos 2 -2 EMPTY +Pos 2 -1 FULL +Pos 3 -1 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 3 0 FULL +Pos -1 1 EMPTY +Pos 0 1 FULL +Pos 1 1 FULL +Pos 2 1 FULL +Pos -1 2 EMPTY +Pos 0 2 FULL +Pos 1 2 FULL +Pos 0 3 FULL +Pos 1 3 FULL +Pos 0 2 INDEX 200 +Pos 2 -1 INDEX 200 + +NewRun + +#corner top-right 3x4 +Index 165 ROTATE XFLIP +Pos 0 0 INDEX 199 +Pos -1 -1 INDEX 199 +Pos -2 -2 EMPTY +Pos -2 -1 FULL +Pos -3 -1 FULL +Pos -1 0 FULL +Pos -2 0 FULL +Pos -3 0 FULL +Pos 1 1 EMPTY +Pos 0 1 FULL +Pos -1 1 FULL +Pos -2 1 FULL +Pos 1 2 EMPTY +Pos 0 2 FULL +Pos -1 2 FULL +Pos 0 3 FULL +Pos -1 3 FULL +Pos 0 2 INDEX 200 +Pos -2 -1 INDEX 200 + +NewRun + +#corner bottom-right 3x4 +Index 165 ROTATE +Pos 0 0 INDEX 199 +Pos -1 1 INDEX 199 +Pos -2 2 EMPTY +Pos -2 1 FULL +Pos -3 1 FULL +Pos -1 0 FULL +Pos -2 0 FULL +Pos -3 0 FULL +Pos 1 -1 EMPTY +Pos 0 -1 FULL +Pos -1 -1 FULL +Pos -2 -1 FULL +Pos 1 -2 EMPTY +Pos 0 -2 FULL +Pos -1 -2 FULL +Pos 0 -3 FULL +Pos -1 -3 FULL +Pos 0 -2 INDEX 200 +Pos -2 1 INDEX 200 +Pos 0 -4 NOTINDEX 165 +Pos 0 -3 NOTINDEX 165 + +NewRun + +#corner bottom-left 3x4 +Index 165 ROTATE YFLIP +Pos 0 0 INDEX 199 +Pos 1 1 INDEX 199 +Pos 2 2 EMPTY +Pos 2 1 FULL +Pos 3 1 FULL +Pos 1 0 FULL +Pos 2 0 FULL +Pos 3 0 FULL +Pos -1 -1 EMPTY +Pos 0 -1 FULL +Pos 1 -1 FULL +Pos 2 -1 FULL +Pos -1 -2 EMPTY +Pos 0 -2 FULL +Pos 1 -2 FULL +Pos 0 -3 FULL +Pos 1 -3 FULL +Pos 0 -2 INDEX 200 +Pos 2 1 INDEX 200 +Pos 0 -4 NOTINDEX 165 +Pos 0 -3 NOTINDEX 165 + +NewRun + +#fill top-left 3x4 +Index 164 ROTATE XFLIP YFLIP +Pos 0 -1 INDEX 165 +Pos -1 0 EMPTY +Pos 0 1 FULL + +Index 163 ROTATE XFLIP YFLIP +Pos 0 -2 INDEX 165 +Pos -1 0 EMPTY +Pos 0 1 FULL + +Index 181 ROTATE XFLIP YFLIP +Pos -1 0 INDEX 165 +Pos -1 -1 EMPTY +Pos 0 -1 FULL + +Index 182 ROTATE XFLIP YFLIP +Pos -1 1 INDEX 165 +Pos 0 1 FULL +Pos 0 -1 EMPTY + +Index 198 ROTATE XFLIP YFLIP +Pos -2 1 INDEX 165 +Pos -1 0 FULL +Pos 0 -1 EMPTY + +#fill top-right 3x4 +Index 164 ROTATE XFLIP +Pos 0 -1 INDEX 165 +Pos 1 0 EMPTY +Pos 0 1 FULL + +Index 163 ROTATE XFLIP +Pos 0 -2 INDEX 165 +Pos 1 0 EMPTY +Pos 0 1 FULL + +Index 181 ROTATE XFLIP +Pos 1 0 INDEX 165 +Pos 1 -1 EMPTY +Pos 0 -1 FULL + +Index 182 ROTATE XFLIP +Pos 1 1 INDEX 165 +Pos 0 1 FULL +Pos 0 -1 EMPTY + +Index 198 ROTATE XFLIP +Pos 2 1 INDEX 165 +Pos 1 0 FULL +Pos 0 -1 EMPTY + +#fill bottom-right 3x4 +Index 164 ROTATE +Pos 0 1 INDEX 165 +Pos 1 0 EMPTY +Pos 0 -1 FULL + +Index 163 ROTATE +Pos 0 2 INDEX 165 +Pos 1 0 EMPTY +Pos 0 -1 FULL + +Index 181 ROTATE +Pos 1 0 INDEX 165 +Pos 1 1 EMPTY +Pos 0 1 FULL + +Index 182 ROTATE +Pos 1 -1 INDEX 165 +Pos 0 -1 FULL +Pos 0 1 EMPTY + +Index 198 ROTATE +Pos 2 -1 INDEX 165 +Pos 1 0 FULL +Pos 0 1 EMPTY + +#fill bottom-left 3x4 +Index 164 ROTATE YFLIP +Pos 0 1 INDEX 165 +Pos -1 0 EMPTY +Pos 0 -1 FULL + +Index 163 ROTATE YFLIP +Pos 0 2 INDEX 165 +Pos -1 0 EMPTY +Pos 0 -1 FULL + +Index 181 ROTATE YFLIP +Pos -1 0 INDEX 165 +Pos -1 1 EMPTY +Pos 0 1 FULL + +Index 182 ROTATE YFLIP +Pos -1 -1 INDEX 165 +Pos 0 -1 FULL +Pos 0 1 EMPTY + +Index 198 ROTATE YFLIP +Pos -2 -1 INDEX 165 +Pos -1 0 FULL +Pos 0 1 EMPTY + +NewRun + +#dead end top +Index 227 XFLIP YFLIP +Pos -1 0 INDEX 215 +Pos 0 0 INDEX 200 +Pos 1 0 INDEX 215 +Pos -1 1 INDEX 200 +Pos 0 1 EMPTY +Pos 1 1 INDEX 200 + +#dead end right +Index 227 ROTATE XFLIP YFLIP +Pos 0 -1 INDEX 215 +Pos 0 0 INDEX 200 +Pos 0 1 INDEX 215 +Pos -1 -1 INDEX 200 +Pos -1 0 EMPTY +Pos -1 1 INDEX 200 + +#dead end bottom +Index 227 +Pos -1 0 INDEX 215 +Pos 0 0 INDEX 200 +Pos 1 0 INDEX 215 +Pos -1 -1 INDEX 200 +Pos 0 -1 EMPTY +Pos 1 -1 INDEX 200 + +#dead end left +Index 227 ROTATE +Pos 0 -1 INDEX 215 +Pos 0 0 INDEX 200 +Pos 0 1 INDEX 215 +Pos 1 -1 INDEX 200 +Pos 1 0 EMPTY +Pos 1 1 INDEX 200 + +NewRun + +#fill dead end top +Index 226 XFLIP YFLIP +Pos -1 0 INDEX 227 +Pos 0 1 INDEX 200 +Pos 1 0 FULL +Pos 0 -1 FULL + +Index 228 XFLIP YFLIP +Pos 1 0 INDEX 227 +Pos 0 1 INDEX 200 +Pos -1 0 FULL +Pos 0 1 FULL + +Index 210 XFLIP YFLIP +Pos -1 -1 INDEX 227 +Pos -1 0 EMPTY + +Index 211 XFLIP YFLIP +Pos 0 0 EMPTY +Pos 0 -1 INDEX 227 +Pos 0 1 EMPTY + +Index 212 XFLIP YFLIP +Pos 1 -1 INDEX 227 +Pos 1 0 EMPTY + +#fill dead end right +Index 226 ROTATE XFLIP YFLIP +Pos 0 -1 INDEX 227 +Pos -1 0 INDEX 200 +Pos 0 1 FULL +Pos 1 0 FULL + +Index 228 ROTATE XFLIP YFLIP +Pos 0 1 INDEX 227 +Pos -1 0 INDEX 200 +Pos 0 -1 FULL +Pos 1 0 FULL + +Index 210 ROTATE XFLIP YFLIP +Pos 1 -1 INDEX 227 +Pos 0 -1 EMPTY + +Index 211 ROTATE XFLIP YFLIP +Pos 0 0 EMPTY +Pos 1 0 INDEX 227 +Pos -1 0 EMPTY + +Index 212 ROTATE XFLIP YFLIP +Pos 1 1 INDEX 227 +Pos 0 1 EMPTY + +#fill dead end bottom +Index 226 +Pos 1 0 INDEX 227 +Pos 0 -1 INDEX 200 +Pos -1 0 FULL +Pos 0 1 FULL + +Index 228 +Pos -1 0 INDEX 227 +Pos 0 -1 INDEX 200 +Pos 1 0 FULL +Pos 0 1 FULL + +Index 210 +Pos 1 1 INDEX 227 +Pos 1 0 EMPTY + +Index 211 +Pos 0 0 EMPTY +Pos 0 1 INDEX 227 +Pos 0 -1 EMPTY + +Index 212 +Pos -1 1 INDEX 227 +Pos -1 0 EMPTY + +#fill dead end left +Index 226 ROTATE +Pos 0 1 INDEX 227 +Pos 1 0 INDEX 200 +Pos 0 -1 FULL +Pos -1 0 FULL + +Index 228 ROTATE +Pos 0 -1 INDEX 227 +Pos 1 0 INDEX 200 +Pos 0 1 FULL +Pos -1 0 FULL + +Index 210 ROTATE +Pos -1 1 INDEX 227 +Pos 0 1 EMPTY + +Index 211 ROTATE +Pos 0 0 EMPTY +Pos -1 0 INDEX 227 +Pos 1 0 EMPTY + +Index 212 ROTATE +Pos -1 -1 INDEX 227 +Pos 0 -1 EMPTY + +NewRun + +#s-curve bottom-left 1 +Index 209 XFLIP YFLIP +Pos 0 0 INDEX 215 +Pos 0 -1 INDEX 200 +Pos 1 0 INDEX 199 +Pos 1 -1 EMPTY + +#s-curve bottom-left 1 alt +Index 229 +Pos 0 0 INDEX 215 +Pos 0 -1 INDEX 200 +Pos 1 0 INDEX 199 +Pos 1 -1 EMPTY +Random 2 + +NewRun + +#s-curve bottom-left 2 +Index 209 ROTATE YFLIP +Pos 0 0 INDEX 215 +Pos 1 0 INDEX 200 +Pos 0 -1 INDEX 199 +Pos 1 -1 EMPTY + +#s-curve bottom-left 2 alt +Index 229 ROTATE XFLIP +Pos 0 0 INDEX 215 +Pos 1 0 INDEX 200 +Pos 0 -1 INDEX 199 +Pos 1 -1 EMPTY +Random 2 + +NewRun + +#s-curve top-left 1 +Index 209 XFLIP +Pos 0 0 INDEX 215 +Pos 0 1 INDEX 200 +Pos 1 0 INDEX 199 +Pos 1 1 EMPTY +Pos 0 2 NOTINDEX 209 OR 229 + +#s-curve top-left 1 alt +Index 229 YFLIP +Pos 0 0 INDEX 215 +Pos 0 1 INDEX 200 +Pos 1 0 INDEX 199 +Pos 1 1 EMPTY +Pos 0 2 NOTINDEX 209 OR 229 +Random 2 + +NewRun + +#s-curve top-left 2 +Index 209 ROTATE XFLIP YFLIP +Pos 0 0 INDEX 215 +Pos 1 0 INDEX 200 +Pos 0 1 INDEX 199 +Pos 1 1 EMPTY + +#s-curve top-left 2 alt +Index 229 ROTATE +Pos 0 0 INDEX 215 +Pos 1 0 INDEX 200 +Pos 0 1 INDEX 199 +Pos 1 1 EMPTY +Random 2 + +NewRun + +#s-curve top-right 1 +Index 209 +Pos 0 0 INDEX 215 +Pos 0 1 INDEX 200 +Pos -1 0 INDEX 199 +Pos -1 1 EMPTY + +#s-curve top-right 1 alt +Index 229 XFLIP YFLIP +Pos 0 0 INDEX 215 +Pos 0 1 INDEX 200 +Pos -1 0 INDEX 199 +Pos -1 1 EMPTY +Random 2 + +NewRun + +#s-curve top-right 2 +Index 209 ROTATE XFLIP +Pos 0 0 INDEX 215 +Pos -1 0 INDEX 200 +Pos 0 1 INDEX 199 +Pos -1 1 EMPTY +Pos -2 0 NOTINDEX 209 OR 229 + +#s-curve top-right 2 alt +Index 229 ROTATE YFLIP +Pos 0 0 INDEX 215 +Pos -1 0 INDEX 200 +Pos 0 1 INDEX 199 +Pos -1 1 EMPTY +Pos -2 0 NOTINDEX 209 OR 229 +Random 2 + +NewRun + +#s-curve bottom-right 1 +Index 209 YFLIP +Pos 0 0 INDEX 215 +Pos 0 -1 INDEX 200 +Pos -1 0 INDEX 199 +Pos -1 -1 EMPTY +Pos 0 -2 NOTINDEX 209 OR 229 + +#s-curve bottom-right 1 alt +Index 229 XFLIP +Pos 0 0 INDEX 215 +Pos 0 -1 INDEX 200 +Pos -1 0 INDEX 199 +Pos -1 -1 EMPTY +Pos 0 -2 NOTINDEX 209 OR 229 +Random 2 + +NewRun + +#s-curve bottom-right 2 +Index 209 ROTATE +Pos 0 0 INDEX 215 +Pos -1 0 INDEX 200 +Pos 0 -1 INDEX 199 +Pos -1 -1 EMPTY +Pos -2 0 NOTINDEX 209 OR 229 + +#s-curve bottom-right 2 alt +Index 229 ROTATE XFLIP YFLIP +Pos 0 0 INDEX 215 +Pos -1 0 INDEX 200 +Pos 0 -1 INDEX 199 +Pos -1 -1 EMPTY +Pos -2 0 NOTINDEX 209 OR 229 +Random 2 + +NewRun + +#fill s-curve bottom-left 1 +Index 225 XFLIP YFLIP +Pos 0 0 INDEX 200 +Pos 0 1 INDEX 209 +Pos 1 0 EMPTY + +Index 208 XFLIP YFLIP +Pos 0 0 INDEX 199 +Pos -1 0 INDEX 209 +Pos 0 -1 EMPTY + +Index 224 XFLIP YFLIP +Pos 0 0 EMPTY +Pos -1 1 INDEX 209 +Pos -1 0 INDEX 200 + +#fill s-curve bottom-left 1 alt +Index 213 +Pos 0 0 INDEX 200 +Pos 0 1 INDEX 229 +Pos 1 0 EMPTY + +Index 230 +Pos 0 0 INDEX 199 +Pos -1 0 INDEX 229 +Pos 0 -1 EMPTY + +Index 214 +Pos 0 0 EMPTY +Pos -1 1 INDEX 229 +Pos -1 0 INDEX 200 + +#fill s-curve bottom-left 2 +Index 225 ROTATE YFLIP +Pos 0 0 INDEX 200 +Pos -1 0 INDEX 209 +Pos 0 -1 EMPTY + +Index 208 ROTATE YFLIP +Pos 0 0 INDEX 199 +Pos 0 1 INDEX 209 +Pos 1 0 EMPTY + +Index 224 ROTATE YFLIP +Pos 0 0 EMPTY +Pos -1 1 INDEX 209 +Pos 0 1 INDEX 200 + +#fill s-curve bottom-left 2 alt +Index 213 ROTATE XFLIP +Pos 0 0 INDEX 200 +Pos -1 0 INDEX 229 +Pos 0 -1 EMPTY + +Index 230 ROTATE XFLIP +Pos 0 0 INDEX 199 +Pos 0 1 INDEX 229 +Pos 1 0 EMPTY + +Index 214 ROTATE XFLIP +Pos 0 0 EMPTY +Pos -1 1 INDEX 229 +Pos 0 1 INDEX 200 + +#fill s-curve top-left 1 +Index 225 XFLIP +Pos 0 0 INDEX 200 +Pos 0 -1 INDEX 209 +Pos 1 0 EMPTY + +Index 208 XFLIP +Pos 0 0 INDEX 199 +Pos -1 0 INDEX 209 +Pos 0 1 EMPTY + +Index 224 XFLIP +Pos 0 0 EMPTY +Pos -1 -1 INDEX 209 +Pos -1 0 INDEX 200 + +#fill s-curve top-left 1 alt +Index 213 YFLIP +Pos 0 0 INDEX 200 +Pos 0 -1 INDEX 229 +Pos 1 0 EMPTY + +Index 230 YFLIP +Pos 0 0 INDEX 199 +Pos -1 0 INDEX 229 +Pos 0 1 EMPTY + +Index 214 YFLIP +Pos 0 0 EMPTY +Pos -1 -1 INDEX 229 +Pos -1 0 INDEX 200 + +#fill s-curve top-left 2 +Index 225 ROTATE XFLIP YFLIP +Pos 0 0 INDEX 200 +Pos -1 0 INDEX 209 +Pos 0 1 EMPTY + +Index 208 ROTATE XFLIP YFLIP +Pos 0 0 INDEX 199 +Pos 0 -1 INDEX 209 +Pos 1 0 EMPTY + +Index 224 ROTATE XFLIP YFLIP +Pos 0 0 EMPTY +Pos -1 -1 INDEX 209 +Pos 0 -1 INDEX 200 + +#fill s-curve top-left 2 alt +Index 213 ROTATE +Pos 0 0 INDEX 200 +Pos -1 0 INDEX 229 +Pos 0 1 EMPTY + +Index 230 ROTATE +Pos 0 0 INDEX 199 +Pos 0 -1 INDEX 229 +Pos 1 0 EMPTY + +Index 214 ROTATE +Pos 0 0 EMPTY +Pos -1 -1 INDEX 229 +Pos 0 -1 INDEX 200 + +#fill s-curve top-right 1 +Index 225 +Pos 0 0 INDEX 200 +Pos 0 -1 INDEX 209 +Pos -1 0 EMPTY + +Index 208 +Pos 0 0 INDEX 199 +Pos 1 0 INDEX 209 +Pos 0 1 EMPTY + +Index 224 +Pos 0 0 EMPTY +Pos 1 -1 INDEX 209 +Pos 1 0 INDEX 200 + +#fill s-curve top-right 1 alt +Index 213 XFLIP YFLIP +Pos 0 0 INDEX 200 +Pos 0 -1 INDEX 229 +Pos -1 0 EMPTY + +Index 230 XFLIP YFLIP +Pos 0 0 INDEX 199 +Pos 1 0 INDEX 229 +Pos 0 1 EMPTY + +Index 214 XFLIP YFLIP +Pos 0 0 EMPTY +Pos 1 -1 INDEX 229 +Pos 1 0 INDEX 200 + +#fill s-curve top-right 2 +Index 225 ROTATE XFLIP +Pos 0 0 INDEX 200 +Pos 1 0 INDEX 209 +Pos 0 1 EMPTY + +Index 208 ROTATE XFLIP +Pos 0 0 INDEX 199 +Pos 0 -1 INDEX 209 +Pos -1 0 EMPTY + +Index 224 ROTATE XFLIP +Pos 0 0 EMPTY +Pos 1 -1 INDEX 209 +Pos 0 -1 INDEX 200 + +#fill s-curve top-right 2 alt +Index 213 ROTATE YFLIP +Pos 0 0 INDEX 200 +Pos 1 0 INDEX 229 +Pos 0 1 EMPTY + +Index 230 ROTATE YFLIP +Pos 0 0 INDEX 199 +Pos 0 -1 INDEX 229 +Pos -1 0 EMPTY + +Index 214 ROTATE YFLIP +Pos 0 0 EMPTY +Pos 1 -1 INDEX 229 +Pos 0 -1 INDEX 200 + +#fill s-curve bottom-right 1 +Index 225 YFLIP +Pos 0 0 INDEX 200 +Pos 0 1 INDEX 209 +Pos -1 0 EMPTY + +Index 208 YFLIP +Pos 0 0 INDEX 199 +Pos 1 0 INDEX 209 +Pos 0 -1 EMPTY + +Index 224 YFLIP +Pos 0 0 EMPTY +Pos 1 1 INDEX 209 +Pos 1 0 INDEX 200 + +#fill s-curve bottom-right 1 alt +Index 213 XFLIP +Pos 0 0 INDEX 200 +Pos 0 1 INDEX 229 +Pos -1 0 EMPTY + +Index 230 XFLIP +Pos 0 0 INDEX 199 +Pos 1 0 INDEX 229 +Pos 0 -1 EMPTY + +Index 214 XFLIP +Pos 0 0 EMPTY +Pos 1 1 INDEX 229 +Pos 1 0 INDEX 200 + +#fill s-curve bottom-right 2 +Index 225 ROTATE +Pos 0 0 INDEX 200 +Pos 1 0 INDEX 209 +Pos 0 -1 EMPTY + +Index 208 ROTATE +Pos 0 0 INDEX 199 +Pos 0 1 INDEX 209 +Pos -1 0 EMPTY + +Index 224 ROTATE +Pos 0 0 EMPTY +Pos 1 1 INDEX 209 +Pos 0 1 INDEX 200 + +#fill s-curve bottom-right 2 alt +Index 213 ROTATE XFLIP YFLIP +Pos 0 0 INDEX 200 +Pos 1 0 INDEX 229 +Pos 0 -1 EMPTY + +Index 230 ROTATE XFLIP YFLIP +Pos 0 0 INDEX 199 +Pos 0 1 INDEX 229 +Pos -1 0 EMPTY + +Index 214 ROTATE XFLIP YFLIP +Pos 0 0 EMPTY +Pos 1 1 INDEX 229 +Pos 0 1 INDEX 200 + + +NewRun + +#top long +Index 233 +Pos 0 0 INDEX 200 +Pos 0 -1 EMPTY +Pos 0 -2 EMPTY +Pos -1 0 FULL +Pos 1 0 INDEX 200 +Pos 2 0 INDEX 200 +Random 5 + +#right long +Index 233 ROTATE +Pos 0 0 INDEX 200 +Pos 1 0 EMPTY +Pos 2 0 EMPTY +Pos 0 -1 FULL +Pos 0 1 INDEX 200 +Pos 0 2 INDEX 200 +Random 5 + +#bottom long +Index 233 XFLIP YFLIP +Pos 0 0 INDEX 200 +Pos 0 1 EMPTY +Pos 0 2 EMPTY +Pos 1 0 FULL +Pos -1 0 INDEX 200 +Pos -2 0 INDEX 200 +Random 5 + +#left long +Index 233 ROTATE XFLIP YFLIP +Pos 0 0 INDEX 200 +Pos -1 0 EMPTY +Pos -2 0 EMPTY +Pos 0 1 FULL +Pos 0 -1 INDEX 200 +Pos 0 -2 INDEX 200 +Random 5 + + +NewRun + +#top overlap +Index 200 +Pos 0 0 233 +Pos 0 -1 EMPTY +Pos 0 0 233 +Pos -1 0 INDEX 233 + +#top overlap 2 +Index 200 +Pos 0 0 233 +Pos 0 -1 EMPTY +Pos 0 0 INDEX 233 +Pos -2 0 INDEX 233 + +#right overlap +Index 200 ROTATE +Pos 0 0 233 +Pos 1 0 EMPTY +Pos 0 0 INDEX 233 +Pos 0 -1 INDEX 233 + +#right overlap 2 +Index 200 ROTATE +Pos 0 0 233 +Pos 1 0 EMPTY +Pos 0 0 INDEX 233 +Pos 0 -2 INDEX 233 + +#bottom overlap +Index 200 XFLIP YFLIP +Pos 0 0 233 +Pos 0 1 EMPTY +Pos 0 0 INDEX 233 +Pos 1 0 INDEX 233 + +#bottom overlap 2 +Index 200 XFLIP YFLIP +Pos 0 0 233 +Pos 0 1 EMPTY +Pos 0 0 INDEX 233 +Pos 2 0 INDEX 233 + +#left overlap +Index 200 ROTATE XFLIP YFLIP +Pos 0 0 233 +Pos -1 0 EMPTY +Pos 0 0 INDEX 233 +Pos 0 1 INDEX 233 + +#left overlap 2 +Index 200 ROTATE XFLIP YFLIP +Pos 0 0 233 +Pos -1 0 EMPTY +Pos 0 0 INDEX 233 +Pos 0 2 INDEX 233 + +NewRun + +#fill top long 1 +Index 234 +Pos -1 0 INDEX 233 +Pos 0 -1 EMPTY + +#fill top long 2 +Index 235 +Pos -2 0 INDEX 233 +Pos -1 0 INDEX 200 +Pos 0 -1 EMPTY +Pos 1 0 FULL + +#fill right long 1 +Index 234 ROTATE +Pos 0 -1 INDEX 233 +Pos 1 0 EMPTY + +#fill right long 2 +Index 235 ROTATE +Pos 0 -2 INDEX 233 +Pos 0 -1 INDEX 200 +Pos 1 0 EMPTY +Pos 0 1 FULL + +#fill bottom long 1 +Index 234 XFLIP YFLIP +Pos 1 0 INDEX 233 +Pos 0 1 EMPTY + +#fill bottom long 2 +Index 235 XFLIP YFLIP +Pos 2 0 INDEX 233 +Pos 1 0 INDEX 200 +Pos 0 1 EMPTY +Pos -1 0 FULL + +#fill left long 1 +Index 234 ROTATE XFLIP YFLIP +Pos 0 1 INDEX 233 +Pos -1 0 EMPTY + +#fill left long 2 +Index 235 ROTATE XFLIP YFLIP +Pos 0 2 INDEX 233 +Pos 0 1 INDEX 200 +Pos -1 0 EMPTY +Pos 0 -1 FULL + +NewRun + +#fill top long 3 +Index 217 +Pos 0 0 EMPTY +Pos 0 1 INDEX 233 +Pos 0 2 FULL + +#fill top long 4 +Index 218 +Pos 0 0 EMPTY +Pos 0 1 INDEX 234 +Pos 0 2 FULL + +#fill top long 5 +Index 219 +Pos 0 0 EMPTY +Pos 0 1 INDEX 235 +Pos 0 2 FULL + +#fill right long 3 +Index 217 ROTATE +Pos 0 0 EMPTY +Pos -1 0 INDEX 233 +Pos -2 0 FULL + +#fill right long 4 +Index 218 ROTATE +Pos 0 0 EMPTY +Pos -1 0 INDEX 234 +Pos -2 0 FULL + +#fill right long 5 +Index 219 ROTATE +Pos 0 0 EMPTY +Pos -1 0 INDEX 235 +Pos -2 0 FULL + +#fill bottom long 3 +Index 217 XFLIP YFLIP +Pos 0 0 EMPTY +Pos 0 -1 INDEX 233 +Pos 0 -2 FULL + +#fill bottom long 4 +Index 218 XFLIP YFLIP +Pos 0 0 EMPTY +Pos 0 -1 INDEX 234 +Pos 0 -2 FULL + +#fill bottom long 5 +Index 219 XFLIP YFLIP +Pos 0 0 EMPTY +Pos 0 -1 INDEX 235 +Pos 0 -2 FULL + +#fill left long 3 +Index 217 ROTATE XFLIP YFLIP +Pos 0 0 EMPTY +Pos 1 0 INDEX 233 +Pos 2 0 FULL + +#fill left long 4 +Index 218 ROTATE XFLIP YFLIP +Pos 0 0 EMPTY +Pos 1 0 INDEX 234 +Pos 2 0 FULL + +#fill left long 5 +Index 219 ROTATE XFLIP YFLIP +Pos 0 0 EMPTY +Pos 1 0 INDEX 235 +Pos 2 0 FULL diff --git a/data/editor/round_tiles.rules b/data/editor/automap/round_tiles.rules similarity index 100% rename from data/editor/round_tiles.rules rename to data/editor/automap/round_tiles.rules diff --git a/data/editor/water.rules b/data/editor/automap/water.rules similarity index 100% rename from data/editor/water.rules rename to data/editor/automap/water.rules diff --git a/data/editor/winter_main.rules b/data/editor/automap/winter_main.rules similarity index 100% rename from data/editor/winter_main.rules rename to data/editor/automap/winter_main.rules diff --git a/src/game/editor/auto_map.cpp b/src/game/editor/auto_map.cpp index bca98c498..831be5b6d 100644 --- a/src/game/editor/auto_map.cpp +++ b/src/game/editor/auto_map.cpp @@ -47,10 +47,15 @@ CAutoMapper::CAutoMapper(CEditor *pEditor) void CAutoMapper::Load(const char *pTileName) { char aPath[IO_MAX_PATH_LENGTH]; - str_format(aPath, sizeof(aPath), "editor/%s.rules", pTileName); + str_format(aPath, sizeof(aPath), "editor/automap/%s.rules", pTileName); IOHANDLE RulesFile = Storage()->OpenFile(aPath, IOFLAG_READ | IOFLAG_SKIP_BOM, IStorage::TYPE_ALL); if(!RulesFile) + { + char aBuf[IO_MAX_PATH_LENGTH + 32]; + str_format(aBuf, sizeof(aBuf), "failed to load %s", aPath); + Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor/automap", aBuf); return; + } CLineReader LineReader; LineReader.Init(RulesFile); From ead33ba05a29d3e66ffe0b03a9348f7b30572aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 2 Sep 2023 12:01:06 +0200 Subject: [PATCH 055/126] Add debug messages for all error cases in `LoadPNG` --- src/engine/gfx/image_loader.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/engine/gfx/image_loader.cpp b/src/engine/gfx/image_loader.cpp index e9ad79ec4..946234a7c 100644 --- a/src/engine/gfx/image_loader.cpp +++ b/src/engine/gfx/image_loader.cpp @@ -17,14 +17,14 @@ struct SLibPNGWarningItem { SLibPNGWarningItem *pUserStruct = (SLibPNGWarningItem *)png_get_error_ptr(png_ptr); pUserStruct->m_pByteLoader->m_Err = -1; - dbg_msg("libpng", "error for file \"%s\": %s", pUserStruct->m_pFileName, error_msg); + dbg_msg("png", "error for file \"%s\": %s", pUserStruct->m_pFileName, error_msg); std::longjmp(pUserStruct->m_Buf, 1); } static void LibPNGWarning(png_structp png_ptr, png_const_charp warning_msg) { SLibPNGWarningItem *pUserStruct = (SLibPNGWarningItem *)png_get_error_ptr(png_ptr); - dbg_msg("libpng", "warning for file \"%s\": %s", pUserStruct->m_pFileName, warning_msg); + dbg_msg("png", "warning for file \"%s\": %s", pUserStruct->m_pFileName, warning_msg); } static bool FileMatchesImageType(SImageByteBuffer &ByteLoader) @@ -175,6 +175,7 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn if(!FileMatchesImageType(ByteLoader)) { LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); + dbg_msg("png", "file does not match image type."); return false; } @@ -189,6 +190,7 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn if(ByteLoader.m_Err != 0) { LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); + dbg_msg("png", "byte loader error."); return false; } @@ -255,6 +257,7 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn if(ByteLoader.m_Err != 0) { LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); + dbg_msg("png", "byte loader error."); return false; } @@ -263,6 +266,7 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn else { LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); + dbg_msg("png", "bytes in row incorrect."); return false; } From 872d6c9e5e962164c2297504f902132855816442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 2 Sep 2023 12:05:31 +0200 Subject: [PATCH 056/126] Move variable declarations closer to usage/definition --- src/engine/gfx/image_loader.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/engine/gfx/image_loader.cpp b/src/engine/gfx/image_loader.cpp index 946234a7c..7bca19969 100644 --- a/src/engine/gfx/image_loader.cpp +++ b/src/engine/gfx/image_loader.cpp @@ -131,13 +131,6 @@ static int PngliteIncompatibility(png_structp pPNGStruct, png_infop pPNGInfo) bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIncompatible, int &Width, int &Height, uint8_t *&pImageBuff, EImageFormat &ImageFormat) { - png_infop pPNGInfo = nullptr; - int ColorType; - png_byte BitDepth; - int ColorChannelCount; - int BytesInRow; - Height = 0; - png_bytepp pRowPointers = nullptr; SLibPNGWarningItem UserErrorStruct = {&ByteLoader, pFileName, {}}; png_structp pPNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); @@ -148,6 +141,9 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn return false; } + png_infop pPNGInfo = nullptr; + png_bytepp pRowPointers = nullptr; + Height = 0; // ensure this is not undefined for the error handler if(setjmp(UserErrorStruct.m_Buf)) { if(pRowPointers != nullptr) @@ -196,8 +192,8 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn Width = png_get_image_width(pPNGStruct, pPNGInfo); Height = png_get_image_height(pPNGStruct, pPNGInfo); - ColorType = png_get_color_type(pPNGStruct, pPNGInfo); - BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo); + const int ColorType = png_get_color_type(pPNGStruct, pPNGInfo); + const png_byte BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo); PngliteIncompatible = PngliteIncompatibility(pPNGStruct, pPNGInfo); if(BitDepth == 16) @@ -229,8 +225,8 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn png_read_update_info(pPNGStruct, pPNGInfo); - ColorChannelCount = LibPNGGetColorChannelCount(ColorType); - BytesInRow = png_get_rowbytes(pPNGStruct, pPNGInfo); + const int ColorChannelCount = LibPNGGetColorChannelCount(ColorType); + const int BytesInRow = png_get_rowbytes(pPNGStruct, pPNGInfo); if(BytesInRow == Width * ColorChannelCount) { From ab6262d7ce9b63261ca34e102cefce3f7824de13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 2 Sep 2023 12:07:46 +0200 Subject: [PATCH 057/126] Use `nullptr` instead of `NULL` --- src/engine/gfx/image_loader.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/engine/gfx/image_loader.cpp b/src/engine/gfx/image_loader.cpp index 7bca19969..bad7d9c69 100644 --- a/src/engine/gfx/image_loader.cpp +++ b/src/engine/gfx/image_loader.cpp @@ -80,7 +80,7 @@ static void LibPNGDeleteReadStruct(png_structp pPNGStruct, png_infop pPNGInfo) { if(pPNGInfo != nullptr) png_destroy_info_struct(pPNGStruct, &pPNGInfo); - png_destroy_read_struct(&pPNGStruct, NULL, NULL); + png_destroy_read_struct(&pPNGStruct, nullptr, nullptr); } static int PngliteIncompatibility(png_structp pPNGStruct, png_infop pPNGInfo) @@ -133,9 +133,9 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn { SLibPNGWarningItem UserErrorStruct = {&ByteLoader, pFileName, {}}; - png_structp pPNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_structp pPNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if(pPNGStruct == NULL) + if(pPNGStruct == nullptr) { dbg_msg("png", "libpng internal failure: png_create_read_struct failed."); return false; @@ -161,9 +161,9 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn pPNGInfo = png_create_info_struct(pPNGStruct); - if(pPNGInfo == NULL) + if(pPNGInfo == nullptr) { - png_destroy_read_struct(&pPNGStruct, NULL, NULL); + png_destroy_read_struct(&pPNGStruct, nullptr, nullptr); dbg_msg("png", "libpng internal failure: png_create_info_struct failed."); return false; } @@ -267,7 +267,7 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn } png_destroy_info_struct(pPNGStruct, &pPNGInfo); - png_destroy_read_struct(&pPNGStruct, NULL, NULL); + png_destroy_read_struct(&pPNGStruct, nullptr, nullptr); return true; } @@ -304,9 +304,9 @@ static int ImageLoaderHelperFormatToColorChannel(EImageFormat Format) bool SavePNG(EImageFormat ImageFormat, const uint8_t *pRawBuffer, SImageByteBuffer &WrittenBytes, int Width, int Height) { - png_structp pPNGStruct = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_structp pPNGStruct = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if(pPNGStruct == NULL) + if(pPNGStruct == nullptr) { dbg_msg("png", "libpng internal failure: png_create_write_struct failed."); return false; @@ -314,9 +314,9 @@ bool SavePNG(EImageFormat ImageFormat, const uint8_t *pRawBuffer, SImageByteBuff png_infop pPNGInfo = png_create_info_struct(pPNGStruct); - if(pPNGInfo == NULL) + if(pPNGInfo == nullptr) { - png_destroy_read_struct(&pPNGStruct, NULL, NULL); + png_destroy_read_struct(&pPNGStruct, nullptr, nullptr); dbg_msg("png", "libpng internal failure: png_create_info_struct failed."); return false; } @@ -361,7 +361,7 @@ bool SavePNG(EImageFormat ImageFormat, const uint8_t *pRawBuffer, SImageByteBuff delete[](pRowPointers); png_destroy_info_struct(pPNGStruct, &pPNGInfo); - png_destroy_write_struct(&pPNGStruct, NULL); + png_destroy_write_struct(&pPNGStruct, nullptr); return true; } From 533d401ea555967414dd7078ce04aeb643820618 Mon Sep 17 00:00:00 2001 From: devdenn Date: Sat, 2 Sep 2023 16:57:24 +0200 Subject: [PATCH 058/126] Remove setting default zoom after leaving multiview --- src/game/client/components/camera.cpp | 3 ++- src/game/client/gameclient.cpp | 9 ++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp index 18275c48d..3937b42b9 100644 --- a/src/game/client/components/camera.cpp +++ b/src/game/client/components/camera.cpp @@ -10,6 +10,7 @@ #include "camera.h" #include "controls.h" + #include CCamera::CCamera() @@ -224,7 +225,7 @@ void CCamera::ConZoom(IConsole::IResult *pResult, void *pUserData) float TargetLevel = pResult->NumArguments() ? pResult->GetFloat(0) : g_Config.m_ClDefaultZoom; pSelf->ChangeZoom(std::pow(CCamera::ZOOM_STEP, TargetLevel - 10), pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active && pSelf->GameClient()->m_MultiViewActivated ? g_Config.m_ClMultiViewZoomSmoothness : g_Config.m_ClSmoothZoomTime); - if(pSelf->GameClient()->m_MultiViewActivated) + if(pSelf->GameClient()->m_MultiViewActivated && pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active) pSelf->GameClient()->m_MultiViewPersonalZoom = 0; } void CCamera::ConSetView(IConsole::IResult *pResult, void *pUserData) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index a449c91b0..7bf4acfd8 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -637,11 +637,6 @@ void CGameClient::UpdatePositions() if(!m_MultiViewActivated && m_MultiView.m_IsInit) ResetMultiView(); - else if(!m_Snap.m_SpecInfo.m_Active) - { - m_Camera.SetZoom(std::pow(CCamera::ZOOM_STEP, g_Config.m_ClDefaultZoom - 10), g_Config.m_ClSmoothZoomTime); - m_MultiViewPersonalZoom = 0; - } UpdateRenderedCharacters(); } @@ -907,7 +902,7 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker, int Conn, bool Dumm // if everyone of a team killed, we have no ids to spectate anymore, so we disable multi view if(!IsMultiViewIdSet()) - m_MultiViewActivated = false; + ResetMultiView(); else { // the "main" tee killed, search a new one @@ -3582,7 +3577,7 @@ void CGameClient::HandleMultiView() m_MultiView.m_SecondChance = Client()->LocalTime() + 0.3f; else if(m_MultiView.m_SecondChance < Client()->LocalTime()) { - m_MultiViewActivated = false; + ResetMultiView(); return; } return; From dde45f7a403549d41d96321ff0ece605003a3e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 27 Apr 2023 17:15:48 +0200 Subject: [PATCH 059/126] Add `CImageInfo::PixelSize` function, use `enum EImageFormat` Use `enum EImageFormat` type for image format literals and variables. Add `PixelSize` function to get the number of bytes/color channels per pixel for a specified image format. Remove unused store format argument of texture loading functions. All textures are automatically being stored as RGBA, so the argument was unused. Also remove the therefore unused `FORMAT_AUTO`. Rename variables consistently to `PixelSize` and use `size_t`, instead of mixing different names like `BPP` and `ColorChannelCount`. Validate image format loaded from maps using `CImageInfo::ImageFormatFromInt`. Add `FORMAT_ERROR` to represent invalid formats. Remove redundant `PixelSize` parameter from graphics backends and commands, which can be derived from the texture format. Fix memory leak when RGB image data is being converted to RGBA format when saving map in editor. --- src/engine/client/backend/backend_base.cpp | 13 +--- src/engine/client/backend/backend_base.h | 5 +- src/engine/client/backend/null/backend_null.h | 2 +- .../client/backend/opengl/backend_opengl.cpp | 47 ++++++------ .../client/backend/opengl/backend_opengl.h | 6 +- .../client/backend/opengl/backend_opengl3.cpp | 41 +++++------ .../client/backend/opengl/backend_opengl3.h | 2 +- .../client/backend/vulkan/backend_vulkan.cpp | 42 ++++++----- src/engine/client/client.cpp | 2 +- src/engine/client/graphics_threaded.cpp | 72 +++++++------------ src/engine/client/graphics_threaded.h | 11 ++- src/engine/client/text.cpp | 12 ++-- src/engine/client/video.cpp | 2 +- src/engine/graphics.h | 59 ++++++++++----- src/engine/textrender.h | 2 +- src/game/client/components/countryflags.cpp | 2 +- src/game/client/components/mapimages.cpp | 45 ++++++------ src/game/client/components/mapimages.h | 2 +- src/game/client/components/menus.cpp | 5 +- src/game/client/gameclient.cpp | 2 +- src/game/editor/editor.cpp | 36 ++++------ src/game/editor/mapitems/map_io.cpp | 24 ++++--- src/game/editor/popups.cpp | 2 +- src/game/editor/tileart.cpp | 35 +++------ src/tools/map_convert_07.cpp | 2 +- src/tools/map_create_pixelart.cpp | 6 +- src/tools/map_replace_image.cpp | 2 +- 27 files changed, 220 insertions(+), 261 deletions(-) diff --git a/src/engine/client/backend/backend_base.cpp b/src/engine/client/backend/backend_base.cpp index 498217a9b..9e2ad5c4d 100644 --- a/src/engine/client/backend/backend_base.cpp +++ b/src/engine/client/backend/backend_base.cpp @@ -1,24 +1,17 @@ #include "backend_base.h" #include -size_t CCommandProcessorFragment_GLBase::TexFormatToImageColorChannelCount(int TexFormat) -{ - if(TexFormat == CCommandBuffer::TEXFORMAT_RGBA) - return 4; - return 4; -} - void *CCommandProcessorFragment_GLBase::Resize(const unsigned char *pData, int Width, int Height, int NewWidth, int NewHeight, int BPP) { return ResizeImage((const uint8_t *)pData, Width, Height, NewWidth, NewHeight, BPP); } -bool CCommandProcessorFragment_GLBase::Texture2DTo3D(void *pImageBuffer, int ImageWidth, int ImageHeight, int ImageColorChannelCount, int SplitCountWidth, int SplitCountHeight, void *pTarget3DImageData, int &Target3DImageWidth, int &Target3DImageHeight) +bool CCommandProcessorFragment_GLBase::Texture2DTo3D(void *pImageBuffer, int ImageWidth, int ImageHeight, size_t PixelSize, int SplitCountWidth, int SplitCountHeight, void *pTarget3DImageData, int &Target3DImageWidth, int &Target3DImageHeight) { Target3DImageWidth = ImageWidth / SplitCountWidth; Target3DImageHeight = ImageHeight / SplitCountHeight; - size_t FullImageWidth = (size_t)ImageWidth * ImageColorChannelCount; + const size_t FullImageWidth = (size_t)ImageWidth * PixelSize; for(int Y = 0; Y < SplitCountHeight; ++Y) { @@ -28,7 +21,7 @@ bool CCommandProcessorFragment_GLBase::Texture2DTo3D(void *pImageBuffer, int Ima { int DepthIndex = X + Y * SplitCountWidth; - size_t TargetImageFullWidth = (size_t)Target3DImageWidth * ImageColorChannelCount; + size_t TargetImageFullWidth = (size_t)Target3DImageWidth * PixelSize; size_t TargetImageFullSize = (size_t)TargetImageFullWidth * Target3DImageHeight; ptrdiff_t ImageOffset = (ptrdiff_t)(((size_t)Y * FullImageWidth * (size_t)Target3DImageHeight) + ((size_t)Y3D * FullImageWidth) + ((size_t)X * TargetImageFullWidth)); ptrdiff_t TargetImageOffset = (ptrdiff_t)(TargetImageFullSize * (size_t)DepthIndex + ((size_t)Y3D * TargetImageFullWidth)); diff --git a/src/engine/client/backend/backend_base.h b/src/engine/client/backend/backend_base.h index 5d389ac76..25af6a684 100644 --- a/src/engine/client/backend/backend_base.h +++ b/src/engine/client/backend/backend_base.h @@ -84,12 +84,11 @@ protected: SGFXErrorContainer m_Error; SGFXWarningContainer m_Warning; - static size_t TexFormatToImageColorChannelCount(int TexFormat); static void *Resize(const unsigned char *pData, int Width, int Height, int NewWidth, int NewHeight, int BPP); - static bool Texture2DTo3D(void *pImageBuffer, int ImageWidth, int ImageHeight, int ImageColorChannelCount, int SplitCountWidth, int SplitCountHeight, void *pTarget3DImageData, int &Target3DImageWidth, int &Target3DImageHeight); + static bool Texture2DTo3D(void *pImageBuffer, int ImageWidth, int ImageHeight, size_t PixelSize, int SplitCountWidth, int SplitCountHeight, void *pTarget3DImageData, int &Target3DImageWidth, int &Target3DImageHeight); - virtual bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector &vDstData) = 0; + virtual bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector &vDstData) = 0; public: virtual ~CCommandProcessorFragment_GLBase() = default; diff --git a/src/engine/client/backend/null/backend_null.h b/src/engine/client/backend/null/backend_null.h index 97641d9bc..dd7670367 100644 --- a/src/engine/client/backend/null/backend_null.h +++ b/src/engine/client/backend/null/backend_null.h @@ -5,7 +5,7 @@ class CCommandProcessorFragment_Null : public CCommandProcessorFragment_GLBase { - bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector &vDstData) override { return false; }; + bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector &vDstData) override { return false; }; ERunCommandReturnTypes RunCommand(const CCommandBuffer::SCommand *pBaseCommand) override; bool Cmd_Init(const SCommand_Init *pCommand); virtual void Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand); diff --git a/src/engine/client/backend/opengl/backend_opengl.cpp b/src/engine/client/backend/opengl/backend_opengl.cpp index cfa0aab88..66e4e2f72 100644 --- a/src/engine/client/backend/opengl/backend_opengl.cpp +++ b/src/engine/client/backend/opengl/backend_opengl.cpp @@ -49,7 +49,7 @@ int CCommandProcessorFragment_OpenGL::TexFormatToOpenGLFormat(int TexFormat) return GL_RGBA; } -size_t CCommandProcessorFragment_OpenGL::GLFormatToImageColorChannelCount(int GLFormat) +size_t CCommandProcessorFragment_OpenGL::GLFormatToPixelSize(int GLFormat) { switch(GLFormat) { @@ -278,7 +278,7 @@ GfxOpenGLMessageCallback(GLenum Source, } #endif -bool CCommandProcessorFragment_OpenGL::GetPresentedImageData(uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector &vDstData) +bool CCommandProcessorFragment_OpenGL::GetPresentedImageData(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector &vDstData) { if(m_CanvasWidth == 0 || m_CanvasHeight == 0) { @@ -314,7 +314,7 @@ bool CCommandProcessorFragment_OpenGL::InitOpenGL(const SCommand_Init *pCommand) m_IsOpenGLES = pCommand->m_RequestedBackend == BACKEND_TYPE_OPENGL_ES; TGLBackendReadPresentedImageData &ReadPresentedImgDataFunc = *pCommand->m_pReadPresentedImageDataFunc; - ReadPresentedImgDataFunc = [this](uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector &vDstData) { return GetPresentedImageData(Width, Height, Format, vDstData); }; + ReadPresentedImgDataFunc = [this](uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector &vDstData) { return GetPresentedImageData(Width, Height, Format, vDstData); }; const char *pVendorString = (const char *)glGetString(GL_VENDOR); dbg_msg("opengl", "Vendor string: %s", pVendorString); @@ -644,7 +644,7 @@ void CCommandProcessorFragment_OpenGL::TextureUpdate(int Slot, int X, int Y, int int ResizedW = (int)(Width * ResizeW); int ResizedH = (int)(Height * ResizeH); - void *pTmpData = Resize(static_cast(pTexData), Width, Height, ResizedW, ResizedH, GLFormatToImageColorChannelCount(GLFormat)); + void *pTmpData = Resize(static_cast(pTexData), Width, Height, ResizedW, ResizedH, GLFormatToPixelSize(GLFormat)); free(pTexData); pTexData = pTmpData; @@ -666,7 +666,7 @@ void CCommandProcessorFragment_OpenGL::TextureUpdate(int Slot, int X, int Y, int Y /= 2; } - void *pTmpData = Resize(static_cast(pTexData), OldWidth, OldHeight, Width, Height, GLFormatToImageColorChannelCount(GLFormat)); + void *pTmpData = Resize(static_cast(pTexData), OldWidth, OldHeight, Width, Height, GLFormatToPixelSize(GLFormat)); free(pTexData); pTexData = pTmpData; } @@ -718,7 +718,7 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Destroy(const CCommandBuffer: DestroyTexture(pCommand->m_Slot); } -void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int Height, int PixelSize, int GLFormat, int GLStoreFormat, int Flags, void *pTexData) +void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int Height, int GLFormat, int GLStoreFormat, int Flags, void *pTexData) { #ifndef BACKEND_GL_MODERN_API @@ -741,7 +741,7 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He int PowerOfTwoHeight = HighestBit(Height); if(Width != PowerOfTwoWidth || Height != PowerOfTwoHeight) { - void *pTmpData = Resize(static_cast(pTexData), Width, Height, PowerOfTwoWidth, PowerOfTwoHeight, GLFormatToImageColorChannelCount(GLFormat)); + void *pTmpData = Resize(static_cast(pTexData), Width, Height, PowerOfTwoWidth, PowerOfTwoHeight, GLFormatToPixelSize(GLFormat)); free(pTexData); pTexData = pTmpData; @@ -773,7 +773,7 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He if(NeedsResize) { - void *pTmpData = Resize(static_cast(pTexData), OldWidth, OldHeight, Width, Height, GLFormatToImageColorChannelCount(GLFormat)); + void *pTmpData = Resize(static_cast(pTexData), OldWidth, OldHeight, Width, Height, GLFormatToPixelSize(GLFormat)); free(pTexData); pTexData = pTmpData; } @@ -782,8 +782,7 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He m_vTextures[Slot].m_Height = Height; m_vTextures[Slot].m_RescaleCount = RescaleCount; - int Oglformat = GLFormat; - int StoreOglformat = GLStoreFormat; + const size_t PixelSize = GLFormatToPixelSize(GLFormat); if((Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0) { @@ -797,7 +796,7 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, StoreOglformat, Width, Height, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); + glTexImage2D(GL_TEXTURE_2D, 0, GLStoreFormat, Width, Height, 0, GLFormat, GL_UNSIGNED_BYTE, pTexData); } } else @@ -813,7 +812,7 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f)); #endif - glTexImage2D(GL_TEXTURE_2D, 0, StoreOglformat, Width, Height, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); + glTexImage2D(GL_TEXTURE_2D, 0, GLStoreFormat, Width, Height, 0, GLFormat, GL_UNSIGNED_BYTE, pTexData); } int Flag2DArrayTexture = (CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE | CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER); @@ -881,14 +880,12 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He glBindSampler(0, 0); } - int ImageColorChannels = GLFormatToImageColorChannelCount(GLFormat); - uint8_t *p3DImageData = NULL; bool IsSingleLayer = (Flags & (CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER | CCommandBuffer::TEXFLAG_TO_3D_TEXTURE_SINGLE_LAYER)) != 0; if(!IsSingleLayer) - p3DImageData = (uint8_t *)malloc((size_t)ImageColorChannels * Width * Height); + p3DImageData = (uint8_t *)malloc((size_t)Width * Height * PixelSize); int Image3DWidth, Image3DHeight; int ConvertWidth = Width; @@ -901,7 +898,7 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He dbg_msg("gfx", "3D/2D array texture was resized"); int NewWidth = maximum(HighestBit(ConvertWidth), 16); int NewHeight = maximum(HighestBit(ConvertHeight), 16); - uint8_t *pNewTexData = (uint8_t *)Resize((const uint8_t *)pTexData, ConvertWidth, ConvertHeight, NewWidth, NewHeight, GLFormatToImageColorChannelCount(GLFormat)); + uint8_t *pNewTexData = (uint8_t *)Resize((const uint8_t *)pTexData, ConvertWidth, ConvertHeight, NewWidth, NewHeight, GLFormatToPixelSize(GLFormat)); ConvertWidth = NewWidth; ConvertHeight = NewHeight; @@ -911,15 +908,15 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He } } - if(IsSingleLayer || (Texture2DTo3D(pTexData, ConvertWidth, ConvertHeight, ImageColorChannels, 16, 16, p3DImageData, Image3DWidth, Image3DHeight))) + if(IsSingleLayer || (Texture2DTo3D(pTexData, ConvertWidth, ConvertHeight, PixelSize, 16, 16, p3DImageData, Image3DWidth, Image3DHeight))) { if(IsSingleLayer) { - glTexImage3D(Target, 0, StoreOglformat, ConvertWidth, ConvertHeight, 1, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); + glTexImage3D(Target, 0, GLStoreFormat, ConvertWidth, ConvertHeight, 1, 0, GLFormat, GL_UNSIGNED_BYTE, pTexData); } else { - glTexImage3D(Target, 0, StoreOglformat, Image3DWidth, Image3DHeight, 256, 0, Oglformat, GL_UNSIGNED_BYTE, p3DImageData); + glTexImage3D(Target, 0, GLStoreFormat, Image3DWidth, Image3DHeight, 256, 0, GLFormat, GL_UNSIGNED_BYTE, p3DImageData); } } @@ -932,12 +929,12 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He m_vTextures[Slot].m_LastWrapMode = CCommandBuffer::WRAP_REPEAT; // calculate memory usage - m_vTextures[Slot].m_MemSize = Width * Height * PixelSize; + m_vTextures[Slot].m_MemSize = (size_t)Width * Height * PixelSize; while(Width > 2 && Height > 2) { Width >>= 1; Height >>= 1; - m_vTextures[Slot].m_MemSize += Width * Height * PixelSize; + m_vTextures[Slot].m_MemSize += (size_t)Width * Height * PixelSize; } m_pTextureMemoryUsage->store(m_pTextureMemoryUsage->load(std::memory_order_relaxed) + m_vTextures[Slot].m_MemSize, std::memory_order_relaxed); @@ -947,7 +944,7 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand) { - TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, pCommand->m_PixelSize, TexFormatToOpenGLFormat(pCommand->m_Format), TexFormatToOpenGLFormat(pCommand->m_StoreFormat), pCommand->m_Flags, pCommand->m_pData); + TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, TexFormatToOpenGLFormat(pCommand->m_Format), TexFormatToOpenGLFormat(pCommand->m_StoreFormat), pCommand->m_Flags, pCommand->m_pData); } void CCommandProcessorFragment_OpenGL::Cmd_TextTexture_Update(const CCommandBuffer::SCommand_TextTexture_Update *pCommand) @@ -963,10 +960,8 @@ void CCommandProcessorFragment_OpenGL::Cmd_TextTextures_Destroy(const CCommandBu void CCommandProcessorFragment_OpenGL::Cmd_TextTextures_Create(const CCommandBuffer::SCommand_TextTextures_Create *pCommand) { - void *pTextData = pCommand->m_pTextData; - void *pTextOutlineData = pCommand->m_pTextOutlineData; - TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, 1, GL_ALPHA, GL_ALPHA, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTextData); - TextureCreate(pCommand->m_SlotOutline, pCommand->m_Width, pCommand->m_Height, 1, GL_ALPHA, GL_ALPHA, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTextOutlineData); + TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, GL_ALPHA, GL_ALPHA, CCommandBuffer::TEXFLAG_NOMIPMAPS, pCommand->m_pTextData); + TextureCreate(pCommand->m_SlotOutline, pCommand->m_Width, pCommand->m_Height, GL_ALPHA, GL_ALPHA, CCommandBuffer::TEXFLAG_NOMIPMAPS, pCommand->m_pTextOutlineData); } void CCommandProcessorFragment_OpenGL::Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand) diff --git a/src/engine/client/backend/opengl/backend_opengl.h b/src/engine/client/backend/opengl/backend_opengl.h index b841d4289..3b2c844b3 100644 --- a/src/engine/client/backend/opengl/backend_opengl.h +++ b/src/engine/client/backend/opengl/backend_opengl.h @@ -80,13 +80,13 @@ protected: virtual bool IsNewApi() { return false; } void DestroyTexture(int Slot); - bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector &vDstData) override; + bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector &vDstData) override; static int TexFormatToOpenGLFormat(int TexFormat); - static size_t GLFormatToImageColorChannelCount(int GLFormat); + static size_t GLFormatToPixelSize(int GLFormat); void TextureUpdate(int Slot, int X, int Y, int Width, int Height, int GLFormat, void *pTexData); - void TextureCreate(int Slot, int Width, int Height, int PixelSize, int GLFormat, int GLStoreFormat, int Flags, void *pTexData); + void TextureCreate(int Slot, int Width, int Height, int GLFormat, int GLStoreFormat, int Flags, void *pTexData); virtual bool Cmd_Init(const SCommand_Init *pCommand); virtual void Cmd_Shutdown(const SCommand_Shutdown *pCommand) {} diff --git a/src/engine/client/backend/opengl/backend_opengl3.cpp b/src/engine/client/backend/opengl/backend_opengl3.cpp index 46b9185d4..f42da3802 100644 --- a/src/engine/client/backend/opengl/backend_opengl3.cpp +++ b/src/engine/client/backend/opengl/backend_opengl3.cpp @@ -555,7 +555,7 @@ void CCommandProcessorFragment_OpenGL3_3::TextureUpdate(int Slot, int X, int Y, Y /= 2; } - void *pTmpData = Resize(static_cast(pTexData), Width, Height, Width, Height, GLFormatToImageColorChannelCount(GLFormat)); + void *pTmpData = Resize(static_cast(pTexData), Width, Height, Width, Height, GLFormatToPixelSize(GLFormat)); free(pTexData); pTexData = pTmpData; } @@ -577,7 +577,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Destroy(const CCommandBuff DestroyTexture(pCommand->m_Slot); } -void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int Height, int PixelSize, int GLFormat, int GLStoreFormat, int Flags, void *pTexData) +void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int Height, int GLFormat, int GLStoreFormat, int Flags, void *pTexData) { if(Slot >= (int)m_vTextures.size()) m_vTextures.resize(m_vTextures.size() * 2); @@ -595,7 +595,7 @@ void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int ++RescaleCount; } while(Width > m_MaxTexSize || Height > m_MaxTexSize); - void *pTmpData = Resize(static_cast(pTexData), Width, Height, Width, Height, GLFormatToImageColorChannelCount(GLFormat)); + void *pTmpData = Resize(static_cast(pTexData), Width, Height, Width, Height, GLFormatToPixelSize(GLFormat)); free(pTexData); pTexData = pTmpData; } @@ -604,10 +604,9 @@ void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int m_vTextures[Slot].m_Height = Height; m_vTextures[Slot].m_RescaleCount = RescaleCount; - int Oglformat = GLFormat; - int StoreOglformat = GLStoreFormat; - if(StoreOglformat == GL_RED) - StoreOglformat = GL_R8; + if(GLStoreFormat == GL_RED) + GLStoreFormat = GL_R8; + const size_t PixelSize = GLFormatToPixelSize(GLFormat); int SamplerSlot = 0; @@ -628,7 +627,7 @@ void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(m_vTextures[Slot].m_Sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glSamplerParameteri(m_vTextures[Slot].m_Sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, StoreOglformat, Width, Height, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); + glTexImage2D(GL_TEXTURE_2D, 0, GLStoreFormat, Width, Height, 0, GLFormat, GL_UNSIGNED_BYTE, pTexData); } } else @@ -649,7 +648,7 @@ void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 5.f); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 5); } - glTexImage2D(GL_TEXTURE_2D, 0, StoreOglformat, Width, Height, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); + glTexImage2D(GL_TEXTURE_2D, 0, GLStoreFormat, Width, Height, 0, GLFormat, GL_UNSIGNED_BYTE, pTexData); glGenerateMipmap(GL_TEXTURE_2D); } @@ -671,14 +670,12 @@ void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int glSamplerParameterf(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_LOD_BIAS, ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f)); #endif - int ImageColorChannels = GLFormatToImageColorChannelCount(GLFormat); - uint8_t *p3DImageData = NULL; bool IsSingleLayer = (Flags & CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER) != 0; if(!IsSingleLayer) - p3DImageData = (uint8_t *)malloc((size_t)ImageColorChannels * Width * Height); + p3DImageData = (uint8_t *)malloc((size_t)Width * Height * PixelSize); int Image3DWidth, Image3DHeight; int ConvertWidth = Width; @@ -691,7 +688,7 @@ void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int dbg_msg("gfx", "3D/2D array texture was resized"); int NewWidth = maximum(HighestBit(ConvertWidth), 16); int NewHeight = maximum(HighestBit(ConvertHeight), 16); - uint8_t *pNewTexData = (uint8_t *)Resize((const uint8_t *)pTexData, ConvertWidth, ConvertHeight, NewWidth, NewHeight, GLFormatToImageColorChannelCount(GLFormat)); + uint8_t *pNewTexData = (uint8_t *)Resize((const uint8_t *)pTexData, ConvertWidth, ConvertHeight, NewWidth, NewHeight, GLFormatToPixelSize(GLFormat)); ConvertWidth = NewWidth; ConvertHeight = NewHeight; @@ -701,15 +698,15 @@ void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int } } - if(IsSingleLayer || (Texture2DTo3D(pTexData, ConvertWidth, ConvertHeight, ImageColorChannels, 16, 16, p3DImageData, Image3DWidth, Image3DHeight))) + if(IsSingleLayer || (Texture2DTo3D(pTexData, ConvertWidth, ConvertHeight, PixelSize, 16, 16, p3DImageData, Image3DWidth, Image3DHeight))) { if(IsSingleLayer) { - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, StoreOglformat, ConvertWidth, ConvertHeight, 1, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GLStoreFormat, ConvertWidth, ConvertHeight, 1, 0, GLFormat, GL_UNSIGNED_BYTE, pTexData); } else { - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, StoreOglformat, Image3DWidth, Image3DHeight, 256, 0, Oglformat, GL_UNSIGNED_BYTE, p3DImageData); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GLStoreFormat, Image3DWidth, Image3DHeight, 256, 0, GLFormat, GL_UNSIGNED_BYTE, p3DImageData); } glGenerateMipmap(GL_TEXTURE_2D_ARRAY); } @@ -723,12 +720,12 @@ void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int m_vTextures[Slot].m_LastWrapMode = CCommandBuffer::WRAP_REPEAT; // calculate memory usage - m_vTextures[Slot].m_MemSize = Width * Height * PixelSize; + m_vTextures[Slot].m_MemSize = (size_t)Width * Height * PixelSize; while(Width > 2 && Height > 2) { Width >>= 1; Height >>= 1; - m_vTextures[Slot].m_MemSize += Width * Height * PixelSize; + m_vTextures[Slot].m_MemSize += (size_t)Width * Height * PixelSize; } m_pTextureMemoryUsage->store(m_pTextureMemoryUsage->load(std::memory_order_relaxed) + m_vTextures[Slot].m_MemSize, std::memory_order_relaxed); @@ -737,7 +734,7 @@ void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand) { - TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, pCommand->m_PixelSize, TexFormatToOpenGLFormat(pCommand->m_Format), TexFormatToOpenGLFormat(pCommand->m_StoreFormat), pCommand->m_Flags, pCommand->m_pData); + TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, TexFormatToOpenGLFormat(pCommand->m_Format), TexFormatToOpenGLFormat(pCommand->m_StoreFormat), pCommand->m_Flags, pCommand->m_pData); } void CCommandProcessorFragment_OpenGL3_3::Cmd_TextTexture_Update(const CCommandBuffer::SCommand_TextTexture_Update *pCommand) @@ -753,10 +750,8 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_TextTextures_Destroy(const CComman void CCommandProcessorFragment_OpenGL3_3::Cmd_TextTextures_Create(const CCommandBuffer::SCommand_TextTextures_Create *pCommand) { - void *pTextData = pCommand->m_pTextData; - void *pTextOutlineData = pCommand->m_pTextOutlineData; - TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, 1, GL_RED, GL_RED, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTextData); - TextureCreate(pCommand->m_SlotOutline, pCommand->m_Width, pCommand->m_Height, 1, GL_RED, GL_RED, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTextOutlineData); + TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, GL_RED, GL_RED, CCommandBuffer::TEXFLAG_NOMIPMAPS, pCommand->m_pTextData); + TextureCreate(pCommand->m_SlotOutline, pCommand->m_Width, pCommand->m_Height, GL_RED, GL_RED, CCommandBuffer::TEXFLAG_NOMIPMAPS, pCommand->m_pTextOutlineData); } void CCommandProcessorFragment_OpenGL3_3::Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand) diff --git a/src/engine/client/backend/opengl/backend_opengl3.h b/src/engine/client/backend/opengl/backend_opengl3.h index 1c56a0e20..4f839564d 100644 --- a/src/engine/client/backend/opengl/backend_opengl3.h +++ b/src/engine/client/backend/opengl/backend_opengl3.h @@ -83,7 +83,7 @@ protected: void RenderText(const CCommandBuffer::SState &State, int DrawNum, int TextTextureIndex, int TextOutlineTextureIndex, int TextureSize, const ColorRGBA &TextColor, const ColorRGBA &TextOutlineColor); void TextureUpdate(int Slot, int X, int Y, int Width, int Height, int GLFormat, void *pTexData); - void TextureCreate(int Slot, int Width, int Height, int PixelSize, int GLFormat, int GLStoreFormat, int Flags, void *pTexData); + void TextureCreate(int Slot, int Width, int Height, int GLFormat, int GLStoreFormat, int Flags, void *pTexData); bool Cmd_Init(const SCommand_Init *pCommand) override; void Cmd_Shutdown(const SCommand_Shutdown *pCommand) override; diff --git a/src/engine/client/backend/vulkan/backend_vulkan.cpp b/src/engine/client/backend/vulkan/backend_vulkan.cpp index e5214b327..b1b3842ec 100644 --- a/src/engine/client/backend/vulkan/backend_vulkan.cpp +++ b/src/engine/client/backend/vulkan/backend_vulkan.cpp @@ -1385,7 +1385,7 @@ protected: } } - [[nodiscard]] bool GetPresentedImageDataImpl(uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector &vDstData, bool FlipImgData, bool ResetAlpha) + [[nodiscard]] bool GetPresentedImageDataImpl(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector &vDstData, bool FlipImgData, bool ResetAlpha) { bool IsB8G8R8A8 = m_VKSurfFormat.format == VK_FORMAT_B8G8R8A8_UNORM; bool UsesRGBALikeFormat = m_VKSurfFormat.format == VK_FORMAT_R8G8B8A8_UNORM || IsB8G8R8A8; @@ -1396,7 +1396,7 @@ protected: Height = Viewport.height; Format = CImageInfo::FORMAT_RGBA; - size_t ImageTotalSize = (size_t)Width * Height * 4; + const size_t ImageTotalSize = (size_t)Width * Height * CImageInfo::PixelSize(Format); uint8_t *pResImageData; if(!PreparePresentedImageDataImage(pResImageData, Width, Height)) @@ -1552,7 +1552,7 @@ protected: } } - [[nodiscard]] bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector &vDstData) override + [[nodiscard]] bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector &vDstData) override { return GetPresentedImageDataImpl(Width, Height, Format, vDstData, false, false); } @@ -2509,7 +2509,7 @@ protected: * TEXTURES ************************/ - size_t VulkanFormatToImageColorChannelCount(VkFormat Format) + size_t VulkanFormatToPixelSize(VkFormat Format) { if(Format == VK_FORMAT_R8G8B8_UNORM) return 3; @@ -2520,9 +2520,9 @@ protected: return 4; } - [[nodiscard]] bool UpdateTexture(size_t TextureSlot, VkFormat Format, void *&pData, int64_t XOff, int64_t YOff, size_t Width, size_t Height, size_t ColorChannelCount) + [[nodiscard]] bool UpdateTexture(size_t TextureSlot, VkFormat Format, void *&pData, int64_t XOff, int64_t YOff, size_t Width, size_t Height) { - size_t ImageSize = Width * Height * ColorChannelCount; + const size_t ImageSize = Width * Height * VulkanFormatToPixelSize(Format); SMemoryBlock StagingBuffer; if(!GetStagingBufferImage(StagingBuffer, pData, ImageSize)) return false; @@ -2540,7 +2540,7 @@ protected: YOff /= 2; } - void *pTmpData = Resize((const uint8_t *)pData, Width, Height, Width, Height, VulkanFormatToImageColorChannelCount(Format)); + void *pTmpData = Resize((const uint8_t *)pData, Width, Height, Width, Height, VulkanFormatToPixelSize(Format)); free(pData); pData = pTmpData; } @@ -2570,14 +2570,13 @@ protected: int Slot, int Width, int Height, - int PixelSize, VkFormat Format, VkFormat StoreFormat, int Flags, void *&pData) { size_t ImageIndex = (size_t)Slot; - int ImageColorChannels = VulkanFormatToImageColorChannelCount(Format); + const size_t PixelSize = VulkanFormatToPixelSize(Format); while(ImageIndex >= m_vTextures.size()) { @@ -2595,7 +2594,7 @@ protected: ++RescaleCount; } while((size_t)Width > m_MaxTextureSize || (size_t)Height > m_MaxTextureSize); - void *pTmpData = Resize((const uint8_t *)(pData), Width, Height, Width, Height, ImageColorChannels); + void *pTmpData = Resize((const uint8_t *)(pData), Width, Height, Width, Height, PixelSize); free(pData); pData = pTmpData; } @@ -2653,7 +2652,7 @@ protected: dbg_msg("vulkan", "3D/2D array texture was resized"); int NewWidth = maximum(HighestBit(ConvertWidth), 16); int NewHeight = maximum(HighestBit(ConvertHeight), 16); - uint8_t *pNewTexData = (uint8_t *)Resize((const uint8_t *)pData, ConvertWidth, ConvertHeight, NewWidth, NewHeight, ImageColorChannels); + uint8_t *pNewTexData = (uint8_t *)Resize((const uint8_t *)pData, ConvertWidth, ConvertHeight, NewWidth, NewHeight, PixelSize); ConvertWidth = NewWidth; ConvertHeight = NewHeight; @@ -2667,8 +2666,8 @@ protected: bool Needs3DTexDel = false; if(!Is2DTextureSingleLayer) { - p3DTexData = malloc((size_t)ImageColorChannels * ConvertWidth * ConvertHeight); - if(!Texture2DTo3D(pData, ConvertWidth, ConvertHeight, ImageColorChannels, 16, 16, p3DTexData, Image3DWidth, Image3DHeight)) + p3DTexData = malloc((size_t)PixelSize * ConvertWidth * ConvertHeight); + if(!Texture2DTo3D(pData, ConvertWidth, ConvertHeight, PixelSize, 16, 16, p3DTexData, Image3DWidth, Image3DHeight)) { free(p3DTexData); p3DTexData = nullptr; @@ -6581,7 +6580,7 @@ public: m_MultiSamplingCount = (g_Config.m_GfxFsaaSamples & 0xFFFFFFFE); // ignore the uneven bit, only even multi sampling works TGLBackendReadPresentedImageData &ReadPresentedImgDataFunc = *pCommand->m_pReadPresentedImageDataFunc; - ReadPresentedImgDataFunc = [this](uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector &vDstData) { return GetPresentedImageData(Width, Height, Format, vDstData); }; + ReadPresentedImgDataFunc = [this](uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector &vDstData) { return GetPresentedImageData(Width, Height, Format, vDstData); }; m_pWindow = pCommand->m_pWindow; @@ -6656,7 +6655,7 @@ public: void *pData = pCommand->m_pData; - if(!UpdateTexture(IndexTex, VK_FORMAT_B8G8R8A8_UNORM, pData, pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height, TexFormatToImageColorChannelCount(pCommand->m_Format))) + if(!UpdateTexture(IndexTex, VK_FORMAT_B8G8R8A8_UNORM, pData, pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height)) return false; free(pData); @@ -6681,13 +6680,12 @@ public: int Slot = pCommand->m_Slot; int Width = pCommand->m_Width; int Height = pCommand->m_Height; - int PixelSize = pCommand->m_PixelSize; int Format = pCommand->m_Format; int StoreFormat = pCommand->m_StoreFormat; int Flags = pCommand->m_Flags; void *pData = pCommand->m_pData; - if(!CreateTextureCMD(Slot, Width, Height, PixelSize, TextureFormatToVulkanFormat(Format), TextureFormatToVulkanFormat(StoreFormat), Flags, pData)) + if(!CreateTextureCMD(Slot, Width, Height, TextureFormatToVulkanFormat(Format), TextureFormatToVulkanFormat(StoreFormat), Flags, pData)) return false; free(pData); @@ -6705,9 +6703,9 @@ public: void *pTmpData = pCommand->m_pTextData; void *pTmpData2 = pCommand->m_pTextOutlineData; - if(!CreateTextureCMD(Slot, Width, Height, 1, VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTmpData)) + if(!CreateTextureCMD(Slot, Width, Height, VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTmpData)) return false; - if(!CreateTextureCMD(SlotOutline, Width, Height, 1, VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTmpData2)) + if(!CreateTextureCMD(SlotOutline, Width, Height, VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTmpData2)) return false; if(!CreateNewTextDescriptorSets(Slot, SlotOutline)) @@ -6740,7 +6738,7 @@ public: void *pData = pCommand->m_pData; - if(!UpdateTexture(IndexTex, VK_FORMAT_R8_UNORM, pData, pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height, 1)) + if(!UpdateTexture(IndexTex, VK_FORMAT_R8_UNORM, pData, pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height)) return false; free(pData); @@ -6814,7 +6812,7 @@ public: uint32_t Width; uint32_t Height; - uint32_t Format; + CImageInfo::EImageFormat Format; if(GetPresentedImageDataImpl(Width, Height, Format, m_vScreenshotHelper, false, true)) { size_t ImgSize = (size_t)Width * (size_t)Height * (size_t)4; @@ -6827,7 +6825,7 @@ public: } pCommand->m_pImage->m_Width = (int)Width; pCommand->m_pImage->m_Height = (int)Height; - pCommand->m_pImage->m_Format = (int)Format; + pCommand->m_pImage->m_Format = Format; return true; } diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 9dbf35d43..4364290a5 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -974,7 +974,7 @@ void CClient::ServerInfoRequest() void CClient::LoadDebugFont() { - m_DebugFont = Graphics()->LoadTexture("debug_font.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0); + m_DebugFont = Graphics()->LoadTexture("debug_font.png", IStorage::TYPE_ALL); } // --- diff --git a/src/engine/client/graphics_threaded.cpp b/src/engine/client/graphics_threaded.cpp index cad8e8b9c..474c593a8 100644 --- a/src/engine/client/graphics_threaded.cpp +++ b/src/engine/client/graphics_threaded.cpp @@ -305,27 +305,17 @@ int CGraphics_Threaded::UnloadTexture(CTextureHandle *pIndex) return 0; } -static int ImageFormatToPixelSize(int Format) -{ - switch(Format) - { - case CImageInfo::FORMAT_RGB: return 3; - case CImageInfo::FORMAT_SINGLE_COMPONENT: return 1; - default: return 4; - } -} - -static bool ConvertToRGBA(uint8_t *pDest, const uint8_t *pSrc, size_t SrcWidth, size_t SrcHeight, int SrcFormat) +static bool ConvertToRGBA(uint8_t *pDest, const uint8_t *pSrc, size_t SrcWidth, size_t SrcHeight, CImageInfo::EImageFormat SrcFormat) { if(SrcFormat == CImageInfo::FORMAT_RGBA) { - mem_copy(pDest, pSrc, SrcWidth * SrcHeight * 4); + mem_copy(pDest, pSrc, SrcWidth * SrcHeight * CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA)); return true; } else { - size_t SrcChannelCount = ImageFormatToPixelSize(SrcFormat); - size_t DstChannelCount = 4; + const size_t SrcChannelCount = CImageInfo::PixelSize(SrcFormat); + const size_t DstChannelCount = CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA); for(size_t Y = 0; Y < SrcHeight; ++Y) { for(size_t X = 0; X < SrcWidth; ++X) @@ -333,12 +323,12 @@ static bool ConvertToRGBA(uint8_t *pDest, const uint8_t *pSrc, size_t SrcWidth, size_t ImgOffsetSrc = (Y * SrcWidth * SrcChannelCount) + (X * SrcChannelCount); size_t ImgOffsetDest = (Y * SrcWidth * DstChannelCount) + (X * DstChannelCount); size_t CopySize = SrcChannelCount; - if(SrcChannelCount == 3) + if(SrcFormat == CImageInfo::FORMAT_RGB) { mem_copy(&pDest[ImgOffsetDest], &pSrc[ImgOffsetSrc], CopySize); pDest[ImgOffsetDest + 3] = 255; } - else if(SrcChannelCount == 1) + else if(SrcFormat == CImageInfo::FORMAT_SINGLE_COMPONENT) { pDest[ImgOffsetDest + 0] = 255; pDest[ImgOffsetDest + 1] = 255; @@ -351,7 +341,7 @@ static bool ConvertToRGBA(uint8_t *pDest, const uint8_t *pSrc, size_t SrcWidth, } } -int CGraphics_Threaded::LoadTextureRawSub(CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, int Format, const void *pData) +int CGraphics_Threaded::LoadTextureRawSub(CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData) { dbg_assert(TextureID.IsValid(), "Invalid texture handle used with LoadTextureRawSub."); @@ -364,7 +354,7 @@ int CGraphics_Threaded::LoadTextureRawSub(CTextureHandle TextureID, int x, int y Cmd.m_Format = CCommandBuffer::TEXFORMAT_RGBA; // calculate memory usage - const size_t MemSize = Width * Height * 4; + const size_t MemSize = Width * Height * CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA); // copy texture data void *pTmpData = malloc(MemSize); @@ -377,7 +367,7 @@ int CGraphics_Threaded::LoadTextureRawSub(CTextureHandle TextureID, int x, int y IGraphics::CTextureHandle CGraphics_Threaded::LoadSpriteTextureImpl(CImageInfo &FromImageInfo, int x, int y, size_t w, size_t h) { - const size_t PixelSize = ImageFormatToPixelSize(FromImageInfo.m_Format); + const size_t PixelSize = FromImageInfo.PixelSize(); m_vSpriteHelper.resize(w * h * PixelSize); CopyTextureFromTextureBufferSub(m_vSpriteHelper.data(), w, h, (uint8_t *)FromImageInfo.m_pData, FromImageInfo.m_Width, FromImageInfo.m_Height, PixelSize, x, y, w, h); return LoadTextureRaw(w, h, FromImageInfo.m_Format, m_vSpriteHelper.data(), FromImageInfo.m_Format, 0); @@ -410,13 +400,13 @@ bool CGraphics_Threaded::IsImageSubFullyTransparent(CImageInfo &FromImageInfo, i if(FromImageInfo.m_Format == CImageInfo::FORMAT_SINGLE_COMPONENT || FromImageInfo.m_Format == CImageInfo::FORMAT_RGBA) { uint8_t *pImgData = (uint8_t *)FromImageInfo.m_pData; - int bpp = ImageFormatToPixelSize(FromImageInfo.m_Format); + const size_t PixelSize = FromImageInfo.PixelSize(); for(int iy = 0; iy < h; ++iy) { for(int ix = 0; ix < w; ++ix) { - int RealOffset = (x + ix) * bpp + (y + iy) * bpp * FromImageInfo.m_Width; - if(pImgData[RealOffset + (bpp - 1)] > 0) + const size_t RealOffset = (x + ix) * PixelSize + (y + iy) * PixelSize * FromImageInfo.m_Width; + if(pImgData[RealOffset + (PixelSize - 1)] > 0) return false; } } @@ -438,7 +428,7 @@ bool CGraphics_Threaded::IsSpriteTextureFullyTransparent(CImageInfo &FromImageIn return IsImageSubFullyTransparent(FromImageInfo, x, y, w, h); } -IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(size_t Width, size_t Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName) +IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData, int Flags, const char *pTexName) { // don't waste memory on texture if we are stress testing #ifdef CONF_DEBUG @@ -472,7 +462,6 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(size_t Width, size_ Cmd.m_Slot = TextureHandle.Id(); Cmd.m_Width = Width; Cmd.m_Height = Height; - Cmd.m_PixelSize = 4; Cmd.m_Format = CCommandBuffer::TEXFORMAT_RGBA; Cmd.m_StoreFormat = CCommandBuffer::TEXFORMAT_RGBA; @@ -492,7 +481,7 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(size_t Width, size_ Cmd.m_Flags |= CCommandBuffer::TEXFLAG_NO_2D_TEXTURE; // copy texture data - const size_t MemSize = Width * Height * Cmd.m_PixelSize; + const size_t MemSize = Width * Height * CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA); void *pTmpData = malloc(MemSize); if(!ConvertToRGBA((uint8_t *)pTmpData, (const uint8_t *)pData, Width, Height, Format)) { @@ -505,17 +494,14 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(size_t Width, size_ } // simple uncompressed RGBA loaders -IGraphics::CTextureHandle CGraphics_Threaded::LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags) +IGraphics::CTextureHandle CGraphics_Threaded::LoadTexture(const char *pFilename, int StorageType, int Flags) { dbg_assert(pFilename[0] != '\0', "Cannot load texture from file with empty filename"); // would cause Valgrind to crash otherwise CImageInfo Img; if(LoadPNG(&Img, pFilename, StorageType)) { - if(StoreFormat == CImageInfo::FORMAT_AUTO) - StoreFormat = Img.m_Format; - - IGraphics::CTextureHandle ID = LoadTextureRaw(Img.m_Width, Img.m_Height, Img.m_Format, Img.m_pData, StoreFormat, Flags, pFilename); + IGraphics::CTextureHandle ID = LoadTextureRaw(Img.m_Width, Img.m_Height, Img.m_Format, Img.m_pData, Flags, pFilename); free(Img.m_pData); if(ID.Id() != m_InvalidTexture.Id() && g_Config.m_Debug) dbg_msg("graphics/texture", "loaded %s", pFilename); @@ -696,15 +682,7 @@ bool CGraphics_Threaded::CheckImageDivisibility(const char *pFileName, CImageInf NewWidth = (NewHeight / DivY) * DivX; } - int ColorChannelCount = 4; - if(Img.m_Format == CImageInfo::FORMAT_SINGLE_COMPONENT) - ColorChannelCount = 1; - else if(Img.m_Format == CImageInfo::FORMAT_RGB) - ColorChannelCount = 3; - else if(Img.m_Format == CImageInfo::FORMAT_RGBA) - ColorChannelCount = 4; - - uint8_t *pNewImg = ResizeImage((uint8_t *)Img.m_pData, Img.m_Width, Img.m_Height, NewWidth, NewHeight, ColorChannelCount); + uint8_t *pNewImg = ResizeImage((uint8_t *)Img.m_pData, Img.m_Width, Img.m_Height, NewWidth, NewHeight, Img.PixelSize()); free(Img.m_pData); Img.m_pData = pNewImg; Img.m_Width = NewWidth; @@ -734,23 +712,23 @@ bool CGraphics_Threaded::IsImageFormatRGBA(const char *pFileName, CImageInfo &Im return true; } -void CGraphics_Threaded::CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, size_t FullWidth, size_t FullHeight, size_t ColorChannelCount, size_t SubOffsetX, size_t SubOffsetY, size_t SubCopyWidth, size_t SubCopyHeight) +void CGraphics_Threaded::CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, size_t FullWidth, size_t FullHeight, size_t PixelSize, size_t SubOffsetX, size_t SubOffsetY, size_t SubCopyWidth, size_t SubCopyHeight) { for(size_t Y = 0; Y < SubCopyHeight; ++Y) { - const size_t ImgOffset = ((SubOffsetY + Y) * FullWidth * ColorChannelCount) + (SubOffsetX * ColorChannelCount); - const size_t CopySize = SubCopyWidth * ColorChannelCount; + const size_t ImgOffset = ((SubOffsetY + Y) * FullWidth * PixelSize) + (SubOffsetX * PixelSize); + const size_t CopySize = SubCopyWidth * PixelSize; mem_copy(&pDestBuffer[ImgOffset], &pSourceBuffer[ImgOffset], CopySize); } } -void CGraphics_Threaded::CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, size_t DestWidth, size_t DestHeight, uint8_t *pSourceBuffer, size_t SrcWidth, size_t SrcHeight, size_t ColorChannelCount, size_t SrcSubOffsetX, size_t SrcSubOffsetY, size_t SrcSubCopyWidth, size_t SrcSubCopyHeight) +void CGraphics_Threaded::CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, size_t DestWidth, size_t DestHeight, uint8_t *pSourceBuffer, size_t SrcWidth, size_t SrcHeight, size_t PixelSize, size_t SrcSubOffsetX, size_t SrcSubOffsetY, size_t SrcSubCopyWidth, size_t SrcSubCopyHeight) { for(size_t Y = 0; Y < SrcSubCopyHeight; ++Y) { - const size_t SrcImgOffset = ((SrcSubOffsetY + Y) * SrcWidth * ColorChannelCount) + (SrcSubOffsetX * ColorChannelCount); - const size_t DstImgOffset = (Y * DestWidth * ColorChannelCount); - const size_t CopySize = SrcSubCopyWidth * ColorChannelCount; + const size_t SrcImgOffset = ((SrcSubOffsetY + Y) * SrcWidth * PixelSize) + (SrcSubOffsetX * PixelSize); + const size_t DstImgOffset = (Y * DestWidth * PixelSize); + const size_t CopySize = SrcSubCopyWidth * PixelSize; mem_copy(&pDestBuffer[DstImgOffset], &pSourceBuffer[SrcImgOffset], CopySize); } } @@ -2671,7 +2649,7 @@ int CGraphics_Threaded::Init() } const int TextureLoadFlags = HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; m_InvalidTexture.Invalidate(); - m_InvalidTexture = LoadTextureRaw(InvalidTextureDimension, InvalidTextureDimension, CImageInfo::FORMAT_RGBA, aNullTextureData, CImageInfo::FORMAT_RGBA, TextureLoadFlags); + m_InvalidTexture = LoadTextureRaw(InvalidTextureDimension, InvalidTextureDimension, CImageInfo::FORMAT_RGBA, aNullTextureData, TextureLoadFlags); } ColorRGBA GPUInfoPrintColor{0.6f, 0.5f, 1.0f, 1.0f}; diff --git a/src/engine/client/graphics_threaded.h b/src/engine/client/graphics_threaded.h index ed2ee85b8..7a3932684 100644 --- a/src/engine/client/graphics_threaded.h +++ b/src/engine/client/graphics_threaded.h @@ -535,7 +535,6 @@ public: size_t m_Width; size_t m_Height; - int m_PixelSize; int m_Format; int m_StoreFormat; int m_Flags; @@ -967,8 +966,8 @@ public: IGraphics::CTextureHandle FindFreeTextureIndex(); void FreeTextureIndex(CTextureHandle *pIndex); int UnloadTexture(IGraphics::CTextureHandle *pIndex) override; - IGraphics::CTextureHandle LoadTextureRaw(size_t Width, size_t Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName = NULL) override; - int LoadTextureRawSub(IGraphics::CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, int Format, const void *pData) override; + IGraphics::CTextureHandle LoadTextureRaw(size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData, int Flags, const char *pTexName = nullptr) override; + int LoadTextureRawSub(IGraphics::CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData) override; IGraphics::CTextureHandle InvalidTexture() const override; bool LoadTextTextures(size_t Width, size_t Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, void *pTextData, void *pTextOutlineData) override; @@ -983,15 +982,15 @@ public: bool IsSpriteTextureFullyTransparent(CImageInfo &FromImageInfo, struct client_data7::CDataSprite *pSprite) override; // simple uncompressed RGBA loaders - IGraphics::CTextureHandle LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags) override; + IGraphics::CTextureHandle LoadTexture(const char *pFilename, int StorageType, int Flags = 0) override; int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType) override; void FreePNG(CImageInfo *pImg) override; bool CheckImageDivisibility(const char *pFileName, CImageInfo &Img, int DivX, int DivY, bool AllowResize) override; bool IsImageFormatRGBA(const char *pFileName, CImageInfo &Img) override; - void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, size_t FullWidth, size_t FullHeight, size_t ColorChannelCount, size_t SubOffsetX, size_t SubOffsetY, size_t SubCopyWidth, size_t SubCopyHeight) override; - void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, size_t DestWidth, size_t DestHeight, uint8_t *pSourceBuffer, size_t SrcWidth, size_t SrcHeight, size_t ColorChannelCount, size_t SrcSubOffsetX, size_t SrcSubOffsetY, size_t SrcSubCopyWidth, size_t SrcSubCopyHeight) override; + void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, size_t FullWidth, size_t FullHeight, size_t PixelSize, size_t SubOffsetX, size_t SubOffsetY, size_t SubCopyWidth, size_t SubCopyHeight) override; + void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, size_t DestWidth, size_t DestHeight, uint8_t *pSourceBuffer, size_t SrcWidth, size_t SrcHeight, size_t PixelSize, size_t SrcSubOffsetX, size_t SrcSubOffsetY, size_t SrcSubCopyWidth, size_t SrcSubCopyHeight) override; bool ScreenshotDirect(); diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp index cf948df4f..817db2976 100644 --- a/src/engine/client/text.cpp +++ b/src/engine/client/text.cpp @@ -729,7 +729,7 @@ public: return vec2(0.0f, 0.0f); } - void UploadEntityLayerText(void *pTexBuff, size_t ImageColorChannelCount, int TexWidth, int TexHeight, int TexSubWidth, int TexSubHeight, const char *pText, int Length, float x, float y, int FontSize) + void UploadEntityLayerText(void *pTexBuff, size_t PixelSize, size_t TexWidth, size_t TexHeight, int TexSubWidth, int TexSubHeight, const char *pText, int Length, float x, float y, int FontSize) { if(FontSize < 1) return; @@ -777,11 +777,11 @@ public: { const int ImgOffX = clamp(x + OffX + WidthLastChars, x, (x + TexSubWidth) - 1); const int ImgOffY = clamp(y + OffY, y, (y + TexSubHeight) - 1); - const size_t ImageOffset = ImgOffY * (TexWidth * ImageColorChannelCount) + ImgOffX * ImageColorChannelCount; + const size_t ImageOffset = ImgOffY * (TexWidth * PixelSize) + ImgOffX * PixelSize; const size_t GlyphOffset = OffY * pBitmap->width + OffX; - for(size_t i = 0; i < ImageColorChannelCount; ++i) + for(size_t i = 0; i < PixelSize; ++i) { - if(i != ImageColorChannelCount - 1) + if(i != PixelSize - 1) { *(pImageBuff + ImageOffset + i) = 255; } @@ -2139,9 +2139,9 @@ public: return TextContainer.m_BoundingBox; } - void UploadEntityLayerText(void *pTexBuff, size_t ImageColorChannelCount, int TexWidth, int TexHeight, int TexSubWidth, int TexSubHeight, const char *pText, int Length, float x, float y, int FontSize) override + void UploadEntityLayerText(void *pTexBuff, size_t PixelSize, size_t TexWidth, size_t TexHeight, int TexSubWidth, int TexSubHeight, const char *pText, int Length, float x, float y, int FontSize) override { - m_pGlyphMap->UploadEntityLayerText(pTexBuff, ImageColorChannelCount, TexWidth, TexHeight, TexSubWidth, TexSubHeight, pText, Length, x, y, FontSize); + m_pGlyphMap->UploadEntityLayerText(pTexBuff, PixelSize, TexWidth, TexHeight, TexSubWidth, TexSubHeight, pText, Length, x, y, FontSize); } int AdjustFontSize(const char *pText, int TextLength, int MaxSize, int MaxWidth) const override diff --git a/src/engine/client/video.cpp b/src/engine/client/video.cpp index 0db67bd08..8fe839a8f 100644 --- a/src/engine/client/video.cpp +++ b/src/engine/client/video.cpp @@ -585,7 +585,7 @@ void CVideo::ReadRGBFromGL(size_t ThreadIndex) { uint32_t Width; uint32_t Height; - uint32_t Format; + CImageInfo::EImageFormat Format; m_pGraphics->GetReadPresentedImageDataFuncUnsafe()(Width, Height, Format, m_vPixelHelper[ThreadIndex]); } diff --git a/src/engine/graphics.h b/src/engine/graphics.h index 5ed0fcab2..d73800974 100644 --- a/src/engine/graphics.h +++ b/src/engine/graphics.h @@ -66,29 +66,54 @@ struct SGraphicTileTexureCoords class CImageInfo { public: - enum + enum EImageFormat { - FORMAT_AUTO = -1, + FORMAT_ERROR = -1, FORMAT_RGB = 0, FORMAT_RGBA = 1, FORMAT_SINGLE_COMPONENT = 2, }; - /* Variable: width - Contains the width of the image */ + /** + * Contains the width of the image + */ int m_Width = 0; - /* Variable: height - Contains the height of the image */ + /** + * Contains the height of the image + */ int m_Height = 0; - /* Variable: format - Contains the format of the image. See for more information. */ - int m_Format = FORMAT_RGB; + /** + * Contains the format of the image. + * + * @see EImageFormat + */ + EImageFormat m_Format = FORMAT_ERROR; - /* Variable: data - Pointer to the image data. */ + /** + * Pointer to the image data. + */ void *m_pData = nullptr; + + static size_t PixelSize(EImageFormat Format) + { + dbg_assert(Format != FORMAT_ERROR, "Format invalid"); + static const size_t s_aSizes[] = {3, 4, 1}; + return s_aSizes[(int)Format]; + } + + size_t PixelSize() const + { + return PixelSize(m_Format); + } + + static EImageFormat ImageFormatFromInt(int Format) + { + if(Format < (int)FORMAT_RGB || Format > (int)FORMAT_SINGLE_COMPONENT) + return FORMAT_ERROR; + return (EImageFormat)Format; + } }; /* @@ -207,7 +232,7 @@ namespace client_data7 { struct CDataSprite; // NOLINT(bugprone-forward-declaration-namespace) } -typedef std::function &vDstData)> TGLBackendReadPresentedImageData; +typedef std::function &vDstData)> TGLBackendReadPresentedImageData; class IGraphics : public IInterface { @@ -307,15 +332,15 @@ public: virtual bool IsImageFormatRGBA(const char *pFileName, CImageInfo &Img) = 0; // destination and source buffer require to have the same width and height - virtual void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, size_t FullWidth, size_t FullHeight, size_t ColorChannelCount, size_t SubOffsetX, size_t SubOffsetY, size_t SubCopyWidth, size_t SubCopyHeight) = 0; + virtual void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, size_t FullWidth, size_t FullHeight, size_t PixelSize, size_t SubOffsetX, size_t SubOffsetY, size_t SubCopyWidth, size_t SubCopyHeight) = 0; // destination width must be equal to the subwidth of the source - virtual void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, size_t DestWidth, size_t DestHeight, uint8_t *pSourceBuffer, size_t SrcWidth, size_t SrcHeight, size_t ColorChannelCount, size_t SrcSubOffsetX, size_t SrcSubOffsetY, size_t SrcSubCopyWidth, size_t SrcSubCopyHeight) = 0; + virtual void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, size_t DestWidth, size_t DestHeight, uint8_t *pSourceBuffer, size_t SrcWidth, size_t SrcHeight, size_t PixelSize, size_t SrcSubOffsetX, size_t SrcSubOffsetY, size_t SrcSubCopyWidth, size_t SrcSubCopyHeight) = 0; virtual int UnloadTexture(CTextureHandle *pIndex) = 0; - virtual CTextureHandle LoadTextureRaw(size_t Width, size_t Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName = nullptr) = 0; - virtual int LoadTextureRawSub(CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, int Format, const void *pData) = 0; - virtual CTextureHandle LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags) = 0; + virtual CTextureHandle LoadTextureRaw(size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData, int Flags, const char *pTexName = nullptr) = 0; + virtual int LoadTextureRawSub(CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData) = 0; + virtual CTextureHandle LoadTexture(const char *pFilename, int StorageType, int Flags = 0) = 0; virtual CTextureHandle InvalidTexture() const = 0; virtual void TextureSet(CTextureHandle Texture) = 0; void TextureClear() { TextureSet(CTextureHandle()); } diff --git a/src/engine/textrender.h b/src/engine/textrender.h index 9a96ca056..419ecb698 100644 --- a/src/engine/textrender.h +++ b/src/engine/textrender.h @@ -281,7 +281,7 @@ public: virtual STextBoundingBox GetBoundingBoxTextContainer(STextContainerIndex TextContainerIndex) = 0; - virtual void UploadEntityLayerText(void *pTexBuff, size_t ImageColorChannelCount, int TexWidth, int TexHeight, int TexSubWidth, int TexSubHeight, const char *pText, int Length, float x, float y, int FontSize) = 0; + virtual void UploadEntityLayerText(void *pTexBuff, size_t PixelSize, size_t TexWidth, size_t TexHeight, int TexSubWidth, int TexSubHeight, const char *pText, int Length, float x, float y, int FontSize) = 0; virtual int AdjustFontSize(const char *pText, int TextLength, int MaxSize, int MaxWidth) const = 0; virtual float GetGlyphOffsetX(int FontSize, char TextCharacter) const = 0; virtual int CalculateTextWidth(const char *pText, int TextLength, int FontWidth, int FontSize) const = 0; diff --git a/src/game/client/components/countryflags.cpp b/src/game/client/components/countryflags.cpp index f99b27490..1f28a4bc5 100644 --- a/src/game/client/components/countryflags.cpp +++ b/src/game/client/components/countryflags.cpp @@ -75,7 +75,7 @@ void CCountryFlags::LoadCountryflagsIndexfile() CCountryFlag CountryFlag; CountryFlag.m_CountryCode = CountryCode; str_copy(CountryFlag.m_aCountryCodeString, aOrigin); - CountryFlag.m_Texture = Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0, aOrigin); + CountryFlag.m_Texture = Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, 0, aOrigin); Graphics()->FreePNG(&Info); if(g_Config.m_Debug) diff --git a/src/game/client/components/mapimages.cpp b/src/game/client/components/mapimages.cpp index 3ee792eef..75fd54813 100644 --- a/src/game/client/components/mapimages.cpp +++ b/src/game/client/components/mapimages.cpp @@ -106,13 +106,13 @@ void CMapImages::OnMapLoadImpl(class CLayers *pLayers, IMap *pMap) { int LoadFlag = (((m_aTextureUsedByTileOrQuadLayerFlag[i] & 1) != 0) ? TextureLoadFlag : 0) | (((m_aTextureUsedByTileOrQuadLayerFlag[i] & 2) != 0) ? 0 : (Graphics()->IsTileBufferingEnabled() ? IGraphics::TEXLOAD_NO_2D_TEXTURE : 0)); const CMapItemImage_v2 *pImg = (CMapItemImage_v2 *)pMap->GetItem(Start + i); - const int Format = pImg->m_Version < CMapItemImage_v2::CURRENT_VERSION ? CImageInfo::FORMAT_RGBA : pImg->m_Format; + const CImageInfo::EImageFormat Format = pImg->m_Version < CMapItemImage_v2::CURRENT_VERSION ? CImageInfo::FORMAT_RGBA : CImageInfo::ImageFormatFromInt(pImg->m_Format); if(pImg->m_External) { char aPath[IO_MAX_PATH_LENGTH]; char *pName = (char *)pMap->GetData(pImg->m_ImageName); str_format(aPath, sizeof(aPath), "mapres/%s.png", pName); - m_aTextures[i] = Graphics()->LoadTexture(aPath, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, LoadFlag); + m_aTextures[i] = Graphics()->LoadTexture(aPath, IStorage::TYPE_ALL, LoadFlag); pMap->UnloadData(pImg->m_ImageName); } else if(Format != CImageInfo::FORMAT_RGBA) @@ -126,7 +126,7 @@ void CMapImages::OnMapLoadImpl(class CLayers *pLayers, IMap *pMap) char *pName = (char *)pMap->GetData(pImg->m_ImageName); char aTexName[128]; str_format(aTexName, sizeof(aTexName), "%s %s", "embedded:", pName); - m_aTextures[i] = Graphics()->LoadTextureRaw(pImg->m_Width, pImg->m_Height, Format, pData, CImageInfo::FORMAT_RGBA, LoadFlag, aTexName); + m_aTextures[i] = Graphics()->LoadTextureRaw(pImg->m_Width, pImg->m_Height, Format, pData, LoadFlag, aTexName); pMap->UnloadData(pImg->m_ImageName); pMap->UnloadData(pImg->m_ImageData); } @@ -241,15 +241,8 @@ IGraphics::CTextureHandle CMapImages::GetEntities(EMapImageEntityLayerType Entit if(ImagePNGLoaded && ImgInfo.m_Width > 0 && ImgInfo.m_Height > 0) { - int ColorChannelCount = 4; - if(ImgInfo.m_Format == CImageInfo::FORMAT_SINGLE_COMPONENT) - ColorChannelCount = 1; - else if(ImgInfo.m_Format == CImageInfo::FORMAT_RGB) - ColorChannelCount = 3; - else if(ImgInfo.m_Format == CImageInfo::FORMAT_RGBA) - ColorChannelCount = 4; - - int BuildImageSize = ColorChannelCount * ImgInfo.m_Width * ImgInfo.m_Height; + const size_t PixelSize = ImgInfo.PixelSize(); + const size_t BuildImageSize = (size_t)ImgInfo.m_Width * ImgInfo.m_Height * PixelSize; uint8_t *pTmpImgData = (uint8_t *)ImgInfo.m_pData; uint8_t *pBuildImgData = (uint8_t *)malloc(BuildImageSize); @@ -318,11 +311,11 @@ IGraphics::CTextureHandle CMapImages::GetEntities(EMapImageEntityLayerType Entit int CopyHeight = ImgInfo.m_Height / 16; if(ValidTile) { - Graphics()->CopyTextureBufferSub(pBuildImgData, pTmpImgData, ImgInfo.m_Width, ImgInfo.m_Height, ColorChannelCount, (size_t)X * CopyWidth, (size_t)Y * CopyHeight, CopyWidth, CopyHeight); + Graphics()->CopyTextureBufferSub(pBuildImgData, pTmpImgData, ImgInfo.m_Width, ImgInfo.m_Height, PixelSize, (size_t)X * CopyWidth, (size_t)Y * CopyHeight, CopyWidth, CopyHeight); } } - m_aaEntitiesTextures[(EntitiesModType * 2) + (int)EntitiesAreMasked][n] = Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, pBuildImgData, ImgInfo.m_Format, TextureLoadFlag, aPath); + m_aaEntitiesTextures[(EntitiesModType * 2) + (int)EntitiesAreMasked][n] = Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, pBuildImgData, TextureLoadFlag, aPath); } else { @@ -331,7 +324,7 @@ IGraphics::CTextureHandle CMapImages::GetEntities(EMapImageEntityLayerType Entit // set everything transparent mem_zero(pBuildImgData, BuildImageSize); - m_TransparentTexture = Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, pBuildImgData, ImgInfo.m_Format, TextureLoadFlag, aPath); + m_TransparentTexture = Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, pBuildImgData, TextureLoadFlag, aPath); } m_aaEntitiesTextures[(EntitiesModType * 2) + (int)EntitiesAreMasked][n] = m_TransparentTexture; } @@ -351,7 +344,7 @@ IGraphics::CTextureHandle CMapImages::GetSpeedupArrow() if(!m_SpeedupArrowIsLoaded) { int TextureLoadFlag = (Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER : IGraphics::TEXLOAD_TO_3D_TEXTURE_SINGLE_LAYER) | IGraphics::TEXLOAD_NO_2D_TEXTURE; - m_SpeedupArrowTexture = Graphics()->LoadTexture(g_pData->m_aImages[IMAGE_SPEEDUP_ARROW].m_pFilename, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag); + m_SpeedupArrowTexture = Graphics()->LoadTexture(g_pData->m_aImages[IMAGE_SPEEDUP_ARROW].m_pFilename, IStorage::TYPE_ALL, TextureLoadFlag); m_SpeedupArrowIsLoaded = true; } @@ -428,20 +421,24 @@ int CMapImages::GetTextureScale() IGraphics::CTextureHandle CMapImages::UploadEntityLayerText(int TextureSize, int MaxWidth, int YOffset) { - void *pMem = calloc(1024 * 1024 * 4, 1); + const size_t Width = 1024; + const size_t Height = 1024; + const size_t PixelSize = CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA); - UpdateEntityLayerText(pMem, 4, 1024, 1024, TextureSize, MaxWidth, YOffset, 0); - UpdateEntityLayerText(pMem, 4, 1024, 1024, TextureSize, MaxWidth, YOffset, 1); - UpdateEntityLayerText(pMem, 4, 1024, 1024, TextureSize, MaxWidth, YOffset, 2, 255); + void *pMem = calloc(Width * Height * PixelSize, 1); - int TextureLoadFlag = (Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE) | IGraphics::TEXLOAD_NO_2D_TEXTURE; - IGraphics::CTextureHandle Texture = Graphics()->LoadTextureRaw(1024, 1024, CImageInfo::FORMAT_RGBA, pMem, CImageInfo::FORMAT_RGBA, TextureLoadFlag); + UpdateEntityLayerText(pMem, PixelSize, Width, Height, TextureSize, MaxWidth, YOffset, 0); + UpdateEntityLayerText(pMem, PixelSize, Width, Height, TextureSize, MaxWidth, YOffset, 1); + UpdateEntityLayerText(pMem, PixelSize, Width, Height, TextureSize, MaxWidth, YOffset, 2, 255); + + const int TextureLoadFlag = (Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE) | IGraphics::TEXLOAD_NO_2D_TEXTURE; + IGraphics::CTextureHandle Texture = Graphics()->LoadTextureRaw(Width, Height, CImageInfo::FORMAT_RGBA, pMem, TextureLoadFlag); free(pMem); return Texture; } -void CMapImages::UpdateEntityLayerText(void *pTexBuffer, int ImageColorChannelCount, int TexWidth, int TexHeight, int TextureSize, int MaxWidth, int YOffset, int NumbersPower, int MaxNumber) +void CMapImages::UpdateEntityLayerText(void *pTexBuffer, size_t PixelSize, size_t TexWidth, size_t TexHeight, int TextureSize, int MaxWidth, int YOffset, int NumbersPower, int MaxNumber) { char aBuf[4]; int DigitsCount = NumbersPower + 1; @@ -468,7 +465,7 @@ void CMapImages::UpdateEntityLayerText(void *pTexBuffer, int ImageColorChannelCo int ApproximateTextWidth = TextRender()->CalculateTextWidth(aBuf, DigitsCount, 0, UniversalSuitableFontSize); int XOffSet = (MaxWidth - clamp(ApproximateTextWidth, 0, MaxWidth)) / 2; - TextRender()->UploadEntityLayerText(pTexBuffer, ImageColorChannelCount, TexWidth, TexHeight, (TexWidth / 16) - XOffSet, (TexHeight / 16) - YOffset, aBuf, DigitsCount, x + XOffSet, y + YOffset, UniversalSuitableFontSize); + TextRender()->UploadEntityLayerText(pTexBuffer, PixelSize, TexWidth, TexHeight, (TexWidth / 16) - XOffSet, (TexHeight / 16) - YOffset, aBuf, DigitsCount, x + XOffSet, y + YOffset, UniversalSuitableFontSize); } } diff --git a/src/game/client/components/mapimages.h b/src/game/client/components/mapimages.h index 28c88e8b2..cdd8bbfba 100644 --- a/src/game/client/components/mapimages.h +++ b/src/game/client/components/mapimages.h @@ -86,7 +86,7 @@ private: void InitOverlayTextures(); IGraphics::CTextureHandle UploadEntityLayerText(int TextureSize, int MaxWidth, int YOffset); - void UpdateEntityLayerText(void *pTexBuffer, int ImageColorChannelCount, int TexWidth, int TexHeight, int TextureSize, int MaxWidth, int YOffset, int NumbersPower, int MaxNumber = -1); + void UpdateEntityLayerText(void *pTexBuffer, size_t PixelSize, size_t TexWidth, size_t TexHeight, int TextureSize, int MaxWidth, int YOffset, int NumbersPower, int MaxNumber = -1); }; #endif diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 68d0e017c..607f973e0 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -864,7 +864,7 @@ void CMenus::OnInit() Console()->Chain("cl_asset_hud", ConchainAssetHud, this); Console()->Chain("cl_asset_extras", ConchainAssetExtras, this); - m_TextureBlob = Graphics()->LoadTexture("blob.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0); + m_TextureBlob = Graphics()->LoadTexture("blob.png", IStorage::TYPE_ALL); // setup load amount const int NumMenuImages = 5; @@ -2173,10 +2173,9 @@ int CMenus::MenuImageScan(const char *pName, int IsDir, int DirType, void *pUser MenuImage.m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); unsigned char *pData = (unsigned char *)Info.m_pData; - //int Pitch = Info.m_Width*4; // create colorless version - int Step = Info.m_Format == CImageInfo::FORMAT_RGBA ? 4 : 3; + const size_t Step = Info.PixelSize(); // make the texture gray scale for(int i = 0; i < Info.m_Width * Info.m_Height; i++) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 7bf4acfd8..0bb571612 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -305,7 +305,7 @@ void CGameClient::OnInit() else if(g_pData->m_aImages[i].m_pFilename[0] == '\0') // handle special null image without filename g_pData->m_aImages[i].m_Id = IGraphics::CTextureHandle(); else - g_pData->m_aImages[i].m_Id = Graphics()->LoadTexture(g_pData->m_aImages[i].m_pFilename, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0); + g_pData->m_aImages[i].m_Id = Graphics()->LoadTexture(g_pData->m_aImages[i].m_pFilename, IStorage::TYPE_ALL); m_Menus.RenderLoading(pLoadingDDNetCaption, Localize("Initializing assets"), 1); } diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index c13ef3b0b..a107fd718 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -3860,18 +3860,14 @@ bool CEditor::ReplaceImage(const char *pFileName, int StorageType, bool CheckDup if(!pImg->m_External && g_Config.m_ClEditorDilate == 1 && pImg->m_Format == CImageInfo::FORMAT_RGBA) { - int ColorChannelCount = 0; - if(ImgInfo.m_Format == CImageInfo::FORMAT_RGBA) - ColorChannelCount = 4; - - DilateImage((unsigned char *)ImgInfo.m_pData, ImgInfo.m_Width, ImgInfo.m_Height, ColorChannelCount); + DilateImage((unsigned char *)ImgInfo.m_pData, ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.PixelSize()); } pImg->m_AutoMapper.Load(pImg->m_aName); int TextureLoadFlag = Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0) TextureLoadFlag = 0; - pImg->m_Texture = Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, TextureLoadFlag, pFileName); + pImg->m_Texture = Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, TextureLoadFlag, pFileName); ImgInfo.m_pData = nullptr; SortImages(); for(size_t i = 0; i < m_Map.m_vpImages.size(); ++i) @@ -3924,17 +3920,13 @@ bool CEditor::AddImage(const char *pFileName, int StorageType, void *pUser) if(!pImg->m_External && g_Config.m_ClEditorDilate == 1 && pImg->m_Format == CImageInfo::FORMAT_RGBA) { - int ColorChannelCount = 0; - if(ImgInfo.m_Format == CImageInfo::FORMAT_RGBA) - ColorChannelCount = 4; - - DilateImage((unsigned char *)ImgInfo.m_pData, ImgInfo.m_Width, ImgInfo.m_Height, ColorChannelCount); + DilateImage((unsigned char *)ImgInfo.m_pData, ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.PixelSize()); } int TextureLoadFlag = pEditor->Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0) TextureLoadFlag = 0; - pImg->m_Texture = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, TextureLoadFlag, pFileName); + pImg->m_Texture = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, TextureLoadFlag, pFileName); ImgInfo.m_pData = nullptr; str_copy(pImg->m_aName, aBuf); pImg->m_AutoMapper.Load(pImg->m_aName); @@ -4618,7 +4610,7 @@ void CEditor::RenderFileDialog() if(Graphics()->LoadPNG(&m_FilePreviewImageInfo, aBuffer, m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType)) { Graphics()->UnloadTexture(&m_FilePreviewImage); - m_FilePreviewImage = Graphics()->LoadTextureRaw(m_FilePreviewImageInfo.m_Width, m_FilePreviewImageInfo.m_Height, m_FilePreviewImageInfo.m_Format, m_FilePreviewImageInfo.m_pData, m_FilePreviewImageInfo.m_Format, 0); + m_FilePreviewImage = Graphics()->LoadTextureRaw(m_FilePreviewImageInfo.m_Width, m_FilePreviewImageInfo.m_Height, m_FilePreviewImageInfo.m_Format, m_FilePreviewImageInfo.m_pData, 0); Graphics()->FreePNG(&m_FilePreviewImageInfo); m_FilePreviewState = PREVIEW_LOADED; } @@ -7443,42 +7435,42 @@ int CEditor::GetTextureUsageFlag() IGraphics::CTextureHandle CEditor::GetFrontTexture() { if(!m_FrontTexture.IsValid()) - m_FrontTexture = Graphics()->LoadTexture("editor/front.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, GetTextureUsageFlag()); + m_FrontTexture = Graphics()->LoadTexture("editor/front.png", IStorage::TYPE_ALL, GetTextureUsageFlag()); return m_FrontTexture; } IGraphics::CTextureHandle CEditor::GetTeleTexture() { if(!m_TeleTexture.IsValid()) - m_TeleTexture = Graphics()->LoadTexture("editor/tele.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, GetTextureUsageFlag()); + m_TeleTexture = Graphics()->LoadTexture("editor/tele.png", IStorage::TYPE_ALL, GetTextureUsageFlag()); return m_TeleTexture; } IGraphics::CTextureHandle CEditor::GetSpeedupTexture() { if(!m_SpeedupTexture.IsValid()) - m_SpeedupTexture = Graphics()->LoadTexture("editor/speedup.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, GetTextureUsageFlag()); + m_SpeedupTexture = Graphics()->LoadTexture("editor/speedup.png", IStorage::TYPE_ALL, GetTextureUsageFlag()); return m_SpeedupTexture; } IGraphics::CTextureHandle CEditor::GetSwitchTexture() { if(!m_SwitchTexture.IsValid()) - m_SwitchTexture = Graphics()->LoadTexture("editor/switch.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, GetTextureUsageFlag()); + m_SwitchTexture = Graphics()->LoadTexture("editor/switch.png", IStorage::TYPE_ALL, GetTextureUsageFlag()); return m_SwitchTexture; } IGraphics::CTextureHandle CEditor::GetTuneTexture() { if(!m_TuneTexture.IsValid()) - m_TuneTexture = Graphics()->LoadTexture("editor/tune.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, GetTextureUsageFlag()); + m_TuneTexture = Graphics()->LoadTexture("editor/tune.png", IStorage::TYPE_ALL, GetTextureUsageFlag()); return m_TuneTexture; } IGraphics::CTextureHandle CEditor::GetEntitiesTexture() { if(!m_EntitiesTexture.IsValid()) - m_EntitiesTexture = Graphics()->LoadTexture("editor/entities/DDNet.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, GetTextureUsageFlag()); + m_EntitiesTexture = Graphics()->LoadTexture("editor/entities/DDNet.png", IStorage::TYPE_ALL, GetTextureUsageFlag()); return m_EntitiesTexture; } @@ -7506,9 +7498,9 @@ void CEditor::Init() for(CEditorComponent &Component : m_vComponents) Component.Init(this); - m_CheckerTexture = Graphics()->LoadTexture("editor/checker.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0); - m_BackgroundTexture = Graphics()->LoadTexture("editor/background.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0); - m_CursorTexture = Graphics()->LoadTexture("editor/cursor.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0); + m_CheckerTexture = Graphics()->LoadTexture("editor/checker.png", IStorage::TYPE_ALL); + m_BackgroundTexture = Graphics()->LoadTexture("editor/background.png", IStorage::TYPE_ALL); + m_CursorTexture = Graphics()->LoadTexture("editor/cursor.png", IStorage::TYPE_ALL); m_pTilesetPicker = std::make_shared(16, 16); m_pTilesetPicker->m_pEditor = this; diff --git a/src/game/editor/mapitems/map_io.cpp b/src/game/editor/mapitems/map_io.cpp index 31c95e0c7..6224494f8 100644 --- a/src/game/editor/mapitems/map_io.cpp +++ b/src/game/editor/mapitems/map_io.cpp @@ -123,24 +123,26 @@ bool CEditorMap::Save(const char *pFileName) } else { + const size_t PixelSize = CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA); + const size_t DataSize = (size_t)Item.m_Width * Item.m_Height * PixelSize; if(pImg->m_Format == CImageInfo::FORMAT_RGB) { // Convert to RGBA - unsigned char *pDataRGBA = (unsigned char *)malloc((size_t)Item.m_Width * Item.m_Height * 4); + unsigned char *pDataRGBA = (unsigned char *)malloc(DataSize); unsigned char *pDataRGB = (unsigned char *)pImg->m_pData; for(int j = 0; j < Item.m_Width * Item.m_Height; j++) { - pDataRGBA[j * 4] = pDataRGB[j * 3]; - pDataRGBA[j * 4 + 1] = pDataRGB[j * 3 + 1]; - pDataRGBA[j * 4 + 2] = pDataRGB[j * 3 + 2]; - pDataRGBA[j * 4 + 3] = 255; + pDataRGBA[j * PixelSize] = pDataRGB[j * 3]; + pDataRGBA[j * PixelSize + 1] = pDataRGB[j * 3 + 1]; + pDataRGBA[j * PixelSize + 2] = pDataRGB[j * 3 + 2]; + pDataRGBA[j * PixelSize + 3] = 255; } - Item.m_ImageData = Writer.AddData(Item.m_Width * Item.m_Height * 4, pDataRGBA); + Item.m_ImageData = Writer.AddData(DataSize, pDataRGBA); free(pDataRGBA); } else { - Item.m_ImageData = Writer.AddData(Item.m_Width * Item.m_Height * 4, pImg->m_pData); + Item.m_ImageData = Writer.AddData(DataSize, pImg->m_pData); } } Writer.AddItem(MAPITEMTYPE_IMAGE, i, sizeof(Item), &Item); @@ -487,7 +489,7 @@ bool CEditorMap::Load(const char *pFileName, int StorageType, const std::functio std::shared_ptr pImg = std::make_shared(m_pEditor); pImg->m_External = pItem->m_External; - const int Format = pItem->m_Version < CMapItemImage_v2::CURRENT_VERSION ? CImageInfo::FORMAT_RGBA : pItem->m_Format; + const CImageInfo::EImageFormat Format = pItem->m_Version < CMapItemImage_v2::CURRENT_VERSION ? CImageInfo::FORMAT_RGBA : CImageInfo::ImageFormatFromInt(pItem->m_Format); if(pImg->m_External || (Format != CImageInfo::FORMAT_RGB && Format != CImageInfo::FORMAT_RGBA)) { char aBuf[IO_MAX_PATH_LENGTH]; @@ -501,7 +503,7 @@ bool CEditorMap::Load(const char *pFileName, int StorageType, const std::functio int TextureLoadFlag = m_pEditor->Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0) TextureLoadFlag = 0; - pImg->m_Texture = m_pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, TextureLoadFlag, aBuf); + pImg->m_Texture = m_pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, TextureLoadFlag, aBuf); ImgInfo.m_pData = nullptr; pImg->m_External = 1; } @@ -514,13 +516,13 @@ bool CEditorMap::Load(const char *pFileName, int StorageType, const std::functio // copy image data void *pData = DataFile.GetData(pItem->m_ImageData); - const size_t DataSize = (size_t)pImg->m_Width * pImg->m_Height * 4; + const size_t DataSize = (size_t)pImg->m_Width * pImg->m_Height * CImageInfo::PixelSize(Format); pImg->m_pData = malloc(DataSize); mem_copy(pImg->m_pData, pData, DataSize); int TextureLoadFlag = m_pEditor->Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; if(pImg->m_Width % 16 != 0 || pImg->m_Height % 16 != 0) TextureLoadFlag = 0; - pImg->m_Texture = m_pEditor->Graphics()->LoadTextureRaw(pImg->m_Width, pImg->m_Height, pImg->m_Format, pImg->m_pData, CImageInfo::FORMAT_AUTO, TextureLoadFlag); + pImg->m_Texture = m_pEditor->Graphics()->LoadTextureRaw(pImg->m_Width, pImg->m_Height, pImg->m_Format, pImg->m_pData, TextureLoadFlag); } // copy image name diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index ca7a7f300..bde8ed6d9 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -2418,7 +2418,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEntities(void *pContext, CUIRect Vie char aBuf[IO_MAX_PATH_LENGTH]; str_format(aBuf, sizeof(aBuf), "editor/entities/%s.png", pName); - pEditor->m_EntitiesTexture = pEditor->Graphics()->LoadTexture(aBuf, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, pEditor->GetTextureUsageFlag()); + pEditor->m_EntitiesTexture = pEditor->Graphics()->LoadTexture(aBuf, IStorage::TYPE_ALL, pEditor->GetTextureUsageFlag()); return CUI::POPUP_CLOSE_CURRENT; } } diff --git a/src/game/editor/tileart.cpp b/src/game/editor/tileart.cpp index 85a37a7bb..c798bcbe2 100644 --- a/src/game/editor/tileart.cpp +++ b/src/game/editor/tileart.cpp @@ -16,27 +16,14 @@ bool operator<(const ColorRGBA &Left, const ColorRGBA &Right) return Left.a < Right.a; } -static int GetNumColorChannels(const CImageInfo &Image) -{ - switch(Image.m_Format) - { - case CImageInfo::FORMAT_RGB: - return 3; - case CImageInfo::FORMAT_SINGLE_COMPONENT: - return 1; - default: - return 4; - } -} - -static ColorRGBA GetPixelColor(const CImageInfo &Image, int x, int y) +static ColorRGBA GetPixelColor(const CImageInfo &Image, size_t x, size_t y) { uint8_t *pData = static_cast(Image.m_pData); - int NumColorChannels = GetNumColorChannels(Image); - int PixelStartIndex = x * NumColorChannels + (Image.m_Width * NumColorChannels * y); + const size_t PixelSize = Image.PixelSize(); + const size_t PixelStartIndex = x * PixelSize + (Image.m_Width * PixelSize * y); ColorRGBA Color = {255, 255, 255, 255}; - if(NumColorChannels == 1) + if(PixelSize == 1) { Color.a = pData[PixelStartIndex]; } @@ -46,20 +33,20 @@ static ColorRGBA GetPixelColor(const CImageInfo &Image, int x, int y) Color.g = pData[PixelStartIndex + 1]; Color.b = pData[PixelStartIndex + 2]; - if(NumColorChannels == 4) + if(PixelSize == 4) Color.a = pData[PixelStartIndex + 3]; } return Color; } -static void SetPixelColor(CImageInfo *pImage, int x, int y, ColorRGBA Color) +static void SetPixelColor(CImageInfo *pImage, size_t x, size_t y, ColorRGBA Color) { uint8_t *pData = static_cast(pImage->m_pData); - int NumColorChannels = GetNumColorChannels(*pImage); - int PixelStartIndex = x * NumColorChannels + (pImage->m_Width * NumColorChannels * y); + const size_t PixelSize = pImage->PixelSize(); + const size_t PixelStartIndex = x * PixelSize + (pImage->m_Width * PixelSize * y); - if(NumColorChannels == 1) + if(PixelSize == 1) { pData[PixelStartIndex] = Color.a; } @@ -69,7 +56,7 @@ static void SetPixelColor(CImageInfo *pImage, int x, int y, ColorRGBA Color) pData[PixelStartIndex + 1] = Color.g; pData[PixelStartIndex + 2] = Color.b; - if(NumColorChannels == 4) + if(PixelSize == 4) pData[PixelStartIndex + 3] = Color.a; } } @@ -168,7 +155,7 @@ static std::shared_ptr ImageInfoToEditorImage(CEditor *pEditor, co pEditorImage->m_pData = Image.m_pData; int TextureLoadFlag = pEditor->Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; - pEditorImage->m_Texture = pEditor->Graphics()->LoadTextureRaw(Image.m_Width, Image.m_Height, Image.m_Format, Image.m_pData, CImageInfo::FORMAT_AUTO, TextureLoadFlag, pName); + pEditorImage->m_Texture = pEditor->Graphics()->LoadTextureRaw(Image.m_Width, Image.m_Height, Image.m_Format, Image.m_pData, TextureLoadFlag, pName); pEditorImage->m_External = 0; str_copy(pEditorImage->m_aName, pName); diff --git a/src/tools/map_convert_07.cpp b/src/tools/map_convert_07.cpp index 679fde2f3..18ac1349c 100644 --- a/src/tools/map_convert_07.cpp +++ b/src/tools/map_convert_07.cpp @@ -124,7 +124,7 @@ void *ReplaceImageItem(CMapItemImage *pImgItem, CMapItemImage *pNewImgItem) pNewImgItem->m_ImageData = g_NextDataItemID++; g_apNewData[g_Index] = ImgInfo.m_pData; - g_aNewDataSize[g_Index] = ImgInfo.m_Width * ImgInfo.m_Height * 4; + g_aNewDataSize[g_Index] = (size_t)ImgInfo.m_Width * ImgInfo.m_Height * ImgInfo.PixelSize(); g_Index++; return (void *)pNewImgItem; diff --git a/src/tools/map_create_pixelart.cpp b/src/tools/map_create_pixelart.cpp index a40fefd08..52d54d471 100644 --- a/src/tools/map_create_pixelart.cpp +++ b/src/tools/map_create_pixelart.cpp @@ -220,9 +220,9 @@ bool GetPixelClamped(const CImageInfo &Img, int x, int y, uint8_t aPixel[4]) aPixel[2] = 255; aPixel[3] = 255; - int BPP = Img.m_Format == CImageInfo::FORMAT_RGB ? 3 : 4; - for(int i = 0; i < BPP; i++) - aPixel[i] = ((uint8_t *)Img.m_pData)[x * BPP + (Img.m_Width * BPP * y) + i]; + const size_t PixelSize = Img.PixelSize(); + for(size_t i = 0; i < PixelSize; i++) + aPixel[i] = ((uint8_t *)Img.m_pData)[x * PixelSize + (Img.m_Width * PixelSize * y) + i]; return aPixel[3] > 0; } diff --git a/src/tools/map_replace_image.cpp b/src/tools/map_replace_image.cpp index ca223526f..4576de186 100644 --- a/src/tools/map_replace_image.cpp +++ b/src/tools/map_replace_image.cpp @@ -95,7 +95,7 @@ void *ReplaceImageItem(CMapItemImage *pImgItem, const char *pImgName, const char IStorage::StripPathAndExtension(pImgFile, g_aNewName, sizeof(g_aNewName)); g_NewDataID = pImgItem->m_ImageData; g_pNewData = ImgInfo.m_pData; - g_NewDataSize = ImgInfo.m_Width * ImgInfo.m_Height * 4; + g_NewDataSize = (size_t)ImgInfo.m_Width * ImgInfo.m_Height * ImgInfo.PixelSize(); return (void *)pNewImgItem; } From b4fa20599ef3b187dc314bd9047ae91a193d3446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 4 Sep 2023 21:16:27 +0200 Subject: [PATCH 060/126] Truncate IP with ellipsis in connecting popup --- src/game/client/components/menus.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 68d0e017c..37c3a6fd0 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -1299,7 +1299,10 @@ int CMenus::Render() if(UseIpLabel) { - UI()->DoLabel(&Part, Client()->ConnectAddressString(), FontSize, TEXTALIGN_MC); + SLabelProperties IpLabelProps; + IpLabelProps.m_MaxWidth = Part.w; + IpLabelProps.m_EllipsisAtEnd = true; + UI()->DoLabel(&Part, Client()->ConnectAddressString(), FontSize, TEXTALIGN_MC, IpLabelProps); Box.HSplitTop(20.f, &Part, &Box); Box.HSplitTop(24.f, &Part, &Box); } From 1604784669fb88255f80d981d61fbe132cd1446e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 5 Sep 2023 13:50:03 +0200 Subject: [PATCH 061/126] Improve Windows pipe (FIFO) support Use `WaitForPipeDrain` to deterministically wait for the pipe to drain instead of using `Start-Sleep`. Use `Dispose` instead of `Close` to properly flush and close the pipe stream. Add error handling for connection timeout and I/O errors. Handle `ERROR_BROKEN_PIPE` separately when peeking at pipe, as this happens when the pipe is disconnected immediately after connecting it or after reading the previous message. Don't ignore `ERROR_BAD_PIPE` anymore, as the pipe should never be in a disconnected (i.e. bad) state at this point of the function. --- scripts/send_named_pipe.ps1 | 34 +++++++++++++++++++++------------- src/engine/shared/fifo.cpp | 8 +++++++- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/scripts/send_named_pipe.ps1 b/scripts/send_named_pipe.ps1 index fa659deb3..f3dcfe86c 100644 --- a/scripts/send_named_pipe.ps1 +++ b/scripts/send_named_pipe.ps1 @@ -4,7 +4,7 @@ # The second argument is the message to send. if ($args.length -lt 2) { Write-Output "Usage: ./send_named_pipe.ps1 [message] ... [message]" - return + exit -1 } $Wrapper = [pscustomobject]@{ @@ -18,16 +18,24 @@ $Wrapper = [pscustomobject]@{ Reader = $null Writer = $null } -$Wrapper.Pipe.Connect(5000) -if (!$?) { - return +try { + $Wrapper.Pipe.Connect(5000) + $Wrapper.Reader = New-Object System.IO.StreamReader($Wrapper.Pipe) + $Wrapper.Writer = New-Object System.IO.StreamWriter($Wrapper.Pipe) + $Wrapper.Writer.AutoFlush = $true + for ($i = 1; $i -lt $args.length; $i++) { + $Wrapper.Writer.WriteLine($args[$i]) + } + # Wait for pipe contents to be read. + $Wrapper.Pipe.WaitForPipeDrain() + # Dispose the pipe, which also calls Flush and Close. + $Wrapper.Pipe.Dispose() + # Explicity set error level 0 for success, as otherwise the current error level is kept. + exit 0 +} catch [TimeoutException] { + Write-Output "Timeout connecting to pipe" + exit 1 +} catch [System.IO.IOException] { + Write-Output "Broken pipe" + exit 2 } -$Wrapper.Reader = New-Object System.IO.StreamReader($Wrapper.Pipe) -$Wrapper.Writer = New-Object System.IO.StreamWriter($Wrapper.Pipe) -$Wrapper.Writer.AutoFlush = $true -for ($i = 1; $i -lt $args.length; $i++) { - $Wrapper.Writer.WriteLine($args[$i]) -} -# We need to wait because the lines will not be written if we close the pipe immediately -Start-Sleep -Seconds 1.5 -$Wrapper.Pipe.Close() diff --git a/src/engine/shared/fifo.cpp b/src/engine/shared/fifo.cpp index 7ed0b296b..af2b49f09 100644 --- a/src/engine/shared/fifo.cpp +++ b/src/engine/shared/fifo.cpp @@ -154,7 +154,13 @@ void CFifo::Update() if(!PeekNamedPipe(m_pPipe, NULL, 0, NULL, &BytesAvailable, NULL)) { const DWORD LastError = GetLastError(); - if(LastError != ERROR_BAD_PIPE) // pipe not connected, not an error + if(LastError == ERROR_BROKEN_PIPE) + { + // Pipe was disconnected from the other side, either immediately + // after connecting or after reading the previous message. + DisconnectNamedPipe(m_pPipe); + } + else { const std::string ErrorMsg = windows_format_system_message(LastError); dbg_msg("fifo", "failed to peek at pipe '%s' (%ld %s)", m_aFilename, LastError, ErrorMsg.c_str()); From 4d9ff1d90465fcd78f440b206dfb58bbac232b27 Mon Sep 17 00:00:00 2001 From: Harri Nieminen Date: Tue, 5 Sep 2023 22:24:33 +0300 Subject: [PATCH 062/126] Fix typos Found by codespell --- src/base/logger.h | 2 +- src/base/rust.rs | 2 +- src/engine/client/backend/vulkan/backend_vulkan.cpp | 6 +++--- src/engine/client/client.cpp | 2 +- src/engine/client/text.cpp | 6 +++--- src/engine/console.rs | 2 +- src/game/client/components/skins.cpp | 2 +- src/game/client/components/spectator.cpp | 2 +- src/game/client/gameclient.cpp | 4 ++-- src/game/client/lineinput.h | 2 +- src/game/editor/map_grid.h | 2 +- src/game/server/entity.h | 2 +- src/game/server/scoreworker.cpp | 10 +++++----- 13 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/base/logger.h b/src/base/logger.h index e3f89a01a..1324d84d1 100644 --- a/src/base/logger.h +++ b/src/base/logger.h @@ -103,7 +103,7 @@ public: */ virtual void GlobalFinish() {} /** - * Notifies thte logger of a changed `m_Filter`. + * Notifies the logger of a changed `m_Filter`. */ virtual void OnFilterChange() {} }; diff --git a/src/base/rust.rs b/src/base/rust.rs index 540bd27ed..b666f1181 100644 --- a/src/base/rust.rs +++ b/src/base/rust.rs @@ -11,7 +11,7 @@ use std::str; /// /// Callbacks in C are usually represented by a function pointer and some /// "userdata" pointer that is also passed to the function pointer. This allows -/// to hand data to the callback. This type represents such a userdata poiner. +/// to hand data to the callback. This type represents such a userdata pointer. /// /// It is `unsafe` to convert the `UserPtr` back to its original pointer using /// [`UserPtr::cast`] because its lifetime and type information was lost. diff --git a/src/engine/client/backend/vulkan/backend_vulkan.cpp b/src/engine/client/backend/vulkan/backend_vulkan.cpp index e5214b327..7470653ce 100644 --- a/src/engine/client/backend/vulkan/backend_vulkan.cpp +++ b/src/engine/client/backend/vulkan/backend_vulkan.cpp @@ -1102,7 +1102,7 @@ protected: bool m_CanAssert = false; /** - * After an error occured, the rendering stop as soon as possible + * After an error occurred, the rendering stop as soon as possible * Always stop the current code execution after a call to this function (e.g. return false) */ void SetError(EGFXErrorType ErrType, const char *pErr, const char *pErrStrExtra = nullptr) @@ -6492,7 +6492,7 @@ public: Ret = CallbackObj.m_CMDIsHandled; if(!CallbackObj.m_CommandCB(pBaseCommand, Buffer)) { - // an error occured, stop this command and ignore all further commands + // an error occurred, stop this command and ignore all further commands return ERunCommandReturnTypes::RUN_COMMAND_COMMAND_ERROR; } } @@ -7652,7 +7652,7 @@ public: { if(!m_aCommandCallbacks[CommandBufferCMDOff(NextCmd.m_Command)].m_CommandCB(NextCmd.m_pRawCommand, NextCmd)) { - // an error occured, the thread will not continue execution + // an error occurred, the thread will not continue execution HasErrorFromCmd = true; break; } diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 9dbf35d43..08e3407b9 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -4626,7 +4626,7 @@ int main(int argc, const char **argv) pClient->GetGPUInfoString(aGPUInfo); char aMessage[768]; str_format(aMessage, sizeof(aMessage), - "An assertion error occured. Please write down or take a screenshot of the following information and report this error.\n" + "An assertion error occurred. Please write down or take a screenshot of the following information and report this error.\n" "Please also share the assert log which you should find in the 'dumps' folder in your config directory.\n\n" "%s\n\n" "Platform: %s\n" diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp index cf948df4f..177d87806 100644 --- a/src/engine/client/text.cpp +++ b/src/engine/client/text.cpp @@ -113,7 +113,7 @@ class CAtlas /** * Sections with a smaller width or height will not be created * when cutting larger sections, to prevent collecting many - * small, mostly unuseable sections. + * small, mostly unusable sections. */ static constexpr size_t MIN_SECTION_DIMENSION = 6; @@ -227,7 +227,7 @@ public: } // We don't iterate sections in the map with increasing width and height at the same time, - // because it's slower and doesn't noticable increase the atlas utilization. + // because it's slower and doesn't noticeable increase the atlas utilization. } // Check vector for larger section @@ -261,7 +261,7 @@ public: } } while(SectionIndex > 0); if(SmallestLossIndex == m_vSections.size()) - return false; // No useable section found in vector + return false; // No usable section found in vector // Use the section with the smallest loss const SSection Section = m_vSections[SmallestLossIndex]; diff --git a/src/engine/console.rs b/src/engine/console.rs index 44cafdfe1..055c0802f 100644 --- a/src/engine/console.rs +++ b/src/engine/console.rs @@ -512,7 +512,7 @@ mod ffi { /// Used as a last parameter in [`IConsole::Print`]. /// /// It is treated as "no color" in the console code, meaning that the default -/// color of the output medium isn't overriden. +/// color of the output medium isn't overridden. #[allow(non_upper_case_globals)] pub const gs_ConsoleDefaultColor: ColorRGBA = ColorRGBA { r: 1.0, diff --git a/src/game/client/components/skins.cpp b/src/game/client/components/skins.cpp index 592743b86..ce4812224 100644 --- a/src/game/client/components/skins.cpp +++ b/src/game/client/components/skins.cpp @@ -234,7 +234,7 @@ const CSkin *CSkins::LoadSkin(const char *pName, CImageInfo &Info) int OrgWeight = 0; int NewWeight = 192; - // find most common frequence + // find most common frequency for(int y = 0; y < BodyHeight; y++) for(int x = 0; x < BodyWidth; x++) { diff --git a/src/game/client/components/spectator.cpp b/src/game/client/components/spectator.cpp index 739aaf2ec..21f6e7914 100644 --- a/src/game/client/components/spectator.cpp +++ b/src/game/client/components/spectator.cpp @@ -173,7 +173,7 @@ void CSpectator::OnConsoleInit() Console()->Register("spectate_next", "", CFGFLAG_CLIENT, ConSpectateNext, this, "Spectate the next player"); Console()->Register("spectate_previous", "", CFGFLAG_CLIENT, ConSpectatePrevious, this, "Spectate the previous player"); Console()->Register("spectate_closest", "", CFGFLAG_CLIENT, ConSpectateClosest, this, "Spectate the closest player"); - Console()->Register("spectate_multiview", "i[id]", CFGFLAG_CLIENT, ConMultiView, this, "Add/remove Client-IDs to spectate them exclusivly (-1 to reset)"); + Console()->Register("spectate_multiview", "i[id]", CFGFLAG_CLIENT, ConMultiView, this, "Add/remove Client-IDs to spectate them exclusively (-1 to reset)"); } bool CSpectator::OnCursorMove(float x, float y, IInput::ECursorType CursorType) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 7bf4acfd8..7d790e5ae 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -3585,7 +3585,7 @@ void CGameClient::HandleMultiView() else if(m_MultiView.m_SecondChance != 0.0f) m_MultiView.m_SecondChance = 0.0f; - // if we only have one tee thats in the list, we activate solo-mode + // if we only have one tee that's in the list, we activate solo-mode m_MultiView.m_Solo = std::count(std::begin(m_aMultiViewId), std::end(m_aMultiViewId), true) == 1; vec2 TargetPos = vec2((Minpos.x + Maxpos.x) / 2.0f, (Minpos.y + Maxpos.y) / 2.0f); @@ -3751,7 +3751,7 @@ float CGameClient::CalculateMultiViewZoom(vec2 MinPos, vec2 MaxPos, float Vel) float Zoom = std::max(ZoomX, ZoomY); // zoom out to maximum 10 percent of the current zoom for 70 velocity float Diff = clamp(MapValue(70.0f, 15.0f, Zoom * 0.10f, 0.0f, Vel), 0.0f, Zoom * 0.10f); - // zoom should stay inbetween 1.1 and 20.0 + // zoom should stay between 1.1 and 20.0 Zoom = clamp(Zoom + Diff, 1.1f, 20.0f); // add the user preference Zoom -= (Zoom * 0.1f) * m_MultiViewPersonalZoom; diff --git a/src/game/client/lineinput.h b/src/game/client/lineinput.h index e3e23e440..93b298532 100644 --- a/src/game/client/lineinput.h +++ b/src/game/client/lineinput.h @@ -53,7 +53,7 @@ private: static char ms_aStars[128]; - char *m_pStr = nullptr; // explicitly set to nullptr outside of contructor, so SetBuffer works in this case + char *m_pStr = nullptr; // explicitly set to nullptr outside of constructor, so SetBuffer works in this case size_t m_MaxSize; size_t m_MaxChars; size_t m_Len; diff --git a/src/game/editor/map_grid.h b/src/game/editor/map_grid.h index 6da8290da..6aabf1757 100644 --- a/src/game/editor/map_grid.h +++ b/src/game/editor/map_grid.h @@ -13,7 +13,7 @@ public: int GridLineDistance() const; /** - * Returns wether the grid is rendered. + * Returns whether the grid is rendered. */ bool IsEnabled() const; diff --git a/src/game/server/entity.h b/src/game/server/entity.h index 799fde8f4..b7948b41a 100644 --- a/src/game/server/entity.h +++ b/src/game/server/entity.h @@ -134,7 +134,7 @@ public: // TODO: Maybe make protected ClientID of the initiator from this entity. -1 created by map. This is used by save/load to remove related entities to the tee. CCharacter should not return the PlayerId, because they get - handled separatly in save/load code. + handled separately in save/load code. */ virtual int GetOwnerID() const { return -1; } diff --git a/src/game/server/scoreworker.cpp b/src/game/server/scoreworker.cpp index 7e4e77c38..11b0e09af 100644 --- a/src/game/server/scoreworker.cpp +++ b/src/game/server/scoreworker.cpp @@ -425,7 +425,7 @@ bool CScoreWorker::SaveScore(IDbConnection *pSqlServer, const ISqlData *pGameDat if(w == Write::NORMAL_FAILED) { int NumUpdated; - // move to non-tmp table succeded. delete from backup again + // move to non-tmp table succeeded. delete from backup again str_format(aBuf, sizeof(aBuf), "INSERT INTO %s_race SELECT * FROM %s_race_backup WHERE GameId=? AND Name=? AND Timestamp=%s", pSqlServer->GetPrefix(), pSqlServer->GetPrefix(), pSqlServer->InsertTimestampAsUtc()); @@ -442,7 +442,7 @@ bool CScoreWorker::SaveScore(IDbConnection *pSqlServer, const ISqlData *pGameDat return true; } - // move to non-tmp table succeded. delete from backup again + // move to non-tmp table succeeded. delete from backup again str_format(aBuf, sizeof(aBuf), "DELETE FROM %s_race_backup WHERE GameId=? AND Name=? AND Timestamp=%s", pSqlServer->GetPrefix(), pSqlServer->InsertTimestampAsUtc()); @@ -1501,7 +1501,7 @@ bool CScoreWorker::SaveTeam(IDbConnection *pSqlServer, const ISqlData *pGameData if(w == Write::NORMAL_SUCCEEDED) { - // write succeded on mysql server. delete from sqlite again + // write succeeded on mysql server. delete from sqlite again char aBuf[128] = {0}; str_format(aBuf, sizeof(aBuf), "DELETE FROM %s_saves_backup WHERE Code = ?", @@ -1518,7 +1518,7 @@ bool CScoreWorker::SaveTeam(IDbConnection *pSqlServer, const ISqlData *pGameData { char aBuf[256] = {0}; bool End; - // move to non-tmp table succeded. delete from backup again + // move to non-tmp table succeeded. delete from backup again str_format(aBuf, sizeof(aBuf), "INSERT INTO %s_saves SELECT * FROM %s_saves_backup WHERE Code = ?", pSqlServer->GetPrefix(), pSqlServer->GetPrefix()); @@ -1532,7 +1532,7 @@ bool CScoreWorker::SaveTeam(IDbConnection *pSqlServer, const ISqlData *pGameData return true; } - // move to non-tmp table succeded. delete from backup again + // move to non-tmp table succeeded. delete from backup again str_format(aBuf, sizeof(aBuf), "DELETE FROM %s_saves_backup WHERE Code = ?", pSqlServer->GetPrefix()); From 5a8e67b323318f30e0a259a5d35cdf5964755bd2 Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Mon, 4 Sep 2023 14:03:56 +0200 Subject: [PATCH 063/126] Fix 0.7 clients not being able to join after slot change If the player slots update the 0.7 clients have to be informed about it. Otherwise the client can block the join button if the outdated playerslots are filled already. --- src/engine/server/server.cpp | 1 + src/game/server/gamecontext.cpp | 35 ++++++++++++++++++++++----------- src/game/server/gamecontext.h | 1 + 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 9bda9978d..649bf6934 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -3810,6 +3810,7 @@ void CServer::RegisterCommands() Console()->Chain("sv_name", ConchainSpecialInfoupdate, this); Console()->Chain("password", ConchainSpecialInfoupdate, this); + Console()->Chain("sv_spectator_slots", ConchainSpecialInfoupdate, this); Console()->Chain("sv_max_clients_per_ip", ConchainMaxclientsperipUpdate, this); Console()->Chain("access_level", ConchainCommandAccessUpdate, this); diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index adb412993..969e94fc3 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -641,17 +641,14 @@ void CGameContext::SendMotd(int ClientID) void CGameContext::SendSettings(int ClientID) { - if(Server()->IsSixup(ClientID)) - { - protocol7::CNetMsg_Sv_ServerSettings Msg; - Msg.m_KickVote = g_Config.m_SvVoteKick; - Msg.m_KickMin = g_Config.m_SvVoteKickMin; - Msg.m_SpecVote = g_Config.m_SvVoteSpectate; - Msg.m_TeamLock = 0; - Msg.m_TeamBalance = 0; - Msg.m_PlayerSlots = g_Config.m_SvMaxClients - g_Config.m_SvSpectatorSlots; - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_NORECORD, ClientID); - } + protocol7::CNetMsg_Sv_ServerSettings Msg; + Msg.m_KickVote = g_Config.m_SvVoteKick; + Msg.m_KickMin = g_Config.m_SvVoteKickMin; + Msg.m_SpecVote = g_Config.m_SvVoteSpectate; + Msg.m_TeamLock = 0; + Msg.m_TeamBalance = 0; + Msg.m_PlayerSlots = g_Config.m_SvMaxClients - g_Config.m_SvSpectatorSlots; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_NORECORD, ClientID); } void CGameContext::SendBroadcast(const char *pText, int ClientID, bool IsImportant) @@ -3324,6 +3321,16 @@ void CGameContext::ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *p } } +void CGameContext::ConchainSettingUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments()) + { + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->SendSettings(-1); + } +} + void CGameContext::OnConsoleInit() { m_pServer = Kernel()->RequestInterface(); @@ -3363,6 +3370,12 @@ void CGameContext::OnConsoleInit() Console()->Chain("sv_motd", ConchainSpecialMotdupdate, this); + Console()->Chain("sv_vote_kick", ConchainSettingUpdate, this); + Console()->Chain("sv_vote_kick_min", ConchainSettingUpdate, this); + Console()->Chain("sv_vote_spectate", ConchainSettingUpdate, this); + Console()->Chain("sv_spectator_slots", ConchainSettingUpdate, this); + Console()->Chain("sv_max_clients", ConchainSettingUpdate, this); + #define CONSOLE_COMMAND(name, params, flags, callback, userdata, help) m_pConsole->Register(name, params, flags, callback, userdata, help); #include #define CHAT_COMMAND(name, params, flags, callback, userdata, help) m_pConsole->Register(name, params, flags, callback, userdata, help); diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index fa3f69512..6acac3313 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -132,6 +132,7 @@ class CGameContext : public IGameServer static void ConDrySave(IConsole::IResult *pResult, void *pUserData); static void ConDumpAntibot(IConsole::IResult *pResult, void *pUserData); static void ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + static void ConchainSettingUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); static void ConDumpLog(IConsole::IResult *pResult, void *pUserData); void Construct(int Resetting); From f71a2e017a8d9a83f06ab2f91b8a918ecb7d4072 Mon Sep 17 00:00:00 2001 From: furo Date: Wed, 6 Sep 2023 15:29:07 +0200 Subject: [PATCH 064/126] Add CTRL+F in load map prompt --- src/game/editor/editor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index a107fd718..e9fe5bf96 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -4560,8 +4560,11 @@ void CEditor::RenderFileDialog() { // render search bar UI()->DoLabel(&FileBoxLabel, "Search:", 10.0f, TEXTALIGN_ML); - if(m_FileDialogOpening) + if(m_FileDialogOpening || (Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())) + { UI()->SetActiveItem(&m_FileDialogFilterInput); + m_FileDialogFilterInput.SelectAll(); + } if(UI()->DoClearableEditBox(&m_FileDialogFilterInput, &FileBox, 10.0f)) { RefreshFilteredFileList(); @@ -4784,6 +4787,7 @@ void CEditor::RenderFileDialog() if(IsDir) // folder { m_FileDialogFilterInput.Clear(); + UI()->SetActiveItem(&m_FileDialogFilterInput); const bool ParentFolder = str_comp(m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename, "..") == 0; if(ParentFolder) // parent folder { From 07d8d591c808cad8a1fed0d0b12e7ce22ce983d3 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Tue, 5 Sep 2023 03:02:13 +0300 Subject: [PATCH 065/126] Add NO_SKIN_CHANGE_FOR_FROZEN game info flag The flag is wanted for mods which use freeze state but need or want to keep the player skins (the skin is critical for some mods). --- datasrc/network.py | 4 ++-- src/game/client/components/players.cpp | 3 ++- src/game/client/gameclient.cpp | 6 ++++++ src/game/client/gameclient.h | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/datasrc/network.py b/datasrc/network.py index 6175cf16f..27b013953 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -26,7 +26,7 @@ GameInfoFlags = [ ] GameInfoFlags2 = [ "ALLOW_X_SKINS", "GAMETYPE_CITY", "GAMETYPE_FDDRACE", "ENTITIES_FDDRACE", "HUD_HEALTH_ARMOR", "HUD_AMMO", - "HUD_DDRACE", "NO_WEAK_HOOK" + "HUD_DDRACE", "NO_WEAK_HOOK", "NO_SKIN_CHANGE_FOR_FROZEN" ] ExPlayerFlags = ["AFK", "PAUSED", "SPEC"] LegacyProjectileFlags = [f"CLIENTID_BIT{i}" for i in range(8)] + [ @@ -71,7 +71,7 @@ enum enum { - GAMEINFO_CURVERSION=8, + GAMEINFO_CURVERSION=9, }; ''' diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 0ab994cf4..95e137b78 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -763,7 +763,8 @@ void CPlayers::OnRender() m_aRenderInfo[i].m_TeeRenderFlags |= TEE_EFFECT_FROZEN; const CGameClient::CSnapState::CCharacterInfo &CharacterInfo = m_pClient->m_Snap.m_aCharacters[i]; - if((CharacterInfo.m_Cur.m_Weapon == WEAPON_NINJA || (CharacterInfo.m_HasExtendedData && CharacterInfo.m_ExtendedData.m_FreezeEnd != 0)) && g_Config.m_ClShowNinja) + const bool Frozen = CharacterInfo.m_HasExtendedData && CharacterInfo.m_ExtendedData.m_FreezeEnd != 0; + if((CharacterInfo.m_Cur.m_Weapon == WEAPON_NINJA || (Frozen && !m_pClient->m_GameInfo.m_NoSkinChangeForFrozen)) && g_Config.m_ClShowNinja) { // change the skin for the player to the ninja const auto *pSkin = m_pClient->m_Skins.FindOrNullptr("x_ninja"); diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 7bf4acfd8..0c531554c 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -1188,6 +1188,7 @@ static CGameInfo GetGameInfo(const CNetObj_GameInfoEx *pInfoEx, int InfoExSize, Info.m_HudAmmo = true; Info.m_HudDDRace = false; Info.m_NoWeakHookAndBounce = false; + Info.m_NoSkinChangeForFrozen = false; if(Version >= 0) { @@ -1243,6 +1244,11 @@ static CGameInfo GetGameInfo(const CNetObj_GameInfoEx *pInfoEx, int InfoExSize, { Info.m_NoWeakHookAndBounce = Flags2 & GAMEINFOFLAG2_NO_WEAK_HOOK; } + if(Version >= 9) + { + Info.m_NoSkinChangeForFrozen = Flags2 & GAMEINFOFLAG2_NO_SKIN_CHANGE_FOR_FROZEN; + } + return Info; } diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index f82fc9513..8e4056959 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -96,6 +96,7 @@ public: bool m_HudDDRace; bool m_NoWeakHookAndBounce; + bool m_NoSkinChangeForFrozen; }; class CSnapEntities From fe95919f63d52894e8af56cca9479d5cd9ab5401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 6 Sep 2023 17:53:49 +0200 Subject: [PATCH 066/126] Support longer lines being rendered in console Don't truncate console lines at 255 bytes anymore. Especially lines containing many Unicode characters would be adversely affected by this limitation. Instead, truncate console lines after 10 wrapped lines are rendered. Rendering too many lines at once currently breaks the console scrolling. Rendering an ellipsis is currently not possible when rendering text with a maximum line count. Increase buffer sizes to handle long (esp. invalid) command inputs. Closes #7132. --- src/engine/shared/console.cpp | 6 +++--- src/game/client/components/console.cpp | 11 +++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp index 9ceb75ed6..eda02ee33 100644 --- a/src/engine/shared/console.cpp +++ b/src/engine/shared/console.cpp @@ -543,7 +543,7 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientID, bo // ends at the first whitespace, which breaks for unknown commands (filenames) containing spaces. if(!m_pfnUnknownCommandCallback(pStr, m_pUnknownCommandUserdata)) { - char aBuf[256]; + char aBuf[512 + 32]; str_format(aBuf, sizeof(aBuf), "No such command: %s.", Result.m_pCommand); Print(OUTPUT_LEVEL_STANDARD, "chatresp", aBuf); } @@ -881,7 +881,7 @@ void CConsole::TraverseChain(FCommandCallback *ppfnCallback, void **ppUserData) void CConsole::ConToggle(IConsole::IResult *pResult, void *pUser) { CConsole *pConsole = static_cast(pUser); - char aBuf[128] = {0}; + char aBuf[512 + 32] = {0}; CCommand *pCommand = pConsole->FindCommand(pResult->GetString(0), pConsole->m_FlagMask); if(pCommand) { @@ -933,7 +933,7 @@ void CConsole::ConToggle(IConsole::IResult *pResult, void *pUser) void CConsole::ConToggleStroke(IConsole::IResult *pResult, void *pUser) { CConsole *pConsole = static_cast(pUser); - char aBuf[128] = {0}; + char aBuf[512 + 32] = {0}; CCommand *pCommand = pConsole->FindCommand(pResult->GetString(1), pConsole->m_FlagMask); if(pCommand) { diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index a26d2df10..fd097e054 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -346,7 +346,7 @@ bool CGameConsole::CInstance::OnInput(const IInput::CEvent &Event) // find the current command { - char aBuf[128]; + char aBuf[512]; StrCopyUntilSpace(aBuf, sizeof(aBuf), GetString()); const IConsole::CCommandInfo *pCommand = m_pGameConsole->m_pConsole->GetCommandInfo(aBuf, m_CompletionFlagmask, m_Type != CGameConsole::CONSOLETYPE_LOCAL && m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands()); @@ -367,9 +367,6 @@ bool CGameConsole::CInstance::OnInput(const IInput::CEvent &Event) void CGameConsole::CInstance::PrintLine(const char *pLine, int Len, ColorRGBA PrintColor) { - if(Len > 255) - Len = 255; - m_BacklogLock.lock(); CBacklogEntry *pEntry = m_Backlog.Allocate(sizeof(CBacklogEntry) + Len); pEntry->m_YOffset = -1.0f; @@ -694,7 +691,7 @@ void CGameConsole::OnRender() if(NumArguments <= 0 && pConsole->m_IsCommand) { - char aBuf[512]; + char aBuf[1024]; str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_pCommandHelp); TextRender()->TextEx(&Info.m_Cursor, aBuf, -1); TextRender()->TextColor(0.75f, 0.75f, 0.75f, 1); @@ -729,8 +726,9 @@ void CGameConsole::OnRender() { TextRender()->SetCursor(&Cursor, 0.0f, 0.0f, FontSize, 0); Cursor.m_LineWidth = Screen.w - 10; + Cursor.m_MaxLines = 10; TextRender()->TextEx(&Cursor, pEntry->m_aText, -1); - pEntry->m_YOffset = Cursor.m_Y + Cursor.m_AlignedFontSize + LineOffset; + pEntry->m_YOffset = Cursor.Height() + LineOffset; } OffsetY += pEntry->m_YOffset; @@ -751,6 +749,7 @@ void CGameConsole::OnRender() { TextRender()->SetCursor(&Cursor, 0.0f, y - OffsetY, FontSize, TEXTFLAG_RENDER); Cursor.m_LineWidth = Screen.w - 10.0f; + Cursor.m_MaxLines = 10; Cursor.m_CalculateSelectionMode = (m_ConsoleState == CONSOLE_OPEN && pConsole->m_MousePress.y < pConsole->m_BoundingBox.m_Y && (pConsole->m_MouseIsPress || (pConsole->m_CurSelStart != pConsole->m_CurSelEnd) || pConsole->m_HasSelection)) ? TEXT_CURSOR_SELECTION_MODE_CALCULATE : TEXT_CURSOR_SELECTION_MODE_NONE; Cursor.m_PressMouse = pConsole->m_MousePress; Cursor.m_ReleaseMouse = pConsole->m_MouseRelease; From dd8b2cd88af2ca54b3a70fad2deafbd5fc29e437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 6 Sep 2023 18:04:14 +0200 Subject: [PATCH 067/126] Fix incorrect text height when maximum number of lines specified Check if maximum number of lines has been reached before starting a new line, to prevent the text cursor from reporting the wrong number of lines and text height in that case. --- src/engine/client/text.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp index 8bb25dd45..155e69677 100644 --- a/src/engine/client/text.cpp +++ b/src/engine/client/text.cpp @@ -1574,7 +1574,11 @@ public: float LastCharX = DrawX; float LastCharWidth = 0; + // Returns true if line was started const auto &&StartNewLine = [&]() { + if(pCursor->m_MaxLines > 0 && LineCount >= pCursor->m_MaxLines) + return false; + DrawX = pCursor->m_StartX; DrawY += pCursor->m_AlignedFontSize; if((RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0) @@ -1587,6 +1591,7 @@ public: LastCharX = DrawX; LastCharWidth = 0; ++LineCount; + return true; }; if(pCursor->m_CalculateSelectionMode != TEXT_CURSOR_SELECTION_MODE_NONE || pCursor->m_CursorMode != TEXT_CURSOR_CURSOR_MODE_NONE) @@ -1606,7 +1611,7 @@ public: bool GotNewLine = false; bool GotNewLineLast = false; - while(pCurrent < pEnd && (pCursor->m_MaxLines < 1 || LineCount <= pCursor->m_MaxLines) && pCurrent != pEllipsis) + while(pCurrent < pEnd && pCurrent != pEllipsis) { bool NewLine = false; const char *pBatchEnd = pEnd; @@ -1667,8 +1672,7 @@ public: if((pCursor->m_Flags & TEXTFLAG_DISALLOW_NEWLINE) == 0) { pLastGlyph = nullptr; - StartNewLine(); - if(pCursor->m_MaxLines > 0 && LineCount > pCursor->m_MaxLines) + if(!StartNewLine()) break; continue; } @@ -1859,7 +1863,8 @@ public: if(NewLine) { - StartNewLine(); + if(!StartNewLine()) + break; GotNewLine = true; GotNewLineLast = true; } From ae7eda1628c1c50044d35fe39123ac514283dc9c Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Tue, 29 Aug 2023 21:43:52 +0200 Subject: [PATCH 068/126] revert change to quad selection (resolves 7025) Some refactorings are kept and rotation being bound to `R` is also not reverted. --- src/game/editor/editor.cpp | 187 +++++++++---------------------------- src/game/editor/editor.h | 12 +-- src/game/editor/popups.cpp | 16 ++-- 3 files changed, 55 insertions(+), 160 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index c13ef3b0b..9a57f3378 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -488,18 +488,6 @@ std::vector CEditor::GetSelectedQuads() return vpQuads; } -std::vector> CEditor::GetSelectedQuadPoints() -{ - std::shared_ptr pQuadLayer = std::static_pointer_cast(GetSelectedLayerType(0, LAYERTYPE_QUADS)); - std::vector> vpQuads; - if(!pQuadLayer) - return vpQuads; - vpQuads.reserve(m_vSelectedQuadPoints.size()); - for(auto [QuadIndex, SelectedPoints] : m_vSelectedQuadPoints) - vpQuads.emplace_back(&pQuadLayer->m_vQuads[QuadIndex], SelectedPoints); - return vpQuads; -} - CSoundSource *CEditor::GetSelectedSource() { std::shared_ptr pSounds = std::static_pointer_cast(GetSelectedLayerType(0, LAYERTYPE_SOUNDS)); @@ -516,8 +504,8 @@ void CEditor::SelectLayer(int LayerIndex, int GroupIndex) m_SelectedGroup = GroupIndex; m_vSelectedLayers.clear(); - m_vSelectedQuads.clear(); - m_vSelectedQuadPoints.clear(); + DeselectQuads(); + DeselectQuadPoints(); AddSelectedLayer(LayerIndex); } @@ -550,27 +538,17 @@ void CEditor::DeselectQuads() void CEditor::DeselectQuadPoints() { - m_vSelectedQuadPoints.clear(); + m_SelectedQuadPoints = 0; } -void CEditor::SelectQuadPoint(int QuadIndex, int Index) +void CEditor::SelectQuadPoint(int Index) { - m_vSelectedQuadPoints.clear(); - m_vSelectedQuadPoints.emplace_back(QuadIndex, 1 << Index); + m_SelectedQuadPoints = 1 << Index; } -void CEditor::ToggleSelectQuadPoint(int QuadIndex, int Index) +void CEditor::ToggleSelectQuadPoint(int Index) { - int ListIndex = FindSelectedQuadPointIndex(QuadIndex); - - if(ListIndex >= 0) - { - m_vSelectedQuadPoints.at(ListIndex).second ^= 1 << Index; - if(m_vSelectedQuadPoints.at(ListIndex).second == 0) - m_vSelectedQuadPoints.erase(m_vSelectedQuadPoints.begin() + ListIndex); - } - else - m_vSelectedQuadPoints.emplace_back(QuadIndex, 1 << Index); + m_SelectedQuadPoints ^= 1 << Index; } void CEditor::DeleteSelectedQuads() @@ -586,13 +564,7 @@ void CEditor::DeleteSelectedQuads() if(m_vSelectedQuads[j] > m_vSelectedQuads[i]) m_vSelectedQuads[j]--; - int QuadIndex = m_vSelectedQuads.at(i); m_vSelectedQuads.erase(m_vSelectedQuads.begin() + i); - - int ListIndex = FindSelectedQuadPointIndex(QuadIndex); - if(ListIndex >= 0) - m_vSelectedQuadPoints.erase(m_vSelectedQuadPoints.begin() + ListIndex); - i--; } } @@ -602,10 +574,9 @@ bool CEditor::IsQuadSelected(int Index) const return FindSelectedQuadIndex(Index) >= 0; } -bool CEditor::IsQuadPointSelected(int QuadIndex, int Index) const +bool CEditor::IsQuadPointSelected(int Index) const { - int ListIndex = FindSelectedQuadPointIndex(QuadIndex); - return ListIndex >= 0 && (m_vSelectedQuadPoints.at(ListIndex).second & (1 << Index)); + return m_SelectedQuadPoints & (1 << Index); } int CEditor::FindSelectedQuadIndex(int Index) const @@ -616,19 +587,6 @@ int CEditor::FindSelectedQuadIndex(int Index) const return -1; } -int CEditor::FindSelectedQuadPointIndex(int QuadIndex) const -{ - auto Iter = std::find_if( - m_vSelectedQuadPoints.begin(), - m_vSelectedQuadPoints.end(), - [QuadIndex](auto Pair) { return Pair.first == QuadIndex; }); - - if(Iter != m_vSelectedQuadPoints.end()) - return Iter - m_vSelectedQuadPoints.begin(); - else - return -1; -} - int CEditor::FindEnvPointIndex(int Index, int Channel) const { auto Iter = std::find( @@ -1383,7 +1341,7 @@ void CEditor::DoQuad(CQuad *pQuad, int Index) if(!IsQuadSelected(Index)) SelectQuad(Index); - if(Input()->ModifierIsPressed()) + if(Input()->ShiftIsPressed()) s_Operation = OP_MOVE_PIVOT; else s_Operation = OP_MOVE_ALL; @@ -1551,7 +1509,7 @@ void CEditor::DoQuad(CQuad *pQuad, int Index) ms_pUiGotContext = pID; Graphics()->SetColor(1, 1, 1, 1); - m_pTooltip = "Left mouse button to move. Hold ctrl to move pivot. Hold alt to ignore grid. Hold shift and right click to delete."; + m_pTooltip = "Left mouse button to move. Hold shift to move pivot. Hold alt to ignore grid. Hold shift and right click to delete."; if(UI()->MouseButton(0)) { @@ -1587,49 +1545,6 @@ void CEditor::DoQuad(CQuad *pQuad, int Index) else Graphics()->SetColor(0, 1, 0, 1); - // Handle copy/paste operations - static bool s_Pasted = false; - if(Input()->KeyPress(KEY_C) && Input()->ModifierIsPressed()) - { - m_vCopyBuffer.clear(); - std::shared_ptr pLayer = std::static_pointer_cast(GetSelectedLayerType(0, LAYERTYPE_QUADS)); - for(int Selected : m_vSelectedQuads) - m_vCopyBuffer.push_back(pLayer->m_vQuads[Selected]); - } - else if(Input()->KeyPress(KEY_V) && Input()->ModifierIsPressed() && !s_Pasted && !m_vCopyBuffer.empty()) - { - std::shared_ptr pLayer = std::static_pointer_cast(GetSelectedLayerType(0, LAYERTYPE_QUADS)); - - std::shared_ptr pLayerQuads = std::make_shared(); - pLayerQuads->m_pEditor = pLayer->m_pEditor; - pLayerQuads->m_Image = pLayer->m_Image; - - int MinX = m_vCopyBuffer.front().m_aPoints[4].x; - int MinY = m_vCopyBuffer.front().m_aPoints[4].y; - for(auto Quad : m_vCopyBuffer) - { - MinX = minimum(MinX, Quad.m_aPoints[4].x); - MinY = minimum(MinY, Quad.m_aPoints[4].y); - } - for(auto Quad : m_vCopyBuffer) - { - for(auto &Point : Quad.m_aPoints) - { - Point.x -= MinX; - Point.y -= MinY; - } - pLayerQuads->m_vQuads.push_back(Quad); - } - - m_pBrush->Clear(); - m_pBrush->AddLayer(pLayerQuads); - - DeselectQuads(); - DeselectQuadPoints(); - } - else if(!Input()->ModifierIsPressed()) - s_Pasted = false; - IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale); Graphics()->QuadsDraw(&QuadItem, 1); } @@ -1645,7 +1560,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) float py = fx2f(pQuad->m_aPoints[V].y); // draw selection background - if(IsQuadPointSelected(QuadIndex, V)) + if(IsQuadPointSelected(V)) { Graphics()->SetColor(0, 0, 0, 1); IGraphics::CQuadItem QuadItem(px, py, 7.0f * m_MouseWScale, 7.0f * m_MouseWScale); @@ -1678,8 +1593,8 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) if(x * x + y * y > 20.0f) { - if(!IsQuadPointSelected(QuadIndex, V)) - SelectQuadPoint(QuadIndex, V); + if(!IsQuadPointSelected(V)) + SelectQuadPoint(V); if(Input()->ShiftIsPressed()) { @@ -1701,34 +1616,30 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) int OffsetX = f2fx(x) - pQuad->m_aPoints[V].x; int OffsetY = f2fx(y) - pQuad->m_aPoints[V].y; - for(auto [pSelectedQuad, SelectedPoints] : GetSelectedQuadPoints()) + for(int m = 0; m < 4; m++) { - for(int m = 0; m < 4; m++) + if(IsQuadPointSelected(m)) { - if(SelectedPoints & (1 << m)) - { - pSelectedQuad->m_aPoints[m].x += OffsetX; - pSelectedQuad->m_aPoints[m].y += OffsetY; - } + pQuad->m_aPoints[m].x += OffsetX; + pQuad->m_aPoints[m].y += OffsetY; } } } else if(s_Operation == OP_MOVEUV) { - for(auto [pSelectedQuad, SelectedPoints] : GetSelectedQuadPoints()) + for(int m = 0; m < 4; m++) { - for(int m = 0; m < 4; m++) - if(SelectedPoints & (1 << m)) - { - // 0,2;1,3 - line x - // 0,1;2,3 - line y + if(IsQuadPointSelected(m)) + { + // 0,2;1,3 - line x + // 0,1;2,3 - line y - pSelectedQuad->m_aTexcoords[m].x += f2fx(m_MouseDeltaWx * 0.001f); - pSelectedQuad->m_aTexcoords[(m + 2) % 4].x += f2fx(m_MouseDeltaWx * 0.001f); + pQuad->m_aTexcoords[m].x += f2fx(m_MouseDeltaWx * 0.001f); + pQuad->m_aTexcoords[(m + 2) % 4].x += f2fx(m_MouseDeltaWx * 0.001f); - pSelectedQuad->m_aTexcoords[m].y += f2fx(m_MouseDeltaWy * 0.001f); - pSelectedQuad->m_aTexcoords[m ^ 1].y += f2fx(m_MouseDeltaWy * 0.001f); - } + pQuad->m_aTexcoords[m].y += f2fx(m_MouseDeltaWy * 0.001f); + pQuad->m_aTexcoords[m ^ 1].y += f2fx(m_MouseDeltaWy * 0.001f); + } } } } @@ -1739,8 +1650,11 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) { if(m_vSelectedLayers.size() == 1) { + if(!IsQuadSelected(QuadIndex)) + SelectQuad(QuadIndex); + m_SelectedQuadPoint = V; - m_SelectedQuadIndex = FindSelectedQuadPointIndex(QuadIndex); + m_SelectedQuadIndex = FindSelectedQuadIndex(QuadIndex); static SPopupMenuId s_PopupPointId; UI()->DoPopupMenu(&s_PopupPointId, UI()->MouseX(), UI()->MouseY(), 120, 75, this, PopupPoint); @@ -1755,9 +1669,9 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) if(s_Operation == OP_SELECT) { if(Input()->ShiftIsPressed()) - ToggleSelectQuadPoint(QuadIndex, V); + ToggleSelectQuadPoint(V); else - SelectQuadPoint(QuadIndex, V); + SelectQuadPoint(V); } UI()->DisableMouseLock(); @@ -1789,8 +1703,8 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) UI()->SetActiveItem(pID); - if(!IsQuadPointSelected(QuadIndex, V)) - SelectQuadPoint(QuadIndex, V); + if(!IsQuadPointSelected(V)) + SelectQuadPoint(V); } } else @@ -2475,7 +2389,7 @@ void CEditor::DoMapEditor(CUIRect View) m_pTooltip = Explain(Explanation, (int)wx / 32 + (int)wy / 32 * 16, Layer); } else if(m_pBrush->IsEmpty() && GetSelectedLayerType(0, LAYERTYPE_QUADS) != nullptr) - m_pTooltip = "Hold shift to select multiple quads. Use ctrl + c and ctrl + v to copy and paste quads. Press R to rotate selected quads."; + m_pTooltip = "Use left mouse button to drag and create a brush. Hold shift to select multiple quads. Press R to rotate selected quads. Use ctrl+right mouse to select layer."; else if(m_pBrush->IsEmpty()) m_pTooltip = "Use left mouse button to drag and create a brush. Use ctrl+right mouse to select layer."; else @@ -2531,31 +2445,16 @@ void CEditor::DoMapEditor(CUIRect View) if(!UI()->MouseButton(0)) { std::shared_ptr pQuadLayer = std::static_pointer_cast(GetSelectedLayerType(0, LAYERTYPE_QUADS)); - if(pQuadLayer) + if(Input()->ShiftIsPressed() && pQuadLayer) { - if(!Input()->ShiftIsPressed()) - { - DeselectQuads(); - DeselectQuadPoints(); - } - for(size_t i = 0; i < pQuadLayer->m_vQuads.size(); i++) { - CQuad *pQuad = &pQuadLayer->m_vQuads[i]; + const CQuad &Quad = pQuadLayer->m_vQuads[i]; + float px = fx2f(Quad.m_aPoints[4].x); + float py = fx2f(Quad.m_aPoints[4].y); - for(size_t v = 0; v < 5; v++) - { - float px = fx2f(pQuad->m_aPoints[v].x); - float py = fx2f(pQuad->m_aPoints[v].y); - - if(px > r.x && px < r.x + r.w && py > r.y && py < r.y + r.h) - { - if(v == 4) - ToggleSelectQuad(i); - else - ToggleSelectQuadPoint(i, v); - } - } + if(r.Inside(px, py) && !IsQuadSelected(i)) + SelectQuad(i); } } else diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 851f62f0b..2ca9712cf 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -940,7 +940,6 @@ public: void RenderMousePointer(); std::vector GetSelectedQuads(); - std::vector> GetSelectedQuadPoints(); std::shared_ptr GetSelectedLayerType(int Index, int Type) const; std::shared_ptr GetSelectedLayer(int Index) const; std::shared_ptr GetSelectedGroup() const; @@ -951,13 +950,12 @@ public: void ToggleSelectQuad(int Index); void DeselectQuads(); void DeselectQuadPoints(); - void SelectQuadPoint(int QuadIndex, int Index); - void ToggleSelectQuadPoint(int QuadIndex, int Index); + void SelectQuadPoint(int Index); + void ToggleSelectQuadPoint(int Index); void DeleteSelectedQuads(); bool IsQuadSelected(int Index) const; - bool IsQuadPointSelected(int QuadIndex, int Index) const; + bool IsQuadPointSelected(int Index) const; int FindSelectedQuadIndex(int Index) const; - int FindSelectedQuadPointIndex(int QuadIndex) const; int FindEnvPointIndex(int Index, int Channel) const; void SelectEnvPoint(int Index); @@ -1183,7 +1181,7 @@ public: int m_SelectedQuadPoint; int m_SelectedQuadIndex; int m_SelectedGroup; - std::vector> m_vSelectedQuadPoints; + int m_SelectedQuadPoints; int m_SelectedEnvelope; std::vector> m_vSelectedEnvelopePoints; int m_SelectedQuadEnvelope; @@ -1195,8 +1193,6 @@ public: std::pair m_SelectedTangentOutPoint; bool m_UpdateEnvPointInfo; - std::vector m_vCopyBuffer; - bool m_QuadKnifeActive; int m_QuadKnifeCount; vec2 m_aQuadKnifePoints[4]; diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index ca7a7f300..5b464566e 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1209,10 +1209,10 @@ CUI::EPopupMenuFunctionResult CEditor::PopupSource(void *pContext, CUIRect View, CUI::EPopupMenuFunctionResult CEditor::PopupPoint(void *pContext, CUIRect View, bool Active) { CEditor *pEditor = static_cast(pContext); - std::vector> vpQuads = pEditor->GetSelectedQuadPoints(); + std::vector vpQuads = pEditor->GetSelectedQuads(); if(!in_range(pEditor->m_SelectedQuadIndex, 0, vpQuads.size() - 1)) return CUI::POPUP_CLOSE_CURRENT; - CQuad *pCurrentQuad = vpQuads[pEditor->m_SelectedQuadIndex].first; + CQuad *pCurrentQuad = vpQuads[pEditor->m_SelectedQuadIndex]; enum { @@ -1252,25 +1252,25 @@ CUI::EPopupMenuFunctionResult CEditor::PopupPoint(void *pContext, CUIRect View, pEditor->m_Map.OnModify(); } - for(auto [pQuad, SelectedPoints] : vpQuads) + for(CQuad *pQuad : vpQuads) { if(Prop == PROP_POS_X) { for(int v = 0; v < 4; v++) - if(SelectedPoints & (1 << v)) + if(pEditor->IsQuadSelected(v)) pQuad->m_aPoints[v].x = i2fx(fx2i(pQuad->m_aPoints[v].x) + NewVal - X); } else if(Prop == PROP_POS_Y) { for(int v = 0; v < 4; v++) - if(SelectedPoints & (1 << v)) + if(pEditor->IsQuadSelected(v)) pQuad->m_aPoints[v].y = i2fx(fx2i(pQuad->m_aPoints[v].y) + NewVal - Y); } else if(Prop == PROP_COLOR) { for(int v = 0; v < 4; v++) { - if(SelectedPoints & (1 << v)) + if(pEditor->IsQuadSelected(v)) { pQuad->m_aColors[v].r = (NewVal >> 24) & 0xff; pQuad->m_aColors[v].g = (NewVal >> 16) & 0xff; @@ -1282,13 +1282,13 @@ CUI::EPopupMenuFunctionResult CEditor::PopupPoint(void *pContext, CUIRect View, else if(Prop == PROP_TEX_U) { for(int v = 0; v < 4; v++) - if(SelectedPoints & (1 << v)) + if(pEditor->IsQuadSelected(v)) pQuad->m_aTexcoords[v].x = f2fx(fx2f(pQuad->m_aTexcoords[v].x) + (NewVal - TextureU) / 1024.0f); } else if(Prop == PROP_TEX_V) { for(int v = 0; v < 4; v++) - if(SelectedPoints & (1 << v)) + if(pEditor->IsQuadSelected(v)) pQuad->m_aTexcoords[v].y = f2fx(fx2f(pQuad->m_aTexcoords[v].y) + (NewVal - TextureV) / 1024.0f); } } From 50d0efb410359e777be15eb131f688fb781d6022 Mon Sep 17 00:00:00 2001 From: furo Date: Wed, 6 Sep 2023 21:03:51 +0200 Subject: [PATCH 069/126] Add "render cut to demo" --- data/languages/arabic.txt | 7 +- data/languages/belarusian.txt | 3 + data/languages/bosnian.txt | 7 +- data/languages/brazilian_portuguese.txt | 3 + data/languages/bulgarian.txt | 23 +- data/languages/catalan.txt | 7 +- data/languages/chuvash.txt | 23 +- data/languages/czech.txt | 7 +- data/languages/danish.txt | 7 +- data/languages/dutch.txt | 3 + data/languages/esperanto.txt | 33 +- data/languages/finnish.txt | 13 +- data/languages/french.txt | 3 + data/languages/galician.txt | 3 + data/languages/german.txt | 3 + data/languages/greek.txt | 23 +- data/languages/hungarian.txt | 3 + data/languages/italian.txt | 625 +++++++++++----------- data/languages/japanese.txt | 7 +- data/languages/korean.txt | 3 + data/languages/kyrgyz.txt | 23 +- data/languages/norwegian.txt | 7 +- data/languages/persian.txt | 3 + data/languages/polish.txt | 3 + data/languages/portuguese.txt | 23 +- data/languages/romanian.txt | 23 +- data/languages/russian.txt | 3 + data/languages/serbian.txt | 12 +- data/languages/serbian_cyrillic.txt | 3 + data/languages/simplified_chinese.txt | 3 + data/languages/slovak.txt | 23 +- data/languages/spanish.txt | 3 + data/languages/swedish.txt | 3 + data/languages/traditional_chinese.txt | 3 + data/languages/turkish.txt | 3 + data/languages/ukrainian.txt | 3 + src/game/client/components/menus_demo.cpp | 25 +- 37 files changed, 549 insertions(+), 423 deletions(-) diff --git a/data/languages/arabic.txt b/data/languages/arabic.txt index 635ce0fba..3bbdbf04f 100644 --- a/data/languages/arabic.txt +++ b/data/languages/arabic.txt @@ -1397,6 +1397,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == @@ -1448,10 +1451,10 @@ Open the directory that contains the configuration and user files Open the directory to add custom themes == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download community skins diff --git a/data/languages/belarusian.txt b/data/languages/belarusian.txt index a07eb5f60..8520864d4 100644 --- a/data/languages/belarusian.txt +++ b/data/languages/belarusian.txt @@ -1727,6 +1727,9 @@ Unable to rename the folder (paused) == +Render cut to video +== + All combined == diff --git a/data/languages/bosnian.txt b/data/languages/bosnian.txt index 4238ef8c7..ff22d6d01 100644 --- a/data/languages/bosnian.txt +++ b/data/languages/bosnian.txt @@ -1289,6 +1289,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == @@ -1361,10 +1364,10 @@ Open the directory to add custom themes Max CSVs == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download skins diff --git a/data/languages/brazilian_portuguese.txt b/data/languages/brazilian_portuguese.txt index e168a9f4f..3b91fe32f 100644 --- a/data/languages/brazilian_portuguese.txt +++ b/data/languages/brazilian_portuguese.txt @@ -1767,3 +1767,6 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == Movido no jogo + +Render cut to video +== diff --git a/data/languages/bulgarian.txt b/data/languages/bulgarian.txt index 758e6dd96..7953c3b06 100644 --- a/data/languages/bulgarian.txt +++ b/data/languages/bulgarian.txt @@ -953,6 +953,9 @@ Cut length Remove chat == +Render cut to video +== + Please use a different name == @@ -1103,10 +1106,10 @@ Max CSVs Dummy settings == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download skins @@ -1160,10 +1163,16 @@ Show all Toggle dyncam == -Toggle dummy +Toggle ghost == -Toggle ghost +Converse +== + +Chat command +== + +Toggle dummy == Dummy copy @@ -1172,9 +1181,6 @@ Dummy copy Hammerfly dummy == -Converse -== - Spectate previous == @@ -1190,9 +1196,6 @@ Show entities Show HUD == -Chat command -== - Enable controller == diff --git a/data/languages/catalan.txt b/data/languages/catalan.txt index cb6b27649..59c7b24fd 100644 --- a/data/languages/catalan.txt +++ b/data/languages/catalan.txt @@ -1469,6 +1469,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == @@ -1514,10 +1517,10 @@ Open the directory that contains the configuration and user files Open the directory to add custom themes == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download community skins diff --git a/data/languages/chuvash.txt b/data/languages/chuvash.txt index 4a88c4c89..54e69dcc8 100644 --- a/data/languages/chuvash.txt +++ b/data/languages/chuvash.txt @@ -956,6 +956,9 @@ Cut length Remove chat == +Render cut to video +== + Please use a different name == @@ -1106,10 +1109,10 @@ Max CSVs Dummy settings == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download skins @@ -1163,10 +1166,16 @@ Show all Toggle dyncam == -Toggle dummy +Toggle ghost == -Toggle ghost +Converse +== + +Chat command +== + +Toggle dummy == Dummy copy @@ -1175,9 +1184,6 @@ Dummy copy Hammerfly dummy == -Converse -== - Statboard == @@ -1190,9 +1196,6 @@ Show entities Show HUD == -Chat command -== - Enable controller == diff --git a/data/languages/czech.txt b/data/languages/czech.txt index b4d037cd6..a5ae8529b 100644 --- a/data/languages/czech.txt +++ b/data/languages/czech.txt @@ -1407,6 +1407,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == @@ -1458,10 +1461,10 @@ Open the directory that contains the configuration and user files Open the directory to add custom themes == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download community skins diff --git a/data/languages/danish.txt b/data/languages/danish.txt index 5acece968..894a9591b 100644 --- a/data/languages/danish.txt +++ b/data/languages/danish.txt @@ -1405,6 +1405,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == @@ -1456,10 +1459,10 @@ Open the directory that contains the configuration and user files Open the directory to add custom themes == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download community skins diff --git a/data/languages/dutch.txt b/data/languages/dutch.txt index f07acc9dc..e52f4bd40 100644 --- a/data/languages/dutch.txt +++ b/data/languages/dutch.txt @@ -1515,6 +1515,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == diff --git a/data/languages/esperanto.txt b/data/languages/esperanto.txt index d63c9d2f2..078b353f9 100644 --- a/data/languages/esperanto.txt +++ b/data/languages/esperanto.txt @@ -900,6 +900,9 @@ Cut interval Cut length == +Render cut to video +== + File already exists, do you want to overwrite it? == @@ -1086,15 +1089,15 @@ Automatically create statboard csv Max CSVs == +Toggle to edit your dummy settings +== + Loading skin files == Your skin == -Toggle to edit your dummy settings -== - Download skins == @@ -1143,18 +1146,9 @@ Default zoom Toggle dyncam == -Toggle dummy -== - Toggle ghost == -Dummy copy -== - -Hammerfly dummy -== - Shotgun == @@ -1173,6 +1167,18 @@ Team chat Converse == +Chat command +== + +Toggle dummy +== + +Dummy copy +== + +Hammerfly dummy +== + Emoticon == @@ -1209,9 +1215,6 @@ Show entities Show HUD == -Chat command -== - Enable controller == diff --git a/data/languages/finnish.txt b/data/languages/finnish.txt index e1e6538f5..b7fba4673 100644 --- a/data/languages/finnish.txt +++ b/data/languages/finnish.txt @@ -1362,6 +1362,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == @@ -1422,10 +1425,10 @@ Automatically create statboard csv Max CSVs == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download community skins @@ -1443,15 +1446,15 @@ Create a random skin Open the directory to add custom skins == +Converse +== + Dummy copy == Hammerfly dummy == -Converse -== - Statboard == diff --git a/data/languages/french.txt b/data/languages/french.txt index ef1125be4..410c1e96b 100644 --- a/data/languages/french.txt +++ b/data/languages/french.txt @@ -1745,6 +1745,9 @@ Cut interval Cut length == +Render cut to video +== + All combined == diff --git a/data/languages/galician.txt b/data/languages/galician.txt index aee7e9697..869a64aea 100644 --- a/data/languages/galician.txt +++ b/data/languages/galician.txt @@ -1716,6 +1716,9 @@ Cut interval Cut length == +Render cut to video +== + All combined == diff --git a/data/languages/german.txt b/data/languages/german.txt index 5059fbe0c..853206e9b 100644 --- a/data/languages/german.txt +++ b/data/languages/german.txt @@ -1760,3 +1760,6 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == Im Spiel bewegt + +Render cut to video +== diff --git a/data/languages/greek.txt b/data/languages/greek.txt index df3bc468e..154bca9b7 100644 --- a/data/languages/greek.txt +++ b/data/languages/greek.txt @@ -959,6 +959,9 @@ Cut length Remove chat == +Render cut to video +== + Please use a different name == @@ -1109,10 +1112,10 @@ Max CSVs Dummy settings == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download skins @@ -1166,10 +1169,16 @@ Show all Toggle dyncam == -Toggle dummy +Toggle ghost == -Toggle ghost +Converse +== + +Chat command +== + +Toggle dummy == Dummy copy @@ -1178,9 +1187,6 @@ Dummy copy Hammerfly dummy == -Converse -== - Statboard == @@ -1193,9 +1199,6 @@ Show entities Show HUD == -Chat command -== - Enable controller == diff --git a/data/languages/hungarian.txt b/data/languages/hungarian.txt index 06de9910e..a13f4e393 100644 --- a/data/languages/hungarian.txt +++ b/data/languages/hungarian.txt @@ -1721,6 +1721,9 @@ Cut interval Cut length == +Render cut to video +== + All combined == diff --git a/data/languages/italian.txt b/data/languages/italian.txt index ea3fb2d78..529c11720 100644 --- a/data/languages/italian.txt +++ b/data/languages/italian.txt @@ -1167,51 +1167,6 @@ Grabs 9+ new mentions == +9 nuove menzioni -[Graphics error] -Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. -== - -[Graphics error] -Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution. -== - -[Graphics error] -An error during command recording occurred. Try to update your GPU drivers. -== - -[Graphics error] -A render command failed. Try to update your GPU drivers. -== - -[Graphics error] -Submitting the render commands failed. Try to update your GPU drivers. -== - -[Graphics error] -Failed to swap framebuffers. Try to update your GPU drivers. -== - -[Graphics error] -Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. -== - -[Graphics error] -Could not initialize the given graphics backend, reverting to the default backend now. -== - -[Graphics error] -Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. -== - -Could not save downloaded map. Try manually deleting this file: %s -== - -The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs. -== - -The format of texture %s is not RGBA which will cause visual bugs. -== - Preparing demo playback == Preparando la riproduzione della demo @@ -1221,9 +1176,6 @@ Connected Loading map file from storage == Caricando il file mappa dalla memoria -Why are you slowmo replaying to read this? -== - Initializing components == Inizializzando i componenti @@ -1302,55 +1254,18 @@ Join Tutorial Server Skip Tutorial == Salta Tutorial -Loading menu images -== - -AFR -== - -ASI -== - -AUS -== - -EUR -== - -NA -== - -SA -== - -CHN -== - Getting server list from master server == Ottenere l'elenco dei server dal server principale Are you sure that you want to disconnect and switch to a different server? == Sei sicuro di voler disconnetterti e passare a un altro server? -Copy info -== - -Leak IP -== - No server selected == Nessun server selezionato -Online players (%d) -== - Online clanmates (%d) == Compagni di clan online -[friends (server browser)] -Offline (%d) -== - Click to select server. Double click to join your friend. == Fare click per selezionare il server. Fai doppio click per unirti al tuo amico. @@ -1372,123 +1287,9 @@ Are you sure that you want to remove the clan '%s' from your friends list? Add Clan == Aggiungi Clan -Play the current demo -== - -Pause the current demo -== - -Stop the current demo -== - -Go back one tick -== - -Go forward one tick -== - -Slow down the demo -== - -Speed up the demo -== - -Mark the beginning of a cut (right click to reset) -== - -Mark the end of a cut (right click to reset) -== - -Export cut as a separate demo -== - -Go back one marker -== - -Go forward one marker -== - -Close the demo player -== - -Toggle keyboard shortcuts -== - -Export demo cut -== - -Cut interval -== - -Cut length -== - -Loading demo files -== - -All combined -== - -Folder Link -== - -Markers: -== - -%.2f MiB -== - -%.2f KiB -== - -Open the directory that contains the demo files -== - -Are you sure that you want to delete the folder '%s'? -== - -Are you sure that you want to delete the demo '%s'? -== - Delete folder == Cancella cartella -Unable to delete the demo '%s' -== - -Unable to delete the folder '%s'. Make sure it's empty first. -== - -Loading ghost files -== - -Menu opened. Press Esc key again to close menu. -== - -Save power by lowering refresh rate (higher input latency) -== - -Settings file -== - -Open the settings file -== - -Config directory -== - -Open the directory that contains the configuration and user files -== - -Open the directory to add custom themes -== - -Loading skin files -== - -Toggle to edit your dummy settings -== - Download community skins == Scarica skin comunitá @@ -1501,18 +1302,9 @@ Create a random skin Open the directory to add custom skins == Apri la cartella per aggiungere una skin custom -Converse -== - -Chat command -== - Enable controller == Abilitá controller -Controller -== - Ingame controller mode == Modalitá controller in gioco @@ -1520,19 +1312,6 @@ Ingame controller mode Relative == Relativo -[Ingame controller mode] -Absolute -== - -Ingame controller sens. -== - -UI controller sens. -== - -Controller jitter tolerance -== - No controller found. Plug in a controller. == Nessun controller trovato. Collega un controller. @@ -1542,12 +1321,6 @@ Axis Status == Stato -Aim bind -== - -Mouse -== - Ingame mouse sens. == Sens. mouse in gioco @@ -1563,9 +1336,6 @@ Are you sure that you want to reset the controls to their defaults? Cancel == Cancella -Dummy -== - Windowed == Finestra @@ -1578,42 +1348,15 @@ Windowed fullscreen Desktop fullscreen == Desktop schermo intero -Allows maps to render with more detail -== - -Renderer -== - -default -== - -custom -== - Graphics card == Scheda grafica -auto -== - -Appearance -== - -Name Plate -== - Hook Collisions == Collisione Hook -Kill Messages -== - Show health, shields and ammo == Mostra vita, scudi e munizioni -DDRace HUD -== - Show client IDs in scoreboard == Mostra gli ID clienti nella scoreboard @@ -1671,51 +1414,12 @@ Nothing hookable Something hookable == Qualcosa hookabile -A Tee -== - -Normal Color -== - -Highlight Color -== - Weapons == Armi -Rifle Laser Outline Color -== - -Rifle Laser Inner Color -== - -Shotgun Laser Outline Color -== - -Shotgun Laser Inner Color -== - -Door Laser Outline Color -== - -Door Laser Inner Color -== - -Freeze Laser Outline Color -== - -Freeze Laser Inner Color -== - -Set all to Rifle -== - When you cross the start line, show a ghost tee replicating the movements of your best time == Quando attraversi la linea di partenza, mostra una maglietta fantasma che riproduce i movimenti del tuo miglior tempo -Overlay entities -== - Opacity == Opacitá @@ -1746,15 +1450,326 @@ Chat command (e.g. showall 1) Unregister protocol and file extensions == Protocollo ed estensioni file non registrati -Extras -== - Loading assets == Caricando assets Open the directory to add custom assets == Apri la cartella per aggiungere assets custom +Can't find a Tutorial server +== Impossibile trovare un Server Tutorial + +Loading race demo files +== Caricando i file della demo della gara + +Loading sound files +== Caricando file musica + +Moved ingame +== Spostato in gioco + +[Graphics error] +Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. +== + +[Graphics error] +Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution. +== + +[Graphics error] +An error during command recording occurred. Try to update your GPU drivers. +== + +[Graphics error] +A render command failed. Try to update your GPU drivers. +== + +[Graphics error] +Submitting the render commands failed. Try to update your GPU drivers. +== + +[Graphics error] +Failed to swap framebuffers. Try to update your GPU drivers. +== + +[Graphics error] +Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. +== + +[Graphics error] +Could not initialize the given graphics backend, reverting to the default backend now. +== + +[Graphics error] +Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. +== + +Could not save downloaded map. Try manually deleting this file: %s +== + +The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs. +== + +The format of texture %s is not RGBA which will cause visual bugs. +== + +Why are you slowmo replaying to read this? +== + +Loading menu images +== + +AFR +== + +ASI +== + +AUS +== + +EUR +== + +NA +== + +SA +== + +CHN +== + +Copy info +== + +Leak IP +== + +Online players (%d) +== + +[friends (server browser)] +Offline (%d) +== + +Play the current demo +== + +Pause the current demo +== + +Stop the current demo +== + +Go back one tick +== + +Go forward one tick +== + +Slow down the demo +== + +Speed up the demo +== + +Mark the beginning of a cut (right click to reset) +== + +Mark the end of a cut (right click to reset) +== + +Export cut as a separate demo +== + +Go back one marker +== + +Go forward one marker +== + +Close the demo player +== + +Toggle keyboard shortcuts +== + +Export demo cut +== + +Cut interval +== + +Cut length +== + +Render cut to video +== + +Loading demo files +== + +All combined +== + +Folder Link +== + +Markers: +== + +%.2f MiB +== + +%.2f KiB +== + +Open the directory that contains the demo files +== + +Are you sure that you want to delete the folder '%s'? +== + +Are you sure that you want to delete the demo '%s'? +== + +Unable to delete the demo '%s' +== + +Unable to delete the folder '%s'. Make sure it's empty first. +== + +Loading ghost files +== + +Menu opened. Press Esc key again to close menu. +== + +Save power by lowering refresh rate (higher input latency) +== + +Settings file +== + +Open the settings file +== + +Config directory +== + +Open the directory that contains the configuration and user files +== + +Open the directory to add custom themes +== + +Toggle to edit your dummy settings +== + +Loading skin files +== + +Converse +== + +Chat command +== + +Controller +== + +[Ingame controller mode] +Absolute +== + +Ingame controller sens. +== + +UI controller sens. +== + +Controller jitter tolerance +== + +Aim bind +== + +Mouse +== + +Dummy +== + +Allows maps to render with more detail +== + +Renderer +== + +default +== + +custom +== + +auto +== + +Appearance +== + +Name Plate +== + +Kill Messages +== + +DDRace HUD +== + +A Tee +== + +Normal Color +== + +Highlight Color +== + +Rifle Laser Outline Color +== + +Rifle Laser Inner Color +== + +Shotgun Laser Outline Color +== + +Shotgun Laser Inner Color +== + +Door Laser Outline Color +== + +Door Laser Inner Color +== + +Freeze Laser Outline Color +== + +Freeze Laser Inner Color +== + +Set all to Rifle +== + +Overlay entities +== + +Extras +== + Discord == @@ -1764,20 +1779,8 @@ https://ddnet.org/discord Tutorial == -Can't find a Tutorial server -== Impossibile trovare un Server Tutorial - -Loading race demo files -== Caricando i file della demo della gara - Super == -Loading sound files -== Caricando file musica - FPM == - -Moved ingame -== Spostato in gioco diff --git a/data/languages/japanese.txt b/data/languages/japanese.txt index 4e9459ab8..04a4312e5 100644 --- a/data/languages/japanese.txt +++ b/data/languages/japanese.txt @@ -1437,6 +1437,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == @@ -1488,10 +1491,10 @@ Open the directory that contains the configuration and user files Open the directory to add custom themes == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download community skins diff --git a/data/languages/korean.txt b/data/languages/korean.txt index 469462054..1e23777d6 100644 --- a/data/languages/korean.txt +++ b/data/languages/korean.txt @@ -1733,6 +1733,9 @@ Cut interval Cut length == +Render cut to video +== + All combined == diff --git a/data/languages/kyrgyz.txt b/data/languages/kyrgyz.txt index 70744b19d..fe3b4294e 100644 --- a/data/languages/kyrgyz.txt +++ b/data/languages/kyrgyz.txt @@ -950,6 +950,9 @@ Cut length Remove chat == +Render cut to video +== + Please use a different name == @@ -1100,10 +1103,10 @@ Max CSVs Dummy settings == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download skins @@ -1157,10 +1160,16 @@ Show all Toggle dyncam == -Toggle dummy +Toggle ghost == -Toggle ghost +Converse +== + +Chat command +== + +Toggle dummy == Dummy copy @@ -1169,9 +1178,6 @@ Dummy copy Hammerfly dummy == -Converse -== - Statboard == @@ -1184,9 +1190,6 @@ Show entities Show HUD == -Chat command -== - Enable controller == diff --git a/data/languages/norwegian.txt b/data/languages/norwegian.txt index 77e1a890c..2a02ee0d0 100644 --- a/data/languages/norwegian.txt +++ b/data/languages/norwegian.txt @@ -1406,6 +1406,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == @@ -1457,10 +1460,10 @@ Open the directory that contains the configuration and user files Open the directory to add custom themes == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download community skins diff --git a/data/languages/persian.txt b/data/languages/persian.txt index 255193ba1..ca46aa734 100644 --- a/data/languages/persian.txt +++ b/data/languages/persian.txt @@ -1703,6 +1703,9 @@ Cut interval Cut length == +Render cut to video +== + All combined == diff --git a/data/languages/polish.txt b/data/languages/polish.txt index 8ea6538ad..000e23a98 100644 --- a/data/languages/polish.txt +++ b/data/languages/polish.txt @@ -1507,6 +1507,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == diff --git a/data/languages/portuguese.txt b/data/languages/portuguese.txt index ce8cca8cf..525155ea5 100644 --- a/data/languages/portuguese.txt +++ b/data/languages/portuguese.txt @@ -1225,6 +1225,9 @@ Cut interval Cut length == +Render cut to video +== + Loading demo files == @@ -1309,10 +1312,10 @@ Open the directory that contains the configuration and user files Open the directory to add custom themes == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download skins @@ -1357,10 +1360,16 @@ Show all Toggle dyncam == -Toggle dummy +Toggle ghost == -Toggle ghost +Converse +== + +Chat command +== + +Toggle dummy == Dummy copy @@ -1369,9 +1378,6 @@ Dummy copy Hammerfly dummy == -Converse -== - Statboard == @@ -1384,9 +1390,6 @@ Show entities Show HUD == -Chat command -== - Enable controller == diff --git a/data/languages/romanian.txt b/data/languages/romanian.txt index b446bcd93..fa6099415 100644 --- a/data/languages/romanian.txt +++ b/data/languages/romanian.txt @@ -965,6 +965,9 @@ Cut length Remove chat == +Render cut to video +== + Please use a different name == @@ -1115,10 +1118,10 @@ Max CSVs Dummy settings == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download skins @@ -1172,10 +1175,16 @@ Show all Toggle dyncam == -Toggle dummy +Toggle ghost == -Toggle ghost +Converse +== + +Chat command +== + +Toggle dummy == Dummy copy @@ -1184,9 +1193,6 @@ Dummy copy Hammerfly dummy == -Converse -== - Statboard == @@ -1199,9 +1205,6 @@ Show entities Show HUD == -Chat command -== - Enable controller == diff --git a/data/languages/russian.txt b/data/languages/russian.txt index e1dfbe068..f6ce50c37 100644 --- a/data/languages/russian.txt +++ b/data/languages/russian.txt @@ -1756,3 +1756,6 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == Перемещены в игре + +Render cut to video +== diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt index 72a309e08..a036086b1 100644 --- a/data/languages/serbian.txt +++ b/data/languages/serbian.txt @@ -1507,11 +1507,6 @@ A render command failed. Try to update your GPU drivers. Submitting the render commands failed. Try to update your GPU drivers. == Неуспешно слање рендерних команди. Покушајте ажурирати драјвере за вашу графичку картицу. -[Graphics error] -Failed to swap framebuffers. Try to update your GPU drivers. -== - -[Graphics error] Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. == Неуспешно замена бафера фреймова. Покушајте ажурирати драјвере за вашу графичку картицу. @@ -1748,3 +1743,10 @@ Open the directory to add custom assets Moved ingame == Померено у игри + +[Graphics error] +Failed to swap framebuffers. Try to update your GPU drivers. +== + +Render cut to video +== diff --git a/data/languages/serbian_cyrillic.txt b/data/languages/serbian_cyrillic.txt index 97f7586a1..ccbe933be 100644 --- a/data/languages/serbian_cyrillic.txt +++ b/data/languages/serbian_cyrillic.txt @@ -1643,6 +1643,9 @@ Cut interval Cut length == +Render cut to video +== + All combined == diff --git a/data/languages/simplified_chinese.txt b/data/languages/simplified_chinese.txt index ff783ed84..f1dccce9f 100644 --- a/data/languages/simplified_chinese.txt +++ b/data/languages/simplified_chinese.txt @@ -1780,3 +1780,6 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == 游戏内移动 + +Render cut to video +== diff --git a/data/languages/slovak.txt b/data/languages/slovak.txt index fb8fc15c1..3c6d21bd7 100644 --- a/data/languages/slovak.txt +++ b/data/languages/slovak.txt @@ -956,6 +956,9 @@ Cut length Remove chat == +Render cut to video +== + Please use a different name == @@ -1106,10 +1109,10 @@ Max CSVs Dummy settings == -Loading skin files +Toggle to edit your dummy settings == -Toggle to edit your dummy settings +Loading skin files == Download skins @@ -1163,10 +1166,16 @@ Show all Toggle dyncam == -Toggle dummy +Toggle ghost == -Toggle ghost +Converse +== + +Chat command +== + +Toggle dummy == Dummy copy @@ -1175,9 +1184,6 @@ Dummy copy Hammerfly dummy == -Converse -== - Statboard == @@ -1190,9 +1196,6 @@ Show entities Show HUD == -Chat command -== - Enable controller == diff --git a/data/languages/spanish.txt b/data/languages/spanish.txt index 593fa8238..947d291f7 100644 --- a/data/languages/spanish.txt +++ b/data/languages/spanish.txt @@ -1763,3 +1763,6 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == Movido dentro del juego + +Render cut to video +== diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt index 12a7e5f87..212739c23 100644 --- a/data/languages/swedish.txt +++ b/data/languages/swedish.txt @@ -1747,3 +1747,6 @@ Loading sound files Moved ingame == Förflyttades i spelet + +Render cut to video +== Rendera snitt till video diff --git a/data/languages/traditional_chinese.txt b/data/languages/traditional_chinese.txt index 844f03e2e..0f66cdad1 100644 --- a/data/languages/traditional_chinese.txt +++ b/data/languages/traditional_chinese.txt @@ -1769,3 +1769,6 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == 遊戲內移動 + +Render cut to video +== diff --git a/data/languages/turkish.txt b/data/languages/turkish.txt index 6784b7da0..2d232b228 100644 --- a/data/languages/turkish.txt +++ b/data/languages/turkish.txt @@ -1751,3 +1751,6 @@ Loading sound files Moved ingame == Hareket edildi + +Render cut to video +== diff --git a/data/languages/ukrainian.txt b/data/languages/ukrainian.txt index 41cc3d3a5..55d5c5000 100644 --- a/data/languages/ukrainian.txt +++ b/data/languages/ukrainian.txt @@ -1744,3 +1744,6 @@ Graphics card Moved ingame == Перемістився в грі + +Render cut to video +== diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 3e36b7ddd..87bb6caf4 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -726,13 +726,22 @@ void CMenus::RenderDemoPlayerSliceSavePopup(CUIRect MainView) // remove chat checkbox static int s_RemoveChat = 0; - CUIRect RemoveChatCheckBox; - Box.HSplitTop(24.0f, &RemoveChatCheckBox, &Box); + + CUIRect CheckBoxBar, RemoveChatCheckBox, RenderCutCheckBox; + Box.HSplitTop(24.0f, &CheckBoxBar, &Box); Box.HSplitTop(20.0f, nullptr, &Box); + CheckBoxBar.VSplitMid(&RemoveChatCheckBox, &RenderCutCheckBox, 40.0f); if(DoButton_CheckBox(&s_RemoveChat, Localize("Remove chat"), s_RemoveChat, &RemoveChatCheckBox)) { s_RemoveChat ^= 1; } +#if defined(CONF_VIDEORECORDER) + static int s_RenderCut = 0; + if(DoButton_CheckBox(&s_RenderCut, Localize("Render cut to video"), s_RenderCut, &RenderCutCheckBox)) + { + s_RenderCut ^= 1; + } +#endif // buttons CUIRect ButtonBar, AbortButton, OkButton; @@ -784,10 +793,20 @@ void CMenus::RenderDemoPlayerSliceSavePopup(CUIRect MainView) str_copy(m_aCurrentDemoSelectionName, m_DemoSliceInput.GetString()); if(str_endswith(m_aCurrentDemoSelectionName, ".demo")) m_aCurrentDemoSelectionName[str_length(m_aCurrentDemoSelectionName) - str_length(".demo")] = '\0'; - m_DemoPlayerState = DEMOPLAYER_NONE; + Client()->DemoSlice(aPath, CMenus::DemoFilterChat, &s_RemoveChat); DemolistPopulate(); DemolistOnUpdate(false); + m_DemoPlayerState = DEMOPLAYER_NONE; +#if defined(CONF_VIDEORECORDER) + if(s_RenderCut) + { + m_Popup = POPUP_RENDER_DEMO; + m_StartPaused = false; + m_DemoRenderInput.Set(m_aCurrentDemoSelectionName); + UI()->SetActiveItem(&m_DemoRenderInput); + } +#endif } if(s_ConfirmPopupContext.m_Result != CUI::SConfirmPopupContext::UNSET) { From a9157e8385278a7d687a25f8ae035161a8e5ab52 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Fri, 8 Sep 2023 00:10:27 +0300 Subject: [PATCH 070/126] CI: Move ASan/UBSan to own build directory --- .github/workflows/clang-sanitizer.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/clang-sanitizer.yml b/.github/workflows/clang-sanitizer.yml index 59873154e..3f46d8364 100644 --- a/.github/workflows/clang-sanitizer.yml +++ b/.github/workflows/clang-sanitizer.yml @@ -24,14 +24,17 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Build with ASan and UBSan run: | + mkdir clang-sanitizer + cd clang-sanitizer export CC=clang export CXX=clang++ export CXXFLAGS="-fsanitize=address,undefined -fsanitize-recover=address,undefined -fno-omit-frame-pointer" export CFLAGS="-fsanitize=address,undefined -fsanitize-recover=address,undefined -fno-omit-frame-pointer" - cmake -DCMAKE_BUILD_TYPE=Debug -DHEADLESS_CLIENT=ON -Werror=dev -DDOWNLOAD_GTEST=ON -DDEV=ON -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG=. . + cmake -DCMAKE_BUILD_TYPE=Debug -DHEADLESS_CLIENT=ON -Werror=dev -DDOWNLOAD_GTEST=ON -DDEV=ON -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG=. .. make -j"$(nproc)" - name: Run server and headless client with ASan and UBSan run: | + cd clang-sanitizer export UBSAN_OPTIONS=suppressions=./ubsan.supp:log_path=./SAN:print_stacktrace=1:halt_on_errors=0 export ASAN_OPTIONS=log_path=./SAN:print_stacktrace=1:check_initialization_order=1:detect_leaks=1:halt_on_errors=0 export LSAN_OPTIONS=suppressions=./lsan.supp @@ -44,6 +47,7 @@ jobs: fi - name: Run unit tests with ASan and UBSan run: | + cd clang-sanitizer cmake --build . --config Debug --target run_cxx_tests # Rust tests work locally, but still not in CI, even with the same directory if test -n "$(find . -maxdepth 1 -name 'SAN.*' -print -quit)" @@ -53,6 +57,7 @@ jobs: fi - name: Run integration tests with ASan and UBSan run: | + cd clang-sanitizer make run_integration_tests if test -n "$(find . -maxdepth 1 -name 'SAN.*' -print -quit)" then From 225b1758894bcf8bccf002476961ab71279e9250 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Thu, 7 Sep 2023 22:18:40 +0300 Subject: [PATCH 071/126] CMake: Set the minimum CMake version to 3.12 In fact this version is required after 00a0e0e72380cdd68c5332e73a2a1f603c65cfc0 (FindPython3 added in 3.12) --- CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5461196e3..4713756e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12...3.19.1) -if(CMAKE_VERSION VERSION_LESS 3.12) - cmake_policy(VERSION ${CMAKE_VERSION}) -endif() +cmake_minimum_required(VERSION 3.12...3.27.4) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15 CACHE INTERNAL "Minimum macOS deployment version") if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.15) From d0ecb5f8dda2332e6121829faaa17b488e38e4f4 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Thu, 7 Sep 2023 23:03:29 +0300 Subject: [PATCH 072/126] CMake: Rework the versioning --- CMakeLists.txt | 62 ++++++---------------- src/engine/client/updater.cpp | 2 + src/game/client/components/menus_start.cpp | 1 + src/game/{version.h => version.h.in} | 7 ++- 4 files changed, 21 insertions(+), 51 deletions(-) rename src/game/{version.h => version.h.in} (79%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4713756e2..2b0f64608 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,40 +5,15 @@ if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.15) message(WARNING "Building for macOS < 10.15 is not supported") endif() -file(STRINGS src/game/version.h VERSION_LINE - LIMIT_COUNT 1 - REGEX "^#define GAME_RELEASE_VERSION " +# Set version if not explicitly set +if(NOT VERSION) + set(VERSION 17.2.1) +endif() + +project(DDNet + VERSION ${VERSION} ) -if(VERSION_LINE MATCHES "\"([0-9]+)\\.([0-9]+)\\.([0-9]+)\"") - set(VERSION_MAJOR ${CMAKE_MATCH_1}) - set(VERSION_MINOR ${CMAKE_MATCH_2}) - set(VERSION_PATCH ${CMAKE_MATCH_3}) -elseif(VERSION_LINE MATCHES "\"([0-9]+)\\.([0-9]+)\"") - set(VERSION_MAJOR ${CMAKE_MATCH_1}) - set(VERSION_MINOR ${CMAKE_MATCH_2}) - set(VERSION_PATCH "0") -else() - message(FATAL_ERROR "Couldn't parse version from src/game/version.h") -endif() - -# Extra support for CMake pre-3.0 -if(NOT POLICY CMP0048) - set(PROJECT_VERSION_MAJOR ${VERSION_MAJOR}) - set(PROJECT_VERSION_MINOR ${VERSION_MINOR}) - set(PROJECT_VERSION_PATCH ${VERSION_PATCH}) - if(VERSION_PATCH STREQUAL "0") - set(PROJECT_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}) - else() - set(PROJECT_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) - endif() -endif() -if(VERSION_PATCH STREQUAL "0") - project(DDNet VERSION ${VERSION_MAJOR}.${VERSION_MINOR}) -else() - project(DDNet VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) -endif() - set(ORIGINAL_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}) set(ORIGINAL_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) set(ORIGINAL_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) @@ -139,11 +114,6 @@ if(TEST_MYSQL) set(MYSQL ON) endif() -# Set version if not explicitly set -if(NOT VERSION) - set(VERSION ${PROJECT_VERSION}) -endif() - set(OpenGL_GL_PREFERENCE LEGACY) # Set the default build type to Release @@ -176,14 +146,14 @@ if(IPO) endif() endif() -if(CMAKE_VERSION VERSION_LESS 3.0) - configure_file(src/game/version.h vd.h) +if(${PROJECT_VERSION_MINOR} GREATER 9) + set(CLIENT_VERSIONNR ${PROJECT_VERSION_MAJOR}${PROJECT_VERSION_MINOR}${PROJECT_VERSION_PATCH}) else() - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS - src/game/version.h - ) + set(CLIENT_VERSIONNR ${PROJECT_VERSION_MAJOR}0${PROJECT_VERSION_MINOR}${PROJECT_VERSION_PATCH}) endif() +configure_file(src/game/version.h.in ${PROJECT_BINARY_DIR}/src/game/version.h) + set(SERVER_EXECUTABLE DDNet-Server CACHE STRING "Name of the built server executable") set(CLIENT_EXECUTABLE DDNet CACHE STRING "Name of the build client executable") @@ -413,7 +383,7 @@ function(set_glob VAR GLOBBING EXTS DIRECTORY) # ... endfunction() function(set_src VAR GLOBBING DIRECTORY) # ... - set_glob(${VAR} ${GLOBBING} "c;cpp;h" ${DIRECTORY} ${ARGN}) + set_glob(${VAR} ${GLOBBING} "c;cpp;h;h.in" ${DIRECTORY} ${ARGN}) set(${VAR} ${${VAR}} PARENT_SCOPE) set(CHECKSUM_SRC ${CHECKSUM_SRC} ${${VAR}} PARENT_SCOPE) endfunction() @@ -2040,7 +2010,7 @@ set_src(GAME_SHARED GLOB src/game teamscore.h tuning.h variables.h - version.h + version.h.in voting.h ) # A bit hacky, but these are needed to register all the UUIDs, even for stuff @@ -2067,6 +2037,7 @@ set(GAME_GENERATED_SHARED src/game/generated/protocol.h src/game/generated/protocol7.h src/game/generated/protocolglue.h + src/game/version.h ) set(DEPS ${DEP_JSON} ${DEP_MD5} ${ZLIB_DEP}) @@ -3386,9 +3357,6 @@ foreach(target ${TARGETS_OWN}) target_compile_definitions(${target} PRIVATE CONF_DISCORD_DYNAMIC) endif() endif() - if(VERSION) - target_compile_definitions(${target} PRIVATE GAME_RELEASE_VERSION="${VERSION}") - endif() if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") target_compile_definitions(${target} PRIVATE CONF_WEBASM) endif() diff --git a/src/engine/client/updater.cpp b/src/engine/client/updater.cpp index 716fe057c..16a03048b 100644 --- a/src/engine/client/updater.cpp +++ b/src/engine/client/updater.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include // system using std::map; diff --git a/src/game/client/components/menus_start.cpp b/src/game/client/components/menus_start.cpp index 580e1f9f1..791afbf06 100644 --- a/src/game/client/components/menus_start.cpp +++ b/src/game/client/components/menus_start.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "menus.h" diff --git a/src/game/version.h b/src/game/version.h.in similarity index 79% rename from src/game/version.h rename to src/game/version.h.in index 65ef0f9e0..87be713d3 100644 --- a/src/game/version.h +++ b/src/game/version.h.in @@ -2,12 +2,11 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #ifndef GAME_VERSION_H #define GAME_VERSION_H -#ifndef GAME_RELEASE_VERSION -#define GAME_RELEASE_VERSION "17.2.1" -#endif + +#define GAME_RELEASE_VERSION "${PROJECT_VERSION}" #define GAME_VERSION "0.6.4, " GAME_RELEASE_VERSION #define GAME_NETVERSION "0.6 626fce9a778df4d4" -#define CLIENT_VERSIONNR 17021 +#define CLIENT_VERSIONNR ${CLIENT_VERSIONNR} extern const char *GIT_SHORTREV_HASH; #define GAME_NAME "DDNet" #endif From f6172a2ec49ca3eb67ad626e1cea78b4478124d2 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Thu, 7 Sep 2023 23:03:49 +0300 Subject: [PATCH 073/126] CMake: Add version range validation --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b0f64608..9f6c2d501 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,12 @@ if(IPO) endif() endif() +if("0${PROJECT_VERSION_PATCH}" GREATER 9) + message(SEND_ERROR "Invalid project patch version (the version is \"${PROJECT_VERSION}\", the patch part must be in range 0-9 if set)") +endif() +if(${PROJECT_VERSION_MINOR} GREATER 99) + message(SEND_ERROR "Invalid project minor version (the version is \"${PROJECT_VERSION}\", the minor part must be in range 0-99)") +endif() if(${PROJECT_VERSION_MINOR} GREATER 9) set(CLIENT_VERSIONNR ${PROJECT_VERSION_MAJOR}${PROJECT_VERSION_MINOR}${PROJECT_VERSION_PATCH}) else() From 0cea5b0120398cd0f44683b4d915b27cba863145 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Thu, 7 Sep 2023 23:07:07 +0300 Subject: [PATCH 074/126] CMake: Remove the code for old CMake versions --- CMakeLists.txt | 88 ++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 56 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f6c2d501..e74bfbad1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,17 +132,13 @@ endif() set(DBG $,$>) if(IPO) - if(CMAKE_VERSION VERSION_GREATER 3.9) - include(CheckIPOSupported) - check_ipo_supported(RESULT ipo_supported OUTPUT ipo_output) - if(ipo_supported) - message(STATUS "IPO is enabled") - set(ENABLE_IPO TRUE) - else() - message(WARNING "IPO is not supported: ${ipo_output}") - endif() + include(CheckIPOSupported) + check_ipo_supported(RESULT ipo_supported OUTPUT ipo_output) + if(ipo_supported) + message(STATUS "IPO is enabled") + set(ENABLE_IPO TRUE) else() - message(WARNING "IPO enablement requires CMake 3.9+") + message(WARNING "IPO is not supported: ${ipo_output}") endif() endif() @@ -263,7 +259,7 @@ if(NOT MSVC AND NOT HAIKU) endif() endif() - if(CMAKE_VERSION VERSION_LESS 3.1 OR TARGET_OS STREQUAL "mac") + if(TARGET_OS STREQUAL "mac") add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -std=gnu++17) endif() @@ -303,12 +299,10 @@ if(NOT MSVC AND NOT HAIKU) endif() add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wall) - if(CMAKE_VERSION VERSION_GREATER 3.3 OR CMAKE_VERSION VERSION_EQUAL 3.3) - add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN - $<$:-Wdeclaration-after-statement> - -Wdeclaration-after-statement - ) - endif() + add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN + $<$:-Wdeclaration-after-statement> + -Wdeclaration-after-statement + ) add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wextra) add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wno-psabi) # parameter passing for argument of type ‘__gnu_cxx::__normal_iterator*, std::vector, std::allocator > > >’ changed in GCC 7.1 add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wno-unused-parameter) @@ -360,28 +354,25 @@ function(set_glob VAR GLOBBING EXTS DIRECTORY) # ... if(NOT FILES STREQUAL GLOB_RESULT) message(AUTHOR_WARNING "${VAR} does not contain every file from directory ${DIRECTORY}") set(LIST_BUT_NOT_GLOB) - if(POLICY CMP0057) - cmake_policy(SET CMP0057 NEW) - foreach(file ${FILES}) - if(NOT file IN_LIST GLOB_RESULT) - list(APPEND LIST_BUT_NOT_GLOB ${file}) - endif() - endforeach() - if(LIST_BUT_NOT_GLOB) - message(AUTHOR_WARNING "Entries only present in ${VAR}: ${LIST_BUT_NOT_GLOB}") + foreach(file ${FILES}) + if(NOT file IN_LIST GLOB_RESULT) + list(APPEND LIST_BUT_NOT_GLOB ${file}) endif() - set(GLOB_BUT_NOT_LIST) - foreach(file ${GLOB_RESULT}) - if(NOT file IN_LIST FILES) - list(APPEND GLOB_BUT_NOT_LIST ${file}) - endif() - endforeach() - if(GLOB_BUT_NOT_LIST) - message(AUTHOR_WARNING "Entries only present in ${DIRECTORY}: ${GLOB_BUT_NOT_LIST}") - endif() - if(NOT LIST_BUT_NOT_GLOB AND NOT GLOB_BUT_NOT_LIST) - message(AUTHOR_WARNING "${VAR} is not alphabetically sorted") + endforeach() + if(LIST_BUT_NOT_GLOB) + message(AUTHOR_WARNING "Entries only present in ${VAR}: ${LIST_BUT_NOT_GLOB}") + endif() + set(GLOB_BUT_NOT_LIST) + foreach(file ${GLOB_RESULT}) + if(NOT file IN_LIST FILES) + list(APPEND GLOB_BUT_NOT_LIST ${file}) endif() + endforeach() + if(GLOB_BUT_NOT_LIST) + message(AUTHOR_WARNING "Entries only present in ${DIRECTORY}: ${GLOB_BUT_NOT_LIST}") + endif() + if(NOT LIST_BUT_NOT_GLOB AND NOT GLOB_BUT_NOT_LIST) + message(AUTHOR_WARNING "${VAR} is not alphabetically sorted") endif() endif() @@ -397,19 +388,11 @@ set(CHECKSUM_SRC) function(set_own_rpath TARGET) if(NOT TARGET_OS STREQUAL "windows" AND NOT TARGET_OS STREQUAL "mac") - if(CMAKE_VERSION VERSION_GREATER 3.8 OR CMAKE_VERSION VERSION_EQUAL 3.8) - set_property(TARGET ${TARGET} PROPERTY BUILD_RPATH "$ORIGIN") - endif() + set_property(TARGET ${TARGET} PROPERTY BUILD_RPATH "$ORIGIN") set_property(TARGET ${TARGET} PROPERTY INSTALL_RPATH "$ORIGIN/../lib/ddnet") endif() endfunction() -if(NOT TARGET_OS STREQUAL "windows" AND NOT TARGET_OS STREQUAL "mac" AND CMAKE_VERSION VERSION_LESS 3.8) - if((CLIENT AND (STEAM OR DISCORD_DYNAMIC)) OR ANTIBOT) - message(STATUS "Can't set BUILD_RPATH in CMake before 3.8, pass -Wl,-rpath,'$ORIGIN' manually if you wish to emulate this. Or just install a newer version of CMake...") - endif() -endif() - ######################################################################## # INITIALIZE TARGET LISTS ######################################################################## @@ -807,9 +790,6 @@ if(NOT(GTEST_FOUND) AND DOWNLOAD_GTEST) set(GTEST_LIBRARIES gtest gmock) set(GTEST_INCLUDE_DIRS) - if(CMAKE_VERSION VERSION_LESS 2.8.11) - set(GTEST_INCLUDE_DIRS "${gtest_SOURCE_DIR}/include" "${gmock_SOURCE_DIR}/include") - endif() endif() endif() endif() @@ -3064,8 +3044,6 @@ endif() if(DEV) # Don't generate CPack targets. -elseif(CMAKE_VERSION VERSION_LESS 3.6 OR CMAKE_VERSION VERSION_EQUAL 3.6) - message(WARNING "Cannot create CPack targets, CMake version too old. Use CMake 3.6 or newer.") else() set(EXTRA_ARGS DESTINATION ${CPACK_PACKAGE_FILE_NAME} COMPONENT portable EXCLUDE_FROM_ALL) install(TARGETS ${CPACK_TARGETS} ${EXTRA_ARGS}) @@ -3295,11 +3273,9 @@ foreach(target ${TARGETS_LINK}) endforeach() foreach(target ${TARGETS_OWN}) - if((CMAKE_VERSION VERSION_GREATER 3.1 OR CMAKE_VERSION VERSION_EQUAL 3.1)) - set_property(TARGET ${target} PROPERTY CXX_STANDARD 17) - set_property(TARGET ${target} PROPERTY CXX_STANDARD_REQUIRED ON) - set_property(TARGET ${target} PROPERTY CXX_EXTENSIONS OFF) - endif() + set_property(TARGET ${target} PROPERTY CXX_STANDARD 17) + set_property(TARGET ${target} PROPERTY CXX_STANDARD_REQUIRED ON) + set_property(TARGET ${target} PROPERTY CXX_EXTENSIONS OFF) if(MSVC) target_compile_options(${target} PRIVATE /wd4244) # Possible loss of data (float -> int, int -> float, etc.). From 53d0e3f851d913d7707f14465406564d45ea50fb Mon Sep 17 00:00:00 2001 From: dobrykafe <121701317+dobrykafe@users.noreply.github.com> Date: Fri, 8 Sep 2023 00:12:18 +0200 Subject: [PATCH 075/126] allow flag scroll while input selected --- src/game/client/components/menus_settings.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index bd0e8e7c9..8bef1e29b 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -329,8 +329,6 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) MainView.HSplitTop(20.0f, 0, &MainView); int OldSelected = -1; static CListBox s_ListBox; - if(UI()->CheckActiveItem(&s_ClanInput) || UI()->CheckActiveItem(&s_NameInput)) - s_ListBox.SetActive(false); s_ListBox.DoStart(50.0f, m_pClient->m_CountryFlags.Num(), 10, 3, OldSelected, &MainView); for(size_t i = 0; i < m_pClient->m_CountryFlags.Num(); ++i) From cbdd83f79093b0e4143e78ad2266cd7f4a38116b Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Fri, 8 Sep 2023 11:03:07 +0200 Subject: [PATCH 076/126] mastersrv: Go to `RawValue` directly Instead of going through `String`. --- src/mastersrv/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mastersrv/src/main.rs b/src/mastersrv/src/main.rs index 1c03f6a1b..d608fa415 100644 --- a/src/mastersrv/src/main.rs +++ b/src/mastersrv/src/main.rs @@ -718,8 +718,7 @@ fn handle_register( let info = i.as_object().ok_or("register info must be an object")?; // Normalize the JSON to strip any spaces etc. - let raw_info = json::to_string(&info).unwrap(); - Ok(json::value::RawValue::from_string(raw_info).unwrap()) + Ok(json::value::to_raw_value(&info).unwrap()) }) .transpose()?; From db5f28e65ac7b5dbe61614be208d704d820226c3 Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Fri, 8 Sep 2023 11:05:51 +0200 Subject: [PATCH 077/126] `.h.in` aren't compiled --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e74bfbad1..a2c9a0286 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -380,7 +380,7 @@ function(set_glob VAR GLOBBING EXTS DIRECTORY) # ... endfunction() function(set_src VAR GLOBBING DIRECTORY) # ... - set_glob(${VAR} ${GLOBBING} "c;cpp;h;h.in" ${DIRECTORY} ${ARGN}) + set_glob(${VAR} ${GLOBBING} "c;cpp;h" ${DIRECTORY} ${ARGN}) set(${VAR} ${${VAR}} PARENT_SCOPE) set(CHECKSUM_SRC ${CHECKSUM_SRC} ${${VAR}} PARENT_SCOPE) endfunction() @@ -1996,7 +1996,6 @@ set_src(GAME_SHARED GLOB src/game teamscore.h tuning.h variables.h - version.h.in voting.h ) # A bit hacky, but these are needed to register all the UUIDs, even for stuff From cebc472cbe7586e9ff8a8c7017c4c2d3bd48394c Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Fri, 8 Sep 2023 11:08:52 +0200 Subject: [PATCH 078/126] Remove C-only compiler flag We don't have any own C source files anymore. --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a2c9a0286..98915faf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -299,10 +299,6 @@ if(NOT MSVC AND NOT HAIKU) endif() add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wall) - add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN - $<$:-Wdeclaration-after-statement> - -Wdeclaration-after-statement - ) add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wextra) add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wno-psabi) # parameter passing for argument of type ‘__gnu_cxx::__normal_iterator*, std::vector, std::allocator > > >’ changed in GCC 7.1 add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wno-unused-parameter) From 237c2eba35d2eee2dbe3d65910f08bcf3aa3ed3b Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Fri, 8 Sep 2023 16:21:52 +0200 Subject: [PATCH 079/126] Revert "`.h.in` aren't compiled" This allows the version.h.in file to show up in IDEs. This reverts commit db5f28e65ac7b5dbe61614be208d704d820226c3. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 98915faf0..3fa2112e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -376,7 +376,7 @@ function(set_glob VAR GLOBBING EXTS DIRECTORY) # ... endfunction() function(set_src VAR GLOBBING DIRECTORY) # ... - set_glob(${VAR} ${GLOBBING} "c;cpp;h" ${DIRECTORY} ${ARGN}) + set_glob(${VAR} ${GLOBBING} "c;cpp;h;h.in" ${DIRECTORY} ${ARGN}) set(${VAR} ${${VAR}} PARENT_SCOPE) set(CHECKSUM_SRC ${CHECKSUM_SRC} ${${VAR}} PARENT_SCOPE) endfunction() @@ -1992,6 +1992,7 @@ set_src(GAME_SHARED GLOB src/game teamscore.h tuning.h variables.h + version.h.in voting.h ) # A bit hacky, but these are needed to register all the UUIDs, even for stuff From 88fc275d8513754fa3b9d78b1aa0bdb167183912 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Tue, 15 Aug 2023 23:12:24 +0300 Subject: [PATCH 080/126] GameContext: Extract OnCallVote() and OnVote() from OnMessage() --- src/game/server/gamecontext.cpp | 546 ++++++++++++++++---------------- src/game/server/gamecontext.h | 2 + 2 files changed, 278 insertions(+), 270 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 969e94fc3..396f6bd77 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -2053,277 +2053,9 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } } else if(MsgID == NETMSGTYPE_CL_CALLVOTE) - { - if(RateLimitPlayerVote(ClientID) || m_VoteCloseTime) - return; - - m_apPlayers[ClientID]->UpdatePlaytime(); - - m_VoteType = VOTE_TYPE_UNKNOWN; - char aChatmsg[512] = {0}; - char aDesc[VOTE_DESC_LENGTH] = {0}; - char aSixupDesc[VOTE_DESC_LENGTH] = {0}; - char aCmd[VOTE_CMD_LENGTH] = {0}; - char aReason[VOTE_REASON_LENGTH] = "No reason given"; - CNetMsg_Cl_CallVote *pMsg = (CNetMsg_Cl_CallVote *)pRawMsg; - if(!str_utf8_check(pMsg->m_pType) || !str_utf8_check(pMsg->m_pReason) || !str_utf8_check(pMsg->m_pValue)) - { - return; - } - if(pMsg->m_pReason[0]) - { - str_copy(aReason, pMsg->m_pReason, sizeof(aReason)); - } - - if(str_comp_nocase(pMsg->m_pType, "option") == 0) - { - int Authed = Server()->GetAuthedState(ClientID); - CVoteOptionServer *pOption = m_pVoteOptionFirst; - while(pOption) - { - if(str_comp_nocase(pMsg->m_pValue, pOption->m_aDescription) == 0) - { - if(!Console()->LineIsValid(pOption->m_aCommand)) - { - SendChatTarget(ClientID, "Invalid option"); - return; - } - if((str_find(pOption->m_aCommand, "sv_map ") != 0 || str_find(pOption->m_aCommand, "change_map ") != 0 || str_find(pOption->m_aCommand, "random_map") != 0 || str_find(pOption->m_aCommand, "random_unfinished_map") != 0) && RateLimitPlayerMapVote(ClientID)) - { - return; - } - - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)", Server()->ClientName(ClientID), - pOption->m_aDescription, aReason); - str_copy(aDesc, pOption->m_aDescription); - - if((str_endswith(pOption->m_aCommand, "random_map") || str_endswith(pOption->m_aCommand, "random_unfinished_map")) && str_length(aReason) == 1 && aReason[0] >= '0' && aReason[0] <= '5') - { - int Stars = aReason[0] - '0'; - str_format(aCmd, sizeof(aCmd), "%s %d", pOption->m_aCommand, Stars); - } - else - { - str_copy(aCmd, pOption->m_aCommand); - } - - m_LastMapVote = time_get(); - break; - } - - pOption = pOption->m_pNext; - } - - if(!pOption) - { - if(Authed != AUTHED_ADMIN) // allow admins to call any vote they want - { - str_format(aChatmsg, sizeof(aChatmsg), "'%s' isn't an option on this server", pMsg->m_pValue); - SendChatTarget(ClientID, aChatmsg); - return; - } - else - { - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s'", Server()->ClientName(ClientID), pMsg->m_pValue); - str_copy(aDesc, pMsg->m_pValue); - str_copy(aCmd, pMsg->m_pValue); - } - } - - m_VoteType = VOTE_TYPE_OPTION; - } - else if(str_comp_nocase(pMsg->m_pType, "kick") == 0) - { - int Authed = Server()->GetAuthedState(ClientID); - if(!Authed && time_get() < m_apPlayers[ClientID]->m_Last_KickVote + (time_freq() * 5)) - return; - else if(!Authed && time_get() < m_apPlayers[ClientID]->m_Last_KickVote + (time_freq() * g_Config.m_SvVoteKickDelay)) - { - str_format(aChatmsg, sizeof(aChatmsg), "There's a %d second wait time between kick votes for each player please wait %d second(s)", - g_Config.m_SvVoteKickDelay, - (int)(((m_apPlayers[ClientID]->m_Last_KickVote + (m_apPlayers[ClientID]->m_Last_KickVote * time_freq())) / time_freq()) - (time_get() / time_freq()))); - SendChatTarget(ClientID, aChatmsg); - m_apPlayers[ClientID]->m_Last_KickVote = time_get(); - return; - } - else if(!g_Config.m_SvVoteKick && !Authed) // allow admins to call kick votes even if they are forbidden - { - SendChatTarget(ClientID, "Server does not allow voting to kick players"); - m_apPlayers[ClientID]->m_Last_KickVote = time_get(); - return; - } - - if(g_Config.m_SvVoteKickMin && !GetDDRaceTeam(ClientID)) - { - char aaAddresses[MAX_CLIENTS][NETADDR_MAXSTRSIZE] = {{0}}; - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(m_apPlayers[i]) - { - Server()->GetClientAddr(i, aaAddresses[i], NETADDR_MAXSTRSIZE); - } - } - int NumPlayers = 0; - for(int i = 0; i < MAX_CLIENTS; ++i) - { - if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS && !GetDDRaceTeam(i)) - { - NumPlayers++; - for(int j = 0; j < i; j++) - { - if(m_apPlayers[j] && m_apPlayers[j]->GetTeam() != TEAM_SPECTATORS && !GetDDRaceTeam(j)) - { - if(str_comp(aaAddresses[i], aaAddresses[j]) == 0) - { - NumPlayers--; - break; - } - } - } - } - } - - if(NumPlayers < g_Config.m_SvVoteKickMin) - { - str_format(aChatmsg, sizeof(aChatmsg), "Kick voting requires %d players", g_Config.m_SvVoteKickMin); - SendChatTarget(ClientID, aChatmsg); - return; - } - } - - int KickID = str_toint(pMsg->m_pValue); - - if(KickID < 0 || KickID >= MAX_CLIENTS || !m_apPlayers[KickID]) - { - SendChatTarget(ClientID, "Invalid client id to kick"); - return; - } - if(KickID == ClientID) - { - SendChatTarget(ClientID, "You can't kick yourself"); - return; - } - if(!Server()->ReverseTranslate(KickID, ClientID)) - { - return; - } - int KickedAuthed = Server()->GetAuthedState(KickID); - if(KickedAuthed > Authed) - { - SendChatTarget(ClientID, "You can't kick authorized players"); - m_apPlayers[ClientID]->m_Last_KickVote = time_get(); - char aBufKick[128]; - str_format(aBufKick, sizeof(aBufKick), "'%s' called for vote to kick you", Server()->ClientName(ClientID)); - SendChatTarget(KickID, aBufKick); - return; - } - - // Don't allow kicking if a player has no character - if(!GetPlayerChar(ClientID) || !GetPlayerChar(KickID) || GetDDRaceTeam(ClientID) != GetDDRaceTeam(KickID)) - { - SendChatTarget(ClientID, "You can kick only your team member"); - m_apPlayers[ClientID]->m_Last_KickVote = time_get(); - return; - } - - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to kick '%s' (%s)", Server()->ClientName(ClientID), Server()->ClientName(KickID), aReason); - str_format(aSixupDesc, sizeof(aSixupDesc), "%2d: %s", KickID, Server()->ClientName(KickID)); - if(!GetDDRaceTeam(ClientID)) - { - if(!g_Config.m_SvVoteKickBantime) - { - str_format(aCmd, sizeof(aCmd), "kick %d Kicked by vote", KickID); - str_format(aDesc, sizeof(aDesc), "Kick '%s'", Server()->ClientName(KickID)); - } - else - { - char aAddrStr[NETADDR_MAXSTRSIZE] = {0}; - Server()->GetClientAddr(KickID, aAddrStr, sizeof(aAddrStr)); - str_format(aCmd, sizeof(aCmd), "ban %s %d Banned by vote", aAddrStr, g_Config.m_SvVoteKickBantime); - str_format(aDesc, sizeof(aDesc), "Ban '%s'", Server()->ClientName(KickID)); - } - } - else - { - str_format(aCmd, sizeof(aCmd), "uninvite %d %d; set_team_ddr %d 0", KickID, GetDDRaceTeam(KickID), KickID); - str_format(aDesc, sizeof(aDesc), "Move '%s' to team 0", Server()->ClientName(KickID)); - } - m_apPlayers[ClientID]->m_Last_KickVote = time_get(); - m_VoteType = VOTE_TYPE_KICK; - m_VoteVictim = KickID; - } - else if(str_comp_nocase(pMsg->m_pType, "spectate") == 0) - { - if(!g_Config.m_SvVoteSpectate) - { - SendChatTarget(ClientID, "Server does not allow voting to move players to spectators"); - return; - } - - int SpectateID = str_toint(pMsg->m_pValue); - - if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !m_apPlayers[SpectateID] || m_apPlayers[SpectateID]->GetTeam() == TEAM_SPECTATORS) - { - SendChatTarget(ClientID, "Invalid client id to move"); - return; - } - if(SpectateID == ClientID) - { - SendChatTarget(ClientID, "You can't move yourself"); - return; - } - if(!Server()->ReverseTranslate(SpectateID, ClientID)) - { - return; - } - - if(!GetPlayerChar(ClientID) || !GetPlayerChar(SpectateID) || GetDDRaceTeam(ClientID) != GetDDRaceTeam(SpectateID)) - { - SendChatTarget(ClientID, "You can only move your team member to spectators"); - return; - } - - str_format(aSixupDesc, sizeof(aSixupDesc), "%2d: %s", SpectateID, Server()->ClientName(SpectateID)); - if(g_Config.m_SvPauseable && g_Config.m_SvVotePause) - { - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to pause '%s' for %d seconds (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), g_Config.m_SvVotePauseTime, aReason); - str_format(aDesc, sizeof(aDesc), "Pause '%s' (%ds)", Server()->ClientName(SpectateID), g_Config.m_SvVotePauseTime); - str_format(aCmd, sizeof(aCmd), "uninvite %d %d; force_pause %d %d", SpectateID, GetDDRaceTeam(SpectateID), SpectateID, g_Config.m_SvVotePauseTime); - } - else - { - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to move '%s' to spectators (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), aReason); - str_format(aDesc, sizeof(aDesc), "Move '%s' to spectators", Server()->ClientName(SpectateID)); - str_format(aCmd, sizeof(aCmd), "uninvite %d %d; set_team %d -1 %d", SpectateID, GetDDRaceTeam(SpectateID), SpectateID, g_Config.m_SvVoteSpectateRejoindelay); - } - m_VoteType = VOTE_TYPE_SPECTATE; - m_VoteVictim = SpectateID; - } - - if(aCmd[0] && str_comp_nocase(aCmd, "info") != 0) - CallVote(ClientID, aDesc, aCmd, aReason, aChatmsg, aSixupDesc[0] ? aSixupDesc : 0); - } + OnCallVoteNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_VOTE) - { - if(!m_VoteCloseTime) - return; - - if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry + Server()->TickSpeed() * 3 > Server()->Tick()) - return; - - int64_t Now = Server()->Tick(); - - pPlayer->m_LastVoteTry = Now; - pPlayer->UpdatePlaytime(); - - CNetMsg_Cl_Vote *pMsg = (CNetMsg_Cl_Vote *)pRawMsg; - if(!pMsg->m_Vote) - return; - - pPlayer->m_Vote = pMsg->m_Vote; - pPlayer->m_VotePos = ++m_VotePos; - m_VoteUpdate = true; - } + OnVoteNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_SETTEAM && !m_World.m_Paused) { CNetMsg_Cl_SetTeam *pMsg = (CNetMsg_Cl_SetTeam *)pRawMsg; @@ -2694,6 +2426,280 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } } +void CGameContext::OnCallVoteNetMessage(const CNetMsg_Cl_CallVote *pMsg, int ClientID) +{ + if(RateLimitPlayerVote(ClientID) || m_VoteCloseTime) + return; + + m_apPlayers[ClientID]->UpdatePlaytime(); + + m_VoteType = VOTE_TYPE_UNKNOWN; + char aChatmsg[512] = {0}; + char aDesc[VOTE_DESC_LENGTH] = {0}; + char aSixupDesc[VOTE_DESC_LENGTH] = {0}; + char aCmd[VOTE_CMD_LENGTH] = {0}; + char aReason[VOTE_REASON_LENGTH] = "No reason given"; + if(!str_utf8_check(pMsg->m_pType) || !str_utf8_check(pMsg->m_pReason) || !str_utf8_check(pMsg->m_pValue)) + { + return; + } + if(pMsg->m_pReason[0]) + { + str_copy(aReason, pMsg->m_pReason, sizeof(aReason)); + } + + if(str_comp_nocase(pMsg->m_pType, "option") == 0) + { + int Authed = Server()->GetAuthedState(ClientID); + CVoteOptionServer *pOption = m_pVoteOptionFirst; + while(pOption) + { + if(str_comp_nocase(pMsg->m_pValue, pOption->m_aDescription) == 0) + { + if(!Console()->LineIsValid(pOption->m_aCommand)) + { + SendChatTarget(ClientID, "Invalid option"); + return; + } + if((str_find(pOption->m_aCommand, "sv_map ") != 0 || str_find(pOption->m_aCommand, "change_map ") != 0 || str_find(pOption->m_aCommand, "random_map") != 0 || str_find(pOption->m_aCommand, "random_unfinished_map") != 0) && RateLimitPlayerMapVote(ClientID)) + { + return; + } + + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)", Server()->ClientName(ClientID), + pOption->m_aDescription, aReason); + str_copy(aDesc, pOption->m_aDescription); + + if((str_endswith(pOption->m_aCommand, "random_map") || str_endswith(pOption->m_aCommand, "random_unfinished_map")) && str_length(aReason) == 1 && aReason[0] >= '0' && aReason[0] <= '5') + { + int Stars = aReason[0] - '0'; + str_format(aCmd, sizeof(aCmd), "%s %d", pOption->m_aCommand, Stars); + } + else + { + str_copy(aCmd, pOption->m_aCommand); + } + + m_LastMapVote = time_get(); + break; + } + + pOption = pOption->m_pNext; + } + + if(!pOption) + { + if(Authed != AUTHED_ADMIN) // allow admins to call any vote they want + { + str_format(aChatmsg, sizeof(aChatmsg), "'%s' isn't an option on this server", pMsg->m_pValue); + SendChatTarget(ClientID, aChatmsg); + return; + } + else + { + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s'", Server()->ClientName(ClientID), pMsg->m_pValue); + str_copy(aDesc, pMsg->m_pValue); + str_copy(aCmd, pMsg->m_pValue); + } + } + + m_VoteType = VOTE_TYPE_OPTION; + } + else if(str_comp_nocase(pMsg->m_pType, "kick") == 0) + { + int Authed = Server()->GetAuthedState(ClientID); + if(!Authed && time_get() < m_apPlayers[ClientID]->m_Last_KickVote + (time_freq() * 5)) + return; + else if(!Authed && time_get() < m_apPlayers[ClientID]->m_Last_KickVote + (time_freq() * g_Config.m_SvVoteKickDelay)) + { + str_format(aChatmsg, sizeof(aChatmsg), "There's a %d second wait time between kick votes for each player please wait %d second(s)", + g_Config.m_SvVoteKickDelay, + (int)(((m_apPlayers[ClientID]->m_Last_KickVote + (m_apPlayers[ClientID]->m_Last_KickVote * time_freq())) / time_freq()) - (time_get() / time_freq()))); + SendChatTarget(ClientID, aChatmsg); + m_apPlayers[ClientID]->m_Last_KickVote = time_get(); + return; + } + else if(!g_Config.m_SvVoteKick && !Authed) // allow admins to call kick votes even if they are forbidden + { + SendChatTarget(ClientID, "Server does not allow voting to kick players"); + m_apPlayers[ClientID]->m_Last_KickVote = time_get(); + return; + } + + if(g_Config.m_SvVoteKickMin && !GetDDRaceTeam(ClientID)) + { + char aaAddresses[MAX_CLIENTS][NETADDR_MAXSTRSIZE] = {{0}}; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_apPlayers[i]) + { + Server()->GetClientAddr(i, aaAddresses[i], NETADDR_MAXSTRSIZE); + } + } + int NumPlayers = 0; + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS && !GetDDRaceTeam(i)) + { + NumPlayers++; + for(int j = 0; j < i; j++) + { + if(m_apPlayers[j] && m_apPlayers[j]->GetTeam() != TEAM_SPECTATORS && !GetDDRaceTeam(j)) + { + if(str_comp(aaAddresses[i], aaAddresses[j]) == 0) + { + NumPlayers--; + break; + } + } + } + } + } + + if(NumPlayers < g_Config.m_SvVoteKickMin) + { + str_format(aChatmsg, sizeof(aChatmsg), "Kick voting requires %d players", g_Config.m_SvVoteKickMin); + SendChatTarget(ClientID, aChatmsg); + return; + } + } + + int KickID = str_toint(pMsg->m_pValue); + + if(KickID < 0 || KickID >= MAX_CLIENTS || !m_apPlayers[KickID]) + { + SendChatTarget(ClientID, "Invalid client id to kick"); + return; + } + if(KickID == ClientID) + { + SendChatTarget(ClientID, "You can't kick yourself"); + return; + } + if(!Server()->ReverseTranslate(KickID, ClientID)) + { + return; + } + int KickedAuthed = Server()->GetAuthedState(KickID); + if(KickedAuthed > Authed) + { + SendChatTarget(ClientID, "You can't kick authorized players"); + m_apPlayers[ClientID]->m_Last_KickVote = time_get(); + char aBufKick[128]; + str_format(aBufKick, sizeof(aBufKick), "'%s' called for vote to kick you", Server()->ClientName(ClientID)); + SendChatTarget(KickID, aBufKick); + return; + } + + // Don't allow kicking if a player has no character + if(!GetPlayerChar(ClientID) || !GetPlayerChar(KickID) || GetDDRaceTeam(ClientID) != GetDDRaceTeam(KickID)) + { + SendChatTarget(ClientID, "You can kick only your team member"); + m_apPlayers[ClientID]->m_Last_KickVote = time_get(); + return; + } + + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to kick '%s' (%s)", Server()->ClientName(ClientID), Server()->ClientName(KickID), aReason); + str_format(aSixupDesc, sizeof(aSixupDesc), "%2d: %s", KickID, Server()->ClientName(KickID)); + if(!GetDDRaceTeam(ClientID)) + { + if(!g_Config.m_SvVoteKickBantime) + { + str_format(aCmd, sizeof(aCmd), "kick %d Kicked by vote", KickID); + str_format(aDesc, sizeof(aDesc), "Kick '%s'", Server()->ClientName(KickID)); + } + else + { + char aAddrStr[NETADDR_MAXSTRSIZE] = {0}; + Server()->GetClientAddr(KickID, aAddrStr, sizeof(aAddrStr)); + str_format(aCmd, sizeof(aCmd), "ban %s %d Banned by vote", aAddrStr, g_Config.m_SvVoteKickBantime); + str_format(aDesc, sizeof(aDesc), "Ban '%s'", Server()->ClientName(KickID)); + } + } + else + { + str_format(aCmd, sizeof(aCmd), "uninvite %d %d; set_team_ddr %d 0", KickID, GetDDRaceTeam(KickID), KickID); + str_format(aDesc, sizeof(aDesc), "Move '%s' to team 0", Server()->ClientName(KickID)); + } + m_apPlayers[ClientID]->m_Last_KickVote = time_get(); + m_VoteType = VOTE_TYPE_KICK; + m_VoteVictim = KickID; + } + else if(str_comp_nocase(pMsg->m_pType, "spectate") == 0) + { + if(!g_Config.m_SvVoteSpectate) + { + SendChatTarget(ClientID, "Server does not allow voting to move players to spectators"); + return; + } + + int SpectateID = str_toint(pMsg->m_pValue); + + if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !m_apPlayers[SpectateID] || m_apPlayers[SpectateID]->GetTeam() == TEAM_SPECTATORS) + { + SendChatTarget(ClientID, "Invalid client id to move"); + return; + } + if(SpectateID == ClientID) + { + SendChatTarget(ClientID, "You can't move yourself"); + return; + } + if(!Server()->ReverseTranslate(SpectateID, ClientID)) + { + return; + } + + if(!GetPlayerChar(ClientID) || !GetPlayerChar(SpectateID) || GetDDRaceTeam(ClientID) != GetDDRaceTeam(SpectateID)) + { + SendChatTarget(ClientID, "You can only move your team member to spectators"); + return; + } + + str_format(aSixupDesc, sizeof(aSixupDesc), "%2d: %s", SpectateID, Server()->ClientName(SpectateID)); + if(g_Config.m_SvPauseable && g_Config.m_SvVotePause) + { + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to pause '%s' for %d seconds (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), g_Config.m_SvVotePauseTime, aReason); + str_format(aDesc, sizeof(aDesc), "Pause '%s' (%ds)", Server()->ClientName(SpectateID), g_Config.m_SvVotePauseTime); + str_format(aCmd, sizeof(aCmd), "uninvite %d %d; force_pause %d %d", SpectateID, GetDDRaceTeam(SpectateID), SpectateID, g_Config.m_SvVotePauseTime); + } + else + { + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to move '%s' to spectators (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), aReason); + str_format(aDesc, sizeof(aDesc), "Move '%s' to spectators", Server()->ClientName(SpectateID)); + str_format(aCmd, sizeof(aCmd), "uninvite %d %d; set_team %d -1 %d", SpectateID, GetDDRaceTeam(SpectateID), SpectateID, g_Config.m_SvVoteSpectateRejoindelay); + } + m_VoteType = VOTE_TYPE_SPECTATE; + m_VoteVictim = SpectateID; + } + + if(aCmd[0] && str_comp_nocase(aCmd, "info") != 0) + CallVote(ClientID, aDesc, aCmd, aReason, aChatmsg, aSixupDesc[0] ? aSixupDesc : 0); +} + +void CGameContext::OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientID) +{ + if(!m_VoteCloseTime) + return; + + CPlayer *pPlayer = m_apPlayers[ClientID]; + + if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry + Server()->TickSpeed() * 3 > Server()->Tick()) + return; + + int64_t Now = Server()->Tick(); + + pPlayer->m_LastVoteTry = Now; + pPlayer->UpdatePlaytime(); + + if(!pMsg->m_Vote) + return; + + pPlayer->m_Vote = pMsg->m_Vote; + pPlayer->m_VotePos = ++m_VotePos; + m_VoteUpdate = true; +} + void CGameContext::ConTuneParam(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 6acac3313..618ebda7e 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -288,6 +288,8 @@ public: void *PreProcessMsg(int *pMsgID, CUnpacker *pUnpacker, int ClientID); void CensorMessage(char *pCensoredMessage, const char *pMessage, int Size); void OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) override; + void OnCallVoteNetMessage(const CNetMsg_Cl_CallVote *pMsg, int ClientID); + void OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientID); bool OnClientDataPersist(int ClientID, void *pData) override; void OnClientConnected(int ClientID, void *pData) override; From 1074a5ff569a2fadeec16b1c6a57d5540cd148ba Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Fri, 8 Sep 2023 23:06:48 +0300 Subject: [PATCH 081/126] CMake: Use DDNet_VERSION to construct DDNET_VERSION_NUMBER --- CMakeLists.txt | 14 +++++++------- src/engine/server/server.cpp | 2 +- src/game/client/gameclient.cpp | 4 ++-- src/game/version.h.in | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 98915faf0..9abf2750f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,16 +142,16 @@ if(IPO) endif() endif() -if("0${PROJECT_VERSION_PATCH}" GREATER 9) - message(SEND_ERROR "Invalid project patch version (the version is \"${PROJECT_VERSION}\", the patch part must be in range 0-9 if set)") +if("0${DDNet_VERSION_PATCH}" GREATER 9) + message(SEND_ERROR "Invalid DDNet patch version (the version is \"${DDNet_VERSION}\", the patch part must be in range 0-9 if set)") endif() -if(${PROJECT_VERSION_MINOR} GREATER 99) - message(SEND_ERROR "Invalid project minor version (the version is \"${PROJECT_VERSION}\", the minor part must be in range 0-99)") +if(${DDNet_VERSION_MINOR} GREATER 99) + message(SEND_ERROR "Invalid DDNet minor version (the version is \"${DDNet_VERSION}\", the minor part must be in range 0-99)") endif() -if(${PROJECT_VERSION_MINOR} GREATER 9) - set(CLIENT_VERSIONNR ${PROJECT_VERSION_MAJOR}${PROJECT_VERSION_MINOR}${PROJECT_VERSION_PATCH}) +if(${DDNet_VERSION_MINOR} GREATER 9) + set(DDNET_VERSION_NUMBER ${DDNet_VERSION_MAJOR}${DDNet_VERSION_MINOR}${DDNet_VERSION_PATCH}) else() - set(CLIENT_VERSIONNR ${PROJECT_VERSION_MAJOR}0${PROJECT_VERSION_MINOR}${PROJECT_VERSION_PATCH}) + set(DDNET_VERSION_NUMBER ${DDNet_VERSION_MAJOR}0${DDNet_VERSION_MINOR}${DDNet_VERSION_PATCH}) endif() configure_file(src/game/version.h.in ${PROJECT_BINARY_DIR}/src/game/version.h) diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 649bf6934..aeff8a1ab 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -762,7 +762,7 @@ int CServer::GetClientVersion(int ClientID) const { // Assume latest client version for server demos if(ClientID == SERVER_DEMO_CLIENT) - return CLIENT_VERSIONNR; + return DDNET_VERSION_NUMBER; CClientInfo Info; if(GetClientInfo(ClientID, &Info)) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 0561942aa..8c7b1d7e6 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -76,7 +76,7 @@ using namespace std::chrono_literals; const char *CGameClient::Version() const { return GAME_VERSION; } const char *CGameClient::NetVersion() const { return GAME_NETVERSION; } -int CGameClient::DDNetVersion() const { return CLIENT_VERSIONNR; } +int CGameClient::DDNetVersion() const { return DDNET_VERSION_NUMBER; } const char *CGameClient::DDNetVersionStr() const { return m_aDDNetVersionStr; } const char *CGameClient::GetItemName(int Type) const { return m_NetObjHandler.GetObjName(Type); } @@ -1770,7 +1770,7 @@ void CGameClient::OnNewSnapshot() continue; } CMsgPacker Msg(NETMSGTYPE_CL_ISDDNETLEGACY, false); - Msg.AddInt(CLIENT_VERSIONNR); + Msg.AddInt(DDNetVersion()); Client()->SendMsg(i, &Msg, MSGFLAG_VITAL); m_aDDRaceMsgSent[i] = true; } diff --git a/src/game/version.h.in b/src/game/version.h.in index 87be713d3..f1fb9e093 100644 --- a/src/game/version.h.in +++ b/src/game/version.h.in @@ -6,7 +6,7 @@ #define GAME_RELEASE_VERSION "${PROJECT_VERSION}" #define GAME_VERSION "0.6.4, " GAME_RELEASE_VERSION #define GAME_NETVERSION "0.6 626fce9a778df4d4" -#define CLIENT_VERSIONNR ${CLIENT_VERSIONNR} +#define DDNET_VERSION_NUMBER ${DDNET_VERSION_NUMBER} extern const char *GIT_SHORTREV_HASH; #define GAME_NAME "DDNet" #endif From 6b78013a13738f1aedf65b1ddba418a18f288f25 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Sat, 9 Sep 2023 00:25:47 +0300 Subject: [PATCH 082/126] CMake: Set package version to the project version Setting the version to VERSION didn't allow to override the project() name and version. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9abf2750f..b769f1e0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2893,7 +2893,7 @@ set(CPACK_SOURCE_GENERATOR ZIP TGZ TBZ2 TXZ) set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) -set(CPACK_PACKAGE_VERSION ${VERSION}) +set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) set(CPACK_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}) if(TARGET_OS AND TARGET_BITS) From 21f7af59a0b66ae3252546e17550c2dc3ce4fdf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 9 Sep 2023 13:13:05 +0200 Subject: [PATCH 083/126] Fix desynced lineinput cursor/selection on external buffer change When the buffer of a lineinput is modified externally, the cursor offset and selection are not updated, which causes them to be rendered wrong and also causes the assertion error "Selection and cursor offset got desynchronized" when changing the selection of a lineinput. --- src/game/client/lineinput.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp index ac44c8574..e230024e4 100644 --- a/src/game/client/lineinput.cpp +++ b/src/game/client/lineinput.cpp @@ -386,6 +386,9 @@ bool CLineInput::ProcessInput(const IInput::CEvent &Event) STextBoundingBox CLineInput::Render(const CUIRect *pRect, float FontSize, int Align, bool Changed, float LineWidth) { + // update derived attributes to handle external changes to the buffer + UpdateStrData(); + m_WasRendered = true; const char *pDisplayStr = GetDisplayedString(); From 84e3c081b48c5fa44297721e03e18558b3e164d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 7 Sep 2023 21:26:40 +0200 Subject: [PATCH 084/126] Fix friendlist update conchain not working with optional argument The clan argument is optional when using `add_friend` and `remove_friend`. --- src/game/client/components/menus_browser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 190545c2a..a48a5dc05 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -1728,7 +1728,7 @@ void CMenus::RenderServerbrowser(CUIRect MainView) void CMenus::ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) { pfnCallback(pResult, pCallbackUserData); - if(pResult->NumArguments() == 2 && ((CMenus *)pUserData)->Client()->State() == IClient::STATE_OFFLINE) + if(pResult->NumArguments() >= 1 && ((CMenus *)pUserData)->Client()->State() == IClient::STATE_OFFLINE) { ((CMenus *)pUserData)->FriendlistOnUpdate(); ((CMenus *)pUserData)->Client()->ServerBrowserUpdate(); @@ -1738,6 +1738,6 @@ void CMenus::ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserDat void CMenus::ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) { pfnCallback(pResult, pCallbackUserData); - if(pResult->NumArguments() && g_Config.m_UiPage == PAGE_FAVORITES) + if(pResult->NumArguments() >= 1 && g_Config.m_UiPage == PAGE_FAVORITES) ((CMenus *)pUserData)->ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES); } From a1ec904596e3e3ddd1cdeabfad4bf6a5251bdd5a Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Tue, 15 Aug 2023 23:17:06 +0300 Subject: [PATCH 085/126] GameContext: Extract OnSayNetMessage() --- src/game/server/gamecontext.cpp | 245 ++++++++++++++++---------------- src/game/server/gamecontext.h | 1 + 2 files changed, 125 insertions(+), 121 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 396f6bd77..2648d98a7 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1931,127 +1931,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) if(Server()->ClientIngame(ClientID)) { if(MsgID == NETMSGTYPE_CL_SAY) - { - CNetMsg_Cl_Say *pMsg = (CNetMsg_Cl_Say *)pRawMsg; - if(!str_utf8_check(pMsg->m_pMessage)) - { - return; - } - bool Check = !pPlayer->m_NotEligibleForFinish && pPlayer->m_EligibleForFinishCheck + 10 * time_freq() >= time_get(); - if(Check && str_comp(pMsg->m_pMessage, "xd sure chillerbot.png is lyfe") == 0 && pMsg->m_Team == 0) - { - if(m_TeeHistorianActive) - { - m_TeeHistorian.RecordPlayerMessage(ClientID, pUnpacker->CompleteData(), pUnpacker->CompleteSize()); - } - - pPlayer->m_NotEligibleForFinish = true; - dbg_msg("hack", "bot detected, cid=%d", ClientID); - return; - } - int Team = pMsg->m_Team; - - // trim right and set maximum length to 256 utf8-characters - int Length = 0; - const char *p = pMsg->m_pMessage; - const char *pEnd = 0; - while(*p) - { - const char *pStrOld = p; - int Code = str_utf8_decode(&p); - - // check if unicode is not empty - if(!str_utf8_isspace(Code)) - { - pEnd = 0; - } - else if(pEnd == 0) - pEnd = pStrOld; - - if(++Length >= 256) - { - *(const_cast(p)) = 0; - break; - } - } - if(pEnd != 0) - *(const_cast(pEnd)) = 0; - - // drop empty and autocreated spam messages (more than 32 characters per second) - if(Length == 0 || (pMsg->m_pMessage[0] != '/' && (g_Config.m_SvSpamprotection && pPlayer->m_LastChat && pPlayer->m_LastChat + Server()->TickSpeed() * ((31 + Length) / 32) > Server()->Tick()))) - return; - - int GameTeam = ((CGameControllerDDRace *)m_pController)->m_Teams.m_Core.Team(pPlayer->GetCID()); - if(Team) - Team = ((pPlayer->GetTeam() == TEAM_SPECTATORS) ? CHAT_SPEC : GameTeam); - else - Team = CHAT_ALL; - - if(pMsg->m_pMessage[0] == '/') - { - if(str_startswith_nocase(pMsg->m_pMessage + 1, "w ")) - { - char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + 3, 256); - Whisper(pPlayer->GetCID(), aWhisperMsg); - } - else if(str_startswith_nocase(pMsg->m_pMessage + 1, "whisper ")) - { - char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + 9, 256); - Whisper(pPlayer->GetCID(), aWhisperMsg); - } - else if(str_startswith_nocase(pMsg->m_pMessage + 1, "c ")) - { - char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + 3, 256); - Converse(pPlayer->GetCID(), aWhisperMsg); - } - else if(str_startswith_nocase(pMsg->m_pMessage + 1, "converse ")) - { - char aWhisperMsg[256]; - str_copy(aWhisperMsg, pMsg->m_pMessage + 10, 256); - Converse(pPlayer->GetCID(), aWhisperMsg); - } - else - { - if(g_Config.m_SvSpamprotection && !str_startswith(pMsg->m_pMessage + 1, "timeout ") && pPlayer->m_aLastCommands[0] && pPlayer->m_aLastCommands[0] + Server()->TickSpeed() > Server()->Tick() && pPlayer->m_aLastCommands[1] && pPlayer->m_aLastCommands[1] + Server()->TickSpeed() > Server()->Tick() && pPlayer->m_aLastCommands[2] && pPlayer->m_aLastCommands[2] + Server()->TickSpeed() > Server()->Tick() && pPlayer->m_aLastCommands[3] && pPlayer->m_aLastCommands[3] + Server()->TickSpeed() > Server()->Tick()) - return; - - int64_t Now = Server()->Tick(); - pPlayer->m_aLastCommands[pPlayer->m_LastCommandPos] = Now; - pPlayer->m_LastCommandPos = (pPlayer->m_LastCommandPos + 1) % 4; - - Console()->SetFlagMask(CFGFLAG_CHAT); - int Authed = Server()->GetAuthedState(ClientID); - if(Authed) - Console()->SetAccessLevel(Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : Authed == AUTHED_MOD ? IConsole::ACCESS_LEVEL_MOD : IConsole::ACCESS_LEVEL_HELPER); - else - Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_USER); - - { - CClientChatLogger Logger(this, ClientID, log_get_scope_logger()); - CLogScope Scope(&Logger); - Console()->ExecuteLine(pMsg->m_pMessage + 1, ClientID, false); - } - // m_apPlayers[ClientID] can be NULL, if the player used a - // timeout code and replaced another client. - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "%d used %s", ClientID, pMsg->m_pMessage); - Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "chat-command", aBuf); - - Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN); - Console()->SetFlagMask(CFGFLAG_SERVER); - } - } - else - { - pPlayer->UpdatePlaytime(); - char aCensoredMessage[256]; - CensorMessage(aCensoredMessage, pMsg->m_pMessage, sizeof(aCensoredMessage)); - SendChat(ClientID, Team, aCensoredMessage, ClientID); - } - } + OnSayNetMessage(static_cast(pRawMsg), ClientID, pUnpacker); else if(MsgID == NETMSGTYPE_CL_CALLVOTE) OnCallVoteNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_VOTE) @@ -2426,6 +2306,129 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } } +void CGameContext::OnSayNetMessage(const CNetMsg_Cl_Say *pMsg, int ClientID, const CUnpacker *pUnpacker) +{ + if(!str_utf8_check(pMsg->m_pMessage)) + { + return; + } + CPlayer *pPlayer = m_apPlayers[ClientID]; + bool Check = !pPlayer->m_NotEligibleForFinish && pPlayer->m_EligibleForFinishCheck + 10 * time_freq() >= time_get(); + if(Check && str_comp(pMsg->m_pMessage, "xd sure chillerbot.png is lyfe") == 0 && pMsg->m_Team == 0) + { + if(m_TeeHistorianActive) + { + m_TeeHistorian.RecordPlayerMessage(ClientID, pUnpacker->CompleteData(), pUnpacker->CompleteSize()); + } + + pPlayer->m_NotEligibleForFinish = true; + dbg_msg("hack", "bot detected, cid=%d", ClientID); + return; + } + int Team = pMsg->m_Team; + + // trim right and set maximum length to 256 utf8-characters + int Length = 0; + const char *p = pMsg->m_pMessage; + const char *pEnd = 0; + while(*p) + { + const char *pStrOld = p; + int Code = str_utf8_decode(&p); + + // check if unicode is not empty + if(!str_utf8_isspace(Code)) + { + pEnd = 0; + } + else if(pEnd == 0) + pEnd = pStrOld; + + if(++Length >= 256) + { + *(const_cast(p)) = 0; + break; + } + } + if(pEnd != 0) + *(const_cast(pEnd)) = 0; + + // drop empty and autocreated spam messages (more than 32 characters per second) + if(Length == 0 || (pMsg->m_pMessage[0] != '/' && (g_Config.m_SvSpamprotection && pPlayer->m_LastChat && pPlayer->m_LastChat + Server()->TickSpeed() * ((31 + Length) / 32) > Server()->Tick()))) + return; + + int GameTeam = ((CGameControllerDDRace *)m_pController)->m_Teams.m_Core.Team(pPlayer->GetCID()); + if(Team) + Team = ((pPlayer->GetTeam() == TEAM_SPECTATORS) ? CHAT_SPEC : GameTeam); + else + Team = CHAT_ALL; + + if(pMsg->m_pMessage[0] == '/') + { + if(str_startswith_nocase(pMsg->m_pMessage + 1, "w ")) + { + char aWhisperMsg[256]; + str_copy(aWhisperMsg, pMsg->m_pMessage + 3, 256); + Whisper(pPlayer->GetCID(), aWhisperMsg); + } + else if(str_startswith_nocase(pMsg->m_pMessage + 1, "whisper ")) + { + char aWhisperMsg[256]; + str_copy(aWhisperMsg, pMsg->m_pMessage + 9, 256); + Whisper(pPlayer->GetCID(), aWhisperMsg); + } + else if(str_startswith_nocase(pMsg->m_pMessage + 1, "c ")) + { + char aWhisperMsg[256]; + str_copy(aWhisperMsg, pMsg->m_pMessage + 3, 256); + Converse(pPlayer->GetCID(), aWhisperMsg); + } + else if(str_startswith_nocase(pMsg->m_pMessage + 1, "converse ")) + { + char aWhisperMsg[256]; + str_copy(aWhisperMsg, pMsg->m_pMessage + 10, 256); + Converse(pPlayer->GetCID(), aWhisperMsg); + } + else + { + if(g_Config.m_SvSpamprotection && !str_startswith(pMsg->m_pMessage + 1, "timeout ") && pPlayer->m_aLastCommands[0] && pPlayer->m_aLastCommands[0] + Server()->TickSpeed() > Server()->Tick() && pPlayer->m_aLastCommands[1] && pPlayer->m_aLastCommands[1] + Server()->TickSpeed() > Server()->Tick() && pPlayer->m_aLastCommands[2] && pPlayer->m_aLastCommands[2] + Server()->TickSpeed() > Server()->Tick() && pPlayer->m_aLastCommands[3] && pPlayer->m_aLastCommands[3] + Server()->TickSpeed() > Server()->Tick()) + return; + + int64_t Now = Server()->Tick(); + pPlayer->m_aLastCommands[pPlayer->m_LastCommandPos] = Now; + pPlayer->m_LastCommandPos = (pPlayer->m_LastCommandPos + 1) % 4; + + Console()->SetFlagMask(CFGFLAG_CHAT); + int Authed = Server()->GetAuthedState(ClientID); + if(Authed) + Console()->SetAccessLevel(Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : Authed == AUTHED_MOD ? IConsole::ACCESS_LEVEL_MOD : IConsole::ACCESS_LEVEL_HELPER); + else + Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_USER); + + { + CClientChatLogger Logger(this, ClientID, log_get_scope_logger()); + CLogScope Scope(&Logger); + Console()->ExecuteLine(pMsg->m_pMessage + 1, ClientID, false); + } + // m_apPlayers[ClientID] can be NULL, if the player used a + // timeout code and replaced another client. + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "%d used %s", ClientID, pMsg->m_pMessage); + Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "chat-command", aBuf); + + Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN); + Console()->SetFlagMask(CFGFLAG_SERVER); + } + } + else + { + pPlayer->UpdatePlaytime(); + char aCensoredMessage[256]; + CensorMessage(aCensoredMessage, pMsg->m_pMessage, sizeof(aCensoredMessage)); + SendChat(ClientID, Team, aCensoredMessage, ClientID); + } +} + void CGameContext::OnCallVoteNetMessage(const CNetMsg_Cl_CallVote *pMsg, int ClientID) { if(RateLimitPlayerVote(ClientID) || m_VoteCloseTime) diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 618ebda7e..3b7d20c96 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -288,6 +288,7 @@ public: void *PreProcessMsg(int *pMsgID, CUnpacker *pUnpacker, int ClientID); void CensorMessage(char *pCensoredMessage, const char *pMessage, int Size); void OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) override; + void OnSayNetMessage(const CNetMsg_Cl_Say *pMsg, int ClientID, const CUnpacker *pUnpacker); void OnCallVoteNetMessage(const CNetMsg_Cl_CallVote *pMsg, int ClientID); void OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientID); From ce5371b038f43de43fba2d33e608ed03adf746bb Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Tue, 15 Aug 2023 23:23:01 +0300 Subject: [PATCH 086/126] GameContext: Extract OnSetTeamNetMessage() --- src/game/server/gamecontext.cpp | 103 ++++++++++++++++---------------- src/game/server/gamecontext.h | 1 + 2 files changed, 54 insertions(+), 50 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 2648d98a7..1f087368b 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1937,56 +1937,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) else if(MsgID == NETMSGTYPE_CL_VOTE) OnVoteNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_SETTEAM && !m_World.m_Paused) - { - CNetMsg_Cl_SetTeam *pMsg = (CNetMsg_Cl_SetTeam *)pRawMsg; - - if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam + Server()->TickSpeed() * g_Config.m_SvTeamChangeDelay > Server()->Tick())) - return; - - //Kill Protection - CCharacter *pChr = pPlayer->GetCharacter(); - if(pChr) - { - int CurrTime = (Server()->Tick() - pChr->m_StartTime) / Server()->TickSpeed(); - if(g_Config.m_SvKillProtection != 0 && CurrTime >= (60 * g_Config.m_SvKillProtection) && pChr->m_DDRaceState == DDRACE_STARTED) - { - SendChatTarget(ClientID, "Kill Protection enabled. If you really want to join the spectators, first type /kill"); - return; - } - } - - if(pPlayer->m_TeamChangeTick > Server()->Tick()) - { - pPlayer->m_LastSetTeam = Server()->Tick(); - int TimeLeft = (pPlayer->m_TeamChangeTick - Server()->Tick()) / Server()->TickSpeed(); - char aTime[32]; - str_time((int64_t)TimeLeft * 100, TIME_HOURS, aTime, sizeof(aTime)); - char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "Time to wait before changing team: %s", aTime); - SendBroadcast(aBuf, ClientID); - return; - } - - // Switch team on given client and kill/respawn them - if(m_pController->CanJoinTeam(pMsg->m_Team, ClientID)) - { - if(pPlayer->IsPaused()) - SendChatTarget(ClientID, "Use /pause first then you can kill"); - else - { - if(pPlayer->GetTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS) - m_VoteUpdate = true; - m_pController->DoTeamChange(pPlayer, pMsg->m_Team); - pPlayer->m_TeamChangeTick = Server()->Tick(); - } - } - else - { - char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "Only %d active players are allowed", Server()->MaxClients() - g_Config.m_SvSpectatorSlots); - SendBroadcast(aBuf, ClientID); - } - } + OnSetTeamNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_ISDDNETLEGACY) { IServer::CClientInfo Info; @@ -2703,6 +2654,58 @@ void CGameContext::OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientID) m_VoteUpdate = true; } +void CGameContext::OnSetTeamNetMessage(const CNetMsg_Cl_SetTeam *pMsg, int ClientID) +{ + CPlayer *pPlayer = m_apPlayers[ClientID]; + + if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam + Server()->TickSpeed() * g_Config.m_SvTeamChangeDelay > Server()->Tick())) + return; + + // Kill Protection + CCharacter *pChr = pPlayer->GetCharacter(); + if(pChr) + { + int CurrTime = (Server()->Tick() - pChr->m_StartTime) / Server()->TickSpeed(); + if(g_Config.m_SvKillProtection != 0 && CurrTime >= (60 * g_Config.m_SvKillProtection) && pChr->m_DDRaceState == DDRACE_STARTED) + { + SendChatTarget(ClientID, "Kill Protection enabled. If you really want to join the spectators, first type /kill"); + return; + } + } + + if(pPlayer->m_TeamChangeTick > Server()->Tick()) + { + pPlayer->m_LastSetTeam = Server()->Tick(); + int TimeLeft = (pPlayer->m_TeamChangeTick - Server()->Tick()) / Server()->TickSpeed(); + char aTime[32]; + str_time((int64_t)TimeLeft * 100, TIME_HOURS, aTime, sizeof(aTime)); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Time to wait before changing team: %s", aTime); + SendBroadcast(aBuf, ClientID); + return; + } + + // Switch team on given client and kill/respawn them + if(m_pController->CanJoinTeam(pMsg->m_Team, ClientID)) + { + if(pPlayer->IsPaused()) + SendChatTarget(ClientID, "Use /pause first then you can kill"); + else + { + if(pPlayer->GetTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS) + m_VoteUpdate = true; + m_pController->DoTeamChange(pPlayer, pMsg->m_Team); + pPlayer->m_TeamChangeTick = Server()->Tick(); + } + } + else + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Only %d active players are allowed", Server()->MaxClients() - g_Config.m_SvSpectatorSlots); + SendBroadcast(aBuf, ClientID); + } +} + void CGameContext::ConTuneParam(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 3b7d20c96..2fe848e52 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -291,6 +291,7 @@ public: void OnSayNetMessage(const CNetMsg_Cl_Say *pMsg, int ClientID, const CUnpacker *pUnpacker); void OnCallVoteNetMessage(const CNetMsg_Cl_CallVote *pMsg, int ClientID); void OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientID); + void OnSetTeamNetMessage(const CNetMsg_Cl_SetTeam *pMsg, int ClientID); bool OnClientDataPersist(int ClientID, void *pData) override; void OnClientConnected(int ClientID, void *pData) override; From 9f668e20db77508965a12cc7c904ccd6d4e17b5a Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Tue, 15 Aug 2023 23:44:37 +0300 Subject: [PATCH 087/126] GameContext::OnMessage: Move the check for World.Paused into OnSetTeam 1. Regardless of the pause the message is CL_SETTEAM and there is no reason to match it against other messages. 2. Another implementation can save the wanted team and apply it later. --- src/game/server/gamecontext.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 1f087368b..86cfaace6 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1936,7 +1936,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) OnCallVoteNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_VOTE) OnVoteNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_SETTEAM && !m_World.m_Paused) + else if(MsgID == NETMSGTYPE_CL_SETTEAM) OnSetTeamNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_ISDDNETLEGACY) { @@ -2656,6 +2656,9 @@ void CGameContext::OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientID) void CGameContext::OnSetTeamNetMessage(const CNetMsg_Cl_SetTeam *pMsg, int ClientID) { + if(m_World.m_Paused) + return; + CPlayer *pPlayer = m_apPlayers[ClientID]; if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam + Server()->TickSpeed() * g_Config.m_SvTeamChangeDelay > Server()->Tick())) From 0a2cc3d84c52b80f10e3d0cd574a24fb3f8d949b Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Tue, 15 Aug 2023 23:45:28 +0300 Subject: [PATCH 088/126] GameContext: Extract OnChangeInfoNetMessage() --- src/game/server/gamecontext.cpp | 201 ++++++++++++++++---------------- src/game/server/gamecontext.h | 1 + 2 files changed, 103 insertions(+), 99 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 86cfaace6..fd8d9d39f 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1995,105 +1995,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) pPlayer->m_SpectatorID = pMsg->m_SpectatorID; } else if(MsgID == NETMSGTYPE_CL_CHANGEINFO) - { - if(g_Config.m_SvSpamprotection && pPlayer->m_LastChangeInfo && pPlayer->m_LastChangeInfo + Server()->TickSpeed() * g_Config.m_SvInfoChangeDelay > Server()->Tick()) - return; - - bool SixupNeedsUpdate = false; - - CNetMsg_Cl_ChangeInfo *pMsg = (CNetMsg_Cl_ChangeInfo *)pRawMsg; - if(!str_utf8_check(pMsg->m_pName) || !str_utf8_check(pMsg->m_pClan) || !str_utf8_check(pMsg->m_pSkin)) - { - return; - } - pPlayer->m_LastChangeInfo = Server()->Tick(); - pPlayer->UpdatePlaytime(); - - // set infos - if(Server()->WouldClientNameChange(ClientID, pMsg->m_pName) && !ProcessSpamProtection(ClientID)) - { - char aOldName[MAX_NAME_LENGTH]; - str_copy(aOldName, Server()->ClientName(ClientID), sizeof(aOldName)); - - Server()->SetClientName(ClientID, pMsg->m_pName); - - char aChatText[256]; - str_format(aChatText, sizeof(aChatText), "'%s' changed name to '%s'", aOldName, Server()->ClientName(ClientID)); - SendChat(-1, CGameContext::CHAT_ALL, aChatText); - - // reload scores - Score()->PlayerData(ClientID)->Reset(); - m_apPlayers[ClientID]->m_Score.reset(); - Score()->LoadPlayerData(ClientID); - - SixupNeedsUpdate = true; - - LogEvent("Name change", ClientID); - } - - if(str_comp(Server()->ClientClan(ClientID), pMsg->m_pClan)) - SixupNeedsUpdate = true; - Server()->SetClientClan(ClientID, pMsg->m_pClan); - - if(Server()->ClientCountry(ClientID) != pMsg->m_Country) - SixupNeedsUpdate = true; - Server()->SetClientCountry(ClientID, pMsg->m_Country); - - str_copy(pPlayer->m_TeeInfos.m_aSkinName, pMsg->m_pSkin, sizeof(pPlayer->m_TeeInfos.m_aSkinName)); - pPlayer->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor; - pPlayer->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody; - pPlayer->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet; - if(!Server()->IsSixup(ClientID)) - pPlayer->m_TeeInfos.ToSixup(); - - if(SixupNeedsUpdate) - { - protocol7::CNetMsg_Sv_ClientDrop Drop; - Drop.m_ClientID = ClientID; - Drop.m_pReason = ""; - Drop.m_Silent = true; - - protocol7::CNetMsg_Sv_ClientInfo Info; - Info.m_ClientID = ClientID; - Info.m_pName = Server()->ClientName(ClientID); - Info.m_Country = pMsg->m_Country; - Info.m_pClan = pMsg->m_pClan; - Info.m_Local = 0; - Info.m_Silent = true; - Info.m_Team = pPlayer->GetTeam(); - - for(int p = 0; p < 6; p++) - { - Info.m_apSkinPartNames[p] = pPlayer->m_TeeInfos.m_apSkinPartNames[p]; - Info.m_aSkinPartColors[p] = pPlayer->m_TeeInfos.m_aSkinPartColors[p]; - Info.m_aUseCustomColors[p] = pPlayer->m_TeeInfos.m_aUseCustomColors[p]; - } - - for(int i = 0; i < Server()->MaxClients(); i++) - { - if(i != ClientID) - { - Server()->SendPackMsg(&Drop, MSGFLAG_VITAL | MSGFLAG_NORECORD, i); - Server()->SendPackMsg(&Info, MSGFLAG_VITAL | MSGFLAG_NORECORD, i); - } - } - } - else - { - protocol7::CNetMsg_Sv_SkinChange Msg; - Msg.m_ClientID = ClientID; - for(int p = 0; p < 6; p++) - { - Msg.m_apSkinPartNames[p] = pPlayer->m_TeeInfos.m_apSkinPartNames[p]; - Msg.m_aSkinPartColors[p] = pPlayer->m_TeeInfos.m_aSkinPartColors[p]; - Msg.m_aUseCustomColors[p] = pPlayer->m_TeeInfos.m_aUseCustomColors[p]; - } - - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_NORECORD, -1); - } - - Server()->ExpireServerInfo(); - } + OnChangeInfoNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_EMOTICON && !m_World.m_Paused) { CNetMsg_Cl_Emoticon *pMsg = (CNetMsg_Cl_Emoticon *)pRawMsg; @@ -2709,6 +2611,107 @@ void CGameContext::OnSetTeamNetMessage(const CNetMsg_Cl_SetTeam *pMsg, int Clien } } +void CGameContext::OnChangeInfoNetMessage(const CNetMsg_Cl_ChangeInfo *pMsg, int ClientID) +{ + CPlayer *pPlayer = m_apPlayers[ClientID]; + if(g_Config.m_SvSpamprotection && pPlayer->m_LastChangeInfo && pPlayer->m_LastChangeInfo + Server()->TickSpeed() * g_Config.m_SvInfoChangeDelay > Server()->Tick()) + return; + + bool SixupNeedsUpdate = false; + + if(!str_utf8_check(pMsg->m_pName) || !str_utf8_check(pMsg->m_pClan) || !str_utf8_check(pMsg->m_pSkin)) + { + return; + } + pPlayer->m_LastChangeInfo = Server()->Tick(); + pPlayer->UpdatePlaytime(); + + // set infos + if(Server()->WouldClientNameChange(ClientID, pMsg->m_pName) && !ProcessSpamProtection(ClientID)) + { + char aOldName[MAX_NAME_LENGTH]; + str_copy(aOldName, Server()->ClientName(ClientID), sizeof(aOldName)); + + Server()->SetClientName(ClientID, pMsg->m_pName); + + char aChatText[256]; + str_format(aChatText, sizeof(aChatText), "'%s' changed name to '%s'", aOldName, Server()->ClientName(ClientID)); + SendChat(-1, CGameContext::CHAT_ALL, aChatText); + + // reload scores + Score()->PlayerData(ClientID)->Reset(); + m_apPlayers[ClientID]->m_Score.reset(); + Score()->LoadPlayerData(ClientID); + + SixupNeedsUpdate = true; + + LogEvent("Name change", ClientID); + } + + if(str_comp(Server()->ClientClan(ClientID), pMsg->m_pClan)) + SixupNeedsUpdate = true; + Server()->SetClientClan(ClientID, pMsg->m_pClan); + + if(Server()->ClientCountry(ClientID) != pMsg->m_Country) + SixupNeedsUpdate = true; + Server()->SetClientCountry(ClientID, pMsg->m_Country); + + str_copy(pPlayer->m_TeeInfos.m_aSkinName, pMsg->m_pSkin, sizeof(pPlayer->m_TeeInfos.m_aSkinName)); + pPlayer->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor; + pPlayer->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody; + pPlayer->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet; + if(!Server()->IsSixup(ClientID)) + pPlayer->m_TeeInfos.ToSixup(); + + if(SixupNeedsUpdate) + { + protocol7::CNetMsg_Sv_ClientDrop Drop; + Drop.m_ClientID = ClientID; + Drop.m_pReason = ""; + Drop.m_Silent = true; + + protocol7::CNetMsg_Sv_ClientInfo Info; + Info.m_ClientID = ClientID; + Info.m_pName = Server()->ClientName(ClientID); + Info.m_Country = pMsg->m_Country; + Info.m_pClan = pMsg->m_pClan; + Info.m_Local = 0; + Info.m_Silent = true; + Info.m_Team = pPlayer->GetTeam(); + + for(int p = 0; p < 6; p++) + { + Info.m_apSkinPartNames[p] = pPlayer->m_TeeInfos.m_apSkinPartNames[p]; + Info.m_aSkinPartColors[p] = pPlayer->m_TeeInfos.m_aSkinPartColors[p]; + Info.m_aUseCustomColors[p] = pPlayer->m_TeeInfos.m_aUseCustomColors[p]; + } + + for(int i = 0; i < Server()->MaxClients(); i++) + { + if(i != ClientID) + { + Server()->SendPackMsg(&Drop, MSGFLAG_VITAL | MSGFLAG_NORECORD, i); + Server()->SendPackMsg(&Info, MSGFLAG_VITAL | MSGFLAG_NORECORD, i); + } + } + } + else + { + protocol7::CNetMsg_Sv_SkinChange Msg; + Msg.m_ClientID = ClientID; + for(int p = 0; p < 6; p++) + { + Msg.m_apSkinPartNames[p] = pPlayer->m_TeeInfos.m_apSkinPartNames[p]; + Msg.m_aSkinPartColors[p] = pPlayer->m_TeeInfos.m_aSkinPartColors[p]; + Msg.m_aUseCustomColors[p] = pPlayer->m_TeeInfos.m_aUseCustomColors[p]; + } + + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_NORECORD, -1); + } + + Server()->ExpireServerInfo(); +} + void CGameContext::ConTuneParam(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 2fe848e52..2c9f20cdf 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -292,6 +292,7 @@ public: void OnCallVoteNetMessage(const CNetMsg_Cl_CallVote *pMsg, int ClientID); void OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientID); void OnSetTeamNetMessage(const CNetMsg_Cl_SetTeam *pMsg, int ClientID); + void OnChangeInfoNetMessage(const CNetMsg_Cl_ChangeInfo *pMsg, int ClientID); bool OnClientDataPersist(int ClientID, void *pData) override; void OnClientConnected(int ClientID, void *pData) override; From 763541af5ac82247774bfe9c3f5980141fad681a Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Wed, 16 Aug 2023 00:27:41 +0300 Subject: [PATCH 089/126] GameContext: Extract OnEmoticonNetMessage() --- src/game/server/gamecontext.cpp | 150 ++++++++++++++++---------------- src/game/server/gamecontext.h | 1 + 2 files changed, 78 insertions(+), 73 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index fd8d9d39f..cd458ae0f 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1997,79 +1997,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) else if(MsgID == NETMSGTYPE_CL_CHANGEINFO) OnChangeInfoNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_EMOTICON && !m_World.m_Paused) - { - CNetMsg_Cl_Emoticon *pMsg = (CNetMsg_Cl_Emoticon *)pRawMsg; - - auto &&CheckPreventEmote = [&](int64_t LastEmote, int64_t DelayInMs) { - return (LastEmote * (int64_t)1000) + (int64_t)Server()->TickSpeed() * DelayInMs > ((int64_t)Server()->Tick() * (int64_t)1000); - }; - - if(g_Config.m_SvSpamprotection && CheckPreventEmote((int64_t)pPlayer->m_LastEmote, (int64_t)g_Config.m_SvEmoticonMsDelay)) - return; - - CCharacter *pChr = pPlayer->GetCharacter(); - // player needs a character to send emotes - if(pChr != nullptr) - { - pPlayer->m_LastEmote = Server()->Tick(); - pPlayer->UpdatePlaytime(); - - // check if the global emoticon is prevented and emotes are only send to nearby players - if(g_Config.m_SvSpamprotection && CheckPreventEmote((int64_t)pPlayer->m_LastEmoteGlobal, (int64_t)g_Config.m_SvGlobalEmoticonMsDelay)) - { - for(int i = 0; i < MAX_CLIENTS; ++i) - { - if(m_apPlayers[i] && pChr->CanSnapCharacter(i) && pChr->IsSnappingCharacterInView(i)) - { - SendEmoticon(ClientID, pMsg->m_Emoticon, i); - } - } - } - else - { - // else send emoticons to all players - pPlayer->m_LastEmoteGlobal = Server()->Tick(); - SendEmoticon(ClientID, pMsg->m_Emoticon, -1); - } - - if(g_Config.m_SvEmotionalTees && pPlayer->m_EyeEmoteEnabled) - { - int EmoteType = EMOTE_NORMAL; - switch(pMsg->m_Emoticon) - { - case EMOTICON_EXCLAMATION: - case EMOTICON_GHOST: - case EMOTICON_QUESTION: - case EMOTICON_WTF: - EmoteType = EMOTE_SURPRISE; - break; - case EMOTICON_DOTDOT: - case EMOTICON_DROP: - case EMOTICON_ZZZ: - EmoteType = EMOTE_BLINK; - break; - case EMOTICON_EYES: - case EMOTICON_HEARTS: - case EMOTICON_MUSIC: - EmoteType = EMOTE_HAPPY; - break; - case EMOTICON_OOP: - case EMOTICON_SORRY: - case EMOTICON_SUSHI: - EmoteType = EMOTE_PAIN; - break; - case EMOTICON_DEVILTEE: - case EMOTICON_SPLATTEE: - case EMOTICON_ZOMG: - EmoteType = EMOTE_ANGRY; - break; - default: - break; - } - pChr->SetEmote(EmoteType, Server()->Tick() + 2 * Server()->TickSpeed()); - } - } - } + OnEmoticonNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_KILL && !m_World.m_Paused) { if(m_VoteCloseTime && m_VoteCreator == ClientID && GetDDRaceTeam(ClientID) && (IsKickVote() || IsSpecVote())) @@ -2712,6 +2640,82 @@ void CGameContext::OnChangeInfoNetMessage(const CNetMsg_Cl_ChangeInfo *pMsg, int Server()->ExpireServerInfo(); } +void CGameContext::OnEmoticonNetMessage(const CNetMsg_Cl_Emoticon *pMsg, int ClientID) +{ + CPlayer *pPlayer = m_apPlayers[ClientID]; + + auto &&CheckPreventEmote = [&](int64_t LastEmote, int64_t DelayInMs) { + return (LastEmote * (int64_t)1000) + (int64_t)Server()->TickSpeed() * DelayInMs > ((int64_t)Server()->Tick() * (int64_t)1000); + }; + + if(g_Config.m_SvSpamprotection && CheckPreventEmote((int64_t)pPlayer->m_LastEmote, (int64_t)g_Config.m_SvEmoticonMsDelay)) + return; + + CCharacter *pChr = pPlayer->GetCharacter(); + + // player needs a character to send emotes + if(!pChr) + return; + + pPlayer->m_LastEmote = Server()->Tick(); + pPlayer->UpdatePlaytime(); + + // check if the global emoticon is prevented and emotes are only send to nearby players + if(g_Config.m_SvSpamprotection && CheckPreventEmote((int64_t)pPlayer->m_LastEmoteGlobal, (int64_t)g_Config.m_SvGlobalEmoticonMsDelay)) + { + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(m_apPlayers[i] && pChr->CanSnapCharacter(i) && pChr->IsSnappingCharacterInView(i)) + { + SendEmoticon(ClientID, pMsg->m_Emoticon, i); + } + } + } + else + { + // else send emoticons to all players + pPlayer->m_LastEmoteGlobal = Server()->Tick(); + SendEmoticon(ClientID, pMsg->m_Emoticon, -1); + } + + if(g_Config.m_SvEmotionalTees && pPlayer->m_EyeEmoteEnabled) + { + int EmoteType = EMOTE_NORMAL; + switch(pMsg->m_Emoticon) + { + case EMOTICON_EXCLAMATION: + case EMOTICON_GHOST: + case EMOTICON_QUESTION: + case EMOTICON_WTF: + EmoteType = EMOTE_SURPRISE; + break; + case EMOTICON_DOTDOT: + case EMOTICON_DROP: + case EMOTICON_ZZZ: + EmoteType = EMOTE_BLINK; + break; + case EMOTICON_EYES: + case EMOTICON_HEARTS: + case EMOTICON_MUSIC: + EmoteType = EMOTE_HAPPY; + break; + case EMOTICON_OOP: + case EMOTICON_SORRY: + case EMOTICON_SUSHI: + EmoteType = EMOTE_PAIN; + break; + case EMOTICON_DEVILTEE: + case EMOTICON_SPLATTEE: + case EMOTICON_ZOMG: + EmoteType = EMOTE_ANGRY; + break; + default: + break; + } + pChr->SetEmote(EmoteType, Server()->Tick() + 2 * Server()->TickSpeed()); + } +} + void CGameContext::ConTuneParam(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 2c9f20cdf..9aaec6888 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -293,6 +293,7 @@ public: void OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientID); void OnSetTeamNetMessage(const CNetMsg_Cl_SetTeam *pMsg, int ClientID); void OnChangeInfoNetMessage(const CNetMsg_Cl_ChangeInfo *pMsg, int ClientID); + void OnEmoticonNetMessage(const CNetMsg_Cl_Emoticon *pMsg, int ClientID); bool OnClientDataPersist(int ClientID, void *pData) override; void OnClientConnected(int ClientID, void *pData) override; From 3be79f568a99ab7e3483a63e5d766530dfeaeb96 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Wed, 16 Aug 2023 00:50:41 +0300 Subject: [PATCH 090/126] GameContext: Extract OnStartInfoNetMessage() --- src/game/server/gamecontext.cpp | 115 +++++++++++++++++--------------- src/game/server/gamecontext.h | 1 + 2 files changed, 61 insertions(+), 55 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index cd458ae0f..2e8da8167 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -2029,61 +2029,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } if(MsgID == NETMSGTYPE_CL_STARTINFO) { - if(pPlayer->m_IsReady) - return; - - CNetMsg_Cl_StartInfo *pMsg = (CNetMsg_Cl_StartInfo *)pRawMsg; - - if(!str_utf8_check(pMsg->m_pName)) - { - Server()->Kick(ClientID, "name is not valid utf8"); - return; - } - if(!str_utf8_check(pMsg->m_pClan)) - { - Server()->Kick(ClientID, "clan is not valid utf8"); - return; - } - if(!str_utf8_check(pMsg->m_pSkin)) - { - Server()->Kick(ClientID, "skin is not valid utf8"); - return; - } - - pPlayer->m_LastChangeInfo = Server()->Tick(); - - // set start infos - Server()->SetClientName(ClientID, pMsg->m_pName); - // trying to set client name can delete the player object, check if it still exists - if(!m_apPlayers[ClientID]) - { - return; - } - Server()->SetClientClan(ClientID, pMsg->m_pClan); - Server()->SetClientCountry(ClientID, pMsg->m_Country); - str_copy(pPlayer->m_TeeInfos.m_aSkinName, pMsg->m_pSkin, sizeof(pPlayer->m_TeeInfos.m_aSkinName)); - pPlayer->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor; - pPlayer->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody; - pPlayer->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet; - if(!Server()->IsSixup(ClientID)) - pPlayer->m_TeeInfos.ToSixup(); - - // send clear vote options - CNetMsg_Sv_VoteClearOptions ClearMsg; - Server()->SendPackMsg(&ClearMsg, MSGFLAG_VITAL, ClientID); - - // begin sending vote options - pPlayer->m_SendVoteIndex = 0; - - // send tuning parameters to client - SendTuningParams(ClientID, pPlayer->m_TuneZone); - - // client is ready to enter - pPlayer->m_IsReady = true; - CNetMsg_Sv_ReadyToEnter m; - Server()->SendPackMsg(&m, MSGFLAG_VITAL | MSGFLAG_FLUSH, ClientID); - - Server()->ExpireServerInfo(); + OnStartInfoNetMessage(static_cast(pRawMsg), ClientID); } } @@ -2716,6 +2662,65 @@ void CGameContext::OnEmoticonNetMessage(const CNetMsg_Cl_Emoticon *pMsg, int Cli } } +void CGameContext::OnStartInfoNetMessage(const CNetMsg_Cl_StartInfo *pMsg, int ClientID) +{ + CPlayer *pPlayer = m_apPlayers[ClientID]; + + if(pPlayer->m_IsReady) + return; + + if(!str_utf8_check(pMsg->m_pName)) + { + Server()->Kick(ClientID, "name is not valid utf8"); + return; + } + if(!str_utf8_check(pMsg->m_pClan)) + { + Server()->Kick(ClientID, "clan is not valid utf8"); + return; + } + if(!str_utf8_check(pMsg->m_pSkin)) + { + Server()->Kick(ClientID, "skin is not valid utf8"); + return; + } + + pPlayer->m_LastChangeInfo = Server()->Tick(); + + // set start infos + Server()->SetClientName(ClientID, pMsg->m_pName); + // trying to set client name can delete the player object, check if it still exists + if(!m_apPlayers[ClientID]) + { + return; + } + Server()->SetClientClan(ClientID, pMsg->m_pClan); + Server()->SetClientCountry(ClientID, pMsg->m_Country); + str_copy(pPlayer->m_TeeInfos.m_aSkinName, pMsg->m_pSkin, sizeof(pPlayer->m_TeeInfos.m_aSkinName)); + pPlayer->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor; + pPlayer->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody; + pPlayer->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet; + if(!Server()->IsSixup(ClientID)) + pPlayer->m_TeeInfos.ToSixup(); + + // send clear vote options + CNetMsg_Sv_VoteClearOptions ClearMsg; + Server()->SendPackMsg(&ClearMsg, MSGFLAG_VITAL, ClientID); + + // begin sending vote options + pPlayer->m_SendVoteIndex = 0; + + // send tuning parameters to client + SendTuningParams(ClientID, pPlayer->m_TuneZone); + + // client is ready to enter + pPlayer->m_IsReady = true; + CNetMsg_Sv_ReadyToEnter m; + Server()->SendPackMsg(&m, MSGFLAG_VITAL | MSGFLAG_FLUSH, ClientID); + + Server()->ExpireServerInfo(); +} + void CGameContext::ConTuneParam(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 9aaec6888..72e7f2027 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -294,6 +294,7 @@ public: void OnSetTeamNetMessage(const CNetMsg_Cl_SetTeam *pMsg, int ClientID); void OnChangeInfoNetMessage(const CNetMsg_Cl_ChangeInfo *pMsg, int ClientID); void OnEmoticonNetMessage(const CNetMsg_Cl_Emoticon *pMsg, int ClientID); + void OnStartInfoNetMessage(const CNetMsg_Cl_StartInfo *pMsg, int ClientID); bool OnClientDataPersist(int ClientID, void *pData) override; void OnClientConnected(int ClientID, void *pData) override; From e6c7d0c96d1b6ac791e84a7ae358f887b6d34205 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Fri, 8 Sep 2023 20:02:59 +0300 Subject: [PATCH 091/126] GameContext: Extract all other messages --- src/game/server/gamecontext.cpp | 190 ++++++++++++++++++-------------- src/game/server/gamecontext.h | 6 + 2 files changed, 113 insertions(+), 83 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 2e8da8167..c2b3c90fd 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1926,8 +1926,6 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) if(!pRawMsg) return; - CPlayer *pPlayer = m_apPlayers[ClientID]; - if(Server()->ClientIngame(ClientID)) { if(MsgID == NETMSGTYPE_CL_SAY) @@ -1939,93 +1937,21 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) else if(MsgID == NETMSGTYPE_CL_SETTEAM) OnSetTeamNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_ISDDNETLEGACY) - { - IServer::CClientInfo Info; - if(Server()->GetClientInfo(ClientID, &Info) && Info.m_GotDDNetVersion) - { - return; - } - int DDNetVersion = pUnpacker->GetInt(); - if(pUnpacker->Error() || DDNetVersion < 0) - { - DDNetVersion = VERSION_DDRACE; - } - Server()->SetClientDDNetVersion(ClientID, DDNetVersion); - OnClientDDNetVersionKnown(ClientID); - } + OnIsDDNetLegacyNetMessage(static_cast(pRawMsg), ClientID, pUnpacker); else if(MsgID == NETMSGTYPE_CL_SHOWOTHERSLEGACY) - { - if(g_Config.m_SvShowOthers && !g_Config.m_SvShowOthersDefault) - { - CNetMsg_Cl_ShowOthersLegacy *pMsg = (CNetMsg_Cl_ShowOthersLegacy *)pRawMsg; - pPlayer->m_ShowOthers = pMsg->m_Show; - } - } + OnShowOthersLegacyNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_SHOWOTHERS) - { - if(g_Config.m_SvShowOthers && !g_Config.m_SvShowOthersDefault) - { - CNetMsg_Cl_ShowOthers *pMsg = (CNetMsg_Cl_ShowOthers *)pRawMsg; - pPlayer->m_ShowOthers = pMsg->m_Show; - } - } + OnShowOthersNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_SHOWDISTANCE) - { - CNetMsg_Cl_ShowDistance *pMsg = (CNetMsg_Cl_ShowDistance *)pRawMsg; - pPlayer->m_ShowDistance = vec2(pMsg->m_X, pMsg->m_Y); - } - else if(MsgID == NETMSGTYPE_CL_SETSPECTATORMODE && !m_World.m_Paused) - { - CNetMsg_Cl_SetSpectatorMode *pMsg = (CNetMsg_Cl_SetSpectatorMode *)pRawMsg; - - pMsg->m_SpectatorID = clamp(pMsg->m_SpectatorID, (int)SPEC_FOLLOW, MAX_CLIENTS - 1); - - if(pMsg->m_SpectatorID >= 0) - if(!Server()->ReverseTranslate(pMsg->m_SpectatorID, ClientID)) - return; - - if((g_Config.m_SvSpamprotection && pPlayer->m_LastSetSpectatorMode && pPlayer->m_LastSetSpectatorMode + Server()->TickSpeed() / 4 > Server()->Tick())) - return; - - pPlayer->m_LastSetSpectatorMode = Server()->Tick(); - pPlayer->UpdatePlaytime(); - if(pMsg->m_SpectatorID >= 0 && (!m_apPlayers[pMsg->m_SpectatorID] || m_apPlayers[pMsg->m_SpectatorID]->GetTeam() == TEAM_SPECTATORS)) - SendChatTarget(ClientID, "Invalid spectator id used"); - else - pPlayer->m_SpectatorID = pMsg->m_SpectatorID; - } + OnShowDistanceNetMessage(static_cast(pRawMsg), ClientID); + else if(MsgID == NETMSGTYPE_CL_SETSPECTATORMODE) + OnSetSpectatorModeNetMessage(static_cast(pRawMsg), ClientID); else if(MsgID == NETMSGTYPE_CL_CHANGEINFO) OnChangeInfoNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_EMOTICON && !m_World.m_Paused) + else if(MsgID == NETMSGTYPE_CL_EMOTICON) OnEmoticonNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_KILL && !m_World.m_Paused) - { - if(m_VoteCloseTime && m_VoteCreator == ClientID && GetDDRaceTeam(ClientID) && (IsKickVote() || IsSpecVote())) - { - SendChatTarget(ClientID, "You are running a vote please try again after the vote is done!"); - return; - } - if(pPlayer->m_LastKill && pPlayer->m_LastKill + Server()->TickSpeed() * g_Config.m_SvKillDelay > Server()->Tick()) - return; - if(pPlayer->IsPaused()) - return; - - CCharacter *pChr = pPlayer->GetCharacter(); - if(!pChr) - return; - - //Kill Protection - int CurrTime = (Server()->Tick() - pChr->m_StartTime) / Server()->TickSpeed(); - if(g_Config.m_SvKillProtection != 0 && CurrTime >= (60 * g_Config.m_SvKillProtection) && pChr->m_DDRaceState == DDRACE_STARTED) - { - SendChatTarget(ClientID, "Kill Protection enabled. If you really want to kill, type /kill"); - return; - } - - pPlayer->m_LastKill = Server()->Tick(); - pPlayer->KillCharacter(WEAPON_SELF); - pPlayer->Respawn(); - } + else if(MsgID == NETMSGTYPE_CL_KILL) + OnKillNetMessage(static_cast(pRawMsg), ClientID); } if(MsgID == NETMSGTYPE_CL_STARTINFO) { @@ -2485,6 +2411,68 @@ void CGameContext::OnSetTeamNetMessage(const CNetMsg_Cl_SetTeam *pMsg, int Clien } } +void CGameContext::OnIsDDNetLegacyNetMessage(const CNetMsg_Cl_IsDDNetLegacy *pMsg, int ClientID, CUnpacker *pUnpacker) +{ + IServer::CClientInfo Info; + if(Server()->GetClientInfo(ClientID, &Info) && Info.m_GotDDNetVersion) + { + return; + } + int DDNetVersion = pUnpacker->GetInt(); + if(pUnpacker->Error() || DDNetVersion < 0) + { + DDNetVersion = VERSION_DDRACE; + } + Server()->SetClientDDNetVersion(ClientID, DDNetVersion); + OnClientDDNetVersionKnown(ClientID); +} + +void CGameContext::OnShowOthersLegacyNetMessage(const CNetMsg_Cl_ShowOthersLegacy *pMsg, int ClientID) +{ + if(g_Config.m_SvShowOthers && !g_Config.m_SvShowOthersDefault) + { + CPlayer *pPlayer = m_apPlayers[ClientID]; + pPlayer->m_ShowOthers = pMsg->m_Show; + } +} + +void CGameContext::OnShowOthersNetMessage(const CNetMsg_Cl_ShowOthers *pMsg, int ClientID) +{ + if(g_Config.m_SvShowOthers && !g_Config.m_SvShowOthersDefault) + { + CPlayer *pPlayer = m_apPlayers[ClientID]; + pPlayer->m_ShowOthers = pMsg->m_Show; + } +} + +void CGameContext::OnShowDistanceNetMessage(const CNetMsg_Cl_ShowDistance *pMsg, int ClientID) +{ + CPlayer *pPlayer = m_apPlayers[ClientID]; + pPlayer->m_ShowDistance = vec2(pMsg->m_X, pMsg->m_Y); +} + +void CGameContext::OnSetSpectatorModeNetMessage(const CNetMsg_Cl_SetSpectatorMode *pMsg, int ClientID) +{ + if(m_World.m_Paused) + return; + + int SpectatorID = clamp(pMsg->m_SpectatorID, (int)SPEC_FOLLOW, MAX_CLIENTS - 1); + if(SpectatorID >= 0) + if(!Server()->ReverseTranslate(SpectatorID, ClientID)) + return; + + CPlayer *pPlayer = m_apPlayers[ClientID]; + if((g_Config.m_SvSpamprotection && pPlayer->m_LastSetSpectatorMode && pPlayer->m_LastSetSpectatorMode + Server()->TickSpeed() / 4 > Server()->Tick())) + return; + + pPlayer->m_LastSetSpectatorMode = Server()->Tick(); + pPlayer->UpdatePlaytime(); + if(SpectatorID >= 0 && (!m_apPlayers[SpectatorID] || m_apPlayers[SpectatorID]->GetTeam() == TEAM_SPECTATORS)) + SendChatTarget(ClientID, "Invalid spectator id used"); + else + pPlayer->m_SpectatorID = SpectatorID; +} + void CGameContext::OnChangeInfoNetMessage(const CNetMsg_Cl_ChangeInfo *pMsg, int ClientID) { CPlayer *pPlayer = m_apPlayers[ClientID]; @@ -2588,6 +2576,9 @@ void CGameContext::OnChangeInfoNetMessage(const CNetMsg_Cl_ChangeInfo *pMsg, int void CGameContext::OnEmoticonNetMessage(const CNetMsg_Cl_Emoticon *pMsg, int ClientID) { + if(m_World.m_Paused) + return; + CPlayer *pPlayer = m_apPlayers[ClientID]; auto &&CheckPreventEmote = [&](int64_t LastEmote, int64_t DelayInMs) { @@ -2662,6 +2653,39 @@ void CGameContext::OnEmoticonNetMessage(const CNetMsg_Cl_Emoticon *pMsg, int Cli } } +void CGameContext::OnKillNetMessage(const CNetMsg_Cl_Kill *pMsg, int ClientID) +{ + if(m_World.m_Paused) + return; + + if(m_VoteCloseTime && m_VoteCreator == ClientID && GetDDRaceTeam(ClientID) && (IsKickVote() || IsSpecVote())) + { + SendChatTarget(ClientID, "You are running a vote please try again after the vote is done!"); + return; + } + CPlayer *pPlayer = m_apPlayers[ClientID]; + if(pPlayer->m_LastKill && pPlayer->m_LastKill + Server()->TickSpeed() * g_Config.m_SvKillDelay > Server()->Tick()) + return; + if(pPlayer->IsPaused()) + return; + + CCharacter *pChr = pPlayer->GetCharacter(); + if(!pChr) + return; + + // Kill Protection + int CurrTime = (Server()->Tick() - pChr->m_StartTime) / Server()->TickSpeed(); + if(g_Config.m_SvKillProtection != 0 && CurrTime >= (60 * g_Config.m_SvKillProtection) && pChr->m_DDRaceState == DDRACE_STARTED) + { + SendChatTarget(ClientID, "Kill Protection enabled. If you really want to kill, type /kill"); + return; + } + + pPlayer->m_LastKill = Server()->Tick(); + pPlayer->KillCharacter(WEAPON_SELF); + pPlayer->Respawn(); +} + void CGameContext::OnStartInfoNetMessage(const CNetMsg_Cl_StartInfo *pMsg, int ClientID) { CPlayer *pPlayer = m_apPlayers[ClientID]; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 72e7f2027..242bdf66e 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -292,8 +292,14 @@ public: void OnCallVoteNetMessage(const CNetMsg_Cl_CallVote *pMsg, int ClientID); void OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientID); void OnSetTeamNetMessage(const CNetMsg_Cl_SetTeam *pMsg, int ClientID); + void OnIsDDNetLegacyNetMessage(const CNetMsg_Cl_IsDDNetLegacy *pMsg, int ClientID, CUnpacker *pUnpacker); + void OnShowOthersLegacyNetMessage(const CNetMsg_Cl_ShowOthersLegacy *pMsg, int ClientID); + void OnShowOthersNetMessage(const CNetMsg_Cl_ShowOthers *pMsg, int ClientID); + void OnShowDistanceNetMessage(const CNetMsg_Cl_ShowDistance *pMsg, int ClientID); + void OnSetSpectatorModeNetMessage(const CNetMsg_Cl_SetSpectatorMode *pMsg, int ClientID); void OnChangeInfoNetMessage(const CNetMsg_Cl_ChangeInfo *pMsg, int ClientID); void OnEmoticonNetMessage(const CNetMsg_Cl_Emoticon *pMsg, int ClientID); + void OnKillNetMessage(const CNetMsg_Cl_Kill *pMsg, int ClientID); void OnStartInfoNetMessage(const CNetMsg_Cl_StartInfo *pMsg, int ClientID); bool OnClientDataPersist(int ClientID, void *pData) override; From 41c83da699c7b03e79043d188bbd404078788c88 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Fri, 8 Sep 2023 20:22:32 +0300 Subject: [PATCH 092/126] CGameContext::OnMessage: Replace if-else with switch() --- src/game/server/gamecontext.cpp | 41 +++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index c2b3c90fd..952ec9ce2 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1928,30 +1928,47 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) if(Server()->ClientIngame(ClientID)) { - if(MsgID == NETMSGTYPE_CL_SAY) + switch(MsgID) + { + case NETMSGTYPE_CL_SAY: OnSayNetMessage(static_cast(pRawMsg), ClientID, pUnpacker); - else if(MsgID == NETMSGTYPE_CL_CALLVOTE) + break; + case NETMSGTYPE_CL_CALLVOTE: OnCallVoteNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_VOTE) + break; + case NETMSGTYPE_CL_VOTE: OnVoteNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_SETTEAM) + break; + case NETMSGTYPE_CL_SETTEAM: OnSetTeamNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_ISDDNETLEGACY) + break; + case NETMSGTYPE_CL_ISDDNETLEGACY: OnIsDDNetLegacyNetMessage(static_cast(pRawMsg), ClientID, pUnpacker); - else if(MsgID == NETMSGTYPE_CL_SHOWOTHERSLEGACY) + break; + case NETMSGTYPE_CL_SHOWOTHERSLEGACY: OnShowOthersLegacyNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_SHOWOTHERS) + break; + case NETMSGTYPE_CL_SHOWOTHERS: OnShowOthersNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_SHOWDISTANCE) + break; + case NETMSGTYPE_CL_SHOWDISTANCE: OnShowDistanceNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_SETSPECTATORMODE) + break; + case NETMSGTYPE_CL_SETSPECTATORMODE: OnSetSpectatorModeNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_CHANGEINFO) + break; + case NETMSGTYPE_CL_CHANGEINFO: OnChangeInfoNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_EMOTICON) + break; + case NETMSGTYPE_CL_EMOTICON: OnEmoticonNetMessage(static_cast(pRawMsg), ClientID); - else if(MsgID == NETMSGTYPE_CL_KILL) + break; + case NETMSGTYPE_CL_KILL: OnKillNetMessage(static_cast(pRawMsg), ClientID); + break; + default: + break; + } } if(MsgID == NETMSGTYPE_CL_STARTINFO) { From ca2926335b364d194c85f763623b9e265b6c99e4 Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Sun, 10 Sep 2023 09:44:32 +0200 Subject: [PATCH 093/126] Disable scissor for clearing the framebuffer --- src/engine/client/backend/opengl/backend_opengl.cpp | 10 ++++++++++ src/engine/client/backend/opengl/backend_opengl3.cpp | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/engine/client/backend/opengl/backend_opengl.cpp b/src/engine/client/backend/opengl/backend_opengl.cpp index 66e4e2f72..cb0ef4824 100644 --- a/src/engine/client/backend/opengl/backend_opengl.cpp +++ b/src/engine/client/backend/opengl/backend_opengl.cpp @@ -966,8 +966,18 @@ void CCommandProcessorFragment_OpenGL::Cmd_TextTextures_Create(const CCommandBuf void CCommandProcessorFragment_OpenGL::Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand) { + // if clip is still active, force disable it for clearing, enable it again afterwards + bool ClipWasEnabled = m_LastClipEnable; + if(ClipWasEnabled) + { + glDisable(GL_SCISSOR_TEST); + } glClearColor(pCommand->m_Color.r, pCommand->m_Color.g, pCommand->m_Color.b, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if(ClipWasEnabled) + { + glEnable(GL_SCISSOR_TEST); + } } void CCommandProcessorFragment_OpenGL::Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand) diff --git a/src/engine/client/backend/opengl/backend_opengl3.cpp b/src/engine/client/backend/opengl/backend_opengl3.cpp index f42da3802..e251329ed 100644 --- a/src/engine/client/backend/opengl/backend_opengl3.cpp +++ b/src/engine/client/backend/opengl/backend_opengl3.cpp @@ -756,12 +756,22 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_TextTextures_Create(const CCommand void CCommandProcessorFragment_OpenGL3_3::Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand) { + // if clip is still active, force disable it for clearing, enable it again afterwards + bool ClipWasEnabled = m_LastClipEnable; + if(ClipWasEnabled) + { + glDisable(GL_SCISSOR_TEST); + } if(pCommand->m_Color.r != m_ClearColor.r || pCommand->m_Color.g != m_ClearColor.g || pCommand->m_Color.b != m_ClearColor.b) { glClearColor(pCommand->m_Color.r, pCommand->m_Color.g, pCommand->m_Color.b, 0.0f); m_ClearColor = pCommand->m_Color; } glClear(GL_COLOR_BUFFER_BIT); + if(ClipWasEnabled) + { + glEnable(GL_SCISSOR_TEST); + } } void CCommandProcessorFragment_OpenGL3_3::UploadStreamBufferData(unsigned int PrimitiveType, const void *pVertices, size_t VertSize, unsigned int PrimitiveCount, bool AsTex3D) From e19a96d0e58acb432bdcabc37ec1a452d9ad8e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 10 Sep 2023 11:59:22 +0200 Subject: [PATCH 094/126] =?UTF-8?q?Render=20editor=20brush=20selection=20s?= =?UTF-8?q?ize=20with=20`=E2=A8=AF`=20instead=20of=20`,`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For example, `20⨯10` instead of `20,10`. This is the "vector" symbol, which makes the most sense in this context. --- src/game/editor/mapitems/layer_tiles.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/editor/mapitems/layer_tiles.cpp b/src/game/editor/mapitems/layer_tiles.cpp index bf722c109..cee314d73 100644 --- a/src/game/editor/mapitems/layer_tiles.cpp +++ b/src/game/editor/mapitems/layer_tiles.cpp @@ -237,7 +237,7 @@ void CLayerTiles::BrushSelecting(CUIRect Rect) m_pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1); m_pEditor->Graphics()->QuadsEnd(); char aBuf[16]; - str_format(aBuf, sizeof(aBuf), "%d,%d", ConvertX(Rect.w), ConvertY(Rect.h)); + str_format(aBuf, sizeof(aBuf), "%d⨯%d", ConvertX(Rect.w), ConvertY(Rect.h)); TextRender()->Text(Rect.x + 3.0f, Rect.y + 3.0f, m_pEditor->m_ShowPicker ? 15.0f : m_pEditor->MapView()->ScaleLength(15.0f), aBuf, -1.0f); } From 4fb44fc0e860f0edb6cb2b46eec43151fc7fa32b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 9 Sep 2023 21:12:21 +0200 Subject: [PATCH 095/126] Improve dropdown menu button label alignment Reduce the space reserved for the label by the space for the dropdown icon instead of rendering the label behind the icon. This is more noticeable with smaller dropdown menu buttons, which will be used in the future. --- src/game/client/ui.cpp | 25 +++++++++++++++---------- src/game/client/ui.h | 1 + 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 7edb5210f..6022aa193 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -866,9 +866,14 @@ bool CUI::DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float int CUI::DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pID, const std::function &GetTextLambda, const CUIRect *pRect, const SMenuButtonProperties &Props) { - CUIRect Text = *pRect; + CUIRect Text = *pRect, DropDownIcon; Text.HMargin(pRect->h >= 20.0f ? 2.0f : 1.0f, &Text); Text.HMargin((Text.h * Props.m_FontFactor) / 2.0f, &Text); + if(Props.m_ShowDropDownIcon) + { + Text.VSplitRight(pRect->h * 0.25f, &Text, nullptr); + Text.VSplitRight(pRect->h * 0.75f, &Text, &DropDownIcon); + } if(!UIElement.AreRectsInit() || Props.m_HintRequiresStringCheck || Props.m_HintCanChangePositionOrSize || !UIElement.Rect(0)->m_UITextContainer.Valid()) { @@ -946,6 +951,14 @@ int CUI::DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pID, const Index = 1; Graphics()->TextureClear(); Graphics()->RenderQuadContainer(UIElement.Rect(Index)->m_UIRectQuadContainer, -1); + if(Props.m_ShowDropDownIcon) + { + TextRender()->SetFontPreset(EFontPreset::ICON_FONT); + 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); + DoLabel(&DropDownIcon, FONT_ICON_CIRCLE_CHEVRON_DOWN, DropDownIcon.h * CUI::ms_FontmodHeight, TEXTALIGN_MR); + TextRender()->SetRenderFlags(0); + TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); + } ColorRGBA ColorText(TextRender()->DefaultTextColor()); ColorRGBA ColorTextOutline(TextRender()->DefaultTextOutlineColor()); if(UIElement.Rect(0)->m_UITextContainer.Valid()) @@ -1638,6 +1651,7 @@ int CUI::DoDropDown(CUIRect *pRect, int CurSelection, const char **pStrs, int Nu SMenuButtonProperties Props; Props.m_HintRequiresStringCheck = true; Props.m_HintCanChangePositionOrSize = true; + Props.m_ShowDropDownIcon = true; if(IsPopupOpen(&State.m_SelectionPopupContext)) Props.m_Corners = IGraphics::CORNER_ALL & (~State.m_SelectionPopupContext.m_Props.m_Corners); if(DoButton_Menu(State.m_UiElement, &State.m_ButtonContainer, LabelFunc, pRect, Props)) @@ -1656,15 +1670,6 @@ int CUI::DoDropDown(CUIRect *pRect, int CurSelection, const char **pStrs, int Nu ShowPopupSelection(pRect->x, pRect->y, &State.m_SelectionPopupContext); } - CUIRect DropDownIcon; - pRect->HMargin(2.0f, &DropDownIcon); - DropDownIcon.VSplitRight(5.0f, &DropDownIcon, nullptr); - TextRender()->SetFontPreset(EFontPreset::ICON_FONT); - 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); - DoLabel(&DropDownIcon, FONT_ICON_CIRCLE_CHEVRON_DOWN, DropDownIcon.h * CUI::ms_FontmodHeight, TEXTALIGN_MR); - TextRender()->SetRenderFlags(0); - TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); - if(State.m_SelectionPopupContext.m_SelectionIndex >= 0) { const int NewSelection = State.m_SelectionPopupContext.m_SelectionIndex; diff --git a/src/game/client/ui.h b/src/game/client/ui.h index eca56fd7b..ac26a640b 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -203,6 +203,7 @@ struct SMenuButtonProperties bool m_HintRequiresStringCheck = false; bool m_HintCanChangePositionOrSize = false; bool m_UseIconFont = false; + bool m_ShowDropDownIcon = false; int m_Corners = IGraphics::CORNER_ALL; float m_Rounding = 5.0f; float m_FontFactor = 0.0f; From 47addc4175b17024bd40c5688ec0b883882104d2 Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Sun, 10 Sep 2023 17:26:58 +0200 Subject: [PATCH 096/126] Make skin refind bit more aggressive --- src/game/client/components/chat.cpp | 4 ++++ src/game/client/components/ghost.cpp | 8 ++++++-- src/game/client/components/killmessages.cpp | 1 + src/game/client/gameclient.cpp | 5 +++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index ead69b10b..b5c875595 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -817,6 +817,10 @@ void CChat::RefindSkins() Line.m_RenderSkinMetrics = pSkin->m_Metrics; } + else + { + Line.m_RenderSkin.Reset(); + } } } diff --git a/src/game/client/components/ghost.cpp b/src/game/client/components/ghost.cpp index 1a14e91a0..e1f007c8a 100644 --- a/src/game/client/components/ghost.cpp +++ b/src/game/client/components/ghost.cpp @@ -675,15 +675,19 @@ void CGhost::RefindSkin() if(!Ghost.Empty()) { IntsToStr(&Ghost.m_Skin.m_Skin0, 6, aSkinName); + CTeeRenderInfo *pRenderInfo = &Ghost.m_RenderInfo; if(aSkinName[0] != '\0') { - CTeeRenderInfo *pRenderInfo = &Ghost.m_RenderInfo; - const CSkin *pSkin = m_pClient->m_Skins.Find(aSkinName); pRenderInfo->m_OriginalRenderSkin = pSkin->m_OriginalSkin; pRenderInfo->m_ColorableRenderSkin = pSkin->m_ColorableSkin; pRenderInfo->m_SkinMetrics = pSkin->m_Metrics; } + else + { + pRenderInfo->m_OriginalRenderSkin.Reset(); + pRenderInfo->m_ColorableRenderSkin.Reset(); + } } } if(!m_CurGhost.Empty()) diff --git a/src/game/client/components/killmessages.cpp b/src/game/client/components/killmessages.cpp index 3f4881912..212c7293c 100644 --- a/src/game/client/components/killmessages.cpp +++ b/src/game/client/components/killmessages.cpp @@ -397,6 +397,7 @@ void CKillMessages::RefindSkins() if(m_aKillmsgs[r].m_KillerID >= 0) { + m_aKillmsgs[r].m_KillerRenderInfo = {}; const CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_KillerID]; if(Client.m_aSkinName[0] != '\0') m_aKillmsgs[r].m_KillerRenderInfo = Client.m_RenderInfo; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 705eb1838..72c1d8591 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -3343,6 +3343,11 @@ void CGameClient::RefindSkins() Client.m_SkinInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; Client.UpdateRenderInfo(IsTeamPlay()); } + else + { + Client.m_SkinInfo.m_OriginalRenderSkin.Reset(); + Client.m_SkinInfo.m_ColorableRenderSkin.Reset(); + } } m_Ghost.RefindSkin(); m_Chat.RefindSkins(); From be74fed25b83bb788a53782bb5be778e6dc040a9 Mon Sep 17 00:00:00 2001 From: louis <69405348+l-ouis@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:32:12 -0400 Subject: [PATCH 097/126] replace unsymmetrical default skin with fixed+optimized version replace unsymmetrical default skin --- data/skins/default.png | Bin 3774 -> 5477 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/skins/default.png b/data/skins/default.png index f167ddeb37aa983604840d0ed24a794211e37439..168a98844ed96683c5ba1cfd5d75f8dd2f908d3a 100644 GIT binary patch literal 5477 zcmV-r6`JaaP)~QfI=0f z02qb_3Ft4xVG4j@s7^pZQJ4ZihM_e77>+0ahT+OSrGF?W0EVGmKQqZ6N9||@JwqSU zpLCAS)35Xi-A5BB$(04b6g1~1g$vfRDvhDr=pemEUtIDQ9i|o3jI4@sw4P2AB>X|6 z9Gx(s>2!vQaM1cj(m;Bf;4daR3JGxqyyT6Dh!FoS9HSJg7c8y-HD(n^;b;Ko%9( zJt54wj}X%+bY15n9?uou@DKb0 z5mbgre62#ab>BxAoY52?9$F73cTiq>Xd(9BGYcDT86c8xC&@%Cogi4~h?WVULzv{( zDs)-*MuY~nD&m8KBKd>!^23XL1>l)GrXwmU625Z>axt7>vEQ}o{5pn7er-aB^}n64 zWywgXfg8l8K`Pi)Cxm>2(g1{Mv--Hh4WUG6L@(SSJ!&dJlk5 ztVp8x_LYer2sVgz(ivD&1k6Y`mF@6*iPS<;i7&kP-~xyOP%5nucp~vMT@gT_h@l*U z4dV0Kz-AZJB!3!RO)aPg^`#!vg07}iUAKi~T&P$!EmsBL=$>033P6R@#lZKvNWn%S zf=yDCb#x~NO!6mDCt63Z(n&%PPSC5gmO4_B4%%wJjuLKjFDu zw@4L$mmXg9j{=ZZGzq@(`f~9Z!3Oz5-oY)1H0nd!=mq*ch|3)>&^GE5^e(BnyR8^y z(n*4iPSP?;3H7#+j4_0<1KR|h{BJ$I5(n;?Es~#ufo39KOf-Bq?a0Mig2m2+ymRLv ziqRx`gJ6NzX(FY#y$IwY5@|J^BZSa7TA7=@w$P0N^!$}SUyS#_-ueHT{BJywiGw@l z_!3{(v2v7H*T+dFt|nOQO`Qi_h)Aa$ltmDd6AGYdqY4W*Xg zjAWwAMC$H5knG=)wP2$jng!J_dkaP;mGB9Se@ z_rpTIZ>2It{2S#IO{NGZ7Xis=Kwl9w^d;5Ta8F<+4+qZk6@`=Iy%}v9VZo$s*t2GA zfW)7GS))22EhWi&X`L2RZlgB+O30(vC{nk97O8ZQKs;L-kEN4Zv8pLPUfI{X5jbf!l|QBn^rETwdSywZY0R8tNyy%1K2)GWt>$!2zzUcCG<4CPG8WE^cfwZOiCxK!b1b;HG+gMsFrR6ESl44adL0fjCLX^l~_N! z1FLsI(O_q(ofC?HWOSo!f`epJ7fp+R%BW6LXcxUlKhZe?ouV)3Ug|?px(%-AP8SIk zOBcqY>-&pDREnPi6-pKI|MuzTq8V+P(glJ8ou?a25wI~-bi`U!((vro!6E^*z|Vtf z6;edUyFg7`Fr!U5`itOTe^41W76DbuRnb`0=p-S%d88Z&+CM=zpoIyM+F@^oz*ge zi!2FMF%GA_|B%hBl0RP*b}h>giKeZmxfif%a!cfkj`SNr<+$mrs!CgTw07u@p?Dy(J4Sb}FFKx=u6NLMRvtP=aG@jQ3DtIRd-B^|0AlDv zLcyf;41)x_SaSN zA5|5l`7~B72w|W(nId388`#wV{RuHVk@nbz{*EgE`?n7k1pwRUw)5W$=%;zddyo(Y z?x&!`Ol^md7l0##PED$cq;izQcMkI%yKb+5Qh3W;0Z&q zG?P9e1nEQCKv$7;s4M`z37l?AB$F#n0t$tb3W^Psp;-Y~Ob9~@HNPy|)N^U(tH=nC zw`PR|WHc#`$r%+eZ*UbX8Ig{~!_zURUnLChPzLRh#~~`x<3F|7$J9@L-bytE zU>>1S%~Fmo0EccL8n6J=tdt@)%FUV;fCmWnGUEXxqcdf5Zy0SG7sJ+RwXo-wdO^8+ zP903@Q6BZGB!Ry#*7Yr_L_vp&0h4x82VV#0`LKck9+T$T}LFaC3-h2A=dj2Qvhso>~3)dz!ZR7F$KUw zS*42=5Q%GvV zY$^ceB;ey~`vfik)Xba&)XgDH3&2OwQ6BH@GizI#{ELRAi?JxL=vn~gIdKLc;pkn% z#Sma~8PFN-)3gBGN@!Fy89Wc*+jYNvMr~}IR0HcKRL82Z)v$bYRWT00XDR=jIjXAf ztUrGSo2S$S3yAM`ci&tOMjaYEWzH zLBnW75Qb7Ws!#bzCY0U|{H&9%1cmCj^4~R@3HCGd0+JJwU+I)!IfSjZbk;ukpWQm} zGRZHd1iMjmx*RH{X#p5P2qQx%h%gg^D4G;h|M38v{8L=^yR4=S%7yg0vmhDCW>Qcy zC73P+pr4o)oX_6Z_PGDyl|BD6`M1px(}IWotg{&pp#J*5nH1EIL`2b-gkEiGYI(^| z-0<^X+Wvj=?{o8D5N$NmsQ{4Ul3%cMB}a+9fW0evX*JsK(bd5v|N7~z#J9iFX{aUR zRf3DXq+9X}PD*~k&XtU!cL)uum(=8#Ke*j~p-vS?J{+_Y!NuC^S^&r~$uHQwf;W(t zFl*QiN|XP>^;sHKyiGt}0F$C_Ozo8TbX)Y&Azz3Z;3AsW$-1dqpqPB&D+u0>r$QijAX9`AR&zB0Ic z(cyleMovh6K{EPKmV?gt_c6(De-TjGAStwuKq#7A5Yzf!#anOfO?Eh(~P${ZL^{F0Jpm-HUKy|}J&_p^(Ae1ehgeiR+j5G7@MnK_d@S zlFO3+syt5F{xhwnbi4YgZo5d8=`PA52=P2w*tkYX^lw`OGY4LcwNqQ-u3I}|&*HAW zMCYsT@BDYn;Q#aId_T|M3v_IGCsne(vZ|Mh=s@2P6#9_{5W_Xe@1ePrL$E*&?Vxg2 zw~?;LNkwTqy-Hp}9(d^$8b?JGTx!w12*lG0I!g$lGn7g3E=qn8D+o3?Mcu7zD<_;O zEk?a)3mvCl377FJ9j7hSlTuWiT~#PAMRzfsAlT>xEvBNxa9#3uCNIGzUYc%In>qDz zNgS1<8dQ&JP&taz<@%~>d58kko1V6C?7&5Oih5B2r)>*~@X-4Nn`K)yK+1*JOil6& z7DOU-pf&U|{Y41EU-S~Kp$?Sb)NLUd*AqhI8;WsE0U(q73fK@S)R>x6cj`;ssW~;G z6gO=P$(TY2p)M43C@%nHl3xKQhr`-#6Cs3_IIaMY>2L}-F&x-#s|g{r&XEOxOouaV z?#7|ka|j_c*{KCUFr6*LoDdFfubPAqxkx1)UjWE-Ho+7DH?-Hwgb-M%|7Ac)Ch^0! z5V)wF%7Z~Y9TN~tRtxvZKv1i z8~T%SBu4xFPVdrA>P-dJ-wdRlNp`abW)7R|z8%;w)F-|v0$NQ7Vi@X^-4p?3Q-T?W zVI~DN48u$dZkUsgWSBjGFr@t0*AyQ0L;x`70K$lN4+sszy&vtK(J21STm>+@d#>*I zr2E5Bl@=c}4#34^Lc#Ktw}EEuuWTD=5~cuHDL0{D`O4csv-VfI4KxT-0IZOgP_TT} zZQ#20Cq0*I|J{Iw6`^4Hs@uRd?N9nI*Pi(R3@byy@>REikf0vUFMg5@i2 z1N{>~$8BI^IH3TT+hHvZ)otLo_9v$-H-YN-UM0es0$`Fqk|xkYG@c@@=nN7OVN(+7 zka#MKfK!$`IwB(C9AClLvwR&+L5C>-0Z7Jnf}bsU?F^E!jfPmAgm#7#=7f|C5ELx8 z2YK7KZ|{BVvB%K1ZCfw@?xn62Y?uPzPcoVi0=O}^ok22gAkcZLOF@UlQ(zybv=AqN zhz2y9Hc}?_q2#=LmRtk~RurGe$jG2D^6~ZOh7B9$T)cP@U}R-wpZ-rF4;vMk`3z%fc0&#J0&G0+w1khKlpbPE?hX9|G(1|07;T@HQ_QE$+o{_ z)FuQl&%$qE`-^~l#M2K1x|ibU-_eTxBG4q=3xL=T(b3T|zUa}T2LeU6Zr#9L`_Jf# zV3`6yk}-gA8U1W706ho+jTawP)AiWMuwvg6DlfO`wTP{L)5B!=sXz$uB&y^26nbUy?z1wfWL1jryVn0z`X^)oCK^-xQqsx6ae{4gz7@+Wk8vecJz7o?%fe6x_0e~ zsHmt<>53qk0+7qgaOVgCoD0Eaz)1Rw5WoqFA&D@ddLWQw>?&Hc=>NM6n3IzuE(7k! z#fLE@<8eX&kA!G2$fJY+_Q)>ZB)|Hhz>?96d;@>{+;N_!OX)!^-Vg{7aaQq^naN`OAvH7*B1Z)01jnXNoGw=04e|g b00;m8000000Mb*F00000NkvXXu0mjfu)ZC7 literal 3774 zcmW+(c{~&T<9_cjV`wwCFl$I6LO#yTRVYUhW5ShvkP= z{~})9A#W(1f7E<;=zwX$rIn?=Rk!}hzICP8+{LEVRb!u%K0f#PE6kliH~?-T{}pL% ztgtc>R0aFT$0&!800dXD&h~|%0qF5fk#|5q$bc%dE+q~n0jiqeA02M+2WV9O?ZFo4 z`IKiQUskuJFWHSD3<3W5w6(FpeLdQ`1-an|)2V~2`b(t>3*477tUA)i8qjxekb0JD zCaWq4kN)c8V?S)1a439kFr%^i)gi&=P(ER~^Ot1IkOH>4Ms$l9Q(cJ> zEJF!P&u}hn-noG_ir>CCLVOYPsjYT2B5b`SXg5xk41Fz2*g54Ss7OO5RO2XOG7DiA z9p&bj;*K}2yOZB7?ap9zB6x@lW^aslnw;%Xgf@&f@v?wo)IRcV#`~t-Qkn|o2WI)@ zoZ|qhLk3fHJ?HMVHXOVo{s{Mbg3#XA8deAP1L3MbX#8L`6-A9zPYywF+s_U$i*JSz zl`Rfi19b!(ESIUt87%2_XqK~6f4B9IM#^S@y7!`_MFjWA**g;)0`y0{$FLNXd>K#U zM-&=DHrj%UEGN@J*W1-n7|&E_^UI{$>o&n^;O5E^q0e*co*Bb#(ZCMCmx*wy-`BhX zzRi%b#@l^QZ5ECm4bUb)lGk`a*q`!tQ1`&4^X5?|NqU1ac4ar0I4`bKStgKLhFoE( z*`eu+EMoG)WjD||*?x%8bM1$9hC-f`?W;UXIJ}iDTBo3Q+70>lvmLgFN8M^n0yjAz zJVu>z8XO#3_wxnt${l-jBImYyY^Te$SO{n*L0)*ryTRWwW6Zvpqy|%-#ejrSG^6*GQ~X=ugm!b z!YF;dMuH)&;8$g*#GU6nWxo%N+dar66#mO%)j~PuB7q{t08v2y~b4ckTeQkl_trBB){N8e`3#W}77~q-P-LqymI&e-s&mG=uCV{!o;5Sy4$x43IC>_Rwvr9C}&v4 zG#0*ZENA^+Tfxl89zXok2_NS=y0?iwo^$@t6$SkY{A5xuN2 zPBAt?L#7s5%(bUZIN%F}=m5POwcRFa%-IxfBaDB!qW+80;-MN@MikH%?qH}Z%oSA) z8^)`nT2k~GFNG{AJPx0H5vseNba^{IRVEcO68=2e&|K-|X7;KKq;*Fb%F+kD-Y}}_%2V1-emmDN+zu04nSTgHfpWWII=f=tU zg2*_iZnFt^?^vlo-lZ_b#KUEQpLSQda`oXy~ zoHU%OUl?xvO#lYUIU_0tp#vx#b2_8$WJ%0IMJ59WB73A=&_ELhLU&HzMu8M;uQ42# z2t`BDN;t5^vrcbB-l$0FWrOX=O9wOJvviOnY{|nn{5F7fU@tLw( z-ygY>$QOz#!=1bpifRzKYvIgT;lAc{%@@Dl2Tnsw5W@!R%whcgPQ{RX^u)DVNc+ne zr0;e~stnyVykGH?@OHwcGL~qg8BfpH7)rnAcLFqT6$yuEf+q1R4y4O_ceC?u15}Q4 z@&%3GRt)8JNXXE<^Zk^`n-Vuj;zmd{yD;9;#+6;dgL{QSFX5}tm+x4I$~3Fo4}TT0 z27FTRMr#y+{RER+0;RDMq#~-%zF0X=>Q@Z_vycpV^CR6d2GQBrQ}6+X6CANd$6XTD zs)h6>QN8HQ#G^epe@O5Hd88+ehktD9&vEq#sK*RoE4 z*dq6MkvZf4VmOJY$^3LOLhm2r<)D}Qp`Op@=-4Z6LC!XPKm;w4!=IME20FrA;ReE~ zKCXYivK2R)$6HivE^_q`b3Y(3fcNGOtyF*ZSZ}2lyheDMvk1w9&7{jh9ag(HD+QA&ZuYn z<3Zjo_r8iaD?U=~x?a6q1$1t)mGJgNP+!?_L_j~wsJV;}ZQoEEubt}r+ZXPMkw!U+ zMk&|`eCc|kE-1`}+|t^?Ny0(bW~%$I?(i0i&2_J*!CrplZPd)C(b2e>lPZ(_n(6h3icejQW;+gG(vRZhCJ~i_AY*&kATE*?Xs@0V^1A5!b z->9Uk-#kle+$Sbv4J&?Mav&63=j9J*wM#2>uSqJElq4+iy(wPKM9Yb zF?Zc-ob|#0Xh#)%;auQwybRmk#{4nvPZ9MLJVb-7SAC9%4i!gPbtHgb9U-{nx=vzd zl4OZOFkJ;tPJE_5AaqI$XJNw-ndq}sqVC2uPd$GxpIIc$#fpiU*=3hk9Giw6hIE#t zOB+=@KO#X_GWss3c=uBcSwq{;4R`m-@f0@~H#JjnBydzUk3K#L1>iL@+y4-fY75X1 zMRN!QoZ?opsPPX(xAj2Y7Z@;@@NM}8t^qiU>mHaDG?ni}mDgvt>VOWFMEv0q4Ypw8 z<2&Yn7?iOv|8+ZLO|P0Lh+Nro&{YPs#$6_QC5bI&GtKy!U8PWf?2d)}(~J~xyex*- zLsHvd_E1+9*w#c95h1MzC-H>({@=hmQz!*Ta;G7yvBVv8V2x7S%W zF33=ieU^4lp}KEcdB&deWckc|Zj~OptdiVvIZomn8jM_EGn5N|{QdWr+ z;BBeguZ_6`;phS=BSAdsDFLMB@CSRZgBf$~#nehuBZFM8`o^lecx~0v^_&Q9RaRjj zIl)*6KuNP2k(tNg2eZfW+!kXf?DEl?@B-rn>QDOV`e{O!!pft+&nX@0`=BmF0qd` zjGaWvA=}t(i(bP?Nhu8w#_3NJNydpN`m5 zA2EVap-jz(0=W)idAJs4h&JJm-n#f?t(_z|Z7Tb2+#l^NF4Nm4wHbihdG_XRMuef9 mj<<03!Gx4#RLh@V3hZs2Y$~nDwEqDyf6p@j From 2e24e743d37797de1d8a9333847d7d406165879b Mon Sep 17 00:00:00 2001 From: louis <69405348+l-ouis@users.noreply.github.com> Date: Mon, 11 Sep 2023 17:23:39 -0400 Subject: [PATCH 098/126] additional optimization --- data/skins/default.png | Bin 5477 -> 4503 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/skins/default.png b/data/skins/default.png index 168a98844ed96683c5ba1cfd5d75f8dd2f908d3a..6c881d76d3bb53c9e9c37878c3fd8c5e2f67d1f8 100644 GIT binary patch literal 4503 zcmZ`-XH*kP(@tm!EkKZfG!qSi^b)Gli*PY?1f_$NNUx%Fh!p8f%9Z9-lwJfW3M3fm z2!aTrKmsT=kN^^p_Hw`P*LTkQ?U~)3v$OMO&dxmZY>Ju5O?DQ3761UiZe*xu0RRBc zRX{A5>3m&oZ96w`=^5#1TZIG3bBqDpm@^#JSln#g5EGcxOWNA<`#3ju!jQD+x3bzH zA>D8kv9?xMx#MnyGG>disM0OJ(zzemc9`PI|0b}cPqwKx z6zS+bcY8c7oFmgkJckp9Lf53Fv~%=Rm^3;`3o3D~Ug=ua{B{Ar#iTM>;o{1l!l*FA^J z%Iu`JpUtcfp7__S>=n#Dc&fy-u)vw}9m9?UG@CILS00*cCoN@+h39$vmKc^e+EQZr zBXvSb5eYgV9Zs}|F}eZv<9pb#4C7tnsL@P_uED*QoVHAVstbiaCi;Gn;KO^}K3$%# zN(b!B( zahT@4m7%~s-2S(PQirut0Q`{K?Q!NVY54 z*87r$ZYS+{IE%&$%xtE<tPRF9wD(TztkYP`Z2t!9iK2z=6x z41mRaxEi7ht^NASD5Xg{26XoKO{ES{cZF4xPH%rFi&HRB90_K@!9>d0swuv=yuwk0kzya6J;(aQ?PTwRD zL&(&)F-N_**{4Jy7kGnK?Gbxp+F=#F)TjpNJiIuhorzXaiJ>9{gK$Hgs=1XF=%r zED~XvL3Z$Bi*D=M=o47j*rmKO$O0Vk-f~jgzs%}tXp)?L`}gqB3n05lum?yr>zk?L zysEv)4V1yBO`(TmkYn;4LjXArBk@FcAW@4&q<;waNpTtfGirJ7 z$`aq|>C(xMZOwAaP+Gr&hEj7NK%X;1geaGsawFkh;Wdzwb`I3lVR!|Sqnzpcv<>GU z=|+tBz5Etq0l)U?XFz+*S{}BCr{6RSfB{YN#1*M<#94`1n>{tfNW2mj_GVj-B`yuh zYxo`JFsjFht;o@vV2wkm%ml24fly)HLqUi=$(#k*|4v@*Yr)IOh+%ws?t8Z5D*2d5 z8zW<*AnJZRPy5_`VuT^&M2Jh)x=Mu!=xMy6%D3FT%~SjAtD^;LU&Lu_M!D>! zqo{p+;f#I=_M3QWk|2aOEn2#`^D{qwkO~#Z?f=1u?^EL}PPJ1x-* z-n=@&^;(cGu}Alq!d3mjsuj<;v^@W(qad3wj^|9>d^@BmYh#s} z{A-JFYqMYAfT;kXl9@>nW4a8jtmdfK9?BPkkNI89sYzJCO(Wb>S7^uNNrA8`o_Zb0 zRhDAiH{vIvKP0>882dSDUi0r!cL~veV zf-k&sXYnn1I<>b9u0*lX(|zN7NP)e$cQ@yKyIY|g@zxsY^FRpr4m7o9x~1+X<<2{;=RF)`BT;LlF8}zx{6+~Y7zUa z$8L8EBPsO#m-s|X@9k$ZN3HV-l(LRG=L@Q^H&x^YXuZ+;94=tda6iy3_D-**x5AHH zt-8Oe`VwhZpF2N(q~lX7fnr5**+VW~%d8i0u3{!W9;Kh-#ywRPLW)mde8Sya9-l&* zntHSp9-1F8Y_SINSv}nu+OODR228xfJRGyO&Xx`KMYxo+qqvnBph7%|F<&9q4rby& zDOVo@JOUQF|AUo}O@W3)vfY4;5=yWk{&&8)CR|dX61`|d<(7Jz*%^unuJ&iYwIN@7 zJkslGEz&+~j<(;j%n-0sA*YN63}0>8i!>;|FfyZJ$aj%_6g&Dj&(OG;NCcUv%kNh* z6RA^C$X2aqO{CHIixEsvgVI=!UdK1?(Wp&C(S`Vowa5bZJQ3|%ETOQ;kW zj@fW-;MOXr3#uiN)V@!^q_k|NU!-GGaq-@JV( z8tXYtqp1P+d@aB2zAPR25|JglCp|J_Cwb|CdP{kwTr9n-*2eQb+DWVlKs)G<%hE+a z3BWJa1OU00FDA&1!*guDt9eRTNe!V=d!}_`;i}CM*lE1KB-4a0;L<7rm%n%|t`q>! zsHafRUWoz|mJiYNU8Vr9I#J;0FO`v*>*m>z>66|23?USmOC^4_`iBy?zPU=w0|x@u zE9Cm1&Qbj1{K5$r-|<@X1T_Z;+N&!8ZEQP#~3;c^d&$_<-f z*#Nd5u1J;bE3{PG&5n#+7ZBM%Yb(A%&9qCYG<5mTE}g!b{(Uru?};Fx+ZZn~DA2GP zXAMOahLLP*3r)q>%61f_4-VB!qOTv)D!OHpF)fK6JADEXz@)GH%2W4yJe3cW4%$XA z+lSv{P_^R=aHV}uFPIqg?D3a(Zwiev4x_%4mA}+_4f2A>=Os#dT+$-A9lm>0d29+F zv)7&7S9AL3UgY*{sSMM^h1-L1mP*|R{lHNja@=xyMqXEqd=LrJQ@itBiO?U%QCs%; zwn_-~{m$5ZYi$_8;4~@33np=CJ~qz~ox|J4!AkazDtmAk2ijT_$1uh??Cm-ca8K&u zX9j}2U{XS!2p(geyy>uX@H$Sj4;SON1aLDu@>ysWws>pX+=MUD*SBpoJmzwxkI_e= zSf}Z1;A<~g2ba9%muKkzq5Jbi_`$B?C(04|91adTSsqEFFpwG>sHX0}&=TvB@CdYexIb*(1JGS zn?{k>G#Z-l4tk<9b*2A{oWRkziskPh9meBy08-5Azx^Asid$J4WYh4gYS9}@Crei1QUa=A z8rZ^d&}+H|iqBKo>gOI2G($y+>>=&rf#h5XE1 zAHfy+X)4%)>%6hzBZKKzch>@(irM7+B{S2nIccB>=-!W4%H3 zB$2(FZ0GUwPxMOHQ(#a;8|&Hl5S%Ei1W*rgY$mzhP)p?t8}xD+f**mNv=|5Yj)M}$ zLB3{}u7RgLbaY;eqR(Cf-ECh94aFZ82=SBqma`k=Ur2{t2E8jDxqBR#oH$MQD z6aFg+{QnPsNdpz}Zm%aV%kr4Iuw3RGdInx-VK#ZSE1%iBBqH1o$6_Mj!eN*5$bE)h z9R&bHgN(gHXGowr?MLuLm8B?e%!3MEovg3WMSWK6^Ix3y+EU8~Qvpnvf%J7(;9CSY zeYVdOGyL-9UeUaye*cZBnt~|utRiA?Xeej4)!TD-dx3KHxjROZ92Nmw{P+f}e}qtu zOpFJIio(O`uYs-C4(JJ~!G)|z=`;Y7R)(;;c&U&^swzHN-*+11(SX=7f)ASWzhF!fDS{DsBDd9BG+SJqPPO&RBVP+cIp zbSJTeO_^^)iZVW!jcl-8|>}8+9nagiysxh`eQopRluY3+*&d1 zyr<#&>opDhPR33Qs&$Nn)L$L%FLbD)>{cAtkI*G*p?-H+>}zh<`JA43&(X6vyn_qB zUD;V#NTC>R(DU*FJ7k#F$14&7<;+aI5aM*XROD5}Z?5>^xl6kFe@vfD)oPVo8+-;3K&}e}8xeh*`|BeaE+~QfI=0f02qb_3Ft4xVG4j@sDDmCK~b0jK!%|-{}_%a z0EXepKBa#sC;*0`T|YC)A4lzI1wBI_)1P#X&eN~-3Ef8%D9M!tz!Ws+CWQ;uvnq|D z+vp&@M_*j>79FM))Qqf(asXE*zs2%704?5Q(1$<0xQ+`8SZELFm5DrRWI3%(;&c(C%pRT5?=uz6iSQ*-{(7$iK7II zWoy&vb$033P6R@#lZKvNWn%Sf`3g?ly!6`22AoNQ72kQuhL0E5Khpm zw3a$jk`CKKGQLhqjP=SU|NHyb`VwC#Te2wlUKc3X@X!T<4GJsiMgL)+*D`aOut9WT%}>J#)XskytY7-iB)f{jknGD->cwvdc5gs}tL1fBeE zJ-reK?wKu;pM!yBB4122d^hdL#ae>J&V;;k=OK#GBzl8jf!ApwrMSHaW*XgjAWwAMC$H5knG=)wP2$jng!GEIhEpYVi5h9T-!S};LzHg;6Mf@A(6iucGCl>+9 zXh2^PH1s9a*Kki@CJzVB^A&}Y;TYoxB(AXJj zqhS%KKzDgOzRw4LaRu|ENOC-43*__uYcF#(MNnR%3LPip(kZH~+aQap>1RSjga`dQ z)WLzML;t8QWn7hvZ$|SMLO)bw4X)@;7YP+h7sjLO`-?88Z&+CM=zpoIyM+F@^oz*gei!2FMF%GA_|B%hBl0RP* zb}h>giKeZmxfif%a(_$Yi;nagLFKsVtg2BK!9^}oO;ZFEKOI;ynvj<;XZVdG$+ULp zj-hxUvpYt2uP-{Dm#%lvbXFcZMsT4cxU}LqfwEr9^@`!b2-Ed_|z{ zm8qiRy-(2&naZjQ!Nod+vKYJlq`&RAQ|60_!o4edJE{OYv45_QuK=u{-Wt(S5u)Rb zaL80vuMu4A*oOX&D**eq4;BRg+vm3P-wWucdB=N@5C-n2puz@SMcSKW#mj*#k`eKqmvA)_<@7M9`mvF!DP^s4M`Qg%$-M zhRzUHOl#}t0w5A!m^tVg(eX}Gv}X5O)FFhSS`IrIScx)d9<8Homt@j-YDuY~ECN=q zXk9{t$Ajm@4L=TaOdJCE*)hKpI2(-Uc(rY>%yKb+5Qh3W;0Z&qG?P9e1nEQCKv$7; zs4M`z34fe!OC*ykP67&rlM0Frl%ZJxSWE~*3pKwi+thPu=BvmEkGE!p1Y|TRj>#Dn zFmG@bEE$oG#lzDvr(Y!u?@$KquP%lwOUEH9(&In1*vHgQe%?wo1z;YbQO#10E&zva z9~!U#)U1>uHpOzKe{^{OO+ zzc1GHEviI8hl&EQozSsKx}yugbK3?5EC9{_9uKfpvjT905QYwGdPSzBhtqRK^P7A& zzulSCxwdjlH8e<1@~yAOfnOv~6o#|_JV6-TrJkb;z@w{r1uOtPnpgFE)RR^P z;1fa^`dHHf@BxR9d+(ZEMvVY$^ceB;ey~ z`vfik)Xba&)XgDH3&2OwQ6BH@GizI#{ELRAi?JxL=vn~gIdKLc;pkn%#Sma~8PFN- z)3gBGN@!Fy89Wc*+jYNvMr~}IR0HcKRL82Z)v$bYRWT00XDR=jIjXAftUrGSn}4U& z1Ph4ocX!`h597O)M_d8l?mx-Riev@?$xj4g1_C+50!_yO6reW<1@cAu1^_jwQv|IV zrl8Zcspx!N+JEYJO)6S8NI~7oiAao#_J3N^SPDAKP+*CZe-B=o3owv-%urw*_0@PF zP%N#m=q9amw416^u%Y&5pygBBIDe`DJRRWM-$6G6)h2|Ynhuy6EShRiYwAJ6XhaZ( zQa7qk`AH^}-VXe%ldc4avHuS)7svjAwY#>G=yXqMdOPqRgfQ}(s@s7z59RX#Oed5~ zP2^p5&YA@{5HTT0vA-t-nWE{5r-uplGxGwH6Ov!)lwdi8t+#a6KKY;BI)Cso$uFh^ zyHRwy94e)00T@9DBSR>NFcX3(niN$3@c^9sQ(X4Dtfmdhh4i|!AQ{PKQcyD`m@WmN zpO_Y$&)(Pexc}jmJ^wTLx6KjLf`|UBvl$Pd{`$X}6x5GIMA4UoUTtb>dC5=Q@bh2V z{(bWAbMs&jZ8X!V0FdL7Uw^Q3B}a+9fW0evX*JsK(bd5v|N7~z#J9iFX{aURRf3DX zq+9X}PD*~k&XtU!cL)uum(=8#Ke*j~p-vS?J{+_Y!NuC^S^&r~$uHQwf;W(tFl*Qi zN|XPEHSrHJ@gG~$TPpDh zI~R6Fg_6ml!=0ci4u3w|97Qh>T;LUoG0AWJdWLd^Z=~~t^m0XU|MCnRykn@Pm-aZD zuQY+!N%C7iD)Uc*gZ)9J)o%-P889Hp=t^0HGHHq6v-f>x<#~+m zdnP{z=SYv_dv41>a4f$V(U&V*=d|PDNrLEbS=3d1hqJ!$I)Bi4y51zeee0~E0h-e( zLPBgl%pG#Ae=@QlWca4#dSZQF-}Ky^qyx_~$yp@uB>(<6ll_rZy~N#nd3NiF>yZ?n zUv#)L)YgT^nq4TH;2_!5*(ATc>#nLH8qtpgkH>>fH&nr{MO{QvhQux&?{~buGPr!v z;eMb-PDp-1GJpC|mV?gt_c6(De-TjGAStwuKq#7A5Yzf!#anOfO?Eh(~P${ZL^{F0Jpm-HUKy|}J&_p^(Ae1ehgeiR+XMHOcRxxs*e&Ko0Go za#pvIuE$A5X*|73UP2yt=@lABMHF0W(Y*-7(+WCE2%$5SN%1a9ei17OHaJDyt!yhN zoGC3vy=V&^r(X$|@hcstE!2}zRGeK^C@)2KF@K#P*ysc;rlQ1fUGjG(FTo~Wnr>B_ zIrVZ$9F?OQRF7&Py|J zIe#^x6gO=P$(TY2p)M43C@%nHl3xKQhr`-#6Cs3_IIaMY>2L}-F&x-#s|g{r&XEOx zOouaV?#7|ka|j_c*{KCUFr6*LoDdFfubPAqxkx1)UjWE-Ho+7DH?-Hwgb-M%|7Ac) zCh^0!5V)wF%7X>2=^Of!awJCk{Z8-FPU=ks)!z)Ho=JAI2WAeN?7kh?Fw`f$DFRwe2x1uOlid^n zWmAF~hG8ZJH4MW{3vQT`k7SrVfH0)|*w+*u^+W(L=K#Wpb`J;*!@VEvp3x}&&3{}4 zFuQxM?)aqp!%>wMA2SZX#biRk@|CxNX6>(R8)y=y09YwEpy7nhMmuvssfQA*JVEL-sz%}hp`Y+d>`2Y+nL&5S@w}Io^pY&g@Ju?Cs zc7%fED{ce*6F|ppU}HF;0GQihEq@NxZQ!`}C#Nhof$I2PCBm8lV3I$QCeTAPo+7R2 z3=$DxQxfWscq)s4Qu|fhbaI7NXB-8pDlUq43e>phFG11c7_w? zgp>>r6fCy~dE2*d?|tmC$I!NITQC3arLGigm;&HWGMW$qxG}e#K{9S2(0_TVOF@Ul zQ(zybv=AqNhz2y9Hc}?_q2#=LmRtk~RurGe$jG2D^6~ZOh7B9$T)cP@U}R-wp^cS z+dTovXhY)E9x-1bY5I9*WV9Kz~vJvML}& zB0L^XP#6(>&7M1VE&|2u*|Wj_zvz3*Am7sz0FsOegv%Iba{(Afpg{hlawH%67lGFa z)Jo#4B?85Y6)VKDIW1guZEj0Tz%0QpOV>O$#dK$(+v z^m+I0-4Q6dcI}F&sHjiriXfQ+kju+(=Li9u3&CZ;NcxKqz<&vfA&D@ddLWQw>?&Hc z=>NM6n3IzuE(7k!#fLE@<8eX&kA!G2$fJY+_Q)>ZB)|Hhz>?96d;@>{_V zfyMI0?Z^?jeDi7kIkcS+L^4KPx(yhYUWb|6fh{5%=9XM_pTp(}OLdi_59bJzzF_O}%%j1~!$I{;I@1>%`vvrWnL Date: Tue, 12 Sep 2023 21:22:17 +0200 Subject: [PATCH 099/126] Hopefully fix intermittent CI failures Thanks to @Robyt3 for the solution. https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/timeout.20investigation https://github.com/serde-rs/serde/issues/2577 https://github.com/dtolnay/rust-toolchain/pull/94 --- .github/workflows/build.yml | 2 ++ .github/workflows/clang-sanitizer.yml | 2 ++ .github/workflows/clang-tidy.yml | 2 ++ .github/workflows/codeql-analysis.yml | 2 ++ .github/workflows/rust.yml | 4 ++++ 5 files changed, 12 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c962f8761..00c64b6ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,6 +10,8 @@ on: jobs: build-cmake: runs-on: ${{ matrix.os }} + env: + CARGO_HTTP_MULTIPLEXING: false strategy: fail-fast: false matrix: diff --git a/.github/workflows/clang-sanitizer.yml b/.github/workflows/clang-sanitizer.yml index 3f46d8364..2a000d6d7 100644 --- a/.github/workflows/clang-sanitizer.yml +++ b/.github/workflows/clang-sanitizer.yml @@ -11,6 +11,8 @@ on: jobs: check-clang-san: runs-on: ubuntu-latest + env: + CARGO_HTTP_MULTIPLEXING: false steps: - uses: actions/checkout@v3 with: diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index d3af9560d..9c4fe0ce5 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -11,6 +11,8 @@ on: jobs: check-clang-tidy: runs-on: ubuntu-latest + env: + CARGO_HTTP_MULTIPLEXING: false steps: - uses: actions/checkout@v3 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6f2c1276d..439760f8f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,6 +18,8 @@ jobs: analyze: name: Analyze runs-on: ubuntu-latest + env: + CARGO_HTTP_MULTIPLEXING: false strategy: fail-fast: false diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5ff381de6..77a23ab3c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,6 +10,8 @@ on: jobs: rustdoc: runs-on: ubuntu-latest + env: + CARGO_HTTP_MULTIPLEXING: false steps: - uses: actions/checkout@v2 - name: Cache Rust dependencies @@ -28,6 +30,8 @@ jobs: cargo-deny: runs-on: ubuntu-latest + env: + CARGO_HTTP_MULTIPLEXING: false strategy: matrix: checks: From 6440cdcf9185198494f374eeb2a35b8c55b3c647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 9 Sep 2023 10:57:04 +0200 Subject: [PATCH 100/126] Fix country flags MW and MQ not being loaded anymore These PNG files were using an indexed color palette, which is currently not supported, although most image optimizer tools will try to use it if possible. Closes #7121. --- data/countryflags/MQ.png | Bin 1637 -> 1441 bytes data/countryflags/MW.png | Bin 2008 -> 2118 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/countryflags/MQ.png b/data/countryflags/MQ.png index 320c9c7a3f76c1915f389e31d4fd72b2219ec710..8d7681326af6ca98a95204f2bda4bfbaefd6382f 100644 GIT binary patch delta 1434 zcmV;L1!el>4515<8Gi-<007d~e}4b~1!ze`K~#90?VZnW6lEC4KQlW6O4+59iy#sY zUetpJd3@l27EhA1JLm})$z#Ha@kTCN^2CQuFmJ(#9p#T5*#mt?=TxF4kJ2DBAIJ`5BzgU6z{xk9I7d%w6T9xkV0lEVKrWxulR8 z#u)njJ|Z&Gv40Vn3g9X9L!Xsa$g7vy{5X4=qYG_lg|N$a5@bPS?-oA`G8G`rax>sy z*K&Stk&jPb@~SQ{Z+h;9yrce`b+$rx$swE3nUHgMX3enOJ~~$yI=aI2(9$n*ql! zwfSN8GDm-E<2M7B4Yq% zDFB6ZKYvtIfHV_1DHPQGPy+!ba#AR$`=JH`z=TZ-1$IBwM1X{em=p@^eyEuM31d4c zw#!i5QT1*O&g*#EH=Q6eeK*cA8$bZ6eKi>aKWUK{9v*D!B44~l^e~7?p z6#w)Y{m77-%}U!RnH2oc=h#A<7k*pe)UhLc_;eFLlES{es67)GzvRwOm>2=>p%oIW zS*AX?!l45Jy9?L~RCV|ry`8?^BZo6NCzv1s6xa;}3ZMIyQ+q7efdi{1*C?Q>h$<@a zDu2_+@wK+Y#0bz*_|t>8L(3NpOH(~&@Q@rTqN;ZfK>@97+8R3ntSWsJqr|A6;Gtyw!K(rcIv%aUocUnJLI71|Rh2bWx(H_Q-cKo9^Wbb?`OX&}w$Osi z3twkyFF;3yWf8iAj#FS4@m_ziA-vy+yMKOKhaVR^hSXL7tICQBOCktyVSS*f@RtYg zhn8=BVUNQ8!FJ$^zn)qO@PA$37-TD0d{_7f zQyT$#s{AWSo*2aYVTmLKO34j;itOZ6@P#0 zR9*l(=={E_OZYiZc>z{b=%~7cpA(f9prgWV5ts0Dq_P6Up)c-om+9A7&wR_9Uc?Q=CHw*@^?yE)-WN29iW~^Xc@k=MS6(F7P=Nl65^-0%@5=n9+mw%&7a$5n? z3I9ZBSOTW4V67lxamB9&nXUjECH%iVI3S=R7}xccBU1rd3cq{sZfN+j5nsuhwt^K! zwHjZBblgj30<5mC^73;}p{f{Tc&69G7=tkebwRJPWF~;B5=9aHejj5@obWUJ|8|ai o*ie8`3l~KZQ53nt*O31J@)MWe1wCUM+5i9m07*qoM6N<$f)7TbO#lD@ literal 1637 zcmdT@3sVzU6u#LI39lfM*JgL`?q&m6bWn6U0tzG~AqgZ99!eZ*0Tl&mi;=$Sd+_uX&KJu~OdJ-f|O zIyYyTGXTKNpx4B*oWrJ)&t-9dUua^9lNqZE1657<2zzi?qcW)gxKijcnal$K@iQCa zwI-9vU@$~PM1+NfMMg%d)#}jDP>aPf=|*-z#gffo$hCM-84J#s!H;H*Mx#_JgMxxa zWz1V4J?R3=wElQ$G`JQY5D@SRWu6j>C0oM4^~Ipn^iD|s?Mjwv{i)I@P#z7A>PJy# z3}vQV$tQ#&f$e;q?HmJ6M1Vr=TcLd1m9%R8iLz+G`XxqiP-}N1!I@}KYy`&*peza; z4hKhc;A%WL7X!+pK#?9C(%N0genPQ|?N(yqmY9&^24uem*&Sw=DTYLJhLDi?(6l#A zc~OvoIE~{J{uX7%rObJZo<+hLB;kb<3Y=gtoR(pv6rGeZ&rv3spxh|}C!vCb3<>3s zpb7RW1~D0wp?^x{gHpOrLO++#!xDN-EVrVh4}p6?T+Wse_^4FgFQL1|RErokiD0J? zR*PUfin(K`RECl=bU;dXh{<0?uvG**gz%sQSIAHShW1LRW)XZWg55$`D}swrtX&8X zOK>k4dRGMFP|OuWho$l^A$drGw~5IYQuz}W#nhODo{}=1Qn>}iD`b>6h6xDFE>)bv zXkQXSnXCt8RuI$-3Sy9;FxiBZslaFt3O+-bl?27Gvtlv|XAxr`$!Ij{^?H>`6&xHK z7#OJ2>B7Uq*)O!*r7)MJTi?V+#eMj{2DqK3*uYNVv`)V?697To$K*6*RIFl`;+a9C ziceMlQZ%&8Q#HNdblD1Z-nS?Bczw3EzGtiC_Wi1g)UtZPjfxm0uRbqAxRtk`w=nO- zho*-q7E?g>S6PFbLVJ6Dp1+`asNEy7HhIpe%loW5c|lpV^YRXe^e?*HY<#1`t+Vfk zJ1etFT(RLFM(XDw^K&n3w>!EvHeFWHts{3?gJWuKadd-sL-X;ow)4vN=Hxovs{TN~ zCqMbwkQ1Y5VESLjuUt?+coV^|nSOtN$=vlb`n`?Cavy72@wt=hj~Bf-Dr&a$&wV;; z4j8+ayz2y5hOcgl^-S8UhHVKmlJ@NM2U<>1fRt+%TN`M9uz`~!E~p23q&(o+f0&Bo z2V58A_*(ITc1|;|tq)YI^8J9mq1KHRT4j)+k2}}t!F|A#nIILFDSv64F*@rgba;^}-UI4dOkx%^JYJRCWbQfeMQV@vS$-CO=*KHs6TYS*L6 zOUtWcKIsiniuKobTpL^>?VafqL-mAY+q<*Uk9IkxA|9vhx8M8TbxsWB7^qlDMq=7H zPnetZ{lsgRnaJY&0m*cSh)O5h9JA!RwwR(s>+E$OIBdxy#668QJ)5@`rl+#PqdB;+ zWRaI&wNeGDf*%*h9ZR%Qprcq~e966v+z#)0yeI#_ z>?hx*I3$gSZcm>*Ims)0owU5><{Ocwp zTZa0doKHCgr$=;|w&+d22`=xe(O5?kGWU9tLyk6n7r)$o<68E4;ec;FJ8?)L zhJ>bRN-7TJ0#vCgT3jK~M!A(%LaNjrDmYa=aG_FfTxi7w6{#l^Zlg5KWxUKxW(D9)`jlX zF|XK+JL49AHC<+v=BUj$A1oJxEBOjOOHsjxEh<2SA_5+q-oYht{3xB{4VSY^Pva1F z>KKh;YBYn=D0b=?kMNzi&4u82q&34I^2_W{4SqYkgnt(%x>xvr*>VCjmFC-Vn>*qr zH|dz31RO9V&{Ud#1w%l%ES~pet%`1nCLo*(mOVOQ$lBlB-e@wTG{0=_;#IrKOSaFd z6qkLIz?KmpLJ=wWb!#^#{g7|PEnYNzZjYP1<_1J4&Iil8-VmYI1PE&a^u&{k;1C)p zA`~awfPb$yrg_z_a(B|^{owdsvYVf0=eAUDuw?|;A0_;vc^zlH<=&*j%Qj6q8tfGgsOP-GIgA|3^FR76W@Ji>+GI1r`$CpZ!mH%AFC+l+gY4##Z9X76^x3Nup2@p>BW}`{Kqi47rgP=qj15pp0p?T$z}<1185OZ)dh8t)ZI{Gz#v5+b zF;BD?xF?w*7s0M$+GY`QgnN=1o_}aBaHEbn;|-U@bIkVH6U8i<9vvOA7`H!hPF16l z0^AWd`RUYlPWU0Gea^usu7*^QfEv^E%={l(K9ywyTJnb;7dE=zou@c#rT< zb0@borWqEnYB!X?ZH;LjYVO1%oDY`11eR^amvq856|rnH4n!&Qx_Z4p&c(G*F!J1}DNU z-QaC+c-;+I&@uNlR#HIDT-8G^)~-PC{%#0wN+yCup;ybrQE;D%>uR}0|~g2=8&}jB{Psfuf%O; zwE!hzB+wO4C~Bw9Ubog0000#=JtRskPV+Ul_(Syu_D4=ZoH|BfJ_kQlZ=l*ifx%cFzARnB; zX8-_z`}wLubkyqXfxx=>3Mtm;0H%caD1p{t!5dvciTBia0zgMGwz88708rVcz%Xx( zM&sw_=kM>YR4M}k0=&Gu6beN|M1*c$rBZo#c(}Q_xwyE%00;qqC5#9lsuzvvO2zac ziY*0ALiKkdoEAcqit4u!qEpN@&4ef#;UuAYGypt+>Tg5XS6QatVf^#Bu#qsy$M_wD zX(dxY13?A|1<;727_SyLE@7_eV43D(ydoz51ZMODF3iJ>&M*Z%q*)Hb@CwWHuNdzb zX0#vGOJ^9wA>3qCKbv7Fh3RK;K_w%AA`vKWRJaN!m@=}cj8A<7s>`ZHTP%(fU~OC#tc3BnD8 zNdl_pPGvR`CXYFm?o_OcB`#p{%b06?X}C27VS(^P!epFdsl|AOOggwa;qgJbRhv)kGaDoocQbU45SAV|(ns4rK*6oQN z@ZFjM07U;cgLjfH?E(P!o}bDyY>(IA&&xZ$3l{Xuk;K^jsJJ+D{eAnQ8qP$VJzwds z4F6L@W&UgNbVzVX_)xW8XXwzE9BD;a!fL_A?K_IEIMm4hJ3s$5uI=&1Z28NPC&SHd zneA_*zC_~zliIJa4imtyM{=WajBCeRIA`j$|65D@!}@FO$GpjCDc?fq9y4%Y&hE5v z_kv>mwRuQg#Fy`W=aNOIFDDmGng6Knd%C(iU~SLvPZ>ua4W^xcDml35m&lEZy1=cH zyBxx=KpUhR&%S+SlTtM^wYK8=QeaA1MwX`9{t4H8eQ}Vs`grYtLbOTteC?2HL?e?x ze>>NTRQ<-ihez82$IN?026@{1 z_51cJ$Ga9RvvPA{gA%vrFgGqF$`5xuf0C)w(S%nW;_Cob=2r9Fu~D?cJd}D2`FbUsW%OCU0vd zo_l}N-de;=Mq_VHf*ScpOc1^2UCwG3Vx;58)m2TqcIHuJ<-$C4F6rgxitC@=Gp)eI z`&C64AJkZ-^Gv3}2Mx|oIRl{TPCQ8?g|)r1N%t4I$BTWFndt=!({CJN1aQq%clzgi z!5!^mm!uaVJ+et-^}S`5-UrsFPpHuI*g=n9%@Kk&VT+N1(SNJw7H4m+{Briqd_ar?_@ky<-Mx+0l!}7Bg57E-jINh%k eaS?vIH5$N5mm@lV)5FlxZ}9UDQnf0hkNzJ$P*wo| From dcd86cb873da933741c6fe145efc9e92f7f91622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 3 Sep 2023 20:34:40 +0200 Subject: [PATCH 101/126] Use golden angle to generate unique, distinct DDTeam colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DDTeam colors were previously generated in HSL by taking the team index and multiplying it by 360/64° to calculate the hue, which results in team colors being evenly distributed over the entire color range like a rainbow. However, this causes colors of adjacent teams to be very similar and therefore hard to distinguish. Now, the hue is calculated by multiplying the team index with the golden angle (~137.50776°) and taking the modulo 360° of that. Due to the properties of the golden angle, this can generate never repeating sequences of unique colors where the adjacent colors are very distinct. Duplicate code is reduced by adding the `CGameClient::GetDDTeamColor` function. --- src/game/client/components/chat.cpp | 2 +- src/game/client/components/killmessages.cpp | 15 ++++++--------- src/game/client/components/nameplates.cpp | 2 +- src/game/client/components/scoreboard.cpp | 2 +- src/game/client/components/spectator.cpp | 2 +- src/game/client/gameclient.cpp | 8 ++++++++ src/game/client/gameclient.h | 1 + 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index b5c875595..17791ed96 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -977,7 +977,7 @@ void CChat::OnPrepareLines() else if(m_aLines[r].m_NameColor == TEAM_SPECTATORS) NameColor = ColorRGBA(0.75f, 0.5f, 0.75f, 1.f); else if(m_aLines[r].m_ClientID >= 0 && g_Config.m_ClChatTeamColors && m_pClient->m_Teams.Team(m_aLines[r].m_ClientID)) - NameColor = color_cast(ColorHSLA(m_pClient->m_Teams.Team(m_aLines[r].m_ClientID) / 64.0f, 1.0f, 0.75f)); + NameColor = m_pClient->GetDDTeamColor(m_pClient->m_Teams.Team(m_aLines[r].m_ClientID), 0.75f); else NameColor = ColorRGBA(0.8f, 0.8f, 0.8f, 1.f); diff --git a/src/game/client/components/killmessages.cpp b/src/game/client/components/killmessages.cpp index 212c7293c..7c4a69af3 100644 --- a/src/game/client/components/killmessages.cpp +++ b/src/game/client/components/killmessages.cpp @@ -278,21 +278,18 @@ void CKillMessages::OnRender() float x = StartX; - ColorRGBA TColor(1.f, 1.f, 1.f, 1.f); - ColorRGBA TOutlineColor(0.f, 0.f, 0.f, 0.3f); - // render victim name x -= m_aKillmsgs[r].m_VictimTextWidth; + ColorRGBA TextColor; if(m_aKillmsgs[r].m_VictimID >= 0 && g_Config.m_ClChatTeamColors && m_aKillmsgs[r].m_VictimDDTeam) - { - TColor = color_cast(ColorHSLA(m_aKillmsgs[r].m_VictimDDTeam / 64.0f, 1.0f, 0.75f)); - TColor.a = 1.f; - } + TextColor = m_pClient->GetDDTeamColor(m_aKillmsgs[r].m_VictimDDTeam, 0.75f); + else + TextColor = TextRender()->DefaultTextColor(); CreateKillmessageNamesIfNotCreated(m_aKillmsgs[r]); if(m_aKillmsgs[r].m_VictimTextContainerIndex.Valid()) - TextRender()->RenderTextContainer(m_aKillmsgs[r].m_VictimTextContainerIndex, TColor, TOutlineColor, x, y + (46.f - 36.f) / 2.f); + TextRender()->RenderTextContainer(m_aKillmsgs[r].m_VictimTextContainerIndex, TextColor, TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f); // render victim tee x -= 24.0f; @@ -380,7 +377,7 @@ void CKillMessages::OnRender() x -= m_aKillmsgs[r].m_KillerTextWidth; if(m_aKillmsgs[r].m_KillerTextContainerIndex.Valid()) - TextRender()->RenderTextContainer(m_aKillmsgs[r].m_KillerTextContainerIndex, TColor, TOutlineColor, x, y + (46.f - 36.f) / 2.f); + TextRender()->RenderTextContainer(m_aKillmsgs[r].m_KillerTextContainerIndex, TextColor, TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f); } y += 46.0f; diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index ca08c6946..521cce7f2 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -144,7 +144,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP float tw = m_aNamePlates[ClientID].m_NameTextWidth; if(g_Config.m_ClNameplatesTeamcolors && m_pClient->m_Teams.Team(ClientID)) - rgb = color_cast(ColorHSLA(m_pClient->m_Teams.Team(ClientID) / 64.0f, 1.0f, 0.75f)); + rgb = m_pClient->GetDDTeamColor(m_pClient->m_Teams.Team(ClientID), 0.75f); ColorRGBA TColor; ColorRGBA TOutlineColor; diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp index b84f07159..5643f57a4 100644 --- a/src/game/client/components/scoreboard.cpp +++ b/src/game/client/components/scoreboard.cpp @@ -355,7 +355,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch if(DDTeam != TEAM_FLOCK) { - ColorRGBA Color = color_cast(ColorHSLA(DDTeam / 64.0f, 1.0f, 0.5f, 0.5f)); + const ColorRGBA Color = m_pClient->GetDDTeamColor(DDTeam).WithAlpha(0.5f); int Corners = 0; if(OldDDTeam != DDTeam) Corners |= IGraphics::CORNER_TL | IGraphics::CORNER_TR; diff --git a/src/game/client/components/spectator.cpp b/src/game/client/components/spectator.cpp index 21f6e7914..0f5d28d40 100644 --- a/src/game/client/components/spectator.cpp +++ b/src/game/client/components/spectator.cpp @@ -401,7 +401,7 @@ void CSpectator::OnRender() if(DDTeam != TEAM_FLOCK) { - ColorRGBA Color = color_cast(ColorHSLA(DDTeam / 64.0f, 1.0f, 0.5f, 0.5f)); + const ColorRGBA Color = m_pClient->GetDDTeamColor(DDTeam).WithAlpha(0.5f); int Corners = 0; if(OldDDTeam != DDTeam) Corners |= IGraphics::CORNER_TL | IGraphics::CORNER_TR; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 72c1d8591..ae72bcdf9 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -765,6 +765,14 @@ bool CGameClient::Predict() const return !m_Snap.m_SpecInfo.m_Active && m_Snap.m_pLocalCharacter; } +ColorRGBA CGameClient::GetDDTeamColor(int DDTeam, float Lightness) const +{ + // Use golden angle to generate unique colors with distinct adjacent colors. + // The first DDTeam (team 1) gets angle 0°, i.e. red hue. + const float Hue = std::fmod((DDTeam - 1) * (137.50776f / 360.0f), 1.0f); + return color_cast(ColorHSLA(Hue, 1.0f, Lightness)); +} + void CGameClient::OnRelease() { // release all systems diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 8e4056959..d758e07a5 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -530,6 +530,7 @@ public: bool Predict() const; bool PredictDummy() { return g_Config.m_ClPredictDummy && Client()->DummyConnected() && m_Snap.m_LocalClientID >= 0 && m_PredictedDummyID >= 0 && !m_aClients[m_PredictedDummyID].m_Paused; } const CTuningParams *GetTuning(int i) { return &m_aTuningList[i]; } + ColorRGBA GetDDTeamColor(int DDTeam, float Lightness = 0.5f) const; CGameWorld m_GameWorld; CGameWorld m_PredictedWorld; From 0ea4d6540ac83c020f9bbaddb5e637c8e1fd1900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 26 Aug 2023 16:37:20 +0200 Subject: [PATCH 102/126] Overhaul demo player skipping UI and UX Make the skipping duration adjustable with a dropdown menu. The dropdown menu includes the durations 1s, 5s, 10s, 30s, 1m, 5m and 10m. The default duration is 5s. Skipping durations longer than the current demo are not shown. The dropdown menu is only shown if two or more durations would be shown. Add buttons for skipping the duration, which was previously only possible with the hotkeys. Add Ctrl+Left/Right hotkeys for skipping to chapters. Add Shift+Left/Right hotkeys for adjusting the skipping time. The Left/Right arrow keys and the J/L keys work identically for all hotkeys now. Ignore ctrl, shift and alt keys for demo speed changes with the mouse wheel, to better support actions like zooming being bound to alt+mousewheel etc. Also handle keypad enter key for play/pause like the normal return key. Use arrow up/down icon for speed adjustment buttons, so that the "backward/forward" icons can be used for duration skipping instead. Closes #7064. --- src/engine/textrender.h | 4 + src/game/client/components/menus_demo.cpp | 199 +++++++++++++++------- 2 files changed, 139 insertions(+), 64 deletions(-) diff --git a/src/engine/textrender.h b/src/engine/textrender.h index 419ecb698..5383f0975 100644 --- a/src/engine/textrender.h +++ b/src/engine/textrender.h @@ -98,6 +98,8 @@ MAYBE_UNUSED static const char *FONT_ICON_PAUSE = "\xEF\x81\x8C"; MAYBE_UNUSED static const char *FONT_ICON_STOP = "\xEF\x81\x8D"; MAYBE_UNUSED static const char *FONT_ICON_CHEVRON_LEFT = "\xEF\x81\x93"; MAYBE_UNUSED static const char *FONT_ICON_CHEVRON_RIGHT = "\xEF\x81\x94"; +MAYBE_UNUSED static const char *FONT_ICON_CHEVRON_UP = "\xEF\x81\xB7"; +MAYBE_UNUSED static const char *FONT_ICON_CHEVRON_DOWN = "\xEF\x81\xB8"; MAYBE_UNUSED static const char *FONT_ICON_BACKWARD = "\xEF\x81\x8A"; MAYBE_UNUSED static const char *FONT_ICON_FORWARD = "\xEF\x81\x8E"; MAYBE_UNUSED static const char *FONT_ICON_RIGHT_FROM_BRACKET = "\xEF\x8B\xB5"; @@ -105,6 +107,8 @@ MAYBE_UNUSED static const char *FONT_ICON_RIGHT_TO_BRACKET = "\xEF\x8B\xB6"; MAYBE_UNUSED static const char *FONT_ICON_ARROW_UP_RIGHT_FROM_SQUARE = "\xEF\x82\x8E"; MAYBE_UNUSED static const char *FONT_ICON_BACKWARD_STEP = "\xEF\x81\x88"; MAYBE_UNUSED static const char *FONT_ICON_FORWARD_STEP = "\xEF\x81\x91"; +MAYBE_UNUSED static const char *FONT_ICON_BACKWARD_FAST = "\xEF\x81\x89"; +MAYBE_UNUSED static const char *FONT_ICON_FORWARD_FAST = "\xEF\x81\x90"; MAYBE_UNUSED static const char *FONT_ICON_KEYBOARD = "\xE2\x8C\xA8"; MAYBE_UNUSED static const char *FONT_ICON_ELLIPSIS = "\xEF\x85\x81"; diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 87bb6caf4..eb6cb0f74 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -109,6 +109,8 @@ void CMenus::DemoSeekTick(IDemoPlayer::ETickOffset TickOffset) void CMenus::RenderDemoPlayer(CUIRect MainView) { const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); + const int CurrentTick = pInfo->m_CurrentTick - pInfo->m_FirstTick; + const int TotalTicks = pInfo->m_LastTick - pInfo->m_FirstTick; // When rendering a demo and starting paused, render the pause indicator permanently. #if defined(CONF_VIDEORECORDER) @@ -128,13 +130,49 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) m_LastSpeedChange = Client()->GlobalTime(); }; + // threshold value, accounts for slight inaccuracy when setting demo position + constexpr int Threshold = 10; + const auto &&FindPreviousMarkerPosition = [&]() { + for(int i = pInfo->m_NumTimelineMarkers - 1; i >= 0; i--) + { + if((pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) < CurrentTick && absolute(((pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) - CurrentTick)) > Threshold) + { + return (float)(pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) / TotalTicks; + } + } + return 0.0f; + }; + const auto &&FindNextMarkerPosition = [&]() { + for(int i = 0; i < pInfo->m_NumTimelineMarkers; i++) + { + if((pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) > CurrentTick && absolute(((pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) - CurrentTick)) > Threshold) + { + return (float)(pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) / TotalTicks; + } + } + return 1.0f; + }; + + static int s_SkipDurationIndex = 1; + static const int s_aSkipDurationsSeconds[] = {1, 5, 10, 30, 60, 5 * 60, 10 * 60}; + const int DemoLengthSeconds = TotalTicks / SERVER_TICK_SPEED; + int NumDurationLabels = 0; + for(size_t i = 0; i < std::size(s_aSkipDurationsSeconds); ++i) + { + if(s_aSkipDurationsSeconds[i] >= DemoLengthSeconds) + break; + NumDurationLabels = i + 1; + } + if(NumDurationLabels > 0 && s_SkipDurationIndex >= NumDurationLabels) + s_SkipDurationIndex = maximum(0, NumDurationLabels - 1); + // handle keyboard shortcuts independent of active menu float PositionToSeek = -1.0f; float TimeToSeek = 0.0f; - if(m_pClient->m_GameConsole.IsClosed() && m_DemoPlayerState == DEMOPLAYER_NONE && g_Config.m_ClDemoKeyboardShortcuts) + if(m_pClient->m_GameConsole.IsClosed() && m_DemoPlayerState == DEMOPLAYER_NONE && g_Config.m_ClDemoKeyboardShortcuts && !UI()->IsPopupOpen()) { // increase/decrease speed - if(!Input()->ShiftIsPressed()) + if(!Input()->ModifierIsPressed() && !Input()->ShiftIsPressed() && !Input()->AltIsPressed()) { if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP) || Input()->KeyPress(KEY_UP)) { @@ -149,7 +187,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) } // pause/unpause - if(Input()->KeyPress(KEY_SPACE) || Input()->KeyPress(KEY_RETURN) || Input()->KeyPress(KEY_K)) + if(Input()->KeyPress(KEY_SPACE) || Input()->KeyPress(KEY_RETURN) || Input()->KeyPress(KEY_KP_ENTER) || Input()->KeyPress(KEY_K)) { if(pInfo->m_Paused) { @@ -162,22 +200,24 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) UpdateLastPauseChange(); } - // seek backward/forward 10/5 seconds - if(Input()->KeyPress(KEY_J)) + // seek backward/forward configured time + if(Input()->KeyPress(KEY_LEFT) || Input()->KeyPress(KEY_J)) { - TimeToSeek = -10.0f; + if(Input()->ModifierIsPressed()) + PositionToSeek = FindPreviousMarkerPosition(); + else if(Input()->ShiftIsPressed()) + s_SkipDurationIndex = maximum(s_SkipDurationIndex - 1, 0); + else + TimeToSeek = -s_aSkipDurationsSeconds[s_SkipDurationIndex]; } - else if(Input()->KeyPress(KEY_L)) + else if(Input()->KeyPress(KEY_RIGHT) || Input()->KeyPress(KEY_L)) { - TimeToSeek = 10.0f; - } - else if(Input()->KeyPress(KEY_LEFT)) - { - TimeToSeek = -5.0f; - } - else if(Input()->KeyPress(KEY_RIGHT)) - { - TimeToSeek = 5.0f; + if(Input()->ModifierIsPressed()) + PositionToSeek = FindNextMarkerPosition(); + else if(Input()->ShiftIsPressed()) + s_SkipDurationIndex = minimum(s_SkipDurationIndex + 1, NumDurationLabels - 1); + else + TimeToSeek = s_aSkipDurationsSeconds[s_SkipDurationIndex]; } // seek to 0-90% @@ -257,9 +297,6 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) m_LastSpeedChange = 0.0f; } - const int CurrentTick = pInfo->m_CurrentTick - pInfo->m_FirstTick; - const int TotalTicks = pInfo->m_LastTick - pInfo->m_FirstTick; - if(CurrentTick == TotalTicks) { DemoPlayer()->Pause(); @@ -496,37 +533,110 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) } GameClient()->m_Tooltips.DoToolTip(&s_ResetButton, &Button, Localize("Stop the current demo")); + // skip time back + ButtonBar.VSplitLeft(Margins + 10.0f, nullptr, &ButtonBar); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static CButtonContainer s_TimeBackButton; + if(DoButton_FontIcon(&s_TimeBackButton, FONT_ICON_BACKWARD, 0, &Button, IGraphics::CORNER_ALL)) + { + TimeToSeek = -s_aSkipDurationsSeconds[s_SkipDurationIndex]; + } + GameClient()->m_Tooltips.DoToolTip(&s_TimeBackButton, &Button, Localize("Go back the specified duration")); + + // skip time dropdown + if(NumDurationLabels >= 2) + { + ButtonBar.VSplitLeft(Margins, nullptr, &ButtonBar); + ButtonBar.VSplitLeft(4 * ButtonbarHeight, &Button, &ButtonBar); + + static std::vector s_vDurationNames; + static std::vector s_vpDurationNames; + s_vDurationNames.resize(NumDurationLabels); + s_vpDurationNames.resize(NumDurationLabels); + + for(int i = 0; i < NumDurationLabels; ++i) + { + char aBuf[256]; + if(s_aSkipDurationsSeconds[i] >= 60) + str_format(aBuf, sizeof(aBuf), Localize("%d min.", "Demo player duration"), s_aSkipDurationsSeconds[i] / 60); + else + str_format(aBuf, sizeof(aBuf), Localize("%d sec.", "Demo player duration"), s_aSkipDurationsSeconds[i]); + s_vDurationNames[i] = aBuf; + s_vpDurationNames[i] = s_vDurationNames[i].c_str(); + } + + static CUI::SDropDownState s_SkipDurationDropDownState; + static CScrollRegion s_SkipDurationDropDownScrollRegion; + s_SkipDurationDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_SkipDurationDropDownScrollRegion; + s_SkipDurationIndex = UI()->DoDropDown(&Button, s_SkipDurationIndex, s_vpDurationNames.data(), NumDurationLabels, s_SkipDurationDropDownState); + GameClient()->m_Tooltips.DoToolTip(&s_SkipDurationDropDownState.m_ButtonContainer, &Button, Localize("Change the skip duration")); + } + + // skip time forward + ButtonBar.VSplitLeft(Margins, nullptr, &ButtonBar); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static CButtonContainer s_TimeForwardButton; + if(DoButton_FontIcon(&s_TimeForwardButton, FONT_ICON_FORWARD, 0, &Button, IGraphics::CORNER_ALL)) + { + TimeToSeek = s_aSkipDurationsSeconds[s_SkipDurationIndex]; + } + GameClient()->m_Tooltips.DoToolTip(&s_TimeForwardButton, &Button, Localize("Go forward the specified duration")); + // one tick back ButtonBar.VSplitLeft(Margins + 10.0f, nullptr, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); static CButtonContainer s_OneTickBackButton; - if(DoButton_FontIcon(&s_OneTickBackButton, FONT_ICON_CHEVRON_LEFT, 0, &Button, IGraphics::CORNER_ALL)) + if(DoButton_FontIcon(&s_OneTickBackButton, FONT_ICON_BACKWARD_STEP, 0, &Button, IGraphics::CORNER_ALL)) + { DemoSeekTick(IDemoPlayer::TICK_PREVIOUS); + } GameClient()->m_Tooltips.DoToolTip(&s_OneTickBackButton, &Button, Localize("Go back one tick")); // one tick forward ButtonBar.VSplitLeft(Margins, nullptr, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); static CButtonContainer s_OneTickForwardButton; - if(DoButton_FontIcon(&s_OneTickForwardButton, FONT_ICON_CHEVRON_RIGHT, 0, &Button, IGraphics::CORNER_ALL)) + if(DoButton_FontIcon(&s_OneTickForwardButton, FONT_ICON_FORWARD_STEP, 0, &Button, IGraphics::CORNER_ALL)) + { DemoSeekTick(IDemoPlayer::TICK_NEXT); + } GameClient()->m_Tooltips.DoToolTip(&s_OneTickForwardButton, &Button, Localize("Go forward one tick")); + // one marker back + ButtonBar.VSplitLeft(Margins + 10.0f, nullptr, &ButtonBar); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static CButtonContainer s_OneMarkerBackButton; + if(DoButton_FontIcon(&s_OneMarkerBackButton, FONT_ICON_BACKWARD_FAST, 0, &Button, IGraphics::CORNER_ALL)) + { + PositionToSeek = FindPreviousMarkerPosition(); + } + GameClient()->m_Tooltips.DoToolTip(&s_OneMarkerBackButton, &Button, Localize("Go back one marker")); + + // one marker forward + ButtonBar.VSplitLeft(Margins, nullptr, &ButtonBar); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static CButtonContainer s_OneMarkerForwardButton; + if(DoButton_FontIcon(&s_OneMarkerForwardButton, FONT_ICON_FORWARD_FAST, 0, &Button, IGraphics::CORNER_ALL)) + { + PositionToSeek = FindNextMarkerPosition(); + } + GameClient()->m_Tooltips.DoToolTip(&s_OneMarkerForwardButton, &Button, Localize("Go forward one marker")); + // slowdown ButtonBar.VSplitLeft(Margins + 10.0f, 0, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); static CButtonContainer s_SlowDownButton; - if(DoButton_FontIcon(&s_SlowDownButton, FONT_ICON_BACKWARD, 0, &Button, IGraphics::CORNER_ALL)) + if(DoButton_FontIcon(&s_SlowDownButton, FONT_ICON_CHEVRON_DOWN, 0, &Button, IGraphics::CORNER_ALL)) DecreaseDemoSpeed = true; GameClient()->m_Tooltips.DoToolTip(&s_SlowDownButton, &Button, Localize("Slow down the demo")); // fastforward ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); - static CButtonContainer s_FastForwardButton; - if(DoButton_FontIcon(&s_FastForwardButton, FONT_ICON_FORWARD, 0, &Button, IGraphics::CORNER_ALL)) + static CButtonContainer s_SpeedUpButton; + if(DoButton_FontIcon(&s_SpeedUpButton, FONT_ICON_CHEVRON_UP, 0, &Button, IGraphics::CORNER_ALL)) IncreaseDemoSpeed = true; - GameClient()->m_Tooltips.DoToolTip(&s_FastForwardButton, &Button, Localize("Speed up the demo")); + GameClient()->m_Tooltips.DoToolTip(&s_SpeedUpButton, &Button, Localize("Speed up the demo")); // speed meter ButtonBar.VSplitLeft(Margins * 12, &SpeedBar, &ButtonBar); @@ -582,45 +692,6 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) } GameClient()->m_Tooltips.DoToolTip(&s_SliceSaveButton, &Button, Localize("Export cut as a separate demo")); - // threshold value, accounts for slight inaccuracy when setting demo position - const int Threshold = 10; - - // one marker back - ButtonBar.VSplitLeft(Margins + 20.0f, nullptr, &ButtonBar); - ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); - static CButtonContainer s_OneMarkerBackButton; - if(DoButton_FontIcon(&s_OneMarkerBackButton, FONT_ICON_BACKWARD_STEP, 0, &Button, IGraphics::CORNER_ALL)) - { - PositionToSeek = 0.0f; - for(int i = pInfo->m_NumTimelineMarkers - 1; i >= 0; i--) - { - if((pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) < CurrentTick && absolute(((pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) - CurrentTick)) > Threshold) - { - PositionToSeek = (float)(pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) / TotalTicks; - break; - } - } - } - GameClient()->m_Tooltips.DoToolTip(&s_OneMarkerBackButton, &Button, Localize("Go back one marker")); - - // one marker forward - ButtonBar.VSplitLeft(Margins, nullptr, &ButtonBar); - ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); - static CButtonContainer s_OneMarkerForwardButton; - if(DoButton_FontIcon(&s_OneMarkerForwardButton, FONT_ICON_FORWARD_STEP, 0, &Button, IGraphics::CORNER_ALL)) - { - PositionToSeek = 1.0f; - for(int i = 0; i < pInfo->m_NumTimelineMarkers; i++) - { - if((pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) > CurrentTick && absolute(((pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) - CurrentTick)) > Threshold) - { - PositionToSeek = (float)(pInfo->m_aTimelineMarkers[i] - pInfo->m_FirstTick) / TotalTicks; - break; - } - } - } - GameClient()->m_Tooltips.DoToolTip(&s_OneMarkerForwardButton, &Button, Localize("Go forward one marker")); - // close button ButtonBar.VSplitRight(ButtonbarHeight, &ButtonBar, &Button); static CButtonContainer s_ExitButton; From 96af62c3fd1befcc0547281176afc7b640c6befb Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Fri, 15 Sep 2023 01:57:25 +0300 Subject: [PATCH 103/126] CMake: Change the net versioning Set DDNET_VERSION_NUMBER from DDNET_NETWORK_VERSION no matter what VERSION is. Provide get_network_version_number() which parses version to network version. --- CMakeLists.txt | 51 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6c2081d9..189834c59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,10 @@ if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.15) endif() # Set version if not explicitly set +set(DDNET_NETWORK_VERSION 17.2.1) + if(NOT VERSION) - set(VERSION 17.2.1) + set(VERSION ${DDNET_NETWORK_VERSION}) endif() project(DDNet @@ -142,17 +144,42 @@ if(IPO) endif() endif() -if("0${DDNet_VERSION_PATCH}" GREATER 9) - message(SEND_ERROR "Invalid DDNet patch version (the version is \"${DDNet_VERSION}\", the patch part must be in range 0-9 if set)") -endif() -if(${DDNet_VERSION_MINOR} GREATER 99) - message(SEND_ERROR "Invalid DDNet minor version (the version is \"${DDNet_VERSION}\", the minor part must be in range 0-99)") -endif() -if(${DDNet_VERSION_MINOR} GREATER 9) - set(DDNET_VERSION_NUMBER ${DDNet_VERSION_MAJOR}${DDNet_VERSION_MINOR}${DDNet_VERSION_PATCH}) -else() - set(DDNET_VERSION_NUMBER ${DDNet_VERSION_MAJOR}0${DDNet_VERSION_MINOR}${DDNet_VERSION_PATCH}) -endif() +function(get_network_version_number VERSION OUTPUT) + string(REPLACE "." ";" VERSION_LIST ${VERSION}) + + list(LENGTH VERSION_LIST VERSION_LIST_LENGTH) + if("${VERSION_LIST_LENGTH}" GREATER 2) + list(GET VERSION_LIST 2 NET_VERSION_PATCH) + else() + set(NET_VERSION_PATCH 0) + endif() + if("${VERSION_LIST_LENGTH}" GREATER 1) + list(GET VERSION_LIST 1 NET_VERSION_MINOR) + else() + set(NET_VERSION_MINOR 0) + endif() + if("${VERSION_LIST_LENGTH}" GREATER 0) + list(GET VERSION_LIST 0 NET_VERSION_MAJOR) + else() + message(SEND_ERROR "Invalid network version \"${VERSION}\"") + set(NET_VERSION_MAJOR 0) + endif() + + if("0${NET_VERSION_PATCH}" GREATER 9) + message(SEND_ERROR "Invalid net patch version (the version is \"${VERSION}\", the patch part must be in range 0-9 if set)") + endif() + if("0${NET_VERSION_MINOR}" GREATER 99) + message(SEND_ERROR "Invalid net minor version (the version is \"${VERSION}\", the minor part must be in range 0-99)") + endif() + if("0${NET_VERSION_MINOR}" GREATER 9) + set(NETWORK_VERSION ${NET_VERSION_MAJOR}${NET_VERSION_MINOR}${NET_VERSION_PATCH}) + else() + set(NETWORK_VERSION ${NET_VERSION_MAJOR}0${NET_VERSION_MINOR}${NET_VERSION_PATCH}) + endif() + set(${OUTPUT} ${NETWORK_VERSION} PARENT_SCOPE) +endfunction() + +get_network_version_number("${DDNET_NETWORK_VERSION}" DDNET_VERSION_NUMBER) configure_file(src/game/version.h.in ${PROJECT_BINARY_DIR}/src/game/version.h) From 321622b66b27de1bc589d651b619c1ddcad50e4c Mon Sep 17 00:00:00 2001 From: furo Date: Fri, 15 Sep 2023 11:15:22 +0200 Subject: [PATCH 104/126] Prevent editor shortcuts when a dialog is open --- src/game/editor/editor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index d8b4022c0..9203049d5 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -1483,7 +1483,7 @@ void CEditor::DoQuad(CQuad *pQuad, int Index) Graphics()->SetColor(1, 1, 1, 1); } - else if(Input()->KeyPress(KEY_R) && !m_vSelectedQuads.empty()) + else if(Input()->KeyPress(KEY_R) && !m_vSelectedQuads.empty() && m_Dialog == DIALOG_NONE) { UI()->EnableMouseLock(pID); UI()->SetActiveItem(pID); @@ -5699,7 +5699,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) ToolBar.VSplitLeft(40.0f, &Button, &ToolBar); UI()->DoLabel(&Button, "Sync.", 10.0f, TEXTALIGN_ML); - if(UI()->MouseInside(&View)) + if(UI()->MouseInside(&View) && m_Dialog == DIALOG_NONE) { UI()->SetHotItem(&s_EnvelopeEditorID); From 9eb4cf7508a28d07eea2a51ef65578855e8e62f7 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Wed, 6 Sep 2023 00:53:03 +0300 Subject: [PATCH 105/126] CCoreCharacter: Add and use HookedPlayer() --- src/game/client/prediction/entities/character.cpp | 2 +- src/game/client/prediction/gameworld.cpp | 4 ++-- src/game/gamecore.h | 3 ++- src/game/server/entities/character.cpp | 12 +++++++----- src/game/server/gameworld.cpp | 2 +- src/game/server/save.cpp | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/game/client/prediction/entities/character.cpp b/src/game/client/prediction/entities/character.cpp index 09edebeb8..931276d57 100644 --- a/src/game/client/prediction/entities/character.cpp +++ b/src/game/client/prediction/entities/character.cpp @@ -1171,7 +1171,7 @@ void CCharacter::ResetPrediction() SetWeaponGot(w, false); SetWeaponAmmo(w, -1); } - if(m_Core.m_HookedPlayer >= 0) + if(m_Core.HookedPlayer() >= 0) { m_Core.SetHookedPlayer(-1); m_Core.m_HookState = HOOK_IDLE; diff --git a/src/game/client/prediction/gameworld.cpp b/src/game/client/prediction/gameworld.cpp index 63d250c22..839ad8d02 100644 --- a/src/game/client/prediction/gameworld.cpp +++ b/src/game/client/prediction/gameworld.cpp @@ -315,7 +315,7 @@ void CGameWorld::ReleaseHooked(int ClientID) for(; pChr; pChr = (CCharacter *)pChr->TypeNext()) { CCharacterCore *pCore = pChr->Core(); - if(pCore->m_HookedPlayer == ClientID) + if(pCore->HookedPlayer() == ClientID) { pCore->SetHookedPlayer(-1); pCore->m_HookState = HOOK_RETRACTED; @@ -556,7 +556,7 @@ void CGameWorld::NetObjEnd() for(int i = 0; i < MAX_CLIENTS; i++) if(CCharacter *pChar = GetCharacterByID(i)) if(!pChar->m_MarkedForDestroy) - if(CCharacter *pHookedChar = GetCharacterByID(pChar->m_Core.m_HookedPlayer)) + if(CCharacter *pHookedChar = GetCharacterByID(pChar->m_Core.HookedPlayer())) if(pHookedChar->m_MarkedForDestroy) { pHookedChar->m_Pos = pHookedChar->m_Core.m_Pos = pChar->m_Core.m_HookPos; diff --git a/src/game/gamecore.h b/src/game/gamecore.h index 42c9ddbc2..be915c9c8 100644 --- a/src/game/gamecore.h +++ b/src/game/gamecore.h @@ -236,8 +236,8 @@ public: vec2 m_HookTeleBase; int m_HookTick; int m_HookState; - int m_HookedPlayer; std::set m_AttachedPlayers; + int HookedPlayer() const { return m_HookedPlayer; } void SetHookedPlayer(int HookedPlayer); int m_ActiveWeapon; @@ -318,6 +318,7 @@ public: private: CTeamsCore *m_pTeams; int m_MoveRestrictions; + int m_HookedPlayer; static bool IsSwitchActiveCb(int Number, void *pUser); }; diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index dafc7dcdb..2babaad32 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -768,7 +768,8 @@ void CCharacter::Tick() if(m_Core.m_TriggeredEvents & COREEVENT_HOOK_ATTACH_PLAYER) { - if(m_Core.m_HookedPlayer != -1 && GameServer()->m_apPlayers[m_Core.m_HookedPlayer]->GetTeam() != TEAM_SPECTATORS) + const int HookedPlayer = m_Core.HookedPlayer(); + if(HookedPlayer != -1 && GameServer()->m_apPlayers[HookedPlayer]->GetTeam() != TEAM_SPECTATORS) { Antibot()->OnHookAttach(m_pPlayer->GetCID(), true); } @@ -1138,7 +1139,7 @@ bool CCharacter::IsSnappingCharacterInView(int SnappingClientID) for(const auto &AttachedPlayerID : m_Core.m_AttachedPlayers) { CCharacter *pOtherPlayer = GameServer()->GetPlayerChar(AttachedPlayerID); - if(pOtherPlayer && pOtherPlayer->m_Core.m_HookedPlayer == ID) + if(pOtherPlayer && pOtherPlayer->m_Core.HookedPlayer() == ID) { if(!NetworkClippedLine(SnappingClientID, m_Pos, pOtherPlayer->m_Pos)) { @@ -1269,7 +1270,7 @@ void CCharacter::FillAntibot(CAntibotCharacterData *pData) pData->m_Pos = m_Pos; pData->m_Vel = m_Core.m_Vel; pData->m_Angle = m_Core.m_Angle; - pData->m_HookedPlayer = m_Core.m_HookedPlayer; + pData->m_HookedPlayer = m_Core.HookedPlayer(); pData->m_SpawnTick = m_SpawnTick; pData->m_WeaponChangeTick = m_WeaponChangeTick; pData->m_aLatestInputs[0].m_TargetX = m_LatestInput.m_TargetX; @@ -2229,7 +2230,7 @@ void CCharacter::Pause(bool Pause) GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = 0; GameServer()->m_World.RemoveEntity(this); - if(m_Core.m_HookedPlayer != -1) // Keeping hook would allow cheats + if(m_Core.HookedPlayer() != -1) // Keeping hook would allow cheats { ResetHook(); GameWorld()->ReleaseHooked(GetPlayer()->GetCID()); @@ -2332,5 +2333,6 @@ CClientMask CCharacter::TeamMask() void CCharacter::SwapClients(int Client1, int Client2) { - m_Core.SetHookedPlayer(m_Core.m_HookedPlayer == Client1 ? Client2 : m_Core.m_HookedPlayer == Client2 ? Client1 : m_Core.m_HookedPlayer); + const int HookedPlayer = m_Core.HookedPlayer(); + m_Core.SetHookedPlayer(HookedPlayer == Client1 ? Client2 : HookedPlayer == Client2 ? Client1 : HookedPlayer); } diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index 7ea14b342..ee931602d 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -428,7 +428,7 @@ void CGameWorld::ReleaseHooked(int ClientID) for(; pChr; pChr = (CCharacter *)pChr->TypeNext()) { CCharacterCore *pCore = pChr->Core(); - if(pCore->m_HookedPlayer == ClientID && !pChr->IsSuper()) + if(pCore->HookedPlayer() == ClientID && !pChr->IsSuper()) { pCore->SetHookedPlayer(-1); pCore->m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; diff --git a/src/game/server/save.cpp b/src/game/server/save.cpp index abdce8acf..fb764e77c 100644 --- a/src/game/server/save.cpp +++ b/src/game/server/save.cpp @@ -100,7 +100,7 @@ void CSaveTee::Save(CCharacter *pChr) m_HookTick = pChr->m_Core.m_HookTick; m_HookState = pChr->m_Core.m_HookState; - m_HookedPlayer = pChr->m_Core.m_HookedPlayer; + m_HookedPlayer = pChr->m_Core.HookedPlayer(); m_NewHook = pChr->m_Core.m_NewHook != 0; m_InputDirection = pChr->m_SavedInput.m_Direction; From 4c55e83d91c59849496bd40b205a8c8c8969e832 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Wed, 6 Sep 2023 01:07:40 +0300 Subject: [PATCH 106/126] Character (cli and srv): Access WorldCore via GameWorld() --- src/game/client/prediction/entities/character.cpp | 2 +- src/game/server/entities/character.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/game/client/prediction/entities/character.cpp b/src/game/client/prediction/entities/character.cpp index 931276d57..552fd84fc 100644 --- a/src/game/client/prediction/entities/character.cpp +++ b/src/game/client/prediction/entities/character.cpp @@ -939,7 +939,7 @@ void CCharacter::HandleTuneLayer() SetTuneZone(GameWorld()->m_WorldConfig.m_UseTuneZones ? Collision()->IsTune(CurrentIndex) : 0); if(m_IsLocal) - m_Core.m_pWorld->m_aTuning[g_Config.m_ClDummy] = *GetTuning(m_TuneZone); // throw tunings (from specific zone if in a tunezone) into gamecore if the character is local + GameWorld()->m_Core.m_aTuning[g_Config.m_ClDummy] = *GetTuning(m_TuneZone); // throw tunings (from specific zone if in a tunezone) into gamecore if the character is local m_Core.m_Tuning = *GetTuning(m_TuneZone); } diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 2babaad32..606a23fe6 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1803,7 +1803,7 @@ void CCharacter::HandleTiles(int Index) { if(m_Core.m_Super) return; - int TeleOut = m_Core.m_pWorld->RandomOr0((*m_pTeleOuts)[z - 1].size()); + int TeleOut = GameWorld()->m_Core.RandomOr0((*m_pTeleOuts)[z - 1].size()); m_Core.m_Pos = (*m_pTeleOuts)[z - 1][TeleOut]; if(!g_Config.m_SvTeleportHoldHook) { @@ -1818,7 +1818,7 @@ void CCharacter::HandleTiles(int Index) { if(m_Core.m_Super) return; - int TeleOut = m_Core.m_pWorld->RandomOr0((*m_pTeleOuts)[evilz - 1].size()); + int TeleOut = GameWorld()->m_Core.RandomOr0((*m_pTeleOuts)[evilz - 1].size()); m_Core.m_Pos = (*m_pTeleOuts)[evilz - 1][TeleOut]; if(!g_Config.m_SvOldTeleportHook && !g_Config.m_SvOldTeleportWeapons) { @@ -1845,7 +1845,7 @@ void CCharacter::HandleTiles(int Index) { if(!(*m_pTeleCheckOuts)[k].empty()) { - int TeleOut = m_Core.m_pWorld->RandomOr0((*m_pTeleCheckOuts)[k].size()); + int TeleOut = GameWorld()->m_Core.RandomOr0((*m_pTeleCheckOuts)[k].size()); m_Core.m_Pos = (*m_pTeleCheckOuts)[k][TeleOut]; m_Core.m_Vel = vec2(0, 0); @@ -1882,7 +1882,7 @@ void CCharacter::HandleTiles(int Index) { if(!(*m_pTeleCheckOuts)[k].empty()) { - int TeleOut = m_Core.m_pWorld->RandomOr0((*m_pTeleCheckOuts)[k].size()); + int TeleOut = GameWorld()->m_Core.RandomOr0((*m_pTeleCheckOuts)[k].size()); m_Core.m_Pos = (*m_pTeleCheckOuts)[k][TeleOut]; if(!g_Config.m_SvTeleportHoldHook) From 7dab9e430d6dbd982bcad9b2488b65da0be7fea8 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Wed, 6 Sep 2023 01:09:10 +0300 Subject: [PATCH 107/126] CCharacterCore: Break the friendship with CCharacter --- src/game/client/prediction/entities/character.cpp | 6 ++---- src/game/gamecore.cpp | 7 +++++++ src/game/gamecore.h | 2 +- src/game/server/entities/character.cpp | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/game/client/prediction/entities/character.cpp b/src/game/client/prediction/entities/character.cpp index 552fd84fc..701e5b387 100644 --- a/src/game/client/prediction/entities/character.cpp +++ b/src/game/client/prediction/entities/character.cpp @@ -1107,7 +1107,7 @@ void CCharacter::GiveAllWeapons() CTeamsCore *CCharacter::TeamsCore() { - return m_Core.m_pTeams; + return GameWorld()->Teams(); } CCharacter::CCharacter(CGameWorld *pGameWorld, int ID, CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtended) : @@ -1355,9 +1355,7 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende void CCharacter::SetCoreWorld(CGameWorld *pGameWorld) { - m_Core.m_pWorld = &pGameWorld->m_Core; - m_Core.m_pCollision = pGameWorld->Collision(); - m_Core.m_pTeams = pGameWorld->Teams(); + m_Core.SetCoreWorld(&pGameWorld->m_Core, pGameWorld->Collision(), pGameWorld->Teams()); } bool CCharacter::Match(CCharacter *pChar) diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp index 0d40db737..bf7e80c69 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -97,6 +97,13 @@ void CCharacterCore::Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore Reset(); } +void CCharacterCore::SetCoreWorld(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams) +{ + m_pWorld = pWorld; + m_pCollision = pCollision; + m_pTeams = pTeams; +} + void CCharacterCore::Reset() { m_Pos = vec2(0, 0); diff --git a/src/game/gamecore.h b/src/game/gamecore.h index be915c9c8..2b5ed4947 100644 --- a/src/game/gamecore.h +++ b/src/game/gamecore.h @@ -220,7 +220,6 @@ public: class CCharacterCore { - friend class CCharacter; CWorldCore *m_pWorld = nullptr; CCollision *m_pCollision; std::map> *m_pTeleOuts; @@ -272,6 +271,7 @@ public: int m_TriggeredEvents; void Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams = nullptr, std::map> *pTeleOuts = nullptr); + void SetCoreWorld(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams); void Reset(); void TickDeferred(); void Tick(bool UseInput, bool DoDeferredTick = true); diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 606a23fe6..04e0a4206 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1262,7 +1262,7 @@ void CCharacter::SetTeleports(std::map> *pTeleOuts, std:: { m_pTeleOuts = pTeleOuts; m_pTeleCheckOuts = pTeleCheckOuts; - m_Core.m_pTeleOuts = pTeleOuts; + m_Core.SetTeleOuts(pTeleOuts); } void CCharacter::FillAntibot(CAntibotCharacterData *pData) From a1b7e0c37c7eb4e342c980671d3469411e7933e2 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Wed, 6 Sep 2023 01:22:44 +0300 Subject: [PATCH 108/126] Move alloc.h to game/ and use it in client/prediction/entity.h --- CMakeLists.txt | 2 +- src/game/{server => }/alloc.h | 4 ++-- src/game/client/prediction/entity.h | 18 +++--------------- src/game/server/entity.h | 3 ++- src/game/server/player.h | 3 ++- 5 files changed, 10 insertions(+), 20 deletions(-) rename src/game/{server => }/alloc.h (97%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6c2081d9..3cb293185 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1968,6 +1968,7 @@ set_src(ENGINE_GFX GLOB src/engine/gfx image_manipulation.h ) set_src(GAME_SHARED GLOB src/game + alloc.h collision.cpp collision.h ddracechat.h @@ -2470,7 +2471,6 @@ if(SERVER) upnp.h ) set_src(GAME_SERVER GLOB_RECURSE src/game/server - alloc.h ddracechat.cpp ddracecommands.cpp entities/character.cpp diff --git a/src/game/server/alloc.h b/src/game/alloc.h similarity index 97% rename from src/game/server/alloc.h rename to src/game/alloc.h index 6ec1301c1..b4a76a9bd 100644 --- a/src/game/server/alloc.h +++ b/src/game/alloc.h @@ -1,7 +1,7 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ -#ifndef GAME_SERVER_ALLOC_H -#define GAME_SERVER_ALLOC_H +#ifndef GAME_ALLOC_H +#define GAME_ALLOC_H #include diff --git a/src/game/client/prediction/entity.h b/src/game/client/prediction/entity.h index 1083c6d33..2c7b75dff 100644 --- a/src/game/client/prediction/entity.h +++ b/src/game/client/prediction/entity.h @@ -3,23 +3,11 @@ #ifndef GAME_CLIENT_PREDICTION_ENTITY_H #define GAME_CLIENT_PREDICTION_ENTITY_H -#include "gameworld.h" #include -#define MACRO_ALLOC_HEAP() \ -public: \ - void *operator new(size_t Size) \ - { \ - void *p = malloc(Size); \ - mem_zero(p, Size); \ - return p; \ - } \ - void operator delete(void *pPtr) \ - { \ - free(pPtr); \ - } \ -\ -private: +#include + +#include "gameworld.h" class CEntity { diff --git a/src/game/server/entity.h b/src/game/server/entity.h index b7948b41a..311d3f0f9 100644 --- a/src/game/server/entity.h +++ b/src/game/server/entity.h @@ -5,7 +5,8 @@ #include -#include "alloc.h" +#include + #include "gameworld.h" class CCollision; diff --git a/src/game/server/player.h b/src/game/server/player.h index 7246079c5..799a04407 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -7,7 +7,8 @@ #include -#include "alloc.h" +#include + #include "teeinfo.h" #include From a244e2ced963acde8d79e240f9c6edd5026c3972 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Wed, 6 Sep 2023 01:23:26 +0300 Subject: [PATCH 109/126] prediction/gameworld.h: Drop unneeded friendship with CCharacter --- src/game/client/prediction/gameworld.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/game/client/prediction/gameworld.h b/src/game/client/prediction/gameworld.h index 696956f8c..32d00dd78 100644 --- a/src/game/client/prediction/gameworld.h +++ b/src/game/client/prediction/gameworld.h @@ -15,8 +15,6 @@ class CEntity; class CGameWorld { - friend CCharacter; - public: enum { From 77fc14f251e92f28e85563c094a042c10d4aa863 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Wed, 6 Sep 2023 01:44:21 +0300 Subject: [PATCH 110/126] Server: Move player (ID) mapping update to GameContext This way the GameWorld does not deal with CPlayer. --- src/game/server/gamecontext.cpp | 63 ++++++++++++++++++++++++++++++++ src/game/server/gamecontext.h | 2 + src/game/server/gameworld.cpp | 65 --------------------------------- src/game/server/gameworld.h | 2 - 4 files changed, 65 insertions(+), 67 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 952ec9ce2..14e0ecde0 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -947,6 +947,8 @@ void CGameContext::OnTick() m_World.m_Core.m_aTuning[0] = m_Tuning; m_World.Tick(); + UpdatePlayerMaps(); + //if(world.paused) // make sure that the game object always updates m_pController->Tick(); @@ -4010,6 +4012,67 @@ void CGameContext::OnPostSnap() m_Events.Clear(); } +void CGameContext::UpdatePlayerMaps() +{ + const auto DistCompare = [](std::pair a, std::pair b) -> bool { + return (a.first < b.first); + }; + + if(Server()->Tick() % g_Config.m_SvMapUpdateRate != 0) + return; + + std::pair Dist[MAX_CLIENTS]; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(!Server()->ClientIngame(i)) + continue; + if(Server()->GetClientVersion(i) >= VERSION_DDNET_OLD) + continue; + int *pMap = Server()->GetIdMap(i); + + // compute distances + for(int j = 0; j < MAX_CLIENTS; j++) + { + Dist[j].second = j; + if(j == i) + continue; + if(!Server()->ClientIngame(j) || !m_apPlayers[j]) + { + Dist[j].first = 1e10; + continue; + } + CCharacter *pChr = m_apPlayers[j]->GetCharacter(); + if(!pChr) + { + Dist[j].first = 1e9; + continue; + } + if(!pChr->CanSnapCharacter(i)) + Dist[j].first = 1e8; + else + Dist[j].first = length_squared(m_apPlayers[i]->m_ViewPos - pChr->GetPos()); + } + + // always send the player themselves, even if all in same position + Dist[i].first = -1; + + std::nth_element(&Dist[0], &Dist[VANILLA_MAX_CLIENTS - 1], &Dist[MAX_CLIENTS], DistCompare); + + int Index = 1; // exclude self client id + for(int j = 0; j < VANILLA_MAX_CLIENTS - 1; j++) + { + pMap[j + 1] = -1; // also fill player with empty name to say chat msgs + if(Dist[j].second == i || Dist[j].first > 5e9f) + continue; + pMap[Index++] = Dist[j].second; + } + + // sort by real client ids, guarantee order on distance changes, O(Nlog(N)) worst case + // sort just clients in game always except first (self client id) and last (fake client id) indexes + std::sort(&pMap[1], &pMap[minimum(Index, VANILLA_MAX_CLIENTS - 1)]); + } +} + bool CGameContext::IsClientReady(int ClientID) const { return m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_IsReady; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 242bdf66e..5ba0d394a 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -285,6 +285,8 @@ public: void OnSnap(int ClientID) override; void OnPostSnap() override; + void UpdatePlayerMaps(); + void *PreProcessMsg(int *pMsgID, CUnpacker *pUnpacker, int ClientID); void CensorMessage(char *pCensoredMessage, const char *pMessage, int Size); void OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) override; diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index ee931602d..e67e55550 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -6,7 +6,6 @@ #include "entity.h" #include "gamecontext.h" #include "gamecontroller.h" -#include "player.h" #include @@ -192,68 +191,6 @@ void CGameWorld::RemoveEntities() } } -bool distCompare(std::pair a, std::pair b) -{ - return (a.first < b.first); -} - -void CGameWorld::UpdatePlayerMaps() -{ - if(Server()->Tick() % g_Config.m_SvMapUpdateRate != 0) - return; - - std::pair Dist[MAX_CLIENTS]; - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(!Server()->ClientIngame(i)) - continue; - if(Server()->GetClientVersion(i) >= VERSION_DDNET_OLD) - continue; - int *pMap = Server()->GetIdMap(i); - - // compute distances - for(int j = 0; j < MAX_CLIENTS; j++) - { - Dist[j].second = j; - if(j == i) - continue; - if(!Server()->ClientIngame(j) || !GameServer()->m_apPlayers[j]) - { - Dist[j].first = 1e10; - continue; - } - CCharacter *pChr = GameServer()->m_apPlayers[j]->GetCharacter(); - if(!pChr) - { - Dist[j].first = 1e9; - continue; - } - if(!pChr->CanSnapCharacter(i)) - Dist[j].first = 1e8; - else - Dist[j].first = length_squared(GameServer()->m_apPlayers[i]->m_ViewPos - pChr->m_Pos); - } - - // always send the player themselves, even if all in same position - Dist[i].first = -1; - - std::nth_element(&Dist[0], &Dist[VANILLA_MAX_CLIENTS - 1], &Dist[MAX_CLIENTS], distCompare); - - int Index = 1; // exclude self client id - for(int j = 0; j < VANILLA_MAX_CLIENTS - 1; j++) - { - pMap[j + 1] = -1; // also fill player with empty name to say chat msgs - if(Dist[j].second == i || Dist[j].first > 5e9f) - continue; - pMap[Index++] = Dist[j].second; - } - - // sort by real client ids, guarantee order on distance changes, O(Nlog(N)) worst case - // sort just clients in game always except first (self client id) and last (fake client id) indexes - std::sort(&pMap[1], &pMap[minimum(Index, VANILLA_MAX_CLIENTS - 1)]); - } -} - void CGameWorld::Tick() { if(m_ResetRequested) @@ -311,8 +248,6 @@ void CGameWorld::Tick() RemoveEntities(); - UpdatePlayerMaps(); - // find the characters' strong/weak id int StrongWeakID = 0; for(CCharacter *pChar = (CCharacter *)FindFirst(ENTTYPE_CHARACTER); pChar; pChar = (CCharacter *)pChar->TypeNext()) diff --git a/src/game/server/gameworld.h b/src/game/server/gameworld.h index 120a550a1..05a222594 100644 --- a/src/game/server/gameworld.h +++ b/src/game/server/gameworld.h @@ -39,8 +39,6 @@ private: class CConfig *m_pConfig; class IServer *m_pServer; - void UpdatePlayerMaps(); - public: class CGameContext *GameServer() { return m_pGameServer; } class CConfig *Config() { return m_pConfig; } From f58eef45b9e02a28229179cef44bdd5c5eaec4cd Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Wed, 6 Sep 2023 02:08:01 +0300 Subject: [PATCH 111/126] Server: Use the tuning params via GameWorld (like in prediction) The world tuning is a part of the world. This way the entities implementation use the same API as available on the client side. This change is a step toward unified/shared world logic for client and server. --- src/game/client/prediction/gameworld.cpp | 5 --- src/game/server/entities/character.cpp | 44 ++++++++++++------------ src/game/server/entities/laser.cpp | 14 ++++---- src/game/server/entities/projectile.cpp | 24 ++++++------- src/game/server/entity.h | 3 ++ src/game/server/gamecontext.cpp | 1 + src/game/server/gameworld.cpp | 5 +++ src/game/server/gameworld.h | 6 ++++ 8 files changed, 56 insertions(+), 46 deletions(-) diff --git a/src/game/client/prediction/gameworld.cpp b/src/game/client/prediction/gameworld.cpp index 839ad8d02..05da81d55 100644 --- a/src/game/client/prediction/gameworld.cpp +++ b/src/game/client/prediction/gameworld.cpp @@ -182,11 +182,6 @@ void CGameWorld::RemoveEntities() } } -bool distCompare(std::pair a, std::pair b) -{ - return (a.first < b.first); -} - void CGameWorld::Tick() { // update all objects diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 04e0a4206..4960d3d16 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -206,9 +206,9 @@ void CCharacter::HandleJetpack() { float Strength; if(!m_TuneZone) - Strength = GameServer()->Tuning()->m_JetpackStrength; + Strength = Tuning()->m_JetpackStrength; else - Strength = GameServer()->TuningList()[m_TuneZone].m_JetpackStrength; + Strength = TuningList()[m_TuneZone].m_JetpackStrength; TakeDamage(Direction * -1.0f * (Strength / 100.0f / 6.11f), 0, m_pPlayer->GetCID(), m_Core.m_ActiveWeapon); } } @@ -255,9 +255,9 @@ void CCharacter::HandleNinja() vec2 GroundElasticity; if(!m_TuneZone) - GroundElasticity = vec2(GameServer()->Tuning()->m_GroundElasticityX, GameServer()->Tuning()->m_GroundElasticityY); + GroundElasticity = vec2(Tuning()->m_GroundElasticityX, Tuning()->m_GroundElasticityY); else - GroundElasticity = vec2(GameServer()->TuningList()[m_TuneZone].m_GroundElasticityX, GameServer()->TuningList()[m_TuneZone].m_GroundElasticityY); + GroundElasticity = vec2(TuningList()[m_TuneZone].m_GroundElasticityX, TuningList()[m_TuneZone].m_GroundElasticityY); Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, vec2(GetProximityRadius(), GetProximityRadius()), GroundElasticity); @@ -470,9 +470,9 @@ void CCharacter::FireWeapon() float Strength; if(!m_TuneZone) - Strength = GameServer()->Tuning()->m_HammerStrength; + Strength = Tuning()->m_HammerStrength; else - Strength = GameServer()->TuningList()[m_TuneZone].m_HammerStrength; + Strength = TuningList()[m_TuneZone].m_HammerStrength; vec2 Temp = pTarget->m_Core.m_Vel + normalize(Dir + vec2(0.f, -1.1f)) * 10.0f; Temp = ClampVel(pTarget->m_MoveRestrictions, Temp); @@ -494,9 +494,9 @@ void CCharacter::FireWeapon() { float FireDelay; if(!m_TuneZone) - FireDelay = GameServer()->Tuning()->m_HammerHitFireDelay; + FireDelay = Tuning()->m_HammerHitFireDelay; else - FireDelay = GameServer()->TuningList()[m_TuneZone].m_HammerHitFireDelay; + FireDelay = TuningList()[m_TuneZone].m_HammerHitFireDelay; m_ReloadTimer = FireDelay * Server()->TickSpeed() / 1000; } } @@ -508,9 +508,9 @@ void CCharacter::FireWeapon() { int Lifetime; if(!m_TuneZone) - Lifetime = (int)(Server()->TickSpeed() * GameServer()->Tuning()->m_GunLifetime); + Lifetime = (int)(Server()->TickSpeed() * Tuning()->m_GunLifetime); else - Lifetime = (int)(Server()->TickSpeed() * GameServer()->TuningList()[m_TuneZone].m_GunLifetime); + Lifetime = (int)(Server()->TickSpeed() * TuningList()[m_TuneZone].m_GunLifetime); new CProjectile( GameWorld(), @@ -534,9 +534,9 @@ void CCharacter::FireWeapon() { float LaserReach; if(!m_TuneZone) - LaserReach = GameServer()->Tuning()->m_LaserReach; + LaserReach = Tuning()->m_LaserReach; else - LaserReach = GameServer()->TuningList()[m_TuneZone].m_LaserReach; + LaserReach = TuningList()[m_TuneZone].m_LaserReach; new CLaser(&GameServer()->m_World, m_Pos, Direction, LaserReach, m_pPlayer->GetCID(), WEAPON_SHOTGUN); GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE, TeamMask()); @@ -547,9 +547,9 @@ void CCharacter::FireWeapon() { int Lifetime; if(!m_TuneZone) - Lifetime = (int)(Server()->TickSpeed() * GameServer()->Tuning()->m_GrenadeLifetime); + Lifetime = (int)(Server()->TickSpeed() * Tuning()->m_GrenadeLifetime); else - Lifetime = (int)(Server()->TickSpeed() * GameServer()->TuningList()[m_TuneZone].m_GrenadeLifetime); + Lifetime = (int)(Server()->TickSpeed() * TuningList()[m_TuneZone].m_GrenadeLifetime); new CProjectile( GameWorld(), @@ -572,9 +572,9 @@ void CCharacter::FireWeapon() { float LaserReach; if(!m_TuneZone) - LaserReach = GameServer()->Tuning()->m_LaserReach; + LaserReach = Tuning()->m_LaserReach; else - LaserReach = GameServer()->TuningList()[m_TuneZone].m_LaserReach; + LaserReach = TuningList()[m_TuneZone].m_LaserReach; new CLaser(GameWorld(), m_Pos, Direction, LaserReach, m_pPlayer->GetCID(), WEAPON_LASER); GameServer()->CreateSound(m_Pos, SOUND_LASER_FIRE, TeamMask()); @@ -601,9 +601,9 @@ void CCharacter::FireWeapon() { float FireDelay; if(!m_TuneZone) - GameServer()->Tuning()->Get(38 + m_Core.m_ActiveWeapon, &FireDelay); + Tuning()->Get(38 + m_Core.m_ActiveWeapon, &FireDelay); else - GameServer()->TuningList()[m_TuneZone].Get(38 + m_Core.m_ActiveWeapon, &FireDelay); + TuningList()[m_TuneZone].Get(38 + m_Core.m_ActiveWeapon, &FireDelay); m_ReloadTimer = FireDelay * Server()->TickSpeed() / 1000; } } @@ -1184,9 +1184,9 @@ void CCharacter::Snap(int SnappingClient) pDDNetCharacter->m_Flags |= CHARACTERFLAG_SUPER; if(m_Core.m_EndlessHook) pDDNetCharacter->m_Flags |= CHARACTERFLAG_ENDLESS_HOOK; - if(m_Core.m_CollisionDisabled || !GameServer()->Tuning()->m_PlayerCollision) + if(m_Core.m_CollisionDisabled || !Tuning()->m_PlayerCollision) pDDNetCharacter->m_Flags |= CHARACTERFLAG_COLLISION_DISABLED; - if(m_Core.m_HookHitDisabled || !GameServer()->Tuning()->m_PlayerHooking) + if(m_Core.m_HookHitDisabled || !Tuning()->m_PlayerHooking) pDDNetCharacter->m_Flags |= CHARACTERFLAG_HOOK_HIT_DISABLED; if(m_Core.m_EndlessJump) pDDNetCharacter->m_Flags |= CHARACTERFLAG_ENDLESS_JUMP; @@ -1915,9 +1915,9 @@ void CCharacter::HandleTuneLayer() m_TuneZone = Collision()->IsTune(CurrentIndex); if(m_TuneZone) - m_Core.m_Tuning = GameServer()->TuningList()[m_TuneZone]; // throw tunings from specific zone into gamecore + m_Core.m_Tuning = TuningList()[m_TuneZone]; // throw tunings from specific zone into gamecore else - m_Core.m_Tuning = *GameServer()->Tuning(); + m_Core.m_Tuning = *Tuning(); if(m_TuneZone != m_TuneZoneOld) // don't send tunigs all the time { diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index a819a2de3..913043d5c 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -58,9 +58,9 @@ bool CLaser::HitCharacter(vec2 From, vec2 To) float Strength; if(!m_TuneZone) - Strength = GameServer()->Tuning()->m_ShotgunStrength; + Strength = Tuning()->m_ShotgunStrength; else - Strength = GameServer()->TuningList()[m_TuneZone].m_ShotgunStrength; + Strength = TuningList()[m_TuneZone].m_ShotgunStrength; vec2 &HitPos = pHit->Core()->m_Pos; if(!g_Config.m_SvOldLaser) @@ -158,7 +158,7 @@ void CLaser::DoBounce() } else if(!m_TuneZone) { - m_Energy -= Distance + GameServer()->Tuning()->m_LaserBounceCost; + m_Energy -= Distance + Tuning()->m_LaserBounceCost; } else { @@ -179,9 +179,9 @@ void CLaser::DoBounce() m_WasTele = false; } - int BounceNum = GameServer()->Tuning()->m_LaserBounceNum; + int BounceNum = Tuning()->m_LaserBounceNum; if(m_TuneZone) - BounceNum = GameServer()->TuningList()[m_TuneZone].m_LaserBounceNum; + BounceNum = TuningList()[m_TuneZone].m_LaserBounceNum; if(m_Bounces > BounceNum) m_Energy = -1; @@ -279,9 +279,9 @@ void CLaser::Tick() float Delay; if(m_TuneZone) - Delay = GameServer()->TuningList()[m_TuneZone].m_LaserBounceDelay; + Delay = TuningList()[m_TuneZone].m_LaserBounceDelay; else - Delay = GameServer()->Tuning()->m_LaserBounceDelay; + Delay = Tuning()->m_LaserBounceDelay; if((Server()->Tick() - m_EvalTick) > (Server()->TickSpeed() * Delay / 1000.0f)) DoBounce(); diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp index 8780b02c3..388859148 100644 --- a/src/game/server/entities/projectile.cpp +++ b/src/game/server/entities/projectile.cpp @@ -64,13 +64,13 @@ vec2 CProjectile::GetPos(float Time) case WEAPON_GRENADE: if(!m_TuneZone) { - Curvature = GameServer()->Tuning()->m_GrenadeCurvature; - Speed = GameServer()->Tuning()->m_GrenadeSpeed; + Curvature = Tuning()->m_GrenadeCurvature; + Speed = Tuning()->m_GrenadeSpeed; } else { - Curvature = GameServer()->TuningList()[m_TuneZone].m_GrenadeCurvature; - Speed = GameServer()->TuningList()[m_TuneZone].m_GrenadeSpeed; + Curvature = TuningList()[m_TuneZone].m_GrenadeCurvature; + Speed = TuningList()[m_TuneZone].m_GrenadeSpeed; } break; @@ -78,13 +78,13 @@ vec2 CProjectile::GetPos(float Time) case WEAPON_SHOTGUN: if(!m_TuneZone) { - Curvature = GameServer()->Tuning()->m_ShotgunCurvature; - Speed = GameServer()->Tuning()->m_ShotgunSpeed; + Curvature = Tuning()->m_ShotgunCurvature; + Speed = Tuning()->m_ShotgunSpeed; } else { - Curvature = GameServer()->TuningList()[m_TuneZone].m_ShotgunCurvature; - Speed = GameServer()->TuningList()[m_TuneZone].m_ShotgunSpeed; + Curvature = TuningList()[m_TuneZone].m_ShotgunCurvature; + Speed = TuningList()[m_TuneZone].m_ShotgunSpeed; } break; @@ -92,13 +92,13 @@ vec2 CProjectile::GetPos(float Time) case WEAPON_GUN: if(!m_TuneZone) { - Curvature = GameServer()->Tuning()->m_GunCurvature; - Speed = GameServer()->Tuning()->m_GunSpeed; + Curvature = Tuning()->m_GunCurvature; + Speed = Tuning()->m_GunSpeed; } else { - Curvature = GameServer()->TuningList()[m_TuneZone].m_GunCurvature; - Speed = GameServer()->TuningList()[m_TuneZone].m_GunSpeed; + Curvature = TuningList()[m_TuneZone].m_GunCurvature; + Speed = TuningList()[m_TuneZone].m_GunSpeed; } break; } diff --git a/src/game/server/entity.h b/src/game/server/entity.h index 311d3f0f9..69e9a4385 100644 --- a/src/game/server/entity.h +++ b/src/game/server/entity.h @@ -61,6 +61,9 @@ public: // TODO: Maybe make protected /* Objects */ std::vector &Switchers() { return m_pGameWorld->m_Core.m_vSwitchers; } CGameWorld *GameWorld() { return m_pGameWorld; } + CTuningParams *Tuning() { return GameWorld()->Tuning(); } + CTuningParams *TuningList() { return GameWorld()->TuningList(); } + CTuningParams *GetTuning(int i) { return GameWorld()->GetTuning(i); } class CConfig *Config() { return m_pGameWorld->Config(); } class CGameContext *GameServer() { return m_pGameWorld->GameServer(); } class IServer *Server() { return m_pGameWorld->Server(); } diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 14e0ecde0..84e66ef8c 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -3480,6 +3480,7 @@ void CGameContext::OnInit(const void *pPersistentData) m_Layers.Init(Kernel()); m_Collision.Init(&m_Layers); + m_World.m_pTuningList = m_aTuningList; m_World.m_Core.InitSwitchers(m_Collision.m_HighestSwitchNumber); char aMapName[IO_MAX_PATH_LENGTH]; diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index e67e55550..c35359a8f 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -371,3 +371,8 @@ void CGameWorld::ReleaseHooked(int ClientID) } } } + +CTuningParams *CGameWorld::Tuning() +{ + return &m_Core.m_aTuning[0]; +} diff --git a/src/game/server/gameworld.h b/src/game/server/gameworld.h index 05a222594..c86af59fd 100644 --- a/src/game/server/gameworld.h +++ b/src/game/server/gameworld.h @@ -164,6 +164,12 @@ public: Returns list with all Characters on line. */ std::vector IntersectedCharacters(vec2 Pos0, vec2 Pos1, float Radius, const CEntity *pNotThis = nullptr); + + CTuningParams *Tuning(); + + CTuningParams *m_pTuningList; + CTuningParams *TuningList() { return m_pTuningList; } + CTuningParams *GetTuning(int i) { return &TuningList()[i]; } }; #endif From 3fd4e10def51736a54a5b55d9f6f17265cc76fbd Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Wed, 6 Sep 2023 03:03:44 +0300 Subject: [PATCH 112/126] World cleanup --- src/game/client/prediction/entity.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/game/client/prediction/entity.h b/src/game/client/prediction/entity.h index 2c7b75dff..364d0141b 100644 --- a/src/game/client/prediction/entity.h +++ b/src/game/client/prediction/entity.h @@ -12,12 +12,14 @@ class CEntity { MACRO_ALLOC_HEAP() - friend class CGameWorld; // entity list handling + +private: + friend CGameWorld; // entity list handling CEntity *m_pPrevTypeEntity; CEntity *m_pNextTypeEntity; protected: - class CGameWorld *m_pGameWorld; + CGameWorld *m_pGameWorld; bool m_MarkedForDestroy; int m_ID; int m_ObjType; @@ -29,7 +31,7 @@ public: virtual ~CEntity(); std::vector &Switchers() { return m_pGameWorld->Switchers(); } - class CGameWorld *GameWorld() { return m_pGameWorld; } + CGameWorld *GameWorld() { return m_pGameWorld; } CTuningParams *Tuning() { return GameWorld()->Tuning(); } CTuningParams *TuningList() { return GameWorld()->TuningList(); } CTuningParams *GetTuning(int i) { return GameWorld()->GetTuning(i); } From 350ba77a743e0a70fcc85ec7c8033435e4b68f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 15 Sep 2023 20:52:03 +0200 Subject: [PATCH 113/126] Minor refactoring of ingame server browser - Remove unnecessary variable `Page`. - Use `IGraphics::CORNER_NONE` instead of `0` and instead of the inconsistent-looking `CORNER_BR` for the KoG-button. - Simplify UI rect layout. --- src/game/client/components/menus_ingame.cpp | 44 ++++++++++----------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 5c074c95e..c4b7235ff 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -812,49 +812,45 @@ void CMenus::RenderServerControl(CUIRect MainView) void CMenus::RenderInGameNetwork(CUIRect MainView) { - CUIRect Box = MainView; - CUIRect Button; - - int Page = g_Config.m_UiPage; - int NewPage = -1; - MainView.Draw(ms_ColorTabbarActive, IGraphics::CORNER_B, 10.0f); - Box.HSplitTop(5.0f, &MainView, &MainView); - Box.HSplitTop(24.0f, &Box, &MainView); + CUIRect TabBar, Button; + MainView.HSplitTop(24.0f, &TabBar, &MainView); - Box.VSplitLeft(100.0f, &Button, &Box); + int NewPage = g_Config.m_UiPage; + + TabBar.VSplitLeft(100.0f, &Button, &TabBar); static CButtonContainer s_InternetButton; - if(DoButton_MenuTab(&s_InternetButton, Localize("Internet"), Page == PAGE_INTERNET, &Button, 0)) + if(DoButton_MenuTab(&s_InternetButton, Localize("Internet"), g_Config.m_UiPage == PAGE_INTERNET, &Button, IGraphics::CORNER_NONE)) { - if(Page != PAGE_INTERNET) + if(g_Config.m_UiPage != PAGE_INTERNET) ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); NewPage = PAGE_INTERNET; } - Box.VSplitLeft(80.0f, &Button, &Box); + TabBar.VSplitLeft(80.0f, &Button, &TabBar); static CButtonContainer s_LanButton; - if(DoButton_MenuTab(&s_LanButton, Localize("LAN"), Page == PAGE_LAN, &Button, 0)) + if(DoButton_MenuTab(&s_LanButton, Localize("LAN"), g_Config.m_UiPage == PAGE_LAN, &Button, IGraphics::CORNER_NONE)) { - if(Page != PAGE_LAN) + if(g_Config.m_UiPage != PAGE_LAN) ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN); NewPage = PAGE_LAN; } - Box.VSplitLeft(110.0f, &Button, &Box); + TabBar.VSplitLeft(110.0f, &Button, &TabBar); static CButtonContainer s_FavoritesButton; - if(DoButton_MenuTab(&s_FavoritesButton, Localize("Favorites"), Page == PAGE_FAVORITES, &Button, 0)) + if(DoButton_MenuTab(&s_FavoritesButton, Localize("Favorites"), g_Config.m_UiPage == PAGE_FAVORITES, &Button, IGraphics::CORNER_NONE)) { - if(Page != PAGE_FAVORITES) + if(g_Config.m_UiPage != PAGE_FAVORITES) ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES); NewPage = PAGE_FAVORITES; } - Box.VSplitLeft(110.0f, &Button, &Box); + TabBar.VSplitLeft(110.0f, &Button, &TabBar); static CButtonContainer s_DDNetButton; - if(DoButton_MenuTab(&s_DDNetButton, "DDNet", Page == PAGE_DDNET, &Button, 0) || Page < PAGE_INTERNET || Page > PAGE_KOG) + if(DoButton_MenuTab(&s_DDNetButton, "DDNet", g_Config.m_UiPage == PAGE_DDNET, &Button, IGraphics::CORNER_NONE) || g_Config.m_UiPage < PAGE_INTERNET || g_Config.m_UiPage > PAGE_KOG) { - if(Page != PAGE_DDNET) + if(g_Config.m_UiPage != PAGE_DDNET) { Client()->RequestDDNetInfo(); ServerBrowser()->Refresh(IServerBrowser::TYPE_DDNET); @@ -862,11 +858,11 @@ void CMenus::RenderInGameNetwork(CUIRect MainView) NewPage = PAGE_DDNET; } - Box.VSplitLeft(110.0f, &Button, &Box); + TabBar.VSplitLeft(110.0f, &Button, &TabBar); static CButtonContainer s_KoGButton; - if(DoButton_MenuTab(&s_KoGButton, "KoG", Page == PAGE_KOG, &Button, IGraphics::CORNER_BR)) + if(DoButton_MenuTab(&s_KoGButton, "KoG", g_Config.m_UiPage == PAGE_KOG, &Button, IGraphics::CORNER_NONE)) { - if(Page != PAGE_KOG) + if(g_Config.m_UiPage != PAGE_KOG) { Client()->RequestDDNetInfo(); ServerBrowser()->Refresh(IServerBrowser::TYPE_KOG); @@ -874,7 +870,7 @@ void CMenus::RenderInGameNetwork(CUIRect MainView) NewPage = PAGE_KOG; } - if(NewPage != -1) + if(NewPage != g_Config.m_UiPage) { if(Client()->State() != IClient::STATE_OFFLINE) SetMenuPage(NewPage); From 25c3e1616e830de4259eb706b6b5269ba823e319 Mon Sep 17 00:00:00 2001 From: furo Date: Fri, 15 Sep 2023 22:30:25 +0200 Subject: [PATCH 114/126] Use colour pack functions for editor --- src/game/editor/editor.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index d8b4022c0..2c4fb02ff 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -2916,11 +2916,7 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int * } else if(pProps[i].m_Type == PROPTYPE_COLOR) { - const ColorRGBA ColorPick = ColorRGBA( - ((pProps[i].m_Value >> 24) & 0xff) / 255.0f, - ((pProps[i].m_Value >> 16) & 0xff) / 255.0f, - ((pProps[i].m_Value >> 8) & 0xff) / 255.0f, - (pProps[i].m_Value & 0xff) / 255.0f); + const ColorRGBA ColorPick = ColorRGBA::UnpackAlphaLast(pProps[i].m_Value); CUIRect ColorRect; Shifter.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f * UI()->ButtonColorMul(&pIDs[i])), IGraphics::CORNER_ALL, 5.0f); @@ -2962,7 +2958,7 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int * else if(UI()->IsPopupOpen(&s_ColorPickerPopupContext)) { ColorRGBA c = color_cast(s_ColorPickerPopupContext.m_HsvaColor); - const int NewColor = ((int)(c.r * 255.0f) & 0xff) << 24 | ((int)(c.g * 255.0f) & 0xff) << 16 | ((int)(c.b * 255.0f) & 0xff) << 8 | ((int)(c.a * 255.0f) & 0xff); + const int NewColor = c.PackAlphaLast(s_ColorPickerPopupContext.m_Alpha); if(NewColor != pProps[i].m_Value) { *pNewVal = NewColor; From f42e39a9e2f52a98d6983b764d4717bf644ab0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 6 Sep 2023 23:44:58 +0200 Subject: [PATCH 115/126] Consistently name `CScrollRegion` and `CListBox` getters Remove `Is` from the getter names for the same reason that removing `Get` from the name is preferred. The word `Is` was inconsistently used as a prefix in `CScrollRegion` but as an infix in `CListBox`. --- src/game/client/components/menus_browser.cpp | 4 ++-- src/game/client/ui_listbox.cpp | 8 ++++---- src/game/client/ui_listbox.h | 6 +++--- src/game/client/ui_scrollregion.cpp | 10 +++++----- src/game/client/ui_scrollregion.h | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index a48a5dc05..cb0d66e32 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -1394,7 +1394,7 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) // friends list static CScrollRegion s_ScrollRegion; - if(!s_ScrollRegion.IsScrollbarShown()) + if(!s_ScrollRegion.ScrollbarShown()) List.VSplitRight(3.0f, &List, nullptr); vec2 ScrollOffset(0.0f, 0.0f); CScrollRegionParams ScrollParams; @@ -1455,7 +1455,7 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) const auto &Friend = m_avFriends[FriendType][FriendIndex]; List.HSplitTop(11.0f + 10.0f + 2 * 2.0f + 1.0f + (Friend.ServerInfo() == nullptr ? 0.0f : 10.0f), &Rect, &List); s_ScrollRegion.AddRect(Rect); - if(s_ScrollRegion.IsRectClipped(Rect)) + if(s_ScrollRegion.RectClipped(Rect)) continue; const bool Inside = UI()->MouseHovered(&Rect); diff --git a/src/game/client/ui_listbox.cpp b/src/game/client/ui_listbox.cpp index 726e6c294..191de4a65 100644 --- a/src/game/client/ui_listbox.cpp +++ b/src/game/client/ui_listbox.cpp @@ -17,7 +17,7 @@ CListBox::CListBox() m_FilterOffset = 0.0f; m_HasHeader = false; m_AutoSpacing = 0.0f; - m_ScrollbarIsShown = false; + m_ScrollbarShown = false; m_Active = true; } @@ -140,7 +140,7 @@ CListboxItem CListBox::DoNextRow() m_RowView.VSplitLeft(m_RowView.w / (m_ListBoxItemsPerRow - m_ListBoxItemIndex % m_ListBoxItemsPerRow), &Item.m_Rect, &m_RowView); Item.m_Selected = m_ListBoxSelectedIndex == m_ListBoxItemIndex; - Item.m_Visible = !m_ScrollRegion.IsRectClipped(Item.m_Rect); + Item.m_Visible = !m_ScrollRegion.RectClipped(Item.m_Rect); m_ListBoxItemIndex++; return Item; @@ -188,7 +188,7 @@ CListboxItem CListBox::DoNextItem(const void *pId, bool Selected) Item.m_Rect.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, m_Active ? 0.5f : 0.33f), IGraphics::CORNER_ALL, 5.0f); } - if(UI()->HotItem() == pId && !m_ScrollRegion.IsAnimating()) + if(UI()->HotItem() == pId && !m_ScrollRegion.Animating()) { Item.m_Rect.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.33f), IGraphics::CORNER_ALL, 5.0f); } @@ -208,7 +208,7 @@ int CListBox::DoEnd() m_ScrollRegion.End(); m_Active |= m_ScrollRegion.Params().m_Active; - m_ScrollbarIsShown = m_ScrollRegion.IsScrollbarShown(); + m_ScrollbarShown = m_ScrollRegion.ScrollbarShown(); if(m_ListBoxNewSelOffset != 0 && m_ListBoxNumItems > 0 && m_ListBoxSelectedIndex == m_ListBoxNewSelected) { m_ListBoxNewSelected = clamp((m_ListBoxNewSelected == -1 ? 0 : m_ListBoxNewSelected) + m_ListBoxNewSelOffset, 0, m_ListBoxNumItems - 1); diff --git a/src/game/client/ui_listbox.h b/src/game/client/ui_listbox.h index e50aa246a..bd72450e3 100644 --- a/src/game/client/ui_listbox.h +++ b/src/game/client/ui_listbox.h @@ -29,7 +29,7 @@ private: int m_ListBoxItemsPerRow; bool m_ListBoxItemSelected; bool m_ListBoxItemActivated; - bool m_ScrollbarIsShown; + bool m_ScrollbarShown; const char *m_pBottomText; float m_FooterHeight; float m_AutoSpacing; @@ -64,8 +64,8 @@ public: bool WasItemSelected() const { return m_ListBoxItemSelected; } bool WasItemActivated() const { return m_ListBoxItemActivated; } - bool ScrollbarIsShown() const { return m_ScrollbarIsShown; } - float ScrollbarWidth() const { return ScrollbarIsShown() ? ScrollbarWidthMax() : 0.0f; } + bool ScrollbarShown() const { return m_ScrollbarShown; } + float ScrollbarWidth() const { return ScrollbarShown() ? ScrollbarWidthMax() : 0.0f; } float ScrollbarWidthMax() const { return 20.0f; } }; diff --git a/src/game/client/ui_scrollregion.cpp b/src/game/client/ui_scrollregion.cpp index 885f63fec..37ec86143 100644 --- a/src/game/client/ui_scrollregion.cpp +++ b/src/game/client/ui_scrollregion.cpp @@ -193,7 +193,7 @@ bool CScrollRegion::AddRect(const CUIRect &Rect, bool ShouldScrollHere) m_ContentH = maximum(std::ceil(Rect.y + Rect.h - (m_ClipRect.y + m_ContentScrollOff.y)) + HEIGHT_MAGIC_FIX, m_ContentH); if(ShouldScrollHere) ScrollHere(); - return !IsRectClipped(Rect); + return !RectClipped(Rect); } void CScrollRegion::ScrollHere(EScrollOption Option) @@ -230,7 +230,7 @@ void CScrollRegion::ScrollRelative(EScrollRelative Direction, float SpeedMultipl void CScrollRegion::DoEdgeScrolling() { - if(!IsScrollbarShown()) + if(!ScrollbarShown()) return; const float ScrollBorderSize = 20.0f; @@ -244,17 +244,17 @@ void CScrollRegion::DoEdgeScrolling() ScrollRelative(SCROLLRELATIVE_DOWN, minimum(MaxScrollMultiplier, (UI()->MouseY() - BottomScrollPosition) * ScrollSpeedFactor)); } -bool CScrollRegion::IsRectClipped(const CUIRect &Rect) const +bool CScrollRegion::RectClipped(const CUIRect &Rect) const { return (m_ClipRect.x > (Rect.x + Rect.w) || (m_ClipRect.x + m_ClipRect.w) < Rect.x || m_ClipRect.y > (Rect.y + Rect.h) || (m_ClipRect.y + m_ClipRect.h) < Rect.y); } -bool CScrollRegion::IsScrollbarShown() const +bool CScrollRegion::ScrollbarShown() const { return m_ContentH > m_ClipRect.h; } -bool CScrollRegion::IsAnimating() const +bool CScrollRegion::Animating() const { return m_AnimTime > 0.0f; } diff --git a/src/game/client/ui_scrollregion.h b/src/game/client/ui_scrollregion.h index 3ade665e9..f196fb61c 100644 --- a/src/game/client/ui_scrollregion.h +++ b/src/game/client/ui_scrollregion.h @@ -68,7 +68,7 @@ Usage: s_ScrollRegion.AddRect(Rect); -- [Optional] Knowing if a rect is clipped -- - s_ScrollRegion.IsRectClipped(Rect); + s_ScrollRegion.RectClipped(Rect); -- [Optional] Scroll to a rect (to the last added rect)-- ... @@ -137,9 +137,9 @@ public: void ScrollRelative(EScrollRelative Direction, float SpeedMultiplier = 1.0f); const CUIRect *ClipRect() const { return &m_ClipRect; } void DoEdgeScrolling(); - bool IsRectClipped(const CUIRect &Rect) const; - bool IsScrollbarShown() const; - bool IsAnimating() const; + bool RectClipped(const CUIRect &Rect) const; + bool ScrollbarShown() const; + bool Animating() const; const CScrollRegionParams &Params() const { return m_Params; } }; From 11a16f553f471073e4426ed75a062a43d8678327 Mon Sep 17 00:00:00 2001 From: Alexander Akulich Date: Sat, 16 Sep 2023 02:52:57 +0300 Subject: [PATCH 116/126] CMake: Revert versioning (move it back to version.h) --- CMakeLists.txt | 81 +++++++++++----------------- src/game/{version.h.in => version.h} | 7 +-- 2 files changed, 36 insertions(+), 52 deletions(-) rename src/game/{version.h.in => version.h} (78%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 189834c59..889c817be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,16 +5,29 @@ if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.15) message(WARNING "Building for macOS < 10.15 is not supported") endif() -# Set version if not explicitly set -set(DDNET_NETWORK_VERSION 17.2.1) +file(STRINGS src/game/version.h VERSION_LINE + LIMIT_COUNT 1 + REGEX "^#define GAME_RELEASE_VERSION " +) -if(NOT VERSION) - set(VERSION ${DDNET_NETWORK_VERSION}) +if(VERSION_LINE MATCHES "\"([0-9]+)\\.([0-9]+)\\.([0-9]+)\"") + set(VERSION_MAJOR ${CMAKE_MATCH_1}) + set(VERSION_MINOR ${CMAKE_MATCH_2}) + set(VERSION_PATCH ${CMAKE_MATCH_3}) +elseif(VERSION_LINE MATCHES "\"([0-9]+)\\.([0-9]+)\"") + set(VERSION_MAJOR ${CMAKE_MATCH_1}) + set(VERSION_MINOR ${CMAKE_MATCH_2}) + set(VERSION_PATCH "0") +else() + message(FATAL_ERROR "Couldn't parse version from src/game/version.h") endif() -project(DDNet - VERSION ${VERSION} -) +# Extra support for CMake pre-3.0 +if(VERSION_PATCH STREQUAL "0") + project(DDNet VERSION ${VERSION_MAJOR}.${VERSION_MINOR}) +else() + project(DDNet VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) +endif() set(ORIGINAL_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}) set(ORIGINAL_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) @@ -116,6 +129,11 @@ if(TEST_MYSQL) set(MYSQL ON) endif() +# Set version if not explicitly set +if(NOT VERSION) + set(VERSION ${PROJECT_VERSION}) +endif() + set(OpenGL_GL_PREFERENCE LEGACY) # Set the default build type to Release @@ -144,44 +162,10 @@ if(IPO) endif() endif() -function(get_network_version_number VERSION OUTPUT) - string(REPLACE "." ";" VERSION_LIST ${VERSION}) - - list(LENGTH VERSION_LIST VERSION_LIST_LENGTH) - if("${VERSION_LIST_LENGTH}" GREATER 2) - list(GET VERSION_LIST 2 NET_VERSION_PATCH) - else() - set(NET_VERSION_PATCH 0) - endif() - if("${VERSION_LIST_LENGTH}" GREATER 1) - list(GET VERSION_LIST 1 NET_VERSION_MINOR) - else() - set(NET_VERSION_MINOR 0) - endif() - if("${VERSION_LIST_LENGTH}" GREATER 0) - list(GET VERSION_LIST 0 NET_VERSION_MAJOR) - else() - message(SEND_ERROR "Invalid network version \"${VERSION}\"") - set(NET_VERSION_MAJOR 0) - endif() - - if("0${NET_VERSION_PATCH}" GREATER 9) - message(SEND_ERROR "Invalid net patch version (the version is \"${VERSION}\", the patch part must be in range 0-9 if set)") - endif() - if("0${NET_VERSION_MINOR}" GREATER 99) - message(SEND_ERROR "Invalid net minor version (the version is \"${VERSION}\", the minor part must be in range 0-99)") - endif() - if("0${NET_VERSION_MINOR}" GREATER 9) - set(NETWORK_VERSION ${NET_VERSION_MAJOR}${NET_VERSION_MINOR}${NET_VERSION_PATCH}) - else() - set(NETWORK_VERSION ${NET_VERSION_MAJOR}0${NET_VERSION_MINOR}${NET_VERSION_PATCH}) - endif() - set(${OUTPUT} ${NETWORK_VERSION} PARENT_SCOPE) -endfunction() - -get_network_version_number("${DDNET_NETWORK_VERSION}" DDNET_VERSION_NUMBER) - -configure_file(src/game/version.h.in ${PROJECT_BINARY_DIR}/src/game/version.h) +if(NOT "${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") + # Remove version.h generated by previous build code + file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/src/game/version.h) +endif() set(SERVER_EXECUTABLE DDNet-Server CACHE STRING "Name of the built server executable") set(CLIENT_EXECUTABLE DDNet CACHE STRING "Name of the build client executable") @@ -403,7 +387,7 @@ function(set_glob VAR GLOBBING EXTS DIRECTORY) # ... endfunction() function(set_src VAR GLOBBING DIRECTORY) # ... - set_glob(${VAR} ${GLOBBING} "c;cpp;h;h.in" ${DIRECTORY} ${ARGN}) + set_glob(${VAR} ${GLOBBING} "c;cpp;h" ${DIRECTORY} ${ARGN}) set(${VAR} ${${VAR}} PARENT_SCOPE) set(CHECKSUM_SRC ${CHECKSUM_SRC} ${${VAR}} PARENT_SCOPE) endfunction() @@ -2019,7 +2003,7 @@ set_src(GAME_SHARED GLOB src/game teamscore.h tuning.h variables.h - version.h.in + version.h voting.h ) # A bit hacky, but these are needed to register all the UUIDs, even for stuff @@ -2046,7 +2030,6 @@ set(GAME_GENERATED_SHARED src/game/generated/protocol.h src/game/generated/protocol7.h src/game/generated/protocolglue.h - src/game/version.h ) set(DEPS ${DEP_JSON} ${DEP_MD5} ${ZLIB_DEP}) @@ -2921,7 +2904,7 @@ set(CPACK_SOURCE_GENERATOR ZIP TGZ TBZ2 TXZ) set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) -set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) +set(CPACK_PACKAGE_VERSION ${VERSION}) set(CPACK_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}) if(TARGET_OS AND TARGET_BITS) diff --git a/src/game/version.h.in b/src/game/version.h similarity index 78% rename from src/game/version.h.in rename to src/game/version.h index f1fb9e093..db1d9c0a4 100644 --- a/src/game/version.h.in +++ b/src/game/version.h @@ -2,11 +2,12 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #ifndef GAME_VERSION_H #define GAME_VERSION_H - -#define GAME_RELEASE_VERSION "${PROJECT_VERSION}" +#ifndef GAME_RELEASE_VERSION +#define GAME_RELEASE_VERSION "17.2.1" +#endif #define GAME_VERSION "0.6.4, " GAME_RELEASE_VERSION #define GAME_NETVERSION "0.6 626fce9a778df4d4" -#define DDNET_VERSION_NUMBER ${DDNET_VERSION_NUMBER} +#define DDNET_VERSION_NUMBER 17021 extern const char *GIT_SHORTREV_HASH; #define GAME_NAME "DDNet" #endif From 643a23aad37c6803f42b7c7a7ed817eea07bfc96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 16 Sep 2023 11:53:23 +0200 Subject: [PATCH 117/126] Add `unninja`, `solo` and `deep` testing commands Closes #7161. --- src/game/ddracecommands.h | 3 +++ src/game/server/ddracecommands.cpp | 26 ++++++++++++++++++++++++++ src/game/server/gamecontext.h | 3 +++ 3 files changed, 32 insertions(+) diff --git a/src/game/ddracecommands.h b/src/game/ddracecommands.h index 29938c983..36b30be20 100644 --- a/src/game/ddracecommands.h +++ b/src/game/ddracecommands.h @@ -25,11 +25,14 @@ CONSOLE_COMMAND("unrifle", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnLaser, this, CONSOLE_COMMAND("unjetpack", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnJetpack, this, "Removes the jetpack from you") CONSOLE_COMMAND("unweapons", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnWeapons, this, "Removes all weapons from you") CONSOLE_COMMAND("ninja", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConNinja, this, "Makes you a ninja") +CONSOLE_COMMAND("unninja", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnNinja, this, "Removes ninja from you") CONSOLE_COMMAND("super", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConSuper, this, "Makes you super") CONSOLE_COMMAND("unsuper", "", CFGFLAG_SERVER, ConUnSuper, this, "Removes super from you") CONSOLE_COMMAND("endless_hook", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConEndlessHook, this, "Gives you endless hook") CONSOLE_COMMAND("unendless_hook", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnEndlessHook, this, "Removes endless hook from you") +CONSOLE_COMMAND("solo", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConSolo, this, "Puts you into solo part") CONSOLE_COMMAND("unsolo", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnSolo, this, "Puts you out of solo part") +CONSOLE_COMMAND("deep", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConDeep, this, "Puts you into deep freeze") CONSOLE_COMMAND("undeep", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnDeep, this, "Puts you out of deep freeze") CONSOLE_COMMAND("livefreeze", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConLiveFreeze, this, "Makes you live frozen") CONSOLE_COMMAND("unlivefreeze", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnLiveFreeze, this, "Puts you out of live freeze") diff --git a/src/game/server/ddracecommands.cpp b/src/game/server/ddracecommands.cpp index 15072e481..156b6ae23 100644 --- a/src/game/server/ddracecommands.cpp +++ b/src/game/server/ddracecommands.cpp @@ -98,6 +98,12 @@ void CGameContext::ConNinja(IConsole::IResult *pResult, void *pUserData) pSelf->ModifyWeapons(pResult, pUserData, WEAPON_NINJA, false); } +void CGameContext::ConUnNinja(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->ModifyWeapons(pResult, pUserData, WEAPON_NINJA, true); +} + void CGameContext::ConEndlessHook(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -147,6 +153,16 @@ void CGameContext::ConUnSuper(IConsole::IResult *pResult, void *pUserData) } } +void CGameContext::ConSolo(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(!CheckClientID(pResult->m_ClientID)) + return; + CCharacter *pChr = pSelf->GetPlayerChar(pResult->m_ClientID); + if(pChr) + pChr->SetSolo(true); +} + void CGameContext::ConUnSolo(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -157,6 +173,16 @@ void CGameContext::ConUnSolo(IConsole::IResult *pResult, void *pUserData) pChr->SetSolo(false); } +void CGameContext::ConDeep(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(!CheckClientID(pResult->m_ClientID)) + return; + CCharacter *pChr = pSelf->GetPlayerChar(pResult->m_ClientID); + if(pChr) + pChr->SetDeepFrozen(true); +} + void CGameContext::ConUnDeep(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 5ba0d394a..5ba1fddab 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -363,9 +363,12 @@ private: static void ConKillPlayer(IConsole::IResult *pResult, void *pUserData); static void ConNinja(IConsole::IResult *pResult, void *pUserData); + static void ConUnNinja(IConsole::IResult *pResult, void *pUserData); static void ConEndlessHook(IConsole::IResult *pResult, void *pUserData); static void ConUnEndlessHook(IConsole::IResult *pResult, void *pUserData); + static void ConSolo(IConsole::IResult *pResult, void *pUserData); static void ConUnSolo(IConsole::IResult *pResult, void *pUserData); + static void ConDeep(IConsole::IResult *pResult, void *pUserData); static void ConUnDeep(IConsole::IResult *pResult, void *pUserData); static void ConLiveFreeze(IConsole::IResult *pResult, void *pUserData); static void ConUnLiveFreeze(IConsole::IResult *pResult, void *pUserData); From b423ee59afe6a9fb709b7a7d198e5398595e76cd Mon Sep 17 00:00:00 2001 From: furo Date: Fri, 15 Sep 2023 13:58:30 +0200 Subject: [PATCH 118/126] Color text if you have voted f3 or f4 Add Sv_YourVote packet --- datasrc/network.py | 4 ++++ src/game/client/components/hud.cpp | 8 ++++++++ src/game/client/components/voting.cpp | 8 +++++++- src/game/server/gamecontext.cpp | 3 +++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/datasrc/network.py b/datasrc/network.py index 27b013953..6d5f18309 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -549,4 +549,8 @@ Messages = [ NetIntRange("m_Team", 0, 'MAX_CLIENTS-1'), NetIntRange("m_First", -1, 'MAX_CLIENTS-1'), ]), + + NetMessageEx("Sv_YourVote", "yourvote@netmsg.ddnet.org", [ + NetIntRange("m_Voted", -1, 1), + ]), ] diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 86bfca2fa..2ae8832c6 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -598,11 +598,19 @@ void CHud::RenderVoting() str_format(aBuf, sizeof(aBuf), "%s - %s", aKey, Localize("Vote yes")); Base.y += Base.h; Base.h = 12.0f; + if(m_pClient->m_Voting.TakenChoice() == 1) + TextRender()->TextColor(ColorRGBA(0.2f, 0.9f, 0.2f, 0.85f)); UI()->DoLabel(&Base, aBuf, 6.0f, TEXTALIGN_ML); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + m_pClient->m_Binds.GetKey("vote no", aKey, sizeof(aKey)); str_format(aBuf, sizeof(aBuf), "%s - %s", Localize("Vote no"), aKey); + if(m_pClient->m_Voting.TakenChoice() == -1) + TextRender()->TextColor(ColorRGBA(0.9f, 0.2f, 0.2f, 0.85f)); UI()->DoLabel(&Base, aBuf, 6.0f, TEXTALIGN_MR); + + TextRender()->TextColor(TextRender()->DefaultTextColor()); } void CHud::RenderCursor() diff --git a/src/game/client/components/voting.cpp b/src/game/client/components/voting.cpp index dd9dca5c6..21242a9d5 100644 --- a/src/game/client/components/voting.cpp +++ b/src/game/client/components/voting.cpp @@ -130,7 +130,8 @@ void CVoting::AddvoteOption(const char *pDescription, const char *pCommand) void CVoting::Vote(int v) { - m_Voted = v; + if(m_Voted == 0) + m_Voted = v; CNetMsg_Cl_Vote Msg = {v}; Client()->SendPackMsgActive(&Msg, MSGFLAG_VITAL); } @@ -299,6 +300,11 @@ void CVoting::OnMessage(int MsgType, void *pRawMsg) } } } + else if(MsgType == NETMSGTYPE_SV_YOURVOTE) + { + CNetMsg_Sv_YourVote *pMsg = (CNetMsg_Sv_YourVote *)pRawMsg; + m_Voted = pMsg->m_Voted; + } } void CVoting::OnRender() diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 84e66ef8c..45c9cbcbe 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -2373,6 +2373,9 @@ void CGameContext::OnVoteNetMessage(const CNetMsg_Cl_Vote *pMsg, int ClientID) pPlayer->m_Vote = pMsg->m_Vote; pPlayer->m_VotePos = ++m_VotePos; m_VoteUpdate = true; + + CNetMsg_Sv_YourVote Msg = {pMsg->m_Vote}; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID); } void CGameContext::OnSetTeamNetMessage(const CNetMsg_Cl_SetTeam *pMsg, int ClientID) From 3e548f0f66c7b8a759accda323cc64669dceac43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 7 Sep 2023 22:00:18 +0200 Subject: [PATCH 119/126] Make listbox scrollbar width and margin adjustable --- src/game/client/ui_listbox.cpp | 4 +++- src/game/client/ui_listbox.h | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/game/client/ui_listbox.cpp b/src/game/client/ui_listbox.cpp index 191de4a65..14cd6e65e 100644 --- a/src/game/client/ui_listbox.cpp +++ b/src/game/client/ui_listbox.cpp @@ -14,7 +14,8 @@ CListBox::CListBox() { m_ScrollOffset = vec2(0.0f, 0.0f); m_ListBoxUpdateScroll = false; - m_FilterOffset = 0.0f; + m_ScrollbarWidth = 20.0f; + m_ScrollbarMargin = 5.0f; m_HasHeader = false; m_AutoSpacing = 0.0f; m_ScrollbarShown = false; @@ -118,6 +119,7 @@ void CListBox::DoStart(float RowHeight, int NumItems, int ItemsPerRow, int RowsP CScrollRegionParams ScrollParams; ScrollParams.m_Active = m_Active; ScrollParams.m_ScrollbarWidth = ScrollbarWidthMax(); + ScrollParams.m_ScrollbarMargin = ScrollbarMargin(); ScrollParams.m_ScrollUnit = (m_ListBoxRowHeight + m_AutoSpacing) * RowsPerScroll; ScrollParams.m_Flags = ForceShowScrollbar ? CScrollRegionParams::FLAG_CONTENT_STATIC_WIDTH : 0; m_ScrollRegion.Begin(&m_ListBoxView, &m_ScrollOffset, &ScrollParams); diff --git a/src/game/client/ui_listbox.h b/src/game/client/ui_listbox.h index bd72450e3..1e2b9af37 100644 --- a/src/game/client/ui_listbox.h +++ b/src/game/client/ui_listbox.h @@ -35,8 +35,9 @@ private: float m_AutoSpacing; CScrollRegion m_ScrollRegion; vec2 m_ScrollOffset; - float m_FilterOffset; int m_BackgroundCorners; + float m_ScrollbarWidth; + float m_ScrollbarMargin; bool m_HasHeader; bool m_Active; @@ -66,7 +67,10 @@ public: bool ScrollbarShown() const { return m_ScrollbarShown; } float ScrollbarWidth() const { return ScrollbarShown() ? ScrollbarWidthMax() : 0.0f; } - float ScrollbarWidthMax() const { return 20.0f; } + float ScrollbarWidthMax() const { return m_ScrollbarWidth; } + void SetScrollbarWidth(float Width) { m_ScrollbarWidth = Width; } + float ScrollbarMargin() const { return m_ScrollbarMargin; } + void SetScrollbarMargin(float Margin) { m_ScrollbarMargin = Margin; } }; #endif From 673b7facbe4a7c0cf8e18d385a4b2c4bc092b5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 16 Sep 2023 13:37:53 +0200 Subject: [PATCH 120/126] Remove unnecessary return value of `net_init` The return value is unused and always 0, as the function fails with an assertion instead of returning an error value. --- src/base/system.cpp | 10 +++------- src/base/system.h | 4 +--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/base/system.cpp b/src/base/system.cpp index 079286342..89a09f68c 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -2209,16 +2209,12 @@ int net_would_block() #endif } -int net_init() +void net_init() { #if defined(CONF_FAMILY_WINDOWS) - WSADATA wsaData; - int err = WSAStartup(MAKEWORD(1, 1), &wsaData); - dbg_assert(err == 0, "network initialization failed."); - return err == 0 ? 0 : 1; + WSADATA wsa_data; + dbg_assert(WSAStartup(MAKEWORD(1, 1), &wsa_data) == 0, "network initialization failed."); #endif - - return 0; } #if defined(CONF_FAMILY_UNIX) diff --git a/src/base/system.h b/src/base/system.h index 98490f1ed..c1f01bfe3 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -879,11 +879,9 @@ typedef struct sockaddr_un UNIXSOCKETADDR; * * @ingroup Network-General * - * @return 0 on success. - * * @remark You must call this function before using any other network functions. */ -int net_init(); +void net_init(); /* Function: net_host_lookup From 0b57d56f48310bad9540a96770c4c78589a18162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 22 Aug 2023 20:45:37 +0200 Subject: [PATCH 121/126] Overhaul server browser filter, details and friends tabs Move the "Filter", "Info" and "Friends" tabs to the top, above the tab content. Use icons instead of text for the tabs. Use animator to animate the tabs on mouse-over. Closes #6613. Make spacings, corners and font sizes used in the filter, details and friends tabs more consistent. Remove some unnecessary dark UI rect backgrounds. Improve alignment of the number of friends with the heart icon for entries in the server list. Improve layout of countries and types filters. Make the filters scrollable when there are many entries. Refactor most of the server browser in preparation for replacing the DDNet and KoG tabs with a community filter, which will work like the countries and types filters. Split rendering of different server browser sections into multiple functions to improve readability. Reduce duplicate code for the countries and types filters. --- src/engine/textrender.h | 2 + src/game/client/components/menus.h | 23 +- src/game/client/components/menus_browser.cpp | 1600 +++++++++--------- 3 files changed, 786 insertions(+), 839 deletions(-) diff --git a/src/engine/textrender.h b/src/engine/textrender.h index 5383f0975..e9bef6407 100644 --- a/src/engine/textrender.h +++ b/src/engine/textrender.h @@ -91,6 +91,8 @@ MAYBE_UNUSED static const char *FONT_ICON_GEAR = "\xEF\x80\x93"; MAYBE_UNUSED static const char *FONT_ICON_PEN_TO_SQUARE = "\xEF\x81\x84"; MAYBE_UNUSED static const char *FONT_ICON_CLAPPERBOARD = "\xEE\x84\xB1"; MAYBE_UNUSED static const char *FONT_ICON_EARTH_AMERICAS = "\xEF\x95\xBD"; +MAYBE_UNUSED static const char *FONT_ICON_LIST_UL = "\xEF\x83\x8A"; +MAYBE_UNUSED static const char *FONT_ICON_INFO = "\xEF\x84\xA9"; MAYBE_UNUSED static const char *FONT_ICON_SLASH = "\xEF\x9C\x95"; MAYBE_UNUSED static const char *FONT_ICON_PLAY = "\xEF\x81\x8B"; diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index dcf154d65..51bc1210b 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -409,8 +409,6 @@ protected: std::vector m_avFriends[NUM_FRIEND_TYPES]; const CFriendItem *m_pRemoveFriend = nullptr; - void FriendlistOnUpdate(); - // found in menus.cpp int Render(); #if defined(CONF_VIDEORECORDER) @@ -454,11 +452,19 @@ protected: // found in menus_browser.cpp int m_SelectedIndex; bool m_ServerBrowserShouldRevealSelection; - void RenderServerbrowserServerList(CUIRect View); + void RenderServerbrowserServerList(CUIRect View, bool &WasListboxItemActivated); + void RenderServerbrowserStatusBox(CUIRect StatusBox, bool WasListboxItemActivated); void Connect(const char *pAddress); void PopupConfirmSwitchServer(); - void RenderServerbrowserServerDetail(CUIRect View); void RenderServerbrowserFilters(CUIRect View); + void RenderServerbrowserDDNetFilter(CUIRect View, + char *pFilterExclude, int FilterExcludeSize, + float ItemHeight, int MaxItems, int ItemsPerRow, + CScrollRegion &ScrollRegion, std::vector &vItemIds, + const std::function &GetItemName, + const std::function &RenderItem); + void RenderServerbrowserCountriesFilter(CUIRect View, int Network); + void RenderServerbrowserTypesFilter(CUIRect View, int Network); struct SPopupCountrySelectionContext { CMenus *m_pMenus; @@ -466,11 +472,17 @@ protected: bool m_New; }; static CUI::EPopupMenuFunctionResult PopupCountrySelection(void *pContext, CUIRect View, bool Active); + void RenderServerbrowserInfo(CUIRect View); + void RenderServerbrowserInfoScoreboard(CUIRect View, const CServerInfo *pSelectedServer); void RenderServerbrowserFriends(CUIRect View); + void FriendlistOnUpdate(); void PopupConfirmRemoveFriend(); + void RenderServerbrowserTabBar(CUIRect TabBar); + void RenderServerbrowserToolBox(CUIRect ToolBox); void RenderServerbrowser(CUIRect MainView); template bool PrintHighlighted(const char *pName, F &&PrintFn); + CTeeRenderInfo GetTeeRenderInfo(vec2 Size, const char *pSkinName, bool CustomSkinColors, int CustomSkinColorBody, int CustomSkinColorFeet) const; static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); static void ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); @@ -583,6 +595,9 @@ public: SMALL_TAB_EDITOR, SMALL_TAB_DEMOBUTTON, SMALL_TAB_SERVER, + SMALL_TAB_BROWSER_FILTER, + SMALL_TAB_BROWSER_INFO, + SMALL_TAB_BROWSER_FRIENDS, SMALL_TAB_LENGTH, }; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index cb0d66e32..b2a0cd23b 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -23,21 +22,14 @@ using namespace FontIcons; -static const int gs_OffsetColFlagLock = 2; -static const int gs_OffsetColFav = gs_OffsetColFlagLock + 3; -static const int gs_OffsetColOff = gs_OffsetColFav + 3; -static const int gs_OffsetColName = gs_OffsetColOff + 3; -static const int gs_OffsetColGameType = gs_OffsetColName + 3; -static const int gs_OffsetColMap = gs_OffsetColGameType + 3; -static const int gs_OffsetColPlayers = gs_OffsetColMap + 3; -static const int gs_OffsetColPing = gs_OffsetColPlayers + 3; -static const int gs_OffsetColVersion = gs_OffsetColPing + 3; +static const ColorRGBA gs_HighlightedTextColor = ColorRGBA(0.4f, 0.4f, 1.0f, 1.0f); -void FormatServerbrowserPing(char *pBuffer, int BufferLength, const CServerInfo *pInfo) +template +static void FormatServerbrowserPing(char (&aBuffer)[N], const CServerInfo *pInfo) { if(!pInfo->m_LatencyIsEstimated) { - str_from_int(pInfo->m_Latency, pBuffer, BufferLength); + str_from_int(pInfo->m_Latency, aBuffer); return; } static const char *LOCATION_NAMES[CServerInfo::NUM_LOCS] = { @@ -51,22 +43,53 @@ void FormatServerbrowserPing(char *pBuffer, int BufferLength, const CServerInfo Localizable("CHN"), // LOC_CHINA }; dbg_assert(0 <= pInfo->m_Location && pInfo->m_Location < CServerInfo::NUM_LOCS, "location out of range"); - str_copy(pBuffer, Localize(LOCATION_NAMES[pInfo->m_Location]), BufferLength); + str_copy(aBuffer, Localize(LOCATION_NAMES[pInfo->m_Location])); } -void CMenus::RenderServerbrowserServerList(CUIRect View) +static ColorRGBA GetPingTextColor(int Latency) { + return color_cast(ColorHSLA((300.0f - clamp(Latency, 0, 300)) / 1000.0f, 1.0f, 0.5f)); +} + +static ColorRGBA GetGametypeTextColor(const char *pGametype) +{ + ColorHSLA HslaColor; + if(str_comp(pGametype, "DM") == 0 || str_comp(pGametype, "TDM") == 0 || str_comp(pGametype, "CTF") == 0) + HslaColor = ColorHSLA(0.33f, 1.0f, 0.75f); + else if(str_find_nocase(pGametype, "catch")) + HslaColor = ColorHSLA(0.17f, 1.0f, 0.75f); + else if(str_find_nocase(pGametype, "idm") || str_find_nocase(pGametype, "itdm") || str_find_nocase(pGametype, "ictf") || str_find_nocase(pGametype, "f-ddrace")) + HslaColor = ColorHSLA(0.0f, 1.0f, 0.75f); + else if(str_find_nocase(pGametype, "fng")) + HslaColor = ColorHSLA(0.83f, 1.0f, 0.75f); + else if(str_find_nocase(pGametype, "gores")) + HslaColor = ColorHSLA(0.525f, 1.0f, 0.75f); + else if(str_find_nocase(pGametype, "BW")) + HslaColor = ColorHSLA(0.05f, 1.0f, 0.75f); + else if(str_find_nocase(pGametype, "ddracenet") || str_find_nocase(pGametype, "ddnet") || str_find_nocase(pGametype, "0xf")) + HslaColor = ColorHSLA(0.58f, 1.0f, 0.75f); + else if(str_find_nocase(pGametype, "ddrace") || str_find_nocase(pGametype, "mkrace")) + HslaColor = ColorHSLA(0.75f, 1.0f, 0.75f); + else if(str_find_nocase(pGametype, "race") || str_find_nocase(pGametype, "fastcap")) + HslaColor = ColorHSLA(0.46f, 1.0f, 0.75f); + else if(str_find_nocase(pGametype, "s-ddr")) + HslaColor = ColorHSLA(1.0f, 1.0f, 0.7f); + else + HslaColor = ColorHSLA(1.0f, 1.0f, 1.0f); + return color_cast(HslaColor); +} + +void CMenus::RenderServerbrowserServerList(CUIRect View, bool &WasListboxItemActivated) +{ + static CListBox s_ListBox; + CUIRect Headers; - CUIRect Status; - View.HSplitTop(ms_ListheaderHeight, &Headers, &View); - View.HSplitBottom(65.0f, &View, &Status); + Headers.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_T, 5.0f); + Headers.VSplitRight(s_ListBox.ScrollbarWidthMax(), &Headers, nullptr); + View.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_NONE, 0.0f); - // split of the scrollbar - Headers.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_T, 5.0f); - Headers.VSplitRight(20.0f, &Headers, 0); - - struct CColumn + struct SColumn { int m_ID; int m_Sort; @@ -74,7 +97,6 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) int m_Direction; float m_Width; CUIRect m_Rect; - CUIRect m_Spacer; }; enum @@ -87,23 +109,39 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) COL_MAP, COL_PLAYERS, COL_PING, - COL_VERSION, + + UI_ELEM_LOCK_ICON = 0, + UI_ELEM_FAVORITE_ICON, + UI_ELEM_OFFICIAL_ICON_1, + UI_ELEM_OFFICIAL_ICON_2, + UI_ELEM_NAME_1, + UI_ELEM_NAME_2, + UI_ELEM_NAME_3, + UI_ELEM_GAMETYPE, + UI_ELEM_MAP_1, + UI_ELEM_MAP_2, + UI_ELEM_MAP_3, + UI_ELEM_FINISH_ICON, + UI_ELEM_PLAYERS, + UI_ELEM_FRIEND_ICON, + UI_ELEM_PING, + NUM_UI_ELEMS, }; - static CColumn s_aCols[] = { - {-1, -1, "", -1, 2.0f, {0}, {0}}, - {COL_FLAG_LOCK, -1, "", -1, 14.0f, {0}, {0}}, - {COL_FLAG_FAV, -1, "", -1, 14.0f, {0}, {0}}, - {COL_FLAG_OFFICIAL, -1, "", -1, 14.0f, {0}, {0}}, - {COL_NAME, IServerBrowser::SORT_NAME, Localizable("Name"), 0, 50.0f, {0}, {0}}, - {COL_GAMETYPE, IServerBrowser::SORT_GAMETYPE, Localizable("Type"), 1, 50.0f, {0}, {0}}, - {COL_MAP, IServerBrowser::SORT_MAP, Localizable("Map"), 1, 120.0f + (Headers.w - 480) / 8, {0}, {0}}, - {COL_PLAYERS, IServerBrowser::SORT_NUMPLAYERS, Localizable("Players"), 1, 85.0f, {0}, {0}}, - {-1, -1, "", 1, 10.0f, {0}, {0}}, - {COL_PING, IServerBrowser::SORT_PING, Localizable("Ping"), 1, 40.0f, {0}, {0}}, + static SColumn s_aCols[] = { + {-1, -1, "", -1, 2.0f, {0}}, + {COL_FLAG_LOCK, -1, "", -1, 14.0f, {0}}, + {COL_FLAG_FAV, -1, "", -1, 14.0f, {0}}, + {COL_FLAG_OFFICIAL, -1, "", -1, 14.0f, {0}}, + {COL_NAME, IServerBrowser::SORT_NAME, Localizable("Name"), 0, 50.0f, {0}}, + {COL_GAMETYPE, IServerBrowser::SORT_GAMETYPE, Localizable("Type"), 1, 50.0f, {0}}, + {COL_MAP, IServerBrowser::SORT_MAP, Localizable("Map"), 1, 120.0f + (Headers.w - 480) / 8, {0}}, + {COL_PLAYERS, IServerBrowser::SORT_NUMPLAYERS, Localizable("Players"), 1, 85.0f, {0}}, + {-1, -1, "", 1, 10.0f, {0}}, + {COL_PING, IServerBrowser::SORT_PING, Localizable("Ping"), 1, 40.0f, {0}}, }; - int NumCols = std::size(s_aCols); + const int NumCols = std::size(s_aCols); // do layout for(int i = 0; i < NumCols; i++) @@ -114,7 +152,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) if(i + 1 < NumCols) { - Headers.VSplitLeft(2, &s_aCols[i].m_Spacer, &Headers); + Headers.VSplitLeft(2.0f, nullptr, &Headers); } } } @@ -124,67 +162,51 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) if(s_aCols[i].m_Direction == 1) { Headers.VSplitRight(s_aCols[i].m_Width, &Headers, &s_aCols[i].m_Rect); - Headers.VSplitRight(2, &Headers, &s_aCols[i].m_Spacer); + Headers.VSplitRight(2.0f, &Headers, nullptr); } } - for(int i = 0; i < NumCols; i++) + for(auto &Col : s_aCols) { - if(s_aCols[i].m_Direction == 0) - s_aCols[i].m_Rect = Headers; + if(Col.m_Direction == 0) + Col.m_Rect = Headers; } const bool PlayersOrPing = (g_Config.m_BrSort == IServerBrowser::SORT_NUMPLAYERS || g_Config.m_BrSort == IServerBrowser::SORT_PING); // do headers - for(int i = 0; i < NumCols; i++) + for(const auto &Col : s_aCols) { - int Checked = g_Config.m_BrSort == s_aCols[i].m_Sort; - if(PlayersOrPing && g_Config.m_BrSortOrder == 2 && (s_aCols[i].m_Sort == IServerBrowser::SORT_NUMPLAYERS || s_aCols[i].m_Sort == IServerBrowser::SORT_PING)) + int Checked = g_Config.m_BrSort == Col.m_Sort; + if(PlayersOrPing && g_Config.m_BrSortOrder == 2 && (Col.m_Sort == IServerBrowser::SORT_NUMPLAYERS || Col.m_Sort == IServerBrowser::SORT_PING)) Checked = 2; - if(DoButton_GridHeader(&s_aCols[i].m_ID, Localize(s_aCols[i].m_pCaption), Checked, &s_aCols[i].m_Rect)) + if(DoButton_GridHeader(&Col.m_ID, Localize(Col.m_pCaption), Checked, &Col.m_Rect)) { - if(s_aCols[i].m_Sort != -1) + if(Col.m_Sort != -1) { - if(g_Config.m_BrSort == s_aCols[i].m_Sort) - { - if(PlayersOrPing) - g_Config.m_BrSortOrder = (g_Config.m_BrSortOrder + 1) % 3; - else - g_Config.m_BrSortOrder = (g_Config.m_BrSortOrder + 1) % 2; - } + if(g_Config.m_BrSort == Col.m_Sort) + g_Config.m_BrSortOrder = (g_Config.m_BrSortOrder + 1) % (PlayersOrPing ? 3 : 2); else g_Config.m_BrSortOrder = 0; - g_Config.m_BrSort = s_aCols[i].m_Sort; + g_Config.m_BrSort = Col.m_Sort; } } } - View.Draw(ColorRGBA(0, 0, 0, 0.15f), 0, 0); - - int NumServers = ServerBrowser()->NumSortedServers(); + const int NumServers = ServerBrowser()->NumSortedServers(); // display important messages in the middle of the screen so no // users misses it { - CUIRect MsgBox = View; - if(!ServerBrowser()->NumServers() && ServerBrowser()->IsGettingServerlist()) - UI()->DoLabel(&MsgBox, Localize("Getting server list from master server"), 16.0f, TEXTALIGN_MC); + UI()->DoLabel(&View, Localize("Getting server list from master server"), 16.0f, TEXTALIGN_MC); else if(!ServerBrowser()->NumServers()) - UI()->DoLabel(&MsgBox, Localize("No servers found"), 16.0f, TEXTALIGN_MC); + UI()->DoLabel(&View, Localize("No servers found"), 16.0f, TEXTALIGN_MC); else if(ServerBrowser()->NumServers() && !NumServers) - UI()->DoLabel(&MsgBox, Localize("No servers match your filter criteria"), 16.0f, TEXTALIGN_MC); + UI()->DoLabel(&View, Localize("No servers match your filter criteria"), 16.0f, TEXTALIGN_MC); } - if(!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_TAB)) - { - const int Direction = Input()->ShiftIsPressed() ? -1 : 1; - g_Config.m_UiToolboxPage = (g_Config.m_UiToolboxPage + 3 + Direction) % 3; - } - - static CListBox s_ListBox; s_ListBox.SetActive(!UI()->IsPopupOpen()); s_ListBox.DoStart(ms_ListheaderHeight, NumServers, 1, 3, -1, &View, false); @@ -195,10 +217,8 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } m_SelectedIndex = -1; - auto RenderBrowserIcons = [this](CUIElement::SUIElementRect &UIRect, CUIRect *pRect, const ColorRGBA &TextColor, const ColorRGBA &TextOutlineColor, const char *pText, int TextAlign, bool SmallFont = false) { - float FontSize = 14.0f; - if(SmallFont) - FontSize = 6.0f; + const auto &&RenderBrowserIcons = [this](CUIElement::SUIElementRect &UIRect, CUIRect *pRect, const ColorRGBA &TextColor, const ColorRGBA &TextOutlineColor, const char *pText, int TextAlign, bool SmallFont = false) { + const float FontSize = SmallFont ? 6.0f : 14.0f; TextRender()->SetFontPreset(EFontPreset::ICON_FONT); 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); TextRender()->TextColor(TextColor); @@ -210,16 +230,13 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); }; - int NumPlayers = 0; for(int i = 0; i < NumServers; i++) { const CServerInfo *pItem = ServerBrowser()->SortedGet(i); - NumPlayers += pItem->m_NumFilteredPlayers; if(pItem->m_pUIElement == nullptr) { - const int UIRectCount = 2 + (COL_VERSION + 1) * 3; - pItem->m_pUIElement = UI()->GetNewUIElement(UIRectCount); + pItem->m_pUIElement = UI()->GetNewUIElement(NUM_UI_ELEMS); } const CListboxItem ListItem = s_ListBox.DoNextItem(pItem, str_comp(pItem->m_aAddress, g_Config.m_UiServerAddress) == 0); @@ -250,37 +267,36 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } const float FontSize = 12.0f; - for(int c = 0; c < NumCols; c++) + char aTemp[64]; + for(const auto &Col : s_aCols) { CUIRect Button; - char aTemp[64]; - Button.x = s_aCols[c].m_Rect.x; + Button.x = Col.m_Rect.x; Button.y = ListItem.m_Rect.y; Button.h = ListItem.m_Rect.h; - Button.w = s_aCols[c].m_Rect.w; - - int ID = s_aCols[c].m_ID; + Button.w = Col.m_Rect.w; + const int ID = Col.m_ID; if(ID == COL_FLAG_LOCK) { if(pItem->m_Flags & SERVER_FLAG_PASSWORD) { - RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColFlagLock + 0), &Button, {0.75f, 0.75f, 0.75f, 1}, TextRender()->DefaultTextOutlineColor(), FONT_ICON_LOCK, TEXTALIGN_MC); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_LOCK_ICON), &Button, ColorRGBA(0.75f, 0.75f, 0.75f, 1.0f), TextRender()->DefaultTextOutlineColor(), FONT_ICON_LOCK, TEXTALIGN_MC); } } else if(ID == COL_FLAG_FAV) { if(pItem->m_Favorite != TRISTATE::NONE) { - RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColFav + 0), &Button, {0.94f, 0.4f, 0.4f, 1}, TextRender()->DefaultTextOutlineColor(), FONT_ICON_HEART, TEXTALIGN_MC); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_FAVORITE_ICON), &Button, ColorRGBA(0.94f, 0.4f, 0.4f, 1.0f), TextRender()->DefaultTextOutlineColor(), FONT_ICON_HEART, TEXTALIGN_MC); } } else if(ID == COL_FLAG_OFFICIAL) { if(pItem->m_Official && g_Config.m_UiPage != PAGE_DDNET && g_Config.m_UiPage != PAGE_KOG) { - RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColOff + 0), &Button, {0.4f, 0.7f, 0.94f, 1}, {0.0f, 0.0f, 0.0f, 1.0f}, FONT_ICON_CERTIFICATE, TEXTALIGN_MC); - RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColOff + 1), &Button, {0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, FONT_ICON_CHECK, TEXTALIGN_MC, true); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_OFFICIAL_ICON_1), &Button, ColorRGBA(0.4f, 0.7f, 0.94f, 1.0f), ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f), FONT_ICON_CERTIFICATE, TEXTALIGN_MC); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_OFFICIAL_ICON_2), &Button, ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f), ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f), FONT_ICON_CHECK, TEXTALIGN_MC, true); } } else if(ID == COL_NAME) @@ -291,90 +307,15 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) Props.m_EnableWidthCheck = false; bool Printed = false; if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_SERVERNAME)) - Printed = PrintHighlighted(pItem->m_aName, [this, pItem, FontSize, Props, &Button](const char *pFilteredStr, const int FilterLen) { - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 0), &Button, pItem->m_aName, FontSize, TEXTALIGN_ML, Props, (int)(pFilteredStr - pItem->m_aName)); - TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 1), &Button, pFilteredStr, FontSize, TEXTALIGN_ML, Props, FilterLen, &pItem->m_pUIElement->Rect(gs_OffsetColName + 0)->m_Cursor); - TextRender()->TextColor(1, 1, 1, 1); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 2), &Button, pFilteredStr + FilterLen, FontSize, TEXTALIGN_ML, Props, -1, &pItem->m_pUIElement->Rect(gs_OffsetColName + 1)->m_Cursor); + Printed = PrintHighlighted(pItem->m_aName, [&](const char *pFilteredStr, const int FilterLen) { + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_NAME_1), &Button, pItem->m_aName, FontSize, TEXTALIGN_ML, Props, (int)(pFilteredStr - pItem->m_aName)); + TextRender()->TextColor(gs_HighlightedTextColor); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_NAME_2), &Button, pFilteredStr, FontSize, TEXTALIGN_ML, Props, FilterLen, &pItem->m_pUIElement->Rect(UI_ELEM_NAME_1)->m_Cursor); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_NAME_3), &Button, pFilteredStr + FilterLen, FontSize, TEXTALIGN_ML, Props, -1, &pItem->m_pUIElement->Rect(UI_ELEM_NAME_2)->m_Cursor); }); if(!Printed) - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName), &Button, pItem->m_aName, FontSize, TEXTALIGN_ML, Props); - } - else if(ID == COL_MAP) - { - if(g_Config.m_UiPage == PAGE_DDNET) - { - CUIRect Icon; - Button.VMargin(4.0f, &Button); - Button.VSplitLeft(Button.h, &Icon, &Button); - Icon.Margin(2.0f, &Icon); - - if(g_Config.m_BrIndicateFinished && pItem->m_HasRank == 1) - { - RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColFlagLock + 1), &Icon, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor(), FONT_ICON_FLAG_CHECKERED, TEXTALIGN_MC); - } - } - - SLabelProperties Props; - Props.m_MaxWidth = Button.w; - Props.m_StopAtEnd = true; - Props.m_EnableWidthCheck = false; - bool Printed = false; - if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_MAPNAME)) - Printed = PrintHighlighted(pItem->m_aMap, [this, pItem, FontSize, Props, &Button](const char *pFilteredStr, const int FilterLen) { - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 0), &Button, pItem->m_aMap, FontSize, TEXTALIGN_ML, Props, (int)(pFilteredStr - pItem->m_aMap)); - TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 1), &Button, pFilteredStr, FontSize, TEXTALIGN_ML, Props, FilterLen, &pItem->m_pUIElement->Rect(gs_OffsetColMap + 0)->m_Cursor); - TextRender()->TextColor(1, 1, 1, 1); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 2), &Button, pFilteredStr + FilterLen, FontSize, TEXTALIGN_ML, Props, -1, &pItem->m_pUIElement->Rect(gs_OffsetColMap + 1)->m_Cursor); - }); - if(!Printed) - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap), &Button, pItem->m_aMap, FontSize, TEXTALIGN_ML, Props); - } - else if(ID == COL_PLAYERS) - { - CUIRect Icon, IconText; - Button.VMargin(2.0f, &Button); - if(pItem->m_FriendState != IFriends::FRIEND_NO) - { - Button.VSplitRight(50.0f, &Icon, &Button); - Icon.Margin(2.0f, &Icon); - Icon.HSplitBottom(6.0f, 0, &IconText); - RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColFav + 1), &Icon, {0.94f, 0.4f, 0.4f, 1}, TextRender()->DefaultTextOutlineColor(), FONT_ICON_HEART, TEXTALIGN_MC); - if(FriendsOnServer > 1) - { - char aBufFriendsOnServer[64]; - str_from_int(FriendsOnServer, aBufFriendsOnServer); - TextRender()->TextColor(0.94f, 0.8f, 0.8f, 1); - UI()->DoLabel(&IconText, aBufFriendsOnServer, 10.0f, TEXTALIGN_MC); - TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1); - } - } - - str_format(aTemp, sizeof(aTemp), "%i/%i", pItem->m_NumFilteredPlayers, ServerBrowser()->Max(*pItem)); - if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_PLAYER)) - TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColPlayers), &Button, aTemp, FontSize, TEXTALIGN_MR); - TextRender()->TextColor(1, 1, 1, 1); - } - else if(ID == COL_PING) - { - Button.VMargin(4.0f, &Button); - FormatServerbrowserPing(aTemp, sizeof(aTemp), pItem); - if(g_Config.m_UiColorizePing) - { - ColorRGBA rgb = color_cast(ColorHSLA((300.0f - clamp(pItem->m_Latency, 0, 300)) / 1000.0f, 1.0f, 0.5f)); - TextRender()->TextColor(rgb); - } - - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColPing), &Button, aTemp, FontSize, TEXTALIGN_MR); - TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); - } - else if(ID == COL_VERSION) - { - const char *pVersion = pItem->m_aVersion; - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColVersion), &Button, pVersion, FontSize, TEXTALIGN_MR); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_NAME_1), &Button, pItem->m_aName, FontSize, TEXTALIGN_ML, Props); } else if(ID == COL_GAMETYPE) { @@ -384,36 +325,77 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) Props.m_EnableWidthCheck = false; if(g_Config.m_UiColorizeGametype) { - ColorHSLA hsl = ColorHSLA(1.0f, 1.0f, 1.0f); - - if(str_comp(pItem->m_aGameType, "DM") == 0 || str_comp(pItem->m_aGameType, "TDM") == 0 || str_comp(pItem->m_aGameType, "CTF") == 0) - hsl = ColorHSLA(0.33f, 1.0f, 0.75f); - else if(str_find_nocase(pItem->m_aGameType, "catch")) - hsl = ColorHSLA(0.17f, 1.0f, 0.75f); - else if(str_find_nocase(pItem->m_aGameType, "idm") || str_find_nocase(pItem->m_aGameType, "itdm") || str_find_nocase(pItem->m_aGameType, "ictf") || str_find_nocase(pItem->m_aGameType, "f-ddrace")) - hsl = ColorHSLA(0.00f, 1.0f, 0.75f); - else if(str_find_nocase(pItem->m_aGameType, "fng")) - hsl = ColorHSLA(0.83f, 1.0f, 0.75f); - else if(str_find_nocase(pItem->m_aGameType, "gores")) - hsl = ColorHSLA(0.525f, 1.0f, 0.75f); - else if(str_find_nocase(pItem->m_aGameType, "BW")) - hsl = ColorHSLA(0.050f, 1.0f, 0.75f); - else if(str_find_nocase(pItem->m_aGameType, "ddracenet") || str_find_nocase(pItem->m_aGameType, "ddnet") || str_find_nocase(pItem->m_aGameType, "0xf")) - hsl = ColorHSLA(0.58f, 1.0f, 0.75f); - else if(str_find_nocase(pItem->m_aGameType, "ddrace") || str_find_nocase(pItem->m_aGameType, "mkrace")) - hsl = ColorHSLA(0.75f, 1.0f, 0.75f); - else if(str_find_nocase(pItem->m_aGameType, "race") || str_find_nocase(pItem->m_aGameType, "fastcap")) - hsl = ColorHSLA(0.46f, 1.0f, 0.75f); - else if(str_find_nocase(pItem->m_aGameType, "s-ddr")) - hsl = ColorHSLA(1.0f, 1.0f, 0.70f); - - ColorRGBA rgb = color_cast(hsl); - TextRender()->TextColor(rgb); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColGameType), &Button, pItem->m_aGameType, FontSize, TEXTALIGN_ML, Props); - TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + TextRender()->TextColor(GetGametypeTextColor(pItem->m_aGameType)); } - else - UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColGameType), &Button, pItem->m_aGameType, FontSize, TEXTALIGN_ML, Props); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_GAMETYPE), &Button, pItem->m_aGameType, FontSize, TEXTALIGN_ML, Props); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + } + else if(ID == COL_MAP) + { + if(g_Config.m_UiPage == PAGE_DDNET) + { + CUIRect Icon; + Button.VMargin(4.0f, &Button); + Button.VSplitLeft(Button.h, &Icon, &Button); + if(g_Config.m_BrIndicateFinished && pItem->m_HasRank == 1) + { + Icon.Margin(2.0f, &Icon); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_FINISH_ICON), &Icon, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor(), FONT_ICON_FLAG_CHECKERED, TEXTALIGN_MC); + } + } + + SLabelProperties Props; + Props.m_MaxWidth = Button.w; + Props.m_StopAtEnd = true; + Props.m_EnableWidthCheck = false; + bool Printed = false; + if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_MAPNAME)) + Printed = PrintHighlighted(pItem->m_aMap, [&](const char *pFilteredStr, const int FilterLen) { + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_MAP_1), &Button, pItem->m_aMap, FontSize, TEXTALIGN_ML, Props, (int)(pFilteredStr - pItem->m_aMap)); + TextRender()->TextColor(gs_HighlightedTextColor); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_MAP_2), &Button, pFilteredStr, FontSize, TEXTALIGN_ML, Props, FilterLen, &pItem->m_pUIElement->Rect(UI_ELEM_MAP_1)->m_Cursor); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_MAP_3), &Button, pFilteredStr + FilterLen, FontSize, TEXTALIGN_ML, Props, -1, &pItem->m_pUIElement->Rect(UI_ELEM_MAP_2)->m_Cursor); + }); + if(!Printed) + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_MAP_1), &Button, pItem->m_aMap, FontSize, TEXTALIGN_ML, Props); + } + else if(ID == COL_PLAYERS) + { + Button.VMargin(2.0f, &Button); + if(pItem->m_FriendState != IFriends::FRIEND_NO) + { + CUIRect Icon; + Button.VSplitRight(50.0f, &Icon, &Button); + Icon.Margin(2.0f, &Icon); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_FRIEND_ICON), &Icon, ColorRGBA(0.94f, 0.4f, 0.4f, 1.0f), TextRender()->DefaultTextOutlineColor(), FONT_ICON_HEART, TEXTALIGN_MC); + if(FriendsOnServer > 1) + { + str_from_int(FriendsOnServer, aTemp); + TextRender()->TextColor(0.94f, 0.8f, 0.8f, 1.0f); + UI()->DoLabel(&Icon, aTemp, 9.0f, TEXTALIGN_MC); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + } + } + + str_format(aTemp, sizeof(aTemp), "%i/%i", pItem->m_NumFilteredPlayers, ServerBrowser()->Max(*pItem)); + if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_PLAYER)) + { + TextRender()->TextColor(gs_HighlightedTextColor); + } + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_PLAYERS), &Button, aTemp, FontSize, TEXTALIGN_MR); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + } + else if(ID == COL_PING) + { + Button.VMargin(4.0f, &Button); + FormatServerbrowserPing(aTemp, pItem); + if(g_Config.m_UiColorizePing) + { + TextRender()->TextColor(GetPingTextColor(pItem->m_Latency)); + } + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_PING), &Button, aTemp, FontSize, TEXTALIGN_MR); + TextRender()->TextColor(TextRender()->DefaultTextColor()); } } } @@ -434,10 +416,15 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } } + WasListboxItemActivated = s_ListBox.WasItemActivated(); +} + +void CMenus::RenderServerbrowserStatusBox(CUIRect StatusBox, bool WasListboxItemActivated) +{ // Render bar that shows the loading progression. // The bar is only shown while loading and fades out when it's done. CUIRect RefreshBar; - Status.HSplitTop(5.0f, &RefreshBar, &Status); + StatusBox.HSplitTop(5.0f, &RefreshBar, &StatusBox); static float s_LoadingProgressionFadeEnd = 0.0f; if(ServerBrowser()->IsRefreshing() && ServerBrowser()->LoadingProgression() < 100) { @@ -452,45 +439,43 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) RefreshBar.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, RefreshBarAlpha), IGraphics::CORNER_NONE, 0.0f); } - CUIRect SearchInfoAndAddr, ServersAndConnect, Status3; - Status.VSplitRight(125.0f, &SearchInfoAndAddr, &ServersAndConnect); + TextRender()->SetFontPreset(EFontPreset::ICON_FONT); + 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); + const float SearchExcludeAddrStrMax = 130.0f; + const float SearchIconWidth = TextRender()->TextWidth(16.0f, FONT_ICON_MAGNIFYING_GLASS); + const float ExcludeIconWidth = TextRender()->TextWidth(16.0f, FONT_ICON_BAN); + const float ExcludeSearchIconMax = maximum(SearchIconWidth, ExcludeIconWidth); + TextRender()->SetRenderFlags(0); + TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); + + CUIRect SearchInfoAndAddr, ServersAndConnect, ServersPlayersOnline, SearchAndInfo, ServerAddr, ConnectButtons; + StatusBox.VSplitRight(135.0f, &SearchInfoAndAddr, &ServersAndConnect); if(SearchInfoAndAddr.w > 350.0f) - SearchInfoAndAddr.VSplitLeft(350.0f, &SearchInfoAndAddr, NULL); - CUIRect SearchAndInfo, ServerAddr, ConnectButtons; + SearchInfoAndAddr.VSplitLeft(350.0f, &SearchInfoAndAddr, nullptr); SearchInfoAndAddr.HSplitTop(40.0f, &SearchAndInfo, &ServerAddr); - ServersAndConnect.HSplitTop(35.0f, &Status3, &ConnectButtons); + ServersAndConnect.HSplitTop(35.0f, &ServersPlayersOnline, &ConnectButtons); ConnectButtons.HSplitTop(5.0f, nullptr, &ConnectButtons); + CUIRect QuickSearch, QuickExclude; - - SearchAndInfo.HSplitTop(20.f, &QuickSearch, &QuickExclude); - QuickSearch.Margin(2.f, &QuickSearch); - QuickExclude.Margin(2.f, &QuickExclude); - - float SearchExcludeAddrStrMax = 130.0f; - - float SearchIconWidth = 0; - float ExcludeIconWidth = 0; - float ExcludeSearchIconMax = 0; + SearchAndInfo.HSplitTop(20.0f, &QuickSearch, &QuickExclude); + QuickSearch.Margin(2.0f, &QuickSearch); + QuickExclude.Margin(2.0f, &QuickExclude); // render quick search { TextRender()->SetFontPreset(EFontPreset::ICON_FONT); 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); - UI()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 16.0f, TEXTALIGN_ML); - SearchIconWidth = TextRender()->TextWidth(16.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f); - ExcludeIconWidth = TextRender()->TextWidth(16.0f, FONT_ICON_BAN, -1, -1.0f); - ExcludeSearchIconMax = maximum(SearchIconWidth, ExcludeIconWidth); TextRender()->SetRenderFlags(0); TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); - QuickSearch.VSplitLeft(ExcludeSearchIconMax, 0, &QuickSearch); - QuickSearch.VSplitLeft(5.0f, 0, &QuickSearch); + QuickSearch.VSplitLeft(ExcludeSearchIconMax, nullptr, &QuickSearch); + QuickSearch.VSplitLeft(5.0f, nullptr, &QuickSearch); char aBufSearch[64]; str_format(aBufSearch, sizeof(aBufSearch), "%s:", Localize("Search")); UI()->DoLabel(&QuickSearch, aBufSearch, 14.0f, TEXTALIGN_ML); - QuickSearch.VSplitLeft(SearchExcludeAddrStrMax, 0, &QuickSearch); - QuickSearch.VSplitLeft(5.0f, 0, &QuickSearch); + QuickSearch.VSplitLeft(SearchExcludeAddrStrMax, nullptr, &QuickSearch); + QuickSearch.VSplitLeft(5.0f, nullptr, &QuickSearch); static CLineInput s_FilterInput(g_Config.m_BrFilterString, sizeof(g_Config.m_BrFilterString)); if(!UI()->IsPopupOpen() && Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed()) @@ -506,64 +491,72 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) { TextRender()->SetFontPreset(EFontPreset::ICON_FONT); 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); - UI()->DoLabel(&QuickExclude, FONT_ICON_BAN, 16.0f, TEXTALIGN_ML); TextRender()->SetRenderFlags(0); TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); - QuickExclude.VSplitLeft(ExcludeSearchIconMax, 0, &QuickExclude); - QuickExclude.VSplitLeft(5.0f, 0, &QuickExclude); + QuickExclude.VSplitLeft(ExcludeSearchIconMax, nullptr, &QuickExclude); + QuickExclude.VSplitLeft(5.0f, nullptr, &QuickExclude); char aBufExclude[64]; str_format(aBufExclude, sizeof(aBufExclude), "%s:", Localize("Exclude")); UI()->DoLabel(&QuickExclude, aBufExclude, 14.0f, TEXTALIGN_ML); - QuickExclude.VSplitLeft(SearchExcludeAddrStrMax, 0, &QuickExclude); - QuickExclude.VSplitLeft(5.0f, 0, &QuickExclude); + QuickExclude.VSplitLeft(SearchExcludeAddrStrMax, nullptr, &QuickExclude); + QuickExclude.VSplitLeft(5.0f, nullptr, &QuickExclude); static CLineInput s_ExcludeInput(g_Config.m_BrExcludeString, sizeof(g_Config.m_BrExcludeString)); if(!UI()->IsPopupOpen() && Input()->KeyPress(KEY_X) && Input()->ShiftIsPressed() && Input()->ModifierIsPressed()) + { UI()->SetActiveItem(&s_ExcludeInput); + s_ExcludeInput.SelectAll(); + } if(UI()->DoClearableEditBox(&s_ExcludeInput, &QuickExclude, 12.0f)) Client()->ServerBrowserUpdate(); } // render status - char aBufSvr[128]; - char aBufPyr[128]; - if(ServerBrowser()->NumServers() != 1) - str_format(aBufSvr, sizeof(aBufSvr), Localize("%d of %d servers"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers()); - else - str_format(aBufSvr, sizeof(aBufSvr), Localize("%d of %d server"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers()); - if(NumPlayers != 1) - str_format(aBufPyr, sizeof(aBufPyr), Localize("%d players"), NumPlayers); - else - str_format(aBufPyr, sizeof(aBufPyr), Localize("%d player"), NumPlayers); - - CUIRect SvrsOnline, PlysOnline; - Status3.HSplitTop(20.f, &PlysOnline, &SvrsOnline); - PlysOnline.VSplitRight(TextRender()->TextWidth(12.0f, aBufPyr, -1, -1.0f), 0, &PlysOnline); - UI()->DoLabel(&PlysOnline, aBufPyr, 12.0f, TEXTALIGN_ML); - SvrsOnline.VSplitRight(TextRender()->TextWidth(12.0f, aBufSvr, -1, -1.0f), 0, &SvrsOnline); - UI()->DoLabel(&SvrsOnline, aBufSvr, 12.0f, TEXTALIGN_ML); - - // status box { + CUIRect ServersOnline, PlayersOnline; + ServersPlayersOnline.HSplitMid(&PlayersOnline, &ServersOnline); + + char aBuf[128]; + if(ServerBrowser()->NumServers() != 1) + str_format(aBuf, sizeof(aBuf), Localize("%d of %d servers"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers()); + else + str_format(aBuf, sizeof(aBuf), Localize("%d of %d server"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers()); + UI()->DoLabel(&ServersOnline, aBuf, 12.0f, TEXTALIGN_MR); + + int NumPlayers = 0; + for(int i = 0; i < ServerBrowser()->NumSortedServers(); i++) + NumPlayers += ServerBrowser()->SortedGet(i)->m_NumFilteredPlayers; + + if(NumPlayers != 1) + str_format(aBuf, sizeof(aBuf), Localize("%d players"), NumPlayers); + else + str_format(aBuf, sizeof(aBuf), Localize("%d player"), NumPlayers); + UI()->DoLabel(&PlayersOnline, aBuf, 12.0f, TEXTALIGN_MR); + } + + // address info + { + CUIRect ServerAddrLabel, ServerAddrEditBox; ServerAddr.Margin(2.0f, &ServerAddr); + ServerAddr.VSplitLeft(SearchExcludeAddrStrMax + 5.0f + ExcludeSearchIconMax + 5.0f, &ServerAddrLabel, &ServerAddrEditBox); - // address info - UI()->DoLabel(&ServerAddr, Localize("Server address:"), 14.0f, TEXTALIGN_ML); - ServerAddr.VSplitLeft(SearchExcludeAddrStrMax + 5.0f + ExcludeSearchIconMax + 5.0f, NULL, &ServerAddr); + UI()->DoLabel(&ServerAddrLabel, Localize("Server address:"), 14.0f, TEXTALIGN_ML); static CLineInput s_ServerAddressInput(g_Config.m_UiServerAddress, sizeof(g_Config.m_UiServerAddress)); - if(UI()->DoClearableEditBox(&s_ServerAddressInput, &ServerAddr, 12.0f)) + if(UI()->DoClearableEditBox(&s_ServerAddressInput, &ServerAddrEditBox, 12.0f)) m_ServerBrowserShouldRevealSelection = true; + } - // button area + // buttons + { CUIRect ButtonRefresh, ButtonConnect; ConnectButtons.VSplitMid(&ButtonRefresh, &ButtonConnect, 5.0f); // refresh button { char aLabelBuf[32] = {0}; - const auto RefreshLabelFunc = [this, aLabelBuf]() mutable { + const auto &&RefreshLabelFunc = [this, aLabelBuf]() mutable { if(ServerBrowser()->IsRefreshing() || ServerBrowser()->IsGettingServerlist()) str_format(aLabelBuf, sizeof(aLabelBuf), "%s%s", FONT_ICON_ARROW_ROTATE_RIGHT, FONT_ICON_ELLIPSIS); else @@ -584,14 +577,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) // connect button { - const auto ConnectLabelFunc = []() { return FONT_ICON_RIGHT_TO_BRACKET; }; + const auto &&ConnectLabelFunc = []() { return FONT_ICON_RIGHT_TO_BRACKET; }; SMenuButtonProperties Props; Props.m_UseIconFont = true; Props.m_Color = ColorRGBA(0.5f, 1.0f, 0.5f, 0.5f); static CButtonContainer s_ConnectButton; - if(UI()->DoButton_Menu(m_ConnectButton, &s_ConnectButton, ConnectLabelFunc, &ButtonConnect, Props) || s_ListBox.WasItemActivated() || (!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))) + if(UI()->DoButton_Menu(m_ConnectButton, &s_ConnectButton, ConnectLabelFunc, &ButtonConnect, Props) || WasListboxItemActivated || (!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))) { Connect(g_Config.m_UiServerAddress); } @@ -617,115 +610,100 @@ void CMenus::PopupConfirmSwitchServer() void CMenus::RenderServerbrowserFilters(CUIRect View) { - CUIRect ServerFilter = View, FilterHeader; - const float FontSize = 12.0f; + const float RowHeight = 18.0f; + const float FontSize = (RowHeight - 4.0f) * CUI::ms_FontmodHeight; // based on DoButton_CheckBox - // server filter - ServerFilter.HSplitTop(ms_ListheaderHeight, &FilterHeader, &ServerFilter); - FilterHeader.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_T, 4.0f); + View.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_B, 4.0f); + View.Margin(5.0f, &View); - ServerFilter.Draw(ColorRGBA(0, 0, 0, 0.15f), IGraphics::CORNER_B, 4.0f); - UI()->DoLabel(&FilterHeader, Localize("Server filter"), FontSize + 2.0f, TEXTALIGN_MC); - CUIRect Button, Button2; + CUIRect Button, ResetButton; + View.HSplitBottom(RowHeight, &View, &ResetButton); + View.HSplitBottom(3.0f, &View, nullptr); - ServerFilter.Margin(3.0f, &ServerFilter); - ServerFilter.VMargin(5.0f, &ServerFilter); - - ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + View.HSplitTop(RowHeight, &Button, &View); if(DoButton_CheckBox(&g_Config.m_BrFilterEmpty, Localize("Has people playing"), g_Config.m_BrFilterEmpty, &Button)) g_Config.m_BrFilterEmpty ^= 1; - ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + View.HSplitTop(RowHeight, &Button, &View); if(DoButton_CheckBox(&g_Config.m_BrFilterSpectators, Localize("Count players only"), g_Config.m_BrFilterSpectators, &Button)) g_Config.m_BrFilterSpectators ^= 1; - ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + View.HSplitTop(RowHeight, &Button, &View); if(DoButton_CheckBox(&g_Config.m_BrFilterFull, Localize("Server not full"), g_Config.m_BrFilterFull, &Button)) g_Config.m_BrFilterFull ^= 1; - ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + View.HSplitTop(RowHeight, &Button, &View); if(DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends only"), g_Config.m_BrFilterFriends, &Button)) g_Config.m_BrFilterFriends ^= 1; - ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + View.HSplitTop(RowHeight, &Button, &View); if(DoButton_CheckBox(&g_Config.m_BrFilterPw, Localize("No password"), g_Config.m_BrFilterPw, &Button)) g_Config.m_BrFilterPw ^= 1; - ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + View.HSplitTop(RowHeight, &Button, &View); if(DoButton_CheckBox(&g_Config.m_BrFilterGametypeStrict, Localize("Strict gametype filter"), g_Config.m_BrFilterGametypeStrict, &Button)) g_Config.m_BrFilterGametypeStrict ^= 1; - ServerFilter.HSplitTop(5.0f, 0, &ServerFilter); - - ServerFilter.HSplitTop(19.0f, &Button, &ServerFilter); + View.HSplitTop(3.0f, nullptr, &View); + View.HSplitTop(RowHeight, &Button, &View); UI()->DoLabel(&Button, Localize("Game types:"), FontSize, TEXTALIGN_ML); - Button.VSplitRight(60.0f, 0, &Button); - ServerFilter.HSplitTop(3.0f, 0, &ServerFilter); + Button.VSplitRight(60.0f, nullptr, &Button); static CLineInput s_GametypeInput(g_Config.m_BrFilterGametype, sizeof(g_Config.m_BrFilterGametype)); if(UI()->DoEditBox(&s_GametypeInput, &Button, FontSize)) Client()->ServerBrowserUpdate(); // server address - ServerFilter.HSplitTop(3.0f, 0, &ServerFilter); - ServerFilter.HSplitTop(19.0f, &Button, &ServerFilter); + View.HSplitTop(6.0f, nullptr, &View); + View.HSplitTop(RowHeight, &Button, &View); + View.HSplitTop(6.0f, nullptr, &View); UI()->DoLabel(&Button, Localize("Server address:"), FontSize, TEXTALIGN_ML); - Button.VSplitRight(60.0f, 0, &Button); + Button.VSplitRight(60.0f, nullptr, &Button); static CLineInput s_FilterServerAddressInput(g_Config.m_BrFilterServerAddress, sizeof(g_Config.m_BrFilterServerAddress)); if(UI()->DoEditBox(&s_FilterServerAddressInput, &Button, FontSize)) Client()->ServerBrowserUpdate(); // player country { - CUIRect Rect; - ServerFilter.HSplitTop(3.0f, nullptr, &ServerFilter); - ServerFilter.HSplitTop(26.0f, &Button, &ServerFilter); - Button.HMargin(3.0f, &Button); - Button.VSplitRight(60.0f, &Button, &Rect); + CUIRect Flag; + View.HSplitTop(RowHeight, &Button, &View); + Button.VSplitRight(60.0f, &Button, &Flag); if(DoButton_CheckBox(&g_Config.m_BrFilterCountry, Localize("Player country:"), g_Config.m_BrFilterCountry, &Button)) g_Config.m_BrFilterCountry ^= 1; - float OldWidth = Rect.w; - Rect.w = Rect.h * 2; - Rect.x += (OldWidth - Rect.w) / 2.0f; - m_pClient->m_CountryFlags.Render(g_Config.m_BrFilterCountryIndex, ColorRGBA(1.0f, 1.0f, 1.0f, UI()->MouseHovered(&Rect) ? 1.0f : g_Config.m_BrFilterCountry ? 0.9f : 0.5f), Rect.x, Rect.y, Rect.w, Rect.h); + const float OldWidth = Flag.w; + Flag.w = Flag.h * 2.0f; + Flag.x += (OldWidth - Flag.w) / 2.0f; + m_pClient->m_CountryFlags.Render(g_Config.m_BrFilterCountryIndex, ColorRGBA(1.0f, 1.0f, 1.0f, UI()->HotItem() == &g_Config.m_BrFilterCountryIndex ? 1.0f : g_Config.m_BrFilterCountry ? 0.9f : 0.5f), Flag.x, Flag.y, Flag.w, Flag.h); - if(UI()->DoButtonLogic(&g_Config.m_BrFilterCountryIndex, 0, &Rect)) + if(UI()->DoButtonLogic(&g_Config.m_BrFilterCountryIndex, 0, &Flag)) { static SPopupMenuId s_PopupCountryId; static SPopupCountrySelectionContext s_PopupCountryContext; s_PopupCountryContext.m_pMenus = this; s_PopupCountryContext.m_Selection = g_Config.m_BrFilterCountryIndex; s_PopupCountryContext.m_New = true; - UI()->DoPopupMenu(&s_PopupCountryId, Rect.x, Rect.y + Rect.h, 490, 210, &s_PopupCountryContext, PopupCountrySelection); + UI()->DoPopupMenu(&s_PopupCountryId, Flag.x, Flag.y + Flag.h, 490, 210, &s_PopupCountryContext, PopupCountrySelection); } } - ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + View.HSplitTop(RowHeight, &Button, &View); if(DoButton_CheckBox(&g_Config.m_BrFilterConnectingPlayers, Localize("Filter connecting players"), g_Config.m_BrFilterConnectingPlayers, &Button)) g_Config.m_BrFilterConnectingPlayers ^= 1; - CUIRect FilterTabs; - ServerFilter.HSplitBottom(23, &ServerFilter, &FilterTabs); - - CUIRect ResetButton; - - ServerFilter.HSplitBottom(ms_ButtonHeight - 5.0f, &ServerFilter, &ResetButton); - // ddnet country filters if(g_Config.m_UiPage == PAGE_DDNET) { - ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + View.HSplitTop(RowHeight, &Button, &View); if(DoButton_CheckBox(&g_Config.m_BrIndicateFinished, Localize("Indicate map finish"), g_Config.m_BrIndicateFinished, &Button)) { g_Config.m_BrIndicateFinished ^= 1; - if(g_Config.m_BrIndicateFinished) ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType()); } if(g_Config.m_BrIndicateFinished) { - ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + View.HSplitTop(RowHeight, &Button, &View); if(DoButton_CheckBox(&g_Config.m_BrFilterUnfinishedMap, Localize("Unfinished map"), g_Config.m_BrFilterUnfinishedMap, &Button)) g_Config.m_BrFilterUnfinishedMap ^= 1; } @@ -737,245 +715,52 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) if(g_Config.m_UiPage == PAGE_DDNET || g_Config.m_UiPage == PAGE_KOG) { - int Network = g_Config.m_UiPage == PAGE_DDNET ? IServerBrowser::NETWORK_DDNET : IServerBrowser::NETWORK_KOG; - // add more space - ServerFilter.HSplitTop(5.0f, 0, &ServerFilter); - ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); - ServerFilter.HSplitTop(120.0f, &ServerFilter, 0); + const ColorRGBA ColorActive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f); + const ColorRGBA ColorInactive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f); - ServerFilter.Draw(ms_ColorTabbarActive, IGraphics::CORNER_B, 10.0f); + const int Network = g_Config.m_UiPage == PAGE_DDNET ? IServerBrowser::NETWORK_DDNET : IServerBrowser::NETWORK_KOG; - Button.VSplitMid(&Button, &Button2); + CUIRect TabContents, CountriesTab, TypesTab; + View.HSplitTop(6.0f, nullptr, &View); + View.HSplitTop(19.0f, &Button, &View); + View.HSplitTop(minimum(120.0f + CScrollRegion::HEIGHT_MAGIC_FIX, View.h), &TabContents, &View); + Button.VSplitMid(&CountriesTab, &TypesTab); + TabContents.Draw(ms_ColorTabbarInactive, IGraphics::CORNER_B, 4.0f); - static int s_ActivePage = 0; + enum EFilterTab + { + FILTERTAB_COUNTRIES = 0, + FILTERTAB_TYPES, + }; + static EFilterTab s_ActiveTab = FILTERTAB_COUNTRIES; static CButtonContainer s_CountriesButton; - if(DoButton_MenuTab(&s_CountriesButton, Localize("Countries"), s_ActivePage == 0, &Button, IGraphics::CORNER_TL)) + if(DoButton_MenuTab(&s_CountriesButton, Localize("Countries"), s_ActiveTab == FILTERTAB_COUNTRIES, &CountriesTab, IGraphics::CORNER_TL, nullptr, &ColorInactive, &ColorActive, nullptr, 4.0f)) { - s_ActivePage = 0; + s_ActiveTab = FILTERTAB_COUNTRIES; } static CButtonContainer s_TypesButton; - if(DoButton_MenuTab(&s_TypesButton, Localize("Types"), s_ActivePage == 1, &Button2, IGraphics::CORNER_TR)) + if(DoButton_MenuTab(&s_TypesButton, Localize("Types"), s_ActiveTab == FILTERTAB_TYPES, &TypesTab, IGraphics::CORNER_TR, nullptr, &ColorInactive, &ColorActive, nullptr, 4.0f)) { - s_ActivePage = 1; + s_ActiveTab = FILTERTAB_TYPES; } - if(s_ActivePage == 1) + if(s_ActiveTab == FILTERTAB_COUNTRIES) { - char *pFilterExcludeTypes = Network == IServerBrowser::NETWORK_DDNET ? g_Config.m_BrFilterExcludeTypes : g_Config.m_BrFilterExcludeTypesKoG; - const int FilterExcludeTypesSize = Network == IServerBrowser::NETWORK_DDNET ? sizeof(g_Config.m_BrFilterExcludeTypes) : sizeof(g_Config.m_BrFilterExcludeTypesKoG); - int MaxTypes = ServerBrowser()->NumTypes(Network); - int NumTypes = ServerBrowser()->NumTypes(Network); - int PerLine = 3; - - ServerFilter.HSplitTop(4.0f, 0, &ServerFilter); - ServerFilter.HSplitBottom(4.0f, &ServerFilter, 0); - - const float TypesWidth = 40.0f; - const float TypesHeight = ServerFilter.h / std::ceil(MaxTypes / (float)PerLine); - - CUIRect TypesRect, Left, Right; - - static std::vector s_vTypeButtons; - s_vTypeButtons.resize(MaxTypes); - - while(NumTypes > 0) - { - ServerFilter.HSplitTop(TypesHeight, &TypesRect, &ServerFilter); - TypesRect.VSplitMid(&Left, &Right); - - for(int i = 0; i < PerLine && NumTypes > 0; i++, NumTypes--) - { - int TypeIndex = MaxTypes - NumTypes; - const char *pName = ServerBrowser()->GetType(Network, TypeIndex); - bool Active = !ServerBrowser()->DDNetFiltered(pFilterExcludeTypes, pName); - - vec2 Pos = vec2(TypesRect.x + TypesRect.w * ((i + 0.5f) / (float)PerLine), TypesRect.y); - - // correct pos - Pos.x -= TypesWidth / 2.0f; - - // create button logic - CUIRect Rect; - - Rect.x = Pos.x; - Rect.y = Pos.y; - Rect.w = TypesWidth; - Rect.h = TypesHeight; - - int Click = UI()->DoButtonLogic(&s_vTypeButtons[TypeIndex], 0, &Rect); - if(Click == 1 || Click == 2) - { - // left/right click to toggle filter - if(pFilterExcludeTypes[0] == '\0') - { - if(Click == 1) - { - // Left click: when all are active, only activate one - for(int j = 0; j < MaxTypes; ++j) - { - if(j != TypeIndex) - ServerBrowser()->DDNetFilterAdd(pFilterExcludeTypes, FilterExcludeTypesSize, ServerBrowser()->GetType(Network, j)); - } - } - else if(Click == 2) - { - // Right click: when all are active, only deactivate one - ServerBrowser()->DDNetFilterAdd(pFilterExcludeTypes, FilterExcludeTypesSize, ServerBrowser()->GetType(Network, TypeIndex)); - } - } - else - { - bool AllFilteredExceptUs = true; - for(int j = 0; j < MaxTypes; ++j) - { - if(j != TypeIndex && !ServerBrowser()->DDNetFiltered(pFilterExcludeTypes, ServerBrowser()->GetType(Network, j))) - { - AllFilteredExceptUs = false; - break; - } - } - // when last one is removed, reset (re-enable all) - if(AllFilteredExceptUs) - { - pFilterExcludeTypes[0] = '\0'; - } - else if(Active) - { - ServerBrowser()->DDNetFilterAdd(pFilterExcludeTypes, FilterExcludeTypesSize, pName); - } - else - { - ServerBrowser()->DDNetFilterRem(pFilterExcludeTypes, FilterExcludeTypesSize, pName); - } - } - - ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType()); - } - else if(Click == 3) - { - // middle click to reset (re-enable all) - pFilterExcludeTypes[0] = '\0'; - ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType()); - } - - TextRender()->TextColor(1.0f, 1.0f, 1.0f, (Active ? 0.9f : 0.2f) + (UI()->HotItem() == &s_vTypeButtons[TypeIndex] ? 0.1f : 0.0f)); - UI()->DoLabel(&Rect, pName, FontSize, TEXTALIGN_MC); - TextRender()->TextColor(1.0, 1.0, 1.0, 1.0f); - } - } + RenderServerbrowserCountriesFilter(TabContents, Network); } - else + else if(s_ActiveTab == FILTERTAB_TYPES) { - char *pFilterExcludeCountries = Network == IServerBrowser::NETWORK_DDNET ? g_Config.m_BrFilterExcludeCountries : g_Config.m_BrFilterExcludeCountriesKoG; - const int FilterExcludeCountriesSize = Network == IServerBrowser::NETWORK_DDNET ? sizeof(g_Config.m_BrFilterExcludeCountries) : sizeof(g_Config.m_BrFilterExcludeCountriesKoG); - ServerFilter.HSplitTop(15.0f, &ServerFilter, &ServerFilter); - - const float FlagWidth = 40.0f; - const float FlagHeight = 20.0f; - - int MaxFlags = ServerBrowser()->NumCountries(Network); - int NumFlags = ServerBrowser()->NumCountries(Network); - int PerLine = MaxFlags > 8 ? 5 : 4; - - CUIRect FlagsRect; - - static std::vector s_vFlagButtons; - s_vFlagButtons.resize(MaxFlags); - - while(NumFlags > 0) - { - ServerFilter.HSplitTop(23.0f, &FlagsRect, &ServerFilter); - - for(int i = 0; i < PerLine && NumFlags > 0; i++, NumFlags--) - { - int CountryIndex = MaxFlags - NumFlags; - const char *pName = ServerBrowser()->GetCountryName(Network, CountryIndex); - bool Active = !ServerBrowser()->DDNetFiltered(pFilterExcludeCountries, pName); - int FlagID = ServerBrowser()->GetCountryFlag(Network, CountryIndex); - - vec2 Pos = vec2(FlagsRect.x + FlagsRect.w * ((i + 0.5f) / (float)PerLine), FlagsRect.y); - - // correct pos - Pos.x -= FlagWidth / 2.0f; - Pos.y -= FlagHeight / 2.0f; - - // create button logic - CUIRect Rect; - - Rect.x = Pos.x; - Rect.y = Pos.y; - Rect.w = FlagWidth; - Rect.h = FlagHeight; - - int Click = UI()->DoButtonLogic(&s_vFlagButtons[CountryIndex], 0, &Rect); - if(Click == 1 || Click == 2) - { - // left/right click to toggle filter - if(pFilterExcludeCountries[0] == '\0') - { - if(Click == 1) - { - // Left click: when all are active, only activate one - for(int j = 0; j < MaxFlags; ++j) - { - if(j != CountryIndex) - ServerBrowser()->DDNetFilterAdd(pFilterExcludeCountries, FilterExcludeCountriesSize, ServerBrowser()->GetCountryName(Network, j)); - } - } - else if(Click == 2) - { - // Right click: when all are active, only deactivate one - ServerBrowser()->DDNetFilterAdd(pFilterExcludeCountries, FilterExcludeCountriesSize, ServerBrowser()->GetCountryName(Network, CountryIndex)); - } - } - else - { - bool AllFilteredExceptUs = true; - for(int j = 0; j < MaxFlags; ++j) - { - if(j != CountryIndex && !ServerBrowser()->DDNetFiltered(pFilterExcludeCountries, ServerBrowser()->GetCountryName(Network, j))) - { - AllFilteredExceptUs = false; - break; - } - } - // when last one is removed, reset (re-enable all) - if(AllFilteredExceptUs) - { - pFilterExcludeCountries[0] = '\0'; - } - else if(Active) - { - ServerBrowser()->DDNetFilterAdd(pFilterExcludeCountries, FilterExcludeCountriesSize, pName); - } - else - { - ServerBrowser()->DDNetFilterRem(pFilterExcludeCountries, FilterExcludeCountriesSize, pName); - } - } - - ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType()); - } - else if(Click == 3) - { - // middle click to reset (re-enable all) - pFilterExcludeCountries[0] = '\0'; - ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType()); - } - - m_pClient->m_CountryFlags.Render(FlagID, ColorRGBA(1.0f, 1.0f, 1.0f, (Active ? 0.9f : 0.2f) + (UI()->HotItem() == &s_vFlagButtons[CountryIndex] ? 0.1f : 0.0f)), Pos.x, Pos.y, FlagWidth, FlagHeight); - } - } + RenderServerbrowserTypesFilter(TabContents, Network); } } - static CButtonContainer s_ClearButton; - if(DoButton_Menu(&s_ClearButton, Localize("Reset filter"), 0, &ResetButton)) + static CButtonContainer s_ResetButton; + if(DoButton_Menu(&s_ResetButton, Localize("Reset filter"), 0, &ResetButton)) { - g_Config.m_BrFilterString[0] = 0; - g_Config.m_BrExcludeString[0] = 0; + g_Config.m_BrFilterString[0] = '\0'; + g_Config.m_BrExcludeString[0] = '\0'; g_Config.m_BrFilterFull = 0; g_Config.m_BrFilterEmpty = 0; g_Config.m_BrFilterSpectators = 0; @@ -983,13 +768,13 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) g_Config.m_BrFilterCountry = 0; g_Config.m_BrFilterCountryIndex = -1; g_Config.m_BrFilterPw = 0; - g_Config.m_BrFilterGametype[0] = 0; + g_Config.m_BrFilterGametype[0] = '\0'; g_Config.m_BrFilterGametypeStrict = 0; g_Config.m_BrFilterConnectingPlayers = 1; g_Config.m_BrFilterUnfinishedMap = 0; - g_Config.m_BrFilterServerAddress[0] = 0; - g_Config.m_BrFilterExcludeCountries[0] = 0; - g_Config.m_BrFilterExcludeTypes[0] = 0; + g_Config.m_BrFilterServerAddress[0] = '\0'; + g_Config.m_BrFilterExcludeCountries[0] = '\0'; + g_Config.m_BrFilterExcludeTypes[0] = '\0'; if(g_Config.m_UiPage == PAGE_DDNET || g_Config.m_UiPage == PAGE_KOG) ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType()); else @@ -997,6 +782,157 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) } } +void CMenus::RenderServerbrowserDDNetFilter(CUIRect View, + char *pFilterExclude, int FilterExcludeSize, + float ItemHeight, int MaxItems, int ItemsPerRow, + CScrollRegion &ScrollRegion, std::vector &vItemIds, + const std::function &GetItemName, + const std::function &RenderItem) +{ + vItemIds.resize(MaxItems); + + vec2 ScrollOffset(0.0f, 0.0f); + CScrollRegionParams ScrollParams; + ScrollParams.m_ScrollbarWidth = 10.0f; + ScrollParams.m_ScrollbarMargin = 3.0f; + ScrollParams.m_ScrollUnit = 2.0f * ItemHeight; + ScrollRegion.Begin(&View, &ScrollOffset, &ScrollParams); + View.y += ScrollOffset.y; + + CUIRect Row; + int ColumnIndex = 0; + for(int ItemIndex = 0; ItemIndex < MaxItems; ++ItemIndex) + { + CUIRect Item; + if(ColumnIndex == 0) + View.HSplitTop(ItemHeight, &Row, &View); + Row.VSplitLeft(View.w / ItemsPerRow, &Item, &Row); + ColumnIndex = (ColumnIndex + 1) % ItemsPerRow; + if(!ScrollRegion.AddRect(Item)) + continue; + + const void *pItemId = &vItemIds[ItemIndex]; + const char *pName = GetItemName(ItemIndex); + const bool Active = !ServerBrowser()->DDNetFiltered(pFilterExclude, pName); + + const int Click = UI()->DoButtonLogic(pItemId, 0, &Item); + if(Click == 1 || Click == 2) + { + // left/right click to toggle filter + if(pFilterExclude[0] == '\0') + { + if(Click == 1) + { + // Left click: when all are active, only activate one + for(int j = 0; j < MaxItems; ++j) + { + if(j != ItemIndex) + ServerBrowser()->DDNetFilterAdd(pFilterExclude, FilterExcludeSize, GetItemName(j)); + } + } + else if(Click == 2) + { + // Right click: when all are active, only deactivate one + ServerBrowser()->DDNetFilterAdd(pFilterExclude, FilterExcludeSize, GetItemName(ItemIndex)); + } + } + else + { + bool AllFilteredExceptUs = true; + for(int j = 0; j < MaxItems; ++j) + { + if(j != ItemIndex && !ServerBrowser()->DDNetFiltered(pFilterExclude, GetItemName(j))) + { + AllFilteredExceptUs = false; + break; + } + } + // when last one is removed, reset (re-enable all) + if(AllFilteredExceptUs) + { + pFilterExclude[0] = '\0'; + } + else if(Active) + { + ServerBrowser()->DDNetFilterAdd(pFilterExclude, FilterExcludeSize, pName); + } + else + { + ServerBrowser()->DDNetFilterRem(pFilterExclude, FilterExcludeSize, pName); + } + } + + ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType()); + } + else if(Click == 3) + { + // middle click to reset (re-enable all) + pFilterExclude[0] = '\0'; + ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType()); + } + + if(UI()->HotItem() == pItemId && !ScrollRegion.Animating()) + Item.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.33f), IGraphics::CORNER_ALL, 2.0f); + RenderItem(ItemIndex, Item, pItemId, Active); + } + + ScrollRegion.End(); +} + +void CMenus::RenderServerbrowserCountriesFilter(CUIRect View, int Network) +{ + char *pFilterExcludeCountries = Network == IServerBrowser::NETWORK_DDNET ? g_Config.m_BrFilterExcludeCountries : g_Config.m_BrFilterExcludeCountriesKoG; + const int FilterExcludeCountriesSize = Network == IServerBrowser::NETWORK_DDNET ? sizeof(g_Config.m_BrFilterExcludeCountries) : sizeof(g_Config.m_BrFilterExcludeCountriesKoG); + const int MaxEntries = ServerBrowser()->NumCountries(Network); + const int EntriesPerRow = MaxEntries > 8 ? 5 : 4; + + static CScrollRegion s_ScrollRegion; + static std::vector s_vItemIds; + + const float ItemHeight = 20.0f; + const float Spacing = 2.0f; + + const auto &&GetItemName = [&](int ItemIndex) { + return ServerBrowser()->GetCountryName(Network, ItemIndex); + }; + const auto &&RenderItem = [&](int ItemIndex, CUIRect Item, const void *pItemId, bool Active) { + Item.Margin(Spacing, &Item); + const float OldWidth = Item.w; + Item.w = Item.h * 2.0f; + Item.x += (OldWidth - Item.w) / 2.0f; + const int FlagID = ServerBrowser()->GetCountryFlag(Network, ItemIndex); + m_pClient->m_CountryFlags.Render(FlagID, ColorRGBA(1.0f, 1.0f, 1.0f, (Active ? 0.9f : 0.2f) + (UI()->HotItem() == pItemId ? 0.1f : 0.0f)), Item.x, Item.y, Item.w, Item.h); + }; + + RenderServerbrowserDDNetFilter(View, pFilterExcludeCountries, FilterExcludeCountriesSize, ItemHeight + 2.0f * Spacing, MaxEntries, EntriesPerRow, s_ScrollRegion, s_vItemIds, GetItemName, RenderItem); +} + +void CMenus::RenderServerbrowserTypesFilter(CUIRect View, int Network) +{ + char *pFilterExcludeTypes = Network == IServerBrowser::NETWORK_DDNET ? g_Config.m_BrFilterExcludeTypes : g_Config.m_BrFilterExcludeTypesKoG; + const int FilterExcludeTypesSize = Network == IServerBrowser::NETWORK_DDNET ? sizeof(g_Config.m_BrFilterExcludeTypes) : sizeof(g_Config.m_BrFilterExcludeTypesKoG); + const int MaxEntries = ServerBrowser()->NumTypes(Network); + const int EntriesPerRow = 3; + + static CScrollRegion s_ScrollRegion; + static std::vector s_vItemIds; + + const float ItemHeight = 13.0f; + const float Spacing = 2.0f; + + const auto &&GetItemName = [&](int ItemIndex) { + return ServerBrowser()->GetType(Network, ItemIndex); + }; + const auto &&RenderItem = [&](int ItemIndex, CUIRect Item, const void *pItemId, bool Active) { + Item.Margin(Spacing, &Item); + TextRender()->TextColor(1.0f, 1.0f, 1.0f, (Active ? 0.9f : 0.2f) + (UI()->HotItem() == pItemId ? 0.1f : 0.0f)); + UI()->DoLabel(&Item, GetItemName(ItemIndex), Item.h * CUI::ms_FontmodHeight, TEXTALIGN_MC); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + }; + + RenderServerbrowserDDNetFilter(View, pFilterExcludeTypes, FilterExcludeTypesSize, ItemHeight + 2.0f * Spacing, MaxEntries, EntriesPerRow, s_ScrollRegion, s_vItemIds, GetItemName, RenderItem); +} + CUI::EPopupMenuFunctionResult CMenus::PopupCountrySelection(void *pContext, CUIRect View, bool Active) { SPopupCountrySelectionContext *pPopupContext = static_cast(pContext); @@ -1045,22 +981,16 @@ CUI::EPopupMenuFunctionResult CMenus::PopupCountrySelection(void *pContext, CUIR return CUI::POPUP_KEEP_OPEN; } -void CMenus::RenderServerbrowserServerDetail(CUIRect View) +void CMenus::RenderServerbrowserInfo(CUIRect View) { - CUIRect ServerDetails = View; - CUIRect ServerScoreBoard, ServerHeader; - const CServerInfo *pSelectedServer = ServerBrowser()->SortedGet(m_SelectedIndex); - // split off a piece to use for scoreboard - ServerDetails.HSplitTop(110.0f, &ServerDetails, &ServerScoreBoard); + const float RowHeight = 18.0f; + const float FontSize = (RowHeight - 4.0f) * CUI::ms_FontmodHeight; // based on DoButton_CheckBox - // server details - const float FontSize = 12.0f; - ServerDetails.HSplitTop(ms_ListheaderHeight, &ServerHeader, &ServerDetails); - ServerHeader.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_T, 4.0f); - ServerDetails.Draw(ColorRGBA(0, 0, 0, 0.15f), IGraphics::CORNER_B, 4.0f); - UI()->DoLabel(&ServerHeader, Localize("Server details"), FontSize + 2.0f, TEXTALIGN_MC); + CUIRect ServerDetails, Scoreboard; + View.HSplitTop(4.0f * 15.0f + RowHeight + 2.0f * 5.0f + 2.0f * 2.0f, &ServerDetails, &Scoreboard); + ServerDetails.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_B, 4.0f); if(pSelectedServer) { @@ -1079,17 +1009,14 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) } } - ServerDetails.HSplitBottom(2.5f, &ServerDetails, nullptr); - // favorite checkbox { - CUIRect Button; - ServerDetails.HSplitBottom(20.0f, &ServerDetails, &Button); - CUIRect ButtonAddFav; - CUIRect ButtonLeakIp; - Button.VSplitMid(&ButtonAddFav, &ButtonLeakIp); + CUIRect ButtonAddFav, ButtonLeakIp; + ServerDetails.HSplitBottom(2.0f, &ServerDetails, nullptr); + ServerDetails.HSplitBottom(RowHeight, &ServerDetails, &ButtonAddFav); + ServerDetails.HSplitBottom(2.0f, &ServerDetails, nullptr); + ButtonAddFav.VSplitMid(&ButtonAddFav, &ButtonLeakIp); static int s_AddFavButton = 0; - static int s_LeakIpButton = 0; if(DoButton_CheckBox_Tristate(&s_AddFavButton, Localize("Favorite"), pSelectedServer->m_Favorite, &ButtonAddFav)) { if(pSelectedServer->m_Favorite != TRISTATE::NONE) @@ -1108,6 +1035,7 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) } if(pSelectedServer->m_Favorite != TRISTATE::NONE) { + static int s_LeakIpButton = 0; if(DoButton_CheckBox_Tristate(&s_LeakIpButton, Localize("Leak IP"), pSelectedServer->m_FavoriteAllowPing, &ButtonLeakIp)) { Favorites()->AllowPing(pSelectedServer->m_aAddresses, pSelectedServer->m_NumAddresses, pSelectedServer->m_FavoriteAllowPing == TRISTATE::NONE); @@ -1135,206 +1063,150 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) UI()->DoLabel(&Row, Localize("Ping"), FontSize, TEXTALIGN_ML); char aTemp[16]; - FormatServerbrowserPing(aTemp, sizeof(aTemp), pSelectedServer); + FormatServerbrowserPing(aTemp, pSelectedServer); RightColumn.HSplitTop(15.0f, &Row, &RightColumn); UI()->DoLabel(&Row, aTemp, FontSize, TEXTALIGN_ML); + + RenderServerbrowserInfoScoreboard(Scoreboard, pSelectedServer); } else { UI()->DoLabel(&ServerDetails, Localize("No server selected"), FontSize, TEXTALIGN_MC); } - - // server scoreboard - ServerScoreBoard.HSplitBottom(23.0f, &ServerScoreBoard, 0x0); - - CTextCursor Cursor; - if(pSelectedServer) - { - static CListBox s_ListBox; - s_ListBox.DoAutoSpacing(1.0f); - s_ListBox.DoStart(25.0f, pSelectedServer->m_NumReceivedClients, 1, 3, -1, &ServerScoreBoard); - - int ClientScoreKind = pSelectedServer->m_ClientScoreKind; - - for(int i = 0; i < pSelectedServer->m_NumReceivedClients; i++) - { - const CServerInfo::CClient &CurrentClient = pSelectedServer->m_aClients[i]; - const CListboxItem Item = s_ListBox.DoNextItem(&CurrentClient); - - if(!Item.m_Visible) - continue; - - const bool HasTeeToRender = pSelectedServer->m_aClients[i].m_aSkin[0] != '\0'; - - CUIRect Skin, Name, Clan, Score, Flag; - Name = Item.m_Rect; - - ColorRGBA Color = CurrentClient.m_FriendState == IFriends::FRIEND_NO ? - ColorRGBA(1.0f, 1.0f, 1.0f, (i % 2 + 1) * 0.05f) : - ColorRGBA(0.5f, 1.0f, 0.5f, 0.15f + (i % 2 + 1) * 0.05f); - Name.Draw(Color, IGraphics::CORNER_ALL, 4.0f); - Name.VSplitLeft(1.0f, nullptr, &Name); - Name.VSplitLeft(34.0f, &Score, &Name); - Name.VSplitLeft(18.0f, &Skin, &Name); - Name.VSplitRight(20.0, &Name, &Flag); - Flag.HMargin(4.0f, &Flag); - Name.HSplitTop(12.0f, &Name, &Clan); - - // score - char aTemp[16]; - - if(!CurrentClient.m_Player) - { - str_copy(aTemp, "SPEC"); - } - else if(ClientScoreKind == CServerInfo::CLIENT_SCORE_KIND_POINTS) - { - str_from_int(CurrentClient.m_Score, aTemp); - } - else - { - std::optional Time = {}; - - if(ClientScoreKind == CServerInfo::CLIENT_SCORE_KIND_TIME_BACKCOMPAT) - { - int TempTime = absolute(CurrentClient.m_Score); - if(TempTime != 0 && TempTime != 9999) - Time = TempTime; - } - else - { - // CServerInfo::CLIENT_SCORE_KIND_POINTS - if(CurrentClient.m_Score >= 0) - Time = CurrentClient.m_Score; - } - - if(Time.has_value()) - { - str_time((int64_t)Time.value() * 100, TIME_HOURS, aTemp, sizeof(aTemp)); - } - else - { - aTemp[0] = 0; - } - } - - UI()->DoLabel(&Score, aTemp, FontSize, TEXTALIGN_ML); - - // render tee if available - if(HasTeeToRender) - { - CTeeRenderInfo TeeInfo; - const CSkin *pSkin = m_pClient->m_Skins.Find(CurrentClient.m_aSkin); - TeeInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; - TeeInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; - TeeInfo.m_SkinMetrics = pSkin->m_Metrics; - TeeInfo.m_CustomColoredSkin = CurrentClient.m_CustomSkinColors; - if(CurrentClient.m_CustomSkinColors) - { - TeeInfo.m_ColorBody = color_cast(ColorHSLA(CurrentClient.m_CustomSkinColorBody).UnclampLighting()); - TeeInfo.m_ColorFeet = color_cast(ColorHSLA(CurrentClient.m_CustomSkinColorFeet).UnclampLighting()); - } - else - { - TeeInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); - TeeInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); - } - TeeInfo.m_Size = minimum(Skin.w, Skin.h); - - const CAnimState *pIdleState = CAnimState::GetIdle(); - vec2 OffsetToMid; - RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &TeeInfo, OffsetToMid); - vec2 TeeRenderPos(Skin.x + TeeInfo.m_Size / 2, Skin.y + Skin.h / 2 + OffsetToMid.y); - - RenderTools()->RenderTee(pIdleState, &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), TeeRenderPos); - } - - // name - TextRender()->SetCursor(&Cursor, Name.x, Name.y + (Name.h - (FontSize - 2)) / 2.f, FontSize - 2, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = Name.w; - const char *pName = CurrentClient.m_aName; - bool Printed = false; - if(g_Config.m_BrFilterString[0]) - Printed = PrintHighlighted(pName, [this, &Cursor, pName](const char *pFilteredStr, const int FilterLen) { - TextRender()->TextEx(&Cursor, pName, (int)(pFilteredStr - pName)); - TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1.0f); - TextRender()->TextEx(&Cursor, pFilteredStr, FilterLen); - TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); - TextRender()->TextEx(&Cursor, pFilteredStr + FilterLen, -1); - }); - if(!Printed) - TextRender()->TextEx(&Cursor, pName, -1); - - // clan - TextRender()->SetCursor(&Cursor, Clan.x, Clan.y + (Clan.h - (FontSize - 2)) / 2.f, FontSize - 2, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = Clan.w; - const char *pClan = CurrentClient.m_aClan; - Printed = false; - if(g_Config.m_BrFilterString[0]) - Printed = PrintHighlighted(pClan, [this, &Cursor, pClan](const char *pFilteredStr, const int FilterLen) { - TextRender()->TextEx(&Cursor, pClan, (int)(pFilteredStr - pClan)); - TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1.0f); - TextRender()->TextEx(&Cursor, pFilteredStr, FilterLen); - TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); - TextRender()->TextEx(&Cursor, pFilteredStr + FilterLen, -1); - }); - if(!Printed) - TextRender()->TextEx(&Cursor, pClan, -1); - - // flag - m_pClient->m_CountryFlags.Render(CurrentClient.m_Country, ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f), - Flag.x, Flag.y + ((Flag.h - Flag.w / 2) / 2), Flag.w, Flag.w / 2); - } - - const int NewSelected = s_ListBox.DoEnd(); - if(s_ListBox.WasItemSelected()) - { - const CServerInfo::CClient &SelectedClient = pSelectedServer->m_aClients[NewSelected]; - if(SelectedClient.m_FriendState == IFriends::FRIEND_PLAYER) - m_pClient->Friends()->RemoveFriend(SelectedClient.m_aName, SelectedClient.m_aClan); - else - m_pClient->Friends()->AddFriend(SelectedClient.m_aName, SelectedClient.m_aClan); - FriendlistOnUpdate(); - Client()->ServerBrowserUpdate(); - } - } } -template -bool CMenus::PrintHighlighted(const char *pName, F &&PrintFn) +void CMenus::RenderServerbrowserInfoScoreboard(CUIRect View, const CServerInfo *pSelectedServer) { - const char *pStr = g_Config.m_BrFilterString; - char aFilterStr[sizeof(g_Config.m_BrFilterString)]; - while((pStr = str_next_token(pStr, IServerBrowser::SEARCH_EXCLUDE_TOKEN, aFilterStr, sizeof(aFilterStr)))) + const float FontSize = 10.0f; + + static CListBox s_ListBox; + View.VSplitLeft(5.0f, nullptr, &View); + if(!s_ListBox.ScrollbarShown()) + View.VSplitRight(5.0f, &View, nullptr); + s_ListBox.DoAutoSpacing(1.0f); + s_ListBox.SetScrollbarWidth(16.0f); + s_ListBox.SetScrollbarMargin(5.0f); + s_ListBox.DoStart(25.0f, pSelectedServer->m_NumReceivedClients, 1, 3, -1, &View); + + for(int i = 0; i < pSelectedServer->m_NumReceivedClients; i++) { - // highlight the parts that matches - const char *pFilteredStr; - int FilterLen = str_length(aFilterStr); - if(aFilterStr[0] == '"' && aFilterStr[FilterLen - 1] == '"') + const CServerInfo::CClient &CurrentClient = pSelectedServer->m_aClients[i]; + const CListboxItem Item = s_ListBox.DoNextItem(&CurrentClient); + if(!Item.m_Visible) + continue; + + CUIRect Skin, Name, Clan, Score, Flag; + Name = Item.m_Rect; + + ColorRGBA Color = CurrentClient.m_FriendState == IFriends::FRIEND_NO ? + ColorRGBA(1.0f, 1.0f, 1.0f, (i % 2 + 1) * 0.05f) : + ColorRGBA(0.5f, 1.0f, 0.5f, 0.15f + (i % 2 + 1) * 0.05f); + Name.Draw(Color, IGraphics::CORNER_ALL, 4.0f); + Name.VSplitLeft(1.0f, nullptr, &Name); + Name.VSplitLeft(34.0f, &Score, &Name); + Name.VSplitLeft(18.0f, &Skin, &Name); + Name.VSplitRight(26.0f, &Name, &Flag); + Flag.HMargin(6.0f, &Flag); + Name.HSplitTop(12.0f, &Name, &Clan); + + // score + char aTemp[16]; + if(!CurrentClient.m_Player) { - aFilterStr[FilterLen - 1] = '\0'; - pFilteredStr = str_comp(pName, &aFilterStr[1]) == 0 ? pName : nullptr; - FilterLen -= 2; + str_copy(aTemp, "SPEC"); + } + else if(pSelectedServer->m_ClientScoreKind == CServerInfo::CLIENT_SCORE_KIND_POINTS) + { + str_from_int(CurrentClient.m_Score, aTemp); } else { - const char *pFilteredStrEnd; - pFilteredStr = str_utf8_find_nocase(pName, aFilterStr, &pFilteredStrEnd); - if(pFilteredStr != nullptr && pFilteredStrEnd != nullptr) - FilterLen = pFilteredStrEnd - pFilteredStr; - } - if(pFilteredStr) - { - PrintFn(pFilteredStr, FilterLen); - return true; - } - } - return false; -} + std::optional Time = {}; -void CMenus::FriendlistOnUpdate() -{ - // TODO: friends are currently updated every frame; optimize and only update friends when necessary + if(pSelectedServer->m_ClientScoreKind == CServerInfo::CLIENT_SCORE_KIND_TIME_BACKCOMPAT) + { + const int TempTime = absolute(CurrentClient.m_Score); + if(TempTime != 0 && TempTime != 9999) + Time = TempTime; + } + else + { + // CServerInfo::CLIENT_SCORE_KIND_POINTS + if(CurrentClient.m_Score >= 0) + Time = CurrentClient.m_Score; + } + + if(Time.has_value()) + { + str_time((int64_t)Time.value() * 100, TIME_HOURS, aTemp, sizeof(aTemp)); + } + else + { + aTemp[0] = '\0'; + } + } + + UI()->DoLabel(&Score, aTemp, FontSize, TEXTALIGN_ML); + + // render tee if available + if(CurrentClient.m_aSkin[0] != '\0') + { + const CTeeRenderInfo TeeInfo = GetTeeRenderInfo(vec2(Skin.w, Skin.h), CurrentClient.m_aSkin, CurrentClient.m_CustomSkinColors, CurrentClient.m_CustomSkinColorBody, CurrentClient.m_CustomSkinColorFeet); + const CAnimState *pIdleState = CAnimState::GetIdle(); + vec2 OffsetToMid; + RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &TeeInfo, OffsetToMid); + const vec2 TeeRenderPos = vec2(Skin.x + TeeInfo.m_Size / 2.0f, Skin.y + Skin.h / 2.0f + OffsetToMid.y); + RenderTools()->RenderTee(pIdleState, &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), TeeRenderPos); + } + + // name + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, Name.x, Name.y + (Name.h - (FontSize - 1.0f)) / 2.0f, FontSize - 1.0f, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = Name.w; + const char *pName = CurrentClient.m_aName; + bool Printed = false; + if(g_Config.m_BrFilterString[0]) + Printed = PrintHighlighted(pName, [&](const char *pFilteredStr, const int FilterLen) { + TextRender()->TextEx(&Cursor, pName, (int)(pFilteredStr - pName)); + TextRender()->TextColor(gs_HighlightedTextColor); + TextRender()->TextEx(&Cursor, pFilteredStr, FilterLen); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + TextRender()->TextEx(&Cursor, pFilteredStr + FilterLen, -1); + }); + if(!Printed) + TextRender()->TextEx(&Cursor, pName, -1); + + // clan + TextRender()->SetCursor(&Cursor, Clan.x, Clan.y + (Clan.h - (FontSize - 2.0f)) / 2.0f, FontSize - 2.0f, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = Clan.w; + const char *pClan = CurrentClient.m_aClan; + Printed = false; + if(g_Config.m_BrFilterString[0]) + Printed = PrintHighlighted(pClan, [&](const char *pFilteredStr, const int FilterLen) { + TextRender()->TextEx(&Cursor, pClan, (int)(pFilteredStr - pClan)); + TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1.0f); + TextRender()->TextEx(&Cursor, pFilteredStr, FilterLen); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + TextRender()->TextEx(&Cursor, pFilteredStr + FilterLen, -1); + }); + if(!Printed) + TextRender()->TextEx(&Cursor, pClan, -1); + + // flag + m_pClient->m_CountryFlags.Render(CurrentClient.m_Country, ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f), Flag.x, Flag.y, Flag.w, Flag.h); + } + + const int NewSelected = s_ListBox.DoEnd(); + if(s_ListBox.WasItemSelected()) + { + const CServerInfo::CClient &SelectedClient = pSelectedServer->m_aClients[NewSelected]; + if(SelectedClient.m_FriendState == IFriends::FRIEND_PLAYER) + m_pClient->Friends()->RemoveFriend(SelectedClient.m_aName, SelectedClient.m_aClan); + else + m_pClient->Friends()->AddFriend(SelectedClient.m_aName, SelectedClient.m_aClan); + FriendlistOnUpdate(); + Client()->ServerBrowserUpdate(); + } } void CMenus::RenderServerbrowserFriends(CUIRect View) @@ -1345,18 +1217,11 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) const ColorRGBA OfflineClanColor = ColorRGBA(0.7f, 0.45f, 0.75f, 1.0f); const float SpacingH = 2.0f; - char aBuf[256]; - CUIRect ServerFriends, FilterHeader, List; - - // header - View.HSplitTop(ms_ListheaderHeight, &FilterHeader, &View); - FilterHeader.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_T, 4.0f); - View.Draw(ColorRGBA(0, 0, 0, 0.15f), 0, 4.0f); - UI()->DoLabel(&FilterHeader, Localize("Friends"), FontSize + 4.0f, TEXTALIGN_MC); - - View.HSplitBottom(84.0f, &List, &ServerFriends); - List.HSplitTop(3.0f, nullptr, &List); - List.VSplitLeft(3.0f, nullptr, &List); + CUIRect List, ServerFriends; + View.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_NONE, 0.0f); + View.HSplitBottom(70.0f, &List, &ServerFriends); + List.HSplitTop(5.0f, nullptr, &List); + List.VSplitLeft(5.0f, nullptr, &List); // calculate friends // TODO: optimize this @@ -1395,26 +1260,27 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) // friends list static CScrollRegion s_ScrollRegion; if(!s_ScrollRegion.ScrollbarShown()) - List.VSplitRight(3.0f, &List, nullptr); + List.VSplitRight(5.0f, &List, nullptr); vec2 ScrollOffset(0.0f, 0.0f); CScrollRegionParams ScrollParams; - ScrollParams.m_ScrollbarWidth = 14.0f; - ScrollParams.m_ScrollbarMargin = 4.0f; + ScrollParams.m_ScrollbarWidth = 16.0f; + ScrollParams.m_ScrollbarMargin = 5.0f; ScrollParams.m_ScrollUnit = 80.0f; s_ScrollRegion.Begin(&List, &ScrollOffset, &ScrollParams); List.y += ScrollOffset.y; + char aBuf[256]; for(size_t FriendType = 0; FriendType < NUM_FRIEND_TYPES; ++FriendType) { // header CUIRect Header, GroupIcon, GroupLabel; List.HSplitTop(ms_ListheaderHeight, &Header, &List); s_ScrollRegion.AddRect(Header); - Header.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, UI()->MouseHovered(&Header) ? 0.4f : 0.25f), IGraphics::CORNER_ALL, 5.0f); + Header.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, UI()->HotItem() == &s_aListExtended[FriendType] ? 0.4f : 0.25f), IGraphics::CORNER_ALL, 5.0f); Header.VSplitLeft(Header.h, &GroupIcon, &GroupLabel); GroupIcon.Margin(2.0f, &GroupIcon); TextRender()->SetFontPreset(EFontPreset::ICON_FONT); - TextRender()->TextColor(UI()->MouseHovered(&Header) ? TextRender()->DefaultTextColor() : ColorRGBA(0.6f, 0.6f, 0.6f, 1.0f)); + TextRender()->TextColor(UI()->HotItem() == &s_aListExtended[FriendType] ? TextRender()->DefaultTextColor() : ColorRGBA(0.6f, 0.6f, 0.6f, 1.0f)); UI()->DoLabel(&GroupIcon, s_aListExtended[FriendType] ? FONT_ICON_SQUARE_MINUS : FONT_ICON_SQUARE_PLUS, GroupIcon.h * CUI::ms_FontmodHeight, TEXTALIGN_MC); TextRender()->TextColor(TextRender()->DefaultTextColor()); TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); @@ -1458,11 +1324,10 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) if(s_ScrollRegion.RectClipped(Rect)) continue; - const bool Inside = UI()->MouseHovered(&Rect); - bool ButtonResult = false; + const bool Inside = UI()->HotItem() == Friend.ListItemId() || UI()->HotItem() == Friend.RemoveButtonId(); + bool ButtonResult = UI()->DoButtonLogic(Friend.ListItemId(), 0, &Rect); if(Friend.ServerInfo()) { - ButtonResult = UI()->DoButtonLogic(Friend.ListItemId(), 0, &Rect); GameClient()->m_Tooltips.DoToolTip(Friend.ListItemId(), &Rect, Localize("Click to select server. Double click to join your friend.")); } const bool OfflineClan = Friend.FriendState() == IFriends::FRIEND_CLAN && FriendType == FRIEND_OFF; @@ -1486,29 +1351,11 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) Rect.VSplitLeft(Rect.h, &Skin, &Rect); Rect.VSplitLeft(2.0f, nullptr, &Rect); - CTeeRenderInfo TeeInfo; - const CSkin *pSkin = m_pClient->m_Skins.Find(Friend.Skin()); - TeeInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; - TeeInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; - TeeInfo.m_SkinMetrics = pSkin->m_Metrics; - TeeInfo.m_CustomColoredSkin = Friend.CustomSkinColors(); - if(Friend.CustomSkinColors()) - { - TeeInfo.m_ColorBody = color_cast(ColorHSLA(Friend.CustomSkinColorBody()).UnclampLighting()); - TeeInfo.m_ColorFeet = color_cast(ColorHSLA(Friend.CustomSkinColorFeet()).UnclampLighting()); - } - else - { - TeeInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); - TeeInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); - } - TeeInfo.m_Size = minimum(Skin.w, Skin.h); - + const CTeeRenderInfo TeeInfo = GetTeeRenderInfo(vec2(Skin.w, Skin.h), Friend.Skin(), Friend.CustomSkinColors(), Friend.CustomSkinColorBody(), Friend.CustomSkinColorFeet()); const CAnimState *pIdleState = CAnimState::GetIdle(); vec2 OffsetToMid; RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &TeeInfo, OffsetToMid); - vec2 TeeRenderPos(Skin.x + Skin.w / 2.0f, Skin.y + Skin.h * 0.55f + OffsetToMid.y); - + const vec2 TeeRenderPos = vec2(Skin.x + Skin.w / 2.0f, Skin.y + Skin.h * 0.55f + OffsetToMid.y); RenderTools()->RenderTee(pIdleState, &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), TeeRenderPos); } Rect.HSplitTop(11.0f, &NameLabel, &ClanLabel); @@ -1548,7 +1395,7 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) // server info text char aLatency[16]; - FormatServerbrowserPing(aLatency, sizeof(aLatency), Friend.ServerInfo()); + FormatServerbrowserPing(aLatency, Friend.ServerInfo()); if(aLatency[0] != '\0') str_format(aBuf, sizeof(aBuf), "%s | %s | %s", Friend.ServerInfo()->m_aMap, Friend.ServerInfo()->m_aGameType, aLatency); else @@ -1557,17 +1404,22 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) } // remove button - TextRender()->TextColor(UI()->MouseHovered(&RemoveButton) ? TextRender()->DefaultTextColor() : ColorRGBA(0.4f, 0.4f, 0.4f, 1.0f)); - TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); - UI()->DoLabel(&RemoveButton, "×", RemoveButton.h * CUI::ms_FontmodHeight, TEXTALIGN_MC); - TextRender()->SetRenderFlags(0); - TextRender()->TextColor(TextRender()->DefaultTextColor()); - if(UI()->DoButtonLogic(Friend.RemoveButtonId(), 0, &RemoveButton)) + if(Inside) { - m_pRemoveFriend = &Friend; - ButtonResult = false; + TextRender()->TextColor(UI()->HotItem() == Friend.RemoveButtonId() ? TextRender()->DefaultTextColor() : ColorRGBA(0.4f, 0.4f, 0.4f, 1.0f)); + TextRender()->SetFontPreset(EFontPreset::ICON_FONT); + TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); + UI()->DoLabel(&RemoveButton, FONT_ICON_TRASH, RemoveButton.h * CUI::ms_FontmodHeight, TEXTALIGN_MC); + TextRender()->SetRenderFlags(0); + TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + if(UI()->DoButtonLogic(Friend.RemoveButtonId(), 0, &RemoveButton)) + { + m_pRemoveFriend = &Friend; + ButtonResult = false; + } + GameClient()->m_Tooltips.DoToolTip(Friend.RemoveButtonId(), &RemoveButton, Friend.FriendState() == IFriends::FRIEND_PLAYER ? Localize("Click to remove this player from your friends list.") : Localize("Click to remove this clan from your friends list.")); } - GameClient()->m_Tooltips.DoToolTip(Friend.RemoveButtonId(), &RemoveButton, Friend.FriendState() == IFriends::FRIEND_PLAYER ? Localize("Click to remove this player from your friends list.") : Localize("Click to remove this clan from your friends list.")); // handle click and double click on item if(ButtonResult && Friend.ServerInfo()) @@ -1612,7 +1464,7 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) if(m_pClient->Friends()->NumFriends() < IFriends::MAX_FRIENDS) { CUIRect Button; - ServerFriends.Margin(3.0f, &ServerFriends); + ServerFriends.Margin(5.0f, &ServerFriends); ServerFriends.HSplitTop(18.0f, &Button, &ServerFriends); str_format(aBuf, sizeof(aBuf), "%s:", Localize("Name")); @@ -1643,6 +1495,11 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) } } +void CMenus::FriendlistOnUpdate() +{ + // TODO: friends are currently updated every frame; optimize and only update friends when necessary +} + void CMenus::PopupConfirmRemoveFriend() { m_pClient->Friends()->RemoveFriend(m_pRemoveFriend->FriendState() == IFriends::FRIEND_PLAYER ? m_pRemoveFriend->Name() : "", m_pRemoveFriend->Clan()); @@ -1651,78 +1508,151 @@ void CMenus::PopupConfirmRemoveFriend() m_pRemoveFriend = nullptr; } +enum +{ + UI_TOOLBOX_PAGE_FILTERS = 0, + UI_TOOLBOX_PAGE_INFO, + UI_TOOLBOX_PAGE_FRIENDS, + NUM_UI_TOOLBOX_PAGES, +}; + +void CMenus::RenderServerbrowserTabBar(CUIRect TabBar) +{ + CUIRect FilterTabButton, InfoTabButton, FriendsTabButton; + TabBar.VSplitLeft(TabBar.w / 3.0f, &FilterTabButton, &TabBar); + TabBar.VSplitMid(&InfoTabButton, &FriendsTabButton); + + const ColorRGBA ColorActive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f); + const ColorRGBA ColorInactive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f); + + if(!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_TAB)) + { + const int Direction = Input()->ShiftIsPressed() ? -1 : 1; + g_Config.m_UiToolboxPage = (g_Config.m_UiToolboxPage + NUM_UI_TOOLBOX_PAGES + Direction) % NUM_UI_TOOLBOX_PAGES; + } + + TextRender()->SetFontPreset(EFontPreset::ICON_FONT); + 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); + + static CButtonContainer s_FilterTabButton; + if(DoButton_MenuTab(&s_FilterTabButton, FONT_ICON_LIST_UL, g_Config.m_UiToolboxPage == UI_TOOLBOX_PAGE_FILTERS, &FilterTabButton, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_BROWSER_FILTER], &ColorInactive, &ColorActive)) + g_Config.m_UiToolboxPage = UI_TOOLBOX_PAGE_FILTERS; + + static CButtonContainer s_InfoTabButton; + if(DoButton_MenuTab(&s_InfoTabButton, FONT_ICON_INFO, g_Config.m_UiToolboxPage == UI_TOOLBOX_PAGE_INFO, &InfoTabButton, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_BROWSER_INFO], &ColorInactive, &ColorActive)) + g_Config.m_UiToolboxPage = UI_TOOLBOX_PAGE_INFO; + + static CButtonContainer s_FriendsTabButton; + if(DoButton_MenuTab(&s_FriendsTabButton, FONT_ICON_HEART, g_Config.m_UiToolboxPage == UI_TOOLBOX_PAGE_FRIENDS, &FriendsTabButton, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_BROWSER_FRIENDS], &ColorInactive, &ColorActive)) + g_Config.m_UiToolboxPage = UI_TOOLBOX_PAGE_FRIENDS; + + TextRender()->SetRenderFlags(0); + TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); +} + +void CMenus::RenderServerbrowserToolBox(CUIRect ToolBox) +{ + ToolBox.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_B, 4.0f); + + switch(g_Config.m_UiToolboxPage) + { + case UI_TOOLBOX_PAGE_FILTERS: + RenderServerbrowserFilters(ToolBox); + return; + case UI_TOOLBOX_PAGE_INFO: + RenderServerbrowserInfo(ToolBox); + return; + case UI_TOOLBOX_PAGE_FRIENDS: + RenderServerbrowserFriends(ToolBox); + return; + default: + dbg_assert(false, "ui_toolbox_page invalid"); + return; + } +} + void CMenus::RenderServerbrowser(CUIRect MainView) { /* - +-----------------+ +-------+ - | | | | - | | | tool | - | server list | | box | - | | | | - | | | | - +-----------------+ | | - status box tab +-------+ + +-----------------+ +--tabs--+ + | | | | + | | | | + | server list | | tool | + | | | box | + | | | | + +-----------------+ | | + status box +--------+ */ - CUIRect ServerList, ToolBox; - - // background + CUIRect ServerList, StatusBox, ToolBox, TabBar; MainView.Draw(ms_ColorTabbarActive, IGraphics::CORNER_B, 10.0f); MainView.Margin(10.0f, &MainView); - - // create server list, status box, tab bar and tool box area MainView.VSplitRight(205.0f, &ServerList, &ToolBox); - ServerList.VSplitRight(5.0f, &ServerList, 0); + ServerList.VSplitRight(5.0f, &ServerList, nullptr); + ToolBox.HSplitTop(24.0f, &TabBar, &ToolBox); + ServerList.HSplitBottom(65.0f, &ServerList, &StatusBox); - // server list + bool WasListboxItemActivated; + RenderServerbrowserServerList(ServerList, WasListboxItemActivated); + RenderServerbrowserStatusBox(StatusBox, WasListboxItemActivated); + + RenderServerbrowserTabBar(TabBar); + RenderServerbrowserToolBox(ToolBox); +} + +template +bool CMenus::PrintHighlighted(const char *pName, F &&PrintFn) +{ + const char *pStr = g_Config.m_BrFilterString; + char aFilterStr[sizeof(g_Config.m_BrFilterString)]; + while((pStr = str_next_token(pStr, IServerBrowser::SEARCH_EXCLUDE_TOKEN, aFilterStr, sizeof(aFilterStr)))) { - RenderServerbrowserServerList(ServerList); + // highlight the parts that matches + const char *pFilteredStr; + int FilterLen = str_length(aFilterStr); + if(aFilterStr[0] == '"' && aFilterStr[FilterLen - 1] == '"') + { + aFilterStr[FilterLen - 1] = '\0'; + pFilteredStr = str_comp(pName, &aFilterStr[1]) == 0 ? pName : nullptr; + FilterLen -= 2; + } + else + { + const char *pFilteredStrEnd; + pFilteredStr = str_utf8_find_nocase(pName, aFilterStr, &pFilteredStrEnd); + if(pFilteredStr != nullptr && pFilteredStrEnd != nullptr) + FilterLen = pFilteredStrEnd - pFilteredStr; + } + if(pFilteredStr) + { + PrintFn(pFilteredStr, FilterLen); + return true; + } } + return false; +} - int ToolboxPage = g_Config.m_UiToolboxPage; +CTeeRenderInfo CMenus::GetTeeRenderInfo(vec2 Size, const char *pSkinName, bool CustomSkinColors, int CustomSkinColorBody, int CustomSkinColorFeet) const +{ + const CSkin *pSkin = m_pClient->m_Skins.Find(pSkinName); - // tool box + CTeeRenderInfo TeeInfo; + TeeInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; + TeeInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; + TeeInfo.m_SkinMetrics = pSkin->m_Metrics; + TeeInfo.m_CustomColoredSkin = CustomSkinColors; + if(CustomSkinColors) { - ToolBox.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_ALL, 4.0f); - - if(ToolboxPage == 0) - RenderServerbrowserFilters(ToolBox); - else if(ToolboxPage == 1) - RenderServerbrowserServerDetail(ToolBox); - else if(ToolboxPage == 2) - RenderServerbrowserFriends(ToolBox); + TeeInfo.m_ColorBody = color_cast(ColorHSLA(CustomSkinColorBody).UnclampLighting()); + TeeInfo.m_ColorFeet = color_cast(ColorHSLA(CustomSkinColorFeet).UnclampLighting()); } - - // tab bar + else { - CUIRect TabBar; - ToolBox.HSplitBottom(18, &ToolBox, &TabBar); - CUIRect TabButton0, TabButton1, TabButton2; - float CurTabBarWidth = ToolBox.w; - TabBar.VSplitLeft(0.333f * CurTabBarWidth, &TabButton0, &TabBar); - TabBar.VSplitLeft(0.333f * CurTabBarWidth, &TabButton1, &TabBar); - TabBar.VSplitLeft(0.333f * CurTabBarWidth, &TabButton2, &TabBar); - ColorRGBA Active = ms_ColorTabbarActive; - ColorRGBA InActive = ms_ColorTabbarInactive; - ms_ColorTabbarActive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f); - ms_ColorTabbarInactive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f); - - static CButtonContainer s_FiltersTab; - if(DoButton_MenuTab(&s_FiltersTab, Localize("Filter"), ToolboxPage == 0, &TabButton0, IGraphics::CORNER_BL, NULL, NULL, NULL, NULL, 4.0f)) - ToolboxPage = 0; - - static CButtonContainer s_InfoTab; - if(DoButton_MenuTab(&s_InfoTab, Localize("Info"), ToolboxPage == 1, &TabButton1, IGraphics::CORNER_NONE, NULL, NULL, NULL, NULL, 4.0f)) - ToolboxPage = 1; - - static CButtonContainer s_FriendsTab; - if(DoButton_MenuTab(&s_FriendsTab, Localize("Friends"), ToolboxPage == 2, &TabButton2, IGraphics::CORNER_BR, NULL, NULL, NULL, NULL, 4.0f)) - ToolboxPage = 2; - - ms_ColorTabbarActive = Active; - ms_ColorTabbarInactive = InActive; - g_Config.m_UiToolboxPage = ToolboxPage; + TeeInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); + TeeInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); } + TeeInfo.m_Size = minimum(Size.x, Size.y); + return TeeInfo; } void CMenus::ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) From 67e753bd6ba2f9110c42a6e188b86afad4511203 Mon Sep 17 00:00:00 2001 From: furo Date: Sat, 16 Sep 2023 22:09:26 +0200 Subject: [PATCH 122/126] Don't include .demo in demo slice input --- src/game/client/components/menus_demo.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index eb6cb0f74..aa8078b9f 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -686,7 +686,6 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) char aDemoName[IO_MAX_PATH_LENGTH]; DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName)); m_DemoSliceInput.Set(aDemoName); - m_DemoSliceInput.Append(".demo"); UI()->SetActiveItem(&m_DemoSliceInput); m_DemoPlayerState = DEMOPLAYER_SLICE_SAVE; } @@ -828,11 +827,11 @@ void CMenus::RenderDemoPlayerSliceSavePopup(CUIRect MainView) if(DoButton_Menu(&s_ButtonOk, Localize("Ok"), 0, &OkButton) || (!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))) { char aDemoName[IO_MAX_PATH_LENGTH]; + char aNameWithoutExt[IO_MAX_PATH_LENGTH]; DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName)); - str_append(aDemoName, ".demo"); - if(!str_endswith(m_DemoSliceInput.GetString(), ".demo")) - m_DemoSliceInput.Append(".demo"); + fs_split_file_extension(m_DemoSliceInput.GetString(), aNameWithoutExt, sizeof(aNameWithoutExt)); + m_DemoSliceInput.Set(aNameWithoutExt); if(str_comp(aDemoName, m_DemoSliceInput.GetString()) == 0) { @@ -860,7 +859,7 @@ void CMenus::RenderDemoPlayerSliceSavePopup(CUIRect MainView) if(s_ConfirmPopupContext.m_Result == CUI::SConfirmPopupContext::CONFIRMED) { char aPath[IO_MAX_PATH_LENGTH]; - str_format(aPath, sizeof(aPath), "%s/%s", m_aCurrentDemoFolder, m_DemoSliceInput.GetString()); + str_format(aPath, sizeof(aPath), "%s/%s.demo", m_aCurrentDemoFolder, m_DemoSliceInput.GetString()); str_copy(m_aCurrentDemoSelectionName, m_DemoSliceInput.GetString()); if(str_endswith(m_aCurrentDemoSelectionName, ".demo")) m_aCurrentDemoSelectionName[str_length(m_aCurrentDemoSelectionName) - str_length(".demo")] = '\0'; From 77840a9c97762e7a3d6506dce196e2f86b02f2a5 Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 17 Sep 2023 13:49:52 +0200 Subject: [PATCH 123/126] Fix everyones swap getting reset when someone swaps --- src/game/server/teams.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index b4f8850d1..30bf030c7 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -891,13 +891,8 @@ void CGameTeams::SwapTeamCharacters(CPlayer *pPrimaryPlayer, CPlayer *pTargetPla return; } - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) - { - GameServer()->m_apPlayers[i]->m_SwapTargetsClientID = -1; - } - } + pPrimaryPlayer->m_SwapTargetsClientID = -1; + pTargetPlayer->m_SwapTargetsClientID = -1; int TimeoutAfterDelay = g_Config.m_SvSaveSwapGamesDelay + g_Config.m_SvSwapTimeout; if(Since >= TimeoutAfterDelay) From e33b747a4c43e4e36c6ac3b3784d6b18dc08d462 Mon Sep 17 00:00:00 2001 From: Dennis Felsing Date: Sun, 17 Sep 2023 14:32:23 +0200 Subject: [PATCH 124/126] Increase password size limit from 32 to 128 --- src/engine/client/client.cpp | 2 +- src/engine/client/client.h | 4 ++-- src/engine/shared/config_variables.h | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index b2b220159..c3e0e0ee4 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -505,7 +505,7 @@ void CClient::RconAuth(const char *pName, const char *pPassword) CMsgPacker Msg(NETMSG_RCON_AUTH, true); Msg.AddString(pName, 32); - Msg.AddString(pPassword, 32); + Msg.AddString(pPassword, 128); Msg.AddInt(1); SendMsgActive(&Msg, MSGFLAG_VITAL); } diff --git a/src/engine/client/client.h b/src/engine/client/client.h index 6578e5ae3..cc2bbef3f 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -164,9 +164,9 @@ class CClient : public IClient, public CDemoPlayer::IListener int m_aCurrentRecvTick[NUM_DUMMIES]; int m_aRconAuthed[NUM_DUMMIES]; char m_aRconUsername[32]; - char m_aRconPassword[32]; + char m_aRconPassword[128]; int m_UseTempRconCommands; - char m_aPassword[32]; + char m_aPassword[128]; bool m_SendPassword; bool m_ButtonRender = false; diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index a45ea0de6..5f7c8eedb 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -10,7 +10,7 @@ MACRO_CONFIG_STR(PlayerName, player_name, 16, "", CFGFLAG_SAVE | CFGFLAG_CLIENT MACRO_CONFIG_STR(PlayerClan, player_clan, 12, "", CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_INSENSITIVE, "Clan of the player") MACRO_CONFIG_INT(PlayerCountry, player_country, -1, -1, 1000, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_INSENSITIVE, "Country of the player") -MACRO_CONFIG_STR(Password, password, 32, "", CFGFLAG_CLIENT | CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Password to the server") +MACRO_CONFIG_STR(Password, password, 128, "", CFGFLAG_CLIENT | CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Password to the server") MACRO_CONFIG_INT(Events, events, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_SERVER, "Enable triggering of events, (eye emotes on some holidays in server, christmas skins in client).") MACRO_CONFIG_STR(SteamName, steam_name, 16, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Last seen name of the Steam profile") @@ -159,9 +159,9 @@ MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "U MACRO_CONFIG_STR(SvRegister, sv_register, 16, "1", CFGFLAG_SERVER, "Register server with master server for public listing, can also accept a comma-separated list of protocols to register on, like 'ipv4,ipv6'") MACRO_CONFIG_STR(SvRegisterExtra, sv_register_extra, 256, "", CFGFLAG_SERVER, "Extra headers to send to the register endpoint, comma separated 'Header: Value' pairs") MACRO_CONFIG_STR(SvRegisterUrl, sv_register_url, 128, "https://master1.ddnet.org/ddnet/15/register", CFGFLAG_SERVER, "Masterserver URL to register to") -MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Remote console password (full access)") -MACRO_CONFIG_STR(SvRconModPassword, sv_rcon_mod_password, 32, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Remote console password for moderators (limited access)") -MACRO_CONFIG_STR(SvRconHelperPassword, sv_rcon_helper_password, 32, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Remote console password for helpers (limited access)") +MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 128, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Remote console password (full access)") +MACRO_CONFIG_STR(SvRconModPassword, sv_rcon_mod_password, 128, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Remote console password for moderators (limited access)") +MACRO_CONFIG_STR(SvRconHelperPassword, sv_rcon_helper_password, 128, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Remote console password for helpers (limited access)") MACRO_CONFIG_INT(SvRconMaxTries, sv_rcon_max_tries, 30, 0, 100, CFGFLAG_SERVER, "Maximum number of tries for remote console authentication") MACRO_CONFIG_INT(SvRconBantime, sv_rcon_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if remote console authentication fails. 0 makes it just use kick") MACRO_CONFIG_INT(SvAutoDemoRecord, sv_auto_demo_record, 0, 0, 1, CFGFLAG_SERVER, "Automatically record demos") @@ -185,7 +185,7 @@ MACRO_CONFIG_INT(SvSkillLevel, sv_skill_level, 1, SERVERINFO_LEVEL_MIN, SERVERIN MACRO_CONFIG_STR(EcBindaddr, ec_bindaddr, 128, "localhost", CFGFLAG_ECON, "Address to bind the external console to. Anything but 'localhost' is dangerous") MACRO_CONFIG_INT(EcPort, ec_port, 0, 0, 0, CFGFLAG_ECON, "Port to use for the external console") -MACRO_CONFIG_STR(EcPassword, ec_password, 32, "", CFGFLAG_ECON, "External console password") +MACRO_CONFIG_STR(EcPassword, ec_password, 128, "", CFGFLAG_ECON, "External console password") MACRO_CONFIG_INT(EcBantime, ec_bantime, 0, 0, 1440, CFGFLAG_ECON, "The time a client gets banned if econ authentication fails. 0 just closes the connection") MACRO_CONFIG_INT(EcAuthTimeout, ec_auth_timeout, 30, 1, 120, CFGFLAG_ECON, "Time in seconds before the the econ authentication times out") MACRO_CONFIG_INT(EcOutputLevel, ec_output_level, 1, 0, 2, CFGFLAG_ECON, "Adjusts the amount of information in the external console") @@ -204,7 +204,7 @@ MACRO_CONFIG_INT(HttpAllowInsecure, http_allow_insecure, 0, 0, 1, CFGFLAG_CLIENT // DDRace MACRO_CONFIG_STR(SvWelcome, sv_welcome, 64, "", CFGFLAG_SERVER, "Message that will be displayed to players who join the server") MACRO_CONFIG_INT(SvReservedSlots, sv_reserved_slots, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "The number of slots that are reserved for special players") -MACRO_CONFIG_STR(SvReservedSlotsPass, sv_reserved_slots_pass, 32, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "The password that is required to use a reserved slot") +MACRO_CONFIG_STR(SvReservedSlotsPass, sv_reserved_slots_pass, 128, "", CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "The password that is required to use a reserved slot") MACRO_CONFIG_INT(SvReservedSlotsAuthLevel, sv_reserved_slots_auth_level, 1, 1, 4, CFGFLAG_SERVER, "Minimum rcon auth level needed to use a reserved slot. 4 = rcon auth disabled") MACRO_CONFIG_INT(SvHit, sv_hit, 1, 0, 1, CFGFLAG_SERVER | CFGFLAG_GAME, "Whether players can hammer/grenade/laser each other or not") MACRO_CONFIG_INT(SvEndlessDrag, sv_endless_drag, 0, 0, 1, CFGFLAG_SERVER | CFGFLAG_GAME, "Turns endless hooking on/off") From 33cae237685dda5be3ef0676b373d0e88ec20c24 Mon Sep 17 00:00:00 2001 From: Dennis Felsing Date: Sun, 17 Sep 2023 16:23:20 +0200 Subject: [PATCH 125/126] Update translations for upcoming 17.3 --- data/languages/arabic.txt | 44 +++++++++++++------------ data/languages/belarusian.txt | 32 +++++++++--------- data/languages/bosnian.txt | 44 +++++++++++++------------ data/languages/brazilian_portuguese.txt | 32 +++++++++--------- data/languages/bulgarian.txt | 44 +++++++++++++------------ data/languages/catalan.txt | 44 +++++++++++++------------ data/languages/chuvash.txt | 44 +++++++++++++------------ data/languages/czech.txt | 44 +++++++++++++------------ data/languages/danish.txt | 44 +++++++++++++------------ data/languages/dutch.txt | 44 +++++++++++++------------ data/languages/esperanto.txt | 44 +++++++++++++------------ data/languages/finnish.txt | 44 +++++++++++++------------ data/languages/french.txt | 32 +++++++++--------- data/languages/galician.txt | 32 +++++++++--------- data/languages/german.txt | 34 ++++++++++--------- data/languages/greek.txt | 44 +++++++++++++------------ data/languages/hungarian.txt | 32 +++++++++--------- data/languages/italian.txt | 44 +++++++++++++------------ data/languages/japanese.txt | 44 +++++++++++++------------ data/languages/korean.txt | 32 +++++++++--------- data/languages/kyrgyz.txt | 44 +++++++++++++------------ data/languages/norwegian.txt | 44 +++++++++++++------------ data/languages/persian.txt | 32 +++++++++--------- data/languages/polish.txt | 44 +++++++++++++------------ data/languages/portuguese.txt | 44 +++++++++++++------------ data/languages/romanian.txt | 44 +++++++++++++------------ data/languages/russian.txt | 32 +++++++++--------- data/languages/serbian.txt | 40 +++++++++++----------- data/languages/serbian_cyrillic.txt | 44 +++++++++++++------------ data/languages/simplified_chinese.txt | 32 +++++++++--------- data/languages/slovak.txt | 44 +++++++++++++------------ data/languages/spanish.txt | 32 +++++++++--------- data/languages/swedish.txt | 32 +++++++++--------- data/languages/traditional_chinese.txt | 32 +++++++++--------- data/languages/turkish.txt | 32 +++++++++--------- data/languages/ukrainian.txt | 32 +++++++++--------- 36 files changed, 737 insertions(+), 665 deletions(-) diff --git a/data/languages/arabic.txt b/data/languages/arabic.txt index 3bbdbf04f..66c7f76c9 100644 --- a/data/languages/arabic.txt +++ b/data/languages/arabic.txt @@ -140,9 +140,6 @@ Favorites Feet == ﺔﻣﺰﺟ -Filter -== ﺔﻴﻔﺼﺗ - Fire == ﺭﺎﻧ @@ -155,9 +152,6 @@ Force vote Free-View == ﺮﺤﻟﺍ ﺮﻈﻨﻤﻟﺍ -Friends -== ﺀﺎﻗﺪﺻﻻﺍ - Fullscreen == ﻞﻣﺎﻜﻟﺍ ﺔﺷﺎﺸﻟﺍ ﻊﺿﻭ @@ -354,12 +348,6 @@ Screenshot Server address: == ﺮﻓﺮﻴﺴﻟﺍ ﻥﺍﻮﻨﻋ: -Server details -== ﺮﻓﺮﻴﺴﻟﺍ ﻞﻴﺻﺎﻔﺗ - -Server filter -== ﺮﻓﺮﻴﺴﻟﺍ ﺔﻴﻔﺼﺗ - Server info == ﺮﻓﺮﻴﺴﻟﺍ ﺕﺎﻣﻮﻠﻌﻣ @@ -528,9 +516,6 @@ Netversion: Map: == ﺏﺎﻤﻟﺍ: -Info -== ﺕﺎﻣﻮﻠﻌﻣ - Hue == ﻥﻮﻟ @@ -1355,12 +1340,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1376,12 +1384,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/belarusian.txt b/data/languages/belarusian.txt index 8520864d4..4f94ab744 100644 --- a/data/languages/belarusian.txt +++ b/data/languages/belarusian.txt @@ -147,9 +147,6 @@ Favorites Feet == Ногі -Filter -== Фільтр - Fire == Стрэл @@ -162,9 +159,6 @@ Force vote Free-View == Вольны агляд -Friends -== Сябры - Fullscreen == Поўнаэкранны рэжым @@ -358,12 +352,6 @@ Screenshot Server address: == Адрас сервера: -Server details -== Дэталі сервера - -Server filter -== Фільтр сервераў - Server info == Інфармацыя @@ -532,9 +520,6 @@ Netversion: Map: == Карта: -Info -== Інфа - Hue == Адценне @@ -1727,6 +1712,23 @@ Unable to rename the folder (paused) == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Render cut to video == diff --git a/data/languages/bosnian.txt b/data/languages/bosnian.txt index ff22d6d01..87cd5b513 100644 --- a/data/languages/bosnian.txt +++ b/data/languages/bosnian.txt @@ -148,9 +148,6 @@ Favorites Feet == Stopala -Filter -== Filter - Fire == Pucanje @@ -163,9 +160,6 @@ Force vote Free-View == Slobodan pogled -Friends -== Prijatelji - Fullscreen == Čitav ekran @@ -359,12 +353,6 @@ Screenshot Server address: == Adresa servera: -Server details -== Podaci o serveru - -Server filter -== Filter servera - Server info == O Serveru @@ -535,9 +523,6 @@ Netversion: Map: == Mapa: -Info -== Info - Hue == Nijans. @@ -1247,12 +1232,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1268,12 +1276,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/brazilian_portuguese.txt b/data/languages/brazilian_portuguese.txt index 3b91fe32f..fa08983a5 100644 --- a/data/languages/brazilian_portuguese.txt +++ b/data/languages/brazilian_portuguese.txt @@ -170,9 +170,6 @@ Favorites Feet == Pés -Filter -== Filtro - Fire == Atirar @@ -185,9 +182,6 @@ Force vote Free-View == Visualização livre -Friends -== Amigos - Fullscreen == Tela cheia @@ -384,12 +378,6 @@ Screenshot Server address: == Endereço: -Server details -== Detalhes do servidor - -Server filter -== Filtro de servidores - Server info == Servidor @@ -576,9 +564,6 @@ Show ghost Clan plates size == Tamanho da placa do clã -Info -== Info - No updates available == Nenhuma atualização disponível @@ -1768,5 +1753,22 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == Movido no jogo +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Render cut to video == diff --git a/data/languages/bulgarian.txt b/data/languages/bulgarian.txt index 7953c3b06..9a195ba72 100644 --- a/data/languages/bulgarian.txt +++ b/data/languages/bulgarian.txt @@ -145,9 +145,6 @@ Favorites Feet == Крака -Filter -== Филтър - Fire == Стрелба @@ -160,9 +157,6 @@ Force vote Free-View == Свободен Изглед -Friends -== Приятели - Fullscreen == Цял Екран @@ -356,12 +350,6 @@ Screenshot Server address: == Адрес на сървъра: -Server details -== Детайли за Сървъра - -Server filter -== Филтър на сървъра - Server info == Инфо @@ -529,9 +517,6 @@ Netversion: Map: == Карта: -Info -== Инфо - Hue == Оттенък @@ -908,12 +893,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -929,12 +937,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/catalan.txt b/data/languages/catalan.txt index 59c7b24fd..20d5a1fae 100644 --- a/data/languages/catalan.txt +++ b/data/languages/catalan.txt @@ -140,9 +140,6 @@ Favorites Feet == Peus -Filter -== Filtre - Fire == Disparar @@ -155,9 +152,6 @@ Force vote Free-View == Vista lliure -Friends -== Amics - Fullscreen == Pantalla completa @@ -354,12 +348,6 @@ Screenshot Server address: == IP del servidor: -Server details -== Detalls del servidor - -Server filter -== Filtre del servidor - Server info == Servidor @@ -540,9 +528,6 @@ Netversion: Map: == Mapa: -Info -== Informació - Hue == Tonalitat @@ -1427,12 +1412,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1448,12 +1456,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/chuvash.txt b/data/languages/chuvash.txt index 54e69dcc8..60072a571 100644 --- a/data/languages/chuvash.txt +++ b/data/languages/chuvash.txt @@ -145,9 +145,6 @@ Favorites Feet == Урасем -Filter -== Фильтр - Fire == Пĕрӳ @@ -160,9 +157,6 @@ Force vote Free-View == Ирĕклĕ обзор -Friends -== Юлташсем - Fullscreen == Тулли экран @@ -356,12 +350,6 @@ Screenshot Server address: == Сервер адресĕ -Server details -== Сервер тĕплĕсем - -Server filter -== Серверсен фильтр - Server info == Пĕлтерӳ @@ -535,9 +523,6 @@ Miscellaneous Netversion: == Версия: -Info -== Пĕлтерӳ - UI Color == Интерфейс тĕсĕ @@ -911,12 +896,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -932,12 +940,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/czech.txt b/data/languages/czech.txt index a5ae8529b..f4dede85f 100644 --- a/data/languages/czech.txt +++ b/data/languages/czech.txt @@ -148,9 +148,6 @@ Favorites Feet == Nohy -Filter -== Filtr - Fire == Střelba @@ -163,9 +160,6 @@ Force vote Free-View == Volný pohled -Friends -== Přátelé - Fullscreen == Celá obrazovka @@ -355,12 +349,6 @@ Screenshot Server address: == Adresa serveru: -Server details -== Detaily serveru - -Server filter -== Filtr serverů - Server info == Informace @@ -531,9 +519,6 @@ Netversion: Map: == Mapa: -Info -== Info - Hue == Tón @@ -1365,12 +1350,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1386,12 +1394,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/danish.txt b/data/languages/danish.txt index 894a9591b..245e92827 100644 --- a/data/languages/danish.txt +++ b/data/languages/danish.txt @@ -146,9 +146,6 @@ Favorites Feet == Fødder -Filter -== Filter - Fire == Skyd @@ -161,9 +158,6 @@ Force vote Free-View == Free-View -Friends -== Venner - Fullscreen == Fuldskærm @@ -357,12 +351,6 @@ Screenshot Server address: == Serveradresse -Server details -== Serverdetaljer - -Server filter -== Serverfilter - Server info == Serverinfo @@ -533,9 +521,6 @@ Netversion: Map: == Bane -Info -== Info - Hue == Farve @@ -1363,12 +1348,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1384,12 +1392,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/dutch.txt b/data/languages/dutch.txt index e52f4bd40..127cd5bff 100644 --- a/data/languages/dutch.txt +++ b/data/languages/dutch.txt @@ -158,9 +158,6 @@ Favorites Feet == Voeten -Filter -== Filter - Fire == Schieten @@ -173,9 +170,6 @@ Force vote Free-View == Vrij bewegen -Friends -== Vrienden - Fullscreen == Volledig scherm @@ -369,12 +363,6 @@ Screenshot Server address: == Serveradres: -Server details -== Serverdetails - -Server filter -== Serverfilter - Server info == Serverinfo @@ -545,9 +533,6 @@ Netversion: Map: == Kaart: -Info -== Info - Hue == Tint @@ -1473,12 +1458,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1494,12 +1502,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/esperanto.txt b/data/languages/esperanto.txt index 078b353f9..ad1adc17c 100644 --- a/data/languages/esperanto.txt +++ b/data/languages/esperanto.txt @@ -273,9 +273,6 @@ Countries Favorite == Favorati -Friends -== Amikoj - Name == Nomo @@ -285,9 +282,6 @@ Clan Add Friend == Aldoni amikon -Filter -== Filtri - Please use a different name == Bonvolu uzi malsaman nomon @@ -767,9 +761,6 @@ The server is running a non-standard tuning on a pure game type. Loading menu images == -Server filter -== - Has people playing == @@ -797,9 +788,6 @@ Types Reset filter == -Server details -== - Copy info == @@ -843,9 +831,6 @@ Remove friend Add Clan == -Info -== - Play the current demo == @@ -855,12 +840,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -876,12 +884,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/finnish.txt b/data/languages/finnish.txt index b7fba4673..c114de532 100644 --- a/data/languages/finnish.txt +++ b/data/languages/finnish.txt @@ -147,9 +147,6 @@ Favorites Feet == Jalat -Filter -== Suotimet - Fire == Ammu @@ -162,9 +159,6 @@ Force vote Free-View == Vapaa näkymä -Friends -== Ystävät - Fullscreen == Koko näyttö @@ -358,12 +352,6 @@ Screenshot Server address: == Palvelimen osoite: -Server details -== Palvelimen yksityiskohdat - -Server filter -== Palvelinsuotimet - Server info == Palvelintiedot @@ -532,9 +520,6 @@ Netversion: Map: == Kenttä: -Info -== Info - Hue == Värisävy @@ -1320,12 +1305,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1341,12 +1349,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/french.txt b/data/languages/french.txt index 410c1e96b..05623a484 100644 --- a/data/languages/french.txt +++ b/data/languages/french.txt @@ -169,9 +169,6 @@ Favorites Feet == Pieds -Filter -== Filtre - Fire == Tirer @@ -184,9 +181,6 @@ Force vote Free-View == Vue libre -Friends -== Amis - Fullscreen == Plein écran @@ -380,12 +374,6 @@ Screenshot Server address: == Adresse du serveur : -Server details -== Détails du serveur - -Server filter -== Filtres du serveur - Server info == Info. serveur @@ -556,9 +544,6 @@ Netversion: Map: == Carte : -Info -== Info. - Hue == Teinte @@ -1727,6 +1712,23 @@ Unable to rename the folder No server selected == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Mark the beginning of a cut (right click to reset) == diff --git a/data/languages/galician.txt b/data/languages/galician.txt index 869a64aea..fbeece3c7 100644 --- a/data/languages/galician.txt +++ b/data/languages/galician.txt @@ -145,9 +145,6 @@ Favorites Feet == Pés -Filter -== Filtro - Fire == Disparar @@ -160,9 +157,6 @@ Force vote Free-View == Vista libre -Friends -== Amigos - Fullscreen == Pantalla completa @@ -359,12 +353,6 @@ Screenshot Server address: == IP do servidor: -Server details -== Detalles do servidor - -Server filter -== Filtro do servidor - Server info == Servidor @@ -536,9 +524,6 @@ Netversion: Map: == Mapa: -Info -== Información - Hue == Matiz @@ -1698,6 +1683,23 @@ Unable to rename the folder No server selected == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Mark the beginning of a cut (right click to reset) == diff --git a/data/languages/german.txt b/data/languages/german.txt index 853206e9b..1d9b74204 100644 --- a/data/languages/german.txt +++ b/data/languages/german.txt @@ -163,9 +163,6 @@ Favorites Feet == Füße -Filter -== Filter - Fire == Feuern @@ -178,9 +175,6 @@ Force vote Free-View == Freie Ansicht -Friends -== Freunde - Fullscreen == Vollbild @@ -377,12 +371,6 @@ Screenshot Server address: == Serveradresse: -Server details -== Serverdetails - -Server filter -== Filter - Server info == Serverinfo @@ -566,9 +554,6 @@ Netversion: Map: == Karte: -Info -== Info - Hue == Farb. @@ -1761,5 +1746,22 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == Im Spiel bewegt +Go back the specified duration +== Gesetzte Dauer zurück + +[Demo player duration] +%d min. +== %d Min. + +[Demo player duration] +%d sec. +== %d Sek. + +Change the skip duration +== Überspring-Dauer ändern + +Go forward the specified duration +== Gesetzte Dauer vorwärts + Render cut to video -== +== Schnitt zu Video rendern diff --git a/data/languages/greek.txt b/data/languages/greek.txt index 154bca9b7..60cd82208 100644 --- a/data/languages/greek.txt +++ b/data/languages/greek.txt @@ -145,9 +145,6 @@ Favorites Feet == Πόδια -Filter -== Φίλτρο - Fire == Πυρ @@ -160,9 +157,6 @@ Force vote Free-View == Ελεύθερη Όψη -Friends -== Φίλοι - Fullscreen == Πλήρης οθόνη @@ -359,12 +353,6 @@ Screenshot Server address: == Διεύθ/ση διακομιστή: -Server details -== Λεπτομέριες διακομιστή - -Server filter -== Φίλτρο διακομιστών - Server info == Πληροφορίες @@ -538,9 +526,6 @@ Netversion: Map: == Χάρτης: -Info -== Πληροφορίες - Hue == Απόχρωση @@ -914,12 +899,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -935,12 +943,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/hungarian.txt b/data/languages/hungarian.txt index a13f4e393..247fbf76d 100644 --- a/data/languages/hungarian.txt +++ b/data/languages/hungarian.txt @@ -144,9 +144,6 @@ Favorites Feet == Láb -Filter -== Szűrő - Fire == Tűz @@ -159,9 +156,6 @@ Force vote Free-View == Szabad nézet -Friends -== Barátok - Fullscreen == Teljesképernyő @@ -349,12 +343,6 @@ Screenshot Server address: == Szerver címe: -Server details -== Szerver részletei - -Server filter -== Szerver szűrő - Server info == Szerver infó @@ -511,9 +499,6 @@ Netversion: Map: == Pálya: -Info -== Infó - Miscellaneous == Egyéb @@ -1703,6 +1688,23 @@ None Add Clan == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Mark the beginning of a cut (right click to reset) == diff --git a/data/languages/italian.txt b/data/languages/italian.txt index 529c11720..ab5717f5d 100644 --- a/data/languages/italian.txt +++ b/data/languages/italian.txt @@ -152,9 +152,6 @@ Favorites Feet == Piedi -Filter -== Filtro - Fire == Fuoco @@ -167,9 +164,6 @@ Force vote Free-View == Visione libera -Friends -== Amici - Fullscreen == Schermo intero @@ -363,12 +357,6 @@ Screenshot Server address: == Indirizzo server: -Server details -== Dettagli server - -Server filter -== Filtro server - Server info == Info server @@ -536,9 +524,6 @@ Netversion: Map: == Mappa: -Info -== Info - Hue == Tinta @@ -1562,12 +1547,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1583,12 +1591,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/japanese.txt b/data/languages/japanese.txt index 04a4312e5..d9f48df32 100644 --- a/data/languages/japanese.txt +++ b/data/languages/japanese.txt @@ -145,9 +145,6 @@ Favorites Feet == 足 -Filter -== フィルタ - Fire == 射撃 @@ -160,9 +157,6 @@ Force vote Free-View == 自由視点 -Friends -== 友達 - Game == ゲーム @@ -353,12 +347,6 @@ Screenshot Server address: == IP アドレス: -Server details -== サーバー情報 - -Server filter -== サーバーフィルタ - Server info == サーバー情報 @@ -532,9 +520,6 @@ Miscellaneous Netversion: == 通信バージョン: -Info -== 情報 - UI Color == UI カラー @@ -1395,12 +1380,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1416,12 +1424,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/korean.txt b/data/languages/korean.txt index 1e23777d6..057614242 100644 --- a/data/languages/korean.txt +++ b/data/languages/korean.txt @@ -154,9 +154,6 @@ Favorites Feet == 발 -Filter -== 필터 - Fire == 발사 @@ -169,9 +166,6 @@ Force vote Free-View == 자유 시점 -Friends -== 친구 - Fullscreen == 전체화면 @@ -359,12 +353,6 @@ Screenshot Server address: == 서버 주소: -Server details -== 서버 세부정보 - -Server filter -== 서버 필터 - Server info == 서버 정보 @@ -550,9 +538,6 @@ Miscellaneous Netversion: == 통신 버전: -Info -== 정보 - UI Color == UI 색상 @@ -1715,6 +1700,23 @@ None Add Clan == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Mark the beginning of a cut (right click to reset) == diff --git a/data/languages/kyrgyz.txt b/data/languages/kyrgyz.txt index fe3b4294e..d7a46067a 100644 --- a/data/languages/kyrgyz.txt +++ b/data/languages/kyrgyz.txt @@ -150,9 +150,6 @@ Favorites Feet == Бут -Filter -== Фильтр - Fire == Атуу @@ -165,9 +162,6 @@ Force vote Free-View == Эркин сереп -Friends -== Достор - Fullscreen == Толук экран @@ -210,9 +204,6 @@ Hook Hue == Түсү -Info -== Маалымат - Internet == Интернет @@ -424,12 +415,6 @@ Screenshot Server address: == Сервер дареги: -Server details -== Сервер деталдары - -Server filter -== Сервер фильтри - Server info == Маалымат @@ -905,12 +890,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -926,12 +934,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/norwegian.txt b/data/languages/norwegian.txt index 2a02ee0d0..4860f940f 100644 --- a/data/languages/norwegian.txt +++ b/data/languages/norwegian.txt @@ -147,9 +147,6 @@ Favorites Feet == Føtter -Filter -== Filter - Fire == Skyt @@ -162,9 +159,6 @@ Force vote Free-View == Fri-visning -Friends -== Venner - Fullscreen == Fullskjerm @@ -358,12 +352,6 @@ Screenshot Server address: == Serveradresse: -Server details -== Serverdetaljer - -Server filter -== Serverfilter - Server info == Serverinfo @@ -534,9 +522,6 @@ Netversion: Map: == Bane: -Info -== Info - Hue == Farge @@ -1364,12 +1349,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1385,12 +1393,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/persian.txt b/data/languages/persian.txt index ca46aa734..46cee0d68 100644 --- a/data/languages/persian.txt +++ b/data/languages/persian.txt @@ -145,9 +145,6 @@ Favorites Feet == ﺎﭘ -Filter -== ﺮﺘﻠﯿﻓ - Fire == ﻚﯿﻠﺷ @@ -160,9 +157,6 @@ Force vote Free-View == ﺩﺍﺯﺁ-ﺪﯾﺩ -Friends -== ﻥﺎﺘﺳﻭﺩ - Fullscreen == ﻞﻣﺎﻛ ى ﻪﺤﻔﺻ @@ -472,9 +466,6 @@ Server address: Refresh == ﯼﺯﺎﺳ ﻩﺯﺎﺗ -Server filter -== ﺭﻭﺮﺳ ﺮﺘﻠﯿﻓ - Server not full == ﺪﺷﺎﺒﻧ ﺮﭘ ﺭﻭﺮﺳ @@ -505,18 +496,12 @@ Types Reset filter == ﺮﺘﻠﯿﻓ ﻥﺩﺮﮐ ﺖﺴﯾﺭ -Server details -== ﺭﻭﺮﺳ ﺕﺎﻋﻼﻃﺍ - Scoreboard == ﺕﺍﺯﺎﯿﺘﻣﺍ ﯼﻮﻠﺑﺎﺗ Remove == ﻥﺩﺮﮐ کﺎﭘ -Info -== ﺕﺎﻋﻼﻃﺍ - Please use a different name == ﻦﮐ ﻩﺩﺎﻔﺘﺳﺍ ﺕﻭﺎﻔﺘﻣ ﻡﺎﻧ ﮏﯾ ﺯﺍ ﺎﻔﻄﻟ @@ -1685,6 +1670,23 @@ None Add Clan == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Mark the beginning of a cut (right click to reset) == diff --git a/data/languages/polish.txt b/data/languages/polish.txt index 000e23a98..ecbee3356 100644 --- a/data/languages/polish.txt +++ b/data/languages/polish.txt @@ -149,9 +149,6 @@ Favorites Feet == Stopy -Filter -== Filtr - Fire == Strzał @@ -164,9 +161,6 @@ Force vote Free-View == Wolna kamera -Friends -== Znajomi - Fullscreen == Pełny ekran @@ -360,12 +354,6 @@ Screenshot Server address: == Adres serwera: -Server details -== Szczegóły serwera - -Server filter -== Filtr serwerów - Server info == Info serwera @@ -551,9 +539,6 @@ Netversion: Map: == Mapa: -Info -== Info - Hue == Kolor @@ -1465,12 +1450,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1486,12 +1494,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/portuguese.txt b/data/languages/portuguese.txt index 525155ea5..871cbad50 100644 --- a/data/languages/portuguese.txt +++ b/data/languages/portuguese.txt @@ -152,9 +152,6 @@ Favorites Feet == Pés -Filter -== Filtro - Fire == Disparar @@ -167,9 +164,6 @@ Force vote Free-View == Vista Livre -Friends -== Amigos - Fullscreen == Ecrã inteiro @@ -375,12 +369,6 @@ Screenshot Server address: == Endereço do servidor: -Server details -== Detalhes do servidor - -Server filter -== Filtro de servidores - Server info == Info de servidor @@ -549,9 +537,6 @@ Map: FSAA samples == Amostras FSAA -Info -== Info - Miscellaneous == Diversos @@ -1183,12 +1168,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1204,12 +1212,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/romanian.txt b/data/languages/romanian.txt index fa6099415..599baffa5 100644 --- a/data/languages/romanian.txt +++ b/data/languages/romanian.txt @@ -151,9 +151,6 @@ Favorites Feet == Picioare -Filter -== Filtre - Fire == Foc @@ -166,9 +163,6 @@ Force vote Free-View == Vizualizare liberă -Friends -== Prieteni - Fullscreen == Ecrat complet @@ -365,12 +359,6 @@ Screenshot Server address: == Adresă server: -Server details -== Detalii server - -Server filter -== Filtru servere: - Server info == Info. server @@ -544,9 +532,6 @@ Netversion: Map: == Harta: -Info -== Informații - Hue == Tentă @@ -920,12 +905,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -941,12 +949,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/russian.txt b/data/languages/russian.txt index f6ce50c37..a0d3642ed 100644 --- a/data/languages/russian.txt +++ b/data/languages/russian.txt @@ -159,9 +159,6 @@ Favorites Feet == Ноги -Filter -== Фильтр - Fire == Стрелять @@ -174,9 +171,6 @@ Force vote Free-View == Свободный обзор -Friends -== Друзья - Fullscreen == Полноэкранный(настр.) @@ -370,12 +364,6 @@ Screenshot Server address: == Адрес сервера: -Server details -== Сведения сервера - -Server filter -== Фильтр серверов - Server info == Информация @@ -607,9 +595,6 @@ Netversion: Map: == Карта: -Info -== Инфо - Hue == Оттен. @@ -1757,5 +1742,22 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == Перемещены в игре +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Render cut to video == diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt index a036086b1..9c0c40541 100644 --- a/data/languages/serbian.txt +++ b/data/languages/serbian.txt @@ -149,9 +149,6 @@ Favorites Feet == Stopala -Filter -== Filter - Fire == Pucaj @@ -164,9 +161,6 @@ Force vote Free-View == Slobodan pregled -Friends -== Prijatelji - Fullscreen == Preko celog ekrana @@ -356,12 +350,6 @@ Screenshot Server address: == Adresa servera: -Server details -== Detalji o serveru - -Server filter -== Filter servera - Server info == O serveru @@ -562,9 +550,6 @@ Netversion: Map: == Mapa -Info -== Info - Hue == Nijansa @@ -1507,10 +1492,6 @@ A render command failed. Try to update your GPU drivers. Submitting the render commands failed. Try to update your GPU drivers. == Неуспешно слање рендерних команди. Покушајте ажурирати драјвере за вашу графичку картицу. -Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. -== Неуспешно замена бафера фреймова. Покушајте ажурирати драјвере за вашу графичку картицу. - -[Graphics error] Could not initialize the given graphics backend, reverting to the default backend now. == Није било могуће иницијализовати дати графички позадину, сада се враћамо на подразумевану позадину. @@ -1748,5 +1729,26 @@ Moved ingame Failed to swap framebuffers. Try to update your GPU drivers. == +[Graphics error] +Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. +== + +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Render cut to video == diff --git a/data/languages/serbian_cyrillic.txt b/data/languages/serbian_cyrillic.txt index ccbe933be..07f602eda 100644 --- a/data/languages/serbian_cyrillic.txt +++ b/data/languages/serbian_cyrillic.txt @@ -145,9 +145,6 @@ Favorites Feet == Стопала -Filter -== Филтер - Fire == Пуцај @@ -160,9 +157,6 @@ Force vote Free-View == Слободан преглед -Friends -== Пријатељи - Fullscreen == Преко целог екрана @@ -352,12 +346,6 @@ Screenshot Server address: == Адреса сервера: -Server details -== Детаљи о серверу - -Server filter -== Филтер сервера - Server info == О серверу @@ -558,9 +546,6 @@ Netversion: Map: == Мапа -Info -== Инфо - Hue == Нијанса @@ -1601,12 +1586,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -1622,12 +1630,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/simplified_chinese.txt b/data/languages/simplified_chinese.txt index f1dccce9f..c3016e9d2 100644 --- a/data/languages/simplified_chinese.txt +++ b/data/languages/simplified_chinese.txt @@ -177,9 +177,6 @@ Favorites Feet == 脚 -Filter -== 筛选 - Fire == 开火 @@ -192,9 +189,6 @@ Force vote Free-View == 自由视角 -Friends -== 好友 - Fullscreen == 独占全屏 @@ -415,12 +409,6 @@ Screenshot Server address: == 服务器地址: -Server details -== 服务器详情 - -Server filter -== 服务器筛选 - Server info == 服务器信息 @@ -616,9 +604,6 @@ Netversion: Map: == 地图: -Info -== 信息 - Hue == 色调 @@ -1781,5 +1766,22 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == 游戏内移动 +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Render cut to video == diff --git a/data/languages/slovak.txt b/data/languages/slovak.txt index 3c6d21bd7..98fb93d3a 100644 --- a/data/languages/slovak.txt +++ b/data/languages/slovak.txt @@ -145,9 +145,6 @@ Favorites Feet == Nohy -Filter -== Filter - Fire == Streľba @@ -160,9 +157,6 @@ Force vote Free-View == Voľná Kamera -Friends -== Priatelia - Fullscreen == Celá obrazovka @@ -356,12 +350,6 @@ Screenshot Server address: == Adresa servera: -Server details -== Detaily servera - -Server filter -== Filter serverov - Server info == Informácie @@ -532,9 +520,6 @@ Netversion: Map: == Mapa: -Info -== Info - Hue == Hue @@ -911,12 +896,35 @@ Pause the current demo Stop the current demo == +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Go back one tick == Go forward one tick == +Go back one marker +== + +Go forward one marker +== + Slow down the demo == @@ -932,12 +940,6 @@ Mark the end of a cut (right click to reset) Export cut as a separate demo == -Go back one marker -== - -Go forward one marker -== - Close the demo player == diff --git a/data/languages/spanish.txt b/data/languages/spanish.txt index 947d291f7..5746d9ee8 100644 --- a/data/languages/spanish.txt +++ b/data/languages/spanish.txt @@ -164,9 +164,6 @@ Favorites Feet == Pies -Filter -== Filtro - Fire == Disparar @@ -179,9 +176,6 @@ Force vote Free-View == Vista libre -Friends -== Amigos - Fullscreen == Pantalla completa @@ -378,12 +372,6 @@ Screenshot Server address: == IP del servidor: -Server details -== Detalles del servidor - -Server filter -== Filtro del servidor - Server info == Servidor @@ -557,9 +545,6 @@ Netversion: Map: == Mapa: -Info -== Información - Hue == Matiz @@ -1764,5 +1749,22 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == Movido dentro del juego +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Render cut to video == diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt index 212739c23..c80144d50 100644 --- a/data/languages/swedish.txt +++ b/data/languages/swedish.txt @@ -150,9 +150,6 @@ Favorites Feet == Fötter -Filter -== Filter - Fire == Skjuta @@ -165,9 +162,6 @@ Force vote Free-View == Friläge -Friends -== Kompisar - Fullscreen == Fullskärm @@ -361,12 +355,6 @@ Screenshot Server address: == Serveradress -Server details -== Serverdetaljer - -Server filter -== Serverfilter - Server info == Serverinfo @@ -535,9 +523,6 @@ Netversion: Map: == Bana -Info -== Info - Hue == Nyans @@ -1750,3 +1735,20 @@ Moved ingame Render cut to video == Rendera snitt till video + +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== diff --git a/data/languages/traditional_chinese.txt b/data/languages/traditional_chinese.txt index 0f66cdad1..eef29643e 100644 --- a/data/languages/traditional_chinese.txt +++ b/data/languages/traditional_chinese.txt @@ -166,9 +166,6 @@ Favorites Feet == 腳 -Filter -== 過濾器 - Fire == 開火 @@ -181,9 +178,6 @@ Force vote Free-View == 自由視角 -Friends -== 好友 - Fullscreen == 獨占全螢幕 @@ -404,12 +398,6 @@ Screenshot Server address: == 伺服器地址: -Server details -== 伺服器詳細資訊 - -Server filter -== 伺服器過濾器 - Server info == 伺服器資訊 @@ -605,9 +593,6 @@ Netversion: Map: == 地圖: -Info -== 資訊 - Hue == 色調 @@ -1770,5 +1755,22 @@ Unable to delete the folder '%s'. Make sure it's empty first. Moved ingame == 遊戲內移動 +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Render cut to video == diff --git a/data/languages/turkish.txt b/data/languages/turkish.txt index 2d232b228..c433123a5 100644 --- a/data/languages/turkish.txt +++ b/data/languages/turkish.txt @@ -152,9 +152,6 @@ Favorites Feet == Ayak -Filter -== Filtre - Fire == Ateş @@ -167,9 +164,6 @@ Force vote Free-View == Serbest Bakış -Friends -== Arkadaşlar - Fullscreen == Tam ekran @@ -360,12 +354,6 @@ Screenshot Server address: == Sunucu adresi: -Server details -== Sunucucu detayları - -Server filter -== Filtreler - Server info == Hakkında @@ -534,9 +522,6 @@ Netversion: Map: == Harita: -Info -== Bilgi - Hue == Renk @@ -1752,5 +1737,22 @@ Loading sound files Moved ingame == Hareket edildi +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Render cut to video == diff --git a/data/languages/ukrainian.txt b/data/languages/ukrainian.txt index 55d5c5000..ca9285dac 100644 --- a/data/languages/ukrainian.txt +++ b/data/languages/ukrainian.txt @@ -94,9 +94,6 @@ Favorites Feet == Ноги -Filter -== Фільтр - Fire == Постріл @@ -266,9 +263,6 @@ Scoreboard Screenshot == Скріншот -Server details -== Деталі сервера - Server info == Інформація @@ -379,9 +373,6 @@ FSAA samples Your skin == Ваш скін -Info -== Інформація - Hue == Відтінок @@ -532,9 +523,6 @@ Search Exclude == Виключити -Server filter -== Фільтр серверів - Count players only == Рахувати тільки гравців @@ -565,9 +553,6 @@ Countries Types == Типи -Friends -== Друзі - Remove == Видалити @@ -1745,5 +1730,22 @@ Graphics card Moved ingame == Перемістився в грі +Go back the specified duration +== + +[Demo player duration] +%d min. +== + +[Demo player duration] +%d sec. +== + +Change the skip duration +== + +Go forward the specified duration +== + Render cut to video == From c23096f7add3d35c22d72b3ed33256e1bd8020cd Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 17 Sep 2023 19:59:13 +0200 Subject: [PATCH 126/126] Update swedish translations for 17.3 --- data/languages/swedish.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt index c80144d50..808c9b73d 100644 --- a/data/languages/swedish.txt +++ b/data/languages/swedish.txt @@ -1737,18 +1737,18 @@ Render cut to video == Rendera snitt till video Go back the specified duration -== +== Gå tillbaka den angivna tiden [Demo player duration] %d min. -== +== %d min. [Demo player duration] %d sec. -== +== %d sek. Change the skip duration -== +== Ändra tiden för framspolning Go forward the specified duration -== +== Gå framåt den angivna tiden