From 42cc27194b89d9ff2737d96d76c1e3a56590073e Mon Sep 17 00:00:00 2001 From: capostrophic Date: Sat, 4 Jan 2020 16:07:59 +0300 Subject: [PATCH 1/3] Fix reported spellcasting discrepancies Make ExplodeSpell behavior closer to Cast behavior (#5242) Nullify on-self absorb spells in a different way (#5241) Allow casting permanent spells through Cast/ExplodeSpell --- CHANGELOG.md | 2 ++ apps/openmw/mwmechanics/spellcasting.cpp | 19 +++++++++++++-- apps/openmw/mwscript/miscextensions.cpp | 31 ++++++++++++++++++------ 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ea093d8fa..d4649c46d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -188,6 +188,8 @@ Bug #5226: Reputation should be capped Bug #5229: Crash if mesh controller node has no data node Bug #5239: OpenMW-CS does not support non-ASCII characters in path names + Bug #5241: On-self absorb spells cannot be detected + Bug #5242: ExplodeSpell behavior differs from Cast behavior Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 4598cd75b9..5d3b83a622 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -447,9 +447,9 @@ namespace MWMechanics if (!checkEffectTarget(effectIt->mEffectID, target, caster, castByPlayer)) continue; - // caster needs to be an actor that's not the target for linked effects (e.g. Absorb) + // caster needs to be an actor for linked effects (e.g. Absorb) if (magicEffect->mData.mFlags & ESM::MagicEffect::CasterLinked - && (caster.isEmpty() || !caster.getClass().isActor() || caster == target)) + && (caster.isEmpty() || !caster.getClass().isActor())) continue; // If player is healing someone, show the target's HP bar @@ -552,6 +552,20 @@ namespace MWMechanics effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; effect.mMagnitude = magnitude; + // Avoid applying absorb effects if the caster is the target + // We still need the spell to be added + if (caster == target) + { + for (int i=0; i<5; ++i) + { + if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) + { + effect.mMagnitude = 0; + break; + } + } + } + bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); if (hasDuration && effectIt->mDuration == 0) { @@ -614,6 +628,7 @@ namespace MWMechanics else caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, absorbEffects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); + break; } } } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 8be3945a9c..4f84cfe47b 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1109,12 +1109,6 @@ namespace MWScript return; } - if (spell->mData.mType != ESM::Spell::ST_Spell && spell->mData.mType != ESM::Spell::ST_Power) - { - runtime.getContext().report("spellcasting failed: you can only cast spells and powers."); - return; - } - if (ptr == MWMechanics::getPlayer()) { MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); @@ -1152,9 +1146,32 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - std::string spell = runtime.getStringLiteral (runtime[0].mInteger); + std::string spellId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(spellId); + if (!spell) + { + runtime.getContext().report("spellcasting failed: cannot find spell \""+spellId+"\""); + return; + } + + if (ptr == MWMechanics::getPlayer()) + { + MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); + store.setSelectedEnchantItem(store.end()); + MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, ptr))); + MWBase::Environment::get().getWindowManager()->updateSpellWindow(); + return; + } + + if (ptr.getClass().isActor()) + { + MWMechanics::AiCast castPackage(ptr.getCellRef().getRefId(), spellId, true); + ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(castPackage, ptr); + return; + } + MWMechanics::CastSpell cast(ptr, ptr, false, true); cast.mHitPosition = ptr.getRefData().getPosition().asVec3(); cast.mAlwaysSucceed = true; From af2ea477d5efac748ed5c8dd3a943b46a1aaf160 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 9 Jan 2020 13:39:12 +0300 Subject: [PATCH 2/3] Don't use loops to detect absorb effects --- apps/openmw/mwmechanics/spellcasting.cpp | 40 ++++++++++-------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 5d3b83a622..0b7c27b86b 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -554,16 +554,11 @@ namespace MWMechanics // Avoid applying absorb effects if the caster is the target // We still need the spell to be added - if (caster == target) + if (caster == target + && effectIt->mEffectID >= ESM::MagicEffect::AbsorbAttribute + && effectIt->mEffectID <= ESM::MagicEffect::AbsorbSkill) { - for (int i=0; i<5; ++i) - { - if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) - { - effect.mMagnitude = 0; - break; - } - } + effect.mMagnitude = 0; } bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); @@ -614,22 +609,19 @@ namespace MWMechanics // magnitude, since we're transferring stats from the target to the caster if (!caster.isEmpty() && caster != target && caster.getClass().isActor()) { - for (int i=0; i<5; ++i) + if (effectIt->mEffectID >= ESM::MagicEffect::AbsorbAttribute && + effectIt->mEffectID <= ESM::MagicEffect::AbsorbSkill) { - if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) - { - std::vector absorbEffects; - ActiveSpells::ActiveEffect effect_ = effect; - effect_.mMagnitude *= -1; - absorbEffects.push_back(effect_); - if (reflected && Settings::Manager::getBool("classic reflected absorb spells behavior", "Game")) - target.getClass().getCreatureStats(target).getActiveSpells().addSpell("", true, - absorbEffects, mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); - else - caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, - absorbEffects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); - break; - } + std::vector absorbEffects; + ActiveSpells::ActiveEffect effect_ = effect; + effect_.mMagnitude *= -1; + absorbEffects.push_back(effect_); + if (reflected && Settings::Manager::getBool("classic reflected absorb spells behavior", "Game")) + target.getClass().getCreatureStats(target).getActiveSpells().addSpell("", true, + absorbEffects, mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); + else + caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, + absorbEffects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); } } } From 1251b32f0fc9144da23d6ad12ee1100c27c04a88 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 9 Jan 2020 13:55:14 +0300 Subject: [PATCH 3/3] Slightly reduce code duplication --- apps/openmw/mwscript/miscextensions.cpp | 11 ++--------- apps/openmw/mwworld/player.cpp | 12 ++++++++++++ apps/openmw/mwworld/player.hpp | 2 ++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 4f84cfe47b..ccf285844c 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1111,10 +1111,7 @@ namespace MWScript if (ptr == MWMechanics::getPlayer()) { - MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); - store.setSelectedEnchantItem(store.end()); - MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, ptr))); - MWBase::Environment::get().getWindowManager()->updateSpellWindow(); + MWBase::Environment::get().getWorld()->getPlayer().setSelectedSpell(spellId); return; } @@ -1122,7 +1119,6 @@ namespace MWScript { MWMechanics::AiCast castPackage(targetId, spellId, true); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(castPackage, ptr); - return; } @@ -1158,10 +1154,7 @@ namespace MWScript if (ptr == MWMechanics::getPlayer()) { - MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); - store.setSelectedEnchantItem(store.end()); - MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, ptr))); - MWBase::Environment::get().getWindowManager()->updateSpellWindow(); + MWBase::Environment::get().getWorld()->getPlayer().setSelectedSpell(spellId); return; } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 8e047677bc..8c5f526551 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -11,6 +11,7 @@ #include #include "../mwworld/esmstore.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -19,6 +20,7 @@ #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/spellcasting.hpp" #include "class.hpp" #include "ptr.hpp" @@ -492,4 +494,14 @@ namespace MWWorld { mPreviousItems.erase(boundItemId); } + + void Player::setSelectedSpell(const std::string& spellId) + { + Ptr player = getPlayer(); + InventoryStore& store = player.getClass().getInventoryStore(player); + store.setSelectedEnchantItem(store.end()); + int castChance = int(MWMechanics::getSpellSuccessChance(spellId, player)); + MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, castChance); + MWBase::Environment::get().getWindowManager()->updateSpellWindow(); + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 96ed20adfc..1e4b0ffdf5 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -135,6 +135,8 @@ namespace MWWorld void setPreviousItem(const std::string& boundItemId, const std::string& previousItemId); std::string getPreviousItem(const std::string& boundItemId); void erasePreviousItem(const std::string& boundItemId); + + void setSelectedSpell(const std::string& spellId); }; } #endif