diff --git a/apps/openmw/mwlua/magicbindings.cpp b/apps/openmw/mwlua/magicbindings.cpp index c4c95e5efe..28755a69de 100644 --- a/apps/openmw/mwlua/magicbindings.cpp +++ b/apps/openmw/mwlua/magicbindings.cpp @@ -9,6 +9,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwmechanics/activespells.hpp" @@ -46,6 +47,8 @@ namespace MWLua bool isActor() const { return !mActor.ptr().isEmpty() && mActor.ptr().getClass().isActor(); } + bool isLObject() const { return mActor.isLObject(); } + void reset() { mIndex = 0; @@ -521,6 +524,18 @@ namespace MWLua return false; }; + // types.Actor.activeSpells(o):remove(id) + activeSpellsT["remove"] = [](const ActorActiveSpells& spells, const sol::object& spellOrId) { + if (spells.isLObject()) + throw std::runtime_error("Local scripts can modify effect only on the actor they are attached to."); + + auto id = toSpellId(spellOrId); + if (auto* store = spells.getStore()) + { + store->removeEffects(spells.mActor.ptr(), id); + } + }; + // pairs(types.Actor.activeEffects(o)) // Note that the indexes are fake, and only for consistency with other lua pairs interfaces. You can't use them // for anything. @@ -544,12 +559,8 @@ namespace MWLua }); }; - // types.Actor.activeEffects(o):getEffect(id, ?arg) - activeEffectsT["getEffect"] = [](const ActorActiveEffects& effects, std::string_view idStr, - sol::optional argStr) -> sol::optional { - if (!effects.isActor()) - return sol::nullopt; - + auto getEffectKey + = [](std::string_view idStr, sol::optional argStr) -> MWMechanics::EffectKey { auto id = ESM::MagicEffect::indexNameToIndex(idStr); auto* rec = MWBase::Environment::get().getWorld()->getStore().get().find(id); @@ -566,10 +577,73 @@ namespace MWLua key = MWMechanics::EffectKey(id, ESM::Skill::stringToSkillId(argStr.value())); } + return key; + }; + + // types.Actor.activeEffects(o):getEffect(id, ?arg) + activeEffectsT["getEffect"] = [getEffectKey](const ActorActiveEffects& effects, std::string_view idStr, + sol::optional argStr) -> sol::optional { + if (!effects.isActor()) + return sol::nullopt; + + MWMechanics::EffectKey key = getEffectKey(idStr, argStr); + if (auto* store = effects.getStore()) if (auto effect = store->get(key)) return ActiveEffect{ key, effect.value() }; return sol::nullopt; }; + + // types.Actor.activeEffects(o):removeEffect(id, ?arg) + activeEffectsT["remove"] = [getEffectKey](const ActorActiveEffects& effects, std::string_view idStr, + sol::optional argStr) { + if (!effects.isActor()) + return; + + if (effects.isLObject()) + throw std::runtime_error("Local scripts can modify effect only on the actor they are attached to."); + + MWMechanics::EffectKey key = getEffectKey(idStr, argStr); + + // Note that, although this is member method of ActorActiveEffects and we are removing an effect (not a + // spell), we still need to use the active spells store to purge this effect from active spells. + auto ptr = effects.mActor.ptr(); + + // TODO: The current ActiveSpell API does not allow us to differentiate between skill/attribute parameters + // of effects. So this cannot remove e.g. "Fortify Luck" without also removing all other fortify attribute + // effects such as "Fortify Speed". + auto& activeSpells = ptr.getClass().getCreatureStats(ptr).getActiveSpells(); + activeSpells.purgeEffect(ptr, key.mId, key.mArg); + + // Now remove any leftover effects that have been added by script/console. + effects.getStore()->remove(key); + }; + + // types.Actor.activeEffects(o):set(value, id, ?arg) + activeEffectsT["set"] = [getEffectKey](const ActorActiveEffects& effects, int value, std::string_view idStr, + sol::optional argStr) { + if (!effects.isActor()) + return; + + if (effects.isLObject()) + throw std::runtime_error("Local scripts can modify effect only on the actor they are attached to."); + + MWMechanics::EffectKey key = getEffectKey(idStr, argStr); + int currentValue = effects.getStore()->getOrDefault(key).getMagnitude(); + effects.getStore()->modifyBase(key, value - currentValue); + }; + + // types.Actor.activeEffects(o):modify(value, id, ?arg) + activeEffectsT["modify"] = [getEffectKey](const ActorActiveEffects& effects, int value, std::string_view idStr, + sol::optional argStr) { + if (!effects.isActor()) + return; + + if (effects.isLObject()) + throw std::runtime_error("Local scripts can modify effect only on the actor they are attached to."); + + MWMechanics::EffectKey key = getEffectKey(idStr, argStr); + effects.getStore()->modifyBase(key, value); + }; } } diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 65683ea2b5..b759cdb8b9 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -521,9 +521,15 @@ namespace MWMechanics purge([=](const ActiveSpellParams& params) { return params.mId == id; }, ptr); } - void ActiveSpells::purgeEffect(const MWWorld::Ptr& ptr, short effectId) + void ActiveSpells::purgeEffect(const MWWorld::Ptr& ptr, int effectId, int effectArg) { - purge([=](const ActiveSpellParams&, const ESM::ActiveEffect& effect) { return effect.mEffectId == effectId; }, + purge( + [=](const ActiveSpellParams&, const ESM::ActiveEffect& effect) { + if (effectArg < 0) + return effect.mEffectId == effectId; + else + return effect.mEffectId == effectId && effect.mArg == effectArg; + }, ptr); } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 0531c35f88..6b71d93d64 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -131,7 +131,7 @@ namespace MWMechanics void removeEffects(const MWWorld::Ptr& ptr, const ESM::RefId& id); /// Remove all active effects with this effect id - void purgeEffect(const MWWorld::Ptr& ptr, short effectId); + void purgeEffect(const MWWorld::Ptr& ptr, int effectId, int effectArg = -1); void purge(EffectPredicate predicate, const MWWorld::Ptr& ptr); void purge(ParamsPredicate predicate, const MWWorld::Ptr& ptr); diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 268175316b..fe195f123c 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -192,10 +192,35 @@ -- Get a specific active effect on the actor. -- @function [parent=#ActorActiveEffects] getEffect -- @param self --- @param string effect ID --- @param string Optional skill or attribute ID +-- @param #string effectId effect ID +-- @param #string extraParam Optional skill or attribute ID -- @return #ActiveEffect if such an effect is active, nil otherwise +--- +-- Completely removes the active effect from the actor. +-- This removes both the effects incurred by active spells and effect added by console, mwscript, or luascript. +-- @function [parent=#ActorActiveEffects] remove +-- @param self +-- @param #string effectId effect ID +-- @param #string extraParam Optional skill or attribute ID + +--- +-- Permanently modifies the magnitude of an active effect to be exactly equal to the provided value. This adds the effect to the list of active effects if not already active. +-- Note that although the modification is permanent, the magnitude will not stay equal to the value if any active spells with this effects are added/removed. +-- @function [parent=#ActorActiveEffects] set +-- @param self +-- @param #number value +-- @param #string effectId effect ID +-- @param #string extraParam Optional skill or attribute ID + +--- +-- Permanently modifies the magnitude of an active effect by increasing it by the provided value. This adds the effect to the list of active effects if not already active. +-- @function [parent=#ActorActiveEffects] modify +-- @param self +-- @param #number value +-- @param #string effectId effect ID +-- @param #string extraParam Optional skill or attribute ID + --- -- Return the active spells (@{#ActorActiveSpells}) currently affecting the given actor. -- @function [parent=#Actor] activeSpells @@ -222,6 +247,12 @@ -- @param #any spellOrId @{openmw.core#Spell} or string spell id -- @return true if spell is active, false otherwise +--- +-- Remove the given spell and all its effects from the given actor's active spells. +-- @function [parent=#ActorActiveSpells] remove +-- @param self +-- @param #any spellOrId @{openmw.core#Spell} or string spell id + --- -- Return the spells (@{#ActorSpells}) of the given actor. -- @function [parent=#Actor] spells