From d4e00db432126675ed39f74dfac6cb01ecca16b9 Mon Sep 17 00:00:00 2001 From: Till Affeldt Date: Wed, 13 May 2020 16:03:28 +0200 Subject: [PATCH] Make wind dependent on height, add additional control commands, improve docu --- README.md | 22 ++++++++------ ROADMAP.md | 6 +--- api_doc.md | 55 ++++++++++++++++++++++++++++------ ca_effects/damage.lua | 2 +- ca_effects/hud_overlay.lua | 4 ++- ca_effects/particles.lua | 3 +- ca_effects/skybox.lua | 2 ++ init.lua | 2 +- lib/commands.lua | 61 +++++++++++++++++++++++++++++++++----- lib/environment.lua | 29 +++++++++--------- lib/influences.lua | 6 ++-- lib/player_physics.lua | 1 - lib/trigger.lua | 2 +- settingtypes.txt | 2 +- 14 files changed, 142 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 69041d7..91959fd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ If you notice __odd movement speeds__ or jump heights of players, you should che Mods that __modify the sky__ (including skybox, moon, sun, stars and clouds) are sadly not fully compatible because they conflict with Climate API's sky system. You should deactivate the sky features in either mod. You can do this here using the ``Override the skybox`` setting. If you're a mod maker then you can also optionally depend on climate_api and use ``climate_api.skybox.add_layer(playername, layer_name, options)`` to register your skybox change in a compatible way. Note that you need __at least Minetest v5.2.0__ for skybox changes to have any effect. -Conflicting skybox changes include the ``weather`` mod included in vanilla __Minetest Game__. You will want to disable that mod in order to use the more advanced cloud system introduced by Climate API. Head to ``Settings → All Settings → Games → Minetest Game`` and set ``Enable weather`` to ``Disabled``. This setting will only exist if you are using Minetest Game v5.2.0 or higher. +__Important__: Conflicting skybox changes include the ``weather`` mod included in vanilla __Minetest Game__. You will want to disable that mod in order to use the more advanced cloud system introduced by Climate API. Head to ``Settings → All Settings → Games → Minetest Game`` and set ``Enable weather`` to ``Disabled``. This setting will only exist if you are using Minetest Game v5.2.0 or higher. The following mods have been created specifically with Climate API in mind: - [Regional Weather](https://github.com/t-affeldt/regional_weather): My own weather pack for climate based weather effects @@ -33,10 +33,12 @@ The following mods complement Climate API particularly well: - ``/weather``: Display information on current weather effects. This command will show you current temperature and humidity, active weather presets and currently playing effects - ``/weather_settings``: Display current mod configuration in the chat - ``/weather_influences``: Display all different factors and how they affect you in this moment. -- ``/weather_status``: Display a list of all installed weather presets and whether they have been forced on, turned off, or are running normally (auto). +- ``/weather_status``: Display a list of all installed weather presets and whether they have been forced on, turned off, or are running normally (auto). If no weather presets are listed here then you need to install a weather mod like Regional Weather. - ``/grant weather``: Enable a specified player to modify the current weather. -- ``/set_heat ``: Override the base heat value used to calculate local climate. Positive numbers will increase temperature by X degrees Fahrenheit, whereas negative values will lower it. -- ``/set_humidity ``: Override the base humidity value used to calculate local climate. Positive numbers will increase humidity by X percent, whereas negative values will lower it. +- ``/set_heat ``: Override global heat levels with given value. +- ``/set_base_heat ``: Override the base heat value used to calculate local climate. Positive numbers will increase temperature by X degrees Fahrenheit, whereas negative values will lower it. +- ``/set_humidity ``: Override global humidity levels with given value. +- ``/set_base_humidity ``: Override the base humidity value used to calculate local climate. Positive numbers will increase humidity by X percent, whereas negative values will lower it. - ``/set_wind ``: Override wind speed and direction. Higher absolute values result in stronger wind. The sign indicates direction. - ``/set_weather ``: Set a weather preset to always be applied (on), disable it completely (off), or reset it to be applied automatically (auto). Turning presets on manually might result in partially missing effects (like no sound if you enable sandstorms but no storms). Use ``/weather_status`` for a full list of installed weather presets. The prefix is important. @@ -51,7 +53,7 @@ This value regulates how often weather presets are recalculated. Higher values will result in smoother transitions between effects as well as faster response times to traveling players. Lower values will significantly increase overall performance at the cost of rougher looking effects. - ``Multiplicator for used particles`` (default 1.0): -This value regulated how many particles will be spawned. +This value regulates how many particles will be spawned. A value of 1 will use the recommended amount of particles. Lower values can possible increase performance. - ``Dynamically modify nodes`` (default true): @@ -91,16 +93,18 @@ A value of 2 will double the speed at which weather presets change. A value of 0.5 will half the speed respectively. ### Preferences -- ``Show degrees in Fahrenheit instead of Celsius`` (default true): -If set to true, temperature information in /weather command will be displayed in Fahrenheit. +- ``Show degrees in Fahrenheit instead of Celsius`` (default false): +If set to true, temperature information in */weather* command will be displayed in Fahrenheit. - ``Play ambient sound loops`` (default true): If set to true, weather effects are allowed to play sound loops. -You can also adjust sound levels instead of deactivating this feature completely. -Setting this value to false will be slightly more performant than setting the volume to zero. +Note that you can also adjust sound levels instead of deactivating this feature completely. - ``Volume of sound effects`` (default 1.0): This value regulates overall sound volume. A value of 2 will double the volume whereas a value of 0.5 will reduce the volume by half. +## Modding Information +Check the [api_doc.md](https://github.com/t-affeldt/climate_api/blob/master/api_doc.md) for a (currently incomplete) documentation on how to register new weather presets and visual effects. Use my weather [presets](https://github.com/t-affeldt/regional_weather/tree/master/ca_weathers) and [effects](https://github.com/t-affeldt/regional_weather/tree/master/ca_effects) as an example. Ask in the [forum](https://forum.minetest.net/viewtopic.php?t=24569) or open an [issue](https://github.com/t-affeldt/climate_api/issues) if you run into problems. Also check the source code of predefined weather effects because they include usage documentation at the top of each file. + ## License - Source Code: *GNU LGPL v3* by me - Sun and moon textures: *CC BY-SA (3.0)* by Cap diff --git a/ROADMAP.md b/ROADMAP.md index 99dff07..e12870e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,15 +1,11 @@ # TODO -## Planned for first release -- Improve value structures of particle effects -- Find good values for weather conditions +## Needs improvement - Write documentation on how to add weathers and effects ## Nice to have - Assign meta data (like "downfall", "wind", etc.) to weather presets - Optimize performance by replacing some particles with animated texture planes -- Adjust size of particle boxes based on player speed -- Generate wind based on speed and yaw instead of x and z values ## Future Plans & Ideas - Complete season system diff --git a/api_doc.md b/api_doc.md index 04cefe3..ccc8d77 100644 --- a/api_doc.md +++ b/api_doc.md @@ -1,7 +1,7 @@ # API Documentation ## How to read this document -If a function states multiple parameters of the same name then either of them has to be passed on function call. Look at the function signature to determine the order of required parameters. +If a function states multiple parameters of the same name then either of them has to be passed on function call. Look at the function signature to determine the order of required parameters. So far, all parameters are mandatory. ## Custom Weather Registration @@ -82,22 +82,29 @@ __Parameter__: ``pos ``: Coordinates of requested location __Returns__: ```` indicating current humidity ### Get Current Windspeed -``climate_api.environment.get_wind()`` +``climate_api.environment.get_wind(pos)`` + +__Parameter__: ``pos ``: Coordinates of requested location. Right now, only y-coordinate is used. __Returns__: ```` indicating speed and direction -### ``climate_api.environment.get_weather_presets(player)`` +### Get Active Weather Presets +``climate_api.environment.get_weather_presets(player)`` -### ``climate_api.environment.get_effects(player)`` +### Get Active Weather Effects +``climate_api.environment.get_effects(player)`` ## Skybox Modification -### ``climate_api.skybox.add(playername, name, sky)`` +### Add Sky Configuration Layer +``climate_api.skybox.add(playername, name, sky)`` -### ``climate_api.skybox.remove(playername, name)`` +### Remove Sky Configuration Layer +``climate_api.skybox.remove(playername, name)`` -### ``climate_api.skybox.update(playername)`` +### Update Player Sky +``climate_api.skybox.update(playername)`` ## Player Physics Modifications @@ -147,6 +154,36 @@ __Parameters__: __Returns__: ```` consisting of all attributes from A and B. -### ``climate_api.utility.sigmoid(value, max, growth, midpoint)`` +### Limit Numeric Boundaries +``climate_api.utility.rangelim(value, min, max)`` -### ``climate_api.utility.normalized_cycle(value)`` +This function will return the specified value if it falls in range between minimum and maximum. Otherwise, it will return the closest boundary. + +__Parameter__: +- ``value ``: The number to check and return +- ``min ``: The lower boundary +- ``max ``: The upper boundary + +__Returns__: ```` being either the value or the closest boundary + +### Statistical Sigmoid Function +``climate_api.utility.sigmoid(value, max, growth, midpoint)`` + +This method provides a logistic function that will result in growing return values for greater input values. The resulting function graph will look like an S and fall in range between ``0`` and ``max``. You can adjust the curve by specifying growth rate and midpoint. + +__Parameters__: +- ``value ``: x value supplied to the function +- ``max ``: Maximum return value +- ``growth ``: Logistic growth rate +- ``midpoint `` Return value for ``value = 0`` and function midpoint + +__Returns__: ```` indicating y coordinate of logistic function + +### Normalized Sinus Cycle +``climate_api.utility.normalized_cycle(value)`` + +This function provides an easy way to generate a simple curve with a maximal turning point of ``y = 1`` located at ``value = 0`` and the lowest points of ``y = 0`` at ``value = +/- 1``. + +__Parameter__: ``value ``: The supplied x coordinate + +__Returns__: ```` indicating resulting y coordinate between ``0`` and ``1``. \ No newline at end of file diff --git a/ca_effects/damage.lua b/ca_effects/damage.lua index e4a27b2..4f3e14c 100644 --- a/ca_effects/damage.lua +++ b/ca_effects/damage.lua @@ -35,7 +35,7 @@ local function check_hit(player, ray) -- use raycating to factor in wind speed local origin = vector.add(ppos, {x = 0, y = ray.height or 0, z = 0 }) if ray.use_wind ~= false then - local wind = climate_api.environment.get_wind() + local wind = climate_api.environment.get_wind(origin) local velocity = ray.velocity or 1 local windpos = vector.multiply( vector.normalize(vector.add({ x = 0, y = -velocity, z = 0 }, wind)), diff --git a/ca_effects/hud_overlay.lua b/ca_effects/hud_overlay.lua index c589637..214e140 100644 --- a/ca_effects/hud_overlay.lua +++ b/ca_effects/hud_overlay.lua @@ -22,7 +22,9 @@ local function apply_hud(pname, weather, hud) local file if hud.color_correction then local pos = vector.add(player:get_pos(), {x = 0, y = 1, z = 0}) - local light = math.floor(math.max(minetest.env:get_node_light(pos) / 15, 0.2) * 256) + local node_light = minetest.env:get_node_light(pos) + if not node_light then node_light = 0 end + local light = math.floor(math.max(node_light / 15, 0.2) * 256) local shadow = 256 - light local dark_file = hud.file .. "^[multiply:#000000ff^[opacity:" .. shadow diff --git a/ca_effects/particles.lua b/ca_effects/particles.lua index 4d8e280..26c3766 100644 --- a/ca_effects/particles.lua +++ b/ca_effects/particles.lua @@ -175,7 +175,8 @@ local function parse_config(player, particles) -- move particles in wind direction if config.use_wind then - local wind = climate_api.environment.get_wind() + local pos = vector.multiply(vector.add(config.minpos, config.maxpos), 0.5) + local wind = climate_api.environment.get_wind(pos) -- adjust velocity to include wind config.minvel = vector.add(config.minvel, wind) config.maxvel = vector.add(config.maxvel, wind) diff --git a/ca_effects/skybox.lua b/ca_effects/skybox.lua index 9ca907f..c76475b 100644 --- a/ca_effects/skybox.lua +++ b/ca_effects/skybox.lua @@ -41,3 +41,5 @@ end climate_api.register_effect(EFFECT_NAME, handle_effect, "tick") climate_api.register_effect(EFFECT_NAME, remove_effect, "stop") + +climate_api.set_effect_cycle(EFFECT_NAME, climate_api.SHORT_CYCLE) \ No newline at end of file diff --git a/init.lua b/init.lua index 636a4dd..e799208 100644 --- a/init.lua +++ b/init.lua @@ -53,7 +53,7 @@ climate_mod.influences = {} climate_mod.current_weather = {} climate_mod.current_effects = {} climate_mod.forced_weather = {} -climate_mod.forced_wind = nil +climate_mod.forced_enviroment = {} -- handle persistent mod storage climate_mod.state = dofile(modpath .. "/lib/datastorage.lua") diff --git a/lib/commands.lua b/lib/commands.lua index c832944..f7a1511 100644 --- a/lib/commands.lua +++ b/lib/commands.lua @@ -55,6 +55,22 @@ minetest.register_chatcommand("weather", { }) -- set base heat to increase or decrease global climate temperatures +minetest.register_chatcommand("set_base_heat", { + params = "", + description = "Override the weather algorithm's base heat", + privs = { weather = true }, + func = function(playername, param) + if param == nil or param == "" then + minetest.chat_send_player(playername, "Provide a number to modify the base heat") + return + end + if param == "auto" then param = 0 end + climate_mod.settings.heat = tonumber(param) + minetest.chat_send_player(playername, "Base heat changed") + end +}) + +-- override global heat levels with given value minetest.register_chatcommand("set_heat", { params = "", description = "Override the weather algorithm's base heat", @@ -64,12 +80,33 @@ minetest.register_chatcommand("set_heat", { minetest.chat_send_player(playername, "Provide a number to modify the base heat") return end - climate_mod.settings.heat = tonumber(param) - minetest.chat_send_player(playername, "Heat changed") + if param == "auto" then + climate_mod.forced_enviroment.heat = nil + minetest.chat_send_player(playername, "Heat value reset") + else + climate_mod.forced_enviroment.heat = tonumber(param) + minetest.chat_send_player(playername, "Heat value changed") + end end }) -- set base heat to increase or decrease global climate humidity +minetest.register_chatcommand("set_base_humidity", { + params = "", + description = "Override the weather algorithm's base humidity", + privs = { weather = true }, + func = function(playername, param) + if param == nil or param == "" then + minetest.chat_send_player(playername, "Provide a number to modify the base humidity") + return + end + if param == "auto" then param = 0 end + climate_mod.settings.humidity = tonumber(param) + minetest.chat_send_player(playername, "Base humidity changed") + end +}) + +-- override global humidity with given value minetest.register_chatcommand("set_humidity", { params = "", description = "Override the weather algorithm's base humidity", @@ -79,8 +116,13 @@ minetest.register_chatcommand("set_humidity", { minetest.chat_send_player(playername, "Provide a number to modify the base humidity") return end - climate_mod.settings.humidity = tonumber(param) - minetest.chat_send_player(playername, "Humidity changed") + if param == "auto" then + climate_mod.forced_enviroment.humidity = nil + minetest.chat_send_player(playername, "Humidity value reset") + else + climate_mod.forced_enviroment.humidity = tonumber(param) + minetest.chat_send_player(playername, "Humidity value changed") + end end }) @@ -99,9 +141,11 @@ minetest.register_chatcommand("set_wind", { local wind_x = arguments[1] local wind_z = arguments[2] if wind_x == "auto" then - climate_mod.forced_wind = nil + climate_mod.forced_enviroment.wind = nil + elseif wind_x == nil or wind_z == nil then + minetest.chat_send_player(playername, "Invalid wind configuration") else - climate_mod.forced_wind = vector.new({ + climate_mod.forced_enviroment.wind = vector.new({ x = tonumber(wind_x), y = 0, z = tonumber(wind_z) @@ -131,11 +175,14 @@ minetest.register_chatcommand("set_weather", { local arguments = {} for w in param:gmatch("%S+") do table.insert(arguments, w) end local weather = arguments[1] - if climate_mod.weathers[weather] == nil then + if weather == nil or climate_mod.weathers[weather] == nil then minetest.chat_send_player(playername, "Unknown weather preset") return end local status + if arguments[2] == nil or arguments[2] == "" then + arguments[2] = "on" + end if arguments[2] == "on" then status = true elseif arguments[2] == "off" then diff --git a/lib/environment.lua b/lib/environment.lua index 9330352..3d369d9 100644 --- a/lib/environment.lua +++ b/lib/environment.lua @@ -1,24 +1,21 @@ local environment = {} -local function get_heat_time() - local time = minetest.get_timeofday() - return climate_api.utility.normalized_cycle(time) * 0.6 + 0.7 -end - -local function get_heat_height(y) - return climate_api.utility.rangelim((-y + 10) / 15, -10, 10) -end - function environment.get_heat(pos) + if climate_mod.forced_enviroment.heat ~= nil then + return climate_mod.forced_enviroment.heat + end local base = climate_mod.settings.heat local biome = minetest.get_heat(pos) - local height = get_heat_height(pos.y) - local time = get_heat_time() + local height = climate_api.utility.rangelim((-pos.y + 10) / 15, -10, 10) + local time = climate_api.utility.normalized_cycle(minetest.get_timeofday()) * 0.6 + 0.7 local random = climate_mod.state:get_float("heat_random"); return (base + biome + height) * time * random end function environment.get_humidity(pos) + if climate_mod.forced_enviroment.humidity ~= nil then + return climate_mod.forced_enviroment.humidity + end local base = climate_mod.settings.humidity local biome = minetest.get_humidity(pos) local random = climate_mod.state:get_float("humidity_random"); @@ -26,13 +23,15 @@ function environment.get_humidity(pos) return (base + biome * 0.7 + random_base * 0.3) * random end -function environment.get_wind() - if climate_mod.forced_wind ~= nil then - return climate_mod.forced_wind +function environment.get_wind(pos) + if climate_mod.forced_enviroment.wind ~= nil then + return climate_mod.forced_enviroment.wind end local wind_x = climate_mod.state:get_float("wind_x") local wind_z = climate_mod.state:get_float("wind_z") - return vector.new({ x = wind_x, y = 0, z = wind_z }) + local base_wind = vector.new({ x = wind_x, y = 0, z = wind_z }) + local height_modifier = climate_api.utility.sigmoid(pos.y, 2, 0.02, 1) + return vector.multiply(base_wind, height_modifier) end function environment.get_weather_presets(player) diff --git a/lib/influences.lua b/lib/influences.lua index a452692..8338219 100644 --- a/lib/influences.lua +++ b/lib/influences.lua @@ -33,13 +33,13 @@ climate_api.register_influence("biome", function(pos) return biome end) -climate_api.register_global_influence("windspeed", function() - local wind = climate_api.environment.get_wind() +climate_api.register_influence("windspeed", function(pos) + local wind = climate_api.environment.get_wind(pos) return vector.length(wind) end) climate_api.register_global_influence("wind_yaw", function() - local wind = climate_api.environment.get_wind() + local wind = climate_api.environment.get_wind({x = 0, y = 0, z = 0}) if vector.length(wind) == 0 then return 0 end return minetest.dir_to_yaw(wind) end) diff --git a/lib/player_physics.lua b/lib/player_physics.lua index 0c97cba..d67556b 100644 --- a/lib/player_physics.lua +++ b/lib/player_physics.lua @@ -49,7 +49,6 @@ elseif mod_pova then -- fallback to vanilla override as last resort else - local physics = {} local function apply_physics(player) local playername = player:get_player_name() local override = { speed = 1, jump = 1, gravity = 1 } diff --git a/lib/trigger.lua b/lib/trigger.lua index 67eca59..a0847f3 100644 --- a/lib/trigger.lua +++ b/lib/trigger.lua @@ -37,7 +37,7 @@ function trigger.test_condition(condition, env, goal) return false else value = env[condition] - return type(value) ~= "nil" and goal == value + return type(value) == "nil" or goal == value end end diff --git a/settingtypes.txt b/settingtypes.txt index 1257c13..d5a6d09 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -5,7 +5,7 @@ # Lower values will significantly increase overall performance at the cost of rougher looking effects. climate_api_tick_speed (Update speed of weather effects) float 1 0.1 10 -# This value regulated how many particles will be spawned. +# This value regulates how many particles will be spawned. # A value of 1 will use the recommended amount of particles. # Lower values can possible increase performance. climate_api_particle_count (Multiplicator for used particles) float 1 0.1 2