diff --git a/CHANGELOG.md b/CHANGELOG.md index adf69f7a47..28691424da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses Bug #5129: Stuttering animation on Centurion Archer 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 #6939: OpenMW-CS: ID columns are too short Bug #6949: Sun Damage effect doesn't work in quasi exteriors diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index df4deada05..040214848c 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -13,11 +13,13 @@ #include +#include "actorutil.hpp" #include "creaturestats.hpp" #include "spellcasting.hpp" #include "spelleffects.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwbase/world.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 for(auto spellIt = mSpells.begin(); spellIt != mSpells.end();) { @@ -239,7 +244,7 @@ namespace MWMechanics for(auto it = spellIt->mEffects.begin(); it != spellIt->mEffects.end();) { auto result = applyMagicEffect(ptr, caster, *spellIt, *it, duration); - if(result == MagicApplicationResult::REFLECTED) + if(result.mType == MagicApplicationResult::Type::REFLECTED) { if(!reflected) { @@ -253,10 +258,22 @@ namespace MWMechanics reflectedEffect.mFlags = ESM::ActiveEffect::Flag_Ignore_Reflect | ESM::ActiveEffect::Flag_Ignore_SpellAbsorption; it = spellIt->mEffects.erase(it); } - else if(result == MagicApplicationResult::REMOVED) + else if(result.mType == MagicApplicationResult::Type::REMOVED) it = spellIt->mEffects.erase(it); else + { ++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); if(removedSpell) break; diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index e16e6973b1..4a9cf49263 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -299,7 +299,7 @@ namespace 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) { auto& stats = target.getClass().getCreatureStats(target); @@ -323,7 +323,7 @@ namespace { if(canReflect && Misc::Rng::roll0to99(prng) < activeEffect.mMagnitude) { - return MWMechanics::MagicApplicationResult::REFLECTED; + return MWMechanics::MagicApplicationResult::Type::REFLECTED; } } else if(activeEffect.mEffectId == ESM::MagicEffect::SpellAbsorption) @@ -331,7 +331,7 @@ namespace if(canAbsorb && Misc::Rng::roll0to99(prng) < activeEffect.mMagnitude) { 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}"); else if (caster == MWMechanics::getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}"); - return MWMechanics::MagicApplicationResult::REMOVED; + return MWMechanics::MagicApplicationResult::Type::REMOVED; } effect.mMinMagnitude *= magnitudeMult; effect.mMaxMagnitude *= magnitudeMult; } - return MWMechanics::MagicApplicationResult::APPLIED; + return MWMechanics::MagicApplicationResult::Type::APPLIED; } static const std::map sBoundItemsMap{ @@ -382,7 +382,7 @@ namespace 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(); 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"); adjustDynamicStat(target, index, -effect.mMagnitude, index == 2 && uncappedDamageFatigue); if(index == 0) - receivedMagicDamage = true; + receivedMagicDamage = affectedHealth = true; } } break; @@ -641,6 +641,8 @@ void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, co restoreSkill(target, effect, effect.mMagnitude); break; case ESM::MagicEffect::RestoreHealth: + affectedHealth = true; + [[fallthrough]]; case ESM::MagicEffect::RestoreMagicka: case ESM::MagicEffect::RestoreFatigue: 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; adjustDynamicStat(target, 0, -damage); if (damage > 0.f) - receivedMagicDamage = true; + receivedMagicDamage = affectedHealth = true; } break; 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; adjustDynamicStat(target, index, -effect.mMagnitude, uncappedDamageFatigue && index == 2); if(index == 0) - receivedMagicDamage = true; + receivedMagicDamage = affectedHealth = true; } break; case ESM::MagicEffect::FortifyHealth: @@ -733,7 +735,7 @@ void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, co if(!caster.isEmpty()) adjustDynamicStat(caster, index, effect.mMagnitude); if(index == 0) - receivedMagicDamage = true; + receivedMagicDamage = affectedHealth = true; } break; case ESM::MagicEffect::AbsorbAttribute: @@ -839,22 +841,23 @@ MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorl bool invalid = false; bool receivedMagicDamage = false; bool recalculateMagicka = false; + bool affectedHealth = false; if(effect.mEffectId == ESM::MagicEffect::Corprus && spellParams.shouldWorsen()) { spellParams.worsen(); for(auto& otherEffect : spellParams.getEffects()) { if(isCorprusEffect(otherEffect)) - applyMagicEffect(target, caster, spellParams, otherEffect, invalid, receivedMagicDamage, recalculateMagicka); + applyMagicEffect(target, caster, spellParams, otherEffect, invalid, receivedMagicDamage, affectedHealth, recalculateMagicka); } if(target == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}"); - return MagicApplicationResult::APPLIED; + return {MagicApplicationResult::Type::APPLIED, receivedMagicDamage, affectedHealth}; } else if(shouldRemoveEffect(target, effect)) { onMagicEffectRemoved(target, spellParams, effect); - return MagicApplicationResult::REMOVED; + return {MagicApplicationResult::Type::REMOVED, receivedMagicDamage, affectedHealth}; } const auto* magicEffect = world->getStore().get().find(effect.mEffectId); 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) { effect.mTimeLeft -= dt; - return MagicApplicationResult::APPLIED; + return {MagicApplicationResult::Type::APPLIED, receivedMagicDamage, affectedHealth}; } else if(!dt) - return MagicApplicationResult::APPLIED; + return {MagicApplicationResult::Type::APPLIED, receivedMagicDamage, affectedHealth}; } if(effect.mEffectId == ESM::MagicEffect::Lock) { @@ -925,9 +928,9 @@ MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorl auto& magnitudes = stats.getMagicEffects(); if(spellParams.getType() != ESM::ActiveSpells::Type_Ability && !(effect.mFlags & ESM::ActiveEffect::Flag_Applied)) { - MagicApplicationResult result = applyProtections(target, caster, spellParams, effect, magicEffect); - if(result != MagicApplicationResult::APPLIED) - return result; + MagicApplicationResult::Type result = applyProtections(target, caster, spellParams, effect, magicEffect); + if(result != MagicApplicationResult::Type::APPLIED) + return {result, receivedMagicDamage, affectedHealth}; } float oldMagnitude = 0.f; if(effect.mFlags & ESM::ActiveEffect::Flag_Applied) @@ -956,13 +959,13 @@ MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorl effect.mMagnitude = oldMagnitude; effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove; effect.mTimeLeft -= dt; - return MagicApplicationResult::APPLIED; + return {MagicApplicationResult::Type::APPLIED, receivedMagicDamage, affectedHealth}; } } if(effect.mEffectId == ESM::MagicEffect::Corprus) spellParams.worsen(); else - applyMagicEffect(target, caster, spellParams, effect, invalid, receivedMagicDamage, recalculateMagicka); + applyMagicEffect(target, caster, spellParams, effect, invalid, receivedMagicDamage, affectedHealth, recalculateMagicka); effect.mMagnitude = magnitude; magnitudes.add(EffectKey(effect.mEffectId, effect.mArg), EffectParam(effect.mMagnitude - oldMagnitude)); } @@ -977,11 +980,9 @@ MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorl } else effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove; - if (receivedMagicDamage && target == getPlayer()) - MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); if(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) diff --git a/apps/openmw/mwmechanics/spelleffects.hpp b/apps/openmw/mwmechanics/spelleffects.hpp index c3a55efd4c..57caa5d33a 100644 --- a/apps/openmw/mwmechanics/spelleffects.hpp +++ b/apps/openmw/mwmechanics/spelleffects.hpp @@ -13,9 +13,15 @@ namespace MWWorld namespace MWMechanics { - enum class MagicApplicationResult + struct MagicApplicationResult { - APPLIED, REMOVED, REFLECTED + enum class Type + { + 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