Add OpenMW commits up to 16 May 2020

# Conflicts:
#	CI/before_script.linux.sh
#	apps/openmw/mwbase/world.hpp
#	apps/openmw/mwinput/inputmanagerimp.cpp
#	apps/openmw/mwscript/globalscripts.cpp
#	apps/openmw/mwscript/interpretercontext.cpp
#	apps/openmw/mwworld/cellstore.cpp
#	apps/openmw/mwworld/cellstore.hpp
#	apps/openmw/mwworld/worldimp.cpp
#	apps/openmw/mwworld/worldimp.hpp
#	components/interpreter/context.hpp
pull/593/head
David Cernat 5 years ago
commit 62df188fd4

@ -2,7 +2,10 @@
------
Bug #1952: Incorrect particle lighting
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
Bug #3676: NiParticleColorModifier isn't applied properly
Bug #4774: Guards are ignorant of an invisible player that tries to attack them
Bug #5108: Savegame bloating due to inefficient fog textures format
Bug #5358: ForceGreeting always resets the dialogue window completely
Bug #5363: Enchantment autocalc not always 0/1
Bug #5364: Script fails/stops if trying to startscript an unknown script
@ -10,6 +13,8 @@
Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures
Bug #5370: Opening an unlocked but trapped door uses the key
Bug #5400: Editor: Verifier checks race of non-skin bodyparts
Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work
Bug #5416: Junk non-node records before the root node are not handled gracefully
Feature #5362: Show the soul gems' trapped soul in count dialog
0.46.0

@ -13,21 +13,28 @@ The OpenMW team is proud to announce the release of version 0.47.0! Grab it from
Check out the release video (***add link***) and the OpenMW-CS release video (***add link***) by the ***add flattering adjective*** Atahualpa, and see below for the full list of changes.
Known Issues:
- There's currently no way to redirect the logging output to the command prompt on Windows Release builds -- this will be resolved in version 0.46.0
- To use generic Linux binaries, Qt4 and libpng12 must be installed on your system
- On macOS, launching OpenMW from OpenMW-CS requires OpenMW.app and OpenMW-CS.app to be siblings
New Features:
- ?
- Dialogue to split item stacks now displays the name of the trapped soul for stacks of soul gems (#5362)
New Editor Features:
- ?
Bug Fixes:
- ?
- NiParticleColorModifier in NIF files is now properly handled which solves issues regarding particle effects, e.g., smoke and fire (#1952, #3676)
- Targetting non-unique actors in scripts is now supported (#2311)
- Guards no longer ignore attacks of invisible players but rather initiate dialogue and flee if the player resists being arrested (#4774)
- Changing the dialogue window without closing it no longer clears the dialogue history in order to allow, e.g., emulation of three-way dialogue via ForceGreeting (#5358)
- Scripts which try to start a non-existent global script now skip that step and continue execution instead of breaking (#5364)
- Selecting already equipped spells or magic items via hotkey no longer triggers the equip sound to play (#5367)
- 'Scale' argument in levelled creature lists is now taken into account when spawning creatures from such lists (#5369)
- Morrowind legacy madness: Using a key on a trapped door/container now only disarms the trap if the door/container is locked (#5370)
Editor Bug Fixes:
- ?
- Verifier no longer checks for alleged 'race' entries in clothing body parts (#5400)
Miscellaneous:
- ?
- Prevent save-game bloating by using an appropriate fog texture format (#5108)
- Ensure that 'Enchantment autocalc" flag is treated as flag in OpenMW-CS and in our esm tools (#5363)

@ -33,7 +33,6 @@ ${ANALYZE} cmake .. \
-DBUILD_ESSIMPORTER=${BUILD_OPENMW_CS} \
-DBUILD_WIZARD=${BUILD_OPENMW_CS} \
-DBUILD_NIFTEST=${BUILD_OPENMW_CS} \
-DBUILD_MYGUI_PLUGIN=${BUILD_OPENMW_CS} \
-DBUILD_OPENMW_MP=ON \
-DBUILD_BROWSER=ON \
-DBUILD_MASTER=ON \

@ -36,6 +36,7 @@ PLATFORM=""
CONFIGURATION=""
TEST_FRAMEWORK=""
GOOGLE_INSTALL_ROOT=""
INSTALL_PREFIX="."
while [ $# -gt 0 ]; do
ARGSTR=$1
@ -83,9 +84,13 @@ while [ $# -gt 0 ]; do
t )
TEST_FRAMEWORK=true ;;
i )
INSTALL_PREFIX=$(echo "$1" | sed 's;\\;/;g' | sed -E 's;/+;/;g')
shift ;;
h )
cat <<EOF
Usage: $0 [-cdehkpuvV]
Usage: $0 [-cdehkpuvVi]
Options:
-c <Release/Debug>
Set the configuration, can also be set with environment variable CONFIGURATION.
@ -109,6 +114,8 @@ Options:
Produce NMake makefiles instead of a Visual Studio solution.
-V
Run verbosely
-i
CMake install prefix
EOF
exit 0
;;
@ -386,9 +393,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
fi
# Bullet
download "Bullet 2.86" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" \
"Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z"
download "Bullet 2.87" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" \
"Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z"
# FFmpeg
download "FFmpeg 3.2.4" \
@ -526,15 +533,15 @@ fi
cd $DEPS
echo
# Bullet
printf "Bullet 2.86... "
printf "Bullet 2.87... "
{
cd $DEPS_INSTALL
if [ -d Bullet ]; then
printf -- "Exists. (No version checking) "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf Bullet
eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
eval 7z x -y "${DEPS}/Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}" Bullet
fi
export BULLET_ROOT="$(real_pwd)/Bullet"
echo Done.
@ -759,8 +766,8 @@ echo
cd $DEPS_INSTALL/..
echo
echo "Setting up OpenMW build..."
add_cmake_opts -DBUILD_MYGUI_PLUGIN=no \
-DOPENMW_MP_BUILD=on
add_cmake_opts -DOPENMW_MP_BUILD=on
add_cmake_opts -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}"
if [ ! -z $CI ]; then
case $STEP in
components )

@ -19,6 +19,5 @@ cmake \
-D OPENMW_OSX_DEPLOYMENT=TRUE \
-D DESIRED_QT_VERSION=5 \
-D BUILD_ESMTOOL=FALSE \
-D BUILD_MYGUI_PLUGIN=FALSE \
-G"Unix Makefiles" \
..

@ -8,7 +8,6 @@ option(BUILD_ESSIMPORTER "Build ESS (Morrowind save game) importer" ON)
option(BUILD_BSATOOL "Build BSA extractor" ON)
option(BUILD_ESMTOOL "Build ESM inspector" ON)
option(BUILD_NIFTEST "Build nif file tester" ON)
option(BUILD_MYGUI_PLUGIN "Build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
option(BUILD_DOCS "Build documentation." OFF )
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
@ -497,9 +496,6 @@ IF(NOT WIN32 AND NOT APPLE)
IF(BUILD_WIZARD)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-wizard" DESTINATION "${BINDIR}" )
ENDIF(BUILD_WIZARD)
#if(BUILD_MYGUI_PLUGIN)
# INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Plugin_MyGUI_OpenMW_Resources.so" DESTINATION "${LIBDIR}" )
#ENDIF(BUILD_MYGUI_PLUGIN)
# Install licenses
INSTALL(FILES "files/mygui/DejaVu Font License.txt" DESTINATION "${LICDIR}" )
@ -557,11 +553,6 @@ if(WIN32)
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
if(BUILD_MYGUI_PLUGIN)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Debug/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
ENDIF(BUILD_MYGUI_PLUGIN)
IF(DESIRED_QT_VERSION MATCHES 5)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)

@ -25,7 +25,8 @@ add_openmw_dir (mwrender
)
add_openmw_dir (mwinput
inputmanagerimp
actions actionmanager bindingsmanager controllermanager controlswitch
inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager
)
add_openmw_dir (mwgui

@ -137,7 +137,7 @@ bool OMW::Engine::frame(float frametime)
// When the window is minimized, pause the game. Currently this *has* to be here to work around a MyGUI bug.
// If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2),
// and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21)
if (!mEnvironment.getInputManager()->isWindowVisible())
if (!mEnvironment.getWindowManager()->isWindowVisible())
{
mEnvironment.getSoundManager()->pausePlayback();
/*
@ -654,20 +654,20 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
else
gameControllerdb = ""; //if it doesn't exist, pass in an empty string
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
mEnvironment.setInputManager (input);
std::string myguiResources = (mResDir / "mygui").string();
osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
guiRoot->setName("GUI Root");
guiRoot->setNodeMask(MWRender::Mask_GUI);
rootNode->addChild(guiRoot);
MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
MWGui::WindowManager* window = new MWGui::WindowManager(mWindow, mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
mCfgMgr.getLogPath().string() + std::string("/"), myguiResources,
mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts,
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
mEnvironment.setWindowManager (window);
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
mEnvironment.setInputManager (input);
// Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
@ -683,7 +683,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
mEnvironment.getWorld()->setupPlayer();
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
window->setStore(mEnvironment.getWorld()->getStore());
window->initUI();

@ -38,8 +38,6 @@ namespace MWBase
virtual ~InputManager() {}
virtual bool isWindowVisible() = 0;
virtual void update(float dt, bool disableControls, bool disableEvents=false) = 0;
virtual void changeInputMode(bool guiMode) = 0;
@ -47,6 +45,8 @@ namespace MWBase
virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed) = 0;
virtual void setDragDrop(bool dragDrop) = 0;
virtual void setGamepadGuiCursorEnabled(bool enabled) = 0;
virtual void setAttemptJump(bool jumping) = 0;
virtual void toggleControlSwitch (const std::string& sw, bool value) = 0;
virtual bool getControlSwitch (const std::string& sw) = 0;
@ -54,8 +54,6 @@ namespace MWBase
virtual std::string getActionDescription (int action) = 0;
virtual std::string getActionKeyBindingName (int action) = 0;
virtual std::string getActionControllerBindingName (int action) = 0;
virtual std::string sdlControllerAxisToString(int axis) = 0;
virtual std::string sdlControllerButtonToString(int button) = 0;
///Actions available for binding to keyboard buttons
virtual std::vector<int> getActionKeySorting() = 0;
///Actions available for binding to controller buttons
@ -69,10 +67,15 @@ namespace MWBase
/// Returns if the last used input device was a joystick or a keyboard
/// @return true if joystick, false otherwise
virtual bool joystickLastUsed() = 0;
virtual void setJoystickLastUsed(bool enabled) = 0;
virtual int countSavedGameRecords() const = 0;
virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) = 0;
virtual void readRecord(ESM::ESMReader& reader, uint32_t type) = 0;
virtual void resetIdleTime() = 0;
virtual void executeAction(int action) = 0;
};
}

@ -35,7 +35,7 @@ namespace MWBase
virtual ~ScriptManager() {}
virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
virtual bool run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
///< Run the script with the given name (compile first, if not compiled yet)
virtual bool compile (const std::string& name) = 0;

@ -11,6 +11,8 @@
#include "../mwgui/mode.hpp"
#include <components/sdlutil/events.hpp>
namespace Loading
{
class Listener;
@ -86,7 +88,7 @@ namespace SFO
namespace MWBase
{
/// \brief Interface for widnow manager (implemented in MWGui)
class WindowManager
class WindowManager : public SDLUtil::WindowListener
{
WindowManager (const WindowManager&);
///< not implemented
@ -344,8 +346,6 @@ namespace MWBase
virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed) = 0;
virtual void windowResized(int x, int y) = 0;
virtual void executeInConsole (const std::string& path) = 0;
/*
@ -446,6 +446,11 @@ namespace MWBase
virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) = 0;
virtual bool injectKeyRelease(MyGUI::KeyCode key) = 0;
virtual void windowVisibilityChange(bool visible) = 0;
virtual void windowResized(int x, int y) = 0;
virtual void windowClosed() = 0;
virtual bool isWindowVisible() = 0;
};
}

@ -200,6 +200,8 @@ namespace MWBase
virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0;
///< Search is limited to the active cells.
virtual MWWorld::Ptr searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) = 0;
/*
Start of tes3mp addition

@ -240,6 +240,7 @@ namespace MWGui
MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize),
MyGUI::Align::Top | MyGUI::Align::Left);
fog->setDepth(Local_FogLayer);
fog->setColour(MyGUI::Colour(0, 0, 0));
map->setNeedMouseFocus(false);
fog->setNeedMouseFocus(false);

@ -33,6 +33,7 @@
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlcursormanager.hpp>
#include <components/sdlutil/sdlvideowrapper.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
@ -139,7 +140,7 @@ namespace MWGui
{
WindowManager::WindowManager(
osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,
ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& userDataPath)
: mOldUpdateMask(0)
@ -208,6 +209,7 @@ namespace MWGui
, mEncoding(encoding)
, mFontHeight(16)
, mVersionDescription(versionDescription)
, mWindowVisible(true)
{
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale);
@ -300,6 +302,10 @@ namespace MWGui
MyGUI::ClipboardManager::getInstance().eventClipboardRequested += MyGUI::newDelegate(this, &WindowManager::onClipboardRequested);
mShowOwned = Settings::Manager::getInt("show owned", "Game");
mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer);
mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"),
Settings::Manager::getFloat("contrast", "Video"));
}
void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version)
@ -674,6 +680,7 @@ namespace MWGui
mGuiPlatform->shutdown();
delete mGuiPlatform;
delete mVideoWrapper;
}
catch(const MyGUI::Exception& e)
{
@ -946,7 +953,7 @@ namespace MWGui
mMessageBoxManager->onFrame(dt);
MWBase::Environment::get().getInputManager()->update(dt, true, false);
if (!MWBase::Environment::get().getInputManager()->isWindowVisible())
if (!mWindowVisible)
OpenThreads::Thread::microSleep(5000);
else
{
@ -1312,6 +1319,7 @@ namespace MWGui
{
mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI"));
bool changeRes = false;
for (const auto& setting : changed)
{
if (setting.first == "HUD" && setting.second == "crosshair")
@ -1320,11 +1328,38 @@ namespace MWGui
mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI");
else if (setting.first == "GUI" && setting.second == "menu transparency")
setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI"));
else if (setting.first == "Video" && (
setting.second == "resolution x"
|| setting.second == "resolution y"
|| setting.second == "fullscreen"
|| setting.second == "window border"))
changeRes = true;
else if (setting.first == "Video" && setting.second == "vsync")
mVideoWrapper->setSyncToVBlank(Settings::Manager::getBool("vsync", "Video"));
else if (setting.first == "Video" && (setting.second == "gamma" || setting.second == "contrast"))
mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"),
Settings::Manager::getFloat("contrast", "Video"));
}
if (changeRes)
{
mVideoWrapper->setVideoMode(Settings::Manager::getInt("resolution x", "Video"),
Settings::Manager::getInt("resolution y", "Video"),
Settings::Manager::getBool("fullscreen", "Video"),
Settings::Manager::getBool("window border", "Video"));
}
}
void WindowManager::windowResized(int x, int y)
{
// Note: this is a side effect of resolution change or window resize.
// There is no need to track these changes.
Settings::Manager::setInt("resolution x", "Video", x);
Settings::Manager::setInt("resolution y", "Video", y);
Settings::Manager::resetPendingChange("resolution x", "Video");
Settings::Manager::resetPendingChange("resolution y", "Video");
mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y);
// scaled size
@ -1354,9 +1389,27 @@ namespace MWGui
for (WindowBase* window : mWindows)
window->onResChange(x, y);
// We should reload TrueType fonts to fit new resolution
loadUserFonts();
// TODO: check if any windows are now off-screen and move them back if so
}
bool WindowManager::isWindowVisible()
{
return mWindowVisible;
}
void WindowManager::windowVisibilityChange(bool visible)
{
mWindowVisible = visible;
}
void WindowManager::windowClosed()
{
MWBase::Environment::get().getStateManager()->requestQuit();
}
void WindowManager::onCursorChange(const std::string &name)
{
mCursorManager->cursorChanged(name);
@ -2054,7 +2107,7 @@ namespace MWGui
MWBase::Environment::get().getInputManager()->update(dt, true, false);
if (!MWBase::Environment::get().getInputManager()->isWindowVisible())
if (!mWindowVisible)
{
mVideoWidget->pause();
OpenThreads::Thread::microSleep(5000);

@ -15,6 +15,7 @@
#include "../mwworld/ptr.hpp"
#include <components/sdlutil/events.hpp>
#include <components/settings/settings.hpp>
#include <components/to_utf8/to_utf8.hpp>
@ -70,6 +71,7 @@ namespace SceneUtil
namespace SDLUtil
{
class SDLCursorManager;
class VideoWrapper;
}
namespace osgMyGUI
@ -124,13 +126,14 @@ namespace MWGui
class JailScreen;
class KeyboardNavigation;
class WindowManager : public MWBase::WindowManager
class WindowManager :
public MWBase::WindowManager
{
public:
typedef std::pair<std::string, int> Faction;
typedef std::vector<Faction> FactionList;
WindowManager(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,
ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& localPath);
virtual ~WindowManager();
@ -372,7 +375,10 @@ namespace MWGui
virtual void processChangedSettings(const Settings::CategorySettingVector& changed);
virtual void windowVisibilityChange(bool visible);
virtual void windowResized(int x, int y);
virtual void windowClosed();
virtual bool isWindowVisible();
virtual void executeInConsole (const std::string& path);
@ -626,10 +632,14 @@ namespace MWGui
std::string mVersionDescription;
bool mWindowVisible;
MWGui::TextColours mTextColours;
std::unique_ptr<KeyboardNavigation> mKeyboardNavigation;
SDLUtil::VideoWrapper* mVideoWrapper;
/**
* Called when MyGUI tries to retrieve a tag's value. Tags must be denoted in #{tag} notation and will be replaced upon setting a user visible text/property.
* Supported syntax:

@ -0,0 +1,665 @@
#include "actionmanager.hpp"
#include <MyGUI_InputManager.h>
#include <SDL_keyboard.h>
#include <components/settings/settings.hpp>
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/LocalPlayer.hpp"
/*
End of tes3mp addition
*/
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "actions.hpp"
#include "bindingsmanager.hpp"
namespace MWInput
{
const float ZOOM_SCALE = 120.f; /// Used for scrolling camera in and out
ActionManager::ActionManager(BindingsManager* bindingsManager,
osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
osg::ref_ptr<osgViewer::Viewer> viewer,
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler)
: mBindingsManager(bindingsManager)
, mViewer(viewer)
, mScreenCaptureHandler(screenCaptureHandler)
, mScreenCaptureOperation(screenCaptureOperation)
, mAlwaysRunActive(Settings::Manager::getBool("always run", "Input"))
, mSneaking(false)
, mAttemptJump(false)
, mOverencumberedMessageDelay(0.f)
, mPreviewPOVDelay(0.f)
, mTimeIdle(0.f)
{
}
void ActionManager::update(float dt, bool triedToMove)
{
// 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;
mOverencumberedMessageDelay = 0.f;
}
// 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();
mOverencumberedMessageDelay -= dt;
if (playerPtr.getClass().getEncumbrance(playerPtr) > playerPtr.getClass().getCapacity(playerPtr))
{
player.setAutoMove (false);
if (mOverencumberedMessageDelay <= 0)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage59}");
mOverencumberedMessageDelay = 1.0;
}
}
}
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch"))
{
if (mBindingsManager->actionIsActive(A_TogglePOV))
{
if (mPreviewPOVDelay <= 0.5 &&
(mPreviewPOVDelay += dt) > 0.5)
{
mPreviewPOVDelay = 1.f;
MWBase::Environment::get().getWorld()->togglePreviewMode(true);
}
}
else
{
//disable preview mode
MWBase::Environment::get().getWorld()->togglePreviewMode(false);
if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5)
{
MWBase::Environment::get().getWorld()->togglePOV();
}
mPreviewPOVDelay = 0.f;
}
}
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 = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25;
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) ||
mBindingsManager->actionIsActive(A_TogglePOV) ||
mBindingsManager->actionIsActive(A_ZoomIn) ||
mBindingsManager->actionIsActive(A_ZoomOut))
{
resetIdleTime();
}
else
{
updateIdleTime(dt);
}
mAttemptJump = false;
}
void ActionManager::resetIdleTime()
{
if (mTimeIdle < 0)
MWBase::Environment::get().getWorld()->toggleVanityMode(false);
mTimeIdle = 0.f;
}
void ActionManager::updateIdleTime(float dt)
{
static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fVanityDelay")->mValue.getFloat();
if (mTimeIdle >= 0.f)
mTimeIdle += dt;
if (mTimeIdle > vanityDelay)
{
MWBase::Environment::get().getWorld()->toggleVanityMode(true);
mTimeIdle = -1.f;
}
}
void ActionManager::executeAction(int action)
{
// trigger action activated
switch (action)
{
case A_GameMenu:
toggleMainMenu ();
break;
case A_Screenshot:
screenshot();
break;
case A_Inventory:
toggleInventory ();
break;
case A_Console:
toggleConsole ();
break;
case A_Activate:
MWBase::Environment::get().getInputManager()->resetIdleTime();
activate();
break;
case A_MoveLeft:
case A_MoveRight:
case A_MoveForward:
case A_MoveBackward:
handleGuiArrowKey(action);
break;
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;
case A_QuickKey2:
quickKey(2);
break;
case A_QuickKey3:
quickKey(3);
break;
case A_QuickKey4:
quickKey(4);
break;
case A_QuickKey5:
quickKey(5);
break;
case A_QuickKey6:
quickKey(6);
break;
case A_QuickKey7:
quickKey(7);
break;
case A_QuickKey8:
quickKey(8);
break;
case A_QuickKey9:
quickKey(9);
break;
case A_QuickKey10:
quickKey(10);
break;
case A_QuickKeysMenu:
showQuickKeysMenu();
break;
case A_ToggleHUD:
MWBase::Environment::get().getWindowManager()->toggleHud();
break;
case A_ToggleDebug:
MWBase::Environment::get().getWindowManager()->toggleDebugWindow();
break;
case A_ZoomIn:
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch") && MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols") && !MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->setCameraDistance(ZOOM_SCALE, true, true);
break;
case A_ZoomOut:
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch") && MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols") && !MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->setCameraDistance(-ZOOM_SCALE, true, true);
break;
case A_QuickSave:
quickSave();
break;
case A_QuickLoad:
quickLoad();
break;
case A_CycleSpellLeft:
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic))
MWBase::Environment::get().getWindowManager()->cycleSpell(false);
break;
case A_CycleSpellRight:
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic))
MWBase::Environment::get().getWindowManager()->cycleSpell(true);
break;
case A_CycleWeaponLeft:
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
MWBase::Environment::get().getWindowManager()->cycleWeapon(false);
break;
case A_CycleWeaponRight:
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->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;
}
}
bool ActionManager::checkAllowedToUseItems() const
{
MWWorld::Ptr player = MWMechanics::getPlayer();
if (player.getClass().getNpcStats(player).isWerewolf())
{
// Cannot use items or spells while in werewolf form
MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}");
return false;
}
return true;
}
void ActionManager::screenshot()
{
bool regularScreenshot = true;
std::string settingStr;
settingStr = Settings::Manager::getString("screenshot type","Video");
regularScreenshot = settingStr.size() == 0 || settingStr.compare("regular") == 0;
if (regularScreenshot)
{
mScreenCaptureHandler->setFramesToCapture(1);
mScreenCaptureHandler->captureNextFrame(*mViewer);
}
else
{
osg::ref_ptr<osg::Image> screenshot (new osg::Image);
if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get(), settingStr))
{
(*mScreenCaptureOperation) (*(screenshot.get()), 0);
// FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason
}
}
}
void ActionManager::toggleMainMenu()
{
if (MyGUI::InputManager::getInstance().isModalAny())
{
MWBase::Environment::get().getWindowManager()->exitCurrentModal();
return;
}
if (MWBase::Environment::get().getWindowManager()->isConsoleMode())
{
MWBase::Environment::get().getWindowManager()->toggleConsole();
return;
}
if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu
{
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
}
else //Close current GUI
{
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
}
}
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())
MWBase::Environment::get().getStateManager()->quickLoad();
}
void ActionManager::quickSave()
{
if (!MyGUI::InputManager::getInstance().isModalAny())
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"))
return;
if (!MWBase::Environment::get().getWindowManager()->getRestEnabled() || MWBase::Environment::get().getWindowManager()->isGuiMode())
return;
/*
Start of tes3mp addition
Ignore attempts to rest if the player has not logged in on the server yet
Set LocalPlayer's isUsingBed to be able to distinguish bed use from regular rest
menu use
*/
if (!mwmp::Main::get().getLocalPlayer()->isLoggedIn())
return;
mwmp::Main::get().getLocalPlayer()->isUsingBed = false;
/*
End of tes3mp addition
*/
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Rest); //Open rest GUI
}
void ActionManager::toggleInventory()
{
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
return;
if (MyGUI::InputManager::getInstance().isModalAny())
return;
if (MWBase::Environment::get().getWindowManager()->isConsoleMode())
return;
/*
Start of tes3mp addition
Ignore attempts to open inventory if the player has not logged in on the server yet
*/
if (!mwmp::Main::get().getLocalPlayer()->isLoggedIn())
return;
/*
End of tes3mp addition
*/
// Toggle between game mode and inventory mode
if(!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Inventory);
else
{
MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode();
if(mode == MWGui::GM_Inventory || mode == MWGui::GM_Container)
MWBase::Environment::get().getWindowManager()->popGuiMode();
}
// .. but don't touch any other mode, except container.
}
void ActionManager::toggleConsole()
{
if (MyGUI::InputManager::getInstance().isModalAny())
return;
/*
Start of tes3mp addition
If a player's console is disabled by the server, go no further
*/
if (!mwmp::Main::get().getLocalPlayer()->consoleAllowed)
return;
/*
End of tes3mp addition
*/
MWBase::Environment::get().getWindowManager()->toggleConsole();
}
void ActionManager::toggleJournal()
{
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
return;
if (MyGUI::InputManager::getInstance ().isModalAny())
return;
if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal
&& MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu
&& MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings
&& MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
{
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal);
}
else if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Journal))
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Journal);
}
}
void ActionManager::quickKey (int index)
{
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols") || !MWBase::Environment::get().getInputManager()->getControlSwitch("playerfighting") || !MWBase::Environment::get().getInputManager()->getControlSwitch("playermagic"))
return;
if (!checkAllowedToUseItems())
return;
if (MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")!=-1)
return;
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWindowManager()->activateQuickKey (index);
}
void ActionManager::showQuickKeysMenu()
{
if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()
&& MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1)
{
if (!checkAllowedToUseItems())
return;
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu);
}
else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu)
{
while (MyGUI::InputManager::getInstance().isModalAny())
{ //Handle any open Modal windows
MWBase::Environment::get().getWindowManager()->exitCurrentModal();
}
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); //And handle the actual main window
}
}
void ActionManager::activate()
{
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed();
if (!SDL_IsTextInputActive() && !mBindingsManager->isLeftOrRightButton(A_Activate, joystickUsed))
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0, false);
}
else if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
{
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
player.activate();
}
}
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);
}
void ActionManager::toggleSneaking()
{
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return;
if (!MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols")) return;
mSneaking = !mSneaking;
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
player.setSneak(mSneaking);
}
void ActionManager::handleGuiArrowKey(int action)
{
bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed();
// This is currently keyboard-specific code
// TODO: see if GUI controls can be refactored into a single function
if (joystickUsed)
return;
if (SDL_IsTextInputActive())
return;
if (mBindingsManager->isLeftOrRightButton(action, joystickUsed))
return;
MyGUI::KeyCode key;
switch (action)
{
case A_MoveLeft:
key = MyGUI::KeyCode::ArrowLeft;
break;
case A_MoveRight:
key = MyGUI::KeyCode::ArrowRight;
break;
case A_MoveForward:
key = MyGUI::KeyCode::ArrowUp;
break;
case A_MoveBackward:
default:
key = MyGUI::KeyCode::ArrowDown;
break;
}
MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false);
}
}

@ -0,0 +1,78 @@
#ifndef MWINPUT_ACTIONMANAGER_H
#define MWINPUT_ACTIONMANAGER_H
#include <osg/ref_ptr>
#include <osgViewer/ViewerEventHandlers>
namespace osgViewer
{
class Viewer;
class ScreenCaptureHandler;
}
namespace MWInput
{
class BindingsManager;
class ActionManager
{
public:
ActionManager(BindingsManager* bindingsManager,
osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
osg::ref_ptr<osgViewer::Viewer> viewer,
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler);
void update(float dt, bool triedToMove);
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();
void quickKey (int index);
void showQuickKeysMenu();
void resetIdleTime();
bool isAlwaysRunActive() const { return mAlwaysRunActive; };
bool isSneaking() const { return mSneaking; };
void setAttemptJump(bool enabled) { mAttemptJump = enabled; }
float getPreviewDelay() const { return mPreviewPOVDelay; };
private:
void handleGuiArrowKey(int action);
void updateIdleTime(float dt);
BindingsManager* mBindingsManager;
osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
osgViewer::ScreenCaptureHandler::CaptureOperation* mScreenCaptureOperation;
bool mAlwaysRunActive;
bool mSneaking;
bool mAttemptJump;
float mOverencumberedMessageDelay;
float mPreviewPOVDelay;
float mTimeIdle;
};
}
#endif

@ -0,0 +1,79 @@
#ifndef MWINPUT_ACTIONS_H
#define MWINPUT_ACTIONS_H
namespace MWInput
{
enum Actions
{
// please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files
A_GameMenu,
A_Unused,
A_Screenshot, // Take a screenshot
A_Inventory, // Toggle inventory screen
A_Console, // Toggle console screen
A_MoveLeft, // Move player left / right
A_MoveRight,
A_MoveForward, // Forward / Backward
A_MoveBackward,
A_Activate,
A_Use, //Use weapon, spell, etc.
A_Jump,
A_AutoMove, //Toggle Auto-move forward
A_Rest, //Rest
A_Journal, //Journal
A_Weapon, //Draw/Sheath weapon
A_Spell, //Ready/Unready Casting
A_Run, //Run when held
A_CycleSpellLeft, //cycling through spells
A_CycleSpellRight,
A_CycleWeaponLeft, //Cycling through weapons
A_CycleWeaponRight,
A_ToggleSneak, //Toggles Sneak
A_AlwaysRun, //Toggle Walking/Running
A_Sneak,
A_QuickSave,
A_QuickLoad,
A_QuickMenu,
A_ToggleWeapon,
A_ToggleSpell,
A_TogglePOV,
A_QuickKey1,
A_QuickKey2,
A_QuickKey3,
A_QuickKey4,
A_QuickKey5,
A_QuickKey6,
A_QuickKey7,
A_QuickKey8,
A_QuickKey9,
A_QuickKey10,
A_QuickKeysMenu,
A_ToggleHUD,
A_ToggleDebug,
A_LookUpDown, //Joystick look
A_LookLeftRight,
A_MoveForwardBackward,
A_MoveLeftRight,
A_ZoomIn,
A_ZoomOut,
A_Last // Marker for the last item
};
}
#endif

@ -0,0 +1,746 @@
#include "bindingsmanager.hpp"
#include <MyGUI_EditBox.h>
#include <extern/oics/ICSChannelListener.h>
#include <extern/oics/ICSInputControlSystem.h>
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/GUIController.hpp"
/*
End of tes3mp addition
*/
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/player.hpp"
#include "actions.hpp"
#include "sdlmappings.hpp"
namespace MWInput
{
static const int sFakeDeviceId = 1; //As we only support one controller at a time, use a fake deviceID so we don't lose bindings when switching controllers
void clearAllKeyBindings(ICS::InputControlSystem* inputBinder, ICS::Control* control)
{
// right now we don't really need multiple bindings for the same action, so remove all others first
if (inputBinder->getKeyBinding(control, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN)
inputBinder->removeKeyBinding(inputBinder->getKeyBinding(control, ICS::Control::INCREASE));
if (inputBinder->getMouseButtonBinding(control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS)
inputBinder->removeMouseButtonBinding(inputBinder->getMouseButtonBinding(control, ICS::Control::INCREASE));
if (inputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE) != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED)
inputBinder->removeMouseWheelBinding(inputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE));
}
void clearAllControllerBindings(ICS::InputControlSystem* inputBinder, ICS::Control* control)
{
// right now we don't really need multiple bindings for the same action, so remove all others first
if (inputBinder->getJoystickAxisBinding(control, sFakeDeviceId, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN)
inputBinder->removeJoystickAxisBinding(sFakeDeviceId, inputBinder->getJoystickAxisBinding(control, sFakeDeviceId, ICS::Control::INCREASE));
if (inputBinder->getJoystickButtonBinding(control, sFakeDeviceId, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS)
inputBinder->removeJoystickButtonBinding(sFakeDeviceId, inputBinder->getJoystickButtonBinding(control, sFakeDeviceId, ICS::Control::INCREASE));
}
class InputControlSystem : public ICS::InputControlSystem
{
public:
InputControlSystem(const std::string& bindingsFile)
: ICS::InputControlSystem(bindingsFile, true, nullptr, nullptr, A_Last)
{
}
};
class BindingsListener :
public ICS::ChannelListener,
public ICS::DetectingBindingListener
{
public:
BindingsListener(ICS::InputControlSystem* inputBinder, BindingsManager* bindingsManager)
: mInputBinder(inputBinder)
, mBindingsManager(bindingsManager)
, mDetectingKeyboard(false)
{
}
virtual ~BindingsListener() = default;
virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue)
{
int action = channel->getNumber();
mBindingsManager->actionValueChanged(action, currentValue, previousValue);
}
virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, SDL_Scancode key, ICS::Control::ControlChangingDirection direction)
{
//Disallow binding escape key
if (key==SDL_SCANCODE_ESCAPE)
{
//Stop binding if esc pressed
mInputBinder->cancelDetectingBindingState();
MWBase::Environment::get().getWindowManager()->notifyInputActionBound();
return;
}
// Disallow binding reserved keys
if (key == SDL_SCANCODE_F3 || key == SDL_SCANCODE_F4 || key == SDL_SCANCODE_F10)
return;
#ifndef __APPLE__
// Disallow binding Windows/Meta keys
if (key == SDL_SCANCODE_LGUI || key == SDL_SCANCODE_RGUI)
return;
#endif
if (!mDetectingKeyboard)
return;
clearAllKeyBindings(mInputBinder, control);
control->setInitialValue(0.0f);
ICS::DetectingBindingListener::keyBindingDetected(ICS, control, key, direction);
MWBase::Environment::get().getWindowManager()->notifyInputActionBound();
}
virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction)
{
// we don't want mouse movement bindings
return;
}
virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, unsigned int button, ICS::Control::ControlChangingDirection direction)
{
if (!mDetectingKeyboard)
return;
clearAllKeyBindings(mInputBinder, control);
control->setInitialValue(0.0f);
ICS::DetectingBindingListener::mouseButtonBindingDetected(ICS, control, button, direction);
MWBase::Environment::get().getWindowManager()->notifyInputActionBound();
}
virtual void mouseWheelBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, ICS::InputControlSystem::MouseWheelClick click, ICS::Control::ControlChangingDirection direction)
{
if (!mDetectingKeyboard)
return;
clearAllKeyBindings(mInputBinder, control);
control->setInitialValue(0.0f);
ICS::DetectingBindingListener::mouseWheelBindingDetected(ICS, control, click, direction);
MWBase::Environment::get().getWindowManager()->notifyInputActionBound();
}
virtual void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control
, int axis, ICS::Control::ControlChangingDirection direction)
{
//only allow binding to the trigers
if (axis != SDL_CONTROLLER_AXIS_TRIGGERLEFT && axis != SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
return;
if (mDetectingKeyboard)
return;
clearAllControllerBindings(mInputBinder, control);
control->setValue(0.5f); //axis bindings must start at 0.5
control->setInitialValue(0.5f);
ICS::DetectingBindingListener::joystickAxisBindingDetected(ICS, deviceID, control, axis, direction);
MWBase::Environment::get().getWindowManager()->notifyInputActionBound();
}
virtual void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control
, unsigned int button, ICS::Control::ControlChangingDirection direction)
{
if (mDetectingKeyboard)
return;
clearAllControllerBindings(mInputBinder,control);
control->setInitialValue(0.0f);
ICS::DetectingBindingListener::joystickButtonBindingDetected (ICS, deviceID, control, button, direction);
MWBase::Environment::get().getWindowManager()->notifyInputActionBound();
}
void setDetectingKeyboard(bool detecting)
{
mDetectingKeyboard = detecting;
}
private:
ICS::InputControlSystem* mInputBinder;
BindingsManager* mBindingsManager;
bool mDetectingKeyboard;
};
BindingsManager::BindingsManager(const std::string& userFile, bool userFileExists)
: mUserFile(userFile)
, mDragDrop(false)
{
std::string file = userFileExists ? userFile : "";
mInputBinder = new InputControlSystem(file);
mListener = new BindingsListener(mInputBinder, this);
mInputBinder->setDetectingBindingListener(mListener);
loadKeyDefaults();
loadControllerDefaults();
for (int i = 0; i < A_Last; ++i)
{
mInputBinder->getChannel(i)->addListener(mListener);
}
}
void BindingsManager::setDragDrop(bool dragDrop)
{
mDragDrop = dragDrop;
}
BindingsManager::~BindingsManager()
{
mInputBinder->save(mUserFile);
delete mInputBinder;
}
void BindingsManager::update(float dt)
{
// update values of channels (as a result of pressed keys)
mInputBinder->update(dt);
}
bool BindingsManager::isLeftOrRightButton(int action, bool joystick) const
{
int mouseBinding = mInputBinder->getMouseButtonBinding(mInputBinder->getControl(action), ICS::Control::INCREASE);
if (mouseBinding != ICS_MAX_DEVICE_BUTTONS)
return true;
int buttonBinding = mInputBinder->getJoystickButtonBinding(mInputBinder->getControl(action), sFakeDeviceId, ICS::Control::INCREASE);
if (joystick && (buttonBinding == 0 || buttonBinding == 1))
return true;
return false;
}
void BindingsManager::setPlayerControlsEnabled(bool enabled)
{
int playerChannels[] = {A_AutoMove, A_AlwaysRun, A_ToggleWeapon,
A_ToggleSpell, A_Rest, A_QuickKey1, A_QuickKey2,
A_QuickKey3, A_QuickKey4, A_QuickKey5, A_QuickKey6,
A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10,
A_Use, A_Journal};
for(int pc : playerChannels)
{
mInputBinder->getChannel(pc)->setEnabled(enabled);
}
}
float BindingsManager::getActionValue (int id) const
{
return mInputBinder->getChannel(id)->getValue();
}
bool BindingsManager::actionIsActive (int id) const
{
return getActionValue(id) == 1.0;
}
void BindingsManager::loadKeyDefaults (bool force)
{
// using hardcoded key defaults is inevitable, if we want the configuration files to stay valid
// across different versions of OpenMW (in the case where another input action is added)
std::map<int, SDL_Scancode> defaultKeyBindings;
//Gets the Keyvalue from the Scancode; gives the button in the same place reguardless of keyboard format
defaultKeyBindings[A_Activate] = SDL_SCANCODE_SPACE;
defaultKeyBindings[A_MoveBackward] = SDL_SCANCODE_S;
defaultKeyBindings[A_MoveForward] = SDL_SCANCODE_W;
defaultKeyBindings[A_MoveLeft] = SDL_SCANCODE_A;
defaultKeyBindings[A_MoveRight] = SDL_SCANCODE_D;
defaultKeyBindings[A_ToggleWeapon] = SDL_SCANCODE_F;
defaultKeyBindings[A_ToggleSpell] = SDL_SCANCODE_R;
defaultKeyBindings[A_CycleSpellLeft] = SDL_SCANCODE_MINUS;
defaultKeyBindings[A_CycleSpellRight] = SDL_SCANCODE_EQUALS;
defaultKeyBindings[A_CycleWeaponLeft] = SDL_SCANCODE_LEFTBRACKET;
defaultKeyBindings[A_CycleWeaponRight] = SDL_SCANCODE_RIGHTBRACKET;
defaultKeyBindings[A_QuickKeysMenu] = SDL_SCANCODE_F1;
defaultKeyBindings[A_Console] = SDL_SCANCODE_GRAVE;
defaultKeyBindings[A_Run] = SDL_SCANCODE_LSHIFT;
defaultKeyBindings[A_Sneak] = SDL_SCANCODE_LCTRL;
defaultKeyBindings[A_AutoMove] = SDL_SCANCODE_Q;
defaultKeyBindings[A_Jump] = SDL_SCANCODE_E;
defaultKeyBindings[A_Journal] = SDL_SCANCODE_J;
defaultKeyBindings[A_Rest] = SDL_SCANCODE_T;
defaultKeyBindings[A_GameMenu] = SDL_SCANCODE_ESCAPE;
defaultKeyBindings[A_TogglePOV] = SDL_SCANCODE_TAB;
defaultKeyBindings[A_QuickKey1] = SDL_SCANCODE_1;
defaultKeyBindings[A_QuickKey2] = SDL_SCANCODE_2;
defaultKeyBindings[A_QuickKey3] = SDL_SCANCODE_3;
defaultKeyBindings[A_QuickKey4] = SDL_SCANCODE_4;
defaultKeyBindings[A_QuickKey5] = SDL_SCANCODE_5;
defaultKeyBindings[A_QuickKey6] = SDL_SCANCODE_6;
defaultKeyBindings[A_QuickKey7] = SDL_SCANCODE_7;
defaultKeyBindings[A_QuickKey8] = SDL_SCANCODE_8;
defaultKeyBindings[A_QuickKey9] = SDL_SCANCODE_9;
defaultKeyBindings[A_QuickKey10] = SDL_SCANCODE_0;
defaultKeyBindings[A_Screenshot] = SDL_SCANCODE_F12;
defaultKeyBindings[A_ToggleHUD] = SDL_SCANCODE_F11;
defaultKeyBindings[A_ToggleDebug] = SDL_SCANCODE_F10;
defaultKeyBindings[A_AlwaysRun] = SDL_SCANCODE_CAPSLOCK;
defaultKeyBindings[A_QuickSave] = SDL_SCANCODE_F5;
defaultKeyBindings[A_QuickLoad] = SDL_SCANCODE_F9;
std::map<int, int> defaultMouseButtonBindings;
defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT;
defaultMouseButtonBindings[A_Use] = SDL_BUTTON_LEFT;
std::map<int, ICS::InputControlSystem::MouseWheelClick> defaultMouseWheelBindings;
defaultMouseWheelBindings[A_ZoomIn] = ICS::InputControlSystem::MouseWheelClick::UP;
defaultMouseWheelBindings[A_ZoomOut] = ICS::InputControlSystem::MouseWheelClick::DOWN;
for (int i = 0; i < A_Last; ++i)
{
ICS::Control* control;
bool controlExists = mInputBinder->getChannel(i)->getControlsCount() != 0;
if (!controlExists)
{
control = new ICS::Control(std::to_string(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX);
mInputBinder->addControl(control);
control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT);
}
else
{
control = mInputBinder->getChannel(i)->getAttachedControls().front().control;
}
if (!controlExists || force ||
(mInputBinder->getKeyBinding(control, ICS::Control::INCREASE) == SDL_SCANCODE_UNKNOWN
&& mInputBinder->getMouseButtonBinding(control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS
&& mInputBinder->getMouseWheelBinding(control, ICS::Control::INCREASE) == ICS::InputControlSystem::MouseWheelClick::UNASSIGNED))
{
clearAllKeyBindings(mInputBinder, control);
if (defaultKeyBindings.find(i) != defaultKeyBindings.end()
&& (force || !mInputBinder->isKeyBound(defaultKeyBindings[i])))
{
control->setInitialValue(0.0f);
mInputBinder->addKeyBinding(control, defaultKeyBindings[i], ICS::Control::INCREASE);
}
else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()
&& (force || !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i])))
{
control->setInitialValue(0.0f);
mInputBinder->addMouseButtonBinding(control, defaultMouseButtonBindings[i], ICS::Control::INCREASE);
}
else if (defaultMouseWheelBindings.find(i) != defaultMouseWheelBindings.end()
&& (force || !mInputBinder->isMouseWheelBound(defaultMouseWheelBindings[i])))
{
control->setInitialValue(0.f);
mInputBinder->addMouseWheelBinding(control, defaultMouseWheelBindings[i], ICS::Control::INCREASE);
}
if (i == A_LookLeftRight && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_4) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_6))
{
mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_6, ICS::Control::INCREASE);
mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_4, ICS::Control::DECREASE);
}
if (i == A_LookUpDown && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_8) && !mInputBinder->isKeyBound(SDL_SCANCODE_KP_2))
{
mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_2, ICS::Control::INCREASE);
mInputBinder->addKeyBinding(control, SDL_SCANCODE_KP_8, ICS::Control::DECREASE);
}
}
}
}
void BindingsManager::loadControllerDefaults(bool force)
{
// using hardcoded key defaults is inevitable, if we want the configuration files to stay valid
// across different versions of OpenMW (in the case where another input action is added)
std::map<int, int> defaultButtonBindings;
defaultButtonBindings[A_Activate] = SDL_CONTROLLER_BUTTON_A;
defaultButtonBindings[A_ToggleWeapon] = SDL_CONTROLLER_BUTTON_X;
defaultButtonBindings[A_ToggleSpell] = SDL_CONTROLLER_BUTTON_Y;
//defaultButtonBindings[A_QuickButtonsMenu] = SDL_GetButtonFromScancode(SDL_SCANCODE_F1); // Need to implement, should be ToggleSpell(5) AND Wait(9)
defaultButtonBindings[A_Sneak] = SDL_CONTROLLER_BUTTON_LEFTSTICK;
defaultButtonBindings[A_Journal] = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
defaultButtonBindings[A_Rest] = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
defaultButtonBindings[A_TogglePOV] = SDL_CONTROLLER_BUTTON_RIGHTSTICK;
defaultButtonBindings[A_Inventory] = SDL_CONTROLLER_BUTTON_B;
defaultButtonBindings[A_GameMenu] = SDL_CONTROLLER_BUTTON_START;
defaultButtonBindings[A_QuickSave] = SDL_CONTROLLER_BUTTON_GUIDE;
defaultButtonBindings[A_MoveForward] = SDL_CONTROLLER_BUTTON_DPAD_UP;
defaultButtonBindings[A_MoveLeft] = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
defaultButtonBindings[A_MoveBackward] = SDL_CONTROLLER_BUTTON_DPAD_DOWN;
defaultButtonBindings[A_MoveRight] = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
std::map<int, int> defaultAxisBindings;
defaultAxisBindings[A_MoveForwardBackward] = SDL_CONTROLLER_AXIS_LEFTY;
defaultAxisBindings[A_MoveLeftRight] = SDL_CONTROLLER_AXIS_LEFTX;
defaultAxisBindings[A_LookUpDown] = SDL_CONTROLLER_AXIS_RIGHTY;
defaultAxisBindings[A_LookLeftRight] = SDL_CONTROLLER_AXIS_RIGHTX;
defaultAxisBindings[A_Use] = SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
defaultAxisBindings[A_Jump] = SDL_CONTROLLER_AXIS_TRIGGERLEFT;
for (int i = 0; i < A_Last; i++)
{
ICS::Control* control;
bool controlExists = mInputBinder->getChannel(i)->getControlsCount() != 0;
if (!controlExists)
{
float initial;
if (defaultAxisBindings.find(i) == defaultAxisBindings.end())
initial = 0.0f;
else initial = 0.5f;
control = new ICS::Control(std::to_string(i), false, true, initial, ICS::ICS_MAX, ICS::ICS_MAX);
mInputBinder->addControl(control);
control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT);
}
else
{
control = mInputBinder->getChannel(i)->getAttachedControls().front().control;
}
if (!controlExists || force || (mInputBinder->getJoystickAxisBinding(control, sFakeDeviceId, ICS::Control::INCREASE) == ICS::InputControlSystem::UNASSIGNED &&
mInputBinder->getJoystickButtonBinding(control, sFakeDeviceId, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS))
{
clearAllControllerBindings(mInputBinder, control);
if (defaultButtonBindings.find(i) != defaultButtonBindings.end()
&& (force || !mInputBinder->isJoystickButtonBound(sFakeDeviceId, defaultButtonBindings[i])))
{
control->setInitialValue(0.0f);
mInputBinder->addJoystickButtonBinding(control, sFakeDeviceId, defaultButtonBindings[i], ICS::Control::INCREASE);
}
else if (defaultAxisBindings.find(i) != defaultAxisBindings.end() && (force || !mInputBinder->isJoystickAxisBound(sFakeDeviceId, defaultAxisBindings[i])))
{
control->setValue(0.5f);
control->setInitialValue(0.5f);
mInputBinder->addJoystickAxisBinding(control, sFakeDeviceId, defaultAxisBindings[i], ICS::Control::INCREASE);
}
}
}
}
std::string BindingsManager::getActionDescription(int action)
{
switch (action)
{
case A_Screenshot:
return "Screenshot";
case A_ZoomIn:
return "Zoom In";
case A_ZoomOut:
return "Zoom Out";
case A_ToggleHUD:
return "Toggle HUD";
case A_Use:
return "#{sUse}";
case A_Activate:
return "#{sActivate}";
case A_MoveBackward:
return "#{sBack}";
case A_MoveForward:
return "#{sForward}";
case A_MoveLeft:
return "#{sLeft}";
case A_MoveRight:
return "#{sRight}";
case A_ToggleWeapon:
return "#{sReady_Weapon}";
case A_ToggleSpell:
return "#{sReady_Magic}";
case A_CycleSpellLeft:
return "#{sPrevSpell}";
case A_CycleSpellRight:
return "#{sNextSpell}";
case A_CycleWeaponLeft:
return "#{sPrevWeapon}";
case A_CycleWeaponRight:
return "#{sNextWeapon}";
case A_Console:
return "#{sConsoleTitle}";
case A_Run:
return "#{sRun}";
case A_Sneak:
return "#{sCrouch_Sneak}";
case A_AutoMove:
return "#{sAuto_Run}";
case A_Jump:
return "#{sJump}";
case A_Journal:
return "#{sJournal}";
case A_Rest:
return "#{sRestKey}";
case A_Inventory:
return "#{sInventory}";
case A_TogglePOV:
return "#{sTogglePOVCmd}";
case A_QuickKeysMenu:
return "#{sQuickMenu}";
case A_QuickKey1:
return "#{sQuick1Cmd}";
case A_QuickKey2:
return "#{sQuick2Cmd}";
case A_QuickKey3:
return "#{sQuick3Cmd}";
case A_QuickKey4:
return "#{sQuick4Cmd}";
case A_QuickKey5:
return "#{sQuick5Cmd}";
case A_QuickKey6:
return "#{sQuick6Cmd}";
case A_QuickKey7:
return "#{sQuick7Cmd}";
case A_QuickKey8:
return "#{sQuick8Cmd}";
case A_QuickKey9:
return "#{sQuick9Cmd}";
case A_QuickKey10:
return "#{sQuick10Cmd}";
case A_AlwaysRun:
return "#{sAlways_Run}";
case A_QuickSave:
return "#{sQuickSaveCmd}";
case A_QuickLoad:
return "#{sQuickLoadCmd}";
default:
return std::string(); // not configurable
}
}
std::string BindingsManager::getActionKeyBindingName(int action)
{
if (mInputBinder->getChannel(action)->getControlsCount() == 0)
return "#{sNone}";
ICS::Control* c = mInputBinder->getChannel(action)->getAttachedControls().front().control;
SDL_Scancode key = mInputBinder->getKeyBinding(c, ICS::Control::INCREASE);
unsigned int mouse = mInputBinder->getMouseButtonBinding(c, ICS::Control::INCREASE);
ICS::InputControlSystem::MouseWheelClick wheel = mInputBinder->getMouseWheelBinding(c, ICS::Control::INCREASE);
if (key != SDL_SCANCODE_UNKNOWN)
return MyGUI::TextIterator::toTagsString(mInputBinder->scancodeToString(key));
else if (mouse != ICS_MAX_DEVICE_BUTTONS)
return "#{sMouse} " + std::to_string(mouse);
else if (wheel != ICS::InputControlSystem::MouseWheelClick::UNASSIGNED)
switch (wheel)
{
case ICS::InputControlSystem::MouseWheelClick::UP:
return "Mouse Wheel Up";
case ICS::InputControlSystem::MouseWheelClick::DOWN:
return "Mouse Wheel Down";
case ICS::InputControlSystem::MouseWheelClick::RIGHT:
return "Mouse Wheel Right";
case ICS::InputControlSystem::MouseWheelClick::LEFT:
return "Mouse Wheel Left";
default:
return "#{sNone}";
}
else
return "#{sNone}";
}
std::string BindingsManager::getActionControllerBindingName(int action)
{
if (mInputBinder->getChannel(action)->getControlsCount() == 0)
return "#{sNone}";
ICS::Control* c = mInputBinder->getChannel(action)->getAttachedControls().front().control;
if (mInputBinder->getJoystickAxisBinding(c, sFakeDeviceId, ICS::Control::INCREASE) != ICS::InputControlSystem::UNASSIGNED)
return sdlControllerAxisToString(mInputBinder->getJoystickAxisBinding(c, sFakeDeviceId, ICS::Control::INCREASE));
else if (mInputBinder->getJoystickButtonBinding(c, sFakeDeviceId, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS)
return sdlControllerButtonToString(mInputBinder->getJoystickButtonBinding(c, sFakeDeviceId, ICS::Control::INCREASE));
else
return "#{sNone}";
}
std::vector<int> BindingsManager::getActionKeySorting()
{
static const std::vector<int> actions
{
A_MoveForward, A_MoveBackward, A_MoveLeft, A_MoveRight, A_TogglePOV, A_ZoomIn, A_ZoomOut,
A_Run, A_AlwaysRun, A_Sneak, A_Activate, A_Use, A_ToggleWeapon, A_ToggleSpell,
A_CycleSpellLeft, A_CycleSpellRight, A_CycleWeaponLeft, A_CycleWeaponRight, A_AutoMove,
A_Jump, A_Inventory, A_Journal, A_Rest, A_Console, A_QuickSave, A_QuickLoad,
A_ToggleHUD, A_Screenshot, A_QuickKeysMenu, A_QuickKey1, A_QuickKey2, A_QuickKey3,
A_QuickKey4, A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10
};
return actions;
}
std::vector<int> BindingsManager::getActionControllerSorting()
{
static const std::vector<int> actions
{
A_TogglePOV, A_ZoomIn, A_ZoomOut, A_Sneak, A_Activate, A_Use, A_ToggleWeapon, A_ToggleSpell,
A_AutoMove, A_Jump, A_Inventory, A_Journal, A_Rest, A_QuickSave, A_QuickLoad, A_ToggleHUD,
A_Screenshot, A_QuickKeysMenu, A_QuickKey1, A_QuickKey2, A_QuickKey3, A_QuickKey4,
A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10,
A_CycleSpellLeft, A_CycleSpellRight, A_CycleWeaponLeft, A_CycleWeaponRight
};
return actions;
}
void BindingsManager::enableDetectingBindingMode(int action, bool keyboard)
{
mListener->setDetectingKeyboard(keyboard);
ICS::Control* c = mInputBinder->getChannel(action)->getAttachedControls().front().control;
mInputBinder->enableDetectingBindingState(c, ICS::Control::INCREASE);
}
bool BindingsManager::isDetectingBindingState() const
{
return mInputBinder->detectingBindingState();
}
void BindingsManager::mousePressed(const SDL_MouseButtonEvent &arg, int deviceID)
{
mInputBinder->mousePressed(arg, deviceID);
}
void BindingsManager::mouseReleased(const SDL_MouseButtonEvent &arg, int deviceID)
{
mInputBinder->mouseReleased(arg, deviceID);
}
void BindingsManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg)
{
mInputBinder->mouseMoved(arg);
}
void BindingsManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg)
{
mInputBinder->mouseWheelMoved(arg);
}
void BindingsManager::keyPressed(const SDL_KeyboardEvent &arg)
{
/*
Start of tes3mp addition
Pass the pressed key to the multiplayer-specific GUI controller
*/
mwmp::Main::get().getGUIController()->pressedKey(arg.keysym.scancode);
/*
End of tes3mp addition
*/
mInputBinder->keyPressed(arg);
}
void BindingsManager::keyReleased(const SDL_KeyboardEvent &arg)
{
mInputBinder->keyReleased(arg);
}
void BindingsManager::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg)
{
mInputBinder->controllerAdded(deviceID, arg);
}
void BindingsManager::controllerRemoved(const SDL_ControllerDeviceEvent &arg)
{
mInputBinder->controllerRemoved(arg);
}
void BindingsManager::controllerButtonPressed(int deviceID, const SDL_ControllerButtonEvent &arg)
{
mInputBinder->buttonPressed(deviceID, arg);
}
void BindingsManager::controllerButtonReleased(int deviceID, const SDL_ControllerButtonEvent &arg)
{
mInputBinder->buttonReleased(deviceID, arg);
}
void BindingsManager::controllerAxisMoved(int deviceID, const SDL_ControllerAxisEvent &arg)
{
mInputBinder->axisMoved(deviceID, arg);
}
SDL_Scancode BindingsManager::getKeyBinding(int actionId)
{
return mInputBinder->getKeyBinding(mInputBinder->getControl(actionId), ICS::Control::INCREASE);
}
void BindingsManager::actionValueChanged(int action, float currentValue, float previousValue)
{
MWBase::Environment::get().getInputManager()->resetIdleTime();
if (mDragDrop && action != A_GameMenu && action != A_Inventory)
return;
if ((previousValue == 1 || previousValue == 0) && (currentValue==1 || currentValue==0))
{
//Is a normal button press, so don't change it at all
}
//Otherwise only trigger button presses as they go through specific points
else if (previousValue >= 0.8 && currentValue < 0.8)
{
currentValue = 0.0;
previousValue = 1.0;
}
else if (previousValue <= 0.6 && currentValue > 0.6)
{
currentValue = 1.0;
previousValue = 0.0;
}
else
{
//If it's not switching between those values, ignore the channel change.
return;
}
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
{
bool joystickUsed = MWBase::Environment::get().getInputManager()->joystickLastUsed();
if (action == A_Use)
{
if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleWeapon))
action = A_CycleWeaponRight;
else if (joystickUsed && currentValue == 1.0 && actionIsActive(A_ToggleSpell))
action = A_CycleSpellRight;
else
{
/*
Start of tes3mp addition
Prevent players from starting attacks while in the persuasion submenu in dialogue
*/
if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue))
return;
/*
End of tes3mp addition
*/
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);
}
}

@ -0,0 +1,73 @@
#ifndef MWINPUT_MWBINDINGSMANAGER_H
#define MWINPUT_MWBINDINGSMANAGER_H
#include <string>
#include <vector>
#include <components/sdlutil/events.hpp>
namespace MWInput
{
class BindingsListener;
class InputControlSystem;
class BindingsManager
{
public:
BindingsManager(const std::string& userFile, bool userFileExists);
virtual ~BindingsManager();
std::string getActionDescription (int action);
std::string getActionKeyBindingName (int action);
std::string getActionControllerBindingName (int action);
std::vector<int> getActionKeySorting();
std::vector<int> getActionControllerSorting();
void enableDetectingBindingMode (int action, bool keyboard);
bool isDetectingBindingState() const;
void loadKeyDefaults(bool force = false);
void loadControllerDefaults(bool force = false);
void setDragDrop(bool dragDrop);
void update(float dt);
void setPlayerControlsEnabled(bool enabled);
bool isLeftOrRightButton(int action, bool joystick) const;
bool actionIsActive(int id) const;
float getActionValue(int id) const;
void mousePressed(const SDL_MouseButtonEvent &evt, int deviceID);
void mouseReleased(const SDL_MouseButtonEvent &arg, int deviceID);
void mouseMoved(const SDLUtil::MouseMotionEvent &arg);
void mouseWheelMoved(const SDL_MouseWheelEvent &arg);
void keyPressed(const SDL_KeyboardEvent &arg);
void keyReleased(const SDL_KeyboardEvent &arg);
void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg);
void controllerRemoved(const SDL_ControllerDeviceEvent &arg);
void controllerButtonPressed(int deviceID, const SDL_ControllerButtonEvent &arg);
void controllerButtonReleased(int deviceID, const SDL_ControllerButtonEvent &arg);
void controllerAxisMoved(int deviceID, const SDL_ControllerAxisEvent &arg);
SDL_Scancode getKeyBinding(int actionId);
void actionValueChanged(int action, float currentValue, float previousValue);
private:
void setupSDLKeyMappings();
InputControlSystem* mInputBinder;
BindingsListener* mListener;
std::string mUserFile;
bool mDragDrop;
};
}
#endif

@ -0,0 +1,400 @@
#include "controllermanager.hpp"
#include <MyGUI_Button.h>
#include <MyGUI_InputManager.h>
#include <MyGUI_Widget.h>
#include <components/debug/debuglog.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/player.hpp"
#include "actions.hpp"
#include "actionmanager.hpp"
#include "bindingsmanager.hpp"
#include "mousemanager.hpp"
#include "sdlmappings.hpp"
namespace MWInput
{
ControllerManager::ControllerManager(BindingsManager* bindingsManager,
ActionManager* actionManager,
MouseManager* mouseManager,
const std::string& userControllerBindingsFile,
const std::string& controllerBindingsFile)
: mBindingsManager(bindingsManager)
, mActionManager(actionManager)
, mMouseManager(mouseManager)
, mJoystickEnabled (Settings::Manager::getBool("enable controller", "Input"))
, mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input"))
, mInvUiScalingFactor(1.f)
, mSneakToggleShortcutTimer(0.f)
, mGamepadZoom(0)
, mGamepadGuiCursorEnabled(true)
, mControlsDisabled(false)
, mJoystickLastUsed(false)
, mSneakGamepadShortcut(false)
, mGamepadPreviewMode(false)
{
if (!controllerBindingsFile.empty())
{
SDL_GameControllerAddMappingsFromFile(controllerBindingsFile.c_str());
}
if (!userControllerBindingsFile.empty())
{
SDL_GameControllerAddMappingsFromFile(userControllerBindingsFile.c_str());
}
// Open all presently connected sticks
int numSticks = SDL_NumJoysticks();
for (int i = 0; i < numSticks; i++)
{
if (SDL_IsGameController(i))
{
SDL_ControllerDeviceEvent evt;
evt.which = i;
static const int fakeDeviceID = 1;
controllerAdded(fakeDeviceID, evt);
Log(Debug::Info) << "Detected game controller: " << SDL_GameControllerNameForIndex(i);
}
else
{
Log(Debug::Info) << "Detected unusable controller: " << SDL_JoystickNameForIndex(i);
}
}
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
if (uiScale != 0.f)
mInvUiScalingFactor = 1.f / uiScale;
}
void ControllerManager::processChangedSettings(const Settings::CategorySettingVector& changed)
{
for (const auto& setting : changed)
{
if (setting.first == "Input" && setting.second == "enable controller")
mJoystickEnabled = Settings::Manager::getBool("enable controller", "Input");
}
}
bool ControllerManager::update(float dt, bool disableControls)
{
mControlsDisabled = disableControls;
mGamepadPreviewMode = mActionManager->getPreviewDelay() == 1.f;
if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled))
{
float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight) * 2.0f - 1.0f;
float yAxis = mBindingsManager->getActionValue(A_MoveForwardBackward) * 2.0f - 1.0f;
float zAxis = mBindingsManager->getActionValue(A_LookUpDown) * 2.0f - 1.0f;
xAxis *= (1.5f - mBindingsManager->getActionValue(A_Use));
yAxis *= (1.5f - mBindingsManager->getActionValue(A_Use));
// We keep track of our own mouse position, so that moving the mouse while in
// game mode does not move the position of the GUI cursor
float xMove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed;
float yMove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed;
if (xMove != 0 || yMove != 0 || zAxis != 0)
{
int mouseWheelMove = static_cast<int>(-zAxis * dt * 1500.0f);
mMouseManager->injectMouseMove(xMove, yMove, mouseWheelMove);
mMouseManager->warpMouse();
MWBase::Environment::get().getWindowManager()->setCursorActive(true);
}
}
// Disable movement in Gui mode
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|| MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
{
mGamepadZoom = 0;
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 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)
{
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));
}
}
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch"))
{
if (!mBindingsManager->actionIsActive(A_TogglePOV))
mGamepadZoom = 0;
if (mGamepadZoom)
{
MWBase::Environment::get().getWorld()->changeVanityModeScale(mGamepadZoom);
MWBase::Environment::get().getWorld()->setCameraDistance(mGamepadZoom, true, true);
}
}
return triedToMove;
}
void ControllerManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg)
{
if (!mJoystickEnabled || mBindingsManager->isDetectingBindingState())
return;
mJoystickLastUsed = true;
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
if (gamepadToGuiControl(arg))
return;
if (mGamepadGuiCursorEnabled)
{
// Temporary mouse binding until keyboard controls are available:
if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click.
{
bool mousePressSuccess = mMouseManager->injectMouseButtonPress(SDL_BUTTON_LEFT);
if (MyGUI::InputManager::getInstance().getMouseFocusWidget())
{
MyGUI::Button* b = MyGUI::InputManager::getInstance().getMouseFocusWidget()->castType<MyGUI::Button>(false);
if (b && b->getEnabled())
MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
}
mBindingsManager->setPlayerControlsEnabled(!mousePressSuccess);
}
}
}
else
mBindingsManager->setPlayerControlsEnabled(true);
//esc, to leave initial movie screen
auto kc = sdlKeyToMyGUI(SDLK_ESCAPE);
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(kc, 0));
if (!mControlsDisabled)
mBindingsManager->controllerButtonPressed(deviceID, arg);
}
void ControllerManager::buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg)
{
if (mBindingsManager->isDetectingBindingState())
{
mBindingsManager->controllerButtonReleased(deviceID, arg);
return;
}
if (!mJoystickEnabled || mControlsDisabled)
return;
mJoystickLastUsed = true;
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
if (mGamepadGuiCursorEnabled)
{
// Temporary mouse binding until keyboard controls are available:
if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click.
{
bool mousePressSuccess = mMouseManager->injectMouseButtonRelease(SDL_BUTTON_LEFT);
if (mBindingsManager->isDetectingBindingState()) // If the player just triggered binding, don't let button release bind.
return;
mBindingsManager->setPlayerControlsEnabled(!mousePressSuccess);
}
}
}
else
mBindingsManager->setPlayerControlsEnabled(true);
//esc, to leave initial movie screen
auto kc = sdlKeyToMyGUI(SDLK_ESCAPE);
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc));
mBindingsManager->controllerButtonReleased(deviceID, arg);
}
void ControllerManager::axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg)
{
if (!mJoystickEnabled || mControlsDisabled)
return;
mJoystickLastUsed = true;
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
gamepadToGuiControl(arg);
}
else
{
if (mGamepadPreviewMode && arg.value) // Preview Mode Gamepad Zooming
{
if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
{
mGamepadZoom = arg.value * 0.85f / 1000.f;
return; // Do not propagate event.
}
else if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT)
{
mGamepadZoom = -arg.value * 0.85f / 1000.f;
return; // Do not propagate event.
}
}
}
mBindingsManager->controllerAxisMoved(deviceID, arg);
}
void ControllerManager::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg)
{
mBindingsManager->controllerAdded(deviceID, arg);
}
void ControllerManager::controllerRemoved(const SDL_ControllerDeviceEvent &arg)
{
mBindingsManager->controllerRemoved(arg);
}
bool ControllerManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg)
{
// Presumption of GUI mode will be removed in the future.
// MyGUI KeyCodes *may* change.
MyGUI::KeyCode key = MyGUI::KeyCode::None;
switch (arg.button)
{
case SDL_CONTROLLER_BUTTON_DPAD_UP:
key = MyGUI::KeyCode::ArrowUp;
break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
key = MyGUI::KeyCode::ArrowRight;
break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
key = MyGUI::KeyCode::ArrowDown;
break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
key = MyGUI::KeyCode::ArrowLeft;
break;
case SDL_CONTROLLER_BUTTON_A:
// If we are using the joystick as a GUI mouse, A must be handled via mouse.
if (mGamepadGuiCursorEnabled)
return false;
key = MyGUI::KeyCode::Space;
break;
case SDL_CONTROLLER_BUTTON_B:
if (MyGUI::InputManager::getInstance().isModalAny())
MWBase::Environment::get().getWindowManager()->exitCurrentModal();
else
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
return true;
case SDL_CONTROLLER_BUTTON_X:
key = MyGUI::KeyCode::Semicolon;
break;
case SDL_CONTROLLER_BUTTON_Y:
key = MyGUI::KeyCode::Apostrophe;
break;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
key = MyGUI::KeyCode::Period;
break;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
key = MyGUI::KeyCode::Slash;
break;
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
mGamepadGuiCursorEnabled = !mGamepadGuiCursorEnabled;
MWBase::Environment::get().getWindowManager()->setCursorActive(mGamepadGuiCursorEnabled);
return true;
default:
return false;
}
// Some keys will work even when Text Input windows/modals are in focus.
if (SDL_IsTextInputActive())
return false;
MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false);
return true;
}
bool ControllerManager::gamepadToGuiControl(const SDL_ControllerAxisEvent &arg)
{
switch (arg.axis)
{
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
if (arg.value == 32767) // Treat like a button.
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Minus, 0, false);
break;
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
if (arg.value == 32767) // Treat like a button.
MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Equals, 0, false);
break;
case SDL_CONTROLLER_AXIS_LEFTX:
case SDL_CONTROLLER_AXIS_LEFTY:
case SDL_CONTROLLER_AXIS_RIGHTX:
case SDL_CONTROLLER_AXIS_RIGHTY:
// If we are using the joystick as a GUI mouse, process mouse movement elsewhere.
if (mGamepadGuiCursorEnabled)
return false;
break;
default:
return false;
}
return true;
}
}

@ -0,0 +1,66 @@
#ifndef MWINPUT_MWCONTROLLERMANAGER_H
#define MWINPUT_MWCONTROLLERMANAGER_H
#include <string>
#include <components/settings/settings.hpp>
#include <components/sdlutil/events.hpp>
namespace MWInput
{
class ActionManager;
class BindingsManager;
class MouseManager;
class ControllerManager : public SDLUtil::ControllerListener
{
public:
ControllerManager(BindingsManager* bindingsManager,
ActionManager* actionManager,
MouseManager* mouseManager,
const std::string& userControllerBindingsFile,
const std::string& controllerBindingsFile);
virtual ~ControllerManager() = default;
bool update(float dt, bool disableControls);
virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg);
virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg);
virtual void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg);
virtual void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg);
virtual void controllerRemoved(const SDL_ControllerDeviceEvent &arg);
void processChangedSettings(const Settings::CategorySettingVector& changed);
void setJoystickLastUsed(bool enabled) { mJoystickLastUsed = enabled; }
bool joystickLastUsed() { return mJoystickLastUsed; }
void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }
void setGamepadGuiCursorEnabled(bool enabled) { mGamepadGuiCursorEnabled = enabled; }
bool gamepadGuiCursorEnabled() { return mGamepadGuiCursorEnabled; }
private:
// Return true if GUI consumes input.
bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg);
bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg);
BindingsManager* mBindingsManager;
ActionManager* mActionManager;
MouseManager* mMouseManager;
bool mJoystickEnabled;
float mGamepadCursorSpeed;
float mInvUiScalingFactor;
float mSneakToggleShortcutTimer;
float mGamepadZoom;
bool mGamepadGuiCursorEnabled;
bool mControlsDisabled;
bool mJoystickLastUsed;
bool mGuiCursorEnabled;
bool mSneakGamepadShortcut;
bool mGamepadPreviewMode;
};
}
#endif

@ -0,0 +1,99 @@
#include "controlswitch.hpp"
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/controlsstate.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/player.hpp"
namespace MWInput
{
ControlSwitch::ControlSwitch()
{
clear();
}
void ControlSwitch::clear()
{
mSwitches["playercontrols"] = true;
mSwitches["playerfighting"] = true;
mSwitches["playerjumping"] = true;
mSwitches["playerlooking"] = true;
mSwitches["playermagic"] = true;
mSwitches["playerviewswitch"] = true;
mSwitches["vanitymode"] = true;
}
bool ControlSwitch::get(const std::string& key)
{
return mSwitches[key];
}
void ControlSwitch::set(const std::string& key, bool value)
{
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
/// \note 7 switches at all, if-else is relevant
if (key == "playercontrols" && !value)
{
player.setLeftRight(0);
player.setForwardBackward(0);
player.setAutoMove(false);
player.setUpDown(0);
}
else if (key == "playerjumping" && !value)
{
/// \fixme maybe crouching at this time
player.setUpDown(0);
}
else if (key == "vanitymode")
{
MWBase::Environment::get().getWorld()->allowVanityMode(value);
}
else if (key == "playerlooking" && !value)
{
MWBase::Environment::get().getWorld()->rotateObject(player.getPlayer(), 0.f, 0.f, 0.f);
}
mSwitches[key] = value;
}
void ControlSwitch::write(ESM::ESMWriter& writer, Loading::Listener& /*progress*/)
{
ESM::ControlsState controls;
controls.mViewSwitchDisabled = !mSwitches["playerviewswitch"];
controls.mControlsDisabled = !mSwitches["playercontrols"];
controls.mJumpingDisabled = !mSwitches["playerjumping"];
controls.mLookingDisabled = !mSwitches["playerlooking"];
controls.mVanityModeDisabled = !mSwitches["vanitymode"];
controls.mWeaponDrawingDisabled = !mSwitches["playerfighting"];
controls.mSpellDrawingDisabled = !mSwitches["playermagic"];
writer.startRecord (ESM::REC_INPU);
controls.save(writer);
writer.endRecord (ESM::REC_INPU);
}
void ControlSwitch::readRecord(ESM::ESMReader& reader, uint32_t type)
{
ESM::ControlsState controls;
controls.load(reader);
set("playerviewswitch", !controls.mViewSwitchDisabled);
set("playercontrols", !controls.mControlsDisabled);
set("playerjumping", !controls.mJumpingDisabled);
set("playerlooking", !controls.mLookingDisabled);
set("vanitymode", !controls.mVanityModeDisabled);
set("playerfighting", !controls.mWeaponDrawingDisabled);
set("playermagic", !controls.mSpellDrawingDisabled);
}
int ControlSwitch::countSavedGameRecords() const
{
return 1;
}
}

@ -0,0 +1,38 @@
#ifndef MWINPUT_CONTROLSWITCH_H
#define MWINPUT_CONTROLSWITCH_H
#include <map>
#include <string>
namespace ESM
{
struct ControlsState;
class ESMReader;
class ESMWriter;
}
namespace Loading
{
class Listener;
}
namespace MWInput
{
class ControlSwitch
{
public:
ControlSwitch();
bool get(const std::string& key);
void set(const std::string& key, bool value);
void clear();
void write(ESM::ESMWriter& writer, Loading::Listener& progress);
void readRecord(ESM::ESMReader& reader, uint32_t type);
int countSavedGameRecords() const;
private:
std::map<std::string, bool> mSwitches;
};
}
#endif

File diff suppressed because it is too large Load Diff

@ -1,22 +1,18 @@
#ifndef MWINPUT_MWINPUTMANAGERIMP_H
#define MWINPUT_MWINPUTMANAGERIMP_H
#include "../mwgui/mode.hpp"
#include <SDL_sensor.h>
#include <osg/ref_ptr>
#include <osgViewer/ViewerEventHandlers>
#include <extern/oics/ICSChannelListener.h>
#include <extern/oics/ICSInputControlSystem.h>
#include <components/settings/settings.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/sdlutil/events.hpp>
#include "../mwbase/inputmanager.hpp"
#include "../mwgui/mode.hpp"
#include "actions.hpp"
namespace MWWorld
{
class Player;
@ -27,51 +23,27 @@ namespace MWBase
class WindowManager;
}
namespace ICS
{
class InputControlSystem;
}
namespace MyGUI
{
struct MouseButton;
}
namespace Files
{
struct ConfigurationManager;
}
namespace SDLUtil
{
class InputWrapper;
class VideoWrapper;
}
namespace osgViewer
{
class Viewer;
class ScreenCaptureHandler;
}
struct SDL_Window;
namespace MWInput
{
const float ZOOM_SCALE = 120.f; /// Used for scrolling camera in and out
class ControlSwitch;
class ActionManager;
class BindingsManager;
class ControllerManager;
class KeyboardManager;
class MouseManager;
class SensorManager;
/**
* @brief Class that handles all input and key bindings for OpenMW.
* @brief Class that provides a high-level API for game input
*/
class InputManager :
public MWBase::InputManager,
public SDLUtil::KeyListener,
public SDLUtil::MouseListener,
public SDLUtil::SensorListener,
public SDLUtil::WindowListener,
public SDLUtil::ControllerListener,
public ICS::ChannelListener,
public ICS::DetectingBindingListener
class InputManager : public MWBase::InputManager
{
public:
InputManager(
@ -85,20 +57,18 @@ namespace MWInput
virtual ~InputManager();
virtual bool isWindowVisible();
/// Clear all savegame-specific data
virtual void clear();
virtual void update(float dt, bool disableControls=false, bool disableEvents=false);
void setPlayer (MWWorld::Player* player) { mPlayer = player; }
virtual void changeInputMode(bool guiMode);
virtual void processChangedSettings(const Settings::CategorySettingVector& changed);
virtual void setDragDrop(bool dragDrop);
virtual void setGamepadGuiCursorEnabled(bool enabled);
virtual void setAttemptJump(bool jumping);
virtual void toggleControlSwitch (const std::string& sw, bool value);
virtual bool getControlSwitch (const std::string& sw);
@ -113,264 +83,42 @@ namespace MWInput
virtual void resetToDefaultKeyBindings();
virtual void resetToDefaultControllerBindings();
virtual bool joystickLastUsed() {return mJoystickLastUsed;}
public:
virtual void keyPressed(const SDL_KeyboardEvent &arg );
virtual void keyReleased( const SDL_KeyboardEvent &arg );
virtual void textInput (const SDL_TextInputEvent &arg);
virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id );
virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id );
virtual void mouseMoved( const SDLUtil::MouseMotionEvent &arg );
virtual void mouseWheelMoved( const SDL_MouseWheelEvent &arg);
virtual void sensorUpdated(const SDL_SensorEvent &arg);
virtual void displayOrientationChanged();
virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg);
virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg);
virtual void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg);
virtual void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg);
virtual void controllerRemoved(const SDL_ControllerDeviceEvent &arg);
virtual void windowVisibilityChange( bool visible );
virtual void windowFocusChange( bool have_focus );
virtual void windowResized (int x, int y);
virtual void windowClosed ();
virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue);
virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, SDL_Scancode key, ICS::Control::ControlChangingDirection direction);
virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction);
virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, unsigned int button, ICS::Control::ControlChangingDirection direction);
virtual void mouseWheelBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, ICS::InputControlSystem::MouseWheelClick click, ICS::Control::ControlChangingDirection direction);
virtual void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control
, int axis, ICS::Control::ControlChangingDirection direction);
virtual void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control
, unsigned int button, ICS::Control::ControlChangingDirection direction);
void clearAllKeyBindings (ICS::Control* control);
void clearAllControllerBindings (ICS::Control* control);
virtual void setJoystickLastUsed(bool enabled);
virtual bool joystickLastUsed();
virtual int countSavedGameRecords() const;
virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress);
virtual void readRecord(ESM::ESMReader& reader, uint32_t type);
private:
enum GyroscopeAxis
{
Unknown = 0,
X = 1,
Y = 2,
Z = 3,
Minus_X = -1,
Minus_Y = -2,
Minus_Z = -3
};
SDL_Window* mWindow;
bool mWindowVisible;
osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
bool mJoystickLastUsed;
MWWorld::Player* mPlayer;
ICS::InputControlSystem* mInputBinder;
SDLUtil::InputWrapper* mInputManager;
SDLUtil::VideoWrapper* mVideoWrapper;
std::string mUserFile;
bool mDragDrop;
bool mGrabCursor;
bool mInvertX;
bool mInvertY;
bool mControlsDisabled;
bool mJoystickEnabled;
float mCameraSensitivity;
float mCameraYMultiplier;
float mPreviewPOVDelay;
float mTimeIdle;
bool mMouseLookEnabled;
bool mGuiCursorEnabled;
bool mGamepadGuiCursorEnabled;
bool mDetectingKeyboard;
virtual void resetIdleTime();
float mOverencumberedMessageDelay;
float mGuiCursorX;
float mGuiCursorY;
int mMouseWheel;
float mGamepadZoom;
bool mUserFileExists;
bool mAlwaysRunActive;
bool mSneakToggles;
float mSneakToggleShortcutTimer;
bool mSneakGamepadShortcut;
bool mSneaking;
bool mAttemptJump;
std::map<std::string, bool> mControlSwitch;
float mInvUiScalingFactor;
float mGamepadCursorSpeed;
float mGyroXSpeed;
float mGyroYSpeed;
float mGyroUpdateTimer;
float mGyroHSensitivity;
float mGyroVSensitivity;
GyroscopeAxis mGyroHAxis;
GyroscopeAxis mGyroVAxis;
float mGyroInputThreshold;
virtual void executeAction(int action);
private:
void convertMousePosForMyGUI(int& x, int& y);
MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button);
virtual std::string sdlControllerAxisToString(int axis);
virtual std::string sdlControllerButtonToString(int button);
void resetIdleTime();
void updateIdleTime(float dt);
void setPlayerControlsEnabled(bool enabled);
void handleGuiArrowKey(int action);
// Return true if GUI consumes input.
bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg);
bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg);
void updateCursorMode();
void updateSensors();
void correctGyroscopeAxes();
GyroscopeAxis mapGyroscopeAxis(const std::string& axis);
bool checkAllowedToUseItems() const;
float getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const;
private:
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();
void quickKey (int index);
void quickKey(int index);
void showQuickKeysMenu();
bool actionIsActive (int id);
void loadKeyDefaults(bool force = false);
void loadControllerDefaults(bool force = false);
int mFakeDeviceID; //As we only support one controller at a time, use a fake deviceID so we don't lose bindings when switching controllers
SDL_Sensor* mGyroscope;
SDLUtil::InputWrapper* mInputWrapper;
private:
enum Actions
{
// please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files
A_GameMenu,
A_Unused,
A_Screenshot, // Take a screenshot
A_Inventory, // Toggle inventory screen
A_Console, // Toggle console screen
A_MoveLeft, // Move player left / right
A_MoveRight,
A_MoveForward, // Forward / Backward
A_MoveBackward,
A_Activate,
A_Use, //Use weapon, spell, etc.
A_Jump,
A_AutoMove, //Toggle Auto-move forward
A_Rest, //Rest
A_Journal, //Journal
A_Weapon, //Draw/Sheath weapon
A_Spell, //Ready/Unready Casting
A_Run, //Run when held
A_CycleSpellLeft, //cycling through spells
A_CycleSpellRight,
A_CycleWeaponLeft, //Cycling through weapons
A_CycleWeaponRight,
A_ToggleSneak, //Toggles Sneak
A_AlwaysRun, //Toggle Walking/Running
A_Sneak,
A_QuickSave,
A_QuickLoad,
A_QuickMenu,
A_ToggleWeapon,
A_ToggleSpell,
A_TogglePOV,
A_QuickKey1,
A_QuickKey2,
A_QuickKey3,
A_QuickKey4,
A_QuickKey5,
A_QuickKey6,
A_QuickKey7,
A_QuickKey8,
A_QuickKey9,
A_QuickKey10,
A_QuickKeysMenu,
A_ToggleHUD,
A_ToggleDebug,
A_LookUpDown, //Joystick look
A_LookLeftRight,
A_MoveForwardBackward,
A_MoveLeftRight,
A_ZoomIn,
A_ZoomOut,
A_Last // Marker for the last item
};
bool mGrabCursor;
ControlSwitch* mControlSwitch;
ActionManager* mActionManager;
BindingsManager* mBindingsManager;
ControllerManager* mControllerManager;
KeyboardManager* mKeyboardManager;
MouseManager* mMouseManager;
SensorManager* mSensorManager;
};
}
#endif

