diff --git a/apps/openmw/mwlua/mwscriptbindings.cpp b/apps/openmw/mwlua/mwscriptbindings.cpp index 6ed3486c8a..b85e72577d 100644 --- a/apps/openmw/mwlua/mwscriptbindings.cpp +++ b/apps/openmw/mwlua/mwscriptbindings.cpp @@ -1,5 +1,6 @@ #include "mwscriptbindings.hpp" +#include #include #include @@ -143,18 +144,75 @@ namespace MWLua }); 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) -> sol::optional { - if (s.mRef.getLocals().hasVar(s.mRef.mId, var)) - return s.mRef.getLocals().getVarAsDouble(s.mRef.mId, Misc::StringUtils::lowerCase(var)); - else + mwscriptVars[sol::meta_function::length] + = [](MWScriptVariables& s) { return s.mRef.getLocals().getSize(s.mRef.mId); }; + mwscriptVars[sol::meta_function::index] = sol::overload( + [](MWScriptVariables& s, std::string_view var) -> sol::optional { + if (s.mRef.getLocals().hasVar(s.mRef.mId, var)) + return s.mRef.getLocals().getVarAsDouble(s.mRef.mId, Misc::StringUtils::lowerCase(var)); + else + return sol::nullopt; + }, + [](MWScriptVariables& s, std::size_t index) -> sol::optional { + auto& locals = s.mRef.getLocals(); + if (index < 1 || locals.getSize(s.mRef.mId) < index) + return sol::nullopt; + if (index <= locals.mShorts.size()) + return locals.mShorts[index - 1]; + index -= locals.mShorts.size(); + if (index <= locals.mLongs.size()) + return locals.mLongs[index - 1]; + index -= locals.mLongs.size(); + if (index <= locals.mFloats.size()) + return locals.mFloats[index - 1]; return sol::nullopt; - }; - 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()); + }); + mwscriptVars[sol::meta_function::new_index] = sol::overload( + [](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()); + }, + [](MWScriptVariables& s, std::size_t index, double val) { + auto& locals = s.mRef.getLocals(); + if (index < 1 || locals.getSize(s.mRef.mId) < index) + throw std::runtime_error("Index out of range in mwscript " + s.mRef.mId.toDebugString()); + if (index <= locals.mShorts.size()) + { + locals.mShorts[index - 1] = static_cast(val); + return; + } + index -= locals.mShorts.size(); + if (index <= locals.mLongs.size()) + { + locals.mLongs[index - 1] = static_cast(val); + return; + } + index -= locals.mLongs.size(); + if (index <= locals.mFloats.size()) + locals.mFloats[index - 1] = static_cast(val); + }); + mwscriptVars[sol::meta_function::pairs] = [](MWScriptVariables& s) { + std::size_t index = 0; + const auto& compilerLocals = MWBase::Environment::get().getScriptManager()->getLocals(s.mRef.mId); + auto& locals = s.mRef.getLocals(); + std::size_t size = locals.getSize(s.mRef.mId); + return sol::as_function( + [&, index, size](sol::this_state ts) mutable -> sol::optional> { + if (index >= size) + return sol::nullopt; + auto i = index++; + if (i <= locals.mShorts.size()) + return std::make_tuple(compilerLocals.get('s')[i], locals.mShorts[i]); + i -= locals.mShorts.size(); + if (i <= locals.mLongs.size()) + return std::make_tuple(compilerLocals.get('l')[i], locals.mLongs[i]); + i -= locals.mLongs.size(); + if (i <= locals.mFloats.size()) + return std::make_tuple(compilerLocals.get('f')[i], locals.mFloats[i]); + return sol::nullopt; + }); }; using GlobalStore = MWWorld::Store; @@ -222,5 +280,4 @@ namespace MWLua }; return LuaUtil::makeReadOnly(api); } - } diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index db5dda5204..3bd4e82059 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -121,6 +121,12 @@ namespace MWScript return true; } + std::size_t Locals::getSize(const ESM::RefId& script) + { + ensure(script); + return mShorts.size() + mLongs.size() + mFloats.size(); + } + bool Locals::write(ESM::Locals& locals, const ESM::RefId& script) const { if (!mInitialised) diff --git a/apps/openmw/mwscript/locals.hpp b/apps/openmw/mwscript/locals.hpp index 1cc8fecb9b..76b582b78c 100644 --- a/apps/openmw/mwscript/locals.hpp +++ b/apps/openmw/mwscript/locals.hpp @@ -67,6 +67,8 @@ namespace MWScript return static_cast(getVarAsDouble(script, var)); } + std::size_t getSize(const ESM::RefId& script); + /// \note If locals have not been configured yet, no data is written. /// /// \return Locals written?