mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 16:29:55 +00:00
Merge branch openmw:master into handtohand-tooltip
This commit is contained in:
commit
3d2dd9201d
78 changed files with 867 additions and 578 deletions
4
.github/workflows/openmw.yml
vendored
4
.github/workflows/openmw.yml
vendored
|
@ -83,9 +83,7 @@ jobs:
|
||||||
max-size: 1000M
|
max-size: 1000M
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: CI/before_script.osx.sh
|
||||||
rm -fr build # remove the build directory
|
|
||||||
CI/before_script.osx.sh
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
|
|
|
@ -501,7 +501,6 @@ Ubuntu_GCC_integration_tests_asan:
|
||||||
paths:
|
paths:
|
||||||
- ccache/
|
- ccache/
|
||||||
script:
|
script:
|
||||||
- rm -fr build # remove the build directory
|
|
||||||
- CI/before_install.osx.sh
|
- CI/before_install.osx.sh
|
||||||
- export CCACHE_BASEDIR="$(pwd)"
|
- export CCACHE_BASEDIR="$(pwd)"
|
||||||
- export CCACHE_DIR="$(pwd)/ccache"
|
- export CCACHE_DIR="$(pwd)/ccache"
|
||||||
|
@ -521,7 +520,6 @@ Ubuntu_GCC_integration_tests_asan:
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/OpenMW-*.dmg
|
- build/OpenMW-*.dmg
|
||||||
- "build/**/*.log"
|
|
||||||
|
|
||||||
macOS14_Xcode15_arm64:
|
macOS14_Xcode15_arm64:
|
||||||
extends: .MacOS
|
extends: .MacOS
|
||||||
|
|
|
@ -191,6 +191,7 @@
|
||||||
Bug #8097: GetEffect doesn't detect 0 magnitude spells
|
Bug #8097: GetEffect doesn't detect 0 magnitude spells
|
||||||
Bug #8124: Normal weapon resistance is applied twice for NPCs
|
Bug #8124: Normal weapon resistance is applied twice for NPCs
|
||||||
Bug #8132: Actors without hello responses turn to face the player
|
Bug #8132: Actors without hello responses turn to face the player
|
||||||
|
Bug #8171: Items with more than 100% health can be repaired
|
||||||
Feature #1415: Infinite fall failsafe
|
Feature #1415: Infinite fall failsafe
|
||||||
Feature #2566: Handle NAM9 records for manual cell references
|
Feature #2566: Handle NAM9 records for manual cell references
|
||||||
Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking
|
Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking
|
||||||
|
|
|
@ -4,14 +4,9 @@ export HOMEBREW_NO_EMOJI=1
|
||||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||||
export HOMEBREW_AUTOREMOVE=1
|
export HOMEBREW_AUTOREMOVE=1
|
||||||
|
|
||||||
# workaround for gitlab's pre-installed brew
|
|
||||||
# purge large and unnecessary packages that get in our way and have caused issues
|
|
||||||
brew uninstall ruby php openjdk node postgresql maven curl || true
|
|
||||||
|
|
||||||
brew tap --repair
|
brew tap --repair
|
||||||
brew update --quiet
|
brew update --quiet
|
||||||
|
|
||||||
# Some of these tools can come from places other than brew, so check before installing
|
|
||||||
brew install curl xquartz gd fontconfig freetype harfbuzz brotli
|
brew install curl xquartz gd fontconfig freetype harfbuzz brotli
|
||||||
|
|
||||||
command -v ccache >/dev/null 2>&1 || brew install ccache
|
command -v ccache >/dev/null 2>&1 || brew install ccache
|
||||||
|
@ -27,8 +22,9 @@ cmake --version
|
||||||
qmake --version
|
qmake --version
|
||||||
|
|
||||||
if [[ "${MACOS_AMD64}" ]]; then
|
if [[ "${MACOS_AMD64}" ]]; then
|
||||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20221113.zip -o ~/openmw-deps.zip
|
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240802.zip -o ~/openmw-deps.zip
|
||||||
else
|
|
||||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240802_arm64.zip -o ~/openmw-deps.zip
|
|
||||||
fi
|
|
||||||
unzip -o ~/openmw-deps.zip -d /tmp > /dev/null
|
unzip -o ~/openmw-deps.zip -d /tmp > /dev/null
|
||||||
|
else
|
||||||
|
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240818-arm64.tar.xz -o ~/openmw-deps.tar.xz
|
||||||
|
tar xf ~/openmw-deps.tar.xz -C /tmp > /dev/null
|
||||||
|
fi
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Silence a git warning
|
# Silence a git warning
|
||||||
git config --global advice.detachedHead false
|
git config --global advice.detachedHead false
|
||||||
|
|
||||||
|
rm -fr build
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,8 @@ namespace
|
||||||
{
|
{
|
||||||
const ObjectId id(&shape);
|
const ObjectId id(&shape);
|
||||||
osg::ref_ptr<Resource::BulletShape> bulletShape(new Resource::BulletShape);
|
osg::ref_ptr<Resource::BulletShape> bulletShape(new Resource::BulletShape);
|
||||||
bulletShape->mFileName = "test.nif";
|
constexpr VFS::Path::NormalizedView test("test.nif");
|
||||||
|
bulletShape->mFileName = test;
|
||||||
bulletShape->mFileHash = "test_hash";
|
bulletShape->mFileHash = "test_hash";
|
||||||
ObjectTransform objectTransform;
|
ObjectTransform objectTransform;
|
||||||
std::fill(std::begin(objectTransform.mPosition.pos), std::end(objectTransform.mPosition.pos), 0.1f);
|
std::fill(std::begin(objectTransform.mPosition.pos), std::end(objectTransform.mPosition.pos), 0.1f);
|
||||||
|
|
|
@ -131,7 +131,8 @@ namespace NavMeshTool
|
||||||
osg::ref_ptr<const Resource::BulletShape> shape = [&] {
|
osg::ref_ptr<const Resource::BulletShape> shape = [&] {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return bulletShapeManager.getShape(Misc::ResourceHelpers::correctMeshPath(model));
|
return bulletShapeManager.getShape(
|
||||||
|
VFS::Path::toNormalized(Misc::ResourceHelpers::correctMeshPath(model)));
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -408,12 +408,12 @@ void CSMPrefs::State::declare()
|
||||||
declareShortcut(mValues->mKeyBindings.mScriptEditorUncomment, "Uncomment Selection");
|
declareShortcut(mValues->mKeyBindings.mScriptEditorUncomment, "Uncomment Selection");
|
||||||
|
|
||||||
declareCategory("Models");
|
declareCategory("Models");
|
||||||
declareString(mValues->mModels.mBaseanim, "base animations").setTooltip("3rd person base model with textkeys-data");
|
declareString(mValues->mModels.mBaseanim, "Base Animations").setTooltip("Third person base model and animations");
|
||||||
declareString(mValues->mModels.mBaseanimkna, "base animations, kna")
|
declareString(mValues->mModels.mBaseanimkna, "Base Animations, Beast")
|
||||||
.setTooltip("3rd person beast race base model with textkeys-data");
|
.setTooltip("Third person beast race base model and animations");
|
||||||
declareString(mValues->mModels.mBaseanimfemale, "base animations, female")
|
declareString(mValues->mModels.mBaseanimfemale, "Base Animations, Female")
|
||||||
.setTooltip("3rd person female base model with textkeys-data");
|
.setTooltip("Third person female base model and animations");
|
||||||
declareString(mValues->mModels.mWolfskin, "base animations, wolf").setTooltip("3rd person werewolf skin");
|
declareString(mValues->mModels.mWolfskin, "Base Animations, Werewolf").setTooltip("Third person werewolf skin");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMPrefs::State::declareCategory(const std::string& key)
|
void CSMPrefs::State::declareCategory(const std::string& key)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
#include "stringsetting.hpp"
|
#include "stringsetting.hpp"
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
|
|
||||||
|
@ -26,17 +27,21 @@ CSMPrefs::StringSetting& CSMPrefs::StringSetting::setTooltip(const std::string&
|
||||||
|
|
||||||
CSMPrefs::SettingWidgets CSMPrefs::StringSetting::makeWidgets(QWidget* parent)
|
CSMPrefs::SettingWidgets CSMPrefs::StringSetting::makeWidgets(QWidget* parent)
|
||||||
{
|
{
|
||||||
|
QLabel* label = new QLabel(getLabel(), parent);
|
||||||
|
|
||||||
mWidget = new QLineEdit(QString::fromStdString(getValue()), parent);
|
mWidget = new QLineEdit(QString::fromStdString(getValue()), parent);
|
||||||
|
mWidget->setMinimumWidth(300);
|
||||||
|
|
||||||
if (!mTooltip.empty())
|
if (!mTooltip.empty())
|
||||||
{
|
{
|
||||||
QString tooltip = QString::fromUtf8(mTooltip.c_str());
|
QString tooltip = QString::fromUtf8(mTooltip.c_str());
|
||||||
|
label->setToolTip(tooltip);
|
||||||
mWidget->setToolTip(tooltip);
|
mWidget->setToolTip(tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(mWidget, &QLineEdit::textChanged, this, &StringSetting::textChanged);
|
connect(mWidget, &QLineEdit::textChanged, this, &StringSetting::textChanged);
|
||||||
|
|
||||||
return SettingWidgets{ .mLabel = nullptr, .mInput = mWidget };
|
return SettingWidgets{ .mLabel = label, .mInput = mWidget };
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMPrefs::StringSetting::updateWidget()
|
void CSMPrefs::StringSetting::updateWidget()
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
#include "rotationflags.hpp"
|
#include "rotationflags.hpp"
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <map>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
#include "../mwworld/doorstate.hpp"
|
#include "../mwworld/doorstate.hpp"
|
||||||
#include "../mwworld/globalvariablename.hpp"
|
#include "../mwworld/globalvariablename.hpp"
|
||||||
|
@ -515,7 +515,7 @@ namespace MWBase
|
||||||
/// Spawn a blood effect for \a ptr at \a worldPosition
|
/// Spawn a blood effect for \a ptr at \a worldPosition
|
||||||
virtual void spawnBloodEffect(const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0;
|
virtual void spawnBloodEffect(const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0;
|
||||||
|
|
||||||
virtual void spawnEffect(const std::string& model, const std::string& textureOverride,
|
virtual void spawnEffect(VFS::Path::NormalizedView model, const std::string& textureOverride,
|
||||||
const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true)
|
const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true)
|
||||||
= 0;
|
= 0;
|
||||||
|
|
||||||
|
|
|
@ -794,18 +794,32 @@ namespace MWGui
|
||||||
if (!Settings::gui().mColorTopicEnable)
|
if (!Settings::gui().mColorTopicEnable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const MyGUI::Colour& specialColour = Settings::gui().mColorTopicSpecific;
|
|
||||||
const MyGUI::Colour& oldColour = Settings::gui().mColorTopicExhausted;
|
|
||||||
|
|
||||||
for (const std::string& keyword : mKeywords)
|
for (const std::string& keyword : mKeywords)
|
||||||
{
|
{
|
||||||
int flag = MWBase::Environment::get().getDialogueManager()->getTopicFlag(ESM::RefId::stringRefId(keyword));
|
int flag = MWBase::Environment::get().getDialogueManager()->getTopicFlag(ESM::RefId::stringRefId(keyword));
|
||||||
MyGUI::Button* button = mTopicsList->getItemWidget(keyword);
|
MyGUI::Button* button = mTopicsList->getItemWidget(keyword);
|
||||||
|
const auto oldCaption = button->getCaption();
|
||||||
|
const MyGUI::IntSize oldSize = button->getSize();
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
if (flag & MWBase::DialogueManager::TopicType::Specific)
|
if (flag & MWBase::DialogueManager::TopicType::Specific)
|
||||||
button->getSubWidgetText()->setTextColour(specialColour);
|
{
|
||||||
|
button->changeWidgetSkin("MW_ListLine_Specific");
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
else if (flag & MWBase::DialogueManager::TopicType::Exhausted)
|
else if (flag & MWBase::DialogueManager::TopicType::Exhausted)
|
||||||
button->getSubWidgetText()->setTextColour(oldColour);
|
{
|
||||||
|
button->changeWidgetSkin("MW_ListLine_Exhausted");
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
button->setCaption(oldCaption);
|
||||||
|
button->getSubWidgetText()->setWordWrap(true);
|
||||||
|
button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left);
|
||||||
|
button->setSize(oldSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
int maxDurability = iter->getClass().getItemMaxHealth(*iter);
|
int maxDurability = iter->getClass().getItemMaxHealth(*iter);
|
||||||
int durability = iter->getClass().getItemHealth(*iter);
|
int durability = iter->getClass().getItemHealth(*iter);
|
||||||
if (maxDurability == durability || maxDurability == 0)
|
if (maxDurability <= durability || maxDurability == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int basePrice = iter->getClass().getValue(*iter);
|
int basePrice = iter->getClass().getValue(*iter);
|
||||||
|
|
|
@ -288,7 +288,7 @@ namespace MWGui
|
||||||
|
|
||||||
if ((mFilter & Filter_OnlyRepairable)
|
if ((mFilter & Filter_OnlyRepairable)
|
||||||
&& (!base.getClass().hasItemHealth(base)
|
&& (!base.getClass().hasItemHealth(base)
|
||||||
|| (base.getClass().getItemHealth(base) == base.getClass().getItemMaxHealth(base))
|
|| (base.getClass().getItemHealth(base) >= base.getClass().getItemMaxHealth(base))
|
||||||
|| (base.getType() != ESM::Weapon::sRecordId && base.getType() != ESM::Armor::sRecordId)))
|
|| (base.getType() != ESM::Weapon::sRecordId && base.getType() != ESM::Armor::sRecordId)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -319,14 +319,14 @@ namespace MWLua
|
||||||
std::string texture = options->get_or<std::string>("particleTextureOverride", "");
|
std::string texture = options->get_or<std::string>("particleTextureOverride", "");
|
||||||
float scale = options->get_or("scale", 1.f);
|
float scale = options->get_or("scale", 1.f);
|
||||||
context.mLuaManager->addAction(
|
context.mLuaManager->addAction(
|
||||||
[world, model = std::string(model), texture = std::move(texture), worldPos, scale,
|
[world, model = VFS::Path::Normalized(model), texture = std::move(texture), worldPos, scale,
|
||||||
magicVfx]() { world->spawnEffect(model, texture, worldPos, scale, magicVfx); },
|
magicVfx]() { world->spawnEffect(model, texture, worldPos, scale, magicVfx); },
|
||||||
"openmw.vfx.spawn");
|
"openmw.vfx.spawn");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.mLuaManager->addAction(
|
context.mLuaManager->addAction([world, model = VFS::Path::Normalized(model),
|
||||||
[world, model = std::string(model), worldPos]() { world->spawnEffect(model, "", worldPos); },
|
worldPos]() { world->spawnEffect(model, "", worldPos); },
|
||||||
"openmw.vfx.spawn");
|
"openmw.vfx.spawn");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -238,6 +238,11 @@ namespace MWLua
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaManager::synchronizedUpdate()
|
void LuaManager::synchronizedUpdate()
|
||||||
|
{
|
||||||
|
mLua.protectedCall([&](LuaUtil::LuaView&) { synchronizedUpdateUnsafe(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaManager::synchronizedUpdateUnsafe()
|
||||||
{
|
{
|
||||||
if (mNewGameStarted)
|
if (mNewGameStarted)
|
||||||
{
|
{
|
||||||
|
|
|
@ -170,6 +170,7 @@ namespace MWLua
|
||||||
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr,
|
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr,
|
||||||
std::optional<LuaUtil::ScriptIdsWithInitializationData> autoStartConf = std::nullopt);
|
std::optional<LuaUtil::ScriptIdsWithInitializationData> autoStartConf = std::nullopt);
|
||||||
void reloadAllScriptsImpl();
|
void reloadAllScriptsImpl();
|
||||||
|
void synchronizedUpdateUnsafe();
|
||||||
|
|
||||||
bool mInitialized = false;
|
bool mInitialized = false;
|
||||||
bool mGlobalScriptsStarted = false;
|
bool mGlobalScriptsStarted = false;
|
||||||
|
|
|
@ -214,7 +214,7 @@ namespace
|
||||||
const ESM::Static* const fx
|
const ESM::Static* const fx
|
||||||
= world->getStore().get<ESM::Static>().search(ESM::RefId::stringRefId("VFX_Soul_Trap"));
|
= world->getStore().get<ESM::Static>().search(ESM::RefId::stringRefId("VFX_Soul_Trap"));
|
||||||
if (fx != nullptr)
|
if (fx != nullptr)
|
||||||
world->spawnEffect(Misc::ResourceHelpers::correctMeshPath(fx->mModel), "",
|
world->spawnEffect(VFS::Path::toNormalized(Misc::ResourceHelpers::correctMeshPath(fx->mModel)), "",
|
||||||
creature.getRefData().getPosition().asVec3());
|
creature.getRefData().getPosition().asVec3());
|
||||||
|
|
||||||
MWBase::Environment::get().getSoundManager()->playSound3D(
|
MWBase::Environment::get().getSoundManager()->playSound3D(
|
||||||
|
@ -1806,7 +1806,8 @@ namespace MWMechanics
|
||||||
ESM::RefId::stringRefId("VFX_Summon_End"));
|
ESM::RefId::stringRefId("VFX_Summon_End"));
|
||||||
if (fx)
|
if (fx)
|
||||||
MWBase::Environment::get().getWorld()->spawnEffect(
|
MWBase::Environment::get().getWorld()->spawnEffect(
|
||||||
Misc::ResourceHelpers::correctMeshPath(fx->mModel), "", ptr.getRefData().getPosition().asVec3());
|
VFS::Path::toNormalized(Misc::ResourceHelpers::correctMeshPath(fx->mModel)), "",
|
||||||
|
ptr.getRefData().getPosition().asVec3());
|
||||||
|
|
||||||
// Remove the summoned creature's summoned creatures as well
|
// Remove the summoned creature's summoned creatures as well
|
||||||
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
|
|
@ -846,14 +846,12 @@ namespace MWMechanics
|
||||||
mAI = true;
|
mAI = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MechanicsManager::isBoundItem(const MWWorld::Ptr& item)
|
namespace
|
||||||
{
|
{
|
||||||
static std::set<ESM::RefId> boundItemIDCache;
|
std::set<ESM::RefId> makeBoundItemIdCache()
|
||||||
|
{
|
||||||
|
std::set<ESM::RefId> boundItemIDCache;
|
||||||
|
|
||||||
// If this is empty then we haven't executed the GMST cache logic yet; or there isn't any sMagicBound* GMST's
|
|
||||||
// for some reason
|
|
||||||
if (boundItemIDCache.empty())
|
|
||||||
{
|
|
||||||
// Build a list of known bound item ID's
|
// Build a list of known bound item ID's
|
||||||
const MWWorld::Store<ESM::GameSetting>& gameSettings
|
const MWWorld::Store<ESM::GameSetting>& gameSettings
|
||||||
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||||
|
@ -870,15 +868,16 @@ namespace MWMechanics
|
||||||
|
|
||||||
boundItemIDCache.insert(ESM::RefId::stringRefId(currentGMSTValue));
|
boundItemIDCache.insert(ESM::RefId::stringRefId(currentGMSTValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return boundItemIDCache;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform bound item check and assign the Flag_Bound bit if it passes
|
bool MechanicsManager::isBoundItem(const MWWorld::Ptr& item)
|
||||||
const ESM::RefId& tempItemID = item.getCellRef().getRefId();
|
{
|
||||||
|
static const std::set<ESM::RefId> boundItemIdCache = makeBoundItemIdCache();
|
||||||
|
|
||||||
if (boundItemIDCache.count(tempItemID) != 0)
|
return boundItemIdCache.find(item.getCellRef().getRefId()) != boundItemIdCache.end();
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MechanicsManager::isAllowedToUse(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim)
|
bool MechanicsManager::isAllowedToUse(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim)
|
||||||
|
|
|
@ -72,12 +72,13 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
if (effectInfo.mData.mRange == ESM::RT_Target)
|
if (effectInfo.mData.mRange == ESM::RT_Target)
|
||||||
world->spawnEffect(
|
world->spawnEffect(
|
||||||
Misc::ResourceHelpers::correctMeshPath(areaStatic->mModel), texture, mHitPosition, 1.0f);
|
VFS::Path::toNormalized(Misc::ResourceHelpers::correctMeshPath(areaStatic->mModel)), texture,
|
||||||
|
mHitPosition, 1.0f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
world->spawnEffect(Misc::ResourceHelpers::correctMeshPath(areaStatic->mModel), texture, mHitPosition,
|
world->spawnEffect(VFS::Path::toNormalized(Misc::ResourceHelpers::correctMeshPath(areaStatic->mModel)),
|
||||||
static_cast<float>(effectInfo.mData.mArea * 2));
|
texture, mHitPosition, static_cast<float>(effectInfo.mData.mArea * 2));
|
||||||
|
|
||||||
// Play explosion sound (make sure to use NoTrack, since we will delete the projectile now)
|
// Play explosion sound (make sure to use NoTrack, since we will delete the projectile now)
|
||||||
{
|
{
|
||||||
|
@ -539,7 +540,8 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
scale = std::max(scale, 1.f);
|
scale = std::max(scale, 1.f);
|
||||||
MWBase::Environment::get().getWorld()->spawnEffect(
|
MWBase::Environment::get().getWorld()->spawnEffect(
|
||||||
Misc::ResourceHelpers::correctMeshPath(castStatic->mModel), effect->mParticle, pos, scale);
|
VFS::Path::toNormalized(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel)),
|
||||||
|
effect->mParticle, pos, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (animation && !mCaster.getClass().isActor())
|
if (animation && !mCaster.getClass().isActor())
|
||||||
|
|
|
@ -412,9 +412,9 @@ namespace MWPhysics
|
||||||
if (ptr.mRef->mData.mPhysicsPostponed)
|
if (ptr.mRef->mData.mPhysicsPostponed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string animationMesh = mesh;
|
const VFS::Path::Normalized animationMesh = ptr.getClass().useAnim()
|
||||||
if (ptr.getClass().useAnim())
|
? Misc::ResourceHelpers::correctActorModelPath(mesh, mResourceSystem->getVFS())
|
||||||
animationMesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mResourceSystem->getVFS());
|
: mesh;
|
||||||
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(animationMesh);
|
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(animationMesh);
|
||||||
if (!shapeInstance || !shapeInstance->mCollisionShape)
|
if (!shapeInstance || !shapeInstance->mCollisionShape)
|
||||||
return;
|
return;
|
||||||
|
@ -562,7 +562,8 @@ namespace MWPhysics
|
||||||
|
|
||||||
void PhysicsSystem::addActor(const MWWorld::Ptr& ptr, const std::string& mesh)
|
void PhysicsSystem::addActor(const MWWorld::Ptr& ptr, const std::string& mesh)
|
||||||
{
|
{
|
||||||
std::string animationMesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mResourceSystem->getVFS());
|
const VFS::Path::Normalized animationMesh
|
||||||
|
= Misc::ResourceHelpers::correctActorModelPath(mesh, mResourceSystem->getVFS());
|
||||||
osg::ref_ptr<const Resource::BulletShape> shape = mShapeManager->getShape(animationMesh);
|
osg::ref_ptr<const Resource::BulletShape> shape = mShapeManager->getShape(animationMesh);
|
||||||
|
|
||||||
// Try to get shape from basic model as fallback for creatures
|
// Try to get shape from basic model as fallback for creatures
|
||||||
|
@ -570,7 +571,7 @@ namespace MWPhysics
|
||||||
{
|
{
|
||||||
if (animationMesh != mesh)
|
if (animationMesh != mesh)
|
||||||
{
|
{
|
||||||
shape = mShapeManager->getShape(mesh);
|
shape = mShapeManager->getShape(VFS::Path::toNormalized(mesh));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -590,7 +591,8 @@ namespace MWPhysics
|
||||||
int PhysicsSystem::addProjectile(
|
int PhysicsSystem::addProjectile(
|
||||||
const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius)
|
const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(mesh);
|
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance
|
||||||
|
= mShapeManager->getInstance(VFS::Path::toNormalized(mesh));
|
||||||
assert(shapeInstance);
|
assert(shapeInstance);
|
||||||
float radius = computeRadius ? shapeInstance->mCollisionBox.mExtents.length() / 2.f : 1.f;
|
float radius = computeRadius ? shapeInstance->mCollisionBox.mExtents.length() / 2.f : 1.f;
|
||||||
|
|
||||||
|
|
|
@ -67,31 +67,29 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
|
|
||||||
PartHolderPtr ActorAnimation::attachMesh(
|
PartHolderPtr ActorAnimation::attachMesh(
|
||||||
const std::string& model, std::string_view bonename, bool enchantedGlow, osg::Vec4f* glowColor)
|
VFS::Path::NormalizedView model, std::string_view bonename, const osg::Vec4f* glowColor)
|
||||||
{
|
{
|
||||||
osg::Group* parent = getBoneByName(bonename);
|
osg::Group* parent = getBoneByName(bonename);
|
||||||
if (!parent)
|
if (!parent)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> instance
|
osg::ref_ptr<osg::Node> instance = mResourceSystem->getSceneManager()->getInstance(model, parent);
|
||||||
= mResourceSystem->getSceneManager()->getInstance(VFS::Path::toNormalized(model), parent);
|
|
||||||
|
|
||||||
const NodeMap& nodeMap = getNodeMap();
|
const NodeMap& nodeMap = getNodeMap();
|
||||||
NodeMap::const_iterator found = nodeMap.find(bonename);
|
NodeMap::const_iterator found = nodeMap.find(bonename);
|
||||||
if (found == nodeMap.end())
|
if (found == nodeMap.end())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (enchantedGlow)
|
if (glowColor != nullptr)
|
||||||
mGlowUpdater = SceneUtil::addEnchantedGlow(instance, mResourceSystem, *glowColor);
|
mGlowUpdater = SceneUtil::addEnchantedGlow(instance, mResourceSystem, *glowColor);
|
||||||
|
|
||||||
return std::make_unique<PartHolder>(instance);
|
return std::make_unique<PartHolder>(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> ActorAnimation::attach(
|
osg::ref_ptr<osg::Node> ActorAnimation::attach(
|
||||||
const std::string& model, std::string_view bonename, std::string_view bonefilter, bool isLight)
|
VFS::Path::NormalizedView model, std::string_view bonename, std::string_view bonefilter, bool isLight)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<const osg::Node> templateNode
|
osg::ref_ptr<const osg::Node> templateNode = mResourceSystem->getSceneManager()->getTemplate(model);
|
||||||
= mResourceSystem->getSceneManager()->getTemplate(VFS::Path::toNormalized(model));
|
|
||||||
|
|
||||||
const NodeMap& nodeMap = getNodeMap();
|
const NodeMap& nodeMap = getNodeMap();
|
||||||
auto found = nodeMap.find(bonename);
|
auto found = nodeMap.find(bonename);
|
||||||
|
@ -218,20 +216,20 @@ namespace MWRender
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string mesh = getSheathedShieldMesh(*shield);
|
const VFS::Path::Normalized mesh = getSheathedShieldMesh(*shield);
|
||||||
if (mesh.empty())
|
if (mesh.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string_view boneName = "Bip01 AttachShield";
|
constexpr std::string_view boneName = "Bip01 AttachShield";
|
||||||
osg::Vec4f glowColor = shield->getClass().getEnchantmentColor(*shield);
|
const bool isEnchanted = !shield->getClass().getEnchantment(*shield).empty();
|
||||||
const std::string holsteredName = addSuffixBeforeExtension(mesh, "_sh");
|
const osg::Vec4f glowColor = isEnchanted ? shield->getClass().getEnchantmentColor(*shield) : osg::Vec4f();
|
||||||
bool isEnchanted = !shield->getClass().getEnchantment(*shield).empty();
|
const VFS::Path::Normalized holsteredName = addSuffixBeforeExtension(mesh, "_sh");
|
||||||
|
|
||||||
// If we have no dedicated sheath model, use basic shield model as fallback.
|
// If we have no dedicated sheath model, use basic shield model as fallback.
|
||||||
if (!mResourceSystem->getVFS()->exists(holsteredName))
|
if (!mResourceSystem->getVFS()->exists(holsteredName))
|
||||||
mHolsteredShield = attachMesh(mesh, boneName, isEnchanted, &glowColor);
|
mHolsteredShield = attachMesh(mesh, boneName, isEnchanted ? &glowColor : nullptr);
|
||||||
else
|
else
|
||||||
mHolsteredShield = attachMesh(holsteredName, boneName, isEnchanted, &glowColor);
|
mHolsteredShield = attachMesh(holsteredName, boneName, isEnchanted ? &glowColor : nullptr);
|
||||||
|
|
||||||
if (!mHolsteredShield)
|
if (!mHolsteredShield)
|
||||||
return;
|
return;
|
||||||
|
@ -245,8 +243,7 @@ namespace MWRender
|
||||||
// file.
|
// file.
|
||||||
if (shieldNode && !shieldNode->getNumChildren())
|
if (shieldNode && !shieldNode->getNumChildren())
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Node> fallbackNode
|
osg::ref_ptr<osg::Node> fallbackNode = mResourceSystem->getSceneManager()->getInstance(mesh, shieldNode);
|
||||||
= mResourceSystem->getSceneManager()->getInstance(VFS::Path::toNormalized(mesh), shieldNode);
|
|
||||||
if (isEnchanted)
|
if (isEnchanted)
|
||||||
SceneUtil::addEnchantedGlow(shieldNode, mResourceSystem, glowColor);
|
SceneUtil::addEnchantedGlow(shieldNode, mResourceSystem, glowColor);
|
||||||
}
|
}
|
||||||
|
@ -341,20 +338,24 @@ namespace MWRender
|
||||||
if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)
|
if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)
|
||||||
showHolsteredWeapons = false;
|
showHolsteredWeapons = false;
|
||||||
|
|
||||||
std::string mesh = weapon->getClass().getCorrectedModel(*weapon);
|
const VFS::Path::Normalized mesh = weapon->getClass().getCorrectedModel(*weapon);
|
||||||
std::string_view boneName = getHolsteredWeaponBoneName(*weapon);
|
if (mesh.empty())
|
||||||
if (mesh.empty() || boneName.empty())
|
return;
|
||||||
|
|
||||||
|
const std::string_view boneName = getHolsteredWeaponBoneName(*weapon);
|
||||||
|
if (boneName.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If the scabbard is not found, use the weapon mesh as fallback.
|
// If the scabbard is not found, use the weapon mesh as fallback.
|
||||||
const std::string scabbardName = addSuffixBeforeExtension(mesh, "_sh");
|
const VFS::Path::Normalized scabbardName = addSuffixBeforeExtension(mesh, "_sh");
|
||||||
bool isEnchanted = !weapon->getClass().getEnchantment(*weapon).empty();
|
const bool isEnchanted = !weapon->getClass().getEnchantment(*weapon).empty();
|
||||||
if (!mResourceSystem->getVFS()->exists(scabbardName))
|
if (!mResourceSystem->getVFS()->exists(scabbardName))
|
||||||
{
|
{
|
||||||
if (showHolsteredWeapons)
|
if (showHolsteredWeapons)
|
||||||
{
|
{
|
||||||
osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);
|
const osg::Vec4f glowColor
|
||||||
mScabbard = attachMesh(mesh, boneName, isEnchanted, &glowColor);
|
= isEnchanted ? weapon->getClass().getEnchantmentColor(*weapon) : osg::Vec4f();
|
||||||
|
mScabbard = attachMesh(mesh, boneName, isEnchanted ? &glowColor : nullptr);
|
||||||
if (mScabbard)
|
if (mScabbard)
|
||||||
resetControllers(mScabbard->getNode());
|
resetControllers(mScabbard->getNode());
|
||||||
}
|
}
|
||||||
|
@ -384,7 +385,7 @@ namespace MWRender
|
||||||
if (!weaponNode->getNumChildren())
|
if (!weaponNode->getNumChildren())
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Node> fallbackNode
|
osg::ref_ptr<osg::Node> fallbackNode
|
||||||
= mResourceSystem->getSceneManager()->getInstance(VFS::Path::toNormalized(mesh), weaponNode);
|
= mResourceSystem->getSceneManager()->getInstance(mesh, weaponNode);
|
||||||
resetControllers(fallbackNode);
|
resetControllers(fallbackNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
#include "../mwworld/containerstore.hpp"
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
|
||||||
#include "animation.hpp"
|
#include "animation.hpp"
|
||||||
|
@ -45,21 +47,18 @@ namespace MWRender
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
osg::Group* getBoneByName(std::string_view boneName) const;
|
osg::Group* getBoneByName(std::string_view boneName) const;
|
||||||
virtual void updateHolsteredWeapon(bool showHolsteredWeapons);
|
void updateHolsteredWeapon(bool showHolsteredWeapons);
|
||||||
virtual void updateHolsteredShield(bool showCarriedLeft);
|
void updateHolsteredShield(bool showCarriedLeft);
|
||||||
virtual void updateQuiver();
|
void updateQuiver();
|
||||||
std::string getShieldMesh(const MWWorld::ConstPtr& shield, bool female) const;
|
std::string getShieldMesh(const MWWorld::ConstPtr& shield, bool female) const;
|
||||||
virtual std::string getSheathedShieldMesh(const MWWorld::ConstPtr& shield) const;
|
virtual std::string getSheathedShieldMesh(const MWWorld::ConstPtr& shield) const;
|
||||||
virtual std::string_view getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon);
|
std::string_view getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon);
|
||||||
virtual PartHolderPtr attachMesh(
|
|
||||||
const std::string& model, std::string_view bonename, bool enchantedGlow, osg::Vec4f* glowColor);
|
PartHolderPtr attachMesh(
|
||||||
virtual PartHolderPtr attachMesh(const std::string& model, std::string_view bonename)
|
VFS::Path::NormalizedView model, std::string_view bonename, const osg::Vec4f* glowColor = nullptr);
|
||||||
{
|
|
||||||
osg::Vec4f stubColor = osg::Vec4f(0, 0, 0, 0);
|
|
||||||
return attachMesh(model, bonename, false, &stubColor);
|
|
||||||
}
|
|
||||||
osg::ref_ptr<osg::Node> attach(
|
osg::ref_ptr<osg::Node> attach(
|
||||||
const std::string& model, std::string_view bonename, std::string_view bonefilter, bool isLight);
|
VFS::Path::NormalizedView model, std::string_view bonename, std::string_view bonefilter, bool isLight);
|
||||||
|
|
||||||
PartHolderPtr mScabbard;
|
PartHolderPtr mScabbard;
|
||||||
PartHolderPtr mHolsteredShield;
|
PartHolderPtr mHolsteredShield;
|
||||||
|
|
|
@ -388,18 +388,21 @@ namespace
|
||||||
std::string_view mEffectId;
|
std::string_view mEffectId;
|
||||||
};
|
};
|
||||||
|
|
||||||
osg::ref_ptr<osg::LightModel> getVFXLightModelInstance()
|
namespace
|
||||||
{
|
{
|
||||||
static osg::ref_ptr<osg::LightModel> lightModel = nullptr;
|
osg::ref_ptr<osg::LightModel> makeVFXLightModelInstance()
|
||||||
|
|
||||||
if (!lightModel)
|
|
||||||
{
|
{
|
||||||
lightModel = new osg::LightModel;
|
osg::ref_ptr<osg::LightModel> lightModel = new osg::LightModel;
|
||||||
lightModel->setAmbientIntensity({ 1, 1, 1, 1 });
|
lightModel->setAmbientIntensity({ 1, 1, 1, 1 });
|
||||||
|
return lightModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const osg::ref_ptr<osg::LightModel>& getVFXLightModelInstance()
|
||||||
|
{
|
||||||
|
static const osg::ref_ptr<osg::LightModel> lightModel = makeVFXLightModelInstance();
|
||||||
return lightModel;
|
return lightModel;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void assignBoneBlendCallbackRecursive(MWRender::BoneAnimBlendController* controller, osg::Node* parent, bool isRoot)
|
void assignBoneBlendCallbackRecursive(MWRender::BoneAnimBlendController* controller, osg::Node* parent, bool isRoot)
|
||||||
{
|
{
|
||||||
|
@ -1508,10 +1511,10 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
animationPath.replace(animationPath.size() - 4, 4, "/");
|
animationPath.replace(animationPath.size() - 4, 4, "/");
|
||||||
|
|
||||||
for (const auto& name : resourceSystem->getVFS()->getRecursiveDirectoryIterator(animationPath))
|
for (const VFS::Path::Normalized& name : resourceSystem->getVFS()->getRecursiveDirectoryIterator(animationPath))
|
||||||
{
|
{
|
||||||
if (Misc::getFileExtension(name) == "nif")
|
if (Misc::getFileExtension(name) == "nif")
|
||||||
loadBonesFromFile(node, VFS::Path::toNormalized(name), resourceSystem);
|
loadBonesFromFile(node, name, resourceSystem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -533,7 +533,7 @@ namespace MWRender
|
||||||
: CharacterPreview(
|
: CharacterPreview(
|
||||||
parent, resourceSystem, MWMechanics::getPlayer(), 512, 512, osg::Vec3f(0, 125, 8), osg::Vec3f(0, 0, 8))
|
parent, resourceSystem, MWMechanics::getPlayer(), 512, 512, osg::Vec3f(0, 125, 8), osg::Vec3f(0, 0, 8))
|
||||||
, mBase(*mCharacter.get<ESM::NPC>()->mBase)
|
, mBase(*mCharacter.get<ESM::NPC>()->mBase)
|
||||||
, mRef(&mBase)
|
, mRef(ESM::makeBlankCellRef(), &mBase)
|
||||||
, mPitchRadians(osg::DegreesToRadians(6.f))
|
, mPitchRadians(osg::DegreesToRadians(6.f))
|
||||||
{
|
{
|
||||||
mCharacter = MWWorld::Ptr(&mRef, nullptr);
|
mCharacter = MWWorld::Ptr(&mRef, nullptr);
|
||||||
|
|
|
@ -111,7 +111,7 @@ namespace MWRender
|
||||||
MWWorld::ConstPtr item = *it;
|
MWWorld::ConstPtr item = *it;
|
||||||
|
|
||||||
std::string_view bonename;
|
std::string_view bonename;
|
||||||
std::string itemModel = item.getClass().getCorrectedModel(item);
|
VFS::Path::Normalized itemModel = item.getClass().getCorrectedModel(item);
|
||||||
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
|
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
|
||||||
{
|
{
|
||||||
if (item.getType() == ESM::Weapon::sRecordId)
|
if (item.getType() == ESM::Weapon::sRecordId)
|
||||||
|
|
|
@ -484,15 +484,24 @@ namespace MWRender
|
||||||
bool is1stPerson = mViewMode == VM_FirstPerson;
|
bool is1stPerson = mViewMode == VM_FirstPerson;
|
||||||
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
||||||
|
|
||||||
|
std::string_view base;
|
||||||
|
if (!isWerewolf)
|
||||||
|
{
|
||||||
|
if (!is1stPerson)
|
||||||
|
base = Settings::models().mXbaseanim.get().value();
|
||||||
|
else
|
||||||
|
base = Settings::models().mXbaseanim1st.get().value();
|
||||||
|
}
|
||||||
|
|
||||||
const std::string defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(
|
const std::string defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(
|
||||||
getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf), mResourceSystem->getVFS());
|
getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf), mResourceSystem->getVFS());
|
||||||
|
|
||||||
std::string smodel = defaultSkeleton;
|
std::string smodel = defaultSkeleton;
|
||||||
bool isBase = !isWerewolf;
|
bool isCustomModel = false;
|
||||||
if (!is1stPerson && !isWerewolf && !mNpc->mModel.empty())
|
if (!is1stPerson && !isWerewolf && !mNpc->mModel.empty())
|
||||||
{
|
{
|
||||||
std::string model = Misc::ResourceHelpers::correctMeshPath(mNpc->mModel);
|
std::string model = Misc::ResourceHelpers::correctMeshPath(mNpc->mModel);
|
||||||
isBase = isDefaultActorSkeleton(model);
|
isCustomModel = !isDefaultActorSkeleton(model);
|
||||||
smodel = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS());
|
smodel = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,33 +509,21 @@ namespace MWRender
|
||||||
|
|
||||||
updateParts();
|
updateParts();
|
||||||
|
|
||||||
if (!is1stPerson)
|
if (!base.empty())
|
||||||
{
|
|
||||||
const std::string& base = Settings::models().mXbaseanim.get().value();
|
|
||||||
if (!isWerewolf)
|
|
||||||
addAnimSource(base, smodel);
|
addAnimSource(base, smodel);
|
||||||
|
|
||||||
if (!isBase)
|
if (defaultSkeleton != base)
|
||||||
{
|
|
||||||
addAnimSource(defaultSkeleton, smodel);
|
addAnimSource(defaultSkeleton, smodel);
|
||||||
addAnimSource(smodel, smodel);
|
|
||||||
}
|
|
||||||
else if (base != defaultSkeleton)
|
|
||||||
{
|
|
||||||
addAnimSource(defaultSkeleton, smodel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isWerewolf && isBeast && mNpc->mRace.contains("argonian"))
|
if (isCustomModel)
|
||||||
addAnimSource("meshes\\xargonian_swimkna.nif", smodel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!isWerewolf)
|
|
||||||
addAnimSource(Settings::models().mXbaseanim1st.get().value(), smodel);
|
|
||||||
|
|
||||||
if (!isBase)
|
|
||||||
addAnimSource(smodel, smodel);
|
addAnimSource(smodel, smodel);
|
||||||
|
|
||||||
|
const bool customArgonianSwim = !is1stPerson && !isWerewolf && isBeast && mNpc->mRace.contains("argonian");
|
||||||
|
if (customArgonianSwim)
|
||||||
|
addAnimSource(Settings::models().mXargonianswimkna.get().value(), smodel);
|
||||||
|
|
||||||
|
if (is1stPerson)
|
||||||
|
{
|
||||||
mObjectRoot->setNodeMask(Mask_FirstPerson);
|
mObjectRoot->setNodeMask(Mask_FirstPerson);
|
||||||
mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView));
|
mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView));
|
||||||
}
|
}
|
||||||
|
@ -681,11 +678,11 @@ namespace MWRender
|
||||||
PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, std::string_view bonename,
|
PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, std::string_view bonename,
|
||||||
std::string_view bonefilter, bool enchantedGlow, osg::Vec4f* glowColor, bool isLight)
|
std::string_view bonefilter, bool enchantedGlow, osg::Vec4f* glowColor, bool isLight)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Node> attached = attach(model, bonename, bonefilter, isLight);
|
osg::ref_ptr<osg::Node> attached = attach(VFS::Path::toNormalized(model), bonename, bonefilter, isLight);
|
||||||
if (enchantedGlow)
|
if (enchantedGlow)
|
||||||
mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, *glowColor);
|
mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, *glowColor);
|
||||||
|
|
||||||
return std::make_unique<PartHolder>(attached);
|
return std::make_unique<PartHolder>(std::move(attached));
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f NpcAnimation::runAnimation(float timepassed)
|
osg::Vec3f NpcAnimation::runAnimation(float timepassed)
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <components/esm3/loaddoor.hpp>
|
#include <components/esm3/loaddoor.hpp>
|
||||||
#include <components/esm3/loadstat.hpp>
|
#include <components/esm3/loadstat.hpp>
|
||||||
#include <components/esm3/readerscache.hpp>
|
#include <components/esm3/readerscache.hpp>
|
||||||
|
#include <components/misc/pathhelpers.hpp>
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
@ -639,7 +640,7 @@ namespace MWRender
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const int type = store.findStatic(ref.mRefId);
|
const int type = store.findStatic(ref.mRefId);
|
||||||
std::string model = getModel(type, ref.mRefId, store);
|
VFS::Path::Normalized model = getModel(type, ref.mRefId, store);
|
||||||
if (model.empty())
|
if (model.empty())
|
||||||
continue;
|
continue;
|
||||||
model = Misc::ResourceHelpers::correctMeshPath(model);
|
model = Misc::ResourceHelpers::correctMeshPath(model);
|
||||||
|
@ -647,10 +648,10 @@ namespace MWRender
|
||||||
if (activeGrid && type != ESM::REC_STAT)
|
if (activeGrid && type != ESM::REC_STAT)
|
||||||
{
|
{
|
||||||
model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS());
|
model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS());
|
||||||
std::string kfname = Misc::StringUtils::lowerCase(model);
|
if (Misc::getFileExtension(model) == "nif")
|
||||||
if (kfname.size() > 4 && kfname.ends_with(".nif"))
|
|
||||||
{
|
{
|
||||||
kfname.replace(kfname.size() - 4, 4, ".kf");
|
VFS::Path::Normalized kfname = model;
|
||||||
|
kfname.changeExtension("kf");
|
||||||
if (mSceneManager->getVFS()->exists(kfname))
|
if (mSceneManager->getVFS()->exists(kfname))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -671,7 +672,7 @@ namespace MWRender
|
||||||
->second;
|
->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<const osg::Node> cnode = mSceneManager->getTemplate(VFS::Path::toNormalized(model), false);
|
osg::ref_ptr<const osg::Node> cnode = mSceneManager->getTemplate(model, false);
|
||||||
|
|
||||||
if (activeGrid)
|
if (activeGrid)
|
||||||
{
|
{
|
||||||
|
|
|
@ -206,8 +206,14 @@ namespace MWRender
|
||||||
};
|
};
|
||||||
|
|
||||||
// PASS: Blot in all ripple spawners
|
// PASS: Blot in all ripple spawners
|
||||||
mProgramBlobber->apply(state);
|
state.pushStateSet(frameState.mStateset);
|
||||||
state.apply(frameState.mStateset);
|
state.apply();
|
||||||
|
state.applyAttribute(mProgramBlobber);
|
||||||
|
for (const auto& [name, stack] : state.getUniformMap())
|
||||||
|
{
|
||||||
|
if (!stack.uniformVec.empty())
|
||||||
|
state.getLastAppliedProgramObject()->apply(*(stack.uniformVec.back().first));
|
||||||
|
}
|
||||||
|
|
||||||
if (mUseCompute)
|
if (mUseCompute)
|
||||||
{
|
{
|
||||||
|
@ -225,8 +231,12 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
|
|
||||||
// PASS: Wave simulation
|
// PASS: Wave simulation
|
||||||
mProgramSimulation->apply(state);
|
state.applyAttribute(mProgramSimulation);
|
||||||
state.apply(frameState.mStateset);
|
for (const auto& [name, stack] : state.getUniformMap())
|
||||||
|
{
|
||||||
|
if (!stack.uniformVec.empty())
|
||||||
|
state.getLastAppliedProgramObject()->apply(*(stack.uniformVec.back().first));
|
||||||
|
}
|
||||||
|
|
||||||
if (mUseCompute)
|
if (mUseCompute)
|
||||||
{
|
{
|
||||||
|
@ -242,6 +252,8 @@ namespace MWRender
|
||||||
state.applyTextureAttribute(0, mTextures[1]);
|
state.applyTextureAttribute(0, mTextures[1]);
|
||||||
osg::Geometry::drawImplementation(renderInfo);
|
osg::Geometry::drawImplementation(renderInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.popStateSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Texture* RipplesSurface::getColorTexture() const
|
osg::Texture* RipplesSurface::getColorTexture() const
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <components/esm3/loadcell.hpp>
|
#include <components/esm3/loadcell.hpp>
|
||||||
#include <components/loadinglistener/reporter.hpp>
|
#include <components/loadinglistener/reporter.hpp>
|
||||||
#include <components/misc/constants.hpp>
|
#include <components/misc/constants.hpp>
|
||||||
|
#include <components/misc/pathhelpers.hpp>
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/misc/strings/algorithm.hpp>
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
#include <components/misc/strings/lower.hpp>
|
#include <components/misc/strings/lower.hpp>
|
||||||
|
@ -105,8 +106,8 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string mesh;
|
VFS::Path::Normalized mesh;
|
||||||
std::string kfname;
|
VFS::Path::Normalized kfname;
|
||||||
for (std::string_view path : mMeshes)
|
for (std::string_view path : mMeshes)
|
||||||
{
|
{
|
||||||
if (mAbort)
|
if (mAbort)
|
||||||
|
@ -121,19 +122,15 @@ namespace MWWorld
|
||||||
if (!vfs.exists(mesh))
|
if (!vfs.exists(mesh))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
size_t slashpos = mesh.find_last_of("/\\");
|
if (Misc::getFileName(mesh).starts_with('x') && Misc::getFileExtension(mesh) == "nif")
|
||||||
if (slashpos != std::string::npos && slashpos != mesh.size() - 1)
|
|
||||||
{
|
|
||||||
if (Misc::StringUtils::toLower(mesh[slashpos + 1]) == 'x'
|
|
||||||
&& Misc::StringUtils::ciEndsWith(mesh, ".nif"))
|
|
||||||
{
|
{
|
||||||
kfname = mesh;
|
kfname = mesh;
|
||||||
kfname.replace(kfname.size() - 4, 4, ".kf");
|
kfname.changeExtension("kf");
|
||||||
if (vfs.exists(kfname))
|
if (vfs.exists(kfname))
|
||||||
mPreloadedObjects.insert(mKeyframeManager->get(kfname));
|
mPreloadedObjects.insert(mKeyframeManager->get(kfname));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
mPreloadedObjects.insert(mSceneManager->getTemplate(VFS::Path::toNormalized(mesh)));
|
mPreloadedObjects.insert(mSceneManager->getTemplate(mesh));
|
||||||
if (mPreloadInstances)
|
if (mPreloadInstances)
|
||||||
mPreloadedObjects.insert(mBulletShapeManager->cacheInstance(mesh));
|
mPreloadedObjects.insert(mBulletShapeManager->cacheInstance(mesh));
|
||||||
else
|
else
|
||||||
|
|
|
@ -317,9 +317,9 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
// new reference
|
// new reference
|
||||||
MWWorld::LiveCellRef<T> ref(record);
|
MWWorld::LiveCellRef<T> ref(ESM::makeBlankCellRef(), record);
|
||||||
ref.load(state);
|
ref.load(state);
|
||||||
collection.mList.push_back(ref);
|
collection.mList.push_back(std::move(ref));
|
||||||
|
|
||||||
MWWorld::LiveCellRefBase* base = &collection.mList.back();
|
MWWorld::LiveCellRefBase* base = &collection.mList.back();
|
||||||
MWBase::Environment::get().getWorldModel()->registerPtr(MWWorld::Ptr(base, cellstore));
|
MWBase::Environment::get().getWorldModel()->registerPtr(MWWorld::Ptr(base, cellstore));
|
||||||
|
@ -426,9 +426,9 @@ namespace MWWorld
|
||||||
liveCellRef.mData.setDeletedByContentFile(true);
|
liveCellRef.mData.setDeletedByContentFile(true);
|
||||||
|
|
||||||
if (iter != mList.end())
|
if (iter != mList.end())
|
||||||
*iter = liveCellRef;
|
*iter = std::move(liveCellRef);
|
||||||
else
|
else
|
||||||
mList.push_back(liveCellRef);
|
mList.push_back(std::move(liveCellRef));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -455,7 +455,7 @@ namespace MWWorld
|
||||||
LiveCellRef<X> liveCellRef(ref, ptr);
|
LiveCellRef<X> liveCellRef(ref, ptr);
|
||||||
if (!isEnabled(ref, esmStore))
|
if (!isEnabled(ref, esmStore))
|
||||||
liveCellRef.mData.disable();
|
liveCellRef.mData.disable();
|
||||||
list.push_back(liveCellRef);
|
list.push_back(std::move(liveCellRef));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename X>
|
template <typename X>
|
||||||
|
|
|
@ -101,9 +101,9 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState(
|
||||||
if (!record)
|
if (!record)
|
||||||
return ContainerStoreIterator(this);
|
return ContainerStoreIterator(this);
|
||||||
|
|
||||||
LiveCellRef<T> ref(record);
|
LiveCellRef<T> ref(ESM::makeBlankCellRef(), record);
|
||||||
ref.load(state);
|
ref.load(state);
|
||||||
collection.mList.push_back(ref);
|
collection.mList.push_back(std::move(ref));
|
||||||
auto it = ContainerStoreIterator(this, --collection.mList.end());
|
auto it = ContainerStoreIterator(this, --collection.mList.end());
|
||||||
MWBase::Environment::get().getWorldModel()->registerPtr(*it);
|
MWBase::Environment::get().getWorldModel()->registerPtr(*it);
|
||||||
|
|
||||||
|
|
|
@ -15,35 +15,55 @@
|
||||||
#include "ptr.hpp"
|
#include "ptr.hpp"
|
||||||
#include "worldmodel.hpp"
|
#include "worldmodel.hpp"
|
||||||
|
|
||||||
MWWorld::LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM::CellRef& cref)
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM::CellRef& cref)
|
||||||
: mClass(&Class::get(type))
|
: mClass(&Class::get(type))
|
||||||
, mRef(cref)
|
, mRef(cref)
|
||||||
, mData(cref)
|
, mData(cref)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM4::Reference& cref)
|
LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM4::Reference& cref)
|
||||||
: mClass(&Class::get(type))
|
: mClass(&Class::get(type))
|
||||||
, mRef(cref)
|
, mRef(cref)
|
||||||
, mData(cref)
|
, mData(cref)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM4::ActorCharacter& cref)
|
LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM4::ActorCharacter& cref)
|
||||||
: mClass(&Class::get(type))
|
: mClass(&Class::get(type))
|
||||||
, mRef(cref)
|
, mRef(cref)
|
||||||
, mData(cref)
|
, mData(cref)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::LiveCellRefBase::~LiveCellRefBase()
|
LiveCellRefBase::LiveCellRefBase(LiveCellRefBase&& other) noexcept
|
||||||
|
: mClass(other.mClass)
|
||||||
|
, mRef(std::move(other.mRef))
|
||||||
|
, mData(std::move(other.mData))
|
||||||
|
, mWorldModel(std::exchange(other.mWorldModel, nullptr))
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWorldModel()->deregisterLiveCellRef(*this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::LiveCellRefBase::loadImp(const ESM::ObjectState& state)
|
LiveCellRefBase::~LiveCellRefBase()
|
||||||
{
|
{
|
||||||
mRef = MWWorld::CellRef(state.mRef);
|
if (mWorldModel != nullptr)
|
||||||
|
mWorldModel->deregisterLiveCellRef(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveCellRefBase& LiveCellRefBase::operator=(LiveCellRefBase&& other) noexcept
|
||||||
|
{
|
||||||
|
mClass = other.mClass;
|
||||||
|
mRef = std::move(other.mRef);
|
||||||
|
mData = std::move(other.mData);
|
||||||
|
mWorldModel = std::exchange(other.mWorldModel, nullptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LiveCellRefBase::loadImp(const ESM::ObjectState& state)
|
||||||
|
{
|
||||||
|
mRef = CellRef(state.mRef);
|
||||||
mData = RefData(state, mData.isDeletedByContentFile());
|
mData = RefData(state, mData.isDeletedByContentFile());
|
||||||
|
|
||||||
Ptr ptr(this);
|
Ptr ptr(this);
|
||||||
|
@ -83,7 +103,7 @@ void MWWorld::LiveCellRefBase::loadImp(const ESM::ObjectState& state)
|
||||||
MWBase::Environment::get().getLuaManager()->loadLocalScripts(ptr, state.mLuaScripts);
|
MWBase::Environment::get().getLuaManager()->loadLocalScripts(ptr, state.mLuaScripts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::LiveCellRefBase::saveImp(ESM::ObjectState& state) const
|
void LiveCellRefBase::saveImp(ESM::ObjectState& state) const
|
||||||
{
|
{
|
||||||
mRef.writeState(state);
|
mRef.writeState(state);
|
||||||
|
|
||||||
|
@ -96,23 +116,21 @@ void MWWorld::LiveCellRefBase::saveImp(ESM::ObjectState& state) const
|
||||||
mClass->writeAdditionalState(ptr, state);
|
mClass->writeAdditionalState(ptr, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MWWorld::LiveCellRefBase::checkStateImp(const ESM::ObjectState& state)
|
bool LiveCellRefBase::checkStateImp(const ESM::ObjectState& state)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int MWWorld::LiveCellRefBase::getType() const
|
unsigned int LiveCellRefBase::getType() const
|
||||||
{
|
{
|
||||||
return mClass->getType();
|
return mClass->getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MWWorld::LiveCellRefBase::isDeleted() const
|
bool LiveCellRefBase::isDeleted() const
|
||||||
{
|
{
|
||||||
return mData.isDeletedByContentFile() || mRef.getCount(false) == 0;
|
return mData.isDeletedByContentFile() || mRef.getCount(false) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWWorld
|
|
||||||
{
|
|
||||||
std::string makeDynamicCastErrorMessage(const LiveCellRefBase* value, std::string_view recordType)
|
std::string makeDynamicCastErrorMessage(const LiveCellRefBase* value, std::string_view recordType)
|
||||||
{
|
{
|
||||||
std::stringstream message;
|
std::stringstream message;
|
||||||
|
|
|
@ -17,6 +17,7 @@ namespace MWWorld
|
||||||
class Ptr;
|
class Ptr;
|
||||||
class ESMStore;
|
class ESMStore;
|
||||||
class Class;
|
class Class;
|
||||||
|
class WorldModel;
|
||||||
|
|
||||||
template <typename X>
|
template <typename X>
|
||||||
struct LiveCellRef;
|
struct LiveCellRef;
|
||||||
|
@ -29,17 +30,28 @@ namespace MWWorld
|
||||||
/** Information about this instance, such as 3D location and rotation
|
/** Information about this instance, such as 3D location and rotation
|
||||||
* and individual type-dependent data.
|
* and individual type-dependent data.
|
||||||
*/
|
*/
|
||||||
MWWorld::CellRef mRef;
|
CellRef mRef;
|
||||||
|
|
||||||
/** runtime-data */
|
/** runtime-data */
|
||||||
RefData mData;
|
RefData mData;
|
||||||
|
|
||||||
LiveCellRefBase(unsigned int type, const ESM::CellRef& cref = ESM::CellRef());
|
WorldModel* mWorldModel = nullptr;
|
||||||
|
|
||||||
|
LiveCellRefBase(unsigned int type, const ESM::CellRef& cref);
|
||||||
LiveCellRefBase(unsigned int type, const ESM4::Reference& cref);
|
LiveCellRefBase(unsigned int type, const ESM4::Reference& cref);
|
||||||
LiveCellRefBase(unsigned int type, const ESM4::ActorCharacter& cref);
|
LiveCellRefBase(unsigned int type, const ESM4::ActorCharacter& cref);
|
||||||
|
|
||||||
|
LiveCellRefBase(const LiveCellRefBase& other) = default;
|
||||||
|
|
||||||
|
LiveCellRefBase(LiveCellRefBase&& other) noexcept;
|
||||||
|
|
||||||
/* Need this for the class to be recognized as polymorphic */
|
/* Need this for the class to be recognized as polymorphic */
|
||||||
virtual ~LiveCellRefBase();
|
virtual ~LiveCellRefBase();
|
||||||
|
|
||||||
|
LiveCellRefBase& operator=(const LiveCellRefBase& other) = default;
|
||||||
|
|
||||||
|
LiveCellRefBase& operator=(LiveCellRefBase&& other) noexcept;
|
||||||
|
|
||||||
virtual void load(const ESM::ObjectState& state) = 0;
|
virtual void load(const ESM::ObjectState& state) = 0;
|
||||||
///< Load state into a LiveCellRef, that has already been initialised with base and class.
|
///< Load state into a LiveCellRef, that has already been initialised with base and class.
|
||||||
///
|
///
|
||||||
|
@ -132,12 +144,6 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveCellRef(const X* b = nullptr)
|
|
||||||
: LiveCellRefBase(X::sRecordId)
|
|
||||||
, mBase(b)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// The object that this instance is based on.
|
// The object that this instance is based on.
|
||||||
const X* mBase;
|
const X* mBase;
|
||||||
|
|
||||||
|
|
|
@ -36,8 +36,20 @@
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
ESM::CellRef makePlayerCellRef()
|
||||||
|
{
|
||||||
|
ESM::CellRef result;
|
||||||
|
result.blank();
|
||||||
|
result.mRefID = ESM::RefId::stringRefId("Player");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Player::Player(const ESM::NPC* player)
|
Player::Player(const ESM::NPC* player)
|
||||||
: mCellStore(nullptr)
|
: mPlayer(makePlayerCellRef(), player)
|
||||||
|
, mCellStore(nullptr)
|
||||||
, mLastKnownExteriorPosition(0, 0, 0)
|
, mLastKnownExteriorPosition(0, 0, 0)
|
||||||
, mMarkedPosition(ESM::Position())
|
, mMarkedPosition(ESM::Position())
|
||||||
, mMarkedCell(nullptr)
|
, mMarkedCell(nullptr)
|
||||||
|
@ -46,11 +58,6 @@ namespace MWWorld
|
||||||
, mPaidCrimeId(-1)
|
, mPaidCrimeId(-1)
|
||||||
, mJumping(false)
|
, mJumping(false)
|
||||||
{
|
{
|
||||||
ESM::CellRef cellRef;
|
|
||||||
cellRef.blank();
|
|
||||||
cellRef.mRefID = ESM::RefId::stringRefId("Player");
|
|
||||||
mPlayer = LiveCellRef<ESM::NPC>(cellRef, player);
|
|
||||||
|
|
||||||
ESM::Position playerPos = mPlayer.mData.getPosition();
|
ESM::Position playerPos = mPlayer.mData.getPosition();
|
||||||
playerPos.pos[0] = playerPos.pos[1] = playerPos.pos[2] = 0;
|
playerPos.pos[0] = playerPos.pos[1] = playerPos.pos[2] = 0;
|
||||||
mPlayer.mData.setPosition(playerPos);
|
mPlayer.mData.setPosition(playerPos);
|
||||||
|
|
|
@ -451,8 +451,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
else if (!ESM::isEsm4Ext(worldspace))
|
else if (!ESM::isEsm4Ext(worldspace))
|
||||||
{
|
{
|
||||||
static std::vector<float> defaultHeight;
|
static const std::vector<float> defaultHeight(verts * verts, ESM::Land::DEFAULT_HEIGHT);
|
||||||
defaultHeight.resize(verts * verts, ESM::Land::DEFAULT_HEIGHT);
|
|
||||||
mPhysics->addHeightField(defaultHeight.data(), cellX, cellY, worldsize, verts,
|
mPhysics->addHeightField(defaultHeight.data(), cellX, cellY, worldsize, verts,
|
||||||
ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT, land.get());
|
ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT, land.get());
|
||||||
}
|
}
|
||||||
|
@ -1126,20 +1125,20 @@ namespace MWWorld
|
||||||
|
|
||||||
void Scene::preload(const std::string& mesh, bool useAnim)
|
void Scene::preload(const std::string& mesh, bool useAnim)
|
||||||
{
|
{
|
||||||
std::string meshPath = mesh;
|
const VFS::Path::Normalized meshPath = useAnim
|
||||||
if (useAnim)
|
? Misc::ResourceHelpers::correctActorModelPath(mesh, mRendering.getResourceSystem()->getVFS())
|
||||||
meshPath = Misc::ResourceHelpers::correctActorModelPath(meshPath, mRendering.getResourceSystem()->getVFS());
|
: mesh;
|
||||||
|
|
||||||
if (!mRendering.getResourceSystem()->getSceneManager()->checkLoaded(meshPath, mRendering.getReferenceTime()))
|
if (mRendering.getResourceSystem()->getSceneManager()->checkLoaded(meshPath, mRendering.getReferenceTime()))
|
||||||
{
|
return;
|
||||||
osg::ref_ptr<PreloadMeshItem> item(new PreloadMeshItem(
|
|
||||||
VFS::Path::toNormalized(meshPath), mRendering.getResourceSystem()->getSceneManager()));
|
osg::ref_ptr<PreloadMeshItem> item(
|
||||||
|
new PreloadMeshItem(meshPath, mRendering.getResourceSystem()->getSceneManager()));
|
||||||
mRendering.getWorkQueue()->addWorkItem(item);
|
mRendering.getWorkQueue()->addWorkItem(item);
|
||||||
const auto isDone = [](const osg::ref_ptr<SceneUtil::WorkItem>& v) { return v->isDone(); };
|
const auto isDone = [](const osg::ref_ptr<SceneUtil::WorkItem>& v) { return v->isDone(); };
|
||||||
mWorkItems.erase(std::remove_if(mWorkItems.begin(), mWorkItems.end(), isDone), mWorkItems.end());
|
mWorkItems.erase(std::remove_if(mWorkItems.begin(), mWorkItems.end(), isDone), mWorkItems.end());
|
||||||
mWorkItems.emplace_back(std::move(item));
|
mWorkItems.emplace_back(std::move(item));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void Scene::preloadCells(float dt)
|
void Scene::preloadCells(float dt)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3644,10 +3644,10 @@ namespace MWWorld
|
||||||
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
|
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::spawnEffect(const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos,
|
void World::spawnEffect(VFS::Path::NormalizedView model, const std::string& textureOverride,
|
||||||
float scale, bool isMagicVFX)
|
const osg::Vec3f& worldPos, float scale, bool isMagicVFX)
|
||||||
{
|
{
|
||||||
mRendering->spawnEffect(VFS::Path::toNormalized(model), textureOverride, worldPos, scale, isMagicVFX);
|
mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ResetActorsVisitor
|
struct ResetActorsVisitor
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <components/esm3/readerscache.hpp>
|
#include <components/esm3/readerscache.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
@ -603,8 +604,8 @@ namespace MWWorld
|
||||||
/// Spawn a blood effect for \a ptr at \a worldPosition
|
/// Spawn a blood effect for \a ptr at \a worldPosition
|
||||||
void spawnBloodEffect(const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) override;
|
void spawnBloodEffect(const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) override;
|
||||||
|
|
||||||
void spawnEffect(const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos,
|
void spawnEffect(VFS::Path::NormalizedView model, const std::string& textureOverride,
|
||||||
float scale = 1.f, bool isMagicVFX = true) override;
|
const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) override;
|
||||||
|
|
||||||
/// @see MWWorld::WeatherManager::isInStorm
|
/// @see MWWorld::WeatherManager::isInStorm
|
||||||
bool isInStorm() const override;
|
bool isInStorm() const override;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/esm/defs.hpp>
|
#include <components/esm/defs.hpp>
|
||||||
|
@ -339,6 +340,20 @@ namespace MWWorld
|
||||||
throw std::runtime_error(std::string("Can't find cell with name ") + std::string(name));
|
throw std::runtime_error(std::string("Can't find cell with name ") + std::string(name));
|
||||||
return *result;
|
return *result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldModel::registerPtr(const Ptr& ptr)
|
||||||
|
{
|
||||||
|
if (ptr.mRef == nullptr)
|
||||||
|
throw std::logic_error("Ptr with nullptr mRef is not allowed to be registered");
|
||||||
|
mPtrRegistry.insert(ptr);
|
||||||
|
ptr.mRef->mWorldModel = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModel::deregisterLiveCellRef(LiveCellRefBase& ref) noexcept
|
||||||
|
{
|
||||||
|
mPtrRegistry.remove(ref);
|
||||||
|
ref.mWorldModel = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr MWWorld::WorldModel::getPtrByRefId(const ESM::RefId& name)
|
MWWorld::Ptr MWWorld::WorldModel::getPtrByRefId(const ESM::RefId& name)
|
||||||
|
|
|
@ -77,9 +77,9 @@ namespace MWWorld
|
||||||
|
|
||||||
std::size_t getPtrRegistryRevision() const { return mPtrRegistry.getRevision(); }
|
std::size_t getPtrRegistryRevision() const { return mPtrRegistry.getRevision(); }
|
||||||
|
|
||||||
void registerPtr(const Ptr& ptr) { mPtrRegistry.insert(ptr); }
|
void registerPtr(const Ptr& ptr);
|
||||||
|
|
||||||
void deregisterLiveCellRef(const LiveCellRefBase& ref) noexcept { mPtrRegistry.remove(ref); }
|
void deregisterLiveCellRef(LiveCellRefBase& ref) noexcept;
|
||||||
|
|
||||||
void assignSaveFileRefNum(ESM::CellRef& ref) { mPtrRegistry.assign(ref); }
|
void assignSaveFileRefNum(ESM::CellRef& ref) { mPtrRegistry.assign(ref); }
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ file(GLOB UNITTEST_SRC_FILES
|
||||||
mwworld/test_store.cpp
|
mwworld/test_store.cpp
|
||||||
mwworld/testduration.cpp
|
mwworld/testduration.cpp
|
||||||
mwworld/testtimestamp.cpp
|
mwworld/testtimestamp.cpp
|
||||||
|
mwworld/testptr.cpp
|
||||||
|
|
||||||
mwdialogue/test_keywordsearch.cpp
|
mwdialogue/test_keywordsearch.cpp
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,28 @@
|
||||||
#include <components/debug/debugging.hpp>
|
#include <components/debug/debugging.hpp>
|
||||||
|
#include <components/misc/strings/conversion.hpp>
|
||||||
|
#include <components/settings/parser.hpp>
|
||||||
|
#include <components/settings/values.hpp>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
Log::sMinDebugLevel = Debug::getDebugLevel();
|
Log::sMinDebugLevel = Debug::getDebugLevel();
|
||||||
|
|
||||||
|
const std::filesystem::path settingsDefaultPath = std::filesystem::path{ OPENMW_PROJECT_SOURCE_DIR } / "files"
|
||||||
|
/ Misc::StringUtils::stringToU8String("settings-default.cfg");
|
||||||
|
|
||||||
|
Settings::SettingsFileParser parser;
|
||||||
|
parser.loadSettingsFile(settingsDefaultPath, Settings::Manager::mDefaultSettings);
|
||||||
|
|
||||||
|
Settings::StaticValues::initDefaults();
|
||||||
|
|
||||||
|
Settings::Manager::mUserSettings = Settings::Manager::mDefaultSettings;
|
||||||
|
|
||||||
|
Settings::StaticValues::init();
|
||||||
|
|
||||||
testing::InitGoogleTest(&argc, argv);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
}
|
}
|
||||||
|
|
82
apps/openmw_tests/mwworld/testptr.cpp
Normal file
82
apps/openmw_tests/mwworld/testptr.cpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
#include "apps/openmw/mwclass/npc.hpp"
|
||||||
|
#include "apps/openmw/mwworld/esmstore.hpp"
|
||||||
|
#include "apps/openmw/mwworld/livecellref.hpp"
|
||||||
|
#include "apps/openmw/mwworld/ptr.hpp"
|
||||||
|
#include "apps/openmw/mwworld/worldmodel.hpp"
|
||||||
|
|
||||||
|
#include <components/esm3/loadnpc.hpp>
|
||||||
|
#include <components/esm3/readerscache.hpp>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
TEST(MWWorldPtrTest, toStringShouldReturnHumanReadableTextRepresentationOfPtrWithNullRef)
|
||||||
|
{
|
||||||
|
Ptr ptr;
|
||||||
|
EXPECT_EQ(ptr.toString(), "null object");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MWWorldPtrTest, toStringShouldReturnHumanReadableTextRepresentationOfPtrWithDeletedRef)
|
||||||
|
{
|
||||||
|
MWClass::Npc::registerSelf();
|
||||||
|
ESM::NPC npc;
|
||||||
|
npc.blank();
|
||||||
|
npc.mId = ESM::RefId::stringRefId("Player");
|
||||||
|
ESMStore store;
|
||||||
|
store.insert(npc);
|
||||||
|
ESM::CellRef cellRef;
|
||||||
|
cellRef.blank();
|
||||||
|
cellRef.mRefID = npc.mId;
|
||||||
|
cellRef.mRefNum = ESM::RefNum{ .mIndex = 0x2a, .mContentFile = 0xd };
|
||||||
|
LiveCellRef<ESM::NPC> liveCellRef(cellRef, &npc);
|
||||||
|
liveCellRef.mData.setDeletedByContentFile(true);
|
||||||
|
Ptr ptr(&liveCellRef);
|
||||||
|
EXPECT_EQ(ptr.toString(), "deleted object0xd00002a (NPC, \"player\")");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MWWorldPtrTest, toStringShouldReturnHumanReadableTextRepresentationOfPtr)
|
||||||
|
{
|
||||||
|
MWClass::Npc::registerSelf();
|
||||||
|
ESM::NPC npc;
|
||||||
|
npc.blank();
|
||||||
|
npc.mId = ESM::RefId::stringRefId("Player");
|
||||||
|
ESMStore store;
|
||||||
|
store.insert(npc);
|
||||||
|
ESM::CellRef cellRef;
|
||||||
|
cellRef.blank();
|
||||||
|
cellRef.mRefID = npc.mId;
|
||||||
|
cellRef.mRefNum = ESM::RefNum{ .mIndex = 0x2a, .mContentFile = 0xd };
|
||||||
|
LiveCellRef<ESM::NPC> liveCellRef(cellRef, &npc);
|
||||||
|
Ptr ptr(&liveCellRef);
|
||||||
|
EXPECT_EQ(ptr.toString(), "object0xd00002a (NPC, \"player\")");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MWWorldPtrTest, underlyingLiveCellRefShouldBeDeregisteredOnDestruction)
|
||||||
|
{
|
||||||
|
MWClass::Npc::registerSelf();
|
||||||
|
ESM::NPC npc;
|
||||||
|
npc.blank();
|
||||||
|
npc.mId = ESM::RefId::stringRefId("Player");
|
||||||
|
ESMStore store;
|
||||||
|
store.insert(npc);
|
||||||
|
ESM::ReadersCache readersCache;
|
||||||
|
WorldModel worldModel(store, readersCache);
|
||||||
|
ESM::CellRef cellRef;
|
||||||
|
cellRef.blank();
|
||||||
|
cellRef.mRefID = npc.mId;
|
||||||
|
cellRef.mRefNum = ESM::FormId{ .mIndex = 0x2a, .mContentFile = 0xd };
|
||||||
|
{
|
||||||
|
LiveCellRef<ESM::NPC> liveCellRef(cellRef, &npc);
|
||||||
|
Ptr ptr(&liveCellRef);
|
||||||
|
worldModel.registerPtr(ptr);
|
||||||
|
ASSERT_EQ(worldModel.getPtr(cellRef.mRefNum), ptr);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(worldModel.getPtr(cellRef.mRefNum), Ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -239,7 +239,7 @@ namespace Debug
|
||||||
group->push(state);
|
group->push(state);
|
||||||
lastAppliedStack.push_back(group);
|
lastAppliedStack.push_back(group);
|
||||||
}
|
}
|
||||||
if (!(lastAppliedStack.back() == this))
|
if (lastAppliedStack.empty() || !(lastAppliedStack.back() == this))
|
||||||
{
|
{
|
||||||
push(state);
|
push(state);
|
||||||
lastAppliedStack.push_back(this);
|
lastAppliedStack.push_back(this);
|
||||||
|
|
|
@ -56,7 +56,7 @@ void ESM::LuaScriptsCfg::load(ESMReader& esm)
|
||||||
{
|
{
|
||||||
mScripts.emplace_back();
|
mScripts.emplace_back();
|
||||||
ESM::LuaScriptCfg& script = mScripts.back();
|
ESM::LuaScriptCfg& script = mScripts.back();
|
||||||
script.mScriptPath = esm.getHString();
|
script.mScriptPath = VFS::Path::Normalized(esm.getHString());
|
||||||
|
|
||||||
esm.getSubNameIs("LUAF");
|
esm.getSubNameIs("LUAF");
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
|
@ -161,7 +161,7 @@ void ESM::LuaScripts::load(ESMReader& esm)
|
||||||
{
|
{
|
||||||
while (esm.isNextSub("LUAS"))
|
while (esm.isNextSub("LUAS"))
|
||||||
{
|
{
|
||||||
std::string name = esm.getHString();
|
VFS::Path::Normalized name(esm.getHString());
|
||||||
std::string data = loadLuaBinaryData(esm);
|
std::string data = loadLuaBinaryData(esm);
|
||||||
std::vector<LuaTimer> timers;
|
std::vector<LuaTimer> timers;
|
||||||
while (esm.isNextSub("LUAT"))
|
while (esm.isNextSub("LUAT"))
|
||||||
|
|
|
@ -287,4 +287,10 @@ namespace ESM
|
||||||
loadDataImpl<false>(esm, isDeleted, cellRef);
|
loadDataImpl<false>(esm, isDeleted, cellRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CellRef makeBlankCellRef()
|
||||||
|
{
|
||||||
|
CellRef result;
|
||||||
|
result.blank();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,8 @@ namespace ESM
|
||||||
};
|
};
|
||||||
|
|
||||||
void skipLoadCellRef(ESMReader& esm, bool wideRefNum = false);
|
void skipLoadCellRef(ESMReader& esm, bool wideRefNum = false);
|
||||||
|
|
||||||
|
CellRef makeBlankCellRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -132,52 +132,60 @@ namespace ESM
|
||||||
esm.writeHNOString("DESC", mDescription);
|
esm.writeHNOString("DESC", mDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::map<short, short> makeEffectsMap()
|
||||||
|
{
|
||||||
|
std::map<short, short> effects;
|
||||||
|
|
||||||
|
effects[MagicEffect::Effects::DisintegrateArmor] = MagicEffect::Effects::Sanctuary;
|
||||||
|
effects[MagicEffect::Effects::DisintegrateWeapon] = MagicEffect::Effects::Sanctuary;
|
||||||
|
|
||||||
|
for (int i = MagicEffect::Effects::DrainAttribute; i <= MagicEffect::Effects::DamageSkill; ++i)
|
||||||
|
effects[i] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
for (int i = MagicEffect::Effects::AbsorbAttribute; i <= MagicEffect::Effects::AbsorbSkill; ++i)
|
||||||
|
effects[i] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
for (int i = MagicEffect::Effects::WeaknessToFire; i <= MagicEffect::Effects::WeaknessToNormalWeapons; ++i)
|
||||||
|
effects[i] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
|
||||||
|
effects[MagicEffect::Effects::Burden] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
effects[MagicEffect::Effects::Charm] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
effects[MagicEffect::Effects::Silence] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
effects[MagicEffect::Effects::Blind] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
effects[MagicEffect::Effects::Sound] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
effects[MagicEffect::Effects::CalmHumanoid + i] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
effects[MagicEffect::Effects::FrenzyHumanoid + i] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
effects[MagicEffect::Effects::DemoralizeHumanoid + i] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
effects[MagicEffect::Effects::RallyHumanoid + i] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
}
|
||||||
|
|
||||||
|
effects[MagicEffect::Effects::TurnUndead] = MagicEffect::Effects::ResistMagicka;
|
||||||
|
|
||||||
|
effects[MagicEffect::Effects::FireDamage] = MagicEffect::Effects::ResistFire;
|
||||||
|
effects[MagicEffect::Effects::FrostDamage] = MagicEffect::Effects::ResistFrost;
|
||||||
|
effects[MagicEffect::Effects::ShockDamage] = MagicEffect::Effects::ResistShock;
|
||||||
|
effects[MagicEffect::Effects::Vampirism] = MagicEffect::Effects::ResistCommonDisease;
|
||||||
|
effects[MagicEffect::Effects::Corprus] = MagicEffect::Effects::ResistCorprusDisease;
|
||||||
|
effects[MagicEffect::Effects::Poison] = MagicEffect::Effects::ResistPoison;
|
||||||
|
effects[MagicEffect::Effects::Paralyze] = MagicEffect::Effects::ResistParalysis;
|
||||||
|
|
||||||
|
return effects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
short MagicEffect::getResistanceEffect(short effect)
|
short MagicEffect::getResistanceEffect(short effect)
|
||||||
{
|
{
|
||||||
// Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute
|
// Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute
|
||||||
|
|
||||||
// <Effect, Effect providing resistance against first effect>
|
// <Effect, Effect providing resistance against first effect>
|
||||||
static std::map<short, short> effects;
|
static const std::map<short, short> effects = makeEffectsMap();
|
||||||
if (effects.empty())
|
|
||||||
{
|
|
||||||
effects[DisintegrateArmor] = Sanctuary;
|
|
||||||
effects[DisintegrateWeapon] = Sanctuary;
|
|
||||||
|
|
||||||
for (int i = DrainAttribute; i <= DamageSkill; ++i)
|
if (const auto it = effects.find(effect); it != effects.end())
|
||||||
effects[i] = ResistMagicka;
|
return it->second;
|
||||||
for (int i = AbsorbAttribute; i <= AbsorbSkill; ++i)
|
|
||||||
effects[i] = ResistMagicka;
|
|
||||||
for (int i = WeaknessToFire; i <= WeaknessToNormalWeapons; ++i)
|
|
||||||
effects[i] = ResistMagicka;
|
|
||||||
|
|
||||||
effects[Burden] = ResistMagicka;
|
|
||||||
effects[Charm] = ResistMagicka;
|
|
||||||
effects[Silence] = ResistMagicka;
|
|
||||||
effects[Blind] = ResistMagicka;
|
|
||||||
effects[Sound] = ResistMagicka;
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; ++i)
|
|
||||||
{
|
|
||||||
effects[CalmHumanoid + i] = ResistMagicka;
|
|
||||||
effects[FrenzyHumanoid + i] = ResistMagicka;
|
|
||||||
effects[DemoralizeHumanoid + i] = ResistMagicka;
|
|
||||||
effects[RallyHumanoid + i] = ResistMagicka;
|
|
||||||
}
|
|
||||||
|
|
||||||
effects[TurnUndead] = ResistMagicka;
|
|
||||||
|
|
||||||
effects[FireDamage] = ResistFire;
|
|
||||||
effects[FrostDamage] = ResistFrost;
|
|
||||||
effects[ShockDamage] = ResistShock;
|
|
||||||
effects[Vampirism] = ResistCommonDisease;
|
|
||||||
effects[Corprus] = ResistCorprusDisease;
|
|
||||||
effects[Poison] = ResistPoison;
|
|
||||||
effects[Paralyze] = ResistParalysis;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (effects.find(effect) != effects.end())
|
|
||||||
return effects[effect];
|
|
||||||
else
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -320,16 +320,18 @@ namespace ESM4
|
||||||
std::filesystem::path path = strings / (prefix + language + suffix);
|
std::filesystem::path path = strings / (prefix + language + suffix);
|
||||||
if (mVFS != nullptr)
|
if (mVFS != nullptr)
|
||||||
{
|
{
|
||||||
std::string vfsPath = Files::pathToUnicodeString(path);
|
VFS::Path::Normalized vfsPath(Files::pathToUnicodeString(path));
|
||||||
if (!mVFS->exists(vfsPath))
|
Files::IStreamPtr stream = mVFS->find(vfsPath);
|
||||||
|
|
||||||
|
if (stream == nullptr)
|
||||||
{
|
{
|
||||||
path = strings / (prefix + altLanguage + suffix);
|
path = strings / (prefix + altLanguage + suffix);
|
||||||
vfsPath = Files::pathToUnicodeString(path);
|
vfsPath = VFS::Path::Normalized(Files::pathToUnicodeString(path));
|
||||||
|
stream = mVFS->find(vfsPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mVFS->exists(vfsPath))
|
if (stream != nullptr)
|
||||||
{
|
{
|
||||||
const Files::IStreamPtr stream = mVFS->get(vfsPath);
|
|
||||||
buildLStringIndex(stringType, *stream);
|
buildLStringIndex(stringType, *stream);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,7 @@ namespace LuaUi
|
||||||
ContentView content(LuaUtil::cast<sol::table>(contentObj));
|
ContentView content(LuaUtil::cast<sol::table>(contentObj));
|
||||||
result.resize(content.size());
|
result.resize(content.size());
|
||||||
size_t minSize = std::min(children.size(), content.size());
|
size_t minSize = std::min(children.size(), content.size());
|
||||||
|
std::vector<WidgetExtension*> toDestroy;
|
||||||
for (size_t i = 0; i < minSize; i++)
|
for (size_t i = 0; i < minSize; i++)
|
||||||
{
|
{
|
||||||
WidgetExtension* ext = children[i];
|
WidgetExtension* ext = children[i];
|
||||||
|
@ -121,7 +122,7 @@ namespace LuaUi
|
||||||
{
|
{
|
||||||
WidgetExtension* root = pluckElementRoot(child, depth);
|
WidgetExtension* root = pluckElementRoot(child, depth);
|
||||||
if (ext != root)
|
if (ext != root)
|
||||||
destroyChild(ext);
|
toDestroy.emplace_back(ext);
|
||||||
result[i] = root;
|
result[i] = root;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -133,14 +134,12 @@ namespace LuaUi
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
destroyChild(ext);
|
toDestroy.emplace_back(ext);
|
||||||
ext = createWidget(newLayout, false, depth);
|
ext = createWidget(newLayout, false, depth);
|
||||||
}
|
}
|
||||||
result[i] = ext;
|
result[i] = ext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t i = minSize; i < children.size(); i++)
|
|
||||||
destroyChild(children[i]);
|
|
||||||
for (size_t i = minSize; i < content.size(); i++)
|
for (size_t i = minSize; i < content.size(); i++)
|
||||||
{
|
{
|
||||||
sol::object child = content.at(i);
|
sol::object child = content.at(i);
|
||||||
|
@ -149,6 +148,11 @@ namespace LuaUi
|
||||||
else
|
else
|
||||||
result[i] = createWidget(child.as<sol::table>(), false, depth);
|
result[i] = createWidget(child.as<sol::table>(), false, depth);
|
||||||
}
|
}
|
||||||
|
// Don't destroy anything until element creation has had a chance to throw
|
||||||
|
for (size_t i = minSize; i < children.size(); i++)
|
||||||
|
destroyChild(children[i]);
|
||||||
|
for (WidgetExtension* ext : toDestroy)
|
||||||
|
destroyChild(ext);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +221,9 @@ namespace LuaUi
|
||||||
std::string setLayer(WidgetExtension* ext, const sol::table& layout)
|
std::string setLayer(WidgetExtension* ext, const sol::table& layout)
|
||||||
{
|
{
|
||||||
MyGUI::ILayer* layerNode = ext->widget()->getLayer();
|
MyGUI::ILayer* layerNode = ext->widget()->getLayer();
|
||||||
std::string currentLayer = layerNode ? layerNode->getName() : std::string();
|
std::string_view currentLayer;
|
||||||
|
if (layerNode)
|
||||||
|
currentLayer = layerNode->getName();
|
||||||
std::string newLayer = layout.get_or(LayoutKeys::layer, std::string());
|
std::string newLayer = layout.get_or(LayoutKeys::layer, std::string());
|
||||||
if (!newLayer.empty() && !MyGUI::LayerManager::getInstance().isExist(newLayer))
|
if (!newLayer.empty() && !MyGUI::LayerManager::getInstance().isExist(newLayer))
|
||||||
throw std::logic_error(std::string("Layer ") + newLayer + " doesn't exist");
|
throw std::logic_error(std::string("Layer ") + newLayer + " doesn't exist");
|
||||||
|
@ -278,9 +284,20 @@ namespace LuaUi
|
||||||
WidgetExtension* parent = mRoot->getParent();
|
WidgetExtension* parent = mRoot->getParent();
|
||||||
auto children = parent->children();
|
auto children = parent->children();
|
||||||
auto it = std::find(children.begin(), children.end(), mRoot);
|
auto it = std::find(children.begin(), children.end(), mRoot);
|
||||||
mRoot = createWidget(layout(), true, 0);
|
|
||||||
assert(it != children.end());
|
assert(it != children.end());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mRoot = createWidget(layout(), true, 0);
|
||||||
*it = mRoot;
|
*it = mRoot;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
// Remove mRoot from its parent's children even if we couldn't replace it
|
||||||
|
children.erase(it);
|
||||||
|
parent->setChildren(children);
|
||||||
|
mRoot = nullptr;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
parent->setChildren(children);
|
parent->setChildren(children);
|
||||||
mRoot->updateCoord();
|
mRoot->updateCoord();
|
||||||
}
|
}
|
||||||
|
@ -300,6 +317,10 @@ namespace LuaUi
|
||||||
{
|
{
|
||||||
if (mRoot != nullptr)
|
if (mRoot != nullptr)
|
||||||
{
|
{
|
||||||
|
// If someone decided to destroy an element used as another element's content, we need to detach it
|
||||||
|
// first so the parent doesn't end up holding a stale pointer
|
||||||
|
if (WidgetExtension* parent = mRoot->getParent())
|
||||||
|
parent->detachChildrenIf([&](WidgetExtension* child) { return child == mRoot; });
|
||||||
destroyRoot(mRoot);
|
destroyRoot(mRoot);
|
||||||
mRoot = nullptr;
|
mRoot = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,7 @@ namespace LuaUi
|
||||||
{
|
{
|
||||||
mParent = nullptr;
|
mParent = nullptr;
|
||||||
widget()->detachFromWidget();
|
widget()->detachFromWidget();
|
||||||
|
widget()->detachFromLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
WidgetExtension* WidgetExtension::findDeep(std::string_view flagName)
|
WidgetExtension* WidgetExtension::findDeep(std::string_view flagName)
|
||||||
|
|
|
@ -179,7 +179,7 @@ namespace LuaUi
|
||||||
|
|
||||||
void updateVisible();
|
void updateVisible();
|
||||||
|
|
||||||
void detachChildrenIf(auto&& predicate, std::vector<WidgetExtension*> children)
|
void detachChildrenIf(auto&& predicate, std::vector<WidgetExtension*>& children)
|
||||||
{
|
{
|
||||||
for (auto it = children.begin(); it != children.end();)
|
for (auto it = children.begin(); it != children.end();)
|
||||||
{
|
{
|
||||||
|
|
|
@ -228,7 +228,7 @@ bool Misc::ResourceHelpers::isHiddenMarker(const ESM::RefId& id)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::string getLODMeshNameImpl(std::string resPath, const VFS::Manager* vfs, std::string_view pattern)
|
std::string getLODMeshNameImpl(std::string resPath, std::string_view pattern)
|
||||||
{
|
{
|
||||||
if (auto w = Misc::findExtension(resPath); w != std::string::npos)
|
if (auto w = Misc::findExtension(resPath); w != std::string::npos)
|
||||||
resPath.insert(w, pattern);
|
resPath.insert(w, pattern);
|
||||||
|
@ -237,7 +237,7 @@ namespace
|
||||||
|
|
||||||
std::string getBestLODMeshName(std::string const& resPath, const VFS::Manager* vfs, std::string_view pattern)
|
std::string getBestLODMeshName(std::string const& resPath, const VFS::Manager* vfs, std::string_view pattern)
|
||||||
{
|
{
|
||||||
if (const auto& result = getLODMeshNameImpl(resPath, vfs, pattern); vfs->exists(result))
|
if (std::string result = getLODMeshNameImpl(resPath, pattern); vfs->exists(result))
|
||||||
return result;
|
return result;
|
||||||
return resPath;
|
return resPath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ namespace osgMyGUI
|
||||||
if (!mImageManager)
|
if (!mImageManager)
|
||||||
throw std::runtime_error("No imagemanager set");
|
throw std::runtime_error("No imagemanager set");
|
||||||
|
|
||||||
osg::ref_ptr<osg::Image> image(mImageManager->getImage(VFS::Path::toNormalized(fname)));
|
osg::ref_ptr<osg::Image> image(mImageManager->getImage(VFS::Path::Normalized(fname)));
|
||||||
mTexture = new osg::Texture2D(image);
|
mTexture = new osg::Texture2D(image);
|
||||||
mTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
mTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||||
mTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
mTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace NifBullet
|
||||||
mShape->mFileName = nif.getFilename();
|
mShape->mFileName = nif.getFilename();
|
||||||
if (roots.empty())
|
if (roots.empty())
|
||||||
{
|
{
|
||||||
warn("Found no root nodes in NIF file " + mShape->mFileName);
|
warn("Found no root nodes in NIF file " + mShape->mFileName.value());
|
||||||
return mShape;
|
return mShape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ namespace NifBullet
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
warn("Invalid Bounding Box node bounds in file " + mShape->mFileName);
|
warn("Invalid Bounding Box node bounds in file " + mShape->mFileName.value());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#ifndef OPENMW_COMPONENTS_RESOURCE_BULLETSHAPE_H
|
#ifndef OPENMW_COMPONENTS_RESOURCE_BULLETSHAPE_H
|
||||||
#define OPENMW_COMPONENTS_RESOURCE_BULLETSHAPE_H
|
#define OPENMW_COMPONENTS_RESOURCE_BULLETSHAPE_H
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -12,6 +11,8 @@
|
||||||
#include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>
|
#include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>
|
||||||
#include <BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h>
|
#include <BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h>
|
||||||
|
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
class btCollisionShape;
|
class btCollisionShape;
|
||||||
|
|
||||||
namespace NifBullet
|
namespace NifBullet
|
||||||
|
@ -56,7 +57,7 @@ namespace Resource
|
||||||
// we store the node's record index mapped to the child index of the shape in the btCompoundShape.
|
// we store the node's record index mapped to the child index of the shape in the btCompoundShape.
|
||||||
std::map<int, int> mAnimatedShapes;
|
std::map<int, int> mAnimatedShapes;
|
||||||
|
|
||||||
std::string mFileName;
|
VFS::Path::Normalized mFileName;
|
||||||
std::string mFileHash;
|
std::string mFileHash;
|
||||||
|
|
||||||
VisualCollisionType mVisualCollisionType = VisualCollisionType::None;
|
VisualCollisionType mVisualCollisionType = VisualCollisionType::None;
|
||||||
|
|
|
@ -106,30 +106,27 @@ namespace Resource
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
BulletShapeManager::~BulletShapeManager() {}
|
BulletShapeManager::~BulletShapeManager() = default;
|
||||||
|
|
||||||
osg::ref_ptr<const BulletShape> BulletShapeManager::getShape(const std::string& name)
|
osg::ref_ptr<const BulletShape> BulletShapeManager::getShape(VFS::Path::NormalizedView name)
|
||||||
{
|
{
|
||||||
const VFS::Path::Normalized normalized(name);
|
if (osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(name))
|
||||||
|
return osg::ref_ptr<BulletShape>(static_cast<BulletShape*>(obj.get()));
|
||||||
|
|
||||||
osg::ref_ptr<BulletShape> shape;
|
osg::ref_ptr<BulletShape> shape;
|
||||||
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
|
|
||||||
if (obj)
|
if (Misc::getFileExtension(name.value()) == "nif")
|
||||||
shape = osg::ref_ptr<BulletShape>(static_cast<BulletShape*>(obj.get()));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Misc::getFileExtension(normalized) == "nif")
|
|
||||||
{
|
{
|
||||||
NifBullet::BulletNifLoader loader;
|
NifBullet::BulletNifLoader loader;
|
||||||
shape = loader.load(*mNifFileManager->get(normalized));
|
shape = loader.load(*mNifFileManager->get(name));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: support .bullet shape files
|
// TODO: support .bullet shape files
|
||||||
|
|
||||||
osg::ref_ptr<const osg::Node> constNode(mSceneManager->getTemplate(normalized));
|
osg::ref_ptr<const osg::Node> constNode(mSceneManager->getTemplate(name));
|
||||||
osg::ref_ptr<osg::Node> node(const_cast<osg::Node*>(
|
// const-trickery required because there is no const version of NodeVisitor
|
||||||
constNode.get())); // const-trickery required because there is no const version of NodeVisitor
|
osg::ref_ptr<osg::Node> node(const_cast<osg::Node*>(constNode.get()));
|
||||||
|
|
||||||
// Check first if there's a custom collision node
|
// Check first if there's a custom collision node
|
||||||
unsigned int visitAllNodesMask = 0xffffffff;
|
unsigned int visitAllNodesMask = 0xffffffff;
|
||||||
|
@ -158,41 +155,34 @@ namespace Resource
|
||||||
|
|
||||||
if (shape != nullptr)
|
if (shape != nullptr)
|
||||||
{
|
{
|
||||||
shape->mFileName = normalized;
|
shape->mFileName = name;
|
||||||
constNode->getUserValue(Misc::OsgUserValues::sFileHash, shape->mFileHash);
|
constNode->getUserValue(Misc::OsgUserValues::sFileHash, shape->mFileHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mCache->addEntryToObjectCache(normalized, shape);
|
mCache->addEntryToObjectCache(name.value(), shape);
|
||||||
}
|
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::cacheInstance(const std::string& name)
|
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::cacheInstance(VFS::Path::NormalizedView name)
|
||||||
{
|
{
|
||||||
const std::string normalized = VFS::Path::normalizeFilename(name);
|
osg::ref_ptr<BulletShapeInstance> instance = createInstance(name);
|
||||||
|
if (instance != nullptr)
|
||||||
osg::ref_ptr<BulletShapeInstance> instance = createInstance(normalized);
|
mInstanceCache->addEntryToObjectCache(name, instance.get());
|
||||||
if (instance)
|
|
||||||
mInstanceCache->addEntryToObjectCache(normalized, instance.get());
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::getInstance(const std::string& name)
|
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::getInstance(VFS::Path::NormalizedView name)
|
||||||
{
|
{
|
||||||
const std::string normalized = VFS::Path::normalizeFilename(name);
|
if (osg::ref_ptr<osg::Object> obj = mInstanceCache->takeFromObjectCache(name))
|
||||||
|
|
||||||
osg::ref_ptr<osg::Object> obj = mInstanceCache->takeFromObjectCache(normalized);
|
|
||||||
if (obj.get())
|
|
||||||
return static_cast<BulletShapeInstance*>(obj.get());
|
return static_cast<BulletShapeInstance*>(obj.get());
|
||||||
else
|
return createInstance(name);
|
||||||
return createInstance(normalized);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::createInstance(const std::string& name)
|
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::createInstance(VFS::Path::NormalizedView name)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<const BulletShape> shape = getShape(name);
|
if (osg::ref_ptr<const BulletShape> shape = getShape(name))
|
||||||
if (shape)
|
|
||||||
return makeInstance(std::move(shape));
|
return makeInstance(std::move(shape));
|
||||||
return osg::ref_ptr<BulletShapeInstance>();
|
return osg::ref_ptr<BulletShapeInstance>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
#ifndef OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H
|
#ifndef OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H
|
||||||
#define OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H
|
#define OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
#include "bulletshape.hpp"
|
#include "bulletshape.hpp"
|
||||||
#include "resourcemanager.hpp"
|
#include "resourcemanager.hpp"
|
||||||
|
|
||||||
|
@ -30,16 +29,16 @@ namespace Resource
|
||||||
~BulletShapeManager();
|
~BulletShapeManager();
|
||||||
|
|
||||||
/// @note May return a null pointer if the object has no shape.
|
/// @note May return a null pointer if the object has no shape.
|
||||||
osg::ref_ptr<const BulletShape> getShape(const std::string& name);
|
osg::ref_ptr<const BulletShape> getShape(VFS::Path::NormalizedView name);
|
||||||
|
|
||||||
/// Create an instance of the given shape and cache it for later use, so that future calls to getInstance() can
|
/// Create an instance of the given shape and cache it for later use, so that future calls to getInstance() can
|
||||||
/// simply return the cached instance instead of having to create a new one.
|
/// simply return the cached instance instead of having to create a new one.
|
||||||
/// @note The returned ref_ptr may be kept by the caller to ensure that the instance stays in cache for as long
|
/// @note The returned ref_ptr may be kept by the caller to ensure that the instance stays in cache for as long
|
||||||
/// as needed.
|
/// as needed.
|
||||||
osg::ref_ptr<BulletShapeInstance> cacheInstance(const std::string& name);
|
osg::ref_ptr<BulletShapeInstance> cacheInstance(VFS::Path::NormalizedView name);
|
||||||
|
|
||||||
/// @note May return a null pointer if the object has no shape.
|
/// @note May return a null pointer if the object has no shape.
|
||||||
osg::ref_ptr<BulletShapeInstance> getInstance(const std::string& name);
|
osg::ref_ptr<BulletShapeInstance> getInstance(VFS::Path::NormalizedView name);
|
||||||
|
|
||||||
/// @see ResourceManager::updateCache
|
/// @see ResourceManager::updateCache
|
||||||
void updateCache(double referenceTime) override;
|
void updateCache(double referenceTime) override;
|
||||||
|
@ -49,7 +48,7 @@ namespace Resource
|
||||||
void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
|
void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name);
|
osg::ref_ptr<BulletShapeInstance> createInstance(VFS::Path::NormalizedView name);
|
||||||
|
|
||||||
osg::ref_ptr<MultiObjectCache> mInstanceCache;
|
osg::ref_ptr<MultiObjectCache> mInstanceCache;
|
||||||
SceneManager* mSceneManager;
|
SceneManager* mSceneManager;
|
||||||
|
|
|
@ -16,10 +16,6 @@
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -97,7 +93,7 @@ namespace Resource
|
||||||
|
|
||||||
for (CellRef& cellRef : cellRefs)
|
for (CellRef& cellRef : cellRefs)
|
||||||
{
|
{
|
||||||
std::string model(getModel(esmData, cellRef.mRefId, cellRef.mType));
|
VFS::Path::Normalized model(getModel(esmData, cellRef.mRefId, cellRef.mType));
|
||||||
if (model.empty())
|
if (model.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -107,7 +103,8 @@ namespace Resource
|
||||||
osg::ref_ptr<const Resource::BulletShape> shape = [&] {
|
osg::ref_ptr<const Resource::BulletShape> shape = [&] {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return bulletShapeManager.getShape("meshes/" + model);
|
constexpr VFS::Path::NormalizedView prefix("meshes");
|
||||||
|
return bulletShapeManager.getShape(prefix / model);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,11 +6,6 @@
|
||||||
|
|
||||||
namespace Resource
|
namespace Resource
|
||||||
{
|
{
|
||||||
|
|
||||||
MultiObjectCache::MultiObjectCache() {}
|
|
||||||
|
|
||||||
MultiObjectCache::~MultiObjectCache() {}
|
|
||||||
|
|
||||||
void MultiObjectCache::removeUnreferencedObjectsInCache()
|
void MultiObjectCache::removeUnreferencedObjectsInCache()
|
||||||
{
|
{
|
||||||
std::vector<osg::ref_ptr<osg::Object>> objectsToRemove;
|
std::vector<osg::ref_ptr<osg::Object>> objectsToRemove;
|
||||||
|
@ -44,7 +39,7 @@ namespace Resource
|
||||||
_objectCache.clear();
|
_objectCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiObjectCache::addEntryToObjectCache(const std::string& filename, osg::Object* object)
|
void MultiObjectCache::addEntryToObjectCache(VFS::Path::NormalizedView filename, osg::Object* object)
|
||||||
{
|
{
|
||||||
if (!object)
|
if (!object)
|
||||||
{
|
{
|
||||||
|
@ -52,23 +47,23 @@ namespace Resource
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||||
_objectCache.insert(std::make_pair(filename, object));
|
_objectCache.emplace(filename, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Object> MultiObjectCache::takeFromObjectCache(const std::string& fileName)
|
osg::ref_ptr<osg::Object> MultiObjectCache::takeFromObjectCache(VFS::Path::NormalizedView fileName)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||||
++mGet;
|
++mGet;
|
||||||
ObjectCacheMap::iterator found = _objectCache.find(fileName);
|
const auto it = _objectCache.find(fileName);
|
||||||
if (found == _objectCache.end())
|
if (it != _objectCache.end())
|
||||||
return osg::ref_ptr<osg::Object>();
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Object> object = std::move(found->second);
|
osg::ref_ptr<osg::Object> object = std::move(it->second);
|
||||||
_objectCache.erase(found);
|
_objectCache.erase(it);
|
||||||
++mHit;
|
++mHit;
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiObjectCache::releaseGLObjects(osg::State* state)
|
void MultiObjectCache::releaseGLObjects(osg::State* state)
|
||||||
|
|
|
@ -3,11 +3,12 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <osg/Referenced>
|
#include <osg/Referenced>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
#include "cachestats.hpp"
|
#include "cachestats.hpp"
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
|
@ -23,18 +24,15 @@ namespace Resource
|
||||||
class MultiObjectCache : public osg::Referenced
|
class MultiObjectCache : public osg::Referenced
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MultiObjectCache();
|
|
||||||
~MultiObjectCache();
|
|
||||||
|
|
||||||
void removeUnreferencedObjectsInCache();
|
void removeUnreferencedObjectsInCache();
|
||||||
|
|
||||||
/** Remove all objects from the cache. */
|
/** Remove all objects from the cache. */
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void addEntryToObjectCache(const std::string& filename, osg::Object* object);
|
void addEntryToObjectCache(VFS::Path::NormalizedView filename, osg::Object* object);
|
||||||
|
|
||||||
/** Take an Object from cache. Return nullptr if no object found. */
|
/** Take an Object from cache. Return nullptr if no object found. */
|
||||||
osg::ref_ptr<osg::Object> takeFromObjectCache(const std::string& fileName);
|
osg::ref_ptr<osg::Object> takeFromObjectCache(VFS::Path::NormalizedView fileName);
|
||||||
|
|
||||||
/** call releaseGLObjects on all objects attached to the object cache.*/
|
/** call releaseGLObjects on all objects attached to the object cache.*/
|
||||||
void releaseGLObjects(osg::State* state);
|
void releaseGLObjects(osg::State* state);
|
||||||
|
@ -42,7 +40,7 @@ namespace Resource
|
||||||
CacheStats getStats() const;
|
CacheStats getStats() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::multimap<std::string, osg::ref_ptr<osg::Object>> ObjectCacheMap;
|
typedef std::multimap<VFS::Path::Normalized, osg::ref_ptr<osg::Object>, std::less<>> ObjectCacheMap;
|
||||||
|
|
||||||
ObjectCacheMap _objectCache;
|
ObjectCacheMap _objectCache;
|
||||||
mutable std::mutex _objectCacheMutex;
|
mutable std::mutex _objectCacheMutex;
|
||||||
|
|
|
@ -597,9 +597,9 @@ namespace Resource
|
||||||
mShaderManager->setShaderPath(path);
|
mShaderManager->setShaderPath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneManager::checkLoaded(const std::string& name, double timeStamp)
|
bool SceneManager::checkLoaded(VFS::Path::NormalizedView name, double timeStamp)
|
||||||
{
|
{
|
||||||
return mCache->checkInObjectCache(VFS::Path::normalizeFilename(name), timeStamp);
|
return mCache->checkInObjectCache(name, timeStamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::setUpNormalsRTForStateSet(osg::StateSet* stateset, bool enabled)
|
void SceneManager::setUpNormalsRTForStateSet(osg::StateSet* stateset, bool enabled)
|
||||||
|
|
|
@ -152,7 +152,7 @@ namespace Resource
|
||||||
void setShaderPath(const std::filesystem::path& path);
|
void setShaderPath(const std::filesystem::path& path);
|
||||||
|
|
||||||
/// Check if a given scene is loaded and if so, update its usage timestamp to prevent it from being unloaded
|
/// Check if a given scene is loaded and if so, update its usage timestamp to prevent it from being unloaded
|
||||||
bool checkLoaded(const std::string& name, double referenceTime);
|
bool checkLoaded(VFS::Path::NormalizedView name, double referenceTime);
|
||||||
|
|
||||||
/// Get a read-only copy of this scene "template"
|
/// Get a read-only copy of this scene "template"
|
||||||
/// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead.
|
/// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead.
|
||||||
|
|
|
@ -82,6 +82,9 @@ namespace SceneUtil
|
||||||
std::string_view mFilter;
|
std::string_view mFilter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
void mergeUserData(const osg::UserDataContainer* source, osg::Object* target)
|
void mergeUserData(const osg::UserDataContainer* source, osg::Object* target)
|
||||||
{
|
{
|
||||||
if (!source)
|
if (!source)
|
||||||
|
@ -97,6 +100,17 @@ namespace SceneUtil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::StateSet> makeFrontFaceStateSet()
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::FrontFace> frontFace = new osg::FrontFace;
|
||||||
|
frontFace->setMode(osg::FrontFace::CLOCKWISE);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::StateSet> frontFaceStateSet = new osg::StateSet;
|
||||||
|
frontFaceStateSet->setAttributeAndModes(frontFace, osg::StateAttribute::ON);
|
||||||
|
return frontFaceStateSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> attach(osg::ref_ptr<const osg::Node> toAttach, osg::Node* master, std::string_view filter,
|
osg::ref_ptr<osg::Node> attach(osg::ref_ptr<const osg::Node> toAttach, osg::Node* master, std::string_view filter,
|
||||||
osg::Group* attachNode, Resource::SceneManager* sceneManager, const osg::Quat* attitude)
|
osg::Group* attachNode, Resource::SceneManager* sceneManager, const osg::Quat* attitude)
|
||||||
{
|
{
|
||||||
|
@ -159,14 +173,8 @@ namespace SceneUtil
|
||||||
// Note: for absolute correctness we would need to check the current front face for every mesh then
|
// Note: for absolute correctness we would need to check the current front face for every mesh then
|
||||||
// invert it However MW isn't doing this either, so don't. Assuming all meshes are using backface
|
// invert it However MW isn't doing this either, so don't. Assuming all meshes are using backface
|
||||||
// culling is more efficient.
|
// culling is more efficient.
|
||||||
static osg::ref_ptr<osg::StateSet> frontFaceStateSet;
|
static const osg::ref_ptr<osg::StateSet> frontFaceStateSet = makeFrontFaceStateSet();
|
||||||
if (!frontFaceStateSet)
|
|
||||||
{
|
|
||||||
frontFaceStateSet = new osg::StateSet;
|
|
||||||
osg::FrontFace* frontFace = new osg::FrontFace;
|
|
||||||
frontFace->setMode(osg::FrontFace::CLOCKWISE);
|
|
||||||
frontFaceStateSet->setAttributeAndModes(frontFace, osg::StateAttribute::ON);
|
|
||||||
}
|
|
||||||
trans->setStateSet(frontFaceStateSet);
|
trans->setStateSet(frontFaceStateSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,11 @@ namespace Settings
|
||||||
SettingValue<bool> mKeyboardNavigation{ mIndex, "GUI", "keyboard navigation" };
|
SettingValue<bool> mKeyboardNavigation{ mIndex, "GUI", "keyboard navigation" };
|
||||||
SettingValue<bool> mColorTopicEnable{ mIndex, "GUI", "color topic enable" };
|
SettingValue<bool> mColorTopicEnable{ mIndex, "GUI", "color topic enable" };
|
||||||
SettingValue<MyGUI::Colour> mColorTopicSpecific{ mIndex, "GUI", "color topic specific" };
|
SettingValue<MyGUI::Colour> mColorTopicSpecific{ mIndex, "GUI", "color topic specific" };
|
||||||
|
SettingValue<MyGUI::Colour> mColorTopicSpecificOver{ mIndex, "GUI", "color topic specific over" };
|
||||||
|
SettingValue<MyGUI::Colour> mColorTopicSpecificPressed{ mIndex, "GUI", "color topic specific pressed" };
|
||||||
SettingValue<MyGUI::Colour> mColorTopicExhausted{ mIndex, "GUI", "color topic exhausted" };
|
SettingValue<MyGUI::Colour> mColorTopicExhausted{ mIndex, "GUI", "color topic exhausted" };
|
||||||
|
SettingValue<MyGUI::Colour> mColorTopicExhaustedOver{ mIndex, "GUI", "color topic exhausted over" };
|
||||||
|
SettingValue<MyGUI::Colour> mColorTopicExhaustedPressed{ mIndex, "GUI", "color topic exhausted pressed" };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef COMPONENTS_TERRAIN_DEFS_HPP
|
#ifndef COMPONENTS_TERRAIN_DEFS_HPP
|
||||||
#define COMPONENTS_TERRAIN_DEFS_HPP
|
#define COMPONENTS_TERRAIN_DEFS_HPP
|
||||||
|
|
||||||
#include <string>
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
namespace Terrain
|
namespace Terrain
|
||||||
{
|
{
|
||||||
|
@ -16,8 +16,8 @@ namespace Terrain
|
||||||
|
|
||||||
struct LayerInfo
|
struct LayerInfo
|
||||||
{
|
{
|
||||||
std::string mDiffuseMap;
|
VFS::Path::Normalized mDiffuseMap;
|
||||||
std::string mNormalMap;
|
VFS::Path::Normalized mNormalMap;
|
||||||
bool mParallax; // Height info in normal map alpha channel?
|
bool mParallax; // Height info in normal map alpha channel?
|
||||||
bool mSpecular; // Specular info in diffuse map alpha channel?
|
bool mSpecular; // Specular info in diffuse map alpha channel?
|
||||||
|
|
||||||
|
|
|
@ -404,6 +404,25 @@ namespace Terrain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::StateSet> makeStateSet()
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::StateSet> stateSet = new osg::StateSet;
|
||||||
|
stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
||||||
|
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
||||||
|
stateSet->setAttributeAndModes(
|
||||||
|
new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE),
|
||||||
|
osg::StateAttribute::ON);
|
||||||
|
osg::ref_ptr<osg::Material> material = new osg::Material;
|
||||||
|
material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 1, 1));
|
||||||
|
material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
|
||||||
|
material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
|
||||||
|
stateSet->setAttributeAndModes(material, osg::StateAttribute::ON);
|
||||||
|
stateSet->setRenderBinDetails(100, "RenderBin");
|
||||||
|
return stateSet;
|
||||||
|
}
|
||||||
|
|
||||||
void updateWaterCullingView(
|
void updateWaterCullingView(
|
||||||
HeightCullCallback* callback, ViewData* vd, osgUtil::CullVisitor* cv, float cellworldsize, bool outofworld)
|
HeightCullCallback* callback, ViewData* vd, osgUtil::CullVisitor* cv, float cellworldsize, bool outofworld)
|
||||||
{
|
{
|
||||||
|
@ -421,8 +440,8 @@ namespace Terrain
|
||||||
for (unsigned int i = 0; i < vd->getNumEntries(); ++i)
|
for (unsigned int i = 0; i < vd->getNumEntries(); ++i)
|
||||||
{
|
{
|
||||||
ViewDataEntry& entry = vd->getEntry(i);
|
ViewDataEntry& entry = vd->getEntry(i);
|
||||||
osg::BoundingBox bb
|
osg::BoundingBox bb = static_cast<TerrainDrawable*>(entry.mRenderingNode->asGroup()->getChild(0))
|
||||||
= static_cast<TerrainDrawable*>(entry.mRenderingNode->asGroup()->getChild(0))->getWaterBoundingBox();
|
->getWaterBoundingBox();
|
||||||
if (!bb.valid())
|
if (!bb.valid())
|
||||||
continue;
|
continue;
|
||||||
osg::Vec3f ofs(
|
osg::Vec3f ofs(
|
||||||
|
@ -440,28 +459,14 @@ namespace Terrain
|
||||||
osg::Box* b = new osg::Box;
|
osg::Box* b = new osg::Box;
|
||||||
b->set(bb.center(), bb._max - bb.center());
|
b->set(bb.center(), bb._max - bb.center());
|
||||||
osg::ShapeDrawable* drw = new osg::ShapeDrawable(b);
|
osg::ShapeDrawable* drw = new osg::ShapeDrawable(b);
|
||||||
static osg::ref_ptr<osg::StateSet> stateset = nullptr;
|
static const osg::ref_ptr<osg::StateSet> stateset = makeStateSet();
|
||||||
if (!stateset)
|
|
||||||
{
|
|
||||||
stateset = new osg::StateSet;
|
|
||||||
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
|
||||||
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
|
||||||
stateset->setAttributeAndModes(
|
|
||||||
new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE),
|
|
||||||
osg::StateAttribute::ON);
|
|
||||||
osg::Material* m = new osg::Material;
|
|
||||||
m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 1, 1));
|
|
||||||
m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
|
|
||||||
m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
|
|
||||||
stateset->setAttributeAndModes(m, osg::StateAttribute::ON);
|
|
||||||
stateset->setRenderBinDetails(100, "RenderBin");
|
|
||||||
}
|
|
||||||
drw->setStateSet(stateset);
|
drw->setStateSet(stateset);
|
||||||
drw->accept(*cv);
|
drw->accept(*cv);
|
||||||
}
|
}
|
||||||
callback->setLowZ(lowZ);
|
callback->setLowZ(lowZ);
|
||||||
cv->popCurrentMask();
|
cv->popCurrentMask();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QuadTreeWorld::accept(osg::NodeVisitor& nv)
|
void QuadTreeWorld::accept(osg::NodeVisitor& nv)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,24 +35,22 @@ namespace Terrain
|
||||||
mCache->call(f);
|
mCache->call(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> TextureManager::getTexture(const std::string& name)
|
osg::ref_ptr<osg::Texture2D> TextureManager::getTexture(VFS::Path::NormalizedView name)
|
||||||
{
|
{
|
||||||
// don't bother with case folding, since there is only one way of referring to terrain textures we can assume
|
// don't bother with case folding, since there is only one way of referring to terrain textures we can assume
|
||||||
// the case is always the same
|
// the case is always the same
|
||||||
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(name);
|
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(name);
|
||||||
if (obj)
|
|
||||||
|
if (obj != nullptr)
|
||||||
return static_cast<osg::Texture2D*>(obj.get());
|
return static_cast<osg::Texture2D*>(obj.get());
|
||||||
else
|
|
||||||
{
|
osg::ref_ptr<osg::Texture2D> texture(new osg::Texture2D(mSceneManager->getImageManager()->getImage(name)));
|
||||||
osg::ref_ptr<osg::Texture2D> texture(
|
|
||||||
new osg::Texture2D(mSceneManager->getImageManager()->getImage(VFS::Path::toNormalized(name))));
|
|
||||||
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||||
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||||
mSceneManager->applyFilterSettings(texture);
|
mSceneManager->applyFilterSettings(texture);
|
||||||
mCache->addEntryToObjectCache(name, texture.get());
|
mCache->addEntryToObjectCache(name.value(), texture.get());
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void TextureManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
|
void TextureManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
#ifndef OPENMW_COMPONENTS_TERRAIN_TEXTUREMANAGER_H
|
#ifndef OPENMW_COMPONENTS_TERRAIN_TEXTUREMANAGER_H
|
||||||
#define OPENMW_COMPONENTS_TERRAIN_TEXTUREMANAGER_H
|
#define OPENMW_COMPONENTS_TERRAIN_TEXTUREMANAGER_H
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <components/resource/resourcemanager.hpp>
|
#include <components/resource/resourcemanager.hpp>
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
namespace Resource
|
namespace Resource
|
||||||
{
|
{
|
||||||
|
@ -25,7 +24,7 @@ namespace Terrain
|
||||||
|
|
||||||
void updateTextureFiltering();
|
void updateTextureFiltering();
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> getTexture(const std::string& name);
|
osg::ref_ptr<osg::Texture2D> getTexture(VFS::Path::NormalizedView name);
|
||||||
|
|
||||||
void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
|
void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
|
||||||
|
|
||||||
|
|
|
@ -157,8 +157,8 @@ color topic specific
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
:Type: RGBA floating point
|
:Type: RGBA floating point
|
||||||
:Range: 0.0 to 1.0
|
:Range: 0.0 to 1.0 for each channel
|
||||||
:Default: empty
|
:Default: 0.45 0.5 0.8 1 (blue)
|
||||||
|
|
||||||
This setting overrides the colour of dialogue topics that have a response unique to the actors speaking.
|
This setting overrides the colour of dialogue topics that have a response unique to the actors speaking.
|
||||||
The value is composed of four floating point values representing the red, green, blue and alpha channels.
|
The value is composed of four floating point values representing the red, green, blue and alpha channels.
|
||||||
|
@ -166,15 +166,67 @@ The alpha value is currently ignored.
|
||||||
|
|
||||||
A topic response is considered unique if its Actor filter field contains the speaking actor's object ID and hasn't yet been read.
|
A topic response is considered unique if its Actor filter field contains the speaking actor's object ID and hasn't yet been read.
|
||||||
|
|
||||||
|
color topic specific over
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
:Type: RGBA floating point
|
||||||
|
:Range: 0.0 to 1.0 for each channel
|
||||||
|
:Default: 0.6 0.6 0.85 1 (blue)
|
||||||
|
|
||||||
|
This setting provides an "over" colour to dialogue topics that meet the color topic specific criteria.
|
||||||
|
The value is composed of four floating point values representing the red, green, blue and alpha channels.
|
||||||
|
The alpha value is currently ignored.
|
||||||
|
|
||||||
|
A dialogue topic is considered "over" if it is the active GUI element through keyboard or mouse events.
|
||||||
|
|
||||||
|
color topic specific pressed
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
:Type: RGBA floating point
|
||||||
|
:Range: 0.0 to 1.0 for each channel
|
||||||
|
:Default: 0.3 0.35 0.75 1 (blue)
|
||||||
|
|
||||||
|
This setting provides an "pressed" colour to dialogue topics that meet the color topic specific criteria.
|
||||||
|
The value is composed of four floating point values representing the red, green, blue and alpha channels.
|
||||||
|
The alpha value is currently ignored.
|
||||||
|
|
||||||
|
A dialogue topic is considered "pressed" if it is the active GUI element and it receives a sustained keyboard or mouse event.
|
||||||
|
|
||||||
color topic exhausted
|
color topic exhausted
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
:Type: RGBA floating point
|
:Type: RGBA floating point
|
||||||
:Range: 0.0 to 1.0
|
:Range: 0.0 to 1.0 for each channel
|
||||||
:Default: empty
|
:Default: 0.3 0.3 0.3 1 (grey)
|
||||||
|
|
||||||
This setting overrides the colour of dialogue topics which have been "exhausted" by the player.
|
This setting overrides the colour of dialogue topics which have been "exhausted" by the player.
|
||||||
The value is composed of four floating point values representing the red, green, blue and alpha channels.
|
The value is composed of four floating point values representing the red, green, blue and alpha channels.
|
||||||
The alpha value is currently ignored.
|
The alpha value is currently ignored.
|
||||||
|
|
||||||
A topic is considered "exhausted" if the response the player is about to see has already been seen.
|
A topic is considered "exhausted" if the response the player is about to see has already been seen.
|
||||||
|
|
||||||
|
color topic exhausted over
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
:Type: RGBA floating point
|
||||||
|
:Range: 0.0 to 1.0 for each channel
|
||||||
|
:Default: 0.55 0.55 0.55 1 (grey)
|
||||||
|
|
||||||
|
This setting provides an "over" colour to dialogue topics that meet the color topic exhausted criteria.
|
||||||
|
The value is composed of four floating point values representing the red, green, blue and alpha channels.
|
||||||
|
The alpha value is currently ignored.
|
||||||
|
|
||||||
|
A dialogue topic is considered "over" if it is the active GUI element through keyboard or mouse events.
|
||||||
|
|
||||||
|
color topic exhausted pressed
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
:Type: RGBA floating point
|
||||||
|
:Range: 0.0 to 1.0 for each channel
|
||||||
|
:Default: 0.45 0.45 0.45 1 (grey)
|
||||||
|
|
||||||
|
This setting provides a "pressed" colour to dialogue topics that meet the color topic exhausted criteria.
|
||||||
|
The value is composed of four floating point values representing the red, green, blue and alpha channels.
|
||||||
|
The alpha value is currently ignored.
|
||||||
|
|
||||||
|
A dialogue topic is considered "pressed" if it is the active GUI element and it receives a sustained keyboard or mouse event.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Camera: "Caméra d’OpenMW"
|
Camera: "OpenMW : Caméra"
|
||||||
settingsPageDescription: "Configuration de la caméra d’OpenMW"
|
settingsPageDescription: "Paramètres de la caméra d’OpenMW"
|
||||||
|
|
||||||
thirdPersonSettings: "Vue à la troisième personne"
|
thirdPersonSettings: "Vue à la troisième personne"
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
ControlsPage: "OpenMW : Contrôles"
|
ControlsPage: "OpenMW : Contrôles"
|
||||||
ControlsPageDescription: "Paramètres additionnels de contrôle"
|
ControlsPageDescription: "Paramètres additionnels des contrôles d'OpenMW"
|
||||||
|
|
||||||
MovementSettings: "Mouvements"
|
MovementSettings: "Mouvements"
|
||||||
|
|
||||||
alwaysRun: "Course permanente"
|
alwaysRun: "Course permanente"
|
||||||
alwaysRunDescription: |
|
alwaysRunDescription: |
|
||||||
Actif : Le personnage se déplace par défaut en courant ; \n\n
|
Actif : Le personnage se déplace par défaut en courant ;
|
||||||
Inactif : Le personnage se déplace par défaut en marchant.\n\n
|
Inactif : Le personnage se déplace par défaut en marchant.
|
||||||
La touche Maj. inverse temporairement ce paramètre.\n\n
|
La touche Maj. inverse temporairement ce paramètre.
|
||||||
La touche Verr Maj inverse ce paramètre lorsqu'elle est verrouillée.
|
La touche Verr Maj inverse ce paramètre lorsqu'elle est verrouillée.
|
||||||
|
|
||||||
toggleSneak: "Mode discrétion maintenu"
|
toggleSneak: "Mode discrétion maintenu"
|
||||||
toggleSneakDescription: |
|
toggleSneakDescription: |
|
||||||
Une simple pression de la touche associée (Ctrl par défaut) active le mode discrétion, une seconde pression désactive le mode discrétion.\n\n
|
Une simple pression de la touche associée (Ctrl par défaut) active le mode discrétion, une seconde pression désactive le mode discrétion.
|
||||||
Il n'est plus nécessaire de maintenir une touche appuyée pour que le mode discrétion soit actif.\n\n
|
Il n'est plus nécessaire de maintenir une touche appuyée pour que le mode discrétion soit actif.
|
||||||
Certains joueurs ayant une utilisation intensive du mode discrétion considèrent qu'il est plus aisé de contrôler leur personnage ainsi.
|
Certains joueurs ayant une utilisation intensive du mode discrétion considèrent qu'il est plus aisé de contrôler leur personnage ainsi.
|
||||||
|
|
||||||
smoothControllerMovement: "Mouvements à la manette adoucis"
|
smoothControllerMovement: "Mouvements à la manette adoucis"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#Music: "OpenMW Music"
|
Music: "OpenMW : Musique"
|
||||||
#settingsPageDescription: "OpenMW Music settings"
|
settingsPageDescription: "Paramètre de la musique d'OpenMW"
|
||||||
|
|
||||||
#musicSettings: "Music configuration"
|
musicSettings: "Configuration de la musique"
|
||||||
|
|
||||||
#CombatMusicEnabled: "Play combat music"
|
CombatMusicEnabled: "Jouer la musique de combat"
|
||||||
#CombatMusicEnabledDescription: "Whether to switch to combat music when there are actors in combat."
|
CombatMusicEnabledDescription: "Si activé, le jeu bascule vers la musique de combat dès qu'un personnage est en combat."
|
||||||
|
|
|
@ -124,6 +124,28 @@
|
||||||
</BasisSkin>
|
</BasisSkin>
|
||||||
</Resource>
|
</Resource>
|
||||||
|
|
||||||
|
<Resource type="ResourceSkin" name="MW_ListLine_Specific" size="5 5">
|
||||||
|
<Property key="FontName" value="Default"/>
|
||||||
|
<Property key="TextAlign" value="Left VCenter"/>
|
||||||
|
|
||||||
|
<BasisSkin type="SimpleText" offset="2 0 1 5" align="Stretch">
|
||||||
|
<State name="normal" colour="#{setting=GUI,color topic specific}"/>
|
||||||
|
<State name="highlighted" colour="#{setting=GUI,color topic specific over}"/>
|
||||||
|
<State name="pushed" colour="#{setting=GUI,color topic specific pressed}"/>
|
||||||
|
</BasisSkin>
|
||||||
|
</Resource>
|
||||||
|
|
||||||
|
<Resource type="ResourceSkin" name="MW_ListLine_Exhausted" size="5 5">
|
||||||
|
<Property key="FontName" value="Default"/>
|
||||||
|
<Property key="TextAlign" value="Left VCenter"/>
|
||||||
|
|
||||||
|
<BasisSkin type="SimpleText" offset="2 0 1 5" align="Stretch">
|
||||||
|
<State name="normal" colour="#{setting=GUI,color topic exhausted}"/>
|
||||||
|
<State name="highlighted" colour="#{setting=GUI,color topic exhausted over}"/>
|
||||||
|
<State name="pushed" colour="#{setting=GUI,color topic exhausted pressed}"/>
|
||||||
|
</BasisSkin>
|
||||||
|
</Resource>
|
||||||
|
|
||||||
<Resource type="ResourceLayout" name="MW_List" size="516 516" align="Left Top">
|
<Resource type="ResourceLayout" name="MW_List" size="516 516" align="Left Top">
|
||||||
<Widget type="Widget" position="0 0 516 516" name="Root">
|
<Widget type="Widget" position="0 0 516 516" name="Root">
|
||||||
<Property key="NeedKey" value="true"/>
|
<Property key="NeedKey" value="true"/>
|
||||||
|
|
|
@ -24,7 +24,7 @@ local function onUpdate()
|
||||||
-- Early-out for actors without targets and without combat state
|
-- Early-out for actors without targets and without combat state
|
||||||
-- TODO: use events or engine handlers to detect when targets change
|
-- TODO: use events or engine handlers to detect when targets change
|
||||||
local isStanceNothing = types.Actor.getStance(self) == types.Actor.STANCE.Nothing
|
local isStanceNothing = types.Actor.getStance(self) == types.Actor.STANCE.Nothing
|
||||||
if isStanceNothing and next(targets) == nil then
|
if isStanceNothing and next(targets) == nil and not AI.isFleeing() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
-- `openmw.ambient` controls background sounds, specific to given player (2D-sounds).
|
-- `openmw.ambient` controls background 2D sounds specific to a given player.
|
||||||
-- Can be used only by menu scripts and local scripts, that are attached to a player.
|
-- Can be used only by menu scripts and local scripts that are attached to a player.
|
||||||
-- @module ambient
|
-- @module ambient
|
||||||
-- @usage local ambient = require('openmw.ambient')
|
-- @usage local ambient = require('openmw.ambient')
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@
|
||||||
-- @param #string soundId ID of Sound record to play
|
-- @param #string soundId ID of Sound record to play
|
||||||
-- @param #table options An optional table with additional optional arguments. Can contain:
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
--
|
--
|
||||||
-- * `timeOffset` - a floating point number >= 0, to skip some time (in seconds) from beginning of sound file (default: 0);
|
-- * `timeOffset` - a floating point number >= 0, to skip some time (in seconds) from the beginning of the sound (default: 0);
|
||||||
-- * `volume` - a floating point number >= 0, to set a sound volume (default: 1);
|
-- * `volume` - a floating point number >= 0, to set the sound's volume (default: 1);
|
||||||
-- * `pitch` - a floating point number >= 0, to set a sound pitch (default: 1);
|
-- * `pitch` - a floating point number >= 0, to set the sound's pitch (default: 1);
|
||||||
-- * `scale` - a boolean, to set if sound pitch should be scaled by simulation time scaling (default: true);
|
-- * `scale` - a boolean, to set if the sound's pitch should be scaled by simulation time scaling (default: true);
|
||||||
-- * `loop` - a boolean, to set if sound should be repeated when it ends (default: false);
|
-- * `loop` - a boolean, to set if the sound should be repeated when it ends (default: false);
|
||||||
-- @usage local params = {
|
-- @usage local params = {
|
||||||
-- timeOffset=0.1
|
-- timeOffset=0.1
|
||||||
-- volume=0.3,
|
-- volume=0.3,
|
||||||
|
@ -29,14 +29,14 @@
|
||||||
---
|
---
|
||||||
-- Play a 2D sound file
|
-- Play a 2D sound file
|
||||||
-- @function [parent=#ambient] playSoundFile
|
-- @function [parent=#ambient] playSoundFile
|
||||||
-- @param #string fileName Path to sound file in VFS
|
-- @param #string fileName Path to a sound file in VFS
|
||||||
-- @param #table options An optional table with additional optional arguments. Can contain:
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
--
|
--
|
||||||
-- * `timeOffset` - a floating point number >= 0, to skip some time (in seconds) from beginning of sound file (default: 0);
|
-- * `timeOffset` - a floating point number >= 0, to skip some time (in seconds) from the beginning of the sound file (default: 0);
|
||||||
-- * `volume` - a floating point number >= 0, to set a sound volume (default: 1);
|
-- * `volume` - a floating point number >= 0, to set the sound's volume (default: 1);
|
||||||
-- * `pitch` - a floating point number >= 0, to set a sound pitch (default: 1);
|
-- * `pitch` - a floating point number >= 0, to set the sound's pitch (default: 1);
|
||||||
-- * `scale` - a boolean, to set if sound pitch should be scaled by simulation time scaling (default: true);
|
-- * `scale` - a boolean, to set if the sound's pitch should be scaled by simulation time scaling (default: true);
|
||||||
-- * `loop` - a boolean, to set if sound should be repeated when it ends (default: false);
|
-- * `loop` - a boolean, to set if the sound should be repeated when it ends (default: false);
|
||||||
-- @usage local params = {
|
-- @usage local params = {
|
||||||
-- timeOffset=0.1
|
-- timeOffset=0.1
|
||||||
-- volume=0.3,
|
-- volume=0.3,
|
||||||
|
@ -55,37 +55,37 @@
|
||||||
---
|
---
|
||||||
-- Stop a sound file
|
-- Stop a sound file
|
||||||
-- @function [parent=#ambient] stopSoundFile
|
-- @function [parent=#ambient] stopSoundFile
|
||||||
-- @param #string fileName Path to sound file in VFS
|
-- @param #string fileName Path to a sound file in VFS
|
||||||
-- @usage ambient.stopSoundFile("Sound\\test.mp3");
|
-- @usage ambient.stopSoundFile("Sound\\test.mp3");
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Check if sound is playing
|
-- Check if a sound is playing
|
||||||
-- @function [parent=#ambient] isSoundPlaying
|
-- @function [parent=#ambient] isSoundPlaying
|
||||||
-- @param #string soundId ID of Sound record to check
|
-- @param #string soundId ID of Sound record to check
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
-- @usage local isPlaying = ambient.isSoundPlaying("shock bolt");
|
-- @usage local isPlaying = ambient.isSoundPlaying("shock bolt");
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Check if sound file is playing
|
-- Check if a sound file is playing
|
||||||
-- @function [parent=#ambient] isSoundFilePlaying
|
-- @function [parent=#ambient] isSoundFilePlaying
|
||||||
-- @param #string fileName Path to sound file in VFS
|
-- @param #string fileName Path to a sound file in VFS
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
-- @usage local isPlaying = ambient.isSoundFilePlaying("Sound\\test.mp3");
|
-- @usage local isPlaying = ambient.isSoundFilePlaying("Sound\\test.mp3");
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Play a sound file as a music track
|
-- Play a sound file as a music track
|
||||||
-- @function [parent=#ambient] streamMusic
|
-- @function [parent=#ambient] streamMusic
|
||||||
-- @param #string fileName Path to file in VFS
|
-- @param #string fileName Path to a file in VFS
|
||||||
-- @param #table options An optional table with additional optional arguments. Can contain:
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
--
|
--
|
||||||
-- * `fadeOut` - a floating point number >= 0, time (in seconds) to fade out current track before playing this one (default 1.0);
|
-- * `fadeOut` - a floating point number >= 0, time (in seconds) to fade out the current track before playing this one (default 1.0);
|
||||||
-- @usage local params = {
|
-- @usage local params = {
|
||||||
-- fadeOut=2.0
|
-- fadeOut=2.0
|
||||||
-- };
|
-- };
|
||||||
-- ambient.streamMusic("Music\\Test\\Test.mp3", params)
|
-- ambient.streamMusic("Music\\Test\\Test.mp3", params)
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Stop to play current music
|
-- Stop the currently playing music
|
||||||
-- @function [parent=#ambient] stopMusic
|
-- @function [parent=#ambient] stopMusic
|
||||||
-- @usage ambient.stopMusic();
|
-- @usage ambient.stopMusic();
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
---
|
---
|
||||||
-- Play an ambient voiceover.
|
-- Play an ambient voiceover.
|
||||||
-- @function [parent=#ambient] say
|
-- @function [parent=#ambient] say
|
||||||
-- @param #string fileName Path to sound file in VFS
|
-- @param #string fileName Path to a sound file in VFS
|
||||||
-- @param #string text Subtitle text (optional)
|
-- @param #string text Subtitle text (optional)
|
||||||
-- @usage -- play voiceover and print messagebox
|
-- @usage -- play voiceover and print messagebox
|
||||||
-- ambient.say("Sound\\Vo\\Misc\\voice.mp3", "Subtitle text")
|
-- ambient.say("Sound\\Vo\\Misc\\voice.mp3", "Subtitle text")
|
||||||
|
@ -108,7 +108,7 @@
|
||||||
---
|
---
|
||||||
-- Stop an ambient voiceover
|
-- Stop an ambient voiceover
|
||||||
-- @function [parent=#ambient] stopSay
|
-- @function [parent=#ambient] stopSay
|
||||||
-- @param #string fileName Path to sound file in VFS
|
-- @param #string fileName Path to a sound file in VFS
|
||||||
-- @usage ambient.stopSay();
|
-- @usage ambient.stopSay();
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
-- `openmw.animation` defines functions that allow control of character animations
|
-- `openmw.animation` defines functions that allow control of character animations.
|
||||||
-- Note that for some methods, such as @{openmw.animation#playBlended} you should use the associated methods on the
|
-- Note that for some methods, such as @{openmw.animation#playBlended} you should use the associated methods on the
|
||||||
-- [AnimationController](interface_animation.html) interface rather than invoking this API directly.
|
-- [AnimationController](interface_animation.html) interface rather than invoking this API directly.
|
||||||
-- @module animation
|
-- @module animation
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Skips animations for one frame, equivalent to mwscript's SkipAnim
|
-- Skips animations for one frame, equivalent to mwscript's SkipAnim.
|
||||||
-- Can be used only in local scripts on self.
|
-- Can be used only in local scripts on self.
|
||||||
-- @function [parent=#animation] skipAnimationThisFrame
|
-- @function [parent=#animation] skipAnimationThisFrame
|
||||||
-- @param openmw.core#GameObject actor
|
-- @param openmw.core#GameObject actor
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Cancels and removes the animation group from the list of active animations
|
-- Cancels and removes the animation group from the list of active animations.
|
||||||
-- Can be used only in local scripts on self.
|
-- Can be used only in local scripts on self.
|
||||||
-- @function [parent=#animation] cancel
|
-- @function [parent=#animation] cancel
|
||||||
-- @param openmw.core#GameObject actor
|
-- @param openmw.core#GameObject actor
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
-- `openmw.async` contains timers and coroutine utils. All functions require
|
-- `openmw.async` contains timers and coroutine utilities. All functions require
|
||||||
-- the package itself as a first argument.
|
-- the package itself as a first argument.
|
||||||
-- @module async
|
-- @module async
|
||||||
-- @usage local async = require('openmw.async')
|
-- @usage local async = require('openmw.async')
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Calls callback(arg) in `delay` simulation seconds.
|
-- Calls callback(arg) in `delay` simulation seconds.
|
||||||
-- Callback must be registered in advance.
|
-- The callback must be registered in advance.
|
||||||
-- @function [parent=#async] newSimulationTimer
|
-- @function [parent=#async] newSimulationTimer
|
||||||
-- @param self
|
-- @param self
|
||||||
-- @param #number delay
|
-- @param #number delay
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Calls callback(arg) in `delay` game seconds.
|
-- Calls callback(arg) in `delay` game seconds.
|
||||||
-- Callback must be registered in advance.
|
-- The callback must be registered in advance.
|
||||||
-- @function [parent=#async] newGameTimer
|
-- @function [parent=#async] newGameTimer
|
||||||
-- @param self
|
-- @param self
|
||||||
-- @param #number delay
|
-- @param #number delay
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
-- @param #function func
|
-- @param #function func
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Wraps Lua function with `Callback` object that can be used in async API calls.
|
-- Wraps a Lua function with a `Callback` object that can be used in async API calls.
|
||||||
-- @function [parent=#async] callback
|
-- @function [parent=#async] callback
|
||||||
-- @param self
|
-- @param self
|
||||||
-- @param #function func
|
-- @param #function func
|
||||||
|
|
|
@ -218,14 +218,14 @@ keyboard navigation = true
|
||||||
color topic enable = false
|
color topic enable = false
|
||||||
|
|
||||||
# The color of dialogue topic keywords that gives unique actor responses
|
# The color of dialogue topic keywords that gives unique actor responses
|
||||||
# Format R G B A or empty for no special formatting
|
|
||||||
# Default to blue
|
|
||||||
color topic specific = 0.45 0.5 0.8 1
|
color topic specific = 0.45 0.5 0.8 1
|
||||||
|
color topic specific over = 0.6 0.6 0.85 1
|
||||||
|
color topic specific pressed = 0.3 0.35 0.75 1
|
||||||
|
|
||||||
# The color of dialogue topic keywords that gives already read responses
|
# The color of dialogue topic keywords that gives already read responses
|
||||||
# Format R G B A or empty for no special formatting
|
|
||||||
# Default to grey
|
|
||||||
color topic exhausted = 0.3 0.3 0.3 1
|
color topic exhausted = 0.3 0.3 0.3 1
|
||||||
|
color topic exhausted over = 0.55 0.55 0.55 1
|
||||||
|
color topic exhausted pressed = 0.45 0.45 0.45 1
|
||||||
|
|
||||||
[HUD]
|
[HUD]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue