1442 lines
40 KiB
Lua
1442 lines
40 KiB
Lua
boxes = {}
|
|
boxes.teleport_to_tutorial_exit = {}
|
|
|
|
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")
|
|
|
|
-- Import/export boxes to files
|
|
dofile(modpath .. "/io.lua")
|
|
|
|
-- Handle inventory-related callbacks (i.e. whether the itemstacks
|
|
-- should be decreased when placing nodes)
|
|
dofile(modpath .. "/inv.lua")
|
|
|
|
-- Nodes that have a box-related effect
|
|
dofile(modpath .. "/nodes.lua")
|
|
|
|
-- Box score recording code
|
|
dofile(modpath .. "/score.lua")
|
|
|
|
local areas = AreaStore("insidethebox")
|
|
|
|
function boxes.find_box(pos)
|
|
local count = 0
|
|
local name = nil
|
|
for k, v in pairs(areas:get_areas_for_pos(pos, false, true)) do
|
|
name = v.data
|
|
count = count + 1
|
|
end
|
|
if count == 1 then
|
|
if boxes.players_in_boxes[name] then
|
|
return boxes.players_in_boxes[name]
|
|
elseif boxes.players_editing_boxes[name] then
|
|
return boxes.players_editing_boxes[name]
|
|
end
|
|
end
|
|
return false
|
|
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()
|
|
minetest.after(0.1, minetest.fix_light, minp, maxp)
|
|
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
|
|
|
|
boxes.players_in_boxes = {}
|
|
|
|
local function open_door(player, pos1, pos2, bminp, bmaxp)
|
|
local minp = vector.min(pos1, pos2)
|
|
local maxp = vector.max(pos1, pos2)
|
|
local drs = {}
|
|
local i = 1
|
|
for x = minp.x, maxp.x do
|
|
for y = minp.y, maxp.y do
|
|
for z = minp.z, maxp.z do
|
|
local door = doors.get({x = x, y = y, z = z})
|
|
if door then
|
|
door:open()
|
|
drs[i] = {x = x, y = y, z = z}
|
|
i = i + 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local od = boxes.players_in_boxes[player:get_player_name()].open_doors
|
|
od[#od + 1] = {minp = bminp, maxp = bmaxp, doors = drs, respawn =
|
|
{x = maxp.x + 1, y = minp.y, z = (minp.z + maxp.z) / 2}}
|
|
end
|
|
|
|
function boxes.player_success(player)
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_in_boxes[name]
|
|
local id = box.box_id
|
|
local time_taken = minetest.get_gametime() - box.start_time
|
|
local deaths = box.deaths
|
|
local damage = box.damage
|
|
local bmeta = db.box_get_meta(id)
|
|
|
|
boxes.score(name, id, "time", {time_taken})
|
|
boxes.score(name, id, "damage", {damage})
|
|
boxes.score(name, id, "deaths", {deaths})
|
|
|
|
bmeta.meta.num_completed_players = db.box_get_num_completed_players(id)
|
|
|
|
db.box_set_meta(id, bmeta)
|
|
for _, p in ipairs(box.signs_to_update) do
|
|
local node = minetest.get_node(p)
|
|
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_exit_update then
|
|
minetest.registered_nodes[node.name].on_exit_update(p, player, {
|
|
time_taken = {format = "time", data = time_taken},
|
|
damage_taken = {format = "text", data = tostring(damage)},
|
|
num_deaths = {format = "text", data = tostring(deaths)},
|
|
})
|
|
end
|
|
end
|
|
|
|
local sid = box.in_series
|
|
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)
|
|
|
|
-- check if player completed tutorial series
|
|
local bxs = db.series_get_boxes(sid)
|
|
if bxs[index + 1] then
|
|
return
|
|
end
|
|
-- omit check if tutorial is actually required here
|
|
if sid == conf.tutorial.series then
|
|
player:set_attribute("tutorial_completed", 1)
|
|
-- reward: create priv
|
|
local privs = minetest.get_player_privs(name)
|
|
privs.create = true
|
|
minetest.set_player_privs(name, privs)
|
|
boxes.teleport_to_tutorial_exit[name] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
function boxes.open_exit(player)
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_in_boxes[name]
|
|
local exds = box.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 = 0, z = 1}), ex.box_minp, ex.box_maxp)
|
|
end
|
|
|
|
function boxes.increase_items(player)
|
|
local name = player:get_player_name()
|
|
if not boxes.players_in_boxes[name] then
|
|
return
|
|
end
|
|
local exds = boxes.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(boxes.players_in_boxes) do
|
|
local i = 1
|
|
local player = minetest.get_player_by_name(playername)
|
|
local pos = player:get_pos()
|
|
local odoors = info.open_doors
|
|
local n = #odoors
|
|
while odoors[i] do
|
|
local off = 0.7
|
|
if odoors[i].minp.x + off <= pos.x and pos.x <= odoors[i].maxp.x - off and
|
|
odoors[i].minp.y <= pos.y and pos.y <= odoors[i].maxp.y and
|
|
odoors[i].minp.z + off <= pos.z and pos.z <= odoors[i].maxp.z - off
|
|
then
|
|
for _, dpos in ipairs(odoors[i].doors) do
|
|
local door = doors.get(dpos)
|
|
if door then
|
|
door:close()
|
|
end
|
|
end
|
|
info.respawn = odoors[i].respawn
|
|
odoors[i] = odoors[n]
|
|
odoors[n] = nil
|
|
n = n - 1
|
|
if not info.start_time then
|
|
info.start_time = minetest.get_gametime()
|
|
info.deaths = 0
|
|
info.damage = 0
|
|
music.start(player, info, "box")
|
|
else
|
|
-- We closed the last door: the player got out!
|
|
boxes.player_success(player)
|
|
minetest.chat_send_all(playername .. " completed box " .. info.box_id .. "!")
|
|
music.start(player, info, "exit")
|
|
end
|
|
else
|
|
i = i + 1
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
local function translate_all(l, offset)
|
|
local result = {}
|
|
for i, p in ipairs(l) do
|
|
result[i] = vector.add(p, offset)
|
|
end
|
|
return result
|
|
end
|
|
|
|
-- Although this functions takes a list as an argument,
|
|
-- several parts of the code rely on it having length exactly
|
|
-- 3 and being entry lobby, box, exit
|
|
function boxes.open_box(player, box_id_list)
|
|
local name = player:get_player_name()
|
|
if boxes.players_in_boxes[name] ~= nil or boxes.players_editing_boxes[name] ~= nil then
|
|
return
|
|
end
|
|
|
|
minetest.log("action", name .. " entered box " .. box_id_list[2])
|
|
|
|
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
|
|
|
|
local spawn_pos = vector.add(minp, vector.add(metas[1].entry, offsets[1]))
|
|
spawn_pos.y = spawn_pos.y - 0.5
|
|
|
|
local base_exit = vector.add(offsets[3], minp)
|
|
local signs_to_update = translate_all(metas[3].signs_to_update, base_exit)
|
|
local star_positions = translate_all(metas[3].star_positions, base_exit)
|
|
local category_positions = translate_all(metas[3].category_positions, base_exit)
|
|
|
|
local areas_id = areas:insert_area(vector.add(minp, 1), vector.subtract(maxp, 1), name)
|
|
boxes.players_in_boxes[name] = {
|
|
name = name,
|
|
box_id = box_id_list[2],
|
|
minp = minp,
|
|
maxp = maxp,
|
|
areas_id = areas_id,
|
|
exit_doors = exit_doors,
|
|
exit = vector.add(minp, vector.add(metas[n].exit, offsets[n])),
|
|
open_doors = {},
|
|
respawn = spawn_pos,
|
|
signs_to_update = signs_to_update,
|
|
star_positions = star_positions,
|
|
category_positions = category_positions,
|
|
}
|
|
|
|
|
|
for i, box in ipairs(box_id_list) do
|
|
boxes.load(vector.add(minp, offsets[i]), box, player)
|
|
end
|
|
|
|
player:set_physics_override({gravity = 0, jump = 0})
|
|
spawn_pos.y = spawn_pos.y + 0.25
|
|
player:set_pos(spawn_pos)
|
|
minetest.after(0.5, function()
|
|
player:set_physics_override({gravity = 1, jump = 1})
|
|
end)
|
|
player:set_look_horizontal(3 * math.pi / 2)
|
|
player:set_look_vertical(0)
|
|
players.give_box_inventory(player)
|
|
skybox.set(player, metas[n - 1].skybox)
|
|
|
|
boxes.open_exit(player)
|
|
|
|
minetest.after(2.0, function()
|
|
if not player then
|
|
return
|
|
end
|
|
|
|
local p = player:get_pos()
|
|
if p and p.y < spawn_pos.y - 0.25 and boxes.players_in_boxes[name] then
|
|
minetest.log("error", name .. " fell from an entrance lobby")
|
|
player:set_pos(spawn_pos)
|
|
end
|
|
end)
|
|
end
|
|
|
|
function boxes.close_box(player)
|
|
local name = player:get_player_name()
|
|
if boxes.players_in_boxes[name] == nil then
|
|
return
|
|
end
|
|
|
|
local bx = boxes.players_in_boxes[name]
|
|
|
|
minetest.log("action", name .. " left box " .. bx.box_id)
|
|
|
|
boxes.cleanup(bx.minp, bx.maxp)
|
|
boxes.vfree(bx.minp)
|
|
areas:remove_area(boxes.players_in_boxes[name].areas_id)
|
|
boxes.players_in_boxes[name] = nil
|
|
end
|
|
|
|
function boxes.next_series(player, sid, is_entering)
|
|
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
|
|
if not is_entering then
|
|
players.return_to_lobby(player)
|
|
end
|
|
return false
|
|
else
|
|
if sid == conf.tutorial.series and
|
|
conf.tutorial.required and
|
|
player:get_attribute("tutorial_completed") ~= "1" then
|
|
boxes.open_box(player, {conf.tutorial.entry_lobby, bxs[index], conf.tutorial.exit_lobby})
|
|
else
|
|
boxes.open_box(player, {0, bxs[index], 1})
|
|
end
|
|
boxes.players_in_boxes[name].in_series = sid
|
|
return true
|
|
end
|
|
end
|
|
|
|
function boxes.validate_pos(player, pos, withborder)
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_in_boxes[name] or boxes.players_editing_boxes[name]
|
|
if not box then
|
|
if not minetest.check_player_privs(player, "server") then
|
|
return false
|
|
end
|
|
box = {
|
|
minp = { x = -32768, y = -32768, z = -32768 },
|
|
maxp = { x = 32768, y = 32768, z = 32768 },
|
|
}
|
|
end
|
|
if withborder then
|
|
if pos.x < box.minp.x or pos.x > box.maxp.x or
|
|
pos.y < box.minp.y or pos.y > box.maxp.y or
|
|
pos.z < box.minp.z or pos.z > box.maxp.z then
|
|
return false
|
|
end
|
|
else
|
|
if pos.x < box.minp.x + 1 or pos.x > box.maxp.x - 1 or
|
|
pos.y < box.minp.y + 1 or pos.y > box.maxp.y - 1 or
|
|
pos.z < box.minp.z + 1 or pos.z > box.maxp.z - 1 then
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
boxes.players_editing_boxes = {}
|
|
|
|
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 boxes.players_in_boxes[name] or boxes.players_editing_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
|
|
-- only server admins may enter any box
|
|
-- normal players may not enter anything but published boxes or their own boxes
|
|
if minetest.check_player_privs(player, "server") or
|
|
meta.meta.status == db.STATUS_ACCEPTED or
|
|
meta.meta.builder == name then
|
|
boxes.open_box(player, {0, id, 1})
|
|
else
|
|
minetest.chat_send_player(name, "You do not have permissions to enter that box.")
|
|
end
|
|
end,
|
|
})
|
|
|
|
minetest.register_chatcommand("series_create", {
|
|
params = "<name>",
|
|
description = "Create a new series",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if param == "" then
|
|
minetest.chat_send_player(name, "Please privide a name for the series")
|
|
return
|
|
end
|
|
local id = db.series_create(param)
|
|
if id then
|
|
minetest.chat_send_player(name, "Series successfully created with id " .. id)
|
|
else
|
|
minetest.chat_send_player(name, "Series failed to create")
|
|
end
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("series_destroy", {
|
|
params = "<series_id>",
|
|
description = "Destroy a series",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if param == "" then
|
|
minetest.chat_send_player(name, "Please privide a series id to destroy")
|
|
return
|
|
end
|
|
local id = tonumber(param)
|
|
if not db.series_destroy(id) then
|
|
minetest.chat_send_player(name, "Failed to destroy series " .. id)
|
|
else
|
|
minetest.chat_send_player(name, "Destroyed series " .. id)
|
|
end
|
|
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_add_at_end(sid, bid)
|
|
minetest.chat_send_player(name, "Done")
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("series_list", {
|
|
params = "",
|
|
description = "List defined series",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if param ~= "" then
|
|
-- list boxes in this series.
|
|
local sid = tonumber(param)
|
|
if not sid or not db.series_get_meta(sid) then
|
|
minetest.chat_send_player(name, "Series doesn't exist.")
|
|
return
|
|
end
|
|
for _, v in ipairs(db.series_get_boxes(sid)) do
|
|
minetest.chat_send_player(name, v)
|
|
end
|
|
else
|
|
-- list all series
|
|
for k, v in ipairs(db.series_get_series()) do
|
|
minetest.chat_send_player(name, v.id .. " - " .. v.name)
|
|
end
|
|
end
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("series_remove", {
|
|
params = "<series_id> <box_id>",
|
|
description = "Remove a box from specified 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_delete_box(sid, bid)
|
|
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 boxes.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)
|
|
music.stop(player)
|
|
players.return_to_lobby(player)
|
|
end,
|
|
})
|
|
|
|
minetest.register_chatcommand("open", {
|
|
params = "",
|
|
description = "Open the current exit.",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
local box = boxes.players_in_boxes[name]
|
|
if not box then
|
|
minetest.chat_send_player(name, "You are not in a box!")
|
|
return
|
|
end
|
|
if box.exit_doors[1] == nil then
|
|
minetest.chat_send_player(name, "There are no more exits to open!")
|
|
return
|
|
end
|
|
if box.open_doors[1] ~= nil then
|
|
minetest.chat_send_player(name, "This box already has an open door!")
|
|
return
|
|
end
|
|
local player = minetest.get_player_by_name(name)
|
|
boxes.open_exit(player)
|
|
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 <box id>",
|
|
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)
|
|
local update_type, box_id = string.match(param, "^([^ ]+) +(.+)$")
|
|
|
|
if update_type ~= "entry" and update_type ~= "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
|
|
box_id = tonumber(box_id)
|
|
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 update_type == "exit" then
|
|
box_id = box_id or 1
|
|
local player = minetest.get_player_by_name(name)
|
|
local exit = vector.subtract(vector.round(player:get_pos()), minp)
|
|
db.box_set_data(box_id, data)
|
|
db.box_set_meta(box_id, {
|
|
type = db.EXIT_TYPE,
|
|
meta = {
|
|
size = vector.add(vector.subtract(maxp, minp), 1),
|
|
entry = p3,
|
|
exit = exit,
|
|
}
|
|
})
|
|
else
|
|
box_id = box_id or 0
|
|
local player = minetest.get_player_by_name(name)
|
|
local entry = vector.subtract(vector.round(player:get_pos()), minp)
|
|
p3.x = p3.x + 1
|
|
db.box_set_data(box_id, data)
|
|
db.box_set_meta(box_id, {
|
|
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)
|
|
|
|
-- Clear existing metadata
|
|
local meta_positions = minetest.find_nodes_with_meta(minp, maxp)
|
|
for _, pos in ipairs(meta_positions) do
|
|
minetest.get_meta(pos):from_table()
|
|
end
|
|
|
|
-- Create the box
|
|
local cid_air = minetest.get_content_id("air")
|
|
local cid_wall = minetest.get_content_id("nodes:marbleb")
|
|
local cid_digit = minetest.get_content_id("nodes:bronzeb")
|
|
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()
|
|
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_wall
|
|
vmdata[index2] = cid_barrier
|
|
index = index + 1
|
|
index2 = index2 + 1
|
|
end
|
|
end
|
|
for y = minp.y, maxp.y - 1 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_wall
|
|
vmdata[index2] = cid_wall
|
|
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 - 1 do
|
|
vmdata[index] = cid_wall
|
|
vmdata[index2] = cid_wall
|
|
index = index + ystride
|
|
index2 = index2 + ystride
|
|
end
|
|
end
|
|
|
|
local box_id = db.get_last_box_id() + 1
|
|
|
|
-- Write the box id
|
|
local id_string = tostring(box_id)
|
|
local id_sz = 4 * string.len(id_string) - 1
|
|
if size < 6 or size < id_sz + 2 then
|
|
minetest.log("error", "boxes.make_new(" .. size .. "): box 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_digit
|
|
end
|
|
end
|
|
end
|
|
xoff = xoff + 4
|
|
end
|
|
end
|
|
local digit_pos = {
|
|
x = math.floor((size + 2 - id_sz) / 2),
|
|
y = math.floor((size + 2 - 5 + 1) / 2),
|
|
z = 0
|
|
}
|
|
|
|
vm:set_data(vmdata)
|
|
vm:set_param2_data(param2)
|
|
vm:update_liquids()
|
|
vm:write_to_map()
|
|
vm:update_map()
|
|
minetest.after(0.1, minetest.fix_light, minp, maxp)
|
|
|
|
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,
|
|
num_items = 0,
|
|
box_name = "(No name)",
|
|
builder = player:get_player_name(),
|
|
build_time = 0,
|
|
skybox = 0,
|
|
digit_pos = digit_pos,
|
|
}
|
|
}
|
|
|
|
player:set_pos(vector.add(minp, {x = 1, y = 1, z = s2 + 1}))
|
|
players.give_edit_inventory(player)
|
|
db.box_set_meta(box_id, meta)
|
|
db.box_set_data(box_id, boxes.save(minp, maxp))
|
|
|
|
local name = player:get_player_name()
|
|
local areas_id = areas:insert_area(vector.add(minp, 1), vector.subtract(maxp, 1), name)
|
|
boxes.players_editing_boxes[name] = {
|
|
name = name,
|
|
box_id = box_id,
|
|
minp = minp,
|
|
maxp = maxp,
|
|
areas_id = areas_id,
|
|
num_items = 0,
|
|
entry = vector.add(minp, entry),
|
|
exit = vector.add(minp, exit),
|
|
start_edit_time = minetest.get_gametime(),
|
|
box_name = "",
|
|
skybox = 0,
|
|
}
|
|
end
|
|
|
|
local function get_digit_pos(player)
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_editing_boxes[name]
|
|
if not box then
|
|
return
|
|
end
|
|
local id = box.box_id
|
|
local meta = db.box_get_meta(id)
|
|
if meta.meta.digit_pos then
|
|
return meta.meta.digit_pos
|
|
end
|
|
|
|
-- Digit position is not specified in meta (old boxes)
|
|
-- We have to infer it.
|
|
local minp = box.minp
|
|
local maxp = {x = box.maxp.x, y = box.maxp.y, z = box.minp.z}
|
|
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 cid_digit = minetest.get_content_id("nodes:bronzeb")
|
|
local maxx = minp.x
|
|
local maxy = minp.y
|
|
for y = minp.y, maxp.y do
|
|
local index = va:index(minp.x, y, minp.z)
|
|
for x = minp.x, maxp.x do
|
|
if vmdata[index] == cid_digit then
|
|
if x > maxx then
|
|
maxx = x
|
|
end
|
|
if y > maxy then
|
|
maxy = y
|
|
end
|
|
end
|
|
index = index + 1
|
|
end
|
|
end
|
|
local id_string = tostring(id)
|
|
local id_sz = 4 * string.len(id_string) - 1
|
|
local digit_pos = {x = maxx - minp.x - id_sz + 1, y = maxy - minp.y - 4, z = 0}
|
|
meta.meta.digit_pos = digit_pos
|
|
db.box_set_meta(id, meta)
|
|
return digit_pos
|
|
end
|
|
|
|
local function paint_digits(pos, node, id)
|
|
local id_string = tostring(id)
|
|
local n = string.len(id_string)
|
|
for i = 1, n 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 p = {x = pos.x + 4 * i - 4 + dx, y = pos.y + dy, z = pos.z}
|
|
minetest.set_node(p, node)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function boxes.move_digits(player, pos)
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_editing_boxes[name]
|
|
if not box then
|
|
return
|
|
end
|
|
local id = box.box_id
|
|
local old_pos = get_digit_pos(player)
|
|
if not old_pos then
|
|
minetest.log("boxes.move_digits: error: old digit pos is nil")
|
|
return
|
|
end
|
|
paint_digits(vector.add(box.minp, old_pos), {name = "nodes:marbleb"}, id)
|
|
paint_digits(pos, {name = "nodes:bronzeb"}, id)
|
|
local meta = db.box_get_meta(id)
|
|
print(dump(meta.meta.digit_pos))
|
|
meta.meta.digit_pos = vector.subtract(pos, box.minp)
|
|
print(dump(meta.meta.digit_pos))
|
|
db.box_set_meta(id, meta)
|
|
end
|
|
|
|
minetest.register_privilege("create", "Can create new boxes")
|
|
|
|
minetest.register_chatcommand("create", {
|
|
params = "<box_size>",
|
|
description = "Start editing a new box.",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
if boxes.players_editing_boxes[name] or boxes.players_in_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
|
|
|
|
if size < 20 or size > 40 then
|
|
minetest.chat_send_player(name, "Size is not in the allowed range [20, 40]")
|
|
return
|
|
end
|
|
|
|
boxes.make_new(minetest.get_player_by_name(name), size)
|
|
end,
|
|
})
|
|
|
|
minetest.register_chatcommand("edit", {
|
|
params = "",
|
|
description = "DEPRECATED",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
minetest.chat_send_player(name, "Did you mean /create or /edite?")
|
|
end,
|
|
})
|
|
|
|
|
|
minetest.register_chatcommand("edite", {
|
|
params = "<box_id>",
|
|
description = "Edit an existing box.",
|
|
privs = {create = true},
|
|
func = function(name, param)
|
|
if boxes.players_editing_boxes[name] or boxes.players_in_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
|
|
|
|
if not minetest.check_player_privs(name, "server") then
|
|
if meta.meta.builder ~= name or meta.meta.status ~= db.STATUS_EDITING then
|
|
minetest.chat_send_player(name, "You are not allowed to edit this box.")
|
|
return
|
|
end
|
|
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))
|
|
|
|
local areas_id = areas:insert_area(vector.add(minp, 1), vector.subtract(maxp, 1), name)
|
|
boxes.players_editing_boxes[name] = {
|
|
name = name,
|
|
box_id = id,
|
|
minp = minp,
|
|
maxp = maxp,
|
|
areas_id = areas_id,
|
|
num_items = meta.meta.num_items,
|
|
entry = vector.add(minp, meta.meta.entry),
|
|
exit = vector.add(minp, meta.meta.exit),
|
|
start_edit_time = minetest.get_gametime(),
|
|
box_name = meta.meta.box_name,
|
|
skybox = meta.meta.skybox,
|
|
}
|
|
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)
|
|
players.give_edit_inventory(player)
|
|
music.start(player, nil, "create")
|
|
-- skybox may not exist for lobbies
|
|
if meta.meta.skybox then
|
|
skybox.set(player, meta.meta.skybox)
|
|
end
|
|
end,
|
|
})
|
|
|
|
function boxes.save_edit(player, id)
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_editing_boxes[name]
|
|
if not box then return end
|
|
|
|
local bmeta = db.box_get_meta(box.box_id)
|
|
if id == nil then
|
|
id = box.box_id
|
|
end
|
|
|
|
db.box_set_data(id, boxes.save(box.minp, box.maxp))
|
|
bmeta.meta.num_items = box.num_items
|
|
bmeta.meta.entry = vector.subtract(box.entry, box.minp)
|
|
bmeta.meta.exit = vector.subtract(box.exit, box.minp)
|
|
bmeta.meta.box_name = box.box_name
|
|
if box.box_name == "" then
|
|
bmeta.meta.box_name = "(No name)"
|
|
end
|
|
bmeta.meta.skybox = box.skybox
|
|
local t = minetest.get_gametime()
|
|
if name == bmeta.meta.builder then -- Do not count admin edit times
|
|
bmeta.meta.build_time = bmeta.meta.build_time + t - box.start_edit_time
|
|
box.start_edit_time = t
|
|
end
|
|
db.box_set_meta(id, bmeta)
|
|
|
|
minetest.log("action", name .. " saved box " .. id)
|
|
minetest.chat_send_player(name, "Box " .. id .. " saved successfully")
|
|
end
|
|
|
|
function boxes.stop_edit(player)
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_editing_boxes[name]
|
|
if not box then return end
|
|
|
|
boxes.cleanup(box.minp, box.maxp)
|
|
boxes.vfree(box.minp)
|
|
areas:remove_area(boxes.players_editing_boxes[name].areas_id)
|
|
boxes.players_editing_boxes[name] = nil
|
|
end
|
|
|
|
minetest.register_chatcommand("setbuilder", {
|
|
params = "<id> <name>",
|
|
description = "Set the builder of the box <id> to <name>.",
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
local box_id, nname = string.match(param, "^([^ ]+) +(.+)$")
|
|
box_id = tonumber(box_id)
|
|
if not box_id or not nname then
|
|
minetest.chat_send_player(name, "Supplied box id/name missing or incorrect format!")
|
|
return
|
|
end
|
|
local bmeta = db.box_get_meta(box_id)
|
|
if not bmeta then
|
|
minetest.chat_send_player(name, "Supplied box id does not exist!")
|
|
return
|
|
end
|
|
if bmeta.type ~= db.BOX_TYPE then
|
|
minetest.chat_send_player(name, "This id does not correspond to a box.")
|
|
return
|
|
end
|
|
bmeta.meta.builder = nname
|
|
db.box_set_meta(box_id, bmeta)
|
|
minetest.chat_send_player(name, "Done.")
|
|
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 boxes.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 boxes.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)
|
|
if param ~= "false" then
|
|
boxes.save_edit(player)
|
|
end
|
|
boxes.stop_edit(player)
|
|
music.stop(player)
|
|
players.return_to_lobby(player)
|
|
end,
|
|
})
|
|
|
|
local function on_leaveplayer(player)
|
|
minetest.log("action", player:get_player_name() .. " left the game")
|
|
music.stop(player)
|
|
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)
|
|
|
|
minetest.register_on_respawnplayer(function(player)
|
|
local name = player:get_player_name()
|
|
if boxes.players_in_boxes[name] then
|
|
local box = boxes.players_in_boxes[name]
|
|
player:setpos(box.respawn)
|
|
elseif boxes.players_editing_boxes[name] then
|
|
local box = boxes.players_editing_boxes[name]
|
|
player:setpos(vector.add(box.entry, {x = 1, y = 0, z = 0}))
|
|
else
|
|
players.return_to_lobby(player)
|
|
end
|
|
return true
|
|
end)
|
|
|
|
minetest.register_on_dieplayer(function(player)
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_in_boxes[name]
|
|
if box and box.deaths then
|
|
box.deaths = box.deaths + 1
|
|
end
|
|
end)
|
|
|
|
minetest.register_on_player_hpchange(function(player, hp_change)
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_in_boxes[name]
|
|
if box and box.damage and hp_change < 0 then
|
|
box.damage = box.damage - hp_change
|
|
end
|
|
end, false)
|
|
|
|
function boxes.can_edit(player)
|
|
local name = player:get_player_name()
|
|
if boxes.players_editing_boxes[name] then
|
|
return true
|
|
end
|
|
if boxes.players_in_boxes[name] then
|
|
return false
|
|
end
|
|
return minetest.check_player_privs(name, "server")
|
|
end
|
|
|
|
|
|
local metadata_updates = {}
|
|
minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing)
|
|
if not puncher then return end
|
|
local name = puncher:get_player_name()
|
|
if not metadata_updates[name] then return end
|
|
if not boxes.players_editing_boxes[name] then return end
|
|
local box = boxes.players_editing_boxes[name]
|
|
local p = vector.subtract(pos, box.minp)
|
|
local upd = metadata_updates[name]
|
|
local key = upd.update_key
|
|
local bmeta = db.box_get_meta(box.box_id)
|
|
if upd.update_type == "set" then
|
|
bmeta.meta[key] = p
|
|
db.box_set_meta(box.box_id, bmeta)
|
|
metadata_updates[name] = nil
|
|
sfinv.set_player_inventory_formspec(puncher)
|
|
elseif upd.update_type == "add" then
|
|
bmeta.meta[key][#bmeta.meta[key] + 1] = p
|
|
db.box_set_meta(box.box_id, bmeta)
|
|
sfinv.set_player_inventory_formspec(puncher)
|
|
end
|
|
end)
|
|
|
|
local meta_types = {
|
|
[db.BOX_TYPE] = {
|
|
{key_name = "builder", type = "text"},
|
|
{key_name = "status", type = "number"},
|
|
},
|
|
[db.ENTRY_TYPE] = {
|
|
{key_name = "status", type = "number"},
|
|
},
|
|
[db.EXIT_TYPE] = {
|
|
{key_name = "signs_to_update", type = "poslist"},
|
|
{key_name = "star_positions", type = "poslist"},
|
|
{key_name = "category_positions", type = "poslist"},
|
|
{key_name = "status", type = "number"},
|
|
}
|
|
}
|
|
|
|
sfinv.register_page("boxes:admin", {
|
|
title = "Admin",
|
|
is_in_nav = function(self, player, context)
|
|
return minetest.check_player_privs(player, "server") and
|
|
boxes.players_editing_boxes[player:get_player_name()]
|
|
end,
|
|
get = function(self, player, context)
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_editing_boxes[name]
|
|
local upd = metadata_updates[name]
|
|
local bmeta = db.box_get_meta(box.box_id)
|
|
local form = ""
|
|
local y = 0
|
|
for i, setting in ipairs(meta_types[bmeta.type]) do
|
|
local current_value = bmeta.meta[setting.key_name]
|
|
if setting.type == "text" then
|
|
form = form .. "field[0.3," .. (y + 0.8) .. ";5,1;" ..
|
|
tostring(i) .. ";" .. setting.key_name .. ";" ..
|
|
minetest.formspec_escape(current_value) .. "]" ..
|
|
"field_close_on_enter[" .. tostring(i) .. ";false]"
|
|
y = y + 1.2
|
|
elseif setting.type == "number" then
|
|
form = form .. "field[0.3," .. (y + 0.8) .. ";5,1;" ..
|
|
tostring(i) .. ";" .. setting.key_name .. ";" ..
|
|
tostring(current_value) .. "]" ..
|
|
"field_close_on_enter[" .. tostring(i) .. ";false]"
|
|
y = y + 1.2
|
|
elseif setting.type == "pos" then
|
|
local blab = "Edit"
|
|
if upd and upd.update_key == setting.key_name then
|
|
blab = "Cancel edit"
|
|
end
|
|
form = form .. "label[0," .. (y + 0.3) .. ";" ..
|
|
minetest.formspec_escape(setting.key_name .. ": " .. minetest.pos_to_string(current_value)) .. "]"
|
|
form = form .. "button[0," .. (y + 0.7) .. ";3,1;" ..
|
|
tostring(i) .. ";" .. blab .. "]"
|
|
y = y + 1.6
|
|
elseif setting.type == "poslist" then
|
|
local blab = "Edit"
|
|
if upd and upd.update_key == setting.key_name then
|
|
blab = "Stop edit"
|
|
end
|
|
local cvs = "{"
|
|
for j, pos in ipairs(current_value) do
|
|
if j > 1 then
|
|
cvs = cvs .. ", "
|
|
end
|
|
cvs = cvs .. minetest.pos_to_string(pos)
|
|
end
|
|
cvs = cvs .. "}"
|
|
form = form .. "label[0," .. (y + 0.3) .. ";" ..
|
|
minetest.formspec_escape(setting.key_name .. ": " .. cvs) .. "]"
|
|
form = form .. "button[0," .. (y + 0.7) .. ";3,1;" ..
|
|
tostring(i) .. ";" .. blab .. "]"
|
|
y = y + 1.6
|
|
end
|
|
end
|
|
return sfinv.make_formspec(player, context, form, false)
|
|
end,
|
|
on_player_receive_fields = function(self, player, context, fields)
|
|
if not minetest.check_player_privs(player, "server") then
|
|
return
|
|
end
|
|
|
|
local update = false
|
|
|
|
local name = player:get_player_name()
|
|
local box = boxes.players_editing_boxes[name]
|
|
local bmeta = db.box_get_meta(box.box_id)
|
|
local settings = meta_types[bmeta.type]
|
|
for index, value in pairs(fields) do
|
|
local i = tonumber(index)
|
|
if i ~= nil then
|
|
local setting = settings[i]
|
|
if setting.type == "number" then
|
|
value = tonumber(value)
|
|
end
|
|
--FIXME log changes
|
|
if value ~= nil and setting.type ~= "pos" and setting.type ~= "poslist" then
|
|
bmeta.meta[setting.key_name] = value
|
|
elseif setting.type == "pos" or setting.type == "poslist" then
|
|
if metadata_updates[name] and metadata_updates[name].update_key == setting.key_name then
|
|
metadata_updates[name] = nil
|
|
else
|
|
if setting.type == "poslist" then
|
|
bmeta.meta[setting.key_name] = {}
|
|
end
|
|
metadata_updates[name] = {
|
|
update_type = (setting.type == "pos" and "set" or "add"),
|
|
update_key = setting.key_name
|
|
}
|
|
end
|
|
update = true
|
|
end
|
|
end
|
|
end
|
|
db.box_set_meta(box.box_id, bmeta)
|
|
|
|
if update then
|
|
sfinv.set_page(player, "boxes:admin")
|
|
end
|
|
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,
|
|
})
|
|
|
|
]]
|