mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-28 05:11:34 +00:00
Merge branch 'master' of gitlab.com:openmw/openmw into lua_controller_cursor
This commit is contained in:
commit
3ef2f71062
154 changed files with 2658 additions and 1683 deletions
|
@ -16,8 +16,10 @@
|
||||||
Bug #4754: Stack of ammunition cannot be equipped partially
|
Bug #4754: Stack of ammunition cannot be equipped partially
|
||||||
Bug #4816: GetWeaponDrawn returns 1 before weapon is attached
|
Bug #4816: GetWeaponDrawn returns 1 before weapon is attached
|
||||||
Bug #4822: Non-weapon equipment and body parts can't inherit time from parent animation
|
Bug #4822: Non-weapon equipment and body parts can't inherit time from parent animation
|
||||||
|
Bug #4898: Odd/Incorrect lighting on meshes
|
||||||
Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses
|
Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses
|
||||||
Bug #5062: Root bone rotations for NPC animation don't work the same as for creature animation
|
Bug #5062: Root bone rotations for NPC animation don't work the same as for creature animation
|
||||||
|
Bug #5065: Actors with scripted animation still try to wander and turn around without moving
|
||||||
Bug #5066: Quirks with starting and stopping scripted animations
|
Bug #5066: Quirks with starting and stopping scripted animations
|
||||||
Bug #5129: Stuttering animation on Centurion Archer
|
Bug #5129: Stuttering animation on Centurion Archer
|
||||||
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
|
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
|
||||||
|
@ -128,7 +130,9 @@
|
||||||
Bug #7742: Governing attribute training limit should use the modified attribute
|
Bug #7742: Governing attribute training limit should use the modified attribute
|
||||||
Bug #7758: Water walking is not taken into account to compute path cost on the water
|
Bug #7758: Water walking is not taken into account to compute path cost on the water
|
||||||
Bug #7761: Rain and ambient loop sounds are mutually exclusive
|
Bug #7761: Rain and ambient loop sounds are mutually exclusive
|
||||||
|
Bug #7765: OpenMW-CS: Touch Record option is broken
|
||||||
Bug #7770: Sword of the Perithia: Script execution failure
|
Bug #7770: Sword of the Perithia: Script execution failure
|
||||||
|
Bug #7780: Non-ASCII texture paths in NIF files don't work
|
||||||
Feature #2566: Handle NAM9 records for manual cell references
|
Feature #2566: Handle NAM9 records for manual cell references
|
||||||
Feature #3537: Shader-based water ripples
|
Feature #3537: Shader-based water ripples
|
||||||
Feature #5173: Support for NiFogProperty
|
Feature #5173: Support for NiFogProperty
|
||||||
|
|
|
@ -174,7 +174,7 @@ namespace
|
||||||
|
|
||||||
constexpr double expiryDelay = 0;
|
constexpr double expiryDelay = 0;
|
||||||
Resource::ImageManager imageManager(&vfs, expiryDelay);
|
Resource::ImageManager imageManager(&vfs, expiryDelay);
|
||||||
Resource::NifFileManager nifFileManager(&vfs);
|
Resource::NifFileManager nifFileManager(&vfs, &encoder.getStatelessEncoder());
|
||||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay);
|
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay);
|
||||||
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
|
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ namespace ESSImport
|
||||||
Misc::StringUtils::lowerCaseInPlace(group);
|
Misc::StringUtils::lowerCaseInPlace(group);
|
||||||
|
|
||||||
ESM::AnimationState::ScriptedAnimation scriptedAnim;
|
ESM::AnimationState::ScriptedAnimation scriptedAnim;
|
||||||
scriptedAnim.mGroup = group;
|
scriptedAnim.mGroup = std::move(group);
|
||||||
scriptedAnim.mTime = anis.mTime;
|
scriptedAnim.mTime = anis.mTime;
|
||||||
scriptedAnim.mAbsolute = true;
|
scriptedAnim.mAbsolute = true;
|
||||||
// Neither loop count nor queueing seems to be supported by the ess format.
|
// Neither loop count nor queueing seems to be supported by the ess format.
|
||||||
|
|
|
@ -306,12 +306,12 @@ namespace ESSImport
|
||||||
mMarkers.push_back(marker);
|
mMarkers.push_back(marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
newcell.mRefs = cellrefs;
|
newcell.mRefs = std::move(cellrefs);
|
||||||
|
|
||||||
if (cell.isExterior())
|
if (cell.isExterior())
|
||||||
mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell;
|
mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = std::move(newcell);
|
||||||
else
|
else
|
||||||
mIntCells[cell.mName] = newcell;
|
mIntCells[cell.mName] = std::move(newcell);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvertCell::writeCell(const Cell& cell, ESM::ESMWriter& esm)
|
void ConvertCell::writeCell(const Cell& cell, ESM::ESMWriter& esm)
|
||||||
|
|
|
@ -34,7 +34,6 @@ Launcher::GraphicsPage::GraphicsPage(QWidget* parent)
|
||||||
connect(standardRadioButton, &QRadioButton::toggled, this, &GraphicsPage::slotStandardToggled);
|
connect(standardRadioButton, &QRadioButton::toggled, this, &GraphicsPage::slotStandardToggled);
|
||||||
connect(screenComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &GraphicsPage::screenChanged);
|
connect(screenComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &GraphicsPage::screenChanged);
|
||||||
connect(framerateLimitCheckBox, &QCheckBox::toggled, this, &GraphicsPage::slotFramerateLimitToggled);
|
connect(framerateLimitCheckBox, &QCheckBox::toggled, this, &GraphicsPage::slotFramerateLimitToggled);
|
||||||
connect(shadowDistanceCheckBox, &QCheckBox::toggled, this, &GraphicsPage::slotShadowDistLimitToggled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Launcher::GraphicsPage::setupSDL()
|
bool Launcher::GraphicsPage::setupSDL()
|
||||||
|
@ -126,58 +125,6 @@ bool Launcher::GraphicsPage::loadSettings()
|
||||||
framerateLimitSpinBox->setValue(fpsLimit);
|
framerateLimitSpinBox->setValue(fpsLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lighting
|
|
||||||
int lightingMethod = 1;
|
|
||||||
switch (Settings::shaders().mLightingMethod)
|
|
||||||
{
|
|
||||||
case SceneUtil::LightingMethod::FFP:
|
|
||||||
lightingMethod = 0;
|
|
||||||
break;
|
|
||||||
case SceneUtil::LightingMethod::PerObjectUniform:
|
|
||||||
lightingMethod = 1;
|
|
||||||
break;
|
|
||||||
case SceneUtil::LightingMethod::SingleUBO:
|
|
||||||
lightingMethod = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lightingMethodComboBox->setCurrentIndex(lightingMethod);
|
|
||||||
|
|
||||||
// Shadows
|
|
||||||
if (Settings::shadows().mActorShadows)
|
|
||||||
actorShadowsCheckBox->setCheckState(Qt::Checked);
|
|
||||||
if (Settings::shadows().mPlayerShadows)
|
|
||||||
playerShadowsCheckBox->setCheckState(Qt::Checked);
|
|
||||||
if (Settings::shadows().mTerrainShadows)
|
|
||||||
terrainShadowsCheckBox->setCheckState(Qt::Checked);
|
|
||||||
if (Settings::shadows().mObjectShadows)
|
|
||||||
objectShadowsCheckBox->setCheckState(Qt::Checked);
|
|
||||||
if (Settings::shadows().mEnableIndoorShadows)
|
|
||||||
indoorShadowsCheckBox->setCheckState(Qt::Checked);
|
|
||||||
|
|
||||||
const auto& boundMethod = Settings::shadows().mComputeSceneBounds.get();
|
|
||||||
if (boundMethod == "bounds")
|
|
||||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(0);
|
|
||||||
else if (boundMethod == "primitives")
|
|
||||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(1);
|
|
||||||
else
|
|
||||||
shadowComputeSceneBoundsComboBox->setCurrentIndex(2);
|
|
||||||
|
|
||||||
const int shadowDistLimit = Settings::shadows().mMaximumShadowMapDistance;
|
|
||||||
if (shadowDistLimit > 0)
|
|
||||||
{
|
|
||||||
shadowDistanceCheckBox->setCheckState(Qt::Checked);
|
|
||||||
shadowDistanceSpinBox->setValue(shadowDistLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
const float shadowFadeStart = Settings::shadows().mShadowFadeStart;
|
|
||||||
if (shadowFadeStart != 0)
|
|
||||||
fadeStartSpinBox->setValue(shadowFadeStart);
|
|
||||||
|
|
||||||
const int shadowRes = Settings::shadows().mShadowMapResolution;
|
|
||||||
int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes));
|
|
||||||
if (shadowResIndex != -1)
|
|
||||||
shadowResolutionComboBox->setCurrentIndex(shadowResIndex);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,53 +167,6 @@ void Launcher::GraphicsPage::saveSettings()
|
||||||
{
|
{
|
||||||
Settings::video().mFramerateLimit.set(0);
|
Settings::video().mFramerateLimit.set(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lighting
|
|
||||||
static constexpr std::array<SceneUtil::LightingMethod, 3> lightingMethodMap = {
|
|
||||||
SceneUtil::LightingMethod::FFP,
|
|
||||||
SceneUtil::LightingMethod::PerObjectUniform,
|
|
||||||
SceneUtil::LightingMethod::SingleUBO,
|
|
||||||
};
|
|
||||||
Settings::shaders().mLightingMethod.set(lightingMethodMap[lightingMethodComboBox->currentIndex()]);
|
|
||||||
|
|
||||||
// Shadows
|
|
||||||
const int cShadowDist = shadowDistanceCheckBox->checkState() != Qt::Unchecked ? shadowDistanceSpinBox->value() : 0;
|
|
||||||
Settings::shadows().mMaximumShadowMapDistance.set(cShadowDist);
|
|
||||||
const float cFadeStart = fadeStartSpinBox->value();
|
|
||||||
if (cShadowDist > 0)
|
|
||||||
Settings::shadows().mShadowFadeStart.set(cFadeStart);
|
|
||||||
|
|
||||||
const bool cActorShadows = actorShadowsCheckBox->checkState() != Qt::Unchecked;
|
|
||||||
const bool cObjectShadows = objectShadowsCheckBox->checkState() != Qt::Unchecked;
|
|
||||||
const bool cTerrainShadows = terrainShadowsCheckBox->checkState() != Qt::Unchecked;
|
|
||||||
const bool cPlayerShadows = playerShadowsCheckBox->checkState() != Qt::Unchecked;
|
|
||||||
if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)
|
|
||||||
{
|
|
||||||
Settings::shadows().mEnableShadows.set(true);
|
|
||||||
Settings::shadows().mActorShadows.set(cActorShadows);
|
|
||||||
Settings::shadows().mPlayerShadows.set(cPlayerShadows);
|
|
||||||
Settings::shadows().mObjectShadows.set(cObjectShadows);
|
|
||||||
Settings::shadows().mTerrainShadows.set(cTerrainShadows);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Settings::shadows().mEnableShadows.set(false);
|
|
||||||
Settings::shadows().mActorShadows.set(false);
|
|
||||||
Settings::shadows().mPlayerShadows.set(false);
|
|
||||||
Settings::shadows().mObjectShadows.set(false);
|
|
||||||
Settings::shadows().mTerrainShadows.set(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings::shadows().mEnableIndoorShadows.set(indoorShadowsCheckBox->checkState() != Qt::Unchecked);
|
|
||||||
Settings::shadows().mShadowMapResolution.set(shadowResolutionComboBox->currentText().toInt());
|
|
||||||
|
|
||||||
auto index = shadowComputeSceneBoundsComboBox->currentIndex();
|
|
||||||
if (index == 0)
|
|
||||||
Settings::shadows().mComputeSceneBounds.set("bounds");
|
|
||||||
else if (index == 1)
|
|
||||||
Settings::shadows().mComputeSceneBounds.set("primitives");
|
|
||||||
else
|
|
||||||
Settings::shadows().mComputeSceneBounds.set("none");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
|
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
|
||||||
|
@ -377,9 +277,3 @@ void Launcher::GraphicsPage::slotFramerateLimitToggled(bool checked)
|
||||||
{
|
{
|
||||||
framerateLimitSpinBox->setEnabled(checked);
|
framerateLimitSpinBox->setEnabled(checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::GraphicsPage::slotShadowDistLimitToggled(bool checked)
|
|
||||||
{
|
|
||||||
shadowDistanceSpinBox->setEnabled(checked);
|
|
||||||
fadeStartSpinBox->setEnabled(checked);
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ namespace Launcher
|
||||||
void slotFullScreenChanged(int state);
|
void slotFullScreenChanged(int state);
|
||||||
void slotStandardToggled(bool checked);
|
void slotStandardToggled(bool checked);
|
||||||
void slotFramerateLimitToggled(bool checked);
|
void slotFramerateLimitToggled(bool checked);
|
||||||
void slotShadowDistLimitToggled(bool checked);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<QStringList> mResolutionsPerScreen;
|
QVector<QStringList> mResolutionsPerScreen;
|
||||||
|
|
|
@ -212,6 +212,65 @@ bool Launcher::SettingsPage::loadSettings()
|
||||||
loadSettingBool(Settings::fog().mExponentialFog, *exponentialFogCheckBox);
|
loadSettingBool(Settings::fog().mExponentialFog, *exponentialFogCheckBox);
|
||||||
loadSettingBool(Settings::fog().mSkyBlending, *skyBlendingCheckBox);
|
loadSettingBool(Settings::fog().mSkyBlending, *skyBlendingCheckBox);
|
||||||
skyBlendingStartComboBox->setValue(Settings::fog().mSkyBlendingStart);
|
skyBlendingStartComboBox->setValue(Settings::fog().mSkyBlendingStart);
|
||||||
|
|
||||||
|
loadSettingBool(Settings::shadows().mActorShadows, *actorShadowsCheckBox);
|
||||||
|
loadSettingBool(Settings::shadows().mPlayerShadows, *playerShadowsCheckBox);
|
||||||
|
loadSettingBool(Settings::shadows().mTerrainShadows, *terrainShadowsCheckBox);
|
||||||
|
loadSettingBool(Settings::shadows().mObjectShadows, *objectShadowsCheckBox);
|
||||||
|
loadSettingBool(Settings::shadows().mEnableIndoorShadows, *indoorShadowsCheckBox);
|
||||||
|
|
||||||
|
const auto& boundMethod = Settings::shadows().mComputeSceneBounds.get();
|
||||||
|
if (boundMethod == "bounds")
|
||||||
|
shadowComputeSceneBoundsComboBox->setCurrentIndex(0);
|
||||||
|
else if (boundMethod == "primitives")
|
||||||
|
shadowComputeSceneBoundsComboBox->setCurrentIndex(1);
|
||||||
|
else
|
||||||
|
shadowComputeSceneBoundsComboBox->setCurrentIndex(2);
|
||||||
|
|
||||||
|
const int shadowDistLimit = Settings::shadows().mMaximumShadowMapDistance;
|
||||||
|
if (shadowDistLimit > 0)
|
||||||
|
{
|
||||||
|
shadowDistanceCheckBox->setCheckState(Qt::Checked);
|
||||||
|
shadowDistanceSpinBox->setValue(shadowDistLimit);
|
||||||
|
shadowDistanceSpinBox->setEnabled(true);
|
||||||
|
fadeStartSpinBox->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float shadowFadeStart = Settings::shadows().mShadowFadeStart;
|
||||||
|
if (shadowFadeStart != 0)
|
||||||
|
fadeStartSpinBox->setValue(shadowFadeStart);
|
||||||
|
|
||||||
|
const int shadowRes = Settings::shadows().mShadowMapResolution;
|
||||||
|
int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes));
|
||||||
|
if (shadowResIndex != -1)
|
||||||
|
shadowResolutionComboBox->setCurrentIndex(shadowResIndex);
|
||||||
|
|
||||||
|
connect(shadowDistanceCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotShadowDistLimitToggled);
|
||||||
|
|
||||||
|
lightsMaxLightsSpinBox->setValue(Settings::shaders().mMaxLights);
|
||||||
|
lightsMaximumDistanceSpinBox->setValue(Settings::shaders().mMaximumLightDistance);
|
||||||
|
lightFadeMultiplierSpinBox->setValue(Settings::shaders().mLightFadeStart);
|
||||||
|
lightsBoundingSphereMultiplierSpinBox->setValue(Settings::shaders().mLightBoundsMultiplier);
|
||||||
|
lightsMinimumInteriorBrightnessSpinBox->setValue(Settings::shaders().mMinimumInteriorBrightness);
|
||||||
|
|
||||||
|
connect(lightingMethodComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||||
|
&SettingsPage::slotLightTypeCurrentIndexChanged);
|
||||||
|
|
||||||
|
int lightingMethod = 1;
|
||||||
|
switch (Settings::shaders().mLightingMethod)
|
||||||
|
{
|
||||||
|
case SceneUtil::LightingMethod::FFP:
|
||||||
|
lightingMethod = 0;
|
||||||
|
break;
|
||||||
|
case SceneUtil::LightingMethod::PerObjectUniform:
|
||||||
|
lightingMethod = 1;
|
||||||
|
break;
|
||||||
|
case SceneUtil::LightingMethod::SingleUBO:
|
||||||
|
lightingMethod = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lightingMethodComboBox->setCurrentIndex(lightingMethod);
|
||||||
|
slotLightTypeCurrentIndexChanged(lightingMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
|
@ -359,6 +418,58 @@ void Launcher::SettingsPage::saveSettings()
|
||||||
saveSettingBool(*exponentialFogCheckBox, Settings::fog().mExponentialFog);
|
saveSettingBool(*exponentialFogCheckBox, Settings::fog().mExponentialFog);
|
||||||
saveSettingBool(*skyBlendingCheckBox, Settings::fog().mSkyBlending);
|
saveSettingBool(*skyBlendingCheckBox, Settings::fog().mSkyBlending);
|
||||||
Settings::fog().mSkyBlendingStart.set(skyBlendingStartComboBox->value());
|
Settings::fog().mSkyBlendingStart.set(skyBlendingStartComboBox->value());
|
||||||
|
|
||||||
|
static constexpr std::array<SceneUtil::LightingMethod, 3> lightingMethodMap = {
|
||||||
|
SceneUtil::LightingMethod::FFP,
|
||||||
|
SceneUtil::LightingMethod::PerObjectUniform,
|
||||||
|
SceneUtil::LightingMethod::SingleUBO,
|
||||||
|
};
|
||||||
|
Settings::shaders().mLightingMethod.set(lightingMethodMap[lightingMethodComboBox->currentIndex()]);
|
||||||
|
|
||||||
|
const int cShadowDist
|
||||||
|
= shadowDistanceCheckBox->checkState() != Qt::Unchecked ? shadowDistanceSpinBox->value() : 0;
|
||||||
|
Settings::shadows().mMaximumShadowMapDistance.set(cShadowDist);
|
||||||
|
const float cFadeStart = fadeStartSpinBox->value();
|
||||||
|
if (cShadowDist > 0)
|
||||||
|
Settings::shadows().mShadowFadeStart.set(cFadeStart);
|
||||||
|
|
||||||
|
const bool cActorShadows = actorShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||||
|
const bool cObjectShadows = objectShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||||
|
const bool cTerrainShadows = terrainShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||||
|
const bool cPlayerShadows = playerShadowsCheckBox->checkState() != Qt::Unchecked;
|
||||||
|
if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)
|
||||||
|
{
|
||||||
|
Settings::shadows().mEnableShadows.set(true);
|
||||||
|
Settings::shadows().mActorShadows.set(cActorShadows);
|
||||||
|
Settings::shadows().mPlayerShadows.set(cPlayerShadows);
|
||||||
|
Settings::shadows().mObjectShadows.set(cObjectShadows);
|
||||||
|
Settings::shadows().mTerrainShadows.set(cTerrainShadows);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Settings::shadows().mEnableShadows.set(false);
|
||||||
|
Settings::shadows().mActorShadows.set(false);
|
||||||
|
Settings::shadows().mPlayerShadows.set(false);
|
||||||
|
Settings::shadows().mObjectShadows.set(false);
|
||||||
|
Settings::shadows().mTerrainShadows.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::shadows().mEnableIndoorShadows.set(indoorShadowsCheckBox->checkState() != Qt::Unchecked);
|
||||||
|
Settings::shadows().mShadowMapResolution.set(shadowResolutionComboBox->currentText().toInt());
|
||||||
|
|
||||||
|
auto index = shadowComputeSceneBoundsComboBox->currentIndex();
|
||||||
|
if (index == 0)
|
||||||
|
Settings::shadows().mComputeSceneBounds.set("bounds");
|
||||||
|
else if (index == 1)
|
||||||
|
Settings::shadows().mComputeSceneBounds.set("primitives");
|
||||||
|
else
|
||||||
|
Settings::shadows().mComputeSceneBounds.set("none");
|
||||||
|
|
||||||
|
Settings::shaders().mMaxLights.set(lightsMaxLightsSpinBox->value());
|
||||||
|
Settings::shaders().mMaximumLightDistance.set(lightsMaximumDistanceSpinBox->value());
|
||||||
|
Settings::shaders().mLightFadeStart.set(lightFadeMultiplierSpinBox->value());
|
||||||
|
Settings::shaders().mLightBoundsMultiplier.set(lightsBoundingSphereMultiplierSpinBox->value());
|
||||||
|
Settings::shaders().mMinimumInteriorBrightness.set(lightsMinimumInteriorBrightnessSpinBox->value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
|
@ -461,3 +572,17 @@ void Launcher::SettingsPage::slotSkyBlendingToggled(bool checked)
|
||||||
skyBlendingStartComboBox->setEnabled(checked);
|
skyBlendingStartComboBox->setEnabled(checked);
|
||||||
skyBlendingStartLabel->setEnabled(checked);
|
skyBlendingStartLabel->setEnabled(checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Launcher::SettingsPage::slotShadowDistLimitToggled(bool checked)
|
||||||
|
{
|
||||||
|
shadowDistanceSpinBox->setEnabled(checked);
|
||||||
|
fadeStartSpinBox->setEnabled(checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Launcher::SettingsPage::slotLightTypeCurrentIndexChanged(int index)
|
||||||
|
{
|
||||||
|
lightsMaximumDistanceSpinBox->setEnabled(index != 0);
|
||||||
|
lightsMaxLightsSpinBox->setEnabled(index != 0);
|
||||||
|
lightsBoundingSphereMultiplierSpinBox->setEnabled(index != 0);
|
||||||
|
lightsMinimumInteriorBrightnessSpinBox->setEnabled(index != 0);
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ namespace Launcher
|
||||||
void slotAnimSourcesToggled(bool checked);
|
void slotAnimSourcesToggled(bool checked);
|
||||||
void slotPostProcessToggled(bool checked);
|
void slotPostProcessToggled(bool checked);
|
||||||
void slotSkyBlendingToggled(bool checked);
|
void slotSkyBlendingToggled(bool checked);
|
||||||
|
void slotShadowDistLimitToggled(bool checked);
|
||||||
|
void slotLightTypeCurrentIndexChanged(int index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Config::GameSettings& mGameSettings;
|
Config::GameSettings& mGameSettings;
|
||||||
|
|
|
@ -11,459 +11,229 @@
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item alignment="Qt::AlignTop">
|
<item>
|
||||||
<widget class="QTabWidget" name="DisplayTabWidget">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
<property name="currentIndex">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<number>0</number>
|
<item>
|
||||||
</property>
|
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
||||||
<widget class="QWidget" name="DisplayWrapper">
|
<item row="5" column="0">
|
||||||
<attribute name="title">
|
<widget class="QLabel" name="screenLabel">
|
||||||
<string>Display</string>
|
<property name="text">
|
||||||
</attribute>
|
<string>Screen</string>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
</property>
|
||||||
<item>
|
</widget>
|
||||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="screenLabel">
|
<widget class="QLabel" name="windowModeLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Window mode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<layout class="QGridLayout" name="resolutionLayout">
|
||||||
|
<item row="1" column="2">
|
||||||
|
<layout class="QHBoxLayout" name="customResolutionLayout" stretch="1,0,1">
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="customWidthSpinBox">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>800</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="multiplyLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string> × </string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="customHeightSpinBox">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>600</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QRadioButton" name="customRadioButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Custom:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QRadioButton" name="standardRadioButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Standard:</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QComboBox" name="resolutionComboBox"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QComboBox" name="antiAliasingComboBox">
|
||||||
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Screen:</string>
|
<string>0</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
<item>
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QComboBox" name="antiAliasingComboBox">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>0</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>2</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>4</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>8</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>16</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="1">
|
|
||||||
<widget class="QComboBox" name="screenComboBox"/>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="windowModeLabel">
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Window Mode:</string>
|
<string>2</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
<item>
|
||||||
<item row="6" column="0">
|
|
||||||
<widget class="QLabel" name="resolutionLabel">
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Resolution:</string>
|
<string>4</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
</item>
|
||||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
<item>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="vSyncLabel">
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Vertical Sync:</string>
|
<string>8</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
<item>
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="antiAliasingLabel">
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Anti-aliasing:</string>
|
<string>16</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
</widget>
|
||||||
<item row="6" column="1">
|
</item>
|
||||||
<layout class="QGridLayout" name="resolutionLayout">
|
<item row="1" column="0">
|
||||||
<item row="1" column="2">
|
<widget class="QCheckBox" name="framerateLimitCheckBox">
|
||||||
<layout class="QHBoxLayout" name="customResolutionLayout" stretch="1,0,1">
|
<property name="text">
|
||||||
<item>
|
<string>Framerate limit</string>
|
||||||
<widget class="QSpinBox" name="customWidthSpinBox">
|
</property>
|
||||||
<property name="minimum">
|
</widget>
|
||||||
<number>800</number>
|
</item>
|
||||||
</property>
|
<item row="0" column="0">
|
||||||
</widget>
|
<widget class="QCheckBox" name="windowBorderCheckBox">
|
||||||
</item>
|
<property name="text">
|
||||||
<item>
|
<string>Window border</string>
|
||||||
<widget class="QLabel" name="multiplyLabel">
|
</property>
|
||||||
<property name="text">
|
</widget>
|
||||||
<string> × </string>
|
</item>
|
||||||
</property>
|
<item row="5" column="1">
|
||||||
</widget>
|
<widget class="QComboBox" name="screenComboBox"/>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="2" column="1">
|
||||||
<widget class="QSpinBox" name="customHeightSpinBox">
|
<widget class="QComboBox" name="vSyncComboBox">
|
||||||
<property name="minimum">
|
<property name="currentIndex">
|
||||||
<number>600</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
<item>
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QRadioButton" name="customRadioButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Custom:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QRadioButton" name="standardRadioButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Standard:</string>
|
|
||||||
</property>
|
|
||||||
<property name="checkable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="2">
|
|
||||||
<widget class="QComboBox" name="resolutionComboBox"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QComboBox" name="windowModeComboBox">
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Fullscreen</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Windowed Fullscreen</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Windowed</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QCheckBox" name="windowBorderCheckBox">
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Window Border</string>
|
<string>Disabled</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
<item>
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QComboBox" name="vSyncComboBox">
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Disabled</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Enabled</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Adaptive</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QCheckBox" name="framerateLimitCheckBox">
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Framerate Limit:</string>
|
<string>Enabled</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
<item>
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QDoubleSpinBox" name="framerateLimitSpinBox">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="suffix">
|
|
||||||
<string> FPS</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<double>1.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>1000.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="singleStep">
|
|
||||||
<double>15.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<double>300.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="LightingWrapper">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Lighting</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QVBoxLayout" name="lightingLayout">
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="lightingMethodLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="lightingMethodLabel">
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Lighting Method:</string>
|
<string>Adaptive</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="lightingMethodComboBox">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>legacy</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>shaders compatibility</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>shaders</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="ShadowWrapper">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Shadows</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<layout class="QGridLayout" name="shadowsLayout" columnstretch="0,0">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QCheckBox" name="playerShadowsCheckBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Enable shadows exclusively for the player character. May have a very minor performance impact.</p></body></html></string>
|
|
||||||
</property>
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QComboBox" name="windowModeComboBox">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Player Shadows</string>
|
<string>Fullscreen</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QCheckBox" name="actorShadowsCheckBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Enable shadows for NPCs and creatures besides the player character. May have a minor performance impact.</p></body></html></string>
|
|
||||||
</property>
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Actor Shadows</string>
|
<string>Windowed Fullscreen</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QCheckBox" name="objectShadowsCheckBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Enable shadows for primarily inanimate objects. May have a significant performance impact.</p></body></html></string>
|
|
||||||
</property>
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Object Shadows</string>
|
<string>Windowed</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
</widget>
|
||||||
<item row="3" column="0">
|
</item>
|
||||||
<widget class="QCheckBox" name="terrainShadowsCheckBox">
|
<item row="6" column="0">
|
||||||
<property name="toolTip">
|
<widget class="QLabel" name="resolutionLabel">
|
||||||
<string><html><head/><body><p>Enable shadows for the terrain including distant terrain. May have a significant performance and shadow quality impact.</p></body></html></string>
|
<property name="text">
|
||||||
</property>
|
<string>Resolution</string>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>Enable Terrain Shadows</string>
|
<property name="alignment">
|
||||||
</property>
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
</widget>
|
||||||
<item row="4" column="0">
|
</item>
|
||||||
<widget class="QCheckBox" name="indoorShadowsCheckBox">
|
<item row="1" column="1">
|
||||||
<property name="toolTip">
|
<widget class="QDoubleSpinBox" name="framerateLimitSpinBox">
|
||||||
<string><html><head/><body><p>Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.</p><p>Has no effect if actor/player shadows are not enabled.</p></body></html></string>
|
<property name="enabled">
|
||||||
</property>
|
<bool>false</bool>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>Enable Indoor Shadows</string>
|
<property name="suffix">
|
||||||
</property>
|
<string> FPS</string>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
<property name="decimals">
|
||||||
<item row="5" column="0">
|
<number>1</number>
|
||||||
<widget class="QLabel" name="shadowComputeSceneBoundsLabel">
|
</property>
|
||||||
<property name="toolTip">
|
<property name="minimum">
|
||||||
<string><html><head/><body><p>Type of "compute scene bounds" computation method to be used. Bounds (default) for good balance between performance and shadow quality, primitives for better looking shadows or none for no computation.</p></body></html></string>
|
<double>1.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="maximum">
|
||||||
<string>Shadow Near Far Computation Method:</string>
|
<double>1000.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
<property name="singleStep">
|
||||||
</item>
|
<double>15.000000000000000</double>
|
||||||
<item row="5" column="1">
|
</property>
|
||||||
<widget class="QComboBox" name="shadowComputeSceneBoundsComboBox">
|
<property name="value">
|
||||||
<item>
|
<double>300.000000000000000</double>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>bounds</string>
|
</widget>
|
||||||
</property>
|
</item>
|
||||||
</item>
|
<item row="4" column="0">
|
||||||
<item>
|
<widget class="QLabel" name="antiAliasingLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>primitives</string>
|
<string>Anti-aliasing</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</widget>
|
||||||
<item>
|
</item>
|
||||||
<property name="text">
|
<item row="2" column="0">
|
||||||
<string>none</string>
|
<widget class="QLabel" name="vSyncLabel">
|
||||||
</property>
|
<property name="text">
|
||||||
</item>
|
<string>Vertical synchronization</string>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
</widget>
|
||||||
<item row="6" column="0">
|
</item>
|
||||||
<widget class="QLabel" name="shadowResolutionLabel">
|
<item row="7" column="0">
|
||||||
<property name="toolTip">
|
<spacer name="verticalSpacer">
|
||||||
<string><html><head/><body><p>The resolution of each individual shadow map. Increasing it significantly improves shadow quality but may have a minor performance impact.</p></body></html></string>
|
<property name="orientation">
|
||||||
</property>
|
<enum>Qt::Vertical</enum>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>Shadow Map Resolution:</string>
|
<property name="sizeHint" stdset="0">
|
||||||
</property>
|
<size>
|
||||||
</widget>
|
<width>20</width>
|
||||||
</item>
|
<height>40</height>
|
||||||
<item row="6" column="1">
|
</size>
|
||||||
<widget class="QComboBox" name="shadowResolutionComboBox">
|
</property>
|
||||||
<item>
|
</spacer>
|
||||||
<property name="text">
|
</item>
|
||||||
<string>512</string>
|
</layout>
|
||||||
</property>
|
</item>
|
||||||
</item>
|
</layout>
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>1024</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>2048</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>4096</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="0">
|
|
||||||
<widget class="QCheckBox" name="shadowDistanceCheckBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>The distance from the camera at which shadows completely disappear.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Shadow Distance Limit:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="1">
|
|
||||||
<widget class="QSpinBox" name="shadowDistanceSpinBox">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>64 game units is 1 real life yard or about 0.9 m</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="suffix">
|
|
||||||
<string> unit(s)</string>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<number>512</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>81920</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<number>8192</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="0">
|
|
||||||
<widget class="QLabel" name="fadeStartLabel">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>The fraction of the limit above at which shadows begin to gradually fade away.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Fade Start Multiplier:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="1">
|
|
||||||
<widget class="QDoubleSpinBox" name="fadeStartSpinBox">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<double>0.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>1.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<double>0.900000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>514</width>
|
<width>515</width>
|
||||||
<height>397</height>
|
<height>397</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -129,16 +129,22 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|
|
@ -7,20 +7,20 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>720</width>
|
<width>720</width>
|
||||||
<height>565</height>
|
<height>635</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>720</width>
|
<width>720</width>
|
||||||
<height>565</height>
|
<height>635</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>OpenMW Launcher</string>
|
<string>OpenMW Launcher</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset resource="../launcher/launcher.qrc">
|
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||||
<normaloff>:/images/openmw.png</normaloff>:/images/openmw.png</iconset>
|
<normaloff>:/images/openmw.png</normaloff>:/images/openmw.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
@ -120,7 +120,7 @@ QToolButton {
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../launcher/launcher.qrc">
|
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||||
<normaloff>:/images/openmw-plugin.png</normaloff>:/images/openmw-plugin.png</iconset>
|
<normaloff>:/images/openmw-plugin.png</normaloff>:/images/openmw-plugin.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -141,11 +141,11 @@ QToolButton {
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../launcher/launcher.qrc">
|
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||||
<normaloff>:/images/preferences-video.png</normaloff>:/images/preferences-video.png</iconset>
|
<normaloff>:/images/preferences-video.png</normaloff>:/images/preferences-video.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Graphics</string>
|
<string>Display</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Allows to change graphics settings</string>
|
<string>Allows to change graphics settings</string>
|
||||||
|
@ -156,7 +156,7 @@ QToolButton {
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../launcher/launcher.qrc">
|
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||||
<normaloff>:/images/preferences.png</normaloff>:/images/preferences.png</iconset>
|
<normaloff>:/images/preferences.png</normaloff>:/images/preferences.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -171,7 +171,7 @@ QToolButton {
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../launcher/launcher.qrc">
|
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||||
<normaloff>:/images/preferences-advanced.png</normaloff>:/images/preferences-advanced.png</iconset>
|
<normaloff>:/images/preferences-advanced.png</normaloff>:/images/preferences-advanced.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -183,7 +183,7 @@ QToolButton {
|
||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../launcher/launcher.qrc"/>
|
<include location="../../../files/launcher/launcher.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -221,7 +221,7 @@ namespace NavMeshTool
|
||||||
constexpr double expiryDelay = 0;
|
constexpr double expiryDelay = 0;
|
||||||
|
|
||||||
Resource::ImageManager imageManager(&vfs, expiryDelay);
|
Resource::ImageManager imageManager(&vfs, expiryDelay);
|
||||||
Resource::NifFileManager nifFileManager(&vfs);
|
Resource::NifFileManager nifFileManager(&vfs, &encoder.getStatelessEncoder());
|
||||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay);
|
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay);
|
||||||
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
|
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
|
||||||
DetourNavigator::RecastGlobalAllocator::init();
|
DetourNavigator::RecastGlobalAllocator::init();
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <components/vfs/bsaarchive.hpp>
|
#include <components/vfs/bsaarchive.hpp>
|
||||||
#include <components/vfs/filesystemarchive.hpp>
|
#include <components/vfs/filesystemarchive.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
|
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ void readNIF(
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Nif::NIFFile file(fullPath);
|
Nif::NIFFile file(fullPath);
|
||||||
Nif::Reader reader(file);
|
Nif::Reader reader(file, nullptr);
|
||||||
if (vfs != nullptr)
|
if (vfs != nullptr)
|
||||||
reader.parse(vfs->get(pathStr));
|
reader.parse(vfs->get(pathStr));
|
||||||
else
|
else
|
||||||
|
|
|
@ -624,7 +624,7 @@ bool CSMFilter::Parser::parse(const std::string& filter, bool allowPredefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node)
|
if (node)
|
||||||
mFilter = node;
|
mFilter = std::move(node);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Empty filter string equals to filter "true".
|
// Empty filter string equals to filter "true".
|
||||||
|
|
|
@ -171,7 +171,7 @@ void CSMTools::ReportModel::flagAsReplaced(int index)
|
||||||
|
|
||||||
hint[0] = 'r';
|
hint[0] = 'r';
|
||||||
|
|
||||||
line.mHint = hint;
|
line.mHint = std::move(hint);
|
||||||
|
|
||||||
emit dataChanged(this->index(index, 0), this->index(index, columnCount()));
|
emit dataChanged(this->index(index, 0), this->index(index, columnCount()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -468,13 +468,13 @@ namespace CSMWorld
|
||||||
if (type == UniversalId::Type_Creature)
|
if (type == UniversalId::Type_Creature)
|
||||||
{
|
{
|
||||||
// Valid creature record
|
// Valid creature record
|
||||||
setupCreature(id, data);
|
setupCreature(id, std::move(data));
|
||||||
emit actorChanged(id);
|
emit actorChanged(id);
|
||||||
}
|
}
|
||||||
else if (type == UniversalId::Type_Npc)
|
else if (type == UniversalId::Type_Npc)
|
||||||
{
|
{
|
||||||
// Valid npc record
|
// Valid npc record
|
||||||
setupNpc(id, data);
|
setupNpc(id, std::move(data));
|
||||||
emit actorChanged(id);
|
emit actorChanged(id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -665,7 +665,7 @@ namespace CSMWorld
|
||||||
RaceDataPtr data = mCachedRaces.get(race);
|
RaceDataPtr data = mCachedRaces.get(race);
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
setupRace(race, data);
|
setupRace(race, std::move(data));
|
||||||
// Race was changed. Need to mark actor dependencies as dirty.
|
// Race was changed. Need to mark actor dependencies as dirty.
|
||||||
// Cannot use markDirtyDependency because that would invalidate
|
// Cannot use markDirtyDependency because that would invalidate
|
||||||
// the current iterator.
|
// the current iterator.
|
||||||
|
@ -683,7 +683,7 @@ namespace CSMWorld
|
||||||
ActorDataPtr data = mCachedActors.get(actor);
|
ActorDataPtr data = mCachedActors.get(actor);
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
setupActor(actor, data);
|
setupActor(actor, std::move(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mDirtyActors.clear();
|
mDirtyActors.clear();
|
||||||
|
|
|
@ -504,7 +504,7 @@ namespace CSMWorld
|
||||||
|
|
||||||
auto record2 = std::make_unique<Record<ESXRecordT>>();
|
auto record2 = std::make_unique<Record<ESXRecordT>>();
|
||||||
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
|
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
|
||||||
record2->mModified = record;
|
record2->mModified = std::move(record);
|
||||||
|
|
||||||
insertRecord(std::move(record2), getAppendIndex(id, type), type);
|
insertRecord(std::move(record2), getAppendIndex(id, type), type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ CSMWorld::TouchCommand::TouchCommand(IdTable& table, const std::string& id, QUnd
|
||||||
|
|
||||||
void CSMWorld::TouchCommand::redo()
|
void CSMWorld::TouchCommand::redo()
|
||||||
{
|
{
|
||||||
mOld.reset(mTable.getRecord(mId).clone().get());
|
mOld = mTable.getRecord(mId).clone();
|
||||||
mChanged = mTable.touchRecord(mId);
|
mChanged = mTable.touchRecord(mId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,9 +181,8 @@ const std::string& CSMWorld::TouchLandCommand::getDestinationId() const
|
||||||
|
|
||||||
void CSMWorld::TouchLandCommand::onRedo()
|
void CSMWorld::TouchLandCommand::onRedo()
|
||||||
{
|
{
|
||||||
|
mOld = mLands.getRecord(mId).clone();
|
||||||
mChanged = mLands.touchRecord(mId);
|
mChanged = mLands.touchRecord(mId);
|
||||||
if (mChanged)
|
|
||||||
mOld.reset(mLands.getRecord(mId).clone().get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::TouchLandCommand::onUndo()
|
void CSMWorld::TouchLandCommand::onUndo()
|
||||||
|
|
|
@ -149,7 +149,8 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
|
||||||
mResourcesManager.setVFS(mVFS.get());
|
mResourcesManager.setVFS(mVFS.get());
|
||||||
|
|
||||||
constexpr double expiryDelay = 0;
|
constexpr double expiryDelay = 0;
|
||||||
mResourceSystem = std::make_unique<Resource::ResourceSystem>(mVFS.get(), expiryDelay);
|
mResourceSystem
|
||||||
|
= std::make_unique<Resource::ResourceSystem>(mVFS.get(), expiryDelay, &mEncoder.getStatelessEncoder());
|
||||||
|
|
||||||
Shader::ShaderManager::DefineMap defines
|
Shader::ShaderManager::DefineMap defines
|
||||||
= mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
|
= mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
|
||||||
|
@ -1107,7 +1108,7 @@ void CSMWorld::Data::loadFallbackEntries()
|
||||||
newMarker.mModel = model;
|
newMarker.mModel = model;
|
||||||
newMarker.mRecordFlags = 0;
|
newMarker.mRecordFlags = 0;
|
||||||
auto record = std::make_unique<CSMWorld::Record<ESM::Static>>();
|
auto record = std::make_unique<CSMWorld::Record<ESM::Static>>();
|
||||||
record->mBase = newMarker;
|
record->mBase = std::move(newMarker);
|
||||||
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
mReferenceables.appendRecord(std::move(record), CSMWorld::UniversalId::Type_Static);
|
mReferenceables.appendRecord(std::move(record), CSMWorld::UniversalId::Type_Static);
|
||||||
}
|
}
|
||||||
|
@ -1123,7 +1124,7 @@ void CSMWorld::Data::loadFallbackEntries()
|
||||||
newMarker.mModel = model;
|
newMarker.mModel = model;
|
||||||
newMarker.mRecordFlags = 0;
|
newMarker.mRecordFlags = 0;
|
||||||
auto record = std::make_unique<CSMWorld::Record<ESM::Door>>();
|
auto record = std::make_unique<CSMWorld::Record<ESM::Door>>();
|
||||||
record->mBase = newMarker;
|
record->mBase = std::move(newMarker);
|
||||||
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
mReferenceables.appendRecord(std::move(record), CSMWorld::UniversalId::Type_Door);
|
mReferenceables.appendRecord(std::move(record), CSMWorld::UniversalId::Type_Door);
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data& data)
|
||||||
completer->setPopup(popup); // The completer takes ownership of the popup
|
completer->setPopup(popup); // The completer takes ownership of the popup
|
||||||
completer->setMaxVisibleItems(10);
|
completer->setMaxVisibleItems(10);
|
||||||
|
|
||||||
mCompleters[current->first] = completer;
|
mCompleters[current->first] = std::move(completer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <components/misc/strings/lower.hpp>
|
#include <components/misc/strings/lower.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
|
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||||
|
|
||||||
CSMWorld::Resources::Resources(
|
CSMWorld::Resources::Resources(
|
||||||
const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type, const char* const* extensions)
|
const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type, const char* const* extensions)
|
||||||
|
|
|
@ -91,7 +91,7 @@ void CSVDoc::AdjusterWidget::setName(const QString& name, bool addon)
|
||||||
{
|
{
|
||||||
// path already points to the local data directory
|
// path already points to the local data directory
|
||||||
message = "Will be saved as: " + Files::pathToQString(path);
|
message = "Will be saved as: " + Files::pathToQString(path);
|
||||||
mResultPath = path;
|
mResultPath = std::move(path);
|
||||||
}
|
}
|
||||||
// in all other cases, ensure the path points to data-local and do an existing file check
|
// in all other cases, ensure the path points to data-local and do an existing file check
|
||||||
else
|
else
|
||||||
|
|
|
@ -410,7 +410,7 @@ bool CSVDoc::ViewManager::removeDocument(CSVDoc::View* view)
|
||||||
remainingViews.push_back(*iter);
|
remainingViews.push_back(*iter);
|
||||||
}
|
}
|
||||||
mDocumentManager.removeDocument(document);
|
mDocumentManager.removeDocument(document);
|
||||||
mViews = remainingViews;
|
mViews = std::move(remainingViews);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -514,7 +514,7 @@ void CSVRender::PagedWorldspaceWidget::moveCellSelection(int x, int y)
|
||||||
addCellToScene(*iter);
|
addCellToScene(*iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
mSelection = newSelection;
|
mSelection = std::move(newSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera(int offsetX, int offsetY)
|
void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera(int offsetX, int offsetY)
|
||||||
|
|
|
@ -541,7 +541,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
|
||||||
= landTable.data(landTable.getModelIndex(cellId, textureColumn))
|
= landTable.data(landTable.getModelIndex(cellId, textureColumn))
|
||||||
.value<CSMWorld::LandTexturesColumn::DataType>();
|
.value<CSMWorld::LandTexturesColumn::DataType>();
|
||||||
newTerrainOtherCell[yInOtherCell * landTextureSize + xInOtherCell] = brushInt;
|
newTerrainOtherCell[yInOtherCell * landTextureSize + xInOtherCell] = brushInt;
|
||||||
pushEditToCommand(newTerrainOtherCell, document, landTable, cellId);
|
pushEditToCommand(newTerrainOtherCell, document, landTable, std::move(cellId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -702,7 +702,7 @@ void CSVRender::TerrainTextureMode::createTexture(const std::string& textureFile
|
||||||
QModelIndex index(ltexTable.getModelIndex(newId, ltexTable.findColumnIndex(CSMWorld::Columns::ColumnId_Texture)));
|
QModelIndex index(ltexTable.getModelIndex(newId, ltexTable.findColumnIndex(CSMWorld::Columns::ColumnId_Texture)));
|
||||||
undoStack.push(new CSMWorld::ModifyCommand(ltexTable, index, textureFileNameVariant));
|
undoStack.push(new CSMWorld::ModifyCommand(ltexTable, index, textureFileNameVariant));
|
||||||
undoStack.endMacro();
|
undoStack.endMacro();
|
||||||
mBrushTexture = newId;
|
mBrushTexture = std::move(newId);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSVRender::TerrainTextureMode::allowLandTextureEditing(const std::string& cellId)
|
bool CSVRender::TerrainTextureMode::allowLandTextureEditing(const std::string& cellId)
|
||||||
|
|
|
@ -184,11 +184,11 @@ void CSVRender::WorldspaceWidget::selectDefaultNavigationMode()
|
||||||
|
|
||||||
void CSVRender::WorldspaceWidget::centerOrbitCameraOnSelection()
|
void CSVRender::WorldspaceWidget::centerOrbitCameraOnSelection()
|
||||||
{
|
{
|
||||||
std::vector<osg::ref_ptr<TagBase>> selection = getSelection(~0u);
|
std::vector<osg::ref_ptr<TagBase>> selection = getSelection(Mask_Reference);
|
||||||
|
|
||||||
for (std::vector<osg::ref_ptr<TagBase>>::iterator it = selection.begin(); it != selection.end(); ++it)
|
for (std::vector<osg::ref_ptr<TagBase>>::iterator it = selection.begin(); it != selection.end(); ++it)
|
||||||
{
|
{
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(it->get()))
|
if (CSVRender::ObjectTag* objectTag = static_cast<CSVRender::ObjectTag*>(it->get()))
|
||||||
{
|
{
|
||||||
mOrbitCamControl->setCenter(objectTag->mObject->getPosition().asVec3());
|
mOrbitCamControl->setCenter(objectTag->mObject->getPosition().asVec3());
|
||||||
}
|
}
|
||||||
|
@ -440,7 +440,7 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||||
osg::Node* node = *nodeIter;
|
osg::Node* node = *nodeIter;
|
||||||
if (osg::ref_ptr<CSVRender::TagBase> tag = dynamic_cast<CSVRender::TagBase*>(node->getUserData()))
|
if (osg::ref_ptr<CSVRender::TagBase> tag = dynamic_cast<CSVRender::TagBase*>(node->getUserData()))
|
||||||
{
|
{
|
||||||
WorldspaceHitResult hit = { true, tag, 0, 0, 0, intersection.getWorldIntersectPoint() };
|
WorldspaceHitResult hit = { true, std::move(tag), 0, 0, 0, intersection.getWorldIntersectPoint() };
|
||||||
if (intersection.indexList.size() >= 3)
|
if (intersection.indexList.size() >= 3)
|
||||||
{
|
{
|
||||||
hit.index0 = intersection.indexList[0];
|
hit.index0 = intersection.indexList[0];
|
||||||
|
@ -757,13 +757,14 @@ void CSVRender::WorldspaceWidget::toggleHiddenInstances()
|
||||||
if (selection.empty())
|
if (selection.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const CSVRender::ObjectTag* firstSelection = dynamic_cast<CSVRender::ObjectTag*>(selection.begin()->get());
|
const CSVRender::ObjectTag* firstSelection = static_cast<CSVRender::ObjectTag*>(selection.begin()->get());
|
||||||
|
assert(firstSelection != nullptr);
|
||||||
|
|
||||||
const CSVRender::Mask firstMask
|
const CSVRender::Mask firstMask
|
||||||
= firstSelection->mObject->getRootNode()->getNodeMask() == Mask_Hidden ? Mask_Reference : Mask_Hidden;
|
= firstSelection->mObject->getRootNode()->getNodeMask() == Mask_Hidden ? Mask_Reference : Mask_Hidden;
|
||||||
|
|
||||||
for (const auto& object : selection)
|
for (const auto& object : selection)
|
||||||
if (const auto objectTag = dynamic_cast<CSVRender::ObjectTag*>(object.get()))
|
if (const auto objectTag = static_cast<CSVRender::ObjectTag*>(object.get()))
|
||||||
objectTag->mObject->getRootNode()->setNodeMask(firstMask);
|
objectTag->mObject->getRootNode()->setNodeMask(firstMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -213,7 +213,7 @@ void CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture)
|
||||||
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));
|
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));
|
||||||
}
|
}
|
||||||
|
|
||||||
mBrushTexture = newBrushTextureId;
|
mBrushTexture = std::move(newBrushTextureId);
|
||||||
|
|
||||||
emit passTextureId(mBrushTexture);
|
emit passTextureId(mBrushTexture);
|
||||||
emit passBrushShape(mBrushShape); // updates the icon tooltip
|
emit passBrushShape(mBrushShape); // updates the icon tooltip
|
||||||
|
|
|
@ -146,7 +146,7 @@ void CSVWorld::ExtendedCommandConfigurator::setupCheckBoxes(const std::vector<CS
|
||||||
CSMWorld::UniversalId type = types[counter];
|
CSMWorld::UniversalId type = types[counter];
|
||||||
current->first->setText(QString::fromUtf8(type.getTypeName().c_str()));
|
current->first->setText(QString::fromUtf8(type.getTypeName().c_str()));
|
||||||
current->first->setChecked(true);
|
current->first->setChecked(true);
|
||||||
current->second = type;
|
current->second = std::move(type);
|
||||||
++counter;
|
++counter;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -169,7 +169,7 @@ void CSVWorld::TableSubView::createFilterRequest(std::vector<CSMWorld::Universal
|
||||||
{
|
{
|
||||||
CSVFilter::FilterData filterData;
|
CSVFilter::FilterData filterData;
|
||||||
filterData.searchData = it->getId();
|
filterData.searchData = it->getId();
|
||||||
filterData.columns = col;
|
filterData.columns = std::move(col);
|
||||||
sourceFilter.emplace_back(filterData);
|
sourceFilter.emplace_back(filterData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ void CSVWorld::TableSubView::createFilterRequest(std::vector<CSMWorld::Universal
|
||||||
|
|
||||||
CSVFilter::FilterData filterData;
|
CSVFilter::FilterData filterData;
|
||||||
filterData.searchData = qData;
|
filterData.searchData = qData;
|
||||||
filterData.columns = searchColumns;
|
filterData.columns = std::move(searchColumns);
|
||||||
|
|
||||||
sourceFilterByValue.emplace_back(filterData);
|
sourceFilterByValue.emplace_back(filterData);
|
||||||
|
|
||||||
|
|
|
@ -321,11 +321,7 @@ bool OMW::Engine::frame(float frametime)
|
||||||
// update GUI by world data
|
// update GUI by world data
|
||||||
{
|
{
|
||||||
ScopedProfile<UserStatsType::WindowManager> profile(frameStart, frameNumber, *timer, *stats);
|
ScopedProfile<UserStatsType::WindowManager> profile(frameStart, frameNumber, *timer, *stats);
|
||||||
|
mWorld->updateWindowManager();
|
||||||
if (mStateManager->getState() != MWBase::StateManager::State_NoGame)
|
|
||||||
{
|
|
||||||
mWorld->updateWindowManager();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mLuaWorker->allowUpdate(); // if there is a separate Lua thread, it starts the update now
|
mLuaWorker->allowUpdate(); // if there is a separate Lua thread, it starts the update now
|
||||||
|
@ -706,7 +702,8 @@ void OMW::Engine::prepareEngine()
|
||||||
|
|
||||||
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
||||||
|
|
||||||
mResourceSystem = std::make_unique<Resource::ResourceSystem>(mVFS.get(), Settings::cells().mCacheExpiryDelay);
|
mResourceSystem = std::make_unique<Resource::ResourceSystem>(
|
||||||
|
mVFS.get(), Settings::cells().mCacheExpiryDelay, &mEncoder.get()->getStatelessEncoder());
|
||||||
mResourceSystem->getSceneManager()->getShaderManager().setMaxTextureUnits(mGlMaxTextureImageUnits);
|
mResourceSystem->getSceneManager()->getShaderManager().setMaxTextureUnits(mGlMaxTextureImageUnits);
|
||||||
mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(
|
mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(
|
||||||
false); // keep to Off for now to allow better state sharing
|
false); // keep to Off for now to allow better state sharing
|
||||||
|
@ -826,7 +823,7 @@ void OMW::Engine::prepareEngine()
|
||||||
}
|
}
|
||||||
listener->loadingOff();
|
listener->loadingOff();
|
||||||
|
|
||||||
mWorld->init(mViewer, rootNode, mWorkQueue.get(), *mUnrefQueue);
|
mWorld->init(mViewer, std::move(rootNode), mWorkQueue.get(), *mUnrefQueue);
|
||||||
mEnvironment.setWorldScene(mWorld->getWorldScene());
|
mEnvironment.setWorldScene(mWorld->getWorldScene());
|
||||||
mWorld->setupPlayer();
|
mWorld->setupPlayer();
|
||||||
mWorld->setRandomSeed(mRandomSeed);
|
mWorld->setRandomSeed(mRandomSeed);
|
||||||
|
|
|
@ -187,6 +187,8 @@ namespace MWBase
|
||||||
|
|
||||||
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
|
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
|
||||||
|
|
||||||
|
virtual bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const = 0;
|
||||||
|
|
||||||
/// Save the current animation state of managed references to their RefData.
|
/// Save the current animation state of managed references to their RefData.
|
||||||
virtual void persistAnimationStates() = 0;
|
virtual void persistAnimationStates() = 0;
|
||||||
|
|
||||||
|
|
|
@ -37,23 +37,6 @@ namespace MWClass
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actor::block(const MWWorld::Ptr& ptr) const
|
|
||||||
{
|
|
||||||
const MWWorld::InventoryStore& inv = getInventoryStore(ptr);
|
|
||||||
MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
|
||||||
if (shield == inv.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
|
||||||
const ESM::RefId skill = shield->getClass().getEquipmentSkill(*shield);
|
|
||||||
if (skill == ESM::Skill::LightArmor)
|
|
||||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f);
|
|
||||||
else if (skill == ESM::Skill::MediumArmor)
|
|
||||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f);
|
|
||||||
else if (skill == ESM::Skill::HeavyArmor)
|
|
||||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::Vec3f Actor::getRotationVector(const MWWorld::Ptr& ptr) const
|
osg::Vec3f Actor::getRotationVector(const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
MWMechanics::Movement& movement = getMovementSettings(ptr);
|
MWMechanics::Movement& movement = getMovementSettings(ptr);
|
||||||
|
|
|
@ -45,8 +45,6 @@ namespace MWClass
|
||||||
|
|
||||||
bool useAnim() const override;
|
bool useAnim() const override;
|
||||||
|
|
||||||
void block(const MWWorld::Ptr& ptr) const override;
|
|
||||||
|
|
||||||
osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const override;
|
osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const override;
|
||||||
///< Return desired rotations, as euler angles. Sets getMovementSettings(ptr).mRotation to zero.
|
///< Return desired rotations, as euler angles. Sets getMovementSettings(ptr).mRotation to zero.
|
||||||
|
|
||||||
|
|
|
@ -183,16 +183,17 @@ namespace MWClass
|
||||||
return getClassModel<ESM::Creature>(ptr);
|
return getClassModel<ESM::Creature>(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Creature::getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const
|
void Creature::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const
|
||||||
{
|
{
|
||||||
std::string model = getModel(ptr);
|
std::string model = getModel(ptr);
|
||||||
if (!model.empty())
|
if (!model.empty())
|
||||||
models.push_back(model);
|
models.push_back(model);
|
||||||
|
|
||||||
// FIXME: use const version of InventoryStore functions once they are available
|
const MWWorld::CustomData* customData = ptr.getRefData().getCustomData();
|
||||||
if (hasInventoryStore(ptr))
|
if (customData && hasInventoryStore(ptr))
|
||||||
{
|
{
|
||||||
const MWWorld::InventoryStore& invStore = getInventoryStore(ptr);
|
const auto& invStore
|
||||||
|
= static_cast<const MWWorld::InventoryStore&>(*customData->asCreatureCustomData().mContainerStore);
|
||||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||||
{
|
{
|
||||||
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
||||||
|
@ -339,10 +340,7 @@ namespace MWClass
|
||||||
MWMechanics::applyElementalShields(ptr, victim);
|
MWMechanics::applyElementalShields(ptr, victim);
|
||||||
|
|
||||||
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))
|
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))
|
||||||
{
|
|
||||||
damage = 0;
|
damage = 0;
|
||||||
victim.getClass().block(victim);
|
|
||||||
}
|
|
||||||
|
|
||||||
MWMechanics::diseaseContact(victim, ptr);
|
MWMechanics::diseaseContact(victim, ptr);
|
||||||
|
|
||||||
|
@ -513,7 +511,7 @@ namespace MWClass
|
||||||
throw std::runtime_error("this creature has no inventory store");
|
throw std::runtime_error("this creature has no inventory store");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Creature::hasInventoryStore(const MWWorld::Ptr& ptr) const
|
bool Creature::hasInventoryStore(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return isFlagBitSet(ptr, ESM::Creature::Weapon);
|
return isFlagBitSet(ptr, ESM::Creature::Weapon);
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ namespace MWClass
|
||||||
MWWorld::InventoryStore& getInventoryStore(const MWWorld::Ptr& ptr) const override;
|
MWWorld::InventoryStore& getInventoryStore(const MWWorld::Ptr& ptr) const override;
|
||||||
///< Return inventory store
|
///< Return inventory store
|
||||||
|
|
||||||
bool hasInventoryStore(const MWWorld::Ptr& ptr) const override;
|
bool hasInventoryStore(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
ESM::RefId getScript(const MWWorld::ConstPtr& ptr) const override;
|
ESM::RefId getScript(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< Return name of the script attached to ptr
|
///< Return name of the script attached to ptr
|
||||||
|
@ -107,7 +107,7 @@ namespace MWClass
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;
|
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const override;
|
||||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||||
///< list getModel().
|
///< list getModel().
|
||||||
|
|
||||||
|
|
|
@ -99,25 +99,6 @@ namespace MWClass
|
||||||
customData.mSpawn = true;
|
customData.mSpawn = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureLevList::getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const
|
|
||||||
{
|
|
||||||
// disable for now, too many false positives
|
|
||||||
/*
|
|
||||||
const MWWorld::LiveCellRef<ESM::CreatureLevList> *ref = ptr.get<ESM::CreatureLevList>();
|
|
||||||
for (std::vector<ESM::LevelledListBase::LevelItem>::const_iterator it = ref->mBase->mList.begin(); it !=
|
|
||||||
ref->mBase->mList.end(); ++it)
|
|
||||||
{
|
|
||||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
|
||||||
if (it->mLevel > player.getClass().getCreatureStats(player).getLevel())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
|
||||||
MWWorld::ManualRef ref(store, it->mId);
|
|
||||||
ref.getPtr().getClass().getModelsToPreload(ref.getPtr(), models);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreatureLevList::insertObjectRendering(
|
void CreatureLevList::insertObjectRendering(
|
||||||
const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
|
const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,10 +20,6 @@ namespace MWClass
|
||||||
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;
|
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< @return true if this object has a tooltip when focused (default implementation: true)
|
///< @return true if this object has a tooltip when focused (default implementation: true)
|
||||||
|
|
||||||
void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;
|
|
||||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
|
||||||
///< list getModel().
|
|
||||||
|
|
||||||
void insertObjectRendering(const MWWorld::Ptr& ptr, const std::string& model,
|
void insertObjectRendering(const MWWorld::Ptr& ptr, const std::string& model,
|
||||||
MWRender::RenderingInterface& renderingInterface) const override;
|
MWRender::RenderingInterface& renderingInterface) const override;
|
||||||
///< Add reference into a cell for rendering
|
///< Add reference into a cell for rendering
|
||||||
|
|
|
@ -436,10 +436,11 @@ namespace MWClass
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Npc::getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const
|
void Npc::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const
|
||||||
{
|
{
|
||||||
const MWWorld::LiveCellRef<ESM::NPC>* npc = ptr.get<ESM::NPC>();
|
const MWWorld::LiveCellRef<ESM::NPC>* npc = ptr.get<ESM::NPC>();
|
||||||
const ESM::Race* race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().search(npc->mBase->mRace);
|
const auto& esmStore = MWBase::Environment::get().getESMStore();
|
||||||
|
const ESM::Race* race = esmStore->get<ESM::Race>().search(npc->mBase->mRace);
|
||||||
if (race && race->mData.mFlags & ESM::Race::Beast)
|
if (race && race->mData.mFlags & ESM::Race::Beast)
|
||||||
models.push_back(Settings::models().mBaseanimkna);
|
models.push_back(Settings::models().mBaseanimkna);
|
||||||
|
|
||||||
|
@ -453,56 +454,57 @@ namespace MWClass
|
||||||
|
|
||||||
if (!npc->mBase->mHead.empty())
|
if (!npc->mBase->mHead.empty())
|
||||||
{
|
{
|
||||||
const ESM::BodyPart* head
|
const ESM::BodyPart* head = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHead);
|
||||||
= MWBase::Environment::get().getESMStore()->get<ESM::BodyPart>().search(npc->mBase->mHead);
|
|
||||||
if (head)
|
if (head)
|
||||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(head->mModel));
|
models.push_back(Misc::ResourceHelpers::correctMeshPath(head->mModel));
|
||||||
}
|
}
|
||||||
if (!npc->mBase->mHair.empty())
|
if (!npc->mBase->mHair.empty())
|
||||||
{
|
{
|
||||||
const ESM::BodyPart* hair
|
const ESM::BodyPart* hair = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHair);
|
||||||
= MWBase::Environment::get().getESMStore()->get<ESM::BodyPart>().search(npc->mBase->mHair);
|
|
||||||
if (hair)
|
if (hair)
|
||||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(hair->mModel));
|
models.push_back(Misc::ResourceHelpers::correctMeshPath(hair->mModel));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool female = (npc->mBase->mFlags & ESM::NPC::Female);
|
bool female = (npc->mBase->mFlags & ESM::NPC::Female);
|
||||||
|
|
||||||
// FIXME: use const version of InventoryStore functions once they are available
|
const MWWorld::CustomData* customData = ptr.getRefData().getCustomData();
|
||||||
// preload equipped items
|
if (customData)
|
||||||
const MWWorld::InventoryStore& invStore = getInventoryStore(ptr);
|
|
||||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
|
||||||
{
|
{
|
||||||
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
const MWWorld::InventoryStore& invStore = customData->asNpcCustomData().mInventoryStore;
|
||||||
if (equipped != invStore.end())
|
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||||
{
|
{
|
||||||
std::vector<ESM::PartReference> parts;
|
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
||||||
if (equipped->getType() == ESM::Clothing::sRecordId)
|
if (equipped != invStore.end())
|
||||||
{
|
{
|
||||||
const ESM::Clothing* clothes = equipped->get<ESM::Clothing>()->mBase;
|
const auto addParts = [&](const std::vector<ESM::PartReference>& parts) {
|
||||||
parts = clothes->mParts.mParts;
|
for (const ESM::PartReference& partRef : parts)
|
||||||
}
|
{
|
||||||
else if (equipped->getType() == ESM::Armor::sRecordId)
|
const ESM::RefId& partname
|
||||||
{
|
= (female && !partRef.mFemale.empty()) || (!female && partRef.mMale.empty())
|
||||||
const ESM::Armor* armor = equipped->get<ESM::Armor>()->mBase;
|
? partRef.mFemale
|
||||||
parts = armor->mParts.mParts;
|
: partRef.mMale;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string model = equipped->getClass().getModel(*equipped);
|
|
||||||
if (!model.empty())
|
|
||||||
models.push_back(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::vector<ESM::PartReference>::const_iterator it = parts.begin(); it != parts.end(); ++it)
|
const ESM::BodyPart* part = esmStore->get<ESM::BodyPart>().search(partname);
|
||||||
{
|
if (part && !part->mModel.empty())
|
||||||
const ESM::RefId& partname
|
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
||||||
= (female && !it->mFemale.empty()) || (!female && it->mMale.empty()) ? it->mFemale : it->mMale;
|
}
|
||||||
|
};
|
||||||
const ESM::BodyPart* part
|
if (equipped->getType() == ESM::Clothing::sRecordId)
|
||||||
= MWBase::Environment::get().getESMStore()->get<ESM::BodyPart>().search(partname);
|
{
|
||||||
if (part && !part->mModel.empty())
|
const ESM::Clothing* clothes = equipped->get<ESM::Clothing>()->mBase;
|
||||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
addParts(clothes->mParts.mParts);
|
||||||
|
}
|
||||||
|
else if (equipped->getType() == ESM::Armor::sRecordId)
|
||||||
|
{
|
||||||
|
const ESM::Armor* armor = equipped->get<ESM::Armor>()->mBase;
|
||||||
|
addParts(armor->mParts.mParts);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string model = equipped->getClass().getModel(*equipped);
|
||||||
|
if (!model.empty())
|
||||||
|
models.push_back(model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -512,9 +514,8 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
const std::vector<const ESM::BodyPart*>& parts
|
const std::vector<const ESM::BodyPart*>& parts
|
||||||
= MWRender::NpcAnimation::getBodyParts(race->mId, female, false, false);
|
= MWRender::NpcAnimation::getBodyParts(race->mId, female, false, false);
|
||||||
for (std::vector<const ESM::BodyPart*>::const_iterator it = parts.begin(); it != parts.end(); ++it)
|
for (const ESM::BodyPart* part : parts)
|
||||||
{
|
{
|
||||||
const ESM::BodyPart* part = *it;
|
|
||||||
if (part && !part->mModel.empty())
|
if (part && !part->mModel.empty())
|
||||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
||||||
}
|
}
|
||||||
|
@ -678,10 +679,7 @@ namespace MWClass
|
||||||
MWMechanics::applyElementalShields(ptr, victim);
|
MWMechanics::applyElementalShields(ptr, victim);
|
||||||
|
|
||||||
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))
|
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))
|
||||||
{
|
|
||||||
damage = 0;
|
damage = 0;
|
||||||
victim.getClass().block(victim);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (victim == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState())
|
if (victim == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState())
|
||||||
damage = 0;
|
damage = 0;
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace MWClass
|
||||||
MWWorld::InventoryStore& getInventoryStore(const MWWorld::Ptr& ptr) const override;
|
MWWorld::InventoryStore& getInventoryStore(const MWWorld::Ptr& ptr) const override;
|
||||||
///< Return inventory store
|
///< Return inventory store
|
||||||
|
|
||||||
bool hasInventoryStore(const MWWorld::Ptr& ptr) const override { return true; }
|
bool hasInventoryStore(const MWWorld::ConstPtr& ptr) const override { return true; }
|
||||||
|
|
||||||
bool evaluateHit(const MWWorld::Ptr& ptr, MWWorld::Ptr& victim, osg::Vec3f& hitPosition) const override;
|
bool evaluateHit(const MWWorld::Ptr& ptr, MWWorld::Ptr& victim, osg::Vec3f& hitPosition) const override;
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ namespace MWClass
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
||||||
const MWMechanics::DamageSourceType sourceType) const override;
|
const MWMechanics::DamageSourceType sourceType) const override;
|
||||||
|
|
||||||
void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;
|
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const override;
|
||||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||||
///< list getModel().
|
///< list getModel().
|
||||||
|
|
||||||
|
|
|
@ -364,9 +364,8 @@ namespace MWGui
|
||||||
if (mCurrentWindowSize == _sender->getSize())
|
if (mCurrentWindowSize == _sender->getSize())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mTopicsList->adjustSize();
|
redrawTopicsList();
|
||||||
updateHistory();
|
updateHistory();
|
||||||
updateTopicFormat();
|
|
||||||
mCurrentWindowSize = _sender->getSize();
|
mCurrentWindowSize = _sender->getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,6 +533,14 @@ namespace MWGui
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DialogueWindow::redrawTopicsList()
|
||||||
|
{
|
||||||
|
mTopicsList->adjustSize();
|
||||||
|
|
||||||
|
// The topics list has been regenerated so topic formatting needs to be updated
|
||||||
|
updateTopicFormat();
|
||||||
|
}
|
||||||
|
|
||||||
void DialogueWindow::updateTopicsPane()
|
void DialogueWindow::updateTopicsPane()
|
||||||
{
|
{
|
||||||
mTopicsList->clear();
|
mTopicsList->clear();
|
||||||
|
@ -591,11 +598,9 @@ namespace MWGui
|
||||||
t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);
|
t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);
|
||||||
mTopicLinks[topicId] = std::move(t);
|
mTopicLinks[topicId] = std::move(t);
|
||||||
}
|
}
|
||||||
mTopicsList->adjustSize();
|
|
||||||
|
|
||||||
|
redrawTopicsList();
|
||||||
updateHistory();
|
updateHistory();
|
||||||
// The topics list has been regenerated so topic formatting needs to be updated
|
|
||||||
updateTopicFormat();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueWindow::updateHistory(bool scrollbar)
|
void DialogueWindow::updateHistory(bool scrollbar)
|
||||||
|
@ -756,21 +761,12 @@ namespace MWGui
|
||||||
+ std::string("/100"));
|
+ std::string("/100"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dispositionWasVisible = mDispositionBar->getVisible();
|
if (mDispositionBar->getVisible() != dispositionVisible)
|
||||||
|
|
||||||
if (dispositionVisible && !dispositionWasVisible)
|
|
||||||
{
|
{
|
||||||
mDispositionBar->setVisible(true);
|
mDispositionBar->setVisible(dispositionVisible);
|
||||||
int offset = mDispositionBar->getHeight() + 5;
|
const int offset = (mDispositionBar->getHeight() + 5) * (dispositionVisible ? 1 : -1);
|
||||||
mTopicsList->setCoord(mTopicsList->getCoord() + MyGUI::IntCoord(0, offset, 0, -offset));
|
mTopicsList->setCoord(mTopicsList->getCoord() + MyGUI::IntCoord(0, offset, 0, -offset));
|
||||||
mTopicsList->adjustSize();
|
redrawTopicsList();
|
||||||
}
|
|
||||||
else if (!dispositionVisible && dispositionWasVisible)
|
|
||||||
{
|
|
||||||
mDispositionBar->setVisible(false);
|
|
||||||
int offset = mDispositionBar->getHeight() + 5;
|
|
||||||
mTopicsList->setCoord(mTopicsList->getCoord() - MyGUI::IntCoord(0, offset, 0, -offset));
|
|
||||||
mTopicsList->adjustSize();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,6 +190,7 @@ namespace MWGui
|
||||||
void updateDisposition();
|
void updateDisposition();
|
||||||
void restock();
|
void restock();
|
||||||
void deleteLater();
|
void deleteLater();
|
||||||
|
void redrawTopicsList();
|
||||||
|
|
||||||
bool mIsCompanion;
|
bool mIsCompanion;
|
||||||
std::list<std::string> mKeywords;
|
std::list<std::string> mKeywords;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
|
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/inputmanager.hpp"
|
#include "../mwbase/inputmanager.hpp"
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
|
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||||
#include <components/widgets/sharedstatebutton.hpp>
|
#include <components/widgets/sharedstatebutton.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
@ -131,7 +132,7 @@ namespace
|
||||||
void updateMaxLightsComboBox(MyGUI::ComboBox* box)
|
void updateMaxLightsComboBox(MyGUI::ComboBox* box)
|
||||||
{
|
{
|
||||||
constexpr int min = 8;
|
constexpr int min = 8;
|
||||||
constexpr int max = 32;
|
constexpr int max = 64;
|
||||||
constexpr int increment = 8;
|
constexpr int increment = 8;
|
||||||
const int maxLights = Settings::shaders().mMaxLights;
|
const int maxLights = Settings::shaders().mMaxLights;
|
||||||
// show increments of 8 in dropdown
|
// show increments of 8 in dropdown
|
||||||
|
|
|
@ -23,6 +23,11 @@ namespace
|
||||||
float mTimeOffset = 0.f;
|
float mTimeOffset = 0.f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct StreamMusicArgs
|
||||||
|
{
|
||||||
|
float mFade = 1.f;
|
||||||
|
};
|
||||||
|
|
||||||
PlaySoundArgs getPlaySoundArgs(const sol::optional<sol::table>& options)
|
PlaySoundArgs getPlaySoundArgs(const sol::optional<sol::table>& options)
|
||||||
{
|
{
|
||||||
PlaySoundArgs args;
|
PlaySoundArgs args;
|
||||||
|
@ -55,6 +60,17 @@ namespace
|
||||||
return MWSound::PlayMode::NoEnvNoScaling;
|
return MWSound::PlayMode::NoEnvNoScaling;
|
||||||
return MWSound::PlayMode::NoEnv;
|
return MWSound::PlayMode::NoEnv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StreamMusicArgs getStreamMusicArgs(const sol::optional<sol::table>& options)
|
||||||
|
{
|
||||||
|
StreamMusicArgs args;
|
||||||
|
|
||||||
|
if (options.has_value())
|
||||||
|
{
|
||||||
|
args.mFade = options->get_or("fadeOut", 1.f);
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWLua
|
namespace MWLua
|
||||||
|
@ -95,9 +111,10 @@ namespace MWLua
|
||||||
return MWBase::Environment::get().getSoundManager()->getSoundPlaying(MWWorld::Ptr(), fileName);
|
return MWBase::Environment::get().getSoundManager()->getSoundPlaying(MWWorld::Ptr(), fileName);
|
||||||
};
|
};
|
||||||
|
|
||||||
api["streamMusic"] = [](std::string_view fileName) {
|
api["streamMusic"] = [](std::string_view fileName, const sol::optional<sol::table>& options) {
|
||||||
|
auto args = getStreamMusicArgs(options);
|
||||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
sndMgr->streamMusic(std::string(fileName), MWSound::MusicType::Scripted);
|
sndMgr->streamMusic(std::string(fileName), MWSound::MusicType::Scripted, args.mFade);
|
||||||
};
|
};
|
||||||
|
|
||||||
api["isMusicPlaying"] = []() { return MWBase::Environment::get().getSoundManager()->isMusicPlaying(); };
|
api["isMusicPlaying"] = []() { return MWBase::Environment::get().getSoundManager()->isMusicPlaying(); };
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "../stats.hpp"
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
|
|
||||||
|
@ -42,6 +43,20 @@ namespace MWLua
|
||||||
record["soulValue"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mSoul; });
|
record["soulValue"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mSoul; });
|
||||||
record["type"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mType; });
|
record["type"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mType; });
|
||||||
record["baseGold"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mGold; });
|
record["baseGold"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mGold; });
|
||||||
|
record["combatSkill"]
|
||||||
|
= sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mCombat; });
|
||||||
|
record["magicSkill"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mMagic; });
|
||||||
|
record["stealthSkill"]
|
||||||
|
= sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mStealth; });
|
||||||
|
record["attack"] = sol::readonly_property([context](const ESM::Creature& rec) -> sol::table {
|
||||||
|
sol::state_view& lua = context.mLua->sol();
|
||||||
|
sol::table res(lua, sol::create);
|
||||||
|
int index = 1;
|
||||||
|
for (auto attack : rec.mData.mAttack)
|
||||||
|
res[index++] = attack;
|
||||||
|
return LuaUtil::makeReadOnly(res);
|
||||||
|
});
|
||||||
|
|
||||||
addActorServicesBindings<ESM::Creature>(record, context);
|
addActorServicesBindings<ESM::Creature>(record, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
#include <components/vfs/pathutil.hpp>
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
|
||||||
|
|
|
@ -2034,6 +2034,14 @@ namespace MWMechanics
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Actors::checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const
|
||||||
|
{
|
||||||
|
const auto iter = mIndex.find(ptr.mRef);
|
||||||
|
if (iter != mIndex.end())
|
||||||
|
return iter->second->getCharacterController().isScriptedAnimPlaying();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Actors::persistAnimationStates() const
|
void Actors::persistAnimationStates() const
|
||||||
{
|
{
|
||||||
for (const Actor& actor : mActors)
|
for (const Actor& actor : mActors)
|
||||||
|
|
|
@ -116,6 +116,7 @@ namespace MWMechanics
|
||||||
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted = false) const;
|
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted = false) const;
|
||||||
void skipAnimation(const MWWorld::Ptr& ptr) const;
|
void skipAnimation(const MWWorld::Ptr& ptr) const;
|
||||||
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) const;
|
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) const;
|
||||||
|
bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const;
|
||||||
void persistAnimationStates() const;
|
void persistAnimationStates() const;
|
||||||
|
|
||||||
void getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out) const;
|
void getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out) const;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/luamanager.hpp"
|
#include "../mwbase/luamanager.hpp"
|
||||||
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
@ -120,12 +121,12 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
||||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
const DetourNavigator::AgentBounds agentBounds = world->getPathfindingAgentBounds(actor);
|
const DetourNavigator::AgentBounds agentBounds = world->getPathfindingAgentBounds(actor);
|
||||||
|
|
||||||
/// Stops the actor when it gets too close to a unloaded cell
|
/// Stops the actor when it gets too close to a unloaded cell or when the actor is playing a scripted animation
|
||||||
//... At current time, this test is unnecessary. AI shuts down when actor is more than "actors processing range"
|
//... At current time, the first test is unnecessary. AI shuts down when actor is more than
|
||||||
// setting value
|
//... "actors processing range" setting value units from player, and exterior cells are 8192 units long and wide.
|
||||||
//... units from player, and exterior cells are 8192 units long and wide.
|
|
||||||
//... But AI processing distance may increase in the future.
|
//... But AI processing distance may increase in the future.
|
||||||
if (isNearInactiveCell(position))
|
if (isNearInactiveCell(position)
|
||||||
|
|| MWBase::Environment::get().getMechanicsManager()->checkScriptedAnimationPlaying(actor))
|
||||||
{
|
{
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
|
|
|
@ -217,7 +217,6 @@ namespace MWMechanics
|
||||||
std::string chooseRandomAttackAnimation() const;
|
std::string chooseRandomAttackAnimation() const;
|
||||||
static bool isRandomAttackAnimation(std::string_view group);
|
static bool isRandomAttackAnimation(std::string_view group);
|
||||||
|
|
||||||
bool isScriptedAnimPlaying() const;
|
|
||||||
bool isMovementAnimationControlled() const;
|
bool isMovementAnimationControlled() const;
|
||||||
|
|
||||||
void updateAnimQueue();
|
void updateAnimQueue();
|
||||||
|
@ -278,6 +277,7 @@ namespace MWMechanics
|
||||||
bool playGroup(std::string_view groupname, int mode, int count, bool scripted = false);
|
bool playGroup(std::string_view groupname, int mode, int count, bool scripted = false);
|
||||||
void skipAnim();
|
void skipAnim();
|
||||||
bool isAnimPlaying(std::string_view groupName) const;
|
bool isAnimPlaying(std::string_view groupName) const;
|
||||||
|
bool isScriptedAnimPlaying() const;
|
||||||
|
|
||||||
enum KillResult
|
enum KillResult
|
||||||
{
|
{
|
||||||
|
|
|
@ -135,6 +135,15 @@ namespace MWMechanics
|
||||||
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
if (Misc::Rng::roll0to99(prng) < x)
|
if (Misc::Rng::roll0to99(prng) < x)
|
||||||
{
|
{
|
||||||
|
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
|
const ESM::RefId skill = shield->getClass().getEquipmentSkill(*shield);
|
||||||
|
if (skill == ESM::Skill::LightArmor)
|
||||||
|
sndMgr->playSound3D(blocker, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f);
|
||||||
|
else if (skill == ESM::Skill::MediumArmor)
|
||||||
|
sndMgr->playSound3D(blocker, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f);
|
||||||
|
else if (skill == ESM::Skill::HeavyArmor)
|
||||||
|
sndMgr->playSound3D(blocker, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f);
|
||||||
|
|
||||||
// Reduce shield durability by incoming damage
|
// Reduce shield durability by incoming damage
|
||||||
int shieldhealth = shield->getClass().getItemHealth(*shield);
|
int shieldhealth = shield->getClass().getItemHealth(*shield);
|
||||||
|
|
||||||
|
|
|
@ -771,6 +771,14 @@ namespace MWMechanics
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MechanicsManager::checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const
|
||||||
|
{
|
||||||
|
if (ptr.getClass().isActor())
|
||||||
|
return mActors.checkScriptedAnimationPlaying(ptr);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool MechanicsManager::onOpen(const MWWorld::Ptr& ptr)
|
bool MechanicsManager::onOpen(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
if (ptr.getClass().isActor())
|
if (ptr.getClass().isActor())
|
||||||
|
|
|
@ -145,6 +145,7 @@ namespace MWMechanics
|
||||||
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted = false) override;
|
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted = false) override;
|
||||||
void skipAnimation(const MWWorld::Ptr& ptr) override;
|
void skipAnimation(const MWWorld::Ptr& ptr) override;
|
||||||
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) override;
|
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) override;
|
||||||
|
bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const override;
|
||||||
void persistAnimationStates() override;
|
void persistAnimationStates() override;
|
||||||
|
|
||||||
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
|
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
|
||||||
|
|
|
@ -101,7 +101,7 @@ namespace MWRender
|
||||||
templateNode, mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager(), &rotation);
|
templateNode, mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager(), &rotation);
|
||||||
}
|
}
|
||||||
return SceneUtil::attach(
|
return SceneUtil::attach(
|
||||||
templateNode, mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager());
|
std::move(templateNode), mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ActorAnimation::getShieldMesh(const MWWorld::ConstPtr& shield, bool female) const
|
std::string ActorAnimation::getShieldMesh(const MWWorld::ConstPtr& shield, bool female) const
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <components/sceneutil/keyframe.hpp>
|
#include <components/sceneutil/keyframe.hpp>
|
||||||
|
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
|
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
#include <components/sceneutil/lightutil.hpp>
|
#include <components/sceneutil/lightutil.hpp>
|
||||||
|
|
|
@ -422,7 +422,8 @@ namespace MWRender
|
||||||
if (cellX > mMaxX || cellX < mMinX || cellY > mMaxY || cellY < mMinY)
|
if (cellX > mMaxX || cellX < mMinX || cellY > mMaxY || cellY < mMinY)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
requestOverlayTextureUpdate(originX, mHeight - originY, cellSize, cellSize, localMapTexture, false, true);
|
requestOverlayTextureUpdate(
|
||||||
|
originX, mHeight - originY, cellSize, cellSize, std::move(localMapTexture), false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalMap::clear()
|
void GlobalMap::clear()
|
||||||
|
@ -554,7 +555,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
mOverlayImage = image;
|
mOverlayImage = image;
|
||||||
|
|
||||||
requestOverlayTextureUpdate(0, 0, mWidth, mHeight, texture, true, false);
|
requestOverlayTextureUpdate(0, 0, mWidth, mHeight, std::move(texture), true, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -562,7 +563,7 @@ namespace MWRender
|
||||||
// In the latter case, we'll want filtering.
|
// In the latter case, we'll want filtering.
|
||||||
// Create a RTT Camera and draw the image onto mOverlayImage in the next frame.
|
// Create a RTT Camera and draw the image onto mOverlayImage in the next frame.
|
||||||
requestOverlayTextureUpdate(destBox.mLeft, destBox.mTop, destBox.mRight - destBox.mLeft,
|
requestOverlayTextureUpdate(destBox.mLeft, destBox.mTop, destBox.mRight - destBox.mLeft,
|
||||||
destBox.mBottom - destBox.mTop, texture, true, true, srcBox.mLeft / float(imageWidth),
|
destBox.mBottom - destBox.mTop, std::move(texture), true, true, srcBox.mLeft / float(imageWidth),
|
||||||
srcBox.mTop / float(imageHeight), srcBox.mRight / float(imageWidth),
|
srcBox.mTop / float(imageHeight), srcBox.mRight / float(imageWidth),
|
||||||
srcBox.mBottom / float(imageHeight));
|
srcBox.mBottom / float(imageHeight));
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace MWRender
|
||||||
auto resolveFragment = shaderManager.getShader("luminance/resolve.frag", defines);
|
auto resolveFragment = shaderManager.getShader("luminance/resolve.frag", defines);
|
||||||
|
|
||||||
mResolveProgram = shaderManager.getProgram(vertex, std::move(resolveFragment));
|
mResolveProgram = shaderManager.getProgram(vertex, std::move(resolveFragment));
|
||||||
mLuminanceProgram = shaderManager.getProgram(vertex, std::move(luminanceFragment));
|
mLuminanceProgram = shaderManager.getProgram(std::move(vertex), std::move(luminanceFragment));
|
||||||
|
|
||||||
for (auto& buffer : mBuffers)
|
for (auto& buffer : mBuffers)
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,7 +68,7 @@ namespace MWRender
|
||||||
ptr.getClass().adjustScale(ptr, scaleVec, true);
|
ptr.getClass().adjustScale(ptr, scaleVec, true);
|
||||||
insert->setScale(scaleVec);
|
insert->setScale(scaleVec);
|
||||||
|
|
||||||
ptr.getRefData().setBaseNode(insert);
|
ptr.getRefData().setBaseNode(std::move(insert));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Objects::insertModel(const MWWorld::Ptr& ptr, const std::string& mesh, bool allowLight)
|
void Objects::insertModel(const MWWorld::Ptr& ptr, const std::string& mesh, bool allowLight)
|
||||||
|
|
|
@ -288,6 +288,8 @@ namespace MWRender
|
||||||
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)
|
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)
|
||||||
.getTexture()));
|
.getTexture()));
|
||||||
|
|
||||||
|
assert(texture != nullptr);
|
||||||
|
|
||||||
texture->setTextureSize(w, h);
|
texture->setTextureSize(w, h);
|
||||||
texture->setNumMipmapLevels(pass.mRenderTexture->getNumMipmapLevels());
|
texture->setNumMipmapLevels(pass.mRenderTexture->getNumMipmapLevels());
|
||||||
texture->dirtyTextureObject();
|
texture->dirtyTextureObject();
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <components/stereo/multiview.hpp>
|
#include <components/stereo/multiview.hpp>
|
||||||
#include <components/stereo/stereomanager.hpp>
|
#include <components/stereo/stereomanager.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
|
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
@ -662,6 +663,11 @@ namespace MWRender
|
||||||
|
|
||||||
for (const auto& name : pass->getRenderTargets())
|
for (const auto& name : pass->getRenderTargets())
|
||||||
{
|
{
|
||||||
|
if (name.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto& renderTarget = technique->getRenderTargetsMap()[name];
|
auto& renderTarget = technique->getRenderTargetsMap()[name];
|
||||||
subPass.mStateSet->setTextureAttribute(subTexUnit, renderTarget.mTarget);
|
subPass.mStateSet->setTextureAttribute(subTexUnit, renderTarget.mTarget);
|
||||||
subPass.mStateSet->addUniform(new osg::Uniform(name.c_str(), subTexUnit));
|
subPass.mStateSet->addUniform(new osg::Uniform(name.c_str(), subTexUnit));
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "precipitationocclusion.hpp"
|
#include "precipitationocclusion.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include <osgUtil/CullVisitor>
|
#include <osgUtil/CullVisitor>
|
||||||
|
|
||||||
#include <components/misc/constants.hpp>
|
#include <components/misc/constants.hpp>
|
||||||
|
@ -120,16 +122,19 @@ namespace MWRender
|
||||||
|
|
||||||
void PrecipitationOccluder::update()
|
void PrecipitationOccluder::update()
|
||||||
{
|
{
|
||||||
|
if (!mRange.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
const osg::Vec3 pos = mSceneCamera->getInverseViewMatrix().getTrans();
|
const osg::Vec3 pos = mSceneCamera->getInverseViewMatrix().getTrans();
|
||||||
|
|
||||||
const float zmin = pos.z() - mRange.z() - Constants::CellSizeInUnits;
|
const float zmin = pos.z() - mRange->z() - Constants::CellSizeInUnits;
|
||||||
const float zmax = pos.z() + mRange.z() + Constants::CellSizeInUnits;
|
const float zmax = pos.z() + mRange->z() + Constants::CellSizeInUnits;
|
||||||
const float near = 0;
|
const float near = 0;
|
||||||
const float far = zmax - zmin;
|
const float far = zmax - zmin;
|
||||||
|
|
||||||
const float left = -mRange.x() / 2;
|
const float left = -mRange->x() / 2;
|
||||||
const float right = -left;
|
const float right = -left;
|
||||||
const float top = mRange.y() / 2;
|
const float top = mRange->y() / 2;
|
||||||
const float bottom = -top;
|
const float bottom = -top;
|
||||||
|
|
||||||
if (SceneUtil::AutoDepth::isReversed())
|
if (SceneUtil::AutoDepth::isReversed())
|
||||||
|
@ -163,10 +168,14 @@ namespace MWRender
|
||||||
mSkyCullCallback = nullptr;
|
mSkyCullCallback = nullptr;
|
||||||
|
|
||||||
mRootNode->removeChild(mCamera);
|
mRootNode->removeChild(mCamera);
|
||||||
|
mRange = std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrecipitationOccluder::updateRange(const osg::Vec3f range)
|
void PrecipitationOccluder::updateRange(const osg::Vec3f range)
|
||||||
{
|
{
|
||||||
|
assert(range.x() != 0);
|
||||||
|
assert(range.y() != 0);
|
||||||
|
assert(range.z() != 0);
|
||||||
const osg::Vec3f margin = { -50, -50, 0 };
|
const osg::Vec3f margin = { -50, -50, 0 };
|
||||||
mRange = range - margin;
|
mRange = range - margin;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include <osg/Camera>
|
#include <osg/Camera>
|
||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
class PrecipitationOccluder
|
class PrecipitationOccluder
|
||||||
|
@ -27,7 +29,7 @@ namespace MWRender
|
||||||
osg::ref_ptr<osg::Camera> mCamera;
|
osg::ref_ptr<osg::Camera> mCamera;
|
||||||
osg::ref_ptr<osg::Camera> mSceneCamera;
|
osg::ref_ptr<osg::Camera> mSceneCamera;
|
||||||
osg::ref_ptr<osg::Texture2D> mDepthTexture;
|
osg::ref_ptr<osg::Texture2D> mDepthTexture;
|
||||||
osg::Vec3f mRange;
|
std::optional<osg::Vec3f> mRange;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -716,9 +716,12 @@ namespace MWRender
|
||||||
// need to wrap this in a StateUpdater?
|
// need to wrap this in a StateUpdater?
|
||||||
mSunLight->setPosition(osg::Vec4(position.x(), position.y(), position.z(), 0));
|
mSunLight->setPosition(osg::Vec4(position.x(), position.y(), position.z(), 0));
|
||||||
|
|
||||||
|
// The sun is not synchronized with the sunlight because sunlight origin can't reach the horizon
|
||||||
|
// This is based on exterior sun orbit and won't make sense for interiors, see WeatherManager::update
|
||||||
|
position.z() = 400.f - std::abs(position.x());
|
||||||
mSky->setSunDirection(position);
|
mSky->setSunDirection(position);
|
||||||
|
|
||||||
mPostProcessor->getStateUpdater()->setSunPos(mSunLight->getPosition(), mNight);
|
mPostProcessor->getStateUpdater()->setSunPos(osg::Vec4f(position, 0.f), mNight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::addCell(const MWWorld::CellStore* store)
|
void RenderingManager::addCell(const MWWorld::CellStore* store)
|
||||||
|
|
|
@ -106,7 +106,7 @@ namespace MWRender
|
||||||
mProgramBlobber = shaderManager.getProgram(
|
mProgramBlobber = shaderManager.getProgram(
|
||||||
vertex, shaderManager.getShader("ripples_blobber.frag", defineMap, osg::Shader::FRAGMENT));
|
vertex, shaderManager.getShader("ripples_blobber.frag", defineMap, osg::Shader::FRAGMENT));
|
||||||
mProgramSimulation = shaderManager.getProgram(
|
mProgramSimulation = shaderManager.getProgram(
|
||||||
vertex, shaderManager.getShader("ripples_simulate.frag", defineMap, osg::Shader::FRAGMENT));
|
std::move(vertex), shaderManager.getShader("ripples_simulate.frag", defineMap, osg::Shader::FRAGMENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RipplesSurface::setupComputePipeline()
|
void RipplesSurface::setupComputePipeline()
|
||||||
|
|
|
@ -764,7 +764,7 @@ namespace MWRender
|
||||||
cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||||
cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||||
|
|
||||||
mCloudUpdater->setTexture(cloudTex);
|
mCloudUpdater->setTexture(std::move(cloudTex));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mStormDirection != weather.mStormDirection)
|
if (mStormDirection != weather.mStormDirection)
|
||||||
|
@ -786,7 +786,7 @@ namespace MWRender
|
||||||
cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||||
cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||||
|
|
||||||
mNextCloudUpdater->setTexture(cloudTex);
|
mNextCloudUpdater->setTexture(std::move(cloudTex));
|
||||||
mNextStormDirection = weather.mStormDirection;
|
mNextStormDirection = weather.mStormDirection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -722,8 +722,8 @@ namespace MWRender
|
||||||
mRainSettingsUpdater = new RainSettingsUpdater();
|
mRainSettingsUpdater = new RainSettingsUpdater();
|
||||||
node->setUpdateCallback(mRainSettingsUpdater);
|
node->setUpdateCallback(mRainSettingsUpdater);
|
||||||
|
|
||||||
mShaderWaterStateSetUpdater
|
mShaderWaterStateSetUpdater = new ShaderWaterStateSetUpdater(
|
||||||
= new ShaderWaterStateSetUpdater(this, mReflection, mRefraction, mRipples, std::move(program), normalMap);
|
this, mReflection, mRefraction, mRipples, std::move(program), std::move(normalMap));
|
||||||
node->addCullCallback(mShaderWaterStateSetUpdater);
|
node->addCullCallback(mShaderWaterStateSetUpdater);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
#include <components/vfs/pathutil.hpp>
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <components/loadinglistener/loadinglistener.hpp>
|
#include <components/loadinglistener/loadinglistener.hpp>
|
||||||
|
|
||||||
#include <components/files/conversion.hpp>
|
#include <components/files/conversion.hpp>
|
||||||
|
#include <components/misc/algorithm.hpp>
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
|
|
||||||
#include <osg/Image>
|
#include <osg/Image>
|
||||||
|
@ -81,10 +82,8 @@ std::map<int, int> MWState::StateManager::buildContentFileIndexMap(const ESM::ES
|
||||||
|
|
||||||
for (int iPrev = 0; iPrev < static_cast<int>(prev.size()); ++iPrev)
|
for (int iPrev = 0; iPrev < static_cast<int>(prev.size()); ++iPrev)
|
||||||
{
|
{
|
||||||
std::string id = Misc::StringUtils::lowerCase(prev[iPrev].name);
|
|
||||||
|
|
||||||
for (int iCurrent = 0; iCurrent < static_cast<int>(current.size()); ++iCurrent)
|
for (int iCurrent = 0; iCurrent < static_cast<int>(current.size()); ++iCurrent)
|
||||||
if (id == Misc::StringUtils::lowerCase(current[iCurrent]))
|
if (Misc::StringUtils::ciEqual(prev[iPrev].name, current[iCurrent]))
|
||||||
{
|
{
|
||||||
map.insert(std::make_pair(iPrev, iCurrent));
|
map.insert(std::make_pair(iPrev, iCurrent));
|
||||||
break;
|
break;
|
||||||
|
@ -410,9 +409,38 @@ void MWState::StateManager::loadGame(const std::filesystem::path& filepath)
|
||||||
loadGame(character, filepath);
|
loadGame(character, filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VersionMismatchError : public std::runtime_error
|
struct SaveFormatVersionError : public std::exception
|
||||||
{
|
{
|
||||||
using std::runtime_error::runtime_error;
|
using std::exception::exception;
|
||||||
|
|
||||||
|
SaveFormatVersionError(ESM::FormatVersion savegameFormat, const std::string& message)
|
||||||
|
: mSavegameFormat(savegameFormat)
|
||||||
|
, mErrorMessage(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* what() const noexcept override { return mErrorMessage.c_str(); }
|
||||||
|
ESM::FormatVersion getFormatVersion() const { return mSavegameFormat; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ESM::FormatVersion mSavegameFormat = ESM::DefaultFormatVersion;
|
||||||
|
std::string mErrorMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SaveVersionTooOldError : SaveFormatVersionError
|
||||||
|
{
|
||||||
|
SaveVersionTooOldError(ESM::FormatVersion savegameFormat)
|
||||||
|
: SaveFormatVersionError(savegameFormat, "format version " + std::to_string(savegameFormat) + " is too old")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SaveVersionTooNewError : SaveFormatVersionError
|
||||||
|
{
|
||||||
|
SaveVersionTooNewError(ESM::FormatVersion savegameFormat)
|
||||||
|
: SaveFormatVersionError(savegameFormat, "format version " + std::to_string(savegameFormat) + " is too new")
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void MWState::StateManager::loadGame(const Character* character, const std::filesystem::path& filepath)
|
void MWState::StateManager::loadGame(const Character* character, const std::filesystem::path& filepath)
|
||||||
|
@ -428,23 +456,9 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
||||||
|
|
||||||
ESM::FormatVersion version = reader.getFormatVersion();
|
ESM::FormatVersion version = reader.getFormatVersion();
|
||||||
if (version > ESM::CurrentSaveGameFormatVersion)
|
if (version > ESM::CurrentSaveGameFormatVersion)
|
||||||
throw VersionMismatchError("#{OMWEngine:LoadingRequiresNewVersionError}");
|
throw SaveVersionTooNewError(version);
|
||||||
else if (version < ESM::MinSupportedSaveGameFormatVersion)
|
else if (version < ESM::MinSupportedSaveGameFormatVersion)
|
||||||
{
|
throw SaveVersionTooOldError(version);
|
||||||
const char* release;
|
|
||||||
// Report the last version still capable of reading this save
|
|
||||||
if (version <= ESM::OpenMW0_48SaveGameFormatVersion)
|
|
||||||
release = "OpenMW 0.48.0";
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Insert additional else if statements above to cover future releases
|
|
||||||
static_assert(ESM::MinSupportedSaveGameFormatVersion <= ESM::OpenMW0_49SaveGameFormatVersion);
|
|
||||||
release = "OpenMW 0.49.0";
|
|
||||||
}
|
|
||||||
auto l10n = MWBase::Environment::get().getL10nManager()->getContext("OMWEngine");
|
|
||||||
std::string message = l10n->formatMessage("LoadingRequiresOldVersionError", { "version" }, { release });
|
|
||||||
throw VersionMismatchError(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<int, int> contentFileMap = buildContentFileIndexMap(reader);
|
std::map<int, int> contentFileMap = buildContentFileIndexMap(reader);
|
||||||
reader.setContentFileMapping(&contentFileMap);
|
reader.setContentFileMapping(&contentFileMap);
|
||||||
|
@ -626,23 +640,49 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
||||||
|
|
||||||
MWBase::Environment::get().getLuaManager()->gameLoaded();
|
MWBase::Environment::get().getLuaManager()->gameLoaded();
|
||||||
}
|
}
|
||||||
|
catch (const SaveVersionTooNewError& e)
|
||||||
|
{
|
||||||
|
std::string error = "#{OMWEngine:LoadingRequiresNewVersionError}";
|
||||||
|
printSavegameFormatError(e.what(), error);
|
||||||
|
}
|
||||||
|
catch (const SaveVersionTooOldError& e)
|
||||||
|
{
|
||||||
|
const char* release;
|
||||||
|
// Report the last version still capable of reading this save
|
||||||
|
if (e.getFormatVersion() <= ESM::OpenMW0_48SaveGameFormatVersion)
|
||||||
|
release = "OpenMW 0.48.0";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Insert additional else if statements above to cover future releases
|
||||||
|
static_assert(ESM::MinSupportedSaveGameFormatVersion <= ESM::OpenMW0_49SaveGameFormatVersion);
|
||||||
|
release = "OpenMW 0.49.0";
|
||||||
|
}
|
||||||
|
auto l10n = MWBase::Environment::get().getL10nManager()->getContext("OMWEngine");
|
||||||
|
std::string error = l10n->formatMessage("LoadingRequiresOldVersionError", { "version" }, { release });
|
||||||
|
printSavegameFormatError(e.what(), error);
|
||||||
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Failed to load saved game: " << e.what();
|
|
||||||
|
|
||||||
cleanup(true);
|
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu);
|
|
||||||
|
|
||||||
std::vector<std::string> buttons;
|
|
||||||
buttons.emplace_back("#{Interface:OK}");
|
|
||||||
|
|
||||||
std::string error = "#{OMWEngine:LoadingFailed}: " + std::string(e.what());
|
std::string error = "#{OMWEngine:LoadingFailed}: " + std::string(e.what());
|
||||||
|
printSavegameFormatError(e.what(), error);
|
||||||
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error, buttons);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MWState::StateManager::printSavegameFormatError(
|
||||||
|
const std::string& exceptionText, const std::string& messageBoxText)
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Failed to load saved game: " << exceptionText;
|
||||||
|
|
||||||
|
cleanup(true);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu);
|
||||||
|
|
||||||
|
std::vector<std::string> buttons;
|
||||||
|
buttons.emplace_back("#{Interface:OK}");
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(messageBoxText, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
void MWState::StateManager::quickLoad()
|
void MWState::StateManager::quickLoad()
|
||||||
{
|
{
|
||||||
if (Character* currentCharacter = getCurrentCharacter())
|
if (Character* currentCharacter = getCurrentCharacter())
|
||||||
|
|
|
@ -22,6 +22,8 @@ namespace MWState
|
||||||
private:
|
private:
|
||||||
void cleanup(bool force = false);
|
void cleanup(bool force = false);
|
||||||
|
|
||||||
|
void printSavegameFormatError(const std::string& exceptionText, const std::string& messageBoxText);
|
||||||
|
|
||||||
bool confirmLoading(const std::vector<std::string_view>& missingFiles) const;
|
bool confirmLoading(const std::vector<std::string_view>& missingFiles) const;
|
||||||
|
|
||||||
void writeScreenshot(std::vector<char>& imageData) const;
|
void writeScreenshot(std::vector<char>& imageData) const;
|
||||||
|
|
|
@ -52,20 +52,13 @@ namespace MWWorld
|
||||||
|
|
||||||
struct ListModelsVisitor
|
struct ListModelsVisitor
|
||||||
{
|
{
|
||||||
ListModelsVisitor(std::vector<std::string>& out)
|
bool operator()(const MWWorld::ConstPtr& ptr)
|
||||||
: mOut(out)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool operator()(const MWWorld::Ptr& ptr)
|
|
||||||
{
|
{
|
||||||
ptr.getClass().getModelsToPreload(ptr, mOut);
|
ptr.getClass().getModelsToPreload(ptr, mOut);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~ListModelsVisitor() = default;
|
|
||||||
|
|
||||||
std::vector<std::string>& mOut;
|
std::vector<std::string>& mOut;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,8 +83,8 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
mTerrainView = mTerrain->createView();
|
mTerrainView = mTerrain->createView();
|
||||||
|
|
||||||
ListModelsVisitor visitor(mMeshes);
|
ListModelsVisitor visitor{ mMeshes };
|
||||||
cell->forEach(visitor);
|
cell->forEachConst(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void abort() override { mAbort = true; }
|
void abort() override { mAbort = true; }
|
||||||
|
|
|
@ -209,8 +209,8 @@ namespace MWWorld
|
||||||
/// false will abort the iteration.
|
/// false will abort the iteration.
|
||||||
/// \note Prefer using forEachConst when possible.
|
/// \note Prefer using forEachConst when possible.
|
||||||
/// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in
|
/// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in
|
||||||
/// unintended behaviour. \attention This function also lists deleted (count 0) objects! \return Iteration
|
/// unintended behaviour. \attention This function also lists deleted (count 0) objects!
|
||||||
/// completed?
|
/// \return Iteration completed?
|
||||||
template <class Visitor>
|
template <class Visitor>
|
||||||
bool forEach(Visitor&& visitor)
|
bool forEach(Visitor&& visitor)
|
||||||
{
|
{
|
||||||
|
@ -224,12 +224,12 @@ namespace MWWorld
|
||||||
|
|
||||||
mHasState = true;
|
mHasState = true;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mMergedRefs.size(); ++i)
|
for (LiveCellRefBase* mergedRef : mMergedRefs)
|
||||||
{
|
{
|
||||||
if (!isAccessible(mMergedRefs[i]->mData, mMergedRefs[i]->mRef))
|
if (!isAccessible(mergedRef->mData, mergedRef->mRef))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!visitor(MWWorld::Ptr(mMergedRefs[i], this)))
|
if (!visitor(MWWorld::Ptr(mergedRef, this)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -238,8 +238,8 @@ namespace MWWorld
|
||||||
/// Call visitor (MWWorld::ConstPtr) for each reference. visitor must return a bool. Returning
|
/// Call visitor (MWWorld::ConstPtr) for each reference. visitor must return a bool. Returning
|
||||||
/// false will abort the iteration.
|
/// false will abort the iteration.
|
||||||
/// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in
|
/// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in
|
||||||
/// unintended behaviour. \attention This function also lists deleted (count 0) objects! \return Iteration
|
/// unintended behaviour. \attention This function also lists deleted (count 0) objects!
|
||||||
/// completed?
|
/// \return Iteration completed?
|
||||||
template <class Visitor>
|
template <class Visitor>
|
||||||
bool forEachConst(Visitor&& visitor) const
|
bool forEachConst(Visitor&& visitor) const
|
||||||
{
|
{
|
||||||
|
@ -249,12 +249,12 @@ namespace MWWorld
|
||||||
if (mMergedRefsNeedsUpdate)
|
if (mMergedRefsNeedsUpdate)
|
||||||
updateMergedRefs();
|
updateMergedRefs();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mMergedRefs.size(); ++i)
|
for (const LiveCellRefBase* mergedRef : mMergedRefs)
|
||||||
{
|
{
|
||||||
if (!isAccessible(mMergedRefs[i]->mData, mMergedRefs[i]->mRef))
|
if (!isAccessible(mergedRef->mData, mergedRef->mRef))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!visitor(MWWorld::ConstPtr(mMergedRefs[i], this)))
|
if (!visitor(MWWorld::ConstPtr(mergedRef, this)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -263,8 +263,8 @@ namespace MWWorld
|
||||||
/// Call visitor (ref) for each reference of given type. visitor must return a bool. Returning
|
/// Call visitor (ref) for each reference of given type. visitor must return a bool. Returning
|
||||||
/// false will abort the iteration.
|
/// false will abort the iteration.
|
||||||
/// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in
|
/// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in
|
||||||
/// unintended behaviour. \attention This function also lists deleted (count 0) objects! \return Iteration
|
/// unintended behaviour. \attention This function also lists deleted (count 0) objects!
|
||||||
/// completed?
|
/// \return Iteration completed?
|
||||||
template <class T, class Visitor>
|
template <class T, class Visitor>
|
||||||
bool forEachType(Visitor&& visitor)
|
bool forEachType(Visitor&& visitor)
|
||||||
{
|
{
|
||||||
|
|
|
@ -118,11 +118,6 @@ namespace MWWorld
|
||||||
throw std::runtime_error("class cannot hit");
|
throw std::runtime_error("class cannot hit");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Class::block(const Ptr& ptr) const
|
|
||||||
{
|
|
||||||
throw std::runtime_error("class cannot block");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker,
|
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker,
|
||||||
const osg::Vec3f& hitPosition, bool successful, const MWMechanics::DamageSourceType sourceType) const
|
const osg::Vec3f& hitPosition, bool successful, const MWMechanics::DamageSourceType sourceType) const
|
||||||
{
|
{
|
||||||
|
@ -149,7 +144,7 @@ namespace MWWorld
|
||||||
throw std::runtime_error("class does not have an inventory store");
|
throw std::runtime_error("class does not have an inventory store");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Class::hasInventoryStore(const Ptr& ptr) const
|
bool Class::hasInventoryStore(const ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -320,7 +315,7 @@ namespace MWWorld
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Class::getModelsToPreload(const Ptr& ptr, std::vector<std::string>& models) const
|
void Class::getModelsToPreload(const ConstPtr& ptr, std::vector<std::string>& models) const
|
||||||
{
|
{
|
||||||
std::string model = getModel(ptr);
|
std::string model = getModel(ptr);
|
||||||
if (!model.empty())
|
if (!model.empty())
|
||||||
|
|
|
@ -151,10 +151,6 @@ namespace MWWorld
|
||||||
/// actor responsible for the attack. \a successful specifies if the hit is
|
/// actor responsible for the attack. \a successful specifies if the hit is
|
||||||
/// successful or not. \a sourceType classifies the damage source.
|
/// successful or not. \a sourceType classifies the damage source.
|
||||||
|
|
||||||
virtual void block(const Ptr& ptr) const;
|
|
||||||
///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield
|
|
||||||
/// (default implementation: throw an exception)
|
|
||||||
|
|
||||||
virtual std::unique_ptr<Action> activate(const Ptr& ptr, const Ptr& actor) const;
|
virtual std::unique_ptr<Action> activate(const Ptr& ptr, const Ptr& actor) const;
|
||||||
///< Generate action for activation (default implementation: return a null action).
|
///< Generate action for activation (default implementation: return a null action).
|
||||||
|
|
||||||
|
@ -170,7 +166,7 @@ namespace MWWorld
|
||||||
///< Return inventory store or throw an exception, if class does not have a
|
///< Return inventory store or throw an exception, if class does not have a
|
||||||
/// inventory store (default implementation: throw an exception)
|
/// inventory store (default implementation: throw an exception)
|
||||||
|
|
||||||
virtual bool hasInventoryStore(const Ptr& ptr) const;
|
virtual bool hasInventoryStore(const ConstPtr& ptr) const;
|
||||||
///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false)
|
///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false)
|
||||||
|
|
||||||
virtual bool canLock(const ConstPtr& ptr) const;
|
virtual bool canLock(const ConstPtr& ptr) const;
|
||||||
|
@ -284,7 +280,7 @@ namespace MWWorld
|
||||||
virtual bool useAnim() const;
|
virtual bool useAnim() const;
|
||||||
///< Whether or not to use animated variant of model (default false)
|
///< Whether or not to use animated variant of model (default false)
|
||||||
|
|
||||||
virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const;
|
virtual void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const;
|
||||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||||
///< list getModel().
|
///< list getModel().
|
||||||
|
|
||||||
|
|
|
@ -747,21 +747,21 @@ namespace MWWorld
|
||||||
const float dayDuration = adjustedNightStart - mSunriseTime;
|
const float dayDuration = adjustedNightStart - mSunriseTime;
|
||||||
const float nightDuration = 24.f - dayDuration;
|
const float nightDuration = 24.f - dayDuration;
|
||||||
|
|
||||||
double theta;
|
float orbit;
|
||||||
if (!is_night)
|
if (!is_night)
|
||||||
{
|
{
|
||||||
theta = static_cast<float>(osg::PI) * (adjustedHour - mSunriseTime) / dayDuration;
|
float t = (adjustedHour - mSunriseTime) / dayDuration;
|
||||||
|
orbit = 1.f - 2.f * t;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
theta = static_cast<float>(osg::PI)
|
float t = (adjustedHour - adjustedNightStart) / nightDuration;
|
||||||
- static_cast<float>(osg::PI) * (adjustedHour - adjustedNightStart) / nightDuration;
|
orbit = 2.f * t - 1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f final(static_cast<float>(cos(theta)),
|
// Hardcoded constant from Morrowind
|
||||||
-0.268f, // approx tan( -15 degrees )
|
const osg::Vec3f sunDir(-400.f * orbit, 75.f, -100.f);
|
||||||
static_cast<float>(sin(theta)));
|
mRendering.setSunDirection(sunDir);
|
||||||
mRendering.setSunDirection(final * -1);
|
|
||||||
mRendering.setNight(is_night);
|
mRendering.setNight(is_night);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/scriptmanager.hpp"
|
#include "../mwbase/scriptmanager.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
#include "../mwbase/statemanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
@ -997,6 +998,9 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
MWWorld::Ptr facedObject;
|
MWWorld::Ptr facedObject;
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
|
||||||
|
return facedObject;
|
||||||
|
|
||||||
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|
||||||
&& MWBase::Environment::get().getWindowManager()->isConsoleMode())
|
&& MWBase::Environment::get().getWindowManager()->isConsoleMode())
|
||||||
facedObject = getFacedObject(getMaxActivationDistance() * 50, false);
|
facedObject = getFacedObject(getMaxActivationDistance() * 50, false);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <components/detournavigator/debug.hpp>
|
||||||
#include <components/detournavigator/gettilespositions.hpp>
|
#include <components/detournavigator/gettilespositions.hpp>
|
||||||
#include <components/detournavigator/settings.hpp>
|
#include <components/detournavigator/settings.hpp>
|
||||||
|
|
||||||
|
@ -86,4 +87,79 @@ namespace
|
||||||
|
|
||||||
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TilesPositionsRangeParams
|
||||||
|
{
|
||||||
|
TilesPositionsRange mA;
|
||||||
|
TilesPositionsRange mB;
|
||||||
|
TilesPositionsRange mResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DetourNavigatorGetIntersectionTest : TestWithParam<TilesPositionsRangeParams>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(DetourNavigatorGetIntersectionTest, should_return_expected_result)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(getIntersection(GetParam().mA, GetParam().mB), GetParam().mResult);
|
||||||
|
EXPECT_EQ(getIntersection(GetParam().mB, GetParam().mA), GetParam().mResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TilesPositionsRangeParams getIntersectionParams[] = {
|
||||||
|
{ .mA = TilesPositionsRange{}, .mB = TilesPositionsRange{}, .mResult = TilesPositionsRange{} },
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 2, 2 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
.mResult = TilesPositionsRange{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 0, 2 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
.mResult = TilesPositionsRange{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 2, 0 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
.mResult = TilesPositionsRange{},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
GetIntersectionParams, DetourNavigatorGetIntersectionTest, ValuesIn(getIntersectionParams));
|
||||||
|
|
||||||
|
struct DetourNavigatorGetUnionTest : TestWithParam<TilesPositionsRangeParams>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(DetourNavigatorGetUnionTest, should_return_expected_result)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(getUnion(GetParam().mA, GetParam().mB), GetParam().mResult);
|
||||||
|
EXPECT_EQ(getUnion(GetParam().mB, GetParam().mA), GetParam().mResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TilesPositionsRangeParams getUnionParams[] = {
|
||||||
|
{ .mA = TilesPositionsRange{}, .mB = TilesPositionsRange{}, .mResult = TilesPositionsRange{} },
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(GetUnionParams, DetourNavigatorGetUnionTest, ValuesIn(getUnionParams));
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ namespace
|
||||||
using namespace DetourNavigator;
|
using namespace DetourNavigator;
|
||||||
using namespace DetourNavigator::Tests;
|
using namespace DetourNavigator::Tests;
|
||||||
|
|
||||||
|
constexpr int heightfieldTileSize = ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1);
|
||||||
|
|
||||||
struct DetourNavigatorNavigatorTest : Test
|
struct DetourNavigatorNavigatorTest : Test
|
||||||
{
|
{
|
||||||
Settings mSettings = makeSettings();
|
Settings mSettings = makeSettings();
|
||||||
|
@ -53,7 +55,6 @@ namespace
|
||||||
AreaCosts mAreaCosts;
|
AreaCosts mAreaCosts;
|
||||||
Loading::Listener mListener;
|
Loading::Listener mListener;
|
||||||
const osg::Vec2i mCellPosition{ 0, 0 };
|
const osg::Vec2i mCellPosition{ 0, 0 };
|
||||||
const int mHeightfieldTileSize = ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1);
|
|
||||||
const float mEndTolerance = 0;
|
const float mEndTolerance = 0;
|
||||||
const btTransform mTransform{ btMatrix3x3::getIdentity(), btVector3(256, 256, 0) };
|
const btTransform mTransform{ btMatrix3x3::getIdentity(), btVector3(256, 256, 0) };
|
||||||
const ObjectTransform mObjectTransform{ ESM::Position{ { 256, 256, 0 }, { 0, 0, 0 } }, 0.0f };
|
const ObjectTransform mObjectTransform{ ESM::Position{ { 256, 256, 0 }, { 0, 0, 0 } }, 0.0f };
|
||||||
|
@ -129,7 +130,7 @@ namespace
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
T& shape() { return static_cast<T&>(*mInstance->mCollisionShape); }
|
T& shape() const { return static_cast<T&>(*mInstance->mCollisionShape); }
|
||||||
const osg::ref_ptr<const Resource::BulletShapeInstance>& instance() const { return mInstance; }
|
const osg::ref_ptr<const Resource::BulletShapeInstance>& instance() const { return mInstance; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -167,7 +168,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, update_then_find_path_should_return_path)
|
TEST_F(DetourNavigatorNavigatorTest, update_then_find_path_should_return_path)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||||
|
@ -189,7 +190,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_path_to_the_start_position_should_contain_single_point)
|
TEST_F(DetourNavigatorNavigatorTest, find_path_to_the_start_position_should_contain_single_point)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||||
|
@ -211,7 +212,7 @@ namespace
|
||||||
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
|
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
|
||||||
|
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||||
compound.shape().addChildShape(
|
compound.shape().addChildShape(
|
||||||
|
@ -256,7 +257,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, update_changed_object_should_change_navmesh)
|
TEST_F(DetourNavigatorNavigatorTest, update_changed_object_should_change_navmesh)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||||
compound.shape().addChildShape(
|
compound.shape().addChildShape(
|
||||||
|
@ -335,7 +336,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, only_one_heightfield_per_cell_is_allowed)
|
TEST_F(DetourNavigatorNavigatorTest, only_one_heightfield_per_cell_is_allowed)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface1 = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface1 = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize1 = mHeightfieldTileSize * (surface1.mSize - 1);
|
const int cellSize1 = heightfieldTileSize * (surface1.mSize - 1);
|
||||||
|
|
||||||
const std::array<float, 5 * 5> heightfieldData2{ {
|
const std::array<float, 5 * 5> heightfieldData2{ {
|
||||||
-25, -25, -25, -25, -25, // row 0
|
-25, -25, -25, -25, -25, // row 0
|
||||||
|
@ -345,7 +346,7 @@ namespace
|
||||||
-25, -25, -25, -25, -25, // row 4
|
-25, -25, -25, -25, -25, // row 4
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface2 = makeSquareHeightfieldSurface(heightfieldData2);
|
const HeightfieldSurface surface2 = makeSquareHeightfieldSurface(heightfieldData2);
|
||||||
const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1);
|
const int cellSize2 = heightfieldTileSize * (surface2.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize1, surface1, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize1, surface1, nullptr);
|
||||||
|
@ -412,7 +413,7 @@ namespace
|
||||||
0, -50, -100, -100, -100, // row 4
|
0, -50, -100, -100, -100, // row 4
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addWater(mCellPosition, cellSize, 300, nullptr);
|
mNavigator->addWater(mCellPosition, cellSize, 300, nullptr);
|
||||||
|
@ -446,7 +447,7 @@ namespace
|
||||||
0, 0, 0, 0, 0, 0, 0, // row 6
|
0, 0, 0, 0, 0, 0, 0, // row 6
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
|
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
|
||||||
|
@ -480,7 +481,7 @@ namespace
|
||||||
0, 0, 0, 0, 0, 0, 0, // row 6
|
0, 0, 0, 0, 0, 0, 0, // row 6
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
@ -513,7 +514,7 @@ namespace
|
||||||
0, 0, 0, 0, 0, 0, 0, // row 6
|
0, 0, 0, 0, 0, 0, 0, // row 6
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
|
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
|
||||||
|
@ -566,7 +567,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, update_heightfield_remove_and_update_then_find_path_should_return_path)
|
TEST_F(DetourNavigatorNavigatorTest, update_heightfield_remove_and_update_then_find_path_should_return_path)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
@ -602,7 +603,7 @@ namespace
|
||||||
0, -25, -100, -100, -100, -100, // row 5
|
0, -25, -100, -100, -100, -100, // row 5
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
@ -629,7 +630,7 @@ namespace
|
||||||
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
|
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
|
||||||
|
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
const btVector3 shift = getHeightfieldShift(mCellPosition, cellSize, surface.mMinHeight, surface.mMaxHeight);
|
const btVector3 shift = getHeightfieldShift(mCellPosition, cellSize, surface.mMinHeight, surface.mMaxHeight);
|
||||||
|
|
||||||
std::vector<CollisionShapeInstance<btBoxShape>> boxes;
|
std::vector<CollisionShapeInstance<btBoxShape>> boxes;
|
||||||
|
@ -718,7 +719,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, update_then_raycast_should_return_position)
|
TEST_F(DetourNavigatorNavigatorTest, update_then_raycast_should_return_position)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
@ -737,7 +738,7 @@ namespace
|
||||||
update_for_oscillating_object_that_does_not_change_navmesh_should_not_trigger_navmesh_update)
|
update_for_oscillating_object_that_does_not_change_navmesh_should_not_trigger_navmesh_update)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
CollisionShapeInstance oscillatingBox(std::make_unique<btBoxShape>(btVector3(20, 20, 20)));
|
CollisionShapeInstance oscillatingBox(std::make_unique<btBoxShape>(btVector3(20, 20, 20)));
|
||||||
const btVector3 oscillatingBoxShapePosition(288, 288, 400);
|
const btVector3 oscillatingBoxShapePosition(288, 288, 400);
|
||||||
|
@ -777,7 +778,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, should_provide_path_over_flat_heightfield)
|
TEST_F(DetourNavigatorNavigatorTest, should_provide_path_over_flat_heightfield)
|
||||||
{
|
{
|
||||||
const HeightfieldPlane plane{ 100 };
|
const HeightfieldPlane plane{ 100 };
|
||||||
const int cellSize = mHeightfieldTileSize * 4;
|
const int cellSize = heightfieldTileSize * 4;
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize, plane, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize, plane, nullptr);
|
||||||
|
@ -796,7 +797,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, for_not_reachable_destination_find_path_should_provide_partial_path)
|
TEST_F(DetourNavigatorNavigatorTest, for_not_reachable_destination_find_path_should_provide_partial_path)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||||
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
|
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
|
||||||
|
@ -822,7 +823,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, end_tolerance_should_extent_available_destinations)
|
TEST_F(DetourNavigatorNavigatorTest, end_tolerance_should_extent_available_destinations)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||||
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
|
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
|
||||||
|
@ -948,7 +949,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nav_mesh_position)
|
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nav_mesh_position)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||||
|
@ -966,7 +967,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_too_far)
|
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_too_far)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||||
|
@ -984,7 +985,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_flags_do_not_match)
|
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_flags_do_not_match)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||||
|
@ -998,4 +999,142 @@ namespace
|
||||||
EXPECT_EQ(findNearestNavMeshPosition(*mNavigator, mAgentBounds, position, searchAreaHalfExtents, Flag_swim),
|
EXPECT_EQ(findNearestNavMeshPosition(*mNavigator, mAgentBounds, position, searchAreaHalfExtents, Flag_swim),
|
||||||
std::nullopt);
|
std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DetourNavigatorUpdateTest : TestWithParam<std::function<void(Navigator&)>>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<TilePosition> getUsedTiles(const NavMeshCacheItem& navMesh)
|
||||||
|
{
|
||||||
|
std::vector<TilePosition> result;
|
||||||
|
navMesh.forEachUsedTile([&](const TilePosition& position, const auto&...) { result.push_back(position); });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(DetourNavigatorUpdateTest, update_should_change_covered_area_when_player_moves)
|
||||||
|
{
|
||||||
|
Loading::Listener listener;
|
||||||
|
Settings settings = makeSettings();
|
||||||
|
settings.mMaxTilesNumber = 5;
|
||||||
|
NavigatorImpl navigator(settings, nullptr);
|
||||||
|
const AgentBounds agentBounds{ CollisionShapeType::Aabb, { 29, 29, 66 } };
|
||||||
|
ASSERT_TRUE(navigator.addAgent(agentBounds));
|
||||||
|
|
||||||
|
GetParam()(navigator);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto updateGuard = navigator.makeUpdateGuard();
|
||||||
|
navigator.update(osg::Vec3f(3000, 3000, 0), updateGuard.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.wait(WaitConditionType::allJobsDone, &listener);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto navMesh = navigator.getNavMesh(agentBounds);
|
||||||
|
ASSERT_NE(navMesh, nullptr);
|
||||||
|
|
||||||
|
const TilePosition expectedTiles[] = { { 3, 4 }, { 4, 3 }, { 4, 4 }, { 4, 5 }, { 5, 4 } };
|
||||||
|
const auto usedTiles = getUsedTiles(*navMesh->lockConst());
|
||||||
|
EXPECT_THAT(usedTiles, UnorderedElementsAreArray(expectedTiles)) << usedTiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto updateGuard = navigator.makeUpdateGuard();
|
||||||
|
navigator.update(osg::Vec3f(4000, 3000, 0), updateGuard.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.wait(WaitConditionType::allJobsDone, &listener);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto navMesh = navigator.getNavMesh(agentBounds);
|
||||||
|
ASSERT_NE(navMesh, nullptr);
|
||||||
|
|
||||||
|
const TilePosition expectedTiles[] = { { 4, 4 }, { 5, 3 }, { 5, 4 }, { 5, 5 }, { 6, 4 } };
|
||||||
|
const auto usedTiles = getUsedTiles(*navMesh->lockConst());
|
||||||
|
EXPECT_THAT(usedTiles, UnorderedElementsAreArray(expectedTiles)) << usedTiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AddHeightfieldSurface
|
||||||
|
{
|
||||||
|
static constexpr std::size_t sSize = 65;
|
||||||
|
static constexpr float sHeights[sSize * sSize]{};
|
||||||
|
|
||||||
|
void operator()(Navigator& navigator) const
|
||||||
|
{
|
||||||
|
const osg::Vec2i cellPosition(0, 0);
|
||||||
|
const HeightfieldSurface surface{
|
||||||
|
.mHeights = sHeights,
|
||||||
|
.mSize = sSize,
|
||||||
|
.mMinHeight = -1,
|
||||||
|
.mMaxHeight = 1,
|
||||||
|
};
|
||||||
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
navigator.addHeightfield(cellPosition, cellSize, surface, nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddHeightfieldPlane
|
||||||
|
{
|
||||||
|
void operator()(Navigator& navigator) const
|
||||||
|
{
|
||||||
|
const osg::Vec2i cellPosition(0, 0);
|
||||||
|
const HeightfieldPlane plane{ .mHeight = 0 };
|
||||||
|
const int cellSize = 8192;
|
||||||
|
navigator.addHeightfield(cellPosition, cellSize, plane, nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddWater
|
||||||
|
{
|
||||||
|
void operator()(Navigator& navigator) const
|
||||||
|
{
|
||||||
|
const osg::Vec2i cellPosition(0, 0);
|
||||||
|
const float level = 0;
|
||||||
|
const int cellSize = 8192;
|
||||||
|
navigator.addWater(cellPosition, cellSize, level, nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddObject
|
||||||
|
{
|
||||||
|
const float mSize = 8192;
|
||||||
|
CollisionShapeInstance<btBoxShape> mBox{ std::make_unique<btBoxShape>(btVector3(mSize, mSize, 1)) };
|
||||||
|
const ObjectTransform mTransform{
|
||||||
|
.mPosition = ESM::Position{ .pos = { 0, 0, 0 }, .rot{ 0, 0, 0 } },
|
||||||
|
.mScale = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
void operator()(Navigator& navigator) const
|
||||||
|
{
|
||||||
|
navigator.addObject(ObjectId(&mBox.shape()), ObjectShapes(mBox.instance(), mTransform),
|
||||||
|
btTransform::getIdentity(), nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddAll
|
||||||
|
{
|
||||||
|
AddHeightfieldSurface mAddHeightfieldSurface;
|
||||||
|
AddHeightfieldPlane mAddHeightfieldPlane;
|
||||||
|
AddWater mAddWater;
|
||||||
|
AddObject mAddObject;
|
||||||
|
|
||||||
|
void operator()(Navigator& navigator) const
|
||||||
|
{
|
||||||
|
mAddHeightfieldSurface(navigator);
|
||||||
|
mAddHeightfieldPlane(navigator);
|
||||||
|
mAddWater(navigator);
|
||||||
|
mAddObject(navigator);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::function<void(Navigator&)> addNavMeshData[] = {
|
||||||
|
AddHeightfieldSurface{},
|
||||||
|
AddHeightfieldPlane{},
|
||||||
|
AddWater{},
|
||||||
|
AddObject{},
|
||||||
|
AddAll{},
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(DifferentNavMeshData, DetourNavigatorUpdateTest, ValuesIn(addNavMeshData));
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,12 +42,24 @@ namespace testing
|
||||||
<< ", " << value.y() << ", " << value.z() << ')';
|
<< ", " << value.y() << ", " << value.z() << ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline testing::Message& Message::operator<<(const osg::Vec2i& value)
|
||||||
|
{
|
||||||
|
return (*this) << "{" << value.x() << ", " << value.y() << '}';
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline testing::Message& Message::operator<<(const Wrapper<osg::Vec3f>& value)
|
inline testing::Message& Message::operator<<(const Wrapper<osg::Vec3f>& value)
|
||||||
{
|
{
|
||||||
return (*this) << value.mValue;
|
return (*this) << value.mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline testing::Message& Message::operator<<(const Wrapper<osg::Vec2i>& value)
|
||||||
|
{
|
||||||
|
return (*this) << value.mValue;
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline testing::Message& Message::operator<<(const Wrapper<float>& value)
|
inline testing::Message& Message::operator<<(const Wrapper<float>& value)
|
||||||
{
|
{
|
||||||
|
@ -72,6 +84,12 @@ namespace testing
|
||||||
return writeRange(*this, value, 1);
|
return writeRange(*this, value, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline testing::Message& Message::operator<<(const std::vector<osg::Vec2i>& value)
|
||||||
|
{
|
||||||
|
return writeRange(*this, value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline testing::Message& Message::operator<<(const std::vector<float>& value)
|
inline testing::Message& Message::operator<<(const std::vector<float>& value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <components/esm/fourcc.hpp>
|
#include <components/esm/fourcc.hpp>
|
||||||
|
#include <components/esm3/aisequence.hpp>
|
||||||
#include <components/esm3/esmreader.hpp>
|
#include <components/esm3/esmreader.hpp>
|
||||||
#include <components/esm3/esmwriter.hpp>
|
#include <components/esm3/esmwriter.hpp>
|
||||||
#include <components/esm3/loadcont.hpp>
|
#include <components/esm3/loadcont.hpp>
|
||||||
|
@ -410,6 +411,85 @@ namespace ESM
|
||||||
EXPECT_EQ(result.mStringId, record.mStringId);
|
EXPECT_EQ(result.mStringId, record.mStringId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(Esm3SaveLoadRecordTest, aiSequenceAiWanderShouldNotChange)
|
||||||
|
{
|
||||||
|
AiSequence::AiWander record;
|
||||||
|
record.mData.mDistance = 1;
|
||||||
|
record.mData.mDuration = 2;
|
||||||
|
record.mData.mTimeOfDay = 3;
|
||||||
|
constexpr std::uint8_t idle[8] = { 4, 5, 6, 7, 8, 9, 10, 11 };
|
||||||
|
static_assert(std::size(idle) == std::size(record.mData.mIdle));
|
||||||
|
std::copy(std::begin(idle), std::end(idle), record.mData.mIdle);
|
||||||
|
record.mData.mShouldRepeat = 12;
|
||||||
|
record.mDurationData.mRemainingDuration = 13;
|
||||||
|
record.mStoredInitialActorPosition = true;
|
||||||
|
constexpr float initialActorPosition[3] = { 15, 16, 17 };
|
||||||
|
static_assert(std::size(initialActorPosition) == std::size(record.mInitialActorPosition.mValues));
|
||||||
|
std::copy(
|
||||||
|
std::begin(initialActorPosition), std::end(initialActorPosition), record.mInitialActorPosition.mValues);
|
||||||
|
|
||||||
|
AiSequence::AiWander result;
|
||||||
|
saveAndLoadRecord(record, GetParam(), result);
|
||||||
|
|
||||||
|
EXPECT_EQ(result.mData.mDistance, record.mData.mDistance);
|
||||||
|
EXPECT_EQ(result.mData.mDuration, record.mData.mDuration);
|
||||||
|
EXPECT_EQ(result.mData.mTimeOfDay, record.mData.mTimeOfDay);
|
||||||
|
EXPECT_THAT(result.mData.mIdle, ElementsAreArray(record.mData.mIdle));
|
||||||
|
EXPECT_EQ(result.mData.mShouldRepeat, record.mData.mShouldRepeat);
|
||||||
|
EXPECT_EQ(result.mDurationData.mRemainingDuration, record.mDurationData.mRemainingDuration);
|
||||||
|
EXPECT_EQ(result.mStoredInitialActorPosition, record.mStoredInitialActorPosition);
|
||||||
|
EXPECT_THAT(result.mInitialActorPosition.mValues, ElementsAreArray(record.mInitialActorPosition.mValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(Esm3SaveLoadRecordTest, aiSequenceAiTravelShouldNotChange)
|
||||||
|
{
|
||||||
|
AiSequence::AiTravel record;
|
||||||
|
record.mData.mX = 1;
|
||||||
|
record.mData.mY = 2;
|
||||||
|
record.mData.mZ = 3;
|
||||||
|
record.mHidden = true;
|
||||||
|
record.mRepeat = true;
|
||||||
|
|
||||||
|
AiSequence::AiTravel result;
|
||||||
|
saveAndLoadRecord(record, GetParam(), result);
|
||||||
|
|
||||||
|
EXPECT_EQ(result.mData.mX, record.mData.mX);
|
||||||
|
EXPECT_EQ(result.mData.mY, record.mData.mY);
|
||||||
|
EXPECT_EQ(result.mData.mZ, record.mData.mZ);
|
||||||
|
EXPECT_EQ(result.mHidden, record.mHidden);
|
||||||
|
EXPECT_EQ(result.mRepeat, record.mRepeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(Esm3SaveLoadRecordTest, aiSequenceAiEscortShouldNotChange)
|
||||||
|
{
|
||||||
|
AiSequence::AiEscort record;
|
||||||
|
record.mData.mX = 1;
|
||||||
|
record.mData.mY = 2;
|
||||||
|
record.mData.mZ = 3;
|
||||||
|
record.mData.mDuration = 4;
|
||||||
|
record.mTargetActorId = 5;
|
||||||
|
record.mTargetId = generateRandomRefId(32);
|
||||||
|
record.mCellId = generateRandomString(257);
|
||||||
|
record.mRemainingDuration = 6;
|
||||||
|
record.mRepeat = true;
|
||||||
|
|
||||||
|
AiSequence::AiEscort result;
|
||||||
|
saveAndLoadRecord(record, GetParam(), result);
|
||||||
|
|
||||||
|
EXPECT_EQ(result.mData.mX, record.mData.mX);
|
||||||
|
EXPECT_EQ(result.mData.mY, record.mData.mY);
|
||||||
|
EXPECT_EQ(result.mData.mZ, record.mData.mZ);
|
||||||
|
if (GetParam() <= MaxOldAiPackageFormatVersion)
|
||||||
|
EXPECT_EQ(result.mData.mDuration, record.mRemainingDuration);
|
||||||
|
else
|
||||||
|
EXPECT_EQ(result.mData.mDuration, record.mData.mDuration);
|
||||||
|
EXPECT_EQ(result.mTargetActorId, record.mTargetActorId);
|
||||||
|
EXPECT_EQ(result.mTargetId, record.mTargetId);
|
||||||
|
EXPECT_EQ(result.mCellId, record.mCellId);
|
||||||
|
EXPECT_EQ(result.mRemainingDuration, record.mRemainingDuration);
|
||||||
|
EXPECT_EQ(result.mRepeat, record.mRepeat);
|
||||||
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(FormatVersions, Esm3SaveLoadRecordTest, ValuesIn(getFormats()));
|
INSTANTIATE_TEST_SUITE_P(FormatVersions, Esm3SaveLoadRecordTest, ValuesIn(getFormats()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <components/misc/strings/conversion.hpp>
|
#include <components/misc/strings/conversion.hpp>
|
||||||
#include <components/vfs/archive.hpp>
|
#include <components/vfs/archive.hpp>
|
||||||
|
#include <components/vfs/file.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
#include <components/vfs/pathutil.hpp>
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ namespace TestingOpenMW
|
||||||
void listResources(VFS::FileMap& out) override
|
void listResources(VFS::FileMap& out) override
|
||||||
{
|
{
|
||||||
for (const auto& [key, value] : mFiles)
|
for (const auto& [key, value] : mFiles)
|
||||||
out.emplace(VFS::Path::normalizeFilename(key), value);
|
out.emplace(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains(std::string_view file) const override { return mFiles.contains(file); }
|
bool contains(std::string_view file) const override { return mFiles.contains(file); }
|
||||||
|
|
|
@ -810,8 +810,7 @@ bool Wizard::UnshieldWorker::extractFile(
|
||||||
if (!dir.mkpath(path))
|
if (!dir.mkpath(path))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
QString fileName(path);
|
path.append(QString::fromUtf8(unshield_file_name(unshield, index)));
|
||||||
fileName.append(QString::fromUtf8(unshield_file_name(unshield, index)));
|
|
||||||
|
|
||||||
// Calculate the percentage done
|
// Calculate the percentage done
|
||||||
int progress = (((float)counter / (float)unshield_file_count(unshield)) * 100);
|
int progress = (((float)counter / (float)unshield_file_count(unshield)) * 100);
|
||||||
|
@ -825,13 +824,13 @@ bool Wizard::UnshieldWorker::extractFile(
|
||||||
emit textChanged(tr("Extracting: %1").arg(QString::fromUtf8(unshield_file_name(unshield, index))));
|
emit textChanged(tr("Extracting: %1").arg(QString::fromUtf8(unshield_file_name(unshield, index))));
|
||||||
emit progressChanged(progress);
|
emit progressChanged(progress);
|
||||||
|
|
||||||
QByteArray array(fileName.toUtf8());
|
QByteArray array(path.toUtf8());
|
||||||
success = unshield_file_save(unshield, index, array.constData());
|
success = unshield_file_save(unshield, index, array.constData());
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
qDebug() << "error";
|
qDebug() << "error";
|
||||||
dir.remove(fileName);
|
dir.remove(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
|
|
@ -76,7 +76,7 @@ namespace Bsa
|
||||||
fail("Corrupted BSA");
|
fail("Corrupted BSA");
|
||||||
}
|
}
|
||||||
|
|
||||||
mFolders[dirHash][{ nameHash, extHash }] = file;
|
mFolders[dirHash][{ nameHash, extHash }] = std::move(file);
|
||||||
|
|
||||||
FileStruct fileStruct{};
|
FileStruct fileStruct{};
|
||||||
mFiles.push_back(fileStruct);
|
mFiles.push_back(fileStruct);
|
||||||
|
@ -177,7 +177,7 @@ namespace Bsa
|
||||||
return std::nullopt; // folder not found
|
return std::nullopt; // folder not found
|
||||||
|
|
||||||
uint32_t fileHash = generateHash(fileName);
|
uint32_t fileHash = generateHash(fileName);
|
||||||
uint32_t extHash = *reinterpret_cast<const uint32_t*>(ext.data() + 1);
|
uint32_t extHash = generateExtensionHash(ext);
|
||||||
auto iter = it->second.find({ fileHash, extHash });
|
auto iter = it->second.find({ fileHash, extHash });
|
||||||
if (iter == it->second.end())
|
if (iter == it->second.end())
|
||||||
return std::nullopt; // file not found
|
return std::nullopt; // file not found
|
||||||
|
|
|
@ -46,4 +46,12 @@ namespace Bsa
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t generateExtensionHash(std::string_view extension)
|
||||||
|
{
|
||||||
|
uint32_t result = 0;
|
||||||
|
for (size_t i = 0; i < 4 && i < extension.size() - 1; i++)
|
||||||
|
result |= static_cast<uint8_t>(extension[i + 1]) << (8 * i);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Bsa
|
} // namespace Bsa
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
namespace Bsa
|
namespace Bsa
|
||||||
{
|
{
|
||||||
uint32_t generateHash(const std::string& name);
|
uint32_t generateHash(const std::string& name);
|
||||||
|
uint32_t generateExtensionHash(std::string_view extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -172,7 +172,7 @@ namespace Bsa
|
||||||
return FileRecord(); // folder not found, return default which has offset of sInvalidOffset
|
return FileRecord(); // folder not found, return default which has offset of sInvalidOffset
|
||||||
|
|
||||||
uint32_t fileHash = generateHash(fileName);
|
uint32_t fileHash = generateHash(fileName);
|
||||||
uint32_t extHash = *reinterpret_cast<const uint32_t*>(ext.data() + 1);
|
uint32_t extHash = generateExtensionHash(ext);
|
||||||
auto iter = it->second.find({ fileHash, extHash });
|
auto iter = it->second.find({ fileHash, extHash });
|
||||||
if (iter == it->second.end())
|
if (iter == it->second.end())
|
||||||
return FileRecord(); // file not found, return default which has offset of sInvalidOffset
|
return FileRecord(); // file not found, return default which has offset of sInvalidOffset
|
||||||
|
|
|
@ -371,7 +371,7 @@ bool Config::GameSettings::writeFileWithComments(QFile& file)
|
||||||
{
|
{
|
||||||
if ((keyMatch.captured(1) + "=" + keyMatch.captured(2)) == keyVal)
|
if ((keyMatch.captured(1) + "=" + keyMatch.captured(2)) == keyVal)
|
||||||
{
|
{
|
||||||
*iter = settingLine;
|
*iter = std::move(settingLine);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -601,7 +601,7 @@ namespace DetourNavigator
|
||||||
if (mSettings.get().mEnableRecastMeshFileNameRevision)
|
if (mSettings.get().mEnableRecastMeshFileNameRevision)
|
||||||
recastMeshRevision = revision;
|
recastMeshRevision = revision;
|
||||||
if (mSettings.get().mEnableNavMeshFileNameRevision)
|
if (mSettings.get().mEnableNavMeshFileNameRevision)
|
||||||
navMeshRevision = revision;
|
navMeshRevision = std::move(revision);
|
||||||
}
|
}
|
||||||
if (recastMesh && mSettings.get().mEnableWriteRecastMeshToFile)
|
if (recastMesh && mSettings.get().mEnableWriteRecastMeshToFile)
|
||||||
writeToFile(*recastMesh,
|
writeToFile(*recastMesh,
|
||||||
|
|
|
@ -76,4 +76,13 @@ namespace DetourNavigator
|
||||||
return {};
|
return {};
|
||||||
return TilesPositionsRange{ TilePosition(beginX, beginY), TilePosition(endX, endY) };
|
return TilesPositionsRange{ TilePosition(beginX, beginY), TilePosition(endX, endY) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TilesPositionsRange getUnion(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept
|
||||||
|
{
|
||||||
|
const int beginX = std::min(a.mBegin.x(), b.mBegin.x());
|
||||||
|
const int endX = std::max(a.mEnd.x(), b.mEnd.x());
|
||||||
|
const int beginY = std::min(a.mBegin.y(), b.mBegin.y());
|
||||||
|
const int endY = std::max(a.mEnd.y(), b.mEnd.y());
|
||||||
|
return TilesPositionsRange{ .mBegin = TilePosition(beginX, beginY), .mEnd = TilePosition(endX, endY) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,8 @@ namespace DetourNavigator
|
||||||
}
|
}
|
||||||
|
|
||||||
TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept;
|
TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept;
|
||||||
|
|
||||||
|
TilesPositionsRange getUnion(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -166,6 +166,7 @@ namespace DetourNavigator
|
||||||
return;
|
return;
|
||||||
mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision();
|
mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision();
|
||||||
mPlayerTile = playerTile;
|
mPlayerTile = playerTile;
|
||||||
|
mRecastMeshManager.setRange(makeRange(playerTile, mSettings.mMaxTilesNumber), guard);
|
||||||
const auto changedTiles = mRecastMeshManager.takeChangedTiles(guard);
|
const auto changedTiles = mRecastMeshManager.takeChangedTiles(guard);
|
||||||
const TilesPositionsRange range = mRecastMeshManager.getLimitedObjectsRange();
|
const TilesPositionsRange range = mRecastMeshManager.getLimitedObjectsRange();
|
||||||
for (const auto& [agentBounds, cached] : mCache)
|
for (const auto& [agentBounds, cached] : mCache)
|
||||||
|
|
|
@ -54,6 +54,15 @@ namespace DetourNavigator
|
||||||
private:
|
private:
|
||||||
const std::optional<std::unique_lock<Mutex>> mImpl;
|
const std::optional<std::unique_lock<Mutex>> mImpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TilesPositionsRange getIndexRange(const auto& index)
|
||||||
|
{
|
||||||
|
const auto bounds = index.bounds();
|
||||||
|
return TilesPositionsRange{
|
||||||
|
.mBegin = makeTilePosition(bounds.min_corner()),
|
||||||
|
.mEnd = makeTilePosition(bounds.max_corner()) + TilePosition(1, 1),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings)
|
TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings)
|
||||||
|
@ -104,14 +113,28 @@ namespace DetourNavigator
|
||||||
|
|
||||||
TilesPositionsRange TileCachedRecastMeshManager::getLimitedObjectsRange() const
|
TilesPositionsRange TileCachedRecastMeshManager::getLimitedObjectsRange() const
|
||||||
{
|
{
|
||||||
if (mObjects.empty())
|
std::optional<TilesPositionsRange> result;
|
||||||
return {};
|
if (!mWater.empty())
|
||||||
const auto bounds = mObjectIndex.bounds();
|
result = getIndexRange(mWaterIndex);
|
||||||
const TilesPositionsRange objectsRange{
|
if (!mHeightfields.empty())
|
||||||
.mBegin = makeTilePosition(bounds.min_corner()),
|
{
|
||||||
.mEnd = makeTilePosition(bounds.max_corner()) + TilePosition(1, 1),
|
const TilesPositionsRange range = getIndexRange(mHeightfieldIndex);
|
||||||
};
|
if (result.has_value())
|
||||||
return getIntersection(mRange, objectsRange);
|
result = getUnion(*result, range);
|
||||||
|
else
|
||||||
|
result = range;
|
||||||
|
}
|
||||||
|
if (!mObjects.empty())
|
||||||
|
{
|
||||||
|
const TilesPositionsRange range = getIndexRange(mObjectIndex);
|
||||||
|
if (result.has_value())
|
||||||
|
result = getUnion(*result, range);
|
||||||
|
else
|
||||||
|
result = range;
|
||||||
|
}
|
||||||
|
if (result.has_value())
|
||||||
|
return getIntersection(mRange, *result);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace, const UpdateGuard* guard)
|
void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace, const UpdateGuard* guard)
|
||||||
|
|
10
components/esm/decompose.hpp
Normal file
10
components/esm/decompose.hpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_ESM_DECOMPOSE_H
|
||||||
|
#define OPENMW_COMPONENTS_ESM_DECOMPOSE_H
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
template <class T>
|
||||||
|
void decompose(T&& value, const auto& apply) = delete;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,32 +3,58 @@
|
||||||
#include "esmreader.hpp"
|
#include "esmreader.hpp"
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/concepts.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
template <Misc::SameAsWithoutCvref<AiSequence::AiWanderData> T>
|
||||||
|
void decompose(T&& v, const auto& f)
|
||||||
|
{
|
||||||
|
f(v.mDistance, v.mDuration, v.mTimeOfDay, v.mIdle, v.mShouldRepeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Misc::SameAsWithoutCvref<AiSequence::AiWanderDuration> T>
|
||||||
|
void decompose(T&& v, const auto& f)
|
||||||
|
{
|
||||||
|
std::uint32_t unused = 0;
|
||||||
|
f(v.mRemainingDuration, unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Misc::SameAsWithoutCvref<AiSequence::AiTravelData> T>
|
||||||
|
void decompose(T&& v, const auto& f)
|
||||||
|
{
|
||||||
|
f(v.mX, v.mY, v.mZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Misc::SameAsWithoutCvref<AiSequence::AiEscortData> T>
|
||||||
|
void decompose(T&& v, const auto& f)
|
||||||
|
{
|
||||||
|
f(v.mX, v.mY, v.mZ, v.mDuration);
|
||||||
|
}
|
||||||
|
|
||||||
namespace AiSequence
|
namespace AiSequence
|
||||||
{
|
{
|
||||||
|
|
||||||
void AiWander::load(ESMReader& esm)
|
void AiWander::load(ESMReader& esm)
|
||||||
{
|
{
|
||||||
esm.getHNT("DATA", mData.mDistance, mData.mDuration, mData.mTimeOfDay, mData.mIdle, mData.mShouldRepeat);
|
esm.getNamedComposite("DATA", mData);
|
||||||
esm.getHNT("STAR", mDurationData.mRemainingDuration, mDurationData.unused); // was mStartTime
|
esm.getNamedComposite("STAR", mDurationData); // was mStartTime
|
||||||
mStoredInitialActorPosition = esm.getHNOT("POS_", mInitialActorPosition.mValues);
|
mStoredInitialActorPosition = esm.getHNOT("POS_", mInitialActorPosition.mValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::save(ESMWriter& esm) const
|
void AiWander::save(ESMWriter& esm) const
|
||||||
{
|
{
|
||||||
esm.writeHNT("DATA", mData);
|
esm.writeNamedComposite("DATA", mData);
|
||||||
esm.writeHNT("STAR", mDurationData);
|
esm.writeNamedComposite("STAR", mDurationData); // was mStartTime
|
||||||
if (mStoredInitialActorPosition)
|
if (mStoredInitialActorPosition)
|
||||||
esm.writeHNT("POS_", mInitialActorPosition);
|
esm.writeHNT("POS_", mInitialActorPosition.mValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiTravel::load(ESMReader& esm)
|
void AiTravel::load(ESMReader& esm)
|
||||||
{
|
{
|
||||||
esm.getHNT("DATA", mData.mX, mData.mY, mData.mZ);
|
esm.getNamedComposite("DATA", mData);
|
||||||
esm.getHNT(mHidden, "HIDD");
|
esm.getHNT(mHidden, "HIDD");
|
||||||
mRepeat = false;
|
mRepeat = false;
|
||||||
esm.getHNOT(mRepeat, "REPT");
|
esm.getHNOT(mRepeat, "REPT");
|
||||||
|
@ -36,7 +62,7 @@ namespace ESM
|
||||||
|
|
||||||
void AiTravel::save(ESMWriter& esm) const
|
void AiTravel::save(ESMWriter& esm) const
|
||||||
{
|
{
|
||||||
esm.writeHNT("DATA", mData);
|
esm.writeNamedComposite("DATA", mData);
|
||||||
esm.writeHNT("HIDD", mHidden);
|
esm.writeHNT("HIDD", mHidden);
|
||||||
if (mRepeat)
|
if (mRepeat)
|
||||||
esm.writeHNT("REPT", mRepeat);
|
esm.writeHNT("REPT", mRepeat);
|
||||||
|
@ -44,7 +70,7 @@ namespace ESM
|
||||||
|
|
||||||
void AiEscort::load(ESMReader& esm)
|
void AiEscort::load(ESMReader& esm)
|
||||||
{
|
{
|
||||||
esm.getHNT("DATA", mData.mX, mData.mY, mData.mZ, mData.mDuration);
|
esm.getNamedComposite("DATA", mData);
|
||||||
mTargetId = esm.getHNRefId("TARG");
|
mTargetId = esm.getHNRefId("TARG");
|
||||||
mTargetActorId = -1;
|
mTargetActorId = -1;
|
||||||
esm.getHNOT(mTargetActorId, "TAID");
|
esm.getHNOT(mTargetActorId, "TAID");
|
||||||
|
@ -64,7 +90,7 @@ namespace ESM
|
||||||
|
|
||||||
void AiEscort::save(ESMWriter& esm) const
|
void AiEscort::save(ESMWriter& esm) const
|
||||||
{
|
{
|
||||||
esm.writeHNT("DATA", mData);
|
esm.writeNamedComposite("DATA", mData);
|
||||||
esm.writeHNRefId("TARG", mTargetId);
|
esm.writeHNRefId("TARG", mTargetId);
|
||||||
esm.writeHNT("TAID", mTargetActorId);
|
esm.writeHNT("TAID", mTargetActorId);
|
||||||
esm.writeHNT("DURA", mRemainingDuration);
|
esm.writeHNT("DURA", mRemainingDuration);
|
||||||
|
@ -76,7 +102,7 @@ namespace ESM
|
||||||
|
|
||||||
void AiFollow::load(ESMReader& esm)
|
void AiFollow::load(ESMReader& esm)
|
||||||
{
|
{
|
||||||
esm.getHNT("DATA", mData.mX, mData.mY, mData.mZ, mData.mDuration);
|
esm.getNamedComposite("DATA", mData);
|
||||||
mTargetId = esm.getHNRefId("TARG");
|
mTargetId = esm.getHNRefId("TARG");
|
||||||
mTargetActorId = -1;
|
mTargetActorId = -1;
|
||||||
esm.getHNOT(mTargetActorId, "TAID");
|
esm.getHNOT(mTargetActorId, "TAID");
|
||||||
|
@ -101,7 +127,7 @@ namespace ESM
|
||||||
|
|
||||||
void AiFollow::save(ESMWriter& esm) const
|
void AiFollow::save(ESMWriter& esm) const
|
||||||
{
|
{
|
||||||
esm.writeHNT("DATA", mData);
|
esm.writeNamedComposite("DATA", mData);
|
||||||
esm.writeHNRefId("TARG", mTargetId);
|
esm.writeHNRefId("TARG", mTargetId);
|
||||||
esm.writeHNT("TAID", mTargetActorId);
|
esm.writeHNT("TAID", mTargetActorId);
|
||||||
esm.writeHNT("DURA", mRemainingDuration);
|
esm.writeHNT("DURA", mRemainingDuration);
|
||||||
|
|
|
@ -36,32 +36,31 @@ namespace ESM
|
||||||
virtual ~AiPackage() {}
|
virtual ~AiPackage() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
struct AiWanderData
|
struct AiWanderData
|
||||||
{
|
{
|
||||||
int16_t mDistance;
|
int16_t mDistance;
|
||||||
int16_t mDuration;
|
int16_t mDuration;
|
||||||
unsigned char mTimeOfDay;
|
std::uint8_t mTimeOfDay;
|
||||||
unsigned char mIdle[8];
|
std::uint8_t mIdle[8];
|
||||||
unsigned char mShouldRepeat;
|
std::uint8_t mShouldRepeat;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AiWanderDuration
|
struct AiWanderDuration
|
||||||
{
|
{
|
||||||
float mRemainingDuration;
|
float mRemainingDuration;
|
||||||
int32_t unused;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AiTravelData
|
struct AiTravelData
|
||||||
{
|
{
|
||||||
float mX, mY, mZ;
|
float mX, mY, mZ;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AiEscortData
|
struct AiEscortData
|
||||||
{
|
{
|
||||||
float mX, mY, mZ;
|
float mX, mY, mZ;
|
||||||
int16_t mDuration;
|
int16_t mDuration;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
struct AiWander : AiPackage
|
struct AiWander : AiPackage
|
||||||
{
|
{
|
||||||
AiWanderData mData;
|
AiWanderData mData;
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
|
|
||||||
#include <components/to_utf8/to_utf8.hpp>
|
#include <components/to_utf8/to_utf8.hpp>
|
||||||
|
|
||||||
|
#include "components/esm/decompose.hpp"
|
||||||
#include "components/esm/esmcommon.hpp"
|
#include "components/esm/esmcommon.hpp"
|
||||||
#include "components/esm/refid.hpp"
|
#include "components/esm/refid.hpp"
|
||||||
|
|
||||||
#include "loadtes3.hpp"
|
#include "loadtes3.hpp"
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
|
@ -177,6 +179,16 @@ namespace ESM
|
||||||
(getT(args), ...);
|
(getT(args), ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void getNamedComposite(NAME name, auto& value)
|
||||||
|
{
|
||||||
|
decompose(value, [&](auto&... args) { getHNT(name, args...); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void getComposite(auto& value)
|
||||||
|
{
|
||||||
|
decompose(value, [&](auto&... args) { (getT(args), ...); });
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<IsReadable<T>>>
|
template <typename T, typename = std::enable_if_t<IsReadable<T>>>
|
||||||
void skipHT()
|
void skipHT()
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "components/esm/decompose.hpp"
|
||||||
#include "components/esm/esmcommon.hpp"
|
#include "components/esm/esmcommon.hpp"
|
||||||
#include "components/esm/refid.hpp"
|
#include "components/esm/refid.hpp"
|
||||||
|
|
||||||
|
@ -121,6 +122,20 @@ namespace ESM
|
||||||
endRecord(name);
|
endRecord(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void writeNamedComposite(NAME name, const auto& value)
|
||||||
|
{
|
||||||
|
decompose(value, [&](const auto&... args) {
|
||||||
|
startSubRecord(name);
|
||||||
|
(writeT(args), ...);
|
||||||
|
endRecord(name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeComposite(const auto& value)
|
||||||
|
{
|
||||||
|
decompose(value, [&](const auto&... args) { (writeT(args), ...); });
|
||||||
|
}
|
||||||
|
|
||||||
// Prevent using writeHNT with strings. This already happened by accident and results in
|
// Prevent using writeHNT with strings. This already happened by accident and results in
|
||||||
// state being discarded without any error on writing or reading it. :(
|
// state being discarded without any error on writing or reading it. :(
|
||||||
// writeHNString and friends must be used instead.
|
// writeHNString and friends must be used instead.
|
||||||
|
@ -132,7 +147,7 @@ namespace ESM
|
||||||
void writeHNT(NAME name, const T (&data)[size], int) = delete;
|
void writeHNT(NAME name, const T (&data)[size], int) = delete;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void writeHNT(NAME name, const T& data, int size)
|
void writeHNT(NAME name, const T& data, std::size_t size)
|
||||||
{
|
{
|
||||||
startSubRecord(name);
|
startSubRecord(name);
|
||||||
writeT(data, size);
|
writeT(data, size);
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace ESM
|
||||||
esm.getHNT(multiplier, "MULT");
|
esm.getHNT(multiplier, "MULT");
|
||||||
params.emplace_back(rand, multiplier);
|
params.emplace_back(rand, multiplier);
|
||||||
}
|
}
|
||||||
mPermanentMagicEffectMagnitudes[id] = params;
|
mPermanentMagicEffectMagnitudes[id] = std::move(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (esm.isNextSub("EQUI"))
|
while (esm.isNextSub("EQUI"))
|
||||||
|
|
|
@ -4,12 +4,19 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/misc/concepts.hpp>
|
||||||
|
|
||||||
#include "esmreader.hpp"
|
#include "esmreader.hpp"
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
template <Misc::SameAsWithoutCvref<Script::SCHDstruct> T>
|
||||||
|
void decompose(T&& v, const auto& f)
|
||||||
|
{
|
||||||
|
f(v.mNumShorts, v.mNumLongs, v.mNumFloats, v.mScriptDataSize, v.mStringTableSize);
|
||||||
|
}
|
||||||
|
|
||||||
void Script::loadSCVR(ESMReader& esm)
|
void Script::loadSCVR(ESMReader& esm)
|
||||||
{
|
{
|
||||||
uint32_t s = mData.mStringTableSize;
|
uint32_t s = mData.mStringTableSize;
|
||||||
|
@ -99,11 +106,7 @@ namespace ESM
|
||||||
{
|
{
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
mId = esm.getMaybeFixedRefIdSize(32);
|
mId = esm.getMaybeFixedRefIdSize(32);
|
||||||
esm.getT(mData.mNumShorts);
|
esm.getComposite(mData);
|
||||||
esm.getT(mData.mNumLongs);
|
|
||||||
esm.getT(mData.mNumFloats);
|
|
||||||
esm.getT(mData.mScriptDataSize);
|
|
||||||
esm.getT(mData.mStringTableSize);
|
|
||||||
|
|
||||||
hasHeader = true;
|
hasHeader = true;
|
||||||
break;
|
break;
|
||||||
|
@ -157,7 +160,7 @@ namespace ESM
|
||||||
|
|
||||||
esm.startSubRecord("SCHD");
|
esm.startSubRecord("SCHD");
|
||||||
esm.writeMaybeFixedSizeRefId(mId, 32);
|
esm.writeMaybeFixedSizeRefId(mId, 32);
|
||||||
esm.writeT(mData, 20);
|
esm.writeComposite(mData);
|
||||||
esm.endRecord("SCHD");
|
esm.endRecord("SCHD");
|
||||||
|
|
||||||
if (isDeleted)
|
if (isDeleted)
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "esmreader.hpp"
|
#include "esmreader.hpp"
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
|
#include "../misc/algorithm.hpp"
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
void SavedGame::load(ESMReader& esm)
|
void SavedGame::load(ESMReader& esm)
|
||||||
|
@ -67,7 +69,9 @@ namespace ESM
|
||||||
std::vector<std::string_view> missingFiles;
|
std::vector<std::string_view> missingFiles;
|
||||||
for (const std::string& contentFile : mContentFiles)
|
for (const std::string& contentFile : mContentFiles)
|
||||||
{
|
{
|
||||||
if (std::find(allContentFiles.begin(), allContentFiles.end(), contentFile) == allContentFiles.end())
|
auto it = std::find_if(allContentFiles.begin(), allContentFiles.end(),
|
||||||
|
[&](const std::string& file) { return Misc::StringUtils::ciEqual(file, contentFile); });
|
||||||
|
if (it == allContentFiles.end())
|
||||||
{
|
{
|
||||||
missingFiles.emplace_back(contentFile);
|
missingFiles.emplace_back(contentFile);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue