mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-26 22:57:00 +02:00
commit
a7e2e8a362
@ -766,10 +766,10 @@ void AIGateway::makeTurn()
|
||||
logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
|
||||
return;
|
||||
}
|
||||
/*catch (std::exception & e)
|
||||
catch (std::exception & e)
|
||||
{
|
||||
logAi->debug("Making turn thread has caught an exception: %s", e.what());
|
||||
}*/
|
||||
}
|
||||
|
||||
endTurn();
|
||||
}
|
||||
|
@ -120,18 +120,23 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
|
||||
|
||||
auto blocker = blockers.front();
|
||||
|
||||
if(isObjectPassable(ai, blocker))
|
||||
continue;
|
||||
|
||||
if(blocker->ID == Obj::GARRISON
|
||||
|| blocker->ID == Obj::MONSTER
|
||||
|| blocker->ID == Obj::GARRISON2
|
||||
|| blocker->ID == Obj::BORDERGUARD
|
||||
|| blocker->ID == Obj::BORDER_GATE
|
||||
|| blocker->ID == Obj::SHIPYARD)
|
||||
|| blocker->ID == Obj::GARRISON2)
|
||||
{
|
||||
if(!isObjectPassable(ai, blocker))
|
||||
if(dynamic_cast<const CArmedInstance *>(blocker)->getArmyStrength() == 0)
|
||||
continue;
|
||||
else
|
||||
return blocker;
|
||||
}
|
||||
|
||||
if(blocker->ID == Obj::QUEST_GUARD && node->actionIsBlocked)
|
||||
if(blocker->ID == Obj::MONSTER
|
||||
|| blocker->ID == Obj::BORDERGUARD
|
||||
|| blocker->ID == Obj::BORDER_GATE
|
||||
|| blocker->ID == Obj::SHIPYARD
|
||||
|| (blocker->ID == Obj::QUEST_GUARD && node->actionIsBlocked))
|
||||
{
|
||||
return blocker;
|
||||
}
|
||||
|
@ -47,14 +47,14 @@ Goals::TGoalVec DefenceBehavior::decompose() const
|
||||
|
||||
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const
|
||||
{
|
||||
logAi->debug("Evaluating defence for %s", town->name);
|
||||
logAi->trace("Evaluating defence for %s", town->name);
|
||||
|
||||
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
|
||||
auto treats = { treatNode.fastestDanger, treatNode.maximumDanger };
|
||||
|
||||
if(!treatNode.fastestDanger.hero)
|
||||
{
|
||||
logAi->debug("No treat found for town %s", town->name);
|
||||
logAi->trace("No treat found for town %s", town->name);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -73,7 +73,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
return;
|
||||
}
|
||||
|
||||
logAi->debug(
|
||||
logAi->trace(
|
||||
"Hero %s in garrison of town %s is suposed to defend the town",
|
||||
town->garrisonHero->name,
|
||||
town->name);
|
||||
@ -85,7 +85,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
|
||||
if(reinforcement)
|
||||
{
|
||||
logAi->debug("Town %s can buy defence army %lld", town->name, reinforcement);
|
||||
logAi->trace("Town %s can buy defence army %lld", town->name, reinforcement);
|
||||
tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
|
||||
for(auto & treat : treats)
|
||||
{
|
||||
logAi->debug(
|
||||
logAi->trace(
|
||||
"Town %s has treat %lld in %s turns, hero: %s",
|
||||
town->name,
|
||||
treat.danger,
|
||||
@ -104,6 +104,12 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
|
||||
for(AIPath & path : paths)
|
||||
{
|
||||
if(town->visitingHero && path.targetHero != town->visitingHero.get())
|
||||
continue;
|
||||
|
||||
if(town->visitingHero && path.getHeroStrength() < town->visitingHero->getHeroStrength())
|
||||
continue;
|
||||
|
||||
if(path.getHeroStrength() > treat.danger)
|
||||
{
|
||||
if((path.turn() <= treat.turn && dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger))
|
||||
@ -111,11 +117,13 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
|| path.turn() < treat.turn - 1
|
||||
|| (path.turn() < treat.turn && treat.turn >= 2))
|
||||
{
|
||||
logAi->debug(
|
||||
#if AI_TRACE_LEVEL >= 1
|
||||
logAi->trace(
|
||||
"Hero %s can eliminate danger for town %s using path %s.",
|
||||
path.targetHero->name,
|
||||
town->name,
|
||||
path.toString());
|
||||
#endif
|
||||
|
||||
treatIsUnderControl = true;
|
||||
break;
|
||||
@ -140,7 +148,9 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
|
||||
if(cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES)
|
||||
{
|
||||
logAi->debug("Hero %s can be recruited to defend %s", hero->name, town->name);
|
||||
#if AI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Hero %s can be recruited to defend %s", hero->name, town->name);
|
||||
#endif
|
||||
tasks.push_back(Goals::sptr(Goals::RecruitHero(town, hero).setpriority(1)));
|
||||
continue;
|
||||
}
|
||||
@ -174,7 +184,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
|
||||
if(paths.empty())
|
||||
{
|
||||
logAi->debug("No ways to defend town %s", town->name);
|
||||
logAi->trace("No ways to defend town %s", town->name);
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -197,9 +207,11 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
#endif
|
||||
if(path.turn() <= treat.turn - 2)
|
||||
{
|
||||
#if AI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Deffer defence of %s by %s because he has enough time to rich the town next trun",
|
||||
town->name,
|
||||
path.targetHero->name);
|
||||
#endif
|
||||
|
||||
defferedPaths[path.targetHero].push_back(i);
|
||||
|
||||
|
@ -116,9 +116,10 @@ void Nullkiller::resetAiState()
|
||||
playerID = ai->playerID;
|
||||
lockedHeroes.clear();
|
||||
dangerHitMap->reset();
|
||||
useHeroChain = true;
|
||||
}
|
||||
|
||||
void Nullkiller::updateAiState(int pass)
|
||||
void Nullkiller::updateAiState(int pass, bool fast)
|
||||
{
|
||||
boost::this_thread::interruption_point();
|
||||
|
||||
@ -126,39 +127,42 @@ void Nullkiller::updateAiState(int pass)
|
||||
|
||||
activeHero = nullptr;
|
||||
|
||||
memory->removeInvisibleObjects(cb.get());
|
||||
|
||||
dangerHitMap->updateHitMap();
|
||||
|
||||
boost::this_thread::interruption_point();
|
||||
|
||||
heroManager->update();
|
||||
logAi->trace("Updating paths");
|
||||
|
||||
std::map<const CGHeroInstance *, HeroRole> activeHeroes;
|
||||
|
||||
for(auto hero : cb->getHeroesInfo())
|
||||
if(!fast)
|
||||
{
|
||||
if(getHeroLockedReason(hero) == HeroLockedReason::DEFENCE)
|
||||
continue;
|
||||
memory->removeInvisibleObjects(cb.get());
|
||||
|
||||
activeHeroes[hero] = heroManager->getHeroRole(hero);
|
||||
dangerHitMap->updateHitMap();
|
||||
|
||||
boost::this_thread::interruption_point();
|
||||
|
||||
heroManager->update();
|
||||
logAi->trace("Updating paths");
|
||||
|
||||
std::map<const CGHeroInstance *, HeroRole> activeHeroes;
|
||||
|
||||
for(auto hero : cb->getHeroesInfo())
|
||||
{
|
||||
if(getHeroLockedReason(hero) == HeroLockedReason::DEFENCE)
|
||||
continue;
|
||||
|
||||
activeHeroes[hero] = heroManager->getHeroRole(hero);
|
||||
}
|
||||
|
||||
PathfinderSettings cfg;
|
||||
cfg.useHeroChain = useHeroChain;
|
||||
cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT;
|
||||
|
||||
if(scanDepth != ScanDepth::FULL)
|
||||
{
|
||||
cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT * ((int)scanDepth + 1);
|
||||
}
|
||||
|
||||
pathfinder->updatePaths(activeHeroes, cfg);
|
||||
|
||||
objectClusterizer->clusterize();
|
||||
}
|
||||
|
||||
PathfinderSettings cfg;
|
||||
cfg.useHeroChain = true;
|
||||
cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT;
|
||||
|
||||
if(scanDepth != ScanDepth::FULL)
|
||||
{
|
||||
cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT * ((int)scanDepth + 1);
|
||||
}
|
||||
|
||||
pathfinder->updatePaths(activeHeroes, cfg);
|
||||
|
||||
armyManager->update();
|
||||
|
||||
objectClusterizer->clusterize();
|
||||
buildAnalyzer->update();
|
||||
decomposer->reset();
|
||||
|
||||
@ -213,13 +217,30 @@ void Nullkiller::makeTurn()
|
||||
{
|
||||
updateAiState(i);
|
||||
|
||||
Goals::TTask bestTask = taskptr(Goals::Invalid());
|
||||
|
||||
do
|
||||
{
|
||||
Goals::TTaskVec fastTasks = {
|
||||
choseBestTask(sptr(BuyArmyBehavior()), 1),
|
||||
choseBestTask(sptr(RecruitHeroBehavior()), 1),
|
||||
choseBestTask(sptr(BuildingBehavior()), 1)
|
||||
};
|
||||
|
||||
bestTask = choseBestTask(fastTasks);
|
||||
|
||||
if(bestTask->priority >= 1)
|
||||
{
|
||||
executeTask(bestTask);
|
||||
updateAiState(i, true);
|
||||
}
|
||||
} while(bestTask->priority >= 1);
|
||||
|
||||
Goals::TTaskVec bestTasks = {
|
||||
choseBestTask(sptr(BuyArmyBehavior()), 1),
|
||||
bestTask,
|
||||
choseBestTask(sptr(CaptureObjectsBehavior()), 1),
|
||||
choseBestTask(sptr(ClusterBehavior()), MAX_DEPTH),
|
||||
choseBestTask(sptr(RecruitHeroBehavior()), 1),
|
||||
choseBestTask(sptr(DefenceBehavior()), MAX_DEPTH),
|
||||
choseBestTask(sptr(BuildingBehavior()), 1),
|
||||
choseBestTask(sptr(GatherArmyBehavior()), MAX_DEPTH)
|
||||
};
|
||||
|
||||
@ -228,19 +249,25 @@ void Nullkiller::makeTurn()
|
||||
bestTasks.push_back(choseBestTask(sptr(StartupBehavior()), 1));
|
||||
}
|
||||
|
||||
Goals::TTask bestTask = choseBestTask(bestTasks);
|
||||
bestTask = choseBestTask(bestTasks);
|
||||
|
||||
HeroPtr hero = bestTask->getHero();
|
||||
|
||||
HeroRole heroRole = HeroRole::MAIN;
|
||||
|
||||
if(hero.validAndSet())
|
||||
heroRole = heroManager->getHeroRole(hero);
|
||||
|
||||
if(heroRole != HeroRole::MAIN || bestTask->getHeroExchangeCount() <= 1)
|
||||
useHeroChain = false;
|
||||
|
||||
if(bestTask->priority < NEXT_SCAN_MIN_PRIORITY
|
||||
&& scanDepth != ScanDepth::FULL)
|
||||
{
|
||||
HeroRole heroRole = HeroRole::MAIN;
|
||||
|
||||
if(hero.validAndSet())
|
||||
heroRole = heroManager->getHeroRole(hero);
|
||||
|
||||
if(heroRole == HeroRole::MAIN || bestTask->priority < MIN_PRIORITY)
|
||||
{
|
||||
useHeroChain = false;
|
||||
|
||||
logAi->trace(
|
||||
"Goal %s has too low priority %f so increasing scan depth",
|
||||
bestTask->toString(),
|
||||
@ -258,26 +285,31 @@ void Nullkiller::makeTurn()
|
||||
return;
|
||||
}
|
||||
|
||||
std::string taskDescr = bestTask->toString();
|
||||
executeTask(bestTask);
|
||||
}
|
||||
}
|
||||
|
||||
boost::this_thread::interruption_point();
|
||||
logAi->debug("Trying to realize %s (value %2.3f)", taskDescr, bestTask->priority);
|
||||
void Nullkiller::executeTask(Goals::TTask task)
|
||||
{
|
||||
std::string taskDescr = task->toString();
|
||||
|
||||
try
|
||||
{
|
||||
bestTask->accept(ai.get());
|
||||
}
|
||||
catch(goalFulfilledException &)
|
||||
{
|
||||
logAi->trace("Task %s completed", bestTask->toString());
|
||||
}
|
||||
catch(std::exception & e)
|
||||
{
|
||||
logAi->debug("Failed to realize subgoal of type %s, I will stop.", taskDescr);
|
||||
logAi->debug("The error message was: %s", e.what());
|
||||
boost::this_thread::interruption_point();
|
||||
logAi->debug("Trying to realize %s (value %2.3f)", taskDescr, task->priority);
|
||||
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
task->accept(ai.get());
|
||||
}
|
||||
catch(goalFulfilledException &)
|
||||
{
|
||||
logAi->trace("Task %s completed", task->toString());
|
||||
}
|
||||
catch(std::exception & e)
|
||||
{
|
||||
logAi->debug("Failed to realize subgoal of type %s, I will stop.", taskDescr);
|
||||
logAi->debug("The error message was: %s", e.what());
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ private:
|
||||
std::map<const CGHeroInstance *, HeroLockedReason> lockedHeroes;
|
||||
ScanDepth scanDepth;
|
||||
TResources lockedResources;
|
||||
bool useHeroChain;
|
||||
|
||||
public:
|
||||
std::unique_ptr<DangerHitMapAnalyzer> dangerHitMap;
|
||||
@ -86,7 +87,8 @@ public:
|
||||
|
||||
private:
|
||||
void resetAiState();
|
||||
void updateAiState(int pass);
|
||||
void updateAiState(int pass, bool fast = false);
|
||||
Goals::TTask choseBestTask(Goals::TSubgoal behavior, int decompositionMaxDepth) const;
|
||||
Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const;
|
||||
void executeTask(Goals::TTask task);
|
||||
};
|
||||
|
@ -162,6 +162,7 @@ namespace Goals
|
||||
virtual std::string toString() const = 0;
|
||||
virtual HeroPtr getHero() const = 0;
|
||||
virtual ~ITask() {}
|
||||
virtual int getHeroExchangeCount() const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,8 @@ namespace Goals
|
||||
virtual bool isElementar() const override { return true; }
|
||||
|
||||
virtual HeroPtr getHero() const override { return AbstractGoal::hero; }
|
||||
|
||||
virtual int getHeroExchangeCount() const override { return 0; }
|
||||
};
|
||||
|
||||
class DLL_EXPORT Invalid : public ElementarGoal<Invalid>
|
||||
|
@ -72,4 +72,9 @@ Composition & Composition::addNext(TSubgoal goal)
|
||||
bool Composition::isElementar() const
|
||||
{
|
||||
return subtasks.back()->isElementar();
|
||||
}
|
||||
|
||||
int Composition::getHeroExchangeCount() const
|
||||
{
|
||||
return isElementar() ? taskptr(*subtasks.back())->getHeroExchangeCount() : 0;
|
||||
}
|
@ -36,5 +36,6 @@ namespace Goals
|
||||
Composition & addNext(TSubgoal goal);
|
||||
virtual TGoalVec decompose() const override;
|
||||
virtual bool isElementar() const override;
|
||||
virtual int getHeroExchangeCount() const override;
|
||||
};
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ namespace Goals
|
||||
virtual bool operator==(const ExecuteHeroChain & other) const override;
|
||||
const AIPath & getPath() const { return chainPath; }
|
||||
|
||||
virtual int getHeroExchangeCount() const override { return chainPath.exchangeCount; }
|
||||
|
||||
private:
|
||||
bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile);
|
||||
};
|
||||
|
@ -23,23 +23,17 @@ std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared;
|
||||
std::set<int3> commitedTiles;
|
||||
std::set<int3> commitedTilesInitial;
|
||||
|
||||
#ifdef ENVIRONMENT64
|
||||
const int BUCKET_COUNT = 11;
|
||||
#else
|
||||
const int BUCKET_COUNT = 7;
|
||||
#endif // ENVIRONMENT64
|
||||
|
||||
const uint64_t FirstActorMask = 1;
|
||||
const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER;
|
||||
const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
|
||||
const uint64_t MIN_ARMY_STRENGTH_FOR_CHAIN = 5000;
|
||||
const uint64_t MIN_ARMY_STRENGTH_FOR_NEXT_ACTOR = 1000;
|
||||
const uint64_t CHAIN_MAX_DEPTH = 4;
|
||||
|
||||
AISharedStorage::AISharedStorage(int3 sizes)
|
||||
{
|
||||
if(!shared){
|
||||
shared.reset(new boost::multi_array<AIPathNode, 5>(
|
||||
boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][NUM_CHAINS]));
|
||||
boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][AIPathfinding::NUM_CHAINS]));
|
||||
}
|
||||
|
||||
nodes = shared;
|
||||
@ -139,16 +133,16 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
|
||||
const EPathfindingLayer layer,
|
||||
const ChainActor * actor)
|
||||
{
|
||||
int bucketIndex = ((uintptr_t)actor) % BUCKET_COUNT;
|
||||
int bucketOffset = bucketIndex * BUCKET_SIZE;
|
||||
auto chains = nodes.get(pos, layer); //FIXME: chain was the innermost layer
|
||||
int bucketIndex = ((uintptr_t)actor) % AIPathfinding::BUCKET_COUNT;
|
||||
int bucketOffset = bucketIndex * AIPathfinding::BUCKET_SIZE;
|
||||
auto chains = nodes.get(pos, layer);
|
||||
|
||||
if(chains[0].blocked())
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
for(auto i = BUCKET_SIZE - 1; i >= 0; i--)
|
||||
for(auto i = AIPathfinding::BUCKET_SIZE - 1; i >= 0; i--)
|
||||
{
|
||||
AIPathNode & node = chains[i + bucketOffset];
|
||||
|
||||
@ -171,8 +165,9 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
|
||||
std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
|
||||
{
|
||||
if(heroChainPass)
|
||||
{
|
||||
calculateTownPortalTeleportations(heroChain);
|
||||
{
|
||||
if(heroChainTurn == 0)
|
||||
calculateTownPortalTeleportations(heroChain);
|
||||
|
||||
return heroChain;
|
||||
}
|
||||
@ -207,7 +202,8 @@ std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
|
||||
}
|
||||
}
|
||||
|
||||
calculateTownPortalTeleportations(initialNodes);
|
||||
if(heroChainTurn == 0)
|
||||
calculateTownPortalTeleportations(initialNodes);
|
||||
|
||||
return initialNodes;
|
||||
}
|
||||
@ -406,8 +402,8 @@ public:
|
||||
AINodeStorage & storage, AISharedStorage & nodes, const std::vector<int3> & tiles, uint64_t chainMask, int heroChainTurn)
|
||||
:existingChains(), newChains(), delayedWork(), nodes(nodes), storage(storage), chainMask(chainMask), heroChainTurn(heroChainTurn), heroChain(), tiles(tiles)
|
||||
{
|
||||
existingChains.reserve(NUM_CHAINS);
|
||||
newChains.reserve(NUM_CHAINS);
|
||||
existingChains.reserve(AIPathfinding::NUM_CHAINS);
|
||||
newChains.reserve(AIPathfinding::NUM_CHAINS);
|
||||
}
|
||||
|
||||
void execute(const blocked_range<size_t>& r)
|
||||
@ -623,6 +619,9 @@ void HeroChainCalculationTask::calculateHeroChain(
|
||||
if((node->actor->chainMask & chainMask) == 0 && (srcNode->actor->chainMask & chainMask) == 0)
|
||||
continue;
|
||||
|
||||
if(node->actor->actorExchangeCount + srcNode->actor->actorExchangeCount > CHAIN_MAX_DEPTH)
|
||||
continue;
|
||||
|
||||
if(node->action == CGPathNode::ENodeAction::BATTLE
|
||||
|| node->action == CGPathNode::ENodeAction::TELEPORT_BATTLE
|
||||
|| node->action == CGPathNode::ENodeAction::TELEPORT_NORMAL
|
||||
@ -994,8 +993,6 @@ struct TowmPortalFinder
|
||||
|
||||
CGPathNode * getBestInitialNodeForTownPortal(const CGTownInstance * targetTown)
|
||||
{
|
||||
CGPathNode * bestNode = nullptr;
|
||||
|
||||
for(CGPathNode * node : initialNodes)
|
||||
{
|
||||
auto aiNode = nodeStorage->getAINode(node);
|
||||
@ -1018,11 +1015,10 @@ struct TowmPortalFinder
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!bestNode || bestNode->getCost() > node->getCost())
|
||||
bestNode = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
return bestNode;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
boost::optional<AIPathNode *> createTownPortalNode(const CGTownInstance * targetTown)
|
||||
@ -1060,6 +1056,55 @@ struct TowmPortalFinder
|
||||
}
|
||||
};
|
||||
|
||||
template<class TVector>
|
||||
void AINodeStorage::calculateTownPortal(
|
||||
const ChainActor * actor,
|
||||
const std::map<const CGHeroInstance *, int> & maskMap,
|
||||
const std::vector<CGPathNode *> & initialNodes,
|
||||
TVector & output)
|
||||
{
|
||||
auto towns = cb->getTownsInfo(false);
|
||||
|
||||
vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
|
||||
{
|
||||
return cb->getPlayerRelations(actor->hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
|
||||
});
|
||||
|
||||
if(!towns.size())
|
||||
{
|
||||
return; // no towns no need to run loop further
|
||||
}
|
||||
|
||||
TowmPortalFinder townPortalFinder(actor, initialNodes, towns, this);
|
||||
|
||||
if(townPortalFinder.actorCanCastTownPortal())
|
||||
{
|
||||
for(const CGTownInstance * targetTown : towns)
|
||||
{
|
||||
// TODO: allow to hide visiting hero in garrison
|
||||
if(targetTown->visitingHero)
|
||||
{
|
||||
auto basicMask = maskMap.at(targetTown->visitingHero.get());
|
||||
bool heroIsInChain = (actor->chainMask & basicMask) != 0;
|
||||
bool sameActorInTown = actor->chainMask == basicMask;
|
||||
|
||||
if(sameActorInTown || !heroIsInChain)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto nodeOptional = townPortalFinder.createTownPortalNode(targetTown);
|
||||
|
||||
if(nodeOptional)
|
||||
{
|
||||
#if PATHFINDER_TRACE_LEVEL >= 1
|
||||
logAi->trace("Adding town portal node at %s", targetTown->name);
|
||||
#endif
|
||||
output.push_back(nodeOptional.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *> & initialNodes)
|
||||
{
|
||||
std::set<const ChainActor *> actorsOfInitial;
|
||||
@ -1068,7 +1113,8 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *>
|
||||
{
|
||||
auto aiNode = getAINode(node);
|
||||
|
||||
actorsOfInitial.insert(aiNode->actor->baseActor);
|
||||
if(aiNode->actor->hero)
|
||||
actorsOfInitial.insert(aiNode->actor->baseActor);
|
||||
}
|
||||
|
||||
std::map<const CGHeroInstance *, int> maskMap;
|
||||
@ -1079,50 +1125,28 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *>
|
||||
maskMap[basicActor->hero] = basicActor->chainMask;
|
||||
}
|
||||
|
||||
for(const ChainActor * actor : actorsOfInitial)
|
||||
boost::sort(initialNodes, NodeComparer<CGPathNode>());
|
||||
|
||||
std::vector<const ChainActor *> actorsVector(actorsOfInitial.begin(), actorsOfInitial.end());
|
||||
tbb::concurrent_vector<CGPathNode *> output;
|
||||
|
||||
if(actorsVector.size() * initialNodes.size() > 1000)
|
||||
{
|
||||
if(!actor->hero)
|
||||
continue;
|
||||
|
||||
auto towns = cb->getTownsInfo(false);
|
||||
|
||||
vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
|
||||
{
|
||||
return cb->getPlayerRelations(actor->hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
|
||||
});
|
||||
|
||||
if(!towns.size())
|
||||
{
|
||||
return; // no towns no need to run loop further
|
||||
}
|
||||
|
||||
TowmPortalFinder townPortalFinder(actor, initialNodes, towns, this);
|
||||
|
||||
if(townPortalFinder.actorCanCastTownPortal())
|
||||
{
|
||||
for(const CGTownInstance * targetTown : towns)
|
||||
parallel_for(blocked_range<size_t>(0, actorsVector.size()), [&](const blocked_range<size_t> & r)
|
||||
{
|
||||
// TODO: allow to hide visiting hero in garrison
|
||||
if(targetTown->visitingHero)
|
||||
for(int i = r.begin(); i != r.end(); i++)
|
||||
{
|
||||
auto basicMask = maskMap[targetTown->visitingHero.get()];
|
||||
bool heroIsInChain = (actor->chainMask & basicMask) != 0;
|
||||
bool sameActorInTown = actor->chainMask == basicMask;
|
||||
|
||||
if(sameActorInTown || !heroIsInChain)
|
||||
continue;
|
||||
calculateTownPortal(actorsVector[i], maskMap, initialNodes, output);
|
||||
}
|
||||
});
|
||||
|
||||
auto nodeOptional = townPortalFinder.createTownPortalNode(targetTown);
|
||||
|
||||
if(nodeOptional)
|
||||
{
|
||||
#if PATHFINDER_TRACE_LEVEL >= 1
|
||||
logAi->trace("Adding town portal node at %s", targetTown->name);
|
||||
#endif
|
||||
initialNodes.push_back(nodeOptional.get());
|
||||
}
|
||||
}
|
||||
std::copy(output.begin(), output.end(), std::back_inserter(initialNodes));
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto actor : actorsVector)
|
||||
{
|
||||
calculateTownPortal(actor, maskMap, initialNodes, initialNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1206,7 +1230,7 @@ bool AINodeStorage::hasBetterChain(
|
||||
continue;
|
||||
}
|
||||
|
||||
#if AI_TRACE_LEVEL >= 2
|
||||
#if PATHFINDER_TRACE_LEVEL >= 2
|
||||
logAi->trace(
|
||||
"Block ineficient move because of stronger hero %s->%s, hero: %s[%X], army %lld, mp diff: %i",
|
||||
source->coord.toString(),
|
||||
@ -1244,7 +1268,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
||||
{
|
||||
std::vector<AIPath> paths;
|
||||
|
||||
paths.reserve(NUM_CHAINS / 4);
|
||||
paths.reserve(AIPathfinding::NUM_CHAINS / 4);
|
||||
|
||||
auto chains = nodes.get(pos, isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL);
|
||||
|
||||
|
@ -25,10 +25,16 @@
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
const int BUCKET_COUNT = 11;
|
||||
const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER;
|
||||
#ifdef ENVIRONMENT64
|
||||
const int BUCKET_COUNT = 7;
|
||||
#else
|
||||
const int BUCKET_COUNT = 5;
|
||||
#endif // ENVIRONMENT64
|
||||
|
||||
const int BUCKET_SIZE = 5;
|
||||
const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
|
||||
const int THREAD_COUNT = 8;
|
||||
const int CHAIN_MAX_DEPTH = 4;
|
||||
}
|
||||
|
||||
struct AIPathNode : public CGPathNode
|
||||
@ -239,7 +245,14 @@ public:
|
||||
return ((uintptr_t)actor * 395) % AIPathfinding::BUCKET_COUNT;
|
||||
}
|
||||
|
||||
|
||||
void calculateTownPortalTeleportations(std::vector<CGPathNode *> & neighbours);
|
||||
void fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const;
|
||||
|
||||
private:
|
||||
template<class TVector>
|
||||
void calculateTownPortal(
|
||||
const ChainActor * actor,
|
||||
const std::map<const CGHeroInstance *, int> & maskMap,
|
||||
const std::vector<CGPathNode *> & initialNodes,
|
||||
TVector & output);
|
||||
};
|
||||
|
@ -275,6 +275,9 @@ ExchangeResult HeroExchangeMap::tryExchangeNoLock(const ChainActor * other)
|
||||
|
||||
if(!differentMasks) return result;
|
||||
|
||||
if(actor->allowSpellCast || other->allowSpellCast)
|
||||
return result;
|
||||
|
||||
TResources resources = ai->cb->getResourceAmount();
|
||||
|
||||
if(!resources.canAfford(actor->armyCost + other->armyCost))
|
||||
|
@ -315,11 +315,28 @@ void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
|
||||
setThreadName("CVCMIServer::handleConnection");
|
||||
c->enterLobbyConnectionMode();
|
||||
|
||||
#ifndef _MSC_VER
|
||||
try
|
||||
{
|
||||
#endif
|
||||
while(c->connected)
|
||||
{
|
||||
CPack * pack = c->retrievePack();
|
||||
CPack * pack;
|
||||
|
||||
try
|
||||
{
|
||||
pack = c->retrievePack();
|
||||
}
|
||||
catch(boost::system::system_error & e)
|
||||
{
|
||||
logNetwork->error("Network error receiving a pack. Connection %s dies. What happened: %s", c->toString(), e.what());
|
||||
|
||||
if(state != EServerState::LOBBY)
|
||||
gh->handleClientDisconnection(c);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(auto lobbyPack = dynamic_ptr_cast<CPackForLobby>(pack))
|
||||
{
|
||||
handleReceivedPack(std::unique_ptr<CPackForLobby>(lobbyPack));
|
||||
@ -329,14 +346,8 @@ void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
|
||||
gh->handleReceivedPack(serverPack);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(boost::system::system_error & e)
|
||||
{
|
||||
(void)e;
|
||||
if(state != EServerState::LOBBY)
|
||||
gh->handleClientDisconnection(c);
|
||||
}
|
||||
/*
|
||||
#ifndef _MSC_VER
|
||||
}
|
||||
catch(const std::exception & e)
|
||||
{
|
||||
(void)e;
|
||||
@ -348,7 +359,8 @@ void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
|
||||
state = EServerState::SHUTDOWN;
|
||||
handleException();
|
||||
throw;
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
|
||||
boost::unique_lock<boost::recursive_mutex> queueLock(mx);
|
||||
// if(state != ENDING_AND_STARTING_GAME)
|
||||
|
Loading…
Reference in New Issue
Block a user