mirror of
https://github.com/OpenMW/openmw.git
synced 2025-10-17 00:46:34 +00:00
Merge branch 'OpenMW:master' into master
This commit is contained in:
commit
51f9ce890e
7 changed files with 42 additions and 143 deletions
|
@ -10,6 +10,7 @@
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -202,8 +203,14 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C
|
||||||
// the addons and don't want to get signals of the system doing it during startup.
|
// the addons and don't want to get signals of the system doing it during startup.
|
||||||
connect(mSelector, &ContentSelectorView::ContentSelector::signalAddonDataChanged, this,
|
connect(mSelector, &ContentSelectorView::ContentSelector::signalAddonDataChanged, this,
|
||||||
&DataFilesPage::slotAddonDataChanged);
|
&DataFilesPage::slotAddonDataChanged);
|
||||||
|
|
||||||
|
mReloadCellsTimer = new QTimer(this);
|
||||||
|
mReloadCellsTimer->setSingleShot(true);
|
||||||
|
mReloadCellsTimer->setInterval(200);
|
||||||
|
connect(mReloadCellsTimer, &QTimer::timeout, this, &DataFilesPage::onReloadCellsTimerTimeout);
|
||||||
|
|
||||||
// Call manually to indicate all changes to addon data during startup.
|
// Call manually to indicate all changes to addon data during startup.
|
||||||
slotAddonDataChanged();
|
onReloadCellsTimerTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
Launcher::DataFilesPage::~DataFilesPage()
|
Launcher::DataFilesPage::~DataFilesPage()
|
||||||
|
@ -1001,6 +1008,11 @@ bool Launcher::DataFilesPage::showDeleteMessageBox(const QString& text)
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::DataFilesPage::slotAddonDataChanged()
|
void Launcher::DataFilesPage::slotAddonDataChanged()
|
||||||
|
{
|
||||||
|
mReloadCellsTimer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Launcher::DataFilesPage::onReloadCellsTimerTimeout()
|
||||||
{
|
{
|
||||||
const ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
|
const ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
|
||||||
QStringList selectedFiles;
|
QStringList selectedFiles;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
class QSortFilterProxyModel;
|
class QSortFilterProxyModel;
|
||||||
class QAbstractItemModel;
|
class QAbstractItemModel;
|
||||||
class QMenu;
|
class QMenu;
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
namespace Files
|
namespace Files
|
||||||
{
|
{
|
||||||
|
@ -137,6 +138,7 @@ namespace Launcher
|
||||||
std::mutex mReloadCellsMutex;
|
std::mutex mReloadCellsMutex;
|
||||||
std::condition_variable mStartReloadCells;
|
std::condition_variable mStartReloadCells;
|
||||||
std::thread mReloadCellsThread;
|
std::thread mReloadCellsThread;
|
||||||
|
QTimer* mReloadCellsTimer;
|
||||||
|
|
||||||
void addArchive(const QString& name, Qt::CheckState selected, int row = -1);
|
void addArchive(const QString& name, Qt::CheckState selected, int row = -1);
|
||||||
void addArchivesFromDir(const QString& dir);
|
void addArchivesFromDir(const QString& dir);
|
||||||
|
@ -151,6 +153,7 @@ namespace Launcher
|
||||||
void addProfile(const QString& profile, bool setAsCurrent);
|
void addProfile(const QString& profile, bool setAsCurrent);
|
||||||
void checkForDefaultProfile();
|
void checkForDefaultProfile();
|
||||||
void populateFileViews(const QString& contentModelName);
|
void populateFileViews(const QString& contentModelName);
|
||||||
|
void onReloadCellsTimerTimeout();
|
||||||
void reloadCells();
|
void reloadCells();
|
||||||
void refreshDataFilesView();
|
void refreshDataFilesView();
|
||||||
void updateNavMeshProgress(int minDataSize);
|
void updateNavMeshProgress(int minDataSize);
|
||||||
|
|
|
@ -193,15 +193,6 @@ namespace MWMechanics
|
||||||
mLevel = level;
|
mLevel = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureStats::modifyMagicEffects(const MagicEffects& effects)
|
|
||||||
{
|
|
||||||
bool recalc = effects.getOrDefault(ESM::MagicEffect::FortifyMaximumMagicka).getModifier()
|
|
||||||
!= mMagicEffects.getOrDefault(ESM::MagicEffect::FortifyMaximumMagicka).getModifier();
|
|
||||||
mMagicEffects.setModifiers(effects);
|
|
||||||
if (recalc)
|
|
||||||
recalculateMagicka();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreatureStats::setAiSetting(AiSetting index, Stat<int> value)
|
void CreatureStats::setAiSetting(AiSetting index, Stat<int> value)
|
||||||
{
|
{
|
||||||
mAiSettings[static_cast<std::underlying_type_t<AiSetting>>(index)] = value;
|
mAiSettings[static_cast<std::underlying_type_t<AiSetting>>(index)] = value;
|
||||||
|
|
|
@ -155,9 +155,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
void setDynamic(int index, const DynamicStat<float>& value);
|
void setDynamic(int index, const DynamicStat<float>& value);
|
||||||
|
|
||||||
/// Set Modifier for each magic effect according to \a effects. Does not touch Base values.
|
|
||||||
void modifyMagicEffects(const MagicEffects& effects);
|
|
||||||
|
|
||||||
void setAttackingOrSpell(bool attackingOrSpell) { mAttackingOrSpell = attackingOrSpell; }
|
void setAttackingOrSpell(bool attackingOrSpell) { mAttackingOrSpell = attackingOrSpell; }
|
||||||
|
|
||||||
void setAttackType(std::string_view attackType) { mAttackType = attackType; }
|
void setAttackType(std::string_view attackType) { mAttackType = attackType; }
|
||||||
|
|
|
@ -121,11 +121,6 @@ namespace MWMechanics
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MagicEffects::remove(const EffectKey& key)
|
|
||||||
{
|
|
||||||
mCollection.erase(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MagicEffects::add(const EffectKey& key, const EffectParam& param)
|
void MagicEffects::add(const EffectKey& key, const EffectParam& param)
|
||||||
{
|
{
|
||||||
Collection::iterator iter = mCollection.find(key);
|
Collection::iterator iter = mCollection.find(key);
|
||||||
|
@ -145,19 +140,6 @@ namespace MWMechanics
|
||||||
mCollection[key].modifyBase(diff);
|
mCollection[key].modifyBase(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MagicEffects::setModifiers(const MagicEffects& effects)
|
|
||||||
{
|
|
||||||
for (Collection::iterator it = mCollection.begin(); it != mCollection.end(); ++it)
|
|
||||||
{
|
|
||||||
it->second.setModifier(effects.getOrDefault(it->first).getModifier());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Collection::const_iterator it = effects.begin(); it != effects.end(); ++it)
|
|
||||||
{
|
|
||||||
mCollection[it->first].setModifier(it->second.getModifier());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EffectParam MagicEffects::getOrDefault(const EffectKey& key) const
|
EffectParam MagicEffects::getOrDefault(const EffectKey& key) const
|
||||||
{
|
{
|
||||||
return get(key).value_or(EffectParam());
|
return get(key).value_or(EffectParam());
|
||||||
|
@ -174,40 +156,6 @@ namespace MWMechanics
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
MagicEffects MagicEffects::diff(const MagicEffects& prev, const MagicEffects& now)
|
|
||||||
{
|
|
||||||
MagicEffects result;
|
|
||||||
|
|
||||||
// adding/changing
|
|
||||||
for (Collection::const_iterator iter(now.begin()); iter != now.end(); ++iter)
|
|
||||||
{
|
|
||||||
Collection::const_iterator other = prev.mCollection.find(iter->first);
|
|
||||||
|
|
||||||
if (other == prev.end())
|
|
||||||
{
|
|
||||||
// adding
|
|
||||||
result.add(iter->first, iter->second);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// changing
|
|
||||||
result.add(iter->first, iter->second - other->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// removing
|
|
||||||
for (Collection::const_iterator iter(prev.begin()); iter != prev.end(); ++iter)
|
|
||||||
{
|
|
||||||
Collection::const_iterator other = now.mCollection.find(iter->first);
|
|
||||||
if (other == now.end())
|
|
||||||
{
|
|
||||||
result.add(iter->first, EffectParam() - iter->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MagicEffects::writeState(ESM::MagicEffects& state) const
|
void MagicEffects::writeState(ESM::MagicEffects& state) const
|
||||||
{
|
{
|
||||||
for (const auto& [key, params] : mCollection)
|
for (const auto& [key, params] : mCollection)
|
||||||
|
|
|
@ -103,19 +103,12 @@ namespace MWMechanics
|
||||||
void writeState(ESM::MagicEffects& state) const;
|
void writeState(ESM::MagicEffects& state) const;
|
||||||
|
|
||||||
void add(const EffectKey& key, const EffectParam& param);
|
void add(const EffectKey& key, const EffectParam& param);
|
||||||
void remove(const EffectKey& key);
|
|
||||||
|
|
||||||
void modifyBase(const EffectKey& key, int diff);
|
void modifyBase(const EffectKey& key, int diff);
|
||||||
|
|
||||||
/// Copy Modifier values from \a effects, but keep original mBase values.
|
|
||||||
void setModifiers(const MagicEffects& effects);
|
|
||||||
|
|
||||||
EffectParam getOrDefault(const EffectKey& key) const;
|
EffectParam getOrDefault(const EffectKey& key) const;
|
||||||
std::optional<EffectParam> get(const EffectKey& key) const;
|
std::optional<EffectParam> get(const EffectKey& key) const;
|
||||||
///< This function can safely be used for keys that are not present.
|
///< This function can safely be used for keys that are not present.
|
||||||
|
|
||||||
static MagicEffects diff(const MagicEffects& prev, const MagicEffects& now);
|
|
||||||
///< Return changes from \a prev to \a now.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string getMagicEffectString(
|
std::string getMagicEffectString(
|
||||||
|
|
|
@ -241,9 +241,6 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
EsmFile* file = item(index.row());
|
EsmFile* file = item(index.row());
|
||||||
QString fileName = file->fileName();
|
|
||||||
bool success = false;
|
|
||||||
|
|
||||||
switch (role)
|
switch (role)
|
||||||
{
|
{
|
||||||
case Qt::EditRole:
|
case Qt::EditRole:
|
||||||
|
@ -257,65 +254,23 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const
|
||||||
file->setFileProperty(EsmFile::FileProperty_GameFile, list.at(i));
|
file->setFileProperty(EsmFile::FileProperty_GameFile, list.at(i));
|
||||||
|
|
||||||
emit dataChanged(index, index);
|
emit dataChanged(index, index);
|
||||||
|
return true;
|
||||||
success = true;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case Qt::UserRole + 1:
|
case Qt::UserRole + 1:
|
||||||
{
|
{
|
||||||
success = (flags(index) & Qt::ItemIsEnabled);
|
return isEnabled(index) && setCheckState(file, value.toBool());
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
success = setCheckState(file, value.toBool());
|
|
||||||
emit dataChanged(index, index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case Qt::CheckStateRole:
|
case Qt::CheckStateRole:
|
||||||
{
|
{
|
||||||
int checkValue = value.toInt();
|
int checkValue = value.toInt();
|
||||||
bool setState = false;
|
if (checkValue == Qt::Checked)
|
||||||
if (file->builtIn() || file->fromAnotherConfigFile())
|
return mCheckedFiles.contains(file) || setCheckState(file, true);
|
||||||
{
|
if (checkValue == Qt::Unchecked)
|
||||||
setState = false;
|
return !mCheckedFiles.contains(file) || setCheckState(file, false);
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
else if (checkValue == Qt::Checked && !mCheckedFiles.contains(file))
|
|
||||||
{
|
|
||||||
setState = true;
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
else if (checkValue == Qt::Checked && mCheckedFiles.contains(file))
|
|
||||||
setState = true;
|
|
||||||
else if (checkValue == Qt::Unchecked)
|
|
||||||
setState = true;
|
|
||||||
|
|
||||||
if (setState)
|
|
||||||
{
|
|
||||||
setCheckState(file, success);
|
|
||||||
emit dataChanged(index, index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return success;
|
|
||||||
|
|
||||||
for (EsmFile* file2 : mFiles)
|
|
||||||
{
|
|
||||||
if (file2->gameFiles().contains(fileName, Qt::CaseInsensitive))
|
|
||||||
{
|
|
||||||
QModelIndex idx = indexFromItem(file2);
|
|
||||||
emit dataChanged(idx, idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
success = true;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContentSelectorModel::ContentModel::insertRows(int position, int rows, const QModelIndex& parent)
|
bool ContentSelectorModel::ContentModel::insertRows(int position, int rows, const QModelIndex& parent)
|
||||||
|
@ -709,6 +664,7 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileL
|
||||||
{
|
{
|
||||||
// setCheckState already gracefully handles builtIn and fromAnotherConfigFile
|
// setCheckState already gracefully handles builtIn and fromAnotherConfigFile
|
||||||
// as necessary, move plug-ins in visible list to match sequence of supplied filelist
|
// as necessary, move plug-ins in visible list to match sequence of supplied filelist
|
||||||
|
// FIXME: setCheckState also does tons of other things which we don't want to happen
|
||||||
int filePosition = indexFromItem(file).row();
|
int filePosition = indexFromItem(file).row();
|
||||||
if (filePosition < previousPosition)
|
if (filePosition < previousPosition)
|
||||||
{
|
{
|
||||||
|
@ -802,38 +758,37 @@ bool ContentSelectorModel::ContentModel::setCheckState(const EsmFile* file, bool
|
||||||
else
|
else
|
||||||
mCheckedFiles.erase(file);
|
mCheckedFiles.erase(file);
|
||||||
|
|
||||||
emit dataChanged(indexFromItem(file), indexFromItem(file));
|
QModelIndex fileIndex = indexFromItem(file);
|
||||||
|
emit dataChanged(fileIndex, fileIndex);
|
||||||
|
|
||||||
|
// FIXME: this should not happen per-file.
|
||||||
|
// Consider not hiding files if their game is disabled so that this is completely unnecessary.
|
||||||
if (file->isGameFile())
|
if (file->isGameFile())
|
||||||
refreshModel();
|
refreshModel();
|
||||||
|
|
||||||
// if we're checking an item, ensure all "upstream" files (dependencies) are checked as well.
|
// Check "upstream" files (dependencies) if the file is checked,
|
||||||
|
// uncheck downstream files if the file is unchecked.
|
||||||
|
// Update the data for downstream files unconditionally (load order warnings).
|
||||||
|
// FIXME: downstream files of toggled upstream/downstream files should be updated, but that would be slow.
|
||||||
if (checkState)
|
if (checkState)
|
||||||
{
|
{
|
||||||
for (const QString& upstreamName : file->gameFiles())
|
for (const QString& upstreamName : file->gameFiles())
|
||||||
{
|
{
|
||||||
const EsmFile* upstreamFile = item(upstreamName);
|
const EsmFile* upstreamFile = item(upstreamName);
|
||||||
|
if (upstreamFile == nullptr || !mCheckedFiles.insert(upstreamFile).second)
|
||||||
if (!upstreamFile)
|
|
||||||
continue;
|
continue;
|
||||||
|
QModelIndex upstreamIndex = indexFromItem(upstreamFile);
|
||||||
mCheckedFiles.insert(upstreamFile);
|
emit dataChanged(upstreamIndex, upstreamIndex);
|
||||||
|
|
||||||
emit dataChanged(indexFromItem(upstreamFile), indexFromItem(upstreamFile));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// otherwise, if we're unchecking an item (or the file is a game file) ensure all downstream files are unchecked.
|
for (const EsmFile* otherFile : mFiles)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
for (const EsmFile* downstreamFile : mFiles)
|
if (!otherFile->gameFiles().contains(file->fileName(), Qt::CaseInsensitive))
|
||||||
{
|
continue;
|
||||||
if (downstreamFile->gameFiles().contains(file->fileName(), Qt::CaseInsensitive))
|
if (!checkState)
|
||||||
{
|
mCheckedFiles.erase(otherFile);
|
||||||
mCheckedFiles.erase(downstreamFile);
|
QModelIndex otherIndex = indexFromItem(otherFile);
|
||||||
|
emit dataChanged(otherIndex, otherIndex);
|
||||||
emit dataChanged(indexFromItem(downstreamFile), indexFromItem(downstreamFile));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to manually let Bloodmoon entry know if Tribunal is checked/unchecked
|
// Need to manually let Bloodmoon entry know if Tribunal is checked/unchecked
|
||||||
|
|
Loading…
Reference in a new issue