1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-28 10:39:40 +00:00

Add OpenMW commits up to 29 Oct 2019

# Conflicts:
#	apps/openmw/mwmechanics/combat.cpp
#	apps/openmw/mwworld/inventorystore.cpp
This commit is contained in:
David Cernat 2019-10-29 23:49:31 +02:00
commit 54301f5f89
124 changed files with 3999 additions and 950 deletions

10
.gitignore vendored
View file

@ -83,3 +83,13 @@ moc_*.cxx
*.[ao] *.[ao]
*.so *.so
venv/ venv/
## recastnavigation unused files
extern/recastnavigation/.travis.yml
extern/recastnavigation/CONTRIBUTING.md
extern/recastnavigation/Docs/
extern/recastnavigation/Doxyfile
extern/recastnavigation/README.md
extern/recastnavigation/RecastDemo/
extern/recastnavigation/Tests/
extern/recastnavigation/appveyor.yml

View file

@ -14,13 +14,7 @@ Debian:
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR - export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
- apt-get update -yq - apt-get update -yq
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev
# - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old - apt-get install -y libmygui-dev libbullet-dev
- curl -L http://archive.ubuntu.com/ubuntu/pool/universe/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb
- curl -L http://archive.ubuntu.com/ubuntu/pool/universe/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb
- curl -L http://archive.ubuntu.com/ubuntu/pool/universe/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb
- curl -L http://archive.ubuntu.com/ubuntu/pool/universe/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb
- curl -L http://archive.ubuntu.com/ubuntu/pool/universe/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb
- dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb
stage: build stage: build
script: script:
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi - cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi

View file

@ -19,6 +19,7 @@
Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation
Bug #3812: Wrong multiline tooltips width when word-wrapping is enabled Bug #3812: Wrong multiline tooltips width when word-wrapping is enabled
Bug #3894: Hostile spell effects not detected/present on first frame of OnPCHitMe Bug #3894: Hostile spell effects not detected/present on first frame of OnPCHitMe
Bug #3977: Non-ASCII characters in object ID's are not supported
Bug #4077: Enchanted items are not recharged if they are not in the player's inventory Bug #4077: Enchanted items are not recharged if they are not in the player's inventory
Bug #4202: Open .omwaddon files without needing toopen openmw-cs first Bug #4202: Open .omwaddon files without needing toopen openmw-cs first
Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect
@ -55,6 +56,7 @@
Bug #4768: Fallback numerical value recovery chokes on invalid arguments Bug #4768: Fallback numerical value recovery chokes on invalid arguments
Bug #4775: Slowfall effect resets player jumping flag Bug #4775: Slowfall effect resets player jumping flag
Bug #4778: Interiors of Illusion puzzle in Sotha Sil Expanded mod is broken Bug #4778: Interiors of Illusion puzzle in Sotha Sil Expanded mod is broken
Bug #4783: Blizzard behavior is incorrect
Bug #4787: Sneaking makes 1st person walking/bobbing animation super-slow Bug #4787: Sneaking makes 1st person walking/bobbing animation super-slow
Bug #4797: Player sneaking and running stances are not accounted for when in air Bug #4797: Player sneaking and running stances are not accounted for when in air
Bug #4800: Standing collisions are not updated immediately when an object is teleported without a cell change Bug #4800: Standing collisions are not updated immediately when an object is teleported without a cell change
@ -135,6 +137,7 @@
Bug #5089: Swimming/Underwater creatures only swim around ground level Bug #5089: Swimming/Underwater creatures only swim around ground level
Bug #5092: NPCs with enchanted weapons play sound when out of charges Bug #5092: NPCs with enchanted weapons play sound when out of charges
Bug #5093: Hand to hand sound plays on knocked out enemies Bug #5093: Hand to hand sound plays on knocked out enemies
Bug #5097: String arguments can't be parsed as number literals in scripts
Bug #5099: Non-swimming enemies will enter water if player is water walking Bug #5099: Non-swimming enemies will enter water if player is water walking
Bug #5103: Sneaking state behavior is still inconsistent Bug #5103: Sneaking state behavior is still inconsistent
Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels
@ -163,6 +166,8 @@
Bug #5177: Editor: Unexplored map tiles get corrupted after a file with terrain is saved Bug #5177: Editor: Unexplored map tiles get corrupted after a file with terrain is saved
Bug #5182: OnPCEquip doesn't trigger on skipped beast race attempts to equip something not equippable by beasts Bug #5182: OnPCEquip doesn't trigger on skipped beast race attempts to equip something not equippable by beasts
Bug #5186: Equipped item enchantments don't affect creatures Bug #5186: Equipped item enchantments don't affect creatures
Bug #5190: On-strike enchantments can be applied to and used with non-projectile ranged weapons
Bug #5196: Dwarven ghosts do not use idle animations
Feature #1774: Handle AvoidNode Feature #1774: Handle AvoidNode
Feature #2229: Improve pathfinding AI Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls Feature #3025: Analogue gamepad movement controls
@ -183,6 +188,7 @@
Feature #4784: Launcher: Duplicate Content Lists Feature #4784: Launcher: Duplicate Content Lists
Feature #4812: Support NiSwitchNode Feature #4812: Support NiSwitchNode
Feature #4836: Daytime node switch Feature #4836: Daytime node switch
Feature #4840: Editor: Transient terrain change support
Feature #4859: Make water reflections more configurable Feature #4859: Make water reflections more configurable
Feature #4882: Support for NiPalette node Feature #4882: Support for NiPalette node
Feature #4887: Add openmw command option to set initial random seed Feature #4887: Add openmw command option to set initial random seed
@ -209,6 +215,8 @@
Feature #5132: Unique animations for different weapon types Feature #5132: Unique animations for different weapon types
Feature #5146: Safe Dispose corpse Feature #5146: Safe Dispose corpse
Feature #5147: Show spell magicka cost in spell buying window Feature #5147: Show spell magicka cost in spell buying window
Feature #5170: Editor: Land shape editing, land selection
Feature #5193: Weapon sheathing
Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4686: Upgrade media decoder to a more current FFmpeg API
Task #4695: Optimize Distant Terrain memory consumption Task #4695: Optimize Distant Terrain memory consumption
Task #4789: Optimize cell transitions Task #4789: Optimize cell transitions

View file

@ -40,6 +40,8 @@ New Features:
New Editor Features: New Editor Features:
- "Faction Ranks" table for "Faction" records (#4209) - "Faction Ranks" table for "Faction" records (#4209)
- Changes to height editing can be cancelled without changes to data (press esc to cancel) (#4840)
- Land heightmap/shape editing and vertex selection (#5170)
Bug Fixes: Bug Fixes:
- Scripted Items cannot be stacked anymore to avoid multiple script execution (#2969) - Scripted Items cannot be stacked anymore to avoid multiple script execution (#2969)

View file

@ -103,7 +103,7 @@ Options:
Build unit tests / Google test Build unit tests / Google test
-u -u
Configure for unity builds. Configure for unity builds.
-v <2013/2015/2017> -v <2013/2015/2017/2019>
Choose the Visual Studio version to use. Choose the Visual Studio version to use.
-n -n
Produce NMake makefiles instead of a Visual Studio solution. Produce NMake makefiles instead of a Visual Studio solution.
@ -247,10 +247,22 @@ if [ -z $CONFIGURATION ]; then
fi fi
if [ -z $VS_VERSION ]; then if [ -z $VS_VERSION ]; then
VS_VERSION="2013" VS_VERSION="2017"
fi fi
case $VS_VERSION in case $VS_VERSION in
16|16.0|2019 )
GENERATOR="Visual Studio 16 2019"
TOOLSET="vc142"
MSVC_REAL_VER="16"
MSVC_VER="14.2"
MSVC_YEAR="2015"
MSVC_DISPLAY_YEAR="2019"
BOOST_VER="1.71.0"
BOOST_VER_URL="1_71_0"
BOOST_VER_SDK="107100"
;;
15|15.0|2017 ) 15|15.0|2017 )
GENERATOR="Visual Studio 15 2017" GENERATOR="Visual Studio 15 2017"
TOOLSET="vc141" TOOLSET="vc141"
@ -258,6 +270,9 @@ case $VS_VERSION in
MSVC_VER="14.1" MSVC_VER="14.1"
MSVC_YEAR="2015" MSVC_YEAR="2015"
MSVC_DISPLAY_YEAR="2017" MSVC_DISPLAY_YEAR="2017"
BOOST_VER="1.67.0"
BOOST_VER_URL="1_67_0"
BOOST_VER_SDK="106700"
;; ;;
14|14.0|2015 ) 14|14.0|2015 )
@ -267,6 +282,9 @@ case $VS_VERSION in
MSVC_VER="14.0" MSVC_VER="14.0"
MSVC_YEAR="2015" MSVC_YEAR="2015"
MSVC_DISPLAY_YEAR="2015" MSVC_DISPLAY_YEAR="2015"
BOOST_VER="1.67.0"
BOOST_VER_URL="1_67_0"
BOOST_VER_SDK="106700"
;; ;;
12|12.0|2013 ) 12|12.0|2013 )
@ -276,6 +294,9 @@ case $VS_VERSION in
MSVC_VER="12.0" MSVC_VER="12.0"
MSVC_YEAR="2013" MSVC_YEAR="2013"
MSVC_DISPLAY_YEAR="2013" MSVC_DISPLAY_YEAR="2013"
BOOST_VER="1.58.0"
BOOST_VER_URL="1_58_0"
BOOST_VER_SDK="105800"
;; ;;
esac esac
@ -315,7 +336,7 @@ case $CONFIGURATION in
;; ;;
esac esac
if [ ${BITS} -eq 64 ]; then if [ $BITS -eq 64 ] && [ $MSVC_REAL_VER -lt 16 ]; then
GENERATOR="${GENERATOR} Win64" GENERATOR="${GENERATOR} Win64"
fi fi
@ -323,7 +344,15 @@ if [ -n "$NMAKE" ]; then
GENERATOR="NMake Makefiles" GENERATOR="NMake Makefiles"
fi fi
add_cmake_opts "-G\"$GENERATOR\"" if [ $MSVC_REAL_VER -ge 16 ]; then
if [ $BITS -eq 64 ]; then
add_cmake_opts "-G\"$GENERATOR\" -A x64"
else
add_cmake_opts "-G\"$GENERATOR\" -A Win32"
fi
else
add_cmake_opts "-G\"$GENERATOR\""
fi
if [ -n "$NMAKE" ]; then if [ -n "$NMAKE" ]; then
add_cmake_opts "-DCMAKE_BUILD_TYPE=${BUILD_CONFIG}" add_cmake_opts "-DCMAKE_BUILD_TYPE=${BUILD_CONFIG}"
@ -351,9 +380,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
# Boost # Boost
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
download "Boost 1.67.0" \ download "Boost ${BOOST_VER}" \
"https://sourceforge.net/projects/boost/files/boost-binaries/1.67.0/boost_1_67_0-msvc-${MSVC_VER}-${BITS}.exe" \ "https://sourceforge.net/projects/boost/files/boost-binaries/${BOOST_VER}/boost_${BOOST_VER_URL}-msvc-${MSVC_VER}-${BITS}.exe" \
"boost-1.67.0-msvc${MSVC_VER}-win${BITS}.exe" "boost-${BOOST_VER}-msvc${MSVC_VER}-win${BITS}.exe"
fi fi
# Bullet # Bullet
@ -444,13 +473,9 @@ echo
# Boost # Boost
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
printf "Boost 1.67.0... " printf "Boost ${BOOST_VER}... "
else else
if [ "${MSVC_VER}" -eq 12.0 ]; then printf "Boost ${BOOST_VER} AppVeyor... "
printf "Boost 1.58.0 AppVeyor... "
else
printf "Boost 1.67.0 AppVeyor... "
fi
fi fi
{ {
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
@ -468,13 +493,13 @@ fi
exit 1; exit 1;
fi fi
if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION 106700" Boost/boost/version.hpp > /dev/null; then if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION ${BOOST_VER_SDK}" Boost/boost/version.hpp > /dev/null; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf Boost rm -rf Boost
CI_EXTRA_INNO_OPTIONS="" CI_EXTRA_INNO_OPTIONS=""
[ -n "$CI" ] && CI_EXTRA_INNO_OPTIONS="//SUPPRESSMSGBOXES //LOG='boost_install.log'" [ -n "$CI" ] && CI_EXTRA_INNO_OPTIONS="//SUPPRESSMSGBOXES //LOG='boost_install.log'"
"${DEPS}/boost-1.67.0-msvc${MSVC_VER}-win${BITS}.exe" //DIR="${CWD_DRIVE_ROOT}" //VERYSILENT //NORESTART ${CI_EXTRA_INNO_OPTIONS} "${DEPS}/boost-${BOOST_VER}-msvc${MSVC_VER}-win${BITS}.exe" //DIR="${CWD_DRIVE_ROOT}" //VERYSILENT //NORESTART ${CI_EXTRA_INNO_OPTIONS}
mv "${CWD_DRIVE_ROOT_BASH}" "${BOOST_SDK}" mv "${CWD_DRIVE_ROOT_BASH}" "${BOOST_SDK}"
fi fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \ add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
@ -482,13 +507,10 @@ fi
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}" add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
echo Done. echo Done.
else else
# Appveyor unstable has all the boost we need already # Appveyor has all the boost we need already
if [ $MSVC_REAL_VER -eq 12 ]; then BOOST_SDK="c:/Libraries/boost_${BOOST_VER_URL}"
BOOST_SDK="c:/Libraries/boost_1_58_0"
else if [ $MSVC_REAL_VER -ge 15 ]; then
BOOST_SDK="c:/Libraries/boost_1_67_0"
fi
if [ $MSVC_REAL_VER -eq 15 ]; then
LIB_SUFFIX="1" LIB_SUFFIX="1"
else else
LIB_SUFFIX="0" LIB_SUFFIX="0"
@ -619,7 +641,7 @@ echo
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
printf "Qt 5.7.0... " printf "Qt 5.7.0... "
else else
printf "Qt 5.10 AppVeyor... " printf "Qt 5.13 AppVeyor... "
fi fi
{ {
if [ $BITS -eq 64 ]; then if [ $BITS -eq 64 ]; then
@ -657,7 +679,7 @@ fi
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll" add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
echo Done. echo Done.
else else
QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}" QT_SDK="C:/Qt/5.13/msvc2017${SUFFIX}"
add_cmake_opts -DDESIRED_QT_VERSION=5 \ add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK" -DCMAKE_PREFIX_PATH="$QT_SDK"

View file

@ -607,7 +607,7 @@ void Record<ESM::Container>::print()
std::cout << " Weight: " << mData.mWeight << std::endl; std::cout << " Weight: " << mData.mWeight << std::endl;
for (const ESM::ContItem &item : mData.mInventory.mList) for (const ESM::ContItem &item : mData.mInventory.mList)
std::cout << " Inventory: Count: " << Misc::StringUtils::format("%4d", item.mCount) std::cout << " Inventory: Count: " << Misc::StringUtils::format("%4d", item.mCount)
<< " Item: " << item.mItem.toString() << std::endl; << " Item: " << item.mItem << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl;
} }
@ -653,7 +653,7 @@ void Record<ESM::Creature>::print()
for (const ESM::ContItem &item : mData.mInventory.mList) for (const ESM::ContItem &item : mData.mInventory.mList)
std::cout << " Inventory: Count: " << Misc::StringUtils::format("%4d", item.mCount) std::cout << " Inventory: Count: " << Misc::StringUtils::format("%4d", item.mCount)
<< " Item: " << item.mItem.toString() << std::endl; << " Item: " << item.mItem << std::endl;
for (const std::string &spell : mData.mSpells.mList) for (const std::string &spell : mData.mSpells.mList)
std::cout << " Spell: " << spell << std::endl; std::cout << " Spell: " << spell << std::endl;
@ -1073,7 +1073,7 @@ void Record<ESM::NPC>::print()
for (const ESM::ContItem &item : mData.mInventory.mList) for (const ESM::ContItem &item : mData.mInventory.mList)
std::cout << " Inventory: Count: " << Misc::StringUtils::format("%4d", item.mCount) std::cout << " Inventory: Count: " << Misc::StringUtils::format("%4d", item.mCount)
<< " Item: " << item.mItem.toString() << std::endl; << " Item: " << item.mItem << std::endl;
for (const std::string &spell : mData.mSpells.mList) for (const std::string &spell : mData.mSpells.mList)
std::cout << " Spell: " << spell << std::endl; std::cout << " Spell: " << spell << std::endl;
@ -1192,7 +1192,7 @@ void Record<ESM::Region>::print()
if (!mData.mSleepList.empty()) if (!mData.mSleepList.empty())
std::cout << " Sleep List: " << mData.mSleepList << std::endl; std::cout << " Sleep List: " << mData.mSleepList << std::endl;
for (const ESM::Region::SoundRef &soundref : mData.mSoundList) for (const ESM::Region::SoundRef &soundref : mData.mSoundList)
std::cout << " Sound: " << (int)soundref.mChance << " = " << soundref.mSound.toString() << std::endl; std::cout << " Sound: " << (int)soundref.mChance << " = " << soundref.mSound << std::endl;
} }
template<> template<>

View file

@ -9,7 +9,7 @@ namespace ESSImport
void convertSCPT(const SCPT &scpt, ESM::GlobalScript &out) void convertSCPT(const SCPT &scpt, ESM::GlobalScript &out)
{ {
out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName.toString()); out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName);
out.mRunning = scpt.mRunning; out.mRunning = scpt.mRunning;
convertSCRI(scpt.mSCRI, out.mLocals); convertSCRI(scpt.mSCRI, out.mLocals);
} }

View file

@ -17,7 +17,7 @@ namespace ESSImport
esm.getHT(contItem); esm.getHT(contItem);
InventoryItem item; InventoryItem item;
item.mId = contItem.mItem.toString(); item.mId = contItem.mItem;
item.mCount = contItem.mCount; item.mCount = contItem.mCount;
item.mRelativeEquipmentSlot = -1; item.mRelativeEquipmentSlot = -1;
item.mLockLevel = 0; item.mLockLevel = 0;

View file

