Merge branch 'master' into 'near_far_mode_in_launcher'

# Conflicts:
#   files/settings-default.cfg
pull/578/head
psi29a 5 years ago
commit 66b5cf9f1d

1
.gitignore vendored

@ -31,6 +31,7 @@ files/windows/*.aps
## qt-creator
CMakeLists.txt.user*
.vs
.vscode
## resources
data

@ -2,14 +2,34 @@
------
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 #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
Bug #4021: Attributes and skills are not stored as floats
Bug #4623: Corprus implementation is incorrect
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 #5165: Active spells should use real time intead of timestamps
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
Bug #5367: Selecting a spell on an enchanted item per hotkey always plays the equip sound
Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures
Bug #5370: Opening an unlocked but trapped door uses the key
Bug #5397: NPC greeting does not reset if you leave + reenter area
Bug #5400: Editor: Verifier checks race of non-skin bodyparts
Bug #5403: Enchantment effect doesn't show on an enemy during death animation
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
Bug #5424: Creatures do not headtrack player
Bug #5425: Poison effect only appears for one frame
Bug #5427: GetDistance unknown ID error is misleading
Bug #5435: Enemies can't hurt the player when collision is off
Bug #5441: Enemies can't push a player character when in critical strike stance
Bug #5451: Magic projectiles don't disappear with the caster
Bug #5452: Autowalk is being included in savegames
Feature #5362: Show the soul gems' trapped soul in count dialog
Feature #5445: Handle NiLines
0.46.0
------
@ -182,8 +202,8 @@
Bug #5158: Objects without a name don't fallback to their ID
Bug #5159: NiMaterialColorController can only control the diffuse color
Bug #5161: Creature companions can't be activated when they are knocked down
Bug #5164: Faction owned items handling is incorrect
Bug #5163: UserData is not copied during node cloning
Bug #5164: Faction owned items handling is incorrect
Bug #5166: Scripts still should be executed after player's death
Bug #5167: Player can select and cast spells before magic menu is enabled
Bug #5168: Force1stPerson and Force3rdPerson commands are not really force view change
@ -217,7 +237,6 @@
Bug #5264: "Damage Fatigue" Magic Effect Can Bring Fatigue below 0
Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted
Bug #5278: Console command Show doesn't fall back to global variable after local var not found
Bug #5300: NPCs don't switch from torch to shield when starting combat
Bug #5308: World map copying makes save loading much slower
Bug #5313: Node properties of identical type are not applied in the correct order
Bug #5326: Formatting issues in the settings.cfg
@ -282,7 +301,7 @@
Feature #5147: Show spell magicka cost in spell buying window
Feature #5170: Editor: Land shape editing, land selection
Feature #5172: Editor: Delete instances/references with keypress in scene window
Feature #5193: Weapon sheathing
Feature #5193: Shields sheathing
Feature #5201: Editor: Show tool outline in scene view, when using editmodes
Feature #5219: Impelement TestCells console command
Feature #5224: Handle NiKeyframeController for NiTriShape

@ -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)

@ -0,0 +1,28 @@
& "${env:COMSPEC}" /c ActivateMSVC.bat "&&" set | ForEach-Object {
if ($_.Contains("=")) {
$name, $value = $_ -split '=', 2
Set-Content env:\"$name" $value
}
}
$MissingTools = $false
$tools = "cl", "link", "rc", "mt"
$descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool"
for ($i = 0; $i -lt $tools.Length; $i++) {
$present = $true
try {
Get-Command $tools[$i] *>&1 | Out-Null
$present = $present -and $?
} catch {
$present = $false
}
if (!$present) {
Write-Warning "$($tools[$i]) ($($descriptions[$i])) missing."
$MissingTools = $true
}
}
if ($MissingTools) {
Write-Error "Some build tools were unavailable after activating MSVC in the shell. It's likely that your Visual Studio $MSVC_DISPLAY_YEAR installation needs repairing."
exit 1
}

@ -0,0 +1,76 @@
#!/bin/bash
set -euo pipefail
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
echo "Error: Script not sourced."
echo "You must source this script for it to work, i.e. "
echo "source ./activate_msvc.sh"
echo "or"
echo ". ./activate_msvc.sh"
exit 1
fi
command -v unixPathAsWindows >/dev/null 2>&1 || function unixPathAsWindows {
if command -v cygpath >/dev/null 2>&1; then
cygpath -w $1
else
echo "$1" | sed "s,^/\([^/]\)/,\\1:/," | sed "s,/,\\\\,g"
fi
}
function windowsSystemPathAsUnix {
if command -v cygpath >/dev/null 2>&1; then
cygpath -u -p $1
else
IFS=';' read -r -a paths <<< "$1"
declare -a convertedPaths
for entry in paths; do
convertedPaths+=(windowsPathAsUnix $entry)
done
convertedPath=printf ":%s" ${convertedPaths[@]}
echo ${convertedPath:1}
fi
}
# capture CMD environment so we know what's been changed
declare -A originalCmdEnv
originalIFS="$IFS"
IFS=$'\n\r'
for pair in $(cmd //c "set"); do
IFS='=' read -r -a separatedPair <<< "${pair}"
originalCmdEnv["${separatedPair[0]}"]="${separatedPair[1]}"
done
# capture CMD environment in a shell with MSVC activated
cmdEnv="$(cmd //c "$(unixPathAsWindows "$(dirname "${BASH_SOURCE[0]}")")\ActivateMSVC.bat" "&&" set)"
declare -A cmdEnvChanges
for pair in $cmdEnv; do
if [ -n "$pair" ]; then
IFS='=' read -r -a separatedPair <<< "${pair}"
key="${separatedPair[0]}"
value="${separatedPair[1]}"
if ! [ ${originalCmdEnv[$key]+_} ] || [ "${originalCmdEnv[$key]}" != "$value" ]; then
if [ $key != 'PATH' ] && [ $key != 'path' ] && [ $key != 'Path' ]; then
export "$key=$value"
else
export PATH=$(windowsSystemPathAsUnix $value)
fi
fi
fi
done
MISSINGTOOLS=0
command -v cl >/dev/null 2>&1 || { echo "Error: cl (MSVC Compiler) missing."; MISSINGTOOLS=1; }
command -v link >/dev/null 2>&1 || { echo "Error: link (MSVC Linker) missing."; MISSINGTOOLS=1; }
command -v rc >/dev/null 2>&1 || { echo "Error: rc (MS Windows Resource Compiler) missing."; MISSINGTOOLS=1; }
command -v mt >/dev/null 2>&1 || { echo "Error: mt (MS Windows Manifest Tool) missing."; MISSINGTOOLS=1; }
if [ $MISSINGTOOLS -ne 0 ]; then
echo "Some build tools were unavailable after activating MSVC in the shell. It's likely that your Visual Studio $MSVC_DISPLAY_YEAR installation needs repairing."
return 1
fi
IFS="$originalIFS"

@ -25,7 +25,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_UNITTESTS=1 \
-DUSE_SYSTEM_TINYXML=1 \
-DDESIRED_QT_VERSION=5 \

@ -1,25 +1,59 @@
#!/bin/bash
# set -x # turn-on for debugging
function wrappedExit {
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
exit $1
else
return $1
fi
}
MISSINGTOOLS=0
command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; }
command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; }
MISSINGPYTHON=0
if ! command -v python >/dev/null 2>&1; then
echo "Warning: Python is not on the path, automatic Qt installation impossible."
MISSINGPYTHON=1
elif ! python --version >/dev/null 2>&1; then
echo "Warning: Python is (probably) fake stub Python that comes bundled with newer versions of Windows, automatic Qt installation impossible."
echo "If you think you have Python installed, try changing the order of your PATH environment variable in Advanced System Settings."
MISSINGPYTHON=1
fi
if [ $MISSINGTOOLS -ne 0 ]; then
exit 1
wrappedExit 1
fi
WORKINGDIR="$(pwd)"
case "$WORKINGDIR" in
*[[:space:]]*)
echo "Error: Working directory contains spaces."
exit 1
wrappedExit 1
;;
esac
set -euo pipefail
function windowsPathAsUnix {
if command -v cygpath >/dev/null 2>&1; then
cygpath -u $1
else
echo "$1" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,"
fi
}
function unixPathAsWindows {
if command -v cygpath >/dev/null 2>&1; then
cygpath -w $1
else
echo "$1" | sed "s,^/\([^/]\)/,\\1:/," | sed "s,/,\\\\,g"
fi
}
APPVEYOR=${APPVEYOR:-}
CI=${CI:-}
STEP=${STEP:-}
@ -32,10 +66,19 @@ KEEP=""
UNITY_BUILD=""
VS_VERSION=""
NMAKE=""
NINJA=""
PDBS=""
PLATFORM=""
CONFIGURATION=""
TEST_FRAMEWORK=""
GOOGLE_INSTALL_ROOT=""
INSTALL_PREFIX="."
BULLET_DOUBLE=""
BULLET_DBL=""
BULLET_DBL_DISPLAY="Single precision"
ACTIVATE_MSVC=""
SINGLE_CONFIG=""
while [ $# -gt 0 ]; do
ARGSTR=$1
@ -44,7 +87,7 @@ while [ $# -gt 0 ]; do
if [ ${ARGSTR:0:1} != "-" ]; then
echo "Unknown argument $ARGSTR"
echo "Try '$0 -h'"
exit 1
wrappedExit 1
fi
for (( i=1; i<${#ARGSTR}; i++ )); do
@ -56,6 +99,9 @@ while [ $# -gt 0 ]; do
d )
SKIP_DOWNLOAD=true ;;
D )
BULLET_DOUBLE=true ;;
e )
SKIP_EXTRACT=true ;;
@ -71,11 +117,17 @@ while [ $# -gt 0 ]; do
n )
NMAKE=true ;;
N )
NINJA=true ;;
p )
PLATFORM=$1
shift ;;
P )
PDBS=true ;;
c )
CONFIGURATION=$1
shift ;;
@ -83,14 +135,20 @@ 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.
-d
Skip checking the downloads.
-D
Use double-precision Bullet
-e
Skip extracting dependencies.
-h
@ -106,23 +164,33 @@ Options:
-v <2013/2015/2017/2019>
Choose the Visual Studio version to use.
-n
Produce NMake makefiles instead of a Visual Studio solution.
Produce NMake makefiles instead of a Visual Studio solution. Cannout be used with -N.
-N
Produce Ninja (multi-config if CMake is new enough to support it) files instead of a Visual Studio solution. Cannot be used with -n..
-P
Download debug symbols where available
-V
Run verbosely
-i
CMake install prefix
EOF
exit 0
wrappedExit 0
;;
* )
echo "Unknown argument $ARG."
echo "Try '$0 -h'"
exit 1 ;;
wrappedExit 1 ;;
esac
done
done
if [ -n "$NMAKE" ]; then
command -v nmake -? >/dev/null 2>&1 || { echo "Error: nmake (NMake) is not on the path. Make sure you have the necessary environment variables set for command-line C++ development (for example, by starting from a Developer Command Prompt)."; exit 1; }
if [ -n "$NMAKE" ] || [ -n "$NINJA" ]; then
if [ -n "$NMAKE" ] && [ -n "$NINJA" ]; then
echo "Cannout run in NMake and Ninja mode at the same time."
wrappedExit 1
fi
ACTIVATE_MSVC=true
fi
if [ -z $VERBOSE ]; then
@ -132,7 +200,7 @@ fi
if [ -z $APPVEYOR ]; then
echo "Running prebuild outside of Appveyor."
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
DIR=$(windowsPathAsUnix "${BASH_SOURCE[0]}")
cd $(dirname "$DIR")/..
else
echo "Running prebuild in Appveyor."
@ -145,8 +213,8 @@ run_cmd() {
shift
if [ -z $VERBOSE ]; then
eval $CMD $@ > output.log 2>&1
RET=$?
RET=0
eval $CMD $@ > output.log 2>&1 || RET=$?
if [ $RET -ne 0 ]; then
if [ -z $APPVEYOR ]; then
@ -162,8 +230,9 @@ run_cmd() {
return $RET
else
eval $CMD $@
return $?
RET=0
eval $CMD $@ || RET=$?
return $RET
fi
}
@ -188,15 +257,16 @@ download() {
printf " Downloading $FILE... "
if [ -z $VERBOSE ]; then
curl --silent --retry 10 -kLy 5 -o $FILE $URL
RET=$?
RET=0
curl --silent --retry 10 -kLy 5 -o $FILE $URL || RET=$?
else
curl --retry 10 -kLy 5 -o $FILE $URL
RET=$?
RET=0
curl --retry 10 -kLy 5 -o $FILE $URL || RET=$?
fi
if [ $RET -ne 0 ]; then
echo "Failed!"
wrappedExit $RET
else
echo "Done."
fi
@ -257,6 +327,7 @@ case $VS_VERSION in
MSVC_REAL_VER="16"
MSVC_VER="14.2"
MSVC_YEAR="2015"
MSVC_REAL_YEAR="2019"
MSVC_DISPLAY_YEAR="2019"
BOOST_VER="1.71.0"
BOOST_VER_URL="1_71_0"
@ -269,6 +340,7 @@ case $VS_VERSION in
MSVC_REAL_VER="15"
MSVC_VER="14.1"
MSVC_YEAR="2015"
MSVC_REAL_YEAR="2017"
MSVC_DISPLAY_YEAR="2017"
BOOST_VER="1.67.0"
BOOST_VER_URL="1_67_0"
@ -281,6 +353,7 @@ case $VS_VERSION in
MSVC_REAL_VER="14"
MSVC_VER="14.0"
MSVC_YEAR="2015"
MSVC_REAL_YEAR="2015"
MSVC_DISPLAY_YEAR="2015"
BOOST_VER="1.67.0"
BOOST_VER_URL="1_67_0"
@ -288,15 +361,8 @@ case $VS_VERSION in
;;
12|12.0|2013 )
GENERATOR="Visual Studio 12 2013"
TOOLSET="vc120"
MSVC_REAL_VER="12"
MSVC_VER="12.0"
MSVC_YEAR="2013"
MSVC_DISPLAY_YEAR="2013"
BOOST_VER="1.58.0"
BOOST_VER_URL="1_58_0"
BOOST_VER_SDK="105800"
echo "Visual Studio 2013 is no longer supported"
exit 1
;;
esac
@ -315,7 +381,7 @@ case $PLATFORM in
* )
echo "Unknown platform $PLATFORM."
exit 1
wrappedExit 1
;;
esac
@ -342,9 +408,18 @@ fi
if [ -n "$NMAKE" ]; then
GENERATOR="NMake Makefiles"
SINGLE_CONFIG=true
fi
if [ $MSVC_REAL_VER -ge 16 ]; then
if [ -n "$NINJA" ]; then
GENERATOR="Ninja Multi-Config"
if ! cmake -E capabilities | grep -F "$GENERATOR" > /dev/null; then
SINGLE_CONFIG=true
GENERATOR="Ninja"
fi
fi
if [ $MSVC_REAL_VER -ge 16 ] && [ -z "$NMAKE" ] && [ -z "$NINJA" ]; then
if [ $BITS -eq 64 ]; then
add_cmake_opts "-G\"$GENERATOR\" -A x64"
else
@ -354,7 +429,7 @@ else
add_cmake_opts "-G\"$GENERATOR\""
fi
if [ -n "$NMAKE" ]; then
if [ -n "$SINGLE_CONFIG" ]; then
add_cmake_opts "-DCMAKE_BUILD_TYPE=${BUILD_CONFIG}"
fi
@ -362,6 +437,12 @@ if ! [ -z $UNITY_BUILD ]; then
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
fi
if [ -n "$BULLET_DOUBLE" ]; then
BULLET_DBL="-double"
BULLET_DBL_DISPLAY="Double precision"
add_cmake_opts "-DBULLET_USE_DOUBLES=True"
fi
echo
echo "==================================="
echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}"
@ -386,45 +467,54 @@ 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.89 (${BULLET_DBL_DISPLAY})" \
"https://rgw.ctrl-c.liu.se/openmw/Deps/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" \
"Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z"
# FFmpeg
download "FFmpeg 3.2.4" \
"https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-3.2.4-win${BITS}-shared.zip" \
"ffmpeg-3.2.4-win${BITS}.zip" \
"https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-3.2.4-win${BITS}-dev.zip" \
"ffmpeg-3.2.4-dev-win${BITS}.zip"
download "FFmpeg 4.2.2" \
"https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-4.2.2-win${BITS}-shared.zip" \
"ffmpeg-4.2.2-win${BITS}.zip" \
"https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-4.2.2-win${BITS}-dev.zip" \
"ffmpeg-4.2.2-dev-win${BITS}.zip"
# MyGUI
download "MyGUI 3.2.2" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" \
"MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z"
download "MyGUI 3.4.0" \
"https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
if [ -n "$PDBS" ]; then
download "MyGUI symbols" \
"https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
"MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
fi
# OpenAL
download "OpenAL-Soft 1.19.1" \
"http://openal-soft.org/openal-binaries/openal-soft-1.19.1-bin.zip" \
"OpenAL-Soft-1.19.1.zip"
download "OpenAL-Soft 1.20.1" \
"http://openal-soft.org/openal-binaries/openal-soft-1.20.1-bin.zip" \
"OpenAL-Soft-1.20.1.zip"
# OSG
download "OpenSceneGraph 3.4.1-scrawl" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \
"OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z"
download "OpenSceneGraph 3.6.5" \
"https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
if [ -n "$PDBS" ]; then
download "OpenSceneGraph symbols" \
"https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
fi
# Qt
if [ -z $APPVEYOR ]; then
if [ $BITS == "64" ]; then
QT_SUFFIX="_64"
else
QT_SUFFIX=""
if [ "${MSVC_REAL_YEAR}" = "2015" ] && [ "${BITS}" = "32" ]; then
echo "Qt no longer provides MSVC2015 Win32 packages, switch to 64-bit or a newer Visual Studio. Sorry."
exit 1
fi
download "Qt 5.7.0" \
"https://download.qt.io/new_archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
"qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
"qt-5-install.qs"
download "AQt installer" \
"https://files.pythonhosted.org/packages/f3/bb/aee972f08deecca31bfc46b5aedfad1ce6c7f3aaf1288d685e4a914b53ac/aqtinstall-0.8-py2.py3-none-any.whl" \
"aqtinstall-0.8-py2.py3-none-any.whl"
fi
# SDL2
@ -449,7 +539,13 @@ cd .. #/..
BUILD_DIR="MSVC${MSVC_DISPLAY_YEAR}_${BITS}"
if [ -n "$NMAKE" ]; then
BUILD_DIR="${BUILD_DIR}_NMake_${BUILD_CONFIG}"
BUILD_DIR="${BUILD_DIR}_NMake"
elif [ -n "$NINJA" ]; then
BUILD_DIR="${BUILD_DIR}_Ninja"
fi
if [ -n "$SINGLE_CONFIG" ]; then
BUILD_DIR="${BUILD_DIR}_${BUILD_CONFIG}"
fi
if [ -z $KEEP ]; then
@ -487,10 +583,10 @@ fi
# We work around this by installing to root of the current working drive and then move it to our deps
# get the current working drive's root, we'll install to that temporarily
CWD_DRIVE_ROOT="$(powershell -command '(get-location).Drive.Root')Boost_temp"
CWD_DRIVE_ROOT_BASH=$(echo "$CWD_DRIVE_ROOT" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
CWD_DRIVE_ROOT_BASH=$(windowsPathAsUnix "$CWD_DRIVE_ROOT")
if [ -d CWD_DRIVE_ROOT_BASH ]; then
printf "Cannot continue, ${CWD_DRIVE_ROOT_BASH} aka ${CWD_DRIVE_ROOT} already exists. Please remove before re-running. ";
exit 1;
wrappedExit 1;
fi
if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION ${BOOST_VER_SDK}" Boost/boost/version.hpp > /dev/null; then
@ -526,15 +622,15 @@ fi
cd $DEPS
echo
# Bullet
printf "Bullet 2.86... "
printf "Bullet 2.89 (${BULLET_DBL_DISPLAY})... "
{
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.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" $STRIP
mv "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}" Bullet
fi
export BULLET_ROOT="$(real_pwd)/Bullet"
echo Done.
@ -542,21 +638,21 @@ printf "Bullet 2.86... "
cd $DEPS
echo
# FFmpeg
printf "FFmpeg 3.2.4... "
printf "FFmpeg 4.2.2... "
{
cd $DEPS_INSTALL
if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then
if [ -d FFmpeg ] && grep "4.2.2" FFmpeg/README.txt > /dev/null; then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf FFmpeg
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP
mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg
cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/
rm -rf "ffmpeg-3.2.4-win${BITS}-dev"
eval 7z x -y "${DEPS}/ffmpeg-4.2.2-win${BITS}.zip" $STRIP
eval 7z x -y "${DEPS}/ffmpeg-4.2.2-dev-win${BITS}.zip" $STRIP
mv "ffmpeg-4.2.2-win${BITS}-shared" FFmpeg
cp -r "ffmpeg-4.2.2-win${BITS}-dev/"* FFmpeg/
rm -rf "ffmpeg-4.2.2-win${BITS}-dev"
fi
export FFMPEG_HOME="$(real_pwd)/FFmpeg"
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-58,avformat-58,avutil-56,swresample-3,swscale-5}.dll
if [ $BITS -eq 32 ]; then
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
fi
@ -565,62 +661,66 @@ printf "FFmpeg 3.2.4... "
cd $DEPS
echo
# MyGUI
printf "MyGUI 3.2.2... "
printf "MyGUI 3.4.0... "
{
cd $DEPS_INSTALL
if [ -d MyGUI ] && \
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_PATCH 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
grep "MYGUI_VERSION_MINOR 4" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_PATCH 0" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf MyGUI
eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI
eval 7z x -y "${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP
mv "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}" MyGUI
fi
export MYGUI_HOME="$(real_pwd)/MyGUI"
if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="_d"
MYGUI_CONFIGURATION="Debug"
else
SUFFIX=""
MYGUI_CONFIGURATION="RelWithDebInfo"
fi
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
add_runtime_dlls "$(pwd)/MyGUI/bin/${MYGUI_CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
echo Done.
}
cd $DEPS
echo
# OpenAL
printf "OpenAL-Soft 1.19.1... "
printf "OpenAL-Soft 1.20.1... "
{
if [ -d openal-soft-1.19.1-bin ]; then
if [ -d openal-soft-1.20.1-bin ]; then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf openal-soft-1.19.1-bin
eval 7z x -y OpenAL-Soft-1.19.1.zip $STRIP
rm -rf openal-soft-1.20.1-bin
eval 7z x -y OpenAL-Soft-1.20.1.zip $STRIP
fi
OPENAL_SDK="$(real_pwd)/openal-soft-1.19.1-bin"
OPENAL_SDK="$(real_pwd)/openal-soft-1.20.1-bin"
add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib"
add_runtime_dlls "$(pwd)/openal-soft-1.19.1-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
add_runtime_dlls "$(pwd)/openal-soft-1.20.1-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
echo Done.
}
cd $DEPS
echo
# OSG
printf "OSG 3.4.1-scrawl... "
printf "OSG 3.6.5... "
{
cd $DEPS_INSTALL
if [ -d OSG ] && \
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_PATCH_VERSION 1" OSG/include/osg/Version > /dev/null
grep "OPENSCENEGRAPH_MINOR_VERSION 6" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_PATCH_VERSION 5" OSG/include/osg/Version > /dev/null
then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf OSG
eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
eval 7z x -y "${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP
mv "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}" OSG
fi
OSG_SDK="$(real_pwd)/OSG"
add_cmake_opts -DOSG_DIR="$OSG_SDK"
@ -629,17 +729,17 @@ printf "OSG 3.4.1-scrawl... "
else
SUFFIX=""
fi
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll
echo Done.
}
cd $DEPS
echo
# Qt
if [ -z $APPVEYOR ]; then
printf "Qt 5.7.0... "
printf "Qt 5.15.0... "
else
printf "Qt 5.13 AppVeyor... "
fi
@ -651,21 +751,49 @@ fi
fi
if [ -z $APPVEYOR ]; then
cd $DEPS_INSTALL
QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then
QT_SDK="$(real_pwd)/Qt/5.15.0/msvc${MSVC_REAL_YEAR}${SUFFIX}"
if [ -d 'Qt/5.15.0' ]; then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
if [ $MISSINGPYTHON -ne 0 ]; then
echo "Can't be automatically installed without Python."
wrappedExit 1
fi
pushd "$DEPS" > /dev/null
if ! [ -d 'aqt-venv' ]; then
echo " Creating Virtualenv for aqt..."
eval python -m venv aqt-venv $STRIP
fi
if [ -d 'aqt-venv/bin' ]; then
VENV_BIN_DIR='bin'
elif [ -d 'aqt-venv/Scripts' ]; then
VENV_BIN_DIR='Scripts'
else
echo "Error: Failed to create virtualenv."
exit 1
fi
if ! [ -e "aqt-venv/${VENV_BIN_DIR}/aqt" ]; then
echo " Installing aqt wheel into virtualenv..."
eval "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall-0.8-py2.py3-none-any.whl $STRIP
fi
popd > /dev/null
rm -rf Qt
cp "${DEPS}/qt-5-install.qs" qt-install.qs
sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs
sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs
printf -- "(Installation might take a while) "
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent
mv qt-install.qs Qt/
echo Done.
mkdir Qt
cd Qt
eval "${DEPS}/aqt-venv/${VENV_BIN_DIR}/aqt" install 5.15.0 windows desktop "win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}" $STRIP
printf " Cleaning up extraneous data... "
rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs}
rm -rf Qt/{aqtinstall.log,Tools}
echo Done.
fi
cd $QT_SDK
add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
@ -688,7 +816,7 @@ fi
else
SUFFIX=""
fi
DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
DIR=$(windowsPathAsUnix "${QT_SDK}")
add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll"
echo Done.
@ -759,10 +887,8 @@ echo
cd $DEPS_INSTALL/..
echo
echo "Setting up OpenMW build..."
add_cmake_opts -DBUILD_BSATOOL=no \
-DBUILD_ESMTOOL=no \
-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 )
@ -801,14 +927,15 @@ fi
#if [ -z $CI ]; then
echo "- Copying Runtime DLLs..."
DLL_PREFIX=""
if [ -z $NMAKE ]; then
if [ -z $SINGLE_CONFIG ]; then
mkdir -p $BUILD_CONFIG
DLL_PREFIX="$BUILD_CONFIG/"
fi
for DLL in $RUNTIME_DLLS; do
TARGET="$(basename "$DLL")"
if [[ "$DLL" == *":"* ]]; then
IFS=':'; SPLIT=( ${DLL} ); unset IFS
originalIFS="$IFS"
IFS=':'; SPLIT=( ${DLL} ); IFS=$originalIFS
DLL=${SPLIT[0]}
TARGET=${SPLIT[1]}
fi
@ -817,10 +944,10 @@ fi
done
echo
echo "- OSG Plugin DLLs..."
mkdir -p ${DLL_PREFIX}osgPlugins-3.4.1
mkdir -p ${DLL_PREFIX}osgPlugins-3.6.5
for DLL in $OSG_PLUGINS; do
echo " $(basename $DLL)."
cp "$DLL" ${DLL_PREFIX}osgPlugins-3.4.1
cp "$DLL" ${DLL_PREFIX}osgPlugins-3.6.5
done
echo
echo "- Qt Platform DLLs..."
@ -831,13 +958,49 @@ fi
done
echo
#fi
if [ -n "$ACTIVATE_MSVC" ]; then
echo -n "- Activating MSVC in the current shell... "
command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; }
MSVC_INSTALLATION_PATH=$(vswhere -legacy -products '*' -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath)
if [ $MSVC_REAL_VER -ge 15 ]; then
echo "@\"${MSVC_INSTALLATION_PATH}\Common7\Tools\VsDevCmd.bat\" -no_logo -arch=$([ $BITS -eq 64 ] && echo "amd64" || echo "x86") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo "amd64" || echo "x86")" > ActivateMSVC.bat
else
if [ $(uname -m) == 'x86_64' ]; then
if [ $BITS -eq 64 ]; then
compiler=amd64
else
compiler=amd64_x86
fi
else
if [ $BITS -eq 64 ]; then
compiler=x86_amd64
else
compiler=x86
fi
fi
echo "@\"${MSVC_INSTALLATION_PATH}\VC\vcvarsall.bat\" $compiler" > ActivateMSVC.bat
fi
cp "../CI/activate_msvc.sh" .
sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" activate_msvc.sh
source ./activate_msvc.sh
cp "../CI/ActivateMSVC.ps1" .
sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" ActivateMSVC.ps1
echo "done."
echo
fi
if [ -z $VERBOSE ]; then
printf -- "- Configuring... "
else
echo "- cmake .. $CMAKE_OPTS"
fi
run_cmd cmake .. $CMAKE_OPTS
RET=$?
RET=0
run_cmd cmake .. $CMAKE_OPTS || RET=$?
if [ -z $VERBOSE ]; then
if [ $RET -eq 0 ]; then
echo Done.
@ -845,4 +1008,38 @@ if [ -z $VERBOSE ]; then
echo Failed.
fi
fi
exit $RET
if [ $RET -ne 0 ]; then
wrappedExit $RET
fi
echo "Script completed successfully."
echo "You now have an OpenMW build system at $(unixPathAsWindows "$(pwd)")"
if [ -n "$ACTIVATE_MSVC" ]; then
echo
echo "Note: you must manually activate MSVC for the shell in which you want to do the build."
echo
echo "Some scripts have been created in the build directory to do so in an existing shell."
echo "Bash: source activate_msvc.sh"
echo "CMD: ActivateMSVC.bat"
echo "PowerShell: ActivateMSVC.ps1"
echo
echo "You may find options to launch a Development/Native Tools/Cross Tools shell in your start menu or Visual Studio."
echo
if [ $(uname -m) == 'x86_64' ]; then
if [ $BITS -eq 64 ]; then
inheritEnvironments=msvc_x64_x64
else
inheritEnvironments=msvc_x64
fi
else
if [ $BITS -eq 64 ]; then
inheritEnvironments=msvc_x86_x64
else
inheritEnvironments=msvc_x86
fi
fi
echo "In Visual Studio 15.3 (2017 Update 3) or later, try setting '\"inheritEnvironments\": [ \"$inheritEnvironments\" ]' in CMakeSettings.json to build in the IDE."
fi
wrappedExit $RET

@ -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" \
..

@ -1,6 +1,6 @@
#!/bin/sh -e
git clone -b release-1.8.1 https://github.com/google/googletest.git
git clone -b release-1.10.0 https://github.com/google/googletest.git
cd googletest
mkdir build
cd build

@ -8,10 +8,10 @@ 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)
option(BULLET_USE_DOUBLES "Use double precision for Bullet" OFF)
if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
set(USE_QT FALSE)
@ -468,9 +468,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}" )
@ -517,11 +514,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)
@ -913,4 +905,3 @@ if (DOXYGEN_FOUND)
WORKING_DIRECTORY ${OpenMW_BINARY_DIR}
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
endif ()

@ -183,19 +183,19 @@ int list(Bsa::BSAFile& bsa, Arguments& info)
{
// List all files
const Bsa::BSAFile::FileList &files = bsa.getList();
for(unsigned int i=0; i<files.size(); i++)
for (const auto& file : files)
{
if(info.longformat)
{
// Long format
std::ios::fmtflags f(std::cout.flags());
std::cout << std::setw(50) << std::left << files[i].name;
std::cout << std::setw(8) << std::left << std::dec << files[i].fileSize;
std::cout << "@ 0x" << std::hex << files[i].offset << std::endl;
std::cout << std::setw(50) << std::left << file.name;
std::cout << std::setw(8) << std::left << std::dec << file.fileSize;
std::cout << "@ 0x" << std::hex << file.offset << std::endl;
std::cout.flags(f);
}
else
std::cout << files[i].name << std::endl;
std::cout << file.name << std::endl;
}
return 0;
@ -252,14 +252,9 @@ int extract(Bsa::BSAFile& bsa, Arguments& info)
int extractAll(Bsa::BSAFile& bsa, Arguments& info)
{
// Get the list of files present in the archive
Bsa::BSAFile::FileList list = bsa.getList();
// Iter on the list
for(Bsa::BSAFile::FileList::iterator it = list.begin(); it != list.end(); ++it) {
const char* archivePath = it->name;
std::string extractPath (archivePath);
for (const auto &file : bsa.getList())
{
std::string extractPath(file.name);
replaceAll(extractPath, "\\", "/");
// Get the target path (the path the file will be extracted to)
@ -278,7 +273,7 @@ int extractAll(Bsa::BSAFile& bsa, Arguments& info)
// Get a stream for the file to extract
// (inefficient because getFile iter on the list again)
Files::IStreamPtr data = bsa.getFile(archivePath);
Files::IStreamPtr data = bsa.getFile(file.name);
bfs::ofstream out(target, std::ios::binary);
// Write the file to disk

@ -352,12 +352,12 @@ int load(Arguments& info)
std::cout << "Author: " << esm.getAuthor() << std::endl
<< "Description: " << esm.getDesc() << std::endl
<< "File format version: " << esm.getFVer() << std::endl;
std::vector<ESM::Header::MasterData> m = esm.getGameFiles();
if (!m.empty())
std::vector<ESM::Header::MasterData> masterData = esm.getGameFiles();
if (!masterData.empty())
{
std::cout << "Masters:" << std::endl;
for(unsigned int i=0;i<m.size();i++)
std::cout << " " << m[i].name << ", " << m[i].size << " bytes" << std::endl;
for(const auto& master : masterData)
std::cout << " " << master.name << ", " << master.size << " bytes" << std::endl;
}
}
@ -369,7 +369,7 @@ int load(Arguments& info)
esm.getRecHeader(flags);
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
if (record == 0)
if (record == nullptr)
{
if (std::find(skipped.begin(), skipped.end(), n.intval) == skipped.end())
{
@ -538,8 +538,8 @@ int comp(Arguments& info)
Arguments fileOne;
Arguments fileTwo;
fileOne.raw_given = 0;
fileTwo.raw_given = 0;
fileOne.raw_given = false;
fileTwo.raw_given = false;
fileOne.mode = "clone";
fileTwo.mode = "clone";

@ -779,7 +779,7 @@ std::string creatureListFlags(int flags)
std::string lightFlags(int flags)
{
std::string properties = "";
std::string properties;
if (flags == 0) properties += "[None] ";
if (flags & ESM::Light::Dynamic) properties += "Dynamic ";
if (flags & ESM::Light::Fire) properties += "Fire ";

@ -9,7 +9,7 @@
namespace
{
void printAIPackage(ESM::AIPackage p)
void printAIPackage(const ESM::AIPackage& p)
{
std::cout << " AI Type: " << aiTypeLabel(p.mType)
<< " (" << Misc::StringUtils::format("0x%08X", p.mType) << ")" << std::endl;
@ -53,7 +53,7 @@ void printAIPackage(ESM::AIPackage p)
std::cout << " Cell Name: " << p.mCellName << std::endl;
}
std::string ruleString(ESM::DialInfo::SelectStruct ss)
std::string ruleString(const ESM::DialInfo::SelectStruct& ss)
{
std::string rule = ss.mSelectRule;
@ -126,7 +126,7 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss)
return result;
}
void printEffectList(ESM::EffectList effects)
void printEffectList(const ESM::EffectList& effects)
{
int i = 0;
for (const ESM::ENAMstruct& effect : effects.mList)
@ -174,7 +174,7 @@ namespace EsmTool {
RecordBase *
RecordBase::create(ESM::NAME type)
{
RecordBase *record = 0;
RecordBase *record = nullptr;
switch (type.intval) {
case ESM::REC_ACTI:
@ -388,7 +388,7 @@ RecordBase::create(ESM::NAME type)
break;
}
default:
record = 0;
record = nullptr;
}
if (record) {
record->mType = type;
@ -728,10 +728,9 @@ void Record<ESM::Faction>::print()
<< " (" << mData.mData.mAttribute[0] << ")" << std::endl;
std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1])
<< " (" << mData.mData.mAttribute[1] << ")" << std::endl;
for (int i = 0; i < 7; i++)
if (mData.mData.mSkills[i] != -1)
std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i])
<< " (" << mData.mData.mSkills[i] << ")" << std::endl;
for (int skill : mData.mData.mSkills)
if (skill != -1)
std::cout << " Skill: " << skillLabel(skill) << " (" << skill << ")" << std::endl;
for (int i = 0; i != 10; i++)
if (!mData.mRanks[i].empty())
{

@ -74,7 +74,7 @@ namespace EsmTool
: mIsDeleted(false)
{}
std::string getId() const {
std::string getId() const override {
return mData.mId;
}
@ -82,15 +82,15 @@ namespace EsmTool
return mData;
}
void save(ESM::ESMWriter &esm) {
void save(ESM::ESMWriter &esm) override {
mData.save(esm, mIsDeleted);
}
void load(ESM::ESMReader &esm) {
void load(ESM::ESMReader &esm) override {
mData.load(esm, mIsDeleted);
}
void print();
void print() override;
};
template<> std::string Record<ESM::Cell>::getId() const;

@ -52,9 +52,7 @@ namespace
// a dynamically created record e.g. player-enchanted weapon
std::string index = indexedRefId.substr(indexedRefId.size()-8);
if(index.find_first_not_of("0123456789ABCDEF") == std::string::npos )
return true;
return false;
return index.find_first_not_of("0123456789ABCDEF") == std::string::npos;
}
void splitIndexedRefId(const std::string& indexedRefId, int& refIndex, std::string& refId)
@ -139,12 +137,12 @@ namespace ESSImport
image2->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
memcpy(image2->data(), &data[0], data.size());
for (std::set<std::pair<int, int> >::const_iterator it = mContext->mExploredCells.begin(); it != mContext->mExploredCells.end(); ++it)
for (const auto & exploredCell : mContext->mExploredCells)
{
if (it->first > mContext->mGlobalMapState.mBounds.mMaxX
|| it->first < mContext->mGlobalMapState.mBounds.mMinX
|| it->second > mContext->mGlobalMapState.mBounds.mMaxY
|| it->second < mContext->mGlobalMapState.mBounds.mMinY)
if (exploredCell.first > mContext->mGlobalMapState.mBounds.mMaxX
|| exploredCell.first < mContext->mGlobalMapState.mBounds.mMinX
|| exploredCell.second > mContext->mGlobalMapState.mBounds.mMaxY
|| exploredCell.second < mContext->mGlobalMapState.mBounds.mMinY)
{
// out of bounds, I think this could happen, since the original engine had a fixed-size map
continue;
@ -152,12 +150,12 @@ namespace ESSImport
int imageLeftSrc = mGlobalMapImage->s()/2;
int imageTopSrc = mGlobalMapImage->t()/2;
imageLeftSrc += it->first * cellSize;
imageTopSrc -= it->second * cellSize;
imageLeftSrc += exploredCell.first * cellSize;
imageTopSrc -= exploredCell.second * cellSize;
int imageLeftDst = width/2;
int imageTopDst = height/2;
imageLeftDst += it->first * cellSize;
imageTopDst -= it->second * cellSize;
imageLeftDst += exploredCell.first * cellSize;
imageTopDst -= exploredCell.second * cellSize;
for (int x=0; x<cellSize; ++x)
for (int y=0; y<cellSize; ++y)
{
@ -329,9 +327,8 @@ namespace ESSImport
csta.mWaterLevel = esmcell.mWater;
csta.save(esm);
for (std::vector<CellRef>::const_iterator refIt = cell.mRefs.begin(); refIt != cell.mRefs.end(); ++refIt)
for (const auto & cellref : cell.mRefs)
{
const CellRef& cellref = *refIt;
ESM::CellRef out (cellref);
// TODO: use mContext->mCreatures/mNpcs
@ -437,16 +434,16 @@ namespace ESSImport
void ConvertCell::write(ESM::ESMWriter &esm)
{
for (std::map<std::string, Cell>::const_iterator it = mIntCells.begin(); it != mIntCells.end(); ++it)
writeCell(it->second, esm);
for (const auto & cell : mIntCells)
writeCell(cell.second, esm);
for (std::map<std::pair<int, int>, Cell>::const_iterator it = mExtCells.begin(); it != mExtCells.end(); ++it)
writeCell(it->second, esm);
for (const auto & cell : mExtCells)
writeCell(cell.second, esm);
for (std::vector<ESM::CustomMarker>::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it)
for (const auto & marker : mMarkers)
{
esm.startRecord(ESM::REC_MARK);
it->save(esm);
marker.save(esm);
esm.endRecord(ESM::REC_MARK);
}
}

@ -79,9 +79,9 @@ template <typename T>
class DefaultConverter : public Converter
{
public:
virtual int getStage() { return 0; }
int getStage() override { return 0; }
virtual void read(ESM::ESMReader& esm)
void read(ESM::ESMReader& esm) override
{
T record;
bool isDeleted = false;
@ -90,7 +90,7 @@ public:
mRecords[record.mId] = record;
}
virtual void write(ESM::ESMWriter& esm)
void write(ESM::ESMWriter& esm) override
{
for (typename std::map<std::string, T>::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it)
{
@ -107,7 +107,7 @@ protected:
class ConvertNPC : public Converter
{
public:
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
ESM::NPC npc;
bool isDeleted = false;
@ -127,8 +127,8 @@ public:
ESM::SpellState::SpellParams empty;
// FIXME: player start spells and birthsign spells aren't listed here,
// need to fix openmw to account for this
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it)
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty;
for (const auto & spell : npc.mSpells.mList)
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[spell] = empty;
// Clear the list now that we've written it, this prevents issues cropping up with
// ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal.
@ -144,7 +144,7 @@ public:
class ConvertCREA : public Converter
{
public:
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
// See comment in ConvertNPC
ESM::Creature creature;
@ -162,7 +162,7 @@ public:
class ConvertGlobal : public DefaultConverter<ESM::Global>
{
public:
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
ESM::Global global;
bool isDeleted = false;
@ -183,7 +183,7 @@ public:
class ConvertClass : public DefaultConverter<ESM::Class>
{
public:
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
ESM::Class class_;
bool isDeleted = false;
@ -199,7 +199,7 @@ public:
class ConvertBook : public DefaultConverter<ESM::Book>
{
public:
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
ESM::Book book;
bool isDeleted = false;
@ -215,7 +215,7 @@ public:
class ConvertNPCC : public Converter
{
public:
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
std::string id = esm.getHNString("NAME");
NPCC npcc;
@ -235,7 +235,7 @@ public:
class ConvertREFR : public Converter
{
public:
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
REFR refr;
refr.load(esm);
@ -261,7 +261,7 @@ public:
}
}
}
virtual void write(ESM::ESMWriter& esm)
void write(ESM::ESMWriter& esm) override
{
esm.startRecord(ESM::REC_ASPL);
esm.writeHNString("ID__", mSelectedSpell);
@ -280,14 +280,14 @@ public:
mLevitationEnabled(true)
{}
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
PCDT pcdt;
pcdt.load(esm);
convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState);
}
virtual void write(ESM::ESMWriter &esm)
void write(ESM::ESMWriter &esm) override
{
esm.startRecord(ESM::REC_ENAB);
esm.writeHNT("TELE", mTeleportingEnabled);
@ -306,7 +306,7 @@ private:
class ConvertCNTC : public Converter
{
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
std::string id = esm.getHNString("NAME");
CNTC cntc;
@ -318,7 +318,7 @@ class ConvertCNTC : public Converter
class ConvertCREC : public Converter
{
public:
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
std::string id = esm.getHNString("NAME");
CREC crec;
@ -330,8 +330,8 @@ public:
class ConvertFMAP : public Converter
{
public:
virtual void read(ESM::ESMReader &esm);
virtual void write(ESM::ESMWriter &esm);
void read(ESM::ESMReader &esm) override;
void write(ESM::ESMWriter &esm) override;
private:
osg::ref_ptr<osg::Image> mGlobalMapImage;
@ -340,8 +340,8 @@ private:
class ConvertCell : public Converter
{
public:
virtual void read(ESM::ESMReader& esm);
virtual void write(ESM::ESMWriter& esm);
void read(ESM::ESMReader& esm) override;
void write(ESM::ESMWriter& esm) override;
private:
struct Cell
@ -362,7 +362,7 @@ private:
class ConvertKLST : public Converter
{
public:
virtual void read(ESM::ESMReader& esm)
void read(ESM::ESMReader& esm) override
{
KLST klst;
klst.load(esm);
@ -371,7 +371,7 @@ public:
mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills;
}
virtual void write(ESM::ESMWriter &esm)
void write(ESM::ESMWriter &esm) override
{
esm.startRecord(ESM::REC_DCOU);
for (std::map<std::string, int>::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
@ -389,7 +389,7 @@ private:
class ConvertFACT : public Converter
{
public:
virtual void read(ESM::ESMReader& esm)
void read(ESM::ESMReader& esm) override
{
ESM::Faction faction;
bool isDeleted = false;
@ -409,7 +409,7 @@ public:
class ConvertSTLN : public Converter
{
public:
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
std::string itemid = esm.getHNString("NAME");
Misc::StringUtils::lowerCaseInPlace(itemid);
@ -428,15 +428,15 @@ public:
}
}
}
virtual void write(ESM::ESMWriter &esm)
void write(ESM::ESMWriter &esm) override
{
ESM::StolenItems items;
for (std::map<std::string, std::set<Owner> >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
{
std::map<std::pair<std::string, bool>, int> owners;
for (std::set<Owner>::const_iterator ownerIt = it->second.begin(); ownerIt != it->second.end(); ++ownerIt)
for (const auto & ownerIt : it->second)
{
owners.insert(std::make_pair(std::make_pair(ownerIt->first, ownerIt->second)
owners.insert(std::make_pair(std::make_pair(ownerIt.first, ownerIt.second)
// Since OpenMW doesn't suffer from the owner contamination bug,
// it needs a count argument. But for legacy savegames, we don't know
// this count, so must assume all items of that ID are stolen,
@ -467,7 +467,7 @@ private:
class ConvertINFO : public Converter
{
public:
virtual void read(ESM::ESMReader& esm)
void read(ESM::ESMReader& esm) override
{
INFO info;
info.load(esm);
@ -477,7 +477,7 @@ public:
class ConvertDIAL : public Converter
{
public:
virtual void read(ESM::ESMReader& esm)
void read(ESM::ESMReader& esm) override
{
std::string id = esm.getHNString("NAME");
DIAL dial;
@ -485,7 +485,7 @@ public:
if (dial.mIndex > 0)
mDials[id] = dial;
}
virtual void write(ESM::ESMWriter &esm)
void write(ESM::ESMWriter &esm) override
{
for (std::map<std::string, DIAL>::const_iterator it = mDials.begin(); it != mDials.end(); ++it)
{
@ -505,7 +505,7 @@ private:
class ConvertQUES : public Converter
{
public:
virtual void read(ESM::ESMReader& esm)
void read(ESM::ESMReader& esm) override
{
std::string id = esm.getHNString("NAME");
QUES quest;
@ -516,7 +516,7 @@ public:
class ConvertJOUR : public Converter
{
public:
virtual void read(ESM::ESMReader& esm)
void read(ESM::ESMReader& esm) override
{
JOUR journal;
journal.load(esm);
@ -531,7 +531,7 @@ public:
{
}
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
mGame.load(esm);
mHasGame = true;
@ -551,7 +551,7 @@ public:
}
}
virtual void write(ESM::ESMWriter &esm)
void write(ESM::ESMWriter &esm) override
{
if (!mHasGame)
return;
@ -578,7 +578,7 @@ private:
class ConvertSCPT : public Converter
{
public:
virtual void read(ESM::ESMReader &esm)
void read(ESM::ESMReader &esm) override
{
SCPT script;
script.load(esm);
@ -586,12 +586,12 @@ public:
convertSCPT(script, out);
mScripts.push_back(out);
}
virtual void write(ESM::ESMWriter &esm)
void write(ESM::ESMWriter &esm) override
{
for (std::vector<ESM::GlobalScript>::const_iterator it = mScripts.begin(); it != mScripts.end(); ++it)
for (const auto & script : mScripts)
{
esm.startRecord(ESM::REC_GSCR);
it->save(esm);
script.save(esm);
esm.endRecord(ESM::REC_GSCR);
}
}
@ -603,9 +603,9 @@ private:
class ConvertPROJ : public Converter
{
public:
virtual int getStage() override { return 2; }
virtual void read(ESM::ESMReader& esm) override;
virtual void write(ESM::ESMWriter& esm) override;
int getStage() override { return 2; }
void read(ESM::ESMReader& esm) override;
void write(ESM::ESMWriter& esm) override;
private:
void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam);
PROJ mProj;
@ -614,8 +614,8 @@ private:
class ConvertSPLM : public Converter
{
public:
virtual void read(ESM::ESMReader& esm) override;
virtual void write(ESM::ESMWriter& esm) override;
void read(ESM::ESMReader& esm) override;
void write(ESM::ESMWriter& esm) override;
private:
SPLM mSPLM;
};

@ -9,21 +9,20 @@ namespace ESSImport
void convertInventory(const Inventory &inventory, ESM::InventoryState &state)
{
int index = 0;
for (std::vector<Inventory::InventoryItem>::const_iterator it = inventory.mItems.begin();
it != inventory.mItems.end(); ++it)
for (const auto & item : inventory.mItems)
{
ESM::ObjectState objstate;
objstate.blank();
objstate.mRef = *it;
objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId);
objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile
objstate.mRef = item;
objstate.mRef.mRefID = Misc::StringUtils::lowerCase(item.mId);
objstate.mCount = std::abs(item.mCount); // restocking items have negative count in the savefile
// openmw handles them differently, so no need to set any flags
state.mItems.push_back(objstate);
if (it->mRelativeEquipmentSlot != -1)
if (item.mRelativeEquipmentSlot != -1)
// Note we should really write the absolute slot here, which we do not know about
// Not a big deal, OpenMW will auto-correct to a valid slot, the only problem is when
// an item could be equipped in two different slots (e.g. equipped two rings)
state.mEquipmentSlots[index] = it->mRelativeEquipmentSlot;
state.mEquipmentSlots[index] = item.mRelativeEquipmentSlot;
++index;
}
}

@ -10,13 +10,13 @@ namespace ESSImport
{
out.mBirthsign = pcdt.mBirthsign;
out.mObject.mNpcStats.mBounty = pcdt.mBounty;
for (std::vector<PCDT::FNAM>::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it)
for (const auto & essFaction : pcdt.mFactions)
{
ESM::NpcStats::Faction faction;
faction.mExpelled = (it->mFlags & 0x2) != 0;
faction.mRank = it->mRank;
faction.mReputation = it->mReputation;
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
faction.mExpelled = (essFaction.mFlags & 0x2) != 0;
faction.mRank = essFaction.mRank;
faction.mReputation = essFaction.mReputation;
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(essFaction.mFactionName.toString())] = faction;
}
for (int i=0; i<3; ++i)
out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i];
@ -35,10 +35,9 @@ namespace ESSImport
teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled);
levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled);
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
it != pcdt.mKnownDialogueTopics.end(); ++it)
for (const auto & knownDialogueTopic : pcdt.mKnownDialogueTopics)
{
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it));
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(knownDialogueTopic));
}
controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled;

@ -1,18 +1,16 @@
#include "convertscri.hpp"
#include <iostream>
namespace
{
template <typename T, ESM::VarType VariantType>
void storeVariables(const std::vector<T>& variables, ESM::Locals& locals, const std::string& scriptname)
{
for (typename std::vector<T>::const_iterator it = variables.begin(); it != variables.end(); ++it)
for (const auto& variable : variables)
{
ESM::Variant val(*it);
ESM::Variant val(variable);
val.setType(VariantType);
locals.mVariables.push_back(std::make_pair(std::string(), val));
locals.mVariables.emplace_back(std::string(), val);
}
}

@ -86,7 +86,9 @@ namespace ESSImport
bool mHasANIS;
ANIS mANIS; // scripted animation state
void load(ESM::ESMReader& esm);
virtual void load(ESM::ESMReader& esm);
virtual ~ActorData() = default;
};
}

@ -25,7 +25,9 @@ namespace ESSImport
bool mDeleted;
void load(ESM::ESMReader& esm);
void load(ESM::ESMReader& esm) override;
virtual ~CellRef() = default;
};
}

@ -16,15 +16,12 @@
#include <components/esm/player.hpp>
#include <components/esm/loadalch.hpp>
#include <components/esm/loadclas.hpp>
#include <components/esm/loadspel.hpp>
#include <components/esm/loadarmo.hpp>
#include <components/esm/loadweap.hpp>
#include <components/esm/loadclot.hpp>
#include <components/esm/loadench.hpp>
#include <components/esm/loadweap.hpp>
#include <components/esm/loadlevlist.hpp>
#include <components/esm/loadglob.hpp>
#include <components/misc/constants.hpp>
@ -49,7 +46,7 @@ namespace
image->allocateImage(128, 128, 1, GL_RGB, GL_UNSIGNED_BYTE);
// need to convert pixel format from BGRA to RGB as the jpg readerwriter doesn't support it otherwise
std::vector<unsigned char>::const_iterator it = fileHeader.mSCRS.begin();
auto it = fileHeader.mSCRS.begin();
for (int y=0; y<128; ++y)
{
for (int x=0; x<128; ++x)
@ -317,10 +314,9 @@ namespace ESSImport
std::set<unsigned int> unknownRecords;
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
it != converters.end(); ++it)
for (const auto & converter : converters)
{
it->second->setContext(context);
converter.second->setContext(context);
}
while (esm.hasMoreRecs())
@ -328,7 +324,7 @@ namespace ESSImport
ESM::NAME n = esm.getRecName();
esm.getRecHeader();
std::map<unsigned int, std::shared_ptr<Converter> >::iterator it = converters.find(n.intval);
auto it = converters.find(n.intval);
if (it != converters.end())
{
it->second->read(esm);
@ -358,17 +354,15 @@ namespace ESSImport
writer.setDescription("");
writer.setRecordCount (0);
for (std::vector<ESM::Header::MasterData>::const_iterator it = header.mMaster.begin();
it != header.mMaster.end(); ++it)
writer.addMaster (it->name, 0); // not using the size information anyway -> use value of 0
for (const auto & master : header.mMaster)
writer.addMaster(master.name, 0); // not using the size information anyway -> use value of 0
writer.save (stream);
ESM::SavedGame profile;
for (std::vector<ESM::Header::MasterData>::const_iterator it = header.mMaster.begin();
it != header.mMaster.end(); ++it)
for (const auto & master : header.mMaster)
{
profile.mContentFiles.push_back(it->name);
profile.mContentFiles.push_back(master.name);
}
profile.mDescription = esm.getDesc();
profile.mInGameTime.mDay = context.mDay;

@ -63,7 +63,6 @@ namespace ESSImport
, mHour(0.f)
, mNextActorId(0)
{
mPlayer.mAutoMove = 0;
ESM::CellId playerCellId;
playerCellId.mPaged = true;
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;

@ -4,8 +4,6 @@
#include <components/esm/esmreader.hpp>
#include <components/esm/loadcont.hpp>
namespace ESSImport
{

@ -33,13 +33,10 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message
CSMWorld::UniversalId id( CSMWorld::UniversalId::Type_BodyPart, bodyPart.mId );
// Check BYDT
if (bodyPart.mData.mPart > 14 )
if (bodyPart.mData.mPart >= ESM::BodyPart::MP_Count )
messages.add(id, "Invalid part", "", CSMDoc::Message::Severity_Error);
if (bodyPart.mData.mFlags > 3 )
messages.add(id, "Invalid flags", "", CSMDoc::Message::Severity_Error);
if (bodyPart.mData.mType > 2 )
if (bodyPart.mData.mType > ESM::BodyPart::MT_Armor )
messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error);
// Check MODL
@ -48,9 +45,12 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message
else if ( mMeshes.searchId( bodyPart.mModel ) == -1 )
messages.add(id, "Model '" + bodyPart.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
// Check FNAM
if ( bodyPart.mRace.empty() )
messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error);
else if ( mRaces.searchId( bodyPart.mRace ) == -1 )
messages.add(id, "Race '" + bodyPart.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error);
// Check FNAM for skin body parts (for non-skin body parts it's meaningless)
if ( bodyPart.mData.mType == ESM::BodyPart::MT_Skin )
{
if ( bodyPart.mRace.empty() )
messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error);
else if ( mRaces.searchId( bodyPart.mRace ) == -1 )
messages.add(id, "Race '" + bodyPart.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
}

@ -21,11 +21,12 @@ add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths recastmesh
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging
)
add_openmw_dir (mwinput
inputmanagerimp
actions actionmanager bindingsmanager controllermanager controlswitch
inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager
)
add_openmw_dir (mwgui
@ -40,7 +41,7 @@ add_openmw_dir (mwgui
itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours
)
add_openmw_dir (mwdialogue
@ -66,7 +67,7 @@ add_openmw_dir (mwworld
actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager
cellpreloader
cellpreloader datetimemanager
)
add_openmw_dir (mwphysics
@ -83,9 +84,10 @@ add_openmw_dir (mwclass
add_openmw_dir (mwmechanics
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellsuccess spellcasting
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype
character actors objects aistate trading weaponpriority spellpriority weapontype spellutil tickableeffects
spellabsorption linkedeffects
)
add_openmw_dir (mwstate

@ -1,6 +1,7 @@
#include "engine.hpp"
#include <iomanip>
#include <fstream>
#include <boost/filesystem/fstream.hpp>
@ -96,7 +97,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();
return false;
@ -180,7 +181,7 @@ bool OMW::Engine::frame(float frametime)
osg::Timer_t afterWorldTick = osg::Timer::instance()->tick();
// update GUI
mEnvironment.getWindowManager()->onFrame(frametime);
mEnvironment.getWindowManager()->update(frametime);
unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
osg::Stats* stats = mViewer->getViewerStats();
@ -202,12 +203,14 @@ bool OMW::Engine::frame(float frametime)
if (stats->collectStats("resource"))
{
stats->setAttribute(frameNumber, "FrameNumber", frameNumber);
mResourceSystem->reportStats(frameNumber, stats);
stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems());
stats->setAttribute(frameNumber, "WorkThread", mWorkQueue->getNumActiveThreads());
mEnvironment.getWorld()->getNavigator()->reportStats(frameNumber, *stats);
mEnvironment.reportStats(frameNumber, *stats);
}
}
@ -532,20 +535,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));
@ -561,7 +564,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();
@ -658,7 +660,6 @@ private:
};
// Initialise and enter main loop.
void OMW::Engine::go()
{
assert (!mContentFiles.empty());
@ -687,7 +688,8 @@ void OMW::Engine::go()
mViewer->setUseConfigureAffinity(false);
#endif
mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
mScreenCaptureOperation = new WriteScreenshotToFileOperation(
mCfgMgr.getScreenshotPath().string(),
Settings::Manager::getString("screenshot format", "General"));
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);
@ -739,6 +741,14 @@ void OMW::Engine::go()
mEnvironment.getWindowManager()->executeInConsole(mStartupScript);
}
std::ofstream stats;
if (const auto path = std::getenv("OPENMW_OSG_STATS_FILE"))
{
stats.open(path, std::ios_base::out);
if (!stats)
Log(Debug::Warning) << "Failed to open file for stats: " << path;
}
// Start the main rendering loop
osg::Timer frameTimer;
double simulationTime = 0.0;
@ -769,6 +779,12 @@ void OMW::Engine::go()
simulationTime += dt;
}
if (stats)
{
const auto frameNumber = mViewer->getFrameStamp()->getFrameNumber();
mViewer->getViewerStats()->report(stats, frameNumber);
}
mEnvironment.limitFrameRate(frameTimer.time_s());
}

@ -198,3 +198,9 @@ const MWBase::Environment& MWBase::Environment::get()
assert (sThis);
return *sThis;
}
void MWBase::Environment::reportStats(unsigned int frameNumber, osg::Stats& stats) const
{
mMechanicsManager->reportStats(frameNumber, stats);
mWorld->reportStats(frameNumber, stats);
}

@ -1,6 +1,11 @@
#ifndef GAME_BASE_ENVIRONMENT_H
#define GAME_BASE_ENVIRONMENT_H
namespace osg
{
class Stats;
}
namespace MWBase
{
class World;
@ -97,6 +102,8 @@ namespace MWBase
static const Environment& get();
///< Return instance of this class.
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
};
}

@ -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,17 @@ 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;
virtual bool controlsDisabled() = 0;
};
}

@ -7,10 +7,14 @@
#include <set>
#include <stdint.h>
#include "../mwmechanics/actorutil.hpp"
// For MWMechanics::GreetingState
#include "../mwworld/ptr.hpp"
namespace osg
{
class Stats;
class Vec3f;
}
@ -269,6 +273,15 @@ namespace MWBase
virtual bool isAttackPreparing(const MWWorld::Ptr& ptr) = 0;
virtual bool isRunning(const MWWorld::Ptr& ptr) = 0;
virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0;
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
virtual int getGreetingTimer(const MWWorld::Ptr& ptr) const = 0;
virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const = 0;
virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0;
virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0;
virtual void restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) = 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
@ -249,13 +251,7 @@ namespace MWBase
/// returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
virtual int readPressedButton() = 0;
virtual void onFrame (float frameDuration) = 0;
/// \todo get rid of this stuff. Move it to the respective UI element classes, if needed.
virtual std::map<int, MWMechanics::SkillValue > getPlayerSkillValues() = 0;
virtual std::map<int, MWMechanics::AttributeValue > getPlayerAttributeValues() = 0;
virtual SkillList getPlayerMinorSkills() = 0;
virtual SkillList getPlayerMajorSkills() = 0;
virtual void update (float duration) = 0;
/**
* Fetches a GMST string from the store, if there is no setting with the given
@ -268,8 +264,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;
virtual void enableRest() = 0;
@ -360,6 +354,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;
};
}

@ -21,6 +21,7 @@ namespace osg
class Matrixf;
class Quat;
class Image;
class Stats;
}
namespace Loading
@ -35,6 +36,7 @@ namespace ESM
struct Position;
struct Cell;
struct Class;
struct Creature;
struct Potion;
struct Spell;
struct NPC;
@ -46,6 +48,7 @@ namespace ESM
struct EffectList;
struct CreatureLevList;
struct ItemLevList;
struct TimeStamp;
}
namespace MWRender
@ -189,6 +192,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;
virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) = 0;
///< Return a pointer to a liveCellRef which contains \a ptr.
/// \note Search is limited to the active cells.
@ -200,24 +205,14 @@ namespace MWBase
virtual void advanceTime (double hours, bool incremental = false) = 0;
///< Advance in-game time.
virtual void setHour (double hour) = 0;
///< Set in-game time hour.
virtual void setMonth (int month) = 0;
///< Set in-game time month.
virtual void setDay (int day) = 0;
///< Set in-game time day.
virtual int getDay() const = 0;
virtual int getMonth() const = 0;
virtual int getYear() const = 0;
virtual std::string getMonthName (int month = -1) const = 0;
///< Return name of month (-1: current month)
virtual MWWorld::TimeStamp getTimeStamp() const = 0;
///< Return current in-game time stamp.
///< Return current in-game time and number of day since new game start.
virtual ESM::EpochTimeStamp getEpochTimeStamp() const = 0;
///< Return current in-game date and time.
virtual bool toggleSky() = 0;
///< \return Resulting mode
@ -375,6 +370,14 @@ namespace MWBase
///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record
virtual const ESM::Creature *createOverrideRecord (const ESM::Creature& record) = 0;
///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record
virtual const ESM::NPC *createOverrideRecord (const ESM::NPC& record) = 0;
///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record
virtual void update (float duration, bool paused) = 0;
virtual void updatePhysics (float duration, bool paused) = 0;
@ -627,6 +630,8 @@ namespace MWBase
virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;
virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0;
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
};
}

@ -276,7 +276,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
int armorSkillType = getEquipmentSkill(ptr);
int armorSkill = actor.getClass().getSkill(actor, armorSkillType);
float armorSkill = actor.getClass().getSkill(actor, armorSkillType);
const MWBase::World *world = MWBase::Environment::get().getWorld();
int iBaseArmorSkill = world->getStore().get<ESM::GameSetting>().find("iBaseArmorSkill")->mValue.getInteger();

@ -507,23 +507,16 @@ namespace MWClass
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
{
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
return 0.f;
const GMST& gmst = getGmst();
float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat());
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);
// The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp)
float runSpeed = walkSpeed;
float moveSpeed;
if(getEncumbrance(ptr) > getCapacity(ptr))
@ -540,19 +533,9 @@ namespace MWClass
moveSpeed = flySpeed;
}
else if(world->isSwimming(ptr))
{
float swimSpeed = walkSpeed;
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
gmst.fSwimRunAthleticsMult->mValue.getFloat();
moveSpeed = swimSpeed;
}
else if(running)
moveSpeed = runSpeed;
moveSpeed = getSwimSpeed(ptr);
else
moveSpeed = walkSpeed;
moveSpeed = getWalkSpeed(ptr);
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f;
@ -605,7 +588,7 @@ namespace MWClass
float Creature::getCapacity (const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
return static_cast<float>(stats.getAttribute(ESM::Attribute::Strength).getModified() * 5);
return stats.getAttribute(ESM::Attribute::Strength).getModified() * 5;
}
int Creature::getServices(const MWWorld::ConstPtr &actor) const
@ -745,7 +728,7 @@ namespace MWClass
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
}
int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const
float Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
@ -809,6 +792,12 @@ namespace MWClass
return;
}
if (ptr.getRefData().getCount() <= 0)
{
state.mHasCustomState = false;
return;
}
const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
ESM::CreatureState& creatureState = state.asCreatureState();
customData.mContainerStore->writeState (creatureState.mInventory);
@ -878,4 +867,36 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
scale *= ref->mBase->mScale;
}
void Creature::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const
{
MWMechanics::setBaseAISetting<ESM::Creature>(id, setting, value);
}
float Creature::getWalkSpeed(const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const GMST& gmst = getGmst();
return gmst.fMinWalkSpeedCreature->mValue.getFloat()
+ 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat());
}
float Creature::getRunSpeed(const MWWorld::Ptr& ptr) const
{
return getWalkSpeed(ptr);
}
float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const GMST& gmst = getGmst();
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
return getWalkSpeed(ptr)
* (1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude())
* (gmst.fSwimRunBase->mValue.getFloat()
+ 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat());
}
}

@ -108,7 +108,7 @@ namespace MWClass
virtual bool canSwim (const MWWorld::ConstPtr &ptr) const;
virtual bool canWalk (const MWWorld::ConstPtr &ptr) const;
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const;
virtual float getSkill(const MWWorld::Ptr &ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;
@ -129,6 +129,14 @@ namespace MWClass
virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const;
/// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
float getWalkSpeed(const MWWorld::Ptr& ptr) const final;
float getRunSpeed(const MWWorld::Ptr& ptr) const final;
float getSwimSpeed(const MWWorld::Ptr& ptr) const final;
};
}

@ -125,7 +125,7 @@ namespace MWClass
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
int alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
float alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();

@ -127,8 +127,8 @@ namespace
}
// initial health
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
float strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
float endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
int multiplier = 3;
@ -947,16 +947,6 @@ namespace MWClass
bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
(gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat());
walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed);
if(sneaking)
walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat();
float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) *
gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat());
float moveSpeed;
if(getEncumbrance(ptr) > getCapacity(ptr))
moveSpeed = 0.0f;
@ -971,19 +961,11 @@ namespace MWClass
moveSpeed = flySpeed;
}
else if (swimming)
{
float swimSpeed = walkSpeed;
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics)*
gmst.fSwimRunAthleticsMult->mValue.getFloat();
moveSpeed = swimSpeed;
}
moveSpeed = getSwimSpeed(ptr);
else if (running && !sneaking)
moveSpeed = runSpeed;
moveSpeed = getRunSpeed(ptr);
else
moveSpeed = walkSpeed;
moveSpeed = getWalkSpeed(ptr);
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f;
@ -1011,7 +993,7 @@ namespace MWClass
gmst.fJumpEncumbranceMultiplier->mValue.getFloat() *
(1.0f - Npc::getNormalizedEncumbrance(ptr));
float a = static_cast<float>(getSkill(ptr, ESM::Skill::Acrobatics));
float a = getSkill(ptr, ESM::Skill::Acrobatics);
float b = 0.0f;
if(a > 50.0f)
{
@ -1136,7 +1118,7 @@ namespace MWClass
float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
int unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored);
float unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored);
float ratings[MWWorld::InventoryStore::Slots];
for(int i = 0;i < MWWorld::InventoryStore::Slots;i++)
@ -1283,7 +1265,7 @@ namespace MWClass
return MWWorld::Ptr(cell.insert(ref), &cell);
}
int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const
float Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const
{
return getNpcStats(ptr).getSkill(skill).getModified();
}
@ -1327,6 +1309,12 @@ namespace MWClass
return;
}
if (ptr.getRefData().getCount() <= 0)
{
state.mHasCustomState = false;
return;
}
const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();
ESM::NpcState& npcState = state.asNpcState();
customData.mInventoryStore.writeState (npcState.mInventory);
@ -1437,4 +1425,61 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->getFactionRank();
}
void Npc::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const
{
MWMechanics::setBaseAISetting<ESM::NPC>(id, setting, value);
}
float Npc::getWalkSpeed(const MWWorld::Ptr& ptr) const
{
const GMST& gmst = getGmst();
const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
const bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);
float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat()
+ 0.01f * npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat());
walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed);
if(sneaking)
walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat();
return walkSpeed;
}
float Npc::getRunSpeed(const MWWorld::Ptr& ptr) const
{
const GMST& gmst = getGmst();
return getWalkSpeed(ptr)
* (0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat()
+ gmst.fBaseRunMultiplier->mValue.getFloat());
}
float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const
{
const GMST& gmst = getGmst();
const MWBase::World* world = MWBase::Environment::get().getWorld();
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects& mageffects = npcdata->mNpcStats.getMagicEffects();
const bool swimming = world->isSwimming(ptr);
const bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
const bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run)
&& (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
float swimSpeed;
if (running)
swimSpeed = getRunSpeed(ptr);
else
swimSpeed = getWalkSpeed(ptr);
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->mValue.getFloat()
+ 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat();
return swimSpeed;
}
}

@ -129,7 +129,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;
@ -164,6 +164,14 @@ namespace MWClass
virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const;
virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const;
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
float getWalkSpeed(const MWWorld::Ptr& ptr) const final;
float getRunSpeed(const MWWorld::Ptr& ptr) const final;
float getSwimSpeed(const MWWorld::Ptr& ptr) const final;
};
}

@ -105,23 +105,32 @@ namespace MWGui
mGenerateClassSpecializations[0] = 0;
mGenerateClassSpecializations[1] = 0;
mGenerateClassSpecializations[2] = 0;
// Setup player stats
for (int i = 0; i < ESM::Attribute::Length; ++i)
mPlayerAttributes.emplace(ESM::Attribute::sAttributeIds[i], MWMechanics::AttributeValue());
for (int i = 0; i < ESM::Skill::Length; ++i)
mPlayerSkillValues.emplace(ESM::Skill::sSkillIds[i], MWMechanics::SkillValue());
}
void CharacterCreation::setValue (const std::string& id, const MWMechanics::AttributeValue& value)
{
if (mReviewDialog)
static const char *ids[] =
{
static const char *ids[] =
{
"AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5",
"AttribVal6", "AttribVal7", "AttribVal8",
0
};
"AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4",
"AttribVal5", "AttribVal6", "AttribVal7", "AttribVal8", 0
};
for (int i=0; ids[i]; ++i)
for (int i=0; ids[i]; ++i)
{
if (ids[i]==id)
{
if (ids[i]==id)
mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value);
mPlayerAttributes[static_cast<ESM::Attribute::AttributeID>(i)] = value;
if (mReviewDialog)
mReviewDialog->setAttribute(static_cast<ESM::Attribute::AttributeID>(i), value);
break;
}
}
}
@ -147,6 +156,7 @@ namespace MWGui
void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value)
{
mPlayerSkillValues[parSkill] = value;
if (mReviewDialog)
mReviewDialog->setSkillValue(parSkill, value);
}
@ -155,6 +165,9 @@ namespace MWGui
{
if (mReviewDialog)
mReviewDialog->configureSkills(major, minor);
mPlayerMajorSkills = major;
mPlayerMinorSkills = minor;
}
void CharacterCreation::onFrame(float duration)
@ -269,31 +282,21 @@ namespace MWGui
mReviewDialog->setClass(*playerClass);
mReviewDialog->setBirthSign(player.getBirthSign());
{
MWWorld::Ptr playerPtr = MWMechanics::getPlayer();
const MWMechanics::CreatureStats& stats = playerPtr.getClass().getCreatureStats(playerPtr);
mReviewDialog->setHealth ( stats.getHealth() );
mReviewDialog->setMagicka( stats.getMagicka() );
mReviewDialog->setFatigue( stats.getFatigue() );
}
MWWorld::Ptr playerPtr = MWMechanics::getPlayer();
const MWMechanics::CreatureStats& stats = playerPtr.getClass().getCreatureStats(playerPtr);
mReviewDialog->setHealth(stats.getHealth());
mReviewDialog->setMagicka(stats.getMagicka());
mReviewDialog->setFatigue(stats.getFatigue());
for (auto& attributePair : mPlayerAttributes)
{
std::map<int, MWMechanics::AttributeValue > attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues();
for (auto& attributePair : attributes)
{
mReviewDialog->setAttribute(static_cast<ESM::Attribute::AttributeID> (attributePair.first), attributePair.second);
}
mReviewDialog->setAttribute(static_cast<ESM::Attribute::AttributeID> (attributePair.first), attributePair.second);
}
for (auto& skillPair : mPlayerSkillValues)
{
std::map<int, MWMechanics::SkillValue > skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues();
for (auto& skillPair : skills)
{
mReviewDialog->setSkillValue(static_cast<ESM::Skill::SkillEnum> (skillPair.first), skillPair.second);
}
mReviewDialog->configureSkills(MWBase::Environment::get().getWindowManager()->getPlayerMajorSkills(), MWBase::Environment::get().getWindowManager()->getPlayerMinorSkills());
mReviewDialog->setSkillValue(static_cast<ESM::Skill::SkillEnum> (skillPair.first), skillPair.second);
}
mReviewDialog->configureSkills(mPlayerMajorSkills, mPlayerMinorSkills);
mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone);
mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack);

@ -4,6 +4,7 @@
#include <components/esm/loadskil.hpp>
#include <components/esm/loadclas.hpp>
#include <map>
#include <vector>
#include "../mwmechanics/stat.hpp"
@ -56,6 +57,10 @@ namespace MWGui
osg::Group* mParent;
Resource::ResourceSystem* mResourceSystem;
SkillList mPlayerMajorSkills, mPlayerMinorSkills;
std::map<int, MWMechanics::AttributeValue> mPlayerAttributes;
std::map<int, MWMechanics::SkillValue> mPlayerSkillValues;
//Dialogs
TextInputDialog* mNameDialog;
RaceDialog* mRaceDialog;

@ -477,6 +477,8 @@ namespace MWGui
void DialogueWindow::onClose()
{
if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Dialogue))
return;
// Reset history
for (DialogueText* text : mHistoryContents)
delete text;

@ -81,6 +81,12 @@ namespace MWGui
MWBase::Environment::get().getMechanicsManager()->rest(mDays * 24, true);
MWBase::Environment::get().getWorld()->advanceTime(mDays * 24);
// We should not worsen corprus when in prison
for (auto& spell : player.getClass().getCreatureStats(player).getCorprusSpells())
{
spell.second.mNextWorsening += mDays * 24;
}
std::set<int> skills;
for (int day=0; day<mDays; ++day)
{
@ -89,9 +95,9 @@ namespace MWGui
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill);
if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak)
value.setBase(std::min(100, value.getBase()+1));
value.setBase(std::min(100.f, value.getBase()+1));
else
value.setBase(std::max(0, value.getBase()-1));
value.setBase(std::max(0.f, value.getBase()-1));
}
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();

@ -157,7 +157,7 @@ namespace MWGui
mAttributeValues[i]->setEnabled(true);
availableAttributes++;
int mult = pcStats.getLevelupAttributeMultiplier (i);
float mult = pcStats.getLevelupAttributeMultiplier (i);
mult = std::min(mult, 100-pcStats.getAttribute(i).getBase());
text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult));
}

@ -16,6 +16,7 @@
#include <components/myguiplatform/myguitexture.hpp>
#include <components/settings/settings.hpp>
#include <components/vfs/manager.hpp>
#include <components/resource/resourcesystem.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/statemanager.hpp"
@ -29,9 +30,9 @@
namespace MWGui
{
LoadingScreen::LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer)
LoadingScreen::LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer)
: WindowBase("openmw_loading_screen.layout")
, mVFS(vfs)
, mResourceSystem(resourceSystem)
, mViewer(viewer)
, mTargetFrameRate(120.0)
, mLastWallpaperChangeTime(0.0)
@ -64,9 +65,9 @@ namespace MWGui
void LoadingScreen::findSplashScreens()
{
const std::map<std::string, VFS::File*>& index = mVFS->getIndex();
const std::map<std::string, VFS::File*>& index = mResourceSystem->getVFS()->getIndex();
std::string pattern = "Splash/";
mVFS->normalizeFilename(pattern);
mResourceSystem->getVFS()->normalizeFilename(pattern);
/* priority given to the left */
const std::array<std::string, 7> supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}};
@ -171,6 +172,11 @@ namespace MWGui
// We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound()
mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback);
if (const osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) {
mOldIcoMin = ico->getMinimumTimeAvailableForGLCompileAndDeletePerFrame();
mOldIcoMax = ico->getMaximumNumOfObjectsToCompilePerFrame();
}
mVisible = visible;
mLoadingBox->setVisible(mVisible);
setVisible(true);
@ -215,6 +221,12 @@ namespace MWGui
//std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl;
setVisible(false);
if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation())
{
ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(mOldIcoMin);
ico->setMaximumNumOfObjectsToCompilePerFrame(mOldIcoMax);
}
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading);
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper);
}
@ -336,7 +348,13 @@ namespace MWGui
MWBase::Environment::get().getInputManager()->update(0, true, true);
//osg::Timer timer;
mResourceSystem->reportStats(mViewer->getFrameStamp()->getFrameNumber(), mViewer->getViewerStats());
if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation())
{
ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(1.f/getTargetFrameRate());
ico->setMaximumNumOfObjectsToCompilePerFrame(1000);
}
// at the time this function is called we are in the middle of a frame,
// so out of order calls are necessary to get a correct frameNumber for the next frame.
// refer to the advance() and frame() order in Engine::go()
@ -344,10 +362,6 @@ namespace MWGui
mViewer->updateTraversal();
mViewer->renderingTraversals();
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
//std::cout << "frame took " << timer.time_m() << std::endl;
//if (mViewer->getIncrementalCompileOperation())
//std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl;
mLastRenderTime = mTimer.time_m();
}

