diff --git a/CREDITS.md b/CREDITS.md index a620d0129..3ae2d102d 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 4bdf197f4..67885b496 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 8ecc67abb..e5da56bb8 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 1b14db540..0ecb102fe 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 fd094732f..ddc1ba01e 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 043b0ee39..5d702b14c 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 673536259..380081981 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 8370b8e4b..a872b1045 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 2030aeccd..ca0ca34fc 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 451c2625f..a8dd99e38 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 cffde705f..e17751059 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; +}