Fix countless bugs, implement chat commands

This commit is contained in:
Till Affeldt 2020-04-14 05:44:46 +02:00
parent fdc457bd09
commit 2090aff6bd
12 changed files with 169 additions and 27 deletions

15
README.md Normal file
View File

@ -0,0 +1,15 @@
# Climate API
A powerful engine for weather presets and visual effects
Use the regional climate to set up different effects for different regions.
Control where your effects are activated based on temperature, humidity, wind,
position, light level or a completely custom activator.
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 in screenshots
- All screenshots and editing by me: *CC BY-SA (4.0)*
- 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

@ -27,13 +27,16 @@
- Make switches between effects more smooth - Make switches between effects more smooth
- Adjust size of particle boxes based on player speed - Adjust size of particle boxes based on player speed
- Ability to register environment conditions dynamically (like the heat) - Ability to register environment conditions dynamically (like the heat)
- Create conditions for time of day, annual progression, biome filters
- Let rain extinguish flames
- Ability to force toggle weather presets on / off with chat commands
## Future Plans & Ideas ## Future Plans & Ideas
- Complete season system - Complete season system
- crops grow better in their respective season or worse in winter - crops grow better in their respective season or worse in winter
- regrowing apples, lemons, etc. in their respective months - regrowing apples, lemons, etc. in their respective months
- holidays and reminders via chat commands - holidays and reminders via chat commands
- day/night nycle adjusted to season - day/night cycle adjusted to season
- special events like a bloodmoon -> increased mob spawns - special events like a bloodmoon -> increased mob spawns
- Fantasy weather effects - Fantasy weather effects
- pretty skyboxes - pretty skyboxes

View File

@ -24,12 +24,6 @@ end
local function handle_effect(player_data) local function handle_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) local player = minetest.get_player_by_name(playername)
local data = player.get_meta()
local current_size = data.get_float("climate_api:cloud_size")
local current_speed_x = data.get_float("climate_api:cloud_speed_x")
local current_speed_z = data.get_float("climate_api:cloud_speed_z")
local clouds = {} local clouds = {}
for weather, value in pairs(data) do for weather, value in pairs(data) do
clouds.size = accumulate(clouds.size, data.size, function(a, b) return a * b end) clouds.size = accumulate(clouds.size, data.size, function(a, b) return a * b end)

View File

@ -7,11 +7,15 @@ local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local function getBoolSetting(name, default) local function getBoolSetting(name, default)
return minetest.is_yes(minetest.settings:get_bool("climate_api_" .. name) or default) local value = minetest.settings:get_bool("climate_api_" .. name)
if type(value) == "nil" then value = default end
return minetest.is_yes(value)
end end
local function getNumericSetting(name, default) local function getNumericSetting(name, default)
return tonumber(minetest.settings:get("climate_api_" .. name) or default) local value = minetest.settings:get("climate_api_" .. name)
if type(value) == "nil" then value = default end
return tonumber(value)
end end
-- load settings from config file -- load settings from config file
@ -21,14 +25,19 @@ climate_mod.settings = {
sound = getBoolSetting("sound", true), sound = getBoolSetting("sound", true),
wind = getBoolSetting("wind", true), wind = getBoolSetting("wind", true),
seasons = getBoolSetting("seasons", true), seasons = getBoolSetting("seasons", true),
fahrenheit = getBoolSetting("fahrenheit", false),
heat = getNumericSetting("heat_base", 0), heat = getNumericSetting("heat_base", 0),
humidity = getNumericSetting("humidity_base", 0), humidity = getNumericSetting("humidity_base", 0),
time_spread = getNumericSetting("time_spread", 1), time_spread = getNumericSetting("time_spread", 1),
particle_count = getNumericSetting("particle_count", 1) particle_count = getNumericSetting("particle_count", 1)
} }
-- initiate empty registers
climate_mod.current_weather = {} climate_mod.current_weather = {}
climate_mod.current_effects = {} climate_mod.current_effects = {}
climate_mod.weathers = {}
climate_mod.effects = {}
climate_mod.cycles = {}
-- import core API -- import core API
climate_mod.state = dofile(modpath .. "/lib/datastorage.lua") climate_mod.state = dofile(modpath .. "/lib/datastorage.lua")
@ -38,6 +47,7 @@ climate_api.environment = dofile(modpath .. "/lib/environment.lua")
climate_mod.world = dofile(modpath .. "/lib/world.lua") climate_mod.world = dofile(modpath .. "/lib/world.lua")
climate_mod.trigger = dofile(modpath .. "/lib/trigger.lua") climate_mod.trigger = dofile(modpath .. "/lib/trigger.lua")
dofile(modpath.."/lib/main.lua") dofile(modpath.."/lib/main.lua")
dofile(modpath.."/lib/commands.lua")
-- import predefined environment effects -- import predefined environment effects
dofile(modpath .. "/ca_effects/particles.lua") dofile(modpath .. "/ca_effects/particles.lua")

