mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
nkai: fixes and skill rewards
This commit is contained in:
parent
202e13ce2e
commit
c93bb0a502
@ -1058,6 +1058,11 @@ void AIGateway::recruitCreatures(const CGDwelling * d, const CArmedInstance * re
|
||||
int count = d->creatures[i].first;
|
||||
CreatureID creID = d->creatures[i].second.back();
|
||||
|
||||
if(!recruiter->getSlotFor(creID).validSlot())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
vstd::amin(count, cb->getResourceAmount() / creID.toCreature()->getFullRecruitCost());
|
||||
if(count > 0)
|
||||
cb->recruitCreatures(d, recruiter, creID, count, i);
|
||||
|
@ -68,19 +68,22 @@ void BuildAnalyzer::updateOtherBuildings(TownDevelopmentInfo & developmentInfo)
|
||||
logAi->trace("Checking other buildings");
|
||||
|
||||
std::vector<std::vector<BuildingID>> otherBuildings = {
|
||||
{BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL}
|
||||
{BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL},
|
||||
{BuildingID::MAGES_GUILD_3, BuildingID::MAGES_GUILD_5}
|
||||
};
|
||||
|
||||
if(developmentInfo.existingDwellings.size() >= 2 && ai->cb->getDate(Date::DAY_OF_WEEK) > boost::date_time::Friday)
|
||||
{
|
||||
otherBuildings.push_back({BuildingID::CITADEL, BuildingID::CASTLE});
|
||||
otherBuildings.push_back({BuildingID::HORDE_1});
|
||||
otherBuildings.push_back({BuildingID::HORDE_2});
|
||||
}
|
||||
|
||||
for(auto & buildingSet : otherBuildings)
|
||||
{
|
||||
for(auto & buildingID : buildingSet)
|
||||
{
|
||||
if(!developmentInfo.town->hasBuilt(buildingID))
|
||||
if(!developmentInfo.town->hasBuilt(buildingID) && developmentInfo.town->town->buildings.count(buildingID))
|
||||
{
|
||||
developmentInfo.addBuildingToBuild(getBuildingOrPrerequisite(developmentInfo.town, buildingID));
|
||||
|
||||
@ -190,12 +193,28 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
|
||||
const CCreature * creature = nullptr;
|
||||
CreatureID baseCreatureID;
|
||||
|
||||
int creatureLevel = -1;
|
||||
int creatureUpgrade = 0;
|
||||
|
||||
if(BuildingID::DWELL_FIRST <= toBuild && toBuild <= BuildingID::DWELL_UP_LAST)
|
||||
{
|
||||
int level = toBuild - BuildingID::DWELL_FIRST;
|
||||
auto creatures = townInfo->creatures.at(level % GameConstants::CREATURES_PER_TOWN);
|
||||
auto creatureID = creatures.size() > level / GameConstants::CREATURES_PER_TOWN
|
||||
? creatures.at(level / GameConstants::CREATURES_PER_TOWN)
|
||||
creatureLevel = (toBuild - BuildingID::DWELL_FIRST) % GameConstants::CREATURES_PER_TOWN;
|
||||
creatureUpgrade = (toBuild - BuildingID::DWELL_FIRST) / GameConstants::CREATURES_PER_TOWN;
|
||||
}
|
||||
else if(toBuild == BuildingID::HORDE_1 || toBuild == BuildingID::HORDE_1_UPGR)
|
||||
{
|
||||
creatureLevel = townInfo->hordeLvl.at(0);
|
||||
}
|
||||
else if(toBuild == BuildingID::HORDE_2 || toBuild == BuildingID::HORDE_2_UPGR)
|
||||
{
|
||||
creatureLevel = townInfo->hordeLvl.at(1);
|
||||
}
|
||||
|
||||
if(creatureLevel >= 0)
|
||||
{
|
||||
auto creatures = townInfo->creatures.at(creatureLevel);
|
||||
auto creatureID = creatures.size() > creatureUpgrade
|
||||
? creatures.at(creatureUpgrade)
|
||||
: creatures.front();
|
||||
|
||||
baseCreatureID = creatures.front();
|
||||
@ -366,12 +385,19 @@ BuildingInfo::BuildingInfo(
|
||||
}
|
||||
else
|
||||
{
|
||||
creatureGrows = creature->getGrowth();
|
||||
if(BuildingID::DWELL_FIRST <= id && id <= BuildingID::DWELL_UP_LAST)
|
||||
{
|
||||
creatureGrows = creature->getGrowth();
|
||||
|
||||
if(town->hasBuilt(BuildingID::CASTLE))
|
||||
creatureGrows *= 2;
|
||||
else if(town->hasBuilt(BuildingID::CITADEL))
|
||||
creatureGrows += creatureGrows / 2;
|
||||
if(town->hasBuilt(BuildingID::CASTLE))
|
||||
creatureGrows *= 2;
|
||||
else if(town->hasBuilt(BuildingID::CITADEL))
|
||||
creatureGrows += creatureGrows / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
creatureGrows = creature->getHorde();
|
||||
}
|
||||
}
|
||||
|
||||
armyStrength = ai->armyManager->evaluateStackPower(creature, creatureGrows);
|
||||
|
@ -213,7 +213,7 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
||||
{
|
||||
captureObjects(ai->nullkiller->objectClusterizer->getNearbyObjects());
|
||||
|
||||
if(tasks.empty() || ai->nullkiller->getScanDepth() == ScanDepth::FULL)
|
||||
if(tasks.empty() || ai->nullkiller->getScanDepth() != ScanDepth::SMALL)
|
||||
captureObjects(ai->nullkiller->objectClusterizer->getFarObjects());
|
||||
}
|
||||
|
||||
|
@ -49,41 +49,98 @@ Goals::TGoalVec DefenceBehavior::decompose() const
|
||||
return tasks;
|
||||
}
|
||||
|
||||
bool isTreatUnderControl(const CGTownInstance * town, const HitMapInfo & treat, const std::vector<AIPath> & paths)
|
||||
{
|
||||
int dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
|
||||
|
||||
for(const AIPath & path : paths)
|
||||
{
|
||||
bool treatIsWeak = path.getHeroStrength() / (float)treat.danger > TREAT_IGNORE_RATIO;
|
||||
bool needToSaveGrowth = treat.turn == 0 && dayOfWeek == 7;
|
||||
|
||||
if(treatIsWeak && !needToSaveGrowth)
|
||||
{
|
||||
if((path.exchangeCount == 1 && path.turn() < treat.turn)
|
||||
|| path.turn() < treat.turn - 1
|
||||
|| (path.turn() < treat.turn && treat.turn >= 2))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 1
|
||||
logAi->trace(
|
||||
"Hero %s can eliminate danger for town %s using path %s.",
|
||||
path.targetHero->getObjectName(),
|
||||
town->getObjectName(),
|
||||
path.toString());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void handleCounterAttack(
|
||||
const CGTownInstance * town,
|
||||
const HitMapInfo & treat,
|
||||
const HitMapInfo & maximumDanger,
|
||||
Goals::TGoalVec & tasks)
|
||||
{
|
||||
if(treat.hero.validAndSet()
|
||||
&& treat.turn <= 1
|
||||
&& (treat.danger == maximumDanger.danger || treat.turn < maximumDanger.turn))
|
||||
{
|
||||
auto heroCapturingPaths = ai->nullkiller->pathfinder->getPathInfo(treat.hero->visitablePos());
|
||||
auto goals = CaptureObjectsBehavior::getVisitGoals(heroCapturingPaths, treat.hero.get());
|
||||
|
||||
for(int i = 0; i < heroCapturingPaths.size(); i++)
|
||||
{
|
||||
AIPath & path = heroCapturingPaths[i];
|
||||
TSubgoal goal = goals[i];
|
||||
|
||||
if(!goal || goal->invalid() || !goal->isElementar()) continue;
|
||||
|
||||
Composition composition;
|
||||
|
||||
composition.addNext(DefendTown(town, treat, path, true)).addNext(goal);
|
||||
|
||||
tasks.push_back(Goals::sptr(composition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoalVec & tasks)
|
||||
{
|
||||
if(ai->nullkiller->isHeroLocked(town->garrisonHero.get()))
|
||||
{
|
||||
logAi->trace(
|
||||
"Hero %s in garrison of town %s is suposed to defend the town",
|
||||
town->garrisonHero->getNameTranslated(),
|
||||
town->getNameTranslated());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!town->visitingHero && cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
|
||||
{
|
||||
logAi->trace(
|
||||
"Extracting hero %s from garrison of town %s",
|
||||
town->garrisonHero->getNameTranslated(),
|
||||
town->getNameTranslated());
|
||||
|
||||
tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, nullptr).setpriority(5)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const
|
||||
{
|
||||
logAi->trace("Evaluating defence for %s", town->getNameTranslated());
|
||||
|
||||
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
|
||||
std::vector<HitMapInfo> treats = ai->nullkiller->dangerHitMap->getTownTreats(town);
|
||||
|
||||
treats.push_back(treatNode.fastestDanger); // no guarantee that fastest danger will be there
|
||||
|
||||
int dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
|
||||
|
||||
if(town->garrisonHero)
|
||||
{
|
||||
if(ai->nullkiller->isHeroLocked(town->garrisonHero.get()))
|
||||
{
|
||||
logAi->trace(
|
||||
"Hero %s in garrison of town %s is suposed to defend the town",
|
||||
town->garrisonHero->getNameTranslated(),
|
||||
town->getNameTranslated());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!town->visitingHero && cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
|
||||
{
|
||||
logAi->trace(
|
||||
"Extracting hero %s from garrison of town %s",
|
||||
town->garrisonHero->getNameTranslated(),
|
||||
town->getNameTranslated());
|
||||
|
||||
tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, nullptr).setpriority(5)));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!treatNode.fastestDanger.hero)
|
||||
{
|
||||
@ -91,6 +148,15 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<HitMapInfo> treats = ai->nullkiller->dangerHitMap->getTownTreats(town);
|
||||
|
||||
treats.push_back(treatNode.fastestDanger); // no guarantee that fastest danger will be there
|
||||
|
||||
if(town->garrisonHero && handleGarrisonHeroFromPreviousTurn(town, tasks))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
|
||||
|
||||
@ -111,74 +177,12 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
std::to_string(treat.turn),
|
||||
treat.hero->getNameTranslated());
|
||||
|
||||
bool treatIsUnderControl = false;
|
||||
handleCounterAttack(town, treat, treatNode.maximumDanger, tasks);
|
||||
|
||||
for(AIPath & path : paths)
|
||||
if(isTreatUnderControl(town, treat, paths))
|
||||
{
|
||||
if(town->visitingHero && path.targetHero == town->visitingHero.get())
|
||||
{
|
||||
if(path.getHeroStrength() < town->visitingHero->getHeroStrength())
|
||||
continue;
|
||||
}
|
||||
else if(town->garrisonHero && path.targetHero == town->garrisonHero.get())
|
||||
{
|
||||
if(path.getHeroStrength() < town->visitingHero->getHeroStrength())
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(town->visitingHero)
|
||||
continue;
|
||||
}
|
||||
|
||||
if(treat.hero.validAndSet()
|
||||
&& treat.turn <= 1
|
||||
&& (treat.danger == treatNode.maximumDanger.danger || treat.turn < treatNode.maximumDanger.turn))
|
||||
{
|
||||
auto heroCapturingPaths = ai->nullkiller->pathfinder->getPathInfo(treat.hero->visitablePos());
|
||||
auto goals = CaptureObjectsBehavior::getVisitGoals(heroCapturingPaths, treat.hero.get());
|
||||
|
||||
for(int i = 0; i < heroCapturingPaths.size(); i++)
|
||||
{
|
||||
AIPath & path = heroCapturingPaths[i];
|
||||
TSubgoal goal = goals[i];
|
||||
|
||||
if(!goal || goal->invalid() || !goal->isElementar()) continue;
|
||||
|
||||
Composition composition;
|
||||
|
||||
composition.addNext(DefendTown(town, treat, path, true)).addNext(goal);
|
||||
|
||||
tasks.push_back(Goals::sptr(composition));
|
||||
}
|
||||
}
|
||||
|
||||
bool treatIsWeak = path.getHeroStrength() / (float)treat.danger > TREAT_IGNORE_RATIO;
|
||||
bool needToSaveGrowth = treat.turn == 0 && dayOfWeek == 7;
|
||||
|
||||
if(treatIsWeak && !needToSaveGrowth)
|
||||
{
|
||||
if((path.exchangeCount == 1 && path.turn() < treat.turn)
|
||||
|| path.turn() < treat.turn - 1
|
||||
|| (path.turn() < treat.turn && treat.turn >= 2))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 1
|
||||
logAi->trace(
|
||||
"Hero %s can eliminate danger for town %s using path %s.",
|
||||
path.targetHero->getObjectName(),
|
||||
town->getObjectName(),
|
||||
path.toString());
|
||||
#endif
|
||||
|
||||
treatIsUnderControl = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(treatIsUnderControl)
|
||||
continue;
|
||||
}
|
||||
|
||||
evaluateRecruitingHero(tasks, treat, town);
|
||||
|
||||
@ -205,6 +209,27 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
path.movementCost(),
|
||||
path.toString());
|
||||
#endif
|
||||
|
||||
auto townDefenseStrength = town->garrisonHero
|
||||
? town->garrisonHero->getTotalStrength()
|
||||
: (town->visitingHero ? town->visitingHero->getTotalStrength() : town->getUpperArmy()->getArmyStrength());
|
||||
|
||||
if(town->visitingHero && path.targetHero == town->visitingHero.get())
|
||||
{
|
||||
if(path.getHeroStrength() < townDefenseStrength)
|
||||
continue;
|
||||
}
|
||||
else if(town->garrisonHero && path.targetHero == town->garrisonHero.get())
|
||||
{
|
||||
if(path.getHeroStrength() < townDefenseStrength)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(town->visitingHero)
|
||||
continue;
|
||||
}
|
||||
|
||||
if(path.turn() <= treat.turn - 2)
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 1
|
||||
@ -296,7 +321,20 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
composition.addNext(DefendTown(town, treat, path));
|
||||
TGoalVec sequence;
|
||||
|
||||
if(town->visitingHero && path.targetHero != town->visitingHero && !path.containsHero(town->visitingHero))
|
||||
if(town->garrisonHero && path.targetHero == town->garrisonHero.get() && path.exchangeCount == 1)
|
||||
{
|
||||
composition.addNext(ExchangeSwapTownHeroes(town, town->garrisonHero.get(), HeroLockedReason::DEFENCE));
|
||||
tasks.push_back(Goals::sptr(composition));
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Locking hero %s in garrison of %s",
|
||||
town->garrisonHero.get()->getObjectName(),
|
||||
town->getObjectName());
|
||||
#endif
|
||||
|
||||
continue;
|
||||
}
|
||||
else if(town->visitingHero && path.targetHero != town->visitingHero && !path.containsHero(town->visitingHero))
|
||||
{
|
||||
if(town->garrisonHero)
|
||||
{
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "../Markers/HeroExchange.h"
|
||||
#include "../Markers/ArmyUpgrade.h"
|
||||
#include "GatherArmyBehavior.h"
|
||||
#include "CaptureObjectsBehavior.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../Goals/ExchangeSwapTownHeroes.h"
|
||||
|
||||
@ -235,6 +236,8 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
#endif
|
||||
|
||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(pos);
|
||||
auto goals = CaptureObjectsBehavior::getVisitGoals(paths);
|
||||
|
||||
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 1
|
||||
@ -251,11 +254,23 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
hasMainAround = true;
|
||||
}
|
||||
|
||||
for(const AIPath & path : paths)
|
||||
for(int i = 0; i < paths.size(); i++)
|
||||
{
|
||||
auto & path = paths[i];
|
||||
auto visitGoal = goals[i];
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Path found %s, %s, %lld", path.toString(), path.targetHero->getObjectName(), path.heroArmy->getArmyStrength());
|
||||
#endif
|
||||
|
||||
if(visitGoal->invalid())
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Ignore path. Not valid way.");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
if(upgrader->visitingHero && (upgrader->visitingHero.get() != path.targetHero || path.exchangeCount == 1))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
@ -370,11 +385,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
|
||||
if(isSafe)
|
||||
{
|
||||
ExecuteHeroChain newWay(path, upgrader);
|
||||
|
||||
newWay.closestWayRatio = 1;
|
||||
|
||||
tasks.push_back(sptr(Composition().addNext(ArmyUpgrade(path, upgrader, upgrade)).addNext(newWay)));
|
||||
tasks.push_back(sptr(Composition().addNext(ArmyUpgrade(path, upgrader, upgrade)).addNext(visitGoal)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior, int decompositi
|
||||
void Nullkiller::resetAiState()
|
||||
{
|
||||
lockedResources = TResources();
|
||||
scanDepth = ScanDepth::FULL;
|
||||
scanDepth = ScanDepth::MAIN_FULL;
|
||||
playerID = ai->playerID;
|
||||
lockedHeroes.clear();
|
||||
dangerHitMap->reset();
|
||||
@ -158,11 +158,15 @@ void Nullkiller::updateAiState(int pass, bool fast)
|
||||
|
||||
PathfinderSettings cfg;
|
||||
cfg.useHeroChain = useHeroChain;
|
||||
cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT;
|
||||
|
||||
if(scanDepth != ScanDepth::FULL)
|
||||
if(scanDepth == ScanDepth::SMALL)
|
||||
{
|
||||
cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT * ((int)scanDepth + 1);
|
||||
cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT;
|
||||
}
|
||||
|
||||
if(scanDepth != ScanDepth::ALL_FULL)
|
||||
{
|
||||
cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT;
|
||||
}
|
||||
|
||||
boost::this_thread::interruption_point();
|
||||
@ -233,8 +237,8 @@ void Nullkiller::makeTurn()
|
||||
updateAiState(i);
|
||||
|
||||
Goals::TTask bestTask = taskptr(Goals::Invalid());
|
||||
|
||||
do
|
||||
|
||||
for(;i <= MAXPASS; i++)
|
||||
{
|
||||
Goals::TTaskVec fastTasks = {
|
||||
choseBestTask(sptr(BuyArmyBehavior()), 1),
|
||||
@ -248,7 +252,11 @@ void Nullkiller::makeTurn()
|
||||
executeTask(bestTask);
|
||||
updateAiState(i, true);
|
||||
}
|
||||
} while(bestTask->priority >= FAST_TASK_MINIMAL_PRIORITY);
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Goals::TTaskVec bestTasks = {
|
||||
bestTask,
|
||||
@ -267,7 +275,6 @@ void Nullkiller::makeTurn()
|
||||
bestTask = choseBestTask(bestTasks);
|
||||
|
||||
HeroPtr hero = bestTask->getHero();
|
||||
|
||||
HeroRole heroRole = HeroRole::MAIN;
|
||||
|
||||
if(hero.validAndSet())
|
||||
@ -276,20 +283,39 @@ void Nullkiller::makeTurn()
|
||||
if(heroRole != HeroRole::MAIN || bestTask->getHeroExchangeCount() <= 1)
|
||||
useHeroChain = false;
|
||||
|
||||
// TODO: better to check turn distance here instead of priority
|
||||
if((heroRole != HeroRole::MAIN || bestTask->priority < SMALL_SCAN_MIN_PRIORITY)
|
||||
&& scanDepth == ScanDepth::FULL)
|
||||
&& scanDepth == ScanDepth::MAIN_FULL)
|
||||
{
|
||||
useHeroChain = false;
|
||||
scanDepth = ScanDepth::SMALL;
|
||||
|
||||
logAi->trace(
|
||||
"Goal %s has too low priority %f so increasing scan depth",
|
||||
"Goal %s has low priority %f so decreasing scan depth to gain performance.",
|
||||
bestTask->toString(),
|
||||
bestTask->priority);
|
||||
}
|
||||
|
||||
if(bestTask->priority < MIN_PRIORITY)
|
||||
{
|
||||
auto heroes = cb->getHeroesInfo();
|
||||
auto hasMp = vstd::contains_if(heroes, [](const CGHeroInstance * h) -> bool
|
||||
{
|
||||
return h->movementPointsRemaining() > 100;
|
||||
});
|
||||
|
||||
if(hasMp && scanDepth != ScanDepth::ALL_FULL)
|
||||
{
|
||||
logAi->trace(
|
||||
"Goal %s has too low priority %f so increasing scan depth to full.",
|
||||
bestTask->toString(),
|
||||
bestTask->priority);
|
||||
|
||||
scanDepth = ScanDepth::ALL_FULL;
|
||||
useHeroChain = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
logAi->trace("Goal %s has too low priority. It is not worth doing it. Ending turn.", bestTask->toString());
|
||||
|
||||
return;
|
||||
|
@ -40,9 +40,11 @@ enum class HeroLockedReason
|
||||
|
||||
enum class ScanDepth
|
||||
{
|
||||
FULL = 0,
|
||||
MAIN_FULL = 0,
|
||||
|
||||
SMALL = 1
|
||||
SMALL = 1,
|
||||
|
||||
ALL_FULL = 2
|
||||
};
|
||||
|
||||
class Nullkiller
|
||||
|
@ -915,6 +915,7 @@ public:
|
||||
evaluationContext.heroRole = HeroRole::MAIN;
|
||||
evaluationContext.movementCostByRole[evaluationContext.heroRole] += bi.prerequisitesCount;
|
||||
evaluationContext.goldCost += bi.buildCostWithPrerequisits[EGameResID::GOLD];
|
||||
evaluationContext.closestWayRatio = 1;
|
||||
|
||||
if(bi.creatureID != CreatureID::NONE)
|
||||
{
|
||||
@ -938,7 +939,12 @@ public:
|
||||
evaluationContext.addNonCriticalStrategicalValue(buildThis.town->creatures.size() * 0.2f);
|
||||
evaluationContext.armyReward += buildThis.townInfo.armyStrength / 2;
|
||||
}
|
||||
else
|
||||
else if(bi.id >= BuildingID::MAGES_GUILD_1 && bi.id <= BuildingID::MAGES_GUILD_5)
|
||||
{
|
||||
evaluationContext.skillReward += 2 * (bi.id - BuildingID::MAGES_GUILD_1);
|
||||
}
|
||||
|
||||
if(evaluationContext.goldReward)
|
||||
{
|
||||
auto goldPreasure = evaluationContext.evaluator.ai->buildAnalyzer->getGoldPreasure();
|
||||
|
||||
|
@ -5,7 +5,7 @@ InputVariable: mainTurnDistance
|
||||
enabled: true
|
||||
range: 0.000 10.000
|
||||
lock-range: true
|
||||
term: LOWEST Ramp 0.250 0.000
|
||||
term: LOWEST Ramp 0.400 0.000
|
||||
term: LOW Discrete 0.000 1.000 0.500 0.800 0.800 0.300 2.000 0.000
|
||||
term: MEDIUM Discrete 0.000 0.000 0.500 0.200 0.800 0.700 2.000 1.000 6.000 0.000
|
||||
term: LONG Discrete 2.000 0.000 6.000 1.000 10.000 0.800
|
||||
@ -238,4 +238,22 @@ RuleBlock: gold
|
||||
rule: if goldReward is MEDIUM and goldPreasure is HIGH and heroRole is SCOUT and danger is not NONE then Value is SMALL
|
||||
rule: if goldReward is MEDIUM and goldPreasure is HIGH and heroRole is MAIN and danger is not NONE and armyLoss is LOW then Value is BITHIGH
|
||||
rule: if goldReward is SMALL and goldPreasure is HIGH and heroRole is SCOUT and danger is NONE then Value is MEDIUM
|
||||
rule: if goldReward is SMALL and goldPreasure is HIGH and heroRole is MAIN and danger is not NONE and armyLoss is LOW then Value is SMALL
|
||||
rule: if goldReward is SMALL and goldPreasure is HIGH and heroRole is MAIN and danger is not NONE and armyLoss is LOW then Value is SMALL
|
||||
RuleBlock: skill reward
|
||||
enabled: true
|
||||
conjunction: AlgebraicProduct
|
||||
disjunction: AlgebraicSum
|
||||
implication: AlgebraicProduct
|
||||
activation: General
|
||||
rule: if heroRole is MAIN and skillReward is LOW and mainTurnDistance is LOWEST and fear is not HIGH then Value is HIGH
|
||||
rule: if heroRole is MAIN and skillReward is MEDIUM and mainTurnDistance is LOWEST and fear is not HIGH then Value is HIGHEST
|
||||
rule: if heroRole is MAIN and skillReward is HIGH and mainTurnDistance is LOWEST and fear is not HIGH then Value is HIGHEST
|
||||
rule: if heroRole is MAIN and skillReward is LOW and mainTurnDistance is LOW and fear is not HIGH then Value is BITHIGH
|
||||
rule: if heroRole is MAIN and skillReward is MEDIUM and mainTurnDistance is LOW and fear is not HIGH then Value is HIGH
|
||||
rule: if heroRole is MAIN and skillReward is HIGH and mainTurnDistance is LOW and fear is not HIGH then Value is HIGHEST
|
||||
rule: if heroRole is MAIN and skillReward is LOW and mainTurnDistance is MEDIUM and fear is not HIGH then Value is MEDIUM
|
||||
rule: if heroRole is MAIN and skillReward is MEDIUM and mainTurnDistance is MEDIUM and fear is not HIGH then Value is BITHIGH
|
||||
rule: if heroRole is MAIN and skillReward is HIGH and mainTurnDistance is MEDIUM and fear is not HIGH then Value is HIGH
|
||||
rule: if heroRole is MAIN and skillReward is LOW and mainTurnDistance is LONG and fear is not HIGH then Value is SMALL
|
||||
rule: if heroRole is MAIN and skillReward is MEDIUM and mainTurnDistance is LONG and fear is not HIGH then Value is MEDIUM
|
||||
rule: if heroRole is MAIN and skillReward is HIGH and mainTurnDistance is LONG and fear is not HIGH then Value is BITHIGH
|
Loading…
Reference in New Issue
Block a user