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;
|
||||
});
|
||||
|
||||
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()
|
||||
@ -254,11 +260,12 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
|
||||
return info;
|
||||
}
|
||||
|
||||
TResources BuildAnalyzer::getDailyIncome() const
|
||||
void BuildAnalyzer::updateDailyIncome()
|
||||
{
|
||||
auto objects = cb->getMyObjects();
|
||||
auto towns = cb->getTownsInfo();
|
||||
TResources dailyIncome = TResources();
|
||||
|
||||
dailyIncome = TResources();
|
||||
|
||||
for(const CGObjectInstance* obj : objects)
|
||||
{
|
||||
@ -274,8 +281,6 @@ TResources BuildAnalyzer::getDailyIncome() const
|
||||
{
|
||||
dailyIncome += town->dailyIncome();
|
||||
}
|
||||
|
||||
return dailyIncome;
|
||||
}
|
||||
|
||||
void TownDevelopmentInfo::addExistingDwelling(const BuildingInfo & existingDwelling)
|
||||
|
@ -70,6 +70,8 @@ private:
|
||||
TResources totalDevelopmentCost;
|
||||
std::vector<TownDevelopmentInfo> developmentInfos;
|
||||
TResources armyCost;
|
||||
TResources dailyIncome;
|
||||
float goldPreasure;
|
||||
|
||||
public:
|
||||
void update();
|
||||
@ -77,8 +79,8 @@ public:
|
||||
TResources getResourcesRequiredNow() const;
|
||||
TResources getTotalResourcesRequired() const;
|
||||
const std::vector<TownDevelopmentInfo> & getDevelopmentInfo() const { return developmentInfos; }
|
||||
|
||||
TResources getDailyIncome() const;
|
||||
TResources getDailyIncome() const { return dailyIncome; }
|
||||
float getGoldPreasure() const { return goldPreasure; }
|
||||
|
||||
private:
|
||||
BuildingInfo getBuildingOrPrerequisite(
|
||||
@ -89,5 +91,6 @@ private:
|
||||
|
||||
void updateTownDwellings(TownDevelopmentInfo & developmentInfo);
|
||||
void updateOtherBuildings(TownDevelopmentInfo & developmentInfo);
|
||||
void updateDailyIncome();
|
||||
void reset();
|
||||
};
|
||||
|
@ -275,6 +275,35 @@ std::vector<UpgradeInfo> ArmyManager::getDwellingUpgrades(const CCreatureSet * a
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -304,6 +333,9 @@ ArmyUpgradeInfo ArmyManager::calculateCreateresUpgrade(
|
||||
const CGObjectInstance * upgrader,
|
||||
const TResources & availableResources) const
|
||||
{
|
||||
if(!upgrader)
|
||||
return ArmyUpgradeInfo();
|
||||
|
||||
std::vector<UpgradeInfo> upgrades = getPossibleUpgrades(army, upgrader);
|
||||
|
||||
vstd::erase_if(upgrades, [&](const UpgradeInfo & u) -> bool
|
||||
|
@ -49,6 +49,7 @@ Goals::TGoalVec BuildingBehavior::getTasks()
|
||||
totalDevelopmentCost.toString());
|
||||
|
||||
auto & developmentInfos = ai->nullkiller->buildAnalyzer->getDevelopmentInfo();
|
||||
auto goldPreasure = ai->nullkiller->buildAnalyzer->getGoldPreasure();
|
||||
|
||||
for(auto & developmentInfo : developmentInfos)
|
||||
{
|
||||
@ -56,7 +57,8 @@ Goals::TGoalVec BuildingBehavior::getTasks()
|
||||
|
||||
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 "../Goals/BuyArmy.h"
|
||||
#include "../Goals/VisitTile.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "lib/mapping/CMap.h" //for victory conditions
|
||||
#include "lib/CPathfinder.h"
|
||||
|
||||
@ -34,6 +35,9 @@ Goals::TGoalVec BuyArmyBehavior::getTasks()
|
||||
|
||||
if(cb->getDate(Date::DAY) == 1)
|
||||
return tasks;
|
||||
|
||||
if(ai->nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE)
|
||||
return tasks;
|
||||
|
||||
auto heroes = cb->getHeroesInfo();
|
||||
|
||||
|
@ -38,11 +38,11 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
||||
return;
|
||||
}
|
||||
|
||||
logAi->trace("Scanning objects, count %d", objs.size());
|
||||
logAi->debug("Scanning objects, count %d", objs.size());
|
||||
|
||||
for(auto objToVisit : objs)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
#ifdef AI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Checking object %s, %s", objToVisit->getObjectName(), objToVisit->visitablePos().toString());
|
||||
#endif
|
||||
|
||||
@ -55,19 +55,19 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
||||
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
||||
std::shared_ptr<ExecuteHeroChain> closestWay;
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
#ifdef AI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Found %d paths", paths.size());
|
||||
#endif
|
||||
|
||||
for(auto & path : paths)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
#ifdef AI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Path found %s", path.toString());
|
||||
#endif
|
||||
|
||||
if(path.getFirstBlockedAction())
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
#ifdef AI_TRACE_LEVEL >= 2
|
||||
// TODO: decomposition?
|
||||
logAi->trace("Ignore path. Action is blocked.");
|
||||
#endif
|
||||
@ -76,8 +76,8 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
||||
|
||||
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %d", path.heroArmy->getArmyStrength());
|
||||
#ifdef AI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
@ -93,11 +93,11 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
||||
|
||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
#ifdef AI_TRACE_LEVEL >= 2
|
||||
logAi->trace(
|
||||
"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",
|
||||
isSafe ? "safe" : "not safe",
|
||||
objToVisit->instanceName,
|
||||
objToVisit->getObjectName(),
|
||||
hero->name,
|
||||
path.getHeroStrength(),
|
||||
danger,
|
||||
@ -123,8 +123,11 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
||||
if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
|
||||
continue;
|
||||
|
||||
if(ai->nullkiller->getHeroLockedReason(way->hero.get()) == HeroLockedReason::STARTUP)
|
||||
continue;
|
||||
|
||||
way->evaluationContext.closestWayRatio
|
||||
= way->evaluationContext.movementCost / closestWay->evaluationContext.movementCost;
|
||||
= closestWay->evaluationContext.movementCost / way->evaluationContext.movementCost;
|
||||
|
||||
tasks.push_back(sptr(*way));
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ Goals::TGoalVec StartupBehavior::getTasks()
|
||||
&& !town->visitingHero
|
||||
&& 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);
|
||||
|
||||
return lockedReason == HeroLockedReason::DEFENCE || lockedReason == HeroLockedReason::STARTUP;
|
||||
return lockedReason == HeroLockedReason::DEFENCE;
|
||||
});
|
||||
|
||||
ai->ah->updatePaths(activeHeroes, true);
|
||||
@ -134,6 +134,13 @@ void Nullkiller::makeTurn()
|
||||
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);
|
||||
|
||||
try
|
||||
|
@ -15,6 +15,9 @@
|
||||
#include "../Goals/AbstractGoal.h"
|
||||
#include "../Behaviors/Behavior.h"
|
||||
|
||||
const float MAX_GOLD_PEASURE = 0.3f;
|
||||
const float MIN_PRIORITY = 0.01f;
|
||||
|
||||
enum class HeroLockedReason
|
||||
{
|
||||
NOT_LOCKED = 0,
|
||||
|
@ -49,6 +49,7 @@ void PriorityEvaluator::initVisitTile()
|
||||
armyLossPersentageVariable = engine->getInputVariable("armyLoss");
|
||||
heroRoleVariable = engine->getInputVariable("heroRole");
|
||||
dangerVariable = engine->getInputVariable("danger");
|
||||
turnVariable = engine->getInputVariable("turn");
|
||||
mainTurnDistanceVariable = engine->getInputVariable("mainTurnDistance");
|
||||
scoutTurnDistanceVariable = engine->getInputVariable("scoutTurnDistance");
|
||||
goldRewardVariable = engine->getInputVariable("goldReward");
|
||||
@ -57,6 +58,8 @@ void PriorityEvaluator::initVisitTile()
|
||||
rewardTypeVariable = engine->getInputVariable("rewardType");
|
||||
closestHeroRatioVariable = engine->getInputVariable("closestHeroRatio");
|
||||
strategicalValueVariable = engine->getInputVariable("strategicalValue");
|
||||
goldPreasureVariable = engine->getInputVariable("goldPreasure");
|
||||
goldCostVariable = engine->getInputVariable("goldCost");
|
||||
value = engine->getOutputVariable("Value");
|
||||
}
|
||||
|
||||
@ -119,6 +122,25 @@ uint64_t getDwellingScore(const CGObjectInstance * target, bool checkGold)
|
||||
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)
|
||||
{
|
||||
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 getEnemyHeroStrategicalValue(const CGHeroInstance * enemy)
|
||||
@ -216,9 +262,11 @@ float getResourceRequirementStrength(int resType)
|
||||
return 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)
|
||||
@ -229,10 +277,11 @@ float getTotalResourceRequirementStrength(int resType)
|
||||
if(requiredResources[resType] == 0)
|
||||
return 0;
|
||||
|
||||
if(dailyIncome[resType] == 0)
|
||||
return requiredResources[resType] / 30;
|
||||
float ratio = dailyIncome[resType] == 0
|
||||
? 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)
|
||||
@ -243,18 +292,21 @@ float getStrategicalValue(const CGObjectInstance * target)
|
||||
switch(target->ID)
|
||||
{
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
return cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
|
||||
? getEnemyHeroStrategicalValue(dynamic_cast<const CGHeroInstance *>(target))
|
||||
: 0;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -388,13 +440,16 @@ public:
|
||||
auto day = cb->getDate(Date::DAY);
|
||||
auto hero = heroPtr.get();
|
||||
bool checkGold = evaluationContext.danger == 0;
|
||||
auto army = chain.getPath().heroArmy;
|
||||
|
||||
evaluationContext.armyLossPersentage = task->evaluationContext.armyLoss / (double)task->evaluationContext.heroStrength;
|
||||
evaluationContext.heroRole = ai->ah->getHeroRole(heroPtr);
|
||||
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.strategicalValue = getStrategicalValue(target);
|
||||
evaluationContext.goldCost = getGoldCost(target, hero, army);
|
||||
evaluationContext.turn = chain.getPath().turn();
|
||||
|
||||
return evaluationContext;
|
||||
}
|
||||
@ -409,15 +464,16 @@ public:
|
||||
Goals::BuildThis & buildThis = dynamic_cast<Goals::BuildThis &>(*task);
|
||||
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.movementCostByRole[evaluationContext.heroRole] = bi.prerequisitesCount;
|
||||
evaluationContext.armyReward = 0;
|
||||
evaluationContext.strategicalValue = buildThis.townInfo.armyScore / 50000.0;
|
||||
evaluationContext.goldCost = bi.buildCostWithPrerequisits[Res::GOLD];
|
||||
|
||||
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)
|
||||
{
|
||||
@ -429,7 +485,11 @@ public:
|
||||
|
||||
evaluationContext.armyReward = upgradedPower - creaturesToUpgrade.power;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
evaluationContext.strategicalValue = ai->nullkiller->buildAnalyzer->getGoldPreasure() * evaluationContext.goldReward / 300.0f;
|
||||
}
|
||||
|
||||
return evaluationContext;
|
||||
}
|
||||
};
|
||||
@ -485,6 +545,9 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
||||
rewardTypeVariable->setValue(rewardType);
|
||||
closestHeroRatioVariable->setValue(evaluationContext.closestWayRatio);
|
||||
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(VISIT_TILE); //TODO: Process only Visit_Tile
|
||||
@ -497,16 +560,18 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
||||
assert(result >= 0);
|
||||
|
||||
#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(),
|
||||
evaluationContext.armyLossPersentage,
|
||||
evaluationContext.movementCostByRole[HeroRole::MAIN],
|
||||
evaluationContext.movementCostByRole[HeroRole::SCOUT],
|
||||
evaluationContext.goldReward,
|
||||
evaluationContext.goldCost,
|
||||
evaluationContext.armyReward,
|
||||
evaluationContext.danger,
|
||||
evaluationContext.heroRole ? "scout" : "main",
|
||||
evaluationContext.strategicalValue,
|
||||
evaluationContext.closestWayRatio,
|
||||
result);
|
||||
#endif
|
||||
|
||||
|
@ -32,6 +32,7 @@ private:
|
||||
fl::InputVariable * heroRoleVariable;
|
||||
fl::InputVariable * mainTurnDistanceVariable;
|
||||
fl::InputVariable * scoutTurnDistanceVariable;
|
||||
fl::InputVariable * turnVariable;
|
||||
fl::InputVariable * goldRewardVariable;
|
||||
fl::InputVariable * armyRewardVariable;
|
||||
fl::InputVariable * dangerVariable;
|
||||
@ -39,6 +40,8 @@ private:
|
||||
fl::InputVariable * strategicalValueVariable;
|
||||
fl::InputVariable * rewardTypeVariable;
|
||||
fl::InputVariable * closestHeroRatioVariable;
|
||||
fl::InputVariable * goldPreasureVariable;
|
||||
fl::InputVariable * goldCostVariable;
|
||||
fl::OutputVariable * value;
|
||||
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());
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
for(auto cre : guards)
|
||||
if(checkGuards)
|
||||
{
|
||||
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
|
||||
|
@ -44,6 +44,6 @@ public:
|
||||
//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec);
|
||||
|
||||
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);
|
||||
};
|
||||
|
@ -104,9 +104,11 @@ namespace Goals
|
||||
float armyLossPersentage;
|
||||
float armyReward;
|
||||
int32_t goldReward;
|
||||
int32_t goldCost;
|
||||
float skillReward;
|
||||
float strategicalValue;
|
||||
HeroRole heroRole;
|
||||
uint8_t turn;
|
||||
|
||||
EvaluationContext()
|
||||
: movementCost(0.0),
|
||||
@ -118,9 +120,11 @@ namespace Goals
|
||||
movementCostByRole(),
|
||||
skillReward(0),
|
||||
goldReward(0),
|
||||
goldCost(0),
|
||||
armyReward(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)
|
||||
cb->swapGarrisonHero(town);
|
||||
|
||||
makePossibleUpgrades(town);
|
||||
ai->makePossibleUpgrades(town);
|
||||
ai->moveHeroToTile(town->visitablePos(), garrisonHero);
|
||||
|
||||
auto upperArmy = town->getUpperArmy();
|
||||
@ -98,7 +98,7 @@ void ExchangeSwapTownHeroes::accept(VCAI * ai)
|
||||
if(town->visitingHero && town->visitingHero != garrisonHero)
|
||||
{
|
||||
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);
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include "AINodeStorage.h"
|
||||
#include "Actions/TownPortalAction.h"
|
||||
#include "../Goals/Goals.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../../../CCallback.h"
|
||||
#include "../../../lib/mapping/CMap.h"
|
||||
#include "../../../lib/mapObjects/MapObjects.h"
|
||||
@ -278,6 +280,7 @@ bool AINodeStorage::calculateHeroChainFinal()
|
||||
for(AIPathNode & node : chains)
|
||||
{
|
||||
if(node.turns > heroChainTurn
|
||||
&& !node.locked
|
||||
&& node.action != CGPathNode::ENodeAction::UNKNOWN
|
||||
&& node.actor->actorExchangeCount > 1
|
||||
&& !hasBetterChain(&node, &node, chains))
|
||||
@ -450,7 +453,7 @@ void AINodeStorage::calculateHeroChain(
|
||||
if(carrier->armyLoss < carrier->actor->armyValue
|
||||
&& (carrier->action != CGPathNode::BATTLE || (carrier->actor->allowBattle && carrier->specialAction))
|
||||
&& carrier->action != CGPathNode::BLOCKING_VISIT
|
||||
&& other->armyLoss < other->actor->armyValue
|
||||
&& (other->armyLoss == 0 || other->armyLoss < other->actor->armyValue)
|
||||
&& carrier->actor->canExchange(other->actor))
|
||||
{
|
||||
#if AI_TRACE_LEVEL >= 2
|
||||
@ -632,18 +635,24 @@ void AINodeStorage::setTownsAndDwellings(
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/*auto dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
|
||||
auto waitForGrowth = dayOfWeek > 4;
|
||||
auto waitForGrowth = dayOfWeek > 4;*/
|
||||
|
||||
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)
|
||||
{
|
||||
@ -665,8 +674,8 @@ void AINodeStorage::setTownsAndDwellings(
|
||||
actors.push_back(dwellingActor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
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.heroArmy = node.actor->creatureSet;
|
||||
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.chainMask = node.actor->chainMask;
|
||||
path.exchangeCount = node.actor->actorExchangeCount;
|
||||
|
@ -159,13 +159,14 @@ public:
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool isDistanceLimitReached(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
||||
{
|
||||
return heroChainPass == EHeroChainPass::CHAIN && destination.node->turns > heroChainTurn;
|
||||
}
|
||||
|
||||
template<class NodeRange>
|
||||
bool hasBetterChain(
|
||||
const CGPathNode * source,
|
||||
@ -185,9 +186,9 @@ public:
|
||||
bool calculateHeroChain();
|
||||
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
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "../../../lib/mapping/CMap.h"
|
||||
#include "../../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
CCreatureSet emptyArmy;
|
||||
|
||||
bool HeroExchangeArmy::needsLastStack() const
|
||||
{
|
||||
return true;
|
||||
@ -56,6 +58,21 @@ std::string ChainActor::toString() const
|
||||
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)
|
||||
:ChainActor(hero, chainMask)
|
||||
{
|
||||
@ -151,18 +168,38 @@ bool HeroExchangeMap::canExchange(const ChainActor * other)
|
||||
|
||||
if(result)
|
||||
{
|
||||
if(other->armyCost.nonZero())
|
||||
{
|
||||
TResources resources = ai->myCb->getResourceAmount();
|
||||
TResources resources = ai->myCb->getResourceAmount();
|
||||
|
||||
if(!resources.canAfford(actor->armyCost + other->armyCost))
|
||||
{
|
||||
result = false;
|
||||
return;
|
||||
}
|
||||
if(!resources.canAfford(actor->armyCost + other->armyCost))
|
||||
{
|
||||
result = false;
|
||||
#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;
|
||||
}
|
||||
@ -204,7 +241,27 @@ HeroActor * HeroExchangeMap::exchange(const ChainActor * other)
|
||||
result = exchangeMap.at(other);
|
||||
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);
|
||||
exchangeMap[other] = result;
|
||||
}
|
||||
@ -212,6 +269,25 @@ HeroActor * HeroExchangeMap::exchange(const ChainActor * other)
|
||||
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 * target = new HeroExchangeArmy();
|
||||
@ -227,8 +303,13 @@ CCreatureSet * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, co
|
||||
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)
|
||||
:ChainActor(
|
||||
:ObjectActor(
|
||||
dwelling,
|
||||
getDwellingCreatures(dwelling, waitForGrowth),
|
||||
chainMask,
|
||||
@ -288,7 +369,7 @@ CCreatureSet * DwellingActor::getDwellingCreatures(const CGDwelling * dwelling,
|
||||
}
|
||||
|
||||
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;
|
||||
ChainActor * exchange(const ChainActor * other) const { return exchange(this, other); }
|
||||
void setBaseActor(HeroActor * base);
|
||||
virtual const CGObjectInstance * getActorObject() const { return hero; }
|
||||
|
||||
protected:
|
||||
virtual ChainActor * exchange(const ChainActor * specialActor, const ChainActor * other) const;
|
||||
@ -86,6 +87,7 @@ public:
|
||||
|
||||
private:
|
||||
CCreatureSet * pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const;
|
||||
CCreatureSet * tryUpgrade(const CCreatureSet * army, const CGObjectInstance * upgrader, TResources resources) const;
|
||||
};
|
||||
|
||||
class HeroActor : public ChainActor
|
||||
@ -112,7 +114,24 @@ protected:
|
||||
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:
|
||||
const CGDwelling * dwelling;
|
||||
@ -127,7 +146,7 @@ protected:
|
||||
CCreatureSet * getDwellingCreatures(const CGDwelling * dwelling, bool waitForGrowth);
|
||||
};
|
||||
|
||||
class TownGarrisonActor : public ChainActor
|
||||
class TownGarrisonActor : public ObjectActor
|
||||
{
|
||||
private:
|
||||
const CGTownInstance * town;
|
||||
|
@ -47,151 +47,171 @@ namespace AIPathfinding
|
||||
{
|
||||
if(nodeStorage->isMovementIneficient(source, destination))
|
||||
{
|
||||
destination.node->locked = true;
|
||||
destination.blocked = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
|
||||
|
||||
if(blocker == BlockingReason::NONE)
|
||||
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)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
auto destGuardians = cb->getGuardingCreatures(destination.coord);
|
||||
return vstd::contains(srcGuardians, destGuard);
|
||||
});
|
||||
|
||||
if(destGuardians.empty())
|
||||
{
|
||||
destination.blocked = true;
|
||||
auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if(guardsAlreadyBypassed && srcNode->actor->allowBattle)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Bypass guard at destination while moving %s -> %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
logAi->trace(
|
||||
"Bypass guard at destination while moving %s -> %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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,
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
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);
|
||||
}
|
||||
|
||||
void makePossibleUpgrades(const CArmedInstance * obj)
|
||||
bool VCAI::makePossibleUpgrades(const CArmedInstance * obj)
|
||||
{
|
||||
if(!obj)
|
||||
return;
|
||||
return false;
|
||||
|
||||
bool upgraded = false;
|
||||
|
||||
for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
|
||||
{
|
||||
if(const CStackInstance * s = obj->getStackPtr(SlotID(i)))
|
||||
{
|
||||
UpgradeInfo ui;
|
||||
cb->getUpgradeInfo(obj, SlotID(i), ui);
|
||||
if(ui.oldID >= 0 && cb->getResourceAmount().canAfford(ui.cost[0] * s->count))
|
||||
myCb->getUpgradeInfo(obj, SlotID(i), ui);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return upgraded;
|
||||
}
|
||||
|
||||
void VCAI::makeTurn()
|
||||
@ -2203,12 +2208,15 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
|
||||
ui64 valueBought = 0;
|
||||
//buy the stacks with largest AI value
|
||||
|
||||
makePossibleUpgrades(t);
|
||||
auto upgradeSuccessfull = makePossibleUpgrades(t);
|
||||
|
||||
auto armyToBuy = ah->getArmyAvailableToBuy(t->getUpperArmy(), t);
|
||||
|
||||
if(armyToBuy.empty())
|
||||
{
|
||||
if(upgradeSuccessfull)
|
||||
throw goalFulfilledException(sptr(g));
|
||||
|
||||
throw cannotFulfillGoalException("No creatures to buy.");
|
||||
}
|
||||
|
||||
|
@ -220,6 +220,7 @@ public:
|
||||
void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr);
|
||||
void moveCreaturesToHero(const CGTownInstance * t);
|
||||
void performObjectInteraction(const CGObjectInstance * obj, HeroPtr h);
|
||||
bool makePossibleUpgrades(const CArmedInstance * obj);
|
||||
|
||||
bool moveHeroToTile(int3 dst, HeroPtr h);
|
||||
void buildStructure(const CGTownInstance * t, BuildingID building); //TODO: move to BuildingManager
|
||||
@ -401,5 +402,3 @@ public:
|
||||
return msg.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
void makePossibleUpgrades(const CArmedInstance * obj);
|
||||
|
Loading…
x
Reference in New Issue
Block a user