forked from mirror/openmw-tes3mp
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
This commit is contained in:
commit
6cb5ac6e63
111 changed files with 1582 additions and 678 deletions
|
@ -34,6 +34,8 @@ MacOS:
|
|||
tags:
|
||||
- macos
|
||||
- xcode
|
||||
except:
|
||||
- branches # because our CI VMs are not public, MRs can't use them and timeout
|
||||
stage: build
|
||||
allow_failure: true
|
||||
script:
|
||||
|
@ -43,4 +45,25 @@ MacOS:
|
|||
- cd build; make -j2 package
|
||||
artifacts:
|
||||
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
|
||||
Roman Melnik (Kromgart)
|
||||
Roman Proskuryakov (kpp)
|
||||
Roman Siromakha (elsid)
|
||||
Sandy Carter (bwrsandman)
|
||||
Scott Howard
|
||||
scrawl
|
||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -2,6 +2,7 @@
|
|||
------
|
||||
|
||||
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 #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
|
||||
Bug #2455: Creatures attacks degrade armor
|
||||
|
@ -11,6 +12,7 @@
|
|||
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 #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 #3486: [Mod] NPC Commands does not work
|
||||
Bug #3591: Angled hit distance too low
|
||||
|
@ -22,15 +24,18 @@
|
|||
Bug #3997: Almalexia doesn't pace
|
||||
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 #4110: Fixed undo / redo menu text losing the assigned shortcuts
|
||||
Bug #4125: OpenMW logo cropped on bugtracker
|
||||
Bug #4215: OpenMW shows book text after last EOL tag
|
||||
Bug #4221: Characters get stuck in V-shaped terrain
|
||||
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 #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 #4307: World cleanup should remove dead bodies only if death animation is finished
|
||||
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 #4393: NPCs walk back to where they were after using ResetActors
|
||||
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 #4458: AiWander console command handles idle chances incorrectly
|
||||
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 #4464: OpenMW keeps AiState cached storages even after we cancel AI packages
|
||||
Bug #4469: Abot Silt Striders – Model turn 90 degrees on horizontal
|
||||
Bug #4474: No fallback when getVampireHead fails
|
||||
Bug #4475: Scripted animations should not cause movement
|
||||
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 #3641: Editor: Limit FPS in 3d preview window
|
||||
Feature #4222: 360° screenshots
|
||||
|
@ -62,6 +78,8 @@
|
|||
Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically
|
||||
Feature #4444: Per-group KF-animation files support
|
||||
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
|
||||
------
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/bash
|
||||
# set -x # turn-on for debugging
|
||||
|
||||
MISSINGTOOLS=0
|
||||
|
||||
|
@ -76,7 +77,6 @@ while [ $# -gt 0 ]; do
|
|||
h )
|
||||
cat <<EOF
|
||||
Usage: $0 [-cdehkpuvV]
|
||||
|
||||
Options:
|
||||
-c <Release/Debug>
|
||||
Set the configuration, can also be set with environment variable CONFIGURATION.
|
||||
|
@ -232,10 +232,9 @@ fi
|
|||
case $VS_VERSION in
|
||||
15|15.0|2017 )
|
||||
GENERATOR="Visual Studio 15 2017"
|
||||
TOOLSET="vc140"
|
||||
TOOLSET_REAL="vc141"
|
||||
TOOLSET="vc141"
|
||||
MSVC_REAL_VER="15"
|
||||
MSVC_VER="14"
|
||||
MSVC_VER="14.1"
|
||||
MSVC_YEAR="2015"
|
||||
MSVC_DISPLAY_YEAR="2017"
|
||||
;;
|
||||
|
@ -243,9 +242,8 @@ case $VS_VERSION in
|
|||
14|14.0|2015 )
|
||||
GENERATOR="Visual Studio 14 2015"
|
||||
TOOLSET="vc140"
|
||||
TOOLSET_REAL="vc140"
|
||||
MSVC_REAL_VER="14"
|
||||
MSVC_VER="14"
|
||||
MSVC_VER="14.0"
|
||||
MSVC_YEAR="2015"
|
||||
MSVC_DISPLAY_YEAR="2015"
|
||||
;;
|
||||
|
@ -253,9 +251,8 @@ case $VS_VERSION in
|
|||
12|12.0|2013 )
|
||||
GENERATOR="Visual Studio 12 2013"
|
||||
TOOLSET="vc120"
|
||||
TOOLSET_REAL="vc120"
|
||||
MSVC_REAL_VER="12"
|
||||
MSVC_VER="12"
|
||||
MSVC_VER="12.0"
|
||||
MSVC_YEAR="2013"
|
||||
MSVC_DISPLAY_YEAR="2013"
|
||||
;;
|
||||
|
@ -325,9 +322,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
|
||||
# Boost
|
||||
if [ -z $APPVEYOR ]; then
|
||||
download "Boost 1.61.0" \
|
||||
"https://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/boost_1_61_0-msvc-${MSVC_VER}.0-${BITS}.exe" \
|
||||
"boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe"
|
||||
download "Boost 1.67.0" \
|
||||
"https://sourceforge.net/projects/boost/files/boost-binaries/1.67.0/boost_1_67_0-msvc-${MSVC_VER}-${BITS}.exe" \
|
||||
"boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe"
|
||||
fi
|
||||
|
||||
# Bullet
|
||||
|
@ -365,8 +362,8 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
QT_SUFFIX=""
|
||||
fi
|
||||
|
||||
download "Qt 5.7.2" \
|
||||
"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" \
|
||||
download "Qt 5.7.0" \
|
||||
"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" \
|
||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
|
||||
"qt-5-install.qs"
|
||||
|
@ -403,9 +400,9 @@ echo
|
|||
|
||||
# Boost
|
||||
if [ -z $APPVEYOR ]; then
|
||||
printf "Boost 1.61.0... "
|
||||
printf "Boost 1.67.0... "
|
||||
else
|
||||
if [ $MSVC_VER -eq 12 ]; then
|
||||
if [ $MSVC_VER -eq 12.0 ]; then
|
||||
printf "Boost 1.58.0 AppVeyor... "
|
||||
else
|
||||
printf "Boost 1.67.0 AppVeyor... "
|
||||
|
@ -417,17 +414,27 @@ fi
|
|||
|
||||
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. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
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
|
||||
|
||||
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}"
|
||||
|
||||
echo Done.
|
||||
else
|
||||
# Appveyor unstable has all the boost we need already
|
||||
|
@ -444,19 +451,17 @@ fi
|
|||
|
||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||
-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.
|
||||
fi
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# Bullet
|
||||
printf "Bullet 2.86... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d Bullet ]; then
|
||||
printf -- "Exists. (No version checking) "
|
||||
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
|
||||
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
|
||||
fi
|
||||
|
||||
export BULLET_ROOT="$(real_pwd)/Bullet"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# FFmpeg
|
||||
printf "FFmpeg 3.2.4... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf FFmpeg
|
||||
|
||||
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP
|
||||
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP
|
||||
|
||||
mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg
|
||||
cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/
|
||||
rm -rf "ffmpeg-3.2.4-win${BITS}-dev"
|
||||
fi
|
||||
|
||||
export FFMPEG_HOME="$(real_pwd)/FFmpeg"
|
||||
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
|
||||
|
||||
if [ $BITS -eq 32 ]; then
|
||||
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
|
||||
fi
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# MyGUI
|
||||
printf "MyGUI 3.2.2... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d MyGUI ] && \
|
||||
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||
|
@ -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
|
||||
mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI
|
||||
fi
|
||||
|
||||
export MYGUI_HOME="$(real_pwd)/MyGUI"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="_d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# OpenAL
|
||||
printf "OpenAL-Soft 1.17.2... "
|
||||
{
|
||||
|
@ -542,24 +532,18 @@ printf "OpenAL-Soft 1.17.2... "
|
|||
rm -rf openal-soft-1.17.2-bin
|
||||
eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP
|
||||
fi
|
||||
|
||||
OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin"
|
||||
|
||||
add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
|
||||
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib"
|
||||
|
||||
add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# OSG
|
||||
printf "OSG 3.4.1-scrawl... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d OSG ] && \
|
||||
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
||||
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
|
||||
|
@ -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
|
||||
mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
|
||||
fi
|
||||
|
||||
OSG_SDK="$(real_pwd)/OSG"
|
||||
|
||||
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# Qt
|
||||
if [ -z $APPVEYOR ]; then
|
||||
printf "Qt 5.7.0... "
|
||||
|
@ -605,71 +582,53 @@ fi
|
|||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
|
||||
if [ -z $APPVEYOR ]; then
|
||||
cd $DEPS_INSTALL
|
||||
QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
|
||||
|
||||
if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Qt
|
||||
cp "${DEPS}/qt-5-install.qs" qt-install.qs
|
||||
|
||||
|
||||
sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs
|
||||
sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs
|
||||
|
||||
printf -- "(Installation might take a while) "
|
||||
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent
|
||||
|
||||
mv qt-install.qs Qt/
|
||||
|
||||
echo Done.
|
||||
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}
|
||||
fi
|
||||
|
||||
cd $QT_SDK
|
||||
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
|
||||
add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
||||
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
|
||||
|
||||
echo Done.
|
||||
else
|
||||
QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}"
|
||||
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
|
||||
DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||
|
||||
add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
||||
add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll"
|
||||
|
||||
echo Done.
|
||||
fi
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# SDL2
|
||||
printf "SDL 2.0.7... "
|
||||
{
|
||||
|
@ -679,26 +638,18 @@ printf "SDL 2.0.7... "
|
|||
rm -rf SDL2-2.0.7
|
||||
eval 7z x -y SDL2-2.0.7.zip $STRIP
|
||||
fi
|
||||
|
||||
export SDL2DIR="$(real_pwd)/SDL2-2.0.7"
|
||||
|
||||
add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
echo
|
||||
|
||||
|
||||
cd $DEPS_INSTALL/..
|
||||
|
||||
echo
|
||||
echo "Setting up OpenMW build..."
|
||||
|
||||
add_cmake_opts -DBUILD_BSATOOL=no \
|
||||
-DBUILD_ESMTOOL=no \
|
||||
-DBUILD_MYGUI_PLUGIN=no \
|
||||
-DOPENMW_MP_BUILD=on
|
||||
|
||||
if [ ! -z $CI ]; then
|
||||
case $STEP in
|
||||
components )
|
||||
|
@ -710,7 +661,6 @@ if [ ! -z $CI ]; then
|
|||
-DBUILD_OPENMW=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
|
||||
openmw )
|
||||
echo " Building subproject: OpenMW."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
|
@ -719,7 +669,6 @@ if [ ! -z $CI ]; then
|
|||
-DBUILD_OPENCS=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
|
||||
opencs )
|
||||
echo " Building subproject: OpenCS."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
|
@ -728,7 +677,6 @@ if [ ! -z $CI ]; then
|
|||
-DBUILD_OPENMW=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
|
||||
misc )
|
||||
echo " Building subprojects: Misc."
|
||||
add_cmake_opts -DBUILD_OPENCS=no \
|
||||
|
@ -736,7 +684,6 @@ if [ ! -z $CI ]; then
|
|||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# NOTE: Disable this when/if we want to run test cases
|
||||
#if [ -z $CI ]; then
|
||||
echo "- Copying Runtime DLLs..."
|
||||
|
@ -745,16 +692,13 @@ fi
|
|||
TARGET="$(basename "$DLL")"
|
||||
if [[ "$DLL" == *":"* ]]; then
|
||||
IFS=':'; SPLIT=( ${DLL} ); unset IFS
|
||||
|
||||
DLL=${SPLIT[0]}
|
||||
TARGET=${SPLIT[1]}
|
||||
fi
|
||||
|
||||
echo " ${TARGET}."
|
||||
cp "$DLL" "$BUILD_CONFIG/$TARGET"
|
||||
done
|
||||
echo
|
||||
|
||||
echo "- OSG Plugin DLLs..."
|
||||
mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1
|
||||
for DLL in $OSG_PLUGINS; do
|
||||
|
@ -762,7 +706,6 @@ fi
|
|||
cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1
|
||||
done
|
||||
echo
|
||||
|
||||
echo "- Qt Platform DLLs..."
|
||||
mkdir -p ${BUILD_CONFIG}/platforms
|
||||
for DLL in $QT_PLATFORMS; do
|
||||
|
@ -771,16 +714,13 @@ fi
|
|||
done
|
||||
echo
|
||||
#fi
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
printf -- "- Configuring... "
|
||||
else
|
||||
echo "- cmake .. $CMAKE_OPTS"
|
||||
fi
|
||||
|
||||
run_cmd cmake .. $CMAKE_OPTS
|
||||
RET=$?
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
if [ $RET -eq 0 ]; then
|
||||
echo Done.
|
||||
|
@ -788,5 +728,4 @@ if [ -z $VERBOSE ]; then
|
|||
echo Failed.
|
||||
fi
|
||||
fi
|
||||
|
||||
exit $RET
|
||||
|
|
|
@ -666,10 +666,10 @@ if (WIN32)
|
|||
endif()
|
||||
|
||||
if (BUILD_OPENMW)
|
||||
# Release builds use the debug console
|
||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
|
||||
set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
|
||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
|
||||
# Release builds don't use the debug console
|
||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
||||
set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_RELEASE "_WINDOWS")
|
||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||
endif()
|
||||
|
||||
# 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
|
||||
# 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
|
||||
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
|
||||
4347 # Non-template function with same name and parameter count as template function
|
||||
|
@ -701,6 +702,7 @@ if (WIN32)
|
|||
|
||||
# caused by MyGUI
|
||||
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
|
||||
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::string baseGameFile("Game Files:GameFile");
|
||||
std::string gameFile("");
|
||||
std::time_t defaultTime = 0;
|
||||
ToUTF8::Utf8Encoder encoder(mEncoding);
|
||||
|
||||
|
@ -890,7 +889,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
|
|||
multistrmap::const_iterator it = ini.begin();
|
||||
for (int i=0; it != ini.end(); i++)
|
||||
{
|
||||
gameFile = baseGameFile;
|
||||
std::string gameFile = baseGameFile;
|
||||
gameFile.append(std::to_string(i));
|
||||
|
||||
it = ini.find(gameFile);
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
#include <QLocalSocket>
|
||||
#include <QMessageBox>
|
||||
|
||||
|
||||
#include <components/crashcatcher/crashcatcher.hpp>
|
||||
|
||||
#include <components/fallback/validate.hpp>
|
||||
|
||||
#include <components/nifosg/nifloader.hpp>
|
||||
|
@ -18,12 +21,16 @@
|
|||
|
||||
using namespace Fallback;
|
||||
|
||||
CS::Editor::Editor ()
|
||||
CS::Editor::Editor (int argc, char **argv)
|
||||
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
|
||||
mViewManager (mDocumentManager), mPid(""),
|
||||
mLock(), mMerge (mDocumentManager),
|
||||
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();
|
||||
|
||||
setupDataFiles (config.first);
|
||||
|
|
|
@ -66,7 +66,7 @@ namespace CS
|
|||
|
||||
public:
|
||||
|
||||
Editor ();
|
||||
Editor (int argc, char **argv);
|
||||
~Editor ();
|
||||
|
||||
bool makeIPCServer();
|
||||
|
|
|
@ -48,7 +48,7 @@ int main(int argc, char *argv[])
|
|||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
{
|
||||
// To allow background thread drawing in OSG
|
||||
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
|
||||
|
||||
|
@ -67,7 +67,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
application.setWindowIcon (QIcon (":./openmw-cs.png"));
|
||||
|
||||
CS::Editor editor;
|
||||
CS::Editor editor(argc, argv);
|
||||
|
||||
if(!editor.makeIPCServer())
|
||||
{
|
||||
|
|
|
@ -22,7 +22,8 @@ void CSMTools::Search::searchTextCell (const CSMWorld::IdTableBase *model,
|
|||
|
||||
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;
|
||||
hint
|
||||
|
@ -120,25 +121,26 @@ QString CSMTools::Search::flatten (const QString& text) const
|
|||
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) {}
|
||||
|
||||
CSMTools::Search::Search (Type type, const std::string& value)
|
||||
: mType (type), mText (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||
CSMTools::Search::Search (Type type, bool caseSensitive, const std::string& value)
|
||||
: mType (type), mText (value), mValue (0), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||
{
|
||||
if (type!=Type_Text && type!=Type_Id)
|
||||
throw std::logic_error ("Invalid search parameter (string)");
|
||||
}
|
||||
|
||||
CSMTools::Search::Search (Type type, const QRegExp& value)
|
||||
: mType (type), mRegExp (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||
CSMTools::Search::Search (Type type, bool caseSensitive, const QRegExp& value)
|
||||
: 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)
|
||||
throw std::logic_error ("Invalid search parameter (RegExp)");
|
||||
}
|
||||
|
||||
CSMTools::Search::Search (Type type, int value)
|
||||
: mType (type), mValue (value), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||
CSMTools::Search::Search (Type type, bool caseSensitive, int value)
|
||||
: mType (type), mValue (value), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||
{
|
||||
if (type!=Type_RecordState)
|
||||
throw std::logic_error ("invalid search parameter (int)");
|
||||
|
|
|
@ -43,6 +43,7 @@ namespace CSMTools
|
|||
std::string mText;
|
||||
QRegExp mRegExp;
|
||||
int mValue;
|
||||
bool mCase;
|
||||
std::set<int> mColumns;
|
||||
int mIdColumn;
|
||||
int mTypeColumn;
|
||||
|
@ -67,11 +68,11 @@ namespace CSMTools
|
|||
|
||||
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.
|
||||
void configure (const CSMWorld::IdTableBase *model);
|
||||
|
|
|
@ -136,7 +136,7 @@ namespace CSMWorld
|
|||
struct VarTypeColumn : public Column<ESXRecordT>
|
||||
{
|
||||
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
|
||||
|
@ -161,7 +161,7 @@ namespace CSMWorld
|
|||
template<typename 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
|
||||
{
|
||||
|
|
|
@ -84,15 +84,28 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value
|
|||
if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole)
|
||||
{
|
||||
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);
|
||||
if (stateColumn != -1)
|
||||
{
|
||||
QModelIndex stateIndex = this->index(index.row(), stateColumn);
|
||||
emit dataChanged(stateIndex, stateIndex);
|
||||
}
|
||||
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);
|
||||
emit dataChanged(stateIndex, stateIndex);
|
||||
}
|
||||
} else
|
||||
emit dataChanged(index, index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -156,4 +156,4 @@ namespace CSMWorld
|
|||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -101,15 +101,39 @@ void CSVDoc::View::setupFileMenu()
|
|||
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()
|
||||
{
|
||||
QMenu *edit = menuBar()->addMenu (tr ("Edit"));
|
||||
|
||||
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo"));
|
||||
setupShortcut("document-edit-undo", mUndo);
|
||||
connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ()));
|
||||
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);
|
||||
edit->addAction (mRedo);
|
||||
|
||||
|
|
|
@ -152,6 +152,10 @@ namespace CSVDoc
|
|||
|
||||
void settingChanged (const CSMPrefs::Setting *setting);
|
||||
|
||||
void undoActionChanged();
|
||||
|
||||
void redoActionChanged();
|
||||
|
||||
void newView();
|
||||
|
||||
void save();
|
||||
|
|
|
@ -372,10 +372,10 @@ osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker (int axis)
|
|||
indices[j] -= VertexCount;
|
||||
}
|
||||
|
||||
size_t offset = i * IndicesPerSegment;
|
||||
size_t elementOffset = i * IndicesPerSegment;
|
||||
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)
|
||||
{
|
||||
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
|
||||
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)
|
||||
: QWidget (parent), mSearch ("Search"), mSearchEnabled (false), mReplace ("Replace All")
|
||||
: QWidget (parent), mSearch (tr("Search")), mSearchEnabled (false), mReplace (tr("Replace All"))
|
||||
{
|
||||
mLayout = new QGridLayout (this);
|
||||
|
||||
|
@ -48,29 +48,26 @@ CSVTools::SearchBox::SearchBox (QWidget *parent)
|
|||
++iter)
|
||||
mRecordState.addItem (QString::fromUtf8 (iter->c_str()));
|
||||
|
||||
mMode.addItem ("Text");
|
||||
mMode.addItem ("Text (RegEx)");
|
||||
mMode.addItem ("ID");
|
||||
mMode.addItem ("ID (RegEx)");
|
||||
mMode.addItem ("Record State");
|
||||
|
||||
mMode.addItem (tr("Text"));
|
||||
mMode.addItem (tr("Text (RegEx)"));
|
||||
mMode.addItem (tr("ID"));
|
||||
mMode.addItem (tr("ID (RegEx)"));
|
||||
mMode.addItem (tr("Record State"));
|
||||
connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int)));
|
||||
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 (1, &mRecordState);
|
||||
mLayout->addWidget (&mInput, 0, 1);
|
||||
|
||||
mLayout->addWidget (&mInput, 0, 1);
|
||||
|
||||
connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int)));
|
||||
|
||||
connect (&mText, SIGNAL (textChanged (const QString&)),
|
||||
this, SLOT (textChanged (const QString&)));
|
||||
mCaseSensitive.setText (tr ("Case"));
|
||||
mLayout->addWidget (&mCaseSensitive, 0, 2);
|
||||
|
||||
connect (&mSearch, SIGNAL (clicked (bool)), this, SLOT (startSearch (bool)));
|
||||
|
||||
connect (&mText, SIGNAL (returnPressed()), this, SLOT (startSearch()));
|
||||
mLayout->addWidget (&mSearch, 0, 3);
|
||||
|
||||
// replace panel
|
||||
mReplaceInput.insertWidget (0, &mReplaceText);
|
||||
|
@ -102,23 +99,24 @@ void CSVTools::SearchBox::setSearchMode (bool enabled)
|
|||
|
||||
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)
|
||||
{
|
||||
case CSMTools::Search::Type_Text:
|
||||
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_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:
|
||||
|
||||
return CSMTools::Search (type, mRecordState.currentIndex());
|
||||
return CSMTools::Search (type, caseSensitive, mRecordState.currentIndex());
|
||||
|
||||
case CSMTools::Search::Type_None:
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QWidget>
|
||||
#include <QLineEdit>
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
#include <QStackedWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
|
@ -24,6 +25,7 @@ namespace CSVTools
|
|||
QStackedWidget mInput;
|
||||
QLineEdit mText;
|
||||
QComboBox mRecordState;
|
||||
QCheckBox mCaseSensitive;
|
||||
QPushButton mSearch;
|
||||
QGridLayout *mLayout;
|
||||
QComboBox mMode;
|
||||
|
|
|
@ -11,12 +11,10 @@ if (ANDROID)
|
|||
set(GAME ${GAME} android_main.c)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT ANDROID)
|
||||
set(GAME ${GAME} crashcatcher.cpp)
|
||||
endif()
|
||||
set(GAME_HEADER
|
||||
engine.hpp
|
||||
)
|
||||
|
||||
source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||
|
||||
add_openmw_dir (mwrender
|
||||
|
@ -83,9 +81,9 @@ add_openmw_dir (mwclass
|
|||
add_openmw_dir (mwmechanics
|
||||
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
|
||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
|
||||
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
|
||||
character actors objects aistate coordinateconverter trading aiface weaponpriority spellpriority
|
||||
character actors objects aistate coordinateconverter trading weaponpriority spellpriority
|
||||
)
|
||||
|
||||
add_openmw_dir (mwstate
|
||||
|
|
|
@ -757,28 +757,19 @@ void OMW::Engine::go()
|
|||
|
||||
std::cout << "OSG version: " << osgGetVersion() << std::endl;
|
||||
|
||||
mViewer = new osgViewer::Viewer;
|
||||
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);
|
||||
|
||||
// Load settings
|
||||
Settings::Manager settings;
|
||||
std::string settingspath;
|
||||
|
||||
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(),
|
||||
Settings::Manager::getString("screenshot format", "General"));
|
||||
|
||||
|
@ -788,10 +779,6 @@ void OMW::Engine::go()
|
|||
|
||||
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));
|
||||
|
||||
// Create encoder
|
||||
ToUTF8::Utf8Encoder encoder (mEncoding);
|
||||
mEncoder = &encoder;
|
||||
|
||||
prepareEngine (settings);
|
||||
|
||||
/*
|
||||
|
@ -814,6 +801,22 @@ void OMW::Engine::go()
|
|||
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())
|
||||
{
|
||||
mEnvironment.getStateManager()->loadGame(mSaveGameFile);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <components/version/version.hpp>
|
||||
#include <components/crashcatcher/crashcatcher.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/files/escape.hpp>
|
||||
#include <components/fallback/validate.hpp>
|
||||
|
@ -34,18 +35,6 @@
|
|||
#include <unistd.h>
|
||||
#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
|
||||
|
||||
|
@ -419,18 +408,7 @@ int main(int argc, char**argv)
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
|
||||
#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
|
||||
crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / "crash.log").string());
|
||||
|
||||
#ifdef __APPLE__
|
||||
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
|
||||
virtual void keepPlayerAlive() = 0;
|
||||
|
||||
virtual bool isCastingSpell (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 void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell) = 0;
|
||||
|
||||
/// 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
|
||||
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
|
||||
virtual void useItem(const MWWorld::Ptr& item) = 0;
|
||||
virtual void useItem(const MWWorld::Ptr& item, bool force=false) = 0;
|
||||
|
||||
virtual void updateSpellWindow() = 0;
|
||||
|
||||
|
|
|
@ -583,7 +583,7 @@ namespace MWBase
|
|||
*/
|
||||
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 launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
|
||||
|
|
|
@ -157,10 +157,9 @@ namespace MWClass
|
|||
return info;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<MWWorld::Action> Apparatus::use (const MWWorld::Ptr& ptr) const
|
||||
std::shared_ptr<MWWorld::Action> Apparatus::use (const MWWorld::Ptr& ptr, bool force) 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
|
||||
|
|
|
@ -50,8 +50,7 @@ namespace MWClass
|
|||
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
|
||||
///< Return name of inventory icon.
|
||||
|
||||
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
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -387,9 +387,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -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
|
||||
/// Second item in the pair specifies the error message
|
||||
|
||||
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
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -195,7 +195,7 @@ namespace MWClass
|
|||
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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
///< 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
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -276,9 +276,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -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.
|
||||
/// Second item in the pair specifies the error message
|
||||
|
||||
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
|
||||
|
||||
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));
|
||||
|
||||
|
|
|
@ -36,8 +36,7 @@ namespace MWClass
|
|||
virtual int getValue (const MWWorld::ConstPtr& ptr) const;
|
||||
///< 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
|
||||
|
||||
static void registerSelf();
|
||||
|
|
|
@ -186,9 +186,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -55,8 +55,7 @@ namespace MWClass
|
|||
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
|
||||
///< Return name of inventory icon.
|
||||
|
||||
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
|
||||
|
||||
virtual void setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const;
|
||||
|
|
|
@ -140,9 +140,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -53,8 +53,7 @@ namespace MWClass
|
|||
|
||||
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)
|
||||
const;
|
||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -259,7 +259,7 @@ namespace MWClass
|
|||
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()))
|
||||
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::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
|
||||
|
||||
virtual float getWeight (const MWWorld::ConstPtr& ptr) const;
|
||||
|
|
|
@ -172,7 +172,7 @@ namespace MWClass
|
|||
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 =
|
||||
ptr.get<ESM::Potion>();
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace MWClass
|
|||
virtual int getValue (const MWWorld::ConstPtr& ptr) const;
|
||||
///< 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
|
||||
|
||||
static void registerSelf();
|
||||
|
|
|
@ -140,9 +140,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -53,8 +53,7 @@ namespace MWClass
|
|||
|
||||
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)
|
||||
const;
|
||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -182,9 +182,9 @@ namespace MWClass
|
|||
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
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace MWClass
|
|||
|
||||
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;
|
||||
///< Generate action for using via inventory menu (default implementation: return a
|
||||
/// null action).
|
||||
|
|
|
@ -435,9 +435,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -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.
|
||||
/// Second item in the pair specifies the error message
|
||||
|
||||
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
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -629,6 +629,9 @@ namespace MWGui
|
|||
|
||||
void DialogueWindow::onTopicActivated(const std::string &topicId)
|
||||
{
|
||||
if (mGoodbye)
|
||||
return;
|
||||
|
||||
MWBase::Environment::get().getDialogueManager()->keywordSelected(topicId, mCallback.get());
|
||||
updateTopics();
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/action.hpp"
|
||||
#include "../mwworld/actionequip.hpp"
|
||||
#include "../mwscript/interpretercontext.hpp"
|
||||
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
|
@ -488,7 +488,7 @@ namespace MWGui
|
|||
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);
|
||||
|
||||
|
@ -497,13 +497,24 @@ 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
|
||||
if (!ptr.getClass().getEquipmentSlots(ptr).first.empty())
|
||||
{
|
||||
std::pair<int, std::string> canEquip = ptr.getClass().canBeEquipped(ptr, player);
|
||||
if (canEquip.first == 0)
|
||||
if (ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}");
|
||||
updateItemView();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force)
|
||||
{
|
||||
std::pair<int, std::string> canEquip = ptr.getClass().canBeEquipped(ptr, player);
|
||||
|
||||
if (canEquip.first == 0)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
||||
updateItemView();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the item has a script, set its OnPcEquip to 1
|
||||
|
@ -526,9 +537,8 @@ namespace MWGui
|
|||
{
|
||||
if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0)
|
||||
{
|
||||
std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr);
|
||||
|
||||
action->execute (player);
|
||||
std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);
|
||||
action->execute(player);
|
||||
}
|
||||
else
|
||||
mSkippedToEquip = ptr;
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace MWGui
|
|||
|
||||
void clear();
|
||||
|
||||
void useItem(const MWWorld::Ptr& ptr);
|
||||
void useItem(const MWWorld::Ptr& ptr, bool force=false);
|
||||
|
||||
void setGuiMode(GuiMode mode);
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ namespace MWGui
|
|||
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -1423,10 +1423,10 @@ namespace MWGui
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
void WindowManager::useItem(const MWWorld::Ptr &item)
|
||||
void WindowManager::useItem(const MWWorld::Ptr &item, bool bypassBeastRestrictions)
|
||||
{
|
||||
if (mInventoryWindow)
|
||||
mInventoryWindow->useItem(item);
|
||||
mInventoryWindow->useItem(item, bypassBeastRestrictions);
|
||||
}
|
||||
|
||||
bool WindowManager::isAllowed (GuiWindow wnd) const
|
||||
|
|
|
@ -196,7 +196,7 @@ namespace MWGui
|
|||
*/
|
||||
|
||||
/// 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();
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation)
|
||||
{
|
||||
mCharacterController.reset(new CharacterController(ptr, animation));
|
||||
|
@ -19,10 +18,4 @@ namespace MWMechanics
|
|||
{
|
||||
return mCharacterController.get();
|
||||
}
|
||||
|
||||
AiState& Actor::getAiState()
|
||||
{
|
||||
return mAiState;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include "aistate.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class Animation;
|
||||
|
@ -29,12 +27,8 @@ namespace MWMechanics
|
|||
|
||||
CharacterController* getCharacterController();
|
||||
|
||||
AiState& getAiState();
|
||||
|
||||
private:
|
||||
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)
|
||||
{
|
||||
if (!actor.getClass().isActor())
|
||||
|
@ -1523,7 +1530,7 @@ namespace MWMechanics
|
|||
{
|
||||
CreatureStats &stats = iter->first.getClass().getCreatureStats(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();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
PtrActorMap::const_iterator it = mActors.find(ptr);
|
||||
|
@ -2220,7 +2236,7 @@ namespace MWMechanics
|
|||
|| ptr.getClass().getCreatureStats(ptr).isParalyzed())
|
||||
continue;
|
||||
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.
|
||||
|
||||
void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell=false);
|
||||
|
||||
void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);
|
||||
///< Updates an actor with a new Ptr
|
||||
|
||||
|
@ -171,6 +173,7 @@ namespace MWMechanics
|
|||
|
||||
void clear(); // Clear death counter
|
||||
|
||||
bool isCastingSpell(const MWWorld::Ptr& ptr) const;
|
||||
bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
|
||||
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;
|
||||
|
||||
|
|
84
apps/openmw/mwmechanics/aicast.cpp
Normal file
84
apps/openmw/mwmechanics/aicast.cpp
Normal file
|
@ -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;
|
||||
}
|
37
apps/openmw/mwmechanics/aicast.hpp
Normal file
37
apps/openmw/mwmechanics/aicast.hpp
Normal file
|
@ -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
|
||||
{
|
||||
|
||||
/// \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)
|
||||
{
|
||||
mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
|
@ -128,7 +60,7 @@ namespace MWMechanics
|
|||
|
||||
void AiCombat::init()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -23,7 +23,72 @@ namespace MWMechanics
|
|||
{
|
||||
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
|
||||
class AiCombat : public AiPackage
|
||||
|
|
|
@ -17,22 +17,6 @@
|
|||
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;
|
||||
|
||||
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z)
|
||||
|
|
|
@ -19,6 +19,21 @@ namespace AiSequence
|
|||
|
||||
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
|
||||
/** 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,
|
||||
TypeIdFace = 8,
|
||||
TypeIdBreathe = 9,
|
||||
TypeIdInternalTravel = 10
|
||||
TypeIdInternalTravel = 10,
|
||||
TypeIdCast = 11
|
||||
};
|
||||
|
||||
///Default constructor
|
||||
|
|
|
@ -180,15 +180,11 @@ bool AiSequence::isPackageDone() const
|
|||
|
||||
bool isActualAiPackage(int packageTypeId)
|
||||
{
|
||||
return (packageTypeId != AiPackage::TypeIdCombat
|
||||
&& packageTypeId != AiPackage::TypeIdPursue
|
||||
&& packageTypeId != AiPackage::TypeIdAvoidDoor
|
||||
&& packageTypeId != AiPackage::TypeIdFace
|
||||
&& packageTypeId != AiPackage::TypeIdBreathe
|
||||
&& packageTypeId != AiPackage::TypeIdInternalTravel);
|
||||
return (packageTypeId >= AiPackage::TypeIdWander &&
|
||||
packageTypeId <= AiPackage::TypeIdActivate);
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
|
@ -262,7 +258,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
|
||||
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
|
||||
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()))
|
||||
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.
|
||||
// Also there is no point to stack return packages.
|
||||
int currentTypeId = getTypeId();
|
||||
|
@ -316,7 +312,8 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
|
|||
if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander
|
||||
&& !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel)
|
||||
&& (newTypeId <= MWMechanics::AiPackage::TypeIdCombat
|
||||
|| newTypeId == MWMechanics::AiPackage::TypeIdPursue))
|
||||
|| newTypeId == MWMechanics::AiPackage::TypeIdPursue
|
||||
|| newTypeId == MWMechanics::AiPackage::TypeIdCast))
|
||||
{
|
||||
osg::Vec3f dest;
|
||||
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
|
||||
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())
|
||||
{
|
||||
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());
|
||||
|
||||
// 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()
|
||||
|
@ -494,12 +506,12 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
|||
mLastAiPackage = sequence.mLastAiPackage;
|
||||
}
|
||||
|
||||
void AiSequence::fastForward(const MWWorld::Ptr& actor, AiState& state)
|
||||
void AiSequence::fastForward(const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (!mPackages.empty())
|
||||
{
|
||||
MWMechanics::AiPackage* package = mPackages.front();
|
||||
package->fastForward(actor, state);
|
||||
package->fastForward(actor, mAiState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <list>
|
||||
|
||||
#include "aistate.hpp"
|
||||
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
|
@ -47,6 +49,7 @@ namespace MWMechanics
|
|||
|
||||
/// The type of AI package that ran last
|
||||
int mLastAiPackage;
|
||||
AiState mAiState;
|
||||
|
||||
public:
|
||||
///Default constructor
|
||||
|
@ -104,10 +107,10 @@ namespace MWMechanics
|
|||
void stopPursuit();
|
||||
|
||||
/// 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
|
||||
void fastForward(const MWWorld::Ptr &actor, AiState &state);
|
||||
void fastForward(const MWWorld::Ptr &actor);
|
||||
|
||||
/// Remove all packages.
|
||||
void clear();
|
||||
|
|
|
@ -51,67 +51,6 @@ namespace MWMechanics
|
|||
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):
|
||||
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))
|
||||
|
@ -221,7 +160,7 @@ namespace MWMechanics
|
|||
mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell()));
|
||||
|
||||
if (mPathFinder.isPathConstructed())
|
||||
storage.setState(Wander_Walking);
|
||||
storage.setState(AiWanderStorage::Wander_Walking);
|
||||
}
|
||||
|
||||
doPerFrameActionsForState(actor, duration, storage, pos);
|
||||
|
@ -270,7 +209,7 @@ namespace MWMechanics
|
|||
|
||||
if(actorCanMoveByZ && mDistance > 0) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -283,7 +222,7 @@ namespace MWMechanics
|
|||
if (Misc::Rng::rollDice(100) >= 96) {
|
||||
wanderNearStart(actor, storage, mDistance);
|
||||
} else {
|
||||
storage.setState(Wander_IdleNow);
|
||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||
}
|
||||
} else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually) {
|
||||
storage.mCanWanderAlongPathGrid = false;
|
||||
|
@ -299,13 +238,13 @@ namespace MWMechanics
|
|||
mDistance = 0;
|
||||
|
||||
// Allow interrupting a walking actor to trigger a greeting
|
||||
WanderState& wanderState = storage.mState;
|
||||
if ((wanderState == Wander_IdleNow) || (wanderState == Wander_Walking))
|
||||
AiWanderStorage::WanderState& wanderState = storage.mState;
|
||||
if ((wanderState == AiWanderStorage::Wander_IdleNow) || (wanderState == AiWanderStorage::Wander_Walking))
|
||||
{
|
||||
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
|
||||
if(!mPathFinder.isPathConstructed())
|
||||
|
@ -381,7 +320,7 @@ namespace MWMechanics
|
|||
|
||||
if (mPathFinder.isPathConstructed())
|
||||
{
|
||||
storage.setState(Wander_Walking, true);
|
||||
storage.setState(AiWanderStorage::Wander_Walking, true);
|
||||
mHasDestination = true;
|
||||
}
|
||||
return;
|
||||
|
@ -410,26 +349,26 @@ namespace MWMechanics
|
|||
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
|
||||
stopWalking(actor, storage);
|
||||
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)
|
||||
{
|
||||
switch (storage.mState)
|
||||
{
|
||||
case Wander_IdleNow:
|
||||
case AiWanderStorage::Wander_IdleNow:
|
||||
onIdleStatePerFrameActions(actor, duration, storage);
|
||||
break;
|
||||
|
||||
case Wander_Walking:
|
||||
case AiWanderStorage::Wander_Walking:
|
||||
onWalkingStatePerFrameActions(actor, duration, storage, pos);
|
||||
break;
|
||||
|
||||
case Wander_ChooseAction:
|
||||
case AiWanderStorage::Wander_ChooseAction:
|
||||
onChooseActionStatePerFrameActions(actor, storage);
|
||||
break;
|
||||
|
||||
case Wander_MoveNow:
|
||||
case AiWanderStorage::Wander_MoveNow:
|
||||
break; // nothing to do
|
||||
|
||||
default:
|
||||
|
@ -451,7 +390,7 @@ namespace MWMechanics
|
|||
if (mDistance && // actor is not intended to be stationary
|
||||
proximityToDoor(actor, distance*1.6f))
|
||||
{
|
||||
storage.setState(Wander_MoveNow);
|
||||
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||
storage.mTrimCurrentNode = false; // just in case
|
||||
return;
|
||||
}
|
||||
|
@ -468,13 +407,13 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
// Check if idle animation finished
|
||||
GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))
|
||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == AiWanderStorage::Greet_Done || greetingState == AiWanderStorage::Greet_None))
|
||||
{
|
||||
if (mPathFinder.isPathConstructed())
|
||||
storage.setState(Wander_Walking);
|
||||
storage.setState(AiWanderStorage::Wander_Walking);
|
||||
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))
|
||||
{
|
||||
stopWalking(actor, storage);
|
||||
storage.setState(Wander_ChooseAction);
|
||||
storage.setState(AiWanderStorage::Wander_ChooseAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -502,7 +441,7 @@ namespace MWMechanics
|
|||
|
||||
if (!idleAnimation && mDistance)
|
||||
{
|
||||
storage.setState(Wander_MoveNow);
|
||||
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||
return;
|
||||
}
|
||||
if(idleAnimation)
|
||||
|
@ -512,13 +451,13 @@ namespace MWMechanics
|
|||
if(!playIdle(actor, idleAnimation))
|
||||
{
|
||||
storage.mBadIdles.push_back(idleAnimation);
|
||||
storage.setState(Wander_ChooseAction);
|
||||
storage.setState(AiWanderStorage::Wander_ChooseAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
storage.setState(Wander_IdleNow);
|
||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||
}
|
||||
|
||||
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos)
|
||||
|
@ -534,7 +473,7 @@ namespace MWMechanics
|
|||
trimAllowedNodes(storage.mAllowedNodes, mPathFinder);
|
||||
mObstacleCheck.clear();
|
||||
stopWalking(actor, storage);
|
||||
storage.setState(Wander_MoveNow);
|
||||
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||
}
|
||||
|
||||
storage.mStuckCount++; // TODO: maybe no longer needed
|
||||
|
@ -545,7 +484,7 @@ namespace MWMechanics
|
|||
{
|
||||
mObstacleCheck.clear();
|
||||
stopWalking(actor, storage);
|
||||
storage.setState(Wander_ChooseAction);
|
||||
storage.setState(AiWanderStorage::Wander_ChooseAction);
|
||||
storage.mStuckCount = 0;
|
||||
}
|
||||
}
|
||||
|
@ -596,8 +535,8 @@ namespace MWMechanics
|
|||
float playerDistSqr = (playerPos - actorPos).length2();
|
||||
|
||||
int& greetingTimer = storage.mGreetingTimer;
|
||||
GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if (greetingState == Greet_None)
|
||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if (greetingState == AiWanderStorage::Greet_None)
|
||||
{
|
||||
if ((playerDistSqr <= helloDistance*helloDistance) &&
|
||||
!player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
||||
|
@ -606,37 +545,37 @@ namespace MWMechanics
|
|||
|
||||
if (greetingTimer >= GREETING_SHOULD_START)
|
||||
{
|
||||
greetingState = Greet_InProgress;
|
||||
greetingState = AiWanderStorage::Greet_InProgress;
|
||||
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
|
||||
greetingTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (greetingState == Greet_InProgress)
|
||||
if (greetingState == AiWanderStorage::Greet_InProgress)
|
||||
{
|
||||
greetingTimer++;
|
||||
|
||||
if (storage.mState == Wander_Walking)
|
||||
if (storage.mState == AiWanderStorage::Wander_Walking)
|
||||
{
|
||||
stopWalking(actor, storage, false);
|
||||
mObstacleCheck.clear();
|
||||
storage.setState(Wander_IdleNow);
|
||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||
}
|
||||
|
||||
turnActorToFacePlayer(actorPos, playerPos, storage);
|
||||
|
||||
if (greetingTimer >= GREETING_SHOULD_END)
|
||||
{
|
||||
greetingState = Greet_Done;
|
||||
greetingState = AiWanderStorage::Greet_Done;
|
||||
greetingTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (greetingState == MWMechanics::AiWander::Greet_Done)
|
||||
if (greetingState == AiWanderStorage::Greet_Done)
|
||||
{
|
||||
float resetDist = 2 * helloDistance;
|
||||
if (playerDistSqr >= resetDist*resetDist)
|
||||
greetingState = Greet_None;
|
||||
greetingState = AiWanderStorage::Greet_None;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -676,7 +615,7 @@ namespace MWMechanics
|
|||
storage.mAllowedNodes.push_back(storage.mCurrentNode);
|
||||
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:
|
||||
else
|
||||
|
|
|
@ -21,8 +21,81 @@ namespace ESM
|
|||
}
|
||||
|
||||
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
|
||||
class AiWander : public AiPackage
|
||||
|
@ -52,19 +125,6 @@ namespace MWMechanics
|
|||
|
||||
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:
|
||||
// NOTE: mDistance and mDuration must be set already
|
||||
void init();
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
#include "aicombataction.hpp"
|
||||
#include "movement.hpp"
|
||||
#include "npcstats.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
|
@ -386,6 +387,10 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState
|
|||
{
|
||||
jumpmask = MWRender::Animation::BlendMask_LowerBody;
|
||||
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;
|
||||
if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos)
|
||||
{
|
||||
movementAnimName += weap->shortgroup;
|
||||
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;
|
||||
if(!mAnimation->hasAnimation(movementAnimName))
|
||||
{
|
||||
movemask = MWRender::Animation::BlendMask_LowerBody;
|
||||
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
|
||||
* beginning. */
|
||||
int mode = ((movementAnimName == mCurrentMovement) ? 2 : 1);
|
||||
// If we're playing the same animation, start it from the point it ended
|
||||
bool sameAnim = (movementAnimName == mCurrentMovement);
|
||||
float startPoint = 0.f;
|
||||
if (sameAnim)
|
||||
mAnimation->getInfo(mCurrentMovement, &startPoint);
|
||||
|
||||
mMovementAnimationControlled = true;
|
||||
|
||||
|
@ -526,7 +540,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// Make sure the character was swimming upon death for forward-compatibility
|
||||
const bool wasSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);
|
||||
|
||||
switch (death)
|
||||
{
|
||||
case CharState_SwimDeath:
|
||||
mCurrentDeath = "swimdeath";
|
||||
break;
|
||||
case CharState_SwimDeathKnockDown:
|
||||
mCurrentDeath = "swimdeathknockdown";
|
||||
mCurrentDeath = (wasSwimming ? "swimdeathknockdown" : "deathknockdown");
|
||||
break;
|
||||
case CharState_SwimDeathKnockOut:
|
||||
mCurrentDeath = "swimdeathknockout";
|
||||
mCurrentDeath = (wasSwimming ? "swimdeathknockout" : "deathknockout");
|
||||
break;
|
||||
case CharState_DeathKnockDown:
|
||||
mCurrentDeath = "deathknockdown";
|
||||
|
@ -808,6 +825,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
|||
, mSecondsOfRunning(0)
|
||||
, mTurnAnimationThreshold(0)
|
||||
, mAttackingOrSpell(false)
|
||||
, mCastingManualSpell(false)
|
||||
, mTimeUntilWake(0.f)
|
||||
{
|
||||
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.
|
||||
&& 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)
|
||||
|
@ -1102,7 +1121,9 @@ bool CharacterController::updateCreatureState()
|
|||
if (weapType == WeapType_Spell)
|
||||
{
|
||||
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
|
||||
|
@ -1123,7 +1144,7 @@ bool CharacterController::updateCreatureState()
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
MWMechanics::CastSpell cast(mPtr, NULL);
|
||||
MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell);
|
||||
cast.playSpellCastingEffects(spellid);
|
||||
|
||||
/*
|
||||
|
@ -1138,7 +1159,10 @@ bool CharacterController::updateCreatureState()
|
|||
*/
|
||||
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
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
|
||||
|
@ -1433,7 +1458,7 @@ bool CharacterController::updateWeaponState()
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
MWMechanics::CastSpell cast(mPtr, NULL);
|
||||
MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell);
|
||||
cast.playSpellCastingEffects(spellid);
|
||||
|
||||
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
|
||||
|
@ -1711,16 +1736,18 @@ bool CharacterController::updateWeaponState()
|
|||
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())
|
||||
{
|
||||
mAnimation->disable(mCurrentWeapon);
|
||||
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, true,
|
||||
MWRender::Animation::BlendMask_UpperBody, true,
|
||||
weapSpeed, start, stop, 0.0f, 0);
|
||||
else
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, false,
|
||||
MWRender::Animation::BlendMask_UpperBody, false,
|
||||
weapSpeed, start, stop, 0.0f, 0);
|
||||
}
|
||||
}
|
||||
|
@ -2498,6 +2525,11 @@ bool CharacterController::isAttackPrepairing() const
|
|||
mUpperBodyState == UpperCharState_MinAttackToMaxAttack;
|
||||
}
|
||||
|
||||
bool CharacterController::isCastingSpell() const
|
||||
{
|
||||
return mCastingManualSpell || mUpperBodyState == UpperCharState_CastingSpell;
|
||||
}
|
||||
|
||||
bool CharacterController::isReadyToBlock() const
|
||||
{
|
||||
return updateCarriedLeftVisible(mWeaponType);
|
||||
|
@ -2561,6 +2593,14 @@ void CharacterController::setAttackingOrSpell(bool 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)
|
||||
{
|
||||
mAttackType = attackType;
|
||||
|
|
|
@ -204,6 +204,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
|||
std::string mAttackType; // slash, chop or thrust
|
||||
|
||||
bool mAttackingOrSpell;
|
||||
bool mCastingManualSpell;
|
||||
|
||||
float mTimeUntilWake;
|
||||
|
||||
|
@ -276,6 +277,7 @@ public:
|
|||
void forceStateUpdate();
|
||||
|
||||
bool isAttackPrepairing() const;
|
||||
bool isCastingSpell() const;
|
||||
bool isReadyToBlock() const;
|
||||
bool isKnockedDown() const;
|
||||
bool isKnockedOut() const;
|
||||
|
@ -286,6 +288,7 @@ public:
|
|||
bool isAttackingOrSpell() const;
|
||||
|
||||
void setAttackingOrSpell(bool attackingOrSpell);
|
||||
void castSpell(const std::string spellId, bool manualSpell=false);
|
||||
void setAIAttackType(const std::string& attackType);
|
||||
static void setAttackTypeRandomly(std::string& attackType);
|
||||
|
||||
|
|
|
@ -266,6 +266,12 @@ namespace MWMechanics
|
|||
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)
|
||||
{
|
||||
if(ptr == mWatched)
|
||||
|
@ -1824,6 +1830,11 @@ namespace MWMechanics
|
|||
stats.resurrect();
|
||||
}
|
||||
|
||||
bool MechanicsManager::isCastingSpell(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
return mActors.isCastingSpell(ptr);
|
||||
}
|
||||
|
||||
bool MechanicsManager::isReadyToBlock(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
return mActors.isReadyToBlock(ptr);
|
||||
|
|
|
@ -200,10 +200,14 @@ namespace MWMechanics
|
|||
|
||||
virtual void keepPlayerAlive();
|
||||
|
||||
virtual bool isCastingSpell (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const;
|
||||
/// Is \a ptr casting spell or using weapon now?
|
||||
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
|
||||
/// 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);
|
||||
|
|
|
@ -332,13 +332,14 @@ namespace MWMechanics
|
|||
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)
|
||||
, mTarget(target)
|
||||
, mStack(false)
|
||||
, mHitPosition(0,0,0)
|
||||
, mAlwaysSucceed(false)
|
||||
, mFromProjectile(fromProjectile)
|
||||
, mIsScripted(isScripted)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -961,7 +962,7 @@ namespace MWMechanics
|
|||
|
||||
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);
|
||||
|
||||
|
@ -1025,7 +1026,7 @@ namespace MWMechanics
|
|||
stats.getSpells().usePower(spell);
|
||||
}
|
||||
|
||||
if (mCaster == getPlayer() && spellIncreasesSkill(spell))
|
||||
if (mCaster == getPlayer() && spellIncreasesSkill())
|
||||
mCaster.getClass().skillUsageSucceeded(mCaster,
|
||||
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)
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -88,9 +88,10 @@ namespace MWMechanics
|
|||
osg::Vec3f mHitPosition; // Used for spawning area orb
|
||||
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 mIsScripted; // True if spell is casted from script and ignores some checks (mana level, success chance, etc.)
|
||||
|
||||
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);
|
||||
|
||||
|
@ -108,6 +109,8 @@ namespace MWMechanics
|
|||
|
||||
void playSpellCastingEffects(const std::string &spellid);
|
||||
|
||||
bool spellIncreasesSkill();
|
||||
|
||||
/// Launch a bolt with the given effects.
|
||||
void launchMagicBolt ();
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/actionequip.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
@ -215,12 +216,11 @@ namespace MWScript
|
|||
"to fulfil requirements of Equip instruction" << std::endl;
|
||||
}
|
||||
|
||||
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
MWBase::Environment::get().getWindowManager()->useItem(*it);
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
MWBase::Environment::get().getWindowManager()->useItem(*it, true);
|
||||
else
|
||||
{
|
||||
std::shared_ptr<MWWorld::Action> action = it->getClass().use(*it);
|
||||
// No equip sound for actors other than the player
|
||||
std::shared_ptr<MWWorld::Action> action = it->getClass().use(*it, true);
|
||||
action->execute(ptr, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
|
||||
#include "../mwmechanics/aicast.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
|
@ -1153,15 +1154,31 @@ namespace MWScript
|
|||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
std::string spell = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
std::string spellId = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
std::string targetId = ::Misc::StringUtils::lowerCase(runtime.getStringLiteral (runtime[0].mInteger));
|
||||
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);
|
||||
|
||||
MWMechanics::CastSpell cast(ptr, target);
|
||||
MWMechanics::CastSpell cast(ptr, target, false, true);
|
||||
cast.mHitPosition = target.getRefData().getPosition().asVec3();
|
||||
cast.mAlwaysSucceed = true;
|
||||
cast.cast(spell);
|
||||
|
@ -1179,7 +1196,7 @@ namespace MWScript
|
|||
std::string spell = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
MWMechanics::CastSpell cast(ptr, ptr);
|
||||
MWMechanics::CastSpell cast(ptr, ptr, false, true);
|
||||
cast.mHitPosition = ptr.getRefData().getPosition().asVec3();
|
||||
cast.mAlwaysSucceed = true;
|
||||
cast.cast(spell);
|
||||
|
|
|
@ -9,12 +9,19 @@
|
|||
|
||||
namespace MWWorld
|
||||
{
|
||||
ActionAlchemy::ActionAlchemy(bool force)
|
||||
: Action (false)
|
||||
, mForce(force)
|
||||
{
|
||||
}
|
||||
|
||||
void ActionAlchemy::executeImp (const Ptr& actor)
|
||||
{
|
||||
if (actor != MWMechanics::getPlayer())
|
||||
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}");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,11 @@ namespace MWWorld
|
|||
{
|
||||
class ActionAlchemy : public Action
|
||||
{
|
||||
virtual void executeImp (const Ptr& actor);
|
||||
bool mForce;
|
||||
virtual void executeImp (const Ptr& actor);
|
||||
|
||||
public:
|
||||
ActionAlchemy(bool force=false);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
|
||||
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,18 +25,29 @@ namespace MWWorld
|
|||
MWWorld::Ptr object = getTarget();
|
||||
MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor);
|
||||
|
||||
std::pair <int, std::string> result = object.getClass().canBeEquipped (object, actor);
|
||||
|
||||
// display error message if the player tried to equip something
|
||||
if (!result.second.empty() && actor == MWMechanics::getPlayer())
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(result.second);
|
||||
|
||||
switch(result.first)
|
||||
if (object.getClass().hasItemHealth(object) && object.getCellRef().getCharge() == 0)
|
||||
{
|
||||
case 0:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
if (actor == MWMechanics::getPlayer())
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mForce)
|
||||
{
|
||||
std::pair <int, std::string> result = object.getClass().canBeEquipped (object, actor);
|
||||
|
||||
// display error message if the player tried to equip something
|
||||
if (!result.second.empty() && actor == MWMechanics::getPlayer())
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(result.second);
|
||||
|
||||
switch(result.first)
|
||||
{
|
||||
case 0:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// slots that this item can be equipped in
|
||||
|
|
|
@ -2,17 +2,18 @@
|
|||
#define GAME_MWWORLD_ACTIONEQUIP_H
|
||||
|
||||
#include "action.hpp"
|
||||
#include "ptr.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class ActionEquip : public Action
|
||||
{
|
||||
virtual void executeImp (const Ptr& actor);
|
||||
bool mForce;
|
||||
|
||||
public:
|
||||
/// @param item to equip
|
||||
ActionEquip (const Ptr& object);
|
||||
virtual void executeImp (const Ptr& actor);
|
||||
|
||||
public:
|
||||
/// @param item to equip
|
||||
ActionEquip (const Ptr& object, bool force=false);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
|
||||
namespace MWWorld
|
||||
{
|
||||
ActionRepair::ActionRepair(const Ptr &item)
|
||||
: Action(false, item)
|
||||
ActionRepair::ActionRepair(const Ptr& item, bool force)
|
||||
: Action (false, item)
|
||||
, mForce(force)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -18,7 +19,8 @@ namespace MWWorld
|
|||
if (actor != MWMechanics::getPlayer())
|
||||
return;
|
||||
|
||||
if(MWMechanics::isPlayerInCombat()) {
|
||||
if(!mForce && MWMechanics::isPlayerInCombat())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage2}");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -7,10 +7,13 @@ namespace MWWorld
|
|||
{
|
||||
class ActionRepair : public Action
|
||||
{
|
||||
virtual void executeImp (const Ptr& actor);
|
||||
bool mForce;
|
||||
|
||||
virtual void executeImp (const Ptr& actor);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ namespace MWWorld
|
|||
virtual std::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const;
|
||||
///< 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;
|
||||
///< Generate action for using via inventory menu (default implementation: return a
|
||||
/// null action).
|
||||
|
|
|
@ -1299,7 +1299,10 @@ namespace MWWorld
|
|||
if (isPlayer)
|
||||
{
|
||||
if (!newCell->isExterior())
|
||||
{
|
||||
changeToInteriorCell(Misc::StringUtils::lowerCase(newCell->getCell()->mName), pos, false);
|
||||
removeContainerScripts(getPlayerPtr());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mWorldScene->isCellActive(*newCell))
|
||||
|
@ -1320,7 +1323,8 @@ namespace MWWorld
|
|||
mWorldScene->addObjectToScene(newPtr);
|
||||
|
||||
std::string script = newPtr.getClass().getScript(newPtr);
|
||||
if (!script.empty()) {
|
||||
if (!script.empty())
|
||||
{
|
||||
mLocalScripts.add(script, newPtr);
|
||||
}
|
||||
addContainerScripts(newPtr, newCell);
|
||||
|
@ -3032,13 +3036,13 @@ namespace MWWorld
|
|||
return !fail;
|
||||
}
|
||||
|
||||
void World::castSpell(const Ptr &actor)
|
||||
void World::castSpell(const Ptr &actor, bool manualSpell)
|
||||
{
|
||||
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.
|
||||
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);
|
||||
|
||||
const float fCombatDistance = getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat();
|
||||
|
@ -3056,51 +3060,71 @@ namespace MWWorld
|
|||
|
||||
if (target.isEmpty())
|
||||
{
|
||||
// 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.
|
||||
// For object targets, we want the detailed shapes (rendering raycast).
|
||||
// If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf.
|
||||
std::pair<MWWorld::Ptr,osg::Vec3f> result1 = getHitContact(actor, fCombatDistance, targetActors);
|
||||
|
||||
// Get the target to use for "on touch" effects, using the facing direction from Head node
|
||||
osg::Vec3f origin = getActorHeadTransform(actor).getTrans();
|
||||
|
||||
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))
|
||||
* osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));
|
||||
|
||||
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
||||
float distance = getMaxActivationDistance();
|
||||
osg::Vec3f dest = origin + direction * distance;
|
||||
|
||||
MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);
|
||||
|
||||
float dist1 = std::numeric_limits<float>::max();
|
||||
float dist2 = std::numeric_limits<float>::max();
|
||||
|
||||
if (!result1.first.isEmpty() && result1.first.getClass().isActor())
|
||||
dist1 = (origin - result1.second).length();
|
||||
if (result2.mHit)
|
||||
dist2 = (origin - result2.mHitPointWorld).length();
|
||||
|
||||
if (!result1.first.isEmpty() && result1.first.getClass().isActor())
|
||||
// For scripted spells we should not use hit contact
|
||||
if (manualSpell)
|
||||
{
|
||||
target = result1.first;
|
||||
hitPosition = result1.second;
|
||||
if (dist1 > getMaxActivationDistance())
|
||||
target = NULL;
|
||||
// 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 if (result2.mHit)
|
||||
else
|
||||
{
|
||||
target = result2.mHitObject;
|
||||
hitPosition = result2.mHitPointWorld;
|
||||
if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target))
|
||||
target = NULL;
|
||||
// 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.
|
||||
// For object targets, we want the detailed shapes (rendering raycast).
|
||||
// If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf.
|
||||
std::pair<MWWorld::Ptr,osg::Vec3f> result1 = getHitContact(actor, fCombatDistance, targetActors);
|
||||
|
||||
// Get the target to use for "on touch" effects, using the facing direction from Head node
|
||||
osg::Vec3f origin = getActorHeadTransform(actor).getTrans();
|
||||
|
||||
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))
|
||||
* osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));
|
||||
|
||||
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
||||
float distance = getMaxActivationDistance();
|
||||
osg::Vec3f dest = origin + direction * distance;
|
||||
|
||||
MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);
|
||||
|
||||
float dist1 = std::numeric_limits<float>::max();
|
||||
float dist2 = std::numeric_limits<float>::max();
|
||||
|
||||
if (!result1.first.isEmpty() && result1.first.getClass().isActor())
|
||||
dist1 = (origin - result1.second).length();
|
||||
if (result2.mHit)
|
||||
dist2 = (origin - result2.mHitPointWorld).length();
|
||||
|
||||
if (!result1.first.isEmpty() && result1.first.getClass().isActor())
|
||||
{
|
||||
target = result1.first;
|
||||
hitPosition = result1.second;
|
||||
if (dist1 > getMaxActivationDistance())
|
||||
target = NULL;
|
||||
}
|
||||
else if (result2.mHit)
|
||||
{
|
||||
target = result2.mHitObject;
|
||||
hitPosition = result2.mHitPointWorld;
|
||||
if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target))
|
||||
target = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string selectedSpell = stats.getSpells().getSelectedSpell();
|
||||
|
||||
MWMechanics::CastSpell cast(actor, target);
|
||||
MWMechanics::CastSpell cast(actor, target, false, manualSpell);
|
||||
cast.mHitPosition = hitPosition;
|
||||
|
||||
if (!selectedSpell.empty())
|
||||
|
|
|
@ -696,7 +696,7 @@ namespace MWWorld
|
|||
* @brief Cast the actual spell, should be called mid-animation
|
||||
* @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 launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
|
||||
|
|
|
@ -207,4 +207,4 @@ macro (copy_all_resource_files source_dir destination_dir_base destination_dir_r
|
|||
get_filename_component(filename ${f} NAME)
|
||||
copy_resource_file("${source_dir}/${f}" "${destination_dir_base}" "${destination_dir_relative}/${filename}")
|
||||
endforeach (f)
|
||||
endmacro (copy_all_resource_files)
|
||||
endmacro (copy_all_resource_files)
|
||||
|
|
|
@ -210,6 +210,11 @@ add_component_dir (fallback
|
|||
fallback validate
|
||||
)
|
||||
|
||||
if(NOT WIN32 AND NOT ANDROID)
|
||||
add_component_dir (crashcatcher
|
||||
crashcatcher
|
||||
)
|
||||
endif()
|
||||
|
||||
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
||||
)
|
||||
|
|
|
@ -14,7 +14,11 @@
|
|||
#include <stdbool.h>
|
||||
#include <sys/ptrace.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
#include <SDL_messagebox.h>
|
||||
|
||||
|
@ -402,7 +406,7 @@ static void crash_handler(const char *logfile)
|
|||
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;
|
||||
stack_t altss;
|
||||
|
@ -454,19 +458,31 @@ int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, co
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
// 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)
|
||||
static bool is_debugger_present()
|
||||
{
|
||||
bool rc = false;
|
||||
FILE *fd = fopen("/tmp", "r");
|
||||
|
||||
if (fileno(fd) > 5)
|
||||
bfs::ifstream file((bfs::path("/proc/self/status")));
|
||||
while (!file.eof())
|
||||
{
|
||||
rc = true;
|
||||
std::string word;
|
||||
file >> word;
|
||||
if (word == "TracerPid:")
|
||||
{
|
||||
file >> word;
|
||||
return word != "0";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
return rc;
|
||||
}
|
20
components/crashcatcher/crashcatcher.hpp
Normal file
20
components/crashcatcher/crashcatcher.hpp
Normal file
|
@ -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
|
||||
{
|
||||
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
|
||||
|
||||
|
||||
//Public functions
|
||||
|
||||
Transformation NIFStream::getTrafo()
|
||||
{
|
||||
Transformation t;
|
||||
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;
|
||||
}
|
||||
|
||||
class NIFStream {
|
||||
|
||||
class NIFStream
|
||||
{
|
||||
/// Input stream
|
||||
Files::IStreamPtr inp;
|
||||
|
||||
|
@ -93,71 +93,71 @@ public:
|
|||
|
||||
void skip(size_t size) { inp->ignore(size); }
|
||||
|
||||
char getChar()
|
||||
char getChar()
|
||||
{
|
||||
return readLittleEndianType<char,char>(inp);
|
||||
return readLittleEndianType<char,char>(inp);
|
||||
}
|
||||
short getShort()
|
||||
{
|
||||
|
||||
short getShort()
|
||||
{
|
||||
return readLittleEndianType<short,short>(inp);
|
||||
}
|
||||
unsigned short getUShort()
|
||||
{
|
||||
|
||||
unsigned short getUShort()
|
||||
{
|
||||
return readLittleEndianType<unsigned short,unsigned short>(inp);
|
||||
}
|
||||
int getInt()
|
||||
|
||||
int getInt()
|
||||
{
|
||||
return readLittleEndianType<int,int>(inp);
|
||||
}
|
||||
unsigned int getUInt()
|
||||
{
|
||||
|
||||
unsigned int getUInt()
|
||||
{
|
||||
return readLittleEndianType<unsigned int,unsigned int>(inp);
|
||||
}
|
||||
float getFloat()
|
||||
{
|
||||
|
||||
float getFloat()
|
||||
{
|
||||
return readLittleEndianType<float,uint32_t>(inp);
|
||||
}
|
||||
|
||||
osg::Vec2f getVector2() {
|
||||
osg::Vec2f getVector2()
|
||||
{
|
||||
osg::Vec2f vec;
|
||||
readLittleEndianBufferOfType<2,float,uint32_t>(inp, (float*)&vec._v[0]);
|
||||
return vec;
|
||||
}
|
||||
osg::Vec3f getVector3() {
|
||||
|
||||
osg::Vec3f getVector3()
|
||||
{
|
||||
osg::Vec3f vec;
|
||||
readLittleEndianBufferOfType<3, float,uint32_t>(inp, (float*)&vec._v[0]);
|
||||
return vec;
|
||||
}
|
||||
osg::Vec4f getVector4() {
|
||||
|
||||
osg::Vec4f getVector4()
|
||||
{
|
||||
osg::Vec4f vec;
|
||||
readLittleEndianBufferOfType<4, float,uint32_t>(inp, (float*)&vec._v[0]);
|
||||
return vec;
|
||||
}
|
||||
Matrix3 getMatrix3() {
|
||||
|
||||
Matrix3 getMatrix3()
|
||||
{
|
||||
Matrix3 mat;
|
||||
readLittleEndianBufferOfType<9, float,uint32_t>(inp, (float*)&mat.mValues);
|
||||
return mat;
|
||||
}
|
||||
osg::Quat 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;
|
||||
}
|
||||
Transformation getTrafo() {
|
||||
Transformation t;
|
||||
t.pos = getVector3();
|
||||
t.rotation = getMatrix3();
|
||||
t.scale = getFloat();
|
||||
return t;
|
||||
}
|
||||
|
||||
osg::Quat getQuaternion();
|
||||
|
||||
Transformation getTrafo();
|
||||
|
||||
///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);
|
||||
|
||||
inp->read(&str[0], length);
|
||||
|
@ -165,41 +165,54 @@ public:
|
|||
return &str[0];
|
||||
}
|
||||
///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);
|
||||
return getString(size);
|
||||
}
|
||||
///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::getline(*inp, 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);
|
||||
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);
|
||||
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);
|
||||
/* The packed storage of each Vec2f is 2 floats exactly */
|
||||
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);
|
||||
/* The packed storage of each Vec3f is 3 floats exactly */
|
||||
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);
|
||||
/* The packed storage of each Vec4f is 4 floats exactly */
|
||||
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);
|
||||
for (size_t i = 0;i < quat.size();i++)
|
||||
quat[i] = getQuaternion();
|
||||
|
|
|
@ -34,6 +34,13 @@ btVector3 getbtVector(const osg::Vec3f &v)
|
|||
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
|
||||
|
@ -88,18 +95,10 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::NIFFilePtr&
|
|||
else
|
||||
{
|
||||
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).
|
||||
// assume all nodes in the file will be animated
|
||||
std::string filename = 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;
|
||||
}
|
||||
const bool isAnimated = pathFileNameStartsWithX(nif->getFilename());
|
||||
|
||||
handleNode(node, 0, autogenerated, isAnimated, autogenerated);
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <osgViewer/Viewer>
|
||||
#include <osgViewer/Renderer>
|
||||
|
||||
#include <components/myguiplatform/myguidatamanager.hpp>
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
|
||||
|
@ -19,7 +21,7 @@ StatsHandler::StatsHandler():
|
|||
_statsType(false),
|
||||
_statsWidth(1280.0f),
|
||||
_statsHeight(1024.0f),
|
||||
_font("fonts/arial.ttf"),
|
||||
_font(""),
|
||||
_characterSize(20.0f)
|
||||
{
|
||||
_camera = new osg::Camera;
|
||||
|
@ -28,6 +30,15 @@ StatsHandler::StatsHandler():
|
|||
_camera->setProjectionResizePolicy(osg::Camera::FIXED);
|
||||
|
||||
_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)
|
||||
|
@ -77,7 +88,6 @@ void StatsHandler::setWindowSize(int width, int height)
|
|||
{
|
||||
_camera->setProjectionMatrix(osg::Matrix::ortho2D(0.0,_statsWidth,_statsHeight-height*_statsWidth/width,_statsHeight));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void StatsHandler::toggle(osgViewer::ViewerBase *viewer)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef OPENMW_COMPONENTS_RESOURCE_STATS_H
|
||||
#define OPENMW_COMPONENTS_RESOURCE_STATS_H
|
||||
|
||||
#include <osgGA/GUIEventHandler>
|
||||
#include <osgViewer/ViewerEventHandlers>
|
||||
|
||||
namespace osgViewer
|
||||
{
|
||||
|
@ -15,6 +15,11 @@ namespace osg
|
|||
|
||||
namespace Resource
|
||||
{
|
||||
class Profiler : public osgViewer::StatsHandler
|
||||
{
|
||||
public:
|
||||
Profiler();
|
||||
};
|
||||
|
||||
class StatsHandler : public osgGA::GUIEventHandler
|
||||
{
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
parse_cmake
|
||||
sphinx
|
||||
sphinx>=1.7.0
|
||||
|
|
|
@ -23,7 +23,7 @@ sys.path.insert(0, project_root)
|
|||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# 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
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
|
@ -34,8 +34,11 @@ extensions = [
|
|||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.autosectionlabel',
|
||||
]
|
||||
|
||||
#autosectionlabel_prefix_document = True
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue