diff --git a/CI/github.env b/CI/github.env index d8ca8b429f..b793834a8e 100644 --- a/CI/github.env +++ b/CI/github.env @@ -1 +1 @@ -VCPKG_DEPS_TAG=2024-11-10 +VCPKG_DEPS_TAG=2025-07-23 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6103ddcb33..78d9aae646 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 82) +set(OPENMW_LUA_API_REVISION 83) set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_VERSION_COMMITHASH "") diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 4dc2fd1fd1..2a5ad2d18d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -63,7 +63,7 @@ add_openmw_dir (mwlua context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings coremwscriptbindings mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings dialoguebindings postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker landbindings magicbindings factionbindings - classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings + classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings weatherbindings types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 244c458f46..0ea8451774 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -781,7 +781,6 @@ void OMW::Engine::prepareEngine() const auto userdefault = mCfgMgr.getUserConfigPath() / "gamecontrollerdb.txt"; const auto localdefault = mCfgMgr.getLocalPath() / "gamecontrollerdb.txt"; - const auto globaldefault = mCfgMgr.getGlobalPath() / "gamecontrollerdb.txt"; std::filesystem::path userGameControllerdb; if (std::filesystem::exists(userdefault)) @@ -790,9 +789,13 @@ void OMW::Engine::prepareEngine() std::filesystem::path gameControllerdb; if (std::filesystem::exists(localdefault)) gameControllerdb = localdefault; - else if (std::filesystem::exists(globaldefault)) - gameControllerdb = globaldefault; - // else if it doesn't exist, pass in an empty string + else if (!mCfgMgr.getGlobalPath().empty()) + { + const auto globaldefault = mCfgMgr.getGlobalPath() / "gamecontrollerdb.txt"; + if (std::filesystem::exists(globaldefault)) + gameControllerdb = globaldefault; + } + // else if it doesn't exist, pass in an empty path // gui needs our shaders path before everything else mResourceSystem->getSceneManager()->setShaderPath(mResDir / "shaders"); diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f268ed0e52..956a038f11 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -22,6 +22,7 @@ namespace osg { class Vec3f; + class Vec4f; class Matrixf; class Quat; class Image; @@ -93,6 +94,7 @@ namespace MWWorld class RefData; class Cell; class DateTimeManager; + class Weather; typedef std::vector> PtrMovementList; } @@ -216,9 +218,21 @@ namespace MWBase virtual void changeWeather(const ESM::RefId& region, const unsigned int id) = 0; - virtual int getCurrentWeather() const = 0; + virtual void changeWeather(const ESM::RefId& region, const ESM::RefId& id) = 0; - virtual int getNextWeather() const = 0; + virtual const std::vector& getAllWeather() const = 0; + + virtual int getCurrentWeatherScriptId() const = 0; + + virtual const MWWorld::Weather& getCurrentWeather() const = 0; + + virtual const MWWorld::Weather* getWeather(size_t index) const = 0; + + virtual const MWWorld::Weather* getWeather(const ESM::RefId& id) const = 0; + + virtual int getNextWeatherScriptId() const = 0; + + virtual const MWWorld::Weather* getNextWeather() const = 0; virtual float getWeatherTransition() const = 0; @@ -478,6 +492,7 @@ namespace MWBase // Allow NPCs to use torches? virtual bool useTorches() const = 0; + virtual const osg::Vec4f& getSunLightPosition() const = 0; virtual float getSunVisibility() const = 0; virtual float getSunPercentage() const = 0; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index b7d4a1361c..a5a3be85f9 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -501,7 +501,7 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons case ESM::DialogueCondition::Function_Weather: - return MWBase::Environment::get().getWorld()->getCurrentWeather(); + return MWBase::Environment::get().getWorld()->getCurrentWeatherScriptId(); case ESM::DialogueCondition::Function_Reputation: if (!mActor.getClass().isNpc()) diff --git a/apps/openmw/mwlua/corebindings.cpp b/apps/openmw/mwlua/corebindings.cpp index b85c14578c..8eb90b48fe 100644 --- a/apps/openmw/mwlua/corebindings.cpp +++ b/apps/openmw/mwlua/corebindings.cpp @@ -27,6 +27,7 @@ #include "magicbindings.hpp" #include "soundbindings.hpp" #include "stats.hpp" +#include "weatherbindings.hpp" namespace MWLua { @@ -104,6 +105,9 @@ namespace MWLua api["land"] = context.cachePackage("openmw_core_land", [context]() { return initCoreLandBindings(context); }); + api["weather"] + = context.cachePackage("openmw_core_weather", [context]() { return initCoreWeatherBindings(context); }); + api["factions"] = context.cachePackage("openmw_core_factions", [context]() { return initCoreFactionBindings(context); }); api["dialogue"] diff --git a/apps/openmw/mwlua/weatherbindings.cpp b/apps/openmw/mwlua/weatherbindings.cpp new file mode 100644 index 0000000000..daabd08620 --- /dev/null +++ b/apps/openmw/mwlua/weatherbindings.cpp @@ -0,0 +1,185 @@ +#include "weatherbindings.hpp" + +#include + +#include +#include +#include +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/weather.hpp" + +#include "context.hpp" + +namespace +{ + class WeatherStore + { + public: + const MWWorld::Weather* get(size_t index) const + { + return MWBase::Environment::get().getWorld()->getWeather(index); + } + const MWWorld::Weather* get(const ESM::RefId& id) const + { + return MWBase::Environment::get().getWorld()->getWeather(id); + } + size_t size() const { return MWBase::Environment::get().getWorld()->getAllWeather().size(); } + }; + + Misc::Color color(const osg::Vec4f& color) + { + return Misc::Color(color.r(), color.g(), color.b(), color.a()); + } +} + +namespace MWLua +{ + sol::table initCoreWeatherBindings(const Context& context) + { + sol::state_view lua = context.sol(); + sol::table api(lua, sol::create); + + auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); + auto weatherT = lua.new_usertype("Weather"); + weatherT[sol::meta_function::to_string] + = [](const MWWorld::Weather& w) -> std::string { return "Weather[" + w.mName + "]"; }; + weatherT["name"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mName; }); + weatherT["windSpeed"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mWindSpeed; }); + weatherT["cloudSpeed"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mCloudSpeed; }); + weatherT["cloudTexture"] = sol::readonly_property([vfs](const MWWorld::Weather& w) { + return Misc::ResourceHelpers::correctTexturePath(w.mCloudTexture, vfs); + }); + weatherT["cloudsMaximumPercent"] + = sol::readonly_property([](const MWWorld::Weather& w) { return w.mCloudsMaximumPercent; }); + weatherT["isStorm"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mIsStorm; }); + weatherT["stormDirection"] + = sol::readonly_property([](const MWWorld::Weather& w) { return w.mStormDirection; }); + weatherT["glareView"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mGlareView; }); + weatherT["rainSpeed"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mRainSpeed; }); + weatherT["rainEntranceSpeed"] + = sol::readonly_property([](const MWWorld::Weather& w) { return w.mRainEntranceSpeed; }); + weatherT["rainEffect"] = sol::readonly_property([](const MWWorld::Weather& w) -> sol::optional { + if (w.mRainEffect.empty()) + return sol::nullopt; + return w.mRainEffect; + }); + weatherT["rainMaxRaindrops"] + = sol::readonly_property([](const MWWorld::Weather& w) { return w.mRainMaxRaindrops; }); + weatherT["rainDiameter"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mRainDiameter; }); + weatherT["rainThreshold"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mRainThreshold; }); + weatherT["rainMaxHeight"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mRainMaxHeight; }); + weatherT["rainMinHeight"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mRainMinHeight; }); + weatherT["rainLoopSoundID"] + = sol::readonly_property([](const MWWorld::Weather& w) { return w.mRainLoopSoundID.serializeText(); }); + weatherT["thunderSoundID"] = sol::readonly_property([lua](const MWWorld::Weather& w) { + sol::table result(lua, sol::create); + for (const auto& soundId : w.mThunderSoundID) + result.add(soundId.serializeText()); + return result; + }); + weatherT["sunDiscSunsetColor"] + = sol::readonly_property([](const MWWorld::Weather& w) { return color(w.mSunDiscSunsetColor); }); + weatherT["ambientLoopSoundID"] + = sol::readonly_property([](const MWWorld::Weather& w) { return w.mAmbientLoopSoundID.serializeText(); }); + weatherT["ambientColor"] = sol::readonly_property([lua](const MWWorld::Weather& w) { + sol::table result(lua, sol::create); + result["sunrise"] = color(w.mAmbientColor.getSunriseValue()); + result["day"] = color(w.mAmbientColor.getDayValue()); + result["sunset"] = color(w.mAmbientColor.getSunsetValue()); + result["night"] = color(w.mAmbientColor.getNightValue()); + return result; + }); + weatherT["fogColor"] = sol::readonly_property([lua](const MWWorld::Weather& w) { + sol::table result(lua, sol::create); + result["sunrise"] = color(w.mFogColor.getSunriseValue()); + result["day"] = color(w.mFogColor.getDayValue()); + result["sunset"] = color(w.mFogColor.getSunsetValue()); + result["night"] = color(w.mFogColor.getNightValue()); + return result; + }); + weatherT["skyColor"] = sol::readonly_property([lua](const MWWorld::Weather& w) { + sol::table result(lua, sol::create); + result["sunrise"] = color(w.mSkyColor.getSunriseValue()); + result["day"] = color(w.mSkyColor.getDayValue()); + result["sunset"] = color(w.mSkyColor.getSunsetValue()); + result["night"] = color(w.mSkyColor.getNightValue()); + return result; + }); + weatherT["sunColor"] = sol::readonly_property([lua](const MWWorld::Weather& w) { + sol::table result(lua, sol::create); + result["sunrise"] = color(w.mSunColor.getSunriseValue()); + result["day"] = color(w.mSunColor.getDayValue()); + result["sunset"] = color(w.mSunColor.getSunsetValue()); + result["night"] = color(w.mSunColor.getNightValue()); + return result; + }); + weatherT["landFogDepth"] = sol::readonly_property([lua](const MWWorld::Weather& w) { + sol::table result(lua, sol::create); + result["sunrise"] = w.mLandFogDepth.getSunriseValue(); + result["day"] = w.mLandFogDepth.getDayValue(); + result["sunset"] = w.mLandFogDepth.getSunsetValue(); + result["night"] = w.mLandFogDepth.getNightValue(); + return result; + }); + weatherT["particleEffect"] + = sol::readonly_property([](const MWWorld::Weather& w) -> sol::optional { + if (w.mParticleEffect.empty()) + return sol::nullopt; + return w.mParticleEffect; + }); + weatherT["distantLandFogFactor"] + = sol::readonly_property([](const MWWorld::Weather& w) { return w.mDL.FogFactor; }); + weatherT["distantLandFogOffset"] + = sol::readonly_property([](const MWWorld::Weather& w) { return w.mDL.FogOffset; }); + weatherT["scriptId"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mScriptId; }); + weatherT["recordId"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mId.serializeText(); }); + + api["getCurrent"] = []() { return MWBase::Environment::get().getWorld()->getCurrentWeather(); }; + api["getNext"] + = []() -> const MWWorld::Weather* { return MWBase::Environment::get().getWorld()->getNextWeather(); }; + api["getTransition"] = []() { return MWBase::Environment::get().getWorld()->getWeatherTransition(); }; + + api["changeWeather"] = [](std::string_view regionId, const MWWorld::Weather& weather) { + ESM::RefId region = ESM::RefId::deserializeText(regionId); + MWBase::Environment::get().getESMStore()->get().find(region); + MWBase::Environment::get().getWorld()->changeWeather(region, weather.mId); + }; + + sol::usertype storeT = lua.new_usertype("WeatherWorldStore"); + storeT[sol::meta_function::to_string] + = [](const WeatherStore& store) { return "{" + std::to_string(store.size()) + " Weather records}"; }; + storeT[sol::meta_function::length] = [](const WeatherStore& store) { return store.size(); }; + storeT[sol::meta_function::index] = sol::overload( + [](const WeatherStore& store, size_t index) -> const MWWorld::Weather* { + return store.get(LuaUtil::fromLuaIndex(index)); + }, + [](const WeatherStore& store, std::string_view id) -> const MWWorld::Weather* { + return store.get(ESM::RefId::deserializeText(id)); + }); + storeT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); + storeT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); + + // Provide access to the store. + api["records"] = WeatherStore{}; + + api["getCurrentSunLightDirection"] = []() { + osg::Vec4f sunPos = MWBase::Environment::get().getWorld()->getSunLightPosition(); + // normalize to get the direction towards the sun + sunPos.normalize(); + + // and invert it to get the direction of the sun light + return -sunPos; + }; + api["getCurrentSunVisibility"] = []() { return MWBase::Environment::get().getWorld()->getSunVisibility(); }; + api["getCurrentSunPercentage"] = []() { return MWBase::Environment::get().getWorld()->getSunPercentage(); }; + api["getCurrentWindSpeed"] = []() { return MWBase::Environment::get().getWorld()->getWindSpeed(); }; + api["getCurrentStormDirection"] = []() { return MWBase::Environment::get().getWorld()->getStormDirection(); }; + + return LuaUtil::makeReadOnly(api); + } +} diff --git a/apps/openmw/mwlua/weatherbindings.hpp b/apps/openmw/mwlua/weatherbindings.hpp new file mode 100644 index 0000000000..c4686602ff --- /dev/null +++ b/apps/openmw/mwlua/weatherbindings.hpp @@ -0,0 +1,14 @@ +#ifndef MWLUA_WEATHERBINDINGS_H +#define MWLUA_WEATHERBINDINGS_H + +#include + +namespace MWLua +{ + struct Context; + + sol::table initCoreWeatherBindings(const Context&); + +} + +#endif // MWLUA_WEATHERBINDINGS_H diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 6afbfcfe7d..0698e8c4ae 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -943,8 +943,8 @@ namespace MWRender stateUpdater->setIsUnderwater(isUnderwater); stateUpdater->setFogColor(fogColor); stateUpdater->setGameHour(world->getTimeStamp().getHour()); - stateUpdater->setWeatherId(world->getCurrentWeather()); - stateUpdater->setNextWeatherId(world->getNextWeather()); + stateUpdater->setWeatherId(world->getCurrentWeatherScriptId()); + stateUpdater->setNextWeatherId(world->getNextWeatherScriptId()); stateUpdater->setWeatherTransition(world->getWeatherTransition()); stateUpdater->setWindSpeed(world->getWindSpeed()); stateUpdater->setSkyColor(mSky->getSkyColor()); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 2e573f8276..4723c59b8a 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -139,6 +139,7 @@ namespace MWRender int skyGetSecundaPhase() const; void skySetMoonColour(bool red); + const osg::Vec4f& getSunLightPosition() const { return mSunLight->getPosition(); } void setSunDirection(const osg::Vec3f& direction); void setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular, float sunVis); void setNight(bool isNight) { mNight = isNight; } diff --git a/apps/openmw/mwscript/skyextensions.cpp b/apps/openmw/mwscript/skyextensions.cpp index d2b41fb87a..b354bb5ce2 100644 --- a/apps/openmw/mwscript/skyextensions.cpp +++ b/apps/openmw/mwscript/skyextensions.cpp @@ -71,7 +71,7 @@ namespace MWScript public: void execute(Interpreter::Runtime& runtime) override { - runtime.push(MWBase::Environment::get().getWorld()->getCurrentWeather()); + runtime.push(MWBase::Environment::get().getWorld()->getCurrentWeatherScriptId()); } }; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 2ee77458d4..f2cffb5611 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -1,5 +1,6 @@ #include "weather.hpp" +#include #include #include @@ -141,9 +142,12 @@ namespace MWWorld return direction; } - Weather::Weather(const std::string& name, float stormWindSpeed, float rainSpeed, float dlFactor, float dlOffset, - const std::string& particleEffect) - : mCloudTexture(Fallback::Map::getString("Weather_" + name + "_Cloud_Texture")) + Weather::Weather(ESM::RefId id, int scriptId, const std::string& name, float stormWindSpeed, float rainSpeed, + float dlFactor, float dlOffset, const std::string& particleEffect) + : mId(id) + , mScriptId(scriptId) + , mName(name) + , mCloudTexture(Fallback::Map::getString("Weather_" + name + "_Cloud_Texture")) , mSkyColor(Fallback::Map::getColour("Weather_" + name + "_Sky_Sunrise_Color"), Fallback::Map::getColour("Weather_" + name + "_Sky_Day_Color"), Fallback::Map::getColour("Weather_" + name + "_Sky_Sunset_Color"), @@ -676,6 +680,41 @@ namespace MWWorld stopSounds(); } + const Weather* WeatherManager::getWeather(size_t index) const + { + if (index < mWeatherSettings.size()) + return &mWeatherSettings[index]; + + return nullptr; + } + + const Weather* WeatherManager::getWeather(const ESM::RefId& id) const + { + auto it = std::find_if( + mWeatherSettings.begin(), mWeatherSettings.end(), [id](const auto& weather) { return weather.mId == id; }); + + if (it != mWeatherSettings.end()) + return &*it; + + return nullptr; + } + + void WeatherManager::changeWeather(const ESM::RefId& regionID, const ESM::RefId& weatherID) + { + auto wIt = std::find_if(mWeatherSettings.begin(), mWeatherSettings.end(), + [weatherID](const auto& weather) { return weather.mId == weatherID; }); + + if (wIt != mWeatherSettings.end()) + { + auto rIt = mRegions.find(regionID); + if (rIt != mRegions.end()) + { + rIt->second.setWeather(std::distance(mWeatherSettings.begin(), wIt)); + regionalWeatherChanged(rIt->first, rIt->second); + } + } + } + void WeatherManager::changeWeather(const ESM::RefId& regionID, const unsigned int weatherID) { // In Morrowind, this seems to have the following behavior, when applied to the current region: @@ -1053,8 +1092,9 @@ namespace MWWorld const std::string& name, float dlFactor, float dlOffset, const std::string& particleEffect) { static const float fStromWindSpeed = mStore.get().find("fStromWindSpeed")->mValue.getFloat(); - - Weather weather(name, fStromWindSpeed, mRainSpeed, dlFactor, dlOffset, particleEffect); + ESM::StringRefId id(name); + Weather weather( + id, mWeatherSettings.size(), name, fStromWindSpeed, mRainSpeed, dlFactor, dlOffset, particleEffect); mWeatherSettings.push_back(weather); } diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 7c27a10316..ddc96f0ae6 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -109,6 +109,11 @@ namespace MWWorld T getValue(const float gameHour, const TimeOfDaySettings& timeSettings, const std::string& prefix) const; + const T& getSunriseValue() const { return mSunriseValue; } + const T& getDayValue() const { return mDayValue; } + const T& getSunsetValue() const { return mSunsetValue; } + const T& getNightValue() const { return mNightValue; } + private: T mSunriseValue, mDayValue, mSunsetValue, mNightValue; }; @@ -119,9 +124,12 @@ namespace MWWorld public: static osg::Vec3f defaultDirection(); - Weather(const std::string& name, float stormWindSpeed, float rainSpeed, float dlFactor, float dlOffset, - const std::string& particleEffect); + Weather(const ESM::RefId id, const int scriptId, const std::string& name, float stormWindSpeed, float rainSpeed, + float dlFactor, float dlOffset, const std::string& particleEffect); + ESM::RefId mId; + int mScriptId; + std::string mName; std::string mCloudTexture; // Sky (atmosphere) color @@ -289,11 +297,12 @@ namespace MWWorld ~WeatherManager(); /** - * Change the weather in the specified region + * Change the weather in the specified region by id of the weather * @param region that should be changed * @param ID of the weather setting to shift to */ void changeWeather(const ESM::RefId& regionID, const unsigned int weatherID); + void changeWeather(const ESM::RefId& regionID, const ESM::RefId& weatherID); void modRegion(const ESM::RefId& regionID, const std::vector& chances); void playerTeleported(const ESM::RefId& playerRegion, bool isExterior); @@ -316,8 +325,23 @@ namespace MWWorld void advanceTime(double hours, bool incremental); + const std::vector& getAllWeather() { return mWeatherSettings; } + + const Weather& getWeather() { return mWeatherSettings[mCurrentWeather]; } + + const Weather* getWeather(size_t index) const; + + const Weather* getWeather(const ESM::RefId& id) const; + int getWeatherID() const { return mCurrentWeather; } + const Weather* getNextWeather() + { + if (mNextWeather > -1) + return &mWeatherSettings[mNextWeather]; + return nullptr; + } + int getNextWeatherID() const { return mNextWeather; } float getTransitionFactor() const { return mTransitionFactor; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 97788669d5..02b93706b3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1868,14 +1868,43 @@ namespace MWWorld return ESM::Cell::sDefaultWorldspaceId; } - int World::getCurrentWeather() const + const std::vector& World::getAllWeather() const { - return mWeatherManager->getWeatherID(); + return mWeatherManager->getAllWeather(); } - int World::getNextWeather() const + int World::getCurrentWeatherScriptId() const { - return mWeatherManager->getNextWeatherID(); + return mWeatherManager->getWeather().mScriptId; + } + + const MWWorld::Weather& World::getCurrentWeather() const + { + return mWeatherManager->getWeather(); + } + + const MWWorld::Weather* World::getWeather(size_t index) const + { + return mWeatherManager->getWeather(index); + } + + const MWWorld::Weather* World::getWeather(const ESM::RefId& id) const + { + return mWeatherManager->getWeather(id); + } + + int World::getNextWeatherScriptId() const + { + auto next = mWeatherManager->getNextWeather(); + if (next == nullptr) + return -1; + + return next->mScriptId; + } + + const MWWorld::Weather* World::getNextWeather() const + { + return mWeatherManager->getNextWeather(); } float World::getWeatherTransition() const @@ -1893,6 +1922,11 @@ namespace MWWorld mWeatherManager->changeWeather(region, id); } + void World::changeWeather(const ESM::RefId& region, const ESM::RefId& id) + { + mWeatherManager->changeWeather(region, id); + } + void World::modRegion(const ESM::RefId& regionid, const std::vector& chances) { mWeatherManager->modRegion(regionid, chances); @@ -3133,6 +3167,11 @@ namespace MWWorld } } + const osg::Vec4f& World::getSunLightPosition() const + { + return mRendering->getSunLightPosition(); + } + float World::getSunVisibility() const { return mWeatherManager->getSunVisibility(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index b1286d5532..a9af507857 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -317,9 +317,16 @@ namespace MWWorld void changeWeather(const ESM::RefId& region, const unsigned int id) override; - int getCurrentWeather() const override; + void changeWeather(const ESM::RefId& region, const ESM::RefId& id) override; - int getNextWeather() const override; + const std::vector& getAllWeather() const override; + + int getCurrentWeatherScriptId() const override; + const MWWorld::Weather& getCurrentWeather() const override; + const MWWorld::Weather* getWeather(size_t index) const override; + const MWWorld::Weather* getWeather(const ESM::RefId& id) const override; + int getNextWeatherScriptId() const override; + const MWWorld::Weather* getNextWeather() const override; float getWeatherTransition() const override; @@ -573,6 +580,7 @@ namespace MWWorld // Allow NPCs to use torches? bool useTorches() const override; + const osg::Vec4f& getSunLightPosition() const override; float getSunVisibility() const override; float getSunPercentage() const override; diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 7b4cbac864..49fdd996a7 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -18,37 +18,33 @@ namespace Files namespace bpo = boost::program_options; + namespace + { #if defined(_WIN32) || defined(__WINDOWS__) - static const char* const applicationName = "OpenMW"; + constexpr auto applicationName = "OpenMW"; #else - static const char* const applicationName = "openmw"; + constexpr auto applicationName = "openmw"; #endif - static constexpr auto localToken = u8"?local?"; - static constexpr auto userConfigToken = u8"?userconfig?"; - static constexpr auto userDataToken = u8"?userdata?"; - static constexpr auto globalToken = u8"?global?"; + using GetPath = const std::filesystem::path& (Files::FixedPath<>::*)() const; + constexpr std::array, 4> sTokenMappings = { + std::make_pair(u8"?local?", &FixedPath<>::getLocalPath), + std::make_pair(u8"?userconfig?", &FixedPath<>::getUserConfigPath), + std::make_pair(u8"?userdata?", &FixedPath<>::getUserDataPath), + std::make_pair(u8"?global?", &FixedPath<>::getGlobalDataPath), + }; + } ConfigurationManager::ConfigurationManager(bool silent) : mFixedPath(applicationName) , mSilent(silent) { - setupTokensMapping(); - // Initialize with fixed paths, will be overridden in `readConfiguration`. mUserDataPath = mFixedPath.getUserDataPath(); mScreenshotPath = mFixedPath.getUserDataPath() / "screenshots"; } - ConfigurationManager::~ConfigurationManager() {} - - void ConfigurationManager::setupTokensMapping() - { - mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath)); - mTokensMapping.insert(std::make_pair(userConfigToken, &FixedPath<>::getUserConfigPath)); - mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath)); - mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); - } + ConfigurationManager::~ConfigurationManager() = default; static bool hasReplaceConfig(const bpo::variables_map& variables) { @@ -74,7 +70,7 @@ namespace Files std::optional config = loadConfig(mFixedPath.getLocalPath(), description); if (config) mActiveConfigPaths.push_back(mFixedPath.getLocalPath()); - else + else if (!mFixedPath.getGlobalConfigPath().empty()) { mActiveConfigPaths.push_back(mFixedPath.getGlobalConfigPath()); config = loadConfig(mFixedPath.getGlobalConfigPath(), description); @@ -305,15 +301,18 @@ namespace Files const auto pos = str.find('?', 1); if (pos != std::u8string::npos && pos != 0) { - auto tokenIt = mTokensMapping.find(str.substr(0, pos + 1)); - if (tokenIt != mTokensMapping.end()) + std::u8string_view view(str); + auto token = view.substr(0, pos + 1); + auto found = std::find_if( + sTokenMappings.begin(), sTokenMappings.end(), [&](const auto& item) { return item.first == token; }); + if (found != sTokenMappings.end()) { - auto tempPath(((mFixedPath).*(tokenIt->second))()); - if (pos < str.length() - 1) + auto tempPath(((mFixedPath).*(found->second))()); + if (!tempPath.empty() && pos < view.length() - 1) { // There is something after the token, so we should // append it to the path - tempPath /= str.substr(pos + 1, str.length() - pos); + tempPath /= view.substr(pos + 1, view.length() - pos); } path = std::move(tempPath); diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 2e10f21252..184c6ebb82 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -62,17 +62,12 @@ namespace Files private: typedef Files::FixedPath<> FixedPathType; - typedef const std::filesystem::path& (FixedPathType::*path_type_f)() const; - typedef std::map TokensMappingContainer; - std::optional loadConfig( const std::filesystem::path& path, const boost::program_options::options_description& description) const; void addExtraConfigDirs( std::stack& dirs, const boost::program_options::variables_map& variables) const; - void setupTokensMapping(); - std::vector mActiveConfigPaths; FixedPathType mFixedPath; @@ -80,8 +75,6 @@ namespace Files std::filesystem::path mUserDataPath; std::filesystem::path mScreenshotPath; - TokensMappingContainer mTokensMapping; - bool mSilent; }; diff --git a/components/files/qtconfigpath.hpp b/components/files/qtconfigpath.hpp index 16e0499cd5..a2154ce110 100644 --- a/components/files/qtconfigpath.hpp +++ b/components/files/qtconfigpath.hpp @@ -8,21 +8,11 @@ namespace Files { - inline QString getLocalConfigPathQString(const Files::ConfigurationManager& cfgMgr) - { - return Files::pathToQString(cfgMgr.getLocalPath() / openmwCfgFile); - } - inline QString getUserConfigPathQString(const Files::ConfigurationManager& cfgMgr) { return Files::pathToQString(cfgMgr.getUserConfigPath() / openmwCfgFile); } - inline QString getGlobalConfigPathQString(const Files::ConfigurationManager& cfgMgr) - { - return Files::pathToQString(cfgMgr.getGlobalPath() / openmwCfgFile); - } - inline QStringList getActiveConfigPathsQString(const Files::ConfigurationManager& cfgMgr) { const auto& activePaths = cfgMgr.getActiveConfigPaths(); diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 77faa23131..60ac5e265c 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -54,19 +54,7 @@ namespace Files { // The concept of a global config path is absurd on Windows. // Always use local config instead. - // The virtual base class requires that we provide this, though. - std::filesystem::path globalPath = std::filesystem::current_path(); - - PWSTR cString; - HRESULT result = SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, nullptr, &cString); - if (SUCCEEDED(result)) - globalPath = std::filesystem::path(cString); - else - Log(Debug::Error) << "Error " << result << " when getting Program Files path"; - - CoTaskMemFree(cString); - - return globalPath / mName; + return {}; } std::filesystem::path WindowsPath::getLocalPath() const diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index 380e831b20..ed2bbdfc2e 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -34,7 +34,7 @@ namespace Files std::filesystem::path getUserDataPath() const; /** - * \brief Returns "X:\Program Files\" + * \brief Returns an empty path * * \return std::filesystem::path */ diff --git a/extern/sol3/README.md b/extern/sol3/README.md index 202b2ca08b..fe249704c4 100644 --- a/extern/sol3/README.md +++ b/extern/sol3/README.md @@ -1,5 +1,8 @@ -The code in this directory is copied from https://github.com/ThePhD/sol2.git (64096348465b980e2f1d0e5ba9cbeea8782e8f27) +The code in this directory is copied from https://github.com/ThePhD/sol2.git (c1f95a773c6f8f4fde8ca3efe872e7286afe4444) and has been patched to include -Additional changes include cherry-picking upstream commit d805d027e0a0a7222e936926139f06e23828ce9f to fix compilation under Clang 19. +https://github.com/ThePhD/sol2/pull/1674 (71d85143ad69164f5f52c3bdab91fb503c676eb4) +https://github.com/ThePhD/sol2/pull/1676 (a6872ef46b08704b9069ebf83161f4637459ce63) +https://github.com/ThePhD/sol2/pull/1716 (5b6881ed94c795298eae72b6848308e9a37e42c5) +https://github.com/ThePhD/sol2/pull/1722 (ab874eb0e8ef8aea4c10074a89efa25f62a29d9a) License: MIT diff --git a/extern/sol3/sol/abort.hpp b/extern/sol3/sol/abort.hpp new file mode 100644 index 0000000000..692244daa7 --- /dev/null +++ b/extern/sol3/sol/abort.hpp @@ -0,0 +1,47 @@ +// sol2 + +// The MIT License (MIT) + +// Copyright (c) 2013-2022 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_ABORT_HPP +#define SOL_ABORT_HPP + +#include + +#include + +#include + +// clang-format off +#if SOL_IS_ON(SOL_DEBUG_BUILD) + #if SOL_IS_ON(SOL_COMPILER_VCXX) + #define SOL_DEBUG_ABORT() \ + if (true) { ::std::abort(); } \ + static_assert(true, "") + #else + #define SOL_DEBUG_ABORT() ::std::abort() + #endif +#else + #define SOL_DEBUG_ABORT() static_assert(true, "") +#endif +// clang-format on + +#endif // SOL_ABORT_HPP diff --git a/extern/sol3/sol/as_args.hpp b/extern/sol3/sol/as_args.hpp index 5afe78b0e4..719a3cdc99 100644 --- a/extern/sol3/sol/as_args.hpp +++ b/extern/sol3/sol/as_args.hpp @@ -2,7 +2,7 @@ // The MIT License (MIT) -// Copyright (c) 2013-2021 Rapptz, ThePhD and contributors +// Copyright (c) 2013-2022 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in diff --git a/extern/sol3/sol/as_returns.hpp b/extern/sol3/sol/as_returns.hpp index 0ac499e67d..982f408b71 100644 --- a/extern/sol3/sol/as_returns.hpp +++ b/extern/sol3/sol/as_returns.hpp @@ -2,7 +2,7 @@ // The MIT License (MIT) -// Copyright (c) 2013-2021 Rapptz, ThePhD and contributors +// Copyright (c) 2013-2022 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in diff --git a/extern/sol3/sol/assert.hpp b/extern/sol3/sol/assert.hpp index e46b9f122a..7f27905dc4 100644 --- a/extern/sol3/sol/assert.hpp +++ b/extern/sol3/sol/assert.hpp @@ -1,99 +1,99 @@ -// sol2 - -// The MIT License (MIT) - -// Copyright (c) 2013-2021 Rapptz, ThePhD and contributors - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -#pragma once - -#ifndef SOL_ASSERT_HPP -#define SOL_ASSERT_HPP - -#include - -#if SOL_IS_ON(SOL2_CI_I_) - -struct pre_main { - pre_main() { -#ifdef _MSC_VER - _set_abort_behavior(0, _WRITE_ABORT_MSG); -#endif - } -} inline sol2_ci_dont_lock_ci_please = {}; - -#endif // Prevent lockup when doing Continuous Integration - - -// clang-format off - -#if SOL_IS_ON(SOL_USER_C_ASSERT_I_) - #define sol_c_assert(...) SOL_C_ASSERT(__VA_ARGS__) -#else - #if SOL_IS_ON(SOL_DEBUG_BUILD_I_) - #include - #include - #include - - #define sol_c_assert(...) \ - do { \ - if (!(__VA_ARGS__)) { \ - std::cerr << "Assertion `" #__VA_ARGS__ "` failed in " << __FILE__ << " line " << __LINE__ << std::endl; \ - std::terminate(); \ - } \ - } while (false) - #else - #define sol_c_assert(...) \ - do { \ - if (false) { \ - (void)(__VA_ARGS__); \ - } \ - } while (false) - #endif -#endif - -#if SOL_IS_ON(SOL_USER_M_ASSERT_I_) - #define sol_m_assert(message, ...) SOL_M_ASSERT(message, __VA_ARGS__) -#else - #if SOL_IS_ON(SOL_DEBUG_BUILD_I_) - #include - #include - #include - - #define sol_m_assert(message, ...) \ - do { \ - if (!(__VA_ARGS__)) { \ - std::cerr << "Assertion `" #__VA_ARGS__ "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \ - std::terminate(); \ - } \ - } while (false) - #else - #define sol_m_assert(message, ...) \ - do { \ - if (false) { \ - (void)(__VA_ARGS__); \ - (void)sizeof(message); \ - } \ - } while (false) - #endif -#endif - -// clang-format on - -#endif // SOL_ASSERT_HPP +// sol2 + +// The MIT License (MIT) + +// Copyright (c) 2013-2022 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#pragma once + +#ifndef SOL_ASSERT_HPP +#define SOL_ASSERT_HPP + +#include + +#if SOL_IS_ON(SOL2_CI) + +struct pre_main { + pre_main() { +#ifdef _MSC_VER + _set_abort_behavior(0, _WRITE_ABORT_MSG); +#endif + } +} inline sol2_ci_dont_lock_ci_please = {}; + +#endif // Prevent lockup when doing Continuous Integration + + +// clang-format off + +#if SOL_IS_ON(SOL_USER_ASSERT) + #define SOL_ASSERT(...) SOL_C_ASSERT(__VA_ARGS__) +#else + #if SOL_IS_ON(SOL_DEBUG_BUILD) + #include + #include + #include + + #define SOL_ASSERT(...) \ + do { \ + if (!(__VA_ARGS__)) { \ + std::cerr << "Assertion `" #__VA_ARGS__ "` failed in " << __FILE__ << " line " << __LINE__ << std::endl; \ + std::terminate(); \ + } \ + } while (false) + #else + #define SOL_ASSERT(...) \ + do { \ + if (false) { \ + (void)(__VA_ARGS__); \ + } \ + } while (false) + #endif +#endif + +#if SOL_IS_ON(SOL_USER_ASSERT_MSG) + #define SOL_ASSERT_MSG(message, ...) SOL_ASSERT_MSG(message, __VA_ARGS__) +#else + #if SOL_IS_ON(SOL_DEBUG_BUILD) + #include + #include + #include + + #define SOL_ASSERT_MSG(message, ...) \ + do { \ + if (!(__VA_ARGS__)) { \ + std::cerr << "Assertion `" #__VA_ARGS__ "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \ + std::terminate(); \ + } \ + } while (false) + #else + #define SOL_ASSERT_MSG(message, ...) \ + do { \ + if (false) { \ + (void)(__VA_ARGS__); \ + (void)sizeof(message); \ + } \ + } while (false) + #endif +#endif + +// clang-format on + +#endif // SOL_ASSERT_HPP diff --git a/extern/sol3/sol/base_traits.hpp b/extern/sol3/sol/base_traits.hpp index a28f23a74c..204afc276f 100644 --- a/extern/sol3/sol/base_traits.hpp +++ b/extern/sol3/sol/base_traits.hpp @@ -1,123 +1,156 @@ -// sol2 - -// The MIT License (MIT) - -// Copyright (c) 2013-2021 Rapptz, ThePhD and contributors - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -#ifndef SOL_BASE_TRAITS_HPP -#define SOL_BASE_TRAITS_HPP - -#include - -namespace sol { - namespace detail { - struct unchecked_t { }; - const unchecked_t unchecked = unchecked_t {}; - } // namespace detail - - namespace meta { - using sfinae_yes_t = std::true_type; - using sfinae_no_t = std::false_type; - - template - using void_t = void; - - template - using unqualified = std::remove_cv>; - - template - using unqualified_t = typename unqualified::type; - - namespace meta_detail { - template - struct unqualified_non_alias : unqualified { }; - - template