From 5242610366a7a489a99133684de08d60ab91ead2 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 14 Oct 2025 21:26:06 +0200 Subject: [PATCH] Differentiate between invalid and unapplied effects and stop marking unapplied revertable effects as applied in godmode --- apps/openmw/mwmechanics/spelleffects.cpp | 200 +++++++++++------------ components/esm3/activespells.hpp | 3 +- 2 files changed, 95 insertions(+), 108 deletions(-) diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index b8fa903b23..6c8bc292ec 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -44,18 +44,16 @@ namespace return effect.mMinMagnitude + Misc::Rng::rollDice(effect.mMaxMagnitude - effect.mMinMagnitude + 1, prng); } - void modifyAiSetting(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, - ESM::MagicEffect::Effects creatureEffect, MWMechanics::AiSetting setting, float magnitude, bool& invalid) + ESM::ActiveEffect::Flags modifyAiSetting(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, + ESM::MagicEffect::Effects creatureEffect, MWMechanics::AiSetting setting, float magnitude) { if (target == MWMechanics::getPlayer() || (effect.mEffectId == creatureEffect) == target.getClass().isNpc()) - invalid = true; - else - { - auto& creatureStats = target.getClass().getCreatureStats(target); - auto stat = creatureStats.getAiSetting(setting); - stat.setModifier(static_cast(stat.getModifier() + magnitude)); - creatureStats.setAiSetting(setting, stat); - } + return ESM::ActiveEffect::Flag_Invalid; + auto& creatureStats = target.getClass().getCreatureStats(target); + auto stat = creatureStats.getAiSetting(setting); + stat.setModifier(static_cast(stat.getModifier() + magnitude)); + creatureStats.setAiSetting(setting, stat); + return ESM::ActiveEffect::Flag_Applied; } void adjustDynamicStat(const MWWorld::Ptr& target, int index, float magnitude, bool allowDecreaseBelowZero = false, @@ -420,12 +418,12 @@ 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& affectedHealth, bool& recalculateMagicka) + ESM::ActiveEffect::Flags applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, + const ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, bool& receivedMagicDamage, + bool& affectedHealth, bool& recalculateMagicka) { const auto world = MWBase::Environment::get().getWorld(); - bool godmode = target == getPlayer() && world->getGodModeState(); + const bool godmode = target == getPlayer() && world->getGodModeState(); switch (effect.mEffectId) { case ESM::MagicEffect::CureCommonDisease: @@ -469,7 +467,7 @@ namespace MWMechanics case ESM::MagicEffect::AlmsiviIntervention: case ESM::MagicEffect::DivineIntervention: if (target != getPlayer()) - invalid = true; + return ESM::ActiveEffect::Flag_Invalid; else if (world->isTeleportingEnabled()) { std::string_view marker @@ -494,7 +492,7 @@ namespace MWMechanics break; case ESM::MagicEffect::Mark: if (target != getPlayer()) - invalid = true; + return ESM::ActiveEffect::Flag_Invalid; else if (world->isTeleportingEnabled()) world->getPlayer().markPosition(target.getCell(), target.getRefData().getPosition()); else if (caster == getPlayer()) @@ -502,7 +500,7 @@ namespace MWMechanics break; case ESM::MagicEffect::Recall: if (target != getPlayer()) - invalid = true; + return ESM::ActiveEffect::Flag_Invalid; else if (world->isTeleportingEnabled()) { MWWorld::CellStore* markedCell = nullptr; @@ -528,7 +526,7 @@ namespace MWMechanics case ESM::MagicEffect::CommandHumanoid: if (caster.isEmpty() || !caster.getClass().isActor() || target == getPlayer() || (effect.mEffectId == ESM::MagicEffect::CommandCreature) == target.getClass().isNpc()) - invalid = true; + return ESM::ActiveEffect::Flag_Invalid; else if (effect.mMagnitude >= target.getClass().getCreatureStats(target).getLevel()) { MWMechanics::AiFollow package(caster, true); @@ -536,41 +534,37 @@ namespace MWMechanics } break; case ESM::MagicEffect::ExtraSpell: - if (target.getClass().hasInventoryStore(target)) + if (!target.getClass().hasInventoryStore(target)) + return ESM::ActiveEffect::Flag_Invalid; + if (target != getPlayer()) { - if (target != getPlayer()) + auto& store = target.getClass().getInventoryStore(target); + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { - auto& store = target.getClass().getInventoryStore(target); - for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + // Unequip everything except weapons, torches, and pants + switch (slot) { - // Unequip everything except weapons, torches, and pants - switch (slot) + case MWWorld::InventoryStore::Slot_Ammunition: + case MWWorld::InventoryStore::Slot_CarriedRight: + case MWWorld::InventoryStore::Slot_Pants: + continue; + case MWWorld::InventoryStore::Slot_CarriedLeft: { - case MWWorld::InventoryStore::Slot_Ammunition: - case MWWorld::InventoryStore::Slot_CarriedRight: - case MWWorld::InventoryStore::Slot_Pants: + auto carried = store.getSlot(slot); + if (carried == store.end() || carried.getType() != MWWorld::ContainerStore::Type_Armor) continue; - case MWWorld::InventoryStore::Slot_CarriedLeft: - { - auto carried = store.getSlot(slot); - if (carried == store.end() - || carried.getType() != MWWorld::ContainerStore::Type_Armor) - continue; - [[fallthrough]]; - } - default: - store.unequipSlot(slot); + [[fallthrough]]; } + default: + store.unequipSlot(slot); } } } - else - invalid = true; break; case ESM::MagicEffect::TurnUndead: if (target.getClass().isNpc() || target.get()->mBase->mData.mType != ESM::Creature::Undead) - invalid = true; + return ESM::ActiveEffect::Flag_Invalid; else { auto& creatureStats = target.getClass().getCreatureStats(target); @@ -581,32 +575,33 @@ namespace MWMechanics break; case ESM::MagicEffect::FrenzyCreature: case ESM::MagicEffect::FrenzyHumanoid: - modifyAiSetting( - target, effect, ESM::MagicEffect::FrenzyCreature, AiSetting::Fight, effect.mMagnitude, invalid); - break; + return modifyAiSetting( + target, effect, ESM::MagicEffect::FrenzyCreature, AiSetting::Fight, effect.mMagnitude); case ESM::MagicEffect::CalmCreature: case ESM::MagicEffect::CalmHumanoid: - modifyAiSetting( - target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, -effect.mMagnitude, invalid); - if (!invalid && effect.mMagnitude > 0) + { + ESM::ActiveEffect::Flags applied = modifyAiSetting( + target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, -effect.mMagnitude); + if (applied != ESM::ActiveEffect::Flag_Applied) + return applied; + if (effect.mMagnitude > 0) { auto& creatureStats = target.getClass().getCreatureStats(target); creatureStats.getAiSequence().stopCombat(); } break; + } case ESM::MagicEffect::DemoralizeCreature: case ESM::MagicEffect::DemoralizeHumanoid: - modifyAiSetting( - target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, effect.mMagnitude, invalid); - break; + return modifyAiSetting( + target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, effect.mMagnitude); case ESM::MagicEffect::RallyCreature: case ESM::MagicEffect::RallyHumanoid: - modifyAiSetting( - target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, -effect.mMagnitude, invalid); - break; + return modifyAiSetting( + target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, -effect.mMagnitude); case ESM::MagicEffect::Charm: if (!target.getClass().isNpc()) - invalid = true; + return ESM::ActiveEffect::Flag_Invalid; break; case ESM::MagicEffect::Sound: if (target == getPlayer()) @@ -643,16 +638,12 @@ namespace MWMechanics case ESM::MagicEffect::SummonCreature04: case ESM::MagicEffect::SummonCreature05: if (!target.isInCell()) - invalid = true; - else - effect.mArg = summonCreature(effect.mEffectId, target); + return ESM::ActiveEffect::Flag_Invalid; + effect.mArg = summonCreature(effect.mEffectId, target); break; case ESM::MagicEffect::BoundGloves: if (!target.getClass().hasInventoryStore(target)) - { - invalid = true; - break; - } + return ESM::ActiveEffect::Flag_Invalid; addBoundItem(ESM::RefId::stringRefId(world->getStore() .get() .find("sMagicBoundRightGauntletID") @@ -672,10 +663,7 @@ namespace MWMechanics case ESM::MagicEffect::BoundShield: { if (!target.getClass().hasInventoryStore(target)) - { - invalid = true; - break; - } + return ESM::ActiveEffect::Flag_Invalid; const std::string& item = sBoundItemsMap.at(effect.mEffectId); const MWWorld::Store& gmst = world->getStore().get(); const ESM::RefId itemId = ESM::RefId::stringRefId(gmst.find(item)->mValue.getString()); @@ -762,7 +750,9 @@ namespace MWMechanics case ESM::MagicEffect::DrainHealth: case ESM::MagicEffect::DrainMagicka: case ESM::MagicEffect::DrainFatigue: - if (!godmode) + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else { int index = effect.mEffectId - ESM::MagicEffect::DrainHealth; // Unlike Absorb and Damage effects Drain effects can bring stats below zero @@ -781,8 +771,9 @@ namespace MWMechanics target, effect.mEffectId - ESM::MagicEffect::FortifyHealth, effect.mMagnitude, false, true); break; case ESM::MagicEffect::DrainAttribute: - if (!godmode) - damageAttribute(target, effect, effect.mMagnitude); + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + damageAttribute(target, effect, effect.mMagnitude); break; case ESM::MagicEffect::FortifyAttribute: // Abilities affect base stats, but not for drain @@ -798,8 +789,9 @@ namespace MWMechanics fortifyAttribute(target, effect, effect.mMagnitude); break; case ESM::MagicEffect::DrainSkill: - if (!godmode && target.getClass().isNpc()) - damageSkill(target, effect, effect.mMagnitude); + if (godmode || !target.getClass().isNpc()) + return ESM::ActiveEffect::Flag_Remove; + damageSkill(target, effect, effect.mMagnitude); break; case ESM::MagicEffect::FortifySkill: if (target.getClass().isNpc()) @@ -821,7 +813,9 @@ namespace MWMechanics case ESM::MagicEffect::AbsorbHealth: case ESM::MagicEffect::AbsorbMagicka: case ESM::MagicEffect::AbsorbFatigue: - if (!godmode) + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else { int index = effect.mEffectId - ESM::MagicEffect::AbsorbHealth; adjustDynamicStat(target, index, -effect.mMagnitude); @@ -832,7 +826,9 @@ namespace MWMechanics } break; case ESM::MagicEffect::AbsorbAttribute: - if (!godmode) + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else { damageAttribute(target, effect, effect.mMagnitude); if (!caster.isEmpty()) @@ -840,7 +836,9 @@ namespace MWMechanics } break; case ESM::MagicEffect::AbsorbSkill: - if (!godmode) + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else { if (target.getClass().isNpc()) damageSkill(target, effect, effect.mMagnitude); @@ -851,10 +849,7 @@ namespace MWMechanics case ESM::MagicEffect::DisintegrateArmor: { if (!target.getClass().hasInventoryStore(target)) - { - invalid = true; - break; - } + return ESM::ActiveEffect::Flag_Invalid; if (godmode) break; static const std::array priorities{ @@ -877,24 +872,18 @@ namespace MWMechanics } case ESM::MagicEffect::DisintegrateWeapon: if (!target.getClass().hasInventoryStore(target)) - { - invalid = true; - break; - } + return ESM::ActiveEffect::Flag_Invalid; if (!godmode) disintegrateSlot(target, MWWorld::InventoryStore::Slot_CarriedRight, effect.mMagnitude); break; } + return ESM::ActiveEffect::Flag_Applied; } bool shouldRemoveEffect(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect) { - if ((effect.mFlags & (ESM::ActiveEffect::Flag_Remove | ESM::ActiveEffect::Flag_Applied)) - == ESM::ActiveEffect::Flag_Remove) - { - // Previously marked invalid + if (effect.mFlags & ESM::ActiveEffect::Flag_Invalid) return true; - } const auto world = MWBase::Environment::get().getWorld(); switch (effect.mEffectId) { @@ -936,7 +925,7 @@ namespace MWMechanics ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt, bool playNonLooping) { const auto world = MWBase::Environment::get().getWorld(); - bool invalid = false; + int32_t applied = ESM::ActiveEffect::Flag_Remove; bool receivedMagicDamage = false; bool recalculateMagicka = false; bool affectedHealth = false; @@ -946,8 +935,8 @@ namespace MWMechanics for (auto& otherEffect : spellParams.getEffects()) { if (isCorprusEffect(otherEffect)) - applyMagicEffect(target, caster, spellParams, otherEffect, invalid, receivedMagicDamage, - affectedHealth, recalculateMagicka); + applyMagicEffect(target, caster, spellParams, otherEffect, receivedMagicDamage, affectedHealth, + recalculateMagicka); } if (target == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}"); @@ -989,7 +978,7 @@ namespace MWMechanics } } else - invalid = true; + applied |= ESM::ActiveEffect::Flag_Invalid; } else if (effect.mEffectId == ESM::MagicEffect::Open) { @@ -1021,11 +1010,11 @@ namespace MWMechanics } } else - invalid = true; + applied |= ESM::ActiveEffect::Flag_Invalid; } else if (!target.getClass().isActor()) { - invalid = true; + applied |= ESM::ActiveEffect::Flag_Invalid; } else { @@ -1041,7 +1030,7 @@ namespace MWMechanics auto& stats = target.getClass().getCreatureStats(target); auto& magnitudes = stats.getMagicEffects(); if (!spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues) - && !(effect.mFlags & ESM::ActiveEffect::Flag_Applied)) + && !(effect.mFlags & ESM::ActiveEffect::Flag_Remove)) { MagicApplicationResult::Type result = applyProtections(target, caster, spellParams, effect, magicEffect); @@ -1051,7 +1040,7 @@ namespace MWMechanics float oldMagnitude = 0.f; if (effect.mFlags & ESM::ActiveEffect::Flag_Applied) oldMagnitude = effect.mMagnitude; - else + else if (!(effect.mFlags & ESM::ActiveEffect::Flag_Remove)) { bool isTemporary = spellParams.hasFlag(ESM::ActiveSpells::Flag_Temporary); bool isEquipment = spellParams.hasFlag(ESM::ActiveSpells::Flag_Equipment); @@ -1086,26 +1075,27 @@ namespace MWMechanics } } if (effect.mEffectId == ESM::MagicEffect::Corprus) + { spellParams.worsen(); + applied |= ESM::ActiveEffect::Flag_Applied; + } else - applyMagicEffect(target, caster, spellParams, effect, invalid, receivedMagicDamage, affectedHealth, - recalculateMagicka); + applied |= applyMagicEffect( + target, caster, spellParams, effect, receivedMagicDamage, affectedHealth, recalculateMagicka); effect.mMagnitude = magnitude; magnitudes.add(EffectKey(effect.mEffectId, effect.getSkillOrAttribute()), EffectParam(effect.mMagnitude - oldMagnitude)); } effect.mTimeLeft -= dt; - if (invalid) + if (applied & ESM::ActiveEffect::Flag_Invalid) { effect.mTimeLeft = 0; - effect.mFlags |= ESM::ActiveEffect::Flag_Remove; auto anim = world->getAnimation(target); if (anim) anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId)); // Note that we can't return REMOVED here because the effect still needs to be detectable } - else - effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove; + effect.mFlags |= applied; if (recalculateMagicka) target.getClass().getCreatureStats(target).recalculateMagicka(); return { MagicApplicationResult::Type::APPLIED, receivedMagicDamage, affectedHealth }; @@ -1116,7 +1106,6 @@ namespace MWMechanics { const auto world = MWBase::Environment::get().getWorld(); auto& magnitudes = target.getClass().getCreatureStats(target).getMagicEffects(); - bool invalid; switch (effect.mEffectId) { case ESM::MagicEffect::CommandCreature: @@ -1144,18 +1133,16 @@ namespace MWMechanics break; case ESM::MagicEffect::FrenzyCreature: case ESM::MagicEffect::FrenzyHumanoid: - modifyAiSetting( - target, effect, ESM::MagicEffect::FrenzyCreature, AiSetting::Fight, -effect.mMagnitude, invalid); + modifyAiSetting(target, effect, ESM::MagicEffect::FrenzyCreature, AiSetting::Fight, -effect.mMagnitude); break; case ESM::MagicEffect::CalmCreature: case ESM::MagicEffect::CalmHumanoid: - modifyAiSetting( - target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, effect.mMagnitude, invalid); + modifyAiSetting(target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, effect.mMagnitude); break; case ESM::MagicEffect::DemoralizeCreature: case ESM::MagicEffect::DemoralizeHumanoid: modifyAiSetting( - target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, -effect.mMagnitude, invalid); + target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, -effect.mMagnitude); break; case ESM::MagicEffect::NightEye: { @@ -1174,8 +1161,7 @@ namespace MWMechanics break; case ESM::MagicEffect::RallyCreature: case ESM::MagicEffect::RallyHumanoid: - modifyAiSetting( - target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, effect.mMagnitude, invalid); + modifyAiSetting(target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, effect.mMagnitude); break; case ESM::MagicEffect::Sound: if (magnitudes.getOrDefault(effect.mEffectId).getModifier() <= 0.f && target == getPlayer()) diff --git a/components/esm3/activespells.hpp b/components/esm3/activespells.hpp index 19e1f53be5..3058e8e4de 100644 --- a/components/esm3/activespells.hpp +++ b/components/esm3/activespells.hpp @@ -26,7 +26,8 @@ namespace ESM Flag_Remove = 1 << 1, Flag_Ignore_Resistances = 1 << 2, Flag_Ignore_Reflect = 1 << 3, - Flag_Ignore_SpellAbsorption = 1 << 4 + Flag_Ignore_SpellAbsorption = 1 << 4, + Flag_Invalid = 1 << 5 }; int32_t mEffectId;