@ -95,7 +95,7 @@ bool Launcher::DataFilesPage::loadSettings()
qDebug() << "The current profile is: " << currentProfile; qDebug() << "The current profile is: " << currentProfile;
foreach (const QString &item, profiles) for (const QString &item : profiles)
addProfile (item, false); addProfile (item, false);
// Hack: also add the current profile // Hack: also add the current profile
@ -114,7 +114,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
if (!mDataLocal.isEmpty()) if (!mDataLocal.isEmpty())
paths.insert(0, mDataLocal); paths.insert(0, mDataLocal);
foreach(const QString &path, paths) for (const QString &path : paths)
mSelector->addFiles(path); mSelector->addFiles(path);
PathIterator pathIterator(paths); PathIterator pathIterator(paths);
@ -127,7 +127,7 @@ QStringList Launcher::DataFilesPage::filesInProfile(const QString& profileName,
QStringList files = mLauncherSettings.getContentListFiles(profileName); QStringList files = mLauncherSettings.getContentListFiles(profileName);
QStringList filepaths; QStringList filepaths;
foreach(const QString& file, files) for (const QString& file : files)
{ {
QString filepath = pathIterator.findFirstPath(file); QString filepath = pathIterator.findFirstPath(file);
@ -152,7 +152,8 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile)
mLauncherSettings.setCurrentContentListName(ui.profilesComboBox->currentText()); mLauncherSettings.setCurrentContentListName(ui.profilesComboBox->currentText());
QStringList fileNames; QStringList fileNames;
foreach(const ContentSelectorModel::EsmFile *item, items) { for (const ContentSelectorModel::EsmFile *item : items)
{
fileNames.append(item->fileName()); fileNames.append(item->fileName());
} }
mLauncherSettings.setContentList(profileName, fileNames); mLauncherSettings.setContentList(profileName, fileNames);
@ -164,7 +165,8 @@ QStringList Launcher::DataFilesPage::selectedFilePaths()
//retrieve the files selected for the profile //retrieve the files selected for the profile
ContentSelectorModel::ContentFileList items = mSelector->selectedFiles(); ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
QStringList filePaths; QStringList filePaths;
foreach(const ContentSelectorModel::EsmFile *item, items) { for (const ContentSelectorModel::EsmFile *item : items)
{
filePaths.append(item->filePath()); filePaths.append(item->filePath());
} }
return filePaths; return filePaths;

View file

@ -280,7 +280,8 @@ bool Launcher::MainDialog::setupLauncherSettings()
paths.append(QString(Config::LauncherSettings::sLauncherConfigFileName)); paths.append(QString(Config::LauncherSettings::sLauncherConfigFileName));
paths.append(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName)); paths.append(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName));
foreach (const QString &path, paths) { for (const QString &path : paths)
{
qDebug() << "Loading config file:" << path.toUtf8().constData(); qDebug() << "Loading config file:" << path.toUtf8().constData();
QFile file(path); QFile file(path);
if (file.exists()) { if (file.exists()) {
@ -338,7 +339,8 @@ bool Launcher::MainDialog::setupGameSettings()
paths.append(localPath + QString("openmw.cfg")); paths.append(localPath + QString("openmw.cfg"));
paths.append(userPath + QString("openmw.cfg")); paths.append(userPath + QString("openmw.cfg"));
foreach (const QString &path2, paths) { for (const QString &path2 : paths)
{
qDebug() << "Loading config file:" << path2.toUtf8().constData(); qDebug() << "Loading config file:" << path2.toUtf8().constData();
file.setFileName(path2); file.setFileName(path2);
@ -366,7 +368,8 @@ bool Launcher::MainDialog::setupGameData()
QStringList dataDirs; QStringList dataDirs;
// Check if the paths actually contain data files // Check if the paths actually contain data files
foreach (const QString path3, mGameSettings.getDataDirs()) { for (const QString& path3 : mGameSettings.getDataDirs())
{
QDir dir(path3); QDir dir(path3);
QStringList filters; QStringList filters;
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";

View file

@ -61,7 +61,8 @@ Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg,
// Detect Morrowind configuration files // Detect Morrowind configuration files
QStringList iniPaths; QStringList iniPaths;
foreach (const QString &path, mGameSettings.getDataDirs()) { for (const QString &path : mGameSettings.getDataDirs())
{
QDir dir(path); QDir dir(path);
dir.setPath(dir.canonicalPath()); // Resolve symlinks dir.setPath(dir.canonicalPath()); // Resolve symlinks

View file

@ -82,14 +82,14 @@ opencs_units_noqt (view/world
opencs_units (view/widget opencs_units (view/widget
scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton
scenetooltoggle2 scenetooltexturebrush completerpopup coloreditor colorpickerpopup droplineedit scenetooltoggle2 scenetooltexturebrush scenetoolshapebrush completerpopup coloreditor colorpickerpopup droplineedit
) )
opencs_units (view/render opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode instanceselectionmode instancemovemode previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
cellwater terraintexturemode actor terrainselection cellwater terraintexturemode actor terrainselection terrainshapemode
) )
opencs_units_noqt (view/render opencs_units_noqt (view/render

View file

@ -228,7 +228,7 @@ void CS::Editor::openFiles (const boost::filesystem::path &savePath, const std::
if(discoveredFiles.empty()) if(discoveredFiles.empty())
{ {
foreach(const QString &path, mFileDialog.selectedFilePaths()) for (const QString &path : mFileDialog.selectedFilePaths())
files.push_back(path.toUtf8().constData()); files.push_back(path.toUtf8().constData());
} }
else else
@ -245,7 +245,7 @@ void CS::Editor::createNewFile (const boost::filesystem::path &savePath)
{ {
std::vector<boost::filesystem::path> files; std::vector<boost::filesystem::path> files;
foreach (const QString &path, mFileDialog.selectedFilePaths()) { for (const QString &path : mFileDialog.selectedFilePaths()) {
files.push_back(path.toUtf8().constData()); files.push_back(path.toUtf8().constData());
} }

View file

@ -170,7 +170,7 @@ void CSMPrefs::State::declare()
"list go to the first/last item"); "list go to the first/last item");
declareCategory ("3D Scene Input"); declareCategory ("3D Scene Input");
declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0); declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0);
declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0); declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0);
declareSeparator(); declareSeparator();
@ -178,7 +178,7 @@ void CSMPrefs::State::declare()
declareDouble ("p-navi-free-sensitivity", "Free Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0); declareDouble ("p-navi-free-sensitivity", "Free Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-free-invert", "Invert Free Camera Mouse Input", false); declareBool ("p-navi-free-invert", "Invert Free Camera Mouse Input", false);
declareDouble ("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0); declareDouble ("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0);
declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28); declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28);
declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0); declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0);
declareSeparator(); declareSeparator();
@ -242,12 +242,24 @@ void CSMPrefs::State::declare()
addValues (insertOutsideCell); addValues (insertOutsideCell);
declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert). declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert).
addValues (insertOutsideVisibleCell); addValues (insertOutsideVisibleCell);
declareEnum ("outside-landedit", "Handling land edit outside of cells", createAndLandEdit). declareEnum ("outside-landedit", "Handling terrain edit outside of cells", createAndLandEdit).
setTooltip("Behavior of terrain editing, if land editing brush reaches an area without cell record.").
addValues (landeditOutsideCell); addValues (landeditOutsideCell);
declareEnum ("outside-visible-landedit", "Handling land edit outside of visible cells", showAndLandEdit). declareEnum ("outside-visible-landedit", "Handling terrain edit outside of visible cells", showAndLandEdit).
setTooltip("Behavior of terrain editing, if land editing brush reaches an area that is not currently visible.").
addValues (landeditOutsideVisibleCell); addValues (landeditOutsideVisibleCell);
declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50). declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50).
setMin (1); setMin (1);
declareInt ("shapebrush-maximumsize", "Maximum height edit brush size", 100).
setTooltip("Setting for the slider range of brush size in terrain height editing.").
setMin (1);
declareBool ("landedit-post-smoothpainting", "Smooth land after painting height", false).
setTooltip("Raise and lower tools will leave bumpy finish without this option");
declareDouble ("landedit-post-smoothstrength", "Smoothing strength (post-edit)", 0.25).
setTooltip("If smoothing land after painting height is used, this is the percentage of smooth applied afterwards. "
"Negative values may be used to roughen instead of smooth.").
setMin (-1).
setMax (1);
declareBool ("open-list-view", "Open displays list view", false). declareBool ("open-list-view", "Open displays list view", false).
setTooltip ("When opening a reference from the scene view, it will open the" setTooltip ("When opening a reference from the scene view, it will open the"
" instance list view instead of the individual instance record view."); " instance list view instead of the individual instance record view.");

View file

@ -910,7 +910,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
{ {
for (size_t i = 0; i < itemList.size(); ++i) for (size_t i = 0; i < itemList.size(); ++i)
{ {
std::string itemName = itemList[i].mItem.toString(); std::string itemName = itemList[i].mItem;
CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName);
if (localIndex.first == -1) if (localIndex.first == -1)

View file

@ -45,7 +45,7 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)
for (const ESM::Region::SoundRef& sound : region.mSoundList) for (const ESM::Region::SoundRef& sound : region.mSoundList)
{ {
if (sound.mChance > 100) if (sound.mChance > 100)
messages.add(id, "Chance of '" + sound.mSound.toString() + "' sound to play is over 100 percent", "", CSMDoc::Message::Severity_Warning); messages.add(id, "Chance of '" + sound.mSound + "' sound to play is over 100 percent", "", CSMDoc::Message::Severity_Warning);
} }
/// \todo check data members that can't be edited in the table view /// \todo check data members that can't be edited in the table view

View file

@ -538,7 +538,7 @@ namespace CSMWorld
for (auto& item : npc.mInventory.mList) for (auto& item : npc.mInventory.mList)
{ {
if (item.mCount <= 0) continue; if (item.mCount <= 0) continue;
std::string itemId = item.mItem.toString(); std::string itemId = item.mItem;
addNpcItem(itemId, data); addNpcItem(itemId, data);
} }
} }

View file

@ -89,7 +89,7 @@ namespace CSMWorld
DataType values(Size, 0); DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_WNAM)) if (land.mDataTypes & Land::DATA_WNAM)
{ {
for (int i = 0; i < Size; ++i) for (int i = 0; i < Size; ++i)
values[i] = land.mWnam[i]; values[i] = land.mWnam[i];

View file

@ -396,7 +396,7 @@ namespace CSMWorld
ESM::Region::SoundRef soundRef = soundList[subRowIndex]; ESM::Region::SoundRef soundRef = soundList[subRowIndex];
switch (subColIndex) switch (subColIndex)
{ {
case 0: return QString(soundRef.mSound.toString().c_str()); case 0: return QString(soundRef.mSound.c_str());
case 1: return soundRef.mChance; case 1: return soundRef.mChance;
default: throw std::runtime_error("Region sounds subcolumn index out of range"); default: throw std::runtime_error("Region sounds subcolumn index out of range");
} }

View file

@ -1257,7 +1257,7 @@ namespace CSMWorld
switch (subColIndex) switch (subColIndex)
{ {
case 0: return QString::fromUtf8(content.mItem.toString().c_str()); case 0: return QString::fromUtf8(content.mItem.c_str());
case 1: return content.mCount; case 1: return content.mCount;
default: default:
throw std::runtime_error("Trying to access non-existing column in the nested table!"); throw std::runtime_error("Trying to access non-existing column in the nested table!");

View file

@ -47,7 +47,7 @@ QStringList CSVDoc::FileDialog::selectedFilePaths()
{ {
QStringList filePaths; QStringList filePaths;
foreach (ContentSelectorModel::EsmFile *file, mSelector->selectedFiles() ) for (ContentSelectorModel::EsmFile *file : mSelector->selectedFiles() )
filePaths.append(file->filePath()); filePaths.append(file->filePath());
return filePaths; return filePaths;

View file

@ -404,7 +404,7 @@ void CSVDoc::View::updateSubViewIndices(SubView *view)
updateTitle(); updateTitle();
foreach (SubView *subView, mSubViews) for (SubView *subView : mSubViews)
{ {
if (!subView->isFloating()) if (!subView->isFloating())
{ {
@ -546,7 +546,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
// User setting to reuse sub views (on a per top level view basis) // User setting to reuse sub views (on a per top level view basis)
if (windows["reuse"].isTrue()) if (windows["reuse"].isTrue())
{ {
foreach(SubView *sb, mSubViews) for (SubView *sb : mSubViews)
{ {
bool isSubViewReferenceable = bool isSubViewReferenceable =
sb->getUniversalId().getType() == CSMWorld::UniversalId::Type_Referenceable; sb->getUniversalId().getType() == CSMWorld::UniversalId::Type_Referenceable;
@ -975,7 +975,7 @@ void CSVDoc::View::resizeViewHeight (int height)
void CSVDoc::View::toggleShowStatusBar (bool show) void CSVDoc::View::toggleShowStatusBar (bool show)
{ {
foreach (QObject *view, mSubViewWindow.children()) for (QObject *view : mSubViewWindow.children())
{ {
if (CSVDoc::SubView *subView = dynamic_cast<CSVDoc::SubView *> (view)) if (CSVDoc::SubView *subView = dynamic_cast<CSVDoc::SubView *> (view))
subView->setStatusBar (show); subView->setStatusBar (show);

View file

@ -134,7 +134,7 @@ void CSVRender::Cell::updateLand()
else else
{ {
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode,
mData.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain)); mData.getResourceSystem().get(), mTerrainStorage, Mask_Terrain));
} }
mTerrain->loadCell(esmLand.mX, esmLand.mY); mTerrain->loadCell(esmLand.mX, esmLand.mY);
@ -149,7 +149,6 @@ void CSVRender::Cell::updateLand()
} }
// No land data // No land data
mLandDeleted = true;
unloadLand(); unloadLand();
} }
@ -169,6 +168,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
{ {
std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id); std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id);
mTerrainStorage = new TerrainStorage(mData);
if (result.second) if (result.second)
mCoordinates = result.first; mCoordinates = result.first;
@ -347,6 +348,28 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
return addObjects (start, end); return addObjects (start, end);
} }
void CSVRender::Cell::setAlteredHeight(int inCellX, int inCellY, float height)
{
mTerrainStorage->setAlteredHeight(inCellX, inCellY, height);
mUpdateLand = true;
}
float CSVRender::Cell::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY)
{
return mTerrainStorage->getSumOfAlteredAndTrueHeight(cellX, cellY, inCellX, inCellY);
}
float* CSVRender::Cell::getAlteredHeight(int inCellX, int inCellY)
{
return mTerrainStorage->getAlteredHeight(inCellX, inCellY);
}
void CSVRender::Cell::resetAlteredHeights()
{
mTerrainStorage->resetHeights();
mUpdateLand = true;
}
void CSVRender::Cell::pathgridModified() void CSVRender::Cell::pathgridModified()
{ {
if (mPathgrid) if (mPathgrid)

View file

@ -9,6 +9,7 @@
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include "../../model/world/cellcoordinates.hpp" #include "../../model/world/cellcoordinates.hpp"
#include "terrainstorage.hpp"
class QModelIndex; class QModelIndex;
@ -58,6 +59,7 @@ namespace CSVRender
int mSubMode; int mSubMode;
unsigned int mSubModeElementMask; unsigned int mSubModeElementMask;
bool mUpdateLand, mLandDeleted; bool mUpdateLand, mLandDeleted;
TerrainStorage *mTerrainStorage;
/// Ignored if cell does not have an object with the given ID. /// Ignored if cell does not have an object with the given ID.
/// ///
@ -118,6 +120,14 @@ namespace CSVRender
/// this cell? /// this cell?
bool referenceAdded (const QModelIndex& parent, int start, int end); bool referenceAdded (const QModelIndex& parent, int start, int end);
void setAlteredHeight(int inCellX, int inCellY, float height);
float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY);
float* getAlteredHeight(int inCellX, int inCellY);
void resetAlteredHeights();
void pathgridModified(); void pathgridModified();
void pathgridRemoved(); void pathgridRemoved();

View file

@ -25,6 +25,7 @@
#include "cameracontroller.hpp" #include "cameracontroller.hpp"
#include "cellarrow.hpp" #include "cellarrow.hpp"
#include "terraintexturemode.hpp" #include "terraintexturemode.hpp"
#include "terrainshapemode.hpp"
bool CSVRender::PagedWorldspaceWidget::adjustCells() bool CSVRender::PagedWorldspaceWidget::adjustCells()
{ {
@ -137,11 +138,9 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
/// \todo replace EditMode with suitable subclasses /// \todo replace EditMode with suitable subclasses
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), new TerrainShapeMode (this, mRootNode, tool), "terrain-shape");
"terrain-shape");
tool->addButton ( tool->addButton (
new TerrainTextureMode (this, mRootNode, tool), new TerrainTextureMode (this, mRootNode, tool), "terrain-texture");
"terrain-texture");
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"),
"terrain-vertex"); "terrain-vertex");
@ -791,6 +790,36 @@ CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& poi
return 0; return 0;
} }
CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const CSMWorld::CellCoordinates& coords) const
{
std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator searchResult = mCells.find(coords);
if (searchResult != mCells.end())
return searchResult->second;
else
return nullptr;
}
void CSVRender::PagedWorldspaceWidget::setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height)
{
std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords);
if (searchResult != mCells.end())
searchResult->second->setAlteredHeight(inCellX, inCellY, height);
}
float* CSVRender::PagedWorldspaceWidget::getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY)
{
std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords);
if (searchResult != mCells.end())
return searchResult->second->getAlteredHeight(inCellX, inCellY);
return nullptr;
}
void CSVRender::PagedWorldspaceWidget::resetAllAlteredHeights()
{
for (const auto& cell : mCells)
cell.second->resetAlteredHeights();
}
std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::PagedWorldspaceWidget::getSelection ( std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::PagedWorldspaceWidget::getSelection (
unsigned int elementMask) const unsigned int elementMask) const
{ {

View file

@ -124,6 +124,14 @@ namespace CSVRender
virtual Cell* getCell(const osg::Vec3d& point) const; virtual Cell* getCell(const osg::Vec3d& point) const;
virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const;
void setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height);
float* getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY);
void resetAllAlteredHeights();
virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask) virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)
const; const;

View file

