1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

AI: extract pathfinding special actions and rules to separate files

This commit is contained in:
Andrii Danylchenko 2019-02-06 14:51:12 +02:00
parent 0ccdd31586
commit 5454848938
21 changed files with 893 additions and 515 deletions

View File

@ -12,6 +12,13 @@ set(VCAI_SRCS
Pathfinding/AIPathfinder.cpp
Pathfinding/AINodeStorage.cpp
Pathfinding/PathfindingManager.cpp
Pathfinding/Actions/BattleAction.cpp
Pathfinding/Actions/BoatActions.cpp
Pathfinding/Actions/TownPortalAction.cpp
Pathfinding/Rules/AILayerTransitionRule.cpp
Pathfinding/Rules/AIMovementAfterDestinationRule.cpp
Pathfinding/Rules/AIMovementToDestinationRule.cpp
Pathfinding/Rules/AIPreviousNodeRule.cpp
AIUtility.cpp
AIhelper.cpp
ResourceManager.cpp
@ -54,6 +61,14 @@ set(VCAI_HEADERS
Pathfinding/AIPathfinder.h
Pathfinding/AINodeStorage.h
Pathfinding/PathfindingManager.h
Pathfinding/Actions/ISpecialAction.h
Pathfinding/Actions/BattleAction.h
Pathfinding/Actions/BoatActions.h
Pathfinding/Actions/TownPortalAction.h
Pathfinding/Rules/AILayerTransitionRule.h
Pathfinding/Rules/AIMovementAfterDestinationRule.h
Pathfinding/Rules/AIMovementToDestinationRule.h
Pathfinding/Rules/AIPreviousNodeRule.h
AIUtility.h
AIhelper.h
ResourceManager.h

View File

@ -9,13 +9,14 @@
*/
#include "StdInc.h"
#include "AINodeStorage.h"
#include "Actions/TownPortalAction.h"
#include "../Goals/Goals.h"
#include "../../../CCallback.h"
#include "../../../lib/mapping/CMap.h"
#include "../../../lib/mapObjects/MapObjects.h"
#include "../../../lib/PathfinderUtil.h"
#include "../../../lib/CPlayerState.h"
extern boost::thread_specific_ptr<CCallback> cb;
@ -191,26 +192,6 @@ void AINodeStorage::setHero(HeroPtr heroPtr)
hero = heroPtr.get();
}
class TownPortalAction : public ISpecialAction
{
private:
const CGTownInstance * target;
const HeroPtr hero;
public:
TownPortalAction(const CGTownInstance * target)
:target(target)
{
}
virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override
{
const CGTownInstance * targetTown = target; // const pointer is not allowed in settown
return sptr(Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL).settown(targetTown).settile(targetTown->visitablePos()));
}
};
std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
const PathNodeInfo & source,
const PathfinderConfig * pathfinderConfig,
@ -299,7 +280,7 @@ void AINodeStorage::calculateTownPortalTeleportations(
AIPathNode * node = nodeOptional.get();
node->theNodeBefore = source.node;
node->specialAction.reset(new TownPortalAction(targetTown));
node->specialAction.reset(new AIPathfinding::TownPortalAction(targetTown));
node->moveRemains = source.node->moveRemains;
neighbours.push_back(node);

View File

@ -14,24 +14,10 @@
#include "../../../lib/mapObjects/CGHeroInstance.h"
#include "../AIUtility.h"
#include "../Goals/AbstractGoal.h"
#include "Actions/ISpecialAction.h"
struct AIPathNode;
class ISpecialAction
{
public:
virtual Goals::TSubgoal whatToDo(HeroPtr hero) const = 0;
virtual void applyOnDestination(
HeroPtr hero,
CDestinationNodeInfo & destination,
const PathNodeInfo & source,
AIPathNode * dstMode,
const AIPathNode * srcNode) const
{
}
};
struct AIPathNode : public CGPathNode
{
uint32_t chainMask;

View File

@ -9,487 +9,13 @@
*/
#include "StdInc.h"
#include "AIPathfinderConfig.h"
#include "../Goals/Goals.h"
#include "../../../CCallback.h"
#include "../../../lib/mapping/CMap.h"
#include "../../../lib/mapObjects/MapObjects.h"
#include "Rules/AILayerTransitionRule.h"
#include "Rules/AIMovementAfterDestinationRule.h"
#include "Rules/AIMovementToDestinationRule.h"
#include "Rules/AIPreviousNodeRule.h"
namespace AIPathfinding
{
class VirtualBoatAction : public ISpecialAction
{
private:
uint64_t specialChain;
public:
VirtualBoatAction(uint64_t specialChain)
:specialChain(specialChain)
{
}
uint64_t getSpecialChain() const
{
return specialChain;
}
};
class BuildBoatAction : public VirtualBoatAction
{
private:
const IShipyard * shipyard;
public:
BuildBoatAction(const IShipyard * shipyard)
:VirtualBoatAction(AINodeStorage::RESOURCE_CHAIN), shipyard(shipyard)
{
}
virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override
{
return sptr(Goals::BuildBoat(shipyard));
}
};
class SummonBoatAction : public VirtualBoatAction
{
public:
SummonBoatAction()
:VirtualBoatAction(AINodeStorage::CAST_CHAIN)
{
}
virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override
{
return sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
}
virtual void applyOnDestination(
HeroPtr hero,
CDestinationNodeInfo & destination,
const PathNodeInfo & source,
AIPathNode * dstMode,
const AIPathNode * srcNode) const override
{
dstMode->manaCost = srcNode->manaCost + getManaCost(hero);
dstMode->theNodeBefore = source.node;
}
bool isAffordableBy(HeroPtr hero, const AIPathNode * source) const
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Hero %s has %d mana and needed %d and already spent %d",
hero->name,
hero->mana,
getManaCost(hero),
source->manaCost);
#endif
return hero->mana >= source->manaCost + getManaCost(hero);
}
private:
uint32_t getManaCost(HeroPtr hero) const
{
SpellID summonBoat = SpellID::SUMMON_BOAT;
return hero->getSpellCost(summonBoat.toSpell());
}
};
class BattleAction : public ISpecialAction
{
private:
const int3 target;
const HeroPtr hero;
public:
BattleAction(const int3 target)
:target(target)
{
}
virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override
{
return sptr(Goals::VisitTile(target).sethero(hero));
}
};
class AILayerTransitionRule : public LayerTransitionRule
{
private:
CPlayerSpecificInfoCallback * cb;
VCAI * ai;
std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
std::shared_ptr<AINodeStorage> nodeStorage;
std::shared_ptr<const SummonBoatAction> summonableVirtualBoat;
public:
AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
:cb(cb), ai(ai), nodeStorage(nodeStorage)
{
setup();
}
virtual void process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const override
{
LayerTransitionRule::process(source, destination, pathfinderConfig, pathfinderHelper);
if(!destination.blocked)
{
return;
}
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL)
{
std::shared_ptr<const VirtualBoatAction> virtualBoat = findVirtualBoat(destination, source);
if(virtualBoat && tryEmbarkVirtualBoat(destination, source, virtualBoat))
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
#endif
}
}
}
private:
void setup()
{
std::vector<const IShipyard *> shipyards;
for(const CGTownInstance * t : cb->getTownsInfo())
{
if(t->hasBuilt(BuildingID::SHIPYARD))
shipyards.push_back(t);
}
for(const CGObjectInstance * obj : ai->visitableObjs)
{
if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
{
if(const IShipyard * shipyard = IShipyard::castFrom(obj))
shipyards.push_back(shipyard);
}
}
for(const IShipyard * shipyard : shipyards)
{
if(shipyard->shipyardStatus() == IShipyard::GOOD)
{
int3 boatLocation = shipyard->bestLocation();
virtualBoats[boatLocation] = std::make_shared<BuildBoatAction>(shipyard);
logAi->debug("Virtual boat added at %s", boatLocation.toString());
}
}
auto hero = nodeStorage->getHero();
auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();
if(hero->canCastThisSpell(summonBoatSpell)
&& hero->getSpellSchoolLevel(summonBoatSpell) >= SecSkillLevel::ADVANCED)
{
// TODO: For lower school level we might need to check the existance of some boat
summonableVirtualBoat.reset(new SummonBoatAction());
}
}
std::shared_ptr<const VirtualBoatAction> findVirtualBoat(
CDestinationNodeInfo &destination,
const PathNodeInfo &source) const
{
std::shared_ptr<const VirtualBoatAction> virtualBoat;
if(vstd::contains(virtualBoats, destination.coord))
{
virtualBoat = virtualBoats.at(destination.coord);
}
else if(
summonableVirtualBoat
&& summonableVirtualBoat->isAffordableBy(nodeStorage->getHero(), nodeStorage->getAINode(source.node)))
{
virtualBoat = summonableVirtualBoat;
}
return virtualBoat;
}
bool tryEmbarkVirtualBoat(
CDestinationNodeInfo &destination,
const PathNodeInfo &source,
std::shared_ptr<const VirtualBoatAction> virtualBoat) const
{
bool result = false;
nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
{
auto boatNodeOptional = nodeStorage->getOrCreateNode(
node->coord,
node->layer,
node->chainMask | virtualBoat->getSpecialChain());
if(boatNodeOptional)
{
AIPathNode * boatNode = boatNodeOptional.get();
if(boatNode->action == CGPathNode::UNKNOWN)
{
boatNode->specialAction = virtualBoat;
destination.blocked = false;
destination.action = CGPathNode::ENodeAction::EMBARK;
destination.node = boatNode;
result = true;
}
else
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Special transition node already allocated. Blocked moving %s -> %s",
source.coord.toString(),
destination.coord.toString());
#endif
}
}
else
{
logAi->debug(
"Can not allocate special transition node while moving %s -> %s",
source.coord.toString(),
destination.coord.toString());
}
});
return result;
}
};
class AIMovementAfterDestinationRule : public MovementAfterDestinationRule
{
private:
CPlayerSpecificInfoCallback * cb;
std::shared_ptr<AINodeStorage> nodeStorage;
public:
AIMovementAfterDestinationRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr<AINodeStorage> nodeStorage)
:cb(cb), nodeStorage(nodeStorage)
{
}
virtual void process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const override
{
if(nodeStorage->hasBetterChain(source, destination))
{
destination.blocked = true;
return;
}
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
if(blocker == BlockingReason::NONE)
return;
if(blocker == BlockingReason::DESTINATION_BLOCKVIS && destination.nodeObject)
{
auto objID = destination.nodeObject->ID;
auto enemyHero = objID == Obj::HERO && destination.objectRelations == PlayerRelations::ENEMIES;
if(!enemyHero && !isObjectRemovable(destination.nodeObject))
{
destination.blocked = true;
}
return;
}
if(blocker == BlockingReason::DESTINATION_VISIT)
{
return;
}
if(blocker == BlockingReason::DESTINATION_GUARDED)
{
auto srcGuardians = cb->getGuardingCreatures(source.coord);
auto destGuardians = cb->getGuardingCreatures(destination.coord);
if(destGuardians.empty())
{
destination.blocked = true;
return;
}
vstd::erase_if(destGuardians, [&](const CGObjectInstance * destGuard) -> bool
{
return vstd::contains(srcGuardians, destGuard);
});
auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size();
if(guardsAlreadyBypassed && nodeStorage->isBattleNode(source.node))
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Bypass guard at destination while moving %s -> %s",
source.coord.toString(),
destination.coord.toString());
#endif
return;
}
const AIPathNode * destNode = nodeStorage->getAINode(destination.node);
auto battleNodeOptional = nodeStorage->getOrCreateNode(
destination.coord,
destination.node->layer,
destNode->chainMask | AINodeStorage::BATTLE_CHAIN);
if(!battleNodeOptional)
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Can not allocate battle node while moving %s -> %s",
source.coord.toString(),
destination.coord.toString());
#endif
destination.blocked = true;
return;
}
AIPathNode * battleNode = battleNodeOptional.get();
if(battleNode->locked)
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Block bypass guard at destination while moving %s -> %s",
source.coord.toString(),
destination.coord.toString());
#endif
destination.blocked = true;
return;
}
auto hero = nodeStorage->getHero();
auto danger = evaluateDanger(destination.coord, hero);
destination.node = battleNode;
nodeStorage->commit(destination, source);
if(battleNode->danger < danger)
{
battleNode->danger = danger;
}
battleNode->specialAction = std::make_shared<BattleAction>(destination.coord);
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Begin bypass guard at destination with danger %s while moving %s -> %s",
std::to_string(danger),
source.coord.toString(),
destination.coord.toString());
#endif
return;
}
destination.blocked = true;
}
};
class AIMovementToDestinationRule : public MovementToDestinationRule
{
private:
std::shared_ptr<AINodeStorage> nodeStorage;
public:
AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage)
: nodeStorage(nodeStorage)
{
}
virtual void process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const override
{
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
if(blocker == BlockingReason::NONE)
return;
if(blocker == BlockingReason::DESTINATION_BLOCKED
&& destination.action == CGPathNode::EMBARK
&& nodeStorage->getAINode(destination.node)->specialAction)
{
return;
}
if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->isBattleNode(source.node))
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Bypass src guard while moving from %s to %s",
source.coord.toString(),
destination.coord.toString());
#endif
return;
}
destination.blocked = true;
}
};
class AIPreviousNodeRule : public MovementToDestinationRule
{
private:
std::shared_ptr<AINodeStorage> nodeStorage;
public:
AIPreviousNodeRule(std::shared_ptr<AINodeStorage> nodeStorage)
: nodeStorage(nodeStorage)
{
}
virtual void process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const override
{
if(source.node->action == CGPathNode::ENodeAction::BLOCKING_VISIT || source.node->action == CGPathNode::ENodeAction::VISIT)
{
// we can not directly bypass objects, we need to interact with them first
destination.node->theNodeBefore = source.node;
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Link src node %s to destination node %s while bypassing visitable obj",
source.coord.toString(),
destination.coord.toString());
#endif
return;
}
auto aiSourceNode = nodeStorage->getAINode(source.node);
if(aiSourceNode->specialAction)
{
// there is some action on source tile which should be performed before we can bypass it
destination.node->theNodeBefore = source.node;
}
}
};
std::vector<std::shared_ptr<IPathfindingRule>> makeRuleset(
CPlayerSpecificInfoCallback * cb,
VCAI * ai,

View File

@ -0,0 +1,21 @@
/*
* BattleAction.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "../../Goals/VisitTile.h"
#include "BattleAction.h"
namespace AIPathfinding
{
Goals::TSubgoal BattleAction::whatToDo(const HeroPtr & hero) const
{
return Goals::sptr(Goals::VisitTile(targetTile).sethero(hero));
}
}

View File

@ -0,0 +1,30 @@
/*
* BattleAction.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "ISpecialAction.h"
namespace AIPathfinding
{
class BattleAction : public ISpecialAction
{
private:
const int3 targetTile;
public:
BattleAction(const int3 targetTile)
:targetTile(targetTile)
{
}
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
};
}

View File

@ -0,0 +1,61 @@
/*
* BoatActions.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "../../Goals/AdventureSpellCast.h"
#include "../../Goals/BuildBoat.h"
#include "../../../../lib/mapping/CMap.h"
#include "../../../../lib/mapObjects/MapObjects.h"
#include "BoatActions.h"
namespace AIPathfinding
{
Goals::TSubgoal BuildBoatAction::whatToDo(const HeroPtr & hero) const
{
return Goals::sptr(Goals::BuildBoat(shipyard));
}
Goals::TSubgoal SummonBoatAction::whatToDo(const HeroPtr & hero) const
{
return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
}
void SummonBoatAction::applyOnDestination(
const HeroPtr & hero,
CDestinationNodeInfo & destination,
const PathNodeInfo & source,
AIPathNode * dstMode,
const AIPathNode * srcNode) const
{
dstMode->manaCost = srcNode->manaCost + getManaCost(hero);
dstMode->theNodeBefore = source.node;
}
bool SummonBoatAction::isAffordableBy(const HeroPtr & hero, const AIPathNode * source) const
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Hero %s has %d mana and needed %d and already spent %d",
hero->name,
hero->mana,
getManaCost(hero),
source->manaCost);
#endif
return hero->mana >= source->manaCost + getManaCost(hero);
}
uint32_t SummonBoatAction::getManaCost(const HeroPtr & hero) const
{
SpellID summonBoat = SpellID::SUMMON_BOAT;
return hero->getSpellCost(summonBoat.toSpell());
}
}

View File

@ -0,0 +1,72 @@
/*
* BoatActions.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "ISpecialAction.h"
#include "../../../lib/mapping/CMap.h"
#include "../../../lib/mapObjects/MapObjects.h"
namespace AIPathfinding
{
class VirtualBoatAction : public ISpecialAction
{
private:
uint64_t specialChain;
public:
VirtualBoatAction(uint64_t specialChain)
:specialChain(specialChain)
{
}
uint64_t getSpecialChain() const
{
return specialChain;
}
};
class SummonBoatAction : public VirtualBoatAction
{
public:
SummonBoatAction()
:VirtualBoatAction(AINodeStorage::CAST_CHAIN)
{
}
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
virtual void applyOnDestination(
const HeroPtr & hero,
CDestinationNodeInfo & destination,
const PathNodeInfo & source,
AIPathNode * dstMode,
const AIPathNode * srcNode) const override;
bool isAffordableBy(const HeroPtr & hero, const AIPathNode * source) const;
private:
uint32_t getManaCost(const HeroPtr & hero) const;
};
class BuildBoatAction : public VirtualBoatAction
{
private:
const IShipyard * shipyard;
public:
BuildBoatAction(const IShipyard * shipyard)
:VirtualBoatAction(AINodeStorage::RESOURCE_CHAIN), shipyard(shipyard)
{
}
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
};
}

View File

@ -0,0 +1,31 @@
/*
* ISpecialAction.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../../AIUtility.h"
#include "../../Goals/AbstractGoal.h"
class AIPathNode;
class ISpecialAction
{
public:
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0;
virtual void applyOnDestination(
const HeroPtr & hero,
CDestinationNodeInfo & destination,
const PathNodeInfo & source,
AIPathNode * dstMode,
const AIPathNode * srcNode) const
{
}
};

View File

@ -0,0 +1,24 @@
/*
* TownPortalAction.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "../../Goals/AdventureSpellCast.h"
#include "../../../../lib/mapping/CMap.h"
#include "../../../../lib/mapObjects/MapObjects.h"
#include "TownPortalAction.h"
using namespace AIPathfinding;
Goals::TSubgoal TownPortalAction::whatToDo(const HeroPtr & hero) const
{
const CGTownInstance * targetTown = target; // const pointer is not allowed in settown
return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL).settown(targetTown).settile(targetTown->visitablePos()));
}

View File

@ -0,0 +1,33 @@
/*
* TownPortalAction.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "ISpecialAction.h"
#include "../../../lib/mapping/CMap.h"
#include "../../../lib/mapObjects/MapObjects.h"
#include "../../Goals/AdventureSpellCast.h"
namespace AIPathfinding
{
class TownPortalAction : public ISpecialAction
{
private:
const CGTownInstance * target;
public:
TownPortalAction(const CGTownInstance * target)
:target(target)
{
}
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
};
}

View File

@ -0,0 +1,154 @@
/*
* AILayerTransitionRule.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "AILayerTransitionRule.h"
namespace AIPathfinding
{
AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
:cb(cb), ai(ai), nodeStorage(nodeStorage)
{
setup();
}
void AILayerTransitionRule::process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const
{
LayerTransitionRule::process(source, destination, pathfinderConfig, pathfinderHelper);
if(!destination.blocked)
{
return;
}
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL)
{
std::shared_ptr<const VirtualBoatAction> virtualBoat = findVirtualBoat(destination, source);
if(virtualBoat && tryEmbarkVirtualBoat(destination, source, virtualBoat))
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
#endif
}
}
}
void AILayerTransitionRule::setup()
{
std::vector<const IShipyard *> shipyards;
for(const CGTownInstance * t : cb->getTownsInfo())
{
if(t->hasBuilt(BuildingID::SHIPYARD))
shipyards.push_back(t);
}
for(const CGObjectInstance * obj : ai->visitableObjs)
{
if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
{
if(const IShipyard * shipyard = IShipyard::castFrom(obj))
shipyards.push_back(shipyard);
}
}
for(const IShipyard * shipyard : shipyards)
{
if(shipyard->shipyardStatus() == IShipyard::GOOD)
{
int3 boatLocation = shipyard->bestLocation();
virtualBoats[boatLocation] = std::make_shared<BuildBoatAction>(shipyard);
logAi->debug("Virtual boat added at %s", boatLocation.toString());
}
}
auto hero = nodeStorage->getHero();
auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();
if(hero->canCastThisSpell(summonBoatSpell)
&& hero->getSpellSchoolLevel(summonBoatSpell) >= SecSkillLevel::ADVANCED)
{
// TODO: For lower school level we might need to check the existance of some boat
summonableVirtualBoat.reset(new SummonBoatAction());
}
}
std::shared_ptr<const VirtualBoatAction> AILayerTransitionRule::findVirtualBoat(
CDestinationNodeInfo & destination,
const PathNodeInfo & source) const
{
std::shared_ptr<const VirtualBoatAction> virtualBoat;
if(vstd::contains(virtualBoats, destination.coord))
{
virtualBoat = virtualBoats.at(destination.coord);
}
else if(
summonableVirtualBoat
&& summonableVirtualBoat->isAffordableBy(nodeStorage->getHero(), nodeStorage->getAINode(source.node)))
{
virtualBoat = summonableVirtualBoat;
}
return virtualBoat;
}
bool AILayerTransitionRule::tryEmbarkVirtualBoat(
CDestinationNodeInfo & destination,
const PathNodeInfo & source,
std::shared_ptr<const VirtualBoatAction> virtualBoat) const
{
bool result = false;
nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
{
auto boatNodeOptional = nodeStorage->getOrCreateNode(
node->coord,
node->layer,
node->chainMask | virtualBoat->getSpecialChain());
if(boatNodeOptional)
{
AIPathNode * boatNode = boatNodeOptional.get();
if(boatNode->action == CGPathNode::UNKNOWN)
{
boatNode->specialAction = virtualBoat;
destination.blocked = false;
destination.action = CGPathNode::ENodeAction::EMBARK;
destination.node = boatNode;
result = true;
}
else
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Special transition node already allocated. Blocked moving %s -> %s",
source.coord.toString(),
destination.coord.toString());
#endif
}
}
else
{
logAi->debug(
"Can not allocate special transition node while moving %s -> %s",
source.coord.toString(),
destination.coord.toString());
}
});
return result;
}
}

View File

@ -0,0 +1,52 @@
/*
* AILayerTransitionRule.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../AINodeStorage.h"
#include "../../VCAI.h"
#include "../Actions/BoatActions.h"
#include "../../../../CCallback.h"
#include "../../../../lib/mapping/CMap.h"
#include "../../../../lib/mapObjects/MapObjects.h"
namespace AIPathfinding
{
class AILayerTransitionRule : public LayerTransitionRule
{
private:
CPlayerSpecificInfoCallback * cb;
VCAI * ai;
std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
std::shared_ptr<AINodeStorage> nodeStorage;
std::shared_ptr<const SummonBoatAction> summonableVirtualBoat;
public:
AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage);
virtual void process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const override;
private:
void setup();
std::shared_ptr<const VirtualBoatAction> findVirtualBoat(
CDestinationNodeInfo & destination,
const PathNodeInfo & source) const;
bool tryEmbarkVirtualBoat(
CDestinationNodeInfo & destination,
const PathNodeInfo & source,
std::shared_ptr<const VirtualBoatAction> virtualBoat) const;
};
}

View File

@ -0,0 +1,148 @@
/*
* AIMovementAfterDestinationRule.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "AIMovementAfterDestinationRule.h"
#include "../Actions/BattleAction.h"
namespace AIPathfinding
{
AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
CPlayerSpecificInfoCallback * cb,
std::shared_ptr<AINodeStorage> nodeStorage)
:cb(cb), nodeStorage(nodeStorage)
{
}
void AIMovementAfterDestinationRule::process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const
{
if(nodeStorage->hasBetterChain(source, destination))
{
destination.blocked = true;
return;
}
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
if(blocker == BlockingReason::NONE)
return;
if(blocker == BlockingReason::DESTINATION_BLOCKVIS && destination.nodeObject)
{
auto objID = destination.nodeObject->ID;
auto enemyHero = objID == Obj::HERO && destination.objectRelations == PlayerRelations::ENEMIES;
if(!enemyHero && !isObjectRemovable(destination.nodeObject))
{
destination.blocked = true;
}
return;
}
if(blocker == BlockingReason::DESTINATION_VISIT)
{
return;
}
if(blocker == BlockingReason::DESTINATION_GUARDED)
{
auto srcGuardians = cb->getGuardingCreatures(source.coord);
auto destGuardians = cb->getGuardingCreatures(destination.coord);
if(destGuardians.empty())
{
destination.blocked = true;
return;
}
vstd::erase_if(destGuardians, [&](const CGObjectInstance * destGuard) -> bool
{
return vstd::contains(srcGuardians, destGuard);
});
auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size();
if(guardsAlreadyBypassed && nodeStorage->isBattleNode(source.node))
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Bypass guard at destination while moving %s -> %s",
source.coord.toString(),
destination.coord.toString());
#endif
return;
}
const AIPathNode * destNode = nodeStorage->getAINode(destination.node);
auto battleNodeOptional = nodeStorage->getOrCreateNode(
destination.coord,
destination.node->layer,
destNode->chainMask | AINodeStorage::BATTLE_CHAIN);
if(!battleNodeOptional)
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Can not allocate battle node while moving %s -> %s",
source.coord.toString(),
destination.coord.toString());
#endif
destination.blocked = true;
return;
}
AIPathNode * battleNode = battleNodeOptional.get();
if(battleNode->locked)
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Block bypass guard at destination while moving %s -> %s",
source.coord.toString(),
destination.coord.toString());
#endif
destination.blocked = true;
return;
}
auto hero = nodeStorage->getHero();
auto danger = evaluateDanger(destination.coord, hero);
destination.node = battleNode;
nodeStorage->commit(destination, source);
if(battleNode->danger < danger)
{
battleNode->danger = danger;
}
battleNode->specialAction = std::make_shared<BattleAction>(destination.coord);
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Begin bypass guard at destination with danger %s while moving %s -> %s",
std::to_string(danger),
source.coord.toString(),
destination.coord.toString());
#endif
return;
}
destination.blocked = true;
}
}

View File

@ -0,0 +1,36 @@
/*
* AIMovementAfterDestinationRule.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../AINodeStorage.h"
#include "../../VCAI.h"
#include "../../../../CCallback.h"
#include "../../../../lib/mapping/CMap.h"
#include "../../../../lib/mapObjects/MapObjects.h"
namespace AIPathfinding
{
class AIMovementAfterDestinationRule : public MovementAfterDestinationRule
{
private:
CPlayerSpecificInfoCallback * cb;
std::shared_ptr<AINodeStorage> nodeStorage;
public:
AIMovementAfterDestinationRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr<AINodeStorage> nodeStorage);
virtual void process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const override;
};
}

View File

@ -0,0 +1,51 @@
/*
* AIMovementToDestinationRule.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "AIMovementToDestinationRule.h"
namespace AIPathfinding
{
AIMovementToDestinationRule::AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage)
: nodeStorage(nodeStorage)
{
}
void AIMovementToDestinationRule::process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const
{
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
if(blocker == BlockingReason::NONE)
return;
if(blocker == BlockingReason::DESTINATION_BLOCKED
&& destination.action == CGPathNode::EMBARK
&& nodeStorage->getAINode(destination.node)->specialAction)
{
return;
}
if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->isBattleNode(source.node))
{
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Bypass src guard while moving from %s to %s",
source.coord.toString(),
destination.coord.toString());
#endif
return;
}
destination.blocked = true;
}
}

View File

@ -0,0 +1,35 @@
/*
* AIMovementToDestinationRule.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../AINodeStorage.h"
#include "../../VCAI.h"
#include "../../../../CCallback.h"
#include "../../../../lib/mapping/CMap.h"
#include "../../../../lib/mapObjects/MapObjects.h"
namespace AIPathfinding
{
class AIMovementToDestinationRule : public MovementToDestinationRule
{
private:
std::shared_ptr<AINodeStorage> nodeStorage;
public:
AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage);
virtual void process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const override;
};
}

View File

@ -0,0 +1,47 @@
/*
* AIPreviousNodeRule.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "AIPreviousNodeRule.h"
namespace AIPathfinding
{
AIPreviousNodeRule::AIPreviousNodeRule(std::shared_ptr<AINodeStorage> nodeStorage)
: nodeStorage(nodeStorage)
{
}
void AIPreviousNodeRule::process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const
{
if(source.node->action == CGPathNode::ENodeAction::BLOCKING_VISIT || source.node->action == CGPathNode::ENodeAction::VISIT)
{
// we can not directly bypass objects, we need to interact with them first
destination.node->theNodeBefore = source.node;
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Link src node %s to destination node %s while bypassing visitable obj",
source.coord.toString(),
destination.coord.toString());
#endif
return;
}
auto aiSourceNode = nodeStorage->getAINode(source.node);
if(aiSourceNode->specialAction)
{
// there is some action on source tile which should be performed before we can bypass it
destination.node->theNodeBefore = source.node;
}
}
}

View File

@ -0,0 +1,35 @@
/*
* AIPreviousNodeRule.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../AINodeStorage.h"
#include "../../VCAI.h"
#include "../../../../CCallback.h"
#include "../../../../lib/mapping/CMap.h"
#include "../../../../lib/mapObjects/MapObjects.h"
namespace AIPathfinding
{
class AIPreviousNodeRule : public MovementToDestinationRule
{
private:
std::shared_ptr<AINodeStorage> nodeStorage;
public:
AIPreviousNodeRule(std::shared_ptr<AINodeStorage> nodeStorage);
virtual void process(
const PathNodeInfo & source,
CDestinationNodeInfo & destination,
const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const override;
};
}

View File

@ -168,10 +168,20 @@
<ClCompile Include="Goals\Win.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="MapObjectsEvaluator.cpp" />
<ClInclude Include="Pathfinding\Actions\BattleAction.h" />
<ClInclude Include="Pathfinding\Actions\TownPortalAction.h" />
<ClCompile Include="Pathfinding\Actions\BattleAction.cpp" />
<ClCompile Include="Pathfinding\Actions\TownPortalAction.cpp" />
<ClInclude Include="Pathfinding\Actions\BoatActions.h" />
<ClCompile Include="Pathfinding\Actions\BoatActions.cpp" />
<ClCompile Include="Pathfinding\AINodeStorage.cpp" />
<ClCompile Include="Pathfinding\AIPathfinder.cpp" />
<ClCompile Include="Pathfinding\AIPathfinderConfig.cpp" />
<ClCompile Include="Pathfinding\PathfindingManager.cpp" />
<ClCompile Include="Pathfinding\Rules\AILayerTransitionRule.cpp" />
<ClCompile Include="Pathfinding\Rules\AIMovementAfterDestinationRule.cpp" />
<ClCompile Include="Pathfinding\Rules\AIMovementToDestinationRule.cpp" />
<ClCompile Include="Pathfinding\Rules\AIPreviousNodeRule.cpp" />
<ClCompile Include="ResourceManager.cpp" />
<ClCompile Include="SectorMap.cpp" />
<ClCompile Include="StdInc.cpp">
@ -214,10 +224,15 @@
<ClInclude Include="Goals\VisitTile.h" />
<ClInclude Include="Goals\Win.h" />
<ClInclude Include="MapObjectsEvaluator.h" />
<ClInclude Include="Pathfinding\Actions\ISpecialAction.h" />
<ClInclude Include="Pathfinding\AINodeStorage.h" />
<ClInclude Include="Pathfinding\AIPathfinder.h" />
<ClInclude Include="Pathfinding\AIPathfinderConfig.h" />
<ClInclude Include="Pathfinding\PathfindingManager.h" />
<ClInclude Include="Pathfinding\Rules\AILayerTransitionRule.h" />
<ClInclude Include="Pathfinding\Rules\AIMovementAfterDestinationRule.h" />
<ClInclude Include="Pathfinding\Rules\AIMovementToDestinationRule.h" />
<ClInclude Include="Pathfinding\Rules\AIPreviousNodeRule.h" />
<ClInclude Include="ResourceManager.h" />
<ClInclude Include="SectorMap.h" />
<ClInclude Include="StdInc.h" />

View File

@ -90,6 +90,15 @@
<ClCompile Include="Goals\AdventureSpellCast.cpp">
<Filter>Goals</Filter>
</ClCompile>
<ClCompile Include="Pathfinding\Actions\BoatActions.cpp">
<Filter>Pathfinding\Actions</Filter>
</ClCompile>
<ClCompile Include="Pathfinding\Actions\TownPortalAction.cpp" />
<ClCompile Include="Pathfinding\Actions\BattleAction.cpp" />
<ClCompile Include="Pathfinding\Rules\AILayerTransitionRule.cpp" />
<ClCompile Include="Pathfinding\Rules\AIMovementAfterDestinationRule.cpp" />
<ClCompile Include="Pathfinding\Rules\AIMovementToDestinationRule.cpp" />
<ClCompile Include="Pathfinding\Rules\AIPreviousNodeRule.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="AIhelper.h" />
@ -189,6 +198,16 @@
<ClInclude Include="Goals\AdventureSpellCast.h">
<Filter>Goals</Filter>
</ClInclude>
<ClInclude Include="Pathfinding\Actions\ISpecialAction.h">
<Filter>Pathfinding\Actions</Filter>
</ClInclude>
<ClInclude Include="Pathfinding\Actions\TownPortalAction.h" />
<ClInclude Include="Pathfinding\Actions\BattleAction.h" />
<ClInclude Include="Pathfinding\Actions\BoatActions.h" />
<ClInclude Include="Pathfinding\Rules\AILayerTransitionRule.h" />
<ClInclude Include="Pathfinding\Rules\AIMovementAfterDestinationRule.h" />
<ClInclude Include="Pathfinding\Rules\AIMovementToDestinationRule.h" />
<ClInclude Include="Pathfinding\Rules\AIPreviousNodeRule.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="Pathfinding">
@ -197,5 +216,11 @@
<Filter Include="Goals">
<UniqueIdentifier>{f97140a0-eee3-456f-b586-4b13265c01da}</UniqueIdentifier>
</Filter>
<Filter Include="Pathfinding\Rules">
<UniqueIdentifier>{beabfdb9-2e76-4daa-8d1a-81086387f319}</UniqueIdentifier>
</Filter>
<Filter Include="Pathfinding\Actions">
<UniqueIdentifier>{3ebb4852-a986-447a-b5cc-20992df76f0c}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>