@ -0,0 +1,71 @@
#include "keyboardmanager.hpp"
#include <cctype>
#include <MyGUI_InputManager.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/player.hpp"
#include "actions.hpp"
#include "bindingsmanager.hpp"
#include "sdlmappings.hpp"
namespace MWInput
{
KeyboardManager::KeyboardManager(BindingsManager* bindingsManager)
: mBindingsManager(bindingsManager)
, mControlsDisabled(false)
{
}
void KeyboardManager::textInput(const SDL_TextInputEvent &arg)
{
MyGUI::UString ustring(&arg.text[0]);
MyGUI::UString::utf32string utf32string = ustring.asUTF32();
for (MyGUI::UString::utf32string::const_iterator it = utf32string.begin(); it != utf32string.end(); ++it)
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it);
}
void KeyboardManager::keyPressed(const SDL_KeyboardEvent &arg)
{
// HACK: to make default keybinding for the console work without printing an extra "^" upon closing
// This assumes that SDL_TextInput events always come *after* the key event
// (which is somewhat reasonable, and hopefully true for all SDL platforms)
auto kc = sdlKeyToMyGUI(arg.keysym.sym);
if (mBindingsManager->getKeyBinding(A_Console) == arg.keysym.scancode
&& MWBase::Environment::get().getWindowManager()->isConsoleMode())
SDL_StopTextInput();
bool consumed = false;
if (kc != MyGUI::KeyCode::None && !mBindingsManager->isDetectingBindingState())
{
consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(kc, 0, arg.repeat);
if (SDL_IsTextInputActive() && // Little trick to check if key is printable
(!(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym)))
consumed = true;
mBindingsManager->setPlayerControlsEnabled(!consumed);
}
if (arg.repeat)
return;
if (!mControlsDisabled && !consumed)
mBindingsManager->keyPressed(arg);
MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false);
}
void KeyboardManager::keyReleased(const SDL_KeyboardEvent &arg)
{
MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false);
auto kc = sdlKeyToMyGUI(arg.keysym.sym);
if (!mBindingsManager->isDetectingBindingState())
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc));
mBindingsManager->keyReleased(arg);
}
}

@ -0,0 +1,30 @@
#ifndef MWINPUT_MWKEYBOARDMANAGER_H
#define MWINPUT_MWKEYBOARDMANAGER_H
#include <components/settings/settings.hpp>
#include <components/sdlutil/events.hpp>
namespace MWInput
{
class BindingsManager;
class KeyboardManager : public SDLUtil::KeyListener
{
public:
KeyboardManager(BindingsManager* bindingsManager);
virtual ~KeyboardManager() = default;
virtual void textInput(const SDL_TextInputEvent &arg);
virtual void keyPressed(const SDL_KeyboardEvent &arg);
virtual void keyReleased(const SDL_KeyboardEvent &arg);
void setControlsDisabled(bool disabled) { mControlsDisabled = disabled; }
private:
BindingsManager* mBindingsManager;
bool mControlsDisabled;
};
}
#endif

@ -0,0 +1,227 @@
#include "mousemanager.hpp"
#include <MyGUI_Button.h>
#include <MyGUI_InputManager.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_Widget.h>
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlinputwrapper.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/player.hpp"
#include "actions.hpp"
#include "bindingsmanager.hpp"
#include "sdlmappings.hpp"
namespace MWInput
{
MouseManager::MouseManager(BindingsManager* bindingsManager, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window)
: mInvertX(Settings::Manager::getBool("invert x axis", "Input"))
, mInvertY(Settings::Manager::getBool("invert y axis", "Input"))
, mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input"))
, mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input"))
, mBindingsManager(bindingsManager)
, mInputWrapper(inputWrapper)
, mInvUiScalingFactor(1.f)
, mGuiCursorX(0)
, mGuiCursorY(0)
, mMouseWheel(0)
, mMouseLookEnabled(false)
, mControlsDisabled(false)
, mGuiCursorEnabled(true)
{
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
if (uiScale != 0.f)
mInvUiScalingFactor = 1.f / uiScale;
int w,h;
SDL_GetWindowSize(window, &w, &h);
mGuiCursorX = mInvUiScalingFactor * w / 2.f;
mGuiCursorY = mInvUiScalingFactor * h / 2.f;
}
void MouseManager::processChangedSettings(const Settings::CategorySettingVector& changed)
{
for (const auto& setting : changed)
{
if (setting.first == "Input" && setting.second == "invert x axis")
mInvertX = Settings::Manager::getBool("invert x axis", "Input");
if (setting.first == "Input" && setting.second == "invert y axis")
mInvertY = Settings::Manager::getBool("invert y axis", "Input");
if (setting.first == "Input" && setting.second == "camera sensitivity")
mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input");
}
}
void MouseManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg)
{
mBindingsManager->mouseMoved(arg);
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
input->setJoystickLastUsed(false);
input->resetIdleTime();
if (mGuiCursorEnabled)
{
input->setGamepadGuiCursorEnabled(true);
// We keep track of our own mouse position, so that moving the mouse while in
// game mode does not move the position of the GUI cursor
mGuiCursorX = static_cast<float>(arg.x) * mInvUiScalingFactor;
mGuiCursorY = static_cast<float>(arg.y) * mInvUiScalingFactor;
mMouseWheel = static_cast<int>(arg.z);
MyGUI::InputManager::getInstance().injectMouseMove(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), mMouseWheel);
// FIXME: inject twice to force updating focused widget states (tooltips) resulting from changing the viewport by scroll wheel
MyGUI::InputManager::getInstance().injectMouseMove(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), mMouseWheel);
MWBase::Environment::get().getWindowManager()->setCursorActive(true);
}
if (mMouseLookEnabled && !mControlsDisabled)
{
float x = arg.xrel * mCameraSensitivity * (mInvertX ? -1 : 1) / 256.f;
float y = arg.yrel * mCameraSensitivity * (mInvertY ? -1 : 1) * mCameraYMultiplier / 256.f;
float rot[3];
rot[0] = -y;
rot[1] = 0.0f;
rot[2] = -x;
// Only actually turn player when we're not in vanity mode
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && input->getControlSwitch("playerlooking"))
{
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
player.yaw(x);
player.pitch(y);
}
if (arg.zrel && input->getControlSwitch("playerviewswitch") && input->getControlSwitch("playercontrols")) //Check to make sure you are allowed to zoomout and there is a change
{
MWBase::Environment::get().getWorld()->changeVanityModeScale(static_cast<float>(arg.zrel));
}
}
}
void MouseManager::mouseReleased(const SDL_MouseButtonEvent &arg, Uint8 id)
{
MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false);
if (mBindingsManager->isDetectingBindingState())
{
mBindingsManager->mouseReleased(arg, id);
}
else
{
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode;
if (mBindingsManager->isDetectingBindingState())
return; // don't allow same mouseup to bind as initiated bind
mBindingsManager->setPlayerControlsEnabled(!guiMode);
mBindingsManager->mouseReleased(arg, id);
}
}
void MouseManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg)
{
if (mBindingsManager->isDetectingBindingState() || !mControlsDisabled)
mBindingsManager->mouseWheelMoved(arg);
MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false);
}
void MouseManager::mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id)
{
MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false);
bool guiMode = false;
if (id == SDL_BUTTON_LEFT || id == SDL_BUTTON_RIGHT) // MyGUI only uses these mouse events
{
guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
guiMode = MyGUI::InputManager::getInstance().injectMousePress(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode;
if (MyGUI::InputManager::getInstance().getMouseFocusWidget () != 0)
{
MyGUI::Button* b = MyGUI::InputManager::getInstance().getMouseFocusWidget()->castType<MyGUI::Button>(false);
if (b && b->getEnabled() && id == SDL_BUTTON_LEFT)
{
MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
}
}
MWBase::Environment::get().getWindowManager()->setCursorActive(true);
}
mBindingsManager->setPlayerControlsEnabled(!guiMode);
// Don't trigger any mouse bindings while in settings menu, otherwise rebinding controls becomes impossible
if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings)
mBindingsManager->mousePressed(arg, id);
}
void MouseManager::update(float dt, bool disableControls)
{
mControlsDisabled = disableControls;
if (!mMouseLookEnabled)
return;
float xAxis = mBindingsManager->getActionValue(A_LookLeftRight) * 2.0f - 1.0f;
float yAxis = mBindingsManager->getActionValue(A_LookUpDown) * 2.0f - 1.0f;
if (xAxis == 0 && yAxis == 0)
return;
float rot[3];
rot[0] = yAxis * dt * 1000.0f * mCameraSensitivity * (mInvertY ? -1 : 1) * mCameraYMultiplier / 256.f;
rot[1] = 0.0f;
rot[2] = xAxis * dt * 1000.0f * mCameraSensitivity * (mInvertX ? -1 : 1) / 256.f;
// Only actually turn player when we're not in vanity mode
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
{
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
player.yaw(rot[2]);
player.pitch(rot[0]);
}
MWBase::Environment::get().getInputManager()->resetIdleTime();
}
bool MouseManager::injectMouseButtonPress(Uint8 button)
{
return MyGUI::InputManager::getInstance().injectMousePress(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(button));
}
bool MouseManager::injectMouseButtonRelease(Uint8 button)
{
return MyGUI::InputManager::getInstance().injectMousePress(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(button));
}
void MouseManager::injectMouseMove(int xMove, int yMove, int mouseWheelMove)
{
mGuiCursorX += xMove;
mGuiCursorY += yMove;
mMouseWheel += mouseWheelMove;
const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();
mGuiCursorX = std::max(0.f, std::min(mGuiCursorX, float(viewSize.width - 1)));
mGuiCursorY = std::max(0.f, std::min(mGuiCursorY, float(viewSize.height - 1)));
MyGUI::InputManager::getInstance().injectMouseMove(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), mMouseWheel);
}
void MouseManager::warpMouse()
{
mInputWrapper->warpMouse(static_cast<int>(mGuiCursorX / mInvUiScalingFactor), static_cast<int>(mGuiCursorY / mInvUiScalingFactor));
}
}

@ -0,0 +1,58 @@
#ifndef MWINPUT_MWMOUSEMANAGER_H
#define MWINPUT_MWMOUSEMANAGER_H
#include <components/settings/settings.hpp>
#include <components/sdlutil/events.hpp>
namespace SDLUtil
{
class InputWrapper;
}
namespace MWInput
{
class BindingsManager;
class MouseManager : public SDLUtil::MouseListener
{
public:
MouseManager(BindingsManager* bindingsManager, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window);
virtual ~MouseManager() = default;
void update(float dt, bool disableControls);
virtual void mouseMoved(const SDLUtil::MouseMotionEvent &arg);
virtual void mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id);
virtual void mouseReleased(const SDL_MouseButtonEvent &arg, Uint8 id);
virtual void mouseWheelMoved(const SDL_MouseWheelEvent &arg);
void processChangedSettings(const Settings::CategorySettingVector& changed);
bool injectMouseButtonPress(Uint8 button);
bool injectMouseButtonRelease(Uint8 button);
void injectMouseMove(int xMove, int yMove, int mouseWheelMove);
void warpMouse();
void setMouseLookEnabled(bool enabled) { mMouseLookEnabled = enabled; }
void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }
private:
bool mInvertX;
bool mInvertY;
float mCameraSensitivity;
float mCameraYMultiplier;
BindingsManager* mBindingsManager;
SDLUtil::InputWrapper* mInputWrapper;
float mInvUiScalingFactor;
float mGuiCursorX;
float mGuiCursorY;
int mMouseWheel;
bool mMouseLookEnabled;
bool mControlsDisabled;
bool mGuiCursorEnabled;
};
}
#endif

@ -0,0 +1,218 @@
#include "sdlmappings.hpp"
#include <map>
#include <MyGUI_MouseButton.h>
#include <SDL_gamecontroller.h>
#include <SDL_mouse.h>
namespace MWInput
{
std::string sdlControllerButtonToString(int button)
{
switch(button)
{
case SDL_CONTROLLER_BUTTON_A:
return "A Button";
case SDL_CONTROLLER_BUTTON_B:
return "B Button";
case SDL_CONTROLLER_BUTTON_BACK:
return "Back Button";
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
return "DPad Down";
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
return "DPad Left";
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
return "DPad Right";
case SDL_CONTROLLER_BUTTON_DPAD_UP:
return "DPad Up";
case SDL_CONTROLLER_BUTTON_GUIDE:
return "Guide Button";
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
return "Left Shoulder";
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
return "Left Stick Button";
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
return "Right Shoulder";
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
return "Right Stick Button";
case SDL_CONTROLLER_BUTTON_START:
return "Start Button";
case SDL_CONTROLLER_BUTTON_X:
return "X Button";
case SDL_CONTROLLER_BUTTON_Y:
return "Y Button";
default:
return "Button " + std::to_string(button);
}
}
std::string sdlControllerAxisToString(int axis)
{
switch(axis)
{
case SDL_CONTROLLER_AXIS_LEFTX:
return "Left Stick X";
case SDL_CONTROLLER_AXIS_LEFTY:
return "Left Stick Y";
case SDL_CONTROLLER_AXIS_RIGHTX:
return "Right Stick X";
case SDL_CONTROLLER_AXIS_RIGHTY:
return "Right Stick Y";
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
return "Left Trigger";
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
return "Right Trigger";
default:
return "Axis " + std::to_string(axis);
}
}
MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button)
{
//The right button is the second button, according to MyGUI
if(button == SDL_BUTTON_RIGHT)
button = SDL_BUTTON_MIDDLE;
else if(button == SDL_BUTTON_MIDDLE)
button = SDL_BUTTON_RIGHT;
//MyGUI's buttons are 0 indexed
return MyGUI::MouseButton::Enum(button - 1);
}
void initKeyMap(std::map<SDL_Keycode, MyGUI::KeyCode>& keyMap)
{
keyMap[SDLK_UNKNOWN] = MyGUI::KeyCode::None;
keyMap[SDLK_ESCAPE] = MyGUI::KeyCode::Escape;
keyMap[SDLK_1] = MyGUI::KeyCode::One;
keyMap[SDLK_2] = MyGUI::KeyCode::Two;
keyMap[SDLK_3] = MyGUI::KeyCode::Three;
keyMap[SDLK_4] = MyGUI::KeyCode::Four;
keyMap[SDLK_5] = MyGUI::KeyCode::Five;
keyMap[SDLK_6] = MyGUI::KeyCode::Six;
keyMap[SDLK_7] = MyGUI::KeyCode::Seven;
keyMap[SDLK_8] = MyGUI::KeyCode::Eight;
keyMap[SDLK_9] = MyGUI::KeyCode::Nine;
keyMap[SDLK_0] = MyGUI::KeyCode::Zero;
keyMap[SDLK_MINUS] = MyGUI::KeyCode::Minus;
keyMap[SDLK_EQUALS] = MyGUI::KeyCode::Equals;
keyMap[SDLK_BACKSPACE] = MyGUI::KeyCode::Backspace;
keyMap[SDLK_TAB] = MyGUI::KeyCode::Tab;
keyMap[SDLK_q] = MyGUI::KeyCode::Q;
keyMap[SDLK_w] = MyGUI::KeyCode::W;
keyMap[SDLK_e] = MyGUI::KeyCode::E;
keyMap[SDLK_r] = MyGUI::KeyCode::R;
keyMap[SDLK_t] = MyGUI::KeyCode::T;
keyMap[SDLK_y] = MyGUI::KeyCode::Y;
keyMap[SDLK_u] = MyGUI::KeyCode::U;
keyMap[SDLK_i] = MyGUI::KeyCode::I;
keyMap[SDLK_o] = MyGUI::KeyCode::O;
keyMap[SDLK_p] = MyGUI::KeyCode::P;
keyMap[SDLK_RETURN] = MyGUI::KeyCode::Return;
keyMap[SDLK_a] = MyGUI::KeyCode::A;
keyMap[SDLK_s] = MyGUI::KeyCode::S;
keyMap[SDLK_d] = MyGUI::KeyCode::D;
keyMap[SDLK_f] = MyGUI::KeyCode::F;
keyMap[SDLK_g] = MyGUI::KeyCode::G;
keyMap[SDLK_h] = MyGUI::KeyCode::H;
keyMap[SDLK_j] = MyGUI::KeyCode::J;
keyMap[SDLK_k] = MyGUI::KeyCode::K;
keyMap[SDLK_l] = MyGUI::KeyCode::L;
keyMap[SDLK_SEMICOLON] = MyGUI::KeyCode::Semicolon;
keyMap[SDLK_QUOTE] = MyGUI::KeyCode::Apostrophe;
keyMap[SDLK_BACKQUOTE] = MyGUI::KeyCode::Grave;
keyMap[SDLK_LSHIFT] = MyGUI::KeyCode::LeftShift;
keyMap[SDLK_BACKSLASH] = MyGUI::KeyCode::Backslash;
keyMap[SDLK_z] = MyGUI::KeyCode::Z;
keyMap[SDLK_x] = MyGUI::KeyCode::X;
keyMap[SDLK_c] = MyGUI::KeyCode::C;
keyMap[SDLK_v] = MyGUI::KeyCode::V;
keyMap[SDLK_b] = MyGUI::KeyCode::B;
keyMap[SDLK_n] = MyGUI::KeyCode::N;
keyMap[SDLK_m] = MyGUI::KeyCode::M;
keyMap[SDLK_COMMA] = MyGUI::KeyCode::Comma;
keyMap[SDLK_PERIOD] = MyGUI::KeyCode::Period;
keyMap[SDLK_SLASH] = MyGUI::KeyCode::Slash;
keyMap[SDLK_RSHIFT] = MyGUI::KeyCode::RightShift;
keyMap[SDLK_KP_MULTIPLY] = MyGUI::KeyCode::Multiply;
keyMap[SDLK_LALT] = MyGUI::KeyCode::LeftAlt;
keyMap[SDLK_SPACE] = MyGUI::KeyCode::Space;
keyMap[SDLK_CAPSLOCK] = MyGUI::KeyCode::Capital;
keyMap[SDLK_F1] = MyGUI::KeyCode::F1;
keyMap[SDLK_F2] = MyGUI::KeyCode::F2;
keyMap[SDLK_F3] = MyGUI::KeyCode::F3;
keyMap[SDLK_F4] = MyGUI::KeyCode::F4;
keyMap[SDLK_F5] = MyGUI::KeyCode::F5;
keyMap[SDLK_F6] = MyGUI::KeyCode::F6;
keyMap[SDLK_F7] = MyGUI::KeyCode::F7;
keyMap[SDLK_F8] = MyGUI::KeyCode::F8;
keyMap[SDLK_F9] = MyGUI::KeyCode::F9;
keyMap[SDLK_F10] = MyGUI::KeyCode::F10;
keyMap[SDLK_NUMLOCKCLEAR] = MyGUI::KeyCode::NumLock;
keyMap[SDLK_SCROLLLOCK] = MyGUI::KeyCode::ScrollLock;
keyMap[SDLK_KP_7] = MyGUI::KeyCode::Numpad7;
keyMap[SDLK_KP_8] = MyGUI::KeyCode::Numpad8;
keyMap[SDLK_KP_9] = MyGUI::KeyCode::Numpad9;
keyMap[SDLK_KP_MINUS] = MyGUI::KeyCode::Subtract;
keyMap[SDLK_KP_4] = MyGUI::KeyCode::Numpad4;
keyMap[SDLK_KP_5] = MyGUI::KeyCode::Numpad5;
keyMap[SDLK_KP_6] = MyGUI::KeyCode::Numpad6;
keyMap[SDLK_KP_PLUS] = MyGUI::KeyCode::Add;
keyMap[SDLK_KP_1] = MyGUI::KeyCode::Numpad1;
keyMap[SDLK_KP_2] = MyGUI::KeyCode::Numpad2;
keyMap[SDLK_KP_3] = MyGUI::KeyCode::Numpad3;
keyMap[SDLK_KP_0] = MyGUI::KeyCode::Numpad0;
keyMap[SDLK_KP_PERIOD] = MyGUI::KeyCode::Decimal;
keyMap[SDLK_F11] = MyGUI::KeyCode::F11;
keyMap[SDLK_F12] = MyGUI::KeyCode::F12;
keyMap[SDLK_F13] = MyGUI::KeyCode::F13;
keyMap[SDLK_F14] = MyGUI::KeyCode::F14;
keyMap[SDLK_F15] = MyGUI::KeyCode::F15;
keyMap[SDLK_KP_EQUALS] = MyGUI::KeyCode::NumpadEquals;
keyMap[SDLK_COLON] = MyGUI::KeyCode::Colon;
keyMap[SDLK_KP_ENTER] = MyGUI::KeyCode::NumpadEnter;
keyMap[SDLK_KP_DIVIDE] = MyGUI::KeyCode::Divide;
keyMap[SDLK_SYSREQ] = MyGUI::KeyCode::SysRq;
keyMap[SDLK_RALT] = MyGUI::KeyCode::RightAlt;
keyMap[SDLK_HOME] = MyGUI::KeyCode::Home;
keyMap[SDLK_UP] = MyGUI::KeyCode::ArrowUp;
keyMap[SDLK_PAGEUP] = MyGUI::KeyCode::PageUp;
keyMap[SDLK_LEFT] = MyGUI::KeyCode::ArrowLeft;
keyMap[SDLK_RIGHT] = MyGUI::KeyCode::ArrowRight;
keyMap[SDLK_END] = MyGUI::KeyCode::End;
keyMap[SDLK_DOWN] = MyGUI::KeyCode::ArrowDown;
keyMap[SDLK_PAGEDOWN] = MyGUI::KeyCode::PageDown;
keyMap[SDLK_INSERT] = MyGUI::KeyCode::Insert;
keyMap[SDLK_DELETE] = MyGUI::KeyCode::Delete;
keyMap[SDLK_APPLICATION] = MyGUI::KeyCode::AppMenu;
//The function of the Ctrl and Meta keys are switched on macOS compared to other platforms.
//For instance] = Cmd+C versus Ctrl+C to copy from the system clipboard
#if defined(__APPLE__)
keyMap[SDLK_LGUI] = MyGUI::KeyCode::LeftControl;
keyMap[SDLK_RGUI] = MyGUI::KeyCode::RightControl;
keyMap[SDLK_LCTRL] = MyGUI::KeyCode::LeftWindows;
keyMap[SDLK_RCTRL] = MyGUI::KeyCode::RightWindows;
#else
keyMap[SDLK_LGUI] = MyGUI::KeyCode::LeftWindows;
keyMap[SDLK_RGUI] = MyGUI::KeyCode::RightWindows;
keyMap[SDLK_LCTRL] = MyGUI::KeyCode::LeftControl;
keyMap[SDLK_RCTRL] = MyGUI::KeyCode::RightControl;
#endif
}
MyGUI::KeyCode sdlKeyToMyGUI(SDL_Keycode code)
{
static std::map<SDL_Keycode, MyGUI::KeyCode> keyMap;
if (keyMap.empty())
initKeyMap(keyMap);
MyGUI::KeyCode kc = MyGUI::KeyCode::None;
auto foundKey = keyMap.find(code);
if (foundKey != keyMap.end())
kc = foundKey->second;
return kc;
}
}

@ -0,0 +1,25 @@
#ifndef MWINPUT_SDLMAPPINGS_H
#define MWINPUT_SDLMAPPINGS_H
#include <string>
#include <MyGUI_KeyCode.h>
#include <SDL_keycode.h>
namespace MyGUI
{
struct MouseButton;
}
namespace MWInput
{
std::string sdlControllerButtonToString(int button);
std::string sdlControllerAxisToString(int axis);
MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button);
MyGUI::KeyCode sdlKeyToMyGUI(SDL_Keycode code);
}
#endif