@ -249,13 +249,11 @@ int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global ver
int localX = x - cellX * (ESM::Land::LAND_SIZE - 1); int localX = x - cellX * (ESM::Land::LAND_SIZE - 1);
int localY = y - cellY * (ESM::Land::LAND_SIZE - 1); int localY = y - cellY * (ESM::Land::LAND_SIZE - 1);
std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY); CSMWorld::CellCoordinates coords (cellX, cellY);
CSMDoc::Document& document = mWorldspaceWidget->getDocument(); float landHeight = 0.f;
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> ( if (CSVRender::Cell* cell = dynamic_cast<CSVRender::Cell*>(mWorldspaceWidget->getCell(coords)))
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); landHeight = cell->getSumOfAlteredAndTrueHeight(cellX, cellY, localX, localY);
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();
return mPointer[localY*ESM::Land::LAND_SIZE + localX]; return landHeight;
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,190 @@
#ifndef CSV_RENDER_TERRAINSHAPEMODE_H
#define CSV_RENDER_TERRAINSHAPEMODE_H
#include "editmode.hpp"
#include <string>
#include <memory>
#include <QWidget>
#include <QEvent>
#ifndef Q_MOC_RUN
#include "../../model/world/data.hpp"
#include "../../model/world/land.hpp"
#include "../../model/doc/document.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/landtexture.hpp"
#include "../widget/brushshapes.hpp"
#endif
#include "terrainselection.hpp"
namespace CSVWidget
{
class SceneToolShapeBrush;
}
namespace CSVRender
{
class PagedWorldspaceWidget;
/// \brief EditMode for handling the terrain shape editing
class TerrainShapeMode : public EditMode
{
Q_OBJECT
public:
enum InteractionType
{
InteractionType_PrimaryEdit,
InteractionType_PrimarySelect,
InteractionType_SecondaryEdit,
InteractionType_SecondarySelect,
InteractionType_None
};
enum ShapeEditTool
{
ShapeEditTool_Drag = 0,
ShapeEditTool_PaintToRaise = 1,
ShapeEditTool_PaintToLower = 2,
ShapeEditTool_Smooth = 3,
ShapeEditTool_Flatten = 4
};
/// Editmode for terrain shape grid
TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);
void primaryOpenPressed (const WorldspaceHitResult& hit) final;
/// Create single command for one-click shape editing
void primaryEditPressed (const WorldspaceHitResult& hit) final;
/// Open brush settings window
void primarySelectPressed(const WorldspaceHitResult&) final;
void secondarySelectPressed(const WorldspaceHitResult&) final;
void activate(CSVWidget::SceneToolbar*) final;
void deactivate(CSVWidget::SceneToolbar*) final;
/// Start shape editing command macro
bool primaryEditStartDrag (const QPoint& pos) final;
bool secondaryEditStartDrag (const QPoint& pos) final;
bool primarySelectStartDrag (const QPoint& pos) final;
bool secondarySelectStartDrag (const QPoint& pos) final;
/// Handle shape edit behavior during dragging
void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) final;
/// End shape editing command macro
void dragCompleted(const QPoint& pos) final;
/// Cancel shape editing, and reset all pending changes
void dragAborted() final;
void dragWheel (int diff, double speedFactor) final;
void dragMoveEvent (QDragMoveEvent *event) final;
private:
/// Remove duplicates and sort mAlteredCells, then limitAlteredHeights forward and reverse
void sortAndLimitAlteredCells();
/// Move pending alteredHeights changes to omwgame/omwaddon -data
void applyTerrainEditChanges();
/// Handle brush mechanics for shape editing
void editTerrainShapeGrid (const std::pair<int, int>& vertexCoords, bool dragOperation);
/// Calculate height, when aiming for bump-shaped terrain change
float calculateBumpShape(float distance, int radius, float height);
/// set the target height for flatten tool
void setFlattenToolTargetHeight(const WorldspaceHitResult& hit);
/// Do a single height alteration for transient shape edit map
void alterHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float alteredHeight, bool useTool = true);
/// Do a single smoothing height alteration for transient shape edit map
void smoothHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength);
/// Do a single flattening height alteration for transient shape edit map
void flattenHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength, int targetHeight);
/// Get altered height values around one vertex
void updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* thisHeight,
float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight,
float* rightHeight, float* rightAlteredHeight, float* downHeight, float* downAlteredHeight);
///Limit steepness based on either X or Y and return false if steepness is limited
void compareAndLimit(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* limitedAlteredHeightXAxis,
float* limitedAlteredHeightYAxis, bool* steepnessIsWithinLimits);
/// Check that the edit doesn't break save format limits, fix if necessary, return true if slope steepness is within limits
bool limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode = false);
/// Handle brush mechanics for terrain shape selection
void selectTerrainShapes (const std::pair<int, int>& vertexCoords, unsigned char selectMode, bool dragOperation);
/// Push terrain shape edits to command macro
void pushEditToCommand (const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, const std::string& cellId);
/// Push land normals edits to command macro
void pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, const std::string& cellId);
/// Generate new land map LOD
void pushLodToCommand(const CSMWorld::LandMapLodColumn::DataType& newLandMapLod, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, const std::string& cellId);
bool noCell(const std::string& cellId);
bool noLand(const std::string& cellId);
bool noLandLoaded(const std::string& cellId);
bool isLandLoaded(const std::string& cellId);
/// Create new blank height record and new normals, if there are valid adjancent cell, take sample points and set the average height based on that
void createNewLandData(const CSMWorld::CellCoordinates& cellCoords);
/// Create new cell and land if needed, only user tools may ask for opening new cells (useTool == false is for automated land changes)
bool allowLandShapeEditing(const std::string& textureFileName, bool useTool = true);
/// Bind the edging vertice to the values of the adjancent cells
void fixEdges(CSMWorld::CellCoordinates cellCoords);
std::string mBrushTexture;
int mBrushSize = 1;
CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point;
std::vector<std::pair<int, int>> mCustomBrushShape;
CSVWidget::SceneToolShapeBrush *mShapeBrushScenetool = nullptr;
int mDragMode = InteractionType_None;
osg::Group* mParentNode;
bool mIsEditing = false;
std::unique_ptr<TerrainSelection> mTerrainShapeSelection;
int mTotalDiffY = 0;
std::vector<CSMWorld::CellCoordinates> mAlteredCells;
osg::Vec3d mEditingPos;
int mShapeEditTool = ShapeEditTool_Drag;
int mShapeEditToolStrength = 8;
int mTargetHeight = 0;
PagedWorldspaceWidget& getPagedWorldspaceWidget();
public slots:
void setBrushSize(int brushSize);
void setBrushShape(CSVWidget::BrushShape brushShape);
void setShapeEditTool(int shapeEditTool);
void setShapeEditToolStrength(int shapeEditToolStrength);
};
}
#endif

View file

@ -3,13 +3,15 @@
#include "../../model/world/land.hpp" #include "../../model/world/land.hpp"
#include "../../model/world/landtexture.hpp" #include "../../model/world/landtexture.hpp"
#include <components/esmterrain/storage.hpp>
namespace CSVRender namespace CSVRender
{ {
TerrainStorage::TerrainStorage(const CSMWorld::Data &data) TerrainStorage::TerrainStorage(const CSMWorld::Data &data)
: ESMTerrain::Storage(data.getResourceSystem()->getVFS()) : ESMTerrain::Storage(data.getResourceSystem()->getVFS())
, mData(data) , mData(data)
{ {
resetHeights();
} }
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY) osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY)
@ -33,10 +35,118 @@ namespace CSVRender
return &mData.getLandTextures().getRecord(row).get(); return &mData.getLandTextures().getRecord(row).get();
} }
void TerrainStorage::setAlteredHeight(int inCellX, int inCellY, float height)
{
mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] = height - fmod(height, 8); //Limit to divisible by 8 to avoid cell seam breakage
}
void TerrainStorage::resetHeights()
{
std::fill(std::begin(mAlteredHeight), std::end(mAlteredHeight), 0);
}
float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY)
{
float height = 0.f;
osg::ref_ptr<const ESMTerrain::LandObject> land = getLand (cellX, cellY);
if (land)
{
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
if (data) height = getVertexHeight(data, inCellX, inCellY);
}
else return height;
return mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] + height;
}
float* TerrainStorage::getAlteredHeight(int inCellX, int inCellY)
{
return &mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX];
}
void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY)
{ {
// not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells // not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells
throw std::runtime_error("getBounds not implemented"); throw std::runtime_error("getBounds not implemented");
} }
int TerrainStorage::getThisHeight(int col, int row, const ESM::Land::LandData *heightData) const
{
return heightData->mHeights[col*ESM::Land::LAND_SIZE + row] +
mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)];
}
int TerrainStorage::getLeftHeight(int col, int row, const ESM::Land::LandData *heightData) const
{
return heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] +
mAlteredHeight[static_cast<unsigned int>((col)*ESM::Land::LAND_SIZE + row - 1)];
}
int TerrainStorage::getRightHeight(int col, int row, const ESM::Land::LandData *heightData) const
{
return heightData->mHeights[col*ESM::Land::LAND_SIZE + row + 1] +
mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row + 1)];
}
int TerrainStorage::getUpHeight(int col, int row, const ESM::Land::LandData *heightData) const
{
return heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] +
mAlteredHeight[static_cast<unsigned int>((col - 1)*ESM::Land::LAND_SIZE + row)];
}
int TerrainStorage::getDownHeight(int col, int row, const ESM::Land::LandData *heightData) const
{
return heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] +
mAlteredHeight[static_cast<unsigned int>((col + 1)*ESM::Land::LAND_SIZE + row)];
}
int TerrainStorage::getHeightDifferenceToLeft(int col, int row, const ESM::Land::LandData *heightData) const
{
return abs(getThisHeight(col, row, heightData) - getLeftHeight(col, row, heightData));
}
int TerrainStorage::getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData *heightData) const
{
return abs(getThisHeight(col, row, heightData) - getRightHeight(col, row, heightData));
}
int TerrainStorage::getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData *heightData) const
{
return abs(getThisHeight(col, row, heightData) - getUpHeight(col, row, heightData));
}
int TerrainStorage::getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData *heightData) const
{
return abs(getThisHeight(col, row, heightData) - getDownHeight(col, row, heightData));
}
bool TerrainStorage::leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const
{
return getHeightDifferenceToLeft(col, row, heightData) >= heightWarningLimit ||
getHeightDifferenceToUp(col, row, heightData) >= heightWarningLimit;
}
bool TerrainStorage::rightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const
{
return getHeightDifferenceToRight(col, row, heightData) >= heightWarningLimit ||
getHeightDifferenceToDown(col, row, heightData) >= heightWarningLimit;
}
void TerrainStorage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const
{
// Highlight broken height changes
int heightWarningLimit = 1024;
if (((col > 0 && row > 0) && leftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData)) ||
((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && rightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData)))
{
color.r() = 255;
color.g() = 0;
color.b() = 0;
}
}
float TerrainStorage::getAlteredHeight(int col, int row) const
{
return mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)];
}
} }

View file

@ -1,13 +1,14 @@
#ifndef OPENCS_RENDER_TERRAINSTORAGE_H #ifndef OPENCS_RENDER_TERRAINSTORAGE_H
#define OPENCS_RENDER_TERRAINSTORAGE_H #define OPENCS_RENDER_TERRAINSTORAGE_H
#include <array>
#include <components/esmterrain/storage.hpp> #include <components/esmterrain/storage.hpp>
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
namespace CSVRender namespace CSVRender
{ {
/** /**
* @brief A bridge between the terrain component and OpenCS's terrain data storage. * @brief A bridge between the terrain component and OpenCS's terrain data storage.
*/ */
@ -15,13 +16,34 @@ namespace CSVRender
{ {
public: public:
TerrainStorage(const CSMWorld::Data& data); TerrainStorage(const CSMWorld::Data& data);
void setAlteredHeight(int inCellX, int inCellY, float heightMap);
void resetHeights();
float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY);
float* getAlteredHeight(int inCellX, int inCellY);
private: private:
const CSMWorld::Data& mData; const CSMWorld::Data& mData;
std::array<float, ESM::Land::LAND_SIZE * ESM::Land::LAND_SIZE> mAlteredHeight;
virtual osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) override; osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) final;
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override; const ESM::LandTexture* getLandTexture(int index, short plugin) final;
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; void getBounds(float& minX, float& maxX, float& minY, float& maxY) final;
int getThisHeight(int col, int row, const ESM::Land::LandData *heightData) const;
int getLeftHeight(int col, int row, const ESM::Land::LandData *heightData) const;
int getRightHeight(int col, int row, const ESM::Land::LandData *heightData) const;
int getUpHeight(int col, int row, const ESM::Land::LandData *heightData) const;
int getDownHeight(int col, int row, const ESM::Land::LandData *heightData) const;
int getHeightDifferenceToLeft(int col, int row, const ESM::Land::LandData *heightData) const;
int getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData *heightData) const;
int getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData *heightData) const;
int getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData *heightData) const;
bool leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const;
bool rightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const;
void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const final;
float getAlteredHeight(int col, int row) const final;
}; };
} }

View file

@ -150,6 +150,11 @@ CSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const osg::Vec3d& p
return mCell.get(); return mCell.get();
} }
CSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const CSMWorld::CellCoordinates& coords) const
{
return mCell.get();
}
std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::UnpagedWorldspaceWidget::getSelection ( std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::UnpagedWorldspaceWidget::getSelection (
unsigned int elementMask) const unsigned int elementMask) const
{ {

View file

@ -17,6 +17,7 @@ namespace CSMDoc
namespace CSMWorld namespace CSMWorld
{ {
class IdTable; class IdTable;
class CellCoordinates;
} }
namespace CSVRender namespace CSVRender
@ -63,6 +64,8 @@ namespace CSVRender
virtual Cell* getCell(const osg::Vec3d& point) const; virtual Cell* getCell(const osg::Vec3d& point) const;
virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const;
virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask) virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)
const; const;

View file

@ -17,6 +17,7 @@ namespace CSMPrefs
namespace CSMWorld namespace CSMWorld
{ {
class CellCoordinates;
class UniversalId; class UniversalId;
} }
@ -170,6 +171,8 @@ namespace CSVRender
/// \note Returns the cell if it exists, otherwise a null pointer /// \note Returns the cell if it exists, otherwise a null pointer
virtual Cell* getCell(const osg::Vec3d& point) const = 0; virtual Cell* getCell(const osg::Vec3d& point) const = 0;
virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const = 0;
virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask) virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)
const = 0; const = 0;

View file

@ -0,0 +1,14 @@
#ifndef CSV_WIDGET_BRUSHSHAPES_H
#define CSV_WIDGET_BRUSHSHAPES_H
namespace CSVWidget
{
enum BrushShape
{
BrushShape_Point,
BrushShape_Square,
BrushShape_Circle,
BrushShape_Custom
};
}
#endif

View file

@ -0,0 +1,263 @@
#include "scenetoolshapebrush.hpp"
#include <QFrame>
#include <QIcon>
#include <QTableWidget>
#include <QHBoxLayout>
#include <QWidget>
#include <QComboBox>
#include <QSpinBox>
#include <QGroupBox>
#include <QSlider>
#include <QEvent>
#include <QDropEvent>
#include <QButtonGroup>
#include <QVBoxLayout>
#include <QDragEnterEvent>
#include <QDrag>
#include <QTableWidget>
#include <QHeaderView>
#include <QApplication>
#include <QSizePolicy>
#include "brushshapes.hpp"
#include "scenetool.hpp"
#include "../../model/doc/document.hpp"
#include "../../model/prefs/state.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/data.hpp"
#include "../../model/world/idcollection.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/landtexture.hpp"
#include "../../model/world/universalid.hpp"
CSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, QWidget *parent)
: QGroupBox(title, parent)
{
mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides);
mBrushSizeSlider->setTickInterval(10);
mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt());
mBrushSizeSlider->setSingleStep(1);
mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt());
mBrushSizeSpinBox->setSingleStep(1);
QHBoxLayout *layoutSliderSize = new QHBoxLayout;
layoutSliderSize->addWidget(mBrushSizeSlider);
layoutSliderSize->addWidget(mBrushSizeSpinBox);
connect(mBrushSizeSlider, SIGNAL(valueChanged(int)), mBrushSizeSpinBox, SLOT(setValue(int)));
connect(mBrushSizeSpinBox, SIGNAL(valueChanged(int)), mBrushSizeSlider, SLOT(setValue(int)));
setLayout(layoutSliderSize);
}
CSVWidget::ShapeBrushWindow::ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent)
: QFrame(parent, Qt::Popup),
mDocument(document)
{
mButtonPoint = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-point")), "", this);
mButtonSquare = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-square")), "", this);
mButtonCircle = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-circle")), "", this);
mButtonCustom = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-custom")), "", this);
mSizeSliders = new ShapeBrushSizeControls("Brush size", this);
QVBoxLayout *layoutMain = new QVBoxLayout;
layoutMain->setSpacing(0);
layoutMain->setContentsMargins(4,0,4,4);
QHBoxLayout *layoutHorizontal = new QHBoxLayout;
layoutHorizontal->setSpacing(0);
layoutHorizontal->setContentsMargins (QMargins (0, 0, 0, 0));
configureButtonInitialSettings(mButtonPoint);
configureButtonInitialSettings(mButtonSquare);
configureButtonInitialSettings(mButtonCircle);
configureButtonInitialSettings(mButtonCustom);
mButtonPoint->setToolTip (toolTipPoint);
mButtonSquare->setToolTip (toolTipSquare);
mButtonCircle->setToolTip (toolTipCircle);
mButtonCustom->setToolTip (toolTipCustom);
QButtonGroup* brushButtonGroup = new QButtonGroup(this);
brushButtonGroup->addButton(mButtonPoint);
brushButtonGroup->addButton(mButtonSquare);
brushButtonGroup->addButton(mButtonCircle);
brushButtonGroup->addButton(mButtonCustom);
brushButtonGroup->setExclusive(true);
layoutHorizontal->addWidget(mButtonPoint, 0, Qt::AlignTop);
layoutHorizontal->addWidget(mButtonSquare, 0, Qt::AlignTop);
layoutHorizontal->addWidget(mButtonCircle, 0, Qt::AlignTop);
layoutHorizontal->addWidget(mButtonCustom, 0, Qt::AlignTop);
mHorizontalGroupBox = new QGroupBox(tr(""));
mHorizontalGroupBox->setLayout(layoutHorizontal);
mToolSelector = new QComboBox(this);
mToolSelector->addItem(tr("Height (drag)"));
mToolSelector->addItem(tr("Height, raise (paint)"));
mToolSelector->addItem(tr("Height, lower (paint)"));
mToolSelector->addItem(tr("Smooth (paint)"));
mToolSelector->addItem(tr("Flatten (paint)"));
QLabel *brushStrengthLabel = new QLabel(this);
brushStrengthLabel->setText("Brush strength:");
mToolStrengthSlider = new QSlider(Qt::Horizontal);
mToolStrengthSlider->setTickPosition(QSlider::TicksBothSides);
mToolStrengthSlider->setTickInterval(8);
mToolStrengthSlider->setRange(8, 128);
mToolStrengthSlider->setSingleStep(8);
mToolStrengthSlider->setValue(8);
layoutMain->addWidget(mHorizontalGroupBox);
layoutMain->addWidget(mSizeSliders);
layoutMain->addWidget(mToolSelector);
layoutMain->addWidget(brushStrengthLabel);
layoutMain->addWidget(mToolStrengthSlider);
setLayout(layoutMain);
connect(mButtonPoint, SIGNAL(clicked()), this, SLOT(setBrushShape()));
connect(mButtonSquare, SIGNAL(clicked()), this, SLOT(setBrushShape()));
connect(mButtonCircle, SIGNAL(clicked()), this, SLOT(setBrushShape()));
connect(mButtonCustom, SIGNAL(clicked()), this, SLOT(setBrushShape()));
}
void CSVWidget::ShapeBrushWindow::configureButtonInitialSettings(QPushButton *button)
{
button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
button->setContentsMargins (QMargins (0, 0, 0, 0));
button->setIconSize (QSize (48-6, 48-6));
button->setFixedSize (48, 48);
button->setCheckable(true);
}
void CSVWidget::ShapeBrushWindow::setBrushSize(int brushSize)
{
mBrushSize = brushSize;
emit passBrushSize(mBrushSize);
}
void CSVWidget::ShapeBrushWindow::setBrushShape()
{
if(mButtonPoint->isChecked()) mBrushShape = BrushShape_Point;
if(mButtonSquare->isChecked()) mBrushShape = BrushShape_Square;
if(mButtonCircle->isChecked()) mBrushShape = BrushShape_Circle;
if(mButtonCustom->isChecked()) mBrushShape = BrushShape_Custom;
emit passBrushShape(mBrushShape);
}
void CSVWidget::SceneToolShapeBrush::adjustToolTips()
{
}
CSVWidget::SceneToolShapeBrush::SceneToolShapeBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document)
: SceneTool (parent, Type_TopAction),
mToolTip (toolTip),
mDocument (document),
mShapeBrushWindow(new ShapeBrushWindow(document, this))
{
setAcceptDrops(true);
connect(mShapeBrushWindow, SIGNAL(passBrushShape(CSVWidget::BrushShape)), this, SLOT(setButtonIcon(CSVWidget::BrushShape)));
setButtonIcon(mShapeBrushWindow->mBrushShape);
mPanel = new QFrame (this, Qt::Popup);
QHBoxLayout *layout = new QHBoxLayout (mPanel);
layout->setContentsMargins (QMargins (0, 0, 0, 0));
mTable = new QTableWidget (0, 2, this);
mTable->setShowGrid (true);
mTable->verticalHeader()->hide();
mTable->horizontalHeader()->hide();
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch);
mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch);
#else
mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch);
mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch);
#endif
mTable->setSelectionMode (QAbstractItemView::NoSelection);
layout->addWidget (mTable);
connect (mTable, SIGNAL (clicked (const QModelIndex&)),
this, SLOT (clicked (const QModelIndex&)));
}
void CSVWidget::SceneToolShapeBrush::setButtonIcon (CSVWidget::BrushShape brushShape)
{
QString tooltip = "Change brush settings <p>Currently selected: ";
switch (brushShape)
{
case BrushShape_Point:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-point")));
tooltip += mShapeBrushWindow->toolTipPoint;
break;
case BrushShape_Square:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-square")));
tooltip += mShapeBrushWindow->toolTipSquare;
break;
case BrushShape_Circle:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-circle")));
tooltip += mShapeBrushWindow->toolTipCircle;
break;
case BrushShape_Custom:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-custom")));
tooltip += mShapeBrushWindow->toolTipCustom;
break;
}
setToolTip (tooltip);
}
void CSVWidget::SceneToolShapeBrush::showPanel (const QPoint& position)
{
}
void CSVWidget::SceneToolShapeBrush::updatePanel ()
{
}
void CSVWidget::SceneToolShapeBrush::clicked (const QModelIndex& index)
{
}
void CSVWidget::SceneToolShapeBrush::activate ()
{
QPoint position = QCursor::pos();
mShapeBrushWindow->mSizeSliders->mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt());
mShapeBrushWindow->mSizeSliders->mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt());
mShapeBrushWindow->move (position);
mShapeBrushWindow->show();
}
void CSVWidget::SceneToolShapeBrush::dragEnterEvent (QDragEnterEvent *event)
{
emit passEvent(event);
event->accept();
}
void CSVWidget::SceneToolShapeBrush::dropEvent (QDropEvent *event)
{
emit passEvent(event);
event->accept();
}

