Merge branch 'playercontrols' into 'master'

Move some of player controls logic from C++ to Lua

See merge request OpenMW/openmw!2541
iwyu_full
psi29a 2 years ago
commit d5f5e53714

@ -84,7 +84,6 @@ bool Launcher::AdvancedPage::loadSettings()
{
// Game mechanics
{
loadSettingBool(toggleSneakCheckBox, "toggle sneak", "Input");
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
@ -253,7 +252,6 @@ void Launcher::AdvancedPage::saveSettings()
{
// Game mechanics
{
saveSettingBool(toggleSneakCheckBox, "toggle sneak", "Input");
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");

@ -46,7 +46,6 @@ namespace MWBase
virtual void setDragDrop(bool dragDrop) = 0;
virtual void setGamepadGuiCursorEnabled(bool enabled) = 0;
virtual void setAttemptJump(bool jumping) = 0;
virtual void toggleControlSwitch(std::string_view sw, bool value) = 0;
virtual bool getControlSwitch(std::string_view sw) = 0;

@ -68,6 +68,7 @@ namespace MWBase
bool mJump = false;
bool mRun = false;
bool mSneak = false;
float mMovement = 0;
float mSideMovement = 0;
float mPitchChange = 0;

@ -155,7 +155,6 @@ namespace MWBase
virtual MWGui::CountDialog* getCountDialog() = 0;
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;
virtual MWGui::TradeWindow* getTradeWindow() = 0;
virtual const std::vector<std::unique_ptr<MWGui::MessageBox>>& getActiveMessageBoxes() const = 0;
virtual MWGui::PostProcessorHud* getPostProcessorHud() = 0;
/// Make the player use an item, while updating GUI state accordingly

@ -53,6 +53,7 @@
#include <components/lua_ui/util.hpp>
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/luamanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwbase/world.hpp"
@ -794,11 +795,6 @@ namespace MWGui
mMessageBoxManager->removeStaticMessageBox();
}
const std::vector<std::unique_ptr<MWGui::MessageBox>>& WindowManager::getActiveMessageBoxes() const
{
return mMessageBoxManager->getActiveMessageBoxes();
}
int WindowManager::readPressedButton()
{
return mMessageBoxManager->readPressedButton();
@ -904,7 +900,8 @@ namespace MWGui
// We should display message about crime only once per frame, even if there are several crimes.
// Otherwise we will get message spam when stealing several items via Take All button.
const MWWorld::Ptr player = MWMechanics::getPlayer();
int currentBounty = player.getClass().getNpcStats(player).getBounty();
const MWWorld::Class& playerCls = player.getClass();
int currentBounty = playerCls.getNpcStats(player).getBounty();
if (currentBounty != mPlayerBounty)
{
if (mPlayerBounty >= 0 && currentBounty > mPlayerBounty)
@ -913,6 +910,26 @@ namespace MWGui
mPlayerBounty = currentBounty;
}
MWBase::LuaManager::ActorControls* playerControls
= MWBase::Environment::get().getLuaManager()->getActorControls(player);
bool triedToMove = playerControls
&& (playerControls->mMovement != 0 || playerControls->mSideMovement != 0 || playerControls->mJump);
if (triedToMove && playerCls.getEncumbrance(player) > playerCls.getCapacity(player))
{
const auto& msgboxs = mMessageBoxManager->getActiveMessageBoxes();
auto it
= std::find_if(msgboxs.begin(), msgboxs.end(), [](const std::unique_ptr<MWGui::MessageBox>& msgbox) {
return (msgbox->getMessage() == "#{sNotifyMessage59}");
});
// if an overencumbered messagebox is already present, reset its expiry timer,
// otherwise create a new one.
if (it != msgboxs.end())
(*it)->mCurrentTime = 0;
else
messageBox("#{sNotifyMessage59}");
}
mDragAndDrop->onFrame();
mHud->onFrame(frameDuration);

@ -179,7 +179,6 @@ namespace MWGui
MWGui::CountDialog* getCountDialog() override;
MWGui::ConfirmationDialog* getConfirmationDialog() override;
MWGui::TradeWindow* getTradeWindow() override;
const std::vector<std::unique_ptr<MWGui::MessageBox>>& getActiveMessageBoxes() const override;
MWGui::PostProcessorHud* getPostProcessorHud() override;
/// Make the player use an item, while updating GUI state accordingly

@ -36,99 +36,12 @@ namespace MWInput
, mViewer(viewer)
, mScreenCaptureHandler(screenCaptureHandler)
, mScreenCaptureOperation(screenCaptureOperation)
, mAlwaysRunActive(Settings::Manager::getBool("always run", "Input"))
, mAttemptJump(false)
, mTimeIdle(0.f)
{
}
void ActionManager::update(float dt, bool triedToMove)
void ActionManager::update(float dt)
{
// Disable movement in Gui mode
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|| MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
{
mAttemptJump = false;
return;
}
// Configure player movement according to keyboard input. Actual movement will
// be done in the physics system.
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
{
bool alwaysRunAllowed = false;
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
if (mBindingsManager->actionIsActive(A_MoveLeft) != mBindingsManager->actionIsActive(A_MoveRight))
{
alwaysRunAllowed = true;
triedToMove = true;
player.setLeftRight(mBindingsManager->actionIsActive(A_MoveRight) ? 1 : -1);
}
if (mBindingsManager->actionIsActive(A_MoveForward) != mBindingsManager->actionIsActive(A_MoveBackward))
{
alwaysRunAllowed = true;
triedToMove = true;
player.setAutoMove(false);
player.setForwardBackward(mBindingsManager->actionIsActive(A_MoveForward) ? 1 : -1);
}
if (player.getAutoMove())
{
alwaysRunAllowed = true;
triedToMove = true;
player.setForwardBackward(1);
}
if (mAttemptJump && MWBase::Environment::get().getInputManager()->getControlSwitch("playerjumping"))
{
player.setUpDown(1);
triedToMove = true;
}
// if player tried to start moving, but can't (due to being overencumbered), display a notification.
if (triedToMove)
{
MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr();
if (playerPtr.getClass().getEncumbrance(playerPtr) > playerPtr.getClass().getCapacity(playerPtr))
{
player.setAutoMove(false);
const auto& msgboxs = MWBase::Environment::get().getWindowManager()->getActiveMessageBoxes();
auto it = std::find_if(
msgboxs.begin(), msgboxs.end(), [](const std::unique_ptr<MWGui::MessageBox>& msgbox) {
return (msgbox->getMessage() == "#{sNotifyMessage59}");
});
// if an overencumbered messagebox is already present, reset its expiry timer, otherwise create new
// one.
if (it != msgboxs.end())
(*it)->mCurrentTime = 0;
else
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage59}");
}
}
if (triedToMove)
MWBase::Environment::get().getInputManager()->resetIdleTime();
static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input");
if (!isToggleSneak)
{
if (!MWBase::Environment::get().getInputManager()->joystickLastUsed())
player.setSneak(mBindingsManager->actionIsActive(A_Sneak));
}
float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight);
float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward);
bool isRunning = osg::Vec2f(xAxis * 2 - 1, yAxis * 2 - 1).length2() > 0.25f;
if ((mAlwaysRunActive && alwaysRunAllowed) || isRunning)
player.setRunState(!mBindingsManager->actionIsActive(A_Run));
else
player.setRunState(mBindingsManager->actionIsActive(A_Run));
}
if (mBindingsManager->actionIsActive(A_MoveForward) || mBindingsManager->actionIsActive(A_MoveBackward)
|| mBindingsManager->actionIsActive(A_MoveLeft) || mBindingsManager->actionIsActive(A_MoveRight)
|| mBindingsManager->actionIsActive(A_Jump) || mBindingsManager->actionIsActive(A_Sneak)
@ -139,8 +52,6 @@ namespace MWInput
}
else
mTimeIdle += dt;
mAttemptJump = false;
}
void ActionManager::resetIdleTime()
@ -181,21 +92,9 @@ namespace MWInput
case A_Journal:
toggleJournal();
break;
case A_AutoMove:
toggleAutoMove();
break;
case A_AlwaysRun:
toggleWalking();
break;
case A_ToggleWeapon:
toggleWeapon();
break;
case A_Rest:
rest();
break;
case A_ToggleSpell:
toggleSpell();
break;
case A_QuickKey1:
quickKey(1);
break;
@ -260,13 +159,6 @@ namespace MWInput
if (checkAllowedToUseItems() && windowManager->isAllowed(MWGui::GW_Inventory))
MWBase::Environment::get().getWindowManager()->cycleWeapon(true);
break;
case A_Sneak:
static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input");
if (isToggleSneak)
{
toggleSneaking();
}
break;
}
}
@ -334,36 +226,6 @@ namespace MWInput
}
}
void ActionManager::toggleSpell()
{
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
return;
// Not allowed before the magic window is accessible
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playermagic")
|| !MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
return;
if (!checkAllowedToUseItems())
return;
// Not allowed if no spell selected
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
MWWorld::InventoryStore& inventory = player.getPlayer().getClass().getInventoryStore(player.getPlayer());
if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty()
&& inventory.getSelectedEnchantItem() == inventory.end())
return;
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player.getPlayer()))
return;
MWMechanics::DrawState state = player.getDrawState();
if (state == MWMechanics::DrawState::Weapon || state == MWMechanics::DrawState::Nothing)
player.setDrawState(MWMechanics::DrawState::Spell);
else
player.setDrawState(MWMechanics::DrawState::Nothing);
}
void ActionManager::quickLoad()
{
if (!MyGUI::InputManager::getInstance().isModalAny())
@ -376,32 +238,6 @@ namespace MWInput
MWBase::Environment::get().getStateManager()->quickSave();
}
void ActionManager::toggleWeapon()
{
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
return;
// Not allowed before the inventory window is accessible
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playerfighting")
|| !MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
return;
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
// We want to interrupt animation only if attack is preparing, but still is not triggered
// Otherwise we will get a "speedshooting" exploit, when player can skip reload animation by hitting "Toggle
// Weapon" key twice
if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(player.getPlayer()))
player.setAttackingOrSpell(false);
else if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player.getPlayer()))
return;
MWMechanics::DrawState state = player.getDrawState();
if (state == MWMechanics::DrawState::Spell || state == MWMechanics::DrawState::Nothing)
player.setDrawState(MWMechanics::DrawState::Weapon);
else
player.setDrawState(MWMechanics::DrawState::Nothing);
}
void ActionManager::rest()
{
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
@ -513,43 +349,12 @@ namespace MWInput
}
}
void ActionManager::toggleAutoMove()
{
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
return;
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
{
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
player.setAutoMove(!player.getAutoMove());
}
}
void ActionManager::toggleWalking()
{
if (MWBase::Environment::get().getWindowManager()->isGuiMode() || SDL_IsTextInputActive())
return;
mAlwaysRunActive = !mAlwaysRunActive;
Settings::Manager::setBool("always run", "Input", mAlwaysRunActive);
}
bool ActionManager::isSneaking() const
{
const MWBase::Environment& env = MWBase::Environment::get();
return env.getMechanicsManager()->isSneaking(env.getWorld()->getPlayer().getPlayer());
}
void ActionManager::toggleSneaking()
{
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
return;
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
return;
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
player.setSneak(!isSneaking());
}
void ActionManager::handleGuiArrowKey(int action)
{
bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed();

@ -21,23 +21,18 @@ namespace MWInput
osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
osg::ref_ptr<osgViewer::Viewer> viewer, osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler);
void update(float dt, bool triedToMove);
void update(float dt);
void executeAction(int action);
bool checkAllowedToUseItems() const;
void toggleMainMenu();
void toggleSpell();
void toggleWeapon();
void toggleInventory();
void toggleConsole();
void screenshot();
void toggleJournal();
void activate();
void toggleWalking();
void toggleSneaking();
void toggleAutoMove();
void rest();
void quickLoad();
void quickSave();
@ -48,11 +43,8 @@ namespace MWInput
void resetIdleTime();
float getIdleTime() const { return mTimeIdle; }
bool isAlwaysRunActive() const { return mAlwaysRunActive; }
bool isSneaking() const;
void setAttemptJump(bool enabled) { mAttemptJump = enabled; }
private:
void handleGuiArrowKey(int action);
@ -61,9 +53,6 @@ namespace MWInput
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
osgViewer::ScreenCaptureHandler::CaptureOperation* mScreenCaptureOperation;
bool mAlwaysRunActive;
bool mAttemptJump;
float mTimeIdle;
};
}

