From 8baebb680b49d9894f811e8b1bdc5dba09a7bc67 Mon Sep 17 00:00:00 2001 From: Starkku Date: Wed, 28 Aug 2024 22:36:44 +0300 Subject: [PATCH] Add allowed / disallowed type lists for FactoryPlant (#1337) ### Allowed / disallowed types for FactoryPlant - It is now possible to customize which TechnoTypes benefit from bonuses of a `FactoryPlant=true` building by listing them on `FactoryPlant.AllowTypes` and/or `FactoryPlant.DisallowTypes`. - `FactoryPlant.Multiplier` (Ares feature) is still applied on the bonuses if they are in effect. In `rulesmd.ini`: ```ini [SOMEBUILDING] ; BuildingType FactoryPlant.AllowTypes= ; List of TechnoTypes FactoryPlant.DisallowTypes= ; List of TechnoTypes ``` --- CREDITS.md | 1 + docs/Fixed-or-Improved-Logics.md | 12 ++++++ docs/Whats-New.md | 1 + src/Ext/Building/Hooks.cpp | 67 +++++++++++++++++++++++++++++ src/Ext/BuildingType/Body.cpp | 5 +++ src/Ext/BuildingType/Body.h | 5 +++ src/Ext/House/Body.cpp | 73 ++++++++++++++++++++++++++++---- src/Ext/House/Body.h | 5 +++ src/Ext/TechnoType/Body.cpp | 2 + src/Ext/TechnoType/Body.h | 2 + src/Ext/TechnoType/Hooks.cpp | 29 +++++++++++++ 11 files changed, 193 insertions(+), 9 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index a620d01298..3ae2d102d8 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -238,6 +238,7 @@ This page lists all the individual contributions to the project by their author. - AI superweapon delay timer customization - Disabling `MultipleFactory` bonus from specific BuildingType - Customizable ChronoSphere teleport delays for units + - Allowed and disallowed types for `FactoryPlant` - **Morton (MortonPL)**: - `XDrawOffset` for animations - Shield passthrough & absorption diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 4bdf197f4b..67885b4960 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -303,6 +303,18 @@ In `rulesmd.ini`: AllowAirstrike= ; boolean ``` +### Allowed / disallowed types for FactoryPlant + +- It is now possible to customize which TechnoTypes benefit from bonuses of a `FactoryPlant=true` building by listing them on `FactoryPlant.AllowTypes` and/or `FactoryPlant.DisallowTypes`. + - `FactoryPlant.Multiplier` (Ares feature) is still applied on the bonuses if they are in effect. + +In `rulesmd.ini`: +```ini +[SOMEBUILDING] ; BuildingType +FactoryPlant.AllowTypes= ; List of TechnoTypes +FactoryPlant.DisallowTypes= ; List of TechnoTypes +``` + ### Apply ZShapePointMove during buildups - By default buildings do not apply `ZShapePointMove` (which offsets the 'z shape' applied on buildings which is used to adjust them in depth buffer and is used to fix issues related to that such as corners of buildings getting cut off when drawn) when buildup is being displayed. This behaviour can now be toggled by setting `ZShapePointMove.OnBuildup`. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 8ecc67abbc..e5da56bb83 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -444,6 +444,7 @@ New: - AI superweapon delay timer customization (by Starkku) - Disabling `MultipleFactory` bonus from specific BuildingType (by Starkku) - Customizable ChronoSphere teleport delays for units (by Starkku) +- Allowed and disallowed types for `FactoryPlant` (by Starkku) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 1b14db540f..0ecb102fee 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -396,3 +396,70 @@ DEFINE_HOOK(0x4511D6, BuildingClass_AnimationAI_SellBuildup, 0x7) return pTypeExt->SellBuildupLength == pThis->Animation.Value ? Continue : Skip; } + +#pragma region FactoryPlant + +DEFINE_HOOK(0x441501, BuildingClass_Unlimbo_FactoryPlant, 0x6) +{ + enum { Skip = 0x441553 }; + + GET(BuildingClass*, pThis, ESI); + + auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + + if (pTypeExt->FactoryPlant_AllowTypes.size() > 0 || pTypeExt->FactoryPlant_DisallowTypes.size() > 0) + { + auto const pHouseExt = HouseExt::ExtMap.Find(pThis->Owner); + pHouseExt->RestrictedFactoryPlants.push_back(pThis); + + return Skip; + } + + return 0; +} + +DEFINE_HOOK(0x448A31, BuildingClass_Captured_FactoryPlant1, 0x6) +{ + enum { Skip = 0x448A78 }; + + GET(BuildingClass*, pThis, ESI); + + auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + + if (pTypeExt->FactoryPlant_AllowTypes.size() > 0 || pTypeExt->FactoryPlant_DisallowTypes.size() > 0) + { + auto const pHouseExt = HouseExt::ExtMap.Find(pThis->Owner); + + if (!pHouseExt->RestrictedFactoryPlants.empty()) + { + auto& vec = pHouseExt->RestrictedFactoryPlants; + vec.erase(std::remove(vec.begin(), vec.end(), pThis), vec.end()); + } + + return Skip; + } + + return 0; +} + +DEFINE_HOOK(0x449149, BuildingClass_Captured_FactoryPlant2, 0x6) +{ + enum { Skip = 0x449197 }; + + GET(BuildingClass*, pThis, ESI); + GET(HouseClass*, pNewOwner, EBP); + + auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + + if (pTypeExt->FactoryPlant_AllowTypes.size() > 0 || pTypeExt->FactoryPlant_DisallowTypes.size() > 0) + { + auto const pHouseExt = HouseExt::ExtMap.Find(pNewOwner); + pHouseExt->RestrictedFactoryPlants.push_back(pThis); + + return Skip; + } + + return 0; +} + +#pragma endregion diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index fd094732fc..ddc1ba01e9 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -149,6 +149,9 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ConsideredVehicle.Read(exINI, pSection, "ConsideredVehicle"); this->SellBuildupLength.Read(exINI, pSection, "SellBuildupLength"); + this->FactoryPlant_AllowTypes.Read(exINI, pSection, "FactoryPlant.AllowTypes"); + this->FactoryPlant_DisallowTypes.Read(exINI, pSection, "FactoryPlant.DisallowTypes"); + if (pThis->NumberOfDocks > 0) { this->AircraftDockingDirs.clear(); @@ -261,6 +264,8 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm) .Process(this->ZShapePointMove_OnBuildup) .Process(this->SellBuildupLength) .Process(this->AircraftDockingDirs) + .Process(this->FactoryPlant_AllowTypes) + .Process(this->FactoryPlant_DisallowTypes) ; } diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 043b0ee393..5d702b14c1 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -65,6 +65,9 @@ class BuildingTypeExt std::vector> AircraftDockingDirs; + ValueableVector FactoryPlant_AllowTypes; + ValueableVector FactoryPlant_DisallowTypes; + ExtData(BuildingTypeClass* OwnerObject) : Extension(OwnerObject) , PowersUp_Owner { AffectedHouse::Owner } , PowersUp_Buildings {} @@ -103,6 +106,8 @@ class BuildingTypeExt , ZShapePointMove_OnBuildup { false } , SellBuildupLength { 23 } , AircraftDockingDirs {} + , FactoryPlant_AllowTypes {} + , FactoryPlant_DisallowTypes {} { } // Ares 0.A functions diff --git a/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp index 673536259b..380081981a 100644 --- a/src/Ext/House/Body.cpp +++ b/src/Ext/House/Body.cpp @@ -557,6 +557,50 @@ int HouseExt::ExtData::GetFactoryCountWithoutNonMFB(AbstractType rtti, bool isNa return Math::max(count, 0); } +float HouseExt::ExtData::GetRestrictedFactoryPlantMult(TechnoTypeClass* pTechnoType) const +{ + float mult = 1.0; + auto const pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType); + + for (auto const pBuilding : this->RestrictedFactoryPlants) + { + auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pBuilding->Type); + + if (pTypeExt->FactoryPlant_AllowTypes.size() > 0 && !pTypeExt->FactoryPlant_AllowTypes.Contains(pTechnoType)) + continue; + + if (pTypeExt->FactoryPlant_DisallowTypes.size() > 0 && pTypeExt->FactoryPlant_DisallowTypes.Contains(pTechnoType)) + continue; + + float currentMult = 1.0f; + + switch (pTechnoType->WhatAmI()) + { + case AbstractType::BuildingType: + if (((BuildingTypeClass*)pTechnoType)->BuildCat == BuildCat::Combat) + currentMult -= pBuilding->Type->DefensesCostBonus; + else + currentMult -= pBuilding->Type->BuildingsCostBonus; + break; + case AbstractType::AircraftType: + currentMult -= pBuilding->Type->AircraftCostBonus; + break; + case AbstractType::InfantryType: + currentMult -= pBuilding->Type->InfantryCostBonus; + break; + case AbstractType::UnitType: + currentMult -= pBuilding->Type->UnitsCostBonus; + break; + default: + break; + } + + mult *= (1.0f - currentMult * pTechnoTypeExt->FactoryPlant_Multiplier); + } + + return mult; +} + void HouseExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) { const char* pSection = this->OwnerObject()->PlainName; @@ -594,6 +638,7 @@ void HouseExt::ExtData::Serialize(T& Stm) .Process(this->Factory_AircraftType) .Process(this->AISuperWeaponDelayTimer) .Process(this->RepairBaseNodes) + .Process(this->RestrictedFactoryPlants) .Process(this->LastBuiltNavalVehicleType) .Process(this->ProducingNavalUnitTypeIndex) .Process(this->NumAirpads_NonMFB) @@ -636,11 +681,21 @@ void HouseExt::ExtData::InvalidatePointer(void* ptr, bool bRemoved) AnnounceInvalidPointer(Factory_NavyType, ptr); AnnounceInvalidPointer(Factory_AircraftType, ptr); - if (!OwnedLimboDeliveredBuildings.empty() && ptr != nullptr) + if (ptr != nullptr) { - auto& vec = this->OwnedLimboDeliveredBuildings; - vec.erase(std::remove(vec.begin(), vec.end(), reinterpret_cast(ptr)), vec.end()); + if (!OwnedLimboDeliveredBuildings.empty()) + { + auto& vec = this->OwnedLimboDeliveredBuildings; + vec.erase(std::remove(vec.begin(), vec.end(), reinterpret_cast(ptr)), vec.end()); + } + + if (!RestrictedFactoryPlants.empty()) + { + auto& vec = this->RestrictedFactoryPlants; + vec.erase(std::remove(vec.begin(), vec.end(), reinterpret_cast(ptr)), vec.end()); + } } + } // ============================= @@ -723,7 +778,7 @@ CanBuildResult HouseExt::BuildLimitGroupCheck(const HouseClass* pThis, const Tec if (!pItemExt->BuildLimitGroup_ExtraLimit_Types.empty() && !pItemExt->BuildLimitGroup_ExtraLimit_Nums.empty()) { - for (size_t i = 0; i < pItemExt->BuildLimitGroup_ExtraLimit_Types.size(); i ++) + for (size_t i = 0; i < pItemExt->BuildLimitGroup_ExtraLimit_Types.size(); i++) { int count = 0; auto pTmpType = pItemExt->BuildLimitGroup_ExtraLimit_Types[i]; @@ -754,7 +809,7 @@ CanBuildResult HouseExt::BuildLimitGroupCheck(const HouseClass* pThis, const Tec { bool reachedLimit = false; - for (size_t i = 0; i < std::min(pItemExt->BuildLimitGroup_Types.size(), pItemExt->BuildLimitGroup_Nums.size()); i ++) + for (size_t i = 0; i < std::min(pItemExt->BuildLimitGroup_Types.size(), pItemExt->BuildLimitGroup_Nums.size()); i++) { TechnoTypeClass* pType = pItemExt->BuildLimitGroup_Types[i]; const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); @@ -807,7 +862,7 @@ CanBuildResult HouseExt::BuildLimitGroupCheck(const HouseClass* pThis, const Tec } else { - for (size_t i = 0; i < std::min(pItemExt->BuildLimitGroup_Types.size(), limits.size()); i ++) + for (size_t i = 0; i < std::min(pItemExt->BuildLimitGroup_Types.size(), limits.size()); i++) { TechnoTypeClass* pType = pItemExt->BuildLimitGroup_Types[i]; const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); @@ -863,7 +918,7 @@ void RemoveProduction(const HouseClass* pHouse, const TechnoTypeClass* pType, in if (num >= 0) queued = Math::min(num, queued); - for (int i = 0; i < queued; i ++) + for (int i = 0; i < queued; i++) { pFactory->RemoveOneFromQueue(pType); } @@ -881,7 +936,7 @@ bool HouseExt::ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass if (!pTypeExt->BuildLimitGroup_ExtraLimit_Types.empty() && !pTypeExt->BuildLimitGroup_ExtraLimit_Nums.empty()) { - for (size_t i = 0; i < pTypeExt->BuildLimitGroup_ExtraLimit_Types.size(); i ++) + for (size_t i = 0; i < pTypeExt->BuildLimitGroup_ExtraLimit_Types.size(); i++) { auto pTmpType = pTypeExt->BuildLimitGroup_ExtraLimit_Types[i]; const auto pBuildingType = abstract_cast(pTmpType); @@ -953,7 +1008,7 @@ bool HouseExt::ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass bool reached = true; bool realReached = true; - for (size_t i = 0; i < size; i ++) + for (size_t i = 0; i < size; i++) { TechnoTypeClass* pTmpType = pTypeExt->BuildLimitGroup_Types[i]; const auto pTmpTypeExt = TechnoTypeExt::ExtMap.Find(pTmpType); diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index 8370b8e4b8..a872b10457 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -40,6 +40,9 @@ class HouseExt //Read from INI bool RepairBaseNodes[3]; + // FactoryPlants with Allow/DisallowTypes set. + std::vector RestrictedFactoryPlants; + int LastBuiltNavalVehicleType; int ProducingNavalUnitTypeIndex; @@ -64,6 +67,7 @@ class HouseExt , Factory_AircraftType { nullptr } , AISuperWeaponDelayTimer {} , RepairBaseNodes { false,false,false } + , RestrictedFactoryPlants {} , LastBuiltNavalVehicleType { -1 } , ProducingNavalUnitTypeIndex { -1 } , NumAirpads_NonMFB { 0 } @@ -79,6 +83,7 @@ class HouseExt int CountOwnedPresentAndLimboed(TechnoTypeClass* pTechnoType); void UpdateNonMFBFactoryCounts(AbstractType rtti, bool remove, bool isNaval); int GetFactoryCountWithoutNonMFB(AbstractType rtti, bool isNaval); + float GetRestrictedFactoryPlantMult(TechnoTypeClass* pTechnoType) const; virtual ~ExtData() = default; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 2030aeccdd..ca0ca34fce 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -131,6 +131,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->UIDescription.Read(exINI, pSection, "UIDescription"); this->LowSelectionPriority.Read(exINI, pSection, "LowSelectionPriority"); this->MindControlRangeLimit.Read(exINI, pSection, "MindControlRangeLimit"); + this->FactoryPlant_Multiplier.Read(exINI, pSection, "FactoryPlant.Multiplier"); this->Spawner_LimitRange.Read(exINI, pSection, "Spawner.LimitRange"); this->Spawner_ExtraLimitRange.Read(exINI, pSection, "Spawner.ExtraLimitRange"); @@ -484,6 +485,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->UIDescription) .Process(this->LowSelectionPriority) .Process(this->MindControlRangeLimit) + .Process(this->FactoryPlant_Multiplier) .Process(this->InterceptorType) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 451c2625f9..a8dd99e38d 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -33,6 +33,7 @@ class TechnoTypeExt Valueable RadarJamRadius; Nullable InhibitorRange; Nullable DesignatorRange; + Valueable FactoryPlant_Multiplier; Valueable MindControlRangeLimit; std::unique_ptr InterceptorType; @@ -264,6 +265,7 @@ class TechnoTypeExt , RadarJamRadius { 0 } , InhibitorRange {} , DesignatorRange { } + , FactoryPlant_Multiplier { 1.0 } , MindControlRangeLimit {} , InterceptorType { nullptr } diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index cffde705f5..e177510592 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -11,6 +11,7 @@ #include "Body.h" #include #include +#include #include #include #include @@ -761,3 +762,31 @@ DEFINE_HOOK(0x737F05, UnitClass_ReceiveDamage_SinkingWake, 0x6) return 0x737F0B; } + +DEFINE_HOOK(0x711F39, TechnoTypeClass_CostOf_FactoryPlant, 0x8) +{ + GET(TechnoTypeClass*, pThis, ESI); + GET(HouseClass*, pHouse, EDI); + REF_STACK(float, mult, STACK_OFFSET(0x10, -0x8)); + + auto const pHouseExt = HouseExt::ExtMap.Find(pHouse); + + if (pHouseExt->RestrictedFactoryPlants.size() > 0) + mult *= pHouseExt->GetRestrictedFactoryPlantMult(pThis); + + return 0; +} + +DEFINE_HOOK(0x711FDF, TechnoTypeClass_RefundAmount_FactoryPlant, 0x8) +{ + GET(TechnoTypeClass*, pThis, ESI); + GET(HouseClass*, pHouse, EDI); + REF_STACK(float, mult, STACK_OFFSET(0x10, -0x4)); + + auto const pHouseExt = HouseExt::ExtMap.Find(pHouse); + + if (pHouseExt->RestrictedFactoryPlants.size() > 0) + mult *= pHouseExt->GetRestrictedFactoryPlantMult(pThis); + + return 0; +}