forked from mirror/openmw-tes3mp
Merge pull request #1329 from akortunov/priorityfix
Combat AI: make default spell priority calculation formula close to vanilla
This commit is contained in:
commit
eac2e52841
5 changed files with 83 additions and 39 deletions
|
@ -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;
|
||||
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;
|
||||
x *= fEffectCostMult;
|
||||
|
||||
if (effect.mRange == ESM::RT_Target)
|
||||
x *= 1.5f;
|
||||
|
||||
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
|
||||
x *= fEffectCostMult;
|
||||
|
||||
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…
Reference in a new issue