@ -681,65 +681,25 @@ namespace MWInput
void BindingsManager::actionValueChanged(int action, float currentValue, float previousValue)
{
MWBase::Environment::get().getInputManager()->resetIdleTime();
auto manager = MWBase::Environment::get().getInputManager();
manager->resetIdleTime();
if (mDragDrop && action != A_GameMenu && action != A_Inventory)
return;
if ((previousValue == 1 || previousValue == 0) && (currentValue == 1 || currentValue == 0))
if (manager->joystickLastUsed() && manager->getControlSwitch("playercontrols"))
{
// Is a normal button press, so don't change it at all
}
// Otherwise only trigger button presses as they go through specific points
else if (previousValue >= 0.8 && currentValue < 0.8)
{
currentValue = 0.0;
previousValue = 1.0;
}
else if (previousValue <= 0.6 && currentValue > 0.6)
{
currentValue = 1.0;
previousValue = 0.0;
}
else
{
// If it's not switching between those values, ignore the channel change.
return;
}
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
{
bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed();
if (action == A_Use)
{
if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon))
action = A_CycleWeaponRight;
else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell))
action = A_CycleSpellRight;
else
{
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
MWMechanics::DrawState state = player.getDrawState();
player.setAttackingOrSpell(currentValue != 0 && state != MWMechanics::DrawState::Nothing);
}
}
else if (action == A_Jump)
{
if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon))
action = A_CycleWeaponLeft;
else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell))
action = A_CycleSpellLeft;
else
MWBase::Environment::get().getInputManager()->setAttemptJump(
currentValue == 1.0 && previousValue == 0.0);
}
if (action == A_Use && actionIsActive(A_ToggleWeapon))
action = A_CycleWeaponRight;
else if (action == A_Use && actionIsActive(A_ToggleSpell))
action = A_CycleSpellRight;
else if (action == A_Jump && actionIsActive(A_ToggleWeapon))
action = A_CycleWeaponLeft;
else if (action == A_Jump && actionIsActive(A_ToggleSpell))
action = A_CycleSpellLeft;
}
if (currentValue == 1)
MWBase::Environment::get().getInputManager()->executeAction(action);
if (previousValue <= 0.6 && currentValue > 0.6)
manager->executeAction(action);
}
}

