mirror of
https://github.com/vcmi/vcmi.git
synced 2025-04-07 07:10:04 +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);
|
LOG_TRACE(logAi);
|
||||||
NET_EVENT_HANDLER;
|
NET_EVENT_HANDLER;
|
||||||
|
nullkiller->invalidatePathfinderData(); // new hero needs to look around
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIGateway::advmapSpellCast(const CGHeroInstance * caster, SpellID spellID)
|
void AIGateway::advmapSpellCast(const CGHeroInstance * caster, SpellID spellID)
|
||||||
@ -929,7 +930,7 @@ void AIGateway::pickBestCreatures(const CArmedInstance * destinationArmy, const
|
|||||||
|
|
||||||
const CArmedInstance * armies[] = {destinationArmy, source};
|
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)
|
for(auto army : armies)
|
||||||
{
|
{
|
||||||
@ -983,7 +984,7 @@ void AIGateway::pickBestCreatures(const CArmedInstance * destinationArmy, const
|
|||||||
&& source->stacksCount() == 1
|
&& source->stacksCount() == 1
|
||||||
&& (!destinationArmy->hasStackAtSlot(i) || destinationArmy->getCreature(i) == targetCreature))
|
&& (!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)
|
if(weakest->creature == targetCreature)
|
||||||
{
|
{
|
||||||
|
@ -13,8 +13,10 @@
|
|||||||
#include "../Engine/Nullkiller.h"
|
#include "../Engine/Nullkiller.h"
|
||||||
#include "../../../CCallback.h"
|
#include "../../../CCallback.h"
|
||||||
#include "../../../lib/mapObjects/MapObjects.h"
|
#include "../../../lib/mapObjects/MapObjects.h"
|
||||||
|
#include "../../../lib/mapping/CMapDefines.h"
|
||||||
#include "../../../lib/IGameSettings.h"
|
#include "../../../lib/IGameSettings.h"
|
||||||
#include "../../../lib/GameConstants.h"
|
#include "../../../lib/GameConstants.h"
|
||||||
|
#include "../../../lib/TerrainHandler.h"
|
||||||
|
|
||||||
namespace NKAI
|
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
|
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
|
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;
|
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;
|
||||||
{
|
|
||||||
if(left.creature->getLevel() != right.creature->getLevel())
|
|
||||||
return left.creature->getLevel() < right.creature->getLevel();
|
|
||||||
|
|
||||||
return left.creature->getMovementRange() > right.creature->getMovementRange();
|
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
|
||||||
|
{
|
||||||
|
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
|
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);
|
auto sortedSlots = getSortedSlots(target, source);
|
||||||
|
|
||||||
@ -218,7 +262,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
|||||||
&& allowedFactions.size() == alignmentMap.size()
|
&& allowedFactions.size() == alignmentMap.size()
|
||||||
&& source->needsLastStack())
|
&& source->needsLastStack())
|
||||||
{
|
{
|
||||||
auto weakest = getWeakestCreature(resultingArmy);
|
auto weakest = getBestUnitForScout(resultingArmy, armyTerrain);
|
||||||
|
|
||||||
if(weakest->count == 1)
|
if(weakest->count == 1)
|
||||||
{
|
{
|
||||||
@ -398,14 +442,14 @@ std::vector<creInfo> ArmyManager::getArmyAvailableToBuy(
|
|||||||
return creaturesInDwellings;
|
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)
|
if(source->stacksCount() == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bestArmy = getBestArmy(armyCarrier, target, source);
|
auto bestArmy = getBestArmy(armyCarrier, target, source, armyTerrain);
|
||||||
uint64_t newArmy = 0;
|
uint64_t newArmy = 0;
|
||||||
uint64_t oldArmy = target->getArmyStrength();
|
uint64_t oldArmy = target->getArmyStrength();
|
||||||
|
|
||||||
|
@ -53,10 +53,11 @@ public:
|
|||||||
virtual ui64 howManyReinforcementsCanGet(
|
virtual ui64 howManyReinforcementsCanGet(
|
||||||
const IBonusBearer * armyCarrier,
|
const IBonusBearer * armyCarrier,
|
||||||
const CCreatureSet * target,
|
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> getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const = 0;
|
||||||
virtual std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) 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> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const = 0;
|
||||||
virtual std::vector<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) const = 0;
|
virtual std::vector<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) const = 0;
|
||||||
|
|
||||||
@ -97,9 +98,9 @@ public:
|
|||||||
uint8_t turn = 0) const override;
|
uint8_t turn = 0) const override;
|
||||||
|
|
||||||
ui64 howManyReinforcementsCanGet(const CGHeroInstance * hero, const CCreatureSet * source) 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;
|
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 override;
|
std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const override;
|
||||||
std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) 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> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
|
||||||
std::vector<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) 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(
|
auto reinforcement = ai->armyManager->howManyReinforcementsCanGet(
|
||||||
targetHero,
|
targetHero,
|
||||||
targetHero,
|
targetHero,
|
||||||
&*townArmyAvailableToBuy);
|
&*townArmyAvailableToBuy,
|
||||||
|
TerrainId::NONE);
|
||||||
|
|
||||||
if(reinforcement)
|
if(reinforcement)
|
||||||
vstd::amin(reinforcement, ai->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town));
|
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(
|
ai->armyManager->getBestArmy(
|
||||||
path.targetHero,
|
path.targetHero,
|
||||||
path.heroArmy,
|
path.heroArmy,
|
||||||
upgrader->getUpperArmy()));
|
upgrader->getUpperArmy(),
|
||||||
|
TerrainId::NONE));
|
||||||
|
|
||||||
armyToGetOrBuy.upgradeValue -= path.heroArmy->getArmyStrength();
|
armyToGetOrBuy.upgradeValue -= path.heroArmy->getArmyStrength();
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
|
|||||||
{
|
{
|
||||||
if(!startupTown->visitingHero)
|
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());
|
auto paths = ai->pathfinder->getPathInfo(startupTown->visitablePos());
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "../Engine/Nullkiller.h"
|
#include "../Engine/Nullkiller.h"
|
||||||
#include "../../../CCallback.h"
|
#include "../../../CCallback.h"
|
||||||
#include "../../../lib/mapObjects/MapObjects.h"
|
#include "../../../lib/mapObjects/MapObjects.h"
|
||||||
|
#include "../../../lib/mapping/CMapDefines.h"
|
||||||
#include "../../../lib/pathfinder/TurnInfo.h"
|
#include "../../../lib/pathfinder/TurnInfo.h"
|
||||||
#include "Actions/BuyArmyAction.h"
|
#include "Actions/BuyArmyAction.h"
|
||||||
|
|
||||||
@ -394,7 +395,7 @@ HeroExchangeArmy * HeroExchangeMap::tryUpgrade(
|
|||||||
HeroExchangeArmy * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
|
HeroExchangeArmy * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
|
||||||
{
|
{
|
||||||
auto * target = new HeroExchangeArmy();
|
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)
|
for(auto & slotInfo : bestArmy)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user