1114 lines
28 KiB
Lua
1114 lines
28 KiB
Lua
boxes = {}
|
|
|
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
|
|
|
-- Box allocation
|
|
dofile(modpath .. "/valloc.lua")
|
|
|
|
-- Handling the data that encodes boxes
|
|
dofile(modpath .. "/data.lua")
|
|
|
|
local function bytes_to_string(bytes)
|
|
local s = {}
|
|
for i = 1, #bytes do
|
|
s[i] = string.char(bytes[i])
|
|
end
|
|
return table.concat(s)
|
|
end
|
|
|
|
-- Set the region from minp to maxp to air
|
|
function boxes.cleanup(minp, maxp)
|
|
-- Clear any leftover entities
|
|
local center = vector.divide(vector.add(minp, maxp), 2)
|
|
local radius = vector.length(vector.subtract(vector.add(maxp, 1), minp)) / 2
|
|
for _, obj in ipairs(minetest.get_objects_inside_radius(center, radius)) do
|
|
if not obj:is_player() then
|
|
local pos = obj:get_pos()
|
|
if minp.x - 0.5 <= pos.x and pos.x <= maxp.x + 0.5 and
|
|
minp.y - 0.5 <= pos.y and pos.y <= maxp.y + 0.5 and
|
|
minp.z - 0.5 <= pos.z and pos.z <= maxp.z + 0.5
|
|
then
|
|
obj:remove()
|
|
end
|
|
end
|
|
end
|
|
|
|
local vm = minetest.get_voxel_manip(minp, maxp)
|
|
local emin, emax = vm:get_emerged_area()
|
|
local va = VoxelArea:new{MinEdge=emin,MaxEdge=emax}
|
|
local vmdata = vm:get_data()
|
|
local param2 = vm:get_param2_data()
|
|
local cid = minetest.get_content_id("air")
|
|
|
|
for z = minp.z, maxp.z do
|
|
for y = minp.y, maxp.y do
|
|
local index = va:index(minp.x, y, z)
|
|
for x = minp.x, maxp.x do
|
|
vmdata[index] = cid
|
|
param2[index] = 0
|
|
index = index + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
vm:set_data(vmdata)
|
|
vm:set_param2_data(param2)
|
|
vm:update_liquids()
|
|
vm:write_to_map()
|
|
vm:update_map()
|
|
end
|
|
|
|
function boxes.import_box(id, box_data)
|
|
local data_index = 1
|
|
local function read_u8()
|
|
local c = string.byte(box_data, data_index)
|
|
data_index = data_index + 1
|
|
return c
|
|
end
|
|
local function read_u16()
|
|
local a = read_u8()
|
|
local b = read_u8()
|
|
return 256 * a + b
|
|
end
|
|
local function read_pos()
|
|
local x = read_u16()
|
|
local y = read_u16()
|
|
local z = read_u16()
|
|
return {x = x, y = y, z = z}
|
|
end
|
|
local version = read_u8()
|
|
assert (version == 1)
|
|
local type = read_u8()
|
|
local size = read_pos()
|
|
local entry = read_pos()
|
|
local exit = read_pos()
|
|
db.box_set_data(id, string.sub(box_data, data_index))
|
|
db.box_set_meta(id, {
|
|
type = type,
|
|
meta = {
|
|
size = size,
|
|
entry = entry,
|
|
exit = exit,
|
|
}
|
|
})
|
|
end
|
|
|
|
function boxes.export_box(id)
|
|
local data_index = 1
|
|
local data = {}
|
|
local function write_u8(x)
|
|
data[data_index] = x
|
|
data_index = data_index + 1
|
|
end
|
|
local function write_u16(x)
|
|
write_u8(math.floor(x / 256))
|
|
write_u8(x % 256)
|
|
end
|
|
local function write_pos(p)
|
|
write_u16(p.x)
|
|
write_u16(p.y)
|
|
write_u16(p.z)
|
|
end
|
|
local meta = db.box_get_meta(id)
|
|
write_u8(1) -- version
|
|
write_u8(meta.type)
|
|
write_pos(meta.meta.size)
|
|
write_pos(meta.meta.entry)
|
|
write_pos(meta.meta.exit)
|
|
return bytes_to_string(data) .. db.box_get_data(id)
|
|
end
|
|
|
|
function vector.min(a, b)
|
|
return {
|
|
x = math.min(a.x, b.x),
|
|
y = math.min(a.y, b.y),
|
|
z = math.min(a.z, b.z),
|
|
}
|
|
end
|
|
|
|
function vector.max(a, b)
|
|
return {
|
|
x = math.max(a.x, b.x),
|
|
y = math.max(a.y, b.y),
|
|
z = math.max(a.z, b.z),
|
|
}
|
|
end
|
|
|
|
local players_in_boxes = {}
|
|
|
|
local function read_file(filename)
|
|
local file = io.open(filename, "r")
|
|
if file ~= nil then
|
|
local file_content = file:read("*all")
|
|
io.close(file)
|
|
return file_content
|
|
end
|
|
return ""
|
|
end
|
|
|
|
local function write_file(filename, data)
|
|
local file, err = io.open(filename, "w")
|
|
if file then
|
|
file:write(data)
|
|
io.close(file)
|
|
else
|
|
error(err)
|
|
end
|
|
end
|
|
|
|
if db.box_get_meta(0) == nil then
|
|
local box_data = read_file(modpath .. "/entry.box")
|
|
boxes.import_box(0, box_data)
|
|
end
|
|
|
|
if db.box_get_meta(1) == nil then
|
|
local box_data = read_file(modpath .. "/exit.box")
|
|
boxes.import_box(1, box_data)
|
|
end
|
|
|
|
if db.box_get_meta(2) == nil then
|
|
local box_data = read_file(modpath .. "/box.box")
|
|
boxes.import_box(2, box_data)
|
|
end
|
|
|
|
-- local worldpath = minetest.get_worldpath()
|
|
-- write_file(worldpath .. "/entry.box", boxes.export_box(0))
|
|
-- write_file(worldpath .. "/exit.box", boxes.export_box(1))
|
|
-- write_file(worldpath .. "/box.box", boxes.export_box(2))
|
|
|
|
local function pop_node(pos)
|
|
local node = minetest.get_node(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local r = {pos = pos, node = node, meta = meta:to_table()}
|
|
minetest.remove_node(pos)
|
|
return r
|
|
end
|
|
|
|
local function unpop_node(data)
|
|
minetest.set_node(data.pos, data.node)
|
|
local meta = minetest.get_meta(data.pos)
|
|
meta:from_table(data.meta)
|
|
end
|
|
|
|
local function open_door(player, pos1, pos2, bminp, bmaxp)
|
|
local minp = vector.min(pos1, pos2)
|
|
local maxp = vector.max(pos1, pos2)
|
|
local nds = {}
|
|
local i = 1
|
|
for x = minp.x, maxp.x do
|
|
for y = minp.y, maxp.y do
|
|
for z = minp.z, maxp.z do
|
|
nds[i] = pop_node({x = x, y = y, z = z})
|
|
i = i + 1
|
|
end
|
|
end
|
|
end
|
|
local od = players_in_boxes[player:get_player_name()].open_doors
|
|
od[#od + 1] = {minp = bminp, maxp = bmaxp, nodes = nds}
|
|
end
|
|
|
|
function boxes.open_exit(player)
|
|
local name = player:get_player_name()
|
|
local exds = players_in_boxes[name].exit_doors
|
|
local n = #exds
|
|
if n == 0 then return end
|
|
local ex = exds[n]
|
|
exds[n] = nil
|
|
open_door(player, ex.door_pos, vector.add(ex.door_pos, {x = -1, y = 1, z = 0}), ex.box_minp, ex.box_maxp)
|
|
end
|
|
|
|
function boxes.increase_items(player)
|
|
local name = player:get_player_name()
|
|
if not players_in_boxes[name] then
|
|
return
|
|
end
|
|
local exds = players_in_boxes[name].exit_doors
|
|
local n = #exds
|
|
if n == 0 then return end
|
|
local ex = exds[n]
|
|
ex.door_items = ex.door_items - 1
|
|
if ex.door_items == 0 then
|
|
boxes.open_exit(player)
|
|
end
|
|
end
|
|
|
|
-- Close doors behind players
|
|
minetest.register_globalstep(function(dtime)
|
|
for playername, info in pairs(players_in_boxes) do
|
|
local i = 1
|
|
local player = minetest.get_player_by_name(playername)
|
|
local pos = player:get_pos()
|
|
local doors = info.open_doors
|
|
local n = #doors
|
|
while doors[i] do
|
|
if doors[i].minp.x + 0.5 <= pos.x and pos.x <= doors[i].maxp.x - 0.5 and
|
|
doors[i].minp.y + 0.5 <= pos.y and pos.y <= doors[i].maxp.y - 0.5 and
|
|
doors[i].minp.z + 0.5 <= pos.z and pos.z <= doors[i].maxp.z - 0.5
|
|
then
|
|
for _, data in ipairs(doors[i].nodes) do
|
|
unpop_node(data)
|
|
end
|
|
doors[i] = doors[n]
|
|
doors[n] = nil
|
|
n = n - 1
|
|
else
|
|
i = i + 1
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
function boxes.open_box(player, box_id_list)
|
|
local name = player:get_player_name()
|
|
if players_in_boxes[name] ~= nil then
|
|
return
|
|
end
|
|
|
|
local offset = {x = 0, y = 0, z = 0}
|
|
local pmin = {x = 0, y = 0, z = 0}
|
|
local pmax = {x = 0, y = 0, z = 0}
|
|
local metas = {}
|
|
local offsets = {}
|
|
for i, box in ipairs(box_id_list) do
|
|
local meta = db.box_get_meta(box).meta
|
|
metas[i] = meta
|
|
offset = vector.subtract(offset, meta.entry)
|
|
pmin = vector.min(pmin, offset)
|
|
pmax = vector.max(pmax, vector.add(offset, meta.size))
|
|
offsets[i] = offset
|
|
offset = vector.add(offset, meta.exit)
|
|
end
|
|
for i, off in ipairs(offsets) do
|
|
offsets[i] = vector.subtract(off, pmin)
|
|
end
|
|
pmax = vector.subtract(pmax, pmin)
|
|
|
|
local size = math.max(pmax.x, pmax.z)
|
|
local minp = boxes.valloc(size)
|
|
local maxp = vector.add(minp, vector.subtract(pmax, 1))
|
|
|
|
local exit_doors = {}
|
|
local n = #box_id_list
|
|
for i = 1, n - 1 do
|
|
local exd = vector.add(minp, vector.add(metas[n - i].exit, offsets[n - i]))
|
|
exit_doors[i] = {
|
|
door_items = metas[n - i].num_items,
|
|
door_pos = exd,
|
|
box_minp = vector.add(minp, offsets[n - i + 1]),
|
|
box_maxp = vector.add(minp, vector.add(offsets[n - i + 1], vector.subtract(metas[n - i + 1].size, 1))),
|
|
}
|
|
end
|
|
players_in_boxes[name] = {
|
|
minp = minp,
|
|
maxp = maxp,
|
|
exit_doors = exit_doors,
|
|
exit = vector.add(minp, vector.add(metas[n].exit, offsets[n])),
|
|
open_doors = {},
|
|
}
|
|
|
|
for i, box in ipairs(box_id_list) do
|
|
boxes.load(vector.add(minp, offsets[i]), box, player)
|
|
end
|
|
|
|
local spawn_pos = vector.add(minp, vector.add(metas[1].entry, offsets[1]))
|
|
player:set_pos(spawn_pos)
|
|
|
|
boxes.open_exit(player)
|
|
end
|
|
|
|
function boxes.close_box(player)
|
|
local name = player:get_player_name()
|
|
if players_in_boxes[name] == nil then
|
|
return
|
|
end
|
|
|
|
local bx = players_in_boxes[name]
|
|
boxes.cleanup(bx.minp, bx.maxp)
|
|
boxes.vfree(bx.minp)
|
|
players_in_boxes[name] = nil
|
|
end
|
|
|
|
function boxes.next_series(player, sid)
|
|
local name = player:get_player_name()
|
|
local pmeta = db.player_get_meta(name)
|
|
local index = pmeta.series_progress[sid] or 1
|
|
local bxs = db.series_get_boxes(sid)
|
|
if not bxs[index] then
|
|
players.return_to_lobby(player)
|
|
else
|
|
boxes.open_box(player, {0, bxs[index], 1})
|
|
players_in_boxes[name].in_series = sid
|
|
end
|
|
end
|
|
|
|
local players_editing_boxes = {}
|
|
-- FIXME: get that from db each time, but how?
|
|
local min_free_id = 0
|
|
while db.box_get_meta(min_free_id) ~= nil do
|
|
min_free_id = min_free_id + 1
|
|
end
|
|
|
|
minetest.register_chatcommand("enter", {
|
|
params = "<boxid>",
|
|
description = "Enter box with this id",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
local player = minetest.get_player_by_name(name)
|
|
if players_in_boxes[name] then
|
|
minetest.chat_send_player(name, "You are already in a box!")
|
|
return
|
|
end
|
|
local id = tonumber(param)
|
|
if not id or id ~= math.floor(id) or id < 0 then
|
|
minetest.chat_send_player(name, "The id you supplied is not a nonnegative interger.")
|
|
return
|
|
end
|
|
local meta = db.box_get_meta(id)
|
|
if not meta or meta.type ~= db.BOX_TYPE then
|
|
minetest.chat_send_player(name, "The id you supplied does not correspond to any box.")
|
|
return
|
|
end
|
|
boxes.open_box(player, {0, id, 1})
|
|
end,
|
|
})
|
|
|
|
local function teleport_update_particles(pos, name)
|
|
local tm = math.random(10, 50) / 10
|
|
minetest.add_particlespawner({
|
|
amount = tm * 3,
|
|
time = tm,
|
|
minpos = {x = pos.x - 7/16, y = pos.y - 5/16, z = pos.z - 7/16},
|
|
maxpos = {x = pos.x + 7/16, y = pos.y - 5/16, z = pos.z + 7/16},
|
|
minvel = vector.new(-1, 2, -1),
|
|
maxvel = vector.new(1, 5, 1),
|
|
minacc = vector.new(0, -9.81, 0),
|
|
maxacc = vector.new(0, -9.81, 0),
|
|
--collisiondetection = true,
|
|
--collision_removal = true,
|
|
texture = "diamond.png",
|
|
playername = name,
|
|
})
|
|
minetest.get_node_timer(pos):start(tm)
|
|
end
|
|
|
|
minetest.register_node("boxes:enter_teleport", {
|
|
description = "Enter teleport",
|
|
tiles = {"diamond.png"},
|
|
drawtype = "nodebox",
|
|
node_box = {
|
|
type = "fixed",
|
|
fixed = {{-7/16, -1/2, -7/16, 7/16, -7/16, 7/16}},
|
|
},
|
|
paramtype = "light",
|
|
groups = {},
|
|
on_walk_over = function(pos, node, player)
|
|
local meta = minetest.get_meta(pos)
|
|
local id = meta:get_int("box")
|
|
if meta:get_int("is_series") == 1 then
|
|
local smeta = db.series_get_meta(id)
|
|
if not smeta then
|
|
return
|
|
end
|
|
boxes.next_series(player, id)
|
|
return
|
|
end
|
|
local bmeta = db.box_get_meta(id)
|
|
if bmeta.type ~= db.BOX_TYPE then
|
|
return
|
|
end
|
|
boxes.open_box(player, {0, id, 1})
|
|
end,
|
|
on_punch = function(pos, node, puncher, pointed_thing)
|
|
if not puncher then return end
|
|
local meta = minetest.get_meta(pos)
|
|
if puncher:get_player_control().sneak then
|
|
meta:set_int("is_series", 1 - meta:get_int("is_series"))
|
|
return
|
|
end
|
|
local id = meta:get_int("box")
|
|
local newid = id + 1
|
|
if db.box_get_meta(newid) == nil then
|
|
newid = 0
|
|
end
|
|
meta:set_int("box", newid)
|
|
minetest.chat_send_player(puncher:get_player_name(), "Destination set to " .. newid)
|
|
end,
|
|
on_rightclick = function(pos, node, puncher, pointed_thing)
|
|
if not puncher then return end
|
|
local meta = minetest.get_meta(pos)
|
|
local id = meta:get_int("box")
|
|
local newid = id - 1
|
|
if db.box_get_meta(newid) == nil then
|
|
newid = min_free_id - 1
|
|
end
|
|
meta:set_int("box", newid)
|
|
minetest.chat_send_player(puncher:get_player_name(), "Destination set to " .. newid)
|
|
end,
|
|
on_construct = function(pos)
|
|
teleport_update_particles(pos, nil)
|
|
end,
|
|
on_timer = function(pos)
|
|
teleport_update_particles(pos, nil)
|
|
end,
|
|
})
|
|
|
|
minetest.register_chatcommand("new_series", {
|
|
params = "<name>",
|
|
description = "Create a new series",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
local id = 1
|
|
while db.series_get_meta(id) do
|
|
id = id + 1
|
|
end
|
|
db.series_set_meta(id, {name = param, meta = {}})
|
|
minetest.chat_send_player(name, "Series successfully created with id " .. id)
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("series_add", {
|
|
params = "<series_id> <box_id>",
|
|
description = "Add a box a series",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
local sid, bid = string.match(param, "^([^ ]+) +(.+)$")
|
|
sid = tonumber(sid)
|
|
bid = tonumber(bid)
|
|
if not sid or not bid or not db.series_get_meta(sid) or not db.box_get_meta(bid) then
|
|
minetest.chat_send_player(name, "Box or series doesn't exist.")
|
|
return
|
|
end
|
|
db.series_insert_box(sid, bid, #db.series_get_boxes(sid))
|
|
minetest.chat_send_player(name, "Done")
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("leave", {
|
|
params = "",
|
|
description = "Leave the current box",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if not players_in_boxes[name] then
|
|
minetest.chat_send_player(name, "You are not in a box!")
|
|
return
|
|
end
|
|
local player = minetest.get_player_by_name(name)
|
|
boxes.close_box(player)
|
|
players.return_to_lobby(player)
|
|
end,
|
|
})
|
|
|
|
minetest.register_node("boxes:exit_teleport", {
|
|
description = "Exit teleport",
|
|
tiles = {"diamond.png"},
|
|
drawtype = "nodebox",
|
|
node_box = {
|
|
type = "fixed",
|
|
fixed = {{-7/16, -1/2, -7/16, 7/16, -7/16, 7/16}},
|
|
},
|
|
paramtype = "light",
|
|
groups = {},
|
|
on_walk_over = function(pos, node, player)
|
|
local name = player:get_player_name()
|
|
if not players_in_boxes[name] then
|
|
return
|
|
end
|
|
local sid = players_in_boxes[name].in_series
|
|
boxes.close_box(player)
|
|
if sid then
|
|
local pmeta = db.player_get_meta(name)
|
|
local index = pmeta.series_progress[sid] or 1
|
|
pmeta.series_progress[sid] = index + 1
|
|
db.player_set_meta(name, pmeta)
|
|
boxes.next_series(player, sid)
|
|
return
|
|
else
|
|
players.return_to_lobby(player)
|
|
end
|
|
end,
|
|
on_construct = function(pos)
|
|
teleport_update_particles(pos, nil)
|
|
end,
|
|
on_timer = function(pos)
|
|
teleport_update_particles(pos, nil)
|
|
end,
|
|
after_box_construct = function(pos)
|
|
teleport_update_particles(pos, nil)
|
|
end,
|
|
})
|
|
|
|
minetest.register_chatcommand("open", {
|
|
params = "",
|
|
description = "Open the current exit.",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if not players_in_boxes[name] then
|
|
minetest.chat_send_player(name, "You are not in a box!")
|
|
return
|
|
end
|
|
if players_in_boxes[name].exit_doors[1] == nil then
|
|
minetest.chat_send_player(name, "There are no more exits to open!")
|
|
return
|
|
end
|
|
local player = minetest.get_player_by_name(name)
|
|
boxes.open_exit(player)
|
|
end,
|
|
})
|
|
|
|
minetest.register_node("boxes:nexus", {
|
|
description = "Nexus",
|
|
drawtype = "mesh",
|
|
mesh = "nexus.obj",
|
|
paramtype = "light",
|
|
paramtype2 = "facedir",
|
|
tiles = {
|
|
{
|
|
name = "nexus.png",
|
|
animation = {
|
|
type = "sheet_2d",
|
|
frames_w = 5,
|
|
frames_h = 10,
|
|
frame_length = 0.125,
|
|
}
|
|
},
|
|
},
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-3/8, -3/8, -3/8, 3/8, 3/8, 3/8},
|
|
},
|
|
collision_box = {
|
|
type = "fixed",
|
|
fixed = {-3/8, -3/8, -3/8, 3/8, 3/8, 3/8},
|
|
},
|
|
group = {node = 0, nexus = 1},
|
|
})
|
|
|
|
minetest.register_node("boxes:nexus_large", {
|
|
description = "Nexus",
|
|
drawtype = "mesh",
|
|
mesh = "nexus.obj",
|
|
paramtype = "light",
|
|
paramtype2 = "facedir",
|
|
visual_scale = 4.0,
|
|
tiles = {
|
|
{
|
|
name = "nexus.png",
|
|
animation = {
|
|
type = "sheet_2d",
|
|
frames_w = 5,
|
|
frames_h = 10,
|
|
frame_length = 0.125,
|
|
}
|
|
},
|
|
},
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-3/8, -3/8, -3/8, 3/8, 3/8, 3/8},
|
|
},
|
|
collision_box = {
|
|
type = "fixed",
|
|
fixed = {-3/8, -3/8, -3/8, 3/8, 3/8, 3/8},
|
|
},
|
|
group = {node = 0},
|
|
on_construct = function(pos)
|
|
-- flip after 2 cycles
|
|
minetest.get_node_timer(pos):start(12.5)
|
|
end,
|
|
on_timer = function(pos, node)
|
|
-- make it self-rotate randomly
|
|
minetest.set_node(pos, {name = "boxes:nexus_large", param2 = math.random(24) - 1})
|
|
end,
|
|
})
|
|
|
|
minetest.register_node("boxes:pedestal", {
|
|
description = "Nexus Pedestal",
|
|
tiles = {"bricks_marble.png"},
|
|
drawtype = "nodebox",
|
|
node_box = {
|
|
type = "connected",
|
|
fixed = {{-1/2, -1/2, -1/2, 1/2, 1/2, 1/2}},
|
|
connect_top = {
|
|
{-1/2, 1/2, -1/2, -1/4, 3/4, -1/4},
|
|
{1/4, 1/2, -1/2, 1/2, 3/4, -1/4},
|
|
{-1/2, 1/2, 1/4, -1/4, 3/4, 1/2},
|
|
{1/4, 1/2, 1/4, 1/2, 3/4, 1/2},
|
|
},
|
|
},
|
|
connects_to = {"boxes:nexus"},
|
|
groups = {node = 1},
|
|
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
|
if itemstack:get_name() ~= "boxes:nexus" then
|
|
return itemstack
|
|
end
|
|
itemstack:take_item()
|
|
minetest.set_node({x = pos.x, y = pos.y + 1, z = pos.z},
|
|
{name = "boxes:nexus", param2 = math.random(24) - 1})
|
|
boxes.increase_items(clicker)
|
|
return itemstack
|
|
end,
|
|
})
|
|
|
|
-- This is only still needed if we want to change the size, the entry, or the
|
|
-- exit of the lobbies; changing the contents can be done by /edite.
|
|
|
|
local lobby_updates = {}
|
|
minetest.register_chatcommand("update_lobby", {
|
|
params = "entry|exit",
|
|
description = "Set corresponding lobby. Use without parameter to start \
|
|
updating a lobby, then punch both corners of the lobby and the bottom node \
|
|
of the exit. In the case of the entry lobby, stand at its spawnpoint to run \
|
|
the command.",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if param ~= "entry" and param ~= "exit" and param ~= "" then
|
|
return
|
|
end
|
|
|
|
if param == "" then
|
|
lobby_updates[name] = {}
|
|
elseif not lobby_updates[name] then
|
|
minetest.chat_send_player(name, "Not all positions have been set.")
|
|
return
|
|
else
|
|
local pos1 = lobby_updates[name].pos1
|
|
local pos2 = lobby_updates[name].pos2
|
|
local pos3 = lobby_updates[name].pos3
|
|
if not pos1 or not pos2 or not pos3 then
|
|
minetest.chat_send_player(name, "Not all positions have been set.")
|
|
return
|
|
end
|
|
local minp = vector.min(pos1, pos2)
|
|
local maxp = vector.max(pos1, pos2)
|
|
local data = boxes.save(minp, maxp)
|
|
local p3 = vector.subtract(pos3, minp)
|
|
if param == "exit" then
|
|
local player = minetest.get_player_by_name(name)
|
|
local exit = vector.subtract(vector.round(player:get_pos()), minp)
|
|
-- Exit lobby is hardcoded as id 1 for now
|
|
db.box_set_data(1, data)
|
|
db.box_set_meta(1, {
|
|
type = db.EXIT_TYPE,
|
|
meta = {
|
|
size = vector.add(vector.subtract(maxp, minp), 1),
|
|
entry = p3,
|
|
exit = exit,
|
|
}
|
|
})
|
|
else
|
|
local player = minetest.get_player_by_name(name)
|
|
local entry = vector.subtract(vector.round(player:get_pos()), minp)
|
|
p3.x = p3.x + 1
|
|
-- Entry lobby is hardcoded as id 0 for now
|
|
db.box_set_data(0, data)
|
|
db.box_set_meta(0, {
|
|
type = db.ENTRY_TYPE,
|
|
meta = {
|
|
size = vector.add(vector.subtract(maxp, minp), 1),
|
|
entry = entry,
|
|
exit = p3,
|
|
}
|
|
})
|
|
end
|
|
minetest.chat_send_player(name, "Updated.")
|
|
lobby_updates[name] = nil
|
|
end
|
|
end,
|
|
})
|
|
|
|
minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing)
|
|
if not puncher then return end
|
|
local name = puncher:get_player_name()
|
|
if not lobby_updates[name] then return end
|
|
if not lobby_updates[name].pos1 then
|
|
lobby_updates[name].pos1 = pos
|
|
minetest.chat_send_player(name, "Position 1 set to " .. dump(pos) .. ".")
|
|
elseif not lobby_updates[name].pos2 then
|
|
lobby_updates[name].pos2 = pos
|
|
minetest.chat_send_player(name, "Position 2 set to " .. dump(pos) .. ".")
|
|
elseif not lobby_updates[name].pos3 then
|
|
lobby_updates[name].pos3 = pos
|
|
minetest.chat_send_player(name, "Position 3 set to " .. dump(pos) .. ".")
|
|
end
|
|
end)
|
|
|
|
|
|
local digits = {[0] =
|
|
{ true, true, true,
|
|
true, false, true,
|
|
true, false, true,
|
|
true, false, true,
|
|
true, true, true,
|
|
},
|
|
{false, false, true,
|
|
false, false, true,
|
|
false, false, true,
|
|
false, false, true,
|
|
false, false, true,
|
|
},
|
|
{ true, true, true,
|
|
false, false, true,
|
|
true, true, true,
|
|
true, false, false,
|
|
true, true, true,
|
|
},
|
|
{ true, true, true,
|
|
false, false, true,
|
|
true, true, true,
|
|
false, false, true,
|
|
true, true, true,
|
|
},
|
|
{ true, false, true,
|
|
true, false, true,
|
|
true, true, true,
|
|
false, false, true,
|
|
false, false, true,
|
|
},
|
|
{ true, true, true,
|
|
true, false, false,
|
|
true, true, true,
|
|
false, false, true,
|
|
true, true, true,
|
|
},
|
|
{ true, true, true,
|
|
true, false, false,
|
|
true, true, true,
|
|
true, false, true,
|
|
true, true, true,
|
|
},
|
|
{ true, true, true,
|
|
false, false, true,
|
|
false, false, true,
|
|
false, false, true,
|
|
false, false, true,
|
|
},
|
|
{ true, true, true,
|
|
true, false, true,
|
|
true, true, true,
|
|
true, false, true,
|
|
true, true, true,
|
|
},
|
|
{ true, true, true,
|
|
true, false, true,
|
|
true, true, true,
|
|
false, false, true,
|
|
true, true, true,
|
|
},
|
|
}
|
|
|
|
function boxes.make_new(player, size)
|
|
local minp = boxes.valloc(size + 2)
|
|
local maxp = vector.add(minp, size + 1)
|
|
|
|
-- Create the box
|
|
local cid_air = minetest.get_content_id("air")
|
|
local cid_stone = minetest.get_content_id("nodes:stone")
|
|
local cid_barrier = minetest.get_content_id("nodes:barrier")
|
|
local cid_marble = minetest.get_content_id("nodes:marble")
|
|
|
|
local vm = minetest.get_voxel_manip(minp, maxp)
|
|
local emin, emax = vm:get_emerged_area()
|
|
local va = VoxelArea:new{MinEdge=emin,MaxEdge=emax}
|
|
local vmdata = vm:get_data()
|
|
local param2 = vm:get_param2_data()
|
|
|
|
-- Set to air
|
|
for z = minp.z, maxp.z do
|
|
for y = minp.y, maxp.y do
|
|
local index = va:index(minp.x, y, z)
|
|
for x = minp.x, maxp.x do
|
|
vmdata[index] = cid_air
|
|
param2[index] = 0
|
|
index = index + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Add stone for walls and barrier at the top
|
|
for z = minp.z, maxp.z do
|
|
local index = va:index(minp.x, minp.y, z)
|
|
local index2 = va:index(minp.x, maxp.y, z)
|
|
for x = minp.x, maxp.x do
|
|
vmdata[index] = cid_stone
|
|
vmdata[index2] = cid_barrier
|
|
index = index + 1
|
|
index2 = index2 + 1
|
|
end
|
|
end
|
|
for y = minp.y, maxp.y do
|
|
local index = va:index(minp.x, y, minp.z)
|
|
local index2 = va:index(minp.x, y, maxp.z)
|
|
for x = minp.x, maxp.x do
|
|
vmdata[index] = cid_stone
|
|
vmdata[index2] = cid_stone
|
|
index = index + 1
|
|
index2 = index2 + 1
|
|
end
|
|
end
|
|
local ystride = emax.x - emin.x + 1
|
|
for z = minp.z, maxp.z do
|
|
local index = va:index(minp.x, minp.y, z)
|
|
local index2 = va:index(maxp.x, minp.y, z)
|
|
for y = minp.y, maxp.y do
|
|
vmdata[index] = cid_stone
|
|
vmdata[index2] = cid_stone
|
|
index = index + ystride
|
|
index2 = index2 + ystride
|
|
end
|
|
end
|
|
|
|
-- Write the box id
|
|
local id_string = tostring(min_free_id)
|
|
local id_sz = 4 * string.len(id_string) - 1
|
|
if size < 6 or size < id_sz + 2 then
|
|
print("WARNING: could not write box id: size too small")
|
|
else
|
|
local xoff = minp.x + math.floor((size + 2 - id_sz) / 2)
|
|
local yoff = minp.y + math.floor((size + 2 - 5 + 1) / 2)
|
|
local n = string.len(id_string)
|
|
for i = 1, string.len(id_string) do
|
|
for dx = 0, 2 do
|
|
for dy = 0, 4 do
|
|
if digits[string.byte(id_string, n - i + 1) - 48][3-dx+3*(4-dy)] then
|
|
local index = va:index(xoff + dx, yoff + dy, minp.z)
|
|
vmdata[index] = cid_marble
|
|
end
|
|
end
|
|
end
|
|
xoff = xoff + 4
|
|
end
|
|
end
|
|
|
|
vm:set_data(vmdata)
|
|
vm:set_param2_data(param2)
|
|
vm:update_liquids()
|
|
vm:write_to_map()
|
|
vm:update_map()
|
|
|
|
local s2 = math.floor(size / 2)
|
|
local entry = {x = 0, y = 1, z = s2 + 1}
|
|
local exit = {x = size + 2, y = 1, z = s2 + 1}
|
|
local sz = {x = size + 2, y = size + 2, z = size + 2}
|
|
local meta = {
|
|
type = db.BOX_TYPE,
|
|
meta = {
|
|
entry = entry,
|
|
exit = exit,
|
|
size = sz,
|
|
}
|
|
}
|
|
|
|
player:set_pos(vector.add(minp, {x = 1, y = 1, z = s2 + 1}))
|
|
db.box_set_meta(min_free_id, meta)
|
|
db.box_set_data(min_free_id, boxes.save(minp, maxp))
|
|
players_editing_boxes[player:get_player_name()] = {
|
|
box_id = min_free_id,
|
|
minp = minp,
|
|
maxp = maxp,
|
|
}
|
|
min_free_id = min_free_id + 1
|
|
end
|
|
|
|
minetest.register_chatcommand("edit", {
|
|
params = "<box_size>",
|
|
description = "Start editing a new box.",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if players_editing_boxes[name] then
|
|
minetest.chat_send_player(name, "You are already editing a box!")
|
|
return
|
|
end
|
|
|
|
local size = tonumber(param)
|
|
if not size or size ~= math.floor(size) or size <= 0 then
|
|
minetest.chat_send_player(name, "Please specify a size.")
|
|
return
|
|
end
|
|
|
|
boxes.make_new(minetest.get_player_by_name(name), size)
|
|
end,
|
|
})
|
|
|
|
|
|
minetest.register_chatcommand("edite", {
|
|
params = "<box_id>",
|
|
description = "Edit an existing box.",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if players_editing_boxes[name] then
|
|
minetest.chat_send_player(name, "You are already editing a box!")
|
|
return
|
|
end
|
|
|
|
local id = tonumber(param)
|
|
if not id or id ~= math.floor(id) or id < 0 then
|
|
minetest.chat_send_player(name, "The id you supplied is not a nonnegative integer.")
|
|
return
|
|
end
|
|
|
|
local meta = db.box_get_meta(id)
|
|
if not meta then
|
|
minetest.chat_send_player(name, "The id you supplied is not in the database.")
|
|
return
|
|
end
|
|
|
|
local player = minetest.get_player_by_name(name)
|
|
local minp = boxes.valloc(math.max(meta.meta.size.x, meta.meta.size.z))
|
|
local maxp = vector.add(minp, vector.subtract(meta.meta.size, 1))
|
|
|
|
players_editing_boxes[name] = {
|
|
box_id = id,
|
|
minp = minp,
|
|
maxp = maxp,
|
|
}
|
|
boxes.load(minp, id, player)
|
|
local spawnpoint = vector.add({x = 1, y = 0, z = 0}, vector.add(minp, meta.meta.entry))
|
|
player:set_pos(spawnpoint)
|
|
end,
|
|
})
|
|
|
|
function boxes.save_edit(player, id)
|
|
local name = player:get_player_name()
|
|
local box = players_editing_boxes[name]
|
|
if not box then return end
|
|
|
|
if id == nil then
|
|
id = box.box_id
|
|
end
|
|
|
|
db.box_set_data(id, boxes.save(box.minp, box.maxp))
|
|
end
|
|
|
|
function boxes.stop_edit(player)
|
|
local name = player:get_player_name()
|
|
local box = players_editing_boxes[name]
|
|
if not box then return end
|
|
|
|
boxes.cleanup(box.minp, box.maxp)
|
|
boxes.vfree(box.minp)
|
|
players_editing_boxes[name] = nil
|
|
end
|
|
|
|
minetest.register_chatcommand("save", {
|
|
params = "[<id>]",
|
|
description = "Save the box you are currently editing. If id is supplied, a copy is instead saved to the box numbered id.",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if not players_editing_boxes[name] then
|
|
minetest.chat_send_player(name, "You are not currently editing a box!")
|
|
return
|
|
end
|
|
|
|
local box_id = nil
|
|
if param ~= "" then
|
|
local id = tonumber(param)
|
|
if not id or id ~= math.floor(id) or id < 0 then
|
|
minetest.chat_send_player(name, "The id you supplied is not a non-negative number.")
|
|
return
|
|
end
|
|
box_id = id
|
|
end
|
|
|
|
boxes.save_edit(minetest.get_player_by_name(name), box_id)
|
|
end,
|
|
})
|
|
|
|
minetest.register_chatcommand("stopedit", {
|
|
params = "",
|
|
description = "Stop editing a box.",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if not players_editing_boxes[name] then
|
|
minetest.chat_send_player(name, "You are not currently editing a box!")
|
|
return
|
|
end
|
|
|
|
local player = minetest.get_player_by_name(name)
|
|
boxes.save_edit(player)
|
|
boxes.stop_edit(player)
|
|
players.return_to_lobby(player)
|
|
end,
|
|
})
|
|
|
|
local function on_leaveplayer(player)
|
|
print("leave", player:get_player_name())
|
|
boxes.close_box(player)
|
|
boxes.save_edit(player)
|
|
boxes.stop_edit(player)
|
|
end
|
|
|
|
minetest.register_on_leaveplayer(on_leaveplayer)
|
|
minetest.register_on_shutdown(function()
|
|
for _, player in ipairs(minetest.get_connected_players()) do
|
|
on_leaveplayer(player)
|
|
end
|
|
db.shutdown()
|
|
end)
|
|
|
|
|
|
--[[
|
|
|
|
-- Handle box expositions
|
|
-- These are boxes whose inside can be seen by the players before entering them
|
|
-- Proof of concept for now, so only one such box.
|
|
|
|
local box_expo_minp = {x = 5, y = 0, z = 5}
|
|
local box_expo_size = {x = 22, y = 22, z = 22}
|
|
|
|
|
|
minetest.register_chatcommand("expo", {
|
|
params = "<box_id>",
|
|
description = "Select the chosen box for exposition",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
local id = tonumber(param)
|
|
if not id or id ~= math.floor(id) or id < 0 then
|
|
minetest.chat_send_player(name, "The id you supplied is not a nonnegative integer.")
|
|
return
|
|
end
|
|
|
|
local meta = db.box_get_meta(id)
|
|
if not meta then
|
|
minetest.chat_send_player(name, "The id you supplied is not in the database.")
|
|
return
|
|
end
|
|
if meta.type ~= db.BOX_TYPE then
|
|
minetest.chat_send_player(name, "The id you supplied does not correspond to a box.")
|
|
return
|
|
end
|
|
|
|
local size = meta.meta.size
|
|
if not vector.equals(size, box_expo_size) then
|
|
minetest.chat_send_player(name, "The box you chose does not have the correct size.")
|
|
return
|
|
end
|
|
|
|
boxes.load(box_expo_minp, id, nil)
|
|
-- Open up a side
|
|
|
|
local minp = vector.add(box_expo_minp, {x = 1, y = 1, z = size.z - 1})
|
|
local maxp = vector.add(box_expo_minp, {x = size.x - 2, y = size.y - 1, z = size.z - 1})
|
|
|
|
local cid_barrier = minetest.get_content_id("nodes:barrier")
|
|
|
|
local vm = minetest.get_voxel_manip(minp, maxp)
|
|
local emin, emax = vm:get_emerged_area()
|
|
local va = VoxelArea:new{MinEdge=emin,MaxEdge=emax}
|
|
local vmdata = vm:get_data()
|
|
|
|
for y = minp.y, maxp.y do
|
|
local index = va:index(minp.x, y, maxp.z)
|
|
for x = minp.x, maxp.x do
|
|
vmdata[index] = cid_barrier
|
|
index = index + 1
|
|
end
|
|
end
|
|
|
|
vm:set_data(vmdata)
|
|
vm:write_to_map()
|
|
vm:update_map()
|
|
|
|
end,
|
|
})
|
|
|
|
]]
|