mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-03 13:01:33 +02:00
Merge pull request #2884 from vcmi/nkai-air-water-walking
NKAI: water and air walking
This commit is contained in:
commit
20a944c551
@ -72,7 +72,13 @@ void DangerHitMapAnalyzer::updateHitMap()
|
||||
if(ai->cb->getPlayerRelations(ai->playerID, pair.first) != PlayerRelations::ENEMIES)
|
||||
continue;
|
||||
|
||||
ai->pathfinder->updatePaths(pair.second, PathfinderSettings());
|
||||
PathfinderSettings ps;
|
||||
|
||||
ps.mainTurnDistanceLimit = 10;
|
||||
ps.scoutTurnDistanceLimit = 10;
|
||||
ps.useHeroChain = false;
|
||||
|
||||
ai->pathfinder->updatePaths(pair.second, ps);
|
||||
|
||||
boost::this_thread::interruption_point();
|
||||
|
||||
|
@ -190,6 +190,41 @@ bool HeroManager::heroCapReached() const
|
||||
|| heroCount >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP);
|
||||
}
|
||||
|
||||
float HeroManager::getMagicStrength(const CGHeroInstance * hero) const
|
||||
{
|
||||
auto hasFly = hero->spellbookContainsSpell(SpellID::FLY);
|
||||
auto hasTownPortal = hero->spellbookContainsSpell(SpellID::TOWN_PORTAL);
|
||||
auto manaLimit = hero->manaLimit();
|
||||
auto spellPower = hero->getPrimSkillLevel(PrimarySkill::SPELL_POWER);
|
||||
auto hasEarth = hero->getSpellSchoolLevel(SpellID(SpellID::TOWN_PORTAL).toSpell()) > 0;
|
||||
|
||||
auto score = 0.0f;
|
||||
|
||||
for(auto spellId : hero->getSpellsInSpellbook())
|
||||
{
|
||||
auto spell = spellId.toSpell();
|
||||
auto schoolLevel = hero->getSpellSchoolLevel(spell);
|
||||
|
||||
score += (spell->getLevel() + 1) * (schoolLevel + 1) * 0.05f;
|
||||
}
|
||||
|
||||
vstd::amin(score, 1);
|
||||
|
||||
score *= std::min(1.0f, spellPower / 10.0f);
|
||||
|
||||
if(hasFly)
|
||||
score += 0.3f;
|
||||
|
||||
if(hasTownPortal && hasEarth)
|
||||
score += 0.6f;
|
||||
|
||||
vstd::amin(score, 1);
|
||||
|
||||
score *= std::min(1.0f, manaLimit / 100.0f);
|
||||
|
||||
return std::min(score, 1.0f);
|
||||
}
|
||||
|
||||
bool HeroManager::canRecruitHero(const CGTownInstance * town) const
|
||||
{
|
||||
if(!town)
|
||||
|
@ -34,6 +34,7 @@ public:
|
||||
virtual bool heroCapReached() const = 0;
|
||||
virtual const CGHeroInstance * findHeroWithGrail() const = 0;
|
||||
virtual const CGHeroInstance * findWeakHeroToDismiss(uint64_t armyLimit) const = 0;
|
||||
virtual float getMagicStrength(const CGHeroInstance * hero) const = 0;
|
||||
};
|
||||
|
||||
class DLL_EXPORT ISecondarySkillRule
|
||||
@ -76,6 +77,7 @@ public:
|
||||
bool heroCapReached() const override;
|
||||
const CGHeroInstance * findHeroWithGrail() const override;
|
||||
const CGHeroInstance * findWeakHeroToDismiss(uint64_t armyLimit) const override;
|
||||
float getMagicStrength(const CGHeroInstance * hero) const override;
|
||||
|
||||
private:
|
||||
float evaluateFightingStrength(const CGHeroInstance * hero) const;
|
||||
|
@ -93,9 +93,14 @@ std::vector<std::shared_ptr<ObjectCluster>> ObjectClusterizer::getLockedClusters
|
||||
const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) const
|
||||
{
|
||||
for(auto node = path.nodes.rbegin(); node != path.nodes.rend(); node++)
|
||||
{
|
||||
std::vector<const CGObjectInstance *> blockers = {};
|
||||
|
||||
if(node->layer == EPathfindingLayer::LAND || node->layer == EPathfindingLayer::SAIL)
|
||||
{
|
||||
auto guardPos = ai->cb->getGuardingCreaturePosition(node->coord);
|
||||
auto blockers = ai->cb->getVisitableObjs(node->coord);
|
||||
|
||||
blockers = ai->cb->getVisitableObjs(node->coord);
|
||||
|
||||
if(guardPos.valid())
|
||||
{
|
||||
@ -106,6 +111,7 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
|
||||
blockers.insert(blockers.begin(), guard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(node->specialAction && node->actionIsBlocked)
|
||||
{
|
||||
|
70
AI/Nullkiller/Behaviors/StayAtTownBehavior.cpp
Normal file
70
AI/Nullkiller/Behaviors/StayAtTownBehavior.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* StartupBehavior.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 "StayAtTownBehavior.h"
|
||||
#include "../AIGateway.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../Goals/StayAtTown.h"
|
||||
#include "../Goals/Composition.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "lib/mapObjects/MapObjects.h" //for victory conditions
|
||||
#include "../Engine/Nullkiller.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
std::string StayAtTownBehavior::toString() const
|
||||
{
|
||||
return "StayAtTownBehavior";
|
||||
}
|
||||
|
||||
Goals::TGoalVec StayAtTownBehavior::decompose() const
|
||||
{
|
||||
Goals::TGoalVec tasks;
|
||||
auto towns = cb->getTownsInfo();
|
||||
|
||||
if(!towns.size())
|
||||
return tasks;
|
||||
|
||||
for(auto town : towns)
|
||||
{
|
||||
if(!town->hasBuilt(BuildingID::MAGES_GUILD_1))
|
||||
continue;
|
||||
|
||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(town->visitablePos());
|
||||
|
||||
for(auto & path : paths)
|
||||
{
|
||||
if(town->visitingHero && town->visitingHero.get() != path.targetHero)
|
||||
continue;
|
||||
|
||||
if(path.turn() == 0 && !path.getFirstBlockedAction() && path.exchangeCount <= 1)
|
||||
{
|
||||
if(path.targetHero->mana == path.targetHero->manaLimit())
|
||||
continue;
|
||||
|
||||
Composition stayAtTown;
|
||||
|
||||
stayAtTown.addNextSequence({
|
||||
sptr(ExecuteHeroChain(path)),
|
||||
sptr(StayAtTown(town, path))
|
||||
});
|
||||
|
||||
tasks.push_back(sptr(stayAtTown));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
}
|
39
AI/Nullkiller/Behaviors/StayAtTownBehavior.h
Normal file
39
AI/Nullkiller/Behaviors/StayAtTownBehavior.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* StayAtTownBehavior.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 "lib/VCMI_Lib.h"
|
||||
#include "../Goals/CGoal.h"
|
||||
#include "../AIUtility.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
namespace Goals
|
||||
{
|
||||
class StayAtTownBehavior : public CGoal<StayAtTownBehavior>
|
||||
{
|
||||
public:
|
||||
StayAtTownBehavior()
|
||||
:CGoal(STAY_AT_TOWN_BEHAVIOR)
|
||||
{
|
||||
}
|
||||
|
||||
virtual TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual bool operator==(const StayAtTownBehavior & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -9,6 +9,7 @@ set(Nullkiller_SRCS
|
||||
Pathfinding/Actions/BuyArmyAction.cpp
|
||||
Pathfinding/Actions/BoatActions.cpp
|
||||
Pathfinding/Actions/TownPortalAction.cpp
|
||||
Pathfinding/Actions/AdventureSpellCastMovementActions.cpp
|
||||
Pathfinding/Rules/AILayerTransitionRule.cpp
|
||||
Pathfinding/Rules/AIMovementAfterDestinationRule.cpp
|
||||
Pathfinding/Rules/AIMovementToDestinationRule.cpp
|
||||
@ -34,6 +35,7 @@ set(Nullkiller_SRCS
|
||||
Goals/ExecuteHeroChain.cpp
|
||||
Goals/ExchangeSwapTownHeroes.cpp
|
||||
Goals/CompleteQuest.cpp
|
||||
Goals/StayAtTown.cpp
|
||||
Markers/ArmyUpgrade.cpp
|
||||
Markers/HeroExchange.cpp
|
||||
Markers/UnlockCluster.cpp
|
||||
@ -52,6 +54,7 @@ set(Nullkiller_SRCS
|
||||
Behaviors/BuildingBehavior.cpp
|
||||
Behaviors/GatherArmyBehavior.cpp
|
||||
Behaviors/ClusterBehavior.cpp
|
||||
Behaviors/StayAtTownBehavior.cpp
|
||||
Helpers/ArmyFormation.cpp
|
||||
AIGateway.cpp
|
||||
)
|
||||
@ -69,6 +72,7 @@ set(Nullkiller_HEADERS
|
||||
Pathfinding/Actions/BuyArmyAction.h
|
||||
Pathfinding/Actions/BoatActions.h
|
||||
Pathfinding/Actions/TownPortalAction.h
|
||||
Pathfinding/Actions/AdventureSpellCastMovementActions.h
|
||||
Pathfinding/Rules/AILayerTransitionRule.h
|
||||
Pathfinding/Rules/AIMovementAfterDestinationRule.h
|
||||
Pathfinding/Rules/AIMovementToDestinationRule.h
|
||||
@ -97,6 +101,7 @@ set(Nullkiller_HEADERS
|
||||
Goals/ExchangeSwapTownHeroes.h
|
||||
Goals/CompleteQuest.h
|
||||
Goals/Goals.h
|
||||
Goals/StayAtTown.h
|
||||
Markers/ArmyUpgrade.h
|
||||
Markers/HeroExchange.h
|
||||
Markers/UnlockCluster.h
|
||||
@ -115,6 +120,7 @@ set(Nullkiller_HEADERS
|
||||
Behaviors/BuildingBehavior.h
|
||||
Behaviors/GatherArmyBehavior.h
|
||||
Behaviors/ClusterBehavior.h
|
||||
Behaviors/StayAtTownBehavior.h
|
||||
Helpers/ArmyFormation.h
|
||||
AIGateway.h
|
||||
)
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "../Behaviors/BuildingBehavior.h"
|
||||
#include "../Behaviors/GatherArmyBehavior.h"
|
||||
#include "../Behaviors/ClusterBehavior.h"
|
||||
#include "../Behaviors/StayAtTownBehavior.h"
|
||||
#include "../Goals/Invalid.h"
|
||||
#include "../Goals/Composition.h"
|
||||
|
||||
@ -262,7 +263,8 @@ void Nullkiller::makeTurn()
|
||||
choseBestTask(sptr(CaptureObjectsBehavior()), 1),
|
||||
choseBestTask(sptr(ClusterBehavior()), MAX_DEPTH),
|
||||
choseBestTask(sptr(DefenceBehavior()), MAX_DEPTH),
|
||||
choseBestTask(sptr(GatherArmyBehavior()), MAX_DEPTH)
|
||||
choseBestTask(sptr(GatherArmyBehavior()), MAX_DEPTH),
|
||||
choseBestTask(sptr(StayAtTownBehavior()), MAX_DEPTH)
|
||||
};
|
||||
|
||||
if(cb->getDate(Date::DAY) == 1)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "../../../lib/filesystem/Filesystem.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "../Goals/BuildThis.h"
|
||||
#include "../Goals/StayAtTown.h"
|
||||
#include "../Goals/ExchangeSwapTownHeroes.h"
|
||||
#include "../Goals/DismissHero.h"
|
||||
#include "../Markers/UnlockCluster.h"
|
||||
@ -309,6 +310,9 @@ uint64_t RewardEvaluator::getArmyReward(
|
||||
: 0;
|
||||
case Obj::PANDORAS_BOX:
|
||||
return 5000;
|
||||
case Obj::MAGIC_WELL:
|
||||
case Obj::MAGIC_SPRING:
|
||||
return getManaRecoveryArmyReward(hero);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -450,6 +454,11 @@ uint64_t RewardEvaluator::townArmyGrowth(const CGTownInstance * town) const
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t RewardEvaluator::getManaRecoveryArmyReward(const CGHeroInstance * hero) const
|
||||
{
|
||||
return ai->heroManager->getMagicStrength(hero) * 10000 * (1.0f - std::sqrt(static_cast<float>(hero->mana) / hero->manaLimit()));
|
||||
}
|
||||
|
||||
float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) const
|
||||
{
|
||||
if(!target)
|
||||
@ -693,6 +702,22 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class StayAtTownManaRecoveryEvaluator : public IEvaluationContextBuilder
|
||||
{
|
||||
public:
|
||||
virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal task) const override
|
||||
{
|
||||
if(task->goalType != Goals::STAY_AT_TOWN)
|
||||
return;
|
||||
|
||||
Goals::StayAtTown & stayAtTown = dynamic_cast<Goals::StayAtTown &>(*task);
|
||||
|
||||
evaluationContext.armyReward += evaluationContext.evaluator.getManaRecoveryArmyReward(stayAtTown.getHero().get());
|
||||
evaluationContext.movementCostByRole[evaluationContext.heroRole] += stayAtTown.getMovementWasted();
|
||||
evaluationContext.movementCost += stayAtTown.getMovementWasted();
|
||||
}
|
||||
};
|
||||
|
||||
void addTileDanger(EvaluationContext & evaluationContext, const int3 & tile, uint8_t turn, uint64_t ourStrength)
|
||||
{
|
||||
HitMapInfo enemyDanger = evaluationContext.evaluator.getEnemyHeroDanger(tile, turn);
|
||||
@ -998,6 +1023,7 @@ PriorityEvaluator::PriorityEvaluator(const Nullkiller * ai)
|
||||
evaluationContextBuilders.push_back(std::make_shared<DefendTownEvaluator>());
|
||||
evaluationContextBuilders.push_back(std::make_shared<ExchangeSwapTownHeroesContextBuilder>());
|
||||
evaluationContextBuilders.push_back(std::make_shared<DismissHeroContextBuilder>(ai));
|
||||
evaluationContextBuilders.push_back(std::make_shared<StayAtTownManaRecoveryEvaluator>());
|
||||
}
|
||||
|
||||
EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgoal goal) const
|
||||
|
@ -49,6 +49,7 @@ public:
|
||||
uint64_t getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const;
|
||||
const HitMapInfo & getEnemyHeroDanger(const int3 & tile, uint8_t turn) const;
|
||||
uint64_t townArmyGrowth(const CGTownInstance * town) const;
|
||||
uint64_t getManaRecoveryArmyReward(const CGHeroInstance * hero) const;
|
||||
};
|
||||
|
||||
struct DLL_EXPORT EvaluationContext
|
||||
|
@ -71,7 +71,9 @@ namespace Goals
|
||||
ARMY_UPGRADE,
|
||||
DEFEND_TOWN,
|
||||
CAPTURE_OBJECT,
|
||||
SAVE_RESOURCES
|
||||
SAVE_RESOURCES,
|
||||
STAY_AT_TOWN_BEHAVIOR,
|
||||
STAY_AT_TOWN
|
||||
};
|
||||
|
||||
class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
|
||||
|
52
AI/Nullkiller/Goals/StayAtTown.cpp
Normal file
52
AI/Nullkiller/Goals/StayAtTown.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* ArmyUpgrade.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 "StayAtTown.h"
|
||||
#include "../AIGateway.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../AIUtility.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
StayAtTown::StayAtTown(const CGTownInstance * town, AIPath & path)
|
||||
: ElementarGoal(Goals::STAY_AT_TOWN)
|
||||
{
|
||||
sethero(path.targetHero);
|
||||
settown(town);
|
||||
movementWasted = static_cast<float>(hero->movementPointsRemaining()) / hero->movementPointsLimit(!hero->boat) - path.movementCost();
|
||||
vstd::amax(movementWasted, 0);
|
||||
}
|
||||
|
||||
bool StayAtTown::operator==(const StayAtTown & other) const
|
||||
{
|
||||
return hero == other.hero && town == other.town;
|
||||
}
|
||||
|
||||
std::string StayAtTown::toString() const
|
||||
{
|
||||
return "Stay at town " + town->getNameTranslated()
|
||||
+ " hero " + hero->getNameTranslated()
|
||||
+ ", mana: " + std::to_string(hero->mana);
|
||||
}
|
||||
|
||||
void StayAtTown::accept(AIGateway * ai)
|
||||
{
|
||||
if(hero->visitedTown != town)
|
||||
{
|
||||
logAi->error("Hero %s expected visiting town %s", hero->getNameTranslated(), town->getNameTranslated());
|
||||
}
|
||||
|
||||
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::DEFENCE);
|
||||
}
|
||||
|
||||
}
|
36
AI/Nullkiller/Goals/StayAtTown.h
Normal file
36
AI/Nullkiller/Goals/StayAtTown.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* ArmyUpgrade.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 "../Goals/CGoal.h"
|
||||
#include "../Pathfinding/AINodeStorage.h"
|
||||
#include "../Analyzers/ArmyManager.h"
|
||||
#include "../Analyzers/DangerHitMapAnalyzer.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT StayAtTown : public ElementarGoal<StayAtTown>
|
||||
{
|
||||
private:
|
||||
float movementWasted;
|
||||
|
||||
public:
|
||||
StayAtTown(const CGTownInstance * town, AIPath & path);
|
||||
|
||||
virtual bool operator==(const StayAtTown & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
void accept(AIGateway * ai) override;
|
||||
float getMovementWasted() const { return movementWasted; }
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -279,9 +279,10 @@ void AINodeStorage::commit(
|
||||
|
||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
||||
logAi->trace(
|
||||
"Commited %s -> %s, cost: %f, turn: %s, mp: %d, hero: %s, mask: %x, army: %lld",
|
||||
"Commited %s -> %s, layer: %d, cost: %f, turn: %s, mp: %d, hero: %s, mask: %x, army: %lld",
|
||||
source->coord.toString(),
|
||||
destination->coord.toString(),
|
||||
destination->layer,
|
||||
destination->getCost(),
|
||||
std::to_string(destination->turns),
|
||||
destination->moveRemains,
|
||||
@ -1343,6 +1344,7 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int pa
|
||||
pathNode.coord = node->coord;
|
||||
pathNode.parentIndex = parentIndex;
|
||||
pathNode.actionIsBlocked = false;
|
||||
pathNode.layer = node->layer;
|
||||
|
||||
if(pathNode.specialAction)
|
||||
{
|
||||
|
@ -45,7 +45,7 @@ struct AIPathNode : public CGPathNode
|
||||
{
|
||||
uint64_t danger;
|
||||
uint64_t armyLoss;
|
||||
uint32_t manaCost;
|
||||
int32_t manaCost;
|
||||
const AIPathNode * chainOther;
|
||||
std::shared_ptr<const SpecialAction> specialAction;
|
||||
const ChainActor * actor;
|
||||
@ -65,6 +65,7 @@ struct AIPathNodeInfo
|
||||
float cost;
|
||||
uint8_t turns;
|
||||
int3 coord;
|
||||
EPathfindingLayer layer;
|
||||
uint64_t danger;
|
||||
const CGHeroInstance * targetHero;
|
||||
int parentIndex;
|
||||
|
@ -44,6 +44,7 @@ namespace AIPathfinding
|
||||
std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage)), aiNodeStorage(nodeStorage)
|
||||
{
|
||||
options.canUseCast = true;
|
||||
}
|
||||
|
||||
AIPathfinderConfig::~AIPathfinderConfig() = default;
|
||||
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* AdventureSpellCastMovementActions.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 "../../AIGateway.h"
|
||||
#include "../../Goals/AdventureSpellCast.h"
|
||||
#include "../../Goals/CaptureObject.h"
|
||||
#include "../../Goals/Invalid.h"
|
||||
#include "../../Goals/BuildBoat.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
#include "AdventureSpellCastMovementActions.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
AdventureCastAction::AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero)
|
||||
:spellToCast(spellToCast), hero(hero)
|
||||
{
|
||||
manaCost = hero->getSpellCost(spellToCast.toSpell());
|
||||
}
|
||||
|
||||
WaterWalkingAction::WaterWalkingAction(const CGHeroInstance * hero)
|
||||
:AdventureCastAction(SpellID::WATER_WALK, hero)
|
||||
{ }
|
||||
|
||||
AirWalkingAction::AirWalkingAction(const CGHeroInstance * hero)
|
||||
: AdventureCastAction(SpellID::FLY, hero)
|
||||
{
|
||||
}
|
||||
|
||||
void AdventureCastAction::applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstMode,
|
||||
const AIPathNode * srcNode) const
|
||||
{
|
||||
dstMode->manaCost = srcNode->manaCost + manaCost;
|
||||
dstMode->theNodeBefore = source.node;
|
||||
}
|
||||
|
||||
void AdventureCastAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
assert(hero == this->hero);
|
||||
|
||||
Goals::AdventureSpellCast(hero, spellToCast).accept(ai);
|
||||
}
|
||||
|
||||
bool AdventureCastAction::canAct(const AIPathNode * source) const
|
||||
{
|
||||
assert(hero == this->hero);
|
||||
|
||||
auto hero = source->actor->hero;
|
||||
|
||||
#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 + manaCost;
|
||||
}
|
||||
|
||||
std::string AdventureCastAction::toString() const
|
||||
{
|
||||
return "Cast " + spellToCast.toSpell()->getNameTranslated() + " by " + hero->getNameTranslated();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* AdventureSpellCastMovementActions.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 "SpecialAction.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class AdventureCastAction : public SpecialAction
|
||||
{
|
||||
private:
|
||||
SpellID spellToCast;
|
||||
const CGHeroInstance * hero;
|
||||
int manaCost;
|
||||
|
||||
public:
|
||||
AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero);
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual void applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstMode,
|
||||
const AIPathNode * srcNode) const override;
|
||||
|
||||
virtual bool canAct(const AIPathNode * source) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
};
|
||||
|
||||
class WaterWalkingAction : public AdventureCastAction
|
||||
{
|
||||
public:
|
||||
WaterWalkingAction(const CGHeroInstance * hero);
|
||||
};
|
||||
|
||||
class AirWalkingAction : public AdventureCastAction
|
||||
{
|
||||
public:
|
||||
AirWalkingAction(const CGHeroInstance * hero);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -114,7 +114,7 @@ namespace AIPathfinding
|
||||
source->manaCost);
|
||||
#endif
|
||||
|
||||
return hero->mana >= (si32)(source->manaCost + getManaCost(hero));
|
||||
return hero->mana >= source->manaCost + getManaCost(hero);
|
||||
}
|
||||
|
||||
std::string SummonBoatAction::toString() const
|
||||
@ -122,7 +122,7 @@ namespace AIPathfinding
|
||||
return "Summon Boat";
|
||||
}
|
||||
|
||||
uint32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const
|
||||
int32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const
|
||||
{
|
||||
SpellID summonBoat = SpellID::SUMMON_BOAT;
|
||||
|
||||
|
@ -20,8 +20,6 @@ namespace AIPathfinding
|
||||
{
|
||||
class VirtualBoatAction : public SpecialAction
|
||||
{
|
||||
public:
|
||||
virtual const ChainActor * getActor(const ChainActor * sourceActor) const = 0;
|
||||
};
|
||||
|
||||
class SummonBoatAction : public VirtualBoatAction
|
||||
@ -43,7 +41,7 @@ namespace AIPathfinding
|
||||
virtual std::string toString() const override;
|
||||
|
||||
private:
|
||||
uint32_t getManaCost(const CGHeroInstance * hero) const;
|
||||
int32_t getManaCost(const CGHeroInstance * hero) const;
|
||||
};
|
||||
|
||||
class BuildBoatAction : public VirtualBoatAction
|
||||
|
@ -22,6 +22,7 @@ namespace NKAI
|
||||
{
|
||||
|
||||
struct AIPathNode;
|
||||
class ChainActor;
|
||||
|
||||
class SpecialAction
|
||||
{
|
||||
@ -54,6 +55,11 @@ public:
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual const ChainActor * getActor(const ChainActor * sourceActor) const
|
||||
{
|
||||
return sourceActor;
|
||||
}
|
||||
};
|
||||
|
||||
class CompositeAction : public SpecialAction
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include "StdInc.h"
|
||||
#include "AILayerTransitionRule.h"
|
||||
#include "../../Engine/Nullkiller.h"
|
||||
#include "../../../../lib/pathfinder/CPathfinder.h"
|
||||
#include "../../../../lib/pathfinder/TurnInfo.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
@ -31,23 +33,79 @@ namespace AIPathfinding
|
||||
|
||||
if(!destination.blocked)
|
||||
{
|
||||
if(source.node->layer == EPathfindingLayer::LAND
|
||||
&& (destination.node->layer == EPathfindingLayer::AIR || destination.node->layer == EPathfindingLayer::WATER))
|
||||
{
|
||||
if(pathfinderHelper->getTurnInfo()->isLayerAvailable(destination.node->layer))
|
||||
return;
|
||||
else
|
||||
destination.blocked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
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))
|
||||
if(virtualBoat && tryUseSpecialAction(destination, source, virtualBoat, EPathNodeAction::EMBARK))
|
||||
{
|
||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 1
|
||||
logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::WATER)
|
||||
{
|
||||
auto action = waterWalkingActions.find(nodeStorage->getHero(source.node));
|
||||
|
||||
if(action != waterWalkingActions.end() && tryUseSpecialAction(destination, source, action->second, EPathNodeAction::NORMAL))
|
||||
{
|
||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
||||
logAi->trace("Casting water walk while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::AIR)
|
||||
{
|
||||
auto action = airWalkingActions.find(nodeStorage->getHero(source.node));
|
||||
|
||||
if(action != airWalkingActions.end() && tryUseSpecialAction(destination, source, action->second, EPathNodeAction::NORMAL))
|
||||
{
|
||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
||||
logAi->trace("Casting fly while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AILayerTransitionRule::setup()
|
||||
{
|
||||
SpellID waterWalk = SpellID::WATER_WALK;
|
||||
SpellID airWalk = SpellID::FLY;
|
||||
|
||||
for(const CGHeroInstance * hero : nodeStorage->getAllHeroes())
|
||||
{
|
||||
if(hero->canCastThisSpell(waterWalk.toSpell()))
|
||||
{
|
||||
waterWalkingActions[hero] = std::make_shared<WaterWalkingAction>(hero);
|
||||
}
|
||||
|
||||
if(hero->canCastThisSpell(airWalk.toSpell()))
|
||||
{
|
||||
airWalkingActions[hero] = std::make_shared<AirWalkingAction>(hero);
|
||||
}
|
||||
}
|
||||
|
||||
collectVirtualBoats();
|
||||
}
|
||||
|
||||
void AILayerTransitionRule::collectVirtualBoats()
|
||||
{
|
||||
std::vector<const IShipyard *> shipyards;
|
||||
|
||||
@ -113,30 +171,36 @@ namespace AIPathfinding
|
||||
return virtualBoat;
|
||||
}
|
||||
|
||||
bool AILayerTransitionRule::tryEmbarkVirtualBoat(
|
||||
bool AILayerTransitionRule::tryUseSpecialAction(
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
std::shared_ptr<const VirtualBoatAction> virtualBoat) const
|
||||
std::shared_ptr<const SpecialAction> specialAction,
|
||||
EPathNodeAction targetAction) const
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if(!specialAction->canAct(nodeStorage->getAINode(source.node)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
|
||||
{
|
||||
auto boatNodeOptional = nodeStorage->getOrCreateNode(
|
||||
auto castNodeOptional = nodeStorage->getOrCreateNode(
|
||||
node->coord,
|
||||
node->layer,
|
||||
virtualBoat->getActor(node->actor));
|
||||
specialAction->getActor(node->actor));
|
||||
|
||||
if(boatNodeOptional)
|
||||
if(castNodeOptional)
|
||||
{
|
||||
AIPathNode * boatNode = boatNodeOptional.value();
|
||||
AIPathNode * castNode = castNodeOptional.value();
|
||||
|
||||
if(boatNode->action == EPathNodeAction::UNKNOWN)
|
||||
if(castNode->action == EPathNodeAction::UNKNOWN)
|
||||
{
|
||||
boatNode->addSpecialAction(virtualBoat);
|
||||
castNode->addSpecialAction(specialAction);
|
||||
destination.blocked = false;
|
||||
destination.action = EPathNodeAction::EMBARK;
|
||||
destination.node = boatNode;
|
||||
destination.action = targetAction;
|
||||
destination.node = castNode;
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../AINodeStorage.h"
|
||||
#include "../../AIGateway.h"
|
||||
#include "../Actions/BoatActions.h"
|
||||
#include "../Actions/AdventureSpellCastMovementActions.h"
|
||||
#include "../../../../CCallback.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../../../lib/pathfinder/PathfindingRules.h"
|
||||
@ -29,6 +30,8 @@ namespace AIPathfinding
|
||||
std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
|
||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||
std::map<const CGHeroInstance *, std::shared_ptr<const SummonBoatAction>> summonableVirtualBoats;
|
||||
std::map<const CGHeroInstance *, std::shared_ptr<const WaterWalkingAction>> waterWalkingActions;
|
||||
std::map<const CGHeroInstance *, std::shared_ptr<const AirWalkingAction>> airWalkingActions;
|
||||
|
||||
public:
|
||||
AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, Nullkiller * ai, std::shared_ptr<AINodeStorage> nodeStorage);
|
||||
@ -41,15 +44,17 @@ namespace AIPathfinding
|
||||
|
||||
private:
|
||||
void setup();
|
||||
void collectVirtualBoats();
|
||||
|
||||
std::shared_ptr<const VirtualBoatAction> findVirtualBoat(
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source) const;
|
||||
|
||||
bool tryEmbarkVirtualBoat(
|
||||
bool tryUseSpecialAction(
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
std::shared_ptr<const VirtualBoatAction> virtualBoat) const;
|
||||
std::shared_ptr<const SpecialAction> specialAction,
|
||||
EPathNodeAction targetAction) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "../TerrainHandler.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../mapping/CMap.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -472,6 +473,12 @@ CPathfinderHelper::CPathfinderHelper(CGameState * gs, const CGHeroInstance * Her
|
||||
turnsInfo.reserve(16);
|
||||
updateTurnInfo();
|
||||
initializePatrol();
|
||||
|
||||
SpellID flySpell = SpellID::FLY;
|
||||
canCastFly = Hero->canCastThisSpell(flySpell.toSpell());
|
||||
|
||||
SpellID waterWalk = SpellID::WATER_WALK;
|
||||
canCastWaterWalk = Hero->canCastThisSpell(waterWalk.toSpell());
|
||||
}
|
||||
|
||||
CPathfinderHelper::~CPathfinderHelper()
|
||||
@ -501,12 +508,18 @@ bool CPathfinderHelper::isLayerAvailable(const EPathfindingLayer & layer) const
|
||||
if(!options.useFlying)
|
||||
return false;
|
||||
|
||||
if(canCastFly && options.canUseCast)
|
||||
return true;
|
||||
|
||||
break;
|
||||
|
||||
case EPathfindingLayer::WATER:
|
||||
if(!options.useWaterWalking)
|
||||
return false;
|
||||
|
||||
if(canCastWaterWalk && options.canUseCast)
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,8 @@ public:
|
||||
const CGHeroInstance * hero;
|
||||
std::vector<TurnInfo *> turnsInfo;
|
||||
const PathfinderOptions & options;
|
||||
bool canCastFly;
|
||||
bool canCastWaterWalk;
|
||||
|
||||
CPathfinderHelper(CGameState * gs, const CGHeroInstance * Hero, const PathfinderOptions & Options);
|
||||
virtual ~CPathfinderHelper();
|
||||
|
@ -31,6 +31,7 @@ PathfinderOptions::PathfinderOptions()
|
||||
, oneTurnSpecialLayersLimit(true)
|
||||
, originalMovementRules(false)
|
||||
, turnLimit(std::numeric_limits<uint8_t>::max())
|
||||
, canUseCast(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,11 @@ struct DLL_LINKAGE PathfinderOptions
|
||||
/// Max number of turns to compute. Default = infinite
|
||||
uint8_t turnLimit;
|
||||
|
||||
/// <summary>
|
||||
/// For AI. Allows water walk and fly layers if hero can cast appropriate spells
|
||||
/// </summary>
|
||||
bool canUseCast;
|
||||
|
||||
PathfinderOptions();
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user