|
|
|
@ -1,6 +1,8 @@
|
|
|
|
|
#include "spellpriority.hpp"
|
|
|
|
|
#include "weaponpriority.hpp"
|
|
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
#include <components/esm3/loadench.hpp>
|
|
|
|
|
#include <components/esm3/loadmgef.hpp>
|
|
|
|
|
#include <components/esm3/loadrace.hpp>
|
|
|
|
@ -86,6 +88,27 @@ namespace
|
|
|
|
|
return spell.getCasterActorId() == actorId && spell.getId() == id;
|
|
|
|
|
}) != active.end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float getRestoreMagickaPriority(const ESM::ENAMstruct& effect, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
|
|
|
|
{
|
|
|
|
|
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
|
|
|
|
const MWMechanics::DynamicStat<float>& current = stats.getMagicka();
|
|
|
|
|
const float magnitude = (effect.mMagnMin + effect.mMagnMax) / 2.f;
|
|
|
|
|
const float toHeal = magnitude * std::max(1, effect.mDuration);
|
|
|
|
|
float priority = 0.f;
|
|
|
|
|
for (const ESM::Spell* spell : stats.getSpells())
|
|
|
|
|
{
|
|
|
|
|
auto found = std::find_if(spell->mEffects.mList.begin(), spell->mEffects.mList.end(),
|
|
|
|
|
[&](const auto& e) { return &e == &effect; });
|
|
|
|
|
if (found != spell->mEffects.mList.end()) // Prevent recursion
|
|
|
|
|
continue;
|
|
|
|
|
int cost = MWMechanics::calcSpellCost(*spell);
|
|
|
|
|
if (cost < current.getCurrent() || cost > current.getCurrent() + toHeal)
|
|
|
|
|
continue;
|
|
|
|
|
priority = std::max(priority, MWMechanics::rateSpell(spell, actor, enemy, false));
|
|
|
|
|
}
|
|
|
|
|
return priority;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace MWMechanics
|
|
|
|
@ -105,18 +128,19 @@ namespace MWMechanics
|
|
|
|
|
return types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float ratePotion(const MWWorld::Ptr& item, const MWWorld::Ptr& actor)
|
|
|
|
|
float ratePotion(const MWWorld::Ptr& item, const MWWorld::Ptr& actor, bool& pureHealing)
|
|
|
|
|
{
|
|
|
|
|
if (item.getType() != ESM::Potion::sRecordId)
|
|
|
|
|
return 0.f;
|
|
|
|
|
|
|
|
|
|
const ESM::Potion* potion = item.get<ESM::Potion>()->mBase;
|
|
|
|
|
pureHealing = isPureHealing(potion->mEffects);
|
|
|
|
|
return rateEffects(potion->mEffects, actor, MWWorld::Ptr());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float rateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
|
|
|
|
float rateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, bool checkMagicka)
|
|
|
|
|
{
|
|
|
|
|
float successChance = MWMechanics::getSpellSuccessChance(spell, actor);
|
|
|
|
|
float successChance = MWMechanics::getSpellSuccessChance(spell, actor, nullptr, true, checkMagicka);
|
|
|
|
|
if (successChance == 0.f)
|
|
|
|
|
return 0.f;
|
|
|
|
|
|
|
|
|
@ -136,19 +160,21 @@ namespace MWMechanics
|
|
|
|
|
int types = getRangeTypes(spell->mEffects);
|
|
|
|
|
if ((types & Self) && isSpellActive(actor, actor, spell->mId))
|
|
|
|
|
return 0.f;
|
|
|
|
|
if (((types & Touch) || (types & Target)) && isSpellActive(actor, enemy, spell->mId))
|
|
|
|
|
if (((types & Touch) || (types & Target)) && !enemy.isEmpty() && isSpellActive(actor, enemy, spell->mId))
|
|
|
|
|
return 0.f;
|
|
|
|
|
|
|
|
|
|
return rateEffects(spell->mEffects, actor, enemy) * (successChance / 100.f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float rateMagicItem(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
|
|
|
|
float rateMagicItem(
|
|
|
|
|
const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, bool& pureHealing)
|
|
|
|
|
{
|
|
|
|
|
if (ptr.getClass().getEnchantment(ptr).empty())
|
|
|
|
|
return 0.f;
|
|
|
|
|
|
|
|
|
|
const ESM::Enchantment* enchantment = MWBase::Environment::get().getESMStore()->get<ESM::Enchantment>().find(
|
|
|
|
|
ptr.getClass().getEnchantment(ptr));
|
|
|
|
|
pureHealing = isPureHealing(enchantment->mEffects);
|
|
|
|
|
|
|
|
|
|
// Spells don't stack, so early out if the spell is still active on the target
|
|
|
|
|
int types = getRangeTypes(enchantment->mEffects);
|
|
|
|
@ -405,30 +431,47 @@ namespace MWMechanics
|
|
|
|
|
return 0.f;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ESM::MagicEffect::AbsorbMagicka:
|
|
|
|
|
if (!enemy.isEmpty() && enemy.getClass().getCreatureStats(enemy).getMagicka().getCurrent() <= 0.f)
|
|
|
|
|
{
|
|
|
|
|
rating = 0.5f;
|
|
|
|
|
float priority = getRestoreMagickaPriority(effect, actor, enemy);
|
|
|
|
|
if (priority == 0.f)
|
|
|
|
|
priority = -1.f;
|
|
|
|
|
rating *= priority;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case ESM::MagicEffect::RestoreHealth:
|
|
|
|
|
case ESM::MagicEffect::RestoreMagicka:
|
|
|
|
|
case ESM::MagicEffect::RestoreFatigue:
|
|
|
|
|
if (effect.mRange == ESM::RT_Self)
|
|
|
|
|
{
|
|
|
|
|
int priority = 1;
|
|
|
|
|
if (effect.mEffectID == ESM::MagicEffect::RestoreHealth)
|
|
|
|
|
priority = 10;
|
|
|
|
|
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
|
|
|
|
const DynamicStat<float>& current
|
|
|
|
|
= stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth);
|
|
|
|
|
// NB: this currently assumes the hardcoded magic effect flags are used
|
|
|
|
|
const float magnitude = (effect.mMagnMin + effect.mMagnMax) / 2.f;
|
|
|
|
|
const float toHeal = magnitude * std::max(1, effect.mDuration);
|
|
|
|
|
// Effect doesn't heal more than we need, *or* we are below 1/2 health
|
|
|
|
|
if (current.getModified() - current.getCurrent() > toHeal
|
|
|
|
|
|| current.getCurrent() < current.getModified() * 0.5)
|
|
|
|
|
const float damage = current.getModified() - current.getCurrent();
|
|
|
|
|
float priority = 0.f;
|
|
|
|
|
if (effect.mEffectID == ESM::MagicEffect::RestoreHealth)
|
|
|
|
|
priority = 4.f;
|
|
|
|
|
else if (effect.mEffectID == ESM::MagicEffect::RestoreMagicka)
|
|
|
|
|
priority = std::max(0.1f, getRestoreMagickaPriority(effect, actor, enemy));
|
|
|
|
|
else if (effect.mEffectID == ESM::MagicEffect::RestoreFatigue)
|
|
|
|
|
priority = 2.f;
|
|
|
|
|
float overheal = 0.f;
|
|
|
|
|
float heal = toHeal;
|
|
|
|
|
if (damage < toHeal && current.getCurrent() > current.getModified() * 0.5)
|
|
|
|
|
{
|
|
|
|
|
return 10000.f * priority
|
|
|
|
|
- (toHeal
|
|
|
|
|
- (current.getModified() - current.getCurrent())); // prefer the most fitting potion
|
|
|
|
|
overheal = toHeal - damage;
|
|
|
|
|
heal = damage;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return -10000.f * priority; // Save for later
|
|
|
|
|
|
|
|
|
|
priority = (std::pow(priority + heal / current.getModified(), damage * 4.f / current.getModified())
|
|
|
|
|
- priority - overheal / current.getModified())
|
|
|
|
|
/ priority;
|
|
|
|
|
rating = priority;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|