Merge pull request #457 from OpenMW/master while resolving conflicts

Conflicts:
	CMakeLists.txt
	apps/openmw/engine.cpp
	apps/openmw/main.cpp
	apps/openmw/mwgui/windowmanagerimp.cpp
	apps/openmw/mwmechanics/character.cpp
	components/CMakeLists.txt
pull/459/head
David Cernat 7 years ago
commit 6cb5ac6e63

@ -34,6 +34,8 @@ MacOS:
tags: tags:
- macos - macos
- xcode - xcode
except:
- branches # because our CI VMs are not public, MRs can't use them and timeout
stage: build stage: build
allow_failure: true allow_failure: true
script: script:
@ -44,3 +46,24 @@ MacOS:
artifacts: artifacts:
paths: paths:
- build/OpenMW-*.dmg - build/OpenMW-*.dmg
Windows:
tags:
- win10
- msvc2017
except:
- branches # because our CI VMs are not public, MRs can't use them and timeout
stage: build
allow_failure: true
script:
# - env # turn on for debugging
- sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V
- SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
- call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS%
- 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\
cache:
paths:
- deps
artifacts:
paths:
- "*.zip"

@ -143,6 +143,7 @@ Programmers
Rohit Nirmal Rohit Nirmal
Roman Melnik (Kromgart) Roman Melnik (Kromgart)
Roman Proskuryakov (kpp) Roman Proskuryakov (kpp)
Roman Siromakha (elsid)
Sandy Carter (bwrsandman) Sandy Carter (bwrsandman)
Scott Howard Scott Howard
scrawl scrawl

@ -2,6 +2,7 @@
------ ------
Bug #1990: Sunrise/sunset not set correct Bug #1990: Sunrise/sunset not set correct
Bug #2131: Lustidrike's spell misses the player every time
Bug #2222: Fatigue's effect on selling price is backwards Bug #2222: Fatigue's effect on selling price is backwards
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
Bug #2455: Creatures attacks degrade armor Bug #2455: Creatures attacks degrade armor
@ -11,6 +12,7 @@
Bug #2852: No murder bounty when a player follower commits murder Bug #2852: No murder bounty when a player follower commits murder
Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
Bug #3249: Fixed revert function not updating views properly
Bug #3374: Touch spells not hitting kwama foragers Bug #3374: Touch spells not hitting kwama foragers
Bug #3486: [Mod] NPC Commands does not work Bug #3486: [Mod] NPC Commands does not work
Bug #3591: Angled hit distance too low Bug #3591: Angled hit distance too low
@ -22,15 +24,18 @@
Bug #3997: Almalexia doesn't pace Bug #3997: Almalexia doesn't pace
Bug #4036: Weird behaviour of AI packages if package target has non-unique ID Bug #4036: Weird behaviour of AI packages if package target has non-unique ID
Bug #4047: OpenMW not reporting its version number in MacOS; OpenMW-CS not doing it fully Bug #4047: OpenMW not reporting its version number in MacOS; OpenMW-CS not doing it fully
Bug #4110: Fixed undo / redo menu text losing the assigned shortcuts
Bug #4125: OpenMW logo cropped on bugtracker Bug #4125: OpenMW logo cropped on bugtracker
Bug #4215: OpenMW shows book text after last EOL tag Bug #4215: OpenMW shows book text after last EOL tag
Bug #4221: Characters get stuck in V-shaped terrain Bug #4221: Characters get stuck in V-shaped terrain
Bug #4251: Stationary NPCs do not return to their position after combat Bug #4251: Stationary NPCs do not return to their position after combat
Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+
Bug #4286: Scripted animations can be interrupted Bug #4286: Scripted animations can be interrupted
Bug #4291: Non-persistent actors that started the game as dead do not play death animations Bug #4291: Non-persistent actors that started the game as dead do not play death animations
Bug #4293: Faction members are not aware of faction ownerships in barter Bug #4293: Faction members are not aware of faction ownerships in barter
Bug #4307: World cleanup should remove dead bodies only if death animation is finished Bug #4307: World cleanup should remove dead bodies only if death animation is finished
Bug #4327: Missing animations during spell/weapon stance switching Bug #4327: Missing animations during spell/weapon stance switching
Bug #4358: Running animation is interrupted when magic mode is toggled
Bug #4368: Settings window ok button doesn't have key focus by default Bug #4368: Settings window ok button doesn't have key focus by default
Bug #4393: NPCs walk back to where they were after using ResetActors Bug #4393: NPCs walk back to where they were after using ResetActors
Bug #4416: Handle exception if we try to play non-music file Bug #4416: Handle exception if we try to play non-music file
@ -47,12 +52,23 @@
Bug #4457: Item without CanCarry flag prevents shield autoequipping in dark areas Bug #4457: Item without CanCarry flag prevents shield autoequipping in dark areas
Bug #4458: AiWander console command handles idle chances incorrectly Bug #4458: AiWander console command handles idle chances incorrectly
Bug #4459: NotCell dialogue condition doesn't support partial matches Bug #4459: NotCell dialogue condition doesn't support partial matches
Bug #4460: Script function "Equip" doesn't bypass beast restrictions
Bug #4461: "Open" spell from non-player caster isn't a crime Bug #4461: "Open" spell from non-player caster isn't a crime
Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages
Bug #4469: Abot Silt Striders Model turn 90 degrees on horizontal Bug #4469: Abot Silt Striders Model turn 90 degrees on horizontal
Bug #4474: No fallback when getVampireHead fails Bug #4474: No fallback when getVampireHead fails
Bug #4475: Scripted animations should not cause movement Bug #4475: Scripted animations should not cause movement
Bug #4479: "Game" category on Advanced page is getting too long Bug #4479: "Game" category on Advanced page is getting too long
Bug #4480: Segfalt in QuickKeysMenu when item no longer in inventory Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory
Bug #4489: Goodbye doesn't block dialogue hyperlinks
Bug #4490: PositionCell on player gives "Error: tried to add local script twice"
Bug #4491: Training cap based off Base Skill instead of Modified Skill
Bug #4495: Crossbow animations blending is buggy
Bug #4496: SpellTurnLeft and SpellTurnRight animation groups are unused
Bug #4497: File names starting with x or X are not classified as animation
Bug #4503: Cast and ExplodeSpell commands increase alteration skill
Feature #2606: Editor: Implemented (optional) case sensitive global search
Feature #3083: Play animation when NPC is casting spell via script
Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results
Feature #3641: Editor: Limit FPS in 3d preview window Feature #3641: Editor: Limit FPS in 3d preview window
Feature #4222: 360° screenshots Feature #4222: 360° screenshots
@ -62,6 +78,8 @@
Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically
Feature #4444: Per-group KF-animation files support Feature #4444: Per-group KF-animation files support
Feature #4466: Editor: Add option to ignore "Base" records when running verifier Feature #4466: Editor: Add option to ignore "Base" records when running verifier
Feature #4012: Editor: Write a log file if OpenCS crashes
Task #2490: Don't open command prompt window on Release-mode builds automatically
0.44.0 0.44.0
------ ------

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# set -x # turn-on for debugging
MISSINGTOOLS=0 MISSINGTOOLS=0
@ -76,7 +77,6 @@ while [ $# -gt 0 ]; do
h ) h )
cat <<EOF cat <<EOF
Usage: $0 [-cdehkpuvV] Usage: $0 [-cdehkpuvV]
Options: Options:
-c <Release/Debug> -c <Release/Debug>
Set the configuration, can also be set with environment variable CONFIGURATION. Set the configuration, can also be set with environment variable CONFIGURATION.
@ -232,10 +232,9 @@ fi
case $VS_VERSION in case $VS_VERSION in
15|15.0|2017 ) 15|15.0|2017 )
GENERATOR="Visual Studio 15 2017" GENERATOR="Visual Studio 15 2017"
TOOLSET="vc140" TOOLSET="vc141"
TOOLSET_REAL="vc141"
MSVC_REAL_VER="15" MSVC_REAL_VER="15"
MSVC_VER="14" MSVC_VER="14.1"
MSVC_YEAR="2015" MSVC_YEAR="2015"
MSVC_DISPLAY_YEAR="2017" MSVC_DISPLAY_YEAR="2017"
;; ;;
@ -243,9 +242,8 @@ case $VS_VERSION in
14|14.0|2015 ) 14|14.0|2015 )
GENERATOR="Visual Studio 14 2015" GENERATOR="Visual Studio 14 2015"
TOOLSET="vc140" TOOLSET="vc140"
TOOLSET_REAL="vc140"
MSVC_REAL_VER="14" MSVC_REAL_VER="14"
MSVC_VER="14" MSVC_VER="14.0"
MSVC_YEAR="2015" MSVC_YEAR="2015"
MSVC_DISPLAY_YEAR="2015" MSVC_DISPLAY_YEAR="2015"
;; ;;
@ -253,9 +251,8 @@ case $VS_VERSION in
12|12.0|2013 ) 12|12.0|2013 )
GENERATOR="Visual Studio 12 2013" GENERATOR="Visual Studio 12 2013"
TOOLSET="vc120" TOOLSET="vc120"
TOOLSET_REAL="vc120"
MSVC_REAL_VER="12" MSVC_REAL_VER="12"
MSVC_VER="12" MSVC_VER="12.0"
MSVC_YEAR="2013" MSVC_YEAR="2013"
MSVC_DISPLAY_YEAR="2013" MSVC_DISPLAY_YEAR="2013"
;; ;;
@ -325,9 +322,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
# Boost # Boost
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
download "Boost 1.61.0" \ download "Boost 1.67.0" \
"https://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/boost_1_61_0-msvc-${MSVC_VER}.0-${BITS}.exe" \ "https://sourceforge.net/projects/boost/files/boost-binaries/1.67.0/boost_1_67_0-msvc-${MSVC_VER}-${BITS}.exe" \
"boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe" "boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe"
fi fi
# Bullet # Bullet
@ -365,8 +362,8 @@ if [ -z $SKIP_DOWNLOAD ]; then
QT_SUFFIX="" QT_SUFFIX=""
fi fi
download "Qt 5.7.2" \ download "Qt 5.7.0" \
"https://download.qt.io/official_releases/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \ "https://download.qt.io/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" \ "qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \ "https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
"qt-5-install.qs" "qt-5-install.qs"
@ -403,9 +400,9 @@ echo
# Boost # Boost
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
printf "Boost 1.61.0... " printf "Boost 1.67.0... "
else else
if [ $MSVC_VER -eq 12 ]; then if [ $MSVC_VER -eq 12.0 ]; then
printf "Boost 1.58.0 AppVeyor... " printf "Boost 1.58.0 AppVeyor... "
else else
printf "Boost 1.67.0 AppVeyor... " printf "Boost 1.67.0 AppVeyor... "
@ -417,17 +414,27 @@ fi
BOOST_SDK="$(real_pwd)/Boost" BOOST_SDK="$(real_pwd)/Boost"
if [ -d Boost ] && grep "BOOST_VERSION 106100" Boost/boost/version.hpp > /dev/null; then # Boost's installer is still based on ms-dos API that doesn't support larger than 260 char path names
# 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,")
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;
fi
if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION 106700" Boost/boost/version.hpp > /dev/null; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf Boost rm -rf Boost
"${DEPS}/boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe" //dir="$(echo $BOOST_SDK | sed s,/,\\\\,g)" //verysilent [ -n "$CI" ] && CI_EXTRA_INNO_OPTIONS="//SUPPRESSMSGBOXES //LOG='boost_install.log'"
"${DEPS}/boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe" //DIR="${CWD_DRIVE_ROOT}" //VERYSILENT //NORESTART ${CI_EXTRA_INNO_OPTIONS}
mv "${CWD_DRIVE_ROOT_BASH}" "${BOOST_SDK}"
fi fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \ add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.0" -DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}"
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}" add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
echo Done. echo Done.
else else
# Appveyor unstable has all the boost we need already # Appveyor unstable has all the boost we need already
@ -444,19 +451,17 @@ fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \ add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}" -DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}"
add_cmake_opts -DBoost_COMPILER="-${TOOLSET_REAL}" add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
echo Done. echo Done.
fi fi
} }
cd $DEPS cd $DEPS
echo echo
# Bullet # Bullet
printf "Bullet 2.86... " printf "Bullet 2.86... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d Bullet ]; then if [ -d Bullet ]; then
printf -- "Exists. (No version checking) " printf -- "Exists. (No version checking) "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
@ -464,49 +469,38 @@ printf "Bullet 2.86... "
eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
fi fi
export BULLET_ROOT="$(real_pwd)/Bullet" export BULLET_ROOT="$(real_pwd)/Bullet"
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# FFmpeg # FFmpeg
printf "FFmpeg 3.2.4... " printf "FFmpeg 3.2.4... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf FFmpeg rm -rf FFmpeg
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-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 mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg
cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/ cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/
rm -rf "ffmpeg-3.2.4-win${BITS}-dev" rm -rf "ffmpeg-3.2.4-win${BITS}-dev"
fi fi
export FFMPEG_HOME="$(real_pwd)/FFmpeg" export FFMPEG_HOME="$(real_pwd)/FFmpeg"
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
if [ $BITS -eq 32 ]; then if [ $BITS -eq 32 ]; then
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\"" add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
fi fi
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# MyGUI # MyGUI
printf "MyGUI 3.2.2... " printf "MyGUI 3.2.2... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d MyGUI ] && \ if [ -d MyGUI ] && \
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
@ -518,21 +512,17 @@ printf "MyGUI 3.2.2... "
eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP 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 mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI
fi fi
export MYGUI_HOME="$(real_pwd)/MyGUI" export MYGUI_HOME="$(real_pwd)/MyGUI"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="_d" SUFFIX="_d"
else else
SUFFIX="" SUFFIX=""
fi fi
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll" add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# OpenAL # OpenAL
printf "OpenAL-Soft 1.17.2... " printf "OpenAL-Soft 1.17.2... "
{ {
@ -542,24 +532,18 @@ printf "OpenAL-Soft 1.17.2... "
rm -rf openal-soft-1.17.2-bin rm -rf openal-soft-1.17.2-bin
eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP
fi fi
OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin" OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin"
add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \ add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib" -DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib"
add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll" add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# OSG # OSG
printf "OSG 3.4.1-scrawl... " printf "OSG 3.4.1-scrawl... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d OSG ] && \ if [ -d OSG ] && \
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \ grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \ grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
@ -571,28 +555,21 @@ printf "OSG 3.4.1-scrawl... "
eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP 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 mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
fi fi
OSG_SDK="$(real_pwd)/OSG" OSG_SDK="$(real_pwd)/OSG"
add_cmake_opts -DOSG_DIR="$OSG_SDK" add_cmake_opts -DOSG_DIR="$OSG_SDK"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d" SUFFIX="d"
else else
SUFFIX="" SUFFIX=""
fi fi
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \ add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer}${SUFFIX}.dll "$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,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}${SUFFIX}.dll add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer}${SUFFIX}.dll
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# Qt # Qt
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
printf "Qt 5.7.0... " printf "Qt 5.7.0... "
@ -605,71 +582,53 @@ fi
else else
SUFFIX="" SUFFIX=""
fi fi
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
cd $DEPS_INSTALL cd $DEPS_INSTALL
QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}" QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf Qt rm -rf Qt
cp "${DEPS}/qt-5-install.qs" qt-install.qs cp "${DEPS}/qt-5-install.qs" qt-install.qs
sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" 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 sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs
printf -- "(Installation might take a while) " printf -- "(Installation might take a while) "
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent "${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent
mv qt-install.qs Qt/ mv qt-install.qs Qt/
echo Done. echo Done.
printf " Cleaning up extraneous data... " printf " Cleaning up extraneous data... "
rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs} rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs}
fi fi
cd $QT_SDK cd $QT_SDK
add_cmake_opts -DDESIRED_QT_VERSION=5 \ add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK" -DCMAKE_PREFIX_PATH="$QT_SDK"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d" SUFFIX="d"
else else
SUFFIX="" SUFFIX=""
fi fi
add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll" add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
echo Done. echo Done.
else else
QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}" QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}"
add_cmake_opts -DDESIRED_QT_VERSION=5 \ add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK" -DCMAKE_PREFIX_PATH="$QT_SDK"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d" SUFFIX="d"
else else
SUFFIX="" SUFFIX=""
fi fi
DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,") DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll" add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll"
echo Done. echo Done.
fi fi
} }
cd $DEPS cd $DEPS
echo echo
# SDL2 # SDL2
printf "SDL 2.0.7... " printf "SDL 2.0.7... "
{ {
@ -679,26 +638,18 @@ printf "SDL 2.0.7... "
rm -rf SDL2-2.0.7 rm -rf SDL2-2.0.7
eval 7z x -y SDL2-2.0.7.zip $STRIP eval 7z x -y SDL2-2.0.7.zip $STRIP
fi fi
export SDL2DIR="$(real_pwd)/SDL2-2.0.7" export SDL2DIR="$(real_pwd)/SDL2-2.0.7"
add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll" add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll"
echo Done. echo Done.
} }
echo echo
cd $DEPS_INSTALL/.. cd $DEPS_INSTALL/..
echo echo
echo "Setting up OpenMW build..." echo "Setting up OpenMW build..."
add_cmake_opts -DBUILD_BSATOOL=no \ add_cmake_opts -DBUILD_BSATOOL=no \
-DBUILD_ESMTOOL=no \ -DBUILD_ESMTOOL=no \
-DBUILD_MYGUI_PLUGIN=no \ -DBUILD_MYGUI_PLUGIN=no \
-DOPENMW_MP_BUILD=on -DOPENMW_MP_BUILD=on
if [ ! -z $CI ]; then if [ ! -z $CI ]; then
case $STEP in case $STEP in
components ) components )
@ -710,7 +661,6 @@ if [ ! -z $CI ]; then
-DBUILD_OPENMW=no \ -DBUILD_OPENMW=no \
-DBUILD_WIZARD=no -DBUILD_WIZARD=no
;; ;;
openmw ) openmw )
echo " Building subproject: OpenMW." echo " Building subproject: OpenMW."
add_cmake_opts -DBUILD_ESSIMPORTER=no \ add_cmake_opts -DBUILD_ESSIMPORTER=no \
@ -719,7 +669,6 @@ if [ ! -z $CI ]; then
-DBUILD_OPENCS=no \ -DBUILD_OPENCS=no \
-DBUILD_WIZARD=no -DBUILD_WIZARD=no
;; ;;
opencs ) opencs )
echo " Building subproject: OpenCS." echo " Building subproject: OpenCS."
add_cmake_opts -DBUILD_ESSIMPORTER=no \ add_cmake_opts -DBUILD_ESSIMPORTER=no \
@ -728,7 +677,6 @@ if [ ! -z $CI ]; then
-DBUILD_OPENMW=no \ -DBUILD_OPENMW=no \
-DBUILD_WIZARD=no -DBUILD_WIZARD=no
;; ;;
misc ) misc )
echo " Building subprojects: Misc." echo " Building subprojects: Misc."
add_cmake_opts -DBUILD_OPENCS=no \ add_cmake_opts -DBUILD_OPENCS=no \
@ -736,7 +684,6 @@ if [ ! -z $CI ]; then
;; ;;
esac esac
fi fi
# NOTE: Disable this when/if we want to run test cases # NOTE: Disable this when/if we want to run test cases
#if [ -z $CI ]; then #if [ -z $CI ]; then
echo "- Copying Runtime DLLs..." echo "- Copying Runtime DLLs..."
@ -745,16 +692,13 @@ fi
TARGET="$(basename "$DLL")" TARGET="$(basename "$DLL")"
if [[ "$DLL" == *":"* ]]; then if [[ "$DLL" == *":"* ]]; then
IFS=':'; SPLIT=( ${DLL} ); unset IFS IFS=':'; SPLIT=( ${DLL} ); unset IFS
DLL=${SPLIT[0]} DLL=${SPLIT[0]}
TARGET=${SPLIT[1]} TARGET=${SPLIT[1]}
fi fi
echo " ${TARGET}." echo " ${TARGET}."
cp "$DLL" "$BUILD_CONFIG/$TARGET" cp "$DLL" "$BUILD_CONFIG/$TARGET"
done done
echo echo
echo "- OSG Plugin DLLs..." echo "- OSG Plugin DLLs..."
mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1 mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1
for DLL in $OSG_PLUGINS; do for DLL in $OSG_PLUGINS; do
@ -762,7 +706,6 @@ fi
cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1 cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1
done done
echo echo
echo "- Qt Platform DLLs..." echo "- Qt Platform DLLs..."
mkdir -p ${BUILD_CONFIG}/platforms mkdir -p ${BUILD_CONFIG}/platforms
for DLL in $QT_PLATFORMS; do for DLL in $QT_PLATFORMS; do
@ -771,16 +714,13 @@ fi
done done
echo echo
#fi #fi
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
printf -- "- Configuring... " printf -- "- Configuring... "
else else
echo "- cmake .. $CMAKE_OPTS" echo "- cmake .. $CMAKE_OPTS"
fi fi
run_cmd cmake .. $CMAKE_OPTS run_cmd cmake .. $CMAKE_OPTS
RET=$? RET=$?
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
if [ $RET -eq 0 ]; then if [ $RET -eq 0 ]; then
echo Done. echo Done.
@ -788,5 +728,4 @@ if [ -z $VERBOSE ]; then
echo Failed. echo Failed.
fi fi
fi fi
exit $RET exit $RET

@ -666,10 +666,10 @@ if (WIN32)
endif() endif()
if (BUILD_OPENMW) if (BUILD_OPENMW)
# Release builds use the debug console # Release builds don't use the debug console
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE") set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE") set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_RELEASE "_WINDOWS")
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE") set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
endif() endif()
# Play a bit with the warning levels # Play a bit with the warning levels
@ -680,7 +680,8 @@ if (WIN32)
# Warnings that aren't enabled normally and don't need to be enabled # Warnings that aren't enabled normally and don't need to be enabled
# They're unneeded and sometimes completely retarded warnings that /Wall enables # They're unneeded and sometimes completely retarded warnings that /Wall enables
# Not going to bother commenting them as they tend to warn on every standard library file # Not going to bother commenting them as they tend to warn on every standard library file
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946 4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625
4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045
# Warnings that are thrown on standard libraries and not OpenMW # Warnings that are thrown on standard libraries and not OpenMW
4347 # Non-template function with same name and parameter count as template function 4347 # Non-template function with same name and parameter count as template function
@ -701,6 +702,7 @@ if (WIN32)
# caused by MyGUI # caused by MyGUI
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception' 4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'
4297 # function assumed not to throw an exception but does
# OpenMW specific warnings # OpenMW specific warnings
4099 # Type mismatch, declared class or struct is defined with other type 4099 # Type mismatch, declared class or struct is defined with other type

