From 2ad903870909ef01e9dba69218a81e039e02bc52 Mon Sep 17 00:00:00 2001 From: Xilmi Date: Thu, 12 Dec 2024 20:06:33 +0100 Subject: [PATCH] AI can now disband units that block slots for buying better units When the AI cannot buy units in a city because all slots are blocked and the units in the slot are cheaper than the units it wants to buy, the AI will now get rid of the units that block that slot in order to be able to buy the better units. --- AI/Nullkiller/Analyzers/ArmyManager.cpp | 35 +++++++++++++++++++++++-- AI/Nullkiller/Goals/BuyArmy.cpp | 23 ++++++++++++++++ AI/Nullkiller/Pathfinding/Actors.cpp | 10 ++++--- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/AI/Nullkiller/Analyzers/ArmyManager.cpp b/AI/Nullkiller/Analyzers/ArmyManager.cpp index b366fa176..1f6997db3 100644 --- a/AI/Nullkiller/Analyzers/ArmyManager.cpp +++ b/AI/Nullkiller/Analyzers/ArmyManager.cpp @@ -309,6 +309,10 @@ std::vector ArmyManager::getArmyAvailableToBuy( ? dynamic_cast(dwelling) : nullptr; + // Keep track of the least valuable slot in the hero's army + SlotID leastValuableSlot; + int leastValuableStackMarketValue = std::numeric_limits::max(); + for(int i = dwelling->creatures.size() - 1; i >= 0; i--) { auto ci = infoFromDC(dwelling->creatures[i]); @@ -322,13 +326,40 @@ std::vector ArmyManager::getArmyAvailableToBuy( if(!ci.count) continue; + // Calculate the market value of the new stack + int newStackMarketValue = ci.creID.toCreature()->getFullRecruitCost().marketValue() * ci.count; + SlotID dst = hero->getSlotFor(ci.creID); if(!hero->hasStackAtSlot(dst)) //need another new slot for this stack { - if(!freeHeroSlots) //no more place for stacks - continue; + if(!freeHeroSlots) // No free slots; consider replacing + { + // Check for the least valuable existing stack + for (auto& slot : hero->Slots()) + { + if(slot.second->getCreatureID() != CreatureID::NONE) + { + int currentStackMarketValue = + slot.second->getCreatureID().toCreature()->getFullRecruitCost().marketValue() * slot.second->getCount(); + + if(currentStackMarketValue < leastValuableStackMarketValue) + { + leastValuableStackMarketValue = currentStackMarketValue; + leastValuableSlot = slot.first; + } + } + } + + // Decide whether to replace the least valuable stack + if(newStackMarketValue <= leastValuableStackMarketValue) + { + continue; // Skip if the new stack isn't worth replacing + } + } else + { freeHeroSlots--; //new slot will be occupied + } } vstd::amin(ci.count, availableRes / ci.creID.toCreature()->getFullRecruitCost()); //max count we can afford diff --git a/AI/Nullkiller/Goals/BuyArmy.cpp b/AI/Nullkiller/Goals/BuyArmy.cpp index 861097eb8..83f131110 100644 --- a/AI/Nullkiller/Goals/BuyArmy.cpp +++ b/AI/Nullkiller/Goals/BuyArmy.cpp @@ -58,6 +58,29 @@ void BuyArmy::accept(AIGateway * ai) if(ci.count) { + if (!town->getUpperArmy()->hasStackAtSlot(town->getUpperArmy()->getSlotFor(ci.creID))) + { + SlotID lowestValueSlot; + int lowestValue = std::numeric_limits::max(); + for (auto slot : town->getUpperArmy()->Slots()) + { + if (slot.second->getCreatureID() != CreatureID::NONE) + { + int currentStackMarketValue = + slot.second->getCreatureID().toCreature()->getFullRecruitCost().marketValue() * slot.second->getCount(); + + if (currentStackMarketValue < lowestValue) + { + lowestValue = currentStackMarketValue; + lowestValueSlot = slot.first; + } + } + } + if (lowestValueSlot.validSlot()) + { + cb->dismissCreature(town->getUpperArmy(), lowestValueSlot); + } + } cb->recruitCreatures(town, town->getUpperArmy(), ci.creID, ci.count, ci.level); valueBought += ci.count * ci.creID.toCreature()->getAIValue(); } diff --git a/AI/Nullkiller/Pathfinding/Actors.cpp b/AI/Nullkiller/Pathfinding/Actors.cpp index 4d3a86a28..8db9230cc 100644 --- a/AI/Nullkiller/Pathfinding/Actors.cpp +++ b/AI/Nullkiller/Pathfinding/Actors.cpp @@ -374,10 +374,12 @@ HeroExchangeArmy * HeroExchangeMap::tryUpgrade( for(auto & creatureToBuy : buyArmy) { auto targetSlot = target->getSlotFor(creatureToBuy.creID.toCreature()); - - target->addToSlot(targetSlot, creatureToBuy.creID, creatureToBuy.count); - target->armyCost += creatureToBuy.creID.toCreature()->getFullRecruitCost() * creatureToBuy.count; - target->requireBuyArmy = true; + if (targetSlot.validSlot()) + { + target->addToSlot(targetSlot, creatureToBuy.creID, creatureToBuy.count); + target->armyCost += creatureToBuy.creID.toCreature()->getFullRecruitCost() * creatureToBuy.count; + target->requireBuyArmy = true; + } } }