1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-19 23:53:52 +00:00

Merge branch 'master' into 'near_far_mode_in_launcher'

# Conflicts:
#   files/settings-default.cfg
This commit is contained in:
psi29a 2020-06-16 23:05:07 +00:00
commit 66b5cf9f1d
363 changed files with 12324 additions and 7989 deletions

1
.gitignore vendored
View file

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

View file

@ -2,14 +2,34 @@
------ ------
Bug #1952: Incorrect particle lighting 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 #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 #5358: ForceGreeting always resets the dialogue window completely
Bug #5363: Enchantment autocalc not always 0/1 Bug #5363: Enchantment autocalc not always 0/1
Bug #5364: Script fails/stops if trying to startscript an unknown script 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 #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 #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures
Bug #5370: Opening an unlocked but trapped door uses the key 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 #5362: Show the soul gems' trapped soul in count dialog
Feature #5445: Handle NiLines
0.46.0 0.46.0
------ ------
@ -182,8 +202,8 @@
Bug #5158: Objects without a name don't fallback to their ID Bug #5158: Objects without a name don't fallback to their ID
Bug #5159: NiMaterialColorController can only control the diffuse color Bug #5159: NiMaterialColorController can only control the diffuse color
Bug #5161: Creature companions can't be activated when they are knocked down 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 #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 #5166: Scripts still should be executed after player's death
Bug #5167: Player can select and cast spells before magic menu is enabled 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 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 #5264: "Damage Fatigue" Magic Effect Can Bring Fatigue below 0
Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted 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 #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 #5308: World map copying makes save loading much slower
Bug #5313: Node properties of identical type are not applied in the correct order Bug #5313: Node properties of identical type are not applied in the correct order
Bug #5326: Formatting issues in the settings.cfg Bug #5326: Formatting issues in the settings.cfg
@ -282,7 +301,7 @@
Feature #5147: Show spell magicka cost in spell buying window Feature #5147: Show spell magicka cost in spell buying window
Feature #5170: Editor: Land shape editing, land selection Feature #5170: Editor: Land shape editing, land selection
Feature #5172: Editor: Delete instances/references with keypress in scene window 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 #5201: Editor: Show tool outline in scene view, when using editmodes
Feature #5219: Impelement TestCells console command Feature #5219: Impelement TestCells console command
Feature #5224: Handle NiKeyframeController for NiTriShape Feature #5224: Handle NiKeyframeController for NiTriShape

View file

@ -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. 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: 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 - 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 - On macOS, launching OpenMW from OpenMW-CS requires OpenMW.app and OpenMW-CS.app to be siblings
New Features: New Features:
- ? - Dialogue to split item stacks now displays the name of the trapped soul for stacks of soul gems (#5362)
New Editor Features: New Editor Features:
- ? - ?
Bug Fixes: 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: Editor Bug Fixes:
- ? - Verifier no longer checks for alleged 'race' entries in clothing body parts (#5400)
Miscellaneous: 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)

28
CI/ActivateMSVC.ps1 Normal file
View file

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

76
CI/activate_msvc.sh Normal file
View file

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

View file

@ -25,7 +25,6 @@ ${ANALYZE} cmake \
-DBUILD_ESSIMPORTER=${BUILD_OPENMW_CS} \ -DBUILD_ESSIMPORTER=${BUILD_OPENMW_CS} \
-DBUILD_WIZARD=${BUILD_OPENMW_CS} \ -DBUILD_WIZARD=${BUILD_OPENMW_CS} \
-DBUILD_NIFTEST=${BUILD_OPENMW_CS} \ -DBUILD_NIFTEST=${BUILD_OPENMW_CS} \
-DBUILD_MYGUI_PLUGIN=${BUILD_OPENMW_CS} \
-DBUILD_UNITTESTS=1 \ -DBUILD_UNITTESTS=1 \
-DUSE_SYSTEM_TINYXML=1 \ -DUSE_SYSTEM_TINYXML=1 \
-DDESIRED_QT_VERSION=5 \ -DDESIRED_QT_VERSION=5 \

View file

@ -1,25 +1,59 @@
#!/bin/bash #!/bin/bash
# set -x # turn-on for debugging # set -x # turn-on for debugging
function wrappedExit {
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
exit $1
else
return $1
fi
}
MISSINGTOOLS=0 MISSINGTOOLS=0
command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; } 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; } 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 if [ $MISSINGTOOLS -ne 0 ]; then
exit 1 wrappedExit 1
fi fi
WORKINGDIR="$(pwd)" WORKINGDIR="$(pwd)"
case "$WORKINGDIR" in case "$WORKINGDIR" in
*[[:space:]]*) *[[:space:]]*)
echo "Error: Working directory contains spaces." echo "Error: Working directory contains spaces."
exit 1 wrappedExit 1
;; ;;
esac esac
set -euo pipefail 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:-} APPVEYOR=${APPVEYOR:-}
CI=${CI:-} CI=${CI:-}
STEP=${STEP:-} STEP=${STEP:-}
@ -32,10 +66,19 @@ KEEP=""
UNITY_BUILD="" UNITY_BUILD=""
VS_VERSION="" VS_VERSION=""
NMAKE="" NMAKE=""
NINJA=""
PDBS=""
PLATFORM="" PLATFORM=""
CONFIGURATION="" CONFIGURATION=""
TEST_FRAMEWORK="" TEST_FRAMEWORK=""
GOOGLE_INSTALL_ROOT="" GOOGLE_INSTALL_ROOT=""
INSTALL_PREFIX="."
BULLET_DOUBLE=""
BULLET_DBL=""
BULLET_DBL_DISPLAY="Single precision"
ACTIVATE_MSVC=""
SINGLE_CONFIG=""
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
ARGSTR=$1 ARGSTR=$1
@ -44,7 +87,7 @@ while [ $# -gt 0 ]; do
if [ ${ARGSTR:0:1} != "-" ]; then if [ ${ARGSTR:0:1} != "-" ]; then
echo "Unknown argument $ARGSTR" echo "Unknown argument $ARGSTR"
echo "Try '$0 -h'" echo "Try '$0 -h'"
exit 1 wrappedExit 1
fi fi
for (( i=1; i<${#ARGSTR}; i++ )); do for (( i=1; i<${#ARGSTR}; i++ )); do
@ -56,6 +99,9 @@ while [ $# -gt 0 ]; do
d ) d )
SKIP_DOWNLOAD=true ;; SKIP_DOWNLOAD=true ;;
D )
BULLET_DOUBLE=true ;;
e ) e )
SKIP_EXTRACT=true ;; SKIP_EXTRACT=true ;;
@ -71,11 +117,17 @@ while [ $# -gt 0 ]; do
n ) n )
NMAKE=true ;; NMAKE=true ;;
N )
NINJA=true ;;
p ) p )
PLATFORM=$1 PLATFORM=$1
shift ;; shift ;;
P )
PDBS=true ;;
c ) c )
CONFIGURATION=$1 CONFIGURATION=$1
shift ;; shift ;;
@ -83,14 +135,20 @@ while [ $# -gt 0 ]; do
t ) t )
TEST_FRAMEWORK=true ;; TEST_FRAMEWORK=true ;;
i )
INSTALL_PREFIX=$(echo "$1" | sed 's;\\;/;g' | sed -E 's;/+;/;g')
shift ;;
h ) h )
cat <<EOF cat <<EOF
Usage: $0 [-cdehkpuvV] Usage: $0 [-cdehkpuvVi]
Options: Options:
-c <Release/Debug> -c <Release/Debug>
Set the configuration, can also be set with environment variable CONFIGURATION. Set the configuration, can also be set with environment variable CONFIGURATION.
-d -d
Skip checking the downloads. Skip checking the downloads.
-D
Use double-precision Bullet
-e -e
Skip extracting dependencies. Skip extracting dependencies.
-h -h
@ -106,23 +164,33 @@ Options:
-v <2013/2015/2017/2019> -v <2013/2015/2017/2019>
Choose the Visual Studio version to use. Choose the Visual Studio version to use.
-n -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 -V
Run verbosely Run verbosely
-i
CMake install prefix
EOF EOF
exit 0 wrappedExit 0
;; ;;
* ) * )
echo "Unknown argument $ARG." echo "Unknown argument $ARG."
echo "Try '$0 -h'" echo "Try '$0 -h'"
exit 1 ;; wrappedExit 1 ;;
esac esac
done done
done done
if [ -n "$NMAKE" ]; then if [ -n "$NMAKE" ] || [ -n "$NINJA" ]; 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
echo "Cannout run in NMake and Ninja mode at the same time."
wrappedExit 1
fi
ACTIVATE_MSVC=true
fi fi
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
@ -132,7 +200,7 @@ fi
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
echo "Running prebuild outside of Appveyor." echo "Running prebuild outside of Appveyor."
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,") DIR=$(windowsPathAsUnix "${BASH_SOURCE[0]}")
cd $(dirname "$DIR")/.. cd $(dirname "$DIR")/..
else else
echo "Running prebuild in Appveyor." echo "Running prebuild in Appveyor."
@ -145,8 +213,8 @@ run_cmd() {
shift shift
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
eval $CMD $@ > output.log 2>&1 RET=0
RET=$? eval $CMD $@ > output.log 2>&1 || RET=$?
if [ $RET -ne 0 ]; then if [ $RET -ne 0 ]; then
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
@ -162,8 +230,9 @@ run_cmd() {
return $RET return $RET
else else
eval $CMD $@ RET=0
return $? eval $CMD $@ || RET=$?
return $RET
fi fi
} }
@ -188,15 +257,16 @@ download() {
printf " Downloading $FILE... " printf " Downloading $FILE... "
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
curl --silent --retry 10 -kLy 5 -o $FILE $URL RET=0
RET=$? curl --silent --retry 10 -kLy 5 -o $FILE $URL || RET=$?
else else
curl --retry 10 -kLy 5 -o $FILE $URL RET=0
RET=$? curl --retry 10 -kLy 5 -o $FILE $URL || RET=$?
fi fi
if [ $RET -ne 0 ]; then if [ $RET -ne 0 ]; then
echo "Failed!" echo "Failed!"
wrappedExit $RET
else else
echo "Done." echo "Done."
fi fi
@ -257,6 +327,7 @@ case $VS_VERSION in
MSVC_REAL_VER="16" MSVC_REAL_VER="16"
MSVC_VER="14.2" MSVC_VER="14.2"
MSVC_YEAR="2015" MSVC_YEAR="2015"
MSVC_REAL_YEAR="2019"
MSVC_DISPLAY_YEAR="2019" MSVC_DISPLAY_YEAR="2019"
BOOST_VER="1.71.0" BOOST_VER="1.71.0"
BOOST_VER_URL="1_71_0" BOOST_VER_URL="1_71_0"
@ -269,6 +340,7 @@ case $VS_VERSION in
MSVC_REAL_VER="15" MSVC_REAL_VER="15"
MSVC_VER="14.1" MSVC_VER="14.1"
MSVC_YEAR="2015" MSVC_YEAR="2015"
MSVC_REAL_YEAR="2017"
MSVC_DISPLAY_YEAR="2017" MSVC_DISPLAY_YEAR="2017"
BOOST_VER="1.67.0" BOOST_VER="1.67.0"
BOOST_VER_URL="1_67_0" BOOST_VER_URL="1_67_0"
@ -281,6 +353,7 @@ case $VS_VERSION in
MSVC_REAL_VER="14" MSVC_REAL_VER="14"
MSVC_VER="14.0" MSVC_VER="14.0"
MSVC_YEAR="2015" MSVC_YEAR="2015"
MSVC_REAL_YEAR="2015"
MSVC_DISPLAY_YEAR="2015" MSVC_DISPLAY_YEAR="2015"
BOOST_VER="1.67.0" BOOST_VER="1.67.0"
BOOST_VER_URL="1_67_0" BOOST_VER_URL="1_67_0"
@ -288,15 +361,8 @@ case $VS_VERSION in
;; ;;
12|12.0|2013 ) 12|12.0|2013 )
GENERATOR="Visual Studio 12 2013" echo "Visual Studio 2013 is no longer supported"
TOOLSET="vc120" exit 1
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"
;; ;;
esac esac
@ -315,7 +381,7 @@ case $PLATFORM in
* ) * )
echo "Unknown platform $PLATFORM." echo "Unknown platform $PLATFORM."
exit 1 wrappedExit 1
;; ;;
esac esac
@ -342,9 +408,18 @@ fi
if [ -n "$NMAKE" ]; then if [ -n "$NMAKE" ]; then
GENERATOR="NMake Makefiles" GENERATOR="NMake Makefiles"
SINGLE_CONFIG=true
fi 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 if [ $BITS -eq 64 ]; then
add_cmake_opts "-G\"$GENERATOR\" -A x64" add_cmake_opts "-G\"$GENERATOR\" -A x64"
else else
@ -354,7 +429,7 @@ else
add_cmake_opts "-G\"$GENERATOR\"" add_cmake_opts "-G\"$GENERATOR\""
fi fi
if [ -n "$NMAKE" ]; then if [ -n "$SINGLE_CONFIG" ]; then
add_cmake_opts "-DCMAKE_BUILD_TYPE=${BUILD_CONFIG}" add_cmake_opts "-DCMAKE_BUILD_TYPE=${BUILD_CONFIG}"
fi fi
@ -362,6 +437,12 @@ if ! [ -z $UNITY_BUILD ]; then
add_cmake_opts "-DOPENMW_UNITY_BUILD=True" add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
fi 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 "===================================" echo "==================================="
echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}" echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}"
@ -386,45 +467,54 @@ if [ -z $SKIP_DOWNLOAD ]; then
fi fi
# Bullet # Bullet
download "Bullet 2.86" \ download "Bullet 2.89 (${BULLET_DBL_DISPLAY})" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" \ "https://rgw.ctrl-c.liu.se/openmw/Deps/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" \
"Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z"
# FFmpeg # FFmpeg
download "FFmpeg 3.2.4" \ download "FFmpeg 4.2.2" \
"https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-3.2.4-win${BITS}-shared.zip" \ "https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-4.2.2-win${BITS}-shared.zip" \
"ffmpeg-3.2.4-win${BITS}.zip" \ "ffmpeg-4.2.2-win${BITS}.zip" \
"https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-3.2.4-win${BITS}-dev.zip" \ "https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-4.2.2-win${BITS}-dev.zip" \
"ffmpeg-3.2.4-dev-win${BITS}.zip" "ffmpeg-4.2.2-dev-win${BITS}.zip"
# MyGUI # MyGUI
download "MyGUI 3.2.2" \ download "MyGUI 3.4.0" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" \ "https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
"MyGUI-3.2.2-msvc${MSVC_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 # OpenAL
download "OpenAL-Soft 1.19.1" \ download "OpenAL-Soft 1.20.1" \
"http://openal-soft.org/openal-binaries/openal-soft-1.19.1-bin.zip" \ "http://openal-soft.org/openal-binaries/openal-soft-1.20.1-bin.zip" \
"OpenAL-Soft-1.19.1.zip" "OpenAL-Soft-1.20.1.zip"
# OSG # OSG
download "OpenSceneGraph 3.4.1-scrawl" \ download "OpenSceneGraph 3.6.5" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \ "https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
"OSG-3.4.1-scrawl-msvc${MSVC_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 # Qt
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
if [ $BITS == "64" ]; then if [ "${MSVC_REAL_YEAR}" = "2015" ] && [ "${BITS}" = "32" ]; then
QT_SUFFIX="_64" echo "Qt no longer provides MSVC2015 Win32 packages, switch to 64-bit or a newer Visual Studio. Sorry."
else exit 1
QT_SUFFIX=""
fi fi
download "Qt 5.7.0" \ download "AQt installer" \
"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" \ "https://files.pythonhosted.org/packages/f3/bb/aee972f08deecca31bfc46b5aedfad1ce6c7f3aaf1288d685e4a914b53ac/aqtinstall-0.8-py2.py3-none-any.whl" \
"qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \ "aqtinstall-0.8-py2.py3-none-any.whl"
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
"qt-5-install.qs"
fi fi
# SDL2 # SDL2
@ -449,7 +539,13 @@ cd .. #/..
BUILD_DIR="MSVC${MSVC_DISPLAY_YEAR}_${BITS}" BUILD_DIR="MSVC${MSVC_DISPLAY_YEAR}_${BITS}"
if [ -n "$NMAKE" ]; then 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 fi
if [ -z $KEEP ]; then 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 # 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 # 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="$(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 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. "; printf "Cannot continue, ${CWD_DRIVE_ROOT_BASH} aka ${CWD_DRIVE_ROOT} already exists. Please remove before re-running. ";
exit 1; wrappedExit 1;
fi fi
if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION ${BOOST_VER_SDK}" Boost/boost/version.hpp > /dev/null; then if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION ${BOOST_VER_SDK}" Boost/boost/version.hpp > /dev/null; then
@ -526,15 +622,15 @@ fi
cd $DEPS cd $DEPS
echo echo
# Bullet # Bullet
printf "Bullet 2.86... " printf "Bullet 2.89 (${BULLET_DBL_DISPLAY})... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d Bullet ]; then if [ -d Bullet ]; then
printf -- "Exists. (No version checking) " printf -- "Exists. (No version checking) "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf Bullet rm -rf Bullet
eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP eval 7z x -y "${DEPS}/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" $STRIP
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet mv "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}" Bullet
fi fi
export BULLET_ROOT="$(real_pwd)/Bullet" export BULLET_ROOT="$(real_pwd)/Bullet"
echo Done. echo Done.
@ -542,21 +638,21 @@ printf "Bullet 2.86... "
cd $DEPS cd $DEPS
echo echo
# FFmpeg # FFmpeg
printf "FFmpeg 3.2.4... " printf "FFmpeg 4.2.2... "
{ {
cd $DEPS_INSTALL 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. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf FFmpeg rm -rf FFmpeg
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP eval 7z x -y "${DEPS}/ffmpeg-4.2.2-win${BITS}.zip" $STRIP
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP eval 7z x -y "${DEPS}/ffmpeg-4.2.2-dev-win${BITS}.zip" $STRIP
mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg mv "ffmpeg-4.2.2-win${BITS}-shared" FFmpeg
cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/ cp -r "ffmpeg-4.2.2-win${BITS}-dev/"* FFmpeg/
rm -rf "ffmpeg-3.2.4-win${BITS}-dev" rm -rf "ffmpeg-4.2.2-win${BITS}-dev"
fi fi
export FFMPEG_HOME="$(real_pwd)/FFmpeg" 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 if [ $BITS -eq 32 ]; then
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\"" add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
fi fi
@ -565,62 +661,66 @@ printf "FFmpeg 3.2.4... "
cd $DEPS cd $DEPS
echo echo
# MyGUI # MyGUI
printf "MyGUI 3.2.2... " printf "MyGUI 3.4.0... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d MyGUI ] && \ if [ -d MyGUI ] && \
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ 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_MINOR 4" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_PATCH 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null grep "MYGUI_VERSION_PATCH 0" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
then then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf MyGUI rm -rf MyGUI
eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP eval 7z x -y "${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI [ -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 fi
export MYGUI_HOME="$(real_pwd)/MyGUI" export MYGUI_HOME="$(real_pwd)/MyGUI"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="_d" SUFFIX="_d"
MYGUI_CONFIGURATION="Debug"
else else
SUFFIX="" SUFFIX=""
MYGUI_CONFIGURATION="RelWithDebInfo"
fi fi
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll" add_runtime_dlls "$(pwd)/MyGUI/bin/${MYGUI_CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# OpenAL # 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. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf openal-soft-1.19.1-bin rm -rf openal-soft-1.20.1-bin
eval 7z x -y OpenAL-Soft-1.19.1.zip $STRIP eval 7z x -y OpenAL-Soft-1.20.1.zip $STRIP
fi 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" \ add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib" -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. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# OSG # OSG
printf "OSG 3.4.1-scrawl... " printf "OSG 3.6.5... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d OSG ] && \ if [ -d OSG ] && \
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \ grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \ grep "OPENSCENEGRAPH_MINOR_VERSION 6" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_PATCH_VERSION 1" OSG/include/osg/Version > /dev/null grep "OPENSCENEGRAPH_PATCH_VERSION 5" OSG/include/osg/Version > /dev/null
then then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf OSG rm -rf OSG
eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP eval 7z x -y "${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG [ -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 fi
OSG_SDK="$(real_pwd)/OSG" OSG_SDK="$(real_pwd)/OSG"
add_cmake_opts -DOSG_DIR="$OSG_SDK" add_cmake_opts -DOSG_DIR="$OSG_SDK"
@ -629,17 +729,17 @@ printf "OSG 3.4.1-scrawl... "
else else
SUFFIX="" SUFFIX=""
fi 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 "$(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.6.5/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_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# Qt # Qt
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
printf "Qt 5.7.0... " printf "Qt 5.15.0... "
else else
printf "Qt 5.13 AppVeyor... " printf "Qt 5.13 AppVeyor... "
fi fi
@ -651,21 +751,49 @@ fi
fi fi
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
cd $DEPS_INSTALL cd $DEPS_INSTALL
QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}" QT_SDK="$(real_pwd)/Qt/5.15.0/msvc${MSVC_REAL_YEAR}${SUFFIX}"
if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then
if [ -d 'Qt/5.15.0' ]; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then 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 rm -rf Qt
cp "${DEPS}/qt-5-install.qs" qt-install.qs
sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs mkdir Qt
sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs cd Qt
printf -- "(Installation might take a while) "
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent eval "${DEPS}/aqt-venv/${VENV_BIN_DIR}/aqt" install 5.15.0 windows desktop "win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}" $STRIP
mv qt-install.qs Qt/
echo Done.
printf " Cleaning up extraneous data... " 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 fi
cd $QT_SDK cd $QT_SDK
add_cmake_opts -DDESIRED_QT_VERSION=5 \ add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
@ -688,7 +816,7 @@ fi
else else
SUFFIX="" SUFFIX=""
fi 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_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll" add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll"
echo Done. echo Done.
@ -759,10 +887,8 @@ echo
cd $DEPS_INSTALL/.. cd $DEPS_INSTALL/..
echo echo
echo "Setting up OpenMW build..." echo "Setting up OpenMW build..."
add_cmake_opts -DBUILD_BSATOOL=no \ add_cmake_opts -DOPENMW_MP_BUILD=on
-DBUILD_ESMTOOL=no \ add_cmake_opts -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}"
-DBUILD_MYGUI_PLUGIN=no \
-DOPENMW_MP_BUILD=on
if [ ! -z $CI ]; then if [ ! -z $CI ]; then
case $STEP in case $STEP in
components ) components )
@ -801,14 +927,15 @@ fi
#if [ -z $CI ]; then #if [ -z $CI ]; then
echo "- Copying Runtime DLLs..." echo "- Copying Runtime DLLs..."
DLL_PREFIX="" DLL_PREFIX=""
if [ -z $NMAKE ]; then if [ -z $SINGLE_CONFIG ]; then
mkdir -p $BUILD_CONFIG mkdir -p $BUILD_CONFIG
DLL_PREFIX="$BUILD_CONFIG/" DLL_PREFIX="$BUILD_CONFIG/"
fi fi
for DLL in $RUNTIME_DLLS; do for DLL in $RUNTIME_DLLS; do
TARGET="$(basename "$DLL")" TARGET="$(basename "$DLL")"
if [[ "$DLL" == *":"* ]]; then if [[ "$DLL" == *":"* ]]; then
IFS=':'; SPLIT=( ${DLL} ); unset IFS originalIFS="$IFS"
IFS=':'; SPLIT=( ${DLL} ); IFS=$originalIFS
DLL=${SPLIT[0]} DLL=${SPLIT[0]}
TARGET=${SPLIT[1]} TARGET=${SPLIT[1]}
fi fi
@ -817,10 +944,10 @@ fi
done done
echo echo
echo "- OSG Plugin DLLs..." 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 for DLL in $OSG_PLUGINS; do
echo " $(basename $DLL)." echo " $(basename $DLL)."
cp "$DLL" ${DLL_PREFIX}osgPlugins-3.4.1 cp "$DLL" ${DLL_PREFIX}osgPlugins-3.6.5
done done
echo echo
echo "- Qt Platform DLLs..." echo "- Qt Platform DLLs..."
@ -831,13 +958,49 @@ fi
done done
echo echo
#fi #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 if [ -z $VERBOSE ]; then
printf -- "- Configuring... " printf -- "- Configuring... "
else else
echo "- cmake .. $CMAKE_OPTS" echo "- cmake .. $CMAKE_OPTS"
fi fi
run_cmd cmake .. $CMAKE_OPTS RET=0
RET=$? run_cmd cmake .. $CMAKE_OPTS || RET=$?
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
if [ $RET -eq 0 ]; then if [ $RET -eq 0 ]; then
echo Done. echo Done.
@ -845,4 +1008,38 @@ if [ -z $VERBOSE ]; then
echo Failed. echo Failed.
fi fi
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

View file

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

View file

@ -1,6 +1,6 @@
#!/bin/sh -e #!/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 cd googletest
mkdir build mkdir build
cd build cd build

View file

@ -8,10 +8,10 @@ option(BUILD_ESSIMPORTER "Build ESS (Morrowind save game) importer" ON)
option(BUILD_BSATOOL "Build BSA extractor" ON) option(BUILD_BSATOOL "Build BSA extractor" ON)
option(BUILD_ESMTOOL "Build ESM inspector" ON) option(BUILD_ESMTOOL "Build ESM inspector" ON)
option(BUILD_NIFTEST "Build nif file tester" 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_DOCS "Build documentation." OFF )
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" 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) if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
set(USE_QT FALSE) set(USE_QT FALSE)
@ -468,9 +468,6 @@ IF(NOT WIN32 AND NOT APPLE)
IF(BUILD_WIZARD) IF(BUILD_WIZARD)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-wizard" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-wizard" DESTINATION "${BINDIR}" )
ENDIF(BUILD_WIZARD) 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 licenses
INSTALL(FILES "files/mygui/DejaVu Font License.txt" DESTINATION "${LICDIR}" ) 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}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) 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) IF(DESIRED_QT_VERSION MATCHES 5)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
@ -913,4 +905,3 @@ if (DOXYGEN_FOUND)
WORKING_DIRECTORY ${OpenMW_BINARY_DIR} WORKING_DIRECTORY ${OpenMW_BINARY_DIR}
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM) COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
endif () endif ()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,21 +9,20 @@ namespace ESSImport
void convertInventory(const Inventory &inventory, ESM::InventoryState &state) void convertInventory(const Inventory &inventory, ESM::InventoryState &state)
{ {
int index = 0; int index = 0;
for (std::vector<Inventory::InventoryItem>::const_iterator it = inventory.mItems.begin(); for (const auto & item : inventory.mItems)
it != inventory.mItems.end(); ++it)
{ {
ESM::ObjectState objstate; ESM::ObjectState objstate;
objstate.blank(); objstate.blank();
objstate.mRef = *it; objstate.mRef = item;
objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId); objstate.mRef.mRefID = Misc::StringUtils::lowerCase(item.mId);
objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile 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 // openmw handles them differently, so no need to set any flags
state.mItems.push_back(objstate); 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 // 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 // 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) // 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; ++index;
} }
} }

View file

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

View file

@ -1,18 +1,16 @@
#include "convertscri.hpp" #include "convertscri.hpp"
#include <iostream>
namespace namespace
{ {
template <typename T, ESM::VarType VariantType> template <typename T, ESM::VarType VariantType>
void storeVariables(const std::vector<T>& variables, ESM::Locals& locals, const std::string& scriptname) 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); val.setType(VariantType);
locals.mVariables.push_back(std::make_pair(std::string(), val)); locals.mVariables.emplace_back(std::string(), val);
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -21,11 +21,12 @@ add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation 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 add_openmw_dir (mwinput
inputmanagerimp actions actionmanager bindingsmanager controllermanager controlswitch
inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager
) )
add_openmw_dir (mwgui add_openmw_dir (mwgui
@ -40,7 +41,7 @@ add_openmw_dir (mwgui
itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours
) )
add_openmw_dir (mwdialogue add_openmw_dir (mwdialogue
@ -66,7 +67,7 @@ add_openmw_dir (mwworld
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager
cellpreloader cellpreloader datetimemanager
) )
add_openmw_dir (mwphysics add_openmw_dir (mwphysics
@ -83,9 +84,10 @@ add_openmw_dir (mwclass
add_openmw_dir (mwmechanics add_openmw_dir (mwmechanics
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe 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 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 add_openmw_dir (mwstate

View file

@ -1,6 +1,7 @@
#include "engine.hpp" #include "engine.hpp"
#include <iomanip> #include <iomanip>
#include <fstream>
#include <boost/filesystem/fstream.hpp> #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. // 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), // 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) // 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(); mEnvironment.getSoundManager()->pausePlayback();
return false; return false;
@ -180,7 +181,7 @@ bool OMW::Engine::frame(float frametime)
osg::Timer_t afterWorldTick = osg::Timer::instance()->tick(); osg::Timer_t afterWorldTick = osg::Timer::instance()->tick();
// update GUI // update GUI
mEnvironment.getWindowManager()->onFrame(frametime); mEnvironment.getWindowManager()->update(frametime);
unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
osg::Stats* stats = mViewer->getViewerStats(); osg::Stats* stats = mViewer->getViewerStats();
@ -202,12 +203,14 @@ bool OMW::Engine::frame(float frametime)
if (stats->collectStats("resource")) if (stats->collectStats("resource"))
{ {
stats->setAttribute(frameNumber, "FrameNumber", frameNumber);
mResourceSystem->reportStats(frameNumber, stats); mResourceSystem->reportStats(frameNumber, stats);
stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems()); stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems());
stats->setAttribute(frameNumber, "WorkThread", mWorkQueue->getNumActiveThreads()); 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 else
gameControllerdb = ""; //if it doesn't exist, pass in an empty string 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(); std::string myguiResources = (mResDir / "mygui").string();
osg::ref_ptr<osg::Group> guiRoot = new osg::Group; osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
guiRoot->setName("GUI Root"); guiRoot->setName("GUI Root");
guiRoot->setNodeMask(MWRender::Mask_GUI); guiRoot->setNodeMask(MWRender::Mask_GUI);
rootNode->addChild(guiRoot); 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, mCfgMgr.getLogPath().string() + std::string("/"), myguiResources,
mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts,
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string()); Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
mEnvironment.setWindowManager (window); mEnvironment.setWindowManager (window);
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
mEnvironment.setInputManager (input);
// Create sound system // Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound)); mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
@ -561,7 +564,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName, mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string())); mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
mEnvironment.getWorld()->setupPlayer(); mEnvironment.getWorld()->setupPlayer();
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
window->setStore(mEnvironment.getWorld()->getStore()); window->setStore(mEnvironment.getWorld()->getStore());
window->initUI(); window->initUI();
@ -658,7 +660,6 @@ private:
}; };
// Initialise and enter main loop. // Initialise and enter main loop.
void OMW::Engine::go() void OMW::Engine::go()
{ {
assert (!mContentFiles.empty()); assert (!mContentFiles.empty());
@ -687,7 +688,8 @@ void OMW::Engine::go()
mViewer->setUseConfigureAffinity(false); mViewer->setUseConfigureAffinity(false);
#endif #endif
mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(), mScreenCaptureOperation = new WriteScreenshotToFileOperation(
mCfgMgr.getScreenshotPath().string(),
Settings::Manager::getString("screenshot format", "General")); Settings::Manager::getString("screenshot format", "General"));
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation); mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);
@ -739,6 +741,14 @@ void OMW::Engine::go()
mEnvironment.getWindowManager()->executeInConsole(mStartupScript); 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 // Start the main rendering loop
osg::Timer frameTimer; osg::Timer frameTimer;
double simulationTime = 0.0; double simulationTime = 0.0;
@ -769,6 +779,12 @@ void OMW::Engine::go()
simulationTime += dt; simulationTime += dt;
} }
if (stats)
{
const auto frameNumber = mViewer->getFrameStamp()->getFrameNumber();
mViewer->getViewerStats()->report(stats, frameNumber);
}
mEnvironment.limitFrameRate(frameTimer.time_s()); mEnvironment.limitFrameRate(frameTimer.time_s());
} }

View file

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

View file

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

View file

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

View file

@ -7,10 +7,14 @@
#include <set> #include <set>
#include <stdint.h> #include <stdint.h>
#include "../mwmechanics/actorutil.hpp"
// For MWMechanics::GreetingState
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
namespace osg namespace osg
{ {
class Stats;
class Vec3f; class Vec3f;
} }
@ -269,6 +273,15 @@ namespace MWBase
virtual bool isAttackPreparing(const MWWorld::Ptr& ptr) = 0; virtual bool isAttackPreparing(const MWWorld::Ptr& ptr) = 0;
virtual bool isRunning(const MWWorld::Ptr& ptr) = 0; virtual bool isRunning(const MWWorld::Ptr& ptr) = 0;
virtual bool isSneaking(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;
}; };
} }

View file

@ -35,7 +35,7 @@ namespace MWBase
virtual ~ScriptManager() {} 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) ///< Run the script with the given name (compile first, if not compiled yet)
virtual bool compile (const std::string& name) = 0; virtual bool compile (const std::string& name) = 0;

View file

@ -11,6 +11,8 @@
#include "../mwgui/mode.hpp" #include "../mwgui/mode.hpp"
#include <components/sdlutil/events.hpp>
namespace Loading namespace Loading
{ {
class Listener; class Listener;
@ -86,7 +88,7 @@ namespace SFO
namespace MWBase namespace MWBase
{ {
/// \brief Interface for widnow manager (implemented in MWGui) /// \brief Interface for widnow manager (implemented in MWGui)
class WindowManager class WindowManager : public SDLUtil::WindowListener
{ {
WindowManager (const WindowManager&); WindowManager (const WindowManager&);
///< not implemented ///< not implemented
@ -249,13 +251,7 @@ namespace MWBase
/// returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) /// returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
virtual int readPressedButton() = 0; virtual int readPressedButton() = 0;
virtual void onFrame (float frameDuration) = 0; virtual void update (float duration) = 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;
/** /**
* Fetches a GMST string from the store, if there is no setting with the given * 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 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 executeInConsole (const std::string& path) = 0;
virtual void enableRest() = 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 injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) = 0;
virtual bool injectKeyRelease(MyGUI::KeyCode key) = 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;
}; };
} }

View file

@ -21,6 +21,7 @@ namespace osg
class Matrixf; class Matrixf;
class Quat; class Quat;
class Image; class Image;
class Stats;
} }
namespace Loading namespace Loading
@ -35,6 +36,7 @@ namespace ESM
struct Position; struct Position;
struct Cell; struct Cell;
struct Class; struct Class;
struct Creature;
struct Potion; struct Potion;
struct Spell; struct Spell;
struct NPC; struct NPC;
@ -46,6 +48,7 @@ namespace ESM
struct EffectList; struct EffectList;
struct CreatureLevList; struct CreatureLevList;
struct ItemLevList; struct ItemLevList;
struct TimeStamp;
} }
namespace MWRender namespace MWRender
@ -189,6 +192,8 @@ namespace MWBase
virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0; virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0;
///< Search is limited to the active cells. ///< 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; virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) = 0;
///< Return a pointer to a liveCellRef which contains \a ptr. ///< Return a pointer to a liveCellRef which contains \a ptr.
/// \note Search is limited to the active cells. /// \note Search is limited to the active cells.
@ -200,24 +205,14 @@ namespace MWBase
virtual void advanceTime (double hours, bool incremental = false) = 0; virtual void advanceTime (double hours, bool incremental = false) = 0;
///< Advance in-game time. ///< 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; virtual std::string getMonthName (int month = -1) const = 0;
///< Return name of month (-1: current month) ///< Return name of month (-1: current month)
virtual MWWorld::TimeStamp getTimeStamp() const = 0; 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; virtual bool toggleSky() = 0;
///< \return Resulting mode ///< \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. ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record /// \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 update (float duration, bool paused) = 0;
virtual void updatePhysics (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 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 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;
}; };
} }

View file

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

View file

@ -507,23 +507,16 @@ namespace MWClass
float Creature::getSpeed(const MWWorld::Ptr &ptr) const 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()) if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
return 0.f; return 0.f;
const GMST& gmst = getGmst(); 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 MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); 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; float moveSpeed;
if(getEncumbrance(ptr) > getCapacity(ptr)) if(getEncumbrance(ptr) > getCapacity(ptr))
@ -540,19 +533,9 @@ namespace MWClass
moveSpeed = flySpeed; moveSpeed = flySpeed;
} }
else if(world->isSwimming(ptr)) else if(world->isSwimming(ptr))
{ moveSpeed = getSwimSpeed(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;
else else
moveSpeed = walkSpeed; moveSpeed = getWalkSpeed(ptr);
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f; moveSpeed *= 0.75f;
@ -605,7 +588,7 @@ namespace MWClass
float Creature::getCapacity (const MWWorld::Ptr& ptr) const float Creature::getCapacity (const MWWorld::Ptr& ptr) const
{ {
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); 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 int Creature::getServices(const MWWorld::ConstPtr &actor) const
@ -745,7 +728,7 @@ namespace MWClass
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); 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 = MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>(); ptr.get<ESM::Creature>();
@ -809,6 +792,12 @@ namespace MWClass
return; return;
} }
if (ptr.getRefData().getCount() <= 0)
{
state.mHasCustomState = false;
return;
}
const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
ESM::CreatureState& creatureState = state.asCreatureState(); ESM::CreatureState& creatureState = state.asCreatureState();
customData.mContainerStore->writeState (creatureState.mInventory); customData.mContainerStore->writeState (creatureState.mInventory);
@ -878,4 +867,36 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
scale *= ref->mBase->mScale; 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());
}
} }

View file

@ -108,7 +108,7 @@ namespace MWClass
virtual bool canSwim (const MWWorld::ConstPtr &ptr) const; virtual bool canSwim (const MWWorld::ConstPtr &ptr) const;
virtual bool canWalk (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) /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; 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; 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 /// @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;
}; };
} }

View file

@ -125,7 +125,7 @@ namespace MWClass
} }
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); 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 = static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();

View file

@ -127,8 +127,8 @@ namespace
} }
// initial health // initial health
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); float strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); float endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
int multiplier = 3; int multiplier = 3;
@ -947,16 +947,6 @@ namespace MWClass
bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(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; float moveSpeed;
if(getEncumbrance(ptr) > getCapacity(ptr)) if(getEncumbrance(ptr) > getCapacity(ptr))
moveSpeed = 0.0f; moveSpeed = 0.0f;
@ -971,19 +961,11 @@ namespace MWClass
moveSpeed = flySpeed; moveSpeed = flySpeed;
} }
else if (swimming) else if (swimming)
{ moveSpeed = getSwimSpeed(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 && !sneaking) else if (running && !sneaking)
moveSpeed = runSpeed; moveSpeed = getRunSpeed(ptr);
else else
moveSpeed = walkSpeed; moveSpeed = getWalkSpeed(ptr);
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f; moveSpeed *= 0.75f;
@ -1011,7 +993,7 @@ namespace MWClass
gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * gmst.fJumpEncumbranceMultiplier->mValue.getFloat() *
(1.0f - Npc::getNormalizedEncumbrance(ptr)); (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; float b = 0.0f;
if(a > 50.0f) if(a > 50.0f)
{ {
@ -1136,7 +1118,7 @@ namespace MWClass
float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
float fUnarmoredBase2 = store.find("fUnarmoredBase2")->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]; float ratings[MWWorld::InventoryStore::Slots];
for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) for(int i = 0;i < MWWorld::InventoryStore::Slots;i++)
@ -1283,7 +1265,7 @@ namespace MWClass
return MWWorld::Ptr(cell.insert(ref), &cell); 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(); return getNpcStats(ptr).getSkill(skill).getModified();
} }
@ -1327,6 +1309,12 @@ namespace MWClass
return; return;
} }
if (ptr.getRefData().getCount() <= 0)
{
state.mHasCustomState = false;
return;
}
const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();
ESM::NpcState& npcState = state.asNpcState(); ESM::NpcState& npcState = state.asNpcState();
customData.mInventoryStore.writeState (npcState.mInventory); customData.mInventoryStore.writeState (npcState.mInventory);
@ -1437,4 +1425,61 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->getFactionRank(); 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;
}
} }

View file

@ -129,7 +129,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; 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) /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;
@ -164,6 +164,14 @@ namespace MWClass
virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const; virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const;
virtual int getPrimaryFactionRank(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;
}; };
} }

View file

@ -105,23 +105,32 @@ namespace MWGui
mGenerateClassSpecializations[0] = 0; mGenerateClassSpecializations[0] = 0;
mGenerateClassSpecializations[1] = 0; mGenerateClassSpecializations[1] = 0;
mGenerateClassSpecializations[2] = 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) 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) mPlayerAttributes[static_cast<ESM::Attribute::AttributeID>(i)] = value;
mReviewDialog->setAttribute(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) void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value)
{ {
mPlayerSkillValues[parSkill] = value;
if (mReviewDialog) if (mReviewDialog)
mReviewDialog->setSkillValue(parSkill, value); mReviewDialog->setSkillValue(parSkill, value);
} }
@ -155,6 +165,9 @@ namespace MWGui
{ {
if (mReviewDialog) if (mReviewDialog)
mReviewDialog->configureSkills(major, minor); mReviewDialog->configureSkills(major, minor);
mPlayerMajorSkills = major;
mPlayerMinorSkills = minor;
} }
void CharacterCreation::onFrame(float duration) void CharacterCreation::onFrame(float duration)
@ -269,31 +282,21 @@ namespace MWGui
mReviewDialog->setClass(*playerClass); mReviewDialog->setClass(*playerClass);
mReviewDialog->setBirthSign(player.getBirthSign()); mReviewDialog->setBirthSign(player.getBirthSign());
{ MWWorld::Ptr playerPtr = MWMechanics::getPlayer();
MWWorld::Ptr playerPtr = MWMechanics::getPlayer(); const MWMechanics::CreatureStats& stats = playerPtr.getClass().getCreatureStats(playerPtr);
const MWMechanics::CreatureStats& stats = playerPtr.getClass().getCreatureStats(playerPtr);
mReviewDialog->setHealth ( stats.getHealth() );
mReviewDialog->setMagicka( stats.getMagicka() );
mReviewDialog->setFatigue( stats.getFatigue() );
}
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(); mReviewDialog->setAttribute(static_cast<ESM::Attribute::AttributeID> (attributePair.first), attributePair.second);
for (auto& attributePair : attributes)
{
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(); mReviewDialog->setSkillValue(static_cast<ESM::Skill::SkillEnum> (skillPair.first), skillPair.second);
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->configureSkills(mPlayerMajorSkills, mPlayerMinorSkills);
mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone);
mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack);

View file

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

View file

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

View file

@ -81,6 +81,12 @@ namespace MWGui
MWBase::Environment::get().getMechanicsManager()->rest(mDays * 24, true); MWBase::Environment::get().getMechanicsManager()->rest(mDays * 24, true);
MWBase::Environment::get().getWorld()->advanceTime(mDays * 24); 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; std::set<int> skills;
for (int day=0; day<mDays; ++day) for (int day=0; day<mDays; ++day)
{ {
@ -89,9 +95,9 @@ namespace MWGui
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill); MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill);
if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak) 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 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>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();

View file

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

View file

@ -16,6 +16,7 @@
#include <components/myguiplatform/myguitexture.hpp> #include <components/myguiplatform/myguitexture.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/resource/resourcesystem.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/statemanager.hpp" #include "../mwbase/statemanager.hpp"
@ -29,9 +30,9 @@
namespace MWGui namespace MWGui
{ {
LoadingScreen::LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer) LoadingScreen::LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer)
: WindowBase("openmw_loading_screen.layout") : WindowBase("openmw_loading_screen.layout")
, mVFS(vfs) , mResourceSystem(resourceSystem)
, mViewer(viewer) , mViewer(viewer)
, mTargetFrameRate(120.0) , mTargetFrameRate(120.0)
, mLastWallpaperChangeTime(0.0) , mLastWallpaperChangeTime(0.0)
@ -64,9 +65,9 @@ namespace MWGui
void LoadingScreen::findSplashScreens() 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/"; std::string pattern = "Splash/";
mVFS->normalizeFilename(pattern); mResourceSystem->getVFS()->normalizeFilename(pattern);
/* priority given to the left */ /* priority given to the left */
const std::array<std::string, 7> supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}}; 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() // 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); mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback);
if (const osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) {
mOldIcoMin = ico->getMinimumTimeAvailableForGLCompileAndDeletePerFrame();
mOldIcoMax = ico->getMaximumNumOfObjectsToCompilePerFrame();
}
mVisible = visible; mVisible = visible;
mLoadingBox->setVisible(mVisible); mLoadingBox->setVisible(mVisible);
setVisible(true); setVisible(true);
@ -215,6 +221,12 @@ namespace MWGui
//std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl;
setVisible(false); 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_Loading);
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper);
} }
@ -336,7 +348,13 @@ namespace MWGui
MWBase::Environment::get().getInputManager()->update(0, true, true); 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, // 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. // 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() // refer to the advance() and frame() order in Engine::go()
@ -344,10 +362,6 @@ namespace MWGui
mViewer->updateTraversal(); mViewer->updateTraversal();
mViewer->renderingTraversals(); mViewer->renderingTraversals();
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); 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(); mLastRenderTime = mTimer.time_m();
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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");
}
}

View file

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

View file

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

View file

@ -74,11 +74,11 @@ namespace MWGui
mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold));
// NPC can train you in his best 3 skills // 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) 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)); skills.push_back(std::make_pair(i, value));
} }

View file

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

View file

@ -443,6 +443,9 @@ namespace MWGui
// constant effects have no duration and no target // constant effects have no duration and no target
if (!mEffectParams.mIsConstant) 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)) 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); spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + MyGUI::utility::toString(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs);

View file