@ -0,0 +1,267 @@
#include "sensormanager.hpp"
#include <components/debug/debuglog.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/player.hpp"
namespace MWInput
{
SensorManager::SensorManager()
: mInvertX(Settings::Manager::getBool("invert x axis", "Input"))
, mInvertY(Settings::Manager::getBool("invert y axis", "Input"))
, mGyroXSpeed(0.f)
, mGyroYSpeed(0.f)
, mGyroUpdateTimer(0.f)
, mGyroHSensitivity(Settings::Manager::getFloat("gyro horizontal sensitivity", "Input"))
, mGyroVSensitivity(Settings::Manager::getFloat("gyro vertical sensitivity", "Input"))
, mGyroHAxis(GyroscopeAxis::Minus_X)
, mGyroVAxis(GyroscopeAxis::Y)
, mGyroInputThreshold(Settings::Manager::getFloat("gyro input threshold", "Input"))
, mGyroscope(nullptr)
, mGuiCursorEnabled(true)
{
init();
}
void SensorManager::init()
{
correctGyroscopeAxes();
updateSensors();
}
SensorManager::~SensorManager()
{
if (mGyroscope != nullptr)
{
SDL_SensorClose(mGyroscope);
mGyroscope = nullptr;
}
}
SensorManager::GyroscopeAxis SensorManager::mapGyroscopeAxis(const std::string& axis)
{
if (axis == "x")
return GyroscopeAxis::X;
else if (axis == "y")
return GyroscopeAxis::Y;
else if (axis == "z")
return GyroscopeAxis::Z;
else if (axis == "-x")
return GyroscopeAxis::Minus_X;
else if (axis == "-y")
return GyroscopeAxis::Minus_Y;
else if (axis == "-z")
return GyroscopeAxis::Minus_Z;
return GyroscopeAxis::Unknown;
}
void SensorManager::correctGyroscopeAxes()
{
if (!Settings::Manager::getBool("enable gyroscope", "Input"))
return;
// Treat setting from config as axes for landscape mode.
// If the device does not support orientation change, do nothing.
// Note: in is unclear how to correct axes for devices with non-standart Z axis direction.
mGyroHAxis = mapGyroscopeAxis(Settings::Manager::getString("gyro horizontal axis", "Input"));
mGyroVAxis = mapGyroscopeAxis(Settings::Manager::getString("gyro vertical axis", "Input"));
SDL_DisplayOrientation currentOrientation = SDL_GetDisplayOrientation(Settings::Manager::getInt("screen", "Video"));
switch (currentOrientation)
{
case SDL_ORIENTATION_UNKNOWN:
return;
case SDL_ORIENTATION_LANDSCAPE:
break;
case SDL_ORIENTATION_LANDSCAPE_FLIPPED:
{
mGyroHAxis = GyroscopeAxis(-mGyroHAxis);
mGyroVAxis = GyroscopeAxis(-mGyroVAxis);
break;
}
case SDL_ORIENTATION_PORTRAIT:
{
GyroscopeAxis oldVAxis = mGyroVAxis;
mGyroVAxis = mGyroHAxis;
mGyroHAxis = GyroscopeAxis(-oldVAxis);
break;
}
case SDL_ORIENTATION_PORTRAIT_FLIPPED:
{
GyroscopeAxis oldVAxis = mGyroVAxis;
mGyroVAxis = GyroscopeAxis(-mGyroHAxis);
mGyroHAxis = oldVAxis;
break;
}
}
}
void SensorManager::updateSensors()
{
if (Settings::Manager::getBool("enable gyroscope", "Input"))
{
int numSensors = SDL_NumSensors();
for (int i = 0; i < numSensors; ++i)
{
if (SDL_SensorGetDeviceType(i) == SDL_SENSOR_GYRO)
{
// It is unclear how to handle several enabled gyroscopes, so use the first one.
// Note: Android registers some gyroscope as two separate sensors, for non-wake-up mode and for wake-up mode.
if (mGyroscope != nullptr)
{
SDL_SensorClose(mGyroscope);
mGyroscope = nullptr;
mGyroXSpeed = mGyroYSpeed = 0.f;
mGyroUpdateTimer = 0.f;
}
// FIXME: SDL2 does not provide a way to configure a sensor update frequency so far.
SDL_Sensor *sensor = SDL_SensorOpen(i);
if (sensor == nullptr)
Log(Debug::Error) << "Couldn't open sensor " << SDL_SensorGetDeviceName(i) << ": " << SDL_GetError();
else
{
mGyroscope = sensor;
break;
}
}
}
}
else
{
if (mGyroscope != nullptr)
{
SDL_SensorClose(mGyroscope);
mGyroscope = nullptr;
mGyroXSpeed = mGyroYSpeed = 0.f;
mGyroUpdateTimer = 0.f;
}
}
}
void SensorManager::processChangedSettings(const Settings::CategorySettingVector& changed)
{
for (const auto& setting : changed)
{
if (setting.first == "Input" && setting.second == "invert x axis")
mInvertX = Settings::Manager::getBool("invert x axis", "Input");
if (setting.first == "Input" && setting.second == "invert y axis")
mInvertY = Settings::Manager::getBool("invert y axis", "Input");
if (setting.first == "Input" && setting.second == "gyro horizontal sensitivity")
mGyroHSensitivity = Settings::Manager::getFloat("gyro horizontal sensitivity", "Input");
if (setting.first == "Input" && setting.second == "gyro vertical sensitivity")
mGyroVSensitivity = Settings::Manager::getFloat("gyro vertical sensitivity", "Input");
if (setting.first == "Input" && setting.second == "enable gyroscope")
init();
if (setting.first == "Input" && setting.second == "gyro horizontal axis")
correctGyroscopeAxes();
if (setting.first == "Input" && setting.second == "gyro vertical axis")
correctGyroscopeAxes();
if (setting.first == "Input" && setting.second == "gyro input threshold")
mGyroInputThreshold = Settings::Manager::getFloat("gyro input threshold", "Input");
}
}
float SensorManager::getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const
{
switch (axis)
{
case GyroscopeAxis::X:
case GyroscopeAxis::Y:
case GyroscopeAxis::Z:
return std::abs(arg.data[0]) >= mGyroInputThreshold ? arg.data[axis-1] : 0.f;
case GyroscopeAxis::Minus_X:
case GyroscopeAxis::Minus_Y:
case GyroscopeAxis::Minus_Z:
return std::abs(arg.data[0]) >= mGyroInputThreshold ? -arg.data[std::abs(axis)-1] : 0.f;
default:
return 0.f;
}
}
void SensorManager::displayOrientationChanged()
{
correctGyroscopeAxes();
}
void SensorManager::sensorUpdated(const SDL_SensorEvent &arg)
{
if (!Settings::Manager::getBool("enable gyroscope", "Input"))
return;
SDL_Sensor *sensor = SDL_SensorFromInstanceID(arg.which);
if (!sensor)
{
Log(Debug::Info) << "Couldn't get sensor for sensor event";
return;
}
switch (SDL_SensorGetType(sensor))
{
case SDL_SENSOR_ACCEL:
break;
case SDL_SENSOR_GYRO:
{
mGyroXSpeed = getGyroAxisSpeed(mGyroHAxis, arg);
mGyroYSpeed = getGyroAxisSpeed(mGyroVAxis, arg);
mGyroUpdateTimer = 0.f;
break;
}
default:
break;
}
}
void SensorManager::update(float dt)
{
if (mGyroXSpeed == 0.f && mGyroYSpeed == 0.f)
return;
if (mGyroUpdateTimer > 0.5f)
{
// More than half of second passed since the last gyroscope update.
// A device more likely was disconnected or switched to the sleep mode.
// Reset current rotation speed and wait for update.
mGyroXSpeed = 0.f;
mGyroYSpeed = 0.f;
mGyroUpdateTimer = 0.f;
return;
}
mGyroUpdateTimer += dt;
if (!mGuiCursorEnabled)
{
float rot[3];
rot[0] = mGyroYSpeed * dt * mGyroVSensitivity * 4 * (mInvertY ? -1 : 1);
rot[1] = 0.0f;
rot[2] = mGyroXSpeed * dt * mGyroHSensitivity * 4 * (mInvertX ? -1 : 1);
// Only actually turn player when we're not in vanity mode
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && MWBase::Environment::get().getInputManager()->getControlSwitch("playerlooking"))
{
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
player.yaw(rot[2]);
player.pitch(rot[0]);
}
MWBase::Environment::get().getInputManager()->resetIdleTime();
}
}
}

@ -0,0 +1,73 @@
#ifndef MWINPUT_MWSENSORMANAGER_H
#define MWINPUT_MWSENSORMANAGER_H
#include <SDL_sensor.h>
#include <components/settings/settings.hpp>
#include <components/sdlutil/events.hpp>
namespace SDLUtil
{
class InputWrapper;
}
namespace MWWorld
{
class Player;
}
namespace MWInput
{
class SensorManager : public SDLUtil::SensorListener
{
public:
SensorManager();
virtual ~SensorManager();
void init();
void update(float dt);
virtual void sensorUpdated(const SDL_SensorEvent &arg);
virtual void displayOrientationChanged();
void processChangedSettings(const Settings::CategorySettingVector& changed);
void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }
private:
enum GyroscopeAxis
{
Unknown = 0,
X = 1,
Y = 2,
Z = 3,
Minus_X = -1,
Minus_Y = -2,
Minus_Z = -3
};
void updateSensors();
void correctGyroscopeAxes();
GyroscopeAxis mapGyroscopeAxis(const std::string& axis);
float getGyroAxisSpeed(GyroscopeAxis axis, const SDL_SensorEvent &arg) const;
bool mInvertX;
bool mInvertY;
float mGyroXSpeed;
float mGyroYSpeed;
float mGyroUpdateTimer;
float mGyroHSensitivity;
float mGyroVSensitivity;
GyroscopeAxis mGyroHAxis;
GyroscopeAxis mGyroVAxis;
float mGyroInputThreshold;
SDL_Sensor* mGyroscope;
bool mGuiCursorEnabled;
};
}
#endif

@ -3,6 +3,7 @@
#include <components/esm/aisequence.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
@ -55,8 +56,9 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte
if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled())
return true;
if (isTargetMagicallyHidden(target))
return true;
if (!MWBase::Environment::get().getWorld()->getLOS(target, actor)
|| !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(target, actor))
return false;
if (target.getClass().getCreatureStats(target).isDead())
return true;

@ -276,8 +276,7 @@ namespace MWMechanics
completeManualWalking(actor, storage);
}
AiWanderStorage::WanderState& wanderState = storage.mState;
if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
if (storage.mState == AiWanderStorage::Wander_MoveNow && storage.mCanWanderAlongPathGrid)
{
// Construct a new path if there isn't one
if(!mPathFinder.isPathConstructed())
@ -293,8 +292,10 @@ namespace MWMechanics
completeManualWalking(actor, storage);
}
if (wanderState == AiWanderStorage::Wander_Walking
&& (isDestinationHidden(actor, mPathFinder.getPath().back())
if (storage.mIsWanderingManually
&& storage.mState == AiWanderStorage::Wander_Walking
&& (mPathFinder.getPathSize() == 0
|| isDestinationHidden(actor, mPathFinder.getPath().back())
|| isAreaOccupiedByOtherActor(actor, mPathFinder.getPath().back())))
completeManualWalking(actor, storage);

@ -2481,8 +2481,12 @@ void CharacterController::update(float duration, bool animationOnly)
}
else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed)
{
float speedmult = speed / mMovementAnimSpeed;
mAnimation->adjustSpeedMult(mCurrentMovement, speedmult);
// Vanilla caps the played animation speed.
const float maxSpeedMult = 10.f;
const float speedMult = speed / mMovementAnimSpeed;
mAnimation->adjustSpeedMult(mCurrentMovement, std::min(maxSpeedMult, speedMult));
// Make sure the actual speed is the "expected" speed even though the animation is slower
scale *= std::max(1.f, speedMult / maxSpeedMult);
}
if (!mSkipAnim)

@ -101,6 +101,9 @@ namespace MWMechanics
float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap, bool checkMagicka)
{
// NB: Base chance is calculated here because the effective school pointer must be filled
float baseChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool);
bool godmode = actor == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
@ -124,7 +127,7 @@ namespace MWMechanics
return 100;
float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude();
float castChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool) + castBonus;
float castChance = baseChance + castBonus;
castChance *= stats.getFatigueTerm();
return std::max(0.f, cap ? std::min(100.f, castChance) : castChance);

@ -5,6 +5,7 @@
#include <components/openmw-mp/Version.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/escape.hpp>
#include "../mwbase/environment.hpp"

@ -7,12 +7,48 @@
#include <LinearMath/btTransform.h>
#include <type_traits>
namespace
{
template <class T>
auto makeHeights(const T* heights, float sqrtVerts)
-> std::enable_if_t<std::is_same<btScalar, T>::value, std::vector<btScalar>>
{
return {};
}
template <class T>
auto makeHeights(const T* heights, float sqrtVerts)
-> std::enable_if_t<!std::is_same<btScalar, T>::value, std::vector<btScalar>>
{
return std::vector<btScalar>(heights, heights + static_cast<std::ptrdiff_t>(sqrtVerts * sqrtVerts));
}
template <class T>
auto getHeights(const T* floatHeights, const std::vector<btScalar>&)
-> std::enable_if_t<std::is_same<btScalar, T>::value, const btScalar*>
{
return floatHeights;
}
template <class T>
auto getHeights(const T*, const std::vector<btScalar>& btScalarHeights)
-> std::enable_if_t<!std::is_same<btScalar, T>::value, const btScalar*>
{
return btScalarHeights.data();
}
}
namespace MWPhysics
{
HeightField::HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject)
: mHeights(makeHeights(heights, sqrtVerts))
{
mShape = new btHeightfieldTerrainShape(
sqrtVerts, sqrtVerts, heights, 1,
sqrtVerts, sqrtVerts,
getHeights(heights, mHeights),
1,
minH, maxH, 2,
PHY_FLOAT, false
);

@ -3,6 +3,10 @@
#include <osg/ref_ptr>
#include <LinearMath/btScalar.h>
#include <vector>
class btCollisionObject;
class btHeightfieldTerrainShape;
@ -27,6 +31,7 @@ namespace MWPhysics
btHeightfieldTerrainShape* mShape;
btCollisionObject* mCollisionObject;
osg::ref_ptr<const osg::Object> mHoldObject;
std::vector<btScalar> mHeights;
void operator=(const HeightField&);
HeightField(const HeightField&);

@ -181,6 +181,10 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
camera->setCullMask(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static);
camera->setNodeMask(Mask_RenderToTexture);
// Disable small feature culling, it's not going to be reliable for this camera
osg::Camera::CullingMode cullingMode = (osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING) & ~(osg::CullStack::SMALL_FEATURE_CULLING);
camera->setCullingMode(cullingMode);
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
stateset->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), osg::StateAttribute::OVERRIDE);
@ -693,12 +697,10 @@ void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm)
return;
}
// TODO: deprecate tga and use raw data instead
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga");
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png");
if (!readerwriter)
{
Log(Debug::Error) << "Error: Unable to load fog, can't find a tga ReaderWriter" ;
Log(Debug::Error) << "Error: Unable to load fog, can't find a png ReaderWriter" ;
return;
}
@ -727,10 +729,10 @@ void LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const
std::ostringstream ostream;
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga");
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png");
if (!readerwriter)
{
Log(Debug::Error) << "Error: Unable to write fog, can't find a tga ReaderWriter";
Log(Debug::Error) << "Error: Unable to write fog, can't find a png ReaderWriter";
return;
}

@ -5,81 +5,169 @@
#include <components/esm/esmwriter.hpp>
#include <components/esm/globalscript.hpp>
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "interpretercontext.hpp"
namespace
{
struct ScriptCreatingVisitor : public boost::static_visitor<ESM::GlobalScript>
{
ESM::GlobalScript operator()(const MWWorld::Ptr &ptr) const
{
ESM::GlobalScript script;
script.mTargetRef.unset();
if (!ptr.isEmpty())
{
if (ptr.getCellRef().hasContentFile())
{
script.mTargetId = ptr.getCellRef().getRefId();
script.mTargetRef = ptr.getCellRef().getRefNum();
}
else if (MWBase::Environment::get().getWorld()->getPlayerPtr() == ptr)
script.mTargetId = ptr.getCellRef().getRefId();
}
return script;
}
ESM::GlobalScript operator()(const std::pair<ESM::RefNum, std::string> &pair) const
{
ESM::GlobalScript script;
script.mTargetId = pair.second;
script.mTargetRef = pair.first;
return script;
}
};
struct PtrGettingVisitor : public boost::static_visitor<const MWWorld::Ptr*>
{
const MWWorld::Ptr* operator()(const MWWorld::Ptr &ptr) const
{
return &ptr;
}
const MWWorld::Ptr* operator()(const std::pair<ESM::RefNum, std::string> &pair) const
{
return nullptr;
}
};
struct PtrResolvingVisitor : public boost::static_visitor<MWWorld::Ptr>
{
MWWorld::Ptr operator()(const MWWorld::Ptr &ptr) const
{
return ptr;
}
MWWorld::Ptr operator()(const std::pair<ESM::RefNum, std::string> &pair) const
{
if (pair.second.empty())
return MWWorld::Ptr();
else if(pair.first.hasContentFile())
return MWBase::Environment::get().getWorld()->searchPtrViaRefNum(pair.second, pair.first);
return MWBase::Environment::get().getWorld()->searchPtr(pair.second, false);
}
};
class MatchPtrVisitor : public boost::static_visitor<bool>
{
const MWWorld::Ptr& mPtr;
public:
MatchPtrVisitor(const MWWorld::Ptr& ptr) : mPtr(ptr) {}
bool operator()(const MWWorld::Ptr &ptr) const
{
return ptr == mPtr;
}
bool operator()(const std::pair<ESM::RefNum, std::string> &pair) const
{
return false;
}
};
}
namespace MWScript
{
GlobalScriptDesc::GlobalScriptDesc() : mRunning (false) {}
const MWWorld::Ptr* GlobalScriptDesc::getPtrIfPresent() const
{
return boost::apply_visitor(PtrGettingVisitor(), mTarget);
}
MWWorld::Ptr GlobalScriptDesc::getPtr()
{
MWWorld::Ptr ptr = boost::apply_visitor(PtrResolvingVisitor(), mTarget);
mTarget = ptr;
return ptr;
}
GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store)
: mStore (store)
{}
void GlobalScripts::addScript (const std::string& name, const std::string& targetId)
void GlobalScripts::addScript (const std::string& name, const MWWorld::Ptr& target)
{
std::map<std::string, GlobalScriptDesc>::iterator iter =
mScripts.find (::Misc::StringUtils::lowerCase (name));
const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));
if (iter==mScripts.end())
{
if (const ESM::Script *script = mStore.get<ESM::Script>().search(name))
{
GlobalScriptDesc desc;
desc.mRunning = true;
desc.mLocals.configure (*script);
desc.mId = targetId;
mScripts.insert (std::make_pair (name, desc));
auto desc = std::make_shared<GlobalScriptDesc>();
MWWorld::Ptr ptr = target;
desc->mTarget = ptr;
desc->mRunning = true;
desc->mLocals.configure (*script);
mScripts.insert (std::make_pair(name, desc));
}
else
{
Log(Debug::Error) << "Failed to add global script " << name << ": script record not found";
}
}
else if (!iter->second.mRunning)
else if (!iter->second->mRunning)
{
iter->second.mRunning = true;
iter->second.mId = targetId;
iter->second->mRunning = true;
MWWorld::Ptr ptr = target;
iter->second->mTarget = ptr;
}
}
void GlobalScripts::removeScript (const std::string& name)
{
std::map<std::string, GlobalScriptDesc>::iterator iter =
mScripts.find (::Misc::StringUtils::lowerCase (name));
const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));
if (iter!=mScripts.end())
iter->second.mRunning = false;
iter->second->mRunning = false;
}
bool GlobalScripts::isRunning (const std::string& name) const
{
std::map<std::string, GlobalScriptDesc>::const_iterator iter =
mScripts.find (::Misc::StringUtils::lowerCase (name));
const auto iter = mScripts.find (::Misc::StringUtils::lowerCase (name));
if (iter==mScripts.end())
return false;
return iter->second.mRunning;
return iter->second->mRunning;
}
void GlobalScripts::run()
{
for (std::map<std::string, GlobalScriptDesc>::iterator iter (mScripts.begin());
iter!=mScripts.end(); ++iter)
for (const auto& script : mScripts)
{
if (iter->second.mRunning)
if (script.second->mRunning)
{
MWWorld::Ptr ptr;
MWScript::InterpreterContext interpreterContext (
&iter->second.mLocals, MWWorld::Ptr(), iter->second.mId);
MWScript::InterpreterContext context(script.second);
/*
Start of tes3mp addition
@ -88,12 +176,13 @@ namespace MWScript
so that packets sent by the Interpreter can have their
origin determined by serverside scripts
*/
interpreterContext.setContextType(Interpreter::Context::SCRIPT_GLOBAL);
context.setContextType(Interpreter::Context::SCRIPT_GLOBAL);
/*
End of tes3mp addition
*/
MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext);
if (!MWBase::Environment::get().getScriptManager()->run(script.first, context))
script.second->mRunning = false;
}
}
}
@ -141,18 +230,15 @@ namespace MWScript
void GlobalScripts::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
{
for (std::map<std::string, GlobalScriptDesc>::const_iterator iter (mScripts.begin());
iter!=mScripts.end(); ++iter)
for (const auto& iter : mScripts)
{
ESM::GlobalScript script;
ESM::GlobalScript script = boost::apply_visitor (ScriptCreatingVisitor(), iter.second->mTarget);
script.mId = iter->first;
script.mId = iter.first;
iter->second.mLocals.write (script.mLocals, iter->first);
iter.second->mLocals.write (script.mLocals, iter.first);
script.mRunning = iter->second.mRunning ? 1 : 0;
script.mTargetId = iter->second.mId;
script.mRunning = iter.second->mRunning ? 1 : 0;
writer.startRecord (ESM::REC_GSCR);
script.save (writer);
@ -167,8 +253,7 @@ namespace MWScript
ESM::GlobalScript script;
script.load (reader);
std::map<std::string, GlobalScriptDesc>::iterator iter =
mScripts.find (script.mId);
auto iter = mScripts.find (script.mId);
if (iter==mScripts.end())
{
@ -176,8 +261,12 @@ namespace MWScript
{
try
{
GlobalScriptDesc desc;
desc.mLocals.configure (*scriptRecord);
auto desc = std::make_shared<GlobalScriptDesc>();
if (!script.mTargetId.empty())
{
desc->mTarget = std::make_pair(script.mTargetRef, script.mTargetId);
}
desc->mLocals.configure (*scriptRecord);
iter = mScripts.insert (std::make_pair (script.mId, desc)).first;
}
@ -194,9 +283,8 @@ namespace MWScript
return true;
}
iter->second.mRunning = script.mRunning!=0;
iter->second.mLocals.read (script.mLocals, script.mId);
iter->second.mId = script.mTargetId;
iter->second->mRunning = script.mRunning!=0;
iter->second->mLocals.read (script.mLocals, script.mId);
return true;
}
@ -207,18 +295,28 @@ namespace MWScript
Locals& GlobalScripts::getLocals (const std::string& name)
{
std::string name2 = ::Misc::StringUtils::lowerCase (name);
std::map<std::string, GlobalScriptDesc>::iterator iter = mScripts.find (name2);
auto iter = mScripts.find (name2);
if (iter==mScripts.end())
{
const ESM::Script *script = mStore.get<ESM::Script>().find (name);
GlobalScriptDesc desc;
desc.mLocals.configure (*script);
auto desc = std::make_shared<GlobalScriptDesc>();
desc->mLocals.configure (*script);
iter = mScripts.insert (std::make_pair (name2, desc)).first;
}
return iter->second.mLocals;
return iter->second->mLocals;
}
void GlobalScripts::updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated)
{
MatchPtrVisitor visitor(base);
for (const auto& script : mScripts)
{
if (boost::apply_visitor (visitor, script.second->mTarget))
script.second->mTarget = updated;
}
}
}

@ -1,17 +1,24 @@
#ifndef GAME_SCRIPT_GLOBALSCRIPTS_H
#define GAME_SCRIPT_GLOBALSCRIPTS_H
#include <boost/variant/variant.hpp>
#include <string>
#include <map>
#include <memory>
#include <utility>
#include <stdint.h>
#include "locals.hpp"
#include "../mwworld/ptr.hpp"
namespace ESM
{
class ESMWriter;
class ESMReader;
struct RefNum;
}
namespace Loading
@ -30,21 +37,25 @@ namespace MWScript
{
bool mRunning;
Locals mLocals;
std::string mId; // ID used to start targeted script (empty if not a targeted script)
boost::variant<MWWorld::Ptr, std::pair<ESM::RefNum, std::string> > mTarget; // Used to start targeted script
GlobalScriptDesc();
const MWWorld::Ptr* getPtrIfPresent() const; // Returns a Ptr if one has been resolved
MWWorld::Ptr getPtr(); // Resolves mTarget to a Ptr and caches the (potentially empty) result
};
class GlobalScripts
{
const MWWorld::ESMStore& mStore;
std::map<std::string, GlobalScriptDesc> mScripts;
std::map<std::string, std::shared_ptr<GlobalScriptDesc> > mScripts;
public:
GlobalScripts (const MWWorld::ESMStore& store);
void addScript (const std::string& name, const std::string& targetId = "");
void addScript (const std::string& name, const MWWorld::Ptr& target = MWWorld::Ptr());
void removeScript (const std::string& name);
@ -70,6 +81,9 @@ namespace MWScript
Locals& getLocals (const std::string& name);
///< If the script \a name has not been added as a global script yet, it is added
/// automatically, but is not set to running state.
void updatePtrs(const MWWorld::Ptr& base, const MWWorld::Ptr& updated);
///< Update the Ptrs stored in mTarget. Should be called after the reference has been moved to a new cell.
};
}

@ -1,7 +1,6 @@
#include "interpretercontext.hpp"
#include <cmath>
#include <stdexcept>
#include <sstream>
#include <components/compiler/locals.hpp>
@ -43,26 +42,6 @@
namespace MWScript
{
MWWorld::Ptr InterpreterContext::getReferenceImp (
const std::string& id, bool activeOnly, bool doThrow)
{
if (!id.empty())
{
return MWBase::Environment::get().getWorld()->getPtr (id, activeOnly);
}
else
{
if (mReference.isEmpty() && !mTargetId.empty())
mReference =
MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false);
if (mReference.isEmpty() && doThrow)
throw std::runtime_error ("no implicit reference");
return mReference;
}
}
/*
Start of tes3mp addition
@ -90,12 +69,11 @@ namespace MWScript
}
else
{
if (mReference.isEmpty() && !mTargetId.empty())
mReference =
MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false);
if (mReference.isEmpty() && mGlobalScriptDesc)
mReference = mGlobalScriptDesc->getPtr();
if (mReference.isEmpty() && doThrow)
throw std::runtime_error ("no implicit reference");
throw MissingImplicitRefError();
return mReference;
}
@ -113,7 +91,7 @@ namespace MWScript
{
const MWWorld::Ptr ptr = getReferenceImp (id, false);
id = ptr.getClass().getScript (ptr);
id = ptr.getClass().getScript (ptr);
ptr.getRefData().setLocals (
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (id));
@ -142,6 +120,8 @@ namespace MWScript
}
}
MissingImplicitRefError::MissingImplicitRefError() : std::runtime_error("no implicit reference") {}
int InterpreterContext::findLocalVariableIndex (const std::string& scriptId,
const std::string& name, char type) const
{
@ -167,27 +147,21 @@ namespace MWScript
throw std::runtime_error (stream.str().c_str());
}
InterpreterContext::InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference)
: mLocals (locals), mReference (reference)
{}
InterpreterContext::InterpreterContext (
MWScript::Locals *locals, const MWWorld::Ptr& reference, const std::string& targetId)
: mLocals (locals), mReference (reference), mTargetId (targetId)
InterpreterContext::InterpreterContext (std::shared_ptr<GlobalScriptDesc> globalScriptDesc)
: mLocals (&(globalScriptDesc->mLocals))
{
// If we run on a reference (local script, dialogue script or console with object
// selected), store the ID of that reference store it so it can be inherited by
// targeted scripts started from this one.
if (targetId.empty() && !reference.isEmpty())
mTargetId = reference.getCellRef().getRefId();
/*
Start of tes3mp addition
Boolean used to check whether value change packets should be sent for the
script being processed by the InterpreterContext
*/
sendPackets = false;
/*
End of tes3mp addition
*/
const MWWorld::Ptr* ptr = globalScriptDesc->getPtrIfPresent();
// A nullptr here signifies that the script's target has not yet been resolved after loading the game.
// Script targets are lazily resolved to MWWorld::Ptrs (which can, upon resolution, be empty)
// because scripts started through dialogue often don't use their implicit target.
if (ptr)
mReference = *ptr;
else
mGlobalScriptDesc = globalScriptDesc;
}
int InterpreterContext::getLocalShort (int index) const
@ -660,7 +634,12 @@ namespace MWScript
void InterpreterContext::startScript (const std::string& name, const std::string& targetId)
{
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, targetId);
MWWorld::Ptr target;
if (targetId.empty())
target = getReference(false);
else
target = getReferenceImp(targetId);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, target);
}
void InterpreterContext::stopScript (const std::string& name)
@ -672,12 +651,7 @@ namespace MWScript
{
// NOTE: id may be empty, indicating an implicit reference
MWWorld::Ptr ref2;
if (id.empty())
ref2 = getReferenceImp();
else
ref2 = MWBase::Environment::get().getWorld()->getPtr(id, false);
MWWorld::Ptr ref2 = getReferenceImp(id);
if (ref2.getContainerStore()) // is the object contained?
{
@ -901,11 +875,6 @@ namespace MWScript
return getReferenceImp ("", true, required);
}
std::string InterpreterContext::getTargetId() const
{
return mTargetId;
}
void InterpreterContext::updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated)
{
if (!mReference.isEmpty() && base == mReference)

@ -1,8 +1,13 @@
#ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H
#define GAME_SCRIPT_INTERPRETERCONTEXT_H
#include <memory>
#include <stdexcept>
#include <components/interpreter/context.hpp>
#include "globalscripts.hpp"
#include "../mwworld/ptr.hpp"
namespace MWSound
@ -19,17 +24,17 @@ namespace MWScript
{
class Locals;
class MissingImplicitRefError : public std::runtime_error
{
public:
MissingImplicitRefError();
};
class InterpreterContext : public Interpreter::Context
{
Locals *mLocals;
mutable MWWorld::Ptr mReference;
std::string mTargetId;
/// If \a id is empty, a reference the script is run from is returned or in case
/// of a non-local script the reference derived from the target ID.
MWWorld::Ptr getReferenceImp (const std::string& id = "", bool activeOnly = false,
bool doThrow=true);
std::shared_ptr<GlobalScriptDesc> mGlobalScriptDesc;
/// If \a id is empty, a reference the script is run from is returned or in case
/// of a non-local script the reference derived from the target ID.
@ -47,17 +52,18 @@ namespace MWScript
char type) const;
public:
InterpreterContext (std::shared_ptr<GlobalScriptDesc> globalScriptDesc);
InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference,
const std::string& targetId = "");
InterpreterContext (MWScript::Locals *locals, const MWWorld::Ptr& reference);
///< The ownership of \a locals is not transferred. 0-pointer allowed.
/*
Start of tes3mp addition
Useful boolean for setting whether scripts send packets
Useful boolean for setting whether scripts send packets, set to false by default
to avoid massive packet spam
*/
bool sendPackets;
bool sendPackets = false;
/*
End of tes3mp addition
*/
@ -177,8 +183,6 @@ namespace MWScript
void updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated);
///< Update the Ptr stored in mReference, if there is one stored there. Should be called after the reference has been moved to a new cell.
virtual std::string getTargetId() const;
};
}

