1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-04-02 13:06:39 +00:00

Change MWScript Lua bindings (#7142)

This commit is contained in:
Petr Mikheev 2023-04-01 15:15:28 +02:00
parent 2a271cdecc
commit 866f67ed19
10 changed files with 173 additions and 53 deletions

View file

@ -60,7 +60,7 @@ add_openmw_dir (mwscript
add_openmw_dir (mwlua add_openmw_dir (mwlua
luamanagerimp object worldview userdataserializer luaevents engineevents objectvariant luamanagerimp object worldview userdataserializer luaevents engineevents objectvariant
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings
camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing types/levelledlist types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing types/levelledlist
worker magicbindings worker magicbindings

View file

@ -18,14 +18,6 @@
#include "context.hpp" #include "context.hpp"
#include "worldview.hpp" #include "worldview.hpp"
namespace MWLua
{
struct LocalMWScript
{
LObject mSelf;
};
}
namespace sol namespace sol
{ {
template <> template <>
@ -36,10 +28,6 @@ namespace sol
struct is_automagical<MWLua::SelfObject> : std::false_type struct is_automagical<MWLua::SelfObject> : std::false_type
{ {
}; };
template <>
struct is_automagical<MWLua::LocalMWScript> : std::false_type
{
};
} }
namespace MWLua namespace MWLua
@ -74,27 +62,6 @@ namespace MWLua
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; }); selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; }; selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; };
selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; }; selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; };
selfAPI["mwscript"] = sol::readonly_property([](SelfObject& self) -> sol::optional<LocalMWScript> {
if (self.ptr().getRefData().getLocals().getScriptId().empty())
return sol::nullopt;
else
return LocalMWScript{ LObject(self.id()) };
});
sol::usertype<LocalMWScript> mwscript = context.mLua->sol().new_usertype<LocalMWScript>("LocalMWScript");
mwscript[sol::meta_function::to_string] = [](const LocalMWScript& s) {
return s.mSelf.ptr().getRefData().getLocals().getScriptId().toDebugString();
};
mwscript[sol::meta_function::index] = [](const LocalMWScript& s, std::string_view var) {
MWScript::Locals& locals = s.mSelf.ptr().getRefData().getLocals();
return locals.getVarAsDouble(locals.getScriptId(), Misc::StringUtils::lowerCase(var));
};
mwscript[sol::meta_function::new_index] = [](const LocalMWScript& s, std::string_view var, double val) {
MWScript::Locals& locals = s.mSelf.ptr().getRefData().getLocals();
if (!locals.setVar(locals.getScriptId(), Misc::StringUtils::lowerCase(var), val))
throw std::runtime_error(
"No variable \"" + std::string(var) + "\" in mwscript " + locals.getScriptId().toDebugString());
};
using AiPackage = MWMechanics::AiPackage; using AiPackage = MWMechanics::AiPackage;
sol::usertype<AiPackage> aiPackage = context.mLua->sol().new_usertype<AiPackage>("AiPackage"); sol::usertype<AiPackage> aiPackage = context.mLua->sol().new_usertype<AiPackage>("AiPackage");

View file

