diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fedc707bd7..cdda455dac 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -108,8 +108,8 @@ Coverity: - ccache/ variables: CCACHE_SIZE: 2G - CC: clang-12 - CXX: clang++-12 + CC: clang + CXX: clang++ CMAKE_BUILD_TYPE: Debug CMAKE_CXX_FLAGS_DEBUG: -O0 before_script: @@ -125,18 +125,34 @@ Coverity: - ccache -z -M "${CCACHE_SIZE}" - CI/before_script.linux.sh - cov-analysis-linux64-*/bin/cov-configure --template --comptype prefix --compiler ccache - # Remove the specific targets and build everything once we can do it under 3h - cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc) - ccache -svv - after_script: - tar cfz cov-int.tar.gz cov-int - - curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME - --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL - --form file=@cov-int.tar.gz --form version="$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA" - --form description="CI_COMMIT_SHORT_SHA / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID" + - echo "OPENMW_JOB_ID=$CI_JOB_ID" >> build.env artifacts: + expire_in: 1 day paths: - /builds/OpenMW/openmw/cov-int/build-log.txt + - /builds/OpenMW/openmw/cov-int.tar.gz + reports: + dotenv: build.env + +Coverity_Upload: + image: ubuntu:24.04 + stage: build + rules: + - if: $CI_PIPELINE_SOURCE == "schedule" + before_script: + - CI/install_debian_deps.sh coverity_upload + script: + - echo "$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/$OPENMW_JOB_ID/artifacts/cov-int.tar.gz" + - curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME + --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL + --form version="$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA" + --form description="CI_COMMIT_SHORT_SHA / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID" + --form url="$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/$OPENMW_JOB_ID/artifacts/cov-int.tar.gz" + needs: + - Coverity Ubuntu_GCC: extends: .Ubuntu diff --git a/CI/install_debian_deps.sh b/CI/install_debian_deps.sh index 4f0e7cdb69..aada722fb1 100755 --- a/CI/install_debian_deps.sh +++ b/CI/install_debian_deps.sh @@ -11,7 +11,8 @@ print_help() { declare -rA GROUPED_DEPS=( [gcc]="binutils gcc build-essential cmake ccache curl unzip git pkg-config mold" [clang]="binutils clang make cmake ccache curl unzip git pkg-config mold" - [coverity]="binutils clang-12 make cmake ccache curl unzip git pkg-config" + [coverity]="binutils clang make cmake ccache curl unzip git pkg-config file" + [coverity_upload]="curl" [gcc_preprocess]=" binutils build-essential diff --git a/CMakeLists.txt b/CMakeLists.txt index b4ef872eb6..57ebeefcfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 50) set(OPENMW_VERSION_RELEASE 0) -set(OPENMW_LUA_API_REVISION 86) +set(OPENMW_LUA_API_REVISION 87) set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_VERSION_COMMITHASH "") diff --git a/apps/launcher/settingspage.cpp b/apps/launcher/settingspage.cpp index 27fa2ca27c..849f13076d 100644 --- a/apps/launcher/settingspage.cpp +++ b/apps/launcher/settingspage.cpp @@ -291,6 +291,7 @@ bool Launcher::SettingsPage::loadSettings() } } loadSettingBool(Settings::sound().mCameraListener, *cameraListenerCheckBox); + dopplerSpinBox->setValue(Settings::sound().mDopplerFactor); } // Interface Changes @@ -482,6 +483,8 @@ void Launcher::SettingsPage::saveSettings() const bool cCameraListener = cameraListenerCheckBox->checkState() != Qt::Unchecked; Settings::sound().mCameraListener.set(cCameraListener); + + Settings::sound().mDopplerFactor.set(dopplerSpinBox->value()); } // Interface Changes diff --git a/apps/launcher/ui/settingspage.ui b/apps/launcher/ui/settingspage.ui index b96d734605..d7e1a4b3ab 100644 --- a/apps/launcher/ui/settingspage.ui +++ b/apps/launcher/ui/settingspage.ui @@ -1224,6 +1224,51 @@ + + + + + + <html><head/><body><p>Controls the strength of the Doppler effect. Zero means it is completely disabled.</p><p>The Doppler effect increases or decreases the pitch of sounds relative to the velocity of the sound source and the listener.</p></body></html> + + + Doppler Factor + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 2 + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.010000000000000 + + + 0.250000000000000 + + + + + diff --git a/apps/opencs/model/world/idcollection.cpp b/apps/opencs/model/world/idcollection.cpp index ca56eceb69..7890dc47b8 100644 --- a/apps/opencs/model/world/idcollection.cpp +++ b/apps/opencs/model/world/idcollection.cpp @@ -59,14 +59,13 @@ namespace CSMWorld const Record* IdCollection::searchRecord(std::uint16_t index, int plugin) const { - auto found = mIndices.find({ plugin, index }); - if (found != mIndices.end()) - { - int index = searchId(found->second); - if (index != -1) - return &getRecord(index); - } - return nullptr; + const auto it = mIndices.find({ plugin, index }); + if (it == mIndices.end()) + return nullptr; + const int recordIndex = searchId(it->second); + if (recordIndex == -1) + return nullptr; + return &getRecord(recordIndex); } const std::string* IdCollection::getLandTexture(std::uint16_t index, int plugin) const diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 0ea8451774..2a66878a04 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -5,7 +5,8 @@ #include #include -#include +#include +#include #include #include @@ -28,7 +29,6 @@ #include -#include #include #include diff --git a/apps/openmw/mwbase/luamanager.hpp b/apps/openmw/mwbase/luamanager.hpp index 61574de3ac..5772c555a3 100644 --- a/apps/openmw/mwbase/luamanager.hpp +++ b/apps/openmw/mwbase/luamanager.hpp @@ -75,6 +75,7 @@ namespace MWBase const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult, std::string_view start, std::string_view stop, float startpoint, uint32_t loops, bool loopfallback) = 0; + virtual void jailTimeServed(const MWWorld::Ptr& actor, int days) = 0; virtual void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) = 0; virtual void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) = 0; virtual void onHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& weapon, diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 532bc771ba..5591b7205c 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -234,6 +234,8 @@ namespace MWBase const osg::Vec3f& pos, const osg::Vec3f& dir, const osg::Vec3f& up, bool underwater) = 0; + virtual void setListenerVel(const osg::Vec3f& vel) = 0; + virtual void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) = 0; void setSimulationTimeScale(float scale) { mSimulationTimeScale = scale; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 157c12af23..74bd9f6a5c 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -496,6 +496,8 @@ namespace MWBase virtual float getSunVisibility() const = 0; virtual float getSunPercentage() const = 0; + virtual float getPhysicsFrameRateDt() const = 0; + virtual bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) = 0; /// Teleports \a ptr to the closest reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 2fbaa8d8ac..9244e9dd2f 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -4,6 +4,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/luamanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" @@ -86,46 +87,6 @@ namespace MWGui // We should not worsen corprus when in prison player.getClass().getCreatureStats(player).getActiveSpells().skipWorsenings(mDays * 24); - - const auto& skillStore = MWBase::Environment::get().getESMStore()->get(); - std::set skills; - for (int day = 0; day < mDays; ++day) - { - auto& prng = MWBase::Environment::get().getWorld()->getPrng(); - const ESM::Skill* skill = skillStore.searchRandom({}, prng); - skills.insert(skill); - - MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill->mId); - if (skill->mId == ESM::Skill::Security || skill->mId == ESM::Skill::Sneak) - value.setBase(std::min(100.f, value.getBase() + 1)); - else - value.setBase(std::max(0.f, value.getBase() - 1)); - } - - const MWWorld::Store& gmst - = MWBase::Environment::get().getESMStore()->get(); - - std::string message; - if (mDays == 1) - message = gmst.find("sNotifyMessage42")->mValue.getString(); - else - message = gmst.find("sNotifyMessage43")->mValue.getString(); - - message = Misc::StringUtils::format(message, mDays); - - for (const ESM::Skill* skill : skills) - { - int skillValue = player.getClass().getNpcStats(player).getSkill(skill->mId).getBase(); - std::string skillMsg = gmst.find("sNotifyMessage44")->mValue.getString(); - if (skill->mId == ESM::Skill::Sneak || skill->mId == ESM::Skill::Security) - skillMsg = gmst.find("sNotifyMessage39")->mValue.getString(); - - skillMsg = Misc::StringUtils::format(skillMsg, skill->mName, skillValue); - message += "\n" + skillMsg; - } - - std::vector buttons; - buttons.emplace_back("#{Interface:OK}"); - MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons); + MWBase::Environment::get().getLuaManager()->jailTimeServed(player, mDays); } } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 59d21886dc..51a765442a 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -774,12 +774,10 @@ namespace MWGui , mGlobalMapRender(std::make_unique(localMapRender->getRoot(), workQueue)) , mEditNoteDialog() { - static bool registered = false; - if (!registered) - { + [[maybe_unused]] static const bool registered = [] { MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - registered = true; - } + return true; + }(); mEditNoteDialog.setVisible(false); mEditNoteDialog.eventOkClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditOk); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index d8302df87c..6bd9ef3ac8 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -352,6 +352,7 @@ namespace MWGui getWidget(mAvailableEffectsList, "AvailableEffects"); getWidget(mUsedEffectsView, "UsedEffects"); getWidget(mPriceLabel, "PriceLabel"); + getWidget(mPlayerGold, "PlayerGold"); getWidget(mBuyButton, "BuyButton"); getWidget(mCancelButton, "CancelButton"); @@ -370,6 +371,10 @@ namespace MWGui mPtr = actor; mNameEdit->setCaption({}); + MWWorld::Ptr player = MWMechanics::getPlayer(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + mPlayerGold->setCaptionWithReplacing(MyGUI::utility::toString(playerGold)); + startEditing(); } diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 6dfe61fc57..0887dd8c94 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -175,6 +175,7 @@ namespace MWGui MyGUI::Button* mBuyButton; MyGUI::Button* mCancelButton; MyGUI::TextBox* mPriceLabel; + MyGUI::TextBox* mPlayerGold; ESM::Spell mSpell; }; diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 4bde77a552..3fc8412d4c 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -29,7 +29,7 @@ namespace MWGui : WindowBase("openmw_trainingwindow.layout") { getWidget(mTrainingOptions, "TrainingOptions"); - getWidget(mCancelButton, "CancelButton"); + getWidget(mCancelButton, "OkButton"); getWidget(mPlayerGold, "PlayerGold"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onCancelButtonClicked); @@ -115,14 +115,14 @@ namespace MWGui MyGUI::Button* button = mTrainingOptions->createWidget(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip - MyGUI::IntCoord(5, 5 + i * lineHeight, mTrainingOptions->getWidth() - 10, lineHeight), + MyGUI::IntCoord(4, 3 + i * lineHeight, mTrainingOptions->getWidth() - 10, lineHeight), MyGUI::Align::Default); button->setUserData(skills[i].first); button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected); button->setCaptionWithReplacing( - MyGUI::TextIterator::toTagsString(skill->mName) + " - " + MyGUI::utility::toString(price)); + MyGUI::TextIterator::toTagsString(skill->mName) + " - " + MyGUI::utility::toString(price) + "#{sgp}"); button->setSize(button->getTextSize().width + 12, button->getSize().height); diff --git a/apps/openmw/mwlua/animationbindings.cpp b/apps/openmw/mwlua/animationbindings.cpp index 1af7acea36..5ecd3bd1fa 100644 --- a/apps/openmw/mwlua/animationbindings.cpp +++ b/apps/openmw/mwlua/animationbindings.cpp @@ -1,13 +1,7 @@ #include "animationbindings.hpp" -#include -#include -#include #include -#include #include -#include -#include #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -15,11 +9,8 @@ #include "../mwmechanics/character.hpp" -#include "../mwworld/esmstore.hpp" - #include "context.hpp" #include "luamanagerimp.hpp" -#include "objectvariant.hpp" namespace MWLua { diff --git a/apps/openmw/mwlua/birthsignbindings.cpp b/apps/openmw/mwlua/birthsignbindings.cpp index 218d05b804..130144ab9b 100644 --- a/apps/openmw/mwlua/birthsignbindings.cpp +++ b/apps/openmw/mwlua/birthsignbindings.cpp @@ -8,7 +8,7 @@ #include "../mwbase/environment.hpp" #include "idcollectionbindings.hpp" -#include "types/types.hpp" +#include "recordstore.hpp" namespace sol { diff --git a/apps/openmw/mwlua/birthsignbindings.hpp b/apps/openmw/mwlua/birthsignbindings.hpp index bf41707d47..e57d56c971 100644 --- a/apps/openmw/mwlua/birthsignbindings.hpp +++ b/apps/openmw/mwlua/birthsignbindings.hpp @@ -3,10 +3,10 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + sol::table initBirthSignRecordBindings(const Context& context); } diff --git a/apps/openmw/mwlua/cellbindings.cpp b/apps/openmw/mwlua/cellbindings.cpp index ec64a3cddd..4e4542d89f 100644 --- a/apps/openmw/mwlua/cellbindings.cpp +++ b/apps/openmw/mwlua/cellbindings.cpp @@ -1,7 +1,5 @@ #include "cellbindings.hpp" -#include - #include #include #include @@ -27,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -38,7 +35,6 @@ #include #include #include -#include #include #include #include diff --git a/apps/openmw/mwlua/cellbindings.hpp b/apps/openmw/mwlua/cellbindings.hpp index 0d8e90e989..70255d8d7f 100644 --- a/apps/openmw/mwlua/cellbindings.hpp +++ b/apps/openmw/mwlua/cellbindings.hpp @@ -1,12 +1,12 @@ #ifndef MWLUA_CELLBINDINGS_H #define MWLUA_CELLBINDINGS_H -#include "context.hpp" - namespace MWLua { - void initCellBindingsForLocalScripts(const Context&); - void initCellBindingsForGlobalScripts(const Context&); + struct Context; + + void initCellBindingsForLocalScripts(const Context& context); + void initCellBindingsForGlobalScripts(const Context& context); } #endif // MWLUA_CELLBINDINGS_H diff --git a/apps/openmw/mwlua/classbindings.cpp b/apps/openmw/mwlua/classbindings.cpp index c94631fb3c..338698bfee 100644 --- a/apps/openmw/mwlua/classbindings.cpp +++ b/apps/openmw/mwlua/classbindings.cpp @@ -4,7 +4,7 @@ #include #include "idcollectionbindings.hpp" -#include "types/types.hpp" +#include "recordstore.hpp" namespace sol { diff --git a/apps/openmw/mwlua/classbindings.hpp b/apps/openmw/mwlua/classbindings.hpp index 1acb0a9ad3..cc67f2df02 100644 --- a/apps/openmw/mwlua/classbindings.hpp +++ b/apps/openmw/mwlua/classbindings.hpp @@ -3,10 +3,10 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + sol::table initClassRecordBindings(const Context& context); } diff --git a/apps/openmw/mwlua/corebindings.cpp b/apps/openmw/mwlua/corebindings.cpp index bdf71710af..282a9f970b 100644 --- a/apps/openmw/mwlua/corebindings.cpp +++ b/apps/openmw/mwlua/corebindings.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -20,6 +19,7 @@ #include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" +#include "context.hpp" #include "coremwscriptbindings.hpp" #include "dialoguebindings.hpp" #include "factionbindings.hpp" diff --git a/apps/openmw/mwlua/corebindings.hpp b/apps/openmw/mwlua/corebindings.hpp index ef385ca993..6d27dd0412 100644 --- a/apps/openmw/mwlua/corebindings.hpp +++ b/apps/openmw/mwlua/corebindings.hpp @@ -3,13 +3,13 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + void addCoreTimeBindings(sol::table& api, const Context& context); - sol::table initCorePackage(const Context&); + sol::table initCorePackage(const Context& context); } #endif // MWLUA_COREBINDINGS_H diff --git a/apps/openmw/mwlua/debugbindings.hpp b/apps/openmw/mwlua/debugbindings.hpp index c508b54496..d7902cb618 100644 --- a/apps/openmw/mwlua/debugbindings.hpp +++ b/apps/openmw/mwlua/debugbindings.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_MWLUA_DEBUGBINDINGS_H #define OPENMW_MWLUA_DEBUGBINDINGS_H -#include +#include namespace MWLua { diff --git a/apps/openmw/mwlua/engineevents.cpp b/apps/openmw/mwlua/engineevents.cpp index 6c652bccba..be7d249bfc 100644 --- a/apps/openmw/mwlua/engineevents.cpp +++ b/apps/openmw/mwlua/engineevents.cpp @@ -113,6 +113,15 @@ namespace MWLua scripts->onSkillLevelUp(event.mSkill, event.mSource); } + void operator()(const OnJailTimeServed& event) const + { + MWWorld::Ptr actor = getPtr(event.mActor); + if (actor.isEmpty()) + return; + if (auto* scripts = getLocalScripts(actor)) + scripts->onJailTimeServed(event.mDays); + } + private: MWWorld::Ptr getPtr(ESM::RefNum id) const { diff --git a/apps/openmw/mwlua/engineevents.hpp b/apps/openmw/mwlua/engineevents.hpp index fb9183eb7c..407739d60e 100644 --- a/apps/openmw/mwlua/engineevents.hpp +++ b/apps/openmw/mwlua/engineevents.hpp @@ -70,8 +70,13 @@ namespace MWLua std::string mSkill; std::string mSource; }; + struct OnJailTimeServed + { + ESM::RefNum mActor; + int mDays; + }; using Event = std::variant; + OnAnimationTextKey, OnSkillUse, OnSkillLevelUp, OnJailTimeServed>; void clear() { mQueue.clear(); } void addToQueue(Event e) { mQueue.push_back(std::move(e)); } diff --git a/apps/openmw/mwlua/factionbindings.hpp b/apps/openmw/mwlua/factionbindings.hpp index 0dc06ceaf2..280c7d4791 100644 --- a/apps/openmw/mwlua/factionbindings.hpp +++ b/apps/openmw/mwlua/factionbindings.hpp @@ -3,10 +3,10 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + sol::table initCoreFactionBindings(const Context& context); } diff --git a/apps/openmw/mwlua/inputbindings.cpp b/apps/openmw/mwlua/inputbindings.cpp index c3b47c5061..9a781106a6 100644 --- a/apps/openmw/mwlua/inputbindings.cpp +++ b/apps/openmw/mwlua/inputbindings.cpp @@ -13,6 +13,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwinput/actions.hpp" +#include "context.hpp" #include "luamanagerimp.hpp" namespace sol diff --git a/apps/openmw/mwlua/inputbindings.hpp b/apps/openmw/mwlua/inputbindings.hpp index 195f69f5f9..e9a19ed34d 100644 --- a/apps/openmw/mwlua/inputbindings.hpp +++ b/apps/openmw/mwlua/inputbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initInputPackage(const Context&); + struct Context; + + sol::table initInputPackage(const Context& context); } #endif // MWLUA_INPUTBINDINGS_H diff --git a/apps/openmw/mwlua/landbindings.cpp b/apps/openmw/mwlua/landbindings.cpp index 3f85e2b066..2b053802db 100644 --- a/apps/openmw/mwlua/landbindings.cpp +++ b/apps/openmw/mwlua/landbindings.cpp @@ -1,14 +1,24 @@ #include "landbindings.hpp" +#include +#include + +#include +#include +#include + #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/worldmodel.hpp" + +#include "context.hpp" #include "object.hpp" namespace diff --git a/apps/openmw/mwlua/landbindings.hpp b/apps/openmw/mwlua/landbindings.hpp index 8cdf30046b..f38d260759 100644 --- a/apps/openmw/mwlua/landbindings.hpp +++ b/apps/openmw/mwlua/landbindings.hpp @@ -1,10 +1,12 @@ #ifndef MWLUA_LANDBINDINGS_H #define MWLUA_LANDBINDINGS_H -#include "context.hpp" +#include namespace MWLua { + struct Context; + sol::table initCoreLandBindings(const Context& context); } diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp index d784328035..99c994304b 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -1,9 +1,5 @@ #include "localscripts.hpp" -#include -#include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/aicombat.hpp" @@ -232,7 +228,7 @@ namespace MWLua [&](LuaUtil::LuaView& view) { addPackage("openmw.self", sol::make_object(view.sol(), &mData)); }); registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers, &mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers, &mOnSkillUse, - &mOnSkillLevelUp }); + &mOnSkillLevelUp, &mOnJailTimeServed }); } void LocalScripts::setActive(bool active, bool callHandlers) diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp index b3ec647d0e..c51d2d8c19 100644 --- a/apps/openmw/mwlua/localscripts.hpp +++ b/apps/openmw/mwlua/localscripts.hpp @@ -1,9 +1,7 @@ #ifndef MWLUA_LOCALSCRIPTS_H #define MWLUA_LOCALSCRIPTS_H -#include -#include -#include +#include #include #include @@ -89,6 +87,7 @@ namespace MWLua { callEngineHandlers(mOnSkillLevelUp, skillId, source); } + void onJailTimeServed(int days) { callEngineHandlers(mOnJailTimeServed, days); } void applyStatsCache(); @@ -118,6 +117,7 @@ namespace MWLua EngineHandlerList mOnPlayAnimationHandlers{ "_onPlayAnimation" }; EngineHandlerList mOnSkillUse{ "_onSkillUse" }; EngineHandlerList mOnSkillLevelUp{ "_onSkillLevelUp" }; + EngineHandlerList mOnJailTimeServed{ "_onJailTimeServed" }; }; } diff --git a/apps/openmw/mwlua/luabindings.hpp b/apps/openmw/mwlua/luabindings.hpp index 749987e5b2..e7b73dbca1 100644 --- a/apps/openmw/mwlua/luabindings.hpp +++ b/apps/openmw/mwlua/luabindings.hpp @@ -1,29 +1,30 @@ #ifndef MWLUA_LUABINDINGS_H #define MWLUA_LUABINDINGS_H -#include #include -#include -#include "context.hpp" +#include +#include namespace MWLua { + struct Context; + // Initialize Lua packages that are available for all scripts. - std::map initCommonPackages(const Context&); + std::map initCommonPackages(const Context& context); // Initialize Lua packages that are available for global scripts (additionally to common packages). - std::map initGlobalPackages(const Context&); + std::map initGlobalPackages(const Context& context); // Initialize Lua packages that are available for local scripts (additionally to common packages). - std::map initLocalPackages(const Context&); + std::map initLocalPackages(const Context& context); // Initialize Lua packages that are available only for local scripts on the player (additionally to common and local // packages). - std::map initPlayerPackages(const Context&); + std::map initPlayerPackages(const Context& context); // Initialize Lua packages that are available only for menu scripts (additionally to common packages). - std::map initMenuPackages(const Context&); + std::map initMenuPackages(const Context& context); } #endif // MWLUA_LUABINDINGS_H diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index df9c9cd50f..66ecfb1c55 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -5,6 +5,10 @@ #include #include +#include +#include +#include + #include #include @@ -15,7 +19,6 @@ #include -#include #include #include @@ -490,6 +493,11 @@ namespace MWLua EngineEvents::OnSkillLevelUp{ getId(actor), skillId.serializeText(), std::string(source) }); } + void LuaManager::jailTimeServed(const MWWorld::Ptr& actor, int days) + { + mEngineEvents.addToQueue(EngineEvents::OnJailTimeServed{ getId(actor), days }); + } + void LuaManager::onHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, int attackType, float attackStrength, float damage, bool isHealth, const osg::Vec3f& hitPos, bool successful, MWMechanics::DamageSourceType sourceType) diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index f47b6f96cf..42b18d236f 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -3,9 +3,10 @@ #include #include -#include #include +#include + #include #include #include @@ -92,6 +93,7 @@ namespace MWLua bool loopfallback) override; void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) override; void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) override; + void jailTimeServed(const MWWorld::Ptr& actor, int days) override; void onHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, int attackType, float attackStrength, float damage, bool isHealth, const osg::Vec3f& hitPos, bool successful, MWMechanics::DamageSourceType sourceType) override; diff --git a/apps/openmw/mwlua/magicbindings.hpp b/apps/openmw/mwlua/magicbindings.hpp index 047bd2e3d9..d9c5182d76 100644 --- a/apps/openmw/mwlua/magicbindings.hpp +++ b/apps/openmw/mwlua/magicbindings.hpp @@ -3,10 +3,10 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + sol::table initCoreMagicBindings(const Context& context); void addActorMagicBindings(sol::table& actor, const Context& context); } diff --git a/apps/openmw/mwlua/markupbindings.hpp b/apps/openmw/mwlua/markupbindings.hpp index 9105ab5edf..ec4b6dbbb2 100644 --- a/apps/openmw/mwlua/markupbindings.hpp +++ b/apps/openmw/mwlua/markupbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initMarkupPackage(const Context&); + struct Context; + + sol::table initMarkupPackage(const Context& context); } #endif // MWLUA_MARKUPBINDINGS_H diff --git a/apps/openmw/mwlua/menuscripts.cpp b/apps/openmw/mwlua/menuscripts.cpp index 32520c0822..a7ff80e63e 100644 --- a/apps/openmw/mwlua/menuscripts.cpp +++ b/apps/openmw/mwlua/menuscripts.cpp @@ -7,6 +7,8 @@ #include "../mwbase/statemanager.hpp" #include "../mwstate/character.hpp" +#include "context.hpp" + namespace MWLua { static const MWState::Character* findCharacter(std::string_view characterDir) diff --git a/apps/openmw/mwlua/menuscripts.hpp b/apps/openmw/mwlua/menuscripts.hpp index 8721224413..11c6cc1912 100644 --- a/apps/openmw/mwlua/menuscripts.hpp +++ b/apps/openmw/mwlua/menuscripts.hpp @@ -9,11 +9,11 @@ #include "../mwbase/luamanager.hpp" -#include "context.hpp" #include "inputprocessor.hpp" namespace MWLua { + struct Context; sol::table initMenuPackage(const Context& context); diff --git a/apps/openmw/mwlua/mwscriptbindings.cpp b/apps/openmw/mwlua/mwscriptbindings.cpp index 90d24b39fe..179b35aa5c 100644 --- a/apps/openmw/mwlua/mwscriptbindings.cpp +++ b/apps/openmw/mwlua/mwscriptbindings.cpp @@ -12,6 +12,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/worldimp.hpp" +#include "context.hpp" #include "object.hpp" #include diff --git a/apps/openmw/mwlua/mwscriptbindings.hpp b/apps/openmw/mwlua/mwscriptbindings.hpp index 9598f051ae..c0c88ec28c 100644 --- a/apps/openmw/mwlua/mwscriptbindings.hpp +++ b/apps/openmw/mwlua/mwscriptbindings.hpp @@ -3,13 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; - sol::table initMWScriptBindings(const Context&); - + sol::table initMWScriptBindings(const Context& context); } #endif // MWLUA_MWSCRIPTBINDINGS_H diff --git a/apps/openmw/mwlua/nearbybindings.cpp b/apps/openmw/mwlua/nearbybindings.cpp index 6c244a0fd4..b31a934a54 100644 --- a/apps/openmw/mwlua/nearbybindings.cpp +++ b/apps/openmw/mwlua/nearbybindings.cpp @@ -13,9 +13,12 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/scene.hpp" +#include "context.hpp" #include "luamanagerimp.hpp" #include "objectlists.hpp" +#include + namespace { template diff --git a/apps/openmw/mwlua/nearbybindings.hpp b/apps/openmw/mwlua/nearbybindings.hpp index ee0022898e..2d8140195e 100644 --- a/apps/openmw/mwlua/nearbybindings.hpp +++ b/apps/openmw/mwlua/nearbybindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initNearbyPackage(const Context&); + struct Context; + + sol::table initNearbyPackage(const Context& context); } #endif // MWLUA_NEARBYBINDINGS_H diff --git a/apps/openmw/mwlua/object.hpp b/apps/openmw/mwlua/object.hpp index d032515314..4a50dee8b6 100644 --- a/apps/openmw/mwlua/object.hpp +++ b/apps/openmw/mwlua/object.hpp @@ -1,9 +1,7 @@ #ifndef MWLUA_OBJECT_H #define MWLUA_OBJECT_H -#include #include -#include #include diff --git a/apps/openmw/mwlua/objectbindings.hpp b/apps/openmw/mwlua/objectbindings.hpp index 5f69c54da3..3861211357 100644 --- a/apps/openmw/mwlua/objectbindings.hpp +++ b/apps/openmw/mwlua/objectbindings.hpp @@ -1,12 +1,12 @@ #ifndef MWLUA_OBJECTBINDINGS_H #define MWLUA_OBJECTBINDINGS_H -#include "context.hpp" - namespace MWLua { - void initObjectBindingsForLocalScripts(const Context&); - void initObjectBindingsForGlobalScripts(const Context&); + struct Context; + + void initObjectBindingsForLocalScripts(const Context& context); + void initObjectBindingsForGlobalScripts(const Context& context); } #endif // MWLUA_OBJECTBINDINGS_H diff --git a/apps/openmw/mwlua/objectlists.cpp b/apps/openmw/mwlua/objectlists.cpp index d0bda5a644..b0169dd651 100644 --- a/apps/openmw/mwlua/objectlists.cpp +++ b/apps/openmw/mwlua/objectlists.cpp @@ -1,9 +1,5 @@ #include "objectlists.hpp" -#include -#include -#include - #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwlua/postprocessingbindings.cpp b/apps/openmw/mwlua/postprocessingbindings.cpp index f12bda8650..6d0c33848a 100644 --- a/apps/openmw/mwlua/postprocessingbindings.cpp +++ b/apps/openmw/mwlua/postprocessingbindings.cpp @@ -8,6 +8,7 @@ #include "../mwbase/world.hpp" #include "../mwrender/postprocessor.hpp" +#include "context.hpp" #include "luamanagerimp.hpp" namespace diff --git a/apps/openmw/mwlua/postprocessingbindings.hpp b/apps/openmw/mwlua/postprocessingbindings.hpp index 50cd84fa24..55b46d2524 100644 --- a/apps/openmw/mwlua/postprocessingbindings.hpp +++ b/apps/openmw/mwlua/postprocessingbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initPostprocessingPackage(const Context&); + struct Context; + + sol::table initPostprocessingPackage(const Context& context); } #endif // MWLUA_POSTPROCESSINGBINDINGS_H diff --git a/apps/openmw/mwlua/racebindings.cpp b/apps/openmw/mwlua/racebindings.cpp index b5c4d6093f..590d16b4c9 100644 --- a/apps/openmw/mwlua/racebindings.cpp +++ b/apps/openmw/mwlua/racebindings.cpp @@ -8,7 +8,7 @@ #include "../mwworld/esmstore.hpp" #include "idcollectionbindings.hpp" -#include "types/types.hpp" +#include "recordstore.hpp" namespace { diff --git a/apps/openmw/mwlua/racebindings.hpp b/apps/openmw/mwlua/racebindings.hpp index 43ba9237c5..405a158899 100644 --- a/apps/openmw/mwlua/racebindings.hpp +++ b/apps/openmw/mwlua/racebindings.hpp @@ -3,10 +3,10 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + sol::table initRaceRecordBindings(const Context& context); } diff --git a/apps/openmw/mwlua/recordstore.hpp b/apps/openmw/mwlua/recordstore.hpp index aed84a1271..f2fb4ed453 100644 --- a/apps/openmw/mwlua/recordstore.hpp +++ b/apps/openmw/mwlua/recordstore.hpp @@ -1,10 +1,16 @@ #ifndef MWLUA_RECORDSTORE_H #define MWLUA_RECORDSTORE_H -#include +#include + +#include +#include +#include +#include +#include +#include +#include -#include -#include #include #include "apps/openmw/mwbase/environment.hpp" diff --git a/apps/openmw/mwlua/soundbindings.hpp b/apps/openmw/mwlua/soundbindings.hpp index 333ed898c4..d7af17cb69 100644 --- a/apps/openmw/mwlua/soundbindings.hpp +++ b/apps/openmw/mwlua/soundbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initCoreSoundBindings(const Context&); + struct Context; + + sol::table initCoreSoundBindings(const Context& context); sol::table initAmbientPackage(const Context& context); } diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 637ae0b002..8b103d5943 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -1,9 +1,11 @@ #include "stats.hpp" #include -#include #include +#include #include +#include +#include #include #include diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index 826338ca7d..8d9892005c 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -108,6 +108,10 @@ namespace MWLua } luaManager->addUIMessage(message, mode); }; + + api["_showInteractiveMessage"] = [windowManager](std::string_view message, sol::optional) { + windowManager->interactiveMessageBox(message, { "#{Interface:OK}" }); + }; api["CONSOLE_COLOR"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs(lua, { { "Default", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Default.substr(1)) }, diff --git a/apps/openmw/mwlua/uibindings.hpp b/apps/openmw/mwlua/uibindings.hpp index 930ba7f3d8..32fb54e590 100644 --- a/apps/openmw/mwlua/uibindings.hpp +++ b/apps/openmw/mwlua/uibindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initUserInterfacePackage(const Context&); + struct Context; + + sol::table initUserInterfacePackage(const Context& context); } #endif // MWLUA_UIBINDINGS_H diff --git a/apps/openmw/mwlua/userdataserializer.hpp b/apps/openmw/mwlua/userdataserializer.hpp index f52bb22723..35e4c4729b 100644 --- a/apps/openmw/mwlua/userdataserializer.hpp +++ b/apps/openmw/mwlua/userdataserializer.hpp @@ -1,7 +1,8 @@ #ifndef MWLUA_USERDATASERIALIZER_H #define MWLUA_USERDATASERIALIZER_H -#include "object.hpp" +#include +#include namespace LuaUtil { diff --git a/apps/openmw/mwlua/vfsbindings.hpp b/apps/openmw/mwlua/vfsbindings.hpp index b251db6fd4..ab3ad3662c 100644 --- a/apps/openmw/mwlua/vfsbindings.hpp +++ b/apps/openmw/mwlua/vfsbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initVFSPackage(const Context&); + struct Context; + + sol::table initVFSPackage(const Context& context); } #endif // MWLUA_VFSBINDINGS_H diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index c02bad3bd3..d98c769e65 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -27,6 +26,7 @@ #include "luamanagerimp.hpp" #include "animationbindings.hpp" +#include "context.hpp" #include "corebindings.hpp" #include "mwscriptbindings.hpp" diff --git a/apps/openmw/mwlua/worldbindings.hpp b/apps/openmw/mwlua/worldbindings.hpp index 4bd2318b68..368344429b 100644 --- a/apps/openmw/mwlua/worldbindings.hpp +++ b/apps/openmw/mwlua/worldbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initWorldPackage(const Context&); + struct Context; + + sol::table initWorldPackage(const Context& context); } #endif // MWLUA_WORLDBINDINGS_H diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index f403f97c2f..adc107f7cc 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -93,9 +93,10 @@ namespace namespace MWPhysics { PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode) - : mShapeManager( - std::make_unique(resourceSystem->getVFS(), resourceSystem->getSceneManager(), - resourceSystem->getNifFileManager(), Settings::cells().mCacheExpiryDelay)) + : mPhysicsDt(1.f / 60.f) + , mShapeManager(std::make_unique(resourceSystem->getVFS(), + resourceSystem->getSceneManager(), resourceSystem->getNifFileManager(), + Settings::cells().mCacheExpiryDelay)) , mResourceSystem(resourceSystem) , mDebugDrawEnabled(false) , mTimeAccum(0.0f) @@ -103,7 +104,6 @@ namespace MWPhysics , mWaterHeight(0) , mWaterEnabled(false) , mParentNode(std::move(parentNode)) - , mPhysicsDt(1.f / 60.f) { mResourceSystem->addResourceManager(mShapeManager.get()); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 8a845b4c41..0ab0cf91a0 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -287,6 +287,8 @@ namespace MWPhysics void reportStats(unsigned int frameNumber, osg::Stats& stats) const; void reportCollision(const btVector3& position, const btVector3& normal); + float mPhysicsDt; + private: void updateWater(); @@ -330,8 +332,6 @@ namespace MWPhysics osg::ref_ptr mParentNode; - float mPhysicsDt; - std::size_t mSimulationsCounter = 0; std::array, 2> mSimulations; std::vector> mActorsPositions; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index c14eee27e4..4aface83d6 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -401,14 +401,12 @@ namespace MWRender { if (mViewMode == VM_FirstPerson) { - static bool prototypeAdded = false; - if (!prototypeAdded) - { + [[maybe_unused]] static const bool prototypeAdded = [&] { osg::ref_ptr depthClearBin(new osgUtil::RenderBin); depthClearBin->setDrawCallback(new DepthClearCallback()); osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); - prototypeAdded = true; - } + return true; + }(); mObjectRoot->getOrCreateStateSet()->setRenderBinDetails( RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); } diff --git a/apps/openmw/mwrender/ripples.cpp b/apps/openmw/mwrender/ripples.cpp index ab982d0c55..b19863d695 100644 --- a/apps/openmw/mwrender/ripples.cpp +++ b/apps/openmw/mwrender/ripples.cpp @@ -88,14 +88,13 @@ namespace MWRender if (mProgramBlobber != nullptr) { - static bool pipelineLogged = [&] { + [[maybe_unused]] static const bool pipelineLogged = [&] { if (mUseCompute) Log(Debug::Info) << "Initialized compute shader pipeline for water ripples"; else Log(Debug::Info) << "Initialized fallback fragment shader pipeline for water ripples"; return true; }(); - (void)pipelineLogged; } setCullCallback(new osg::NodeCallback); diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 9776d7e632..0e752701c7 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -1,6 +1,8 @@ #include "terrainstorage.hpp" #include +#include +#include #include #include "../mwbase/environment.hpp" @@ -111,4 +113,15 @@ namespace MWRender return esmStore.get().search(index, plugin); } + const ESM4::LandTexture* TerrainStorage::getEsm4LandTexture(ESM::RefId ltexId) const + { + const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); + return esmStore.get().search(ltexId); + } + + const ESM4::TextureSet* TerrainStorage::getEsm4TextureSet(ESM::RefId txstId) const + { + const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); + return esmStore.get().search(txstId); + } } diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 731f396713..406894fd9b 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -24,6 +24,9 @@ namespace MWRender osg::ref_ptr getLand(ESM::ExteriorCellLocation cellLocation) override; const std::string* getLandTexture(std::uint16_t index, int plugin) override; + const ESM4::LandTexture* getEsm4LandTexture(ESM::RefId ltexId) const override; + const ESM4::TextureSet* getEsm4TextureSet(ESM::RefId txstId) const override; + bool hasData(ESM::ExteriorCellLocation cellLocation) override; /// Get bounds of the whole terrain in cell units diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index fd2064463e..1511909332 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -604,7 +604,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - std::string_view effect = runtime.getStringLiteral(runtime[0].mInteger); + const std::string_view effectName = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); if (!ptr.getClass().isActor()) @@ -615,11 +615,11 @@ namespace MWScript long key; - if (const auto k = ::Misc::StringUtils::toNumeric(effect); + if (const auto k = ::Misc::StringUtils::toNumeric(effectName); k.has_value() && *k >= 0 && *k <= 32767) key = *k; else - key = ESM::MagicEffect::effectGmstIdToIndex(effect); + key = ESM::MagicEffect::effectGmstIdToIndex(effectName); const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); for (const auto& spell : stats.getActiveSpells()) diff --git a/apps/openmw/mwsound/ffmpegdecoder.cpp b/apps/openmw/mwsound/ffmpegdecoder.cpp index 5a0f336a93..f8b40ddabb 100644 --- a/apps/openmw/mwsound/ffmpegdecoder.cpp +++ b/apps/openmw/mwsound/ffmpegdecoder.cpp @@ -522,16 +522,14 @@ namespace MWSound /* We need to make sure ffmpeg is initialized. Optionally silence warning * output from the lib */ - static bool done_init = false; - if (!done_init) - { + [[maybe_unused]] static const bool doneInit = [] { // This is not needed anymore above FFMpeg version 4.0 #if LIBAVCODEC_VERSION_INT < 3805796 av_register_all(); #endif av_log_set_level(AV_LOG_ERROR); - done_init = true; - } + return true; + }(); } FFmpegDecoder::~FFmpegDecoder() diff --git a/apps/openmw/mwsound/openaloutput.cpp b/apps/openmw/mwsound/openaloutput.cpp index 60c9e5f3ea..ecce476685 100644 --- a/apps/openmw/mwsound/openaloutput.cpp +++ b/apps/openmw/mwsound/openaloutput.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "efxpresets.h" @@ -963,6 +964,7 @@ namespace MWSound // Speed of sound is in units per second. Take the sound speed in air (assumed // meters per second), multiply by the units per meter to get the speed in u/s. alSpeedOfSound(Constants::SoundSpeedInAir * Constants::UnitsPerMeter); + alDopplerFactor(Settings::sound().mDopplerFactor); alGetError(); mInitialized = true; @@ -1142,8 +1144,8 @@ namespace MWSound alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } - void OpenALOutput::initCommon3D(ALuint source, const osg::Vec3f& pos, ALfloat mindist, ALfloat maxdist, - ALfloat gain, ALfloat pitch, bool loop, bool useenv) + void OpenALOutput::initCommon3D(ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat mindist, + ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv) { alSourcef(source, AL_REFERENCE_DISTANCE, mindist); alSourcef(source, AL_MAX_DISTANCE, maxdist); @@ -1179,11 +1181,11 @@ namespace MWSound alSourcef(source, AL_PITCH, pitch); alSourcefv(source, AL_POSITION, pos.ptr()); alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alSourcefv(source, AL_VELOCITY, vel.ptr()); } - void OpenALOutput::updateCommon( - ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv) + void OpenALOutput::updateCommon(ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat maxdist, + ALfloat gain, ALfloat pitch, bool useenv) { if (useenv && mListenerEnv == Env_Underwater && !mWaterFilter) { @@ -1195,7 +1197,7 @@ namespace MWSound alSourcef(source, AL_PITCH, pitch); alSourcefv(source, AL_POSITION, pos.ptr()); alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alSourcefv(source, AL_VELOCITY, vel.ptr()); } bool OpenALOutput::playSound(Sound* sound, Sound_Handle data, float offset) @@ -1248,8 +1250,9 @@ namespace MWSound } source = mFreeSources.front(); - initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), - sound->getRealVolume(), getTimeScaledPitch(sound), sound->getIsLooping(), sound->getUseEnv()); + initCommon3D(source, sound->getPosition(), sound->getVelocity(), sound->getMinDistance(), + sound->getMaxDistance(), sound->getRealVolume(), getTimeScaledPitch(sound), sound->getIsLooping(), + sound->getUseEnv()); alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcef(source, AL_SEC_OFFSET, offset); if (getALError() != AL_NO_ERROR) @@ -1312,8 +1315,8 @@ namespace MWSound return; ALuint source = GET_PTRID(sound->mHandle); - updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), - getTimeScaledPitch(sound), sound->getUseEnv()); + updateCommon(source, sound->getPosition(), sound->getVelocity(), sound->getMaxDistance(), + sound->getRealVolume(), getTimeScaledPitch(sound), sound->getUseEnv()); getALError(); } @@ -1360,8 +1363,8 @@ namespace MWSound if (sound->getIsLooping()) Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\""; - initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), - sound->getRealVolume(), getTimeScaledPitch(sound), false, sound->getUseEnv()); + initCommon3D(source, sound->getPosition(), sound->getVelocity(), sound->getMinDistance(), + sound->getMaxDistance(), sound->getRealVolume(), getTimeScaledPitch(sound), false, sound->getUseEnv()); if (getALError() != AL_NO_ERROR) return false; @@ -1443,8 +1446,8 @@ namespace MWSound OpenAL_SoundStream* stream = reinterpret_cast(sound->mHandle); ALuint source = stream->mSource; - updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), - getTimeScaledPitch(sound), sound->getUseEnv()); + updateCommon(source, sound->getPosition(), sound->getVelocity(), sound->getMaxDistance(), + sound->getRealVolume(), getTimeScaledPitch(sound), sound->getUseEnv()); getALError(); } @@ -1459,12 +1462,13 @@ namespace MWSound } void OpenALOutput::updateListener( - const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, Environment env) + const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, const osg::Vec3f& vel, Environment env) { if (mContext) { ALfloat orient[6] = { atdir.x(), atdir.y(), atdir.z(), updir.x(), updir.y(), updir.z() }; alListenerfv(AL_POSITION, pos.ptr()); + alListenerfv(AL_VELOCITY, vel.ptr()); alListenerfv(AL_ORIENTATION, orient); if (env != mListenerEnv) @@ -1497,6 +1501,7 @@ namespace MWSound } mListenerPos = pos; + mListenerVel = vel; mListenerEnv = env; } @@ -1582,7 +1587,6 @@ namespace MWSound : SoundOutput(mgr) , mDevice(nullptr) , mContext(nullptr) - , mListenerPos(0.0f, 0.0f, 0.0f) , mListenerEnv(Env_Normal) , mWaterFilter(0) , mWaterEffect(0) diff --git a/apps/openmw/mwsound/openaloutput.hpp b/apps/openmw/mwsound/openaloutput.hpp index 4e96dd1627..d689d08546 100644 --- a/apps/openmw/mwsound/openaloutput.hpp +++ b/apps/openmw/mwsound/openaloutput.hpp @@ -46,6 +46,7 @@ namespace MWSound StreamVec mActiveStreams; osg::Vec3f mListenerPos; + osg::Vec3f mListenerVel; Environment mListenerEnv; ALuint mWaterFilter; @@ -64,11 +65,11 @@ namespace MWSound std::unique_ptr mDefaultDeviceThread; void initCommon2D(ALuint source, const osg::Vec3f& pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv); - void initCommon3D(ALuint source, const osg::Vec3f& pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, - ALfloat pitch, bool loop, bool useenv); + void initCommon3D(ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat mindist, ALfloat maxdist, + ALfloat gain, ALfloat pitch, bool loop, bool useenv); - void updateCommon( - ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv); + void updateCommon(ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat maxdist, ALfloat gain, + ALfloat pitch, bool useenv); float getTimeScaledPitch(SoundBase* sound); @@ -108,8 +109,8 @@ namespace MWSound void startUpdate() override; void finishUpdate() override; - void updateListener( - const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, Environment env) override; + void updateListener(const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, + const osg::Vec3f& vel, Environment env) override; void pauseSounds(int types) override; void resumeSounds(int types) override; diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 5160b2934f..7d020c31f9 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -31,6 +31,8 @@ namespace MWSound struct SoundParams { osg::Vec3f mPos; + osg::Vec3f mLastPos; + osg::Vec3f mVel; float mVolume = 1.0f; float mBaseVolume = 1.0f; float mPitch = 1.0f; @@ -57,6 +59,8 @@ namespace MWSound public: void setPosition(const osg::Vec3f& pos) { mParams.mPos = pos; } + void setLastPosition(const osg::Vec3f& lastpos) { mParams.mLastPos = lastpos; } + void setVelocity(const osg::Vec3f& vel) { mParams.mVel = vel; } void setVolume(float volume) { mParams.mVolume = volume; } void setBaseVolume(float volume) { mParams.mBaseVolume = volume; } void setFadeout(float duration) { setFade(duration, 0.0, Play_StopAtFadeEnd); } @@ -150,6 +154,8 @@ namespace MWSound } const osg::Vec3f& getPosition() const { return mParams.mPos; } + const osg::Vec3f& getLastPosition() const { return mParams.mLastPos; } + const osg::Vec3f& getVelocity() const { return mParams.mVel; } float getRealVolume() const { return mParams.mVolume * mParams.mBaseVolume * mParams.mFadeVolume; } float getPitch() const { return mParams.mPitch; } float getMinDistance() const { return mParams.mMinDistance; } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 66bdfbdbfa..86631448a9 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -119,6 +119,7 @@ namespace MWSound , mListenerPos(0, 0, 0) , mListenerDir(1, 0, 0) , mListenerUp(0, 0, 1) + , mListenerVel(0, 0, 0) , mUnderwaterSound(nullptr) , mNearWaterSound(nullptr) , mPlaybackPaused(false) @@ -960,7 +961,7 @@ namespace MWSound } mOutput->startUpdate(); - mOutput->updateListener(mListenerPos, mListenerDir, mListenerUp, env); + mOutput->updateListener(mListenerPos, mListenerDir, mListenerUp, mListenerVel, env); updateMusic(duration); @@ -977,7 +978,13 @@ namespace MWSound if (sound->getIs3D()) { if (!ptr.isEmpty()) + { + sound->setLastPosition(sound->getPosition()); sound->setPosition(ptr.getRefData().getPosition().asVec3()); + MWBase::World* world = MWBase::Environment::get().getWorld(); + sound->setVelocity( + (sound->getPosition() - sound->getLastPosition()) / world->getPhysicsFrameRateDt()); + } cull3DSound(sound); } @@ -1013,8 +1020,11 @@ namespace MWSound { if (!ptr.isEmpty()) { + sound->setLastPosition(sound->getPosition()); MWBase::World* world = MWBase::Environment::get().getWorld(); sound->setPosition(world->getActorHeadTransform(ptr).getTrans()); + sound->setVelocity( + (sound->getPosition() - sound->getLastPosition()) / world->getPhysicsFrameRateDt()); } cull3DSound(sound); @@ -1153,6 +1163,11 @@ namespace MWSound mWaterSoundUpdater.setUnderwater(underwater); } + void SoundManager::setListenerVel(const osg::Vec3f& vel) + { + mListenerVel = vel; + } + void SoundManager::updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) { SoundMap::iterator snditer = mActiveSounds.find(old.mRef); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 8fc7e6701f..f1b79557a0 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -92,6 +92,7 @@ namespace MWSound osg::Vec3f mListenerPos; osg::Vec3f mListenerDir; osg::Vec3f mListenerUp; + osg::Vec3f mListenerVel; int mPausedSoundTypes[BlockerType::MaxCount] = {}; @@ -283,6 +284,8 @@ namespace MWSound void setListenerPosDir( const osg::Vec3f& pos, const osg::Vec3f& dir, const osg::Vec3f& up, bool underwater) override; + void setListenerVel(const osg::Vec3f& vel) override; + void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) override; void clear() override; diff --git a/apps/openmw/mwsound/soundoutput.hpp b/apps/openmw/mwsound/soundoutput.hpp index e5c266b204..4b24f159d3 100644 --- a/apps/openmw/mwsound/soundoutput.hpp +++ b/apps/openmw/mwsound/soundoutput.hpp @@ -61,8 +61,8 @@ namespace MWSound virtual void startUpdate() = 0; virtual void finishUpdate() = 0; - virtual void updateListener( - const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, Environment env) + virtual void updateListener(const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, + const osg::Vec3f& vel, Environment env) = 0; virtual void pauseSounds(int types) = 0; diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 0c37f243e8..fc4ef2713b 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -110,6 +110,7 @@ namespace ESM4 struct Static; struct StaticCollection; struct Terminal; + struct TextureSet; struct Tree; struct Weapon; struct World; @@ -149,7 +150,7 @@ namespace MWWorld Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, - Store, Store, Store, Store>; + Store, Store, Store, Store, Store>; private: template diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 6ea510e1e2..c436f86df5 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -461,6 +461,11 @@ namespace MWWorld update(magicBoltState, duration); + for (const auto& sound : magicBoltState.mSounds) + { + sound->setVelocity(direction * speed); + } + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit // result. std::vector targetActors; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 0c9a13bc47..1a68142c15 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1273,13 +1273,12 @@ namespace MWWorld const std::size_t leftCapacity = mPreloader->getMaxCacheSize() - mPreloader->getCacheSize(); if (cells.size() > leftCapacity) { - static bool logged = [&] { + [[maybe_unused]] static const bool logged = [&] { Log(Debug::Warning) << "Not enough cell preloader cache capacity to preload exterior cells, consider " "increasing \"preload cell cache max\" up to " << (mPreloader->getCacheSize() + cells.size()); return true; }(); - (void)logged; cells.resize(leftCapacity); } diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 80bcdb056a..e4e67c2f3d 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1354,6 +1354,7 @@ template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; +template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c07f5b9161..d8bc768b39 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1460,6 +1460,8 @@ namespace MWWorld void World::queueMovement(const Ptr& ptr, const osg::Vec3f& velocity) { mPhysics->queueObjectMovement(ptr, velocity); + if (ptr == MWMechanics::getPlayer()) + MWBase::Environment::get().getSoundManager()->setListenerVel(velocity); } void World::updateAnimatedCollisionShape(const Ptr& ptr) @@ -3182,6 +3184,11 @@ namespace MWWorld return mWeatherManager->getSunPercentage(getTimeStamp().getHour()); } + float World::getPhysicsFrameRateDt() const + { + return mPhysics->mPhysicsDt; + } + bool World::findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) { if (cell->isExterior()) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 16f91177a1..dfa0c33ecf 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -584,6 +584,8 @@ namespace MWWorld float getSunVisibility() const override; float getSunPercentage() const override; + float getPhysicsFrameRateDt() const override; + bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) override; /// Teleports \a ptr to the closest reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 8a3325ea71..6b7fe32d40 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -407,6 +407,7 @@ add_component_dir(detournavigator areatype asyncnavmeshupdater bounds + cellgridbounds changetype collisionshapetype commulativeaabb diff --git a/components/detournavigator/dbrefgeometryobject.hpp b/components/detournavigator/dbrefgeometryobject.hpp index dbbafe7e18..71e035d9e6 100644 --- a/components/detournavigator/dbrefgeometryobject.hpp +++ b/components/detournavigator/dbrefgeometryobject.hpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/components/detournavigator/generatenavmeshtile.cpp b/components/detournavigator/generatenavmeshtile.cpp index 118114181e..115ca75d40 100644 --- a/components/detournavigator/generatenavmeshtile.cpp +++ b/components/detournavigator/generatenavmeshtile.cpp @@ -10,10 +10,8 @@ #include -#include #include #include -#include #include namespace DetourNavigator @@ -79,7 +77,7 @@ namespace DetourNavigator return; } - const auto data + const std::unique_ptr data = prepareNavMeshTileData(*recastMesh, mWorldspace, mTilePosition, mAgentBounds, mSettings.mRecast); if (data == nullptr) diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index fe63d27a1e..06bb693aad 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -3,8 +3,6 @@ #include "exceptions.hpp" #include "flags.hpp" #include "navmeshdata.hpp" -#include "navmeshdb.hpp" -#include "navmeshtilescache.hpp" #include "offmeshconnection.hpp" #include "preparednavmeshdata.hpp" #include "recastcontext.hpp" @@ -22,8 +20,6 @@ #include #include -#include -#include namespace DetourNavigator { diff --git a/components/detournavigator/navmeshtileview.cpp b/components/detournavigator/navmeshtileview.cpp index d22efcbfff..e77962bb3c 100644 --- a/components/detournavigator/navmeshtileview.cpp +++ b/components/detournavigator/navmeshtileview.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include diff --git a/components/detournavigator/offmeshconnection.hpp b/components/detournavigator/offmeshconnection.hpp index fb8b166498..01bae02732 100644 --- a/components/detournavigator/offmeshconnection.hpp +++ b/components/detournavigator/offmeshconnection.hpp @@ -6,7 +6,6 @@ #include #include -#include namespace DetourNavigator { diff --git a/components/detournavigator/preparednavmeshdata.cpp b/components/detournavigator/preparednavmeshdata.cpp index a737ae19a5..32e559b8d0 100644 --- a/components/detournavigator/preparednavmeshdata.cpp +++ b/components/detournavigator/preparednavmeshdata.cpp @@ -4,8 +4,6 @@ #include -#include - namespace { void initPolyMeshDetail(rcPolyMeshDetail& value) noexcept diff --git a/components/detournavigator/raycast.hpp b/components/detournavigator/raycast.hpp index 855c5562fc..374b09320c 100644 --- a/components/detournavigator/raycast.hpp +++ b/components/detournavigator/raycast.hpp @@ -3,9 +3,10 @@ #include "flags.hpp" -#include #include +#include + class dtNavMeshQuery; namespace DetourNavigator diff --git a/components/detournavigator/recastmesh.hpp b/components/detournavigator/recastmesh.hpp index 6d06db0799..1a67bb5495 100644 --- a/components/detournavigator/recastmesh.hpp +++ b/components/detournavigator/recastmesh.hpp @@ -11,9 +11,7 @@ #include #include -#include #include -#include #include #include diff --git a/components/esm/common.hpp b/components/esm/common.hpp index 90c60ad6ab..7d149a865c 100644 --- a/components/esm/common.hpp +++ b/components/esm/common.hpp @@ -31,6 +31,7 @@ namespace ESM VER_134 = 0x3fab851f, // FONV, GunRunnersArsenal, LonesomeRoad, OldWorldBlues VER_094 = 0x3f70a3d7, // TES5/FO3 VER_170 = 0x3fd9999a, // TES5 + VER_171 = 0x3fdae148, // TES5 VER_095 = 0x3f733333, // FO4 }; diff --git a/components/esm/esmterrain.cpp b/components/esm/esmterrain.cpp index 4bc6768e51..ec824d5112 100644 --- a/components/esm/esmterrain.cpp +++ b/components/esm/esmterrain.cpp @@ -33,6 +33,7 @@ ESM::LandData::LandData(const ESM::Land& land, int loadFlags) , mNormals(mData->mNormals) , mColors(mData->mColours) , mTextures(mData->mTextures) + , mIsEsm4(false) { } @@ -43,9 +44,11 @@ ESM::LandData::LandData(const ESM4::Land& land, int /*loadFlags*/) , mMaxHeight(std::numeric_limits::lowest()) , mSize(Constants::ESM4CellSizeInUnits) , mLandSize(ESM4::Land::sVertsPerSide) + , mPlugin(land.mId.mContentFile) , mNormals(land.mVertNorm) , mColors(land.mVertColr) , mTextures(textures) + , mIsEsm4(true) { float rowOffset = land.mHeightMap.heightOffset; for (int y = 0; y < mLandSize; y++) @@ -69,6 +72,9 @@ ESM::LandData::LandData(const ESM4::Land& land, int /*loadFlags*/) } mHeights = mHeightsData; + + for (int i = 0; i < 4; ++i) + mEsm4Textures[i] = land.mTextures[i]; } namespace ESM diff --git a/components/esm/esmterrain.hpp b/components/esm/esmterrain.hpp index 991df54c8f..b6b62773d5 100644 --- a/components/esm/esmterrain.hpp +++ b/components/esm/esmterrain.hpp @@ -1,15 +1,13 @@ #ifndef COMPONENTS_ESM_ESMTERRAIN #define COMPONENTS_ESM_ESMTERRAIN +#include #include #include #include #include -namespace ESM4 -{ - struct Land; -} +#include namespace ESM { @@ -28,7 +26,6 @@ namespace ESM std::span getHeights() const { return mHeights; } std::span getNormals() const { return mNormals; } std::span getColors() const { return mColors; } - std::span getTextures() const { return mTextures; } float getSize() const { return mSize; } float getMinHeight() const { return mMinHeight; } float getMaxHeight() const { return mMaxHeight; } @@ -36,6 +33,22 @@ namespace ESM int getLoadFlags() const { return mLoadFlags; } int getPlugin() const { return mPlugin; } + bool isEsm4() const { return mIsEsm4; } + + std::span getTextures() const + { + if (mIsEsm4) + throw std::logic_error("ESM3 textures requested from ESM4 LandData"); + return mTextures; + } + + const ESM4::Land::Texture& getEsm4Texture(std::size_t quad) const + { + if (!mIsEsm4) + throw std::logic_error("ESM4 texture requested from ESM3 LandData"); + return mEsm4Textures[quad]; + } + private: std::unique_ptr mData; int mLoadFlags = 0; @@ -49,6 +62,8 @@ namespace ESM std::span mNormals; std::span mColors; std::span mTextures; + std::array mEsm4Textures; + bool mIsEsm4; }; } diff --git a/components/esm/formid.hpp b/components/esm/formid.hpp index e9416e35c7..9fe89585c2 100644 --- a/components/esm/formid.hpp +++ b/components/esm/formid.hpp @@ -51,10 +51,10 @@ namespace std { size_t operator()(const ESM::FormId& formId) const { - static_assert(sizeof(ESM::FormId) == sizeof(size_t)); - size_t s; - memcpy(&s, &formId, sizeof(size_t)); - return hash()(s); + static_assert(sizeof(ESM::FormId) == sizeof(uint64_t)); + uint64_t s; + memcpy(&s, &formId, sizeof(ESM::FormId)); + return hash()(s); } }; diff --git a/components/esm/records.hpp b/components/esm/records.hpp index 0b76fab0ff..3b9f8ccd15 100644 --- a/components/esm/records.hpp +++ b/components/esm/records.hpp @@ -79,6 +79,7 @@ #include #include #include +#include #include #include diff --git a/components/esm4/loadland.cpp b/components/esm4/loadland.cpp index 53fb1de083..a92b2b5960 100644 --- a/components/esm4/loadland.cpp +++ b/components/esm4/loadland.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2015-2016, 2018, 2020-2021 cc9cii + Copyright (C) 2015 - 2024 cc9cii This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -17,7 +17,7 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. - cc9cii cc9c@iinet.net.au + cc9cii cc9cii@hotmail.com Much of the information on the data structures are based on the information from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by @@ -26,13 +26,51 @@ */ #include "loadland.hpp" +#include #include #include #include #include "reader.hpp" -// #include "writer.hpp" + +namespace +{ + void assignDefaultTextures(ESM4::Land& land, ESM4::Reader& reader) + { + std::uint32_t esmVer = reader.esmVersion(); + + // Note: in games after TES4 it can be configured in ini file (sDefaultLandDiffuseTexture) + if (!reader.hasFormVersion() && (esmVer == ESM::VER_080 || esmVer == ESM::VER_100)) // TES4 + { + land.mDefaultDiffuseMap = VFS::Path::NormalizedView("textures/landscape/terrainhddirt01.dds"); + land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/terrainhddirt01_n.dds"); + } + else if (reader.hasFormVersion() && reader.formVersion() >= 16 + && (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || esmVer == ESM::VER_171)) // TES5 + { + land.mDefaultDiffuseMap = VFS::Path::NormalizedView("textures/landscape/dirt02.dds"); + land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/dirt02_n.dds"); + } + else if (esmVer == ESM::VER_095 || esmVer == ESM::VER_100) // FO4 + { + land.mDefaultDiffuseMap + = VFS::Path::NormalizedView("textures/landscape/ground/commonwealthdefault01_d.dds"); + land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/ground/commonwealthdefault01_n.dds"); + } + else if (esmVer == ESM::VER_094 || esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134) + { // FO3, FONV + land.mDefaultDiffuseMap = VFS::Path::NormalizedView("textures/landscape/dirtwasteland01.dds"); + land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/dirtwasteland01_n.dds"); + } + else + { + // Nothing especially bad happens if default texture is not set (except of the missing texture of course), + // but we throw an error because this case is unexpected and detection logic needs to be updated. + throw std::runtime_error("ESM4::Land unknown ESM version"); + } + } +} // overlap north // @@ -53,12 +91,16 @@ void ESM4::Land::load(ESM4::Reader& reader) { mId = reader.getFormIdFromHeader(); mFlags = reader.hdr().record.flags; + mDataTypes = 0; mCell = reader.currCell(); TxtLayer layer; std::int8_t currentAddQuad = -1; // for VTXT following ATXT + assignDefaultTextures(*this, reader); - // std::map uniqueTextures; // FIXME: for temp testing only + layer.texture.formId = 0; + for (int i = 0; i < 4; ++i) + mTextures[i].base.formId = 0; while (reader.getSubRecordHeader()) { @@ -78,12 +120,6 @@ void ESM4::Land::load(ESM4::Reader& reader) } case ESM::fourCC("VHGT"): // vertex height gradient, 4+33x33+3 = 4+1089+3 = 1096 { -#if 0 - reader.get(mHeightMap.heightOffset); - reader.get(mHeightMap.gradientData); - reader.get(mHeightMap.unknown1); - reader.get(mHeightMap.unknown2); -#endif reader.get(mHeightMap); mDataTypes |= LAND_VHGT; break; @@ -102,13 +138,9 @@ void ESM4::Land::load(ESM4::Reader& reader) if (base.quadrant >= 4) throw std::runtime_error("base texture quadrant index error"); - reader.adjustFormId(base.formId); - mTextures[base.quadrant].base = std::move(base); -#if 0 - std::cout << "Base Texture formid: 0x" - << std::hex << mTextures[base.quadrant].base.formId - << ", quad " << std::dec << (int)base.quadrant << std::endl; -#endif + if (base.formId != 0) + reader.adjustFormId(base.formId); + mTextures[base.quadrant].base = base; } break; } @@ -116,31 +148,23 @@ void ESM4::Land::load(ESM4::Reader& reader) { if (currentAddQuad != -1) { - // FIXME: sometimes there are no VTXT following an ATXT? Just add a dummy one for now - Log(Debug::Verbose) << "ESM4::Land VTXT empty layer " << layer.texture.layerIndex; + // NOTE: sometimes there are no VTXT following an ATXT + layer.data.resize(1); // just one spot + layer.data.back().position = 0; // this corner + layer.data.back().opacity = 0.f; // transparent + + if (layer.texture.layerIndex != mTextures[currentAddQuad].layers.size()) + throw std::runtime_error("ESM4::LAND additional texture skipping layer"); + mTextures[currentAddQuad].layers.push_back(layer); } + reader.get(layer.texture); - reader.adjustFormId(layer.texture.formId); + if (layer.texture.formId != 0) + reader.adjustFormId(layer.texture.formId); if (layer.texture.quadrant >= 4) - throw std::runtime_error("additional texture quadrant index error"); -#if 0 - FormId txt = layer.texture.formId; - std::map::iterator lb = uniqueTextures.lower_bound(txt); - if (lb != uniqueTextures.end() && !(uniqueTextures.key_comp()(txt, lb->first))) - { - lb->second += 1; - } - else - uniqueTextures.insert(lb, std::make_pair(txt, 1)); -#endif -#if 0 - std::cout << "Additional Texture formId: 0x" - << std::hex << layer.texture.formId - << ", quad " << std::dec << (int)layer.texture.quadrant << std::endl; - std::cout << "Additional Texture layer: " - << std::dec << (int)layer.texture.layerIndex << std::endl; -#endif + throw std::runtime_error("ESM4::LAND additional texture quadrant index error"); + currentAddQuad = layer.texture.quadrant; break; } @@ -156,25 +180,17 @@ void ESM4::Land::load(ESM4::Reader& reader) if (count) { layer.data.resize(count); - std::vector::iterator it = layer.data.begin(); - for (; it != layer.data.end(); ++it) - { - reader.get(*it); - // FIXME: debug only - // std::cout << "pos: " << std::dec << (int)(*it).position << std::endl; - } + for (ESM4::Land::VTXT& vtxt : layer.data) + reader.get(vtxt); } - mTextures[currentAddQuad].layers.push_back(layer); - // Assumed that the layers are added in the correct sequence - // FIXME: Knights.esp doesn't seem to observe this - investigate more - // assert(layer.texture.layerIndex == mTextures[currentAddQuad].layers.size()-1 - //&& "additional texture layer index error"); + if (layer.texture.layerIndex != mTextures[currentAddQuad].layers.size()) + throw std::runtime_error("ESM4::LAND additional texture skipping layer"); + + mTextures[currentAddQuad].layers.push_back(layer); currentAddQuad = -1; layer.data.clear(); - // FIXME: debug only - // std::cout << "VTXT: count " << std::dec << count << std::endl; break; } case ESM::fourCC("VTEX"): // only in Oblivion? @@ -195,44 +211,14 @@ void ESM4::Land::load(ESM4::Reader& reader) reader.skipSubRecordData(); break; default: - throw std::runtime_error("ESM4::LAND::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + throw std::runtime_error("ESM4::LAND - Unknown subrecord " + ESM::printName(subHdr.typeId)); } } if (currentAddQuad != -1) { - // FIXME: not sure if it happens here as well + // not sure if it happens here as well, if so just ignore Log(Debug::Verbose) << "ESM4::Land VTXT empty layer " << layer.texture.layerIndex << " quad " << static_cast(layer.texture.quadrant); - mTextures[currentAddQuad].layers.push_back(layer); } - - bool missing = false; - for (int i = 0; i < 4; ++i) - { - if (mTextures[i].base.formId == 0) - { - // std::cout << "ESM4::LAND " << ESM4::formIdToString(mFormId) << " missing base, quad " << i << std::endl; - // std::cout << "layers " << mTextures[i].layers.size() << std::endl; - // NOTE: can't set the default here since FO3/FONV may have different defaults - // mTextures[i].base.formId = 0x000008C0; // TerrainHDDirt01.dds - missing = true; - } - // else - //{ - // std::cout << "ESM4::LAND " << ESM4::formIdToString(mFormId) << " base, quad " << i << std::endl; - // std::cout << "layers " << mTextures[i].layers.size() << std::endl; - // } - } - // at least one of the quadrants do not have a base texture, return without setting the flag - if (!missing) - mDataTypes |= LAND_VTEX; } - -// void ESM4::Land::save(ESM4::Writer& writer) const -//{ -// } - -// void ESM4::Land::blank() -//{ -// } diff --git a/components/esm4/loadland.hpp b/components/esm4/loadland.hpp index ccfa009d80..600a235f1c 100644 --- a/components/esm4/loadland.hpp +++ b/components/esm4/loadland.hpp @@ -32,6 +32,7 @@ #include #include +#include namespace ESM4 { @@ -124,6 +125,8 @@ namespace ESM4 Texture mTextures[4]; // 0 = bottom left, 1 = bottom right, 2 = top left, 3 = top right std::vector mIds; // land texture (LTEX) formids ESM::FormId mCell; + VFS::Path::NormalizedView mDefaultDiffuseMap; + VFS::Path::NormalizedView mDefaultNormalMap; void load(Reader& reader); diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 35ec814aa2..1a07f6fe0a 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -89,6 +91,8 @@ namespace ESMTerrain LandObject::LandObject(const ESM4::Land& land, int loadFlags) : mData(land, loadFlags) { + mEsm4DefaultLayerInfo.mDiffuseMap = land.mDefaultDiffuseMap; + mEsm4DefaultLayerInfo.mNormalMap = land.mDefaultNormalMap; } LandObject::LandObject(const ESM::Land& land, int loadFlags) @@ -385,9 +389,105 @@ namespace ESMTerrain return Misc::ResourceHelpers::correctTexturePath(texture, mVFS); } + void Storage::getEsm4Blendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, + std::vector& layerList, ESM::RefId worldspace) + { + const osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize - 1, chunkSize + 1) * 0.5f; + const int startCellX = static_cast(std::floor(origin.x())); + const int startCellY = static_cast(std::floor(origin.y())); + + constexpr int quadsPerCell = 2; + constexpr int quadSize = ESM4::Land::sVertsPerSide / quadsPerCell; + const int quadCount = static_cast(chunkSize * quadsPerCell); + assert(quadCount > 0); + + const int blendmapSize = quadCount * quadSize + 1; + + LandCache cache(startCellX - 1, startCellY - 1, static_cast(std::ceil(chunkSize)) + 2); + std::pair lastCell{ startCellX, startCellY }; + const LandObject* land = getLand(ESM::ExteriorCellLocation(startCellX, startCellY, worldspace), cache); + + std::map textureIndicesMap; + + auto getOrCreateBlendmap = [&](ESM::FormId texId) -> unsigned char* { + auto found = textureIndicesMap.find(texId); + if (found != textureIndicesMap.end()) + return blendmaps[found->second]->data(); + Terrain::LayerInfo info + = texId.isZeroOrUnset() ? land->getEsm4DefaultLayerInfo() : getLandTextureLayerInfo(texId); + osg::ref_ptr image(new osg::Image); + image->allocateImage(blendmapSize, blendmapSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE); + std::memset(image->data(), 0, image->getTotalDataSize()); + textureIndicesMap.emplace(texId, blendmaps.size()); + blendmaps.push_back(std::move(image)); + layerList.push_back(std::move(info)); + return blendmaps.back()->data(); + }; + + const auto handleSample = [&](const CellSample& sample) { + const std::pair cell{ sample.mCellX, sample.mCellY }; + if (lastCell != cell) + { + land = getLand(ESM::ExteriorCellLocation(sample.mCellX, sample.mCellY, worldspace), cache); + lastCell = cell; + } + if (!land) + return; + const ESM::LandData* ldata = land->getData(0); + if (!ldata) + return; + int quad; + if (sample.mSrcRow == 0) + quad = sample.mSrcCol == 0 ? 0 : 2; + else + quad = sample.mSrcCol == 0 ? 1 : 3; + const ESM4::Land::Texture& ltex = ldata->getEsm4Texture(quad); + + unsigned char* const baseBlendmap = getOrCreateBlendmap(ESM::FormId::fromUint32(ltex.base.formId)); + int starty = (static_cast(sample.mDstCol) - 1) * quadSize; + int startx = sample.mDstRow * quadSize; + for (int y = std::max(0, starty + 1); y <= starty + quadSize && y < blendmapSize; ++y) + { + unsigned char* const row = baseBlendmap + (blendmapSize - y - 1) * blendmapSize; + for (int x = startx; x < startx + quadSize && x < blendmapSize; ++x) + row[x] = 255; + } + + for (const auto& layer : ltex.layers) + { + unsigned char* const layerBlendmap = getOrCreateBlendmap(ESM::FormId::fromUint32(layer.texture.formId)); + for (const ESM4::Land::VTXT& v : layer.data) + { + int y = v.position / (quadSize + 1); + int x = v.position % (quadSize + 1); + if (x == quadSize || startx + x >= blendmapSize || y == 0 || starty + y >= blendmapSize + || starty + y < 0) + { + continue; + } + int index = (blendmapSize - starty - y - 1) * blendmapSize + startx + x; + int delta = std::clamp(static_cast(v.opacity * 255), 0, 255); + baseBlendmap[index] = std::max(0, baseBlendmap[index] - delta); + layerBlendmap[index] = delta; + } + } + }; + + sampleBlendmaps(chunkSize, origin.x(), origin.y(), quadsPerCell, handleSample); + + if (blendmaps.size() == 1) + blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend + } + void Storage::getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, std::vector& layerList, ESM::RefId worldspace) { + if (ESM::isEsm4Ext(worldspace)) + { + getEsm4Blendmaps(chunkSize, chunkCenter, blendmaps, layerList, worldspace); + return; + } + const osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize, chunkSize) * 0.5f; const int startCellX = static_cast(std::floor(origin.x())); const int startCellY = static_cast(std::floor(origin.y())); @@ -613,6 +713,47 @@ namespace ESMTerrain return info; } + Terrain::LayerInfo Storage::getLandTextureLayerInfo(ESM::FormId id) + { + if (const ESM4::LandTexture* ltex = getEsm4LandTexture(id)) + { + if (!ltex->mTextureFile.empty()) + return getLayerInfo("textures/landscape/" + ltex->mTextureFile); // TES4 + if (const ESM4::TextureSet* txst = getEsm4TextureSet(ltex->mTexture)) + return getTextureSetLayerInfo(*txst); // TES5 + else + Log(Debug::Warning) << "TextureSet not found: " << ltex->mTexture.toString(); + } + else + Log(Debug::Warning) << "LandTexture not found: " << id.toString(); + return getLayerInfo(""); + } + + Terrain::LayerInfo Storage::getTextureSetLayerInfo(const ESM4::TextureSet& txst) + { + Terrain::LayerInfo info; + + assert(!txst.mDiffuse.empty() && "getlayerInfo: empty diffuse map"); + info.mDiffuseMap = "textures/" + txst.mDiffuse; + + if (!txst.mNormalMap.empty()) + info.mNormalMap = "textures/" + txst.mNormalMap; + + // FIXME: this flag indicates height info in alpha channel of normal map + // but the normal map alpha channel has specular info instead + // (probably needs some flag in the terrain shader to fix) + info.mParallax = false; + // FIXME: this flag indicates specular info in alpha channel of diffuse + // but the diffuse alpha channel has transparency data instead + // (probably needs some flag in the terrain shader to fix) + info.mSpecular = false; + + // FIXME: should support other features of ESM4::TextureSet + // probably need corresponding support in the terrain shader + + return info; + } + float Storage::getCellWorldSize(ESM::RefId worldspace) { return static_cast(ESM::getCellSize(worldspace)); @@ -623,9 +764,12 @@ namespace ESMTerrain return ESM::getLandSize(worldspace); } - int Storage::getBlendmapScale(float chunkSize) + int Storage::getTextureTileCount(float chunkSize, ESM::RefId worldspace) { - return ESM::Land::LAND_TEXTURE_SIZE * chunkSize; + if (ESM::isEsm4Ext(worldspace)) + return static_cast(2 * ESM4::Land::sQuadTexturePerSide * chunkSize); + else + return static_cast(ESM::Land::LAND_TEXTURE_SIZE * chunkSize); } } diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 402f2147ab..be3ba87751 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -13,6 +14,8 @@ namespace ESM4 { struct Land; + struct LandTexture; + struct TextureSet; } namespace ESM @@ -51,9 +54,13 @@ namespace ESMTerrain int getPlugin() const { return mData.getPlugin(); } + const Terrain::LayerInfo& getEsm4DefaultLayerInfo() const { return mEsm4DefaultLayerInfo; } + private: ESM::LandData mData; + Terrain::LayerInfo mEsm4DefaultLayerInfo; + LandObject(const LandObject& copy, const osg::CopyOp& copyOp); }; @@ -74,6 +81,11 @@ namespace ESMTerrain // Not implemented in this class, because we need different Store implementations for game and editor virtual osg::ref_ptr getLand(ESM::ExteriorCellLocation cellLocation) = 0; virtual const std::string* getLandTexture(std::uint16_t index, int plugin) = 0; + + // Not implemented in this class because requires ESMStore + virtual const ESM4::LandTexture* getEsm4LandTexture(ESM::RefId ltexId) const { return nullptr; } + virtual const ESM4::TextureSet* getEsm4TextureSet(ESM::RefId txstId) const { return nullptr; } + /// Get bounds of the whole terrain in cell units void getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace) override = 0; @@ -120,7 +132,7 @@ namespace ESMTerrain /// Get the number of vertices on one side for each cell. Should be (power of two)+1 int getCellVertices(ESM::RefId worldspace) override; - int getBlendmapScale(float chunkSize) override; + int getTextureTileCount(float chunkSize, ESM::RefId worldspace) override; float getVertexHeight(const ESM::LandData* data, int x, int y) { @@ -159,6 +171,11 @@ namespace ESMTerrain bool mAutoUseSpecularMaps; Terrain::LayerInfo getLayerInfo(const std::string& texture); + Terrain::LayerInfo getTextureSetLayerInfo(const ESM4::TextureSet& txst); + Terrain::LayerInfo getLandTextureLayerInfo(ESM::FormId id); + + void getEsm4Blendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, + std::vector& layerList, ESM::RefId worldspace); }; } diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 49fdd996a7..bef98b73f0 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -1,6 +1,7 @@ #include "configurationmanager.hpp" #include +#include #include #include diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 184c6ebb82..da4397146b 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -1,7 +1,6 @@ #ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP -#include #include #include #include diff --git a/components/misc/strings/algorithm.hpp b/components/misc/strings/algorithm.hpp index 18f72104bd..681bc0607a 100644 --- a/components/misc/strings/algorithm.hpp +++ b/components/misc/strings/algorithm.hpp @@ -4,6 +4,7 @@ #include "lower.hpp" #include +#include #include #include #include @@ -85,17 +86,17 @@ namespace Misc::StringUtils { using is_transparent = void; - constexpr std::size_t operator()(std::string_view str) const + std::size_t operator()(std::string_view str) const { // FNV-1a - std::size_t hash{ 0xcbf29ce484222325ull }; - constexpr std::size_t prime{ 0x00000100000001B3ull }; + std::uint64_t hash{ 0xcbf29ce484222325ull }; + constexpr std::uint64_t prime{ 0x00000100000001B3ull }; for (char c : str) { - hash ^= static_cast(toLower(c)); + hash ^= static_cast(toLower(c)); hash *= prime; } - return hash; + return std::hash()(hash); } }; diff --git a/components/sceneutil/depth.hpp b/components/sceneutil/depth.hpp index a9f99b145f..b9004c457f 100644 --- a/components/sceneutil/depth.hpp +++ b/components/sceneutil/depth.hpp @@ -95,13 +95,10 @@ namespace SceneUtil static void setReversed(bool reverseZ) { - static bool init = false; - - if (!init) - { + [[maybe_unused]] static const bool init = [&] { AutoDepth::sReversed = reverseZ; - init = true; - } + return true; + }(); } static bool isReversed() diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 9fa01385d5..d456795781 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include "glextensions.hpp" diff --git a/components/settings/categories/sound.hpp b/components/settings/categories/sound.hpp index 8398a38c55..99e70fcdb2 100644 --- a/components/settings/categories/sound.hpp +++ b/components/settings/categories/sound.hpp @@ -24,6 +24,7 @@ namespace Settings SettingValue mHrtfEnable{ mIndex, "Sound", "hrtf enable" }; SettingValue mHrtf{ mIndex, "Sound", "hrtf" }; SettingValue mCameraListener{ mIndex, "Sound", "camera listener" }; + SettingValue mDopplerFactor{ mIndex, "Sound", "doppler factor", makeClampSanitizerFloat(0, 1) }; }; } diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 3d7c2b1dfc..242f3e5700 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -5,6 +5,7 @@ #include +#include #include #include @@ -205,10 +206,10 @@ namespace Terrain blendmapTextures.push_back(texture); } - float blendmapScale = mStorage->getBlendmapScale(chunkSize); + float tileCount = mStorage->getTextureTileCount(chunkSize, mWorldspace); return ::Terrain::createPasses( - useShaders, mSceneManager, layers, blendmapTextures, blendmapScale, blendmapScale); + useShaders, mSceneManager, layers, blendmapTextures, tileCount, tileCount, ESM::isEsm4Ext(mWorldspace)); } osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f& chunkCenter, unsigned char lod, diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 9c3a7f589d..350b174d69 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -224,7 +224,7 @@ namespace Terrain { std::vector> createPasses(bool useShaders, Resource::SceneManager* sceneManager, const std::vector& layers, const std::vector>& blendmaps, - int blendmapScale, float layerTileSize) + int blendmapScale, float layerTileSize, bool esm4terrain) { auto& shaderManager = sceneManager->getShaderManager(); std::vector> passes; @@ -269,7 +269,8 @@ namespace Terrain osg::ref_ptr blendmap = blendmaps.at(blendmapIndex++); stateset->setTextureAttributeAndModes(1, blendmap.get()); - stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale)); + if (!esm4terrain) + stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale)); stateset->addUniform(UniformCollection::value().mBlendMap); } @@ -329,7 +330,8 @@ namespace Terrain stateset->setTextureAttributeAndModes(1, blendmap.get()); // This is to map corner vertices directly to the center of a blendmap texel. - stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale)); + if (!esm4terrain) + stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale)); stateset->setTextureAttributeAndModes(1, TexEnvCombine::value(), osg::StateAttribute::ON); } } diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp index 1dbf6d8fc8..fca14d8b3e 100644 --- a/components/terrain/material.hpp +++ b/components/terrain/material.hpp @@ -20,14 +20,13 @@ namespace Terrain { osg::ref_ptr mDiffuseMap; osg::ref_ptr mNormalMap; // optional - bool mParallax; - bool mSpecular; + bool mParallax = false; + bool mSpecular = false; }; std::vector> createPasses(bool useShaders, Resource::SceneManager* sceneManager, const std::vector& layers, const std::vector>& blendmaps, - int blendmapScale, float layerTileSize); - + int blendmapScale, float layerTileSize, bool esm4terrain = false); } #endif diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 99938d7b3a..0d7f1b7762 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -289,11 +289,12 @@ namespace Terrain , mLodFactor(lodFactor) , mVertexLodMod(vertexLodMod) , mViewDistance(std::numeric_limits::max()) - , mMinSize(ESM::isEsm4Ext(worldspace) ? 1 / 4.f : 1 / 8.f) + , mMinSize(ESM::isEsm4Ext(worldspace) ? 1 / 2.f : 1 / 8.f) , mDebugTerrainChunks(debugChunks) { mChunkManager->setCompositeMapSize(compMapResolution); - mChunkManager->setCompositeMapLevel(compMapLevel); + mChunkManager->setCompositeMapLevel( + ESM::isEsm4Ext(worldspace) ? compMapLevel * 2 /*because cells are twice smaller*/ : compMapLevel); mChunkManager->setMaxCompositeGeometrySize(maxCompGeometrySize); mChunkManagers.push_back(mChunkManager.get()); diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index b1007c13e6..38aee20b8b 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -88,7 +88,8 @@ namespace Terrain /// Get the number of vertices on one side for each cell. Should be (power of two)+1 virtual int getCellVertices(ESM::RefId worldspace) = 0; - virtual int getBlendmapScale(float chunkSize) = 0; + /// Get the number of texture tiles on one side per chunk (chunkSize 1.0 = 1 cell). + virtual int getTextureTileCount(float chunkSize, ESM::RefId worldspace) = 0; }; } diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 88fcaf667d..aff72d2d79 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -9,6 +9,8 @@ #include "heightcull.hpp" #include "storage.hpp" #include "view.hpp" + +#include #include namespace Terrain @@ -27,13 +29,13 @@ namespace Terrain unsigned int borderMask) : Terrain::World( parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask, worldspace, expiryDelay) - , mNumSplits(4) + , mNumSplits(ESM::isEsm4Ext(worldspace) ? 2 : 4) { } TerrainGrid::TerrainGrid(osg::Group* parent, Storage* storage, ESM::RefId worldspace, unsigned int nodeMask) : Terrain::World(parent, storage, nodeMask, worldspace) - , mNumSplits(4) + , mNumSplits(ESM::isEsm4Ext(worldspace) ? 2 : 4) { } diff --git a/docs/source/reference/lua-scripting/events.rst b/docs/source/reference/lua-scripting/events.rst index 590ff14d88..810739caeb 100644 --- a/docs/source/reference/lua-scripting/events.rst +++ b/docs/source/reference/lua-scripting/events.rst @@ -107,8 +107,8 @@ Example: local attack = { attacker = self, weapon = Actor.getEquipment(self, Actor.EQUIPMENT_SLOT.CarriedRight), - sourceType = I.Combat.ATTACK_SOURCE_TYPE.Melee, - strenght = 1, + sourceType = I.Combat.ATTACK_SOURCE_TYPES.Melee, + strength = 1, type = self.ATTACK_TYPE.Chop, damage = { health = 20, diff --git a/docs/source/reference/lua-scripting/index_interfaces.rst b/docs/source/reference/lua-scripting/index_interfaces.rst index 37a2df63c4..0b113c7744 100644 --- a/docs/source/reference/lua-scripting/index_interfaces.rst +++ b/docs/source/reference/lua-scripting/index_interfaces.rst @@ -11,6 +11,7 @@ Interfaces AI AnimationController Camera + Combat Controls Crimes GamepadControls diff --git a/docs/source/reference/modding/settings/sound.rst b/docs/source/reference/modding/settings/sound.rst index e38cd9de78..4e73df989c 100644 --- a/docs/source/reference/modding/settings/sound.rst +++ b/docs/source/reference/modding/settings/sound.rst @@ -121,3 +121,13 @@ Sound Settings When true, uses the camera position and direction for audio instead of the player position. This makes audio in third person sound relative to camera instead of the player. False is vanilla Morrowind behaviour. + +.. omw-setting:: + :title: doppler factor + :type: float32 + :range: 0.0 (disabled), 1.0 (maximum strength) + :default: 0.25 + :location: :bdg-success:`Launcher > Settings > Audio` + + This setting controls the strength of the Doppler effect. The Doppler effect increases or decreases the pitch of sounds + relative to the velocity of the sound source and the listener. diff --git a/files/data/mygui/openmw_spellcreation_dialog.layout b/files/data/mygui/openmw_spellcreation_dialog.layout index c284927a37..d616e00dcb 100644 --- a/files/data/mygui/openmw_spellcreation_dialog.layout +++ b/files/data/mygui/openmw_spellcreation_dialog.layout @@ -68,6 +68,12 @@ + + + + + + diff --git a/files/data/mygui/openmw_trainingwindow.layout b/files/data/mygui/openmw_trainingwindow.layout index 80526068ce..f3cadcaf46 100644 --- a/files/data/mygui/openmw_trainingwindow.layout +++ b/files/data/mygui/openmw_trainingwindow.layout @@ -1,28 +1,28 @@ - + - + - + - + - + - + - + diff --git a/files/data/scripts/omw/mechanics/playercontroller.lua b/files/data/scripts/omw/mechanics/playercontroller.lua index 6de31afdea..a0d1b11362 100644 --- a/files/data/scripts/omw/mechanics/playercontroller.lua +++ b/files/data/scripts/omw/mechanics/playercontroller.lua @@ -39,7 +39,8 @@ end local function skillLevelUpHandler(skillid, source, params) local skillStat = NPC.stats.skills[skillid](self) - if skillStat.base >= 100 then + if (skillStat.base >= 100 and params.skillIncreaseValue > 0) or + (skillStat.base <= 0 and params.skillIncreaseValue < 0) then return false end @@ -62,25 +63,67 @@ local function skillLevelUpHandler(skillid, source, params) = levelStat.skillIncreasesForSpecialization[params.levelUpSpecialization] + params.levelUpSpecializationIncreaseValue; end - local skillRecord = Skill.record(skillid) - local npcRecord = NPC.record(self) - local class = NPC.classes.record(npcRecord.class) + if source ~= 'jail' then + local skillRecord = Skill.record(skillid) + local npcRecord = NPC.record(self) + local class = NPC.classes.record(npcRecord.class) - ambient.playSound("skillraise") + ambient.playSound("skillraise") - local message = string.format(core.getGMST('sNotifyMessage39'),skillRecord.name,skillStat.base) + local message = string.format(core.getGMST('sNotifyMessage39'),skillRecord.name,skillStat.base) - if source == I.SkillProgression.SKILL_INCREASE_SOURCES.Book then - message = '#{sBookSkillMessage}\n'..message + if source == I.SkillProgression.SKILL_INCREASE_SOURCES.Book then + message = '#{sBookSkillMessage}\n'..message + end + + ui.showMessage(message, { showInDialogue = false }) + + if levelStat.progress >= core.getGMST('iLevelUpTotal') then + ui.showMessage('#{sLevelUpMsg}', { showInDialogue = false }) + end + + if not source or source == I.SkillProgression.SKILL_INCREASE_SOURCES.Usage then skillStat.progress = 0 end end +end - ui.showMessage(message, { showInDialogue = false }) +local function jailTimeServed(days) + if not days or days <= 0 then + return + end - if levelStat.progress >= core.getGMST('iLevelUpTotal') then - ui.showMessage('#{sLevelUpMsg}', { showInDialogue = false }) + local oldSkillLevels = {} + local skillByNumber = {} + for skillid, skillStat in pairs(NPC.stats.skills) do + oldSkillLevels[skillid] = skillStat(self).base + skillByNumber[#skillByNumber+1] = skillid + end + + math.randomseed(core.getSimulationTime()) + for day=1,days do + local skillid = skillByNumber[math.random(#skillByNumber)] + -- skillLevelUp() handles skill-based increase/decrease + I.SkillProgression.skillLevelUp(skillid, I.SkillProgression.SKILL_INCREASE_SOURCES.Jail) end - if not source or source == I.SkillProgression.SKILL_INCREASE_SOURCES.Usage then skillStat.progress = 0 end + local message = '' + if days == 1 then + message = string.format(core.getGMST('sNotifyMessage42'), days) + else + message = string.format(core.getGMST('sNotifyMessage43'), days) + end + for skillid, skillStat in pairs(NPC.stats.skills) do + local diff = skillStat(self).base - oldSkillLevels[skillid] + if diff ~= 0 then + local skillMsg = core.getGMST('sNotifyMessage39') + if diff < 0 then + skillMsg = core.getGMST('sNotifyMessage44') + end + local skillRecord = Skill.record(skillid) + message = message..'\n'..string.format(skillMsg, skillRecord.name, skillStat(self).base) + end + end + + I.UI.showInteractiveMessage(message) end local function skillUsedHandler(skillid, params) @@ -114,6 +157,7 @@ I.SkillProgression.addSkillLevelUpHandler(skillLevelUpHandler) return { engineHandlers = { onUpdate = onUpdate, + _onJailTimeServed = jailTimeServed, }, eventHandlers = { diff --git a/files/data/scripts/omw/skillhandlers.lua b/files/data/scripts/omw/skillhandlers.lua index d3a224dc46..9b58d81174 100644 --- a/files/data/scripts/omw/skillhandlers.lua +++ b/files/data/scripts/omw/skillhandlers.lua @@ -38,6 +38,7 @@ local Skill = core.stats.Skill -- Table of all existing sources for skill increases. Any sources not listed below will be treated as equal to Trainer. -- @type SkillLevelUpSource -- @field #string Book book +-- @field #string Jail jail -- @field #string Trainer trainer -- @field #string Usage usage @@ -131,15 +132,17 @@ local function skillLevelUp(skillid, source) levelUpAttributeIncreaseValue = core.getGMST('iLevelUpMajorMultAttribute') end - local options = - { - skillIncreaseValue = 1, - levelUpProgress = levelUpProgress, - levelUpAttribute = skillRecord.attribute, - levelUpAttributeIncreaseValue = levelUpAttributeIncreaseValue, - levelUpSpecialization = skillRecord.specialization, - levelUpSpecializationIncreaseValue = core.getGMST('iLevelupSpecialization'), - } + local options = {} + if source == 'jail' and not (skillid == 'security' or skillid == 'sneak') then + options.skillIncreaseValue = -1 + else + options.skillIncreaseValue = 1 + options.levelUpProgress = levelUpProgress + options.levelUpAttribute = skillRecord.attribute + options.levelUpAttributeIncreaseValue = levelUpAttributeIncreaseValue + options.levelUpSpecialization = skillRecord.specialization + options.levelUpSpecializationIncreaseValue = core.getGMST('iLevelupSpecialization') + end for i = #skillLevelUpHandlers, 1, -1 do if skillLevelUpHandlers[i](skillid, source, options) == false then @@ -156,8 +159,15 @@ return { -- @context local -- @usage local I = require('openmw.interfaces') -- + -- -- Make jail time hurt sneak skill instead of benefitting it + -- I.SkillProgression.addSkillLevelUpHandler(function(skillid, source, options) + -- if skillid == 'sneak' and source == 'jail' and options.skillIncreaseValue > 0 then + -- options.skillIncreaseValue = -options.skillIncreaseValue + -- end + -- end) + -- -- -- Forbid increasing destruction skill past 50 - -- I.SkillProgression.addSkillLevelUpHandler(function(skillid, options) + -- I.SkillProgression.addSkillLevelUpHandler(function(skillid, source, options) -- if skillid == 'destruction' and types.NPC.stats.skills.destruction(self).base >= 50 then -- return false -- end @@ -187,7 +197,7 @@ return { -- a modifiable table of skill level up values, and can be modified to change the behavior of later handlers. -- These values are calculated based on vanilla mechanics. Setting any value to nil will cause that mechanic to be skipped. By default contains these values: -- - -- * `skillIncreaseValue` - The numeric amount of skill levels gained. + -- * `skillIncreaseValue` - The numeric amount of skill levels gained. By default this is 1, except when the source is jail in which case it will instead be -1 for all skills except sneak and security. -- * `levelUpProgress` - The numeric amount of level up progress gained. -- * `levelUpAttribute` - The string identifying the attribute that should receive points from this skill level up. -- * `levelUpAttributeIncreaseValue` - The numeric amount of attribute increase points received. This contributes to the amount of each attribute the character receives during a vanilla level up. @@ -263,7 +273,7 @@ return { --- Trigger a skill level up, activating relevant handlers -- @function [parent=#SkillProgression] skillLevelUp -- @param #string skillid The id of the skill to level up. - -- @param #SkillLevelUpSource source The source of the skill increase. + -- @param #SkillLevelUpSource source The source of the skill increase. Note that passing a value of @{#SkillLevelUpSource.Jail} will cause a skill decrease for all skills except sneak and security. skillLevelUp = skillLevelUp, --- @{#SkillLevelUpSource} @@ -272,6 +282,7 @@ return { Book = 'book', Usage = 'usage', Trainer = 'trainer', + Jail = 'jail', }, --- Compute the total skill gain required to level up a skill based on its current level, and other modifying factors such as major skills and specialization. diff --git a/files/data/scripts/omw/ui.lua b/files/data/scripts/omw/ui.lua index 31502fd6ee..c63c085fab 100644 --- a/files/data/scripts/omw/ui.lua +++ b/files/data/scripts/omw/ui.lua @@ -171,7 +171,7 @@ return { interface = { --- Interface version -- @field [parent=#UI] #number version - version = 2, + version = 3, --- All available UI modes. -- Use `view(I.UI.MODE)` in `luap` console mode to see the list. @@ -254,6 +254,13 @@ return { -- @return #boolean isWindowVisible = isWindowVisible, + --- + -- Shows a message as an interactive message box pausing the game, with a single button with the localized text OK. + -- @function [parent=#UI] showInteractiveMessage + -- @param #string message Message to display + -- @param #table options Options (none yet) + showInteractiveMessage = ui._showInteractiveMessage + -- TODO -- registerHudElement = function(name, showFn, hideFn) end, -- showHudElement = function(name, bool) end, diff --git a/files/lang/launcher_de.ts b/files/lang/launcher_de.ts index 80e44d7036..83d52c473d 100644 --- a/files/lang/launcher_de.ts +++ b/files/lang/launcher_de.ts @@ -1451,5 +1451,13 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Run Script After Startup: + + <html><head/><body><p>Controls the strength of the Doppler effect. Zero means it is completely disabled.</p><p>The Doppler effect increases or decreases the pitch of sounds relative to the velocity of the sound source and the listener.</p></body></html> + + + + Doppler Factor + + diff --git a/files/lang/launcher_en.ts b/files/lang/launcher_en.ts index 5b14aa0ade..4c3bd2819d 100644 --- a/files/lang/launcher_en.ts +++ b/files/lang/launcher_en.ts @@ -1451,5 +1451,13 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov <html><head/><body><p>If enabled - makes transitions between different animations/poses much smoother. Also allows to load animation blending config YAML files that can be bundled with animations in order to customise blending styles.</p></body></html> + + <html><head/><body><p>Controls the strength of the Doppler effect. Zero means it is completely disabled.</p><p>The Doppler effect increases or decreases the pitch of sounds relative to the velocity of the sound source and the listener.</p></body></html> + + + + Doppler Factor + + diff --git a/files/lang/launcher_fr.ts b/files/lang/launcher_fr.ts index ad779ac896..2f4efe8161 100644 --- a/files/lang/launcher_fr.ts +++ b/files/lang/launcher_fr.ts @@ -1454,5 +1454,13 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Run Script After Startup: Script à lancer après démarrage : + + <html><head/><body><p>Controls the strength of the Doppler effect. Zero means it is completely disabled.</p><p>The Doppler effect increases or decreases the pitch of sounds relative to the velocity of the sound source and the listener.</p></body></html> + + + + Doppler Factor + + diff --git a/files/lang/launcher_ru.ts b/files/lang/launcher_ru.ts index 57789902f7..bd7e05d128 100644 --- a/files/lang/launcher_ru.ts +++ b/files/lang/launcher_ru.ts @@ -1466,5 +1466,13 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Use the Camera as the Sound Listener Использовать камеру как слушателя + + <html><head/><body><p>Controls the strength of the Doppler effect. Zero means it is completely disabled.</p><p>The Doppler effect increases or decreases the pitch of sounds relative to the velocity of the sound source and the listener.</p></body></html> + <html><head/><body><p>Определяет силу эффекта Доплера. Нулевое значение означает, что эффект отключен полностью.</p><p>Эффект Доплера увеличивает или уменьшает высоту звуков в зависимости от скорости источника звука и слушателя.</p></body></html> + + + Doppler Factor + Множитель эффекта Доплера + diff --git a/files/lang/launcher_sv.ts b/files/lang/launcher_sv.ts index e1903889b7..43bb4ce6e1 100644 --- a/files/lang/launcher_sv.ts +++ b/files/lang/launcher_sv.ts @@ -1470,5 +1470,13 @@ de ordinarie fonterna i Morrowind. Bocka denna ruta om du ändå föredrar ordin <html><head/><body><p>If enabled - makes transitions between different animations/poses much smoother. Also allows to load animation blending config YAML files that can be bundled with animations in order to customise blending styles.</p></body></html> <html><head/><body><p>Vid aktivering gör denna funktion att övergångarna mellan olika animationer och poser blir mycket mjukare. Funktionen gör det också möjligt att konfigurera animationsövergångarna i YAML-filer. Dessa filer kan buntas ihop tillsammans med nya animationsfiler.</p></body></html> + + <html><head/><body><p>Controls the strength of the Doppler effect. Zero means it is completely disabled.</p><p>The Doppler effect increases or decreases the pitch of sounds relative to the velocity of the sound source and the listener.</p></body></html> + <html><head/><body><p>Kontrollerar styrkan på dopplereffekten. Noll innebär helt inaktiverat.</p><p>Dopplereffekten höjer eller sänker tonhöjden på ljud i förhållande till ljudkällans hastighet och lyssnaren.</p></body></html> + + + Doppler Factor + Dopplerfaktor + diff --git a/files/lua_api/openmw/ambient.lua b/files/lua_api/openmw/ambient.lua index 436f10de1a..a9e8c08d4a 100644 --- a/files/lua_api/openmw/ambient.lua +++ b/files/lua_api/openmw/ambient.lua @@ -18,7 +18,7 @@ -- * `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 the sound should be repeated when it ends (default: false); -- @usage local params = { --- timeOffset=0.1 +-- timeOffset=0.1, -- volume=0.3, -- scale=false, -- pitch=1.0, @@ -38,7 +38,7 @@ -- * `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 the sound should be repeated when it ends (default: false); -- @usage local params = { --- timeOffset=0.1 +-- timeOffset=0.1, -- volume=0.3, -- scale=false, -- pitch=1.0, diff --git a/files/lua_api/openmw/animation.lua b/files/lua_api/openmw/animation.lua index 929a784ba9..9fcbe8ea21 100644 --- a/files/lua_api/openmw/animation.lua +++ b/files/lua_api/openmw/animation.lua @@ -188,7 +188,7 @@ -- -- * `loops` - a number >= 0, the number of times the animation should loop after the first play (default: 0). -- * `priority` - Either a single #Priority value that will be assigned to all bone groups. Or a table mapping bone groups to its priority (default: PRIORITY.Default). --- * `blendMask` - A mask of which bone groups to include in the animation (Default: BLEND_MASK.All. +-- * `blendMask` - A mask of which bone groups to include in the animation (Default: BLEND_MASK.All). -- * `autoDisable` - If true, the animation will be immediately removed upon finishing, which means information will not be possible to query once completed. (Default: true) -- * `speed` - a floating point number >= 0, the speed at which the animation should play (default: 1) -- * `startKey` - the animation key at which the animation should start (default: "start") @@ -242,7 +242,7 @@ -- model = types.Static.record(mgef.hitStatic).model, -- options = { -- vfxId = mgef.id, --- particuleTextureOverride = mgef.particle, +-- particleTextureOverride = mgef.particle, -- loop = false, -- } -- }) @@ -254,7 +254,7 @@ -- Can only be used on self. -- @function [parent=#animation] removeVfx -- @param openmw.core#GameObject actor --- @param #number vfxId an integer ID that uniquely identifies the VFX to remove +-- @param #string vfxId a string ID that uniquely identifies the VFX to remove --- -- Removes all vfx from the actor. diff --git a/files/lua_api/openmw/camera.lua b/files/lua_api/openmw/camera.lua index b6b605fc10..67b3ec2a8e 100644 --- a/files/lua_api/openmw/camera.lua +++ b/files/lua_api/openmw/camera.lua @@ -109,7 +109,6 @@ --- -- Additional summand for the yaw angle; useful for camera shaking effects. --- Setting extra pitch doesn't block player input. -- Full yaw is `getYaw()+getExtraYaw()`. -- @function [parent=#camera] setExtraYaw -- @param #number value @@ -122,7 +121,7 @@ --- -- Additional summand for the roll angle; useful for camera shaking effects. --- Full yaw is `getRoll()+getExtraRoll()`. +-- Full roll is `getRoll()+getExtraRoll()`. -- @function [parent=#camera] setExtraRoll -- @param #number value @@ -150,7 +149,7 @@ --- -- Set preferred offset between tracked position (see `getTrackedPosition`) and the camera focal point (the center of the screen) in third person mode. -- The offset is a 2d vector (X, Y) where X is horizontal (to the right from the character) and Y component is vertical (upward). --- The real offset can differ from the preferred one during smooth transition of if blocked by an obstacle. +-- The real offset can differ from the preferred one during smooth transition or if blocked by an obstacle. -- Smooth transition happens by default every time when the preferred offset was changed. Use `instantTransition()` to skip the current transition. -- @function [parent=#camera] setFocalPreferredOffset -- @param openmw.util#Vector2 offset diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 054e96674b..c1b888bd43 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -801,7 +801,7 @@ -- * `pitch` - a floating point number >= 0, to set a sound pitch (default: 1); -- * `loop` - a boolean, to set if sound should be repeated when it ends (default: false); -- @usage local params = { --- timeOffset=0.1 +-- timeOffset=0.1, -- volume=0.3, -- loop=false, -- pitch=1.0 @@ -822,7 +822,7 @@ -- * `pitch` - a floating point number >= 0, to set a sound pitch (default: 1); -- * `loop` - a boolean, to set if sound should be repeated when it ends (default: false); -- @usage local params = { --- timeOffset=0.1 +-- timeOffset=0.1, -- volume=0.3, -- loop=false, -- pitch=1.0 diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index b2711c895b..6d51987ac7 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -1004,7 +1004,7 @@ -- @param openmw.core#GameObject actor NPC object -- @param #string faction Faction ID -- @usage local NPC = require('openmw.types').NPC; --- NPC.clearExpell(player, "mages guild"); +-- NPC.clearExpelled(player, "mages guild"); --- -- Check if NPC is expelled from given faction. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 76be5bfc6b..c33f02ad35 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -624,6 +624,9 @@ hrtf = # Specifies whether to use camera as audio listener camera listener = false +# Specifies strength of doppler effect +doppler factor = 0.25 + [Video] # Resolution of the OpenMW window or screen.