mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-23 00:28:08 +02:00
NKAI: improve boat handling by object graph, a set of fixes
This commit is contained in:
@ -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)
|
||||
|
Reference in New Issue
Block a user