mirror of
https://github.com/OpenMW/openmw.git
synced 2025-07-14 16:31:41 +00:00
Merge branch 'playercontrols' into 'master'
Move some of player controls logic from C++ to Lua See merge request OpenMW/openmw!2541
This commit is contained in:
commit
d5f5e53714
38 changed files with 374 additions and 484 deletions
|
@ -84,7 +84,6 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||||
{
|
{
|
||||||
// Game mechanics
|
// Game mechanics
|
||||||
{
|
{
|
||||||
loadSettingBool(toggleSneakCheckBox, "toggle sneak", "Input");
|
|
||||||
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
||||||
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
||||||
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
||||||
|
@ -253,7 +252,6 @@ void Launcher::AdvancedPage::saveSettings()
|
||||||
{
|
{
|
||||||
// Game mechanics
|
// Game mechanics
|
||||||
{
|
{
|
||||||
saveSettingBool(toggleSneakCheckBox, "toggle sneak", "Input");
|
|
||||||
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
||||||
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
||||||
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
||||||
|
|
|
@ -46,7 +46,6 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void setDragDrop(bool dragDrop) = 0;
|
virtual void setDragDrop(bool dragDrop) = 0;
|
||||||
virtual void setGamepadGuiCursorEnabled(bool enabled) = 0;
|
virtual void setGamepadGuiCursorEnabled(bool enabled) = 0;
|
||||||
virtual void setAttemptJump(bool jumping) = 0;
|
|
||||||
|
|
||||||
virtual void toggleControlSwitch(std::string_view sw, bool value) = 0;
|
virtual void toggleControlSwitch(std::string_view sw, bool value) = 0;
|
||||||
virtual bool getControlSwitch(std::string_view sw) = 0;
|
virtual bool getControlSwitch(std::string_view sw) = 0;
|
||||||
|
|
|
@ -68,6 +68,7 @@ namespace MWBase
|
||||||
|
|
||||||
bool mJump = false;
|
bool mJump = false;
|
||||||
bool mRun = false;
|
bool mRun = false;
|
||||||
|
bool mSneak = false;
|
||||||
float mMovement = 0;
|
float mMovement = 0;
|
||||||
float mSideMovement = 0;
|
float mSideMovement = 0;
|
||||||
float mPitchChange = 0;
|
float mPitchChange = 0;
|
||||||
|
|
|
@ -155,7 +155,6 @@ namespace MWBase
|
||||||
virtual MWGui::CountDialog* getCountDialog() = 0;
|
virtual MWGui::CountDialog* getCountDialog() = 0;
|
||||||
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;
|
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;
|
||||||
virtual MWGui::TradeWindow* getTradeWindow() = 0;
|
virtual MWGui::TradeWindow* getTradeWindow() = 0;
|
||||||
virtual const std::vector<std::unique_ptr<MWGui::MessageBox>>& getActiveMessageBoxes() const = 0;
|
|
||||||
virtual MWGui::PostProcessorHud* getPostProcessorHud() = 0;
|
virtual MWGui::PostProcessorHud* getPostProcessorHud() = 0;
|
||||||
|
|
||||||
/// Make the player use an item, while updating GUI state accordingly
|
/// Make the player use an item, while updating GUI state accordingly
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#include <components/lua_ui/util.hpp>
|
#include <components/lua_ui/util.hpp>
|
||||||
|
|
||||||
#include "../mwbase/inputmanager.hpp"
|
#include "../mwbase/inputmanager.hpp"
|
||||||
|
#include "../mwbase/luamanager.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
#include "../mwbase/statemanager.hpp"
|
#include "../mwbase/statemanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
@ -794,11 +795,6 @@ namespace MWGui
|
||||||
mMessageBoxManager->removeStaticMessageBox();
|
mMessageBoxManager->removeStaticMessageBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::unique_ptr<MWGui::MessageBox>>& WindowManager::getActiveMessageBoxes() const
|
|
||||||
{
|
|
||||||
return mMessageBoxManager->getActiveMessageBoxes();
|
|
||||||
}
|
|
||||||
|
|
||||||
int WindowManager::readPressedButton()
|
int WindowManager::readPressedButton()
|
||||||
{
|
{
|
||||||
return mMessageBoxManager->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.
|
// 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.
|
// Otherwise we will get message spam when stealing several items via Take All button.
|
||||||
const MWWorld::Ptr player = MWMechanics::getPlayer();
|
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 (currentBounty != mPlayerBounty)
|
||||||
{
|
{
|
||||||
if (mPlayerBounty >= 0 && currentBounty > mPlayerBounty)
|
if (mPlayerBounty >= 0 && currentBounty > mPlayerBounty)
|
||||||
|
@ -913,6 +910,26 @@ namespace MWGui
|
||||||
mPlayerBounty = currentBounty;
|
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();
|
mDragAndDrop->onFrame();
|
||||||
|
|
||||||
mHud->onFrame(frameDuration);
|
mHud->onFrame(frameDuration);
|
||||||
|
|
|
@ -179,7 +179,6 @@ namespace MWGui
|
||||||
MWGui::CountDialog* getCountDialog() override;
|
MWGui::CountDialog* getCountDialog() override;
|
||||||
MWGui::ConfirmationDialog* getConfirmationDialog() override;
|
MWGui::ConfirmationDialog* getConfirmationDialog() override;
|
||||||
MWGui::TradeWindow* getTradeWindow() override;
|
MWGui::TradeWindow* getTradeWindow() override;
|
||||||
const std::vector<std::unique_ptr<MWGui::MessageBox>>& getActiveMessageBoxes() const override;
|
|
||||||
MWGui::PostProcessorHud* getPostProcessorHud() override;
|
MWGui::PostProcessorHud* getPostProcessorHud() override;
|
||||||
|
|
||||||
/// Make the player use an item, while updating GUI state accordingly
|
/// Make the player use an item, while updating GUI state accordingly
|
||||||
|
|
|
@ -36,99 +36,12 @@ namespace MWInput
|
||||||
, mViewer(viewer)
|
, mViewer(viewer)
|
||||||
, mScreenCaptureHandler(screenCaptureHandler)
|
, mScreenCaptureHandler(screenCaptureHandler)
|
||||||
, mScreenCaptureOperation(screenCaptureOperation)
|
, mScreenCaptureOperation(screenCaptureOperation)
|
||||||
, mAlwaysRunActive(Settings::Manager::getBool("always run", "Input"))
|
|
||||||
, mAttemptJump(false)
|
|
||||||
, mTimeIdle(0.f)
|
, 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)
|
if (mBindingsManager->actionIsActive(A_MoveForward) || mBindingsManager->actionIsActive(A_MoveBackward)
|
||||||
|| mBindingsManager->actionIsActive(A_MoveLeft) || mBindingsManager->actionIsActive(A_MoveRight)
|
|| mBindingsManager->actionIsActive(A_MoveLeft) || mBindingsManager->actionIsActive(A_MoveRight)
|
||||||
|| mBindingsManager->actionIsActive(A_Jump) || mBindingsManager->actionIsActive(A_Sneak)
|
|| mBindingsManager->actionIsActive(A_Jump) || mBindingsManager->actionIsActive(A_Sneak)
|
||||||
|
@ -139,8 +52,6 @@ namespace MWInput
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mTimeIdle += dt;
|
mTimeIdle += dt;
|
||||||
|
|
||||||
mAttemptJump = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionManager::resetIdleTime()
|
void ActionManager::resetIdleTime()
|
||||||
|
@ -181,21 +92,9 @@ namespace MWInput
|
||||||
case A_Journal:
|
case A_Journal:
|
||||||
toggleJournal();
|
toggleJournal();
|
||||||
break;
|
break;
|
||||||
case A_AutoMove:
|
|
||||||
toggleAutoMove();
|
|
||||||
break;
|
|
||||||
case A_AlwaysRun:
|
|
||||||
toggleWalking();
|
|
||||||
break;
|
|
||||||
case A_ToggleWeapon:
|
|
||||||
toggleWeapon();
|
|
||||||
break;
|
|
||||||
case A_Rest:
|
case A_Rest:
|
||||||
rest();
|
rest();
|
||||||
break;
|
break;
|
||||||
case A_ToggleSpell:
|
|
||||||
toggleSpell();
|
|
||||||
break;
|
|
||||||
case A_QuickKey1:
|
case A_QuickKey1:
|
||||||
quickKey(1);
|
quickKey(1);
|
||||||
break;
|
break;
|
||||||
|
@ -260,13 +159,6 @@ namespace MWInput
|
||||||
if (checkAllowedToUseItems() && windowManager->isAllowed(MWGui::GW_Inventory))
|
if (checkAllowedToUseItems() && windowManager->isAllowed(MWGui::GW_Inventory))
|
||||||
MWBase::Environment::get().getWindowManager()->cycleWeapon(true);
|
MWBase::Environment::get().getWindowManager()->cycleWeapon(true);
|
||||||
break;
|
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()
|
void ActionManager::quickLoad()
|
||||||
{
|
{
|
||||||
if (!MyGUI::InputManager::getInstance().isModalAny())
|
if (!MyGUI::InputManager::getInstance().isModalAny())
|
||||||
|
@ -376,32 +238,6 @@ namespace MWInput
|
||||||
MWBase::Environment::get().getStateManager()->quickSave();
|
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()
|
void ActionManager::rest()
|
||||||
{
|
{
|
||||||
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
|
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
|
bool ActionManager::isSneaking() const
|
||||||
{
|
{
|
||||||
const MWBase::Environment& env = MWBase::Environment::get();
|
const MWBase::Environment& env = MWBase::Environment::get();
|
||||||
return env.getMechanicsManager()->isSneaking(env.getWorld()->getPlayer().getPlayer());
|
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)
|
void ActionManager::handleGuiArrowKey(int action)
|
||||||
{
|
{
|
||||||
bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed();
|
bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed();
|
||||||
|
|
|
@ -21,23 +21,18 @@ namespace MWInput
|
||||||
osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
|
osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
|
||||||
osg::ref_ptr<osgViewer::Viewer> viewer, osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler);
|
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);
|
void executeAction(int action);
|
||||||
|
|
||||||
bool checkAllowedToUseItems() const;
|
bool checkAllowedToUseItems() const;
|
||||||
|
|
||||||
void toggleMainMenu();
|
void toggleMainMenu();
|
||||||
void toggleSpell();
|
|
||||||
void toggleWeapon();
|
|
||||||
void toggleInventory();
|
void toggleInventory();
|
||||||
void toggleConsole();
|
void toggleConsole();
|
||||||
void screenshot();
|
void screenshot();
|
||||||
void toggleJournal();
|
void toggleJournal();
|
||||||
void activate();
|
void activate();
|
||||||
void toggleWalking();
|
|
||||||
void toggleSneaking();
|
|
||||||
void toggleAutoMove();
|
|
||||||
void rest();
|
void rest();
|
||||||
void quickLoad();
|
void quickLoad();
|
||||||
void quickSave();
|
void quickSave();
|
||||||
|
@ -48,11 +43,8 @@ namespace MWInput
|
||||||
void resetIdleTime();
|
void resetIdleTime();
|
||||||
float getIdleTime() const { return mTimeIdle; }
|
float getIdleTime() const { return mTimeIdle; }
|
||||||
|
|
||||||
bool isAlwaysRunActive() const { return mAlwaysRunActive; }
|
|
||||||
bool isSneaking() const;
|
bool isSneaking() const;
|
||||||
|
|
||||||
void setAttemptJump(bool enabled) { mAttemptJump = enabled; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleGuiArrowKey(int action);
|
void handleGuiArrowKey(int action);
|
||||||
|
|
||||||
|
@ -61,9 +53,6 @@ namespace MWInput
|
||||||
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
|
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
|
||||||
osgViewer::ScreenCaptureHandler::CaptureOperation* mScreenCaptureOperation;
|
osgViewer::ScreenCaptureHandler::CaptureOperation* mScreenCaptureOperation;
|
||||||
|
|
||||||
bool mAlwaysRunActive;
|
|
||||||
bool mAttemptJump;
|
|
||||||
|
|
||||||
float mTimeIdle;
|
float mTimeIdle;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -681,65 +681,25 @@ namespace MWInput
|
||||||
|
|
||||||
void BindingsManager::actionValueChanged(int action, float currentValue, float previousValue)
|
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)
|
if (mDragDrop && action != A_GameMenu && action != A_Inventory)
|
||||||
return;
|
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
|
if (action == A_Use && actionIsActive(A_ToggleWeapon))
|
||||||
}
|
action = A_CycleWeaponRight;
|
||||||
// Otherwise only trigger button presses as they go through specific points
|
else if (action == A_Use && actionIsActive(A_ToggleSpell))
|
||||||
else if (previousValue >= 0.8 && currentValue < 0.8)
|
action = A_CycleSpellRight;
|
||||||
{
|
else if (action == A_Jump && actionIsActive(A_ToggleWeapon))
|
||||||
currentValue = 0.0;
|
action = A_CycleWeaponLeft;
|
||||||
previousValue = 1.0;
|
else if (action == A_Jump && actionIsActive(A_ToggleSpell))
|
||||||
}
|
action = A_CycleSpellLeft;
|
||||||
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"))
|
if (previousValue <= 0.6 && currentValue > 0.6)
|
||||||
{
|
manager->executeAction(action);
|
||||||
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 (currentValue == 1)
|
|
||||||
MWBase::Environment::get().getInputManager()->executeAction(action);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,20 +25,16 @@
|
||||||
|
|
||||||
namespace MWInput
|
namespace MWInput
|
||||||
{
|
{
|
||||||
ControllerManager::ControllerManager(BindingsManager* bindingsManager, ActionManager* actionManager,
|
ControllerManager::ControllerManager(BindingsManager* bindingsManager, MouseManager* mouseManager,
|
||||||
MouseManager* mouseManager, const std::filesystem::path& userControllerBindingsFile,
|
const std::filesystem::path& userControllerBindingsFile, const std::filesystem::path& controllerBindingsFile)
|
||||||
const std::filesystem::path& controllerBindingsFile)
|
|
||||||
: mBindingsManager(bindingsManager)
|
: mBindingsManager(bindingsManager)
|
||||||
, mActionManager(actionManager)
|
|
||||||
, mMouseManager(mouseManager)
|
, mMouseManager(mouseManager)
|
||||||
, mJoystickEnabled(Settings::Manager::getBool("enable controller", "Input"))
|
, mJoystickEnabled(Settings::Manager::getBool("enable controller", "Input"))
|
||||||
, mGyroAvailable(false)
|
, mGyroAvailable(false)
|
||||||
, mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input"))
|
, mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input"))
|
||||||
, mSneakToggleShortcutTimer(0.f)
|
|
||||||
, mGamepadGuiCursorEnabled(true)
|
, mGamepadGuiCursorEnabled(true)
|
||||||
, mGuiCursorEnabled(true)
|
, mGuiCursorEnabled(true)
|
||||||
, mJoystickLastUsed(false)
|
, mJoystickLastUsed(false)
|
||||||
, mSneakGamepadShortcut(false)
|
|
||||||
{
|
{
|
||||||
if (!controllerBindingsFile.empty())
|
if (!controllerBindingsFile.empty())
|
||||||
{
|
{
|
||||||
|
@ -82,7 +78,7 @@ namespace MWInput
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ControllerManager::update(float dt)
|
void ControllerManager::update(float dt)
|
||||||
{
|
{
|
||||||
if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled))
|
if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled))
|
||||||
{
|
{
|
||||||
|
@ -108,77 +104,18 @@ namespace MWInput
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable movement in Gui mode
|
if (!MWBase::Environment::get().getWindowManager()->isGuiMode()
|
||||||
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|
&& MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running
|
||||||
|| MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
|
&& MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
|
||||||
{
|
|
||||||
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"))
|
|
||||||
{
|
{
|
||||||
float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight);
|
float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight);
|
||||||
float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward);
|
float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward);
|
||||||
if (xAxis != 0.5)
|
if (xAxis != 0.5 || yAxis != 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)
|
|
||||||
{
|
{
|
||||||
mJoystickLastUsed = true;
|
mJoystickLastUsed = true;
|
||||||
MWBase::Environment::get().getInputManager()->resetIdleTime();
|
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)
|
void ControllerManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent& arg)
|
||||||
|
|
|
@ -9,20 +9,19 @@
|
||||||
|
|
||||||
namespace MWInput
|
namespace MWInput
|
||||||
{
|
{
|
||||||
class ActionManager;
|
|
||||||
class BindingsManager;
|
class BindingsManager;
|
||||||
class MouseManager;
|
class MouseManager;
|
||||||
|
|
||||||
class ControllerManager : public SDLUtil::ControllerListener
|
class ControllerManager : public SDLUtil::ControllerListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ControllerManager(BindingsManager* bindingsManager, ActionManager* actionManager, MouseManager* mouseManager,
|
ControllerManager(BindingsManager* bindingsManager, MouseManager* mouseManager,
|
||||||
const std::filesystem::path& userControllerBindingsFile,
|
const std::filesystem::path& userControllerBindingsFile,
|
||||||
const std::filesystem::path& controllerBindingsFile);
|
const std::filesystem::path& controllerBindingsFile);
|
||||||
|
|
||||||
virtual ~ControllerManager() = default;
|
virtual ~ControllerManager() = default;
|
||||||
|
|
||||||
bool update(float dt);
|
void update(float dt);
|
||||||
|
|
||||||
void buttonPressed(int deviceID, const SDL_ControllerButtonEvent& arg) override;
|
void buttonPressed(int deviceID, const SDL_ControllerButtonEvent& arg) override;
|
||||||
void buttonReleased(int deviceID, const SDL_ControllerButtonEvent& arg) override;
|
void buttonReleased(int deviceID, const SDL_ControllerButtonEvent& arg) override;
|
||||||
|
@ -58,17 +57,14 @@ namespace MWInput
|
||||||
void enableGyroSensor();
|
void enableGyroSensor();
|
||||||
|
|
||||||
BindingsManager* mBindingsManager;
|
BindingsManager* mBindingsManager;
|
||||||
ActionManager* mActionManager;
|
|
||||||
MouseManager* mMouseManager;
|
MouseManager* mMouseManager;
|
||||||
|
|
||||||
bool mJoystickEnabled;
|
bool mJoystickEnabled;
|
||||||
bool mGyroAvailable;
|
bool mGyroAvailable;
|
||||||
float mGamepadCursorSpeed;
|
float mGamepadCursorSpeed;
|
||||||
float mSneakToggleShortcutTimer;
|
|
||||||
bool mGamepadGuiCursorEnabled;
|
bool mGamepadGuiCursorEnabled;
|
||||||
bool mGuiCursorEnabled;
|
bool mGuiCursorEnabled;
|
||||||
bool mJoystickLastUsed;
|
bool mJoystickLastUsed;
|
||||||
bool mSneakGamepadShortcut;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -39,24 +39,10 @@ namespace MWInput
|
||||||
|
|
||||||
void ControlSwitch::set(std::string_view key, bool value)
|
void ControlSwitch::set(std::string_view key, bool value)
|
||||||
{
|
{
|
||||||
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
if (key == "playerlooking" && !value)
|
||||||
|
|
||||||
/// \note 7 switches at all, if-else is relevant
|
|
||||||
if (key == "playercontrols" && !value)
|
|
||||||
{
|
{
|
||||||
player.setLeftRight(0);
|
auto world = MWBase::Environment::get().getWorld();
|
||||||
player.setForwardBackward(0);
|
world->rotateObject(world->getPlayerPtr(), osg::Vec3f());
|
||||||
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)
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(player.getPlayer(), osg::Vec3f());
|
|
||||||
}
|
}
|
||||||
auto it = mSwitches.find(key);
|
auto it = mSwitches.find(key);
|
||||||
if (it == mSwitches.end())
|
if (it == mSwitches.end())
|
||||||
|
|
|
@ -37,8 +37,8 @@ namespace MWInput
|
||||||
mBindingsManager.get(), screenCaptureOperation, viewer, screenCaptureHandler))
|
mBindingsManager.get(), screenCaptureOperation, viewer, screenCaptureHandler))
|
||||||
, mKeyboardManager(std::make_unique<KeyboardManager>(mBindingsManager.get()))
|
, mKeyboardManager(std::make_unique<KeyboardManager>(mBindingsManager.get()))
|
||||||
, mMouseManager(std::make_unique<MouseManager>(mBindingsManager.get(), mInputWrapper.get(), window))
|
, mMouseManager(std::make_unique<MouseManager>(mBindingsManager.get(), mInputWrapper.get(), window))
|
||||||
, mControllerManager(std::make_unique<ControllerManager>(mBindingsManager.get(), mActionManager.get(),
|
, mControllerManager(std::make_unique<ControllerManager>(
|
||||||
mMouseManager.get(), userControllerBindingsFile, controllerBindingsFile))
|
mBindingsManager.get(), mMouseManager.get(), userControllerBindingsFile, controllerBindingsFile))
|
||||||
, mSensorManager(std::make_unique<SensorManager>())
|
, mSensorManager(std::make_unique<SensorManager>())
|
||||||
, mGyroManager(std::make_unique<GyroManager>())
|
, mGyroManager(std::make_unique<GyroManager>())
|
||||||
{
|
{
|
||||||
|
@ -57,11 +57,6 @@ namespace MWInput
|
||||||
|
|
||||||
InputManager::~InputManager() {}
|
InputManager::~InputManager() {}
|
||||||
|
|
||||||
void InputManager::setAttemptJump(bool jumping)
|
|
||||||
{
|
|
||||||
mActionManager->setAttemptJump(jumping);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputManager::update(float dt, bool disableControls, bool disableEvents)
|
void InputManager::update(float dt, bool disableControls, bool disableEvents)
|
||||||
{
|
{
|
||||||
mControlsDisabled = disableControls;
|
mControlsDisabled = disableControls;
|
||||||
|
@ -79,10 +74,10 @@ namespace MWInput
|
||||||
|
|
||||||
mMouseManager->updateCursorMode();
|
mMouseManager->updateCursorMode();
|
||||||
|
|
||||||
bool controllerMove = mControllerManager->update(dt);
|
mControllerManager->update(dt);
|
||||||
mMouseManager->update(dt);
|
mMouseManager->update(dt);
|
||||||
mSensorManager->update(dt);
|
mSensorManager->update(dt);
|
||||||
mActionManager->update(dt, controllerMove);
|
mActionManager->update(dt);
|
||||||
|
|
||||||
if (mGyroManager->isEnabled())
|
if (mGyroManager->isEnabled())
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,7 +68,6 @@ namespace MWInput
|
||||||
|
|
||||||
void setDragDrop(bool dragDrop) override;
|
void setDragDrop(bool dragDrop) override;
|
||||||
void setGamepadGuiCursorEnabled(bool enabled) override;
|
void setGamepadGuiCursorEnabled(bool enabled) override;
|
||||||
void setAttemptJump(bool jumping) override;
|
|
||||||
|
|
||||||
void toggleControlSwitch(std::string_view sw, bool value) override;
|
void toggleControlSwitch(std::string_view sw, bool value) override;
|
||||||
bool getControlSwitch(std::string_view sw) override;
|
bool getControlSwitch(std::string_view sw) override;
|
||||||
|
|
|
@ -48,6 +48,7 @@ namespace MWLua
|
||||||
controls["pitchChange"] = CONTROL(float, mPitchChange);
|
controls["pitchChange"] = CONTROL(float, mPitchChange);
|
||||||
controls["yawChange"] = CONTROL(float, mYawChange);
|
controls["yawChange"] = CONTROL(float, mYawChange);
|
||||||
controls["run"] = CONTROL(bool, mRun);
|
controls["run"] = CONTROL(bool, mRun);
|
||||||
|
controls["sneak"] = CONTROL(bool, mSneak);
|
||||||
controls["jump"] = CONTROL(bool, mJump);
|
controls["jump"] = CONTROL(bool, mJump);
|
||||||
controls["use"] = CONTROL(int, mUse);
|
controls["use"] = CONTROL(int, mUse);
|
||||||
#undef CONTROL
|
#undef CONTROL
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace MWLua
|
||||||
{
|
{
|
||||||
auto* lua = context.mLua;
|
auto* lua = context.mLua;
|
||||||
sol::table api(lua->sol(), sol::create);
|
sol::table api(lua->sol(), sol::create);
|
||||||
api["API_REVISION"] = 30;
|
api["API_REVISION"] = 31;
|
||||||
api["quit"] = [lua]() {
|
api["quit"] = [lua]() {
|
||||||
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
||||||
MWBase::Environment::get().getStateManager()->requestQuit();
|
MWBase::Environment::get().getStateManager()->requestQuit();
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <components/detournavigator/agentbounds.hpp>
|
#include <components/detournavigator/agentbounds.hpp>
|
||||||
#include <components/lua/luastate.hpp>
|
#include <components/lua/luastate.hpp>
|
||||||
|
|
||||||
|
#include <apps/openmw/mwbase/mechanicsmanager.hpp>
|
||||||
#include <apps/openmw/mwmechanics/creaturestats.hpp>
|
#include <apps/openmw/mwmechanics/creaturestats.hpp>
|
||||||
#include <apps/openmw/mwmechanics/drawstate.hpp>
|
#include <apps/openmw/mwmechanics/drawstate.hpp>
|
||||||
#include <apps/openmw/mwworld/class.hpp>
|
#include <apps/openmw/mwworld/class.hpp>
|
||||||
|
@ -150,6 +151,38 @@ namespace MWLua
|
||||||
else
|
else
|
||||||
throw std::runtime_error("Actor expected");
|
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) {
|
actor["canMove"] = [](const Object& o) {
|
||||||
const MWWorld::Class& cls = o.ptr().getClass();
|
const MWWorld::Class& cls = o.ptr().getClass();
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#include <apps/openmw/mwbase/environment.hpp>
|
#include <apps/openmw/mwbase/environment.hpp>
|
||||||
#include <apps/openmw/mwbase/world.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 <apps/openmw/mwworld/esmstore.hpp>
|
||||||
|
|
||||||
#include "../stats.hpp"
|
#include "../stats.hpp"
|
||||||
|
@ -43,5 +45,14 @@ namespace MWLua
|
||||||
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHair.getRefIdString(); });
|
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHair.getRefIdString(); });
|
||||||
record["head"]
|
record["head"]
|
||||||
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHead.getRefIdString(); });
|
= 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 float rotationZ = mov.mRotation[2];
|
||||||
const bool jump = mov.mPosition[2] == 1;
|
const bool jump = mov.mPosition[2] == 1;
|
||||||
const bool runFlag = stats.getMovementFlag(MWMechanics::CreatureStats::Flag_Run);
|
const bool runFlag = stats.getMovementFlag(MWMechanics::CreatureStats::Flag_Run);
|
||||||
|
const bool sneakFlag = stats.getMovementFlag(MWMechanics::CreatureStats::Flag_Sneak);
|
||||||
const bool attackingOrSpell = stats.getAttackingOrSpell();
|
const bool attackingOrSpell = stats.getAttackingOrSpell();
|
||||||
if (controls.mChanged)
|
if (controls.mChanged)
|
||||||
{
|
{
|
||||||
|
@ -363,16 +364,24 @@ namespace MWMechanics
|
||||||
mov.mRotation[2] = controls.mYawChange;
|
mov.mRotation[2] = controls.mYawChange;
|
||||||
mov.mSpeedFactor = osg::Vec2(controls.mMovement, controls.mSideMovement).length();
|
mov.mSpeedFactor = osg::Vec2(controls.mMovement, controls.mSideMovement).length();
|
||||||
stats.setMovementFlag(MWMechanics::CreatureStats::Flag_Run, controls.mRun);
|
stats.setMovementFlag(MWMechanics::CreatureStats::Flag_Run, controls.mRun);
|
||||||
|
stats.setMovementFlag(MWMechanics::CreatureStats::Flag_Sneak, controls.mSneak);
|
||||||
stats.setAttackingOrSpell((controls.mUse & 1) == 1);
|
stats.setAttackingOrSpell((controls.mUse & 1) == 1);
|
||||||
controls.mChanged = false;
|
controls.mChanged = false;
|
||||||
}
|
}
|
||||||
controls.mSideMovement = movement.x();
|
// For the player we don't need to copy these values to Lua because mwinput doesn't change them.
|
||||||
controls.mMovement = movement.y();
|
// 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.mPitchChange = rotationX;
|
||||||
controls.mYawChange = rotationZ;
|
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);
|
movementSettings.mSpeedFactor = std::min(vec.length(), 1.f);
|
||||||
vec.normalize();
|
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");
|
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
|
||||||
if (smoothMovement)
|
if (smoothMovement)
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,8 +40,6 @@ namespace MWWorld
|
||||||
, mLastKnownExteriorPosition(0, 0, 0)
|
, mLastKnownExteriorPosition(0, 0, 0)
|
||||||
, mMarkedPosition(ESM::Position())
|
, mMarkedPosition(ESM::Position())
|
||||||
, mMarkedCell(nullptr)
|
, mMarkedCell(nullptr)
|
||||||
, mAutoMove(false)
|
|
||||||
, mForwardBackward(0)
|
|
||||||
, mTeleported(false)
|
, mTeleported(false)
|
||||||
, mCurrentCrimeId(-1)
|
, mCurrentCrimeId(-1)
|
||||||
, mPaidCrimeId(-1)
|
, mPaidCrimeId(-1)
|
||||||
|
@ -163,61 +161,6 @@ namespace MWWorld
|
||||||
ptr.getClass().getNpcStats(ptr).setDrawState(state);
|
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)
|
void Player::yaw(float yaw)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr ptr = getPlayer();
|
MWWorld::Ptr ptr = getPlayer();
|
||||||
|
@ -272,11 +215,6 @@ namespace MWWorld
|
||||||
mTeleported = teleported;
|
mTeleported = teleported;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setAttackingOrSpell(bool attackingOrSpell)
|
|
||||||
{
|
|
||||||
getPlayer().getClass().getCreatureStats(getPlayer()).setAttackingOrSpell(attackingOrSpell);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::setJumping(bool jumping)
|
void Player::setJumping(bool jumping)
|
||||||
{
|
{
|
||||||
mJumping = jumping;
|
mJumping = jumping;
|
||||||
|
@ -315,8 +253,6 @@ namespace MWWorld
|
||||||
mCellStore = nullptr;
|
mCellStore = nullptr;
|
||||||
mSign = ESM::RefId::sEmpty;
|
mSign = ESM::RefId::sEmpty;
|
||||||
mMarkedCell = nullptr;
|
mMarkedCell = nullptr;
|
||||||
mAutoMove = false;
|
|
||||||
mForwardBackward = 0;
|
|
||||||
mTeleported = false;
|
mTeleported = false;
|
||||||
mJumping = false;
|
mJumping = false;
|
||||||
mCurrentCrimeId = -1;
|
mCurrentCrimeId = -1;
|
||||||
|
@ -475,7 +411,6 @@ namespace MWWorld
|
||||||
mMarkedCell = nullptr;
|
mMarkedCell = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
mForwardBackward = 0;
|
|
||||||
mTeleported = false;
|
mTeleported = false;
|
||||||
|
|
||||||
mPreviousItems = player.mPreviousItems;
|
mPreviousItems = player.mPreviousItems;
|
||||||
|
|
|
@ -41,8 +41,6 @@ namespace MWWorld
|
||||||
// If no position was marked, this is nullptr
|
// If no position was marked, this is nullptr
|
||||||
CellStore* mMarkedCell;
|
CellStore* mMarkedCell;
|
||||||
|
|
||||||
bool mAutoMove;
|
|
||||||
float mForwardBackward;
|
|
||||||
bool mTeleported;
|
bool mTeleported;
|
||||||
|
|
||||||
int mCurrentCrimeId; // the id assigned witnesses
|
int mCurrentCrimeId; // the id assigned witnesses
|
||||||
|
@ -90,17 +88,6 @@ namespace MWWorld
|
||||||
/// Activate the object under the crosshair, if any
|
/// Activate the object under the crosshair, if any
|
||||||
void activate();
|
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 yaw(float yaw);
|
||||||
void pitch(float pitch);
|
void pitch(float pitch);
|
||||||
void roll(float roll);
|
void roll(float roll);
|
||||||
|
@ -108,8 +95,6 @@ namespace MWWorld
|
||||||
bool wasTeleported() const;
|
bool wasTeleported() const;
|
||||||
void setTeleported(bool teleported);
|
void setTeleported(bool teleported);
|
||||||
|
|
||||||
void setAttackingOrSpell(bool attackingOrSpell);
|
|
||||||
|
|
||||||
void setJumping(bool jumping);
|
void setJumping(bool jumping);
|
||||||
bool getJumping() const;
|
bool getJumping() const;
|
||||||
|
|
||||||
|
|
|
@ -3651,7 +3651,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(player))
|
if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(player))
|
||||||
{
|
{
|
||||||
mPlayer->setAttackingOrSpell(false);
|
player.getClass().getCreatureStats(player).setAttackingOrSpell(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
mPlayer->setDrawState(MWMechanics::DrawState::Nothing);
|
mPlayer->setDrawState(MWMechanics::DrawState::Nothing);
|
||||||
|
|
|
@ -26,6 +26,7 @@ $DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw/*lua
|
||||||
cd $FILES_DIR/data
|
cd $FILES_DIR/data
|
||||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw_aux/*lua
|
$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/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/camera/camera.lua
|
||||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/mwui/init.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
|
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/settings/player.lua
|
||||||
|
|
|
@ -29,6 +29,7 @@ Lua API reference
|
||||||
openmw_aux_ui
|
openmw_aux_ui
|
||||||
interface_ai
|
interface_ai
|
||||||
interface_camera
|
interface_camera
|
||||||
|
interface_controls
|
||||||
interface_mwui
|
interface_mwui
|
||||||
interface_settings
|
interface_settings
|
||||||
iterables
|
iterables
|
||||||
|
@ -73,6 +74,10 @@ Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can overrid
|
||||||
- by player scripts
|
- by player scripts
|
||||||
- | Allows to alter behavior of the built-in camera script
|
- | Allows to alter behavior of the built-in camera script
|
||||||
| without overriding the script completely.
|
| 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>`
|
* - :ref:`Settings <Interface Settings>`
|
||||||
- by player and global scripts
|
- by player and global scripts
|
||||||
- Save, display and track changes of setting values.
|
- 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
|
- by player scripts
|
||||||
- | Allows to alter behavior of the built-in camera script
|
- | Allows to alter behavior of the built-in camera script
|
||||||
| without overriding the script completely.
|
| 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>`
|
* - :ref:`Settings <Interface Settings>`
|
||||||
- by player and global scripts
|
- by player and global scripts
|
||||||
- Save, display and track changes of setting values.
|
- 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.
|
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.
|
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
|
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,
|
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.
|
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
|
camera sensitivity
|
||||||
------------------
|
------------------
|
||||||
|
|
|
@ -50,6 +50,7 @@ set(BUILTIN_DATA_FILES
|
||||||
l10n/OMWCamera/ru.yaml
|
l10n/OMWCamera/ru.yaml
|
||||||
l10n/OMWCamera/sv.yaml
|
l10n/OMWCamera/sv.yaml
|
||||||
l10n/OMWCamera/fr.yaml
|
l10n/OMWCamera/fr.yaml
|
||||||
|
l10n/OMWControls/en.yaml
|
||||||
l10n/PostProcessing/de.yaml
|
l10n/PostProcessing/de.yaml
|
||||||
l10n/PostProcessing/en.yaml
|
l10n/PostProcessing/en.yaml
|
||||||
l10n/PostProcessing/ru.yaml
|
l10n/PostProcessing/ru.yaml
|
||||||
|
@ -83,6 +84,7 @@ set(BUILTIN_DATA_FILES
|
||||||
scripts/omw/console/player.lua
|
scripts/omw/console/player.lua
|
||||||
scripts/omw/console/global.lua
|
scripts/omw/console/global.lua
|
||||||
scripts/omw/console/local.lua
|
scripts/omw/console/local.lua
|
||||||
|
scripts/omw/playercontrols.lua
|
||||||
scripts/omw/settings/player.lua
|
scripts/omw/settings/player.lua
|
||||||
scripts/omw/settings/global.lua
|
scripts/omw/settings/global.lua
|
||||||
scripts/omw/settings/common.lua
|
scripts/omw/settings/common.lua
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
PLAYER: scripts/omw/mwui/init.lua
|
PLAYER: scripts/omw/mwui/init.lua
|
||||||
GLOBAL: scripts/omw/settings/global.lua
|
GLOBAL: scripts/omw/settings/global.lua
|
||||||
PLAYER: scripts/omw/settings/player.lua
|
PLAYER: scripts/omw/settings/player.lua
|
||||||
|
PLAYER: scripts/omw/playercontrols.lua
|
||||||
PLAYER: scripts/omw/camera/camera.lua
|
PLAYER: scripts/omw/camera/camera.lua
|
||||||
NPC,CREATURE: scripts/omw/ai.lua
|
NPC,CREATURE: scripts/omw/ai.lua
|
||||||
PLAYER: scripts/omw/console/player.lua
|
PLAYER: scripts/omw/console/player.lua
|
||||||
|
|
16
files/data/l10n/OMWControls/en.yaml
Normal file
16
files/data/l10n/OMWControls/en.yaml
Normal file
|
@ -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 vanityDelay = core.getGMST('fVanityDelay')
|
||||||
|
|
||||||
local function updateVanity(dt)
|
local function updateVanity(dt)
|
||||||
if input.isIdle() then
|
|
||||||
idleTimer = idleTimer + dt
|
|
||||||
else
|
|
||||||
idleTimer = 0
|
|
||||||
end
|
|
||||||
local vanityAllowed = input.getControlSwitch(input.CONTROL_SWITCH.VanityMode)
|
local vanityAllowed = input.getControlSwitch(input.CONTROL_SWITCH.VanityMode)
|
||||||
if vanityAllowed and idleTimer > vanityDelay and camera.getMode() ~= MODE.Vanity then
|
if vanityAllowed and idleTimer > vanityDelay and camera.getMode() ~= MODE.Vanity then
|
||||||
camera.setMode(MODE.Vanity)
|
camera.setMode(MODE.Vanity)
|
||||||
|
@ -177,8 +172,19 @@ local function onUpdate(dt)
|
||||||
pov_auto_switch.onUpdate(dt)
|
pov_auto_switch.onUpdate(dt)
|
||||||
end
|
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)
|
local function onFrame(dt)
|
||||||
if core.isWorldPaused() then return end
|
if core.isWorldPaused() then return end
|
||||||
|
updateIdleTimer(dt)
|
||||||
local mode = camera.getMode()
|
local mode = camera.getMode()
|
||||||
if mode == MODE.FirstPerson or mode == MODE.ThirdPerson then
|
if mode == MODE.FirstPerson or mode == MODE.ThirdPerson then
|
||||||
primaryMode = mode
|
primaryMode = mode
|
||||||
|
|
189
files/data/scripts/omw/playercontrols.lua
Normal file
189
files/data/scripts/omw/playercontrols.lua
Normal file
|
@ -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 yawChange Turn right (radians); if negative - turn left
|
||||||
-- @field [parent=#ActorControls] #number pitchChange Look down (radians); if negative - look up
|
-- @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 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] #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.
|
-- @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
|
-- @param openmw.core#GameObject actor
|
||||||
-- @return #number
|
-- @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.
|
-- Returns `true` if the item is equipped on the actor.
|
||||||
-- @function [parent=#Actor] isEquipped
|
-- @function [parent=#Actor] isEquipped
|
||||||
|
@ -477,6 +484,12 @@
|
||||||
-- @param openmw.core#GameObject object
|
-- @param openmw.core#GameObject object
|
||||||
-- @return #boolean
|
-- @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
|
-- Returns the read-only @{#NpcRecord} of an NPC
|
||||||
-- @function [parent=#NPC] record
|
-- @function [parent=#NPC] record
|
||||||
|
|
|
@ -504,12 +504,6 @@ soft particles = false
|
||||||
# Capture control of the cursor prevent movement outside the window.
|
# Capture control of the cursor prevent movement outside the window.
|
||||||
grab cursor = true
|
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 when not in GUI mode. (>0.0, e.g. 0.1 to 5.0).
|
||||||
camera sensitivity = 1.0
|
camera sensitivity = 1.0
|
||||||
|
|
||||||
|
|
|
@ -64,12 +64,12 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QCheckBox" name="toggleSneakCheckBox">
|
<widget class="QLabel" name="deprecatedToggleSneakLabel">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>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. </p></body></html></string>
|
<string><html><head/><body><p>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. </p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Toggle sneak</string>
|
<string><s>Toggle sneak</s> (moved to the in-game menu)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
# It is an empty file that overrides builtin.omwscripts and disables builtin scripts
|
Loading…
Reference in a new issue