diff --git a/.gitignore b/.gitignore index 8a73b6350..f91a5b2b7 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ cmake-build-* ## qt-creator CMakeLists.txt.user* .vs +.vscode ## resources data diff --git a/CHANGELOG.md b/CHANGELOG.md index 434b45fb3..85d65526d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,15 +6,22 @@ Bug #3676: NiParticleColorModifier isn't applied properly Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #5108: Savegame bloating due to inefficient fog textures format + Bug #5165: Active spells should use real time intead of timestamps Bug #5358: ForceGreeting always resets the dialogue window completely Bug #5363: Enchantment autocalc not always 0/1 Bug #5364: Script fails/stops if trying to startscript an unknown script Bug #5367: Selecting a spell on an enchanted item per hotkey always plays the equip sound Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures Bug #5370: Opening an unlocked but trapped door uses the key + Bug #5397: NPC greeting does not reset if you leave + reenter area Bug #5400: Editor: Verifier checks race of non-skin bodyparts Bug #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 Feature #5362: Show the soul gems' trapped soul in count dialog 0.46.0 @@ -188,8 +195,8 @@ Bug #5158: Objects without a name don't fallback to their ID Bug #5159: NiMaterialColorController can only control the diffuse color Bug #5161: Creature companions can't be activated when they are knocked down - Bug #5164: Faction owned items handling is incorrect Bug #5163: UserData is not copied during node cloning + Bug #5164: Faction owned items handling is incorrect Bug #5166: Scripts still should be executed after player's death Bug #5167: Player can select and cast spells before magic menu is enabled Bug #5168: Force1stPerson and Force3rdPerson commands are not really force view change diff --git a/CI/ActivateMSVC.ps1 b/CI/ActivateMSVC.ps1 new file mode 100644 index 000000000..ca78ef588 --- /dev/null +++ b/CI/ActivateMSVC.ps1 @@ -0,0 +1,26 @@ +& "${env:COMSPEC}" /c ActivateMSVC.bat "&&" set | ForEach-Object { + $name, $value = $_ -split '=', 2 + Set-Content env:\"$name" $value +} + +$MissingTools = $false +$tools = "cl", "link", "rc", "mt", "awooga" +$descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool", "A made up command" +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 +} \ No newline at end of file diff --git a/CI/activate_msvc.sh b/CI/activate_msvc.sh new file mode 100644 index 000000000..0764cd02f --- /dev/null +++ b/CI/activate_msvc.sh @@ -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" \ No newline at end of file diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 3a0b062e6..2f40aef9c 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -1,25 +1,50 @@ #!/bin/bash # set -x # turn-on for debugging +function wrappedExit { + if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + exit $1 + else + return $1 + fi +} + MISSINGTOOLS=0 command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; } command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; } +command -v python >/dev/null 2>&1 || { echo "Warning: Python is not on the path, automatic Qt installation impossible."; } if [ $MISSINGTOOLS -ne 0 ]; then - exit 1 + wrappedExit 1 fi WORKINGDIR="$(pwd)" case "$WORKINGDIR" in *[[:space:]]*) echo "Error: Working directory contains spaces." - exit 1 + wrappedExit 1 ;; esac set -euo pipefail +function windowsPathAsUnix { + if command -v cygpath >/dev/null 2>&1; then + cygpath -u $1 + else + echo "$1" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1," + fi +} + +function unixPathAsWindows { + if command -v cygpath >/dev/null 2>&1; then + cygpath -w $1 + else + echo "$1" | sed "s,^/\([^/]\)/,\\1:/," | sed "s,/,\\\\,g" + fi +} + APPVEYOR=${APPVEYOR:-} CI=${CI:-} STEP=${STEP:-} @@ -32,11 +57,19 @@ KEEP="" UNITY_BUILD="" VS_VERSION="" NMAKE="" +NINJA="" +PDBS="" PLATFORM="" CONFIGURATION="" TEST_FRAMEWORK="" GOOGLE_INSTALL_ROOT="" INSTALL_PREFIX="." +BULLET_DOUBLE="" +BULLET_DBL="" +BULLET_DBL_DISPLAY="Single precision" + +ACTIVATE_MSVC="" +SINGLE_CONFIG="" while [ $# -gt 0 ]; do ARGSTR=$1 @@ -45,7 +78,7 @@ while [ $# -gt 0 ]; do if [ ${ARGSTR:0:1} != "-" ]; then echo "Unknown argument $ARGSTR" echo "Try '$0 -h'" - exit 1 + wrappedExit 1 fi for (( i=1; i<${#ARGSTR}; i++ )); do @@ -57,6 +90,9 @@ while [ $# -gt 0 ]; do d ) SKIP_DOWNLOAD=true ;; + D ) + BULLET_DOUBLE=true ;; + e ) SKIP_EXTRACT=true ;; @@ -72,11 +108,17 @@ while [ $# -gt 0 ]; do n ) NMAKE=true ;; + + N ) + NINJA=true ;; p ) PLATFORM=$1 shift ;; + P ) + PDBS=true ;; + c ) CONFIGURATION=$1 shift ;; @@ -96,6 +138,8 @@ Options: Set the configuration, can also be set with environment variable CONFIGURATION. -d Skip checking the downloads. + -D + Use double-precision Bullet -e Skip extracting dependencies. -h @@ -111,25 +155,33 @@ Options: -v <2013/2015/2017/2019> Choose the Visual Studio version to use. -n - Produce NMake makefiles instead of a Visual Studio solution. + Produce NMake makefiles instead of a Visual Studio solution. Cannout be used with -N. + -N + Produce Ninja (multi-config if CMake is new enough to support it) files instead of a Visual Studio solution. Cannot be used with -n.. + -P + Download debug symbols where available -V Run verbosely -i CMake install prefix EOF - exit 0 + wrappedExit 0 ;; * ) echo "Unknown argument $ARG." echo "Try '$0 -h'" - exit 1 ;; + wrappedExit 1 ;; esac done done -if [ -n "$NMAKE" ]; then - command -v nmake -? >/dev/null 2>&1 || { echo "Error: nmake (NMake) is not on the path. Make sure you have the necessary environment variables set for command-line C++ development (for example, by starting from a Developer Command Prompt)."; exit 1; } +if [ -n "$NMAKE" ] || [ -n "$NINJA" ]; then + if [ -n "$NMAKE" ] && [ -n "$NINJA" ]; then + echo "Cannout run in NMake and Ninja mode at the same time." + wrappedExit 1 + fi + ACTIVATE_MSVC=true fi if [ -z $VERBOSE ]; then @@ -139,7 +191,7 @@ fi if [ -z $APPVEYOR ]; then echo "Running prebuild outside of Appveyor." - DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,") + DIR=$(windowsPathAsUnix "${BASH_SOURCE[0]}") cd $(dirname "$DIR")/.. else echo "Running prebuild in Appveyor." @@ -264,6 +316,7 @@ case $VS_VERSION in MSVC_REAL_VER="16" MSVC_VER="14.2" MSVC_YEAR="2015" + MSVC_REAL_YEAR="2019" MSVC_DISPLAY_YEAR="2019" BOOST_VER="1.71.0" BOOST_VER_URL="1_71_0" @@ -276,6 +329,7 @@ case $VS_VERSION in MSVC_REAL_VER="15" MSVC_VER="14.1" MSVC_YEAR="2015" + MSVC_REAL_YEAR="2017" MSVC_DISPLAY_YEAR="2017" BOOST_VER="1.67.0" BOOST_VER_URL="1_67_0" @@ -288,6 +342,7 @@ case $VS_VERSION in MSVC_REAL_VER="14" MSVC_VER="14.0" MSVC_YEAR="2015" + MSVC_REAL_YEAR="2015" MSVC_DISPLAY_YEAR="2015" BOOST_VER="1.67.0" BOOST_VER_URL="1_67_0" @@ -295,15 +350,8 @@ case $VS_VERSION in ;; 12|12.0|2013 ) - GENERATOR="Visual Studio 12 2013" - TOOLSET="vc120" - MSVC_REAL_VER="12" - MSVC_VER="12.0" - MSVC_YEAR="2013" - MSVC_DISPLAY_YEAR="2013" - BOOST_VER="1.58.0" - BOOST_VER_URL="1_58_0" - BOOST_VER_SDK="105800" + echo "Visual Studio 2013 is no longer supported" + exit 1 ;; esac @@ -322,7 +370,7 @@ case $PLATFORM in * ) echo "Unknown platform $PLATFORM." - exit 1 + wrappedExit 1 ;; esac @@ -349,9 +397,18 @@ fi if [ -n "$NMAKE" ]; then GENERATOR="NMake Makefiles" + SINGLE_CONFIG=true fi -if [ $MSVC_REAL_VER -ge 16 ]; then +if [ -n "$NINJA" ]; then + GENERATOR="Ninja Multi-Config" + if ! cmake -E capabilities | grep -F "$GENERATOR" > /dev/null; then + SINGLE_CONFIG=true + GENERATOR="Ninja" + fi +fi + +if [ $MSVC_REAL_VER -ge 16 ] && [ -z "$NMAKE" ] && [ -z "$NINJA" ]; then if [ $BITS -eq 64 ]; then add_cmake_opts "-G\"$GENERATOR\" -A x64" else @@ -361,7 +418,7 @@ else add_cmake_opts "-G\"$GENERATOR\"" fi -if [ -n "$NMAKE" ]; then +if [ -n "$SINGLE_CONFIG" ]; then add_cmake_opts "-DCMAKE_BUILD_TYPE=${BUILD_CONFIG}" fi @@ -369,6 +426,12 @@ if ! [ -z $UNITY_BUILD ]; then add_cmake_opts "-DOPENMW_UNITY_BUILD=True" fi +if [ -n "$BULLET_DOUBLE" ]; then + BULLET_DBL="-double" + BULLET_DBL_DISPLAY="Double precision" + add_cmake_opts "-DBULLET_USE_DOUBLES=True" +fi + echo echo "===================================" echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}" @@ -393,45 +456,54 @@ if [ -z $SKIP_DOWNLOAD ]; then fi # Bullet - download "Bullet 2.87" \ - "https://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" \ - "Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" + download "Bullet 2.89 (${BULLET_DBL_DISPLAY})" \ + "https://rgw.ctrl-c.liu.se/openmw/Deps/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" \ + "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" # FFmpeg - download "FFmpeg 3.2.4" \ - "https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-3.2.4-win${BITS}-shared.zip" \ - "ffmpeg-3.2.4-win${BITS}.zip" \ - "https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-3.2.4-win${BITS}-dev.zip" \ - "ffmpeg-3.2.4-dev-win${BITS}.zip" + download "FFmpeg 4.2.2" \ + "https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-4.2.2-win${BITS}-shared.zip" \ + "ffmpeg-4.2.2-win${BITS}.zip" \ + "https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-4.2.2-win${BITS}-dev.zip" \ + "ffmpeg-4.2.2-dev-win${BITS}.zip" # MyGUI - download "MyGUI 3.2.2" \ - "https://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" \ - "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" + download "MyGUI 3.4.0" \ + "https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \ + "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" + + if [ -n "$PDBS" ]; then + download "MyGUI symbols" \ + "https://rgw.ctrl-c.liu.se/openmw/Deps/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \ + "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" + fi # OpenAL - download "OpenAL-Soft 1.19.1" \ - "http://openal-soft.org/openal-binaries/openal-soft-1.19.1-bin.zip" \ - "OpenAL-Soft-1.19.1.zip" + download "OpenAL-Soft 1.20.1" \ + "http://openal-soft.org/openal-binaries/openal-soft-1.20.1-bin.zip" \ + "OpenAL-Soft-1.20.1.zip" # OSG - download "OpenSceneGraph 3.4.1-scrawl" \ - "https://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \ - "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" + download "OpenSceneGraph 3.6.5" \ + "https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \ + "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" + + if [ -n "$PDBS" ]; then + download "OpenSceneGraph symbols" \ + "https://rgw.ctrl-c.liu.se/openmw/Deps/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \ + "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" + fi # Qt if [ -z $APPVEYOR ]; then - if [ $BITS == "64" ]; then - QT_SUFFIX="_64" - else - QT_SUFFIX="" + if [ "${MSVC_REAL_YEAR}" = "2015" ] && [ "${BITS}" = "32" ]; then + echo "Qt no longer provides MSVC2015 Win32 packages, switch to 64-bit or a newer Visual Studio. Sorry." + exit 1 fi - download "Qt 5.7.0" \ - "https://download.qt.io/new_archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \ - "qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \ - "https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \ - "qt-5-install.qs" + download "AQt installer" \ + "https://files.pythonhosted.org/packages/f3/bb/aee972f08deecca31bfc46b5aedfad1ce6c7f3aaf1288d685e4a914b53ac/aqtinstall-0.8-py2.py3-none-any.whl" \ + "aqtinstall-0.8-py2.py3-none-any.whl" fi # SDL2 @@ -456,7 +528,13 @@ cd .. #/.. BUILD_DIR="MSVC${MSVC_DISPLAY_YEAR}_${BITS}" if [ -n "$NMAKE" ]; then - BUILD_DIR="${BUILD_DIR}_NMake_${BUILD_CONFIG}" + BUILD_DIR="${BUILD_DIR}_NMake" +elif [ -n "$NINJA" ]; then + BUILD_DIR="${BUILD_DIR}_Ninja" +fi + +if [ -n "$SINGLE_CONFIG" ]; then + BUILD_DIR="${BUILD_DIR}_${BUILD_CONFIG}" fi if [ -z $KEEP ]; then @@ -494,10 +572,10 @@ fi # We work around this by installing to root of the current working drive and then move it to our deps # get the current working drive's root, we'll install to that temporarily CWD_DRIVE_ROOT="$(powershell -command '(get-location).Drive.Root')Boost_temp" - CWD_DRIVE_ROOT_BASH=$(echo "$CWD_DRIVE_ROOT" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,") + CWD_DRIVE_ROOT_BASH=$(windowsPathAsUnix "$CWD_DRIVE_ROOT") if [ -d CWD_DRIVE_ROOT_BASH ]; then printf "Cannot continue, ${CWD_DRIVE_ROOT_BASH} aka ${CWD_DRIVE_ROOT} already exists. Please remove before re-running. "; - exit 1; + wrappedExit 1; fi if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION ${BOOST_VER_SDK}" Boost/boost/version.hpp > /dev/null; then @@ -533,15 +611,15 @@ fi cd $DEPS echo # Bullet -printf "Bullet 2.87... " +printf "Bullet 2.89 (${BULLET_DBL_DISPLAY})... " { cd $DEPS_INSTALL if [ -d Bullet ]; then printf -- "Exists. (No version checking) " elif [ -z $SKIP_EXTRACT ]; then rm -rf Bullet - eval 7z x -y "${DEPS}/Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP - mv "Bullet-2.87-msvc${MSVC_YEAR}-win${BITS}" Bullet + eval 7z x -y "${DEPS}/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" $STRIP + mv "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}" Bullet fi export BULLET_ROOT="$(real_pwd)/Bullet" echo Done. @@ -549,21 +627,21 @@ printf "Bullet 2.87... " cd $DEPS echo # FFmpeg -printf "FFmpeg 3.2.4... " +printf "FFmpeg 4.2.2... " { cd $DEPS_INSTALL - if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then + if [ -d FFmpeg ] && grep "4.2.2" FFmpeg/README.txt > /dev/null; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then rm -rf FFmpeg - eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP - eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP - mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg - cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/ - rm -rf "ffmpeg-3.2.4-win${BITS}-dev" + eval 7z x -y "${DEPS}/ffmpeg-4.2.2-win${BITS}.zip" $STRIP + eval 7z x -y "${DEPS}/ffmpeg-4.2.2-dev-win${BITS}.zip" $STRIP + mv "ffmpeg-4.2.2-win${BITS}-shared" FFmpeg + cp -r "ffmpeg-4.2.2-win${BITS}-dev/"* FFmpeg/ + rm -rf "ffmpeg-4.2.2-win${BITS}-dev" fi export FFMPEG_HOME="$(real_pwd)/FFmpeg" - add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll + add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-58,avformat-58,avutil-56,swresample-3,swscale-5}.dll if [ $BITS -eq 32 ]; then add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\"" fi @@ -572,62 +650,66 @@ printf "FFmpeg 3.2.4... " cd $DEPS echo # MyGUI -printf "MyGUI 3.2.2... " +printf "MyGUI 3.4.0... " { cd $DEPS_INSTALL if [ -d MyGUI ] && \ grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ - grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ - grep "MYGUI_VERSION_PATCH 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null + grep "MYGUI_VERSION_MINOR 4" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ + grep "MYGUI_VERSION_PATCH 0" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then rm -rf MyGUI - eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP - mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI + eval 7z x -y "${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP + [ -n "$PDBS" ] && eval 7z x -y "${DEPS}/MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP + mv "MyGUI-3.4.0-msvc${MSVC_REAL_YEAR}-win${BITS}" MyGUI fi export MYGUI_HOME="$(real_pwd)/MyGUI" if [ $CONFIGURATION == "Debug" ]; then SUFFIX="_d" + MYGUI_CONFIGURATION="Debug" else SUFFIX="" + MYGUI_CONFIGURATION="RelWithDebInfo" fi - add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll" + add_runtime_dlls "$(pwd)/MyGUI/bin/${MYGUI_CONFIGURATION}/MyGUIEngine${SUFFIX}.dll" echo Done. } cd $DEPS echo # OpenAL -printf "OpenAL-Soft 1.19.1... " +printf "OpenAL-Soft 1.20.1... " { - if [ -d openal-soft-1.19.1-bin ]; then + if [ -d openal-soft-1.20.1-bin ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then - rm -rf openal-soft-1.19.1-bin - eval 7z x -y OpenAL-Soft-1.19.1.zip $STRIP + rm -rf openal-soft-1.20.1-bin + eval 7z x -y OpenAL-Soft-1.20.1.zip $STRIP fi - OPENAL_SDK="$(real_pwd)/openal-soft-1.19.1-bin" + OPENAL_SDK="$(real_pwd)/openal-soft-1.20.1-bin" add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \ -DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib" - add_runtime_dlls "$(pwd)/openal-soft-1.19.1-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll" + add_runtime_dlls "$(pwd)/openal-soft-1.20.1-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll" echo Done. } cd $DEPS echo # OSG -printf "OSG 3.4.1-scrawl... " +printf "OSG 3.6.5... " { cd $DEPS_INSTALL if [ -d OSG ] && \ grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \ - grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \ - grep "OPENSCENEGRAPH_PATCH_VERSION 1" OSG/include/osg/Version > /dev/null + grep "OPENSCENEGRAPH_MINOR_VERSION 6" OSG/include/osg/Version > /dev/null && \ + grep "OPENSCENEGRAPH_PATCH_VERSION 5" OSG/include/osg/Version > /dev/null then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then rm -rf OSG - eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP - mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG + eval 7z x -y "${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP + [ -n "$PDBS" ] && eval 7z x -y "${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP + mv "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}" OSG fi OSG_SDK="$(real_pwd)/OSG" add_cmake_opts -DOSG_DIR="$OSG_SDK" @@ -636,17 +718,17 @@ printf "OSG 3.4.1-scrawl... " else SUFFIX="" fi - add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \ + add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \ "$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll - add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll - add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll + add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll + add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll echo Done. } cd $DEPS echo # Qt if [ -z $APPVEYOR ]; then - printf "Qt 5.7.0... " + printf "Qt 5.15.0... " else printf "Qt 5.13 AppVeyor... " fi @@ -658,21 +740,44 @@ fi fi if [ -z $APPVEYOR ]; then cd $DEPS_INSTALL - QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}" - if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then + QT_SDK="$(real_pwd)/Qt/5.15.0/msvc${MSVC_REAL_YEAR}${SUFFIX}" + + if [ -d 'Qt/5.15.0' ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then + pushd "$DEPS" > /dev/null + if ! [ -d 'aqt-venv' ]; then + echo " Creating Virtualenv for aqt..." + eval python -m venv aqt-venv $STRIP + fi + if [ -d 'aqt-venv/bin' ]; then + VENV_BIN_DIR='bin' + elif [ -d 'aqt-venv/Scripts' ]; then + VENV_BIN_DIR='Scripts' + else + echo "Error: Failed to create virtualenv." + exit 1 + fi + + if ! [ -e "aqt-venv/${VENV_BIN_DIR}/aqt" ]; then + echo " Installing aqt wheel into virtualenv..." + eval "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall-0.8-py2.py3-none-any.whl $STRIP + fi + popd > /dev/null + rm -rf Qt - cp "${DEPS}/qt-5-install.qs" qt-install.qs - sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs - sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs - printf -- "(Installation might take a while) " - "${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent - mv qt-install.qs Qt/ - echo Done. + + mkdir Qt + cd Qt + + eval "${DEPS}/aqt-venv/${VENV_BIN_DIR}/aqt" install 5.15.0 windows desktop "win${BITS}_msvc${MSVC_REAL_YEAR}${SUFFIX}" $STRIP + printf " Cleaning up extraneous data... " - rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs} + rm -rf Qt/{aqtinstall.log,Tools} + + echo Done. fi + cd $QT_SDK add_cmake_opts -DDESIRED_QT_VERSION=5 \ -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ @@ -695,7 +800,7 @@ fi else SUFFIX="" fi - DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,") + DIR=$(windowsPathAsUnix "${QT_SDK}") add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll" echo Done. @@ -806,14 +911,15 @@ fi #if [ -z $CI ]; then echo "- Copying Runtime DLLs..." DLL_PREFIX="" - if [ -z $NMAKE ]; then + if [ -z $SINGLE_CONFIG ]; then mkdir -p $BUILD_CONFIG DLL_PREFIX="$BUILD_CONFIG/" fi for DLL in $RUNTIME_DLLS; do TARGET="$(basename "$DLL")" if [[ "$DLL" == *":"* ]]; then - IFS=':'; SPLIT=( ${DLL} ); unset IFS + originalIFS="$IFS" + IFS=':'; SPLIT=( ${DLL} ); IFS=$originalIFS DLL=${SPLIT[0]} TARGET=${SPLIT[1]} fi @@ -822,10 +928,10 @@ fi done echo echo "- OSG Plugin DLLs..." - mkdir -p ${DLL_PREFIX}osgPlugins-3.4.1 + mkdir -p ${DLL_PREFIX}osgPlugins-3.6.5 for DLL in $OSG_PLUGINS; do echo " $(basename $DLL)." - cp "$DLL" ${DLL_PREFIX}osgPlugins-3.4.1 + cp "$DLL" ${DLL_PREFIX}osgPlugins-3.6.5 done echo echo "- Qt Platform DLLs..." @@ -836,6 +942,42 @@ fi done echo #fi + +if ! [ -z $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 -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) + if [ $MSVC_REAL_VER -ge 15 ]; then + echo "@\"${MSVC_INSTALLATION_PATH}\Common7\Tools\VsDevCmd.bat\" -no_logo -arch=$([ $BITS -eq 64 ] && echo "amd64" || echo "x86") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo "amd64" || echo "x86")" > ActivateMSVC.bat + else + if [ $(uname -m) == 'x86_64' ]; then + if [ $BITS -eq 64 ]; then + compiler=amd64 + else + compiler=amd64_x86 + fi + else + if [ $BITS -eq 64 ]; then + compiler=x86_amd64 + else + compiler=x86 + fi + fi + echo "@\"${MSVC_INSTALLATION_PATH}\VC\vcvarsall.bat\" $compiler" > ActivateMSVC.bat + fi + + cp "../CI/activate_msvc.sh" . + sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" activate_msvc.sh + source ./activate_msvc.sh + + cp "../CI/ActivateMSVC.ps1" . + sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" ActivateMSVC.ps1 + + echo "done." + echo +fi + if [ -z $VERBOSE ]; then printf -- "- Configuring... " else @@ -846,8 +988,34 @@ RET=$? if [ -z $VERBOSE ]; then if [ $RET -eq 0 ]; then echo Done. + 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 else echo Failed. fi fi -exit $RET +wrappedExit $RET diff --git a/CMakeLists.txt b/CMakeLists.txt index b75ab8ee5..0dd39896d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ option(BUILD_NIFTEST "Build nif file tester" ON) option(BUILD_DOCS "Build documentation." OFF ) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF) +option(BULLET_USE_DOUBLES "Use double precision for Bullet" OFF) option(BUILD_OPENMW_MP "Build OpenMW-MP" ON) option(BUILD_BROWSER "Build tes3mp Server Browser" ON) option(BUILD_MASTER "Build tes3mp Master Server" OFF) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 4059c0938..5563f910d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -15,6 +15,10 @@ set(GAME_HEADER engine.hpp ) +if (BULLET_USE_DOUBLES) + add_definitions(-DBT_USE_DOUBLE_PRECISION) +endif() + source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender @@ -41,7 +45,7 @@ add_openmw_dir (mwgui itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview - draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation + draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 6e0917f89..d8552599a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -1,6 +1,7 @@ #include "engine.hpp" #include +#include #include @@ -270,7 +271,7 @@ bool OMW::Engine::frame(float frametime) osg::Timer_t afterWorldTick = osg::Timer::instance()->tick(); // update GUI - mEnvironment.getWindowManager()->onFrame(frametime); + mEnvironment.getWindowManager()->update(frametime); unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); osg::Stats* stats = mViewer->getViewerStats(); @@ -292,12 +293,14 @@ bool OMW::Engine::frame(float frametime) if (stats->collectStats("resource")) { + stats->setAttribute(frameNumber, "FrameNumber", frameNumber); + mResourceSystem->reportStats(frameNumber, stats); stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems()); stats->setAttribute(frameNumber, "WorkThread", mWorkQueue->getNumActiveThreads()); - mEnvironment.getWorld()->getNavigator()->reportStats(frameNumber, *stats); + mEnvironment.reportStats(frameNumber, *stats); } } @@ -891,6 +894,14 @@ void OMW::Engine::go() mEnvironment.getWindowManager()->executeInConsole(mStartupScript); } + std::ofstream stats; + if (const auto path = std::getenv("OPENMW_OSG_STATS_FILE")) + { + stats.open(path, std::ios_base::out); + if (!stats) + Log(Debug::Warning) << "Failed to open file for stats: " << path; + } + // Start the main rendering loop osg::Timer frameTimer; double simulationTime = 0.0; @@ -933,6 +944,12 @@ void OMW::Engine::go() simulationTime += dt; } + if (stats) + { + const auto frameNumber = mViewer->getFrameStamp()->getFrameNumber(); + mViewer->getViewerStats()->report(stats, frameNumber); + } + mEnvironment.limitFrameRate(frameTimer.time_s()); } diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 5d01525b9..c70debda1 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -198,3 +198,9 @@ const MWBase::Environment& MWBase::Environment::get() assert (sThis); return *sThis; } + +void MWBase::Environment::reportStats(unsigned int frameNumber, osg::Stats& stats) const +{ + mMechanicsManager->reportStats(frameNumber, stats); + mWorld->reportStats(frameNumber, stats); +} diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 9163b21f3..80e6a6243 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -1,6 +1,11 @@ #ifndef GAME_BASE_ENVIRONMENT_H #define GAME_BASE_ENVIRONMENT_H +namespace osg +{ + class Stats; +} + namespace MWBase { class World; @@ -97,6 +102,8 @@ namespace MWBase static const Environment& get(); ///< Return instance of this class. + + void reportStats(unsigned int frameNumber, osg::Stats& stats) const; }; } diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 932463e97..951b5053a 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -76,6 +76,8 @@ namespace MWBase virtual void resetIdleTime() = 0; virtual void executeAction(int action) = 0; + + virtual bool controlsDisabled() = 0; }; } diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 6759714d2..cc29878d6 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -7,10 +7,14 @@ #include #include +#include "../mwmechanics/actorutil.hpp" +// For MWMechanics::GreetingState + #include "../mwworld/ptr.hpp" namespace osg { + class Stats; class Vec3f; } @@ -300,6 +304,13 @@ namespace MWBase virtual bool isAttackPreparing(const MWWorld::Ptr& ptr) = 0; virtual bool isRunning(const MWWorld::Ptr& ptr) = 0; virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0; + + virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0; + + virtual int getGreetingTimer(const MWWorld::Ptr& ptr) const = 0; + virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const = 0; + virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0; + virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0; }; } diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c5438a4f8..04af7b0a1 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -327,13 +327,7 @@ namespace MWBase End of tes3mp addition */ - virtual void onFrame (float frameDuration) = 0; - - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - virtual std::map getPlayerSkillValues() = 0; - virtual std::map getPlayerAttributeValues() = 0; - virtual SkillList getPlayerMinorSkills() = 0; - virtual SkillList getPlayerMajorSkills() = 0; + virtual void update (float duration) = 0; /** * Fetches a GMST string from the store, if there is no setting with the given diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 11a49d2f5..b9928ddbb 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -21,6 +21,7 @@ namespace osg class Matrixf; class Quat; class Image; + class Stats; } namespace Loading @@ -831,6 +832,8 @@ namespace MWBase virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0; virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0; + + virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0; }; } diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index a1fa16afd..f54385a11 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -105,23 +105,32 @@ namespace MWGui mGenerateClassSpecializations[0] = 0; mGenerateClassSpecializations[1] = 0; mGenerateClassSpecializations[2] = 0; + + // Setup player stats + for (int i = 0; i < ESM::Attribute::Length; ++i) + mPlayerAttributes.emplace(ESM::Attribute::sAttributeIds[i], MWMechanics::AttributeValue()); + + for (int i = 0; i < ESM::Skill::Length; ++i) + mPlayerSkillValues.emplace(ESM::Skill::sSkillIds[i], MWMechanics::SkillValue()); } void CharacterCreation::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { - if (mReviewDialog) + static const char *ids[] = { - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8", - 0 - }; + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", + "AttribVal5", "AttribVal6", "AttribVal7", "AttribVal8", 0 + }; - for (int i=0; ids[i]; ++i) + for (int i=0; ids[i]; ++i) + { + if (ids[i]==id) { - if (ids[i]==id) - mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); + mPlayerAttributes[static_cast(i)] = value; + if (mReviewDialog) + mReviewDialog->setAttribute(static_cast(i), value); + + break; } } } @@ -147,6 +156,7 @@ namespace MWGui void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) { + mPlayerSkillValues[parSkill] = value; if (mReviewDialog) mReviewDialog->setSkillValue(parSkill, value); } @@ -155,6 +165,9 @@ namespace MWGui { if (mReviewDialog) mReviewDialog->configureSkills(major, minor); + + mPlayerMajorSkills = major; + mPlayerMinorSkills = minor; } void CharacterCreation::onFrame(float duration) @@ -269,31 +282,21 @@ namespace MWGui mReviewDialog->setClass(*playerClass); mReviewDialog->setBirthSign(player.getBirthSign()); - { - MWWorld::Ptr playerPtr = MWMechanics::getPlayer(); - const MWMechanics::CreatureStats& stats = playerPtr.getClass().getCreatureStats(playerPtr); - - mReviewDialog->setHealth ( stats.getHealth() ); - mReviewDialog->setMagicka( stats.getMagicka() ); - mReviewDialog->setFatigue( stats.getFatigue() ); - } + MWWorld::Ptr playerPtr = MWMechanics::getPlayer(); + const MWMechanics::CreatureStats& stats = playerPtr.getClass().getCreatureStats(playerPtr); + mReviewDialog->setHealth(stats.getHealth()); + mReviewDialog->setMagicka(stats.getMagicka()); + mReviewDialog->setFatigue(stats.getFatigue()); + for (auto& attributePair : mPlayerAttributes) { - std::map attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); - for (auto& attributePair : attributes) - { - mReviewDialog->setAttribute(static_cast (attributePair.first), attributePair.second); - } + mReviewDialog->setAttribute(static_cast (attributePair.first), attributePair.second); } - + for (auto& skillPair : mPlayerSkillValues) { - std::map skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues(); - for (auto& skillPair : skills) - { - mReviewDialog->setSkillValue(static_cast (skillPair.first), skillPair.second); - } - mReviewDialog->configureSkills(MWBase::Environment::get().getWindowManager()->getPlayerMajorSkills(), MWBase::Environment::get().getWindowManager()->getPlayerMinorSkills()); + mReviewDialog->setSkillValue(static_cast (skillPair.first), skillPair.second); } + mReviewDialog->configureSkills(mPlayerMajorSkills, mPlayerMinorSkills); mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index d5739c3d0..4f6296785 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "../mwmechanics/stat.hpp" @@ -56,6 +57,10 @@ namespace MWGui osg::Group* mParent; Resource::ResourceSystem* mResourceSystem; + SkillList mPlayerMajorSkills, mPlayerMinorSkills; + std::map mPlayerAttributes; + std::map mPlayerSkillValues; + //Dialogs TextInputDialog* mNameDialog; RaceDialog* mRaceDialog; diff --git a/apps/openmw/mwgui/textcolours.cpp b/apps/openmw/mwgui/textcolours.cpp new file mode 100644 index 000000000..77afa5e26 --- /dev/null +++ b/apps/openmw/mwgui/textcolours.cpp @@ -0,0 +1,36 @@ +#include "textcolours.hpp" + +#include + +#include + +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"); + } +} diff --git a/apps/openmw/mwgui/textcolours.hpp b/apps/openmw/mwgui/textcolours.hpp index 3fa55654b..83bc1d3f5 100644 --- a/apps/openmw/mwgui/textcolours.hpp +++ b/apps/openmw/mwgui/textcolours.hpp @@ -5,14 +5,12 @@ namespace MWGui { - struct TextColours { MyGUI::Colour header; MyGUI::Colour normal; MyGUI::Colour notify; - MyGUI::Colour link; MyGUI::Colour linkOver; MyGUI::Colour linkPressed; @@ -28,6 +26,9 @@ namespace MWGui MyGUI::Colour journalTopic; MyGUI::Colour journalTopicOver; MyGUI::Colour journalTopicPressed; + + public: + void loadColours(); }; } diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 0568efeb4..74076641a 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -443,6 +443,9 @@ namespace MWGui // constant effects have no duration and no target if (!mEffectParams.mIsConstant) { + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce)) + mEffectParams.mDuration = std::max(1, mEffectParams.mDuration); + if (mEffectParams.mDuration > 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) { spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + MyGUI::utility::toString(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 255049e91..19d6e2a07 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -15,6 +15,9 @@ #include #include +// For BT_NO_PROFILE +#include + #include #include @@ -126,19 +129,8 @@ #include "keyboardnavigation.hpp" #include "resourceskin.hpp" -namespace -{ - - MyGUI::Colour getTextColour(const std::string& type) - { - return MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=" + type + "}")); - } - -} - namespace MWGui { - WindowManager::WindowManager( 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, @@ -191,12 +183,6 @@ namespace MWGui , mCursorVisible(true) , mCursorActive(false) , mPlayerBounty(-1) - , mPlayerName() - , mPlayerRaceId() - , mPlayerAttributes() - , mPlayerMajorSkills() - , mPlayerMinorSkills() - , mPlayerSkillValues() , mGui(nullptr) , mGuiModes() , mCursorManager(nullptr) @@ -207,7 +193,6 @@ namespace MWGui , mRestAllowed(true) , mShowOwned(0) , mEncoding(encoding) - , mFontHeight(16) , mVersionDescription(versionDescription) , mWindowVisible(true) { @@ -245,13 +230,6 @@ namespace MWGui SpellView::registerComponents(); Gui::registerAllWidgets(); - int fontSize = Settings::Manager::getInt("font size", "GUI"); - fontSize = std::min(std::max(12, fontSize), 20); - mFontHeight = fontSize; - - MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); - MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate("Resource") = newDelegate(this, &WindowManager::loadFontDelegate); - MyGUI::FactoryManager::getInstance().registerFactory("Controller"); MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); @@ -308,94 +286,6 @@ namespace MWGui Settings::Manager::getFloat("contrast", "Video")); } - void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) - { - MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator(); - bool createCopy = false; - while (resourceNode.next("Resource")) - { - std::string type, name; - resourceNode->findAttribute("type", type); - resourceNode->findAttribute("name", name); - - if (name.empty()) - continue; - - if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) - { - createCopy = true; - - // For TrueType fonts we should override Size and Resolution properties - // to allow to configure font size via config file, without need to edit XML files. - // Also we should take UI scaling factor in account. - int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); - resolution = std::min(960, std::max(48, resolution)); - - float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); - resolution *= uiScale; - - MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property"); - resolutionNode->addAttribute("key", "Resolution"); - resolutionNode->addAttribute("value", std::to_string(resolution)); - - MyGUI::xml::ElementPtr sizeNode = resourceNode->createChild("Property"); - sizeNode->addAttribute("key", "Size"); - sizeNode->addAttribute("value", std::to_string(mFontHeight)); - } - else if (Misc::StringUtils::ciEqual(type, "ResourceSkin") || - Misc::StringUtils::ciEqual(type, "AutoSizedResourceSkin")) - { - // We should adjust line height for MyGUI widgets depending on font size - MyGUI::xml::ElementPtr heightNode = resourceNode->createChild("Property"); - heightNode->addAttribute("key", "HeightLine"); - heightNode->addAttribute("value", std::to_string(mFontHeight+2)); - } - } - - MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version); - - if (createCopy) - { - MyGUI::xml::ElementPtr copy = _node->createCopy(); - - MyGUI::xml::ElementEnumerator copyFont = copy->getElementEnumerator(); - while (copyFont.next("Resource")) - { - std::string type, name; - copyFont->findAttribute("type", type); - copyFont->findAttribute("name", name); - - if (name.empty()) - continue; - - if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) - { - // Since the journal and books use the custom scaling factor depending on resolution, - // setup separate fonts with different Resolution to fit these windows. - // These fonts have an internal prefix. - int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); - resolution = std::min(960, std::max(48, resolution)); - - float currentX = Settings::Manager::getInt("resolution x", "Video"); - float currentY = Settings::Manager::getInt("resolution y", "Video"); - // TODO: read size from openmw_layout.xml - float heightScale = (currentY / 520); - float widthScale = (currentX / 600); - float uiScale = std::min(widthScale, heightScale); - resolution *= uiScale; - - MyGUI::xml::ElementPtr resolutionNode = copyFont->createChild("Property"); - resolutionNode->addAttribute("key", "Resolution"); - resolutionNode->addAttribute("value", std::to_string(resolution)); - - copyFont->setAttribute("name", "Journalbook " + name); - } - } - - MyGUI::ResourceManager::getInstance().loadFromXmlNode(copy, _file, _version); - } - } - void WindowManager::loadUserFonts() { mFontLoader->loadTrueTypeFonts(); @@ -407,26 +297,7 @@ namespace MWGui int w = MyGUI::RenderManager::getInstance().getViewSize().width; int h = MyGUI::RenderManager::getInstance().getViewSize().height; - mTextColours.header = getTextColour("header"); - mTextColours.normal = getTextColour("normal"); - mTextColours.notify = getTextColour("notify"); - - mTextColours.link = getTextColour("link"); - mTextColours.linkOver = getTextColour("link_over"); - mTextColours.linkPressed = getTextColour("link_pressed"); - - mTextColours.answer = getTextColour("answer"); - mTextColours.answerOver = getTextColour("answer_over"); - mTextColours.answerPressed = getTextColour("answer_pressed"); - - mTextColours.journalLink = getTextColour("journal_link"); - mTextColours.journalLinkOver = getTextColour("journal_link_over"); - mTextColours.journalLinkPressed = getTextColour("journal_link_pressed"); - - mTextColours.journalTopic = getTextColour("journal_topic"); - mTextColours.journalTopicOver = getTextColour("journal_topic_over"); - mTextColours.journalTopicPressed = getTextColour("journal_topic_pressed"); - + mTextColours.loadColours(); mDragAndDrop = new DragAndDrop(); @@ -612,17 +483,6 @@ namespace MWGui mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem); - // Setup player stats - for (int i = 0; i < ESM::Attribute::Length; ++i) - { - mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::AttributeValue())); - } - - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::SkillValue())); - } - updatePinnedWindows(); // Set up visibility @@ -631,7 +491,7 @@ namespace MWGui int WindowManager::getFontHeight() const { - return mFontHeight; + return mFontLoader->getFontHeight(); } void WindowManager::setNewGame(bool newgame) @@ -652,7 +512,6 @@ namespace MWGui { mKeyboardNavigation.reset(); - MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); @@ -824,40 +683,14 @@ namespace MWGui { mStatsWindow->setValue (id, value); mCharGen->setValue(id, value); - - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8" - }; - static ESM::Attribute::AttributeID attributes[] = - { - ESM::Attribute::Strength, - ESM::Attribute::Intelligence, - ESM::Attribute::Willpower, - ESM::Attribute::Agility, - ESM::Attribute::Speed, - ESM::Attribute::Endurance, - ESM::Attribute::Personality, - ESM::Attribute::Luck - }; - for (size_t i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i) - { - if (id != ids[i]) - continue; - mPlayerAttributes[attributes[i]] = value; - break; - } } - void WindowManager::setValue (int parSkill, const MWMechanics::SkillValue& value) { /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we /// allow custom skills. mStatsWindow->setValue(static_cast (parSkill), value); mCharGen->setValue(static_cast (parSkill), value); - mPlayerSkillValues[parSkill] = value; } void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) @@ -870,10 +703,6 @@ namespace MWGui void WindowManager::setValue (const std::string& id, const std::string& value) { mStatsWindow->setValue (id, value); - if (id=="name") - mPlayerName = value; - else if (id=="race") - mPlayerRaceId = value; } void WindowManager::setValue (const std::string& id, int value) @@ -895,8 +724,6 @@ namespace MWGui { mStatsWindow->configureSkills (major, minor); mCharGen->configureSkills(major, minor); - mPlayerMajorSkills = major; - mPlayerMinorSkills = minor; } void WindowManager::updateSkillArea() @@ -1045,7 +872,7 @@ namespace MWGui mHud->setPlayerPos(x, y, u, v); } - void WindowManager::onFrame (float frameDuration) + void WindowManager::update (float frameDuration) { bool gameRunning = MWBase::Environment::get().getStateManager()->getState()!= MWBase::StateManager::State_NoGame; @@ -1731,26 +1558,6 @@ namespace MWGui return mGuiModes.back(); } - std::map WindowManager::getPlayerSkillValues() - { - return mPlayerSkillValues; - } - - std::map WindowManager::getPlayerAttributeValues() - { - return mPlayerAttributes; - } - - WindowManager::SkillList WindowManager::getPlayerMinorSkills() - { - return mPlayerMinorSkills; - } - - WindowManager::SkillList WindowManager::getPlayerMajorSkills() - { - return mPlayerMajorSkills; - } - void WindowManager::disallowMouse() { mInputBlocker->setVisible (true); @@ -2331,7 +2138,9 @@ namespace MWGui void WindowManager::toggleDebugWindow() { +#ifndef BT_NO_PROFILE mDebugWindow->setVisible(!mDebugWindow->isVisible()); +#endif } void WindowManager::cycleSpell(bool next) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 33fb801c8..c34bfa01f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -356,13 +356,7 @@ namespace MWGui End of tes3mp addition */ - virtual void onFrame (float frameDuration); - - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - virtual std::map getPlayerSkillValues(); - virtual std::map getPlayerAttributeValues(); - virtual SkillList getPlayerMinorSkills(); - virtual SkillList getPlayerMajorSkills(); + virtual void update (float duration); /** * Fetches a GMST string from the store, if there is no setting with the given @@ -503,8 +497,6 @@ namespace MWGui MWWorld::Ptr mSelectedEnchantItem; MWWorld::Ptr mSelectedWeapon; - void loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version); - std::vector mCurrentModals; // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). @@ -571,14 +563,6 @@ namespace MWGui void setCursorVisible(bool visible); - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - // Various stats about player as needed by window manager - std::string mPlayerName; - std::string mPlayerRaceId; - std::map mPlayerAttributes; - SkillList mPlayerMajorSkills, mPlayerMinorSkills; - std::map mPlayerSkillValues; - MyGUI::Gui *mGui; // Gui struct GuiModeState @@ -628,8 +612,6 @@ namespace MWGui ToUTF8::FromType mEncoding; - int mFontHeight; - std::string mVersionDescription; bool mWindowVisible; diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index a71c5b31a..50a169c5c 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -36,7 +36,6 @@ namespace MWInput , mSneakToggleShortcutTimer(0.f) , mGamepadZoom(0) , mGamepadGuiCursorEnabled(true) - , mControlsDisabled(false) , mJoystickLastUsed(false) , mSneakGamepadShortcut(false) , mGamepadPreviewMode(false) @@ -83,9 +82,8 @@ namespace MWInput } } - bool ControllerManager::update(float dt, bool disableControls) + bool ControllerManager::update(float dt) { - mControlsDisabled = disableControls; mGamepadPreviewMode = mActionManager->getPreviewDelay() == 1.f; if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled)) @@ -232,7 +230,7 @@ namespace MWInput auto kc = sdlKeyToMyGUI(SDLK_ESCAPE); mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(kc, 0)); - if (!mControlsDisabled) + if (!MWBase::Environment::get().getInputManager()->controlsDisabled()) mBindingsManager->controllerButtonPressed(deviceID, arg); } @@ -244,7 +242,7 @@ namespace MWInput return; } - if (!mJoystickEnabled || mControlsDisabled) + if (!mJoystickEnabled || MWBase::Environment::get().getInputManager()->controlsDisabled()) return; mJoystickLastUsed = true; @@ -275,7 +273,7 @@ namespace MWInput void ControllerManager::axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg) { - if (!mJoystickEnabled || mControlsDisabled) + if (!mJoystickEnabled || MWBase::Environment::get().getInputManager()->controlsDisabled()) return; mJoystickLastUsed = true; diff --git a/apps/openmw/mwinput/controllermanager.hpp b/apps/openmw/mwinput/controllermanager.hpp index 6b9546b0c..94faff088 100644 --- a/apps/openmw/mwinput/controllermanager.hpp +++ b/apps/openmw/mwinput/controllermanager.hpp @@ -23,7 +23,7 @@ namespace MWInput virtual ~ControllerManager() = default; - bool update(float dt, bool disableControls); + bool update(float dt); virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg); @@ -56,7 +56,6 @@ namespace MWInput float mSneakToggleShortcutTimer; float mGamepadZoom; bool mGamepadGuiCursorEnabled; - bool mControlsDisabled; bool mJoystickLastUsed; bool mGuiCursorEnabled; bool mSneakGamepadShortcut; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 5f2910571..dd98f93f3 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -31,7 +31,7 @@ namespace MWInput osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation, const std::string& userFile, bool userFileExists, const std::string& userControllerBindingsFile, const std::string& controllerBindingsFile, bool grab) - : mGrabCursor(Settings::Manager::getBool("grab cursor", "Input")) + : mControlsDisabled(false) { mInputWrapper = new SDLUtil::InputWrapper(window, viewer, grab); mInputWrapper->setWindowEventCallback(MWBase::Environment::get().getWindowManager()); @@ -81,47 +81,25 @@ namespace MWInput mActionManager->setAttemptJump(jumping); } - void InputManager::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) - { - mMouseManager->warpMouse(); - } - } - void InputManager::update(float dt, bool disableControls, bool disableEvents) { + mControlsDisabled = disableControls; + mInputWrapper->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); mInputWrapper->capture(disableEvents); - mKeyboardManager->setControlsDisabled(disableControls); if (disableControls) { - updateCursorMode(); + mMouseManager->updateCursorMode(); return; } mBindingsManager->update(dt); - updateCursorMode(); + mMouseManager->updateCursorMode(); - bool controllerMove = mControllerManager->update(dt, disableControls); - mMouseManager->update(dt, disableControls); + bool controllerMove = mControllerManager->update(dt); + mMouseManager->update(dt); mSensorManager->update(dt); mActionManager->update(dt, controllerMove); } @@ -152,12 +130,6 @@ namespace MWInput void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - for (const auto& setting : changed) - { - if (setting.first == "Input" && setting.second == "grab cursor") - mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); - } - mMouseManager->processChangedSettings(changed); mSensorManager->processChangedSettings(changed); } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 4ecb6a82d..debbf27e0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -94,13 +94,13 @@ namespace MWInput virtual void executeAction(int action); + virtual bool controlsDisabled() { return mControlsDisabled; } + private: void convertMousePosForMyGUI(int& x, int& y); void handleGuiArrowKey(int action); - void updateCursorMode(); - void quickKey(int index); void showQuickKeysMenu(); @@ -109,7 +109,7 @@ namespace MWInput SDLUtil::InputWrapper* mInputWrapper; - bool mGrabCursor; + bool mControlsDisabled; ControlSwitch* mControlSwitch; diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index 20115155e..db047a342 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -18,7 +18,6 @@ namespace MWInput { KeyboardManager::KeyboardManager(BindingsManager* bindingsManager) : mBindingsManager(bindingsManager) - , mControlsDisabled(false) { } @@ -53,10 +52,11 @@ namespace MWInput if (arg.repeat) return; - if (!mControlsDisabled && !consumed) + MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); + if (!input->controlsDisabled() && !consumed) mBindingsManager->keyPressed(arg); - MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); + input->setJoystickLastUsed(false); } void KeyboardManager::keyReleased(const SDL_KeyboardEvent &arg) diff --git a/apps/openmw/mwinput/keyboardmanager.hpp b/apps/openmw/mwinput/keyboardmanager.hpp index ae473be51..b7027220f 100644 --- a/apps/openmw/mwinput/keyboardmanager.hpp +++ b/apps/openmw/mwinput/keyboardmanager.hpp @@ -19,12 +19,8 @@ namespace MWInput virtual void keyPressed(const SDL_KeyboardEvent &arg); virtual void keyReleased(const SDL_KeyboardEvent &arg); - void setControlsDisabled(bool disabled) { mControlsDisabled = disabled; } - private: BindingsManager* mBindingsManager; - - bool mControlsDisabled; }; } #endif diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index 2cce1cd80..df38868e1 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -24,8 +24,9 @@ namespace MWInput MouseManager::MouseManager(BindingsManager* bindingsManager, SDLUtil::InputWrapper* inputWrapper, SDL_Window* window) : mInvertX(Settings::Manager::getBool("invert x axis", "Input")) , mInvertY(Settings::Manager::getBool("invert y axis", "Input")) - , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) - , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) + , 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) @@ -33,7 +34,6 @@ namespace MWInput , mGuiCursorY(0) , mMouseWheel(0) , mMouseLookEnabled(false) - , mControlsDisabled(false) , mGuiCursorEnabled(true) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); @@ -59,6 +59,9 @@ namespace MWInput 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"); } } @@ -88,7 +91,7 @@ namespace MWInput MWBase::Environment::get().getWindowManager()->setCursorActive(true); } - if (mMouseLookEnabled && !mControlsDisabled) + 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; @@ -136,10 +139,11 @@ namespace MWInput void MouseManager::mouseWheelMoved(const SDL_MouseWheelEvent &arg) { - if (mBindingsManager->isDetectingBindingState() || !mControlsDisabled) + MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); + if (mBindingsManager->isDetectingBindingState() || !input->controlsDisabled()) mBindingsManager->mouseWheelMoved(arg); - MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); + input->setJoystickLastUsed(false); } void MouseManager::mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id) @@ -169,10 +173,31 @@ namespace MWInput mBindingsManager->mousePressed(arg, id); } - void MouseManager::update(float dt, bool disableControls) + void MouseManager::updateCursorMode() { - mControlsDisabled = disableControls; + 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; diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp index 58d97f6e5..0f523591a 100644 --- a/apps/openmw/mwinput/mousemanager.hpp +++ b/apps/openmw/mwinput/mousemanager.hpp @@ -20,7 +20,8 @@ namespace MWInput virtual ~MouseManager() = default; - void update(float dt, bool disableControls); + void updateCursorMode(); + void update(float dt); virtual void mouseMoved(const SDLUtil::MouseMotionEvent &arg); virtual void mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id); @@ -40,6 +41,7 @@ namespace MWInput private: bool mInvertX; bool mInvertY; + bool mGrabCursor; float mCameraSensitivity; float mCameraYMultiplier; @@ -51,7 +53,6 @@ namespace MWInput float mGuiCursorY; int mMouseWheel; bool mMouseLookEnabled; - bool mControlsDisabled; bool mGuiCursorEnabled; }; } diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index ac2ffc7de..89d31fe7b 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -12,14 +12,12 @@ namespace MWMechanics { - void ActiveSpells::update() const + void ActiveSpells::update(float duration) const { bool rebuild = false; - MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp(); - // Erase no longer active spells and effects - if (mLastUpdate!=now) + if (duration > 0) { TContainer::iterator iter (mSpells.begin()); while (iter!=mSpells.end()) @@ -34,21 +32,20 @@ namespace MWMechanics std::vector& effects = iter->second.mEffects; for (std::vector::iterator effectIt = effects.begin(); effectIt != effects.end();) { - MWWorld::TimeStamp start = iter->second.mTimeStamp; - MWWorld::TimeStamp end = start + static_cast(effectIt->mDuration)*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); - if (end <= now) + if (effectIt->mTimeLeft <= 0) { effectIt = effects.erase(effectIt); rebuild = true; } else + { + effectIt->mTimeLeft -= duration; ++effectIt; + } } ++iter; } } - - mLastUpdate = now; } if (mSpellsChanged) @@ -63,24 +60,15 @@ namespace MWMechanics void ActiveSpells::rebuildEffects() const { - MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp(); - mEffects = MagicEffects(); for (TIterator iter (begin()); iter!=end(); ++iter) { - const MWWorld::TimeStamp& start = iter->second.mTimeStamp; - const std::vector& effects = iter->second.mEffects; for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) { - double duration = effectIt->mDuration; - MWWorld::TimeStamp end = start; - end += duration * - MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); - - if (end>now) + if (effectIt->mTimeLeft > 0) mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude)); } } @@ -88,12 +76,11 @@ namespace MWMechanics ActiveSpells::ActiveSpells() : mSpellsChanged (false) - , mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp()) {} const MagicEffects& ActiveSpells::getMagicEffects() const { - update(); + update(0.f); return mEffects; } @@ -116,19 +103,14 @@ namespace MWMechanics for (std::vector::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) { - if (iter->mDuration > duration) - duration = iter->mDuration; + if (iter->mTimeLeft > duration) + duration = iter->mTimeLeft; } - double scaledDuration = duration * - MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); - - double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp() - iterator->second.mTimeStamp; - - if (usedUp>=scaledDuration) + if (duration < 0) return 0; - return scaledDuration-usedUp; + return duration; } bool ActiveSpells::isSpellActive(const std::string& id) const @@ -152,7 +134,6 @@ namespace MWMechanics TContainer::iterator it(mSpells.find(id)); ActiveSpellParams params; - params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); params.mEffects = effects; params.mDisplayName = displayName; params.mCasterActorId = casterActorId; @@ -211,19 +192,15 @@ namespace MWMechanics { for (TContainer::const_iterator it = begin(); it != end(); ++it) { - float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - for (std::vector::const_iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end(); ++effectIt) { std::string name = it->second.mDisplayName; - float remainingTime = effectIt->mDuration + - static_cast(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale; float magnitude = effectIt->mMagnitude; if (magnitude) - visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration); + visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, effectIt->mTimeLeft, effectIt->mDuration); } } } @@ -365,7 +342,6 @@ namespace MWMechanics params.mEffects = it->second.mEffects; params.mCasterActorId = it->second.mCasterActorId; params.mDisplayName = it->second.mDisplayName; - params.mTimeStamp = it->second.mTimeStamp.toEsm(); state.mSpells.insert (std::make_pair(it->first, params)); } @@ -380,7 +356,6 @@ namespace MWMechanics params.mEffects = it->second.mEffects; params.mCasterActorId = it->second.mCasterActorId; params.mDisplayName = it->second.mDisplayName; - params.mTimeStamp = MWWorld::TimeStamp(it->second.mTimeStamp); mSpells.insert (std::make_pair(it->first, params)); mSpellsChanged = true; diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index c29c0cbd3..de7778e2a 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -44,15 +44,14 @@ namespace MWMechanics TIterator end() const; + void update(float duration) const; + private: mutable TContainer mSpells; mutable MagicEffects mEffects; mutable bool mSpellsChanged; - mutable MWWorld::TimeStamp mLastUpdate; - void update() const; - void rebuildEffects() const; /// Add any effects that are in "from" and not in "addTo" to "addTo" diff --git a/apps/openmw/mwmechanics/actor.cpp b/apps/openmw/mwmechanics/actor.cpp index f7c6b7f1c..a5c55633a 100644 --- a/apps/openmw/mwmechanics/actor.cpp +++ b/apps/openmw/mwmechanics/actor.cpp @@ -18,4 +18,44 @@ namespace MWMechanics { return mCharacterController.get(); } + + int Actor::getGreetingTimer() const + { + return mGreetingTimer; + } + + void Actor::setGreetingTimer(int timer) + { + mGreetingTimer = timer; + } + + float Actor::getAngleToPlayer() const + { + return mTargetAngleRadians; + } + + void Actor::setAngleToPlayer(float angle) + { + mTargetAngleRadians = angle; + } + + GreetingState Actor::getGreetingState() const + { + return mGreetingState; + } + + void Actor::setGreetingState(GreetingState state) + { + mGreetingState = state; + } + + bool Actor::isTurningToPlayer() const + { + return mIsTurningToPlayer; + } + + void Actor::setTurningToPlayer(bool turning) + { + mIsTurningToPlayer = turning; + } } diff --git a/apps/openmw/mwmechanics/actor.hpp b/apps/openmw/mwmechanics/actor.hpp index 119527b64..287ca420f 100644 --- a/apps/openmw/mwmechanics/actor.hpp +++ b/apps/openmw/mwmechanics/actor.hpp @@ -3,6 +3,8 @@ #include +#include "../mwmechanics/actorutil.hpp" + namespace MWRender { class Animation; @@ -27,8 +29,24 @@ namespace MWMechanics CharacterController* getCharacterController(); + int getGreetingTimer() const; + void setGreetingTimer(int timer); + + float getAngleToPlayer() const; + void setAngleToPlayer(float angle); + + GreetingState getGreetingState() const; + void setGreetingState(GreetingState state); + + bool isTurningToPlayer() const; + void setTurningToPlayer(bool turning); + private: std::unique_ptr mCharacterController; + int mGreetingTimer{0}; + float mTargetAngleRadians{0.f}; + GreetingState mGreetingState{Greet_None}; + bool mIsTurningToPlayer{false}; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 18341db9d..60815c3a4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -480,7 +480,7 @@ namespace MWMechanics actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor; } - void Actors::updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly) + void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly) { if (!actor.getClass().isActor() || actor == getPlayer()) return; @@ -493,9 +493,9 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->isSwimming(actor) || (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1)) { - stats.setTurningToPlayer(false); - stats.setGreetingTimer(0); - stats.setGreetingState(Greet_None); + actorState.setTurningToPlayer(false); + actorState.setGreetingTimer(0); + actorState.setGreetingState(Greet_None); return; } @@ -504,14 +504,14 @@ namespace MWMechanics osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); osg::Vec3f dir = playerPos - actorPos; - if (stats.isTurningToPlayer()) + if (actorState.isTurningToPlayer()) { // Reduce the turning animation glitch by using a *HUGE* value of // epsilon... TODO: a proper fix might be in either the physics or the // animation subsystem - if (zTurn(actor, stats.getAngleToPlayer(), osg::DegreesToRadians(5.f))) + if (zTurn(actor, actorState.getAngleToPlayer(), osg::DegreesToRadians(5.f))) { - stats.setTurningToPlayer(false); + actorState.setTurningToPlayer(false); // An original engine launches an endless idle2 when an actor greets player. playAnimationGroup (actor, "idle2", 0, std::numeric_limits::max(), false); } @@ -526,8 +526,8 @@ namespace MWMechanics float helloDistance = static_cast(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier); - int greetingTimer = stats.getGreetingTimer(); - GreetingState greetingState = stats.getGreetingState(); + int greetingTimer = actorState.getGreetingTimer(); + GreetingState greetingState = actorState.getGreetingState(); if (greetingState == Greet_None) { if ((playerPos - actorPos).length2() <= helloDistance*helloDistance && @@ -549,7 +549,7 @@ namespace MWMechanics greetingTimer++; if (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor)) - turnActorToFacePlayer(actor, dir); + turnActorToFacePlayer(actor, actorState, dir); if (greetingTimer >= GREETING_COOLDOWN) { @@ -565,20 +565,19 @@ namespace MWMechanics greetingState = Greet_None; } - stats.setGreetingTimer(greetingTimer); - stats.setGreetingState(greetingState); + actorState.setGreetingTimer(greetingTimer); + actorState.setGreetingState(greetingState); } - void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir) + void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[0] = 0; - CreatureStats &stats = actor.getClass().getCreatureStats(actor); - if (!stats.isTurningToPlayer()) + if (!actorState.isTurningToPlayer()) { - stats.setAngleToPlayer(std::atan2(dir.x(), dir.y())); - stats.setTurningToPlayer(true); + actorState.setAngleToPlayer(std::atan2(dir.x(), dir.y())); + actorState.setTurningToPlayer(true); } } @@ -819,6 +818,9 @@ namespace MWMechanics if (visitor.mRemainingTime > 0) { double timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + if(timeScale == 0.0) + timeScale = 1; + restoreHours = std::max(0.0, hours - visitor.mRemainingTime * timeScale / 3600.f); } else if (visitor.mRemainingTime == -1) @@ -1782,6 +1784,8 @@ namespace MWMechanics End of tes3mp change (major) */ + iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); + // For dead actors we need to remove looping spell particles if (iter->first.getClass().getCreatureStats(iter->first).isDead()) ctrl->updateContinuousVfx(); @@ -1862,7 +1866,7 @@ namespace MWMechanics if (isConscious(iter->first)) { stats.getAiSequence().execute(iter->first, *ctrl, duration); - updateGreetingState(iter->first, timerUpdateHello > 0); + updateGreetingState(iter->first, *iter->second, timerUpdateHello > 0); playIdleDialogue(iter->first); updateMovementSpeed(iter->first); } @@ -2165,7 +2169,11 @@ namespace MWMechanics void Actors::rest(double hours, bool sleep) { - float duration = hours * 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + float duration = hours * 3600.f; + float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + if (timeScale != 0.f) + duration /= timeScale; + const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); @@ -2675,6 +2683,42 @@ namespace MWMechanics End of tes3mp addition */ + int Actors::getGreetingTimer(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return 0; + + return it->second->getGreetingTimer(); + } + + float Actors::getAngleToPlayer(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return 0.f; + + return it->second->getAngleToPlayer(); + } + + GreetingState Actors::getGreetingState(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return Greet_None; + + return it->second->getGreetingState(); + } + + bool Actors::isTurningToPlayer(const MWWorld::Ptr& ptr) const + { + PtrActorMap::const_iterator it = mActors.find(ptr); + if (it == mActors.end()) + return false; + + return it->second->isTurningToPlayer(); + } + void Actors::fastForwardAi() { if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 84fc8d8dc..668f740fb 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -7,6 +7,8 @@ #include #include +#include "../mwmechanics/actorutil.hpp" + namespace ESM { class ESMReader; @@ -70,6 +72,7 @@ namespace MWMechanics PtrActorMap::const_iterator begin() { return mActors.begin(); } PtrActorMap::const_iterator end() { return mActors.end(); } + std::size_t size() const { return mActors.size(); } void notifyDied(const MWWorld::Ptr &actor); @@ -122,8 +125,8 @@ namespace MWMechanics void playIdleDialogue(const MWWorld::Ptr& actor); void updateMovementSpeed(const MWWorld::Ptr& actor); - void updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly); - void turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir); + void updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly); + void turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir); void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance); @@ -214,6 +217,11 @@ namespace MWMechanics End of tes3mp addition */ + int getGreetingTimer(const MWWorld::Ptr& ptr) const; + float getAngleToPlayer(const MWWorld::Ptr& ptr) const; + GreetingState getGreetingState(const MWWorld::Ptr& ptr) const; + bool isTurningToPlayer(const MWWorld::Ptr& ptr) const; + private: void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl); diff --git a/apps/openmw/mwmechanics/actorutil.hpp b/apps/openmw/mwmechanics/actorutil.hpp index 82a904799..cf3d92558 100644 --- a/apps/openmw/mwmechanics/actorutil.hpp +++ b/apps/openmw/mwmechanics/actorutil.hpp @@ -8,6 +8,13 @@ namespace MWWorld namespace MWMechanics { + enum GreetingState + { + Greet_None, + Greet_InProgress, + Greet_Done + }; + MWWorld::Ptr getPlayer(); bool isPlayerInCombat(); bool canActorMoveByZAxis(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index d85c377c6..9138658ef 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -29,7 +29,7 @@ namespace MWMechanics { /// \brief Causes actor to walk to activatable object and activate it /** Will activate when close to object **/ - class AiActivate : public AiPackage + class AiActivate final : public AiPackage { public: /// Constructor @@ -49,11 +49,11 @@ namespace MWMechanics AiActivate(const ESM::AiSequence::AiActivate* activate); - virtual AiActivate *clone() const; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); - virtual int getTypeId() const; + AiActivate *clone() const final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + int getTypeId() const final; - virtual void writeState(ESM::AiSequence::AiSequence& sequence) const; + void writeState(ESM::AiSequence::AiSequence& sequence) const final; private: std::string mObjectId; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 4c8be29eb..39a78192b 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -16,22 +16,22 @@ namespace MWMechanics /// \brief AiPackage to have an actor avoid an opening door /** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it **/ - class AiAvoidDoor : public AiPackage + class AiAvoidDoor final : public AiPackage { public: /// Avoid door until the door is fully open AiAvoidDoor(const MWWorld::ConstPtr& doorPtr); - virtual AiAvoidDoor *clone() const; + AiAvoidDoor *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual unsigned int getPriority() const; + unsigned int getPriority() const final; - virtual bool canCancel() const { return false; } - virtual bool shouldCancelPreviousAi() const { return false; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } private: float mDuration; diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index 263ab8c2b..daa2782c2 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -7,21 +7,21 @@ namespace MWMechanics { /// \brief AiPackage to have an actor resurface to breathe // The AI will go up if lesser than half breath left - class AiBreathe : public AiPackage + class AiBreathe final : public AiPackage { public: AiBreathe(); - virtual AiBreathe *clone() const; + AiBreathe *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual unsigned int getPriority() const; + unsigned int getPriority() const final; - virtual bool canCancel() const { return false; } - virtual bool shouldCancelPreviousAi() const { return false; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } }; } #endif diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 7128fe7a2..6b10370c6 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -11,22 +11,22 @@ namespace MWWorld namespace MWMechanics { /// AiPackage which makes an actor to cast given spell. - class AiCast : public AiPackage { + class AiCast final : public AiPackage { public: AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false); - virtual AiPackage *clone() const; + AiPackage *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual MWWorld::Ptr getTarget() const; + MWWorld::Ptr getTarget() const final; - virtual unsigned int getPriority() const; + unsigned int getPriority() const final; - virtual bool canCancel() const { return false; } - virtual bool shouldCancelPreviousAi() const { return false; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } private: std::string mTargetId; diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index f89e71678..049857e71 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -91,7 +91,7 @@ namespace MWMechanics }; /// \brief Causes the actor to fight another actor - class AiCombat : public AiPackage + class AiCombat final : public AiPackage { public: ///Constructor @@ -102,21 +102,21 @@ namespace MWMechanics void init(); - virtual AiCombat *clone() const; + AiCombat *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual unsigned int getPriority() const; + unsigned int getPriority() const final; ///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; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } private: /// Returns true if combat should end diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index e4319b425..5b49807a2 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -16,7 +16,7 @@ namespace AiSequence namespace MWMechanics { /// \brief AI Package to have an NPC lead the player to a specific point - class AiEscort : public AiPackage + class AiEscort final : public AiPackage { public: /// Implementation of AiEscort @@ -30,21 +30,21 @@ namespace MWMechanics AiEscort(const ESM::AiSequence::AiEscort* escort); - virtual AiEscort *clone() const; + AiEscort *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual bool useVariableSpeed() const { return true;} + bool useVariableSpeed() const final { return true; } - virtual bool sideWithTarget() const { return true; } + bool sideWithTarget() const final { return true; } - void writeState(ESM::AiSequence::AiSequence &sequence) const; + void writeState(ESM::AiSequence::AiSequence &sequence) const final; - void fastForward(const MWWorld::Ptr& actor, AiState& state); + void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - virtual osg::Vec3f getDestination() const { return osg::Vec3f(mX, mY, mZ); } + osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } private: std::string mCellId; diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index 099e5d237..98d9ea04b 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -6,20 +6,20 @@ namespace MWMechanics { /// AiPackage which makes an actor face a certain direction. - class AiFace : public AiPackage { + class AiFace final : public AiPackage { public: AiFace(float targetX, float targetY); - virtual AiPackage *clone() const; + AiPackage *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual unsigned int getPriority() const; + unsigned int getPriority() const final; - virtual bool canCancel() const { return false; } - virtual bool shouldCancelPreviousAi() const { return false; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } private: float mTargetX, mTargetY; diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index c0d447529..bfaa50084 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -39,7 +39,7 @@ namespace MWMechanics /// \brief AiPackage for an actor to follow another actor/the PC /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely **/ - class AiFollow : public AiPackage + class AiFollow final : public AiPackage { public: AiFollow(const std::string &actorId, float duration, float x, float y, float z); @@ -53,30 +53,30 @@ namespace MWMechanics AiFollow(const ESM::AiSequence::AiFollow* follow); - virtual bool sideWithTarget() const { return true; } - virtual bool followTargetThroughDoors() const { return true; } - virtual bool shouldCancelPreviousAi() const { return !mCommanded; } + bool sideWithTarget() const final { return true; } + bool followTargetThroughDoors() const final { return true; } + bool shouldCancelPreviousAi() const final { return !mCommanded; } - virtual AiFollow *clone() const; + AiFollow *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual bool useVariableSpeed() const { return true;} + bool useVariableSpeed() const final { return true; } /// Returns the actor being followed std::string getFollowedActor(); - virtual void writeState (ESM::AiSequence::AiSequence& sequence) const; + void writeState (ESM::AiSequence::AiSequence& sequence) const final; bool isCommanded() const; int getFollowIndex() const; - void fastForward(const MWWorld::Ptr& actor, AiState& state); + void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - virtual osg::Vec3f getDestination() const + osg::Vec3f getDestination() const final { MWWorld::Ptr target = getTarget(); if (target.isEmpty()) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 114e011ce..dca882a3b 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -24,8 +24,6 @@ #include -MWMechanics::AiPackage::~AiPackage() {} - MWMechanics::AiPackage::AiPackage() : mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild mTargetActorRefId(""), diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index ec0715e52..a09362baa 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -56,8 +56,7 @@ namespace MWMechanics ///Default constructor AiPackage(); - ///Default Deconstructor - virtual ~AiPackage(); + virtual ~AiPackage() = default; ///Clones the package virtual AiPackage *clone() const = 0; diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index ea83a10e5..3f2c2923e 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -17,7 +17,7 @@ namespace MWMechanics /** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them. Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the path is completed). **/ - class AiPursue : public AiPackage + class AiPursue final : public AiPackage { public: ///Constructor @@ -26,16 +26,16 @@ namespace MWMechanics AiPursue(const ESM::AiSequence::AiPursue* pursue); - virtual AiPursue *clone() const; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); - virtual int getTypeId() const; + AiPursue *clone() const final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + int getTypeId() const final; - 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; } + bool canCancel() const final { return false; } + bool shouldCancelPreviousAi() const final { return false; } }; } #endif diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 5f3931fcf..00d44202a 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -212,7 +212,8 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac return; } - MWMechanics::AiPackage* package = mPackages.front(); + auto packageIt = mPackages.begin(); + MWMechanics::AiPackage* package = *packageIt; if (!package->alwaysActive() && outOfRange) return; @@ -223,7 +224,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac // if active package is combat one, choose nearest target if (packageTypeId == AiPackage::TypeIdCombat) { - std::list::iterator itActualCombat; + auto itActualCombat = mPackages.end(); float nearestDist = std::numeric_limits::max(); osg::Vec3f vActorPos = actor.getRefData().getPosition().asVec3(); @@ -265,16 +266,18 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } } - if (!mPackages.empty()) - { - if (nearestDist < std::numeric_limits::max() && mPackages.begin() != itActualCombat) - { - // move combat package with nearest target to the front - mPackages.splice(mPackages.begin(), mPackages, itActualCombat); - } + assert(!mPackages.empty()); - package = mPackages.front(); + if (nearestDist < std::numeric_limits::max() && mPackages.begin() != itActualCombat) + { + assert(itActualCombat != mPackages.end()); + // move combat package with nearest target to the front + mPackages.splice(mPackages.begin(), mPackages, itActualCombat); } + + packageIt = mPackages.begin(); + package = *packageIt; + packageTypeId = package->getTypeId(); } try @@ -289,9 +292,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } // To account for the rare case where AiPackage::execute() queued another AI package // (e.g. AiPursue executing a dialogue script that uses startCombat) - std::list::iterator toRemove = - std::find(mPackages.begin(), mPackages.end(), package); - mPackages.erase(toRemove); + mPackages.erase(packageIt); delete package; if (isActualAiPackage(packageTypeId)) mDone = true; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index dba70316b..822523c76 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -3,6 +3,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" @@ -43,14 +44,15 @@ namespace MWMechanics bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { - auto& stats = actor.getClass().getCreatureStats(actor); + MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager(); - if (stats.isTurningToPlayer() || stats.getGreetingState() == Greet_InProgress) + if (mechMgr->isTurningToPlayer(actor) || mechMgr->getGreetingState(actor) == Greet_InProgress) return false; const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); const osg::Vec3f targetPos(mX, mY, mZ); + auto& stats = actor.getClass().getCreatureStats(actor); stats.setMovementFlag(CreatureStats::Flag_Run, false); stats.setDrawState(DrawState_Nothing); diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index e7895462f..43b6c9d16 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -14,7 +14,7 @@ namespace AiSequence namespace MWMechanics { /// \brief Causes the AI to travel to the specified point - class AiTravel : public AiPackage + class AiTravel final : public AiPackage { public: /// Default constructor @@ -22,21 +22,21 @@ namespace MWMechanics AiTravel(const ESM::AiSequence::AiTravel* travel); /// Simulates the passing of time - virtual void fastForward(const MWWorld::Ptr& actor, AiState& state); + void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - void writeState(ESM::AiSequence::AiSequence &sequence) const; + void writeState(ESM::AiSequence::AiSequence &sequence) const final; - virtual AiTravel *clone() const; + AiTravel *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual bool useVariableSpeed() const { return true;} + bool useVariableSpeed() const final { return true; } - virtual bool alwaysActive() const { return true; } + bool alwaysActive() const final { return true; } - virtual osg::Vec3f getDestination() const { return osg::Vec3f(mX, mY, mZ); } + osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } private: float mX; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 53e80f54e..2c40c1ba5 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -89,6 +89,11 @@ namespace MWMechanics const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())); return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor); } + + void stopMovement(const MWWorld::Ptr& actor) + { + actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + } } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): @@ -165,7 +170,7 @@ namespace MWMechanics * actors will enter combat (i.e. no longer wandering) and different pathfinding * will kick in. */ - bool AiWander::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) + bool AiWander::execute (const MWWorld::Ptr& actor, CharacterController& /*characterController*/, AiState& state, float duration) { MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor); if (cStats.isDead() || cStats.getHealth().getCurrent() <= 0) @@ -201,12 +206,12 @@ namespace MWMechanics storage.setState(AiWanderStorage::Wander_Walking); } - GreetingState greetingState = cStats.getGreetingState(); + GreetingState greetingState = MWBase::Environment::get().getMechanicsManager()->getGreetingState(actor); if (greetingState == Greet_InProgress) { if (storage.mState == AiWanderStorage::Wander_Walking) { - stopWalking(actor, storage, false); + stopMovement(actor); mObstacleCheck.clear(); storage.setState(AiWanderStorage::Wander_IdleNow); } @@ -230,8 +235,9 @@ namespace MWMechanics if (mDistance <= 0) storage.mCanWanderAlongPathGrid = false; - if (isPackageCompleted(actor, storage)) + if (isPackageCompleted()) { + stopWalking(actor); // Reset package so it can be used again mRemainingDuration=mDuration; init(); @@ -315,19 +321,10 @@ namespace MWMechanics return actor.getRefData().getPosition().asVec3(); } - bool AiWander::isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage) + bool AiWander::isPackageCompleted() const { - if (mDuration) - { - // End package if duration is complete - if (mRemainingDuration <= 0) - { - stopWalking(actor, storage); - return true; - } - } - // if get here, not yet completed - return false; + // End package if duration is complete + return mDuration && mRemainingDuration <= 0; } /* @@ -395,7 +392,7 @@ namespace MWMechanics } void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) { - stopWalking(actor, storage); + stopWalking(actor); mObstacleCheck.clear(); storage.setState(AiWanderStorage::Wander_IdleNow); } @@ -445,7 +442,7 @@ namespace MWMechanics } // Check if idle animation finished - GreetingState greetingState = actor.getClass().getCreatureStats(actor).getGreetingState(); + GreetingState greetingState = MWBase::Environment::get().getMechanicsManager()->getGreetingState(actor); if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None)) { if (mPathFinder.isPathConstructed()) @@ -460,13 +457,13 @@ namespace MWMechanics // Is there no destination or are we there yet? if ((!mPathFinder.isPathConstructed()) || pathTo(actor, osg::Vec3f(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE)) { - stopWalking(actor, storage); + stopWalking(actor); storage.setState(AiWanderStorage::Wander_ChooseAction); } else { // have not yet reached the destination - evadeObstacles(actor, duration, storage); + evadeObstacles(actor, storage); } } @@ -497,15 +494,12 @@ namespace MWMechanics storage.setState(AiWanderStorage::Wander_IdleNow); } - void AiWander::evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage) + void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage) { if (mUsePathgrid) { const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); - const float actorTolerance = 2 * actor.getClass().getSpeed(actor) * duration - + 1.2 * std::max(halfExtents.x(), halfExtents.y()); - const float pointTolerance = std::max(MIN_TOLERANCE, actorTolerance); - mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor), pointTolerance); + mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor)); } if (mObstacleCheck.isEvading()) @@ -518,7 +512,7 @@ namespace MWMechanics storage.mTrimCurrentNode = true; trimAllowedNodes(storage.mAllowedNodes, mPathFinder); mObstacleCheck.clear(); - stopWalking(actor, storage); + stopWalking(actor); storage.setState(AiWanderStorage::Wander_MoveNow); } @@ -529,7 +523,7 @@ namespace MWMechanics if (storage.mStuckCount >= getCountBeforeReset(actor)) // something has gone wrong, reset { mObstacleCheck.clear(); - stopWalking(actor, storage); + stopWalking(actor); storage.setState(AiWanderStorage::Wander_ChooseAction); storage.mStuckCount = 0; } @@ -609,14 +603,11 @@ namespace MWMechanics return TypeIdWander; } - void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage, bool clearPath) + void AiWander::stopWalking(const MWWorld::Ptr& actor) { - if (clearPath) - { - mPathFinder.clearPath(); - mHasDestination = false; - } - actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + mPathFinder.clearPath(); + mHasDestination = false; + stopMovement(actor); } bool AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 376be3a25..f6e7f6d0c 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -78,7 +78,7 @@ namespace MWMechanics }; /// \brief Causes the Actor to wander within a specified range - class AiWander : public AiPackage + class AiWander final : public AiPackage { public: /// Constructor @@ -91,23 +91,23 @@ namespace MWMechanics AiWander (const ESM::AiSequence::AiWander* wander); - virtual AiPackage *clone() const; + AiPackage *clone() const final; - virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - virtual int getTypeId() const; + int getTypeId() const final; - virtual bool useVariableSpeed() const { return true;} + bool useVariableSpeed() const final { return true; } - virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; + void writeState(ESM::AiSequence::AiSequence &sequence) const final; - virtual void fastForward(const MWWorld::Ptr& actor, AiState& state); + void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - bool getRepeat() const; + bool getRepeat() const final; - osg::Vec3f getDestination(const MWWorld::Ptr& actor) const; + osg::Vec3f getDestination(const MWWorld::Ptr& actor) const final; - virtual osg::Vec3f getDestination() const + osg::Vec3f getDestination() const final { if (!mHasDestination) return osg::Vec3f(0, 0, 0); @@ -118,7 +118,7 @@ namespace MWMechanics private: // NOTE: mDistance and mDuration must be set already void init(); - void stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage, bool clearPath = true); + void stopWalking(const MWWorld::Ptr& actor); /// Have the given actor play an idle animation /// @return Success or error @@ -126,14 +126,14 @@ namespace MWMechanics bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); short unsigned getRandomIdle(); void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos); - void evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); + void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage); void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage); void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage); bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos); - bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage); + inline bool isPackageCompleted() const; void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance); bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage); diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index 6d3090918..9cee1aa31 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -262,6 +262,8 @@ namespace MWMechanics int duration = 0; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) duration = effect.mDuration; + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce)) + duration = std::max(1, duration); static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() .get().find("fEffectCostMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index aa3c7979b..662355e63 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1013,7 +1013,7 @@ void split(const std::string &s, char delim, std::vector &elems) { } } -void CharacterController::handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, const std::multimap &map) +void CharacterController::handleTextKey(const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, const NifOsg::TextKeyMap& map) { const std::string &evt = key->second; @@ -1368,10 +1368,9 @@ bool CharacterController::updateWeaponState(CharacterState& idle) } } - // Use blending only with 3d-person movement animations for bipedal actors - bool firstPersonPlayer = (mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson()); + // For biped actors, blend weapon animations with lower body animations with higher priority MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon); - if (!firstPersonPlayer && mPtr.getClass().isBipedal(mPtr)) + if (mPtr.getClass().isBipedal(mPtr)) priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; bool forcestateupdate = false; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 29d6023d3..18b7555f0 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -238,8 +238,7 @@ public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); - virtual void handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, - const std::multimap& map); + virtual void handleTextKey(const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, const NifOsg::TextKeyMap& map); // Be careful when to call this, see comment in Actors void updateContinuousVfx(); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 17db0f2df..e4b7e61b6 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -34,53 +34,12 @@ namespace MWMechanics mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1), - mDeathAnimation(-1), mTimeOfDeath(), mGreetingState(Greet_None), - mGreetingTimer(0), mTargetAngleRadians(0), mIsTurningToPlayer(false), mLevel (0) + mDeathAnimation(-1), mTimeOfDeath(), mLevel (0) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; } - int MWMechanics::CreatureStats::getGreetingTimer() const - { - return mGreetingTimer; - } - - void MWMechanics::CreatureStats::setGreetingTimer(int timer) - { - mGreetingTimer = timer; - } - - float MWMechanics::CreatureStats::getAngleToPlayer() const - { - return mTargetAngleRadians; - } - - void MWMechanics::CreatureStats::setAngleToPlayer(float angle) - { - mTargetAngleRadians = angle; - } - - GreetingState MWMechanics::CreatureStats::getGreetingState() const - { - return mGreetingState; - } - - void MWMechanics::CreatureStats::setGreetingState(GreetingState state) - { - mGreetingState = state; - } - - bool MWMechanics::CreatureStats::isTurningToPlayer() const - { - return mIsTurningToPlayer; - } - - void MWMechanics::CreatureStats::setTurningToPlayer(bool turning) - { - mIsTurningToPlayer = turning; - } - const AiSequence& CreatureStats::getAiSequence() const { return mAiSequence; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 57e9c8363..02f04dcc4 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -19,13 +19,6 @@ namespace ESM namespace MWMechanics { - enum GreetingState - { - Greet_None, - Greet_InProgress, - Greet_Done - }; - /// \brief Common creature stats /// /// @@ -77,11 +70,6 @@ namespace MWMechanics MWWorld::TimeStamp mTimeOfDeath; - GreetingState mGreetingState; - int mGreetingTimer; - float mTargetAngleRadians; - bool mIsTurningToPlayer; - public: typedef std::pair SummonKey; // private: @@ -97,18 +85,6 @@ namespace MWMechanics public: CreatureStats(); - 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); - DrawState_ getDrawState() const; void setDrawState(DrawState_ state); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 729065bfb..83d37bd1a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1,5 +1,7 @@ #include "mechanicsmanagerimp.hpp" +#include + #include #include @@ -2097,4 +2099,29 @@ namespace MWMechanics mActors.cleanupSummonedCreature(caster.getClass().getCreatureStats(caster), creatureActorId); } + void MechanicsManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const + { + stats.setAttribute(frameNumber, "Mechanics Actors", mActors.size()); + stats.setAttribute(frameNumber, "Mechanics Objects", mObjects.size()); + } + + int MechanicsManager::getGreetingTimer(const MWWorld::Ptr &ptr) const + { + return mActors.getGreetingTimer(ptr); + } + + float MechanicsManager::getAngleToPlayer(const MWWorld::Ptr &ptr) const + { + return mActors.getAngleToPlayer(ptr); + } + + GreetingState MechanicsManager::getGreetingState(const MWWorld::Ptr &ptr) const + { + return mActors.getGreetingState(ptr); + } + + bool MechanicsManager::isTurningToPlayer(const MWWorld::Ptr &ptr) const + { + return mActors.isTurningToPlayer(ptr); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 263314556..42644f733 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -270,6 +270,13 @@ namespace MWMechanics virtual bool isRunning(const MWWorld::Ptr& ptr) override; virtual bool isSneaking(const MWWorld::Ptr& ptr) override; + virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; + + virtual int getGreetingTimer(const MWWorld::Ptr& ptr) const override; + virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const override; + virtual GreetingState getGreetingState(const MWWorld::Ptr& ptr) const override; + virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const override; + private: bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set &playerFollowers); diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp index 1bcf646a4..5160114a3 100644 --- a/apps/openmw/mwmechanics/objects.hpp +++ b/apps/openmw/mwmechanics/objects.hpp @@ -52,6 +52,11 @@ namespace MWMechanics void persistAnimationStates(); void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector& out); + + std::size_t size() const + { + return mObjects.size(); + } }; } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index a7bba5b63..b072f55f8 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -361,7 +361,7 @@ namespace MWMechanics } void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, - const DetourNavigator::Flags flags, const float pointTolerance) + const DetourNavigator::Flags flags) { if (mPath.empty()) return; diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 06b4aa10d..cb33471ca 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -97,7 +97,7 @@ namespace MWMechanics const DetourNavigator::Flags flags); void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, - const DetourNavigator::Flags flags, const float pointTolerance); + const DetourNavigator::Flags flags); /// Remove front point if exist and within tolerance void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index a1a0f711c..399b51295 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -200,6 +200,7 @@ namespace MWMechanics effect.mEffectId = effectIt->mEffectID; effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; effect.mMagnitude = magnitude; + effect.mTimeLeft = 0.f; // Avoid applying absorb effects if the caster is the target // We still need the spell to be added @@ -224,10 +225,15 @@ namespace MWMechanics } bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); - if (hasDuration && effectIt->mDuration == 0) + effect.mDuration = hasDuration ? static_cast(effectIt->mDuration) : 1.f; + + bool appliedOnce = magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce; + if (!appliedOnce) + effect.mDuration = std::max(1.f, effect.mDuration); + + if (effect.mDuration == 0) { // We still should add effect to list to allow GetSpellEffects to detect this spell - effect.mDuration = 0.f; appliedLastingEffects.push_back(effect); // duration 0 means apply full magnitude instantly @@ -264,7 +270,7 @@ namespace MWMechanics } else { - effect.mDuration = hasDuration ? static_cast(effectIt->mDuration) : 1.f; + effect.mTimeLeft = effect.mDuration; targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 9428beafc..81658193d 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -394,8 +394,9 @@ namespace MWMechanics priority = 10; const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); const DynamicStat& current = stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth); + // NB: this currently assumes the hardcoded magic effect flags are used const float magnitude = (effect.mMagnMin + effect.mMagnMax)/2.f; - const float toHeal = magnitude * effect.mDuration; + const float toHeal = magnitude * std::max(1, effect.mDuration); // Effect doesn't heal more than we need, *or* we are below 1/2 health if (current.getModified() - current.getCurrent() > toHeal || current.getCurrent() < current.getModified()*0.5) diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index def3bbbc8..bb4953e48 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -31,9 +31,12 @@ namespace MWMechanics magicEffect = store.get().find(effect.mEffectID); bool hasMagnitude = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude); bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); + bool appliedOnce = magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce; int minMagn = hasMagnitude ? effect.mMagnMin : 1; int maxMagn = hasMagnitude ? effect.mMagnMax : 1; int duration = hasDuration ? effect.mDuration : 1; + if (!appliedOnce) + duration = std::max(1, duration); static const float fEffectCostMult = store.get().find("fEffectCostMult")->mValue.getFloat(); float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 73c37f48d..70df37c0a 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1,6 +1,7 @@ #include "physicssystem.hpp" #include +#include #include #include @@ -669,7 +670,7 @@ namespace MWPhysics bool cmode = found->second->getCollisionMode(); cmode = !cmode; found->second->enableCollisionMode(cmode); - found->second->enableCollisionBody(cmode); + // NB: Collision body isn't disabled for vanilla TCL compatibility return cmode; } @@ -905,4 +906,11 @@ namespace MWPhysics mCollisionWorld->getBroadphase()->aabbTest(aabbMin, aabbMax, callback); return callback.getResult(); } + + void PhysicsSystem::reportStats(unsigned int frameNumber, osg::Stats& stats) const + { + stats.setAttribute(frameNumber, "Physics Actors", mActors.size()); + stats.setAttribute(frameNumber, "Physics Objects", mObjects.size()); + stats.setAttribute(frameNumber, "Physics HeightFields", mHeightFields.size()); + } } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 8efb670a5..7b6d08edf 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -17,6 +17,7 @@ namespace osg { class Group; class Object; + class Stats; } namespace MWRender @@ -196,6 +197,8 @@ namespace MWPhysics bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const; + void reportStats(unsigned int frameNumber, osg::Stats& stats) const; + private: void updateWater(); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8f3641795..8f8e8c233 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -151,20 +151,8 @@ namespace } }; - NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname) - { - NifOsg::TextKeyMap::const_iterator iter(keys.begin()); - for(;iter != keys.end();++iter) - { - if(iter->second.compare(0, groupname.size(), groupname) == 0 && - iter->second.compare(groupname.size(), 2, ": ") == 0) - break; - } - return iter; - } - - float calcAnimVelocity(const std::multimap& keys, - NifOsg::KeyframeController *nonaccumctrl, const osg::Vec3f& accum, const std::string &groupname) + float calcAnimVelocity(const NifOsg::TextKeyMap& keys, NifOsg::KeyframeController *nonaccumctrl, + const osg::Vec3f& accum, const std::string &groupname) { const std::string start = groupname+": start"; const std::string loopstart = groupname+": loop start"; @@ -179,7 +167,7 @@ namespace // but the animation velocity calculation uses the second one. // As result the animation velocity calculation is not correct, and this incorrect velocity must be replicated, // because otherwise the Creature's Speed (dagoth uthol) would not be sufficient to move fast enough. - NifOsg::TextKeyMap::const_reverse_iterator keyiter(keys.rbegin()); + auto keyiter = keys.rbegin(); while(keyiter != keys.rend()) { if(keyiter->second == start || keyiter->second == loopstart) @@ -553,7 +541,7 @@ namespace MWRender ControllerMap mControllerMap[Animation::sNumBlendMasks]; - const std::multimap& getTextKeys() const; + const NifOsg::TextKeyMap& getTextKeys() const; }; void UpdateVfxCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) @@ -702,7 +690,7 @@ namespace MWRender return 0; } - const std::multimap &Animation::AnimSource::getTextKeys() const + const NifOsg::TextKeyMap &Animation::AnimSource::getTextKeys() const { return mKeyframes->mTextKeys; } @@ -825,7 +813,7 @@ namespace MWRender for(;iter != mAnimSources.end();++iter) { const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); - if(findGroupStart(keys, anim) != keys.end()) + if (keys.hasGroupStart(anim)) return true; } @@ -838,7 +826,7 @@ namespace MWRender { const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); - NifOsg::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); + const auto found = keys.findGroupStart(groupname); if(found != keys.end()) return found->first; } @@ -851,7 +839,7 @@ namespace MWRender { const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); - for(NifOsg::TextKeyMap::const_iterator iterKey(keys.begin()); iterKey != keys.end(); ++iterKey) + for(auto iterKey = keys.begin(); iterKey != keys.end(); ++iterKey) { if(iterKey->second.compare(0, textKey.size(), textKey) == 0) return iterKey->first; @@ -861,8 +849,8 @@ namespace MWRender return -1.f; } - void Animation::handleTextKey(AnimState &state, const std::string &groupname, const std::multimap::const_iterator &key, - const std::multimap& map) + void Animation::handleTextKey(AnimState &state, const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, + const NifOsg::TextKeyMap& map) { const std::string &evt = key->second; @@ -939,7 +927,7 @@ namespace MWRender if (state.mPlaying) { - NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.getTime())); + auto textkey = textkeys.lowerBound(state.getTime()); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, groupname, textkey, textkeys); @@ -955,7 +943,7 @@ namespace MWRender if(state.getTime() >= state.mLoopStopTime) break; - NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.getTime())); + auto textkey = textkeys.lowerBound(state.getTime()); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, groupname, textkey, textkeys); @@ -974,7 +962,7 @@ namespace MWRender { // Look for text keys in reverse. This normally wouldn't matter, but for some reason undeadwolf_2.nif has two // separate walkforward keys, and the last one is supposed to be used. - NifOsg::TextKeyMap::const_reverse_iterator groupend(keys.rbegin()); + auto groupend = keys.rbegin(); for(;groupend != keys.rend();++groupend) { if(groupend->second.compare(0, groupname.size(), groupname) == 0 && @@ -983,7 +971,7 @@ namespace MWRender } std::string starttag = groupname+": "+start; - NifOsg::TextKeyMap::const_reverse_iterator startkey(groupend); + auto startkey = groupend; while(startkey != keys.rend() && startkey->second != starttag) ++startkey; if(startkey == keys.rend() && start == "loop start") @@ -997,7 +985,7 @@ namespace MWRender return false; const std::string stoptag = groupname+": "+stop; - NifOsg::TextKeyMap::const_reverse_iterator stopkey(groupend); + auto stopkey = groupend; while(stopkey != keys.rend() // We have to ignore extra garbage at the end. // The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop". @@ -1030,7 +1018,7 @@ namespace MWRender const std::string loopstarttag = groupname+": loop start"; const std::string loopstoptag = groupname+": loop stop"; - NifOsg::TextKeyMap::const_reverse_iterator key(groupend); + auto key = groupend; for (; key != startkey && key != keys.rend(); ++key) { if (key->first > state.getTime()) @@ -1064,7 +1052,7 @@ namespace MWRender void Animation::resetActiveGroups() { // remove all previous external controllers from the scene graph - for (ControllerMap::iterator it = mActiveControllers.begin(); it != mActiveControllers.end(); ++it) + for (auto it = mActiveControllers.begin(); it != mActiveControllers.end(); ++it) { osg::Node* node = it->first; node->removeUpdateCallback(it->second); @@ -1103,7 +1091,7 @@ namespace MWRender osg::ref_ptr node = getNodeMap().at(it->first); // this should not throw, we already checked for the node existing in addAnimSource node->addUpdateCallback(it->second); - mActiveControllers.insert(std::make_pair(node, it->second)); + mActiveControllers.emplace_back(node, it->second); if (blendMask == 0 && node == mAccumRoot) { @@ -1116,7 +1104,7 @@ namespace MWRender mResetAccumRootCallback->setAccumulate(mAccumulate); } mAccumRoot->addUpdateCallback(mResetAccumRootCallback); - mActiveControllers.insert(std::make_pair(mAccumRoot, mResetAccumRootCallback)); + mActiveControllers.emplace_back(mAccumRoot, mResetAccumRootCallback); } } } @@ -1201,7 +1189,7 @@ namespace MWRender for(;animsrc != mAnimSources.rend();++animsrc) { const NifOsg::TextKeyMap &keys = (*animsrc)->getTextKeys(); - if(findGroupStart(keys, groupname) != keys.end()) + if (keys.hasGroupStart(groupname)) break; } if(animsrc == mAnimSources.rend()) @@ -1280,7 +1268,7 @@ namespace MWRender } const NifOsg::TextKeyMap &textkeys = state.mSource->getTextKeys(); - NifOsg::TextKeyMap::const_iterator textkey(textkeys.upper_bound(state.getTime())); + auto textkey = textkeys.upperBound(state.getTime()); float timepassed = duration * state.mSpeedMult; while(state.mPlaying) @@ -1316,7 +1304,7 @@ namespace MWRender state.setTime(state.mLoopStartTime); state.mPlaying = true; - textkey = textkeys.lower_bound(state.getTime()); + textkey = textkeys.lowerBound(state.getTime()); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, stateiter->first, textkey, textkeys); @@ -1827,33 +1815,30 @@ namespace MWRender { mHeadController = nullptr; - if (mPtr.getClass().isBipedal(mPtr)) + NodeMap::const_iterator found = getNodeMap().find("bip01 head"); + if (found == getNodeMap().end()) + return; + + osg::MatrixTransform* node = found->second; + + bool foundKeyframeCtrl = false; + osg::Callback* cb = node->getUpdateCallback(); + while (cb) { - NodeMap::const_iterator found = getNodeMap().find("bip01 head"); - if (found != getNodeMap().end()) + if (dynamic_cast(cb)) { - osg::MatrixTransform* node = found->second; - - bool foundKeyframeCtrl = false; - osg::Callback* cb = node->getUpdateCallback(); - while (cb) - { - if (dynamic_cast(cb)) - { - foundKeyframeCtrl = true; - break; - } - cb = cb->getNestedCallback(); - } - - if (foundKeyframeCtrl) - { - mHeadController = new RotateController(mObjectRoot.get()); - node->addUpdateCallback(mHeadController); - mActiveControllers.insert(std::make_pair(node, mHeadController)); - } + foundKeyframeCtrl = true; + break; } + cb = cb->getNestedCallback(); } + + if (!foundKeyframeCtrl) + return; + + mHeadController = new RotateController(mObjectRoot.get()); + node->addUpdateCallback(mHeadController); + mActiveControllers.emplace_back(node, mHeadController); } void Animation::setHeadPitch(float pitchRadians) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 7b1e1d3e9..2890e7be4 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -5,6 +5,9 @@ #include #include +#include + +#include namespace ESM { @@ -147,8 +150,8 @@ public: class TextKeyListener { public: - virtual void handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, - const std::multimap& map) = 0; + virtual void handleTextKey(const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, + const NifOsg::TextKeyMap& map) = 0; virtual ~TextKeyListener() = default; }; @@ -246,8 +249,7 @@ protected: // Keep track of controllers that we added to our scene graph. // We may need to rebuild these controllers when the active animation groups / sources change. - typedef std::multimap, osg::ref_ptr > ControllerMap; - ControllerMap mActiveControllers; + std::vector, osg::ref_ptr>> mActiveControllers; std::shared_ptr mAnimationTimePtr[sNumBlendMasks]; @@ -296,12 +298,12 @@ protected: * the marker is not found, or if the markers are the same, it returns * false. */ - bool reset(AnimState &state, const std::multimap &keys, + bool reset(AnimState &state, const NifOsg::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, bool loopfallback); - void handleTextKey(AnimState &state, const std::string &groupname, const std::multimap::const_iterator &key, - const std::multimap& map); + void handleTextKey(AnimState &state, const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, + const NifOsg::TextKeyMap& map); /** Sets the root model of the object. * diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 41fadd98e..ec825ca2f 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -956,7 +956,7 @@ void NpcAnimation::addControllers() osg::MatrixTransform* node = found->second.get(); mFirstPersonNeckController = new NeckController(mObjectRoot.get()); node->addUpdateCallback(mFirstPersonNeckController); - mActiveControllers.emplace(node, mFirstPersonNeckController); + mActiveControllers.emplace_back(node, mFirstPersonNeckController); } } else if (mViewMode == VM_Normal) diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 4b9aad998..0ba5baec6 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -231,7 +231,7 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) } void WeaponAnimation::addControllers(const std::map >& nodes, - std::multimap, osg::ref_ptr > &map, osg::Node* objectRoot) + std::vector, osg::ref_ptr>> &map, osg::Node* objectRoot) { for (int i=0; i<2; ++i) { @@ -243,7 +243,7 @@ void WeaponAnimation::addControllers(const std::mapsecond; mSpineControllers[i] = new RotateController(objectRoot); node->addUpdateCallback(mSpineControllers[i]); - map.insert(std::make_pair(node, mSpineControllers[i])); + map.emplace_back(node, mSpineControllers[i]); } } } diff --git a/apps/openmw/mwrender/weaponanimation.hpp b/apps/openmw/mwrender/weaponanimation.hpp index ece0beaa6..a1988703c 100644 --- a/apps/openmw/mwrender/weaponanimation.hpp +++ b/apps/openmw/mwrender/weaponanimation.hpp @@ -41,7 +41,7 @@ namespace MWRender /// Add WeaponAnimation-related controllers to \a nodes and store the added controllers in \a map. void addControllers(const std::map >& nodes, - std::multimap, osg::ref_ptr >& map, osg::Node* objectRoot); + std::vector, osg::ref_ptr>>& map, osg::Node* objectRoot); void deleteControllers(); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index f50603491..d929edf0f 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -496,13 +496,12 @@ namespace MWScript if (!targetPtr.isEmpty() && targetPtr.getCellRef().getRefId() == testedTargetId) targetsAreEqual = true; } - else + else if (testedTargetId == "player") // Currently the player ID is hardcoded { - bool turningToPlayer = creatureStats.isTurningToPlayer(); - bool greeting = creatureStats.getGreetingState() == MWMechanics::Greet_InProgress; + MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager(); + bool greeting = mechMgr->getGreetingState(actor) == MWMechanics::Greet_InProgress; bool sayActive = MWBase::Environment::get().getSoundManager()->sayActive(actor); - if (turningToPlayer || (greeting && sayActive)) - targetsAreEqual = (testedTargetId == "player"); // Currently the player ID is hardcoded + targetsAreEqual = (greeting && sayActive) || mechMgr->isTurningToPlayer(actor); } runtime.push(int(targetsAreEqual)); } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 5a333a5b7..ccc579b30 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -464,5 +464,20 @@ op 0x200030d: RepairedOnMe, explicit op 0x200030e: TestCells op 0x200030f: TestInteriorCells op 0x2000310: ToggleRecastMesh +op 0x2000311: MenuMode +op 0x2000312: Random +op 0x2000313: ScriptRunning +op 0x2000314: StartScript +op 0x2000315: StopScript +op 0x2000316: GetSecondsPassed +op 0x2000317: Enable +op 0x2000318: Disable +op 0x2000319: GetDisabled +op 0x200031a: Enable, explicit +op 0x200031b: Disable, explicit +op 0x200031c: GetDisabled, explicit +op 0x200031d: StartScript, explicit +op 0x200031e: GetDistance +op 0x200031f: GetDistance, explicit -opcodes 0x2000311-0x3ffffff unused +opcodes 0x2000320-0x3ffffff unused diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 49739622c..795bc591b 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -315,20 +315,6 @@ namespace MWScript { } - bool InterpreterContext::menuMode() - { - /* - Start of tes3mp change (major) - - Being in a menu should not pause scripts in multiplayer, so always return false - */ - //return MWBase::Environment::get().getWindowManager()->isGuiMode(); - return false; - /* - End of tes3mp change (major) - */ - } - int InterpreterContext::getGlobalShort (const std::string& name) const { return MWBase::Environment::get().getWorld()->getGlobalInt (name); @@ -627,58 +613,6 @@ namespace MWScript return MWBase::Environment::get().getWorld()->getCellName(); } - bool InterpreterContext::isScriptRunning (const std::string& name) const - { - return MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name); - } - - void InterpreterContext::startScript (const std::string& name, const std::string& targetId) - { - MWWorld::Ptr target; - if (targetId.empty()) - target = getReference(false); - else - target = getReferenceImp(targetId); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, target); - } - - void InterpreterContext::stopScript (const std::string& name) - { - MWBase::Environment::get().getScriptManager()->getGlobalScripts().removeScript (name); - } - - float InterpreterContext::getDistance (const std::string& name, const std::string& id) const - { - // NOTE: id may be empty, indicating an implicit reference - - MWWorld::Ptr ref2 = getReferenceImp(id); - - if (ref2.getContainerStore()) // is the object contained? - { - MWWorld::Ptr container = MWBase::Environment::get().getWorld()->findContainer(ref2); - - if (!container.isEmpty()) - ref2 = container; - else - throw std::runtime_error("failed to find container ptr"); - } - - const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->getPtr(name, false); - - // If the objects are in different worldspaces, return a large value (just like vanilla) - if (!ref.isInCell() || !ref2.isInCell() || ref.getCell()->getCell()->getCellId().mWorldspace != ref2.getCell()->getCell()->getCellId().mWorldspace) - return std::numeric_limits::max(); - - double diff[3]; - - const float* const pos1 = ref.getRefData().getPosition().pos; - const float* const pos2 = ref2.getRefData().getPosition().pos; - for (int i=0; i<3; ++i) - diff[i] = pos1[i] - pos2[i]; - - return static_cast(std::sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])); - } - void InterpreterContext::executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor) { std::shared_ptr action = (ptr.getClass().activate(ptr, actor)); @@ -689,101 +623,6 @@ namespace MWScript } } - float InterpreterContext::getSecondsPassed() const - { - return MWBase::Environment::get().getFrameDuration(); - } - - bool InterpreterContext::isDisabled (const std::string& id) const - { - const MWWorld::Ptr ref = getReferenceImp (id, false); - return !ref.getRefData().isEnabled(); - } - - void InterpreterContext::enable (const std::string& id) - { - MWWorld::Ptr ref = getReferenceImp (id, false); - - /* - Start of tes3mp addition - - Send an ID_OBJECT_STATE packet whenever an object is enabled, as long as - the player is logged in on the server, the object is still disabled, and our last - packet regarding its state did not already attempt to enable it (to prevent - packet spam) - */ - if (mwmp::Main::get().getLocalPlayer()->isLoggedIn()) - { - if (ref.isInCell() && !ref.getRefData().isEnabled() && - ref.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Enabled) - { - ref.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Enabled); - - mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList(); - objectList->reset(); - objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(getContextType()); - objectList->addObjectState(ref, true); - objectList->sendObjectState(); - } - } - /* - End of tes3mp addition - */ - - /* - Start of tes3mp change (major) - - Disable unilateral state enabling on this client and expect the server's reply to our - packet to do it instead - */ - //MWBase::Environment::get().getWorld()->enable (ref); - /* - End of tes3mp change (major) - */ - } - - void InterpreterContext::disable (const std::string& id) - { - MWWorld::Ptr ref = getReferenceImp (id, false); - - /* - Start of tes3mp addition - - Send an ID_OBJECT_STATE packet whenever an object should be disabled, as long as - the player is logged in on the server, the object is still enabled, and our last - packet regarding its state did not already attempt to disable it (to prevent - packet spam) - */ - if (mwmp::Main::get().getLocalPlayer()->isLoggedIn()) - { - if (ref.isInCell() && ref.getRefData().isEnabled() && - ref.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Disabled) - { - ref.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Disabled); - - mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList(); - objectList->reset(); - objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(getContextType()); - objectList->addObjectState(ref, false); - objectList->sendObjectState(); - } - } - /* - End of tes3mp addition - */ - - /* - Start of tes3mp change (major) - - Disable unilateral state disabling on this client and expect the server's reply to our - packet to do it instead - */ - //MWBase::Environment::get().getWorld()->disable (ref); - /* - End of tes3mp change (major) - */ - } - int InterpreterContext::getMemberShort (const std::string& id, const std::string& name, bool global) const { diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index f605ea9bc..eedbbff3b 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -102,8 +102,6 @@ namespace MWScript virtual void report (const std::string& message); ///< By default, do nothing. - virtual bool menuMode(); - virtual int getGlobalShort (const std::string& name) const; virtual int getGlobalLong (const std::string& name) const; @@ -146,26 +144,9 @@ namespace MWScript virtual std::string getCurrentCellName() const; - virtual bool isScriptRunning (const std::string& name) const; - - virtual void startScript (const std::string& name, const std::string& targetId = ""); - - virtual void stopScript (const std::string& name); - - virtual float getDistance (const std::string& name, const std::string& id = "") const; - ///< @note if \a id is empty, assumes an implicit reference - void executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor); ///< Execute the activation action for this ptr. If ptr is mActivated, mark activation as handled. - virtual float getSecondsPassed() const; - - virtual bool isDisabled (const std::string& id = "") const; - - virtual void enable (const std::string& id = ""); - - virtual void disable (const std::string& id = ""); - virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const; virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 0771c65b1..b25e91345 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include #include @@ -92,6 +94,198 @@ namespace MWScript { namespace Misc { + class OpMenuMode : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + /* + Start of tes3mp change (major) + + Being in a menu should not pause scripts in multiplayer, so always return false + */ + //runtime.push (MWBase::Environment::get().getWindowManager()->isGuiMode()); + runtime.push(false); + /* + End of tes3mp change (major) + */ + } + }; + + class OpRandom : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + Interpreter::Type_Integer limit = runtime[0].mInteger; + runtime.pop(); + + if (limit<0) + throw std::runtime_error ( + "random: argument out of range (Don't be so negative!)"); + + runtime.push (static_cast(::Misc::Rng::rollDice(limit))); // [o, limit) + } + }; + + template + class OpStartScript : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr target = R()(runtime, false); + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, target); + } + }; + + class OpScriptRunning : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + runtime.push(MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name)); + } + }; + + class OpStopScript : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().removeScript (name); + } + }; + + class OpGetSecondsPassed : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.push (MWBase::Environment::get().getFrameDuration()); + } + }; + + template + class OpEnable : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + /* + Start of tes3mp addition + + Send an ID_OBJECT_STATE packet whenever an object is enabled, as long as + the player is logged in on the server, the object is still disabled, and our last + packet regarding its state did not already attempt to enable it (to prevent + packet spam) + */ + if (mwmp::Main::get().getLocalPlayer()->isLoggedIn()) + { + if (ptr.isInCell() && !ptr.getRefData().isEnabled() && + ptr.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Enabled) + { + ptr.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Enabled); + + mwmp::ObjectList* objectList = mwmp::Main::get().getNetworking()->getObjectList(); + objectList->reset(); + objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType()); + objectList->addObjectState(ptr, true); + objectList->sendObjectState(); + } + } + /* + End of tes3mp addition + */ + + /* + Start of tes3mp change (major) + + Disable unilateral state enabling on this client and expect the server's reply to our + packet to do it instead + */ + //MWBase::Environment::get().getWorld()->enable (ptr); + /* + End of tes3mp change (major) + */ + } + }; + + template + class OpDisable : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + /* + Start of tes3mp addition + + Send an ID_OBJECT_STATE packet whenever an object should be disabled, as long as + the player is logged in on the server, the object is still enabled, and our last + packet regarding its state did not already attempt to disable it (to prevent + packet spam) + */ + if (mwmp::Main::get().getLocalPlayer()->isLoggedIn()) + { + if (ptr.isInCell() && ptr.getRefData().isEnabled() && + ptr.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Disabled) + { + ptr.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Disabled); + + mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList(); + objectList->reset(); + objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType()); + objectList->addObjectState(ptr, false); + objectList->sendObjectState(); + } + } + /* + End of tes3mp addition + */ + + /* + Start of tes3mp change (major) + + Disable unilateral state disabling on this client and expect the server's reply to our + packet to do it instead + */ + //MWBase::Environment::get().getWorld()->disable (ptr); + /* + End of tes3mp change (major) + */ + } + }; + + template + class OpGetDisabled : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push (!ptr.getRefData().isEnabled()); + } + }; + class OpPlayBink : public Interpreter::Opcode0 { public: @@ -1575,6 +1769,19 @@ namespace MWScript void installOpcodes (Interpreter::Interpreter& interpreter) { + interpreter.installSegment5 (Compiler::Misc::opcodeMenuMode, new OpMenuMode); + interpreter.installSegment5 (Compiler::Misc::opcodeRandom, new OpRandom); + interpreter.installSegment5 (Compiler::Misc::opcodeScriptRunning, new OpScriptRunning); + interpreter.installSegment5 (Compiler::Misc::opcodeStartScript, new OpStartScript); + interpreter.installSegment5 (Compiler::Misc::opcodeStartScriptExplicit, new OpStartScript); + interpreter.installSegment5 (Compiler::Misc::opcodeStopScript, new OpStopScript); + interpreter.installSegment5 (Compiler::Misc::opcodeGetSecondsPassed, new OpGetSecondsPassed); + interpreter.installSegment5 (Compiler::Misc::opcodeEnable, new OpEnable); + interpreter.installSegment5 (Compiler::Misc::opcodeEnableExplicit, new OpEnable); + interpreter.installSegment5 (Compiler::Misc::opcodeDisable, new OpDisable); + interpreter.installSegment5 (Compiler::Misc::opcodeDisableExplicit, new OpDisable); + interpreter.installSegment5 (Compiler::Misc::opcodeGetDisabled, new OpGetDisabled); + interpreter.installSegment5 (Compiler::Misc::opcodeGetDisabledExplicit, new OpGetDisabled); interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); interpreter.installSegment5 (Compiler::Misc::opcodeOnActivate, new OpOnActivate); interpreter.installSegment5 (Compiler::Misc::opcodeOnActivateExplicit, new OpOnActivate); diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 0df319014..fe8ca7cc7 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -56,6 +56,63 @@ namespace MWScript } } + template + class OpGetDistance : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr from = R()(runtime); + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + if (from.getContainerStore()) // is the object contained? + { + MWWorld::Ptr container = MWBase::Environment::get().getWorld()->findContainer(from); + + if (!container.isEmpty()) + from = container; + else + { + std::string error = "Failed to find the container of object '" + from.getCellRef().getRefId() + "'"; + runtime.getContext().report(error); + Log(Debug::Error) << error; + runtime.push(0.f); + return; + } + } + + const MWWorld::Ptr to = MWBase::Environment::get().getWorld()->searchPtr(name, false); + if (to.isEmpty()) + { + std::string error = "Failed to find an instance of object '" + name + "'"; + runtime.getContext().report(error); + Log(Debug::Error) << error; + runtime.push(0.f); + return; + } + + float distance; + // If the objects are in different worldspaces, return a large value (just like vanilla) + if (!to.isInCell() || !from.isInCell() || to.getCell()->getCell()->getCellId().mWorldspace != from.getCell()->getCell()->getCellId().mWorldspace) + distance = std::numeric_limits::max(); + else + { + double diff[3]; + + const float* const pos1 = to.getRefData().getPosition().pos; + const float* const pos2 = from.getRefData().getPosition().pos; + for (int i=0; i<3; ++i) + diff[i] = pos1[i] - pos2[i]; + + distance = static_cast(std::sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])); + } + + runtime.push(distance); + } + }; + template class OpSetScale : public Interpreter::Opcode0 { @@ -902,6 +959,8 @@ namespace MWScript void installOpcodes (Interpreter::Interpreter& interpreter) { + interpreter.installSegment5(Compiler::Transformation::opcodeGetDistance, new OpGetDistance); + interpreter.installSegment5(Compiler::Transformation::opcodeGetDistanceExplicit, new OpGetDistance); interpreter.installSegment5(Compiler::Transformation::opcodeSetScale,new OpSetScale); interpreter.installSegment5(Compiler::Transformation::opcodeSetScaleExplicit,new OpSetScale); interpreter.installSegment5(Compiler::Transformation::opcodeSetAngle,new OpSetAngle); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 929208f4b..855bd7077 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -240,12 +240,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot ESM::ESMWriter writer; - const std::vector& current = - MWBase::Environment::get().getWorld()->getContentFiles(); - - for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); - ++iter) - writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 + for (const std::string& contentFile : MWBase::Environment::get().getWorld()->getContentFiles()) + writer.addMaster(contentFile, 0); // not using the size information anyway -> use value of 0 writer.setFormat (ESM::SavedGame::sCurrentFormat); @@ -356,10 +352,10 @@ void MWState::StateManager::quickSave (std::string name) if (currentCharacter) { - for (Character::SlotIterator it = currentCharacter->begin(); it != currentCharacter->end(); ++it) + for (auto& save : *currentCharacter) { //Visiting slots allows the quicksave finder to find the oldest quicksave - saveFinder.visitSave(&*it); + saveFinder.visitSave(&save); } } @@ -370,12 +366,10 @@ void MWState::StateManager::quickSave (std::string name) void MWState::StateManager::loadGame(const std::string& filepath) { - for (CharacterIterator it = mCharacterManager.begin(); it != mCharacterManager.end(); ++it) + for (const auto& character : mCharacterManager) { - const MWState::Character& character = *it; - for (MWState::Character::SlotIterator slotIt = character.begin(); slotIt != character.end(); ++slotIt) + for (const auto& slot : character) { - const MWState::Slot& slot = *slotIt; if (slot.mPath == boost::filesystem::path(filepath)) { loadGame(&character, slot.mPath.string()); @@ -650,13 +644,12 @@ bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const { const std::vector& selectedContentFiles = MWBase::Environment::get().getWorld()->getContentFiles(); bool notFound = false; - for (std::vector::const_iterator it = profile.mContentFiles.begin(); - it != profile.mContentFiles.end(); ++it) + for (const std::string& contentFile : profile.mContentFiles) { - if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), *it) + if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), contentFile) == selectedContentFiles.end()) { - Log(Debug::Warning) << "Warning: Saved game dependency " << *it << " is missing."; + Log(Debug::Warning) << "Warning: Saved game dependency " << contentFile << " is missing."; notFound = true; } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4e4876065..e89cbd5f6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -785,7 +785,10 @@ namespace MWWorld Ptr ret = searchPtr(name, activeOnly); if (!ret.isEmpty()) return ret; - throw std::runtime_error ("unknown ID: " + name); + std::string error = "failed to find an instance of object '" + name + "'"; + if (activeOnly) + error += " in active cells"; + throw std::runtime_error(error); } Ptr World::searchPtrViaActorId (int actorId) @@ -4509,4 +4512,10 @@ namespace MWWorld { return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore); } + + void World::reportStats(unsigned int frameNumber, osg::Stats& stats) const + { + mNavigator->reportStats(frameNumber, stats); + mPhysics->reportStats(frameNumber, stats); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7e5596c46..76cb444a3 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -20,6 +20,7 @@ namespace osg { class Group; + class Stats; } namespace osgViewer @@ -920,6 +921,8 @@ namespace MWWorld bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override; bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const override; + + void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; }; } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 188c0dd6f..19c292a23 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -114,7 +114,7 @@ add_component_dir (compiler add_component_dir (interpreter context controlopcodes genericopcodes installopcodes interpreter localopcodes mathopcodes - miscopcodes opcodes runtime scriptopcodes spatialopcodes types defines + miscopcodes opcodes runtime types defines ) add_component_dir (translation @@ -368,3 +368,7 @@ endif() # Make the variable accessible for other subdirectories set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) + +if (BULLET_USE_DOUBLES) + add_definitions(-DBT_USE_DOUBLE_PRECISION) +endif() diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 20873385b..8c8fe640e 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -372,9 +372,7 @@ namespace Compiler keyword==Scanner::K_elseif || keyword==Scanner::K_while || keyword==Scanner::K_endwhile || keyword==Scanner::K_return || keyword==Scanner::K_messagebox || keyword==Scanner::K_set || - keyword==Scanner::K_to || keyword==Scanner::K_startscript || - keyword==Scanner::K_stopscript || keyword==Scanner::K_enable || - keyword==Scanner::K_disable) + keyword==Scanner::K_to) { return parseName (loc.mLiteral, loc, scanner); } @@ -385,53 +383,6 @@ namespace Compiler { if (mRefOp && mNextOperand) { - if (keyword==Scanner::K_getdisabled) - { - start(); - - mTokenLoc = loc; - - Generator::getDisabled (mCode, mLiterals, mExplicit); - mOperands.push_back ('l'); - mExplicit.clear(); - mRefOp = false; - - std::vector ignore; - parseArguments ("x", scanner, ignore); - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_getdistance) - { - start(); - - mTokenLoc = loc; - parseArguments ("c", scanner); - - Generator::getDistance (mCode, mLiterals, mExplicit); - mOperands.push_back ('f'); - mExplicit.clear(); - mRefOp = false; - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_scriptrunning) - { - start(); - - mTokenLoc = loc; - parseArguments ("c", scanner); - - Generator::scriptRunning (mCode); - mOperands.push_back ('l'); - - mExplicit.clear(); - mRefOp = false; - mNextOperand = false; - return true; - } // check for custom extensions if (const Extensions *extensions = getContext().getExtensions()) @@ -483,84 +434,6 @@ namespace Compiler mNextOperand = false; return true; } - else if (keyword==Scanner::K_menumode) - { - start(); - - mTokenLoc = loc; - - Generator::menuMode (mCode); - mOperands.push_back ('l'); - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_random) - { - start(); - - mTokenLoc = loc; - parseArguments ("l", scanner); - - Generator::random (mCode); - mOperands.push_back ('f'); - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_scriptrunning) - { - start(); - - mTokenLoc = loc; - parseArguments ("c", scanner); - - Generator::scriptRunning (mCode); - mOperands.push_back ('l'); - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_getdistance) - { - start(); - - mTokenLoc = loc; - parseArguments ("c", scanner); - - Generator::getDistance (mCode, mLiterals, ""); - mOperands.push_back ('f'); - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_getsecondspassed) - { - start(); - - mTokenLoc = loc; - - Generator::getSecondsPassed (mCode); - mOperands.push_back ('f'); - - mNextOperand = false; - return true; - } - else if (keyword==Scanner::K_getdisabled) - { - start(); - - mTokenLoc = loc; - - Generator::getDisabled (mCode, mLiterals, ""); - mOperands.push_back ('l'); - - std::vector ignore; - parseArguments ("x", scanner, ignore); - - mNextOperand = false; - return true; - } else { // check for custom extensions diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index f23d2d86e..c67af29c2 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -241,6 +241,15 @@ namespace Compiler { void registerExtensions (Extensions& extensions) { + extensions.registerFunction ("menumode", 'l', "", opcodeMenuMode); + extensions.registerFunction ("random", 'f', "l", opcodeRandom); + extensions.registerFunction ("scriptrunning", 'l', "c", opcodeScriptRunning); + extensions.registerInstruction ("startscript", "c", opcodeStartScript, opcodeStartScriptExplicit); + extensions.registerInstruction ("stopscript", "c", opcodeStopScript); + extensions.registerFunction ("getsecondspassed", 'f', "", opcodeGetSecondsPassed); + extensions.registerInstruction ("enable", "", opcodeEnable, opcodeEnableExplicit); + extensions.registerInstruction ("disable", "", opcodeDisable, opcodeDisableExplicit); + extensions.registerFunction ("getdisabled", 'l', "x", opcodeGetDisabled, opcodeGetDisabledExplicit); extensions.registerFunction ("xbox", 'l', "", opcodeXBox); extensions.registerFunction ("onactivate", 'l', "", opcodeOnActivate, opcodeOnActivateExplicit); extensions.registerInstruction ("activate", "x", opcodeActivate, opcodeActivateExplicit); @@ -533,6 +542,7 @@ namespace Compiler { void registerExtensions (Extensions& extensions) { + extensions.registerFunction("getdistance",'f',"c",opcodeGetDistance,opcodeGetDistanceExplicit); extensions.registerInstruction("setscale","f",opcodeSetScale,opcodeSetScaleExplicit); extensions.registerFunction("getscale",'f',"",opcodeGetScale,opcodeGetScaleExplicit); extensions.registerInstruction("setangle","cf",opcodeSetAngle,opcodeSetAngleExplicit); diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp index 7e6437e20..2787488c2 100644 --- a/components/compiler/generator.cpp +++ b/components/compiler/generator.cpp @@ -222,11 +222,6 @@ namespace code.push_back (Compiler::Generator::segment5 (37)); } - void opMenuMode (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (38)); - } - void opStoreGlobalShort (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (39)); @@ -286,71 +281,6 @@ namespace { code.push_back (Compiler::Generator::segment5 (global ? 70 : 64)); } - - void opRandom (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (45)); - } - - void opScriptRunning (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (46)); - } - - void opStartScript (Compiler::Generator::CodeContainer& code, bool targeted) - { - code.push_back (Compiler::Generator::segment5 (targeted ? 71 : 47)); - } - - void opStopScript (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (48)); - } - - void opGetDistance (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (49)); - } - - void opGetSecondsPassed (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (50)); - } - - void opEnable (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (51)); - } - - void opDisable (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (52)); - } - - void opGetDisabled (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (53)); - } - - void opEnableExplicit (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (54)); - } - - void opDisableExplicit (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (55)); - } - - void opGetDisabledExplicit (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (56)); - } - - void opGetDistanceExplicit (Compiler::Generator::CodeContainer& code) - { - code.push_back (Compiler::Generator::segment5 (57)); - } } namespace Compiler @@ -645,11 +575,6 @@ namespace Compiler } } - void menuMode (CodeContainer& code) - { - opMenuMode (code); - } - void assignToGlobal (CodeContainer& code, Literals& literals, char localType, const std::string& name, const CodeContainer& value, char valueType) { @@ -806,93 +731,5 @@ namespace Compiler assert (0); } } - - void random (CodeContainer& code) - { - opRandom (code); - } - - void scriptRunning (CodeContainer& code) - { - opScriptRunning (code); - } - - void startScript (CodeContainer& code, Literals& literals, const std::string& id) - { - if (id.empty()) - opStartScript (code, false); - else - { - int index = literals.addString (id); - opPushInt (code, index); - opStartScript (code, true); - } - } - - void stopScript (CodeContainer& code) - { - opStopScript (code); - } - - void getDistance (CodeContainer& code, Literals& literals, const std::string& id) - { - if (id.empty()) - { - opGetDistance (code); - } - else - { - int index = literals.addString (id); - opPushInt (code, index); - opGetDistanceExplicit (code); - } - } - - void getSecondsPassed (CodeContainer& code) - { - opGetSecondsPassed (code); - } - - void getDisabled (CodeContainer& code, Literals& literals, const std::string& id) - { - if (id.empty()) - { - opGetDisabled (code); - } - else - { - int index = literals.addString (id); - opPushInt (code, index); - opGetDisabledExplicit (code); - } - } - - void enable (CodeContainer& code, Literals& literals, const std::string& id) - { - if (id.empty()) - { - opEnable (code); - } - else - { - int index = literals.addString (id); - opPushInt (code, index); - opEnableExplicit (code); - } - } - - void disable (CodeContainer& code, Literals& literals, const std::string& id) - { - if (id.empty()) - { - opDisable (code); - } - else - { - int index = literals.addString (id); - opPushInt (code, index); - opDisableExplicit (code); - } - } } } diff --git a/components/compiler/generator.hpp b/components/compiler/generator.hpp index a56d7c1f1..55bba2a75 100644 --- a/components/compiler/generator.hpp +++ b/components/compiler/generator.hpp @@ -91,8 +91,6 @@ namespace Compiler void compare (CodeContainer& code, char op, char valueType1, char valueType2); - void menuMode (CodeContainer& code); - void assignToGlobal (CodeContainer& code, Literals& literals, char localType, const std::string& name, const CodeContainer& value, char valueType); @@ -106,24 +104,6 @@ namespace Compiler void fetchMember (CodeContainer& code, Literals& literals, char memberType, const std::string& name, const std::string& id, bool global); ///< \param global Member of a global script instead of a script of a reference. - - void random (CodeContainer& code); - - void scriptRunning (CodeContainer& code); - - void startScript (CodeContainer& code, Literals& literals, const std::string& id); - - void stopScript (CodeContainer& code); - - void getDistance (CodeContainer& code, Literals& literals, const std::string& id); - - void getSecondsPassed (CodeContainer& code); - - void getDisabled (CodeContainer& code, Literals& literals, const std::string& id); - - void enable (CodeContainer& code, Literals& literals, const std::string& id); - - void disable (CodeContainer& code, Literals& literals, const std::string& id); } } diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 646685b39..326b5f9f6 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -247,35 +247,6 @@ namespace Compiler if (mState==BeginState || mState==ExplicitState) { - switch (keyword) - { - case Scanner::K_enable: - - Generator::enable (mCode, mLiterals, mExplicit); - mState = PotentialEndState; - return true; - - case Scanner::K_disable: - - Generator::disable (mCode, mLiterals, mExplicit); - mState = PotentialEndState; - return true; - - case Scanner::K_startscript: - - mExprParser.parseArguments ("c", scanner, mCode); - Generator::startScript (mCode, mLiterals, mExplicit); - mState = EndState; - return true; - - case Scanner::K_stopscript: - - mExprParser.parseArguments ("c", scanner, mCode); - Generator::stopScript (mCode); - mState = EndState; - return true; - } - // check for custom extensions if (const Extensions *extensions = getContext().getExtensions()) { @@ -323,21 +294,6 @@ namespace Compiler } } - if (keyword==Scanner::K_getdisabled || keyword==Scanner::K_getdistance) - { - if (mAllowExpression) - { - scanner.putbackKeyword (keyword, loc); - parseExpression (scanner, loc); - } - else - { - getErrorHandler().warning ("Unexpected naked expression", loc); - } - mState = EndState; - return true; - } - if (const Extensions *extensions = getContext().getExtensions()) { char returnType; @@ -416,13 +372,6 @@ namespace Compiler mState = EndState; return true; - case Scanner::K_stopscript: - - mExprParser.parseArguments ("c", scanner, mCode); - Generator::stopScript (mCode); - mState = EndState; - return true; - case Scanner::K_else: getErrorHandler().warning ("Stray else", loc); @@ -484,19 +433,6 @@ namespace Compiler return true; } - if (mAllowExpression) - { - if (keyword==Scanner::K_getsquareroot || keyword==Scanner::K_menumode || - keyword==Scanner::K_random || keyword==Scanner::K_scriptrunning || - keyword==Scanner::K_getsecondspassed) - { - scanner.putbackKeyword (keyword, loc); - parseExpression (scanner, loc); - mState = EndState; - return true; - } - } - return Parser::parseKeyword (keyword, loc, scanner); } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index d3446e688..fdc3e0eab 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -305,6 +305,19 @@ namespace Compiler const int opcodeRepairedOnMe = 0x200030c; const int opcodeRepairedOnMeExplicit = 0x200030d; const int opcodeToggleRecastMesh = 0x2000310; + const int opcodeMenuMode = 0x2000311; + const int opcodeRandom = 0x2000312; + const int opcodeScriptRunning = 0x2000313; + const int opcodeStartScript = 0x2000314; + const int opcodeStopScript = 0x2000315; + const int opcodeGetSecondsPassed = 0x2000316; + const int opcodeEnable = 0x2000317; + const int opcodeDisable = 0x2000318; + const int opcodeGetDisabled = 0x2000319; + const int opcodeEnableExplicit = 0x200031a; + const int opcodeDisableExplicit = 0x200031b; + const int opcodeGetDisabledExplicit = 0x200031c; + const int opcodeStartScriptExplicit = 0x200031d; } namespace Sky @@ -513,6 +526,8 @@ namespace Compiler const int opcodeMoveWorldExplicit = 0x2000209; const int opcodeResetActors = 0x20002f4; const int opcodeFixme = 0x2000302; + const int opcodeGetDistance = 0x200031e; + const int opcodeGetDistanceExplicit = 0x200031f; } namespace User diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 83686e4c1..9f2865868 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -266,12 +266,6 @@ namespace Compiler "messagebox", "set", "to", "getsquareroot", - "menumode", - "random", - "startscript", "stopscript", "scriptrunning", - "getdistance", - "getsecondspassed", - "enable", "disable", "getdisabled", 0 }; diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index b8d057677..b6321a92d 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -208,13 +208,7 @@ namespace Compiler K_return, K_messagebox, K_set, K_to, - K_getsquareroot, - K_menumode, - K_random, - K_startscript, K_stopscript, K_scriptrunning, - K_getdistance, - K_getsecondspassed, - K_enable, K_disable, K_getdisabled + K_getsquareroot }; enum special diff --git a/components/compiler/stringparser.cpp b/components/compiler/stringparser.cpp index ad88bb857..1bacf7941 100644 --- a/components/compiler/stringparser.cpp +++ b/components/compiler/stringparser.cpp @@ -63,12 +63,7 @@ namespace Compiler keyword==Scanner::K_elseif || keyword==Scanner::K_while || keyword==Scanner::K_endwhile || keyword==Scanner::K_return || keyword==Scanner::K_messagebox || keyword==Scanner::K_set || - keyword==Scanner::K_to || keyword==Scanner::K_startscript || - keyword==Scanner::K_stopscript || keyword==Scanner::K_enable || - keyword==Scanner::K_disable || keyword==Scanner::K_getdisabled || - keyword==Scanner::K_getdistance || keyword==Scanner::K_scriptrunning || - keyword==Scanner::K_getsquareroot || keyword==Scanner::K_menumode || - keyword==Scanner::K_random || keyword==Scanner::K_getsecondspassed) + keyword==Scanner::K_to || keyword==Scanner::K_getsquareroot) { return parseName (loc.mLiteral, loc, scanner); } diff --git a/components/esm/activespells.cpp b/components/esm/activespells.cpp index 4f51a619e..46558ceb7 100644 --- a/components/esm/activespells.cpp +++ b/components/esm/activespells.cpp @@ -16,7 +16,6 @@ namespace ESM esm.writeHNT ("CAST", params.mCasterActorId); esm.writeHNString ("DISP", params.mDisplayName); - esm.writeHNT ("TIME", params.mTimeStamp); for (std::vector::const_iterator effectIt = params.mEffects.begin(); effectIt != params.mEffects.end(); ++effectIt) { @@ -25,12 +24,15 @@ namespace ESM esm.writeHNT ("ARG_", effectIt->mArg); esm.writeHNT ("MAGN", effectIt->mMagnitude); esm.writeHNT ("DURA", effectIt->mDuration); + esm.writeHNT ("LEFT", effectIt->mTimeLeft); } } } void ActiveSpells::load(ESMReader &esm) { + int format = esm.getFormat(); + while (esm.isNextSub("ID__")) { std::string spellId = esm.getHString(); @@ -38,7 +40,10 @@ namespace ESM ActiveSpellParams params; esm.getHNT (params.mCasterActorId, "CAST"); params.mDisplayName = esm.getHNString ("DISP"); - esm.getHNT (params.mTimeStamp, "TIME"); + + // spell casting timestamp, no longer used + if (esm.isNextSub("TIME")) + esm.skipHSub(); while (esm.isNextSub("MGEF")) { @@ -48,6 +53,11 @@ namespace ESM esm.getHNOT(effect.mArg, "ARG_"); esm.getHNT (effect.mMagnitude, "MAGN"); esm.getHNT (effect.mDuration, "DURA"); + if (format < 9) + effect.mTimeLeft = effect.mDuration; + else + esm.getHNT (effect.mTimeLeft, "LEFT"); + params.mEffects.push_back(effect); } mSpells.insert(std::make_pair(spellId, params)); diff --git a/components/esm/activespells.hpp b/components/esm/activespells.hpp index d9e9a8c63..20b2f652d 100644 --- a/components/esm/activespells.hpp +++ b/components/esm/activespells.hpp @@ -21,6 +21,7 @@ namespace ESM float mMagnitude; int mArg; // skill or attribute float mDuration; + float mTimeLeft; }; // format 0, saved games only @@ -29,7 +30,6 @@ namespace ESM struct ActiveSpellParams { std::vector mEffects; - ESM::TimeStamp mTimeStamp; std::string mDisplayName; int mCasterActorId; }; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index f2ebc7bf0..6696ed478 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 8; +int ESM::SavedGame::sCurrentFormat = 9; void ESM::SavedGame::load (ESMReader &esm) { diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 7f15a7efe..92d1a9ff0 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include namespace bconv = boost::locale::conv; diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index dae83a1f9..6dcebe0bd 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -21,6 +21,8 @@ #include +#include + namespace { unsigned long utf8ToUnicode(const std::string& utf8) @@ -147,15 +149,24 @@ namespace Gui FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath) : mVFS(vfs) , mUserDataPath(userDataPath) + , mFontHeight(16) { if (encoding == ToUTF8::WINDOWS_1252) mEncoding = ToUTF8::CP437; else mEncoding = encoding; + + int fontSize = Settings::Manager::getInt("font size", "GUI"); + mFontHeight = std::min(std::max(12, fontSize), 20); + + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); + MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate("Resource") = MyGUI::newDelegate(this, &FontLoader::loadFontFromXml); } FontLoader::~FontLoader() { + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); + for (std::vector::iterator it = mTextures.begin(); it != mTextures.end(); ++it) delete *it; mTextures.clear(); @@ -190,7 +201,7 @@ namespace Gui { size_t pos = name.find_last_of('.'); if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".fnt") == 0) - loadFont(name, exportToFile); + loadBitmapFont(name, exportToFile); } else break; @@ -238,7 +249,7 @@ namespace Gui float ascent; } GlyphInfo; - void FontLoader::loadFont(const std::string &fileName, bool exportToFile) + void FontLoader::loadBitmapFont(const std::string &fileName, bool exportToFile) { Files::IStreamPtr file = mVFS->get(fileName); @@ -527,4 +538,96 @@ namespace Gui MyGUI::ResourceManager::getInstance().addResource(bookFont); } + void FontLoader::loadFontFromXml(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) + { + MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator(); + bool createCopy = false; + while (resourceNode.next("Resource")) + { + std::string type, name; + resourceNode->findAttribute("type", type); + resourceNode->findAttribute("name", name); + + if (name.empty()) + continue; + + if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) + { + createCopy = true; + + // For TrueType fonts we should override Size and Resolution properties + // to allow to configure font size via config file, without need to edit XML files. + // Also we should take UI scaling factor in account. + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::min(960, std::max(48, resolution)); + + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + resolution *= uiScale; + + MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property"); + resolutionNode->addAttribute("key", "Resolution"); + resolutionNode->addAttribute("value", std::to_string(resolution)); + + MyGUI::xml::ElementPtr sizeNode = resourceNode->createChild("Property"); + sizeNode->addAttribute("key", "Size"); + sizeNode->addAttribute("value", std::to_string(mFontHeight)); + } + else if (Misc::StringUtils::ciEqual(type, "ResourceSkin") || + Misc::StringUtils::ciEqual(type, "AutoSizedResourceSkin")) + { + // We should adjust line height for MyGUI widgets depending on font size + MyGUI::xml::ElementPtr heightNode = resourceNode->createChild("Property"); + heightNode->addAttribute("key", "HeightLine"); + heightNode->addAttribute("value", std::to_string(mFontHeight+2)); + } + } + + MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version); + + if (createCopy) + { + MyGUI::xml::ElementPtr copy = _node->createCopy(); + + MyGUI::xml::ElementEnumerator copyFont = copy->getElementEnumerator(); + while (copyFont.next("Resource")) + { + std::string type, name; + copyFont->findAttribute("type", type); + copyFont->findAttribute("name", name); + + if (name.empty()) + continue; + + if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) + { + // Since the journal and books use the custom scaling factor depending on resolution, + // setup separate fonts with different Resolution to fit these windows. + // These fonts have an internal prefix. + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::min(960, std::max(48, resolution)); + + float currentX = Settings::Manager::getInt("resolution x", "Video"); + float currentY = Settings::Manager::getInt("resolution y", "Video"); + // TODO: read size from openmw_layout.xml somehow + 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); + } + } + + int FontLoader::getFontHeight() + { + return mFontHeight; + } } diff --git a/components/fontloader/fontloader.hpp b/components/fontloader/fontloader.hpp index 39301f9f5..94b022501 100644 --- a/components/fontloader/fontloader.hpp +++ b/components/fontloader/fontloader.hpp @@ -3,6 +3,9 @@ #include "boost/filesystem/operations.hpp" +#include +#include + #include #include @@ -19,8 +22,6 @@ namespace MyGUI namespace Gui { - - /// @brief loads Morrowind's .fnt/.tex fonts for use with MyGUI and OSG /// @note The FontLoader needs to remain in scope as long as you want to use the loaded fonts. class FontLoader @@ -33,16 +34,21 @@ namespace Gui void loadBitmapFonts (bool exportToFile); void loadTrueTypeFonts (); + void loadFontFromXml(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version); + + int getFontHeight(); + private: ToUTF8::FromType mEncoding; const VFS::Manager* mVFS; std::string mUserDataPath; + int mFontHeight; std::vector mTextures; std::vector mFonts; /// @param exportToFile export the converted font (Image and XML with glyph metrics) to files? - void loadFont (const std::string& fileName, bool exportToFile); + void loadBitmapFont (const std::string& fileName, bool exportToFile); FontLoader(const FontLoader&); void operator=(const FontLoader&); diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 35f20cbba..b98388d14 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -51,8 +51,6 @@ namespace Interpreter virtual void report (const std::string& message) = 0; - virtual bool menuMode() = 0; - virtual int getGlobalShort (const std::string& name) const = 0; virtual int getGlobalLong (const std::string& name) const = 0; @@ -95,23 +93,6 @@ namespace Interpreter virtual std::string getCurrentCellName() const = 0; - virtual bool isScriptRunning (const std::string& name) const = 0; - - virtual void startScript (const std::string& name, const std::string& targetId = "") = 0; - - virtual void stopScript (const std::string& name) = 0; - - virtual float getDistance (const std::string& name, const std::string& id = "") const - = 0; - - virtual float getSecondsPassed() const = 0; - - virtual bool isDisabled (const std::string& id = "") const = 0; - - virtual void enable (const std::string& id = "") = 0; - - virtual void disable (const std::string& id = "") = 0; - virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const = 0; virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const = 0; diff --git a/components/interpreter/docs/vmformat.txt b/components/interpreter/docs/vmformat.txt index 5d1eba088..b5c9cf0ae 100644 --- a/components/interpreter/docs/vmformat.txt +++ b/components/interpreter/docs/vmformat.txt @@ -96,27 +96,14 @@ op 34: compare (float) stack[1] with stack[0]; pop twice; push 1 if lesser than op 35: compare (float) stack[1] with stack[0]; pop twice; push 1 if lesser or equal, 0 else op 36: compare (float) stack[1] with stack[0]; pop twice; push 1 if greater than, 0 else op 37: compare (float) stack[1] with stack[0]; pop twice; push 1 if greater or equal, 0 else -op 38: push 1 if game is in menu mode, 0 else +opcode 38 unused op 39: store stack[0] in global short stack[1] and pop twice op 40: store stack[0] in global long stack[1] and pop twice op 41: store stack[0] in global float stack[1] and pop twice op 42: replace stack[0] with global short stack[0] op 43: replace stack[0] with global long stack[0] op 44: replace stack[0] with global float stack[0] -op 45: replace stack[0] with a random integer value in the range [0, stack[0]-1] -op 46: replace stack[0] with 1, if global script stack[0] is running, 0 else -op 47: start script stack[0] and pop -op 48: stop script stack[0] and pop -op 49: replace stack[0] with distance between implicit reference and a reference of ID stack[0] -op 50: push frame duration (float) -op 51: enable implicit reference -op 52: disable implicit reference -op 53: push 1, if implicit reference is disabled, 0 else -op 54: explicit reference = stack[0]; pop; enable explicit reference -op 55: explicit reference = stack[0]; pop; disable explicit reference -op 56: explicit reference = stack[0]; pop; push 1, if explicit reference is disabled, 0 else -op 57: explicit reference = stack[0]; pop; - replace stack[0] with distance between explicit reference and a reference of ID stack[0] +opcodes 45-57 unused op 58: report string literal index in stack[0]; additional arguments (if any) in stack[n]..stack[1]; n is determined according to the message string @@ -133,6 +120,5 @@ op 67: store stack[0] in member float stack[2] of global script with ID stack[1] op 68: replace stack[0] with member short stack[1] of global script with ID stack[0] op 69: replace stack[0] with member short stack[1] of global script with ID stack[0] op 70: replace stack[0] with member short stack[1] of global script with ID stack[0] -op 71: explicit reference (target) = stack[0]; pop; start script stack[0] and pop -opcodes 72-33554431 unused +opcodes 71-33554431 unused opcodes 33554432-67108863 reserved for extensions diff --git a/components/interpreter/installopcodes.cpp b/components/interpreter/installopcodes.cpp index 31e911f8b..afee36bc2 100644 --- a/components/interpreter/installopcodes.cpp +++ b/components/interpreter/installopcodes.cpp @@ -8,8 +8,6 @@ #include "mathopcodes.hpp" #include "controlopcodes.hpp" #include "miscopcodes.hpp" -#include "scriptopcodes.hpp" -#include "spatialopcodes.hpp" namespace Interpreter { @@ -97,25 +95,6 @@ namespace Interpreter // misc interpreter.installSegment3 (0, new OpMessageBox); - interpreter.installSegment5 (38, new OpMenuMode); - interpreter.installSegment5 (45, new OpRandom); - interpreter.installSegment5 (50, new OpGetSecondsPassed); - interpreter.installSegment5 (51, new OpEnable); - interpreter.installSegment5 (52, new OpDisable); - interpreter.installSegment5 (53, new OpGetDisabled); - interpreter.installSegment5 (54, new OpEnableExplicit); - interpreter.installSegment5 (55, new OpDisableExplicit); - interpreter.installSegment5 (56, new OpGetDisabledExplicit); interpreter.installSegment5 (58, new OpReport); - - // script control - interpreter.installSegment5 (46, new OpScriptRunning); - interpreter.installSegment5 (47, new OpStartScript); - interpreter.installSegment5 (48, new OpStopScript); - interpreter.installSegment5 (71, new OpStartScriptExplicit); - - // spacial - interpreter.installSegment5 (49, new OpGetDistance); - interpreter.installSegment5 (57, new OpGetDistanceExplicit); } } diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index a77e0d7d8..07bd84ec9 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -11,7 +11,6 @@ #include "runtime.hpp" #include "defines.hpp" -#include #include namespace Interpreter @@ -168,116 +167,6 @@ namespace Interpreter } }; - class OpMenuMode : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - runtime.push (runtime.getContext().menuMode()); - } - }; - - class OpRandom : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - Type_Integer limit = runtime[0].mInteger; - - if (limit<0) - throw std::runtime_error ( - "random: argument out of range (Don't be so negative!)"); - - runtime[0].mFloat = static_cast(Misc::Rng::rollDice(limit)); // [o, limit) - } - }; - - class OpGetSecondsPassed : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - Type_Float duration = runtime.getContext().getSecondsPassed(); - - runtime.push (duration); - } - }; - - class OpEnable : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - runtime.getContext().enable(); - } - }; - - class OpDisable : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - runtime.getContext().disable(); - } - }; - - class OpGetDisabled : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - runtime.push (runtime.getContext().isDisabled()); - } - }; - - class OpEnableExplicit : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - int index = runtime[0].mInteger; - runtime.pop(); - std::string id = runtime.getStringLiteral (index); - - runtime.getContext().enable (id); - } - }; - - class OpDisableExplicit : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - int index = runtime[0].mInteger; - runtime.pop(); - std::string id = runtime.getStringLiteral (index); - - runtime.getContext().disable (id); - } - }; - - class OpGetDisabledExplicit : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - int index = runtime[0].mInteger; - runtime.pop(); - std::string id = runtime.getStringLiteral (index); - - runtime.push (runtime.getContext().isDisabled (id)); - } - }; - } #endif diff --git a/components/interpreter/scriptopcodes.hpp b/components/interpreter/scriptopcodes.hpp deleted file mode 100644 index c98bcd23e..000000000 --- a/components/interpreter/scriptopcodes.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef INTERPRETER_SCRIPTOPCODES_H_INCLUDED -#define INTERPRETER_SCRIPTOPCODES_H_INCLUDED - -#include "opcodes.hpp" -#include "runtime.hpp" -#include "context.hpp" - -namespace Interpreter -{ - class OpScriptRunning : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - runtime[0].mInteger = runtime.getContext().isScriptRunning (name); - } - }; - - class OpStartScript : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - runtime.getContext().startScript (name); - } - }; - - class OpStartScriptExplicit : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - std::string targetId = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - - runtime.getContext().startScript (name, targetId); - } - }; - - class OpStopScript : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - runtime.getContext().stopScript (name); - } - }; -} - -#endif - diff --git a/components/interpreter/spatialopcodes.hpp b/components/interpreter/spatialopcodes.hpp deleted file mode 100644 index e37df8116..000000000 --- a/components/interpreter/spatialopcodes.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef INTERPRETER_SPATIALOPCODES_H_INCLUDED -#define INTERPRETER_SPATIALOPCODES_H_INCLUDED - -#include "opcodes.hpp" -#include "runtime.hpp" - -namespace Interpreter -{ - class OpGetDistance : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - - Type_Float distance = runtime.getContext().getDistance (name); - - runtime[0].mFloat = distance; - } - }; - - class OpGetDistanceExplicit : public Opcode0 - { - public: - - virtual void execute (Runtime& runtime) - { - int index = runtime[0].mInteger; - runtime.pop(); - std::string id = runtime.getStringLiteral (index); - - std::string name = runtime.getStringLiteral (runtime[0].mInteger); - - Type_Float distance = runtime.getContext().getDistance (name, id); - - runtime[0].mFloat = distance; - } - }; -} - -#endif - diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 521764274..131f0c470 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -157,7 +157,8 @@ namespace nextpos = std::distance(str.begin(), ++last); } std::string result = str.substr(pos, nextpos-pos); - textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::lowerCase(result))); + Misc::StringUtils::lowerCaseInPlace(result); + textkeys.emplace(tk->list[i].time, std::move(result)); pos = nextpos; } @@ -495,14 +496,6 @@ namespace NifOsg switch (nifNode->recType) { - case Nif::RC_NiAutoNormalParticles: - case Nif::RC_NiRotatingParticles: - // Leaf nodes in the NIF hierarchy, so won't be able to dynamically attach children. - // No support for keyframe controllers (just crashes in the original engine). - if (nifNode->trafo.isIdentity()) - node = new osg::Group; - dataVariance = osg::Object::STATIC; - break; case Nif::RC_NiBillboardNode: dataVariance = osg::Object::DYNAMIC; break; @@ -519,17 +512,9 @@ namespace NifOsg // This takes advantage of the fact root nodes can't have additional controllers // loaded from an external .kf file (original engine just throws "can't find node" errors if you try). if (!nifNode->parent && nifNode->controller.empty() && nifNode->trafo.isIdentity()) - { node = new osg::Group; - dataVariance = osg::Object::STATIC; - } - else - { - dataVariance = (nifNode->controller.empty() ? osg::Object::STATIC : osg::Object::DYNAMIC); - } - if (nifNode->isBone) - dataVariance = osg::Object::DYNAMIC; + dataVariance = nifNode->isBone ? osg::Object::DYNAMIC : osg::Object::STATIC; break; } @@ -542,7 +527,7 @@ namespace NifOsg } osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager, - std::vector boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool isAnimated, TextKeyMap* textKeys, osg::Node* rootNode=nullptr) + std::vector boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool hasAnimatedParents, TextKeyMap* textKeys, osg::Node* rootNode=nullptr) { if (rootNode != nullptr && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) return nullptr; @@ -554,9 +539,6 @@ namespace NifOsg node->addCullCallback(new BillboardCallback); } - if (!nifNode->controller.empty() && nifNode->controller->recType == Nif::RC_NiKeyframeController) - isAnimated = true; - node->setName(nifNode->name); if (parentNode) @@ -626,16 +608,6 @@ namespace NifOsg node->setNodeMask(Loader::getHiddenNodeMask()); } - if ((skipMeshes || hasMarkers) && isAnimated) // make sure the empty node is not optimized away so the physicssystem can find it. - { - node->setDataVariance(osg::Object::DYNAMIC); - } - - if ((nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips) && isAnimated) // Same thing for animated shapes - { - node->setDataVariance(osg::Object::DYNAMIC); - } - osg::ref_ptr composite = new SceneUtil::CompositeStateSetUpdater; applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags); @@ -667,9 +639,12 @@ namespace NifOsg if (composite->getNumControllers() > 0) node->addUpdateCallback(composite); - if (nifNode->recType != Nif::RC_NiTriShape && nifNode->recType != Nif::RC_NiTriStrips - && !nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC) - handleNodeControllers(nifNode, static_cast(node.get()), animflags); + bool isAnimated = false; + handleNodeControllers(nifNode, node, animflags, isAnimated); + hasAnimatedParents |= isAnimated; + // Make sure empty nodes are not optimized away so the physics system can find them. + if (isAnimated || (hasAnimatedParents && (skipMeshes || hasMarkers))) + node->setDataVariance(osg::Object::DYNAMIC); // LOD and Switch nodes must be wrapped by a transform (the current node) to support transformations properly // and we need to attach their children to the osg::LOD/osg::Switch nodes @@ -710,7 +685,7 @@ namespace NifOsg for(size_t i = 0;i < children.length();++i) { if(!children[i].empty()) - handleNode(children[i].getPtr(), currentNode, imageManager, boundTextures, animflags, skipMeshes, hasMarkers, isAnimated, textKeys, rootNode); + handleNode(children[i].getPtr(), currentNode, imageManager, boundTextures, animflags, skipMeshes, hasMarkers, hasAnimatedParents, textKeys, rootNode); } } @@ -741,40 +716,10 @@ namespace NifOsg setupController(niuvctrl, uvctrl, animflags); composite->addController(uvctrl); } - else if (ctrl->recType == Nif::RC_NiKeyframeController) - { - const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if(!key->data.empty()) - { - osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); - - setupController(key, callback, animflags); - node->addUpdateCallback(callback); - } - } - else if (ctrl->recType == Nif::RC_NiPathController) - { - const Nif::NiPathController *path = static_cast(ctrl.getPtr()); - if (!path->posData.empty() && !path->floatData.empty()) - { - osg::ref_ptr callback(new PathController(path)); - - setupController(path, callback, animflags); - node->addUpdateCallback(callback); - } - } - else if (ctrl->recType == Nif::RC_NiVisController) - { - handleVisController(static_cast(ctrl.getPtr()), node, animflags); - } - else if(ctrl->recType == Nif::RC_NiGeomMorpherController) - {} // handled in handleTriShape - else - Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } } - void handleNodeControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, int animflags) + void handleNodeControllers(const Nif::Node* nifNode, osg::Node* node, int animflags, bool& isAnimated) { for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { @@ -783,56 +728,54 @@ namespace NifOsg if (ctrl->recType == Nif::RC_NiKeyframeController) { const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if(!key->data.empty()) - { - osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); - - setupController(key, callback, animflags); - transformNode->addUpdateCallback(callback); - } + if (key->data.empty()) + continue; + osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); + setupController(key, callback, animflags); + node->addUpdateCallback(callback); + isAnimated = true; } else if (ctrl->recType == Nif::RC_NiPathController) { const Nif::NiPathController *path = static_cast(ctrl.getPtr()); - if (!path->posData.empty() && !path->floatData.empty()) - { - osg::ref_ptr callback(new PathController(path)); - - setupController(path, callback, animflags); - transformNode->addUpdateCallback(callback); - } + if (path->posData.empty() || path->floatData.empty()) + continue; + osg::ref_ptr callback(new PathController(path)); + setupController(path, callback, animflags); + node->addUpdateCallback(callback); + isAnimated = true; } else if (ctrl->recType == Nif::RC_NiVisController) { - handleVisController(static_cast(ctrl.getPtr()), transformNode, animflags); + const Nif::NiVisController *visctrl = static_cast(ctrl.getPtr()); + if (visctrl->data.empty()) + continue; + osg::ref_ptr callback(new VisController(visctrl->data.getPtr(), Loader::getHiddenNodeMask())); + setupController(visctrl, callback, animflags); + node->addUpdateCallback(callback); } else if (ctrl->recType == Nif::RC_NiRollController) { - handleRollController(static_cast(ctrl.getPtr()), transformNode, animflags); + const Nif::NiRollController *rollctrl = static_cast(ctrl.getPtr()); + if (rollctrl->data.empty()) + continue; + osg::ref_ptr callback(new RollController(rollctrl->data.getPtr())); + setupController(rollctrl, callback, animflags); + node->addUpdateCallback(callback); + isAnimated = true; + } + else if (ctrl->recType == Nif::RC_NiGeomMorpherController + || ctrl->recType == Nif::RC_NiParticleSystemController + || ctrl->recType == Nif::RC_NiBSPArrayController + || ctrl->recType == Nif::RC_NiUVController) + { + // These controllers are handled elsewhere } else Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } } - void handleVisController(const Nif::NiVisController* visctrl, osg::Node* node, int animflags) - { - if (visctrl->data.empty()) - return; - osg::ref_ptr callback(new VisController(visctrl->data.getPtr(), Loader::getHiddenNodeMask())); - setupController(visctrl, callback, animflags); - node->addUpdateCallback(callback); - } - - void handleRollController(const Nif::NiRollController* rollctrl, osg::Node* node, int animflags) - { - if (rollctrl->data.empty()) - return; - osg::ref_ptr callback(new RollController(rollctrl->data.getPtr())); - setupController(rollctrl, callback, animflags); - node->addUpdateCallback(callback); - } - void handleMaterialControllers(const Nif::Property *materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags) { for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) @@ -927,6 +870,8 @@ namespace NifOsg else if (affectors->recType == Nif::RC_NiParticleColorModifier) { const Nif::NiParticleColorModifier *cl = static_cast(affectors.getPtr()); + if (cl->data.empty()) + continue; const Nif::NiColorData *clrdata = cl->data.getPtr(); program->addOperator(new ParticleColorAffector(clrdata)); } @@ -1069,8 +1014,6 @@ namespace NifOsg continue; if(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiBSPArrayController) partctrl = static_cast(ctrl.getPtr()); - else - Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } if (!partctrl) { @@ -1234,12 +1177,7 @@ namespace NifOsg osg::ref_ptr drawable; osg::ref_ptr geom (new osg::Geometry); triShapeToGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags); - Nif::ControllerPtr ctrl; - if (nifNode->recType == Nif::RC_NiTriShape) - ctrl = static_cast(nifNode)->controller; - else - ctrl = static_cast(nifNode)->controller; - for (; !ctrl.empty(); ctrl = ctrl->next) + for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) continue; diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index 4de9027b8..49a78ad5f 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -7,6 +7,7 @@ #include #include "controller.hpp" +#include "textkeymap.hpp" namespace osg { @@ -20,8 +21,6 @@ namespace Resource namespace NifOsg { - typedef std::multimap TextKeyMap; - struct TextKeyMapHolder : public osg::Object { public: diff --git a/components/nifosg/textkeymap.hpp b/components/nifosg/textkeymap.hpp new file mode 100644 index 000000000..49e1e461e --- /dev/null +++ b/components/nifosg/textkeymap.hpp @@ -0,0 +1,87 @@ +#ifndef OPENMW_COMPONENTS_NIFOSG_TEXTKEYMAP +#define OPENMW_COMPONENTS_NIFOSG_TEXTKEYMAP + +#include +#include +#include +#include + +namespace NifOsg +{ + class TextKeyMap + { + public: + using ConstIterator = std::multimap::const_iterator; + + auto begin() const noexcept + { + return mTextKeyByTime.begin(); + } + + auto end() const noexcept + { + return mTextKeyByTime.end(); + } + + auto rbegin() const noexcept + { + return mTextKeyByTime.rbegin(); + } + + auto rend() const noexcept + { + return mTextKeyByTime.rend(); + } + + auto lowerBound(float time) const + { + return mTextKeyByTime.lower_bound(time); + } + + auto upperBound(float time) const + { + return mTextKeyByTime.upper_bound(time); + } + + void emplace(float time, std::string&& textKey) + { + const auto separator = textKey.find(": "); + if (separator != std::string::npos) + mGroups.emplace(textKey.substr(0, separator)); + + mTextKeyByTime.emplace(time, std::move(textKey)); + } + + bool empty() const noexcept + { + return mTextKeyByTime.empty(); + } + + auto findGroupStart(const std::string &groupName) const + { + return std::find_if(mTextKeyByTime.begin(), mTextKeyByTime.end(), IsGroupStart{groupName}); + } + + bool hasGroupStart(const std::string &groupName) const + { + return mGroups.count(groupName) > 0; + } + + private: + struct IsGroupStart + { + const std::string &mGroupName; + + bool operator ()(const std::multimap::value_type& value) const + { + return value.second.compare(0, mGroupName.size(), mGroupName) == 0 && + value.second.compare(mGroupName.size(), 2, ": ") == 0; + } + }; + + std::set mGroups; + std::multimap mTextKeyByTime; + }; +} + +#endif diff --git a/components/openmw-mp/Packets/Player/PacketPlayerSpellsActive.cpp b/components/openmw-mp/Packets/Player/PacketPlayerSpellsActive.cpp index 974cf3776..6ebec15d6 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerSpellsActive.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerSpellsActive.cpp @@ -24,7 +24,7 @@ void PacketPlayerSpellsActive::Packet(RakNet::BitStream *newBitstream, bool send spell != player->activeSpells.mSpells.end(); ++spell) { RW(spell->first, true); - RW(spell->second.mTimeStamp, true); + //RW(spell->second.mTimeStamp, true); uint32_t effects = spell->second.mEffects.size(); RW(effects, true); @@ -39,7 +39,7 @@ void PacketPlayerSpellsActive::Packet(RakNet::BitStream *newBitstream, bool send ESM::ActiveSpells::TContainer::value_type spell; RW(spell.first, false); - RW(spell.second.mTimeStamp, false); + //RW(spell.second.mTimeStamp, false); uint32_t effects; RW(effects, false); diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 59d65e889..0dd52ffb6 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -262,7 +262,6 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) stateset->setAttribute(new osg::PolygonMode(), osg::StateAttribute::PROTECTED); #endif - osg::Vec3 pos(_statsWidth-420.f, _statsHeight-500.0f,0.0f); osg::Vec4 backgroundColor(0.0, 0.0, 0.0f, 0.3); osg::Vec4 staticTextColor(1.0, 1.0, 0.0f, 1.0); osg::Vec4 dynamicTextColor(1.0, 1.0, 1.0f, 1.0); @@ -277,6 +276,8 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) _switch->addChild(group, false); static const std::vector statNames({ + "FrameNumber", + "", "Compiling", "WorkQueue", "WorkThread", @@ -302,16 +303,25 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) "NavMesh CacheSize", "NavMesh UsedTiles", "NavMesh CachedTiles", + "", + "Mechanics Actors", + "Mechanics Objects", + "", + "Physics Actors", + "Physics Objects", + "Physics HeightFields", }); static const auto longest = std::max_element(statNames.begin(), statNames.end(), [] (const std::string& lhs, const std::string& rhs) { return lhs.size() < rhs.size(); }); - const int numLines = statNames.size(); const float statNamesWidth = 13 * _characterSize + 2 * backgroundMargin; + const float statTextWidth = 7 * _characterSize + 2 * backgroundMargin; + const float statHeight = statNames.size() * _characterSize + 2 * backgroundMargin; + osg::Vec3 pos(_statsWidth - statNamesWidth - backgroundSpacing - statTextWidth, statHeight, 0.0f); group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0), statNamesWidth, - numLines * _characterSize + 2 * backgroundMargin, + statHeight, backgroundColor)); osg::ref_ptr staticText = new osgText::Text; @@ -335,8 +345,8 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) pos.x() += statNamesWidth + backgroundSpacing; group->addChild(createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0), - 7 * _characterSize + 2 * backgroundMargin, - numLines * _characterSize + 2 * backgroundMargin, + statTextWidth, + statHeight, backgroundColor)); osg::ref_ptr statsText = new osgText::Text; diff --git a/components/widgets/fontwrapper.hpp b/components/widgets/fontwrapper.hpp index 84406c70c..8b0011dda 100644 --- a/components/widgets/fontwrapper.hpp +++ b/components/widgets/fontwrapper.hpp @@ -38,7 +38,7 @@ namespace Gui std::string getFontSize() { - // Note: we can not use the WindowManager here, so there is a code duplication a bit. + // Note: we can not use the FontLoader here, so there is a code duplication a bit. static const std::string fontSize = std::to_string(clamp(Settings::Manager::getInt("font size", "GUI"), 12, 20)); return fontSize; }