initial commit
parent
81e265c7a3
commit
7a3648b7d9
41
README.md
41
README.md
|
@ -1 +1,40 @@
|
|||
# happy_weather
|
||||
Happy Weather
|
||||
=======================
|
||||
A weather mod for Minetest (http://minetest.net/)
|
||||
|
||||
Weathers included
|
||||
-----------------------
|
||||
* light rain
|
||||
* heavy rain
|
||||
* thunder
|
||||
|
||||
Commands
|
||||
-----------------------
|
||||
* `start_weather <weather code>` requires `weather_manager` privilege.
|
||||
* `stop_weather <weather code>` requires `weather_manager` privilege.
|
||||
|
||||
Dependencies
|
||||
-----------------------
|
||||
* [happy_weather_api](https://github.com/xeranas/happy_weather_api) mod-api (used to manage weathers)
|
||||
* [skycolor](https://github.com/xeranas/skycolor) mod-api (used to manage sky color)
|
||||
* [lightning](https://github.com/minetest-mods/lightning) mod (used by thunder weather).
|
||||
|
||||
|
||||
License of source code:
|
||||
-----------------------
|
||||
MIT
|
||||
|
||||
Authors of media files:
|
||||
-----------------------
|
||||
|
||||
xeranas:
|
||||
|
||||
* `happy_weather_heavy_rain_drops.png` - CC-0
|
||||
* `happy_weather_light_rain_raindrop_1.png` - CC-0
|
||||
* `happy_weather_light_rain_raindrop_2.png` - CC-0
|
||||
* `happy_weather_light_rain_raindrop_3.png` - CC-0
|
||||
|
||||
inchadney (http://freesound.org/people/inchadney/):
|
||||
|
||||
* `light_rain_drop.ogg` - CC-0 (http://freesound.org/people/rcproductions54/sounds/265045/)
|
||||
* `heavy_rain_drop.ogg` - CC-BY-SA 3.0 (cut from http://freesound.org/people/inchadney/sounds/58835/)
|
|
@ -0,0 +1,3 @@
|
|||
happy_weather_api
|
||||
skycolor
|
||||
lightning
|
|
@ -0,0 +1,157 @@
|
|||
--
|
||||
-- Happy Weather: Heavy Rain
|
||||
|
||||
-- License: MIT
|
||||
|
||||
-- Credits:
|
||||
-- * xeranas
|
||||
|
||||
local heavy_rain = {}
|
||||
|
||||
-- Weather identification code
|
||||
heavy_rain.code = "heavy_rain"
|
||||
|
||||
-- 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_heavy_rain_sky"
|
||||
|
||||
heavy_rain.about_to_start = function(dtime)
|
||||
if manual_trigger_start then
|
||||
manual_trigger_start = false
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
heavy_rain.about_to_end = function(dtime)
|
||||
if manual_trigger_end then
|
||||
manual_trigger_end = false
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local set_sky_box = function()
|
||||
skycolor.add_layer(
|
||||
SKYCOLOR_LAYER,
|
||||
{{r=0, g=0, b=0},
|
||||
{r=65, g=66, b=78},
|
||||
{r=112, g=110, b=119},
|
||||
{r=65, g=66, b=78},
|
||||
{r=0, g=0, b=0}})
|
||||
skycolor.active = true
|
||||
end
|
||||
|
||||
local set_rain_sound = function(player)
|
||||
return minetest.sound_play("heavy_rain_drop", {
|
||||
object = player,
|
||||
max_hear_distance = 2,
|
||||
loop = true,
|
||||
})
|
||||
end
|
||||
|
||||
local remove_rain_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
|
||||
|
||||
heavy_rain.setup = function(player)
|
||||
sound_handlers[player:get_player_name()] = set_rain_sound(player)
|
||||
set_sky_box()
|
||||
end
|
||||
|
||||
heavy_rain.clear_up = function(player)
|
||||
remove_rain_sound(player)
|
||||
skycolor.remove_layer(SKYCOLOR_LAYER)
|
||||
end
|
||||
|
||||
local rain_drop_texture = "happy_weather_heavy_rain_drops.png"
|
||||
|
||||
local add_close_range_rain_particle = function(player)
|
||||
local offset = {
|
||||
front = 1,
|
||||
back = 0,
|
||||
top = 2
|
||||
}
|
||||
|
||||
local random_pos = utils.get_random_pos(player, offset)
|
||||
local offset_y = -1 -- rain drop visual size / 2 + player size / 2
|
||||
|
||||
if utils.is_outdoor(random_pos, offset_y) then
|
||||
minetest.add_particle({
|
||||
pos = {x=random_pos.x, y=random_pos.y, z=random_pos.z},
|
||||
velocity = {x=0, y=-10, z=0},
|
||||
acceleration = {x=0, y=-10, z=0},
|
||||
expirationtime = 5,
|
||||
size = 30,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
vertical = true,
|
||||
texture = rain_drop_texture,
|
||||
playername = player:get_player_name()
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local add_wide_range_rain_particle = function(player)
|
||||
local offset = {
|
||||
front = 10,
|
||||
back = 5,
|
||||
top = 8
|
||||
}
|
||||
|
||||
local random_pos = utils.get_random_pos(player, offset)
|
||||
|
||||
if utils.is_outdoor(random_pos) then
|
||||
minetest.add_particle({
|
||||
pos = {x=random_pos.x, y=random_pos.y, z=random_pos.z},
|
||||
velocity = {x=0, y=-10, z=0},
|
||||
acceleration = {x=0, y=-30, z=0},
|
||||
expirationtime = 5,
|
||||
size = 30,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
vertical = true,
|
||||
texture = rain_drop_texture,
|
||||
playername = player:get_player_name()
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local display_rain_particles = function(player)
|
||||
if utils.is_underwater(player) then
|
||||
return
|
||||
end
|
||||
|
||||
add_close_range_rain_particle(player)
|
||||
|
||||
local particles_number_per_update = 5
|
||||
for i=particles_number_per_update, 1,-1 do
|
||||
add_wide_range_rain_particle(player)
|
||||
end
|
||||
end
|
||||
|
||||
heavy_rain.update = function(dtime, player)
|
||||
display_rain_particles(player)
|
||||
end
|
||||
|
||||
heavy_rain.manual_trigger_start = function()
|
||||
manual_trigger_start = true
|
||||
end
|
||||
|
||||
heavy_rain.manual_trigger_end = function()
|
||||
manual_trigger_end = true
|
||||
end
|
||||
|
||||
happy_weather.register_weather(heavy_rain)
|
|
@ -0,0 +1,9 @@
|
|||
local modpath = minetest.get_modpath("happy_weather");
|
||||
|
||||
-- Utilities / Helpers
|
||||
dofile(modpath.."/utils.lua")
|
||||
|
||||
-- Weathers
|
||||
dofile(modpath.."/light_rain.lua")
|
||||
dofile(modpath.."/heavy_rain.lua")
|
||||
dofile(modpath.."/thunder.lua")
|
|
@ -0,0 +1,139 @@
|
|||
--
|
||||
-- Happy Weather: Light Rain
|
||||
|
||||
-- License: MIT
|
||||
|
||||
-- Credits:
|
||||
-- * xeranas
|
||||
|
||||
|
||||
local light_rain = {}
|
||||
|
||||
-- Weather identification code
|
||||
light_rain.code = "light_rain"
|
||||
|
||||
-- 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_light_rain_sky"
|
||||
|
||||
light_rain.about_to_start = function(dtime)
|
||||
if manual_trigger_start then
|
||||
manual_trigger_start = false
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
light_rain.about_to_end = function(dtime)
|
||||
if manual_trigger_end then
|
||||
manual_trigger_end = false
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local set_sky_box = function()
|
||||
skycolor.add_layer(
|
||||
SKYCOLOR_LAYER,
|
||||
{{r=0, g=0, b=0},
|
||||
{r=85, g=86, b=98},
|
||||
{r=152, g=150, b=159},
|
||||
{r=85, g=86, b=98},
|
||||
{r=0, g=0, b=0}})
|
||||
skycolor.active = true
|
||||
end
|
||||
|
||||
local set_rain_sound = function(player)
|
||||
return minetest.sound_play("light_rain_drop", {
|
||||
object = player,
|
||||
max_hear_distance = 2,
|
||||
loop = true,
|
||||
})
|
||||
end
|
||||
|
||||
local remove_rain_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
|
||||
|
||||
light_rain.setup = function(player)
|
||||
sound_handlers[player:get_player_name()] = set_rain_sound(player)
|
||||
set_sky_box()
|
||||
end
|
||||
|
||||
light_rain.clear_up = function(player)
|
||||
remove_rain_sound(player)
|
||||
skycolor.remove_layer(SKYCOLOR_LAYER)
|
||||
end
|
||||
|
||||
-- Random texture getter
|
||||
local choice_random_rain_drop_texture = function()
|
||||
local texture_name
|
||||
local random_number = math.random()
|
||||
if random_number > 0.33 then
|
||||
texture_name = "happy_weather_light_rain_raindrop_1.png"
|
||||
elseif random_number > 0.66 then
|
||||
texture_name = "happy_weather_light_rain_raindrop_2.png"
|
||||
else
|
||||
texture_name = "happy_weather_light_rain_raindrop_3.png"
|
||||
end
|
||||
return texture_name;
|
||||
end
|
||||
|
||||
local add_rain_particle = function(player)
|
||||
local offset = {
|
||||
front = 5,
|
||||
back = 2,
|
||||
top = 4
|
||||
}
|
||||
|
||||
local random_pos = utils.get_random_pos(player, offset)
|
||||
|
||||
if utils.is_outdoor(random_pos) then
|
||||
minetest.add_particle({
|
||||
pos = {x=random_pos.x, y=random_pos.y, z=random_pos.z},
|
||||
velocity = {x=0, y=-10, z=0},
|
||||
acceleration = {x=0, y=-30, z=0},
|
||||
expirationtime = 2,
|
||||
size = math.random(0.5, 3),
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
vertical = true,
|
||||
texture = choice_random_rain_drop_texture(),
|
||||
playername = player:get_player_name()
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local display_rain_particles = function(player)
|
||||
if utils.is_underwater(player) then
|
||||
return
|
||||
end
|
||||
|
||||
add_rain_particle(player)
|
||||
end
|
||||
|
||||
light_rain.update = function(dtime, player)
|
||||
display_rain_particles(player)
|
||||
end
|
||||
|
||||
light_rain.manual_trigger_start = function()
|
||||
manual_trigger_start = true
|
||||
end
|
||||
|
||||
light_rain.manual_trigger_end = function()
|
||||
manual_trigger_end = true
|
||||
end
|
||||
|
||||
happy_weather.register_weather(light_rain)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 264 B |
Binary file not shown.
After Width: | Height: | Size: 244 B |
Binary file not shown.
After Width: | Height: | Size: 269 B |
|
@ -0,0 +1,87 @@
|
|||
-- Turn off lightning mod 'auto mode'
|
||||
lightning.auto = false
|
||||
|
||||
local thunder = {}
|
||||
|
||||
-- Weather identification code
|
||||
thunder.code = "thunder"
|
||||
|
||||
local thunder_target_weather_code = "heavy_rain"
|
||||
--
|
||||
-- Happy Weather: Thunder
|
||||
|
||||
-- License: MIT
|
||||
|
||||
-- Credits:
|
||||
-- * xeranas
|
||||
|
||||
-- Manual triggers flags
|
||||
local manual_trigger_start = false
|
||||
local manual_trigger_end = false
|
||||
|
||||
-- Thunder weather appearance control
|
||||
local thunder_weather_chance = 5 -- 5 percent appearance during heavy rain
|
||||
local thunder_weather_next_check = 0
|
||||
local thunder_weather_check_delay = 600 -- to avoid checks continuously
|
||||
|
||||
thunder.about_to_start = function(dtime)
|
||||
if manual_trigger_start then
|
||||
manual_trigger_start = false
|
||||
return true
|
||||
end
|
||||
|
||||
if thunder_weather_next_check > os.time() then
|
||||
return
|
||||
end
|
||||
|
||||
if happy_weather.is_weather_active(thunder_target_weather_code) then
|
||||
local random_roll = math.random(0,100)
|
||||
thunder_weather_next_check = os.time() + thunder_weather_check_delay
|
||||
if random_roll <= thunder_weather_chance then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
thunder.about_to_end = function(dtime)
|
||||
if manual_trigger_end then
|
||||
manual_trigger_end = false
|
||||
return true
|
||||
end
|
||||
|
||||
if happy_weather.is_weather_active(thunder_target_weather_code) == false then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local calculate_thunder_strike_delay = function()
|
||||
local delay = math.random(thunder.min_delay, thunder.max_delay)
|
||||
thunder.next_strike = os.time() + delay
|
||||
end
|
||||
|
||||
thunder.setup = function(dtime)
|
||||
checked = false
|
||||
thunder.next_strike = 0
|
||||
thunder.min_delay = 5
|
||||
thunder.max_delay = math.random(5, 45)
|
||||
end
|
||||
|
||||
thunder.update = function(dtime, player)
|
||||
if thunder.next_strike <= os.time() then
|
||||
lightning.strike()
|
||||
calculate_thunder_strike_delay()
|
||||
end
|
||||
end
|
||||
|
||||
thunder.manual_trigger_start = function()
|
||||
manual_trigger_start = true
|
||||
end
|
||||
|
||||
thunder.manual_trigger_end = function()
|
||||
manual_trigger_end = true
|
||||
end
|
||||
|
||||
happy_weather.register_weather(thunder)
|
|
@ -0,0 +1,60 @@
|
|||
utils = {}
|
||||
|
||||
-- outdoor check based on node light level
|
||||
utils.is_outdoor = function(pos, offset_y)
|
||||
if offset_y == nil then
|
||||
offset_y = 0
|
||||
end
|
||||
|
||||
if minetest.get_node_light({x=pos.x, y=pos.y + offset_y, z=pos.z}, 0.5) == 15 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- checks if player is undewater. This is needed in order to
|
||||
-- turn off weather particles generation.
|
||||
utils.is_underwater = function(player)
|
||||
local ppos = player:getpos()
|
||||
local offset = player:get_eye_offset()
|
||||
local player_eye_pos = {x = ppos.x + offset.x,
|
||||
y = ppos.y + offset.y + 1.5,
|
||||
z = ppos.z + offset.z}
|
||||
local node_level = minetest.get_node_level(player_eye_pos)
|
||||
if node_level == 8 or node_level == 7 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- trying to locate position for particles by player look direction for performance reason.
|
||||
-- it is costly to generate many particles around player so goal is focus mainly on front view.
|
||||
utils.get_random_pos = function(player, offset)
|
||||
local look_dir = player:get_look_dir()
|
||||
local player_pos = player:getpos()
|
||||
|
||||
local random_pos_x = 0
|
||||
local random_pos_y = 0
|
||||
local random_pos_z = 0
|
||||
|
||||
if look_dir.x > 0 then
|
||||
if look_dir.z > 0 then
|
||||
random_pos_x = math.random(player_pos.x - offset.back, player_pos.x + offset.front)
|
||||
random_pos_z = math.random(player_pos.z - offset.back, player_pos.z + offset.front)
|
||||
else
|
||||
random_pos_x = math.random(player_pos.x - offset.back, player_pos.x + offset.front)
|
||||
random_pos_z = math.random(player_pos.z - offset.front, player_pos.z + offset.back)
|
||||
end
|
||||
else
|
||||
if look_dir.z > 0 then
|
||||
random_pos_x = math.random(player_pos.x - offset.front, player_pos.x + offset.back)
|
||||
random_pos_z = math.random(player_pos.z - offset.back, player_pos.z + offset.front)
|
||||
else
|
||||
random_pos_x = math.random(player_pos.x - offset.front, player_pos.x + offset.back)
|
||||
random_pos_z = math.random(player_pos.z - offset.front, player_pos.z + offset.back)
|
||||
end
|
||||
end
|
||||
|
||||
random_pos_y = player_pos.y + offset.top
|
||||
return {x=random_pos_x, y=random_pos_y, z=random_pos_z}
|
||||
end
|
Loading…
Reference in New Issue