@ -26,6 +26,7 @@
#include "luaevents.hpp" #include "luaevents.hpp"
#include "luamanagerimp.hpp" #include "luamanagerimp.hpp"
#include "mwscriptbindings.hpp"
#include "worldview.hpp" #include "worldview.hpp"
#include "camerabindings.hpp" #include "camerabindings.hpp"
@ -111,7 +112,7 @@ namespace MWLua
{ {
auto* lua = context.mLua; auto* lua = context.mLua;
sol::table api(lua->sol(), sol::create); sol::table api(lua->sol(), sol::create);
api["API_REVISION"] = 40; api["API_REVISION"] = 41;
api["quit"] = [lua]() { api["quit"] = [lua]() {
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback(); Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
MWBase::Environment::get().getStateManager()->requestQuit(); MWBase::Environment::get().getStateManager()->requestQuit();
@ -221,6 +222,7 @@ namespace MWLua
WorldView* worldView = context.mWorldView; WorldView* worldView = context.mWorldView;
addTimeBindings(api, context, true); addTimeBindings(api, context, true);
addCellGetters(api, context); addCellGetters(api, context);
api["mwscript"] = initMWScriptBindings(context);
api["activeActors"] = GObjectList{ worldView->getActorsInScene() }; api["activeActors"] = GObjectList{ worldView->getActorsInScene() };
api["players"] = GObjectList{ worldView->getPlayers() }; api["players"] = GObjectList{ worldView->getPlayers() };
api["createObject"] = [](std::string_view recordId, sol::optional<int> count) -> GObject { api["createObject"] = [](std::string_view recordId, sol::optional<int> count) -> GObject {

View file

@ -0,0 +1,109 @@
#include "mwscriptbindings.hpp"
#include <components/lua/luastate.hpp>
#include "../mwbase/world.hpp"
#include "../mwscript/scriptmanagerimp.hpp"
#include "object.hpp"
namespace MWLua
{
struct MWScriptRef
{
ESM::RefId mId;
sol::optional<GObject> mObj;
MWScript::Locals& getLocals()
{
if (mObj)
return mObj->ptr().getRefData().getLocals();
else
return MWBase::Environment::get().getScriptManager()->getGlobalScripts().getLocals(mId);
}
};
struct MWScriptVariables
{
MWScriptRef mRef;
};
}
namespace sol
{
template <>
struct is_automagical<MWLua::MWScriptRef> : std::false_type
{
};
template <>
struct is_automagical<MWLua::MWScriptVariables> : std::false_type
{
};
}
namespace MWLua
{
sol::table initMWScriptBindings(const Context& context)
{
sol::table api(context.mLua->sol(), sol::create);
api["getGlobalScript"]
= [](std::string_view recordId, sol::optional<GObject> player) -> sol::optional<MWScriptRef> {
if (player.has_value() && player->ptr() != MWBase::Environment::get().getWorld()->getPlayerPtr())
throw std::runtime_error("Second argument must either be a player or be missing");
auto scriptId = ESM::RefId::deserializeText(recordId);
if (MWBase::Environment::get().getScriptManager()->getGlobalScripts().getScriptIfPresent(scriptId))
return MWScriptRef{ scriptId, sol::nullopt };
else
return sol::nullopt;
};
api["getLocalScript"] = [](const GObject& obj, sol::optional<GObject> player) -> sol::optional<MWScriptRef> {
if (player.has_value() && player->ptr() != MWBase::Environment::get().getWorld()->getPlayerPtr())
throw std::runtime_error("Second argument must either be a player or be missing");
auto scriptId = obj.ptr().getRefData().getLocals().getScriptId();
if (scriptId.empty())
return sol::nullopt;
return MWScriptRef{ scriptId, obj };
};
// In multiplayer it will be possible to have several instances (per player) of a single script,
// so we will likely add functions returning Lua table of scripts.
// api["getGlobalScripts"] = [](std::string_view recordId) -> list of scripts
// api["getLocalScripts"] = [](const GObject& obj) -> list of scripts
sol::usertype<MWScriptRef> mwscript = context.mLua->sol().new_usertype<MWScriptRef>("MWScript");
sol::usertype<MWScriptVariables> mwscriptVars
= context.mLua->sol().new_usertype<MWScriptVariables>("MWScriptVariables");
mwscript[sol::meta_function::to_string]
= [](const MWScriptRef& s) { return std::string("MWScript{") + s.mId.toDebugString() + "}"; };
mwscript["recordId"] = sol::readonly_property([](const MWScriptRef& s) { return s.mId.serializeText(); });
mwscript["variables"] = sol::readonly_property([](const MWScriptRef& s) { return MWScriptVariables{ s }; });
mwscript["object"] = sol::readonly_property([](const MWScriptRef& s) -> sol::optional<GObject> {
if (s.mObj)
return s.mObj;
const MWScript::GlobalScriptDesc* script
= MWBase::Environment::get().getScriptManager()->getGlobalScripts().getScriptIfPresent(s.mId);
if (!script)
throw std::runtime_error("Invalid MWScriptRef");
const MWWorld::Ptr* ptr = script->getPtrIfPresent();
if (ptr && !ptr->isEmpty())
return GObject(*ptr);
else
return sol::nullopt;
});
mwscript["player"] = sol::readonly_property(
[](const MWScriptRef&) { return GObject(MWBase::Environment::get().getWorld()->getPlayerPtr()); });
mwscriptVars[sol::meta_function::index] = [](MWScriptVariables& s, std::string_view var) {
return s.mRef.getLocals().getVarAsDouble(s.mRef.mId, Misc::StringUtils::lowerCase(var));
};
mwscriptVars[sol::meta_function::new_index] = [](MWScriptVariables& s, std::string_view var, double val) {
MWScript::Locals& locals = s.mRef.getLocals();
if (!locals.setVar(s.mRef.mId, Misc::StringUtils::lowerCase(var), val))
throw std::runtime_error(
"No variable \"" + std::string(var) + "\" in mwscript " + s.mRef.mId.toDebugString());
};
return LuaUtil::makeReadOnly(api);
}
}

View file

@ -0,0 +1,15 @@
#ifndef MWLUA_MWSCRIPTBINDINGS_H
#define MWLUA_MWSCRIPTBINDINGS_H
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initMWScriptBindings(const Context&);
}
#endif // MWLUA_MWSCRIPTBINDINGS_H

View file

@ -309,12 +309,12 @@ namespace MWScript
return iter->second->mLocals; return iter->second->mLocals;
} }
const Locals* GlobalScripts::getLocalsIfPresent(const ESM::RefId& name) const const GlobalScriptDesc* GlobalScripts::getScriptIfPresent(const ESM::RefId& name) const
{ {
auto iter = mScripts.find(name); auto iter = mScripts.find(name);
if (iter == mScripts.end()) if (iter == mScripts.end())
return nullptr; return nullptr;
return &iter->second->mLocals; return iter->second.get();
} }
void GlobalScripts::updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated) void GlobalScripts::updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated)

View file

@ -87,7 +87,7 @@ namespace MWScript
///< If the script \a name has not been added as a global script yet, it is added ///< If the script \a name has not been added as a global script yet, it is added
/// automatically, but is not set to running state. /// automatically, but is not set to running state.
const Locals* getLocalsIfPresent(const ESM::RefId& name) const; const GlobalScriptDesc* getScriptIfPresent(const ESM::RefId& name) const;
void updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated); void updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated);
///< Update the Ptrs stored in mTarget. Should be called after the reference has been moved to a new cell. ///< Update the Ptrs stored in mTarget. Should be called after the reference has been moved to a new cell.

