1
0
Fork 0
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:
psi29a 2023-07-02 19:23:09 +00:00
commit b1e00b59f2
10 changed files with 173 additions and 53 deletions

View file

@ -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

View file

@ -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");

View file

@ -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 {

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;
}
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)

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
/// 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.

View file

@ -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
{

View file

@ -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

View file

@ -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