@ -874,7 +874,6 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
{ {
std::vector<std::pair<std::time_t, boost::filesystem::path>> contentFiles; std::vector<std::pair<std::time_t, boost::filesystem::path>> contentFiles;
std::string baseGameFile("Game Files:GameFile"); std::string baseGameFile("Game Files:GameFile");
std::string gameFile("");
std::time_t defaultTime = 0; std::time_t defaultTime = 0;
ToUTF8::Utf8Encoder encoder(mEncoding); ToUTF8::Utf8Encoder encoder(mEncoding);
@ -890,7 +889,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
multistrmap::const_iterator it = ini.begin(); multistrmap::const_iterator it = ini.begin();
for (int i=0; it != ini.end(); i++) for (int i=0; it != ini.end(); i++)
{ {
gameFile = baseGameFile; std::string gameFile = baseGameFile;
gameFile.append(std::to_string(i)); gameFile.append(std::to_string(i));
it = ini.find(gameFile); it = ini.find(gameFile);

@ -5,6 +5,9 @@
#include <QLocalSocket> #include <QLocalSocket>
#include <QMessageBox> #include <QMessageBox>
#include <components/crashcatcher/crashcatcher.hpp>
#include <components/fallback/validate.hpp> #include <components/fallback/validate.hpp>
#include <components/nifosg/nifloader.hpp> #include <components/nifosg/nifloader.hpp>
@ -18,12 +21,16 @@
using namespace Fallback; using namespace Fallback;
CS::Editor::Editor () CS::Editor::Editor (int argc, char **argv)
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr), : mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mPid(""), mViewManager (mDocumentManager), mPid(""),
mLock(), mMerge (mDocumentManager), mLock(), mMerge (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
{ {
// install the crash handler as soon as possible. note that the log path
// does not depend on config being read.
crashCatcherInstall(argc, argv, (mCfgMgr.getLogPath() / "openmw-cs-crash.log").string());
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig(); std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
setupDataFiles (config.first); setupDataFiles (config.first);

@ -66,7 +66,7 @@ namespace CS
public: public:
Editor (); Editor (int argc, char **argv);
~Editor (); ~Editor ();
bool makeIPCServer(); bool makeIPCServer();

@ -67,7 +67,7 @@ int main(int argc, char *argv[])
application.setWindowIcon (QIcon (":./openmw-cs.png")); application.setWindowIcon (QIcon (":./openmw-cs.png"));
CS::Editor editor; CS::Editor editor(argc, argv);
if(!editor.makeIPCServer()) if(!editor.makeIPCServer())
{ {

@ -22,7 +22,8 @@ void CSMTools::Search::searchTextCell (const CSMWorld::IdTableBase *model,
int pos = 0; int pos = 0;
while ((pos = text.indexOf (search, pos, Qt::CaseInsensitive))!=-1) Qt::CaseSensitivity caseSensitivity = mCase ? Qt::CaseSensitive : Qt::CaseInsensitive;
while ((pos = text.indexOf (search, pos, caseSensitivity))!=-1)
{ {
std::ostringstream hint; std::ostringstream hint;
hint hint
@ -120,25 +121,26 @@ QString CSMTools::Search::flatten (const QString& text) const
return flat; return flat;
} }
CSMTools::Search::Search() : mType (Type_None), mValue (0), mIdColumn (0), mTypeColumn (0), CSMTools::Search::Search() : mType (Type_None), mValue (0), mCase (false), mIdColumn (0), mTypeColumn (0),
mPaddingBefore (10), mPaddingAfter (10) {} mPaddingBefore (10), mPaddingAfter (10) {}
CSMTools::Search::Search (Type type, const std::string& value) CSMTools::Search::Search (Type type, bool caseSensitive, const std::string& value)
: mType (type), mText (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) : mType (type), mText (value), mValue (0), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
{ {
if (type!=Type_Text && type!=Type_Id) if (type!=Type_Text && type!=Type_Id)
throw std::logic_error ("Invalid search parameter (string)"); throw std::logic_error ("Invalid search parameter (string)");
} }
CSMTools::Search::Search (Type type, const QRegExp& value) CSMTools::Search::Search (Type type, bool caseSensitive, const QRegExp& value)
: mType (type), mRegExp (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) : mType (type), mRegExp (value), mValue (0), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
{ {
mRegExp.setCaseSensitivity(mCase ? Qt::CaseSensitive : Qt::CaseInsensitive);
if (type!=Type_TextRegEx && type!=Type_IdRegEx) if (type!=Type_TextRegEx && type!=Type_IdRegEx)
throw std::logic_error ("Invalid search parameter (RegExp)"); throw std::logic_error ("Invalid search parameter (RegExp)");
} }
CSMTools::Search::Search (Type type, int value) CSMTools::Search::Search (Type type, bool caseSensitive, int value)
: mType (type), mValue (value), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) : mType (type), mValue (value), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
{ {
if (type!=Type_RecordState) if (type!=Type_RecordState)
throw std::logic_error ("invalid search parameter (int)"); throw std::logic_error ("invalid search parameter (int)");

@ -43,6 +43,7 @@ namespace CSMTools
std::string mText; std::string mText;
QRegExp mRegExp; QRegExp mRegExp;
int mValue; int mValue;
bool mCase;
std::set<int> mColumns; std::set<int> mColumns;
int mIdColumn; int mIdColumn;
int mTypeColumn; int mTypeColumn;
@ -67,11 +68,11 @@ namespace CSMTools
Search(); Search();
Search (Type type, const std::string& value); Search (Type type, bool caseSensitive, const std::string& value);
Search (Type type, const QRegExp& value); Search (Type type, bool caseSensitive, const QRegExp& value);
Search (Type type, int value); Search (Type type, bool caseSensitive, int value);
// Configure search for the specified model. // Configure search for the specified model.
void configure (const CSMWorld::IdTableBase *model); void configure (const CSMWorld::IdTableBase *model);

@ -136,7 +136,7 @@ namespace CSMWorld
struct VarTypeColumn : public Column<ESXRecordT> struct VarTypeColumn : public Column<ESXRecordT>
{ {
VarTypeColumn (ColumnBase::Display display) VarTypeColumn (ColumnBase::Display display)
: Column<ESXRecordT> (Columns::ColumnId_ValueType, display) : Column<ESXRecordT> (Columns::ColumnId_ValueType, display, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)
{} {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
@ -161,7 +161,7 @@ namespace CSMWorld
template<typename ESXRecordT> template<typename ESXRecordT>
struct VarValueColumn : public Column<ESXRecordT> struct VarValueColumn : public Column<ESXRecordT>
{ {
VarValueColumn() : Column<ESXRecordT> (Columns::ColumnId_Value, ColumnBase::Display_Var) {} VarValueColumn() : Column<ESXRecordT> (Columns::ColumnId_Value, ColumnBase::Display_Var, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh) {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
{ {

@ -84,15 +84,28 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value
if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole) if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole)
{ {
mIdCollection->setData (index.row(), index.column(), value); mIdCollection->setData (index.row(), index.column(), value);
emit dataChanged(index, index);
// Modifying a value can also change the Modified status of a record.
int stateColumn = searchColumnIndex(Columns::ColumnId_Modification); int stateColumn = searchColumnIndex(Columns::ColumnId_Modification);
if (stateColumn != -1) if (stateColumn != -1)
{ {
if (index.column() == stateColumn)
{
// modifying the state column can modify other values. we need to tell
// views that the whole row has changed.
emit dataChanged(this->index(index.row(), 0),
this->index(index.row(), columnCount(index.parent())));
} else
{
emit dataChanged(index, index);
// Modifying a value can also change the Modified status of a record.
QModelIndex stateIndex = this->index(index.row(), stateColumn); QModelIndex stateIndex = this->index(index.row(), stateColumn);
emit dataChanged(stateIndex, stateIndex); emit dataChanged(stateIndex, stateIndex);
} }
} else
emit dataChanged(index, index);
return true; return true;
} }

@ -101,15 +101,39 @@ void CSVDoc::View::setupFileMenu()
file->addAction(exit); file->addAction(exit);
} }
namespace
{
void updateUndoRedoAction(QAction *action, const std::string &settingsKey)
{
QKeySequence seq;
CSMPrefs::State::get().getShortcutManager().getSequence(settingsKey, seq);
action->setShortcut(seq);
}
}
void CSVDoc::View::undoActionChanged()
{
updateUndoRedoAction(mUndo, "document-edit-undo");
}
void CSVDoc::View::redoActionChanged()
{
updateUndoRedoAction(mRedo, "document-edit-redo");
}
void CSVDoc::View::setupEditMenu() void CSVDoc::View::setupEditMenu()
{ {
QMenu *edit = menuBar()->addMenu (tr ("Edit")); QMenu *edit = menuBar()->addMenu (tr ("Edit"));
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo")); mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo"));
setupShortcut("document-edit-undo", mUndo); setupShortcut("document-edit-undo", mUndo);
connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ()));
edit->addAction (mUndo); edit->addAction (mUndo);
mRedo= mDocument->getUndoStack().createRedoAction (this, tr("Redo")); mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo"));
connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ()));
setupShortcut("document-edit-redo", mRedo); setupShortcut("document-edit-redo", mRedo);
edit->addAction (mRedo); edit->addAction (mRedo);

@ -152,6 +152,10 @@ namespace CSVDoc
void settingChanged (const CSMPrefs::Setting *setting); void settingChanged (const CSMPrefs::Setting *setting);
void undoActionChanged();
void redoActionChanged();
void newView(); void newView();
void save(); void save();

@ -372,10 +372,10 @@ osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker (int axis)
indices[j] -= VertexCount; indices[j] -= VertexCount;
} }
size_t offset = i * IndicesPerSegment; size_t elementOffset = i * IndicesPerSegment;
for (size_t j = 0; j < IndicesPerSegment; ++j) for (size_t j = 0; j < IndicesPerSegment; ++j)
{ {
primitives->setElement(offset++, indices[IndexPattern[j]]); primitives->setElement(elementOffset++, indices[IndexPattern[j]]);
} }
} }

@ -651,7 +651,6 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
if (mDragMode == InteractionType_PrimaryEdit) if (mDragMode == InteractionType_PrimaryEdit)
{ {
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos
} }
} }

@ -35,7 +35,7 @@ void CSVTools::SearchBox::updateSearchButton()
} }
CSVTools::SearchBox::SearchBox (QWidget *parent) CSVTools::SearchBox::SearchBox (QWidget *parent)
: QWidget (parent), mSearch ("Search"), mSearchEnabled (false), mReplace ("Replace All") : QWidget (parent), mSearch (tr("Search")), mSearchEnabled (false), mReplace (tr("Replace All"))
{ {
mLayout = new QGridLayout (this); mLayout = new QGridLayout (this);
@ -48,29 +48,26 @@ CSVTools::SearchBox::SearchBox (QWidget *parent)
++iter) ++iter)
mRecordState.addItem (QString::fromUtf8 (iter->c_str())); mRecordState.addItem (QString::fromUtf8 (iter->c_str()));
mMode.addItem ("Text"); mMode.addItem (tr("Text"));
mMode.addItem ("Text (RegEx)"); mMode.addItem (tr("Text (RegEx)"));
mMode.addItem ("ID"); mMode.addItem (tr("ID"));
mMode.addItem ("ID (RegEx)"); mMode.addItem (tr("ID (RegEx)"));
mMode.addItem ("Record State"); mMode.addItem (tr("Record State"));
connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int)));
mLayout->addWidget (&mMode, 0, 0); mLayout->addWidget (&mMode, 0, 0);
mLayout->addWidget (&mSearch, 0, 3); connect (&mText, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
connect (&mText, SIGNAL (returnPressed()), this, SLOT (startSearch()));
mInput.insertWidget (0, &mText); mInput.insertWidget (0, &mText);
mInput.insertWidget (1, &mRecordState);
mInput.insertWidget (1, &mRecordState);
mLayout->addWidget (&mInput, 0, 1); mLayout->addWidget (&mInput, 0, 1);
connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int))); mCaseSensitive.setText (tr ("Case"));
mLayout->addWidget (&mCaseSensitive, 0, 2);
connect (&mText, SIGNAL (textChanged (const QString&)),
this, SLOT (textChanged (const QString&)));
connect (&mSearch, SIGNAL (clicked (bool)), this, SLOT (startSearch (bool))); connect (&mSearch, SIGNAL (clicked (bool)), this, SLOT (startSearch (bool)));
mLayout->addWidget (&mSearch, 0, 3);
connect (&mText, SIGNAL (returnPressed()), this, SLOT (startSearch()));
// replace panel // replace panel
mReplaceInput.insertWidget (0, &mReplaceText); mReplaceInput.insertWidget (0, &mReplaceText);
@ -103,22 +100,23 @@ void CSVTools::SearchBox::setSearchMode (bool enabled)
CSMTools::Search CSVTools::SearchBox::getSearch() const CSMTools::Search CSVTools::SearchBox::getSearch() const
{ {
CSMTools::Search::Type type = static_cast<CSMTools::Search::Type> (mMode.currentIndex()); CSMTools::Search::Type type = static_cast<CSMTools::Search::Type> (mMode.currentIndex());
bool caseSensitive = mCaseSensitive.isChecked();
switch (type) switch (type)
{ {
case CSMTools::Search::Type_Text: case CSMTools::Search::Type_Text:
case CSMTools::Search::Type_Id: case CSMTools::Search::Type_Id:
return CSMTools::Search (type, std::string (mText.text().toUtf8().data())); return CSMTools::Search (type, caseSensitive, std::string (mText.text().toUtf8().data()));
case CSMTools::Search::Type_TextRegEx: case CSMTools::Search::Type_TextRegEx:
case CSMTools::Search::Type_IdRegEx: case CSMTools::Search::Type_IdRegEx:
return CSMTools::Search (type, QRegExp (mText.text().toUtf8().data(), Qt::CaseInsensitive)); return CSMTools::Search (type, caseSensitive, QRegExp (mText.text().toUtf8().data(), Qt::CaseInsensitive));
case CSMTools::Search::Type_RecordState: case CSMTools::Search::Type_RecordState:
return CSMTools::Search (type, mRecordState.currentIndex()); return CSMTools::Search (type, caseSensitive, mRecordState.currentIndex());
case CSMTools::Search::Type_None: case CSMTools::Search::Type_None:

@ -4,6 +4,7 @@
#include <QWidget> #include <QWidget>
#include <QLineEdit> #include <QLineEdit>
#include <QComboBox> #include <QComboBox>
#include <QCheckBox>
#include <QStackedWidget> #include <QStackedWidget>
#include <QPushButton> #include <QPushButton>
#include <QLabel> #include <QLabel>
@ -24,6 +25,7 @@ namespace CSVTools
QStackedWidget mInput; QStackedWidget mInput;
QLineEdit mText; QLineEdit mText;
QComboBox mRecordState; QComboBox mRecordState;
QCheckBox mCaseSensitive;
QPushButton mSearch; QPushButton mSearch;
QGridLayout *mLayout; QGridLayout *mLayout;
QComboBox mMode; QComboBox mMode;

@ -11,12 +11,10 @@ if (ANDROID)
set(GAME ${GAME} android_main.c) set(GAME ${GAME} android_main.c)
endif() endif()
if(NOT WIN32 AND NOT ANDROID)
set(GAME ${GAME} crashcatcher.cpp)
endif()
set(GAME_HEADER set(GAME_HEADER
engine.hpp engine.hpp
) )
source_group(game FILES ${GAME} ${GAME_HEADER}) source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
@ -83,9 +81,9 @@ add_openmw_dir (mwclass
add_openmw_dir (mwmechanics add_openmw_dir (mwmechanics
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting aicast aiescort aiface aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
character actors objects aistate coordinateconverter trading aiface weaponpriority spellpriority character actors objects aistate coordinateconverter trading weaponpriority spellpriority
) )
add_openmw_dir (mwstate add_openmw_dir (mwstate

@ -757,28 +757,19 @@ void OMW::Engine::go()
std::cout << "OSG version: " << osgGetVersion() << std::endl; std::cout << "OSG version: " << osgGetVersion() << std::endl;
mViewer = new osgViewer::Viewer; // Load settings
mViewer->setReleaseContextAtEndOfFrameHint(false);
osg::ref_ptr<osgViewer::StatsHandler> statshandler = new osgViewer::StatsHandler;
statshandler->setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3);
statshandler->addUserStatsLine("Script", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
"script_time_taken", 1000.0, true, false, "script_time_begin", "script_time_end", 10000);
statshandler->addUserStatsLine("Mechanics", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
"mechanics_time_taken", 1000.0, true, false, "mechanics_time_begin", "mechanics_time_end", 10000);
statshandler->addUserStatsLine("Physics", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
"physics_time_taken", 1000.0, true, false, "physics_time_begin", "physics_time_end", 10000);
mViewer->addEventHandler(statshandler);
mViewer->addEventHandler(new Resource::StatsHandler);
Settings::Manager settings; Settings::Manager settings;
std::string settingspath; std::string settingspath;
settingspath = loadSettings (settings); settingspath = loadSettings (settings);
// Create encoder
ToUTF8::Utf8Encoder encoder (mEncoding);
mEncoder = &encoder;
// Setup viewer
mViewer = new osgViewer::Viewer;
mViewer->setReleaseContextAtEndOfFrameHint(false);
mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(), mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
Settings::Manager::getString("screenshot format", "General")); Settings::Manager::getString("screenshot format", "General"));
@ -788,10 +779,6 @@ void OMW::Engine::go()
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video")); mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));
// Create encoder
ToUTF8::Utf8Encoder encoder (mEncoding);
mEncoder = &encoder;
prepareEngine (settings); prepareEngine (settings);
/* /*
@ -814,6 +801,22 @@ void OMW::Engine::go()
End of tes3mp change (major) End of tes3mp change (major)
*/ */
// Setup profiler
osg::ref_ptr<Resource::Profiler> statshandler = new Resource::Profiler;
statshandler->addUserStatsLine("Script", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
"script_time_taken", 1000.0, true, false, "script_time_begin", "script_time_end", 10000);
statshandler->addUserStatsLine("Mechanics", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
"mechanics_time_taken", 1000.0, true, false, "mechanics_time_begin", "mechanics_time_end", 10000);
statshandler->addUserStatsLine("Physics", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
"physics_time_taken", 1000.0, true, false, "physics_time_begin", "physics_time_end", 10000);
mViewer->addEventHandler(statshandler);
osg::ref_ptr<Resource::StatsHandler> resourceshandler = new Resource::StatsHandler;
mViewer->addEventHandler(resourceshandler);
// Start the game
if (!mSaveGameFile.empty()) if (!mSaveGameFile.empty())
{ {
mEnvironment.getStateManager()->loadGame(mSaveGameFile); mEnvironment.getStateManager()->loadGame(mSaveGameFile);

@ -1,6 +1,7 @@
#include <iostream> #include <iostream>
#include <components/version/version.hpp> #include <components/version/version.hpp>
#include <components/crashcatcher/crashcatcher.hpp>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <components/files/escape.hpp> #include <components/files/escape.hpp>
#include <components/fallback/validate.hpp> #include <components/fallback/validate.hpp>
@ -34,18 +35,6 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#if (defined(__APPLE__) || (defined(__linux) && !defined(ANDROID)) || (defined(__unix) && !defined(ANDROID)) || defined(__posix))
#define USE_CRASH_CATCHER 1
#else
#define USE_CRASH_CATCHER 0
#endif
#if USE_CRASH_CATCHER
#include <csignal>
extern int cc_install_handlers(int argc, char **argv, int num_signals, int *sigs, const char *logfile, int (*user_info)(char*, char*));
extern int is_debugger_attached(void);
#endif
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -419,18 +408,7 @@ int main(int argc, char**argv)
End of tes3mp addition End of tes3mp addition
*/ */
crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / "crash.log").string());
#if USE_CRASH_CATCHER
// Unix crash catcher
if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached())
{
int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };
cc_install_handlers(argc, argv, 5, s, (cfgMgr.getLogPath() / "crash.log").string().c_str(), NULL);
std::cout << "Installing crash catcher" << std::endl;
}
else
std::cout << "Running in a debugger, not installing crash catcher" << std::endl;
#endif
#ifdef __APPLE__ #ifdef __APPLE__
boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0])); boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));

@ -235,9 +235,12 @@ namespace MWBase
/// Resurrects the player if necessary /// Resurrects the player if necessary
virtual void keepPlayerAlive() = 0; virtual void keepPlayerAlive() = 0;
virtual bool isCastingSpell (const MWWorld::Ptr& ptr) const = 0;
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0; virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0;
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const = 0; virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const = 0;
virtual void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell) = 0;
/// Check if the target actor was detected by an observer /// Check if the target actor was detected by an observer
/// If the observer is a non-NPC, check all actors in AI processing distance as observers /// If the observer is a non-NPC, check all actors in AI processing distance as observers
virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) = 0; virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) = 0;

@ -157,7 +157,7 @@ namespace MWBase
*/ */
/// Make the player use an item, while updating GUI state accordingly /// Make the player use an item, while updating GUI state accordingly
virtual void useItem(const MWWorld::Ptr& item) = 0; virtual void useItem(const MWWorld::Ptr& item, bool force=false) = 0;
virtual void updateSpellWindow() = 0; virtual void updateSpellWindow() = 0;

@ -583,7 +583,7 @@ namespace MWBase
*/ */
virtual bool startSpellCast (const MWWorld::Ptr& actor) = 0; virtual bool startSpellCast (const MWWorld::Ptr& actor) = 0;
virtual void castSpell (const MWWorld::Ptr& actor) = 0; virtual void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) = 0;
virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0; virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0;
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,

@ -157,10 +157,9 @@ namespace MWClass
return info; return info;
} }
std::shared_ptr<MWWorld::Action> Apparatus::use (const MWWorld::Ptr& ptr, bool force) const
std::shared_ptr<MWWorld::Action> Apparatus::use (const MWWorld::Ptr& ptr) const
{ {
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionAlchemy()); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionAlchemy(force));
} }
MWWorld::Ptr Apparatus::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const MWWorld::Ptr Apparatus::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const

@ -50,8 +50,7 @@ namespace MWClass
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
///< Return name of inventory icon. ///< Return name of inventory icon.
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;

@ -387,9 +387,9 @@ namespace MWClass
return std::make_pair(1,""); return std::make_pair(1,"");
} }
std::shared_ptr<MWWorld::Action> Armor::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Armor::use (const MWWorld::Ptr& ptr, bool force) const
{ {
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr)); std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
action->setSound(getUpSoundId(ptr)); action->setSound(getUpSoundId(ptr));

@ -73,8 +73,7 @@ namespace MWClass
///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. \n ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. \n
/// Second item in the pair specifies the error message /// Second item in the pair specifies the error message
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;

@ -195,7 +195,7 @@ namespace MWClass
return record->mId; return record->mId;
} }
std::shared_ptr<MWWorld::Action> Book::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Book::use (const MWWorld::Ptr& ptr, bool force) const
{ {
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRead(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRead(ptr));
} }

@ -53,7 +53,7 @@ namespace MWClass
virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const;
///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it.
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) const; virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;

@ -276,9 +276,9 @@ namespace MWClass
return std::make_pair (1, ""); return std::make_pair (1, "");
} }
std::shared_ptr<MWWorld::Action> Clothing::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Clothing::use (const MWWorld::Ptr& ptr, bool force) const
{ {
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr)); std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
action->setSound(getUpSoundId(ptr)); action->setSound(getUpSoundId(ptr));

@ -65,8 +65,7 @@ namespace MWClass
///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that.
/// Second item in the pair specifies the error message /// Second item in the pair specifies the error message
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;

@ -108,7 +108,7 @@ namespace MWClass
} }
std::shared_ptr<MWWorld::Action> Ingredient::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Ingredient::use (const MWWorld::Ptr& ptr, bool force) const
{ {
std::shared_ptr<MWWorld::Action> action (new MWWorld::ActionEat (ptr)); std::shared_ptr<MWWorld::Action> action (new MWWorld::ActionEat (ptr));

@ -36,8 +36,7 @@ namespace MWClass
virtual int getValue (const MWWorld::ConstPtr& ptr) const; virtual int getValue (const MWWorld::ConstPtr& ptr) const;
///< Return trade value of the object. Throws an exception, if the object can't be traded. ///< Return trade value of the object. Throws an exception, if the object can't be traded.
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
static void registerSelf(); static void registerSelf();

@ -186,9 +186,9 @@ namespace MWClass
return Class::showsInInventory(ptr); return Class::showsInInventory(ptr);
} }
std::shared_ptr<MWWorld::Action> Light::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Light::use (const MWWorld::Ptr& ptr, bool force) const
{ {
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr)); std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
action->setSound(getUpSoundId(ptr)); action->setSound(getUpSoundId(ptr));

@ -55,8 +55,7 @@ namespace MWClass
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
///< Return name of inventory icon. ///< Return name of inventory icon.
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
virtual void setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const; virtual void setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const;

@ -140,9 +140,9 @@ namespace MWClass
return info; return info;
} }
std::shared_ptr<MWWorld::Action> Lockpick::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Lockpick::use (const MWWorld::Ptr& ptr, bool force) const
{ {
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr)); std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
action->setSound(getUpSoundId(ptr)); action->setSound(getUpSoundId(ptr));

@ -53,8 +53,7 @@ namespace MWClass
virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const; virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const;
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;

@ -259,7 +259,7 @@ namespace MWClass
return newPtr; return newPtr;
} }
std::shared_ptr<MWWorld::Action> Miscellaneous::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Miscellaneous::use (const MWWorld::Ptr& ptr, bool force) const
{ {
if (ptr.getCellRef().getSoul().empty() || !MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(ptr.getCellRef().getSoul())) if (ptr.getCellRef().getSoul().empty() || !MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(ptr.getCellRef().getSoul()))
return std::shared_ptr<MWWorld::Action>(new MWWorld::NullAction()); return std::shared_ptr<MWWorld::Action>(new MWWorld::NullAction());

@ -49,8 +49,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual float getWeight (const MWWorld::ConstPtr& ptr) const;

@ -172,7 +172,7 @@ namespace MWClass
return info; return info;
} }
std::shared_ptr<MWWorld::Action> Potion::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Potion::use (const MWWorld::Ptr& ptr, bool force) const
{ {
MWWorld::LiveCellRef<ESM::Potion> *ref = MWWorld::LiveCellRef<ESM::Potion> *ref =
ptr.get<ESM::Potion>(); ptr.get<ESM::Potion>();

@ -36,7 +36,7 @@ namespace MWClass
virtual int getValue (const MWWorld::ConstPtr& ptr) const; virtual int getValue (const MWWorld::ConstPtr& ptr) const;
///< Return trade value of the object. Throws an exception, if the object can't be traded. ///< Return trade value of the object. Throws an exception, if the object can't be traded.
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) const; virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
static void registerSelf(); static void registerSelf();

@ -140,9 +140,9 @@ namespace MWClass
return info; return info;
} }
std::shared_ptr<MWWorld::Action> Probe::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Probe::use (const MWWorld::Ptr& ptr, bool force) const
{ {
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr)); std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
action->setSound(getUpSoundId(ptr)); action->setSound(getUpSoundId(ptr));

@ -53,8 +53,7 @@ namespace MWClass
virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const; virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const;
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;

@ -182,9 +182,9 @@ namespace MWClass
return MWWorld::Ptr(cell.insert(ref), &cell); return MWWorld::Ptr(cell.insert(ref), &cell);
} }
std::shared_ptr<MWWorld::Action> Repair::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Repair::use (const MWWorld::Ptr& ptr, bool force) const
{ {
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRepair(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRepair(ptr, force));
} }
bool Repair::canSell (const MWWorld::ConstPtr& item, int npcServices) const bool Repair::canSell (const MWWorld::ConstPtr& item, int npcServices) const

@ -49,7 +49,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false)
const; const;
///< Generate action for using via inventory menu (default implementation: return a ///< Generate action for using via inventory menu (default implementation: return a
/// null action). /// null action).

@ -435,9 +435,9 @@ namespace MWClass
return std::make_pair(1, ""); return std::make_pair(1, "");
} }
std::shared_ptr<MWWorld::Action> Weapon::use (const MWWorld::Ptr& ptr) const std::shared_ptr<MWWorld::Action> Weapon::use (const MWWorld::Ptr& ptr, bool force) const
{ {
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr)); std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
action->setSound(getUpSoundId(ptr)); action->setSound(getUpSoundId(ptr));

@ -72,8 +72,7 @@ namespace MWClass
///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that.
/// Second item in the pair specifies the error message /// Second item in the pair specifies the error message
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
const;
///< Generate action for using via inventory menu ///< Generate action for using via inventory menu
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;

@ -629,6 +629,9 @@ namespace MWGui
void DialogueWindow::onTopicActivated(const std::string &topicId) void DialogueWindow::onTopicActivated(const std::string &topicId)
{ {
if (mGoodbye)
return;
MWBase::Environment::get().getDialogueManager()->keywordSelected(topicId, mCallback.get()); MWBase::Environment::get().getDialogueManager()->keywordSelected(topicId, mCallback.get());
updateTopics(); updateTopics();
} }

@ -39,7 +39,7 @@
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/action.hpp" #include "../mwworld/actionequip.hpp"
#include "../mwscript/interpretercontext.hpp" #include "../mwscript/interpretercontext.hpp"
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
@ -488,7 +488,7 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory);
} }
void InventoryWindow::useItem(const MWWorld::Ptr &ptr) void InventoryWindow::useItem(const MWWorld::Ptr &ptr, bool force)
{ {
const std::string& script = ptr.getClass().getScript(ptr); const std::string& script = ptr.getClass().getScript(ptr);
@ -496,8 +496,18 @@ namespace MWGui
// early-out for items that need to be equipped, but can't be equipped: we don't want to set OnPcEquip in that case // early-out for items that need to be equipped, but can't be equipped: we don't want to set OnPcEquip in that case
if (!ptr.getClass().getEquipmentSlots(ptr).first.empty()) if (!ptr.getClass().getEquipmentSlots(ptr).first.empty())
{
if (ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}");
updateItemView();
return;
}
if (!force)
{ {
std::pair<int, std::string> canEquip = ptr.getClass().canBeEquipped(ptr, player); std::pair<int, std::string> canEquip = ptr.getClass().canBeEquipped(ptr, player);
if (canEquip.first == 0) if (canEquip.first == 0)
{ {
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second); MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
@ -505,6 +515,7 @@ namespace MWGui
return; return;
} }
} }
}
// If the item has a script, set its OnPcEquip to 1 // If the item has a script, set its OnPcEquip to 1
if (!script.empty() if (!script.empty()
@ -526,9 +537,8 @@ namespace MWGui
{ {
if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0) if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0)
{ {
std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr); std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);
action->execute(player);
action->execute (player);
} }
else else
mSkippedToEquip = ptr; mSkippedToEquip = ptr;

@ -58,7 +58,7 @@ namespace MWGui
void clear(); void clear();
void useItem(const MWWorld::Ptr& ptr); void useItem(const MWWorld::Ptr& ptr, bool force=false);
void setGuiMode(GuiMode mode); void setGuiMode(GuiMode mode);

@ -91,7 +91,7 @@ namespace MWGui
for (int i=0; i<ESM::Skill::Length; ++i) for (int i=0; i<ESM::Skill::Length; ++i)
{ {
int value = npcStats.getSkill (i).getBase (); int value = npcStats.getSkill (i).getModified ();
skills.push_back(std::make_pair(i, value)); skills.push_back(std::make_pair(i, value));
} }

@ -1423,10 +1423,10 @@ namespace MWGui
End of tes3mp addition End of tes3mp addition
*/ */
void WindowManager::useItem(const MWWorld::Ptr &item) void WindowManager::useItem(const MWWorld::Ptr &item, bool bypassBeastRestrictions)
{ {
if (mInventoryWindow) if (mInventoryWindow)
mInventoryWindow->useItem(item); mInventoryWindow->useItem(item, bypassBeastRestrictions);
} }
bool WindowManager::isAllowed (GuiWindow wnd) const bool WindowManager::isAllowed (GuiWindow wnd) const

@ -196,7 +196,7 @@ namespace MWGui
*/ */
/// Make the player use an item, while updating GUI state accordingly /// Make the player use an item, while updating GUI state accordingly
virtual void useItem(const MWWorld::Ptr& item); virtual void useItem(const MWWorld::Ptr& item, bool bypassBeastRestrictions=false);
virtual void updateSpellWindow(); virtual void updateSpellWindow();

@ -4,7 +4,6 @@
namespace MWMechanics namespace MWMechanics
{ {
Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation) Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation)
{ {
mCharacterController.reset(new CharacterController(ptr, animation)); mCharacterController.reset(new CharacterController(ptr, animation));
@ -19,10 +18,4 @@ namespace MWMechanics
{ {
return mCharacterController.get(); return mCharacterController.get();
} }
AiState& Actor::getAiState()
{
return mAiState;
}
} }

@ -3,8 +3,6 @@
#include <memory> #include <memory>
#include "aistate.hpp"
namespace MWRender namespace MWRender
{ {
class Animation; class Animation;
@ -29,12 +27,8 @@ namespace MWMechanics
CharacterController* getCharacterController(); CharacterController* getCharacterController();
AiState& getAiState();
private: private:
std::unique_ptr<CharacterController> mCharacterController; std::unique_ptr<CharacterController> mCharacterController;
AiState mAiState;
}; };
} }

@ -1253,6 +1253,13 @@ namespace MWMechanics
} }
} }
void Actors::castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell)
{
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second->getCharacterController()->castSpell(spellId, manualSpell);
}
bool Actors::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) bool Actors::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer)
{ {
if (!actor.getClass().isActor()) if (!actor.getClass().isActor())
@ -1523,7 +1530,7 @@ namespace MWMechanics
{ {
CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
if (isConscious(iter->first)) if (isConscious(iter->first))
stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), iter->second->getAiState(), duration); stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), duration);
} }
} }
/* /*
@ -2195,6 +2202,15 @@ namespace MWMechanics
return it->second->getCharacterController()->isReadyToBlock(); return it->second->getCharacterController()->isReadyToBlock();
} }
bool Actors::isCastingSpell(const MWWorld::Ptr &ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
return false;
return it->second->getCharacterController()->isCastingSpell();
}
bool Actors::isAttackingOrSpell(const MWWorld::Ptr& ptr) const bool Actors::isAttackingOrSpell(const MWWorld::Ptr& ptr) const
{ {
PtrActorMap::const_iterator it = mActors.find(ptr); PtrActorMap::const_iterator it = mActors.find(ptr);
@ -2220,7 +2236,7 @@ namespace MWMechanics
|| ptr.getClass().getCreatureStats(ptr).isParalyzed()) || ptr.getClass().getCreatureStats(ptr).isParalyzed())
continue; continue;
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
seq.fastForward(ptr, it->second->getAiState()); seq.fastForward(ptr);
} }
} }
} }

@ -77,6 +77,8 @@ namespace MWMechanics
/// ///
/// \note Ignored, if \a ptr is not a registered actor. /// \note Ignored, if \a ptr is not a registered actor.
void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell=false);
void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr); void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);
///< Updates an actor with a new Ptr ///< Updates an actor with a new Ptr
@ -171,6 +173,7 @@ namespace MWMechanics
void clear(); // Clear death counter void clear(); // Clear death counter
bool isCastingSpell(const MWWorld::Ptr& ptr) const;
bool isReadyToBlock(const MWWorld::Ptr& ptr) const; bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const; bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;

@ -0,0 +1,84 @@
#include "aicast.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/class.hpp"
#include "aicombataction.hpp"
#include "creaturestats.hpp"
#include "spellcasting.hpp"
#include "steering.hpp"
MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0)
{
ActionSpell action = ActionSpell(spellId);
bool isRanged;
mDistance = action.getCombatRange(isRanged);
}
MWMechanics::AiPackage *MWMechanics::AiCast::clone() const
{
return new AiCast(*this);
}
bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration)
{
MWWorld::Ptr target;
if (actor.getCellRef().getRefId() == mTargetId)
{
// If the target has the same ID as caster, consider that actor casts spell with Self range.
target = actor;
}
else
{
target = getTarget();
if (!target)
return true;
if (!mManual && !pathTo(actor, target.getRefData().getPosition().pos, duration, mDistance))
{
return false;
}
osg::Vec3f dir = target.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3();
bool turned = smoothTurn(actor, getZAngleToDir(dir), 2, osg::DegreesToRadians(3.f));
turned &= smoothTurn(actor, getXAngleToDir(dir), 0, osg::DegreesToRadians(3.f));
if (!turned)
return false;
}
// Check if the actor is already casting another spell
bool isCasting = MWBase::Environment::get().getMechanicsManager()->isCastingSpell(actor);
if (isCasting && !mCasting)
return false;
if (!mCasting)
{
MWBase::Environment::get().getMechanicsManager()->castSpell(actor, mSpellId, mManual);
mCasting = true;
return false;
}
// Finish package, if actor finished spellcasting
return !isCasting;
}
MWWorld::Ptr MWMechanics::AiCast::getTarget() const
{
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mTargetId, false);
return target;
}
int MWMechanics::AiCast::getTypeId() const
{
return AiPackage::TypeIdCast;
}
unsigned int MWMechanics::AiCast::getPriority() const
{
return 3;
}

@ -0,0 +1,37 @@
#ifndef GAME_MWMECHANICS_AICAST_H
#define GAME_MWMECHANICS_AICAST_H
#include "../mwbase/world.hpp"
#include "aipackage.hpp"
namespace MWMechanics
{
/// AiPackage which makes an actor to cast given spell.
class AiCast : public AiPackage {
public:
AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false);
virtual AiPackage *clone() const;
virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration);
virtual int getTypeId() const;
virtual MWWorld::Ptr getTarget() const;
virtual unsigned int getPriority() const;
virtual bool canCancel() const { return false; }
virtual bool shouldCancelPreviousAi() const { return false; }
private:
std::string mTargetId;
std::string mSpellId;
bool mCasting;
bool mManual;
float mDistance;
};
}
#endif

@ -48,74 +48,6 @@ namespace
namespace MWMechanics namespace MWMechanics
{ {
/// \brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive.
struct AiCombatStorage : AiTemporaryBase
{
float mAttackCooldown;
float mTimerReact;
float mTimerCombatMove;
bool mReadyToAttack;
bool mAttack;
float mAttackRange;
bool mCombatMove;
osg::Vec3f mLastTargetPos;
const MWWorld::CellStore* mCell;
std::shared_ptr<Action> mCurrentAction;
float mActionCooldown;
float mStrength;
bool mForceNoShortcut;
ESM::Position mShortcutFailPos;
MWMechanics::Movement mMovement;
enum FleeState
{
FleeState_None,
FleeState_Idle,
FleeState_RunBlindly,
FleeState_RunToDestination
};
FleeState mFleeState;
bool mLOS;
float mUpdateLOSTimer;
float mFleeBlindRunTimer;
ESM::Pathgrid::Point mFleeDest;
AiCombatStorage():
mAttackCooldown(0.0f),
mTimerReact(AI_REACTION_TIME),
mTimerCombatMove(0.0f),
mReadyToAttack(false),
mAttack(false),
mAttackRange(0.0f),
mCombatMove(false),
mLastTargetPos(0,0,0),
mCell(NULL),
mCurrentAction(),
mActionCooldown(0.0f),
mStrength(),
mForceNoShortcut(false),
mShortcutFailPos(),
mMovement(),
mFleeState(FleeState_None),
mLOS(false),
mUpdateLOSTimer(0.0f),
mFleeBlindRunTimer(0.0f)
{}
void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
void updateCombatMove(float duration);
void stopCombatMove();
void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,
const ESM::Weapon* weapon, bool distantCombat);
void updateAttack(CharacterController& characterController);
void stopAttack();
void startFleeing();
void stopFleeing();
bool isFleeing();
};
AiCombat::AiCombat(const MWWorld::Ptr& actor) AiCombat::AiCombat(const MWWorld::Ptr& actor)
{ {
mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId(); mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();

@ -23,7 +23,72 @@ namespace MWMechanics
{ {
class Action; class Action;
struct AiCombatStorage; /// \brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive.
struct AiCombatStorage : AiTemporaryBase
{
float mAttackCooldown;
float mTimerReact;
float mTimerCombatMove;
bool mReadyToAttack;
bool mAttack;
float mAttackRange;
bool mCombatMove;
osg::Vec3f mLastTargetPos;
const MWWorld::CellStore* mCell;
std::shared_ptr<Action> mCurrentAction;
float mActionCooldown;
float mStrength;
bool mForceNoShortcut;
ESM::Position mShortcutFailPos;
MWMechanics::Movement mMovement;
enum FleeState
{
FleeState_None,
FleeState_Idle,
FleeState_RunBlindly,
FleeState_RunToDestination
};
FleeState mFleeState;
bool mLOS;
float mUpdateLOSTimer;
float mFleeBlindRunTimer;
ESM::Pathgrid::Point mFleeDest;
AiCombatStorage():
mAttackCooldown(0.0f),
mTimerReact(AI_REACTION_TIME),
mTimerCombatMove(0.0f),
mReadyToAttack(false),
mAttack(false),
mAttackRange(0.0f),
mCombatMove(false),
mLastTargetPos(0,0,0),
mCell(NULL),
mCurrentAction(),
mActionCooldown(0.0f),
mStrength(),
mForceNoShortcut(false),
mShortcutFailPos(),
mMovement(),
mFleeState(FleeState_None),
mLOS(false),
mUpdateLOSTimer(0.0f),
mFleeBlindRunTimer(0.0f)
{}
void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
void updateCombatMove(float duration);
void stopCombatMove();
void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,
const ESM::Weapon* weapon, bool distantCombat);
void updateAttack(CharacterController& characterController);
void stopAttack();
void startFleeing();
void stopFleeing();
bool isFleeing();
};
/// \brief Causes the actor to fight another actor /// \brief Causes the actor to fight another actor
class AiCombat : public AiPackage class AiCombat : public AiPackage

@ -17,22 +17,6 @@
namespace MWMechanics namespace MWMechanics
{ {
struct AiFollowStorage : AiTemporaryBase
{
float mTimer;
bool mMoving;
float mTargetAngleRadians;
bool mTurnActorToTarget;
AiFollowStorage() :
mTimer(0.f),
mMoving(false),
mTargetAngleRadians(0.f),
mTurnActorToTarget(false)
{}
};
int AiFollow::mFollowIndexCounter = 0; int AiFollow::mFollowIndexCounter = 0;
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z) AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z)

@ -19,6 +19,21 @@ namespace AiSequence
namespace MWMechanics namespace MWMechanics
{ {
struct AiFollowStorage : AiTemporaryBase
{
float mTimer;
bool mMoving;
float mTargetAngleRadians;
bool mTurnActorToTarget;
AiFollowStorage() :
mTimer(0.f),
mMoving(false),
mTargetAngleRadians(0.f),
mTurnActorToTarget(false)
{}
};
/// \brief AiPackage for an actor to follow another actor/the PC /// \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 /** 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
**/ **/

@ -49,7 +49,8 @@ namespace MWMechanics
TypeIdAvoidDoor = 7, TypeIdAvoidDoor = 7,
TypeIdFace = 8, TypeIdFace = 8,
TypeIdBreathe = 9, TypeIdBreathe = 9,
TypeIdInternalTravel = 10 TypeIdInternalTravel = 10,
TypeIdCast = 11
}; };
///Default constructor ///Default constructor

@ -180,15 +180,11 @@ bool AiSequence::isPackageDone() const
bool isActualAiPackage(int packageTypeId) bool isActualAiPackage(int packageTypeId)
{ {
return (packageTypeId != AiPackage::TypeIdCombat return (packageTypeId >= AiPackage::TypeIdWander &&
&& packageTypeId != AiPackage::TypeIdPursue packageTypeId <= AiPackage::TypeIdActivate);
&& packageTypeId != AiPackage::TypeIdAvoidDoor
&& packageTypeId != AiPackage::TypeIdFace
&& packageTypeId != AiPackage::TypeIdBreathe
&& packageTypeId != AiPackage::TypeIdInternalTravel);
} }
void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration)
{ {
if(actor != getPlayer()) if(actor != getPlayer())
{ {
@ -262,7 +258,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
try try
{ {
if (package->execute (actor,characterController,state,duration)) if (package->execute (actor, characterController, mAiState, duration))
{ {
// Put repeating noncombat AI packages on the end of the stack so they can be used again // Put repeating noncombat AI packages on the end of the stack so they can be used again
if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat())) if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat()))
@ -308,7 +304,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
if (isActualAiPackage(package.getTypeId())) if (isActualAiPackage(package.getTypeId()))
stopCombat(); stopCombat();
// We should return a wandering actor back after combat or pursuit. // We should return a wandering actor back after combat, casting or pursuit.
// The same thing for actors without AI packages. // The same thing for actors without AI packages.
// Also there is no point to stack return packages. // Also there is no point to stack return packages.
int currentTypeId = getTypeId(); int currentTypeId = getTypeId();
@ -316,7 +312,8 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander
&& !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel) && !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel)
&& (newTypeId <= MWMechanics::AiPackage::TypeIdCombat && (newTypeId <= MWMechanics::AiPackage::TypeIdCombat
|| newTypeId == MWMechanics::AiPackage::TypeIdPursue)) || newTypeId == MWMechanics::AiPackage::TypeIdPursue
|| newTypeId == MWMechanics::AiPackage::TypeIdCast))
{ {
osg::Vec3f dest; osg::Vec3f dest;
if (currentTypeId == MWMechanics::AiPackage::TypeIdWander) if (currentTypeId == MWMechanics::AiPackage::TypeIdWander)
@ -352,6 +349,13 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
// insert new package in correct place depending on priority // insert new package in correct place depending on priority
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); ++it) for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
// We should keep current AiCast package, if we try to add a new one.
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast &&
package.getTypeId() == MWMechanics::AiPackage::TypeIdCast)
{
continue;
}
if((*it)->getPriority() <= package.getPriority()) if((*it)->getPriority() <= package.getPriority())
{ {
mPackages.insert(it,package.clone()); mPackages.insert(it,package.clone());
@ -360,6 +364,14 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
} }
mPackages.push_back (package.clone()); mPackages.push_back (package.clone());
// Make sure that temporary storage is empty
if (cancelOther)
{
mAiState.moveIn(new AiCombatStorage());
mAiState.moveIn(new AiFollowStorage());
mAiState.moveIn(new AiWanderStorage());
}
} }
AiPackage* MWMechanics::AiSequence::getActivePackage() AiPackage* MWMechanics::AiSequence::getActivePackage()
@ -494,12 +506,12 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
mLastAiPackage = sequence.mLastAiPackage; mLastAiPackage = sequence.mLastAiPackage;
} }
void AiSequence::fastForward(const MWWorld::Ptr& actor, AiState& state) void AiSequence::fastForward(const MWWorld::Ptr& actor)
{ {
if (!mPackages.empty()) if (!mPackages.empty())
{ {
MWMechanics::AiPackage* package = mPackages.front(); MWMechanics::AiPackage* package = mPackages.front();
package->fastForward(actor, state); package->fastForward(actor, mAiState);
} }
} }

@ -3,6 +3,8 @@
#include <list> #include <list>
#include "aistate.hpp"
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
namespace MWWorld namespace MWWorld
@ -47,6 +49,7 @@ namespace MWMechanics
/// The type of AI package that ran last /// The type of AI package that ran last
int mLastAiPackage; int mLastAiPackage;
AiState mAiState;
public: public:
///Default constructor ///Default constructor
@ -104,10 +107,10 @@ namespace MWMechanics
void stopPursuit(); void stopPursuit();
/// Execute current package, switching if needed. /// Execute current package, switching if needed.
void execute (const MWWorld::Ptr& actor, CharacterController& characterController, MWMechanics::AiState& state, float duration); void execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration);
/// Simulate the passing of time using the currently active AI package /// Simulate the passing of time using the currently active AI package
void fastForward(const MWWorld::Ptr &actor, AiState &state); void fastForward(const MWWorld::Ptr &actor);
/// Remove all packages. /// Remove all packages.
void clear(); void clear();

@ -51,67 +51,6 @@ namespace MWMechanics
std::string("idle9"), std::string("idle9"),
}; };
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
struct AiWanderStorage : AiTemporaryBase
{
// the z rotation angle to reach
// when mTurnActorGivingGreetingToFacePlayer is true
float mTargetAngleRadians;
bool mTurnActorGivingGreetingToFacePlayer;
float mReaction; // update some actions infrequently
AiWander::GreetingState mSaidGreeting;
int mGreetingTimer;
const MWWorld::CellStore* mCell; // for detecting cell change
// AiWander states
AiWander::WanderState mState;
bool mIsWanderingManually;
bool mCanWanderAlongPathGrid;
unsigned short mIdleAnimation;
std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors
// do we need to calculate allowed nodes based on mDistance
bool mPopulateAvailableNodes;
// allowed pathgrid nodes based on mDistance from the spawn point
// in local coordinates of mCell
std::vector<ESM::Pathgrid::Point> mAllowedNodes;
ESM::Pathgrid::Point mCurrentNode;
bool mTrimCurrentNode;
float mDoorCheckDuration;
int mStuckCount;
AiWanderStorage():
mTargetAngleRadians(0),
mTurnActorGivingGreetingToFacePlayer(false),
mReaction(0),
mSaidGreeting(AiWander::Greet_None),
mGreetingTimer(0),
mCell(NULL),
mState(AiWander::Wander_ChooseAction),
mIsWanderingManually(false),
mCanWanderAlongPathGrid(true),
mIdleAnimation(0),
mBadIdles(),
mPopulateAvailableNodes(true),
mAllowedNodes(),
mTrimCurrentNode(false),
mDoorCheckDuration(0), // TODO: maybe no longer needed
mStuckCount(0)
{};
void setState(const AiWander::WanderState wanderState, const bool isManualWander = false) {
mState = wanderState;
mIsWanderingManually = isManualWander;
}
};
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat): AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle),
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)) mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0))
@ -221,7 +160,7 @@ namespace MWMechanics
mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell())); mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell()));
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
storage.setState(Wander_Walking); storage.setState(AiWanderStorage::Wander_Walking);
} }
doPerFrameActionsForState(actor, duration, storage, pos); doPerFrameActionsForState(actor, duration, storage, pos);
@ -270,7 +209,7 @@ namespace MWMechanics
if(actorCanMoveByZ && mDistance > 0) { if(actorCanMoveByZ && mDistance > 0) {
// Typically want to idle for a short time before the next wander // Typically want to idle for a short time before the next wander
if (Misc::Rng::rollDice(100) >= 92 && storage.mState != Wander_Walking) { if (Misc::Rng::rollDice(100) >= 92 && storage.mState != AiWanderStorage::Wander_Walking) {
wanderNearStart(actor, storage, mDistance); wanderNearStart(actor, storage, mDistance);
} }
@ -283,7 +222,7 @@ namespace MWMechanics
if (Misc::Rng::rollDice(100) >= 96) { if (Misc::Rng::rollDice(100) >= 96) {
wanderNearStart(actor, storage, mDistance); wanderNearStart(actor, storage, mDistance);
} else { } else {
storage.setState(Wander_IdleNow); storage.setState(AiWanderStorage::Wander_IdleNow);
} }
} else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually) { } else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually) {
storage.mCanWanderAlongPathGrid = false; storage.mCanWanderAlongPathGrid = false;
@ -299,13 +238,13 @@ namespace MWMechanics
mDistance = 0; mDistance = 0;
// Allow interrupting a walking actor to trigger a greeting // Allow interrupting a walking actor to trigger a greeting
WanderState& wanderState = storage.mState; AiWanderStorage::WanderState& wanderState = storage.mState;
if ((wanderState == Wander_IdleNow) || (wanderState == Wander_Walking)) if ((wanderState == AiWanderStorage::Wander_IdleNow) || (wanderState == AiWanderStorage::Wander_Walking))
{ {
playGreetingIfPlayerGetsTooClose(actor, storage); playGreetingIfPlayerGetsTooClose(actor, storage);
} }
if ((wanderState == Wander_MoveNow) && storage.mCanWanderAlongPathGrid) if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
{ {
// Construct a new path if there isn't one // Construct a new path if there isn't one
if(!mPathFinder.isPathConstructed()) if(!mPathFinder.isPathConstructed())
@ -381,7 +320,7 @@ namespace MWMechanics
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
{ {
storage.setState(Wander_Walking, true); storage.setState(AiWanderStorage::Wander_Walking, true);
mHasDestination = true; mHasDestination = true;
} }
return; return;
@ -410,26 +349,26 @@ namespace MWMechanics
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) { void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
stopWalking(actor, storage); stopWalking(actor, storage);
mObstacleCheck.clear(); mObstacleCheck.clear();
storage.setState(Wander_IdleNow); storage.setState(AiWanderStorage::Wander_IdleNow);
} }
void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos) void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos)
{ {
switch (storage.mState) switch (storage.mState)
{ {
case Wander_IdleNow: case AiWanderStorage::Wander_IdleNow:
onIdleStatePerFrameActions(actor, duration, storage); onIdleStatePerFrameActions(actor, duration, storage);
break; break;
case Wander_Walking: case AiWanderStorage::Wander_Walking:
onWalkingStatePerFrameActions(actor, duration, storage, pos); onWalkingStatePerFrameActions(actor, duration, storage, pos);
break; break;
case Wander_ChooseAction: case AiWanderStorage::Wander_ChooseAction:
onChooseActionStatePerFrameActions(actor, storage); onChooseActionStatePerFrameActions(actor, storage);
break; break;
case Wander_MoveNow: case AiWanderStorage::Wander_MoveNow:
break; // nothing to do break; // nothing to do
default: default:
@ -451,7 +390,7 @@ namespace MWMechanics
if (mDistance && // actor is not intended to be stationary if (mDistance && // actor is not intended to be stationary
proximityToDoor(actor, distance*1.6f)) proximityToDoor(actor, distance*1.6f))
{ {
storage.setState(Wander_MoveNow); storage.setState(AiWanderStorage::Wander_MoveNow);
storage.mTrimCurrentNode = false; // just in case storage.mTrimCurrentNode = false; // just in case
return; return;
} }
@ -468,13 +407,13 @@ namespace MWMechanics
} }
// Check if idle animation finished // Check if idle animation finished
GreetingState& greetingState = storage.mSaidGreeting; AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None)) if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == AiWanderStorage::Greet_Done || greetingState == AiWanderStorage::Greet_None))
{ {
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
storage.setState(Wander_Walking); storage.setState(AiWanderStorage::Wander_Walking);
else else
storage.setState(Wander_ChooseAction); storage.setState(AiWanderStorage::Wander_ChooseAction);
} }
} }
@ -485,7 +424,7 @@ namespace MWMechanics
if ((!mPathFinder.isPathConstructed()) || pathTo(actor, ESM::Pathgrid::Point(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE)) if ((!mPathFinder.isPathConstructed()) || pathTo(actor, ESM::Pathgrid::Point(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE))
{ {
stopWalking(actor, storage); stopWalking(actor, storage);
storage.setState(Wander_ChooseAction); storage.setState(AiWanderStorage::Wander_ChooseAction);
} }
else else
{ {
@ -502,7 +441,7 @@ namespace MWMechanics
if (!idleAnimation && mDistance) if (!idleAnimation && mDistance)
{ {
storage.setState(Wander_MoveNow); storage.setState(AiWanderStorage::Wander_MoveNow);
return; return;
} }
if(idleAnimation) if(idleAnimation)
@ -512,13 +451,13 @@ namespace MWMechanics
if(!playIdle(actor, idleAnimation)) if(!playIdle(actor, idleAnimation))
{ {
storage.mBadIdles.push_back(idleAnimation); storage.mBadIdles.push_back(idleAnimation);
storage.setState(Wander_ChooseAction); storage.setState(AiWanderStorage::Wander_ChooseAction);
return; return;
} }
} }
} }
storage.setState(Wander_IdleNow); storage.setState(AiWanderStorage::Wander_IdleNow);
} }
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos) void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos)
@ -534,7 +473,7 @@ namespace MWMechanics
trimAllowedNodes(storage.mAllowedNodes, mPathFinder); trimAllowedNodes(storage.mAllowedNodes, mPathFinder);
mObstacleCheck.clear(); mObstacleCheck.clear();
stopWalking(actor, storage); stopWalking(actor, storage);
storage.setState(Wander_MoveNow); storage.setState(AiWanderStorage::Wander_MoveNow);
} }
storage.mStuckCount++; // TODO: maybe no longer needed storage.mStuckCount++; // TODO: maybe no longer needed
@ -545,7 +484,7 @@ namespace MWMechanics
{ {
mObstacleCheck.clear(); mObstacleCheck.clear();
stopWalking(actor, storage); stopWalking(actor, storage);
storage.setState(Wander_ChooseAction); storage.setState(AiWanderStorage::Wander_ChooseAction);
storage.mStuckCount = 0; storage.mStuckCount = 0;
} }
} }
@ -596,8 +535,8 @@ namespace MWMechanics
float playerDistSqr = (playerPos - actorPos).length2(); float playerDistSqr = (playerPos - actorPos).length2();
int& greetingTimer = storage.mGreetingTimer; int& greetingTimer = storage.mGreetingTimer;
GreetingState& greetingState = storage.mSaidGreeting; AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
if (greetingState == Greet_None) if (greetingState == AiWanderStorage::Greet_None)
{ {
if ((playerDistSqr <= helloDistance*helloDistance) && if ((playerDistSqr <= helloDistance*helloDistance) &&
!player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor) !player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor)
@ -606,37 +545,37 @@ namespace MWMechanics
if (greetingTimer >= GREETING_SHOULD_START) if (greetingTimer >= GREETING_SHOULD_START)
{ {
greetingState = Greet_InProgress; greetingState = AiWanderStorage::Greet_InProgress;
MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
greetingTimer = 0; greetingTimer = 0;
} }
} }
if (greetingState == Greet_InProgress) if (greetingState == AiWanderStorage::Greet_InProgress)
{ {
greetingTimer++; greetingTimer++;
if (storage.mState == Wander_Walking) if (storage.mState == AiWanderStorage::Wander_Walking)
{ {
stopWalking(actor, storage, false); stopWalking(actor, storage, false);
mObstacleCheck.clear(); mObstacleCheck.clear();
storage.setState(Wander_IdleNow); storage.setState(AiWanderStorage::Wander_IdleNow);
} }
turnActorToFacePlayer(actorPos, playerPos, storage); turnActorToFacePlayer(actorPos, playerPos, storage);
if (greetingTimer >= GREETING_SHOULD_END) if (greetingTimer >= GREETING_SHOULD_END)
{ {
greetingState = Greet_Done; greetingState = AiWanderStorage::Greet_Done;
greetingTimer = 0; greetingTimer = 0;
} }
} }
if (greetingState == MWMechanics::AiWander::Greet_Done) if (greetingState == AiWanderStorage::Greet_Done)
{ {
float resetDist = 2 * helloDistance; float resetDist = 2 * helloDistance;
if (playerDistSqr >= resetDist*resetDist) if (playerDistSqr >= resetDist*resetDist)
greetingState = Greet_None; greetingState = AiWanderStorage::Greet_None;
} }
} }
@ -676,7 +615,7 @@ namespace MWMechanics
storage.mAllowedNodes.push_back(storage.mCurrentNode); storage.mAllowedNodes.push_back(storage.mCurrentNode);
storage.mCurrentNode = temp; storage.mCurrentNode = temp;
storage.setState(Wander_Walking); storage.setState(AiWanderStorage::Wander_Walking);
} }
// Choose a different node and delete this one from possible nodes because it is uncreachable: // Choose a different node and delete this one from possible nodes because it is uncreachable:
else else

