678 lines
14 KiB
Lua
678 lines
14 KiB
Lua
|
|
|
|
|
|
local FREQ_BIAS = -.6
|
|
local FREQ_SCALE = 3
|
|
local SPEED = .02
|
|
|
|
local fns = {
|
|
on_strike = {},
|
|
freq_bias = {},
|
|
intens_bias = {},
|
|
heat_bias = {},
|
|
humidity_bias = {},
|
|
}
|
|
|
|
|
|
storms = {
|
|
}
|
|
|
|
|
|
|
|
local perlins = {}
|
|
|
|
|
|
local sounds = {
|
|
thunder = {
|
|
"storms_thunder_01_ccby_hantorio",
|
|
"storms_thunder_01_ccby_hantorio",
|
|
-- "storms_thunder_02_PD",
|
|
"storms_thunder_03_PD",
|
|
},
|
|
}
|
|
|
|
|
|
|
|
local function color_lerp(a, b, x)
|
|
local x1 = 1.0 - x
|
|
|
|
return {
|
|
r = a.r * x1 + b.r * x,
|
|
g = a.g * x1 + b.g * x,
|
|
b = a.b * x1 + b.b * x,
|
|
-- a = a.a * x1 + b.a * x,
|
|
}
|
|
end
|
|
|
|
|
|
storms.register_on_lightning_strike = function(fn)
|
|
table.insert(fns.on_strike, fn)
|
|
end
|
|
|
|
|
|
storms.register_freq_bias = function(fn)
|
|
table.insert(fns.freq_bias, fn)
|
|
end
|
|
|
|
storms.register_intensity_bias = function(fn)
|
|
table.insert(fns.intens_bias, fn)
|
|
end
|
|
|
|
storms.register_heat_bias = function(fn)
|
|
table.insert(fns.heat_bias, fn)
|
|
end
|
|
|
|
storms.register_humidity_bias = function(fn)
|
|
table.insert(fns.humidty_bias, fn)
|
|
end
|
|
|
|
|
|
local function get_intens_bias(pos, orig_intens, player)
|
|
local bias = 0
|
|
for _,fn in ipairs(fns.intens_bias) do
|
|
bias = bias + fn(pos, orig_intens, player)
|
|
end
|
|
return bias
|
|
end
|
|
|
|
local function get_freq_bias(pos, orig_freq, player)
|
|
local bias = 0
|
|
for _,fn in ipairs(fns.freq_bias) do
|
|
bias = bias + fn(pos, orig_freq, player)
|
|
end
|
|
return bias
|
|
end
|
|
|
|
local function get_heat_bias(pos, orig)
|
|
local bias = 0
|
|
for _,fn in ipairs(fns.heat_bias) do
|
|
bias = bias + fn(pos, orig)
|
|
end
|
|
return bias
|
|
end
|
|
|
|
local function get_humidity_bias(pos, orig)
|
|
local bias = 0
|
|
for _,fn in ipairs(fns.humidity_bias) do
|
|
bias = bias + fn(pos, orig)
|
|
end
|
|
return bias
|
|
end
|
|
|
|
local on = false
|
|
local storm_players = {}
|
|
|
|
|
|
local good_biomes = {}
|
|
for _,def in pairs(minetest.registered_biomes) do
|
|
if def.y_max >= 10 and def.y_min <= 10 then
|
|
table.insert(good_biomes, def)
|
|
end
|
|
end
|
|
|
|
local function get_noise(pos)
|
|
return heat_noise:get2d({x=pos.x, y=pos.z}), humidity_noise:get2d({x=pos.x, y=pos.z})
|
|
end
|
|
|
|
|
|
local function find_biome(he, hu)
|
|
local smallest = 99999999999
|
|
local tmp = nil
|
|
|
|
for _,def in pairs(good_biomes) do
|
|
local a = he - def.heat_point
|
|
local b = hu - def.humidity_point
|
|
local c = math.sqrt(a*a + b*b)
|
|
if c < smallest then
|
|
smallest = c
|
|
tmp = def
|
|
end
|
|
end
|
|
|
|
return tmp.name
|
|
end
|
|
|
|
local function get_biome(pos)
|
|
local he, hu = get_noise(pos)
|
|
he = he + get_heat_bias(pos, he)
|
|
hu = hu + get_humidity_bias(pos, hu)
|
|
|
|
return find_biome(he, hu)
|
|
end
|
|
|
|
|
|
minetest.after(0, function()
|
|
local noise = minetest.get_mapgen_setting_noiseparams("mg_biome_np_heat")
|
|
heat_noise = minetest.get_perlin(noise)
|
|
|
|
noise = minetest.get_mapgen_setting_noiseparams("mg_biome_np_humidity")
|
|
humidity_noise = minetest.get_perlin(noise)
|
|
end)
|
|
|
|
|
|
|
|
|
|
|
|
local function randompos(center, dist)
|
|
return {
|
|
x = center.x + math.random(-dist, dist),
|
|
y = center.y,
|
|
z = center.z + math.random(-dist, dist),
|
|
}
|
|
end
|
|
|
|
local function pcopy(p)
|
|
return {x=p.x, y=p.y, z=p.z}
|
|
end
|
|
|
|
local function do_lightning(cloudh, pos)
|
|
|
|
local h = cloudh - 10
|
|
|
|
while h > -15 do
|
|
minetest.add_particle({
|
|
pos = {x=pos.x, y=h, z=pos.z},
|
|
velocity = {x=0, y=0, z=0},
|
|
acceleration = {x=0, y=0, z=0},
|
|
expirationtime = .1,
|
|
size = 300,
|
|
collisiondetection = false,
|
|
vertical = true,
|
|
texture = "storms_lightning.png",
|
|
playername = "singleplayer"
|
|
})
|
|
|
|
h = h - 30
|
|
end
|
|
|
|
local p = {x=pos.x, y=cloudh, z=pos.z}
|
|
while p.y >= -1 do
|
|
local n = minetest.get_node(p)
|
|
if n.name ~= "air" and n.name ~= "ignore" then
|
|
|
|
-- node callbacks
|
|
local def = minetest.registered_nodes[n.name]
|
|
if def.on_lightning_strike then
|
|
def.on_lightning_strike(pcopy(p))
|
|
end
|
|
|
|
-- global callbacks
|
|
for _,fn in pairs(fns.on_strike) do
|
|
fn(pcopy(p), n)
|
|
end
|
|
|
|
break
|
|
end
|
|
|
|
p.y = p.y - 1
|
|
end
|
|
|
|
minetest.sound_play(sounds.thunder[math.random(#sounds.thunder)], {
|
|
pos = p,
|
|
max_hear_distance = 100,
|
|
gain = 5.0,
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function spawn_blizzard(pos, vel, sz)
|
|
local ht = 7
|
|
|
|
minetest.add_particlespawner({
|
|
amount = 4000,
|
|
time = 5,
|
|
minpos = vector.add({x=pos.x-sz, y=pos.y-ht, z=pos.z-sz}, vector.multiply(vel, -.95)),
|
|
maxpos = vector.add({x=pos.x+sz, y=pos.y+ht, z=pos.z+sz}, vector.multiply(vel, -.95)),
|
|
minvel = vector.add(vel, {x=-1, y=0, z=-1}),
|
|
maxvel = vector.add(vel, {x=5, y=0.5, z=5}),
|
|
minacc = {x=-01.1, y=0.1, z=-01.1},
|
|
maxacc = {x=01.1, y=01.3, z=01.1},
|
|
minexptime = 1.5,
|
|
maxexptime = 2.5,
|
|
collisiondetection = true,
|
|
collision_removal = true,
|
|
minsize = 40,
|
|
maxsize = 45,
|
|
texture = "storms_snow.png",
|
|
})
|
|
end
|
|
|
|
local function spawn_sandstorm(pos, vel, sz, lvl)
|
|
local ht = 7
|
|
|
|
minetest.add_particlespawner({
|
|
amount = lvl * 5000,
|
|
time = 5,
|
|
minpos = vector.add({x=pos.x-sz, y=pos.y-ht, z=pos.z-sz}, vector.multiply(vel, -.95)),
|
|
maxpos = vector.add({x=pos.x+sz, y=pos.y+ht, z=pos.z+sz}, vector.multiply(vel, -.95)),
|
|
minvel = vector.add(vel, {x=-1, y=0, z=-1}),
|
|
maxvel = vector.add(vel, {x=5, y=0.5, z=5}),
|
|
minacc = {x=-01.1, y=0.1, z=-01.1},
|
|
maxacc = {x=01.1, y=01.3, z=01.1},
|
|
minexptime = 1.5,
|
|
maxexptime = 2.5,
|
|
collisiondetection = true,
|
|
collision_removal = true,
|
|
minsize = 40,
|
|
maxsize = 45,
|
|
texture = "storms_dust.png",
|
|
})
|
|
end
|
|
|
|
|
|
local function spawn_rainclouds(pos, vel, sz, lvl)
|
|
local offht = 60
|
|
local ht = 10
|
|
|
|
minetest.add_particlespawner({
|
|
amount = lvl * 5000,
|
|
time = 5,
|
|
minpos = {x=pos.x-sz, y=pos.y+offht, z=pos.z-sz},
|
|
maxpos = {x=pos.x+sz, y=pos.y+offht+ht, z=pos.z+sz},
|
|
minvel = vector.add(vel, {x=-1, y=0, z=-1}),
|
|
maxvel = vector.add(vel, {x=5, y=0.5, z=5}),
|
|
minacc = {x=-0.1, y=0.1, z=-0.1},
|
|
maxacc = {x=0.1, y=0.3, z=0.1},
|
|
minexptime = 2,
|
|
maxexptime = 7,
|
|
minsize = 300,
|
|
maxsize = 400,
|
|
texture = "storms_cloud.png^[colorize:black:120",
|
|
})
|
|
end
|
|
|
|
local function spawn_rain(pos, vel, sz, lvl)
|
|
local offht = 10
|
|
local ht = 10
|
|
|
|
minetest.add_particlespawner({
|
|
amount = lvl * 1000,
|
|
time = 5,
|
|
minpos = {x=pos.x-sz, y=pos.y+offht, z=pos.z-sz},
|
|
maxpos = {x=pos.x+sz, y=pos.y+offht+ht, z=pos.z+sz},
|
|
minvel = {x=vel.x, y=-40, z=vel.z},
|
|
maxvel = {x=vel.x, y=-40, z=vel.z},
|
|
minacc = {x=-0.1, y=0.1, z=-0.1},
|
|
maxacc = {x=0.1, y=0.3, z=0.1},
|
|
collisiondetection = true,
|
|
collision_removal = true,
|
|
minexptime = 2,
|
|
maxexptime = 7,
|
|
minsize = 10,
|
|
maxsize = 15,
|
|
texture = "storms_raindrop.png",
|
|
})
|
|
end
|
|
|
|
local function spawn_snow(pos, vel, sz, lvl)
|
|
local offht = 10
|
|
local ht = 10
|
|
|
|
minetest.add_particlespawner({
|
|
amount = lvl * 1000,
|
|
time = 5,
|
|
minpos = {x=pos.x-sz, y=pos.y+offht, z=pos.z-sz},
|
|
maxpos = {x=pos.x+sz, y=pos.y+offht+ht, z=pos.z+sz},
|
|
minvel = {x=vel.x, y=-20, z=vel.z},
|
|
maxvel = {x=vel.x, y=-20, z=vel.z},
|
|
minacc = {x=-0.1, y=0.1, z=-0.1},
|
|
maxacc = {x=0.1, y=0.3, z=0.1},
|
|
collisiondetection = true,
|
|
collision_removal = true,
|
|
minexptime = 2,
|
|
maxexptime = 7,
|
|
minsize = 10,
|
|
maxsize = 15,
|
|
texture = "storms_snowflake.png",
|
|
})
|
|
end
|
|
|
|
|
|
local function spawn_lightning(pos, amount, sz)
|
|
local offht = 60
|
|
for i = 1,math.random(amount) do
|
|
minetest.after(math.random(5), function()
|
|
do_lightning(pos.y+60, randompos(pos, sz))
|
|
end)
|
|
end
|
|
end
|
|
|
|
|
|
local biome_skies = {
|
|
["tundra"] = {color = {r=255, g=255, b=255}, clouds = false},
|
|
["taiga"] = {color = {r=255, g=255, b=255}, clouds = false},
|
|
["snowy_grassland"] = {color = {r=255, g=255, b=255}, clouds = false},
|
|
["cold_desert"] = {color = {r=255, g=255, b=255}, clouds = false},
|
|
|
|
["desert"] = {color = {r=130, g=105, b=25}, clouds = false},
|
|
["sandstone_desert"] = {color = {r=130, g=105, b=25}, clouds = false},
|
|
|
|
["grassland"] = {color = {r=20, g=20, b=30}, clouds = false},
|
|
["deciduous_forest"] = {color = {r=20, g=20, b=30}, clouds = false},
|
|
["coniferous_forest"] = {color = {r=20, g=20, b=30}, clouds = false},
|
|
["savanna"] = {color = {r=20, g=20, b=30}, clouds = false},
|
|
["rainforest"] = {color = {r=20, g=20, b=30}, clouds = false},
|
|
}
|
|
|
|
|
|
local biome_spawners = {}
|
|
|
|
|
|
biome_spawners.tundra = function(pos, dir, lvl)
|
|
spawn_blizzard(pos, dir, 15, lvl)
|
|
end
|
|
|
|
biome_spawners.taiga = function(pos, dir, lvl)
|
|
spawn_snow(pos, {x=0, y=0, z=0}, 20, lvl)
|
|
end
|
|
|
|
biome_spawners.grassland = function(pos, dir, lvl)
|
|
spawn_rainclouds(pos, dir, 200, lvl)
|
|
spawn_rain(pos, {x=0, y=0, z=0}, 20, lvl)
|
|
spawn_lightning(pos, 15 * lvl, 60)
|
|
end
|
|
|
|
biome_spawners.snowy_grassland = function(pos, dir, lvl)
|
|
spawn_blizzard(pos, dir, 15, lvl)
|
|
end
|
|
|
|
biome_spawners.savanna = function(pos, dir, lvl)
|
|
spawn_rainclouds(pos, dir, 200, lvl)
|
|
-- spawn_rain(pos, {x=0, y=0, z=0}, 10)
|
|
spawn_lightning(pos, 30 * lvl, 70)
|
|
end
|
|
|
|
biome_spawners.deciduous_forest = function(pos, dir, lvl)
|
|
spawn_rainclouds(pos, dir, 200, lvl)
|
|
spawn_rain(pos, {x=0, y=0, z=0}, 20, lvl)
|
|
end
|
|
|
|
biome_spawners.rainforest = function(pos, dir, lvl)
|
|
spawn_rainclouds(pos, dir, 200, lvl)
|
|
spawn_rain(pos, {x=0, y=0, z=0}, 20, lvl)
|
|
end
|
|
|
|
biome_spawners.coniferous_forest = function(pos, dir, lvl)
|
|
spawn_rainclouds(pos, dir, 200, lvl)
|
|
spawn_rain(pos, {x=0, y=0, z=0}, 20, lvl)
|
|
end
|
|
|
|
biome_spawners.cold_desert = function(pos, dir, lvl)
|
|
spawn_blizzard(pos, dir, 15, lvl)
|
|
end
|
|
|
|
biome_spawners.desert = function(pos, dir, lvl)
|
|
spawn_sandstorm(pos, dir, 15, lvl)
|
|
end
|
|
|
|
biome_spawners.sandstone_desert = function(pos, dir, lvl)
|
|
spawn_sandstorm(pos, dir, 15, lvl)
|
|
end
|
|
|
|
|
|
local function set_biome_storm_sky(pinfo, b1, b2, n_biome, n_normal)
|
|
local sky1 = biome_skies[b1]
|
|
local sky2 = biome_skies[b2]
|
|
if not sky1 then
|
|
print("missing biome: ".. b1)
|
|
return
|
|
end
|
|
if not sky2 then
|
|
print("missing biome: ".. b2)
|
|
return
|
|
end
|
|
|
|
local color = color_lerp(sky1.color, sky2.color, n_biome)
|
|
color = color_lerp(color, pinfo.default_sky.color, n_normal)
|
|
|
|
local sky
|
|
if n_normal > .75 then
|
|
sky = pinfo.default_sky
|
|
elseif n_biome > .5 then
|
|
sky = sky2
|
|
else
|
|
sky = sky1
|
|
end
|
|
|
|
pinfo.player:set_sky(color, sky.type or "plain", sky.tex, sky.clouds or false)
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
minetest.register_craftitem("storms:rainstick", {
|
|
description = "Magic Rainstick",
|
|
inventory_image = "default_stick.png^[colorize:gold:80",
|
|
stack_max = 1,
|
|
on_use = function(itemstack, player, pointed_thing)
|
|
|
|
|
|
if sky_defaults == nil then
|
|
sky_defaults = {}
|
|
sky_defaults.col, sky_defaults.tp, sky_defaults.tex, sky_defaults.cl = player:get_sky()
|
|
end
|
|
|
|
|
|
if on then
|
|
on = false
|
|
player:set_sky(sky_defaults.col, sky_defaults.tp, sky_defaults.tex, sky_defaults.cl)
|
|
else
|
|
on = true
|
|
|
|
|
|
|
|
local function spawn_storm()
|
|
local pos = player:get_pos()
|
|
local biome = get_biome(pos)
|
|
|
|
|
|
local fn = biome_spawners[biome]
|
|
|
|
if not fn then
|
|
print("missing spawner biome: "..biome)
|
|
end
|
|
fn(pos, {x=20, y=0, x=10}, 1)
|
|
|
|
-- set_biome_storm_sky(player, biome)
|
|
|
|
if on then
|
|
minetest.after(5, function()
|
|
spawn_storm()
|
|
end)
|
|
end
|
|
|
|
end
|
|
|
|
spawn_storm()
|
|
|
|
player:set_sky({r=20, g=20, b=30}, "plain", nil, false)
|
|
end
|
|
|
|
|
|
end,
|
|
})
|
|
|
|
|
|
storms.register_on_lightning_strike(function(pos)
|
|
-- pos.y = pos.y + 1
|
|
-- minetest.set_node(pos, {name="fire:basic_flame"})
|
|
|
|
end)
|
|
|
|
|
|
local function get_player_sky(player)
|
|
local sky = {}
|
|
sky.color, sky.type, sky.tex, sky.clouds = player:get_sky()
|
|
return sky
|
|
end
|
|
|
|
|
|
|
|
|
|
minetest.register_on_joinplayer(function(player)
|
|
local name = player:get_player_name()
|
|
storm_players[name] = {
|
|
a = nil,
|
|
b = nil,
|
|
fill = 1,
|
|
player = player,
|
|
default_sky = get_player_sky(player),
|
|
}
|
|
|
|
print(dump(storm_players[name].default_sky))
|
|
end)
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
local name = player:get_player_name()
|
|
storm_players[name] = nil
|
|
end)
|
|
|
|
|
|
local function activate_storm(player )
|
|
local pos = player:get_pos()
|
|
local biome = get_biome(pos)
|
|
|
|
|
|
local fn = biome_spawners[biome]
|
|
|
|
if not fn then
|
|
print("missing spawner biome: "..biome)
|
|
end
|
|
fn(pos)
|
|
|
|
end
|
|
|
|
local function clamp(min, max, n)
|
|
if n < min then return min end
|
|
if n > max then return max end
|
|
return n
|
|
end
|
|
|
|
local function storm_loop()
|
|
local time = minetest.get_gametime()
|
|
|
|
for name,pinfo in pairs(storm_players) do
|
|
local pos = pinfo.player:get_pos()
|
|
|
|
local t = math.sin(time / (5 * 2 * math.pi))
|
|
local t2 = math.cos(time / (7 * 2 * math.pi))
|
|
|
|
|
|
local p1 = {
|
|
x = ((t * 20 + t2 * 30 + pos.x + time) % 65536) - 32768,
|
|
y = 0,
|
|
z = ((t * 20 + t2 * 30 + pos.z + time) % 65536) - 32768,
|
|
}
|
|
|
|
local dx = perlins.dx:get2dMap_flat(p1)[1]
|
|
local dz = perlins.dz:get2dMap_flat(p1)[1]
|
|
|
|
|
|
-- for i = 1,2000,3 do
|
|
-- local f3 = perlins.freq3:get3dMap_flat({x=pos.x, y=pos.z, z=i --[[(time *20) % 2000]]})[1]
|
|
-- f3 = (f3 * 4) - 1.6
|
|
-- print("perlin: ".. dump(f3))
|
|
-- end
|
|
local f3 = perlins.freq3:get3dMap_flat({x=pos.x, y=pos.z, z=(time * SPEED) % 65536})[1]
|
|
f3 = (f3 * FREQ_SCALE) + FREQ_BIAS -- - 1.6
|
|
-- print("perlin: ".. dump(f3))
|
|
|
|
f3 = f3 + get_freq_bias(pos, f3, pinfo.player)
|
|
|
|
local biome = get_biome(pos)
|
|
-- f3 = nil
|
|
-- print("storm intensity: ".. f .. " ("..f1..", "..f2..")")
|
|
local intens = f3 + get_intens_bias(pos, f3, pinfo.player)
|
|
if f3 > 0 then
|
|
|
|
local dir = {x = dx, y = 0, z = dz}
|
|
|
|
local fn = biome_spawners[biome]
|
|
|
|
if not fn then
|
|
print("missing spawner biome: "..biome)
|
|
end
|
|
fn(pos, dir, 1, intens)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
set_biome_storm_sky(pinfo, biome, biome, 1, clamp(0, 1, 1-intens))
|
|
end
|
|
|
|
|
|
minetest.after(5, storm_loop)
|
|
end
|
|
|
|
|
|
minetest.after(2, function()
|
|
|
|
perlins.dx = minetest.get_perlin_map({
|
|
flags = {eased = false},
|
|
lacunarity = 4,
|
|
octaves = 4,
|
|
offset = 0,
|
|
persistence = 0.65,
|
|
seed = 35363,
|
|
scale = 10,
|
|
spread = {x=1000, y=1000, z=1000}
|
|
}, {x=1,y=1,z=1})
|
|
|
|
perlins.dz = minetest.get_perlin_map({
|
|
flags = {eased = false},
|
|
lacunarity = 4,
|
|
octaves = 4,
|
|
offset = 0,
|
|
persistence = 0.65,
|
|
seed = 678786,
|
|
scale = 10,
|
|
spread = {x=1000, y=1000, z=1000}
|
|
}, {x=1,y=1,z=1})
|
|
|
|
--[[
|
|
perlins.freq1 = minetest.get_perlin_map({
|
|
flags = {eased = false},
|
|
lacunarity = 2,
|
|
octaves = 3,
|
|
offset = 0,
|
|
persistence = 0.25,
|
|
seed = 79932,
|
|
scale = 1,
|
|
spread = {x=2000, y=2000, z=2000}
|
|
}, {x=1,y=1,z=1})
|
|
|
|
perlins.freq2 = minetest.get_perlin_map({
|
|
flags = {eased = false},
|
|
lacunarity = 2,
|
|
octaves = 1,
|
|
offset = 0,
|
|
persistence = 0.25,
|
|
seed = 6445,
|
|
scale = 1,
|
|
spread = {x=2000, y=2000, z=2000}
|
|
}, {x=1,y=1,z=1})
|
|
]]
|
|
|
|
perlins.freq3 = minetest.get_perlin_map({
|
|
flags = {eased = false},
|
|
lacunarity = 4,
|
|
octaves = 5,
|
|
offset = 0,
|
|
persistence = 0.45,
|
|
seed = 179334,
|
|
scale = 1,
|
|
spread = {x=500, y=500, z=500}
|
|
}, {x=2,y=2,z=2})
|
|
|
|
|
|
storm_loop()
|
|
end)
|
|
|