Merge branch 'master' of https://gitlab.com/madsbuvi/openmw.git into openxr_vr

# Conflicts:
#	apps/openmw/mwbase/world.hpp
#	apps/openmw/mwworld/worldimp.cpp
#	apps/openmw/mwworld/worldimp.hpp
pull/615/head
Mads Buvik Sandvei 5 years ago
commit 14bb0f0208

@ -9,19 +9,15 @@ env:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
- secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE="
cache:
ccache: true
directories:
- ${HOME}/.ccache
cache: ccache
addons:
apt:
sources:
- sourceline: 'ppa:openmw/openmw'
- ubuntu-toolchain-r-test
- llvm-toolchain-xenial-7
packages: [
# Dev
cmake, clang-7, clang-tools-7, gcc-8, g++-8, ccache,
cmake, clang-tools, gcc-9, g++-9, ccache,
# Boost
libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev,
# FFmpeg
@ -45,50 +41,51 @@ matrix:
os: osx
osx_image: xcode10.2
if: branch != coverity_scan
- name: OpenMW (all) on Ubuntu Xenial GCC-5
- name: OpenMW (all) on Ubuntu Bionic GCC-7
os: linux
dist: xenial
dist: bionic
sudo: required
if: branch != coverity_scan
- name: OpenMW (all) on Ubuntu Xenial GCC-8
- name: OpenMW (all) on Ubuntu Bionic GCC-9
os: linux
dist: xenial
dist: bionic
sudo: required
env:
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
- MATRIX_EVAL="CC=gcc-9 && CXX=g++-9"
if: branch != coverity_scan
- name: OpenMW (openmw) on Ubuntu Xenial Clang-7 with Static Analysis
- name: OpenMW (openmw) on Ubuntu Bionic Clang-6 with Static Analysis
os: linux
dist: xenial
dist: bionic
sudo: required
env:
- MATRIX_EVAL="CC=clang-7 && CXX=clang++-7"
- ANALYZE="scan-build-7 --force-analyze-debug-code --use-cc clang-7 --use-c++ clang++-7"
- MATRIX_EVAL="CC=clang && CXX=clang++"
- ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++"
- BUILD_OPENMW_CS="OFF"
if: branch != coverity_scan
compiler: clang
- name: OpenMW (openmw-cs) on Ubuntu Xenial Clang-7 with Static Analysis
- name: OpenMW (openmw-cs) on Ubuntu Bionic Clang-6 with Static Analysis
os: linux
dist: xenial
dist: bionic
sudo: required
env:
- MATRIX_EVAL="CC=clang-7 && CXX=clang++-7"
- ANALYZE="scan-build-7 --force-analyze-debug-code --use-cc clang-7 --use-c++ clang++-7"
- MATRIX_EVAL="CC=clang && CXX=clang++"
- ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++"
- BUILD_OPENMW="OFF"
if: branch != coverity_scan
compiler: clang
- name: OpenMW Components Coverity Scan
os: linux
dist: xenial
dist: bionic
sudo: required
if: branch = coverity_scan
# allow_failures:
# - name: OpenMW (openmw) on Ubuntu Xenial Clang-7 with Static Analysis
# - name: OpenMW (openmw) on Ubuntu Bionic Clang-6 with Static Analysis
before_install:
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_install.${TRAVIS_OS_NAME}.sh; fi
before_script:
- ccache -z
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_script.${TRAVIS_OS_NAME}.sh; fi
script:
- cd ./build
@ -98,6 +95,7 @@ script:
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
- cd "${TRAVIS_BUILD_DIR}"
- ccache -s
deploy:
provider: script
script: ./CI/deploy.osx.sh

@ -74,6 +74,7 @@ Programmers
Fil Krynicki (filkry)
Finbar Crago(finbar-crago)
Florian Weber (Florianjw)
Frédéric Chardon (fr3dz10)
Gaëtan Dezeiraud (Brouilles)
Gašper Sedej
Gijsbert ter Horst (Ghostbird)
@ -87,6 +88,7 @@ Programmers
Jacob Essex (Yacoby)
Jake Westrip (16bitint)
James Carty (MrTopCat)
James Moore (moore.work)
James Stephens (james-h-stephens)
Jan-Peter Nilsson (peppe)
Jan Borsodi (am0s)

@ -23,6 +23,7 @@
Bug #3977: Non-ASCII characters in object ID's are not supported
Bug #4009: Launcher does not show data files on the first run after installing
Bug #4077: Enchanted items are not recharged if they are not in the player's inventory
Bug #4141: PCSkipEquip isn't set to 1 when reading books/scrolls
Bug #4202: Open .omwaddon files without needing toopen openmw-cs first
Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect
Bug #4262: Rain settings are hardcoded
@ -36,10 +37,13 @@
Bug #4411: Reloading a saved game while falling prevents damage in some cases
Bug #4449: Value returned by GetWindSpeed is incorrect
Bug #4456: AiActivate should not be cancelled after target activation
Bug #4493: If the setup doesn't find what it is expecting, it fails silently and displays the requester again instead of letting the user know what wasn't found.
Bug #4523: "player->ModCurrentFatigue -0.001" in global script does not cause the running player to fall
Bug #4540: Rain delay when exiting water
Bug #4594: Actors without AI packages don't use Hello dialogue
Bug #4598: Script parser does not support non-ASCII characters
Bug #4600: Crash when no sound output is available or --no-sound is used.
Bug #4601: Filtering referenceables by gender is broken
Bug #4639: Black screen after completing first mages guild mission + training
Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog
Bug #4680: Heap corruption on faulty esp
@ -192,9 +196,19 @@
Bug #5239: OpenMW-CS does not support non-ASCII characters in path names
Bug #5241: On-self absorb spells cannot be detected
Bug #5242: ExplodeSpell behavior differs from Cast behavior
Bug #5246: Water ripples persist after cell change
Bug #5249: Wandering NPCs start walking too soon after they hello
Bug #5250: Creatures display shield ground mesh instead of shield body part
Bug #5255: "GetTarget, player" doesn't return 1 during NPC hello
Bug #5261: Creatures can sometimes become stuck playing idles and never wander again
Bug #5264: "Damage Fatigue" Magic Effect Can Bring Fatigue below 0
Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted
Bug #5278: Console command Show doesn't fall back to global variable after local var not found
Bug #5300: NPCs don't switch from torch to shield when starting combat
Bug #5308: World map copying makes save loading much slower
Bug #5313: Node properties of identical type are not applied in the correct order
Bug #5326: Formatting issues in the settings.cfg
Bug #5328: Skills aren't properly reset for dead actors
Feature #1774: Handle AvoidNode
Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls
@ -215,9 +229,11 @@
Feature #4544: Actors movement deceleration
Feature #4673: Weapon sheathing
Feature #4675: Support for NiRollController
Feature #4708: Radial fog support
Feature #4730: Native animated containers support
Feature #4784: Launcher: Duplicate Content Lists
Feature #4812: Support NiSwitchNode
Feature #4831: Item search in the player's inventory
Feature #4836: Daytime node switch
Feature #4840: Editor: Transient terrain change support
Feature #4859: Make water reflections more configurable
@ -240,6 +256,7 @@
Feature #5091: Human-readable light source duration
Feature #5094: Unix like console hotkeys
Feature #5098: Allow user controller bindings
Feature #5114: Refresh launcher mod list
Feature #5121: Handle NiTriStrips and NiTriStripsData
Feature #5122: Use magic glow for enchanted arrows
Feature #5131: Custom skeleton bones
@ -247,9 +264,13 @@
Feature #5146: Safe Dispose corpse
Feature #5147: Show spell magicka cost in spell buying window
Feature #5170: Editor: Land shape editing, land selection
Feature #5172: Editor: Delete instances/references with keypress in scene window
Feature #5193: Weapon sheathing
Feature #5219: Impelement TestCells console command
Feature #5224: Handle NiKeyframeController for NiTriShape
Feature #5274: Editor: Keyboard shortcut to drop objects to ground/obstacle in scene view
Feature #5304: Morrowind-style bump-mapping
Feature #5314: Ingredient filter in the alchemy window
Task #4686: Upgrade media decoder to a more current FFmpeg API
Task #4695: Optimize Distant Terrain memory consumption
Task #4789: Optimize cell transitions

@ -42,6 +42,8 @@ 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)
- Deleting instances with a keypress (#5172)
- Dropping objects with keyboard shortcuts (#5274)
Bug Fixes:
- The Mouse Wheel can now be used for key bindings (#2679)

@ -1,4 +1,4 @@
#!/bin/bash -ex
sudo ln -sf /usr/bin/clang-7 /usr/local/bin/clang
sudo ln -sf /usr/bin/clang++-7 /usr/local/bin/clang++
#sudo ln -sf /usr/bin/clang-6 /usr/local/bin/clang
#sudo ln -sf /usr/bin/clang++-6 /usr/local/bin/clang++

@ -7,9 +7,7 @@ GOOGLETEST_DIR="$(pwd)/googletest/build"
mkdir build
cd build
export CODE_COVERAGE=1
if [[ "${CC}" =~ "clang" ]]; then export CODE_COVERAGE=0; fi
if [[ -z "${BUILD_OPENMW}" ]]; then export BUILD_OPENMW=ON; fi
if [[ -z "${BUILD_OPENMW_CS}" ]]; then export BUILD_OPENMW_CS=ON; fi
@ -28,7 +26,6 @@ ${ANALYZE} cmake \
-DBUILD_WIZARD=${BUILD_OPENMW_CS} \
-DBUILD_NIFTEST=${BUILD_OPENMW_CS} \
-DBUILD_MYGUI_PLUGIN=${BUILD_OPENMW_CS} \
-DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} \
-DBUILD_UNITTESTS=1 \
-DUSE_SYSTEM_TINYXML=1 \
-DDESIRED_QT_VERSION=5 \

@ -421,16 +421,16 @@ if [ -z $SKIP_DOWNLOAD ]; then
fi
download "Qt 5.7.0" \
"https://download.qt.io/archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
"https://download.qt.io/new_archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
"qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
"qt-5-install.qs"
fi
# SDL2
download "SDL 2.0.7" \
"https://www.libsdl.org/release/SDL2-devel-2.0.7-VC.zip" \
"SDL2-2.0.7.zip"
download "SDL 2.0.12" \
"https://www.libsdl.org/release/SDL2-devel-2.0.12-VC.zip" \
"SDL2-2.0.12.zip"
# Google test and mock
if [ ! -z $TEST_FRAMEWORK ]; then
@ -697,16 +697,16 @@ fi
cd $DEPS
echo
# SDL2
printf "SDL 2.0.7... "
printf "SDL 2.0.12... "
{
if [ -d SDL2-2.0.7 ]; then
if [ -d SDL2-2.0.12 ]; then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
rm -rf SDL2-2.0.7
eval 7z x -y SDL2-2.0.7.zip $STRIP
rm -rf SDL2-2.0.12
eval 7z x -y SDL2-2.0.12.zip $STRIP
fi
export SDL2DIR="$(real_pwd)/SDL2-2.0.7"
add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll"
export SDL2DIR="$(real_pwd)/SDL2-2.0.12"
add_runtime_dlls "$(pwd)/SDL2-2.0.12/lib/x${ARCHSUFFIX}/SDL2.dll"
echo Done.
}
cd $DEPS

@ -534,10 +534,18 @@ void Record<ESM::Cell>::print()
if (mData.mData.mFlags & ESM::Cell::Interior &&
!(mData.mData.mFlags & ESM::Cell::QuasiEx))
{
std::cout << " Ambient Light Color: " << mData.mAmbi.mAmbient << std::endl;
std::cout << " Sunlight Color: " << mData.mAmbi.mSunlight << std::endl;
std::cout << " Fog Color: " << mData.mAmbi.mFog << std::endl;
std::cout << " Fog Density: " << mData.mAmbi.mFogDensity << std::endl;
if (mData.hasAmbient())
{
// TODO: see if we can change the integer representation to something more sensible
std::cout << " Ambient Light Color: " << mData.mAmbi.mAmbient << std::endl;
std::cout << " Sunlight Color: " << mData.mAmbi.mSunlight << std::endl;
std::cout << " Fog Color: " << mData.mAmbi.mFog << std::endl;
std::cout << " Fog Density: " << mData.mAmbi.mFogDensity << std::endl;
}
else
{
std::cout << " No Ambient Information" << std::endl;
}
std::cout << " Water Level: " << mData.mWater << std::endl;
}
else

@ -89,6 +89,7 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
}
loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
// Input Settings
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
@ -152,6 +153,7 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
// Input Settings
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");

@ -62,10 +62,13 @@ void Launcher::DataFilesPage::buildView()
{
ui.verticalLayout->insertWidget (0, mSelector->uiWidget());
QToolButton * refreshButton = mSelector->refreshButton();
//tool buttons
ui.newProfileButton->setToolTip ("Create a new Content List");
ui.cloneProfileButton->setToolTip ("Clone the current Content List");
ui.deleteProfileButton->setToolTip ("Delete an existing Content List");
refreshButton->setToolTip("Refresh Data Files");
//combo box
ui.profilesComboBox->addItem(mDefaultContentListName);
@ -76,6 +79,7 @@ void Launcher::DataFilesPage::buildView()
ui.newProfileButton->setDefaultAction (ui.newProfileAction);
ui.cloneProfileButton->setDefaultAction (ui.cloneProfileAction);
ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction);
refreshButton->setDefaultAction(ui.refreshDataFilesAction);
//establish connections
connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)),
@ -86,6 +90,8 @@ void Launcher::DataFilesPage::buildView()
connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString, QString)),
this, SLOT (slotProfileChangedByUser(QString, QString)));
connect(ui.refreshDataFilesAction, SIGNAL(triggered()),this, SLOT(slotRefreshButtonClicked()));
}
bool Launcher::DataFilesPage::loadSettings()
@ -114,6 +120,8 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
if (!mDataLocal.isEmpty())
paths.insert(0, mDataLocal);
mSelector->clearFiles();
for (const QString &path : paths)
mSelector->addFiles(path);
@ -167,7 +175,16 @@ QStringList Launcher::DataFilesPage::selectedFilePaths()
QStringList filePaths;
for (const ContentSelectorModel::EsmFile *item : items)
{
filePaths.append(item->filePath());
QFile file(item->filePath());
if(file.exists())
{
filePaths.append(item->filePath());
}
else
{
slotRefreshButtonClicked();
}
}
return filePaths;
}
@ -221,6 +238,18 @@ void Launcher::DataFilesPage::slotProfileDeleted (const QString &item)
removeProfile (item);
}
void Launcher::DataFilesPage:: refreshDataFilesView ()
{
QString currentProfile = ui.profilesComboBox->currentText();
saveSettings(currentProfile);
populateFileViews(currentProfile);
}
void Launcher::DataFilesPage::slotRefreshButtonClicked ()
{
refreshDataFilesView();
}
void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString &current)
{
setProfile(previous, current, true);

@ -61,6 +61,7 @@ namespace Launcher
void slotProfileRenamed(const QString &previous, const QString &current);
void slotProfileDeleted(const QString &item);
void slotAddonDataChanged ();
void slotRefreshButtonClicked ();
void updateNewProfileOkButton(const QString &text);
void updateCloneProfileOkButton(const QString &text);
@ -100,6 +101,7 @@ namespace Launcher
void checkForDefaultProfile();
void populateFileViews(const QString& contentModelName);
void reloadCells(QStringList selectedFiles);
void refreshDataFilesView ();
class PathIterator
{

@ -97,10 +97,6 @@ opencs_units_noqt (view/render
cellarrow cellmarker cellborder pathgrid
)
opencs_hdrs_noqt (view/render
mask
)
opencs_units (view/tools
reportsubview reporttable searchsubview searchbox merge

@ -355,6 +355,11 @@ void CSMPrefs::State::declare()
declareShortcut ("scene-select-secondary", "Secondary Select",
QKeySequence(Qt::ControlModifier | (int)Qt::MiddleButton));
declareModifier ("scene-speed-modifier", "Speed Modifier", Qt::Key_Shift);
declareShortcut ("scene-delete", "Delete Instance", QKeySequence(Qt::Key_Delete));
declareShortcut ("scene-instance-drop-terrain", "Drop to terrain level", QKeySequence(Qt::Key_G));
declareShortcut ("scene-instance-drop-collision", "Drop to collision", QKeySequence(Qt::Key_H));
declareShortcut ("scene-instance-drop-terrain-separately", "Drop to terrain level separately", QKeySequence());
declareShortcut ("scene-instance-drop-collision-separately", "Drop to collision separately", QKeySequence());
declareShortcut ("scene-load-cam-cell", "Load Camera Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_5));
declareShortcut ("scene-load-cam-eastcell", "Load East Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_6));
declareShortcut ("scene-load-cam-northcell", "Load North Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_8));

@ -76,53 +76,6 @@ namespace CSMWorld
return false;
}
/* LandMapLodColumn */
LandMapLodColumn::LandMapLodColumn()
: Column<Land>(Columns::ColumnId_LandMapLodIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandMapLodColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_GLOBAL_MAP_LOD_SIZE;
const Land& land = record.get();
DataType values(Size, 0);
if (land.mDataTypes & Land::DATA_WNAM)
{
for (int i = 0; i < Size; ++i)
values[i] = land.mWnam[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandMapLodColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_GLOBAL_MAP_LOD_SIZE)
throw std::runtime_error("invalid land map LOD data");
Land copy = record.get();
copy.add(Land::DATA_WNAM);
for (int i = 0; i < values.size(); ++i)
{
copy.mWnam[i] = values[i];
}
record.setModified(copy);
}
bool LandMapLodColumn::isEditable() const
{
return true;
}
/* LandNormalsColumn */
LandNormalsColumn::LandNormalsColumn()
: Column<Land>(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0)

@ -1770,7 +1770,7 @@ namespace CSMWorld
struct GenderNpcColumn : public Column<ESXRecordT>
{
GenderNpcColumn()
: Column<ESXRecordT>(Columns::ColumnId_GenderNpc, ColumnBase::Display_GenderNpc)
: Column<ESXRecordT>(Columns::ColumnId_Gender, ColumnBase::Display_GenderNpc)
{}
virtual QVariant get(const Record<ESXRecordT>& record) const
@ -2461,17 +2461,6 @@ namespace CSMWorld
bool isEditable() const override;
};
struct LandMapLodColumn : public Column<Land>
{
using DataType = QVector<signed char>;
LandMapLodColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandNormalsColumn : public Column<Land>
{
using DataType = QVector<signed char>;
@ -2529,8 +2518,7 @@ namespace CSMWorld
}
// This is required to access the type as a QVariant.
Q_DECLARE_METATYPE(CSMWorld::LandMapLodColumn::DataType)
//Q_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType) // Same as LandMapLodColumn::DataType
Q_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType)
Q_DECLARE_METATYPE(CSMWorld::LandHeightsColumn::DataType)
Q_DECLARE_METATYPE(CSMWorld::LandColoursColumn::DataType)
Q_DECLARE_METATYPE(CSMWorld::LandTexturesColumn::DataType)

@ -288,7 +288,6 @@ namespace CSMWorld
{ ColumnId_UChar, "Value [0..255]" },
{ ColumnId_NpcMisc, "NPC Misc" },
{ ColumnId_Level, "Level" },
{ ColumnId_GenderNpc, "Gender"},
{ ColumnId_Mana, "Mana" },
{ ColumnId_Fatigue, "Fatigue" },
{ ColumnId_NpcDisposition, "NPC Disposition" },

@ -273,7 +273,7 @@ namespace CSMWorld
ColumnId_UChar = 250,
ColumnId_NpcMisc = 251,
ColumnId_Level = 252,
ColumnId_GenderNpc = 254,
// unused
ColumnId_Mana = 255,
ColumnId_Fatigue = 256,
ColumnId_NpcDisposition = 257,

@ -79,8 +79,10 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
Shader::ShaderManager::DefineMap defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
Shader::ShaderManager::DefineMap shadowDefines = SceneUtil::ShadowManager::getShadowsDisabledDefines();
defines["forcePPL"] = "0";
defines["clamp"] = "1";
defines["forcePPL"] = "0"; // Don't force per-pixel lighting
defines["clamp"] = "1"; // Clamp lighting
defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind
defines["radialFog"] = "0";
for (const auto& define : shadowDefines)
defines[define.first] = define.second;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
@ -443,7 +445,6 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
mLand.addColumn (new RecordStateColumn<Land>);
mLand.addColumn (new FixedRecordTypeColumn<Land>(UniversalId::Type_Land));
mLand.addColumn (new LandPluginIndexColumn);
mLand.addColumn (new LandMapLodColumn);
mLand.addColumn (new LandNormalsColumn);
mLand.addColumn (new LandHeightsColumn);
mLand.addColumn (new LandColoursColumn);

@ -867,6 +867,8 @@ namespace CSMWorld
switch (subColIndex)
{
case 0: return isInterior;
// While the ambient information is not necessarily valid if the subrecord wasn't loaded,
// the user should still be allowed to edit it
case 1: return (isInterior && !behaveLikeExterior) ?
cell.mAmbi.mAmbient : QVariant(QVariant::UserType);
case 2: return (isInterior && !behaveLikeExterior) ?
@ -912,7 +914,10 @@ namespace CSMWorld
case 1:
{
if (isInterior && !behaveLikeExterior)
{
cell.mAmbi.mAmbient = static_cast<int32_t>(value.toInt());
cell.setHasAmbient(true);
}
else
return; // return without saving
break;
@ -920,7 +925,10 @@ namespace CSMWorld
case 2:
{
if (isInterior && !behaveLikeExterior)
{
cell.mAmbi.mSunlight = static_cast<int32_t>(value.toInt());
cell.setHasAmbient(true);
}
else
return; // return without saving
break;
@ -928,7 +936,10 @@ namespace CSMWorld
case 3:
{
if (isInterior && !behaveLikeExterior)
{
cell.mAmbi.mFog = static_cast<int32_t>(value.toInt());
cell.setHasAmbient(true);
}
else
return; // return without saving
break;
@ -936,7 +947,10 @@ namespace CSMWorld
case 4:
{
if (isInterior && !behaveLikeExterior)
{
cell.mAmbi.mFogDensity = value.toFloat();
cell.setHasAmbient(true);
}
else
return; // return without saving
break;

@ -486,7 +486,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.push_back (RefIdColumn (Columns::ColumnId_Head, ColumnBase::Display_BodyPart));
npcColumns.mHead = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_GenderNpc, ColumnBase::Display_GenderNpc));
mColumns.push_back (RefIdColumn (Columns::ColumnId_Gender, ColumnBase::Display_GenderNpc));
npcColumns.mGender = &mColumns.back();
npcColumns.mFlags.insert (std::make_pair (essential, ESM::NPC::Essential));

@ -9,6 +9,7 @@
#include <components/esm/loadcell.hpp>
#include <components/esm/loadland.hpp>
#include <components/sceneutil/pathgridutil.hpp>
#include <components/sceneutil/vismask.hpp>
#include <components/terrain/terraingrid.hpp>
#include "../../model/world/idtable.hpp"
@ -21,7 +22,6 @@
#include "cellborder.hpp"
#include "cellarrow.hpp"
#include "cellmarker.hpp"
#include "mask.hpp"
#include "pathgrid.hpp"
#include "terrainstorage.hpp"
#include "object.hpp"
@ -92,7 +92,7 @@ bool CSVRender::Cell::addObjects (int start, int end)
std::unique_ptr<Object> object (new Object (mData, mCellNode, id, false));
if (mSubModeElementMask & Mask_Reference)
if (mSubModeElementMask & SceneUtil::Mask_EditorReference)
object->setSubMode (mSubMode);
mObjects.insert (std::make_pair (id, object.release()));
@ -134,7 +134,7 @@ void CSVRender::Cell::updateLand()
else
{
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode,
mData.getResourceSystem().get(), mTerrainStorage, Mask_Terrain));
mData.getResourceSystem().get(), mTerrainStorage));
}
mTerrain->loadCell(esmLand.mX, esmLand.mY);
@ -434,7 +434,7 @@ void CSVRender::Cell::reloadAssets()
void CSVRender::Cell::setSelection (int elementMask, Selection mode)
{
if (elementMask & Mask_Reference)
if (elementMask & SceneUtil::Mask_EditorReference)
{
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
@ -451,7 +451,7 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode)
iter->second->setSelected (selected);
}
}
if (mPathgrid && elementMask & Mask_Pathgrid)
if (mPathgrid && elementMask & SceneUtil::Mask_Pathgrid)
{
// Only one pathgrid may be selected, so some operations will only have an effect
// if the pathgrid is already focused
@ -546,12 +546,12 @@ std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::Cell::getSelection (un
{
std::vector<osg::ref_ptr<TagBase> > result;
if (elementMask & Mask_Reference)
if (elementMask & SceneUtil::Mask_EditorReference)
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
if (iter->second->getSelected())
result.push_back (iter->second->getTag());
if (mPathgrid && elementMask & Mask_Pathgrid)
if (mPathgrid && elementMask & SceneUtil::Mask_Pathgrid)
if (mPathgrid->isSelected())
result.push_back(mPathgrid->getTag());
@ -562,7 +562,7 @@ std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::Cell::getEdited (unsig
{
std::vector<osg::ref_ptr<TagBase> > result;
if (elementMask & Mask_Reference)
if (elementMask & SceneUtil::Mask_EditorReference)
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
if (iter->second->isEdited())
@ -576,7 +576,7 @@ void CSVRender::Cell::setSubMode (int subMode, unsigned int elementMask)
mSubMode = subMode;
mSubModeElementMask = elementMask;
if (elementMask & Mask_Reference)
if (elementMask & SceneUtil::Mask_EditorReference)
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
iter->second->setSubMode (subMode);
@ -584,10 +584,10 @@ void CSVRender::Cell::setSubMode (int subMode, unsigned int elementMask)
void CSVRender::Cell::reset (unsigned int elementMask)
{
if (elementMask & Mask_Reference)
if (elementMask & SceneUtil::Mask_EditorReference)
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
iter->second->reset();
if (mPathgrid && elementMask & Mask_Pathgrid)
if (mPathgrid && elementMask & SceneUtil::Mask_Pathgrid)
mPathgrid->resetIndicators();
}

@ -11,11 +11,10 @@
#include "../../model/prefs/shortcutmanager.hpp"
#include <components/misc/constants.hpp>
#include "mask.hpp"
#include <components/sceneutil/vismask.hpp>
CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow)
: TagBase (Mask_CellArrow), mArrow (arrow)
: TagBase (SceneUtil::Mask_EditorCellArrow), mArrow (arrow)
{}
CSVRender::CellArrow *CSVRender::CellArrowTag::getCellArrow() const
@ -175,7 +174,7 @@ CSVRender::CellArrow::CellArrow (osg::Group *cellNode, Direction direction,
mParentNode->addChild (mBaseNode);
mBaseNode->setNodeMask (Mask_CellArrow);
mBaseNode->setNodeMask (SceneUtil::Mask_EditorCellArrow);
adjustTransform();
buildShape();

@ -7,8 +7,7 @@
#include <osg/PrimitiveSet>
#include <components/esm/loadland.hpp>
#include "mask.hpp"
#include <components/sceneutil/vismask.hpp>
#include "../../model/world/cellcoordinates.hpp"
@ -20,7 +19,7 @@ CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoor
: mParentNode(cellNode)
{
mBaseNode = new osg::PositionAttitudeTransform();
mBaseNode->setNodeMask(Mask_CellBorder);
mBaseNode->setNodeMask(SceneUtil::Mask_EditorCellBorder);
mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10));
mParentNode->addChild(mBaseNode);

@ -8,7 +8,7 @@
#include <components/misc/constants.hpp>
CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker)
: TagBase(Mask_CellMarker), mMarker(marker)
: TagBase(SceneUtil::Mask_EditorCellMarker), mMarker(marker)
{}
CSVRender::CellMarker *CSVRender::CellMarkerTag::getCellMarker() const
@ -79,7 +79,7 @@ CSVRender::CellMarker::CellMarker(
mMarkerNode->getOrCreateStateSet()->setAttribute(mat);
mMarkerNode->setUserData(new CellMarkerTag(this));
mMarkerNode->setNodeMask(Mask_CellMarker);
mMarkerNode->setNodeMask(SceneUtil::Mask_EditorCellMarker);
mCellNode->addChild(mMarkerNode);

@ -11,12 +11,12 @@
#include <components/resource/imagemanager.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/sceneutil/waterutil.hpp>
#include <components/sceneutil/vismask.hpp>
#include "../../model/world/cell.hpp"
#include "../../model/world/cellcoordinates.hpp"
#include "../../model/world/data.hpp"
#include "mask.hpp"
namespace CSVRender
{
@ -38,7 +38,7 @@ namespace CSVRender
mWaterTransform->setPosition(osg::Vec3f(cellCoords.getX() * CellSize + CellSize / 2.f,
cellCoords.getY() * CellSize + CellSize / 2.f, 0));
mWaterTransform->setNodeMask(Mask_Water);
mWaterTransform->setNodeMask(SceneUtil::Mask_Water);
mParentNode->addChild(mWaterTransform);
mWaterNode = new osg::Geode();

@ -3,18 +3,25 @@
#include <QDragEnterEvent>
#include <QPoint>
#include <QString>
#include "../../model/prefs/state.hpp"
#include <osg/ComputeBoundsVisitor>
#include <osg/Group>
#include <osg/Vec3d>
#include <osgUtil/LineSegmentIntersector>
#include "../../model/world/idtable.hpp"
#include "../../model/world/idtree.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/commandmacro.hpp"
#include "../../model/prefs/shortcut.hpp"
#include "../widget/scenetoolbar.hpp"
#include "../widget/scenetoolmode.hpp"
#include "mask.hpp"
#include <components/sceneutil/vismask.hpp>
#include "object.hpp"
#include "worldspacewidget.hpp"
@ -89,13 +96,26 @@ osg::Vec3f CSVRender::InstanceMode::getScreenCoords(const osg::Vec3f& pos)
return pos * combined;
}
CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent)
: EditMode (worldspaceWidget, QIcon (":scenetoolbar/editing-instance"), Mask_Reference | Mask_Terrain, "Instance editing",
CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, osg::ref_ptr<osg::Group> parentNode, QWidget *parent)
: EditMode (worldspaceWidget, QIcon (":scenetoolbar/editing-instance"), SceneUtil::Mask_EditorReference | SceneUtil::Mask_Terrain, "Instance editing",
parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None),
mDragAxis (-1), mLocked (false), mUnitScaleDist(1)
mDragAxis (-1), mLocked (false), mUnitScaleDist(1), mParentNode (parentNode)
{
connect(this, SIGNAL(requestFocus(const std::string&)),
worldspaceWidget, SIGNAL(requestFocus(const std::string&)));
CSMPrefs::Shortcut* deleteShortcut = new CSMPrefs::Shortcut("scene-delete", worldspaceWidget);
connect(deleteShortcut, SIGNAL(activated(bool)), this, SLOT(deleteSelectedInstances(bool)));
// Following classes could be simplified by using QSignalMapper, which is obsolete in Qt5.10, but not in Qt4.8 and Qt5.14
CSMPrefs::Shortcut* dropToCollisionShortcut = new CSMPrefs::Shortcut("scene-instance-drop-collision", worldspaceWidget);
connect(dropToCollisionShortcut, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToCollision()));
CSMPrefs::Shortcut* dropToTerrainLevelShortcut = new CSMPrefs::Shortcut("scene-instance-drop-terrain", worldspaceWidget);
connect(dropToTerrainLevelShortcut, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToTerrain()));
CSMPrefs::Shortcut* dropToCollisionShortcut2 = new CSMPrefs::Shortcut("scene-instance-drop-collision-separately", worldspaceWidget);
connect(dropToCollisionShortcut2, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToCollisionSeparately()));
CSMPrefs::Shortcut* dropToTerrainLevelShortcut2 = new CSMPrefs::Shortcut("scene-instance-drop-terrain-separately", worldspaceWidget);
connect(dropToTerrainLevelShortcut2, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToTerrainSeparately()));
}
void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar)
@ -137,13 +157,13 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar)
std::string subMode = mSubMode->getCurrentId();
getWorldspaceWidget().setSubMode (getSubModeFromId (subMode), Mask_Reference);
getWorldspaceWidget().setSubMode (getSubModeFromId (subMode), SceneUtil::Mask_EditorReference);
}
void CSVRender::InstanceMode::deactivate (CSVWidget::SceneToolbar *toolbar)
{
mDragMode = DragMode_None;
getWorldspaceWidget().reset (Mask_Reference);
getWorldspaceWidget().reset (SceneUtil::Mask_EditorReference);
if (mSelectionMode)
{
@ -196,7 +216,7 @@ void CSVRender::InstanceMode::secondaryEditPressed (const WorldspaceHitResult& h
void CSVRender::InstanceMode::primarySelectPressed (const WorldspaceHitResult& hit)
{
getWorldspaceWidget().clearSelection (Mask_Reference);
getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference);
if (hit.tag)
{
@ -231,13 +251,13 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos)
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Reference);
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference);
if (selection.empty())
{
// Only change selection at the start of drag if no object is already selected
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
{
getWorldspaceWidget().clearSelection (Mask_Reference);
getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference);
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (hit.tag.get()))
{
CSVRender::Object* object = objectTag->mObject;
@ -245,7 +265,7 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos)
}
}
selection = getWorldspaceWidget().getSelection (Mask_Reference);
selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference);
if (selection.empty())
return false;
}
@ -271,7 +291,7 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos)
mDragMode = DragMode_Scale;
// Calculate scale factor
std::vector<osg::ref_ptr<TagBase> > editedSelection = getWorldspaceWidget().getEdited (Mask_Reference);
std::vector<osg::ref_ptr<TagBase> > editedSelection = getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference);
osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection));
int widgetHeight = getWorldspaceWidget().height();
@ -307,7 +327,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
osg::Vec3f offset;
osg::Quat rotation;
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getEdited (Mask_Reference);
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference);
if (mDragMode == DragMode_Move)
{
@ -464,7 +484,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
void CSVRender::InstanceMode::dragCompleted(const QPoint& pos)
{
std::vector<osg::ref_ptr<TagBase> > selection =
getWorldspaceWidget().getEdited (Mask_Reference);
getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference);
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
@ -496,7 +516,7 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos)
void CSVRender::InstanceMode::dragAborted()
{
getWorldspaceWidget().reset (Mask_Reference);
getWorldspaceWidget().reset (SceneUtil::Mask_EditorReference);
mDragMode = DragMode_None;
}
@ -515,7 +535,7 @@ void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor)
offset *= diff * speedFactor;
std::vector<osg::ref_ptr<TagBase> > selection =
getWorldspaceWidget().getEdited (Mask_Reference);
getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference);
for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin());
iter!=selection.end(); ++iter)
@ -657,5 +677,207 @@ void CSVRender::InstanceMode::subModeChanged (const std::string& id)
{
mSubModeId = id;
getWorldspaceWidget().abortDrag();
getWorldspaceWidget().setSubMode (getSubModeFromId (id), Mask_Reference);
getWorldspaceWidget().setSubMode (getSubModeFromId (id), SceneUtil::Mask_EditorReference);
}
void CSVRender::InstanceMode::deleteSelectedInstances(bool active)
{
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference);
if (selection.empty()) return;
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& referencesTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_References));
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::CommandMacro macro (undoStack, "Delete Instances");
for(osg::ref_ptr<TagBase> tag: selection)
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (tag.get()))
macro.push(new CSMWorld::DeleteCommand(referencesTable, objectTag->mObject->getReferenceId()));
getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference);
}
void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* object, float objectHeight)
{
osg::Vec3d point = object->getPosition().asVec3();
osg::Vec3d start = point;
start.z() += objectHeight;
osg::Vec3d end = point;
end.z() = std::numeric_limits<float>::lowest();
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(
osgUtil::Intersector::MODEL, start, end) );
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);
osgUtil::IntersectionVisitor visitor(intersector);
if (dropMode == TerrainSep)
visitor.setTraversalMask(SceneUtil::Mask_Terrain);
if (dropMode == CollisionSep)
visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference);
mParentNode->accept(visitor);
for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();
it != intersector->getIntersections().end(); ++it)
{
osgUtil::LineSegmentIntersector::Intersection intersection = *it;
ESM::Position position = object->getPosition();
object->setEdited (Object::Override_Position);
position.pos[2] = intersection.getWorldIntersectPoint().z() + objectHeight;
object->setPosition(position.pos);
return;
}
}
float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Object* object, float objectHeight)
{
osg::Vec3d point = object->getPosition().asVec3();
osg::Vec3d start = point;
start.z() += objectHeight;
osg::Vec3d end = point;
end.z() = std::numeric_limits<float>::lowest();
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(
osgUtil::Intersector::MODEL, start, end) );
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);
osgUtil::IntersectionVisitor visitor(intersector);
if (dropMode == Terrain)
visitor.setTraversalMask(SceneUtil::Mask_Terrain);
if (dropMode == Collision)
visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference);
mParentNode->accept(visitor);
for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();
it != intersector->getIntersections().end(); ++it)
{
osgUtil::LineSegmentIntersector::Intersection intersection = *it;
float collisionLevel = intersection.getWorldIntersectPoint().z();
return point.z() - collisionLevel + objectHeight;
}
return 0.0f;
}
void CSVRender::InstanceMode::dropSelectedInstancesToCollision()
{
handleDropMethod(Collision, "Drop instances to next collision");
}
void CSVRender::InstanceMode::dropSelectedInstancesToTerrain()
{
handleDropMethod(Terrain, "Drop instances to terrain level");
}
void CSVRender::InstanceMode::dropSelectedInstancesToCollisionSeparately()
{
handleDropMethod(TerrainSep, "Drop instances to next collision level separately");
}
void CSVRender::InstanceMode::dropSelectedInstancesToTerrainSeparately()
{
handleDropMethod(CollisionSep, "Drop instances to terrain level separately");
}
void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString commandMsg)
{
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference);
if (selection.empty())
return;
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::CommandMacro macro (undoStack, commandMsg);
DropObjectDataHandler dropObjectDataHandler(&getWorldspaceWidget());
switch (dropMode)
{
case Terrain:
case Collision:
{
float smallestDropHeight = std::numeric_limits<float>::max();
int counter = 0;
for(osg::ref_ptr<TagBase> tag: selection)
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (tag.get()))
{
float thisDrop = getDropHeight(dropMode, objectTag->mObject, dropObjectDataHandler.mObjectHeights[counter]);
if (thisDrop < smallestDropHeight)
smallestDropHeight = thisDrop;
counter++;
}
for(osg::ref_ptr<TagBase> tag: selection)
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (tag.get()))
{
objectTag->mObject->setEdited (Object::Override_Position);
ESM::Position position = objectTag->mObject->getPosition();
position.pos[2] -= smallestDropHeight;
objectTag->mObject->setPosition(position.pos);
objectTag->mObject->apply (macro);
}
}
break;
case TerrainSep:
case CollisionSep:
{
int counter = 0;
for(osg::ref_ptr<TagBase> tag: selection)
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (tag.get()))
{
dropInstance(dropMode, objectTag->mObject, dropObjectDataHandler.mObjectHeights[counter]);
objectTag->mObject->apply (macro);
counter++;
}
}
break;
}
}
CSVRender::DropObjectDataHandler::DropObjectDataHandler(WorldspaceWidget* worldspacewidget)
: mWorldspaceWidget(worldspacewidget)
{
std::vector<osg::ref_ptr<TagBase> > selection = mWorldspaceWidget->getSelection (SceneUtil::Mask_EditorReference);
for(osg::ref_ptr<TagBase> tag: selection)
{
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (tag.get()))
{
osg::ref_ptr<osg::Group> objectNodeWithGUI = objectTag->mObject->getRootNode();
osg::ref_ptr<osg::Group> objectNodeWithoutGUI = objectTag->mObject->getBaseNode();
osg::ComputeBoundsVisitor computeBounds;
computeBounds.setTraversalMask(SceneUtil::Mask_EditorReference);
objectNodeWithoutGUI->accept(computeBounds);
osg::BoundingBox bounds = computeBounds.getBoundingBox();
float boundingBoxOffset = 0.0f;
if (bounds.valid())
boundingBoxOffset = bounds.zMin();
mObjectHeights.emplace_back(boundingBoxOffset);
mOldMasks.emplace_back(objectNodeWithGUI->getNodeMask());
objectNodeWithGUI->setNodeMask(SceneUtil::Mask_Disabled);
}
}
}
CSVRender::DropObjectDataHandler::~DropObjectDataHandler()
{
std::vector<osg::ref_ptr<TagBase> > selection = mWorldspaceWidget->getSelection (SceneUtil::Mask_EditorReference);
int counter = 0;
for(osg::ref_ptr<TagBase> tag: selection)
{
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (tag.get()))
{
osg::ref_ptr<osg::Group> objectNodeWithGUI = objectTag->mObject->getRootNode();
objectNodeWithGUI->setNodeMask(mOldMasks[counter]);
counter++;
}
}
}

@ -1,7 +1,10 @@
#ifndef CSV_RENDER_INSTANCEMODE_H
#define CSV_RENDER_INSTANCEMODE_H
#include <QString>
#include <osg/ref_ptr>
#include <osg/Group>
#include <osg/Quat>
#include <osg/Vec3f>
@ -16,6 +19,7 @@ namespace CSVRender
{
class TagBase;
class InstanceSelectionMode;
class Object;
class InstanceMode : public EditMode
{
@ -29,6 +33,14 @@ namespace CSVRender
DragMode_Scale
};
enum DropMode
{
Collision,
Terrain,
CollisionSep,
TerrainSep
};
CSVWidget::SceneToolMode *mSubMode;
std::string mSubModeId;
InstanceSelectionMode *mSelectionMode;
@ -36,6 +48,7 @@ namespace CSVRender
int mDragAxis;
bool mLocked;
float mUnitScaleDist;
osg::ref_ptr<osg::Group> mParentNode;
int getSubModeFromId (const std::string& id) const;
@ -44,10 +57,12 @@ namespace CSVRender
osg::Vec3f getSelectionCenter(const std::vector<osg::ref_ptr<TagBase> >& selection) const;
osg::Vec3f getScreenCoords(const osg::Vec3f& pos);
void dropInstance(DropMode dropMode, CSVRender::Object* object, float objectHeight);
float getDropHeight(DropMode dropMode, CSVRender::Object* object, float objectHeight);
public:
InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent = 0);
InstanceMode (WorldspaceWidget *worldspaceWidget, osg::ref_ptr<osg::Group> parentNode, QWidget *parent = 0);
virtual void activate (CSVWidget::SceneToolbar *toolbar);
@ -92,6 +107,25 @@ namespace CSVRender
private slots:
void subModeChanged (const std::string& id);
void deleteSelectedInstances(bool active);
void dropSelectedInstancesToCollision();
void dropSelectedInstancesToTerrain();
void dropSelectedInstancesToCollisionSeparately();
void dropSelectedInstancesToTerrainSeparately();
void handleDropMethod(DropMode dropMode, QString commandMsg);
};
/// \brief Helper class to handle object mask data in safe way
class DropObjectDataHandler
{
public:
DropObjectDataHandler(WorldspaceWidget* worldspacewidget);
~DropObjectDataHandler();
std::vector<float> mObjectHeights;
private:
WorldspaceWidget* mWorldspaceWidget;
std::vector<osg::Node::NodeMask> mOldMasks;
};
}

@ -6,13 +6,15 @@
#include "../../model/world/idtable.hpp"
#include "../../model/world/commands.hpp"
#include <components/sceneutil/vismask.hpp>
#include "worldspacewidget.hpp"
#include "object.hpp"
namespace CSVRender
{
InstanceSelectionMode::InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget)
: SelectionMode(parent, worldspaceWidget, Mask_Reference)
: SelectionMode(parent, worldspaceWidget, SceneUtil::Mask_EditorReference)
{
mSelectSame = new QAction("Extend selection to instances with same object ID", this);
mDeleteSelection = new QAction("Delete selected instances", this);
@ -36,12 +38,12 @@ namespace CSVRender
void InstanceSelectionMode::selectSame()
{
getWorldspaceWidget().selectAllWithSameParentId(Mask_Reference);
getWorldspaceWidget().selectAllWithSameParentId(SceneUtil::Mask_EditorReference);
}
void InstanceSelectionMode::deleteSelection()
{
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection(Mask_Reference);
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection(SceneUtil::Mask_EditorReference);
CSMWorld::IdTable& referencesTable = dynamic_cast<CSMWorld::IdTable&>(
*getWorldspaceWidget().getDocument().getData().getTableModel(CSMWorld::UniversalId::Type_References));

@ -1,35 +0,0 @@
#ifndef CSV_RENDER_ELEMENTS_H
#define CSV_RENDER_ELEMENTS_H
namespace CSVRender
{
/// Node masks used on the OSG scene graph in OpenMW-CS.
/// @note See the respective file in OpenMW (apps/openmw/mwrender/vismask.hpp)
/// for general usage hints about node masks.
/// @copydoc MWRender::VisMask
enum Mask
{
// internal use within NifLoader, do not change
Mask_UpdateVisitor = 0x1,
// elements that are part of the actual scene
Mask_Reference = 0x2,
Mask_Pathgrid = 0x4,
Mask_Water = 0x8,
Mask_Fog = 0x10,
Mask_Terrain = 0x20,
// used within models
Mask_ParticleSystem = 0x100,
Mask_Lighting = 0x200,
// control elements
Mask_CellMarker = 0x10000,
Mask_CellArrow = 0x20000,
Mask_CellBorder = 0x40000
};
}
#endif

@ -29,9 +29,9 @@
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/lightutil.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/vismask.hpp>
#include "actor.hpp"
#include "mask.hpp"
const float CSVRender::Object::MarkerShaftWidth = 30;
@ -58,7 +58,7 @@ namespace
CSVRender::ObjectTag::ObjectTag (Object* object)
: TagBase (Mask_Reference), mObject (object)
: TagBase (SceneUtil::Mask_EditorReference), mObject (object)
{}
QString CSVRender::ObjectTag::getToolTip (bool hideBasics) const
@ -140,7 +140,7 @@ void CSVRender::Object::update()
if (light)
{
bool isExterior = false; // FIXME
SceneUtil::addLight(mBaseNode, light, Mask_ParticleSystem, Mask_Lighting, isExterior);
SceneUtil::addLight(mBaseNode, light, isExterior);
}
}
@ -429,7 +429,7 @@ CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode,
parentNode->addChild (mRootNode);
mRootNode->setNodeMask(Mask_Reference);
mRootNode->setNodeMask(SceneUtil::Mask_EditorReference);
if (referenceable)
{
@ -477,6 +477,16 @@ bool CSVRender::Object::getSelected() const
return mSelected;
}
osg::ref_ptr<osg::Group> CSVRender::Object::getRootNode()
{
return mRootNode;
}
osg::ref_ptr<osg::Group> CSVRender::Object::getBaseNode()
{
return mBaseNode;
}
bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
@ -705,7 +715,7 @@ void CSVRender::Object::apply (CSMWorld::CommandMacro& commands)
CSMWorld::Columns::ColumnId_PositionXRot+i));
commands.push (new CSMWorld::ModifyCommand (*model,
model->index (recordIndex, column), mPositionOverride.rot[i]));
model->index (recordIndex, column), osg::RadiansToDegrees(mPositionOverride.rot[i])));
}
}

@ -146,6 +146,12 @@ namespace CSVRender
bool getSelected() const;
/// Get object node with GUI graphics
osg::ref_ptr<osg::Group> getRootNode();
/// Get object node without GUI graphics
osg::ref_ptr<osg::Group> getBaseNode();
/// \return Did this call result in a modification of the visual representation of
/// this object?
bool referenceableDataChanged (const QModelIndex& topLeft,

@ -21,7 +21,6 @@
#include "../widget/scenetooltoggle2.hpp"
#include "editmode.hpp"
#include "mask.hpp"
#include "cameracontroller.hpp"
#include "cellarrow.hpp"
#include "terraintexturemode.hpp"
@ -127,8 +126,8 @@ void CSVRender::PagedWorldspaceWidget::addVisibilitySelectorButtons (
CSVWidget::SceneToolToggle2 *tool)
{
WorldspaceWidget::addVisibilitySelectorButtons (tool);
tool->addButton (Button_Terrain, Mask_Terrain, "Terrain");
tool->addButton (Button_Fog, Mask_Fog, "Fog", "", true);
tool->addButton (Button_Terrain, SceneUtil::Mask_Terrain, "Terrain");
//tool->addButton (Button_Fog, Mask_Fog, "Fog", "", true);
}
void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
@ -142,16 +141,16 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
tool->addButton (
new TerrainTextureMode (this, mRootNode, tool), "terrain-texture");
tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"),
new EditMode (this, QIcon (":placeholder"), SceneUtil::Mask_EditorReference, "Terrain vertex paint editing"),
"terrain-vertex");
tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain movement"),
new EditMode (this, QIcon (":placeholder"), SceneUtil::Mask_EditorReference, "Terrain movement"),
"terrain-move");
}
void CSVRender::PagedWorldspaceWidget::handleInteractionPress (const WorldspaceHitResult& hit, InteractionType type)
{
if (hit.tag && hit.tag->getMask()==Mask_CellArrow)
if (hit.tag && hit.tag->getMask()==SceneUtil::Mask_EditorCellArrow)
{
if (CellArrowTag *cellArrowTag = dynamic_cast<CSVRender::CellArrowTag *> (hit.tag.get()))
{
@ -874,9 +873,9 @@ CSVWidget::SceneToolToggle2 *CSVRender::PagedWorldspaceWidget::makeControlVisibi
mControlElements = new CSVWidget::SceneToolToggle2 (parent,
"Controls & Guides Visibility", ":scenetoolbar/scene-view-marker-c", ":scenetoolbar/scene-view-marker-");
mControlElements->addButton (1, Mask_CellMarker, "Cell Marker");
mControlElements->addButton (2, Mask_CellArrow, "Cell Arrows");
mControlElements->addButton (4, Mask_CellBorder, "Cell Border");
mControlElements->addButton (1, SceneUtil::Mask_EditorCellMarker, "Cell Marker");
mControlElements->addButton (2, SceneUtil::Mask_EditorCellArrow, "Cell Arrows");
mControlElements->addButton (4, SceneUtil::Mask_EditorCellBorder, "Cell Border");
mControlElements->setSelectionMask (0xffffffff);

@ -10,6 +10,7 @@
#include <osg/Vec3>
#include <components/sceneutil/pathgridutil.hpp>
#include <components/sceneutil/vismask.hpp>
#include "../../model/world/cell.hpp"
#include "../../model/world/commands.hpp"
@ -31,7 +32,7 @@ namespace CSVRender
};
PathgridTag::PathgridTag(Pathgrid* pathgrid)
: TagBase(Mask_Pathgrid), mPathgrid(pathgrid)
: TagBase(SceneUtil::Mask_Pathgrid), mPathgrid(pathgrid)
{
}
@ -70,7 +71,7 @@ namespace CSVRender
mBaseNode->setPosition(osg::Vec3f(mCoords.getX() * CoordScalar, mCoords.getY() * CoordScalar, 0.f));
mBaseNode->setUserData(mTag);
mBaseNode->setUpdateCallback(new PathgridNodeCallback());
mBaseNode->setNodeMask(Mask_Pathgrid);
mBaseNode->setNodeMask(SceneUtil::Mask_Pathgrid);
mParent->addChild(mBaseNode);
mPathgridGeode = new osg::Geode();

@ -4,6 +4,7 @@
#include <QPoint>
#include <components/sceneutil/pathgridutil.hpp>
#include <components/sceneutil/vismask.hpp>
#include "../../model/prefs/state.hpp"
@ -15,7 +16,6 @@
#include "../widget/scenetoolbar.hpp"
#include "cell.hpp"
#include "mask.hpp"
#include "pathgrid.hpp"
#include "pathgridselectionmode.hpp"
#include "worldspacewidget.hpp"
@ -23,7 +23,7 @@
namespace CSVRender
{
PathgridMode::PathgridMode(WorldspaceWidget* worldspaceWidget, QWidget* parent)
: EditMode(worldspaceWidget, QIcon(":placeholder"), Mask_Pathgrid | Mask_Terrain | Mask_Reference,
: EditMode(worldspaceWidget, QIcon(":placeholder"), SceneUtil::Mask_Pathgrid | SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference,
getTooltip(), parent)
, mDragMode(DragMode_None)
, mFromNode(0)
@ -110,7 +110,7 @@ namespace CSVRender
void PathgridMode::primarySelectPressed(const WorldspaceHitResult& hit)
{
getWorldspaceWidget().clearSelection(Mask_Pathgrid);
getWorldspaceWidget().clearSelection(SceneUtil::Mask_Pathgrid);
if (hit.tag)
{
@ -131,7 +131,7 @@ namespace CSVRender
{
if (tag->getPathgrid()->getId() != mLastId)
{
getWorldspaceWidget().clearSelection(Mask_Pathgrid);
getWorldspaceWidget().clearSelection(SceneUtil::Mask_Pathgrid);
mLastId = tag->getPathgrid()->getId();
}
@ -142,12 +142,12 @@ namespace CSVRender
}
}
getWorldspaceWidget().clearSelection(Mask_Pathgrid);
getWorldspaceWidget().clearSelection(SceneUtil::Mask_Pathgrid);
}
bool PathgridMode::primaryEditStartDrag(const QPoint& pos)
{
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid);
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid);
if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
{
@ -156,7 +156,7 @@ namespace CSVRender
if (dynamic_cast<PathgridTag*>(hit.tag.get()))
{
primarySelectPressed(hit);
selection = getWorldspaceWidget().getSelection (Mask_Pathgrid);
selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid);
}
}
@ -192,7 +192,7 @@ namespace CSVRender
{
if (mDragMode == DragMode_Move)
{
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection(Mask_Pathgrid);
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection(SceneUtil::Mask_Pathgrid);
for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it != selection.end(); ++it)
{
@ -233,7 +233,7 @@ namespace CSVRender
{
if (mDragMode == DragMode_Move)
{
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid);
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid);
for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it != selection.end(); ++it)
{
if (PathgridTag* tag = dynamic_cast<PathgridTag*>(it->get()))
@ -272,11 +272,11 @@ namespace CSVRender
}
mDragMode = DragMode_None;
getWorldspaceWidget().reset(Mask_Pathgrid);
getWorldspaceWidget().reset(SceneUtil::Mask_Pathgrid);
}
void PathgridMode::dragAborted()
{
getWorldspaceWidget().reset(Mask_Pathgrid);
getWorldspaceWidget().reset(SceneUtil::Mask_Pathgrid);
}
}

@ -13,7 +13,7 @@
namespace CSVRender
{
PathgridSelectionMode::PathgridSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget)
: SelectionMode(parent, worldspaceWidget, Mask_Pathgrid)
: SelectionMode(parent, worldspaceWidget, SceneUtil::Mask_Pathgrid)
{
mRemoveSelectedNodes = new QAction("Remove selected nodes", this);
mRemoveSelectedEdges = new QAction("Remove edges between selected nodes", this);
@ -37,7 +37,7 @@ namespace CSVRender
void PathgridSelectionMode::removeSelectedNodes()
{
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid);
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid);
for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it != selection.end(); ++it)
{
@ -54,7 +54,7 @@ namespace CSVRender
void PathgridSelectionMode::removeSelectedEdges()
{
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid);
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid);
for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it != selection.end(); ++it)
{

@ -17,6 +17,7 @@
#include <components/resource/scenemanager.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/vismask.hpp>
#include "../widget/scenetoolmode.hpp"
@ -25,7 +26,6 @@
#include "../../model/prefs/shortcuteventhandler.hpp"
#include "lighting.hpp"
#include "mask.hpp"
#include "cameracontroller.hpp"
namespace CSVRender
@ -71,7 +71,7 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f)
SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager;
lightMgr->setStartLight(1);
lightMgr->setLightingMask(Mask_Lighting);
lightMgr->setLightingMask(SceneUtil::Mask_Lighting);
mRootNode = lightMgr;
mView->getCamera()->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
@ -88,7 +88,7 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f)
// Add ability to signal osg to show its statistics for debugging purposes
mView->addEventHandler(new osgViewer::StatsHandler);
mView->getCamera()->setCullMask(~(Mask_UpdateVisitor));
mView->getCamera()->setCullMask(~(SceneUtil::Mask_UpdateVisitor));
viewer.addView(mView);
viewer.setDone(false);
@ -100,6 +100,14 @@ RenderWidget::~RenderWidget()
try
{
CompositeViewer::get().removeView(mView);
#if OSG_VERSION_LESS_THAN(3,6,5)
// before OSG 3.6.4, the default font was a static object, and if it wasn't attached to the scene when a graphics context was destroyed, it's program wouldn't be released.
// 3.6.4 moved it into the object cache, which meant it usually got released, but not here.
// 3.6.5 improved cleanup with osgViewer::CompositeViewer::removeView so it more reliably released associated state for objects in the object cache.
osg::ref_ptr<osg::GraphicsContext> graphicsContext = mView->getCamera()->getGraphicsContext();
osgText::Font::getDefaultFont()->releaseGLObjects(graphicsContext->getState());
#endif
}
catch(const std::exception& e)
{
@ -114,7 +122,7 @@ void RenderWidget::flagAsModified()
void RenderWidget::setVisibilityMask(int mask)
{
mView->getCamera()->setCullMask(mask | Mask_ParticleSystem | Mask_Lighting);
mView->getCamera()->setCullMask(mask | SceneUtil::Mask_ParticleSystem | SceneUtil::Mask_Lighting);
}
osg::Camera *RenderWidget::getCamera()
@ -204,7 +212,7 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
mOrbitCamControl = new OrbitCameraController(this);
mCurrentCamControl = mFreeCamControl;
mOrbitCamControl->setPickingMask(Mask_Reference | Mask_Terrain);
mOrbitCamControl->setPickingMask(SceneUtil::Mask_EditorReference | SceneUtil::Mask_Terrain);
mOrbitCamControl->setConstRoll( CSMPrefs::get()["3D Scene Input"]["navi-orbit-const-roll"].isTrue() );
@ -213,7 +221,7 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
setLighting(&mLightingDay);
mResourceSystem->getSceneManager()->setParticleSystemMask(Mask_ParticleSystem);
mResourceSystem->getSceneManager()->setParticleSystemMask(SceneUtil::Mask_ParticleSystem);
// Recieve mouse move event even if mouse button is not pressed
setMouseTracking(true);
@ -342,7 +350,7 @@ void SceneWidget::update(double dt)
}
else
{
mCurrentCamControl->setup(mRootNode, Mask_Reference | Mask_Terrain, CameraController::WorldUp);
mCurrentCamControl->setup(mRootNode, SceneUtil::Mask_EditorReference | SceneUtil::Mask_Terrain, CameraController::WorldUp);
mCamPositionSet = true;
}
}

@ -3,8 +3,6 @@
#include "../widget/scenetoolmode.hpp"
#include "mask.hpp"
class QAction;
namespace CSVRender

@ -1,9 +1,9 @@
#include "tagbase.hpp"
CSVRender::TagBase::TagBase (Mask mask) : mMask (mask) {}
CSVRender::TagBase::TagBase (SceneUtil::VisMask mask) : mMask (mask) {}
CSVRender::Mask CSVRender::TagBase::getMask() const
SceneUtil::VisMask CSVRender::TagBase::getMask() const
{
return mMask;
}

@ -5,19 +5,19 @@
#include <QString>
#include "mask.hpp"
#include <components/sceneutil/vismask.hpp>
namespace CSVRender
{
class TagBase : public osg::Referenced
{
Mask mMask;
SceneUtil::VisMask mMask;
public:
TagBase (Mask mask);
TagBase (SceneUtil::VisMask mask);
Mask getMask() const;
SceneUtil::VisMask getMask() const;
virtual QString getToolTip (bool hideBasics) const;

@ -17,6 +17,7 @@
#include <components/esm/loadland.hpp>
#include <components/debug/debuglog.hpp>
#include <components/sceneutil/vismask.hpp>
#include "../widget/brushshapes.hpp"
#include "../widget/modebutton.hpp"
@ -38,13 +39,12 @@
#include "editmode.hpp"
#include "pagedworldspacewidget.hpp"
#include "mask.hpp"
#include "tagbase.hpp"
#include "terrainselection.hpp"
#include "worldspacewidget.hpp"
CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent)
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain | Mask_Reference, "Terrain land editing", parent),
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference, "Terrain land editing", parent),
mParentNode(parentNode)
{
}
@ -273,7 +273,6 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
int landMapLodColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandMapLodIndex);
int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex);
QUndoStack& undoStack = document.getUndoStack();
@ -287,9 +286,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY());
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();
const CSMWorld::LandMapLodColumn::DataType landMapLodPointer = landTable.data(landTable.getModelIndex(cellId, landMapLodColumn)).value<CSMWorld::LandMapLodColumn::DataType>();
CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer);
CSMWorld::LandMapLodColumn::DataType mapLodShapeNew(landMapLodPointer);
CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget());
// Generate land height record
@ -304,26 +301,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
}
}
// Generate WNAM record
int sqrtLandGlobalMapLodSize = sqrt(ESM::Land::LAND_GLOBAL_MAP_LOD_SIZE);
for(int i = 0; i < sqrtLandGlobalMapLodSize; ++i)
{
for(int j = 0; j < sqrtLandGlobalMapLodSize; ++j)
{
int col = (static_cast<float>(j) / sqrtLandGlobalMapLodSize) * (ESM::Land::LAND_SIZE - 1);
int row = (static_cast<float>(i) / sqrtLandGlobalMapLodSize) * (ESM::Land::LAND_SIZE - 1);
signed char lodHeight = 0;
float floatLodHeight = 0;
if (landShapeNew[col * ESM::Land::LAND_SIZE + row] > 0) floatLodHeight = landShapeNew[col * ESM::Land::LAND_SIZE + row] / 128;
if (landShapeNew[col * ESM::Land::LAND_SIZE + row] <= 0) floatLodHeight = landShapeNew[col * ESM::Land::LAND_SIZE + row] / 16;
if (floatLodHeight > std::numeric_limits<signed char>::max()) lodHeight = std::numeric_limits<signed char>::max();
else if (floatLodHeight < std::numeric_limits<signed char>::min()) lodHeight = std::numeric_limits<signed char>::min();
else lodHeight = static_cast<signed char>(floatLodHeight);
mapLodShapeNew[j * sqrtLandGlobalMapLodSize + i] = lodHeight;
}
}
pushEditToCommand(landShapeNew, document, landTable, cellId);
pushLodToCommand(mapLodShapeNew, document, landTable, cellId);
}
for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells)
@ -1136,18 +1114,6 @@ void CSVRender::TerrainShapeMode::pushNormalsEditToCommand(const CSMWorld::LandN
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));
}
void CSVRender::TerrainShapeMode::pushLodToCommand(const CSMWorld::LandMapLodColumn::DataType& newLandMapLod, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, const std::string& cellId)
{
QVariant changedLod;
changedLod.setValue(newLandMapLod);
QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandMapLodIndex)));
QUndoStack& undoStack = document.getUndoStack();
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLod));
}
bool CSVRender::TerrainShapeMode::noCell(const std::string& cellId)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();

@ -148,10 +148,6 @@ namespace CSVRender
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);

@ -13,6 +13,7 @@
#include <osg/Group>
#include <components/esm/loadland.hpp>
#include <components/sceneutil/vismask.hpp>
#include "../widget/modebutton.hpp"
#include "../widget/scenetoolbar.hpp"
@ -34,12 +35,11 @@
#include "editmode.hpp"
#include "pagedworldspacewidget.hpp"
#include "mask.hpp"
#include "object.hpp" // Something small needed regarding pointers from here ()
#include "worldspacewidget.hpp"
CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent)
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent),
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference, "Terrain texture editing", parent),
mBrushTexture("L0#0"),
mBrushSize(1),
mBrushShape(0),

@ -16,7 +16,6 @@
#include "../widget/scenetooltoggle2.hpp"
#include "cameracontroller.hpp"
#include "mask.hpp"
#include "tagbase.hpp"
void CSVRender::UnpagedWorldspaceWidget::update()
@ -304,8 +303,8 @@ void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons (
CSVWidget::SceneToolToggle2 *tool)
{
WorldspaceWidget::addVisibilitySelectorButtons (tool);
tool->addButton (Button_Terrain, Mask_Terrain, "Terrain", "", true);
tool->addButton (Button_Fog, Mask_Fog, "Fog");
tool->addButton (Button_Terrain, SceneUtil::Mask_Terrain, "Terrain", "", true);
//tool->addButton (Button_Fog, Mask_Fog, "Fog");
}
std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction()

@ -26,8 +26,9 @@
#include "../widget/scenetooltoggle2.hpp"
#include "../widget/scenetoolrun.hpp"
#include <components/sceneutil/vismask.hpp>
#include "object.hpp"
#include "mask.hpp"
#include "instancemode.hpp"
#include "pathgridmode.hpp"
#include "cameracontroller.hpp"
@ -138,7 +139,7 @@ void CSVRender::WorldspaceWidget::settingChanged (const CSMPrefs::Setting *setti
{
float alpha = setting->toDouble();
// getSelection is virtual, thus this can not be called from the constructor
auto selection = getSelection(Mask_Reference);
auto selection = getSelection(SceneUtil::Mask_EditorReference);
for (osg::ref_ptr<TagBase> tag : selection)
{
if (auto objTag = dynamic_cast<ObjectTag*>(tag.get()))
@ -345,7 +346,7 @@ unsigned int CSVRender::WorldspaceWidget::getVisibilityMask() const
void CSVRender::WorldspaceWidget::setInteractionMask (unsigned int mask)
{
mInteractionMask = mask | Mask_CellMarker | Mask_CellArrow;
mInteractionMask = mask | SceneUtil::Mask_EditorCellMarker | SceneUtil::Mask_EditorCellArrow;
}
unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const
@ -361,15 +362,15 @@ void CSVRender::WorldspaceWidget::setEditLock (bool locked)
void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons (
CSVWidget::SceneToolToggle2 *tool)
{
tool->addButton (Button_Reference, Mask_Reference, "Instances");
tool->addButton (Button_Water, Mask_Water, "Water");
tool->addButton (Button_Pathgrid, Mask_Pathgrid, "Pathgrid");
tool->addButton (Button_Reference, SceneUtil::Mask_EditorReference, "Instances");
tool->addButton (Button_Water, SceneUtil::Mask_Water, "Water");
tool->addButton (Button_Pathgrid, SceneUtil::Mask_Pathgrid, "Pathgrid");
}
void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool)
{
/// \todo replace EditMode with suitable subclasses
tool->addButton (new InstanceMode (this, tool), "object");
tool->addButton (new InstanceMode (this, mRootNode, tool), "object");
tool->addButton (new PathgridMode (this, tool), "pathgrid");
}

@ -8,7 +8,6 @@
#include "../../model/world/tablemimedata.hpp"
#include "scenewidget.hpp"
#include "mask.hpp"
namespace CSMPrefs
{

@ -18,10 +18,10 @@ set(GAME_HEADER
source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
actors objects renderingmanager animation rotatecontroller sky npcanimation
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths
renderbin actoranimation landmanager navmesh actorspaths recastmesh
)
add_openmw_dir (mwinput

@ -26,6 +26,7 @@
#include <components/compiler/extensions0.hpp>
#include <components/sceneutil/vismask.hpp>
#include <components/sceneutil/workqueue.hpp>
#include <components/files/configurationmanager.hpp>
@ -47,8 +48,6 @@
#include "mwworld/player.hpp"
#include "mwworld/worldimp.hpp"
#include "mwrender/vismask.hpp"
#include "mwclass/classes.hpp"
#include "mwdialogue/dialoguemanagerimp.hpp"
@ -558,7 +557,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
std::string myguiResources = (mResDir / "mygui").string();
osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
guiRoot->setName("GUI Root");
guiRoot->setNodeMask(MWRender::Mask_GUI);
guiRoot->setNodeMask(SceneUtil::Mask_GUI);
rootNode->addChild(guiRoot);
MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
mCfgMgr.getLogPath().string() + std::string("/"), myguiResources,

@ -325,6 +325,8 @@ namespace MWBase
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;
virtual bool castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask, const MWWorld::ConstPtr& ignore) = 0;
virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external) = 0;
virtual bool isActorCollisionEnabled(const MWWorld::Ptr& ptr) = 0;
@ -653,6 +655,8 @@ namespace MWBase
virtual MWPhysics::PhysicsSystem* getPhysicsSystem(void) = 0;
virtual void toggleWaterRTT(bool enable) = 0;
virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0;
};
}

@ -3,6 +3,7 @@
#include <components/esm/loadacti.hpp>
#include <components/misc/rng.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/vismask.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
@ -19,7 +20,6 @@
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwrender/vismask.hpp"
#include "../mwgui/tooltips.hpp"
@ -34,7 +34,7 @@ namespace MWClass
if (!model.empty())
{
renderingInterface.getObjects().insertModel(ptr, model, true);
ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static);
ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Static);
}
}

@ -317,7 +317,6 @@ namespace MWClass
{
if (!state.mHasCustomState)
return;
const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state);
if (!ptr.getRefData().getCustomData())
{
@ -326,21 +325,21 @@ namespace MWClass
ptr.getRefData().setCustomData (data.release());
}
dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
readState (state2.mInventory);
ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData();
const ESM::ContainerState& containerState = state.asContainerState();
customData.mContainerStore.readState (containerState.mInventory);
}
void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
{
ESM::ContainerState& state2 = dynamic_cast<ESM::ContainerState&> (state);
if (!ptr.getRefData().getCustomData())
{
state.mHasCustomState = false;
return;
}
dynamic_cast<const ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
writeState (state2.mInventory);
const ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData();
ESM::ContainerState& containerState = state.asContainerState();
customData.mContainerStore.writeState (containerState.mInventory);
}
}

@ -777,8 +777,6 @@ namespace MWClass
if (!state.mHasCustomState)
return;
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
if (state.mVersion > 0)
{
if (!ptr.getRefData().getCustomData())
@ -798,16 +796,14 @@ namespace MWClass
ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless.
CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
customData.mContainerStore->readState (state2.mInventory);
customData.mCreatureStats.readState (state2.mCreatureStats);
const ESM::CreatureState& creatureState = state.asCreatureState();
customData.mContainerStore->readState (creatureState.mInventory);
customData.mCreatureStats.readState (creatureState.mCreatureStats);
}
void Creature::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state)
const
{
ESM::CreatureState& state2 = dynamic_cast<ESM::CreatureState&> (state);
if (!ptr.getRefData().getCustomData())
{
state.mHasCustomState = false;
@ -815,9 +811,9 @@ namespace MWClass
}
const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
customData.mContainerStore->writeState (state2.mInventory);
customData.mCreatureStats.writeState (state2.mCreatureStats);
ESM::CreatureState& creatureState = state.asCreatureState();
customData.mContainerStore->writeState (creatureState.mInventory);
customData.mCreatureStats.writeState (creatureState.mCreatureStats);
}
int Creature::getBaseGold(const MWWorld::ConstPtr& ptr) const

@ -151,19 +151,16 @@ namespace MWClass
if (!state.mHasCustomState)
return;
const ESM::CreatureLevListState& state2 = dynamic_cast<const ESM::CreatureLevListState&> (state);
ensureCustomData(ptr);
CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();
customData.mSpawnActorId = state2.mSpawnActorId;
customData.mSpawn = state2.mSpawn;
const ESM::CreatureLevListState& levListState = state.asCreatureLevListState();
customData.mSpawnActorId = levListState.mSpawnActorId;
customData.mSpawn = levListState.mSpawn;
}
void CreatureLevList::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state)
const
{
ESM::CreatureLevListState& state2 = dynamic_cast<ESM::CreatureLevListState&> (state);
if (!ptr.getRefData().getCustomData())
{
state.mHasCustomState = false;
@ -171,7 +168,8 @@ namespace MWClass
}
const CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();
state2.mSpawnActorId = customData.mSpawnActorId;
state2.mSpawn = customData.mSpawn;
ESM::CreatureLevListState& levListState = state.asCreatureLevListState();
levListState.mSpawnActorId = customData.mSpawnActorId;
levListState.mSpawn = customData.mSpawn;
}
}

@ -3,6 +3,7 @@
#include <components/esm/loaddoor.hpp>
#include <components/esm/doorstate.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/vismask.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -25,7 +26,6 @@
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwrender/animation.hpp"
#include "../mwrender/vismask.hpp"
#include "../mwmechanics/actorutil.hpp"
@ -58,7 +58,7 @@ namespace MWClass
if (!model.empty())
{
renderingInterface.getObjects().insertModel(ptr, model, true);
ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static);
ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Static);
}
}
@ -370,11 +370,11 @@ namespace MWClass
{
if (!state.mHasCustomState)
return;
ensureCustomData(ptr);
DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
const ESM::DoorState& state2 = dynamic_cast<const ESM::DoorState&>(state);
customData.mDoorState = static_cast<MWWorld::DoorState>(state2.mDoorState);
const ESM::DoorState& doorState = state.asDoorState();
customData.mDoorState = MWWorld::DoorState(doorState.mDoorState);
}
void Door::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
@ -384,10 +384,10 @@ namespace MWClass
state.mHasCustomState = false;
return;
}
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
ESM::DoorState& state2 = dynamic_cast<ESM::DoorState&>(state);
state2.mDoorState = static_cast<int>(customData.mDoorState);
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
ESM::DoorState& doorState = state.asDoorState();
doorState.mDoorState = int(customData.mDoorState);
}
}

@ -1307,8 +1307,6 @@ namespace MWClass
if (!state.mHasCustomState)
return;
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
if (state.mVersion > 0)
{
if (!ptr.getRefData().getCustomData())
@ -1322,17 +1320,15 @@ namespace MWClass
ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless.
NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();
customData.mInventoryStore.readState (state2.mInventory);
customData.mNpcStats.readState (state2.mNpcStats);
static_cast<MWMechanics::CreatureStats&> (customData.mNpcStats).readState (state2.mCreatureStats);
const ESM::NpcState& npcState = state.asNpcState();
customData.mInventoryStore.readState (npcState.mInventory);
customData.mNpcStats.readState (npcState.mNpcStats);
customData.mNpcStats.readState (npcState.mCreatureStats);
}
void Npc::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state)
const
{
ESM::NpcState& state2 = dynamic_cast<ESM::NpcState&> (state);
if (!ptr.getRefData().getCustomData())
{
state.mHasCustomState = false;
@ -1340,10 +1336,10 @@ namespace MWClass
}
const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();
customData.mInventoryStore.writeState (state2.mInventory);
customData.mNpcStats.writeState (state2.mNpcStats);
static_cast<const MWMechanics::CreatureStats&> (customData.mNpcStats).writeState (state2.mCreatureStats);
ESM::NpcState& npcState = state.asNpcState();
customData.mInventoryStore.writeState (npcState.mInventory);
customData.mNpcStats.writeState (npcState.mNpcStats);
customData.mNpcStats.writeState (npcState.mCreatureStats);
}
int Npc::getBaseGold(const MWWorld::ConstPtr& ptr) const

@ -2,6 +2,7 @@
#include <components/esm/loadstat.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/vismask.hpp>
#include "../mwworld/ptr.hpp"
#include "../mwphysics/physicssystem.hpp"
@ -9,7 +10,6 @@
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwrender/vismask.hpp"
namespace MWClass
{
@ -19,7 +19,7 @@ namespace MWClass
if (!model.empty())
{
renderingInterface.getObjects().insertModel(ptr, model);
ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static);
ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Static);
}
}

@ -3,6 +3,7 @@
#include <MyGUI_Gui.h>
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
#include <MyGUI_ComboBox.h>
#include <MyGUI_ControllerManager.h>
#include <MyGUI_ControllerRepeatClick.h>
@ -17,6 +18,7 @@
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include <MyGUI_Macros.h>
#include <components/esm/records.hpp>
#include "inventoryitemmodel.hpp"
@ -29,6 +31,7 @@ namespace MWGui
{
AlchemyWindow::AlchemyWindow()
: WindowBase("openmw_alchemy_window.layout")
, mModel(nullptr)
, mSortModel(nullptr)
, mAlchemy(new MWMechanics::Alchemy())
, mApparatus (4)
@ -50,6 +53,8 @@ namespace MWGui
getWidget(mDecreaseButton, "DecreaseButton");
getWidget(mNameEdit, "NameEdit");
getWidget(mItemView, "ItemView");
getWidget(mFilterValue, "FilterValue");
getWidget(mFilterType, "FilterType");
mBrewCountEdit->eventValueChanged += MyGUI::newDelegate(this, &AlchemyWindow::onCountValueChanged);
mBrewCountEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);
@ -72,6 +77,9 @@ namespace MWGui
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked);
mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);
mFilterValue->eventComboChangePosition += MyGUI::newDelegate(this, &AlchemyWindow::onFilterChanged);
mFilterValue->eventEditTextChange += MyGUI::newDelegate(this, &AlchemyWindow::onFilterEdited);
mFilterType->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::switchFilterType);
center();
}
@ -136,16 +144,110 @@ namespace MWGui
removeIngredient(mIngredients[i]);
}
updateFilters();
update();
}
void AlchemyWindow::initFilter()
{
auto const& wm = MWBase::Environment::get().getWindowManager();
auto const ingredient = wm->getGameSettingString("sIngredients", "Ingredients");
auto const effect = wm->getGameSettingString("sMagicEffects", "Magic Effects");
if (mFilterType->getCaption() == ingredient)
mCurrentFilter = FilterType::ByName;
else
mCurrentFilter = FilterType::ByEffect;
updateFilters();
mFilterValue->clearIndexSelected();
updateFilters();
}
void AlchemyWindow::switchFilterType(MyGUI::Widget* _sender)
{
auto const& wm = MWBase::Environment::get().getWindowManager();
auto const ingredient = wm->getGameSettingString("sIngredients", "Ingredients");
auto const effect = wm->getGameSettingString("sMagicEffects", "Magic Effects");
auto *button = _sender->castType<MyGUI::Button>();
if (button->getCaption() == ingredient)
{
button->setCaption(effect);
mCurrentFilter = FilterType::ByEffect;
}
else
{
button->setCaption(ingredient);
mCurrentFilter = FilterType::ByName;
}
mSortModel->setNameFilter({});
mSortModel->setEffectFilter({});
mFilterValue->clearIndexSelected();
updateFilters();
mItemView->update();
}
void AlchemyWindow::updateFilters()
{
std::set<std::string> itemNames, itemEffects;
for (size_t i = 0; i < mModel->getItemCount(); ++i)
{
auto const& base = mModel->getItem(i).mBase;
if (base.getTypeName() != typeid(ESM::Ingredient).name())
continue;
itemNames.insert(base.getClass().getName(base));
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
auto const alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
auto const effects = MWMechanics::Alchemy::effectsDescription(base, alchemySkill);
itemEffects.insert(effects.begin(), effects.end());
}
mFilterValue->removeAllItems();
auto const addItems = [&](auto const& container)
{
for (auto const& item : container)
mFilterValue->addItem(item);
};
switch (mCurrentFilter)
{
case FilterType::ByName: addItems(itemNames); break;
case FilterType::ByEffect: addItems(itemEffects); break;
}
}
void AlchemyWindow::applyFilter(const std::string& filter)
{
switch (mCurrentFilter)
{
case FilterType::ByName: mSortModel->setNameFilter(filter); break;
case FilterType::ByEffect: mSortModel->setEffectFilter(filter); break;
}
mItemView->update();
}
void AlchemyWindow::onFilterChanged(MyGUI::ComboBox* _sender, size_t _index)
{
// ignore spurious event fired when one edit the content after selection.
// onFilterEdited will handle it.
if (_index != MyGUI::ITEM_NONE)
applyFilter(_sender->getItemNameAt(_index));
}
void AlchemyWindow::onFilterEdited(MyGUI::EditBox* _sender)
{
applyFilter(_sender->getCaption());
}
void AlchemyWindow::onOpen()
{
mAlchemy->clear();
mAlchemy->setAlchemist (MWMechanics::getPlayer());
InventoryItemModel* model = new InventoryItemModel(MWMechanics::getPlayer());
mSortModel = new SortFilterItemModel(model);
mModel = new InventoryItemModel(MWMechanics::getPlayer());
mSortModel = new SortFilterItemModel(mModel);
mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients);
mItemView->setModel (mSortModel);
mItemView->resetScrollBars();
@ -167,6 +269,7 @@ namespace MWGui
}
update();
initFilter();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit);
}

@ -5,7 +5,9 @@
#include <vector>
#include <MyGUI_ControllerItem.h>
#include <MyGUI_ComboBox.h>
#include <components/widgets/box.hpp>
#include <components/widgets/numericeditbox.hpp>
#include "windowbase.hpp"
@ -19,6 +21,7 @@ namespace MWGui
{
class ItemView;
class ItemWidget;
class InventoryItemModel;
class SortFilterItemModel;
class AlchemyWindow : public WindowBase
@ -36,8 +39,11 @@ namespace MWGui
static const float sCountChangeInterval; // in seconds
std::string mSuggestedPotionName;
enum class FilterType { ByName, ByEffect };
FilterType mCurrentFilter;
ItemView* mItemView;
InventoryItemModel* mModel;
SortFilterItemModel* mSortModel;
MyGUI::Button* mCreateButton;
@ -47,6 +53,8 @@ namespace MWGui
MyGUI::Button* mIncreaseButton;
MyGUI::Button* mDecreaseButton;
Gui::AutoSizedButton* mFilterType;
MyGUI::ComboBox* mFilterValue;
MyGUI::EditBox* mNameEdit;
Gui::NumericEditBox* mBrewCountEdit;
@ -60,6 +68,13 @@ namespace MWGui
void onCountValueChanged(int value);
void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller);
void applyFilter(const std::string& filter);
void initFilter();
void onFilterChanged(MyGUI::ComboBox* _sender, size_t _index);
void onFilterEdited(MyGUI::EditBox* _sender);
void switchFilterType(MyGUI::Widget* _sender);
void updateFilters();
void addRepeatController(MyGUI::Widget* widget);
void onIncreaseButtonTriggered();

@ -9,7 +9,7 @@ namespace MWGui
/**
* @brief A variant of MyGUI::ImageBox with aspect ratio correction using black bars
*/
class BackgroundImage : public MyGUI::ImageBox
class BackgroundImage final : public MyGUI::ImageBox
{
MYGUI_RTTI_DERIVED(BackgroundImage)
@ -22,8 +22,8 @@ namespace MWGui
*/
void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool stretch=true);
virtual void setSize (const MyGUI::IntSize &_value);
virtual void setCoord (const MyGUI::IntCoord &_value);
void setSize (const MyGUI::IntSize &_value) final;
void setCoord (const MyGUI::IntCoord &_value) final;
private:
MyGUI::ImageBox* mChild;

@ -817,7 +817,7 @@ namespace
};
}
class PageDisplay : public MyGUI::ISubWidgetText
class PageDisplay final : public MyGUI::ISubWidgetText
{
MYGUI_RTTI_DERIVED(PageDisplay)
protected:
@ -1140,7 +1140,7 @@ public:
i->second->createDrawItem (mNode);
}
void setVisible (bool newVisible)
void setVisible (bool newVisible) final
{
if (mVisible == newVisible)
return;
@ -1162,7 +1162,7 @@ public:
}
}
void createDrawItem(MyGUI::ITexture* texture, MyGUI::ILayerNode* node)
void createDrawItem(MyGUI::ITexture* texture, MyGUI::ILayerNode* node) final
{
mNode = node;
@ -1230,9 +1230,9 @@ public:
// ISubWidget should not necessarily be a drawitem
// in this case, it is not...
void doRender() { }
void doRender() final { }
void _updateView ()
void _updateView () final
{
_checkMargin();
@ -1241,7 +1241,7 @@ public:
mNode->outOfDate (i->second->mRenderItem);
}
void _correctView()
void _correctView() final
{
_checkMargin ();
@ -1251,7 +1251,7 @@ public:
}
void destroyDrawItem()
void destroyDrawItem() final
{
for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i)
i->second->destroyDrawItem (mNode);
@ -1261,7 +1261,7 @@ public:
};
class BookPageImpl : public BookPage
class BookPageImpl final : public BookPage
{
MYGUI_RTTI_DERIVED(BookPage)
public:
@ -1271,24 +1271,24 @@ public:
{
}
void showPage (TypesetBook::Ptr book, size_t page)
void showPage (TypesetBook::Ptr book, size_t page) final
{
mPageDisplay->showPage (book, page);
}
void adviseLinkClicked (std::function <void (InteractiveId)> linkClicked)
void adviseLinkClicked (std::function <void (InteractiveId)> linkClicked) final
{
mPageDisplay->mLinkClicked = linkClicked;
}
void unadviseLinkClicked ()
void unadviseLinkClicked () final
{
mPageDisplay->mLinkClicked = std::function <void (InteractiveId)> ();
}
protected:
virtual void initialiseOverride()
void initialiseOverride() final
{
Base::initialiseOverride();
@ -1302,24 +1302,24 @@ protected:
}
}
void onMouseLostFocus(Widget* _new)
void onMouseLostFocus(Widget* _new) final
{
// NOTE: MyGUI also fires eventMouseLostFocus for widgets that are about to be destroyed (if they had focus).
// Child widgets may already be destroyed! So be careful.
mPageDisplay->onMouseLostFocus ();
}
void onMouseMove(int left, int top)
void onMouseMove(int left, int top) final
{
mPageDisplay->onMouseMove (left, top);
}
void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id)
void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) final
{
mPageDisplay->onMouseButtonPressed (left, top, id);
}
void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id)
void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) final
{
mPageDisplay->onMouseButtonReleased (left, top, id);
}

@ -46,9 +46,11 @@ CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* ma
getWidget(mCloseButton, "CloseButton");
getWidget(mProfitLabel, "ProfitLabel");
getWidget(mEncumbranceBar, "EncumbranceBar");
getWidget(mFilterEdit, "FilterEdit");
getWidget(mItemView, "ItemView");
mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &CompanionWindow::onBackgroundSelected);
mItemView->eventItemClicked += MyGUI::newDelegate(this, &CompanionWindow::onItemSelected);
mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &CompanionWindow::onNameFilterChanged);
mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CompanionWindow::onCloseButtonClicked);
@ -92,6 +94,12 @@ void CompanionWindow::onItemSelected(int index)
dragItem (nullptr, count);
}
void CompanionWindow::onNameFilterChanged(MyGUI::EditBox* _sender)
{
mSortModel->setNameFilter(_sender->getCaption());
mItemView->update();
}
void CompanionWindow::dragItem(MyGUI::Widget* sender, int count)
{
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
@ -113,6 +121,7 @@ void CompanionWindow::setPtr(const MWWorld::Ptr& npc)
mModel = new CompanionItemModel(npc);
mSortModel = new SortFilterItemModel(mModel);
mFilterEdit->setCaption(std::string());
mItemView->setModel(mSortModel);
mItemView->resetScrollBars();

@ -39,11 +39,13 @@ namespace MWGui
DragAndDrop* mDragAndDrop;
MyGUI::Button* mCloseButton;
MyGUI::EditBox* mFilterEdit;
MyGUI::TextBox* mProfitLabel;
Widgets::MWDynamicStat* mEncumbranceBar;
MessageBoxManager* mMessageBoxManager;
void onItemSelected(int index);
void onNameFilterChanged(MyGUI::EditBox* _sender);
void onBackgroundSelected();
void dragItem(MyGUI::Widget* sender, int count);

@ -14,14 +14,14 @@ namespace MWGui
namespace Controllers
{
/// Automatically positions a widget below the mouse cursor.
class ControllerFollowMouse :
class ControllerFollowMouse final :
public MyGUI::ControllerItem
{
MYGUI_RTTI_DERIVED( ControllerFollowMouse )
private:
bool addTime(MyGUI::Widget* _widget, float _time);
void prepareItem(MyGUI::Widget* _widget);
bool addTime(MyGUI::Widget* _widget, float _time) final;
void prepareItem(MyGUI::Widget* _widget) final;
};
}
}

@ -11,7 +11,7 @@ namespace MWGui
/// ResourceImageSetPointer that we need.
/// \example MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
/// MyGUI::ResourceManager::getInstance().load("core.xml");
class ResourceImageSetPointerFix :
class ResourceImageSetPointerFix final :
public MyGUI::IPointer
{
MYGUI_RTTI_DERIVED( ResourceImageSetPointerFix )
@ -20,17 +20,17 @@ namespace MWGui
ResourceImageSetPointerFix();
virtual ~ResourceImageSetPointerFix();
virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version);
void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) final;
virtual void setImage(MyGUI::ImageBox* _image);
virtual void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point);
void setImage(MyGUI::ImageBox* _image) final;
void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point) final;
//and now for the whole point of this class, allow us to get
//the hot spot, the image and the size of the cursor.
virtual MyGUI::ResourceImageSetPtr getImageSet();
virtual MyGUI::IntPoint getHotSpot();
virtual MyGUI::IntSize getSize();
virtual int getRotation();
MyGUI::ResourceImageSetPtr getImageSet();
MyGUI::IntPoint getHotSpot();
MyGUI::IntSize getSize();
int getRotation();
private:
MyGUI::IntPoint mPoint;

@ -8,6 +8,7 @@
#include <MyGUI_RenderManager.h>
#include <MyGUI_InputManager.h>
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
#include <osg/Texture2D>
@ -21,12 +22,10 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/actionequip.hpp"
#include "../mwscript/interpretercontext.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/creaturestats.hpp"
@ -90,6 +89,7 @@ namespace MWGui
getWidget(mLeftPane, "LeftPane");
getWidget(mRightPane, "RightPane");
getWidget(mArmorRating, "ArmorRating");
getWidget(mFilterEdit, "FilterEdit");
mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked);
mAvatarImage->setRenderItemTexture(mPreviewTexture.get());
@ -104,6 +104,7 @@ namespace MWGui
mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
mFilterMagic->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
mFilterMisc->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &InventoryWindow::onNameFilterChanged);
mFilterAll->setStateSelected(true);
@ -133,6 +134,8 @@ namespace MWGui
else
mSortModel = new SortFilterItemModel(mTradeModel);
mSortModel->setNameFilter(mFilterEdit->getCaption());
mItemView->setModel(mSortModel);
mFilterAll->setStateSelected(true);
@ -388,6 +391,11 @@ namespace MWGui
void InventoryWindow::onOpen()
{
// Reset the filter focus when opening the window
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (focus == mFilterEdit)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr);
if (!mPtr.isEmpty())
{
updateEncumbranceBar();
@ -465,6 +473,12 @@ namespace MWGui
width*mScaleFactor/float(mPreview->getTextureWidth()), height*mScaleFactor/float(mPreview->getTextureHeight())));
}
void InventoryWindow::onNameFilterChanged(MyGUI::EditBox* _sender)
{
mSortModel->setNameFilter(_sender->getCaption());
mItemView->update();
}
void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender)
{
if (_sender == mFilterAll)
@ -477,7 +491,6 @@ namespace MWGui
mSortModel->setCategory(SortFilterItemModel::Category_Magic);
else if (_sender == mFilterMisc)
mSortModel->setCategory(SortFilterItemModel::Category_Misc);
mFilterAll->setStateSelected(false);
mFilterWeapon->setStateSelected(false);
mFilterApparel->setStateSelected(false);
@ -507,6 +520,16 @@ namespace MWGui
void InventoryWindow::useItem(const MWWorld::Ptr &ptr, bool force)
{
const std::string& script = ptr.getClass().getScript(ptr);
if (!script.empty())
{
// Don't try to equip the item if PCSkipEquip is set to 1
if (ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1)
{
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
return;
}
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 0);
}
MWWorld::Ptr player = MWMechanics::getPlayer();
@ -526,43 +549,28 @@ namespace MWGui
if (canEquip.first == 0)
{
/// If PCSkipEquip is set, set OnPCEquip to 1 and don't message anything
if (!script.empty() && ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1)
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
else
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
updateItemView();
return;
}
}
}
// If the item has a script, set its OnPcEquip to 1
if (!script.empty()
// Another morrowind oddity: when an item has skipped equipping and pcskipequip is reset to 0 afterwards,
// the next time it is equipped will work normally, but will not set onpcequip
&& (ptr != mSkippedToEquip || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1))
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
// Give the script a chance to run once before we do anything else
// this is important when setting pcskipequip as a reaction to onpcequip being set (bk_treasuryreport does this)
if (!force && !script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled())
// If the item has a script, set OnPCEquip or PCSkipEquip to 1
if (!script.empty())
{
MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr);
MWBase::Environment::get().getScriptManager()->run (script, interpreterContext);
// Ingredients, books and repair hammers must not have OnPCEquip set to 1 here
const std::string& type = ptr.getTypeName();
bool isBook = type == typeid(ESM::Book).name();
if (!isBook && type != typeid(ESM::Ingredient).name() && type != typeid(ESM::Repair).name())
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
// Books must have PCSkipEquip set to 1 instead
else if (isBook)
ptr.getRefData().getLocals().setVarByInt(script, "pcskipequip", 1);
}
mSkippedToEquip = MWWorld::Ptr();
if (ptr.getRefData().getCount()) // make sure the item is still there, the script might have removed it
{
if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0)
{
std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);
action->execute(player);
}
else
mSkippedToEquip = ptr;
}
std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);
action->execute(player);
if (isVisible())
{

@ -92,8 +92,8 @@ namespace MWGui
MyGUI::Button* mFilterApparel;
MyGUI::Button* mFilterMagic;
MyGUI::Button* mFilterMisc;
MWWorld::Ptr mSkippedToEquip;
MyGUI::EditBox* mFilterEdit;
GuiMode mGuiMode;
@ -121,6 +121,7 @@ namespace MWGui
void onWindowResize(MyGUI::Window* _sender);
void onFilterChanged(MyGUI::Widget* _sender);
void onNameFilterChanged(MyGUI::EditBox* _sender);
void onAvatarClicked(MyGUI::Widget* _sender);
void onPinToggled();

@ -21,7 +21,7 @@ namespace MWGui
class ItemModel;
class ItemWidget;
class ItemChargeView : public MyGUI::Widget
class ItemChargeView final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED(ItemChargeView)
public:
@ -36,7 +36,7 @@ namespace MWGui
/// Register needed components with MyGUI's factory manager
static void registerComponents();
virtual void initialiseOverride();
void initialiseOverride() final;
/// Takes ownership of \a model
void setModel(ItemModel* model);
@ -47,8 +47,8 @@ namespace MWGui
void layoutWidgets();
void resetScrollbars();
virtual void setSize(const MyGUI::IntSize& value);
virtual void setCoord(const MyGUI::IntCoord& value);
void setSize(const MyGUI::IntSize& value) final;
void setCoord(const MyGUI::IntCoord& value) final;
MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, const MWWorld::Ptr&> eventItemClicked;

@ -8,7 +8,7 @@
namespace MWGui
{
class ItemView : public MyGUI::Widget
class ItemView final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED(ItemView)
public:
@ -33,12 +33,12 @@ namespace MWGui
void resetScrollBars();
private:
virtual void initialiseOverride();
void initialiseOverride() final;
void layoutWidgets();
virtual void setSize(const MyGUI::IntSize& _value);
virtual void setCoord(const MyGUI::IntCoord& _value);
void setSize(const MyGUI::IntSize& _value) final;
void setCoord(const MyGUI::IntCoord& _value) final;
void onSelectedItem (MyGUI::Widget* sender);
void onSelectedBackground (MyGUI::Widget* sender);

@ -41,7 +41,7 @@ namespace MWGui
void setFrame (const std::string& frame, const MyGUI::IntCoord& coord);
protected:
virtual void initialiseOverride();
void initialiseOverride() final;
MyGUI::ImageBox* mItem;
MyGUI::ImageBox* mItemShadow;

@ -16,9 +16,6 @@ namespace MWGui
bool shouldAcceptKeyFocus(MyGUI::Widget* w)
{
if (w && w->getUserString("IgnoreTabKey") == "y")
return false;
return w && !w->castType<MyGUI::Window>(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled();
}

@ -14,14 +14,13 @@
#include <components/myguiplatform/myguitexture.hpp>
#include <components/settings/settings.hpp>
#include <components/vfs/manager.hpp>
#include <components/sceneutil/vismask.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwrender/vismask.hpp"
#include "backgroundimage.hpp"
namespace MWGui
@ -335,8 +334,8 @@ namespace MWGui
// Turn off rendering except the GUI
int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();
int oldCullMask = mViewer->getCamera()->getCullMask();
mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI|MWRender::Mask_PreCompile);
mViewer->getCamera()->setCullMask(MWRender::Mask_GUI|MWRender::Mask_PreCompile);
mViewer->getUpdateVisitor()->setTraversalMask(SceneUtil::Mask_GUI|SceneUtil::Mask_PreCompile);
mViewer->getCamera()->setCullMask(SceneUtil::Mask_GUI|SceneUtil::Mask_PreCompile);
MWBase::Environment::get().getInputManager()->update(0, true, true);

@ -54,7 +54,7 @@ namespace
/// @brief A widget that changes its color when hovered.
class MarkerWidget: public MyGUI::Widget
class MarkerWidget final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED(MarkerWidget)
@ -74,12 +74,12 @@ namespace
MyGUI::Colour mNormalColour;
MyGUI::Colour mHoverColour;
void onMouseLostFocus(MyGUI::Widget* _new)
void onMouseLostFocus(MyGUI::Widget* _new) final
{
setColour(mNormalColour);
}
void onMouseSetFocus(MyGUI::Widget* _old)
void onMouseSetFocus(MyGUI::Widget* _old) final
{
setColour(mHoverColour);
}

@ -5,12 +5,12 @@
namespace MWGui
{
class AutoSizedResourceSkin : public MyGUI::ResourceSkin
class AutoSizedResourceSkin final : public MyGUI::ResourceSkin
{
MYGUI_RTTI_DERIVED( AutoSizedResourceSkin )
public:
virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version);
void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) final;
};
}

@ -23,6 +23,8 @@
#include "../mwworld/nullaction.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/alchemy.hpp"
namespace
{
bool compareType(const std::string& type1, const std::string& type2)
@ -151,6 +153,8 @@ namespace MWGui
: mCategory(Category_All)
, mFilter(0)
, mSortByType(true)
, mNameFilter("")
, mEffectFilter("")
{
mSourceModel = sourceModel;
}
@ -199,8 +203,39 @@ namespace MWGui
if (!(category & mCategory))
return false;
if ((mFilter & Filter_OnlyIngredients) && base.getTypeName() != typeid(ESM::Ingredient).name())
return false;
if (mFilter & Filter_OnlyIngredients)
{
if (base.getTypeName() != typeid(ESM::Ingredient).name())
return false;
if (!mNameFilter.empty() && !mEffectFilter.empty())
throw std::logic_error("name and magic effect filter are mutually exclusive");
if (!mNameFilter.empty())
{
const auto itemName = Misc::StringUtils::lowerCase(base.getClass().getName(base));
return itemName.find(mNameFilter) != std::string::npos;
}
if (!mEffectFilter.empty())
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
const auto alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
const auto effects = MWMechanics::Alchemy::effectsDescription(base, alchemySkill);
for (const auto& effect : effects)
{
const auto ciEffect = Misc::StringUtils::lowerCase(effect);
if (ciEffect.find(mEffectFilter) != std::string::npos)
return true;
}
return false;
}
return true;
}
if ((mFilter & Filter_OnlyEnchanted) && !(item.mFlags & ItemStack::Flag_Enchanted))
return false;
if ((mFilter & Filter_OnlyChargedSoulstones) && (base.getTypeName() != typeid(ESM::Miscellaneous).name()
@ -250,6 +285,10 @@ namespace MWGui
return false;
}
std::string compare = Misc::StringUtils::lowerCase(item.mBase.getClass().getName(item.mBase));
if(compare.find(mNameFilter) == std::string::npos)
return false;
return true;
}
@ -277,6 +316,16 @@ namespace MWGui
mFilter = filter;
}
void SortFilterItemModel::setNameFilter (const std::string& filter)
{
mNameFilter = Misc::StringUtils::lowerCase(filter);
}
void SortFilterItemModel::setEffectFilter (const std::string& filter)
{
mEffectFilter = Misc::StringUtils::lowerCase(filter);
}
void SortFilterItemModel::update()
{
mSourceModel->update();

@ -25,6 +25,8 @@ namespace MWGui
void setCategory (int category);
void setFilter (int filter);
void setNameFilter (const std::string& filter);
void setEffectFilter (const std::string& filter);
/// Use ItemStack::Type for sorting?
void setSortByType(bool sort) { mSortByType = sort; }
@ -57,6 +59,9 @@ namespace MWGui
int mCategory;
int mFilter;
bool mSortByType;
std::string mNameFilter; // filter by item name
std::string mEffectFilter; // filter by magic effect
};
}

@ -19,7 +19,7 @@ namespace MWGui
class SpellModel;
///@brief Displays a SpellModel in a list widget
class SpellView : public MyGUI::Widget
class SpellView final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED(SpellView)
public:
@ -47,10 +47,10 @@ namespace MWGui
/// Fired when a spell was clicked
EventHandle_ModelIndex eventSpellClicked;
virtual void initialiseOverride();
void initialiseOverride() final;
virtual void setSize(const MyGUI::IntSize& _value);
virtual void setCoord(const MyGUI::IntCoord& _value);
void setSize(const MyGUI::IntSize& _value) final;
void setCoord(const MyGUI::IntCoord& _value) final;
void resetScrollbars();

@ -45,8 +45,6 @@ namespace MWGui
getWidget(mEffectBox, "EffectsBox");
getWidget(mFilterEdit, "FilterEdit");
mFilterEdit->setUserString("IgnoreTabKey", "y");
mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected);
mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &SpellWindow::onFilterChanged);
deleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onDeleteClicked);

@ -69,6 +69,7 @@ namespace MWGui
getWidget(mTotalBalance, "TotalBalance");
getWidget(mTotalBalanceLabel, "TotalBalanceLabel");
getWidget(mBottomPane, "BottomPane");
getWidget(mFilterEdit, "FilterEdit");
getWidget(mItemView, "ItemView");
mItemView->eventItemClicked += MyGUI::newDelegate(this, &TradeWindow::onItemSelected);
@ -80,6 +81,7 @@ namespace MWGui
mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged);
mFilterMagic->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged);
mFilterMisc->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged);
mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &TradeWindow::onNameFilterChanged);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onCancelButtonClicked);
mOfferButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onOfferButtonClicked);
@ -135,8 +137,7 @@ namespace MWGui
setTitle(actor.getClass().getName(actor));
onFilterChanged(mFilterAll);
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTotalBalance);
mFilterEdit->setCaption("");
}
void TradeWindow::onFrame(float dt)
@ -144,6 +145,12 @@ namespace MWGui
checkReferenceAvailable();
}
void TradeWindow::onNameFilterChanged(MyGUI::EditBox* _sender)
{
mSortModel->setNameFilter(_sender->getCaption());
mItemView->update();
}
void TradeWindow::onFilterChanged(MyGUI::Widget* _sender)
{
if (_sender == mFilterAll)

@ -59,6 +59,8 @@ namespace MWGui
MyGUI::Button* mFilterMagic;
MyGUI::Button* mFilterMisc;
MyGUI::EditBox* mFilterEdit;
MyGUI::Button* mIncreaseButton;
MyGUI::Button* mDecreaseButton;
MyGUI::TextBox* mTotalBalanceLabel;
@ -86,6 +88,7 @@ namespace MWGui
void sellItem (MyGUI::Widget* sender, int count);
void onFilterChanged(MyGUI::Widget* _sender);
void onNameFilterChanged(MyGUI::EditBox* _sender);
void onOfferButtonClicked(MyGUI::Widget* _sender);
void onAccept(MyGUI::EditBox* sender);
void onCancelButtonClicked(MyGUI::Widget* _sender);

@ -227,9 +227,9 @@ namespace MWGui
void WaitDialog::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character)
{
if (key == MyGUI::KeyCode::ArrowDown)
if (key == MyGUI::KeyCode::ArrowUp)
mHourSlider->setScrollPosition(std::min(mHourSlider->getScrollPosition()+1, mHourSlider->getScrollRange()-1));
else if (key == MyGUI::KeyCode::ArrowUp)
else if (key == MyGUI::KeyCode::ArrowDown)
mHourSlider->setScrollPosition(std::max(static_cast<int>(mHourSlider->getScrollPosition())-1, 0));
else
return;

@ -90,7 +90,7 @@ namespace MWGui
typedef std::vector<SpellEffectParams> SpellEffectList;
class MWSkill : public MyGUI::Widget
class MWSkill final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED( MWSkill )
public:
@ -116,7 +116,7 @@ namespace MWGui
protected:
virtual ~MWSkill();
virtual void initialiseOverride();
void initialiseOverride() final;
void onClicked(MyGUI::Widget* _sender);
@ -131,7 +131,7 @@ namespace MWGui
};
typedef MWSkill* MWSkillPtr;
class MWAttribute : public MyGUI::Widget
class MWAttribute final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED( MWAttribute )
public:
@ -156,7 +156,7 @@ namespace MWGui
protected:
virtual ~MWAttribute();
virtual void initialiseOverride();
void initialiseOverride() final;
void onClicked(MyGUI::Widget* _sender);
@ -175,7 +175,7 @@ namespace MWGui
* @todo remove this class and use MWEffectList instead
*/
class MWSpellEffect;
class MWSpell : public MyGUI::Widget
class MWSpell final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED( MWSpell )
public:
@ -199,7 +199,7 @@ namespace MWGui
protected:
virtual ~MWSpell();
virtual void initialiseOverride();
void initialiseOverride() final;
private:
void updateWidgets();
@ -209,7 +209,7 @@ namespace MWGui
};
typedef MWSpell* MWSpellPtr;
class MWEffectList : public MyGUI::Widget
class MWEffectList final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED( MWEffectList )
public:
@ -241,7 +241,7 @@ namespace MWGui
protected:
virtual ~MWEffectList();
virtual void initialiseOverride();
void initialiseOverride() final;
private:
void updateWidgets();
@ -250,7 +250,7 @@ namespace MWGui
};
typedef MWEffectList* MWEffectListPtr;
class MWSpellEffect : public MyGUI::Widget
class MWSpellEffect final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED( MWSpellEffect )
public:
@ -265,7 +265,7 @@ namespace MWGui
protected:
virtual ~MWSpellEffect();
virtual void initialiseOverride();
void initialiseOverride() final;
private:
static const int sIconOffset = 24;
@ -279,7 +279,7 @@ namespace MWGui
};
typedef MWSpellEffect* MWSpellEffectPtr;
class MWDynamicStat : public MyGUI::Widget
class MWDynamicStat final : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED( MWDynamicStat )
public:
@ -294,7 +294,7 @@ namespace MWGui
protected:
virtual ~MWDynamicStat();
virtual void initialiseOverride();
void initialiseOverride() final;
private:

@ -30,6 +30,7 @@
#include <components/resource/resourcesystem.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/sceneutil/vismask.hpp>
#include <components/sceneutil/workqueue.hpp>
#include <components/translation/translation.hpp>
@ -50,8 +51,6 @@
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwrender/vismask.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/cellstore.hpp"
@ -1905,8 +1904,8 @@ namespace MWGui
// Turn off all rendering except for the GUI
int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();
int oldCullMask = mViewer->getCamera()->getCullMask();
mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI);
mViewer->getCamera()->setCullMask(MWRender::Mask_GUI);
mViewer->getUpdateVisitor()->setTraversalMask(SceneUtil::Mask_GUI);
mViewer->getCamera()->setCullMask(SceneUtil::Mask_GUI);
MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize();
sizeVideo(screenSize.width, screenSize.height);

@ -4,6 +4,7 @@
#include <components/esm/esmwriter.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/vismask.hpp>
#include <components/debug/debuglog.hpp>
#include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
@ -24,8 +25,6 @@
#include "../mwmechanics/aibreathe.hpp"
#include "../mwrender/vismask.hpp"
#include "spellcasting.hpp"
#include "steering.hpp"
#include "npcstats.hpp"
@ -452,22 +451,12 @@ namespace MWMechanics
return;
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
int hello = stats.getAiSetting(CreatureStats::AI_Hello).getModified();
if (hello == 0)
return;
if (MWBase::Environment::get().getWorld()->isSwimming(actor))
return;
MWWorld::Ptr player = getPlayer();
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
osg::Vec3f dir = playerPos - actorPos;
const MWMechanics::AiSequence& seq = stats.getAiSequence();
int packageId = seq.getTypeId();
if (seq.isInCombat() || (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1))
if (seq.isInCombat() ||
MWBase::Environment::get().getWorld()->isSwimming(actor) ||
(packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1))
{
stats.setTurningToPlayer(false);
stats.setGreetingTimer(0);
@ -475,6 +464,11 @@ namespace MWMechanics
return;
}
MWWorld::Ptr player = getPlayer();
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
osg::Vec3f dir = playerPos - actorPos;
if (stats.isTurningToPlayer())
{
// Reduce the turning animation glitch by using a *HUGE* value of
@ -492,11 +486,10 @@ namespace MWMechanics
return;
// Play a random voice greeting if the player gets too close
float helloDistance = static_cast<float>(hello);
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
helloDistance *= iGreetDistanceMultiplier;
float helloDistance = static_cast<float>(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier);
int greetingTimer = stats.getGreetingTimer();
GreetingState greetingState = stats.getGreetingState();
@ -1241,6 +1234,11 @@ namespace MWMechanics
if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name())
inventoryStore.unequipItem(*heldIter, ptr);
}
else if (heldIter == inventoryStore.end() || heldIter->getTypeName() == typeid(ESM::Light).name())
{
// For hostile NPCs, see if they have anything better to equip first
inventoryStore.autoEquip(ptr);
}
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
@ -1420,11 +1418,11 @@ namespace MWMechanics
const float dist = (player.getRefData().getPosition().asVec3() - ptr.getRefData().getPosition().asVec3()).length();
if (dist > mActorsProcessingRange)
{
ptr.getRefData().getBaseNode()->setNodeMask(0);
ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Disabled);
return;
}
else
ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor);
ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor);
// Fade away actors on large distance (>90% of actor's processing distance)
float visibilityRatio = 1.0;
@ -1748,12 +1746,12 @@ namespace MWMechanics
if (!inRange)
{
iter->first.getRefData().getBaseNode()->setNodeMask(0);
iter->first.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Disabled);
world->setActorCollisionMode(iter->first, false, false);
continue;
}
else if (!isPlayer)
iter->first.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor);
iter->first.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor);
const bool isDead = iter->first.getClass().getCreatureStats(iter->first).isDead();
if (!isDead && iter->first.getClass().getCreatureStats(iter->first).isParalyzed())
@ -1852,6 +1850,8 @@ namespace MWMechanics
stats.getActiveSpells().visitEffectSources(soulTrap);
}
// Magic effects will be reset later, and the magic effect that could kill the actor
// needs to be determined now
calculateCreatureStatModifiers(iter->first, 0);
if (cls.isEssential(iter->first))
@ -1869,7 +1869,10 @@ namespace MWMechanics
// Make sure spell effects are removed
purgeSpellEffects(stats.getActorId());
// Reset dynamic stats, attributes and skills
calculateCreatureStatModifiers(iter->first, 0);
if (iter->first.getClass().isNpc())
calculateNpcStatModifiers(iter->first, 0);
if( iter->first == getPlayer())
{

@ -44,7 +44,7 @@ namespace MWMechanics
void fastForward(const MWWorld::Ptr& actor, AiState& state);
virtual osg::Vec3f getDestination() { return osg::Vec3f(mX, mY, mZ); }
virtual osg::Vec3f getDestination() const { return osg::Vec3f(mX, mY, mZ); }
private:
std::string mCellId;

@ -76,7 +76,7 @@ namespace MWMechanics
void fastForward(const MWWorld::Ptr& actor, AiState& state);
virtual osg::Vec3f getDestination()
virtual osg::Vec3f getDestination() const
{
MWWorld::Ptr target = getTarget();
if (target.isEmpty())

@ -102,7 +102,7 @@ namespace MWMechanics
/// Return true if this package should repeat. Currently only used for Wander packages.
virtual bool getRepeat() const;
virtual osg::Vec3f getDestination() { return osg::Vec3f(0, 0, 0); }
virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); }
// Return true if any loaded actor with this AI package must be active.
virtual bool alwaysActive() const { return false; }

@ -28,6 +28,10 @@ void AiSequence::copy (const AiSequence& sequence)
for (std::list<AiPackage *>::const_iterator iter (sequence.mPackages.begin());
iter!=sequence.mPackages.end(); ++iter)
mPackages.push_back ((*iter)->clone());
// We need to keep an AiWander storage, if present - it has a state machine.
// Not sure about another temporary storages
sequence.mAiState.copy<AiWanderStorage>(mAiState);
}
AiSequence::AiSequence() : mDone (false), mRepeat(false), mLastAiPackage(-1) {}

@ -38,6 +38,14 @@ namespace MWMechanics
//return a reference to the (new allocated) object
return *result;
}
template< class Derived >
void copy(DerivedClassStorage& destination) const
{
Derived* result = dynamic_cast<Derived*>(mStorage);
if (result != nullptr)
destination.store<Derived>(*result);
}
template< class Derived >
void store( const Derived& payload )

@ -54,8 +54,9 @@ namespace MWMechanics
stats.setMovementFlag(CreatureStats::Flag_Run, false);
stats.setDrawState(DrawState_Nothing);
// Note: we should cancel internal "return after combat" package, if original location is too far away
if (!isWithinMaxRange(targetPos, actorPos))
return false;
return mHidden;
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor.
// If we got close to target, check for actors nearby. If they are, finish AI package.

@ -36,7 +36,7 @@ namespace MWMechanics
virtual bool alwaysActive() const { return true; }
virtual osg::Vec3f getDestination() { return osg::Vec3f(mX, mY, mZ); }
virtual osg::Vec3f getDestination() const { return osg::Vec3f(mX, mY, mZ); }
private:
float mX;

@ -61,6 +61,34 @@ namespace MWMechanics
rotation.makeRotate(randomDirection, osg::Vec3f(0.0, 0.0, 1.0));
return position + osg::Vec3f(distance, 0.0, 0.0) * rotation;
}
bool isDestinationHidden(const MWWorld::ConstPtr &actor, const osg::Vec3f& destination)
{
const auto position = actor.getRefData().getPosition().asVec3();
const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
osg::Vec3f direction = destination - position;
direction.normalize();
const auto visibleDestination = (
isWaterCreature || isFlyingCreature
? destination
: destination + osg::Vec3f(0, 0, halfExtents.z())
) + direction * std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
const int mask = MWPhysics::CollisionType_World
| MWPhysics::CollisionType_HeightMap
| MWPhysics::CollisionType_Door
| MWPhysics::CollisionType_Actor;
return MWBase::Environment::get().getWorld()->castRay(position, visibleDestination, mask, actor);
}
bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr &actor, const osg::Vec3f& destination)
{
const auto world = MWBase::Environment::get().getWorld();
const osg::Vec3f halfExtents = world->getPathfindingHalfExtents(actor);
const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor);
}
}
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
@ -145,15 +173,6 @@ namespace MWMechanics
// get or create temporary storage
AiWanderStorage& storage = state.get<AiWanderStorage>();
const MWWorld::CellStore*& currentCell = storage.mCell;
bool cellChange = currentCell && (actor.getCell() != currentCell);
if(!currentCell || cellChange)
{
stopWalking(actor, storage);
currentCell = actor.getCell();
storage.mPopulateAvailableNodes = true;
mStoredInitialActorPosition = false;
}
mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600);
@ -200,14 +219,13 @@ namespace MWMechanics
if (AI_REACTION_TIME <= lastReaction)
{
lastReaction = 0;
return reactionTimeActions(actor, storage, currentCell, cellChange, pos);
return reactionTimeActions(actor, storage, pos);
}
else
return false;
}
bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage,
const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos)
bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos)
{
if (mDistance <= 0)
storage.mCanWanderAlongPathGrid = false;
@ -229,7 +247,7 @@ namespace MWMechanics
// Initialization to discover & store allowed node points for this actor.
if (storage.mPopulateAvailableNodes)
{
getAllowedNodes(actor, currentCell->getCell(), storage);
getAllowedNodes(actor, actor.getCell()->getCell(), storage);
}
if (canActorMoveByZAxis(actor) && mDistance > 0) {
@ -258,10 +276,6 @@ namespace MWMechanics
completeManualWalking(actor, storage);
}
// Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles.
if(mDistance && cellChange)
mDistance = 0;
AiWanderStorage::WanderState& wanderState = storage.mState;
if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
{
@ -279,6 +293,11 @@ namespace MWMechanics
completeManualWalking(actor, storage);
}
if (wanderState == AiWanderStorage::Wander_Walking
&& (isDestinationHidden(actor, mPathFinder.getPath().back())
|| isAreaOccupiedByOtherActor(actor, mPathFinder.getPath().back())))
completeManualWalking(actor, storage);
return false; // AiWander package not yet completed
}
@ -330,7 +349,7 @@ namespace MWMechanics
if (!isWaterCreature && !isFlyingCreature)
{
// findRandomPointAroundCircle uses wanderDistance as limit for random and not as exact distance
if (const auto destination = navigator->findRandomPointAroundCircle(halfExtents, currentPosition, wanderDistance, navigatorFlags))
if (const auto destination = navigator->findRandomPointAroundCircle(halfExtents, mInitialActorPosition, wanderDistance, navigatorFlags))
mDestination = *destination;
else
mDestination = getRandomPointAround(mInitialActorPosition, wanderRadius);
@ -342,7 +361,10 @@ namespace MWMechanics
if (!isWaterCreature && destinationIsAtWater(actor, mDestination))
continue;
if ((isWaterCreature || isFlyingCreature) && destinationThroughGround(currentPosition, mDestination))
if (isDestinationHidden(actor, mDestination))
continue;
if (isAreaOccupiedByOtherActor(actor, mDestination))
continue;
if (isWaterCreature || isFlyingCreature)
@ -371,16 +393,6 @@ namespace MWMechanics
return MWBase::Environment::get().getWorld()->isUnderwater(actor.getCell(), positionBelowSurface);
}
/*
* Returns true if the start to end point travels through a collision point (land).
*/
bool AiWander::destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination) {
const int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door;
return MWBase::Environment::get().getWorld()->castRay(startPoint.x(), startPoint.y(), startPoint.z(),
destination.x(), destination.y(), destination.z(),
mask);
}
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
stopWalking(actor, storage);
mObstacleCheck.clear();
@ -529,7 +541,7 @@ namespace MWMechanics
unsigned int randNode = Misc::Rng::rollDice(storage.mAllowedNodes.size());
ESM::Pathgrid::Point dest(storage.mAllowedNodes[randNode]);
ToWorldCoordinates(dest, storage.mCell->getCell());
ToWorldCoordinates(dest, actor.getCell()->getCell());
// actor position is already in world coordinates
const osg::Vec3f start = actorPos.asVec3();

@ -27,8 +27,6 @@ namespace MWMechanics
{
float mReaction; // update some actions infrequently
const MWWorld::CellStore* mCell; // for detecting cell change
// AiWander states
enum WanderState
{
@ -60,7 +58,6 @@ namespace MWMechanics
AiWanderStorage():
mReaction(0),
mCell(nullptr),
mState(Wander_ChooseAction),
mIsWanderingManually(false),
mCanWanderAlongPathGrid(true),
@ -100,6 +97,8 @@ namespace MWMechanics
virtual int getTypeId() const;
virtual bool useVariableSpeed() const { return true;}
virtual void writeState(ESM::AiSequence::AiSequence &sequence) const;
virtual void fastForward(const MWWorld::Ptr& actor, AiState& state);
@ -108,6 +107,14 @@ namespace MWMechanics
osg::Vec3f getDestination(const MWWorld::Ptr& actor) const;
virtual osg::Vec3f getDestination() const
{
if (!mHasDestination)
return osg::Vec3f(0, 0, 0);
return mDestination;
}
private:
// NOTE: mDistance and mDuration must be set already
void init();
@ -125,12 +132,10 @@ namespace MWMechanics
void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage);
bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage,
const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos);
bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos);
bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage);
void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance);
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
bool destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination);
void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage);
int mDistance; // how far the actor can wander from the spawn point
@ -141,7 +146,7 @@ namespace MWMechanics
bool mRepeat;
bool mStoredInitialActorPosition;
osg::Vec3f mInitialActorPosition;
osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell
bool mHasDestination;
osg::Vec3f mDestination;

@ -554,3 +554,37 @@ std::string MWMechanics::Alchemy::suggestPotionName()
return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
ESM::MagicEffect::effectIdToString(effectId))->mValue.getString();
}
std::vector<std::string> MWMechanics::Alchemy::effectsDescription (const MWWorld::ConstPtr &ptr, const int alchemySkill)
{
std::vector<std::string> effects;
const auto& item = ptr.get<ESM::Ingredient>()->mBase;
const auto& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
const static auto fWortChanceValue = gmst.find("fWortChanceValue")->mValue.getFloat();
const auto& data = item->mData;
for (auto i = 0; i < 4; ++i)
{
const auto effectID = data.mEffectID[i];
const auto skillID = data.mSkills[i];
const auto attributeID = data.mAttributes[i];
if (alchemySkill < fWortChanceValue * (i + 1))
break;
if (effectID != -1)
{
std::string effect = gmst.find(ESM::MagicEffect::effectIdToString(effectID))->mValue.getString();
if (skillID != -1)
effect += " " + gmst.find(ESM::Skill::sSkillNameIds[skillID])->mValue.getString();
else if (attributeID != -1)
effect += " " + gmst.find(ESM::Attribute::sGmstAttributeIds[attributeID])->mValue.getString();
effects.push_back(effect);
}
}
return effects;
}

@ -131,6 +131,8 @@ namespace MWMechanics
///< Try to create potions from the ingredients, place them in the inventory of the alchemist and
/// adjust the skills of the alchemist accordingly.
/// \param name must not be an empty string, or Result_NoName is returned
static std::vector<std::string> effectsDescription (const MWWorld::ConstPtr &ptr, const int alchemySKill);
};
}

@ -455,6 +455,11 @@ void MWMechanics::NpcStats::setTimeToStartDrowning(float time)
mTimeToStartDrowning=time;
}
void MWMechanics::NpcStats::writeState (ESM::CreatureStats& state) const
{
CreatureStats::writeState(state);
}
void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
{
for (std::map<std::string, int>::const_iterator iter (mFactionRank.begin());
@ -494,6 +499,10 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
state.mTimeToStartDrowning = mTimeToStartDrowning;
}
void MWMechanics::NpcStats::readState (const ESM::CreatureStats& state)
{
CreatureStats::readState(state);
}
void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
{

@ -127,8 +127,10 @@ namespace MWMechanics
/// @param time value from [0,20]
void setTimeToStartDrowning(float time);
void writeState (ESM::CreatureStats& state) const;
void writeState (ESM::NpcStats& state) const;
void readState (const ESM::CreatureStats& state);
void readState (const ESM::NpcStats& state);
};
}

@ -120,6 +120,7 @@ namespace MWMechanics
mWalkState = WalkState::Norm;
mStateDuration = 0;
mPrev = position;
mInitialDistance = (destination - position).length();
return;
}
@ -129,10 +130,11 @@ namespace MWMechanics
const float prevDistance = (destination - mPrev).length();
const float currentDistance = (destination - position).length();
const float movedDistance = prevDistance - currentDistance;
const float movedFromInitialDistance = mInitialDistance - currentDistance;
mPrev = position;
if (movedDistance >= distSameSpot)
if (movedDistance >= distSameSpot && movedFromInitialDistance >= distSameSpot)
{
mWalkState = WalkState::Norm;
mStateDuration = 0;
@ -143,6 +145,7 @@ namespace MWMechanics
{
mWalkState = WalkState::CheckStuck;
mStateDuration = duration;
mInitialDistance = (destination - position).length();
return;
}

@ -54,6 +54,7 @@ namespace MWMechanics
float mStateDuration;
int mEvadeDirectionIndex;
float mInitialDistance = 0;
void chooseEvasionDirection();
};

@ -314,7 +314,9 @@ namespace MWMechanics
{
mPath.clear();
buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath));
// If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path
if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)))
mPath.push_back(endPoint);
mConstructed = true;
}
@ -335,24 +337,27 @@ namespace MWMechanics
mConstructed = true;
}
void PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
bool PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
std::back_insert_iterator<std::deque<osg::Vec3f>> out)
{
try
{
const auto world = MWBase::Environment::get().getWorld();
const auto stepSize = getPathStepSize(actor);
const auto navigator = world->getNavigator();
navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out);
}
catch (const DetourNavigator::NavigatorException& exception)
const auto world = MWBase::Environment::get().getWorld();
const auto stepSize = getPathStepSize(actor);
const auto navigator = world->getNavigator();
const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out);
if (status == DetourNavigator::Status::NavMeshNotFound)
return false;
if (status != DetourNavigator::Status::Success)
{
Log(Debug::Debug) << "Build path by navigator exception: \"" << exception.what()
Log(Debug::Debug) << "Build path by navigator error: \"" << DetourNavigator::getMessage(status)
<< "\" for \"" << actor.getClass().getName(actor) << "\" (" << actor.getBase()
<< ") from " << startPoint << " to " << endPoint << " with flags ("
<< DetourNavigator::WriteFlags {flags} << ")";
}
return true;
}
void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
@ -367,26 +372,30 @@ namespace MWMechanics
if (sqrDistanceIgnoreZ(mPath.front(), startPoint) <= 4 * stepSize * stepSize)
return;
try
{
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
std::deque<osg::Vec3f> prePath;
navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, std::back_inserter(prePath));
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
std::deque<osg::Vec3f> prePath;
auto prePathInserter = std::back_inserter(prePath);
const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags,
prePathInserter);
while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.front(), startPoint) < stepSize * stepSize)
prePath.pop_front();
while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.back(), mPath.front()) < stepSize * stepSize)
prePath.pop_back();
if (status == DetourNavigator::Status::NavMeshNotFound)
return;
std::copy(prePath.rbegin(), prePath.rend(), std::front_inserter(mPath));
}
catch (const DetourNavigator::NavigatorException& exception)
if (status != DetourNavigator::Status::Success)
{
Log(Debug::Debug) << "Build path by navigator exception: \"" << exception.what()
Log(Debug::Debug) << "Build path by navigator error: \"" << DetourNavigator::getMessage(status)
<< "\" for \"" << actor.getClass().getName(actor) << "\" (" << actor.getBase()
<< ") from " << startPoint << " to " << mPath.front() << " with flags ("
<< DetourNavigator::WriteFlags {flags} << ")";
return;
}
while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.front(), startPoint) < stepSize * stepSize)
prePath.pop_front();
while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.back(), mPath.front()) < stepSize * stepSize)
prePath.pop_back();
std::copy(prePath.rbegin(), prePath.rend(), std::front_inserter(mPath));
}
}

@ -201,7 +201,7 @@ namespace MWMechanics
void buildPathByPathgridImpl(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
const PathgridGraph& pathgridGraph, std::back_insert_iterator<std::deque<osg::Vec3f>> out);
void buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
bool buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
std::back_insert_iterator<std::deque<osg::Vec3f>> out);
};

@ -1199,10 +1199,10 @@ namespace MWMechanics
return false;
}
void adjustDynamicStat(CreatureStats& creatureStats, int index, float magnitude)
void adjustDynamicStat(CreatureStats& creatureStats, int index, float magnitude, bool allowDecreaseBelowZero = false)
{
DynamicStat<float> stat = creatureStats.getDynamic(index);
stat.setCurrent(stat.getCurrent() + magnitude, index == 2);
stat.setCurrent(stat.getCurrent() + magnitude, allowDecreaseBelowZero);
creatureStats.setDynamic(index, stat);
}
@ -1241,9 +1241,12 @@ namespace MWMechanics
case ESM::MagicEffect::DamageMagicka:
case ESM::MagicEffect::DamageFatigue:
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
{
int index = effectKey.mId-ESM::MagicEffect::DamageHealth;
static const bool uncappedDamageFatigue = Settings::Manager::getBool("uncapped damage fatigue", "Game");
adjustDynamicStat(creatureStats, index, -magnitude, index == 2 && uncappedDamageFatigue);
break;
}
case ESM::MagicEffect::AbsorbHealth:
if (magnitude > 0.f)
receivedMagicDamage = true;

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

Loading…
Cancel
Save