View file

@ -0,0 +1,127 @@
#ifndef CSV_WIDGET_SCENETOOLSHAPEBRUSH_H
#define CSV_WIDGET_SCENETOOLSHAPEBRUSH_H
#include <QIcon>
#include <QFrame>
#include <QModelIndex>
#include <QWidget>
#include <QLabel>
#include <QComboBox>
#include <QSpinBox>
#include <QGroupBox>
#include <QSlider>
#include <QEvent>
#include <QHBoxLayout>
#include <QPushButton>
#ifndef Q_MOC_RUN
#include "brushshapes.hpp"
#include "scenetool.hpp"
#include "../../model/doc/document.hpp"
#endif
class QTableWidget;
namespace CSVRender
{
class TerrainShapeMode;
}
namespace CSVWidget
{
/// \brief Layout-box for some brush button settings
class ShapeBrushSizeControls : public QGroupBox
{
Q_OBJECT
public:
ShapeBrushSizeControls(const QString &title, QWidget *parent);
private:
QSlider *mBrushSizeSlider = new QSlider(Qt::Horizontal);
QSpinBox *mBrushSizeSpinBox = new QSpinBox;
friend class SceneToolShapeBrush;
friend class CSVRender::TerrainShapeMode;
};
/// \brief Brush settings window
class ShapeBrushWindow : public QFrame
{
Q_OBJECT
public:
ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent = 0);
void configureButtonInitialSettings(QPushButton *button);
const QString toolTipPoint = "Paint single point";
const QString toolTipSquare = "Paint with square brush";
const QString toolTipCircle = "Paint with circle brush";
const QString toolTipCustom = "Paint with custom brush, defined by terrain selection";
private:
CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point;
int mBrushSize = 1;
CSMDoc::Document& mDocument;
QGroupBox *mHorizontalGroupBox;
QComboBox *mToolSelector;
QSlider *mToolStrengthSlider;
QPushButton *mButtonPoint;
QPushButton *mButtonSquare;
QPushButton *mButtonCircle;
QPushButton *mButtonCustom;
ShapeBrushSizeControls* mSizeSliders;
friend class SceneToolShapeBrush;
friend class CSVRender::TerrainShapeMode;
public slots:
void setBrushShape();
void setBrushSize(int brushSize);
signals:
void passBrushSize (int brushSize);
void passBrushShape(CSVWidget::BrushShape brushShape);
};
class SceneToolShapeBrush : public SceneTool
{
Q_OBJECT
QString mToolTip;
CSMDoc::Document& mDocument;
QFrame *mPanel;
QTableWidget *mTable;
ShapeBrushWindow *mShapeBrushWindow;
private:
void adjustToolTips();
public:
SceneToolShapeBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document);
virtual void showPanel (const QPoint& position);
void updatePanel ();
void dropEvent (QDropEvent *event);
void dragEnterEvent (QDragEnterEvent *event);
friend class CSVRender::TerrainShapeMode;
public slots:
void setButtonIcon(CSVWidget::BrushShape brushShape);
void clicked (const QModelIndex& index);
virtual void activate();
signals:
void passEvent(QDropEvent *event);
void passEvent(QDragEnterEvent *event);
};
}
#endif

View file

@ -343,7 +343,7 @@ std::vector< CSMWorld::UniversalId > CSVWorld::RegionMap::getDraggedRecords() co
{ {
QModelIndexList selected(getSelectedCells(true, false)); QModelIndexList selected(getSelectedCells(true, false));
std::vector<CSMWorld::UniversalId> ids; std::vector<CSMWorld::UniversalId> ids;
foreach (QModelIndex it, selected) for (const QModelIndex& it : selected)
{ {
ids.push_back( ids.push_back(
CSMWorld::UniversalId( CSMWorld::UniversalId(
@ -351,7 +351,7 @@ std::vector< CSMWorld::UniversalId > CSVWorld::RegionMap::getDraggedRecords() co
model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData())); model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData()));
} }
selected = getSelectedCells(false, true); selected = getSelectedCells(false, true);
foreach (QModelIndex it, selected) for (const QModelIndex& it : selected)
{ {
ids.push_back( ids.push_back(
CSMWorld::UniversalId( CSMWorld::UniversalId(

View file

@ -25,12 +25,12 @@ namespace MWGui
{ {
} }
MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count) MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)
{ {
if (hasProfit(mActor)) if (hasProfit(mActor))
modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count); modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count);
return InventoryItemModel::copyItem(item, count); return InventoryItemModel::copyItem(item, count, allowAutoEquip);
} }
void CompanionItemModel::removeItem (const ItemStack& item, size_t count) void CompanionItemModel::removeItem (const ItemStack& item, size_t count)

View file

@ -13,7 +13,7 @@ namespace MWGui
public: public:
CompanionItemModel (const MWWorld::Ptr& actor); CompanionItemModel (const MWWorld::Ptr& actor);
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
bool hasProfit(const MWWorld::Ptr& actor); bool hasProfit(const MWWorld::Ptr& actor);

View file

@ -91,12 +91,12 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
return -1; return -1;
} }
MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count) MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)
{ {
const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1];
if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))
throw std::runtime_error("Item to copy needs to be from a different container!"); throw std::runtime_error("Item to copy needs to be from a different container!");
return *source.getClass().getContainerStore(source).add(item.mBase, count, source); return *source.getClass().getContainerStore(source).add(item.mBase, count, source, allowAutoEquip);
} }
void ContainerItemModel::removeItem (const ItemStack& item, size_t count) void ContainerItemModel::removeItem (const ItemStack& item, size_t count)

View file

@ -26,7 +26,7 @@ namespace MWGui
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);
virtual size_t getItemCount(); virtual size_t getItemCount();
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
virtual void update(); virtual void update();

View file

@ -52,7 +52,7 @@ namespace MWGui
public: public:
WorldItemModel(float left, float top) : mLeft(left), mTop(top) {} WorldItemModel(float left, float top) : mLeft(left), mTop(top) {}
virtual ~WorldItemModel() {} virtual ~WorldItemModel() {}
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count) virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool /*allowAutoEquip*/)
{ {
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();

View file

@ -46,11 +46,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item)
return -1; return -1;
} }
MWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count) MWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)
{ {
if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor))
throw std::runtime_error("Item to copy needs to be from a different container!"); throw std::runtime_error("Item to copy needs to be from a different container!");
return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor); return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, allowAutoEquip);
} }
void InventoryItemModel::removeItem (const ItemStack& item, size_t count) void InventoryItemModel::removeItem (const ItemStack& item, size_t count)

View file

@ -17,7 +17,7 @@ namespace MWGui
virtual bool onTakeItem(const MWWorld::Ptr &item, int count); virtual bool onTakeItem(const MWWorld::Ptr &item, int count);
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
/// Move items from this model to \a otherModel. /// Move items from this model to \a otherModel.

View file

@ -116,9 +116,9 @@ namespace MWGui
return mSourceModel->allowedToUseItems(); return mSourceModel->allowedToUseItems();
} }
MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count) MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)
{ {
return mSourceModel->copyItem (item, count); return mSourceModel->copyItem (item, count, allowAutoEquip);
} }
void ProxyItemModel::removeItem (const ItemStack& item, size_t count) void ProxyItemModel::removeItem (const ItemStack& item, size_t count)

View file

@ -65,9 +65,7 @@ namespace MWGui
/// @note Derived implementations may return an empty Ptr if the move was unsuccessful. /// @note Derived implementations may return an empty Ptr if the move was unsuccessful.
virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
/// @param setNewOwner If true, set the copied item's owner to the actor we are copying to, virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0;
/// otherwise reset owner to ""
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count) = 0;
virtual void removeItem (const ItemStack& item, size_t count) = 0; virtual void removeItem (const ItemStack& item, size_t count) = 0;
/// Is the player allowed to use items from this item model? (default true) /// Is the player allowed to use items from this item model? (default true)
@ -97,7 +95,7 @@ namespace MWGui
virtual bool onDropItem(const MWWorld::Ptr &item, int count); virtual bool onDropItem(const MWWorld::Ptr &item, int count);
virtual bool onTakeItem(const MWWorld::Ptr &item, int count); virtual bool onTakeItem(const MWWorld::Ptr &item, int count);
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);

View file

@ -1,6 +1,7 @@
#include "tradeitemmodel.hpp" #include "tradeitemmodel.hpp"
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/settings/settings.hpp>
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
@ -139,8 +140,9 @@ namespace MWGui
throw std::runtime_error("The borrowed item disappeared"); throw std::runtime_error("The borrowed item disappeared");
const ItemStack& item = sourceModel->getItem(i); const ItemStack& item = sourceModel->getItem(i);
static const bool prevent = Settings::Manager::getBool("prevent merchant equipping", "Game");
// copy the borrowed items to our model // copy the borrowed items to our model
copyItem(item, itemStack.mCount); copyItem(item, itemStack.mCount, !prevent);
// then remove them from the source model // then remove them from the source model
sourceModel->removeItem(item, itemStack.mCount); sourceModel->removeItem(item, itemStack.mCount);
} }

View file

@ -344,7 +344,7 @@ namespace MWMechanics
if (actor.getClass().getCreatureStats(actor).isDead()) if (actor.getClass().getCreatureStats(actor).isDead())
return; return;
if (!actor.getClass().hasInventoryStore(actor) || !actor.getClass().getInventoryStore(actor).canActorAutoEquip(actor)) if (!actor.getClass().hasInventoryStore(actor))
return; return;
if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
@ -1293,7 +1293,7 @@ namespace MWMechanics
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
// If we have a torch and can equip it, then equip it now. // If we have a torch and can equip it, then equip it now.
if (heldIter == inventoryStore.end() && inventoryStore.canActorAutoEquip(ptr)) if (heldIter == inventoryStore.end())
{ {
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr); inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr);
} }
@ -2056,6 +2056,7 @@ namespace MWMechanics
// One case where we need this is to make sure bound items are removed upon death // One case where we need this is to make sure bound items are removed upon death
stats.modifyMagicEffects(MWMechanics::MagicEffects()); stats.modifyMagicEffects(MWMechanics::MagicEffects());
stats.getActiveSpells().clear(); stats.getActiveSpells().clear();
stats.getSpells().clear();
// Make sure spell effects are removed // Make sure spell effects are removed
purgeSpellEffects(stats.getActorId()); purgeSpellEffects(stats.getActorId());

View file

@ -133,7 +133,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
if (!mIsShortcutting) if (!mIsShortcutting)
{ {
if (wasShortcutting || doesPathNeedRecalc(dest, actor.getCell())) // if need to rebuild path if (wasShortcutting || doesPathNeedRecalc(dest, actor)) // if need to rebuild path
{ {
const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor); const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor);
mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()), mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),
@ -328,11 +328,11 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin
return false; return false;
} }
bool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::CellStore* currentCell) bool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::Ptr& actor) const
{ {
return mPathFinder.getPath().empty() return mPathFinder.getPath().empty()
|| (distance(mPathFinder.getPath().back(), newDest) > 10) || getPathDistance(actor, mPathFinder.getPath().back(), newDest) > 10
|| mPathFinder.getPathCell() != currentCell; || mPathFinder.getPathCell() != actor.getCell();
} }
bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position) bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position)

View file

@ -120,7 +120,7 @@ namespace MWMechanics
/// Check if the way to the destination is clear, taking into account actor speed /// Check if the way to the destination is clear, taking into account actor speed
bool checkWayIsClearForActor(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::Ptr& actor); bool checkWayIsClearForActor(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::Ptr& actor);
bool doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::CellStore* currentCell); bool doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::Ptr& actor) const;
void evadeObstacles(const MWWorld::Ptr& actor); void evadeObstacles(const MWWorld::Ptr& actor);

View file