@ -22,7 +22,80 @@ namespace ESM
namespace MWMechanics namespace MWMechanics
{ {
struct AiWanderStorage; /// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
struct AiWanderStorage : AiTemporaryBase
{
// the z rotation angle to reach
// when mTurnActorGivingGreetingToFacePlayer is true
float mTargetAngleRadians;
bool mTurnActorGivingGreetingToFacePlayer;
float mReaction; // update some actions infrequently
enum GreetingState
{
Greet_None,
Greet_InProgress,
Greet_Done
};
GreetingState mSaidGreeting;
int mGreetingTimer;
const MWWorld::CellStore* mCell; // for detecting cell change
// AiWander states
enum WanderState
{
Wander_ChooseAction,
Wander_IdleNow,
Wander_MoveNow,
Wander_Walking
};
WanderState mState;
bool mIsWanderingManually;
bool mCanWanderAlongPathGrid;
unsigned short mIdleAnimation;
std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors
// do we need to calculate allowed nodes based on mDistance
bool mPopulateAvailableNodes;
// allowed pathgrid nodes based on mDistance from the spawn point
// in local coordinates of mCell
std::vector<ESM::Pathgrid::Point> mAllowedNodes;
ESM::Pathgrid::Point mCurrentNode;
bool mTrimCurrentNode;
float mDoorCheckDuration;
int mStuckCount;
AiWanderStorage():
mTargetAngleRadians(0),
mTurnActorGivingGreetingToFacePlayer(false),
mReaction(0),
mSaidGreeting(Greet_None),
mGreetingTimer(0),
mCell(NULL),
mState(Wander_ChooseAction),
mIsWanderingManually(false),
mCanWanderAlongPathGrid(true),
mIdleAnimation(0),
mBadIdles(),
mPopulateAvailableNodes(true),
mAllowedNodes(),
mTrimCurrentNode(false),
mDoorCheckDuration(0), // TODO: maybe no longer needed
mStuckCount(0)
{};
void setState(const WanderState wanderState, const bool isManualWander = false)
{
mState = wanderState;
mIsWanderingManually = isManualWander;
}
};
/// \brief Causes the Actor to wander within a specified range /// \brief Causes the Actor to wander within a specified range
class AiWander : public AiPackage class AiWander : public AiPackage
@ -52,19 +125,6 @@ namespace MWMechanics
osg::Vec3f getDestination(const MWWorld::Ptr& actor) const; osg::Vec3f getDestination(const MWWorld::Ptr& actor) const;
enum GreetingState {
Greet_None,
Greet_InProgress,
Greet_Done
};
enum WanderState {
Wander_ChooseAction,
Wander_IdleNow,
Wander_MoveNow,
Wander_Walking
};
private: private:
// NOTE: mDistance and mDuration must be set already // NOTE: mDistance and mDuration must be set already
void init(); void init();

@ -55,6 +55,7 @@
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "aicombataction.hpp"
#include "movement.hpp" #include "movement.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
@ -386,6 +387,10 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState
{ {
jumpmask = MWRender::Animation::BlendMask_LowerBody; jumpmask = MWRender::Animation::BlendMask_LowerBody;
jumpAnimName = "jump"; jumpAnimName = "jump";
// For crossbow animations use 1h ones as fallback
if (mWeaponType == WeapType_Crossbow)
jumpAnimName += "1h";
} }
} }
} }
@ -434,11 +439,18 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
movementAnimName = movestate->groupname; movementAnimName = movestate->groupname;
if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos)
{ {
if (mWeaponType == WeapType_Spell && (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)) // Spellcasting stance turning is a special case
movementAnimName = weap->shortgroup + movementAnimName;
else
movementAnimName += weap->shortgroup; movementAnimName += weap->shortgroup;
if(!mAnimation->hasAnimation(movementAnimName)) if(!mAnimation->hasAnimation(movementAnimName))
{ {
movemask = MWRender::Animation::BlendMask_LowerBody; movemask = MWRender::Animation::BlendMask_LowerBody;
movementAnimName = movestate->groupname; movementAnimName = movestate->groupname;
// For crossbow animations use 1h ones as fallback
if (mWeaponType == WeapType_Crossbow)
movementAnimName += "1h";
} }
} }
@ -475,9 +487,11 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
} }
} }
/* If we're playing the same animation, restart from the loop start instead of the // If we're playing the same animation, start it from the point it ended
* beginning. */ bool sameAnim = (movementAnimName == mCurrentMovement);
int mode = ((movementAnimName == mCurrentMovement) ? 2 : 1); float startPoint = 0.f;
if (sameAnim)
mAnimation->getInfo(mCurrentMovement, &startPoint);
mMovementAnimationControlled = true; mMovementAnimationControlled = true;
@ -526,7 +540,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
} }
mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false, mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false,
1.f, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul, true); 1.f, (!sameAnim ? "start" : "loop start"), "stop", startPoint, ~0ul, true);
} }
} }
} }
@ -676,16 +690,19 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I
void CharacterController::playDeath(float startpoint, CharacterState death) void CharacterController::playDeath(float startpoint, CharacterState death)
{ {
// Make sure the character was swimming upon death for forward-compatibility
const bool wasSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);
switch (death) switch (death)
{ {
case CharState_SwimDeath: case CharState_SwimDeath:
mCurrentDeath = "swimdeath"; mCurrentDeath = "swimdeath";
break; break;
case CharState_SwimDeathKnockDown: case CharState_SwimDeathKnockDown:
mCurrentDeath = "swimdeathknockdown"; mCurrentDeath = (wasSwimming ? "swimdeathknockdown" : "deathknockdown");
break; break;
case CharState_SwimDeathKnockOut: case CharState_SwimDeathKnockOut:
mCurrentDeath = "swimdeathknockout"; mCurrentDeath = (wasSwimming ? "swimdeathknockout" : "deathknockout");
break; break;
case CharState_DeathKnockDown: case CharState_DeathKnockDown:
mCurrentDeath = "deathknockdown"; mCurrentDeath = "deathknockdown";
@ -808,6 +825,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mSecondsOfRunning(0) , mSecondsOfRunning(0)
, mTurnAnimationThreshold(0) , mTurnAnimationThreshold(0)
, mAttackingOrSpell(false) , mAttackingOrSpell(false)
, mCastingManualSpell(false)
, mTimeUntilWake(0.f) , mTimeUntilWake(0.f)
{ {
if(!mAnimation) if(!mAnimation)
@ -1021,7 +1039,8 @@ void CharacterController::handleTextKey(const std::string &groupname, const std:
// the same animation for all range types, so there are 3 "release" keys on the same time, one for each range type. // the same animation for all range types, so there are 3 "release" keys on the same time, one for each range type.
&& evt.compare(off, len, mAttackType + " release") == 0) && evt.compare(off, len, mAttackType + " release") == 0)
{ {
MWBase::Environment::get().getWorld()->castSpell(mPtr); MWBase::Environment::get().getWorld()->castSpell(mPtr, mCastingManualSpell);
mCastingManualSpell = false;
} }
else if (groupname == "shield" && evt.compare(off, len, "block hit") == 0) else if (groupname == "shield" && evt.compare(off, len, "block hit") == 0)
@ -1102,7 +1121,9 @@ bool CharacterController::updateCreatureState()
if (weapType == WeapType_Spell) if (weapType == WeapType_Spell)
{ {
const std::string spellid = stats.getSpells().getSelectedSpell(); const std::string spellid = stats.getSpells().getSelectedSpell();
if (!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr);
if (!spellid.empty() && canCast)
{ {
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -1123,7 +1144,7 @@ bool CharacterController::updateCreatureState()
End of tes3mp addition End of tes3mp addition
*/ */
MWMechanics::CastSpell cast(mPtr, NULL); MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell);
cast.playSpellCastingEffects(spellid); cast.playSpellCastingEffects(spellid);
/* /*
@ -1138,7 +1159,10 @@ bool CharacterController::updateCreatureState()
*/ */
if (!mAnimation->hasAnimation("spellcast")) if (!mAnimation->hasAnimation("spellcast"))
MWBase::Environment::get().getWorld()->castSpell(mPtr); // No "release" text key to use, so cast immediately {
MWBase::Environment::get().getWorld()->castSpell(mPtr, mCastingManualSpell); // No "release" text key to use, so cast immediately
mCastingManualSpell = false;
}
else else
{ {
const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellid); const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellid);
@ -1411,8 +1435,9 @@ bool CharacterController::updateWeaponState()
stats.getSpells().setSelectedSpell(selectedSpell); stats.getSpells().setSelectedSpell(selectedSpell);
} }
std::string spellid = stats.getSpells().getSelectedSpell(); std::string spellid = stats.getSpells().getSelectedSpell();
bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr);
if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) if(!spellid.empty() && canCast)
{ {
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -1433,7 +1458,7 @@ bool CharacterController::updateWeaponState()
End of tes3mp addition End of tes3mp addition
*/ */
MWMechanics::CastSpell cast(mPtr, NULL); MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell);
cast.playSpellCastingEffects(spellid); cast.playSpellCastingEffects(spellid);
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid); const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
@ -1711,16 +1736,18 @@ bool CharacterController::updateWeaponState()
break; break;
} }
// Note: apply reload animations only for upper body since blending with movement animations can give weird result.
// Especially noticable with crossbow reload animation.
if(!start.empty()) if(!start.empty())
{ {
mAnimation->disable(mCurrentWeapon); mAnimation->disable(mCurrentWeapon);
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop) if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
mAnimation->play(mCurrentWeapon, priorityWeapon, mAnimation->play(mCurrentWeapon, priorityWeapon,
MWRender::Animation::BlendMask_All, true, MWRender::Animation::BlendMask_UpperBody, true,
weapSpeed, start, stop, 0.0f, 0); weapSpeed, start, stop, 0.0f, 0);
else else
mAnimation->play(mCurrentWeapon, priorityWeapon, mAnimation->play(mCurrentWeapon, priorityWeapon,
MWRender::Animation::BlendMask_All, false, MWRender::Animation::BlendMask_UpperBody, false,
weapSpeed, start, stop, 0.0f, 0); weapSpeed, start, stop, 0.0f, 0);
} }
} }
@ -2498,6 +2525,11 @@ bool CharacterController::isAttackPrepairing() const
mUpperBodyState == UpperCharState_MinAttackToMaxAttack; mUpperBodyState == UpperCharState_MinAttackToMaxAttack;
} }
bool CharacterController::isCastingSpell() const
{
return mCastingManualSpell || mUpperBodyState == UpperCharState_CastingSpell;
}
bool CharacterController::isReadyToBlock() const bool CharacterController::isReadyToBlock() const
{ {
return updateCarriedLeftVisible(mWeaponType); return updateCarriedLeftVisible(mWeaponType);
@ -2561,6 +2593,14 @@ void CharacterController::setAttackingOrSpell(bool attackingOrSpell)
mAttackingOrSpell = attackingOrSpell; mAttackingOrSpell = attackingOrSpell;
} }
void CharacterController::castSpell(const std::string spellId, bool manualSpell)
{
mAttackingOrSpell = true;
mCastingManualSpell = manualSpell;
ActionSpell action = ActionSpell(spellId);
action.prepare(mPtr);
}
void CharacterController::setAIAttackType(const std::string& attackType) void CharacterController::setAIAttackType(const std::string& attackType)
{ {
mAttackType = attackType; mAttackType = attackType;

@ -204,6 +204,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
std::string mAttackType; // slash, chop or thrust std::string mAttackType; // slash, chop or thrust
bool mAttackingOrSpell; bool mAttackingOrSpell;
bool mCastingManualSpell;
float mTimeUntilWake; float mTimeUntilWake;
@ -276,6 +277,7 @@ public:
void forceStateUpdate(); void forceStateUpdate();
bool isAttackPrepairing() const; bool isAttackPrepairing() const;
bool isCastingSpell() const;
bool isReadyToBlock() const; bool isReadyToBlock() const;
bool isKnockedDown() const; bool isKnockedDown() const;
bool isKnockedOut() const; bool isKnockedOut() const;
@ -286,6 +288,7 @@ public:
bool isAttackingOrSpell() const; bool isAttackingOrSpell() const;
void setAttackingOrSpell(bool attackingOrSpell); void setAttackingOrSpell(bool attackingOrSpell);
void castSpell(const std::string spellId, bool manualSpell=false);
void setAIAttackType(const std::string& attackType); void setAIAttackType(const std::string& attackType);
static void setAttackTypeRandomly(std::string& attackType); static void setAttackTypeRandomly(std::string& attackType);

@ -266,6 +266,12 @@ namespace MWMechanics
mObjects.addObject(ptr); mObjects.addObject(ptr);
} }
void MechanicsManager::castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell)
{
if(ptr.getClass().isActor())
mActors.castSpell(ptr, spellId, manualSpell);
}
void MechanicsManager::remove(const MWWorld::Ptr& ptr) void MechanicsManager::remove(const MWWorld::Ptr& ptr)
{ {
if(ptr == mWatched) if(ptr == mWatched)
@ -1824,6 +1830,11 @@ namespace MWMechanics
stats.resurrect(); stats.resurrect();
} }
bool MechanicsManager::isCastingSpell(const MWWorld::Ptr &ptr) const
{
return mActors.isCastingSpell(ptr);
}
bool MechanicsManager::isReadyToBlock(const MWWorld::Ptr &ptr) const bool MechanicsManager::isReadyToBlock(const MWWorld::Ptr &ptr) const
{ {
return mActors.isReadyToBlock(ptr); return mActors.isReadyToBlock(ptr);

@ -200,10 +200,14 @@ namespace MWMechanics
virtual void keepPlayerAlive(); virtual void keepPlayerAlive();
virtual bool isCastingSpell (const MWWorld::Ptr& ptr) const;
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const; virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const;
/// Is \a ptr casting spell or using weapon now? /// Is \a ptr casting spell or using weapon now?
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const; virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const;
virtual void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell=false);
/// Check if the target actor was detected by an observer /// Check if the target actor was detected by an observer
/// If the observer is a non-NPC, check all actors in AI processing distance as observers /// If the observer is a non-NPC, check all actors in AI processing distance as observers
virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer); virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer);

