mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-15 20:03:15 +02:00
hero chain stabilisation
This commit is contained in:
committed by
Andrii Danylchenko
parent
87f1079c60
commit
774f531c4e
@@ -132,7 +132,8 @@ std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
|
|||||||
initialNode->turns = actor->initialTurn;
|
initialNode->turns = actor->initialTurn;
|
||||||
initialNode->moveRemains = actor->initialMovement;
|
initialNode->moveRemains = actor->initialMovement;
|
||||||
initialNode->danger = 0;
|
initialNode->danger = 0;
|
||||||
initialNode->cost = 0.0;
|
initialNode->cost = actor->initialTurn;
|
||||||
|
initialNode->action = CGPathNode::ENodeAction::NORMAL;
|
||||||
|
|
||||||
if(actor->isMovable)
|
if(actor->isMovable)
|
||||||
{
|
{
|
||||||
@@ -175,6 +176,16 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
|
|||||||
{
|
{
|
||||||
dstNode->specialAction->applyOnDestination(dstNode->actor->hero, destination, source, dstNode, srcNode);
|
dstNode->specialAction->applyOnDestination(dstNode->actor->hero, destination, source, dstNode, srcNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||||
|
logAi->trace(
|
||||||
|
"Commited %s -> %s, cost: %f, hero: %s, mask: %i",
|
||||||
|
source.coord.toString(),
|
||||||
|
destination.coord.toString(),
|
||||||
|
destination.cost,
|
||||||
|
dstNode->actor->hero->name,
|
||||||
|
dstNode->actor->chainMask);
|
||||||
|
#endif
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +205,7 @@ void AINodeStorage::commit(
|
|||||||
destination->manaCost = source->manaCost;
|
destination->manaCost = source->manaCost;
|
||||||
destination->danger = source->danger;
|
destination->danger = source->danger;
|
||||||
destination->theNodeBefore = source->theNodeBefore;
|
destination->theNodeBefore = source->theNodeBefore;
|
||||||
|
destination->chainOther = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
||||||
@@ -227,33 +239,53 @@ bool AINodeStorage::calculateHeroChain()
|
|||||||
heroChainPass = true;
|
heroChainPass = true;
|
||||||
heroChain.resize(0);
|
heroChain.resize(0);
|
||||||
|
|
||||||
|
std::vector<AIPathNode *> buffer;
|
||||||
|
|
||||||
|
buffer.reserve(NUM_CHAINS);
|
||||||
|
|
||||||
foreach_tile_pos([&](const int3 & pos) {
|
foreach_tile_pos([&](const int3 & pos) {
|
||||||
auto layer = EPathfindingLayer::LAND;
|
auto layer = EPathfindingLayer::LAND;
|
||||||
auto chains = nodes[pos.x][pos.y][pos.z][layer];
|
auto chains = nodes[pos.x][pos.y][pos.z][layer];
|
||||||
|
|
||||||
|
buffer.resize(0);
|
||||||
|
|
||||||
for(AIPathNode & node : chains)
|
for(AIPathNode & node : chains)
|
||||||
{
|
{
|
||||||
if(node.locked && node.turns < 1)
|
if(node.turns <= heroChainMaxTurns && node.action != CGPathNode::ENodeAction::UNKNOWN)
|
||||||
addHeroChain(&node);
|
buffer.push_back(&node);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(AIPathNode * node : buffer)
|
||||||
|
{
|
||||||
|
addHeroChain(node, buffer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return heroChain.size();
|
return heroChain.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AINodeStorage::addHeroChain(AIPathNode * srcNode)
|
void AINodeStorage::addHeroChain(AIPathNode * srcNode, std::vector<AIPathNode *> variants)
|
||||||
{
|
{
|
||||||
auto chains = nodes[srcNode->coord.x][srcNode->coord.y][srcNode->coord.z][srcNode->layer];
|
for(AIPathNode * node : variants)
|
||||||
|
|
||||||
for(AIPathNode & node : chains)
|
|
||||||
{
|
{
|
||||||
if(!node.locked || !node.actor || node.action == CGPathNode::ENodeAction::UNKNOWN && node.actor->hero)
|
if(node == srcNode || !node->actor || node->turns > heroChainMaxTurns
|
||||||
|
|| node->action == CGPathNode::ENodeAction::UNKNOWN && node->actor->hero)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
addHeroChain(srcNode, &node);
|
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||||
addHeroChain(&node, srcNode);
|
logAi->trace(
|
||||||
|
"Thy exchange %s[%i] -> %s[%i] at %s",
|
||||||
|
node->actor->hero->name,
|
||||||
|
node->actor->chainMask,
|
||||||
|
srcNode->actor->hero->name,
|
||||||
|
srcNode->actor->chainMask,
|
||||||
|
srcNode->coord.toString());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
addHeroChain(srcNode, node);
|
||||||
|
//addHeroChain(&node, srcNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,37 +293,65 @@ void AINodeStorage::addHeroChain(AIPathNode * carrier, AIPathNode * other)
|
|||||||
{
|
{
|
||||||
if(carrier->actor->canExchange(other->actor))
|
if(carrier->actor->canExchange(other->actor))
|
||||||
{
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||||
|
logAi->trace(
|
||||||
|
"Exchange allowed %s[%i] -> %s[%i] at %s",
|
||||||
|
other->actor->hero->name,
|
||||||
|
other->actor->chainMask,
|
||||||
|
carrier->actor->hero->name,
|
||||||
|
carrier->actor->chainMask,
|
||||||
|
carrier->coord.toString());
|
||||||
|
#endif
|
||||||
|
|
||||||
bool hasLessMp = carrier->turns > other->turns || carrier->moveRemains < other->moveRemains;
|
bool hasLessMp = carrier->turns > other->turns || carrier->moveRemains < other->moveRemains;
|
||||||
bool hasLessExperience = carrier->actor->hero->exp < other->actor->hero->exp;
|
bool hasLessExperience = carrier->actor->hero->exp < other->actor->hero->exp;
|
||||||
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
|
||||||
logAi->trace("Check hero exhange at %s, %s -> %s", carrier->coord.toString(), other->actor->hero->name, carrier->actor->hero->name);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(hasLessMp && hasLessExperience)
|
if(hasLessMp && hasLessExperience)
|
||||||
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||||
|
logAi->trace("Exchange at %s is ineficient. Blocked.", carrier->coord.toString());
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto newActor = carrier->actor->exchange(other->actor);
|
auto newActor = carrier->actor->exchange(other->actor);
|
||||||
auto chainNodeOptional = getOrCreateNode(carrier->coord, carrier->layer, newActor);
|
auto chainNodeOptional = getOrCreateNode(carrier->coord, carrier->layer, newActor);
|
||||||
|
|
||||||
if(!chainNodeOptional)
|
if(!chainNodeOptional)
|
||||||
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||||
|
logAi->trace("Exchange at %s can not allocate node. Blocked.", carrier->coord.toString());
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto chainNode = chainNodeOptional.get();
|
auto chainNode = chainNodeOptional.get();
|
||||||
|
|
||||||
if(chainNode->locked)
|
if(chainNode->action != CGPathNode::ENodeAction::UNKNOWN)
|
||||||
return;
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
logAi->trace("Exchange at %s node is already in use. Blocked.", carrier->coord.toString());
|
||||||
logAi->trace("Hero exhange at %s, %s -> %s", carrier->coord.toString(), other->actor->hero->name, carrier->actor->hero->name);
|
|
||||||
#endif
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
commitExchange(chainNode, carrier, other);
|
if(commitExchange(chainNode, carrier, other))
|
||||||
heroChain.push_back(chainNode);
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||||
|
logAi->trace(
|
||||||
|
"Chain accepted at %s %s -> %s, mask %i, cost %f",
|
||||||
|
chainNode->coord.toString(),
|
||||||
|
other->actor->hero->name,
|
||||||
|
chainNode->actor->hero->name,
|
||||||
|
chainNode->actor->chainMask,
|
||||||
|
chainNode->cost);
|
||||||
|
#endif
|
||||||
|
heroChain.push_back(chainNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AINodeStorage::commitExchange(
|
bool AINodeStorage::commitExchange(
|
||||||
AIPathNode * exchangeNode,
|
AIPathNode * exchangeNode,
|
||||||
AIPathNode * carrierParentNode,
|
AIPathNode * carrierParentNode,
|
||||||
AIPathNode * otherParentNode) const
|
AIPathNode * otherParentNode) const
|
||||||
@@ -302,7 +362,7 @@ void AINodeStorage::commitExchange(
|
|||||||
|
|
||||||
auto armyLoss = carrierParentNode->armyLoss + otherParentNode->armyLoss;
|
auto armyLoss = carrierParentNode->armyLoss + otherParentNode->armyLoss;
|
||||||
auto turns = carrierParentNode->turns;
|
auto turns = carrierParentNode->turns;
|
||||||
auto cost = carrierParentNode->cost;
|
auto cost = carrierParentNode->cost + otherParentNode->cost / 1000.0;
|
||||||
auto movementLeft = carrierParentNode->moveRemains;
|
auto movementLeft = carrierParentNode->moveRemains;
|
||||||
|
|
||||||
if(carrierParentNode->turns < otherParentNode->turns)
|
if(carrierParentNode->turns < otherParentNode->turns)
|
||||||
@@ -312,27 +372,24 @@ void AINodeStorage::commitExchange(
|
|||||||
+ carrierParentNode->moveRemains / (float)moveRemains;
|
+ carrierParentNode->moveRemains / (float)moveRemains;
|
||||||
|
|
||||||
turns = otherParentNode->turns;
|
turns = otherParentNode->turns;
|
||||||
cost = waitingCost;
|
cost += waitingCost;
|
||||||
movementLeft = moveRemains;
|
movementLeft = moveRemains;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(exchangeNode->turns != 0xFF && exchangeNode->cost < cost)
|
if(exchangeNode->turns != 0xFF && exchangeNode->cost < cost)
|
||||||
return;
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
logAi->trace("Exchange at %s is is not effective enough. %f < %f", exchangeNode->coord.toString(), exchangeNode->cost, cost);
|
||||||
logAi->trace(
|
|
||||||
"Accepted hero exhange at %s, carrier %s, mp cost %f",
|
|
||||||
exchangeNode->coord.toString(),
|
|
||||||
carrierActor->hero->name,
|
|
||||||
cost);
|
|
||||||
#endif
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
commit(exchangeNode, carrierParentNode, carrierParentNode->action, turns, movementLeft, cost);
|
commit(exchangeNode, carrierParentNode, carrierParentNode->action, turns, movementLeft, cost);
|
||||||
|
|
||||||
exchangeNode->theNodeBefore = carrierParentNode;
|
|
||||||
exchangeNode->chainOther = otherParentNode;
|
exchangeNode->chainOther = otherParentNode;
|
||||||
exchangeNode->armyLoss = armyLoss;
|
exchangeNode->armyLoss = armyLoss;
|
||||||
exchangeNode->manaCost = carrierParentNode->manaCost;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CGHeroInstance * AINodeStorage::getHero(const CGPathNode * node) const
|
const CGHeroInstance * AINodeStorage::getHero(const CGPathNode * node) const
|
||||||
@@ -538,6 +595,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
|||||||
path.heroArmy = node.actor->creatureSet;
|
path.heroArmy = node.actor->creatureSet;
|
||||||
path.armyLoss = node.armyLoss;
|
path.armyLoss = node.armyLoss;
|
||||||
path.targetObjectDanger = evaluateDanger(pos, path.targetHero);
|
path.targetObjectDanger = evaluateDanger(pos, path.targetHero);
|
||||||
|
path.chainMask = node.actor->chainMask;
|
||||||
|
|
||||||
fillChainInfo(&node, path);
|
fillChainInfo(&node, path);
|
||||||
|
|
||||||
@@ -551,22 +609,26 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path) const
|
|||||||
{
|
{
|
||||||
while(node != nullptr)
|
while(node != nullptr)
|
||||||
{
|
{
|
||||||
if(!node->actor->hero || node->coord == node->actor->hero->visitablePos())
|
if(!node->actor->hero)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AIPathNodeInfo pathNode;
|
|
||||||
pathNode.cost = node->cost;
|
|
||||||
pathNode.targetHero = node->actor->hero;
|
|
||||||
pathNode.turns = node->turns;
|
|
||||||
pathNode.danger = node->danger;
|
|
||||||
pathNode.coord = node->coord;
|
|
||||||
|
|
||||||
path.nodes.push_back(pathNode);
|
|
||||||
path.specialAction = node->specialAction;
|
|
||||||
|
|
||||||
if(node->chainOther)
|
if(node->chainOther)
|
||||||
fillChainInfo(node->chainOther, path);
|
fillChainInfo(node->chainOther, path);
|
||||||
|
|
||||||
|
if(node->actor->hero->visitablePos() != node->coord)
|
||||||
|
{
|
||||||
|
AIPathNodeInfo pathNode;
|
||||||
|
pathNode.cost = node->cost;
|
||||||
|
pathNode.targetHero = node->actor->hero;
|
||||||
|
pathNode.turns = node->turns;
|
||||||
|
pathNode.danger = node->danger;
|
||||||
|
pathNode.coord = node->coord;
|
||||||
|
|
||||||
|
path.nodes.push_back(pathNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.specialAction = node->specialAction;
|
||||||
|
|
||||||
node = getAINode(node->theNodeBefore);
|
node = getAINode(node->theNodeBefore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -586,6 +648,11 @@ int3 AIPath::firstTileToGet() const
|
|||||||
return int3(-1, -1, -1);
|
return int3(-1, -1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AIPathNodeInfo & AIPath::firstNode() const
|
||||||
|
{
|
||||||
|
return nodes.back();
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t AIPath::getPathDanger() const
|
uint64_t AIPath::getPathDanger() const
|
||||||
{
|
{
|
||||||
if(nodes.size())
|
if(nodes.size())
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VCMI_TRACE_PATHFINDER
|
#define VCMI_TRACE_PATHFINDER
|
||||||
|
#define VCMI_TRACE_PATHFINDER_EX
|
||||||
|
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
#include "../Goals/AbstractGoal.h"
|
#include "../Goals/AbstractGoal.h"
|
||||||
#include "Actions/ISpecialAction.h"
|
#include "Actions/ISpecialAction.h"
|
||||||
#include "Actors.h"
|
#include "Actors.h"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
#define VCMI_TRACE_PATHFINDER
|
#define VCMI_TRACE_PATHFINDER
|
||||||
|
|
||||||
@@ -49,6 +51,7 @@ struct AIPath
|
|||||||
uint64_t armyLoss;
|
uint64_t armyLoss;
|
||||||
const CGHeroInstance * targetHero;
|
const CGHeroInstance * targetHero;
|
||||||
const CCreatureSet * heroArmy;
|
const CCreatureSet * heroArmy;
|
||||||
|
uint64_t chainMask;
|
||||||
|
|
||||||
AIPath();
|
AIPath();
|
||||||
|
|
||||||
@@ -60,6 +63,8 @@ struct AIPath
|
|||||||
|
|
||||||
int3 firstTileToGet() const;
|
int3 firstTileToGet() const;
|
||||||
|
|
||||||
|
const AIPathNodeInfo & firstNode() const;
|
||||||
|
|
||||||
float movementCost() const;
|
float movementCost() const;
|
||||||
|
|
||||||
uint64_t getHeroStrength() const;
|
uint64_t getHeroStrength() const;
|
||||||
@@ -78,6 +83,7 @@ private:
|
|||||||
std::vector<std::shared_ptr<ChainActor>> actors;
|
std::vector<std::shared_ptr<ChainActor>> actors;
|
||||||
std::vector<CGPathNode *> heroChain;
|
std::vector<CGPathNode *> heroChain;
|
||||||
bool heroChainPass; // true if we need to calculate hero chain
|
bool heroChainPass; // true if we need to calculate hero chain
|
||||||
|
int heroChainMaxTurns = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one.
|
/// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one.
|
||||||
@@ -123,7 +129,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
STRONG_INLINE
|
STRONG_INLINE
|
||||||
void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);
|
void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);
|
||||||
void addHeroChain(AIPathNode * srcNode);
|
void addHeroChain(AIPathNode * srcNode, std::vector<AIPathNode *> variants);
|
||||||
void addHeroChain(AIPathNode * carrier, AIPathNode * other);
|
void addHeroChain(AIPathNode * carrier, AIPathNode * other);
|
||||||
void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector<CGPathNode *> & neighbours);
|
void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector<CGPathNode *> & neighbours);
|
||||||
void fillChainInfo(const AIPathNode * node, AIPath & path) const;
|
void fillChainInfo(const AIPathNode * node, AIPath & path) const;
|
||||||
@@ -135,7 +141,7 @@ private:
|
|||||||
int movementLeft,
|
int movementLeft,
|
||||||
float cost) const;
|
float cost) const;
|
||||||
|
|
||||||
void AINodeStorage::commitExchange(
|
bool AINodeStorage::commitExchange(
|
||||||
AIPathNode * exchangeNode,
|
AIPathNode * exchangeNode,
|
||||||
AIPathNode * carrierParentNode,
|
AIPathNode * carrierParentNode,
|
||||||
AIPathNode * otherParentNode) const;
|
AIPathNode * otherParentNode) const;
|
||||||
|
@@ -51,18 +51,20 @@ void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
|
|||||||
}
|
}
|
||||||
|
|
||||||
logAi->debug("Recalculate all paths");
|
logAi->debug("Recalculate all paths");
|
||||||
|
int pass = 0;
|
||||||
|
|
||||||
storage->clear();
|
storage->clear();
|
||||||
storage->setHeroes(heroes, ai);
|
storage->setHeroes(heroes, ai);
|
||||||
|
|
||||||
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage);
|
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage);
|
||||||
cb->calculatePaths(config);
|
|
||||||
|
|
||||||
while(useHeroChain && storage->calculateHeroChain())
|
do {
|
||||||
{
|
logAi->trace("Recalculate paths pass %" PRIi32, pass++);
|
||||||
config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage);
|
|
||||||
cb->calculatePaths(config);
|
cb->calculatePaths(config);
|
||||||
}
|
|
||||||
|
logAi->trace("Recalculate chain pass %" PRIi32, pass);
|
||||||
|
useHeroChain = useHeroChain && storage->calculateHeroChain();
|
||||||
|
} while(useHeroChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIPathfinder::updatePaths(const HeroPtr & hero)
|
void AIPathfinder::updatePaths(const HeroPtr & hero)
|
||||||
|
@@ -13,7 +13,6 @@
|
|||||||
#include "Rules/AIMovementAfterDestinationRule.h"
|
#include "Rules/AIMovementAfterDestinationRule.h"
|
||||||
#include "Rules/AIMovementToDestinationRule.h"
|
#include "Rules/AIMovementToDestinationRule.h"
|
||||||
#include "Rules/AIPreviousNodeRule.h"
|
#include "Rules/AIPreviousNodeRule.h"
|
||||||
#include "Rules/AIMovementCostRule.h"
|
|
||||||
|
|
||||||
namespace AIPathfinding
|
namespace AIPathfinding
|
||||||
{
|
{
|
||||||
|
@@ -82,21 +82,16 @@ void ChainActor::setBaseActor(HeroActor * base)
|
|||||||
armyValue = base->armyValue;
|
armyValue = base->armyValue;
|
||||||
chainMask = base->chainMask;
|
chainMask = base->chainMask;
|
||||||
creatureSet = base->creatureSet;
|
creatureSet = base->creatureSet;
|
||||||
|
isMovable = base->isMovable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeroActor::setupSpecialActors()
|
void HeroActor::setupSpecialActors()
|
||||||
{
|
{
|
||||||
auto allActors = std::vector<ChainActor *>{ this };
|
auto allActors = std::vector<ChainActor *>{ this };
|
||||||
|
|
||||||
for(int i = 1; i <= SPECIAL_ACTORS_COUNT; i++)
|
for(ChainActor & specialActor : specialActors)
|
||||||
{
|
{
|
||||||
ChainActor & specialActor = specialActors[i - 1];
|
|
||||||
|
|
||||||
specialActor.setBaseActor(this);
|
specialActor.setBaseActor(this);
|
||||||
specialActor.allowBattle = (i & 1) > 0;
|
|
||||||
specialActor.allowSpellCast = (i & 2) > 0;
|
|
||||||
specialActor.allowUseResources = (i & 4) > 0;
|
|
||||||
|
|
||||||
allActors.push_back(&specialActor);
|
allActors.push_back(&specialActor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +99,9 @@ void HeroActor::setupSpecialActors()
|
|||||||
{
|
{
|
||||||
ChainActor * actor = allActors[i];
|
ChainActor * actor = allActors[i];
|
||||||
|
|
||||||
|
actor->allowBattle = (i & 1) > 0;
|
||||||
|
actor->allowSpellCast = (i & 2) > 0;
|
||||||
|
actor->allowUseResources = (i & 4) > 0;
|
||||||
actor->battleActor = allActors[i | 1];
|
actor->battleActor = allActors[i | 1];
|
||||||
actor->castActor = allActors[i | 2];
|
actor->castActor = allActors[i | 2];
|
||||||
actor->resourceActor = allActors[i | 4];
|
actor->resourceActor = allActors[i | 4];
|
||||||
@@ -180,7 +178,7 @@ CCreatureSet * HeroActor::pickBestCreatures(const CCreatureSet * army1, const CC
|
|||||||
{
|
{
|
||||||
for(auto & i : armyPtr->Slots())
|
for(auto & i : armyPtr->Slots())
|
||||||
{
|
{
|
||||||
creToPower[i.second->type] += i.second->getPower();
|
creToPower[i.second->type] += i.second->count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc)
|
//TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc)
|
||||||
@@ -193,7 +191,7 @@ CCreatureSet * HeroActor::pickBestCreatures(const CCreatureSet * army1, const CC
|
|||||||
typedef const std::pair<const CCreature *, int> & CrePowerPair;
|
typedef const std::pair<const CCreature *, int> & CrePowerPair;
|
||||||
auto creIt = boost::max_element(creToPower, [](CrePowerPair lhs, CrePowerPair rhs)
|
auto creIt = boost::max_element(creToPower, [](CrePowerPair lhs, CrePowerPair rhs)
|
||||||
{
|
{
|
||||||
return lhs.second < rhs.second;
|
return lhs.first->AIValue * lhs.second < rhs.first->AIValue * rhs.second;
|
||||||
});
|
});
|
||||||
|
|
||||||
target->addToSlot(SlotID(i), creIt->first->idNumber, TQuantity(creIt->second));
|
target->addToSlot(SlotID(i), creIt->first->idNumber, TQuantity(creIt->second));
|
||||||
|
@@ -125,14 +125,23 @@ Goals::TGoalVec PathfindingManager::findPaths(
|
|||||||
|
|
||||||
for(auto path : chainInfo)
|
for(auto path : chainInfo)
|
||||||
{
|
{
|
||||||
if(hero && hero.get() != path.targetHero)
|
if(hero && hero.get() != path.targetHero || path.nodes.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int3 firstTileToGet = path.firstTileToGet();
|
const AIPathNodeInfo & firstNode = path.firstNode();
|
||||||
|
int3 firstTileToGet = firstNode.coord;
|
||||||
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
logAi->trace("Path found size=%i, first tile=%s", path.nodes.size(), firstTileToGet.toString());
|
std::stringstream str;
|
||||||
|
|
||||||
|
str << "Path found ";
|
||||||
|
|
||||||
|
for(auto node : path.nodes)
|
||||||
|
str << node.targetHero->name << "->" << node.coord.toString() << "; ";
|
||||||
|
|
||||||
|
logAi->trace(str.str());
|
||||||
#endif
|
#endif
|
||||||
if(firstTileToGet.valid() && ai->isTileNotReserved(hero.get(), firstTileToGet))
|
if(ai->isTileNotReserved(hero.get(), firstTileToGet))
|
||||||
{
|
{
|
||||||
danger = path.getTotalDanger(hero);
|
danger = path.getTotalDanger(hero);
|
||||||
|
|
||||||
@@ -148,7 +157,7 @@ Goals::TGoalVec PathfindingManager::findPaths(
|
|||||||
{
|
{
|
||||||
solution = dest == firstTileToGet
|
solution = dest == firstTileToGet
|
||||||
? doVisitTile(firstTileToGet)
|
? doVisitTile(firstTileToGet)
|
||||||
: clearWayTo(hero, firstTileToGet);
|
: clearWayTo(firstNode.targetHero, firstTileToGet);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(solution->invalid())
|
if(solution->invalid())
|
||||||
@@ -161,7 +170,13 @@ Goals::TGoalVec PathfindingManager::findPaths(
|
|||||||
solution->evaluationContext.armyLoss += path.armyLoss;
|
solution->evaluationContext.armyLoss += path.armyLoss;
|
||||||
solution->evaluationContext.heroStrength = path.getHeroStrength();
|
solution->evaluationContext.heroStrength = path.getHeroStrength();
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
logAi->trace("It's safe for %s to visit tile %s with danger %s, goal %s", hero->name, dest.toString(), std::to_string(danger), solution->name());
|
logAi->trace("It's safe for %s to visit tile %s with danger %s, loss %s, army strength %s, goal %s",
|
||||||
|
hero->name,
|
||||||
|
dest.toString(),
|
||||||
|
std::to_string(danger),
|
||||||
|
std::to_string(path.armyLoss),
|
||||||
|
std::to_string(path.heroArmy->getArmyStrength()),
|
||||||
|
solution->name());
|
||||||
#endif
|
#endif
|
||||||
result.push_back(solution);
|
result.push_back(solution);
|
||||||
|
|
||||||
@@ -210,9 +225,7 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
|
|||||||
{
|
{
|
||||||
if(topObj != hero.get(true)) //the hero we want to free
|
if(topObj != hero.get(true)) //the hero we want to free
|
||||||
{
|
{
|
||||||
logAi->error("%s stands in the way of %s", topObj->getObjectName(), hero->getObjectName());
|
logAi->warn("%s stands in the way of %s", topObj->getObjectName(), hero->getObjectName());
|
||||||
|
|
||||||
return sptr(Goals::Invalid());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,10 +253,10 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
|
|||||||
return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
|
return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes)
|
void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
|
||||||
{
|
{
|
||||||
logAi->debug("AIPathfinder has been reseted.");
|
logAi->debug("AIPathfinder has been reseted.");
|
||||||
pathfinder->updatePaths(heroes);
|
pathfinder->updatePaths(heroes, useHeroChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PathfindingManager::updatePaths(const HeroPtr & hero)
|
void PathfindingManager::updatePaths(const HeroPtr & hero)
|
||||||
|
@@ -20,7 +20,7 @@ public:
|
|||||||
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
|
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
|
||||||
virtual void setAI(VCAI * AI) = 0;
|
virtual void setAI(VCAI * AI) = 0;
|
||||||
|
|
||||||
virtual void updatePaths(std::vector<HeroPtr> heroes) = 0;
|
virtual void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) = 0;
|
||||||
virtual void updatePaths(const HeroPtr & hero) = 0;
|
virtual void updatePaths(const HeroPtr & hero) = 0;
|
||||||
virtual Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const = 0;
|
virtual Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const = 0;
|
||||||
virtual Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const = 0;
|
virtual Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const = 0;
|
||||||
@@ -47,7 +47,7 @@ public:
|
|||||||
Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const override;
|
Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const override;
|
||||||
Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const override;
|
Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const override;
|
||||||
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
|
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
|
||||||
void updatePaths(std::vector<HeroPtr> heroes) override;
|
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
|
||||||
void updatePaths(const HeroPtr & hero) override;
|
void updatePaths(const HeroPtr & hero) override;
|
||||||
|
|
||||||
STRONG_INLINE
|
STRONG_INLINE
|
||||||
|
@@ -38,39 +38,10 @@ namespace AIPathfinding
|
|||||||
|
|
||||||
auto srcNode = nodeStorage->getAINode(source.node);
|
auto srcNode = nodeStorage->getAINode(source.node);
|
||||||
|
|
||||||
if(srcNode->specialAction)
|
if(srcNode->specialAction || srcNode->chainOther)
|
||||||
{
|
{
|
||||||
// there is some action on source tile which should be performed before we can bypass it
|
// there is some action on source tile which should be performed before we can bypass it
|
||||||
destination.node->theNodeBefore = source.node;
|
destination.node->theNodeBefore = source.node;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dstNode = nodeStorage->getAINode(destination.node);
|
|
||||||
auto srcActor = srcNode->actor;
|
|
||||||
auto dstActor = dstNode->actor;
|
|
||||||
|
|
||||||
if(srcActor == dstActor)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto carrierActor = dstActor->carrierParent;
|
|
||||||
auto otherActor = dstActor->otherParent;
|
|
||||||
|
|
||||||
nodeStorage->updateAINode(destination.node, [&](AIPathNode * dstNode) {
|
|
||||||
if(source.coord == destination.coord)
|
|
||||||
{
|
|
||||||
auto carrierNode = nodeStorage->getOrCreateNode(source.coord, source.node->layer, carrierActor).get();
|
|
||||||
auto otherNode = nodeStorage->getOrCreateNode(source.coord, source.node->layer, otherActor).get();
|
|
||||||
|
|
||||||
if(destination.coord != carrierNode->coord)
|
|
||||||
dstNode->theNodeBefore = carrierNode;
|
|
||||||
|
|
||||||
dstNode->chainOther = otherNode;
|
|
||||||
|
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
|
||||||
logAi->trace("Link Hero exhange at %s, %s -> %s", dstNode->coord.toString(), otherNode->actor->hero->name, carrierNode->actor->hero->name);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user