View file

@ -35,13 +35,13 @@ namespace MWScript
if (mInitialised) if (mInitialised)
return false; return false;
const Locals* global const GlobalScriptDesc* global
= MWBase::Environment::get().getScriptManager()->getGlobalScripts().getLocalsIfPresent(script.mId); = MWBase::Environment::get().getScriptManager()->getGlobalScripts().getScriptIfPresent(script.mId);
if (global) if (global)
{ {
mShorts = global->mShorts; mShorts = global->mLocals.mShorts;
mLongs = global->mLongs; mLongs = global->mLocals.mLongs;
mFloats = global->mFloats; mFloats = global->mLocals.mFloats;
} }
else else
{ {

View file

@ -36,16 +36,6 @@
-- Movement controls (only for actors) -- Movement controls (only for actors)
-- @field [parent=#self] #ActorControls controls -- @field [parent=#self] #ActorControls controls
---
-- Local variables of an mwscript on this object (nil if there is no mwscript)
-- @usage if self.mwscript then ... end -- check if there is an mwscript on this object
-- @usage tostring(self.mwscript) -- name of the script
-- @usage -- print value of local variable 'something' (0 if there is no such variable)
-- print(self.mwscript.something)
-- @usage -- set local variable 'something' (raises an error if there is no such variable)
-- self.mwscript.something = 5
-- @field [parent=#self] #MWScriptVariables mwscript
--- ---
-- Allows to view and/or modify controls of an actor. All fields are mutable. -- Allows to view and/or modify controls of an actor. All fields are mutable.
-- @type ActorControls -- @type ActorControls

View file

@ -14,6 +14,43 @@
-- List of players. Currently (since multiplayer is not yet implemented) always has one element. -- List of players. Currently (since multiplayer is not yet implemented) always has one element.
-- @field [parent=#world] openmw.core#ObjectList players -- @field [parent=#world] openmw.core#ObjectList players
---
-- Functions related to MWScript (see @{#MWScriptFunctions}).
-- @field [parent=#world] #MWScriptFunctions mwscript
---
-- Functions related to MWScript.
-- @type MWScriptFunctions
---
-- Returns local mwscript on ``object``. Returns `nil` if the script doesn't exist or is not started.
-- @function [parent=#MWScriptFunctions] getLocalScript
-- @param openmw.core#GameObject object
-- @param openmw.core#GameObject player (optional) Will be used in multiplayer mode to get the script if there is a separate instance for each player. Currently has no effect.
-- @return #MWScript, #nil
---
-- Returns global mwscript with given recordId. Returns `nil` if the script doesn't exist or is not started.
-- Currently there can be only one instance of each mwscript, but in multiplayer it will be possible to have a separate instance per player.
-- @function [parent=#MWScriptFunctions] getGlobalScript
-- @param #string recordId
-- @param openmw.core#GameObject player (optional) Will be used in multiplayer mode to get the script if there is a separate instance for each player. Currently has no effect.
-- @return #MWScript, #nil
---
-- @type MWScript
-- @field #string recordId Id of the script
-- @field openmw.core#GameObject object The object the script is attached to.
-- @field openmw.core#GameObject player The player the script refers to.
-- @field #MWScriptVariables variables Local variables of the script (mutable)
-- @usage
-- for _, script in ipairs(world.mwscript.getLocalScripts(object)) do
-- -- print the value of local variable 'something' (0 if there is no such variable)
-- print(script.variables.something)
-- -- set the variable 'something' (raises an error if there is no such variable)
-- script.variables.something = 5
-- end
--- ---
-- Loads a named cell -- Loads a named cell
-- @function [parent=#world] getCellByName -- @function [parent=#world] getCellByName