@ -493,6 +493,11 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
if (movement == mMovementState && idle == mIdleState && !force) if (movement == mMovementState && idle == mIdleState && !force)
return; return;
// Reset idle if we actually play movement animations excepts of these cases:
// 1. When we play turning animations
// 2. When we use a fallback animation for lower body since movement animation for given weapon is missing (e.g. for crossbows and spellcasting)
bool resetIdle = (movement != CharState_None && !isTurning());
std::string movementAnimName; std::string movementAnimName;
MWRender::Animation::BlendMask movemask; MWRender::Animation::BlendMask movemask;
const StateInfo *movestate; const StateInfo *movestate;
@ -524,14 +529,9 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
// For upper body there will be idle animation. // For upper body there will be idle animation.
if (movemask == MWRender::Animation::BlendMask_LowerBody && idle == CharState_None) if (movemask == MWRender::Animation::BlendMask_LowerBody && idle == CharState_None)
idle = CharState_Idle; idle = CharState_Idle;
}
else if (idle == CharState_None) if (movemask == MWRender::Animation::BlendMask_LowerBody)
{ resetIdle = false;
// In the 1st-person mode use ground idle animations as fallback
if (mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())
idle = CharState_Idle;
else
idle = CharState_IdleSwim;
} }
} }
} }
@ -554,7 +554,11 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
if(mAnimation->hasAnimation(weapMovementAnimName)) if(mAnimation->hasAnimation(weapMovementAnimName))
movementAnimName = weapMovementAnimName; movementAnimName = weapMovementAnimName;
else else
{
movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask); movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);
if (movemask == MWRender::Animation::BlendMask_LowerBody)
resetIdle = false;
}
} }
} }
@ -581,6 +585,10 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
mMovementAnimationControlled = true; mMovementAnimationControlled = true;
mAnimation->disable(mCurrentMovement); mAnimation->disable(mCurrentMovement);
if (!mAnimation->hasAnimation(movementAnimName))
movementAnimName.clear();
mCurrentMovement = movementAnimName; mCurrentMovement = movementAnimName;
if(!mCurrentMovement.empty()) if(!mCurrentMovement.empty())
{ {
@ -623,7 +631,12 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false, mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false,
1.f, "start", "stop", startpoint, ~0ul, true); 1.f, "start", "stop", startpoint, ~0ul, true);
if (resetIdle)
mAnimation->disable(mCurrentIdle);
} }
else
mMovementState = CharState_None;
} }
} }
@ -1015,7 +1028,11 @@ void CharacterController::handleTextKey(const std::string &groupname, const std:
size_t off = groupname.size()+2; size_t off = groupname.size()+2;
size_t len = evt.size() - off; size_t len = evt.size() - off;
if(evt.compare(off, len, "equip attach") == 0) if(groupname == "shield" && evt.compare(off, len, "equip attach") == 0)
mAnimation->showCarriedLeft(true);
else if(groupname == "shield" && evt.compare(off, len, "unequip detach") == 0)
mAnimation->showCarriedLeft(false);
else if(evt.compare(off, len, "equip attach") == 0)
mAnimation->showWeapons(true); mAnimation->showWeapons(true);
else if(evt.compare(off, len, "unequip detach") == 0) else if(evt.compare(off, len, "unequip detach") == 0)
mAnimation->showWeapons(false); mAnimation->showWeapons(false);
@ -1253,7 +1270,7 @@ bool CharacterController::updateCarriedLeftVisible(const int weaptype) const
// Shields/torches shouldn't be visible during any operation involving two hands // Shields/torches shouldn't be visible during any operation involving two hands
// There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop", // There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop",
// but they are also present in weapon drawing animation. // but they are also present in weapon drawing animation.
return !(getWeaponType(weaptype)->mFlags & ESM::WeaponType::TwoHanded); return mAnimation->updateCarriedLeftVisible(weaptype);
} }
bool CharacterController::updateWeaponState(CharacterState& idle) bool CharacterController::updateWeaponState(CharacterState& idle)
@ -1335,8 +1352,19 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{ {
// Note: we do not disable unequipping animation automatically to avoid body desync // Note: we do not disable unequipping animation automatically to avoid body desync
weapgroup = getWeaponAnimation(mWeaponType); weapgroup = getWeaponAnimation(mWeaponType);
mAnimation->play(weapgroup, priorityWeapon, int unequipMask = MWRender::Animation::BlendMask_All;
MWRender::Animation::BlendMask_All, false, bool useShieldAnims = mAnimation->useShieldAnimations();
if (useShieldAnims && mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && !(mWeaponType == ESM::Weapon::None && weaptype == ESM::Weapon::Spell))
{
unequipMask = unequipMask |~MWRender::Animation::BlendMask_LeftArm;
mAnimation->play("shield", Priority_Block,
MWRender::Animation::BlendMask_LeftArm, true,
1.0f, "unequip start", "unequip stop", 0.0f, 0);
}
else if (mWeaponType == ESM::Weapon::HandToHand)
mAnimation->showCarriedLeft(false);
mAnimation->play(weapgroup, priorityWeapon, unequipMask, false,
1.0f, "unequip start", "unequip stop", 0.0f, 0); 1.0f, "unequip start", "unequip stop", 0.0f, 0);
mUpperBodyState = UpperCharState_UnEquipingWeap; mUpperBodyState = UpperCharState_UnEquipingWeap;
@ -1361,7 +1389,10 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if (weaptype != mWeaponType) if (weaptype != mWeaponType)
{ {
forcestateupdate = true; forcestateupdate = true;
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype)); bool useShieldAnims = mAnimation->useShieldAnimations();
if (!useShieldAnims)
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
weapgroup = getWeaponAnimation(weaptype); weapgroup = getWeaponAnimation(weaptype);
// Note: controllers for ranged weapon should use time for beginning of animation to play shooting properly, // Note: controllers for ranged weapon should use time for beginning of animation to play shooting properly,
@ -1376,8 +1407,16 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if (weaptype != ESM::Weapon::None) if (weaptype != ESM::Weapon::None)
{ {
mAnimation->showWeapons(false); mAnimation->showWeapons(false);
mAnimation->play(weapgroup, priorityWeapon, int equipMask = MWRender::Animation::BlendMask_All;
MWRender::Animation::BlendMask_All, true, if (useShieldAnims && weaptype != ESM::Weapon::Spell)
{
equipMask = equipMask |~MWRender::Animation::BlendMask_LeftArm;
mAnimation->play("shield", Priority_Block,
MWRender::Animation::BlendMask_LeftArm, true,
1.0f, "equip start", "equip stop", 0.0f, 0);
}
mAnimation->play(weapgroup, priorityWeapon, equipMask, true,
1.0f, "equip start", "equip stop", 0.0f, 0); 1.0f, "equip start", "equip stop", 0.0f, 0);
mUpperBodyState = UpperCharState_EquipingWeap; mUpperBodyState = UpperCharState_EquipingWeap;
@ -2313,10 +2352,7 @@ void CharacterController::update(float duration, bool animationOnly)
if(mAnimQueue.empty() || inwater || sneak) if(mAnimQueue.empty() || inwater || sneak)
{ {
// Note: turning animations should not interrupt idle ones if (inwater)
if (movestate != CharState_None && !isTurning())
idlestate = CharState_None;
else if (inwater)
idlestate = CharState_IdleSwim; idlestate = CharState_IdleSwim;
else if (sneak && !inJump) else if (sneak && !inJump)
idlestate = CharState_IdleSneak; idlestate = CharState_IdleSneak;

View file

@ -35,6 +35,7 @@
#include "spellcasting.hpp" #include "spellcasting.hpp"
#include "difficultyscaling.hpp" #include "difficultyscaling.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
#include "pathfinding.hpp"
namespace namespace
{ {
@ -327,26 +328,17 @@ namespace MWMechanics
reduceWeaponCondition(damage, validVictim, weapon, attacker); reduceWeaponCondition(damage, validVictim, weapon, attacker);
// Apply "On hit" effect of the weapon & projectile // Apply "On hit" effect of the projectile
bool appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, projectile, hitPosition, true);
/* /*
Start of tes3mp change (minor) Start of tes3mp change (minor)
Track whether the strike enchantment is successful for attacks by the Track whether the strike enchantment is successful for attacks by the
LocalPlayer or LocalActors for both their weapon and projectile LocalPlayer or LocalActors for their projectile
*/ */
bool appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, weapon, hitPosition, true);
if (localAttack) if (localAttack)
localAttack->applyWeaponEnchantment = appliedEnchantment; localAttack->applyAmmoEnchantment = appliedEnchantment;
if (weapon != projectile)
{
appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, projectile, hitPosition, true);
if (localAttack)
localAttack->applyAmmoEnchantment = appliedEnchantment;
}
/* /*
End of tes3mp change (minor) End of tes3mp change (minor)
*/ */
@ -581,13 +573,8 @@ namespace MWMechanics
{ {
osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3()); osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3());
osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3()); osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3());
if (canActorMoveByZAxis(actor2))
{
pos1.z() = 0.f;
pos2.z() = 0.f;
}
float d = (pos1 - pos2).length(); float d = getAggroDistance(actor2, pos1, pos2);
static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"iFightDistanceBase")->mValue.getInteger(); "iFightDistanceBase")->mValue.getInteger();
@ -603,4 +590,11 @@ namespace MWMechanics
return (magicEffects.get(ESM::MagicEffect::Invisibility).getMagnitude() > 0) return (magicEffects.get(ESM::MagicEffect::Invisibility).getMagnitude() > 0)
|| (magicEffects.get(ESM::MagicEffect::Chameleon).getMagnitude() > 75); || (magicEffects.get(ESM::MagicEffect::Chameleon).getMagnitude() > 75);
} }
float getAggroDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs)
{
if (canActorMoveByZAxis(actor))
return distanceIgnoreZ(lhs, rhs);
return distance(lhs, rhs);
}
} }

View file

@ -55,6 +55,9 @@ void applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon,
float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2); float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2);
bool isTargetMagicallyHidden(const MWWorld::Ptr& target); bool isTargetMagicallyHidden(const MWWorld::Ptr& target);
float getAggroDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs);
} }
#endif #endif

View file

@ -27,6 +27,7 @@
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "spellcasting.hpp" #include "spellcasting.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
#include "weapontype.hpp"
namespace MWMechanics namespace MWMechanics
{ {
@ -38,13 +39,13 @@ namespace MWMechanics
void Enchanting::setOldItem(const MWWorld::Ptr& oldItem) void Enchanting::setOldItem(const MWWorld::Ptr& oldItem)
{ {
mOldItemPtr=oldItem; mOldItemPtr=oldItem;
mWeaponType = -1;
mObjectType.clear();
if(!itemEmpty()) if(!itemEmpty())
{ {
mObjectType = mOldItemPtr.getTypeName(); mObjectType = mOldItemPtr.getTypeName();
} if (mObjectType == typeid(ESM::Weapon).name())
else mWeaponType = mOldItemPtr.get<ESM::Weapon>()->mBase->mData.mType;
{
mObjectType="";
} }
} }
@ -148,7 +149,7 @@ namespace MWMechanics
return; return;
} }
} }
else if(mObjectType == typeid(ESM::Weapon).name()) else if (mWeaponType != -1)
{ // Weapon { // Weapon
switch(mCastStyle) switch(mCastStyle)
{ {
@ -158,11 +159,13 @@ namespace MWMechanics
case ESM::Enchantment::WhenUsed: case ESM::Enchantment::WhenUsed:
if (powerfulSoul) if (powerfulSoul)
mCastStyle = ESM::Enchantment::ConstantEffect; mCastStyle = ESM::Enchantment::ConstantEffect;
else else if (getWeaponType(mWeaponType)->mWeaponClass != ESM::WeaponType::Ranged)
mCastStyle = ESM::Enchantment::WhenStrikes; mCastStyle = ESM::Enchantment::WhenStrikes;
return; return;
default: // takes care of Constant effect too default: // takes care of Constant effect too
mCastStyle = ESM::Enchantment::WhenStrikes; mCastStyle = ESM::Enchantment::WhenUsed;
if (getWeaponType(mWeaponType)->mWeaponClass != ESM::WeaponType::Ranged)
mCastStyle = ESM::Enchantment::WhenStrikes;
return; return;
} }
} }

View file

@ -23,6 +23,7 @@ namespace MWMechanics
std::string mNewItemName; std::string mNewItemName;
std::string mObjectType; std::string mObjectType;
int mWeaponType;
public: public:
Enchanting(); Enchanting();

View file

@ -18,6 +18,7 @@
#include "pathgrid.hpp" #include "pathgrid.hpp"
#include "coordinateconverter.hpp" #include "coordinateconverter.hpp"
#include "actorutil.hpp"
namespace namespace
{ {
@ -80,10 +81,24 @@ namespace
const auto realHalfExtents = world->getHalfExtents(actor); const auto realHalfExtents = world->getHalfExtents(actor);
return 2 * std::max(realHalfExtents.x(), realHalfExtents.y()); return 2 * std::max(realHalfExtents.x(), realHalfExtents.y());
} }
float getHeight(const MWWorld::ConstPtr& actor)
{
const auto world = MWBase::Environment::get().getWorld();
const auto halfExtents = world->getHalfExtents(actor);
return 2.0 * halfExtents.z();
}
} }
namespace MWMechanics namespace MWMechanics
{ {
float getPathDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs)
{
if (std::abs(lhs.z() - rhs.z()) > getHeight(actor) || canActorMoveByZAxis(actor))
return distance(lhs, rhs);
return distanceIgnoreZ(lhs, rhs);
}
bool checkWayIsClear(const osg::Vec3f& from, const osg::Vec3f& to, float offsetXY) bool checkWayIsClear(const osg::Vec3f& from, const osg::Vec3f& to, float offsetXY)
{ {
osg::Vec3f dir = to - from; osg::Vec3f dir = to - from;

View file

@ -13,17 +13,29 @@ namespace MWWorld
{ {
class CellStore; class CellStore;
class ConstPtr; class ConstPtr;
class Ptr;
} }
namespace MWMechanics namespace MWMechanics
{ {
class PathgridGraph; class PathgridGraph;
inline float distance(const osg::Vec3f& lhs, const osg::Vec3f& rhs) template <class T>
inline float distance(const T& lhs, const T& rhs)
{ {
static_assert(std::is_same<T, osg::Vec2f>::value
|| std::is_same<T, osg::Vec3f>::value,
"T is not a position");
return (lhs - rhs).length(); return (lhs - rhs).length();
} }
inline float distanceIgnoreZ(const osg::Vec3f& lhs, const osg::Vec3f& rhs)
{
return distance(osg::Vec2f(lhs.x(), lhs.y()), osg::Vec2f(rhs.x(), rhs.y()));
}
float getPathDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs);
inline float getZAngleToDir(const osg::Vec3f& dir) inline float getZAngleToDir(const osg::Vec3f& dir)
{ {
return std::atan2(dir.x(), dir.y()); return std::atan2(dir.x(), dir.y());

View file

@ -62,6 +62,7 @@ ActorAnimation::~ActorAnimation()
} }
mScabbard.reset(); mScabbard.reset();
mHolsteredShield.reset();
} }
PartHolderPtr ActorAnimation::attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor) PartHolderPtr ActorAnimation::attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor)
@ -83,6 +84,168 @@ PartHolderPtr ActorAnimation::attachMesh(const std::string& model, const std::st
return PartHolderPtr(new PartHolder(instance)); return PartHolderPtr(new PartHolder(instance));
} }
std::string ActorAnimation::getShieldMesh(MWWorld::ConstPtr shield) const
{
std::string mesh = shield.getClass().getModel(shield);
std::string holsteredName = mesh;
holsteredName = holsteredName.replace(holsteredName.size()-4, 4, "_sh.nif");
if(mResourceSystem->getVFS()->exists(holsteredName))
{
osg::ref_ptr<osg::Node> shieldTemplate = mResourceSystem->getSceneManager()->getInstance(holsteredName);
SceneUtil::FindByNameVisitor findVisitor ("Bip01 Sheath");
shieldTemplate->accept(findVisitor);
osg::ref_ptr<osg::Node> sheathNode = findVisitor.mFoundNode;
if(!sheathNode)
return std::string();
}
return mesh;
}
bool ActorAnimation::updateCarriedLeftVisible(const int weaptype) const
{
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
if (shieldSheathing)
{
const MWWorld::Class &cls = mPtr.getClass();
MWMechanics::CreatureStats &stats = cls.getCreatureStats(mPtr);
if (cls.hasInventoryStore(mPtr) && weaptype != ESM::Weapon::Spell)
{
SceneUtil::FindByNameVisitor findVisitor ("Bip01 AttachShield");
mObjectRoot->accept(findVisitor);
if (findVisitor.mFoundNode)
{
const MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);
const MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
const MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if (shield != inv.end() && shield->getTypeName() == typeid(ESM::Armor).name() && !getShieldMesh(*shield).empty())
{
if(stats.getDrawState() != MWMechanics::DrawState_Weapon)
return false;
if (weapon != inv.end())
{
const std::string &type = weapon->getTypeName();
if(type == typeid(ESM::Weapon).name())
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType;
return !(MWMechanics::getWeaponType(weaponType)->mFlags & ESM::WeaponType::TwoHanded);
}
else if (type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
return true;
}
}
}
}
}
return !(MWMechanics::getWeaponType(weaptype)->mFlags & ESM::WeaponType::TwoHanded);
}
void ActorAnimation::updateHolsteredShield(bool showCarriedLeft)
{
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
if (!shieldSheathing)
return;
if (!mPtr.getClass().hasInventoryStore(mPtr))
return;
mHolsteredShield.reset();
if (showCarriedLeft)
return;
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if (shield == inv.end() || shield->getTypeName() != typeid(ESM::Armor).name())
return;
// Can not show holdstered shields with two-handed weapons at all
const MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == inv.end())
return;
const std::string &type = weapon->getTypeName();
if(type == typeid(ESM::Weapon).name())
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType;
if (MWMechanics::getWeaponType(weaponType)->mFlags & ESM::WeaponType::TwoHanded)
return;
}
std::string mesh = getShieldMesh(*shield);
if (mesh.empty())
return;
std::string boneName = "Bip01 AttachShield";
osg::Vec4f glowColor = shield->getClass().getEnchantmentColor(*shield);
std::string holsteredName = mesh;
holsteredName = holsteredName.replace(holsteredName.size()-4, 4, "_sh.nif");
bool isEnchanted = !shield->getClass().getEnchantment(*shield).empty();
// If we have no dedicated sheath model, use basic shield model as fallback.
if (!mResourceSystem->getVFS()->exists(holsteredName))
mHolsteredShield = attachMesh(mesh, boneName, isEnchanted, &glowColor);
else
mHolsteredShield = attachMesh(holsteredName, boneName, isEnchanted, &glowColor);
if (!mHolsteredShield)
return;
SceneUtil::FindByNameVisitor findVisitor ("Bip01 Sheath");
mHolsteredShield->getNode()->accept(findVisitor);
osg::Group* shieldNode = findVisitor.mFoundNode;
// If mesh author declared an empty sheath node, use transformation from this node, but use the common shield mesh.
// This approach allows to tweak shield position without need to store the whole shield mesh in the _sh file.
if (shieldNode && !shieldNode->getNumChildren())
{
osg::ref_ptr<osg::Node> fallbackNode = mResourceSystem->getSceneManager()->getInstance(mesh, shieldNode);
if (isEnchanted)
SceneUtil::addEnchantedGlow(shieldNode, mResourceSystem, glowColor);
}
if (mAlpha != 1.f)
mResourceSystem->getSceneManager()->recreateShaders(mHolsteredShield->getNode());
}
bool ActorAnimation::useShieldAnimations() const
{
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
if (!shieldSheathing)
return false;
const MWWorld::Class &cls = mPtr.getClass();
if (!cls.hasInventoryStore(mPtr))
return false;
if (getTextKeyTime("shield: equip attach") < 0 || getTextKeyTime("shield: unequip detach") < 0)
return false;
const MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);
const MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
const MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if (weapon != inv.end() && shield != inv.end() &&
shield->getTypeName() == typeid(ESM::Armor).name() &&
!getShieldMesh(*shield).empty())
{
const std::string &type = weapon->getTypeName();
if(type == typeid(ESM::Weapon).name())
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType;
return !(MWMechanics::getWeaponType(weaponType)->mFlags & ESM::WeaponType::TwoHanded);
}
else if (type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
return true;
}
return false;
}
osg::Group* ActorAnimation::getBoneByName(const std::string& boneName) osg::Group* ActorAnimation::getBoneByName(const std::string& boneName)
{ {
if (!mObjectRoot) if (!mObjectRoot)

View file

@ -38,11 +38,15 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
virtual void itemAdded(const MWWorld::ConstPtr& item, int count); virtual void itemAdded(const MWWorld::ConstPtr& item, int count);
virtual void itemRemoved(const MWWorld::ConstPtr& item, int count); virtual void itemRemoved(const MWWorld::ConstPtr& item, int count);
virtual bool isArrowAttached() const { return false; } virtual bool isArrowAttached() const { return false; }
virtual bool useShieldAnimations() const;
bool updateCarriedLeftVisible(const int weaptype) const;
protected: protected:
osg::Group* getBoneByName(const std::string& boneName); osg::Group* getBoneByName(const std::string& boneName);
virtual void updateHolsteredWeapon(bool showHolsteredWeapons); virtual void updateHolsteredWeapon(bool showHolsteredWeapons);
virtual void updateHolsteredShield(bool showCarriedLeft);
virtual void updateQuiver(); virtual void updateQuiver();
virtual std::string getShieldMesh(MWWorld::ConstPtr shield) const;
virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon); virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon);
virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor); virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor);
virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename) virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename)
@ -52,6 +56,7 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
}; };
PartHolderPtr mScabbard; PartHolderPtr mScabbard;
PartHolderPtr mHolsteredShield;
private: private:
void addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight); void addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight);

View file

