mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-30 15:45:33 +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
|
||||
Yohaulticetl
|
||||
Yuri Krupenin
|
||||
Yury Stepovikov
|
||||
zelurker
|
||||
|
||||
Documentation
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
Bug #4754: Stack of ammunition cannot be equipped partially
|
||||
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 #4898: Odd/Incorrect lighting on meshes
|
||||
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 #5065: Actors with scripted animation still try to wander and turn around without moving
|
||||
Bug #5066: Quirks with starting and stopping scripted animations
|
||||
Bug #5129: Stuttering animation on Centurion Archer
|
||||
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 #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 #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 #6550: Cloned body parts don't inherit texture effects
|
||||
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 #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 #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 #3537: Shader-based water ripples
|
||||
Feature #5173: Support for NiFogProperty
|
||||
|
|
|
@ -174,7 +174,7 @@ namespace
|
|||
|
||||
constexpr double expiryDelay = 0;
|
||||
Resource::ImageManager imageManager(&vfs, expiryDelay);
|
||||
Resource::NifFileManager nifFileManager(&vfs);
|
||||
Resource::NifFileManager nifFileManager(&vfs, &encoder.getStatelessEncoder());
|
||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay);
|
||||
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ namespace ESSImport
|
|||
Misc::StringUtils::lowerCaseInPlace(group);
|
||||
|
||||
ESM::AnimationState::ScriptedAnimation scriptedAnim;
|
||||
scriptedAnim.mGroup = group;
|
||||
scriptedAnim.mGroup = std::move(group);
|
||||
scriptedAnim.mTime = anis.mTime;
|
||||
scriptedAnim.mAbsolute = true;
|
||||
// Neither loop count nor queueing seems to be supported by the ess format.
|
||||
|
|
|
@ -306,12 +306,12 @@ namespace ESSImport
|
|||
mMarkers.push_back(marker);
|
||||
}
|
||||
|
||||
newcell.mRefs = cellrefs;
|
||||
newcell.mRefs = std::move(cellrefs);
|
||||
|
||||
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
|
||||
mIntCells[cell.mName] = newcell;
|
||||
mIntCells[cell.mName] = std::move(newcell);
|
||||
}
|
||||
|
||||
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(screenComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &GraphicsPage::screenChanged);
|
||||
connect(framerateLimitCheckBox, &QCheckBox::toggled, this, &GraphicsPage::slotFramerateLimitToggled);
|
||||
connect(shadowDistanceCheckBox, &QCheckBox::toggled, this, &GraphicsPage::slotShadowDistLimitToggled);
|
||||
}
|
||||
|
||||
bool Launcher::GraphicsPage::setupSDL()
|
||||
|
@ -126,58 +125,6 @@ bool Launcher::GraphicsPage::loadSettings()
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -220,53 +167,6 @@ void Launcher::GraphicsPage::saveSettings()
|
|||
{
|
||||
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)
|
||||
|
@ -377,9 +277,3 @@ void Launcher::GraphicsPage::slotFramerateLimitToggled(bool 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 slotStandardToggled(bool checked);
|
||||
void slotFramerateLimitToggled(bool checked);
|
||||
void slotShadowDistLimitToggled(bool checked);
|
||||
|
||||
private:
|
||||
QVector<QStringList> mResolutionsPerScreen;
|
||||
|
|
|
@ -41,11 +41,6 @@ int runLauncher(int argc, char* argv[])
|
|||
appTranslator.load(":/translations/" + locale + ".qm");
|
||||
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::FirstRunDialogResult result = mainWin.showFirstRunDialog();
|
||||
|
|
|
@ -212,6 +212,65 @@ bool Launcher::SettingsPage::loadSettings()
|
|||
loadSettingBool(Settings::fog().mExponentialFog, *exponentialFogCheckBox);
|
||||
loadSettingBool(Settings::fog().mSkyBlending, *skyBlendingCheckBox);
|
||||
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
|
||||
|
@ -359,6 +418,58 @@ void Launcher::SettingsPage::saveSettings()
|
|||
saveSettingBool(*exponentialFogCheckBox, Settings::fog().mExponentialFog);
|
||||
saveSettingBool(*skyBlendingCheckBox, Settings::fog().mSkyBlending);
|
||||
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
|
||||
|
@ -461,3 +572,17 @@ void Launcher::SettingsPage::slotSkyBlendingToggled(bool checked)
|
|||
skyBlendingStartComboBox->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 slotPostProcessToggled(bool checked);
|
||||
void slotSkyBlendingToggled(bool checked);
|
||||
void slotShadowDistLimitToggled(bool checked);
|
||||
void slotLightTypeCurrentIndexChanged(int index);
|
||||
|
||||
private:
|
||||
Config::GameSettings& mGameSettings;
|
||||
|
|
|
@ -11,459 +11,229 @@
|
|||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QTabWidget" name="DisplayTabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="DisplayWrapper">
|
||||
<attribute name="title">
|
||||
<string>Display</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="screenLabel">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="screenLabel">
|
||||
<property name="text">
|
||||
<string>Screen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="windowModeLabel">
|
||||
<property name="text">
|
||||
<string>Window mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<layout class="QGridLayout" name="resolutionLayout">
|
||||
<item row="1" column="2">
|
||||
<layout class="QHBoxLayout" name="customResolutionLayout" stretch="1,0,1">
|
||||
<item>
|
||||
<widget class="QSpinBox" name="customWidthSpinBox">
|
||||
<property name="minimum">
|
||||
<number>800</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="multiplyLabel">
|
||||
<property name="text">
|
||||
<string> × </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="customHeightSpinBox">
|
||||
<property name="minimum">
|
||||
<number>600</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QRadioButton" name="customRadioButton">
|
||||
<property name="text">
|
||||
<string>Custom:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QRadioButton" name="standardRadioButton">
|
||||
<property name="text">
|
||||
<string>Standard:</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QComboBox" name="resolutionComboBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="antiAliasingComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Screen:</string>
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="antiAliasingComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>8</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>16</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="screenComboBox"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="windowModeLabel">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Window Mode:</string>
|
||||
<string>2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="resolutionLabel">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Resolution:</string>
|
||||
<string>4</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">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Vertical Sync:</string>
|
||||
<string>8</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="antiAliasingLabel">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Anti-aliasing:</string>
|
||||
<string>16</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<layout class="QGridLayout" name="resolutionLayout">
|
||||
<item row="1" column="2">
|
||||
<layout class="QHBoxLayout" name="customResolutionLayout" stretch="1,0,1">
|
||||
<item>
|
||||
<widget class="QSpinBox" name="customWidthSpinBox">
|
||||
<property name="minimum">
|
||||
<number>800</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="multiplyLabel">
|
||||
<property name="text">
|
||||
<string> × </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="customHeightSpinBox">
|
||||
<property name="minimum">
|
||||
<number>600</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QRadioButton" name="customRadioButton">
|
||||
<property name="text">
|
||||
<string>Custom:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QRadioButton" name="standardRadioButton">
|
||||
<property name="text">
|
||||
<string>Standard:</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QComboBox" name="resolutionComboBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="windowModeComboBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Fullscreen</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Windowed Fullscreen</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Windowed</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="windowBorderCheckBox">
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="framerateLimitCheckBox">
|
||||
<property name="text">
|
||||
<string>Framerate limit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="windowBorderCheckBox">
|
||||
<property name="text">
|
||||
<string>Window border</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="screenComboBox"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="vSyncComboBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Window Border</string>
|
||||
<string>Disabled</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="vSyncComboBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disabled</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enabled</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Adaptive</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="framerateLimitCheckBox">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Framerate Limit:</string>
|
||||
<string>Enabled</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QDoubleSpinBox" name="framerateLimitSpinBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> FPS</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>15.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>300.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="LightingWrapper">
|
||||
<attribute name="title">
|
||||
<string>Lighting</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="lightingLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="lightingMethodLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lightingMethodLabel">
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Lighting Method:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="lightingMethodComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>legacy</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>shaders compatibility</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>shaders</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="ShadowWrapper">
|
||||
<attribute name="title">
|
||||
<string>Shadows</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="shadowsLayout" columnstretch="0,0">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="playerShadowsCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable shadows exclusively for the player character. May have a very minor performance impact.</p></body></html></string>
|
||||
<string>Adaptive</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="windowModeComboBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<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>
|
||||
<string>Fullscreen</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<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>
|
||||
<string>Windowed Fullscreen</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enable Object Shadows</string>
|
||||
<string>Windowed</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>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QDoubleSpinBox" name="framerateLimitSpinBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> FPS</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>15.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>300.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="antiAliasingLabel">
|
||||
<property name="text">
|
||||
<string>Anti-aliasing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="vSyncLabel">
|
||||
<property name="text">
|
||||
<string>Vertical synchronization</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>514</width>
|
||||
<width>515</width>
|
||||
<height>397</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -129,16 +129,22 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
|
|
@ -7,20 +7,20 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>720</width>
|
||||
<height>565</height>
|
||||
<height>635</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>720</width>
|
||||
<height>565</height>
|
||||
<height>635</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>OpenMW Launcher</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../launcher/launcher.qrc">
|
||||
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||
<normaloff>:/images/openmw.png</normaloff>:/images/openmw.png</iconset>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
|
@ -120,7 +120,7 @@ QToolButton {
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<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>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
@ -141,11 +141,11 @@ QToolButton {
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Graphics</string>
|
||||
<string>Display</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Allows to change graphics settings</string>
|
||||
|
@ -156,7 +156,7 @@ QToolButton {
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../launcher/launcher.qrc">
|
||||
<iconset resource="../../../files/launcher/launcher.qrc">
|
||||
<normaloff>:/images/preferences.png</normaloff>:/images/preferences.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
@ -171,7 +171,7 @@ QToolButton {
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<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>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
@ -183,7 +183,7 @@ QToolButton {
|
|||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../launcher/launcher.qrc"/>
|
||||
<include location="../../../files/launcher/launcher.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -221,7 +221,7 @@ namespace NavMeshTool
|
|||
constexpr double expiryDelay = 0;
|
||||
|
||||
Resource::ImageManager imageManager(&vfs, expiryDelay);
|
||||
Resource::NifFileManager nifFileManager(&vfs);
|
||||
Resource::NifFileManager nifFileManager(&vfs, &encoder.getStatelessEncoder());
|
||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay);
|
||||
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
|
||||
DetourNavigator::RecastGlobalAllocator::init();
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <components/vfs/bsaarchive.hpp>
|
||||
#include <components/vfs/filesystemarchive.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.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.
|
||||
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.
|
||||
bool isBSA(const std::filesystem::path& filename)
|
||||
|
@ -75,7 +76,10 @@ void readNIF(
|
|||
const std::string pathStr = Files::pathToUnicodeString(path);
|
||||
if (!quiet)
|
||||
{
|
||||
std::cout << "Reading NIF file '" << pathStr << "'";
|
||||
if (hasExtension(path, ".kf"))
|
||||
std::cout << "Reading KF file '" << pathStr << "'";
|
||||
else
|
||||
std::cout << "Reading NIF file '" << pathStr << "'";
|
||||
if (!source.empty())
|
||||
std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'";
|
||||
std::cout << std::endl;
|
||||
|
@ -84,7 +88,7 @@ void readNIF(
|
|||
try
|
||||
{
|
||||
Nif::NIFFile file(fullPath);
|
||||
Nif::Reader reader(file);
|
||||
Nif::Reader reader(file, nullptr);
|
||||
if (vfs != nullptr)
|
||||
reader.parse(vfs->get(pathStr));
|
||||
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& 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:
|
||||
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.
|
||||
|
||||
Allowed options)");
|
||||
|
@ -240,7 +244,8 @@ int main(int argc, char** argv)
|
|||
}
|
||||
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)
|
||||
|
|
|
@ -81,11 +81,6 @@ int runApplication(int argc, char* argv[])
|
|||
|
||||
Application application(argc, argv);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
QDir::setCurrent(dir.absolutePath());
|
||||
#endif
|
||||
|
||||
application.setWindowIcon(QIcon(":./openmw-cs.png"));
|
||||
|
||||
CS::Editor editor(argc, argv);
|
||||
|
|
|
@ -624,7 +624,7 @@ bool CSMFilter::Parser::parse(const std::string& filter, bool allowPredefined)
|
|||
}
|
||||
|
||||
if (node)
|
||||
mFilter = node;
|
||||
mFilter = std::move(node);
|
||||
else
|
||||
{
|
||||
// Empty filter string equals to filter "true".
|
||||
|
|
|
@ -171,7 +171,7 @@ void CSMTools::ReportModel::flagAsReplaced(int index)
|
|||
|
||||
hint[0] = 'r';
|
||||
|
||||
line.mHint = hint;
|
||||
line.mHint = std::move(hint);
|
||||
|
||||
emit dataChanged(this->index(index, 0), this->index(index, columnCount()));
|
||||
}
|
||||
|
|
|
@ -468,13 +468,13 @@ namespace CSMWorld
|
|||
if (type == UniversalId::Type_Creature)
|
||||
{
|
||||
// Valid creature record
|
||||
setupCreature(id, data);
|
||||
setupCreature(id, std::move(data));
|
||||
emit actorChanged(id);
|
||||
}
|
||||
else if (type == UniversalId::Type_Npc)
|
||||
{
|
||||
// Valid npc record
|
||||
setupNpc(id, data);
|
||||
setupNpc(id, std::move(data));
|
||||
emit actorChanged(id);
|
||||
}
|
||||
else
|
||||
|
@ -665,7 +665,7 @@ namespace CSMWorld
|
|||
RaceDataPtr data = mCachedRaces.get(race);
|
||||
if (data)
|
||||
{
|
||||
setupRace(race, data);
|
||||
setupRace(race, std::move(data));
|
||||
// Race was changed. Need to mark actor dependencies as dirty.
|
||||
// Cannot use markDirtyDependency because that would invalidate
|
||||
// the current iterator.
|
||||
|
@ -683,7 +683,7 @@ namespace CSMWorld
|
|||
ActorDataPtr data = mCachedActors.get(actor);
|
||||
if (data)
|
||||
{
|
||||
setupActor(actor, data);
|
||||
setupActor(actor, std::move(data));
|
||||
}
|
||||
}
|
||||
mDirtyActors.clear();
|
||||
|
|
|
@ -15,11 +15,13 @@
|
|||
#include <QVariant>
|
||||
|
||||
#include <components/esm3/loaddial.hpp>
|
||||
#include <components/esm3/loadmgef.hpp>
|
||||
#include <components/esm3/loadskil.hpp>
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
|
||||
#include "collectionbase.hpp"
|
||||
#include "columnbase.hpp"
|
||||
#include "columnimp.hpp"
|
||||
#include "info.hpp"
|
||||
#include "land.hpp"
|
||||
#include "landtexture.hpp"
|
||||
|
@ -82,6 +84,17 @@ namespace CSMWorld
|
|||
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)
|
||||
{
|
||||
return ESM::RefId::stringRefId(LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex));
|
||||
|
@ -504,7 +517,7 @@ namespace CSMWorld
|
|||
|
||||
auto record2 = std::make_unique<Record<ESXRecordT>>();
|
||||
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
|
||||
record2->mModified = record;
|
||||
record2->mModified = std::move(record);
|
||||
|
||||
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()
|
||||
{
|
||||
mOld.reset(mTable.getRecord(mId).clone().get());
|
||||
mOld = mTable.getRecord(mId).clone();
|
||||
mChanged = mTable.touchRecord(mId);
|
||||
}
|
||||
|
||||
|
@ -181,9 +181,8 @@ const std::string& CSMWorld::TouchLandCommand::getDestinationId() const
|
|||
|
||||
void CSMWorld::TouchLandCommand::onRedo()
|
||||
{
|
||||
mOld = mLands.getRecord(mId).clone();
|
||||
mChanged = mLands.touchRecord(mId);
|
||||
if (mChanged)
|
||||
mOld.reset(mLands.getRecord(mId).clone().get());
|
||||
}
|
||||
|
||||
void CSMWorld::TouchLandCommand::onUndo()
|
||||
|
|
|
@ -149,7 +149,8 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
|
|||
mResourcesManager.setVFS(mVFS.get());
|
||||
|
||||
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
|
||||
= mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
|
||||
|
@ -1107,7 +1108,7 @@ void CSMWorld::Data::loadFallbackEntries()
|
|||
newMarker.mModel = model;
|
||||
newMarker.mRecordFlags = 0;
|
||||
auto record = std::make_unique<CSMWorld::Record<ESM::Static>>();
|
||||
record->mBase = newMarker;
|
||||
record->mBase = std::move(newMarker);
|
||||
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||
mReferenceables.appendRecord(std::move(record), CSMWorld::UniversalId::Type_Static);
|
||||
}
|
||||
|
@ -1123,7 +1124,7 @@ void CSMWorld::Data::loadFallbackEntries()
|
|||
newMarker.mModel = model;
|
||||
newMarker.mRecordFlags = 0;
|
||||
auto record = std::make_unique<CSMWorld::Record<ESM::Door>>();
|
||||
record->mBase = newMarker;
|
||||
record->mBase = std::move(newMarker);
|
||||
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||
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->setMaxVisibleItems(10);
|
||||
|
||||
mCompleters[current->first] = completer;
|
||||
mCompleters[current->first] = std::move(completer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
CSMWorld::Resources::Resources(
|
||||
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
|
||||
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
|
||||
else
|
||||
|
|
|
@ -410,7 +410,7 @@ bool CSVDoc::ViewManager::removeDocument(CSVDoc::View* view)
|
|||
remainingViews.push_back(*iter);
|
||||
}
|
||||
mDocumentManager.removeDocument(document);
|
||||
mViews = remainingViews;
|
||||
mViews = std::move(remainingViews);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -514,7 +514,7 @@ void CSVRender::PagedWorldspaceWidget::moveCellSelection(int x, int y)
|
|||
addCellToScene(*iter);
|
||||
}
|
||||
|
||||
mSelection = newSelection;
|
||||
mSelection = std::move(newSelection);
|
||||
}
|
||||
|
||||
void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera(int offsetX, int offsetY)
|
||||
|
|
|
@ -541,7 +541,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
|
|||
= landTable.data(landTable.getModelIndex(cellId, textureColumn))
|
||||
.value<CSMWorld::LandTexturesColumn::DataType>();
|
||||
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)));
|
||||
undoStack.push(new CSMWorld::ModifyCommand(ltexTable, index, textureFileNameVariant));
|
||||
undoStack.endMacro();
|
||||
mBrushTexture = newId;
|
||||
mBrushTexture = std::move(newId);
|
||||
}
|
||||
|
||||
bool CSVRender::TerrainTextureMode::allowLandTextureEditing(const std::string& cellId)
|
||||
|
|
|
@ -184,11 +184,11 @@ void CSVRender::WorldspaceWidget::selectDefaultNavigationMode()
|
|||
|
||||
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)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
@ -440,7 +440,7 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
|||
osg::Node* node = *nodeIter;
|
||||
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)
|
||||
{
|
||||
hit.index0 = intersection.indexList[0];
|
||||
|
@ -757,13 +757,14 @@ void CSVRender::WorldspaceWidget::toggleHiddenInstances()
|
|||
if (selection.empty())
|
||||
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
|
||||
= firstSelection->mObject->getRootNode()->getNodeMask() == Mask_Hidden ? Mask_Reference : Mask_Hidden;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ void CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture)
|
|||
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));
|
||||
}
|
||||
|
||||
mBrushTexture = newBrushTextureId;
|
||||
mBrushTexture = std::move(newBrushTextureId);
|
||||
|
||||
emit passTextureId(mBrushTexture);
|
||||
emit passBrushShape(mBrushShape); // updates the icon tooltip
|
||||
|
|
|
@ -146,7 +146,7 @@ void CSVWorld::ExtendedCommandConfigurator::setupCheckBoxes(const std::vector<CS
|
|||
CSMWorld::UniversalId type = types[counter];
|
||||
current->first->setText(QString::fromUtf8(type.getTypeName().c_str()));
|
||||
current->first->setChecked(true);
|
||||
current->second = type;
|
||||
current->second = std::move(type);
|
||||
++counter;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -169,7 +169,7 @@ void CSVWorld::TableSubView::createFilterRequest(std::vector<CSMWorld::Universal
|
|||
{
|
||||
CSVFilter::FilterData filterData;
|
||||
filterData.searchData = it->getId();
|
||||
filterData.columns = col;
|
||||
filterData.columns = std::move(col);
|
||||
sourceFilter.emplace_back(filterData);
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ void CSVWorld::TableSubView::createFilterRequest(std::vector<CSMWorld::Universal
|
|||
|
||||
CSVFilter::FilterData filterData;
|
||||
filterData.searchData = qData;
|
||||
filterData.columns = searchColumns;
|
||||
filterData.columns = std::move(searchColumns);
|
||||
|
||||
sourceFilterByValue.emplace_back(filterData);
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ add_openmw_dir (mwrender
|
|||
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
|
||||
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover
|
||||
postprocessor pingpongcull luminancecalculator pingpongcanvas transparentpass precipitationocclusion ripples
|
||||
actorutil distortion
|
||||
actorutil distortion animationpriority bonegroup blendmask
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
@ -64,7 +64,7 @@ add_openmw_dir (mwlua
|
|||
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
||||
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings
|
||||
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/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
|
||||
|
@ -113,7 +113,7 @@ add_openmw_dir (mwstate
|
|||
|
||||
add_openmw_dir (mwbase
|
||||
environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager
|
||||
inputmanager windowmanager statemanager
|
||||
inputmanager windowmanager statemanager luamanager
|
||||
)
|
||||
|
||||
# Main executable
|
||||
|
|
|
@ -321,11 +321,7 @@ bool OMW::Engine::frame(float frametime)
|
|||
// update GUI by world data
|
||||
{
|
||||
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
|
||||
|
@ -706,7 +702,8 @@ void OMW::Engine::prepareEngine()
|
|||
|
||||
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()->setUnRefImageDataAfterApply(
|
||||
false); // keep to Off for now to allow better state sharing
|
||||
|
@ -826,7 +823,7 @@ void OMW::Engine::prepareEngine()
|
|||
}
|
||||
listener->loadingOff();
|
||||
|
||||
mWorld->init(mViewer, rootNode, mWorkQueue.get(), *mUnrefQueue);
|
||||
mWorld->init(mViewer, std::move(rootNode), mWorkQueue.get(), *mUnrefQueue);
|
||||
mEnvironment.setWorldScene(mWorld->getWorldScene());
|
||||
mWorld->setupPlayer();
|
||||
mWorld->setRandomSeed(mRandomSeed);
|
||||
|
|
|
@ -219,8 +219,6 @@ int runApplication(int argc, char* argv[])
|
|||
Platform::init();
|
||||
|
||||
#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);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <SDL_events.h>
|
||||
|
||||
#include "../mwgui/mode.hpp"
|
||||
#include "../mwrender/animationpriority.hpp"
|
||||
#include <components/sdlutil/events.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
|
@ -61,10 +62,14 @@ namespace MWBase
|
|||
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 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 actorDied(const MWWorld::Ptr& actor) = 0;
|
||||
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
||||
|
||||
// `arg` is either forwarded from MWGui::pushGuiMode or empty
|
||||
virtual void uiModeChanged(const MWWorld::Ptr& arg) = 0;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "../mwmechanics/greetingstate.hpp"
|
||||
#include "../mwrender/animationpriority.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
|
@ -170,16 +171,33 @@ namespace MWBase
|
|||
///< Forces an object to refresh its animation state.
|
||||
|
||||
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;
|
||||
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
||||
/// in the scene should be ignored.
|
||||
///
|
||||
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
|
||||
/// \param count How many times the animation should be run
|
||||
/// \param persist Whether the animation state should be stored in saved games
|
||||
/// and persist after cell unload.
|
||||
/// \param number How many times the animation should be run
|
||||
/// \param scripted Whether the animation should be treated as a scripted animation.
|
||||
/// \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;
|
||||
///< 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 checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const = 0;
|
||||
|
||||
/// Save the current animation state of managed references to their RefData.
|
||||
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
|
||||
/// paused we may want to do it manually (after equipping permanent enchantment)
|
||||
virtual void updateMagicEffects(const MWWorld::Ptr& ptr) = 0;
|
||||
|
|
|
@ -37,23 +37,6 @@ namespace MWClass
|
|||
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
|
||||
{
|
||||
MWMechanics::Movement& movement = getMovementSettings(ptr);
|
||||
|
|
|
@ -45,8 +45,6 @@ namespace MWClass
|
|||
|
||||
bool useAnim() const override;
|
||||
|
||||
void block(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.
|
||||
|
||||
|
|
|
@ -183,16 +183,17 @@ namespace MWClass
|
|||
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);
|
||||
if (!model.empty())
|
||||
models.push_back(model);
|
||||
|
||||
// FIXME: use const version of InventoryStore functions once they are available
|
||||
if (hasInventoryStore(ptr))
|
||||
const MWWorld::CustomData* customData = ptr.getRefData().getCustomData();
|
||||
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)
|
||||
{
|
||||
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
||||
|
@ -339,10 +340,7 @@ namespace MWClass
|
|||
MWMechanics::applyElementalShields(ptr, victim);
|
||||
|
||||
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))
|
||||
{
|
||||
damage = 0;
|
||||
victim.getClass().block(victim);
|
||||
}
|
||||
|
||||
MWMechanics::diseaseContact(victim, ptr);
|
||||
|
||||
|
@ -513,7 +511,7 @@ namespace MWClass
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ namespace MWClass
|
|||
MWWorld::InventoryStore& getInventoryStore(const MWWorld::Ptr& ptr) const override;
|
||||
///< 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;
|
||||
///< Return name of the script attached to ptr
|
||||
|
@ -107,7 +107,7 @@ namespace MWClass
|
|||
|
||||
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:
|
||||
///< list getModel().
|
||||
|
||||
|
|
|
@ -99,25 +99,6 @@ namespace MWClass
|
|||
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(
|
||||
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;
|
||||
///< @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,
|
||||
MWRender::RenderingInterface& renderingInterface) const override;
|
||||
///< Add reference into a cell for rendering
|
||||
|
|
|
@ -143,7 +143,7 @@ namespace MWClass
|
|||
|
||||
list.push_back(params);
|
||||
}
|
||||
info.effects = list;
|
||||
info.effects = std::move(list);
|
||||
|
||||
info.text = std::move(text);
|
||||
info.isIngredient = true;
|
||||
|
|
|
@ -436,10 +436,11 @@ namespace MWClass
|
|||
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 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)
|
||||
models.push_back(Settings::models().mBaseanimkna);
|
||||
|
||||
|
@ -453,56 +454,57 @@ namespace MWClass
|
|||
|
||||
if (!npc->mBase->mHead.empty())
|
||||
{
|
||||
const ESM::BodyPart* head
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::BodyPart>().search(npc->mBase->mHead);
|
||||
const ESM::BodyPart* head = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHead);
|
||||
if (head)
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(head->mModel));
|
||||
}
|
||||
if (!npc->mBase->mHair.empty())
|
||||
{
|
||||
const ESM::BodyPart* hair
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::BodyPart>().search(npc->mBase->mHair);
|
||||
const ESM::BodyPart* hair = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHair);
|
||||
if (hair)
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(hair->mModel));
|
||||
}
|
||||
|
||||
bool female = (npc->mBase->mFlags & ESM::NPC::Female);
|
||||
|
||||
// FIXME: use const version of InventoryStore functions once they are available
|
||||
// preload equipped items
|
||||
const MWWorld::InventoryStore& invStore = getInventoryStore(ptr);
|
||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
const MWWorld::CustomData* customData = ptr.getRefData().getCustomData();
|
||||
if (customData)
|
||||
{
|
||||
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
||||
if (equipped != invStore.end())
|
||||
const MWWorld::InventoryStore& invStore = customData->asNpcCustomData().mInventoryStore;
|
||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
{
|
||||
std::vector<ESM::PartReference> parts;
|
||||
if (equipped->getType() == ESM::Clothing::sRecordId)
|
||||
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
||||
if (equipped != invStore.end())
|
||||
{
|
||||
const ESM::Clothing* clothes = equipped->get<ESM::Clothing>()->mBase;
|
||||
parts = clothes->mParts.mParts;
|
||||
}
|
||||
else if (equipped->getType() == ESM::Armor::sRecordId)
|
||||
{
|
||||
const ESM::Armor* armor = equipped->get<ESM::Armor>()->mBase;
|
||||
parts = armor->mParts.mParts;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string model = equipped->getClass().getModel(*equipped);
|
||||
if (!model.empty())
|
||||
models.push_back(model);
|
||||
}
|
||||
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;
|
||||
|
||||
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));
|
||||
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)
|
||||
{
|
||||
const ESM::Clothing* clothes = equipped->get<ESM::Clothing>()->mBase;
|
||||
addParts(clothes->mParts.mParts);
|
||||
}
|
||||
else if (equipped->getType() == ESM::Armor::sRecordId)
|
||||
{
|
||||
const ESM::Armor* armor = equipped->get<ESM::Armor>()->mBase;
|
||||
addParts(armor->mParts.mParts);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string model = equipped->getClass().getModel(*equipped);
|
||||
if (!model.empty())
|
||||
models.push_back(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -512,9 +514,8 @@ namespace MWClass
|
|||
{
|
||||
const std::vector<const ESM::BodyPart*>& parts
|
||||
= 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())
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
||||
}
|
||||
|
@ -678,10 +679,7 @@ namespace MWClass
|
|||
MWMechanics::applyElementalShields(ptr, victim);
|
||||
|
||||
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))
|
||||
{
|
||||
damage = 0;
|
||||
victim.getClass().block(victim);
|
||||
}
|
||||
|
||||
if (victim == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState())
|
||||
damage = 0;
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace MWClass
|
|||
MWWorld::InventoryStore& getInventoryStore(const MWWorld::Ptr& ptr) const override;
|
||||
///< 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;
|
||||
|
||||
|
@ -85,7 +85,7 @@ namespace MWClass
|
|||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
||||
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:
|
||||
///< list getModel().
|
||||
|
||||
|
|
|
@ -63,17 +63,17 @@ namespace
|
|||
switch (order)
|
||||
{
|
||||
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:
|
||||
return { question, { r0, r2, r1 }, sound };
|
||||
return { std::move(question), { std::move(r0), std::move(r2), std::move(r1) }, std::move(sound) };
|
||||
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:
|
||||
return { question, { r1, r2, r0 }, sound };
|
||||
return { std::move(question), { std::move(r1), std::move(r2), std::move(r0) }, std::move(sound) };
|
||||
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:
|
||||
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
|
||||
= Misc::StringUtils::lowerCase(windowManager->getTranslationDataStorage().topicStandardForm(link));
|
||||
|
||||
std::string displayName = link;
|
||||
std::string displayName = std::move(link);
|
||||
while (displayName[displayName.size() - 1] == '*')
|
||||
displayName.erase(displayName.size() - 1, 1);
|
||||
|
||||
|
@ -248,7 +248,7 @@ namespace MWGui
|
|||
i = match.mEnd;
|
||||
}
|
||||
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())
|
||||
return;
|
||||
|
||||
mTopicsList->adjustSize();
|
||||
redrawTopicsList();
|
||||
updateHistory();
|
||||
updateTopicFormat();
|
||||
mCurrentWindowSize = _sender->getSize();
|
||||
}
|
||||
|
||||
|
@ -534,6 +533,14 @@ namespace MWGui
|
|||
return true;
|
||||
}
|
||||
|
||||
void DialogueWindow::redrawTopicsList()
|
||||
{
|
||||
mTopicsList->adjustSize();
|
||||
|
||||
// The topics list has been regenerated so topic formatting needs to be updated
|
||||
updateTopicFormat();
|
||||
}
|
||||
|
||||
void DialogueWindow::updateTopicsPane()
|
||||
{
|
||||
mTopicsList->clear();
|
||||
|
@ -591,11 +598,9 @@ namespace MWGui
|
|||
t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);
|
||||
mTopicLinks[topicId] = std::move(t);
|
||||
}
|
||||
mTopicsList->adjustSize();
|
||||
|
||||
redrawTopicsList();
|
||||
updateHistory();
|
||||
// The topics list has been regenerated so topic formatting needs to be updated
|
||||
updateTopicFormat();
|
||||
}
|
||||
|
||||
void DialogueWindow::updateHistory(bool scrollbar)
|
||||
|
@ -756,21 +761,12 @@ namespace MWGui
|
|||
+ std::string("/100"));
|
||||
}
|
||||
|
||||
bool dispositionWasVisible = mDispositionBar->getVisible();
|
||||
|
||||
if (dispositionVisible && !dispositionWasVisible)
|
||||
if (mDispositionBar->getVisible() != dispositionVisible)
|
||||
{
|
||||
mDispositionBar->setVisible(true);
|
||||
int offset = mDispositionBar->getHeight() + 5;
|
||||
mDispositionBar->setVisible(dispositionVisible);
|
||||
const int offset = (mDispositionBar->getHeight() + 5) * (dispositionVisible ? 1 : -1);
|
||||
mTopicsList->setCoord(mTopicsList->getCoord() + MyGUI::IntCoord(0, offset, 0, -offset));
|
||||
mTopicsList->adjustSize();
|
||||
}
|
||||
else if (!dispositionVisible && dispositionWasVisible)
|
||||
{
|
||||
mDispositionBar->setVisible(false);
|
||||
int offset = mDispositionBar->getHeight() + 5;
|
||||
mTopicsList->setCoord(mTopicsList->getCoord() - MyGUI::IntCoord(0, offset, 0, -offset));
|
||||
mTopicsList->adjustSize();
|
||||
redrawTopicsList();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -190,6 +190,7 @@ namespace MWGui
|
|||
void updateDisposition();
|
||||
void restock();
|
||||
void deleteLater();
|
||||
void redrawTopicsList();
|
||||
|
||||
bool mIsCompanion;
|
||||
std::list<std::string> mKeywords;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
|
|
|
@ -136,9 +136,9 @@ namespace MWGui
|
|||
return;
|
||||
|
||||
if (enabled)
|
||||
processor->enableTechnique(technique);
|
||||
processor->enableTechnique(std::move(technique));
|
||||
else
|
||||
processor->disableTechnique(technique);
|
||||
processor->disableTechnique(std::move(technique));
|
||||
processor->saveChain();
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ namespace MWGui
|
|||
if (technique->getDynamic())
|
||||
return;
|
||||
|
||||
if (processor->enableTechnique(technique, index) != MWRender::PostProcessor::Status_Error)
|
||||
if (processor->enableTechnique(std::move(technique), index) != MWRender::PostProcessor::Status_Error)
|
||||
processor->saveChain();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
#include <components/widgets/sharedstatebutton.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -131,7 +132,7 @@ namespace
|
|||
void updateMaxLightsComboBox(MyGUI::ComboBox* box)
|
||||
{
|
||||
constexpr int min = 8;
|
||||
constexpr int max = 32;
|
||||
constexpr int max = 64;
|
||||
constexpr int increment = 8;
|
||||
const int maxLights = Settings::shaders().mMaxLights;
|
||||
// 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());
|
||||
};
|
||||
|
||||
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>)
|
||||
{ // only for global scripts
|
||||
cellT["getAll"] = [ids = getPackageToTypeTable(context.mLua->sol())](
|
||||
|
@ -270,7 +277,7 @@ namespace MWLua
|
|||
if (!ok)
|
||||
throw std::runtime_error(
|
||||
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/esmstore.hpp"
|
||||
|
||||
#include "animationbindings.hpp"
|
||||
#include "factionbindings.hpp"
|
||||
#include "luaevents.hpp"
|
||||
#include "magicbindings.hpp"
|
||||
|
@ -83,6 +84,7 @@ namespace MWLua
|
|||
};
|
||||
api["contentFiles"] = initContentFilesBindings(lua->sol());
|
||||
api["sound"] = initCoreSoundBindings(context);
|
||||
api["vfx"] = initCoreVfxBindings(context);
|
||||
api["getFormId"] = [](std::string_view contentFile, unsigned int index) -> std::string {
|
||||
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
|
||||
for (size_t i = 0; i < contentList.size(); ++i)
|
||||
|
@ -133,6 +135,7 @@ namespace MWLua
|
|||
api[k] = v;
|
||||
api["sendGlobalEvent"] = sol::nil;
|
||||
api["sound"] = sol::nil;
|
||||
api["vfx"] = sol::nil;
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,15 @@ namespace MWLua
|
|||
|
||||
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:
|
||||
MWWorld::Ptr getPtr(ESM::RefNum id) const
|
||||
{
|
||||
|
|
|
@ -51,7 +51,14 @@ namespace MWLua
|
|||
{
|
||||
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 addToQueue(Event e) { mQueue.push_back(std::move(e)); }
|
||||
|
|
|
@ -82,13 +82,18 @@ namespace MWLua
|
|||
inputActions[sol::meta_function::pairs] = pairs;
|
||||
}
|
||||
|
||||
context.mLua->sol().new_usertype<LuaUtil::InputAction::Info>("ActionInfo", "key",
|
||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mKey; }), "name",
|
||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mName; }), "description",
|
||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mDescription; }), "type",
|
||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mType; }), "l10n",
|
||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mL10n; }), "defaultValue",
|
||||
sol::property([](const LuaUtil::InputAction::Info& info) { return info.mDefaultValue; }));
|
||||
auto actionInfo = context.mLua->sol().new_usertype<LuaUtil::InputAction::Info>("ActionInfo");
|
||||
actionInfo["key"] = sol::readonly_property(
|
||||
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mKey; });
|
||||
actionInfo["name"] = sol::readonly_property(
|
||||
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mName; });
|
||||
actionInfo["description"] = sol::readonly_property(
|
||||
[](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");
|
||||
inputTriggers[sol::meta_function::index]
|
||||
|
@ -108,11 +113,15 @@ namespace MWLua
|
|||
inputTriggers[sol::meta_function::pairs] = pairs;
|
||||
}
|
||||
|
||||
context.mLua->sol().new_usertype<LuaUtil::InputTrigger::Info>("TriggerInfo", "key",
|
||||
sol::property([](const LuaUtil::InputTrigger::Info& info) { return info.mKey; }), "name",
|
||||
sol::property([](const LuaUtil::InputTrigger::Info& info) { return info.mName; }), "description",
|
||||
sol::property([](const LuaUtil::InputTrigger::Info& info) { return info.mDescription; }), "l10n",
|
||||
sol::property([](const LuaUtil::InputTrigger::Info& info) { return info.mL10n; }));
|
||||
auto triggerInfo = context.mLua->sol().new_usertype<LuaUtil::InputTrigger::Info>("TriggerInfo");
|
||||
triggerInfo["key"] = sol::readonly_property(
|
||||
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mKey; });
|
||||
triggerInfo["name"] = sol::readonly_property(
|
||||
[](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();
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include <components/esm3/loadcell.hpp>
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwmechanics/aicombat.hpp"
|
||||
#include "../mwmechanics/aiescort.hpp"
|
||||
#include "../mwmechanics/aifollow.hpp"
|
||||
|
@ -162,6 +164,10 @@ namespace MWLua
|
|||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
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)
|
||||
|
@ -170,7 +176,7 @@ namespace MWLua
|
|||
{
|
||||
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
|
||||
registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers,
|
||||
&mOnTeleportedHandlers });
|
||||
&mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers });
|
||||
}
|
||||
|
||||
void LocalScripts::setActive(bool active)
|
||||
|
|
|
@ -71,6 +71,14 @@ namespace MWLua
|
|||
void onConsume(const LObject& consumable) { callEngineHandlers(mOnConsumeHandlers, consumable); }
|
||||
void onActivated(const LObject& actor) { callEngineHandlers(mOnActivatedHandlers, actor); }
|
||||
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();
|
||||
|
||||
|
@ -83,6 +91,8 @@ namespace MWLua
|
|||
EngineHandlerList mOnConsumeHandlers{ "onConsume" };
|
||||
EngineHandlerList mOnActivatedHandlers{ "onActivated" };
|
||||
EngineHandlerList mOnTeleportedHandlers{ "onTeleported" };
|
||||
EngineHandlerList mOnAnimationTextKeyHandlers{ "_onAnimationTextKey" };
|
||||
EngineHandlerList mOnPlayAnimationHandlers{ "_onPlayAnimation" };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/datetimemanager.hpp"
|
||||
|
||||
#include "animationbindings.hpp"
|
||||
#include "camerabindings.hpp"
|
||||
#include "cellbindings.hpp"
|
||||
#include "corebindings.hpp"
|
||||
|
@ -30,6 +31,7 @@ namespace MWLua
|
|||
sol::state_view lua = context.mLua->sol();
|
||||
MWWorld::DateTimeManager* tm = MWBase::Environment::get().getWorld()->getTimeManager();
|
||||
return {
|
||||
{ "openmw.animation", initAnimationPackage(context) },
|
||||
{ "openmw.async",
|
||||
LuaUtil::getAsyncPackageInitializer(
|
||||
lua, [tm] { return tm->getSimulationTime(); }, [tm] { return tm->getGameTime(); }) },
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwrender/bonegroup.hpp"
|
||||
#include "../mwrender/postprocessor.hpp"
|
||||
|
||||
#include "../mwworld/datetimemanager.hpp"
|
||||
|
@ -402,6 +403,49 @@ namespace MWLua
|
|||
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)
|
||||
{
|
||||
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) });
|
||||
}
|
||||
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
|
||||
{
|
||||
mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell });
|
||||
|
|
|
@ -389,6 +389,17 @@ namespace MWLua
|
|||
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||
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 {
|
||||
return MWBase::Environment::get()
|
||||
.getWorld()
|
||||
|
@ -730,7 +741,7 @@ namespace MWLua
|
|||
auto id = sol::make_object(lua, self.mIterator->getId().serializeText());
|
||||
auto params = sol::make_object(lua, ActiveSpell{ self.mActor, *self.mIterator });
|
||||
self.advance();
|
||||
return { params, params };
|
||||
return { id, params };
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -104,8 +104,12 @@ namespace MWLua
|
|||
});
|
||||
mwscript["player"] = sol::readonly_property(
|
||||
[](const MWScriptRef&) { return GObject(MWBase::Environment::get().getWorld()->getPlayerPtr()); });
|
||||
mwscriptVars[sol::meta_function::index] = [](MWScriptVariables& s, std::string_view var) {
|
||||
return s.mRef.getLocals().getVarAsDouble(s.mRef.mId, Misc::StringUtils::lowerCase(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));
|
||||
else
|
||||
return sol::nullopt;
|
||||
};
|
||||
mwscriptVars[sol::meta_function::new_index] = [](MWScriptVariables& s, std::string_view var, double val) {
|
||||
MWScript::Locals& locals = s.mRef.getLocals();
|
||||
|
|
|
@ -613,7 +613,7 @@ namespace MWLua
|
|||
MWBase::Environment::get().getWorldModel()->registerPtr(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) {
|
||||
|
@ -661,7 +661,7 @@ namespace MWLua
|
|||
list->push_back(getId(item));
|
||||
}
|
||||
}
|
||||
return ObjectList<ObjectT>{ list };
|
||||
return ObjectList<ObjectT>{ std::move(list) };
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,11 @@ namespace
|
|||
float mTimeOffset = 0.f;
|
||||
};
|
||||
|
||||
struct StreamMusicArgs
|
||||
{
|
||||
float mFade = 1.f;
|
||||
};
|
||||
|
||||
PlaySoundArgs getPlaySoundArgs(const sol::optional<sol::table>& options)
|
||||
{
|
||||
PlaySoundArgs args;
|
||||
|
@ -55,6 +60,17 @@ namespace
|
|||
return MWSound::PlayMode::NoEnvNoScaling;
|
||||
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
|
||||
|
@ -99,9 +115,10 @@ namespace MWLua
|
|||
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();
|
||||
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(); };
|
||||
|
|
|
@ -380,7 +380,7 @@ namespace MWLua
|
|||
addProp(context, attributeStatT, "base", &MWMechanics::AttributeValue::getBase);
|
||||
addProp(context, attributeStatT, "damage", &MWMechanics::AttributeValue::getDamage);
|
||||
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);
|
||||
sol::table attributes(context.mLua->sol(), sol::create);
|
||||
stats["attributes"] = LuaUtil::makeReadOnly(attributes);
|
||||
|
@ -399,7 +399,8 @@ namespace MWLua
|
|||
auto skillStatT = context.mLua->sol().new_usertype<SkillStat>("SkillStat");
|
||||
addProp(context, skillStatT, "base", &MWMechanics::SkillValue::getBase);
|
||||
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);
|
||||
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); });
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "../stats.hpp"
|
||||
#include "actor.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["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["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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <components/settings/values.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/pathutil.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
|
|
|
@ -324,7 +324,7 @@ namespace MWMechanics
|
|||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);
|
||||
if (animation && !reflectStatic->mModel.empty())
|
||||
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);
|
||||
}
|
||||
if (removedSpell)
|
||||
|
|
|
@ -2019,6 +2019,24 @@ namespace MWMechanics
|
|||
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
|
||||
{
|
||||
const auto iter = mIndex.find(ptr.mRef);
|
||||
|
@ -2034,12 +2052,27 @@ namespace MWMechanics
|
|||
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
|
||||
{
|
||||
for (const Actor& actor : mActors)
|
||||
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
|
||||
{
|
||||
for (const Actor& actor : mActors)
|
||||
|
|
|
@ -114,9 +114,14 @@ namespace MWMechanics
|
|||
|
||||
bool playAnimationGroup(
|
||||
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;
|
||||
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) const;
|
||||
bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) 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;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/world.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();
|
||||
const DetourNavigator::AgentBounds agentBounds = world->getPathfindingAgentBounds(actor);
|
||||
|
||||
/// Stops the actor when it gets too close to a unloaded cell
|
||||
//... At current time, this test is unnecessary. AI shuts down when actor is more than "actors processing range"
|
||||
// setting value
|
||||
//... units from player, and exterior cells are 8192 units long and wide.
|
||||
/// Stops the actor when it gets too close to a unloaded cell or when the actor is playing a scripted animation
|
||||
//... At current time, the first test is unnecessary. AI shuts down when actor is more than
|
||||
//... "actors processing range" setting value units from player, and exterior cells are 8192 units long and wide.
|
||||
//... 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[1] = 0;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "../mwrender/animation.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -270,7 +271,7 @@ namespace
|
|||
case CharState_IdleSwim:
|
||||
return Priority_SwimIdle;
|
||||
case CharState_IdleSneak:
|
||||
priority[MWRender::Animation::BoneGroup_LowerBody] = Priority_SneakIdleLowerBody;
|
||||
priority[MWRender::BoneGroup_LowerBody] = Priority_SneakIdleLowerBody;
|
||||
[[fallthrough]];
|
||||
default:
|
||||
return priority;
|
||||
|
@ -444,8 +445,8 @@ namespace MWMechanics
|
|||
{
|
||||
mHitState = CharState_Block;
|
||||
priority = Priority_Hit;
|
||||
priority[MWRender::Animation::BoneGroup_LeftArm] = Priority_Block;
|
||||
priority[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
|
||||
priority[MWRender::BoneGroup_LeftArm] = Priority_Block;
|
||||
priority[MWRender::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
|
||||
startKey = "block start";
|
||||
stopKey = "block stop";
|
||||
}
|
||||
|
@ -482,8 +483,7 @@ namespace MWMechanics
|
|||
return;
|
||||
}
|
||||
|
||||
mAnimation->play(
|
||||
mCurrentHit, priority, MWRender::Animation::BlendMask_All, true, 1, startKey, stopKey, 0.0f, ~0ul);
|
||||
playBlendedAnimation(mCurrentHit, priority, MWRender::BlendMask_All, true, 1, startKey, stopKey, 0.0f, ~0ul);
|
||||
}
|
||||
|
||||
void CharacterController::refreshJumpAnims(JumpingState jump, bool force)
|
||||
|
@ -502,7 +502,7 @@ namespace MWMechanics
|
|||
std::string_view weapShortGroup = getWeaponShortGroup(mWeaponType);
|
||||
std::string jumpAnimName = "jump";
|
||||
jumpAnimName += weapShortGroup;
|
||||
MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All;
|
||||
MWRender::Animation::BlendMask jumpmask = MWRender::BlendMask_All;
|
||||
if (!weapShortGroup.empty() && !mAnimation->hasAnimation(jumpAnimName))
|
||||
jumpAnimName = fallbackShortWeaponGroup("jump", &jumpmask);
|
||||
|
||||
|
@ -520,10 +520,10 @@ namespace MWMechanics
|
|||
|
||||
mCurrentJump = jumpAnimName;
|
||||
if (mJumpState == JumpState_InAir)
|
||||
mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false, 1.0f, startAtLoop ? "loop start" : "start",
|
||||
"stop", 0.f, ~0ul);
|
||||
playBlendedAnimation(jumpAnimName, Priority_Jump, jumpmask, false, 1.0f,
|
||||
startAtLoop ? "loop start" : "start", "stop", 0.f, ~0ul);
|
||||
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
|
||||
|
@ -539,8 +539,8 @@ namespace MWMechanics
|
|||
if (mAnimation->isPlaying("containerclose"))
|
||||
return false;
|
||||
|
||||
mAnimation->play("containeropen", Priority_Scripted, MWRender::Animation::BlendMask_All, false, 1.0f,
|
||||
"start", "stop", 0.f, 0);
|
||||
mAnimation->play(
|
||||
"containeropen", Priority_Scripted, MWRender::BlendMask_All, false, 1.0f, "start", "stop", 0.f, 0);
|
||||
if (mAnimation->isPlaying("containeropen"))
|
||||
return false;
|
||||
}
|
||||
|
@ -560,8 +560,8 @@ namespace MWMechanics
|
|||
if (animPlaying)
|
||||
startPoint = 1.f - complete;
|
||||
|
||||
mAnimation->play("containerclose", Priority_Scripted, MWRender::Animation::BlendMask_All, false, 1.0f,
|
||||
"start", "stop", startPoint, 0);
|
||||
mAnimation->play("containerclose", Priority_Scripted, MWRender::BlendMask_All, false, 1.0f, "start", "stop",
|
||||
startPoint, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,7 +600,7 @@ namespace MWMechanics
|
|||
if (!isRealWeapon(mWeaponType))
|
||||
{
|
||||
if (blendMask != nullptr)
|
||||
*blendMask = MWRender::Animation::BlendMask_LowerBody;
|
||||
*blendMask = MWRender::BlendMask_LowerBody;
|
||||
|
||||
return baseGroupName;
|
||||
}
|
||||
|
@ -619,13 +619,13 @@ namespace MWMechanics
|
|||
|
||||
// Special case for crossbows - we should apply 1h animations a fallback only for lower body
|
||||
if (mWeaponType == ESM::Weapon::MarksmanCrossbow && blendMask != nullptr)
|
||||
*blendMask = MWRender::Animation::BlendMask_LowerBody;
|
||||
*blendMask = MWRender::BlendMask_LowerBody;
|
||||
|
||||
if (!mAnimation->hasAnimation(groupName))
|
||||
{
|
||||
groupName = baseGroupName;
|
||||
if (blendMask != nullptr)
|
||||
*blendMask = MWRender::Animation::BlendMask_LowerBody;
|
||||
*blendMask = MWRender::BlendMask_LowerBody;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -684,7 +684,7 @@ namespace MWMechanics
|
|||
if (!mAnimation->hasAnimation(weapMovementAnimName))
|
||||
weapMovementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);
|
||||
|
||||
movementAnimName = weapMovementAnimName;
|
||||
movementAnimName = std::move(weapMovementAnimName);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -798,7 +798,7 @@ namespace MWMechanics
|
|||
weapIdleGroup += weapShortGroup;
|
||||
if (!mAnimation->hasAnimation(weapIdleGroup))
|
||||
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
|
||||
// this replicates original engine behavior for the "Idle1h" 1st-person animation
|
||||
|
@ -820,9 +820,9 @@ namespace MWMechanics
|
|||
mAnimation->getInfo(mCurrentIdle, &startPoint);
|
||||
|
||||
clearStateAnimation(mCurrentIdle);
|
||||
mCurrentIdle = idleGroup;
|
||||
mAnimation->play(mCurrentIdle, priority, MWRender::Animation::BlendMask_All, false, 1.0f, "start", "stop",
|
||||
startPoint, numLoops, true);
|
||||
mCurrentIdle = std::move(idleGroup);
|
||||
playBlendedAnimation(
|
||||
mCurrentIdle, priority, MWRender::BlendMask_All, false, 1.0f, "start", "stop", startPoint, numLoops, true);
|
||||
}
|
||||
|
||||
void CharacterController::refreshCurrentAnims(
|
||||
|
@ -855,8 +855,8 @@ namespace MWMechanics
|
|||
resetCurrentIdleState();
|
||||
resetCurrentJumpState();
|
||||
|
||||
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::BlendMask_All, false, 1.0f, "start",
|
||||
"stop", startpoint, 0);
|
||||
playBlendedAnimation(
|
||||
mCurrentDeath, Priority_Death, MWRender::BlendMask_All, false, 1.0f, "start", "stop", startpoint, 0);
|
||||
}
|
||||
|
||||
CharacterState CharacterController::chooseRandomDeathState() const
|
||||
|
@ -998,6 +998,8 @@ namespace MWMechanics
|
|||
{
|
||||
std::string_view evt = key->second;
|
||||
|
||||
MWBase::Environment::get().getLuaManager()->animationTextKey(mPtr, key->second);
|
||||
|
||||
if (evt.substr(0, 7) == "sound: ")
|
||||
{
|
||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
|
@ -1189,8 +1191,9 @@ namespace MWMechanics
|
|||
{
|
||||
if (!animPlaying)
|
||||
{
|
||||
int mask = MWRender::Animation::BlendMask_Torso | MWRender::Animation::BlendMask_RightArm;
|
||||
mAnimation->play("idlestorm", Priority_Storm, mask, true, 1.0f, "start", "stop", 0.0f, ~0ul, true);
|
||||
int mask = MWRender::BlendMask_Torso | MWRender::BlendMask_RightArm;
|
||||
playBlendedAnimation(
|
||||
"idlestorm", Priority_Storm, mask, true, 1.0f, "start", "stop", 0.0f, ~0ul, true);
|
||||
}
|
||||
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()
|
||||
{
|
||||
// If the current animation is scripted, we can't do anything here.
|
||||
|
@ -1357,8 +1325,8 @@ namespace MWMechanics
|
|||
if (mAnimation->isPlaying("shield"))
|
||||
mAnimation->disable("shield");
|
||||
|
||||
mAnimation->play("torch", Priority_Torch, MWRender::Animation::BlendMask_LeftArm, false, 1.0f, "start",
|
||||
"stop", 0.0f, std::numeric_limits<size_t>::max(), true);
|
||||
playBlendedAnimation("torch", Priority_Torch, MWRender::BlendMask_LeftArm, false, 1.0f, "start", "stop",
|
||||
0.0f, std::numeric_limits<size_t>::max(), true);
|
||||
}
|
||||
else if (mAnimation->isPlaying("torch"))
|
||||
{
|
||||
|
@ -1369,7 +1337,7 @@ namespace MWMechanics
|
|||
// For biped actors, blend weapon animations with lower body animations with higher priority
|
||||
MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon);
|
||||
if (cls.isBipedal(mPtr))
|
||||
priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
|
||||
priorityWeapon[MWRender::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
|
||||
|
||||
bool forcestateupdate = false;
|
||||
|
||||
|
@ -1400,19 +1368,19 @@ namespace MWMechanics
|
|||
{
|
||||
// Note: we do not disable unequipping animation automatically to avoid body desync
|
||||
weapgroup = getWeaponAnimation(mWeaponType);
|
||||
int unequipMask = MWRender::Animation::BlendMask_All;
|
||||
int unequipMask = MWRender::BlendMask_All;
|
||||
bool useShieldAnims = mAnimation->useShieldAnimations();
|
||||
if (useShieldAnims && mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell
|
||||
&& !(mWeaponType == ESM::Weapon::None && weaptype == ESM::Weapon::Spell))
|
||||
{
|
||||
unequipMask = unequipMask | ~MWRender::Animation::BlendMask_LeftArm;
|
||||
mAnimation->play("shield", Priority_Block, MWRender::Animation::BlendMask_LeftArm, true, 1.0f,
|
||||
unequipMask = unequipMask | ~MWRender::BlendMask_LeftArm;
|
||||
playBlendedAnimation("shield", Priority_Block, MWRender::BlendMask_LeftArm, true, 1.0f,
|
||||
"unequip start", "unequip stop", 0.0f, 0);
|
||||
}
|
||||
else if (mWeaponType == ESM::Weapon::HandToHand)
|
||||
mAnimation->showCarriedLeft(false);
|
||||
|
||||
mAnimation->play(
|
||||
playBlendedAnimation(
|
||||
weapgroup, priorityWeapon, unequipMask, false, 1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperBodyState::Unequipping;
|
||||
|
||||
|
@ -1458,15 +1426,15 @@ namespace MWMechanics
|
|||
if (weaptype != ESM::Weapon::None)
|
||||
{
|
||||
mAnimation->showWeapons(false);
|
||||
int equipMask = MWRender::Animation::BlendMask_All;
|
||||
int equipMask = MWRender::BlendMask_All;
|
||||
if (useShieldAnims && weaptype != ESM::Weapon::Spell)
|
||||
{
|
||||
equipMask = equipMask | ~MWRender::Animation::BlendMask_LeftArm;
|
||||
mAnimation->play("shield", Priority_Block, MWRender::Animation::BlendMask_LeftArm, true,
|
||||
1.0f, "equip start", "equip stop", 0.0f, 0);
|
||||
equipMask = equipMask | ~MWRender::BlendMask_LeftArm;
|
||||
playBlendedAnimation("shield", Priority_Block, MWRender::BlendMask_LeftArm, true, 1.0f,
|
||||
"equip start", "equip stop", 0.0f, 0);
|
||||
}
|
||||
|
||||
mAnimation->play(
|
||||
playBlendedAnimation(
|
||||
weapgroup, priorityWeapon, equipMask, true, 1.0f, "equip start", "equip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperBodyState::Equipping;
|
||||
|
||||
|
@ -1617,11 +1585,11 @@ namespace MWMechanics
|
|||
|
||||
if (mAnimation->getNode("Bip01 L Hand"))
|
||||
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"))
|
||||
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
|
||||
const ESM::ENAMstruct& firstEffect = effects->front();
|
||||
|
@ -1656,9 +1624,8 @@ namespace MWMechanics
|
|||
startKey = mAttackType + " start";
|
||||
stopKey = mAttackType + " stop";
|
||||
}
|
||||
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false,
|
||||
1, startKey, stopKey, 0.0f, 0);
|
||||
playBlendedAnimation(mCurrentWeapon, priorityWeapon, MWRender::BlendMask_All, false, 1,
|
||||
startKey, stopKey, 0.0f, 0);
|
||||
mUpperBodyState = UpperBodyState::Casting;
|
||||
}
|
||||
}
|
||||
|
@ -1709,8 +1676,8 @@ namespace MWMechanics
|
|||
mAttackVictim = MWWorld::Ptr();
|
||||
mAttackHitPos = osg::Vec3f();
|
||||
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false,
|
||||
weapSpeed, startKey, stopKey, 0.0f, 0);
|
||||
playBlendedAnimation(mCurrentWeapon, priorityWeapon, MWRender::BlendMask_All, false, weapSpeed,
|
||||
startKey, stopKey, 0.0f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1783,7 +1750,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1813,7 +1780,7 @@ namespace MWMechanics
|
|||
// Follow animations have lower priority than movement for non-biped creatures, logic be damned
|
||||
if (!cls.isBipedal(mPtr))
|
||||
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);
|
||||
mUpperBodyState = UpperBodyState::AttackEnd;
|
||||
|
||||
|
@ -1935,9 +1902,16 @@ namespace MWMechanics
|
|||
mIdleState = CharState_SpecialIdle;
|
||||
auto priority = mAnimQueue.front().mScripted ? Priority_Scripted : Priority_Default;
|
||||
mAnimation->setPlayScriptedOnly(mAnimQueue.front().mScripted);
|
||||
mAnimation->play(mAnimQueue.front().mGroup, priority, MWRender::Animation::BlendMask_All, false, 1.0f,
|
||||
(loopStart ? "loop start" : "start"), "stop", mAnimQueue.front().mTime, mAnimQueue.front().mLoopCount,
|
||||
mAnimQueue.front().mLooping);
|
||||
if (mAnimQueue.front().mScripted)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2504,6 +2478,7 @@ namespace MWMechanics
|
|||
state.mScriptedAnims.clear();
|
||||
for (AnimationQueue::const_iterator iter = mAnimQueue.begin(); iter != mAnimQueue.end(); ++iter)
|
||||
{
|
||||
// TODO: Probably want to presist lua animations too
|
||||
if (!iter->mScripted)
|
||||
continue;
|
||||
|
||||
|
@ -2541,8 +2516,10 @@ namespace MWMechanics
|
|||
AnimationQueueEntry entry;
|
||||
entry.mGroup = iter->mGroup;
|
||||
entry.mLoopCount = iter->mLoopCount;
|
||||
entry.mScripted = true;
|
||||
entry.mLooping = isLoopingAnimation(entry.mGroup);
|
||||
entry.mLooping = mAnimation->isLoopingAnimation(entry.mGroup);
|
||||
entry.mStartKey = "start";
|
||||
entry.mStopKey = "stop";
|
||||
entry.mSpeed = 1.f;
|
||||
entry.mTime = iter->mTime;
|
||||
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)
|
||||
{
|
||||
if (!mAnimation || !mAnimation->hasAnimation(groupname))
|
||||
|
@ -2568,7 +2557,7 @@ namespace MWMechanics
|
|||
if (isScriptedAnimPlaying() && !scripted)
|
||||
return true;
|
||||
|
||||
bool looping = isLoopingAnimation(groupname);
|
||||
bool looping = mAnimation->isLoopingAnimation(groupname);
|
||||
|
||||
// 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
|
||||
|
@ -2602,8 +2591,12 @@ namespace MWMechanics
|
|||
entry.mGroup = groupname;
|
||||
entry.mLoopCount = count;
|
||||
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.mSpeed = 1.f;
|
||||
entry.mStartKey = ((mode == 2) ? "loop start" : "start");
|
||||
entry.mStopKey = "stop";
|
||||
|
||||
bool playImmediately = false;
|
||||
|
||||
|
@ -2618,10 +2611,6 @@ namespace MWMechanics
|
|||
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);
|
||||
|
||||
if (playImmediately)
|
||||
|
@ -2630,6 +2619,42 @@ namespace MWMechanics
|
|||
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()
|
||||
{
|
||||
mSkipAnim = true;
|
||||
|
@ -2745,18 +2770,20 @@ namespace MWMechanics
|
|||
// as it's extremely spread out (ActiveSpells, Spells, InventoryStore effects, etc...) so we do it here.
|
||||
|
||||
// Stop any effects that are no longer active
|
||||
std::vector<int> effects;
|
||||
mAnimation->getLoopingEffects(effects);
|
||||
std::vector<std::string_view> effects = mAnimation->getLoopingEffects();
|
||||
|
||||
for (int effectId : effects)
|
||||
for (std::string_view effectId : effects)
|
||||
{
|
||||
if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished()
|
||||
|| mPtr.getClass()
|
||||
.getCreatureStats(mPtr)
|
||||
.getMagicEffects()
|
||||
.getOrDefault(MWMechanics::EffectKey(effectId))
|
||||
.getMagnitude()
|
||||
<= 0)
|
||||
auto index = ESM::MagicEffect::indexNameToIndex(effectId);
|
||||
|
||||
if (index >= 0
|
||||
&& (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished()
|
||||
|| mPtr.getClass()
|
||||
.getCreatureStats(mPtr)
|
||||
.getMagicEffects()
|
||||
.getOrDefault(MWMechanics::EffectKey(index))
|
||||
.getMagnitude()
|
||||
<= 0))
|
||||
mAnimation->removeEffect(effectId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,9 +138,13 @@ namespace MWMechanics
|
|||
float mTime;
|
||||
bool mLooping;
|
||||
bool mScripted;
|
||||
std::string mStartKey;
|
||||
std::string mStopKey;
|
||||
float mSpeed;
|
||||
};
|
||||
typedef std::deque<AnimationQueueEntry> AnimationQueue;
|
||||
AnimationQueue mAnimQueue;
|
||||
bool mLuaAnimations{ false };
|
||||
|
||||
CharacterState mIdleState{ CharState_None };
|
||||
std::string mCurrentIdle;
|
||||
|
@ -209,15 +213,12 @@ namespace MWMechanics
|
|||
void refreshMovementAnims(CharacterState movement, bool force = false);
|
||||
void refreshIdleAnims(CharacterState idle, bool force = false);
|
||||
|
||||
void clearAnimQueue(bool clearScriptedAnims = false);
|
||||
|
||||
bool updateWeaponState();
|
||||
void updateIdleStormState(bool inwater) const;
|
||||
|
||||
std::string chooseRandomAttackAnimation() const;
|
||||
static bool isRandomAttackAnimation(std::string_view group);
|
||||
|
||||
bool isScriptedAnimPlaying() const;
|
||||
bool isMovementAnimationControlled() const;
|
||||
|
||||
void updateAnimQueue();
|
||||
|
@ -248,8 +249,6 @@ namespace MWMechanics
|
|||
|
||||
void prepareHit();
|
||||
|
||||
bool isLoopingAnimation(std::string_view group) const;
|
||||
|
||||
public:
|
||||
CharacterController(const MWWorld::Ptr& ptr, MWRender::Animation* anim);
|
||||
virtual ~CharacterController();
|
||||
|
@ -275,9 +274,17 @@ namespace MWMechanics
|
|||
void persistAnimationState() const;
|
||||
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 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();
|
||||
bool isAnimPlaying(std::string_view groupName) const;
|
||||
bool isScriptedAnimPlaying() const;
|
||||
void clearAnimQueue(bool clearScriptedAnims = false);
|
||||
|
||||
enum KillResult
|
||||
{
|
||||
|
|
|
@ -135,6 +135,15 @@ namespace MWMechanics
|
|||
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||
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
|
||||
int shieldhealth = shield->getClass().getItemHealth(*shield);
|
||||
|
||||
|
|
|
@ -756,6 +756,21 @@ namespace MWMechanics
|
|||
else
|
||||
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)
|
||||
{
|
||||
if (ptr.getClass().isActor())
|
||||
|
@ -771,6 +786,14 @@ namespace MWMechanics
|
|||
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)
|
||||
{
|
||||
if (ptr.getClass().isActor())
|
||||
|
@ -791,6 +814,14 @@ namespace MWMechanics
|
|||
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)
|
||||
{
|
||||
mActors.updateMagicEffects(ptr);
|
||||
|
|
|
@ -143,9 +143,14 @@ namespace MWMechanics
|
|||
/// @return Success or error
|
||||
bool playAnimationGroup(
|
||||
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;
|
||||
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) override;
|
||||
bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const 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
|
||||
/// paused we may want to do it manually (after equipping permanent enchantment)
|
||||
|
|
|
@ -113,6 +113,23 @@ namespace MWMechanics
|
|||
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)
|
||||
{
|
||||
const auto iter = mIndex.find(ptr.mRef);
|
||||
|
@ -126,6 +143,13 @@ namespace MWMechanics
|
|||
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
|
||||
{
|
||||
for (const CharacterController& object : mObjects)
|
||||
|
|
|
@ -47,8 +47,12 @@ namespace MWMechanics
|
|||
|
||||
bool playAnimationGroup(
|
||||
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 persistAnimationStates();
|
||||
void clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted);
|
||||
|
||||
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);
|
||||
if (animation)
|
||||
{
|
||||
animation->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel), effect->mIndex, false,
|
||||
{}, effect->mParticle);
|
||||
animation->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel),
|
||||
ESM::MagicEffect::indexToName(effect->mIndex), false, {}, effect->mParticle);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -626,8 +626,8 @@ namespace MWMechanics
|
|||
{
|
||||
// Don't play particle VFX unless the effect is new or it should be looping.
|
||||
if (playNonLooping || loop)
|
||||
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel), magicEffect.mIndex, loop,
|
||||
{}, magicEffect.mParticle);
|
||||
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel),
|
||||
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"));
|
||||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);
|
||||
if (animation && !absorbStatic->mModel.empty())
|
||||
animation->addEffect(
|
||||
Misc::ResourceHelpers::correctMeshPath(absorbStatic->mModel), ESM::MagicEffect::SpellAbsorption, false);
|
||||
animation->addEffect(Misc::ResourceHelpers::correctMeshPath(absorbStatic->mModel),
|
||||
ESM::MagicEffect::indexToName(ESM::MagicEffect::SpellAbsorption), false);
|
||||
const ESM::Spell* spell = esmStore.get<ESM::Spell>().search(spellId);
|
||||
int spellCost = 0;
|
||||
if (spell)
|
||||
|
@ -455,11 +455,11 @@ namespace MWMechanics
|
|||
if (!caster.isEmpty())
|
||||
{
|
||||
MWRender::Animation* anim = world->getAnimation(caster);
|
||||
anim->removeEffect(effect.mEffectId);
|
||||
anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId));
|
||||
const ESM::Static* fx
|
||||
= world->getStore().get<ESM::Static>().search(ESM::RefId::stringRefId("VFX_Summon_end"));
|
||||
if (fx)
|
||||
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(fx->mModel), -1);
|
||||
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(fx->mModel), "");
|
||||
}
|
||||
}
|
||||
else if (caster == getPlayer())
|
||||
|
@ -490,7 +490,7 @@ namespace MWMechanics
|
|||
if (!caster.isEmpty())
|
||||
{
|
||||
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;
|
||||
auto anim = world->getAnimation(target);
|
||||
if (anim)
|
||||
anim->removeEffect(effect.mEffectId);
|
||||
anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId));
|
||||
}
|
||||
else
|
||||
effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove;
|
||||
|
@ -1287,7 +1287,7 @@ namespace MWMechanics
|
|||
{
|
||||
auto anim = MWBase::Environment::get().getWorld()->getAnimation(target);
|
||||
if (anim)
|
||||
anim->removeEffect(effect.mEffectId);
|
||||
anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ namespace MWMechanics
|
|||
const ESM::Static* fx
|
||||
= world->getStore().get<ESM::Static>().search(ESM::RefId::stringRefId("VFX_Summon_Start"));
|
||||
if (fx)
|
||||
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(fx->mModel), -1, false);
|
||||
anim->addEffect(Misc::ResourceHelpers::correctMeshPath(fx->mModel), "", false);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
|
|
|
@ -101,7 +101,7 @@ namespace MWRender
|
|||
templateNode, mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager(), &rotation);
|
||||
}
|
||||
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
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <components/sceneutil/keyframe.hpp>
|
||||
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/sceneutil/lightutil.hpp>
|
||||
|
@ -45,6 +46,7 @@
|
|||
#include <components/settings/values.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -52,6 +54,7 @@
|
|||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwmechanics/character.hpp" // FIXME: for MWMechanics::Priority
|
||||
#include "../mwmechanics/weapontype.hpp"
|
||||
|
||||
#include "actorutil.hpp"
|
||||
#include "rotatecontroller.hpp"
|
||||
|
@ -300,11 +303,10 @@ namespace
|
|||
RemoveCallbackVisitor()
|
||||
: RemoveVisitor()
|
||||
, mHasMagicEffects(false)
|
||||
, mEffectId(-1)
|
||||
{
|
||||
}
|
||||
|
||||
RemoveCallbackVisitor(int effectId)
|
||||
RemoveCallbackVisitor(std::string_view effectId)
|
||||
: RemoveVisitor()
|
||||
, mHasMagicEffects(false)
|
||||
, mEffectId(effectId)
|
||||
|
@ -323,7 +325,7 @@ namespace
|
|||
MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast<MWRender::UpdateVfxCallback*>(callback);
|
||||
if (vfxCallback)
|
||||
{
|
||||
bool toRemove = mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId;
|
||||
bool toRemove = mEffectId == "" || vfxCallback->mParams.mEffectId == mEffectId;
|
||||
if (toRemove)
|
||||
mToRemove.emplace_back(group.asNode(), group.getParent(0));
|
||||
else
|
||||
|
@ -337,7 +339,7 @@ namespace
|
|||
void apply(osg::Geometry&) override {}
|
||||
|
||||
private:
|
||||
int mEffectId;
|
||||
std::string_view mEffectId;
|
||||
};
|
||||
|
||||
class FindVfxCallbacksVisitor : public osg::NodeVisitor
|
||||
|
@ -347,11 +349,10 @@ namespace
|
|||
|
||||
FindVfxCallbacksVisitor()
|
||||
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||
, mEffectId(-1)
|
||||
{
|
||||
}
|
||||
|
||||
FindVfxCallbacksVisitor(int effectId)
|
||||
FindVfxCallbacksVisitor(std::string_view effectId)
|
||||
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||
, mEffectId(effectId)
|
||||
{
|
||||
|
@ -367,7 +368,7 @@ namespace
|
|||
MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast<MWRender::UpdateVfxCallback*>(callback);
|
||||
if (vfxCallback)
|
||||
{
|
||||
if (mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId)
|
||||
if (mEffectId == "" || vfxCallback->mParams.mEffectId == mEffectId)
|
||||
{
|
||||
mCallbacks.push_back(vfxCallback);
|
||||
}
|
||||
|
@ -381,7 +382,7 @@ namespace
|
|||
void apply(osg::Geometry&) override {}
|
||||
|
||||
private:
|
||||
int mEffectId;
|
||||
std::string_view mEffectId;
|
||||
};
|
||||
|
||||
osg::ref_ptr<osg::LightModel> getVFXLightModelInstance()
|
||||
|
@ -446,7 +447,7 @@ namespace MWRender
|
|||
|
||||
typedef std::map<std::string, osg::ref_ptr<SceneUtil::KeyframeController>> ControllerMap;
|
||||
|
||||
ControllerMap mControllerMap[Animation::sNumBlendMasks];
|
||||
ControllerMap mControllerMap[sNumBlendMasks];
|
||||
|
||||
const SceneUtil::TextKeyMap& getTextKeys() const;
|
||||
};
|
||||
|
@ -714,6 +715,41 @@ namespace MWRender
|
|||
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
|
||||
{
|
||||
for (AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)
|
||||
|
@ -757,16 +793,14 @@ namespace MWRender
|
|||
state.mLoopStopTime = key->first;
|
||||
}
|
||||
|
||||
if (mTextKeyListener)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
if (mTextKeyListener != nullptr)
|
||||
mTextKeyListener->handleTextKey(groupname, key, map);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "Error handling text key " << evt << ": " << e.what();
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "Error handling text key " << evt << ": " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -922,7 +956,7 @@ namespace MWRender
|
|||
return true;
|
||||
}
|
||||
|
||||
void Animation::setTextKeyListener(Animation::TextKeyListener* listener)
|
||||
void Animation::setTextKeyListener(TextKeyListener* listener)
|
||||
{
|
||||
mTextKeyListener = listener;
|
||||
}
|
||||
|
@ -1051,7 +1085,16 @@ namespace MWRender
|
|||
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);
|
||||
if (iter == mStates.end())
|
||||
|
@ -1495,8 +1538,8 @@ namespace MWRender
|
|||
mExtraLightSource->setActorFade(mAlpha);
|
||||
}
|
||||
|
||||
void Animation::addEffect(
|
||||
const std::string& model, int effectId, bool loop, std::string_view bonename, std::string_view texture)
|
||||
void Animation::addEffect(std::string_view model, std::string_view effectId, bool loop, std::string_view bonename,
|
||||
std::string_view texture)
|
||||
{
|
||||
if (!mObjectRoot.get())
|
||||
return;
|
||||
|
@ -1578,7 +1621,7 @@ namespace MWRender
|
|||
overrideFirstRootTexture(texture, mResourceSystem, *node);
|
||||
}
|
||||
|
||||
void Animation::removeEffect(int effectId)
|
||||
void Animation::removeEffect(std::string_view effectId)
|
||||
{
|
||||
RemoveCallbackVisitor visitor(effectId);
|
||||
mInsert->accept(visitor);
|
||||
|
@ -1588,17 +1631,19 @@ namespace MWRender
|
|||
|
||||
void Animation::removeEffects()
|
||||
{
|
||||
removeEffect(-1);
|
||||
removeEffect("");
|
||||
}
|
||||
|
||||
void Animation::getLoopingEffects(std::vector<int>& out) const
|
||||
std::vector<std::string_view> Animation::getLoopingEffects() const
|
||||
{
|
||||
if (!mHasMagicEffects)
|
||||
return;
|
||||
return {};
|
||||
|
||||
FindVfxCallbacksVisitor visitor;
|
||||
mInsert->accept(visitor);
|
||||
|
||||
std::vector<std::string_view> out;
|
||||
|
||||
for (std::vector<UpdateVfxCallback*>::iterator it = visitor.mCallbacks.begin(); it != visitor.mCallbacks.end();
|
||||
++it)
|
||||
{
|
||||
|
@ -1607,6 +1652,7 @@ namespace MWRender
|
|||
if (callback->mParams.mLoop && !callback->mFinished)
|
||||
out.push_back(callback->mParams.mEffectId);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void Animation::updateEffects()
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#ifndef GAME_RENDER_ANIMATION_H
|
||||
#define GAME_RENDER_ANIMATION_H
|
||||
|
||||
#include "animationpriority.hpp"
|
||||
#include "blendmask.hpp"
|
||||
#include "bonegroup.hpp"
|
||||
|
||||
#include "../mwworld/movementdirection.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::shared_ptr<EffectAnimationTime> mAnimTime;
|
||||
float mMaxControllerLength;
|
||||
int mEffectId;
|
||||
std::string mEffectId;
|
||||
bool mLoop;
|
||||
std::string mBoneName;
|
||||
};
|
||||
|
@ -92,60 +96,9 @@ namespace MWRender
|
|||
class Animation : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
enum BoneGroup
|
||||
{
|
||||
BoneGroup_LowerBody = 0,
|
||||
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];
|
||||
};
|
||||
using BlendMask = MWRender::BlendMask;
|
||||
using BoneGroup = MWRender::BoneGroup;
|
||||
using AnimPriority = MWRender::AnimPriority;
|
||||
|
||||
class TextKeyListener
|
||||
{
|
||||
|
@ -384,11 +337,11 @@ namespace MWRender
|
|||
* @param texture override the texture specified in the model's materials - if empty, do not override
|
||||
* @note Will not add an effect twice.
|
||||
*/
|
||||
void addEffect(const std::string& model, int effectId, bool loop = false, std::string_view bonename = {},
|
||||
std::string_view texture = {});
|
||||
void removeEffect(int effectId);
|
||||
void addEffect(std::string_view model, std::string_view effectId, bool loop = false,
|
||||
std::string_view bonename = {}, std::string_view texture = {});
|
||||
void removeEffect(std::string_view effectId);
|
||||
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,
|
||||
// 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 isLoopingAnimation(std::string_view group) const;
|
||||
|
||||
// Specifies the axis' to accumulate on. Non-accumulated axis will just
|
||||
// move visually, but not affect the actual movement. Each x/y/z value
|
||||
// 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,
|
||||
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.
|
||||
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
|
||||
/// the given group.
|
||||
float getCurrentTime(const std::string& groupname) const;
|
||||
float getCurrentTime(std::string_view groupname) const;
|
||||
|
||||
/** Disables the specified animation group;
|
||||
* \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
|
||||
if (mAnimation->hasAnimation(inventoryGroup))
|
||||
groupname = inventoryGroup;
|
||||
groupname = std::move(inventoryGroup);
|
||||
else
|
||||
{
|
||||
static const std::string oneHandFallback
|
||||
|
@ -456,15 +456,15 @@ namespace MWRender
|
|||
|
||||
mAnimation->showCarriedLeft(showCarriedLeft);
|
||||
|
||||
mCurrentAnimGroup = groupname;
|
||||
mAnimation->play(mCurrentAnimGroup, 1, Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0);
|
||||
mCurrentAnimGroup = std::move(groupname);
|
||||
mAnimation->play(mCurrentAnimGroup, 1, BlendMask::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0);
|
||||
|
||||
MWWorld::ConstContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
||||
if (torch != inv.end() && torch->getType() == ESM::Light::sRecordId && showCarriedLeft)
|
||||
{
|
||||
if (!mAnimation->getInfo("torch"))
|
||||
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"))
|
||||
mAnimation->disable("torch");
|
||||
|
@ -591,7 +591,7 @@ namespace MWRender
|
|||
void RaceSelectionPreview::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);
|
||||
|
||||
// attach camera to follow the head node
|
||||
|
|
|
@ -170,7 +170,7 @@ namespace MWRender
|
|||
else
|
||||
source = mAnimationTimePtr[0];
|
||||
|
||||
SceneUtil::AssignControllerSourcesVisitor assignVisitor(source);
|
||||
SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::move(source));
|
||||
attached->accept(assignVisitor);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
|
|
|
@ -363,7 +363,7 @@ namespace MWRender
|
|||
imageDest.mImage = image;
|
||||
imageDest.mX = x;
|
||||
imageDest.mY = y;
|
||||
mPendingImageDest[camera] = imageDest;
|
||||
mPendingImageDest[camera] = std::move(imageDest);
|
||||
}
|
||||
|
||||
// Create a quad rendering the updated texture
|
||||
|
@ -422,7 +422,8 @@ namespace MWRender
|
|||
if (cellX > mMaxX || cellX < mMinX || cellY > mMaxY || cellY < mMinY)
|
||||
return;
|
||||
|
||||
requestOverlayTextureUpdate(originX, mHeight - originY, cellSize, cellSize, localMapTexture, false, true);
|
||||
requestOverlayTextureUpdate(
|
||||
originX, mHeight - originY, cellSize, cellSize, std::move(localMapTexture), false, true);
|
||||
}
|
||||
|
||||
void GlobalMap::clear()
|
||||
|
@ -554,7 +555,7 @@ namespace MWRender
|
|||
{
|
||||
mOverlayImage = image;
|
||||
|
||||
requestOverlayTextureUpdate(0, 0, mWidth, mHeight, texture, true, false);
|
||||
requestOverlayTextureUpdate(0, 0, mWidth, mHeight, std::move(texture), true, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -562,7 +563,7 @@ namespace MWRender
|
|||
// In the latter case, we'll want filtering.
|
||||
// Create a RTT Camera and draw the image onto mOverlayImage in the next frame.
|
||||
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.mBottom / float(imageHeight));
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace MWRender
|
|||
auto resolveFragment = shaderManager.getShader("luminance/resolve.frag", defines);
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -853,7 +853,7 @@ namespace MWRender
|
|||
src = mWeaponAnimationTime;
|
||||
else
|
||||
src = mAnimationTimePtr[0];
|
||||
SceneUtil::AssignControllerSourcesVisitor assignVisitor(src);
|
||||
SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::move(src));
|
||||
node->accept(assignVisitor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace MWRender
|
|||
ptr.getClass().adjustScale(ptr, scaleVec, true);
|
||||
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)
|
||||
|
|
|
@ -288,6 +288,8 @@ namespace MWRender
|
|||
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)
|
||||
.getTexture()));
|
||||
|
||||
assert(texture != nullptr);
|
||||
|
||||
texture->setTextureSize(w, h);
|
||||
texture->setNumMipmapLevels(pass.mRenderTexture->getNumMipmapLevels());
|
||||
texture->dirtyTextureObject();
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <components/stereo/multiview.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/recursivedirectoryiterator.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -662,6 +663,11 @@ namespace MWRender
|
|||
|
||||
for (const auto& name : pass->getRenderTargets())
|
||||
{
|
||||
if (name.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& renderTarget = technique->getRenderTargetsMap()[name];
|
||||
subPass.mStateSet->setTextureAttribute(subTexUnit, renderTarget.mTarget);
|
||||
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