mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 07:39:41 +00:00
Lua console
This commit is contained in:
parent
1363292fc9
commit
88d09c336c
18 changed files with 522 additions and 28 deletions
|
@ -92,6 +92,8 @@ namespace MWBase
|
|||
|
||||
// Drops script cache and reloads all scripts. Calls `onSave` and `onLoad` for every script.
|
||||
virtual void reloadAllScripts() = 0;
|
||||
|
||||
virtual void handleConsoleCommand(const std::string& consoleMode, const std::string& command, const MWWorld::Ptr& selectedPtr) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -154,6 +154,13 @@ namespace MWBase
|
|||
virtual void updateSpellWindow() = 0;
|
||||
|
||||
virtual void setConsoleSelectedObject(const MWWorld::Ptr& object) = 0;
|
||||
virtual void setConsoleMode(const std::string& mode) = 0;
|
||||
|
||||
static constexpr std::string_view sConsoleColor_Default = "#FFFFFF";
|
||||
static constexpr std::string_view sConsoleColor_Error = "#FF2222";
|
||||
static constexpr std::string_view sConsoleColor_Success = "#FF00FF";
|
||||
static constexpr std::string_view sConsoleColor_Info = "#AAAAAA";
|
||||
virtual void printToConsole(const std::string& msg, std::string_view color) = 0;
|
||||
|
||||
/// Set time left for the player to start drowning (update the drowning bar)
|
||||
/// @param time time left to start drowning
|
||||
|
|
|
@ -162,25 +162,34 @@ namespace MWGui
|
|||
MyGUI::LayerManager::getInstance().upLayerItem(mMainWidget);
|
||||
}
|
||||
|
||||
void Console::print(const std::string &msg, const std::string& color)
|
||||
void Console::print(const std::string &msg, std::string_view color)
|
||||
{
|
||||
mHistory->addText(color + MyGUI::TextIterator::toTagsString(msg));
|
||||
mHistory->addText(std::string(color) + MyGUI::TextIterator::toTagsString(msg));
|
||||
}
|
||||
|
||||
void Console::printOK(const std::string &msg)
|
||||
{
|
||||
print(msg + "\n", "#FF00FF");
|
||||
print(msg + "\n", MWBase::WindowManager::sConsoleColor_Success);
|
||||
}
|
||||
|
||||
void Console::printError(const std::string &msg)
|
||||
{
|
||||
print(msg + "\n", "#FF2222");
|
||||
print(msg + "\n", MWBase::WindowManager::sConsoleColor_Error);
|
||||
}
|
||||
|
||||
void Console::execute (const std::string& command)
|
||||
{
|
||||
// Log the command
|
||||
print("> " + command + "\n");
|
||||
if (mConsoleMode.empty())
|
||||
print("> " + command + "\n");
|
||||
else
|
||||
print(mConsoleMode + " " + command + "\n");
|
||||
|
||||
if (!mConsoleMode.empty() || (command.size() >= 3 && std::string_view(command).substr(0, 3) == "lua"))
|
||||
{
|
||||
MWBase::Environment::get().getLuaManager()->handleConsoleCommand(mConsoleMode, command, mPtr);
|
||||
return;
|
||||
}
|
||||
|
||||
Compiler::Locals locals;
|
||||
if (!mPtr.isEmpty())
|
||||
|
@ -271,7 +280,7 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
}
|
||||
else if(key == MyGUI::KeyCode::Tab)
|
||||
else if(key == MyGUI::KeyCode::Tab && mConsoleMode.empty())
|
||||
{
|
||||
std::vector<std::string> matches;
|
||||
listNames();
|
||||
|
@ -475,7 +484,7 @@ namespace MWGui
|
|||
|
||||
void Console::onResChange(int width, int height)
|
||||
{
|
||||
setCoord(10,10, width-10, height/2);
|
||||
setCoord(10, 10, width-10, height/2);
|
||||
}
|
||||
|
||||
void Console::updateSelectedObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr)
|
||||
|
@ -489,23 +498,31 @@ namespace MWGui
|
|||
if (!object.isEmpty())
|
||||
{
|
||||
if (object == mPtr)
|
||||
{
|
||||
setTitle("#{sConsoleTitle}");
|
||||
mPtr=MWWorld::Ptr();
|
||||
}
|
||||
mPtr = MWWorld::Ptr();
|
||||
else
|
||||
{
|
||||
setTitle("#{sConsoleTitle} (" + object.getCellRef().getRefId() + ")");
|
||||
mPtr = object;
|
||||
}
|
||||
// User clicked on an object. Restore focus to the console command line.
|
||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
setTitle("#{sConsoleTitle}");
|
||||
mPtr = MWWorld::Ptr();
|
||||
}
|
||||
updateConsoleTitle();
|
||||
}
|
||||
|
||||
void Console::updateConsoleTitle()
|
||||
{
|
||||
std::string title = "#{sConsoleTitle}";
|
||||
if (!mConsoleMode.empty())
|
||||
title = mConsoleMode + " " + title;
|
||||
if (!mPtr.isEmpty())
|
||||
title.append(" (" + mPtr.getCellRef().getRefId() + ")");
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
void Console::setConsoleMode(std::string_view mode)
|
||||
{
|
||||
mConsoleMode = std::string(mode);
|
||||
updateConsoleTitle();
|
||||
}
|
||||
|
||||
void Console::onReferenceUnavailable()
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <components/compiler/output.hpp>
|
||||
#include <components/compiler/extensions.hpp>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwscript/compilercontext.hpp"
|
||||
#include "../mwscript/interpretercontext.hpp"
|
||||
|
||||
|
@ -40,7 +42,7 @@ namespace MWGui
|
|||
void onResChange(int width, int height) override;
|
||||
|
||||
// Print a message to the console, in specified color.
|
||||
void print(const std::string &msg, const std::string& color = "#FFFFFF");
|
||||
void print(const std::string &msg, std::string_view color = MWBase::WindowManager::sConsoleColor_Default);
|
||||
|
||||
// These are pre-colored versions that you should use.
|
||||
|
||||
|
@ -60,12 +62,19 @@ namespace MWGui
|
|||
|
||||
void resetReference () override;
|
||||
|
||||
const std::string& getConsoleMode() const { return mConsoleMode; }
|
||||
void setConsoleMode(std::string_view mode);
|
||||
|
||||
protected:
|
||||
|
||||
void onReferenceUnavailable() override;
|
||||
|
||||
private:
|
||||
|
||||
std::string mConsoleMode;
|
||||
|
||||
void updateConsoleTitle();
|
||||
|
||||
void keyPress(MyGUI::Widget* _sender,
|
||||
MyGUI::KeyCode key,
|
||||
MyGUI::Char _char);
|
||||
|
|
|
@ -2069,6 +2069,16 @@ namespace MWGui
|
|||
mConsole->setSelectedObject(object);
|
||||
}
|
||||
|
||||
void WindowManager::printToConsole(const std::string& msg, std::string_view color)
|
||||
{
|
||||
mConsole->print(msg, color);
|
||||
}
|
||||
|
||||
void WindowManager::setConsoleMode(const std::string& mode)
|
||||
{
|
||||
mConsole->setConsoleMode(mode);
|
||||
}
|
||||
|
||||
std::string WindowManager::correctIconPath(const std::string& path)
|
||||
{
|
||||
return Misc::ResourceHelpers::correctIconPath(path, mResourceSystem->getVFS());
|
||||
|
|
|
@ -195,6 +195,8 @@ namespace MWGui
|
|||
void updateSpellWindow() override;
|
||||
|
||||
void setConsoleSelectedObject(const MWWorld::Ptr& object) override;
|
||||
void printToConsole(const std::string& msg, std::string_view color) override;
|
||||
void setConsoleMode(const std::string& mode) override;
|
||||
|
||||
/// Set time left for the player to start drowning (update the drowning bar)
|
||||
/// @param time time left to start drowning
|
||||
|
|
|
@ -242,6 +242,9 @@ namespace MWLua
|
|||
for (const std::string& message : mUIMessages)
|
||||
windowManager->messageBox(message);
|
||||
mUIMessages.clear();
|
||||
for (auto& [msg, color] : mInGameConsoleMessages)
|
||||
windowManager->printToConsole(msg, "#" + color.toHex());
|
||||
mInGameConsoleMessages.clear();
|
||||
|
||||
for (std::unique_ptr<Action>& action : mActionQueue)
|
||||
action->safeApply(mWorldView);
|
||||
|
@ -256,6 +259,7 @@ namespace MWLua
|
|||
{
|
||||
LuaUi::clearUserInterface();
|
||||
mUiResourceManager.clear();
|
||||
MWBase::Environment::get().getWindowManager()->setConsoleMode("");
|
||||
mActiveLocalScripts.clear();
|
||||
mLocalEvents.clear();
|
||||
mGlobalEvents.clear();
|
||||
|
@ -480,6 +484,7 @@ namespace MWLua
|
|||
Log(Debug::Info) << "Reload Lua";
|
||||
|
||||
LuaUi::clearUserInterface();
|
||||
MWBase::Environment::get().getWindowManager()->setConsoleMode("");
|
||||
mUiResourceManager.clear();
|
||||
mLua.dropScriptCache();
|
||||
initConfiguration();
|
||||
|
@ -503,6 +508,25 @@ namespace MWLua
|
|||
scripts->receiveEngineEvent(LocalScripts::OnActive());
|
||||
}
|
||||
|
||||
void LuaManager::handleConsoleCommand(const std::string& consoleMode, const std::string& command, const MWWorld::Ptr& selectedPtr)
|
||||
{
|
||||
PlayerScripts* playerScripts = nullptr;
|
||||
if (!mPlayer.isEmpty())
|
||||
playerScripts = dynamic_cast<PlayerScripts*>(mPlayer.getRefData().getLuaScripts());
|
||||
if (!playerScripts)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->printToConsole("You must enter a game session to run Lua commands\n",
|
||||
MWBase::WindowManager::sConsoleColor_Error);
|
||||
return;
|
||||
}
|
||||
sol::object selected = sol::nil;
|
||||
if (!selectedPtr.isEmpty())
|
||||
selected = sol::make_object(mLua.sol(), LObject(getId(selectedPtr), mWorldView.getObjectRegistry()));
|
||||
if (!playerScripts->consoleCommand(consoleMode, command, selected))
|
||||
MWBase::Environment::get().getWindowManager()->printToConsole("No Lua handlers for console\n",
|
||||
MWBase::WindowManager::sConsoleColor_Error);
|
||||
}
|
||||
|
||||
LuaManager::Action::Action(LuaUtil::LuaState* state)
|
||||
{
|
||||
static const bool luaDebug = Settings::Manager::getBool("lua debug", "Lua");
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include <components/lua_ui/resources.hpp>
|
||||
|
||||
#include <components/misc/color.hpp>
|
||||
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
|
||||
#include "object.hpp"
|
||||
|
@ -60,6 +62,10 @@ namespace MWLua
|
|||
// Used only in Lua bindings
|
||||
void addCustomLocalScript(const MWWorld::Ptr&, int scriptId);
|
||||
void addUIMessage(std::string_view message) { mUIMessages.emplace_back(message); }
|
||||
void addInGameConsoleMessage(const std::string& msg, const Misc::Color& color)
|
||||
{
|
||||
mInGameConsoleMessages.push_back({msg, color});
|
||||
}
|
||||
|
||||
// Some changes to the game world can not be done from the scripting thread (because it runs in parallel with OSG Cull),
|
||||
// so we need to queue it and apply from the main thread. All such changes should be implemented as classes inherited
|
||||
|
@ -94,6 +100,8 @@ namespace MWLua
|
|||
// Drops script cache and reloads all scripts. Calls `onSave` and `onLoad` for every script.
|
||||
void reloadAllScripts() override;
|
||||
|
||||
void handleConsoleCommand(const std::string& consoleMode, const std::string& command, const MWWorld::Ptr& selectedPtr) override;
|
||||
|
||||
// Used to call Lua callbacks from C++
|
||||
void queueCallback(LuaUtil::Callback callback, sol::object arg)
|
||||
{
|
||||
|
@ -172,6 +180,7 @@ namespace MWLua
|
|||
std::vector<std::unique_ptr<Action>> mActionQueue;
|
||||
std::unique_ptr<Action> mTeleportPlayerAction;
|
||||
std::vector<std::string> mUIMessages;
|
||||
std::vector<std::pair<std::string, Misc::Color>> mInGameConsoleMessages;
|
||||
|
||||
LuaUtil::LuaStorage mGlobalStorage{mLua.sol()};
|
||||
LuaUtil::LuaStorage mPlayerStorage{mLua.sol()};
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace MWLua
|
|||
PlayerScripts(LuaUtil::LuaState* lua, const LObject& obj) : LocalScripts(lua, obj, ESM::LuaScriptCfg::sPlayer)
|
||||
{
|
||||
registerEngineHandlers({
|
||||
&mKeyPressHandlers, &mKeyReleaseHandlers,
|
||||
&mConsoleCommandHandlers, &mKeyPressHandlers, &mKeyReleaseHandlers,
|
||||
&mControllerButtonPressHandlers, &mControllerButtonReleaseHandlers,
|
||||
&mActionHandlers, &mInputUpdateHandlers,
|
||||
&mTouchpadPressed, &mTouchpadReleased, &mTouchpadMoved
|
||||
|
@ -59,7 +59,14 @@ namespace MWLua
|
|||
|
||||
void inputUpdate(float dt) { callEngineHandlers(mInputUpdateHandlers, dt); }
|
||||
|
||||
bool consoleCommand(const std::string& consoleMode, const std::string& command, const sol::object& selectedObject)
|
||||
{
|
||||
callEngineHandlers(mConsoleCommandHandlers, consoleMode, command, selectedObject);
|
||||
return !mConsoleCommandHandlers.mList.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
EngineHandlerList mConsoleCommandHandlers{"onConsoleCommand"};
|
||||
EngineHandlerList mKeyPressHandlers{"onKeyPress"};
|
||||
EngineHandlerList mKeyReleaseHandlers{"onKeyRelease"};
|
||||
EngineHandlerList mControllerButtonPressHandlers{"onControllerButtonPress"};
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "context.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
namespace
|
||||
|
@ -213,6 +215,33 @@ namespace MWLua
|
|||
{
|
||||
luaManager->addUIMessage(message);
|
||||
};
|
||||
api["CONSOLE_COLOR"] = LuaUtil::makeReadOnly(context.mLua->tableFromPairs<std::string, Misc::Color>({
|
||||
{"Default", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Default.substr(1))},
|
||||
{"Error", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Error.substr(1))},
|
||||
{"Success", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Success.substr(1))},
|
||||
{"Info", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Info.substr(1))},
|
||||
}));
|
||||
api["printToConsole"] = [luaManager=context.mLuaManager](const std::string& message, const Misc::Color& color)
|
||||
{
|
||||
luaManager->addInGameConsoleMessage(message + "\n", color);
|
||||
};
|
||||
api["setConsoleMode"] = [luaManager=context.mLuaManager](std::string_view mode)
|
||||
{
|
||||
luaManager->addAction(
|
||||
[mode = std::string(mode)]{ MWBase::Environment::get().getWindowManager()->setConsoleMode(mode); });
|
||||
};
|
||||
api["setConsoleSelectedObject"] = [luaManager=context.mLuaManager](const sol::object& obj)
|
||||
{
|
||||
auto* wm = MWBase::Environment::get().getWindowManager();
|
||||
if (obj == sol::nil)
|
||||
luaManager->addAction([wm]{ wm->setConsoleSelectedObject(MWWorld::Ptr()); });
|
||||
else
|
||||
{
|
||||
if (!obj.is<LObject>())
|
||||
throw std::runtime_error("Game object expected");
|
||||
luaManager->addAction([wm, obj=obj.as<LObject>()]{ wm->setConsoleSelectedObject(obj.ptr()); });
|
||||
}
|
||||
};
|
||||
api["content"] = [](const sol::table& table)
|
||||
{
|
||||
return LuaUi::Content(table);
|
||||
|
|
|
@ -70,23 +70,28 @@ Engine handler is a function defined by a script, that can be called by the engi
|
|||
:widths: 20 80
|
||||
|
||||
* - onInputUpdate(dt)
|
||||
- | Called every frame (if the game is not paused) right after processing
|
||||
| user input. Use it only for latency-critical stuff.
|
||||
- | Called every frame (if the game is not paused) right after
|
||||
| processing user input. Use it only for latency-critical stuff.
|
||||
* - onKeyPress(key)
|
||||
- | `Key <openmw_input.html##(KeyboardEvent)>`_ is pressed.
|
||||
| Usage example: ``if key.symbol == 'z' and key.withShift then ...``
|
||||
| Usage example:
|
||||
| ``if key.symbol == 'z' and key.withShift then ...``
|
||||
* - onKeyRelease(key)
|
||||
- | `Key <openmw_input.html##(KeyboardEvent)>`_ is released.
|
||||
| Usage example: ``if key.symbol == 'z' and key.withShift then ...``
|
||||
| Usage example:
|
||||
| ``if key.symbol == 'z' and key.withShift then ...``
|
||||
* - onControllerButtonPress(id)
|
||||
- | A `button <openmw_input.html##(CONTROLLER_BUTTON)>`_ on a game controller is pressed.
|
||||
| Usage example: ``if id == input.CONTROLLER_BUTTON.LeftStick then ...``
|
||||
| Usage example:
|
||||
| ``if id == input.CONTROLLER_BUTTON.LeftStick then ...``
|
||||
* - onControllerButtonRelease(id)
|
||||
- | A `button <openmw_input.html##(CONTROLLER_BUTTON)>`_ on a game controller is released.
|
||||
| Usage example: ``if id == input.CONTROLLER_BUTTON.LeftStick then ...``
|
||||
| Usage example:
|
||||
| ``if id == input.CONTROLLER_BUTTON.LeftStick then ...``
|
||||
* - onInputAction(id)
|
||||
- | `Game control <openmw_input.html##(ACTION)>`_ is pressed.
|
||||
| Usage example: ``if id == input.ACTION.ToggleWeapon then ...``
|
||||
| Usage example:
|
||||
| ``if id == input.ACTION.ToggleWeapon then ...``
|
||||
* - onTouchPress(touchEvent)
|
||||
- | A finger pressed on a touch device.
|
||||
| `Touch event <openmw_input.html##(TouchEvent)>`_.
|
||||
|
@ -95,4 +100,9 @@ Engine handler is a function defined by a script, that can be called by the engi
|
|||
| `Touch event <openmw_input.html##(TouchEvent)>`_.
|
||||
* - onTouchMove(touchEvent)
|
||||
- | A finger moved on a touch device.
|
||||
| `Touch event <openmw_input.html##(TouchEvent)>`_.
|
||||
| `Touch event <openmw_input.html##(TouchEvent)>`_.
|
||||
* - | onConsoleCommand(
|
||||
| mode, command, selectedObject)
|
||||
- | User entered `command` in in-game console. Called if either
|
||||
| `mode` is not default or `command` starts with prefix `lua`.
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ set(LUA_BUILTIN_FILES
|
|||
scripts/omw/camera.lua
|
||||
scripts/omw/head_bobbing.lua
|
||||
scripts/omw/third_person.lua
|
||||
scripts/omw/console/player.lua
|
||||
scripts/omw/console/global.lua
|
||||
scripts/omw/console/local.lua
|
||||
|
||||
l10n/Calendar/en.yaml
|
||||
)
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
PLAYER: scripts/omw/camera.lua
|
||||
NPC,CREATURE: scripts/omw/ai.lua
|
||||
PLAYER: scripts/omw/console/player.lua
|
||||
GLOBAL: scripts/omw/console/global.lua
|
||||
CUSTOM: scripts/omw/console/local.lua
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
-------------------------------------------------------------------------------
|
||||
---
|
||||
-- `openmw_aux.util` defines utility functions that are implemented in Lua rather than in C++.
|
||||
-- Implementation can be found in `resources/vfs/openmw_aux/util.lua`.
|
||||
-- @module util
|
||||
|
@ -6,6 +6,27 @@
|
|||
|
||||
local aux_util = {}
|
||||
|
||||
---
|
||||
-- Works like `tostring` but shows also content of tables.
|
||||
-- @function [parent=#util] deepToString
|
||||
-- @param #any value The value to conver to string
|
||||
-- @param #number maxDepth Max depth of tables unpacking (optional, 2 by default)
|
||||
function aux_util.deepToString(val, level, prefix)
|
||||
level = (level or 1) - 1
|
||||
local ok, iter, t = pcall(function() return pairs(val) end)
|
||||
if level < 0 or not ok then
|
||||
return tostring(val)
|
||||
end
|
||||
local prefix = prefix or ''
|
||||
local newPrefix = prefix .. ' '
|
||||
local strs = {tostring(val) .. ' {\n'}
|
||||
for k, v in iter, t do
|
||||
strs[#strs + 1] = newPrefix .. tostring(k) .. ' = ' .. aux_util.deepToString(v, level, newPrefix) .. ',\n'
|
||||
end
|
||||
strs[#strs + 1] = prefix .. '}'
|
||||
return table.concat(strs)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Finds the nearest object to the given point in the given list.
|
||||
-- Ignores cells, uses only coordinates. Returns the nearest object,
|
||||
|
|
81
files/builtin_scripts/scripts/omw/console/global.lua
Normal file
81
files/builtin_scripts/scripts/omw/console/global.lua
Normal file
|
@ -0,0 +1,81 @@
|
|||
local util = require('openmw.util')
|
||||
|
||||
local player = nil
|
||||
|
||||
local function printToConsole(...)
|
||||
local strs = {}
|
||||
for i = 1, select('#', ...) do
|
||||
strs[i] = tostring(select(i, ...))
|
||||
end
|
||||
player:sendEvent('OMWConsolePrint', table.concat(strs, '\t'))
|
||||
end
|
||||
|
||||
local function printRes(...)
|
||||
if select('#', ...) >= 0 then
|
||||
printToConsole(...)
|
||||
end
|
||||
end
|
||||
|
||||
local env = {
|
||||
I = require('openmw.interfaces'),
|
||||
util = require('openmw.util'),
|
||||
storage = require('openmw.storage'),
|
||||
core = require('openmw.core'),
|
||||
types = require('openmw.types'),
|
||||
async = require('openmw.async'),
|
||||
world = require('openmw.world'),
|
||||
aux_util = require('openmw_aux.util'),
|
||||
view = require('openmw_aux.util').deepToString,
|
||||
print = printToConsole,
|
||||
exit = function() player:sendEvent('OMWConsoleExit') end,
|
||||
help = function() player:sendEvent('OMWConsoleHelp') end,
|
||||
}
|
||||
env._G = env
|
||||
setmetatable(env, {__index = _G, __metatable = false})
|
||||
_G = nil
|
||||
|
||||
local function executeLuaCode(code)
|
||||
local fn
|
||||
local ok, err = pcall(function() fn = util.loadCode('return ' .. code, env) end)
|
||||
if ok then
|
||||
ok, err = pcall(function() printRes(fn()) end)
|
||||
else
|
||||
ok, err = pcall(function() util.loadCode(code, env)() end)
|
||||
end
|
||||
if not ok then
|
||||
player:sendEvent('OMWConsoleError', err)
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
eventHandlers = {
|
||||
OMWConsoleEval = function(data)
|
||||
player = data.player
|
||||
env.selected = data.selected
|
||||
executeLuaCode(data.code)
|
||||
if env.selected ~= data.selected then
|
||||
local ok, err = pcall(function() player:sendEvent('OMWConsoleSetSelected', env.selected) end)
|
||||
if not ok then player:sendEvent('OMWConsoleError', err) end
|
||||
end
|
||||
end,
|
||||
OMWConsoleStartLocal = function(data)
|
||||
player = data.player
|
||||
ok, err = pcall(function()
|
||||
if not data.selected:hasScript('scripts/omw/console/local.lua') then
|
||||
data.selected:addScript('scripts/omw/console/local.lua')
|
||||
end
|
||||
end)
|
||||
if ok then
|
||||
player:sendEvent('OMWConsoleSetContext', data.selected)
|
||||
else
|
||||
player:sendEvent('OMWConsoleError', err)
|
||||
end
|
||||
end,
|
||||
OMWConsoleStopLocal = function(obj)
|
||||
if obj:hasScript('scripts/omw/console/local.lua') then
|
||||
obj:removeScript('scripts/omw/console/local.lua')
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
71
files/builtin_scripts/scripts/omw/console/local.lua
Normal file
71
files/builtin_scripts/scripts/omw/console/local.lua
Normal file
|
@ -0,0 +1,71 @@
|
|||
local util = require('openmw.util')
|
||||
local core = require('openmw.core')
|
||||
local self = require('openmw.self')
|
||||
|
||||
local player = nil
|
||||
|
||||
local function printToConsole(...)
|
||||
local strs = {}
|
||||
for i = 1, select('#', ...) do
|
||||
strs[i] = tostring(select(i, ...))
|
||||
end
|
||||
player:sendEvent('OMWConsolePrint', table.concat(strs, '\t'))
|
||||
end
|
||||
|
||||
local function printRes(...)
|
||||
if select('#', ...) >= 0 then
|
||||
printToConsole(...)
|
||||
end
|
||||
end
|
||||
|
||||
local env = {
|
||||
I = require('openmw.interfaces'),
|
||||
util = require('openmw.util'),
|
||||
storage = require('openmw.storage'),
|
||||
core = require('openmw.core'),
|
||||
types = require('openmw.types'),
|
||||
async = require('openmw.async'),
|
||||
nearby = require('openmw.nearby'),
|
||||
self = require('openmw.self'),
|
||||
aux_util = require('openmw_aux.util'),
|
||||
view = require('openmw_aux.util').deepToString,
|
||||
print = printToConsole,
|
||||
exit = function() player:sendEvent('OMWConsoleExit') end,
|
||||
help = function() player:sendEvent('OMWConsoleHelp') end,
|
||||
}
|
||||
env._G = env
|
||||
setmetatable(env, {__index = _G, __metatable = false})
|
||||
_G = nil
|
||||
|
||||
local function executeLuaCode(code)
|
||||
local fn
|
||||
local ok, err = pcall(function() fn = util.loadCode('return ' .. code, env) end)
|
||||
if ok then
|
||||
ok, err = pcall(function() printRes(fn()) end)
|
||||
else
|
||||
ok, err = pcall(function() util.loadCode(code, env)() end)
|
||||
end
|
||||
if not ok then
|
||||
player:sendEvent('OMWConsoleError', err)
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
eventHandlers = {
|
||||
OMWConsoleEval = function(data)
|
||||
player = data.player
|
||||
env.selected = data.selected
|
||||
executeLuaCode(data.code)
|
||||
if env.selected ~= data.selected then
|
||||
local ok, err = pcall(function() player:sendEvent('OMWConsoleSetSelected', env.selected) end)
|
||||
if not ok then player:sendEvent('OMWConsoleError', err) end
|
||||
end
|
||||
end,
|
||||
},
|
||||
engineHandlers = {
|
||||
onLoad = function()
|
||||
core.sendGlobalEvent('OMWConsoleStopLocal', self.object)
|
||||
end,
|
||||
}
|
||||
}
|
||||
|
158
files/builtin_scripts/scripts/omw/console/player.lua
Normal file
158
files/builtin_scripts/scripts/omw/console/player.lua
Normal file
|
@ -0,0 +1,158 @@
|
|||
local ui = require('openmw.ui')
|
||||
local util = require('openmw.util')
|
||||
local self = require('openmw.self')
|
||||
local core = require('openmw.core')
|
||||
|
||||
local function printHelp()
|
||||
local msg = [[
|
||||
This is the built-in Lua interpreter.
|
||||
help() - print this message
|
||||
exit() - exit Lua mode
|
||||
selected - currently selected object (click on any object to change)
|
||||
view(_G) - print content of the table `_G` (current environment)
|
||||
standard libraries (math, string, etc.) are loaded by default but not visible in `_G`
|
||||
view(types, 2) - print table `types` (i.e. `openmw.types`) and its subtables (2 - traversal depth)]]
|
||||
ui.printToConsole(msg, ui.CONSOLE_COLOR.Info)
|
||||
end
|
||||
|
||||
local function printToConsole(...)
|
||||
local strs = {}
|
||||
for i = 1, select('#', ...) do
|
||||
strs[i] = tostring(select(i, ...))
|
||||
end
|
||||
return ui.printToConsole(table.concat(strs, '\t'), ui.CONSOLE_COLOR.Info)
|
||||
end
|
||||
|
||||
local function printRes(...)
|
||||
if select('#', ...) >= 0 then
|
||||
printToConsole(...)
|
||||
end
|
||||
end
|
||||
|
||||
local currentSelf = nil
|
||||
local currentMode = ''
|
||||
|
||||
local function exitLuaMode()
|
||||
currentSelf = nil
|
||||
currentMode = ''
|
||||
ui.setConsoleMode('')
|
||||
ui.printToConsole('Lua mode OFF', ui.CONSOLE_COLOR.Success)
|
||||
end
|
||||
|
||||
local function setContext(obj)
|
||||
ui.printToConsole('Lua mode ON, use exit() to return, help() for more info', ui.CONSOLE_COLOR.Success)
|
||||
if obj == self then
|
||||
currentMode = 'Lua[Player]'
|
||||
ui.printToConsole('Context: Player', ui.CONSOLE_COLOR.Success)
|
||||
elseif obj then
|
||||
if not obj:isValid() then error('Object not available') end
|
||||
currentMode = 'Lua['..obj.recordId..']'
|
||||
ui.printToConsole('Context: Local['..tostring(obj)..']', ui.CONSOLE_COLOR.Success)
|
||||
else
|
||||
currentMode = 'Lua[Global]'
|
||||
ui.printToConsole('Context: Global', ui.CONSOLE_COLOR.Success)
|
||||
end
|
||||
currentSelf = obj
|
||||
ui.setConsoleMode(currentMode)
|
||||
end
|
||||
|
||||
local function setSelected(obj)
|
||||
local ok, err = pcall(function() ui.setConsoleSelectedObject(obj) end)
|
||||
if ok then
|
||||
ui.printToConsole('Selected object changed', ui.CONSOLE_COLOR.Success)
|
||||
else
|
||||
ui.printToConsole(err, ui.CONSOLE_COLOR.Error)
|
||||
end
|
||||
end
|
||||
|
||||
local env = {
|
||||
I = require('openmw.interfaces'),
|
||||
util = require('openmw.util'),
|
||||
storage = require('openmw.storage'),
|
||||
core = require('openmw.core'),
|
||||
types = require('openmw.types'),
|
||||
async = require('openmw.async'),
|
||||
nearby = require('openmw.nearby'),
|
||||
self = require('openmw.self'),
|
||||
input = require('openmw.input'),
|
||||
ui = require('openmw.ui'),
|
||||
camera = require('openmw.camera'),
|
||||
aux_util = require('openmw_aux.util'),
|
||||
view = require('openmw_aux.util').deepToString,
|
||||
print = printToConsole,
|
||||
exit = exitLuaMode,
|
||||
help = printHelp,
|
||||
}
|
||||
env._G = env
|
||||
setmetatable(env, {__index = _G, __metatable = false})
|
||||
_G = nil
|
||||
|
||||
local function executeLuaCode(code)
|
||||
local fn
|
||||
local ok, err = pcall(function() fn = util.loadCode('return ' .. code, env) end)
|
||||
if ok then
|
||||
ok, err = pcall(function() printRes(fn()) end)
|
||||
else
|
||||
ok, err = pcall(function() util.loadCode(code, env)() end)
|
||||
end
|
||||
if not ok then
|
||||
ui.printToConsole(err, ui.CONSOLE_COLOR.Error)
|
||||
end
|
||||
end
|
||||
|
||||
local function onConsoleCommand(mode, cmd, selectedObject)
|
||||
env.selected = selectedObject
|
||||
if mode == '' then
|
||||
cmd, arg = cmd:lower():match('(%w+) *(%w*)')
|
||||
if cmd == 'lua' then
|
||||
if arg == 'player' then
|
||||
cmd = 'luap'
|
||||
elseif arg == 'global' then
|
||||
cmd = 'luag'
|
||||
elseif arg == 'selected' then
|
||||
cmd = 'luas'
|
||||
else
|
||||
local msg = [[
|
||||
Usage: 'lua player' or 'luap' - enter player context
|
||||
'lua global' or 'luag' - enter global context
|
||||
'lua selected' or 'luas' - enter local context on the selected object]]
|
||||
ui.printToConsole(msg, ui.CONSOLE_COLOR.Info)
|
||||
end
|
||||
end
|
||||
if cmd == 'luap' or (cmd == 'luas' and selectedObject == self.object) then
|
||||
setContext(self)
|
||||
elseif cmd == 'luag' then
|
||||
setContext()
|
||||
elseif cmd == 'luas' then
|
||||
if selectedObject then
|
||||
core.sendGlobalEvent('OMWConsoleStartLocal', {player=self.object, selected=selectedObject})
|
||||
else
|
||||
ui.printToConsole('No selected object', ui.CONSOLE_COLOR.Error)
|
||||
end
|
||||
end
|
||||
elseif mode == currentMode then
|
||||
if cmd == 'exit()' then
|
||||
exitLuaMode()
|
||||
elseif currentSelf == self then
|
||||
executeLuaCode(cmd)
|
||||
if env.selected ~= selectedObject then setSelected(env.selected) end
|
||||
elseif currentSelf then
|
||||
currentSelf:sendEvent('OMWConsoleEval', {player=self.object, code=cmd, selected=selectedObject})
|
||||
else
|
||||
core.sendGlobalEvent('OMWConsoleEval', {player=self.object, code=cmd, selected=selectedObject})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
engineHandlers = {onConsoleCommand = onConsoleCommand},
|
||||
eventHandlers = {
|
||||
OMWConsolePrint = function(msg) ui.printToConsole(tostring(msg), ui.CONSOLE_COLOR.Info) end,
|
||||
OMWConsoleError = function(msg) ui.printToConsole(tostring(msg), ui.CONSOLE_COLOR.Error) end,
|
||||
OMWConsoleSetContext = setContext,
|
||||
OMWConsoleSetSelected = setSelected,
|
||||
OMWConsoleExit = exitLuaMode,
|
||||
OMWConsoleHelp = printHelp,
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,37 @@
|
|||
-- @function [parent=#ui] showMessage
|
||||
-- @param #string msg
|
||||
|
||||
---
|
||||
-- Predefined colors for console output
|
||||
-- @field [parent=#ui] #CONSOLE_COLOR CONSOLE_COLOR
|
||||
|
||||
---
|
||||
-- Predefined colors for console output
|
||||
-- @type CONSOLE_COLOR
|
||||
-- @field openmw.util#Color Default
|
||||
-- @field openmw.util#Color Error
|
||||
-- @field openmw.util#Color Success
|
||||
-- @field openmw.util#Color Info
|
||||
|
||||
---
|
||||
-- Print to the in-game console.
|
||||
-- @function [parent=#ui] printToConsole
|
||||
-- @param #string msg
|
||||
-- @param openmw.util#Color color
|
||||
|
||||
---
|
||||
-- Set mode of the in-game console.
|
||||
-- The mode can be any string, by default is empty.
|
||||
-- If not empty, then the console doesn't handle mwscript commands and
|
||||
-- instead passes user input to Lua scripts via `onConsoleCommand` engine handler.
|
||||
-- @function [parent=#ui] setConsoleMode
|
||||
-- @param #string mode
|
||||
|
||||
---
|
||||
-- Set selected object for console.
|
||||
-- @function [parent=#ui] setConsoleSelectedObject
|
||||
-- @param openmw.core#GameObject obj
|
||||
|
||||
---
|
||||
-- Returns the size of the OpenMW window in pixels as a 2D vector.
|
||||
-- @function [parent=#ui] screenSize
|
||||
|
|
Loading…
Reference in a new issue