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 "../../../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);

View File

@@ -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)
{
}

View File

@@ -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);
};
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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
{

View File

@@ -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())

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 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;
};