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

Add OpenMW commits up to 29 Oct 2019

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

10
.gitignore vendored
View file

@ -83,3 +83,13 @@ moc_*.cxx
*.[ao]
*.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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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"

View file

@ -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<>

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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";

View file

@ -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

View file

@ -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

View file

@ -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());
}

View file

@ -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.");

View file

@ -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)

View file

@ -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

View file

@ -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);
}
}

View file

@ -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];

View file

@ -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");
}

View file

@ -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!");

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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();

View file

@ -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
{

View file

@ -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;

View file

@ -249,13 +249,11 @@ int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global ver
int localX = x - cellX * (ESM::Land::LAND_SIZE - 1);
int 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;
}

File diff suppressed because it is too large Load diff

View file

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

View file

@ -3,13 +3,15 @@
#include "../../model/world/land.hpp"
#include "../../model/world/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)];
}
}

View file

@ -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;
};
}

View file

@ -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
{

View file

@ -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;

View file

@ -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;

View file

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

View file

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

View file

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

View file

@ -343,7 +343,7 @@ std::vector< CSMWorld::UniversalId > CSVWorld::RegionMap::getDraggedRecords() co
{
QModelIndexList selected(getSelectedCells(true, false));
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(

View file

@ -25,12 +25,12 @@ namespace MWGui
{
}
MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count)
MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)
{
if (hasProfit(mActor))
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)

View file

@ -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);

View file

@ -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)

View file

@ -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();

View file

@ -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();

View file

@ -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)

View file

@ -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.

View file

@ -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)

View file

@ -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);

View file

@ -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);
}

View file

@ -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());

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -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);
}
}

View file

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

View file

@ -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;
}
}

View file

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

View file

@ -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;

View file

@ -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());

View file

@ -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)

View file

@ -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);

View file

@ -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) {}

View file

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

View file

@ -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();
}

View file

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

View file

@ -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);

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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_);

View file

@ -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.

View file

@ -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);
}

View file

@ -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);

View file

@ -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})

View file

@ -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;
}
}

View file

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

View file

@ -139,7 +139,8 @@ bool Wizard::ComponentSelectionPage::validatePage()
mWizard->mInstallations[path].hasBloodmoon = false;
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);
}

View file

@ -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())
{

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

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

View file

@ -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
{

View file

@ -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,

View file

@ -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);
}

View file

@ -29,7 +29,8 @@ QStringList Config::LauncherSettings::subKeys(const QString &key)
QStringList result;
foreach (const QString &currentKey, keys) {
for (const QString &currentKey : 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);
}

View file

@ -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;

View 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);

View file

@ -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");
}
}

View file

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

View file

@ -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");
}
}

View file

@ -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

View file

@ -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)
{

View file

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

View file

@ -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);

View file

@ -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