Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
bff54bfb74 | |||
1057342ac6 | |||
e24b0340e5 | |||
c9d0cfca21 | |||
d1bdf92937 | |||
0c5c9f0d15 | |||
a6cfca7745 | |||
241a0a82c1 | |||
18045b1943 |
99
README.md
@ -1,5 +1,8 @@
|
|||||||
# Climate API
|
# Climate API
|
||||||
A powerful engine for weather presets and visual effects
|
A powerful engine for weather presets and visual effects.
|
||||||
|
Requires a weather pack like [Regional Weather](https://github.com/t-affeldt/regional_weather).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
Use the regional climate to set up different effects for different regions.
|
Use the regional climate to set up different effects for different regions.
|
||||||
Control where your effects are activated based on temperature, humidity, wind,
|
Control where your effects are activated based on temperature, humidity, wind,
|
||||||
@ -8,8 +11,98 @@ Climate API provides temperature and humidity values on a block-per-block basis
|
|||||||
that follow the seasons, day / night cycle and random changes.
|
that follow the seasons, day / night cycle and random changes.
|
||||||
Make it rain, change the sky or poison the player - it's up to you.
|
Make it rain, change the sky or poison the player - it's up to you.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
Generally speaking, most mods should be compatible.
|
||||||
|
|
||||||
|
If you notice __odd movement speeds__ or jump heights of players, you should check for mods that also modify player physics. Use a compatibility mod like [player_monoids](https://github.com/minetest-mods/player_monoids) or [playerphysics](https://forum.minetest.net/viewtopic.php?t=22172) to get rid of this problem. This requires the conflicting mod to also support the chosen compatibility layer.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
- [Moon Phases](https://github.com/t-affeldt/minetest_moon_phase): Complements weather effects with dynamic sky changes and a full moon cycle
|
||||||
|
- [Sailing Kit](https://github.com/t-affeldt/sailing_kit) (Fork): Uses Climate API's new wind system to sail across the sea.
|
||||||
|
|
||||||
|
The following mods complement Climate API particularly well:
|
||||||
|
- [Lightning](https://github.com/minetest-mods/lightning): Adds to heavy rain by enabling additional lightning effects
|
||||||
|
- [Ambience](https://notabug.org/TenPlus1/ambience): Plays some nice ambient sound effects based on where you are.
|
||||||
|
|
||||||
|
## Chat Commands
|
||||||
|
- ``/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).
|
||||||
|
- ``/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_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.
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
You can find all mod configuration options in your Minetest launcher.
|
||||||
|
Go to ``Settings → All Settings → Mods → climate_api`` to change them.
|
||||||
|
Individual weather packs may provide additional configuration options in their respective mod configuration section.
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- ``Update speed of weather effects`` (default 1.0):
|
||||||
|
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.
|
||||||
|
A value of 1 will use the recommended amount of particles.
|
||||||
|
Lower values can possible increase performance.
|
||||||
|
- ``Dynamically modify nodes`` (default true):
|
||||||
|
If set to true, weather packs are allowed to register node update handlers.
|
||||||
|
These can be used to dynamically place snow layers, melt ice, or hydrate soil.
|
||||||
|
|
||||||
|
### Visuals
|
||||||
|
- ``Show particle effects`` (default true):
|
||||||
|
If set to true, weather effects (like rain) are allowed to render particles.
|
||||||
|
Deactivating this feature will prevent some presets from being visible.
|
||||||
|
For performance considerations it is recommended to decrease the amount of particles instead.
|
||||||
|
- ``Override the skybox`` (default true):
|
||||||
|
If set to true, weather effects are allowed to modify a player's sky.
|
||||||
|
This includes skybox, sun, moon, and clouds (also used for fog effects).
|
||||||
|
Running this mod on Minetest 5.1.2 or earlier versions will automatically disable this feature.
|
||||||
|
- ``Display HUD overlays`` (default true):
|
||||||
|
If set to true, weather effects are allowed to render an image on top of the gameplay.
|
||||||
|
This is usually an optional effect used to increase immersion (like a frozen-over camera in a snow storm).
|
||||||
|
|
||||||
|
### Environment
|
||||||
|
- ``Global base temperature`` (default 0):
|
||||||
|
This value will be added to all biome's base temperatures before applying random modifiers.
|
||||||
|
Every unit here will increase the global base heat by one degree Fahrenheit.
|
||||||
|
Negative values will cool down global base heat respectively.
|
||||||
|
- ``Global base humidity`` (default 0):
|
||||||
|
This value will be added to all biome's base humidity before applying random modifiers.
|
||||||
|
Every unit here will increase the global base humidity by one percent.
|
||||||
|
Negative values will dry up global base humidity respectively.
|
||||||
|
- ``Time rate of weather changes`` (default 1.0):
|
||||||
|
This value regulates how quickly environment factors like heat, humidity and wind are changing.
|
||||||
|
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.
|
||||||
|
- ``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.
|
||||||
|
- ``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.
|
||||||
|
|
||||||
|
## License
|
||||||
|
- Source Code: *GNU LGPL v3* by me
|
||||||
|
- Sun and moon textures: *CC BY-SA (3.0)* by Cap
|
||||||
|
|
||||||
## Assets in screenshots
|
## Assets in screenshots
|
||||||
- All screenshots and editing by me: *CC BY-SA (4.0)*
|
- Screenshots and editing by me: *CC BY-SA (3.0)*
|
||||||
|
- Logos and artwork: *CC BY-SA (3.0)* by Cap
|
||||||
- Lato Font (for the Logo): *OFL* by Łukasz Dziedzic from http://www.latofonts.com/lato-free-fonts/
|
- Lato Font (for the Logo): *OFL* by Łukasz Dziedzic from http://www.latofonts.com/lato-free-fonts/
|
||||||
- Liberation Fonts (for the text): *OFL*, see https://github.com/liberationfonts/liberation-fonts
|
- Source Sans Pro (for the subtitles): *OFL*, see https://fonts.google.com/specimen/Source+Sans+Pro
|
||||||
- Used texture pack: Polygonia (128px edition) *CC BY-SA (4.0)* by Lokrates. See https://forum.minetest.net/viewtopic.php?f=4&t=19043
|
- Used texture pack: Polygonia (128px edition) *CC BY-SA (4.0)* by Lokrates. See https://forum.minetest.net/viewtopic.php?f=4&t=19043
|
26
ROADMAP.md
@ -1,30 +1,15 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
## Required for MVP
|
|
||||||
- Find good values for weather conditions
|
|
||||||
- Make sure all weather presets are working
|
|
||||||
|
|
||||||
## Required for Beta
|
|
||||||
- Ability to register environment conditions dynamically (like the heat)
|
|
||||||
|
|
||||||
## Planned for first release
|
## Planned for first release
|
||||||
- Improve test_condition function
|
- Improve value structures of particle effects
|
||||||
- Write helpful README
|
- Find good values for weather conditions
|
||||||
- Set effects on player join
|
- Write documentation on how to add weathers and effects
|
||||||
- Improve value structures of particle and skybox effects
|
|
||||||
- Make sounds adjust to changes by weather presets
|
|
||||||
- Ability to force set a wind speed via chat commands
|
|
||||||
|
|
||||||
## Nice to have
|
## Nice to have
|
||||||
- Write documentation on how to add weathers and effects
|
|
||||||
- Assign meta data (like "downfall", "wind", etc.) to weather presets
|
- Assign meta data (like "downfall", "wind", etc.) to weather presets
|
||||||
- Fog effects
|
|
||||||
- Optimize performance by replacing some particles with animated texture planes
|
- Optimize performance by replacing some particles with animated texture planes
|
||||||
- Make switches between effects more smooth
|
|
||||||
- Adjust size of particle boxes based on player speed
|
- Adjust size of particle boxes based on player speed
|
||||||
- Create conditions for time of day, annual progression, biome filters
|
- Generate wind based on speed and yaw instead of x and z values
|
||||||
- Fork lightning so that it uses skylayer
|
|
||||||
- Support for sail boats mod
|
|
||||||
|
|
||||||
## Future Plans & Ideas
|
## Future Plans & Ideas
|
||||||
- Complete season system
|
- Complete season system
|
||||||
@ -51,4 +36,5 @@
|
|||||||
- swimming will cool down
|
- swimming will cool down
|
||||||
- standing near fire will warm up
|
- standing near fire will warm up
|
||||||
- craftable warm clothes
|
- craftable warm clothes
|
||||||
- metal armor will worsen heat issues
|
- metal armor will worsen heat issues
|
||||||
|
- A flag indicating wind direction
|
@ -4,9 +4,25 @@ local EFFECT_NAME = "climate_api:hud_overlay"
|
|||||||
|
|
||||||
local handles = {}
|
local handles = {}
|
||||||
local function apply_hud(pname, weather, hud)
|
local function apply_hud(pname, weather, hud)
|
||||||
if handles[pname] == nil then handles[pname] = {} end
|
|
||||||
if handles[pname][weather] ~= nil then return end
|
|
||||||
local player = minetest.get_player_by_name(pname)
|
local player = minetest.get_player_by_name(pname)
|
||||||
|
if handles[pname] == nil then handles[pname] = {} end
|
||||||
|
if handles[pname][weather] ~= nil then
|
||||||
|
player:hud_remove(handles[pname][weather])
|
||||||
|
end
|
||||||
|
|
||||||
|
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 shadow = 256 - light
|
||||||
|
|
||||||
|
local dark_file = hud.file .. "^[multiply:#000000ff^[opacity:" .. shadow
|
||||||
|
local light_file = hud.file .. "^[opacity:" .. light
|
||||||
|
file = "(" .. light_file .. ")^(" .. dark_file .. ")"
|
||||||
|
else
|
||||||
|
file = hud.file
|
||||||
|
end
|
||||||
|
|
||||||
local handle = player:hud_add({
|
local handle = player:hud_add({
|
||||||
name = weather,
|
name = weather,
|
||||||
hud_elem_type = "image",
|
hud_elem_type = "image",
|
||||||
@ -14,7 +30,7 @@ local function apply_hud(pname, weather, hud)
|
|||||||
alignment = {x = 1, y = 1},
|
alignment = {x = 1, y = 1},
|
||||||
scale = { x = -100, y = -100},
|
scale = { x = -100, y = -100},
|
||||||
z_index = hud.z_index,
|
z_index = hud.z_index,
|
||||||
text = hud.file,
|
text = file,
|
||||||
offset = {x = 0, y = 0}
|
offset = {x = 0, y = 0}
|
||||||
})
|
})
|
||||||
handles[pname][weather] = handle
|
handles[pname][weather] = handle
|
||||||
@ -39,7 +55,12 @@ end
|
|||||||
local function handle_effect(player_data, prev_data)
|
local function handle_effect(player_data, prev_data)
|
||||||
for playername, data in pairs(player_data) do
|
for playername, data in pairs(player_data) do
|
||||||
for weather, value in pairs(data) do
|
for weather, value in pairs(data) do
|
||||||
if prev_data[playername][weather] == nil then
|
if prev_data[playername][weather] == nil
|
||||||
|
or value.color_correction == true
|
||||||
|
or prev_data[playername][weather].color_correction == true
|
||||||
|
or value.file ~= prev_data[playername][weather].file
|
||||||
|
or value.z_index ~= prev_data[playername][weather].z_index
|
||||||
|
then
|
||||||
apply_hud(playername, weather, value)
|
apply_hud(playername, weather, value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -11,44 +11,58 @@ end
|
|||||||
|
|
||||||
local function spawn_particles(player, particles)
|
local function spawn_particles(player, particles)
|
||||||
local ppos = player:getpos()
|
local ppos = player:getpos()
|
||||||
local wind_x = climate_mod.state:get_float("wind_x")
|
local wind = climate_api.environment.get_wind()
|
||||||
local wind_z = climate_mod.state:get_float("wind_z")
|
|
||||||
local wind = vector.new(wind_x, 0, wind_z)
|
|
||||||
local wind_pos = vector.multiply(wind, -1)
|
|
||||||
local wind_speed = vector.length(wind)
|
|
||||||
|
|
||||||
local amount = particles.amount * climate_mod.settings.particle_count
|
local amount = particles.amount * climate_mod.settings.particle_count
|
||||||
local texture = get_particle_texture(particles)
|
local texture = get_particle_texture(particles)
|
||||||
|
|
||||||
local minp = vector.add(vector.add(ppos, particles.min_pos), wind_pos)
|
|
||||||
local maxp = vector.add(vector.add(ppos, particles.max_pos), wind_pos)
|
|
||||||
|
|
||||||
local vel = vector.new({
|
local vel = vector.new({
|
||||||
x = wind.x,
|
x = wind.x,
|
||||||
y = -particles.falling_speed,
|
y = -particles.falling_speed,
|
||||||
z = wind.z
|
z = wind.z
|
||||||
})
|
})
|
||||||
local acc = vector.new({x=0, y=0, z=0})
|
|
||||||
|
|
||||||
local exp = particles.exptime
|
if particles.acceleration == nil then
|
||||||
local vertical = math.abs(vector.normalize(vel).y) >= 0.6
|
particles.acceleration = vector.new({x=0, y=0, z=0})
|
||||||
|
end
|
||||||
|
|
||||||
|
local wind_pos = vector.multiply(
|
||||||
|
vector.normalize(vel),
|
||||||
|
-vector.length(wind)
|
||||||
|
)
|
||||||
|
wind_pos.y = 0
|
||||||
|
local minp = vector.add(vector.add(ppos, particles.min_pos), wind_pos)
|
||||||
|
local maxp = vector.add(vector.add(ppos, particles.max_pos), wind_pos)
|
||||||
|
|
||||||
|
if particles.time == nil then
|
||||||
|
particles.time = 0.5
|
||||||
|
end
|
||||||
|
|
||||||
|
if particles.vertical == nil then
|
||||||
|
particles.vertical = math.abs(vector.normalize(vel).y) >= 0.6
|
||||||
|
end
|
||||||
|
|
||||||
|
if particles.size ~= nil then
|
||||||
|
particles.min_size = particles.size
|
||||||
|
particles.max_size = particles.size
|
||||||
|
end
|
||||||
|
|
||||||
minetest.add_particlespawner({
|
minetest.add_particlespawner({
|
||||||
amount = amount,
|
amount = amount,
|
||||||
time = 0.5,
|
time = particles.time,
|
||||||
minpos = minp,
|
minpos = minp,
|
||||||
maxpos = maxp,
|
maxpos = maxp,
|
||||||
minvel = vel,
|
minvel = vel,
|
||||||
maxvel = vel,
|
maxvel = vel,
|
||||||
minacc = acc,
|
minacc = particles.acceleration,
|
||||||
maxacc = acc,
|
maxacc = particles.acceleration,
|
||||||
minexptime = exp,
|
minexptime = particles.exptime,
|
||||||
maxexptime = exp,
|
maxexptime = particles.exptime,
|
||||||
minsize = particles.size,
|
minsize = particles.min_size,
|
||||||
maxsize = particles.size,
|
maxsize = particles.max_size,
|
||||||
collisiondetection = true,
|
collisiondetection = true,
|
||||||
collision_removal = true,
|
collision_removal = true,
|
||||||
vertical = vertical,
|
vertical = particles.vertical,
|
||||||
texture = texture,
|
texture = texture,
|
||||||
player = player:get_player_name()
|
player = player:get_player_name()
|
||||||
})
|
})
|
||||||
|
@ -2,46 +2,30 @@ if not climate_mod.settings.skybox then return end
|
|||||||
|
|
||||||
local EFFECT_NAME = "climate_api:skybox"
|
local EFFECT_NAME = "climate_api:skybox"
|
||||||
|
|
||||||
local function set_skybox(player, sky)
|
local function handle_effect(player_data, prev_data)
|
||||||
if sky.sky_data ~= nil then
|
for playername, data in pairs(prev_data) do
|
||||||
player:set_sky(sky.sky_data)
|
for weather, _ in pairs(data) do
|
||||||
end
|
if player_data[playername] == nil or player_data[playername][weather] == nil then
|
||||||
if sky.cloud_data ~= nil then
|
climate_api.skybox.remove_layer(playername, weather)
|
||||||
player:set_clouds(sky.cloud_data)
|
end
|
||||||
end
|
|
||||||
if sky.moon_data ~= nil then
|
|
||||||
player:set_moon(sky.moon_data)
|
|
||||||
end
|
|
||||||
if sky.sun_data ~= nil then
|
|
||||||
player:set_sun(sky.sun_data)
|
|
||||||
end
|
|
||||||
if sky.stars_data ~= nil then
|
|
||||||
player:set_sun(sky.stars_data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function remove_skybox(player)
|
|
||||||
player:set_sky({ type = "regular", clouds = true})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function handle_effect(player_data)
|
|
||||||
for playername, data in pairs(player_data) do
|
|
||||||
local player = minetest.get_player_by_name(playername)
|
|
||||||
local sky = {}
|
|
||||||
for weather, value in pairs(data) do
|
|
||||||
climate_api.utility.merge_tables(sky, value)
|
|
||||||
end
|
end
|
||||||
set_skybox(player, sky)
|
end
|
||||||
|
for playername, data in pairs(player_data) do
|
||||||
|
for weather, value in pairs(data) do
|
||||||
|
climate_api.skybox.add_layer(playername, weather, value)
|
||||||
|
end
|
||||||
|
climate_api.skybox.update_skybox(playername)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function remove_effect(player_data)
|
local function remove_effect(player_data)
|
||||||
for playername, data in pairs(player_data) do
|
for playername, data in pairs(player_data) do
|
||||||
local player = minetest.get_player_by_name(playername)
|
for weather, _ in pairs(data) do
|
||||||
remove_skybox(player)
|
climate_api.skybox.remove_layer(playername, weather)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
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("climate_api:skybox", climate_api.LONG_CYCLE)
|
climate_api.set_effect_cycle("climate_api:skybox", climate_api.MEDIUM_CYCLE)
|
@ -1,29 +1,23 @@
|
|||||||
if not climate_mod.settings.sound then return end
|
if not climate_mod.settings.sound then return end
|
||||||
|
|
||||||
local EFFECT_NAME = "climate_api:sound"
|
local EFFECT_NAME = "climate_api:sound"
|
||||||
|
local FADE_DURATION = climate_api.LONG_CYCLE
|
||||||
|
|
||||||
local handles = {}
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
local function start_sound(pname, weather, sound)
|
local soundloop = dofile(modpath .. "/lib/soundloop.lua")
|
||||||
if handles[pname] == nil then handles[pname] = {} end
|
|
||||||
if handles[pname][weather] ~= nil then return end
|
local function start_sound(pname, sound)
|
||||||
local handle = minetest.sound_play(sound, {
|
return soundloop.play(pname, sound, FADE_DURATION)
|
||||||
to_player = pname,
|
|
||||||
loop = true
|
|
||||||
})
|
|
||||||
handles[pname][weather] = handle
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function stop_sound(pname, weather, sound)
|
local function stop_sound(pname, sound)
|
||||||
if handles[pname] == nil or handles[pname][weather] == nil then return end
|
return soundloop.stop(pname, sound, FADE_DURATION)
|
||||||
local handle = handles[pname][weather]
|
|
||||||
minetest.sound_stop(handle)
|
|
||||||
handles[pname][weather] = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function start_effect(player_data)
|
local function start_effect(player_data)
|
||||||
for playername, data in pairs(player_data) do
|
for playername, data in pairs(player_data) do
|
||||||
for weather, value in pairs(data) do
|
for weather, value in pairs(data) do
|
||||||
start_sound(playername, weather, value)
|
start_sound(playername, value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -32,7 +26,7 @@ local function handle_effect(player_data, prev_data)
|
|||||||
for playername, data in pairs(player_data) do
|
for playername, data in pairs(player_data) do
|
||||||
for weather, value in pairs(data) do
|
for weather, value in pairs(data) do
|
||||||
if prev_data[playername][weather] == nil then
|
if prev_data[playername][weather] == nil then
|
||||||
start_sound(playername, weather, value)
|
start_sound(playername, value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -40,17 +34,16 @@ local function handle_effect(player_data, prev_data)
|
|||||||
for playername, data in pairs(prev_data) do
|
for playername, data in pairs(prev_data) do
|
||||||
for weather, value in pairs(data) do
|
for weather, value in pairs(data) do
|
||||||
if player_data[playername][weather] == nil then
|
if player_data[playername][weather] == nil then
|
||||||
stop_sound(playername, weather, value)
|
stop_sound(playername, value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function stop_effect(prev_data)
|
local function stop_effect(prev_data)
|
||||||
minetest.log(dump2(prev_data, "stop_effect"))
|
|
||||||
for playername, data in pairs(prev_data) do
|
for playername, data in pairs(prev_data) do
|
||||||
for weather, value in pairs(data) do
|
for weather, value in pairs(data) do
|
||||||
stop_sound(playername, weather, value)
|
stop_sound(playername, value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -58,4 +51,4 @@ end
|
|||||||
climate_api.register_effect(EFFECT_NAME, start_effect, "start")
|
climate_api.register_effect(EFFECT_NAME, start_effect, "start")
|
||||||
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
|
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
|
||||||
climate_api.register_effect(EFFECT_NAME, stop_effect, "stop")
|
climate_api.register_effect(EFFECT_NAME, stop_effect, "stop")
|
||||||
climate_api.set_effect_cycle(EFFECT_NAME, climate_api.LONG_CYCLE)
|
climate_api.set_effect_cycle(EFFECT_NAME, climate_api.MEDIUM_CYCLE)
|
10
init.lua
@ -27,25 +27,33 @@ climate_mod.settings = {
|
|||||||
wind = get_setting_bool("wind", true),
|
wind = get_setting_bool("wind", true),
|
||||||
seasons = get_setting_bool("seasons", true),
|
seasons = get_setting_bool("seasons", true),
|
||||||
fahrenheit = get_setting_bool("fahrenheit", false),
|
fahrenheit = get_setting_bool("fahrenheit", false),
|
||||||
|
block_updates = get_setting_bool("block_updates", true),
|
||||||
heat = get_setting_number("heat_base", 0),
|
heat = get_setting_number("heat_base", 0),
|
||||||
humidity = get_setting_number("humidity_base", 0),
|
humidity = get_setting_number("humidity_base", 0),
|
||||||
time_spread = get_setting_number("time_spread", 1),
|
time_spread = get_setting_number("time_spread", 1),
|
||||||
particle_count = get_setting_number("particle_count", 1)
|
particle_count = get_setting_number("particle_count", 1),
|
||||||
|
tick_speed = get_setting_number("tick_speed", 1),
|
||||||
|
volume = get_setting_number("volume", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
-- initiate empty registers
|
-- initiate empty registers
|
||||||
climate_mod.weathers = {}
|
climate_mod.weathers = {}
|
||||||
climate_mod.effects = {}
|
climate_mod.effects = {}
|
||||||
climate_mod.cycles = {}
|
climate_mod.cycles = {}
|
||||||
|
climate_mod.global_environment = {}
|
||||||
|
climate_mod.global_influences = {}
|
||||||
climate_mod.influences = {}
|
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
|
||||||
|
|
||||||
-- import core API
|
-- import core API
|
||||||
climate_mod.state = dofile(modpath .. "/lib/datastorage.lua")
|
climate_mod.state = dofile(modpath .. "/lib/datastorage.lua")
|
||||||
climate_api = dofile(modpath .. "/lib/api.lua")
|
climate_api = dofile(modpath .. "/lib/api.lua")
|
||||||
climate_api.utility = dofile(modpath .. "/lib/api_utility.lua")
|
climate_api.utility = dofile(modpath .. "/lib/api_utility.lua")
|
||||||
|
climate_api.skybox = dofile(modpath .. "/lib/skybox_merger.lua")
|
||||||
|
dofile(modpath .. "/lib/influences.lua")
|
||||||
climate_api.environment = dofile(modpath .. "/lib/environment.lua")
|
climate_api.environment = dofile(modpath .. "/lib/environment.lua")
|
||||||
--climate_api = dofile(modpath .. "/lib/influences.lua")
|
--climate_api = dofile(modpath .. "/lib/influences.lua")
|
||||||
climate_mod.world = dofile(modpath .. "/lib/world.lua")
|
climate_mod.world = dofile(modpath .. "/lib/world.lua")
|
||||||
|
26
lib/api.lua
@ -17,7 +17,7 @@ end
|
|||||||
function api.register_effect(name, handler, htype)
|
function api.register_effect(name, handler, htype)
|
||||||
-- check for valid handler types
|
-- check for valid handler types
|
||||||
if htype ~= "start" and htype ~= "tick" and htype ~= "stop" then
|
if htype ~= "start" and htype ~= "tick" and htype ~= "stop" then
|
||||||
minetest.log("warning", "[Climate API] Invalid effect handler type: " .. htype)
|
minetest.log("warning", "[Climate API] Effect " .. dump(name) .. " uses invalid callback type: " .. dump(htype))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- create effect handler registry if not existent yet
|
-- create effect handler registry if not existent yet
|
||||||
@ -33,11 +33,17 @@ function api.set_effect_cycle(name, cycle)
|
|||||||
climate_mod.cycles[name].timespan = cycle
|
climate_mod.cycles[name].timespan = cycle
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[function api.register_influence(name, func)
|
function api.register_global_influence(name, func)
|
||||||
|
climate_mod.global_influences[name] = func
|
||||||
|
end
|
||||||
|
|
||||||
|
function api.register_influence(name, func)
|
||||||
climate_mod.influences[name] = func
|
climate_mod.influences[name] = func
|
||||||
end]]
|
end
|
||||||
|
|
||||||
function api.register_abm(config)
|
function api.register_abm(config)
|
||||||
|
if not climate_mod.settings.block_updates then return end
|
||||||
|
|
||||||
local conditions = config.conditions
|
local conditions = config.conditions
|
||||||
local action = config.action
|
local action = config.action
|
||||||
local pos_override = config.pos_override
|
local pos_override = config.pos_override
|
||||||
@ -52,19 +58,9 @@ function api.register_abm(config)
|
|||||||
return action(pos, node, env)
|
return action(pos, node, env)
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.log(dump2(env, "env"))
|
|
||||||
minetest.log(dump2(conditions, "conditions"))
|
|
||||||
|
|
||||||
for condition, goal in pairs(conditions) do
|
for condition, goal in pairs(conditions) do
|
||||||
local value = env[condition:sub(5)]
|
local is_applicable = climate_mod.trigger.test_condition(condition, env, goal)
|
||||||
if condition:sub(1, 4) == "min_" then
|
if not is_applicable then return end
|
||||||
if type(value) == "nil" or goal > value then return end
|
|
||||||
elseif condition:sub(1, 4) == "max_" then
|
|
||||||
if type(value) == "nil" or goal <= value then return end
|
|
||||||
else
|
|
||||||
value = env[condition]
|
|
||||||
if type(value) == "nil" or goal ~= value then return end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return action(pos, node, env)
|
return action(pos, node, env)
|
||||||
end
|
end
|
||||||
|
@ -77,6 +77,32 @@ minetest.register_chatcommand("set_humidity", {
|
|||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
|
minetest.register_chatcommand("set_wind", {
|
||||||
|
params = "<wind>",
|
||||||
|
description = "Override the weather algorithm's windspeed",
|
||||||
|
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
|
||||||
|
local arguments = {}
|
||||||
|
for w in param:gmatch("%S+") do table.insert(arguments, w) end
|
||||||
|
local wind_x = arguments[1]
|
||||||
|
local wind_z = arguments[2]
|
||||||
|
if wind_x == "auto" then
|
||||||
|
climate_mod.forced_wind = nil
|
||||||
|
else
|
||||||
|
climate_mod.forced_wind = vector.new({
|
||||||
|
x = tonumber(wind_x),
|
||||||
|
y = 0,
|
||||||
|
z = tonumber(wind_z)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
minetest.chat_send_player(playername, "Wind changed")
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
minetest.register_chatcommand("weather_settings", {
|
minetest.register_chatcommand("weather_settings", {
|
||||||
description = "Print the active Climate API configuration",
|
description = "Print the active Climate API configuration",
|
||||||
func = function(playername)
|
func = function(playername)
|
||||||
@ -130,3 +156,15 @@ minetest.register_chatcommand("weather_status", {
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
|
minetest.register_chatcommand("weather_influences", {
|
||||||
|
description = "Prints which weather influences cause your current weather",
|
||||||
|
func = function(playername)
|
||||||
|
minetest.chat_send_player(playername, "Current influences rules:\n================")
|
||||||
|
local player = minetest.get_player_by_name(playername)
|
||||||
|
local influences = climate_mod.trigger.get_player_environment(player)
|
||||||
|
for influence, value in pairs(influences) do
|
||||||
|
minetest.chat_send_player(playername, dump2(value, influence))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
@ -5,13 +5,6 @@ local function get_heat_time()
|
|||||||
return climate_api.utility.normalized_cycle(time) * 0.6 + 0.7
|
return climate_api.utility.normalized_cycle(time) * 0.6 + 0.7
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_heat_calendar()
|
|
||||||
-- European heat center in August instead of June
|
|
||||||
local day = minetest.get_day_count()
|
|
||||||
local progression = ((day + 61) % 365) / 365
|
|
||||||
return climate_api.utility.normalized_cycle(progression) * 0.6 + 0.7
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_heat_height(y)
|
local function get_heat_height(y)
|
||||||
return climate_api.utility.rangelim((-y + 10) / 15, -10, 10)
|
return climate_api.utility.rangelim((-y + 10) / 15, -10, 10)
|
||||||
end
|
end
|
||||||
@ -21,9 +14,8 @@ function environment.get_heat(pos)
|
|||||||
local biome = minetest.get_heat(pos)
|
local biome = minetest.get_heat(pos)
|
||||||
local height = get_heat_height(pos.y)
|
local height = get_heat_height(pos.y)
|
||||||
local time = get_heat_time()
|
local time = get_heat_time()
|
||||||
local date = get_heat_calendar()
|
|
||||||
local random = climate_mod.state:get_float("heat_random");
|
local random = climate_mod.state:get_float("heat_random");
|
||||||
return (base + biome + height) * time * date * random
|
return (base + biome + height) * time * random
|
||||||
end
|
end
|
||||||
|
|
||||||
function environment.get_humidity(pos)
|
function environment.get_humidity(pos)
|
||||||
@ -31,16 +23,18 @@ function environment.get_humidity(pos)
|
|||||||
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");
|
||||||
local random_base = climate_mod.state:get_float("humidity_base");
|
local random_base = climate_mod.state:get_float("humidity_base");
|
||||||
--[[for _, player in ipairs(minetest.get_connected_players()) do
|
|
||||||
local pname = player:get_player_name()
|
|
||||||
minetest.chat_send_player(pname, dump2(biome, "biome"))
|
|
||||||
minetest.chat_send_player(pname, dump2(random_base, "random_base"))
|
|
||||||
minetest.chat_send_player(pname, dump2(random, "random"))
|
|
||||||
minetest.chat_send_player(pname, dump2((base + biome * 0.7 + random_base * 0.3) * random, "total"))
|
|
||||||
end]]
|
|
||||||
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()
|
||||||
|
if climate_mod.forced_wind ~= nil then
|
||||||
|
return climate_mod.forced_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 })
|
||||||
|
end
|
||||||
|
|
||||||
function environment.get_weather_presets(player)
|
function environment.get_weather_presets(player)
|
||||||
local pname = player:get_player_name()
|
local pname = player:get_player_name()
|
||||||
local weathers = climate_mod.current_weather[pname]
|
local weathers = climate_mod.current_weather[pname]
|
||||||
|
@ -1,30 +1,50 @@
|
|||||||
climate_api.register_influence("heat", function(player)
|
climate_api.register_influence("heat", function(pos)
|
||||||
return climate_mod.get_heat(player:get_pos())
|
return climate_api.environment.get_heat(pos)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
climate_api.register_influence("humidity", function(player)
|
climate_api.register_influence("base_heat", function(pos)
|
||||||
return climate_mod.get_humidity(player:get_pos())
|
return minetest.get_heat(pos)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
climate_api.register_influence("windspeed", function(player)
|
climate_api.register_influence("humidity", function(pos)
|
||||||
local wind_x = climate_mod.state:get_float("wind_x")
|
return climate_api.environment.get_humidity(pos)
|
||||||
local wind_z = climate_mod.state:get_float("wind_z")
|
|
||||||
return vector.length({x = wind_x, y = 0, z = wind_z})
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
climate_api.register_influence("wind_x", function(player)
|
climate_api.register_influence("base_humidity", function(pos)
|
||||||
return climate_mod.state:get_float("wind_x")
|
return minetest.get_humidity(pos)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
climate_api.register_influence("wind_z", function(player)
|
climate_api.register_influence("biome", function(pos)
|
||||||
return climate_mod.state:get_float("wind_z")
|
local data = minetest.get_biome_data(pos)
|
||||||
|
local biome = minetest.get_biome_name(data.biome)
|
||||||
|
return biome
|
||||||
end)
|
end)
|
||||||
|
|
||||||
climate_api.register_influence("height", function(player)
|
climate_api.register_global_influence("windspeed", function()
|
||||||
local ppos = player:get_pos()
|
local wind = climate_api.environment.get_wind()
|
||||||
return ppos.y
|
return vector.length(wind)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
climate_api.register_influence("light", function(player)
|
climate_api.register_global_influence("wind_yaw", function()
|
||||||
return minetest.env:get_node_light(player:get_pos(), 0.5)
|
local wind = climate_api.environment.get_wind()
|
||||||
|
if vector.length(wind) == 0 then return 0 end
|
||||||
|
return minetest.dir_to_yaw(wind)
|
||||||
|
end)
|
||||||
|
|
||||||
|
climate_api.register_influence("height", function(pos)
|
||||||
|
return pos.y
|
||||||
|
end)
|
||||||
|
|
||||||
|
climate_api.register_influence("light", function(pos)
|
||||||
|
pos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||||
|
return minetest.env:get_node_light(pos)
|
||||||
|
end)
|
||||||
|
|
||||||
|
climate_api.register_influence("daylight", function(pos)
|
||||||
|
pos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||||
|
return minetest.env:get_node_light(pos, 0.5)
|
||||||
|
end)
|
||||||
|
|
||||||
|
climate_api.register_global_influence("time", function()
|
||||||
|
return minetest.get_timeofday()
|
||||||
end)
|
end)
|
@ -1,5 +1,5 @@
|
|||||||
local GSCYCLE = 0.03
|
local GSCYCLE = 0.03 * climate_mod.settings.tick_speed
|
||||||
local WORLD_CYCLE = 2
|
local WORLD_CYCLE = 2 * climate_mod.settings.tick_speed
|
||||||
|
|
||||||
local gs_timer = 0
|
local gs_timer = 0
|
||||||
local world_timer = 0
|
local world_timer = 0
|
||||||
@ -14,13 +14,15 @@ minetest.register_globalstep(function(dtime)
|
|||||||
world_timer = 0
|
world_timer = 0
|
||||||
climate_mod.state:set_float("noise_timer", noise_timer)
|
climate_mod.state:set_float("noise_timer", noise_timer)
|
||||||
climate_mod.world.update_status(noise_timer)
|
climate_mod.world.update_status(noise_timer)
|
||||||
|
climate_mod.global_environment = climate_mod.trigger.get_global_environment()
|
||||||
end
|
end
|
||||||
|
|
||||||
local previous_effects = table.copy(climate_mod.current_effects)
|
local previous_effects = table.copy(climate_mod.current_effects)
|
||||||
local current_effects = climate_mod.trigger.get_active_effects()
|
local current_effects = climate_mod.trigger.get_active_effects()
|
||||||
|
|
||||||
for name, effect in pairs(climate_mod.effects) do
|
for name, effect in pairs(climate_mod.effects) do
|
||||||
if climate_mod.cycles[name].timespan < climate_mod.cycles[name].timer + dtime then
|
local cycle = climate_mod.cycles[name].timespan * climate_mod.settings.tick_speed
|
||||||
|
if cycle < climate_mod.cycles[name].timer + dtime then
|
||||||
climate_mod.cycles[name].timer = 0
|
climate_mod.cycles[name].timer = 0
|
||||||
climate_mod.current_effects[name] = current_effects[name]
|
climate_mod.current_effects[name] = current_effects[name]
|
||||||
climate_mod.trigger.call_handlers(name, current_effects[name], previous_effects[name])
|
climate_mod.trigger.call_handlers(name, current_effects[name], previous_effects[name])
|
||||||
|
109
lib/skybox_merger.lua
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
local default_sky = {
|
||||||
|
sky_data = {
|
||||||
|
base_color = nil,
|
||||||
|
type = "regular",
|
||||||
|
textures = nil,
|
||||||
|
clouds = true,
|
||||||
|
sky_color = {
|
||||||
|
day_sky = "#8cbafa",
|
||||||
|
day_horizon = "#9bc1f0",
|
||||||
|
dawn_sky = "#b4bafa",
|
||||||
|
dawn_horizon = "#bac1f0",
|
||||||
|
night_sky = "#006aff",
|
||||||
|
night_horizon = "#4090ff",
|
||||||
|
indoors = "#646464",
|
||||||
|
fog_tint_type = "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cloud_data = {
|
||||||
|
density = 0.4,
|
||||||
|
color = "#fff0f0e5",
|
||||||
|
ambient = "#000000",
|
||||||
|
height = 120,
|
||||||
|
thickness = 16,
|
||||||
|
speed = {x=0, z=-2}
|
||||||
|
},
|
||||||
|
sun_data = {
|
||||||
|
visible = true,
|
||||||
|
texture = "sun.png",
|
||||||
|
tonemap = "sun_tonemap.png",
|
||||||
|
sunrise = "sunrisebg.png",
|
||||||
|
sunrise_visible = true,
|
||||||
|
scale = 1
|
||||||
|
},
|
||||||
|
moon_data = {
|
||||||
|
visible = true,
|
||||||
|
texture = "moon.png",
|
||||||
|
tonemap = "moon_tonemap.png",
|
||||||
|
scale = 1
|
||||||
|
},
|
||||||
|
star_data = {
|
||||||
|
visible = true,
|
||||||
|
count = 1000,
|
||||||
|
star_color = "#ebebff69",
|
||||||
|
scale = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local skybox = {}
|
||||||
|
local layers = {}
|
||||||
|
|
||||||
|
-- from https://stackoverflow.com/a/29133654
|
||||||
|
-- merges two tables together
|
||||||
|
-- if in conflict, b will override values of a
|
||||||
|
local function merge_tables(a, b)
|
||||||
|
if type(a) == "table" and type(b) == "table" then
|
||||||
|
for k,v in pairs(b) do
|
||||||
|
if type(v)=="table" and type(a[k] or false)=="table" then
|
||||||
|
merge_tables(a[k],v)
|
||||||
|
else a[k]=v end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
|
||||||
|
local function set_skybox(playername, sky)
|
||||||
|
local player = minetest.get_player_by_name(playername)
|
||||||
|
if not player.get_stars then return end
|
||||||
|
player:set_sky(sky.sky_data)
|
||||||
|
player:set_clouds(sky.cloud_data)
|
||||||
|
player:set_moon(sky.moon_data)
|
||||||
|
player:set_sun(sky.sun_data)
|
||||||
|
player:set_stars(sky.star_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
function skybox.update_skybox(playername)
|
||||||
|
local p_layers = layers[playername]
|
||||||
|
local sky = table.copy(default_sky)
|
||||||
|
if p_layers == nil then p_layers = {} end
|
||||||
|
local numbered_layers = {}
|
||||||
|
for layer, values in pairs(p_layers) do
|
||||||
|
table.insert(numbered_layers, values)
|
||||||
|
end
|
||||||
|
table.sort(numbered_layers, function(left, right)
|
||||||
|
if left.priority == nil then left.priority = 1 end
|
||||||
|
if right.priority == nil then right.priority = 1 end
|
||||||
|
return left.priority < right.priority
|
||||||
|
end)
|
||||||
|
for i=1,#numbered_layers do
|
||||||
|
sky = merge_tables(sky, numbered_layers[i])
|
||||||
|
end
|
||||||
|
set_skybox(playername, sky)
|
||||||
|
end
|
||||||
|
|
||||||
|
function skybox.add_layer(playername, name, sky)
|
||||||
|
if layers[playername] == nil then layers[playername] = {} end
|
||||||
|
layers[playername][name] = sky
|
||||||
|
end
|
||||||
|
|
||||||
|
function skybox.remove_layer(playername, name)
|
||||||
|
if layers[playername] == nil or layers[playername][name] == nil then return end
|
||||||
|
layers[playername][name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_on_leaveplayer(function(player)
|
||||||
|
local playername = player:get_player_name()
|
||||||
|
layers[playername] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
return skybox
|
57
lib/soundloop.lua
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
local soundloop = {}
|
||||||
|
local sounds = {}
|
||||||
|
|
||||||
|
local function parse_sound(sound)
|
||||||
|
if type(sound) == "string" then
|
||||||
|
return { name = sound, gain = 1, pitch = 1 }
|
||||||
|
end
|
||||||
|
if sound.gain == nil then sound.gain = 1 end
|
||||||
|
if sound.pitch == nil then sound.pitch = 1 end
|
||||||
|
return sound
|
||||||
|
end
|
||||||
|
|
||||||
|
soundloop.play = function(player, sound, fade)
|
||||||
|
sound = parse_sound(sound)
|
||||||
|
if fade == nil then fade = 1 end
|
||||||
|
local step
|
||||||
|
local handle
|
||||||
|
local start_gain
|
||||||
|
if sounds[player] == nil then sounds[player] = {} end
|
||||||
|
if sounds[player][sound.name] == nil then
|
||||||
|
step = sound.gain / fade
|
||||||
|
start_gain = 0
|
||||||
|
elseif sounds[player][sound.name] ~= sound.gain then
|
||||||
|
minetest.sound_stop(sounds[player][sound.name].handle)
|
||||||
|
start_gain = sounds[player][sound.name].gain
|
||||||
|
local change = sound.gain - start_gain
|
||||||
|
step = change / fade
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
handle = minetest.sound_play(sound.name, {
|
||||||
|
to_player = player,
|
||||||
|
loop = true,
|
||||||
|
gain = 0
|
||||||
|
})
|
||||||
|
sounds[player][sound.name] = {
|
||||||
|
gain = sound.gain,
|
||||||
|
handle = handle
|
||||||
|
}
|
||||||
|
minetest.sound_fade(handle, step, sound.gain)
|
||||||
|
return handle
|
||||||
|
end
|
||||||
|
|
||||||
|
soundloop.stop = function(player, sound, fade)
|
||||||
|
sound = parse_sound(sound)
|
||||||
|
if sounds[player] == nil or sounds[player][sound.name] == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if fade == nil then fade = 1 end
|
||||||
|
local handle = sounds[player][sound.name].handle
|
||||||
|
local step = -sounds[player][sound.name].gain / fade
|
||||||
|
minetest.sound_fade(handle, step, 0)
|
||||||
|
sounds[player][sound.name].gain = 0
|
||||||
|
minetest.after(fade, minetest.sound_stop, handle)
|
||||||
|
end
|
||||||
|
|
||||||
|
return soundloop
|
@ -1,19 +1,18 @@
|
|||||||
local trigger = {}
|
local trigger = {}
|
||||||
|
|
||||||
function trigger.get_position_environment(pos)
|
function trigger.get_global_environment()
|
||||||
local wind_x = climate_mod.state:get_float("wind_x")
|
|
||||||
local wind_z = climate_mod.state:get_float("wind_z")
|
|
||||||
|
|
||||||
local env = {}
|
local env = {}
|
||||||
env.pos = pos
|
for influence, func in pairs(climate_mod.global_influences) do
|
||||||
env.height = pos.y
|
env[influence] = func()
|
||||||
env.wind = vector.new(wind_x, 0, wind_z)
|
end
|
||||||
env.windspeed = vector.length(env.wind)
|
return env
|
||||||
env.heat = climate_api.environment.get_heat(pos)
|
end
|
||||||
env.humidity = climate_api.environment.get_humidity(pos)
|
|
||||||
env.time = minetest.get_timeofday()
|
function trigger.get_position_environment(pos)
|
||||||
env.date = minetest.get_day_count()
|
local env = table.copy(climate_mod.global_environment)
|
||||||
env.light = minetest.get_node_light(vector.add(pos, vector.new({x=0,y=1,z=0})), 0.5)
|
for influence, func in pairs(climate_mod.influences) do
|
||||||
|
env[influence] = func(pos)
|
||||||
|
end
|
||||||
return env
|
return env
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -24,15 +23,21 @@ function trigger.get_player_environment(player)
|
|||||||
return env
|
return env
|
||||||
end
|
end
|
||||||
|
|
||||||
local function test_condition(condition, env, goal)
|
function trigger.test_condition(condition, env, goal)
|
||||||
local value = env[condition:sub(5)]
|
local value = env[condition:sub(5)]
|
||||||
if condition:sub(1, 4) == "min_" then
|
if condition:sub(1, 4) == "min_" then
|
||||||
return type(value) ~= "nil" and goal <= value
|
return type(value) ~= "nil" and goal <= value
|
||||||
elseif condition:sub(1, 4) == "max_" then
|
elseif condition:sub(1, 4) == "max_" then
|
||||||
return type(value) ~= "nil" and goal > value
|
return type(value) ~= "nil" and goal > value
|
||||||
else
|
elseif condition:sub(1, 4) == "has_" then
|
||||||
Minetest.log("warning", "[Climate API] Invalid effect condition")
|
if type(value) == "nil" then return false end
|
||||||
|
for _, g in ipairs(goal) do
|
||||||
|
if value == g then return true end
|
||||||
|
end
|
||||||
return false
|
return false
|
||||||
|
else
|
||||||
|
value = env[condition]
|
||||||
|
return type(value) ~= "nil" and goal == value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -45,7 +50,7 @@ local function is_weather_active(player, weather, env)
|
|||||||
return config.conditions(env)
|
return config.conditions(env)
|
||||||
end
|
end
|
||||||
for condition, goal in pairs(config.conditions) do
|
for condition, goal in pairs(config.conditions) do
|
||||||
if not test_condition(condition, env, goal) then
|
if not trigger.test_condition(condition, env, goal) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -145,7 +150,6 @@ function trigger.call_handlers(name, effect, prev_effect)
|
|||||||
|
|
||||||
-- remaining table lists ending effects
|
-- remaining table lists ending effects
|
||||||
if has_stops then
|
if has_stops then
|
||||||
minetest.log(dump2(name, "AAAAAAAAAAA"))
|
|
||||||
for _, handler in ipairs(climate_mod.effects[name]["stop"]) do
|
for _, handler in ipairs(climate_mod.effects[name]["stop"]) do
|
||||||
handler(stops)
|
handler(stops)
|
||||||
end
|
end
|
||||||
|
@ -2,11 +2,11 @@ local world = {}
|
|||||||
|
|
||||||
local WIND_SPREAD = 600
|
local WIND_SPREAD = 600
|
||||||
local WIND_SCALE = 2
|
local WIND_SCALE = 2
|
||||||
local HEAT_SPREAD = 200
|
local HEAT_SPREAD = 400
|
||||||
local HEAT_SCALE = 0.3
|
local HEAT_SCALE = 0.3
|
||||||
local HUMIDITY_SPREAD = 60
|
local HUMIDITY_SPREAD = 150
|
||||||
local HUMIDITY_SCALE = 0.5
|
local HUMIDITY_SCALE = 0.5
|
||||||
local HUMIDITY_BASE_SPREAD = 600
|
local HUMIDITY_BASE_SPREAD = 800
|
||||||
local HUMIDITY_BASE_SCALE = 40
|
local HUMIDITY_BASE_SCALE = 40
|
||||||
|
|
||||||
local nobj_wind_x
|
local nobj_wind_x
|
||||||
|
2
mod.conf
@ -1,7 +1,7 @@
|
|||||||
name = climate_api
|
name = climate_api
|
||||||
title = Climate API
|
title = Climate API
|
||||||
author = TestificateMods
|
author = TestificateMods
|
||||||
release = 1
|
release = 2
|
||||||
optional_depends = skylayer, player_monoids, playerphysics
|
optional_depends = skylayer, player_monoids, playerphysics
|
||||||
description = """
|
description = """
|
||||||
A powerful engine for weather presets and visual effects.
|
A powerful engine for weather presets and visual effects.
|
||||||
|
BIN
screenshot.2.png
Before Width: | Height: | Size: 2.0 MiB |
BIN
screenshot.3.png
Before Width: | Height: | Size: 2.0 MiB |
BIN
screenshot.png
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.6 MiB |
@ -1,11 +1,64 @@
|
|||||||
climate_api_particles (Show particle effects) bool true
|
[Performance]
|
||||||
climate_api_skybox (Allow weather effects to modify the skybox) bool true
|
|
||||||
climate_api_sound (Allow weather presets to play ambient sounds) bool true
|
# This value regulates how often weather presets are recalculated.
|
||||||
climate_api_hud_overlay (Allow weather presets to display a HUD overlay) bool true
|
# Higher values will result in smoother transitions between effects as well as faster response times to traveling players.
|
||||||
climate_api_wind (Allow wind to angle rainfall) bool true
|
# Lower values will significantly increase overall performance at the cost of rougher looking effects.
|
||||||
climate_api_seasons (Change global temperature based on an annual cycle) bool true
|
climate_api_tick_speed (Update speed of weather effects) float 1 0.1 10
|
||||||
climate_api_heat_base (Global base temperature) float 0
|
|
||||||
climate_api_humidity_base (Global base humidity) float 0
|
# This value regulated how many particles will be spawned.
|
||||||
climate_api_time_spread (Regulates how quickly the weather changes) float 1 0.1 10
|
# 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
|
climate_api_particle_count (Multiplicator for used particles) float 1 0.1 2
|
||||||
climate_api_fahrenheit (Show degrees in Fahrenheit instead of Celsius) bool false
|
|
||||||
|
# If set to true, weather packs are allowed to register node update handlers.
|
||||||
|
# These can be used to dynamically place snow layers, melt ice, or hydrate soil.
|
||||||
|
climate_api_block_updates (Dynamically modify nodes) bool true
|
||||||
|
|
||||||
|
|
||||||
|
[Visuals]
|
||||||
|
# If set to true, weather effects (like rain) are allowed to render particles.
|
||||||
|
# Deactivating this feature will prevent some presets from being visible.
|
||||||
|
# For performance considerations it is recommended to decrease the amount of particles instead.
|
||||||
|
climate_api_particles (Show particle effects) bool true
|
||||||
|
|
||||||
|
# If set to true, weather effects are allowed to modify a player's sky.
|
||||||
|
# This includes skybox, sun, moon, and clouds (also used for fog effects).
|
||||||
|
# Running this mod on Minetest 5.1.2 or earlier versions will automatically disable this feature.
|
||||||
|
climate_api_skybox (Override the skybox) bool true
|
||||||
|
|
||||||
|
# If set to true, weather effects are allowed to render an image on top of the gameplay.
|
||||||
|
# This is usually an optional effect used to increase immersion (like a frozen-over camera in a snow storm).
|
||||||
|
climate_api_hud_overlay (Display HUD overlays) bool true
|
||||||
|
|
||||||
|
|
||||||
|
[Environment]
|
||||||
|
|
||||||
|
# This value will be added to all biome's base temperatures before applying random modifiers.
|
||||||
|
# Every unit here will increase the global base heat by one degree Fahrenheit.
|
||||||
|
# Negative values will cool down global base heat respectively.
|
||||||
|
climate_api_heat_base (Global base temperature) float 0
|
||||||
|
|
||||||
|
# This value will be added to all biome's base humidity before applying random modifiers.
|
||||||
|
# Every unit here will increase the global base humidity by one percent.
|
||||||
|
# Negative values will dry up global base humidity respectively.
|
||||||
|
climate_api_humidity_base (Global base humidity) float 0
|
||||||
|
|
||||||
|
# This value regulates how quickly environment factors like heat, humidity and wind are changing.
|
||||||
|
# A value of 2 will double the speed at which weather presets change.
|
||||||
|
# A value of 0.5 will half the speed respectively.
|
||||||
|
climate_api_time_spread (Time rate of weather changes) float 1 0.1 10
|
||||||
|
|
||||||
|
|
||||||
|
[Preferences]
|
||||||
|
|
||||||
|
# If set to true, temperature information in /weather command will be displayed in Fahrenheit.
|
||||||
|
climate_api_fahrenheit (Show degrees in Fahrenheit instead of Celsius) bool false
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
climate_api_sound (Play ambient sound loops) bool true
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
climate_api_volume (Volume of sound effects) float 1 0 10
|
||||||
|
Before Width: | Height: | Size: 2.2 MiB |
BIN
textures/moon.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
textures/sun.png
Normal file
After Width: | Height: | Size: 1.2 KiB |