@ -332,13 +332,14 @@ namespace MWMechanics
return true; return true;
} }
CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile) CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool isScripted)
: mCaster(caster) : mCaster(caster)
, mTarget(target) , mTarget(target)
, mStack(false) , mStack(false)
, mHitPosition(0,0,0) , mHitPosition(0,0,0)
, mAlwaysSucceed(false) , mAlwaysSucceed(false)
, mFromProjectile(fromProjectile) , mFromProjectile(fromProjectile)
, mIsScripted(isScripted)
{ {
} }
@ -961,7 +962,7 @@ namespace MWMechanics
bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
if (mCaster.getClass().isActor() && !mAlwaysSucceed) if (mCaster.getClass().isActor() && !mAlwaysSucceed && !mIsScripted)
{ {
school = getSpellSchool(spell, mCaster); school = getSpellSchool(spell, mCaster);
@ -1025,7 +1026,7 @@ namespace MWMechanics
stats.getSpells().usePower(spell); stats.getSpells().usePower(spell);
} }
if (mCaster == getPlayer() && spellIncreasesSkill(spell)) if (mCaster == getPlayer() && spellIncreasesSkill())
mCaster.getClass().skillUsageSucceeded(mCaster, mCaster.getClass().skillUsageSucceeded(mCaster,
spellSchoolToSkill(school), 0); spellSchoolToSkill(school), 0);
@ -1149,6 +1150,14 @@ namespace MWMechanics
} }
} }
bool CastSpell::spellIncreasesSkill()
{
if (mIsScripted)
return false;
return MWMechanics::spellIncreasesSkill(mId);
}
int getEffectiveEnchantmentCastCost(float castCost, const MWWorld::Ptr &actor) int getEffectiveEnchantmentCastCost(float castCost, const MWWorld::Ptr &actor)
{ {
/* /*

@ -88,9 +88,10 @@ namespace MWMechanics
osg::Vec3f mHitPosition; // Used for spawning area orb osg::Vec3f mHitPosition; // Used for spawning area orb
bool mAlwaysSucceed; // Always succeed spells casted by NPCs/creatures regardless of their chance (default: false) bool mAlwaysSucceed; // Always succeed spells casted by NPCs/creatures regardless of their chance (default: false)
bool mFromProjectile; // True if spell is cast by enchantment of some projectile (arrow, bolt or thrown weapon) bool mFromProjectile; // True if spell is cast by enchantment of some projectile (arrow, bolt or thrown weapon)
bool mIsScripted; // True if spell is casted from script and ignores some checks (mana level, success chance, etc.)
public: public:
CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target, const bool fromProjectile=false); CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target, const bool fromProjectile=false, const bool isScripted=false);
bool cast (const ESM::Spell* spell); bool cast (const ESM::Spell* spell);
@ -108,6 +109,8 @@ namespace MWMechanics
void playSpellCastingEffects(const std::string &spellid); void playSpellCastingEffects(const std::string &spellid);
bool spellIncreasesSkill();
/// Launch a bolt with the given effects. /// Launch a bolt with the given effects.
void launchMagicBolt (); void launchMagicBolt ();

@ -31,6 +31,7 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/actionequip.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
@ -215,12 +216,11 @@ namespace MWScript
"to fulfil requirements of Equip instruction" << std::endl; "to fulfil requirements of Equip instruction" << std::endl;
} }
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) if (ptr == MWMechanics::getPlayer())
MWBase::Environment::get().getWindowManager()->useItem(*it); MWBase::Environment::get().getWindowManager()->useItem(*it, true);
else else
{ {
std::shared_ptr<MWWorld::Action> action = it->getClass().use(*it); std::shared_ptr<MWWorld::Action> action = it->getClass().use(*it, true);
// No equip sound for actors other than the player
action->execute(ptr, true); action->execute(ptr, true);
} }
} }

@ -37,6 +37,7 @@
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwmechanics/aicast.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
@ -1153,15 +1154,31 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
std::string spell = runtime.getStringLiteral (runtime[0].mInteger); std::string spellId = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
std::string targetId = ::Misc::StringUtils::lowerCase(runtime.getStringLiteral (runtime[0].mInteger)); std::string targetId = ::Misc::StringUtils::lowerCase(runtime.getStringLiteral (runtime[0].mInteger));
runtime.pop(); runtime.pop();
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (spellId);
if (spell && spell->mData.mType != ESM::Spell::ST_Spell && spell->mData.mType != ESM::Spell::ST_Power)
{
runtime.getContext().report("spellcasting failed: you can cast only spells and powers.");
return;
}
// Obviously we can not use casting animation for player here
if (ptr.getClass().isActor() && ptr != MWMechanics::getPlayer())
{
MWMechanics::AiCast castPackage(targetId, spellId, true);
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(castPackage, ptr);
return;
}
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false); MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false);
MWMechanics::CastSpell cast(ptr, target); MWMechanics::CastSpell cast(ptr, target, false, true);
cast.mHitPosition = target.getRefData().getPosition().asVec3(); cast.mHitPosition = target.getRefData().getPosition().asVec3();
cast.mAlwaysSucceed = true; cast.mAlwaysSucceed = true;
cast.cast(spell); cast.cast(spell);
@ -1179,7 +1196,7 @@ namespace MWScript
std::string spell = runtime.getStringLiteral (runtime[0].mInteger); std::string spell = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
MWMechanics::CastSpell cast(ptr, ptr); MWMechanics::CastSpell cast(ptr, ptr, false, true);
cast.mHitPosition = ptr.getRefData().getPosition().asVec3(); cast.mHitPosition = ptr.getRefData().getPosition().asVec3();
cast.mAlwaysSucceed = true; cast.mAlwaysSucceed = true;
cast.cast(spell); cast.cast(spell);

@ -9,12 +9,19 @@
namespace MWWorld namespace MWWorld
{ {
ActionAlchemy::ActionAlchemy(bool force)
: Action (false)
, mForce(force)
{
}
void ActionAlchemy::executeImp (const Ptr& actor) void ActionAlchemy::executeImp (const Ptr& actor)
{ {
if (actor != MWMechanics::getPlayer()) if (actor != MWMechanics::getPlayer())
return; return;
if(MWMechanics::isPlayerInCombat()) { //Ensure we're not in combat if(!mForce && MWMechanics::isPlayerInCombat())
{ //Ensure we're not in combat
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage3}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage3}");
return; return;
} }

@ -7,7 +7,11 @@ namespace MWWorld
{ {
class ActionAlchemy : public Action class ActionAlchemy : public Action
{ {
bool mForce;
virtual void executeImp (const Ptr& actor); virtual void executeImp (const Ptr& actor);
public:
ActionAlchemy(bool force=false);
}; };
} }

@ -14,7 +14,9 @@
namespace MWWorld namespace MWWorld
{ {
ActionEquip::ActionEquip (const MWWorld::Ptr& object) : Action (false, object) ActionEquip::ActionEquip (const MWWorld::Ptr& object, bool force)
: Action (false, object)
, mForce(force)
{ {
} }
@ -23,6 +25,16 @@ namespace MWWorld
MWWorld::Ptr object = getTarget(); MWWorld::Ptr object = getTarget();
MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor); MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor);
if (object.getClass().hasItemHealth(object) && object.getCellRef().getCharge() == 0)
{
if (actor == MWMechanics::getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}");
return;
}
if (!mForce)
{
std::pair <int, std::string> result = object.getClass().canBeEquipped (object, actor); std::pair <int, std::string> result = object.getClass().canBeEquipped (object, actor);
// display error message if the player tried to equip something // display error message if the player tried to equip something
@ -36,6 +48,7 @@ namespace MWWorld
default: default:
break; break;
} }
}
// slots that this item can be equipped in // slots that this item can be equipped in
std::pair<std::vector<int>, bool> slots_ = getTarget().getClass().getEquipmentSlots(getTarget()); std::pair<std::vector<int>, bool> slots_ = getTarget().getClass().getEquipmentSlots(getTarget());

@ -2,17 +2,18 @@
#define GAME_MWWORLD_ACTIONEQUIP_H #define GAME_MWWORLD_ACTIONEQUIP_H
#include "action.hpp" #include "action.hpp"
#include "ptr.hpp"
namespace MWWorld namespace MWWorld
{ {
class ActionEquip : public Action class ActionEquip : public Action
{ {
bool mForce;
virtual void executeImp (const Ptr& actor); virtual void executeImp (const Ptr& actor);
public: public:
/// @param item to equip /// @param item to equip
ActionEquip (const Ptr& object); ActionEquip (const Ptr& object, bool force=false);
}; };
} }

@ -8,8 +8,9 @@
namespace MWWorld namespace MWWorld
{ {
ActionRepair::ActionRepair(const Ptr &item) ActionRepair::ActionRepair(const Ptr& item, bool force)
: Action(false, item) : Action (false, item)
, mForce(force)
{ {
} }
@ -18,7 +19,8 @@ namespace MWWorld
if (actor != MWMechanics::getPlayer()) if (actor != MWMechanics::getPlayer())
return; return;
if(MWMechanics::isPlayerInCombat()) { if(!mForce && MWMechanics::isPlayerInCombat())
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage2}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage2}");
return; return;
} }

@ -7,10 +7,13 @@ namespace MWWorld
{ {
class ActionRepair : public Action class ActionRepair : public Action
{ {
bool mForce;
virtual void executeImp (const Ptr& actor); virtual void executeImp (const Ptr& actor);
public: public:
ActionRepair(const MWWorld::Ptr& item); /// @param item repair hammer
ActionRepair(const Ptr& item, bool force=false);
}; };
} }

@ -113,7 +113,7 @@ namespace MWWorld
return std::shared_ptr<Action> (new NullAction); return std::shared_ptr<Action> (new NullAction);
} }
std::shared_ptr<Action> Class::use (const Ptr& ptr) const std::shared_ptr<Action> Class::use (const Ptr& ptr, bool force) const
{ {
return std::shared_ptr<Action> (new NullAction); return std::shared_ptr<Action> (new NullAction);
} }

@ -141,7 +141,7 @@ namespace MWWorld
virtual std::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const; virtual std::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const;
///< Generate action for activation (default implementation: return a null action). ///< Generate action for activation (default implementation: return a null action).
virtual std::shared_ptr<Action> use (const Ptr& ptr) virtual std::shared_ptr<Action> use (const Ptr& ptr, bool force=false)
const; const;
///< Generate action for using via inventory menu (default implementation: return a ///< Generate action for using via inventory menu (default implementation: return a
/// null action). /// null action).

@ -1299,7 +1299,10 @@ namespace MWWorld
if (isPlayer) if (isPlayer)
{ {
if (!newCell->isExterior()) if (!newCell->isExterior())
{
changeToInteriorCell(Misc::StringUtils::lowerCase(newCell->getCell()->mName), pos, false); changeToInteriorCell(Misc::StringUtils::lowerCase(newCell->getCell()->mName), pos, false);
removeContainerScripts(getPlayerPtr());
}
else else
{ {
if (mWorldScene->isCellActive(*newCell)) if (mWorldScene->isCellActive(*newCell))
@ -1320,7 +1323,8 @@ namespace MWWorld
mWorldScene->addObjectToScene(newPtr); mWorldScene->addObjectToScene(newPtr);
std::string script = newPtr.getClass().getScript(newPtr); std::string script = newPtr.getClass().getScript(newPtr);
if (!script.empty()) { if (!script.empty())
{
mLocalScripts.add(script, newPtr); mLocalScripts.add(script, newPtr);
} }
addContainerScripts(newPtr, newCell); addContainerScripts(newPtr, newCell);
@ -3032,13 +3036,13 @@ namespace MWWorld
return !fail; return !fail;
} }
void World::castSpell(const Ptr &actor) void World::castSpell(const Ptr &actor, bool manualSpell)
{ {
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
std::vector<MWWorld::Ptr> targetActors; std::vector<MWWorld::Ptr> targetActors;
if (!actor.isEmpty() && actor != MWMechanics::getPlayer()) if (!actor.isEmpty() && actor != MWMechanics::getPlayer() && !manualSpell)
actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors); actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors);
const float fCombatDistance = getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat(); const float fCombatDistance = getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat();
@ -3055,6 +3059,25 @@ namespace MWWorld
target = NULL; target = NULL;
if (target.isEmpty()) if (target.isEmpty())
{
// For scripted spells we should not use hit contact
if (manualSpell)
{
// Actors that are targeted by this actor's Follow or Escort packages also side with them
if (actor != MWMechanics::getPlayer())
{
const MWMechanics::CreatureStats &stats = actor.getClass().getCreatureStats(actor);
for (std::list<MWMechanics::AiPackage*>::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it)
{
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast)
{
target = (*it)->getTarget();
break;
}
}
}
}
else
{ {
// For actor targets, we want to use hit contact with bounding boxes. // For actor targets, we want to use hit contact with bounding boxes.
// This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise. // This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise.
@ -3097,10 +3120,11 @@ namespace MWWorld
target = NULL; target = NULL;
} }
} }
}
std::string selectedSpell = stats.getSpells().getSelectedSpell(); std::string selectedSpell = stats.getSpells().getSelectedSpell();
MWMechanics::CastSpell cast(actor, target); MWMechanics::CastSpell cast(actor, target, false, manualSpell);
cast.mHitPosition = hitPosition; cast.mHitPosition = hitPosition;
if (!selectedSpell.empty()) if (!selectedSpell.empty())

@ -696,7 +696,7 @@ namespace MWWorld
* @brief Cast the actual spell, should be called mid-animation * @brief Cast the actual spell, should be called mid-animation
* @param actor * @param actor
*/ */
void castSpell (const MWWorld::Ptr& actor) override; void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) override;
void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) override; void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) override;
void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,

@ -210,6 +210,11 @@ add_component_dir (fallback
fallback validate fallback validate
) )
if(NOT WIN32 AND NOT ANDROID)
add_component_dir (crashcatcher
crashcatcher
)
endif()
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
) )

@ -14,7 +14,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <sys/ptrace.h> #include <sys/ptrace.h>
#include <string> #include <iostream>
#include <boost/filesystem/fstream.hpp>
namespace bfs = boost::filesystem;
#include <SDL_messagebox.h> #include <SDL_messagebox.h>
@ -402,7 +406,7 @@ static void crash_handler(const char *logfile)
exit(0); exit(0);
} }
int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, const char *logfile, int (*user_info)(char*, char*)) int crashCatcherInstallHandlers(int argc, char **argv, int num_signals, int *signals, const char *logfile, int (*user_info)(char*, char*))
{ {
struct sigaction sa; struct sigaction sa;
stack_t altss; stack_t altss;
@ -454,19 +458,31 @@ int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, co
return retval; return retval;
} }
static bool is_debugger_present()
// gdb apparently opens FD(s) 3,4,5 (whereas a typical prog uses only stdin=0, stdout=1,stderr=2)
bool
is_debugger_attached(void)
{ {
bool rc = false; bfs::ifstream file((bfs::path("/proc/self/status")));
FILE *fd = fopen("/tmp", "r"); while (!file.eof())
{
if (fileno(fd) > 5) std::string word;
file >> word;
if (word == "TracerPid:")
{ {
rc = true; file >> word;
return word != "0";
}
} }
return false;
}
fclose(fd); void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath)
return rc; {
if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_present())
{
int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };
if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), NULL) == -1)
{
std::cerr << "Installing crash handler failed" << std::endl;
} else
std::cout << "Crash handler installed" << std::endl;
}
} }

@ -0,0 +1,20 @@
#ifndef CRASHCATCHER_H
#define CRASHCATCHER_H
#include <string>
#if (defined(__APPLE__) || (defined(__linux) && !defined(ANDROID)) || (defined(__unix) && !defined(ANDROID)) || defined(__posix))
#define USE_CRASH_CATCHER 1
#else
#define USE_CRASH_CATCHER 0
#endif
#if USE_CRASH_CATCHER
extern void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath);
#else
inline void crashCatcherInstall(int, char **, const std::string &crashLogPath)
{
}
#endif
#endif

@ -4,10 +4,24 @@
namespace Nif namespace Nif
{ {
osg::Quat NIFStream::getQuaternion()
{
float f[4];
readLittleEndianBufferOfType<4, float,uint32_t>(inp, (float*)&f);
osg::Quat quat;
quat.w() = f[0];
quat.x() = f[1];
quat.y() = f[2];
quat.z() = f[3];
return quat;
}
//Private functions Transformation NIFStream::getTrafo()
{
Transformation t;
//Public functions t.pos = getVector3();
t.rotation = getMatrix3();
t.scale = getFloat();
return t;
}
} }

@ -80,8 +80,8 @@ template<typename type, typename IntegerT> type inline readLittleEndianType(File
return val; return val;
} }
class NIFStream { class NIFStream
{
/// Input stream /// Input stream
Files::IStreamPtr inp; Files::IStreamPtr inp;
@ -97,67 +97,67 @@ public:
{ {
return readLittleEndianType<char,char>(inp); return readLittleEndianType<char,char>(inp);
} }
short getShort() short getShort()
{ {
return readLittleEndianType<short,short>(inp); return readLittleEndianType<short,short>(inp);
} }
unsigned short getUShort() unsigned short getUShort()
{ {
return readLittleEndianType<unsigned short,unsigned short>(inp); return readLittleEndianType<unsigned short,unsigned short>(inp);
} }
int getInt() int getInt()
{ {
return readLittleEndianType<int,int>(inp); return readLittleEndianType<int,int>(inp);
} }
unsigned int getUInt() unsigned int getUInt()
{ {
return readLittleEndianType<unsigned int,unsigned int>(inp); return readLittleEndianType<unsigned int,unsigned int>(inp);
} }
float getFloat() float getFloat()
{ {
return readLittleEndianType<float,uint32_t>(inp); return readLittleEndianType<float,uint32_t>(inp);
} }
osg::Vec2f getVector2() { osg::Vec2f getVector2()
{
osg::Vec2f vec; osg::Vec2f vec;
readLittleEndianBufferOfType<2,float,uint32_t>(inp, (float*)&vec._v[0]); readLittleEndianBufferOfType<2,float,uint32_t>(inp, (float*)&vec._v[0]);
return vec; return vec;
} }
osg::Vec3f getVector3() {
osg::Vec3f getVector3()
{
osg::Vec3f vec; osg::Vec3f vec;
readLittleEndianBufferOfType<3, float,uint32_t>(inp, (float*)&vec._v[0]); readLittleEndianBufferOfType<3, float,uint32_t>(inp, (float*)&vec._v[0]);
return vec; return vec;
} }
osg::Vec4f getVector4() {
osg::Vec4f getVector4()
{
osg::Vec4f vec; osg::Vec4f vec;
readLittleEndianBufferOfType<4, float,uint32_t>(inp, (float*)&vec._v[0]); readLittleEndianBufferOfType<4, float,uint32_t>(inp, (float*)&vec._v[0]);
return vec; return vec;
} }
Matrix3 getMatrix3() {
Matrix3 getMatrix3()
{
Matrix3 mat; Matrix3 mat;
readLittleEndianBufferOfType<9, float,uint32_t>(inp, (float*)&mat.mValues); readLittleEndianBufferOfType<9, float,uint32_t>(inp, (float*)&mat.mValues);
return mat; return mat;
} }
osg::Quat getQuaternion() {
float f[4]; osg::Quat getQuaternion();
readLittleEndianBufferOfType<4, float,uint32_t>(inp, (float*)&f);
osg::Quat quat; Transformation getTrafo();
quat.w() = f[0];
quat.x() = f[1];
quat.y() = f[2];
quat.z() = f[3];
return quat;
}
Transformation getTrafo() {
Transformation t;
t.pos = getVector3();
t.rotation = getMatrix3();
t.scale = getFloat();
return t;
}
///Read in a string of the given length ///Read in a string of the given length
std::string getString(size_t length) { std::string getString(size_t length)
{
std::vector<char> str(length + 1, 0); std::vector<char> str(length + 1, 0);
inp->read(&str[0], length); inp->read(&str[0], length);
@ -165,41 +165,54 @@ public:
return &str[0]; return &str[0];
} }
///Read in a string of the length specified in the file ///Read in a string of the length specified in the file
std::string getString() { std::string getString()
{
size_t size = readLittleEndianType<uint32_t,uint32_t>(inp); size_t size = readLittleEndianType<uint32_t,uint32_t>(inp);
return getString(size); return getString(size);
} }
///This is special since the version string doesn't start with a number, and ends with "\n" ///This is special since the version string doesn't start with a number, and ends with "\n"
std::string getVersionString() { std::string getVersionString()
{
std::string result; std::string result;
std::getline(*inp, result); std::getline(*inp, result);
return result; return result;
} }
void getUShorts(std::vector<unsigned short> &vec, size_t size) { void getUShorts(std::vector<unsigned short> &vec, size_t size)
{
vec.resize(size); vec.resize(size);
readLittleEndianDynamicBufferOfType<unsigned short,unsigned short>(inp, &vec.front(), size); readLittleEndianDynamicBufferOfType<unsigned short,unsigned short>(inp, &vec.front(), size);
} }
void getFloats(std::vector<float> &vec, size_t size) {
void getFloats(std::vector<float> &vec, size_t size)
{
vec.resize(size); vec.resize(size);
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, &vec.front(), size); readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, &vec.front(), size);
} }
void getVector2s(std::vector<osg::Vec2f> &vec, size_t size) {
void getVector2s(std::vector<osg::Vec2f> &vec, size_t size)
{
vec.resize(size); vec.resize(size);
/* The packed storage of each Vec2f is 2 floats exactly */ /* The packed storage of each Vec2f is 2 floats exactly */
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp,(float*) &vec.front(), size*2); readLittleEndianDynamicBufferOfType<float,uint32_t>(inp,(float*) &vec.front(), size*2);
} }
void getVector3s(std::vector<osg::Vec3f> &vec, size_t size) {
void getVector3s(std::vector<osg::Vec3f> &vec, size_t size)
{
vec.resize(size); vec.resize(size);
/* The packed storage of each Vec3f is 3 floats exactly */ /* The packed storage of each Vec3f is 3 floats exactly */
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, (float*) &vec.front(), size*3); readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, (float*) &vec.front(), size*3);
} }
void getVector4s(std::vector<osg::Vec4f> &vec, size_t size) {
void getVector4s(std::vector<osg::Vec4f> &vec, size_t size)
{
vec.resize(size); vec.resize(size);
/* The packed storage of each Vec4f is 4 floats exactly */ /* The packed storage of each Vec4f is 4 floats exactly */
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, (float*) &vec.front(), size*4); readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, (float*) &vec.front(), size*4);
} }
void getQuaternions(std::vector<osg::Quat> &quat, size_t size) {
void getQuaternions(std::vector<osg::Quat> &quat, size_t size)
{
quat.resize(size); quat.resize(size);
for (size_t i = 0;i < quat.size();i++) for (size_t i = 0;i < quat.size();i++)
quat[i] = getQuaternion(); quat[i] = getQuaternion();

@ -34,6 +34,13 @@ btVector3 getbtVector(const osg::Vec3f &v)
return btVector3(v.x(), v.y(), v.z()); return btVector3(v.x(), v.y(), v.z());
} }
bool pathFileNameStartsWithX(const std::string& path)
{
const std::size_t slashpos = path.find_last_of("/\\");
const std::size_t letterPos = slashpos == std::string::npos ? 0 : slashpos + 1;
return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X');
}
} }
namespace NifBullet namespace NifBullet
@ -88,18 +95,10 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::NIFFilePtr&
else else
{ {
bool autogenerated = hasAutoGeneratedCollision(node); bool autogenerated = hasAutoGeneratedCollision(node);
bool isAnimated = false;
// files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource).
// assume all nodes in the file will be animated // assume all nodes in the file will be animated
std::string filename = nif->getFilename(); const bool isAnimated = pathFileNameStartsWithX(nif->getFilename());
size_t slashpos = filename.find_last_of("/\\");
if (slashpos == std::string::npos)
slashpos = 0;
if (slashpos+1 < filename.size() && (filename[slashpos+1] == 'x' || filename[slashpos+1] == 'X'))
{
isAnimated = true;
}
handleNode(node, 0, autogenerated, isAnimated, autogenerated); handleNode(node, 0, autogenerated, isAnimated, autogenerated);

@ -10,6 +10,8 @@
#include <osgViewer/Viewer> #include <osgViewer/Viewer>
#include <osgViewer/Renderer> #include <osgViewer/Renderer>
#include <components/myguiplatform/myguidatamanager.hpp>
namespace Resource namespace Resource
{ {
@ -19,7 +21,7 @@ StatsHandler::StatsHandler():
_statsType(false), _statsType(false),
_statsWidth(1280.0f), _statsWidth(1280.0f),
_statsHeight(1024.0f), _statsHeight(1024.0f),
_font("fonts/arial.ttf"), _font(""),
_characterSize(20.0f) _characterSize(20.0f)
{ {
_camera = new osg::Camera; _camera = new osg::Camera;
@ -28,6 +30,15 @@ StatsHandler::StatsHandler():
_camera->setProjectionResizePolicy(osg::Camera::FIXED); _camera->setProjectionResizePolicy(osg::Camera::FIXED);
_resourceStatsChildNum = 0; _resourceStatsChildNum = 0;
_font = osgMyGUI::DataManager::getInstance().getDataPath("DejaVuLGCSansMono.ttf");
}
Profiler::Profiler()
{
_font = osgMyGUI::DataManager::getInstance().getDataPath("DejaVuLGCSansMono.ttf");
setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3);
} }
bool StatsHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) bool StatsHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
@ -77,7 +88,6 @@ void StatsHandler::setWindowSize(int width, int height)
{ {
_camera->setProjectionMatrix(osg::Matrix::ortho2D(0.0,_statsWidth,_statsHeight-height*_statsWidth/width,_statsHeight)); _camera->setProjectionMatrix(osg::Matrix::ortho2D(0.0,_statsWidth,_statsHeight-height*_statsWidth/width,_statsHeight));
} }
} }
void StatsHandler::toggle(osgViewer::ViewerBase *viewer) void StatsHandler::toggle(osgViewer::ViewerBase *viewer)

@ -1,7 +1,7 @@
#ifndef OPENMW_COMPONENTS_RESOURCE_STATS_H #ifndef OPENMW_COMPONENTS_RESOURCE_STATS_H
#define OPENMW_COMPONENTS_RESOURCE_STATS_H #define OPENMW_COMPONENTS_RESOURCE_STATS_H
#include <osgGA/GUIEventHandler> #include <osgViewer/ViewerEventHandlers>
namespace osgViewer namespace osgViewer
{ {
@ -15,6 +15,11 @@ namespace osg
namespace Resource namespace Resource
{ {
class Profiler : public osgViewer::StatsHandler
{
public:
Profiler();
};
class StatsHandler : public osgGA::GUIEventHandler class StatsHandler : public osgGA::GUIEventHandler
{ {

@ -1,2 +1,2 @@
parse_cmake parse_cmake
sphinx sphinx>=1.7.0

@ -23,7 +23,7 @@ sys.path.insert(0, project_root)
# -- General configuration ------------------------------------------------ # -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0' needs_sphinx = '1.7'
# Add any Sphinx extension module names here, as strings. They can be # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@ -34,8 +34,11 @@ extensions = [
'sphinx.ext.todo', 'sphinx.ext.todo',
'sphinx.ext.coverage', 'sphinx.ext.coverage',
'sphinx.ext.viewcode', 'sphinx.ext.viewcode',
'sphinx.ext.autosectionlabel',
] ]
#autosectionlabel_prefix_document = True
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ['_templates']

@ -1,10 +1,8 @@
Welcome to OpenMW's Documentation! Welcome to OpenMW's Documentation!
================================== ==================================
Sections
--------
.. toctree:: .. toctree::
:caption: Table of Contents
:maxdepth: 3 :maxdepth: 3
manuals/index manuals/index

@ -0,0 +1,50 @@
###############
Common Problems
###############
ERROR: Unknown fallback name: FontColor_color_header
####################################################
:Symptoms:
OpenMW crashes at startup with
``ERROR: Unknown fallback name: FontColor_color_header``
message at the end of ``openmw.log``, located `here </docs/source/reference/modding/paths>`_.
:Cause:
The OpenMW `configuration file </docs/source/reference/modding/paths>`_ ``openmw.cfg``
is severely lacking and missing fallback values
because "Settings Importer" was not run correctly.
:Fix:
Re-run "Settings Importer" from the OpenMW launcher.
Installing game files via Steam on macOS: DISK WRITE ERROR
##########################################################
:Symptoms:
Steam stages the download for Morrowind, but does not proceed.
The download will read "Paused: DISK WRITE ERROR".
:Cause:
The OpenMW `configuration file </docs/source/reference/modding/paths>`_ ``openmw.cfg``
is severely lacking and missing fallback values
because "Settings Importer" was not run correctly.
:Fix:
Open appmanifest_22320.acf in your favorite text editor.
Locate or create an entry under the "StateFlags" entry titled "installdir"
and give it the value "Morrowind".
Your file should now look something like this::
"AppState"
{
"appid" "22320"
"Universe" "1"
"name" "The Elder Scrolls III: Morrowind"
"StateFlags" "4"
"installdir" "Morrowind"
[other entries]
}
Restart the Steam client. The download should now proceed.

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

Loading…
Cancel
Save