mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-28 05:39:39 +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:
commit
54301f5f89
124 changed files with 3999 additions and 950 deletions
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -83,3 +83,13 @@ moc_*.cxx
|
|||
*.[ao]
|
||||
*.so
|
||||
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
|
||||
|
|
|
@ -14,13 +14,7 @@ Debian:
|
|||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||
- 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 install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old
|
||||
- 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
|
||||
- apt-get install -y libmygui-dev libbullet-dev
|
||||
stage: build
|
||||
script:
|
||||
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
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 #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 #4202: Open .omwaddon files without needing toopen openmw-cs first
|
||||
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 #4775: Slowfall effect resets player jumping flag
|
||||
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 #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
|
||||
|
@ -135,6 +137,7 @@
|
|||
Bug #5089: Swimming/Underwater creatures only swim around ground level
|
||||
Bug #5092: NPCs with enchanted weapons play sound when out of charges
|
||||
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 #5103: Sneaking state behavior is still inconsistent
|
||||
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 #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 #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 #2229: Improve pathfinding AI
|
||||
Feature #3025: Analogue gamepad movement controls
|
||||
|
@ -183,6 +188,7 @@
|
|||
Feature #4784: Launcher: Duplicate Content Lists
|
||||
Feature #4812: Support NiSwitchNode
|
||||
Feature #4836: Daytime node switch
|
||||
Feature #4840: Editor: Transient terrain change support
|
||||
Feature #4859: Make water reflections more configurable
|
||||
Feature #4882: Support for NiPalette node
|
||||
Feature #4887: Add openmw command option to set initial random seed
|
||||
|
@ -209,6 +215,8 @@
|
|||
Feature #5132: Unique animations for different weapon types
|
||||
Feature #5146: Safe Dispose corpse
|
||||
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 #4695: Optimize Distant Terrain memory consumption
|
||||
Task #4789: Optimize cell transitions
|
||||
|
|
|
@ -40,6 +40,8 @@ New Features:
|
|||
|
||||
New Editor Features:
|
||||
- "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:
|
||||
- Scripted Items cannot be stacked anymore to avoid multiple script execution (#2969)
|
||||
|
|
|
@ -103,7 +103,7 @@ Options:
|
|||
Build unit tests / Google test
|
||||
-u
|
||||
Configure for unity builds.
|
||||
-v <2013/2015/2017>
|
||||
-v <2013/2015/2017/2019>
|
||||
Choose the Visual Studio version to use.
|
||||
-n
|
||||
Produce NMake makefiles instead of a Visual Studio solution.
|
||||
|
@ -247,10 +247,22 @@ if [ -z $CONFIGURATION ]; then
|
|||
fi
|
||||
|
||||
if [ -z $VS_VERSION ]; then
|
||||
VS_VERSION="2013"
|
||||
VS_VERSION="2017"
|
||||
fi
|
||||
|
||||
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 )
|
||||
GENERATOR="Visual Studio 15 2017"
|
||||
TOOLSET="vc141"
|
||||
|
@ -258,6 +270,9 @@ case $VS_VERSION in
|
|||
MSVC_VER="14.1"
|
||||
MSVC_YEAR="2015"
|
||||
MSVC_DISPLAY_YEAR="2017"
|
||||
BOOST_VER="1.67.0"
|
||||
BOOST_VER_URL="1_67_0"
|
||||
BOOST_VER_SDK="106700"
|
||||
;;
|
||||
|
||||
14|14.0|2015 )
|
||||
|
@ -267,6 +282,9 @@ case $VS_VERSION in
|
|||
MSVC_VER="14.0"
|
||||
MSVC_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 )
|
||||
|
@ -276,6 +294,9 @@ case $VS_VERSION in
|
|||
MSVC_VER="12.0"
|
||||
MSVC_YEAR="2013"
|
||||
MSVC_DISPLAY_YEAR="2013"
|
||||
BOOST_VER="1.58.0"
|
||||
BOOST_VER_URL="1_58_0"
|
||||
BOOST_VER_SDK="105800"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
@ -315,7 +336,7 @@ case $CONFIGURATION in
|
|||
;;
|
||||
esac
|
||||
|
||||
if [ ${BITS} -eq 64 ]; then
|
||||
if [ $BITS -eq 64 ] && [ $MSVC_REAL_VER -lt 16 ]; then
|
||||
GENERATOR="${GENERATOR} Win64"
|
||||
fi
|
||||
|
||||
|
@ -323,7 +344,15 @@ if [ -n "$NMAKE" ]; then
|
|||
GENERATOR="NMake Makefiles"
|
||||
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
|
||||
add_cmake_opts "-DCMAKE_BUILD_TYPE=${BUILD_CONFIG}"
|
||||
|
@ -351,9 +380,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
|
||||
# Boost
|
||||
if [ -z $APPVEYOR ]; then
|
||||
download "Boost 1.67.0" \
|
||||
"https://sourceforge.net/projects/boost/files/boost-binaries/1.67.0/boost_1_67_0-msvc-${MSVC_VER}-${BITS}.exe" \
|
||||
"boost-1.67.0-msvc${MSVC_VER}-win${BITS}.exe"
|
||||
download "Boost ${BOOST_VER}" \
|
||||
"https://sourceforge.net/projects/boost/files/boost-binaries/${BOOST_VER}/boost_${BOOST_VER_URL}-msvc-${MSVC_VER}-${BITS}.exe" \
|
||||
"boost-${BOOST_VER}-msvc${MSVC_VER}-win${BITS}.exe"
|
||||
fi
|
||||
|
||||
# Bullet
|
||||
|
@ -444,13 +473,9 @@ echo
|
|||
|
||||
# Boost
|
||||
if [ -z $APPVEYOR ]; then
|
||||
printf "Boost 1.67.0... "
|
||||
printf "Boost ${BOOST_VER}... "
|
||||
else
|
||||
if [ "${MSVC_VER}" -eq 12.0 ]; then
|
||||
printf "Boost 1.58.0 AppVeyor... "
|
||||
else
|
||||
printf "Boost 1.67.0 AppVeyor... "
|
||||
fi
|
||||
printf "Boost ${BOOST_VER} AppVeyor... "
|
||||
fi
|
||||
{
|
||||
if [ -z $APPVEYOR ]; then
|
||||
|
@ -468,13 +493,13 @@ fi
|
|||
exit 1;
|
||||
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. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Boost
|
||||
CI_EXTRA_INNO_OPTIONS=""
|
||||
[ -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}"
|
||||
fi
|
||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||
|
@ -482,13 +507,10 @@ fi
|
|||
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
|
||||
echo Done.
|
||||
else
|
||||
# Appveyor unstable has all the boost we need already
|
||||
if [ $MSVC_REAL_VER -eq 12 ]; then
|
||||
BOOST_SDK="c:/Libraries/boost_1_58_0"
|
||||
else
|
||||
BOOST_SDK="c:/Libraries/boost_1_67_0"
|
||||
fi
|
||||
if [ $MSVC_REAL_VER -eq 15 ]; then
|
||||
# Appveyor has all the boost we need already
|
||||
BOOST_SDK="c:/Libraries/boost_${BOOST_VER_URL}"
|
||||
|
||||
if [ $MSVC_REAL_VER -ge 15 ]; then
|
||||
LIB_SUFFIX="1"
|
||||
else
|
||||
LIB_SUFFIX="0"
|
||||
|
@ -619,7 +641,7 @@ echo
|
|||
if [ -z $APPVEYOR ]; then
|
||||
printf "Qt 5.7.0... "
|
||||
else
|
||||
printf "Qt 5.10 AppVeyor... "
|
||||
printf "Qt 5.13 AppVeyor... "
|
||||
fi
|
||||
{
|
||||
if [ $BITS -eq 64 ]; then
|
||||
|
@ -657,7 +679,7 @@ fi
|
|||
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
|
||||
echo Done.
|
||||
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 \
|
||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
|
|
|
@ -607,7 +607,7 @@ void Record<ESM::Container>::print()
|
|||
std::cout << " Weight: " << mData.mWeight << std::endl;
|
||||
for (const ESM::ContItem &item : mData.mInventory.mList)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -653,7 +653,7 @@ void Record<ESM::Creature>::print()
|
|||
|
||||
for (const ESM::ContItem &item : mData.mInventory.mList)
|
||||
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)
|
||||
std::cout << " Spell: " << spell << std::endl;
|
||||
|
@ -1073,7 +1073,7 @@ void Record<ESM::NPC>::print()
|
|||
|
||||
for (const ESM::ContItem &item : mData.mInventory.mList)
|
||||
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)
|
||||
std::cout << " Spell: " << spell << std::endl;
|
||||
|
@ -1192,7 +1192,7 @@ void Record<ESM::Region>::print()
|
|||
if (!mData.mSleepList.empty())
|
||||
std::cout << " Sleep List: " << mData.mSleepList << std::endl;
|
||||
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<>
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace ESSImport
|
|||
|
||||
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;
|
||||
convertSCRI(scpt.mSCRI, out.mLocals);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace ESSImport
|
|||
esm.getHT(contItem);
|
||||
|
||||
InventoryItem item;
|
||||
item.mId = contItem.mItem.toString();
|
||||
item.mId = contItem.mItem;
|
||||
item.mCount = contItem.mCount;
|
||||
item.mRelativeEquipmentSlot = -1;
|
||||
item.mLockLevel = 0;
|
||||
|
|
|
@ -95,7 +95,7 @@ bool Launcher::DataFilesPage::loadSettings()
|
|||
|
||||
qDebug() << "The current profile is: " << currentProfile;
|
||||
|
||||
foreach (const QString &item, profiles)
|
||||
for (const QString &item : profiles)
|
||||
addProfile (item, false);
|
||||
|
||||
// Hack: also add the current profile
|
||||
|
@ -114,7 +114,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
|
|||
if (!mDataLocal.isEmpty())
|
||||
paths.insert(0, mDataLocal);
|
||||
|
||||
foreach(const QString &path, paths)
|
||||
for (const QString &path : paths)
|
||||
mSelector->addFiles(path);
|
||||
|
||||
PathIterator pathIterator(paths);
|
||||
|
@ -127,7 +127,7 @@ QStringList Launcher::DataFilesPage::filesInProfile(const QString& profileName,
|
|||
QStringList files = mLauncherSettings.getContentListFiles(profileName);
|
||||
QStringList filepaths;
|
||||
|
||||
foreach(const QString& file, files)
|
||||
for (const QString& file : files)
|
||||
{
|
||||
QString filepath = pathIterator.findFirstPath(file);
|
||||
|
||||
|
@ -152,7 +152,8 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile)
|
|||
mLauncherSettings.setCurrentContentListName(ui.profilesComboBox->currentText());
|
||||
|
||||
QStringList fileNames;
|
||||
foreach(const ContentSelectorModel::EsmFile *item, items) {
|
||||
for (const ContentSelectorModel::EsmFile *item : items)
|
||||
{
|
||||
fileNames.append(item->fileName());
|
||||
}
|
||||
mLauncherSettings.setContentList(profileName, fileNames);
|
||||
|
@ -164,7 +165,8 @@ QStringList Launcher::DataFilesPage::selectedFilePaths()
|
|||
//retrieve the files selected for the profile
|
||||
ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
|
||||
QStringList filePaths;
|
||||
foreach(const ContentSelectorModel::EsmFile *item, items) {
|
||||
for (const ContentSelectorModel::EsmFile *item : items)
|
||||
{
|
||||
filePaths.append(item->filePath());
|
||||
}
|
||||
return filePaths;
|
||||
|
|
|
@ -280,7 +280,8 @@ bool Launcher::MainDialog::setupLauncherSettings()
|
|||
paths.append(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();
|
||||
QFile file(path);
|
||||
if (file.exists()) {
|
||||
|
@ -338,7 +339,8 @@ bool Launcher::MainDialog::setupGameSettings()
|
|||
paths.append(localPath + 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();
|
||||
|
||||
file.setFileName(path2);
|
||||
|
@ -366,7 +368,8 @@ bool Launcher::MainDialog::setupGameData()
|
|||
QStringList dataDirs;
|
||||
|
||||
// Check if the paths actually contain data files
|
||||
foreach (const QString path3, mGameSettings.getDataDirs()) {
|
||||
for (const QString& path3 : mGameSettings.getDataDirs())
|
||||
{
|
||||
QDir dir(path3);
|
||||
QStringList filters;
|
||||
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
|
||||
|
|
|
@ -61,7 +61,8 @@ Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg,
|
|||
// Detect Morrowind configuration files
|
||||
QStringList iniPaths;
|
||||
|
||||
foreach (const QString &path, mGameSettings.getDataDirs()) {
|
||||
for (const QString &path : mGameSettings.getDataDirs())
|
||||
{
|
||||
QDir dir(path);
|
||||
dir.setPath(dir.canonicalPath()); // Resolve symlinks
|
||||
|
||||
|
|
|
@ -82,14 +82,14 @@ opencs_units_noqt (view/world
|
|||
|
||||
opencs_units (view/widget
|
||||
scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton
|
||||
scenetooltoggle2 scenetooltexturebrush completerpopup coloreditor colorpickerpopup droplineedit
|
||||
scenetooltoggle2 scenetooltexturebrush scenetoolshapebrush completerpopup coloreditor colorpickerpopup droplineedit
|
||||
)
|
||||
|
||||
opencs_units (view/render
|
||||
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
||||
previewwidget editmode instancemode instanceselectionmode instancemovemode
|
||||
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
|
||||
cellwater terraintexturemode actor terrainselection
|
||||
cellwater terraintexturemode actor terrainselection terrainshapemode
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/render
|
||||
|
|
|
@ -228,7 +228,7 @@ void CS::Editor::openFiles (const boost::filesystem::path &savePath, const std::
|
|||
|
||||
if(discoveredFiles.empty())
|
||||
{
|
||||
foreach(const QString &path, mFileDialog.selectedFilePaths())
|
||||
for (const QString &path : mFileDialog.selectedFilePaths())
|
||||
files.push_back(path.toUtf8().constData());
|
||||
}
|
||||
else
|
||||
|
@ -245,7 +245,7 @@ void CS::Editor::createNewFile (const boost::filesystem::path &savePath)
|
|||
{
|
||||
std::vector<boost::filesystem::path> files;
|
||||
|
||||
foreach (const QString &path, mFileDialog.selectedFilePaths()) {
|
||||
for (const QString &path : mFileDialog.selectedFilePaths()) {
|
||||
files.push_back(path.toUtf8().constData());
|
||||
}
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ void CSMPrefs::State::declare()
|
|||
"list go to the first/last item");
|
||||
|
||||
declareCategory ("3D Scene Input");
|
||||
|
||||
|
||||
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);
|
||||
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);
|
||||
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-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);
|
||||
declareSeparator();
|
||||
|
||||
|
@ -242,12 +242,24 @@ void CSMPrefs::State::declare()
|
|||
addValues (insertOutsideCell);
|
||||
declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert).
|
||||
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);
|
||||
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);
|
||||
declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50).
|
||||
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).
|
||||
setTooltip ("When opening a reference from the scene view, it will open the"
|
||||
" instance list view instead of the individual instance record view.");
|
||||
|
|
|
@ -910,7 +910,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
|
|||
{
|
||||
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);
|
||||
|
||||
if (localIndex.first == -1)
|
||||
|
|
|
@ -45,7 +45,7 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)
|
|||
for (const ESM::Region::SoundRef& sound : region.mSoundList)
|
||||
{
|
||||
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
|
||||
|
|
|
@ -538,7 +538,7 @@ namespace CSMWorld
|
|||
for (auto& item : npc.mInventory.mList)
|
||||
{
|
||||
if (item.mCount <= 0) continue;
|
||||
std::string itemId = item.mItem.toString();
|
||||
std::string itemId = item.mItem;
|
||||
addNpcItem(itemId, data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ namespace CSMWorld
|
|||
|
||||
DataType values(Size, 0);
|
||||
|
||||
if (land.isDataLoaded(Land::DATA_WNAM))
|
||||
if (land.mDataTypes & Land::DATA_WNAM)
|
||||
{
|
||||
for (int i = 0; i < Size; ++i)
|
||||
values[i] = land.mWnam[i];
|
||||
|
|
|
@ -396,7 +396,7 @@ namespace CSMWorld
|
|||
ESM::Region::SoundRef soundRef = soundList[subRowIndex];
|
||||
switch (subColIndex)
|
||||
{
|
||||
case 0: return QString(soundRef.mSound.toString().c_str());
|
||||
case 0: return QString(soundRef.mSound.c_str());
|
||||
case 1: return soundRef.mChance;
|
||||
default: throw std::runtime_error("Region sounds subcolumn index out of range");
|
||||
}
|
||||
|
|
|
@ -1257,7 +1257,7 @@ namespace CSMWorld
|
|||
|
||||
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;
|
||||
default:
|
||||
throw std::runtime_error("Trying to access non-existing column in the nested table!");
|
||||
|
|
|
@ -47,7 +47,7 @@ QStringList CSVDoc::FileDialog::selectedFilePaths()
|
|||
{
|
||||
QStringList filePaths;
|
||||
|
||||
foreach (ContentSelectorModel::EsmFile *file, mSelector->selectedFiles() )
|
||||
for (ContentSelectorModel::EsmFile *file : mSelector->selectedFiles() )
|
||||
filePaths.append(file->filePath());
|
||||
|
||||
return filePaths;
|
||||
|
|
|
@ -404,7 +404,7 @@ void CSVDoc::View::updateSubViewIndices(SubView *view)
|
|||
|
||||
updateTitle();
|
||||
|
||||
foreach (SubView *subView, mSubViews)
|
||||
for (SubView *subView : mSubViews)
|
||||
{
|
||||
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)
|
||||
if (windows["reuse"].isTrue())
|
||||
{
|
||||
foreach(SubView *sb, mSubViews)
|
||||
for (SubView *sb : mSubViews)
|
||||
{
|
||||
bool isSubViewReferenceable =
|
||||
sb->getUniversalId().getType() == CSMWorld::UniversalId::Type_Referenceable;
|
||||
|
@ -975,7 +975,7 @@ void CSVDoc::View::resizeViewHeight (int height)
|
|||
|
||||
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))
|
||||
subView->setStatusBar (show);
|
||||
|
|
|
@ -134,7 +134,7 @@ void CSVRender::Cell::updateLand()
|
|||
else
|
||||
{
|
||||
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);
|
||||
|
@ -149,7 +149,6 @@ void CSVRender::Cell::updateLand()
|
|||
}
|
||||
|
||||
// No land data
|
||||
mLandDeleted = true;
|
||||
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);
|
||||
|
||||
mTerrainStorage = new TerrainStorage(mData);
|
||||
|
||||
if (result.second)
|
||||
mCoordinates = result.first;
|
||||
|
||||
|
@ -347,6 +348,28 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
|
|||
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()
|
||||
{
|
||||
if (mPathgrid)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <osg/ref_ptr>
|
||||
|
||||
#include "../../model/world/cellcoordinates.hpp"
|
||||
#include "terrainstorage.hpp"
|
||||
|
||||
class QModelIndex;
|
||||
|
||||
|
@ -58,6 +59,7 @@ namespace CSVRender
|
|||
int mSubMode;
|
||||
unsigned int mSubModeElementMask;
|
||||
bool mUpdateLand, mLandDeleted;
|
||||
TerrainStorage *mTerrainStorage;
|
||||
|
||||
/// Ignored if cell does not have an object with the given ID.
|
||||
///
|
||||
|
@ -118,6 +120,14 @@ namespace CSVRender
|
|||
/// this cell?
|
||||
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 pathgridRemoved();
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "cameracontroller.hpp"
|
||||
#include "cellarrow.hpp"
|
||||
#include "terraintexturemode.hpp"
|
||||
#include "terrainshapemode.hpp"
|
||||
|
||||
bool CSVRender::PagedWorldspaceWidget::adjustCells()
|
||||
{
|
||||
|
@ -137,11 +138,9 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
|
|||
|
||||
/// \todo replace EditMode with suitable subclasses
|
||||
tool->addButton (
|
||||
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"),
|
||||
"terrain-shape");
|
||||
new TerrainShapeMode (this, mRootNode, tool), "terrain-shape");
|
||||
tool->addButton (
|
||||
new TerrainTextureMode (this, mRootNode, tool),
|
||||
"terrain-texture");
|
||||
new TerrainTextureMode (this, mRootNode, tool), "terrain-texture");
|
||||
tool->addButton (
|
||||
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"),
|
||||
"terrain-vertex");
|
||||
|
@ -791,6 +790,36 @@ CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& poi
|
|||
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 (
|
||||
unsigned int elementMask) const
|
||||
{
|
||||
|
|
|
@ -124,6 +124,14 @@ namespace CSVRender
|
|||
|
||||
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)
|
||||
const;
|
||||
|
||||
|
|
|
@ -249,13 +249,11 @@ int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global ver
|
|||
int localX = x - cellX * (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();
|
||||
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
|
||||
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
|
||||
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
|
||||
const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();
|
||||
float landHeight = 0.f;
|
||||
if (CSVRender::Cell* cell = dynamic_cast<CSVRender::Cell*>(mWorldspaceWidget->getCell(coords)))
|
||||
landHeight = cell->getSumOfAlteredAndTrueHeight(cellX, cellY, localX, localY);
|
||||
|
||||
return mPointer[localY*ESM::Land::LAND_SIZE + localX];
|
||||
return landHeight;
|
||||
}
|
||||
|
|
1447
apps/opencs/view/render/terrainshapemode.cpp
Normal file
1447
apps/opencs/view/render/terrainshapemode.cpp
Normal file
File diff suppressed because it is too large
Load diff
190
apps/opencs/view/render/terrainshapemode.hpp
Normal file
190
apps/opencs/view/render/terrainshapemode.hpp
Normal 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
|
|
@ -3,13 +3,15 @@
|
|||
#include "../../model/world/land.hpp"
|
||||
#include "../../model/world/landtexture.hpp"
|
||||
|
||||
#include <components/esmterrain/storage.hpp>
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
|
||||
TerrainStorage::TerrainStorage(const CSMWorld::Data &data)
|
||||
: ESMTerrain::Storage(data.getResourceSystem()->getVFS())
|
||||
, mData(data)
|
||||
{
|
||||
resetHeights();
|
||||
}
|
||||
|
||||
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY)
|
||||
|
@ -33,10 +35,118 @@ namespace CSVRender
|
|||
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)
|
||||
{
|
||||
// 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");
|
||||
}
|
||||
|
||||
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)];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
#ifndef OPENCS_RENDER_TERRAINSTORAGE_H
|
||||
#define OPENCS_RENDER_TERRAINSTORAGE_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <components/esmterrain/storage.hpp>
|
||||
|
||||
#include "../../model/world/data.hpp"
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A bridge between the terrain component and OpenCS's terrain data storage.
|
||||
*/
|
||||
|
@ -15,13 +16,34 @@ namespace CSVRender
|
|||
{
|
||||
public:
|
||||
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:
|
||||
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;
|
||||
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override;
|
||||
osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) final;
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -150,6 +150,11 @@ CSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const osg::Vec3d& p
|
|||
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 (
|
||||
unsigned int elementMask) const
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace CSMDoc
|
|||
namespace CSMWorld
|
||||
{
|
||||
class IdTable;
|
||||
class CellCoordinates;
|
||||
}
|
||||
|
||||
namespace CSVRender
|
||||
|
@ -63,6 +64,8 @@ namespace CSVRender
|
|||
|
||||
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)
|
||||
const;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace CSMPrefs
|
|||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class CellCoordinates;
|
||||
class UniversalId;
|
||||
}
|
||||
|
||||
|
@ -170,6 +171,8 @@ namespace CSVRender
|
|||
/// \note Returns the cell if it exists, otherwise a null pointer
|
||||
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)
|
||||
const = 0;
|
||||
|
||||
|
|
14
apps/opencs/view/widget/brushshapes.hpp
Normal file
14
apps/opencs/view/widget/brushshapes.hpp
Normal 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
|
263
apps/opencs/view/widget/scenetoolshapebrush.cpp
Normal file
263
apps/opencs/view/widget/scenetoolshapebrush.cpp
Normal 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();
|
||||
}
|
127
apps/opencs/view/widget/scenetoolshapebrush.hpp
Normal file
127
apps/opencs/view/widget/scenetoolshapebrush.hpp
Normal 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
|
|
@ -343,7 +343,7 @@ std::vector< CSMWorld::UniversalId > CSVWorld::RegionMap::getDraggedRecords() co
|
|||
{
|
||||
QModelIndexList selected(getSelectedCells(true, false));
|
||||
std::vector<CSMWorld::UniversalId> ids;
|
||||
foreach (QModelIndex it, selected)
|
||||
for (const QModelIndex& it : selected)
|
||||
{
|
||||
ids.push_back(
|
||||
CSMWorld::UniversalId(
|
||||
|
@ -351,7 +351,7 @@ std::vector< CSMWorld::UniversalId > CSVWorld::RegionMap::getDraggedRecords() co
|
|||
model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData()));
|
||||
}
|
||||
selected = getSelectedCells(false, true);
|
||||
foreach (QModelIndex it, selected)
|
||||
for (const QModelIndex& it : selected)
|
||||
{
|
||||
ids.push_back(
|
||||
CSMWorld::UniversalId(
|
||||
|
|
|
@ -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))
|
||||
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)
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace MWGui
|
|||
public:
|
||||
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);
|
||||
|
||||
bool hasProfit(const MWWorld::Ptr& actor);
|
||||
|
|
|
@ -91,12 +91,12 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
|
|||
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];
|
||||
if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))
|
||||
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)
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace MWGui
|
|||
virtual ModelIndex getIndex (ItemStack item);
|
||||
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 update();
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace MWGui
|
|||
public:
|
||||
WorldItemModel(float left, float top) : mLeft(left), mTop(top) {}
|
||||
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();
|
||||
|
||||
|
|
|
@ -46,11 +46,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item)
|
|||
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))
|
||||
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)
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace MWGui
|
|||
|
||||
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);
|
||||
|
||||
/// Move items from this model to \a otherModel.
|
||||
|
|
|
@ -116,9 +116,9 @@ namespace MWGui
|
|||
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)
|
||||
|
|
|
@ -65,9 +65,7 @@ namespace MWGui
|
|||
/// @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);
|
||||
|
||||
/// @param setNewOwner If true, set the copied item's owner to the actor we are copying to,
|
||||
/// otherwise reset owner to ""
|
||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count) = 0;
|
||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0;
|
||||
virtual void removeItem (const ItemStack& item, size_t count) = 0;
|
||||
|
||||
/// 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 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 ModelIndex getIndex (ItemStack item);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "tradeitemmodel.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
|
@ -139,8 +140,9 @@ namespace MWGui
|
|||
throw std::runtime_error("The borrowed item disappeared");
|
||||
|
||||
const ItemStack& item = sourceModel->getItem(i);
|
||||
static const bool prevent = Settings::Manager::getBool("prevent merchant equipping", "Game");
|
||||
// copy the borrowed items to our model
|
||||
copyItem(item, itemStack.mCount);
|
||||
copyItem(item, itemStack.mCount, !prevent);
|
||||
// then remove them from the source model
|
||||
sourceModel->removeItem(item, itemStack.mCount);
|
||||
}
|
||||
|
|
|
@ -344,7 +344,7 @@ namespace MWMechanics
|
|||
if (actor.getClass().getCreatureStats(actor).isDead())
|
||||
return;
|
||||
|
||||
if (!actor.getClass().hasInventoryStore(actor) || !actor.getClass().getInventoryStore(actor).canActorAutoEquip(actor))
|
||||
if (!actor.getClass().hasInventoryStore(actor))
|
||||
return;
|
||||
|
||||
if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||
|
@ -1293,7 +1293,7 @@ namespace MWMechanics
|
|||
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
@ -2056,6 +2056,7 @@ namespace MWMechanics
|
|||
// One case where we need this is to make sure bound items are removed upon death
|
||||
stats.modifyMagicEffects(MWMechanics::MagicEffects());
|
||||
stats.getActiveSpells().clear();
|
||||
stats.getSpells().clear();
|
||||
// Make sure spell effects are removed
|
||||
purgeSpellEffects(stats.getActorId());
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
|||
|
||||
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);
|
||||
mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),
|
||||
|
@ -328,11 +328,11 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin
|
|||
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()
|
||||
|| (distance(mPathFinder.getPath().back(), newDest) > 10)
|
||||
|| mPathFinder.getPathCell() != currentCell;
|
||||
|| getPathDistance(actor, mPathFinder.getPath().back(), newDest) > 10
|
||||
|| mPathFinder.getPathCell() != actor.getCell();
|
||||
}
|
||||
|
||||
bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position)
|
||||
|
|
|
@ -120,7 +120,7 @@ namespace MWMechanics
|
|||
/// 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 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);
|
||||
|
||||
|
|
|
@ -493,6 +493,11 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
|
|||
if (movement == mMovementState && idle == mIdleState && !force)
|
||||
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;
|
||||
MWRender::Animation::BlendMask movemask;
|
||||
const StateInfo *movestate;
|
||||
|
@ -524,14 +529,9 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
|
|||
// For upper body there will be idle animation.
|
||||
if (movemask == MWRender::Animation::BlendMask_LowerBody && idle == CharState_None)
|
||||
idle = CharState_Idle;
|
||||
}
|
||||
else if (idle == CharState_None)
|
||||
{
|
||||
// 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;
|
||||
|
||||
if (movemask == MWRender::Animation::BlendMask_LowerBody)
|
||||
resetIdle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -554,7 +554,11 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
|
|||
if(mAnimation->hasAnimation(weapMovementAnimName))
|
||||
movementAnimName = weapMovementAnimName;
|
||||
else
|
||||
{
|
||||
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;
|
||||
|
||||
mAnimation->disable(mCurrentMovement);
|
||||
|
||||
if (!mAnimation->hasAnimation(movementAnimName))
|
||||
movementAnimName.clear();
|
||||
|
||||
mCurrentMovement = movementAnimName;
|
||||
if(!mCurrentMovement.empty())
|
||||
{
|
||||
|
@ -623,7 +631,12 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
|
|||
|
||||
mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false,
|
||||
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 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);
|
||||
else if(evt.compare(off, len, "unequip detach") == 0)
|
||||
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
|
||||
// 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.
|
||||
return !(getWeaponType(weaptype)->mFlags & ESM::WeaponType::TwoHanded);
|
||||
return mAnimation->updateCarriedLeftVisible(weaptype);
|
||||
}
|
||||
|
||||
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
|
||||
weapgroup = getWeaponAnimation(mWeaponType);
|
||||
mAnimation->play(weapgroup, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, false,
|
||||
int unequipMask = MWRender::Animation::BlendMask_All;
|
||||
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);
|
||||
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
||||
|
||||
|
@ -1361,7 +1389,10 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
|
|||
if (weaptype != mWeaponType)
|
||||
{
|
||||
forcestateupdate = true;
|
||||
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
|
||||
bool useShieldAnims = mAnimation->useShieldAnimations();
|
||||
if (!useShieldAnims)
|
||||
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
|
||||
|
||||
weapgroup = getWeaponAnimation(weaptype);
|
||||
|
||||
// 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)
|
||||
{
|
||||
mAnimation->showWeapons(false);
|
||||
mAnimation->play(weapgroup, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, true,
|
||||
int equipMask = MWRender::Animation::BlendMask_All;
|
||||
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);
|
||||
mUpperBodyState = UpperCharState_EquipingWeap;
|
||||
|
||||
|
@ -2313,10 +2352,7 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
|
||||
if(mAnimQueue.empty() || inwater || sneak)
|
||||
{
|
||||
// Note: turning animations should not interrupt idle ones
|
||||
if (movestate != CharState_None && !isTurning())
|
||||
idlestate = CharState_None;
|
||||
else if (inwater)
|
||||
if (inwater)
|
||||
idlestate = CharState_IdleSwim;
|
||||
else if (sneak && !inJump)
|
||||
idlestate = CharState_IdleSneak;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "spellcasting.hpp"
|
||||
#include "difficultyscaling.hpp"
|
||||
#include "actorutil.hpp"
|
||||
#include "pathfinding.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -327,26 +328,17 @@ namespace MWMechanics
|
|||
|
||||
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)
|
||||
|
||||
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)
|
||||
localAttack->applyWeaponEnchantment = appliedEnchantment;
|
||||
|
||||
if (weapon != projectile)
|
||||
{
|
||||
appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, projectile, hitPosition, true);
|
||||
|
||||
if (localAttack)
|
||||
localAttack->applyAmmoEnchantment = appliedEnchantment;
|
||||
}
|
||||
localAttack->applyAmmoEnchantment = appliedEnchantment;
|
||||
/*
|
||||
End of tes3mp change (minor)
|
||||
*/
|
||||
|
@ -581,13 +573,8 @@ namespace MWMechanics
|
|||
{
|
||||
osg::Vec3f pos1 (actor1.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(
|
||||
"iFightDistanceBase")->mValue.getInteger();
|
||||
|
@ -603,4 +590,11 @@ namespace MWMechanics
|
|||
return (magicEffects.get(ESM::MagicEffect::Invisibility).getMagnitude() > 0)
|
||||
|| (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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,9 @@ void applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon,
|
|||
float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2);
|
||||
|
||||
bool isTargetMagicallyHidden(const MWWorld::Ptr& target);
|
||||
|
||||
float getAggroDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "creaturestats.hpp"
|
||||
#include "spellcasting.hpp"
|
||||
#include "actorutil.hpp"
|
||||
#include "weapontype.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
@ -38,13 +39,13 @@ namespace MWMechanics
|
|||
void Enchanting::setOldItem(const MWWorld::Ptr& oldItem)
|
||||
{
|
||||
mOldItemPtr=oldItem;
|
||||
mWeaponType = -1;
|
||||
mObjectType.clear();
|
||||
if(!itemEmpty())
|
||||
{
|
||||
mObjectType = mOldItemPtr.getTypeName();
|
||||
}
|
||||
else
|
||||
{
|
||||
mObjectType="";
|
||||
if (mObjectType == typeid(ESM::Weapon).name())
|
||||
mWeaponType = mOldItemPtr.get<ESM::Weapon>()->mBase->mData.mType;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +149,7 @@ namespace MWMechanics
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if(mObjectType == typeid(ESM::Weapon).name())
|
||||
else if (mWeaponType != -1)
|
||||
{ // Weapon
|
||||
switch(mCastStyle)
|
||||
{
|
||||
|
@ -158,11 +159,13 @@ namespace MWMechanics
|
|||
case ESM::Enchantment::WhenUsed:
|
||||
if (powerfulSoul)
|
||||
mCastStyle = ESM::Enchantment::ConstantEffect;
|
||||
else
|
||||
else if (getWeaponType(mWeaponType)->mWeaponClass != ESM::WeaponType::Ranged)
|
||||
mCastStyle = ESM::Enchantment::WhenStrikes;
|
||||
return;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace MWMechanics
|
|||
|
||||
std::string mNewItemName;
|
||||
std::string mObjectType;
|
||||
int mWeaponType;
|
||||
|
||||
public:
|
||||
Enchanting();
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "pathgrid.hpp"
|
||||
#include "coordinateconverter.hpp"
|
||||
#include "actorutil.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -80,10 +81,24 @@ namespace
|
|||
const auto realHalfExtents = world->getHalfExtents(actor);
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
osg::Vec3f dir = to - from;
|
||||
|
|
|
@ -13,17 +13,29 @@ namespace MWWorld
|
|||
{
|
||||
class CellStore;
|
||||
class ConstPtr;
|
||||
class Ptr;
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return std::atan2(dir.x(), dir.y());
|
||||
|
|
|
@ -62,6 +62,7 @@ ActorAnimation::~ActorAnimation()
|
|||
}
|
||||
|
||||
mScabbard.reset();
|
||||
mHolsteredShield.reset();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!mObjectRoot)
|
||||
|
|
|
@ -38,11 +38,15 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
|
|||
virtual void itemAdded(const MWWorld::ConstPtr& item, int count);
|
||||
virtual void itemRemoved(const MWWorld::ConstPtr& item, int count);
|
||||
virtual bool isArrowAttached() const { return false; }
|
||||
virtual bool useShieldAnimations() const;
|
||||
bool updateCarriedLeftVisible(const int weaptype) const;
|
||||
|
||||
protected:
|
||||
osg::Group* getBoneByName(const std::string& boneName);
|
||||
virtual void updateHolsteredWeapon(bool showHolsteredWeapons);
|
||||
virtual void updateHolsteredShield(bool showCarriedLeft);
|
||||
virtual void updateQuiver();
|
||||
virtual std::string getShieldMesh(MWWorld::ConstPtr shield) const;
|
||||
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)
|
||||
|
@ -52,6 +56,7 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
|
|||
};
|
||||
|
||||
PartHolderPtr mScabbard;
|
||||
PartHolderPtr mHolsteredShield;
|
||||
|
||||
private:
|
||||
void addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight);
|
||||
|
|
|
@ -153,6 +153,8 @@ public:
|
|||
|
||||
void setTextKeyListener(TextKeyListener* listener);
|
||||
|
||||
virtual bool updateCarriedLeftVisible(const int weaptype) const { return false; };
|
||||
|
||||
protected:
|
||||
class AnimationTime : public SceneUtil::ControllerSource
|
||||
{
|
||||
|
@ -453,6 +455,7 @@ public:
|
|||
/// @note The matching is case-insensitive.
|
||||
const osg::Node* getNode(const std::string& name) const;
|
||||
|
||||
virtual bool useShieldAnimations() const { return false; }
|
||||
virtual void showWeapons(bool showWeapon) {}
|
||||
virtual void showCarriedLeft(bool show) {}
|
||||
virtual void setWeaponGroup(const std::string& group, bool relativeDuration) {}
|
||||
|
|
|
@ -90,6 +90,7 @@ void CreatureWeaponAnimation::updateParts()
|
|||
|
||||
updateHolsteredWeapon(!mShowWeapons);
|
||||
updateQuiver();
|
||||
updateHolsteredShield(mShowCarriedLeft);
|
||||
|
||||
if (mShowWeapons)
|
||||
updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include <components/nifosg/nifloader.hpp> // TextKeyMapHolder
|
||||
|
||||
#include <components/vfs/manager.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -511,6 +513,55 @@ void NpcAnimation::updateNpcBase()
|
|||
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()
|
||||
{
|
||||
if (!mObjectRoot.get())
|
||||
|
@ -954,6 +1005,8 @@ void NpcAnimation::showCarriedLeft(bool show)
|
|||
}
|
||||
else
|
||||
removeIndividualPart(ESM::PRT_Shield);
|
||||
|
||||
updateHolsteredShield(mShowCarriedLeft);
|
||||
}
|
||||
|
||||
void NpcAnimation::attachArrow()
|
||||
|
@ -1051,6 +1104,14 @@ void NpcAnimation::setWeaponGroup(const std::string &group, bool relativeDuratio
|
|||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ private:
|
|||
protected:
|
||||
virtual void addControllers();
|
||||
virtual bool isArrowAttached() const;
|
||||
virtual std::string getShieldMesh(MWWorld::ConstPtr shield) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
|
|
@ -225,7 +225,7 @@ protected:
|
|||
virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
|
||||
{
|
||||
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(1, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
|
@ -1108,7 +1108,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
|
|||
, mMonth(0)
|
||||
, mCloudAnimationTimer(0.f)
|
||||
, mRainTimer(0.f)
|
||||
, mStormDirection(0,-1,0)
|
||||
, mStormDirection(0,1,0)
|
||||
, mClouds()
|
||||
, mNextClouds()
|
||||
, mCloudBlendFactor(0.0f)
|
||||
|
@ -1597,10 +1597,14 @@ void SkyManager::update(float duration)
|
|||
osg::Quat quat;
|
||||
quat.makeRotate(osg::Vec3f(0,1,0), mStormDirection);
|
||||
|
||||
if (mParticleNode)
|
||||
mParticleNode->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
|
||||
mCloudNode->setAttitude(osg::Quat());
|
||||
|
@ -1636,7 +1640,7 @@ void SkyManager::updateRainParameters()
|
|||
{
|
||||
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->setAngle(angle);
|
||||
|
||||
|
|
|
@ -527,7 +527,7 @@ namespace MWScript
|
|||
|
||||
MWMechanics::MagicEffects effects = stats.getSpells().getMagicEffects();
|
||||
effects += stats.getActiveSpells().getMagicEffects();
|
||||
if (ptr.getClass().hasInventoryStore(ptr))
|
||||
if (ptr.getClass().hasInventoryStore(ptr) && !stats.isDeathAnimationFinished())
|
||||
{
|
||||
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
|
||||
effects += store.getMagicEffects();
|
||||
|
|
|
@ -924,7 +924,7 @@ namespace MWSound
|
|||
{
|
||||
if(r - pos < sndref.mChance)
|
||||
{
|
||||
playSound(sndref.mSound.toString(), 1.0f, 1.0f);
|
||||
playSound(sndref.mSound, 1.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
pos += sndref.mChance;
|
||||
|
|
|
@ -66,6 +66,11 @@ namespace MWWorld
|
|||
return mCellRef.mRefID;
|
||||
}
|
||||
|
||||
const std::string* CellRef::getRefIdPtr() const
|
||||
{
|
||||
return &mCellRef.mRefID;
|
||||
}
|
||||
|
||||
bool CellRef::getTeleport() const
|
||||
{
|
||||
return mCellRef.mTeleport;
|
||||
|
|
|
@ -66,6 +66,9 @@ namespace MWWorld
|
|||
// Id of object being referenced
|
||||
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
|
||||
// if it should open through animation.
|
||||
bool getTeleport() const;
|
||||
|
|
|
@ -150,7 +150,7 @@ namespace
|
|||
{
|
||||
for (typename MWWorld::CellRefList<T>::List::iterator iter (collection.mList.begin());
|
||||
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
|
||||
iter->load (state);
|
||||
|
@ -464,7 +464,7 @@ namespace MWWorld
|
|||
const std::string *mIdToFind;
|
||||
bool operator()(const PtrType& ptr)
|
||||
{
|
||||
if (ptr.getCellRef().getRefId() == *mIdToFind)
|
||||
if (*ptr.getCellRef().getRefIdPtr() == *mIdToFind)
|
||||
{
|
||||
mFound = ptr;
|
||||
return false;
|
||||
|
|
|
@ -297,7 +297,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &
|
|||
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();
|
||||
|
||||
|
@ -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();
|
||||
++iter)
|
||||
{
|
||||
std::string id = Misc::StringUtils::lowerCase(iter->mItem.toString());
|
||||
std::string id = Misc::StringUtils::lowerCase(iter->mItem);
|
||||
addInitialItem(id, owner, iter->mCount);
|
||||
}
|
||||
|
||||
|
@ -718,10 +718,10 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW
|
|||
if (it->mCount >= 0)
|
||||
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 (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);
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ namespace MWWorld
|
|||
|
||||
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)
|
||||
///
|
||||
/// \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
|
||||
/// 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.
|
||||
|
||||
ContainerStoreIterator add(const std::string& id, int count, const Ptr& actorPtr);
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <components/esm/loadench.hpp>
|
||||
#include <components/esm/inventorystate.hpp>
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
@ -144,12 +143,12 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor
|
|||
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
|
||||
if (actorPtr != MWMechanics::getPlayer()
|
||||
if (allowAutoEquip && actorPtr != MWMechanics::getPlayer()
|
||||
&& actorPtr.getClass().isNpc() && !actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())
|
||||
{
|
||||
std::string type = itemPtr.getTypeName();
|
||||
|
@ -221,33 +220,6 @@ MWWorld::ConstContainerStoreIterator MWWorld::InventoryStore::getSlot (int 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
|
||||
{
|
||||
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
|
||||
*/
|
||||
|
||||
if (!canActorAutoEquip(actor))
|
||||
return;
|
||||
|
||||
TSlots slots_;
|
||||
initSlots (slots_);
|
||||
|
||||
|
|
|
@ -124,17 +124,15 @@ namespace MWWorld
|
|||
|
||||
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)
|
||||
/// 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.
|
||||
///
|
||||
/// \attention Do not add items to an existing stack by increasing the count instead of
|
||||
/// 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.
|
||||
|
||||
void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor);
|
||||
|
@ -160,8 +158,6 @@ namespace MWWorld
|
|||
void autoEquip (const MWWorld::Ptr& actor);
|
||||
///< Auto equip items according to stats and item value.
|
||||
|
||||
bool canActorAutoEquip(const MWWorld::Ptr& actor);
|
||||
|
||||
const MWMechanics::MagicEffects& getMagicEffects() const;
|
||||
///< Return magic effects from worn items.
|
||||
|
||||
|
|
|
@ -745,17 +745,6 @@ void WeatherManager::setRegionWeather(const std::string& region, const int curre
|
|||
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 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;
|
||||
|
||||
if (updatedSpeed > 0.5f * targetSpeed && updatedSpeed < 2.f * targetSpeed)
|
||||
{
|
||||
currentSpeed = updatedSpeed;
|
||||
}
|
||||
|
||||
// Take in account direction to the Red Mountain, when needed
|
||||
if (weatherId == 6 || weatherId == 7)
|
||||
{
|
||||
currentSpeed = (calculateStormDirection() * currentSpeed).length();
|
||||
}
|
||||
|
||||
return currentSpeed;
|
||||
}
|
||||
|
@ -834,7 +815,16 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time,
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -426,7 +426,6 @@ namespace MWWorld
|
|||
bool updateWeatherRegion(const std::string& playerRegion);
|
||||
void updateWeatherTransitions(const float elapsedRealSeconds);
|
||||
void forceWeather(const int weatherID);
|
||||
osg::Vec3f calculateStormDirection();
|
||||
|
||||
bool inTransition();
|
||||
void addWeatherTransition(const int weatherID);
|
||||
|
|
|
@ -25,6 +25,8 @@ if (GTEST_FOUND AND GMOCK_FOUND)
|
|||
detournavigator/recastmeshobject.cpp
|
||||
detournavigator/navmeshtilescache.cpp
|
||||
detournavigator/tilecachedrecastmeshmanager.cpp
|
||||
|
||||
settings/parser.cpp
|
||||
)
|
||||
|
||||
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})
|
||||
|
|
|
@ -123,14 +123,14 @@ namespace
|
|||
osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375),
|
||||
osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375),
|
||||
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(125.5897216796875, -125.5897216796875, -31.063449859619140625),
|
||||
osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625),
|
||||
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010540008544921875),
|
||||
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985321044921875),
|
||||
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386486053466796875),
|
||||
osg::Vec3f(215, -215, 1.877177715301513671875),
|
||||
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625),
|
||||
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625),
|
||||
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125),
|
||||
osg::Vec3f(215, -215, 1.87718021869659423828125),
|
||||
})) << mPath;
|
||||
}
|
||||
|
||||
|
@ -173,14 +173,14 @@ namespace
|
|||
osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375),
|
||||
osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375),
|
||||
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(125.5897216796875, -125.5897216796875, -31.063449859619140625),
|
||||
osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625),
|
||||
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010540008544921875),
|
||||
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985321044921875),
|
||||
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386486053466796875),
|
||||
osg::Vec3f(215, -215, 1.877177715301513671875),
|
||||
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625),
|
||||
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625),
|
||||
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125),
|
||||
osg::Vec3f(215, -215, 1.87718021869659423828125),
|
||||
})) << mPath;
|
||||
|
||||
mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity());
|
||||
|
@ -292,14 +292,14 @@ namespace
|
|||
osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375),
|
||||
osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375),
|
||||
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(125.5897216796875, -125.5897216796875, -31.063449859619140625),
|
||||
osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625),
|
||||
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010540008544921875),
|
||||
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985321044921875),
|
||||
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386486053466796875),
|
||||
osg::Vec3f(215, -215, 1.877177715301513671875),
|
||||
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625),
|
||||
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625),
|
||||
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125),
|
||||
osg::Vec3f(215, -215, 1.87718021869659423828125),
|
||||
})) << mPath;
|
||||
}
|
||||
|
||||
|
@ -645,14 +645,14 @@ namespace
|
|||
osg::Vec3f(25.41626739501953125, -25.41626739501953125, -67.9694671630859375),
|
||||
osg::Vec3f(45.450958251953125, -45.450958251953125, -60.5882568359375),
|
||||
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(125.5897216796875, -125.5897216796875, -31.063449859619140625),
|
||||
osg::Vec3f(145.6244049072265625, -145.6244049072265625, -23.6822509765625),
|
||||
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010540008544921875),
|
||||
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985321044921875),
|
||||
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386486053466796875),
|
||||
osg::Vec3f(215, -215, 1.877177715301513671875),
|
||||
osg::Vec3f(165.659088134765625, -165.659088134765625, -16.3010501861572265625),
|
||||
osg::Vec3f(185.6937713623046875, -185.6937713623046875, -8.91985416412353515625),
|
||||
osg::Vec3f(205.7284698486328125, -205.7284698486328125, -1.5386505126953125),
|
||||
osg::Vec3f(215, -215, 1.87718021869659423828125),
|
||||
})) << mPath;
|
||||
}
|
||||
}
|
||||
|
|
411
apps/openmw_test_suite/settings/parser.cpp
Normal file
411
apps/openmw_test_suite/settings/parser.cpp
Normal 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"}
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -139,7 +139,8 @@ bool Wizard::ComponentSelectionPage::validatePage()
|
|||
mWizard->mInstallations[path].hasBloodmoon = false;
|
||||
QList<QListWidgetItem*> items = componentsList->findItems(QLatin1String("Bloodmoon"), Qt::MatchStartsWith);
|
||||
|
||||
foreach (QListWidgetItem *item, items) {
|
||||
for (QListWidgetItem *item : items)
|
||||
{
|
||||
item->setText(QLatin1String("Bloodmoon"));
|
||||
item->setCheckState(Qt::Checked);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ void Wizard::ExistingInstallationPage::initializePage()
|
|||
// Hide the default item if there are installations to choose from
|
||||
installationsList->item(0)->setHidden(!paths.isEmpty());
|
||||
|
||||
foreach (const QString &path, paths)
|
||||
for (const QString &path : paths)
|
||||
{
|
||||
if (installationsList->findItems(path, Qt::MatchExactly).isEmpty())
|
||||
{
|
||||
|
|
|
@ -21,7 +21,8 @@ QStringList Wizard::IniSettings::findKeys(const QString &text)
|
|||
{
|
||||
QStringList result;
|
||||
|
||||
foreach (const QString &key, mSettings.keys()) {
|
||||
for (const QString &key : mSettings.keys())
|
||||
{
|
||||
|
||||
if (key.startsWith(text))
|
||||
result << key;
|
||||
|
|
|
@ -162,7 +162,8 @@ void Wizard::MainWizard::setupGameSettings()
|
|||
paths.append(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();
|
||||
|
||||
file.setFileName(path2);
|
||||
|
@ -222,7 +223,8 @@ void Wizard::MainWizard::setupLauncherSettings()
|
|||
void Wizard::MainWizard::setupInstallations()
|
||||
{
|
||||
// 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))
|
||||
addInstallation(path);
|
||||
|
|
|
@ -217,7 +217,8 @@ bool Wizard::UnshieldWorker::removeDirectory(const QString &dirName)
|
|||
QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot |
|
||||
QDir::System | QDir::Hidden |
|
||||
QDir::AllDirs | QDir::Files, QDir::DirsFirst));
|
||||
foreach(QFileInfo info, list) {
|
||||
for (const QFileInfo& info : list)
|
||||
{
|
||||
if (info.isDir()) {
|
||||
result = removeDirectory(info.absoluteFilePath());
|
||||
} else {
|
||||
|
@ -279,7 +280,8 @@ bool Wizard::UnshieldWorker::copyDirectory(const QString &source, const QString
|
|||
QDir::System | QDir::Hidden |
|
||||
QDir::AllDirs | QDir::Files, QDir::DirsFirst));
|
||||
|
||||
foreach (const QFileInfo &info, list) {
|
||||
for (const QFileInfo &info : list)
|
||||
{
|
||||
QString relativePath(info.absoluteFilePath());
|
||||
relativePath.remove(source);
|
||||
|
||||
|
@ -315,7 +317,8 @@ bool Wizard::UnshieldWorker::installFiles(const QString &fileName, const QString
|
|||
|
||||
QStringList files(findFiles(fileName, path, flags));
|
||||
|
||||
foreach (const QString &file, files) {
|
||||
for (const QString &file : files)
|
||||
{
|
||||
QFileInfo info(file);
|
||||
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));
|
||||
|
||||
foreach (const QString &dir, directories) {
|
||||
for (const QString &dir : directories)
|
||||
{
|
||||
QFileInfo info(dir);
|
||||
emit textChanged(tr("Installing: %1 directory").arg(info.fileName()));
|
||||
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()));
|
||||
|
||||
foreach (const QString &file, list) {
|
||||
for (const QString &file : list)
|
||||
{
|
||||
|
||||
qDebug() << "current archive: " << file;
|
||||
|
||||
|
@ -579,7 +584,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
|
|||
<< QLatin1String("Textures")
|
||||
<< QLatin1String("Video");
|
||||
|
||||
foreach (const QString &dir, directories) {
|
||||
for (const QString &dir : directories)
|
||||
{
|
||||
if (!installDirectories(dir, temp.absolutePath())) {
|
||||
emit error(tr("Could not install directory!"),
|
||||
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
|
||||
foreach (const QString &dir, directories) {
|
||||
for (const QString &dir : directories)
|
||||
{
|
||||
if (!installDirectories(dir, info.absolutePath(), false, true)) {
|
||||
emit error(tr("Could not install directory!"),
|
||||
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(".mrk");
|
||||
|
||||
foreach (const QString &extension, extensions) {
|
||||
for (const QString &extension : extensions)
|
||||
{
|
||||
if (!installFiles(extension, info.absolutePath(), Qt::MatchEndsWith)) {
|
||||
emit error(tr("Could not install translation file!"),
|
||||
tr("Failed to install *%1 files.").arg(extension));
|
||||
|
@ -617,7 +625,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
|
|||
files << QLatin1String("Morrowind.esm")
|
||||
<< QLatin1String("Morrowind.bsa");
|
||||
|
||||
foreach (const QString &file, files) {
|
||||
for (const QString &file : files)
|
||||
{
|
||||
if (!installFile(file, temp.absolutePath())) {
|
||||
emit error(tr("Could not install Morrowind data file!"),
|
||||
tr("Failed to install %1.").arg(file));
|
||||
|
@ -658,7 +667,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
|
|||
files << QLatin1String("Tribunal.esm")
|
||||
<< QLatin1String("Tribunal.bsa");
|
||||
|
||||
foreach (const QString &file, files) {
|
||||
for (const QString &file : files)
|
||||
{
|
||||
if (!installFile(file, temp.absolutePath())) {
|
||||
emit error(tr("Could not find Tribunal data file!"),
|
||||
tr("Failed to find %1.").arg(file));
|
||||
|
@ -683,7 +693,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
|
|||
files << QLatin1String("Bloodmoon.esm")
|
||||
<< QLatin1String("Bloodmoon.bsa");
|
||||
|
||||
foreach (const QString &file, files) {
|
||||
for (const QString &file : files)
|
||||
{
|
||||
if (!installFile(file, temp.absolutePath())) {
|
||||
emit error(tr("Could not find Bloodmoon data 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"));
|
||||
|
||||
foreach (const QString &inx, list) {
|
||||
for (const QString &inx : list)
|
||||
{
|
||||
mIniSettings.parseInx(inx);
|
||||
}
|
||||
}
|
||||
|
@ -705,7 +717,8 @@ bool Wizard::UnshieldWorker::installComponent(Component component, const QString
|
|||
QStringList datafiles(findDirectories(QLatin1String("Data Files"), temp.absolutePath()));
|
||||
datafiles.append(findDirectories(QLatin1String("Data Files"), info.absolutePath()));
|
||||
|
||||
foreach (const QString &dir, datafiles) {
|
||||
for (const QString &dir : datafiles)
|
||||
{
|
||||
QFileInfo info(dir);
|
||||
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 |
|
||||
QDir::AllDirs | QDir::Files, QDir::DirsFirst));
|
||||
foreach(QFileInfo info, list) {
|
||||
for (const QFileInfo& info : list)
|
||||
{
|
||||
if (info.isSymLink())
|
||||
continue;
|
||||
|
||||
|
|
|
@ -8,13 +8,10 @@ branches:
|
|||
|
||||
environment:
|
||||
matrix:
|
||||
- msvc: 2015
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
- msvc: 2017
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
matrix:
|
||||
allow_failures:
|
||||
- msvc: 2015
|
||||
- msvc: 2019
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
|
||||
platform:
|
||||
# - Win32
|
||||
|
|
|
@ -29,7 +29,7 @@ endif (GIT_CHECKOUT)
|
|||
# source files
|
||||
|
||||
add_component_dir (settings
|
||||
settings
|
||||
settings parser
|
||||
)
|
||||
|
||||
add_component_dir (bsa
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <algorithm>
|
||||
#include <stack>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
|
@ -324,6 +325,21 @@ namespace Compiler
|
|||
mExplicit = name2;
|
||||
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
|
||||
{
|
||||
|
|
|
@ -180,7 +180,7 @@ namespace Compiler
|
|||
extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
|
||||
extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic);
|
||||
extensions.registerInstruction ("choice", "j/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice);
|
||||
extensions.registerInstruction("forcegreeting","X",opcodeForceGreeting,
|
||||
extensions.registerInstruction("forcegreeting","z",opcodeForceGreeting,
|
||||
opcodeForceGreetingExplicit);
|
||||
extensions.registerInstruction("goodbye", "", opcodeGoodbye);
|
||||
extensions.registerInstruction("setreputation", "l", opcodeSetReputation,
|
||||
|
|
|
@ -44,7 +44,8 @@ void Config::GameSettings::validatePaths()
|
|||
QStringList paths = mSettings.values(QString("data"));
|
||||
Files::PathContainer dataDirs;
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
for (const QString &path : paths)
|
||||
{
|
||||
QByteArray bytes = path.toUtf8();
|
||||
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)
|
||||
{
|
||||
remove(sContentKey);
|
||||
foreach(const QString& fileName, fileNames)
|
||||
for (const QString& fileName : fileNames)
|
||||
{
|
||||
setMultiValue(sContentKey, fileName);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ QStringList Config::LauncherSettings::subKeys(const QString &key)
|
|||
|
||||
QStringList result;
|
||||
|
||||
foreach (const QString ¤tKey, keys) {
|
||||
for (const QString ¤tKey : keys)
|
||||
{
|
||||
|
||||
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
|
||||
foreach(const QString &listName, getContentLists())
|
||||
for (const QString &listName : getContentLists())
|
||||
{
|
||||
if (isEqual(files, getContentListFiles(listName)))
|
||||
{
|
||||
|
@ -140,7 +141,7 @@ void Config::LauncherSettings::setContentList(const QString& contentListName, co
|
|||
{
|
||||
removeContentList(contentListName);
|
||||
QString key = makeContentListKey(contentListName);
|
||||
foreach(const QString& fileName, fileNames)
|
||||
for (const QString& fileName : fileNames)
|
||||
{
|
||||
setMultiValue(key, fileName);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(co
|
|||
if (name.contains ('/'))
|
||||
fp = EsmFile::FileProperty_FilePath;
|
||||
|
||||
foreach (const EsmFile *file, mFiles)
|
||||
for (const EsmFile *file : mFiles)
|
||||
{
|
||||
if (name.compare(file->fileProperty (fp).toString(), Qt::CaseInsensitive) == 0)
|
||||
return file;
|
||||
|
@ -108,7 +108,7 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index
|
|||
// addon can be checked if its gamefile is
|
||||
// ... special case, addon with no dependency can be used with any gamefile.
|
||||
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())
|
||||
{
|
||||
|
@ -283,7 +283,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const
|
|||
else
|
||||
return success;
|
||||
|
||||
foreach (EsmFile *file2, mFiles)
|
||||
for (EsmFile *file2 : mFiles)
|
||||
{
|
||||
if (file2->gameFiles().contains(fileName, Qt::CaseInsensitive))
|
||||
{
|
||||
|
@ -346,7 +346,7 @@ QMimeData *ContentSelectorModel::ContentModel::mimeData(const QModelIndexList &i
|
|||
{
|
||||
QByteArray encodedData;
|
||||
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
for (const QModelIndex &index : indexes)
|
||||
{
|
||||
if (!index.isValid())
|
||||
continue;
|
||||
|
@ -424,7 +424,7 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path)
|
|||
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
|
||||
dir.setNameFilters(filters);
|
||||
|
||||
foreach (const QString &path2, dir.entryList())
|
||||
for (const QString &path2 : dir.entryList())
|
||||
{
|
||||
QFileInfo info(dir.absoluteFilePath(path2));
|
||||
|
||||
|
@ -486,7 +486,7 @@ void ContentSelectorModel::ContentModel::clearFiles()
|
|||
QStringList ContentSelectorModel::ContentModel::gameFiles() const
|
||||
{
|
||||
QStringList gameFiles;
|
||||
foreach(const ContentSelectorModel::EsmFile *file, mFiles)
|
||||
for (const ContentSelectorModel::EsmFile *file : mFiles)
|
||||
{
|
||||
if (file->isGameFile())
|
||||
{
|
||||
|
@ -557,7 +557,7 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList &fileL
|
|||
{
|
||||
mPluginsWithLoadOrderError.clear();
|
||||
int previousPosition = -1;
|
||||
foreach (const QString &filepath, fileList)
|
||||
for (const QString &filepath : fileList)
|
||||
{
|
||||
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<LoadOrderError> errors = QList<LoadOrderError>();
|
||||
foreach(const QString &dependentfileName, file->gameFiles())
|
||||
for (const QString &dependentfileName : file->gameFiles())
|
||||
{
|
||||
const EsmFile* dependentFile = item(dependentfileName);
|
||||
|
||||
|
@ -627,7 +627,7 @@ QString ContentSelectorModel::ContentModel::toolTip(const EsmFile *file) const
|
|||
{
|
||||
QString text("<b>");
|
||||
int index = indexFromItem(item(file->filePath())).row();
|
||||
foreach(const LoadOrderError& error, checkForLoadOrderErrors(file, index))
|
||||
for (const LoadOrderError& error : checkForLoadOrderErrors(file, index))
|
||||
{
|
||||
text += "<p>";
|
||||
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 (state == Qt::Checked)
|
||||
{
|
||||
foreach (QString upstreamName, file->gameFiles())
|
||||
for (const QString& upstreamName : file->gameFiles())
|
||||
{
|
||||
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.
|
||||
if (state == Qt::Unchecked)
|
||||
{
|
||||
foreach (const EsmFile *downstreamFile, mFiles)
|
||||
for (const EsmFile *downstreamFile : mFiles)
|
||||
{
|
||||
QFileInfo fileInfo(filepath);
|
||||
QString filename = fileInfo.fileName();
|
||||
|
@ -714,7 +714,7 @@ ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checke
|
|||
// TODO:
|
||||
// First search for game files and next addons,
|
||||
// so we get more or less correct game files vs addons order.
|
||||
foreach (EsmFile *file, mFiles)
|
||||
for (EsmFile *file : mFiles)
|
||||
if (isChecked(file->filePath()))
|
||||
list << file;
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ void ContentSelectorView::ContentSelector::setProfileContent(const QStringList &
|
|||
{
|
||||
clearCheckStates();
|
||||
|
||||
foreach (const QString &filepath, fileList)
|
||||
for (const QString &filepath : fileList)
|
||||
{
|
||||
const ContentSelectorModel::EsmFile *file = mContentModel->item(filepath);
|
||||
if (file && file->isGameFile())
|
||||
|
@ -139,7 +139,7 @@ void ContentSelectorView::ContentSelector::addFiles(const QString &path)
|
|||
mContentModel->addFiles(path);
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
@ -225,7 +225,7 @@ void ContentSelectorView::ContentSelector::slotShowContextMenu(const QPoint& pos
|
|||
void ContentSelectorView::ContentSelector::setCheckStateForMultiSelectedItems(bool checked)
|
||||
{
|
||||
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);
|
||||
if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() != checkState)
|
||||
|
@ -249,7 +249,7 @@ void ContentSelectorView::ContentSelector::slotCopySelectedItemsPaths()
|
|||
{
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
QString filepaths;
|
||||
foreach (const QModelIndex& index, ui.addonView->selectionModel()->selectedIndexes())
|
||||
for (const QModelIndex& index : ui.addonView->selectionModel()->selectedIndexes())
|
||||
{
|
||||
int row = mAddonProxyModel->mapToSource(index).row();
|
||||
const ContentSelectorModel::EsmFile *file = mContentModel->item(row);
|
||||
|
|
|
@ -9,8 +9,10 @@ namespace ESM
|
|||
|
||||
void InventoryList::add(ESMReader &esm)
|
||||
{
|
||||
esm.getSubHeader();
|
||||
ContItem ci;
|
||||
esm.getHT(ci, 36);
|
||||
esm.getT(ci.mCount);
|
||||
ci.mItem.assign(esm.getString(32));
|
||||
mList.push_back(ci);
|
||||
}
|
||||
|
||||
|
@ -18,7 +20,10 @@ namespace ESM
|
|||
{
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class ESMWriter;
|
|||
struct ContItem
|
||||
{
|
||||
int mCount;
|
||||
NAME32 mItem;
|
||||
std::string mItem;
|
||||
};
|
||||
|
||||
/// InventoryList, NPCO subrecord
|
||||
|
|
|
@ -62,8 +62,10 @@ namespace ESM
|
|||
break;
|
||||
case ESM::FourCC<'S','N','A','M'>::value:
|
||||
{
|
||||
esm.getSubHeader();
|
||||
SoundRef sr;
|
||||
esm.getHT(sr, 33);
|
||||
sr.mSound.assign(esm.getString(32));
|
||||
esm.getT(sr.mChance);
|
||||
mSoundList.push_back(sr);
|
||||
break;
|
||||
}
|
||||
|
@ -103,7 +105,10 @@ namespace ESM
|
|||
esm.writeHNT("CNAM", mMapColor);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,14 +33,14 @@ struct Region
|
|||
// the engine uses mA as "snow" and mB as "blizard"
|
||||
mA, mB;
|
||||
}; // 10 bytes
|
||||
#pragma pack(pop)
|
||||
|
||||
// Reference to a sound that is played randomly in this region
|
||||
struct SoundRef
|
||||
{
|
||||
NAME32 mSound;
|
||||
std::string mSound;
|
||||
unsigned char mChance;
|
||||
}; // 33 bytes
|
||||
#pragma pack(pop)
|
||||
};
|
||||
|
||||
WEATstruct mData;
|
||||
int mMapColor; // RGBA
|
||||
|
|
|
@ -77,10 +77,10 @@ namespace ESM
|
|||
{
|
||||
case ESM::FourCC<'S','C','H','D'>::value:
|
||||
{
|
||||
SCHD data;
|
||||
esm.getHT(data, 52);
|
||||
mData = data.mData;
|
||||
mId = data.mName.toString();
|
||||
esm.getSubHeader();
|
||||
mId = esm.getString(32);
|
||||
esm.getT(mData);
|
||||
|
||||
hasHeader = true;
|
||||
break;
|
||||
}
|
||||
|
@ -131,13 +131,10 @@ namespace ESM
|
|||
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
|
||||
varNameString.append(*it);
|
||||
|
||||
SCHD data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
data.mData = mData;
|
||||
data.mName.assign(mId);
|
||||
|
||||
esm.writeHNT("SCHD", data, 52);
|
||||
esm.startSubRecord("SCHD");
|
||||
esm.writeFixedSizeString(mId, 32);
|
||||
esm.writeT(mData, 20);
|
||||
esm.endRecord("SCHD");
|
||||
|
||||
if (isDeleted)
|
||||
{
|
||||
|
|
|
@ -31,9 +31,9 @@ public:
|
|||
};
|
||||
struct SCHD
|
||||
{
|
||||
NAME32 mName;
|
||||
std::string mName;
|
||||
Script::SCHDstruct mData;
|
||||
}; // 52 bytes
|
||||
};
|
||||
|
||||
std::string mId;
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ namespace ESMTerrain
|
|||
(*positions)[static_cast<unsigned int>(vertX*numVerts + vertY)]
|
||||
= osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits,
|
||||
(vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits,
|
||||
height);
|
||||
height + getAlteredHeight(col, row));
|
||||
|
||||
if (normalData)
|
||||
{
|
||||
|
@ -291,6 +291,8 @@ namespace ESMTerrain
|
|||
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...
|
||||
if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mLayerInfoMutex);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H
|
||||
#define COMPONENTS_ESM_TERRAIN_STORAGE_H
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <OpenThreads/Mutex>
|
||||
|
||||
#include <components/terrain/storage.hpp>
|
||||
|
@ -107,6 +109,13 @@ namespace ESMTerrain
|
|||
|
||||
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:
|
||||
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 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);
|
||||
|
||||
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
|
||||
// in order to retrieve the correct texture name.
|
||||
// pair <texture id, plugin id>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue