Browse Source

Removed hardcoded checks for Summon Boat spell

Ivan Savenko 4 months ago
parent
commit
b0c511149d

+ 2 - 4
AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp

@@ -92,7 +92,7 @@ namespace AIPathfinding
 
 	void SummonBoatAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
 	{
-		Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai);
+		Goals::AdventureSpellCast(hero, usedSpell).accept(ai);
 	}
 
 	const ChainActor * SummonBoatAction::getActor(const ChainActor * sourceActor) const
@@ -139,10 +139,8 @@ namespace AIPathfinding
 
 	int32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const
 	{
-		SpellID summonBoat = SpellID::SUMMON_BOAT;
-
 		// FIXME: this should be hero->getSpellCost, however currently queries to bonus system are too slow
-		return summonBoat.toSpell()->getCost(0);
+		return usedSpell.toSpell()->getCost(0);
 	}
 }
 

+ 6 - 0
AI/Nullkiller/Pathfinding/Actions/BoatActions.h

@@ -24,7 +24,13 @@ namespace AIPathfinding
 	
 	class SummonBoatAction : public VirtualBoatAction
 	{
+		SpellID usedSpell;
 	public:
+		SummonBoatAction(SpellID usedSpell)
+			: usedSpell(usedSpell)
+		{
+		}
+
 		void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
 
 		virtual void applyOnDestination(

+ 16 - 6
AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp

@@ -12,6 +12,8 @@
 #include "../../Engine/Nullkiller.h"
 #include "../../../../lib/pathfinder/CPathfinder.h"
 #include "../../../../lib/pathfinder/TurnInfo.h"
+#include "../../../../lib/spells/ISpellMechanics.h"
+#include "../../../../lib/spells/adventure/SummonBoatEffect.h"
 
 namespace NKAI
 {
@@ -159,13 +161,21 @@ namespace AIPathfinding
 
 		for(const CGHeroInstance * hero : nodeStorage->getAllHeroes())
 		{
-			auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();
-
-			if(hero->canCastThisSpell(summonBoatSpell)
-				&& hero->getSpellSchoolLevel(summonBoatSpell) >= MasteryLevel::ADVANCED)
+			for (const auto & spell : LIBRARY->spellh->objects)
 			{
-				// TODO: For lower school level we might need to check the existence of some boat
-				summonableVirtualBoats[hero] = std::make_shared<SummonBoatAction>();
+				if (!spell || !spell->isAdventure())
+					continue;
+
+				auto effect = spell->getAdventureMechanics().getEffectAs<SummonBoatEffect>(hero);
+
+				if (!effect || !hero->canCastThisSpell(spell.get()))
+					continue;
+
+				if (effect->canCreateNewBoat() && effect->getSuccessChance(hero) == 100)
+				{
+					// TODO: For lower school level we might need to check the existence of some boat
+					summonableVirtualBoats[hero] = std::make_shared<SummonBoatAction>(spell->id);
+				}
 			}
 		}
 	}

+ 2 - 4
AI/VCAI/Pathfinding/Actions/BoatActions.cpp

@@ -23,7 +23,7 @@ namespace AIPathfinding
 
 	Goals::TSubgoal SummonBoatAction::whatToDo(const HeroPtr & hero) const
 	{
-		return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
+		return Goals::sptr(Goals::AdventureSpellCast(hero, usedSpell));
 	}
 
 	void SummonBoatAction::applyOnDestination(
@@ -53,8 +53,6 @@ namespace AIPathfinding
 
 	uint32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const
 	{
-		SpellID summonBoat = SpellID::SUMMON_BOAT;
-
-		return hero->getSpellCost(summonBoat.toSpell());
+		return hero->getSpellCost(usedSpell.toSpell());
 	}
 }

+ 3 - 1
AI/VCAI/Pathfinding/Actions/BoatActions.h

@@ -34,9 +34,11 @@ namespace AIPathfinding
 	
 	class SummonBoatAction : public VirtualBoatAction
 	{
+		SpellID usedSpell;
 	public:
-		SummonBoatAction()
+		SummonBoatAction(SpellID usedSpell)
 			:VirtualBoatAction(AINodeStorage::CAST_CHAIN)
+			,usedSpell(usedSpell)
 		{
 		}
 

+ 17 - 5
AI/VCAI/Pathfinding/Rules/AILayerTransitionRule.cpp

@@ -10,6 +10,9 @@
 #include "StdInc.h"
 #include "AILayerTransitionRule.h"
 
+#include "../../../../lib/spells/ISpellMechanics.h"
+#include "../../../../lib/spells/adventure/SummonBoatEffect.h"
+
 namespace AIPathfinding
 {
 	AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
@@ -74,13 +77,22 @@ namespace AIPathfinding
 		}
 
 		auto hero = nodeStorage->getHero();
-		auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();
 
-		if(hero->canCastThisSpell(summonBoatSpell)
-			&& hero->getSpellSchoolLevel(summonBoatSpell) >= MasteryLevel::ADVANCED)
+		for (const auto & spell : LIBRARY->spellh->objects)
 		{
-			// TODO: For lower school level we might need to check the existence of some boat
-			summonableVirtualBoat.reset(new SummonBoatAction());
+			if (!spell || !spell->isAdventure())
+				continue;
+
+			auto effect = spell->getAdventureMechanics().getEffectAs<SummonBoatEffect>(hero);
+
+			if (!effect || !hero->canCastThisSpell(spell.get()))
+				continue;
+
+			if (effect->canCreateNewBoat() && effect->getSuccessChance(hero) == 100)
+			{
+				// TODO: For lower school level we might need to check the existence of some boat
+				summonableVirtualBoat.reset(new SummonBoatAction(spell->id));
+			}
 		}
 	}
 

+ 4 - 0
lib/callback/CDynLibHandler.cpp

@@ -67,6 +67,10 @@ VCMI_LIB_NAMESPACE_BEGIN
 		getName = reinterpret_cast<TGetNameFun>(dlsym(dll, "GetAiName"));
 		getAI = reinterpret_cast<TGetAIFun>(dlsym(dll, methodName.c_str()));
 	}
+	else
+	{
+		logGlobal->error("Cannot open dynamic library '%s'. Reason: %s", libpath.string(), dlerror());
+	}
 #endif // VCMI_WINDOWS
 
 	if (!dll)

+ 2 - 2
lib/spells/adventure/AdventureSpellEffect.h

@@ -43,7 +43,7 @@ public:
 	AdventureSpellEffect() = default;
 };
 
-class AdventureSpellRangedEffect : public IAdventureSpellEffect
+class DLL_LINKAGE AdventureSpellRangedEffect : public IAdventureSpellEffect
 {
 	int rangeX;
 	int rangeY;
@@ -52,7 +52,7 @@ class AdventureSpellRangedEffect : public IAdventureSpellEffect
 public:
 	AdventureSpellRangedEffect(const JsonNode & config);
 
-	DLL_LINKAGE bool isTargetInRange(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const;
+	bool isTargetInRange(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const;
 	std::string getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override = 0; //must be implemented in derived classes
 	bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override = 0; //must be implemented in derived classes
 };

+ 12 - 3
lib/spells/adventure/SummonBoatEffect.cpp

@@ -28,6 +28,17 @@ SummonBoatEffect::SummonBoatEffect(const CSpell * s, const JsonNode & config)
 {
 }
 
+bool SummonBoatEffect::canCreateNewBoat() const
+{
+	return createNewBoat;
+}
+
+int SummonBoatEffect::getSuccessChance(const spells::Caster * caster) const
+{
+	const auto schoolLevel = caster->getSpellSchoolLevel(owner);
+	return owner->getLevelPower(schoolLevel);
+}
+
 bool SummonBoatEffect::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const
 {
 	if(!caster->getHeroCaster())
@@ -56,10 +67,8 @@ bool SummonBoatEffect::canBeCastImpl(spells::Problem & problem, const IGameInfoC
 
 ESpellCastResult SummonBoatEffect::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
 {
-	const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
-
 	//check if spell works at all
-	if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success
+	if(env->getRNG()->nextInt(0, 99) >= getSuccessChance(parameters.caster)) //power is % chance of success
 	{
 		InfoWindow iw;
 		iw.player = parameters.caster->getCasterOwner();

+ 4 - 1
lib/spells/adventure/SummonBoatEffect.h

@@ -14,7 +14,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class SummonBoatEffect final : public IAdventureSpellEffect
+class DLL_LINKAGE SummonBoatEffect final : public IAdventureSpellEffect
 {
 	const CSpell * owner;
 	bool useExistingBoat;
@@ -23,6 +23,9 @@ class SummonBoatEffect final : public IAdventureSpellEffect
 public:
 	SummonBoatEffect(const CSpell * s, const JsonNode & config);
 
+	bool canCreateNewBoat() const;
+	int getSuccessChance(const spells::Caster * caster) const;
+
 protected:
 	bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const final;