Merge pull request #1329 from akortunov/priorityfix

Combat AI: make default spell priority calculation formula close to vanilla
pull/258/head
scrawl 8 years ago committed by GitHub
commit eac2e52841

@ -448,22 +448,11 @@ namespace MWGui
for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{
float x = 0.5f * (it->mMagnMin + it->mMagnMax);
const ESM::ENAMstruct& effect = *it;
const ESM::MagicEffect* effect =
store.get<ESM::MagicEffect>().find(it->mEffectID);
y += std::max(1.f, MWMechanics::calcEffectCost(effect));
x *= 0.1f * effect->mData.mBaseCost;
x *= 1 + it->mDuration;
x += 0.05f * std::max(1, it->mArea) * effect->mData.mBaseCost;
float fEffectCostMult =
store.get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
y += x * fEffectCostMult;
y = std::max(1.f,y);
if (it->mRange == ESM::RT_Target)
if (effect.mRange == ESM::RT_Target)
y *= 1.5;
}
@ -483,8 +472,10 @@ namespace MWGui
mPriceLabel->setCaption(MyGUI::utility::toString(int(price)));
float chance = MWMechanics::getSpellSuccessChance(&mSpell, MWMechanics::getPlayer());
mSuccessChance->setCaption(MyGUI::utility::toString(int(chance)));
float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), NULL);
int intChance = std::min(100, int(chance));
mSuccessChance->setCaption(MyGUI::utility::toString(intChance));
}
// ------------------------------------------------------------------------------------------------

@ -577,8 +577,6 @@ namespace MWMechanics
return 0.f;
}
rating *= magicEffect->mData.mBaseCost;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
{
rating *= -1.f;
@ -613,13 +611,8 @@ namespace MWMechanics
return 0.f;
}
}
else
{
rating *= (effect.mMagnMin + effect.mMagnMax)/2.f;
}
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
rating *= effect.mDuration;
rating *= calcEffectCost(effect);
// Currently treating all "on target" or "on touch" effects to target the enemy actor.
// Combat AI is egoistic, so doesn't consider applying positive effects to friendly actors.
@ -636,6 +629,9 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)
{
rating += rateEffect(*it, actor, enemy);
if (it->mRange == ESM::RT_Target)
rating *= 1.5f;
}
return rating;
}

@ -1,4 +1,5 @@
#include "autocalcspell.hpp"
#include "spellcasting.hpp"
#include <climits>
#include <limits>
@ -255,27 +256,39 @@ namespace MWMechanics
void calcWeakestSchool (const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm)
{
// Morrowind for some reason uses a formula slightly different from magicka cost calculation
float minChance = std::numeric_limits<float>::max();
const ESM::EffectList& effects = spell->mEffects;
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
{
const ESM::ENAMstruct& effect = *it;
float x = static_cast<float>(effect.mDuration);
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
x = std::max(1.f, x);
x *= 0.1f * magicEffect->mData.mBaseCost;
x *= 0.5f * (effect.mMagnMin + effect.mMagnMax);
x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost;
if (effect.mRange == ESM::RT_Target)
x *= 1.5f;
int minMagn = 1;
int maxMagn = 1;
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
{
minMagn = effect.mMagnMin;
maxMagn = effect.mMagnMax;
}
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
int duration = 0;
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
duration = effect.mDuration;
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn));
x *= 0.1 * magicEffect->mData.mBaseCost;
x *= 1 + duration;
x += 0.05 * std::max(1, effect.mArea) * magicEffect->mData.mBaseCost;
x *= fEffectCostMult;
if (effect.mRange == ESM::RT_Target)
x *= 1.5f;
float s = 2.f * actorSkills[mapSchoolToSkill(magicEffect->mData.mSchool)];
if (s - x < minChance)
{

@ -44,8 +44,36 @@ namespace MWMechanics
return schoolSkillMap[school];
}
float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
float calcEffectCost(const ESM::ENAMstruct& effect)
{
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
int minMagn = 1;
int maxMagn = 1;
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
{
minMagn = effect.mMagnMin;
maxMagn = effect.mMagnMax;
}
int duration = 0;
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
duration = effect.mDuration;
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn));
x *= 0.1 * magicEffect->mData.mBaseCost;
x *= 1 + duration;
x += 0.05 * std::max(1, effect.mArea) * magicEffect->mData.mBaseCost;
return x * fEffectCostMult;
}
float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool)
{
// Morrowind for some reason uses a formula slightly different from magicka cost calculation
float y = std::numeric_limits<float>::max();
float lowestSkill = 0;
@ -54,8 +82,10 @@ namespace MWMechanics
float x = static_cast<float>(it->mDuration);
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(
it->mEffectID);
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
x = std::max(1.f, x);
x *= 0.1f * magicEffect->mData.mBaseCost;
x *= 0.5f * (it->mMagnMin + it->mMagnMax);
x *= it->mArea * 0.05f * magicEffect->mData.mBaseCost;
@ -75,6 +105,18 @@ namespace MWMechanics
}
}
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck);
return castChance;
}
float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
{
bool godmode = actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
@ -98,10 +140,8 @@ namespace MWMechanics
float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude();
int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2f * actorWillpower + 0.1f * actorLuck) * stats.getFatigueTerm();
float castChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool) + castBonus;
castChance *= stats.getFatigueTerm();
if (!cap)
return std::max(0.f, castChance);

@ -1,6 +1,7 @@
#ifndef MWMECHANICS_SPELLSUCCESS_H
#define MWMECHANICS_SPELLSUCCESS_H
#include <components/esm/effectlist.hpp>
#include <components/esm/loadskil.hpp>
#include "../mwworld/ptr.hpp"
@ -21,6 +22,8 @@ namespace MWMechanics
ESM::Skill::SkillEnum spellSchoolToSkill(int school);
float calcEffectCost(const ESM::ENAMstruct& effect);
bool isSummoningEffect(int effectId);
/**
@ -62,6 +65,7 @@ namespace MWMechanics
bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer);
int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor);
float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool);
/// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed
/// @return Was the effect a tickable effect with a magnitude?

Loading…
Cancel
Save