mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-04 12:39:55 +00:00
Merge branch 'healplz' into 'master'
Tweak AI rating to reduce healing spam Closes #4207 See merge request OpenMW/openmw!2553
This commit is contained in:
commit
33d0774637
@ -5,6 +5,7 @@
|
|||||||
Bug #3842: Body part skeletons override the main skeleton
|
Bug #3842: Body part skeletons override the main skeleton
|
||||||
Bug #4127: Weapon animation looks choppy
|
Bug #4127: Weapon animation looks choppy
|
||||||
Bug #4204: Dead slaughterfish doesn't float to water surface after loading saved game
|
Bug #4204: Dead slaughterfish doesn't float to water surface after loading saved game
|
||||||
|
Bug #4207: RestoreHealth/Fatigue spells have a huge priority even if a success chance is near 0
|
||||||
Bug #4382: Sound output device does not change when it should
|
Bug #4382: Sound output device does not change when it should
|
||||||
Bug #4610: Casting a Bound Weapon spell cancels the casting animation by equipping the weapon prematurely
|
Bug #4610: Casting a Bound Weapon spell cancels the casting animation by equipping the weapon prematurely
|
||||||
Bug #4754: Stack of ammunition cannot be equipped partially
|
Bug #4754: Stack of ammunition cannot be equipped partially
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "combat.hpp"
|
#include "combat.hpp"
|
||||||
#include "npcstats.hpp"
|
#include "npcstats.hpp"
|
||||||
#include "spellpriority.hpp"
|
#include "spellpriority.hpp"
|
||||||
|
#include "spellutil.hpp"
|
||||||
#include "weaponpriority.hpp"
|
#include "weaponpriority.hpp"
|
||||||
#include "weapontype.hpp"
|
#include "weapontype.hpp"
|
||||||
|
|
||||||
@ -181,23 +182,25 @@ namespace MWMechanics
|
|||||||
|
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||||
{
|
{
|
||||||
float rating = ratePotion(*it, actor);
|
if (it->getType() == ESM::Potion::sRecordId)
|
||||||
if (rating > bestActionRating)
|
|
||||||
{
|
{
|
||||||
bestActionRating = rating;
|
float rating = ratePotion(*it, actor);
|
||||||
bestAction = std::make_unique<ActionPotion>(*it);
|
if (rating > bestActionRating)
|
||||||
antiFleeRating = std::numeric_limits<float>::max();
|
{
|
||||||
|
bestActionRating = rating;
|
||||||
|
bestAction = std::make_unique<ActionPotion>(*it);
|
||||||
|
antiFleeRating = std::numeric_limits<float>::max();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (!it->getClass().getEnchantment(*it).empty())
|
||||||
|
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
|
||||||
{
|
|
||||||
float rating = rateMagicItem(*it, actor, enemy);
|
|
||||||
if (rating > bestActionRating)
|
|
||||||
{
|
{
|
||||||
bestActionRating = rating;
|
float rating = rateMagicItem(*it, actor, enemy);
|
||||||
bestAction = std::make_unique<ActionEnchantedItem>(it);
|
if (rating > bestActionRating)
|
||||||
antiFleeRating = std::numeric_limits<float>::max();
|
{
|
||||||
|
bestActionRating = rating;
|
||||||
|
bestAction = std::make_unique<ActionEnchantedItem>(it);
|
||||||
|
antiFleeRating = std::numeric_limits<float>::max();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ namespace MWMechanics
|
|||||||
virtual ~Action() {}
|
virtual ~Action() {}
|
||||||
virtual void prepare(const MWWorld::Ptr& actor) = 0;
|
virtual void prepare(const MWWorld::Ptr& actor) = 0;
|
||||||
virtual float getCombatRange(bool& isRanged) const = 0;
|
virtual float getCombatRange(bool& isRanged) const = 0;
|
||||||
virtual float getActionCooldown() { return 0.f; }
|
virtual float getActionCooldown() const { return 0.f; }
|
||||||
virtual const ESM::Weapon* getWeapon() const { return nullptr; }
|
virtual const ESM::Weapon* getWeapon() const { return nullptr; }
|
||||||
virtual bool isAttackingOrSpell() const { return true; }
|
virtual bool isAttackingOrSpell() const { return true; }
|
||||||
virtual bool isFleeing() const { return false; }
|
virtual bool isFleeing() const { return false; }
|
||||||
@ -26,7 +26,7 @@ namespace MWMechanics
|
|||||||
ActionFlee() {}
|
ActionFlee() {}
|
||||||
void prepare(const MWWorld::Ptr& actor) override {}
|
void prepare(const MWWorld::Ptr& actor) override {}
|
||||||
float getCombatRange(bool& isRanged) const override { return 0.0f; }
|
float getCombatRange(bool& isRanged) const override { return 0.0f; }
|
||||||
float getActionCooldown() override { return 3.0f; }
|
float getActionCooldown() const override { return 3.0f; }
|
||||||
bool isAttackingOrSpell() const override { return false; }
|
bool isAttackingOrSpell() const override { return false; }
|
||||||
bool isFleeing() const override { return true; }
|
bool isFleeing() const override { return true; }
|
||||||
};
|
};
|
||||||
@ -58,7 +58,7 @@ namespace MWMechanics
|
|||||||
float getCombatRange(bool& isRanged) const override;
|
float getCombatRange(bool& isRanged) const override;
|
||||||
|
|
||||||
/// Since this action has no animation, apply a small cool down for using it
|
/// Since this action has no animation, apply a small cool down for using it
|
||||||
float getActionCooldown() override { return 0.75f; }
|
float getActionCooldown() const override { return 0.75f; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ActionPotion : public Action
|
class ActionPotion : public Action
|
||||||
@ -75,7 +75,7 @@ namespace MWMechanics
|
|||||||
bool isAttackingOrSpell() const override { return false; }
|
bool isAttackingOrSpell() const override { return false; }
|
||||||
|
|
||||||
/// Since this action has no animation, apply a small cool down for using it
|
/// Since this action has no animation, apply a small cool down for using it
|
||||||
float getActionCooldown() override { return 0.75f; }
|
float getActionCooldown() const override { return 0.75f; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ActionWeapon : public Action
|
class ActionWeapon : public Action
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "spellpriority.hpp"
|
#include "spellpriority.hpp"
|
||||||
#include "weaponpriority.hpp"
|
#include "weaponpriority.hpp"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include <components/esm3/loadench.hpp>
|
#include <components/esm3/loadench.hpp>
|
||||||
#include <components/esm3/loadmgef.hpp>
|
#include <components/esm3/loadmgef.hpp>
|
||||||
#include <components/esm3/loadrace.hpp>
|
#include <components/esm3/loadrace.hpp>
|
||||||
@ -86,6 +88,21 @@ namespace
|
|||||||
return spell.getCasterActorId() == actorId && spell.getId() == id;
|
return spell.getCasterActorId() == actorId && spell.getId() == id;
|
||||||
}) != active.end();
|
}) != active.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getRestoreMagickaPriority(const MWWorld::Ptr& actor)
|
||||||
|
{
|
||||||
|
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||||
|
const MWMechanics::DynamicStat<float>& current = stats.getMagicka();
|
||||||
|
for (const ESM::Spell* spell : stats.getSpells())
|
||||||
|
{
|
||||||
|
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||||
|
continue;
|
||||||
|
int cost = MWMechanics::calcSpellCost(*spell);
|
||||||
|
if (cost > current.getCurrent() && cost < current.getModified())
|
||||||
|
return 2.f;
|
||||||
|
}
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
@ -114,9 +131,9 @@ namespace MWMechanics
|
|||||||
return rateEffects(potion->mEffects, actor, MWWorld::Ptr());
|
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)
|
if (successChance == 0.f)
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
@ -136,7 +153,7 @@ namespace MWMechanics
|
|||||||
int types = getRangeTypes(spell->mEffects);
|
int types = getRangeTypes(spell->mEffects);
|
||||||
if ((types & Self) && isSpellActive(actor, actor, spell->mId))
|
if ((types & Self) && isSpellActive(actor, actor, spell->mId))
|
||||||
return 0.f;
|
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 0.f;
|
||||||
|
|
||||||
return rateEffects(spell->mEffects, actor, enemy) * (successChance / 100.f);
|
return rateEffects(spell->mEffects, actor, enemy) * (successChance / 100.f);
|
||||||
@ -405,30 +422,44 @@ namespace MWMechanics
|
|||||||
return 0.f;
|
return 0.f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ESM::MagicEffect::AbsorbMagicka:
|
||||||
|
if (!enemy.isEmpty() && enemy.getClass().getCreatureStats(enemy).getMagicka().getCurrent() <= 0.f)
|
||||||
|
{
|
||||||
|
rating = 0.5f;
|
||||||
|
rating *= getRestoreMagickaPriority(actor);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ESM::MagicEffect::RestoreHealth:
|
case ESM::MagicEffect::RestoreHealth:
|
||||||
case ESM::MagicEffect::RestoreMagicka:
|
case ESM::MagicEffect::RestoreMagicka:
|
||||||
case ESM::MagicEffect::RestoreFatigue:
|
case ESM::MagicEffect::RestoreFatigue:
|
||||||
if (effect.mRange == ESM::RT_Self)
|
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 MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||||
const DynamicStat<float>& current
|
const DynamicStat<float>& current
|
||||||
= stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth);
|
= stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth);
|
||||||
// NB: this currently assumes the hardcoded magic effect flags are used
|
// NB: this currently assumes the hardcoded magic effect flags are used
|
||||||
const float magnitude = (effect.mMagnMin + effect.mMagnMax) / 2.f;
|
const float magnitude = (effect.mMagnMin + effect.mMagnMax) / 2.f;
|
||||||
const float toHeal = magnitude * std::max(1, effect.mDuration);
|
const float toHeal = magnitude * std::max(1, effect.mDuration);
|
||||||
// Effect doesn't heal more than we need, *or* we are below 1/2 health
|
const float damage = current.getModified() - current.getCurrent();
|
||||||
if (current.getModified() - current.getCurrent() > toHeal
|
float priority = 0.f;
|
||||||
|| current.getCurrent() < current.getModified() * 0.5)
|
if (effect.mEffectID == ESM::MagicEffect::RestoreHealth)
|
||||||
|
priority = 4.f;
|
||||||
|
else if (effect.mEffectID == ESM::MagicEffect::RestoreMagicka)
|
||||||
|
priority = std::max(0.1f, getRestoreMagickaPriority(actor));
|
||||||
|
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
|
overheal = toHeal - damage;
|
||||||
- (toHeal
|
heal = damage;
|
||||||
- (current.getModified() - current.getCurrent())); // prefer the most fitting potion
|
|
||||||
}
|
}
|
||||||
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;
|
break;
|
||||||
|
|
||||||
|
@ -25,7 +25,8 @@ namespace MWMechanics
|
|||||||
|
|
||||||
int getRangeTypes(const ESM::EffectList& effects);
|
int getRangeTypes(const ESM::EffectList& effects);
|
||||||
|
|
||||||
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 = true);
|
||||||
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);
|
||||||
float ratePotion(const MWWorld::Ptr& item, const MWWorld::Ptr& actor);
|
float ratePotion(const MWWorld::Ptr& item, const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user