Make wind dependent on height, add additional control commands, improve docu

This commit is contained in:
Till Affeldt 2020-05-13 16:03:28 +02:00
parent f42b4183e5
commit d4e00db432
14 changed files with 142 additions and 55 deletions

View File

@ -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. 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: 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 - [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``: 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_settings``: Display current mod configuration in the chat
- ``/weather_influences``: Display all different factors and how they affect you in this moment. - ``/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 <playername> weather``: Enable a specified player to modify the current weather. - ``/grant <playername> weather``: Enable a specified player to modify the current weather.
- ``/set_heat <value>``: 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_heat <value>``: Override global heat levels with given value.
- ``/set_humidity <value>``: 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_base_heat <value>``: 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 <value>``: Override global humidity levels with given value.
- ``/set_base_humidity <value>``: 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 <x> <z>``: Override wind speed and direction. Higher absolute values result in stronger wind. The sign indicates direction. - ``/set_wind <x> <z>``: Override wind speed and direction. Higher absolute values result in stronger wind. The sign indicates direction.
- ``/set_weather <weather> <on|off|auto>``: 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. - ``/set_weather <weather> <on|off|auto>``: 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. 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. Lower values will significantly increase overall performance at the cost of rougher looking effects.
- ``Multiplicator for used particles`` (default 1.0): - ``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. A value of 1 will use the recommended amount of particles.
Lower values can possible increase performance. Lower values can possible increase performance.
- ``Dynamically modify nodes`` (default true): - ``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. A value of 0.5 will half the speed respectively.
### Preferences ### Preferences
- ``Show degrees in Fahrenheit instead of Celsius`` (default true): - ``Show degrees in Fahrenheit instead of Celsius`` (default false):
If set to true, temperature information in /weather command will be displayed in Fahrenheit. If set to true, temperature information in */weather* command will be displayed in Fahrenheit.
- ``Play ambient sound loops`` (default true): - ``Play ambient sound loops`` (default true):
If set to true, weather effects are allowed to play sound loops. If set to true, weather effects are allowed to play sound loops.
You can also adjust sound levels instead of deactivating this feature completely. Note that 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.
- ``Volume of sound effects`` (default 1.0): - ``Volume of sound effects`` (default 1.0):
This value regulates overall sound volume. 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. 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 ## License
- Source Code: *GNU LGPL v3* by me - Source Code: *GNU LGPL v3* by me
- Sun and moon textures: *CC BY-SA (3.0)* by Cap - Sun and moon textures: *CC BY-SA (3.0)* by Cap

View File

@ -1,15 +1,11 @@
# TODO # TODO
## Planned for first release ## Needs improvement
- Improve value structures of particle effects
- Find good values for weather conditions
- Write documentation on how to add weathers and effects - Write documentation on how to add weathers and effects
## Nice to have ## Nice to have
- Assign meta data (like "downfall", "wind", etc.) to weather presets - Assign meta data (like "downfall", "wind", etc.) to weather presets
- Optimize performance by replacing some particles with animated texture planes - 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 ## Future Plans & Ideas
- Complete season system - Complete season system

View File

@ -1,7 +1,7 @@
# API Documentation # API Documentation
## How to read this document ## 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 ## Custom Weather Registration
@ -82,22 +82,29 @@ __Parameter__: ``pos <vector>``: Coordinates of requested location
__Returns__: ``<number>`` indicating current humidity __Returns__: ``<number>`` indicating current humidity
### Get Current Windspeed ### Get Current Windspeed
``climate_api.environment.get_wind()`` ``climate_api.environment.get_wind(pos)``
__Parameter__: ``pos <vector>``: Coordinates of requested location. Right now, only y-coordinate is used.
__Returns__: ``<vector>`` indicating speed and direction __Returns__: ``<vector>`` 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 ## 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 ## Player Physics Modifications
@ -147,6 +154,36 @@ __Parameters__:
__Returns__: ``<table>`` consisting of all attributes from A and B. __Returns__: ``<table>`` 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 <number>``: The number to check and return
- ``min <number>``: The lower boundary
- ``max <number>``: The upper boundary
__Returns__: ``<number>`` 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 <number>``: x value supplied to the function
- ``max <number>``: Maximum return value
- ``growth <number>``: Logistic growth rate
- ``midpoint <number>`` Return value for ``value = 0`` and function midpoint
__Returns__: ``<number>`` 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 <number>``: The supplied x coordinate
__Returns__: ``<number>`` indicating resulting y coordinate between ``0`` and ``1``.

View File

@ -35,7 +35,7 @@ local function check_hit(player, ray)
-- use raycating to factor in wind speed -- use raycating to factor in wind speed
local origin = vector.add(ppos, {x = 0, y = ray.height or 0, z = 0 }) local origin = vector.add(ppos, {x = 0, y = ray.height or 0, z = 0 })
if ray.use_wind ~= false then 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 velocity = ray.velocity or 1
local windpos = vector.multiply( local windpos = vector.multiply(
vector.normalize(vector.add({ x = 0, y = -velocity, z = 0 }, wind)), vector.normalize(vector.add({ x = 0, y = -velocity, z = 0 }, wind)),

View File

@ -22,7 +22,9 @@ local function apply_hud(pname, weather, hud)
local file local file
if hud.color_correction then if hud.color_correction then
local pos = vector.add(player:get_pos(), {x = 0, y = 1, z = 0}) 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 shadow = 256 - light
local dark_file = hud.file .. "^[multiply:#000000ff^[opacity:" .. shadow local dark_file = hud.file .. "^[multiply:#000000ff^[opacity:" .. shadow

View File

@ -175,7 +175,8 @@ local function parse_config(player, particles)
-- move particles in wind direction -- move particles in wind direction
if config.use_wind then 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 -- adjust velocity to include wind
config.minvel = vector.add(config.minvel, wind) config.minvel = vector.add(config.minvel, wind)
config.maxvel = vector.add(config.maxvel, wind) config.maxvel = vector.add(config.maxvel, wind)

View File

@ -41,3 +41,5 @@ end
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick") climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
climate_api.register_effect(EFFECT_NAME, remove_effect, "stop") climate_api.register_effect(EFFECT_NAME, remove_effect, "stop")
climate_api.set_effect_cycle(EFFECT_NAME, climate_api.SHORT_CYCLE)

View File

@ -53,7 +53,7 @@ climate_mod.influences = {}
climate_mod.current_weather = {} climate_mod.current_weather = {}
climate_mod.current_effects = {} climate_mod.current_effects = {}
climate_mod.forced_weather = {} climate_mod.forced_weather = {}
climate_mod.forced_wind = nil climate_mod.forced_enviroment = {}
-- handle persistent mod storage -- handle persistent mod storage
climate_mod.state = dofile(modpath .. "/lib/datastorage.lua") climate_mod.state = dofile(modpath .. "/lib/datastorage.lua")

View File

@ -55,6 +55,22 @@ minetest.register_chatcommand("weather", {
}) })
-- set base heat to increase or decrease global climate temperatures -- set base heat to increase or decrease global climate temperatures
minetest.register_chatcommand("set_base_heat", {
params = "<heat>",
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", { minetest.register_chatcommand("set_heat", {
params = "<heat>", params = "<heat>",
description = "Override the weather algorithm's base heat", 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") minetest.chat_send_player(playername, "Provide a number to modify the base heat")
return return
end end
climate_mod.settings.heat = tonumber(param) if param == "auto" then
minetest.chat_send_player(playername, "Heat changed") 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 end
}) })
-- set base heat to increase or decrease global climate humidity -- set base heat to increase or decrease global climate humidity
minetest.register_chatcommand("set_base_humidity", {
params = "<humidity>",
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", { minetest.register_chatcommand("set_humidity", {
params = "<humidity>", params = "<humidity>",
description = "Override the weather algorithm's base humidity", 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") minetest.chat_send_player(playername, "Provide a number to modify the base humidity")
return return
end end
climate_mod.settings.humidity = tonumber(param) if param == "auto" then
minetest.chat_send_player(playername, "Humidity changed") 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 end
}) })
@ -99,9 +141,11 @@ minetest.register_chatcommand("set_wind", {
local wind_x = arguments[1] local wind_x = arguments[1]
local wind_z = arguments[2] local wind_z = arguments[2]
if wind_x == "auto" then 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 else
climate_mod.forced_wind = vector.new({ climate_mod.forced_enviroment.wind = vector.new({
x = tonumber(wind_x), x = tonumber(wind_x),
y = 0, y = 0,
z = tonumber(wind_z) z = tonumber(wind_z)
@ -131,11 +175,14 @@ minetest.register_chatcommand("set_weather", {
local arguments = {} local arguments = {}
for w in param:gmatch("%S+") do table.insert(arguments, w) end for w in param:gmatch("%S+") do table.insert(arguments, w) end
local weather = arguments[1] 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") minetest.chat_send_player(playername, "Unknown weather preset")
return return
end end
local status local status
if arguments[2] == nil or arguments[2] == "" then
arguments[2] = "on"
end
if arguments[2] == "on" then if arguments[2] == "on" then
status = true status = true
elseif arguments[2] == "off" then elseif arguments[2] == "off" then

View File

@ -1,24 +1,21 @@
local environment = {} 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) 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 base = climate_mod.settings.heat
local biome = minetest.get_heat(pos) local biome = minetest.get_heat(pos)
local height = get_heat_height(pos.y) local height = climate_api.utility.rangelim((-pos.y + 10) / 15, -10, 10)
local time = get_heat_time() local time = climate_api.utility.normalized_cycle(minetest.get_timeofday()) * 0.6 + 0.7
local random = climate_mod.state:get_float("heat_random"); local random = climate_mod.state:get_float("heat_random");
return (base + biome + height) * time * random return (base + biome + height) * time * random
end end
function environment.get_humidity(pos) 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 base = climate_mod.settings.humidity
local biome = minetest.get_humidity(pos) local biome = minetest.get_humidity(pos)
local random = climate_mod.state:get_float("humidity_random"); 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 return (base + biome * 0.7 + random_base * 0.3) * random
end end
function environment.get_wind() function environment.get_wind(pos)
if climate_mod.forced_wind ~= nil then if climate_mod.forced_enviroment.wind ~= nil then
return climate_mod.forced_wind return climate_mod.forced_enviroment.wind
end end
local wind_x = climate_mod.state:get_float("wind_x") local wind_x = climate_mod.state:get_float("wind_x")
local wind_z = climate_mod.state:get_float("wind_z") 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 end
function environment.get_weather_presets(player) function environment.get_weather_presets(player)

View File

@ -33,13 +33,13 @@ climate_api.register_influence("biome", function(pos)
return biome return biome
end) end)
climate_api.register_global_influence("windspeed", function() climate_api.register_influence("windspeed", function(pos)
local wind = climate_api.environment.get_wind() local wind = climate_api.environment.get_wind(pos)
return vector.length(wind) return vector.length(wind)
end) end)
climate_api.register_global_influence("wind_yaw", function() 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 if vector.length(wind) == 0 then return 0 end
return minetest.dir_to_yaw(wind) return minetest.dir_to_yaw(wind)
end) end)

View File

@ -49,7 +49,6 @@ elseif mod_pova then
-- fallback to vanilla override as last resort -- fallback to vanilla override as last resort
else else
local physics = {}
local function apply_physics(player) local function apply_physics(player)
local playername = player:get_player_name() local playername = player:get_player_name()
local override = { speed = 1, jump = 1, gravity = 1 } local override = { speed = 1, jump = 1, gravity = 1 }

View File

@ -37,7 +37,7 @@ function trigger.test_condition(condition, env, goal)
return false return false
else else
value = env[condition] value = env[condition]
return type(value) ~= "nil" and goal == value return type(value) == "nil" or goal == value
end end
end end

View File

@ -5,7 +5,7 @@
# Lower values will significantly increase overall performance at the cost of rougher looking effects. # 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 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. # A value of 1 will use the recommended amount of particles.
# Lower values can possible increase performance. # Lower values can possible increase performance.
climate_api_particle_count (Multiplicator for used particles) float 1 0.1 2 climate_api_particle_count (Multiplicator for used particles) float 1 0.1 2