@ -20,9 +20,9 @@ namespace osg
class Texture2D;
}
namespace VFS
namespace Resource
{
class Manager;
class ResourceSystem;
}
namespace MWGui
@ -32,7 +32,7 @@ namespace MWGui
class LoadingScreen : public WindowBase, public Loading::Listener
{
public:
LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer);
LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer);
virtual ~LoadingScreen();
/// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details
@ -53,7 +53,7 @@ namespace MWGui
void setupCopyFramebufferToTextureCallback();
const VFS::Manager* mVFS;
Resource::ResourceSystem* mResourceSystem;
osg::ref_ptr<osgViewer::Viewer> mViewer;
double mTargetFrameRate;
@ -70,6 +70,8 @@ namespace MWGui
size_t mProgress;
bool mShowWallpaper;
float mOldIcoMin = 0.f;
unsigned int mOldIcoMax = 0;
MyGUI::Widget* mLoadingBox;

@ -209,6 +209,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);

@ -22,7 +22,7 @@ namespace MWGui
{
MWWorld::Ptr player = MWMechanics::getPlayer();
mSourceModel = sourceModel;
int chance = player.getClass().getSkill(player, ESM::Skill::Sneak);
float chance = player.getClass().getSkill(player, ESM::Skill::Sneak);
mSourceModel->update();

@ -19,7 +19,7 @@
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/actorutil.hpp"

@ -15,10 +15,10 @@
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/spells.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "tooltips.hpp"
#include "class.hpp"

@ -7,7 +7,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp"

@ -18,7 +18,7 @@
#include "../mwworld/esmstore.hpp"
#include "../mwworld/player.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "../mwmechanics/spells.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/actorutil.hpp"

@ -159,7 +159,7 @@ namespace MWGui
for (int i=0; ids[i]; ++i)
if (ids[i]==id)
{
setText (id, std::to_string(value.getModified()));
setText (id, std::to_string(static_cast<int>(value.getModified())));
MyGUI::TextBox* box;
getWidget(box, id);

@ -0,0 +1,36 @@
#include "textcolours.hpp"
#include <MyGUI_LanguageManager.h>
#include <string>
namespace MWGui
{
MyGUI::Colour getTextColour(const std::string& type)
{
return MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=" + type + "}"));
}
void TextColours::loadColours()
{
header = getTextColour("header");
normal = getTextColour("normal");
notify = getTextColour("notify");
link = getTextColour("link");
linkOver = getTextColour("link_over");
linkPressed = getTextColour("link_pressed");
answer = getTextColour("answer");
answerOver = getTextColour("answer_over");
answerPressed = getTextColour("answer_pressed");
journalLink = getTextColour("journal_link");
journalLinkOver = getTextColour("journal_link_over");
journalLinkPressed = getTextColour("journal_link_pressed");
journalTopic = getTextColour("journal_topic");
journalTopicOver = getTextColour("journal_topic_over");
journalTopicPressed = getTextColour("journal_topic_pressed");
}
}

@ -5,14 +5,12 @@
namespace MWGui
{
struct TextColours
{
MyGUI::Colour header;
MyGUI::Colour normal;
MyGUI::Colour notify;
MyGUI::Colour link;
MyGUI::Colour linkOver;
MyGUI::Colour linkPressed;
@ -28,6 +26,9 @@ namespace MWGui
MyGUI::Colour journalTopic;
MyGUI::Colour journalTopicOver;
MyGUI::Colour journalTopicPressed;
public:
void loadColours();
};
}

@ -17,7 +17,7 @@
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "mapwindow.hpp"

@ -74,11 +74,11 @@ namespace MWGui
mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold));
// NPC can train you in his best 3 skills
std::vector< std::pair<int, int> > skills;
std::vector< std::pair<int, float> > skills;
for (int i=0; i<ESM::Skill::Length; ++i)
{
int value = actor.getClass().getSkill(actor, i);
float value = actor.getClass().getSkill(actor, i);
skills.push_back(std::make_pair(i, value));
}

@ -150,11 +150,10 @@ namespace MWGui
if (hour >= 13) hour -= 12;
if (hour == 0) hour = 12;
std::string dateTimeText =
MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getDay ()) + " "
+ month + " (#{sDay} " + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay())
+ ") " + MyGUI::utility::toString(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}");
ESM::EpochTimeStamp currentDate = MWBase::Environment::get().getWorld()->getEpochTimeStamp();
int daysPassed = MWBase::Environment::get().getWorld()->getTimeStamp().getDay();
std::string formattedHour = pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}";
std::string dateTimeText = Misc::StringUtils::format("%i %s (#{sDay} %i) %i %s", currentDate.mDay, month, daysPassed, hour, formattedHour);
mDateTimeText->setCaptionWithReplacing (dateTimeText);
}

@ -443,6 +443,9 @@ namespace MWGui
// constant effects have no duration and no target
if (!mEffectParams.mIsConstant)
{
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce))
mEffectParams.mDuration = std::max(1, mEffectParams.mDuration);
if (mEffectParams.mDuration > 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
{
spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + MyGUI::utility::toString(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs);

@ -15,12 +15,16 @@
#include <MyGUI_ClipboardManager.h>
#include <MyGUI_WidgetManager.h>
// For BT_NO_PROFILE
#include <LinearMath/btQuickprof.h>
#include <SDL_keyboard.h>
#include <SDL_clipboard.h>
#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>
@ -113,21 +117,10 @@
#include "keyboardnavigation.hpp"
#include "resourceskin.hpp"
namespace
{
MyGUI::Colour getTextColour(const std::string& type)
{
return MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=" + type + "}"));
}
}
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)
@ -178,12 +171,6 @@ namespace MWGui
, mCursorVisible(true)
, mCursorActive(false)
, mPlayerBounty(-1)
, mPlayerName()
, mPlayerRaceId()
, mPlayerAttributes()
, mPlayerMajorSkills()
, mPlayerMinorSkills()
, mPlayerSkillValues()
, mGui(nullptr)
, mGuiModes()
, mCursorManager(nullptr)
@ -194,8 +181,8 @@ namespace MWGui
, mRestAllowed(true)
, mShowOwned(0)
, 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);
@ -231,13 +218,6 @@ namespace MWGui
SpellView::registerComponents();
Gui::registerAllWidgets();
int fontSize = Settings::Manager::getInt("font size", "GUI");
fontSize = std::min(std::max(12, fontSize), 20);
mFontHeight = fontSize;
MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource");
MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate("Resource") = newDelegate(this, &WindowManager::loadFontDelegate);
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerFollowMouse>("Controller");
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
@ -250,7 +230,7 @@ namespace MWGui
mKeyboardNavigation->setEnabled(keyboardNav);
Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav);
mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer);
mLoadingScreen = new LoadingScreen(mResourceSystem, mViewer);
mWindows.push_back(mLoadingScreen);
//set up the hardware cursor manager
@ -288,94 +268,10 @@ namespace MWGui
MyGUI::ClipboardManager::getInstance().eventClipboardRequested += MyGUI::newDelegate(this, &WindowManager::onClipboardRequested);
mShowOwned = Settings::Manager::getInt("show owned", "Game");
}
void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version)
{
MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator();
bool createCopy = false;
while (resourceNode.next("Resource"))
{
std::string type, name;
resourceNode->findAttribute("type", type);
resourceNode->findAttribute("name", name);
if (name.empty())
continue;
if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont"))
{
createCopy = true;
// For TrueType fonts we should override Size and Resolution properties
// to allow to configure font size via config file, without need to edit XML files.
// Also we should take UI scaling factor in account.
int resolution = Settings::Manager::getInt("ttf resolution", "GUI");
resolution = std::min(960, std::max(48, resolution));
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
resolution *= uiScale;
MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property");
resolutionNode->addAttribute("key", "Resolution");
resolutionNode->addAttribute("value", std::to_string(resolution));
MyGUI::xml::ElementPtr sizeNode = resourceNode->createChild("Property");
sizeNode->addAttribute("key", "Size");
sizeNode->addAttribute("value", std::to_string(mFontHeight));
}
else if (Misc::StringUtils::ciEqual(type, "ResourceSkin") ||
Misc::StringUtils::ciEqual(type, "AutoSizedResourceSkin"))
{
// We should adjust line height for MyGUI widgets depending on font size
MyGUI::xml::ElementPtr heightNode = resourceNode->createChild("Property");
heightNode->addAttribute("key", "HeightLine");
heightNode->addAttribute("value", std::to_string(mFontHeight+2));
}
}
MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version);
if (createCopy)
{
MyGUI::xml::ElementPtr copy = _node->createCopy();
MyGUI::xml::ElementEnumerator copyFont = copy->getElementEnumerator();
while (copyFont.next("Resource"))
{
std::string type, name;
copyFont->findAttribute("type", type);
copyFont->findAttribute("name", name);
if (name.empty())
continue;
if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont"))
{
// Since the journal and books use the custom scaling factor depending on resolution,
// setup separate fonts with different Resolution to fit these windows.
// These fonts have an internal prefix.
int resolution = Settings::Manager::getInt("ttf resolution", "GUI");
resolution = std::min(960, std::max(48, resolution));
float currentX = Settings::Manager::getInt("resolution x", "Video");
float currentY = Settings::Manager::getInt("resolution y", "Video");
// TODO: read size from openmw_layout.xml
float heightScale = (currentY / 520);
float widthScale = (currentX / 600);
float uiScale = std::min(widthScale, heightScale);
resolution *= uiScale;
MyGUI::xml::ElementPtr resolutionNode = copyFont->createChild("Property");
resolutionNode->addAttribute("key", "Resolution");
resolutionNode->addAttribute("value", std::to_string(resolution));
copyFont->setAttribute("name", "Journalbook " + name);
}
}
MyGUI::ResourceManager::getInstance().loadFromXmlNode(copy, _file, _version);
}
mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer);
mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"),
Settings::Manager::getFloat("contrast", "Video"));
}
void WindowManager::loadUserFonts()
@ -389,26 +285,7 @@ namespace MWGui
int w = MyGUI::RenderManager::getInstance().getViewSize().width;
int h = MyGUI::RenderManager::getInstance().getViewSize().height;
mTextColours.header = getTextColour("header");
mTextColours.normal = getTextColour("normal");
mTextColours.notify = getTextColour("notify");
mTextColours.link = getTextColour("link");
mTextColours.linkOver = getTextColour("link_over");
mTextColours.linkPressed = getTextColour("link_pressed");
mTextColours.answer = getTextColour("answer");
mTextColours.answerOver = getTextColour("answer_over");
mTextColours.answerPressed = getTextColour("answer_pressed");
mTextColours.journalLink = getTextColour("journal_link");
mTextColours.journalLinkOver = getTextColour("journal_link_over");
mTextColours.journalLinkPressed = getTextColour("journal_link_pressed");
mTextColours.journalTopic = getTextColour("journal_topic");
mTextColours.journalTopicOver = getTextColour("journal_topic_over");
mTextColours.journalTopicPressed = getTextColour("journal_topic_pressed");
mTextColours.loadColours();
mDragAndDrop = new DragAndDrop();
@ -585,17 +462,6 @@ namespace MWGui
mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem);
// Setup player stats
for (int i = 0; i < ESM::Attribute::Length; ++i)
{
mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::AttributeValue()));
}
for (int i = 0; i < ESM::Skill::Length; ++i)
{
mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::SkillValue()));
}
updatePinnedWindows();
// Set up visibility
@ -604,7 +470,7 @@ namespace MWGui
int WindowManager::getFontHeight() const
{
return mFontHeight;
return mFontLoader->getFontHeight();
}
void WindowManager::setNewGame(bool newgame)
@ -625,7 +491,6 @@ namespace MWGui
{
mKeyboardNavigation.reset();
MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource");
MyGUI::LanguageManager::getInstance().eventRequestTag.clear();
MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear();
MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear();
@ -653,6 +518,7 @@ namespace MWGui
mGuiPlatform->shutdown();
delete mGuiPlatform;
delete mVideoWrapper;
}
catch(const MyGUI::Exception& e)
{
@ -787,40 +653,14 @@ namespace MWGui
{
mStatsWindow->setValue (id, value);
mCharGen->setValue(id, value);
static const char *ids[] =
{
"AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5",
"AttribVal6", "AttribVal7", "AttribVal8"
};
static ESM::Attribute::AttributeID attributes[] =
{
ESM::Attribute::Strength,
ESM::Attribute::Intelligence,
ESM::Attribute::Willpower,
ESM::Attribute::Agility,
ESM::Attribute::Speed,
ESM::Attribute::Endurance,
ESM::Attribute::Personality,
ESM::Attribute::Luck
};
for (size_t i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i)
{
if (id != ids[i])
continue;
mPlayerAttributes[attributes[i]] = value;
break;
}
}
void WindowManager::setValue (int parSkill, const MWMechanics::SkillValue& value)
{
/// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we
/// allow custom skills.
mStatsWindow->setValue(static_cast<ESM::Skill::SkillEnum> (parSkill), value);
mCharGen->setValue(static_cast<ESM::Skill::SkillEnum> (parSkill), value);
mPlayerSkillValues[parSkill] = value;
}
void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value)
@ -833,10 +673,6 @@ namespace MWGui
void WindowManager::setValue (const std::string& id, const std::string& value)
{
mStatsWindow->setValue (id, value);
if (id=="name")
mPlayerName = value;
else if (id=="race")
mPlayerRaceId = value;
}
void WindowManager::setValue (const std::string& id, int value)
@ -858,8 +694,6 @@ namespace MWGui
{
mStatsWindow->configureSkills (major, minor);
mCharGen->configureSkills(major, minor);
mPlayerMajorSkills = major;
mPlayerMinorSkills = minor;
}
void WindowManager::updateSkillArea()
@ -916,7 +750,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
{
@ -995,7 +829,7 @@ namespace MWGui
mHud->setPlayerPos(x, y, u, v);
}
void WindowManager::onFrame (float frameDuration)
void WindowManager::update (float frameDuration)
{
bool gameRunning = MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame;
@ -1241,6 +1075,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")
@ -1249,11 +1084,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
@ -1283,9 +1145,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);
@ -1583,26 +1463,6 @@ namespace MWGui
return mGuiModes.back();
}
std::map<int, MWMechanics::SkillValue > WindowManager::getPlayerSkillValues()
{
return mPlayerSkillValues;
}
std::map<int, MWMechanics::AttributeValue > WindowManager::getPlayerAttributeValues()
{
return mPlayerAttributes;
}
WindowManager::SkillList WindowManager::getPlayerMinorSkills()
{
return mPlayerMinorSkills;
}
WindowManager::SkillList WindowManager::getPlayerMajorSkills()
{
return mPlayerMajorSkills;
}
void WindowManager::disallowMouse()
{
mInputBlocker->setVisible (true);
@ -1925,7 +1785,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);
@ -2149,7 +2009,9 @@ namespace MWGui
void WindowManager::toggleDebugWindow()
{
#ifndef BT_NO_PROFILE
mDebugWindow->setVisible(!mDebugWindow->isVisible());
#endif
}
void WindowManager::cycleSpell(bool next)

@ -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();
@ -277,13 +280,7 @@ namespace MWGui
virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
virtual void onFrame (float frameDuration);
/// \todo get rid of this stuff. Move it to the respective UI element classes, if needed.
virtual std::map<int, MWMechanics::SkillValue > getPlayerSkillValues();
virtual std::map<int, MWMechanics::AttributeValue > getPlayerAttributeValues();
virtual SkillList getPlayerMinorSkills();
virtual SkillList getPlayerMajorSkills();
virtual void update (float duration);
/**
* Fetches a GMST string from the store, if there is no setting with the given
@ -296,7 +293,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);
@ -411,8 +411,6 @@ namespace MWGui
MWWorld::Ptr mSelectedEnchantItem;
MWWorld::Ptr mSelectedWeapon;
void loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version);
std::vector<WindowModal*> mCurrentModals;
// Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window).
@ -468,14 +466,6 @@ namespace MWGui
void setCursorVisible(bool visible);
/// \todo get rid of this stuff. Move it to the respective UI element classes, if needed.
// Various stats about player as needed by window manager
std::string mPlayerName;
std::string mPlayerRaceId;
std::map<int, MWMechanics::AttributeValue > mPlayerAttributes;
SkillList mPlayerMajorSkills, mPlayerMinorSkills;
std::map<int, MWMechanics::SkillValue > mPlayerSkillValues;
MyGUI::Gui *mGui; // Gui
struct GuiModeState
@ -525,14 +515,16 @@ namespace MWGui
ToUTF8::FromType mEncoding;
int mFontHeight;
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,616 @@
#include "actionmanager.hpp"
#include <MyGUI_InputManager.h>
#include <SDL_keyboard.h>
#include <components/settings/settings.hpp>
#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;
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;
// 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;
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,714 @@
#include "bindingsmanager.hpp"
#include <MyGUI_EditBox.h>
#include <extern/oics/ICSChannelListener.h>
#include <extern/oics/ICSInputControlSystem.h>
#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)
{
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
{
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,398 @@
#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)
, 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)
{
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 (!MWBase::Environment::get().getInputManager()->controlsDisabled())
mBindingsManager->controllerButtonPressed(deviceID, arg);
}
void ControllerManager::buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg)
{
if (mBindingsManager->isDetectingBindingState())
{
mBindingsManager->controllerButtonReleased(deviceID, arg);
return;
}
if (!mJoystickEnabled || MWBase::Environment::get().getInputManager()->controlsDisabled())
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 || MWBase::Environment::get().getInputManager()->controlsDisabled())
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,65 @@
#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);
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 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;
virtual void executeAction(int action);
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 bool controlsDisabled() { return mControlsDisabled; }
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 mControlsDisabled;
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)
{
}
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;
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
if (!input->controlsDisabled() && !consumed)
mBindingsManager->keyPressed(arg);
input->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,26 @@
#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);
private:
BindingsManager* mBindingsManager;
};
}
#endif

@ -0,0 +1,252 @@
#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"))
, mGrabCursor(Settings::Manager::getBool("grab cursor", "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)
, 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");
if (setting.first == "Input" && setting.second == "grab cursor")
mGrabCursor = Settings::Manager::getBool("grab cursor", "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 && !input->controlsDisabled())
{
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)
{
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
if (mBindingsManager->isDetectingBindingState() || !input->controlsDisabled())
mBindingsManager->mouseWheelMoved(arg);
input->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::updateCursorMode()
{
bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu)
&& !MWBase::Environment::get().getWindowManager()->isConsoleMode();
bool wasRelative = mInputWrapper->getMouseRelative();
bool isRelative = !MWBase::Environment::get().getWindowManager()->isGuiMode();
// don't keep the pointer away from the window edge in gui mode
// stop using raw mouse motions and switch to system cursor movements
mInputWrapper->setMouseRelative(isRelative);
//we let the mouse escape in the main menu
mInputWrapper->setGrabPointer(grab && (mGrabCursor || isRelative));
//we switched to non-relative mode, move our cursor to where the in-game
//cursor is
if (!isRelative && wasRelative != isRelative)
{
warpMouse();
}
}
void MouseManager::update(float dt)
{
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,59 @@
#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 updateCursorMode();
void update(float dt);
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;
bool mGrabCursor;
float mCameraSensitivity;
float mCameraYMultiplier;
BindingsManager* mBindingsManager;
SDLUtil::InputWrapper* mInputWrapper;
float mInvUiScalingFactor;
float mGuiCursorX;
float mGuiCursorY;
int mMouseWheel;
bool mMouseLookEnabled;
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

@ -12,14 +12,12 @@
namespace MWMechanics
{
void ActiveSpells::update() const
void ActiveSpells::update(float duration) const
{
bool rebuild = false;
MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp();
// Erase no longer active spells and effects
if (mLastUpdate!=now)
if (duration > 0)
{
TContainer::iterator iter (mSpells.begin());
while (iter!=mSpells.end())
@ -31,24 +29,35 @@ namespace MWMechanics
}
else
{
bool interrupt = false;
std::vector<ActiveEffect>& effects = iter->second.mEffects;
for (std::vector<ActiveEffect>::iterator effectIt = effects.begin(); effectIt != effects.end();)
{
MWWorld::TimeStamp start = iter->second.mTimeStamp;
MWWorld::TimeStamp end = start + static_cast<double>(effectIt->mDuration)*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
if (end <= now)
if (effectIt->mTimeLeft <= 0)
{
effectIt = effects.erase(effectIt);
rebuild = true;
// Note: it we expire a Corprus effect, we should remove the whole spell.
if (effectIt->mEffectId == ESM::MagicEffect::Corprus)
{
iter = mSpells.erase (iter);
interrupt = true;
break;
}
effectIt = effects.erase(effectIt);
}
else
{
effectIt->mTimeLeft -= duration;
++effectIt;
}
}
++iter;
if (!interrupt)
++iter;
}
}
mLastUpdate = now;
}
if (mSpellsChanged)
@ -63,24 +72,15 @@ namespace MWMechanics
void ActiveSpells::rebuildEffects() const
{
MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp();
mEffects = MagicEffects();
for (TIterator iter (begin()); iter!=end(); ++iter)
{
const MWWorld::TimeStamp& start = iter->second.mTimeStamp;
const std::vector<ActiveEffect>& effects = iter->second.mEffects;
for (std::vector<ActiveEffect>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
{
double duration = effectIt->mDuration;
MWWorld::TimeStamp end = start;
end += duration *
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
if (end>now)
if (effectIt->mTimeLeft > 0)
mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude));
}
}
@ -88,12 +88,11 @@ namespace MWMechanics
ActiveSpells::ActiveSpells()
: mSpellsChanged (false)
, mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp())
{}
const MagicEffects& ActiveSpells::getMagicEffects() const
{
update();
update(0.f);
return mEffects;
}
@ -116,19 +115,14 @@ namespace MWMechanics
for (std::vector<ActiveEffect>::const_iterator iter (effects.begin());
iter!=effects.end(); ++iter)
{
if (iter->mDuration > duration)
duration = iter->mDuration;
if (iter->mTimeLeft > duration)
duration = iter->mTimeLeft;
}
double scaledDuration = duration *
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp() - iterator->second.mTimeStamp;
if (usedUp>=scaledDuration)
if (duration < 0)
return 0;
return scaledDuration-usedUp;
return duration;
}
bool ActiveSpells::isSpellActive(const std::string& id) const
@ -152,7 +146,6 @@ namespace MWMechanics
TContainer::iterator it(mSpells.find(id));
ActiveSpellParams params;
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
params.mEffects = effects;
params.mDisplayName = displayName;
params.mCasterActorId = casterActorId;
@ -211,19 +204,15 @@ namespace MWMechanics
{
for (TContainer::const_iterator it = begin(); it != end(); ++it)
{
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
for (std::vector<ActiveEffect>::const_iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end(); ++effectIt)
{
std::string name = it->second.mDisplayName;
float remainingTime = effectIt->mDuration +
static_cast<float>(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
float magnitude = effectIt->mMagnitude;
if (magnitude)
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration);
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, effectIt->mTimeLeft, effectIt->mDuration);
}
}
}
@ -301,6 +290,31 @@ namespace MWMechanics
mSpellsChanged = true;
}
void ActiveSpells::purgeCorprusDisease()
{
for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();)
{
bool hasCorprusEffect = false;
for (std::vector<ActiveEffect>::iterator effectIt = iter->second.mEffects.begin();
effectIt != iter->second.mEffects.end();++effectIt)
{
if (effectIt->mEffectId == ESM::MagicEffect::Corprus)
{
hasCorprusEffect = true;
break;
}
}
if (hasCorprusEffect)
{
mSpells.erase(iter++);
mSpellsChanged = true;
}
else
++iter;
}
}
void ActiveSpells::clear()
{
mSpells.clear();
@ -316,7 +330,6 @@ namespace MWMechanics
params.mEffects = it->second.mEffects;
params.mCasterActorId = it->second.mCasterActorId;
params.mDisplayName = it->second.mDisplayName;
params.mTimeStamp = it->second.mTimeStamp.toEsm();
state.mSpells.insert (std::make_pair(it->first, params));
}
@ -331,7 +344,6 @@ namespace MWMechanics
params.mEffects = it->second.mEffects;
params.mCasterActorId = it->second.mCasterActorId;
params.mDisplayName = it->second.mDisplayName;
params.mTimeStamp = MWWorld::TimeStamp(it->second.mTimeStamp);
mSpells.insert (std::make_pair(it->first, params));
mSpellsChanged = true;

@ -44,15 +44,14 @@ namespace MWMechanics
TIterator end() const;
void update(float duration) const;
private:
mutable TContainer mSpells;
mutable MagicEffects mEffects;
mutable bool mSpellsChanged;
mutable MWWorld::TimeStamp mLastUpdate;
void update() const;
void rebuildEffects() const;
/// Add any effects that are in "from" and not in "addTo" to "addTo"
@ -100,6 +99,8 @@ namespace MWMechanics
bool isSpellActive (const std::string& id) const;
///< case insensitive
void purgeCorprusDisease();
const MagicEffects& getMagicEffects() const;
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;

@ -18,4 +18,44 @@ namespace MWMechanics
{
return mCharacterController.get();
}
int Actor::getGreetingTimer() const
{
return mGreetingTimer;
}
void Actor::setGreetingTimer(int timer)
{
mGreetingTimer = timer;
}
float Actor::getAngleToPlayer() const
{
return mTargetAngleRadians;
}
void Actor::setAngleToPlayer(float angle)
{
mTargetAngleRadians = angle;
}
GreetingState Actor::getGreetingState() const
{
return mGreetingState;
}
void Actor::setGreetingState(GreetingState state)
{
mGreetingState = state;
}
bool Actor::isTurningToPlayer() const
{
return mIsTurningToPlayer;
}
void Actor::setTurningToPlayer(bool turning)
{
mIsTurningToPlayer = turning;
}
}

@ -3,6 +3,8 @@
#include <memory>
#include "../mwmechanics/actorutil.hpp"
namespace MWRender
{
class Animation;
@ -27,8 +29,24 @@ namespace MWMechanics
CharacterController* getCharacterController();
int getGreetingTimer() const;
void setGreetingTimer(int timer);
float getAngleToPlayer() const;
void setAngleToPlayer(float angle);
GreetingState getGreetingState() const;
void setGreetingState(GreetingState state);
bool isTurningToPlayer() const;
void setTurningToPlayer(bool turning);
private:
std::unique_ptr<CharacterController> mCharacterController;
int mGreetingTimer{0};
float mTargetAngleRadians{0.f};
GreetingState mGreetingState{Greet_None};
bool mIsTurningToPlayer{false};
};
}

@ -40,6 +40,7 @@
#include "summoning.hpp"
#include "combat.hpp"
#include "actorutil.hpp"
#include "tickableeffects.hpp"
namespace
{
@ -111,11 +112,11 @@ void adjustCommandedActor (const MWWorld::Ptr& actor)
bool hasCommandPackage = false;
std::list<MWMechanics::AiPackage*>::const_iterator it;
for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it)
auto it = stats.getAiSequence().begin();
for (; it != stats.getAiSequence().end(); ++it)
{
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow &&
static_cast<MWMechanics::AiFollow*>(*it)->isCommanded())
static_cast<const MWMechanics::AiFollow*>(it->get())->isCommanded())
{
hasCommandPackage = true;
break;
@ -131,7 +132,7 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
health = 0.1f * endurance;
float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat ();
@ -175,6 +176,49 @@ namespace MWMechanics
}
};
class GetCurrentMagnitudes : public MWMechanics::EffectSourceVisitor
{
std::string mSpellId;
public:
GetCurrentMagnitudes(const std::string& spellId)
: mSpellId(spellId)
{
}
virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& sourceId, int casterActorId,
float magnitude, float remainingTime = -1, float totalTime = -1)
{
if (magnitude <= 0)
return;
if (sourceId != mSpellId)
return;
mMagnitudes.push_back(std::make_pair(key, magnitude));
}
std::vector<std::pair<MWMechanics::EffectKey, float>> mMagnitudes;
};
class GetCorprusSpells : public MWMechanics::EffectSourceVisitor
{
public:
virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& sourceId, int casterActorId,
float magnitude, float remainingTime = -1, float totalTime = -1)
{
if (key.mId != ESM::MagicEffect::Corprus)
return;
mSpells.push_back(sourceId);
}
std::vector<std::string> mSpells;
};
class SoulTrap : public MWMechanics::EffectSourceVisitor
{
MWWorld::Ptr mCreature;
@ -418,7 +462,7 @@ namespace MWMechanics
if (world->isSwimming(actor) || (playerPos - actorPos).length2() >= 3000 * 3000)
return;
// Our implementation is not FPS-dependent unlike Morrowind's so it needs to be recalibrated.
// Our implementation is not FPS-dependent unlike Morrowind's so it needs to be recalibrated.
// We chose to use the chance MW would have when run at 60 FPS with the default value of the GMST.
const float delta = MWBase::Environment::get().getFrameDuration() * 6.f;
static const float fVoiceIdleOdds = world->getStore().get<ESM::GameSetting>().find("fVoiceIdleOdds")->mValue.getFloat();
@ -434,9 +478,9 @@ namespace MWMechanics
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
MWMechanics::AiSequence& seq = stats.getAiSequence();
if (!seq.isEmpty() && seq.getActivePackage()->useVariableSpeed())
if (!seq.isEmpty() && seq.getActivePackage().useVariableSpeed())
{
osg::Vec3f targetPos = seq.getActivePackage()->getDestination();
osg::Vec3f targetPos = seq.getActivePackage().getDestination();
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
float distance = (targetPos - actorPos).length();
if (distance < DECELERATE_DISTANCE)
@ -446,7 +490,7 @@ namespace MWMechanics
actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor;
}
void Actors::updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly)
void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly)
{
if (!actor.getClass().isActor() || actor == getPlayer())
return;
@ -459,9 +503,9 @@ namespace MWMechanics
MWBase::Environment::get().getWorld()->isSwimming(actor) ||
(packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1))
{
stats.setTurningToPlayer(false);
stats.setGreetingTimer(0);
stats.setGreetingState(Greet_None);
actorState.setTurningToPlayer(false);
actorState.setGreetingTimer(0);
actorState.setGreetingState(Greet_None);
return;
}
@ -470,14 +514,14 @@ namespace MWMechanics
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
osg::Vec3f dir = playerPos - actorPos;
if (stats.isTurningToPlayer())
if (actorState.isTurningToPlayer())
{
// Reduce the turning animation glitch by using a *HUGE* value of
// epsilon... TODO: a proper fix might be in either the physics or the
// animation subsystem
if (zTurn(actor, stats.getAngleToPlayer(), osg::DegreesToRadians(5.f)))
if (zTurn(actor, actorState.getAngleToPlayer(), osg::DegreesToRadians(5.f)))
{
stats.setTurningToPlayer(false);
actorState.setTurningToPlayer(false);
// An original engine launches an endless idle2 when an actor greets player.
playAnimationGroup (actor, "idle2", 0, std::numeric_limits<int>::max(), false);
}
@ -492,8 +536,8 @@ namespace MWMechanics
float helloDistance = static_cast<float>(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier);
int greetingTimer = stats.getGreetingTimer();
GreetingState greetingState = stats.getGreetingState();
int greetingTimer = actorState.getGreetingTimer();
GreetingState greetingState = actorState.getGreetingState();
if (greetingState == Greet_None)
{
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
@ -515,7 +559,7 @@ namespace MWMechanics
greetingTimer++;
if (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor))
turnActorToFacePlayer(actor, dir);
turnActorToFacePlayer(actor, actorState, dir);
if (greetingTimer >= GREETING_COOLDOWN)
{
@ -531,20 +575,19 @@ namespace MWMechanics
greetingState = Greet_None;
}
stats.setGreetingTimer(greetingTimer);
stats.setGreetingState(greetingState);
actorState.setGreetingTimer(greetingTimer);
actorState.setGreetingState(greetingState);
}
void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir)
void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir)
{
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
if (!stats.isTurningToPlayer())
if (!actorState.isTurningToPlayer())
{
stats.setAngleToPlayer(std::atan2(dir.x(), dir.y()));
stats.setTurningToPlayer(true);
actorState.setAngleToPlayer(std::atan2(dir.x(), dir.y()));
actorState.setTurningToPlayer(true);
}
}
@ -604,7 +647,7 @@ namespace MWMechanics
bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end();
// If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them
// Doesn't apply for player followers/escorters
// Doesn't apply for player followers/escorters
if (!aggressive && !isPlayerFollowerOrEscorter)
{
// Check that actor2 is in combat with actor1
@ -673,7 +716,7 @@ namespace MWMechanics
return;
bool followerOrEscorter = false;
for (const AiPackage* package : creatureStats2.getAiSequence())
for (const auto& package : creatureStats2.getAiSequence())
{
// The follow package must be first or have nothing but combat before it
if (package->sideWithTarget())
@ -722,7 +765,7 @@ namespace MWMechanics
{
CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);
int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();
float intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();
float base = 1.f;
if (ptr == getPlayer())
@ -773,6 +816,9 @@ namespace MWMechanics
if (visitor.mRemainingTime > 0)
{
double timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
if(timeScale == 0.0)
timeScale = 1;
restoreHours = std::max(0.0, hours - visitor.mRemainingTime * timeScale / 3600.f);
}
else if (visitor.mRemainingTime == -1)
@ -798,7 +844,7 @@ namespace MWMechanics
float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat ();
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr);
if (normalizedEncumbrance > 1)
@ -825,7 +871,7 @@ namespace MWMechanics
return;
// Restore fatigue
int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified();
float endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified();
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat ();
static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
@ -939,21 +985,75 @@ namespace MWMechanics
if (creatureStats.needToRecalcDynamicStats())
calculateDynamicStats(ptr);
if (ptr == getPlayer())
{
Spells & spells = creatureStats.getSpells();
for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
GetCorprusSpells getCorprusSpellsVisitor;
creatureStats.getSpells().visitEffectSources(getCorprusSpellsVisitor);
creatureStats.getActiveSpells().visitEffectSources(getCorprusSpellsVisitor);
ptr.getClass().getInventoryStore(ptr).visitEffectSources(getCorprusSpellsVisitor);
std::vector<std::string> corprusSpells = getCorprusSpellsVisitor.mSpells;
std::vector<std::string> corprusSpellsToRemove;
for (auto it = creatureStats.getCorprusSpells().begin(); it != creatureStats.getCorprusSpells().end(); ++it)
{
if (spells.getCorprusSpells().find(it->first) != spells.getCorprusSpells().end())
if(std::find(corprusSpells.begin(), corprusSpells.end(), it->first) == corprusSpells.end())
{
if (MWBase::Environment::get().getWorld()->getTimeStamp() >= spells.getCorprusSpells().at(it->first).mNextWorsening)
{
spells.worsenCorprus(it->first);
// Corprus effect expired, remove entry and restore stats.
MWBase::Environment::get().getMechanicsManager()->restoreStatsAfterCorprus(ptr, it->first);
corprusSpellsToRemove.push_back(it->first);
corprusSpells.erase(std::remove(corprusSpells.begin(), corprusSpells.end(), it->first), corprusSpells.end());
continue;
}
if (ptr == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}");
corprusSpells.erase(std::remove(corprusSpells.begin(), corprusSpells.end(), it->first), corprusSpells.end());
if (MWBase::Environment::get().getWorld()->getTimeStamp() >= it->second.mNextWorsening)
{
it->second.mNextWorsening += CorprusStats::sWorseningPeriod;
GetCurrentMagnitudes getMagnitudesVisitor (it->first);
creatureStats.getSpells().visitEffectSources(getMagnitudesVisitor);
creatureStats.getActiveSpells().visitEffectSources(getMagnitudesVisitor);
ptr.getClass().getInventoryStore(ptr).visitEffectSources(getMagnitudesVisitor);
for (auto& effectMagnitude : getMagnitudesVisitor.mMagnitudes)
{
if (effectMagnitude.first.mId == ESM::MagicEffect::FortifyAttribute)
{
AttributeValue attr = creatureStats.getAttribute(effectMagnitude.first.mArg);
attr.damage(-effectMagnitude.second);
creatureStats.setAttribute(effectMagnitude.first.mArg, attr);
it->second.mWorsenings[effectMagnitude.first.mArg] = 0;
}
else if (effectMagnitude.first.mId == ESM::MagicEffect::DrainAttribute)
{
AttributeValue attr = creatureStats.getAttribute(effectMagnitude.first.mArg);
int currentDamage = attr.getDamage();
if (currentDamage >= 0)
it->second.mWorsenings[effectMagnitude.first.mArg] = std::min(it->second.mWorsenings[effectMagnitude.first.mArg], currentDamage);
it->second.mWorsenings[effectMagnitude.first.mArg] += effectMagnitude.second;
attr.damage(effectMagnitude.second);
creatureStats.setAttribute(effectMagnitude.first.mArg, attr);
}
}
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}");
}
}
for (std::string& oldCorprusSpell : corprusSpellsToRemove)
{
creatureStats.removeCorprusSpell(oldCorprusSpell);
}
for (std::string& newCorprusSpell : corprusSpells)
{
CorprusStats corprus;
for (int i=0; i<ESM::Attribute::Length; ++i)
corprus.mWorsenings[i] = 0;
corprus.mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod;
creatureStats.addCorprusSpell(newCorprusSpell, corprus);
}
}
// AI setting modifiers
@ -1235,11 +1335,6 @@ namespace MWMechanics
if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name())
inventoryStore.unequipItem(*heldIter, ptr);
}
else if (heldIter == inventoryStore.end() || heldIter->getTypeName() == typeid(ESM::Light).name())
{
// For hostile NPCs, see if they have anything better to equip first
inventoryStore.autoEquip(ptr);
}
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
@ -1623,9 +1718,16 @@ namespace MWMechanics
player.getClass().getCreatureStats(player).setHitAttemptActorId(-1);
}
// For dead actors we need to remove looping spell particles
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
// For dead actors we need to update looping spell particles
if (iter->first.getClass().getCreatureStats(iter->first).isDead())
{
// They can be added during the death animation
if (!iter->first.getClass().getCreatureStats(iter->first).isDeathAnimationFinished())
adjustMagicEffects(iter->first);
ctrl->updateContinuousVfx();
}
else
{
bool cellChanged = world->hasCellChanged();
@ -1694,7 +1796,7 @@ namespace MWMechanics
if (isConscious(iter->first))
{
stats.getAiSequence().execute(iter->first, *ctrl, duration);
updateGreetingState(iter->first, timerUpdateHello > 0);
updateGreetingState(iter->first, *iter->second, timerUpdateHello > 0);
playIdleDialogue(iter->first);
updateMovementSpeed(iter->first);
}
@ -1738,7 +1840,7 @@ namespace MWMechanics
if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed())
{
MWMechanics::AiSequence& seq = stats.getAiSequence();
alwaysActive = !seq.isEmpty() && seq.getActivePackage()->alwaysActive();
alwaysActive = !seq.isEmpty() && seq.getActivePackage().alwaysActive();
}
bool inRange = isPlayer || dist <= mActorsProcessingRange || alwaysActive;
int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower)
@ -1939,12 +2041,18 @@ namespace MWMechanics
void Actors::rest(double hours, bool sleep)
{
float duration = hours * 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor();
float duration = hours * 3600.f;
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
if (timeScale != 0.f)
duration /= timeScale;
const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
if (iter->first.getClass().getCreatureStats(iter->first).isDead())
continue;
@ -2154,7 +2262,7 @@ namespace MWMechanics
// An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package
// Actors that are targeted by this actor's Follow or Escort packages also side with them
for (const AiPackage* package : stats.getAiSequence())
for (const auto& package : stats.getAiSequence())
{
if (package->sideWithTarget() && !package->getTarget().isEmpty())
{
@ -2188,9 +2296,9 @@ namespace MWMechanics
if (stats.isDead())
continue;
// An actor counts as following if AiFollow is the current AiPackage,
// An actor counts as following if AiFollow is the current AiPackage,
// or there are only Combat and Wander packages before the AiFollow package
for (const AiPackage* package : stats.getAiSequence())
for (const auto& package : stats.getAiSequence())
{
if (package->followTargetThroughDoors() && package->getTarget() == actor)
list.push_back(iteratedActor);
@ -2253,11 +2361,11 @@ namespace MWMechanics
// An actor counts as following if AiFollow is the current AiPackage,
// or there are only Combat and Wander packages before the AiFollow package
for (AiPackage* package : stats.getAiSequence())
for (const auto& package : stats.getAiSequence())
{
if (package->followTargetThroughDoors() && package->getTarget() == actor)
{
list.push_back(static_cast<AiFollow*>(package)->getFollowIndex());
list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
break;
}
else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander)
@ -2385,6 +2493,42 @@ namespace MWMechanics
return ctrl->isAttackingOrSpell();
}
int Actors::getGreetingTimer(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
return 0;
return it->second->getGreetingTimer();
}
float Actors::getAngleToPlayer(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
return 0.f;
return it->second->getAngleToPlayer();
}
GreetingState Actors::getGreetingState(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
return Greet_None;
return it->second->getGreetingState();
}
bool Actors::isTurningToPlayer(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
return false;
return it->second->isTurningToPlayer();
}
void Actors::fastForwardAi()
{
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())

@ -7,6 +7,8 @@
#include <list>
#include <map>
#include "../mwmechanics/actorutil.hpp"
namespace ESM
{
class ESMReader;
@ -70,6 +72,7 @@ namespace MWMechanics
PtrActorMap::const_iterator begin() { return mActors.begin(); }
PtrActorMap::const_iterator end() { return mActors.end(); }
std::size_t size() const { return mActors.size(); }
void notifyDied(const MWWorld::Ptr &actor);
@ -122,8 +125,8 @@ namespace MWMechanics
void playIdleDialogue(const MWWorld::Ptr& actor);
void updateMovementSpeed(const MWWorld::Ptr& actor);
void updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly);
void turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir);
void updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly);
void turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir);
void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance);
@ -194,6 +197,11 @@ namespace MWMechanics
bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;
int getGreetingTimer(const MWWorld::Ptr& ptr) const;
float getAngleToPlayer(const MWWorld::Ptr& ptr) const;
GreetingState getGreetingState(const MWWorld::Ptr& ptr) const;
bool isTurningToPlayer(const MWWorld::Ptr& ptr) const;
private:
void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl);

@ -1,6 +1,16 @@
#ifndef OPENMW_MWMECHANICS_ACTORUTIL_H
#define OPENMW_MWMECHANICS_ACTORUTIL_H
#include <components/esm/loadcrea.hpp>
#include <components/esm/loadnpc.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp"
#include "./creaturestats.hpp"
namespace MWWorld
{
class Ptr;
@ -8,9 +18,43 @@ namespace MWWorld
namespace MWMechanics
{
enum GreetingState
{
Greet_None,
Greet_InProgress,
Greet_Done
};
MWWorld::Ptr getPlayer();
bool isPlayerInCombat();
bool canActorMoveByZAxis(const MWWorld::Ptr& actor);
template<class T>
void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value)
{
T copy = *MWBase::Environment::get().getWorld()->getStore().get<T>().find(id);
switch(setting)
{
case MWMechanics::CreatureStats::AiSetting::AI_Hello:
copy.mAiData.mHello = value;
break;
case MWMechanics::CreatureStats::AiSetting::AI_Fight:
copy.mAiData.mFight = value;
break;
case MWMechanics::CreatureStats::AiSetting::AI_Flee:
copy.mAiData.mFlee = value;
break;
case MWMechanics::CreatureStats::AiSetting::AI_Alarm:
copy.mAiData.mAlarm = value;
break;
default:
assert(0);
}
MWBase::Environment::get().getWorld()->createOverrideRecord(copy);
}
template void setBaseAISetting<ESM::Creature>(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value);
template void setBaseAISetting<ESM::NPC>(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value);
}
#endif

@ -18,11 +18,6 @@ namespace MWMechanics
{
}
AiActivate *MWMechanics::AiActivate::clone() const
{
return new AiActivate(*this);
}
bool AiActivate::execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
{
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow
@ -49,11 +44,6 @@ namespace MWMechanics
return false;
}
int AiActivate::getTypeId() const
{
return TypeIdActivate;
}
void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::unique_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate());

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AIACTIVATE_H
#define GAME_MWMECHANICS_AIACTIVATE_H
#include "aipackage.hpp"
#include "typedaipackage.hpp"
#include <string>
@ -19,7 +19,7 @@ namespace MWMechanics
{
/// \brief Causes actor to walk to activatable object and activate it
/** Will activate when close to object **/
class AiActivate : public AiPackage
class AiActivate final : public TypedAiPackage<AiActivate>
{
public:
/// Constructor
@ -28,14 +28,14 @@ namespace MWMechanics
AiActivate(const ESM::AiSequence::AiActivate* activate);
virtual AiActivate *clone() const;
virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration);
virtual int getTypeId() const;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
virtual void writeState(ESM::AiSequence::AiSequence& sequence) const;
static constexpr TypeId getTypeId() { return TypeIdActivate; }
void writeState(ESM::AiSequence::AiSequence& sequence) const final;
private:
std::string mObjectId;
const std::string mObjectId;
};
}
#endif // GAME_MWMECHANICS_AIACTIVATE_H

