mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-06 09:09:40 +02:00
AI: CompleteQuest goal and summon boat spell support
This commit is contained in:
committed by
ArseniyShestakov
parent
e996879733
commit
5d022ba77c
@@ -82,6 +82,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPat
|
||||
|
||||
heroNode.chainMask = 0;
|
||||
heroNode.danger = 0;
|
||||
heroNode.manaCost = 0;
|
||||
heroNode.specialAction.reset();
|
||||
heroNode.update(coord, layer, accessibility);
|
||||
}
|
||||
@@ -97,6 +98,12 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
|
||||
dstNode->danger = srcNode->danger;
|
||||
dstNode->action = destination.action;
|
||||
dstNode->theNodeBefore = srcNode->theNodeBefore;
|
||||
dstNode->manaCost = srcNode->manaCost;
|
||||
|
||||
if(dstNode->specialAction)
|
||||
{
|
||||
dstNode->specialAction->applyOnDestination(getHero(), destination, source, dstNode, srcNode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -186,6 +193,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(int3 pos, bool isOnLand) const
|
||||
{
|
||||
std::vector<AIPath> paths;
|
||||
auto chains = nodes[pos.x][pos.y][pos.z][isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL];
|
||||
auto initialPos = hero->visitablePos();
|
||||
|
||||
for(const AIPathNode & node : chains)
|
||||
{
|
||||
@@ -197,7 +205,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(int3 pos, bool isOnLand) const
|
||||
AIPath path;
|
||||
const AIPathNode * current = &node;
|
||||
|
||||
while(current != nullptr)
|
||||
while(current != nullptr && current->coord != initialPos)
|
||||
{
|
||||
AIPathNodeInfo pathNode;
|
||||
|
||||
|
||||
@@ -15,16 +15,28 @@
|
||||
#include "../AIUtility.h"
|
||||
#include "../Goals/AbstractGoal.h"
|
||||
|
||||
class 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;
|
||||
uint64_t danger;
|
||||
uint32_t manaCost;
|
||||
std::shared_ptr<const ISpecialAction> specialAction;
|
||||
};
|
||||
|
||||
|
||||
@@ -48,10 +48,10 @@ std::vector<AIPath> AIPathfinder::getPathInfo(HeroPtr hero, int3 tile)
|
||||
}
|
||||
|
||||
storageMap[hero] = nodeStorage;
|
||||
nodeStorage->setHero(hero.get());
|
||||
|
||||
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
|
||||
|
||||
nodeStorage->setHero(hero.get());
|
||||
cb->calculatePaths(config, hero.get());
|
||||
}
|
||||
else
|
||||
|
||||
@@ -16,14 +16,31 @@
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class BuildBoatAction : public ISpecialAction
|
||||
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)
|
||||
:shipyard(shipyard)
|
||||
:VirtualBoatAction(AINodeStorage::RESOURCE_CHAIN), shipyard(shipyard)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -33,6 +50,51 @@ namespace AIPathfinding
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
logAi->trace(
|
||||
"Hero %s has %d mana and needed %d and already spent %d",
|
||||
hero->name,
|
||||
hero->mana,
|
||||
getManaCost(hero),
|
||||
source->manaCost);
|
||||
|
||||
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:
|
||||
@@ -58,6 +120,7 @@ namespace AIPathfinding
|
||||
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)
|
||||
@@ -79,37 +142,14 @@ namespace AIPathfinding
|
||||
return;
|
||||
}
|
||||
|
||||
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL
|
||||
&& vstd::contains(virtualBoats, destination.coord))
|
||||
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL)
|
||||
{
|
||||
logAi->trace("Bypassing virtual boat at %s!", destination.coord.toString());
|
||||
std::shared_ptr<const VirtualBoatAction> virtualBoat = findVirtualBoat(destination, source);
|
||||
|
||||
nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
|
||||
if(virtualBoat && tryEmbarkVirtualBoat(destination, source, virtualBoat))
|
||||
{
|
||||
std::shared_ptr<const BuildBoatAction> virtualBoat = virtualBoats.at(destination.coord);
|
||||
|
||||
auto boatNodeOptional = nodeStorage->getOrCreateNode(
|
||||
node->coord,
|
||||
node->layer,
|
||||
node->chainMask | AINodeStorage::RESOURCE_CHAIN);
|
||||
|
||||
if(boatNodeOptional)
|
||||
{
|
||||
AIPathNode * boatNode = boatNodeOptional.get();
|
||||
|
||||
boatNode->specialAction = virtualBoat;
|
||||
destination.blocked = false;
|
||||
destination.action = CGPathNode::ENodeAction::EMBARK;
|
||||
destination.node = boatNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
logAi->trace(
|
||||
"Can not allocate boat node while moving %s -> %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
}
|
||||
});
|
||||
logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +182,84 @@ namespace AIPathfinding
|
||||
logAi->debug("Virtual boat added at %s", boatLocation.toString());
|
||||
}
|
||||
}
|
||||
|
||||
auto hero = nodeStorage->getHero();
|
||||
|
||||
if(vstd::contains(hero->spells, SpellID::SUMMON_BOAT))
|
||||
{
|
||||
auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();
|
||||
|
||||
if(hero->getSpellSchoolLevel(summonBoatSpell) == SecSkillLevel::EXPERT)
|
||||
{
|
||||
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::NOT_SET)
|
||||
{
|
||||
boatNode->specialAction = virtualBoat;
|
||||
destination.blocked = false;
|
||||
destination.action = CGPathNode::ENodeAction::EMBARK;
|
||||
destination.node = boatNode;
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
logAi->trace(
|
||||
"Special transition node already allocated. Blocked moving %s -> %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logAi->trace(
|
||||
"Can not allocate special transition node while moving %s -> %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "PathfindingManager.h"
|
||||
#include "AIPathfinder.h"
|
||||
#include "AIPathfinderConfig.h"
|
||||
#include "Goals/Goals.h"
|
||||
#include "../Goals/Goals.h"
|
||||
#include "../../../lib/CGameInfoCallback.h"
|
||||
#include "../../../lib/mapping/CMap.h"
|
||||
|
||||
@@ -130,8 +130,6 @@ Goals::TGoalVec PathfindingManager::findPath(
|
||||
|
||||
if(isSafeToVisit(hero, danger))
|
||||
{
|
||||
logAi->trace("It's safe for %s to visit tile %s with danger %s", hero->name, dest.toString(), std::to_string(danger));
|
||||
|
||||
Goals::TSubgoal solution;
|
||||
|
||||
if(path.specialAction)
|
||||
@@ -153,6 +151,8 @@ Goals::TGoalVec PathfindingManager::findPath(
|
||||
|
||||
solution->evaluationContext.movementCost += path.movementCost();
|
||||
|
||||
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());
|
||||
|
||||
result.push_back(solution);
|
||||
|
||||
continue;
|
||||
@@ -212,11 +212,15 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
|
||||
return sptr(Goals::VisitObj(topObj->id.getNum()).sethero(hero));
|
||||
}
|
||||
|
||||
//TODO: we should be able to return apriopriate quest here
|
||||
//ret.push_back(ai->questToGoal());
|
||||
//however, visiting obj for firts time will give us quest
|
||||
//do not access quets guard if we can't complete the quest
|
||||
logAi->trace("Can not visit this quest guard! Not ready!");
|
||||
auto questObj = dynamic_cast<const IQuestObject*>(topObj);
|
||||
|
||||
if(questObj)
|
||||
{
|
||||
auto questInfo = QuestInfo(questObj->quest, topObj, topObj->visitablePos());
|
||||
|
||||
return sptr(Goals::CompleteQuest(questInfo));
|
||||
}
|
||||
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user