mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-06 09:09:40 +02:00
Remove hardcoded checks for town portal from AI code
This commit is contained in:
@@ -10,6 +10,8 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "AdventureSpellCast.h"
|
#include "AdventureSpellCast.h"
|
||||||
#include "../AIGateway.h"
|
#include "../AIGateway.h"
|
||||||
|
#include "../../../lib/spells/ISpellMechanics.h"
|
||||||
|
#include "../../../lib/spells/adventure/TownPortalEffect.h"
|
||||||
|
|
||||||
namespace NKAI
|
namespace NKAI
|
||||||
{
|
{
|
||||||
@@ -39,8 +41,9 @@ void AdventureSpellCast::accept(AIGateway * ai)
|
|||||||
if(hero->mana < hero->getSpellCost(spell))
|
if(hero->mana < hero->getSpellCost(spell))
|
||||||
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated());
|
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated());
|
||||||
|
|
||||||
|
auto townPortalEffect = spell->getAdventureMechanics().getEffectAs<TownPortalEffect>(hero);
|
||||||
|
|
||||||
if(town && spellID == SpellID::TOWN_PORTAL)
|
if(town && townPortalEffect)
|
||||||
{
|
{
|
||||||
ai->selectedObject = town->id;
|
ai->selectedObject = town->id;
|
||||||
|
|
||||||
@@ -61,7 +64,7 @@ void AdventureSpellCast::accept(AIGateway * ai)
|
|||||||
cb->waitTillRealize = true;
|
cb->waitTillRealize = true;
|
||||||
cb->castSpell(hero, spellID, tile);
|
cb->castSpell(hero, spellID, tile);
|
||||||
|
|
||||||
if(town && spellID == SpellID::TOWN_PORTAL)
|
if(town && townPortalEffect)
|
||||||
{
|
{
|
||||||
// visit town
|
// visit town
|
||||||
ai->moveHeroToTile(town->visitablePos(), hero);
|
ai->moveHeroToTile(town->visitablePos(), hero);
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
#include "../../../lib/pathfinder/CPathfinder.h"
|
#include "../../../lib/pathfinder/CPathfinder.h"
|
||||||
#include "../../../lib/pathfinder/PathfinderUtil.h"
|
#include "../../../lib/pathfinder/PathfinderUtil.h"
|
||||||
#include "../../../lib/pathfinder/PathfinderOptions.h"
|
#include "../../../lib/pathfinder/PathfinderOptions.h"
|
||||||
|
#include "../../../lib/spells/ISpellMechanics.h"
|
||||||
|
#include "../../../lib/spells/adventure/TownPortalEffect.h"
|
||||||
#include "../../../lib/IGameSettings.h"
|
#include "../../../lib/IGameSettings.h"
|
||||||
#include "../../../lib/CPlayerState.h"
|
#include "../../../lib/CPlayerState.h"
|
||||||
|
|
||||||
@@ -1069,22 +1071,17 @@ struct TownPortalFinder
|
|||||||
SpellID spellID;
|
SpellID spellID;
|
||||||
const CSpell * townPortal;
|
const CSpell * townPortal;
|
||||||
|
|
||||||
TownPortalFinder(
|
TownPortalFinder(const ChainActor * actor, const std::vector<CGPathNode *> & initialNodes, std::vector<const CGTownInstance *> targetTowns, AINodeStorage * nodeStorage, SpellID spellID)
|
||||||
const ChainActor * actor,
|
: actor(actor)
|
||||||
const std::vector<CGPathNode *> & initialNodes,
|
, initialNodes(initialNodes)
|
||||||
std::vector<const CGTownInstance *> targetTowns,
|
, hero(actor->hero)
|
||||||
AINodeStorage * nodeStorage)
|
, targetTowns(targetTowns)
|
||||||
:actor(actor), initialNodes(initialNodes), hero(actor->hero),
|
, nodeStorage(nodeStorage)
|
||||||
targetTowns(targetTowns), nodeStorage(nodeStorage)
|
, spellID(spellID)
|
||||||
|
, townPortal(spellID.toSpell())
|
||||||
{
|
{
|
||||||
spellID = SpellID::TOWN_PORTAL;
|
auto townPortalEffect = townPortal->getAdventureMechanics().getEffectAs<TownPortalEffect>(hero);
|
||||||
townPortal = spellID.toSpell();
|
movementNeeded = townPortalEffect->getMovementPointsRequired();
|
||||||
|
|
||||||
// TODO: Copy/Paste from TownPortalMechanics
|
|
||||||
townPortalSkillLevel = MasteryLevel::Type(hero->getSpellSchoolLevel(townPortal));
|
|
||||||
|
|
||||||
int baseCost = hero->cb->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
|
|
||||||
movementNeeded = baseCost * (townPortalSkillLevel >= MasteryLevel::EXPERT ? 2 : 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool actorCanCastTownPortal()
|
bool actorCanCastTownPortal()
|
||||||
@@ -1151,7 +1148,7 @@ struct TownPortalFinder
|
|||||||
DO_NOT_SAVE_TO_COMMITTED_TILES);
|
DO_NOT_SAVE_TO_COMMITTED_TILES);
|
||||||
|
|
||||||
node->theNodeBefore = bestNode;
|
node->theNodeBefore = bestNode;
|
||||||
node->addSpecialAction(std::make_shared<AIPathfinding::TownPortalAction>(targetTown));
|
node->addSpecialAction(std::make_shared<AIPathfinding::TownPortalAction>(targetTown, spellID));
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeOptional;
|
return nodeOptional;
|
||||||
@@ -1177,10 +1174,21 @@ void AINodeStorage::calculateTownPortal(
|
|||||||
return; // no towns no need to run loop further
|
return; // no towns no need to run loop further
|
||||||
}
|
}
|
||||||
|
|
||||||
TownPortalFinder townPortalFinder(actor, initialNodes, towns, this);
|
for (const auto & spell : LIBRARY->spellh->objects)
|
||||||
|
|
||||||
if(townPortalFinder.actorCanCastTownPortal())
|
|
||||||
{
|
{
|
||||||
|
if (!spell || !spell->isAdventure())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto townPortalEffect = spell->getAdventureMechanics().getEffectAs<TownPortalEffect>(actor->hero);
|
||||||
|
|
||||||
|
if (!townPortalEffect)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TownPortalFinder townPortalFinder(actor, initialNodes, towns, this, spell->id);
|
||||||
|
|
||||||
|
if(!townPortalFinder.actorCanCastTownPortal())
|
||||||
|
continue;
|
||||||
|
|
||||||
for(const CGTownInstance * targetTown : towns)
|
for(const CGTownInstance * targetTown : towns)
|
||||||
{
|
{
|
||||||
if(targetTown->getVisitingHero()
|
if(targetTown->getVisitingHero()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ using namespace AIPathfinding;
|
|||||||
|
|
||||||
void TownPortalAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
void TownPortalAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
auto goal = Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL);
|
auto goal = Goals::AdventureSpellCast(hero, usedSpell);
|
||||||
|
|
||||||
goal.town = target;
|
goal.town = target;
|
||||||
goal.tile = target->visitablePos();
|
goal.tile = target->visitablePos();
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ namespace AIPathfinding
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const CGTownInstance * target;
|
const CGTownInstance * target;
|
||||||
|
SpellID usedSpell;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TownPortalAction(const CGTownInstance * target)
|
TownPortalAction(const CGTownInstance * target, SpellID usedSpell)
|
||||||
:target(target)
|
:target(target)
|
||||||
|
,usedSpell(usedSpell)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
#include "../FuzzyHelper.h"
|
#include "../FuzzyHelper.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../../../lib/mapObjects/CGTownInstance.h"
|
#include "../../../lib/mapObjects/CGTownInstance.h"
|
||||||
|
#include "../../../lib/spells/ISpellMechanics.h"
|
||||||
|
#include "../../../lib/spells/adventure/TownPortalEffect.h"
|
||||||
|
|
||||||
using namespace Goals;
|
using namespace Goals;
|
||||||
|
|
||||||
@@ -39,7 +41,9 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve()
|
|||||||
if(hero->mana < hero->getSpellCost(spell))
|
if(hero->mana < hero->getSpellCost(spell))
|
||||||
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated());
|
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated());
|
||||||
|
|
||||||
if(spellID == SpellID::TOWN_PORTAL && town && town->getVisitingHero())
|
auto townPortalEffect = spell->getAdventureMechanics().getEffectAs<TownPortalEffect>(hero.h);
|
||||||
|
|
||||||
|
if(townPortalEffect && town && town->getVisitingHero())
|
||||||
throw cannotFulfillGoalException("The town is already occupied by " + town->getVisitingHero()->getNameTranslated());
|
throw cannotFulfillGoalException("The town is already occupied by " + town->getVisitingHero()->getNameTranslated());
|
||||||
|
|
||||||
return iAmElementar();
|
return iAmElementar();
|
||||||
@@ -47,7 +51,9 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve()
|
|||||||
|
|
||||||
void AdventureSpellCast::accept(VCAI * ai)
|
void AdventureSpellCast::accept(VCAI * ai)
|
||||||
{
|
{
|
||||||
if(town && spellID == SpellID::TOWN_PORTAL)
|
auto townPortalEffect = spellID.toSpell()->getAdventureMechanics().getEffectAs<TownPortalEffect>(hero.h);
|
||||||
|
|
||||||
|
if(town && townPortalEffect)
|
||||||
{
|
{
|
||||||
ai->selectedObject = town->id;
|
ai->selectedObject = town->id;
|
||||||
}
|
}
|
||||||
@@ -57,7 +63,7 @@ void AdventureSpellCast::accept(VCAI * ai)
|
|||||||
cb->waitTillRealize = true;
|
cb->waitTillRealize = true;
|
||||||
cb->castSpell(hero.h, spellID, tile);
|
cb->castSpell(hero.h, spellID, tile);
|
||||||
|
|
||||||
if(town && spellID == SpellID::TOWN_PORTAL)
|
if(town && townPortalEffect)
|
||||||
{
|
{
|
||||||
// visit town
|
// visit town
|
||||||
ai->moveHeroToTile(town->visitablePos(), hero);
|
ai->moveHeroToTile(town->visitablePos(), hero);
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
#include "../../../lib/pathfinder/CPathfinder.h"
|
#include "../../../lib/pathfinder/CPathfinder.h"
|
||||||
#include "../../../lib/pathfinder/PathfinderOptions.h"
|
#include "../../../lib/pathfinder/PathfinderOptions.h"
|
||||||
#include "../../../lib/pathfinder/PathfinderUtil.h"
|
#include "../../../lib/pathfinder/PathfinderUtil.h"
|
||||||
|
#include "../../../lib/spells/ISpellMechanics.h"
|
||||||
|
#include "../../../lib/spells/adventure/TownPortalEffect.h"
|
||||||
#include "../../../lib/IGameSettings.h"
|
#include "../../../lib/IGameSettings.h"
|
||||||
#include "../../../lib/CPlayerState.h"
|
#include "../../../lib/CPlayerState.h"
|
||||||
|
|
||||||
@@ -225,12 +227,21 @@ void AINodeStorage::calculateTownPortalTeleportations(
|
|||||||
const PathNodeInfo & source,
|
const PathNodeInfo & source,
|
||||||
std::vector<CGPathNode *> & neighbours)
|
std::vector<CGPathNode *> & neighbours)
|
||||||
{
|
{
|
||||||
SpellID spellID = SpellID::TOWN_PORTAL;
|
|
||||||
const CSpell * townPortal = spellID.toSpell();
|
|
||||||
auto srcNode = getAINode(source.node);
|
auto srcNode = getAINode(source.node);
|
||||||
|
|
||||||
if(hero->canCastThisSpell(townPortal) && hero->mana >= hero->getSpellCost(townPortal))
|
for (const auto & spell : LIBRARY->spellh->objects)
|
||||||
{
|
{
|
||||||
|
if (!spell || !spell->isAdventure())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto townPortalEffect = spell->getAdventureMechanics().getEffectAs<TownPortalEffect>(hero);
|
||||||
|
|
||||||
|
if (!townPortalEffect)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!hero->canCastThisSpell(spell.get()) || hero->mana < hero->getSpellCost(spell.get()))
|
||||||
|
continue;
|
||||||
|
|
||||||
auto towns = cb->getTownsInfo(false);
|
auto towns = cb->getTownsInfo(false);
|
||||||
|
|
||||||
vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
|
vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
|
||||||
@@ -238,22 +249,15 @@ void AINodeStorage::calculateTownPortalTeleportations(
|
|||||||
return cb->getPlayerRelations(hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
|
return cb->getPlayerRelations(hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!towns.size())
|
if(towns.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(hero->movementPointsRemaining() < townPortalEffect->getMovementPointsRequired())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Copy/Paste from TownPortalMechanics
|
if(!townPortalEffect->townSelectionAllowed())
|
||||||
auto skillLevel = hero->getSpellSchoolLevel(townPortal);
|
|
||||||
int baseCost = hero->cb->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
|
|
||||||
auto movementCost = baseCost * (skillLevel >= 3 ? 2 : 3);
|
|
||||||
|
|
||||||
if(hero->movementPointsRemaining() < movementCost)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(skillLevel < MasteryLevel::ADVANCED)
|
|
||||||
{
|
{
|
||||||
const CGTownInstance * nearestTown = *vstd::minElementByFun(towns, [&](const CGTownInstance * t) -> int
|
const CGTownInstance * nearestTown = *vstd::minElementByFun(towns, [&](const CGTownInstance * t) -> int
|
||||||
{
|
{
|
||||||
@@ -279,7 +283,7 @@ void AINodeStorage::calculateTownPortalTeleportations(
|
|||||||
AIPathNode * node = nodeOptional.value();
|
AIPathNode * node = nodeOptional.value();
|
||||||
|
|
||||||
node->theNodeBefore = source.node;
|
node->theNodeBefore = source.node;
|
||||||
node->specialAction.reset(new AIPathfinding::TownPortalAction(targetTown));
|
node->specialAction.reset(new AIPathfinding::TownPortalAction(targetTown, spell->id));
|
||||||
node->moveRemains = source.node->moveRemains;
|
node->moveRemains = source.node->moveRemains;
|
||||||
|
|
||||||
neighbours.push_back(node);
|
neighbours.push_back(node);
|
||||||
|
|||||||
@@ -19,5 +19,5 @@ Goals::TSubgoal TownPortalAction::whatToDo(const HeroPtr & hero) const
|
|||||||
{
|
{
|
||||||
const CGTownInstance * targetTown = target; // const pointer is not allowed in settown
|
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()));
|
return Goals::sptr(Goals::AdventureSpellCast(hero, spellToUse).settown(targetTown).settile(targetTown->visitablePos()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ namespace AIPathfinding
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const CGTownInstance * target;
|
const CGTownInstance * target;
|
||||||
|
SpellID spellToUse;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TownPortalAction(const CGTownInstance * target)
|
TownPortalAction(const CGTownInstance * target, SpellID spellToUse)
|
||||||
:target(target)
|
:target(target)
|
||||||
|
,spellToUse(spellToUse)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -856,16 +856,16 @@ public:
|
|||||||
NONE = -1,
|
NONE = -1,
|
||||||
|
|
||||||
// Adventure map spells
|
// Adventure map spells
|
||||||
SUMMON_BOAT = 0,
|
SUMMON_BOAT [[deprecated]] = 0,
|
||||||
SCUTTLE_BOAT = 1,
|
SCUTTLE_BOAT [[deprecated]] = 1,
|
||||||
VISIONS = 2,
|
VISIONS [[deprecated]] = 2,
|
||||||
VIEW_EARTH = 3,
|
VIEW_EARTH [[deprecated]] = 3,
|
||||||
DISGUISE = 4,
|
DISGUISE [[deprecated]] = 4,
|
||||||
VIEW_AIR = 5,
|
VIEW_AIR [[deprecated]] = 5,
|
||||||
FLY = 6,
|
FLY [[deprecated]] = 6,
|
||||||
WATER_WALK = 7,
|
WATER_WALK [[deprecated]] = 7,
|
||||||
DIMENSION_DOOR = 8,
|
DIMENSION_DOOR [[deprecated]] = 8,
|
||||||
TOWN_PORTAL = 9,
|
TOWN_PORTAL [[deprecated]] = 9,
|
||||||
|
|
||||||
// Combat spells
|
// Combat spells
|
||||||
QUICKSAND = 10,
|
QUICKSAND = 10,
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ class TownPortalEffect final : public IAdventureSpellEffect
|
|||||||
public:
|
public:
|
||||||
TownPortalEffect(const CSpell * s, const JsonNode & config);
|
TownPortalEffect(const CSpell * s, const JsonNode & config);
|
||||||
|
|
||||||
|
bool getMovementPointsRequired() const { return movementPointsRequired; }
|
||||||
|
bool townSelectionAllowed() const { return allowTownSelection; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override;
|
ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override;
|
||||||
ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const AdventureSpellMechanics & mechanics) const override;
|
ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const AdventureSpellMechanics & mechanics) const override;
|
||||||
|
|||||||
Reference in New Issue
Block a user