浏览代码

Removed hardcoded AI logic for Water Walk and Fly spell

Ivan Savenko 3 月之前
父节点
当前提交
f51c7c5c28

+ 10 - 9
AI/Nullkiller/Analyzers/HeroManager.cpp

@@ -12,6 +12,8 @@
 #include "../Engine/Nullkiller.h"
 #include "../../../lib/mapObjects/MapObjects.h"
 #include "../../../lib/IGameSettings.h"
+#include "../../../lib/spells/ISpellMechanics.h"
+#include "../../../lib/spells/adventure/TownPortalEffect.h"
 
 namespace NKAI
 {
@@ -210,32 +212,31 @@ float HeroManager::getFightingStrengthCached(const CGHeroInstance * hero) const
 
 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;
 
+	// FIXME: this will not cover spells give by scrolls / tomes. Intended?
 	for(auto spellId : hero->getSpellsInSpellbook())
 	{
 		auto spell = spellId.toSpell();
 		auto schoolLevel = hero->getSpellSchoolLevel(spell);
+		auto townPortalEffect = spell->getAdventureMechanics().getEffectAs<TownPortalEffect>(hero);
 
 		score += (spell->getLevel() + 1) * (schoolLevel + 1) * 0.05f;
+
+		if (spell->getAdventureMechanics().givesBonus(hero, BonusType::FLYING_MOVEMENT))
+			score += 0.3;
+
+		if(townPortalEffect != nullptr && schoolLevel != 0)
+			score += 0.6f;
 	}
 
 	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);

+ 4 - 4
AI/Nullkiller/Pathfinding/Actions/AdventureSpellCastMovementActions.cpp

@@ -28,12 +28,12 @@ namespace AIPathfinding
 		manaCost = hero->getSpellCost(spellToCast.toSpell());
 	}
 
-	WaterWalkingAction::WaterWalkingAction(const CGHeroInstance * hero)
-		:AdventureCastAction(SpellID::WATER_WALK, hero, DayFlags::WATER_WALK_CAST)
+	WaterWalkingAction::WaterWalkingAction(const CGHeroInstance * hero, SpellID spellToCast)
+		:AdventureCastAction(spellToCast, hero, DayFlags::WATER_WALK_CAST)
 	{ }
 
-	AirWalkingAction::AirWalkingAction(const CGHeroInstance * hero)
-		: AdventureCastAction(SpellID::FLY, hero, DayFlags::FLY_CAST)
+	AirWalkingAction::AirWalkingAction(const CGHeroInstance * hero, SpellID spellToCast)
+		: AdventureCastAction(spellToCast, hero, DayFlags::FLY_CAST)
 	{
 	}
 

+ 4 - 2
AI/Nullkiller/Pathfinding/Actions/AdventureSpellCastMovementActions.h

@@ -45,14 +45,16 @@ namespace AIPathfinding
 
 	class WaterWalkingAction : public AdventureCastAction
 	{
+		SpellID spellToCast;
 	public:
-		WaterWalkingAction(const CGHeroInstance * hero);
+		WaterWalkingAction(const CGHeroInstance * hero, SpellID spellToCast);
 	};
 
 	class AirWalkingAction : public AdventureCastAction
 	{
+		SpellID spellToCast;
 	public:
-		AirWalkingAction(const CGHeroInstance * hero);
+		AirWalkingAction(const CGHeroInstance * hero, SpellID spellToCast);
 	};
 }
 

+ 12 - 9
AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp

@@ -111,19 +111,22 @@ namespace AIPathfinding
 
 	void AILayerTransitionRule::setup()
 	{
-		SpellID waterWalk = SpellID::WATER_WALK;
-		SpellID airWalk = SpellID::FLY;
-
 		for(const CGHeroInstance * hero : nodeStorage->getAllHeroes())
 		{
-			if(hero->canCastThisSpell(waterWalk.toSpell()) && hero->mana >= hero->getSpellCost(waterWalk.toSpell()))
+			for (const auto & spell : LIBRARY->spellh->objects)
 			{
-				waterWalkingActions[hero] = std::make_shared<WaterWalkingAction>(hero);
-			}
+				if (!spell || !spell->isAdventure())
+					continue;
 
-			if(hero->canCastThisSpell(airWalk.toSpell()) && hero->mana >= hero->getSpellCost(airWalk.toSpell()))
-			{
-				airWalkingActions[hero] = std::make_shared<AirWalkingAction>(hero);
+				if(spell->getAdventureMechanics().givesBonus(hero, BonusType::WATER_WALKING) && hero->canCastThisSpell(spell.get()) && hero->mana >= hero->getSpellCost(spell.get()))
+				{
+					waterWalkingActions[hero] = std::make_shared<WaterWalkingAction>(hero, spell->id);
+				}
+
+				if(spell->getAdventureMechanics().givesBonus(hero, BonusType::FLYING_MOVEMENT) && hero->canCastThisSpell(spell.get()) && hero->mana >= hero->getSpellCost(spell.get()))
+				{
+					airWalkingActions[hero] = std::make_shared<AirWalkingAction>(hero, spell->id);
+				}
 			}
 		}
 

+ 17 - 5
lib/pathfinder/CPathfinder.cpp

@@ -25,6 +25,7 @@
 #include "../mapObjects/MiscObjects.h"
 #include "../mapping/CMap.h"
 #include "../spells/CSpellHandler.h"
+#include "spells/ISpellMechanics.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -502,7 +503,9 @@ CPathfinderHelper::CPathfinderHelper(const IGameInfoCallback & gameInfo, const C
 	turn(-1),
 	owner(Hero->tempOwner),
 	hero(Hero),
-	options(Options)
+	options(Options),
+	canCastFly(false),
+	canCastWaterWalk(false)
 {
 	turnsInfo.reserve(16);
 	updateTurnInfo();
@@ -510,11 +513,20 @@ CPathfinderHelper::CPathfinderHelper(const IGameInfoCallback & gameInfo, const C
 
 	whirlpoolProtection = Hero->hasBonusOfType(BonusType::WHIRLPOOL_PROTECTION);
 
-	SpellID flySpell = SpellID::FLY;
-	canCastFly = Hero->canCastThisSpell(flySpell.toSpell());
+	if (options.canUseCast)
+	{
+		for (const auto & spell : LIBRARY->spellh->objects)
+		{
+			if (!spell || !spell->isAdventure())
+				continue;
 
-	SpellID waterWalk = SpellID::WATER_WALK;
-	canCastWaterWalk = Hero->canCastThisSpell(waterWalk.toSpell());
+			if(spell->getAdventureMechanics().givesBonus(hero, BonusType::WATER_WALKING) && hero->canCastThisSpell(spell.get()) && hero->mana >= hero->getSpellCost(spell.get()))
+				canCastWaterWalk = true;
+
+			if(spell->getAdventureMechanics().givesBonus(hero, BonusType::FLYING_MOVEMENT) && hero->canCastThisSpell(spell.get()) && hero->mana >= hero->getSpellCost(spell.get()))
+				canCastFly = true;
+		}
+	}
 }
 
 CPathfinderHelper::~CPathfinderHelper() = default;

+ 2 - 0
lib/spells/ISpellMechanics.h

@@ -359,6 +359,8 @@ public:
 
 	static std::unique_ptr<IAdventureSpellMechanics> createMechanics(const CSpell * s);
 
+	virtual bool givesBonus(const spells::Caster * caster, BonusType which) const = 0;
+
 	template<typename EffectType>
 	const EffectType * getEffectAs(const spells::Caster * caster) const
 	{

+ 9 - 0
lib/spells/adventure/AdventureSpellMechanics.cpp

@@ -84,6 +84,15 @@ const IAdventureSpellEffect * AdventureSpellMechanics::getEffect(const spells::C
 	return getLevel(caster).effect.get();
 }
 
+bool AdventureSpellMechanics::givesBonus(const spells::Caster * caster, BonusType which) const
+{
+	for (const auto & bonus : getLevel(caster).bonuses)
+		if (bonus->type == which)
+			return true;
+
+	return false;
+}
+
 bool AdventureSpellMechanics::canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const
 {
 	if(!owner->isAdventure())

+ 1 - 0
lib/spells/adventure/AdventureSpellMechanics.h

@@ -39,6 +39,7 @@ public:
 	bool canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
 	bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const final;
 	const IAdventureSpellEffect * getEffect(const spells::Caster * caster) const final;
+	bool givesBonus(const spells::Caster * caster, BonusType which) const final;
 	void performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
 };