Merge remote-tracking branch 'refs/remotes/origin/master' into pathfinding
# Conflicts: # apps/openmw/mwmechanics/aiactivate.cpp # apps/openmw/mwmechanics/aicombat.cpp # apps/openmw/mwmechanics/aicombat.hpp # apps/openmw/mwmechanics/aifollow.cpp # apps/openmw/mwmechanics/aipackage.cpp # apps/openmw/mwmechanics/aipackage.hpp # apps/openmw/mwmechanics/aiwander.cpp # apps/openmw/mwmechanics/aiwander.hpppull/35/head
commit
d2fe6fe857
@ -1,9 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
brew tap openmw/openmw
|
||||
brew update
|
||||
brew unlink boost
|
||||
brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg openmw/openmw/qt unshield
|
||||
brew rm cmake || true
|
||||
brew rm pkgconfig || true
|
||||
brew rm qt5 || true
|
||||
brew install cmake pkgconfig qt5
|
||||
|
||||
curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip
|
||||
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||
|
@ -1,7 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
free -m
|
||||
mkdir build
|
||||
cd build
|
||||
export CODE_COVERAGE=1
|
||||
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi
|
||||
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE
|
||||
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="None" -DUSE_SYSTEM_TINYXML=TRUE
|
||||
|
@ -0,0 +1,671 @@
|
||||
#!/bin/bash
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
ARGSTR=$1
|
||||
shift
|
||||
|
||||
if [ ${ARGSTR:0:1} != "-" ]; then
|
||||
echo "Unknown argument $ARGSTR"
|
||||
echo "Try '$0 -h'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for (( i=1; i<${#ARGSTR}; i++ )); do
|
||||
ARG=${ARGSTR:$i:1}
|
||||
case $ARG in
|
||||
V )
|
||||
VERBOSE=true ;;
|
||||
|
||||
v )
|
||||
VS_VERSION=$1
|
||||
shift ;;
|
||||
|
||||
d )
|
||||
SKIP_DOWNLOAD=true ;;
|
||||
|
||||
e )
|
||||
SKIP_EXTRACT=true ;;
|
||||
|
||||
k )
|
||||
KEEP=true ;;
|
||||
|
||||
u )
|
||||
UNITY_BUILD=true ;;
|
||||
|
||||
p )
|
||||
PLATFORM=$1
|
||||
shift ;;
|
||||
|
||||
c )
|
||||
CONFIGURATION=$1
|
||||
shift ;;
|
||||
|
||||
h )
|
||||
cat <<EOF
|
||||
Usage: $0 [-cdehkpuvV]
|
||||
|
||||
Options:
|
||||
-c <Release/Debug>
|
||||
Set the configuration, can also be set with environment variable CONFIGURATION.
|
||||
-d
|
||||
Skip checking the downloads.
|
||||
-e
|
||||
Skip extracting dependencies.
|
||||
-h
|
||||
Show this message.
|
||||
-k
|
||||
Keep the old build directory, default is to delete it.
|
||||
-p <Win32/Win64>
|
||||
Set the build platform, can also be set with environment variable PLATFORM.
|
||||
-u
|
||||
Configure for unity builds.
|
||||
-v <2013/2015>
|
||||
Choose the Visual Studio version to use.
|
||||
-V
|
||||
Run verbosely
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "Unknown argument $ARG."
|
||||
echo "Try '$0 -h'"
|
||||
exit 1 ;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
STRIP="> /dev/null 2>&1"
|
||||
fi
|
||||
if [ -z $VS_VERSION ]; then
|
||||
VS_VERSION="2013"
|
||||
fi
|
||||
|
||||
if [ -z $APPVEYOR ]; then
|
||||
echo "Running prebuild outside of Appveyor."
|
||||
|
||||
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||
cd $(dirname "$DIR")/..
|
||||
else
|
||||
echo "Running prebuild in Appveyor."
|
||||
|
||||
cd $APPVEYOR_BUILD_FOLDER
|
||||
VERSION="$(cat README.md | grep Version: | awk '{ print $3; }')-$(git rev-parse --short HEAD)"
|
||||
appveyor UpdateBuild -Version "$VERSION" > /dev/null &
|
||||
fi
|
||||
|
||||
run_cmd() {
|
||||
CMD="$1"
|
||||
shift
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
eval $CMD $@ > output.log 2>&1
|
||||
RET=$?
|
||||
|
||||
if [ $RET -ne 0 ]; then
|
||||
if [ -z $APPVEYOR ]; then
|
||||
echo "Command $CMD failed, output can be found in `real_pwd`/output.log"
|
||||
else
|
||||
echo
|
||||
echo "Command $CMD failed;"
|
||||
cat output.log
|
||||
fi
|
||||
else
|
||||
rm output.log
|
||||
fi
|
||||
|
||||
return $RET
|
||||
else
|
||||
eval $CMD $@
|
||||
return $?
|
||||
fi
|
||||
}
|
||||
|
||||
download() {
|
||||
if [ $# -lt 3 ]; then
|
||||
echo "Invalid parameters to download."
|
||||
return 1
|
||||
fi
|
||||
|
||||
NAME=$1
|
||||
shift
|
||||
|
||||
echo "$NAME..."
|
||||
|
||||
while [ $# -gt 1 ]; do
|
||||
URL=$1
|
||||
FILE=$2
|
||||
shift
|
||||
shift
|
||||
|
||||
if ! [ -f $FILE ]; then
|
||||
printf " Downloading $FILE... "
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
curl --silent --retry 10 -kLy 5 -o $FILE $URL
|
||||
RET=$?
|
||||
else
|
||||
curl --retry 10 -kLy 5 -o $FILE $URL
|
||||
RET=$?
|
||||
fi
|
||||
|
||||
if [ $RET -ne 0 ]; then
|
||||
echo "Failed!"
|
||||
else
|
||||
echo "Done."
|
||||
fi
|
||||
else
|
||||
echo " $FILE exists, skipping."
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $# -ne 0 ]; then
|
||||
echo "Missing parameter."
|
||||
fi
|
||||
}
|
||||
|
||||
real_pwd() {
|
||||
pwd | sed "s,/\(.\),\1:,"
|
||||
}
|
||||
|
||||
CMAKE_OPTS=""
|
||||
add_cmake_opts() {
|
||||
CMAKE_OPTS="$CMAKE_OPTS $@"
|
||||
}
|
||||
|
||||
RUNTIME_DLLS=""
|
||||
add_runtime_dlls() {
|
||||
RUNTIME_DLLS="$RUNTIME_DLLS $@"
|
||||
}
|
||||
|
||||
OSG_PLUGINS=""
|
||||
add_osg_dlls() {
|
||||
OSG_PLUGINS="$OSG_PLUGINS $@"
|
||||
}
|
||||
|
||||
if [ -z $PLATFORM ]; then
|
||||
PLATFORM=`uname -m`
|
||||
fi
|
||||
|
||||
if [ -z $CONFIGURATION ]; then
|
||||
CONFIGURATION="Debug"
|
||||
fi
|
||||
|
||||
case $VS_VERSION in
|
||||
14|2015 )
|
||||
GENERATOR="Visual Studio 14 2015"
|
||||
XP_TOOLSET="v140_xp"
|
||||
;;
|
||||
|
||||
# 12|2013|
|
||||
* )
|
||||
GENERATOR="Visual Studio 12 2013"
|
||||
XP_TOOLSET="v120_xp"
|
||||
;;
|
||||
esac
|
||||
|
||||
case $PLATFORM in
|
||||
x64|x86_64|x86-64|win64|Win64 )
|
||||
ARCHNAME=x86-64
|
||||
ARCHSUFFIX=64
|
||||
BITS=64
|
||||
|
||||
BASE_OPTS="-G\"$GENERATOR Win64\""
|
||||
add_cmake_opts "-G\"$GENERATOR Win64\""
|
||||
;;
|
||||
|
||||
x32|x86|i686|i386|win32|Win32 )
|
||||
ARCHNAME=x86
|
||||
ARCHSUFFIX=86
|
||||
BITS=32
|
||||
|
||||
BASE_OPTS="-G\"$GENERATOR\" -T$XP_TOOLSET"
|
||||
add_cmake_opts "-G\"$GENERATOR\"" -T$XP_TOOLSET
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "Unknown platform $PLATFORM."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! [ -z $UNITY_BUILD ]; then
|
||||
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
|
||||
fi
|
||||
|
||||
case $CONFIGURATION in
|
||||
debug|Debug|DEBUG )
|
||||
CONFIGURATION=Debug
|
||||
;;
|
||||
|
||||
release|Release|RELEASE )
|
||||
CONFIGURATION=Release
|
||||
;;
|
||||
|
||||
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
|
||||
CONFIGURATION=RelWithDebInfo
|
||||
;;
|
||||
esac
|
||||
|
||||
echo
|
||||
echo "=========================="
|
||||
echo "Starting prebuild on win$BITS"
|
||||
echo "=========================="
|
||||
echo
|
||||
|
||||
# cd OpenMW/AppVeyor-test
|
||||
mkdir -p deps
|
||||
cd deps
|
||||
|
||||
DEPS="`pwd`"
|
||||
|
||||
if [ -z $SKIP_DOWNLOAD ]; then
|
||||
echo "Downloading dependency packages."
|
||||
echo
|
||||
|
||||
# Boost
|
||||
if [ -z $APPVEYOR ]; then
|
||||
download "Boost 1.58.0" \
|
||||
http://sourceforge.net/projects/boost/files/boost-binaries/1.58.0/boost_1_58_0-msvc-12.0-$BITS.exe \
|
||||
boost-1.58.0-win$BITS.exe
|
||||
fi
|
||||
|
||||
# Bullet
|
||||
download "Bullet 2.83.5" \
|
||||
http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.83.5-win$BITS.7z \
|
||||
Bullet-2.83.5-win$BITS.7z
|
||||
|
||||
# FFmpeg
|
||||
download "FFmpeg 2.5.2" \
|
||||
http://ffmpeg.zeranoe.com/builds/win$BITS/shared/ffmpeg-2.5.2-win$BITS-shared.7z \
|
||||
ffmpeg$BITS-2.5.2.7z \
|
||||
http://ffmpeg.zeranoe.com/builds/win$BITS/dev/ffmpeg-2.5.2-win$BITS-dev.7z \
|
||||
ffmpeg$BITS-2.5.2-dev.7z
|
||||
|
||||
# MyGUI
|
||||
download "MyGUI 3.2.2" \
|
||||
http://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-win$BITS.7z \
|
||||
MyGUI-3.2.2-win$BITS.7z
|
||||
|
||||
# OpenAL
|
||||
download "OpenAL-Soft 1.16.0" \
|
||||
http://kcat.strangesoft.net/openal-binaries/openal-soft-1.16.0-bin.zip \
|
||||
OpenAL-Soft-1.16.0.zip
|
||||
|
||||
# OSG
|
||||
download "OpenSceneGraph 3.3.8" \
|
||||
http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.3.8-win$BITS.7z \
|
||||
OSG-3.3.8-win$BITS.7z
|
||||
|
||||
# Qt
|
||||
if [ -z $APPVEYOR ]; then
|
||||
download "Qt 4.8.6" \
|
||||
http://sourceforge.net/projects/qt64ng/files/qt/$ARCHNAME/4.8.6/msvc2013/qt-4.8.6-x$ARCHSUFFIX-msvc2013.7z \
|
||||
qt$BITS-4.8.6.7z
|
||||
fi
|
||||
|
||||
# SDL2
|
||||
download "SDL 2.0.3" \
|
||||
https://www.libsdl.org/release/SDL2-devel-2.0.3-VC.zip \
|
||||
SDL2-2.0.3.zip
|
||||
fi
|
||||
|
||||
cd .. #/..
|
||||
|
||||
# Set up dependencies
|
||||
if [ -z $KEEP ]; then
|
||||
echo
|
||||
printf "Preparing build directory... "
|
||||
|
||||
rm -rf Build_$BITS
|
||||
mkdir -p Build_$BITS/deps
|
||||
|
||||
echo Done.
|
||||
fi
|
||||
mkdir -p Build_$BITS/deps
|
||||
cd Build_$BITS/deps
|
||||
|
||||
DEPS_INSTALL=`pwd`
|
||||
cd $DEPS
|
||||
|
||||
echo
|
||||
echo "Extracting dependencies..."
|
||||
|
||||
|
||||
# Boost
|
||||
printf "Boost 1.58.0... "
|
||||
{
|
||||
if [ -z $APPVEYOR ]; then
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
BOOST_SDK="`real_pwd`/Boost"
|
||||
|
||||
if [ -d Boost ] && grep "BOOST_VERSION 105800" Boost/boost/version.hpp > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Boost
|
||||
$DEPS/boost-1.58.0-win$BITS.exe //dir="$(echo $BOOST_SDK | sed s,/,\\\\,g)" //verysilent
|
||||
fi
|
||||
|
||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0"
|
||||
|
||||
echo Done.
|
||||
else
|
||||
# Appveyor unstable has all the boost we need already
|
||||
BOOST_SDK="c:/Libraries/boost"
|
||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0"
|
||||
|
||||
echo AppVeyor.
|
||||
fi
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# Bullet
|
||||
printf "Bullet 2.83.5... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d Bullet ]; then
|
||||
printf "Exists. (No version checking) "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Bullet
|
||||
eval 7z x -y $DEPS/Bullet-2.83.5-win$BITS.7z $STRIP
|
||||
mv Bullet-2.83.5-win$BITS Bullet
|
||||
fi
|
||||
|
||||
export BULLET_ROOT="`real_pwd`/Bullet"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# FFmpeg
|
||||
printf "FFmpeg 2.5.2... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d FFmpeg ] && grep "FFmpeg version: 2.5.2" FFmpeg/README.txt > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf FFmpeg
|
||||
|
||||
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2.7z $STRIP
|
||||
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2-dev.7z $STRIP
|
||||
|
||||
mv ffmpeg-2.5.2-win$BITS-shared FFmpeg
|
||||
cp -r ffmpeg-2.5.2-win$BITS-dev/* FFmpeg/
|
||||
rm -rf ffmpeg-2.5.2-win$BITS-dev
|
||||
fi
|
||||
|
||||
export FFMPEG_HOME="`real_pwd`/FFmpeg"
|
||||
add_runtime_dlls `pwd`/FFmpeg/bin/{avcodec-56,avformat-56,avutil-54,swresample-1,swscale-3}.dll
|
||||
|
||||
if [ $BITS -eq 32 ]; then
|
||||
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
|
||||
fi
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# 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 && \
|
||||
grep "MYGUI_VERSION_PATCH 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null
|
||||
then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf MyGUI
|
||||
eval 7z x -y $DEPS/MyGUI-3.2.2-win$BITS.7z $STRIP
|
||||
mv MyGUI-3.2.2-win$BITS MyGUI
|
||||
fi
|
||||
|
||||
MYGUI_SDK="`real_pwd`/MyGUI"
|
||||
|
||||
add_cmake_opts -DMYGUISDK="$MYGUI_SDK" \
|
||||
-DMYGUI_INCLUDE_DIRS="$MYGUI_SDK/include/MYGUI" \
|
||||
-DMYGUI_PREQUEST_FILE="$MYGUI_SDK/include/MYGUI/MyGUI_Prerequest.h"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="_d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls `pwd`/MyGUI/bin/$CONFIGURATION/MyGUIEngine$SUFFIX.dll
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# OpenAL
|
||||
printf "OpenAL-Soft 1.16.0... "
|
||||
{
|
||||
if [ -d openal-soft-1.16.0-bin ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf openal-soft-1.16.0-bin
|
||||
eval 7z x -y OpenAL-Soft-1.16.0.zip $STRIP
|
||||
fi
|
||||
|
||||
OPENAL_SDK="`real_pwd`/openal-soft-1.16.0-bin"
|
||||
|
||||
add_cmake_opts -DOPENAL_INCLUDE_DIR="$OPENAL_SDK/include/AL" \
|
||||
-DOPENAL_LIBRARY="$OPENAL_SDK/libs/Win$BITS/OpenAL32.lib"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# OSG
|
||||
printf "OSG 3.3.8... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d OSG ] && \
|
||||
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
||||
grep "OPENSCENEGRAPH_MINOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
||||
grep "OPENSCENEGRAPH_PATCH_VERSION 8" OSG/include/osg/Version > /dev/null
|
||||
then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf OSG
|
||||
eval 7z x -y $DEPS/OSG-3.3.8-win$BITS.7z $STRIP
|
||||
mv OSG-3.3.8-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}$SUFFIX.dll \
|
||||
`pwd`/OSG/bin/osg{,Animation,DB,FX,GA,Particle,Qt,Text,Util,Viewer}$SUFFIX.dll
|
||||
|
||||
add_osg_dlls `pwd`/OSG/bin/osgPlugins-3.3.8/osgdb_{bmp,dds,gif,jpeg,png,tga}$SUFFIX.dll
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# Qt
|
||||
if [ -z $APPVEYOR ]; then
|
||||
printf "Qt 4.8.6... "
|
||||
else
|
||||
printf "Qt 5.4... "
|
||||
fi
|
||||
{
|
||||
if [ -z $APPVEYOR ]; then
|
||||
cd $DEPS_INSTALL
|
||||
QT_SDK="`real_pwd`/Qt"
|
||||
|
||||
if [ -d Qt ] && head -n2 Qt/BUILDINFO.txt | grep "4.8.6" > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Qt
|
||||
eval 7z x -y $DEPS/qt$BITS-4.8.6.7z $STRIP
|
||||
mv qt-4.8.6-* Qt
|
||||
cd Qt
|
||||
eval ./qtbinpatcher.exe $STRIP
|
||||
fi
|
||||
|
||||
cd $QT_SDK
|
||||
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=4 \
|
||||
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d4"
|
||||
else
|
||||
SUFFIX="4"
|
||||
fi
|
||||
|
||||
add_runtime_dlls `pwd`/bin/Qt{Core,Gui,Network,OpenGL}$SUFFIX.dll
|
||||
|
||||
echo Done.
|
||||
else
|
||||
if [ $BITS -eq 32 ]; then
|
||||
QT_SDK="C:/Qt/5.4/msvc2013_opengl"
|
||||
else
|
||||
QT_SDK="C:/Qt/5.4/msvc2013_64_opengl"
|
||||
fi
|
||||
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
|
||||
echo AppVeyor.
|
||||
fi
|
||||
}
|
||||
cd $DEPS
|
||||
|
||||
# SDL2
|
||||
printf "SDL 2.0.3... "
|
||||
{
|
||||
if [ -d SDL2-2.0.3 ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf SDL2-2.0.3
|
||||
eval 7z x -y SDL2-2.0.3.zip $STRIP
|
||||
fi
|
||||
|
||||
SDL_SDK="`real_pwd`/SDL2-2.0.3"
|
||||
add_cmake_opts -DSDL2_INCLUDE_DIR="$SDL_SDK/include" \
|
||||
-DSDL2MAIN_LIBRARY="$SDL_SDK/lib/x$ARCHSUFFIX/SDL2main.lib" \
|
||||
-DSDL2_LIBRARY_PATH="$SDL_SDK/lib/x$ARCHSUFFIX/SDL2.lib"
|
||||
|
||||
add_runtime_dlls `pwd`/SDL2-2.0.3/lib/x$ARCHSUFFIX/SDL2.dll
|
||||
|
||||
echo Done.
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
echo " (Outside of CI, doing full build.)"
|
||||
else
|
||||
case $STEP in
|
||||
components )
|
||||
echo " Subproject: Components."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
-DBUILD_LAUNCHER=no \
|
||||
-DBUILD_MWINIIMPORTER=no \
|
||||
-DBUILD_OPENCS=no \
|
||||
-DBUILD_OPENMW=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
openmw )
|
||||
echo " Subproject: OpenMW."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
-DBUILD_LAUNCHER=no \
|
||||
-DBUILD_MWINIIMPORTER=no \
|
||||
-DBUILD_OPENCS=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
opencs )
|
||||
echo " Subproject: OpenCS."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
-DBUILD_LAUNCHER=no \
|
||||
-DBUILD_MWINIIMPORTER=no \
|
||||
-DBUILD_OPENMW=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
misc )
|
||||
echo " Subproject: Misc."
|
||||
add_cmake_opts -DBUILD_OPENCS=no \
|
||||
-DBUILD_OPENMW=no
|
||||
;;
|
||||
* )
|
||||
echo " Building everything."
|
||||
;;
|
||||
esac
|
||||
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.
|
||||
else echo Failed.; fi
|
||||
fi
|
||||
|
||||
echo
|
||||
|
||||
# NOTE: Disable this when/if we want to run test cases
|
||||
if [ -z $CI ]; then
|
||||
echo "Copying Runtime DLLs..."
|
||||
mkdir -p $CONFIGURATION
|
||||
for DLL in $RUNTIME_DLLS; do
|
||||
echo " `basename $DLL`."
|
||||
cp "$DLL" $CONFIGURATION/
|
||||
done
|
||||
echo "OSG Plugin DLLs..."
|
||||
mkdir -p $CONFIGURATION/osgPlugins-3.3.8
|
||||
for DLL in $OSG_PLUGINS; do
|
||||
echo " `basename $DLL`."
|
||||
cp "$DLL" $CONFIGURATION/osgPlugins-3.3.8
|
||||
done
|
||||
echo
|
||||
|
||||
echo "Copying Runtime Resources/Config Files"
|
||||
|
||||
echo " gamecontrollerdb.txt"
|
||||
cp $CONFIGURATION/../gamecontrollerdb.txt $CONFIGURATION/gamecontrollerdb.txt
|
||||
echo " openmw.cfg"
|
||||
cp $CONFIGURATION/../openmw.cfg.install $CONFIGURATION/openmw.cfg
|
||||
echo " openmw-cs.cfg"
|
||||
cp $CONFIGURATION/../openmw-cs.cfg $CONFIGURATION/openmw-cs.cfg
|
||||
echo " settings-default.cfg"
|
||||
cp $CONFIGURATION/../settings-default.cfg $CONFIGURATION/settings-default.cfg
|
||||
echo " resources/"
|
||||
cp -r $CONFIGURATION/../resources $CONFIGURATION/resources
|
||||
fi
|
||||
|
||||
exit $RET
|
@ -1,5 +1,25 @@
|
||||
#!/bin/sh
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||
QT_PATH="/usr/local/opt/qt5"
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_FRAMEWORK_PATH="/usr/local/lib/macosx/Release" -DCMAKE_EXE_LINKER_FLAGS="-F/usr/local/lib/macosx/Release" -DCMAKE_CXX_FLAGS="-stdlib=libstdc++" -DCMAKE_BUILD_TYPE=Debug -DBUILD_MYGUI_PLUGIN=OFF -G"Unix Makefiles" ..
|
||||
|
||||
cmake \
|
||||
-D PKG_CONFIG_USE_CMAKE_PREFIX_PATH=ON \
|
||||
-D CMAKE_EXE_LINKER_FLAGS="-lz" \
|
||||
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
|
||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \
|
||||
-D CMAKE_OSX_SYSROOT="macosx10.11" \
|
||||
-D CMAKE_BUILD_TYPE=Debug \
|
||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||
-D DESIRED_QT_VERSION=5 \
|
||||
-D OSG_PLUGIN_LIB_SEARCH_PATH="$DEPENDENCIES_ROOT/lib/osgPlugins-3.4.0" \
|
||||
-D BUILD_ESMTOOL=FALSE \
|
||||
-D BUILD_MYGUI_PLUGIN=FALSE \
|
||||
-G"Unix Makefiles" \
|
||||
..
|
||||
|
@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z $PLATFORM ]; then
|
||||
PLATFORM=`uname -m`
|
||||
fi
|
||||
|
||||
if [ -z $CONFIGURATION ]; then
|
||||
CONFIGURATION="Debug"
|
||||
fi
|
||||
|
||||
case $PLATFORM in
|
||||
x32|x86|i686|i386|win32|Win32 )
|
||||
BITS=32
|
||||
PLATFORM=Win32
|
||||
;;
|
||||
|
||||
x64|x86_64|x86-64|win64|Win64 )
|
||||
BITS=64
|
||||
PLATFORM=x64
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "Unknown platform $PLATFORM."
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
if [ -z $APPVEYOR ]; then
|
||||
echo "Running $BITS-bit $CONFIGURATION build outside of Appveyor."
|
||||
|
||||
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||
cd $(dirname "$DIR")/..
|
||||
else
|
||||
echo "Running $BITS-bit $CONFIGURATION build in Appveyor."
|
||||
|
||||
cd $APPVEYOR_BUILD_FOLDER
|
||||
fi
|
||||
|
||||
cd build_$BITS
|
||||
|
||||
which msbuild > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
msbuild() {
|
||||
/c/Program\ Files\ \(x86\)/MSBuild/12.0/Bin/MSBuild.exe "$@"
|
||||
}
|
||||
fi
|
||||
|
||||
if [ -z $APPVEYOR ]; then
|
||||
msbuild OpenMW.sln //t:Build //m:8
|
||||
else
|
||||
msbuild OpenMW.sln //t:Build //m:8 //logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
fi
|
||||
|
||||
RET=$?
|
||||
if [ $RET -eq 0 ] && [ ! -z $PACKAGE ]; then
|
||||
msbuild PACKAGE.vcxproj //t:Build //m:8
|
||||
RET=$?
|
||||
fi
|
||||
|
||||
exit $RET
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,123 @@
|
||||
#include "gmstcheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "../world/defaultgmsts.hpp"
|
||||
|
||||
CSMTools::GmstCheckStage::GmstCheckStage(const CSMWorld::IdCollection<ESM::GameSetting>& gameSettings)
|
||||
: mGameSettings(gameSettings)
|
||||
{}
|
||||
|
||||
int CSMTools::GmstCheckStage::setup()
|
||||
{
|
||||
return mGameSettings.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::GmstCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
||||
{
|
||||
const CSMWorld::Record<ESM::GameSetting>& record = mGameSettings.getRecord (stage);
|
||||
|
||||
if (record.isDeleted())
|
||||
return;
|
||||
|
||||
const ESM::GameSetting& gmst = record.get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Gmst, gmst.mId);
|
||||
|
||||
// Test for empty string
|
||||
if (gmst.mValue.getType() == ESM::VT_String && gmst.mValue.getString().empty())
|
||||
messages.add(id, gmst.mId + " is an empty string", "", CSMDoc::Message::Severity_Warning);
|
||||
|
||||
// Checking type and limits
|
||||
// optimization - compare it to lists based on naming convention (f-float,i-int,s-string)
|
||||
if (gmst.mId[0] == 'f')
|
||||
{
|
||||
for (size_t i = 0; i < CSMWorld::DefaultGmsts::FloatCount; ++i)
|
||||
{
|
||||
if (gmst.mId == CSMWorld::DefaultGmsts::Floats[i])
|
||||
{
|
||||
if (gmst.mValue.getType() != ESM::VT_Float)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Expected float type for " << gmst.mId << " but found "
|
||||
<< varTypeToString(gmst.mValue.getType()) << " type";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
if (gmst.mValue.getFloat() < CSMWorld::DefaultGmsts::FloatLimits[i*2])
|
||||
messages.add(id, gmst.mId + " is less than the suggested range", "",
|
||||
CSMDoc::Message::Severity_Warning);
|
||||
|
||||
if (gmst.mValue.getFloat() > CSMWorld::DefaultGmsts::FloatLimits[i*2+1])
|
||||
messages.add(id, gmst.mId + " is more than the suggested range", "",
|
||||
CSMDoc::Message::Severity_Warning);
|
||||
|
||||
break; // for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (gmst.mId[0] == 'i')
|
||||
{
|
||||
for (size_t i = 0; i < CSMWorld::DefaultGmsts::IntCount; ++i)
|
||||
{
|
||||
if (gmst.mId == CSMWorld::DefaultGmsts::Ints[i])
|
||||
{
|
||||
if (gmst.mValue.getType() != ESM::VT_Int)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Expected int type for " << gmst.mId << " but found "
|
||||
<< varTypeToString(gmst.mValue.getType()) << " type";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
if (gmst.mValue.getInteger() < CSMWorld::DefaultGmsts::IntLimits[i*2])
|
||||
messages.add(id, gmst.mId + " is less than the suggested range", "",
|
||||
CSMDoc::Message::Severity_Warning);
|
||||
|
||||
if (gmst.mValue.getInteger() > CSMWorld::DefaultGmsts::IntLimits[i*2+1])
|
||||
messages.add(id, gmst.mId + " is more than the suggested range", "",
|
||||
CSMDoc::Message::Severity_Warning);
|
||||
|
||||
break; // for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (gmst.mId[0] == 's')
|
||||
{
|
||||
for (size_t i = 0; i < CSMWorld::DefaultGmsts::StringCount; ++i)
|
||||
{
|
||||
if (gmst.mId == CSMWorld::DefaultGmsts::Strings[i])
|
||||
{
|
||||
ESM::VarType type = gmst.mValue.getType();
|
||||
|
||||
if (type != ESM::VT_String && type != ESM::VT_None)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Expected string or none type for " << gmst.mId << " but found "
|
||||
<< varTypeToString(gmst.mValue.getType()) << " type";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
break; // for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string CSMTools::GmstCheckStage::varTypeToString(ESM::VarType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ESM::VT_Unknown: return "unknown";
|
||||
case ESM::VT_None: return "none";
|
||||
case ESM::VT_Short: return "short";
|
||||
case ESM::VT_Int: return "int";
|
||||
case ESM::VT_Long: return "long";
|
||||
case ESM::VT_Float: return "float";
|
||||
case ESM::VT_String: return "string";
|
||||
default: return "unhandled";
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#ifndef CSM_TOOLS_GMSTCHECK_H
|
||||
#define CSM_TOOLS_GMSTCHECK_H
|
||||
|
||||
#include <components/esm/loadgmst.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that GMSTs are alright
|
||||
class GmstCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
public:
|
||||
|
||||
GmstCheckStage(const CSMWorld::IdCollection<ESM::GameSetting>& gameSettings);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform(int stage, CSMDoc::Messages& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages
|
||||
|
||||
private:
|
||||
|
||||
const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;
|
||||
|
||||
std::string varTypeToString(ESM::VarType);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,79 @@
|
||||
#include "journalcheck.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
CSMTools::JournalCheckStage::JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue> &journals,
|
||||
const CSMWorld::InfoCollection& journalInfos)
|
||||
: mJournals(journals), mJournalInfos(journalInfos)
|
||||
{}
|
||||
|
||||
int CSMTools::JournalCheckStage::setup()
|
||||
{
|
||||
return mJournals.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
||||
{
|
||||
const CSMWorld::Record<ESM::Dialogue> &journalRecord = mJournals.getRecord(stage);
|
||||
|
||||
if (journalRecord.isDeleted())
|
||||
return;
|
||||
|
||||
const ESM::Dialogue &journal = journalRecord.get();
|
||||
int statusNamedCount = 0;
|
||||
int totalInfoCount = 0;
|
||||
std::set<int> questIndices;
|
||||
|
||||
CSMWorld::InfoCollection::Range range = mJournalInfos.getTopicRange(journal.mId);
|
||||
|
||||
for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it)
|
||||
{
|
||||
const CSMWorld::Record<CSMWorld::Info> infoRecord = (*it);
|
||||
|
||||
if (infoRecord.isDeleted())
|
||||
continue;
|
||||
|
||||
const CSMWorld::Info& journalInfo = infoRecord.get();
|
||||
|
||||
totalInfoCount += 1;
|
||||
|
||||
if (journalInfo.mQuestStatus == ESM::DialInfo::QS_Name)
|
||||
{
|
||||
statusNamedCount += 1;
|
||||
}
|
||||
|
||||
if (journalInfo.mResponse.empty())
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
||||
|
||||
messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning);
|
||||
}
|
||||
|
||||
std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex);
|
||||
|
||||
// Duplicate index
|
||||
if (result.second == false)
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex;
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
||||
if (totalInfoCount == 0)
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
|
||||
|
||||
messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning);
|
||||
}
|
||||
else if (statusNamedCount > 1)
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
|
||||
|
||||
messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
#ifndef CSM_TOOLS_JOURNALCHECK_H
|
||||
#define CSM_TOOLS_JOURNALCHECK_H
|
||||
|
||||
#include <components/esm/loaddial.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
#include "../world/infocollection.hpp"
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that journal infos are good
|
||||
class JournalCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
public:
|
||||
|
||||
JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue>& journals,
|
||||
const CSMWorld::InfoCollection& journalInfos);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform(int stage, CSMDoc::Messages& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages
|
||||
|
||||
private:
|
||||
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
|
||||
const CSMWorld::InfoCollection& mJournalInfos;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,441 @@
|
||||
#include "topicinfocheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "../world/infoselectwrapper.hpp"
|
||||
|
||||
CSMTools::TopicInfoCheckStage::TopicInfoCheckStage(
|
||||
const CSMWorld::InfoCollection& topicInfos,
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
|
||||
const CSMWorld::IdCollection<ESM::Class>& classes,
|
||||
const CSMWorld::IdCollection<ESM::Faction>& factions,
|
||||
const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,
|
||||
const CSMWorld::IdCollection<ESM::Global>& globals,
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& journals,
|
||||
const CSMWorld::IdCollection<ESM::Race>& races,
|
||||
const CSMWorld::IdCollection<ESM::Region>& regions,
|
||||
const CSMWorld::IdCollection<ESM::Dialogue> &topics,
|
||||
const CSMWorld::RefIdData& referencables,
|
||||
const CSMWorld::Resources& soundFiles)
|
||||
: mTopicInfos(topicInfos),
|
||||
mCells(cells),
|
||||
mClasses(classes),
|
||||
mFactions(factions),
|
||||
mGameSettings(gmsts),
|
||||
mGlobals(globals),
|
||||
mJournals(journals),
|
||||
mRaces(races),
|
||||
mRegions(regions),
|
||||
mTopics(topics),
|
||||
mReferencables(referencables),
|
||||
mSoundFiles(soundFiles)
|
||||
{}
|
||||
|
||||
int CSMTools::TopicInfoCheckStage::setup()
|
||||
{
|
||||
// Generate list of cell names for reference checking
|
||||
|
||||
mCellNames.clear();
|
||||
for (int i = 0; i < mCells.getSize(); ++i)
|
||||
{
|
||||
const CSMWorld::Record<CSMWorld::Cell>& cellRecord = mCells.getRecord(i);
|
||||
|
||||
if (cellRecord.isDeleted())
|
||||
continue;
|
||||
|
||||
mCellNames.insert(cellRecord.get().mName);
|
||||
}
|
||||
// Cell names can also include region names
|
||||
for (int i = 0; i < mRegions.getSize(); ++i)
|
||||
{
|
||||
const CSMWorld::Record<ESM::Region>& regionRecord = mRegions.getRecord(i);
|
||||
|
||||
if (regionRecord.isDeleted())
|
||||
continue;
|
||||
|
||||
mCellNames.insert(regionRecord.get().mName);
|
||||
}
|
||||
// Default cell name
|
||||
int index = mGameSettings.searchId("sDefaultCellname");
|
||||
if (index != -1)
|
||||
{
|
||||
const CSMWorld::Record<ESM::GameSetting>& gmstRecord = mGameSettings.getRecord(index);
|
||||
|
||||
if (!gmstRecord.isDeleted() && gmstRecord.get().mValue.getType() == ESM::VT_String)
|
||||
{
|
||||
mCellNames.insert(gmstRecord.get().mValue.getString());
|
||||
}
|
||||
}
|
||||
|
||||
return mTopicInfos.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
||||
{
|
||||
const CSMWorld::Record<CSMWorld::Info>& infoRecord = mTopicInfos.getRecord(stage);
|
||||
|
||||
if (infoRecord.isDeleted())
|
||||
return;
|
||||
|
||||
const CSMWorld::Info& topicInfo = infoRecord.get();
|
||||
|
||||
// There should always be a topic that matches
|
||||
int topicIndex = mTopics.searchId(topicInfo.mTopicId);
|
||||
|
||||
const CSMWorld::Record<ESM::Dialogue>& topicRecord = mTopics.getRecord(topicIndex);
|
||||
|
||||
if (topicRecord.isDeleted())
|
||||
return;
|
||||
|
||||
const ESM::Dialogue& topic = topicRecord.get();
|
||||
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_TopicInfo, topicInfo.mId);
|
||||
|
||||
// Check fields
|
||||
|
||||
if (!topicInfo.mActor.empty())
|
||||
{
|
||||
verifyActor(topicInfo.mActor, id, messages);
|
||||
}
|
||||
|
||||
if (!topicInfo.mClass.empty())
|
||||
{
|
||||
verifyId(topicInfo.mClass, mClasses, id, messages);
|
||||
}
|
||||
|
||||
if (!topicInfo.mCell.empty())
|
||||
{
|
||||
verifyCell(topicInfo.mCell, id, messages);
|
||||
}
|
||||
|
||||
if (!topicInfo.mFaction.empty())
|
||||
{
|
||||
if (verifyId(topicInfo.mFaction, mFactions, id, messages))
|
||||
{
|
||||
verifyFactionRank(topicInfo.mFaction, topicInfo.mData.mRank, id, messages);
|
||||
}
|
||||
}
|
||||
|
||||
if (!topicInfo.mPcFaction.empty())
|
||||
{
|
||||
if (verifyId(topicInfo.mPcFaction, mFactions, id, messages))
|
||||
{
|
||||
verifyFactionRank(topicInfo.mPcFaction, topicInfo.mData.mPCrank, id, messages);
|
||||
}
|
||||
}
|
||||
|
||||
if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
if (!topicInfo.mRace.empty())
|
||||
{
|
||||
verifyId(topicInfo.mRace, mRaces, id, messages);
|
||||
}
|
||||
|
||||
if (!topicInfo.mSound.empty())
|
||||
{
|
||||
verifySound(topicInfo.mSound, id, messages);
|
||||
}
|
||||
|
||||
if (topicInfo.mResponse.empty() && topic.mType != ESM::Dialogue::Voice)
|
||||
{
|
||||
messages.add(id, "Response is empty", "", CSMDoc::Message::Severity_Warning);
|
||||
}
|
||||
|
||||
// Check info conditions
|
||||
|
||||
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator it = topicInfo.mSelects.begin();
|
||||
it != topicInfo.mSelects.end(); ++it)
|
||||
{
|
||||
verifySelectStruct((*it), id, messages);
|
||||
}
|
||||
}
|
||||
|
||||
// Verification functions
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Actor";
|
||||
|
||||
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor);
|
||||
|
||||
if (index.first == -1)
|
||||
{
|
||||
writeMissingIdError(specifier, actor, id, messages);
|
||||
return false;
|
||||
}
|
||||
else if (mReferencables.getRecord(index).isDeleted())
|
||||
{
|
||||
writeDeletedRecordError(specifier, actor, id, messages);
|
||||
return false;
|
||||
}
|
||||
else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature)
|
||||
{
|
||||
writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Cell";
|
||||
|
||||
if (mCellNames.find(cell) == mCellNames.end())
|
||||
{
|
||||
writeMissingIdError(specifier, cell, id, messages);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& factionName, int rank, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
if (rank < -1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = mFactions.searchId(factionName);
|
||||
|
||||
const ESM::Faction &faction = mFactions.getRecord(index).get();
|
||||
|
||||
int limit = 0;
|
||||
for (; limit < 10; ++limit)
|
||||
{
|
||||
if (faction.mRanks[limit].empty())
|
||||
break;
|
||||
}
|
||||
|
||||
if (rank >= limit)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1
|
||||
<< " for the " << factionName << " faction";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Item";
|
||||
|
||||
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item);
|
||||
|
||||
if (index.first == -1)
|
||||
{
|
||||
writeMissingIdError(specifier, item, id, messages);
|
||||
return false;
|
||||
}
|
||||
else if (mReferencables.getRecord(index).isDeleted())
|
||||
{
|
||||
writeDeletedRecordError(specifier, item, id, messages);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (index.second)
|
||||
{
|
||||
case CSMWorld::UniversalId::Type_Potion:
|
||||
case CSMWorld::UniversalId::Type_Apparatus:
|
||||
case CSMWorld::UniversalId::Type_Armor:
|
||||
case CSMWorld::UniversalId::Type_Book:
|
||||
case CSMWorld::UniversalId::Type_Clothing:
|
||||
case CSMWorld::UniversalId::Type_Ingredient:
|
||||
case CSMWorld::UniversalId::Type_Light:
|
||||
case CSMWorld::UniversalId::Type_Lockpick:
|
||||
case CSMWorld::UniversalId::Type_Miscellaneous:
|
||||
case CSMWorld::UniversalId::Type_Probe:
|
||||
case CSMWorld::UniversalId::Type_Repair:
|
||||
case CSMWorld::UniversalId::Type_Weapon:
|
||||
case CSMWorld::UniversalId::Type_ItemLevelledList:
|
||||
break;
|
||||
|
||||
default:
|
||||
writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::SelectStruct& select,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||
{
|
||||
CSMWorld::ConstInfoSelectWrapper infoCondition(select);
|
||||
|
||||
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None)
|
||||
{
|
||||
messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else if (!infoCondition.variantTypeIsValid())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of ";
|
||||
|
||||
switch (select.mValue.getType())
|
||||
{
|
||||
case ESM::VT_None: stream << "None"; break;
|
||||
case ESM::VT_Short: stream << "Short"; break;
|
||||
case ESM::VT_Int: stream << "Int"; break;
|
||||
case ESM::VT_Long: stream << "Long"; break;
|
||||
case ESM::VT_Float: stream << "Float"; break;
|
||||
case ESM::VT_String: stream << "String"; break;
|
||||
default: stream << "Unknown"; break;
|
||||
}
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.conditionIsAlwaysTrue())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Info Condition: " << infoCondition.toString() << " is always true";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.conditionIsNeverTrue())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Info Condition: " << infoCondition.toString() << " is never true";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Id checks
|
||||
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Global &&
|
||||
!verifyId(infoCondition.getVariableName(), mGlobals, id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Journal &&
|
||||
!verifyId(infoCondition.getVariableName(), mJournals, id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Item &&
|
||||
!verifyItem(infoCondition.getVariableName(), id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Dead &&
|
||||
!verifyActor(infoCondition.getVariableName(), id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotId &&
|
||||
!verifyActor(infoCondition.getVariableName(), id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotFaction &&
|
||||
!verifyId(infoCondition.getVariableName(), mFactions, id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotClass &&
|
||||
!verifyId(infoCondition.getVariableName(), mClasses, id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotRace &&
|
||||
!verifyId(infoCondition.getVariableName(), mRaces, id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotCell &&
|
||||
!verifyCell(infoCondition.getVariableName(), id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Sound File";
|
||||
|
||||
if (mSoundFiles.searchId(sound) == -1)
|
||||
{
|
||||
writeMissingIdError(specifier, sound, id, messages);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||
{
|
||||
int index = collection.searchId(name);
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
writeMissingIdError(T::getRecordType(), name, id, messages);
|
||||
return false;
|
||||
}
|
||||
else if (collection.getRecord(index).isDeleted())
|
||||
{
|
||||
writeDeletedRecordError(T::getRecordType(), name, id, messages);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Error functions
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << specifier << ": ID or name \"" << missingId << "\" could not be found";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
|
||||
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
CSMWorld::UniversalId tempId(invalidType, invalidId);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \""
|
||||
<< invalidId << "\" (can be of type " << expectedType << ")";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
#ifndef CSM_TOOLS_TOPICINFOCHECK_HPP
|
||||
#define CSM_TOOLS_TOPICINFOCHECK_HPP
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <components/esm/loadclas.hpp>
|
||||
#include <components/esm/loaddial.hpp>
|
||||
#include <components/esm/loadfact.hpp>
|
||||
#include <components/esm/loadglob.hpp>
|
||||
#include <components/esm/loadgmst.hpp>
|
||||
#include <components/esm/loadrace.hpp>
|
||||
#include <components/esm/loadregn.hpp>
|
||||
|
||||
#include "../world/cell.hpp"
|
||||
#include "../world/idcollection.hpp"
|
||||
#include "../world/infocollection.hpp"
|
||||
#include "../world/refiddata.hpp"
|
||||
#include "../world/resources.hpp"
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: check topics
|
||||
class TopicInfoCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
public:
|
||||
|
||||
TopicInfoCheckStage(
|
||||
const CSMWorld::InfoCollection& topicInfos,
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
|
||||
const CSMWorld::IdCollection<ESM::Class>& classes,
|
||||
const CSMWorld::IdCollection<ESM::Faction>& factions,
|
||||
const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,
|
||||
const CSMWorld::IdCollection<ESM::Global>& globals,
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& journals,
|
||||
const CSMWorld::IdCollection<ESM::Race>& races,
|
||||
const CSMWorld::IdCollection<ESM::Region>& regions,
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& topics,
|
||||
const CSMWorld::RefIdData& referencables,
|
||||
const CSMWorld::Resources& soundFiles);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform(int step, CSMDoc::Messages& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages
|
||||
|
||||
private:
|
||||
|
||||
const CSMWorld::InfoCollection& mTopicInfos;
|
||||
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
|
||||
const CSMWorld::IdCollection<ESM::Class>& mClasses;
|
||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||
const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;
|
||||
const CSMWorld::IdCollection<ESM::Global>& mGlobals;
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
|
||||
const CSMWorld::IdCollection<ESM::Race>& mRaces;
|
||||
const CSMWorld::IdCollection<ESM::Region>& mRegions;
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;
|
||||
|
||||
const CSMWorld::RefIdData& mReferencables;
|
||||
const CSMWorld::Resources& mSoundFiles;
|
||||
|
||||
std::set<std::string> mCellNames;
|
||||
|
||||
// These return false when not successful and write an error
|
||||
bool verifyActor(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
bool verifyCell(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
bool verifyFactionRank(const std::string& name, int rank, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages);
|
||||
bool verifyItem(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
bool verifySelectStruct(const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages);
|
||||
bool verifySound(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
template <typename T>
|
||||
bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
// Common error messages
|
||||
void writeMissingIdError(const std::string& specifier, const std::string& missingId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
void writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
|
||||
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,26 @@
|
||||
|
||||
#include "commandmacro.hpp"
|
||||
|
||||
#include <QUndoStack>
|
||||
#include <QUndoCommand>
|
||||
|
||||
CSMWorld::CommandMacro::CommandMacro (QUndoStack& undoStack, const QString& description)
|
||||
: mUndoStack (undoStack), mDescription (description), mStarted (false)
|
||||
{}
|
||||
|
||||
CSMWorld::CommandMacro::~CommandMacro()
|
||||
{
|
||||
if (mStarted)
|
||||
mUndoStack.endMacro();
|
||||
}
|
||||
|
||||
void CSMWorld::CommandMacro::push (QUndoCommand *command)
|
||||
{
|
||||
if (!mStarted)
|
||||
{
|
||||
mUndoStack.beginMacro (mDescription.isEmpty() ? command->text() : mDescription);
|
||||
mStarted = true;
|
||||
}
|
||||
|
||||
mUndoStack.push (command);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#ifndef CSM_WOLRD_COMMANDMACRO_H
|
||||
#define CSM_WOLRD_COMMANDMACRO_H
|
||||
|
||||
class QUndoStack;
|
||||
class QUndoCommand;
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class CommandMacro
|
||||
{
|
||||
QUndoStack& mUndoStack;
|
||||
QString mDescription;
|
||||
bool mStarted;
|
||||
|
||||
/// not implemented
|
||||
CommandMacro (const CommandMacro&);
|
||||
|
||||
/// not implemented
|
||||
CommandMacro& operator= (const CommandMacro&);
|
||||
|
||||
public:
|
||||
|
||||
/// If \a description is empty, the description of the first command is used.
|
||||
CommandMacro (QUndoStack& undoStack, const QString& description = "");
|
||||
|
||||
~CommandMacro();
|
||||
|
||||
void push (QUndoCommand *command);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,34 @@
|
||||
#ifndef CSM_WORLD_DEFAULTGMSTS_H
|
||||
#define CSM_WORLD_DEFAULTGMSTS_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace CSMWorld {
|
||||
namespace DefaultGmsts {
|
||||
|
||||
const size_t FloatCount = 258;
|
||||
const size_t IntCount = 89;
|
||||
const size_t StringCount = 1174;
|
||||
|
||||
const size_t OptionalFloatCount = 42;
|
||||
const size_t OptionalIntCount = 4;
|
||||
const size_t OptionalStringCount = 26;
|
||||
|
||||
extern const char* Floats[];
|
||||
extern const char * Ints[];
|
||||
extern const char * Strings[];
|
||||
|
||||
extern const char * OptionalFloats[];
|
||||
extern const char * OptionalInts[];
|
||||
extern const char * OptionalStrings[];
|
||||
|
||||
extern const float FloatsDefaultValues[];
|
||||
extern const int IntsDefaultValues[];
|
||||
|
||||
extern const float FloatLimits[];
|
||||
extern const int IntLimits[];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,893 @@
|
||||
#include "infoselectwrapper.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
const size_t CSMWorld::ConstInfoSelectWrapper::RuleMinSize = 5;
|
||||
|
||||
const size_t CSMWorld::ConstInfoSelectWrapper::FunctionPrefixOffset = 1;
|
||||
const size_t CSMWorld::ConstInfoSelectWrapper::FunctionIndexOffset = 2;
|
||||
const size_t CSMWorld::ConstInfoSelectWrapper::RelationIndexOffset = 4;
|
||||
const size_t CSMWorld::ConstInfoSelectWrapper::VarNameOffset = 5;
|
||||
|
||||
const char* CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings[] =
|
||||
{
|
||||
"Rank Low",
|
||||
"Rank High",
|
||||
"Rank Requirement",
|
||||
"Reputation",
|
||||
"Health Percent",
|
||||
"PC Reputation",
|
||||
"PC Level",
|
||||
"PC Health Percent",
|
||||
"PC Magicka",
|
||||
"PC Fatigue",
|
||||
"PC Strength",
|
||||
"PC Block",
|
||||
"PC Armorer",
|
||||
"PC Medium Armor",
|
||||
"PC Heavy Armor",
|
||||
"PC Blunt Weapon",
|
||||
"PC Long Blade",
|
||||
"PC Axe",
|
||||
"PC Spear",
|
||||
"PC Athletics",
|
||||
"PC Enchant",
|
||||
"PC Detruction",
|
||||
"PC Alteration",
|
||||
"PC Illusion",
|
||||
"PC Conjuration",
|
||||
"PC Mysticism",
|
||||
"PC Restoration",
|
||||
"PC Alchemy",
|
||||
"PC Unarmored",
|
||||
"PC Security",
|
||||
"PC Sneak",
|
||||
"PC Acrobatics",
|
||||
"PC Light Armor",
|
||||
"PC Short Blade",
|
||||
"PC Marksman",
|
||||
"PC Merchantile",
|
||||
"PC Speechcraft",
|
||||
"PC Hand to Hand",
|
||||
"PC Sex",
|
||||
"PC Expelled",
|
||||
"PC Common Disease",
|
||||
"PC Blight Disease",
|
||||
"PC Clothing Modifier",
|
||||
"PC Crime Level",
|
||||
"Same Sex",
|
||||
"Same Race",
|
||||
"Same Faction",
|
||||
"Faction Rank Difference",
|
||||
"Detected",
|
||||
"Alarmed",
|
||||
"Choice",
|
||||
"PC Intelligence",
|
||||
"PC Willpower",
|
||||
"PC Agility",
|
||||
"PC Speed",
|
||||
"PC Endurance",
|
||||
"PC Personality",
|
||||
"PC Luck",
|
||||
"PC Corpus",
|
||||
"Weather",
|
||||
"PC Vampire",
|
||||
"Level",
|
||||
"Attacked",
|
||||
"Talked to PC",
|
||||
"PC Health",
|
||||
"Creature Target",
|
||||
"Friend Hit",
|
||||
"Fight",
|
||||
"Hello",
|
||||
"Alarm",
|
||||
"Flee",
|
||||
"Should Attack",
|
||||
"Werewolf",
|
||||
"PC Werewolf Kills",
|
||||
"Global",
|
||||
"Local",
|
||||
"Journal",
|
||||
"Item",
|
||||
"Dead",
|
||||
"Not Id",
|
||||
"Not Faction",
|
||||
"Not Class",
|
||||
"Not Race",
|
||||
"Not Cell",
|
||||
"Not Local",
|
||||
0
|
||||
};
|
||||
|
||||
const char* CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings[] =
|
||||
{
|
||||
"=",
|
||||
"!=",
|
||||
">",
|
||||
">=",
|
||||
"<",
|
||||
"<=",
|
||||
0
|
||||
};
|
||||
|
||||
const char* CSMWorld::ConstInfoSelectWrapper::ComparisonEnumStrings[] =
|
||||
{
|
||||
"Boolean",
|
||||
"Integer",
|
||||
"Numeric",
|
||||
0
|
||||
};
|
||||
|
||||
// static functions
|
||||
|
||||
std::string CSMWorld::ConstInfoSelectWrapper::convertToString(FunctionName name)
|
||||
{
|
||||
if (name < Function_None)
|
||||
return FunctionEnumStrings[name];
|
||||
else
|
||||
return "(Invalid Data: Function)";
|
||||
}
|
||||
|
||||
std::string CSMWorld::ConstInfoSelectWrapper::convertToString(RelationType type)
|
||||
{
|
||||
if (type < Relation_None)
|
||||
return RelationEnumStrings[type];
|
||||
else
|
||||
return "(Invalid Data: Relation)";
|
||||
}
|
||||
|
||||
std::string CSMWorld::ConstInfoSelectWrapper::convertToString(ComparisonType type)
|
||||
{
|
||||
if (type < Comparison_None)
|
||||
return ComparisonEnumStrings[type];
|
||||
else
|
||||
return "(Invalid Data: Comparison)";
|
||||
}
|
||||
|
||||
// ConstInfoSelectWrapper
|
||||
|
||||
CSMWorld::ConstInfoSelectWrapper::ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select)
|
||||
: mConstSelect(select)
|
||||
{
|
||||
readRule();
|
||||
}
|
||||
|
||||
CSMWorld::ConstInfoSelectWrapper::FunctionName CSMWorld::ConstInfoSelectWrapper::getFunctionName() const
|
||||
{
|
||||
return mFunctionName;
|
||||
}
|
||||
|
||||
CSMWorld::ConstInfoSelectWrapper::RelationType CSMWorld::ConstInfoSelectWrapper::getRelationType() const
|
||||
{
|
||||
return mRelationType;
|
||||
}
|
||||
|
||||
CSMWorld::ConstInfoSelectWrapper::ComparisonType CSMWorld::ConstInfoSelectWrapper::getComparisonType() const
|
||||
{
|
||||
return mComparisonType;
|
||||
}
|
||||
|
||||
bool CSMWorld::ConstInfoSelectWrapper::hasVariable() const
|
||||
{
|
||||
return mHasVariable;
|
||||
}
|
||||
|
||||
const std::string& CSMWorld::ConstInfoSelectWrapper::getVariableName() const
|
||||
{
|
||||
return mVariableName;
|
||||
}
|
||||
|
||||
bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue() const
|
||||
{
|
||||
if (!variantTypeIsValid())
|
||||
return false;
|
||||
|
||||
if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer)
|
||||
{
|
||||
if (mConstSelect.mValue.getType() == ESM::VT_Float)
|
||||
return conditionIsAlwaysTrue(getConditionFloatRange(), getValidIntRange());
|
||||
else
|
||||
return conditionIsAlwaysTrue(getConditionIntRange(), getValidIntRange());
|
||||
}
|
||||
else if (mComparisonType == Comparison_Numeric)
|
||||
{
|
||||
if (mConstSelect.mValue.getType() == ESM::VT_Float)
|
||||
return conditionIsAlwaysTrue(getConditionFloatRange(), getValidFloatRange());
|
||||
else
|
||||
return conditionIsAlwaysTrue(getConditionIntRange(), getValidFloatRange());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue() const
|
||||
{
|
||||
if (!variantTypeIsValid())
|
||||
return false;
|
||||
|
||||
if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer)
|
||||
{
|
||||
if (mConstSelect.mValue.getType() == ESM::VT_Float)
|
||||
return conditionIsNeverTrue(getConditionFloatRange(), getValidIntRange());
|
||||
else
|
||||
return conditionIsNeverTrue(getConditionIntRange(), getValidIntRange());
|
||||
}
|
||||
else if (mComparisonType == Comparison_Numeric)
|
||||
{
|
||||
if (mConstSelect.mValue.getType() == ESM::VT_Float)
|
||||
return conditionIsNeverTrue(getConditionFloatRange(), getValidFloatRange());
|
||||
else
|
||||
return conditionIsNeverTrue(getConditionIntRange(), getValidFloatRange());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CSMWorld::ConstInfoSelectWrapper::variantTypeIsValid() const
|
||||
{
|
||||
return (mConstSelect.mValue.getType() == ESM::VT_Int || mConstSelect.mValue.getType() == ESM::VT_Float);
|
||||
}
|
||||
|
||||
const ESM::Variant& CSMWorld::ConstInfoSelectWrapper::getVariant() const
|
||||
{
|
||||
return mConstSelect.mValue;
|
||||
}
|
||||
|
||||
std::string CSMWorld::ConstInfoSelectWrapper::toString() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << convertToString(mFunctionName) << " ";
|
||||
|
||||
if (mHasVariable)
|
||||
stream << mVariableName << " ";
|
||||
|
||||
stream << convertToString(mRelationType) << " ";
|
||||
|
||||
switch (mConstSelect.mValue.getType())
|
||||
{
|
||||
case ESM::VT_Int:
|
||||
stream << mConstSelect.mValue.getInteger();
|
||||
break;
|
||||
|
||||
case ESM::VT_Float:
|
||||
stream << mConstSelect.mValue.getFloat();
|
||||
break;
|
||||
|
||||
default:
|
||||
stream << "(Invalid value type)";
|
||||
break;
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
void CSMWorld::ConstInfoSelectWrapper::readRule()
|
||||
{
|
||||
if (mConstSelect.mSelectRule.size() < RuleMinSize)
|
||||
throw std::runtime_error("InfoSelectWrapper: rule is to small");
|
||||
|
||||
readFunctionName();
|
||||
readRelationType();
|
||||
readVariableName();
|
||||
updateHasVariable();
|
||||
updateComparisonType();
|
||||
}
|
||||
|
||||
void CSMWorld::ConstInfoSelectWrapper::readFunctionName()
|
||||
{
|
||||
char functionPrefix = mConstSelect.mSelectRule[FunctionPrefixOffset];
|
||||
std::string functionIndex = mConstSelect.mSelectRule.substr(FunctionIndexOffset, 2);
|
||||
int convertedIndex = -1;
|
||||
|
||||
// Read in function index, form ## from 00 .. 73, skip leading zero
|
||||
if (functionIndex[0] == '0')
|
||||
functionIndex = functionIndex[1];
|
||||
|
||||
std::stringstream stream;
|
||||
stream << functionIndex;
|
||||
stream >> convertedIndex;
|
||||
|
||||
switch (functionPrefix)
|
||||
{
|
||||
case '1':
|
||||
if (convertedIndex >= 0 && convertedIndex <= 73)
|
||||
mFunctionName = static_cast<FunctionName>(convertedIndex);
|
||||
else
|
||||
mFunctionName = Function_None;
|
||||
break;
|
||||
|
||||
case '2': mFunctionName = Function_Global; break;
|
||||
case '3': mFunctionName = Function_Local; break;
|
||||
case '4': mFunctionName = Function_Journal; break;
|
||||
case '5': mFunctionName = Function_Item; break;
|
||||
case '6': mFunctionName = Function_Dead; break;
|
||||
case '7': mFunctionName = Function_NotId; break;
|
||||
case '8': mFunctionName = Function_NotFaction; break;
|
||||
case '9': mFunctionName = Function_NotClass; break;
|
||||
case 'A': mFunctionName = Function_NotRace; break;
|
||||
case 'B': mFunctionName = Function_NotCell; break;
|
||||
case 'C': mFunctionName = Function_NotLocal; break;
|
||||
default: mFunctionName = Function_None; break;
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::ConstInfoSelectWrapper::readRelationType()
|
||||
{
|
||||
char relationIndex = mConstSelect.mSelectRule[RelationIndexOffset];
|
||||
|
||||
switch (relationIndex)
|
||||
{
|
||||
case '0': mRelationType = Relation_Equal; break;
|
||||
case '1': mRelationType = Relation_NotEqual; break;
|
||||
case '2': mRelationType = Relation_Greater; break;
|
||||
case '3': mRelationType = Relation_GreaterOrEqual; break;
|
||||
case '4': mRelationType = Relation_Less; break;
|
||||
case '5': mRelationType = Relation_LessOrEqual; break;
|
||||
default: mRelationType = Relation_None;
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::ConstInfoSelectWrapper::readVariableName()
|
||||
{
|
||||
if (mConstSelect.mSelectRule.size() >= VarNameOffset)
|
||||
mVariableName = mConstSelect.mSelectRule.substr(VarNameOffset);
|
||||
else
|
||||
mVariableName.clear();
|
||||
}
|
||||
|
||||
void CSMWorld::ConstInfoSelectWrapper::updateHasVariable()
|
||||
{
|
||||
switch (mFunctionName)
|
||||
{
|
||||
case Function_Global:
|
||||
case Function_Local:
|
||||
case Function_Journal:
|
||||
case Function_Item:
|
||||
case Function_Dead:
|
||||
case Function_NotId:
|
||||
case Function_NotFaction:
|
||||
case Function_NotClass:
|
||||
case Function_NotRace:
|
||||
case Function_NotCell:
|
||||
case Function_NotLocal:
|
||||
mHasVariable = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
mHasVariable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::ConstInfoSelectWrapper::updateComparisonType()
|
||||
{
|
||||
switch (mFunctionName)
|
||||
{
|
||||
// Boolean
|
||||
case Function_NotId:
|
||||
case Function_NotFaction:
|
||||
case Function_NotClass:
|
||||
case Function_NotRace:
|
||||
case Function_NotCell:
|
||||
case Function_NotLocal:
|
||||
case Function_PcExpelled:
|
||||
case Function_PcCommonDisease:
|
||||
case Function_PcBlightDisease:
|
||||
case Function_SameSex:
|
||||
case Function_SameRace:
|
||||
case Function_SameFaction:
|
||||
case Function_Detected:
|
||||
case Function_Alarmed:
|
||||
case Function_PcCorpus:
|
||||
case Function_PcVampire:
|
||||
case Function_Attacked:
|
||||
case Function_TalkedToPc:
|
||||
case Function_ShouldAttack:
|
||||
case Function_Werewolf:
|
||||
mComparisonType = Comparison_Boolean;
|
||||
break;
|
||||
|
||||
// Integer
|
||||
case Function_Journal:
|
||||
case Function_Item:
|
||||
case Function_Dead:
|
||||
case Function_RankLow:
|
||||
case Function_RankHigh:
|
||||
case Function_RankRequirement:
|
||||
case Function_Reputation:
|
||||
case Function_PcReputation:
|
||||
case Function_PcLevel:
|
||||
case Function_PcStrength:
|
||||
case Function_PcBlock:
|
||||
case Function_PcArmorer:
|
||||
case Function_PcMediumArmor:
|
||||
case Function_PcHeavyArmor:
|
||||
case Function_PcBluntWeapon:
|
||||
case Function_PcLongBlade:
|
||||
case Function_PcAxe:
|
||||
case Function_PcSpear:
|
||||
case Function_PcAthletics:
|
||||
case Function_PcEnchant:
|
||||
case Function_PcDestruction:
|
||||
case Function_PcAlteration:
|
||||
case Function_PcIllusion:
|
||||
case Function_PcConjuration:
|
||||
case Function_PcMysticism:
|
||||
case Function_PcRestoration:
|
||||
case Function_PcAlchemy:
|
||||
case Function_PcUnarmored:
|
||||
case Function_PcSecurity:
|
||||
case Function_PcSneak:
|
||||
case Function_PcAcrobatics:
|
||||
case Function_PcLightArmor:
|
||||
case Function_PcShortBlade:
|
||||
case Function_PcMarksman:
|
||||
case Function_PcMerchantile:
|
||||
case Function_PcSpeechcraft:
|
||||
case Function_PcHandToHand:
|
||||
case Function_PcGender:
|
||||
case Function_PcClothingModifier:
|
||||
case Function_PcCrimeLevel:
|
||||
case Function_FactionRankDifference:
|
||||
case Function_Choice:
|
||||
case Function_PcIntelligence:
|
||||
case Function_PcWillpower:
|
||||
case Function_PcAgility:
|
||||
case Function_PcSpeed:
|
||||
case Function_PcEndurance:
|
||||
case Function_PcPersonality:
|
||||
case Function_PcLuck:
|
||||
case Function_Weather:
|
||||
case Function_Level:
|
||||
case Function_CreatureTarget:
|
||||
case Function_FriendHit:
|
||||
case Function_Fight:
|
||||
case Function_Hello:
|
||||
case Function_Alarm:
|
||||
case Function_Flee:
|
||||
case Function_PcWerewolfKills:
|
||||
mComparisonType = Comparison_Integer;
|
||||
break;
|
||||
|
||||
// Numeric
|
||||
case Function_Global:
|
||||
case Function_Local:
|
||||
|
||||
case Function_Health_Percent:
|
||||
case Function_PcHealthPercent:
|
||||
case Function_PcMagicka:
|
||||
case Function_PcFatigue:
|
||||
case Function_PcHealth:
|
||||
mComparisonType = Comparison_Numeric;
|
||||
break;
|
||||
|
||||
default:
|
||||
mComparisonType = Comparison_None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<int, int> CSMWorld::ConstInfoSelectWrapper::getConditionIntRange() const
|
||||
{
|
||||
const int IntMax = std::numeric_limits<int>::max();
|
||||
const int IntMin = std::numeric_limits<int>::min();
|
||||
const std::pair<int, int> InvalidRange(IntMax, IntMin);
|
||||
|
||||
int value = mConstSelect.mValue.getInteger();
|
||||
|
||||
switch (mRelationType)
|
||||
{
|
||||
case Relation_Equal:
|
||||
case Relation_NotEqual:
|
||||
return std::pair<int, int>(value, value);
|
||||
|
||||
case Relation_Greater:
|
||||
if (value == IntMax)
|
||||
{
|
||||
return InvalidRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::pair<int, int>(value + 1, IntMax);
|
||||
}
|
||||
break;
|
||||
|
||||
case Relation_GreaterOrEqual:
|
||||
return std::pair<int, int>(value, IntMax);
|
||||
|
||||
case Relation_Less:
|
||||
if (value == IntMin)
|
||||
{
|
||||
return InvalidRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::pair<int, int>(IntMin, value - 1);
|
||||
}
|
||||
|
||||
case Relation_LessOrEqual:
|
||||
return std::pair<int, int>(IntMin, value);
|
||||
|
||||
default:
|
||||
throw std::logic_error("InfoSelectWrapper: relation does not have a range");
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getConditionFloatRange() const
|
||||
{
|
||||
const float FloatMax = std::numeric_limits<float>::infinity();
|
||||
const float FloatMin = -std::numeric_limits<float>::infinity();
|
||||
const float Epsilon = std::numeric_limits<float>::epsilon();
|
||||
const std::pair<float, float> InvalidRange(FloatMax, FloatMin);
|
||||
|
||||
float value = mConstSelect.mValue.getFloat();
|
||||
|
||||
switch (mRelationType)
|
||||
{
|
||||
case Relation_Equal:
|
||||
case Relation_NotEqual:
|
||||
return std::pair<float, float>(value, value);
|
||||
|
||||
case Relation_Greater:
|
||||
return std::pair<float, float>(value + Epsilon, FloatMax);
|
||||
|
||||
case Relation_GreaterOrEqual:
|
||||
return std::pair<float, float>(value, FloatMax);
|
||||
|
||||
case Relation_Less:
|
||||
return std::pair<float, float>(FloatMin, value - Epsilon);
|
||||
|
||||
case Relation_LessOrEqual:
|
||||
return std::pair<float, float>(FloatMin, value);
|
||||
|
||||
default:
|
||||
throw std::logic_error("InfoSelectWrapper: given relation does not have a range");
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<int, int> CSMWorld::ConstInfoSelectWrapper::getValidIntRange() const
|
||||
{
|
||||
const int IntMax = std::numeric_limits<int>::max();
|
||||
const int IntMin = std::numeric_limits<int>::min();
|
||||
|
||||
switch (mFunctionName)
|
||||
{
|
||||
// Boolean
|
||||
case Function_NotId:
|
||||
case Function_NotFaction:
|
||||
case Function_NotClass:
|
||||
case Function_NotRace:
|
||||
case Function_NotCell:
|
||||
case Function_NotLocal:
|
||||
case Function_PcExpelled:
|
||||
case Function_PcCommonDisease:
|
||||
case Function_PcBlightDisease:
|
||||
case Function_SameSex:
|
||||
case Function_SameRace:
|
||||
case Function_SameFaction:
|
||||
case Function_Detected:
|
||||
case Function_Alarmed:
|
||||
case Function_PcCorpus:
|
||||
case Function_PcVampire:
|
||||
case Function_Attacked:
|
||||
case Function_TalkedToPc:
|
||||
case Function_ShouldAttack:
|
||||
case Function_Werewolf:
|
||||
return std::pair<int, int>(0, 1);
|
||||
|
||||
// Integer
|
||||
case Function_RankLow:
|
||||
case Function_RankHigh:
|
||||
case Function_Reputation:
|
||||
case Function_PcReputation:
|
||||
case Function_Journal:
|
||||
return std::pair<int, int>(IntMin, IntMax);
|
||||
|
||||
case Function_Item:
|
||||
case Function_Dead:
|
||||
case Function_PcLevel:
|
||||
case Function_PcStrength:
|
||||
case Function_PcBlock:
|
||||
case Function_PcArmorer:
|
||||
case Function_PcMediumArmor:
|
||||
case Function_PcHeavyArmor:
|
||||
case Function_PcBluntWeapon:
|
||||
case Function_PcLongBlade:
|
||||
case Function_PcAxe:
|
||||
case Function_PcSpear:
|
||||
case Function_PcAthletics:
|
||||
case Function_PcEnchant:
|
||||
case Function_PcDestruction:
|
||||
case Function_PcAlteration:
|
||||
case Function_PcIllusion:
|
||||
case Function_PcConjuration:
|
||||
case Function_PcMysticism:
|
||||
case Function_PcRestoration:
|
||||
case Function_PcAlchemy:
|
||||
case Function_PcUnarmored:
|
||||
case Function_PcSecurity:
|
||||
case Function_PcSneak:
|
||||
case Function_PcAcrobatics:
|
||||
case Function_PcLightArmor:
|
||||
case Function_PcShortBlade:
|
||||
case Function_PcMarksman:
|
||||
case Function_PcMerchantile:
|
||||
case Function_PcSpeechcraft:
|
||||
case Function_PcHandToHand:
|
||||
case Function_PcClothingModifier:
|
||||
case Function_PcCrimeLevel:
|
||||
case Function_Choice:
|
||||
case Function_PcIntelligence:
|
||||
case Function_PcWillpower:
|
||||
case Function_PcAgility:
|
||||
case Function_PcSpeed:
|
||||
case Function_PcEndurance:
|
||||
case Function_PcPersonality:
|
||||
case Function_PcLuck:
|
||||
case Function_Level:
|
||||
case Function_PcWerewolfKills:
|
||||
return std::pair<int, int>(0, IntMax);
|
||||
|
||||
case Function_Fight:
|
||||
case Function_Hello:
|
||||
case Function_Alarm:
|
||||
case Function_Flee:
|
||||
return std::pair<int, int>(0, 100);
|
||||
|
||||
case Function_Weather:
|
||||
return std::pair<int, int>(0, 9);
|
||||
|
||||
case Function_FriendHit:
|
||||
return std::pair<int, int>(0, 4);
|
||||
|
||||
case Function_RankRequirement:
|
||||
return std::pair<int, int>(0, 3);
|
||||
|
||||
case Function_CreatureTarget:
|
||||
return std::pair<int, int>(0, 2);
|
||||
|
||||
case Function_PcGender:
|
||||
return std::pair<int, int>(0, 1);
|
||||
|
||||
case Function_FactionRankDifference:
|
||||
return std::pair<int, int>(-9, 9);
|
||||
|
||||
// Numeric
|
||||
case Function_Global:
|
||||
case Function_Local:
|
||||
return std::pair<int, int>(IntMin, IntMax);
|
||||
|
||||
case Function_PcMagicka:
|
||||
case Function_PcFatigue:
|
||||
case Function_PcHealth:
|
||||
return std::pair<int, int>(0, IntMax);
|
||||
|
||||
case Function_Health_Percent:
|
||||
case Function_PcHealthPercent:
|
||||
return std::pair<int, int>(0, 100);
|
||||
|
||||
default:
|
||||
throw std::runtime_error("InfoSelectWrapper: function does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getValidFloatRange() const
|
||||
{
|
||||
const float FloatMax = std::numeric_limits<float>::infinity();
|
||||
const float FloatMin = -std::numeric_limits<float>::infinity();
|
||||
|
||||
switch (mFunctionName)
|
||||
{
|
||||
// Numeric
|
||||
case Function_Global:
|
||||
case Function_Local:
|
||||
case Function_NotLocal:
|
||||
return std::pair<float, float>(FloatMin, FloatMax);
|
||||
|
||||
case Function_PcMagicka:
|
||||
case Function_PcFatigue:
|
||||
case Function_PcHealth:
|
||||
return std::pair<float, float>(0, FloatMax);
|
||||
|
||||
case Function_Health_Percent:
|
||||
case Function_PcHealthPercent:
|
||||
return std::pair<float, float>(0, 100);
|
||||
|
||||
default:
|
||||
throw std::runtime_error("InfoSelectWrapper: function does not exist or is not numeric");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool CSMWorld::ConstInfoSelectWrapper::rangeContains(T1 value, std::pair<T2,T2> range) const
|
||||
{
|
||||
return (value >= range.first && value <= range.second);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool CSMWorld::ConstInfoSelectWrapper::rangeFullyContains(std::pair<T1,T1> containingRange,
|
||||
std::pair<T2,T2> testRange) const
|
||||
{
|
||||
return (containingRange.first <= testRange.first) && (testRange.second <= containingRange.second);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool CSMWorld::ConstInfoSelectWrapper::rangesOverlap(std::pair<T1,T1> range1, std::pair<T2,T2> range2) const
|
||||
{
|
||||
// One of the bounds of either range should fall within the other range
|
||||
return
|
||||
(range1.first <= range2.first && range2.first <= range1.second) ||
|
||||
(range1.first <= range2.second && range2.second <= range1.second) ||
|
||||
(range2.first <= range1.first && range1.first <= range2.second) ||
|
||||
(range2.first <= range1.second && range1.second <= range2.second);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool CSMWorld::ConstInfoSelectWrapper::rangesMatch(std::pair<T1,T1> range1, std::pair<T2,T2> range2) const
|
||||
{
|
||||
return (range1.first == range2.first && range1.second == range2.second);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue(std::pair<T1,T1> conditionRange,
|
||||
std::pair<T2,T2> validRange) const
|
||||
{
|
||||
switch (mRelationType)
|
||||
{
|
||||
case Relation_Equal:
|
||||
return false;
|
||||
|
||||
case Relation_NotEqual:
|
||||
// If value is not within range, it will always be true
|
||||
return !rangeContains(conditionRange.first, validRange);
|
||||
|
||||
case Relation_Greater:
|
||||
case Relation_GreaterOrEqual:
|
||||
case Relation_Less:
|
||||
case Relation_LessOrEqual:
|
||||
// If the valid range is completely within the condition range, it will always be true
|
||||
return rangeFullyContains(conditionRange, validRange);
|
||||
|
||||
default:
|
||||
throw std::logic_error("InfoCondition: operator can not be used to compare");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue(std::pair<T1,T1> conditionRange,
|
||||
std::pair<T2,T2> validRange) const
|
||||
{
|
||||
switch (mRelationType)
|
||||
{
|
||||
case Relation_Equal:
|
||||
return !rangeContains(conditionRange.first, validRange);
|
||||
|
||||
case Relation_NotEqual:
|
||||
return false;
|
||||
|
||||
case Relation_Greater:
|
||||
case Relation_GreaterOrEqual:
|
||||
case Relation_Less:
|
||||
case Relation_LessOrEqual:
|
||||
// If ranges do not overlap, it will never be true
|
||||
return !rangesOverlap(conditionRange, validRange);
|
||||
|
||||
default:
|
||||
throw std::logic_error("InfoCondition: operator can not be used to compare");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// InfoSelectWrapper
|
||||
|
||||
CSMWorld::InfoSelectWrapper::InfoSelectWrapper(ESM::DialInfo::SelectStruct& select)
|
||||
: CSMWorld::ConstInfoSelectWrapper(select), mSelect(select)
|
||||
{
|
||||
}
|
||||
|
||||
void CSMWorld::InfoSelectWrapper::setFunctionName(FunctionName name)
|
||||
{
|
||||
mFunctionName = name;
|
||||
updateHasVariable();
|
||||
updateComparisonType();
|
||||
}
|
||||
|
||||
void CSMWorld::InfoSelectWrapper::setRelationType(RelationType type)
|
||||
{
|
||||
mRelationType = type;
|
||||
}
|
||||
|
||||
void CSMWorld::InfoSelectWrapper::setVariableName(const std::string& name)
|
||||
{
|
||||
mVariableName = name;
|
||||
}
|
||||
|
||||
void CSMWorld::InfoSelectWrapper::setDefaults()
|
||||
{
|
||||
if (!variantTypeIsValid())
|
||||
mSelect.mValue.setType(ESM::VT_Int);
|
||||
|
||||
switch (mComparisonType)
|
||||
{
|
||||
case Comparison_Boolean:
|
||||
setRelationType(Relation_Equal);
|
||||
mSelect.mValue.setInteger(1);
|
||||
break;
|
||||
|
||||
case Comparison_Integer:
|
||||
case Comparison_Numeric:
|
||||
setRelationType(Relation_Greater);
|
||||
mSelect.mValue.setInteger(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void CSMWorld::InfoSelectWrapper::update()
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
// Leading 0
|
||||
stream << '0';
|
||||
|
||||
// Write Function
|
||||
|
||||
bool writeIndex = false;
|
||||
size_t functionIndex = static_cast<size_t>(mFunctionName);
|
||||
|
||||
switch (mFunctionName)
|
||||
{
|
||||
case Function_None: stream << '0'; break;
|
||||
case Function_Global: stream << '2'; break;
|
||||
case Function_Local: stream << '3'; break;
|
||||
case Function_Journal: stream << '4'; break;
|
||||
case Function_Item: stream << '5'; break;
|
||||
case Function_Dead: stream << '6'; break;
|
||||
case Function_NotId: stream << '7'; break;
|
||||
case Function_NotFaction: stream << '8'; break;
|
||||
case Function_NotClass: stream << '9'; break;
|
||||
case Function_NotRace: stream << 'A'; break;
|
||||
case Function_NotCell: stream << 'B'; break;
|
||||
case Function_NotLocal: stream << 'C'; break;
|
||||
default: stream << '1'; writeIndex = true; break;
|
||||
}
|
||||
|
||||
if (writeIndex && functionIndex < 10) // leading 0
|
||||
stream << '0' << functionIndex;
|
||||
else if (writeIndex)
|
||||
stream << functionIndex;
|
||||
else
|
||||
stream << "00";
|
||||
|
||||
// Write Relation
|
||||
switch (mRelationType)
|
||||
{
|
||||
case Relation_Equal: stream << '0'; break;
|
||||
case Relation_NotEqual: stream << '1'; break;
|
||||
case Relation_Greater: stream << '2'; break;
|
||||
case Relation_GreaterOrEqual: stream << '3'; break;
|
||||
case Relation_Less: stream << '4'; break;
|
||||
case Relation_LessOrEqual: stream << '5'; break;
|
||||
default: stream << '0'; break;
|
||||
}
|
||||
|
||||
if (mHasVariable)
|
||||
stream << mVariableName;
|
||||
|
||||
mSelect.mSelectRule = stream.str();
|
||||
}
|
||||
|
||||
ESM::Variant& CSMWorld::InfoSelectWrapper::getVariant()
|
||||
{
|
||||
return mSelect.mValue;
|
||||
}
|
@ -0,0 +1,243 @@
|
||||
#ifndef CSM_WORLD_INFOSELECTWRAPPER_H
|
||||
#define CSM_WORLD_INFOSELECTWRAPPER_H
|
||||
|
||||
#include <components/esm/loadinfo.hpp>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
// ESM::DialInfo::SelectStruct.mSelectRule
|
||||
// 012345...
|
||||
// ^^^ ^^
|
||||
// ||| ||
|
||||
// ||| |+------------- condition variable string
|
||||
// ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc
|
||||
// ||+---------------- function index (encoded, where function == '1')
|
||||
// |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc
|
||||
// +------------------ unknown
|
||||
//
|
||||
|
||||
// Wrapper for DialInfo::SelectStruct
|
||||
class ConstInfoSelectWrapper
|
||||
{
|
||||
public:
|
||||
|
||||
// Order matters
|
||||
enum FunctionName
|
||||
{
|
||||
Function_RankLow=0,
|
||||
Function_RankHigh,
|
||||
Function_RankRequirement,
|
||||
Function_Reputation,
|
||||
Function_Health_Percent,
|
||||
Function_PcReputation,
|
||||
Function_PcLevel,
|
||||
Function_PcHealthPercent,
|
||||
Function_PcMagicka,
|
||||
Function_PcFatigue,
|
||||
Function_PcStrength,
|
||||
Function_PcBlock,
|
||||
Function_PcArmorer,
|
||||
Function_PcMediumArmor,
|
||||
Function_PcHeavyArmor,
|
||||
Function_PcBluntWeapon,
|
||||
Function_PcLongBlade,
|
||||
Function_PcAxe,
|
||||
Function_PcSpear,
|
||||
Function_PcAthletics,
|
||||
Function_PcEnchant,
|
||||
Function_PcDestruction,
|
||||
Function_PcAlteration,
|
||||
Function_PcIllusion,
|
||||
Function_PcConjuration,
|
||||
Function_PcMysticism,
|
||||
Function_PcRestoration,
|
||||
Function_PcAlchemy,
|
||||
Function_PcUnarmored,
|
||||
Function_PcSecurity,
|
||||
Function_PcSneak,
|
||||
Function_PcAcrobatics,
|
||||
Function_PcLightArmor,
|
||||
Function_PcShortBlade,
|
||||
Function_PcMarksman,
|
||||
Function_PcMerchantile,
|
||||
Function_PcSpeechcraft,
|
||||
Function_PcHandToHand,
|
||||
Function_PcGender,
|
||||
Function_PcExpelled,
|
||||
Function_PcCommonDisease,
|
||||
Function_PcBlightDisease,
|
||||
Function_PcClothingModifier,
|
||||
Function_PcCrimeLevel,
|
||||
Function_SameSex,
|
||||
Function_SameRace,
|
||||
Function_SameFaction,
|
||||
Function_FactionRankDifference,
|
||||
Function_Detected,
|
||||
Function_Alarmed,
|
||||
Function_Choice,
|
||||
Function_PcIntelligence,
|
||||
Function_PcWillpower,
|
||||
Function_PcAgility,
|
||||
Function_PcSpeed,
|
||||
Function_PcEndurance,
|
||||
Function_PcPersonality,
|
||||
Function_PcLuck,
|
||||
Function_PcCorpus,
|
||||
Function_Weather,
|
||||
Function_PcVampire,
|
||||
Function_Level,
|
||||
Function_Attacked,
|
||||
Function_TalkedToPc,
|
||||
Function_PcHealth,
|
||||
Function_CreatureTarget,
|
||||
Function_FriendHit,
|
||||
Function_Fight,
|
||||
Function_Hello,
|
||||
Function_Alarm,
|
||||
Function_Flee,
|
||||
Function_ShouldAttack,
|
||||
Function_Werewolf,
|
||||
Function_PcWerewolfKills=73,
|
||||
|
||||
Function_Global,
|
||||
Function_Local,
|
||||
Function_Journal,
|
||||
Function_Item,
|
||||
Function_Dead,
|
||||
Function_NotId,
|
||||
Function_NotFaction,
|
||||
Function_NotClass,
|
||||
Function_NotRace,
|
||||
Function_NotCell,
|
||||
Function_NotLocal,
|
||||
|
||||
Function_None
|
||||
};
|
||||
|
||||
enum RelationType
|
||||
{
|
||||
Relation_Equal,
|
||||
Relation_NotEqual,
|
||||
Relation_Greater,
|
||||
Relation_GreaterOrEqual,
|
||||
Relation_Less,
|
||||
Relation_LessOrEqual,
|
||||
|
||||
Relation_None
|
||||
};
|
||||
|
||||
enum ComparisonType
|
||||
{
|
||||
Comparison_Boolean,
|
||||
Comparison_Integer,
|
||||
Comparison_Numeric,
|
||||
|
||||
Comparison_None
|
||||
};
|
||||
|
||||
static const size_t RuleMinSize;
|
||||
|
||||
static const size_t FunctionPrefixOffset;
|
||||
static const size_t FunctionIndexOffset;
|
||||
static const size_t RelationIndexOffset;
|
||||
static const size_t VarNameOffset;
|
||||
|
||||
static const char* FunctionEnumStrings[];
|
||||
static const char* RelationEnumStrings[];
|
||||
static const char* ComparisonEnumStrings[];
|
||||
|
||||
static std::string convertToString(FunctionName name);
|
||||
static std::string convertToString(RelationType type);
|
||||
static std::string convertToString(ComparisonType type);
|
||||
|
||||
ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select);
|
||||
|
||||
FunctionName getFunctionName() const;
|
||||
RelationType getRelationType() const;
|
||||
ComparisonType getComparisonType() const;
|
||||
|
||||
bool hasVariable() const;
|
||||
const std::string& getVariableName() const;
|
||||
|
||||
bool conditionIsAlwaysTrue() const;
|
||||
bool conditionIsNeverTrue() const;
|
||||
bool variantTypeIsValid() const;
|
||||
|
||||
const ESM::Variant& getVariant() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
protected:
|
||||
|
||||
void readRule();
|
||||
void readFunctionName();
|
||||
void readRelationType();
|
||||
void readVariableName();
|
||||
void updateHasVariable();
|
||||
void updateComparisonType();
|
||||
|
||||
std::pair<int, int> getConditionIntRange() const;
|
||||
std::pair<float, float> getConditionFloatRange() const;
|
||||
|
||||
std::pair<int, int> getValidIntRange() const;
|
||||
std::pair<float, float> getValidFloatRange() const;
|
||||
|
||||
template <typename Type1, typename Type2>
|
||||
bool rangeContains(Type1 value, std::pair<Type2,Type2> range) const;
|
||||
|
||||
template <typename Type1, typename Type2>
|
||||
bool rangesOverlap(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const;
|
||||
|
||||
template <typename Type1, typename Type2>
|
||||
bool rangeFullyContains(std::pair<Type1,Type1> containing, std::pair<Type2,Type2> test) const;
|
||||
|
||||
template <typename Type1, typename Type2>
|
||||
bool rangesMatch(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const;
|
||||
|
||||
template <typename Type1, typename Type2>
|
||||
bool conditionIsAlwaysTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const;
|
||||
|
||||
template <typename Type1, typename Type2>
|
||||
bool conditionIsNeverTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const;
|
||||
|
||||
FunctionName mFunctionName;
|
||||
RelationType mRelationType;
|
||||
ComparisonType mComparisonType;
|
||||
|
||||
bool mHasVariable;
|
||||
std::string mVariableName;
|
||||
|
||||
private:
|
||||
|
||||
const ESM::DialInfo::SelectStruct& mConstSelect;
|
||||
};
|
||||
|
||||
// Wrapper for DialInfo::SelectStruct that can modify the wrapped select struct
|
||||
class InfoSelectWrapper : public ConstInfoSelectWrapper
|
||||
{
|
||||
public:
|
||||
|
||||
InfoSelectWrapper(ESM::DialInfo::SelectStruct& select);
|
||||
|
||||
// Wrapped SelectStruct will not be modified until update() is called
|
||||
void setFunctionName(FunctionName name);
|
||||
void setRelationType(RelationType type);
|
||||
void setVariableName(const std::string& name);
|
||||
|
||||
// Modified wrapped SelectStruct
|
||||
void update();
|
||||
|
||||
// This sets properties based on the function name to its defaults and updates the wrapped object
|
||||
void setDefaults();
|
||||
|
||||
ESM::Variant& getVariant();
|
||||
|
||||
private:
|
||||
|
||||
ESM::DialInfo::SelectStruct& mSelect;
|
||||
|
||||
void writeRule();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,650 @@
|
||||
#include "cameracontroller.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include <osg/BoundingBox>
|
||||
#include <osg/Camera>
|
||||
#include <osg/ComputeBoundsVisitor>
|
||||
#include <osg/Drawable>
|
||||
#include <osg/Group>
|
||||
#include <osg/Matrixd>
|
||||
#include <osg/Quat>
|
||||
|
||||
#include <osgUtil/LineSegmentIntersector>
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
|
||||
/*
|
||||
Camera Controller
|
||||
*/
|
||||
|
||||
const osg::Vec3d CameraController::WorldUp = osg::Vec3d(0, 0, 1);
|
||||
|
||||
const osg::Vec3d CameraController::LocalUp = osg::Vec3d(0, 1, 0);
|
||||
const osg::Vec3d CameraController::LocalLeft = osg::Vec3d(1, 0, 0);
|
||||
const osg::Vec3d CameraController::LocalForward = osg::Vec3d(0, 0, 1);
|
||||
|
||||
CameraController::CameraController()
|
||||
: mActive(false)
|
||||
, mInverted(false)
|
||||
, mCameraSensitivity(1/650.f)
|
||||
, mSecondaryMoveMult(50)
|
||||
, mWheelMoveMult(8)
|
||||
, mCamera(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
CameraController::~CameraController()
|
||||
{
|
||||
}
|
||||
|
||||
bool CameraController::isActive() const
|
||||
{
|
||||
return mActive;
|
||||
}
|
||||
|
||||
osg::Camera* CameraController::getCamera() const
|
||||
{
|
||||
return mCamera;
|
||||
}
|
||||
|
||||
double CameraController::getCameraSensitivity() const
|
||||
{
|
||||
return mCameraSensitivity;
|
||||
}
|
||||
|
||||
bool CameraController::getInverted() const
|
||||
{
|
||||
return mInverted;
|
||||
}
|
||||
|
||||
double CameraController::getSecondaryMovementMultiplier() const
|
||||
{
|
||||
return mSecondaryMoveMult;
|
||||
}
|
||||
|
||||
double CameraController::getWheelMovementMultiplier() const
|
||||
{
|
||||
return mWheelMoveMult;
|
||||
}
|
||||
|
||||
void CameraController::setCamera(osg::Camera* camera)
|
||||
{
|
||||
mCamera = camera;
|
||||
mActive = (mCamera != NULL);
|
||||
|
||||
if (mActive)
|
||||
onActivate();
|
||||
}
|
||||
|
||||
void CameraController::setCameraSensitivity(double value)
|
||||
{
|
||||
mCameraSensitivity = value;
|
||||
}
|
||||
|
||||
void CameraController::setInverted(bool value)
|
||||
{
|
||||
mInverted = value;
|
||||
}
|
||||
|
||||
void CameraController::setSecondaryMovementMultiplier(double value)
|
||||
{
|
||||
mSecondaryMoveMult = value;
|
||||
}
|
||||
|
||||
void CameraController::setWheelMovementMultiplier(double value)
|
||||
{
|
||||
mWheelMoveMult = value;
|
||||
}
|
||||
|
||||
void CameraController::setup(osg::Group* root, unsigned int mask, const osg::Vec3d& up)
|
||||
{
|
||||
// Find World bounds
|
||||
osg::ComputeBoundsVisitor boundsVisitor;
|
||||
osg::BoundingBox& boundingBox = boundsVisitor.getBoundingBox();
|
||||
|
||||
boundsVisitor.setTraversalMask(mask);
|
||||
root->accept(boundsVisitor);
|
||||
|
||||
if (!boundingBox.valid())
|
||||
{
|
||||
// Try again without any mask
|
||||
boundsVisitor.reset();
|
||||
boundsVisitor.setTraversalMask(~0);
|
||||
root->accept(boundsVisitor);
|
||||
|
||||
// Last resort, set a default
|
||||
if (!boundingBox.valid())
|
||||
{
|
||||
boundingBox.set(-1, -1, -1, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate a good starting position
|
||||
osg::Vec3d minBounds = boundingBox.corner(0) - boundingBox.center();
|
||||
osg::Vec3d maxBounds = boundingBox.corner(7) - boundingBox.center();
|
||||
|
||||
osg::Vec3d camOffset = up * maxBounds > 0 ? maxBounds : minBounds;
|
||||
camOffset *= 2;
|
||||
|
||||
osg::Vec3d eye = camOffset + boundingBox.center();
|
||||
osg::Vec3d center = boundingBox.center();
|
||||
|
||||
getCamera()->setViewMatrixAsLookAt(eye, center, up);
|
||||
}
|
||||
|
||||
/*
|
||||
Free Camera Controller
|
||||
*/
|
||||
|
||||
FreeCameraController::FreeCameraController()
|
||||
: mLockUpright(false)
|
||||
, mModified(false)
|
||||
, mFast(false)
|
||||
, mLeft(false)
|
||||
, mRight(false)
|
||||
, mForward(false)
|
||||
, mBackward(false)
|
||||
, mRollLeft(false)
|
||||
, mRollRight(false)
|
||||
, mUp(LocalUp)
|
||||
, mLinSpeed(1000)
|
||||
, mRotSpeed(osg::PI / 2)
|
||||
, mSpeedMult(8)
|
||||
{
|
||||
}
|
||||
|
||||
double FreeCameraController::getLinearSpeed() const
|
||||
{
|
||||
return mLinSpeed;
|
||||
}
|
||||
|
||||
double FreeCameraController::getRotationalSpeed() const
|
||||
{
|
||||
return mRotSpeed;
|
||||
}
|
||||
|
||||
double FreeCameraController::getSpeedMultiplier() const
|
||||
{
|
||||
return mSpeedMult;
|
||||
}
|
||||
|
||||
void FreeCameraController::setLinearSpeed(double value)
|
||||
{
|
||||
mLinSpeed = value;
|
||||
}
|
||||
|
||||
void FreeCameraController::setRotationalSpeed(double value)
|
||||
{
|
||||
mRotSpeed = value;
|
||||
}
|
||||
|
||||
void FreeCameraController::setSpeedMultiplier(double value)
|
||||
{
|
||||
mSpeedMult = value;
|
||||
}
|
||||
|
||||
void FreeCameraController::fixUpAxis(const osg::Vec3d& up)
|
||||
{
|
||||
mLockUpright = true;
|
||||
mUp = up;
|
||||
mModified = true;
|
||||
}
|
||||
|
||||
void FreeCameraController::unfixUpAxis()
|
||||
{
|
||||
mLockUpright = false;
|
||||
}
|
||||
|
||||
bool FreeCameraController::handleKeyEvent(QKeyEvent* event, bool pressed)
|
||||
{
|
||||
if (!isActive())
|
||||
return false;
|
||||
|
||||
if (event->key() == Qt::Key_Q)
|
||||
{
|
||||
mRollLeft = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_E)
|
||||
{
|
||||
mRollRight = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_A)
|
||||
{
|
||||
mLeft = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_D)
|
||||
{
|
||||
mRight = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_W)
|
||||
{
|
||||
mForward = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_S)
|
||||
{
|
||||
mBackward = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_Shift)
|
||||
{
|
||||
mFast = pressed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FreeCameraController::handleMouseMoveEvent(std::string mode, int x, int y)
|
||||
{
|
||||
if (!isActive())
|
||||
return false;
|
||||
|
||||
if (mode == "p-navi")
|
||||
{
|
||||
double scalar = getCameraSensitivity() * (getInverted() ? -1.0 : 1.0);
|
||||
yaw(x * scalar);
|
||||
pitch(y * scalar);
|
||||
}
|
||||
else if (mode == "s-navi")
|
||||
{
|
||||
osg::Vec3d movement;
|
||||
movement += LocalLeft * -x * getSecondaryMovementMultiplier();
|
||||
movement += LocalUp * y * getSecondaryMovementMultiplier();
|
||||
|
||||
translate(movement);
|
||||
}
|
||||
else if (mode == "t-navi")
|
||||
{
|
||||
translate(LocalForward * x * (mFast ? getWheelMovementMultiplier() : 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FreeCameraController::update(double dt)
|
||||
{
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
double linDist = mLinSpeed * dt;
|
||||
double rotDist = mRotSpeed * dt;
|
||||
|
||||
if (mFast)
|
||||
linDist *= mSpeedMult;
|
||||
|
||||
if (mLeft)
|
||||
translate(LocalLeft * linDist);
|
||||
if (mRight)
|
||||
translate(LocalLeft * -linDist);
|
||||
if (mForward)
|
||||
translate(LocalForward * linDist);
|
||||
if (mBackward)
|
||||
translate(LocalForward * -linDist);
|
||||
|
||||
if (!mLockUpright)
|
||||
{
|
||||
if (mRollLeft)
|
||||
roll(-rotDist);
|
||||
if (mRollRight)
|
||||
roll(rotDist);
|
||||
}
|
||||
else if(mModified)
|
||||
{
|
||||
stabilize();
|
||||
mModified = false;
|
||||
}
|
||||
|
||||
// Normalize the matrix to counter drift
|
||||
getCamera()->getViewMatrix().orthoNormal(getCamera()->getViewMatrix());
|
||||
}
|
||||
|
||||
void FreeCameraController::resetInput()
|
||||
{
|
||||
mFast = false;
|
||||
mLeft = false;
|
||||
mRight = false;
|
||||
mForward = false;
|
||||
mBackward = false;
|
||||
mRollLeft = false;
|
||||
mRollRight = false;
|
||||
}
|
||||
|
||||
void FreeCameraController::yaw(double value)
|
||||
{
|
||||
getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalUp);
|
||||
mModified = true;
|
||||
}
|
||||
|
||||
void FreeCameraController::pitch(double value)
|
||||
{
|
||||
const double Constraint = osg::PI / 2 - 0.1;
|
||||
|
||||
if (mLockUpright)
|
||||
{
|
||||
osg::Vec3d eye, center, up;
|
||||
getCamera()->getViewMatrixAsLookAt(eye, center, up);
|
||||
|
||||
osg::Vec3d forward = center - eye;
|
||||
osg::Vec3d left = up ^ forward;
|
||||
|
||||
double pitchAngle = std::acos(up * mUp);
|
||||
if ((mUp ^ up) * left < 0)
|
||||
pitchAngle *= -1;
|
||||
|
||||
if (std::abs(pitchAngle + value) > Constraint)
|
||||
value = (pitchAngle > 0 ? 1 : -1) * Constraint - pitchAngle;
|
||||
}
|
||||
|
||||
getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalLeft);
|
||||
mModified = true;
|
||||
}
|
||||
|
||||
void FreeCameraController::roll(double value)
|
||||
{
|
||||
getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalForward);
|
||||
mModified = true;
|
||||
}
|
||||
|
||||
void FreeCameraController::translate(const osg::Vec3d& offset)
|
||||
{
|
||||
getCamera()->getViewMatrix() *= osg::Matrixd::translate(offset);
|
||||
mModified = true;
|
||||
}
|
||||
|
||||
void FreeCameraController::stabilize()
|
||||
{
|
||||
osg::Vec3d eye, center, up;
|
||||
getCamera()->getViewMatrixAsLookAt(eye, center, up);
|
||||
getCamera()->setViewMatrixAsLookAt(eye, center, mUp);
|
||||
}
|
||||
|
||||
/*
|
||||
Orbit Camera Controller
|
||||
*/
|
||||
|
||||
OrbitCameraController::OrbitCameraController()
|
||||
: mInitialized(false)
|
||||
, mFast(false)
|
||||
, mLeft(false)
|
||||
, mRight(false)
|
||||
, mUp(false)
|
||||
, mDown(false)
|
||||
, mRollLeft(false)
|
||||
, mRollRight(false)
|
||||
, mPickingMask(~0)
|
||||
, mCenter(0,0,0)
|
||||
, mDistance(0)
|
||||
, mOrbitSpeed(osg::PI / 4)
|
||||
, mOrbitSpeedMult(4)
|
||||
{
|
||||
}
|
||||
|
||||
osg::Vec3d OrbitCameraController::getCenter() const
|
||||
{
|
||||
return mCenter;
|
||||
}
|
||||
|
||||
double OrbitCameraController::getOrbitSpeed() const
|
||||
{
|
||||
return mOrbitSpeed;
|
||||
}
|
||||
|
||||
double OrbitCameraController::getOrbitSpeedMultiplier() const
|
||||
{
|
||||
return mOrbitSpeedMult;
|
||||
}
|
||||
|
||||
unsigned int OrbitCameraController::getPickingMask() const
|
||||
{
|
||||
return mPickingMask;
|
||||
}
|
||||
|
||||
void OrbitCameraController::setCenter(const osg::Vec3d& value)
|
||||
{
|
||||
osg::Vec3d eye, center, up;
|
||||
getCamera()->getViewMatrixAsLookAt(eye, center, up);
|
||||
|
||||
mCenter = value;
|
||||
mDistance = (eye - mCenter).length();
|
||||
|
||||
getCamera()->setViewMatrixAsLookAt(eye, mCenter, up);
|
||||
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
void OrbitCameraController::setOrbitSpeed(double value)
|
||||
{
|
||||
mOrbitSpeed = value;
|
||||
}
|
||||
|
||||
void OrbitCameraController::setOrbitSpeedMultiplier(double value)
|
||||
{
|
||||
mOrbitSpeedMult = value;
|
||||
}
|
||||
|
||||
void OrbitCameraController::setPickingMask(unsigned int value)
|
||||
{
|
||||
mPickingMask = value;
|
||||
}
|
||||
|
||||
bool OrbitCameraController::handleKeyEvent(QKeyEvent* event, bool pressed)
|
||||
{
|
||||
if (!isActive())
|
||||
return false;
|
||||
|
||||
if (!mInitialized)
|
||||
initialize();
|
||||
|
||||
if (event->key() == Qt::Key_Q)
|
||||
{
|
||||
mRollLeft = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_E)
|
||||
{
|
||||
mRollRight = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_A)
|
||||
{
|
||||
mLeft = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_D)
|
||||
{
|
||||
mRight = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_W)
|
||||
{
|
||||
mUp = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_S)
|
||||
{
|
||||
mDown = pressed;
|
||||
}
|
||||
else if (event->key() == Qt::Key_Shift)
|
||||
{
|
||||
mFast = pressed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OrbitCameraController::handleMouseMoveEvent(std::string mode, int x, int y)
|
||||
{
|
||||
if (!isActive())
|
||||
return false;
|
||||
|
||||
if (!mInitialized)
|
||||
initialize();
|
||||
|
||||
if (mode == "p-navi")
|
||||
{
|
||||
double scalar = getCameraSensitivity() * (getInverted() ? -1.0 : 1.0);
|
||||
rotateHorizontal(x * scalar);
|
||||
rotateVertical(-y * scalar);
|
||||
}
|
||||
else if (mode == "s-navi")
|
||||
{
|
||||
osg::Vec3d movement;
|
||||
movement += LocalLeft * x * getSecondaryMovementMultiplier();
|
||||
movement += LocalUp * -y * getSecondaryMovementMultiplier();
|
||||
|
||||
translate(movement);
|
||||
}
|
||||
else if (mode == "t-navi")
|
||||
{
|
||||
zoom(-x * (mFast ? getWheelMovementMultiplier() : 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OrbitCameraController::update(double dt)
|
||||
{
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
if (!mInitialized)
|
||||
initialize();
|
||||
|
||||
double rotDist = mOrbitSpeed * dt;
|
||||
|
||||
if (mFast)
|
||||
rotDist *= mOrbitSpeedMult;
|
||||
|
||||
if (mLeft)
|
||||
rotateHorizontal(-rotDist);
|
||||
if (mRight)
|
||||
rotateHorizontal(rotDist);
|
||||
if (mUp)
|
||||
rotateVertical(rotDist);
|
||||
if (mDown)
|
||||
rotateVertical(-rotDist);
|
||||
|
||||
if (mRollLeft)
|
||||
roll(-rotDist);
|
||||
if (mRollRight)
|
||||
roll(rotDist);
|
||||
|
||||
// Normalize the matrix to counter drift
|
||||
getCamera()->getViewMatrix().orthoNormal(getCamera()->getViewMatrix());
|
||||
}
|
||||
|
||||
void OrbitCameraController::resetInput()
|
||||
{
|
||||
mFast = false;
|
||||
mLeft = false;
|
||||
mRight =false;
|
||||
mUp = false;
|
||||
mDown = false;
|
||||
mRollLeft = false;
|
||||
mRollRight = false;
|
||||
}
|
||||
|
||||
void OrbitCameraController::onActivate()
|
||||
{
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
void OrbitCameraController::initialize()
|
||||
{
|
||||
static const int DefaultStartDistance = 10000.f;
|
||||
|
||||
// Try to intelligently pick focus object
|
||||
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(
|
||||
osgUtil::Intersector::PROJECTION, osg::Vec3d(0, 0, 0), LocalForward));
|
||||
|
||||
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);
|
||||
osgUtil::IntersectionVisitor visitor(intersector);
|
||||
|
||||
visitor.setTraversalMask(mPickingMask);
|
||||
|
||||
getCamera()->accept(visitor);
|
||||
|
||||
osg::Vec3d eye, center, up;
|
||||
getCamera()->getViewMatrixAsLookAt(eye, center, up, DefaultStartDistance);
|
||||
|
||||
if (intersector->getIntersections().begin() != intersector->getIntersections().end())
|
||||
{
|
||||
mCenter = intersector->getIntersections().begin()->getWorldIntersectPoint();
|
||||
mDistance = (eye - mCenter).length();
|
||||
}
|
||||
else
|
||||
{
|
||||
mCenter = center;
|
||||
mDistance = DefaultStartDistance;
|
||||
}
|
||||
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
void OrbitCameraController::rotateHorizontal(double value)
|
||||
{
|
||||
osg::Vec3d eye, center, up;
|
||||
getCamera()->getViewMatrixAsLookAt(eye, center, up);
|
||||
|
||||
osg::Quat rotation = osg::Quat(value, up);
|
||||
osg::Vec3d oldOffset = eye - mCenter;
|
||||
osg::Vec3d newOffset = rotation * oldOffset;
|
||||
|
||||
getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up);
|
||||
}
|
||||
|
||||
void OrbitCameraController::rotateVertical(double value)
|
||||
{
|
||||
osg::Vec3d eye, center, up;
|
||||
getCamera()->getViewMatrixAsLookAt(eye, center, up);
|
||||
|
||||
osg::Vec3d forward = center - eye;
|
||||
osg::Quat rotation = osg::Quat(value, up ^ forward);
|
||||
osg::Vec3d oldOffset = eye - mCenter;
|
||||
osg::Vec3d newOffset = rotation * oldOffset;
|
||||
|
||||
getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up);
|
||||
}
|
||||
|
||||
void OrbitCameraController::roll(double value)
|
||||
{
|
||||
getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalForward);
|
||||
}
|
||||
|
||||
void OrbitCameraController::translate(const osg::Vec3d& offset)
|
||||
{
|
||||
osg::Vec3d eye, center, up;
|
||||
getCamera()->getViewMatrixAsLookAt(eye, center, up);
|
||||
|
||||
osg::Vec3d newOffset = getCamera()->getViewMatrix().getRotate().inverse() * offset;
|
||||
mCenter += newOffset;
|
||||
eye += newOffset;
|
||||
|
||||
getCamera()->setViewMatrixAsLookAt(eye, mCenter, up);
|
||||
}
|
||||
|
||||
void OrbitCameraController::zoom(double value)
|
||||
{
|
||||
mDistance = std::max(10., mDistance + value);
|
||||
|
||||
osg::Vec3d eye, center, up;
|
||||
getCamera()->getViewMatrixAsLookAt(eye, center, up, 1.f);
|
||||
|
||||
osg::Vec3d offset = (eye - center) * mDistance;
|
||||
|
||||
getCamera()->setViewMatrixAsLookAt(mCenter + offset, mCenter, up);
|
||||
}
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
#ifndef OPENCS_VIEW_CAMERACONTROLLER_H
|
||||
#define OPENCS_VIEW_CAMERACONTROLLER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Vec3d>
|
||||
|
||||
class QKeyEvent;
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Camera;
|
||||
class Group;
|
||||
}
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
class CameraController
|
||||
{
|
||||
public:
|
||||
|
||||
static const osg::Vec3d WorldUp;
|
||||
|
||||
static const osg::Vec3d LocalUp;
|
||||
static const osg::Vec3d LocalLeft;
|
||||
static const osg::Vec3d LocalForward;
|
||||
|
||||
CameraController();
|
||||
virtual ~CameraController();
|
||||
|
||||
bool isActive() const;
|
||||
|
||||
osg::Camera* getCamera() const;
|
||||
double getCameraSensitivity() const;
|
||||
bool getInverted() const;
|
||||
double getSecondaryMovementMultiplier() const;
|
||||
double getWheelMovementMultiplier() const;
|
||||
|
||||
void setCamera(osg::Camera*);
|
||||
void setCameraSensitivity(double value);
|
||||
void setInverted(bool value);
|
||||
void setSecondaryMovementMultiplier(double value);
|
||||
void setWheelMovementMultiplier(double value);
|
||||
|
||||
// moves the camera to an intelligent position
|
||||
void setup(osg::Group* root, unsigned int mask, const osg::Vec3d& up);
|
||||
|
||||
virtual bool handleKeyEvent(QKeyEvent* event, bool pressed) = 0;
|
||||
virtual bool handleMouseMoveEvent(std::string mode, int x, int y) = 0;
|
||||
|
||||
virtual void update(double dt) = 0;
|
||||
|
||||
virtual void resetInput() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void onActivate(){}
|
||||
|
||||
private:
|
||||
|
||||
bool mActive, mInverted;
|
||||
double mCameraSensitivity;
|
||||
double mSecondaryMoveMult;
|
||||
double mWheelMoveMult;
|
||||
|
||||
osg::Camera* mCamera;
|
||||
};
|
||||
|
||||
class FreeCameraController : public CameraController
|
||||
{
|
||||
public:
|
||||
|
||||
FreeCameraController();
|
||||
|
||||
double getLinearSpeed() const;
|
||||
double getRotationalSpeed() const;
|
||||
double getSpeedMultiplier() const;
|
||||
|
||||
void setLinearSpeed(double value);
|
||||
void setRotationalSpeed(double value);
|
||||
void setSpeedMultiplier(double value);
|
||||
|
||||
void fixUpAxis(const osg::Vec3d& up);
|
||||
void unfixUpAxis();
|
||||
|
||||
bool handleKeyEvent(QKeyEvent* event, bool pressed);
|
||||
bool handleMouseMoveEvent(std::string mode, int x, int y);
|
||||
|
||||
void update(double dt);
|
||||
|
||||
void resetInput();
|
||||
|
||||
private:
|
||||
|
||||
void yaw(double value);
|
||||
void pitch(double value);
|
||||
void roll(double value);
|
||||
void translate(const osg::Vec3d& offset);
|
||||
|
||||
void stabilize();
|
||||
|
||||
bool mLockUpright, mModified;
|
||||
bool mFast, mLeft, mRight, mForward, mBackward, mRollLeft, mRollRight;
|
||||
osg::Vec3d mUp;
|
||||
|
||||
double mLinSpeed;
|
||||
double mRotSpeed;
|
||||
double mSpeedMult;
|
||||
};
|
||||
|
||||
class OrbitCameraController : public CameraController
|
||||
{
|
||||
public:
|
||||
|
||||
OrbitCameraController();
|
||||
|
||||
osg::Vec3d getCenter() const;
|
||||
double getOrbitSpeed() const;
|
||||
double getOrbitSpeedMultiplier() const;
|
||||
unsigned int getPickingMask() const;
|
||||
|
||||
void setCenter(const osg::Vec3d& center);
|
||||
void setOrbitSpeed(double value);
|
||||
void setOrbitSpeedMultiplier(double value);
|
||||
void setPickingMask(unsigned int value);
|
||||
|
||||
bool handleKeyEvent(QKeyEvent* event, bool pressed);
|
||||
bool handleMouseMoveEvent(std::string mode, int x, int y);
|
||||
|
||||
void update(double dt);
|
||||
|
||||
void resetInput();
|
||||
|
||||
private:
|
||||
|
||||
void onActivate();
|
||||
|
||||
void initialize();
|
||||
|
||||
void rotateHorizontal(double value);
|
||||
void rotateVertical(double value);
|
||||
void roll(double value);
|
||||
void translate(const osg::Vec3d& offset);
|
||||
void zoom(double value);
|
||||
|
||||
bool mInitialized;
|
||||
bool mFast, mLeft, mRight, mUp, mDown, mRollLeft, mRollRight;
|
||||
unsigned int mPickingMask;
|
||||
osg::Vec3d mCenter;
|
||||
double mDistance;
|
||||
|
||||
double mOrbitSpeed;
|
||||
double mOrbitSpeedMult;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,96 @@
|
||||
#include "cellborder.hpp"
|
||||
|
||||
#include <osg/Group>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
|
||||
#include <components/esm/loadland.hpp>
|
||||
|
||||
#include "mask.hpp"
|
||||
|
||||
#include "../../model/world/cellcoordinates.hpp"
|
||||
|
||||
const int CSVRender::CellBorder::CellSize = ESM::Land::REAL_SIZE;
|
||||
const int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 3;
|
||||
|
||||
|
||||
CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords)
|
||||
: mParentNode(cellNode)
|
||||
{
|
||||
mBaseNode = new osg::PositionAttitudeTransform();
|
||||
mBaseNode->setNodeMask(Mask_CellBorder);
|
||||
mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10));
|
||||
|
||||
mParentNode->addChild(mBaseNode);
|
||||
}
|
||||
|
||||
CSVRender::CellBorder::~CellBorder()
|
||||
{
|
||||
mParentNode->removeChild(mBaseNode);
|
||||
}
|
||||
|
||||
void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand)
|
||||
{
|
||||
const ESM::Land::LandData* landData = esmLand.getLandData(ESM::Land::DATA_VHGT);
|
||||
|
||||
if (!landData)
|
||||
return;
|
||||
|
||||
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
|
||||
|
||||
// Vertices
|
||||
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array();
|
||||
|
||||
int x = 0, y = 0;
|
||||
for (; x < ESM::Land::LAND_SIZE; ++x)
|
||||
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
||||
|
||||
x = ESM::Land::LAND_SIZE - 1;
|
||||
for (; y < ESM::Land::LAND_SIZE; ++y)
|
||||
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
||||
|
||||
y = ESM::Land::LAND_SIZE - 1;
|
||||
for (; x >= 0; --x)
|
||||
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
||||
|
||||
x = 0;
|
||||
for (; y >= 0; --y)
|
||||
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
||||
|
||||
geometry->setVertexArray(vertices);
|
||||
|
||||
// Color
|
||||
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array();
|
||||
colors->push_back(osg::Vec4f(0.f, 0.5f, 0.f, 1.f));
|
||||
|
||||
geometry->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET);
|
||||
|
||||
// Primitive
|
||||
osg::ref_ptr<osg::DrawElementsUShort> primitives =
|
||||
new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount+1);
|
||||
|
||||
for (size_t i = 0; i < VertexCount; ++i)
|
||||
primitives->setElement(i, i);
|
||||
|
||||
primitives->setElement(VertexCount, 0);
|
||||
|
||||
geometry->addPrimitiveSet(primitives);
|
||||
geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||
|
||||
|
||||
osg::ref_ptr<osg::Geode> geode = new osg::Geode();
|
||||
geode->addDrawable(geometry);
|
||||
mBaseNode->addChild(geode);
|
||||
}
|
||||
|
||||
size_t CSVRender::CellBorder::landIndex(int x, int y)
|
||||
{
|
||||
return y * ESM::Land::LAND_SIZE + x;
|
||||
}
|
||||
|
||||
float CSVRender::CellBorder::scaleToWorld(int value)
|
||||
{
|
||||
return (CellSize + 128) * (float)value / ESM::Land::LAND_SIZE;
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
#ifndef OPENCS_VIEW_CELLBORDER_H
|
||||
#define OPENCS_VIEW_CELLBORDER_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Group;
|
||||
class PositionAttitudeTransform;
|
||||
}
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Land;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class CellCoordinates;
|
||||
}
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
|
||||
class CellBorder
|
||||
{
|
||||
public:
|
||||
|
||||
CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords);
|
||||
~CellBorder();
|
||||
|
||||
void buildShape(const ESM::Land& esmLand);
|
||||
|
||||
private:
|
||||
|
||||
static const int CellSize;
|
||||
static const int VertexCount;
|
||||
|
||||
size_t landIndex(int x, int y);
|
||||
float scaleToWorld(int val);
|
||||
|
||||
// unimplemented
|
||||
CellBorder(const CellBorder&);
|
||||
CellBorder& operator=(const CellBorder&);
|
||||
|
||||
osg::Group* mParentNode;
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,91 @@
|
||||
#include "cellmarker.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <osg/AutoTransform>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Group>
|
||||
#include <osgText/Text>
|
||||
|
||||
#include "mask.hpp"
|
||||
|
||||
CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker)
|
||||
: TagBase(Mask_CellMarker), mMarker(marker)
|
||||
{}
|
||||
|
||||
CSVRender::CellMarker *CSVRender::CellMarkerTag::getCellMarker() const
|
||||
{
|
||||
return mMarker;
|
||||
}
|
||||
|
||||
void CSVRender::CellMarker::buildMarker()
|
||||
{
|
||||
const int characterSize = 20;
|
||||
|
||||
// Set up attributes of marker text.
|
||||
osg::ref_ptr<osgText::Text> markerText (new osgText::Text);
|
||||
markerText->setLayout(osgText::Text::LEFT_TO_RIGHT);
|
||||
markerText->setCharacterSize(characterSize);
|
||||
markerText->setAlignment(osgText::Text::CENTER_CENTER);
|
||||
markerText->setDrawMode(osgText::Text::TEXT | osgText::Text::FILLEDBOUNDINGBOX);
|
||||
|
||||
// If cell exists then show black bounding box otherwise show red.
|
||||
if (mExists)
|
||||
{
|
||||
markerText->setBoundingBoxColor(osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
markerText->setBoundingBoxColor(osg::Vec4f(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
// Add text containing cell's coordinates.
|
||||
std::string coordinatesText =
|
||||
boost::lexical_cast<std::string>(mCoordinates.getX()) + "," +
|
||||
boost::lexical_cast<std::string>(mCoordinates.getY());
|
||||
markerText->setText(coordinatesText);
|
||||
|
||||
// Add text to marker node.
|
||||
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
|
||||
geode->addDrawable(markerText);
|
||||
mMarkerNode->addChild(geode);
|
||||
}
|
||||
|
||||
void CSVRender::CellMarker::positionMarker()
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
const int markerHeight = 0;
|
||||
|
||||
// Move marker to center of cell.
|
||||
int x = (mCoordinates.getX() * cellSize) + (cellSize / 2);
|
||||
int y = (mCoordinates.getY() * cellSize) + (cellSize / 2);
|
||||
mMarkerNode->setPosition(osg::Vec3f(x, y, markerHeight));
|
||||
}
|
||||
|
||||
CSVRender::CellMarker::CellMarker(
|
||||
osg::Group *cellNode,
|
||||
const CSMWorld::CellCoordinates& coordinates,
|
||||
const bool cellExists
|
||||
) : mCellNode(cellNode),
|
||||
mCoordinates(coordinates),
|
||||
mExists(cellExists)
|
||||
{
|
||||
// Set up node for cell marker.
|
||||
mMarkerNode = new osg::AutoTransform();
|
||||
mMarkerNode->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN);
|
||||
mMarkerNode->setAutoScaleToScreen(true);
|
||||
mMarkerNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
||||
|
||||
mMarkerNode->setUserData(new CellMarkerTag(this));
|
||||
mMarkerNode->setNodeMask(Mask_CellMarker);
|
||||
|
||||
mCellNode->addChild(mMarkerNode);
|
||||
|
||||
buildMarker();
|
||||
positionMarker();
|
||||
}
|
||||
|
||||
CSVRender::CellMarker::~CellMarker()
|
||||
{
|
||||
mCellNode->removeChild(mMarkerNode);
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
#ifndef OPENCS_VIEW_CELLMARKER_H
|
||||
#define OPENCS_VIEW_CELLMARKER_H
|
||||
|
||||
#include "tagbase.hpp"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include "../../model/world/cellcoordinates.hpp"
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class AutoTransform;
|
||||
class Group;
|
||||
}
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
class CellMarker;
|
||||
|
||||
class CellMarkerTag : public TagBase
|
||||
{
|
||||
private:
|
||||
|
||||
CellMarker *mMarker;
|
||||
|
||||
public:
|
||||
|
||||
CellMarkerTag(CellMarker *marker);
|
||||
|
||||
CellMarker *getCellMarker() const;
|
||||
};
|
||||
|
||||
/// \brief Marker to display cell coordinates.
|
||||
class CellMarker
|
||||
{
|
||||
private:
|
||||
|
||||
osg::Group* mCellNode;
|
||||
osg::ref_ptr<osg::AutoTransform> mMarkerNode;
|
||||
CSMWorld::CellCoordinates mCoordinates;
|
||||
bool mExists;
|
||||
|
||||
// Not implemented.
|
||||
CellMarker(const CellMarker&);
|
||||
CellMarker& operator=(const CellMarker&);
|
||||
|
||||
/// \brief Build marker containing cell's coordinates.
|
||||
void buildMarker();
|
||||
|
||||
/// \brief Position marker at center of cell.
|
||||
void positionMarker();
|
||||
|
||||
public:
|
||||
|
||||
/// \brief Constructor.
|
||||
/// \param cellNode Cell to create marker for.
|
||||
/// \param coordinates Coordinates of cell.
|
||||
/// \param cellExists Whether or not cell exists.
|
||||
CellMarker(
|
||||
osg::Group *cellNode,
|
||||
const CSMWorld::CellCoordinates& coordinates,
|
||||
const bool cellExists);
|
||||
|
||||
~CellMarker();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue