1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-30 03:15:32 +00:00

Add OpenMW commits up to 30 May 2020

# Conflicts:
#	CMakeLists.txt
#	apps/openmw/mwbase/windowmanager.hpp
#	apps/openmw/mwgui/windowmanagerimp.hpp
#	apps/openmw/mwmechanics/actors.cpp
#	apps/openmw/mwmechanics/actors.hpp
#	apps/openmw/mwscript/interpretercontext.cpp
This commit is contained in:
David Cernat 2020-06-09 17:50:26 +03:00
commit 36a2308acd
109 changed files with 1620 additions and 1778 deletions

1
.gitignore vendored
View file

@ -32,6 +32,7 @@ cmake-build-*
## qt-creator
CMakeLists.txt.user*
.vs
.vscode
## resources
data

View file

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

26
CI/ActivateMSVC.ps1 Normal file
View file

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

76
CI/activate_msvc.sh Normal file
View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
#include "engine.hpp"
#include <iomanip>
#include <fstream>
#include <boost/filesystem/fstream.hpp>
@ -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());
}

View file

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

View file

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

View file

@ -76,6 +76,8 @@ namespace MWBase
virtual void resetIdleTime() = 0;
virtual void executeAction(int action) = 0;
virtual bool controlsDisabled() = 0;
};
}

View file

@ -7,10 +7,14 @@
#include <set>
#include <stdint.h>
#include "../mwmechanics/actorutil.hpp"
// For MWMechanics::GreetingState
#include "../mwworld/ptr.hpp"
namespace osg
{
class Stats;
class Vec3f;
}
@ -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;
};
}

View file

