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.
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 <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_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_heat <value>``: Override global heat levels with given value.
- ``/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_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.
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

View File

@ -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

View File

@ -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 <vector>``: Coordinates of requested location
__Returns__: ``<number>`` indicating current humidity
### 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
### ``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__: ``<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
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)),

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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 = "<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", {
params = "<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")
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 = "<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", {
params = "<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")
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

View File

@ -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)

View File

@ -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)

View File

@ -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 }

View File

@ -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

View File

@ -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