@ -19,6 +19,7 @@
#include "../mwworld/esmstore.hpp"
#include "extensions.hpp"
#include "interpretercontext.hpp"
namespace MWScript
{
@ -88,7 +89,7 @@ namespace MWScript
return false;
}
void ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext)
bool ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext)
{
// compile script
ScriptCollection::iterator iter = mScripts.find (name);
@ -100,7 +101,7 @@ namespace MWScript
// failed -> ignore script from now on.
std::vector<Interpreter::Type_Code> empty;
mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals())));
return;
return false;
}
iter = mScripts.find (name);
@ -118,14 +119,19 @@ namespace MWScript
}
mInterpreter.run (&iter->second.first[0], iter->second.first.size(), interpreterContext);
return true;
}
catch (const MissingImplicitRefError& e)
{
Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what();
}
catch (const std::exception& e)
{
Log(Debug::Error) << "Execution of script " << name << " failed:";
Log(Debug::Error) << e.what();
Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what();
iter->second.first.clear(); // don't execute again.
}
return false;
}
std::pair<int, int> ScriptManager::compileAll()

@ -55,7 +55,7 @@ namespace MWScript
Compiler::Context& compilerContext, int warningsMode,
const std::vector<std::string>& scriptBlacklist);
virtual void run (const std::string& name, Interpreter::Context& interpreterContext);
virtual bool run (const std::string& name, Interpreter::Context& interpreterContext);
///< Run the script with the given name (compile first, if not compiled yet)
virtual bool compile (const std::string& name);

@ -144,6 +144,7 @@ void MWState::StateManager::newGame (bool bypass)
try
{
Log(Debug::Info) << "Starting a new game";
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
MWBase::Environment::get().getWorld()->startNewGame (bypass);
@ -220,6 +221,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
profile.mTimePlayed = mTimePlayed;
profile.mDescription = description;
Log(Debug::Info) << "Making a screenshot for saved game '" << description << "'";;
writeScreenshot(profile.mScreenshot);
if (!slot)
@ -230,6 +232,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
// Make sure the animation state held by references is up to date before saving the game.
MWBase::Environment::get().getMechanicsManager()->persistAnimationStates();
Log(Debug::Info) << "Writing saved game '" << description << "' for character '" << profile.mPlayerName << "'";
// Write to a memory stream first. If there is an exception during the save process, we don't want to trash the
// existing save file we are overwriting.
std::stringstream stream;
@ -324,6 +328,16 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
void MWState::StateManager::quickSave (std::string name)
{
/*
Start of tes3mp change (major)
It should not be possible to quicksave the game in multiplayer, so it has been disabled
*/
return;
/*
End of tes3mp change (major)
*/
if (!(mState==State_Running &&
MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1 // char gen
&& MWBase::Environment::get().getWindowManager()->isSavingAllowed()))
@ -380,6 +394,8 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
{
cleanup();
Log(Debug::Info) << "Reading save file " << boost::filesystem::path(filepath).filename().string();
ESM::ESMReader reader;
reader.open (filepath);
@ -418,6 +434,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
return;
}
mTimePlayed = profile.mTimePlayed;
Log(Debug::Info) << "Loading saved game '" << profile.mDescription << "' for character '" << profile.mPlayerName << "'";
}
break;
@ -526,6 +543,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
else
{
// Cell no longer exists (i.e. changed game files), choose a default cell
Log(Debug::Warning) << "Warning: Player character's cell no longer exists, changing to the default cell";
MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(0,0);
float x,y;
MWBase::Environment::get().getWorld()->indexToPosition(0,0,x,y,false);
@ -565,6 +583,16 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
void MWState::StateManager::quickLoad()
{
/*
Start of tes3mp change (major)
It should not be possible to quickload the game in multiplayer, so it has been disabled
*/
return;
/*
End of tes3mp change (major)
*/
if (Character* currentCharacter = getCurrentCharacter ())
{
if (currentCharacter->begin() == currentCharacter->end())
@ -628,7 +656,7 @@ bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const
if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), *it)
== selectedContentFiles.end())
{
Log(Debug::Warning) << "Warning: Savegame dependency " << *it << " is missing.";
Log(Debug::Warning) << "Warning: Saved game dependency " << *it << " is missing.";
notFound = true;
}
}

@ -5,6 +5,7 @@
#include <components/esm/esmwriter.hpp>
#include <components/esm/defs.hpp>
#include <components/esm/cellstate.hpp>
#include <components/esm/cellref.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/settings/settings.hpp>
@ -291,6 +292,37 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name)
return Ptr();
}
MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& id, const ESM::RefNum& refNum)
{
for (auto& pair : mInteriors)
{
Ptr ptr = getPtr(pair.second, id, refNum);
if (!ptr.isEmpty())
return ptr;
}
for (auto& pair : mExteriors)
{
Ptr ptr = getPtr(pair.second, id, refNum);
if (!ptr.isEmpty())
return ptr;
}
return Ptr();
}
MWWorld::Ptr MWWorld::Cells::getPtr(CellStore& cellStore, const std::string& id, const ESM::RefNum& refNum)
{
if (cellStore.getState() == CellStore::State_Unloaded)
cellStore.preload();
if (cellStore.getState() == CellStore::State_Preloaded)
{
if (cellStore.hasId(id))
cellStore.load();
else
return Ptr();
}
return cellStore.searchViaRefNum(refNum);
}
void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector<MWWorld::Ptr> &out)
{
const MWWorld::Store<ESM::Cell> &cells = mStore.get<ESM::Cell>();

@ -13,6 +13,7 @@ namespace ESM
class ESMWriter;
struct CellId;
struct Cell;
struct RefNum;
}
namespace Loading
@ -41,6 +42,8 @@ namespace MWWorld
Ptr getPtrAndCache (const std::string& name, CellStore& cellStore);
Ptr getPtr(CellStore& cellStore, const std::string& id, const ESM::RefNum& refNum);
void writeCell (ESM::ESMWriter& writer, CellStore& cell) const;
public:
@ -73,6 +76,8 @@ namespace MWWorld
/// @note name must be lower case
Ptr getPtr (const std::string& name);
Ptr getPtr(const std::string& id, const ESM::RefNum& refNum);
void rest (double hours);
void recharge (float duration);

@ -19,6 +19,7 @@
#include <components/esm/cellstate.hpp>
#include <components/esm/cellid.hpp>
#include <components/esm/cellref.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/objectstate.hpp>
@ -509,19 +510,47 @@ namespace MWWorld
return Ptr();
}
class RefNumSearchVisitor
{
const ESM::RefNum& mRefNum;
public:
RefNumSearchVisitor(const ESM::RefNum& refNum) : mRefNum(refNum) {}
Ptr mFound;
bool operator()(const Ptr& ptr)
{
if (ptr.getCellRef().getRefNum() == mRefNum)
{
mFound = ptr;
return false;
}
return true;
}
};
Ptr CellStore::searchViaRefNum(const ESM::RefNum& refNum)
{
RefNumSearchVisitor searchVisitor(refNum);
forEach(searchVisitor);
return searchVisitor.mFound;
}
/*
Start of tes3mp addition
A custom type of search visitor used to find objects by their reference numbers
*/
template <typename PtrType>
struct SearchExactVisitor
class SearchExactVisitor
{
PtrType mFound;
unsigned int mRefNumToFind;
unsigned int mMpNumToFind;
const unsigned int mRefNumToFind;
const unsigned int mMpNumToFind;
public:
SearchExactVisitor(const unsigned int refNum, const unsigned int mpNum) : mRefNumToFind(refNum), mMpNumToFind(mpNum) {}
bool operator()(const PtrType& ptr)
Ptr mFound;
bool operator()(const Ptr& ptr)
{
if (ptr.getCellRef().getRefNum().mIndex == mRefNumToFind && ptr.getCellRef().getMpNum() == mMpNumToFind)
{
@ -540,15 +569,13 @@ namespace MWWorld
Allow the searching of objects by their reference numbers
*/
Ptr CellStore::searchExact (unsigned int refNum, unsigned int mpNum)
Ptr CellStore::searchExact (const unsigned int refNum, const unsigned int mpNum)
{
// Ensure that all objects searched for have a valid reference number
if (refNum == 0 && mpNum == 0)
return 0;
SearchExactVisitor<MWWorld::Ptr> searchVisitor;
searchVisitor.mRefNumToFind = refNum;
searchVisitor.mMpNumToFind = mpNum;
SearchExactVisitor searchVisitor(refNum, mpNum);
forEach(searchVisitor);
return searchVisitor.mFound;
}

@ -41,6 +41,7 @@ namespace ESM
struct CellState;
struct FogState;
struct CellId;
struct RefNum;
}
namespace MWWorld
@ -242,6 +243,11 @@ namespace MWWorld
Ptr searchViaActorId (int id);
///< Will return an empty Ptr if cell is not loaded.
Ptr searchViaRefNum (const ESM::RefNum& refNum);
///< Will return an empty Ptr if cell is not loaded. Does not check references in
/// containers.
/// @note Triggers CellStore hasState flag.
/*
Start of tes3mp addition

@ -33,6 +33,7 @@
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/cellid.hpp>
#include <components/esm/cellref.hpp>
#include <components/misc/constants.hpp>
#include <components/misc/resourcehelpers.hpp>
@ -796,6 +797,11 @@ namespace MWWorld
return mWorldScene->searchPtrViaActorId (actorId);
}
Ptr World::searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum)
{
return mCells.getPtr (id, refNum);
}
/*
Start of tes3mp addition
@ -1477,6 +1483,7 @@ namespace MWWorld
mRendering->updatePtr(ptr, newPtr);
MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr);
mPhysics->updatePtr(ptr, newPtr);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);
MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager();
mechMgr->updateCell(ptr, newPtr);

@ -316,6 +316,8 @@ namespace MWWorld
Ptr searchPtrViaActorId (int actorId) override;
///< Search is limited to the active cells.
Ptr searchPtrViaRefNum (const std::string& id, const ESM::RefNum& refNum) override;
/*
Start of tes3mp addition

@ -20,22 +20,62 @@ namespace DetourNavigator
}
}
namespace
{
template <class T>
struct Wrapper {
const T& mValue;
};
template <class Range>
inline testing::Message& writeRange(testing::Message& message, const Range& range)
{
message << "{\n";
for (const auto& v : range)
message << Wrapper<typename std::decay<decltype(v)>::type> {v} << ",\n";
return message << "}";
}
}
namespace testing
{
template <>
inline testing::Message& Message::operator <<(const osg::Vec3f& value)
{
return (*this) << "osg::Vec3f(" << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.x()
<< ", " << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.y()
<< ", " << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.z()
<< ')';
}
template <>
inline testing::Message& Message::operator <<(const Wrapper<osg::Vec3f>& value)
{
return (*this) << value.mValue;
}
template <>
inline testing::Message& Message::operator <<(const Wrapper<float>& value)
{
return (*this) << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.mValue;
}
template <>
inline testing::Message& Message::operator <<(const std::deque<osg::Vec3f>& value)
{
(*this) << "{\n";
for (const auto& v : value)
{
std::ostringstream stream;
stream << "osg::Vec3f("
<< std::setprecision(std::numeric_limits<float>::max_exponent10) << v.x() << ", "
<< std::setprecision(std::numeric_limits<float>::max_exponent10) << v.y() << ", "
<< std::setprecision(std::numeric_limits<float>::max_exponent10) << v.z() << ")";
(*this) << stream.str() << ",\n";
}
return (*this) << "}";
return writeRange(*this, value);
}
template <>
inline testing::Message& Message::operator <<(const std::vector<osg::Vec3f>& value)
{
return writeRange(*this, value);
}
template <>
inline testing::Message& Message::operator <<(const std::vector<float>& value)
{
return writeRange(*this, value);
}
}

@ -363,11 +363,11 @@ namespace
AreaType_ground
);
const auto recastMesh = builder.create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({
1.41421353816986083984375, 0, 1.1920928955078125e-07,
-1.41421353816986083984375, 0, -1.1920928955078125e-07,
1.1920928955078125e-07, 0, -1.41421353816986083984375,
}));
})));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
}

@ -9,6 +9,8 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <algorithm>
namespace
{
template <class T>
@ -30,6 +32,34 @@ namespace
shape.processAllTriangles(&callback, aabbMin, aabbMax);
return result;
}
bool isNear(btScalar lhs, btScalar rhs)
{
return std::abs(lhs - rhs) <= 1e-5;
}
bool isNear(const btVector3& lhs, const btVector3& rhs)
{
return std::equal(
static_cast<const btScalar*>(lhs),
static_cast<const btScalar*>(lhs) + 3,
static_cast<const btScalar*>(rhs),
[] (btScalar lhs, btScalar rhs) { return isNear(lhs, rhs); }
);
}
bool isNear(const btMatrix3x3& lhs, const btMatrix3x3& rhs)
{
for (int i = 0; i < 3; ++i)
if (!isNear(lhs[i], rhs[i]))
return false;
return true;
}
bool isNear(const btTransform& lhs, const btTransform& rhs)
{
return isNear(lhs.getOrigin(), rhs.getOrigin()) && isNear(lhs.getBasis(), rhs.getBasis());
}
}
static std::ostream& operator <<(std::ostream& stream, const btVector3& value)
@ -157,7 +187,7 @@ static bool operator ==(const btCompoundShape& lhs, const btCompoundShape& rhs)
for (int i = 0; i < lhs.getNumChildShapes(); ++i)
{
if (!compareObjects(lhs.getChildShape(i), rhs.getChildShape(i))
|| !(lhs.getChildTransform(i) == rhs.getChildTransform(i)))
|| !isNear(lhs.getChildTransform(i), rhs.getChildTransform(i)))
return false;
}
return true;
@ -165,13 +195,13 @@ static bool operator ==(const btCompoundShape& lhs, const btCompoundShape& rhs)
static bool operator ==(const btBoxShape& lhs, const btBoxShape& rhs)
{
return lhs.getLocalScaling() == rhs.getLocalScaling()
return isNear(lhs.getLocalScaling(), rhs.getLocalScaling())
&& lhs.getHalfExtentsWithoutMargin() == rhs.getHalfExtentsWithoutMargin();
}
static bool operator ==(const btBvhTriangleMeshShape& lhs, const btBvhTriangleMeshShape& rhs)
{
return lhs.getLocalScaling() == rhs.getLocalScaling()
return isNear(lhs.getLocalScaling(), rhs.getLocalScaling())
&& lhs.usesQuantizedAabbCompression() == rhs.usesQuantizedAabbCompression()
&& lhs.getOwnsBvh() == rhs.getOwnsBvh()
&& getTriangles(lhs) == getTriangles(rhs);
@ -349,19 +379,6 @@ namespace
EXPECT_EQ(*result, expected);
}
TEST_F(TestBulletNifLoader, for_root_not_nif_node_should_return_default)
{
StrictMock<RecordMock> record;
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&record));
const auto result = mLoader.load(mNifFile);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected);
}
TEST_F(TestBulletNifLoader, for_default_root_nif_node_should_return_default)
{
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));

@ -30,8 +30,8 @@ configuration:
clone_depth: 1
cache:
- C:\projects\openmw\deps\Bullet-2.86-msvc2015-win32.7z
- C:\projects\openmw\deps\Bullet-2.86-msvc2015-win64.7z
- C:\projects\openmw\deps\Bullet-2.87-msvc2015-win32.7z
- C:\projects\openmw\deps\Bullet-2.87-msvc2015-win64.7z
- C:\projects\openmw\deps\MyGUI-3.2.2-msvc2015-win32.7z
- C:\projects\openmw\deps\MyGUI-3.2.2-msvc2015-win64.7z
- C:\projects\openmw\deps\OSG-3.4.1-scrawl-msvc2015-win32.7z
@ -50,15 +50,16 @@ install:
before_build:
- cmd: git submodule update --init --recursive
- cmd: sh %APPVEYOR_BUILD_FOLDER%\CI\before_script.msvc.sh -c %configuration% -p %PLATFORM% -v %msvc% -V
- cmd: sh %APPVEYOR_BUILD_FOLDER%\CI\before_script.msvc.sh -c %configuration% -p %PLATFORM% -v %msvc% -V -i %APPVEYOR_BUILD_FOLDER%\install
build_script:
- cmd: if %PLATFORM%==Win32 set build=MSVC%msvc%_32
- cmd: if %PLATFORM%==x64 set build=MSVC%msvc%_64
- cmd: msbuild %build%\OpenMW.sln /t:Build /p:Configuration=%configuration% /m:2 /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
- cmd: cmake --install %build% --config %configuration%
after_build:
- cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%.zip %APPVEYOR_BUILD_FOLDER%\MSVC%msvc%_64\%configuration%\ -xr"!*.pdb"
- cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%.zip %APPVEYOR_BUILD_FOLDER%\install -xr"!*.pdb"
#- cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_%platform%_%appveyor_pull_request_number%_%appveyor_pull_request_head_commit%_pdb.zip %APPVEYOR_BUILD_FOLDER%\MSVC%msvc%_64\%configuration%\*.pdb
test: off

@ -143,7 +143,7 @@ add_component_dir (fontloader
)
add_component_dir (sdlutil
sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper OISCompat events sdlcursormanager
sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper events sdlcursormanager
)
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)

@ -163,15 +163,15 @@ namespace DetourNavigator
transformBoundingBox(transform, aabbMin, aabbMax);
aabbMin.setX(std::max(mBounds.mMin.x(), aabbMin.x()));
aabbMin.setX(std::min(mBounds.mMax.x(), aabbMin.x()));
aabbMin.setY(std::max(mBounds.mMin.y(), aabbMin.y()));
aabbMin.setY(std::min(mBounds.mMax.y(), aabbMin.y()));
aabbMax.setX(std::max(mBounds.mMin.x(), aabbMax.x()));
aabbMax.setX(std::min(mBounds.mMax.x(), aabbMax.x()));
aabbMax.setY(std::max(mBounds.mMin.y(), aabbMax.y()));
aabbMax.setY(std::min(mBounds.mMax.y(), aabbMax.y()));
aabbMin.setX(std::max(static_cast<btScalar>(mBounds.mMin.x()), aabbMin.x()));
aabbMin.setX(std::min(static_cast<btScalar>(mBounds.mMax.x()), aabbMin.x()));
aabbMin.setY(std::max(static_cast<btScalar>(mBounds.mMin.y()), aabbMin.y()));
aabbMin.setY(std::min(static_cast<btScalar>(mBounds.mMax.y()), aabbMin.y()));
aabbMax.setX(std::max(static_cast<btScalar>(mBounds.mMin.x()), aabbMax.x()));
aabbMax.setX(std::min(static_cast<btScalar>(mBounds.mMax.x()), aabbMax.x()));
aabbMax.setY(std::max(static_cast<btScalar>(mBounds.mMin.y()), aabbMax.y()));
aabbMax.setY(std::min(static_cast<btScalar>(mBounds.mMax.y()), aabbMax.y()));
transformBoundingBox(transform.inverse(), aabbMin, aabbMax);

@ -17,53 +17,60 @@ void ESM::CreatureStats::load (ESMReader &esm)
mTradeTime.mHour = 0;
esm.getHNOT (mTradeTime, "TIME");
int flags = 0;
mDead = false;
esm.getHNOT (mDead, "DEAD");
mDeathAnimationFinished = false;
esm.getHNOT (mDeathAnimationFinished, "DFNT");
if (esm.getFormat() < 3 && mDead)
mDeathAnimationFinished = true;
mDied = false;
esm.getHNOT (mDied, "DIED");
mMurdered = false;
esm.getHNOT (mMurdered, "MURD");
if (esm.isNextSub("FRHT"))
esm.skipHSub(); // Friendly hits, no longer used
mTalkedTo = false;
esm.getHNOT (mTalkedTo, "TALK");
mAlarmed = false;
esm.getHNOT (mAlarmed, "ALRM");
mAttacked = false;
esm.getHNOT (mAttacked, "ATKD");
if (esm.isNextSub("HOST"))
esm.skipHSub(); // Hostile, no longer used
if (esm.isNextSub("ATCK"))
esm.skipHSub(); // attackingOrSpell, no longer used
mKnockdown = false;
esm.getHNOT (mKnockdown, "KNCK");
mKnockdownOneFrame = false;
esm.getHNOT (mKnockdownOneFrame, "KNC1");
mKnockdownOverOneFrame = false;
esm.getHNOT (mKnockdownOverOneFrame, "KNCO");
mHitRecovery = false;
esm.getHNOT (mHitRecovery, "HITR");
mBlock = false;
esm.getHNOT (mBlock, "BLCK");
mRecalcDynamicStats = false;
if (esm.getFormat() < 8)
{
esm.getHNOT (mDead, "DEAD");
esm.getHNOT (mDeathAnimationFinished, "DFNT");
if (esm.getFormat() < 3 && mDead)
mDeathAnimationFinished = true;
esm.getHNOT (mDied, "DIED");
esm.getHNOT (mMurdered, "MURD");
if (esm.isNextSub("FRHT"))
esm.skipHSub(); // Friendly hits, no longer used
esm.getHNOT (mTalkedTo, "TALK");
esm.getHNOT (mAlarmed, "ALRM");
esm.getHNOT (mAttacked, "ATKD");
if (esm.isNextSub("HOST"))
esm.skipHSub(); // Hostile, no longer used
if (esm.isNextSub("ATCK"))
esm.skipHSub(); // attackingOrSpell, no longer used
esm.getHNOT (mKnockdown, "KNCK");
esm.getHNOT (mKnockdownOneFrame, "KNC1");
esm.getHNOT (mKnockdownOverOneFrame, "KNCO");
esm.getHNOT (mHitRecovery, "HITR");
esm.getHNOT (mBlock, "BLCK");
}
else
{
esm.getHNOT(flags, "AFLG");
mDead = flags & Dead;
mDeathAnimationFinished = flags & DeathAnimationFinished;
mDied = flags & Died;
mMurdered = flags & Murdered;
mTalkedTo = flags & TalkedTo;
mAlarmed = flags & Alarmed;
mAttacked = flags & Attacked;
mKnockdown = flags & Knockdown;
mKnockdownOneFrame = flags & KnockdownOneFrame;
mKnockdownOverOneFrame = flags & KnockdownOverOneFrame;
mHitRecovery = flags & HitRecovery;
mBlock = flags & Block;
mRecalcDynamicStats = flags & RecalcDynamicStats;
}
mMovementFlags = 0;
esm.getHNOT (mMovementFlags, "MOVE");
@ -78,8 +85,8 @@ void ESM::CreatureStats::load (ESMReader &esm)
mLastHitAttemptObject = esm.getHNOString ("LHAT");
mRecalcDynamicStats = false;
esm.getHNOT (mRecalcDynamicStats, "CALC");
if (esm.getFormat() < 8)
esm.getHNOT (mRecalcDynamicStats, "CALC");
mDrawState = 0;
esm.getHNOT (mDrawState, "DRAW");
@ -90,9 +97,6 @@ void ESM::CreatureStats::load (ESMReader &esm)
mActorId = -1;
esm.getHNOT (mActorId, "ACID");
//mHitAttemptActorId = -1;
//esm.getHNOT(mHitAttemptActorId, "HAID");
mDeathAnimation = -1;
esm.getHNOT (mDeathAnimation, "DANM");
@ -134,7 +138,6 @@ void ESM::CreatureStats::load (ESMReader &esm)
void ESM::CreatureStats::save (ESMWriter &esm) const
{
for (int i=0; i<8; ++i)
mAttributes[i].save (esm);
@ -147,41 +150,23 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
if (mTradeTime.mDay != 0 || mTradeTime.mHour != 0)
esm.writeHNT ("TIME", mTradeTime);
if (mDead)
esm.writeHNT ("DEAD", mDead);
if (mDeathAnimationFinished)
esm.writeHNT ("DFNT", mDeathAnimationFinished);
if (mDied)
esm.writeHNT ("DIED", mDied);
if (mMurdered)
esm.writeHNT ("MURD", mMurdered);
if (mTalkedTo)
esm.writeHNT ("TALK", mTalkedTo);
if (mAlarmed)
esm.writeHNT ("ALRM", mAlarmed);
if (mAttacked)
esm.writeHNT ("ATKD", mAttacked);
if (mKnockdown)
esm.writeHNT ("KNCK", mKnockdown);
if (mKnockdownOneFrame)
esm.writeHNT ("KNC1", mKnockdownOneFrame);
if (mKnockdownOverOneFrame)
esm.writeHNT ("KNCO", mKnockdownOverOneFrame);
if (mHitRecovery)
esm.writeHNT ("HITR", mHitRecovery);
if (mBlock)
esm.writeHNT ("BLCK", mBlock);
int flags = 0;
if (mDead) flags |= Dead;
if (mDeathAnimationFinished) flags |= DeathAnimationFinished;
if (mDied) flags |= Died;
if (mMurdered) flags |= Murdered;
if (mTalkedTo) flags |= TalkedTo;
if (mAlarmed) flags |= Alarmed;
if (mAttacked) flags |= Attacked;
if (mKnockdown) flags |= Knockdown;
if (mKnockdownOneFrame) flags |= KnockdownOneFrame;
if (mKnockdownOverOneFrame) flags |= KnockdownOverOneFrame;
if (mHitRecovery) flags |= HitRecovery;
if (mBlock) flags |= Block;
if (mRecalcDynamicStats) flags |= RecalcDynamicStats;
if (flags)
esm.writeHNT ("AFLG", flags);
if (mMovementFlags)
esm.writeHNT ("MOVE", mMovementFlags);
@ -195,9 +180,6 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
if (!mLastHitAttemptObject.empty())
esm.writeHNString ("LHAT", mLastHitAttemptObject);
if (mRecalcDynamicStats)
esm.writeHNT ("CALC", mRecalcDynamicStats);
if (mDrawState)
esm.writeHNT ("DRAW", mDrawState);
@ -207,13 +189,10 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
if (mActorId != -1)
esm.writeHNT ("ACID", mActorId);
//if (mHitAttemptActorId != -1)
// esm.writeHNT("HAID", mHitAttemptActorId);
if (mDeathAnimation != -1)
esm.writeHNT ("DANM", mDeathAnimation);
if (mTimeOfDeath.mHour != 0 && mTimeOfDeath.mDay != 0)
if (mTimeOfDeath.mHour != 0 || mTimeOfDeath.mDay != 0)
esm.writeHNT ("DTIM", mTimeOfDeath);
mSpells.save(esm);
@ -247,7 +226,6 @@ void ESM::CreatureStats::blank()
mTradeTime.mDay = 0;
mGoldPool = 0;
mActorId = -1;
//mHitAttemptActorId = -1;
mHasAiSettings = false;
mDead = false;
mDeathAnimationFinished = false;

@ -40,6 +40,22 @@ namespace ESM
int mActorId;
//int mHitAttemptActorId;
enum Flags
{
Dead = 0x0001,
DeathAnimationFinished = 0x0002,
Died = 0x0004,
Murdered = 0x0008,
TalkedTo = 0x0010,
Alarmed = 0x0020,
Attacked = 0x0040,
Knockdown = 0x0080,
KnockdownOneFrame = 0x0100,
KnockdownOverOneFrame = 0x0200,
HitRecovery = 0x0400,
Block = 0x0800,
RecalcDynamicStats = 0x1000
};
bool mDead;
bool mDeathAnimationFinished;
bool mDied;

@ -3,10 +3,60 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include <osgDB/ReadFile>
#include <components/debug/debuglog.hpp>
#include <components/files/memorystream.hpp>
#include "savedgame.hpp"
void convertFogOfWar(std::vector<char>& imageData)
{
if (imageData.empty())
{
return;
}
osgDB::ReaderWriter* tgaReader = osgDB::Registry::instance()->getReaderWriterForExtension("tga");
if (!tgaReader)
{
Log(Debug::Error) << "Error: Unable to load fog, can't find a tga ReaderWriter";
return;
}
Files::IMemStream in(&imageData[0], imageData.size());
osgDB::ReaderWriter::ReadResult result = tgaReader->readImage(in);
if (!result.success())
{
Log(Debug::Error) << "Error: Failed to read fog: " << result.message() << " code " << result.status();
return;
}
osgDB::ReaderWriter* pngWriter = osgDB::Registry::instance()->getReaderWriterForExtension("png");
if (!pngWriter)
{
Log(Debug::Error) << "Error: Unable to write fog, can't find a png ReaderWriter";
return;
}
std::ostringstream ostream;
osgDB::ReaderWriter::WriteResult png = pngWriter->writeImage(*result.getImage(), ostream);
if (!png.success())
{
Log(Debug::Error) << "Error: Unable to write fog: " << png.message() << " code " << png.status();
return;
}
std::string str = ostream.str();
imageData = std::vector<char>(str.begin(), str.end());
}
void ESM::FogState::load (ESMReader &esm)
{
esm.getHNOT(mBounds, "BOUN");
esm.getHNOT(mNorthMarkerAngle, "ANGL");
int dataFormat = esm.getFormat();
while (esm.isNextSub("FTEX"))
{
esm.getSubHeader();
@ -18,6 +68,10 @@ void ESM::FogState::load (ESMReader &esm)
size_t imageSize = esm.getSubSize()-sizeof(int)*2;
tex.mImageData.resize(imageSize);
esm.getExact(&tex.mImageData[0], imageSize);
if (dataFormat < 7)
convertFogOfWar(tex.mImageData);
mFogTextures.push_back(tex);
}
}

@ -12,7 +12,11 @@ void ESM::GlobalScript::load (ESMReader &esm)
mRunning = 0;
esm.getHNOT (mRunning, "RUN_");
mTargetId = esm.getHNOString ("TARG");
mTargetRef.unset();
if (esm.peekNextSub("TARG"))
mTargetId = esm.getHNString ("TARG");
if (esm.peekNextSub("FRMR"))
mTargetRef.load(esm, true, "FRMR");
}
void ESM::GlobalScript::save (ESMWriter &esm) const
@ -24,5 +28,10 @@ void ESM::GlobalScript::save (ESMWriter &esm) const
if (mRunning)
esm.writeHNT ("RUN_", mRunning);
esm.writeHNOString ("TARG", mTargetId);
if (!mTargetId.empty())
{
esm.writeHNOString ("TARG", mTargetId);
if (mTargetRef.hasContentFile())
mTargetRef.save (esm, true, "FRMR");
}
}

@ -2,6 +2,7 @@
#define OPENMW_ESM_GLOBALSCRIPT_H
#include "locals.hpp"
#include "cellref.hpp"
namespace ESM
{
@ -16,6 +17,7 @@ namespace ESM
Locals mLocals;
int mRunning;
std::string mTargetId; // for targeted scripts
RefNum mTargetRef;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;

@ -5,6 +5,7 @@
void ESM::InventoryState::load (ESMReader &esm)
{
// obsolete
int index = 0;
while (esm.isNextSub ("IOBJ"))
{
@ -31,6 +32,22 @@ void ESM::InventoryState::load (ESMReader &esm)
++index;
}
int itemsCount = 0;
esm.getHNOT(itemsCount, "ICNT");
for (int i = 0; i < itemsCount; i++)
{
ObjectState state;
state.mRef.loadId(esm, true);
state.load (esm);
if (state.mCount == 0)
continue;
mItems.push_back (state);
}
//Next item is Levelled item
while (esm.isNextSub("LEVM"))
{
@ -72,18 +89,35 @@ void ESM::InventoryState::load (ESMReader &esm)
mEquipmentSlots[equipIndex] = slot;
}
if (esm.isNextSub("EQIP"))
{
esm.getSubHeader();
int slotsCount = 0;
esm.getT(slotsCount);
for (int i = 0; i < slotsCount; i++)
{
int equipIndex;
esm.getT(equipIndex);
int slot;
esm.getT(slot);
mEquipmentSlots[equipIndex] = slot;
}
}
mSelectedEnchantItem = -1;
esm.getHNOT(mSelectedEnchantItem, "SELE");
}
void ESM::InventoryState::save (ESMWriter &esm) const
{
for (std::vector<ObjectState>::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter)
int itemsCount = static_cast<int>(mItems.size());
if (itemsCount > 0)
{
int unused = 0;
esm.writeHNT ("IOBJ", unused);
iter->save (esm, true);
esm.writeHNT ("ICNT", itemsCount);
for (const ObjectState& state : mItems)
{
state.save (esm, true);
}
}
for (std::map<std::pair<std::string, std::string>, int>::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it)
@ -105,12 +139,17 @@ void ESM::InventoryState::save (ESMWriter &esm) const
}
}
for (std::map<int, int>::const_iterator it = mEquipmentSlots.begin(); it != mEquipmentSlots.end(); ++it)
int slotsCount = static_cast<int>(mEquipmentSlots.size());
if (slotsCount > 0)
{
esm.startSubRecord("EQUI");
esm.writeT(it->first);
esm.writeT(it->second);
esm.endRecord("EQUI");
esm.startSubRecord("EQIP");
esm.writeT(slotsCount);
for (std::map<int, int>::const_iterator it = mEquipmentSlots.begin(); it != mEquipmentSlots.end(); ++it)
{
esm.writeT(it->first);
esm.writeT(it->second);
}
esm.endRecord("EQIP");
}
if (mSelectedEnchantItem != -1)

@ -210,13 +210,15 @@ namespace ESM
std::string Cell::getDescription() const
{
if (mData.mFlags & Interior)
{
return mName;
}
else
{
return std::to_string(mData.mX) + ", " + std::to_string(mData.mY);
}
std::string cellGrid = "(" + std::to_string(mData.mX) + ", " + std::to_string(mData.mY) + ")";
if (!mName.empty())
return mName + ' ' + cellGrid;
// FIXME: should use sDefaultCellname GMST instead, but it's not available in this scope
std::string region = !mRegion.empty() ? mRegion : "Wilderness";
return region + ' ' + cellGrid;
}
bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool &isDeleted, bool ignoreMoves, MovedCellRef *mref)

@ -35,7 +35,7 @@ void ESM::NpcStats::load (ESMReader &esm)
mSkills[i].load (esm);
mWerewolfDeprecatedData = false;
if (esm.peekNextSub("STBA"))
if (esm.getFormat() < 8 && esm.peekNextSub("STBA"))
{
// we have deprecated werewolf skills, stored interleaved
// Load into one big vector, then remove every 2nd value
@ -95,7 +95,9 @@ void ESM::NpcStats::load (ESMReader &esm)
mLevelProgress = 0;
esm.getHNOT (mLevelProgress, "LPRO");
esm.getHNT (mSkillIncrease, "INCR");
for (int i = 0; i < 8; ++i)
mSkillIncrease[i] = 0;
esm.getHNOT (mSkillIncrease, "INCR");
for (int i=0; i<3; ++i)
mSpecIncreases[i] = 0;
@ -160,8 +162,21 @@ void ESM::NpcStats::save (ESMWriter &esm) const
if (mLevelProgress)
esm.writeHNT ("LPRO", mLevelProgress);
esm.writeHNT ("INCR", mSkillIncrease);
bool saveSkillIncreases = false;
for (int i = 0; i < 8; ++i)
{
if (mSkillIncrease[i] != 0)
{
saveSkillIncreases = true;
break;
}
}
if (saveSkillIncreases)
esm.writeHNT ("INCR", mSkillIncrease);
if (mSpecIncreases[0] != 0 ||
mSpecIncreases[1] != 0 ||
mSpecIncreases[2] != 0)
esm.writeHNT ("SPEC", mSpecIncreases);
for (std::vector<std::string>::const_iterator iter (mUsedIds.begin()); iter!=mUsedIds.end();

@ -5,7 +5,7 @@
#include "defs.hpp"
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
int ESM::SavedGame::sCurrentFormat = 5;
int ESM::SavedGame::sCurrentFormat = 8;
void ESM::SavedGame::load (ESMReader &esm)
{

@ -125,8 +125,6 @@ namespace Interpreter
virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global)
= 0;
virtual std::string getTargetId() const = 0;
/*
Start of tes3mp addition

@ -26,7 +26,7 @@ namespace Interpreter
{
std::string name = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
runtime.getContext().startScript (name, runtime.getContext().getTargetId());
runtime.getContext().startScript (name);
}
};

@ -123,12 +123,10 @@ namespace Nif
{
Controller::read(nif);
/*
int = 1
2xfloat
short = 0 or 1
*/
nif->skip(14);
bankDir = nif->getInt();
maxBankAngle = nif->getFloat();
smoothing = nif->getFloat();
followAxis = nif->getShort();
posData.read(nif);
floatData.read(nif);
}

@ -96,6 +96,20 @@ public:
NiPosDataPtr posData;
NiFloatDataPtr floatData;
enum Flags
{
Flag_OpenCurve = 0x020,
Flag_AllowFlip = 0x040,
Flag_Bank = 0x080,
Flag_ConstVelocity = 0x100,
Flag_Follow = 0x200,
Flag_FlipFollowAxis = 0x400
};
int bankDir;
float maxBankAngle, smoothing;
short followAxis;
void read(NIFStream *nif);
void post(NIFFile *nif);
};