View File

@ -5,10 +5,6 @@ api.DEFAULT_CYCLE = 0.1 -- for most effect types
api.MEDIUM_CYCKE = 2.0 -- for ressource intensive tasks api.MEDIUM_CYCKE = 2.0 -- for ressource intensive tasks
api.LONG_CYCLE = 5.0 -- for write operations and skybox changes api.LONG_CYCLE = 5.0 -- for write operations and skybox changes
climate_mod.weathers = {}
climate_mod.effects = {}
climate_mod.cycles = {}
function api.register_weather(name, conditions, effects) function api.register_weather(name, conditions, effects)
-- TODO: check and sanitize -- TODO: check and sanitize
climate_mod.weathers[name] = { climate_mod.weathers[name] = {

89
lib/commands.lua Normal file
View File

@ -0,0 +1,89 @@
local function parse_heat(heat)
local indicator = "°F"
if not climate_mod.settings.fahrenheit then
heat = (heat - 32) * 5 / 9
indicator = "°C"
end
heat = math.floor(heat * 100) / 100
return heat .. indicator
end
minetest.register_privilege("weather", {
description = "Make changes to the current weather",
give_to_singleplayer = false
})
minetest.register_chatcommand("weather", {
description ="Display weather information",
func = function(playername)
local player = minetest.get_player_by_name(playername)
local ppos = player:get_pos()
local weathers = climate_api.environment.get_weather_presets(player)
local effects = climate_api.environment.get_effects(player)
local heat = climate_api.environment.get_heat(ppos)
local humidity = math.floor(climate_api.environment.get_humidity(ppos) * 100) / 100
local msg = ""
if #weathers > 0 then
msg = msg .. "The following weather presets are active for you: "
for _, weather in ipairs(weathers) do
msg = msg .. weather .. ", "
end
msg = msg:sub(1, #msg-2) .. "\n"
else
msg = msg .. "Your sky is clear. No weather presets are currently active.\n"
end
if #effects > 0 then
msg = msg .. "As a result, the following environment effects are applied: "
for _, effect in ipairs(effects) do
msg = msg .. effect .. ", "
end
msg = msg:sub(1, #msg-2) .. "\n"
end
local heat_desc
if heat > 80 then heat_desc = "scorching"
elseif heat > 50 then heat_desc = "pleasant"
else heat_desc = "chilly" end
msg = msg .. "It is a " .. heat_desc .. " " .. parse_heat(heat) .. " right now and "
msg = msg .. "humidity is at " .. humidity .. "%.\n"
minetest.chat_send_player(playername, msg)
end
})
minetest.register_chatcommand("set_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
climate_mod.settings.heat = tonumber(param)
minetest.chat_send_player(playername, "Heat changed")
end
})
minetest.register_chatcommand("set_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
climate_mod.settings.humidity = tonumber(param)
minetest.chat_send_player(playername, "Humidity changed")
end
})
minetest.register_chatcommand("weather_settings", {
description = "Print the active Climate API configuration",
privs = { weather = true },
func = function(playername)
minetest.chat_send_player(playername, "Current Settings\n================")
for setting, value in pairs(climate_mod.settings) do
minetest.chat_send_player(playername, dump2(value, setting))
end
end
})

View File

@ -41,4 +41,22 @@ function environment.get_humidity(pos)
return (base + biome * 0.7 + random_base * 0.3) * random return (base + biome * 0.7 + random_base * 0.3) * random
end end
function environment.get_weather_presets(player)
local pname = player:get_player_name()
local weathers = climate_mod.current_weather[pname]
if type(weathers) == "nil" then weathers = {} end
return weathers
end
function environment.get_effects(player)
local pname = player:get_player_name()
local effects = {}
for effect, players in pairs(climate_mod.current_effects) do
if type(players[pname]) ~= "nil" then
table.insert(effects, effect)
end
end
return effects
end
return environment return environment

View File

@ -16,12 +16,14 @@ minetest.register_globalstep(function(dtime)
climate_mod.world.update_status(noise_timer) climate_mod.world.update_status(noise_timer)
end end
local effects = climate_mod.trigger.get_active_effects() climate_mod.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 if climate_mod.cycles[name].timespan < climate_mod.cycles[name].timer + dtime then
climate_mod.cycles[name].timer = 0 climate_mod.cycles[name].timer = 0
climate_mod.trigger.call_handlers(name, effects[name]) climate_mod.trigger.call_handlers(name, climate_mod.current_effects[name])
else
climate_mod.cycles[name].timer = climate_mod.cycles[name].timer + dtime
end end
end end
end) end)

View File

@ -43,10 +43,19 @@ local function is_weather_active(player, weather_config, env)
end end
local function get_weather_effects(player, weather_config, env) local function get_weather_effects(player, weather_config, env)
local config = {}
local effects = {}
if type(weather_config.effects) == "function" then if type(weather_config.effects) == "function" then
return weather_config.effects(env) config = weather_config.effects(env)
else
config = weather_config.effects
end end
return weather_config.effects for effect, value in pairs(config) do
if type(climate_mod.effects[effect]) ~= "nil" then
effects[effect] = value
end
end
return effects
end end
function trigger.get_active_effects() function trigger.get_active_effects()
@ -56,11 +65,16 @@ function trigger.get_active_effects()
end end
local effects = {} local effects = {}
climate_mod.current_weather = {}
for wname, wconfig in pairs(climate_mod.weathers) do for wname, wconfig in pairs(climate_mod.weathers) do
for _, player in ipairs(minetest.get_connected_players()) do for _, player in ipairs(minetest.get_connected_players()) do
local pname = player:get_player_name() local pname = player:get_player_name()
local env = environments[pname] local env = environments[pname]
if is_weather_active(player, wconfig, env) then if is_weather_active(player, wconfig, env) then
if type(climate_mod.current_weather[pname]) == "nil" then
climate_mod.current_weather[pname] = {}
end
table.insert(climate_mod.current_weather[pname], wname)
local player_effects = get_weather_effects(player, wconfig, env) local player_effects = get_weather_effects(player, wconfig, env)
for effect, value in pairs(player_effects) do for effect, value in pairs(player_effects) do
if type(effects[effect]) == "nil" then if type(effects[effect]) == "nil" then

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -8,3 +8,4 @@ climate_api_heat_base (Global base temperature) float 0
climate_api_humidity_base (Global base humidity) float 0 climate_api_humidity_base (Global base humidity) float 0
climate_api_time_spread (Regulates how quickly the weather changes) float 1 0.1 10 climate_api_time_spread (Regulates how quickly the weather changes) float 1 0.1 10
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