From 7b303738216a218424e4b2e1cb5dd15aad203359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Mon, 26 May 2025 13:59:14 +0800 Subject: [PATCH 1/8] impl --- Phobos.vcxproj | 2 + src/Commands/CeaseFireStance.cpp | 107 +++++++++++ src/Commands/CeaseFireStance.h | 14 ++ src/Commands/Commands.cpp | 11 ++ src/Commands/PassiveAcquireMode.cpp | 210 ++++++++++++++++++++++ src/Commands/PassiveAcquireMode.h | 25 +++ src/Ext/Event/Body.cpp | 36 +++- src/Ext/Event/Body.h | 27 ++- src/Ext/Rules/Body.cpp | 5 +- src/Ext/Rules/Body.h | 5 +- src/Ext/Techno/Body.cpp | 93 ++++++++++ src/Ext/Techno/Body.h | 12 +- src/Ext/Techno/Hooks.TargetEvaluation.cpp | 20 +++ src/Ext/TechnoType/Body.cpp | 51 +++++- src/Ext/TechnoType/Body.h | 24 ++- 15 files changed, 622 insertions(+), 20 deletions(-) create mode 100644 src/Commands/CeaseFireStance.cpp create mode 100644 src/Commands/CeaseFireStance.h create mode 100644 src/Commands/PassiveAcquireMode.cpp create mode 100644 src/Commands/PassiveAcquireMode.h diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 5e5f7ceead..1d09d14706 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -32,6 +32,7 @@ + @@ -219,6 +220,7 @@ + diff --git a/src/Commands/CeaseFireStance.cpp b/src/Commands/CeaseFireStance.cpp new file mode 100644 index 0000000000..28e432ba45 --- /dev/null +++ b/src/Commands/CeaseFireStance.cpp @@ -0,0 +1,107 @@ +#include "CeaseFireStance.h" + +#include "Ext/Techno/Body.h" +#include + +const char* CeaseFireStanceClass::GetName() const +{ + return "CeaseFireStance"; +} + +const wchar_t* CeaseFireStanceClass::GetUIName() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_CEASEFIRE_STANCE", L"Cease Fire Stance"); +} + +const wchar_t* CeaseFireStanceClass::GetUICategory() const +{ + return CATEGORY_CONTROL; +} + +const wchar_t* CeaseFireStanceClass::GetUIDescription() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_CEASEFIRE_STANCE_DESC", L"Cease Fire Stance"); +} + +void CeaseFireStanceClass::Execute(WWKey eInput) const +{ + std::vector TechnoVectorCeaseFire; + std::vector TechnoVectorNonCeaseFire; + + // Get current selected units. + // If all selected units are at CeaseFire stance, we should cancel their CeaseFire stance. + // Otherwise, we should turn them into CeaseFire stance. + bool isAnySelectedUnitTogglable = false; + bool isAllSelectedUnitCeaseFireStance = true; + + auto processATechno = [&](TechnoClass* pTechno) + { + const auto pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + + // If not togglable then exclude it from the iteration. + if (!pTechnoExt->CanToggleCeaseFireStance()) + return; + + isAnySelectedUnitTogglable = true; + + if (pTechnoExt->GetCeaseFireStance()) + { + TechnoVectorCeaseFire.push_back(pTechno); + } + else + { + isAllSelectedUnitCeaseFireStance = false; + TechnoVectorNonCeaseFire.push_back(pTechno); + } + return; + }; + + for (const auto& pUnit : ObjectClass::CurrentObjects) + { + // try to cast to TechnoClass + TechnoClass* pTechno = abstract_cast(pUnit); + + // if not a techno or is in berserk or is not controlled by the local player then ignore it + if (!pTechno || pTechno->Berzerk || !pTechno->Owner->IsControlledByCurrentPlayer()) + continue; + + processATechno(pTechno); + + if (auto pPassenger = pTechno->Passengers.GetFirstPassenger()) + { + for (; pPassenger; pPassenger = abstract_cast(pPassenger->NextObject)) + processATechno(pPassenger); + } + + if (auto pBuilding = abstract_cast(pTechno)) + { + for (auto pOccupier : pBuilding->Occupants) + processATechno(pOccupier); + } + } + + // If this boolean is false, then none of the selected units are togglable, meaning this hotket doesn't need to do anything. + if (isAnySelectedUnitTogglable) + { + // If all selected units are CeaseFire stance, then cancel their CeaseFire stance; + // otherwise, make all selected units CeaseFire stance. + if (isAllSelectedUnitCeaseFireStance) + { + for (const auto& pTechno : TechnoVectorCeaseFire) + EventExt::RaiseToggleCeaseFireStance(pTechno); + + wchar_t buffer[0x100]; + swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:CeaseFire_STANCE_OFF", L"%i unit(s) ceased CeaseFire Stance."), TechnoVectorCeaseFire.size()); + MessageListClass::Instance.PrintMessage(buffer); + } + else + { + for (const auto& pTechno : TechnoVectorNonCeaseFire) + EventExt::RaiseToggleCeaseFireStance(pTechno); + + wchar_t buffer[0x100]; + swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:CeaseFire_STANCE_ON", L"%i unit(s) entered CeaseFire Stance."), TechnoVectorNonCeaseFire.size()); + MessageListClass::Instance.PrintMessage(buffer); + } + } +} diff --git a/src/Commands/CeaseFireStance.h b/src/Commands/CeaseFireStance.h new file mode 100644 index 0000000000..44c1f4644e --- /dev/null +++ b/src/Commands/CeaseFireStance.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Commands.h" + +class CeaseFireStanceClass : public CommandClass +{ +public: + // CommandClass + 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; +}; diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index 8d9d2611a1..254b77349e 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -12,6 +12,15 @@ #include "SaveVariablesToFile.h" #include "ToggleSWSidebar.h" #include "FireTacticalSW.h" +#include "AggressiveStance.h" +#include "CeaseFireStance.h" + +#include +#include +#include +#include + +#include #include DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) @@ -22,6 +31,8 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); MakeCommand(); + MakeCommand(); + MakeCommand(); MakeCommand(); if (Phobos::Config::SuperWeaponSidebarCommands) diff --git a/src/Commands/PassiveAcquireMode.cpp b/src/Commands/PassiveAcquireMode.cpp new file mode 100644 index 0000000000..4dfa3e5905 --- /dev/null +++ b/src/Commands/PassiveAcquireMode.cpp @@ -0,0 +1,210 @@ +#include "PassiveAcquireMode.h" + +#include "Ext/Techno/Body.h" +#include + +const char* AggressiveModeClass::GetName() const +{ + return "AggressiveMode"; +} + +const wchar_t* AggressiveModeClass::GetUIName() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_AGGRESSIVE_MODE", L"Aggressive Mode"); +} + +const wchar_t* AggressiveModeClass::GetUICategory() const +{ + return CATEGORY_CONTROL; +} + +const wchar_t* AggressiveModeClass::GetUIDescription() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_AGGRESSIVE_MODE_DESC", L"Aggressive Mode"); +} + +void AggressiveModeClass::Execute(WWKey eInput) const +{ + std::vector TechnoVectorAggressive; + std::vector TechnoVectorNonAggressive; + + // Get current selected units. + // If all selected units are at Aggressive mode, we should cancel their Aggressive mode. + // Otherwise, we should turn them into Aggressive mode. + bool isAnySelectedUnitTogglable = false; + bool isAllSelectedUnitAggressiveMode = true; + + auto processATechno = [&](TechnoClass* pTechno) + { + const auto pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + + // If not togglable then exclude it from the iteration. + if (!pTechnoExt->CanTogglePassiveAcquireMode()) + return; + + isAnySelectedUnitTogglable = true; + + if (pTechnoExt->GetPassiveAcquireMode() == PassiveAcquireMode::Aggressive) + { + TechnoVectorAggressive.push_back(pTechno); + } + else + { + isAllSelectedUnitAggressiveMode = false; + TechnoVectorNonAggressive.push_back(pTechno); + } + return; + }; + + for (const auto& pUnit : ObjectClass::CurrentObjects) + { + // try to cast to TechnoClass + TechnoClass* pTechno = abstract_cast(pUnit); + + // if not a techno or is in berserk or is not controlled by the local player then ignore it + if (!pTechno || pTechno->Berzerk || !pTechno->Owner->IsControlledByCurrentPlayer()) + continue; + + processATechno(pTechno); + + if (auto pPassenger = pTechno->Passengers.GetFirstPassenger()) + { + for (; pPassenger; pPassenger = abstract_cast(pPassenger->NextObject)) + processATechno(pPassenger); + } + + if (auto pBuilding = abstract_cast(pTechno)) + { + for (auto pOccupier : pBuilding->Occupants) + processATechno(pOccupier); + } + } + + // If this boolean is false, then none of the selected units are togglable, meaning this hotket doesn't need to do anything. + if (isAnySelectedUnitTogglable) + { + // If all selected units are Aggressive mode, then cancel their Aggressive mode; + // otherwise, make all selected units Aggressive mode. + if (isAllSelectedUnitAggressiveMode) + { + for (const auto& pTechno : TechnoVectorAggressive) + EventExt::RaiseTogglePassiveAcquireMode(pTechno, PassiveAcquireMode::Normal); + + wchar_t buffer[0x100]; + swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:AGGRESSIVE_MODE_OFF", L"%i unit(s) ceased Aggressive Mode."), TechnoVectorAggressive.size()); + MessageListClass::Instance.PrintMessage(buffer); + } + else + { + for (const auto& pTechno : TechnoVectorNonAggressive) + EventExt::RaiseTogglePassiveAcquireMode(pTechno, PassiveAcquireMode::Aggressive); + + wchar_t buffer[0x100]; + swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:AGGRESSIVE_MODE_ON", L"%i unit(s) entered Aggressive Mode."), TechnoVectorNonAggressive.size()); + MessageListClass::Instance.PrintMessage(buffer); + } + } +} + +const char* CeaseFireModeClass::GetName() const +{ + return "CeaseFireMode"; +} + +const wchar_t* CeaseFireModeClass::GetUIName() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_CEASEFIRE_MODE", L"Cease Fire Mode"); +} + +const wchar_t* CeaseFireModeClass::GetUICategory() const +{ + return CATEGORY_CONTROL; +} + +const wchar_t* CeaseFireModeClass::GetUIDescription() const +{ + return GeneralUtils::LoadStringUnlessMissing("TXT_CEASEFIRE_MODE_DESC", L"Cease Fire Mode"); +} + +void CeaseFireModeClass::Execute(WWKey eInput) const +{ + std::vector TechnoVectorCeaseFire; + std::vector TechnoVectorNonCeaseFire; + + // Get current selected units. + // If all selected units are at CeaseFire mode, we should cancel their CeaseFire mode. + // Otherwise, we should turn them into CeaseFire mode. + bool isAnySelectedUnitTogglable = false; + bool isAllSelectedUnitCeaseFireMode = true; + + auto processATechno = [&](TechnoClass* pTechno) + { + const auto pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + + // If not togglable then exclude it from the iteration. + if (!pTechnoExt->CanTogglePassiveAcquireMode()) + return; + + isAnySelectedUnitTogglable = true; + + if (pTechnoExt->GetPassiveAcquireMode() == PassiveAcquireMode::CeaseFire) + { + TechnoVectorCeaseFire.push_back(pTechno); + } + else + { + isAllSelectedUnitCeaseFireMode = false; + TechnoVectorNonCeaseFire.push_back(pTechno); + } + return; + }; + + for (const auto& pUnit : ObjectClass::CurrentObjects) + { + // try to cast to TechnoClass + TechnoClass* pTechno = abstract_cast(pUnit); + + // if not a techno or is in berserk or is not controlled by the local player then ignore it + if (!pTechno || pTechno->Berzerk || !pTechno->Owner->IsControlledByCurrentPlayer()) + continue; + + processATechno(pTechno); + + if (auto pPassenger = pTechno->Passengers.GetFirstPassenger()) + { + for (; pPassenger; pPassenger = abstract_cast(pPassenger->NextObject)) + processATechno(pPassenger); + } + + if (auto pBuilding = abstract_cast(pTechno)) + { + for (auto pOccupier : pBuilding->Occupants) + processATechno(pOccupier); + } + } + + // If this boolean is false, then none of the selected units are togglable, meaning this hotket doesn't need to do anything. + if (isAnySelectedUnitTogglable) + { + // If all selected units are CeaseFire mode, then cancel their CeaseFire mode; + // otherwise, make all selected units CeaseFire mode. + if (isAllSelectedUnitCeaseFireMode) + { + for (const auto& pTechno : TechnoVectorCeaseFire) + EventExt::RaiseTogglePassiveAcquireMode(pTechno, PassiveAcquireMode::Normal); + + wchar_t buffer[0x100]; + swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:CEASEFIRE_MODE_OFF", L"%i unit(s) ceased Cease Fire Mode."), TechnoVectorCeaseFire.size()); + MessageListClass::Instance.PrintMessage(buffer); + } + else + { + for (const auto& pTechno : TechnoVectorNonCeaseFire) + EventExt::RaiseTogglePassiveAcquireMode(pTechno, PassiveAcquireMode::CeaseFire); + + wchar_t buffer[0x100]; + swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:CEASEFIRE_MODE_ON", L"%i unit(s) entered Cease Fire Mode."), TechnoVectorNonCeaseFire.size()); + MessageListClass::Instance.PrintMessage(buffer); + } + } +} diff --git a/src/Commands/PassiveAcquireMode.h b/src/Commands/PassiveAcquireMode.h new file mode 100644 index 0000000000..4eb831c68a --- /dev/null +++ b/src/Commands/PassiveAcquireMode.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Commands.h" + +class AggressiveModeClass : public CommandClass +{ +public: + // CommandClass + 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 CeaseFireModeClass : public CommandClass +{ +public: + // CommandClass + 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; +}; diff --git a/src/Ext/Event/Body.cpp b/src/Ext/Event/Body.cpp index c4c471c55d..9ecdc9211c 100644 --- a/src/Ext/Event/Body.cpp +++ b/src/Ext/Event/Body.cpp @@ -1,4 +1,4 @@ -/* + #include "Body.h" #include @@ -13,18 +13,43 @@ void EventExt::RespondEvent() { switch (this->Type) { - case EventTypeExt::Sample: - // Place the handler here + case EventTypeExt::TogglePassiveAcquireMode: + this->RespondToTogglePassiveAcquireMode(); break; } } +void EventExt::RaiseTogglePassiveAcquireMode(TechnoClass* pTechno, PassiveAcquireMode mode) +{ + EventExt eventExt {}; + eventExt.Type = EventTypeExt::TogglePassiveAcquireMode; + eventExt.HouseIndex = static_cast(pTechno->Owner->ArrayIndex); + eventExt.Frame = Unsorted::CurrentFrame; + eventExt.TogglePassiveAcquireMode.Who = TargetClass(pTechno); + eventExt.TogglePassiveAcquireMode.Mode = mode; + eventExt.AddEvent(); +} + +void EventExt::RespondToTogglePassiveAcquireMode() +{ + if (const auto pTechno = this->TogglePassiveAcquireMode.Who.As_Techno()) + { + if (pTechno->IsAlive && !pTechno->Berzerk) + { + const auto pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + + if (pTechnoExt->CanTogglePassiveAcquireMode()) + pTechnoExt->TogglePassiveAcquireMode(this->TogglePassiveAcquireMode.Mode); + } + } +} + size_t EventExt::GetDataSize(EventTypeExt type) { switch (type) { - case EventTypeExt::Sample: - return sizeof(EventExt::Sample); + case EventTypeExt::TogglePassiveAcquireMode: + return sizeof(EventExt::TogglePassiveAcquireMode); } return 0; @@ -98,4 +123,3 @@ DEFINE_HOOK(0x64C30E, sub_64BDD0_GetEventSize2, 0x6) return 0; } -*/ diff --git a/src/Ext/Event/Body.h b/src/Ext/Event/Body.h index a9d37294e6..2717645abf 100644 --- a/src/Ext/Event/Body.h +++ b/src/Ext/Event/Body.h @@ -1,18 +1,25 @@ #pragma once -/* + #include #include +#include "HouseClass.h" +#include "TechnoClass.h" +#include "TargetClass.h" + +#include + enum class EventTypeExt : uint8_t { // Vanilla game used Events from 0x00 to 0x2F // CnCNet reserved Events from 0x30 to 0x3F // Ares used Events 0x60 and 0x61 - Sample = 0x40, // Sample event, remove it when Phobos needs its own events + TogglePassiveAcquireMode = 0x81, + ToggleCeaseFireMode = 0x82, - FIRST = Sample, - LAST = Sample + FIRST = TogglePassiveAcquireMode, + LAST = ToggleCeaseFireMode }; #pragma pack(push, 1) @@ -27,15 +34,19 @@ class EventExt { char DataBuffer[104]; - struct Sample + struct TogglePassiveAcquireMode { - char DataBuffer[104]; - } Sample; + TargetClass Who; + PassiveAcquireMode Mode; + } TogglePassiveAcquireMode; }; bool AddEvent(); void RespondEvent(); + static void RaiseTogglePassiveAcquireMode(TechnoClass* pTechno, PassiveAcquireMode mode); + void RespondToTogglePassiveAcquireMode(); + static size_t GetDataSize(EventTypeExt type); static bool IsValidType(EventTypeExt type); }; @@ -43,4 +54,4 @@ class EventExt static_assert(sizeof(EventExt) == 111); static_assert(offsetof(EventExt, DataBuffer) == 7); #pragma pack(pop) -*/ + diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index c30c2e8849..efccac6549 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -1,4 +1,4 @@ -#include "Body.h" +#include "Body.h" #include #include #include @@ -313,6 +313,8 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->InfantryAutoDeploy.Read(exINI, GameStrings::General, "InfantryAutoDeploy"); + this->EnablePassiveAcquireMode.Read(exINI, GameStrings::General, "EnablePassiveAcquireMode"); + // Section AITargetTypes int itemsCount = pINI->GetKeyCount("AITargetTypes"); for (int i = 0; i < itemsCount; ++i) @@ -576,6 +578,7 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->AttackMove_IgnoreWeaponCheck) .Process(this->AttackMove_StopWhenTargetAcquired) .Process(this->Parasite_GrappleAnim) + .Process(this->GatherWhenMCVDeploy) .Process(this->InfantryAutoDeploy) ; } diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 190cd8b2e7..7ac7194b4d 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -225,6 +225,8 @@ class RulesExt Valueable AIAllToHunt; Valueable RepairBaseNodes; + Valueable EnablePassiveAcquireMode; + Valueable WarheadParticleAlphaImageIsLightFlash; Valueable CombatLightDetailLevel; Valueable LightFlashAlphaImageDetailLevel; @@ -435,6 +437,7 @@ class RulesExt , AIFireSaleDelay { 0 } , AIAllToHunt { true } , RepairBaseNodes { false } + , EnablePassiveAcquireMode { false } , WarheadParticleAlphaImageIsLightFlash { false } , CombatLightDetailLevel { 0 } , LightFlashAlphaImageDetailLevel { 0 } diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 5d43b41ccf..22759de06f 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -802,6 +802,98 @@ void TechnoExt::HandleOnDeployAmmoChange(TechnoClass* pThis, int maxAmmoOverride } } +void TechnoExt::ExtData::InitPassiveAcquireMode() +{ + this->PassiveAquireMode = this->TypeExtData->PassiveAcquireMode.Get(); +} + +PassiveAcquireMode TechnoExt::ExtData::GetPassiveAcquireMode() const +{ + // if this is a passenger then obey the configuration of the transport + if (auto pTransport = this->OwnerObject()->Transporter) + return TechnoExt::ExtMap.Find(pTransport)->GetPassiveAcquireMode(); + + return this->PassiveAquireMode; +} + +void TechnoExt::ExtData::TogglePassiveAcquireMode(PassiveAcquireMode newMode) +{ + auto previousMode = this->PassiveAquireMode; + this->PassiveAquireMode = newMode; + + if (newMode == previousMode) + return; + + const auto pThis = this->OwnerObject(); + const auto pTechnoType = this->TypeExtData->OwnerObject(); + int voiceIndex; + + if (newMode == PassiveAcquireMode::Normal) + { + if (previousMode == PassiveAcquireMode::CeaseFire) + { + voiceIndex = this->TypeExtData->VoiceExitCeaseFireMode.Get(); + + if (voiceIndex < 0) + { + const auto& voiceList = pTechnoType->VoiceAttack.Count ? pTechnoType->VoiceAttack : pTechnoType->VoiceMove; + + if (const auto count = voiceList.Count) + voiceIndex = voiceList.GetItem(Randomizer::Global.Random() % count); + } + } + else + { + pThis->SetTarget(nullptr); + voiceIndex = this->TypeExtData->VoiceExitAggressiveMode.Get(); + + if (voiceIndex < 0) + { + const auto& voiceList = pTechnoType->VoiceMove.Count ? pTechnoType->VoiceMove : pTechnoType->VoiceSelect; + + if (const auto count = voiceList.Count) + voiceIndex = voiceList.GetItem(Randomizer::Global.Random() % count); + } + } + } + else if (newMode == PassiveAcquireMode::CeaseFire) + { + pThis->SetTarget(nullptr); + voiceIndex = this->TypeExtData->VoiceEnterCeaseFireMode.Get(); + + if (voiceIndex < 0) + { + const auto& voiceList = pTechnoType->VoiceSelect.Count ? pTechnoType->VoiceSelect : pTechnoType->VoiceMove; + + if (const auto count = voiceList.Count) + voiceIndex = voiceList.GetItem(Randomizer::Global.Random() % count); + } + } + else + { + voiceIndex = this->TypeExtData->VoiceEnterAggressiveMode.Get(); + + if (voiceIndex < 0) + { + const auto& voiceList = pTechnoType->VoiceAttack.Count ? pTechnoType->VoiceAttack : pTechnoType->VoiceMove; + + if (const auto count = voiceList.Count) + voiceIndex = voiceList.GetItem(Randomizer::Global.Random() % count); + } + } + + pThis->QueueVoice(voiceIndex); +} + +bool TechnoExt::ExtData::CanTogglePassiveAcquireMode() +{ + if (!RulesExt::Global()->EnablePassiveAcquireMode) + return false; + + return this->TypeExtData->PassiveAcquireMode_Togglable; +} + + // ============================= // load / save @@ -866,6 +958,7 @@ void TechnoExt::ExtData::Serialize(T& Stm) .Process(this->TintIntensityAllies) .Process(this->TintIntensityEnemies) .Process(this->AttackMoveFollowerTempCount) + .Process(this->PassiveAquireMode) ; } diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 3734979dd4..e32218112e 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -1,7 +1,9 @@ -#pragma once +#pragma once #include #include +#include + #include #include #include @@ -94,6 +96,8 @@ class TechnoExt int AttackMoveFollowerTempCount; + PassiveAcquireMode PassiveAquireMode; + ExtData(TechnoClass* OwnerObject) : Extension(OwnerObject) , TypeExtData { nullptr } , Shield {} @@ -153,6 +157,7 @@ class TechnoExt , TintIntensityAllies { 0 } , TintIntensityEnemies { 0 } , AttackMoveFollowerTempCount { 0 } + , PassiveAquireMode{ PassiveAcquireMode::Normal } { } void OnEarlyUpdate(); @@ -196,6 +201,11 @@ class TechnoExt virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; + void InitPassiveAcquireMode(); + PassiveAcquireMode GetPassiveAcquireMode() const; + void TogglePassiveAcquireMode(PassiveAcquireMode mode); + bool CanTogglePassiveAcquireMode(); + private: template void Serialize(T& Stm); diff --git a/src/Ext/Techno/Hooks.TargetEvaluation.cpp b/src/Ext/Techno/Hooks.TargetEvaluation.cpp index 10daf28aac..c8011241e6 100644 --- a/src/Ext/Techno/Hooks.TargetEvaluation.cpp +++ b/src/Ext/Techno/Hooks.TargetEvaluation.cpp @@ -376,3 +376,23 @@ Action __fastcall InfantryClass__WhatAction_Wrapper(InfantryClass* pThis, void* DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB0CC, InfantryClass__WhatAction_Wrapper) #pragma endregion + +#pragma region CeaseFireStance + +DEFINE_HOOK(0x6F8E1F, TechnoClass_SelectAutoTarget_CeaseFireStance, 0x6) +{ + GET(TechnoTypeClass*, pType, EAX); + GET(TechnoClass*, pThis, ESI); + R->CL(pType->NoAutoFire || (TechnoExt::ExtMap.Find(pThis)->GetPassiveAcquireMode()) == PassiveAcquireMode::CeaseFire); + return R->Origin() + 0x6; +} + +DEFINE_HOOK(0x7087DD, TechnoClass_CanRetaliateToAttacker_CeaseFireStance, 0x6) +{ + GET(TechnoTypeClass*, pType, EAX); + GET(TechnoClass*, pThis, ESI); + R->CL(pType->CanRetaliate && (TechnoExt::ExtMap.Find(pThis)->GetPassiveAcquireMode() != PassiveAcquireMode::CeaseFire)); + return R->Origin() + 0x6; +} + +#pragma endregion diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 306016e501..3cec41b219 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1,4 +1,4 @@ -#include "Body.h" +#include "Body.h" #include #include @@ -956,7 +956,14 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AttackMove_PursuitTarget.Read(exINI, pSection, "AttackMove.PursuitTarget"); this->InfantryAutoDeploy.Read(exINI, pSection, "InfantryAutoDeploy"); - + + this->PassiveAcquireMode.Read(exINI, pSection, "PassiveAcquireMode"); + this->PassiveAcquireMode_Togglable.Read(exINI, pSection, "PassiveAcquireMode.Togglable"); + this->VoiceEnterAggressiveMode.Read(exINI, pSection, "VoiceEnterAggressiveMode"); + this->VoiceExitAggressiveMode.Read(exINI, pSection, "VoiceExitAggressiveMode"); + this->VoiceEnterCeaseFireMode.Read(exINI, pSection, "VoiceEnterCeaseFireMode"); + this->VoiceExitCeaseFireMode.Read(exINI, pSection, "VoiceExitCeaseFireMode"); + // Ares 0.2 this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius"); @@ -1340,6 +1347,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->AutoFire) .Process(this->AutoFire_TargetSelf) + .Process(this->NoSecondaryWeaponFallback) .Process(this->NoSecondaryWeaponFallback_AllowAA) .Process(this->NoAmmoWeapon) @@ -1599,6 +1607,14 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->InfantryAutoDeploy) .Process(this->TurretResponse) + + .Process(this->PassiveAcquireMode) + .Process(this->PassiveAcquireMode_Togglable) + .Process(this->VoiceEnterAggressiveMode) + .Process(this->VoiceExitAggressiveMode) + .Process(this->VoiceEnterCeaseFireMode) + .Process(this->VoiceExitCeaseFireMode) + ; } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) @@ -1702,3 +1718,34 @@ DEFINE_HOOK(0x747E90, UnitTypeClass_LoadFromINI, 0x5) return 0; } + +namespace detail +{ + template <> + inline bool read(PassiveAcquireMode& value, INI_EX& parser, const char* pSection, const char* pKey) + { + if (parser.ReadString(pSection, pKey)) + { + auto str = parser.value(); + if (_strcmpi(str, "Normal") == 0) + { + value = PassiveAcquireMode::Normal; + } + else if (_strcmpi(str, "Aggressive") == 0) + { + value = PassiveAcquireMode::Aggressive; + } + else if (_strcmpi(str, "CeaseFire") == 0) + { + value = PassiveAcquireMode::CeaseFire; + } + else + { + Debug::INIParseFailed(pSection, pKey, str, "Expected passive acquire mode."); + return false; + } + return true; + } + return false; + } +} diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index b603ab0aab..86daf352bd 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -16,6 +16,13 @@ #include #include +enum class PassiveAcquireMode : BYTE +{ + Normal = 0, + Aggressive = 1, + CeaseFire = 2, +}; + class Matrix3D; class ParticleSystemTypeClass; class TechnoTypeExt @@ -425,6 +432,13 @@ class TechnoTypeExt Nullable TurretResponse; + Valueable PassiveAcquireMode; + Valueable PassiveAcquireMode_Togglable; + ValueableIdx VoiceEnterAggressiveMode; + ValueableIdx VoiceExitAggressiveMode; + ValueableIdx VoiceEnterCeaseFireMode; + ValueableIdx VoiceExitCeaseFireMode; + ExtData(TechnoTypeClass* OwnerObject) : Extension(OwnerObject) , HealthBar_Hide { false } , HealthBar_HidePips { false } @@ -799,6 +813,14 @@ class TechnoTypeExt , InfantryAutoDeploy {} , TurretResponse {} + + , PassiveAcquireMode { PassiveAcquireMode::Normal } + , PassiveAcquireMode_Togglable { true } + , VoiceEnterAggressiveMode { -1 } + , VoiceExitAggressiveMode { -1 } + , VoiceEnterCeaseFireMode { -1 } + , VoiceExitCeaseFireMode { -1 } + { } virtual ~ExtData() = default; From e279223eeade95012cb8a7dd9a3829efd6576d72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Mon, 26 May 2025 14:25:05 +0800 Subject: [PATCH 2/8] Update Hooks.TargetEvaluation.cpp --- src/Commands/CeaseFireStance.cpp | 107 ------------------------------- src/Commands/CeaseFireStance.h | 14 ---- 2 files changed, 121 deletions(-) delete mode 100644 src/Commands/CeaseFireStance.cpp delete mode 100644 src/Commands/CeaseFireStance.h diff --git a/src/Commands/CeaseFireStance.cpp b/src/Commands/CeaseFireStance.cpp deleted file mode 100644 index 28e432ba45..0000000000 --- a/src/Commands/CeaseFireStance.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "CeaseFireStance.h" - -#include "Ext/Techno/Body.h" -#include - -const char* CeaseFireStanceClass::GetName() const -{ - return "CeaseFireStance"; -} - -const wchar_t* CeaseFireStanceClass::GetUIName() const -{ - return GeneralUtils::LoadStringUnlessMissing("TXT_CEASEFIRE_STANCE", L"Cease Fire Stance"); -} - -const wchar_t* CeaseFireStanceClass::GetUICategory() const -{ - return CATEGORY_CONTROL; -} - -const wchar_t* CeaseFireStanceClass::GetUIDescription() const -{ - return GeneralUtils::LoadStringUnlessMissing("TXT_CEASEFIRE_STANCE_DESC", L"Cease Fire Stance"); -} - -void CeaseFireStanceClass::Execute(WWKey eInput) const -{ - std::vector TechnoVectorCeaseFire; - std::vector TechnoVectorNonCeaseFire; - - // Get current selected units. - // If all selected units are at CeaseFire stance, we should cancel their CeaseFire stance. - // Otherwise, we should turn them into CeaseFire stance. - bool isAnySelectedUnitTogglable = false; - bool isAllSelectedUnitCeaseFireStance = true; - - auto processATechno = [&](TechnoClass* pTechno) - { - const auto pTechnoExt = TechnoExt::ExtMap.Find(pTechno); - - // If not togglable then exclude it from the iteration. - if (!pTechnoExt->CanToggleCeaseFireStance()) - return; - - isAnySelectedUnitTogglable = true; - - if (pTechnoExt->GetCeaseFireStance()) - { - TechnoVectorCeaseFire.push_back(pTechno); - } - else - { - isAllSelectedUnitCeaseFireStance = false; - TechnoVectorNonCeaseFire.push_back(pTechno); - } - return; - }; - - for (const auto& pUnit : ObjectClass::CurrentObjects) - { - // try to cast to TechnoClass - TechnoClass* pTechno = abstract_cast(pUnit); - - // if not a techno or is in berserk or is not controlled by the local player then ignore it - if (!pTechno || pTechno->Berzerk || !pTechno->Owner->IsControlledByCurrentPlayer()) - continue; - - processATechno(pTechno); - - if (auto pPassenger = pTechno->Passengers.GetFirstPassenger()) - { - for (; pPassenger; pPassenger = abstract_cast(pPassenger->NextObject)) - processATechno(pPassenger); - } - - if (auto pBuilding = abstract_cast(pTechno)) - { - for (auto pOccupier : pBuilding->Occupants) - processATechno(pOccupier); - } - } - - // If this boolean is false, then none of the selected units are togglable, meaning this hotket doesn't need to do anything. - if (isAnySelectedUnitTogglable) - { - // If all selected units are CeaseFire stance, then cancel their CeaseFire stance; - // otherwise, make all selected units CeaseFire stance. - if (isAllSelectedUnitCeaseFireStance) - { - for (const auto& pTechno : TechnoVectorCeaseFire) - EventExt::RaiseToggleCeaseFireStance(pTechno); - - wchar_t buffer[0x100]; - swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:CeaseFire_STANCE_OFF", L"%i unit(s) ceased CeaseFire Stance."), TechnoVectorCeaseFire.size()); - MessageListClass::Instance.PrintMessage(buffer); - } - else - { - for (const auto& pTechno : TechnoVectorNonCeaseFire) - EventExt::RaiseToggleCeaseFireStance(pTechno); - - wchar_t buffer[0x100]; - swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:CeaseFire_STANCE_ON", L"%i unit(s) entered CeaseFire Stance."), TechnoVectorNonCeaseFire.size()); - MessageListClass::Instance.PrintMessage(buffer); - } - } -} diff --git a/src/Commands/CeaseFireStance.h b/src/Commands/CeaseFireStance.h deleted file mode 100644 index 44c1f4644e..0000000000 --- a/src/Commands/CeaseFireStance.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Commands.h" - -class CeaseFireStanceClass : public CommandClass -{ -public: - // CommandClass - 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 fc899ac9fcb8fb0262caef790c3549c221e08709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Wed, 10 Sep 2025 00:46:13 +0800 Subject: [PATCH 3/8] AggressiveMode --- src/Ext/BuildingType/Body.cpp | 3 +++ src/Ext/BuildingType/Body.h | 3 +++ src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.TargetEvaluation.cpp | 27 +++++++++++++++++------ 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index acad4d12e9..0cb5b2a33c 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -219,6 +219,8 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Refinery_UseNormalActiveAnim.Read(exArtINI, pArtSection, "Refinery.UseNormalActiveAnim"); + this->AggressiveModeExempt.Read(exINI, pSection, "AggressiveModeExempt"); + // Ares tag this->SpyEffect_Custom.Read(exINI, pSection, "SpyEffect.Custom"); if (SuperWeaponTypeClass::Array.Count > 0) @@ -334,6 +336,7 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm) .Process(this->BuildingRepairedSound) .Process(this->Refinery_UseNormalActiveAnim) .Process(this->HasPowerUpAnim) + .Process(this->AggressiveModeExempt) ; } diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 9cf41a7af4..dbd38e000d 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -99,6 +99,8 @@ class BuildingTypeExt ValueableVector HasPowerUpAnim; + Valueable AggressiveModeExempt; + ExtData(BuildingTypeClass* OwnerObject) : Extension(OwnerObject) , PowersUp_Owner { AffectedHouse::Owner } , PowersUp_Buildings {} @@ -161,6 +163,7 @@ class BuildingTypeExt , BuildingRepairedSound {} , Refinery_UseNormalActiveAnim { false } , HasPowerUpAnim {} + , AggressiveModeExempt{} { } // Ares 0.A functions diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index e32218112e..3948be8c80 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -3,6 +3,7 @@ #include #include +#include #include #include diff --git a/src/Ext/Techno/Hooks.TargetEvaluation.cpp b/src/Ext/Techno/Hooks.TargetEvaluation.cpp index c8011241e6..01f3f00965 100644 --- a/src/Ext/Techno/Hooks.TargetEvaluation.cpp +++ b/src/Ext/Techno/Hooks.TargetEvaluation.cpp @@ -206,12 +206,25 @@ DEFINE_HOOK(0x6F85AB, TechnoClass_CanAutoTargetObject_AggressiveAttackMove, 0x6) if (!pThis->Owner->IsControlledByHuman()) return CanTarget; - if (!pThis->MegaMissionIsAttackMove()) - return ContinueCheck; + GET(TechnoClass*, pTarget, ESI); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + if (pTarget->WhatAmI() == AbstractType::Building) + { + // Fallback to unmodded behavior if the building is an exempt of aggressive stance. + if (BuildingTypeExt::ExtMap.Find(static_cast(pTarget)->Type)->AggressiveModeExempt) + return ContinueCheck; + + if (TechnoExt::ExtMap.Find(pThis)->GetPassiveAcquireMode() == PassiveAcquireMode::Aggressive) + return CanTarget; + } + + if (pThis->MegaMissionIsAttackMove()) + { + if (TechnoExt::ExtMap.Find(pThis)->TypeExtData->AttackMove_Aggressive.Get(RulesExt::Global()->AttackMove_Aggressive)) + return CanTarget; + } - return pExt->TypeExtData->AttackMove_Aggressive.Get(RulesExt::Global()->AttackMove_Aggressive) ? CanTarget : ContinueCheck; + return ContinueCheck; } #pragma endregion @@ -377,9 +390,9 @@ DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB0CC, InfantryClass__WhatAction_Wrapper) #pragma endregion -#pragma region CeaseFireStance +#pragma region PassiveAcquireMode -DEFINE_HOOK(0x6F8E1F, TechnoClass_SelectAutoTarget_CeaseFireStance, 0x6) +DEFINE_HOOK(0x6F8E1F, TechnoClass_SelectAutoTarget_CeaseFireMode, 0x6) { GET(TechnoTypeClass*, pType, EAX); GET(TechnoClass*, pThis, ESI); @@ -387,7 +400,7 @@ DEFINE_HOOK(0x6F8E1F, TechnoClass_SelectAutoTarget_CeaseFireStance, 0x6) return R->Origin() + 0x6; } -DEFINE_HOOK(0x7087DD, TechnoClass_CanRetaliateToAttacker_CeaseFireStance, 0x6) +DEFINE_HOOK(0x7087DD, TechnoClass_CanRetaliateToAttacker_CeaseFireMode, 0x6) { GET(TechnoTypeClass*, pType, EAX); GET(TechnoClass*, pThis, ESI); From d304b1c65c254eb414aa666ef75434ac2757b167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Wed, 10 Sep 2025 00:49:38 +0800 Subject: [PATCH 4/8] Update Commands.cpp --- src/Commands/Commands.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index 254b77349e..8a3f2322a6 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -12,8 +12,7 @@ #include "SaveVariablesToFile.h" #include "ToggleSWSidebar.h" #include "FireTacticalSW.h" -#include "AggressiveStance.h" -#include "CeaseFireStance.h" +#include "PassiveAcquireMode.h" #include #include @@ -31,8 +30,8 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); MakeCommand(); - MakeCommand(); - MakeCommand(); + MakeCommand(); + MakeCommand(); MakeCommand(); if (Phobos::Config::SuperWeaponSidebarCommands) From ba3fb49cfb1c796b49ddb2573806f76dd65774f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Wed, 10 Sep 2025 00:51:04 +0800 Subject: [PATCH 5/8] Update Phobos.vcxproj --- Phobos.vcxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 1d09d14706..5c4b9ee1d1 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -18,6 +18,7 @@ + @@ -32,7 +33,6 @@ - @@ -214,13 +214,13 @@ + - From 96ccf7497f91fd1d5725cd8d6811a82ee7d9a1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Wed, 10 Sep 2025 00:57:56 +0800 Subject: [PATCH 6/8] update --- Phobos.vcxproj | 2 ++ src/Ext/Event/Body.h | 3 +-- src/Ext/Rules/Body.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 5c4b9ee1d1..9bc5163b77 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -19,6 +19,7 @@ + @@ -216,6 +217,7 @@ + diff --git a/src/Ext/Event/Body.h b/src/Ext/Event/Body.h index 2717645abf..67ad6f6c06 100644 --- a/src/Ext/Event/Body.h +++ b/src/Ext/Event/Body.h @@ -16,10 +16,9 @@ enum class EventTypeExt : uint8_t // Ares used Events 0x60 and 0x61 TogglePassiveAcquireMode = 0x81, - ToggleCeaseFireMode = 0x82, FIRST = TogglePassiveAcquireMode, - LAST = ToggleCeaseFireMode + LAST = TogglePassiveAcquireMode }; #pragma pack(push, 1) diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index efccac6549..a25b8f372f 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -578,8 +578,8 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->AttackMove_IgnoreWeaponCheck) .Process(this->AttackMove_StopWhenTargetAcquired) .Process(this->Parasite_GrappleAnim) - .Process(this->GatherWhenMCVDeploy) .Process(this->InfantryAutoDeploy) + .Process(this->EnablePassiveAcquireMode) ; } From 8a7a38b984665b8dff6c31a4a8f1992944792a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:10:55 +0800 Subject: [PATCH 7/8] docs and update --- CREDITS.md | 5 ++- docs/New-or-Enhanced-Logics.md | 26 ++++++++++++ docs/User-Interface.md | 10 +++++ docs/Whats-New.md | 1 + src/Commands/Commands.cpp | 2 +- src/Commands/PassiveAcquireMode.cpp | 50 +++++++++++------------ src/Commands/PassiveAcquireMode.h | 2 +- src/Ext/Techno/Body.cpp | 8 ++-- src/Ext/Techno/Hooks.TargetEvaluation.cpp | 8 ++-- src/Ext/TechnoType/Body.cpp | 12 +++--- src/Ext/TechnoType/Body.h | 10 ++--- 11 files changed, 87 insertions(+), 47 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 599ca1b29a..a3ca076457 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -609,13 +609,16 @@ This page lists all the individual contributions to the project by their author. - Auto deploy for GI-like infantry - Fix an issue that Ares' Type Conversion not resetting barrel's direction by `FireAngle` - Fix an issue that jumpjets in air can not correctly spawn missiles + - Passive acquire mode - **solar-III (凤九歌)** - Target scanning delay customization (documentation) - Skip target scanning function calling for unarmed technos (documentation) - **tyuah8**: - Drive/Jumpjet/Ship/Teleport locomotor did not power on when it is un-piggybacked bugfix - Destroyed unit leaves sensors bugfix -- **Aephiex** - initial fix for Ares academy not working on the initial payloads of vehicles built from a war factory +- **Aephiex** + - initial fix for Ares academy not working on the initial payloads of vehicles built from a war factory + - Passive acquire mode (aggressive mode part) - **Multfinite** - Allow to toggle main exception handler via command line argument `-ExceptionHandler=boolean` - **hejiajun107, Xkein** - Fix a jumpjet crash related to voxel shadow drawing - **Ares developers**: diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index b920d5b00e..c4313d2cd8 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1794,6 +1794,32 @@ In `rulesmd.ini`: Promote.IncludeSpawns=false ; boolean ``` +### Passive acquire mode + +- Now you can order the units to enter "Ceasefire Mode" or "Aggressive Mode", just like in RA3. + - In ceasefire mode, units will only attack the targets specified by the player. + - In aggressive mode, units will automatically attack all enemies, including buildings without weapons. + - In normal mode, units will auto-target like usual. +- You can use [two](User-Interface#ceasefire-mode) [hotkeys](User-Interface#aggressive-mode) to switch between these two modes if `[General] -> EnablePassiveAcquireMode` is set to `true`. When you press one of these hotkeys: + - If all selected units are in that mode, they will switch to the normal mode. Otherwise, they will enter that mode. Units that cannot switch modes are not counted. + - If any unit successfully switches its mode, a sound effect will be emitted. This sound can be customized through `[TechnoType] -> Voice(Enter/Exit)(Aggressive/Ceasefire)Mode`. +- You can use `[TechnoType] -> PassiveAcquireMode.Togglable` to specify whether the unit can toggle its mode. +- You can use `[TechnoType] -> PassiveAcquireMode` to specify the unit's initial mode. + +In `rulesmd.ini`: +```ini +[General] +EnablePassiveAcquireMode=false ; boolean + +[SOMETECHNO] ; TechnoType +PassiveAcquireMode=Normal ; passive acquire mode, Normal / Aggressive / Ceasefire +PassiveAcquireMode.Togglable=true ; boolean +VoiceEnterAggressiveMode= ; Sound entry, default to VoiceAttack or VoiceMove +VoiceExitAggressiveMode= ; Sound entry, default to VoiceMove or VoiceSelect +VoiceEnterCeasefireMode= ; Sound entry, default to VoiceSelect or VoiceMove +VoiceExitCeasefireMode= ; Sound entry, default to VoiceAttack or VoiceMove +``` + ### Promotion animation - You can now specify an animation on the unit or structure promotion. diff --git a/docs/User-Interface.md b/docs/User-Interface.md index 7d583c76bf..8e1c570132 100644 --- a/docs/User-Interface.md +++ b/docs/User-Interface.md @@ -479,6 +479,16 @@ For this command to work in multiplayer - you need to use a version of [YRpp spa - These vanilla CSF entries will be used: `TXT_SAVING_GAME`, `TXT_GAME_WAS_SAVED` and `TXT_ERROR_SAVING_GAME`. - The save should be looks like `Allied Mission 25: Esther's Money - QuickSaved`. +### `[ ]` Ceasefire Mode + +- Order the selected units to enter or exit the ceasefire mode. See [this](New-or-Enhanced-Logics#passive-acquire-mode) for details. +- For localization add `TXT_CEASEFIRE_MODE`, `TXT_CEASEFIRE_MODE_DESC`, `MSG:CEASEFIRE_MODE_ON` and `MSG:CEASEFIRE_MODE_OFF` into your `.csf` file. + +### `[ ]` Aggressive Mode + +- Order the selected units to enter or exit the aggressive mode. See [this](New-or-Enhanced-Logics#passive-acquire-mode) for details. +- For localization add `TXT_AGGRESSIVE_MODE`, `TXT_AGGRESSIVE_MODE_DESC`, `MSG:AGGRESSIVE_MODE_ON` and `MSG:AGGRESSIVE_MODE_OFF` 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 432a86fe92..a33cdec955 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -443,6 +443,7 @@ New: - [Ammo-based deploy customizations for vehicles expanded to non-IsSimpleDeployer deploy functions](New-or-Enhanced-Logics.md#automatic-deploy-and-blocking-deploying-based-on-ammo) (by Starkku) - Randomized anims for several behaviors (by Ollerus) - Shield respawn animation and weapon (by Ollerus) +- Passive acquire mode (by TaranDahl & Aephiex) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index 8a3f2322a6..81144fa223 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -31,7 +31,7 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand(); MakeCommand(); MakeCommand(); - MakeCommand(); + MakeCommand(); MakeCommand(); if (Phobos::Config::SuperWeaponSidebarCommands) diff --git a/src/Commands/PassiveAcquireMode.cpp b/src/Commands/PassiveAcquireMode.cpp index 4dfa3e5905..827e03d0d4 100644 --- a/src/Commands/PassiveAcquireMode.cpp +++ b/src/Commands/PassiveAcquireMode.cpp @@ -106,36 +106,36 @@ void AggressiveModeClass::Execute(WWKey eInput) const } } -const char* CeaseFireModeClass::GetName() const +const char* CeasefireModeClass::GetName() const { - return "CeaseFireMode"; + return "CeasefireMode"; } -const wchar_t* CeaseFireModeClass::GetUIName() const +const wchar_t* CeasefireModeClass::GetUIName() const { - return GeneralUtils::LoadStringUnlessMissing("TXT_CEASEFIRE_MODE", L"Cease Fire Mode"); + return GeneralUtils::LoadStringUnlessMissing("TXT_CEASEFIRE_MODE", L"Ceasefire Mode"); } -const wchar_t* CeaseFireModeClass::GetUICategory() const +const wchar_t* CeasefireModeClass::GetUICategory() const { return CATEGORY_CONTROL; } -const wchar_t* CeaseFireModeClass::GetUIDescription() const +const wchar_t* CeasefireModeClass::GetUIDescription() const { - return GeneralUtils::LoadStringUnlessMissing("TXT_CEASEFIRE_MODE_DESC", L"Cease Fire Mode"); + return GeneralUtils::LoadStringUnlessMissing("TXT_CEASEFIRE_MODE_DESC", L"Ceasefire Mode"); } -void CeaseFireModeClass::Execute(WWKey eInput) const +void CeasefireModeClass::Execute(WWKey eInput) const { - std::vector TechnoVectorCeaseFire; - std::vector TechnoVectorNonCeaseFire; + std::vector TechnoVectorCeasefire; + std::vector TechnoVectorNonCeasefire; // Get current selected units. - // If all selected units are at CeaseFire mode, we should cancel their CeaseFire mode. - // Otherwise, we should turn them into CeaseFire mode. + // If all selected units are at Ceasefire mode, we should cancel their Ceasefire mode. + // Otherwise, we should turn them into Ceasefire mode. bool isAnySelectedUnitTogglable = false; - bool isAllSelectedUnitCeaseFireMode = true; + bool isAllSelectedUnitCeasefireMode = true; auto processATechno = [&](TechnoClass* pTechno) { @@ -147,14 +147,14 @@ void CeaseFireModeClass::Execute(WWKey eInput) const isAnySelectedUnitTogglable = true; - if (pTechnoExt->GetPassiveAcquireMode() == PassiveAcquireMode::CeaseFire) + if (pTechnoExt->GetPassiveAcquireMode() == PassiveAcquireMode::Ceasefire) { - TechnoVectorCeaseFire.push_back(pTechno); + TechnoVectorCeasefire.push_back(pTechno); } else { - isAllSelectedUnitCeaseFireMode = false; - TechnoVectorNonCeaseFire.push_back(pTechno); + isAllSelectedUnitCeasefireMode = false; + TechnoVectorNonCeasefire.push_back(pTechno); } return; }; @@ -186,24 +186,24 @@ void CeaseFireModeClass::Execute(WWKey eInput) const // If this boolean is false, then none of the selected units are togglable, meaning this hotket doesn't need to do anything. if (isAnySelectedUnitTogglable) { - // If all selected units are CeaseFire mode, then cancel their CeaseFire mode; - // otherwise, make all selected units CeaseFire mode. - if (isAllSelectedUnitCeaseFireMode) + // If all selected units are Ceasefire mode, then cancel their Ceasefire mode; + // otherwise, make all selected units Ceasefire mode. + if (isAllSelectedUnitCeasefireMode) { - for (const auto& pTechno : TechnoVectorCeaseFire) + for (const auto& pTechno : TechnoVectorCeasefire) EventExt::RaiseTogglePassiveAcquireMode(pTechno, PassiveAcquireMode::Normal); wchar_t buffer[0x100]; - swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:CEASEFIRE_MODE_OFF", L"%i unit(s) ceased Cease Fire Mode."), TechnoVectorCeaseFire.size()); + swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:CEASEFIRE_MODE_OFF", L"%i unit(s) ceased Ceasefire Mode."), TechnoVectorCeasefire.size()); MessageListClass::Instance.PrintMessage(buffer); } else { - for (const auto& pTechno : TechnoVectorNonCeaseFire) - EventExt::RaiseTogglePassiveAcquireMode(pTechno, PassiveAcquireMode::CeaseFire); + for (const auto& pTechno : TechnoVectorNonCeasefire) + EventExt::RaiseTogglePassiveAcquireMode(pTechno, PassiveAcquireMode::Ceasefire); wchar_t buffer[0x100]; - swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:CEASEFIRE_MODE_ON", L"%i unit(s) entered Cease Fire Mode."), TechnoVectorNonCeaseFire.size()); + swprintf_s(buffer, GeneralUtils::LoadStringUnlessMissing("MSG:CEASEFIRE_MODE_ON", L"%i unit(s) entered Ceasefire Mode."), TechnoVectorNonCeasefire.size()); MessageListClass::Instance.PrintMessage(buffer); } } diff --git a/src/Commands/PassiveAcquireMode.h b/src/Commands/PassiveAcquireMode.h index 4eb831c68a..51ba94177c 100644 --- a/src/Commands/PassiveAcquireMode.h +++ b/src/Commands/PassiveAcquireMode.h @@ -13,7 +13,7 @@ class AggressiveModeClass : public CommandClass virtual void Execute(WWKey eInput) const override; }; -class CeaseFireModeClass : public CommandClass +class CeasefireModeClass : public CommandClass { public: // CommandClass diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 22759de06f..2c0294111c 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -830,9 +830,9 @@ void TechnoExt::ExtData::TogglePassiveAcquireMode(PassiveAcquireMode newMode) if (newMode == PassiveAcquireMode::Normal) { - if (previousMode == PassiveAcquireMode::CeaseFire) + if (previousMode == PassiveAcquireMode::Ceasefire) { - voiceIndex = this->TypeExtData->VoiceExitCeaseFireMode.Get(); + voiceIndex = this->TypeExtData->VoiceExitCeasefireMode.Get(); if (voiceIndex < 0) { @@ -856,10 +856,10 @@ void TechnoExt::ExtData::TogglePassiveAcquireMode(PassiveAcquireMode newMode) } } } - else if (newMode == PassiveAcquireMode::CeaseFire) + else if (newMode == PassiveAcquireMode::Ceasefire) { pThis->SetTarget(nullptr); - voiceIndex = this->TypeExtData->VoiceEnterCeaseFireMode.Get(); + voiceIndex = this->TypeExtData->VoiceEnterCeasefireMode.Get(); if (voiceIndex < 0) { diff --git a/src/Ext/Techno/Hooks.TargetEvaluation.cpp b/src/Ext/Techno/Hooks.TargetEvaluation.cpp index 01f3f00965..8f04c08ac3 100644 --- a/src/Ext/Techno/Hooks.TargetEvaluation.cpp +++ b/src/Ext/Techno/Hooks.TargetEvaluation.cpp @@ -392,19 +392,19 @@ DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB0CC, InfantryClass__WhatAction_Wrapper) #pragma region PassiveAcquireMode -DEFINE_HOOK(0x6F8E1F, TechnoClass_SelectAutoTarget_CeaseFireMode, 0x6) +DEFINE_HOOK(0x6F8E1F, TechnoClass_SelectAutoTarget_CeasefireMode, 0x6) { GET(TechnoTypeClass*, pType, EAX); GET(TechnoClass*, pThis, ESI); - R->CL(pType->NoAutoFire || (TechnoExt::ExtMap.Find(pThis)->GetPassiveAcquireMode()) == PassiveAcquireMode::CeaseFire); + R->CL(pType->NoAutoFire || (TechnoExt::ExtMap.Find(pThis)->GetPassiveAcquireMode()) == PassiveAcquireMode::Ceasefire); return R->Origin() + 0x6; } -DEFINE_HOOK(0x7087DD, TechnoClass_CanRetaliateToAttacker_CeaseFireMode, 0x6) +DEFINE_HOOK(0x7087DD, TechnoClass_CanRetaliateToAttacker_CeasefireMode, 0x6) { GET(TechnoTypeClass*, pType, EAX); GET(TechnoClass*, pThis, ESI); - R->CL(pType->CanRetaliate && (TechnoExt::ExtMap.Find(pThis)->GetPassiveAcquireMode() != PassiveAcquireMode::CeaseFire)); + R->CL(pType->CanRetaliate && (TechnoExt::ExtMap.Find(pThis)->GetPassiveAcquireMode() != PassiveAcquireMode::Ceasefire)); return R->Origin() + 0x6; } diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 3cec41b219..f97c830601 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -961,8 +961,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->PassiveAcquireMode_Togglable.Read(exINI, pSection, "PassiveAcquireMode.Togglable"); this->VoiceEnterAggressiveMode.Read(exINI, pSection, "VoiceEnterAggressiveMode"); this->VoiceExitAggressiveMode.Read(exINI, pSection, "VoiceExitAggressiveMode"); - this->VoiceEnterCeaseFireMode.Read(exINI, pSection, "VoiceEnterCeaseFireMode"); - this->VoiceExitCeaseFireMode.Read(exINI, pSection, "VoiceExitCeaseFireMode"); + this->VoiceEnterCeasefireMode.Read(exINI, pSection, "VoiceEnterCeasefireMode"); + this->VoiceExitCeasefireMode.Read(exINI, pSection, "VoiceExitCeasefireMode"); // Ares 0.2 this->RadarJamRadius.Read(exINI, pSection, "RadarJamRadius"); @@ -1612,8 +1612,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->PassiveAcquireMode_Togglable) .Process(this->VoiceEnterAggressiveMode) .Process(this->VoiceExitAggressiveMode) - .Process(this->VoiceEnterCeaseFireMode) - .Process(this->VoiceExitCeaseFireMode) + .Process(this->VoiceEnterCeasefireMode) + .Process(this->VoiceExitCeasefireMode) ; } @@ -1735,9 +1735,9 @@ namespace detail { value = PassiveAcquireMode::Aggressive; } - else if (_strcmpi(str, "CeaseFire") == 0) + else if (_strcmpi(str, "Ceasefire") == 0) { - value = PassiveAcquireMode::CeaseFire; + value = PassiveAcquireMode::Ceasefire; } else { diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 86daf352bd..ffbea6ef12 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -20,7 +20,7 @@ enum class PassiveAcquireMode : BYTE { Normal = 0, Aggressive = 1, - CeaseFire = 2, + Ceasefire = 2, }; class Matrix3D; @@ -436,8 +436,8 @@ class TechnoTypeExt Valueable PassiveAcquireMode_Togglable; ValueableIdx VoiceEnterAggressiveMode; ValueableIdx VoiceExitAggressiveMode; - ValueableIdx VoiceEnterCeaseFireMode; - ValueableIdx VoiceExitCeaseFireMode; + ValueableIdx VoiceEnterCeasefireMode; + ValueableIdx VoiceExitCeasefireMode; ExtData(TechnoTypeClass* OwnerObject) : Extension(OwnerObject) , HealthBar_Hide { false } @@ -818,8 +818,8 @@ class TechnoTypeExt , PassiveAcquireMode_Togglable { true } , VoiceEnterAggressiveMode { -1 } , VoiceExitAggressiveMode { -1 } - , VoiceEnterCeaseFireMode { -1 } - , VoiceExitCeaseFireMode { -1 } + , VoiceEnterCeasefireMode { -1 } + , VoiceExitCeasefireMode { -1 } { } From 715e194ee2f322326a48927bd28204126e96c09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Fri, 12 Sep 2025 00:44:03 +0800 Subject: [PATCH 8/8] Update Hooks.cpp --- src/Ext/Techno/Hooks.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 75007b1560..c3bec9cc99 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -231,6 +231,8 @@ DEFINE_HOOK(0x6F42F7, TechnoClass_Init, 0x2) pThis->TargetingTimer.Start(ScenarioClass::Instance->Random.RandomRanged(0, 15)); } + pExt->InitPassiveAcquireMode(); + return 0; }