609 lines
16 KiB
Lua
609 lines
16 KiB
Lua
--nothing--
|
|
local S = minetest.get_translator("nc_sky_isgen")
|
|
local grass = "nc_terrain:dirt_with_grass"
|
|
local dirt = "nc_terrain:dirt"
|
|
local tree = "nc_tree:eggcorn_planted"
|
|
local stone = "nc_terrain:stone"
|
|
local queue = {}
|
|
local cooldown = 120
|
|
local cooldowns = {}
|
|
|
|
local function maxval(octaves,persistence,scale)
|
|
local m = 1
|
|
local poc = 1
|
|
if octaves > 1 then
|
|
for n=2,octaves do
|
|
poc = poc*persistence
|
|
m=m+poc
|
|
end
|
|
end
|
|
return scale/m
|
|
end
|
|
|
|
local function dist(x1,y1,z1, x2,y2,z2)
|
|
local x,y,z = x2-x1, y2-y1, z2-z1
|
|
return (x*x+y*y+z*z)^0.5
|
|
end
|
|
|
|
local function cosify(x)
|
|
return 1-math.cos(x*math.pi/2)
|
|
end
|
|
|
|
local pers = 0.75
|
|
local oct = 5
|
|
|
|
local noise = PerlinNoise{
|
|
scale = maxval(oct,pers,64),
|
|
spread = {x=128,y=64,z=128},
|
|
seed=1297,
|
|
octaves = oct,
|
|
persistence = pers,
|
|
lacunarity = 2,
|
|
flags = "eased"
|
|
}
|
|
|
|
local function sigmoid(z)
|
|
return 1/(1+math.exp(-z))
|
|
end
|
|
|
|
local function island(pos,r,call)
|
|
local x,y,z = pos.x,pos.y,pos.z
|
|
minetest.emerge_area({x=x-r,y=y-r,z=z-r},{x=x+r,y=y+r,z=z+r},function(bp,act,crem)
|
|
if crem > 0 then
|
|
return
|
|
end
|
|
local n = 0
|
|
local c = 0
|
|
local cm = (r*2+1)^3
|
|
local function geto(x1,y1,z1,n)
|
|
local r1 = 48+n
|
|
local dista = dist(0,0,0,x1,y1*3,z1)
|
|
local m = 1-dista/r1
|
|
local r2 = -(sigmoid((48+n/2)/48*3)*56)
|
|
if y1 <= 0 and y1 >= r2 then
|
|
local r3 = (1-math.sin(y1/r2*math.pi/2))*r1
|
|
local distb = dist(0,y1,0,x1,y1,z1)
|
|
local m2 = 1-distb/r3
|
|
m = math.max(m2,m)
|
|
end
|
|
return m
|
|
end
|
|
local grasses = {}
|
|
local stones = {}
|
|
for x=-r,r do
|
|
for z=-r,r do
|
|
for y=-r,r do
|
|
local xx,yy,zz = pos.x+x,pos.y+y,pos.z+z
|
|
local ppos = {x=xx,y=yy,z=zz}
|
|
local o,o2 = geto(x,y,z,noise:get_3d(ppos)),geto(x,y+1,z,noise:get_3d({x=xx,y=yy+1,z=zz}))
|
|
local oc = (o > 0.4)
|
|
local oc2 = o2 > 0.4
|
|
local og = (o > 0.5) and o2 > 0.4
|
|
if oc then
|
|
n=n+1
|
|
local nam = dirt
|
|
if not og and not oc2 then
|
|
nam = grass
|
|
table.insert(grasses,ppos)
|
|
end
|
|
if og then
|
|
nam = stone
|
|
table.insert(stones,ppos)
|
|
end
|
|
minetest.set_node(ppos,{name=nam})
|
|
else
|
|
n=n+0.01
|
|
end
|
|
if n > 1000 then
|
|
n=0
|
|
--coroutine.yield()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for n=1,24 do
|
|
if #stones > 1 then
|
|
local n = math.random(1,#stones)
|
|
local v = table.remove(stones,n)
|
|
minetest.set_node(v,{name="nc_crystal:ore"})
|
|
end
|
|
if #stones > 1 then
|
|
local n = math.random(1,#stones)
|
|
local v = table.remove(stones,n)
|
|
minetest.set_node(v,{name="nc_lux:stone"})
|
|
end
|
|
end
|
|
for n=1,8 do
|
|
if #grasses > 1 then
|
|
local n = math.random(1,#grasses)
|
|
local v = table.remove(grasses,n)
|
|
minetest.set_node(v,{name=tree})
|
|
local meta = minetest.get_meta(v)
|
|
meta:set_float("growth",5000)
|
|
end
|
|
end
|
|
if call then
|
|
call()
|
|
end
|
|
end)
|
|
end
|
|
local f,abs = math.floor, math.abs
|
|
local function raw_spawn_island(pos,call)
|
|
pos = {x=f(pos.x),y=f(pos.y),z=f(pos.z)}
|
|
island(pos,48,call)
|
|
end
|
|
local function spawn_island(name,pos)
|
|
local ref = minetest.get_player_by_name(name)
|
|
raw_spawn_island(pos,function()
|
|
local ref = minetest.get_player_by_name(name)
|
|
if ref then
|
|
ref:set_pos({x=pos.x,y=pos.y+64,z=pos.z})
|
|
end
|
|
end)
|
|
end
|
|
local store = minetest.get_mod_storage()
|
|
local players = minetest.deserialize(store:get_string("players")) or {}
|
|
local cells = minetest.deserialize(store:get_string("cells")) or {}
|
|
local function save()
|
|
store:set_string("players",minetest.serialize(players))
|
|
store:set_string("cells",minetest.serialize(cells))
|
|
end
|
|
do
|
|
local c = cells
|
|
cells = {}
|
|
for k,v in pairs(c) do
|
|
if v.island then
|
|
if v.valid and not v.name or v.name == "" then
|
|
table.insert(cells,{pos=v.islandpos,valid=v.valid})
|
|
end
|
|
else
|
|
table.insert(cells,v)
|
|
end
|
|
end
|
|
|
|
local p = players
|
|
players = {}
|
|
for k,v in pairs(p) do
|
|
players[k] = {pos=v.islandpos or v.pos, valid = v.valid}
|
|
end
|
|
save()
|
|
end
|
|
|
|
local air_c = minetest.get_content_id("air")
|
|
local ignore_c = minetest.get_content_id("ignore")
|
|
|
|
local function spawn_particle(x,y,z,player)
|
|
minetest.add_particlespawner{
|
|
amount = 7,
|
|
-- Number of particles spawned over the time period `time`.
|
|
|
|
time = 15,
|
|
|
|
minpos = {x=x, y=y, z=z},
|
|
maxpos = {x=x, y=y, z=z},
|
|
minvel = {x=-0.5, y=-0.5, z=-0.5},
|
|
maxvel = {x=0.5, y=0.5, z=0.5},
|
|
minacc = {x=0, y=0, z=0},
|
|
maxacc = {x=0, y=0, z=0},
|
|
minexptime = 0.2,
|
|
maxexptime = 1,
|
|
minsize = 10,
|
|
maxsize = 60,
|
|
vertical = false,
|
|
texture = "nc_stonework_stone.png",
|
|
playername = player,
|
|
|
|
glow = 14
|
|
}
|
|
end
|
|
local function island_range(x,y,z)
|
|
return {x=x-32,y=y-32,z=z-32},{x=x+32,y=y+32,z=z+32}
|
|
end
|
|
local function islandgen_range(x,y,z)
|
|
return {x=x-64,y=y-64,z=z-64},{x=x+64,y=y+64,z=z+64}
|
|
end
|
|
local function spawn_particles(x,y,z,player)
|
|
local mi,ma = island_range(x,y,z)
|
|
local visited = {}
|
|
for n1=0,1 do
|
|
for n2=0,1 do
|
|
minetest.after(0,function() for _,ord1 in ipairs({"x","y","z"}) do
|
|
minetest.after(0,function() for _,ord2 in ipairs({"x","y","z"}) do
|
|
minetest.after(0,function()for _,ord3 in ipairs({"x","y","z"}) do
|
|
if ord2 ~= ord1 and ord3 ~= ord2 and ord3 ~= ord1 then
|
|
for x=mi[ord1],ma[ord1] do
|
|
local pos = {[ord1]=x}
|
|
if n1 == 0 then
|
|
pos[ord2] = mi[ord2]
|
|
else
|
|
pos[ord2] = ma[ord2]
|
|
end
|
|
if n2 == 0 then
|
|
pos[ord3] = mi[ord3]
|
|
else
|
|
pos[ord3] = ma[ord3]
|
|
end
|
|
--print(dump(pos))
|
|
if math.random(3)==1 then
|
|
spawn_particle(pos.x,pos.y,pos.z,player)
|
|
end
|
|
end
|
|
end
|
|
end end)
|
|
end end)
|
|
end end)
|
|
end
|
|
end
|
|
end
|
|
local function checkpos(x,y,z,ch)
|
|
local mi,ma = islandgen_range(f(x),f(y),f(z))
|
|
print("requested check for pos "..x.." "..y.." "..z)
|
|
minetest.emerge_area(mi,ma,function(bpos,act,crem)
|
|
if crem > 0 then
|
|
return
|
|
end
|
|
print("checking pos "..x.." "..y.." "..z)
|
|
local vm = VoxelManip(mi,ma)
|
|
local emi,ema = vm:get_emerged_area()
|
|
local dat = vm:get_data()
|
|
local ar = VoxelArea:new{MinEdge=emi,MaxEdge=ema}
|
|
local v = true
|
|
for x = mi.x,ma.x do
|
|
for y = mi.y,ma.y do
|
|
for z = mi.z,ma.z do
|
|
local d = dat[ar:index(x,y,z)]
|
|
if d ~= air_c then
|
|
v = false
|
|
break
|
|
end
|
|
end
|
|
if not v then break end
|
|
end
|
|
if not v then break end
|
|
end
|
|
print("running callback; v="..tostring(v))
|
|
ch(v)
|
|
end)
|
|
end
|
|
|
|
|
|
local function intersect(a,b)
|
|
local ap1,ap2 = a[1],a[2]
|
|
local bp1,bp2 = b[1],b[2]
|
|
local ax1,ay1,az1 = ap1.x,ap1.y,ap1.z
|
|
local ax2,ay2,az2 = ap2.x,ap2.y,ap2.z
|
|
local bx1,by1,bz1 = bp1.x,bp1.y,bp1.z
|
|
local bx2,by2,bz2 = bp2.x,bp2.y,bp2.z
|
|
|
|
return
|
|
ax1 <= bx2 and
|
|
ax2 >= bx1 and
|
|
ay1 <= by2 and
|
|
ay2 >= by1 and
|
|
az1 <= bz2 and
|
|
az2 >= bz1
|
|
end
|
|
|
|
local function get_standing_island(name)
|
|
local ref = minetest.get_player_by_name(name)
|
|
local pos = ref:get_pos()
|
|
local islands = {}
|
|
for k,v in pairs(cells) do
|
|
if intersect({island_range(v.pos.x,v.pos.y,v.pos.z)},{pos,pos}) then
|
|
table.insert(islands,{pos=v.pos,owner=nil,valid = not not v.valid,t=cells,k=k})
|
|
end
|
|
end
|
|
for k,v in pairs(players) do
|
|
if intersect({island_range(v.pos.x,v.pos.y,v.pos.z)},{pos,pos}) then
|
|
table.insert(islands,{pos=v.pos,owner=k,valid = not not v.valid,t=players,k=k})
|
|
end
|
|
end
|
|
if #islands == 0 then
|
|
return
|
|
end
|
|
local closesti = table.remove(islands)
|
|
local closestl = vector.length(vector.subtract(closesti.pos,pos))
|
|
while #islands > 0 do
|
|
local isl = table.remove(islands)
|
|
local l = vector.length(vector.subtract(isl.pos,pos))
|
|
if l < closestl then
|
|
closesti = isl
|
|
closestl = l
|
|
end
|
|
end
|
|
return closesti
|
|
end
|
|
|
|
local function gen_island_pos(name,fgen)
|
|
local ended = 0
|
|
local function gen(n)
|
|
if n > (fgen or 1) then
|
|
return
|
|
end
|
|
local x,y,z = 0,0,0
|
|
x,z = x+math.random(-64,64),z+math.random(-64,64)
|
|
y=256+math.random(-32,32)
|
|
if not fgen then
|
|
for k,isl in pairs(cells) do
|
|
if isl and isl.valid then
|
|
table.remove(cells,k)
|
|
local ip = isl.pos
|
|
local ref = minetest.get_player_by_name(name)
|
|
local pos = ip
|
|
players[name] = {pos = pos, valid = true}
|
|
save()
|
|
pos = {x=f(pos.x),y=f(pos.y),z=f(pos.z)}
|
|
ref:set_pos({x=pos.x,y=pos.y+64,z=pos.z})
|
|
return
|
|
end
|
|
end
|
|
end
|
|
local function ch(ok)
|
|
if not ok then
|
|
local sx,sz = math.random(-64,64),math.random(-64,64)
|
|
if ((x+sx)^2+(z+sz)^2)^0.5 < (x^2+z^2)^0.5 then
|
|
sx=-sx
|
|
sz=-sz
|
|
end
|
|
x,z = x+sx,z+sz
|
|
y=256+math.random(-32,32)
|
|
checkpos(x,y,z,ch)
|
|
else
|
|
local ip = {x=x,y=y,z=z}
|
|
--checkpos(x,y,z,function(ok)
|
|
-- assert(ok,"super-duper-error")
|
|
if not fgen then
|
|
spawn_island(name,ip)
|
|
else
|
|
raw_spawn_island(ip,function()
|
|
ended = ended + 1
|
|
if ended >= (fgen or 1) then
|
|
minetest.chat_send_player(name,S"Island generation done")
|
|
else
|
|
minetest.chat_send_player(name,S("Generated an island (@1%)", math.floor(ended/(fgen or 1)*1000)/10))
|
|
end
|
|
gen(n+1)
|
|
end)
|
|
end
|
|
local pl = {
|
|
pos = ip,
|
|
valid = true
|
|
}
|
|
if not fgen then
|
|
players[name] = pl
|
|
else
|
|
table.insert(cells,pl)
|
|
end
|
|
save()
|
|
--end)
|
|
end
|
|
end
|
|
checkpos(x,y,z,ch)
|
|
end
|
|
minetest.after(0,gen,1)
|
|
end
|
|
local updrate = 0.5
|
|
local to_upd = updrate
|
|
minetest.register_globalstep(function(dt)
|
|
for f,_ in pairs(queue) do
|
|
local ok,err = coroutine.resume(f)
|
|
if not ok then
|
|
print(err)
|
|
queue[f]=nil
|
|
end
|
|
end
|
|
to_upd = to_upd-dt
|
|
if to_upd <= 0 then
|
|
to_upd = to_upd+updrate
|
|
for k,ref in pairs(minetest.get_connected_players()) do
|
|
local is = get_standing_island(ref:get_player_name())
|
|
if is and not minetest.check_player_privs(ref,{server = true}) then
|
|
is.t[is.k].valid = false
|
|
end
|
|
local p = ref:get_pos()
|
|
if p.y < -128 then
|
|
ref:set_hp(ref:get_hp()-4,{reason="set_hp"})
|
|
if p.y < -256 then
|
|
local land = players[ref:get_player_name()]
|
|
local pos
|
|
if land then
|
|
pos = land.pos
|
|
elseif minetest.check_player_privs(ref,{interact = true}) then
|
|
gen_island_pos(ref:get_player_name())
|
|
end
|
|
if pos then
|
|
pos.y=pos.y+64
|
|
ref:set_pos(pos)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
minetest.register_chatcommand("register", {
|
|
description = S"Register island",
|
|
privs = {server = true},
|
|
func = function(name,param)
|
|
if get_standing_island(name) then
|
|
minetest.chat_send_player(name,S"There is an island already")
|
|
return
|
|
end
|
|
local ref = minetest.get_player_by_name(name)
|
|
local pos = ref:get_pos()
|
|
ip = {x=f(pos.x+.5),y=f(pos.y+.5),z=f(pos.z+.5)}
|
|
table.insert(cells,{pos=ip, valid=false})
|
|
minetest.chat_send_player(name,"OK")
|
|
save()
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("unregister", {
|
|
description = S"Unregister island",
|
|
privs = {server = true},
|
|
func = function(name,param)
|
|
local is = get_standing_island(name)
|
|
if not is then
|
|
minetest.chat_send_player(name,S"No island here")
|
|
return
|
|
end
|
|
if is.t == cells then
|
|
cells[is.k] = nil
|
|
end
|
|
if is.t == players then
|
|
local nam = is.owner
|
|
players[nam] = nil
|
|
end
|
|
minetest.chat_send_player(name,S"OK")
|
|
save()
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("assign", {
|
|
description = S"Reassign this island",
|
|
privs = {server = true},
|
|
func = function(name,param)
|
|
local is = get_standing_island(name)
|
|
if not minetest.get_player_by_name(param) then
|
|
minetest.chat_send_player(name,S("No player @1",param))
|
|
return
|
|
end
|
|
if players[param] then
|
|
local ois = players[param]
|
|
table.insert(cells,{pos=ois.pos,valid = false})
|
|
players[param] = nil
|
|
end
|
|
if not is then
|
|
minetest.chat_send_player(name,S"No island here")
|
|
return
|
|
else
|
|
players[name] = {pos = is.pos, valid = is.valid}
|
|
is.t[is.k] = nil
|
|
end
|
|
minetest.chat_send_player(name,S"OK")
|
|
save()
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("unassign", {
|
|
description = S"Unassign this island",
|
|
privs = {server = true},
|
|
func = function(name,param)
|
|
local is = get_standing_island(name)
|
|
if not is then
|
|
minetest.chat_send_player(name,S"No island here")
|
|
return
|
|
end
|
|
if is.t == cells then
|
|
minetest.chat_send_player(name,S"Island is already unassigned")
|
|
return
|
|
end
|
|
if is.t == players then
|
|
local nam = is.owner
|
|
players[nam] = nil
|
|
table.insert(cells,{pos=is.pos,valid=is.valid})
|
|
end
|
|
minetest.chat_send_player(name,S"OK")
|
|
save()
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("validate", {
|
|
description = S"Make this island avaible for newcomers, if not owned by a player",
|
|
privs = {server = true},
|
|
func = function(name,param)
|
|
local is = get_standing_island(name)
|
|
if is then
|
|
is.t[is.k].valid = true
|
|
minetest.chat_send_player(name,S"OK")
|
|
else
|
|
minetest.chat_send_player(name,S"No island here")
|
|
return
|
|
end
|
|
save()
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("invalidate", {
|
|
description = S"Make this island unavaible for newcomers",
|
|
privs = {server = true},
|
|
func = function(name,param)
|
|
local is = get_standing_island(name)
|
|
if is then
|
|
is.t[is.k].valid = false
|
|
minetest.chat_send_player(name,S"OK")
|
|
else
|
|
minetest.chat_send_player(name,S"No island here")
|
|
return
|
|
end
|
|
save()
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("query", {
|
|
description = S"Query information about this island",
|
|
func = function(name,param)
|
|
local is
|
|
if param ~= "" then
|
|
is = players[param]
|
|
if not is then
|
|
minetest.chat_send_player(name,S("Can't find @1's island",param))
|
|
return
|
|
end
|
|
is = {pos=is.pos,valid=is.valid,owner=param}
|
|
end
|
|
if not is then
|
|
is = get_standing_island(name)
|
|
end
|
|
if is then
|
|
minetest.chat_send_player(name,"[")
|
|
minetest.chat_send_player(name," owner: "..((is.owner == "" and "(none)" or is.owner) or "(none)"))
|
|
minetest.chat_send_player(name," takeable: "..tostring(is.valid))
|
|
minetest.chat_send_player(name," pos: "..minetest.pos_to_string(is.pos))
|
|
minetest.chat_send_player(name,"]")
|
|
local p = is.pos
|
|
spawn_particles(p.x,p.y,p.z,name)
|
|
else
|
|
minetest.chat_send_player(name,S"No island here")
|
|
end
|
|
save()
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("reset",{
|
|
description = S"Get a new island",
|
|
privs = {interact = true},
|
|
func = function(name)
|
|
if not minetest.check_player_privs(name,{server=true}) then
|
|
local utime = minetest.get_server_uptime()
|
|
local cd = cooldowns[name]
|
|
if cd and cd > utime then
|
|
minetest.chat_send_player(name,S("You can't get a new island yet. Wait @1 secs.",(cd-utime)))
|
|
return
|
|
end
|
|
cooldowns[name] = utime + cooldown
|
|
end
|
|
local is = players[name]
|
|
if is then
|
|
players[name] = nil
|
|
table.insert(cells,{pos=is.pos,valid=is.valid})
|
|
end
|
|
gen_island_pos(name)
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("islandgen",{
|
|
description = S"Bulk island generation",
|
|
privs = {interact = true, server = true},
|
|
func = function(name,param)
|
|
gen_island_pos(name,tonumber(param) or 1)
|
|
end
|
|
})
|
|
|
|
minetest.register_on_joinplayer(function(ref)
|
|
local name = ref:get_player_name()
|
|
if not players[name] and minetest.check_player_privs(ref,{interact=true}) then
|
|
gen_island_pos(name)
|
|
end
|
|
end)
|