1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-06 09:09:40 +02:00

Removed hardcoded AI logic for Water Walk and Fly spell

This commit is contained in:
Ivan Savenko
2025-07-11 17:38:03 +03:00
parent b0c511149d
commit f51c7c5c28
8 changed files with 59 additions and 29 deletions

View File

@@ -12,6 +12,8 @@
#include "../Engine/Nullkiller.h" #include "../Engine/Nullkiller.h"
#include "../../../lib/mapObjects/MapObjects.h" #include "../../../lib/mapObjects/MapObjects.h"
#include "../../../lib/IGameSettings.h" #include "../../../lib/IGameSettings.h"
#include "../../../lib/spells/ISpellMechanics.h"
#include "../../../lib/spells/adventure/TownPortalEffect.h"
namespace NKAI namespace NKAI
{ {
@@ -210,32 +212,31 @@ float HeroManager::getFightingStrengthCached(const CGHeroInstance * hero) const
float HeroManager::getMagicStrength(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 manaLimit = hero->manaLimit();
auto spellPower = hero->getPrimSkillLevel(PrimarySkill::SPELL_POWER); auto spellPower = hero->getPrimSkillLevel(PrimarySkill::SPELL_POWER);
auto hasEarth = hero->getSpellSchoolLevel(SpellID(SpellID::TOWN_PORTAL).toSpell()) > 0;
auto score = 0.0f; auto score = 0.0f;
// FIXME: this will not cover spells give by scrolls / tomes. Intended?
for(auto spellId : hero->getSpellsInSpellbook()) for(auto spellId : hero->getSpellsInSpellbook())
{ {
auto spell = spellId.toSpell(); auto spell = spellId.toSpell();
auto schoolLevel = hero->getSpellSchoolLevel(spell); auto schoolLevel = hero->getSpellSchoolLevel(spell);
auto townPortalEffect = spell->getAdventureMechanics().getEffectAs<TownPortalEffect>(hero);
score += (spell->getLevel() + 1) * (schoolLevel + 1) * 0.05f; 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); vstd::amin(score, 1);
score *= std::min(1.0f, spellPower / 10.0f); score *= std::min(1.0f, spellPower / 10.0f);
if(hasFly)
score += 0.3f;
if(hasTownPortal && hasEarth)
score += 0.6f;
vstd::amin(score, 1); vstd::amin(score, 1);
score *= std::min(1.0f, manaLimit / 100.0f); score *= std::min(1.0f, manaLimit / 100.0f);

View File

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

View File

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

View File

@@ -111,19 +111,22 @@ namespace AIPathfinding
void AILayerTransitionRule::setup() void AILayerTransitionRule::setup()
{ {
SpellID waterWalk = SpellID::WATER_WALK;
SpellID airWalk = SpellID::FLY;
for(const CGHeroInstance * hero : nodeStorage->getAllHeroes()) 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())) if(spell->getAdventureMechanics().givesBonus(hero, BonusType::WATER_WALKING) && hero->canCastThisSpell(spell.get()) && hero->mana >= hero->getSpellCost(spell.get()))
{ {
airWalkingActions[hero] = std::make_shared<AirWalkingAction>(hero); 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);
}
} }
} }

View File

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

View File

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

View File

@@ -84,6 +84,15 @@ const IAdventureSpellEffect * AdventureSpellMechanics::getEffect(const spells::C
return getLevel(caster).effect.get(); 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 bool AdventureSpellMechanics::canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const
{ {
if(!owner->isAdventure()) if(!owner->isAdventure())

View File

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