mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-03 13:01:33 +02:00
Nullkiller: gold preasure and turn variables for priority evaluation. Tweaking building behavior
This commit is contained in:
parent
a39fa51e14
commit
17a960e850
@ -156,6 +156,12 @@ void BuildAnalyzer::update()
|
|||||||
|
|
||||||
return val1 > val2;
|
return val1 > val2;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateDailyIncome();
|
||||||
|
|
||||||
|
goldPreasure = (float)armyCost[Res::GOLD] / (1 + cb->getResourceAmount(Res::GOLD) + (float)dailyIncome[Res::GOLD] * 7.0f);
|
||||||
|
|
||||||
|
logAi->trace("Gold preasure: %f", goldPreasure);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildAnalyzer::reset()
|
void BuildAnalyzer::reset()
|
||||||
@ -254,11 +260,12 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
TResources BuildAnalyzer::getDailyIncome() const
|
void BuildAnalyzer::updateDailyIncome()
|
||||||
{
|
{
|
||||||
auto objects = cb->getMyObjects();
|
auto objects = cb->getMyObjects();
|
||||||
auto towns = cb->getTownsInfo();
|
auto towns = cb->getTownsInfo();
|
||||||
TResources dailyIncome = TResources();
|
|
||||||
|
dailyIncome = TResources();
|
||||||
|
|
||||||
for(const CGObjectInstance* obj : objects)
|
for(const CGObjectInstance* obj : objects)
|
||||||
{
|
{
|
||||||
@ -274,8 +281,6 @@ TResources BuildAnalyzer::getDailyIncome() const
|
|||||||
{
|
{
|
||||||
dailyIncome += town->dailyIncome();
|
dailyIncome += town->dailyIncome();
|
||||||
}
|
}
|
||||||
|
|
||||||
return dailyIncome;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TownDevelopmentInfo::addExistingDwelling(const BuildingInfo & existingDwelling)
|
void TownDevelopmentInfo::addExistingDwelling(const BuildingInfo & existingDwelling)
|
||||||
|
@ -70,6 +70,8 @@ private:
|
|||||||
TResources totalDevelopmentCost;
|
TResources totalDevelopmentCost;
|
||||||
std::vector<TownDevelopmentInfo> developmentInfos;
|
std::vector<TownDevelopmentInfo> developmentInfos;
|
||||||
TResources armyCost;
|
TResources armyCost;
|
||||||
|
TResources dailyIncome;
|
||||||
|
float goldPreasure;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void update();
|
void update();
|
||||||
@ -77,8 +79,8 @@ public:
|
|||||||
TResources getResourcesRequiredNow() const;
|
TResources getResourcesRequiredNow() const;
|
||||||
TResources getTotalResourcesRequired() const;
|
TResources getTotalResourcesRequired() const;
|
||||||
const std::vector<TownDevelopmentInfo> & getDevelopmentInfo() const { return developmentInfos; }
|
const std::vector<TownDevelopmentInfo> & getDevelopmentInfo() const { return developmentInfos; }
|
||||||
|
TResources getDailyIncome() const { return dailyIncome; }
|
||||||
TResources getDailyIncome() const;
|
float getGoldPreasure() const { return goldPreasure; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BuildingInfo getBuildingOrPrerequisite(
|
BuildingInfo getBuildingOrPrerequisite(
|
||||||
@ -89,5 +91,6 @@ private:
|
|||||||
|
|
||||||
void updateTownDwellings(TownDevelopmentInfo & developmentInfo);
|
void updateTownDwellings(TownDevelopmentInfo & developmentInfo);
|
||||||
void updateOtherBuildings(TownDevelopmentInfo & developmentInfo);
|
void updateOtherBuildings(TownDevelopmentInfo & developmentInfo);
|
||||||
|
void updateDailyIncome();
|
||||||
void reset();
|
void reset();
|
||||||
};
|
};
|
||||||
|
@ -275,6 +275,35 @@ std::vector<UpgradeInfo> ArmyManager::getDwellingUpgrades(const CCreatureSet * a
|
|||||||
{
|
{
|
||||||
std::vector<UpgradeInfo> upgrades;
|
std::vector<UpgradeInfo> upgrades;
|
||||||
|
|
||||||
|
for(auto creature : army->Slots())
|
||||||
|
{
|
||||||
|
CreatureID initial = creature.second->getCreatureID();
|
||||||
|
auto possibleUpgrades = initial.toCreature()->upgrades;
|
||||||
|
|
||||||
|
vstd::erase_if(possibleUpgrades, [&](CreatureID creID) -> bool
|
||||||
|
{
|
||||||
|
for(auto pair : dwelling->creatures)
|
||||||
|
{
|
||||||
|
if(vstd::contains(pair.second, creID))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(possibleUpgrades.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CreatureID strongestUpgrade = *vstd::minElementByFun(possibleUpgrades, [](CreatureID cre) -> uint64_t
|
||||||
|
{
|
||||||
|
return cre.toCreature()->AIValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
UpgradeInfo upgrade = UpgradeInfo(initial, strongestUpgrade, creature.second->count);
|
||||||
|
|
||||||
|
upgrades.push_back(upgrade);
|
||||||
|
}
|
||||||
|
|
||||||
return upgrades;
|
return upgrades;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,6 +333,9 @@ ArmyUpgradeInfo ArmyManager::calculateCreateresUpgrade(
|
|||||||
const CGObjectInstance * upgrader,
|
const CGObjectInstance * upgrader,
|
||||||
const TResources & availableResources) const
|
const TResources & availableResources) const
|
||||||
{
|
{
|
||||||
|
if(!upgrader)
|
||||||
|
return ArmyUpgradeInfo();
|
||||||
|
|
||||||
std::vector<UpgradeInfo> upgrades = getPossibleUpgrades(army, upgrader);
|
std::vector<UpgradeInfo> upgrades = getPossibleUpgrades(army, upgrader);
|
||||||
|
|
||||||
vstd::erase_if(upgrades, [&](const UpgradeInfo & u) -> bool
|
vstd::erase_if(upgrades, [&](const UpgradeInfo & u) -> bool
|
||||||
|
@ -49,6 +49,7 @@ Goals::TGoalVec BuildingBehavior::getTasks()
|
|||||||
totalDevelopmentCost.toString());
|
totalDevelopmentCost.toString());
|
||||||
|
|
||||||
auto & developmentInfos = ai->nullkiller->buildAnalyzer->getDevelopmentInfo();
|
auto & developmentInfos = ai->nullkiller->buildAnalyzer->getDevelopmentInfo();
|
||||||
|
auto goldPreasure = ai->nullkiller->buildAnalyzer->getGoldPreasure();
|
||||||
|
|
||||||
for(auto & developmentInfo : developmentInfos)
|
for(auto & developmentInfo : developmentInfos)
|
||||||
{
|
{
|
||||||
@ -56,7 +57,8 @@ Goals::TGoalVec BuildingBehavior::getTasks()
|
|||||||
|
|
||||||
for(auto & buildingInfo : developmentInfo.toBuild)
|
for(auto & buildingInfo : developmentInfo.toBuild)
|
||||||
{
|
{
|
||||||
tasks.push_back(sptr(BuildThis(buildingInfo, developmentInfo)));
|
if(goldPreasure < MAX_GOLD_PEASURE || buildingInfo.dailyIncome[Res::GOLD] > 0)
|
||||||
|
tasks.push_back(sptr(BuildThis(buildingInfo, developmentInfo)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "../AIUtility.h"
|
#include "../AIUtility.h"
|
||||||
#include "../Goals/BuyArmy.h"
|
#include "../Goals/BuyArmy.h"
|
||||||
#include "../Goals/VisitTile.h"
|
#include "../Goals/VisitTile.h"
|
||||||
|
#include "../Engine/Nullkiller.h"
|
||||||
#include "lib/mapping/CMap.h" //for victory conditions
|
#include "lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "lib/CPathfinder.h"
|
#include "lib/CPathfinder.h"
|
||||||
|
|
||||||
@ -34,6 +35,9 @@ Goals::TGoalVec BuyArmyBehavior::getTasks()
|
|||||||
|
|
||||||
if(cb->getDate(Date::DAY) == 1)
|
if(cb->getDate(Date::DAY) == 1)
|
||||||
return tasks;
|
return tasks;
|
||||||
|
|
||||||
|
if(ai->nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE)
|
||||||
|
return tasks;
|
||||||
|
|
||||||
auto heroes = cb->getHeroesInfo();
|
auto heroes = cb->getHeroesInfo();
|
||||||
|
|
||||||
|
@ -38,11 +38,11 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logAi->trace("Scanning objects, count %d", objs.size());
|
logAi->debug("Scanning objects, count %d", objs.size());
|
||||||
|
|
||||||
for(auto objToVisit : objs)
|
for(auto objToVisit : objs)
|
||||||
{
|
{
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef AI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Checking object %s, %s", objToVisit->getObjectName(), objToVisit->visitablePos().toString());
|
logAi->trace("Checking object %s, %s", objToVisit->getObjectName(), objToVisit->visitablePos().toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -55,19 +55,19 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
|||||||
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
||||||
std::shared_ptr<ExecuteHeroChain> closestWay;
|
std::shared_ptr<ExecuteHeroChain> closestWay;
|
||||||
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef AI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Found %d paths", paths.size());
|
logAi->trace("Found %d paths", paths.size());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for(auto & path : paths)
|
for(auto & path : paths)
|
||||||
{
|
{
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef AI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Path found %s", path.toString());
|
logAi->trace("Path found %s", path.toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(path.getFirstBlockedAction())
|
if(path.getFirstBlockedAction())
|
||||||
{
|
{
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef AI_TRACE_LEVEL >= 2
|
||||||
// TODO: decomposition?
|
// TODO: decomposition?
|
||||||
logAi->trace("Ignore path. Action is blocked.");
|
logAi->trace("Ignore path. Action is blocked.");
|
||||||
#endif
|
#endif
|
||||||
@ -76,8 +76,8 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
|||||||
|
|
||||||
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
||||||
{
|
{
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef AI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %d", path.heroArmy->getArmyStrength());
|
logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
|
||||||
#endif
|
#endif
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -93,11 +93,11 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
|||||||
|
|
||||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
||||||
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef AI_TRACE_LEVEL >= 2
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",
|
"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",
|
||||||
isSafe ? "safe" : "not safe",
|
isSafe ? "safe" : "not safe",
|
||||||
objToVisit->instanceName,
|
objToVisit->getObjectName(),
|
||||||
hero->name,
|
hero->name,
|
||||||
path.getHeroStrength(),
|
path.getHeroStrength(),
|
||||||
danger,
|
danger,
|
||||||
@ -123,8 +123,11 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
|||||||
if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
|
if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if(ai->nullkiller->getHeroLockedReason(way->hero.get()) == HeroLockedReason::STARTUP)
|
||||||
|
continue;
|
||||||
|
|
||||||
way->evaluationContext.closestWayRatio
|
way->evaluationContext.closestWayRatio
|
||||||
= way->evaluationContext.movementCost / closestWay->evaluationContext.movementCost;
|
= closestWay->evaluationContext.movementCost / way->evaluationContext.movementCost;
|
||||||
|
|
||||||
tasks.push_back(sptr(*way));
|
tasks.push_back(sptr(*way));
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ Goals::TGoalVec StartupBehavior::getTasks()
|
|||||||
&& !town->visitingHero
|
&& !town->visitingHero
|
||||||
&& ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE)
|
&& ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE)
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(0.0001f)));
|
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(MIN_PRIORITY)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ void Nullkiller::updateAiState()
|
|||||||
{
|
{
|
||||||
auto lockedReason = getHeroLockedReason(hero.h);
|
auto lockedReason = getHeroLockedReason(hero.h);
|
||||||
|
|
||||||
return lockedReason == HeroLockedReason::DEFENCE || lockedReason == HeroLockedReason::STARTUP;
|
return lockedReason == HeroLockedReason::DEFENCE;
|
||||||
});
|
});
|
||||||
|
|
||||||
ai->ah->updatePaths(activeHeroes, true);
|
ai->ah->updatePaths(activeHeroes, true);
|
||||||
@ -134,6 +134,13 @@ void Nullkiller::makeTurn()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(bestTask->priority < MIN_PRIORITY)
|
||||||
|
{
|
||||||
|
logAi->trace("Goal %s has too low priority. It is not worth doing it. Ending turn.", bestTask->name());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
logAi->debug("Trying to realize %s (value %2.3f)", bestTask->name(), bestTask->priority);
|
logAi->debug("Trying to realize %s (value %2.3f)", bestTask->name(), bestTask->priority);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
#include "../Goals/AbstractGoal.h"
|
#include "../Goals/AbstractGoal.h"
|
||||||
#include "../Behaviors/Behavior.h"
|
#include "../Behaviors/Behavior.h"
|
||||||
|
|
||||||
|
const float MAX_GOLD_PEASURE = 0.3f;
|
||||||
|
const float MIN_PRIORITY = 0.01f;
|
||||||
|
|
||||||
enum class HeroLockedReason
|
enum class HeroLockedReason
|
||||||
{
|
{
|
||||||
NOT_LOCKED = 0,
|
NOT_LOCKED = 0,
|
||||||
|
@ -49,6 +49,7 @@ void PriorityEvaluator::initVisitTile()
|
|||||||
armyLossPersentageVariable = engine->getInputVariable("armyLoss");
|
armyLossPersentageVariable = engine->getInputVariable("armyLoss");
|
||||||
heroRoleVariable = engine->getInputVariable("heroRole");
|
heroRoleVariable = engine->getInputVariable("heroRole");
|
||||||
dangerVariable = engine->getInputVariable("danger");
|
dangerVariable = engine->getInputVariable("danger");
|
||||||
|
turnVariable = engine->getInputVariable("turn");
|
||||||
mainTurnDistanceVariable = engine->getInputVariable("mainTurnDistance");
|
mainTurnDistanceVariable = engine->getInputVariable("mainTurnDistance");
|
||||||
scoutTurnDistanceVariable = engine->getInputVariable("scoutTurnDistance");
|
scoutTurnDistanceVariable = engine->getInputVariable("scoutTurnDistance");
|
||||||
goldRewardVariable = engine->getInputVariable("goldReward");
|
goldRewardVariable = engine->getInputVariable("goldReward");
|
||||||
@ -57,6 +58,8 @@ void PriorityEvaluator::initVisitTile()
|
|||||||
rewardTypeVariable = engine->getInputVariable("rewardType");
|
rewardTypeVariable = engine->getInputVariable("rewardType");
|
||||||
closestHeroRatioVariable = engine->getInputVariable("closestHeroRatio");
|
closestHeroRatioVariable = engine->getInputVariable("closestHeroRatio");
|
||||||
strategicalValueVariable = engine->getInputVariable("strategicalValue");
|
strategicalValueVariable = engine->getInputVariable("strategicalValue");
|
||||||
|
goldPreasureVariable = engine->getInputVariable("goldPreasure");
|
||||||
|
goldCostVariable = engine->getInputVariable("goldCost");
|
||||||
value = engine->getOutputVariable("Value");
|
value = engine->getOutputVariable("Value");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +122,25 @@ uint64_t getDwellingScore(const CGObjectInstance * target, bool checkGold)
|
|||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getDwellingArmyCost(const CGObjectInstance * target)
|
||||||
|
{
|
||||||
|
auto dwelling = dynamic_cast<const CGDwelling *>(target);
|
||||||
|
int cost = 0;
|
||||||
|
|
||||||
|
for(auto & creLevel : dwelling->creatures)
|
||||||
|
{
|
||||||
|
if(creLevel.first && creLevel.second.size())
|
||||||
|
{
|
||||||
|
auto creature = creLevel.second.back().toCreature();
|
||||||
|
auto creaturesAreFree = creature->level == 1;
|
||||||
|
if(!creaturesAreFree)
|
||||||
|
cost += creature->cost[Res::GOLD] * creLevel.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t evaluateArtifactArmyValue(CArtifactInstance * art)
|
uint64_t evaluateArtifactArmyValue(CArtifactInstance * art)
|
||||||
{
|
{
|
||||||
if(art->artType->id == ArtifactID::SPELL_SCROLL)
|
if(art->artType->id == ArtifactID::SPELL_SCROLL)
|
||||||
@ -192,6 +214,30 @@ uint64_t getArmyReward(const CGObjectInstance * target, const CGHeroInstance * h
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army)
|
||||||
|
{
|
||||||
|
if(!target)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch(target->ID)
|
||||||
|
{
|
||||||
|
case Obj::HILL_FORT:
|
||||||
|
return ai->ah->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeCost[Res::GOLD];
|
||||||
|
case Obj::SCHOOL_OF_MAGIC:
|
||||||
|
case Obj::SCHOOL_OF_WAR:
|
||||||
|
return 1000;
|
||||||
|
case Obj::UNIVERSITY:
|
||||||
|
return 2000;
|
||||||
|
case Obj::CREATURE_GENERATOR1:
|
||||||
|
case Obj::CREATURE_GENERATOR2:
|
||||||
|
case Obj::CREATURE_GENERATOR3:
|
||||||
|
case Obj::CREATURE_GENERATOR4:
|
||||||
|
return getDwellingArmyCost(target);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float getStrategicalValue(const CGObjectInstance * target);
|
float getStrategicalValue(const CGObjectInstance * target);
|
||||||
|
|
||||||
float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy)
|
float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy)
|
||||||
@ -216,9 +262,11 @@ float getResourceRequirementStrength(int resType)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(dailyIncome[resType] == 0)
|
if(dailyIncome[resType] == 0)
|
||||||
return 1;
|
return 1.0f;
|
||||||
|
|
||||||
return (float)requiredResources[resType] / dailyIncome[resType] / 3;
|
float ratio = (float)requiredResources[resType] / dailyIncome[resType] / 2;
|
||||||
|
|
||||||
|
return std::min(ratio, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float getTotalResourceRequirementStrength(int resType)
|
float getTotalResourceRequirementStrength(int resType)
|
||||||
@ -229,10 +277,11 @@ float getTotalResourceRequirementStrength(int resType)
|
|||||||
if(requiredResources[resType] == 0)
|
if(requiredResources[resType] == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(dailyIncome[resType] == 0)
|
float ratio = dailyIncome[resType] == 0
|
||||||
return requiredResources[resType] / 30;
|
? requiredResources[resType] / 50
|
||||||
|
: (float)requiredResources[resType] / dailyIncome[resType] / 50;
|
||||||
|
|
||||||
return (float)requiredResources[resType] / dailyIncome[resType] / 30;
|
return std::min(ratio, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float getStrategicalValue(const CGObjectInstance * target)
|
float getStrategicalValue(const CGObjectInstance * target)
|
||||||
@ -243,18 +292,21 @@ float getStrategicalValue(const CGObjectInstance * target)
|
|||||||
switch(target->ID)
|
switch(target->ID)
|
||||||
{
|
{
|
||||||
case Obj::MINE:
|
case Obj::MINE:
|
||||||
return target->subID == Res::GOLD ? 0.8f : 0.05f + 0.3f * getTotalResourceRequirementStrength(target->subID) + 0.5f * getResourceRequirementStrength(target->subID);
|
return target->subID == Res::GOLD ? 0.5f : 0.05f * getTotalResourceRequirementStrength(target->subID) + 0.05f * getResourceRequirementStrength(target->subID);
|
||||||
|
|
||||||
case Obj::RESOURCE:
|
case Obj::RESOURCE:
|
||||||
return target->subID == Res::GOLD ? 0 : 0.5f * getResourceRequirementStrength(target->subID);
|
return target->subID == Res::GOLD ? 0 : 0.3f * getResourceRequirementStrength(target->subID);
|
||||||
|
|
||||||
case Obj::TOWN:
|
case Obj::TOWN:
|
||||||
return target->tempOwner == PlayerColor::NEUTRAL ? 0.5 : 1;
|
return dynamic_cast<const CGTownInstance *>(target)->hasFort()
|
||||||
|
? (target->tempOwner == PlayerColor::NEUTRAL ? 0.8f : 1.0f)
|
||||||
|
: 0.4f;
|
||||||
|
|
||||||
case Obj::HERO:
|
case Obj::HERO:
|
||||||
return cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
|
return cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
|
||||||
? getEnemyHeroStrategicalValue(dynamic_cast<const CGHeroInstance *>(target))
|
? getEnemyHeroStrategicalValue(dynamic_cast<const CGHeroInstance *>(target))
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -388,13 +440,16 @@ public:
|
|||||||
auto day = cb->getDate(Date::DAY);
|
auto day = cb->getDate(Date::DAY);
|
||||||
auto hero = heroPtr.get();
|
auto hero = heroPtr.get();
|
||||||
bool checkGold = evaluationContext.danger == 0;
|
bool checkGold = evaluationContext.danger == 0;
|
||||||
|
auto army = chain.getPath().heroArmy;
|
||||||
|
|
||||||
evaluationContext.armyLossPersentage = task->evaluationContext.armyLoss / (double)task->evaluationContext.heroStrength;
|
evaluationContext.armyLossPersentage = task->evaluationContext.armyLoss / (double)task->evaluationContext.heroStrength;
|
||||||
evaluationContext.heroRole = ai->ah->getHeroRole(heroPtr);
|
evaluationContext.heroRole = ai->ah->getHeroRole(heroPtr);
|
||||||
evaluationContext.goldReward = getGoldReward(target, hero);
|
evaluationContext.goldReward = getGoldReward(target, hero);
|
||||||
evaluationContext.armyReward = getArmyReward(target, hero, chain.getPath().heroArmy, checkGold);
|
evaluationContext.armyReward = getArmyReward(target, hero, army, checkGold);
|
||||||
evaluationContext.skillReward = getSkillReward(target, hero, evaluationContext.heroRole);
|
evaluationContext.skillReward = getSkillReward(target, hero, evaluationContext.heroRole);
|
||||||
evaluationContext.strategicalValue = getStrategicalValue(target);
|
evaluationContext.strategicalValue = getStrategicalValue(target);
|
||||||
|
evaluationContext.goldCost = getGoldCost(target, hero, army);
|
||||||
|
evaluationContext.turn = chain.getPath().turn();
|
||||||
|
|
||||||
return evaluationContext;
|
return evaluationContext;
|
||||||
}
|
}
|
||||||
@ -409,15 +464,16 @@ public:
|
|||||||
Goals::BuildThis & buildThis = dynamic_cast<Goals::BuildThis &>(*task);
|
Goals::BuildThis & buildThis = dynamic_cast<Goals::BuildThis &>(*task);
|
||||||
auto & bi = buildThis.buildingInfo;
|
auto & bi = buildThis.buildingInfo;
|
||||||
|
|
||||||
evaluationContext.goldReward = bi.dailyIncome[Res::GOLD] / 2;
|
evaluationContext.goldReward = 7 * bi.dailyIncome[Res::GOLD] / 2; // 7 day income but half we already have
|
||||||
evaluationContext.heroRole = HeroRole::MAIN;
|
evaluationContext.heroRole = HeroRole::MAIN;
|
||||||
evaluationContext.movementCostByRole[evaluationContext.heroRole] = bi.prerequisitesCount;
|
evaluationContext.movementCostByRole[evaluationContext.heroRole] = bi.prerequisitesCount;
|
||||||
evaluationContext.armyReward = 0;
|
evaluationContext.armyReward = 0;
|
||||||
evaluationContext.strategicalValue = buildThis.townInfo.armyScore / 50000.0;
|
evaluationContext.strategicalValue = buildThis.townInfo.armyScore / 50000.0;
|
||||||
|
evaluationContext.goldCost = bi.buildCostWithPrerequisits[Res::GOLD];
|
||||||
|
|
||||||
if(bi.creatureID != CreatureID::NONE)
|
if(bi.creatureID != CreatureID::NONE)
|
||||||
{
|
{
|
||||||
evaluationContext.strategicalValue += 0.5f + 0.1f * bi.creatureLevel;
|
evaluationContext.strategicalValue += (0.5f + 0.1f * bi.creatureLevel) / (float)bi.prerequisitesCount;
|
||||||
|
|
||||||
if(bi.baseCreatureID == bi.creatureID)
|
if(bi.baseCreatureID == bi.creatureID)
|
||||||
{
|
{
|
||||||
@ -429,7 +485,11 @@ public:
|
|||||||
|
|
||||||
evaluationContext.armyReward = upgradedPower - creaturesToUpgrade.power;
|
evaluationContext.armyReward = upgradedPower - creaturesToUpgrade.power;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
evaluationContext.strategicalValue = ai->nullkiller->buildAnalyzer->getGoldPreasure() * evaluationContext.goldReward / 300.0f;
|
||||||
|
}
|
||||||
|
|
||||||
return evaluationContext;
|
return evaluationContext;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -485,6 +545,9 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
|||||||
rewardTypeVariable->setValue(rewardType);
|
rewardTypeVariable->setValue(rewardType);
|
||||||
closestHeroRatioVariable->setValue(evaluationContext.closestWayRatio);
|
closestHeroRatioVariable->setValue(evaluationContext.closestWayRatio);
|
||||||
strategicalValueVariable->setValue(evaluationContext.strategicalValue);
|
strategicalValueVariable->setValue(evaluationContext.strategicalValue);
|
||||||
|
goldPreasureVariable->setValue(ai->nullkiller->buildAnalyzer->getGoldPreasure());
|
||||||
|
goldCostVariable->setValue(evaluationContext.goldCost / ((float)cb->getResourceAmount(Res::GOLD) + (float)ai->nullkiller->buildAnalyzer->getDailyIncome()[Res::GOLD] + 1.0f));
|
||||||
|
turnVariable->setValue(evaluationContext.turn);
|
||||||
|
|
||||||
engine->process();
|
engine->process();
|
||||||
//engine.process(VISIT_TILE); //TODO: Process only Visit_Tile
|
//engine.process(VISIT_TILE); //TODO: Process only Visit_Tile
|
||||||
@ -497,16 +560,18 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
|||||||
assert(result >= 0);
|
assert(result >= 0);
|
||||||
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
logAi->trace("Evaluated %s, loss: %f, turns main: %f, scout: %f, gold: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, result %f",
|
logAi->trace("Evaluated %s, loss: %f, turns main: %f, scout: %f, gold: %d, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, result %f",
|
||||||
task->name(),
|
task->name(),
|
||||||
evaluationContext.armyLossPersentage,
|
evaluationContext.armyLossPersentage,
|
||||||
evaluationContext.movementCostByRole[HeroRole::MAIN],
|
evaluationContext.movementCostByRole[HeroRole::MAIN],
|
||||||
evaluationContext.movementCostByRole[HeroRole::SCOUT],
|
evaluationContext.movementCostByRole[HeroRole::SCOUT],
|
||||||
evaluationContext.goldReward,
|
evaluationContext.goldReward,
|
||||||
|
evaluationContext.goldCost,
|
||||||
evaluationContext.armyReward,
|
evaluationContext.armyReward,
|
||||||
evaluationContext.danger,
|
evaluationContext.danger,
|
||||||
evaluationContext.heroRole ? "scout" : "main",
|
evaluationContext.heroRole ? "scout" : "main",
|
||||||
evaluationContext.strategicalValue,
|
evaluationContext.strategicalValue,
|
||||||
|
evaluationContext.closestWayRatio,
|
||||||
result);
|
result);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ private:
|
|||||||
fl::InputVariable * heroRoleVariable;
|
fl::InputVariable * heroRoleVariable;
|
||||||
fl::InputVariable * mainTurnDistanceVariable;
|
fl::InputVariable * mainTurnDistanceVariable;
|
||||||
fl::InputVariable * scoutTurnDistanceVariable;
|
fl::InputVariable * scoutTurnDistanceVariable;
|
||||||
|
fl::InputVariable * turnVariable;
|
||||||
fl::InputVariable * goldRewardVariable;
|
fl::InputVariable * goldRewardVariable;
|
||||||
fl::InputVariable * armyRewardVariable;
|
fl::InputVariable * armyRewardVariable;
|
||||||
fl::InputVariable * dangerVariable;
|
fl::InputVariable * dangerVariable;
|
||||||
@ -39,6 +40,8 @@ private:
|
|||||||
fl::InputVariable * strategicalValueVariable;
|
fl::InputVariable * strategicalValueVariable;
|
||||||
fl::InputVariable * rewardTypeVariable;
|
fl::InputVariable * rewardTypeVariable;
|
||||||
fl::InputVariable * closestHeroRatioVariable;
|
fl::InputVariable * closestHeroRatioVariable;
|
||||||
|
fl::InputVariable * goldPreasureVariable;
|
||||||
|
fl::InputVariable * goldCostVariable;
|
||||||
fl::OutputVariable * value;
|
fl::OutputVariable * value;
|
||||||
std::map<Goals::EGoals, std::shared_ptr<IEvaluationContextBuilder>> evaluationContextBuilders;
|
std::map<Goals::EGoals, std::shared_ptr<IEvaluationContextBuilder>> evaluationContextBuilders;
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
|
|||||||
return evaluateDanger(tile, visitor, ai.get());
|
return evaluateDanger(tile, visitor, ai.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai)
|
ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai, bool checkGuards)
|
||||||
{
|
{
|
||||||
auto cb = ai->myCb;
|
auto cb = ai->myCb;
|
||||||
const TerrainTile * t = cb->getTile(tile, false);
|
const TerrainTile * t = cb->getTile(tile, false);
|
||||||
@ -260,12 +260,15 @@ ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto guards = cb->getGuardingCreatures(tile);
|
if(checkGuards)
|
||||||
for(auto cre : guards)
|
|
||||||
{
|
{
|
||||||
float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre));
|
auto guards = cb->getGuardingCreatures(tile);
|
||||||
|
for(auto cre : guards)
|
||||||
|
{
|
||||||
|
float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre));
|
||||||
|
|
||||||
vstd::amax(guardDanger, evaluateDanger(cre, ai) * tacticalAdvantage); //we are interested in strongest monster around
|
vstd::amax(guardDanger, evaluateDanger(cre, ai) * tacticalAdvantage); //we are interested in strongest monster around
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO mozna odwiedzic blockvis nie ruszajac straznika
|
//TODO mozna odwiedzic blockvis nie ruszajac straznika
|
||||||
|
@ -44,6 +44,6 @@ public:
|
|||||||
//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec);
|
//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec);
|
||||||
|
|
||||||
ui64 evaluateDanger(const CGObjectInstance * obj, const VCAI * ai);
|
ui64 evaluateDanger(const CGObjectInstance * obj, const VCAI * ai);
|
||||||
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai);
|
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai, bool checkGuards = true);
|
||||||
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
|
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
|
||||||
};
|
};
|
||||||
|
@ -104,9 +104,11 @@ namespace Goals
|
|||||||
float armyLossPersentage;
|
float armyLossPersentage;
|
||||||
float armyReward;
|
float armyReward;
|
||||||
int32_t goldReward;
|
int32_t goldReward;
|
||||||
|
int32_t goldCost;
|
||||||
float skillReward;
|
float skillReward;
|
||||||
float strategicalValue;
|
float strategicalValue;
|
||||||
HeroRole heroRole;
|
HeroRole heroRole;
|
||||||
|
uint8_t turn;
|
||||||
|
|
||||||
EvaluationContext()
|
EvaluationContext()
|
||||||
: movementCost(0.0),
|
: movementCost(0.0),
|
||||||
@ -118,9 +120,11 @@ namespace Goals
|
|||||||
movementCostByRole(),
|
movementCostByRole(),
|
||||||
skillReward(0),
|
skillReward(0),
|
||||||
goldReward(0),
|
goldReward(0),
|
||||||
|
goldCost(0),
|
||||||
armyReward(0),
|
armyReward(0),
|
||||||
armyLossPersentage(0),
|
armyLossPersentage(0),
|
||||||
heroRole(HeroRole::SCOUT)
|
heroRole(HeroRole::SCOUT),
|
||||||
|
turn(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -69,7 +69,7 @@ void ExchangeSwapTownHeroes::accept(VCAI * ai)
|
|||||||
if(town->visitingHero && town->visitingHero.get() != garrisonHero)
|
if(town->visitingHero && town->visitingHero.get() != garrisonHero)
|
||||||
cb->swapGarrisonHero(town);
|
cb->swapGarrisonHero(town);
|
||||||
|
|
||||||
makePossibleUpgrades(town);
|
ai->makePossibleUpgrades(town);
|
||||||
ai->moveHeroToTile(town->visitablePos(), garrisonHero);
|
ai->moveHeroToTile(town->visitablePos(), garrisonHero);
|
||||||
|
|
||||||
auto upperArmy = town->getUpperArmy();
|
auto upperArmy = town->getUpperArmy();
|
||||||
@ -98,7 +98,7 @@ void ExchangeSwapTownHeroes::accept(VCAI * ai)
|
|||||||
if(town->visitingHero && town->visitingHero != garrisonHero)
|
if(town->visitingHero && town->visitingHero != garrisonHero)
|
||||||
{
|
{
|
||||||
ai->nullkiller->unlockHero(town->visitingHero.get());
|
ai->nullkiller->unlockHero(town->visitingHero.get());
|
||||||
makePossibleUpgrades(town->visitingHero);
|
ai->makePossibleUpgrades(town->visitingHero);
|
||||||
}
|
}
|
||||||
|
|
||||||
logAi->debug("Put hero %s to garrison of %s", garrisonHero->name, town->name);
|
logAi->debug("Put hero %s to garrison of %s", garrisonHero->name, town->name);
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#include "AINodeStorage.h"
|
#include "AINodeStorage.h"
|
||||||
#include "Actions/TownPortalAction.h"
|
#include "Actions/TownPortalAction.h"
|
||||||
#include "../Goals/Goals.h"
|
#include "../Goals/Goals.h"
|
||||||
|
#include "../VCAI.h"
|
||||||
|
#include "../Engine/Nullkiller.h"
|
||||||
#include "../../../CCallback.h"
|
#include "../../../CCallback.h"
|
||||||
#include "../../../lib/mapping/CMap.h"
|
#include "../../../lib/mapping/CMap.h"
|
||||||
#include "../../../lib/mapObjects/MapObjects.h"
|
#include "../../../lib/mapObjects/MapObjects.h"
|
||||||
@ -278,6 +280,7 @@ bool AINodeStorage::calculateHeroChainFinal()
|
|||||||
for(AIPathNode & node : chains)
|
for(AIPathNode & node : chains)
|
||||||
{
|
{
|
||||||
if(node.turns > heroChainTurn
|
if(node.turns > heroChainTurn
|
||||||
|
&& !node.locked
|
||||||
&& node.action != CGPathNode::ENodeAction::UNKNOWN
|
&& node.action != CGPathNode::ENodeAction::UNKNOWN
|
||||||
&& node.actor->actorExchangeCount > 1
|
&& node.actor->actorExchangeCount > 1
|
||||||
&& !hasBetterChain(&node, &node, chains))
|
&& !hasBetterChain(&node, &node, chains))
|
||||||
@ -450,7 +453,7 @@ void AINodeStorage::calculateHeroChain(
|
|||||||
if(carrier->armyLoss < carrier->actor->armyValue
|
if(carrier->armyLoss < carrier->actor->armyValue
|
||||||
&& (carrier->action != CGPathNode::BATTLE || (carrier->actor->allowBattle && carrier->specialAction))
|
&& (carrier->action != CGPathNode::BATTLE || (carrier->actor->allowBattle && carrier->specialAction))
|
||||||
&& carrier->action != CGPathNode::BLOCKING_VISIT
|
&& carrier->action != CGPathNode::BLOCKING_VISIT
|
||||||
&& other->armyLoss < other->actor->armyValue
|
&& (other->armyLoss == 0 || other->armyLoss < other->actor->armyValue)
|
||||||
&& carrier->actor->canExchange(other->actor))
|
&& carrier->actor->canExchange(other->actor))
|
||||||
{
|
{
|
||||||
#if AI_TRACE_LEVEL >= 2
|
#if AI_TRACE_LEVEL >= 2
|
||||||
@ -632,18 +635,24 @@ void AINodeStorage::setTownsAndDwellings(
|
|||||||
{
|
{
|
||||||
uint64_t mask = 1 << actors.size();
|
uint64_t mask = 1 << actors.size();
|
||||||
|
|
||||||
if(!town->garrisonHero && town->getUpperArmy()->getArmyStrength())
|
if(!town->garrisonHero || ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE)
|
||||||
{
|
{
|
||||||
actors.push_back(std::make_shared<TownGarrisonActor>(town, mask));
|
actors.push_back(std::make_shared<TownGarrisonActor>(town, mask));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*auto dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
|
/*auto dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
|
||||||
auto waitForGrowth = dayOfWeek > 4;
|
auto waitForGrowth = dayOfWeek > 4;*/
|
||||||
|
|
||||||
for(auto obj: visitableObjs)
|
for(auto obj: visitableObjs)
|
||||||
{
|
{
|
||||||
const CGDwelling * dwelling = dynamic_cast<const CGDwelling *>(obj);
|
if(obj->ID == Obj::HILL_FORT)
|
||||||
|
{
|
||||||
|
uint64_t mask = 1 << actors.size();
|
||||||
|
|
||||||
|
actors.push_back(std::make_shared<HillFortActor>(obj, mask));
|
||||||
|
}
|
||||||
|
/*const CGDwelling * dwelling = dynamic_cast<const CGDwelling *>(obj);
|
||||||
|
|
||||||
if(dwelling)
|
if(dwelling)
|
||||||
{
|
{
|
||||||
@ -665,8 +674,8 @@ void AINodeStorage::setTownsAndDwellings(
|
|||||||
actors.push_back(dwellingActor);
|
actors.push_back(dwellingActor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
|
std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
|
||||||
@ -979,7 +988,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
|||||||
path.targetHero = node.actor->hero;
|
path.targetHero = node.actor->hero;
|
||||||
path.heroArmy = node.actor->creatureSet;
|
path.heroArmy = node.actor->creatureSet;
|
||||||
path.armyLoss = node.armyLoss;
|
path.armyLoss = node.armyLoss;
|
||||||
path.targetObjectDanger = evaluateDanger(pos, path.targetHero);
|
path.targetObjectDanger = evaluateDanger(pos, path.targetHero, false);
|
||||||
path.targetObjectArmyLoss = evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), path.targetObjectDanger);
|
path.targetObjectArmyLoss = evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), path.targetObjectDanger);
|
||||||
path.chainMask = node.actor->chainMask;
|
path.chainMask = node.actor->chainMask;
|
||||||
path.exchangeCount = node.actor->actorExchangeCount;
|
path.exchangeCount = node.actor->actorExchangeCount;
|
||||||
|
@ -159,13 +159,14 @@ public:
|
|||||||
|
|
||||||
bool isMovementIneficient(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
bool isMovementIneficient(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
||||||
{
|
{
|
||||||
// further chain distribution is calculated as the last stage
|
|
||||||
if(heroChainPass == EHeroChainPass::CHAIN && destination.node->turns > heroChainTurn)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return hasBetterChain(source, destination);
|
return hasBetterChain(source, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isDistanceLimitReached(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
||||||
|
{
|
||||||
|
return heroChainPass == EHeroChainPass::CHAIN && destination.node->turns > heroChainTurn;
|
||||||
|
}
|
||||||
|
|
||||||
template<class NodeRange>
|
template<class NodeRange>
|
||||||
bool hasBetterChain(
|
bool hasBetterChain(
|
||||||
const CGPathNode * source,
|
const CGPathNode * source,
|
||||||
@ -185,9 +186,9 @@ public:
|
|||||||
bool calculateHeroChain();
|
bool calculateHeroChain();
|
||||||
bool calculateHeroChainFinal();
|
bool calculateHeroChainFinal();
|
||||||
|
|
||||||
uint64_t evaluateDanger(const int3 & tile, const CGHeroInstance * hero) const
|
uint64_t evaluateDanger(const int3 & tile, const CGHeroInstance * hero, bool checkGuards) const
|
||||||
{
|
{
|
||||||
return dangerEvaluator->evaluateDanger(tile, hero, ai);
|
return dangerEvaluator->evaluateDanger(tile, hero, ai, checkGuards);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const
|
uint64_t evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
#include "../../../lib/mapping/CMap.h"
|
#include "../../../lib/mapping/CMap.h"
|
||||||
#include "../../../lib/mapObjects/MapObjects.h"
|
#include "../../../lib/mapObjects/MapObjects.h"
|
||||||
|
|
||||||
|
CCreatureSet emptyArmy;
|
||||||
|
|
||||||
bool HeroExchangeArmy::needsLastStack() const
|
bool HeroExchangeArmy::needsLastStack() const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -56,6 +58,21 @@ std::string ChainActor::toString() const
|
|||||||
return hero->name;
|
return hero->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjectActor::ObjectActor(const CGObjectInstance * obj, const CCreatureSet * army, uint64_t chainMask, int initialTurn)
|
||||||
|
:ChainActor(obj, army, chainMask, initialTurn), object(obj)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const CGObjectInstance * ObjectActor::getActorObject() const
|
||||||
|
{
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ObjectActor::toString() const
|
||||||
|
{
|
||||||
|
return object->getObjectName() + " at " + object->visitablePos().toString();
|
||||||
|
}
|
||||||
|
|
||||||
HeroActor::HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const VCAI * ai)
|
HeroActor::HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const VCAI * ai)
|
||||||
:ChainActor(hero, chainMask)
|
:ChainActor(hero, chainMask)
|
||||||
{
|
{
|
||||||
@ -151,18 +168,38 @@ bool HeroExchangeMap::canExchange(const ChainActor * other)
|
|||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
{
|
{
|
||||||
if(other->armyCost.nonZero())
|
TResources resources = ai->myCb->getResourceAmount();
|
||||||
{
|
|
||||||
TResources resources = ai->myCb->getResourceAmount();
|
|
||||||
|
|
||||||
if(!resources.canAfford(actor->armyCost + other->armyCost))
|
if(!resources.canAfford(actor->armyCost + other->armyCost))
|
||||||
{
|
{
|
||||||
result = false;
|
result = false;
|
||||||
return;
|
#if AI_TRACE_LEVEL >= 2
|
||||||
}
|
logAi->trace(
|
||||||
|
"Can not afford exchange because of total cost %s but we have %s",
|
||||||
|
(actor->armyCost + other->armyCost).toString(),
|
||||||
|
resources.toString());
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t reinforcment = ai->ah->howManyReinforcementsCanGet(actor->creatureSet, other->creatureSet);
|
auto upgradeInfo = ai->ah->calculateCreateresUpgrade(
|
||||||
|
actor->creatureSet,
|
||||||
|
other->getActorObject(),
|
||||||
|
resources - actor->armyCost - other->armyCost);
|
||||||
|
|
||||||
|
uint64_t reinforcment = upgradeInfo.upgradeValue;
|
||||||
|
|
||||||
|
if(other->creatureSet->Slots().size())
|
||||||
|
reinforcment += ai->ah->howManyReinforcementsCanGet(actor->creatureSet, other->creatureSet);
|
||||||
|
|
||||||
|
#if AI_TRACE_LEVEL >= 2
|
||||||
|
logAi->trace(
|
||||||
|
"Exchange %s->%s reinforcement: %d, %f%%",
|
||||||
|
actor->toString(),
|
||||||
|
other->toString(),
|
||||||
|
reinforcment,
|
||||||
|
100.0f * reinforcment / actor->armyValue);
|
||||||
|
#endif
|
||||||
|
|
||||||
result = reinforcment > actor->armyValue / 10 || reinforcment > 1000;
|
result = reinforcment > actor->armyValue / 10 || reinforcment > 1000;
|
||||||
}
|
}
|
||||||
@ -204,7 +241,27 @@ HeroActor * HeroExchangeMap::exchange(const ChainActor * other)
|
|||||||
result = exchangeMap.at(other);
|
result = exchangeMap.at(other);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CCreatureSet * newArmy = pickBestCreatures(actor->creatureSet, other->creatureSet);
|
TResources availableResources = ai->myCb->getResourceAmount() - actor->armyCost - other->armyCost;
|
||||||
|
CCreatureSet * upgradedInitialArmy = tryUpgrade(actor->creatureSet, other->getActorObject(), availableResources);
|
||||||
|
CCreatureSet * newArmy;
|
||||||
|
|
||||||
|
if(other->creatureSet->Slots().size())
|
||||||
|
{
|
||||||
|
if(upgradedInitialArmy)
|
||||||
|
{
|
||||||
|
newArmy = pickBestCreatures(upgradedInitialArmy, other->creatureSet);
|
||||||
|
delete upgradedInitialArmy;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newArmy = pickBestCreatures(actor->creatureSet, other->creatureSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newArmy = upgradedInitialArmy;
|
||||||
|
}
|
||||||
|
|
||||||
result = new HeroActor(actor, other, newArmy, ai);
|
result = new HeroActor(actor, other, newArmy, ai);
|
||||||
exchangeMap[other] = result;
|
exchangeMap[other] = result;
|
||||||
}
|
}
|
||||||
@ -212,6 +269,25 @@ HeroActor * HeroExchangeMap::exchange(const ChainActor * other)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CCreatureSet * HeroExchangeMap::tryUpgrade(const CCreatureSet * army, const CGObjectInstance * upgrader, TResources resources) const
|
||||||
|
{
|
||||||
|
auto upgradeInfo = ai->ah->calculateCreateresUpgrade(army, upgrader, resources);
|
||||||
|
|
||||||
|
if(!upgradeInfo.upgradeValue)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
CCreatureSet * target = new HeroExchangeArmy();
|
||||||
|
|
||||||
|
for(auto & slotInfo : upgradeInfo.resultingArmy)
|
||||||
|
{
|
||||||
|
auto targetSlot = target->getFreeSlot();
|
||||||
|
|
||||||
|
target->addToSlot(targetSlot, slotInfo.creature->idNumber, TQuantity(slotInfo.count));
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
CCreatureSet * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
|
CCreatureSet * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
|
||||||
{
|
{
|
||||||
CCreatureSet * target = new HeroExchangeArmy();
|
CCreatureSet * target = new HeroExchangeArmy();
|
||||||
@ -227,8 +303,13 @@ CCreatureSet * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, co
|
|||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HillFortActor::HillFortActor(const CGObjectInstance * hillFort, uint64_t chainMask)
|
||||||
|
:ObjectActor(hillFort, &emptyArmy, chainMask, 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
DwellingActor::DwellingActor(const CGDwelling * dwelling, uint64_t chainMask, bool waitForGrowth, int dayOfWeek)
|
DwellingActor::DwellingActor(const CGDwelling * dwelling, uint64_t chainMask, bool waitForGrowth, int dayOfWeek)
|
||||||
:ChainActor(
|
:ObjectActor(
|
||||||
dwelling,
|
dwelling,
|
||||||
getDwellingCreatures(dwelling, waitForGrowth),
|
getDwellingCreatures(dwelling, waitForGrowth),
|
||||||
chainMask,
|
chainMask,
|
||||||
@ -288,7 +369,7 @@ CCreatureSet * DwellingActor::getDwellingCreatures(const CGDwelling * dwelling,
|
|||||||
}
|
}
|
||||||
|
|
||||||
TownGarrisonActor::TownGarrisonActor(const CGTownInstance * town, uint64_t chainMask)
|
TownGarrisonActor::TownGarrisonActor(const CGTownInstance * town, uint64_t chainMask)
|
||||||
:ChainActor(town, town->getUpperArmy(), chainMask, 0), town(town)
|
:ObjectActor(town, town->getUpperArmy(), chainMask, 0), town(town)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ public:
|
|||||||
virtual std::string toString() const;
|
virtual std::string toString() const;
|
||||||
ChainActor * exchange(const ChainActor * other) const { return exchange(this, other); }
|
ChainActor * exchange(const ChainActor * other) const { return exchange(this, other); }
|
||||||
void setBaseActor(HeroActor * base);
|
void setBaseActor(HeroActor * base);
|
||||||
|
virtual const CGObjectInstance * getActorObject() const { return hero; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ChainActor * exchange(const ChainActor * specialActor, const ChainActor * other) const;
|
virtual ChainActor * exchange(const ChainActor * specialActor, const ChainActor * other) const;
|
||||||
@ -86,6 +87,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
CCreatureSet * pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const;
|
CCreatureSet * pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const;
|
||||||
|
CCreatureSet * tryUpgrade(const CCreatureSet * army, const CGObjectInstance * upgrader, TResources resources) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HeroActor : public ChainActor
|
class HeroActor : public ChainActor
|
||||||
@ -112,7 +114,24 @@ protected:
|
|||||||
virtual ChainActor * exchange(const ChainActor * specialActor, const ChainActor * other) const override;
|
virtual ChainActor * exchange(const ChainActor * specialActor, const ChainActor * other) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DwellingActor : public ChainActor
|
class ObjectActor : public ChainActor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const CGObjectInstance * object;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ObjectActor(const CGObjectInstance * obj, const CCreatureSet * army, uint64_t chainMask, int initialTurn);
|
||||||
|
virtual std::string toString() const override;
|
||||||
|
const CGObjectInstance * getActorObject() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HillFortActor : public ObjectActor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HillFortActor(const CGObjectInstance * hillFort, uint64_t chainMask);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DwellingActor : public ObjectActor
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const CGDwelling * dwelling;
|
const CGDwelling * dwelling;
|
||||||
@ -127,7 +146,7 @@ protected:
|
|||||||
CCreatureSet * getDwellingCreatures(const CGDwelling * dwelling, bool waitForGrowth);
|
CCreatureSet * getDwellingCreatures(const CGDwelling * dwelling, bool waitForGrowth);
|
||||||
};
|
};
|
||||||
|
|
||||||
class TownGarrisonActor : public ChainActor
|
class TownGarrisonActor : public ObjectActor
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const CGTownInstance * town;
|
const CGTownInstance * town;
|
||||||
|
@ -47,151 +47,171 @@ namespace AIPathfinding
|
|||||||
{
|
{
|
||||||
if(nodeStorage->isMovementIneficient(source, destination))
|
if(nodeStorage->isMovementIneficient(source, destination))
|
||||||
{
|
{
|
||||||
|
destination.node->locked = true;
|
||||||
destination.blocked = true;
|
destination.blocked = true;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
|
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
|
||||||
|
|
||||||
if(blocker == BlockingReason::NONE)
|
if(blocker == BlockingReason::NONE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(blocker == BlockingReason::DESTINATION_BLOCKVIS && destination.nodeObject)
|
auto destGuardians = cb->getGuardingCreatures(destination.coord);
|
||||||
|
bool allowBypass = true;
|
||||||
|
|
||||||
|
switch(blocker)
|
||||||
{
|
{
|
||||||
auto enemyHero = destination.nodeHero && destination.heroRelations == PlayerRelations::ENEMIES;
|
case BlockingReason::DESTINATION_GUARDED:
|
||||||
|
allowBypass = bypassDestinationGuards(destGuardians, source, destination, pathfinderConfig, pathfinderHelper);
|
||||||
|
|
||||||
if(!enemyHero && !isObjectRemovable(destination.nodeObject))
|
break;
|
||||||
|
|
||||||
|
case BlockingReason::DESTINATION_BLOCKVIS:
|
||||||
|
allowBypass = destination.nodeObject && bypassRemovableObject(source, destination, pathfinderConfig, pathfinderHelper);
|
||||||
|
|
||||||
|
if(allowBypass && destGuardians.size())
|
||||||
|
allowBypass = bypassDestinationGuards(destGuardians, source, destination, pathfinderConfig, pathfinderHelper);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
destination.blocked = !allowBypass || nodeStorage->isDistanceLimitReached(source, destination);
|
||||||
|
destination.node->locked = !allowBypass;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AIMovementAfterDestinationRule::bypassRemovableObject(
|
||||||
|
const PathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
const PathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) const
|
||||||
|
{
|
||||||
|
auto enemyHero = destination.nodeHero && destination.heroRelations == PlayerRelations::ENEMIES;
|
||||||
|
|
||||||
|
if(!enemyHero && !isObjectRemovable(destination.nodeObject))
|
||||||
|
{
|
||||||
|
if(nodeStorage->getHero(destination.node) == destination.nodeHero)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(destination.nodeObject->ID == Obj::QUEST_GUARD || destination.nodeObject->ID == Obj::BORDERGUARD)
|
||||||
|
{
|
||||||
|
auto questObj = dynamic_cast<const IQuestObject *>(destination.nodeObject);
|
||||||
|
auto nodeHero = pathfinderHelper->hero;
|
||||||
|
|
||||||
|
if(!destination.nodeObject->wasVisited(nodeHero->tempOwner)
|
||||||
|
|| !questObj->checkQuest(nodeHero))
|
||||||
{
|
{
|
||||||
if(nodeStorage->getHero(destination.node) == destination.nodeHero)
|
nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
|
||||||
return;
|
|
||||||
|
|
||||||
destination.blocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(destination.nodeObject->ID == Obj::QUEST_GUARD || destination.nodeObject->ID == Obj::BORDERGUARD)
|
|
||||||
{
|
|
||||||
auto questObj = dynamic_cast<const IQuestObject *>(destination.nodeObject);
|
|
||||||
auto nodeHero = pathfinderHelper->hero;
|
|
||||||
|
|
||||||
if(!destination.nodeObject->wasVisited(nodeHero->tempOwner)
|
|
||||||
|| !questObj->checkQuest(nodeHero))
|
|
||||||
{
|
{
|
||||||
nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
|
auto questInfo = QuestInfo(questObj->quest, destination.nodeObject, destination.coord);
|
||||||
{
|
|
||||||
auto questInfo = QuestInfo(questObj->quest, destination.nodeObject, destination.coord);
|
|
||||||
|
|
||||||
node->specialAction.reset(new QuestAction(questInfo));
|
node->specialAction.reset(new QuestAction(questInfo));
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(blocker == BlockingReason::DESTINATION_VISIT)
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AIMovementAfterDestinationRule::bypassDestinationGuards(
|
||||||
|
std::vector<const CGObjectInstance *> destGuardians,
|
||||||
|
const PathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
const PathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) const
|
||||||
|
{
|
||||||
|
auto srcGuardians = cb->getGuardingCreatures(source.coord);
|
||||||
|
|
||||||
|
if(destGuardians.empty())
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(blocker == BlockingReason::DESTINATION_GUARDED)
|
auto srcNode = nodeStorage->getAINode(source.node);
|
||||||
|
|
||||||
|
vstd::erase_if(destGuardians, [&](const CGObjectInstance * destGuard) -> bool
|
||||||
{
|
{
|
||||||
auto srcGuardians = cb->getGuardingCreatures(source.coord);
|
return vstd::contains(srcGuardians, destGuard);
|
||||||
auto destGuardians = cb->getGuardingCreatures(destination.coord);
|
});
|
||||||
|
|
||||||
if(destGuardians.empty())
|
auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size();
|
||||||
{
|
|
||||||
destination.blocked = true;
|
|
||||||
|
|
||||||
return;
|
if(guardsAlreadyBypassed && srcNode->actor->allowBattle)
|
||||||
}
|
{
|
||||||
|
|
||||||
vstd::erase_if(destGuardians, [&](const CGObjectInstance * destGuard) -> bool
|
|
||||||
{
|
|
||||||
return vstd::contains(srcGuardians, destGuard);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size();
|
|
||||||
auto srcNode = nodeStorage->getAINode(source.node);
|
|
||||||
if(guardsAlreadyBypassed && srcNode->actor->allowBattle)
|
|
||||||
{
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Bypass guard at destination while moving %s -> %s",
|
"Bypass guard at destination while moving %s -> %s",
|
||||||
source.coord.toString(),
|
source.coord.toString(),
|
||||||
destination.coord.toString());
|
destination.coord.toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
const AIPathNode * destNode = nodeStorage->getAINode(destination.node);
|
|
||||||
auto battleNodeOptional = nodeStorage->getOrCreateNode(
|
|
||||||
destination.coord,
|
|
||||||
destination.node->layer,
|
|
||||||
destNode->actor->battleActor);
|
|
||||||
|
|
||||||
if(!battleNodeOptional)
|
|
||||||
{
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
|
||||||
logAi->trace(
|
|
||||||
"Can not allocate battle node while moving %s -> %s",
|
|
||||||
source.coord.toString(),
|
|
||||||
destination.coord.toString());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
destination.blocked = true;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AIPathNode * battleNode = battleNodeOptional.get();
|
|
||||||
|
|
||||||
if(battleNode->locked)
|
|
||||||
{
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
|
||||||
logAi->trace(
|
|
||||||
"Block bypass guard at destination while moving %s -> %s",
|
|
||||||
source.coord.toString(),
|
|
||||||
destination.coord.toString());
|
|
||||||
#endif
|
|
||||||
destination.blocked = true;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto hero = nodeStorage->getHero(source.node);
|
|
||||||
auto danger = nodeStorage->evaluateDanger(destination.coord, hero);
|
|
||||||
double actualArmyValue = srcNode->actor->armyValue - srcNode->armyLoss;
|
|
||||||
double loss = nodeStorage->evaluateArmyLoss(hero, actualArmyValue, danger);
|
|
||||||
|
|
||||||
if(loss < actualArmyValue)
|
|
||||||
{
|
|
||||||
destination.node = battleNode;
|
|
||||||
nodeStorage->commit(destination, source);
|
|
||||||
|
|
||||||
battleNode->armyLoss += loss;
|
|
||||||
|
|
||||||
vstd::amax(battleNode->danger, danger);
|
|
||||||
|
|
||||||
battleNode->specialAction = std::make_shared<BattleAction>(destination.coord);
|
|
||||||
|
|
||||||
if(source.nodeObject && isObjectRemovable(source.nodeObject))
|
|
||||||
{
|
|
||||||
battleNode->theNodeBefore = source.node;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
|
||||||
logAi->trace(
|
|
||||||
"Begin bypass guard at destination with danger %s while moving %s -> %s",
|
|
||||||
std::to_string(danger),
|
|
||||||
source.coord.toString(),
|
|
||||||
destination.coord.toString());
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destination.blocked = true;
|
const AIPathNode * destNode = nodeStorage->getAINode(destination.node);
|
||||||
|
auto battleNodeOptional = nodeStorage->getOrCreateNode(
|
||||||
|
destination.coord,
|
||||||
|
destination.node->layer,
|
||||||
|
destNode->actor->battleActor);
|
||||||
|
|
||||||
|
if(!battleNodeOptional)
|
||||||
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
|
logAi->trace(
|
||||||
|
"Can not allocate battle node while moving %s -> %s",
|
||||||
|
source.coord.toString(),
|
||||||
|
destination.coord.toString());
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AIPathNode * battleNode = battleNodeOptional.get();
|
||||||
|
|
||||||
|
if(battleNode->locked)
|
||||||
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
|
logAi->trace(
|
||||||
|
"Block bypass guard at destination while moving %s -> %s",
|
||||||
|
source.coord.toString(),
|
||||||
|
destination.coord.toString());
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hero = nodeStorage->getHero(source.node);
|
||||||
|
auto danger = nodeStorage->evaluateDanger(destination.coord, hero, true);
|
||||||
|
double actualArmyValue = srcNode->actor->armyValue - srcNode->armyLoss;
|
||||||
|
double loss = nodeStorage->evaluateArmyLoss(hero, actualArmyValue, danger);
|
||||||
|
|
||||||
|
if(loss < actualArmyValue)
|
||||||
|
{
|
||||||
|
destination.node = battleNode;
|
||||||
|
nodeStorage->commit(destination, source);
|
||||||
|
|
||||||
|
battleNode->armyLoss += loss;
|
||||||
|
|
||||||
|
vstd::amax(battleNode->danger, danger);
|
||||||
|
|
||||||
|
battleNode->specialAction = std::make_shared<BattleAction>(destination.coord);
|
||||||
|
|
||||||
|
if(source.nodeObject && isObjectRemovable(source.nodeObject))
|
||||||
|
{
|
||||||
|
battleNode->theNodeBefore = source.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
|
logAi->trace(
|
||||||
|
"Begin bypass guard at destination with danger %s while moving %s -> %s",
|
||||||
|
std::to_string(danger),
|
||||||
|
source.coord.toString(),
|
||||||
|
destination.coord.toString());
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,5 +32,19 @@ namespace AIPathfinding
|
|||||||
CDestinationNodeInfo & destination,
|
CDestinationNodeInfo & destination,
|
||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
CPathfinderHelper * pathfinderHelper) const override;
|
CPathfinderHelper * pathfinderHelper) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool bypassDestinationGuards(
|
||||||
|
std::vector<const CGObjectInstance *> destGuardians,
|
||||||
|
const PathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
const PathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) const;
|
||||||
|
|
||||||
|
bool bypassRemovableObject(
|
||||||
|
const PathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
const PathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -762,24 +762,29 @@ void VCAI::loadGame(BinaryDeserializer & h, const int version)
|
|||||||
serializeInternal(h, version);
|
serializeInternal(h, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
void makePossibleUpgrades(const CArmedInstance * obj)
|
bool VCAI::makePossibleUpgrades(const CArmedInstance * obj)
|
||||||
{
|
{
|
||||||
if(!obj)
|
if(!obj)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
|
bool upgraded = false;
|
||||||
|
|
||||||
for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
|
for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
|
||||||
{
|
{
|
||||||
if(const CStackInstance * s = obj->getStackPtr(SlotID(i)))
|
if(const CStackInstance * s = obj->getStackPtr(SlotID(i)))
|
||||||
{
|
{
|
||||||
UpgradeInfo ui;
|
UpgradeInfo ui;
|
||||||
cb->getUpgradeInfo(obj, SlotID(i), ui);
|
myCb->getUpgradeInfo(obj, SlotID(i), ui);
|
||||||
if(ui.oldID >= 0 && cb->getResourceAmount().canAfford(ui.cost[0] * s->count))
|
if(ui.oldID >= 0 && myCb->getResourceAmount().canAfford(ui.cost[0] * s->count))
|
||||||
{
|
{
|
||||||
cb->upgradeCreature(obj, SlotID(i), ui.newID[0]);
|
myCb->upgradeCreature(obj, SlotID(i), ui.newID[0]);
|
||||||
|
upgraded = true;
|
||||||
logAi->debug("Upgraded %d %s to %s", s->count, ui.oldID.toCreature()->namePl, ui.newID[0].toCreature()->namePl);
|
logAi->debug("Upgraded %d %s to %s", s->count, ui.oldID.toCreature()->namePl, ui.newID[0].toCreature()->namePl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return upgraded;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCAI::makeTurn()
|
void VCAI::makeTurn()
|
||||||
@ -2203,12 +2208,15 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
|
|||||||
ui64 valueBought = 0;
|
ui64 valueBought = 0;
|
||||||
//buy the stacks with largest AI value
|
//buy the stacks with largest AI value
|
||||||
|
|
||||||
makePossibleUpgrades(t);
|
auto upgradeSuccessfull = makePossibleUpgrades(t);
|
||||||
|
|
||||||
auto armyToBuy = ah->getArmyAvailableToBuy(t->getUpperArmy(), t);
|
auto armyToBuy = ah->getArmyAvailableToBuy(t->getUpperArmy(), t);
|
||||||
|
|
||||||
if(armyToBuy.empty())
|
if(armyToBuy.empty())
|
||||||
{
|
{
|
||||||
|
if(upgradeSuccessfull)
|
||||||
|
throw goalFulfilledException(sptr(g));
|
||||||
|
|
||||||
throw cannotFulfillGoalException("No creatures to buy.");
|
throw cannotFulfillGoalException("No creatures to buy.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +220,7 @@ public:
|
|||||||
void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr);
|
void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr);
|
||||||
void moveCreaturesToHero(const CGTownInstance * t);
|
void moveCreaturesToHero(const CGTownInstance * t);
|
||||||
void performObjectInteraction(const CGObjectInstance * obj, HeroPtr h);
|
void performObjectInteraction(const CGObjectInstance * obj, HeroPtr h);
|
||||||
|
bool makePossibleUpgrades(const CArmedInstance * obj);
|
||||||
|
|
||||||
bool moveHeroToTile(int3 dst, HeroPtr h);
|
bool moveHeroToTile(int3 dst, HeroPtr h);
|
||||||
void buildStructure(const CGTownInstance * t, BuildingID building); //TODO: move to BuildingManager
|
void buildStructure(const CGTownInstance * t, BuildingID building); //TODO: move to BuildingManager
|
||||||
@ -401,5 +402,3 @@ public:
|
|||||||
return msg.c_str();
|
return msg.c_str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void makePossibleUpgrades(const CArmedInstance * obj);
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user