@ -16,7 +16,7 @@
static const int MAX_DIRECTIONS = 4;
MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr)
: AiPackage(), mDuration(1), mDoorPtr(doorPtr), mDirection(0)
: mDuration(1), mDoorPtr(doorPtr), mDirection(0)
{
}
@ -72,21 +72,6 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
return false;
}
MWMechanics::AiAvoidDoor *MWMechanics::AiAvoidDoor::clone() const
{
return new AiAvoidDoor(*this);
}
int MWMechanics::AiAvoidDoor::getTypeId() const
{
return TypeIdAvoidDoor;
}
unsigned int MWMechanics::AiAvoidDoor::getPriority() const
{
return 2;
}
bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const
{
return (actorPos - mLastPos).length2() < 10 * 10;

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H
#define GAME_MWMECHANICS_AIAVOIDDOOR_H
#include "aipackage.hpp"
#include "typedaipackage.hpp"
#include <string>
@ -16,26 +16,28 @@ namespace MWMechanics
/// \brief AiPackage to have an actor avoid an opening door
/** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it
**/
class AiAvoidDoor : public AiPackage
class AiAvoidDoor final : public TypedAiPackage<AiAvoidDoor>
{
public:
/// Avoid door until the door is fully open
AiAvoidDoor(const MWWorld::ConstPtr& doorPtr);
virtual AiAvoidDoor *clone() const;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration);
static constexpr TypeId getTypeId() { return TypeIdAvoidDoor; }
virtual int getTypeId() const;
virtual unsigned int getPriority() const;
virtual bool canCancel() const { return false; }
virtual bool shouldCancelPreviousAi() const { return false; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private:
float mDuration;
MWWorld::ConstPtr mDoorPtr;
const MWWorld::ConstPtr mDoorPtr;
osg::Vec3f mLastPos;
int mDirection;

@ -11,12 +11,6 @@
#include "movement.hpp"
#include "steering.hpp"
MWMechanics::AiBreathe::AiBreathe()
: AiPackage()
{
}
bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
{
static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->mValue.getFloat();
@ -37,18 +31,3 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro
return true;
}
MWMechanics::AiBreathe *MWMechanics::AiBreathe::clone() const
{
return new AiBreathe(*this);
}
int MWMechanics::AiBreathe::getTypeId() const
{
return TypeIdBreathe;
}
unsigned int MWMechanics::AiBreathe::getPriority() const
{
return 2;
}

@ -1,28 +1,27 @@
#ifndef GAME_MWMECHANICS_AIBREATHE_H
#define GAME_MWMECHANICS_AIBREATHE_H
#include "aipackage.hpp"
#include "typedaipackage.hpp"
namespace MWMechanics
{
/// \brief AiPackage to have an actor resurface to breathe
// The AI will go up if lesser than half breath left
class AiBreathe : public AiPackage
class AiBreathe final : public TypedAiPackage<AiBreathe>
{
public:
AiBreathe();
virtual AiBreathe *clone() const;
virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration);
virtual int getTypeId() const;
virtual unsigned int getPriority() const;
virtual bool canCancel() const { return false; }
virtual bool shouldCancelPreviousAi() const { return false; }
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdBreathe; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
};
}
#endif

@ -10,17 +10,22 @@
#include "creaturestats.hpp"
#include "steering.hpp"
MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0)
namespace MWMechanics
{
ActionSpell action = ActionSpell(spellId);
bool isRanged;
mDistance = action.getCombatRange(isRanged);
namespace
{
float getInitialDistance(const std::string& spellId)
{
ActionSpell action = ActionSpell(spellId);
bool isRanged;
return action.getCombatRange(isRanged);
}
}
}
MWMechanics::AiPackage *MWMechanics::AiCast::clone() const
MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(getInitialDistance(spellId))
{
return new AiCast(*this);
}
bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration)
@ -84,13 +89,3 @@ MWWorld::Ptr MWMechanics::AiCast::getTarget() const
return target;
}
int MWMechanics::AiCast::getTypeId() const
{
return AiPackage::TypeIdCast;
}
unsigned int MWMechanics::AiCast::getPriority() const
{
return 3;
}

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AICAST_H
#define GAME_MWMECHANICS_AICAST_H
#include "aipackage.hpp"
#include "typedaipackage.hpp"
namespace MWWorld
{
@ -11,29 +11,31 @@ namespace MWWorld
namespace MWMechanics
{
/// AiPackage which makes an actor to cast given spell.
class AiCast : public AiPackage {
class AiCast final : public TypedAiPackage<AiCast> {
public:
AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false);
virtual AiPackage *clone() const;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration);
static constexpr TypeId getTypeId() { return TypeIdCast; }
virtual int getTypeId() const;
MWWorld::Ptr getTarget() const final;
virtual MWWorld::Ptr getTarget() const;
virtual unsigned int getPriority() const;
virtual bool canCancel() const { return false; }
virtual bool shouldCancelPreviousAi() const { return false; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mPriority = 3;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private:
std::string mTargetId;
std::string mSpellId;
const std::string mTargetId;
const std::string mSpellId;
bool mCasting;
bool mManual;
float mDistance;
const bool mManual;
const float mDistance;
};
}

@ -1,6 +1,7 @@
#include "aicombat.hpp"
#include <components/misc/rng.hpp>
#include <components/misc/coordinateconverter.hpp>
#include <components/esm/aisequence.hpp>
@ -21,7 +22,6 @@
#include "movement.hpp"
#include "character.hpp"
#include "aicombataction.hpp"
#include "coordinateconverter.hpp"
#include "actorutil.hpp"
namespace
@ -302,7 +302,7 @@ namespace MWMechanics
if (pathgrid && !actor.getClass().isPureWaterCreature(actor))
{
ESM::Pathgrid::PointList points;
CoordinateConverter coords(storage.mCell->getCell());
Misc::CoordinateConverter coords(storage.mCell->getCell());
osg::Vec3f localPos = actor.getRefData().getPosition().asVec3();
coords.toLocal(localPos);
@ -406,26 +406,11 @@ namespace MWMechanics
}
}
int AiCombat::getTypeId() const
{
return TypeIdCombat;
}
unsigned int AiCombat::getPriority() const
{
return 1;
}
MWWorld::Ptr AiCombat::getTarget() const
{
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
}
AiCombat *MWMechanics::AiCombat::clone() const
{
return new AiCombat(*this);
}
void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::unique_ptr<ESM::AiSequence::AiCombat> combat(new ESM::AiSequence::AiCombat());

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AICOMBAT_H
#define GAME_MWMECHANICS_AICOMBAT_H
#include "aipackage.hpp"
#include "typedaipackage.hpp"
#include "../mwworld/cellstore.hpp" // for Doors
@ -91,7 +91,7 @@ namespace MWMechanics
};
/// \brief Causes the actor to fight another actor
class AiCombat : public AiPackage
class AiCombat final : public TypedAiPackage<AiCombat>
{
public:
///Constructor
@ -102,21 +102,23 @@ namespace MWMechanics
void init();
virtual AiCombat *clone() const;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration);
static constexpr TypeId getTypeId() { return TypeIdCombat; }
virtual int getTypeId() const;
virtual unsigned int getPriority() const;
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mPriority = 1;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
///Returns target ID
MWWorld::Ptr getTarget() const;
virtual void writeState(ESM::AiSequence::AiSequence &sequence) const;
MWWorld::Ptr getTarget() const final;
virtual bool canCancel() const { return false; }
virtual bool shouldCancelPreviousAi() const { return false; }
void writeState(ESM::AiSequence::AiSequence &sequence) const final;
private:
/// Returns true if combat should end

@ -14,7 +14,6 @@
#include "../mwworld/cellstore.hpp"
#include "npcstats.hpp"
#include "spellcasting.hpp"
#include "combat.hpp"
#include "weaponpriority.hpp"
#include "spellpriority.hpp"

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save