mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
NKAI: improve boat handling by object graph, a set of fixes
This commit is contained in:
parent
adfcb650e2
commit
30d9daf62c
@ -329,14 +329,24 @@ void ObjectClusterizer::clusterizeObject(
|
||||
if(ai->heroManager->getHeroRole(path.targetHero) == HeroRole::SCOUT)
|
||||
{
|
||||
if(path.movementCost() > 2.0f)
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Path is too far %f", path.movementCost());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if(path.movementCost() > 4.0f)
|
||||
else if(path.movementCost() > 4.0f && obj->ID != Obj::TOWN)
|
||||
{
|
||||
auto strategicalValue = valueEvaluator.getStrategicalValue(obj);
|
||||
|
||||
if(strategicalValue < 0.5f)
|
||||
if(strategicalValue < 0.3f)
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Object value is too low %f", strategicalValue);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(!shouldVisit(ai, path.targetHero, obj))
|
||||
|
@ -49,6 +49,9 @@ Goals::TGoalVec StayAtTownBehavior::decompose() const
|
||||
if(town->visitingHero && town->visitingHero.get() != path.targetHero)
|
||||
continue;
|
||||
|
||||
if(!path.targetHero->hasSpellbook() || path.targetHero->mana >= 0.75f * path.targetHero->manaLimit())
|
||||
continue;
|
||||
|
||||
if(path.turn() == 0 && !path.getFirstBlockedAction() && path.exchangeCount <= 1)
|
||||
{
|
||||
if(path.targetHero->mana == path.targetHero->manaLimit())
|
||||
|
@ -67,40 +67,40 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
|
||||
for(int i = chainPath.nodes.size() - 1; i >= 0; i--)
|
||||
{
|
||||
auto & node = chainPath.nodes[i];
|
||||
auto * node = &chainPath.nodes[i];
|
||||
|
||||
const CGHeroInstance * hero = node.targetHero;
|
||||
const CGHeroInstance * hero = node->targetHero;
|
||||
HeroPtr heroPtr = hero;
|
||||
|
||||
if(node.parentIndex >= i)
|
||||
if(node->parentIndex >= i)
|
||||
{
|
||||
logAi->error("Invalid parentIndex while executing node " + node.coord.toString());
|
||||
logAi->error("Invalid parentIndex while executing node " + node->coord.toString());
|
||||
}
|
||||
|
||||
if(vstd::contains(blockedIndexes, i))
|
||||
{
|
||||
blockedIndexes.insert(node.parentIndex);
|
||||
blockedIndexes.insert(node->parentIndex);
|
||||
ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
logAi->debug("Executing chain node %d. Moving hero %s to %s", i, hero->getNameTranslated(), node.coord.toString());
|
||||
logAi->debug("Executing chain node %d. Moving hero %s to %s", i, hero->getNameTranslated(), node->coord.toString());
|
||||
|
||||
try
|
||||
{
|
||||
if(hero->movementPointsRemaining() > 0)
|
||||
{
|
||||
ai->nullkiller->setActive(hero, node.coord);
|
||||
ai->nullkiller->setActive(hero, node->coord);
|
||||
|
||||
if(node.specialAction)
|
||||
if(node->specialAction)
|
||||
{
|
||||
if(node.actionIsBlocked)
|
||||
if(node->actionIsBlocked)
|
||||
{
|
||||
throw cannotFulfillGoalException("Path is nondeterministic.");
|
||||
}
|
||||
|
||||
node.specialAction->execute(hero);
|
||||
node->specialAction->execute(hero);
|
||||
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
@ -109,10 +109,34 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(node.turns == 0 && node.coord != hero->visitablePos())
|
||||
else if(i > 0 && ai->nullkiller->settings->isObjectGraphAllowed())
|
||||
{
|
||||
auto targetNode = cb->getPathsInfo(hero)->getPathInfo(node.coord);
|
||||
auto chainMask = i < chainPath.nodes.size() - 1 ? chainPath.nodes[i + 1].chainMask : node->chainMask;
|
||||
|
||||
for(auto j = i - 1; j >= 0; j--)
|
||||
{
|
||||
auto & nextNode = chainPath.nodes[j];
|
||||
|
||||
if(nextNode.specialAction || nextNode.chainMask != chainMask)
|
||||
break;
|
||||
|
||||
auto targetNode = cb->getPathsInfo(hero)->getPathInfo(nextNode.coord);
|
||||
|
||||
if(!targetNode->reachable()
|
||||
|| targetNode->getCost() > nextNode.cost)
|
||||
break;
|
||||
|
||||
i = j;
|
||||
node = &nextNode;
|
||||
|
||||
if(targetNode->action == EPathNodeAction::BATTLE || targetNode->action == EPathNodeAction::TELEPORT_BATTLE)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(node->turns == 0 && node->coord != hero->visitablePos())
|
||||
{
|
||||
auto targetNode = cb->getPathsInfo(hero)->getPathInfo(node->coord);
|
||||
|
||||
if(targetNode->accessible == EPathAccessibility::NOT_SET
|
||||
|| targetNode->accessible == EPathAccessibility::BLOCKED
|
||||
@ -122,7 +146,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
logAi->error(
|
||||
"Unable to complete chain. Expected hero %s to arrive to %s in 0 turns but he cannot do this",
|
||||
hero->getNameTranslated(),
|
||||
node.coord.toString());
|
||||
node->coord.toString());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -132,7 +156,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(moveHeroToTile(hero, node.coord))
|
||||
if(moveHeroToTile(hero, node->coord))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -149,11 +173,11 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
if(hero->movementPointsRemaining() > 0)
|
||||
{
|
||||
CGPath path;
|
||||
bool isOk = cb->getPathsInfo(hero)->getPath(path, node.coord);
|
||||
bool isOk = cb->getPathsInfo(hero)->getPath(path, node->coord);
|
||||
|
||||
if(isOk && path.nodes.back().turns > 0)
|
||||
{
|
||||
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero->getNameTranslated(), hero->movementPointsRemaining(), node.coord.toString());
|
||||
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero->getNameTranslated(), hero->movementPointsRemaining(), node->coord.toString());
|
||||
|
||||
ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
|
||||
return;
|
||||
@ -165,15 +189,15 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
}
|
||||
}
|
||||
|
||||
if(node.coord == hero->visitablePos())
|
||||
if(node->coord == hero->visitablePos())
|
||||
continue;
|
||||
|
||||
if(node.turns == 0)
|
||||
if(node->turns == 0)
|
||||
{
|
||||
logAi->error(
|
||||
"Unable to complete chain. Expected hero %s to arive to %s but he is at %s",
|
||||
hero->getNameTranslated(),
|
||||
node.coord.toString(),
|
||||
node->coord.toString(),
|
||||
hero->visitablePos().toString());
|
||||
|
||||
return;
|
||||
@ -181,13 +205,13 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
|
||||
// no exception means we were not able to reach the tile
|
||||
ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
|
||||
blockedIndexes.insert(node.parentIndex);
|
||||
blockedIndexes.insert(node->parentIndex);
|
||||
}
|
||||
catch(const goalFulfilledException &)
|
||||
{
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->debug("Hero %s was killed while attempting to reach %s", heroPtr.name, node.coord.toString());
|
||||
logAi->debug("Hero %s was killed while attempting to reach %s", heroPtr.name, node->coord.toString());
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -529,7 +529,7 @@ bool AINodeStorage::calculateHeroChain()
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, data.size()), [&](const tbb::blocked_range<size_t>& r)
|
||||
{
|
||||
//auto r = blocked_range<size_t>(0, data.size());
|
||||
HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn);
|
||||
HeroChainCalculationTask task(*this, data, chainMask, heroChainTurn);
|
||||
|
||||
task.execute(r);
|
||||
|
||||
@ -543,7 +543,7 @@ bool AINodeStorage::calculateHeroChain()
|
||||
else
|
||||
{
|
||||
auto r = tbb::blocked_range<size_t>(0, data.size());
|
||||
HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn);
|
||||
HeroChainCalculationTask task(*this, data, chainMask, heroChainTurn);
|
||||
|
||||
task.execute(r);
|
||||
task.flushResult(heroChain);
|
||||
|
@ -28,7 +28,7 @@ namespace AIPathfinding
|
||||
bool allowBypassObjects)
|
||||
{
|
||||
std::vector<std::shared_ptr<IPathfindingRule>> rules = {
|
||||
std::make_shared<AILayerTransitionRule>(cb, ai, nodeStorage),
|
||||
std::make_shared<AILayerTransitionRule>(cb, ai, nodeStorage, allowBypassObjects),
|
||||
std::make_shared<DestinationActionRule>(),
|
||||
std::make_shared<AIMovementToDestinationRule>(nodeStorage, allowBypassObjects),
|
||||
std::make_shared<MovementCostRule>(),
|
||||
|
@ -37,10 +37,8 @@ namespace AIPathfinding
|
||||
return Goals::sptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
bool BuildBoatAction::canAct(const AIPathNode * source) const
|
||||
bool BuildBoatAction::canAct(const CGHeroInstance * hero, const TResources & reservedResources) const
|
||||
{
|
||||
auto hero = source->actor->hero;
|
||||
|
||||
if(cb->getPlayerRelations(hero->tempOwner, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL > 1
|
||||
@ -53,7 +51,7 @@ namespace AIPathfinding
|
||||
|
||||
shipyard->getBoatCost(boatCost);
|
||||
|
||||
if(!cb->getResourceAmount().canAfford(source->actor->armyCost + boatCost))
|
||||
if(!cb->getResourceAmount().canAfford(reservedResources + boatCost))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL > 1
|
||||
logAi->trace("Can not build a boat. Not enough resources.");
|
||||
@ -65,6 +63,18 @@ namespace AIPathfinding
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BuildBoatAction::canAct(const AIPathNode * source) const
|
||||
{
|
||||
return canAct(source->actor->hero, source->actor->armyCost);
|
||||
}
|
||||
|
||||
bool BuildBoatAction::canAct(const AIPathNodeInfo & source) const
|
||||
{
|
||||
TResources res;
|
||||
|
||||
return canAct(source.targetHero, res);
|
||||
}
|
||||
|
||||
const CGObjectInstance * BuildBoatAction::targetObject() const
|
||||
{
|
||||
return dynamic_cast<const CGObjectInstance*>(shipyard);
|
||||
@ -75,6 +85,11 @@ namespace AIPathfinding
|
||||
return sourceActor->resourceActor;
|
||||
}
|
||||
|
||||
std::shared_ptr<SpecialAction> BuildBoatActionFactory::create(const Nullkiller * ai)
|
||||
{
|
||||
return std::make_shared<BuildBoatAction>(ai->cb.get(), dynamic_cast<const IShipyard * >(ai->cb->getObj(shipyard)));
|
||||
}
|
||||
|
||||
void SummonBoatAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai);
|
||||
|
@ -57,6 +57,8 @@ namespace AIPathfinding
|
||||
}
|
||||
|
||||
bool canAct(const AIPathNode * source) const override;
|
||||
bool canAct(const AIPathNodeInfo & source) const override;
|
||||
bool canAct(const CGHeroInstance * hero, const TResources & reservedResources) const;
|
||||
|
||||
void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
@ -68,6 +70,19 @@ namespace AIPathfinding
|
||||
|
||||
const CGObjectInstance * targetObject() const override;
|
||||
};
|
||||
|
||||
class BuildBoatActionFactory : public ISpecialActionFactory
|
||||
{
|
||||
ObjectInstanceID shipyard;
|
||||
|
||||
public:
|
||||
BuildBoatActionFactory(ObjectInstanceID shipyard)
|
||||
:shipyard(shipyard)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<SpecialAction> create(const Nullkiller * ai) override;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,11 @@ namespace AIPathfinding
|
||||
return canAct(node->actor->hero);
|
||||
}
|
||||
|
||||
bool QuestAction::canAct(const AIPathNodeInfo & node) const
|
||||
{
|
||||
return canAct(node.targetHero);
|
||||
}
|
||||
|
||||
bool QuestAction::canAct(const CGHeroInstance * hero) const
|
||||
{
|
||||
if(questInfo.obj->ID == Obj::BORDER_GATE || questInfo.obj->ID == Obj::BORDERGUARD)
|
||||
|
@ -29,7 +29,7 @@ namespace AIPathfinding
|
||||
}
|
||||
|
||||
bool canAct(const AIPathNode * node) const override;
|
||||
|
||||
bool canAct(const AIPathNodeInfo & node) const override;
|
||||
bool canAct(const CGHeroInstance * hero) const;
|
||||
|
||||
Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
|
||||
|
@ -22,6 +22,7 @@ namespace NKAI
|
||||
{
|
||||
|
||||
struct AIPathNode;
|
||||
struct AIPathNodeInfo;
|
||||
class ChainActor;
|
||||
|
||||
class SpecialAction
|
||||
@ -34,6 +35,11 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool canAct(const AIPathNodeInfo & source) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal decompose(const CGHeroInstance * hero) const;
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const;
|
||||
@ -89,4 +95,10 @@ public:
|
||||
const AIPathNode * srcNode) const override;
|
||||
};
|
||||
|
||||
class ISpecialActionFactory
|
||||
{
|
||||
public:
|
||||
virtual std::shared_ptr<SpecialAction> create(const Nullkiller * ai) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../../../lib/logging/VisualLogger.h"
|
||||
#include "Actions/QuestAction.h"
|
||||
#include "../pforeach.h"
|
||||
#include "Actions/BoatActions.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
@ -121,7 +122,7 @@ public:
|
||||
|
||||
ConnectionCostInfo currentCost = getConnectionsCost(paths);
|
||||
|
||||
if(currentCost.connectionsCount <= 2)
|
||||
if(currentCost.connectionsCount <= 1 || currentCost.connectionsCount == 2 && currentCost.totalCost < 3.0f)
|
||||
return;
|
||||
|
||||
float neighborCost = getNeighborConnectionsCost(pos, paths);
|
||||
@ -170,7 +171,7 @@ private:
|
||||
|
||||
auto obj = ai->cb->getTopObj(pos);
|
||||
|
||||
if(obj && obj->ID == Obj::BOAT)
|
||||
if((obj && obj->ID == Obj::BOAT) || target->isVirtualBoat(pos))
|
||||
{
|
||||
ai->pathfinder->calculatePathInfo(pathCache, pos);
|
||||
|
||||
@ -230,9 +231,9 @@ private:
|
||||
if(!cb->getTile(pos)->isWater())
|
||||
continue;
|
||||
|
||||
auto startingObjIsBoatOrShipyard = obj1 && (obj1->ID == Obj::BOAT || obj1->ID == Obj::SHIPYARD);
|
||||
auto startingObjIsBoat = (obj1 && obj1->ID == Obj::BOAT) || target->isVirtualBoat(pos1);
|
||||
|
||||
if(!startingObjIsBoatOrShipyard)
|
||||
if(!startingObjIsBoat)
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -320,12 +321,22 @@ private:
|
||||
assert(objectActor->visitablePos() == visitablePos);
|
||||
|
||||
actorObjectMap[objectActor] = obj;
|
||||
actors[objectActor] = obj->ID == Obj::TOWN || obj->ID == Obj::SHIPYARD || obj->ID == Obj::BOAT ? HeroRole::MAIN : HeroRole::SCOUT;
|
||||
actors[objectActor] = obj->ID == Obj::TOWN || obj->ID == Obj::BOAT ? HeroRole::MAIN : HeroRole::SCOUT;
|
||||
|
||||
target->addObject(obj);
|
||||
|
||||
auto shipyard = dynamic_cast<const IShipyard *>(obj);
|
||||
|
||||
if(shipyard && shipyard->bestLocation().valid())
|
||||
{
|
||||
int3 virtualBoat = shipyard->bestLocation();
|
||||
|
||||
addJunctionActor(virtualBoat, true);
|
||||
target->addVirtualBoat(virtualBoat, obj);
|
||||
}
|
||||
}
|
||||
|
||||
void addJunctionActor(const int3 & visitablePos)
|
||||
void addJunctionActor(const int3 & visitablePos, bool isVirtualBoat = false)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(syncLock);
|
||||
|
||||
@ -339,7 +350,7 @@ private:
|
||||
objectActor->pos = objectActor->convertFromVisitablePos(visitablePos);
|
||||
objectActor->initObj(rng);
|
||||
|
||||
if(ai->cb->getTile(visitablePos)->isWater())
|
||||
if(isVirtualBoat || ai->cb->getTile(visitablePos)->isWater())
|
||||
{
|
||||
objectActor->boat = temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get();
|
||||
}
|
||||
@ -347,7 +358,7 @@ private:
|
||||
assert(objectActor->visitablePos() == visitablePos);
|
||||
|
||||
actorObjectMap[objectActor] = nullptr;
|
||||
actors[objectActor] = HeroRole::SCOUT;
|
||||
actors[objectActor] = isVirtualBoat ? HeroRole::MAIN : HeroRole::SCOUT;
|
||||
|
||||
target->registerJunction(visitablePos);
|
||||
}
|
||||
@ -397,7 +408,15 @@ bool ObjectGraph::tryAddConnection(
|
||||
float cost,
|
||||
uint64_t danger)
|
||||
{
|
||||
return nodes[from].connections[to].update(cost, danger);
|
||||
auto result = nodes[from].connections[to].update(cost, danger);
|
||||
auto & connection = nodes[from].connections[to];
|
||||
|
||||
if(result && isVirtualBoat(to) && !connection.specialAction)
|
||||
{
|
||||
connection.specialAction = std::make_shared<AIPathfinding::BuildBoatActionFactory>(virtualBoats[to]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ObjectGraph::removeConnection(const int3 & from, const int3 & to)
|
||||
@ -422,19 +441,30 @@ void ObjectGraph::updateGraph(const Nullkiller * ai)
|
||||
|
||||
void ObjectGraph::addObject(const CGObjectInstance * obj)
|
||||
{
|
||||
nodes[obj->visitablePos()].init(obj);
|
||||
if(!hasNodeAt(obj->visitablePos()))
|
||||
nodes[obj->visitablePos()].init(obj);
|
||||
}
|
||||
|
||||
void ObjectGraph::addVirtualBoat(const int3 & pos, const CGObjectInstance * shipyard)
|
||||
{
|
||||
if(!isVirtualBoat(pos))
|
||||
{
|
||||
virtualBoats[pos] = shipyard->id;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectGraph::registerJunction(const int3 & pos)
|
||||
{
|
||||
nodes[pos].initJunction();
|
||||
if(!hasNodeAt(pos))
|
||||
nodes[pos].initJunction();
|
||||
|
||||
}
|
||||
|
||||
void ObjectGraph::removeObject(const CGObjectInstance * obj)
|
||||
{
|
||||
nodes[obj->visitablePos()].objectExists = false;
|
||||
|
||||
if(obj->ID == Obj::BOAT)
|
||||
if(obj->ID == Obj::BOAT && !isVirtualBoat(obj->visitablePos()))
|
||||
{
|
||||
vstd::erase_if(nodes[obj->visitablePos()].connections, [&](const std::pair<int3, ObjectLink> & link) -> bool
|
||||
{
|
||||
@ -512,6 +542,27 @@ GraphPaths::GraphPaths()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<SpecialAction> getCompositeAction(
|
||||
const Nullkiller * ai,
|
||||
std::shared_ptr<ISpecialActionFactory> linkActionFactory,
|
||||
std::shared_ptr<SpecialAction> transitionAction)
|
||||
{
|
||||
if(!linkActionFactory)
|
||||
return transitionAction;
|
||||
|
||||
auto linkAction = linkActionFactory->create(ai);
|
||||
|
||||
if(!transitionAction)
|
||||
return linkAction;
|
||||
|
||||
std::vector<std::shared_ptr<const SpecialAction>> actionsArray = {
|
||||
transitionAction,
|
||||
linkAction
|
||||
};
|
||||
|
||||
return std::make_shared<CompositeAction>(actionsArray);
|
||||
}
|
||||
|
||||
void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkiller * ai)
|
||||
{
|
||||
graph.copyFrom(*ai->baseGraph);
|
||||
@ -561,15 +612,16 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
|
||||
|
||||
node.isInQueue = false;
|
||||
|
||||
graph.iterateConnections(pos.coord, [this, ai, &pos, &node, &transitionAction, &pq](int3 target, ObjectLink o)
|
||||
graph.iterateConnections(pos.coord, [this, ai, &pos, &node, &transitionAction, &pq](int3 target, const ObjectLink & o)
|
||||
{
|
||||
auto targetNodeType = o.danger || transitionAction ? GrapthPathNodeType::BATTLE : pos.nodeType;
|
||||
auto compositeAction = getCompositeAction(ai, o.specialAction, transitionAction);
|
||||
auto targetNodeType = o.danger || compositeAction ? GrapthPathNodeType::BATTLE : pos.nodeType;
|
||||
auto targetPointer = GraphPathNodePointer(target, targetNodeType);
|
||||
auto & targetNode = getOrCreateNode(targetPointer);
|
||||
|
||||
if(targetNode.tryUpdate(pos, node, o))
|
||||
{
|
||||
targetNode.specialAction = transitionAction;
|
||||
targetNode.specialAction = compositeAction;
|
||||
|
||||
auto targetGraphNode = graph.getNode(target);
|
||||
|
||||
@ -704,6 +756,11 @@ void GraphPaths::addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHe
|
||||
n.parentIndex = -1;
|
||||
n.specialAction = getNode(*graphTile).specialAction;
|
||||
|
||||
if(n.specialAction)
|
||||
{
|
||||
n.actionIsBlocked = !n.specialAction->canAct(n);
|
||||
}
|
||||
|
||||
for(auto & node : path.nodes)
|
||||
{
|
||||
node.parentIndex++;
|
||||
@ -806,7 +863,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector<AIPath> & paths, int3
|
||||
}
|
||||
}
|
||||
|
||||
if(!path.nodes.empty())
|
||||
if(path.nodes.size() > 1)
|
||||
continue;
|
||||
|
||||
for(auto graphTile = tilesToPass.rbegin(); graphTile != tilesToPass.rend(); graphTile++)
|
||||
@ -820,6 +877,11 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector<AIPath> & paths, int3
|
||||
n.specialAction = node.specialAction;
|
||||
n.parentIndex = path.nodes.size();
|
||||
|
||||
if(n.specialAction)
|
||||
{
|
||||
n.actionIsBlocked = !n.specialAction->canAct(n);
|
||||
}
|
||||
|
||||
auto blocker = ai->objectClusterizer->getBlocker(n);
|
||||
|
||||
if(!blocker)
|
||||
|
@ -22,6 +22,7 @@ struct ObjectLink
|
||||
{
|
||||
float cost = 100000; // some big number
|
||||
uint64_t danger = 0;
|
||||
std::shared_ptr<ISpecialActionFactory> specialAction;
|
||||
|
||||
bool update(float newCost, uint64_t newDanger)
|
||||
{
|
||||
@ -62,20 +63,33 @@ struct ObjectNode
|
||||
class ObjectGraph
|
||||
{
|
||||
std::unordered_map<int3, ObjectNode> nodes;
|
||||
std::unordered_map<int3, ObjectInstanceID> virtualBoats;
|
||||
|
||||
public:
|
||||
ObjectGraph()
|
||||
:nodes(), virtualBoats()
|
||||
{
|
||||
}
|
||||
|
||||
void updateGraph(const Nullkiller * ai);
|
||||
void addObject(const CGObjectInstance * obj);
|
||||
void registerJunction(const int3 & pos);
|
||||
void addVirtualBoat(const int3 & pos, const CGObjectInstance * shipyard);
|
||||
void connectHeroes(const Nullkiller * ai);
|
||||
void removeObject(const CGObjectInstance * obj);
|
||||
bool tryAddConnection(const int3 & from, const int3 & to, float cost, uint64_t danger);
|
||||
void removeConnection(const int3 & from, const int3 & to);
|
||||
void dumpToLog(std::string visualKey) const;
|
||||
|
||||
bool isVirtualBoat(const int3 & tile) const
|
||||
{
|
||||
return vstd::contains(virtualBoats, tile);
|
||||
}
|
||||
|
||||
void copyFrom(const ObjectGraph & other)
|
||||
{
|
||||
nodes = other.nodes;
|
||||
virtualBoats = other.virtualBoats;
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
|
@ -17,8 +17,12 @@ namespace NKAI
|
||||
{
|
||||
namespace AIPathfinding
|
||||
{
|
||||
AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, Nullkiller * ai, std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
:cb(cb), ai(ai), nodeStorage(nodeStorage)
|
||||
AILayerTransitionRule::AILayerTransitionRule(
|
||||
CPlayerSpecificInfoCallback * cb,
|
||||
Nullkiller * ai,
|
||||
std::shared_ptr<AINodeStorage> nodeStorage,
|
||||
bool allowEmbark)
|
||||
:cb(cb), ai(ai), nodeStorage(nodeStorage), allowEmbark(allowEmbark)
|
||||
{
|
||||
setup();
|
||||
}
|
||||
|
@ -32,9 +32,14 @@ namespace AIPathfinding
|
||||
std::map<const CGHeroInstance *, std::shared_ptr<const SummonBoatAction>> summonableVirtualBoats;
|
||||
std::map<const CGHeroInstance *, std::shared_ptr<const WaterWalkingAction>> waterWalkingActions;
|
||||
std::map<const CGHeroInstance *, std::shared_ptr<const AirWalkingAction>> airWalkingActions;
|
||||
bool allowEmbark;
|
||||
|
||||
public:
|
||||
AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, Nullkiller * ai, std::shared_ptr<AINodeStorage> nodeStorage);
|
||||
AILayerTransitionRule(
|
||||
CPlayerSpecificInfoCallback * cb,
|
||||
Nullkiller * ai,
|
||||
std::shared_ptr<AINodeStorage> nodeStorage,
|
||||
bool allowEmbark);
|
||||
|
||||
virtual void process(
|
||||
const PathNodeInfo & source,
|
||||
|
@ -41,6 +41,16 @@ namespace AIPathfinding
|
||||
return;
|
||||
}
|
||||
|
||||
if(!allowBypassObjects
|
||||
&& destination.action == EPathNodeAction::EMBARK
|
||||
&& source.node->layer == EPathfindingLayer::LAND
|
||||
&& destination.node->layer == EPathfindingLayer::SAIL)
|
||||
{
|
||||
destination.blocked = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
|
||||
|
||||
if(blocker == BlockingReason::NONE)
|
||||
|
@ -396,7 +396,11 @@ void ApplyClientNetPackVisitor::visitPlayerEndsGame(PlayerEndsGame & pack)
|
||||
|
||||
// In auto testing pack.mode we always close client if red pack.player won or lose
|
||||
if(!settings["session"]["testmap"].isNull() && pack.player == PlayerColor(0))
|
||||
{
|
||||
logAi->info("Red player %s. Ending game.", pack.victoryLossCheckResult.victory() ? "won" : "lost");
|
||||
|
||||
handleQuit(settings["session"]["spectate"].Bool()); // if spectator is active ask to close client or not
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyClientNetPackVisitor::visitPlayerReinitInterface(PlayerReinitInterface & pack)
|
||||
|
Loading…
Reference in New Issue
Block a user