mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-30 22:45:36 +00:00
Merge branch 'master' into menuscripts
This commit is contained in:
commit
9cce2e39ba
215 changed files with 4307 additions and 2011 deletions
|
@ -245,6 +245,7 @@ Programmers
|
||||||
xyzz
|
xyzz
|
||||||
Yohaulticetl
|
Yohaulticetl
|
||||||
Yuri Krupenin
|
Yuri Krupenin
|
||||||
|
Yury Stepovikov
|
||||||
zelurker
|
zelurker
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
|
|
|
@ -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
|
||||||
|
@ -32,6 +34,7 @@
|
||||||
Bug #6190: Unintuitive sun specularity time of day dependence
|
Bug #6190: Unintuitive sun specularity time of day dependence
|
||||||
Bug #6222: global map cell size can crash openmw if set to too high a value
|
Bug #6222: global map cell size can crash openmw if set to too high a value
|
||||||
Bug #6313: Followers with high Fight can turn hostile
|
Bug #6313: Followers with high Fight can turn hostile
|
||||||
|
Bug #6402: The sound of a thunderstorm does not stop playing after entering the premises
|
||||||
Bug #6427: Enemy health bar disappears before damaging effect ends
|
Bug #6427: Enemy health bar disappears before damaging effect ends
|
||||||
Bug #6550: Cloned body parts don't inherit texture effects
|
Bug #6550: Cloned body parts don't inherit texture effects
|
||||||
Bug #6645: Enemy block sounds align with animation instead of blocked hits
|
Bug #6645: Enemy block sounds align with animation instead of blocked hits
|
||||||
|
@ -127,6 +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 #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;
|
||||||
|
|
|
@ -41,11 +41,6 @@ int runLauncher(int argc, char* argv[])
|
||||||
appTranslator.load(":/translations/" + locale + ".qm");
|
appTranslator.load(":/translations/" + locale + ".qm");
|
||||||
app.installTranslator(&appTranslator);
|
app.installTranslator(&appTranslator);
|
||||||
|
|
||||||
// Now we make sure the current dir is set to application path
|
|
||||||
QDir dir(QCoreApplication::applicationDirPath());
|
|
||||||
|
|
||||||
QDir::setCurrent(dir.absolutePath());
|
|
||||||
|
|
||||||
Launcher::MainDialog mainWin(configurationManager);
|
Launcher::MainDialog mainWin(configurationManager);
|
||||||
|
|
||||||
Launcher::FirstRunDialogResult result = mainWin.showFirstRunDialog();
|
Launcher::FirstRunDialogResult result = mainWin.showFirstRunDialog();
|
||||||
|
|
|
@ -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,85 +11,22 @@
|
||||||
</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>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="DisplayWrapper">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Display</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
||||||
<item row="5" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="screenLabel">
|
<widget class="QLabel" name="screenLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Screen:</string>
|
<string>Screen</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</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">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="windowModeLabel">
|
<widget class="QLabel" name="windowModeLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Window Mode:</string>
|
<string>Window mode</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="0">
|
|
||||||
<widget class="QLabel" name="resolutionLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Resolution:</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="vSyncLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Vertical Sync:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="antiAliasingLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Anti-aliasing:</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -142,35 +79,52 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QComboBox" name="windowModeComboBox">
|
<widget class="QComboBox" name="antiAliasingComboBox">
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Fullscreen</string>
|
<string>0</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Windowed Fullscreen</string>
|
<string>2</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Windowed</string>
|
<string>4</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>8</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>16</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="framerateLimitCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Framerate limit</string>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QCheckBox" name="windowBorderCheckBox">
|
<widget class="QCheckBox" name="windowBorderCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Window Border</string>
|
<string>Window border</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QComboBox" name="screenComboBox"/>
|
||||||
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QComboBox" name="vSyncComboBox">
|
<widget class="QComboBox" name="vSyncComboBox">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
|
@ -193,10 +147,35 @@
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="3" column="1">
|
||||||
<widget class="QCheckBox" name="framerateLimitCheckBox">
|
<widget class="QComboBox" name="windowModeComboBox">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Framerate Limit:</string>
|
<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="6" column="0">
|
||||||
|
<widget class="QLabel" name="resolutionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Resolution</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -225,46 +204,21 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
<item row="4" column="0">
|
||||||
</item>
|
<widget class="QLabel" name="antiAliasingLabel">
|
||||||
</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>Anti-aliasing</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="2" column="0">
|
||||||
<widget class="QComboBox" name="lightingMethodComboBox">
|
<widget class="QLabel" name="vSyncLabel">
|
||||||
<item>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>legacy</string>
|
<string>Vertical synchronization</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>shaders compatibility</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>shaders</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
<item row="7" column="0">
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
|
@ -278,192 +232,8 @@
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</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 name="text">
|
|
||||||
<string>Enable Player Shadows</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 name="text">
|
|
||||||
<string>Enable Actor Shadows</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 name="text">
|
|
||||||
<string>Enable Object Shadows</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QCheckBox" name="terrainShadowsCheckBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<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>
|
|
||||||
<property name="text">
|
|
||||||
<string>Enable Terrain Shadows</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QCheckBox" name="indoorShadowsCheckBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<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>
|
|
||||||
<property name="text">
|
|
||||||
<string>Enable Indoor Shadows</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<widget class="QLabel" name="shadowComputeSceneBoundsLabel">
|
|
||||||
<property name="toolTip">
|
|
||||||
<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>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Shadow Near Far Computation Method:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="1">
|
|
||||||
<widget class="QComboBox" name="shadowComputeSceneBoundsComboBox">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>bounds</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>primitives</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>none</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="0">
|
|
||||||
<widget class="QLabel" name="shadowResolutionLabel">
|
|
||||||
<property name="toolTip">
|
|
||||||
<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>
|
|
||||||
<property name="text">
|
|
||||||
<string>Shadow Map Resolution:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="1">
|
|
||||||
<widget class="QComboBox" name="shadowResolutionComboBox">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>512</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<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>
|
</item>
|
||||||
</layout>
|
</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,18 +129,24 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ bool hasExtension(const std::filesystem::path& filename, const std::string& exte
|
||||||
/// See if the file has the "nif" extension.
|
/// See if the file has the "nif" extension.
|
||||||
bool isNIF(const std::filesystem::path& filename)
|
bool isNIF(const std::filesystem::path& filename)
|
||||||
{
|
{
|
||||||
return hasExtension(filename, ".nif");
|
return hasExtension(filename, ".nif") || hasExtension(filename, ".kf");
|
||||||
}
|
}
|
||||||
/// See if the file has the "bsa" extension.
|
/// See if the file has the "bsa" extension.
|
||||||
bool isBSA(const std::filesystem::path& filename)
|
bool isBSA(const std::filesystem::path& filename)
|
||||||
|
@ -75,6 +76,9 @@ void readNIF(
|
||||||
const std::string pathStr = Files::pathToUnicodeString(path);
|
const std::string pathStr = Files::pathToUnicodeString(path);
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
{
|
{
|
||||||
|
if (hasExtension(path, ".kf"))
|
||||||
|
std::cout << "Reading KF file '" << pathStr << "'";
|
||||||
|
else
|
||||||
std::cout << "Reading NIF file '" << pathStr << "'";
|
std::cout << "Reading NIF file '" << pathStr << "'";
|
||||||
if (!source.empty())
|
if (!source.empty())
|
||||||
std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'";
|
std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'";
|
||||||
|
@ -84,7 +88,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
|
||||||
|
@ -138,10 +142,10 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
|
||||||
bool parseOptions(int argc, char** argv, Files::PathContainer& files, Files::PathContainer& archives,
|
bool parseOptions(int argc, char** argv, Files::PathContainer& files, Files::PathContainer& archives,
|
||||||
bool& writeDebugLog, bool& quiet)
|
bool& writeDebugLog, bool& quiet)
|
||||||
{
|
{
|
||||||
bpo::options_description desc(R"(Ensure that OpenMW can use the provided NIF and BSA files
|
bpo::options_description desc(R"(Ensure that OpenMW can use the provided NIF, KF and BSA/BA2 files
|
||||||
|
|
||||||
Usages:
|
Usages:
|
||||||
niftest <nif files, BSA files, or directories>
|
niftest <nif files, kf files, BSA/BA2 files, or directories>
|
||||||
Scan the file or directories for NIF errors.
|
Scan the file or directories for NIF errors.
|
||||||
|
|
||||||
Allowed options)");
|
Allowed options)");
|
||||||
|
@ -240,7 +244,8 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cerr << "Error: '" << pathStr << "' is not a NIF file, BSA/BA2 archive, or directory" << std::endl;
|
std::cerr << "Error: '" << pathStr << "' is not a NIF/KF file, BSA/BA2 archive, or directory"
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
|
|
|
@ -81,11 +81,6 @@ int runApplication(int argc, char* argv[])
|
||||||
|
|
||||||
Application application(argc, argv);
|
Application application(argc, argv);
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
QDir dir(QCoreApplication::applicationDirPath());
|
|
||||||
QDir::setCurrent(dir.absolutePath());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
application.setWindowIcon(QIcon(":./openmw-cs.png"));
|
application.setWindowIcon(QIcon(":./openmw-cs.png"));
|
||||||
|
|
||||||
CS::Editor editor(argc, argv);
|
CS::Editor editor(argc, argv);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include <components/esm3/loaddial.hpp>
|
#include <components/esm3/loaddial.hpp>
|
||||||
|
#include <components/esm3/loadmgef.hpp>
|
||||||
#include <components/esm3/loadskil.hpp>
|
#include <components/esm3/loadskil.hpp>
|
||||||
#include <components/misc/strings/lower.hpp>
|
#include <components/misc/strings/lower.hpp>
|
||||||
|
|
||||||
#include "collectionbase.hpp"
|
#include "collectionbase.hpp"
|
||||||
#include "columnbase.hpp"
|
#include "columnbase.hpp"
|
||||||
|
#include "columnimp.hpp"
|
||||||
#include "info.hpp"
|
#include "info.hpp"
|
||||||
#include "land.hpp"
|
#include "land.hpp"
|
||||||
#include "landtexture.hpp"
|
#include "landtexture.hpp"
|
||||||
|
@ -82,6 +84,17 @@ namespace CSMWorld
|
||||||
record.mIndex = index;
|
record.mIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline ESM::RefId getRecordId(const ESM::MagicEffect& record)
|
||||||
|
{
|
||||||
|
return ESM::RefId::stringRefId(CSMWorld::getStringId(record.mId));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setRecordId(const ESM::RefId& id, ESM::MagicEffect& record)
|
||||||
|
{
|
||||||
|
int index = ESM::MagicEffect::indexNameToIndex(id.getRefIdString());
|
||||||
|
record.mId = ESM::RefId::index(ESM::REC_MGEF, static_cast<std::uint32_t>(index));
|
||||||
|
}
|
||||||
|
|
||||||
inline ESM::RefId getRecordId(const LandTexture& record)
|
inline ESM::RefId getRecordId(const LandTexture& record)
|
||||||
{
|
{
|
||||||
return ESM::RefId::stringRefId(LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex));
|
return ESM::RefId::stringRefId(LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex));
|
||||||
|
@ -504,7 +517,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);
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ add_openmw_dir (mwrender
|
||||||
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
|
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
|
||||||
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover
|
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover
|
||||||
postprocessor pingpongcull luminancecalculator pingpongcanvas transparentpass precipitationocclusion ripples
|
postprocessor pingpongcull luminancecalculator pingpongcanvas transparentpass precipitationocclusion ripples
|
||||||
actorutil distortion
|
actorutil distortion animationpriority bonegroup blendmask
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwinput
|
add_openmw_dir (mwinput
|
||||||
|
@ -64,7 +64,7 @@ add_openmw_dir (mwlua
|
||||||
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
||||||
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings
|
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings
|
||||||
postprocessingbindings stats debugbindings corebindings worldbindings worker magicbindings factionbindings
|
postprocessingbindings stats debugbindings corebindings worldbindings worker magicbindings factionbindings
|
||||||
classbindings itemdata inputprocessor
|
classbindings itemdata inputprocessor animationbindings
|
||||||
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
||||||
types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus
|
types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus
|
||||||
types/potion types/ingredient types/misc types/repair types/armor types/light types/static
|
types/potion types/ingredient types/misc types/repair types/armor types/light types/static
|
||||||
|
@ -113,7 +113,7 @@ add_openmw_dir (mwstate
|
||||||
|
|
||||||
add_openmw_dir (mwbase
|
add_openmw_dir (mwbase
|
||||||
environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager
|
environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager
|
||||||
inputmanager windowmanager statemanager
|
inputmanager windowmanager statemanager luamanager
|
||||||
)
|
)
|
||||||
|
|
||||||
# Main executable
|
# Main executable
|
||||||
|
|
|
@ -321,12 +321,8 @@ 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);
|
||||||
|
|
||||||
if (mStateManager->getState() != MWBase::StateManager::State_NoGame)
|
|
||||||
{
|
|
||||||
mWorld->updateWindowManager();
|
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);
|
||||||
|
|
|
@ -219,8 +219,6 @@ int runApplication(int argc, char* argv[])
|
||||||
Platform::init();
|
Platform::init();
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
std::filesystem::path binary_path = std::filesystem::absolute(std::filesystem::path(argv[0]));
|
|
||||||
std::filesystem::current_path(binary_path.parent_path());
|
|
||||||
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <SDL_events.h>
|
#include <SDL_events.h>
|
||||||
|
|
||||||
#include "../mwgui/mode.hpp"
|
#include "../mwgui/mode.hpp"
|
||||||
|
#include "../mwrender/animationpriority.hpp"
|
||||||
#include <components/sdlutil/events.hpp>
|
#include <components/sdlutil/events.hpp>
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
|
@ -61,10 +62,14 @@ namespace MWBase
|
||||||
virtual void itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) = 0;
|
virtual void itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) = 0;
|
||||||
virtual void objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0;
|
virtual void objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0;
|
||||||
virtual void useItem(const MWWorld::Ptr& object, const MWWorld::Ptr& actor, bool force) = 0;
|
virtual void useItem(const MWWorld::Ptr& object, const MWWorld::Ptr& actor, bool force) = 0;
|
||||||
|
virtual void animationTextKey(const MWWorld::Ptr& actor, const std::string& key) = 0;
|
||||||
|
virtual void playAnimation(const MWWorld::Ptr& object, const std::string& groupname,
|
||||||
|
const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
|
||||||
|
std::string_view start, std::string_view stop, float startpoint, size_t loops, bool loopfallback)
|
||||||
|
= 0;
|
||||||
virtual void exteriorCreated(MWWorld::CellStore& cell) = 0;
|
virtual void exteriorCreated(MWWorld::CellStore& cell) = 0;
|
||||||
virtual void actorDied(const MWWorld::Ptr& actor) = 0;
|
virtual void actorDied(const MWWorld::Ptr& actor) = 0;
|
||||||
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
||||||
|
|
||||||
// `arg` is either forwarded from MWGui::pushGuiMode or empty
|
// `arg` is either forwarded from MWGui::pushGuiMode or empty
|
||||||
virtual void uiModeChanged(const MWWorld::Ptr& arg) = 0;
|
virtual void uiModeChanged(const MWWorld::Ptr& arg) = 0;
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "../mwmechanics/greetingstate.hpp"
|
#include "../mwmechanics/greetingstate.hpp"
|
||||||
|
#include "../mwrender/animationpriority.hpp"
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
@ -170,16 +171,33 @@ namespace MWBase
|
||||||
///< Forces an object to refresh its animation state.
|
///< Forces an object to refresh its animation state.
|
||||||
|
|
||||||
virtual bool playAnimationGroup(
|
virtual bool playAnimationGroup(
|
||||||
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number = 1, bool persist = false)
|
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number = 1, bool scripted = false)
|
||||||
= 0;
|
= 0;
|
||||||
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
||||||
/// in the scene should be ignored.
|
/// in the scene should be ignored.
|
||||||
///
|
///
|
||||||
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
|
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
|
||||||
/// \param count How many times the animation should be run
|
/// \param number How many times the animation should be run
|
||||||
/// \param persist Whether the animation state should be stored in saved games
|
/// \param scripted Whether the animation should be treated as a scripted animation.
|
||||||
/// and persist after cell unload.
|
|
||||||
/// \return Success or error
|
/// \return Success or error
|
||||||
|
virtual bool playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, int loops, float speed,
|
||||||
|
std::string_view startKey, std::string_view stopKey, bool forceLoop)
|
||||||
|
= 0;
|
||||||
|
///< Lua variant of playAnimationGroup. The mode parameter is omitted
|
||||||
|
/// and forced to 0. modes 1 and 2 can be emulated by doing clearAnimationQueue() and
|
||||||
|
/// setting the startKey.
|
||||||
|
///
|
||||||
|
/// \param number How many times the animation should be run
|
||||||
|
/// \param speed How fast to play the animation, where 1.f = normal speed
|
||||||
|
/// \param startKey Which textkey to start the animation from
|
||||||
|
/// \param stopKey Which textkey to stop the animation on
|
||||||
|
/// \param forceLoop Force the animation to be looping, even if it's normally not looping.
|
||||||
|
/// \param blendMask See MWRender::Animation::BlendMask
|
||||||
|
/// \param scripted Whether the animation should be treated as as scripted animation
|
||||||
|
/// \return Success or error
|
||||||
|
///
|
||||||
|
|
||||||
|
virtual void enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable) = 0;
|
||||||
|
|
||||||
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
|
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
|
||||||
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
|
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
|
||||||
|
@ -187,9 +205,14 @@ 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;
|
||||||
|
|
||||||
|
/// Clear out the animation queue, and cancel any animation currently playing from the queue
|
||||||
|
virtual void clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted) = 0;
|
||||||
|
|
||||||
/// 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
|
||||||
/// paused we may want to do it manually (after equipping permanent enchantment)
|
/// paused we may want to do it manually (after equipping permanent enchantment)
|
||||||
virtual void updateMagicEffects(const MWWorld::Ptr& ptr) = 0;
|
virtual void updateMagicEffects(const MWWorld::Ptr& ptr) = 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
|
||||||
|
|
|
@ -143,7 +143,7 @@ namespace MWClass
|
||||||
|
|
||||||
list.push_back(params);
|
list.push_back(params);
|
||||||
}
|
}
|
||||||
info.effects = list;
|
info.effects = std::move(list);
|
||||||
|
|
||||||
info.text = std::move(text);
|
info.text = std::move(text);
|
||||||
info.isIngredient = true;
|
info.isIngredient = true;
|
||||||
|
|
|
@ -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,39 +454,50 @@ 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);
|
{
|
||||||
|
const MWWorld::InventoryStore& invStore = customData->asNpcCustomData().mInventoryStore;
|
||||||
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);
|
||||||
if (equipped != invStore.end())
|
if (equipped != invStore.end())
|
||||||
{
|
{
|
||||||
std::vector<ESM::PartReference> parts;
|
const auto addParts = [&](const std::vector<ESM::PartReference>& parts) {
|
||||||
|
for (const ESM::PartReference& partRef : parts)
|
||||||
|
{
|
||||||
|
const ESM::RefId& partname
|
||||||
|
= (female && !partRef.mFemale.empty()) || (!female && partRef.mMale.empty())
|
||||||
|
? partRef.mFemale
|
||||||
|
: partRef.mMale;
|
||||||
|
|
||||||
|
const ESM::BodyPart* part = esmStore->get<ESM::BodyPart>().search(partname);
|
||||||
|
if (part && !part->mModel.empty())
|
||||||
|
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
||||||
|
}
|
||||||
|
};
|
||||||
if (equipped->getType() == ESM::Clothing::sRecordId)
|
if (equipped->getType() == ESM::Clothing::sRecordId)
|
||||||
{
|
{
|
||||||
const ESM::Clothing* clothes = equipped->get<ESM::Clothing>()->mBase;
|
const ESM::Clothing* clothes = equipped->get<ESM::Clothing>()->mBase;
|
||||||
parts = clothes->mParts.mParts;
|
addParts(clothes->mParts.mParts);
|
||||||
}
|
}
|
||||||
else if (equipped->getType() == ESM::Armor::sRecordId)
|
else if (equipped->getType() == ESM::Armor::sRecordId)
|
||||||
{
|
{
|
||||||
const ESM::Armor* armor = equipped->get<ESM::Armor>()->mBase;
|
const ESM::Armor* armor = equipped->get<ESM::Armor>()->mBase;
|
||||||
parts = armor->mParts.mParts;
|
addParts(armor->mParts.mParts);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -493,16 +505,6 @@ namespace MWClass
|
||||||
if (!model.empty())
|
if (!model.empty())
|
||||||
models.push_back(model);
|
models.push_back(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::vector<ESM::PartReference>::const_iterator it = parts.begin(); it != parts.end(); ++it)
|
|
||||||
{
|
|
||||||
const ESM::RefId& partname
|
|
||||||
= (female && !it->mFemale.empty()) || (!female && it->mMale.empty()) ? it->mFemale : it->mMale;
|
|
||||||
|
|
||||||
const ESM::BodyPart* part
|
|
||||||
= MWBase::Environment::get().getESMStore()->get<ESM::BodyPart>().search(partname);
|
|
||||||
if (part && !part->mModel.empty())
|
|
||||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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().
|
||||||
|
|
||||||
|
|
|
@ -63,17 +63,17 @@ namespace
|
||||||
switch (order)
|
switch (order)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
return { question, { r0, r1, r2 }, sound };
|
return { std::move(question), { std::move(r0), std::move(r1), std::move(r2) }, std::move(sound) };
|
||||||
case 1:
|
case 1:
|
||||||
return { question, { r0, r2, r1 }, sound };
|
return { std::move(question), { std::move(r0), std::move(r2), std::move(r1) }, std::move(sound) };
|
||||||
case 2:
|
case 2:
|
||||||
return { question, { r1, r0, r2 }, sound };
|
return { std::move(question), { std::move(r1), std::move(r0), std::move(r2) }, std::move(sound) };
|
||||||
case 3:
|
case 3:
|
||||||
return { question, { r1, r2, r0 }, sound };
|
return { std::move(question), { std::move(r1), std::move(r2), std::move(r0) }, std::move(sound) };
|
||||||
case 4:
|
case 4:
|
||||||
return { question, { r2, r0, r1 }, sound };
|
return { std::move(question), { std::move(r2), std::move(r0), std::move(r1) }, std::move(sound) };
|
||||||
default:
|
default:
|
||||||
return { question, { r2, r1, r0 }, sound };
|
return { std::move(question), { std::move(r2), std::move(r1), std::move(r0) }, std::move(sound) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,7 +196,7 @@ namespace MWGui
|
||||||
std::string topicName
|
std::string topicName
|
||||||
= Misc::StringUtils::lowerCase(windowManager->getTranslationDataStorage().topicStandardForm(link));
|
= Misc::StringUtils::lowerCase(windowManager->getTranslationDataStorage().topicStandardForm(link));
|
||||||
|
|
||||||
std::string displayName = link;
|
std::string displayName = std::move(link);
|
||||||
while (displayName[displayName.size() - 1] == '*')
|
while (displayName[displayName.size() - 1] == '*')
|
||||||
displayName.erase(displayName.size() - 1, 1);
|
displayName.erase(displayName.size() - 1, 1);
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ namespace MWGui
|
||||||
i = match.mEnd;
|
i = match.mEnd;
|
||||||
}
|
}
|
||||||
if (i != text.end())
|
if (i != text.end())
|
||||||
addTopicLink(typesetter, 0, i - text.begin(), text.size());
|
addTopicLink(std::move(typesetter), 0, i - text.begin(), text.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
|
|
@ -136,9 +136,9 @@ namespace MWGui
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (enabled)
|
if (enabled)
|
||||||
processor->enableTechnique(technique);
|
processor->enableTechnique(std::move(technique));
|
||||||
else
|
else
|
||||||
processor->disableTechnique(technique);
|
processor->disableTechnique(std::move(technique));
|
||||||
processor->saveChain();
|
processor->saveChain();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ namespace MWGui
|
||||||
if (technique->getDynamic())
|
if (technique->getDynamic())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (processor->enableTechnique(technique, index) != MWRender::PostProcessor::Status_Error)
|
if (processor->enableTechnique(std::move(technique), index) != MWRender::PostProcessor::Status_Error)
|
||||||
processor->saveChain();
|
processor->saveChain();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
365
apps/openmw/mwlua/animationbindings.cpp
Normal file
365
apps/openmw/mwlua/animationbindings.cpp
Normal file
|
@ -0,0 +1,365 @@
|
||||||
|
#include <components/esm3/loadmgef.hpp>
|
||||||
|
#include <components/esm3/loadstat.hpp>
|
||||||
|
#include <components/lua/asyncpackage.hpp>
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
#include <components/lua/utilpackage.hpp>
|
||||||
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
|
#include <components/settings/values.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwmechanics/character.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
#include "context.hpp"
|
||||||
|
#include "luamanagerimp.hpp"
|
||||||
|
#include "objectvariant.hpp"
|
||||||
|
|
||||||
|
#include "animationbindings.hpp"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
struct AnimationGroup;
|
||||||
|
struct TextKeyCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sol
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct is_automagical<MWLua::AnimationGroup> : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct is_automagical<std::shared_ptr<MWLua::TextKeyCallback>> : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
using BlendMask = MWRender::Animation::BlendMask;
|
||||||
|
using BoneGroup = MWRender::Animation::BoneGroup;
|
||||||
|
using Priority = MWMechanics::Priority;
|
||||||
|
using AnimationPriorities = MWRender::Animation::AnimPriority;
|
||||||
|
|
||||||
|
MWWorld::Ptr getMutablePtrOrThrow(const ObjectVariant& variant)
|
||||||
|
{
|
||||||
|
if (variant.isLObject())
|
||||||
|
throw std::runtime_error("Local scripts can only modify animations of the object they are attached to.");
|
||||||
|
|
||||||
|
MWWorld::Ptr ptr = variant.ptr();
|
||||||
|
if (ptr.isEmpty())
|
||||||
|
throw std::runtime_error("Invalid object");
|
||||||
|
if (!ptr.getRefData().isEnabled())
|
||||||
|
throw std::runtime_error("Can't use a disabled object");
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr getPtrOrThrow(const ObjectVariant& variant)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr ptr = variant.ptr();
|
||||||
|
if (ptr.isEmpty())
|
||||||
|
throw std::runtime_error("Invalid object");
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWRender::Animation* getMutableAnimationOrThrow(const ObjectVariant& variant)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr ptr = getMutablePtrOrThrow(variant);
|
||||||
|
auto world = MWBase::Environment::get().getWorld();
|
||||||
|
MWRender::Animation* anim = world->getAnimation(ptr);
|
||||||
|
if (!anim)
|
||||||
|
throw std::runtime_error("Object has no animation");
|
||||||
|
return anim;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MWRender::Animation* getConstAnimationOrThrow(const ObjectVariant& variant)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr ptr = getPtrOrThrow(variant);
|
||||||
|
auto world = MWBase::Environment::get().getWorld();
|
||||||
|
const MWRender::Animation* anim = world->getAnimation(ptr);
|
||||||
|
if (!anim)
|
||||||
|
throw std::runtime_error("Object has no animation");
|
||||||
|
return anim;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM::Static* getStatic(const sol::object& staticOrID)
|
||||||
|
{
|
||||||
|
if (staticOrID.is<ESM::Static>())
|
||||||
|
return staticOrID.as<const ESM::Static*>();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESM::RefId id = ESM::RefId::deserializeText(LuaUtil::cast<std::string_view>(staticOrID));
|
||||||
|
return MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getStaticModelOrThrow(const sol::object& staticOrID)
|
||||||
|
{
|
||||||
|
const ESM::Static* static_ = getStatic(staticOrID);
|
||||||
|
if (!static_)
|
||||||
|
throw std::runtime_error("Invalid static");
|
||||||
|
|
||||||
|
return Misc::ResourceHelpers::correctMeshPath(static_->mModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AnimationPriorities getPriorityArgument(const sol::table& args)
|
||||||
|
{
|
||||||
|
auto asPriorityEnum = args.get<sol::optional<Priority>>("priority");
|
||||||
|
if (asPriorityEnum)
|
||||||
|
return asPriorityEnum.value();
|
||||||
|
|
||||||
|
auto asTable = args.get<sol::optional<sol::table>>("priority");
|
||||||
|
if (asTable)
|
||||||
|
{
|
||||||
|
AnimationPriorities priorities = AnimationPriorities(Priority::Priority_Default);
|
||||||
|
for (auto entry : asTable.value())
|
||||||
|
{
|
||||||
|
if (!entry.first.is<BoneGroup>() || !entry.second.is<Priority>())
|
||||||
|
throw std::runtime_error("Priority table must consist of BoneGroup-Priority pairs only");
|
||||||
|
auto group = entry.first.as<BoneGroup>();
|
||||||
|
auto priority = entry.second.as<Priority>();
|
||||||
|
if (group < 0 || group >= BoneGroup::Num_BoneGroups)
|
||||||
|
throw std::runtime_error("Invalid bonegroup: " + std::to_string(group));
|
||||||
|
priorities[group] = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
return priorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Priority::Priority_Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
sol::table initAnimationPackage(const Context& context)
|
||||||
|
{
|
||||||
|
auto* lua = context.mLua;
|
||||||
|
auto mechanics = MWBase::Environment::get().getMechanicsManager();
|
||||||
|
auto world = MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
|
sol::table api(lua->sol(), sol::create);
|
||||||
|
|
||||||
|
api["PRIORITY"]
|
||||||
|
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWMechanics::Priority>({
|
||||||
|
{ "Default", MWMechanics::Priority::Priority_Default },
|
||||||
|
{ "WeaponLowerBody", MWMechanics::Priority::Priority_WeaponLowerBody },
|
||||||
|
{ "SneakIdleLowerBody", MWMechanics::Priority::Priority_SneakIdleLowerBody },
|
||||||
|
{ "SwimIdle", MWMechanics::Priority::Priority_SwimIdle },
|
||||||
|
{ "Jump", MWMechanics::Priority::Priority_Jump },
|
||||||
|
{ "Movement", MWMechanics::Priority::Priority_Movement },
|
||||||
|
{ "Hit", MWMechanics::Priority::Priority_Hit },
|
||||||
|
{ "Weapon", MWMechanics::Priority::Priority_Weapon },
|
||||||
|
{ "Block", MWMechanics::Priority::Priority_Block },
|
||||||
|
{ "Knockdown", MWMechanics::Priority::Priority_Knockdown },
|
||||||
|
{ "Torch", MWMechanics::Priority::Priority_Torch },
|
||||||
|
{ "Storm", MWMechanics::Priority::Priority_Storm },
|
||||||
|
{ "Death", MWMechanics::Priority::Priority_Death },
|
||||||
|
{ "Scripted", MWMechanics::Priority::Priority_Scripted },
|
||||||
|
}));
|
||||||
|
|
||||||
|
api["BLEND_MASK"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, BlendMask>({
|
||||||
|
{ "LowerBody", BlendMask::BlendMask_LowerBody },
|
||||||
|
{ "Torso", BlendMask::BlendMask_Torso },
|
||||||
|
{ "LeftArm", BlendMask::BlendMask_LeftArm },
|
||||||
|
{ "RightArm", BlendMask::BlendMask_RightArm },
|
||||||
|
{ "UpperBody", BlendMask::BlendMask_UpperBody },
|
||||||
|
{ "All", BlendMask::BlendMask_All },
|
||||||
|
}));
|
||||||
|
|
||||||
|
api["BONE_GROUP"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, BoneGroup>({
|
||||||
|
{ "LowerBody", BoneGroup::BoneGroup_LowerBody },
|
||||||
|
{ "Torso", BoneGroup::BoneGroup_Torso },
|
||||||
|
{ "LeftArm", BoneGroup::BoneGroup_LeftArm },
|
||||||
|
{ "RightArm", BoneGroup::BoneGroup_RightArm },
|
||||||
|
}));
|
||||||
|
|
||||||
|
api["hasAnimation"] = [world](const sol::object& object) -> bool {
|
||||||
|
return world->getAnimation(getPtrOrThrow(ObjectVariant(object))) != nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// equivalent to MWScript's SkipAnim
|
||||||
|
api["skipAnimationThisFrame"] = [mechanics](const sol::object& object) {
|
||||||
|
MWWorld::Ptr ptr = getMutablePtrOrThrow(ObjectVariant(object));
|
||||||
|
// This sets a flag that is only used during the update pass, so
|
||||||
|
// there's no need to queue
|
||||||
|
mechanics->skipAnimation(ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
api["getTextKeyTime"] = [](const sol::object& object, std::string_view key) -> sol::optional<float> {
|
||||||
|
float time = getConstAnimationOrThrow(ObjectVariant(object))->getTextKeyTime(key);
|
||||||
|
if (time >= 0.f)
|
||||||
|
return time;
|
||||||
|
return sol::nullopt;
|
||||||
|
};
|
||||||
|
api["isPlaying"] = [](const sol::object& object, std::string_view groupname) {
|
||||||
|
return getConstAnimationOrThrow(ObjectVariant(object))->isPlaying(groupname);
|
||||||
|
};
|
||||||
|
api["getCurrentTime"] = [](const sol::object& object, std::string_view groupname) -> sol::optional<float> {
|
||||||
|
float time = getConstAnimationOrThrow(ObjectVariant(object))->getCurrentTime(groupname);
|
||||||
|
if (time >= 0.f)
|
||||||
|
return time;
|
||||||
|
return sol::nullopt;
|
||||||
|
};
|
||||||
|
api["isLoopingAnimation"] = [](const sol::object& object, std::string_view groupname) {
|
||||||
|
return getConstAnimationOrThrow(ObjectVariant(object))->isLoopingAnimation(groupname);
|
||||||
|
};
|
||||||
|
api["cancel"] = [](const sol::object& object, std::string_view groupname) {
|
||||||
|
return getMutableAnimationOrThrow(ObjectVariant(object))->disable(groupname);
|
||||||
|
};
|
||||||
|
api["setLoopingEnabled"] = [](const sol::object& object, std::string_view groupname, bool enabled) {
|
||||||
|
return getMutableAnimationOrThrow(ObjectVariant(object))->setLoopingEnabled(groupname, enabled);
|
||||||
|
};
|
||||||
|
// MWRender::Animation::getInfo can also return the current speed multiplier, but this is never used.
|
||||||
|
api["getCompletion"] = [](const sol::object& object, std::string_view groupname) -> sol::optional<float> {
|
||||||
|
float completion = 0.f;
|
||||||
|
if (getConstAnimationOrThrow(ObjectVariant(object))->getInfo(groupname, &completion))
|
||||||
|
return completion;
|
||||||
|
return sol::nullopt;
|
||||||
|
};
|
||||||
|
api["getLoopCount"] = [](const sol::object& object, std::string groupname) -> sol::optional<size_t> {
|
||||||
|
size_t loops = 0;
|
||||||
|
if (getConstAnimationOrThrow(ObjectVariant(object))->getInfo(groupname, nullptr, nullptr, &loops))
|
||||||
|
return loops;
|
||||||
|
return sol::nullopt;
|
||||||
|
};
|
||||||
|
api["getSpeed"] = [](const sol::object& object, std::string groupname) -> sol::optional<float> {
|
||||||
|
float speed = 0.f;
|
||||||
|
if (getConstAnimationOrThrow(ObjectVariant(object))->getInfo(groupname, nullptr, &speed, nullptr))
|
||||||
|
return speed;
|
||||||
|
return sol::nullopt;
|
||||||
|
};
|
||||||
|
api["setSpeed"] = [](const sol::object& object, std::string groupname, float speed) {
|
||||||
|
getMutableAnimationOrThrow(ObjectVariant(object))->adjustSpeedMult(groupname, speed);
|
||||||
|
};
|
||||||
|
api["getActiveGroup"] = [](const sol::object& object, MWRender::BoneGroup boneGroup) -> std::string_view {
|
||||||
|
return getConstAnimationOrThrow(ObjectVariant(object))->getActiveGroup(boneGroup);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clears out the animation queue, and cancel any animation currently playing from the queue
|
||||||
|
api["clearAnimationQueue"] = [mechanics](const sol::object& object, bool clearScripted) {
|
||||||
|
MWWorld::Ptr ptr = getMutablePtrOrThrow(ObjectVariant(object));
|
||||||
|
mechanics->clearAnimationQueue(ptr, clearScripted);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extended variant of MWScript's PlayGroup and LoopGroup
|
||||||
|
api["playQueued"] = sol::overload(
|
||||||
|
[mechanics](const sol::object& object, const std::string& groupname, const sol::table& options) {
|
||||||
|
int numberOfLoops = options.get_or("loops", std::numeric_limits<int>::max());
|
||||||
|
float speed = options.get_or("speed", 1.f);
|
||||||
|
std::string startKey = options.get_or<std::string>("startkey", "start");
|
||||||
|
std::string stopKey = options.get_or<std::string>("stopkey", "stop");
|
||||||
|
bool forceLoop = options.get_or("forceloop", false);
|
||||||
|
|
||||||
|
MWWorld::Ptr ptr = getMutablePtrOrThrow(ObjectVariant(object));
|
||||||
|
mechanics->playAnimationGroupLua(ptr, groupname, numberOfLoops, speed, startKey, stopKey, forceLoop);
|
||||||
|
},
|
||||||
|
[mechanics](const sol::object& object, const std::string& groupname) {
|
||||||
|
MWWorld::Ptr ptr = getMutablePtrOrThrow(ObjectVariant(object));
|
||||||
|
mechanics->playAnimationGroupLua(
|
||||||
|
ptr, groupname, std::numeric_limits<int>::max(), 1, "start", "stop", false);
|
||||||
|
});
|
||||||
|
|
||||||
|
api["playBlended"] = [](const sol::object& object, std::string_view groupname, const sol::table& options) {
|
||||||
|
int loops = options.get_or("loops", 0);
|
||||||
|
MWRender::Animation::AnimPriority priority = getPriorityArgument(options);
|
||||||
|
BlendMask blendMask = options.get_or("blendmask", BlendMask::BlendMask_All);
|
||||||
|
bool autoDisable = options.get_or("autodisable", true);
|
||||||
|
float speed = options.get_or("speed", 1.0f);
|
||||||
|
std::string start = options.get_or<std::string>("startkey", "start");
|
||||||
|
std::string stop = options.get_or<std::string>("stopkey", "stop");
|
||||||
|
float startpoint = options.get_or("startpoint", 0.0f);
|
||||||
|
bool forceLoop = options.get_or("forceloop", false);
|
||||||
|
|
||||||
|
auto animation = getMutableAnimationOrThrow(ObjectVariant(object));
|
||||||
|
animation->play(groupname, priority, blendMask, autoDisable, speed, start, stop, startpoint, loops,
|
||||||
|
forceLoop || animation->isLoopingAnimation(groupname));
|
||||||
|
};
|
||||||
|
|
||||||
|
api["hasGroup"] = [](const sol::object& object, std::string_view groupname) -> bool {
|
||||||
|
const MWRender::Animation* anim = getConstAnimationOrThrow(ObjectVariant(object));
|
||||||
|
return anim->hasAnimation(groupname);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note: This checks the nodemap, and does not read the scene graph itself, and so should be thread safe.
|
||||||
|
api["hasBone"] = [](const sol::object& object, std::string_view bonename) -> bool {
|
||||||
|
const MWRender::Animation* anim = getConstAnimationOrThrow(ObjectVariant(object));
|
||||||
|
return anim->getNode(bonename) != nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
api["addVfx"] = sol::overload(
|
||||||
|
[context](const sol::object& object, const sol::object& staticOrID) {
|
||||||
|
context.mLuaManager->addAction(
|
||||||
|
[object = ObjectVariant(object), model = getStaticModelOrThrow(staticOrID)] {
|
||||||
|
MWRender::Animation* anim = getMutableAnimationOrThrow(object);
|
||||||
|
anim->addEffect(model, "");
|
||||||
|
},
|
||||||
|
"addVfxAction");
|
||||||
|
},
|
||||||
|
[context](const sol::object& object, const sol::object& staticOrID, const sol::table& options) {
|
||||||
|
context.mLuaManager->addAction(
|
||||||
|
[object = ObjectVariant(object), model = getStaticModelOrThrow(staticOrID),
|
||||||
|
effectId = options.get_or<std::string>("vfxId", ""), loop = options.get_or("loop", false),
|
||||||
|
bonename = options.get_or<std::string>("bonename", ""),
|
||||||
|
particleTexture = options.get_or<std::string>("particleTextureOverride", "")] {
|
||||||
|
MWRender::Animation* anim = getMutableAnimationOrThrow(ObjectVariant(object));
|
||||||
|
|
||||||
|
anim->addEffect(model, effectId, loop, bonename, particleTexture);
|
||||||
|
},
|
||||||
|
"addVfxAction");
|
||||||
|
});
|
||||||
|
|
||||||
|
api["removeVfx"] = [context](const sol::object& object, std::string_view effectId) {
|
||||||
|
context.mLuaManager->addAction(
|
||||||
|
[object = ObjectVariant(object), effectId = std::string(effectId)] {
|
||||||
|
MWRender::Animation* anim = getMutableAnimationOrThrow(object);
|
||||||
|
anim->removeEffect(effectId);
|
||||||
|
},
|
||||||
|
"removeVfxAction");
|
||||||
|
};
|
||||||
|
|
||||||
|
api["removeAllVfx"] = [context](const sol::object& object) {
|
||||||
|
context.mLuaManager->addAction(
|
||||||
|
[object = ObjectVariant(object)] {
|
||||||
|
MWRender::Animation* anim = getMutableAnimationOrThrow(object);
|
||||||
|
anim->removeEffects();
|
||||||
|
},
|
||||||
|
"removeVfxAction");
|
||||||
|
};
|
||||||
|
|
||||||
|
return LuaUtil::makeReadOnly(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
sol::table initCoreVfxBindings(const Context& context)
|
||||||
|
{
|
||||||
|
sol::state_view& lua = context.mLua->sol();
|
||||||
|
sol::table api(lua, sol::create);
|
||||||
|
auto world = MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
|
api["spawn"] = sol::overload(
|
||||||
|
[world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos) {
|
||||||
|
auto model = getStaticModelOrThrow(staticOrID);
|
||||||
|
context.mLuaManager->addAction(
|
||||||
|
[world, model, worldPos]() { world->spawnEffect(model, "", worldPos); }, "openmw.vfx.spawn");
|
||||||
|
},
|
||||||
|
[world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos, const sol::table& options) {
|
||||||
|
auto model = getStaticModelOrThrow(staticOrID);
|
||||||
|
|
||||||
|
bool magicVfx = options.get_or("mwMagicVfx", true);
|
||||||
|
std::string textureOverride = options.get_or<std::string>("particleTextureOverride", "");
|
||||||
|
float scale = options.get_or("scale", 1.f);
|
||||||
|
|
||||||
|
context.mLuaManager->addAction(
|
||||||
|
[world, model, textureOverride, worldPos, scale, magicVfx]() {
|
||||||
|
world->spawnEffect(model, textureOverride, worldPos, scale, magicVfx);
|
||||||
|
},
|
||||||
|
"openmw.vfx.spawn");
|
||||||
|
});
|
||||||
|
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
}
|
12
apps/openmw/mwlua/animationbindings.hpp
Normal file
12
apps/openmw/mwlua/animationbindings.hpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef MWLUA_ANIMATIONBINDINGS_H
|
||||||
|
#define MWLUA_ANIMATIONBINDINGS_H
|
||||||
|
|
||||||
|
#include <sol/forward.hpp>
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
sol::table initAnimationPackage(const Context& context);
|
||||||
|
sol::table initCoreVfxBindings(const Context& context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_ANIMATIONBINDINGS_H
|
|
@ -115,6 +115,13 @@ namespace MWLua
|
||||||
return cell == c.mStore || (cell->getCell()->getWorldSpace() == c.mStore->getCell()->getWorldSpace());
|
return cell == c.mStore || (cell->getCell()->getWorldSpace() == c.mStore->getCell()->getWorldSpace());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
cellT["waterLevel"] = sol::readonly_property([](const CellT& c) -> sol::optional<float> {
|
||||||
|
if (c.mStore->getCell()->hasWater())
|
||||||
|
return c.mStore->getWaterLevel();
|
||||||
|
else
|
||||||
|
return sol::nullopt;
|
||||||
|
});
|
||||||
|
|
||||||
if constexpr (std::is_same_v<CellT, GCell>)
|
if constexpr (std::is_same_v<CellT, GCell>)
|
||||||
{ // only for global scripts
|
{ // only for global scripts
|
||||||
cellT["getAll"] = [ids = getPackageToTypeTable(context.mLua->sol())](
|
cellT["getAll"] = [ids = getPackageToTypeTable(context.mLua->sol())](
|
||||||
|
@ -270,7 +277,7 @@ namespace MWLua
|
||||||
if (!ok)
|
if (!ok)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
std::string("Incorrect type argument in cell:getAll: " + LuaUtil::toString(*type)));
|
std::string("Incorrect type argument in cell:getAll: " + LuaUtil::toString(*type)));
|
||||||
return GObjectList{ res };
|
return GObjectList{ std::move(res) };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "../mwworld/datetimemanager.hpp"
|
#include "../mwworld/datetimemanager.hpp"
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
#include "animationbindings.hpp"
|
||||||
#include "factionbindings.hpp"
|
#include "factionbindings.hpp"
|
||||||
#include "luaevents.hpp"
|
#include "luaevents.hpp"
|
||||||
#include "magicbindings.hpp"
|
#include "magicbindings.hpp"
|
||||||
|
@ -83,6 +84,7 @@ namespace MWLua
|
||||||
};
|
};
|
||||||
api["contentFiles"] = initContentFilesBindings(lua->sol());
|
api["contentFiles"] = initContentFilesBindings(lua->sol());
|
||||||
api["sound"] = initCoreSoundBindings(context);
|
api["sound"] = initCoreSoundBindings(context);
|
||||||
|
api["vfx"] = initCoreVfxBindings(context);
|
||||||
api["getFormId"] = [](std::string_view contentFile, unsigned int index) -> std::string {
|
api["getFormId"] = [](std::string_view contentFile, unsigned int index) -> std::string {
|
||||||
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
|
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
|
||||||
for (size_t i = 0; i < contentList.size(); ++i)
|
for (size_t i = 0; i < contentList.size(); ++i)
|
||||||
|
@ -133,6 +135,7 @@ namespace MWLua
|
||||||
api[k] = v;
|
api[k] = v;
|
||||||
api["sendGlobalEvent"] = sol::nil;
|
api["sendGlobalEvent"] = sol::nil;
|
||||||
api["sound"] = sol::nil;
|
api["sound"] = sol::nil;
|
||||||
|
api["vfx"] = sol::nil;
|
||||||
return LuaUtil::makeReadOnly(api);
|
return LuaUtil::makeReadOnly(api);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,15 @@ namespace MWLua
|
||||||
|
|
||||||
void operator()(const OnNewExterior& event) const { mGlobalScripts.onNewExterior(GCell{ &event.mCell }); }
|
void operator()(const OnNewExterior& event) const { mGlobalScripts.onNewExterior(GCell{ &event.mCell }); }
|
||||||
|
|
||||||
|
void operator()(const OnAnimationTextKey& event) const
|
||||||
|
{
|
||||||
|
MWWorld::Ptr actor = getPtr(event.mActor);
|
||||||
|
if (actor.isEmpty())
|
||||||
|
return;
|
||||||
|
if (auto* scripts = getLocalScripts(actor))
|
||||||
|
scripts->onAnimationTextKey(event.mGroupname, event.mKey);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MWWorld::Ptr getPtr(ESM::RefNum id) const
|
MWWorld::Ptr getPtr(ESM::RefNum id) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,7 +51,14 @@ namespace MWLua
|
||||||
{
|
{
|
||||||
MWWorld::CellStore& mCell;
|
MWWorld::CellStore& mCell;
|
||||||
};
|
};
|
||||||
using Event = std::variant<OnActive, OnInactive, OnConsume, OnActivate, OnUseItem, OnNewExterior, OnTeleported>;
|
struct OnAnimationTextKey
|
||||||
|
{
|
||||||
|
ESM::RefNum mActor;
|
||||||
|
std::string mGroupname;
|
||||||
|
std::string mKey;
|
||||||
|
};
|
||||||
|
using Event = std::variant<OnActive, OnInactive, OnConsume, OnActivate, OnUseItem, OnNewExterior, OnTeleported,
|
||||||
|
OnAnimationTextKey>;
|
||||||
|
|
||||||
void clear() { mQueue.clear(); }
|
void clear() { mQueue.clear(); }
|
||||||
void addToQueue(Event e) { mQueue.push_back(std::move(e)); }
|
void addToQueue(Event e) { mQueue.push_back(std::move(e)); }
|
||||||
|
|
|
@ -82,13 +82,18 @@ namespace MWLua
|
||||||
inputActions[sol::meta_function::pairs] = pairs;
|
inputActions[sol::meta_function::pairs] = pairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.mLua->sol().new_usertype<LuaUtil::InputAction::Info>("ActionInfo", "key",
|
auto actionInfo = context.mLua->sol().new_usertype<LuaUtil::InputAction::Info>("ActionInfo");
|
||||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mKey; }), "name",
|
actionInfo["key"] = sol::readonly_property(
|
||||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mName; }), "description",
|
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mKey; });
|
||||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mDescription; }), "type",
|
actionInfo["name"] = sol::readonly_property(
|
||||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mType; }), "l10n",
|
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mName; });
|
||||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mL10n; }), "defaultValue",
|
actionInfo["description"] = sol::readonly_property(
|
||||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mDefaultValue; }));
|
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mDescription; });
|
||||||
|
actionInfo["l10n"] = sol::readonly_property(
|
||||||
|
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mL10n; });
|
||||||
|
actionInfo["type"] = sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mType; });
|
||||||
|
actionInfo["defaultValue"]
|
||||||
|
= sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mDefaultValue; });
|
||||||
|
|
||||||
auto inputTriggers = context.mLua->sol().new_usertype<LuaUtil::InputTrigger::Registry>("InputTriggers");
|
auto inputTriggers = context.mLua->sol().new_usertype<LuaUtil::InputTrigger::Registry>("InputTriggers");
|
||||||
inputTriggers[sol::meta_function::index]
|
inputTriggers[sol::meta_function::index]
|
||||||
|
@ -108,11 +113,15 @@ namespace MWLua
|
||||||
inputTriggers[sol::meta_function::pairs] = pairs;
|
inputTriggers[sol::meta_function::pairs] = pairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.mLua->sol().new_usertype<LuaUtil::InputTrigger::Info>("TriggerInfo", "key",
|
auto triggerInfo = context.mLua->sol().new_usertype<LuaUtil::InputTrigger::Info>("TriggerInfo");
|
||||||
sol::property([](const LuaUtil::InputTrigger::Info& info) { return info.mKey; }), "name",
|
triggerInfo["key"] = sol::readonly_property(
|
||||||
sol::property([](const LuaUtil::InputTrigger::Info& info) { return info.mName; }), "description",
|
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mKey; });
|
||||||
sol::property([](const LuaUtil::InputTrigger::Info& info) { return info.mDescription; }), "l10n",
|
triggerInfo["name"] = sol::readonly_property(
|
||||||
sol::property([](const LuaUtil::InputTrigger::Info& info) { return info.mL10n; }));
|
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mName; });
|
||||||
|
triggerInfo["description"] = sol::readonly_property(
|
||||||
|
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mDescription; });
|
||||||
|
triggerInfo["l10n"] = sol::readonly_property(
|
||||||
|
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mL10n; });
|
||||||
|
|
||||||
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
|
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
|
||||||
sol::table api(context.mLua->sol(), sol::create);
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include <components/esm3/loadcell.hpp>
|
#include <components/esm3/loadcell.hpp>
|
||||||
#include <components/misc/strings/lower.hpp>
|
#include <components/misc/strings/lower.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwmechanics/aicombat.hpp"
|
#include "../mwmechanics/aicombat.hpp"
|
||||||
#include "../mwmechanics/aiescort.hpp"
|
#include "../mwmechanics/aiescort.hpp"
|
||||||
#include "../mwmechanics/aifollow.hpp"
|
#include "../mwmechanics/aifollow.hpp"
|
||||||
|
@ -162,6 +164,10 @@ namespace MWLua
|
||||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||||
ai.stack(MWMechanics::AiTravel(target.x(), target.y(), target.z(), false), ptr, cancelOther);
|
ai.stack(MWMechanics::AiTravel(target.x(), target.y(), target.z(), false), ptr, cancelOther);
|
||||||
};
|
};
|
||||||
|
selfAPI["_enableLuaAnimations"] = [](SelfObject& self, bool enable) {
|
||||||
|
const MWWorld::Ptr& ptr = self.ptr();
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->enableLuaAnimations(ptr, enable);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj)
|
LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj)
|
||||||
|
@ -170,7 +176,7 @@ namespace MWLua
|
||||||
{
|
{
|
||||||
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
|
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
|
||||||
registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers,
|
registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers,
|
||||||
&mOnTeleportedHandlers });
|
&mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers });
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalScripts::setActive(bool active)
|
void LocalScripts::setActive(bool active)
|
||||||
|
|
|
@ -71,6 +71,14 @@ namespace MWLua
|
||||||
void onConsume(const LObject& consumable) { callEngineHandlers(mOnConsumeHandlers, consumable); }
|
void onConsume(const LObject& consumable) { callEngineHandlers(mOnConsumeHandlers, consumable); }
|
||||||
void onActivated(const LObject& actor) { callEngineHandlers(mOnActivatedHandlers, actor); }
|
void onActivated(const LObject& actor) { callEngineHandlers(mOnActivatedHandlers, actor); }
|
||||||
void onTeleported() { callEngineHandlers(mOnTeleportedHandlers); }
|
void onTeleported() { callEngineHandlers(mOnTeleportedHandlers); }
|
||||||
|
void onAnimationTextKey(std::string_view groupname, std::string_view key)
|
||||||
|
{
|
||||||
|
callEngineHandlers(mOnAnimationTextKeyHandlers, groupname, key);
|
||||||
|
}
|
||||||
|
void onPlayAnimation(std::string_view groupname, const sol::table& options)
|
||||||
|
{
|
||||||
|
callEngineHandlers(mOnPlayAnimationHandlers, groupname, options);
|
||||||
|
}
|
||||||
|
|
||||||
void applyStatsCache();
|
void applyStatsCache();
|
||||||
|
|
||||||
|
@ -83,6 +91,8 @@ namespace MWLua
|
||||||
EngineHandlerList mOnConsumeHandlers{ "onConsume" };
|
EngineHandlerList mOnConsumeHandlers{ "onConsume" };
|
||||||
EngineHandlerList mOnActivatedHandlers{ "onActivated" };
|
EngineHandlerList mOnActivatedHandlers{ "onActivated" };
|
||||||
EngineHandlerList mOnTeleportedHandlers{ "onTeleported" };
|
EngineHandlerList mOnTeleportedHandlers{ "onTeleported" };
|
||||||
|
EngineHandlerList mOnAnimationTextKeyHandlers{ "_onAnimationTextKey" };
|
||||||
|
EngineHandlerList mOnPlayAnimationHandlers{ "_onPlayAnimation" };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwworld/datetimemanager.hpp"
|
#include "../mwworld/datetimemanager.hpp"
|
||||||
|
|
||||||
|
#include "animationbindings.hpp"
|
||||||
#include "camerabindings.hpp"
|
#include "camerabindings.hpp"
|
||||||
#include "cellbindings.hpp"
|
#include "cellbindings.hpp"
|
||||||
#include "corebindings.hpp"
|
#include "corebindings.hpp"
|
||||||
|
@ -30,6 +31,7 @@ namespace MWLua
|
||||||
sol::state_view lua = context.mLua->sol();
|
sol::state_view lua = context.mLua->sol();
|
||||||
MWWorld::DateTimeManager* tm = MWBase::Environment::get().getWorld()->getTimeManager();
|
MWWorld::DateTimeManager* tm = MWBase::Environment::get().getWorld()->getTimeManager();
|
||||||
return {
|
return {
|
||||||
|
{ "openmw.animation", initAnimationPackage(context) },
|
||||||
{ "openmw.async",
|
{ "openmw.async",
|
||||||
LuaUtil::getAsyncPackageInitializer(
|
LuaUtil::getAsyncPackageInitializer(
|
||||||
lua, [tm] { return tm->getSimulationTime(); }, [tm] { return tm->getGameTime(); }) },
|
lua, [tm] { return tm->getSimulationTime(); }, [tm] { return tm->getGameTime(); }) },
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwrender/bonegroup.hpp"
|
||||||
#include "../mwrender/postprocessor.hpp"
|
#include "../mwrender/postprocessor.hpp"
|
||||||
|
|
||||||
#include "../mwworld/datetimemanager.hpp"
|
#include "../mwworld/datetimemanager.hpp"
|
||||||
|
@ -402,6 +403,49 @@ namespace MWLua
|
||||||
mEngineEvents.addToQueue(EngineEvents::OnUseItem{ getId(actor), getId(object), force });
|
mEngineEvents.addToQueue(EngineEvents::OnUseItem{ getId(actor), getId(object), force });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LuaManager::animationTextKey(const MWWorld::Ptr& actor, const std::string& key)
|
||||||
|
{
|
||||||
|
auto pos = key.find(": ");
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
mEngineEvents.addToQueue(
|
||||||
|
EngineEvents::OnAnimationTextKey{ getId(actor), key.substr(0, pos), key.substr(pos + 2) });
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::playAnimation(const MWWorld::Ptr& actor, const std::string& groupname,
|
||||||
|
const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
|
||||||
|
std::string_view start, std::string_view stop, float startpoint, size_t loops, bool loopfallback)
|
||||||
|
{
|
||||||
|
sol::table options = mLua.newTable();
|
||||||
|
options["blendmask"] = blendMask;
|
||||||
|
options["autodisable"] = autodisable;
|
||||||
|
options["speed"] = speedmult;
|
||||||
|
options["startkey"] = start;
|
||||||
|
options["stopkey"] = stop;
|
||||||
|
options["startpoint"] = startpoint;
|
||||||
|
options["loops"] = loops;
|
||||||
|
options["forceloop"] = loopfallback;
|
||||||
|
|
||||||
|
bool priorityAsTable = false;
|
||||||
|
for (uint32_t i = 1; i < MWRender::sNumBlendMasks; i++)
|
||||||
|
if (priority[static_cast<MWRender::BoneGroup>(i)] != priority[static_cast<MWRender::BoneGroup>(0)])
|
||||||
|
priorityAsTable = true;
|
||||||
|
if (priorityAsTable)
|
||||||
|
{
|
||||||
|
sol::table priorityTable = mLua.newTable();
|
||||||
|
for (uint32_t i = 0; i < MWRender::sNumBlendMasks; i++)
|
||||||
|
priorityTable[static_cast<MWRender::BoneGroup>(i)] = priority[static_cast<MWRender::BoneGroup>(i)];
|
||||||
|
options["priority"] = priorityTable;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
options["priority"] = priority[MWRender::BoneGroup_LowerBody];
|
||||||
|
|
||||||
|
// mEngineEvents.addToQueue(event);
|
||||||
|
// Has to be called immediately, otherwise engine details that depend on animations playing immediately
|
||||||
|
// break.
|
||||||
|
if (auto* scripts = actor.getRefData().getLuaScripts())
|
||||||
|
scripts->onPlayAnimation(groupname, options);
|
||||||
|
}
|
||||||
|
|
||||||
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
|
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
mObjectLists.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
|
mObjectLists.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
|
||||||
|
|
|
@ -82,6 +82,10 @@ namespace MWLua
|
||||||
mEngineEvents.addToQueue(EngineEvents::OnActivate{ getId(actor), getId(object) });
|
mEngineEvents.addToQueue(EngineEvents::OnActivate{ getId(actor), getId(object) });
|
||||||
}
|
}
|
||||||
void useItem(const MWWorld::Ptr& object, const MWWorld::Ptr& actor, bool force) override;
|
void useItem(const MWWorld::Ptr& object, const MWWorld::Ptr& actor, bool force) override;
|
||||||
|
void animationTextKey(const MWWorld::Ptr& actor, const std::string& key) override;
|
||||||
|
void playAnimation(const MWWorld::Ptr& actor, const std::string& groupname,
|
||||||
|
const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
|
||||||
|
std::string_view start, std::string_view stop, float startpoint, size_t loops, bool loopfallback) override;
|
||||||
void exteriorCreated(MWWorld::CellStore& cell) override
|
void exteriorCreated(MWWorld::CellStore& cell) override
|
||||||
{
|
{
|
||||||
mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell });
|
mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell });
|
||||||
|
|
|
@ -389,6 +389,17 @@ namespace MWLua
|
||||||
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||||
});
|
});
|
||||||
|
magicEffectT["particle"]
|
||||||
|
= sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string_view { return rec.mParticle; });
|
||||||
|
magicEffectT["continuousVfx"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> bool {
|
||||||
|
return (rec.mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;
|
||||||
|
});
|
||||||
|
magicEffectT["castingStatic"] = sol::readonly_property(
|
||||||
|
[](const ESM::MagicEffect& rec) -> std::string { return rec.mCasting.serializeText(); });
|
||||||
|
magicEffectT["hitStatic"] = sol::readonly_property(
|
||||||
|
[](const ESM::MagicEffect& rec) -> std::string { return rec.mHit.serializeText(); });
|
||||||
|
magicEffectT["areaStatic"] = sol::readonly_property(
|
||||||
|
[](const ESM::MagicEffect& rec) -> std::string { return rec.mArea.serializeText(); });
|
||||||
magicEffectT["name"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string_view {
|
magicEffectT["name"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string_view {
|
||||||
return MWBase::Environment::get()
|
return MWBase::Environment::get()
|
||||||
.getWorld()
|
.getWorld()
|
||||||
|
@ -730,7 +741,7 @@ namespace MWLua
|
||||||
auto id = sol::make_object(lua, self.mIterator->getId().serializeText());
|
auto id = sol::make_object(lua, self.mIterator->getId().serializeText());
|
||||||
auto params = sol::make_object(lua, ActiveSpell{ self.mActor, *self.mIterator });
|
auto params = sol::make_object(lua, ActiveSpell{ self.mActor, *self.mIterator });
|
||||||
self.advance();
|
self.advance();
|
||||||
return { params, params };
|
return { id, params };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -104,8 +104,12 @@ namespace MWLua
|
||||||
});
|
});
|
||||||
mwscript["player"] = sol::readonly_property(
|
mwscript["player"] = sol::readonly_property(
|
||||||
[](const MWScriptRef&) { return GObject(MWBase::Environment::get().getWorld()->getPlayerPtr()); });
|
[](const MWScriptRef&) { return GObject(MWBase::Environment::get().getWorld()->getPlayerPtr()); });
|
||||||
mwscriptVars[sol::meta_function::index] = [](MWScriptVariables& s, std::string_view var) {
|
mwscriptVars[sol::meta_function::index]
|
||||||
|
= [](MWScriptVariables& s, std::string_view var) -> sol::optional<double> {
|
||||||
|
if (s.mRef.getLocals().hasVar(s.mRef.mId, var))
|
||||||
return s.mRef.getLocals().getVarAsDouble(s.mRef.mId, Misc::StringUtils::lowerCase(var));
|
return s.mRef.getLocals().getVarAsDouble(s.mRef.mId, Misc::StringUtils::lowerCase(var));
|
||||||
|
else
|
||||||
|
return sol::nullopt;
|
||||||
};
|
};
|
||||||
mwscriptVars[sol::meta_function::new_index] = [](MWScriptVariables& s, std::string_view var, double val) {
|
mwscriptVars[sol::meta_function::new_index] = [](MWScriptVariables& s, std::string_view var, double val) {
|
||||||
MWScript::Locals& locals = s.mRef.getLocals();
|
MWScript::Locals& locals = s.mRef.getLocals();
|
||||||
|
|
|
@ -613,7 +613,7 @@ namespace MWLua
|
||||||
MWBase::Environment::get().getWorldModel()->registerPtr(item);
|
MWBase::Environment::get().getWorldModel()->registerPtr(item);
|
||||||
list->push_back(getId(item));
|
list->push_back(getId(item));
|
||||||
}
|
}
|
||||||
return ObjectList<ObjectT>{ list };
|
return ObjectList<ObjectT>{ std::move(list) };
|
||||||
};
|
};
|
||||||
|
|
||||||
inventoryT["countOf"] = [](const InventoryT& inventory, std::string_view recordId) {
|
inventoryT["countOf"] = [](const InventoryT& inventory, std::string_view recordId) {
|
||||||
|
@ -661,7 +661,7 @@ namespace MWLua
|
||||||
list->push_back(getId(item));
|
list->push_back(getId(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ObjectList<ObjectT>{ list };
|
return ObjectList<ObjectT>{ std::move(list) };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
@ -99,9 +115,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(); };
|
||||||
|
|
|
@ -380,7 +380,7 @@ namespace MWLua
|
||||||
addProp(context, attributeStatT, "base", &MWMechanics::AttributeValue::getBase);
|
addProp(context, attributeStatT, "base", &MWMechanics::AttributeValue::getBase);
|
||||||
addProp(context, attributeStatT, "damage", &MWMechanics::AttributeValue::getDamage);
|
addProp(context, attributeStatT, "damage", &MWMechanics::AttributeValue::getDamage);
|
||||||
attributeStatT["modified"]
|
attributeStatT["modified"]
|
||||||
= sol::property([=](const AttributeStat& stat) { return stat.getModified(context); });
|
= sol::readonly_property([=](const AttributeStat& stat) { return stat.getModified(context); });
|
||||||
addProp(context, attributeStatT, "modifier", &MWMechanics::AttributeValue::getModifier);
|
addProp(context, attributeStatT, "modifier", &MWMechanics::AttributeValue::getModifier);
|
||||||
sol::table attributes(context.mLua->sol(), sol::create);
|
sol::table attributes(context.mLua->sol(), sol::create);
|
||||||
stats["attributes"] = LuaUtil::makeReadOnly(attributes);
|
stats["attributes"] = LuaUtil::makeReadOnly(attributes);
|
||||||
|
@ -399,7 +399,8 @@ namespace MWLua
|
||||||
auto skillStatT = context.mLua->sol().new_usertype<SkillStat>("SkillStat");
|
auto skillStatT = context.mLua->sol().new_usertype<SkillStat>("SkillStat");
|
||||||
addProp(context, skillStatT, "base", &MWMechanics::SkillValue::getBase);
|
addProp(context, skillStatT, "base", &MWMechanics::SkillValue::getBase);
|
||||||
addProp(context, skillStatT, "damage", &MWMechanics::SkillValue::getDamage);
|
addProp(context, skillStatT, "damage", &MWMechanics::SkillValue::getDamage);
|
||||||
skillStatT["modified"] = sol::property([=](const SkillStat& stat) { return stat.getModified(context); });
|
skillStatT["modified"]
|
||||||
|
= sol::readonly_property([=](const SkillStat& stat) { return stat.getModified(context); });
|
||||||
addProp(context, skillStatT, "modifier", &MWMechanics::SkillValue::getModifier);
|
addProp(context, skillStatT, "modifier", &MWMechanics::SkillValue::getModifier);
|
||||||
skillStatT["progress"] = sol::property([context](const SkillStat& stat) { return stat.getProgress(context); },
|
skillStatT["progress"] = sol::property([context](const SkillStat& stat) { return stat.getProgress(context); },
|
||||||
[context](const SkillStat& stat, const sol::object& value) { stat.cache(context, "progress", value); });
|
[context](const SkillStat& stat, const sol::object& value) { stat.cache(context, "progress", value); });
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -324,7 +324,7 @@ namespace MWMechanics
|
||||||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);
|
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);
|
||||||
if (animation && !reflectStatic->mModel.empty())
|
if (animation && !reflectStatic->mModel.empty())
|
||||||
animation->addEffect(Misc::ResourceHelpers::correctMeshPath(reflectStatic->mModel),
|
animation->addEffect(Misc::ResourceHelpers::correctMeshPath(reflectStatic->mModel),
|
||||||
ESM::MagicEffect::Reflect, false);
|
ESM::MagicEffect::indexToName(ESM::MagicEffect::Reflect), false);
|
||||||
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell(*reflected);
|
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell(*reflected);
|
||||||
}
|
}
|
||||||
if (removedSpell)
|
if (removedSpell)
|
||||||
|
|
|
@ -2019,6 +2019,24 @@ namespace MWMechanics
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Actors::playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, int loops, float speed,
|
||||||
|
std::string_view startKey, std::string_view stopKey, bool forceLoop)
|
||||||
|
{
|
||||||
|
const auto iter = mIndex.find(ptr.mRef);
|
||||||
|
if (iter != mIndex.end())
|
||||||
|
return iter->second->getCharacterController().playGroupLua(
|
||||||
|
groupName, speed, startKey, stopKey, loops, forceLoop);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actors::enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable)
|
||||||
|
{
|
||||||
|
const auto iter = mIndex.find(ptr.mRef);
|
||||||
|
if (iter != mIndex.end())
|
||||||
|
iter->second->getCharacterController().enableLuaAnimations(enable);
|
||||||
|
}
|
||||||
|
|
||||||
void Actors::skipAnimation(const MWWorld::Ptr& ptr) const
|
void Actors::skipAnimation(const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
const auto iter = mIndex.find(ptr.mRef);
|
const auto iter = mIndex.find(ptr.mRef);
|
||||||
|
@ -2034,12 +2052,27 @@ 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)
|
||||||
actor.getCharacterController().persistAnimationState();
|
actor.getCharacterController().persistAnimationState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Actors::clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted)
|
||||||
|
{
|
||||||
|
const auto iter = mIndex.find(ptr.mRef);
|
||||||
|
if (iter != mIndex.end())
|
||||||
|
iter->second->getCharacterController().clearAnimQueue(clearScripted);
|
||||||
|
}
|
||||||
|
|
||||||
void Actors::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out) const
|
void Actors::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out) const
|
||||||
{
|
{
|
||||||
for (const Actor& actor : mActors)
|
for (const Actor& actor : mActors)
|
||||||
|
|
|
@ -114,9 +114,14 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool playAnimationGroup(
|
bool playAnimationGroup(
|
||||||
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;
|
||||||
|
bool playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, int loops, float speed,
|
||||||
|
std::string_view startKey, std::string_view stopKey, bool forceLoop);
|
||||||
|
void enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable);
|
||||||
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 clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted);
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "../mwrender/animation.hpp"
|
#include "../mwrender/animation.hpp"
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/luamanager.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
@ -270,7 +271,7 @@ namespace
|
||||||
case CharState_IdleSwim:
|
case CharState_IdleSwim:
|
||||||
return Priority_SwimIdle;
|
return Priority_SwimIdle;
|
||||||
case CharState_IdleSneak:
|
case CharState_IdleSneak:
|
||||||
priority[MWRender::Animation::BoneGroup_LowerBody] = Priority_SneakIdleLowerBody;
|
priority[MWRender::BoneGroup_LowerBody] = Priority_SneakIdleLowerBody;
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
default:
|
default:
|
||||||
return priority;
|
return priority;
|
||||||
|
@ -444,8 +445,8 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
mHitState = CharState_Block;
|
mHitState = CharState_Block;
|
||||||
priority = Priority_Hit;
|
priority = Priority_Hit;
|
||||||
priority[MWRender::Animation::BoneGroup_LeftArm] = Priority_Block;
|
priority[MWRender::BoneGroup_LeftArm] = Priority_Block;
|
||||||
priority[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
|
priority[MWRender::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
|
||||||
startKey = "block start";
|
startKey = "block start";
|
||||||
stopKey = "block stop";
|
stopKey = "block stop";
|
||||||
}
|
}
|
||||||
|
@ -482,8 +483,7 @@ namespace MWMechanics
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimation->play(
|
playBlendedAnimation(mCurrentHit, priority, MWRender::BlendMask_All, true, 1, startKey, stopKey, 0.0f, ~0ul);
|
||||||
mCurrentHit, priority, MWRender::Animation::BlendMask_All, true, 1, startKey, stopKey, 0.0f, ~0ul);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::refreshJumpAnims(JumpingState jump, bool force)
|
void CharacterController::refreshJumpAnims(JumpingState jump, bool force)
|
||||||
|
@ -502,7 +502,7 @@ namespace MWMechanics
|
||||||
std::string_view weapShortGroup = getWeaponShortGroup(mWeaponType);
|
std::string_view weapShortGroup = getWeaponShortGroup(mWeaponType);
|
||||||
std::string jumpAnimName = "jump";
|
std::string jumpAnimName = "jump";
|
||||||
jumpAnimName += weapShortGroup;
|
jumpAnimName += weapShortGroup;
|
||||||
MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All;
|
MWRender::Animation::BlendMask jumpmask = MWRender::BlendMask_All;
|
||||||
if (!weapShortGroup.empty() && !mAnimation->hasAnimation(jumpAnimName))
|
if (!weapShortGroup.empty() && !mAnimation->hasAnimation(jumpAnimName))
|
||||||
jumpAnimName = fallbackShortWeaponGroup("jump", &jumpmask);
|
jumpAnimName = fallbackShortWeaponGroup("jump", &jumpmask);
|
||||||
|
|
||||||
|
@ -520,10 +520,10 @@ namespace MWMechanics
|
||||||
|
|
||||||
mCurrentJump = jumpAnimName;
|
mCurrentJump = jumpAnimName;
|
||||||
if (mJumpState == JumpState_InAir)
|
if (mJumpState == JumpState_InAir)
|
||||||
mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false, 1.0f, startAtLoop ? "loop start" : "start",
|
playBlendedAnimation(jumpAnimName, Priority_Jump, jumpmask, false, 1.0f,
|
||||||
"stop", 0.f, ~0ul);
|
startAtLoop ? "loop start" : "start", "stop", 0.f, ~0ul);
|
||||||
else if (mJumpState == JumpState_Landing)
|
else if (mJumpState == JumpState_Landing)
|
||||||
mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true, 1.0f, "loop stop", "stop", 0.0f, 0);
|
playBlendedAnimation(jumpAnimName, Priority_Jump, jumpmask, true, 1.0f, "loop stop", "stop", 0.0f, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::onOpen() const
|
bool CharacterController::onOpen() const
|
||||||
|
@ -539,8 +539,8 @@ namespace MWMechanics
|
||||||
if (mAnimation->isPlaying("containerclose"))
|
if (mAnimation->isPlaying("containerclose"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mAnimation->play("containeropen", Priority_Scripted, MWRender::Animation::BlendMask_All, false, 1.0f,
|
mAnimation->play(
|
||||||
"start", "stop", 0.f, 0);
|
"containeropen", Priority_Scripted, MWRender::BlendMask_All, false, 1.0f, "start", "stop", 0.f, 0);
|
||||||
if (mAnimation->isPlaying("containeropen"))
|
if (mAnimation->isPlaying("containeropen"))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -560,8 +560,8 @@ namespace MWMechanics
|
||||||
if (animPlaying)
|
if (animPlaying)
|
||||||
startPoint = 1.f - complete;
|
startPoint = 1.f - complete;
|
||||||
|
|
||||||
mAnimation->play("containerclose", Priority_Scripted, MWRender::Animation::BlendMask_All, false, 1.0f,
|
mAnimation->play("containerclose", Priority_Scripted, MWRender::BlendMask_All, false, 1.0f, "start", "stop",
|
||||||
"start", "stop", startPoint, 0);
|
startPoint, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,7 +600,7 @@ namespace MWMechanics
|
||||||
if (!isRealWeapon(mWeaponType))
|
if (!isRealWeapon(mWeaponType))
|
||||||
{
|
{
|
||||||
if (blendMask != nullptr)
|
if (blendMask != nullptr)
|
||||||
*blendMask = MWRender::Animation::BlendMask_LowerBody;
|
*blendMask = MWRender::BlendMask_LowerBody;
|
||||||
|
|
||||||
return baseGroupName;
|
return baseGroupName;
|
||||||
}
|
}
|
||||||
|
@ -619,13 +619,13 @@ namespace MWMechanics
|
||||||
|
|
||||||
// Special case for crossbows - we should apply 1h animations a fallback only for lower body
|
// Special case for crossbows - we should apply 1h animations a fallback only for lower body
|
||||||
if (mWeaponType == ESM::Weapon::MarksmanCrossbow && blendMask != nullptr)
|
if (mWeaponType == ESM::Weapon::MarksmanCrossbow && blendMask != nullptr)
|
||||||
*blendMask = MWRender::Animation::BlendMask_LowerBody;
|
*blendMask = MWRender::BlendMask_LowerBody;
|
||||||
|
|
||||||
if (!mAnimation->hasAnimation(groupName))
|
if (!mAnimation->hasAnimation(groupName))
|
||||||
{
|
{
|
||||||
groupName = baseGroupName;
|
groupName = baseGroupName;
|
||||||
if (blendMask != nullptr)
|
if (blendMask != nullptr)
|
||||||
*blendMask = MWRender::Animation::BlendMask_LowerBody;
|
*blendMask = MWRender::BlendMask_LowerBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupName;
|
return groupName;
|
||||||
|
@ -658,7 +658,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All;
|
MWRender::Animation::BlendMask movemask = MWRender::BlendMask_All;
|
||||||
|
|
||||||
std::string_view weapShortGroup = getWeaponShortGroup(mWeaponType);
|
std::string_view weapShortGroup = getWeaponShortGroup(mWeaponType);
|
||||||
|
|
||||||
|
@ -684,7 +684,7 @@ namespace MWMechanics
|
||||||
if (!mAnimation->hasAnimation(weapMovementAnimName))
|
if (!mAnimation->hasAnimation(weapMovementAnimName))
|
||||||
weapMovementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);
|
weapMovementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);
|
||||||
|
|
||||||
movementAnimName = weapMovementAnimName;
|
movementAnimName = std::move(weapMovementAnimName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mAnimation->hasAnimation(movementAnimName))
|
if (!mAnimation->hasAnimation(movementAnimName))
|
||||||
|
@ -749,7 +749,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimation->play(
|
playBlendedAnimation(
|
||||||
mCurrentMovement, Priority_Movement, movemask, false, 1.f, "start", "stop", startpoint, ~0ul, true);
|
mCurrentMovement, Priority_Movement, movemask, false, 1.f, "start", "stop", startpoint, ~0ul, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,7 +798,7 @@ namespace MWMechanics
|
||||||
weapIdleGroup += weapShortGroup;
|
weapIdleGroup += weapShortGroup;
|
||||||
if (!mAnimation->hasAnimation(weapIdleGroup))
|
if (!mAnimation->hasAnimation(weapIdleGroup))
|
||||||
weapIdleGroup = fallbackShortWeaponGroup(idleGroup);
|
weapIdleGroup = fallbackShortWeaponGroup(idleGroup);
|
||||||
idleGroup = weapIdleGroup;
|
idleGroup = std::move(weapIdleGroup);
|
||||||
|
|
||||||
// play until the Loop Stop key 2 to 5 times, then play until the Stop key
|
// play until the Loop Stop key 2 to 5 times, then play until the Stop key
|
||||||
// this replicates original engine behavior for the "Idle1h" 1st-person animation
|
// this replicates original engine behavior for the "Idle1h" 1st-person animation
|
||||||
|
@ -820,9 +820,9 @@ namespace MWMechanics
|
||||||
mAnimation->getInfo(mCurrentIdle, &startPoint);
|
mAnimation->getInfo(mCurrentIdle, &startPoint);
|
||||||
|
|
||||||
clearStateAnimation(mCurrentIdle);
|
clearStateAnimation(mCurrentIdle);
|
||||||
mCurrentIdle = idleGroup;
|
mCurrentIdle = std::move(idleGroup);
|
||||||
mAnimation->play(mCurrentIdle, priority, MWRender::Animation::BlendMask_All, false, 1.0f, "start", "stop",
|
playBlendedAnimation(
|
||||||
startPoint, numLoops, true);
|
mCurrentIdle, priority, MWRender::BlendMask_All, false, 1.0f, "start", "stop", startPoint, numLoops, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::refreshCurrentAnims(
|
void CharacterController::refreshCurrentAnims(
|
||||||
|
@ -855,8 +855,8 @@ namespace MWMechanics
|
||||||
resetCurrentIdleState();
|
resetCurrentIdleState();
|
||||||
resetCurrentJumpState();
|
resetCurrentJumpState();
|
||||||
|
|
||||||
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::BlendMask_All, false, 1.0f, "start",
|
playBlendedAnimation(
|
||||||
"stop", startpoint, 0);
|
mCurrentDeath, Priority_Death, MWRender::BlendMask_All, false, 1.0f, "start", "stop", startpoint, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterState CharacterController::chooseRandomDeathState() const
|
CharacterState CharacterController::chooseRandomDeathState() const
|
||||||
|
@ -998,6 +998,8 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
std::string_view evt = key->second;
|
std::string_view evt = key->second;
|
||||||
|
|
||||||
|
MWBase::Environment::get().getLuaManager()->animationTextKey(mPtr, key->second);
|
||||||
|
|
||||||
if (evt.substr(0, 7) == "sound: ")
|
if (evt.substr(0, 7) == "sound: ")
|
||||||
{
|
{
|
||||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
|
@ -1189,8 +1191,9 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
if (!animPlaying)
|
if (!animPlaying)
|
||||||
{
|
{
|
||||||
int mask = MWRender::Animation::BlendMask_Torso | MWRender::Animation::BlendMask_RightArm;
|
int mask = MWRender::BlendMask_Torso | MWRender::BlendMask_RightArm;
|
||||||
mAnimation->play("idlestorm", Priority_Storm, mask, true, 1.0f, "start", "stop", 0.0f, ~0ul, true);
|
playBlendedAnimation(
|
||||||
|
"idlestorm", Priority_Storm, mask, true, 1.0f, "start", "stop", 0.0f, ~0ul, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1247,41 +1250,6 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::isLoopingAnimation(std::string_view group) const
|
|
||||||
{
|
|
||||||
// In Morrowind, a some animation groups are always considered looping, regardless
|
|
||||||
// of loop start/stop keys.
|
|
||||||
// To be match vanilla behavior we probably only need to check this list, but we don't
|
|
||||||
// want to prevent modded animations with custom group names from looping either.
|
|
||||||
static const std::unordered_set<std::string_view> loopingAnimations = { "walkforward", "walkback", "walkleft",
|
|
||||||
"walkright", "swimwalkforward", "swimwalkback", "swimwalkleft", "swimwalkright", "runforward", "runback",
|
|
||||||
"runleft", "runright", "swimrunforward", "swimrunback", "swimrunleft", "swimrunright", "sneakforward",
|
|
||||||
"sneakback", "sneakleft", "sneakright", "turnleft", "turnright", "swimturnleft", "swimturnright",
|
|
||||||
"spellturnleft", "spellturnright", "torch", "idle", "idle2", "idle3", "idle4", "idle5", "idle6", "idle7",
|
|
||||||
"idle8", "idle9", "idlesneak", "idlestorm", "idleswim", "jump", "inventoryhandtohand",
|
|
||||||
"inventoryweapononehand", "inventoryweapontwohand", "inventoryweapontwowide" };
|
|
||||||
static const std::vector<std::string_view> shortGroups = getAllWeaponTypeShortGroups();
|
|
||||||
|
|
||||||
if (mAnimation && mAnimation->getTextKeyTime(std::string(group) + ": loop start") >= 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Most looping animations have variants for each weapon type shortgroup.
|
|
||||||
// Just remove the shortgroup instead of enumerating all of the possible animation groupnames.
|
|
||||||
// Make sure we pick the longest shortgroup so e.g. "bow" doesn't get picked over "crossbow"
|
|
||||||
// when the shortgroup is crossbow.
|
|
||||||
std::size_t suffixLength = 0;
|
|
||||||
for (std::string_view suffix : shortGroups)
|
|
||||||
{
|
|
||||||
if (suffix.length() > suffixLength && group.ends_with(suffix))
|
|
||||||
{
|
|
||||||
suffixLength = suffix.length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.remove_suffix(suffixLength);
|
|
||||||
|
|
||||||
return loopingAnimations.count(group) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CharacterController::updateWeaponState()
|
bool CharacterController::updateWeaponState()
|
||||||
{
|
{
|
||||||
// If the current animation is scripted, we can't do anything here.
|
// If the current animation is scripted, we can't do anything here.
|
||||||
|
@ -1357,8 +1325,8 @@ namespace MWMechanics
|
||||||
if (mAnimation->isPlaying("shield"))
|
if (mAnimation->isPlaying("shield"))
|
||||||
mAnimation->disable("shield");
|
mAnimation->disable("shield");
|
||||||
|
|
||||||
mAnimation->play("torch", Priority_Torch, MWRender::Animation::BlendMask_LeftArm, false, 1.0f, "start",
|
playBlendedAnimation("torch", Priority_Torch, MWRender::BlendMask_LeftArm, false, 1.0f, "start", "stop",
|
||||||
"stop", 0.0f, std::numeric_limits<size_t>::max(), true);
|
0.0f, std::numeric_limits<size_t>::max(), true);
|
||||||
}
|
}
|
||||||
else if (mAnimation->isPlaying("torch"))
|
else if (mAnimation->isPlaying("torch"))
|
||||||
{
|
{
|
||||||
|
@ -1369,7 +1337,7 @@ namespace MWMechanics
|
||||||
// For biped actors, blend weapon animations with lower body animations with higher priority
|
// For biped actors, blend weapon animations with lower body animations with higher priority
|
||||||
MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon);
|
MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon);
|
||||||
if (cls.isBipedal(mPtr))
|
if (cls.isBipedal(mPtr))
|
||||||
priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
|
priorityWeapon[MWRender::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
|
||||||
|
|
||||||
bool forcestateupdate = false;
|
bool forcestateupdate = false;
|
||||||
|
|
||||||
|
@ -1400,19 +1368,19 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
// Note: we do not disable unequipping animation automatically to avoid body desync
|
// Note: we do not disable unequipping animation automatically to avoid body desync
|
||||||
weapgroup = getWeaponAnimation(mWeaponType);
|
weapgroup = getWeaponAnimation(mWeaponType);
|
||||||
int unequipMask = MWRender::Animation::BlendMask_All;
|
int unequipMask = MWRender::BlendMask_All;
|
||||||
bool useShieldAnims = mAnimation->useShieldAnimations();
|
bool useShieldAnims = mAnimation->useShieldAnimations();
|
||||||
if (useShieldAnims && mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell
|
if (useShieldAnims && mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell
|
||||||
&& !(mWeaponType == ESM::Weapon::None && weaptype == ESM::Weapon::Spell))
|
&& !(mWeaponType == ESM::Weapon::None && weaptype == ESM::Weapon::Spell))
|
||||||
{
|
{
|
||||||
unequipMask = unequipMask | ~MWRender::Animation::BlendMask_LeftArm;
|
unequipMask = unequipMask | ~MWRender::BlendMask_LeftArm;
|
||||||
mAnimation->play("shield", Priority_Block, MWRender::Animation::BlendMask_LeftArm, true, 1.0f,
|
playBlendedAnimation("shield", Priority_Block, MWRender::BlendMask_LeftArm, true, 1.0f,
|
||||||
"unequip start", "unequip stop", 0.0f, 0);
|
"unequip start", "unequip stop", 0.0f, 0);
|
||||||
}
|
}
|
||||||
else if (mWeaponType == ESM::Weapon::HandToHand)
|
else if (mWeaponType == ESM::Weapon::HandToHand)
|
||||||
mAnimation->showCarriedLeft(false);
|
mAnimation->showCarriedLeft(false);
|
||||||
|
|
||||||
mAnimation->play(
|
playBlendedAnimation(
|
||||||
weapgroup, priorityWeapon, unequipMask, false, 1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
weapgroup, priorityWeapon, unequipMask, false, 1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||||
mUpperBodyState = UpperBodyState::Unequipping;
|
mUpperBodyState = UpperBodyState::Unequipping;
|
||||||
|
|
||||||
|
@ -1458,15 +1426,15 @@ namespace MWMechanics
|
||||||
if (weaptype != ESM::Weapon::None)
|
if (weaptype != ESM::Weapon::None)
|
||||||
{
|
{
|
||||||
mAnimation->showWeapons(false);
|
mAnimation->showWeapons(false);
|
||||||
int equipMask = MWRender::Animation::BlendMask_All;
|
int equipMask = MWRender::BlendMask_All;
|
||||||
if (useShieldAnims && weaptype != ESM::Weapon::Spell)
|
if (useShieldAnims && weaptype != ESM::Weapon::Spell)
|
||||||
{
|
{
|
||||||
equipMask = equipMask | ~MWRender::Animation::BlendMask_LeftArm;
|
equipMask = equipMask | ~MWRender::BlendMask_LeftArm;
|
||||||
mAnimation->play("shield", Priority_Block, MWRender::Animation::BlendMask_LeftArm, true,
|
playBlendedAnimation("shield", Priority_Block, MWRender::BlendMask_LeftArm, true, 1.0f,
|
||||||
1.0f, "equip start", "equip stop", 0.0f, 0);
|
"equip start", "equip stop", 0.0f, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimation->play(
|
playBlendedAnimation(
|
||||||
weapgroup, priorityWeapon, equipMask, true, 1.0f, "equip start", "equip stop", 0.0f, 0);
|
weapgroup, priorityWeapon, equipMask, true, 1.0f, "equip start", "equip stop", 0.0f, 0);
|
||||||
mUpperBodyState = UpperBodyState::Equipping;
|
mUpperBodyState = UpperBodyState::Equipping;
|
||||||
|
|
||||||
|
@ -1617,11 +1585,11 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (mAnimation->getNode("Bip01 L Hand"))
|
if (mAnimation->getNode("Bip01 L Hand"))
|
||||||
mAnimation->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel),
|
mAnimation->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel),
|
||||||
-1, false, "Bip01 L Hand", effect->mParticle);
|
"", false, "Bip01 L Hand", effect->mParticle);
|
||||||
|
|
||||||
if (mAnimation->getNode("Bip01 R Hand"))
|
if (mAnimation->getNode("Bip01 R Hand"))
|
||||||
mAnimation->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel),
|
mAnimation->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel),
|
||||||
-1, false, "Bip01 R Hand", effect->mParticle);
|
"", false, "Bip01 R Hand", effect->mParticle);
|
||||||
}
|
}
|
||||||
// first effect used for casting animation
|
// first effect used for casting animation
|
||||||
const ESM::ENAMstruct& firstEffect = effects->front();
|
const ESM::ENAMstruct& firstEffect = effects->front();
|
||||||
|
@ -1656,9 +1624,8 @@ namespace MWMechanics
|
||||||
startKey = mAttackType + " start";
|
startKey = mAttackType + " start";
|
||||||
stopKey = mAttackType + " stop";
|
stopKey = mAttackType + " stop";
|
||||||
}
|
}
|
||||||
|
playBlendedAnimation(mCurrentWeapon, priorityWeapon, MWRender::BlendMask_All, false, 1,
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false,
|
startKey, stopKey, 0.0f, 0);
|
||||||
1, startKey, stopKey, 0.0f, 0);
|
|
||||||
mUpperBodyState = UpperBodyState::Casting;
|
mUpperBodyState = UpperBodyState::Casting;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1709,8 +1676,8 @@ namespace MWMechanics
|
||||||
mAttackVictim = MWWorld::Ptr();
|
mAttackVictim = MWWorld::Ptr();
|
||||||
mAttackHitPos = osg::Vec3f();
|
mAttackHitPos = osg::Vec3f();
|
||||||
|
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false,
|
playBlendedAnimation(mCurrentWeapon, priorityWeapon, MWRender::BlendMask_All, false, weapSpeed,
|
||||||
weapSpeed, startKey, stopKey, 0.0f, 0);
|
startKey, stopKey, 0.0f, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1783,7 +1750,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimation->disable(mCurrentWeapon);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false, weapSpeed,
|
playBlendedAnimation(mCurrentWeapon, priorityWeapon, MWRender::BlendMask_All, false, weapSpeed,
|
||||||
mAttackType + " max attack", mAttackType + ' ' + hit, startPoint, 0);
|
mAttackType + " max attack", mAttackType + ' ' + hit, startPoint, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1813,7 +1780,7 @@ namespace MWMechanics
|
||||||
// Follow animations have lower priority than movement for non-biped creatures, logic be damned
|
// Follow animations have lower priority than movement for non-biped creatures, logic be damned
|
||||||
if (!cls.isBipedal(mPtr))
|
if (!cls.isBipedal(mPtr))
|
||||||
priorityFollow = Priority_Default;
|
priorityFollow = Priority_Default;
|
||||||
mAnimation->play(mCurrentWeapon, priorityFollow, MWRender::Animation::BlendMask_All, false, weapSpeed,
|
playBlendedAnimation(mCurrentWeapon, priorityFollow, MWRender::BlendMask_All, false, weapSpeed,
|
||||||
mAttackType + ' ' + start, mAttackType + ' ' + stop, 0.0f, 0);
|
mAttackType + ' ' + start, mAttackType + ' ' + stop, 0.0f, 0);
|
||||||
mUpperBodyState = UpperBodyState::AttackEnd;
|
mUpperBodyState = UpperBodyState::AttackEnd;
|
||||||
|
|
||||||
|
@ -1935,8 +1902,15 @@ namespace MWMechanics
|
||||||
mIdleState = CharState_SpecialIdle;
|
mIdleState = CharState_SpecialIdle;
|
||||||
auto priority = mAnimQueue.front().mScripted ? Priority_Scripted : Priority_Default;
|
auto priority = mAnimQueue.front().mScripted ? Priority_Scripted : Priority_Default;
|
||||||
mAnimation->setPlayScriptedOnly(mAnimQueue.front().mScripted);
|
mAnimation->setPlayScriptedOnly(mAnimQueue.front().mScripted);
|
||||||
mAnimation->play(mAnimQueue.front().mGroup, priority, MWRender::Animation::BlendMask_All, false, 1.0f,
|
if (mAnimQueue.front().mScripted)
|
||||||
(loopStart ? "loop start" : "start"), "stop", mAnimQueue.front().mTime, mAnimQueue.front().mLoopCount,
|
mAnimation->play(mAnimQueue.front().mGroup, priority, MWRender::BlendMask_All, false,
|
||||||
|
mAnimQueue.front().mSpeed, (loopStart ? "loop start" : mAnimQueue.front().mStartKey),
|
||||||
|
mAnimQueue.front().mStopKey, mAnimQueue.front().mTime, mAnimQueue.front().mLoopCount,
|
||||||
|
mAnimQueue.front().mLooping);
|
||||||
|
else
|
||||||
|
playBlendedAnimation(mAnimQueue.front().mGroup, priority, MWRender::BlendMask_All, false,
|
||||||
|
mAnimQueue.front().mSpeed, (loopStart ? "loop start" : mAnimQueue.front().mStartKey),
|
||||||
|
mAnimQueue.front().mStopKey, mAnimQueue.front().mTime, mAnimQueue.front().mLoopCount,
|
||||||
mAnimQueue.front().mLooping);
|
mAnimQueue.front().mLooping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2504,6 +2478,7 @@ namespace MWMechanics
|
||||||
state.mScriptedAnims.clear();
|
state.mScriptedAnims.clear();
|
||||||
for (AnimationQueue::const_iterator iter = mAnimQueue.begin(); iter != mAnimQueue.end(); ++iter)
|
for (AnimationQueue::const_iterator iter = mAnimQueue.begin(); iter != mAnimQueue.end(); ++iter)
|
||||||
{
|
{
|
||||||
|
// TODO: Probably want to presist lua animations too
|
||||||
if (!iter->mScripted)
|
if (!iter->mScripted)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -2541,8 +2516,10 @@ namespace MWMechanics
|
||||||
AnimationQueueEntry entry;
|
AnimationQueueEntry entry;
|
||||||
entry.mGroup = iter->mGroup;
|
entry.mGroup = iter->mGroup;
|
||||||
entry.mLoopCount = iter->mLoopCount;
|
entry.mLoopCount = iter->mLoopCount;
|
||||||
entry.mScripted = true;
|
entry.mLooping = mAnimation->isLoopingAnimation(entry.mGroup);
|
||||||
entry.mLooping = isLoopingAnimation(entry.mGroup);
|
entry.mStartKey = "start";
|
||||||
|
entry.mStopKey = "stop";
|
||||||
|
entry.mSpeed = 1.f;
|
||||||
entry.mTime = iter->mTime;
|
entry.mTime = iter->mTime;
|
||||||
if (iter->mAbsolute)
|
if (iter->mAbsolute)
|
||||||
{
|
{
|
||||||
|
@ -2559,6 +2536,18 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CharacterController::playBlendedAnimation(const std::string& groupname, const MWRender::AnimPriority& priority,
|
||||||
|
int blendMask, bool autodisable, float speedmult, std::string_view start, std::string_view stop,
|
||||||
|
float startpoint, size_t loops, bool loopfallback) const
|
||||||
|
{
|
||||||
|
if (mLuaAnimations)
|
||||||
|
MWBase::Environment::get().getLuaManager()->playAnimation(mPtr, groupname, priority, blendMask, autodisable,
|
||||||
|
speedmult, start, stop, startpoint, loops, loopfallback);
|
||||||
|
else
|
||||||
|
mAnimation->play(
|
||||||
|
groupname, priority, blendMask, autodisable, speedmult, start, stop, startpoint, loops, loopfallback);
|
||||||
|
}
|
||||||
|
|
||||||
bool CharacterController::playGroup(std::string_view groupname, int mode, int count, bool scripted)
|
bool CharacterController::playGroup(std::string_view groupname, int mode, int count, bool scripted)
|
||||||
{
|
{
|
||||||
if (!mAnimation || !mAnimation->hasAnimation(groupname))
|
if (!mAnimation || !mAnimation->hasAnimation(groupname))
|
||||||
|
@ -2568,7 +2557,7 @@ namespace MWMechanics
|
||||||
if (isScriptedAnimPlaying() && !scripted)
|
if (isScriptedAnimPlaying() && !scripted)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
bool looping = isLoopingAnimation(groupname);
|
bool looping = mAnimation->isLoopingAnimation(groupname);
|
||||||
|
|
||||||
// If this animation is a looped animation that is already playing
|
// If this animation is a looped animation that is already playing
|
||||||
// and has not yet reached the end of the loop, allow it to continue animating with its existing loop count
|
// and has not yet reached the end of the loop, allow it to continue animating with its existing loop count
|
||||||
|
@ -2602,8 +2591,12 @@ namespace MWMechanics
|
||||||
entry.mGroup = groupname;
|
entry.mGroup = groupname;
|
||||||
entry.mLoopCount = count;
|
entry.mLoopCount = count;
|
||||||
entry.mTime = 0.f;
|
entry.mTime = 0.f;
|
||||||
entry.mScripted = scripted;
|
// "PlayGroup idle" is a special case, used to remove to stop scripted animations playing
|
||||||
|
entry.mScripted = (scripted && groupname != "idle");
|
||||||
entry.mLooping = looping;
|
entry.mLooping = looping;
|
||||||
|
entry.mSpeed = 1.f;
|
||||||
|
entry.mStartKey = ((mode == 2) ? "loop start" : "start");
|
||||||
|
entry.mStopKey = "stop";
|
||||||
|
|
||||||
bool playImmediately = false;
|
bool playImmediately = false;
|
||||||
|
|
||||||
|
@ -2618,10 +2611,6 @@ namespace MWMechanics
|
||||||
mAnimQueue.resize(1);
|
mAnimQueue.resize(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// "PlayGroup idle" is a special case, used to stop and remove scripted animations playing
|
|
||||||
if (groupname == "idle")
|
|
||||||
entry.mScripted = false;
|
|
||||||
|
|
||||||
mAnimQueue.push_back(entry);
|
mAnimQueue.push_back(entry);
|
||||||
|
|
||||||
if (playImmediately)
|
if (playImmediately)
|
||||||
|
@ -2630,6 +2619,42 @@ namespace MWMechanics
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CharacterController::playGroupLua(std::string_view groupname, float speed, std::string_view startKey,
|
||||||
|
std::string_view stopKey, int loops, bool forceLoop)
|
||||||
|
{
|
||||||
|
// Note: In mwscript, "idle" is a special case used to clear the anim queue.
|
||||||
|
// In lua we offer an explicit clear method instead so this method does not treat "idle" special.
|
||||||
|
|
||||||
|
if (!mAnimation || !mAnimation->hasAnimation(groupname))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AnimationQueueEntry entry;
|
||||||
|
entry.mGroup = groupname;
|
||||||
|
// Note: MWScript gives one less loop to actors than non-actors.
|
||||||
|
// But this is the Lua version. We don't need to reproduce this weirdness here.
|
||||||
|
entry.mLoopCount = std::max(loops, 0);
|
||||||
|
entry.mStartKey = startKey;
|
||||||
|
entry.mStopKey = stopKey;
|
||||||
|
entry.mLooping = mAnimation->isLoopingAnimation(groupname) || forceLoop;
|
||||||
|
entry.mScripted = true;
|
||||||
|
entry.mSpeed = speed;
|
||||||
|
entry.mTime = 0;
|
||||||
|
|
||||||
|
if (mAnimQueue.size() > 1)
|
||||||
|
mAnimQueue.resize(1);
|
||||||
|
mAnimQueue.push_back(entry);
|
||||||
|
|
||||||
|
if (mAnimQueue.size() == 1)
|
||||||
|
playAnimQueue();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterController::enableLuaAnimations(bool enable)
|
||||||
|
{
|
||||||
|
mLuaAnimations = enable;
|
||||||
|
}
|
||||||
|
|
||||||
void CharacterController::skipAnim()
|
void CharacterController::skipAnim()
|
||||||
{
|
{
|
||||||
mSkipAnim = true;
|
mSkipAnim = true;
|
||||||
|
@ -2745,18 +2770,20 @@ namespace MWMechanics
|
||||||
// as it's extremely spread out (ActiveSpells, Spells, InventoryStore effects, etc...) so we do it here.
|
// as it's extremely spread out (ActiveSpells, Spells, InventoryStore effects, etc...) so we do it here.
|
||||||
|
|
||||||
// Stop any effects that are no longer active
|
// Stop any effects that are no longer active
|
||||||
std::vector<int> effects;
|
std::vector<std::string_view> effects = mAnimation->getLoopingEffects();
|
||||||
mAnimation->getLoopingEffects(effects);
|
|
||||||
|
|
||||||
for (int effectId : effects)
|
for (std::string_view effectId : effects)
|
||||||
{
|
{
|
||||||
if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished()
|
auto index = ESM::MagicEffect::indexNameToIndex(effectId);
|
||||||
|
|
||||||
|
if (index >= 0
|
||||||
|
&& (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished()
|
||||||
|| mPtr.getClass()
|
|| mPtr.getClass()
|
||||||
.getCreatureStats(mPtr)
|
.getCreatureStats(mPtr)
|
||||||
.getMagicEffects()
|
.getMagicEffects()
|
||||||
.getOrDefault(MWMechanics::EffectKey(effectId))
|
.getOrDefault(MWMechanics::EffectKey(index))
|
||||||
.getMagnitude()
|
.getMagnitude()
|
||||||
<= 0)
|
<= 0))
|
||||||
mAnimation->removeEffect(effectId);
|
mAnimation->removeEffect(effectId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,9 +138,13 @@ namespace MWMechanics
|
||||||
float mTime;
|
float mTime;
|
||||||
bool mLooping;
|
bool mLooping;
|
||||||
bool mScripted;
|
bool mScripted;
|
||||||
|
std::string mStartKey;
|
||||||
|
std::string mStopKey;
|
||||||
|
float mSpeed;
|
||||||
};
|
};
|
||||||
typedef std::deque<AnimationQueueEntry> AnimationQueue;
|
typedef std::deque<AnimationQueueEntry> AnimationQueue;
|
||||||
AnimationQueue mAnimQueue;
|
AnimationQueue mAnimQueue;
|
||||||
|
bool mLuaAnimations{ false };
|
||||||
|
|
||||||
CharacterState mIdleState{ CharState_None };
|
CharacterState mIdleState{ CharState_None };
|
||||||
std::string mCurrentIdle;
|
std::string mCurrentIdle;
|
||||||
|
@ -209,15 +213,12 @@ namespace MWMechanics
|
||||||
void refreshMovementAnims(CharacterState movement, bool force = false);
|
void refreshMovementAnims(CharacterState movement, bool force = false);
|
||||||
void refreshIdleAnims(CharacterState idle, bool force = false);
|
void refreshIdleAnims(CharacterState idle, bool force = false);
|
||||||
|
|
||||||
void clearAnimQueue(bool clearScriptedAnims = false);
|
|
||||||
|
|
||||||
bool updateWeaponState();
|
bool updateWeaponState();
|
||||||
void updateIdleStormState(bool inwater) const;
|
void updateIdleStormState(bool inwater) const;
|
||||||
|
|
||||||
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();
|
||||||
|
@ -248,8 +249,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
void prepareHit();
|
void prepareHit();
|
||||||
|
|
||||||
bool isLoopingAnimation(std::string_view group) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CharacterController(const MWWorld::Ptr& ptr, MWRender::Animation* anim);
|
CharacterController(const MWWorld::Ptr& ptr, MWRender::Animation* anim);
|
||||||
virtual ~CharacterController();
|
virtual ~CharacterController();
|
||||||
|
@ -275,9 +274,17 @@ namespace MWMechanics
|
||||||
void persistAnimationState() const;
|
void persistAnimationState() const;
|
||||||
void unpersistAnimationState();
|
void unpersistAnimationState();
|
||||||
|
|
||||||
|
void playBlendedAnimation(const std::string& groupname, const MWRender::AnimPriority& priority, int blendMask,
|
||||||
|
bool autodisable, float speedmult, std::string_view start, std::string_view stop, float startpoint,
|
||||||
|
size_t loops, bool loopfallback = false) const;
|
||||||
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);
|
||||||
|
bool playGroupLua(std::string_view groupname, float speed, std::string_view startKey, std::string_view stopKey,
|
||||||
|
int loops, bool forceLoop);
|
||||||
|
void enableLuaAnimations(bool enable);
|
||||||
void skipAnim();
|
void skipAnim();
|
||||||
bool isAnimPlaying(std::string_view groupName) const;
|
bool isAnimPlaying(std::string_view groupName) const;
|
||||||
|
bool isScriptedAnimPlaying() const;
|
||||||
|
void clearAnimQueue(bool clearScriptedAnims = false);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|
|
@ -756,6 +756,21 @@ namespace MWMechanics
|
||||||
else
|
else
|
||||||
return mObjects.playAnimationGroup(ptr, groupName, mode, number, scripted);
|
return mObjects.playAnimationGroup(ptr, groupName, mode, number, scripted);
|
||||||
}
|
}
|
||||||
|
bool MechanicsManager::playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, int loops,
|
||||||
|
float speed, std::string_view startKey, std::string_view stopKey, bool forceLoop)
|
||||||
|
{
|
||||||
|
if (ptr.getClass().isActor())
|
||||||
|
return mActors.playAnimationGroupLua(ptr, groupName, loops, speed, startKey, stopKey, forceLoop);
|
||||||
|
else
|
||||||
|
return mObjects.playAnimationGroupLua(ptr, groupName, loops, speed, startKey, stopKey, forceLoop);
|
||||||
|
}
|
||||||
|
void MechanicsManager::enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable)
|
||||||
|
{
|
||||||
|
if (ptr.getClass().isActor())
|
||||||
|
mActors.enableLuaAnimations(ptr, enable);
|
||||||
|
else
|
||||||
|
mObjects.enableLuaAnimations(ptr, enable);
|
||||||
|
}
|
||||||
void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr)
|
void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
if (ptr.getClass().isActor())
|
if (ptr.getClass().isActor())
|
||||||
|
@ -771,6 +786,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())
|
||||||
|
@ -791,6 +814,14 @@ namespace MWMechanics
|
||||||
mObjects.persistAnimationStates();
|
mObjects.persistAnimationStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MechanicsManager::clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted)
|
||||||
|
{
|
||||||
|
if (ptr.getClass().isActor())
|
||||||
|
mActors.clearAnimationQueue(ptr, clearScripted);
|
||||||
|
else
|
||||||
|
mObjects.clearAnimationQueue(ptr, clearScripted);
|
||||||
|
}
|
||||||
|
|
||||||
void MechanicsManager::updateMagicEffects(const MWWorld::Ptr& ptr)
|
void MechanicsManager::updateMagicEffects(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
mActors.updateMagicEffects(ptr);
|
mActors.updateMagicEffects(ptr);
|
||||||
|
|
|
@ -143,9 +143,14 @@ namespace MWMechanics
|
||||||
/// @return Success or error
|
/// @return Success or error
|
||||||
bool playAnimationGroup(
|
bool playAnimationGroup(
|
||||||
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;
|
||||||
|
bool playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, int loops, float speed,
|
||||||
|
std::string_view startKey, std::string_view stopKey, bool forceLoop) override;
|
||||||
|
void enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable) 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;
|
||||||
|
void clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted) 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
|
||||||
/// paused we may want to do it manually (after equipping permanent enchantment)
|
/// paused we may want to do it manually (after equipping permanent enchantment)
|
||||||
|
|
|
@ -113,6 +113,23 @@ namespace MWMechanics
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Objects::playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, int loops, float speed,
|
||||||
|
std::string_view startKey, std::string_view stopKey, bool forceLoop)
|
||||||
|
{
|
||||||
|
const auto iter = mIndex.find(ptr.mRef);
|
||||||
|
if (iter != mIndex.end())
|
||||||
|
return iter->second->playGroupLua(groupName, speed, startKey, stopKey, loops, forceLoop);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Objects::enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable)
|
||||||
|
{
|
||||||
|
const auto iter = mIndex.find(ptr.mRef);
|
||||||
|
if (iter != mIndex.end())
|
||||||
|
iter->second->enableLuaAnimations(enable);
|
||||||
|
}
|
||||||
|
|
||||||
void Objects::skipAnimation(const MWWorld::Ptr& ptr)
|
void Objects::skipAnimation(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
const auto iter = mIndex.find(ptr.mRef);
|
const auto iter = mIndex.find(ptr.mRef);
|
||||||
|
@ -126,6 +143,13 @@ namespace MWMechanics
|
||||||
object.persistAnimationState();
|
object.persistAnimationState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Objects::clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted)
|
||||||
|
{
|
||||||
|
const auto iter = mIndex.find(ptr.mRef);
|
||||||
|
if (iter != mIndex.end())
|
||||||
|
iter->second->clearAnimQueue(clearScripted);
|
||||||
|
}
|
||||||
|
|
||||||
void Objects::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out) const
|
void Objects::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out) const
|
||||||
{
|
{
|
||||||
for (const CharacterController& object : mObjects)
|
for (const CharacterController& object : mObjects)
|
||||||
|
|
|
@ -47,8 +47,12 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool playAnimationGroup(
|
bool playAnimationGroup(
|
||||||
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted = false);
|
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number, bool scripted = false);
|
||||||
|
bool playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, int loops, float speed,
|
||||||
|
std::string_view startKey, std::string_view stopKey, bool forceLoop);
|
||||||
|
void enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable);
|
||||||
void skipAnimation(const MWWorld::Ptr& ptr);
|
void skipAnimation(const MWWorld::Ptr& ptr);
|
||||||
void persistAnimationStates();
|
void persistAnimationStates();
|
||||||
|
void clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted);
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
|
|
@ -552,8 +552,8 @@ namespace MWMechanics
|
||||||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster);
|
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster);
|
||||||
if (animation)
|
if (animation)
|
||||||
{
|
{
|
||||||
animation->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel), effect->mIndex, false,
|
animation->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel),
|
||||||
{}, effect->mParticle);
|
ESM::MagicEffect::indexToName(effect->mIndex), false, {}, effect->mParticle);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -626,8 +626,8 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
// Don't play particle VFX unless the effect is new or it should be looping.
|
// Don't play particle VFX unless the effect is new or it should be looping.
|
||||||
if (playNonLooping || loop)
|
if (playNonLooping || loop)
|
||||||
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel), magicEffect.mIndex, loop,
|
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel),
|
||||||
{}, magicEffect.mParticle);
|
ESM::MagicEffect::indexToName(magicEffect.mIndex), loop, {}, magicEffect.mParticle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -285,8 +285,8 @@ namespace
|
||||||
const ESM::Static* absorbStatic = esmStore.get<ESM::Static>().find(ESM::RefId::stringRefId("VFX_Absorb"));
|
const ESM::Static* absorbStatic = esmStore.get<ESM::Static>().find(ESM::RefId::stringRefId("VFX_Absorb"));
|
||||||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);
|
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);
|
||||||
if (animation && !absorbStatic->mModel.empty())
|
if (animation && !absorbStatic->mModel.empty())
|
||||||
animation->addEffect(
|
animation->addEffect(Misc::ResourceHelpers::correctMeshPath(absorbStatic->mModel),
|
||||||
Misc::ResourceHelpers::correctMeshPath(absorbStatic->mModel), ESM::MagicEffect::SpellAbsorption, false);
|
ESM::MagicEffect::indexToName(ESM::MagicEffect::SpellAbsorption), false);
|
||||||
const ESM::Spell* spell = esmStore.get<ESM::Spell>().search(spellId);
|
const ESM::Spell* spell = esmStore.get<ESM::Spell>().search(spellId);
|
||||||
int spellCost = 0;
|
int spellCost = 0;
|
||||||
if (spell)
|
if (spell)
|
||||||
|
@ -455,11 +455,11 @@ namespace MWMechanics
|
||||||
if (!caster.isEmpty())
|
if (!caster.isEmpty())
|
||||||
{
|
{
|
||||||
MWRender::Animation* anim = world->getAnimation(caster);
|
MWRender::Animation* anim = world->getAnimation(caster);
|
||||||
anim->removeEffect(effect.mEffectId);
|
anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId));
|
||||||
const ESM::Static* fx
|
const ESM::Static* fx
|
||||||
= world->getStore().get<ESM::Static>().search(ESM::RefId::stringRefId("VFX_Summon_end"));
|
= world->getStore().get<ESM::Static>().search(ESM::RefId::stringRefId("VFX_Summon_end"));
|
||||||
if (fx)
|
if (fx)
|
||||||
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(fx->mModel), -1);
|
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(fx->mModel), "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (caster == getPlayer())
|
else if (caster == getPlayer())
|
||||||
|
@ -490,7 +490,7 @@ namespace MWMechanics
|
||||||
if (!caster.isEmpty())
|
if (!caster.isEmpty())
|
||||||
{
|
{
|
||||||
MWRender::Animation* anim = world->getAnimation(caster);
|
MWRender::Animation* anim = world->getAnimation(caster);
|
||||||
anim->removeEffect(effect.mEffectId);
|
anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1045,7 +1045,7 @@ namespace MWMechanics
|
||||||
effect.mFlags |= ESM::ActiveEffect::Flag_Remove;
|
effect.mFlags |= ESM::ActiveEffect::Flag_Remove;
|
||||||
auto anim = world->getAnimation(target);
|
auto anim = world->getAnimation(target);
|
||||||
if (anim)
|
if (anim)
|
||||||
anim->removeEffect(effect.mEffectId);
|
anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove;
|
effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove;
|
||||||
|
@ -1287,7 +1287,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
auto anim = MWBase::Environment::get().getWorld()->getAnimation(target);
|
auto anim = MWBase::Environment::get().getWorld()->getAnimation(target);
|
||||||
if (anim)
|
if (anim)
|
||||||
anim->removeEffect(effect.mEffectId);
|
anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ namespace MWMechanics
|
||||||
const ESM::Static* fx
|
const ESM::Static* fx
|
||||||
= world->getStore().get<ESM::Static>().search(ESM::RefId::stringRefId("VFX_Summon_Start"));
|
= world->getStore().get<ESM::Static>().search(ESM::RefId::stringRefId("VFX_Summon_Start"));
|
||||||
if (fx)
|
if (fx)
|
||||||
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(fx->mModel), -1, false);
|
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(fx->mModel), "", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
|
|
|
@ -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>
|
||||||
|
@ -45,6 +46,7 @@
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/luamanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
@ -52,6 +54,7 @@
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/character.hpp" // FIXME: for MWMechanics::Priority
|
#include "../mwmechanics/character.hpp" // FIXME: for MWMechanics::Priority
|
||||||
|
#include "../mwmechanics/weapontype.hpp"
|
||||||
|
|
||||||
#include "actorutil.hpp"
|
#include "actorutil.hpp"
|
||||||
#include "rotatecontroller.hpp"
|
#include "rotatecontroller.hpp"
|
||||||
|
@ -300,11 +303,10 @@ namespace
|
||||||
RemoveCallbackVisitor()
|
RemoveCallbackVisitor()
|
||||||
: RemoveVisitor()
|
: RemoveVisitor()
|
||||||
, mHasMagicEffects(false)
|
, mHasMagicEffects(false)
|
||||||
, mEffectId(-1)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveCallbackVisitor(int effectId)
|
RemoveCallbackVisitor(std::string_view effectId)
|
||||||
: RemoveVisitor()
|
: RemoveVisitor()
|
||||||
, mHasMagicEffects(false)
|
, mHasMagicEffects(false)
|
||||||
, mEffectId(effectId)
|
, mEffectId(effectId)
|
||||||
|
@ -323,7 +325,7 @@ namespace
|
||||||
MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast<MWRender::UpdateVfxCallback*>(callback);
|
MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast<MWRender::UpdateVfxCallback*>(callback);
|
||||||
if (vfxCallback)
|
if (vfxCallback)
|
||||||
{
|
{
|
||||||
bool toRemove = mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId;
|
bool toRemove = mEffectId == "" || vfxCallback->mParams.mEffectId == mEffectId;
|
||||||
if (toRemove)
|
if (toRemove)
|
||||||
mToRemove.emplace_back(group.asNode(), group.getParent(0));
|
mToRemove.emplace_back(group.asNode(), group.getParent(0));
|
||||||
else
|
else
|
||||||
|
@ -337,7 +339,7 @@ namespace
|
||||||
void apply(osg::Geometry&) override {}
|
void apply(osg::Geometry&) override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mEffectId;
|
std::string_view mEffectId;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FindVfxCallbacksVisitor : public osg::NodeVisitor
|
class FindVfxCallbacksVisitor : public osg::NodeVisitor
|
||||||
|
@ -347,11 +349,10 @@ namespace
|
||||||
|
|
||||||
FindVfxCallbacksVisitor()
|
FindVfxCallbacksVisitor()
|
||||||
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||||
, mEffectId(-1)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
FindVfxCallbacksVisitor(int effectId)
|
FindVfxCallbacksVisitor(std::string_view effectId)
|
||||||
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||||
, mEffectId(effectId)
|
, mEffectId(effectId)
|
||||||
{
|
{
|
||||||
|
@ -367,7 +368,7 @@ namespace
|
||||||
MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast<MWRender::UpdateVfxCallback*>(callback);
|
MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast<MWRender::UpdateVfxCallback*>(callback);
|
||||||
if (vfxCallback)
|
if (vfxCallback)
|
||||||
{
|
{
|
||||||
if (mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId)
|
if (mEffectId == "" || vfxCallback->mParams.mEffectId == mEffectId)
|
||||||
{
|
{
|
||||||
mCallbacks.push_back(vfxCallback);
|
mCallbacks.push_back(vfxCallback);
|
||||||
}
|
}
|
||||||
|
@ -381,7 +382,7 @@ namespace
|
||||||
void apply(osg::Geometry&) override {}
|
void apply(osg::Geometry&) override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mEffectId;
|
std::string_view mEffectId;
|
||||||
};
|
};
|
||||||
|
|
||||||
osg::ref_ptr<osg::LightModel> getVFXLightModelInstance()
|
osg::ref_ptr<osg::LightModel> getVFXLightModelInstance()
|
||||||
|
@ -446,7 +447,7 @@ namespace MWRender
|
||||||
|
|
||||||
typedef std::map<std::string, osg::ref_ptr<SceneUtil::KeyframeController>> ControllerMap;
|
typedef std::map<std::string, osg::ref_ptr<SceneUtil::KeyframeController>> ControllerMap;
|
||||||
|
|
||||||
ControllerMap mControllerMap[Animation::sNumBlendMasks];
|
ControllerMap mControllerMap[sNumBlendMasks];
|
||||||
|
|
||||||
const SceneUtil::TextKeyMap& getTextKeys() const;
|
const SceneUtil::TextKeyMap& getTextKeys() const;
|
||||||
};
|
};
|
||||||
|
@ -714,6 +715,41 @@ namespace MWRender
|
||||||
return mSupportedAnimations.find(anim) != mSupportedAnimations.end();
|
return mSupportedAnimations.find(anim) != mSupportedAnimations.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Animation::isLoopingAnimation(std::string_view group) const
|
||||||
|
{
|
||||||
|
// In Morrowind, a some animation groups are always considered looping, regardless
|
||||||
|
// of loop start/stop keys.
|
||||||
|
// To be match vanilla behavior we probably only need to check this list, but we don't
|
||||||
|
// want to prevent modded animations with custom group names from looping either.
|
||||||
|
static const std::unordered_set<std::string_view> loopingAnimations = { "walkforward", "walkback", "walkleft",
|
||||||
|
"walkright", "swimwalkforward", "swimwalkback", "swimwalkleft", "swimwalkright", "runforward", "runback",
|
||||||
|
"runleft", "runright", "swimrunforward", "swimrunback", "swimrunleft", "swimrunright", "sneakforward",
|
||||||
|
"sneakback", "sneakleft", "sneakright", "turnleft", "turnright", "swimturnleft", "swimturnright",
|
||||||
|
"spellturnleft", "spellturnright", "torch", "idle", "idle2", "idle3", "idle4", "idle5", "idle6", "idle7",
|
||||||
|
"idle8", "idle9", "idlesneak", "idlestorm", "idleswim", "jump", "inventoryhandtohand",
|
||||||
|
"inventoryweapononehand", "inventoryweapontwohand", "inventoryweapontwowide" };
|
||||||
|
static const std::vector<std::string_view> shortGroups = MWMechanics::getAllWeaponTypeShortGroups();
|
||||||
|
|
||||||
|
if (getTextKeyTime(std::string(group) + ": loop start") >= 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Most looping animations have variants for each weapon type shortgroup.
|
||||||
|
// Just remove the shortgroup instead of enumerating all of the possible animation groupnames.
|
||||||
|
// Make sure we pick the longest shortgroup so e.g. "bow" doesn't get picked over "crossbow"
|
||||||
|
// when the shortgroup is crossbow.
|
||||||
|
std::size_t suffixLength = 0;
|
||||||
|
for (std::string_view suffix : shortGroups)
|
||||||
|
{
|
||||||
|
if (suffix.length() > suffixLength && group.ends_with(suffix))
|
||||||
|
{
|
||||||
|
suffixLength = suffix.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.remove_suffix(suffixLength);
|
||||||
|
|
||||||
|
return loopingAnimations.count(group) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
float Animation::getStartTime(const std::string& groupname) const
|
float Animation::getStartTime(const std::string& groupname) const
|
||||||
{
|
{
|
||||||
for (AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)
|
for (AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)
|
||||||
|
@ -757,10 +793,9 @@ namespace MWRender
|
||||||
state.mLoopStopTime = key->first;
|
state.mLoopStopTime = key->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mTextKeyListener)
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (mTextKeyListener != nullptr)
|
||||||
mTextKeyListener->handleTextKey(groupname, key, map);
|
mTextKeyListener->handleTextKey(groupname, key, map);
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
|
@ -768,7 +803,6 @@ namespace MWRender
|
||||||
Log(Debug::Error) << "Error handling text key " << evt << ": " << e.what();
|
Log(Debug::Error) << "Error handling text key " << evt << ": " << e.what();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void Animation::play(std::string_view groupname, const AnimPriority& priority, int blendMask, bool autodisable,
|
void Animation::play(std::string_view groupname, const AnimPriority& priority, int blendMask, bool autodisable,
|
||||||
float speedmult, std::string_view start, std::string_view stop, float startpoint, size_t loops,
|
float speedmult, std::string_view start, std::string_view stop, float startpoint, size_t loops,
|
||||||
|
@ -922,7 +956,7 @@ namespace MWRender
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::setTextKeyListener(Animation::TextKeyListener* listener)
|
void Animation::setTextKeyListener(TextKeyListener* listener)
|
||||||
{
|
{
|
||||||
mTextKeyListener = listener;
|
mTextKeyListener = listener;
|
||||||
}
|
}
|
||||||
|
@ -1051,7 +1085,16 @@ namespace MWRender
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Animation::getCurrentTime(const std::string& groupname) const
|
std::string_view Animation::getActiveGroup(BoneGroup boneGroup) const
|
||||||
|
{
|
||||||
|
if (auto timePtr = mAnimationTimePtr[boneGroup]->getTimePtr())
|
||||||
|
for (auto& state : mStates)
|
||||||
|
if (state.second.mTime == timePtr)
|
||||||
|
return state.first;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
float Animation::getCurrentTime(std::string_view groupname) const
|
||||||
{
|
{
|
||||||
AnimStateMap::const_iterator iter = mStates.find(groupname);
|
AnimStateMap::const_iterator iter = mStates.find(groupname);
|
||||||
if (iter == mStates.end())
|
if (iter == mStates.end())
|
||||||
|
@ -1495,8 +1538,8 @@ namespace MWRender
|
||||||
mExtraLightSource->setActorFade(mAlpha);
|
mExtraLightSource->setActorFade(mAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::addEffect(
|
void Animation::addEffect(std::string_view model, std::string_view effectId, bool loop, std::string_view bonename,
|
||||||
const std::string& model, int effectId, bool loop, std::string_view bonename, std::string_view texture)
|
std::string_view texture)
|
||||||
{
|
{
|
||||||
if (!mObjectRoot.get())
|
if (!mObjectRoot.get())
|
||||||
return;
|
return;
|
||||||
|
@ -1578,7 +1621,7 @@ namespace MWRender
|
||||||
overrideFirstRootTexture(texture, mResourceSystem, *node);
|
overrideFirstRootTexture(texture, mResourceSystem, *node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::removeEffect(int effectId)
|
void Animation::removeEffect(std::string_view effectId)
|
||||||
{
|
{
|
||||||
RemoveCallbackVisitor visitor(effectId);
|
RemoveCallbackVisitor visitor(effectId);
|
||||||
mInsert->accept(visitor);
|
mInsert->accept(visitor);
|
||||||
|
@ -1588,17 +1631,19 @@ namespace MWRender
|
||||||
|
|
||||||
void Animation::removeEffects()
|
void Animation::removeEffects()
|
||||||
{
|
{
|
||||||
removeEffect(-1);
|
removeEffect("");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::getLoopingEffects(std::vector<int>& out) const
|
std::vector<std::string_view> Animation::getLoopingEffects() const
|
||||||
{
|
{
|
||||||
if (!mHasMagicEffects)
|
if (!mHasMagicEffects)
|
||||||
return;
|
return {};
|
||||||
|
|
||||||
FindVfxCallbacksVisitor visitor;
|
FindVfxCallbacksVisitor visitor;
|
||||||
mInsert->accept(visitor);
|
mInsert->accept(visitor);
|
||||||
|
|
||||||
|
std::vector<std::string_view> out;
|
||||||
|
|
||||||
for (std::vector<UpdateVfxCallback*>::iterator it = visitor.mCallbacks.begin(); it != visitor.mCallbacks.end();
|
for (std::vector<UpdateVfxCallback*>::iterator it = visitor.mCallbacks.begin(); it != visitor.mCallbacks.end();
|
||||||
++it)
|
++it)
|
||||||
{
|
{
|
||||||
|
@ -1607,6 +1652,7 @@ namespace MWRender
|
||||||
if (callback->mParams.mLoop && !callback->mFinished)
|
if (callback->mParams.mLoop && !callback->mFinished)
|
||||||
out.push_back(callback->mParams.mEffectId);
|
out.push_back(callback->mParams.mEffectId);
|
||||||
}
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::updateEffects()
|
void Animation::updateEffects()
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
#ifndef GAME_RENDER_ANIMATION_H
|
#ifndef GAME_RENDER_ANIMATION_H
|
||||||
#define GAME_RENDER_ANIMATION_H
|
#define GAME_RENDER_ANIMATION_H
|
||||||
|
|
||||||
|
#include "animationpriority.hpp"
|
||||||
|
#include "blendmask.hpp"
|
||||||
|
#include "bonegroup.hpp"
|
||||||
|
|
||||||
#include "../mwworld/movementdirection.hpp"
|
#include "../mwworld/movementdirection.hpp"
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
@ -84,7 +88,7 @@ namespace MWRender
|
||||||
std::string mModelName; // Just here so we don't add the same effect twice
|
std::string mModelName; // Just here so we don't add the same effect twice
|
||||||
std::shared_ptr<EffectAnimationTime> mAnimTime;
|
std::shared_ptr<EffectAnimationTime> mAnimTime;
|
||||||
float mMaxControllerLength;
|
float mMaxControllerLength;
|
||||||
int mEffectId;
|
std::string mEffectId;
|
||||||
bool mLoop;
|
bool mLoop;
|
||||||
std::string mBoneName;
|
std::string mBoneName;
|
||||||
};
|
};
|
||||||
|
@ -92,60 +96,9 @@ namespace MWRender
|
||||||
class Animation : public osg::Referenced
|
class Animation : public osg::Referenced
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum BoneGroup
|
using BlendMask = MWRender::BlendMask;
|
||||||
{
|
using BoneGroup = MWRender::BoneGroup;
|
||||||
BoneGroup_LowerBody = 0,
|
using AnimPriority = MWRender::AnimPriority;
|
||||||
BoneGroup_Torso,
|
|
||||||
BoneGroup_LeftArm,
|
|
||||||
BoneGroup_RightArm
|
|
||||||
};
|
|
||||||
|
|
||||||
enum BlendMask
|
|
||||||
{
|
|
||||||
BlendMask_LowerBody = 1 << 0,
|
|
||||||
BlendMask_Torso = 1 << 1,
|
|
||||||
BlendMask_LeftArm = 1 << 2,
|
|
||||||
BlendMask_RightArm = 1 << 3,
|
|
||||||
|
|
||||||
BlendMask_UpperBody = BlendMask_Torso | BlendMask_LeftArm | BlendMask_RightArm,
|
|
||||||
|
|
||||||
BlendMask_All = BlendMask_LowerBody | BlendMask_UpperBody
|
|
||||||
};
|
|
||||||
/* This is the number of *discrete* blend masks. */
|
|
||||||
static constexpr size_t sNumBlendMasks = 4;
|
|
||||||
|
|
||||||
/// Holds an animation priority value for each BoneGroup.
|
|
||||||
struct AnimPriority
|
|
||||||
{
|
|
||||||
/// Convenience constructor, initialises all priorities to the same value.
|
|
||||||
AnimPriority(int priority)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < sNumBlendMasks; ++i)
|
|
||||||
mPriority[i] = priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const AnimPriority& other) const
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < sNumBlendMasks; ++i)
|
|
||||||
if (other.mPriority[i] != mPriority[i])
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int& operator[](BoneGroup n) { return mPriority[n]; }
|
|
||||||
|
|
||||||
const int& operator[](BoneGroup n) const { return mPriority[n]; }
|
|
||||||
|
|
||||||
bool contains(int priority) const
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < sNumBlendMasks; ++i)
|
|
||||||
if (priority == mPriority[i])
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mPriority[sNumBlendMasks];
|
|
||||||
};
|
|
||||||
|
|
||||||
class TextKeyListener
|
class TextKeyListener
|
||||||
{
|
{
|
||||||
|
@ -384,11 +337,11 @@ namespace MWRender
|
||||||
* @param texture override the texture specified in the model's materials - if empty, do not override
|
* @param texture override the texture specified in the model's materials - if empty, do not override
|
||||||
* @note Will not add an effect twice.
|
* @note Will not add an effect twice.
|
||||||
*/
|
*/
|
||||||
void addEffect(const std::string& model, int effectId, bool loop = false, std::string_view bonename = {},
|
void addEffect(std::string_view model, std::string_view effectId, bool loop = false,
|
||||||
std::string_view texture = {});
|
std::string_view bonename = {}, std::string_view texture = {});
|
||||||
void removeEffect(int effectId);
|
void removeEffect(std::string_view effectId);
|
||||||
void removeEffects();
|
void removeEffects();
|
||||||
void getLoopingEffects(std::vector<int>& out) const;
|
std::vector<std::string_view> getLoopingEffects() const;
|
||||||
|
|
||||||
// Add a spell casting glow to an object. From measuring video taken from the original engine,
|
// Add a spell casting glow to an object. From measuring video taken from the original engine,
|
||||||
// the glow seems to be about 1.5 seconds except for telekinesis, which is 1 second.
|
// the glow seems to be about 1.5 seconds except for telekinesis, which is 1 second.
|
||||||
|
@ -398,6 +351,8 @@ namespace MWRender
|
||||||
|
|
||||||
bool hasAnimation(std::string_view anim) const;
|
bool hasAnimation(std::string_view anim) const;
|
||||||
|
|
||||||
|
bool isLoopingAnimation(std::string_view group) const;
|
||||||
|
|
||||||
// Specifies the axis' to accumulate on. Non-accumulated axis will just
|
// Specifies the axis' to accumulate on. Non-accumulated axis will just
|
||||||
// move visually, but not affect the actual movement. Each x/y/z value
|
// move visually, but not affect the actual movement. Each x/y/z value
|
||||||
// should be on the scale of 0 to 1.
|
// should be on the scale of 0 to 1.
|
||||||
|
@ -446,6 +401,9 @@ namespace MWRender
|
||||||
bool getInfo(std::string_view groupname, float* complete = nullptr, float* speedmult = nullptr,
|
bool getInfo(std::string_view groupname, float* complete = nullptr, float* speedmult = nullptr,
|
||||||
size_t* loopcount = nullptr) const;
|
size_t* loopcount = nullptr) const;
|
||||||
|
|
||||||
|
/// Returns the group name of the animation currently active on that bone group.
|
||||||
|
std::string_view getActiveGroup(BoneGroup boneGroup) const;
|
||||||
|
|
||||||
/// Get the absolute position in the animation track of the first text key with the given group.
|
/// Get the absolute position in the animation track of the first text key with the given group.
|
||||||
float getStartTime(const std::string& groupname) const;
|
float getStartTime(const std::string& groupname) const;
|
||||||
|
|
||||||
|
@ -454,7 +412,7 @@ namespace MWRender
|
||||||
|
|
||||||
/// Get the current absolute position in the animation track for the animation that is currently playing from
|
/// Get the current absolute position in the animation track for the animation that is currently playing from
|
||||||
/// the given group.
|
/// the given group.
|
||||||
float getCurrentTime(const std::string& groupname) const;
|
float getCurrentTime(std::string_view groupname) const;
|
||||||
|
|
||||||
/** Disables the specified animation group;
|
/** Disables the specified animation group;
|
||||||
* \param groupname Animation group to disable.
|
* \param groupname Animation group to disable.
|
||||||
|
|
42
apps/openmw/mwrender/animationpriority.hpp
Normal file
42
apps/openmw/mwrender/animationpriority.hpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef GAME_RENDER_ANIMATIONPRIORITY_H
|
||||||
|
#define GAME_RENDER_ANIMATIONPRIORITY_H
|
||||||
|
|
||||||
|
#include "blendmask.hpp"
|
||||||
|
#include "bonegroup.hpp"
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
/// Holds an animation priority value for each BoneGroup.
|
||||||
|
struct AnimPriority
|
||||||
|
{
|
||||||
|
/// Convenience constructor, initialises all priorities to the same value.
|
||||||
|
AnimPriority(int priority)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < sNumBlendMasks; ++i)
|
||||||
|
mPriority[i] = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const AnimPriority& other) const
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < sNumBlendMasks; ++i)
|
||||||
|
if (other.mPriority[i] != mPriority[i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int& operator[](BoneGroup n) { return mPriority[n]; }
|
||||||
|
|
||||||
|
const int& operator[](BoneGroup n) const { return mPriority[n]; }
|
||||||
|
|
||||||
|
bool contains(int priority) const
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < sNumBlendMasks; ++i)
|
||||||
|
if (priority == mPriority[i])
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mPriority[sNumBlendMasks];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
22
apps/openmw/mwrender/blendmask.hpp
Normal file
22
apps/openmw/mwrender/blendmask.hpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef GAME_RENDER_BLENDMASK_H
|
||||||
|
#define GAME_RENDER_BLENDMASK_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
enum BlendMask
|
||||||
|
{
|
||||||
|
BlendMask_LowerBody = 1 << 0,
|
||||||
|
BlendMask_Torso = 1 << 1,
|
||||||
|
BlendMask_LeftArm = 1 << 2,
|
||||||
|
BlendMask_RightArm = 1 << 3,
|
||||||
|
|
||||||
|
BlendMask_UpperBody = BlendMask_Torso | BlendMask_LeftArm | BlendMask_RightArm,
|
||||||
|
|
||||||
|
BlendMask_All = BlendMask_LowerBody | BlendMask_UpperBody
|
||||||
|
};
|
||||||
|
/* This is the number of *discrete* blend masks. */
|
||||||
|
static constexpr size_t sNumBlendMasks = 4;
|
||||||
|
}
|
||||||
|
#endif
|
16
apps/openmw/mwrender/bonegroup.hpp
Normal file
16
apps/openmw/mwrender/bonegroup.hpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef GAME_RENDER_BONEGROUP_H
|
||||||
|
#define GAME_RENDER_BONEGROUP_H
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
enum BoneGroup
|
||||||
|
{
|
||||||
|
BoneGroup_LowerBody = 0,
|
||||||
|
BoneGroup_Torso,
|
||||||
|
BoneGroup_LeftArm,
|
||||||
|
BoneGroup_RightArm,
|
||||||
|
|
||||||
|
Num_BoneGroups
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -436,7 +436,7 @@ namespace MWRender
|
||||||
|
|
||||||
// We still should use one-handed animation as fallback
|
// We still should use one-handed animation as fallback
|
||||||
if (mAnimation->hasAnimation(inventoryGroup))
|
if (mAnimation->hasAnimation(inventoryGroup))
|
||||||
groupname = inventoryGroup;
|
groupname = std::move(inventoryGroup);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static const std::string oneHandFallback
|
static const std::string oneHandFallback
|
||||||
|
@ -456,15 +456,15 @@ namespace MWRender
|
||||||
|
|
||||||
mAnimation->showCarriedLeft(showCarriedLeft);
|
mAnimation->showCarriedLeft(showCarriedLeft);
|
||||||
|
|
||||||
mCurrentAnimGroup = groupname;
|
mCurrentAnimGroup = std::move(groupname);
|
||||||
mAnimation->play(mCurrentAnimGroup, 1, Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0);
|
mAnimation->play(mCurrentAnimGroup, 1, BlendMask::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0);
|
||||||
|
|
||||||
MWWorld::ConstContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
MWWorld::ConstContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
||||||
if (torch != inv.end() && torch->getType() == ESM::Light::sRecordId && showCarriedLeft)
|
if (torch != inv.end() && torch->getType() == ESM::Light::sRecordId && showCarriedLeft)
|
||||||
{
|
{
|
||||||
if (!mAnimation->getInfo("torch"))
|
if (!mAnimation->getInfo("torch"))
|
||||||
mAnimation->play(
|
mAnimation->play(
|
||||||
"torch", 2, Animation::BlendMask_LeftArm, false, 1.0f, "start", "stop", 0.0f, ~0ul, true);
|
"torch", 2, BlendMask::BlendMask_LeftArm, false, 1.0f, "start", "stop", 0.0f, ~0ul, true);
|
||||||
}
|
}
|
||||||
else if (mAnimation->getInfo("torch"))
|
else if (mAnimation->getInfo("torch"))
|
||||||
mAnimation->disable("torch");
|
mAnimation->disable("torch");
|
||||||
|
@ -591,7 +591,7 @@ namespace MWRender
|
||||||
void RaceSelectionPreview::onSetup()
|
void RaceSelectionPreview::onSetup()
|
||||||
{
|
{
|
||||||
CharacterPreview::onSetup();
|
CharacterPreview::onSetup();
|
||||||
mAnimation->play("idle", 1, Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0);
|
mAnimation->play("idle", 1, BlendMask::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0);
|
||||||
mAnimation->runAnimation(0.f);
|
mAnimation->runAnimation(0.f);
|
||||||
|
|
||||||
// attach camera to follow the head node
|
// attach camera to follow the head node
|
||||||
|
|
|
@ -170,7 +170,7 @@ namespace MWRender
|
||||||
else
|
else
|
||||||
source = mAnimationTimePtr[0];
|
source = mAnimationTimePtr[0];
|
||||||
|
|
||||||
SceneUtil::AssignControllerSourcesVisitor assignVisitor(source);
|
SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::move(source));
|
||||||
attached->accept(assignVisitor);
|
attached->accept(assignVisitor);
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
|
|
|
@ -363,7 +363,7 @@ namespace MWRender
|
||||||
imageDest.mImage = image;
|
imageDest.mImage = image;
|
||||||
imageDest.mX = x;
|
imageDest.mX = x;
|
||||||
imageDest.mY = y;
|
imageDest.mY = y;
|
||||||
mPendingImageDest[camera] = imageDest;
|
mPendingImageDest[camera] = std::move(imageDest);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a quad rendering the updated texture
|
// Create a quad rendering the updated texture
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -853,7 +853,7 @@ namespace MWRender
|
||||||
src = mWeaponAnimationTime;
|
src = mWeaponAnimationTime;
|
||||||
else
|
else
|
||||||
src = mAnimationTimePtr[0];
|
src = mAnimationTimePtr[0];
|
||||||
SceneUtil::AssignControllerSourcesVisitor assignVisitor(src);
|
SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::move(src));
|
||||||
node->accept(assignVisitor);
|
node->accept(assignVisitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue