8 Commits

24 changed files with 451 additions and 164 deletions

View File

@ -8,8 +8,12 @@ Climate API provides temperature and humidity values on a block-per-block basis
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.
## Assets
- Sun and moon textures: *CC BY-SA (3.0)* by Cap
## 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/
- Liberation Fonts (for the text): *OFL*, see https://github.com/liberationfonts/liberation-fonts
- Used texture pack: Polygonia (128px edition) *CC BY-SA (4.0)* by Lokrates. See https://forum.minetest.net/viewtopic.php?f=4&t=19043

View File

@ -1,30 +1,19 @@
# 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)
- Write helpful README
## Planned for first release
- Improve test_condition function
- Write helpful README
- Set effects on player join
- 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
- Improve value structures of particle effects
- Find good values for weather conditions
- Write documentation on how to add weathers and effects
- Ability to register global influences that are the same for any position
## Nice to have
- Write documentation on how to add weathers and effects
- Assign meta data (like "downfall", "wind", etc.) to weather presets
- Fog effects
- 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
- Create conditions for time of day, annual progression, biome filters
- Fork lightning so that it uses skylayer
- Support for sail boats mod
- Generate wind based on speed and yaw instead of x and z values
## Future Plans & Ideas
- Complete season system
@ -52,3 +41,4 @@
- standing near fire will warm up
- craftable warm clothes
- metal armor will worsen heat issues
- A flag indicating wind direction

View File

@ -4,9 +4,25 @@ local EFFECT_NAME = "climate_api:hud_overlay"
local handles = {}
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)
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({
name = weather,
hud_elem_type = "image",
@ -14,7 +30,7 @@ local function apply_hud(pname, weather, hud)
alignment = {x = 1, y = 1},
scale = { x = -100, y = -100},
z_index = hud.z_index,
text = hud.file,
text = file,
offset = {x = 0, y = 0}
})
handles[pname][weather] = handle
@ -39,7 +55,12 @@ end
local function handle_effect(player_data, prev_data)
for playername, data in pairs(player_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)
end
end

View File

@ -11,44 +11,58 @@ end
local function spawn_particles(player, particles)
local ppos = player:getpos()
local wind_x = climate_mod.state:get_float("wind_x")
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 wind = climate_api.environment.get_wind()
local amount = particles.amount * climate_mod.settings.particle_count
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({
x = wind.x,
y = -particles.falling_speed,
z = wind.z
})
local acc = vector.new({x=0, y=0, z=0})
local exp = particles.exptime
local vertical = math.abs(vector.normalize(vel).y) >= 0.6
if particles.acceleration == nil then
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({
amount = amount,
time = 0.5,
time = particles.time,
minpos = minp,
maxpos = maxp,
minvel = vel,
maxvel = vel,
minacc = acc,
maxacc = acc,
minexptime = exp,
maxexptime = exp,
minsize = particles.size,
maxsize = particles.size,
minacc = particles.acceleration,
maxacc = particles.acceleration,
minexptime = particles.exptime,
maxexptime = particles.exptime,
minsize = particles.min_size,
maxsize = particles.max_size,
collisiondetection = true,
collision_removal = true,
vertical = vertical,
vertical = particles.vertical,
texture = texture,
player = player:get_player_name()
})

View File

@ -2,46 +2,30 @@ if not climate_mod.settings.skybox then return end
local EFFECT_NAME = "climate_api:skybox"
local function set_skybox(player, sky)
if sky.sky_data ~= nil then
player:set_sky(sky.sky_data)
end
if sky.cloud_data ~= nil then
player:set_clouds(sky.cloud_data)
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)
local function handle_effect(player_data, prev_data)
for playername, data in pairs(prev_data) do
for weather, _ in pairs(data) do
if player_data[playername] == nil or player_data[playername][weather] == nil then
climate_api.skybox.remove_layer(playername, weather)
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
local function remove_effect(player_data)
for playername, data in pairs(player_data) do
local player = minetest.get_player_by_name(playername)
remove_skybox(player)
for weather, _ in pairs(data) do
climate_api.skybox.remove_layer(playername, weather)
end
end
end
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
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)

View File

@ -1,29 +1,23 @@
if not climate_mod.settings.sound then return end
local EFFECT_NAME = "climate_api:sound"
local FADE_DURATION = climate_api.LONG_CYCLE
local handles = {}
local function start_sound(pname, weather, sound)
if handles[pname] == nil then handles[pname] = {} end
if handles[pname][weather] ~= nil then return end
local handle = minetest.sound_play(sound, {
to_player = pname,
loop = true
})
handles[pname][weather] = handle
local modpath = minetest.get_modpath(minetest.get_current_modname())
local soundloop = dofile(modpath .. "/lib/soundloop.lua")
local function start_sound(pname, sound)
return soundloop.play(pname, sound, FADE_DURATION)
end
local function stop_sound(pname, weather, sound)
if handles[pname] == nil or handles[pname][weather] == nil then return end
local handle = handles[pname][weather]
minetest.sound_stop(handle)
handles[pname][weather] = nil
local function stop_sound(pname, sound)
return soundloop.stop(pname, sound, FADE_DURATION)
end
local function start_effect(player_data)
for playername, data in pairs(player_data) do
for weather, value in pairs(data) do
start_sound(playername, weather, value)
start_sound(playername, value)
end
end
end
@ -32,7 +26,7 @@ local function handle_effect(player_data, prev_data)
for playername, data in pairs(player_data) do
for weather, value in pairs(data) do
if prev_data[playername][weather] == nil then
start_sound(playername, weather, value)
start_sound(playername, value)
end
end
end
@ -40,17 +34,16 @@ local function handle_effect(player_data, prev_data)
for playername, data in pairs(prev_data) do
for weather, value in pairs(data) do
if player_data[playername][weather] == nil then
stop_sound(playername, weather, value)
stop_sound(playername, value)
end
end
end
end
local function stop_effect(prev_data)
minetest.log(dump2(prev_data, "stop_effect"))
for playername, data in pairs(prev_data) do
for weather, value in pairs(data) do
stop_sound(playername, weather, value)
stop_sound(playername, value)
end
end
end
@ -58,4 +51,4 @@ end
climate_api.register_effect(EFFECT_NAME, start_effect, "start")
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
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)

View File

@ -27,10 +27,13 @@ climate_mod.settings = {
wind = get_setting_bool("wind", true),
seasons = get_setting_bool("seasons", true),
fahrenheit = get_setting_bool("fahrenheit", false),
block_updates = get_setting_bool("block_updates", true),
heat = get_setting_number("heat_base", 0),
humidity = get_setting_number("humidity_base", 0),
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
@ -41,11 +44,14 @@ climate_mod.influences = {}
climate_mod.current_weather = {}
climate_mod.current_effects = {}
climate_mod.forced_weather = {}
climate_mod.forced_wind = nil
-- import core API
climate_mod.state = dofile(modpath .. "/lib/datastorage.lua")
climate_api = dofile(modpath .. "/lib/api.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 = dofile(modpath .. "/lib/influences.lua")
climate_mod.world = dofile(modpath .. "/lib/world.lua")

View File

@ -17,7 +17,7 @@ end
function api.register_effect(name, handler, htype)
-- check for valid handler types
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
end
-- create effect handler registry if not existent yet
@ -33,11 +33,13 @@ function api.set_effect_cycle(name, cycle)
climate_mod.cycles[name].timespan = cycle
end
--[[function api.register_influence(name, func)
function api.register_influence(name, func)
climate_mod.influences[name] = func
end]]
end
function api.register_abm(config)
if not climate_mod.settings.block_updates then return end
local conditions = config.conditions
local action = config.action
local pos_override = config.pos_override
@ -52,19 +54,9 @@ function api.register_abm(config)
return action(pos, node, env)
end
minetest.log(dump2(env, "env"))
minetest.log(dump2(conditions, "conditions"))
for condition, goal in pairs(conditions) do
local value = env[condition:sub(5)]
if condition:sub(1, 4) == "min_" then
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
local is_applicable = climate_mod.trigger.test_condition(condition, env, goal)
if not is_applicable then return end
end
return action(pos, node, env)
end

View File

@ -77,6 +77,32 @@ minetest.register_chatcommand("set_humidity", {
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", {
description = "Print the active Climate API configuration",
func = function(playername)
@ -130,3 +156,15 @@ minetest.register_chatcommand("weather_status", {
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
})

View File

@ -5,13 +5,6 @@ local function get_heat_time()
return climate_api.utility.normalized_cycle(time) * 0.6 + 0.7
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)
return climate_api.utility.rangelim((-y + 10) / 15, -10, 10)
end
@ -21,9 +14,8 @@ function environment.get_heat(pos)
local biome = minetest.get_heat(pos)
local height = get_heat_height(pos.y)
local time = get_heat_time()
local date = get_heat_calendar()
local random = climate_mod.state:get_float("heat_random");
return (base + biome + height) * time * date * random
return (base + biome + height) * time * random
end
function environment.get_humidity(pos)
@ -31,16 +23,18 @@ function environment.get_humidity(pos)
local biome = minetest.get_humidity(pos)
local random = climate_mod.state:get_float("humidity_random");
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
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)
local pname = player:get_player_name()
local weathers = climate_mod.current_weather[pname]

View File

@ -1,30 +1,54 @@
climate_api.register_influence("heat", function(player)
return climate_mod.get_heat(player:get_pos())
climate_api.register_influence("heat", function(pos)
return climate_api.environment.get_heat(pos)
end)
climate_api.register_influence("humidity", function(player)
return climate_mod.get_humidity(player:get_pos())
climate_api.register_influence("base_heat", function(pos)
return minetest.get_heat(pos)
end)
climate_api.register_influence("windspeed", function(player)
local wind_x = climate_mod.state:get_float("wind_x")
local wind_z = climate_mod.state:get_float("wind_z")
return vector.length({x = wind_x, y = 0, z = wind_z})
climate_api.register_influence("humidity", function(pos)
return climate_api.environment.get_humidity(pos)
end)
climate_api.register_influence("wind_x", function(player)
return climate_mod.state:get_float("wind_x")
climate_api.register_influence("base_humidity", function(pos)
return minetest.get_humidity(pos)
end)
climate_api.register_influence("wind_z", function(player)
return climate_mod.state:get_float("wind_z")
climate_api.register_influence("biome", function(pos)
local data = minetest.get_biome_data(pos)
local biome = minetest.get_biome_name(data.biome)
return biome
end)
climate_api.register_influence("height", function(player)
local ppos = player:get_pos()
return ppos.y
climate_api.register_influence("windspeed", function(_)
local wind = climate_api.environment.get_wind()
return vector.length(wind)
end)
climate_api.register_influence("light", function(player)
return minetest.env:get_node_light(player:get_pos(), 0.5)
climate_api.register_influence("wind_yaw", function(_)
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_influence("time", function(_)
return minetest.get_timeofday()
end)
climate_api.register_influence("day_count", function(_)
return minetest.get_day_count()
end)

View File

@ -1,5 +1,5 @@
local GSCYCLE = 0.03
local WORLD_CYCLE = 2
local GSCYCLE = 0.03 * climate_mod.settings.tick_speed
local WORLD_CYCLE = 2 * climate_mod.settings.tick_speed
local gs_timer = 0
local world_timer = 0
@ -20,7 +20,8 @@ minetest.register_globalstep(function(dtime)
local current_effects = climate_mod.trigger.get_active_effects()
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.current_effects[name] = current_effects[name]
climate_mod.trigger.call_handlers(name, current_effects[name], previous_effects[name])

109
lib/skybox_merger.lua Normal file
View 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
View 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

View File

@ -4,7 +4,7 @@ function trigger.get_position_environment(pos)
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
env.height = pos.y
env.wind = vector.new(wind_x, 0, wind_z)
@ -13,7 +13,11 @@ function trigger.get_position_environment(pos)
env.humidity = climate_api.environment.get_humidity(pos)
env.time = minetest.get_timeofday()
env.date = minetest.get_day_count()
env.light = minetest.get_node_light(vector.add(pos, vector.new({x=0,y=1,z=0})), 0.5)
env.light = minetest.get_node_light(vector.add(pos, vector.new({x=0,y=1,z=0})), 0.5)]]
local env = {}
for influence, func in pairs(climate_mod.influences) do
env[influence] = func(pos)
end
return env
end
@ -24,15 +28,21 @@ function trigger.get_player_environment(player)
return env
end
local function test_condition(condition, env, goal)
function trigger.test_condition(condition, env, goal)
local value = env[condition:sub(5)]
if condition:sub(1, 4) == "min_" then
return type(value) ~= "nil" and goal <= value
elseif condition:sub(1, 4) == "max_" then
return type(value) ~= "nil" and goal > value
else
Minetest.log("warning", "[Climate API] Invalid effect condition")
elseif condition:sub(1, 4) == "has_" then
if type(value) == "nil" then return false end
for _, g in ipairs(goal) do
if value == g then return true end
end
return false
else
value = env[condition]
return type(value) ~= "nil" and goal == value
end
end
@ -45,7 +55,7 @@ local function is_weather_active(player, weather, env)
return config.conditions(env)
end
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
end
end
@ -145,7 +155,6 @@ function trigger.call_handlers(name, effect, prev_effect)
-- remaining table lists ending effects
if has_stops then
minetest.log(dump2(name, "AAAAAAAAAAA"))
for _, handler in ipairs(climate_mod.effects[name]["stop"]) do
handler(stops)
end

View File

@ -2,11 +2,11 @@ local world = {}
local WIND_SPREAD = 600
local WIND_SCALE = 2
local HEAT_SPREAD = 200
local HEAT_SPREAD = 400
local HEAT_SCALE = 0.3
local HUMIDITY_SPREAD = 60
local HUMIDITY_SPREAD = 150
local HUMIDITY_SCALE = 0.5
local HUMIDITY_BASE_SPREAD = 600
local HUMIDITY_BASE_SPREAD = 800
local HUMIDITY_BASE_SCALE = 40
local nobj_wind_x

View File

@ -1,7 +1,7 @@
name = climate_api
title = Climate API
author = TestificateMods
release = 1
release = 2
optional_depends = skylayer, player_monoids, playerphysics
description = """
A powerful engine for weather presets and visual effects.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@ -1,11 +1,62 @@
[Features]
# 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
climate_api_skybox (Allow weather effects to modify the skybox) bool true
climate_api_sound (Allow weather presets to play ambient sounds) bool true
climate_api_hud_overlay (Allow weather presets to display a HUD overlay) bool true
climate_api_wind (Allow wind to angle rainfall) bool true
climate_api_seasons (Change global temperature based on an annual cycle) 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 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
# 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
# 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
[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
climate_api_time_spread (Regulates how quickly the weather changes) float 1 0.1 10
# 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
# 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.
climate_api_tick_speed (Update speed of weather effects) float 1 0.1 10
[Preferences]
# 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.
climate_api_particle_count (Multiplicator for used particles) float 1 0.1 2
# 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
# 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

BIN
textures/moon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
textures/sun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB