From 325989ff2df243b632da2275d7eef337e8db4eec Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 16 Dec 2024 00:48:56 +0800 Subject: [PATCH 01/13] Core --- CREDITS.md | 1 + Phobos.vcxproj | 2 + docs/User-Interface.md | 11 ++ docs/Whats-New.md | 1 + src/Commands/Commands.cpp | 3 + src/Commands/DistributionMode.cpp | 215 ++++++++++++++++++++++++++++++ src/Commands/DistributionMode.h | 27 ++++ 7 files changed, 260 insertions(+) create mode 100644 src/Commands/DistributionMode.cpp create mode 100644 src/Commands/DistributionMode.h diff --git a/CREDITS.md b/CREDITS.md index f977b2a3ed..56bbf02df6 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -371,6 +371,7 @@ This page lists all the individual contributions to the project by their author. - Allow to change the speed of gas particles - **CrimRecya** - Fix `LimboKill` not working reliably + - Distribution click action mode - **Ollerus** - Build limit group enhancement - Customizable rocker amplitude diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 79495c5f4c..917e9e3566 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -31,6 +31,7 @@ + @@ -196,6 +197,7 @@ + diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 2e1ad86969..fcac944e00 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -317,6 +317,17 @@ SelectionFlashDuration=0 ; integer, number of frames - Switches on/off [frame by frame mode](Miscellanous.html#frame-step-in). - For localization add `TXT_FRAME_BY_FRAME` and `TXT_FRAME_BY_FRAME_DESC` into your `.csf` file. +### `[ ]` Distribution Mode Spread / Filter + +- Change the click action. + - When the range is 0, it is the original default behavior of the game. The range can be adjusted to 4, 8 or 16 cells by shortcut keys. + - The targets within the range will be allocated equally to the selected technos. Only when the behavior to be performed by the current techno is the same as that displayed by the mouse will it be allocated. Otherwise, it will return to the original default behavior of the game (it will not be effective for technos in the air) + - When the filter is `None`, it is the default behavior of the game. You can adjust the filter mode to: + - `Auto` - if the behavior to be executed by the current techno is different from the behavior displayed by the mouse, and the behavior to be executed will make the techno move near the target, the behavior will be replaced with area guard. + - `Type` - on the basis of `Auto`, only targets of the same type (like infantries, vehicles or buildings) will be selected among the targets allocated in the range. + - `Name` - on the basis of `Type`, only targets of the same name (or with the same `GroupAs`) will be selected among the targets allocated in the range. +- For localization add `TXT_DISTR_SPREAD`, `TXT_DISTR_FILTER`, `TXT_DISTR_SPREAD_DESC` and `TXT_DISTR_FILTER_DESC` into your `.csf` file. + ## Loading screen - PCX files can now be used as loadscreen images. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 0b6f8c19c6..85fd153e58 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -469,6 +469,7 @@ New: - Allow infantry to use land sequences in water (by Starkku) - `` can now be used as owner for pre-placed objects on skirmish and multiplayer maps (by Starkku) - Allow customizing charge turret delays per burst on a weapon (by Starkku) +- Distribution click action mode (by CrimRecya) - Unit `Speed` setting now accepts floating point values (by Starkku) Vanilla fixes: diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index 712a596467..67aa534ae3 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -10,6 +10,7 @@ #include "ToggleDigitalDisplay.h" #include "ToggleDesignatorRange.h" #include "SaveVariablesToFile.h" +#include "DistributionMode.h" DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) { @@ -19,6 +20,8 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); MakeCommand(); + MakeCommand(); + MakeCommand(); if (Phobos::Config::DevelopmentCommands) { diff --git a/src/Commands/DistributionMode.cpp b/src/Commands/DistributionMode.cpp new file mode 100644 index 0000000000..4ccf19709d --- /dev/null +++ b/src/Commands/DistributionMode.cpp @@ -0,0 +1,215 @@ +#include "DistributionMode.h" + +#include +#include +#include + +#include +#include + +int DistributionMode1CommandClass::Mode = 0; +int DistributionMode2CommandClass::Mode = 0; + +void __fastcall PrintDistributionModeMessage() +{ + wchar_t text[0x40]; + swprintf_s(text, L"Distribution Mode < Spread: r=%2d , Filter: %s >", + (DistributionMode1CommandClass::Mode ? (2 << DistributionMode1CommandClass::Mode) : 0), + ((DistributionMode2CommandClass::Mode > 1) ? ((DistributionMode2CommandClass::Mode == 3) ? L"Name" : L"Type") : (DistributionMode2CommandClass::Mode == 1) ? L"Auto" : L"None") + ); + MessageListClass::Instance->PrintMessage(text, 200, 5, true); +} + +const char* DistributionMode1CommandClass::GetName() const +{ + return "Distribution Mode Spread"; +} + +const wchar_t* DistributionMode1CommandClass::GetUIName() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_SPREAD", L"Distribution spread"); +} + +const wchar_t* DistributionMode1CommandClass::GetUICategory() const +{ + return CATEGORY_CONTROL; +} + +const wchar_t* DistributionMode1CommandClass::GetUIDescription() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_SPREAD_DESC", L"Automatically and averagely select similar targets around the original target. This is for changing the search range"); +} + +void DistributionMode1CommandClass::Execute(WWKey eInput) const +{ + DistributionMode1CommandClass::Mode = ((DistributionMode1CommandClass::Mode + 1) & 3); + PrintDistributionModeMessage(); +} + +const char* DistributionMode2CommandClass::GetName() const +{ + return "Distribution Mode Filter"; +} + +const wchar_t* DistributionMode2CommandClass::GetUIName() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_FILTER", L"Distribution filter"); +} + +const wchar_t* DistributionMode2CommandClass::GetUICategory() const +{ + return CATEGORY_CONTROL; +} + +const wchar_t* DistributionMode2CommandClass::GetUIDescription() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_FILTER_DESC", L"Automatically and averagely select similar targets around the original target. This is for changing the filter criteria"); +} + +void DistributionMode2CommandClass::Execute(WWKey eInput) const +{ + DistributionMode2CommandClass::Mode = ((DistributionMode2CommandClass::Mode + 1) & 3); + PrintDistributionModeMessage(); +} + +DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) +{ + enum { SkipGameCode = 0x4AE85C }; + + GET(ObjectClass* const, pTarget, EBP); + GET_STACK(const Action, mouseAction, STACK_OFFSET(0x20, 0xC)); + + const auto count = ObjectClass::CurrentObjects->Count; + + if (count > 0) + { + const auto mode1 = DistributionMode1CommandClass::Mode; + const auto mode2 = DistributionMode2CommandClass::Mode; + + // Distribution mode main + if (mode1 && count > 1 && mouseAction != Action::NoMove && !PlanningNodeClass::PlanningModeActive && (pTarget->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None && !pTarget->IsInAir()) + { + const auto pSpecial = HouseClass::FindSpecial(); + const auto pCivilian = HouseClass::FindCivilianSide(); + const auto pNeutral = HouseClass::FindNeutral(); + + const auto pTargetHouse = static_cast(pTarget)->Owner; + const bool targetIsNeutral = pTargetHouse == pSpecial || pTargetHouse == pCivilian || pTargetHouse == pNeutral; + + const auto range = (2 << mode1); + const auto pItems = Helpers::Alex::getCellSpreadItems(pTarget->Location, range); + std::map record; + int current = 1; + + for (const auto& pItem : pItems) + record[pItem] = 0; + + for (const auto& pSelect : ObjectClass::CurrentObjects()) + { + TechnoClass* pCanTarget = nullptr; + TechnoClass* pNewTarget = nullptr; + + for (const auto& [pItem, num] : record) + { + if (pSelect->MouseOverObject(pItem) == mouseAction && (targetIsNeutral || (pItem->Owner != pSpecial && pItem->Owner != pCivilian && pItem->Owner != pNeutral)) + && (mode2 < 2 || (pItem->WhatAmI() == pTarget->WhatAmI() + && (mode2 < 3 || TechnoTypeExt::GetSelectionGroupID(pItem->GetTechnoType()) == TechnoTypeExt::GetSelectionGroupID(pTarget->GetTechnoType()))))) + { + pCanTarget = pItem; + + if (num < current) + { + pNewTarget = pCanTarget; + break; + } + } + } + + if (!pNewTarget) + { + if (pCanTarget) + { + ++current; + pNewTarget = pCanTarget; + } + } + + if (pNewTarget) + { + if (record.contains(pNewTarget)) + ++record[pNewTarget]; + + pSelect->ObjectClickedAction(mouseAction, pNewTarget, false); + } + else + { + const auto currentAction = pSelect->MouseOverObject(pTarget); + + if (mode2 && currentAction == Action::NoMove && (pSelect->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None) + static_cast(pSelect)->ClickedMission(Mission::Area_Guard, reinterpret_cast(pSelect->GetCellAgain()), nullptr, nullptr); + else + pSelect->ObjectClickedAction(currentAction, pTarget, false); + } + + Unsorted::MoveFeedback = false; + } + } + else // Vanilla + { + for (const auto& pSelect : ObjectClass::CurrentObjects()) + { + const auto currentAction = pSelect->MouseOverObject(pTarget); + + if (mode2 && mouseAction != Action::NoMove && currentAction == Action::NoMove && (pSelect->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None) + static_cast(pSelect)->ClickedMission(Mission::Area_Guard, reinterpret_cast(pSelect->GetCellAgain()), nullptr, nullptr); + else + pSelect->ObjectClickedAction(currentAction, pTarget, false); + + Unsorted::MoveFeedback = false; + } + } + } + + return SkipGameCode; +} +/* +DEFINE_HOOK(0x4F4596, GScreenClass_DrawOnTop_DrawDistributionModeShape, 0x7) +{ + const auto mode1 = DistributionMode1CommandClass::Mode; + const auto mode2 = DistributionMode2CommandClass::Mode; + + if (mode1 || mode2) + { + auto position = WWMouseClass::Instance->XY1; + RectangleStruct rect = DSurface::Composite->GetRect(); + rect.Height -= 32; + + if (position.X < rect.Width && position.Y < rect.Height) + { + position.Y += 20; + const auto mode2Text = ((mode2 > 1) ? ((mode2 == 3) ? L"Name" : L"Type") : (mode2 == 1) ? L"Auto" : L"None"); + wchar_t text[0x20]; + swprintf_s(text, L"%s:%2d", mode2Text, (mode1 ? (2 << mode1) : 0)); + RectangleStruct dim = Drawing::GetTextDimensions(text, Point2D::Empty, 0); + dim.Width += 4; + const int dX = rect.Width - dim.Width; + const int dY = rect.Height - dim.Height; + + if (position.X >= dX) + position.X = dX; + + if (position.Y >= dY) + position.Y = dY; + + dim.X = position.X; + dim.Y = position.Y; + ColorStruct color { 0, 0, 0 }; + DSurface::Composite->FillRectTrans(&dim, &color, 40); + position.X += 2; + DSurface::Composite->DrawTextA(text, &rect, &position, COLOR_WHITE, COLOR_BLACK, TextPrintType::LightShadow); + } + } + + return 0; +} +*/ diff --git a/src/Commands/DistributionMode.h b/src/Commands/DistributionMode.h new file mode 100644 index 0000000000..292ff49b2b --- /dev/null +++ b/src/Commands/DistributionMode.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Commands.h" + +class DistributionMode1CommandClass : public CommandClass +{ +public: + static int Mode; + + virtual const char* GetName() const override; + virtual const wchar_t* GetUIName() const override; + virtual const wchar_t* GetUICategory() const override; + virtual const wchar_t* GetUIDescription() const override; + virtual void Execute(WWKey eInput) const override; +}; + +class DistributionMode2CommandClass : public CommandClass +{ +public: + static int Mode; + + virtual const char* GetName() const override; + virtual const wchar_t* GetUIName() const override; + virtual const wchar_t* GetUICategory() const override; + virtual const wchar_t* GetUIDescription() const override; + virtual void Execute(WWKey eInput) const override; +}; From 6edc48e44c0980bd55715cb9ffc66d70355d093b Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sun, 26 Jan 2025 19:11:39 +0800 Subject: [PATCH 02/13] Update Whats-New.md --- docs/Whats-New.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index fab381ac63..33c10ebb8d 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -314,6 +314,7 @@ New: - Enable building production queue (by CrimRecya) - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) - Custom exit cell for infantry factory (by Starkku) +- Distribution click action mode (by CrimRecya) Vanilla fixes: - Aircraft will now behave as expected according to it's `MovementZone` and `SpeedType` when moving onto different surfaces. In particular, this fixes erratic behavior when vanilla aircraft is ordered to move onto water surface and instead the movement order changes to a shore nearby (by CrimRecya) @@ -485,7 +486,6 @@ New: - Allow infantry to use land sequences in water (by Starkku) - `` can now be used as owner for pre-placed objects on skirmish and multiplayer maps (by Starkku) - Allow customizing charge turret delays per burst on a weapon (by Starkku) -- Distribution click action mode (by CrimRecya) - Unit `Speed` setting now accepts floating point values (by Starkku) Vanilla fixes: From 73a6c0b2d79f97020ba7a1d20c22b0b30ec52148 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sun, 26 Jan 2025 19:11:58 +0800 Subject: [PATCH 03/13] Draw at mouse --- src/Commands/DistributionMode.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Commands/DistributionMode.cpp b/src/Commands/DistributionMode.cpp index 4ccf19709d..a2108fa6e2 100644 --- a/src/Commands/DistributionMode.cpp +++ b/src/Commands/DistributionMode.cpp @@ -172,7 +172,7 @@ DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) return SkipGameCode; } -/* + DEFINE_HOOK(0x4F4596, GScreenClass_DrawOnTop_DrawDistributionModeShape, 0x7) { const auto mode1 = DistributionMode1CommandClass::Mode; @@ -212,4 +212,3 @@ DEFINE_HOOK(0x4F4596, GScreenClass_DrawOnTop_DrawDistributionModeShape, 0x7) return 0; } -*/ From 97c070dae3584683f6fcabf13066ff93ed0c76ae Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sun, 26 Jan 2025 20:22:40 +0800 Subject: [PATCH 04/13] Show range ring --- docs/User-Interface.md | 10 +++---- src/Commands/DistributionMode.cpp | 48 ++++--------------------------- 2 files changed, 11 insertions(+), 47 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index b915ea2fa2..0aa2994d25 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -323,11 +323,11 @@ SelectionFlashDuration=0 ; integer, number of frames - Change the click action. - When the range is 0, it is the original default behavior of the game. The range can be adjusted to 4, 8 or 16 cells by shortcut keys. - - The targets within the range will be allocated equally to the selected technos. Only when the behavior to be performed by the current techno is the same as that displayed by the mouse will it be allocated. Otherwise, it will return to the original default behavior of the game (it will not be effective for technos in the air) - - When the filter is `None`, it is the default behavior of the game. You can adjust the filter mode to: - - `Auto` - if the behavior to be executed by the current techno is different from the behavior displayed by the mouse, and the behavior to be executed will make the techno move near the target, the behavior will be replaced with area guard. - - `Type` - on the basis of `Auto`, only targets of the same type (like infantries, vehicles or buildings) will be selected among the targets allocated in the range. - - `Name` - on the basis of `Type`, only targets of the same name (or with the same `GroupAs`) will be selected among the targets allocated in the range. + - The targets within the range will be allocated equally to the selected technos. Only when the behavior to be performed by the current techno is the same as that displayed by the mouse will it be allocated. Otherwise, it will return to the original default behavior of the game (it will not be effective for technos in the air). This will display a range ring. + - When the filter is `None`, it is the default behavior of the game. If the range is not zero at this time, a green ring will be displayed. You can adjust the filter mode to: + - `Auto` - if the behavior to be executed by the current techno is different from the behavior displayed by the mouse, and the behavior to be executed will make the techno move near the target, the behavior will be replaced with area guard. At this time, a blue ring will be displayed. + - `Type` - on the basis of `Auto`, only targets of the same type (like infantries, vehicles or buildings) will be selected among the targets allocated in the range. At this time, a yellow ring will be displayed. + - `Name` - on the basis of `Type`, only targets of the same name (or with the same `GroupAs`) will be selected among the targets allocated in the range. At this time, a red ring will be displayed. - For localization add `TXT_DISTR_SPREAD`, `TXT_DISTR_FILTER`, `TXT_DISTR_SPREAD_DESC` and `TXT_DISTR_FILTER_DESC` into your `.csf` file. ## Loading screen diff --git a/src/Commands/DistributionMode.cpp b/src/Commands/DistributionMode.cpp index a2108fa6e2..1c7b4f0f5a 100644 --- a/src/Commands/DistributionMode.cpp +++ b/src/Commands/DistributionMode.cpp @@ -5,21 +5,10 @@ #include #include -#include int DistributionMode1CommandClass::Mode = 0; int DistributionMode2CommandClass::Mode = 0; -void __fastcall PrintDistributionModeMessage() -{ - wchar_t text[0x40]; - swprintf_s(text, L"Distribution Mode < Spread: r=%2d , Filter: %s >", - (DistributionMode1CommandClass::Mode ? (2 << DistributionMode1CommandClass::Mode) : 0), - ((DistributionMode2CommandClass::Mode > 1) ? ((DistributionMode2CommandClass::Mode == 3) ? L"Name" : L"Type") : (DistributionMode2CommandClass::Mode == 1) ? L"Auto" : L"None") - ); - MessageListClass::Instance->PrintMessage(text, 200, 5, true); -} - const char* DistributionMode1CommandClass::GetName() const { return "Distribution Mode Spread"; @@ -43,7 +32,6 @@ const wchar_t* DistributionMode1CommandClass::GetUIDescription() const void DistributionMode1CommandClass::Execute(WWKey eInput) const { DistributionMode1CommandClass::Mode = ((DistributionMode1CommandClass::Mode + 1) & 3); - PrintDistributionModeMessage(); } const char* DistributionMode2CommandClass::GetName() const @@ -69,7 +57,6 @@ const wchar_t* DistributionMode2CommandClass::GetUIDescription() const void DistributionMode2CommandClass::Execute(WWKey eInput) const { DistributionMode2CommandClass::Mode = ((DistributionMode2CommandClass::Mode + 1) & 3); - PrintDistributionModeMessage(); } DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) @@ -173,41 +160,18 @@ DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) return SkipGameCode; } -DEFINE_HOOK(0x4F4596, GScreenClass_DrawOnTop_DrawDistributionModeShape, 0x7) +DEFINE_HOOK(0x6DBE74, TacticalClass_DrawAllRadialIndicators_DrawDistributionRange, 0x7) { const auto mode1 = DistributionMode1CommandClass::Mode; const auto mode2 = DistributionMode2CommandClass::Mode; if (mode1 || mode2) { - auto position = WWMouseClass::Instance->XY1; - RectangleStruct rect = DSurface::Composite->GetRect(); - rect.Height -= 32; - - if (position.X < rect.Width && position.Y < rect.Height) - { - position.Y += 20; - const auto mode2Text = ((mode2 > 1) ? ((mode2 == 3) ? L"Name" : L"Type") : (mode2 == 1) ? L"Auto" : L"None"); - wchar_t text[0x20]; - swprintf_s(text, L"%s:%2d", mode2Text, (mode1 ? (2 << mode1) : 0)); - RectangleStruct dim = Drawing::GetTextDimensions(text, Point2D::Empty, 0); - dim.Width += 4; - const int dX = rect.Width - dim.Width; - const int dY = rect.Height - dim.Height; - - if (position.X >= dX) - position.X = dX; - - if (position.Y >= dY) - position.Y = dY; - - dim.X = position.X; - dim.Y = position.Y; - ColorStruct color { 0, 0, 0 }; - DSurface::Composite->FillRectTrans(&dim, &color, 40); - position.X += 2; - DSurface::Composite->DrawTextA(text, &rect, &position, COLOR_WHITE, COLOR_BLACK, TextPrintType::LightShadow); - } + const auto pCell = MapClass::Instance->GetCellAt(DisplayClass::Instance->CurrentFoundation_CenterCell); + const auto color = ((mode2 > 1) + ? ((mode2 == 3) ? ColorStruct { 255, 0, 0 } : ColorStruct { 200, 200, 0 }) + : (mode2 == 1) ? ColorStruct { 0, 100, 255 } : ColorStruct { 0, 255, 50 }); + Game::DrawRadialIndicator(false, true, pCell->GetCoords(), color, (mode1 ? static_cast(2 << mode1) : 0.5), false, true); } return 0; From e2426699dae0d3fb12ab818d17a28f2c159cd7c4 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Sun, 26 Jan 2025 21:12:12 +0800 Subject: [PATCH 05/13] Fix a typo --- src/Commands/DistributionMode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/DistributionMode.cpp b/src/Commands/DistributionMode.cpp index 1c7b4f0f5a..854e66fe7c 100644 --- a/src/Commands/DistributionMode.cpp +++ b/src/Commands/DistributionMode.cpp @@ -171,7 +171,7 @@ DEFINE_HOOK(0x6DBE74, TacticalClass_DrawAllRadialIndicators_DrawDistributionRang const auto color = ((mode2 > 1) ? ((mode2 == 3) ? ColorStruct { 255, 0, 0 } : ColorStruct { 200, 200, 0 }) : (mode2 == 1) ? ColorStruct { 0, 100, 255 } : ColorStruct { 0, 255, 50 }); - Game::DrawRadialIndicator(false, true, pCell->GetCoords(), color, (mode1 ? static_cast(2 << mode1) : 0.5), false, true); + Game::DrawRadialIndicator(false, true, pCell->GetCoords(), color, static_cast(mode1 ? (2 << mode1) : 0.5), false, true); } return 0; From ab9c046302a29dd3cc70531bbef3b4661aa821e4 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 3 Feb 2025 02:21:17 +0800 Subject: [PATCH 06/13] Fix target cloaked units --- src/Commands/DistributionMode.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Commands/DistributionMode.cpp b/src/Commands/DistributionMode.cpp index 854e66fe7c..c72fd8b3ff 100644 --- a/src/Commands/DistributionMode.cpp +++ b/src/Commands/DistributionMode.cpp @@ -89,7 +89,10 @@ DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) int current = 1; for (const auto& pItem : pItems) - record[pItem] = 0; + { + if (pItem->CloakState != CloakState::Cloaked || pItem->GetCell()->Sensors_InclHouse(HouseClass::CurrentPlayer->ArrayIndex)) + record[pItem] = 0; + } for (const auto& pSelect : ObjectClass::CurrentObjects()) { From 32c90bded6f720ad15594d11a32c5f24e11b0fcd Mon Sep 17 00:00:00 2001 From: Noble Fish <89088785+DeathFishAtEase@users.noreply.github.com> Date: Mon, 3 Feb 2025 04:12:50 +0800 Subject: [PATCH 07/13] ")" --- docs/Fixed-or-Improved-Logics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index d39932ba2b..5606546923 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -1124,7 +1124,7 @@ Palette= ; filename - excluding .pal extension and three-character the - Instead of showing at 1 point of HP left, TerrainTypes switch to damaged frames once their health reaches `[AudioVisual]` -> `ConditionYellow.Terrain` percentage of their maximum health. Defaults to `ConditionYellow` if not set. - In addition, TerrainTypes can now show 'crumbling' animation after their health has reached zero and before they are deleted from the map by setting `HasCrumblingFrames` to true. - Crumbling frames start from first frame after both regular & damaged frames and ends at halfway point of the frames in TerrainType's image. - - Note that the number of regular & damage frames considered for this depends on value of `HasDamagedFrames` and for `IsAnimated` TerrainTypes, `AnimationLength` (see [Animated TerrainTypes](#animated-terraintypes). Exercise caution and ensure there are correct amount of frames to display. + - Note that the number of regular & damage frames considered for this depends on value of `HasDamagedFrames` and for `IsAnimated` TerrainTypes, `AnimationLength` (see [Animated TerrainTypes](#animated-terraintypes)). Exercise caution and ensure there are correct amount of frames to display. - Sound event from `CrumblingSound` (if set) is played when crumbling animation starts playing. - [Destroy animation & sound](New-or-Enhanced-Logics.md#destroy-animation--sound) only play after crumbling animation has finished. From 82ea77caa5938458bb32b1ffbeaa8eeed9e4f709 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 3 Feb 2025 13:15:08 +0800 Subject: [PATCH 08/13] Add a hold down key to enable --- src/Commands/Commands.cpp | 1 + src/Commands/DistributionMode.cpp | 43 ++++++++++++++++++++++++++++++- src/Commands/DistributionMode.h | 14 ++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index 67aa534ae3..885c5b79bd 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -22,6 +22,7 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); MakeCommand(); + MakeCommand(); if (Phobos::Config::DevelopmentCommands) { diff --git a/src/Commands/DistributionMode.cpp b/src/Commands/DistributionMode.cpp index c72fd8b3ff..15b837d57a 100644 --- a/src/Commands/DistributionMode.cpp +++ b/src/Commands/DistributionMode.cpp @@ -8,6 +8,8 @@ int DistributionMode1CommandClass::Mode = 0; int DistributionMode2CommandClass::Mode = 0; +bool DistributionMode3CommandClass::Enabled = false; +int DistributionMode3CommandClass::ShowTime = 0; const char* DistributionMode1CommandClass::GetName() const { @@ -32,6 +34,7 @@ const wchar_t* DistributionMode1CommandClass::GetUIDescription() const void DistributionMode1CommandClass::Execute(WWKey eInput) const { DistributionMode1CommandClass::Mode = ((DistributionMode1CommandClass::Mode + 1) & 3); + DistributionMode3CommandClass::ShowTime = SystemTimer::GetTime(); } const char* DistributionMode2CommandClass::GetName() const @@ -57,6 +60,40 @@ const wchar_t* DistributionMode2CommandClass::GetUIDescription() const void DistributionMode2CommandClass::Execute(WWKey eInput) const { DistributionMode2CommandClass::Mode = ((DistributionMode2CommandClass::Mode + 1) & 3); + DistributionMode3CommandClass::ShowTime = SystemTimer::GetTime(); +} + +const char* DistributionMode3CommandClass::GetName() const +{ + return "Distribution Mode Hold Down"; +} + +const wchar_t* DistributionMode3CommandClass::GetUIName() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_HOLDDOWN", L"Distribution hold down"); +} + +const wchar_t* DistributionMode3CommandClass::GetUICategory() const +{ + return CATEGORY_CONTROL; +} + +const wchar_t* DistributionMode3CommandClass::GetUIDescription() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_HOLDDOWN_DESC", L"Automatically and averagely select similar targets around the original target. This is for holding down to toggle on/off"); +} + +bool DistributionMode3CommandClass::ExtraTriggerCondition(WWKey eInput) const +{ + return true; +} + +void DistributionMode3CommandClass::Execute(WWKey eInput) const +{ + if (eInput & WWKey::Release) + DistributionMode3CommandClass::Enabled = false; + else + DistributionMode3CommandClass::Enabled = true; } DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) @@ -74,7 +111,8 @@ DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) const auto mode2 = DistributionMode2CommandClass::Mode; // Distribution mode main - if (mode1 && count > 1 && mouseAction != Action::NoMove && !PlanningNodeClass::PlanningModeActive && (pTarget->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None && !pTarget->IsInAir()) + if (DistributionMode3CommandClass::Enabled && mode1 && count > 1 && mouseAction != Action::NoMove && !PlanningNodeClass::PlanningModeActive + && (pTarget->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None && !pTarget->IsInAir()) { const auto pSpecial = HouseClass::FindSpecial(); const auto pCivilian = HouseClass::FindCivilianSide(); @@ -165,6 +203,9 @@ DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) DEFINE_HOOK(0x6DBE74, TacticalClass_DrawAllRadialIndicators_DrawDistributionRange, 0x7) { + if (!DistributionMode3CommandClass::Enabled && SystemTimer::GetTime() - DistributionMode3CommandClass::ShowTime > 30) + return 0; + const auto mode1 = DistributionMode1CommandClass::Mode; const auto mode2 = DistributionMode2CommandClass::Mode; diff --git a/src/Commands/DistributionMode.h b/src/Commands/DistributionMode.h index 292ff49b2b..4002422af0 100644 --- a/src/Commands/DistributionMode.h +++ b/src/Commands/DistributionMode.h @@ -25,3 +25,17 @@ class DistributionMode2CommandClass : public CommandClass virtual const wchar_t* GetUIDescription() const override; virtual void Execute(WWKey eInput) const override; }; + +class DistributionMode3CommandClass : public CommandClass +{ +public: + static bool Enabled; + static int ShowTime; + + virtual const char* GetName() const override; + virtual const wchar_t* GetUIName() const override; + virtual const wchar_t* GetUICategory() const override; + virtual const wchar_t* GetUIDescription() const override; + virtual bool ExtraTriggerCondition(WWKey eInput) const override; + virtual void Execute(WWKey eInput) const override; +}; From d279944f8ec1971ce7ad576fbe416922f93f0f19 Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 3 Feb 2025 13:21:13 +0800 Subject: [PATCH 09/13] Doc --- docs/User-Interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 29a0a11b21..652cbcfb78 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -331,14 +331,14 @@ ShowFlashOnSelecting=false ; boolean ### `[ ]` Distribution Mode Spread / Filter -- Change the click action. +- Change the click action when hold down the specific hotkey. - When the range is 0, it is the original default behavior of the game. The range can be adjusted to 4, 8 or 16 cells by shortcut keys. - The targets within the range will be allocated equally to the selected technos. Only when the behavior to be performed by the current techno is the same as that displayed by the mouse will it be allocated. Otherwise, it will return to the original default behavior of the game (it will not be effective for technos in the air). This will display a range ring. - When the filter is `None`, it is the default behavior of the game. If the range is not zero at this time, a green ring will be displayed. You can adjust the filter mode to: - `Auto` - if the behavior to be executed by the current techno is different from the behavior displayed by the mouse, and the behavior to be executed will make the techno move near the target, the behavior will be replaced with area guard. At this time, a blue ring will be displayed. - `Type` - on the basis of `Auto`, only targets of the same type (like infantries, vehicles or buildings) will be selected among the targets allocated in the range. At this time, a yellow ring will be displayed. - `Name` - on the basis of `Type`, only targets of the same name (or with the same `GroupAs`) will be selected among the targets allocated in the range. At this time, a red ring will be displayed. -- For localization add `TXT_DISTR_SPREAD`, `TXT_DISTR_FILTER`, `TXT_DISTR_SPREAD_DESC` and `TXT_DISTR_FILTER_DESC` into your `.csf` file. +- For localization add `TXT_DISTR_SPREAD`, `TXT_DISTR_FILTER`, `TXT_DISTR_HOLDDOWN`, `TXT_DISTR_SPREAD_DESC`, `TXT_DISTR_FILTER_DESC` and `TXT_DISTR_HOLDDOWN_DESC` into your `.csf` file. ## Loading screen From 44442223a04744d220496b223b771f7094c92e7b Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 3 Feb 2025 15:39:39 +0800 Subject: [PATCH 10/13] Fix target shrouded units --- src/Commands/DistributionMode.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Commands/DistributionMode.cpp b/src/Commands/DistributionMode.cpp index 15b837d57a..014cf2b6c9 100644 --- a/src/Commands/DistributionMode.cpp +++ b/src/Commands/DistributionMode.cpp @@ -129,7 +129,16 @@ DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) for (const auto& pItem : pItems) { if (pItem->CloakState != CloakState::Cloaked || pItem->GetCell()->Sensors_InclHouse(HouseClass::CurrentPlayer->ArrayIndex)) - record[pItem] = 0; + { + auto coords = pItem->GetCoords(); + coords.Z = MapClass::Instance->GetCellFloorHeight(coords); + + if (MapClass::Instance->GetCellAt(coords)->ContainsBridge()) + coords.Z += CellClass::BridgeHeight; + + if (!MapClass::Instance->IsLocationShrouded(coords)) + record[pItem] = 0; + } } for (const auto& pSelect : ObjectClass::CurrentObjects()) From 40051d9d40138d06aafe5fb9f44fe4e33f286fec Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 3 Feb 2025 16:58:17 +0800 Subject: [PATCH 11/13] Fix target disguised units --- src/Commands/DistributionMode.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Commands/DistributionMode.cpp b/src/Commands/DistributionMode.cpp index 014cf2b6c9..545e7de7f8 100644 --- a/src/Commands/DistributionMode.cpp +++ b/src/Commands/DistributionMode.cpp @@ -124,11 +124,13 @@ DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) const auto range = (2 << mode1); const auto pItems = Helpers::Alex::getCellSpreadItems(pTarget->Location, range); std::map record; + record[static_cast(pTarget)] = 0; int current = 1; for (const auto& pItem : pItems) { - if (pItem->CloakState != CloakState::Cloaked || pItem->GetCell()->Sensors_InclHouse(HouseClass::CurrentPlayer->ArrayIndex)) + if ((pItem->CloakState != CloakState::Cloaked || pItem->GetCell()->Sensors_InclHouse(HouseClass::CurrentPlayer->ArrayIndex)) + && !pItem->IsDisguisedAs(HouseClass::CurrentPlayer)) { auto coords = pItem->GetCoords(); coords.Z = MapClass::Instance->GetCellFloorHeight(coords); From 2e3859421e8a67143fd8be267d91c43016ec9b4b Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Mon, 3 Feb 2025 16:59:01 +0800 Subject: [PATCH 12/13] Fix target outside units --- src/Commands/DistributionMode.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Commands/DistributionMode.cpp b/src/Commands/DistributionMode.cpp index 545e7de7f8..be5259c5bd 100644 --- a/src/Commands/DistributionMode.cpp +++ b/src/Commands/DistributionMode.cpp @@ -133,6 +133,10 @@ DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA) && !pItem->IsDisguisedAs(HouseClass::CurrentPlayer)) { auto coords = pItem->GetCoords(); + + if (!MapClass::Instance->IsWithinUsableArea(coords)) + continue; + coords.Z = MapClass::Instance->GetCellFloorHeight(coords); if (MapClass::Instance->GetCellAt(coords)->ContainsBridge()) From 55723f3e5b84b5396397a20ed4b9d0a473c99fbb Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Thu, 13 Feb 2025 17:13:34 +0800 Subject: [PATCH 13/13] Global toggle --- docs/User-Interface.md | 10 ++++++++-- src/Commands/Commands.cpp | 10 +++++++--- src/Phobos.INI.cpp | 3 +++ src/Phobos.h | 1 + 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 652cbcfb78..5153bf885e 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -329,9 +329,9 @@ ShowFlashOnSelecting=false ; boolean - Switches on/off [frame by frame mode](Miscellanous.md#frame-step-in). - For localization add `TXT_FRAME_BY_FRAME` and `TXT_FRAME_BY_FRAME_DESC` into your `.csf` file. -### `[ ]` Distribution Mode Spread / Filter +### `[ ]` Distribution Mode Spread / Filter / Enable -- Change the click action when hold down the specific hotkey. +- Change the click action when hold down the specific hotkey if enabled `AllowDistributionCommand`. - When the range is 0, it is the original default behavior of the game. The range can be adjusted to 4, 8 or 16 cells by shortcut keys. - The targets within the range will be allocated equally to the selected technos. Only when the behavior to be performed by the current techno is the same as that displayed by the mouse will it be allocated. Otherwise, it will return to the original default behavior of the game (it will not be effective for technos in the air). This will display a range ring. - When the filter is `None`, it is the default behavior of the game. If the range is not zero at this time, a green ring will be displayed. You can adjust the filter mode to: @@ -340,6 +340,12 @@ ShowFlashOnSelecting=false ; boolean - `Name` - on the basis of `Type`, only targets of the same name (or with the same `GroupAs`) will be selected among the targets allocated in the range. At this time, a red ring will be displayed. - For localization add `TXT_DISTR_SPREAD`, `TXT_DISTR_FILTER`, `TXT_DISTR_HOLDDOWN`, `TXT_DISTR_SPREAD_DESC`, `TXT_DISTR_FILTER_DESC` and `TXT_DISTR_HOLDDOWN_DESC` into your `.csf` file. +In `rulesmd.ini`: +```ini +[GlobalControls] +AllowDistributionCommand=false ; boolean +``` + ## Loading screen - PCX files can now be used as loadscreen images. diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index 885c5b79bd..c07b9ae75d 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -20,9 +20,13 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); MakeCommand(); - MakeCommand(); - MakeCommand(); - MakeCommand(); + + if (Phobos::Config::AllowDistributionCommand) + { + MakeCommand(); + MakeCommand(); + MakeCommand(); + } if (Phobos::Config::DevelopmentCommands) { diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index 247eb46fee..92aaa12e90 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -57,6 +57,7 @@ bool Phobos::Config::ShowWeedsCounter = false; bool Phobos::Config::HideLightFlashEffects = true; bool Phobos::Config::ShowFlashOnSelecting = false; bool Phobos::Config::UnitPowerDrain = false; +bool Phobos::Config::AllowDistributionCommand = false; bool Phobos::Misc::CustomGS = false; int Phobos::Misc::CustomGS_ChangeInterval[7] = { -1, -1, -1, -1, -1, -1, -1 }; @@ -222,6 +223,8 @@ DEFINE_HOOK(0x52D21F, InitRules_ThingsThatShouldntBeSerailized, 0x6) #endif Phobos::Config::ShowPlanningPath = pINI_RULESMD->ReadBool("GlobalControls", "DebugPlanningPaths", Phobos::Config::ShowPlanningPath); + Phobos::Config::AllowDistributionCommand = pINI_RULESMD->ReadBool("GlobalControls", "AllowDistributionCommand", Phobos::Config::AllowDistributionCommand); + return 0; } diff --git a/src/Phobos.h b/src/Phobos.h index da85fd883b..df6d58b4fd 100644 --- a/src/Phobos.h +++ b/src/Phobos.h @@ -92,6 +92,7 @@ class Phobos static bool HideLightFlashEffects; static bool ShowFlashOnSelecting; static bool UnitPowerDrain; + static bool AllowDistributionCommand; }; class Misc