local lib = ...
local V = vector.new
local ofs = {
neighbors = {
V( 0, 1, 0);
V( 0, -1, 0);
V( 1, 0, 0);
V(-1, 0, 0);
V( 0, 0, 1);
V( 0, 0, -1);
};
corners = {
V( 1, 0, 1);
V(-1, 0, 1);
V(-1, 0, -1);
V( 1, 0, -1);
};
planecorners = {
V( 1, 0, 1);
V(-1, 0, 1);
V(-1, 0, -1);
V( 1, 0, -1);
V( 1, 1, 0);
V(-1, 1, 0);
V(-1, -1, 0);
V( 1, -1, 0);
};
cubecorners = {
V( 1, 1, 1);
V(-1, 1, 1);
V(-1, -1, 1);
V(-1, -1, -1);
V( 1, -1, -1);
V( 1, 1, -1);
V( 1, -1, 1);
V(-1, 1, -1);
};
nextto = {
V( 1, 0, 0);
V(-1, 0, 0);
V( 0, 0, 1);
V( 0, 0, -1);
};
cardinal = {
V( 1, 0, 0);
V(-1, 0, 0);
V( 0, 0, 1);
V( 0, 0, -1);
};
}
ofs.adjoining = lib.tbl.append(lib.tbl.append(
ofs.neighbors,ofs.planecorners),ofs.cubecorners)
local purge_container = function(only, pos,node,meta,user)
local offset = function(pos,range)
local r = function(min,max)
return (math.random() * (max - min)) + min
end
return {
x = pos.x + r(0 - range, range);
y = pos.y;
z = pos.z + r(0 - range, range);
}
end
for name, inv in pairs(meta.inventory) do
if only and not lib.tbl.has(only,name) then goto skip end
for _, item in pairs(inv) do
if not item:is_empty() then
minetest.add_item(offset(pos,0.4), item)
end
end
::skip::end
end;
local force = function(pos,preload_for)
local n = minetest.get_node_or_nil(pos)
if preload_for then lib.node.preload(pos,preload_for) end
if n then return n end
minetest.load_area(pos)
return minetest.get_node(pos)
end;
local amass = function(startpoint,names,directions)
if not directions then directions = ofs.neighbors end
local check = function(n)
return lib.tbl.has(names, n.name, function(check,against)
return lib.item.groupmatch(against,check)
end)-- match found
end
if type(names) == 'function' then check = names end
local nodes, positions, checked = {},{},{}
local checkedp = function(pos)
for _,v in pairs(checked) do
if vector.equals(pos,v) then return true end
end
return false
end
local i,stack = 1,{startpoint} repeat
local pos = stack[i]
local n = force(pos)
if check(n, pos, nodes, positions) then
-- record the find
nodes[pos] = n.name
if positions[n.name]
then positions[n.name][#positions[n.name]+1] = pos
else positions[n.name] = {pos}
end
-- check selected neighbors to see if any need scanning
for _,d in pairs(directions) do
local sum = vector.add(pos, d)
if not checkedp(sum) then
stack[#stack + 1] = sum
checked[#checked+1] = sum
end
end
end
checked[#checked+1] = pos
i = i + 1
until i > #stack
return nodes, positions
end;
local is_air = function(pos)
local n = force(pos)
if n.name == 'air' then return true end
local d = minetest.registered_nodes[n.name]
if not d then return false end
return (d.walkable == false) and (d.drawtype == 'airlike' or d.buildable_to == true)
end;
local is_clear = function(pos)
if not lib.node.is_air(pos) then return false end
local ents = minetest.get_objects_inside_radius(pos,0.5)
if #ents > 0 then return false end
return true
end;
local function boxwarp(nb, mogrifier, par)
if nb == nil then
return
elseif nb.type then
if nb.type == 'fixed' or nb.type == 'leveled' then
for i, b in ipairs(nb.fixed) do
boxwarp(b, mogrifier, nb)
end
elseif nb.type == 'wallmounted' then
boxwarp(nb.wall_top, mogrifier, nb)
boxwarp(nb.wall_bottom, mogrifier, nb)
boxwarp(nb.wall_side, mogrifier, nb)
elseif nb.type == 'connected' then
for _, state in pairs{'connect', 'disconnected'} do
for _, dir in pairs{'top','bottom','front','left','back','right'} do
boxwarp(nb[state ..'_'.. dir], mogrifier, nb)
end
boxwarp(nb.disconnected, mogrifier, nb)
boxwarp(nb.disconnected_sides, mogrifier, nb)
end
elseif nb.type == 'regular' then
nb.type = 'fixed'
nb.fixed = {-.5, -.5, -.5; .5, .5, .5};
boxwarp(nb.fixed, mogrifier, nb);
end
elseif nb[1] then
mogrifier(nb, par)
end
end
local function boxwarped(box, warp) --oof
local c = lib.tbl.deepcopy(box)
boxwarp(c, warp)
return c
end
return {
offsets = ofs;
purge_container = function(...) return purge_container(nil, ...) end;
purge_only = function(lst)
return function(...)
return purge_container(lst, ...)
end
end;
is_air = is_air;
is_clear = is_clear;
insert = function(item, slot, npos, user, inv)
inv = inv or minetest.get_meta(npos):get_inventory()
if inv:room_for_item(slot,item) then
inv:add_item(slot,item)
else repeat
if user then
local ui = user:get_inventory()
if ui:room_for_item('main', item) then
ui:add_item('main', item)
break
end
end
minetest.add_item(npos, item)
until true end
end;
install_bed = function(bed, where, dir)
local bottom = bed .. '_bottom'
local top = bed .. '_top'
local d
if type(dir) == 'number' then
d = dir
dir = minetest.facedir_to_dir(d)
else
d = minetest.dir_to_facedir(dir)
end
if not is_clear(where) and is_clear(where - dir) then return false end
minetest.set_node(where, {name = top, param2 = d})
minetest.set_node(where - dir, {name = bottom, param2 = d})
return true
end;
get_arrival_point = function(pos)
local try = function(p)
local air = lib.node.is_clear
if air(p) then
if air(vector.offset(p,0,1,0)) then return p end
if air(vector.offset(p,0,-1,0)) then return vector.offset(p,0,-1,0) end
end
return false
end
do local t = try(pos) if t then return t end end
for _,o in pairs(ofs.neighbors) do
local p = vector.add(pos, o)
do local t = try(p) if t then return t end end
end
end;
forneighbor = function(pos, n, fn)
for _,p in pairs(n) do
local sum = vector.add(pos, p)
local n = minetest.get_node(sum)
if n.name == 'ignore' then
minetest.load_area(sum)
n = minetest.get_node(sum)
end
if fn(sum, n) == false then break end
end
end;
amass = amass;
force = force;
blockpos = function(pos)
return {
x = math.floor(pos.x / 16);
y = math.floor(pos.y / 16);
z = math.floor(pos.z / 16);
}
end;
preload = function(pos, user)
minetest.load_area(pos)
user:send_mapblock(lib.node.blockpos(pos))
end;
discharger = function(pos)
local below = force(vector.subtract(pos,{x=0,y=1,z=0}))
if below.name == 'hopper:hopper'
or below.name == 'hopper:hopper_side' then
local hopper = minetest.get_meta(below):get_inventory()
return function(i)
if hopper:room_for_item('main',i) then
return hopper:add_item('main',i), true
end
return i, false
end
else
return function(i) return i, false end
end
end;
autopreserve = function(id, tbl)
tbl.drop = tbl.drop or {
max_items = 1;
items = {
{ items = {id} };
};
}
local next_apn = tbl.after_place_node
tbl.after_place_node = function(...) local pos, who, stack = ...
minetest.get_meta(pos):from_table(stack:get_meta():to_table())
if next_apn then return next_apn(...) end
end
local next_pm = tbl.preserve_metadata
tbl.preserve_metadata = function(...) local pos, node, meta, drops = ...
drops[1]:get_meta():from_table({fields = meta})
if next_pm then return next_pm(...) end
end
return tbl
end;
reg_autopreserve = function(id, tbl)
minetest.register_node(id, lib.node.autopreserve(id, tbl))
end;
boxwarp = boxwarp;
boxwarped = boxwarped;
}