@ -25,20 +25,16 @@
namespace MWInput
{
ControllerManager::ControllerManager(BindingsManager* bindingsManager, ActionManager* actionManager,
MouseManager* mouseManager, const std::filesystem::path& userControllerBindingsFile,
const std::filesystem::path& controllerBindingsFile)
ControllerManager::ControllerManager(BindingsManager* bindingsManager, MouseManager* mouseManager,
const std::filesystem::path& userControllerBindingsFile, const std::filesystem::path& controllerBindingsFile)
: mBindingsManager(bindingsManager)
, mActionManager(actionManager)
, mMouseManager(mouseManager)
, mJoystickEnabled(Settings::Manager::getBool("enable controller", "Input"))
, mGyroAvailable(false)
, mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input"))
, mSneakToggleShortcutTimer(0.f)
, mGamepadGuiCursorEnabled(true)
, mGuiCursorEnabled(true)
, mJoystickLastUsed(false)
, mSneakGamepadShortcut(false)
{
if (!controllerBindingsFile.empty())
{
@ -82,7 +78,7 @@ namespace MWInput
}
}
bool ControllerManager::update(float dt)
void ControllerManager::update(float dt)
{
if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled))
{
@ -108,77 +104,18 @@ namespace MWInput
}
}
// Disable movement in Gui mode
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|| MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
{
return false;
}
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
bool triedToMove = false;
// Configure player movement according to controller input. Actual movement will
// be done in the physics system.
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
if (!MWBase::Environment::get().getWindowManager()->isGuiMode()
&& MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running
&& MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
{
float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight);
float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward);
if (xAxis != 0.5)
{
triedToMove = true;
player.setLeftRight((xAxis - 0.5f) * 2);
}
if (yAxis != 0.5)
{
triedToMove = true;
player.setAutoMove(false);
player.setForwardBackward((0.5f - yAxis) * 2);
}
if (triedToMove)
if (xAxis != 0.5 || yAxis != 0.5)
{
mJoystickLastUsed = true;
MWBase::Environment::get().getInputManager()->resetIdleTime();
}
static const bool isToggleSneak = Settings::Manager::getBool("toggle sneak", "Input");
if (!isToggleSneak)
{
if (mJoystickLastUsed)
{
if (mBindingsManager->actionIsActive(A_Sneak))
{
if (mSneakToggleShortcutTimer) // New Sneak Button Press
{
if (mSneakToggleShortcutTimer <= 0.3f)
{
mSneakGamepadShortcut = true;
mActionManager->toggleSneaking();
}
else
mSneakGamepadShortcut = false;
}
if (!mActionManager->isSneaking())
mActionManager->toggleSneaking();
mSneakToggleShortcutTimer = 0.f;
}
else
{
if (!mSneakGamepadShortcut && mActionManager->isSneaking())
mActionManager->toggleSneaking();
if (mSneakToggleShortcutTimer <= 0.3f)
mSneakToggleShortcutTimer += dt;
}
}
else
player.setSneak(mBindingsManager->actionIsActive(A_Sneak));
}
}
return triedToMove;
}
void ControllerManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent& arg)

@ -9,20 +9,19 @@
namespace MWInput
{
class ActionManager;
class BindingsManager;
class MouseManager;
class ControllerManager : public SDLUtil::ControllerListener
{
public:
ControllerManager(BindingsManager* bindingsManager, ActionManager* actionManager, MouseManager* mouseManager,
ControllerManager(BindingsManager* bindingsManager, MouseManager* mouseManager,
const std::filesystem::path& userControllerBindingsFile,
const std::filesystem::path& controllerBindingsFile);
virtual ~ControllerManager() = default;
bool update(float dt);
void update(float dt);
void buttonPressed(int deviceID, const SDL_ControllerButtonEvent& arg) override;
void buttonReleased(int deviceID, const SDL_ControllerButtonEvent& arg) override;
@ -58,17 +57,14 @@ namespace MWInput
void enableGyroSensor();
BindingsManager* mBindingsManager;
ActionManager* mActionManager;
MouseManager* mMouseManager;
bool mJoystickEnabled;
bool mGyroAvailable;
float mGamepadCursorSpeed;
float mSneakToggleShortcutTimer;
bool mGamepadGuiCursorEnabled;
bool mGuiCursorEnabled;
bool mJoystickLastUsed;
bool mSneakGamepadShortcut;
};
}
#endif

@ -39,24 +39,10 @@ namespace MWInput
void ControlSwitch::set(std::string_view key, bool value)
{
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
/// \note 7 switches at all, if-else is relevant
if (key == "playercontrols" && !value)
{
player.setLeftRight(0);
player.setForwardBackward(0);
player.setAutoMove(false);
player.setUpDown(0);
}
else if (key == "playerjumping" && !value)
{
/// \fixme maybe crouching at this time
player.setUpDown(0);
}
else if (key == "playerlooking" && !value)
if (key == "playerlooking" && !value)
{
MWBase::Environment::get().getWorld()->rotateObject(player.getPlayer(), osg::Vec3f());
auto world = MWBase::Environment::get().getWorld();
world->rotateObject(world->getPlayerPtr(), osg::Vec3f());
}
auto it = mSwitches.find(key);
if (it == mSwitches.end())

@ -37,8 +37,8 @@ namespace MWInput
mBindingsManager.get(), screenCaptureOperation, viewer, screenCaptureHandler))
, mKeyboardManager(std::make_unique<KeyboardManager>(mBindingsManager.get()))
, mMouseManager(std::make_unique<MouseManager>(mBindingsManager.get(), mInputWrapper.get(), window))
, mControllerManager(std::make_unique<ControllerManager>(mBindingsManager.get(), mActionManager.get(),
mMouseManager.get(), userControllerBindingsFile, controllerBindingsFile))
, mControllerManager(std::make_unique<ControllerManager>(
mBindingsManager.get(), mMouseManager.get(), userControllerBindingsFile, controllerBindingsFile))
, mSensorManager(std::make_unique<SensorManager>())
, mGyroManager(std::make_unique<GyroManager>())
{
@ -57,11 +57,6 @@ namespace MWInput
InputManager::~InputManager() {}
void InputManager::setAttemptJump(bool jumping)
{
mActionManager->setAttemptJump(jumping);
}
void InputManager::update(float dt, bool disableControls, bool disableEvents)
{
mControlsDisabled = disableControls;
@ -79,10 +74,10 @@ namespace MWInput
mMouseManager->updateCursorMode();
bool controllerMove = mControllerManager->update(dt);
mControllerManager->update(dt);
mMouseManager->update(dt);
mSensorManager->update(dt);
mActionManager->update(dt, controllerMove);
mActionManager->update(dt);
if (mGyroManager->isEnabled())
{

@ -68,7 +68,6 @@ namespace MWInput
void setDragDrop(bool dragDrop) override;
void setGamepadGuiCursorEnabled(bool enabled) override;
void setAttemptJump(bool jumping) override;
void toggleControlSwitch(std::string_view sw, bool value) override;
bool getControlSwitch(std::string_view sw) override;

@ -48,6 +48,7 @@ namespace MWLua
controls["pitchChange"] = CONTROL(float, mPitchChange);
controls["yawChange"] = CONTROL(float, mYawChange);
controls["run"] = CONTROL(bool, mRun);
controls["sneak"] = CONTROL(bool, mSneak);
controls["jump"] = CONTROL(bool, mJump);
controls["use"] = CONTROL(int, mUse);
#undef CONTROL

@ -48,7 +48,7 @@ namespace MWLua
{
auto* lua = context.mLua;
sol::table api(lua->sol(), sol::create);
api["API_REVISION"] = 30;
api["API_REVISION"] = 31;
api["quit"] = [lua]() {
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
MWBase::Environment::get().getStateManager()->requestQuit();

@ -3,6 +3,7 @@
#include <components/detournavigator/agentbounds.hpp>
#include <components/lua/luastate.hpp>
#include <apps/openmw/mwbase/mechanicsmanager.hpp>
#include <apps/openmw/mwmechanics/creaturestats.hpp>
#include <apps/openmw/mwmechanics/drawstate.hpp>
#include <apps/openmw/mwworld/class.hpp>
@ -150,6 +151,38 @@ namespace MWLua
else
throw std::runtime_error("Actor expected");
};
actor["setStance"] = [](const SelfObject& self, int stance) {
const MWWorld::Class& cls = self.ptr().getClass();
if (!cls.isActor())
throw std::runtime_error("Actor expected");
auto& stats = cls.getCreatureStats(self.ptr());
if (stance != static_cast<int>(MWMechanics::DrawState::Nothing)
&& stance != static_cast<int>(MWMechanics::DrawState::Weapon)
&& stance != static_cast<int>(MWMechanics::DrawState::Spell))
{
throw std::runtime_error("Incorrect stance");
}
MWMechanics::DrawState newDrawState = static_cast<MWMechanics::DrawState>(stance);
if (stats.getDrawState() == newDrawState)
return;
if (newDrawState == MWMechanics::DrawState::Spell && stats.getSpells().getSelectedSpell().empty())
{
if (!cls.hasInventoryStore(self.ptr()))
return; // No selected spell and no items; can't use magic stance.
MWWorld::InventoryStore& store = cls.getInventoryStore(self.ptr());
if (store.getSelectedEnchantItem() == store.end())
return; // No selected spell and no selected enchanted item; can't use magic stance.
}
MWBase::MechanicsManager* mechanics = MWBase::Environment::get().getMechanicsManager();
// We want to interrupt animation only if attack is preparing, but still is not triggered.
// Otherwise we will get a "speedshooting" exploit, when player can skip reload animation by hitting "Toggle
// Weapon" key twice.
if (mechanics->isAttackPreparing(self.ptr()))
stats.setAttackingOrSpell(false); // interrupt attack
else if (mechanics->isAttackingOrSpell(self.ptr()))
return; // can't be interrupted; ignore setStance
stats.setDrawState(newDrawState);
};
actor["canMove"] = [](const Object& o) {
const MWWorld::Class& cls = o.ptr().getClass();

@ -5,6 +5,8 @@
#include <apps/openmw/mwbase/environment.hpp>
#include <apps/openmw/mwbase/world.hpp>
#include <apps/openmw/mwmechanics/npcstats.hpp>
#include <apps/openmw/mwworld/class.hpp>
#include <apps/openmw/mwworld/esmstore.hpp>
#include "../stats.hpp"
@ -43,5 +45,14 @@ namespace MWLua
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHair.getRefIdString(); });
record["head"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHead.getRefIdString(); });
// This function is game-specific, in future we should replace it with something more universal.
npc["isWerewolf"] = [](const Object& o) {
const MWWorld::Class& cls = o.ptr().getClass();
if (cls.isNpc())
return cls.getNpcStats(o.ptr()).isWerewolf();
else
throw std::runtime_error("NPC or Player expected");
};
}
}

