From 360abd9b908d89ce42a2d9cb4f81abf2fc8751f5 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Mon, 26 May 2025 19:34:03 +0200 Subject: [PATCH 1/2] Render openmw.animation inaccessible in menu scripts --- apps/openmw/mwlua/luabindings.cpp | 3 ++- docs/source/reference/lua-scripting/tables/packages.rst | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 8debbe153d..30bbab5be7 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -32,7 +32,6 @@ namespace MWLua sol::state_view lua = context.mLua->unsafeState(); MWWorld::DateTimeManager* tm = MWBase::Environment::get().getWorld()->getTimeManager(); return { - { "openmw.animation", initAnimationPackage(context) }, { "openmw.async", LuaUtil::getAsyncPackageInitializer( lua, [tm] { return tm->getSimulationTime(); }, [tm] { return tm->getGameTime(); }) }, @@ -47,6 +46,7 @@ namespace MWLua initObjectBindingsForGlobalScripts(context); initCellBindingsForGlobalScripts(context); return { + { "openmw.animation", initAnimationPackage(context) }, { "openmw.core", initCorePackage(context) }, { "openmw.types", initTypesPackage(context) }, { "openmw.world", initWorldPackage(context) }, @@ -59,6 +59,7 @@ namespace MWLua initCellBindingsForLocalScripts(context); LocalScripts::initializeSelfPackage(context); return { + { "openmw.animation", initAnimationPackage(context) }, { "openmw.core", initCorePackage(context) }, { "openmw.types", initTypesPackage(context) }, { "openmw.nearby", initNearbyPackage(context) }, diff --git a/docs/source/reference/lua-scripting/tables/packages.rst b/docs/source/reference/lua-scripting/tables/packages.rst index d0aa32abea..99edbb8378 100644 --- a/docs/source/reference/lua-scripting/tables/packages.rst +++ b/docs/source/reference/lua-scripting/tables/packages.rst @@ -4,7 +4,8 @@ |:ref:`openmw.ambient ` | by player and menu | | Controls background sounds for given player. | | | scripts | | +------------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.animation ` | everywhere | | Animation controls | +|:ref:`openmw.animation ` | by global, local, | | Animation controls | +| | and player scripts | | +------------------------------------------------------------+--------------------+---------------------------------------------------------------+ |:ref:`openmw.async ` | everywhere | | Timers and callbacks. | +------------------------------------------------------------+--------------------+---------------------------------------------------------------+ From c05d2d1d38369f75122623e9aa28336bf73dd4a3 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 31 May 2025 17:40:22 +0200 Subject: [PATCH 2/2] Restrict openmw.animation to local scripts --- apps/openmw/mwlua/animationbindings.cpp | 230 +++++++++--------- apps/openmw/mwlua/luabindings.cpp | 1 - .../lua-scripting/tables/packages.rst | 4 +- files/data/scripts/omw/console/global.lua | 1 - files/lua_api/openmw/animation.lua | 24 +- 5 files changed, 123 insertions(+), 137 deletions(-) diff --git a/apps/openmw/mwlua/animationbindings.cpp b/apps/openmw/mwlua/animationbindings.cpp index 2f6f60ea3e..776ec2cee0 100644 --- a/apps/openmw/mwlua/animationbindings.cpp +++ b/apps/openmw/mwlua/animationbindings.cpp @@ -23,79 +23,67 @@ namespace MWLua { - using BlendMask = MWRender::Animation::BlendMask; - using BoneGroup = MWRender::Animation::BoneGroup; - using Priority = MWMechanics::Priority; - using AnimationPriorities = MWRender::Animation::AnimPriority; - - MWWorld::Ptr getMutablePtrOrThrow(const ObjectVariant& variant) + namespace { - if (variant.isLObject()) - throw std::runtime_error("Local scripts can only modify animations of the object they are attached to."); + using BlendMask = MWRender::Animation::BlendMask; + using BoneGroup = MWRender::Animation::BoneGroup; + using Priority = MWMechanics::Priority; + using AnimationPriorities = MWRender::Animation::AnimPriority; - MWWorld::Ptr ptr = variant.ptr(); - if (ptr.isEmpty()) - throw std::runtime_error("Invalid object"); - if (!ptr.getRefData().isEnabled()) - throw std::runtime_error("Can't use a disabled object"); - - return ptr; - } - - MWWorld::Ptr getPtrOrThrow(const ObjectVariant& variant) - { - MWWorld::Ptr ptr = variant.ptr(); - if (ptr.isEmpty()) - throw std::runtime_error("Invalid object"); - - return ptr; - } - - MWRender::Animation* getMutableAnimationOrThrow(const ObjectVariant& variant) - { - MWWorld::Ptr ptr = getMutablePtrOrThrow(variant); - auto world = MWBase::Environment::get().getWorld(); - MWRender::Animation* anim = world->getAnimation(ptr); - if (!anim) - throw std::runtime_error("Object has no animation"); - return anim; - } - - const MWRender::Animation* getConstAnimationOrThrow(const ObjectVariant& variant) - { - MWWorld::Ptr ptr = getPtrOrThrow(variant); - auto world = MWBase::Environment::get().getWorld(); - const MWRender::Animation* anim = world->getAnimation(ptr); - if (!anim) - throw std::runtime_error("Object has no animation"); - return anim; - } - - static AnimationPriorities getPriorityArgument(const sol::table& args) - { - auto asPriorityEnum = args.get>("priority"); - if (asPriorityEnum) - return asPriorityEnum.value(); - - auto asTable = args.get>("priority"); - if (asTable) + const MWWorld::Ptr& getMutablePtrOrThrow(const Object& object) { - AnimationPriorities priorities = AnimationPriorities(Priority::Priority_Default); - for (const auto& entry : asTable.value()) - { - if (!entry.first.is() || !entry.second.is()) - throw std::runtime_error("Priority table must consist of BoneGroup-Priority pairs only"); - auto group = entry.first.as(); - auto priority = entry.second.as(); - if (group < 0 || group >= BoneGroup::Num_BoneGroups) - throw std::runtime_error("Invalid bonegroup: " + std::to_string(group)); - priorities[group] = priority; - } + const MWWorld::Ptr& ptr = object.ptr(); + if (!ptr.getRefData().isEnabled()) + throw std::runtime_error("Can't use a disabled object"); - return priorities; + return ptr; } - return Priority::Priority_Default; + MWRender::Animation* getMutableAnimationOrThrow(const Object& object) + { + const MWWorld::Ptr& ptr = getMutablePtrOrThrow(object); + auto world = MWBase::Environment::get().getWorld(); + MWRender::Animation* anim = world->getAnimation(ptr); + if (!anim) + throw std::runtime_error("Object has no animation"); + return anim; + } + + const MWRender::Animation* getConstAnimationOrThrow(const Object& object) + { + auto world = MWBase::Environment::get().getWorld(); + const MWRender::Animation* anim = world->getAnimation(object.ptr()); + if (!anim) + throw std::runtime_error("Object has no animation"); + return anim; + } + + AnimationPriorities getPriorityArgument(const sol::table& args) + { + auto asPriorityEnum = args.get>("priority"); + if (asPriorityEnum) + return asPriorityEnum.value(); + + auto asTable = args.get>("priority"); + if (asTable) + { + AnimationPriorities priorities = AnimationPriorities(Priority::Priority_Default); + for (const auto& entry : asTable.value()) + { + if (!entry.first.is() || !entry.second.is()) + throw std::runtime_error("Priority table must consist of BoneGroup-Priority pairs only"); + auto group = entry.first.as(); + auto priority = entry.second.as(); + if (group < 0 || group >= BoneGroup::Num_BoneGroups) + throw std::runtime_error("Invalid bonegroup: " + std::to_string(group)); + priorities[group] = priority; + } + + return priorities; + } + + return Priority::Priority_Default; + } } sol::table initAnimationPackage(const Context& context) @@ -104,7 +92,6 @@ namespace MWLua auto view = context.sol(); auto mechanics = MWBase::Environment::get().getMechanicsManager(); - auto world = MWBase::Environment::get().getWorld(); sol::table api(view, sol::create); @@ -145,95 +132,95 @@ namespace MWLua { "RightArm", BoneGroup::BoneGroup_RightArm }, })); - api["hasAnimation"] = [world](const sol::object& object) -> bool { - return world->getAnimation(getPtrOrThrow(ObjectVariant(object))) != nullptr; + api["hasAnimation"] = [](const LObject& object) -> bool { + return MWBase::Environment::get().getWorld()->getAnimation(object.ptr()) != nullptr; }; // equivalent to MWScript's SkipAnim - api["skipAnimationThisFrame"] = [mechanics](const sol::object& object) { - MWWorld::Ptr ptr = getMutablePtrOrThrow(ObjectVariant(object)); + api["skipAnimationThisFrame"] = [mechanics](const SelfObject& object) { + const MWWorld::Ptr& ptr = getMutablePtrOrThrow(object); // This sets a flag that is only used during the update pass, so // there's no need to queue mechanics->skipAnimation(ptr); }; - api["getTextKeyTime"] = [](const sol::object& object, std::string_view key) -> sol::optional { - float time = getConstAnimationOrThrow(ObjectVariant(object))->getTextKeyTime(key); + api["getTextKeyTime"] = [](const LObject& object, std::string_view key) -> sol::optional { + float time = getConstAnimationOrThrow(object)->getTextKeyTime(key); if (time >= 0.f) return time; return sol::nullopt; }; - api["isPlaying"] = [](const sol::object& object, std::string_view groupname) { - return getConstAnimationOrThrow(ObjectVariant(object))->isPlaying(groupname); + api["isPlaying"] = [](const LObject& object, std::string_view groupname) { + return getConstAnimationOrThrow(object)->isPlaying(groupname); }; - api["getCurrentTime"] = [](const sol::object& object, std::string_view groupname) -> sol::optional { - float time = getConstAnimationOrThrow(ObjectVariant(object))->getCurrentTime(groupname); + api["getCurrentTime"] = [](const LObject& object, std::string_view groupname) -> sol::optional { + float time = getConstAnimationOrThrow(object)->getCurrentTime(groupname); if (time >= 0.f) return time; return sol::nullopt; }; - api["isLoopingAnimation"] = [](const sol::object& object, std::string_view groupname) { - return getConstAnimationOrThrow(ObjectVariant(object))->isLoopingAnimation(groupname); + api["isLoopingAnimation"] = [](const LObject& object, std::string_view groupname) { + return getConstAnimationOrThrow(object)->isLoopingAnimation(groupname); }; - api["cancel"] = [](const sol::object& object, std::string_view groupname) { - return getMutableAnimationOrThrow(ObjectVariant(object))->disable(groupname); + api["cancel"] = [](const SelfObject& object, std::string_view groupname) { + return getMutableAnimationOrThrow(object)->disable(groupname); }; - api["setLoopingEnabled"] = [](const sol::object& object, std::string_view groupname, bool enabled) { - return getMutableAnimationOrThrow(ObjectVariant(object))->setLoopingEnabled(groupname, enabled); + api["setLoopingEnabled"] = [](const SelfObject& object, std::string_view groupname, bool enabled) { + return getMutableAnimationOrThrow(object)->setLoopingEnabled(groupname, enabled); }; // MWRender::Animation::getInfo can also return the current speed multiplier, but this is never used. - api["getCompletion"] = [](const sol::object& object, std::string_view groupname) -> sol::optional { + api["getCompletion"] = [](const LObject& object, std::string_view groupname) -> sol::optional { float completion = 0.f; - if (getConstAnimationOrThrow(ObjectVariant(object))->getInfo(groupname, &completion)) + if (getConstAnimationOrThrow(object)->getInfo(groupname, &completion)) return completion; return sol::nullopt; }; - api["getLoopCount"] = [](const sol::object& object, std::string groupname) -> sol::optional { + api["getLoopCount"] = [](const LObject& object, std::string groupname) -> sol::optional { size_t loops = 0; - if (getConstAnimationOrThrow(ObjectVariant(object))->getInfo(groupname, nullptr, nullptr, &loops)) + if (getConstAnimationOrThrow(object)->getInfo(groupname, nullptr, nullptr, &loops)) return loops; return sol::nullopt; }; - api["getSpeed"] = [](const sol::object& object, std::string groupname) -> sol::optional { + api["getSpeed"] = [](const LObject& object, std::string groupname) -> sol::optional { float speed = 0.f; - if (getConstAnimationOrThrow(ObjectVariant(object))->getInfo(groupname, nullptr, &speed, nullptr)) + if (getConstAnimationOrThrow(object)->getInfo(groupname, nullptr, &speed, nullptr)) return speed; return sol::nullopt; }; - api["setSpeed"] = [](const sol::object& object, std::string groupname, const FiniteFloat speed) { - getMutableAnimationOrThrow(ObjectVariant(object))->adjustSpeedMult(groupname, speed); + api["setSpeed"] = [](const SelfObject& object, std::string groupname, const FiniteFloat speed) { + getMutableAnimationOrThrow(object)->adjustSpeedMult(groupname, speed); }; - api["getActiveGroup"] = [](const sol::object& object, MWRender::BoneGroup boneGroup) -> std::string_view { + api["getActiveGroup"] = [](const LObject& object, MWRender::BoneGroup boneGroup) -> std::string_view { if (boneGroup < 0 || boneGroup >= BoneGroup::Num_BoneGroups) throw std::runtime_error("Invalid bonegroup: " + std::to_string(boneGroup)); - return getConstAnimationOrThrow(ObjectVariant(object))->getActiveGroup(boneGroup); + return getConstAnimationOrThrow(object)->getActiveGroup(boneGroup); }; // Clears out the animation queue, and cancel any animation currently playing from the queue - api["clearAnimationQueue"] = [mechanics](const sol::object& object, bool clearScripted) { - MWWorld::Ptr ptr = getMutablePtrOrThrow(ObjectVariant(object)); + api["clearAnimationQueue"] = [mechanics](const SelfObject& object, bool clearScripted) { + const MWWorld::Ptr& ptr = getMutablePtrOrThrow(object); mechanics->clearAnimationQueue(ptr, clearScripted); }; // Extended variant of MWScript's PlayGroup and LoopGroup api["playQueued"] = sol::overload( - [mechanics](const sol::object& object, const std::string& groupname, const sol::table& options) { + [mechanics](const SelfObject& object, const std::string& groupname, const sol::table& options) { uint32_t numberOfLoops = options.get_or("loops", std::numeric_limits::max()); float speed = options.get_or("speed", 1.f); std::string startKey = options.get_or("startKey", "start"); std::string stopKey = options.get_or("stopKey", "stop"); bool forceLoop = options.get_or("forceLoop", false); - MWWorld::Ptr ptr = getMutablePtrOrThrow(ObjectVariant(object)); + const MWWorld::Ptr& ptr = getMutablePtrOrThrow(object); mechanics->playAnimationGroupLua(ptr, groupname, numberOfLoops, speed, startKey, stopKey, forceLoop); }, - [mechanics](const sol::object& object, const std::string& groupname) { - MWWorld::Ptr ptr = getMutablePtrOrThrow(ObjectVariant(object)); + [mechanics](const SelfObject& object, const std::string& groupname) { + const MWWorld::Ptr& ptr = getMutablePtrOrThrow(object); mechanics->playAnimationGroupLua( ptr, groupname, std::numeric_limits::max(), 1, "start", "stop", false); }); - api["playBlended"] = [](const sol::object& object, std::string_view groupName, const sol::table& options) { + api["playBlended"] = [](const SelfObject& object, std::string_view groupName, const sol::table& options) { uint32_t loops = options.get_or("loops", 0u); MWRender::Animation::AnimPriority priority = getPriorityArgument(options); BlendMask blendMask = options.get_or("blendMask", BlendMask::BlendMask_All); @@ -246,33 +233,32 @@ namespace MWLua const std::string lowerGroup = Misc::StringUtils::lowerCase(groupName); - auto animation = getMutableAnimationOrThrow(ObjectVariant(object)); + auto animation = getMutableAnimationOrThrow(object); animation->play(lowerGroup, priority, blendMask, autoDisable, speed, start, stop, startPoint, loops, forceLoop || animation->isLoopingAnimation(lowerGroup)); }; - api["hasGroup"] = [](const sol::object& object, std::string_view groupname) -> bool { - const MWRender::Animation* anim = getConstAnimationOrThrow(ObjectVariant(object)); + api["hasGroup"] = [](const LObject& object, std::string_view groupname) -> bool { + const MWRender::Animation* anim = getConstAnimationOrThrow(object); return anim->hasAnimation(groupname); }; // Note: This checks the nodemap, and does not read the scene graph itself, and so should be thread safe. - api["hasBone"] = [](const sol::object& object, std::string_view bonename) -> bool { - const MWRender::Animation* anim = getConstAnimationOrThrow(ObjectVariant(object)); + api["hasBone"] = [](const LObject& object, std::string_view bonename) -> bool { + const MWRender::Animation* anim = getConstAnimationOrThrow(object); return anim->getNode(bonename) != nullptr; }; - api["addVfx"] = [context]( - const sol::object& object, std::string_view model, sol::optional options) { + api["addVfx"] = [context](const SelfObject& object, std::string_view model, sol::optional options) { if (options) { context.mLuaManager->addAction( - [object = ObjectVariant(object), model = std::string(model), + [object = Object(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", ""), useAmbientLight = options->get_or("useAmbientLight", true)] { - MWRender::Animation* anim = getMutableAnimationOrThrow(ObjectVariant(object)); + MWRender::Animation* anim = getMutableAnimationOrThrow(object); anim->addEffect(model, effectId, loop, boneName, particleTexture, useAmbientLight); }, @@ -281,7 +267,7 @@ namespace MWLua else { context.mLuaManager->addAction( - [object = ObjectVariant(object), model = std::string(model)] { + [object = Object(object), model = std::string(model)] { MWRender::Animation* anim = getMutableAnimationOrThrow(object); anim->addEffect(model, ""); }, @@ -289,18 +275,18 @@ namespace MWLua } }; - api["removeVfx"] = [context](const sol::object& object, std::string_view effectId) { + api["removeVfx"] = [context](const SelfObject& object, std::string_view effectId) { context.mLuaManager->addAction( - [object = ObjectVariant(object), effectId = std::string(effectId)] { + [object = Object(object), effectId = std::string(effectId)] { MWRender::Animation* anim = getMutableAnimationOrThrow(object); anim->removeEffect(effectId); }, "removeVfxAction"); }; - api["removeAllVfx"] = [context](const sol::object& object) { + api["removeAllVfx"] = [context](const SelfObject& object) { context.mLuaManager->addAction( - [object = ObjectVariant(object)] { + [object = Object(object)] { MWRender::Animation* anim = getMutableAnimationOrThrow(object); anim->removeEffects(); }, @@ -313,10 +299,9 @@ namespace MWLua sol::table initWorldVfxBindings(const Context& context) { sol::table api(context.mLua->unsafeState(), sol::create); - auto world = MWBase::Environment::get().getWorld(); api["spawn"] - = [world, context](std::string_view model, const osg::Vec3f& worldPos, sol::optional options) { + = [context](std::string_view model, const osg::Vec3f& worldPos, sol::optional options) { if (options) { bool magicVfx = options->get_or("mwMagicVfx", true); @@ -324,16 +309,19 @@ namespace MWLua float scale = options->get_or("scale", 1.f); bool useAmbientLight = options->get_or("useAmbientLight", true); context.mLuaManager->addAction( - [world, model = VFS::Path::Normalized(model), texture = std::move(texture), worldPos, scale, + [model = VFS::Path::Normalized(model), texture = std::move(texture), worldPos, scale, magicVfx, useAmbientLight]() { - world->spawnEffect(model, texture, worldPos, scale, magicVfx, useAmbientLight); + MWBase::Environment::get().getWorld()->spawnEffect( + model, texture, worldPos, scale, magicVfx, useAmbientLight); }, "openmw.vfx.spawn"); } else { - context.mLuaManager->addAction([world, model = VFS::Path::Normalized(model), - worldPos]() { world->spawnEffect(model, "", worldPos, 1.f); }, + context.mLuaManager->addAction( + [model = VFS::Path::Normalized(model), worldPos]() { + MWBase::Environment::get().getWorld()->spawnEffect(model, "", worldPos, 1.f); + }, "openmw.vfx.spawn"); } }; diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 30bbab5be7..0e9c56a482 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -46,7 +46,6 @@ namespace MWLua initObjectBindingsForGlobalScripts(context); initCellBindingsForGlobalScripts(context); return { - { "openmw.animation", initAnimationPackage(context) }, { "openmw.core", initCorePackage(context) }, { "openmw.types", initTypesPackage(context) }, { "openmw.world", initWorldPackage(context) }, diff --git a/docs/source/reference/lua-scripting/tables/packages.rst b/docs/source/reference/lua-scripting/tables/packages.rst index 99edbb8378..e66926e5e4 100644 --- a/docs/source/reference/lua-scripting/tables/packages.rst +++ b/docs/source/reference/lua-scripting/tables/packages.rst @@ -4,8 +4,8 @@ |:ref:`openmw.ambient ` | by player and menu | | Controls background sounds for given player. | | | scripts | | +------------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.animation ` | by global, local, | | Animation controls | -| | and player scripts | | +|:ref:`openmw.animation ` | by local and | | Animation controls | +| | player scripts | | +------------------------------------------------------------+--------------------+---------------------------------------------------------------+ |:ref:`openmw.async ` | everywhere | | Timers and callbacks. | +------------------------------------------------------------+--------------------+---------------------------------------------------------------+ diff --git a/files/data/scripts/omw/console/global.lua b/files/data/scripts/omw/console/global.lua index 0ec557b376..8b3e18eb99 100644 --- a/files/data/scripts/omw/console/global.lua +++ b/files/data/scripts/omw/console/global.lua @@ -29,7 +29,6 @@ local env = { aux_util = require('openmw_aux.util'), calendar = require('openmw_aux.calendar'), time = require('openmw_aux.time'), - anim = require('openmw.animation'), view = require('openmw_aux.util').deepToString, print = printToConsole, exit = function() player:sendEvent('OMWConsoleExit') end, diff --git a/files/lua_api/openmw/animation.lua b/files/lua_api/openmw/animation.lua index d15241afff..cde046b443 100644 --- a/files/lua_api/openmw/animation.lua +++ b/files/lua_api/openmw/animation.lua @@ -56,7 +56,7 @@ --- -- Skips animations for one frame, equivalent to mwscript's SkipAnim. --- Can be used only in local scripts on self. +-- Can only be used on self. -- @function [parent=#animation] skipAnimationThisFrame -- @param openmw.core#GameObject actor @@ -99,14 +99,14 @@ --- -- Cancels and removes the animation group from the list of active animations. --- Can be used only in local scripts on self. +-- Can only be used on self. -- @function [parent=#animation] cancel -- @param openmw.core#GameObject actor -- @param #string groupName --- -- Enables or disables looping for the given animation group. Looping is enabled by default. --- Can be used only in local scripts on self. +-- Can only be used on self. -- @function [parent=#animation] setLoopingEnabled -- @param openmw.core#GameObject actor -- @param #string groupName @@ -136,7 +136,7 @@ --- -- Modifies the playback speed of an animation group. -- Note that this is not sticky and only affects the speed until the currently playing sequence ends. --- Can be used only in local scripts on self. +-- Can only be used on self. -- @function [parent=#animation] setSpeed -- @param openmw.core#GameObject actor -- @param #string groupName @@ -144,7 +144,7 @@ --- -- Clears all animations currently in the animation queue. This affects animations played by mwscript, @{openmw.animation#playQueued}, and ai packages, but does not affect animations played using @{openmw.animation#playBlended}. --- Can be used only in local scripts on self. +-- Can only be used on self. -- @function [parent=#animation] clearAnimationQueue -- @param openmw.core#GameObject actor -- @param #boolean clearScripted whether to keep animation with priority Scripted or not. @@ -153,7 +153,7 @@ -- Acts as a slightly extended version of MWScript's LoopGroup. Plays this animation exclusively -- until it ends, or the queue is cleared using #clearAnimationQueue. Use #clearAnimationQueue and the `startkey` option -- to imitate the behavior of LoopGroup's play modes. --- Can be used only in local scripts on self. +-- Can only be used on self. -- @function [parent=#animation] playQueued -- @param openmw.core#GameObject actor -- @param #string groupName @@ -179,7 +179,7 @@ -- instead of calling this directly. Note that the still hardcoded character controller may at any time and for any reason alter -- or cancel currently playing animations, so making your own calls to this function either directly or through the [AnimationController](interface_animation.html) -- interface may be of limited utility. For now, use openmw.animation#playQueued to script your own animations. --- Can be used only in local scripts on self. +-- Can only be used on self. -- @function [parent=#animation] playBlended -- @param openmw.core#GameObject actor -- @param #string groupName @@ -218,7 +218,7 @@ --- -- Plays a VFX on the actor. --- Can be used only in local scripts on self. Can also be evoked by sending an AddVfx event to the target actor. +-- Can only be used 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 #string model path (normally taken from a record such as @{openmw.types#StaticRecord.model} or similar) @@ -249,15 +249,15 @@ --- --- Removes a specific VFX --- Can be used only in local scripts on self. +-- Removes a specific VFX. +-- Can only be used on self. -- @function [parent=#animation] removeVfx -- @param openmw.core#GameObject actor -- @param #number vfxId an integer ID that uniquely identifies the VFX to remove --- --- Removes all vfx from the actor --- Can be used only in local scripts on self. +-- Removes all vfx from the actor. +-- Can only be used on self. -- @function [parent=#animation] removeAllVfx -- @param openmw.core#GameObject actor