@ -15,12 +15,16 @@
#include <MyGUI_ClipboardManager.h> #include <MyGUI_ClipboardManager.h>
#include <MyGUI_WidgetManager.h> #include <MyGUI_WidgetManager.h>
// For BT_NO_PROFILE
#include <LinearMath/btQuickprof.h>
#include <SDL_keyboard.h> #include <SDL_keyboard.h>
#include <SDL_clipboard.h> #include <SDL_clipboard.h>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlcursormanager.hpp> #include <components/sdlutil/sdlcursormanager.hpp>
#include <components/sdlutil/sdlvideowrapper.hpp>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
@ -113,21 +117,10 @@
#include "keyboardnavigation.hpp" #include "keyboardnavigation.hpp"
#include "resourceskin.hpp" #include "resourceskin.hpp"
namespace
{
MyGUI::Colour getTextColour(const std::string& type)
{
return MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=" + type + "}"));
}
}
namespace MWGui namespace MWGui
{ {
WindowManager::WindowManager( 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, 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) ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& userDataPath)
: mOldUpdateMask(0) : mOldUpdateMask(0)
@ -178,12 +171,6 @@ namespace MWGui
, mCursorVisible(true) , mCursorVisible(true)
, mCursorActive(false) , mCursorActive(false)
, mPlayerBounty(-1) , mPlayerBounty(-1)
, mPlayerName()
, mPlayerRaceId()
, mPlayerAttributes()
, mPlayerMajorSkills()
, mPlayerMinorSkills()
, mPlayerSkillValues()
, mGui(nullptr) , mGui(nullptr)
, mGuiModes() , mGuiModes()
, mCursorManager(nullptr) , mCursorManager(nullptr)
@ -194,8 +181,8 @@ namespace MWGui
, mRestAllowed(true) , mRestAllowed(true)
, mShowOwned(0) , mShowOwned(0)
, mEncoding(encoding) , mEncoding(encoding)
, mFontHeight(16)
, mVersionDescription(versionDescription) , mVersionDescription(versionDescription)
, mWindowVisible(true)
{ {
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale); mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale);
@ -231,13 +218,6 @@ namespace MWGui
SpellView::registerComponents(); SpellView::registerComponents();
Gui::registerAllWidgets(); 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<MWGui::Controllers::ControllerFollowMouse>("Controller");
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer"); MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
@ -250,7 +230,7 @@ namespace MWGui
mKeyboardNavigation->setEnabled(keyboardNav); mKeyboardNavigation->setEnabled(keyboardNav);
Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav); Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav);
mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer); mLoadingScreen = new LoadingScreen(mResourceSystem, mViewer);
mWindows.push_back(mLoadingScreen); mWindows.push_back(mLoadingScreen);
//set up the hardware cursor manager //set up the hardware cursor manager
@ -288,94 +268,10 @@ namespace MWGui
MyGUI::ClipboardManager::getInstance().eventClipboardRequested += MyGUI::newDelegate(this, &WindowManager::onClipboardRequested); MyGUI::ClipboardManager::getInstance().eventClipboardRequested += MyGUI::newDelegate(this, &WindowManager::onClipboardRequested);
mShowOwned = Settings::Manager::getInt("show owned", "Game"); mShowOwned = Settings::Manager::getInt("show owned", "Game");
}
void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer);
{ mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"),
MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator(); Settings::Manager::getFloat("contrast", "Video"));
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);
}
} }
void WindowManager::loadUserFonts() void WindowManager::loadUserFonts()
@ -389,26 +285,7 @@ namespace MWGui
int w = MyGUI::RenderManager::getInstance().getViewSize().width; int w = MyGUI::RenderManager::getInstance().getViewSize().width;
int h = MyGUI::RenderManager::getInstance().getViewSize().height; int h = MyGUI::RenderManager::getInstance().getViewSize().height;
mTextColours.header = getTextColour("header"); mTextColours.loadColours();
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");
mDragAndDrop = new DragAndDrop(); mDragAndDrop = new DragAndDrop();
@ -585,17 +462,6 @@ namespace MWGui
mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem); 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(); updatePinnedWindows();
// Set up visibility // Set up visibility
@ -604,7 +470,7 @@ namespace MWGui
int WindowManager::getFontHeight() const int WindowManager::getFontHeight() const
{ {
return mFontHeight; return mFontLoader->getFontHeight();
} }
void WindowManager::setNewGame(bool newgame) void WindowManager::setNewGame(bool newgame)
@ -625,7 +491,6 @@ namespace MWGui
{ {
mKeyboardNavigation.reset(); mKeyboardNavigation.reset();
MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource");
MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); MyGUI::LanguageManager::getInstance().eventRequestTag.clear();
MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear();
MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear();
@ -653,6 +518,7 @@ namespace MWGui
mGuiPlatform->shutdown(); mGuiPlatform->shutdown();
delete mGuiPlatform; delete mGuiPlatform;
delete mVideoWrapper;
} }
catch(const MyGUI::Exception& e) catch(const MyGUI::Exception& e)
{ {
@ -787,40 +653,14 @@ namespace MWGui
{ {
mStatsWindow->setValue (id, value); mStatsWindow->setValue (id, value);
mCharGen->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) 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 /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we
/// allow custom skills. /// allow custom skills.
mStatsWindow->setValue(static_cast<ESM::Skill::SkillEnum> (parSkill), value); mStatsWindow->setValue(static_cast<ESM::Skill::SkillEnum> (parSkill), value);
mCharGen->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) 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) void WindowManager::setValue (const std::string& id, const std::string& value)
{ {
mStatsWindow->setValue (id, 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) void WindowManager::setValue (const std::string& id, int value)
@ -858,8 +694,6 @@ namespace MWGui
{ {
mStatsWindow->configureSkills (major, minor); mStatsWindow->configureSkills (major, minor);
mCharGen->configureSkills(major, minor); mCharGen->configureSkills(major, minor);
mPlayerMajorSkills = major;
mPlayerMinorSkills = minor;
} }
void WindowManager::updateSkillArea() void WindowManager::updateSkillArea()
@ -916,7 +750,7 @@ namespace MWGui
mMessageBoxManager->onFrame(dt); mMessageBoxManager->onFrame(dt);
MWBase::Environment::get().getInputManager()->update(dt, true, false); MWBase::Environment::get().getInputManager()->update(dt, true, false);
if (!MWBase::Environment::get().getInputManager()->isWindowVisible()) if (!mWindowVisible)
OpenThreads::Thread::microSleep(5000); OpenThreads::Thread::microSleep(5000);
else else
{ {
@ -995,7 +829,7 @@ namespace MWGui
mHud->setPlayerPos(x, y, u, v); mHud->setPlayerPos(x, y, u, v);
} }
void WindowManager::onFrame (float frameDuration) void WindowManager::update (float frameDuration)
{ {
bool gameRunning = MWBase::Environment::get().getStateManager()->getState()!= bool gameRunning = MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame; MWBase::StateManager::State_NoGame;
@ -1241,6 +1075,7 @@ namespace MWGui
{ {
mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI"));
bool changeRes = false;
for (const auto& setting : changed) for (const auto& setting : changed)
{ {
if (setting.first == "HUD" && setting.second == "crosshair") if (setting.first == "HUD" && setting.second == "crosshair")
@ -1249,11 +1084,38 @@ namespace MWGui
mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI");
else if (setting.first == "GUI" && setting.second == "menu transparency") else if (setting.first == "GUI" && setting.second == "menu transparency")
setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); 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) 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); mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y);
// scaled size // scaled size
@ -1283,9 +1145,27 @@ namespace MWGui
for (WindowBase* window : mWindows) for (WindowBase* window : mWindows)
window->onResChange(x, y); 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 // 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) void WindowManager::onCursorChange(const std::string &name)
{ {
mCursorManager->cursorChanged(name); mCursorManager->cursorChanged(name);
@ -1583,26 +1463,6 @@ namespace MWGui
return mGuiModes.back(); 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() void WindowManager::disallowMouse()
{ {
mInputBlocker->setVisible (true); mInputBlocker->setVisible (true);
@ -1925,7 +1785,7 @@ namespace MWGui
MWBase::Environment::get().getInputManager()->update(dt, true, false); MWBase::Environment::get().getInputManager()->update(dt, true, false);
if (!MWBase::Environment::get().getInputManager()->isWindowVisible()) if (!mWindowVisible)
{ {
mVideoWidget->pause(); mVideoWidget->pause();
OpenThreads::Thread::microSleep(5000); OpenThreads::Thread::microSleep(5000);
@ -2149,7 +2009,9 @@ namespace MWGui
void WindowManager::toggleDebugWindow() void WindowManager::toggleDebugWindow()
{ {
#ifndef BT_NO_PROFILE
mDebugWindow->setVisible(!mDebugWindow->isVisible()); mDebugWindow->setVisible(!mDebugWindow->isVisible());
#endif
} }
void WindowManager::cycleSpell(bool next) void WindowManager::cycleSpell(bool next)

View file

@ -15,6 +15,7 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include <components/sdlutil/events.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
@ -70,6 +71,7 @@ namespace SceneUtil
namespace SDLUtil namespace SDLUtil
{ {
class SDLCursorManager; class SDLCursorManager;
class VideoWrapper;
} }
namespace osgMyGUI namespace osgMyGUI
@ -124,13 +126,14 @@ namespace MWGui
class JailScreen; class JailScreen;
class KeyboardNavigation; class KeyboardNavigation;
class WindowManager : public MWBase::WindowManager class WindowManager :
public MWBase::WindowManager
{ {
public: public:
typedef std::pair<std::string, int> Faction; typedef std::pair<std::string, int> Faction;
typedef std::vector<Faction> FactionList; 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, 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); ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& localPath);
virtual ~WindowManager(); 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 int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
virtual void onFrame (float frameDuration); virtual void update (float duration);
/// \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();
/** /**
* Fetches a GMST string from the store, if there is no setting with the given * 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 processChangedSettings(const Settings::CategorySettingVector& changed);
virtual void windowVisibilityChange(bool visible);
virtual void windowResized(int x, int y); virtual void windowResized(int x, int y);
virtual void windowClosed();
virtual bool isWindowVisible();
virtual void executeInConsole (const std::string& path); virtual void executeInConsole (const std::string& path);
@ -411,8 +411,6 @@ namespace MWGui
MWWorld::Ptr mSelectedEnchantItem; MWWorld::Ptr mSelectedEnchantItem;
MWWorld::Ptr mSelectedWeapon; MWWorld::Ptr mSelectedWeapon;
void loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version);
std::vector<WindowModal*> mCurrentModals; std::vector<WindowModal*> mCurrentModals;
// Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). // 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); 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 MyGUI::Gui *mGui; // Gui
struct GuiModeState struct GuiModeState
@ -525,14 +515,16 @@ namespace MWGui
ToUTF8::FromType mEncoding; ToUTF8::FromType mEncoding;
int mFontHeight;
std::string mVersionDescription; std::string mVersionDescription;
bool mWindowVisible;
MWGui::TextColours mTextColours; MWGui::TextColours mTextColours;
std::unique_ptr<KeyboardNavigation> mKeyboardNavigation; 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. * 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: * Supported syntax:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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;
}
}

View file

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

View file

@ -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;
}
}

View file

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

View file

@ -1,22 +1,18 @@
#ifndef MWINPUT_MWINPUTMANAGERIMP_H #ifndef MWINPUT_MWINPUTMANAGERIMP_H
#define MWINPUT_MWINPUTMANAGERIMP_H #define MWINPUT_MWINPUTMANAGERIMP_H
#include "../mwgui/mode.hpp"
#include <SDL_sensor.h>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osgViewer/ViewerEventHandlers> #include <osgViewer/ViewerEventHandlers>
#include <extern/oics/ICSChannelListener.h>
#include <extern/oics/ICSInputControlSystem.h>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/sdlutil/events.hpp> #include <components/sdlutil/events.hpp>
#include "../mwbase/inputmanager.hpp" #include "../mwbase/inputmanager.hpp"
#include "../mwgui/mode.hpp"
#include "actions.hpp"
namespace MWWorld namespace MWWorld
{ {
class Player; class Player;
@ -27,51 +23,27 @@ namespace MWBase
class WindowManager; class WindowManager;
} }
namespace ICS
{
class InputControlSystem;
}
namespace MyGUI
{
struct MouseButton;
}
namespace Files
{
struct ConfigurationManager;
}
namespace SDLUtil namespace SDLUtil
{ {
class InputWrapper; class InputWrapper;
class VideoWrapper;
}
namespace osgViewer
{
class Viewer;
class ScreenCaptureHandler;
} }
struct SDL_Window; struct SDL_Window;
namespace MWInput 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 : class InputManager : public MWBase::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
{ {
public: public:
InputManager( InputManager(
@ -85,20 +57,18 @@ namespace MWInput
virtual ~InputManager(); virtual ~InputManager();
virtual bool isWindowVisible();
/// Clear all savegame-specific data /// Clear all savegame-specific data
virtual void clear(); virtual void clear();
virtual void update(float dt, bool disableControls=false, bool disableEvents=false); 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 changeInputMode(bool guiMode);
virtual void processChangedSettings(const Settings::CategorySettingVector& changed); virtual void processChangedSettings(const Settings::CategorySettingVector& changed);
virtual void setDragDrop(bool dragDrop); 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 void toggleControlSwitch (const std::string& sw, bool value);
virtual bool getControlSwitch (const std::string& sw); virtual bool getControlSwitch (const std::string& sw);
@ -113,264 +83,42 @@ namespace MWInput
virtual void resetToDefaultKeyBindings(); virtual void resetToDefaultKeyBindings();
virtual void resetToDefaultControllerBindings(); virtual void resetToDefaultControllerBindings();
virtual bool joystickLastUsed() {return mJoystickLastUsed;} virtual void setJoystickLastUsed(bool enabled);
virtual bool joystickLastUsed();
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 int countSavedGameRecords() const; virtual int countSavedGameRecords() const;
virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress); virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress);
virtual void readRecord(ESM::ESMReader& reader, uint32_t type); virtual void readRecord(ESM::ESMReader& reader, uint32_t type);
private: virtual void resetIdleTime();
enum GyroscopeAxis
{
Unknown = 0,
X = 1,
Y = 2,
Z = 3,
Minus_X = -1,
Minus_Y = -2,
Minus_Z = -3
};
SDL_Window* mWindow; virtual void executeAction(int action);
bool mWindowVisible;
osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
bool mJoystickLastUsed; virtual bool controlsDisabled() { return mControlsDisabled; }
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;
float mOverencumberedMessageDelay;
float mGuiCursorX;
float mGuiCursorY;
int mMouseWheel;
float mGamepadZoom;
bool mUserFileExists;
bool mAlwaysRunActive;
bool mSneakToggles;
float mSneakToggleShortcutTimer;
bool mSneakGamepadShortcut;
bool mSneaking;
bool mAttemptJump;
std::map<std::string, bool> mControlSwitch;
float mInvUiScalingFactor;
float mGamepadCursorSpeed;
float mGyroXSpeed;
float mGyroYSpeed;
float mGyroUpdateTimer;
float mGyroHSensitivity;
float mGyroVSensitivity;
GyroscopeAxis mGyroHAxis;
GyroscopeAxis mGyroVAxis;
float mGyroInputThreshold;
private: private:
void convertMousePosForMyGUI(int& x, int& y); 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); 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 quickKey(int index);
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 showQuickKeysMenu(); void showQuickKeysMenu();
bool actionIsActive (int id);
void loadKeyDefaults(bool force = false); void loadKeyDefaults(bool force = false);
void loadControllerDefaults(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 SDLUtil::InputWrapper* mInputWrapper;
SDL_Sensor* mGyroscope;
private: bool mControlsDisabled;
enum Actions
{
// please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files
A_GameMenu, ControlSwitch* mControlSwitch;
A_Unused, ActionManager* mActionManager;
BindingsManager* mBindingsManager;
A_Screenshot, // Take a screenshot ControllerManager* mControllerManager;
KeyboardManager* mKeyboardManager;
A_Inventory, // Toggle inventory screen MouseManager* mMouseManager;
SensorManager* mSensorManager;
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 #endif

View file

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

View file

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

View file

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

View file

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

View file

@ -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;
}
}

View file

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

View file

@ -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();
}
}
}

View file

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

View file

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

View file

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

View file

@ -18,4 +18,44 @@ namespace MWMechanics
{ {
return mCharacterController.get(); 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;
}
} }

View file

@ -3,6 +3,8 @@
#include <memory> #include <memory>
#include "../mwmechanics/actorutil.hpp"
namespace MWRender namespace MWRender
{ {
class Animation; class Animation;
@ -27,8 +29,24 @@ namespace MWMechanics
CharacterController* getCharacterController(); 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: private:
std::unique_ptr<CharacterController> mCharacterController; std::unique_ptr<CharacterController> mCharacterController;
int mGreetingTimer{0};
float mTargetAngleRadians{0.f};
GreetingState mGreetingState{Greet_None};
bool mIsTurningToPlayer{false};
}; };
} }

View file

@ -40,6 +40,7 @@
#include "summoning.hpp" #include "summoning.hpp"
#include "combat.hpp" #include "combat.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
#include "tickableeffects.hpp"
namespace namespace
{ {
@ -111,11 +112,11 @@ void adjustCommandedActor (const MWWorld::Ptr& actor)
bool hasCommandPackage = false; bool hasCommandPackage = false;
std::list<MWMechanics::AiPackage*>::const_iterator it; auto it = stats.getAiSequence().begin();
for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) for (; it != stats.getAiSequence().end(); ++it)
{ {
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow &&
static_cast<MWMechanics::AiFollow*>(*it)->isCommanded()) static_cast<const MWMechanics::AiFollow*>(it->get())->isCommanded())
{ {
hasCommandPackage = true; hasCommandPackage = true;
break; break;
@ -131,7 +132,7 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); 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; health = 0.1f * endurance;
float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); 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 class SoulTrap : public MWMechanics::EffectSourceVisitor
{ {
MWWorld::Ptr mCreature; MWWorld::Ptr mCreature;
@ -418,7 +462,7 @@ namespace MWMechanics
if (world->isSwimming(actor) || (playerPos - actorPos).length2() >= 3000 * 3000) if (world->isSwimming(actor) || (playerPos - actorPos).length2() >= 3000 * 3000)
return; 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. // 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; const float delta = MWBase::Environment::get().getFrameDuration() * 6.f;
static const float fVoiceIdleOdds = world->getStore().get<ESM::GameSetting>().find("fVoiceIdleOdds")->mValue.getFloat(); 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); CreatureStats &stats = actor.getClass().getCreatureStats(actor);
MWMechanics::AiSequence& seq = stats.getAiSequence(); 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(); osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
float distance = (targetPos - actorPos).length(); float distance = (targetPos - actorPos).length();
if (distance < DECELERATE_DISTANCE) if (distance < DECELERATE_DISTANCE)
@ -446,7 +490,7 @@ namespace MWMechanics
actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor; 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()) if (!actor.getClass().isActor() || actor == getPlayer())
return; return;
@ -459,9 +503,9 @@ namespace MWMechanics
MWBase::Environment::get().getWorld()->isSwimming(actor) || MWBase::Environment::get().getWorld()->isSwimming(actor) ||
(packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1)) (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1))
{ {
stats.setTurningToPlayer(false); actorState.setTurningToPlayer(false);
stats.setGreetingTimer(0); actorState.setGreetingTimer(0);
stats.setGreetingState(Greet_None); actorState.setGreetingState(Greet_None);
return; return;
} }
@ -470,14 +514,14 @@ namespace MWMechanics
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
osg::Vec3f dir = playerPos - actorPos; osg::Vec3f dir = playerPos - actorPos;
if (stats.isTurningToPlayer()) if (actorState.isTurningToPlayer())
{ {
// Reduce the turning animation glitch by using a *HUGE* value of // Reduce the turning animation glitch by using a *HUGE* value of
// epsilon... TODO: a proper fix might be in either the physics or the // epsilon... TODO: a proper fix might be in either the physics or the
// animation subsystem // 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. // An original engine launches an endless idle2 when an actor greets player.
playAnimationGroup (actor, "idle2", 0, std::numeric_limits<int>::max(), false); 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); float helloDistance = static_cast<float>(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier);
int greetingTimer = stats.getGreetingTimer(); int greetingTimer = actorState.getGreetingTimer();
GreetingState greetingState = stats.getGreetingState(); GreetingState greetingState = actorState.getGreetingState();
if (greetingState == Greet_None) if (greetingState == Greet_None)
{ {
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance && if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
@ -515,7 +559,7 @@ namespace MWMechanics
greetingTimer++; greetingTimer++;
if (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor)) if (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor))
turnActorToFacePlayer(actor, dir); turnActorToFacePlayer(actor, actorState, dir);
if (greetingTimer >= GREETING_COOLDOWN) if (greetingTimer >= GREETING_COOLDOWN)
{ {
@ -531,20 +575,19 @@ namespace MWMechanics
greetingState = Greet_None; greetingState = Greet_None;
} }
stats.setGreetingTimer(greetingTimer); actorState.setGreetingTimer(greetingTimer);
stats.setGreetingState(greetingState); 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[1] = 0;
actor.getClass().getMovementSettings(actor).mPosition[0] = 0; actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
CreatureStats &stats = actor.getClass().getCreatureStats(actor); if (!actorState.isTurningToPlayer())
if (!stats.isTurningToPlayer())
{ {
stats.setAngleToPlayer(std::atan2(dir.x(), dir.y())); actorState.setAngleToPlayer(std::atan2(dir.x(), dir.y()));
stats.setTurningToPlayer(true); actorState.setTurningToPlayer(true);
} }
} }
@ -604,7 +647,7 @@ namespace MWMechanics
bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end(); 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 // 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) if (!aggressive && !isPlayerFollowerOrEscorter)
{ {
// Check that actor2 is in combat with actor1 // Check that actor2 is in combat with actor1
@ -673,7 +716,7 @@ namespace MWMechanics
return; return;
bool followerOrEscorter = false; 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 // The follow package must be first or have nothing but combat before it
if (package->sideWithTarget()) if (package->sideWithTarget())
@ -722,7 +765,7 @@ namespace MWMechanics
{ {
CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr); 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; float base = 1.f;
if (ptr == getPlayer()) if (ptr == getPlayer())
@ -773,6 +816,9 @@ namespace MWMechanics
if (visitor.mRemainingTime > 0) if (visitor.mRemainingTime > 0)
{ {
double timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); double timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
if(timeScale == 0.0)
timeScale = 1;
restoreHours = std::max(0.0, hours - visitor.mRemainingTime * timeScale / 3600.f); restoreHours = std::max(0.0, hours - visitor.mRemainingTime * timeScale / 3600.f);
} }
else if (visitor.mRemainingTime == -1) else if (visitor.mRemainingTime == -1)
@ -798,7 +844,7 @@ namespace MWMechanics
float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
float fEndFatigueMult = settings.find("fEndFatigueMult")->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); float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr);
if (normalizedEncumbrance > 1) if (normalizedEncumbrance > 1)
@ -825,7 +871,7 @@ namespace MWMechanics
return; return;
// Restore fatigue // 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>(); 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 fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat ();
static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
@ -939,20 +985,74 @@ namespace MWMechanics
if (creatureStats.needToRecalcDynamicStats()) if (creatureStats.needToRecalcDynamicStats())
calculateDynamicStats(ptr); calculateDynamicStats(ptr);
if (ptr == getPlayer())
{ {
Spells & spells = creatureStats.getSpells(); GetCorprusSpells getCorprusSpellsVisitor;
for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) creatureStats.getSpells().visitEffectSources(getCorprusSpellsVisitor);
{ creatureStats.getActiveSpells().visitEffectSources(getCorprusSpellsVisitor);
if (spells.getCorprusSpells().find(it->first) != spells.getCorprusSpells().end()) ptr.getClass().getInventoryStore(ptr).visitEffectSources(getCorprusSpellsVisitor);
{ std::vector<std::string> corprusSpells = getCorprusSpellsVisitor.mSpells;
if (MWBase::Environment::get().getWorld()->getTimeStamp() >= spells.getCorprusSpells().at(it->first).mNextWorsening) std::vector<std::string> corprusSpellsToRemove;
{
spells.worsenCorprus(it->first);
if (ptr == getPlayer()) for (auto it = creatureStats.getCorprusSpells().begin(); it != creatureStats.getCorprusSpells().end(); ++it)
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}"); {
} if(std::find(corprusSpells.begin(), corprusSpells.end(), it->first) == corprusSpells.end())
{
// 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;
} }
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);
} }
} }
@ -1235,11 +1335,6 @@ namespace MWMechanics
if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name())
inventoryStore.unequipItem(*heldIter, ptr); 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); heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
@ -1623,9 +1718,16 @@ namespace MWMechanics
player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); 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()) 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(); ctrl->updateContinuousVfx();
}
else else
{ {
bool cellChanged = world->hasCellChanged(); bool cellChanged = world->hasCellChanged();
@ -1694,7 +1796,7 @@ namespace MWMechanics
if (isConscious(iter->first)) if (isConscious(iter->first))
{ {
stats.getAiSequence().execute(iter->first, *ctrl, duration); stats.getAiSequence().execute(iter->first, *ctrl, duration);
updateGreetingState(iter->first, timerUpdateHello > 0); updateGreetingState(iter->first, *iter->second, timerUpdateHello > 0);
playIdleDialogue(iter->first); playIdleDialogue(iter->first);
updateMovementSpeed(iter->first); updateMovementSpeed(iter->first);
} }
@ -1738,7 +1840,7 @@ namespace MWMechanics
if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed()) if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed())
{ {
MWMechanics::AiSequence& seq = stats.getAiSequence(); MWMechanics::AiSequence& seq = stats.getAiSequence();
alwaysActive = !seq.isEmpty() && seq.getActivePackage()->alwaysActive(); alwaysActive = !seq.isEmpty() && seq.getActivePackage().alwaysActive();
} }
bool inRange = isPlayer || dist <= mActorsProcessingRange || 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) 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) 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 MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) 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()) if (iter->first.getClass().getCreatureStats(iter->first).isDead())
continue; 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 // 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 // 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()) if (package->sideWithTarget() && !package->getTarget().isEmpty())
{ {
@ -2188,9 +2296,9 @@ namespace MWMechanics
if (stats.isDead()) if (stats.isDead())
continue; 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 // 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) if (package->followTargetThroughDoors() && package->getTarget() == actor)
list.push_back(iteratedActor); list.push_back(iteratedActor);
@ -2253,11 +2361,11 @@ namespace MWMechanics
// 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 // 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) 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; break;
} }
else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander)
@ -2385,6 +2493,42 @@ namespace MWMechanics
return ctrl->isAttackingOrSpell(); 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() void Actors::fastForwardAi()
{ {
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())

View file

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

View file

@ -1,6 +1,16 @@
#ifndef OPENMW_MWMECHANICS_ACTORUTIL_H #ifndef OPENMW_MWMECHANICS_ACTORUTIL_H
#define 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 namespace MWWorld
{ {
class Ptr; class Ptr;
@ -8,9 +18,43 @@ namespace MWWorld
namespace MWMechanics namespace MWMechanics
{ {
enum GreetingState
{
Greet_None,
Greet_InProgress,
Greet_Done
};
MWWorld::Ptr getPlayer(); MWWorld::Ptr getPlayer();
bool isPlayerInCombat(); bool isPlayerInCombat();
bool canActorMoveByZAxis(const MWWorld::Ptr& actor); 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 #endif

View file

@ -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) 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 const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow
@ -49,11 +44,6 @@ namespace MWMechanics
return false; return false;
} }
int AiActivate::getTypeId() const
{
return TypeIdActivate;
}
void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const
{ {
std::unique_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate()); std::unique_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate());

View file

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

View file

@ -16,7 +16,7 @@
static const int MAX_DIRECTIONS = 4; static const int MAX_DIRECTIONS = 4;
MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr) 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; 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 bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const
{ {
return (actorPos - mLastPos).length2() < 10 * 10; return (actorPos - mLastPos).length2() < 10 * 10;

View file

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H #ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H
#define GAME_MWMECHANICS_AIAVOIDDOOR_H #define GAME_MWMECHANICS_AIAVOIDDOOR_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
#include <string> #include <string>
@ -16,26 +16,28 @@ namespace MWMechanics
/// \brief AiPackage to have an actor avoid an opening door /// \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 /** 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: public:
/// Avoid door until the door is fully open /// Avoid door until the door is fully open
AiAvoidDoor(const MWWorld::ConstPtr& doorPtr); 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; static constexpr Options makeDefaultOptions()
{
virtual unsigned int getPriority() const; AiPackage::Options options;
options.mPriority = 2;
virtual bool canCancel() const { return false; } options.mCanCancel = false;
virtual bool shouldCancelPreviousAi() const { return false; } options.mShouldCancelPreviousAi = false;
return options;
}
private: private:
float mDuration; float mDuration;
MWWorld::ConstPtr mDoorPtr; const MWWorld::ConstPtr mDoorPtr;
osg::Vec3f mLastPos; osg::Vec3f mLastPos;
int mDirection; int mDirection;

View file

@ -11,12 +11,6 @@
#include "movement.hpp" #include "movement.hpp"
#include "steering.hpp" #include "steering.hpp"
MWMechanics::AiBreathe::AiBreathe()
: AiPackage()
{
}
bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) 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(); 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; 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;
}

View file

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

View file

@ -10,17 +10,22 @@
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "steering.hpp" #include "steering.hpp"
MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell) namespace MWMechanics
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0)
{ {
ActionSpell action = ActionSpell(spellId); namespace
bool isRanged; {
mDistance = action.getCombatRange(isRanged); 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) 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; return target;
} }
int MWMechanics::AiCast::getTypeId() const
{
return AiPackage::TypeIdCast;
}
unsigned int MWMechanics::AiCast::getPriority() const
{
return 3;
}

View file

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

View file

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

View file

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

View file

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

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