@ -153,6 +153,8 @@ public:
void setTextKeyListener(TextKeyListener* listener); void setTextKeyListener(TextKeyListener* listener);
virtual bool updateCarriedLeftVisible(const int weaptype) const { return false; };
protected: protected:
class AnimationTime : public SceneUtil::ControllerSource class AnimationTime : public SceneUtil::ControllerSource
{ {
@ -453,6 +455,7 @@ public:
/// @note The matching is case-insensitive. /// @note The matching is case-insensitive.
const osg::Node* getNode(const std::string& name) const; const osg::Node* getNode(const std::string& name) const;
virtual bool useShieldAnimations() const { return false; }
virtual void showWeapons(bool showWeapon) {} virtual void showWeapons(bool showWeapon) {}
virtual void showCarriedLeft(bool show) {} virtual void showCarriedLeft(bool show) {}
virtual void setWeaponGroup(const std::string& group, bool relativeDuration) {} virtual void setWeaponGroup(const std::string& group, bool relativeDuration) {}

View file

@ -90,6 +90,7 @@ void CreatureWeaponAnimation::updateParts()
updateHolsteredWeapon(!mShowWeapons); updateHolsteredWeapon(!mShowWeapons);
updateQuiver(); updateQuiver();
updateHolsteredShield(mShowCarriedLeft);
if (mShowWeapons) if (mShowWeapons)
updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight); updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight);

View file

@ -24,6 +24,8 @@
#include <components/nifosg/nifloader.hpp> // TextKeyMapHolder #include <components/nifosg/nifloader.hpp> // TextKeyMapHolder
#include <components/vfs/manager.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -511,6 +513,55 @@ void NpcAnimation::updateNpcBase()
mWeaponAnimationTime->updateStartTime(); mWeaponAnimationTime->updateStartTime();
} }
std::string NpcAnimation::getShieldMesh(MWWorld::ConstPtr shield) const
{
std::string mesh = shield.getClass().getModel(shield);
const ESM::Armor *armor = shield.get<ESM::Armor>()->mBase;
std::vector<ESM::PartReference> bodyparts = armor->mParts.mParts;
if (!bodyparts.empty())
{
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
// For NPCs try to get shield model from bodyparts first, with ground model as fallback
for (auto & part : bodyparts)
{
if (part.mPart != ESM::PRT_Shield)
continue;
std::string bodypartName;
if (!mNpc->isMale() && !part.mFemale.empty())
bodypartName = part.mFemale;
else if (!part.mMale.empty())
bodypartName = part.mMale;
if (!bodypartName.empty())
{
const ESM::BodyPart *bodypart = 0;
bodypart = partStore.search(bodypartName);
if (bodypart->mData.mType != ESM::BodyPart::MT_Armor)
return "";
else if (!bodypart->mModel.empty())
mesh = "meshes\\" + bodypart->mModel;
}
}
}
std::string holsteredName = mesh;
holsteredName = holsteredName.replace(holsteredName.size()-4, 4, "_sh.nif");
if(mResourceSystem->getVFS()->exists(holsteredName))
{
osg::ref_ptr<osg::Node> shieldTemplate = mResourceSystem->getSceneManager()->getInstance(holsteredName);
SceneUtil::FindByNameVisitor findVisitor ("Bip01 Sheath");
shieldTemplate->accept(findVisitor);
osg::ref_ptr<osg::Node> sheathNode = findVisitor.mFoundNode;
if(!sheathNode)
return std::string();
}
return mesh;
}
void NpcAnimation::updateParts() void NpcAnimation::updateParts()
{ {
if (!mObjectRoot.get()) if (!mObjectRoot.get())
@ -954,6 +1005,8 @@ void NpcAnimation::showCarriedLeft(bool show)
} }
else else
removeIndividualPart(ESM::PRT_Shield); removeIndividualPart(ESM::PRT_Shield);
updateHolsteredShield(mShowCarriedLeft);
} }
void NpcAnimation::attachArrow() void NpcAnimation::attachArrow()
@ -1051,6 +1104,14 @@ void NpcAnimation::setWeaponGroup(const std::string &group, bool relativeDuratio
void NpcAnimation::equipmentChanged() void NpcAnimation::equipmentChanged()
{ {
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
if (shieldSheathing)
{
int weaptype;
MWMechanics::getActiveWeapon(mPtr, &weaptype);
showCarriedLeft(updateCarriedLeftVisible(weaptype));
}
updateParts(); updateParts();
} }

View file

@ -98,6 +98,7 @@ private:
protected: protected:
virtual void addControllers(); virtual void addControllers();
virtual bool isArrowAttached() const; virtual bool isArrowAttached() const;
virtual std::string getShieldMesh(MWWorld::ConstPtr shield) const;
public: public:
/** /**

View file

@ -225,7 +225,7 @@ protected:
virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
{ {
osg::TexMat* texMat = static_cast<osg::TexMat*>(stateset->getTextureAttribute(0, osg::StateAttribute::TEXMAT)); osg::TexMat* texMat = static_cast<osg::TexMat*>(stateset->getTextureAttribute(0, osg::StateAttribute::TEXMAT));
texMat->setMatrix(osg::Matrix::translate(osg::Vec3f(0, mAnimationTimer, 0.f))); texMat->setMatrix(osg::Matrix::translate(osg::Vec3f(0, -mAnimationTimer, 0.f)));
stateset->setTextureAttribute(0, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); stateset->setTextureAttribute(0, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
stateset->setTextureAttribute(1, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); stateset->setTextureAttribute(1, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
@ -1108,7 +1108,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
, mMonth(0) , mMonth(0)
, mCloudAnimationTimer(0.f) , mCloudAnimationTimer(0.f)
, mRainTimer(0.f) , mRainTimer(0.f)
, mStormDirection(0,-1,0) , mStormDirection(0,1,0)
, mClouds() , mClouds()
, mNextClouds() , mNextClouds()
, mCloudBlendFactor(0.0f) , mCloudBlendFactor(0.0f)
@ -1597,10 +1597,14 @@ void SkyManager::update(float duration)
osg::Quat quat; osg::Quat quat;
quat.makeRotate(osg::Vec3f(0,1,0), mStormDirection); quat.makeRotate(osg::Vec3f(0,1,0), mStormDirection);
if (mParticleNode)
mParticleNode->setAttitude(quat);
mCloudNode->setAttitude(quat); mCloudNode->setAttitude(quat);
if (mParticleNode)
{
// Morrowind deliberately rotates the blizzard mesh, so so should we.
if (mCurrentParticleEffect == "meshes\\blizzard.nif")
quat.makeRotate(osg::Vec3f(-1,0,0), mStormDirection);
mParticleNode->setAttitude(quat);
}
} }
else else
mCloudNode->setAttitude(osg::Quat()); mCloudNode->setAttitude(osg::Quat());
@ -1636,7 +1640,7 @@ void SkyManager::updateRainParameters()
{ {
if (mRainShooter) if (mRainShooter)
{ {
float angle = -std::atan2(1, 50.f/mWindSpeed); float angle = -std::atan(mWindSpeed/50.f);
mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed*std::sin(angle), -mRainSpeed/std::cos(angle))); mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed*std::sin(angle), -mRainSpeed/std::cos(angle)));
mRainShooter->setAngle(angle); mRainShooter->setAngle(angle);

View file

@ -527,7 +527,7 @@ namespace MWScript
MWMechanics::MagicEffects effects = stats.getSpells().getMagicEffects(); MWMechanics::MagicEffects effects = stats.getSpells().getMagicEffects();
effects += stats.getActiveSpells().getMagicEffects(); effects += stats.getActiveSpells().getMagicEffects();
if (ptr.getClass().hasInventoryStore(ptr)) if (ptr.getClass().hasInventoryStore(ptr) && !stats.isDeathAnimationFinished())
{ {
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
effects += store.getMagicEffects(); effects += store.getMagicEffects();

View file

@ -924,7 +924,7 @@ namespace MWSound
{ {
if(r - pos < sndref.mChance) if(r - pos < sndref.mChance)
{ {
playSound(sndref.mSound.toString(), 1.0f, 1.0f); playSound(sndref.mSound, 1.0f, 1.0f);
break; break;
} }
pos += sndref.mChance; pos += sndref.mChance;

View file

@ -66,6 +66,11 @@ namespace MWWorld
return mCellRef.mRefID; return mCellRef.mRefID;
} }
const std::string* CellRef::getRefIdPtr() const
{
return &mCellRef.mRefID;
}
bool CellRef::getTeleport() const bool CellRef::getTeleport() const
{ {
return mCellRef.mTeleport; return mCellRef.mTeleport;

View file

@ -66,6 +66,9 @@ namespace MWWorld
// Id of object being referenced // Id of object being referenced
std::string getRefId() const; std::string getRefId() const;
// Pointer to ID of the object being referenced
const std::string* getRefIdPtr() const;
// For doors - true if this door teleports to somewhere else, false // For doors - true if this door teleports to somewhere else, false
// if it should open through animation. // if it should open through animation.
bool getTeleport() const; bool getTeleport() const;

View file

@ -150,7 +150,7 @@ namespace
{ {
for (typename MWWorld::CellRefList<T>::List::iterator iter (collection.mList.begin()); for (typename MWWorld::CellRefList<T>::List::iterator iter (collection.mList.begin());
iter!=collection.mList.end(); ++iter) iter!=collection.mList.end(); ++iter)
if (iter->mRef.getRefNum()==state.mRef.mRefNum && iter->mRef.getRefId() == state.mRef.mRefID) if (iter->mRef.getRefNum()==state.mRef.mRefNum && *iter->mRef.getRefIdPtr() == state.mRef.mRefID)
{ {
// overwrite existing reference // overwrite existing reference
iter->load (state); iter->load (state);
@ -464,7 +464,7 @@ namespace MWWorld
const std::string *mIdToFind; const std::string *mIdToFind;
bool operator()(const PtrType& ptr) bool operator()(const PtrType& ptr)
{ {
if (ptr.getCellRef().getRefId() == *mIdToFind) if (*ptr.getCellRef().getRefIdPtr() == *mIdToFind)
{ {
mFound = ptr; mFound = ptr;
return false; return false;

View file

@ -297,7 +297,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &
return add(ref.getPtr(), count, actorPtr); return add(ref.getPtr(), count, actorPtr);
} }
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr) MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool /*allowAutoEquip*/)
{ {
Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
@ -587,7 +587,7 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::
for (std::vector<ESM::ContItem>::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); for (std::vector<ESM::ContItem>::const_iterator iter (items.mList.begin()); iter!=items.mList.end();
++iter) ++iter)
{ {
std::string id = Misc::StringUtils::lowerCase(iter->mItem.toString()); std::string id = Misc::StringUtils::lowerCase(iter->mItem);
addInitialItem(id, owner, iter->mCount); addInitialItem(id, owner, iter->mCount);
} }
@ -718,10 +718,10 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW
if (it->mCount >= 0) if (it->mCount >= 0)
continue; continue;
std::string itemOrList = Misc::StringUtils::lowerCase(it->mItem.toString()); std::string itemOrList = Misc::StringUtils::lowerCase(it->mItem);
//If it's levelled list, restock if there's need to do so. //If it's levelled list, restock if there's need to do so.
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::ItemLevList>().search(it->mItem.toString())) if (MWBase::Environment::get().getWorld()->getStore().get<ESM::ItemLevList>().search(it->mItem))
{ {
std::map<std::string, int>::iterator listInMap = allowedForReplace.find(itemOrList); std::map<std::string, int>::iterator listInMap = allowedForReplace.find(itemOrList);

View file

@ -138,7 +138,7 @@ namespace MWWorld
bool hasVisibleItems() const; bool hasVisibleItems() const;
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true);
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
/// ///
/// \note The item pointed to is not required to exist beyond this function call. /// \note The item pointed to is not required to exist beyond this function call.
@ -146,8 +146,6 @@ namespace MWWorld
/// \attention Do not add items to an existing stack by increasing the count instead of /// \attention Do not add items to an existing stack by increasing the count instead of
/// calling this function! /// calling this function!
/// ///
/// @param setOwner Set the owner of the added item to \a actorPtr? If false, the owner is reset to "".
///
/// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item.
ContainerStoreIterator add(const std::string& id, int count, const Ptr& actorPtr); ContainerStoreIterator add(const std::string& id, int count, const Ptr& actorPtr);

View file

@ -7,7 +7,6 @@
#include <components/esm/loadench.hpp> #include <components/esm/loadench.hpp>
#include <components/esm/inventorystate.hpp> #include <components/esm/inventorystate.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -144,12 +143,12 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor
return *this; return *this;
} }
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr) MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip)
{ {
const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr); const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, allowAutoEquip);
// Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves // Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves
if (actorPtr != MWMechanics::getPlayer() if (allowAutoEquip && actorPtr != MWMechanics::getPlayer()
&& actorPtr.getClass().isNpc() && !actorPtr.getClass().getNpcStats(actorPtr).isWerewolf()) && actorPtr.getClass().isNpc() && !actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())
{ {
std::string type = itemPtr.getTypeName(); std::string type = itemPtr.getTypeName();
@ -221,33 +220,6 @@ MWWorld::ConstContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot)
return findSlot (slot); return findSlot (slot);
} }
bool MWWorld::InventoryStore::canActorAutoEquip(const MWWorld::Ptr& actor)
{
// Treat player as non-trader indifferently from service flags.
if (actor == MWMechanics::getPlayer())
return true;
static const bool prevent = Settings::Manager::getBool("prevent merchant equipping", "Game");
if (!prevent)
return true;
// Corpses can be dressed up by the player as desired.
if (actor.getClass().getCreatureStats(actor).isDead())
return true;
// Companions can autoequip items.
if (!actor.getClass().getScript(actor).empty() &&
actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))
return true;
// If the actor is trader, he can auto-equip items only during initial auto-equipping
int services = actor.getClass().getServices(actor);
if (services & ESM::NPC::AllItems)
return mFirstAutoEquip;
return true;
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::findSlot (int slot) const MWWorld::ContainerStoreIterator MWWorld::InventoryStore::findSlot (int slot) const
{ {
if (slot<0 || slot>=static_cast<int> (mSlots.size())) if (slot<0 || slot>=static_cast<int> (mSlots.size()))
@ -589,9 +561,6 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
End of tes3mp addition End of tes3mp addition
*/ */
if (!canActorAutoEquip(actor))
return;
TSlots slots_; TSlots slots_;
initSlots (slots_); initSlots (slots_);

View file

@ -124,17 +124,15 @@ namespace MWWorld
virtual InventoryStore* clone() { return new InventoryStore(*this); } virtual InventoryStore* clone() { return new InventoryStore(*this); }
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true);
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
/// Auto-equip items if specific conditions are fulfilled (see the implementation). /// Auto-equip items if specific conditions are fulfilled and allowAutoEquip is true (see the implementation).
/// ///
/// \note The item pointed to is not required to exist beyond this function call. /// \note The item pointed to is not required to exist beyond this function call.
/// ///
/// \attention Do not add items to an existing stack by increasing the count instead of /// \attention Do not add items to an existing stack by increasing the count instead of
/// calling this function! /// calling this function!
/// ///
/// @param setOwner Set the owner of the added item to \a actorPtr?
///
/// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item.
void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor); void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor);
@ -160,8 +158,6 @@ namespace MWWorld
void autoEquip (const MWWorld::Ptr& actor); void autoEquip (const MWWorld::Ptr& actor);
///< Auto equip items according to stats and item value. ///< Auto equip items according to stats and item value.
bool canActorAutoEquip(const MWWorld::Ptr& actor);
const MWMechanics::MagicEffects& getMagicEffects() const; const MWMechanics::MagicEffects& getMagicEffects() const;
///< Return magic effects from worn items. ///< Return magic effects from worn items.

View file

@ -745,17 +745,6 @@ void WeatherManager::setRegionWeather(const std::string& region, const int curre
End of tes3mp addition End of tes3mp addition
*/ */
osg::Vec3f WeatherManager::calculateStormDirection()
{
osg::Vec3f playerPos (MWMechanics::getPlayer().getRefData().getPosition().asVec3());
playerPos.z() = 0;
osg::Vec3f redMountainPos (25000, 70000, 0);
osg::Vec3f stormDirection = (playerPos - redMountainPos);
stormDirection.normalize();
return stormDirection;
}
float WeatherManager::calculateWindSpeed(int weatherId, float currentSpeed) float WeatherManager::calculateWindSpeed(int weatherId, float currentSpeed)
{ {
float targetSpeed = std::min(8.0f * mWeatherSettings[weatherId].mWindSpeed, 70.f); float targetSpeed = std::min(8.0f * mWeatherSettings[weatherId].mWindSpeed, 70.f);
@ -766,15 +755,7 @@ float WeatherManager::calculateWindSpeed(int weatherId, float currentSpeed)
float updatedSpeed = (Misc::Rng::rollClosedProbability() - 0.5f) * multiplier * targetSpeed + currentSpeed; float updatedSpeed = (Misc::Rng::rollClosedProbability() - 0.5f) * multiplier * targetSpeed + currentSpeed;
if (updatedSpeed > 0.5f * targetSpeed && updatedSpeed < 2.f * targetSpeed) if (updatedSpeed > 0.5f * targetSpeed && updatedSpeed < 2.f * targetSpeed)
{
currentSpeed = updatedSpeed; currentSpeed = updatedSpeed;
}
// Take in account direction to the Red Mountain, when needed
if (weatherId == 6 || weatherId == 7)
{
currentSpeed = (calculateStormDirection() * currentSpeed).length();
}
return currentSpeed; return currentSpeed;
} }
@ -834,7 +815,16 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time,
if (mIsStorm) if (mIsStorm)
{ {
mStormDirection = calculateStormDirection(); osg::Vec3f stormDirection(0, 1, 0);
if (mResult.mParticleEffect == "meshes\\ashcloud.nif" || mResult.mParticleEffect == "meshes\\blightcloud.nif")
{
osg::Vec3f playerPos (MWMechanics::getPlayer().getRefData().getPosition().asVec3());
playerPos.z() = 0;
osg::Vec3f redMountainPos (25000, 70000, 0);
stormDirection = (playerPos - redMountainPos);
stormDirection.normalize();
}
mStormDirection = stormDirection;
mRendering.getSkyManager()->setStormDirection(mStormDirection); mRendering.getSkyManager()->setStormDirection(mStormDirection);
} }

View file

@ -426,7 +426,6 @@ namespace MWWorld
bool updateWeatherRegion(const std::string& playerRegion); bool updateWeatherRegion(const std::string& playerRegion);
void updateWeatherTransitions(const float elapsedRealSeconds); void updateWeatherTransitions(const float elapsedRealSeconds);
void forceWeather(const int weatherID); void forceWeather(const int weatherID);
osg::Vec3f calculateStormDirection();
bool inTransition(); bool inTransition();
void addWeatherTransition(const int weatherID); void addWeatherTransition(const int weatherID);

View file

@ -25,6 +25,8 @@ if (GTEST_FOUND AND GMOCK_FOUND)
detournavigator/recastmeshobject.cpp detournavigator/recastmeshobject.cpp
detournavigator/navmeshtilescache.cpp detournavigator/navmeshtilescache.cpp
detournavigator/tilecachedrecastmeshmanager.cpp detournavigator/tilecachedrecastmeshmanager.cpp
settings/parser.cpp
) )
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})

