diff --git a/apps/openmw/mwlua/mwscriptbindings.cpp b/apps/openmw/mwlua/mwscriptbindings.cpp index dbe02a9fed..92957efb71 100644 --- a/apps/openmw/mwlua/mwscriptbindings.cpp +++ b/apps/openmw/mwlua/mwscriptbindings.cpp @@ -7,6 +7,7 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwbase/world.hpp" #include "../mwscript/globalscripts.hpp" +#include "../mwworld/esmstore.hpp" #include "object.hpp" @@ -43,6 +44,10 @@ namespace sol struct is_automagical : std::false_type { }; + template <> + struct is_automagical : std::false_type + { + }; } namespace MWLua @@ -76,6 +81,7 @@ namespace MWLua // api["getGlobalScripts"] = [](std::string_view recordId) -> list of scripts // api["getLocalScripts"] = [](const GObject& obj) -> list of scripts + sol::state_view& lua = context.mLua->sol(); sol::usertype mwscript = context.mLua->sol().new_usertype("MWScript"); sol::usertype mwscriptVars = context.mLua->sol().new_usertype("MWScriptVariables"); @@ -108,6 +114,51 @@ namespace MWLua "No variable \"" + std::string(var) + "\" in mwscript " + s.mRef.mId.toDebugString()); }; + using GlobalStore = MWWorld::Store; + sol::usertype globalStoreT = lua.new_usertype("ESM3_GlobalStore"); + const GlobalStore* globalStore = &MWBase::Environment::get().getWorld()->getStore().get(); + globalStoreT[sol::meta_function::to_string] = [](const GlobalStore& store) { + return "ESM3_GlobalStore{" + std::to_string(store.getSize()) + " globals}"; + }; + globalStoreT[sol::meta_function::length] = [](const GlobalStore& store) { return store.getSize(); }; + globalStoreT[sol::meta_function::index] + = sol::overload([](const GlobalStore& store, std::string_view globalId) -> sol::optional { + auto g = store.search(ESM::RefId::deserializeText(globalId)); + if (g == nullptr) + return sol::nullopt; + char varType = MWBase::Environment::get().getWorld()->getGlobalVariableType(globalId); + if (varType == 's' || varType == 'l') + { + return static_cast(MWBase::Environment::get().getWorld()->getGlobalInt(globalId)); + } + else + { + return MWBase::Environment::get().getWorld()->getGlobalFloat(globalId); + } + }); + globalStoreT[sol::meta_function::new_index] + = sol::overload([](const GlobalStore& store, std::string_view globalId, float val) { + auto g = store.search(ESM::RefId::deserializeText(globalId)); + if (g == nullptr) + return; + char varType = MWBase::Environment::get().getWorld()->getGlobalVariableType(globalId); + if (varType == 's' || varType == 'l') + { + MWBase::Environment::get().getWorld()->setGlobalInt(globalId, static_cast(val)); + } + else + { + MWBase::Environment::get().getWorld()->setGlobalFloat(globalId, val); + } + }); + globalStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); + globalStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); + api["getGlobalVariables"] = [globalStore](sol::optional player) { + if (player.has_value() && player->ptr() != MWBase::Environment::get().getWorld()->getPlayerPtr()) + throw std::runtime_error("First argument must either be a player or be missing"); + + return globalStore; + }; return LuaUtil::makeReadOnly(api); } diff --git a/files/lua_api/openmw/world.lua b/files/lua_api/openmw/world.lua index 90868387e5..13fa75e0ad 100644 --- a/files/lua_api/openmw/world.lua +++ b/files/lua_api/openmw/world.lua @@ -29,6 +29,12 @@ -- @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 mutable global variables. In multiplayer, these may be specific to the provided player. +-- @function [parent=#MWScriptFunctions] getGlobalVariables +-- @param openmw.core#GameObject player (optional) Will be used in multiplayer mode to get the globals if there is a separate instance for each player. Currently has no effect. +-- @return #list<#number> + --- -- 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.