mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 15:29:55 +00:00
Merge branch 'lua_mwscript' into 'master'
Change MWScript Lua bindings (#7142) See merge request OpenMW/openmw!2881
This commit is contained in:
commit
b1e00b59f2
10 changed files with 173 additions and 53 deletions
|
@ -60,7 +60,7 @@ add_openmw_dir (mwscript
|
|||
|
||||
add_openmw_dir (mwlua
|
||||
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
|
||||
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
|
||||
|
|
|
@ -18,14 +18,6 @@
|
|||
#include "context.hpp"
|
||||
#include "worldview.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
struct LocalMWScript
|
||||
{
|
||||
LObject mSelf;
|
||||
};
|
||||
}
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
|
@ -36,10 +28,6 @@ namespace sol
|
|||
struct is_automagical<MWLua::SelfObject> : std::false_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct is_automagical<MWLua::LocalMWScript> : std::false_type
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
|
@ -74,27 +62,6 @@ namespace MWLua
|
|||
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
|
||||
selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; };
|
||||
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;
|
||||
sol::usertype<AiPackage> aiPackage = context.mLua->sol().new_usertype<AiPackage>("AiPackage");
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "luaevents.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
#include "mwscriptbindings.hpp"
|
||||
#include "worldview.hpp"
|
||||
|
||||
#include "camerabindings.hpp"
|
||||
|
@ -111,7 +112,7 @@ namespace MWLua
|
|||
{
|
||||
auto* lua = context.mLua;
|
||||
sol::table api(lua->sol(), sol::create);
|
||||
api["API_REVISION"] = 40;
|
||||
api["API_REVISION"] = 41;
|
||||
api["quit"] = [lua]() {
|
||||
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
||||
MWBase::Environment::get().getStateManager()->requestQuit();
|
||||
|
@ -221,6 +222,7 @@ namespace MWLua
|
|||
WorldView* worldView = context.mWorldView;
|
||||
addTimeBindings(api, context, true);
|
||||
addCellGetters(api, context);
|
||||
api["mwscript"] = initMWScriptBindings(context);
|
||||
api["activeActors"] = GObjectList{ worldView->getActorsInScene() };
|
||||
api["players"] = GObjectList{ worldView->getPlayers() };
|
||||
api["createObject"] = [](std::string_view recordId, sol::optional<int> count) -> GObject {
|
||||
|
|
109
apps/openmw/mwlua/mwscriptbindings.cpp
Normal file
109
apps/openmw/mwlua/mwscriptbindings.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
15
apps/openmw/mwlua/mwscriptbindings.hpp
Normal file
15
apps/openmw/mwlua/mwscriptbindings.hpp
Normal 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
|
|
@ -309,12 +309,12 @@ namespace MWScript
|
|||
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);
|
||||
if (iter == mScripts.end())
|
||||
return nullptr;
|
||||
return &iter->second->mLocals;
|
||||
return iter->second.get();
|
||||
}
|
||||
|
||||
void GlobalScripts::updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated)
|
||||
|
|
|
@ -87,7 +87,7 @@ namespace MWScript
|
|||
///< 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.
|
||||
|
||||
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);
|
||||
///< Update the Ptrs stored in mTarget. Should be called after the reference has been moved to a new cell.
|
||||
|
|
|
@ -35,13 +35,13 @@ namespace MWScript
|
|||
if (mInitialised)
|
||||
return false;
|
||||
|
||||
const Locals* global
|
||||
= MWBase::Environment::get().getScriptManager()->getGlobalScripts().getLocalsIfPresent(script.mId);
|
||||
const GlobalScriptDesc* global
|
||||
= MWBase::Environment::get().getScriptManager()->getGlobalScripts().getScriptIfPresent(script.mId);
|
||||
if (global)
|
||||
{
|
||||
mShorts = global->mShorts;
|
||||
mLongs = global->mLongs;
|
||||
mFloats = global->mFloats;
|
||||
mShorts = global->mLocals.mShorts;
|
||||
mLongs = global->mLocals.mLongs;
|
||||
mFloats = global->mLocals.mFloats;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -36,16 +36,6 @@
|
|||
-- Movement controls (only for actors)
|
||||
-- @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.
|
||||
-- @type ActorControls
|
||||
|
|
|
@ -14,6 +14,43 @@
|
|||
-- List of players. Currently (since multiplayer is not yet implemented) always has one element.
|
||||
-- @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
|
||||
-- @function [parent=#world] getCellByName
|
||||
|
|
Loading…
Reference in a new issue