mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-09-09 12:25:17 -04:00
Merge branch 'OpenMW:master' into master
This commit is contained in:
commit
6185683ca3
@ -234,6 +234,7 @@
|
||||
Bug #8445: Launcher crashes on exit when cell name loading thread is still running
|
||||
Bug #8462: Crashes when resizing the window on macOS
|
||||
Bug #8465: Blue screen w/ antialiasing and post-processing on macOS
|
||||
Bug #8503: Camera does not handle NaN gracefully
|
||||
Feature #1415: Infinite fall failsafe
|
||||
Feature #2566: Handle NAM9 records for manual cell references
|
||||
Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking
|
||||
|
@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...")
|
||||
set(OPENMW_VERSION_MAJOR 0)
|
||||
set(OPENMW_VERSION_MINOR 49)
|
||||
set(OPENMW_VERSION_RELEASE 0)
|
||||
set(OPENMW_LUA_API_REVISION 73)
|
||||
set(OPENMW_LUA_API_REVISION 75)
|
||||
set(OPENMW_POSTPROCESSING_API_REVISION 2)
|
||||
|
||||
set(OPENMW_VERSION_COMMITHASH "")
|
||||
|
@ -365,15 +365,15 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
|
||||
|
||||
QIcon containsDataIcon(":/images/openmw-plugin.png");
|
||||
|
||||
QProgressDialog progressBar("Adding data directories", {}, 0, directories.count(), this);
|
||||
QProgressDialog progressBar("Adding data directories", {}, 0, static_cast<int>(directories.size()), this);
|
||||
progressBar.setWindowModality(Qt::WindowModal);
|
||||
progressBar.setValue(0);
|
||||
|
||||
std::unordered_set<QString> visitedDirectories;
|
||||
for (const Config::SettingValue& currentDir : directories)
|
||||
for (qsizetype i = 0; i < directories.size(); ++i)
|
||||
{
|
||||
progressBar.setValue(progressBar.value() + 1);
|
||||
progressBar.setValue(static_cast<int>(i));
|
||||
|
||||
const Config::SettingValue& currentDir = directories.at(i);
|
||||
if (!visitedDirectories.insert(currentDir.value).second)
|
||||
continue;
|
||||
|
||||
@ -436,6 +436,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
|
||||
}
|
||||
item->setToolTip(tooltip.join('\n'));
|
||||
}
|
||||
progressBar.setValue(progressBar.maximum());
|
||||
mSelector->sortFiles();
|
||||
|
||||
QList<Config::SettingValue> selectedArchives = mGameSettings.getArchiveList();
|
||||
@ -1001,7 +1002,11 @@ bool Launcher::DataFilesPage::showDeleteMessageBox(const QString& text)
|
||||
|
||||
void Launcher::DataFilesPage::slotAddonDataChanged()
|
||||
{
|
||||
QStringList selectedFiles = selectedFilePaths();
|
||||
const ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
|
||||
QStringList selectedFiles;
|
||||
for (const ContentSelectorModel::EsmFile* item : items)
|
||||
selectedFiles.append(item->filePath());
|
||||
|
||||
if (mSelectedFiles != selectedFiles)
|
||||
{
|
||||
const std::lock_guard lock(mReloadCellsMutex);
|
||||
@ -1013,6 +1018,7 @@ void Launcher::DataFilesPage::slotAddonDataChanged()
|
||||
|
||||
void Launcher::DataFilesPage::reloadCells()
|
||||
{
|
||||
QStringList selectedFiles;
|
||||
std::unique_lock lock(mReloadCellsMutex);
|
||||
|
||||
while (true)
|
||||
@ -1025,16 +1031,26 @@ void Launcher::DataFilesPage::reloadCells()
|
||||
if (!std::exchange(mReloadCells, false))
|
||||
continue;
|
||||
|
||||
QStringList selectedFiles = mSelectedFiles;
|
||||
const QStringList newSelectedFiles = mSelectedFiles;
|
||||
|
||||
lock.unlock();
|
||||
|
||||
CellNameLoader cellNameLoader;
|
||||
QSet<QString> set = cellNameLoader.getCellNames(selectedFiles);
|
||||
QStringList cellNamesList(set.begin(), set.end());
|
||||
std::sort(cellNamesList.begin(), cellNamesList.end());
|
||||
QStringList filteredFiles;
|
||||
for (const QString& v : newSelectedFiles)
|
||||
if (QFile::exists(v))
|
||||
filteredFiles.append(v);
|
||||
|
||||
emit signalLoadedCellsChanged(std::move(cellNamesList));
|
||||
if (selectedFiles != filteredFiles)
|
||||
{
|
||||
selectedFiles = std::move(filteredFiles);
|
||||
|
||||
CellNameLoader cellNameLoader;
|
||||
QSet<QString> set = cellNameLoader.getCellNames(selectedFiles);
|
||||
QStringList cellNamesList(set.begin(), set.end());
|
||||
std::sort(cellNamesList.begin(), cellNamesList.end());
|
||||
|
||||
emit signalLoadedCellsChanged(std::move(cellNamesList));
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
|
||||
|
@ -465,7 +465,6 @@ namespace MWGui
|
||||
|
||||
void Console::findOccurrence(const SearchDirection direction)
|
||||
{
|
||||
|
||||
if (mCurrentSearchTerm.empty())
|
||||
{
|
||||
return;
|
||||
@ -478,17 +477,16 @@ namespace MWGui
|
||||
size_t firstIndex{ 0 };
|
||||
size_t lastIndex{ historyText.length() };
|
||||
|
||||
// If search is not the first adjust the range based on the direction and previous occurrence.
|
||||
// If this isn't the first search, adjust the range based on the previous occurrence.
|
||||
if (mCurrentOccurrenceIndex != std::string::npos)
|
||||
{
|
||||
if (direction == SearchDirection::Forward && mCurrentOccurrenceIndex > 1)
|
||||
if (direction == SearchDirection::Forward)
|
||||
{
|
||||
firstIndex = mCurrentOccurrenceIndex + mCurrentOccurrenceLength;
|
||||
}
|
||||
else if (direction == SearchDirection::Reverse
|
||||
&& (historyText.length() - mCurrentOccurrenceIndex) > mCurrentOccurrenceLength)
|
||||
else if (direction == SearchDirection::Reverse)
|
||||
{
|
||||
lastIndex = mCurrentOccurrenceIndex - 1;
|
||||
lastIndex = mCurrentOccurrenceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@ -523,6 +521,13 @@ namespace MWGui
|
||||
void Console::findInHistoryText(const std::string& historyText, const SearchDirection direction,
|
||||
const size_t firstIndex, const size_t lastIndex)
|
||||
{
|
||||
if (lastIndex <= firstIndex)
|
||||
{
|
||||
mCurrentOccurrenceIndex = std::string::npos;
|
||||
mCurrentOccurrenceLength = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRegExSearch)
|
||||
{
|
||||
findWithRegex(historyText, direction, firstIndex, lastIndex);
|
||||
@ -570,7 +575,7 @@ namespace MWGui
|
||||
const size_t firstIndex, const size_t lastIndex)
|
||||
{
|
||||
// Search in given text interval for search term
|
||||
const size_t substringLength{ (lastIndex - firstIndex) + 1 };
|
||||
const size_t substringLength = lastIndex - firstIndex;
|
||||
const std::string_view historyTextView((historyText.c_str() + firstIndex), substringLength);
|
||||
if (direction == SearchDirection::Forward)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/utilpackage.hpp>
|
||||
#include <components/misc/finitenumbers.hpp>
|
||||
#include <components/settings/values.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
@ -11,11 +12,12 @@
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
using CameraMode = MWRender::Camera::Mode;
|
||||
|
||||
sol::table initCameraPackage(sol::state_view lua)
|
||||
{
|
||||
using FiniteFloat = Misc::FiniteFloat;
|
||||
|
||||
MWRender::Camera* camera = MWBase::Environment::get().getWorld()->getCamera();
|
||||
MWRender::RenderingManager* renderingManager = MWBase::Environment::get().getWorld()->getRenderingManager();
|
||||
|
||||
@ -49,26 +51,27 @@ namespace MWLua
|
||||
api["getRoll"] = [camera]() { return -camera->getRoll(); };
|
||||
|
||||
api["setStaticPosition"] = [camera](const osg::Vec3f& pos) { camera->setStaticPosition(pos); };
|
||||
api["setPitch"] = [camera](float v) {
|
||||
api["setPitch"] = [camera](const FiniteFloat v) {
|
||||
camera->setPitch(-v, true);
|
||||
if (camera->getMode() == CameraMode::ThirdPerson)
|
||||
camera->calculateDeferredRotation();
|
||||
};
|
||||
api["setYaw"] = [camera](float v) {
|
||||
api["setYaw"] = [camera](const FiniteFloat v) {
|
||||
camera->setYaw(-v, true);
|
||||
if (camera->getMode() == CameraMode::ThirdPerson)
|
||||
camera->calculateDeferredRotation();
|
||||
};
|
||||
api["setRoll"] = [camera](float v) { camera->setRoll(-v); };
|
||||
api["setExtraPitch"] = [camera](float v) { camera->setExtraPitch(-v); };
|
||||
api["setExtraYaw"] = [camera](float v) { camera->setExtraYaw(-v); };
|
||||
api["setExtraRoll"] = [camera](float v) { camera->setExtraRoll(-v); };
|
||||
api["setRoll"] = [camera](const FiniteFloat v) { camera->setRoll(-v); };
|
||||
api["setExtraPitch"] = [camera](const FiniteFloat v) { camera->setExtraPitch(-v); };
|
||||
api["setExtraYaw"] = [camera](const FiniteFloat v) { camera->setExtraYaw(-v); };
|
||||
api["setExtraRoll"] = [camera](const FiniteFloat v) { camera->setExtraRoll(-v); };
|
||||
api["getExtraPitch"] = [camera]() { return -camera->getExtraPitch(); };
|
||||
api["getExtraYaw"] = [camera]() { return -camera->getExtraYaw(); };
|
||||
api["getExtraRoll"] = [camera]() { return -camera->getExtraRoll(); };
|
||||
|
||||
api["getThirdPersonDistance"] = [camera]() { return camera->getCameraDistance(); };
|
||||
api["setPreferredThirdPersonDistance"] = [camera](float v) { camera->setPreferredCameraDistance(v); };
|
||||
api["setPreferredThirdPersonDistance"]
|
||||
= [camera](const FiniteFloat v) { camera->setPreferredCameraDistance(v); };
|
||||
|
||||
api["getFirstPersonOffset"] = [camera]() { return camera->getFirstPersonOffset(); };
|
||||
api["setFirstPersonOffset"] = [camera](const osg::Vec3f& v) { camera->setFirstPersonOffset(v); };
|
||||
@ -76,7 +79,7 @@ namespace MWLua
|
||||
api["getFocalPreferredOffset"] = [camera]() -> osg::Vec2f { return camera->getFocalPointTargetOffset(); };
|
||||
api["setFocalPreferredOffset"] = [camera](const osg::Vec2f& v) { camera->setFocalPointTargetOffset(v); };
|
||||
api["getFocalTransitionSpeed"] = [camera]() { return camera->getFocalPointTransitionSpeed(); };
|
||||
api["setFocalTransitionSpeed"] = [camera](float v) { camera->setFocalPointTransitionSpeed(v); };
|
||||
api["setFocalTransitionSpeed"] = [camera](const FiniteFloat v) { camera->setFocalPointTransitionSpeed(v); };
|
||||
api["instantTransition"] = [camera]() { camera->instantTransition(); };
|
||||
|
||||
api["getCollisionType"] = [camera]() { return camera->getCollisionType(); };
|
||||
@ -86,11 +89,12 @@ namespace MWLua
|
||||
api["getFieldOfView"]
|
||||
= [renderingManager]() { return osg::DegreesToRadians(renderingManager->getFieldOfView()); };
|
||||
api["setFieldOfView"]
|
||||
= [renderingManager](float v) { renderingManager->setFieldOfView(osg::RadiansToDegrees(v)); };
|
||||
= [renderingManager](const FiniteFloat v) { renderingManager->setFieldOfView(osg::RadiansToDegrees(v)); };
|
||||
|
||||
api["getBaseViewDistance"] = [] { return Settings::camera().mViewingDistance.get(); };
|
||||
api["getViewDistance"] = [renderingManager]() { return renderingManager->getViewDistance(); };
|
||||
api["setViewDistance"] = [renderingManager](float d) { renderingManager->setViewDistance(d, true); };
|
||||
api["setViewDistance"]
|
||||
= [renderingManager](const FiniteFloat d) { renderingManager->setViewDistance(d, true); };
|
||||
|
||||
api["getViewTransform"] = [camera]() { return LuaUtil::TransformM{ camera->getViewMatrix() }; };
|
||||
|
||||
|
@ -100,7 +100,8 @@ namespace MWLua
|
||||
stats.land(true);
|
||||
stats.setTeleported(true);
|
||||
world->getPlayer().setTeleported(true);
|
||||
world->changeToCell(destCell->getCell()->getId(), toPos(pos, rot), false);
|
||||
bool differentCell = ptr.getCell() != destCell;
|
||||
world->changeToCell(destCell->getCell()->getId(), toPos(pos, rot), false, differentCell);
|
||||
MWWorld::Ptr newPtr = world->getPlayerPtr();
|
||||
world->moveObject(newPtr, pos);
|
||||
world->rotateObject(newPtr, rot);
|
||||
@ -350,8 +351,8 @@ namespace MWLua
|
||||
throw std::runtime_error(
|
||||
"The argument of `activateBy` must be an actor who activates the object. Got: "
|
||||
+ actor.toString());
|
||||
if (objPtr.getRefData().activate())
|
||||
MWBase::Environment::get().getLuaManager()->objectActivated(objPtr, actorPtr);
|
||||
|
||||
MWBase::Environment::get().getLuaManager()->objectActivated(objPtr, actorPtr);
|
||||
};
|
||||
|
||||
auto isEnabled = [](const ObjectT& o) { return o.ptr().getRefData().isEnabled(); };
|
||||
|
@ -160,26 +160,31 @@ namespace MWLua
|
||||
else
|
||||
return LuaUtil::toLuaIndex(index);
|
||||
};
|
||||
layersTable["insertAfter"] = [context](
|
||||
std::string_view afterName, std::string_view name, const sol::object& opt) {
|
||||
layersTable["insertAfter"] = [context](std::string afterName, std::string_view name, const sol::object& opt) {
|
||||
LuaUi::Layer::Options options;
|
||||
options.mInteractive = LuaUtil::getValueOrDefault(LuaUtil::getFieldOrNil(opt, "interactive"), true);
|
||||
size_t index = LuaUi::Layer::indexOf(afterName);
|
||||
if (index == LuaUi::Layer::count())
|
||||
throw std::logic_error(std::string("Layer not found"));
|
||||
index++;
|
||||
context.mLuaManager->addAction(
|
||||
[=, name = std::string(name)]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
|
||||
[=]() {
|
||||
size_t index = LuaUi::Layer::indexOf(afterName);
|
||||
if (index == LuaUi::Layer::count())
|
||||
throw std::logic_error(
|
||||
Misc::StringUtils::format("Couldn't insert after non-existent layer %s", afterName));
|
||||
LuaUi::Layer::insert(index + 1, name, options);
|
||||
},
|
||||
"Insert after UI layer");
|
||||
};
|
||||
layersTable["insertBefore"] = [context](
|
||||
std::string_view beforename, std::string_view name, const sol::object& opt) {
|
||||
layersTable["insertBefore"] = [context](std::string beforeName, std::string_view name, const sol::object& opt) {
|
||||
LuaUi::Layer::Options options;
|
||||
options.mInteractive = LuaUtil::getValueOrDefault(LuaUtil::getFieldOrNil(opt, "interactive"), true);
|
||||
size_t index = LuaUi::Layer::indexOf(beforename);
|
||||
if (index == LuaUi::Layer::count())
|
||||
throw std::logic_error(std::string("Layer not found"));
|
||||
context.mLuaManager->addAction(
|
||||
[=, name = std::string(name)]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
|
||||
[=]() {
|
||||
size_t index = LuaUi::Layer::indexOf(beforeName);
|
||||
if (index == LuaUi::Layer::count())
|
||||
throw std::logic_error(
|
||||
Misc::StringUtils::format("Couldn't insert before non-existent layer %s", beforeName));
|
||||
LuaUi::Layer::insert(index, name, options);
|
||||
},
|
||||
"Insert before UI layer");
|
||||
};
|
||||
sol::table layers = LuaUtil::makeReadOnly(layersTable);
|
||||
sol::table layersMeta = layers[sol::metatable_key];
|
||||
|
@ -1388,6 +1388,7 @@ namespace MWMechanics
|
||||
// Note: we do not disable unequipping animation automatically to avoid body desync
|
||||
weapgroup = getWeaponAnimation(mWeaponType);
|
||||
int unequipMask = MWRender::BlendMask_All;
|
||||
mUpperBodyState = UpperBodyState::Unequipping;
|
||||
bool useShieldAnims = mAnimation->useShieldAnimations();
|
||||
if (useShieldAnims && mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell
|
||||
&& !(mWeaponType == ESM::Weapon::None && weaptype == ESM::Weapon::Spell))
|
||||
@ -1402,7 +1403,6 @@ namespace MWMechanics
|
||||
mAnimation->disable(weapgroup);
|
||||
playBlendedAnimation(
|
||||
weapgroup, priorityWeapon, unequipMask, false, 1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperBodyState::Unequipping;
|
||||
|
||||
mAnimation->detachArrow();
|
||||
|
||||
@ -1447,6 +1447,7 @@ namespace MWMechanics
|
||||
{
|
||||
mAnimation->showWeapons(false);
|
||||
int equipMask = MWRender::BlendMask_All;
|
||||
mUpperBodyState = UpperBodyState::Equipping;
|
||||
if (useShieldAnims && weaptype != ESM::Weapon::Spell)
|
||||
{
|
||||
equipMask = equipMask | ~MWRender::BlendMask_LeftArm;
|
||||
@ -1459,7 +1460,6 @@ namespace MWMechanics
|
||||
playBlendedAnimation(weapgroup, priorityWeapon, equipMask, true, 1.0f, "equip start",
|
||||
"equip stop", 0.0f, 0);
|
||||
}
|
||||
mUpperBodyState = UpperBodyState::Equipping;
|
||||
|
||||
// If we do not have the "equip attach" key, show weapon manually.
|
||||
if (weaptype != ESM::Weapon::Spell
|
||||
|
@ -38,19 +38,19 @@ void Config::GameSettings::validatePaths()
|
||||
|
||||
mDataDirs.clear();
|
||||
|
||||
QProgressDialog progressBar("Validating paths", {}, 0, paths.count() + 1);
|
||||
QProgressDialog progressBar("Validating paths", {}, 0, static_cast<int>(paths.size() + 1));
|
||||
progressBar.setWindowModality(Qt::WindowModal);
|
||||
progressBar.setValue(0);
|
||||
|
||||
for (const auto& dataDir : paths)
|
||||
{
|
||||
progressBar.setValue(progressBar.value() + 1);
|
||||
if (QDir(dataDir.value).exists())
|
||||
{
|
||||
SettingValue copy = dataDir;
|
||||
copy.value = QDir(dataDir.value).canonicalPath();
|
||||
mDataDirs.append(copy);
|
||||
}
|
||||
progressBar.setValue(progressBar.value() + 1);
|
||||
}
|
||||
|
||||
// Do the same for data-local
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <QDirIterator>
|
||||
#include <QFont>
|
||||
#include <QIODevice>
|
||||
#include <QProgressDialog>
|
||||
|
||||
#include <components/esm/format.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
@ -116,37 +117,26 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex& index
|
||||
if (file == mGameFile)
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
|
||||
|
||||
Qt::ItemFlags returnFlags;
|
||||
// files with no dependencies can always be checked
|
||||
if (file->gameFiles().empty())
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;
|
||||
|
||||
// addon can be checked if its gamefile is
|
||||
// ... special case, addon with no dependency can be used with any gamefile.
|
||||
bool gamefileChecked = false;
|
||||
bool noGameFiles = true;
|
||||
for (const QString& fileName : file->gameFiles())
|
||||
// Show the file if the game it is for is enabled.
|
||||
// NB: The file may theoretically depend on multiple games.
|
||||
// Early exit means that a file is visible only if its earliest found game dependency is enabled.
|
||||
// This can be counterintuitive, but it is okay for non-bizarre content setups. And also faster.
|
||||
for (const EsmFile* depFile : mFiles)
|
||||
{
|
||||
for (QListIterator<EsmFile*> dependencyIter(mFiles); dependencyIter.hasNext(); dependencyIter.next())
|
||||
if (depFile->isGameFile() && file->gameFiles().contains(depFile->fileName(), Qt::CaseInsensitive))
|
||||
{
|
||||
// compare filenames only. Multiple instances
|
||||
// of the filename (with different paths) is not relevant here.
|
||||
EsmFile* depFile = dependencyIter.peekNext();
|
||||
if (!depFile->isGameFile() || depFile->fileName().compare(fileName, Qt::CaseInsensitive) != 0)
|
||||
continue;
|
||||
|
||||
noGameFiles = false;
|
||||
if (depFile->builtIn() || depFile->fromAnotherConfigFile() || mCheckedFiles.contains(depFile))
|
||||
{
|
||||
gamefileChecked = true;
|
||||
if (!depFile->builtIn() && !depFile->fromAnotherConfigFile() && !mCheckedFiles.contains(depFile))
|
||||
break;
|
||||
}
|
||||
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
if (gamefileChecked || noGameFiles)
|
||||
{
|
||||
returnFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;
|
||||
}
|
||||
|
||||
return returnFlags;
|
||||
return Qt::NoItemFlags;
|
||||
}
|
||||
|
||||
QVariant ContentSelectorModel::ContentModel::data(const QModelIndex& index, int role) const
|
||||
@ -278,7 +268,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const
|
||||
|
||||
if (success)
|
||||
{
|
||||
success = setCheckState(file->filePath(), value.toBool());
|
||||
success = setCheckState(file, value.toBool());
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
}
|
||||
@ -305,7 +295,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const
|
||||
|
||||
if (setState)
|
||||
{
|
||||
setCheckState(file->filePath(), success);
|
||||
setCheckState(file, success);
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
else
|
||||
@ -707,14 +697,18 @@ bool ContentSelectorModel::ContentModel::isLoadOrderError(const EsmFile* file) c
|
||||
|
||||
void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileList)
|
||||
{
|
||||
QProgressDialog progressDialog("Setting content list", {}, 0, static_cast<int>(fileList.size()));
|
||||
progressDialog.setWindowModality(Qt::WindowModal);
|
||||
progressDialog.setValue(0);
|
||||
|
||||
int previousPosition = -1;
|
||||
for (const QString& filepath : fileList)
|
||||
for (qsizetype i = 0, n = fileList.size(); i < n; ++i)
|
||||
{
|
||||
if (setCheckState(filepath, true))
|
||||
const EsmFile* file = item(fileList[i]);
|
||||
if (setCheckState(file, true))
|
||||
{
|
||||
// setCheckState already gracefully handles builtIn and fromAnotherConfigFile
|
||||
// as necessary, move plug-ins in visible list to match sequence of supplied filelist
|
||||
const EsmFile* file = item(filepath);
|
||||
int filePosition = indexFromItem(file).row();
|
||||
if (filePosition < previousPosition)
|
||||
{
|
||||
@ -725,8 +719,11 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileL
|
||||
previousPosition = filePosition;
|
||||
}
|
||||
}
|
||||
|
||||
progressDialog.setValue(static_cast<int>(i + 1));
|
||||
}
|
||||
emit dataChanged(index(0, 0), index(rowCount(), columnCount()));
|
||||
|
||||
refreshModel();
|
||||
}
|
||||
|
||||
QList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::checkForLoadOrderErrors(
|
||||
@ -790,18 +787,13 @@ QString ContentSelectorModel::ContentModel::toolTip(const EsmFile* file) const
|
||||
}
|
||||
}
|
||||
|
||||
void ContentSelectorModel::ContentModel::refreshModel()
|
||||
void ContentSelectorModel::ContentModel::refreshModel(std::initializer_list<int> roles)
|
||||
{
|
||||
emit dataChanged(index(0, 0), index(rowCount() - 1, 0));
|
||||
emit dataChanged(index(0, 0), index(rowCount() - 1, 0), roles);
|
||||
}
|
||||
|
||||
bool ContentSelectorModel::ContentModel::setCheckState(const QString& filepath, bool checkState)
|
||||
bool ContentSelectorModel::ContentModel::setCheckState(const EsmFile* file, bool checkState)
|
||||
{
|
||||
if (filepath.isEmpty())
|
||||
return false;
|
||||
|
||||
const EsmFile* file = item(filepath);
|
||||
|
||||
if (!file || file->builtIn() || file->fromAnotherConfigFile())
|
||||
return false;
|
||||
|
||||
@ -810,7 +802,7 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString& filepath,
|
||||
else
|
||||
mCheckedFiles.erase(file);
|
||||
|
||||
emit dataChanged(indexFromItem(item(filepath)), indexFromItem(item(filepath)));
|
||||
emit dataChanged(indexFromItem(file), indexFromItem(file));
|
||||
|
||||
if (file->isGameFile())
|
||||
refreshModel();
|
||||
@ -835,10 +827,7 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString& filepath,
|
||||
{
|
||||
for (const EsmFile* downstreamFile : mFiles)
|
||||
{
|
||||
QFileInfo fileInfo(filepath);
|
||||
QString filename = fileInfo.fileName();
|
||||
|
||||
if (downstreamFile->gameFiles().contains(filename, Qt::CaseInsensitive))
|
||||
if (downstreamFile->gameFiles().contains(file->fileName(), Qt::CaseInsensitive))
|
||||
{
|
||||
mCheckedFiles.erase(downstreamFile);
|
||||
|
||||
@ -878,5 +867,5 @@ ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checke
|
||||
void ContentSelectorModel::ContentModel::uncheckAll()
|
||||
{
|
||||
mCheckedFiles.clear();
|
||||
emit dataChanged(index(0, 0), index(rowCount(), columnCount()), { Qt::CheckStateRole, Qt::UserRole + 1 });
|
||||
refreshModel({ Qt::CheckStateRole, Qt::UserRole + 1 });
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ namespace ContentSelectorModel
|
||||
void setCurrentGameFile(const EsmFile* file);
|
||||
|
||||
bool isEnabled(const QModelIndex& index) const;
|
||||
bool setCheckState(const QString& filepath, bool isChecked);
|
||||
bool setCheckState(const EsmFile* file, bool isChecked);
|
||||
bool isNew(const QString& filepath) const;
|
||||
void setNew(const QString& filepath, bool isChecked);
|
||||
void setNonUserContent(const QStringList& fileList);
|
||||
@ -67,7 +67,7 @@ namespace ContentSelectorModel
|
||||
ContentFileList checkedItems() const;
|
||||
void uncheckAll();
|
||||
|
||||
void refreshModel();
|
||||
void refreshModel(std::initializer_list<int> roles = {});
|
||||
|
||||
private:
|
||||
void addFile(EsmFile* file);
|
||||
|
@ -2,13 +2,15 @@
|
||||
|
||||
ContentSelectorModel::EsmFile::EsmFile(const QString& fileName, ModelItem* parent)
|
||||
: ModelItem(parent)
|
||||
, mFileName(fileName)
|
||||
{
|
||||
setFileName(fileName);
|
||||
}
|
||||
|
||||
void ContentSelectorModel::EsmFile::setFileName(const QString& fileName)
|
||||
{
|
||||
mFileName = fileName;
|
||||
mHasGameExtension = (mFileName.endsWith(QLatin1String(".esm"), Qt::CaseInsensitive)
|
||||
|| mFileName.endsWith(QLatin1String(".omwgame"), Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
void ContentSelectorModel::EsmFile::setAuthor(const QString& author)
|
||||
@ -53,9 +55,7 @@ void ContentSelectorModel::EsmFile::setFromAnotherConfigFile(bool fromAnotherCon
|
||||
|
||||
bool ContentSelectorModel::EsmFile::isGameFile() const
|
||||
{
|
||||
return (mGameFiles.size() == 0)
|
||||
&& (mFileName.endsWith(QLatin1String(".esm"), Qt::CaseInsensitive)
|
||||
|| mFileName.endsWith(QLatin1String(".omwgame"), Qt::CaseInsensitive));
|
||||
return mHasGameExtension && mGameFiles.empty();
|
||||
}
|
||||
|
||||
QVariant ContentSelectorModel::EsmFile::fileProperty(const FileProperty prop) const
|
||||
@ -108,7 +108,7 @@ void ContentSelectorModel::EsmFile::setFileProperty(const FileProperty prop, con
|
||||
switch (prop)
|
||||
{
|
||||
case FileProperty_FileName:
|
||||
mFileName = value;
|
||||
setFileName(value);
|
||||
break;
|
||||
|
||||
case FileProperty_Author:
|
||||
|
@ -102,6 +102,7 @@ namespace ContentSelectorModel
|
||||
QString mToolTip;
|
||||
bool mBuiltIn = false;
|
||||
bool mFromAnotherConfigFile = false;
|
||||
bool mHasGameExtension = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <QClipboard>
|
||||
#include <QMenu>
|
||||
#include <QModelIndex>
|
||||
#include <QProgressDialog>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
ContentSelectorView::ContentSelector::ContentSelector(QWidget* parent, bool showOMWScripts)
|
||||
@ -156,7 +157,7 @@ void ContentSelectorView::ContentSelector::setGameFile(const QString& filename)
|
||||
index = ui->gameFileView->findText(file->fileName());
|
||||
|
||||
// verify that the current index is also checked in the model
|
||||
if (!mContentModel->setCheckState(filename, true))
|
||||
if (!mContentModel->setCheckState(file, true))
|
||||
{
|
||||
// throw error in case file not found?
|
||||
return;
|
||||
@ -292,27 +293,33 @@ void ContentSelectorView::ContentSelector::slotShowContextMenu(const QPoint& pos
|
||||
mContextMenu->exec(globalPos);
|
||||
}
|
||||
|
||||
void ContentSelectorView::ContentSelector::setCheckStateForMultiSelectedItems(bool checked)
|
||||
void ContentSelectorView::ContentSelector::setCheckStateForMultiSelectedItems(Qt::CheckState checkState)
|
||||
{
|
||||
Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked;
|
||||
for (const QModelIndex& index : ui->addonView->selectionModel()->selectedIndexes())
|
||||
const QModelIndexList selectedIndexes = ui->addonView->selectionModel()->selectedIndexes();
|
||||
|
||||
QProgressDialog progressDialog("Updating content selection", {}, 0, static_cast<int>(selectedIndexes.size()));
|
||||
progressDialog.setWindowModality(Qt::WindowModal);
|
||||
progressDialog.setValue(0);
|
||||
|
||||
for (qsizetype i = 0, n = selectedIndexes.size(); i < n; ++i)
|
||||
{
|
||||
QModelIndex sourceIndex = mAddonProxyModel->mapToSource(index);
|
||||
const QModelIndex sourceIndex = mAddonProxyModel->mapToSource(selectedIndexes[i]);
|
||||
|
||||
if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() != checkState)
|
||||
{
|
||||
mContentModel->setData(sourceIndex, checkState, Qt::CheckStateRole);
|
||||
}
|
||||
|
||||
progressDialog.setValue(static_cast<int>(i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
void ContentSelectorView::ContentSelector::slotUncheckMultiSelectedItems()
|
||||
{
|
||||
setCheckStateForMultiSelectedItems(false);
|
||||
setCheckStateForMultiSelectedItems(Qt::Unchecked);
|
||||
}
|
||||
|
||||
void ContentSelectorView::ContentSelector::slotCheckMultiSelectedItems()
|
||||
{
|
||||
setCheckStateForMultiSelectedItems(true);
|
||||
setCheckStateForMultiSelectedItems(Qt::Checked);
|
||||
}
|
||||
|
||||
void ContentSelectorView::ContentSelector::slotCopySelectedItemsPaths()
|
||||
|
@ -69,7 +69,7 @@ namespace ContentSelectorView
|
||||
void buildAddonView();
|
||||
void buildContextMenu();
|
||||
void setGameFileSelected(int index, bool selected);
|
||||
void setCheckStateForMultiSelectedItems(bool checked);
|
||||
void setCheckStateForMultiSelectedItems(Qt::CheckState checkState);
|
||||
|
||||
signals:
|
||||
void signalCurrentGamefileIndexChanged(int);
|
||||
|
45
components/misc/finitenumbers.hpp
Normal file
45
components/misc/finitenumbers.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef OPENMW_COMPONENTS_MISC_FINITENUMBERS_HPP
|
||||
#define OPENMW_COMPONENTS_MISC_FINITENUMBERS_HPP
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
struct FiniteFloat
|
||||
{
|
||||
float mValue;
|
||||
FiniteFloat(float v)
|
||||
{
|
||||
if (!std::isfinite(v))
|
||||
throw std::invalid_argument("Value must be a finite number");
|
||||
mValue = v;
|
||||
}
|
||||
operator float() const { return mValue; }
|
||||
};
|
||||
}
|
||||
|
||||
namespace sol
|
||||
{
|
||||
using FiniteFloat = Misc::FiniteFloat;
|
||||
|
||||
template <typename Handler>
|
||||
bool sol_lua_check(
|
||||
sol::types<FiniteFloat>, lua_State* L, int index, Handler&& handler, sol::stack::record& tracking)
|
||||
{
|
||||
bool success = sol::stack::check<float>(L, lua_absindex(L, index), handler);
|
||||
tracking.use(1);
|
||||
return success;
|
||||
}
|
||||
|
||||
static FiniteFloat sol_lua_get(sol::types<FiniteFloat>, lua_State* L, int index, sol::stack::record& tracking)
|
||||
{
|
||||
float val = sol::stack::get<float>(L, lua_absindex(L, index));
|
||||
tracking.use(1);
|
||||
return FiniteFloat(val);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -104,6 +104,8 @@ namespace SceneUtil
|
||||
("shadowTexture" + std::to_string(i - mShadowSettings->getBaseShadowTextureUnit())).c_str(),
|
||||
static_cast<int>(i)));
|
||||
}
|
||||
stateset.addUniform(new osg::Uniform("maximumShadowMapDistance", 0.00001f));
|
||||
stateset.addUniform(new osg::Uniform("shadowFadeStart", 0.0f));
|
||||
}
|
||||
|
||||
ShadowManager::ShadowManager(osg::ref_ptr<osg::Group> sceneRoot, osg::ref_ptr<osg::Group> rootNode,
|
||||
|
@ -95,7 +95,7 @@ namespace Terrain
|
||||
if (!terrainNode)
|
||||
return; // no terrain defined
|
||||
|
||||
TerrainGrid::World::loadCell(x, y);
|
||||
Terrain::World::loadCell(x, y);
|
||||
|
||||
mTerrainRoot->addChild(terrainNode);
|
||||
|
||||
|
@ -7,5 +7,6 @@
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw_aux.time <Package openmw_aux.time>` | everywhere | | Timers and game time utils |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw_aux.ui <Package openmw_aux.ui>` | by player scripts | | User interface utils |
|
||||
|:ref:`openmw_aux.ui <Package openmw_aux.ui>` | by player and menu | | User interface utils |
|
||||
| | scripts | |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|
@ -34,10 +34,10 @@
|
||||
- | Control, extend, and override skill progression of the
|
||||
| player.
|
||||
* - :ref:`Settings <Interface Settings>`
|
||||
- by player and global scripts
|
||||
- by player, menu, and global scripts
|
||||
- Save, display and track changes of setting values.
|
||||
* - :ref:`MWUI <Interface MWUI>`
|
||||
- by player scripts
|
||||
- by player and menu scripts
|
||||
- Morrowind-style UI templates.
|
||||
* - :ref:`UI <Interface UI>`
|
||||
- by player scripts
|
||||
|
@ -1,43 +1,48 @@
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Package | Can be used | Description |
|
||||
+============================================================+====================+===============================================================+
|
||||
|:ref:`openmw.interfaces <Script interfaces>` | everywhere | | Public interfaces of other scripts. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.util <Package openmw.util>` | everywhere | | Defines utility functions and classes like 3D vectors, |
|
||||
| | | | that don't depend on the game world. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.storage <Package openmw.storage>` | everywhere | | Storage API. In particular can be used to store data |
|
||||
| | | | between game sessions. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.core <Package openmw.core>` | everywhere | | Functions that are common for both global and local scripts |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.types <Package openmw.types>` | everywhere | | Functions for specific types of game objects. |
|
||||
|:ref:`openmw.ambient <Package openmw.ambient>` | by player and menu | | Controls background sounds for given player. |
|
||||
| | scripts | |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.animation <Package openmw.animation>` | everywhere | | Animation controls |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.async <Package openmw.async>` | everywhere | | Timers and callbacks. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.vfs <Package openmw.vfs>` | everywhere | | Read-only access to data directories via VFS. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.markup <Package openmw.markup>` | everywhere | | API to work with markup languages. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.world <Package openmw.world>` | by global scripts | | Read-write access to the game world. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.self <Package openmw.self>` | by local scripts | | Full access to the object the script is attached to. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.nearby <Package openmw.nearby>` | by local scripts | | Read-only access to the nearest area of the game world. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.ambient <Package openmw.ambient>` | by player scripts | | Controls background sounds for given player. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.input <Package openmw.input>` | by player scripts | | User input. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.ui <Package openmw.ui>` | by player scripts | | Controls :ref:`user interface <User interface reference>`. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.menu <Package openmw.menu>` | by menu scripts | | Main menu functionality, such as managing game saves |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.camera <Package openmw.camera>` | by player scripts | | Controls camera. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.postprocessing <Package openmw.postprocessing>`| by player scripts | | Controls post-process shaders. |
|
||||
|:ref:`openmw.core <Package openmw.core>` | everywhere | | Functions that are common for both global and local scripts |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.debug <Package openmw.debug>` | by player scripts | | Collection of debug utils. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.input <Package openmw.input>` | by player and menu | | User input. |
|
||||
| | scripts | |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.interfaces <Script interfaces>` | everywhere | | Public interfaces of other scripts. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.markup <Package openmw.markup>` | everywhere | | API to work with markup languages. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.menu <Package openmw.menu>` | by menu scripts | | Main menu functionality, such as managing game saves |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.nearby <Package openmw.nearby>` | by local and | | Read-only access to the nearest area of the game world. |
|
||||
| | player scripts | |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.postprocessing <Package openmw.postprocessing>`| by player scripts | | Controls post-process shaders. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.self <Package openmw.self>` | by local and | | Full access to the object the script is attached to. |
|
||||
| | player scripts | |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.storage <Package openmw.storage>` | everywhere | | Storage API. In particular can be used to store data |
|
||||
| | | | between game sessions. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.types <Package openmw.types>` | everywhere | | Functions for specific types of game objects. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.ui <Package openmw.ui>` | by player and menu | | Controls :ref:`user interface <User interface reference>`. |
|
||||
| | scripts | |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.util <Package openmw.util>` | everywhere | | Defines utility functions and classes like 3D vectors, |
|
||||
| | | | that don't depend on the game world. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.vfs <Package openmw.vfs>` | everywhere | | Read-only access to data directories via VFS. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.world <Package openmw.world>` | by global scripts | | Read-write access to the game world. |
|
||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|
Loading…
x
Reference in New Issue
Block a user