mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-25 21:38:59 +02:00
Merge pull request #5412 from IvanSavenko/ai_scouts
NKAI - Prefer giving fast units to scouts
This commit is contained in:
commit
32a2413b5e
@ -411,6 +411,7 @@ void AIGateway::heroCreated(const CGHeroInstance * h)
|
||||
{
|
||||
LOG_TRACE(logAi);
|
||||
NET_EVENT_HANDLER;
|
||||
nullkiller->invalidatePathfinderData(); // new hero needs to look around
|
||||
}
|
||||
|
||||
void AIGateway::advmapSpellCast(const CGHeroInstance * caster, SpellID spellID)
|
||||
@ -929,7 +930,7 @@ void AIGateway::pickBestCreatures(const CArmedInstance * destinationArmy, const
|
||||
|
||||
const CArmedInstance * armies[] = {destinationArmy, source};
|
||||
|
||||
auto bestArmy = nullkiller->armyManager->getBestArmy(destinationArmy, destinationArmy, source);
|
||||
auto bestArmy = nullkiller->armyManager->getBestArmy(destinationArmy, destinationArmy, source, myCb->getTile(source->visitablePos())->getTerrainID());
|
||||
|
||||
for(auto army : armies)
|
||||
{
|
||||
@ -983,7 +984,7 @@ void AIGateway::pickBestCreatures(const CArmedInstance * destinationArmy, const
|
||||
&& source->stacksCount() == 1
|
||||
&& (!destinationArmy->hasStackAtSlot(i) || destinationArmy->getCreature(i) == targetCreature))
|
||||
{
|
||||
auto weakest = nullkiller->armyManager->getWeakestCreature(bestArmy);
|
||||
auto weakest = nullkiller->armyManager->getBestUnitForScout(bestArmy, myCb->getTile(source->visitablePos())->getTerrainID());
|
||||
|
||||
if(weakest->creature == targetCreature)
|
||||
{
|
||||
|
@ -13,8 +13,10 @@
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../../../CCallback.h"
|
||||
#include "../../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../../lib/mapping/CMapDefines.h"
|
||||
#include "../../../lib/IGameSettings.h"
|
||||
#include "../../../lib/GameConstants.h"
|
||||
#include "../../../lib/TerrainHandler.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
@ -76,7 +78,7 @@ std::vector<SlotInfo> ArmyManager::toSlotInfo(std::vector<creInfo> army) const
|
||||
|
||||
uint64_t ArmyManager::howManyReinforcementsCanGet(const CGHeroInstance * hero, const CCreatureSet * source) const
|
||||
{
|
||||
return howManyReinforcementsCanGet(hero, hero, source);
|
||||
return howManyReinforcementsCanGet(hero, hero, source, ai->cb->getTile(hero->visitablePos())->getTerrainID());
|
||||
}
|
||||
|
||||
std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const
|
||||
@ -111,17 +113,59 @@ std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, c
|
||||
return resultingArmy;
|
||||
}
|
||||
|
||||
std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<SlotInfo> & army) const
|
||||
std::vector<SlotInfo>::iterator ArmyManager::getBestUnitForScout(std::vector<SlotInfo> & army, const TerrainId & armyTerrain) const
|
||||
{
|
||||
auto weakest = boost::min_element(army, [](const SlotInfo & left, const SlotInfo & right) -> bool
|
||||
uint64_t totalPower = 0;
|
||||
|
||||
for (const auto & unit : army)
|
||||
totalPower += unit.power;
|
||||
|
||||
// TODO: replace with EGameSettings::HEROES_MOVEMENT_COST_BASE in 1.7
|
||||
bool terrainHasPenalty = armyTerrain.hasValue() && armyTerrain.toEntity(VLC)->moveCost != GameConstants::BASE_MOVEMENT_COST;
|
||||
|
||||
// arbitrary threshold - don't give scout more than specified part of total AI value of our army
|
||||
uint64_t maxUnitValue = totalPower / 100;
|
||||
|
||||
const auto & movementPointsLimits = cb->getSettings().getVector(EGameSettings::HEROES_MOVEMENT_POINTS_LAND);
|
||||
|
||||
auto fastest = boost::min_element(army, [&](const SlotInfo & left, const SlotInfo & right) -> bool
|
||||
{
|
||||
if(left.creature->getLevel() != right.creature->getLevel())
|
||||
return left.creature->getLevel() < right.creature->getLevel();
|
||||
|
||||
return left.creature->getMovementRange() > right.creature->getMovementRange();
|
||||
uint64_t leftUnitPower = left.power / left.count;
|
||||
uint64_t rightUnitPower = left.power / left.count;
|
||||
bool leftUnitIsWeak = leftUnitPower < maxUnitValue || left.creature->getLevel() < 4;
|
||||
bool rightUnitIsWeak = rightUnitPower < maxUnitValue || right.creature->getLevel() < 4;
|
||||
|
||||
if (leftUnitIsWeak != rightUnitIsWeak)
|
||||
return leftUnitIsWeak;
|
||||
|
||||
if (terrainHasPenalty)
|
||||
{
|
||||
auto leftNativeTerrain = left.creature->getFactionID().toFaction()->nativeTerrain;
|
||||
auto rightNativeTerrain = right.creature->getFactionID().toFaction()->nativeTerrain;
|
||||
|
||||
if (leftNativeTerrain != rightNativeTerrain)
|
||||
{
|
||||
if (leftNativeTerrain == armyTerrain)
|
||||
return true;
|
||||
|
||||
if (rightNativeTerrain == armyTerrain)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int leftEffectiveMovement = std::min<int>(movementPointsLimits.size() - 1, left.creature->getMovementRange());
|
||||
int rightEffectiveMovement = std::min<int>(movementPointsLimits.size() - 1, right.creature->getMovementRange());
|
||||
|
||||
int leftMovementPointsLimit = movementPointsLimits[leftEffectiveMovement];
|
||||
int rightMovementPointsLimit = movementPointsLimits[rightEffectiveMovement];
|
||||
|
||||
if (leftMovementPointsLimit != rightMovementPointsLimit)
|
||||
return leftMovementPointsLimit > rightMovementPointsLimit;
|
||||
|
||||
return leftUnitPower < rightUnitPower;
|
||||
});
|
||||
|
||||
return weakest;
|
||||
return fastest;
|
||||
}
|
||||
|
||||
class TemporaryArmy : public CArmedInstance
|
||||
@ -134,7 +178,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const
|
||||
std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const
|
||||
{
|
||||
auto sortedSlots = getSortedSlots(target, source);
|
||||
|
||||
@ -218,7 +262,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
&& allowedFactions.size() == alignmentMap.size()
|
||||
&& source->needsLastStack())
|
||||
{
|
||||
auto weakest = getWeakestCreature(resultingArmy);
|
||||
auto weakest = getBestUnitForScout(resultingArmy, armyTerrain);
|
||||
|
||||
if(weakest->count == 1)
|
||||
{
|
||||
@ -398,14 +442,14 @@ std::vector<creInfo> ArmyManager::getArmyAvailableToBuy(
|
||||
return creaturesInDwellings;
|
||||
}
|
||||
|
||||
ui64 ArmyManager::howManyReinforcementsCanGet(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const
|
||||
ui64 ArmyManager::howManyReinforcementsCanGet(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const
|
||||
{
|
||||
if(source->stacksCount() == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto bestArmy = getBestArmy(armyCarrier, target, source);
|
||||
auto bestArmy = getBestArmy(armyCarrier, target, source, armyTerrain);
|
||||
uint64_t newArmy = 0;
|
||||
uint64_t oldArmy = target->getArmyStrength();
|
||||
|
||||
|
@ -53,10 +53,11 @@ public:
|
||||
virtual ui64 howManyReinforcementsCanGet(
|
||||
const IBonusBearer * armyCarrier,
|
||||
const CCreatureSet * target,
|
||||
const CCreatureSet * source) const = 0;
|
||||
const CCreatureSet * source,
|
||||
const TerrainId & armyTerrain) const = 0;
|
||||
|
||||
virtual std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const = 0;
|
||||
virtual std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const = 0;
|
||||
virtual std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const = 0;
|
||||
virtual std::vector<SlotInfo>::iterator getBestUnitForScout(std::vector<SlotInfo> & army, const TerrainId & armyTerrain) const = 0;
|
||||
virtual std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const = 0;
|
||||
virtual std::vector<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) const = 0;
|
||||
|
||||
@ -97,9 +98,9 @@ public:
|
||||
uint8_t turn = 0) const override;
|
||||
|
||||
ui64 howManyReinforcementsCanGet(const CGHeroInstance * hero, const CCreatureSet * source) const override;
|
||||
ui64 howManyReinforcementsCanGet(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const override;
|
||||
std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const override;
|
||||
std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override;
|
||||
ui64 howManyReinforcementsCanGet(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const override;
|
||||
std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const override;
|
||||
std::vector<SlotInfo>::iterator getBestUnitForScout(std::vector<SlotInfo> & army, const TerrainId & armyTerrain) const override;
|
||||
std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
|
||||
std::vector<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) const override;
|
||||
|
||||
|
@ -57,7 +57,8 @@ Goals::TGoalVec BuyArmyBehavior::decompose(const Nullkiller * ai) const
|
||||
auto reinforcement = ai->armyManager->howManyReinforcementsCanGet(
|
||||
targetHero,
|
||||
targetHero,
|
||||
&*townArmyAvailableToBuy);
|
||||
&*townArmyAvailableToBuy,
|
||||
TerrainId::NONE);
|
||||
|
||||
if(reinforcement)
|
||||
vstd::amin(reinforcement, ai->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town));
|
||||
|
@ -300,7 +300,8 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const Nullkiller * ai, const CGT
|
||||
ai->armyManager->getBestArmy(
|
||||
path.targetHero,
|
||||
path.heroArmy,
|
||||
upgrader->getUpperArmy()));
|
||||
upgrader->getUpperArmy(),
|
||||
TerrainId::NONE));
|
||||
|
||||
armyToGetOrBuy.upgradeValue -= path.heroArmy->getArmyStrength();
|
||||
|
||||
|
@ -149,7 +149,7 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
|
||||
{
|
||||
if(!startupTown->visitingHero)
|
||||
{
|
||||
if(ai->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), startupTown->getUpperArmy(), closestHero) > 200)
|
||||
if(ai->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), startupTown->getUpperArmy(), closestHero, TerrainId::NONE) > 200)
|
||||
{
|
||||
auto paths = ai->pathfinder->getPathInfo(startupTown->visitablePos());
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../../../CCallback.h"
|
||||
#include "../../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../../lib/mapping/CMapDefines.h"
|
||||
#include "../../../lib/pathfinder/TurnInfo.h"
|
||||
#include "Actions/BuyArmyAction.h"
|
||||
|
||||
@ -394,7 +395,7 @@ HeroExchangeArmy * HeroExchangeMap::tryUpgrade(
|
||||
HeroExchangeArmy * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
|
||||
{
|
||||
auto * target = new HeroExchangeArmy();
|
||||
auto bestArmy = ai->armyManager->getBestArmy(actor->hero, army1, army2);
|
||||
auto bestArmy = ai->armyManager->getBestArmy(actor->hero, army1, army2, ai->cb->getTile(actor->hero->visitablePos())->getTerrainID());
|
||||
|
||||
for(auto & slotInfo : bestArmy)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user