@ -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<int, MWMechanics::SkillValue > getPlayerSkillValues() = 0;
virtual std::map<int, MWMechanics::AttributeValue > getPlayerAttributeValues() = 0;
virtual SkillList getPlayerMinorSkills() = 0;
virtual SkillList getPlayerMajorSkills() = 0;
virtual void update (float duration) = 0;
/**
* Fetches a GMST string from the store, if there is no setting with the given

View file

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

View file

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

View file

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

View file

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

View file

@ -5,14 +5,12 @@
namespace MWGui
{
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();
};
}

View file

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

View file

@ -15,6 +15,9 @@
#include <MyGUI_ClipboardManager.h>
#include <MyGUI_WidgetManager.h>
// For BT_NO_PROFILE
#include <LinearMath/btQuickprof.h>
#include <SDL_keyboard.h>
#include <SDL_clipboard.h>
@ -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<MWGui::Controllers::ControllerFollowMouse>("Controller");
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("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<ESM::Skill::SkillEnum> (parSkill), value);
mCharGen->setValue(static_cast<ESM::Skill::SkillEnum> (parSkill), value);
mPlayerSkillValues[parSkill] = value;
}
void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value)
@ -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<int, MWMechanics::SkillValue > WindowManager::getPlayerSkillValues()
{
return mPlayerSkillValues;
}
std::map<int, MWMechanics::AttributeValue > WindowManager::getPlayerAttributeValues()
{
return mPlayerAttributes;
}
WindowManager::SkillList WindowManager::getPlayerMinorSkills()
{
return mPlayerMinorSkills;
}
WindowManager::SkillList WindowManager::getPlayerMajorSkills()
{
return mPlayerMajorSkills;
}
void WindowManager::disallowMouse()
{
mInputBlocker->setVisible (true);
@ -2331,7 +2138,9 @@ namespace MWGui
void WindowManager::toggleDebugWindow()
{
#ifndef BT_NO_PROFILE
mDebugWindow->setVisible(!mDebugWindow->isVisible());
#endif
}
void WindowManager::cycleSpell(bool next)

View file

@ -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<int, MWMechanics::SkillValue > getPlayerSkillValues();
virtual std::map<int, MWMechanics::AttributeValue > getPlayerAttributeValues();
virtual SkillList getPlayerMinorSkills();
virtual SkillList getPlayerMajorSkills();
virtual void update (float duration);
/**
* Fetches a GMST string from the store, if there is no setting with the given
@ -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<WindowModal*> 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<int, MWMechanics::AttributeValue > mPlayerAttributes;
SkillList mPlayerMajorSkills, mPlayerMinorSkills;
std::map<int, MWMechanics::SkillValue > mPlayerSkillValues;
MyGUI::Gui *mGui; // Gui
struct GuiModeState
@ -628,8 +612,6 @@ namespace MWGui
ToUTF8::FromType mEncoding;
int mFontHeight;
std::string mVersionDescription;
bool mWindowVisible;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<ActiveEffect>& effects = iter->second.mEffects;
for (std::vector<ActiveEffect>::iterator effectIt = effects.begin(); effectIt != effects.end();)
{
MWWorld::TimeStamp start = iter->second.mTimeStamp;
MWWorld::TimeStamp end = start + static_cast<double>(effectIt->mDuration)*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
if (end <= now)
if (effectIt->mTimeLeft <= 0)
{
effectIt = effects.erase(effectIt);
rebuild = true;
}
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<ActiveEffect>& effects = iter->second.mEffects;
for (std::vector<ActiveEffect>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
{
double duration = effectIt->mDuration;
MWWorld::TimeStamp end = start;
end += duration *
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
if (end>now)
if (effectIt->mTimeLeft > 0)
mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude));
}
}
@ -88,12 +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<ActiveEffect>::const_iterator iter (effects.begin());
iter!=effects.end(); ++iter)
{
if (iter->mDuration > duration)
duration = iter->mDuration;
if (iter->mTimeLeft > duration)
duration = iter->mTimeLeft;
}
double scaledDuration = duration *
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp() - iterator->second.mTimeStamp;
if (usedUp>=scaledDuration)
if (duration < 0)
return 0;
return scaledDuration-usedUp;
return duration;
}
bool ActiveSpells::isSpellActive(const std::string& id) const
@ -152,7 +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<ActiveEffect>::const_iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end(); ++effectIt)
{
std::string name = it->second.mDisplayName;
float remainingTime = effectIt->mDuration +
static_cast<float>(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
float magnitude = effectIt->mMagnitude;
if (magnitude)
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration);
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, effectIt->mTimeLeft, effectIt->mDuration);
}
}
}
@ -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;

View file

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

View file

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

View file

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

View file

@ -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<int>::max(), false);
}
@ -526,8 +526,8 @@ namespace MWMechanics
float helloDistance = static_cast<float>(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier);
int greetingTimer = stats.getGreetingTimer();
GreetingState greetingState = stats.getGreetingState();
int greetingTimer = actorState.getGreetingTimer();
GreetingState greetingState = actorState.getGreetingState();
if (greetingState == Greet_None)
{
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
@ -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())

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -24,8 +24,6 @@
#include <osg/Quat>
MWMechanics::AiPackage::~AiPackage() {}
MWMechanics::AiPackage::AiPackage() :
mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild
mTargetActorRefId(""),

View file

@ -56,8 +56,7 @@ namespace MWMechanics
///Default constructor
AiPackage();
///Default Deconstructor
virtual ~AiPackage();
virtual ~AiPackage() = default;
///Clones the package
virtual AiPackage *clone() const = 0;

View file

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

View file

@ -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<AiPackage *>::iterator itActualCombat;
auto itActualCombat = mPackages.end();
float nearestDist = std::numeric_limits<float>::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<float>::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<float>::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<MWMechanics::AiPackage*>::iterator toRemove =
std::find(mPackages.begin(), mPackages.end(), package);
mPackages.erase(toRemove);
mPackages.erase(packageIt);
delete package;
if (isActualAiPackage(packageTypeId))
mDone = true;

View file

@ -3,6 +3,7 @@
#include <components/esm/aisequence.hpp>
#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);

View file

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

View file

@ -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<unsigned char>& 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)

View file

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

View file

@ -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<ESM::GameSetting>().find("fEffectCostMult")->mValue.getFloat();

View file

@ -1013,7 +1013,7 @@ void split(const std::string &s, char delim, std::vector<std::string> &elems) {
}
}
void CharacterController::handleTextKey(const std::string &groupname, const std::multimap<float, std::string>::const_iterator &key, const std::multimap<float, std::string> &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;

View file

@ -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<float, std::string>::const_iterator &key,
const std::multimap<float, std::string>& 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();

View file

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

View file

@ -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<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
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);

View file

@ -1,5 +1,7 @@
#include "mechanicsmanagerimp.hpp"
#include <osg/Stats>
#include <components/misc/rng.hpp>
#include <components/esm/esmwriter.hpp>
@ -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);
}
}

View file

@ -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<MWWorld::Ptr> &playerFollowers);

View file

@ -52,6 +52,11 @@ namespace MWMechanics
void persistAnimationStates();
void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out);
std::size_t size() const
{
return mObjects.size();
}
};
}

View file

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

View file

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

View file

@ -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<float>(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<float>(effectIt->mDuration) : 1.f;
effect.mTimeLeft = effect.mDuration;
targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude));

View file

@ -394,8 +394,9 @@ namespace MWMechanics
priority = 10;
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
const DynamicStat<float>& 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)

View file

@ -31,9 +31,12 @@ namespace MWMechanics
magicEffect = store.get<ESM::MagicEffect>().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<ESM::GameSetting>().find("fEffectCostMult")->mValue.getFloat();
float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn));

View file

@ -1,6 +1,7 @@
#include "physicssystem.hpp"
#include <osg/Group>
#include <osg/Stats>
#include <BulletCollision/CollisionShapes/btConeShape.h>
#include <BulletCollision/CollisionShapes/btSphereShape.h>
@ -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());
}
}

View file

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

View file

@ -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<float, std::string>& 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<float, std::string>& 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<float, std::string> &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<float, std::string>::const_iterator &key,
const std::multimap<float, std::string>& 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<osg::Node> 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<NifOsg::KeyframeController*>(cb))
{
osg::MatrixTransform* node = found->second;
bool foundKeyframeCtrl = false;
osg::Callback* cb = node->getUpdateCallback();
while (cb)
{
if (dynamic_cast<NifOsg::KeyframeController*>(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)

View file

@ -5,6 +5,9 @@
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/util.hpp>
#include <components/nifosg/textkeymap.hpp>
#include <vector>
namespace ESM
{
@ -147,8 +150,8 @@ public:
class TextKeyListener
{
public:
virtual void handleTextKey(const std::string &groupname, const std::multimap<float, std::string>::const_iterator &key,
const std::multimap<float, std::string>& 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<osg::Node>, osg::ref_ptr<osg::NodeCallback> > ControllerMap;
ControllerMap mActiveControllers;
std::vector<std::pair<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback>>> mActiveControllers;
std::shared_ptr<AnimationTime> 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<float, std::string> &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<float, std::string>::const_iterator &key,
const std::multimap<float, std::string>& map);
void handleTextKey(AnimState &state, const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key,
const NifOsg::TextKeyMap& map);
/** Sets the root model of the object.
*

View file

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

View file

@ -231,7 +231,7 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
}
void WeaponAnimation::addControllers(const std::map<std::string, osg::ref_ptr<osg::MatrixTransform> >& nodes,
std::multimap<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback> > &map, osg::Node* objectRoot)
std::vector<std::pair<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback>>> &map, osg::Node* objectRoot)
{
for (int i=0; i<2; ++i)
{
@ -243,7 +243,7 @@ void WeaponAnimation::addControllers(const std::map<std::string, osg::ref_ptr<os
osg::Node* node = found->second;
mSpineControllers[i] = new RotateController(objectRoot);
node->addUpdateCallback(mSpineControllers[i]);
map.insert(std::make_pair(node, mSpineControllers[i]));
map.emplace_back(node, mSpineControllers[i]);
}
}
}

View file

@ -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<std::string, osg::ref_ptr<osg::MatrixTransform> >& nodes,
std::multimap<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback> >& map, osg::Node* objectRoot);
std::vector<std::pair<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback>>>& map, osg::Node* objectRoot);
void deleteControllers();

View file

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

View file

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

View file

@ -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<float>::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<float>(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<MWWorld::Action> 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
{

View file

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

View file

@ -26,6 +26,8 @@
#include <components/interpreter/runtime.hpp>
#include <components/interpreter/opcodes.hpp>
#include <components/misc/rng.hpp>
#include <components/esm/loadmgef.hpp>
#include <components/esm/loadcrea.hpp>
@ -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<Interpreter::Type_Float>(::Misc::Rng::rollDice(limit))); // [o, limit)
}
};
template<class R>
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 R>
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 R>
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 R>
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<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeStartScriptExplicit, new OpStartScript<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeStopScript, new OpStopScript);
interpreter.installSegment5 (Compiler::Misc::opcodeGetSecondsPassed, new OpGetSecondsPassed);
interpreter.installSegment5 (Compiler::Misc::opcodeEnable, new OpEnable<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeEnableExplicit, new OpEnable<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeDisable, new OpDisable<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeDisableExplicit, new OpDisable<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetDisabled, new OpGetDisabled<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetDisabledExplicit, new OpGetDisabled<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox);
interpreter.installSegment5 (Compiler::Misc::opcodeOnActivate, new OpOnActivate<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeOnActivateExplicit, new OpOnActivate<ExplicitRef>);

View file

@ -56,6 +56,63 @@ namespace MWScript
}
}
template<class R>
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<float>::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<float>(std::sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]));
}
runtime.push(distance);
}
};
template<class R>
class OpSetScale : public Interpreter::Opcode0
{
@ -902,6 +959,8 @@ namespace MWScript
void installOpcodes (Interpreter::Interpreter& interpreter)
{
interpreter.installSegment5(Compiler::Transformation::opcodeGetDistance, new OpGetDistance<ImplicitRef>);
interpreter.installSegment5(Compiler::Transformation::opcodeGetDistanceExplicit, new OpGetDistance<ExplicitRef>);
interpreter.installSegment5(Compiler::Transformation::opcodeSetScale,new OpSetScale<ImplicitRef>);
interpreter.installSegment5(Compiler::Transformation::opcodeSetScaleExplicit,new OpSetScale<ExplicitRef>);
interpreter.installSegment5(Compiler::Transformation::opcodeSetAngle,new OpSetAngle<ImplicitRef>);

View file

@ -240,12 +240,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
ESM::ESMWriter writer;
const std::vector<std::string>& current =
MWBase::Environment::get().getWorld()->getContentFiles();
for (std::vector<std::string>::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<std::string>& selectedContentFiles = MWBase::Environment::get().getWorld()->getContentFiles();
bool notFound = false;
for (std::vector<std::string>::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;
}
}

View file

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

View file

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

View file

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

View file

@ -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<Interpreter::Type_Code> 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<Interpreter::Type_Code> ignore;
parseArguments ("x", scanner, ignore);
mNextOperand = false;
return true;
}
else
{
// check for custom extensions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -266,12 +266,6 @@ namespace Compiler
"messagebox",
"set", "to",
"getsquareroot",
"menumode",
"random",
"startscript", "stopscript", "scriptrunning",
"getdistance",
"getsecondspassed",
"enable", "disable", "getdisabled",
0
};

View file

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

View file

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

View file

@ -16,7 +16,6 @@ namespace ESM
esm.writeHNT ("CAST", params.mCasterActorId);
esm.writeHNString ("DISP", params.mDisplayName);
esm.writeHNT ("TIME", params.mTimeStamp);
for (std::vector<ActiveEffect>::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));

View file

@ -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<ActiveEffect> mEffects;
ESM::TimeStamp mTimeStamp;
std::string mDisplayName;
int mCasterActorId;
};

View file

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

View file

@ -6,7 +6,7 @@
#include <shlobj.h>
#include <shlwapi.h>
#include <WinReg.h>
#include <winreg.h>
#include <boost/locale.hpp>
namespace bconv = boost::locale::conv;

View file

@ -21,6 +21,8 @@
#include <components/myguiplatform/myguitexture.hpp>
#include <components/settings/settings.hpp>
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<MyGUI::ITexture*>::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;
}
}

View file

@ -3,6 +3,9 @@
#include "boost/filesystem/operations.hpp"
#include <MyGUI_XmlDocument.h>
#include <MyGUI_Version.h>
#include <components/myguiplatform/myguidatamanager.hpp>
#include <components/to_utf8/to_utf8.hpp>
@ -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<MyGUI::ITexture*> mTextures;
std::vector<MyGUI::ResourceManualFont*> 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&);

View file

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

View file

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

View file

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

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