View file

@ -123,14 +123,14 @@ namespace
osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375),
osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375), osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375),
osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375),
osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.82585906982421875), osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.8258514404296875),
osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125),
osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625), osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625),
osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625), osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625),
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010540008544921875), osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625),
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985321044921875), osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625),
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386486053466796875), osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125),
osg::Vec3f(215, -215, 1.877177715301513671875), osg::Vec3f(215, -215, 1.87718021869659423828125),
})) << mPath; })) << mPath;
} }
@ -173,14 +173,14 @@ namespace
osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375),
osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375), osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375),
osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375),
osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.82585906982421875), osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.8258514404296875),
osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125),
osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625), osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625),
osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625), osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625),
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010540008544921875), osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625),
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985321044921875), osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625),
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386486053466796875), osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125),
osg::Vec3f(215, -215, 1.877177715301513671875), osg::Vec3f(215, -215, 1.87718021869659423828125),
})) << mPath; })) << mPath;
mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity());
@ -292,14 +292,14 @@ namespace
osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375),
osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375), osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375),
osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375),
osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.82585906982421875), osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.8258514404296875),
osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125),
osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625), osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625),
osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625), osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625),
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010540008544921875), osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625),
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985321044921875), osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625),
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386486053466796875), osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125),
osg::Vec3f(215, -215, 1.877177715301513671875), osg::Vec3f(215, -215, 1.87718021869659423828125),
})) << mPath; })) << mPath;
} }
@ -645,14 +645,14 @@ namespace
osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375), osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375),
osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375), osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375),
osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375), osg::Vec3f(65.48564910888671875, -65.48564910888671875, -53.20705413818359375),
osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.82585906982421875), osg::Vec3f(85.5203399658203125, -85.5203399658203125, -45.8258514404296875),
osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125), osg::Vec3f(105.55503082275390625, -105.55503082275390625, -38.44464874267578125),
osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625), osg::Vec3f(125.5897216796875, -125.5897216796875, -31.063449859619140625),
osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625), osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625),
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010540008544921875), osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625),
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985321044921875), osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625),
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386486053466796875), osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125),
osg::Vec3f(215, -215, 1.877177715301513671875), osg::Vec3f(215, -215, 1.87718021869659423828125),
})) << mPath; })) << mPath;
} }
} }

View file

@ -0,0 +1,411 @@
#include <components/settings/parser.hpp>
#include <boost/filesystem/fstream.hpp>
#include <gtest/gtest.h>
namespace
{
using namespace testing;
using namespace Settings;
struct SettingsFileParserTest : Test
{
SettingsFileParser mLoader;
SettingsFileParser mSaver;
template <typename F>
void withSettingsFile( const std::string& content, F&& f)
{
const auto path = std::string(UnitTest::GetInstance()->current_test_info()->name()) + ".cfg";
{
boost::filesystem::ofstream stream;
stream.open(path);
stream << content;
stream.close();
}
f(path);
}
};
TEST_F(SettingsFileParserTest, load_empty_file)
{
const std::string content;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap());
});
}
TEST_F(SettingsFileParserTest, file_with_single_empty_section)
{
const std::string content =
"[Section]\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap());
});
}
TEST_F(SettingsFileParserTest, file_with_single_section_and_key)
{
const std::string content =
"[Section]\n"
"key = value\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", "key"), "value"}
}));
});
}
TEST_F(SettingsFileParserTest, file_with_single_section_and_key_and_line_comments)
{
const std::string content =
"# foo\n"
"[Section]\n"
"# bar\n"
"key = value\n"
"# baz\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", "key"), "value"}
}));
});
}
TEST_F(SettingsFileParserTest, file_with_single_section_and_key_file_and_inline_section_comment)
{
const std::string content =
"[Section] # foo\n"
"key = value\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);
});
}
TEST_F(SettingsFileParserTest, file_single_section_and_key_and_inline_key_comment)
{
const std::string content =
"[Section]\n"
"key = value # foo\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", "key"), "value # foo"}
}));
});
}
TEST_F(SettingsFileParserTest, file_with_single_section_and_key_and_whitespaces)
{
const std::string content =
" [ Section ] \n"
" key = value \n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", "key"), "value"}
}));
});
}
TEST_F(SettingsFileParserTest, file_with_quoted_string_value)
{
const std::string content =
"[Section]\n"
R"(key = "value")"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", "key"), R"("value")"}
}));
});
}
TEST_F(SettingsFileParserTest, file_with_quoted_string_value_and_eol)
{
const std::string content =
"[Section]\n"
R"(key = "value"\n)"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", "key"), R"("value"\n)"}
}));
});
}
TEST_F(SettingsFileParserTest, file_with_empty_value)
{
const std::string content =
"[Section]\n"
"key =\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", "key"), ""}
}));
});
}
TEST_F(SettingsFileParserTest, file_with_empty_key)
{
const std::string content =
"[Section]\n"
"=\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", ""), ""}
}));
});
}
TEST_F(SettingsFileParserTest, file_with_multiple_keys)
{
const std::string content =
"[Section]\n"
"key1 = value1\n"
"key2 = value2\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", "key1"), "value1"},
{CategorySetting("Section", "key2"), "value2"},
}));
});
}
TEST_F(SettingsFileParserTest, file_with_multiple_sections)
{
const std::string content =
"[Section1]\n"
"key1 = value1\n"
"[Section2]\n"
"key2 = value2\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section1", "key1"), "value1"},
{CategorySetting("Section2", "key2"), "value2"},
}));
});
}
TEST_F(SettingsFileParserTest, file_with_multiple_sections_and_keys)
{
const std::string content =
"[Section1]\n"
"key1 = value1\n"
"key2 = value2\n"
"[Section2]\n"
"key3 = value3\n"
"key4 = value4\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section1", "key1"), "value1"},
{CategorySetting("Section1", "key2"), "value2"},
{CategorySetting("Section2", "key3"), "value3"},
{CategorySetting("Section2", "key4"), "value4"},
}));
});
}
TEST_F(SettingsFileParserTest, file_with_repeated_sections)
{
const std::string content =
"[Section]\n"
"key1 = value1\n"
"[Section]\n"
"key2 = value2\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", "key1"), "value1"},
{CategorySetting("Section", "key2"), "value2"},
}));
});
}
TEST_F(SettingsFileParserTest, file_with_repeated_keys)
{
const std::string content =
"[Section]\n"
"key = value\n"
"key = value\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);
});
}
TEST_F(SettingsFileParserTest, file_with_repeated_keys_in_differrent_sections)
{
const std::string content =
"[Section1]\n"
"key = value\n"
"[Section2]\n"
"key = value\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section1", "key"), "value"},
{CategorySetting("Section2", "key"), "value"},
}));
});
}
TEST_F(SettingsFileParserTest, file_with_unterminated_section)
{
const std::string content =
"[Section"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);
});
}
TEST_F(SettingsFileParserTest, file_with_single_empty_section_name)
{
const std::string content =
"[]\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap());
});
}
TEST_F(SettingsFileParserTest, file_with_key_and_without_section)
{
const std::string content =
"key = value\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);
});
}
TEST_F(SettingsFileParserTest, file_with_key_in_empty_name_section)
{
const std::string content =
"[]"
"key = value\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);
});
}
TEST_F(SettingsFileParserTest, file_with_unterminated_key)
{
const std::string content =
"[Section]\n"
"key\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
EXPECT_THROW(mLoader.loadSettingsFile(path, map), std::runtime_error);
});
}
TEST_F(SettingsFileParserTest, file_with_empty_lines)
{
const std::string content =
"\n"
"[Section]\n"
"\n"
"key = value\n"
"\n"
;
withSettingsFile(content, [this] (const auto& path) {
CategorySettingValueMap map;
mLoader.loadSettingsFile(path, map);
EXPECT_EQ(map, CategorySettingValueMap({
{CategorySetting("Section", "key"), "value"}
}));
});
}
}

View file

@ -139,7 +139,8 @@ bool Wizard::ComponentSelectionPage::validatePage()
mWizard->mInstallations[path].hasBloodmoon = false; mWizard->mInstallations[path].hasBloodmoon = false;
QList<QListWidgetItem*> items = componentsList->findItems(QLatin1String("Bloodmoon"), Qt::MatchStartsWith); QList<QListWidgetItem*> items = componentsList->findItems(QLatin1String("Bloodmoon"), Qt::MatchStartsWith);
foreach (QListWidgetItem *item, items) { for (QListWidgetItem *item : items)
{
item->setText(QLatin1String("Bloodmoon")); item->setText(QLatin1String("Bloodmoon"));
item->setCheckState(Qt::Checked); item->setCheckState(Qt::Checked);
} }

View file

