6ee59aea0e
I also removed the infotext, which seemed probably unnecessary and which would hinder the hiding.
422 lines
11 KiB
Lua
422 lines
11 KiB
Lua
local function get_node_force(pos)
|
|
local node = minetest.get_node_or_nil(pos)
|
|
if node then return node end
|
|
-- Try to load the block:
|
|
local vm = minetest.get_voxel_manip()
|
|
vm:read_from_map(pos, pos)
|
|
node = minetest.get_node_or_nil(pos)
|
|
assert(node)
|
|
return node
|
|
end
|
|
|
|
local container_name_prefix = "area_containers:container_"
|
|
|
|
local exit_offset = vector.new(0, 2, 1)
|
|
local digiline_offset = vector.new(3, 0, 3)
|
|
|
|
local port_labels = {
|
|
px = "+X", nx = "-X",
|
|
pz = "+Z", nz = "-Z",
|
|
py = "+Y", ny = "-Y",
|
|
}
|
|
local port_offsets = {
|
|
px = vector.new(0, 2, 4), nx = vector.new(0, 2, 6),
|
|
pz = vector.new(0, 2, 8), nz = vector.new(0, 2, 10),
|
|
py = vector.new(0, 2, 12), ny = vector.new(0, 2, 14),
|
|
}
|
|
local port_dirs = {
|
|
px = vector.new(1, 0, 0), nx = vector.new(-1, 0, 0),
|
|
pz = vector.new(0, 0, 1), nz = vector.new(0, 0, -1),
|
|
py = vector.new(0, 1, 0), ny = vector.new(0, -1, 0),
|
|
}
|
|
local port_ids_horiz = {"px", "nx", "pz", "nz"}
|
|
|
|
local port_name_prefix = "area_containers:port_"
|
|
|
|
local function get_port_id_from_name(node_name)
|
|
return string.sub(node_name,
|
|
#port_name_prefix + 1, #port_name_prefix + 2)
|
|
end
|
|
|
|
local inside_offsets = { exit_offset, digiline_offset }
|
|
for _, offset in pairs(port_offsets) do
|
|
inside_offsets[#inside_offsets + 1] = offset
|
|
end
|
|
|
|
local function set_up_exit(param1, param2, inside_pos)
|
|
local pos = vector.add(inside_pos, exit_offset)
|
|
minetest.set_node(pos, {
|
|
name = "area_containers:exit",
|
|
param1 = param1, param2 = param2,
|
|
})
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("infotext", "Exit")
|
|
end
|
|
|
|
local function set_up_digiline(param1, param2, inside_pos)
|
|
local pos = vector.add(inside_pos, digiline_offset)
|
|
minetest.set_node(pos, {
|
|
name = "area_containers:digiline",
|
|
param1 = param1, param2 = param2,
|
|
})
|
|
end
|
|
|
|
local function set_up_ports(param1, param2, inside_pos)
|
|
for id, offset in pairs(port_offsets) do
|
|
local pos = vector.add(inside_pos, offset)
|
|
minetest.set_node(pos, {
|
|
name = port_name_prefix .. id .. "_off",
|
|
param1 = param1, param2 = param2,
|
|
})
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("infotext", port_labels[id])
|
|
end
|
|
end
|
|
|
|
local function construct_inside(container_pos, param1, param2)
|
|
local inside_pos = area_containers.get_related_inside(param1, param2)
|
|
local min_pos = inside_pos
|
|
local max_pos = vector.add(min_pos, 15)
|
|
|
|
local vm = minetest.get_voxel_manip()
|
|
local min_edge, max_edge = vm:read_from_map(min_pos, max_pos)
|
|
local area = VoxelArea:new{
|
|
MinEdge = min_edge,
|
|
MaxEdge = max_edge,
|
|
}
|
|
|
|
local data = vm:get_data()
|
|
local c_air = minetest.get_content_id("air")
|
|
local c_wall = minetest.get_content_id("area_containers:wall")
|
|
for z = min_pos.z, max_pos.z do
|
|
for y = min_pos.y, max_pos.y do
|
|
for x = min_pos.x, max_pos.x do
|
|
local content_id = c_air
|
|
if x == min_pos.x or x == max_pos.x or
|
|
y == min_pos.y or y == max_pos.y or
|
|
z == min_pos.z or z == max_pos.z then
|
|
content_id = c_wall
|
|
end
|
|
data[area:index(x, y, z)] = content_id
|
|
end
|
|
end
|
|
end
|
|
vm:set_data(data)
|
|
vm:write_to_map(true)
|
|
|
|
area_containers.set_related_container(param1, param2, container_pos)
|
|
set_up_exit(param1, param2, inside_pos)
|
|
set_up_digiline(param1, param2, inside_pos)
|
|
set_up_ports(param1, param2, inside_pos)
|
|
end
|
|
|
|
area_containers.container = {}
|
|
|
|
-- There are 16 combinations of the horizontal sides, each with its own name:
|
|
area_containers.all_container_states = {}
|
|
local all_container_variants = {
|
|
"off", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
|
|
"1000", "1001", "1010", "1011", "1100", "1101", "1110", "on",
|
|
}
|
|
for i, variant in ipairs(all_container_variants) do
|
|
area_containers.all_container_states[i] =
|
|
container_name_prefix .. variant
|
|
end
|
|
|
|
function area_containers.container.on_construct(pos)
|
|
local node = get_node_force(pos)
|
|
local param1 = node.param1
|
|
local param2 = node.param2
|
|
if not area_containers.params_are_null(param1, param2) then
|
|
-- The node probably moved.
|
|
if area_containers.reclaim_relation(param1, param2) then
|
|
area_containers.set_related_container(param1, param2,
|
|
pos)
|
|
return
|
|
end
|
|
end
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("infotext", "Area container")
|
|
param1, param2 = area_containers.alloc_relation()
|
|
if param1 then
|
|
construct_inside(pos, param1, param2)
|
|
minetest.swap_node(pos, {
|
|
name = node.name,
|
|
param1 = param1, param2 = param2,
|
|
})
|
|
end
|
|
end
|
|
|
|
function area_containers.container.on_destruct(pos)
|
|
-- Only free properly allocated containers:
|
|
local node = get_node_force(pos)
|
|
if not area_containers.params_are_null(node.param1, node.param2) then
|
|
area_containers.free_relation(node.param1, node.param2)
|
|
end
|
|
end
|
|
|
|
function area_containers.container.on_rightclick(pos, node, clicker)
|
|
if clicker and minetest.is_player(clicker) then
|
|
local inside_pos = area_containers.get_related_inside(
|
|
node.param1, node.param2)
|
|
local self_pos = area_containers.get_related_container(
|
|
node.param1, node.param2)
|
|
-- Make sure the player will be able to get back:
|
|
if self_pos and vector.equals(pos, self_pos) then
|
|
local dest = vector.add(inside_pos, 1)
|
|
clicker:set_pos(dest)
|
|
end
|
|
end
|
|
end
|
|
|
|
function area_containers.container_is_empty(pos, node)
|
|
node = node or get_node_force(pos)
|
|
local name_prefix = string.sub(node.name, 1, #container_name_prefix)
|
|
if name_prefix ~= container_name_prefix then return true end
|
|
local inside_pos = area_containers.get_related_inside(
|
|
node.param1, node.param2)
|
|
-- These represent the area of the inner chamber (inclusive):
|
|
local min_pos = vector.add(inside_pos, 1)
|
|
local max_pos = vector.add(inside_pos, 14)
|
|
|
|
-- Detect nodes left inside.
|
|
local vm = minetest.get_voxel_manip()
|
|
local min_edge, max_edge = vm:read_from_map(min_pos, max_pos)
|
|
local area = VoxelArea:new{
|
|
MinEdge = min_edge,
|
|
MaxEdge = max_edge,
|
|
}
|
|
local data = vm:get_data()
|
|
local c_air = minetest.get_content_id("air")
|
|
for z = min_pos.z, max_pos.z do
|
|
for y = min_pos.y, max_pos.y do
|
|
for x = min_pos.x, max_pos.x do
|
|
if data[area:index(x, y, z)] ~= c_air then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Detect players inside.
|
|
-- (Detecting all objects would probably cause problems.)
|
|
local objects_inside = minetest.get_objects_in_area(
|
|
vector.subtract(min_pos, 1), vector.add(max_pos, 1))
|
|
for _, object in ipairs(objects_inside) do
|
|
if minetest.is_player(object) then return false end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function area_containers.container.can_dig(pos)
|
|
return area_containers.container_is_empty(pos)
|
|
end
|
|
|
|
function area_containers.container.on_blast()
|
|
-- The simplest way to preserve the inside is just to do nothing.
|
|
end
|
|
|
|
area_containers.container.digiline = {
|
|
effector = {},
|
|
receptor = {},
|
|
}
|
|
|
|
function area_containers.container.digiline.effector.action(pos, node,
|
|
channel, msg)
|
|
local inside_pos = area_containers.get_related_inside(
|
|
node.param1, node.param2)
|
|
local digiline_pos = vector.add(inside_pos, digiline_offset)
|
|
digiline:receptor_send(digiline_pos, digiline.rules.default,
|
|
channel, msg)
|
|
end
|
|
|
|
area_containers.container.groups = {
|
|
tubedevice = 1,
|
|
tubedevice_receiver = 1,
|
|
}
|
|
|
|
area_containers.container.tube = {
|
|
connect_sides = {
|
|
left = 1, right = 1,
|
|
back = 1, front = 1,
|
|
bottom = 1, top = 1,
|
|
},
|
|
}
|
|
|
|
function area_containers.container.tube.can_insert()
|
|
return true
|
|
end
|
|
|
|
function area_containers.container.tube.insert_object(pos, node, stack, dir,
|
|
owner)
|
|
local inside_pos = area_containers.get_related_inside(
|
|
node.param1, node.param2)
|
|
local port_id = "nx"
|
|
if dir.x < 0 then
|
|
port_id = "px"
|
|
elseif dir.z > 0 then
|
|
port_id = "nz"
|
|
elseif dir.z < 0 then
|
|
port_id = "pz"
|
|
elseif dir.y > 0 then
|
|
port_id = "ny"
|
|
elseif dir.y < 0 then
|
|
port_id = "py"
|
|
end
|
|
local port_pos = vector.add(inside_pos, port_offsets[port_id])
|
|
local out_speed = math.max(vector.length(dir), 0.1)
|
|
local out_vel = vector.new(out_speed, 0, 0)
|
|
pipeworks.tube_inject_item(port_pos, port_pos, out_vel, stack, owner)
|
|
return ItemStack() -- All inserted.
|
|
end
|
|
|
|
area_containers.container.mesecons = {conductor = {
|
|
states = area_containers.all_container_states,
|
|
}}
|
|
|
|
function area_containers.container.mesecons.conductor.rules(node)
|
|
local rules = {
|
|
{
|
|
{x = 1, y = 1, z = 0},
|
|
{x = 1, y = 0, z = 0},
|
|
{x = 1, y = -1, z = 0},
|
|
},
|
|
{
|
|
{x = -1, y = 1, z = 0},
|
|
{x = -1, y = 0, z = 0},
|
|
{x = -1, y = -1, z = 0},
|
|
},
|
|
{
|
|
{x = 0, y = 1, z = 1},
|
|
{x = 0, y = 0, z = 1},
|
|
{x = 0, y = -1, z = 1},
|
|
},
|
|
{
|
|
{x = 0, y = 1, z = -1},
|
|
{x = 0, y = 0, z = -1},
|
|
{x = 0, y = -1, z = -1},
|
|
},
|
|
}
|
|
local self_pos = area_containers.get_related_container(
|
|
node.param1, node.param2)
|
|
if self_pos then
|
|
local inside_pos = area_containers.get_related_inside(
|
|
node.param1, node.param2)
|
|
for i, id in ipairs(port_ids_horiz) do
|
|
local port_pos =
|
|
vector.add(inside_pos, port_offsets[id])
|
|
local offset = vector.subtract(port_pos, self_pos)
|
|
local face_rules = rules[i]
|
|
face_rules[#face_rules + 1] = offset
|
|
end
|
|
end
|
|
return rules
|
|
end
|
|
|
|
area_containers.exit = {}
|
|
|
|
function area_containers.exit.on_rightclick(pos, node, clicker)
|
|
if clicker and minetest.is_player(clicker) then
|
|
local container_pos = area_containers.get_related_container(
|
|
node.param1, node.param2)
|
|
if container_pos then
|
|
local dest = vector.offset(container_pos, 0, 1, 0)
|
|
clicker:set_pos(dest)
|
|
end
|
|
end
|
|
end
|
|
|
|
area_containers.digiline = {
|
|
digiline = {
|
|
effector = {},
|
|
receptor = {},
|
|
}
|
|
}
|
|
|
|
function area_containers.digiline.digiline.effector.action(pos, node,
|
|
channel, msg)
|
|
local container_pos =
|
|
area_containers.get_related_container(node.param1, node.param2)
|
|
if not container_pos then return end
|
|
digiline:receptor_send(container_pos, digiline.rules.default,
|
|
channel, msg)
|
|
end
|
|
|
|
area_containers.port = {
|
|
groups = {
|
|
tubedevice = 1,
|
|
tubedevice_receiver = 1,
|
|
},
|
|
tube = {
|
|
connect_sides = {
|
|
left = 1, right = 1,
|
|
back = 1, front = 1,
|
|
bottom = 1, top = 1,
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
function area_containers.port.tube.can_insert(pos, node)
|
|
return area_containers.get_related_container(node.param1, node.param2)
|
|
~= nil
|
|
end
|
|
|
|
function area_containers.port.tube.insert_object(pos, node, stack, dir, owner)
|
|
local container_pos = area_containers.get_related_container(
|
|
node.param1, node.param2)
|
|
if not container_pos then return stack end
|
|
local id = get_port_id_from_name(node.name)
|
|
local out_dir = port_dirs[id]
|
|
local out_speed = math.max(vector.length(dir), 0.1)
|
|
local out_vel = vector.multiply(out_dir, out_speed)
|
|
pipeworks.tube_inject_item(container_pos, container_pos, out_vel, stack,
|
|
owner)
|
|
return ItemStack() -- All inserted.
|
|
end
|
|
|
|
local function get_port_rules(node)
|
|
local rules = {
|
|
{x = 1, y = -1, z = 0},
|
|
{x = 1, y = 0, z = 0},
|
|
{x = 1, y = 1, z = 0},
|
|
}
|
|
local container_pos = area_containers.get_related_container(
|
|
node.param1, node.param2)
|
|
if container_pos then
|
|
local id = get_port_id_from_name(node.name)
|
|
local inside_pos = area_containers.get_related_inside(
|
|
node.param1, node.param2)
|
|
local self_pos = vector.add(inside_pos, port_offsets[id])
|
|
local container_offset =
|
|
vector.subtract(container_pos, self_pos)
|
|
rules[#rules + 1] = container_offset
|
|
end
|
|
return rules
|
|
end
|
|
|
|
-- The vertical faces don't get mesecons since it wasn't working with them.
|
|
area_containers.all_port_variants = {
|
|
py_off = {},
|
|
ny_off = {},
|
|
}
|
|
for _, id in ipairs(port_ids_horiz) do
|
|
local on_state = id .. "_on"
|
|
local off_state = id .. "_off"
|
|
area_containers.all_port_variants[on_state] = {
|
|
mesecons = {conductor = {
|
|
state = "on",
|
|
offstate = port_name_prefix .. off_state,
|
|
rules = get_port_rules,
|
|
}},
|
|
}
|
|
area_containers.all_port_variants[off_state] = {
|
|
mesecons = {conductor = {
|
|
state = "off",
|
|
onstate = port_name_prefix .. on_state,
|
|
rules = get_port_rules,
|
|
}},
|
|
}
|
|
end
|