223 lines
5.5 KiB
Lua
223 lines
5.5 KiB
Lua
local modpath = ...
|
||
local climatez = {}
|
||
climatez.wind = {}
|
||
climatez.climates = {}
|
||
climatez.settings = {}
|
||
|
||
--Settings
|
||
|
||
local settings = Settings(modpath .. "/climatez.conf")
|
||
|
||
climatez.settings.climate_change_ratio = tonumber(settings:get("climate_change_ratio"))
|
||
climatez.settings.radius = tonumber(settings:get("climate_radius"))
|
||
climatez.settings.climate_duration = tonumber(settings:get("climate_duration"))
|
||
climatez.settings.duration_random_ratio = tonumber(settings:get("climate_duration_random_ratio"))
|
||
|
||
local check_light = minetest.is_yes(minetest.settings:get_bool('light_roofcheck', true))
|
||
|
||
--Helper Functions
|
||
|
||
local function player_inside_climate(player_pos)
|
||
--If sphere's centre coordinates is (cx,cy,cz) and its radius is r,
|
||
--then point (x,y,z) is in the sphere if (x−cx)2+(y−cy)2+(z−cz)2<r2.
|
||
for i, climate in ipairs(climatez.climates) do
|
||
local climate_center = climatez.climates[i].center
|
||
if climatez.settings.radius > math.sqrt((player_pos.x - climate_center.x)^2+
|
||
(player_pos.y - climate_center.y)^2 +
|
||
(player_pos.z - climate_center.z)^2
|
||
) then
|
||
return i
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
local function has_light(minp, maxp)
|
||
local manip = minetest.get_voxel_manip()
|
||
local e1, e2 = manip:read_from_map(minp, maxp)
|
||
local area = VoxelArea:new{MinEdge=e1, MaxEdge=e2}
|
||
local data = manip:get_light_data()
|
||
local node_num = 0
|
||
local light = false
|
||
|
||
for i in area:iterp(minp, maxp) do
|
||
node_num = node_num + 1
|
||
if node_num < 5 then
|
||
if data[i] and data[i] == 15 then
|
||
light = true
|
||
break
|
||
end
|
||
else
|
||
node_num = 0
|
||
end
|
||
end
|
||
|
||
return light
|
||
end
|
||
|
||
local function array_remove(tab, idx)
|
||
tab[idx] = nil
|
||
local new_tab = {}
|
||
for _, value in pairs(tab) do
|
||
new_tab[ #new_tab+1] = value
|
||
end
|
||
return new_tab
|
||
end
|
||
|
||
--DOWNFALLS REGISTRATIONS
|
||
|
||
climatez.registered_downfalls = {}
|
||
|
||
local function register_downfall(name, def)
|
||
local new_def = table.copy(def)
|
||
climatez.registered_downfalls[name] = new_def
|
||
end
|
||
|
||
register_downfall("rain", {
|
||
min_pos = {x = -15, y = 10, z = -15},
|
||
max_pos = {x = 15, y = 10, z = 15},
|
||
falling_speed = 10,
|
||
amount = 25,
|
||
exptime = 1,
|
||
size = 1,
|
||
texture = "climatez_rain.png",
|
||
})
|
||
|
||
register_downfall("snow", {
|
||
min_pos = {x = -15, y = 10, z= -15},
|
||
max_pos = {x = 15, y = 10, z = 15},
|
||
falling_speed = 5,
|
||
amount = 15,
|
||
exptime = 7,
|
||
size = 1,
|
||
texture= "climatez_snow.png",
|
||
})
|
||
|
||
register_downfall("sand", {
|
||
min_pos = {x = -20, y = -4, z = -20},
|
||
max_pos = {x = 20, y = 4, z = 20},
|
||
falling_speed = -1,
|
||
amount = 40,
|
||
exptime = 1,
|
||
size = 1,
|
||
texture = "climatez_sand.png",
|
||
})
|
||
|
||
--WIND STUFF
|
||
|
||
local function create_wind()
|
||
local wind = {
|
||
x = math.random(0,10),
|
||
y = 0,
|
||
z = math.random(0,10)
|
||
}
|
||
return wind
|
||
end
|
||
|
||
function get_player_wind(player)
|
||
local player_pos = player:get_pos()
|
||
local climate_id = player_inside_climate(player_pos)
|
||
if climate_id then
|
||
return climatez.climates[climate_id].wind
|
||
else
|
||
return create_wind()
|
||
end
|
||
end
|
||
|
||
--CLIMATE FUNCTIONS
|
||
|
||
local function create_climate(player_pos)
|
||
--get some data
|
||
local biome_data = minetest.get_biome_data(player_pos)
|
||
local biome_heat = biome_data.heat
|
||
local biome_humidity = biome_data.humidity
|
||
|
||
local downfall
|
||
|
||
if biome_heat > 40 and biome_humidity > 50 then
|
||
downfall = "rain"
|
||
elseif biome_heat > 50 and biome_humidity < 20 then
|
||
downfall = "sand"
|
||
else
|
||
downfall = "snow"
|
||
end
|
||
|
||
if not downfall then
|
||
return
|
||
end
|
||
|
||
--create wind
|
||
local wind = create_wind()
|
||
|
||
--create climate
|
||
local climate_id = #climatez.climates+1
|
||
climatez.climates[climate_id]= {
|
||
center = player_pos,
|
||
downfall = downfall,
|
||
wind = wind,
|
||
}
|
||
|
||
--program climate's end
|
||
local climate_duration = climatez.settings.climate_duration
|
||
local climate_duration_random_ratio = climatez.settings.duration_random_ratio
|
||
local random_end_time = (math.random(climate_duration- (climate_duration*climate_duration_random_ratio),
|
||
climate_duration+ (climate_duration*climate_duration_random_ratio)))
|
||
minetest.after(random_end_time, function()
|
||
climatez.climates = array_remove(climatez.climates, climate_id)
|
||
end)
|
||
end
|
||
|
||
local function apply_climate(player, player_pos, climate_id)
|
||
|
||
local climate = climatez.climates[climate_id]
|
||
local downfall = climatez.registered_downfalls[climate.downfall]
|
||
local wind = climatez.climates[climate_id].wind
|
||
local wind_pos = vector.multiply(wind, -1)
|
||
local minp = vector.add(vector.add(player_pos, downfall.min_pos), wind_pos)
|
||
local maxp = vector.add(vector.add(player_pos, downfall.max_pos), wind_pos)
|
||
|
||
--Check if in player in interiors or not
|
||
if check_light and not has_light(minp, maxp) then
|
||
return
|
||
end
|
||
|
||
local vel = {x = wind.x, y = - downfall.falling_speed, z = wind.z}
|
||
local acc = {x = 0, y = 0, z = 0}
|
||
local exp = downfall.exptime
|
||
|
||
minetest.add_particlespawner({
|
||
amount = downfall.amount, time=0.5,
|
||
minpos = minp, maxpos = maxp,
|
||
minvel = vel, maxvel = vel,
|
||
minacc = acc, maxacc = acc,
|
||
minexptime = exp, maxexptime = exp,
|
||
minsize = downfall.size, maxsize= downfall.size,
|
||
collisiondetection = true, collision_removal = true,
|
||
vertical = true,
|
||
texture = downfall.texture, playername = player:get_player_name()
|
||
})
|
||
end
|
||
|
||
--CLIMATE CORE: GLOBALSTEP
|
||
|
||
local timer = 0
|
||
minetest.register_globalstep(function(dtime)
|
||
timer = timer + dtime;
|
||
local player_pos, climate_id
|
||
for _, player in ipairs(minetest.get_connected_players()) do
|
||
player_pos = player:get_pos()
|
||
climate_id = player_inside_climate(player_pos)
|
||
if climate_id then
|
||
apply_climate(player, player_pos, climate_id)
|
||
else
|
||
if timer >= 1 then
|
||
local chance = math.random(climatez.settings.climate_change_ratio)
|
||
if chance == 1 then
|
||
create_climate(player_pos)
|
||
end
|
||
timer = 0
|
||
end
|
||
end
|
||
end
|
||
end)
|