diff --git a/src/Ext/BuildingType/Hooks.Upgrade.cpp b/src/Ext/BuildingType/Hooks.Upgrade.cpp index b1a78b31ab..f0eaddb958 100644 --- a/src/Ext/BuildingType/Hooks.Upgrade.cpp +++ b/src/Ext/BuildingType/Hooks.Upgrade.cpp @@ -100,18 +100,13 @@ DEFINE_HOOK(0x4F8361, HouseClass_CanBuild_UpgradesInteraction, 0x5) GET_STACK(bool const, includeInProduction, 0xC); GET(CanBuildResult const, resultOfAres, EAX); - if (auto const pBuilding = abstract_cast(pItem)) - { - if (resultOfAres == CanBuildResult::Buildable && BuildingTypeExt::ExtMap.Find(pBuilding)->PowersUp_Buildings.size() > 0) - R->EAX(CheckBuildLimit(pThis, pBuilding, includeInProduction)); - } + if (resultOfAres != CanBuildResult::Buildable) + return 0; - if (resultOfAres == CanBuildResult::Buildable) + if (auto const pBuilding = abstract_cast(pItem)) { - R->EAX(HouseExt::BuildLimitGroupCheck(pThis, pItem, buildLimitOnly, includeInProduction)); - - if (HouseExt::ReachedBuildLimit(pThis, pItem, true)) - R->EAX(CanBuildResult::TemporarilyUnbuildable); + if (BuildingTypeExt::ExtMap.Find(pBuilding)->PowersUp_Buildings.size() > 0) + R->EAX(HouseExt::BuildLimitGroupUpgradeCheck(pThis, pItem, buildLimitOnly, includeInProduction)); } return 0; diff --git a/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp index 49176529ef..3f93c91f55 100644 --- a/src/Ext/House/Body.cpp +++ b/src/Ext/House/Body.cpp @@ -846,7 +846,27 @@ static int CountOwnedIncludeDeploy(const HouseClass* pThis, const TechnoTypeClas return count; } -CanBuildResult HouseExt::BuildLimitGroupCheck(const HouseClass* pThis, const TechnoTypeClass* pItem, bool buildLimitOnly, bool includeQueued) +static void RemoveProduction(const HouseClass* pHouse, const TechnoTypeClass* pType, int num) +{ + const auto absType = pType->WhatAmI(); + const auto buildCat = (absType == AbstractType::BuildingType ? static_cast(pType)->BuildCat : BuildCat::DontCare); + const auto pFactory = pHouse->GetPrimaryFactory(absType, pType->Naval, buildCat); + + if (pFactory) + { + int queued = pFactory->CountTotal(pType); + + if (num >= 0) + queued = Math::min(num, queued); + + for (int i = 0; i < queued; i++) + { + pFactory->RemoveOneFromQueue(pType); + } + } +} + +CanBuildResult HouseExt::BuildLimitGroupUpgradeCheck(const HouseClass* pThis, const TechnoTypeClass* pItem, bool buildLimitOnly, bool includeQueued) { const auto pItemExt = TechnoTypeExt::ExtMap.Find(pItem); @@ -889,88 +909,171 @@ CanBuildResult HouseExt::BuildLimitGroupCheck(const HouseClass* pThis, const Tec } auto const& buildLimit = pItemExt->BuildLimitGroup_Types; + std::vector contributions; + contributions.reserve(buildLimit.size()); + + for (const auto pType : buildLimit) + { + auto const pBuildingType = abstract_cast(pType); + int ownedNow; + + if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) + ownedNow = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pThis)); + else + ownedNow = CountOwnedIncludeDeploy(pThis, pType); + + contributions.push_back(ownedNow * TechnoTypeExt::ExtMap.Find(pType)->BuildLimitGroup_Factor); + } + const int factor = pItemExt->BuildLimitGroup_Factor; + const bool notBuildable = pItemExt->BuildLimitGroup_NotBuildableIfQueueMatch; + const bool contentIfAny = pItemExt->BuildLimitGroup_ContentIfAnyMatch; - if (pItemExt->BuildLimitGroup_ContentIfAnyMatch.Get()) + if (limits.size() == 1) { - bool reachedLimit = false; + int sum = 0; + bool inside = false; - for (size_t i = 0; i < std::min(buildLimit.size(), pItemExt->BuildLimitGroup_Nums.size()); i++) + for (size_t i = 0; i < buildLimit.size(); ++i) { - const auto pType = buildLimit[i]; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); - const auto pBuildingType = abstract_cast(pType); - int ownedNow = 0; + sum += contributions[i]; - if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) - ownedNow = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pThis)); - else - ownedNow = CountOwnedIncludeDeploy(pThis, pType); + if (buildLimit[i] == pItem) + inside = true; + } - ownedNow *= pTypeExt->BuildLimitGroup_Factor; + if (sum >= limits[0] + 1 - factor) + { + const int num = sum - limits[0]; - if (ownedNow >= limits[i] + 1 - factor) - reachedLimit |= (includeQueued && FactoryClass::FindByOwnerAndProduct(pThis, pType)) ? false : true; - } + if (inside) + RemoveProduction(pThis, pItem, (num + factor - 1) / factor); + else if (num >= 1 - factor || notBuildable) + RemoveProduction(pThis, pItem, -1); - return reachedLimit ? CanBuildResult::TemporarilyUnbuildable : CanBuildResult::Buildable; + return CanBuildResult::TemporarilyUnbuildable; + } } else { - if (limits.size() == 1U) + const size_t size = Math::min(limits.size(), buildLimit.size()); + bool reached = true; + bool realReached = true; + + for (size_t i = 0; i < size; i++) { - int sum = 0; - bool reachedLimit = false; + const auto pTmpType = buildLimit[i]; + const int num = contributions[i] - limits[i]; - for (const auto pType : buildLimit) + if (pItem == pTmpType) { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); - const auto pBuildingType = abstract_cast(pType); - int owned = 0; - - if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) - owned = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pThis)); - else - owned = CountOwnedIncludeDeploy(pThis, pType); - - sum += owned * pTypeExt->BuildLimitGroup_Factor; + if (num >= 1 - factor) + { + if (contentIfAny) + { + RemoveProduction(pThis, pItem, (num + factor - 1) / factor); + return CanBuildResult::TemporarilyUnbuildable; + } + else if (num < 1 - factor) + { + realReached = false; + } + } } - - if (sum >= limits[0] + 1 - factor) + else { - for (const auto pType : buildLimit) + if (num >= 0) + { + if (contentIfAny) + { + if (num >= 0 || notBuildable) + { + RemoveProduction(pThis, pItem, -1); + return CanBuildResult::TemporarilyUnbuildable; + } + } + else if (num < 0) + { + realReached = false; + } + } + else { - reachedLimit |= (includeQueued && FactoryClass::FindByOwnerAndProduct(pThis, pType)) ? false : true; + reached = false; } } + } - return reachedLimit ? CanBuildResult::TemporarilyUnbuildable : CanBuildResult::Buildable; + if (reached && (realReached || notBuildable)) + { + RemoveProduction(pThis, pItem, -1); + return CanBuildResult::TemporarilyUnbuildable; } - else + } + + if (contentIfAny) + { + for (size_t i = 0; i < std::min(buildLimit.size(), limits.size()); i++) { - for (size_t i = 0; i < std::min(buildLimit.size(), limits.size()); i++) + const auto pType = buildLimit[i]; + + if (contributions[i] >= limits[i] + 1 - factor) { - const auto pType = buildLimit[i]; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); - const auto pBuildingType = abstract_cast(pType); - int ownedNow = 0; + if (!includeQueued || !FactoryClass::FindByOwnerAndProduct(pThis, pType)) + return CanBuildResult::TemporarilyUnbuildable; + } + } - if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) - ownedNow = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pThis)); - else - ownedNow = CountOwnedIncludeDeploy(pThis, pType); + return CanBuildResult::Buildable; + } + else if (limits.size() == 1) + { + int sum = 0; - ownedNow *= pTypeExt->BuildLimitGroup_Factor; + for (int c : contributions) sum += c; - if ((pItem == pType && ownedNow < limits[i] + 1 - factor) || includeQueued && FactoryClass::FindByOwnerAndProduct(pThis, pType)) - return CanBuildResult::Buildable; + if (sum >= limits[0] + 1 - factor) + { + bool anyQueued = false; - if ((pItem != pType && ownedNow < limits[i]) || includeQueued && FactoryClass::FindByOwnerAndProduct(pThis, pType)) - return CanBuildResult::Buildable; + for (const auto pType : buildLimit) + { + if (includeQueued && FactoryClass::FindByOwnerAndProduct(pThis, pType)) + { + anyQueued = true; + break; + } } - return CanBuildResult::TemporarilyUnbuildable; + if (!anyQueued) + return CanBuildResult::TemporarilyUnbuildable; + } + + return CanBuildResult::Buildable; + } + else + { + for (size_t i = 0; i < std::min(buildLimit.size(), limits.size()); i++) + { + const auto pType = buildLimit[i]; + + if (pItem == pType) + { + if (contributions[i] < limits[i] + 1 - factor) + return CanBuildResult::Buildable; + else if (includeQueued && FactoryClass::FindByOwnerAndProduct(pThis, pType)) + return CanBuildResult::Buildable; + } + else + { + if (contributions[i] < limits[i]) + return CanBuildResult::Buildable; + else if (includeQueued && FactoryClass::FindByOwnerAndProduct(pThis, pType)) + return CanBuildResult::Buildable; + } } + + return CanBuildResult::TemporarilyUnbuildable; } } @@ -995,26 +1098,6 @@ static int QueuedNum(const HouseClass* pHouse, const TechnoTypeClass* pType) return queued; } -static void RemoveProduction(const HouseClass* pHouse, const TechnoTypeClass* pType, int num) -{ - const auto absType = pType->WhatAmI(); - const auto buildCat = (absType == AbstractType::BuildingType ? static_cast(pType)->BuildCat : BuildCat::DontCare); - const auto pFactory = pHouse->GetPrimaryFactory(absType, pType->Naval, buildCat); - - if (pFactory) - { - int queued = pFactory->CountTotal(pType); - - if (num >= 0) - queued = Math::min(num, queued); - - for (int i = 0; i < queued; i++) - { - pFactory->RemoveOneFromQueue(pType); - } - } -} - bool HouseExt::ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass* pType, bool ignoreQueued) { const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index ab0276b395..5c1f70cea3 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -206,7 +206,7 @@ class HouseExt static std::vector AIProduction_BestChoices; static std::vector AIProduction_BestChoicesNaval; - static CanBuildResult BuildLimitGroupCheck(const HouseClass* pThis, const TechnoTypeClass* pItem, bool buildLimitOnly, bool includeQueued); + static CanBuildResult BuildLimitGroupUpgradeCheck(const HouseClass* pThis, const TechnoTypeClass* pItem, bool buildLimitOnly, bool includeQueued); static bool ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass* pType, bool ignoreQueued); static void CalculatePowerSurplus(HouseClass* pThis);