diff --git a/apps/openmw/mwlua/animationbindings.cpp b/apps/openmw/mwlua/animationbindings.cpp index d45bb856ab..d959e6b478 100644 --- a/apps/openmw/mwlua/animationbindings.cpp +++ b/apps/openmw/mwlua/animationbindings.cpp @@ -70,26 +70,6 @@ namespace MWLua return anim; } - const ESM::Static* getStatic(const sol::object& staticOrID) - { - if (staticOrID.is()) - return staticOrID.as(); - else - { - ESM::RefId id = ESM::RefId::deserializeText(LuaUtil::cast(staticOrID)); - return MWBase::Environment::get().getWorld()->getStore().get().find(id); - } - } - - std::string getStaticModelOrThrow(const sol::object& staticOrID) - { - const ESM::Static* static_ = getStatic(staticOrID); - if (!static_) - throw std::runtime_error("Invalid static"); - - return Misc::ResourceHelpers::correctMeshPath(static_->mModel); - } - static AnimationPriorities getPriorityArgument(const sol::table& args) { auto asPriorityEnum = args.get>("priority"); @@ -276,27 +256,31 @@ namespace MWLua return anim->getNode(bonename) != nullptr; }; - api["addVfx"] = sol::overload( - [context](const sol::object& object, const sol::object& staticOrID) { - context.mLuaManager->addAction( - [object = ObjectVariant(object), model = getStaticModelOrThrow(staticOrID)] { - MWRender::Animation* anim = getMutableAnimationOrThrow(object); - anim->addEffect(model, ""); - }, - "addVfxAction"); - }, - [context](const sol::object& object, const sol::object& staticOrID, const sol::table& options) { + api["addVfx"] = [context]( + const sol::object& object, std::string_view model, sol::optional options) { + if (options) + { context.mLuaManager->addAction( - [object = ObjectVariant(object), model = getStaticModelOrThrow(staticOrID), - effectId = options.get_or("vfxId", ""), loop = options.get_or("loop", false), - boneName = options.get_or("boneName", ""), - particleTexture = options.get_or("particleTextureOverride", "")] { + [object = ObjectVariant(object), model = std::string(model), + effectId = options->get_or("vfxId", ""), loop = options->get_or("loop", false), + boneName = options->get_or("boneName", ""), + particleTexture = options->get_or("particleTextureOverride", "")] { MWRender::Animation* anim = getMutableAnimationOrThrow(ObjectVariant(object)); anim->addEffect(model, effectId, loop, boneName, particleTexture); }, "addVfxAction"); - }); + } + else + { + context.mLuaManager->addAction( + [object = ObjectVariant(object), model = std::string(model)] { + MWRender::Animation* anim = getMutableAnimationOrThrow(object); + anim->addEffect(model, ""); + }, + "addVfxAction"); + } + }; api["removeVfx"] = [context](const sol::object& object, std::string_view effectId) { context.mLuaManager->addAction( @@ -319,32 +303,31 @@ namespace MWLua return LuaUtil::makeReadOnly(api); } - sol::table initCoreVfxBindings(const Context& context) + sol::table initWorldVfxBindings(const Context& context) { sol::state_view& lua = context.mLua->sol(); sol::table api(lua, sol::create); auto world = MWBase::Environment::get().getWorld(); - api["spawn"] = sol::overload( - [world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos) { - auto model = getStaticModelOrThrow(staticOrID); - context.mLuaManager->addAction( - [world, model = std::move(model), worldPos]() { world->spawnEffect(model, "", worldPos); }, - "openmw.vfx.spawn"); - }, - [world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos, const sol::table& options) { - auto model = getStaticModelOrThrow(staticOrID); - - bool magicVfx = options.get_or("mwMagicVfx", true); - std::string texture = options.get_or("particleTextureOverride", ""); - float scale = options.get_or("scale", 1.f); - - context.mLuaManager->addAction( - [world, model = std::move(model), texture = std::move(texture), worldPos, scale, magicVfx]() { - world->spawnEffect(model, texture, worldPos, scale, magicVfx); - }, - "openmw.vfx.spawn"); - }); + api["spawn"] + = [world, context](std::string_view model, const osg::Vec3f& worldPos, sol::optional options) { + if (options) + { + bool magicVfx = options->get_or("mwMagicVfx", true); + std::string texture = options->get_or("particleTextureOverride", ""); + float scale = options->get_or("scale", 1.f); + context.mLuaManager->addAction( + [world, model = std::string(model), texture = std::move(texture), worldPos, scale, + magicVfx]() { world->spawnEffect(model, texture, worldPos, scale, magicVfx); }, + "openmw.vfx.spawn"); + } + else + { + context.mLuaManager->addAction( + [world, model = std::string(model), worldPos]() { world->spawnEffect(model, "", worldPos); }, + "openmw.vfx.spawn"); + } + }; return api; } diff --git a/apps/openmw/mwlua/animationbindings.hpp b/apps/openmw/mwlua/animationbindings.hpp index 251de42ee8..aa89649a35 100644 --- a/apps/openmw/mwlua/animationbindings.hpp +++ b/apps/openmw/mwlua/animationbindings.hpp @@ -8,7 +8,7 @@ namespace MWLua struct Context; sol::table initAnimationPackage(const Context& context); - sol::table initCoreVfxBindings(const Context& context); + sol::table initWorldVfxBindings(const Context& context); } #endif // MWLUA_ANIMATIONBINDINGS_H diff --git a/apps/openmw/mwlua/corebindings.cpp b/apps/openmw/mwlua/corebindings.cpp index f57948a211..7ac88e1e0b 100644 --- a/apps/openmw/mwlua/corebindings.cpp +++ b/apps/openmw/mwlua/corebindings.cpp @@ -19,7 +19,6 @@ #include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" -#include "animationbindings.hpp" #include "dialoguebindings.hpp" #include "factionbindings.hpp" #include "luaevents.hpp" @@ -137,7 +136,6 @@ namespace MWLua }; api["sound"] = context.cachePackage("openmw_core_sound", [context]() { return initCoreSoundBindings(context); }); - api["vfx"] = initCoreVfxBindings(context); } else { diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index 16a287c30a..018c33cfa4 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -25,6 +25,7 @@ #include "luamanagerimp.hpp" +#include "animationbindings.hpp" #include "corebindings.hpp" #include "mwscriptbindings.hpp" @@ -219,6 +220,8 @@ namespace MWLua "_runStandardUseAction"); }; + api["vfx"] = initWorldVfxBindings(context); + return LuaUtil::makeReadOnly(api); } } diff --git a/files/data/scripts/omw/mechanics/animationcontroller.lua b/files/data/scripts/omw/mechanics/animationcontroller.lua index 3293668387..c7a9182a8e 100644 --- a/files/data/scripts/omw/mechanics/animationcontroller.lua +++ b/files/data/scripts/omw/mechanics/animationcontroller.lua @@ -141,5 +141,11 @@ return { end handlers[#handlers + 1] = handler end, + }, + + eventHandlers = { + AddVfx = function(data) + anim.addVfx(self, data.model, data.options) + end, } -} \ No newline at end of file +} diff --git a/files/data/scripts/omw/worldeventhandlers.lua b/files/data/scripts/omw/worldeventhandlers.lua index 4bf6044178..4e7f96fcb7 100644 --- a/files/data/scripts/omw/worldeventhandlers.lua +++ b/files/data/scripts/omw/worldeventhandlers.lua @@ -6,5 +6,6 @@ return { Unpause = function(tag) world.unpause(tag) end, SetGameTimeScale = function(scale) world.setGameTimeScale(scale) end, SetSimulationTimeScale = function(scale) world.setSimulationTimeScale(scale) end, + SpawnVfx = function(data) world.vfx.spawn(data.model, data.position, data.options) end, }, } diff --git a/files/lua_api/openmw/animation.lua b/files/lua_api/openmw/animation.lua index e62440edac..ba57708a94 100644 --- a/files/lua_api/openmw/animation.lua +++ b/files/lua_api/openmw/animation.lua @@ -218,22 +218,34 @@ --- -- Plays a VFX on the actor. --- Can be used only in local scripts on self. +-- Can be used only in local scripts on self. Can also be evoked by sending an AddVfx event to the target actor. -- @function [parent=#animation] addVfx -- @param openmw.core#GameObject actor --- @param #any static @{openmw.core#StaticRecord} or #string ID +-- @param #string model #string model path (normally taken from a record such as @{openmw.types#StaticRecord.model} or similar) -- @param #table options optional table of parameters. Can contain: -- -- * `loop` - boolean, if true the effect will loop until removed (default: 0). -- * `boneName` - name of the bone to attach the vfx to. (default: "") --- * `particle` - name of the particle texture to use. (default: "") +-- * `particleTextureOverride` - name of the particle texture to use. (default: "") -- * `vfxId` - a string ID that can be used to remove the effect later, using #removeVfx, and to avoid duplicate effects. The default value of "" can have duplicates. To avoid interaction with the engine, use unique identifiers unrelated to magic effect IDs. The engine uses this identifier to add and remove magic effects based on what effects are active on the actor. If this is set equal to the @{openmw.core#MagicEffectId} identifier of the magic effect being added, for example core.magic.EFFECT_TYPE.FireDamage, then the engine will remove it once the fire damage effect on the actor reaches 0. (Default: ""). -- -- @usage local mgef = core.magic.effects.records[myEffectName] --- anim.addVfx(self, 'VFX_Hands', {boneName = 'Bip01 L Hand', particle = mgef.particle, loop = mgef.continuousVfx, vfxId = mgef.id..'_myuniquenamehere'}) +-- anim.addVfx(self, 'VFX_Hands', {boneName = 'Bip01 L Hand', particleTextureOverride = mgef.particle, loop = mgef.continuousVfx, vfxId = mgef.id..'_myuniquenamehere'}) -- -- later: -- anim.removeVfx(self, mgef.id..'_myuniquenamehere') -- +-- @usage -- Add vfx to another actor using an event +-- local mgef = core.magic.effects.records[myEffectName] +-- target:sendEvent('AddVfx', { +-- model = types.Static.record(mgef.hitStatic).model, +-- options = { +-- vfxId = mgef.id, +-- particuleTextureOverride = mgef.particle, +-- loop = false, +-- } +-- }) +-- + --- -- Removes a specific VFX diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 88f2a63371..7e6400ad38 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -1142,24 +1142,4 @@ -- @field #number favouredSkillValue Secondary skill value required to get this rank. -- @field #number factionReaction Reaction of faction members if player is in this faction. ---- @{#VFX}: Visual effects --- @field [parent=#core] #VFX vfx - ---- --- Spawn a VFX at the given location in the world --- @function [parent=#VFX] spawn --- @param #any static openmw.core#StaticRecord or #string ID --- @param openmw.util#Vector3 location --- @param #table options optional table of parameters. Can contain: --- --- * `mwMagicVfx` - Boolean that if true causes the textureOverride parameter to only affect nodes with the Nif::RC_NiTexturingProperty property set. (default: true). --- * `particleTextureOverride` - Name of a particle texture that should override this effect's default texture. (default: "") --- * `scale` - A number that scales the size of the vfx (Default: 1) --- --- @usage -- Spawn a sanctuary effect near the player --- local effect = core.magic.effects.records[core.magic.EFFECT_TYPE.Sanctuary] --- pos = self.position + util.vector3(0, 100, 0) --- core.vfx.spawn(effect.castingStatic, pos) --- - return nil diff --git a/files/lua_api/openmw/world.lua b/files/lua_api/openmw/world.lua index b497dfeacd..3ed85e9ab5 100644 --- a/files/lua_api/openmw/world.lua +++ b/files/lua_api/openmw/world.lua @@ -180,4 +180,25 @@ -- @param #any record A record to be registered in the database. Must be one of the supported types. -- @return #any A new record added to the database. The type is the same as the input's. +--- @{#VFX}: Visual effects +-- @field [parent=#world] #VFX vfx + +--- +-- Spawn a VFX at the given location in the world. Best invoked through the SpawnVfx global event +-- @function [parent=#VFX] spawn +-- @param #string model string model path (normally taken from a record such as @{openmw.types#StaticRecord.model} or similar) +-- @param openmw.util#Vector3 position +-- @param #table options optional table of parameters. Can contain: +-- +-- * `mwMagicVfx` - Boolean that if true causes the textureOverride parameter to only affect nodes with the Nif::RC_NiTexturingProperty property set. (default: true). +-- * `particleTextureOverride` - Name of a particle texture that should override this effect's default texture. (default: "") +-- * `scale` - A number that scales the size of the vfx (Default: 1) +-- +-- @usage -- Spawn a sanctuary effect near the player +-- local effect = core.magic.effects.records[core.magic.EFFECT_TYPE.Sanctuary] +-- local pos = self.position + util.vector3(0, 100, 0) +-- local model = types.Static.record(effect.castingStatic).model +-- core.sendGlobalEvent('SpawnVfx', {model = model, position = pos}) +-- + return nil