@ -31,7 +31,7 @@ void NiSkinInstance::post(NIFFile *nif)
}
}
void ShapeData::read(NIFStream *nif)
void NiGeometryData::read(NIFStream *nif)
{
int verts = nif->getUShort();
@ -69,7 +69,7 @@ void ShapeData::read(NIFStream *nif)
void NiTriShapeData::read(NIFStream *nif)
{
ShapeData::read(nif);
NiGeometryData::read(nif);
/*int tris =*/ nif->getUShort();
@ -92,7 +92,7 @@ void NiTriShapeData::read(NIFStream *nif)
void NiTriStripsData::read(NIFStream *nif)
{
ShapeData::read(nif);
NiGeometryData::read(nif);
// Every strip with n points defines n-2 triangles, so this should be unnecessary.
/*int tris =*/ nif->getUShort();
@ -112,7 +112,7 @@ void NiTriStripsData::read(NIFStream *nif)
void NiAutoNormalParticlesData::read(NIFStream *nif)
{
ShapeData::read(nif);
NiGeometryData::read(nif);
// Should always match the number of vertices
numParticles = nif->getUShort();
@ -163,11 +163,8 @@ void NiPixelData::read(NIFStream *nif)
{
fmt = (Format)nif->getUInt();
rmask = nif->getUInt(); // usually 0xff
gmask = nif->getUInt(); // usually 0xff00
bmask = nif->getUInt(); // usually 0xff0000
amask = nif->getUInt(); // usually 0xff000000 or zero
for (unsigned int i = 0; i < 4; ++i)
colorMask[i] = nif->getUInt();
bpp = nif->getUInt();
// 8 bytes of "Old Fast Compare". Whatever that means.
@ -190,10 +187,9 @@ void NiPixelData::read(NIFStream *nif)
}
// Read the data
unsigned int dataSize = nif->getUInt();
data.reserve(dataSize);
for (unsigned i=0; i<dataSize; ++i)
data.push_back((unsigned char)nif->getChar());
unsigned int numPixels = nif->getUInt();
if (numPixels)
nif->getUChars(data, numPixels);
}
void NiPixelData::post(NIFFile *nif)
@ -225,7 +221,7 @@ void NiSkinData::read(NIFStream *nif)
trafo.scale = nif->getFloat();
int boneNum = nif->getInt();
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFFile::NIFVersion::VER_GAMEBRYO)
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0))
nif->skip(4); // NiSkinPartition link
bones.resize(boneNum);

@ -32,7 +32,7 @@ namespace Nif
{
// Common ancestor for several data classes
class ShapeData : public Record
class NiGeometryData : public Record
{
public:
std::vector<osg::Vec3f> vertices, normals;
@ -44,7 +44,7 @@ public:
void read(NIFStream *nif);
};
class NiTriShapeData : public ShapeData
class NiTriShapeData : public NiGeometryData
{
public:
// Triangles, three vertex indices per triangle
@ -53,7 +53,7 @@ public:
void read(NIFStream *nif);
};
class NiTriStripsData : public ShapeData
class NiTriStripsData : public NiGeometryData
{
public:
// Triangle strips, series of vertex indices.
@ -62,7 +62,7 @@ public:
void read(NIFStream *nif);
};
class NiAutoNormalParticlesData : public ShapeData
class NiAutoNormalParticlesData : public NiGeometryData
{
public:
int numParticles;
@ -124,7 +124,8 @@ public:
};
Format fmt;
unsigned int rmask, gmask, bmask, amask, bpp;
unsigned int colorMask[4];
unsigned int bpp;
NiPalettePtr palette;
unsigned int numberOfMipmaps;

@ -144,7 +144,7 @@ void NIFFile::parse(Files::IStreamPtr stream)
ver = nif.getUInt();
// 4.0.0.0 is an older, practically identical version of the format.
// It's not used by Morrowind assets but Morrowind supports it.
if(ver != nif.generateVersion(4,0,0,0) && ver != VER_MW)
if(ver != NIFStream::generateVersion(4,0,0,0) && ver != VER_MW)
fail("Unsupported NIF version: " + printVersion(ver));
// Number of records
size_t recNum = nif.getInt();

@ -79,11 +79,7 @@ public:
enum NIFVersion
{
VER_MW = 0x04000002, // 4.0.0.2. Main Morrowind NIF version.
VER_CI = 0x04020200, // 4.2.2.0. Main Culpa Innata NIF version, also used in Civ4.
VER_ZT2 = 0x0A000100, // 10.0.1.0. Main Zoo Tycoon 2 NIF version, also used in Oblivion and Civ4.
VER_OB_OLD = 0x0A000102, // 10.0.1.2. Main older Oblivion NIF version.
VER_GAMEBRYO = 0x0A010000, // 10.1.0.0. Lots of games use it. The first version that has Gamebryo File Format header.
VER_CIV4 = 0x14000004, // 20.0.0.4. Main Civilization IV NIF version.
VER_OB = 0x14000005, // 20.0.0.5. Main Oblivion NIF version.
VER_BGS = 0x14020007 // 20.2.0.7. Main Fallout 3/4/76/New Vegas and Skyrim/SkyrimSE NIF version.
};

@ -200,6 +200,18 @@ public:
return result;
}
void getChars(std::vector<char> &vec, size_t size)
{
vec.resize(size);
readLittleEndianDynamicBufferOfType<char,char>(inp, vec.data(), size);
}
void getUChars(std::vector<unsigned char> &vec, size_t size)
{
vec.resize(size);
readLittleEndianDynamicBufferOfType<unsigned char,unsigned char>(inp, vec.data(), size);
}
void getUShorts(std::vector<unsigned short> &vec, size_t size)
{
vec.resize(size);

@ -128,7 +128,12 @@ struct NiNode : Node
}
};
struct NiTriShape : Node
struct NiGeometry : Node
{
NiSkinInstancePtr skin;
};
struct NiTriShape : NiGeometry
{
/* Possible flags:
0x40 - mesh has no vertex normals ?
@ -138,7 +143,6 @@ struct NiTriShape : Node
*/
NiTriShapeDataPtr data;
NiSkinInstancePtr skin;
void read(NIFStream *nif)
{
@ -157,10 +161,9 @@ struct NiTriShape : Node
}
};
struct NiTriStrips : Node
struct NiTriStrips : NiGeometry
{
NiTriStripsDataPtr data;
NiSkinInstancePtr skin;
void read(NIFStream *nif)
{
@ -285,7 +288,7 @@ struct NiLODNode : public NiSwitchNode
void read(NIFStream *nif)
{
NiSwitchNode::read(nif);
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFFile::NIFVersion::VER_ZT2)
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
lodCenter = nif->getVector3();
unsigned int numLodLevels = nif->getUInt();
for (unsigned int i=0; i<numLodLevels; ++i)

@ -132,20 +132,18 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
mStaticMesh.reset();
mAvoidStaticMesh.reset();
if (nif.numRoots() < 1)
Nif::Node* node = nullptr;
const size_t numRoots = nif.numRoots();
for (size_t i = 0; i < numRoots; ++i)
{
warn("Found no root nodes in NIF.");
return mShape;
Nif::Record* r = nif.getRoot(i);
assert(r != nullptr);
if ((node = dynamic_cast<Nif::Node*>(r)))
break;
}
Nif::Record *r = nif.getRoot(0);
assert(r != nullptr);
Nif::Node *node = dynamic_cast<Nif::Node*>(r);
if (node == nullptr)
if (!node)
{
warn("First root in file was not a node, but a " +
r->recName + ". Skipping file.");
warn("Found no root nodes in NIF.");
return mShape;
}

@ -521,4 +521,50 @@ void ParticleSystemController::operator() (osg::Node* node, osg::NodeVisitor* nv
traverse(node, nv);
}
PathController::PathController(const PathController &copy, const osg::CopyOp &copyop)
: osg::NodeCallback(copy, copyop)
, Controller(copy)
, mPath(copy.mPath)
, mPercent(copy.mPercent)
, mFlags(copy.mFlags)
{
}
PathController::PathController(const Nif::NiPathController* ctrl)
: mPath(ctrl->posData->mKeyList, osg::Vec3f())
, mPercent(ctrl->floatData->mKeyList, 1.f)
, mFlags(ctrl->flags)
{
}
float PathController::getPercent(float time) const
{
float percent = mPercent.interpKey(time);
if (percent < 0.f)
percent = std::fmod(percent, 1.f) + 1.f;
else if (percent > 1.f)
percent = std::fmod(percent, 1.f);
return percent;
}
void PathController::operator() (osg::Node* node, osg::NodeVisitor* nv)
{
if (mPath.empty() || mPercent.empty() || !hasInput())
{
traverse(node, nv);
return;
}
osg::MatrixTransform* trans = static_cast<osg::MatrixTransform*>(node);
osg::Matrix mat = trans->getMatrix();
float time = getInputValue(nv);
float percent = getPercent(time);
osg::Vec3f pos(mPath.interpKey(percent));
mat.setTrans(pos);
trans->setMatrix(mat);
traverse(node, nv);
}
}

@ -342,6 +342,25 @@ namespace NifOsg
float mEmitStop;
};
class PathController : public osg::NodeCallback, public SceneUtil::Controller
{
public:
PathController(const Nif::NiPathController* ctrl);
PathController() = default;
PathController(const PathController& copy, const osg::CopyOp& copyop);
META_Object(NifOsg, PathController)
virtual void operator() (osg::Node*, osg::NodeVisitor*);
private:
Vec3Interpolator mPath;
FloatInterpolator mPercent;
int mFlags{0};
float getPercent(float time) const;
};
}
#endif

