1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-22 18:11:34 +00:00

Keep showing NPC health while dealing damage

This commit is contained in:
unknown 2022-09-03 19:49:59 +02:00
parent 7a0a11b30c
commit f68e7ce0b5
4 changed files with 52 additions and 27 deletions

View file

@ -6,6 +6,7 @@
Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses
Bug #5129: Stuttering animation on Centurion Archer Bug #5129: Stuttering animation on Centurion Archer
Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load
Bug #6427: Enemy health bar disappears before damaging effect ends
Bug #6937: Divided by Nix Hounds quest is broken Bug #6937: Divided by Nix Hounds quest is broken
Bug #6939: OpenMW-CS: ID columns are too short Bug #6939: OpenMW-CS: ID columns are too short
Bug #6949: Sun Damage effect doesn't work in quasi exteriors Bug #6949: Sun Damage effect doesn't work in quasi exteriors

View file

@ -13,11 +13,13 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include "actorutil.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "spellcasting.hpp" #include "spellcasting.hpp"
#include "spelleffects.hpp" #include "spelleffects.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwrender/animation.hpp" #include "../mwrender/animation.hpp"
@ -230,6 +232,9 @@ namespace MWMechanics
} }
} }
const MWWorld::Ptr player = MWMechanics::getPlayer();
bool updatedHitOverlay = false;
bool updatedEnemy = false;
// Update effects // Update effects
for(auto spellIt = mSpells.begin(); spellIt != mSpells.end();) for(auto spellIt = mSpells.begin(); spellIt != mSpells.end();)
{ {
@ -239,7 +244,7 @@ namespace MWMechanics
for(auto it = spellIt->mEffects.begin(); it != spellIt->mEffects.end();) for(auto it = spellIt->mEffects.begin(); it != spellIt->mEffects.end();)
{ {
auto result = applyMagicEffect(ptr, caster, *spellIt, *it, duration); auto result = applyMagicEffect(ptr, caster, *spellIt, *it, duration);
if(result == MagicApplicationResult::REFLECTED) if(result.mType == MagicApplicationResult::Type::REFLECTED)
{ {
if(!reflected) if(!reflected)
{ {
@ -253,10 +258,22 @@ namespace MWMechanics
reflectedEffect.mFlags = ESM::ActiveEffect::Flag_Ignore_Reflect | ESM::ActiveEffect::Flag_Ignore_SpellAbsorption; reflectedEffect.mFlags = ESM::ActiveEffect::Flag_Ignore_Reflect | ESM::ActiveEffect::Flag_Ignore_SpellAbsorption;
it = spellIt->mEffects.erase(it); it = spellIt->mEffects.erase(it);
} }
else if(result == MagicApplicationResult::REMOVED) else if(result.mType == MagicApplicationResult::Type::REMOVED)
it = spellIt->mEffects.erase(it); it = spellIt->mEffects.erase(it);
else else
{
++it; ++it;
if(!updatedEnemy && result.mShowHealth && caster == player && ptr != player)
{
MWBase::Environment::get().getWindowManager()->setEnemy(ptr);
updatedEnemy = true;
}
if(!updatedHitOverlay && result.mShowHit && ptr == player)
{
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
updatedHitOverlay = true;
}
}
removedSpell = applyPurges(ptr, &spellIt, &it); removedSpell = applyPurges(ptr, &spellIt, &it);
if(removedSpell) if(removedSpell)
break; break;

View file

@ -299,7 +299,7 @@ namespace
stats.setMagicka(magicka); stats.setMagicka(magicka);
} }
MWMechanics::MagicApplicationResult applyProtections(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, MWMechanics::MagicApplicationResult::Type applyProtections(const MWWorld::Ptr& target, const MWWorld::Ptr& caster,
const MWMechanics::ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, const ESM::MagicEffect* magicEffect) const MWMechanics::ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, const ESM::MagicEffect* magicEffect)
{ {
auto& stats = target.getClass().getCreatureStats(target); auto& stats = target.getClass().getCreatureStats(target);
@ -323,7 +323,7 @@ namespace
{ {
if(canReflect && Misc::Rng::roll0to99(prng) < activeEffect.mMagnitude) if(canReflect && Misc::Rng::roll0to99(prng) < activeEffect.mMagnitude)
{ {
return MWMechanics::MagicApplicationResult::REFLECTED; return MWMechanics::MagicApplicationResult::Type::REFLECTED;
} }
} }
else if(activeEffect.mEffectId == ESM::MagicEffect::SpellAbsorption) else if(activeEffect.mEffectId == ESM::MagicEffect::SpellAbsorption)
@ -331,7 +331,7 @@ namespace
if(canAbsorb && Misc::Rng::roll0to99(prng) < activeEffect.mMagnitude) if(canAbsorb && Misc::Rng::roll0to99(prng) < activeEffect.mMagnitude)
{ {
absorbSpell(spellParams.getId(), caster, target); absorbSpell(spellParams.getId(), caster, target);
return MWMechanics::MagicApplicationResult::REMOVED; return MWMechanics::MagicApplicationResult::Type::REMOVED;
} }
} }
} }
@ -356,12 +356,12 @@ namespace
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}");
else if (caster == MWMechanics::getPlayer()) else if (caster == MWMechanics::getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}");
return MWMechanics::MagicApplicationResult::REMOVED; return MWMechanics::MagicApplicationResult::Type::REMOVED;
} }
effect.mMinMagnitude *= magnitudeMult; effect.mMinMagnitude *= magnitudeMult;
effect.mMaxMagnitude *= magnitudeMult; effect.mMaxMagnitude *= magnitudeMult;
} }
return MWMechanics::MagicApplicationResult::APPLIED; return MWMechanics::MagicApplicationResult::Type::APPLIED;
} }
static const std::map<int, std::string> sBoundItemsMap{ static const std::map<int, std::string> sBoundItemsMap{
@ -382,7 +382,7 @@ namespace
namespace MWMechanics namespace MWMechanics
{ {
void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, bool& invalid, bool& receivedMagicDamage, bool& recalculateMagicka) void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, bool& invalid, bool& receivedMagicDamage, bool& affectedHealth, bool& recalculateMagicka)
{ {
const auto world = MWBase::Environment::get().getWorld(); const auto world = MWBase::Environment::get().getWorld();
bool godmode = target == getPlayer() && world->getGodModeState(); bool godmode = target == getPlayer() && world->getGodModeState();
@ -606,7 +606,7 @@ void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, co
static const bool uncappedDamageFatigue = Settings::Manager::getBool("uncapped damage fatigue", "Game"); static const bool uncappedDamageFatigue = Settings::Manager::getBool("uncapped damage fatigue", "Game");
adjustDynamicStat(target, index, -effect.mMagnitude, index == 2 && uncappedDamageFatigue); adjustDynamicStat(target, index, -effect.mMagnitude, index == 2 && uncappedDamageFatigue);
if(index == 0) if(index == 0)
receivedMagicDamage = true; receivedMagicDamage = affectedHealth = true;
} }
} }
break; break;
@ -641,6 +641,8 @@ void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, co
restoreSkill(target, effect, effect.mMagnitude); restoreSkill(target, effect, effect.mMagnitude);
break; break;
case ESM::MagicEffect::RestoreHealth: case ESM::MagicEffect::RestoreHealth:
affectedHealth = true;
[[fallthrough]];
case ESM::MagicEffect::RestoreMagicka: case ESM::MagicEffect::RestoreMagicka:
case ESM::MagicEffect::RestoreFatigue: case ESM::MagicEffect::RestoreFatigue:
adjustDynamicStat(target, effect.mEffectId - ESM::MagicEffect::RestoreHealth, effect.mMagnitude); adjustDynamicStat(target, effect.mEffectId - ESM::MagicEffect::RestoreHealth, effect.mMagnitude);
@ -662,7 +664,7 @@ void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, co
float damage = effect.mMagnitude * damageScale; float damage = effect.mMagnitude * damageScale;
adjustDynamicStat(target, 0, -damage); adjustDynamicStat(target, 0, -damage);
if (damage > 0.f) if (damage > 0.f)
receivedMagicDamage = true; receivedMagicDamage = affectedHealth = true;
} }
break; break;
case ESM::MagicEffect::DrainHealth: case ESM::MagicEffect::DrainHealth:
@ -674,7 +676,7 @@ void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, co
int index = effect.mEffectId - ESM::MagicEffect::DrainHealth; int index = effect.mEffectId - ESM::MagicEffect::DrainHealth;
adjustDynamicStat(target, index, -effect.mMagnitude, uncappedDamageFatigue && index == 2); adjustDynamicStat(target, index, -effect.mMagnitude, uncappedDamageFatigue && index == 2);
if(index == 0) if(index == 0)
receivedMagicDamage = true; receivedMagicDamage = affectedHealth = true;
} }
break; break;
case ESM::MagicEffect::FortifyHealth: case ESM::MagicEffect::FortifyHealth:
@ -733,7 +735,7 @@ void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, co
if(!caster.isEmpty()) if(!caster.isEmpty())
adjustDynamicStat(caster, index, effect.mMagnitude); adjustDynamicStat(caster, index, effect.mMagnitude);
if(index == 0) if(index == 0)
receivedMagicDamage = true; receivedMagicDamage = affectedHealth = true;
} }
break; break;
case ESM::MagicEffect::AbsorbAttribute: case ESM::MagicEffect::AbsorbAttribute:
@ -839,22 +841,23 @@ MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorl
bool invalid = false; bool invalid = false;
bool receivedMagicDamage = false; bool receivedMagicDamage = false;
bool recalculateMagicka = false; bool recalculateMagicka = false;
bool affectedHealth = false;
if(effect.mEffectId == ESM::MagicEffect::Corprus && spellParams.shouldWorsen()) if(effect.mEffectId == ESM::MagicEffect::Corprus && spellParams.shouldWorsen())
{ {
spellParams.worsen(); spellParams.worsen();
for(auto& otherEffect : spellParams.getEffects()) for(auto& otherEffect : spellParams.getEffects())
{ {
if(isCorprusEffect(otherEffect)) if(isCorprusEffect(otherEffect))
applyMagicEffect(target, caster, spellParams, otherEffect, invalid, receivedMagicDamage, recalculateMagicka); applyMagicEffect(target, caster, spellParams, otherEffect, invalid, receivedMagicDamage, affectedHealth, recalculateMagicka);
} }
if(target == getPlayer()) if(target == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}");
return MagicApplicationResult::APPLIED; return {MagicApplicationResult::Type::APPLIED, receivedMagicDamage, affectedHealth};
} }
else if(shouldRemoveEffect(target, effect)) else if(shouldRemoveEffect(target, effect))
{ {
onMagicEffectRemoved(target, spellParams, effect); onMagicEffectRemoved(target, spellParams, effect);
return MagicApplicationResult::REMOVED; return {MagicApplicationResult::Type::REMOVED, receivedMagicDamage, affectedHealth};
} }
const auto* magicEffect = world->getStore().get<ESM::MagicEffect>().find(effect.mEffectId); const auto* magicEffect = world->getStore().get<ESM::MagicEffect>().find(effect.mEffectId);
if(effect.mFlags & ESM::ActiveEffect::Flag_Applied) if(effect.mFlags & ESM::ActiveEffect::Flag_Applied)
@ -862,10 +865,10 @@ MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorl
if(magicEffect->mData.mFlags & ESM::MagicEffect::Flags::AppliedOnce) if(magicEffect->mData.mFlags & ESM::MagicEffect::Flags::AppliedOnce)
{ {
effect.mTimeLeft -= dt; effect.mTimeLeft -= dt;
return MagicApplicationResult::APPLIED; return {MagicApplicationResult::Type::APPLIED, receivedMagicDamage, affectedHealth};
} }
else if(!dt) else if(!dt)
return MagicApplicationResult::APPLIED; return {MagicApplicationResult::Type::APPLIED, receivedMagicDamage, affectedHealth};
} }
if(effect.mEffectId == ESM::MagicEffect::Lock) if(effect.mEffectId == ESM::MagicEffect::Lock)
{ {
@ -925,9 +928,9 @@ MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorl
auto& magnitudes = stats.getMagicEffects(); auto& magnitudes = stats.getMagicEffects();
if(spellParams.getType() != ESM::ActiveSpells::Type_Ability && !(effect.mFlags & ESM::ActiveEffect::Flag_Applied)) if(spellParams.getType() != ESM::ActiveSpells::Type_Ability && !(effect.mFlags & ESM::ActiveEffect::Flag_Applied))
{ {
MagicApplicationResult result = applyProtections(target, caster, spellParams, effect, magicEffect); MagicApplicationResult::Type result = applyProtections(target, caster, spellParams, effect, magicEffect);
if(result != MagicApplicationResult::APPLIED) if(result != MagicApplicationResult::Type::APPLIED)
return result; return {result, receivedMagicDamage, affectedHealth};
} }
float oldMagnitude = 0.f; float oldMagnitude = 0.f;
if(effect.mFlags & ESM::ActiveEffect::Flag_Applied) if(effect.mFlags & ESM::ActiveEffect::Flag_Applied)
@ -956,13 +959,13 @@ MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorl
effect.mMagnitude = oldMagnitude; effect.mMagnitude = oldMagnitude;
effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove; effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove;
effect.mTimeLeft -= dt; effect.mTimeLeft -= dt;
return MagicApplicationResult::APPLIED; return {MagicApplicationResult::Type::APPLIED, receivedMagicDamage, affectedHealth};
} }
} }
if(effect.mEffectId == ESM::MagicEffect::Corprus) if(effect.mEffectId == ESM::MagicEffect::Corprus)
spellParams.worsen(); spellParams.worsen();
else else
applyMagicEffect(target, caster, spellParams, effect, invalid, receivedMagicDamage, recalculateMagicka); applyMagicEffect(target, caster, spellParams, effect, invalid, receivedMagicDamage, affectedHealth, recalculateMagicka);
effect.mMagnitude = magnitude; effect.mMagnitude = magnitude;
magnitudes.add(EffectKey(effect.mEffectId, effect.mArg), EffectParam(effect.mMagnitude - oldMagnitude)); magnitudes.add(EffectKey(effect.mEffectId, effect.mArg), EffectParam(effect.mMagnitude - oldMagnitude));
} }
@ -977,11 +980,9 @@ MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorl
} }
else else
effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove; effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove;
if (receivedMagicDamage && target == getPlayer())
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
if(recalculateMagicka) if(recalculateMagicka)
target.getClass().getCreatureStats(target).recalculateMagicka(); target.getClass().getCreatureStats(target).recalculateMagicka();
return MagicApplicationResult::APPLIED; return {MagicApplicationResult::Type::APPLIED, receivedMagicDamage, affectedHealth};
} }
void removeMagicEffect(const MWWorld::Ptr& target, ActiveSpells::ActiveSpellParams& spellParams, const ESM::ActiveEffect& effect) void removeMagicEffect(const MWWorld::Ptr& target, ActiveSpells::ActiveSpellParams& spellParams, const ESM::ActiveEffect& effect)

View file

@ -13,10 +13,16 @@ namespace MWWorld
namespace MWMechanics namespace MWMechanics
{ {
enum class MagicApplicationResult struct MagicApplicationResult
{
enum class Type
{ {
APPLIED, REMOVED, REFLECTED APPLIED, REMOVED, REFLECTED
}; };
Type mType;
bool mShowHit;
bool mShowHealth;
};
// Applies a tick of a single effect. Returns true if the effect should be removed immediately // Applies a tick of a single effect. Returns true if the effect should be removed immediately
MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt); MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt);