mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-29 22:45:36 +00:00
Allow Lua scripts to extend or override standard activation mechanics
This commit is contained in:
parent
a82b7cb872
commit
61d207bd78
13 changed files with 126 additions and 50 deletions
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
#include "../mwworld/action.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
|
@ -106,6 +107,16 @@ namespace MWLua
|
|||
// TODO: add here overloads for other records
|
||||
);
|
||||
|
||||
api["_runStandardActivationAction"] = [context](const GObject& object, const GObject& actor) {
|
||||
context.mLuaManager->addAction(
|
||||
[object, actor] {
|
||||
const MWWorld::Ptr& objPtr = object.ptr();
|
||||
const MWWorld::Ptr& actorPtr = actor.ptr();
|
||||
objPtr.getClass().activate(objPtr, actorPtr)->execute(actorPtr);
|
||||
},
|
||||
"_runStandardActivationAction");
|
||||
};
|
||||
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <components/lua/luastate.hpp>
|
||||
|
||||
#include "../mwworld/action.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
|
@ -102,44 +101,6 @@ namespace MWLua
|
|||
osg::Vec3f mRot;
|
||||
};
|
||||
|
||||
class ActivateAction final : public LuaManager::Action
|
||||
{
|
||||
public:
|
||||
ActivateAction(LuaUtil::LuaState* state, ObjectId object, ObjectId actor)
|
||||
: Action(state)
|
||||
, mObject(object)
|
||||
, mActor(actor)
|
||||
{
|
||||
}
|
||||
|
||||
void apply() const override
|
||||
{
|
||||
MWWorld::Ptr object = MWBase::Environment::get().getWorldModel()->getPtr(mObject);
|
||||
if (object.isEmpty())
|
||||
throw std::runtime_error(std::string("Object not found: " + mObject.toString()));
|
||||
MWWorld::Ptr actor = MWBase::Environment::get().getWorldModel()->getPtr(mActor);
|
||||
if (actor.isEmpty())
|
||||
throw std::runtime_error(std::string("Actor not found: " + mActor.toString()));
|
||||
|
||||
if (object.getRefData().activate())
|
||||
{
|
||||
MWBase::Environment::get().getLuaManager()->objectActivated(object, actor);
|
||||
std::unique_ptr<MWWorld::Action> action = object.getClass().activate(object, actor);
|
||||
action->execute(actor);
|
||||
}
|
||||
}
|
||||
|
||||
std::string toString() const override
|
||||
{
|
||||
return std::string("ActivateAction object=") + mObject.toString() + std::string(" actor=")
|
||||
+ mActor.toString();
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectId mObject;
|
||||
ObjectId mActor;
|
||||
};
|
||||
|
||||
template <typename ObjT>
|
||||
using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>;
|
||||
|
||||
|
@ -165,6 +126,7 @@ namespace MWLua
|
|||
template <class ObjectT>
|
||||
void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
||||
{
|
||||
objectT["id"] = sol::readonly_property([](const ObjectT& o) -> std::string { return o.id().toString(); });
|
||||
objectT["isValid"] = [](const ObjectT& o) { return !o.ptrOrNull().isEmpty(); };
|
||||
objectT["recordId"] = sol::readonly_property(
|
||||
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().serializeText(); });
|
||||
|
@ -192,13 +154,16 @@ namespace MWLua
|
|||
{ dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
|
||||
};
|
||||
|
||||
objectT["activateBy"] = [context](const ObjectT& o, const ObjectT& actor) {
|
||||
uint32_t esmRecordType = actor.ptr().getType();
|
||||
objectT["activateBy"] = [](const ObjectT& object, const ObjectT& actor) {
|
||||
const MWWorld::Ptr& objPtr = object.ptr();
|
||||
const MWWorld::Ptr& actorPtr = actor.ptr();
|
||||
uint32_t esmRecordType = actorPtr.getType();
|
||||
if (esmRecordType != ESM::REC_CREA && esmRecordType != ESM::REC_NPC_)
|
||||
throw std::runtime_error(
|
||||
"The argument of `activateBy` must be an actor who activates the object. Got: "
|
||||
+ actor.toString());
|
||||
context.mLuaManager->addAction(std::make_unique<ActivateAction>(context.mLua, o.id(), actor.id()));
|
||||
if (objPtr.getRefData().activate())
|
||||
MWBase::Environment::get().getLuaManager()->objectActivated(objPtr, actorPtr);
|
||||
};
|
||||
|
||||
auto isEnabled = [](const ObjectT& o) { return o.ptr().getRefData().isEnabled(); };
|
||||
|
|
|
@ -419,13 +419,23 @@ namespace MWScript
|
|||
|
||||
void InterpreterContext::executeActivation(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor)
|
||||
{
|
||||
// MWScripted activations don't go through Lua because 1-frame delay can brake mwscripts.
|
||||
#if 0
|
||||
MWBase::Environment::get().getLuaManager()->objectActivated(ptr, actor);
|
||||
|
||||
// TODO: Enable this branch after implementing one of the options:
|
||||
// 1) Pause this mwscript (or maybe all mwscripts) for one frame and continue from the same
|
||||
// command when the activation is processed by Lua script.
|
||||
// 2) Force Lua scripts to handle a zero-length extra frame right now, so when control
|
||||
// returns to the mwscript, the activation is already processed.
|
||||
#else
|
||||
std::unique_ptr<MWWorld::Action> action = (ptr.getClass().activate(ptr, actor));
|
||||
action->execute(actor);
|
||||
if (action->getTarget() != MWWorld::Ptr() && action->getTarget() != ptr)
|
||||
{
|
||||
updatePtr(ptr, action->getTarget());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int InterpreterContext::getMemberShort(const ESM::RefId& id, std::string_view name, bool global) const
|
||||
|
|
|
@ -3779,13 +3779,8 @@ namespace MWWorld
|
|||
void World::activate(const Ptr& object, const Ptr& actor)
|
||||
{
|
||||
breakInvisibility(actor);
|
||||
|
||||
if (object.getRefData().activate())
|
||||
{
|
||||
MWBase::Environment::get().getLuaManager()->objectActivated(object, actor);
|
||||
std::unique_ptr<MWWorld::Action> action = object.getClass().activate(object, actor);
|
||||
action->execute(actor);
|
||||
}
|
||||
}
|
||||
|
||||
struct ResetActorsVisitor
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
paths=(
|
||||
openmw_aux/*lua
|
||||
scripts/omw/activationhandlers.lua
|
||||
scripts/omw/ai.lua
|
||||
scripts/omw/playercontrols.lua
|
||||
scripts/omw/camera/camera.lua
|
||||
|
|
|
@ -27,6 +27,7 @@ Lua API reference
|
|||
openmw_aux_util
|
||||
openmw_aux_time
|
||||
openmw_aux_ui
|
||||
interface_activation
|
||||
interface_ai
|
||||
interface_camera
|
||||
interface_controls
|
||||
|
@ -68,6 +69,9 @@ Interfaces of built-in scripts
|
|||
* - Interface
|
||||
- Can be used
|
||||
- Description
|
||||
* - :ref:`Activation <Interface Activation>`
|
||||
- by global scripts
|
||||
- Allows to extend or override built-in activation mechanics.
|
||||
* - :ref:`AI <Interface AI>`
|
||||
- by local scripts
|
||||
- Control basic AI of NPCs and creatures.
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Interface Activation
|
||||
====================
|
||||
|
||||
.. raw:: html
|
||||
:file: generated_html/scripts_omw_activationhandlers.html
|
||||
|
|
@ -461,6 +461,9 @@ The order in which the scripts are started is important. So if one mod should ov
|
|||
* - Interface
|
||||
- Can be used
|
||||
- Description
|
||||
* - :ref:`Activation <Interface Activation>`
|
||||
- by global scripts
|
||||
- Allows to extend or override built-in activation mechanics.
|
||||
* - :ref:`AI <Interface AI>`
|
||||
- by local scripts
|
||||
- Control basic AI of NPCs and creatures.
|
||||
|
|
|
@ -63,6 +63,7 @@ set(BUILTIN_DATA_FILES
|
|||
|
||||
builtin.omwscripts
|
||||
|
||||
scripts/omw/activationhandlers.lua
|
||||
scripts/omw/ai.lua
|
||||
scripts/omw/camera/camera.lua
|
||||
scripts/omw/camera/head_bobbing.lua
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
# UI framework
|
||||
PLAYER: scripts/omw/mwui/init.lua
|
||||
|
||||
# Settings framework
|
||||
GLOBAL: scripts/omw/settings/global.lua
|
||||
PLAYER: scripts/omw/settings/player.lua
|
||||
|
||||
# Mechanics
|
||||
GLOBAL: scripts/omw/activationhandlers.lua
|
||||
PLAYER: scripts/omw/playercontrols.lua
|
||||
PLAYER: scripts/omw/camera/camera.lua
|
||||
NPC,CREATURE: scripts/omw/ai.lua
|
||||
|
||||
# Lua console
|
||||
PLAYER: scripts/omw/console/player.lua
|
||||
GLOBAL: scripts/omw/console/global.lua
|
||||
CUSTOM: scripts/omw/console/local.lua
|
||||
|
|
68
files/data/scripts/omw/activationhandlers.lua
Normal file
68
files/data/scripts/omw/activationhandlers.lua
Normal file
|
@ -0,0 +1,68 @@
|
|||
local types = require('openmw.types')
|
||||
local world = require('openmw.world')
|
||||
|
||||
local handlersPerObject = {}
|
||||
local handlersPerType = {}
|
||||
|
||||
local function onActivate(obj, actor)
|
||||
local handlers = handlersPerObject[obj.id]
|
||||
if handlers then
|
||||
for i = #handlers, 1, -1 do
|
||||
if handlers[i](obj, actor) == false then
|
||||
return -- skip other handlers
|
||||
end
|
||||
end
|
||||
end
|
||||
handlers = handlersPerType[obj.type]
|
||||
if handlers then
|
||||
for i = #handlers, 1, -1 do
|
||||
if handlers[i](obj, actor) == false then
|
||||
return -- skip other handlers
|
||||
end
|
||||
end
|
||||
end
|
||||
world._runStandardActivationAction(obj, actor)
|
||||
end
|
||||
|
||||
return {
|
||||
interfaceName = 'Activation',
|
||||
---
|
||||
-- @module Activation
|
||||
-- @usage require('openmw.interfaces').Activation
|
||||
interface = {
|
||||
--- Interface version
|
||||
-- @field [parent=#Activation] #number version
|
||||
version = 0,
|
||||
|
||||
--- Add new activation handler for a specific object.
|
||||
-- If `handler(object, actor)` returns false, other handlers for
|
||||
-- the same object (including type handlers) will be skipped.
|
||||
-- @function [parent=#Activation] addHandlerForObject
|
||||
-- @param openmw.core#GameObject obj The object.
|
||||
-- @param #function handler The handler.
|
||||
addHandlerForObject = function(obj, handler)
|
||||
local handlers = handlersPerObject[obj.id]
|
||||
if handlers == nil then
|
||||
handlers = {}
|
||||
handlersPerObject[obj.id] = handlers
|
||||
end
|
||||
handlers[#handlers + 1] = handler
|
||||
end,
|
||||
|
||||
--- Add new activation handler for a type of objects.
|
||||
-- If `handler(object, actor)` returns false, other handlers for
|
||||
-- the same object (including type handlers) will be skipped.
|
||||
-- @function [parent=#Activation] addHandlerForType
|
||||
-- @param #userdata type A type from the `openmw.types` package.
|
||||
-- @param #function handler The handler.
|
||||
addHandlerForType = function(type, handler)
|
||||
local handlers = handlersPerType[type]
|
||||
if handlers == nil then
|
||||
handlers = {}
|
||||
handlersPerType[type] = handlers
|
||||
end
|
||||
handlers[#handlers + 1] = handler
|
||||
end,
|
||||
},
|
||||
engineHandlers = { onActivate = onActivate },
|
||||
}
|
|
@ -107,6 +107,7 @@
|
|||
-- Player, actors, items, and statics are game objects.
|
||||
-- @type GameObject
|
||||
-- @extends #userdata
|
||||
-- @field #string id A unique id of this object (not record id), can be used as a key in a table.
|
||||
-- @field #boolean enabled Whether the object is enabled or disabled. Global scripts can set the value. Items in containers or inventories can't be disabled.
|
||||
-- @field openmw.util#Vector3 position Object position.
|
||||
-- @field openmw.util#Vector3 rotation Object rotation (ZXY order).
|
||||
|
|
|
@ -3,16 +3,19 @@
|
|||
-- @usage local I = require('openmw.interfaces')
|
||||
|
||||
---
|
||||
-- @field [parent=#interfaces] scripts.omw.camera.camera#scripts.omw.camera.camera Camera
|
||||
-- @field [parent=#interfaces] scripts.omw.ai#scripts.omw.activationhandlers Activation
|
||||
|
||||
---
|
||||
-- @field [parent=#interfaces] scripts.omw.settings.player#scripts.omw.settings.player Settings
|
||||
-- @field [parent=#interfaces] scripts.omw.ai#scripts.omw.ai AI
|
||||
|
||||
---
|
||||
-- @field [parent=#interfaces] scripts.omw.camera.camera#scripts.omw.camera.camera Camera
|
||||
|
||||
---
|
||||
-- @field [parent=#interfaces] scripts.omw.mwui.init#scripts.omw.mwui.init MWUI
|
||||
|
||||
---
|
||||
-- @field [parent=#interfaces] scripts.omw.ai#scripts.omw.ai AI
|
||||
-- @field [parent=#interfaces] scripts.omw.settings.player#scripts.omw.settings.player Settings
|
||||
|
||||
---
|
||||
-- @function [parent=#interfaces] __index
|
||||
|
|
Loading…
Reference in a new issue