mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 15:29:55 +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
|
||||
{
|
||||
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))
|
||||
if (action == A_Use && actionIsActive(A_ToggleWeapon))
|
||||
action = A_CycleWeaponRight;
|
||||
|
||||
else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell))
|
||||
else if (action == A_Use && 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))
|
||||
else if (action == A_Jump && actionIsActive(A_ToggleWeapon))
|
||||
action = A_CycleWeaponLeft;
|
||||
|
||||
else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell))
|
||||
else if (action == A_Jump && 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);
|
||||
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)
|
||||
if (key == "playerlooking" && !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)
|
||||
{
|
||||
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,17 +364,25 @@ 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;
|
||||
}
|
||||
// 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.mPitchChange = rotationX;
|
||||
controls.mYawChange = rotationZ;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void Actors::updateActor(const MWWorld::Ptr& ptr, float duration) const
|
||||
|
|
|
@ -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
|
||||
|
|
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 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
|
||||
|
|
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 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><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 name="text">
|
||||
<string>Toggle sneak</string>
|
||||
<string><s>Toggle sneak</s> (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…
Reference in a new issue