mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-12 09:39:58 +00:00
Merge branch 'activation' into 'master'
Allow Lua scripts to extend or override standard activation mechanics See merge request OpenMW/openmw!2935
This commit is contained in:
commit
2a6e301925
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/statemanager.hpp"
|
#include "../mwbase/statemanager.hpp"
|
||||||
|
#include "../mwworld/action.hpp"
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
#include "../mwworld/manualref.hpp"
|
#include "../mwworld/manualref.hpp"
|
||||||
@ -106,6 +107,16 @@ namespace MWLua
|
|||||||
// TODO: add here overloads for other records
|
// 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);
|
return LuaUtil::makeReadOnly(api);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <components/lua/luastate.hpp>
|
#include <components/lua/luastate.hpp>
|
||||||
|
|
||||||
#include "../mwworld/action.hpp"
|
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/containerstore.hpp"
|
#include "../mwworld/containerstore.hpp"
|
||||||
@ -91,44 +90,6 @@ namespace MWLua
|
|||||||
world->enable(newPtr);
|
world->enable(newPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
template <typename ObjT>
|
||||||
using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>;
|
using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>;
|
||||||
|
|
||||||
@ -154,6 +115,7 @@ namespace MWLua
|
|||||||
template <class ObjectT>
|
template <class ObjectT>
|
||||||
void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
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["isValid"] = [](const ObjectT& o) { return !o.ptrOrNull().isEmpty(); };
|
||||||
objectT["recordId"] = sol::readonly_property(
|
objectT["recordId"] = sol::readonly_property(
|
||||||
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().serializeText(); });
|
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().serializeText(); });
|
||||||
@ -181,13 +143,16 @@ namespace MWLua
|
|||||||
{ dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
|
{ dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
|
||||||
};
|
};
|
||||||
|
|
||||||
objectT["activateBy"] = [context](const ObjectT& o, const ObjectT& actor) {
|
objectT["activateBy"] = [](const ObjectT& object, const ObjectT& actor) {
|
||||||
uint32_t esmRecordType = actor.ptr().getType();
|
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_)
|
if (esmRecordType != ESM::REC_CREA && esmRecordType != ESM::REC_NPC_)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"The argument of `activateBy` must be an actor who activates the object. Got: "
|
"The argument of `activateBy` must be an actor who activates the object. Got: "
|
||||||
+ actor.toString());
|
+ 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(); };
|
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)
|
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);
|
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));
|
std::unique_ptr<MWWorld::Action> action = (ptr.getClass().activate(ptr, actor));
|
||||||
action->execute(actor);
|
action->execute(actor);
|
||||||
if (action->getTarget() != MWWorld::Ptr() && action->getTarget() != ptr)
|
if (action->getTarget() != MWWorld::Ptr() && action->getTarget() != ptr)
|
||||||
{
|
{
|
||||||
updatePtr(ptr, action->getTarget());
|
updatePtr(ptr, action->getTarget());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int InterpreterContext::getMemberShort(const ESM::RefId& id, std::string_view name, bool global) const
|
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)
|
void World::activate(const Ptr& object, const Ptr& actor)
|
||||||
{
|
{
|
||||||
breakInvisibility(actor);
|
breakInvisibility(actor);
|
||||||
|
|
||||||
if (object.getRefData().activate())
|
if (object.getRefData().activate())
|
||||||
{
|
|
||||||
MWBase::Environment::get().getLuaManager()->objectActivated(object, actor);
|
MWBase::Environment::get().getLuaManager()->objectActivated(object, actor);
|
||||||
std::unique_ptr<MWWorld::Action> action = object.getClass().activate(object, actor);
|
|
||||||
action->execute(actor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ResetActorsVisitor
|
struct ResetActorsVisitor
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
paths=(
|
paths=(
|
||||||
openmw_aux/*lua
|
openmw_aux/*lua
|
||||||
|
scripts/omw/activationhandlers.lua
|
||||||
scripts/omw/ai.lua
|
scripts/omw/ai.lua
|
||||||
scripts/omw/playercontrols.lua
|
scripts/omw/playercontrols.lua
|
||||||
scripts/omw/camera/camera.lua
|
scripts/omw/camera/camera.lua
|
||||||
|
@ -27,6 +27,7 @@ Lua API reference
|
|||||||
openmw_aux_util
|
openmw_aux_util
|
||||||
openmw_aux_time
|
openmw_aux_time
|
||||||
openmw_aux_ui
|
openmw_aux_ui
|
||||||
|
interface_activation
|
||||||
interface_ai
|
interface_ai
|
||||||
interface_camera
|
interface_camera
|
||||||
interface_controls
|
interface_controls
|
||||||
@ -68,6 +69,9 @@ Interfaces of built-in scripts
|
|||||||
* - Interface
|
* - Interface
|
||||||
- Can be used
|
- Can be used
|
||||||
- Description
|
- Description
|
||||||
|
* - :ref:`Activation <Interface Activation>`
|
||||||
|
- by global scripts
|
||||||
|
- Allows to extend or override built-in activation mechanics.
|
||||||
* - :ref:`AI <Interface AI>`
|
* - :ref:`AI <Interface AI>`
|
||||||
- by local scripts
|
- by local scripts
|
||||||
- Control basic AI of NPCs and creatures.
|
- 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
|
* - Interface
|
||||||
- Can be used
|
- Can be used
|
||||||
- Description
|
- Description
|
||||||
|
* - :ref:`Activation <Interface Activation>`
|
||||||
|
- by global scripts
|
||||||
|
- Allows to extend or override built-in activation mechanics.
|
||||||
* - :ref:`AI <Interface AI>`
|
* - :ref:`AI <Interface AI>`
|
||||||
- by local scripts
|
- by local scripts
|
||||||
- Control basic AI of NPCs and creatures.
|
- Control basic AI of NPCs and creatures.
|
||||||
|
@ -63,6 +63,7 @@ set(BUILTIN_DATA_FILES
|
|||||||
|
|
||||||
builtin.omwscripts
|
builtin.omwscripts
|
||||||
|
|
||||||
|
scripts/omw/activationhandlers.lua
|
||||||
scripts/omw/ai.lua
|
scripts/omw/ai.lua
|
||||||
scripts/omw/camera/camera.lua
|
scripts/omw/camera/camera.lua
|
||||||
scripts/omw/camera/head_bobbing.lua
|
scripts/omw/camera/head_bobbing.lua
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
|
# UI framework
|
||||||
PLAYER: scripts/omw/mwui/init.lua
|
PLAYER: scripts/omw/mwui/init.lua
|
||||||
|
|
||||||
|
# Settings framework
|
||||||
GLOBAL: scripts/omw/settings/global.lua
|
GLOBAL: scripts/omw/settings/global.lua
|
||||||
PLAYER: scripts/omw/settings/player.lua
|
PLAYER: scripts/omw/settings/player.lua
|
||||||
|
|
||||||
|
# Mechanics
|
||||||
|
GLOBAL: scripts/omw/activationhandlers.lua
|
||||||
PLAYER: scripts/omw/playercontrols.lua
|
PLAYER: scripts/omw/playercontrols.lua
|
||||||
PLAYER: scripts/omw/camera/camera.lua
|
PLAYER: scripts/omw/camera/camera.lua
|
||||||
NPC,CREATURE: scripts/omw/ai.lua
|
NPC,CREATURE: scripts/omw/ai.lua
|
||||||
|
|
||||||
|
# Lua console
|
||||||
PLAYER: scripts/omw/console/player.lua
|
PLAYER: scripts/omw/console/player.lua
|
||||||
GLOBAL: scripts/omw/console/global.lua
|
GLOBAL: scripts/omw/console/global.lua
|
||||||
CUSTOM: scripts/omw/console/local.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.
|
-- Player, actors, items, and statics are game objects.
|
||||||
-- @type GameObject
|
-- @type GameObject
|
||||||
-- @extends #userdata
|
-- @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 #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 position Object position.
|
||||||
-- @field openmw.util#Vector3 rotation Object rotation (ZXY order).
|
-- @field openmw.util#Vector3 rotation Object rotation (ZXY order).
|
||||||
|
@ -3,16 +3,19 @@
|
|||||||
-- @usage local I = require('openmw.interfaces')
|
-- @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.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
|
-- @function [parent=#interfaces] __index
|
||||||
|
Loading…
x
Reference in New Issue
Block a user