@ -247,22 +247,24 @@ namespace NifOsg
static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target)
{
if(nif->numRoots() < 1)
const Nif::NiSequenceStreamHelper *seq = nullptr;
const size_t numRoots = nif->numRoots();
for (size_t i = 0; i < numRoots; ++i)
{
nif->warn("Found no root nodes");
return;
const Nif::Record *r = nif->getRoot(i);
assert(r != nullptr);
if (r->recType == Nif::RC_NiSequenceStreamHelper)
{
seq = static_cast<const Nif::NiSequenceStreamHelper*>(r);
break;
}
}
const Nif::Record *r = nif->getRoot(0);
assert(r != nullptr);
if(r->recType != Nif::RC_NiSequenceStreamHelper)
if (!seq)
{
nif->warn("First root was not a NiSequenceStreamHelper, but a "+
r->recName+".");
nif->warn("Found no NiSequenceStreamHelper root record");
return;
}
const Nif::NiSequenceStreamHelper *seq = static_cast<const Nif::NiSequenceStreamHelper*>(r);
Nif::ExtraPtr extra = seq->extra;
if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData)
@ -303,15 +305,17 @@ namespace NifOsg
osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr nif, Resource::ImageManager* imageManager)
{
if (nif->numRoots() < 1)
const Nif::Node* nifNode = nullptr;
const size_t numRoots = nif->numRoots();
for (size_t i = 0; i < numRoots; ++i)
{
const Nif::Record* r = nif->getRoot(i);
if ((nifNode = dynamic_cast<const Nif::Node*>(r)))
break;
}
if (!nifNode)
nif->fail("Found no root nodes");
const Nif::Record* r = nif->getRoot(0);
const Nif::Node* nifNode = dynamic_cast<const Nif::Node*>(r);
if (nifNode == nullptr)
nif->fail("First root was not a node, but a " + r->recName);
osg::ref_ptr<TextKeyMapHolder> textkeys (new TextKeyMapHolder);
osg::ref_ptr<osg::Node> created = handleNode(nifNode, nullptr, imageManager, std::vector<unsigned int>(), 0, false, false, false, &textkeys->mTextKeys);
@ -612,7 +616,7 @@ namespace NifOsg
bool hasVisController = false;
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
{
if (hasVisController |= (ctrl->recType == Nif::RC_NiVisController))
if ((hasVisController |= (ctrl->recType == Nif::RC_NiVisController)))
break;
}
@ -645,11 +649,7 @@ namespace NifOsg
const bool isMarker = hasMarkers && !nodeName.compare(0, markerName.size(), markerName);
if (!isMarker && nodeName.compare(0, shadowName.size(), shadowName) && nodeName.compare(0, shadowName2.size(), shadowName2))
{
Nif::NiSkinInstancePtr skin;
if (nifNode->recType == Nif::RC_NiTriShape)
skin = static_cast<const Nif::NiTriShape*>(nifNode)->skin;
else // if (nifNode->recType == Nif::RC_NiTriStrips)
skin = static_cast<const Nif::NiTriStrips*>(nifNode)->skin;
Nif::NiSkinInstancePtr skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin;
if (skin.empty())
handleTriShape(nifNode, node, composite, boundTextures, animflags);
@ -752,6 +752,17 @@ namespace NifOsg
node->addUpdateCallback(callback);
}
}
else if (ctrl->recType == Nif::RC_NiPathController)
{
const Nif::NiPathController *path = static_cast<const Nif::NiPathController*>(ctrl.getPtr());
if (!path->posData.empty() && !path->floatData.empty())
{
osg::ref_ptr<PathController> callback(new PathController(path));
setupController(path, callback, animflags);
node->addUpdateCallback(callback);
}
}
else if (ctrl->recType == Nif::RC_NiVisController)
{
handleVisController(static_cast<const Nif::NiVisController*>(ctrl.getPtr()), node, animflags);
@ -780,6 +791,17 @@ namespace NifOsg
transformNode->addUpdateCallback(callback);
}
}
else if (ctrl->recType == Nif::RC_NiPathController)
{
const Nif::NiPathController *path = static_cast<const Nif::NiPathController*>(ctrl.getPtr());
if (!path->posData.empty() && !path->floatData.empty())
{
osg::ref_ptr<PathController> callback(new PathController(path));
setupController(path, callback, animflags);
transformNode->addUpdateCallback(callback);
}
}
else if (ctrl->recType == Nif::RC_NiVisController)
{
handleVisController(static_cast<const Nif::NiVisController*>(ctrl.getPtr()), transformNode, animflags);
@ -1268,12 +1290,7 @@ namespace NifOsg
// Assign bone weights
osg::ref_ptr<SceneUtil::RigGeometry::InfluenceMap> map (new SceneUtil::RigGeometry::InfluenceMap);
Nif::NiSkinInstancePtr skinPtr;
if (nifNode->recType == Nif::RC_NiTriShape)
skinPtr = static_cast<const Nif::NiTriShape*>(nifNode)->skin;
else
skinPtr = static_cast<const Nif::NiTriStrips*>(nifNode)->skin;
const Nif::NiSkinInstance *skin = skinPtr.getPtr();
const Nif::NiSkinInstance *skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin.getPtr();
const Nif::NiSkinData *data = skin->data.getPtr();
const Nif::NodeList &bones = skin->bones;
for(size_t i = 0;i < bones.length();i++)

@ -45,23 +45,24 @@ namespace SceneUtil
virtual void apply(osg::Drawable& drawable)
{
std::string lowerName = Misc::StringUtils::lowerCase(drawable.getName());
if ((lowerName.size() >= mFilter.size() && lowerName.compare(0, mFilter.size(), mFilter) == 0)
|| (lowerName.size() >= mFilter2.size() && lowerName.compare(0, mFilter2.size(), mFilter2) == 0))
if (!filterMatches(drawable.getName()))
return;
osg::Node* node = &drawable;
while (node->getNumParents())
{
osg::Node* node = &drawable;
while (node && node->getNumParents() && !node->getStateSet())
node = node->getParent(0);
if (node)
mToCopy.push_back(node);
osg::Group* parent = node->getParent(0);
if (!parent || !filterMatches(parent->getName()))
break;
node = parent;
}
mToCopy.emplace(node);
}
void doCopy()
{
for (std::vector<osg::ref_ptr<osg::Node> >::iterator it = mToCopy.begin(); it != mToCopy.end(); ++it)
for (const osg::ref_ptr<osg::Node>& node : mToCopy)
{
osg::ref_ptr<osg::Node> node = *it;
if (node->getNumParents() > 1)
Log(Debug::Error) << "Error CopyRigVisitor: node has multiple parents";
while (node->getNumParents())
@ -73,8 +74,16 @@ namespace SceneUtil
}
private:
typedef std::vector<osg::ref_ptr<osg::Node> > NodeVector;
NodeVector mToCopy;
bool filterMatches(const std::string& name) const
{
std::string lowerName = Misc::StringUtils::lowerCase(name);
return (lowerName.size() >= mFilter.size() && lowerName.compare(0, mFilter.size(), mFilter) == 0)
|| (lowerName.size() >= mFilter2.size() && lowerName.compare(0, mFilter2.size(), mFilter2) == 0);
}
using NodeSet = std::set<osg::ref_ptr<osg::Node>>;
NodeSet mToCopy;
osg::ref_ptr<osg::Group> mParent;
std::string mFilter;

@ -1,159 +0,0 @@
#ifndef OIS_SDL_COMPAT_H
#define OIS_SDL_COMPAT_H
#include <SDL_events.h>
#include <SDL_types.h>
namespace OIS
{
//! Keyboard scan codes
enum KeyCode
{
KC_UNASSIGNED = 0x00,
KC_ESCAPE = 0x01,
KC_1 = 0x02,
KC_2 = 0x03,
KC_3 = 0x04,
KC_4 = 0x05,
KC_5 = 0x06,
KC_6 = 0x07,
KC_7 = 0x08,
KC_8 = 0x09,
KC_9 = 0x0A,
KC_0 = 0x0B,
KC_MINUS = 0x0C, // - on main keyboard
KC_EQUALS = 0x0D,
KC_BACK = 0x0E, // backspace
KC_TAB = 0x0F,
KC_Q = 0x10,
KC_W = 0x11,
KC_E = 0x12,
KC_R = 0x13,
KC_T = 0x14,
KC_Y = 0x15,
KC_U = 0x16,
KC_I = 0x17,
KC_O = 0x18,
KC_P = 0x19,
KC_LBRACKET = 0x1A,
KC_RBRACKET = 0x1B,
KC_RETURN = 0x1C, // Enter on main keyboard
KC_LCONTROL = 0x1D,
KC_A = 0x1E,
KC_S = 0x1F,
KC_D = 0x20,
KC_F = 0x21,
KC_G = 0x22,
KC_H = 0x23,
KC_J = 0x24,
KC_K = 0x25,
KC_L = 0x26,
KC_SEMICOLON = 0x27,
KC_APOSTROPHE = 0x28,
KC_GRAVE = 0x29, // accent
KC_LSHIFT = 0x2A,
KC_BACKSLASH = 0x2B,
KC_Z = 0x2C,
KC_X = 0x2D,
KC_C = 0x2E,
KC_V = 0x2F,
KC_B = 0x30,
KC_N = 0x31,
KC_M = 0x32,
KC_COMMA = 0x33,
KC_PERIOD = 0x34, // . on main keyboard
KC_SLASH = 0x35, // / on main keyboard
KC_RSHIFT = 0x36,
KC_MULTIPLY = 0x37, // * on numeric keypad
KC_LMENU = 0x38, // left Alt
KC_SPACE = 0x39,
KC_CAPITAL = 0x3A,
KC_F1 = 0x3B,
KC_F2 = 0x3C,
KC_F3 = 0x3D,
KC_F4 = 0x3E,
KC_F5 = 0x3F,
KC_F6 = 0x40,
KC_F7 = 0x41,
KC_F8 = 0x42,
KC_F9 = 0x43,
KC_F10 = 0x44,
KC_NUMLOCK = 0x45,
KC_SCROLL = 0x46, // Scroll Lock
KC_NUMPAD7 = 0x47,
KC_NUMPAD8 = 0x48,
KC_NUMPAD9 = 0x49,
KC_SUBTRACT = 0x4A, // - on numeric keypad
KC_NUMPAD4 = 0x4B,
KC_NUMPAD5 = 0x4C,
KC_NUMPAD6 = 0x4D,
KC_ADD = 0x4E, // + on numeric keypad
KC_NUMPAD1 = 0x4F,
KC_NUMPAD2 = 0x50,
KC_NUMPAD3 = 0x51,
KC_NUMPAD0 = 0x52,
KC_DECIMAL = 0x53, // . on numeric keypad
KC_OEM_102 = 0x56, // < > | on UK/Germany keyboards
KC_F11 = 0x57,
KC_F12 = 0x58,
KC_F13 = 0x64, // (NEC PC98)
KC_F14 = 0x65, // (NEC PC98)
KC_F15 = 0x66, // (NEC PC98)
KC_KANA = 0x70, // (Japanese keyboard)
KC_ABNT_C1 = 0x73, // / ? on Portugese (Brazilian) keyboards
KC_CONVERT = 0x79, // (Japanese keyboard)
KC_NOCONVERT = 0x7B, // (Japanese keyboard)
KC_YEN = 0x7D, // (Japanese keyboard)
KC_ABNT_C2 = 0x7E, // Numpad . on Portugese (Brazilian) keyboards
KC_NUMPADEQUALS= 0x8D, // = on numeric keypad (NEC PC98)
KC_PREVTRACK = 0x90, // Previous Track (KC_CIRCUMFLEX on Japanese keyboard)
KC_AT = 0x91, // (NEC PC98)
KC_COLON = 0x92, // (NEC PC98)
KC_UNDERLINE = 0x93, // (NEC PC98)
KC_KANJI = 0x94, // (Japanese keyboard)
KC_STOP = 0x95, // (NEC PC98)
KC_AX = 0x96, // (Japan AX)
KC_UNLABELED = 0x97, // (J3100)
KC_NEXTTRACK = 0x99, // Next Track
KC_NUMPADENTER = 0x9C, // Enter on numeric keypad
KC_RCONTROL = 0x9D,
KC_MUTE = 0xA0, // Mute
KC_CALCULATOR = 0xA1, // Calculator
KC_PLAYPAUSE = 0xA2, // Play / Pause
KC_MEDIASTOP = 0xA4, // Media Stop
KC_VOLUMEDOWN = 0xAE, // Volume -
KC_VOLUMEUP = 0xB0, // Volume +
KC_WEBHOME = 0xB2, // Web home
KC_NUMPADCOMMA = 0xB3, // , on numeric keypad (NEC PC98)
KC_DIVIDE = 0xB5, // / on numeric keypad
KC_SYSRQ = 0xB7,
KC_RMENU = 0xB8, // right Alt
KC_PAUSE = 0xC5, // Pause
KC_HOME = 0xC7, // Home on arrow keypad
KC_UP = 0xC8, // UpArrow on arrow keypad
KC_PGUP = 0xC9, // PgUp on arrow keypad
KC_LEFT = 0xCB, // LeftArrow on arrow keypad
KC_RIGHT = 0xCD, // RightArrow on arrow keypad
KC_END = 0xCF, // End on arrow keypad
KC_DOWN = 0xD0, // DownArrow on arrow keypad
KC_PGDOWN = 0xD1, // PgDn on arrow keypad
KC_INSERT = 0xD2, // Insert on arrow keypad
KC_DELETE = 0xD3, // Delete on arrow keypad
KC_LWIN = 0xDB, // Left Windows key
KC_RWIN = 0xDC, // Right Windows key
KC_APPS = 0xDD, // AppMenu key
KC_POWER = 0xDE, // System Power
KC_SLEEP = 0xDF, // System Sleep
KC_WAKE = 0xE3, // System Wake
KC_WEBSEARCH = 0xE5, // Web Search
KC_WEBFAVORITES= 0xE6, // Web Favorites
KC_WEBREFRESH = 0xE7, // Web Refresh
KC_WEBSTOP = 0xE8, // Web Stop
KC_WEBFORWARD = 0xE9, // Web Forward
KC_WEBBACK = 0xEA, // Web Back
KC_MYCOMPUTER = 0xEB, // My Computer
KC_MAIL = 0xEC, // Mail
KC_MEDIASELECT = 0xED // Media Select
};
}
#endif

@ -79,9 +79,6 @@ public:
/** @remarks The window's visibility changed */
virtual void windowVisibilityChange( bool visible ) {}
/** @remarks The window got / lost input focus */
virtual void windowFocusChange( bool have_focus ) {}
virtual void windowClosed () {}
virtual void windowResized (int x, int y) {}

@ -33,8 +33,6 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> v
mWindowHasFocus(true),
mMouseInWindow(true)
{
_setupOISKeys();
Uint32 flags = SDL_GetWindowFlags(mSDLWindow);
mWindowHasFocus = (flags & SDL_WINDOW_INPUT_FOCUS);
mMouseInWindow = (flags & SDL_WINDOW_MOUSE_FOCUS);
@ -231,15 +229,10 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> v
case SDL_WINDOWEVENT_FOCUS_GAINED:
mWindowHasFocus = true;
updateMouseSettings();
if (mWindowListener)
mWindowListener->windowFocusChange(true);
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
mWindowHasFocus = false;
updateMouseSettings();
if (mWindowListener)
mWindowListener->windowFocusChange(false);
break;
case SDL_WINDOWEVENT_CLOSE:
break;
@ -402,139 +395,4 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> v
return pack_evt;
}
OIS::KeyCode InputWrapper::sdl2OISKeyCode(SDL_Keycode code)
{
OIS::KeyCode kc = OIS::KC_UNASSIGNED;
KeyMap::const_iterator ois_equiv = mKeyMap.find(code);
if(ois_equiv != mKeyMap.end())
kc = ois_equiv->second;
return kc;
}
void InputWrapper::_setupOISKeys()
{
//lifted from OIS's SDLKeyboard.cpp
mKeyMap.insert( KeyMap::value_type(SDLK_UNKNOWN, OIS::KC_UNASSIGNED));
mKeyMap.insert( KeyMap::value_type(SDLK_ESCAPE, OIS::KC_ESCAPE) );
mKeyMap.insert( KeyMap::value_type(SDLK_1, OIS::KC_1) );
mKeyMap.insert( KeyMap::value_type(SDLK_2, OIS::KC_2) );
mKeyMap.insert( KeyMap::value_type(SDLK_3, OIS::KC_3) );
mKeyMap.insert( KeyMap::value_type(SDLK_4, OIS::KC_4) );
mKeyMap.insert( KeyMap::value_type(SDLK_5, OIS::KC_5) );
mKeyMap.insert( KeyMap::value_type(SDLK_6, OIS::KC_6) );
mKeyMap.insert( KeyMap::value_type(SDLK_7, OIS::KC_7) );
mKeyMap.insert( KeyMap::value_type(SDLK_8, OIS::KC_8) );
mKeyMap.insert( KeyMap::value_type(SDLK_9, OIS::KC_9) );
mKeyMap.insert( KeyMap::value_type(SDLK_0, OIS::KC_0) );
mKeyMap.insert( KeyMap::value_type(SDLK_MINUS, OIS::KC_MINUS) );
mKeyMap.insert( KeyMap::value_type(SDLK_EQUALS, OIS::KC_EQUALS) );
mKeyMap.insert( KeyMap::value_type(SDLK_BACKSPACE, OIS::KC_BACK) );
mKeyMap.insert( KeyMap::value_type(SDLK_TAB, OIS::KC_TAB) );
mKeyMap.insert( KeyMap::value_type(SDLK_q, OIS::KC_Q) );
mKeyMap.insert( KeyMap::value_type(SDLK_w, OIS::KC_W) );
mKeyMap.insert( KeyMap::value_type(SDLK_e, OIS::KC_E) );
mKeyMap.insert( KeyMap::value_type(SDLK_r, OIS::KC_R) );
mKeyMap.insert( KeyMap::value_type(SDLK_t, OIS::KC_T) );
mKeyMap.insert( KeyMap::value_type(SDLK_y, OIS::KC_Y) );
mKeyMap.insert( KeyMap::value_type(SDLK_u, OIS::KC_U) );
mKeyMap.insert( KeyMap::value_type(SDLK_i, OIS::KC_I) );
mKeyMap.insert( KeyMap::value_type(SDLK_o, OIS::KC_O) );
mKeyMap.insert( KeyMap::value_type(SDLK_p, OIS::KC_P) );
mKeyMap.insert( KeyMap::value_type(SDLK_RETURN, OIS::KC_RETURN) );
mKeyMap.insert( KeyMap::value_type(SDLK_a, OIS::KC_A) );
mKeyMap.insert( KeyMap::value_type(SDLK_s, OIS::KC_S) );
mKeyMap.insert( KeyMap::value_type(SDLK_d, OIS::KC_D) );
mKeyMap.insert( KeyMap::value_type(SDLK_f, OIS::KC_F) );
mKeyMap.insert( KeyMap::value_type(SDLK_g, OIS::KC_G) );
mKeyMap.insert( KeyMap::value_type(SDLK_h, OIS::KC_H) );
mKeyMap.insert( KeyMap::value_type(SDLK_j, OIS::KC_J) );
mKeyMap.insert( KeyMap::value_type(SDLK_k, OIS::KC_K) );
mKeyMap.insert( KeyMap::value_type(SDLK_l, OIS::KC_L) );
mKeyMap.insert( KeyMap::value_type(SDLK_SEMICOLON, OIS::KC_SEMICOLON) );
mKeyMap.insert( KeyMap::value_type(SDLK_COLON, OIS::KC_COLON) );
mKeyMap.insert( KeyMap::value_type(SDLK_QUOTE, OIS::KC_APOSTROPHE) );
mKeyMap.insert( KeyMap::value_type(SDLK_BACKQUOTE, OIS::KC_GRAVE) );
mKeyMap.insert( KeyMap::value_type(SDLK_LSHIFT, OIS::KC_LSHIFT) );
mKeyMap.insert( KeyMap::value_type(SDLK_BACKSLASH, OIS::KC_BACKSLASH) );
mKeyMap.insert( KeyMap::value_type(SDLK_SLASH, OIS::KC_SLASH) );
mKeyMap.insert( KeyMap::value_type(SDLK_z, OIS::KC_Z) );
mKeyMap.insert( KeyMap::value_type(SDLK_x, OIS::KC_X) );
mKeyMap.insert( KeyMap::value_type(SDLK_c, OIS::KC_C) );
mKeyMap.insert( KeyMap::value_type(SDLK_v, OIS::KC_V) );
mKeyMap.insert( KeyMap::value_type(SDLK_b, OIS::KC_B) );
mKeyMap.insert( KeyMap::value_type(SDLK_n, OIS::KC_N) );
mKeyMap.insert( KeyMap::value_type(SDLK_m, OIS::KC_M) );
mKeyMap.insert( KeyMap::value_type(SDLK_COMMA, OIS::KC_COMMA) );
mKeyMap.insert( KeyMap::value_type(SDLK_PERIOD, OIS::KC_PERIOD));
mKeyMap.insert( KeyMap::value_type(SDLK_RSHIFT, OIS::KC_RSHIFT));
mKeyMap.insert( KeyMap::value_type(SDLK_KP_MULTIPLY, OIS::KC_MULTIPLY) );
mKeyMap.insert( KeyMap::value_type(SDLK_LALT, OIS::KC_LMENU) );
mKeyMap.insert( KeyMap::value_type(SDLK_SPACE, OIS::KC_SPACE));
mKeyMap.insert( KeyMap::value_type(SDLK_CAPSLOCK, OIS::KC_CAPITAL) );
mKeyMap.insert( KeyMap::value_type(SDLK_F1, OIS::KC_F1) );
mKeyMap.insert( KeyMap::value_type(SDLK_F2, OIS::KC_F2) );
mKeyMap.insert( KeyMap::value_type(SDLK_F3, OIS::KC_F3) );
mKeyMap.insert( KeyMap::value_type(SDLK_F4, OIS::KC_F4) );
mKeyMap.insert( KeyMap::value_type(SDLK_F5, OIS::KC_F5) );
mKeyMap.insert( KeyMap::value_type(SDLK_F6, OIS::KC_F6) );
mKeyMap.insert( KeyMap::value_type(SDLK_F7, OIS::KC_F7) );
mKeyMap.insert( KeyMap::value_type(SDLK_F8, OIS::KC_F8) );
mKeyMap.insert( KeyMap::value_type(SDLK_F9, OIS::KC_F9) );
mKeyMap.insert( KeyMap::value_type(SDLK_F10, OIS::KC_F10) );
mKeyMap.insert( KeyMap::value_type(SDLK_NUMLOCKCLEAR, OIS::KC_NUMLOCK) );
mKeyMap.insert( KeyMap::value_type(SDLK_SCROLLLOCK, OIS::KC_SCROLL));
mKeyMap.insert( KeyMap::value_type(SDLK_KP_7, OIS::KC_NUMPAD7) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_8, OIS::KC_NUMPAD8) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_9, OIS::KC_NUMPAD9) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_MINUS, OIS::KC_SUBTRACT) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_4, OIS::KC_NUMPAD4) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_5, OIS::KC_NUMPAD5) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_6, OIS::KC_NUMPAD6) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_PLUS, OIS::KC_ADD) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_1, OIS::KC_NUMPAD1) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_2, OIS::KC_NUMPAD2) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_3, OIS::KC_NUMPAD3) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_0, OIS::KC_NUMPAD0) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_PERIOD, OIS::KC_DECIMAL) );
mKeyMap.insert( KeyMap::value_type(SDLK_F11, OIS::KC_F11) );
mKeyMap.insert( KeyMap::value_type(SDLK_F12, OIS::KC_F12) );
mKeyMap.insert( KeyMap::value_type(SDLK_F13, OIS::KC_F13) );
mKeyMap.insert( KeyMap::value_type(SDLK_F14, OIS::KC_F14) );
mKeyMap.insert( KeyMap::value_type(SDLK_F15, OIS::KC_F15) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_EQUALS, OIS::KC_NUMPADEQUALS) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_DIVIDE, OIS::KC_DIVIDE) );
mKeyMap.insert( KeyMap::value_type(SDLK_SYSREQ, OIS::KC_SYSRQ) );
mKeyMap.insert( KeyMap::value_type(SDLK_RALT, OIS::KC_RMENU) );
mKeyMap.insert( KeyMap::value_type(SDLK_HOME, OIS::KC_HOME) );
mKeyMap.insert( KeyMap::value_type(SDLK_UP, OIS::KC_UP) );
mKeyMap.insert( KeyMap::value_type(SDLK_PAGEUP, OIS::KC_PGUP) );
mKeyMap.insert( KeyMap::value_type(SDLK_LEFT, OIS::KC_LEFT) );
mKeyMap.insert( KeyMap::value_type(SDLK_RIGHT, OIS::KC_RIGHT) );
mKeyMap.insert( KeyMap::value_type(SDLK_END, OIS::KC_END) );
mKeyMap.insert( KeyMap::value_type(SDLK_DOWN, OIS::KC_DOWN) );
mKeyMap.insert( KeyMap::value_type(SDLK_PAGEDOWN, OIS::KC_PGDOWN) );
mKeyMap.insert( KeyMap::value_type(SDLK_INSERT, OIS::KC_INSERT) );
mKeyMap.insert( KeyMap::value_type(SDLK_DELETE, OIS::KC_DELETE) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_ENTER, OIS::KC_NUMPADENTER) );
mKeyMap.insert( KeyMap::value_type(SDLK_APPLICATION, OIS::KC_APPS) );
//The function of the Ctrl and Meta keys are switched on macOS compared to other platforms.
//For instance, Cmd+C versus Ctrl+C to copy from the system clipboard
#if defined(__APPLE__)
mKeyMap.insert( KeyMap::value_type(SDLK_LGUI, OIS::KC_LCONTROL) );
mKeyMap.insert( KeyMap::value_type(SDLK_RGUI, OIS::KC_RCONTROL) );
mKeyMap.insert( KeyMap::value_type(SDLK_LCTRL, OIS::KC_LWIN));
mKeyMap.insert( KeyMap::value_type(SDLK_RCTRL, OIS::KC_RWIN) );
#else
mKeyMap.insert( KeyMap::value_type(SDLK_LGUI, OIS::KC_LWIN) );
mKeyMap.insert( KeyMap::value_type(SDLK_RGUI, OIS::KC_RWIN) );
mKeyMap.insert( KeyMap::value_type(SDLK_LCTRL, OIS::KC_LCONTROL));
mKeyMap.insert( KeyMap::value_type(SDLK_RCTRL, OIS::KC_RCONTROL) );
#endif
}
}

@ -8,7 +8,6 @@
#include <SDL_events.h>
#include <SDL_version.h>
#include "OISCompat.hpp"
#include "events.hpp"
namespace osgViewer
@ -40,8 +39,6 @@ namespace SDLUtil
bool getMouseRelative() { return mMouseRelative; }
void setGrabPointer(bool grab);
OIS::KeyCode sdl2OISKeyCode(SDL_Keycode code);
void warpMouse(int x, int y);
void updateMouseSettings();
@ -53,8 +50,6 @@ namespace SDLUtil
void _wrapMousePointer(const SDL_MouseMotionEvent &evt);
MouseMotionEvent _packageMouseMotion(const SDL_Event& evt);
void _setupOISKeys();
SDL_Window* mSDLWindow;
osg::ref_ptr<osgViewer::Viewer> mViewer;
@ -64,9 +59,6 @@ namespace SDLUtil
WindowListener* mWindowListener;
ControllerListener* mConListener;
typedef std::map<SDL_Keycode, OIS::KeyCode> KeyMap;
KeyMap mKeyMap;
Uint16 mWarpX;
Uint16 mWarpY;
bool mWarpCompensate;

@ -263,15 +263,21 @@ namespace Shader
case osg::Material::OFF:
colorMode = 0;
break;
case GL_AMBIENT:
colorMode = 3;
case osg::Material::EMISSION:
colorMode = 1;
break;
default:
case GL_AMBIENT_AND_DIFFUSE:
case osg::Material::AMBIENT_AND_DIFFUSE:
colorMode = 2;
break;
case GL_EMISSION:
colorMode = 1;
case osg::Material::AMBIENT:
colorMode = 3;
break;
case osg::Material::DIFFUSE:
colorMode = 4;
break;
case osg::Material::SPECULAR:
colorMode = 5;
break;
}

@ -2,6 +2,13 @@
uniform int colorMode;
const int ColorMode_None = 0;
const int ColorMode_Emission = 1;
const int ColorMode_AmbientAndDiffuse = 2;
const int ColorMode_Ambient = 3;
const int ColorMode_Diffuse = 4;
const int ColorMode_Specular = 5;
void perLight(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal, vec4 diffuse, vec3 ambient)
{
vec3 lightDir;
@ -22,22 +29,25 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, float shadowing
vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadowDiffuse)
#endif
{
vec4 diffuse;
vec3 ambient;
if (colorMode == 3)
vec4 diffuse = gl_FrontMaterial.diffuse;
vec3 ambient = gl_FrontMaterial.ambient.xyz;
vec3 emission = gl_FrontMaterial.emission.xyz;
if (colorMode == ColorMode_AmbientAndDiffuse)
{
diffuse = gl_FrontMaterial.diffuse;
diffuse = vertexColor;
ambient = vertexColor.xyz;
}
else if (colorMode == 2)
else if (colorMode == ColorMode_Ambient)
{
diffuse = vertexColor;
ambient = vertexColor.xyz;
}
else
else if (colorMode == ColorMode_Diffuse)
{
diffuse = gl_FrontMaterial.diffuse;
ambient = gl_FrontMaterial.ambient.xyz;
diffuse = vertexColor;
}
else if (colorMode == ColorMode_Emission)
{
emission = vertexColor.xyz;
}
vec4 lightResult = vec4(0.0, 0.0, 0.0, diffuse.a);
@ -55,12 +65,7 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadow
lightResult.xyz += ambientLight + diffuseLight;
}
lightResult.xyz += gl_LightModel.ambient.xyz * ambient;
if (colorMode == 1)
lightResult.xyz += vertexColor.xyz;
else
lightResult.xyz += gl_FrontMaterial.emission.xyz;
lightResult.xyz += gl_LightModel.ambient.xyz * ambient + emission;
#if @clamp
lightResult = clamp(lightResult, vec4(0.0), vec4(1.0));

@ -59,9 +59,8 @@ varying float linearDepth;
#if !PER_PIXEL_LIGHTING
centroid varying vec4 lighting;
centroid varying vec3 shadowDiffuseLighting;
#else
centroid varying vec4 passColor;
#endif
centroid varying vec4 passColor;
varying vec3 passViewPos;
varying vec3 passNormal;
@ -178,6 +177,8 @@ void main()
#else
float shininess = gl_FrontMaterial.shininess;
vec3 matSpec = gl_FrontMaterial.specular.xyz;
if (colorMode == ColorMode_Specular)
matSpec = passColor.xyz;
#endif
gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing;

@ -45,9 +45,8 @@ varying float linearDepth;
#if !PER_PIXEL_LIGHTING
centroid varying vec4 lighting;
centroid varying vec3 shadowDiffuseLighting;
#else
centroid varying vec4 passColor;
#endif
centroid varying vec4 passColor;
varying vec3 passViewPos;
varying vec3 passNormal;
@ -108,9 +107,8 @@ void main(void)
#if !PER_PIXEL_LIGHTING
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting);
#else
passColor = gl_Color;
#endif
passColor = gl_Color;
passViewPos = viewPos.xyz;
passNormal = gl_Normal.xyz;

@ -20,9 +20,8 @@ varying float linearDepth;
#if !PER_PIXEL_LIGHTING
centroid varying vec4 lighting;
centroid varying vec3 shadowDiffuseLighting;
#else
centroid varying vec4 passColor;
#endif
centroid varying vec4 passColor;
varying vec3 passViewPos;
varying vec3 passNormal;
@ -83,10 +82,12 @@ void main()
#if @specularMap
float shininess = 128.0; // TODO: make configurable
vec3 matSpec = vec3(diffuseTex.a, diffuseTex.a, diffuseTex.a);
vec3 matSpec = vec3(diffuseTex.a);
#else
float shininess = gl_FrontMaterial.shininess;
vec3 matSpec = gl_FrontMaterial.specular.xyz;
if (colorMode == ColorMode_Specular)
matSpec = passColor.xyz;
#endif
gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing;

@ -9,9 +9,8 @@ varying float linearDepth;
#if !PER_PIXEL_LIGHTING
centroid varying vec4 lighting;
centroid varying vec3 shadowDiffuseLighting;
#else
centroid varying vec4 passColor;
#endif
centroid varying vec4 passColor;
varying vec3 passViewPos;
varying vec3 passNormal;
@ -32,9 +31,8 @@ void main(void)
#if !PER_PIXEL_LIGHTING
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting);
#else
passColor = gl_Color;
#endif
passColor = gl_Color;
passNormal = gl_Normal.xyz;
passViewPos = viewPos.xyz;

Loading…
Cancel
Save