From 6be96da6a4e1e075986bb4c227cab9cf1bd5d547 Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Fri, 24 Jan 2025 23:06:18 +0100 Subject: [PATCH 01/11] lua - add weatherbindings to core --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 13 +- apps/openmw/mwdialogue/filter.cpp | 2 +- apps/openmw/mwlua/corebindings.cpp | 4 + apps/openmw/mwlua/weatherbindings.cpp | 161 ++++++++++++++++++++++ apps/openmw/mwlua/weatherbindings.hpp | 15 ++ apps/openmw/mwrender/renderingmanager.cpp | 4 +- apps/openmw/mwscript/skyextensions.cpp | 2 +- apps/openmw/mwworld/weather.cpp | 50 ++++++- apps/openmw/mwworld/weather.hpp | 30 +++- apps/openmw/mwworld/worldimp.cpp | 29 +++- apps/openmw/mwworld/worldimp.hpp | 10 +- files/lua_api/openmw/core.lua | 91 ++++++++++++ 13 files changed, 392 insertions(+), 21 deletions(-) create mode 100644 apps/openmw/mwlua/weatherbindings.cpp create mode 100644 apps/openmw/mwlua/weatherbindings.hpp 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/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f268ed0e52..4b7fbaea8e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -16,6 +16,7 @@ #include "../mwworld/globalvariablename.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/spellcaststate.hpp" +#include "../mwworld/weather.hpp" #include "../mwrender/rendermode.hpp" @@ -216,9 +217,17 @@ 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 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 const MWWorld::Weather* getNextWeather() const = 0; virtual float getWeatherTransition() const = 0; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index b7d4a1361c..4a46b9be60 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()->getCurrentWeather().mScriptId; 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..ebf185c515 --- /dev/null +++ b/apps/openmw/mwlua/weatherbindings.cpp @@ -0,0 +1,161 @@ +#include "weatherbindings.hpp" + +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/weather.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(); } + }; +} + +namespace MWLua +{ + sol::table initCoreWeatherBindings(const Context& context) + { + sol::state_view lua = context.sol(); + sol::table api(lua, sol::create); + + 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([](const MWWorld::Weather& w) { return w.mCloudTexture; }); + 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) { 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 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"] = w.mAmbientColor.getSunriseValue(); + result["day"] = w.mAmbientColor.getDayValue(); + result["sunset"] = w.mAmbientColor.getSunsetValue(); + result["night"] = w.mAmbientColor.getNightValue(); + return result; + }); + weatherT["fogColor"] = sol::readonly_property([lua](const MWWorld::Weather& w) { + sol::table result(lua, sol::create); + result["sunrise"] = w.mFogColor.getSunriseValue(); + result["day"] = w.mFogColor.getDayValue(); + result["sunset"] = w.mFogColor.getSunsetValue(); + result["night"] = w.mFogColor.getNightValue(); + return result; + }); + weatherT["skyColor"] = sol::readonly_property([lua](const MWWorld::Weather& w) { + sol::table result(lua, sol::create); + result["sunrise"] = w.mSkyColor.getSunriseValue(); + result["day"] = w.mSkyColor.getDayValue(); + result["sunset"] = w.mSkyColor.getSunsetValue(); + result["night"] = w.mSkyColor.getNightValue(); + return result; + }); + weatherT["sunColor"] = sol::readonly_property([lua](const MWWorld::Weather& w) { + sol::table result(lua, sol::create); + result["sunrise"] = w.mSunColor.getSunriseValue(); + result["day"] = w.mSunColor.getDayValue(); + result["sunset"] = w.mSunColor.getSunsetValue(); + result["night"] = 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) { 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"] = []() -> sol::optional { + auto next = MWBase::Environment::get().getWorld()->getNextWeather(); + if (next != nullptr) + return *next; + return sol::nullopt; + }; + 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); + const ESM::Region* reg = MWBase::Environment::get().getESMStore()->get().search(region); + if (reg) + MWBase::Environment::get().getWorld()->changeWeather(region, weather.mId); + else + throw std::runtime_error("Region not found"); + }; + + 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["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..4f4809354c --- /dev/null +++ b/apps/openmw/mwlua/weatherbindings.hpp @@ -0,0 +1,15 @@ +#ifndef MWLUA_WEATHERBINDINGS_H +#define MWLUA_WEATHERBINDINGS_H + +#include + +#include "context.hpp" + +namespace MWLua +{ + + 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..ed3f0e513e 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->getCurrentWeather().mScriptId); + stateUpdater->setNextWeatherId(world->getNextWeather() != nullptr ? world->getNextWeather()->mScriptId : -1); stateUpdater->setWeatherTransition(world->getWeatherTransition()); stateUpdater->setWindSpeed(world->getWindSpeed()); stateUpdater->setSkyColor(mSky->getSkyColor()); diff --git a/apps/openmw/mwscript/skyextensions.cpp b/apps/openmw/mwscript/skyextensions.cpp index d2b41fb87a..462f7bd811 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()->getCurrentWeather().mScriptId); } }; 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..24e4049fc9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,6 +1,7 @@ #include "worldimp.hpp" #include +#include #include #include @@ -1868,14 +1869,29 @@ 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 + const MWWorld::Weather& World::getCurrentWeather() const { - return mWeatherManager->getNextWeatherID(); + 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); + } + + const MWWorld::Weather* World::getNextWeather() const + { + return mWeatherManager->getNextWeather(); } float World::getWeatherTransition() const @@ -1893,6 +1909,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); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index b1286d5532..1501ca4ddc 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -317,9 +317,15 @@ 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; + + 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; + + const MWWorld::Weather* getNextWeather() const override; float getWeatherTransition() const override; diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 94fefb2b6d..a0cd2790b8 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -1198,4 +1198,95 @@ -- @field #string id MWScript id -- @field #string text MWScript content + +--- @{#Weather}: Weather +-- @field [parent=#core] #Weather weather + +--- List of all @{#WeatherRecord}s. +-- @field [parent=#Weather] #list<#WeatherRecord> records A read-only list of all @{#WeatherRecord}s in the world database, may be indexed by recordId. +-- Implements [iterables#List](iterables.html#List) of #WeatherRecord. +-- @usage local weather = core.weather.records.records['Cloudy'] -- get by id +-- @usage local weather = core.weather.records.records[1] -- get by index +-- @usage -- Print all storms +-- for _, weather in pairs(core.weather.records) do +-- if weather.isStorm then +-- print(weather.name) +-- end +-- end + +--- +-- Get the current weather +-- @function [parent=#Weather] getCurrent +-- @return #WeatherData + +--- +-- Get the next weather if any +-- @function [parent=#Weather] getNext +-- @return #any can be nil + +--- +-- Get current weather transition value +-- @function [parent=#Weather] getTransition +-- @return #number + +--- +-- Change the weather +-- @function [parent=#Weather] changeWeather +-- @param #string regionId +-- @param #WeatherData The weather to change to + +--- +-- Get the current sun visibility taking weather transition into account. +-- @function [parent=#Weather] getCurrentSunVisibility +-- @return #number + +--- +-- Get the current sun percentage taking weather transition into account. +-- @function [parent=#Weather] getCurrentSunPercentage +-- @return #number + +--- +-- Get the current wind speed taking weather transition into account. +-- @function [parent=#Weather] getCurrentWindSpeed +-- @return #number + +--- +-- Get the current storm direction taking weather transition into account. +-- @function [parent=#Weather] getCurrentStormDirection +-- @return openmw.util#Vector3 + +--- +-- Weather data +-- @type WeatherRecord +-- @extends #userdata +-- @field #string recordId +-- @field #number scriptId +-- @field #string name +-- @field #number windSpeed +-- @field #number cloudSpeed +-- @field #string cloudTexture +-- @field #number cloudsMaximumPercent +-- @field #boolean isStorm +-- @field openmw.util#Vector3 stormDirection +-- @field #number glareView +-- @field #number rainSpeed +-- @field #number rainEntranceSpeed +-- @field #string rainEffect +-- @field #number rainMaxRaindrops +-- @field #number rainDiameter +-- @field #number rainMaxHeight +-- @field #number rainMinHeight +-- @field #string rainLoopSoundID +-- @field #table thunderSoundID An array containing the recordIds of the thunder sounds +-- @field #string ambientLoopSoundID +-- @field #number particleEffect +-- @field #number distantLandFogFactor +-- @field #number distantLandFogOffset +-- @field openmw.util#Vector4 sunDiscSunsetColor +-- @field #table landFogDepth A table with the keys "sunrise", "day", "sunset" and "night" +-- @field #table skyColor A table with the keys "sunrise", "day", "sunset" and "night" +-- @field #table ambientColor A table with the keys "sunrise", "day", "sunset" and "night" +-- @field #table fogColor A table with the keys "sunrise", "day", "sunset" and "night" +-- @field #table sunColor A table with the keys "sunrise", "day", "sunset" and "night" + return nil From d20a56517b3a5ad2843d5f14e16e926fbbb0d39d Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Wed, 23 Jul 2025 23:48:54 +0200 Subject: [PATCH 02/11] add getCurrentSunLightDirection --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwlua/weatherbindings.cpp | 14 ++++++++------ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 6 +++++- apps/openmw/mwworld/worldimp.hpp | 1 + files/lua_api/openmw/core.lua | 5 +++++ 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 4b7fbaea8e..b0486b40a2 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -487,6 +487,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/mwlua/weatherbindings.cpp b/apps/openmw/mwlua/weatherbindings.cpp index ebf185c515..e2a75a2c0b 100644 --- a/apps/openmw/mwlua/weatherbindings.cpp +++ b/apps/openmw/mwlua/weatherbindings.cpp @@ -117,12 +117,8 @@ namespace MWLua weatherT["recordId"] = sol::readonly_property([](const MWWorld::Weather& w) { return w.mId.serializeText(); }); api["getCurrent"] = []() { return MWBase::Environment::get().getWorld()->getCurrentWeather(); }; - api["getNext"] = []() -> sol::optional { - auto next = MWBase::Environment::get().getWorld()->getNextWeather(); - if (next != nullptr) - return *next; - return sol::nullopt; - }; + 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) { @@ -151,6 +147,12 @@ namespace MWLua // Provide access to the store. api["records"] = WeatherStore{}; + api["getCurrentSunLightDirection"] = []() { + osg::Vec4f sunPos = MWBase::Environment::get().getWorld()->getSunLightPosition(); + sunPos.normalize(); + + 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(); }; 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/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 24e4049fc9..057456abe4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,7 +1,6 @@ #include "worldimp.hpp" #include -#include #include #include @@ -3154,6 +3153,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 1501ca4ddc..d7c05a0191 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -579,6 +579,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/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index a0cd2790b8..668c1c0fd0 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -1235,6 +1235,11 @@ -- @param #string regionId -- @param #WeatherData The weather to change to +--- +-- Get the current direction of the light of the sun. +-- @function [parent=#Weather] getCurrentSunLightDirection +-- @return openmw.util#Vector4 + --- -- Get the current sun visibility taking weather transition into account. -- @function [parent=#Weather] getCurrentSunVisibility From 27adbf0cdecabdd8c069a8caf6f43f25da832424 Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Fri, 25 Jul 2025 00:41:24 +0200 Subject: [PATCH 03/11] less preprocessed code lines --- apps/openmw/mwbase/world.hpp | 3 ++- apps/openmw/mwdialogue/filter.cpp | 1 + apps/openmw/mwlua/weatherbindings.cpp | 9 ++++----- apps/openmw/mwlua/weatherbindings.hpp | 3 +-- apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwscript/skyextensions.cpp | 1 + 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b0486b40a2..bc7c504471 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -16,13 +16,13 @@ #include "../mwworld/globalvariablename.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/spellcaststate.hpp" -#include "../mwworld/weather.hpp" #include "../mwrender/rendermode.hpp" namespace osg { class Vec3f; + class Vec4f; class Matrixf; class Quat; class Image; @@ -94,6 +94,7 @@ namespace MWWorld class RefData; class Cell; class DateTimeManager; + class Weather; typedef std::vector> PtrMovementList; } diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 4a46b9be60..92ec453c58 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -18,6 +18,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/weather.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/creaturestats.hpp" diff --git a/apps/openmw/mwlua/weatherbindings.cpp b/apps/openmw/mwlua/weatherbindings.cpp index e2a75a2c0b..d67851c3c6 100644 --- a/apps/openmw/mwlua/weatherbindings.cpp +++ b/apps/openmw/mwlua/weatherbindings.cpp @@ -8,6 +8,8 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/weather.hpp" +#include "context.hpp" + namespace { class WeatherStore @@ -123,11 +125,8 @@ namespace MWLua api["changeWeather"] = [](std::string_view regionId, const MWWorld::Weather& weather) { ESM::RefId region = ESM::RefId::deserializeText(regionId); - const ESM::Region* reg = MWBase::Environment::get().getESMStore()->get().search(region); - if (reg) - MWBase::Environment::get().getWorld()->changeWeather(region, weather.mId); - else - throw std::runtime_error("Region not found"); + MWBase::Environment::get().getESMStore()->get().find(region); + MWBase::Environment::get().getWorld()->changeWeather(region, weather.mId); }; sol::usertype storeT = lua.new_usertype("WeatherWorldStore"); diff --git a/apps/openmw/mwlua/weatherbindings.hpp b/apps/openmw/mwlua/weatherbindings.hpp index 4f4809354c..c4686602ff 100644 --- a/apps/openmw/mwlua/weatherbindings.hpp +++ b/apps/openmw/mwlua/weatherbindings.hpp @@ -3,10 +3,9 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; sol::table initCoreWeatherBindings(const Context&); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ed3f0e513e..82cc9b8b3c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -60,6 +60,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/groundcoverstore.hpp" #include "../mwworld/scene.hpp" +#include "../mwworld/weather.hpp" #include "../mwgui/postprocessorhud.hpp" diff --git a/apps/openmw/mwscript/skyextensions.cpp b/apps/openmw/mwscript/skyextensions.cpp index 462f7bd811..e85d8381ba 100644 --- a/apps/openmw/mwscript/skyextensions.cpp +++ b/apps/openmw/mwscript/skyextensions.cpp @@ -14,6 +14,7 @@ #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwworld/weather.hpp" namespace MWScript { From 3f54d3e5693cca306790a748fa9ab13ca8c80337 Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Fri, 25 Jul 2025 00:44:07 +0200 Subject: [PATCH 04/11] update lua api revision --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 "") From f1447207b2297ca4016a62f20eee22c150980cd0 Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Fri, 25 Jul 2025 01:13:43 +0200 Subject: [PATCH 05/11] more less preprocessed code lines --- apps/openmw/mwbase/world.hpp | 4 ++++ apps/openmw/mwdialogue/filter.cpp | 3 +-- apps/openmw/mwrender/renderingmanager.cpp | 5 ++--- apps/openmw/mwscript/skyextensions.cpp | 3 +-- apps/openmw/mwworld/worldimp.cpp | 14 ++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 3 ++- 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index bc7c504471..956a038f11 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -222,12 +222,16 @@ namespace MWBase 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; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 92ec453c58..a5a3be85f9 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -18,7 +18,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/weather.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -502,7 +501,7 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons case ESM::DialogueCondition::Function_Weather: - return MWBase::Environment::get().getWorld()->getCurrentWeather().mScriptId; + return MWBase::Environment::get().getWorld()->getCurrentWeatherScriptId(); case ESM::DialogueCondition::Function_Reputation: if (!mActor.getClass().isNpc()) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 82cc9b8b3c..0698e8c4ae 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -60,7 +60,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/groundcoverstore.hpp" #include "../mwworld/scene.hpp" -#include "../mwworld/weather.hpp" #include "../mwgui/postprocessorhud.hpp" @@ -944,8 +943,8 @@ namespace MWRender stateUpdater->setIsUnderwater(isUnderwater); stateUpdater->setFogColor(fogColor); stateUpdater->setGameHour(world->getTimeStamp().getHour()); - stateUpdater->setWeatherId(world->getCurrentWeather().mScriptId); - stateUpdater->setNextWeatherId(world->getNextWeather() != nullptr ? world->getNextWeather()->mScriptId : -1); + 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/mwscript/skyextensions.cpp b/apps/openmw/mwscript/skyextensions.cpp index e85d8381ba..b354bb5ce2 100644 --- a/apps/openmw/mwscript/skyextensions.cpp +++ b/apps/openmw/mwscript/skyextensions.cpp @@ -14,7 +14,6 @@ #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwworld/weather.hpp" namespace MWScript { @@ -72,7 +71,7 @@ namespace MWScript public: void execute(Interpreter::Runtime& runtime) override { - runtime.push(MWBase::Environment::get().getWorld()->getCurrentWeather().mScriptId); + runtime.push(MWBase::Environment::get().getWorld()->getCurrentWeatherScriptId()); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 057456abe4..02b93706b3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1873,6 +1873,11 @@ namespace MWWorld return mWeatherManager->getAllWeather(); } + int World::getCurrentWeatherScriptId() const + { + return mWeatherManager->getWeather().mScriptId; + } + const MWWorld::Weather& World::getCurrentWeather() const { return mWeatherManager->getWeather(); @@ -1888,6 +1893,15 @@ namespace MWWorld 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(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d7c05a0191..a9af507857 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -321,10 +321,11 @@ namespace MWWorld 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; From b8fec360c3f4862e27981ee35c3700e42d8b48b8 Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Fri, 25 Jul 2025 02:20:26 +0200 Subject: [PATCH 06/11] typo --- files/lua_api/openmw/core.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 668c1c0fd0..7feed7843b 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -1205,8 +1205,8 @@ --- List of all @{#WeatherRecord}s. -- @field [parent=#Weather] #list<#WeatherRecord> records A read-only list of all @{#WeatherRecord}s in the world database, may be indexed by recordId. -- Implements [iterables#List](iterables.html#List) of #WeatherRecord. --- @usage local weather = core.weather.records.records['Cloudy'] -- get by id --- @usage local weather = core.weather.records.records[1] -- get by index +-- @usage local weather = core.weather.records['Cloudy'] -- get by id +-- @usage local weather = core.weather.records[1] -- get by index -- @usage -- Print all storms -- for _, weather in pairs(core.weather.records) do -- if weather.isStorm then From cbb96e0fc29b533f55dd06814067b76fc39f1163 Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Fri, 25 Jul 2025 20:10:21 +0200 Subject: [PATCH 07/11] colors should use Misc::Color --- apps/openmw/mwlua/weatherbindings.cpp | 45 ++++++++++++++++----------- files/lua_api/openmw/core.lua | 8 ++--- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwlua/weatherbindings.cpp b/apps/openmw/mwlua/weatherbindings.cpp index d67851c3c6..624382b293 100644 --- a/apps/openmw/mwlua/weatherbindings.cpp +++ b/apps/openmw/mwlua/weatherbindings.cpp @@ -1,7 +1,10 @@ #include "weatherbindings.hpp" +#include + #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -25,6 +28,11 @@ namespace } 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 @@ -66,39 +74,39 @@ namespace MWLua return result; }); weatherT["sunDiscSunsetColor"] - = sol::readonly_property([](const MWWorld::Weather& w) { return w.mSunDiscSunsetColor; }); + = 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"] = w.mAmbientColor.getSunriseValue(); - result["day"] = w.mAmbientColor.getDayValue(); - result["sunset"] = w.mAmbientColor.getSunsetValue(); - result["night"] = w.mAmbientColor.getNightValue(); + 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"] = w.mFogColor.getSunriseValue(); - result["day"] = w.mFogColor.getDayValue(); - result["sunset"] = w.mFogColor.getSunsetValue(); - result["night"] = w.mFogColor.getNightValue(); + 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"] = w.mSkyColor.getSunriseValue(); - result["day"] = w.mSkyColor.getDayValue(); - result["sunset"] = w.mSkyColor.getSunsetValue(); - result["night"] = w.mSkyColor.getNightValue(); + 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"] = w.mSunColor.getSunriseValue(); - result["day"] = w.mSunColor.getDayValue(); - result["sunset"] = w.mSunColor.getSunsetValue(); - result["night"] = w.mSunColor.getNightValue(); + 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) { @@ -128,7 +136,8 @@ namespace MWLua 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}"; }; diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 7feed7843b..7e2f33c638 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -1289,9 +1289,9 @@ -- @field #number distantLandFogOffset -- @field openmw.util#Vector4 sunDiscSunsetColor -- @field #table landFogDepth A table with the keys "sunrise", "day", "sunset" and "night" --- @field #table skyColor A table with the keys "sunrise", "day", "sunset" and "night" --- @field #table ambientColor A table with the keys "sunrise", "day", "sunset" and "night" --- @field #table fogColor A table with the keys "sunrise", "day", "sunset" and "night" --- @field #table sunColor A table with the keys "sunrise", "day", "sunset" and "night" +-- @field #table skyColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a openmw.util#Color. +-- @field #table ambientColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a openmw.util#Color. +-- @field #table fogColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a openmw.util#Color. +-- @field #table sunColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a openmw.util#Color. return nil From a560aceb2a8449ea263d23a137d4bf779d3ce307 Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Fri, 25 Jul 2025 20:37:05 +0200 Subject: [PATCH 08/11] correct sunDiscSunsetColor type --- files/lua_api/openmw/core.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 7e2f33c638..7b1ac8fb76 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -1287,7 +1287,7 @@ -- @field #number particleEffect -- @field #number distantLandFogFactor -- @field #number distantLandFogOffset --- @field openmw.util#Vector4 sunDiscSunsetColor +-- @field openmw.util#Color sunDiscSunsetColor -- @field #table landFogDepth A table with the keys "sunrise", "day", "sunset" and "night" -- @field #table skyColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a openmw.util#Color. -- @field #table ambientColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a openmw.util#Color. From 855b236ee89eed5362a47deb3049f276040fd276 Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Fri, 25 Jul 2025 21:00:09 +0200 Subject: [PATCH 09/11] direction of the sun light != direction to the sun --- apps/openmw/mwlua/weatherbindings.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwlua/weatherbindings.cpp b/apps/openmw/mwlua/weatherbindings.cpp index 624382b293..0024cbdfa1 100644 --- a/apps/openmw/mwlua/weatherbindings.cpp +++ b/apps/openmw/mwlua/weatherbindings.cpp @@ -157,9 +157,11 @@ namespace MWLua api["getCurrentSunLightDirection"] = []() { osg::Vec4f sunPos = MWBase::Environment::get().getWorld()->getSunLightPosition(); + // normalize to get the direction towards the sun sunPos.normalize(); - return sunPos; + // 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(); }; From 95fc66bccd3fdfa1722a8c52929b80e35a172bf1 Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Fri, 25 Jul 2025 23:22:33 +0200 Subject: [PATCH 10/11] adjusments to weatherbindings - return nil for not existing effects - also use currectTexturePath for cloud texture - corrections for docs --- apps/openmw/mwlua/weatherbindings.cpp | 22 +++++++++++++++++----- files/lua_api/openmw/core.lua | 12 ++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwlua/weatherbindings.cpp b/apps/openmw/mwlua/weatherbindings.cpp index 0024cbdfa1..daabd08620 100644 --- a/apps/openmw/mwlua/weatherbindings.cpp +++ b/apps/openmw/mwlua/weatherbindings.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -42,13 +44,16 @@ namespace MWLua 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([](const MWWorld::Weather& w) { return w.mCloudTexture; }); + 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; }); @@ -58,7 +63,11 @@ namespace MWLua 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) { return w.mRainEffect; }); + 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; }); @@ -118,7 +127,11 @@ namespace MWLua return result; }); weatherT["particleEffect"] - = sol::readonly_property([](const MWWorld::Weather& w) { return w.mParticleEffect; }); + = 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"] @@ -136,8 +149,7 @@ namespace MWLua 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}"; }; diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 7b1ac8fb76..dc38922d6f 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -1276,7 +1276,7 @@ -- @field #number glareView -- @field #number rainSpeed -- @field #number rainEntranceSpeed --- @field #string rainEffect +-- @field #string rainEffect Will return nil if weather has no rainEffect -- @field #number rainMaxRaindrops -- @field #number rainDiameter -- @field #number rainMaxHeight @@ -1284,14 +1284,14 @@ -- @field #string rainLoopSoundID -- @field #table thunderSoundID An array containing the recordIds of the thunder sounds -- @field #string ambientLoopSoundID --- @field #number particleEffect +-- @field #string particleEffect Will return nil if weather has no particleEffect -- @field #number distantLandFogFactor -- @field #number distantLandFogOffset -- @field openmw.util#Color sunDiscSunsetColor -- @field #table landFogDepth A table with the keys "sunrise", "day", "sunset" and "night" --- @field #table skyColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a openmw.util#Color. --- @field #table ambientColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a openmw.util#Color. --- @field #table fogColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a openmw.util#Color. --- @field #table sunColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a openmw.util#Color. +-- @field #table skyColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a #{openmw.util#Color}. +-- @field #table ambientColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a #{openmw.util#Color}. +-- @field #table fogColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a @{openmw.util#Color}. +-- @field #table sunColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a @{openmw.util#Color}. return nil From 534d6b3ae93d0152d26ea5d429431f1054d297f5 Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Sat, 26 Jul 2025 13:39:37 +0200 Subject: [PATCH 11/11] typo --- files/lua_api/openmw/core.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index dc38922d6f..ceb3829cd3 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -1289,8 +1289,8 @@ -- @field #number distantLandFogOffset -- @field openmw.util#Color sunDiscSunsetColor -- @field #table landFogDepth A table with the keys "sunrise", "day", "sunset" and "night" --- @field #table skyColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a #{openmw.util#Color}. --- @field #table ambientColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a #{openmw.util#Color}. +-- @field #table skyColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a @{openmw.util#Color}. +-- @field #table ambientColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a @{openmw.util#Color}. -- @field #table fogColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a @{openmw.util#Color}. -- @field #table sunColor A table with the keys "sunrise", "day", "sunset" and "night". Each is a @{openmw.util#Color}.