@ -31,7 +31,7 @@ void Wizard::ExistingInstallationPage::initializePage()
// Hide the default item if there are installations to choose from // Hide the default item if there are installations to choose from
installationsList->item(0)->setHidden(!paths.isEmpty()); installationsList->item(0)->setHidden(!paths.isEmpty());
foreach (const QString &path, paths) for (const QString &path : paths)
{ {
if (installationsList->findItems(path, Qt::MatchExactly).isEmpty()) if (installationsList->findItems(path, Qt::MatchExactly).isEmpty())
{ {

View file

@ -21,7 +21,8 @@ QStringList Wizard::IniSettings::findKeys(const QString &text)
{ {
QStringList result; QStringList result;
foreach (const QString &key, mSettings.keys()) { for (const QString &key : mSettings.keys())
{
if (key.startsWith(text)) if (key.startsWith(text))
result << key; result << key;

View file

@ -162,7 +162,8 @@ void Wizard::MainWizard::setupGameSettings()
paths.append(QLatin1String("openmw.cfg")); paths.append(QLatin1String("openmw.cfg"));
paths.append(globalPath + QLatin1String("openmw.cfg")); paths.append(globalPath + QLatin1String("openmw.cfg"));
foreach (const QString &path2, paths) { for (const QString &path2 : paths)
{
qDebug() << "Loading config file:" << path2.toUtf8().constData(); qDebug() << "Loading config file:" << path2.toUtf8().constData();
file.setFileName(path2); file.setFileName(path2);
@ -222,7 +223,8 @@ void Wizard::MainWizard::setupLauncherSettings()
void Wizard::MainWizard::setupInstallations() void Wizard::MainWizard::setupInstallations()
{ {
// Check if the paths actually contain a Morrowind installation // Check if the paths actually contain a Morrowind installation
foreach (const QString path, mGameSettings.getDataDirs()) { for (const QString& path : mGameSettings.getDataDirs())
{
if (findFiles(QLatin1String("Morrowind"), path)) if (findFiles(QLatin1String("Morrowind"), path))
addInstallation(path); addInstallation(path);

View file

@ -217,7 +217,8 @@ bool Wizard::UnshieldWorker::removeDirectory(const QString &dirName)
QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot | QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot |
QDir::System | QDir::Hidden | QDir::System | QDir::Hidden |
QDir::AllDirs | QDir::Files, QDir::DirsFirst)); QDir::AllDirs | QDir::Files, QDir::DirsFirst));
foreach(QFileInfo info, list) { for (const QFileInfo& info : list)
{
if (info.isDir()) { if (info.isDir()) {
result = removeDirectory(info.absoluteFilePath()); result = removeDirectory(info.absoluteFilePath());
} else { } else {
@ -279,7 +280,8 @@ bool Wizard::UnshieldWorker::copyDirectory(const QString &source, const QString
QDir::System | QDir::Hidden | QDir::System | QDir::Hidden |
QDir::AllDirs | QDir::Files, QDir::DirsFirst)); QDir::AllDirs | QDir::Files, QDir::DirsFirst));
foreach (const QFileInfo &info, list) { for (const QFileInfo &info : list)
{
QString relativePath(info.absoluteFilePath()); QString relativePath(info.absoluteFilePath());
relativePath.remove(source); relativePath.remove(source);
@ -315,7 +317,8 @@ bool Wizard::UnshieldWorker::installFiles(const QString &fileName, const QString
QStringList files(findFiles(fileName, path, flags)); QStringList files(findFiles(fileName, path, flags));
foreach (const QString &file, files) { for (const QString &file : files)
{
QFileInfo info(file); QFileInfo info(file);
emit textChanged(tr("Installing: %1").arg(info.fileName())); emit textChanged(tr("Installing: %1").arg(info.fileName()));
@ -339,7 +342,8 @@ bool Wizard::UnshieldWorker::installDirectories(const QString &dirName, const QS
QStringList directories(findDirectories(dirName, path, recursive)); QStringList directories(findDirectories(dirName, path, recursive));
foreach (const QString &dir, directories) { for (const QString &dir : directories)
{
QFileInfo info(dir); QFileInfo info(dir);
emit textChanged(tr("Installing: %1 directory").arg(info.fileName())); emit textChanged(tr("Installing: %1 directory").arg(info.fileName()));
if (!copyDirectory(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource)) if (!copyDirectory(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource))
@ -460,7 +464,8 @@ bool Wizard::UnshieldWorker::setupComponent(Component component)
QStringList list(findFiles(QLatin1String("data1.hdr"), disk.absolutePath())); QStringList list(findFiles(QLatin1String("data1.hdr"), disk.absolutePath()));
foreach (const QString &file, list) { for (const QString &file : list)
{
qDebug() << "current archive: " << file; qDebug() << "current archive: " << file;
@ -579,7 +584,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
<< QLatin1String("Textures") << QLatin1String("Textures")
<< QLatin1String("Video"); << QLatin1String("Video");
foreach (const QString &dir, directories) { for (const QString &dir : directories)
{
if (!installDirectories(dir, temp.absolutePath())) { if (!installDirectories(dir, temp.absolutePath())) {
emit error(tr("Could not install directory!"), emit error(tr("Could not install directory!"),
tr("Installing %1 to %2 failed.").arg(dir, temp.absolutePath())); tr("Installing %1 to %2 failed.").arg(dir, temp.absolutePath()));
@ -588,7 +594,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
} }
// Install directories from disk // Install directories from disk
foreach (const QString &dir, directories) { for (const QString &dir : directories)
{
if (!installDirectories(dir, info.absolutePath(), false, true)) { if (!installDirectories(dir, info.absolutePath(), false, true)) {
emit error(tr("Could not install directory!"), emit error(tr("Could not install directory!"),
tr("Installing %1 to %2 failed.").arg(dir, info.absolutePath())); tr("Installing %1 to %2 failed.").arg(dir, info.absolutePath()));
@ -603,7 +610,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
<< QLatin1String(".top") << QLatin1String(".top")
<< QLatin1String(".mrk"); << QLatin1String(".mrk");
foreach (const QString &extension, extensions) { for (const QString &extension : extensions)
{
if (!installFiles(extension, info.absolutePath(), Qt::MatchEndsWith)) { if (!installFiles(extension, info.absolutePath(), Qt::MatchEndsWith)) {
emit error(tr("Could not install translation file!"), emit error(tr("Could not install translation file!"),
tr("Failed to install *%1 files.").arg(extension)); tr("Failed to install *%1 files.").arg(extension));
@ -617,7 +625,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
files << QLatin1String("Morrowind.esm") files << QLatin1String("Morrowind.esm")
<< QLatin1String("Morrowind.bsa"); << QLatin1String("Morrowind.bsa");
foreach (const QString &file, files) { for (const QString &file : files)
{
if (!installFile(file, temp.absolutePath())) { if (!installFile(file, temp.absolutePath())) {
emit error(tr("Could not install Morrowind data file!"), emit error(tr("Could not install Morrowind data file!"),
tr("Failed to install %1.").arg(file)); tr("Failed to install %1.").arg(file));
@ -658,7 +667,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
files << QLatin1String("Tribunal.esm") files << QLatin1String("Tribunal.esm")
<< QLatin1String("Tribunal.bsa"); << QLatin1String("Tribunal.bsa");
foreach (const QString &file, files) { for (const QString &file : files)
{
if (!installFile(file, temp.absolutePath())) { if (!installFile(file, temp.absolutePath())) {
emit error(tr("Could not find Tribunal data file!"), emit error(tr("Could not find Tribunal data file!"),
tr("Failed to find %1.").arg(file)); tr("Failed to find %1.").arg(file));
@ -683,7 +693,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
files << QLatin1String("Bloodmoon.esm") files << QLatin1String("Bloodmoon.esm")
<< QLatin1String("Bloodmoon.bsa"); << QLatin1String("Bloodmoon.bsa");
foreach (const QString &file, files) { for (const QString &file : files)
{
if (!installFile(file, temp.absolutePath())) { if (!installFile(file, temp.absolutePath())) {
emit error(tr("Could not find Bloodmoon data file!"), emit error(tr("Could not find Bloodmoon data file!"),
tr("Failed to find %1.").arg(file)); tr("Failed to find %1.").arg(file));
@ -696,7 +707,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
emit textChanged(tr("Updating Morrowind configuration file")); emit textChanged(tr("Updating Morrowind configuration file"));
foreach (const QString &inx, list) { for (const QString &inx : list)
{
mIniSettings.parseInx(inx); mIniSettings.parseInx(inx);
} }
} }
@ -705,7 +717,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
QStringList datafiles(findDirectories(QLatin1String("Data Files"), temp.absolutePath())); QStringList datafiles(findDirectories(QLatin1String("Data Files"), temp.absolutePath()));
datafiles.append(findDirectories(QLatin1String("Data Files"), info.absolutePath())); datafiles.append(findDirectories(QLatin1String("Data Files"), info.absolutePath()));
foreach (const QString &dir, datafiles) { for (const QString &dir : datafiles)
{
QFileInfo info(dir); QFileInfo info(dir);
emit textChanged(tr("Installing: %1 directory").arg(info.fileName())); emit textChanged(tr("Installing: %1 directory").arg(info.fileName()));
@ -849,7 +862,8 @@ QStringList Wizard::UnshieldWorker::findFiles(const QString &fileName, const QSt
QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot | QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot |
QDir::AllDirs | QDir::Files, QDir::DirsFirst)); QDir::AllDirs | QDir::Files, QDir::DirsFirst));
foreach(QFileInfo info, list) { for (const QFileInfo& info : list)
{
if (info.isSymLink()) if (info.isSymLink())
continue; continue;

View file

@ -8,13 +8,10 @@ branches:
environment: environment:
matrix: matrix:
- msvc: 2015
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- msvc: 2017 - msvc: 2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
matrix: - msvc: 2019
allow_failures: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
- msvc: 2015
platform: platform:
# - Win32 # - Win32

View file

@ -29,7 +29,7 @@ endif (GIT_CHECKOUT)
# source files # source files
add_component_dir (settings add_component_dir (settings
settings settings parser
) )
add_component_dir (bsa add_component_dir (bsa

View file

@ -5,6 +5,7 @@
#include <algorithm> #include <algorithm>
#include <stack> #include <stack>
#include <iterator> #include <iterator>
#include <sstream>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
@ -324,6 +325,21 @@ namespace Compiler
mExplicit = name2; mExplicit = name2;
return true; return true;
} }
// This is terrible, but of course we must have this for legacy content.
// Convert the string to a number even if it's impossible and use it as a number literal.
// Can't use stof/atof or to_string out of locale concerns.
float number;
std::stringstream stream(name2);
stream >> number;
stream.str(std::string());
stream.clear();
stream << number;
pushFloatLiteral(number);
mTokenLoc = loc;
getErrorHandler().warning ("Parsing a non-variable string as a number: " + stream.str(), loc);
return true;
} }
else else
{ {

View file

@ -180,7 +180,7 @@ namespace Compiler
extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex); extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic); extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic);
extensions.registerInstruction ("choice", "j/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice); extensions.registerInstruction ("choice", "j/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice);
extensions.registerInstruction("forcegreeting","X",opcodeForceGreeting, extensions.registerInstruction("forcegreeting","z",opcodeForceGreeting,
opcodeForceGreetingExplicit); opcodeForceGreetingExplicit);
extensions.registerInstruction("goodbye", "", opcodeGoodbye); extensions.registerInstruction("goodbye", "", opcodeGoodbye);
extensions.registerInstruction("setreputation", "l", opcodeSetReputation, extensions.registerInstruction("setreputation", "l", opcodeSetReputation,

View file

@ -44,7 +44,8 @@ void Config::GameSettings::validatePaths()
QStringList paths = mSettings.values(QString("data")); QStringList paths = mSettings.values(QString("data"));
Files::PathContainer dataDirs; Files::PathContainer dataDirs;
foreach (const QString &path, paths) { for (const QString &path : paths)
{
QByteArray bytes = path.toUtf8(); QByteArray bytes = path.toUtf8();
dataDirs.push_back(Files::PathContainer::value_type(std::string(bytes.constData(), bytes.length()))); dataDirs.push_back(Files::PathContainer::value_type(std::string(bytes.constData(), bytes.length())));
} }
@ -511,7 +512,7 @@ bool Config::GameSettings::hasMaster()
void Config::GameSettings::setContentList(const QStringList& fileNames) void Config::GameSettings::setContentList(const QStringList& fileNames)
{ {
remove(sContentKey); remove(sContentKey);
foreach(const QString& fileName, fileNames) for (const QString& fileName : fileNames)
{ {
setMultiValue(sContentKey, fileName); setMultiValue(sContentKey, fileName);
} }

View file

@ -29,7 +29,8 @@ QStringList Config::LauncherSettings::subKeys(const QString &key)
QStringList result; QStringList result;
foreach (const QString &currentKey, keys) { for (const QString &currentKey : keys)
{
if (keyRe.indexIn(currentKey) != -1) if (keyRe.indexIn(currentKey) != -1)
{ {
@ -110,7 +111,7 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings)
} }
// if any existing profile in launcher matches the content list, make that profile the default // if any existing profile in launcher matches the content list, make that profile the default
foreach(const QString &listName, getContentLists()) for (const QString &listName : getContentLists())
{ {
if (isEqual(files, getContentListFiles(listName))) if (isEqual(files, getContentListFiles(listName)))
{ {
@ -140,7 +141,7 @@ void Config::LauncherSettings::setContentList(const QString& contentListName, co
{ {
removeContentList(contentListName); removeContentList(contentListName);
QString key = makeContentListKey(contentListName); QString key = makeContentListKey(contentListName);
foreach(const QString& fileName, fileNames) for (const QString& fileName : fileNames)
{ {
setMultiValue(key, fileName); setMultiValue(key, fileName);
} }

View file

@ -70,7 +70,7 @@ const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(co
if (name.contains ('/')) if (name.contains ('/'))
fp = EsmFile::FileProperty_FilePath; fp = EsmFile::FileProperty_FilePath;
foreach (const EsmFile *file, mFiles) for (const EsmFile *file : mFiles)
{ {
if (name.compare(file->fileProperty (fp).toString(), Qt::CaseInsensitive) == 0) if (name.compare(file->fileProperty (fp).toString(), Qt::CaseInsensitive) == 0)
return file; return file;
@ -108,7 +108,7 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index
// addon can be checked if its gamefile is // addon can be checked if its gamefile is
// ... special case, addon with no dependency can be used with any gamefile. // ... special case, addon with no dependency can be used with any gamefile.
bool gamefileChecked = (file->gameFiles().count() == 0); bool gamefileChecked = (file->gameFiles().count() == 0);
foreach (const QString &fileName, file->gameFiles()) for (const QString &fileName : file->gameFiles())
{ {
for (QListIterator<EsmFile *> dependencyIter(mFiles); dependencyIter.hasNext(); dependencyIter.next()) for (QListIterator<EsmFile *> dependencyIter(mFiles); dependencyIter.hasNext(); dependencyIter.next())
{ {
@ -283,7 +283,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const
else else
return success; return success;
foreach (EsmFile *file2, mFiles) for (EsmFile *file2 : mFiles)
{ {
if (file2->gameFiles().contains(fileName, Qt::CaseInsensitive)) if (file2->gameFiles().contains(fileName, Qt::CaseInsensitive))
{ {
@ -346,7 +346,7 @@ QMimeData *ContentSelectorModel::ContentModel::mimeData(const QModelIndexList &i
{ {
QByteArray encodedData; QByteArray encodedData;
foreach (const QModelIndex &index, indexes) for (const QModelIndex &index : indexes)
{ {
if (!index.isValid()) if (!index.isValid())
continue; continue;
@ -424,7 +424,7 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path)
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
dir.setNameFilters(filters); dir.setNameFilters(filters);
foreach (const QString &path2, dir.entryList()) for (const QString &path2 : dir.entryList())
{ {
QFileInfo info(dir.absoluteFilePath(path2)); QFileInfo info(dir.absoluteFilePath(path2));
@ -486,7 +486,7 @@ void ContentSelectorModel::ContentModel::clearFiles()
QStringList ContentSelectorModel::ContentModel::gameFiles() const QStringList ContentSelectorModel::ContentModel::gameFiles() const
{ {
QStringList gameFiles; QStringList gameFiles;
foreach(const ContentSelectorModel::EsmFile *file, mFiles) for (const ContentSelectorModel::EsmFile *file : mFiles)
{ {
if (file->isGameFile()) if (file->isGameFile())
{ {
@ -557,7 +557,7 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList &fileL
{ {
mPluginsWithLoadOrderError.clear(); mPluginsWithLoadOrderError.clear();
int previousPosition = -1; int previousPosition = -1;
foreach (const QString &filepath, fileList) for (const QString &filepath : fileList)
{ {
if (setCheckState(filepath, true)) if (setCheckState(filepath, true))
{ {
@ -598,7 +598,7 @@ void ContentSelectorModel::ContentModel::checkForLoadOrderErrors()
QList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::checkForLoadOrderErrors(const EsmFile *file, int row) const QList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::checkForLoadOrderErrors(const EsmFile *file, int row) const
{ {
QList<LoadOrderError> errors = QList<LoadOrderError>(); QList<LoadOrderError> errors = QList<LoadOrderError>();
foreach(const QString &dependentfileName, file->gameFiles()) for (const QString &dependentfileName : file->gameFiles())
{ {
const EsmFile* dependentFile = item(dependentfileName); const EsmFile* dependentFile = item(dependentfileName);
@ -627,7 +627,7 @@ QString ContentSelectorModel::ContentModel::toolTip(const EsmFile *file) const
{ {
QString text("<b>"); QString text("<b>");
int index = indexFromItem(item(file->filePath())).row(); int index = indexFromItem(item(file->filePath())).row();
foreach(const LoadOrderError& error, checkForLoadOrderErrors(file, index)) for (const LoadOrderError& error : checkForLoadOrderErrors(file, index))
{ {
text += "<p>"; text += "<p>";
text += error.toolTip(); text += error.toolTip();
@ -672,7 +672,7 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString &filepath,
//if we're checking an item, ensure all "upstream" files (dependencies) are checked as well. //if we're checking an item, ensure all "upstream" files (dependencies) are checked as well.
if (state == Qt::Checked) if (state == Qt::Checked)
{ {
foreach (QString upstreamName, file->gameFiles()) for (const QString& upstreamName : file->gameFiles())
{ {
const EsmFile *upstreamFile = item(upstreamName); const EsmFile *upstreamFile = item(upstreamName);
@ -689,7 +689,7 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString &filepath,
//otherwise, if we're unchecking an item (or the file is a game file) ensure all downstream files are unchecked. //otherwise, if we're unchecking an item (or the file is a game file) ensure all downstream files are unchecked.
if (state == Qt::Unchecked) if (state == Qt::Unchecked)
{ {
foreach (const EsmFile *downstreamFile, mFiles) for (const EsmFile *downstreamFile : mFiles)
{ {
QFileInfo fileInfo(filepath); QFileInfo fileInfo(filepath);
QString filename = fileInfo.fileName(); QString filename = fileInfo.fileName();
@ -714,7 +714,7 @@ ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checke
// TODO: // TODO:
// First search for game files and next addons, // First search for game files and next addons,
// so we get more or less correct game files vs addons order. // so we get more or less correct game files vs addons order.
foreach (EsmFile *file, mFiles) for (EsmFile *file : mFiles)
if (isChecked(file->filePath())) if (isChecked(file->filePath()))
list << file; list << file;

View file

@ -72,7 +72,7 @@ void ContentSelectorView::ContentSelector::setProfileContent(const QStringList &
{ {
clearCheckStates(); clearCheckStates();
foreach (const QString &filepath, fileList) for (const QString &filepath : fileList)
{ {
const ContentSelectorModel::EsmFile *file = mContentModel->item(filepath); const ContentSelectorModel::EsmFile *file = mContentModel->item(filepath);
if (file && file->isGameFile()) if (file && file->isGameFile())
@ -139,7 +139,7 @@ void ContentSelectorView::ContentSelector::addFiles(const QString &path)
mContentModel->addFiles(path); mContentModel->addFiles(path);
// add any game files to the combo box // add any game files to the combo box
foreach(const QString gameFileName, mContentModel->gameFiles()) for (const QString& gameFileName : mContentModel->gameFiles())
{ {
if (ui.gameFileView->findText(gameFileName) == -1) if (ui.gameFileView->findText(gameFileName) == -1)
{ {
@ -225,7 +225,7 @@ void ContentSelectorView::ContentSelector::slotShowContextMenu(const QPoint& pos
void ContentSelectorView::ContentSelector::setCheckStateForMultiSelectedItems(bool checked) void ContentSelectorView::ContentSelector::setCheckStateForMultiSelectedItems(bool checked)
{ {
Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked; Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked;
foreach(const QModelIndex& index, ui.addonView->selectionModel()->selectedIndexes()) for (const QModelIndex& index : ui.addonView->selectionModel()->selectedIndexes())
{ {
QModelIndex sourceIndex = mAddonProxyModel->mapToSource(index); QModelIndex sourceIndex = mAddonProxyModel->mapToSource(index);
if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() != checkState) if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() != checkState)
@ -249,7 +249,7 @@ void ContentSelectorView::ContentSelector::slotCopySelectedItemsPaths()
{ {
QClipboard *clipboard = QApplication::clipboard(); QClipboard *clipboard = QApplication::clipboard();
QString filepaths; QString filepaths;
foreach (const QModelIndex& index, ui.addonView->selectionModel()->selectedIndexes()) for (const QModelIndex& index : ui.addonView->selectionModel()->selectedIndexes())
{ {
int row = mAddonProxyModel->mapToSource(index).row(); int row = mAddonProxyModel->mapToSource(index).row();
const ContentSelectorModel::EsmFile *file = mContentModel->item(row); const ContentSelectorModel::EsmFile *file = mContentModel->item(row);

View file

@ -9,8 +9,10 @@ namespace ESM
void InventoryList::add(ESMReader &esm) void InventoryList::add(ESMReader &esm)
{ {
esm.getSubHeader();
ContItem ci; ContItem ci;
esm.getHT(ci, 36); esm.getT(ci.mCount);
ci.mItem.assign(esm.getString(32));
mList.push_back(ci); mList.push_back(ci);
} }
@ -18,7 +20,10 @@ namespace ESM
{ {
for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it) for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
{ {
esm.writeHNT("NPCO", *it, 36); esm.startSubRecord("NPCO");
esm.writeT(it->mCount);
esm.writeFixedSizeString(it->mItem, 32);
esm.endRecord("NPCO");
} }
} }

View file

@ -19,7 +19,7 @@ class ESMWriter;
struct ContItem struct ContItem
{ {
int mCount; int mCount;
NAME32 mItem; std::string mItem;
}; };
/// InventoryList, NPCO subrecord /// InventoryList, NPCO subrecord

View file

@ -62,8 +62,10 @@ namespace ESM
break; break;
case ESM::FourCC<'S','N','A','M'>::value: case ESM::FourCC<'S','N','A','M'>::value:
{ {
esm.getSubHeader();
SoundRef sr; SoundRef sr;
esm.getHT(sr, 33); sr.mSound.assign(esm.getString(32));
esm.getT(sr.mChance);
mSoundList.push_back(sr); mSoundList.push_back(sr);
break; break;
} }
@ -103,7 +105,10 @@ namespace ESM
esm.writeHNT("CNAM", mMapColor); esm.writeHNT("CNAM", mMapColor);
for (std::vector<SoundRef>::const_iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) for (std::vector<SoundRef>::const_iterator it = mSoundList.begin(); it != mSoundList.end(); ++it)
{ {
esm.writeHNT<SoundRef>("SNAM", *it); esm.startSubRecord("SNAM");
esm.writeFixedSizeString(it->mSound, 32);
esm.writeT(it->mChance);
esm.endRecord("NPCO");
} }
} }

View file

@ -33,14 +33,14 @@ struct Region
// the engine uses mA as "snow" and mB as "blizard" // the engine uses mA as "snow" and mB as "blizard"
mA, mB; mA, mB;
}; // 10 bytes }; // 10 bytes
#pragma pack(pop)
// Reference to a sound that is played randomly in this region // Reference to a sound that is played randomly in this region
struct SoundRef struct SoundRef
{ {
NAME32 mSound; std::string mSound;
unsigned char mChance; unsigned char mChance;
}; // 33 bytes };
#pragma pack(pop)
WEATstruct mData; WEATstruct mData;
int mMapColor; // RGBA int mMapColor; // RGBA

View file

@ -77,10 +77,10 @@ namespace ESM
{ {
case ESM::FourCC<'S','C','H','D'>::value: case ESM::FourCC<'S','C','H','D'>::value:
{ {
SCHD data; esm.getSubHeader();
esm.getHT(data, 52); mId = esm.getString(32);
mData = data.mData; esm.getT(mData);
mId = data.mName.toString();
hasHeader = true; hasHeader = true;
break; break;
} }
@ -131,13 +131,10 @@ namespace ESM
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
varNameString.append(*it); varNameString.append(*it);
SCHD data; esm.startSubRecord("SCHD");
memset(&data, 0, sizeof(data)); esm.writeFixedSizeString(mId, 32);
esm.writeT(mData, 20);
data.mData = mData; esm.endRecord("SCHD");
data.mName.assign(mId);
esm.writeHNT("SCHD", data, 52);
if (isDeleted) if (isDeleted)
{ {

View file

@ -31,9 +31,9 @@ public:
}; };
struct SCHD struct SCHD
{ {
NAME32 mName; std::string mName;
Script::SCHDstruct mData; Script::SCHDstruct mData;
}; // 52 bytes };
std::string mId; std::string mId;

View file

@ -255,7 +255,7 @@ namespace ESMTerrain
(*positions)[static_cast<unsigned int>(vertX*numVerts + vertY)] (*positions)[static_cast<unsigned int>(vertX*numVerts + vertY)]
= osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits,
(vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits,
height); height + getAlteredHeight(col, row));
if (normalData) if (normalData)
{ {
@ -291,6 +291,8 @@ namespace ESMTerrain
color.b() = 255; color.b() = 255;
} }
adjustColor(col, row, heightData, color); //Does nothing by default, override in OpenMW-CS
// Unlike normals, colors mostly connect seamlessly between cells, but not always... // Unlike normals, colors mostly connect seamlessly between cells, but not always...
if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
fixColour(color, cellX, cellY, col, row, cache); fixColour(color, cellX, cellY, col, row, cache);
@ -521,13 +523,6 @@ namespace ESMTerrain
} }
float Storage::getVertexHeight(const ESM::Land::LandData* data, int x, int y)
{
assert(x < ESM::Land::LAND_SIZE);
assert(y < ESM::Land::LAND_SIZE);
return data->mHeights[y * ESM::Land::LAND_SIZE + x];
}
const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache) const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache)
{ {
LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY));
@ -540,6 +535,15 @@ namespace ESMTerrain
} }
} }
void Storage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const
{
}
float Storage::getAlteredHeight(int col, int row) const
{
return 0;
}
Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture)
{ {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mLayerInfoMutex); OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mLayerInfoMutex);

View file

@ -1,6 +1,8 @@
#ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H #ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H
#define COMPONENTS_ESM_TERRAIN_STORAGE_H #define COMPONENTS_ESM_TERRAIN_STORAGE_H
#include <cassert>
#include <OpenThreads/Mutex> #include <OpenThreads/Mutex>
#include <components/terrain/storage.hpp> #include <components/terrain/storage.hpp>
@ -107,6 +109,13 @@ namespace ESMTerrain
virtual int getBlendmapScale(float chunkSize); virtual int getBlendmapScale(float chunkSize);
float getVertexHeight (const ESM::Land::LandData* data, int x, int y)
{
assert(x < ESM::Land::LAND_SIZE);
assert(y < ESM::Land::LAND_SIZE);
return data->mHeights[y * ESM::Land::LAND_SIZE + x];
}
private: private:
const VFS::Manager* mVFS; const VFS::Manager* mVFS;
@ -114,10 +123,11 @@ namespace ESMTerrain
inline void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache); inline void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache);
inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache);
inline float getVertexHeight (const ESM::Land::LandData* data, int x, int y);
inline const LandObject* getLand(int cellX, int cellY, LandCache& cache); inline const LandObject* getLand(int cellX, int cellY, LandCache& cache);
virtual void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const;
virtual float getAlteredHeight(int col, int row) const;
// Since plugins can define new texture palettes, we need to know the plugin index too // Since plugins can define new texture palettes, we need to know the plugin index too
// in order to retrieve the correct texture name. // in order to retrieve the correct texture name.
// pair <texture id, plugin id> // pair <texture id, plugin id>

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