@ -352,6 +352,7 @@ namespace MWMechanics
const float rotationZ = mov.mRotation[2];
const bool jump = mov.mPosition[2] == 1;
const bool runFlag = stats.getMovementFlag(MWMechanics::CreatureStats::Flag_Run);
const bool sneakFlag = stats.getMovementFlag(MWMechanics::CreatureStats::Flag_Sneak);
const bool attackingOrSpell = stats.getAttackingOrSpell();
if (controls.mChanged)
{
@ -363,16 +364,24 @@ namespace MWMechanics
mov.mRotation[2] = controls.mYawChange;
mov.mSpeedFactor = osg::Vec2(controls.mMovement, controls.mSideMovement).length();
stats.setMovementFlag(MWMechanics::CreatureStats::Flag_Run, controls.mRun);
stats.setMovementFlag(MWMechanics::CreatureStats::Flag_Sneak, controls.mSneak);
stats.setAttackingOrSpell((controls.mUse & 1) == 1);
controls.mChanged = false;
}
controls.mSideMovement = movement.x();
controls.mMovement = movement.y();
// For the player we don't need to copy these values to Lua because mwinput doesn't change them.
// All handling of these player controls was moved from C++ to a built-in Lua script.
if (!isPlayer)
{
controls.mSideMovement = movement.x();
controls.mMovement = movement.y();
controls.mJump = jump;
controls.mRun = runFlag;
controls.mSneak = sneakFlag;
controls.mUse = attackingOrSpell ? controls.mUse | 1 : controls.mUse & ~1;
}
// For the player these controls are still handled by mwinput, so we need to update the values.
controls.mPitchChange = rotationX;
controls.mYawChange = rotationZ;
controls.mJump = jump;
controls.mRun = runFlag;
controls.mUse = attackingOrSpell ? controls.mUse | 1 : controls.mUse & ~1;
}
}

@ -1907,13 +1907,6 @@ namespace MWMechanics
movementSettings.mSpeedFactor = std::min(vec.length(), 1.f);
vec.normalize();
// TODO: Move this check to mwinput.
// Joystick analogue movement.
// Due to the half way split between walking/running, we multiply speed by 2 while walking, unless a
// keyboard was used.
if (isPlayer && !isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f)
movementSettings.mSpeedFactor *= 2.f;
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
if (smoothMovement)
{

@ -40,8 +40,6 @@ namespace MWWorld
, mLastKnownExteriorPosition(0, 0, 0)
, mMarkedPosition(ESM::Position())
, mMarkedCell(nullptr)
, mAutoMove(false)
, mForwardBackward(0)
, mTeleported(false)
, mCurrentCrimeId(-1)
, mPaidCrimeId(-1)
@ -163,61 +161,6 @@ namespace MWWorld
ptr.getClass().getNpcStats(ptr).setDrawState(state);
}
bool Player::getAutoMove() const
{
return mAutoMove;
}
void Player::setAutoMove(bool enable)
{
MWWorld::Ptr ptr = getPlayer();
mAutoMove = enable;
int value = mForwardBackward;
if (mAutoMove)
value = 1;
ptr.getClass().getMovementSettings(ptr).mPosition[1] = value;
}
void Player::setLeftRight(float value)
{
MWWorld::Ptr ptr = getPlayer();
ptr.getClass().getMovementSettings(ptr).mPosition[0] = value;
}
void Player::setForwardBackward(float value)
{
MWWorld::Ptr ptr = getPlayer();
mForwardBackward = value;
if (mAutoMove)
value = 1;
ptr.getClass().getMovementSettings(ptr).mPosition[1] = value;
}
void Player::setUpDown(int value)
{
MWWorld::Ptr ptr = getPlayer();
ptr.getClass().getMovementSettings(ptr).mPosition[2] = static_cast<float>(value);
}
void Player::setRunState(bool run)
{
MWWorld::Ptr ptr = getPlayer();
ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, run);
}
void Player::setSneak(bool sneak)
{
MWWorld::Ptr ptr = getPlayer();
ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Sneak, sneak);
}
void Player::yaw(float yaw)
{
MWWorld::Ptr ptr = getPlayer();
@ -272,11 +215,6 @@ namespace MWWorld
mTeleported = teleported;
}
void Player::setAttackingOrSpell(bool attackingOrSpell)
{
getPlayer().getClass().getCreatureStats(getPlayer()).setAttackingOrSpell(attackingOrSpell);
}
void Player::setJumping(bool jumping)
{
mJumping = jumping;
@ -315,8 +253,6 @@ namespace MWWorld
mCellStore = nullptr;
mSign = ESM::RefId::sEmpty;
mMarkedCell = nullptr;
mAutoMove = false;
mForwardBackward = 0;
mTeleported = false;
mJumping = false;
mCurrentCrimeId = -1;
@ -475,7 +411,6 @@ namespace MWWorld
mMarkedCell = nullptr;
}
mForwardBackward = 0;
mTeleported = false;
mPreviousItems = player.mPreviousItems;

@ -41,8 +41,6 @@ namespace MWWorld
// If no position was marked, this is nullptr
CellStore* mMarkedCell;
bool mAutoMove;
float mForwardBackward;
bool mTeleported;
int mCurrentCrimeId; // the id assigned witnesses
@ -90,17 +88,6 @@ namespace MWWorld
/// Activate the object under the crosshair, if any
void activate();
bool getAutoMove() const;
void setAutoMove(bool enable);
void setLeftRight(float value);
void setForwardBackward(float value);
void setUpDown(int value);
void setRunState(bool run);
void setSneak(bool sneak);
void yaw(float yaw);
void pitch(float pitch);
void roll(float roll);
@ -108,8 +95,6 @@ namespace MWWorld
bool wasTeleported() const;
void setTeleported(bool teleported);
void setAttackingOrSpell(bool attackingOrSpell);
void setJumping(bool jumping);
bool getJumping() const;

@ -3651,7 +3651,7 @@ namespace MWWorld
{
if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(player))
{
mPlayer->setAttackingOrSpell(false);
player.getClass().getCreatureStats(player).setAttackingOrSpell(false);
}
mPlayer->setDrawState(MWMechanics::DrawState::Nothing);

@ -26,6 +26,7 @@ $DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw/*lua
cd $FILES_DIR/data
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw_aux/*lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/ai.lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/playercontrols.lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/camera/camera.lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/mwui/init.lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/settings/player.lua

@ -29,6 +29,7 @@ Lua API reference
openmw_aux_ui
interface_ai
interface_camera
interface_controls
interface_mwui
interface_settings
iterables
@ -73,6 +74,10 @@ Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can overrid
- by player scripts
- | Allows to alter behavior of the built-in camera script
| without overriding the script completely.
* - :ref:`Controls <Interface Controls>`
- by player scripts
- | Allows to alter behavior of the built-in script
| that handles player controls.
* - :ref:`Settings <Interface Settings>`
- by player and global scripts
- Save, display and track changes of setting values.

@ -0,0 +1,6 @@
Interface Controls
==================
.. raw:: html
:file: generated_html/scripts_omw_playercontrols.html

@ -452,6 +452,10 @@ The order in which the scripts are started is important. So if one mod should ov
- by player scripts
- | Allows to alter behavior of the built-in camera script
| without overriding the script completely.
* - :ref:`Controls <Interface Controls>`
- by player scripts
- | Allows to alter behavior of the built-in script
| that handles player controls.
* - :ref:`Settings <Interface Settings>`
- by player and global scripts
- Save, display and track changes of setting values.

@ -38,7 +38,9 @@ This setting causes the behavior of the sneak key (bound to Ctrl by default)
to toggle sneaking on and off rather than requiring the key to be held down while sneaking.
Players that spend significant time sneaking may find the character easier to control with this option enabled.
This setting can be toggled in the launcher under "Advanced" -> "Game Mechanics" -> "Toggle sneak".
**This setting is removed from settings.cfg.**
Can be configured in game in the settings menu.
always run
----------
@ -52,7 +54,9 @@ The shift key will temporarily invert this setting, and the caps lock key will i
This setting is updated every time you exit the game,
based on whether the caps lock key was on or off at the time you exited.
This settings can be toggled in game by pressing the CapsLock key and exiting.
**This setting is removed from settings.cfg.**
This setting can be toggled in game by pressing the CapsLock key or in the settings menu.
camera sensitivity
------------------

@ -50,6 +50,7 @@ set(BUILTIN_DATA_FILES
l10n/OMWCamera/ru.yaml
l10n/OMWCamera/sv.yaml
l10n/OMWCamera/fr.yaml
l10n/OMWControls/en.yaml
l10n/PostProcessing/de.yaml
l10n/PostProcessing/en.yaml
l10n/PostProcessing/ru.yaml
@ -83,6 +84,7 @@ set(BUILTIN_DATA_FILES
scripts/omw/console/player.lua
scripts/omw/console/global.lua
scripts/omw/console/local.lua
scripts/omw/playercontrols.lua
scripts/omw/settings/player.lua
scripts/omw/settings/global.lua
scripts/omw/settings/common.lua

@ -1,6 +1,7 @@
PLAYER: scripts/omw/mwui/init.lua
GLOBAL: scripts/omw/settings/global.lua
PLAYER: scripts/omw/settings/player.lua
PLAYER: scripts/omw/playercontrols.lua
PLAYER: scripts/omw/camera/camera.lua
NPC,CREATURE: scripts/omw/ai.lua
PLAYER: scripts/omw/console/player.lua

@ -0,0 +1,16 @@
ControlsPage: "OpenMW Controls"
ControlsPageDescription: "Additional settings related to player controls"
MovementSettings: "Movement"
alwaysRun: "Always run"
alwaysRunDescription: |
If this setting is true, the character is running by default, otherwise the character is walking by default.
The shift key will temporarily invert this setting, and the caps lock key will invert this setting while it's "locked".
toggleSneak: "Toggle sneak"
toggleSneakDescription: |
This setting causes the behavior of the sneak key (bound to Ctrl by default)
to toggle sneaking on and off rather than requiring the key to be held down while sneaking.
Players that spend significant time sneaking may find the character easier to control with this option enabled.

@ -84,11 +84,6 @@ local idleTimer = 0
local vanityDelay = core.getGMST('fVanityDelay')
local function updateVanity(dt)
if input.isIdle() then
idleTimer = idleTimer + dt
else
idleTimer = 0
end
local vanityAllowed = input.getControlSwitch(input.CONTROL_SWITCH.VanityMode)
if vanityAllowed and idleTimer > vanityDelay and camera.getMode() ~= MODE.Vanity then
camera.setMode(MODE.Vanity)
@ -177,8 +172,19 @@ local function onUpdate(dt)
pov_auto_switch.onUpdate(dt)
end
local function updateIdleTimer(dt)
if not input.isIdle() then
idleTimer = 0
elseif self.controls.movement ~= 0 or self.controls.sideMovement ~= 0 or self.controls.jump or self.controls.use ~= 0 then
idleTimer = 0 -- also reset the timer in case of a scripted movement
else
idleTimer = idleTimer + dt
end
end
local function onFrame(dt)
if core.isWorldPaused() then return end
updateIdleTimer(dt)
local mode = camera.getMode()
if mode == MODE.FirstPerson or mode == MODE.ThirdPerson then
primaryMode = mode

@ -0,0 +1,189 @@
local core = require('openmw.core')
local input = require('openmw.input')
local self = require('openmw.self')
local util = require('openmw.util')
local ui = require('openmw.ui')
local Actor = require('openmw.types').Actor
local Player = require('openmw.types').Player
local storage = require('openmw.storage')
local I = require('openmw.interfaces')
local settingsGroup = 'SettingsOMWControls'
local function boolSetting(key, default)
return {
key = key,
renderer = 'checkbox',
name = key,
description = key..'Description',
default = default,
}
end
I.Settings.registerPage({
key = 'OMWControls',
l10n = 'OMWControls',
name = 'ControlsPage',
description = 'ControlsPageDescription',
})
I.Settings.registerGroup({
key = settingsGroup,
page = 'OMWControls',
l10n = 'OMWControls',
name = 'MovementSettings',
permanentStorage = true,
settings = {
boolSetting('alwaysRun', false),
boolSetting('toggleSneak', false),
},
})
local settings = storage.playerSection(settingsGroup)
local attemptJump = false
local startAttack = false
local autoMove = false
local movementControlsOverridden = false
local combatControlsOverridden = false
local function processMovement()
local controllerMovement = -input.getAxisValue(input.CONTROLLER_AXIS.MoveForwardBackward)
local controllerSideMovement = input.getAxisValue(input.CONTROLLER_AXIS.MoveLeftRight)
if controllerMovement ~= 0 or controllerSideMovement ~= 0 then
-- controller movement
if util.vector2(controllerMovement, controllerSideMovement):length2() < 0.25
and not self.controls.sneak and Actor.isOnGround(self) and not Actor.isSwimming(self) then
self.controls.run = false
self.controls.movement = controllerMovement * 2
self.controls.sideMovement = controllerSideMovement * 2
else
self.controls.run = true
self.controls.movement = controllerMovement
self.controls.sideMovement = controllerSideMovement
end
else
-- keyboard movement
self.controls.movement = 0
self.controls.sideMovement = 0
if input.isActionPressed(input.ACTION.MoveLeft) then
self.controls.sideMovement = self.controls.sideMovement - 1
end
if input.isActionPressed(input.ACTION.MoveRight) then
self.controls.sideMovement = self.controls.sideMovement + 1
end
if input.isActionPressed(input.ACTION.MoveBackward) then
self.controls.movement = self.controls.movement - 1
end
if input.isActionPressed(input.ACTION.MoveForward) then
self.controls.movement = self.controls.movement + 1
end
self.controls.run = input.isActionPressed(input.ACTION.Run) ~= settings:get('alwaysRun')
end
if self.controls.movement ~= 0 or not Actor.canMove(self) then
autoMove = false
elseif autoMove then
self.controls.movement = 1
end
self.controls.jump = attemptJump and input.getControlSwitch(input.CONTROL_SWITCH.Jumping)
if not settings:get('toggleSneak') then
self.controls.sneak = input.isActionPressed(input.ACTION.Sneak)
end
end
local function processAttacking()
if startAttack then
self.controls.use = 1
elseif Actor.stance(self) == Actor.STANCE.Spell then
self.controls.use = 0
elseif input.getAxisValue(input.CONTROLLER_AXIS.TriggerRight) < 0.6
and not input.isActionPressed(input.ACTION.Use) then
-- The value "0.6" shouldn't exceed the triggering threshold in BindingsManager::actionValueChanged.
-- TODO: Move more logic from BindingsManager to Lua and consider to make this threshold configurable.
self.controls.use = 0
end
end
local function onFrame(dt)
controlsAllowed = input.getControlSwitch(input.CONTROL_SWITCH.Controls) and not core.isWorldPaused()
if not movementControlsOverridden then
if controlsAllowed then
processMovement()
else
self.controls.movement = 0
self.controls.sideMovement = 0
self.controls.jump = false
end
end
if controlsAllowed and not combatControlsOverridden then
processAttacking()
end
attemptJump = false
startAttack = false
end
local function onInputAction(action)
if core.isWorldPaused() or not input.getControlSwitch(input.CONTROL_SWITCH.Controls) then
return
end
if action == input.ACTION.Jump then
attemptJump = true
elseif action == input.ACTION.Use then
startAttack = true
elseif action == input.ACTION.AutoMove and not movementControlsOverridden then
autoMove = true
elseif action == input.ACTION.AlwaysRun and not movementControlsOverridden then
settings:set('alwaysRun', not settings:get('alwaysRun'))
elseif action == input.ACTION.Sneak and not movementControlsOverridden then
if settings:get('toggleSneak') then
self.controls.sneak = not self.controls.sneak
end
elseif action == input.ACTION.ToggleSpell and not combatControlsOverridden then
if Actor.stance(self) == Actor.STANCE.Spell then
Actor.setStance(self, Actor.STANCE.Nothing)
elseif input.getControlSwitch(input.CONTROL_SWITCH.Magic) then
if Player.isWerewolf(self) then
ui.showMessage(core.getGMST('sWerewolfRefusal'))
else
Actor.setStance(self, Actor.STANCE.Spell)
end
end
elseif action == input.ACTION.ToggleWeapon and not combatControlsOverridden then
if Actor.stance(self) == Actor.STANCE.Weapon then
Actor.setStance(self, Actor.STANCE.Nothing)
elseif input.getControlSwitch(input.CONTROL_SWITCH.Fighting) then
Actor.setStance(self, Actor.STANCE.Weapon)
end
end
end
return {
engineHandlers = {
onFrame = onFrame,
onInputAction = onInputAction,
},
interfaceName = 'Controls',
---
-- @module Controls
-- @usage require('openmw.interfaces').Controls
interface = {
--- Interface version
-- @field [parent=#Controls] #number version
version = 0,
--- When set to true then the movement controls including jump and sneak are not processed and can be handled by another script.
-- If movement should be dissallowed completely, consider to use `input.setControlSwitch` instead.
-- @function [parent=#Controls] overrideMovementControls
-- @param #boolean value
overrideMovementControls = function(v) movementControlsOverridden = v end,
--- When set to true then the controls "attack", "toggle spell", "toggle weapon" are not processed and can be handled by another script.
-- If combat should be dissallowed completely, consider to use `input.setControlSwitch` instead.
-- @function [parent=#Controls] overrideCombatControls
-- @param #boolean value
overrideCombatControls = function(v) combatControlsOverridden = v end,
}
}

@ -33,6 +33,7 @@
-- @field [parent=#ActorControls] #number yawChange Turn right (radians); if negative - turn left
-- @field [parent=#ActorControls] #number pitchChange Look down (radians); if negative - look up
-- @field [parent=#ActorControls] #boolean run true - run, false - walk
-- @field [parent=#ActorControls] #boolean sneak If true - sneak
-- @field [parent=#ActorControls] #boolean jump If true - initiate a jump
-- @field [parent=#ActorControls] #number use if 1 - activates the readied weapon/spell. For weapons, keeping at 1 will charge the attack until set to 0.

@ -105,6 +105,13 @@
-- @param openmw.core#GameObject actor
-- @return #number
---
-- Sets the current stance (whether a weapon/spell is readied), see the list of @{#STANCE} values.
-- Can be used only in local scripts on self.
-- @function [parent=#Actor] setStance
-- @param openmw.core#GameObject actor
-- @param #number stance
---
-- Returns `true` if the item is equipped on the actor.
-- @function [parent=#Actor] isEquipped
@ -477,6 +484,12 @@
-- @param openmw.core#GameObject object
-- @return #boolean
---
-- Whether the actor is in the werewolf form at the moment.
-- @function [parent=#Actor] isWerewolf
-- @param openmw.core#GameObject actor
-- @return #boolean
---
-- Returns the read-only @{#NpcRecord} of an NPC
-- @function [parent=#NPC] record

@ -504,12 +504,6 @@ soft particles = false
# Capture control of the cursor prevent movement outside the window.
grab cursor = true
# Key controlling sneak toggles setting instead of being held down.
toggle sneak = false
# Player is running by default.
always run = false
# Camera sensitivity when not in GUI mode. (>0.0, e.g. 0.1 to 5.0).
camera sensitivity = 1.0

@ -64,12 +64,12 @@
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="toggleSneakCheckBox">
<widget class="QLabel" name="deprecatedToggleSneakLabel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This setting causes the behavior of the sneak key (bound to Ctrl by default) to toggle sneaking on and off rather than requiring the key to be held down while sneaking. Players that spend significant time sneaking may find the character easier to control with this option enabled. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Toggle sneak</string>
<string>&lt;s&gt;Toggle sneak&lt;/s&gt; (moved to the in-game menu)</string>
</property>
</widget>
</item>

@ -0,0 +1 @@
# It is an empty file that overrides builtin.omwscripts and disables builtin scripts
Loading…
Cancel
Save