diff --git a/README.md b/README.md index cdeee54..8dffcc9 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ Weather mod for Minetest (http://minetest.net/) Feedback and Improvements ----------------------- -* See newest version at https://github.com/xeranas/weather_pack -* Register bugs at https://github.com/xeranas/weather_pack/issues +* See newest version at https://gitlab.com/zombiebot/weather_pack +* Register bugs at https://gitlab.com/zombiebot/weather_pack/issues * Questions / Discussion at https://forum.minetest.net/viewtopic.php?p=215869 Weathers included diff --git a/init.lua b/init.lua index 27d8e82..371681b 100644 --- a/init.lua +++ b/init.lua @@ -2,25 +2,32 @@ local modpath = minetest.get_modpath("weather_pack"); -- If skylayer mod not located then embeded version will be loaded. if minetest.get_modpath("skylayer") == nil then - dofile(modpath.."/embedded_sky_layer_api.lua") + dofile(modpath.."/lib_sky_layer_api.lua") end -- If happy_weather_api mod not located then embeded version will be loaded. if minetest.get_modpath("happy_weather_api") == nil then - dofile(modpath.."/embedded_happy_weather_api.lua") + dofile(modpath.."/lib_happy_weather_api.lua") dofile(modpath.."/commands.lua") end +legacy_MT_version = false +if minetest.get_humidity == nil then + minetest.log("warning", "MOD [weather_pack]: Old Minetest version detected, some mod features will not work.") + legacy_MT_version = true +end + -- Happy Weather utilities dofile(modpath.."/utils.lua") -dofile(modpath.."/light_rain.lua") -dofile(modpath.."/rain.lua") -dofile(modpath.."/heavy_rain.lua") -dofile(modpath.."/snow.lua") +dofile(modpath.."/weathers/light_rain.lua") +dofile(modpath.."/weathers/rain.lua") +dofile(modpath.."/weathers/heavy_rain.lua") +dofile(modpath.."/weathers/snow.lua") +dofile(modpath.."/weathers/snowstorm.lua") if minetest.get_modpath("lightning") ~= nil then - dofile(modpath.."/thunder.lua") + dofile(modpath.."/weathers/thunder.lua") -- Turn off lightning mod 'auto mode' lightning.auto = false diff --git a/embedded_happy_weather_api.lua b/lib_happy_weather_api.lua similarity index 79% rename from embedded_happy_weather_api.lua rename to lib_happy_weather_api.lua index e4b256a..0008d2e 100644 --- a/embedded_happy_weather_api.lua +++ b/lib_happy_weather_api.lua @@ -11,6 +11,7 @@ happy_weather = {} -- Local variables which helps organize active and deactive weahers local registered_weathers = {} local active_weathers = {} +local meta_plawpos = {} -- meta about Player Last Active Weaher Position ------------------------------------ -- Local helper / utility methods -- @@ -79,6 +80,44 @@ local is_player_affected = function(affected_players, player_name) end end + +local remove_meta_plawpos = function(weather_code, player_name) + if #meta_plawpos == 0 then + return + end + + for k, meta_ in ipairs(meta_plawpos) do + if (meta_.name == player_name and meta_.code == weather_code) then + table.remove(meta_plawpos, k) + end + end +end + +local add_meta_plawpos = function(weather_code, player) + local meta = {} + meta.code = weather_code + meta.pos = player:getpos() + meta.name = player:get_player_name() + + remove_meta_plawpos(weather_code, player:get_player_name()) + table.insert(meta_plawpos, meta) +end + +local get_meta_plawpos = function(weather_code, player_name) + if #meta_plawpos == 0 then + return nil + end + + for k, meta_ in ipairs(meta_plawpos) do + if (meta_.name == player_name and meta_.code == weather_code) then + return meta_.pos + end + end + + return nil +end + + --------------------------- -- Weather API functions -- --------------------------- @@ -179,7 +218,7 @@ local weather_remove_player = function(weather_obj, player) weather_obj.remove_player(player) end --- Weather remove_player method nil-safe wrapper +-- Weather in_area method nil-safe wrapper local weather_in_area = function(weather_obj, position) if weather_obj.in_area == nil then return true @@ -224,14 +263,36 @@ local prepare_starting = function(weather_obj) add_active_weather(weather_obj) end +local MAX_DISTANCE_FROM_WEATHER = 35 + +-- This function aims to remove weather flickering effect when player walks on biome edge. +-- To accomlish that extra distance is applied before removing player from weather affection. +local is_outside_recent_weather = function(weather_code, player) + local pos = get_meta_plawpos(weather_code, player:get_player_name()) + if pos == nil then + return false + end + + local ppos = player:getpos() + local d = ((ppos.x - pos.x)^2 + (ppos.y - pos.y)^2 + (ppos.z - pos.z)^2)^0.5 + return MAX_DISTANCE_FROM_WEATHER - d < 0 +end + + -- While still active weather can or can not affect players based on area they are local render_if_in_area = function(weather_obj, dtime, player) if is_player_affected(weather_obj.affected_players, player:get_player_name()) then if weather_in_area(weather_obj, player:getpos()) then weather_render(weather_obj, dtime, player) + add_meta_plawpos(weather_obj.code, player) else - weather_remove_player(weather_obj, player) - remove_player(weather_obj.affected_players, player:get_player_name()) + if (is_outside_recent_weather(weather_obj.code, player)) then + weather_remove_player(weather_obj, player) + remove_player(weather_obj.affected_players, player:get_player_name()) + -- render weather until player will be completely outside weather range + else + weather_render(weather_obj, dtime, player) + end end else if weather_in_area(weather_obj, player:getpos()) then @@ -265,7 +326,7 @@ minetest.register_globalstep(function(dtime) -- Loop through connected players for ii, player in ipairs(minetest.get_connected_players()) do - + -- Weaher is active checking if it about to end if weather_.active then if weather_is_ending(weather_, dtime) or deactivate_weather then @@ -280,7 +341,7 @@ minetest.register_globalstep(function(dtime) -- Weaher is not active checking if it about to start else - if weather_.is_starting(dtime, player:getpos()) then + if weather_is_starting(weather_, dtime, player:getpos()) then activate_weather = true end end diff --git a/embedded_sky_layer_api.lua b/lib_sky_layer_api.lua similarity index 100% rename from embedded_sky_layer_api.lua rename to lib_sky_layer_api.lua diff --git a/sounds/happy_weather_snowstorm.ogg b/sounds/happy_weather_snowstorm.ogg new file mode 100644 index 0000000..2419b0d Binary files /dev/null and b/sounds/happy_weather_snowstorm.ogg differ diff --git a/textures/happy_weather_snowstorm.png b/textures/happy_weather_snowstorm.png new file mode 100644 index 0000000..fce00c0 Binary files /dev/null and b/textures/happy_weather_snowstorm.png differ diff --git a/utils.lua b/utils.lua index 41080af..cd28497 100644 --- a/utils.lua +++ b/utils.lua @@ -77,80 +77,29 @@ hw_utils.get_random_pos = function(player, offset) return {x=random_pos_x, y=random_pos_y, z=random_pos_z} end -local np_temp = { - offset = 50, - scale = 50, - spread = {x = 1000, y = 1000, z = 1000}, - seed = 5349, - octaves = 3, - persist = 0.5, - lacunarity = 2.0 -} - -local np_humid = { - offset = 50, - scale = 50, - spread = {x = 1000, y = 1000, z = 1000}, - seed = 842, - octaves = 3, - persist = 0.5, - lacunarity = 2.0 -} - -local np_biome_v6 = { - offset = 0, - scale = 1.0, - spread = {x = 500.0, y = 500.0, z = 500.0}, - seed = 9130, - octaves = 3, - persist = 0.50, - lacunarity = 2.0 -} - -local np_humidity_v6 = { - offset = 0.5, - scale = 0.5, - spread = {x = 500.0, y = 500.0, z = 500.0}, - seed = 72384, - octaves = 4, - persist = 0.66, - lacunarity = 2.0 -} - local is_biome_frozen = function(position) - local posx = math.floor(position.x) - local posz = math.floor(position.z) - local noise_obj = minetest.get_perlin(np_temp) - local noise_temp = noise_obj:get2d({x = posx, y = posz}) - + if legacy_MT_version then + return false; + end + local heat = minetest.get_heat(position) -- below 35 heat biome considered to be frozen type - return noise_temp < 35 + return heat < 35 end hw_utils.is_biome_frozen = function(position) if mg_name == "v6" then - return false -- v6 not supported yet. + return false -- v6 not supported. end return is_biome_frozen(position) end -local is_biome_dry_v6 = function(position) - local posx = math.floor(position.x) - local posz = math.floor(position.z) - local noise_obj = minetest.get_perlin(np_biome_v6) - local noise_biome = noise_obj:get2d({x = posx, y = posz}) - -- TODO futher investigation needed towards on biome check for v6 mapgen - return noise_biome > 0.45 -end - local is_biome_dry = function(position) - local posx = math.floor(position.x) - local posz = math.floor(position.z) - local noise_obj = minetest.get_perlin(np_humid) - local noise_humid = noise_obj:get2d({x = posx, y = posz}) - - -- below 50 humid biome considered to be dry type (at least by this mod) - return noise_humid < 50 + if legacy_MT_version then + return false; + end + local humidity = minetest.get_humidity(position) + local heat = minetest.get_heat(position) + return humidity < 50 and heat > 65 end hw_utils.is_biome_dry = function(position) @@ -161,15 +110,14 @@ hw_utils.is_biome_dry = function(position) end local is_biome_tropic = function(position) - local posx = math.floor(position.x) - local posz = math.floor(position.z) - local noise_obj = minetest.get_perlin(np_humid) - local noise_humid = noise_obj:get2d({x = posx, y = posz}) - noise_obj = minetest.get_perlin(np_temp) - local noise_temp = noise_obj:get2d({x = posx, y = posz}) + if legacy_MT_version then + return false; + end + local humidity = minetest.get_humidity(position) + local heat = minetest.get_heat(position) -- humid and temp values are taked by testing flying around world (not sure actually) - return noise_humid > 55 and noise_temp > 80 + return humidity > 55 and heat > 70 end hw_utils.is_biome_tropic = function(position) diff --git a/heavy_rain.lua b/weathers/heavy_rain.lua similarity index 100% rename from heavy_rain.lua rename to weathers/heavy_rain.lua diff --git a/light_rain.lua b/weathers/light_rain.lua similarity index 100% rename from light_rain.lua rename to weathers/light_rain.lua diff --git a/rain.lua b/weathers/rain.lua similarity index 100% rename from rain.lua rename to weathers/rain.lua diff --git a/snow.lua b/weathers/snow.lua similarity index 100% rename from snow.lua rename to weathers/snow.lua diff --git a/weathers/snowstorm.lua b/weathers/snowstorm.lua new file mode 100644 index 0000000..39d8a19 --- /dev/null +++ b/weathers/snowstorm.lua @@ -0,0 +1,203 @@ +---------------------------- +-- Happy Weather: Snowfall + +-- License: MIT + +-- Credits: xeranas +---------------------------- + +local snowstorm = {} + +-- Weather identification code +snowstorm.code = "snowstorm" +snowstorm.last_check = 0 +snowstorm.check_interval = 300 +snowstorm.chance = 0.05 + +-- Keeps sound handler references +local sound_handlers = {} + +-- Manual triggers flags +local manual_trigger_start = false +local manual_trigger_end = false + +-- Skycolor layer id +local SKYCOLOR_LAYER = "happy_weather_snowstorm_sky" + +local set_weather_sound = function(player) + return minetest.sound_play("happy_weather_snowstorm", { + object = player, + max_hear_distance = 2, + loop = true, + }) +end + +local remove_weather_sound = function(player) + local sound = sound_handlers[player:get_player_name()] + if sound ~= nil then + minetest.sound_stop(sound) + sound_handlers[player:get_player_name()] = nil + end +end + +snowstorm.is_starting = function(dtime, position) + if snowstorm.last_check + snowstorm.check_interval < os.time() then + snowstorm.last_check = os.time() + if math.random() < snowstorm.chance then + return true + end + end + + if manual_trigger_start then + manual_trigger_start = false + return true + end + + return false +end + +snowstorm.is_ending = function(dtime) + if manual_trigger_end then + manual_trigger_end = false + return true + end + + return false +end + +local set_sky_box = function(player_name) + local sl = {} + sl.layer_type = skylayer.SKY_PLAIN + sl.name = SKYCOLOR_LAYER + sl.sky_data = { + gradient_colors = { + {r=0, g=0, b=0}, + {r=231, g=234, b=239}, + {r=0, g=0, b=0} + } + } + skylayer.add_layer(player_name, sl) +end + +snowstorm.in_area = function(position) + if hw_utils.is_biome_frozen(position) == false then + return false + end + + if position.y > 30 and position.y < 140 then + return true + end + return false +end + +snowstorm.add_player = function(player) + sound_handlers[player:get_player_name()] = set_weather_sound(player) + set_sky_box(player:get_player_name()) +end + +snowstorm.remove_player = function(player) + remove_weather_sound(player) + skylayer.remove_layer(player:get_player_name(), SKYCOLOR_LAYER) +end + +local rain_drop_texture = "happy_weather_snowstorm.png" + +local sign = function (number) + if number >= 0 then + return 1 + else + return -1 + end +end + +local add_wide_range_rain_particle = function(player) + local offset = { + front = 7, + back = 4, + top = 3, + bottom = 0 + } + + local random_pos = hw_utils.get_random_pos(player, offset) + local p_pos = player:getpos() + + local look_dir = player:get_look_dir() + + if hw_utils.is_outdoor(random_pos) then + minetest.add_particle({ + pos = {x=random_pos.x, y=random_pos.y, z=random_pos.z}, + velocity = {x = sign(look_dir.x) * -10, y = -1, z = sign(look_dir.z) * -10}, + acceleration = {x = sign(look_dir.x) * -10, y = -1, z = sign(look_dir.z) * -10}, + expirationtime = 0.3, + size = 30, + collisiondetection = true, + texture = "happy_weather_snowstorm.png", + playername = player:get_player_name() + }) + end +end + + +-- Random texture getter +local choice_random_rain_drop_texture = function() + local base_name = "happy_weather_light_snow_snowflake_" + local number = math.random(1, 3) + local extension = ".png" + return base_name .. number .. extension +end + +local add_snow_particle = function(player) + local offset = { + front = 5, + back = 2, + top = 4 + } + + local random_pos = hw_utils.get_random_pos(player, offset) + + if hw_utils.is_outdoor(random_pos) then + minetest.add_particle({ + pos = {x=random_pos.x, y=random_pos.y, z=random_pos.z}, + velocity = {x = math.random(-5,-2.5), y = math.random(-10,-5), z = math.random(-5,-2.5)}, + acceleration = {x = math.random(-5,-2.5), y=-2.5, z = math.random(-5,-2.5)}, + expirationtime = 2.0, + size = math.random(1, 3), + collisiondetection = true, + collision_removal = true, + vertical = true, + texture = choice_random_rain_drop_texture(), + playername = player:get_player_name() + }) + end +end + +local display_particles = function(player) + if hw_utils.is_underwater(player) then + return + end + + local particles_number_per_update = 3 + for i=particles_number_per_update, 1,-1 do + add_wide_range_rain_particle(player) + end + + local snow_particles_number_per_update = 10 + for i=snow_particles_number_per_update, 1,-1 do + add_snow_particle(player) + end +end + +snowstorm.render = function(dtime, player) + display_particles(player) +end + +snowstorm.start = function() + manual_trigger_start = true +end + +snowstorm.stop = function() + manual_trigger_end = true +end + +happy_weather.register_weather(snowstorm) + diff --git a/thunder.lua b/weathers/thunder.lua similarity index 100% rename from thunder.lua rename to weathers/thunder.lua