From b6aee6d491de49a8d3993abdb5625f0abdeee90c Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Fri, 27 Dec 2024 20:44:17 +0800 Subject: [PATCH 1/2] Core --- CREDITS.md | 1 + docs/New-or-Enhanced-Logics.md | 17 +++++ docs/Whats-New.md | 1 + src/Ext/House/Hooks.cpp | 109 +++++++++++++++++++++++++++++++++ src/Ext/Rules/Body.cpp | 9 +++ src/Ext/Rules/Body.h | 9 +++ 6 files changed, 146 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index 4c788fe55f..238154ace4 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -372,6 +372,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 + - AI base construction modification - **Ollerus** - Build limit group enhancement - Customizable rocker amplitude diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index e553a34732..24c32e488a 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -581,6 +581,23 @@ SpyEffect.VictimSuperWeapon= ; SuperWeaponType SpyEffect.InfiltratorSuperWeapon= ; SuperWeaponType ``` +### AI base construction modification + +- AI can now have some new behaviors. + - `AIAutoDeployMCV` controls whether AI will still automatically deploy the mcv after owning a construction yard. + - `AISetBaseCenter` controls whether AI will still set the newly deployed construction yard as the base center after owning a construction yard. + - `AIBiasSpawnCell` controls whether AI will preferentially select the construction yard close to the birth point as the base center (useless in campaign). + - `AIForbidConYard` controls whether AI cannot place buildings with `ConstructionYard=true`. AI will try to build one after a construction yard is destroyed but will not put it down. After that, it will continue to build other buildings. Building a construction yard will still take some time. You can try to reduce the build time of it. + +In `rulesmd.ini`: +```ini +[AI] +AIAutoDeployMCV=true ; boolean +AISetBaseCenter=true ; boolean +AIBiasSpawnCell=false ; boolean +AIForbidConYard=false ; boolean +``` + ## Infantry ### Customizable FLH When Infantry Is Prone Or Deployed diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 37c9f39e2f..a8066d30c6 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) +- AI base construction modification (by CrimRecya) - Unit `Speed` setting now accepts floating point values (by Starkku) Vanilla fixes: diff --git a/src/Ext/House/Hooks.cpp b/src/Ext/House/Hooks.cpp index fa005f0ff1..1688dca3fa 100644 --- a/src/Ext/House/Hooks.cpp +++ b/src/Ext/House/Hooks.cpp @@ -233,6 +233,115 @@ DEFINE_HOOK(0x7015C9, TechnoClass_Captured_UpdateTracking, 0x6) #pragma endregion +#pragma region AIConstructionYard + +DEFINE_HOOK(0x740A11, UnitClass_Mission_Guard_AIAutoDeployMCV, 0x6) +{ + enum { SkipGameCode = 0x740A50 }; + + GET(UnitClass*, pMCV, ESI); + + return (!RulesExt::Global()->AIAutoDeployMCV && pMCV->Owner->NumConYards > 0) ? SkipGameCode : 0; +} + +DEFINE_HOOK(0x739889, UnitClass_TryToDeploy_AISetBaseCenter, 0x6) +{ + enum { SkipGameCode = 0x73992B }; + + GET(UnitClass*, pMCV, EBP); + + return (!RulesExt::Global()->AISetBaseCenter && pMCV->Owner->NumConYards > 1) ? SkipGameCode : 0; +} + +DEFINE_HOOK(0x4FD538, HouseClass_AIHouseUpdate_CheckAIBaseCenter, 0x7) +{ + if (RulesExt::Global()->AIBiasSpawnCell && !SessionClass::IsCampaign()) + { + GET(HouseClass*, pAI, EBX); + + if (const auto count = pAI->ConYards.Count) + { + const auto wayPoint = pAI->GetSpawnPosition(); + + if (wayPoint != -1) + { + const auto center = ScenarioClass::Instance->GetWaypointCoords(wayPoint); + auto newCenter = center; + double distanceSquared = 131072.0; + + for (int i = 0; i < count; ++i) + { + if (const auto pBuilding = pAI->ConYards.GetItem(i)) + { + if (pBuilding->IsAlive && pBuilding->Health && !pBuilding->InLimbo) + { + const auto newDistanceSquared = pBuilding->GetMapCoords().DistanceFromSquared(center); + + if (newDistanceSquared < distanceSquared) + { + distanceSquared = newDistanceSquared; + newCenter = pBuilding->GetMapCoords(); + } + } + } + } + + if (newCenter != center) + { + pAI->BaseSpawnCell = newCenter; + pAI->Base.BaseNodes.Items->MapCoords = newCenter; + pAI->Base.Center = newCenter; + } + } + } + } + + return 0; +} + +DEFINE_HOOK(0x4451F8, BuildingClass_KickOutUnit_CleanUpAIBuildingSpace, 0x6) +{ + enum { BuildFailed = 0x445696 }; + + GET(BaseNodeClass* const, pBaseNode, EBX); + GET(BuildingClass* const, pBuilding, EDI); + + const auto pBuildingType = pBuilding->Type; + + if (RulesExt::Global()->AIForbidConYard && pBuildingType->ConstructionYard) + { + if (pBaseNode) + { + pBaseNode->Placed = true; + pBaseNode->Attempts = 0; + } + + return BuildFailed; + } + + return 0; +} + +DEFINE_HOOK(0x42EB8E, BaseClass_GetBaseNodeIndex_CheckValidBaseNode, 0x6) +{ + enum { Valid = 0x42EBC3, Invalid = 0x42EBAE }; + + GET(BaseClass* const, pBase, ESI); + GET(BaseNodeClass* const, pBaseNode, EAX); + + if (pBaseNode->Placed) + { + const auto index = pBaseNode->BuildingTypeIndex; + + if (index >= 0 && index < BuildingTypeClass::Array->Count && BuildingTypeClass::Array->Items[index]->ConstructionYard && RulesExt::Global()->AIForbidConYard) + return Invalid; + } + + return reinterpret_cast(0x50CAD0)(pBase->Owner, pBaseNode) ? Valid : Invalid; +} + +#pragma endregion + DEFINE_HOOK(0x65EB8D, HouseClass_SendSpyPlanes_PlaceAircraft, 0x6) { enum { SkipGameCode = 0x65EBE5, SkipGameCodeNoSuccess = 0x65EC12 }; diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index bc72f69570..c74bb14c7b 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -197,6 +197,11 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->UseFixedVoxelLighting.Read(exINI, GameStrings::AudioVisual, "UseFixedVoxelLighting"); + this->AIAutoDeployMCV.Read(exINI, GameStrings::AI, "AIAutoDeployMCV"); + this->AISetBaseCenter.Read(exINI, GameStrings::AI, "AISetBaseCenter"); + this->AIBiasSpawnCell.Read(exINI, GameStrings::AI, "AIBiasSpawnCell"); + this->AIForbidConYard.Read(exINI, GameStrings::AI, "AIForbidConYard"); + this->GatherWhenMCVDeploy.Read(exINI, GameStrings::General, "GatherWhenMCVDeploy"); this->AIFireSale.Read(exINI, GameStrings::General, "AIFireSale"); this->AIFireSaleDelay.Read(exINI, GameStrings::General, "AIFireSaleDelay"); @@ -381,6 +386,10 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->VoxelLightSource) // .Process(this->VoxelShadowLightSource) .Process(this->UseFixedVoxelLighting) + .Process(this->AIAutoDeployMCV) + .Process(this->AISetBaseCenter) + .Process(this->AIBiasSpawnCell) + .Process(this->AIForbidConYard) .Process(this->GatherWhenMCVDeploy) .Process(this->AIFireSale) .Process(this->AIFireSaleDelay) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index b2cce2d4cd..dde22a747f 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -155,6 +155,11 @@ class RulesExt // Nullable> VoxelShadowLightSource; Valueable UseFixedVoxelLighting; + Valueable AIAutoDeployMCV; + Valueable AISetBaseCenter; + Valueable AIBiasSpawnCell; + Valueable AIForbidConYard; + Valueable GatherWhenMCVDeploy; Valueable AIFireSale; Valueable AIFireSaleDelay; @@ -276,6 +281,10 @@ class RulesExt , VoxelLightSource { } // , VoxelShadowLightSource { } , UseFixedVoxelLighting { false } + , AIAutoDeployMCV { true } + , AISetBaseCenter { true } + , AIBiasSpawnCell { false } + , AIForbidConYard { false } , GatherWhenMCVDeploy { true } , AIFireSale { true } , AIFireSaleDelay { 0 } From 7bd5e27ff632d809209885f2263532d5c4ad9a2b Mon Sep 17 00:00:00 2001 From: CrimRecya <335958461@qq.com> Date: Wed, 22 Jan 2025 13:52:48 +0800 Subject: [PATCH 2/2] Remove useless --- src/Ext/House/Hooks.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ext/House/Hooks.cpp b/src/Ext/House/Hooks.cpp index 1688dca3fa..2072d122aa 100644 --- a/src/Ext/House/Hooks.cpp +++ b/src/Ext/House/Hooks.cpp @@ -289,7 +289,6 @@ DEFINE_HOOK(0x4FD538, HouseClass_AIHouseUpdate_CheckAIBaseCenter, 0x7) if (newCenter != center) { pAI->BaseSpawnCell = newCenter; - pAI->Base.BaseNodes.Items->MapCoords = newCenter; pAI->Base.Center = newCenter; } }