mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-14 02:33:51 +02:00
AI: town portal support
This commit is contained in:
parent
2e20dce71f
commit
8fee46de7c
@ -98,6 +98,19 @@ float FuzzyHelper::evaluate(Goals::BuildBoat & g)
|
|||||||
return g.parent->accept(this) - buildBoatPenalty;
|
return g.parent->accept(this) - buildBoatPenalty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float FuzzyHelper::evaluate(Goals::AdventureSpellCast & g)
|
||||||
|
{
|
||||||
|
if(!g.parent)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSpell * spell = g.getSpell();
|
||||||
|
const float spellCastPenalty = (float)g.hero->getSpellCost(spell) / g.hero->mana;
|
||||||
|
|
||||||
|
return g.parent->accept(this) - spellCastPenalty;
|
||||||
|
}
|
||||||
|
|
||||||
float FuzzyHelper::evaluate(Goals::CompleteQuest & g)
|
float FuzzyHelper::evaluate(Goals::CompleteQuest & g)
|
||||||
{
|
{
|
||||||
// TODO: How to evaluate quest complexity?
|
// TODO: How to evaluate quest complexity?
|
||||||
@ -120,6 +133,7 @@ float FuzzyHelper::evaluate(Goals::VisitObj & g)
|
|||||||
|
|
||||||
return visitObjEngine.evaluate(g);
|
return visitObjEngine.evaluate(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
float FuzzyHelper::evaluate(Goals::VisitHero & g)
|
float FuzzyHelper::evaluate(Goals::VisitHero & g)
|
||||||
{
|
{
|
||||||
auto obj = ai->myCb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similar
|
auto obj = ai->myCb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similar
|
||||||
|
@ -33,6 +33,7 @@ public:
|
|||||||
float evaluate(Goals::GatherArmy & g);
|
float evaluate(Goals::GatherArmy & g);
|
||||||
float evaluate(Goals::ClearWayTo & g);
|
float evaluate(Goals::ClearWayTo & g);
|
||||||
float evaluate(Goals::CompleteQuest & g);
|
float evaluate(Goals::CompleteQuest & g);
|
||||||
|
float evaluate(Goals::AdventureSpellCast & g);
|
||||||
float evaluate(Goals::Invalid & g);
|
float evaluate(Goals::Invalid & g);
|
||||||
float evaluate(Goals::AbstractGoal & g);
|
float evaluate(Goals::AbstractGoal & g);
|
||||||
void setPriority(Goals::TSubgoal & g);
|
void setPriority(Goals::TSubgoal & g);
|
||||||
|
@ -31,7 +31,7 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve()
|
|||||||
if(!hero.validAndSet())
|
if(!hero.validAndSet())
|
||||||
throw cannotFulfillGoalException("Invalid hero!");
|
throw cannotFulfillGoalException("Invalid hero!");
|
||||||
|
|
||||||
auto spell = spellID.toSpell();
|
auto spell = getSpell();
|
||||||
|
|
||||||
logAi->trace("Decomposing adventure spell cast of %s for hero %s", spell->name, hero->name);
|
logAi->trace("Decomposing adventure spell cast of %s for hero %s", spell->name, hero->name);
|
||||||
|
|
||||||
@ -49,7 +49,26 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve()
|
|||||||
|
|
||||||
void AdventureSpellCast::accept(VCAI * ai)
|
void AdventureSpellCast::accept(VCAI * ai)
|
||||||
{
|
{
|
||||||
|
if(town && spellID == SpellID::TOWN_PORTAL)
|
||||||
|
{
|
||||||
|
ai->selectedObject = town->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wait = cb->waitTillRealize;
|
||||||
|
|
||||||
|
cb->waitTillRealize = true;
|
||||||
cb->castSpell(hero.h, spellID, tile);
|
cb->castSpell(hero.h, spellID, tile);
|
||||||
|
ai->ah->resetPaths();
|
||||||
|
|
||||||
|
if(town && spellID == SpellID::TOWN_PORTAL)
|
||||||
|
{
|
||||||
|
// visit town
|
||||||
|
ai->moveHeroToTile(town->visitablePos(), hero);
|
||||||
|
}
|
||||||
|
|
||||||
|
cb->waitTillRealize = wait;
|
||||||
|
|
||||||
|
throw goalFulfilledException(sptr(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AdventureSpellCast::name() const
|
std::string AdventureSpellCast::name() const
|
||||||
|
@ -30,6 +30,11 @@ namespace Goals
|
|||||||
return TGoalVec();
|
return TGoalVec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CSpell * getSpell() const
|
||||||
|
{
|
||||||
|
return spellID.toSpell();
|
||||||
|
}
|
||||||
|
|
||||||
TSubgoal whatToDoToAchieve() override;
|
TSubgoal whatToDoToAchieve() override;
|
||||||
void accept(VCAI * ai) override;
|
void accept(VCAI * ai) override;
|
||||||
std::string name() const override;
|
std::string name() const override;
|
||||||
|
@ -71,6 +71,8 @@ void BuildBoat::accept(VCAI * ai)
|
|||||||
shipyard->bestLocation().toString());
|
shipyard->bestLocation().toString());
|
||||||
|
|
||||||
cb->buildBoat(shipyard);
|
cb->buildBoat(shipyard);
|
||||||
|
|
||||||
|
throw goalFulfilledException(sptr(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BuildBoat::name() const
|
std::string BuildBoat::name() const
|
||||||
|
@ -57,15 +57,17 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
|
|||||||
//TODO: include evaluation of monsters gather in calculation
|
//TODO: include evaluation of monsters gather in calculation
|
||||||
for(auto t : cb->getTownsInfo())
|
for(auto t : cb->getTownsInfo())
|
||||||
{
|
{
|
||||||
auto pos = t->visitablePos();
|
auto waysToVisit = ai->ah->howToVisitObj(hero, t);
|
||||||
if(ai->isAccessibleForHero(pos, hero))
|
|
||||||
|
if(waysToVisit.size())
|
||||||
{
|
{
|
||||||
//grab army from town
|
//grab army from town
|
||||||
if(!t->visitingHero && howManyReinforcementsCanGet(hero.get(), t))
|
if(!t->visitingHero && howManyReinforcementsCanGet(hero.get(), t))
|
||||||
{
|
{
|
||||||
if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
|
if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
|
||||||
ret.push_back(sptr(VisitTile(pos).sethero(hero)));
|
vstd::concatenate(ret, waysToVisit);
|
||||||
}
|
}
|
||||||
|
|
||||||
//buy army in town
|
//buy army in town
|
||||||
if (!t->visitingHero || t->visitingHero == hero.get(true))
|
if (!t->visitingHero || t->visitingHero == hero.get(true))
|
||||||
{
|
{
|
||||||
@ -118,15 +120,20 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
|
|||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
for(auto h : otherHeroes)
|
for(auto h : otherHeroes)
|
||||||
{
|
{
|
||||||
// Go to the other hero if we are faster
|
// Go to the other hero if we are faster
|
||||||
if (!vstd::contains(ai->visitedHeroes[hero], h)
|
if(!vstd::contains(ai->visitedHeroes[hero], h))
|
||||||
&& ai->isAccessibleForHero(h->visitablePos(), hero, true)) //visit only once each turn //FIXME: this is only bug workaround
|
{
|
||||||
ret.push_back(sptr(VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero)));
|
vstd::concatenate(ret, ai->ah->howToVisitObj(hero, h));
|
||||||
// Let the other hero come to us
|
}
|
||||||
if (!vstd::contains(ai->visitedHeroes[h], hero))
|
|
||||||
ret.push_back(sptr(VisitHero(hero->id.getNum()).setisAbstract(true).sethero(h)));
|
// Go to the other hero if we are faster
|
||||||
|
if(!vstd::contains(ai->visitedHeroes[h], hero))
|
||||||
|
{
|
||||||
|
vstd::concatenate(ret, ai->ah->howToVisitObj(h, hero.get()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const CGObjectInstance *> objs;
|
std::vector<const CGObjectInstance *> objs;
|
||||||
@ -161,6 +168,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto h : cb->getHeroesInfo())
|
for(auto h : cb->getHeroesInfo())
|
||||||
{
|
{
|
||||||
for(auto obj : objs)
|
for(auto obj : objs)
|
||||||
|
@ -9,7 +9,12 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "AINodeStorage.h"
|
#include "AINodeStorage.h"
|
||||||
|
#include "../Goals/Goals.h"
|
||||||
|
#include "../../../CCallback.h"
|
||||||
|
#include "../../../lib/mapping/CMap.h"
|
||||||
|
#include "../../../lib/mapObjects/MapObjects.h"
|
||||||
|
|
||||||
|
extern boost::thread_specific_ptr<CCallback> cb;
|
||||||
|
|
||||||
AINodeStorage::AINodeStorage(const int3 & Sizes)
|
AINodeStorage::AINodeStorage(const int3 & Sizes)
|
||||||
: sizes(Sizes)
|
: sizes(Sizes)
|
||||||
@ -133,28 +138,128 @@ std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
|||||||
return neighbours;
|
return neighbours;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
|
||||||
const PathNodeInfo & source,
|
const PathNodeInfo & source,
|
||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper)
|
const CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
std::vector<CGPathNode *> neighbours;
|
std::vector<CGPathNode *> neighbours;
|
||||||
auto accessibleExits = pathfinderHelper->getTeleportExits(source);
|
|
||||||
auto srcNode = getAINode(source.node);
|
|
||||||
|
|
||||||
for(auto & neighbour : accessibleExits)
|
if(source.isNodeObjectVisitable())
|
||||||
{
|
{
|
||||||
auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->chainMask);
|
auto accessibleExits = pathfinderHelper->getTeleportExits(source);
|
||||||
|
auto srcNode = getAINode(source.node);
|
||||||
|
|
||||||
if(!node)
|
for(auto & neighbour : accessibleExits)
|
||||||
continue;
|
{
|
||||||
|
auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->chainMask);
|
||||||
|
|
||||||
neighbours.push_back(node.get());
|
if(!node)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
neighbours.push_back(node.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hero->getPosition(false) == source.coord)
|
||||||
|
{
|
||||||
|
calculateTownPortalTeleportations(source, neighbours);
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbours;
|
return neighbours;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AINodeStorage::calculateTownPortalTeleportations(
|
||||||
|
const PathNodeInfo & source,
|
||||||
|
std::vector<CGPathNode *> & neighbours)
|
||||||
|
{
|
||||||
|
SpellID spellID = SpellID::TOWN_PORTAL;
|
||||||
|
const CSpell * townPortal = spellID.toSpell();
|
||||||
|
auto srcNode = getAINode(source.node);
|
||||||
|
|
||||||
|
if(hero->canCastThisSpell(townPortal) && hero->mana >= hero->getSpellCost(townPortal))
|
||||||
|
{
|
||||||
|
auto towns = cb->getTownsInfo(false);
|
||||||
|
|
||||||
|
vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
|
||||||
|
{
|
||||||
|
return cb->getPlayerRelations(hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!towns.size())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Copy/Paste from TownPortalMechanics
|
||||||
|
auto skillLevel = hero->getSpellSchoolLevel(townPortal);
|
||||||
|
auto movementCost = GameConstants::BASE_MOVEMENT_COST * (skillLevel >= 3 ? 2 : 3);
|
||||||
|
|
||||||
|
if(hero->movement < movementCost)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(skillLevel < SecSkillLevel::ADVANCED)
|
||||||
|
{
|
||||||
|
const CGTownInstance * nearestTown = *vstd::minElementByFun(towns, [&](const CGTownInstance * t) -> int
|
||||||
|
{
|
||||||
|
return hero->visitablePos().dist2dSQ(t->visitablePos());
|
||||||
|
});
|
||||||
|
|
||||||
|
towns = std::vector<const CGTownInstance *>{ nearestTown };
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const CGTownInstance * targetTown : towns)
|
||||||
|
{
|
||||||
|
if(targetTown->visitingHero)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto nodeOptional = getOrCreateNode(targetTown->visitablePos(), EPathfindingLayer::LAND, srcNode->chainMask | CAST_CHAIN);
|
||||||
|
|
||||||
|
if(nodeOptional)
|
||||||
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
|
logAi->trace("Adding town portal node at %s", targetTown->name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AIPathNode * node = nodeOptional.get();
|
||||||
|
|
||||||
|
node->theNodeBefore = source.node;
|
||||||
|
node->specialAction.reset(new TownPortalAction(targetTown));
|
||||||
|
node->moveRemains = source.node->moveRemains;
|
||||||
|
|
||||||
|
neighbours.push_back(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
||||||
{
|
{
|
||||||
auto pos = destination.coord;
|
auto pos = destination.coord;
|
||||||
@ -174,12 +279,14 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
|
|||||||
if(node.turns < destinationNode->turns
|
if(node.turns < destinationNode->turns
|
||||||
|| (node.turns == destinationNode->turns && node.moveRemains >= destinationNode->moveRemains))
|
|| (node.turns == destinationNode->turns && node.moveRemains >= destinationNode->moveRemains))
|
||||||
{
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Block ineficient move %s:->%s, mask=%i, mp diff: %i",
|
"Block ineficient move %s:->%s, mask=%i, mp diff: %i",
|
||||||
source.coord.toString(),
|
source.coord.toString(),
|
||||||
destination.coord.toString(),
|
destination.coord.toString(),
|
||||||
destinationNode->chainMask,
|
destinationNode->chainMask,
|
||||||
node.moveRemains - destinationNode->moveRemains);
|
node.moveRemains - destinationNode->moveRemains);
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -113,13 +113,13 @@ public:
|
|||||||
std::vector<AIPath> getChainInfo(int3 pos, bool isOnLand) const;
|
std::vector<AIPath> getChainInfo(int3 pos, bool isOnLand) const;
|
||||||
bool isTileAccessible(int3 pos, const EPathfindingLayer layer) const;
|
bool isTileAccessible(int3 pos, const EPathfindingLayer layer) const;
|
||||||
|
|
||||||
void setHero(HeroPtr heroPtr)
|
void setHero(HeroPtr heroPtr);
|
||||||
{
|
|
||||||
hero = heroPtr.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
const CGHeroInstance * getHero() const
|
const CGHeroInstance * getHero() const
|
||||||
{
|
{
|
||||||
return hero;
|
return hero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector<CGPathNode *> & neighbours);
|
||||||
};
|
};
|
||||||
|
@ -741,9 +741,7 @@ void VCAI::showMapObjectSelectDialog(QueryID askID, const Component & icon, cons
|
|||||||
{
|
{
|
||||||
NET_EVENT_HANDLER;
|
NET_EVENT_HANDLER;
|
||||||
status.addQuery(askID, "Map object select query");
|
status.addQuery(askID, "Map object select query");
|
||||||
requestActionASAP([=](){ answerQuery(askID, 0); });
|
requestActionASAP([=](){ answerQuery(askID, selectedObject.getNum()); });
|
||||||
|
|
||||||
//TODO: Town portal destination selection goes here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCAI::saveGame(BinarySerializer & h, const int version)
|
void VCAI::saveGame(BinarySerializer & h, const int version)
|
||||||
|
@ -112,6 +112,7 @@ public:
|
|||||||
std::shared_ptr<CCallback> myCb;
|
std::shared_ptr<CCallback> myCb;
|
||||||
|
|
||||||
std::unique_ptr<boost::thread> makingTurn;
|
std::unique_ptr<boost::thread> makingTurn;
|
||||||
|
ObjectInstanceID selectedObject;
|
||||||
|
|
||||||
AIhelper * ah;
|
AIhelper * ah;
|
||||||
|
|
||||||
|
@ -710,7 +710,7 @@ std::vector < const CGTownInstance *> CPlayerSpecificInfoCallback::getTownsInfo(
|
|||||||
{
|
{
|
||||||
for(const auto & town : i.second.towns)
|
for(const auto & town : i.second.towns)
|
||||||
{
|
{
|
||||||
if (i.first==player || (isVisible(town, player) && !onlyOur))
|
if(i.first == player || (!onlyOur && isVisible(town, player)))
|
||||||
{
|
{
|
||||||
ret.push_back(town);
|
ret.push_back(town);
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,10 @@ std::vector<CGPathNode *> NodeStorage::calculateTeleportations(
|
|||||||
const CPathfinderHelper * pathfinderHelper)
|
const CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
std::vector<CGPathNode *> neighbours;
|
std::vector<CGPathNode *> neighbours;
|
||||||
|
|
||||||
|
if(!source.isNodeObjectVisitable())
|
||||||
|
return neighbours;
|
||||||
|
|
||||||
auto accessibleExits = pathfinderHelper->getTeleportExits(source);
|
auto accessibleExits = pathfinderHelper->getTeleportExits(source);
|
||||||
|
|
||||||
for(auto & neighbour : accessibleExits)
|
for(auto & neighbour : accessibleExits)
|
||||||
@ -315,7 +319,7 @@ void CPathfinder::calculatePaths()
|
|||||||
|
|
||||||
/// For now we disable teleports usage for patrol movement
|
/// For now we disable teleports usage for patrol movement
|
||||||
/// VCAI not aware about patrol and may stuck while attempt to use teleport
|
/// VCAI not aware about patrol and may stuck while attempt to use teleport
|
||||||
if(!source.isNodeObjectVisitable() || patrolState == PATROL_RADIUS)
|
if(patrolState == PATROL_RADIUS)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto teleportationNodes = config->nodeStorage->calculateTeleportations(source, config.get(), hlp.get());
|
auto teleportationNodes = config->nodeStorage->calculateTeleportations(source, config.get(), hlp.get());
|
||||||
|
Loading…
Reference in New Issue
Block a user