dev_positioning/init.lua
2020-08-21 17:48:41 +02:00

260 lines
7.1 KiB
Lua

local todeg = 180 / math.pi
local torad = math.pi / 180
local contexts = {}
local mod_storage = minetest.get_mod_storage()
local saved_positionings = minetest.deserialize(
mod_storage:get_string("positionings"), true) or {}
local function get_positionings(player_name)
return saved_positionings[player_name] or {}
end
local function save_positionings(player_name, player_positionings)
saved_positionings[player_name] = player_positionings
mod_storage:set_string("positionings", minetest.serialize(saved_positionings))
end
local function list_positionings(player_name)
local result = {}
for name, _ in pairs(get_positionings(player_name)) do
result[#result + 1] = name
end
table.sort(result)
return result
end
local function set_player_positioning(player_name, positioning)
local player = minetest.get_player_by_name(player_name)
if not player then
return
end
player:set_pos({
x = positioning.pos_x,
y = positioning.pos_y,
z = positioning.pos_z,
})
player:set_look_vertical(positioning.look_v * torad)
player:set_look_horizontal(positioning.look_h * torad)
player:set_fov(positioning.fov)
end
local function get_player_positioning(player_name)
local player = minetest.get_player_by_name(player_name)
if not player then
return
end
local pos = player:get_pos()
local fov = player:get_fov()
if fov == 0 then
fov = minetest.settings:get("fov")
end
return {
pos_x = pos.x,
pos_y = pos.y,
pos_z = pos.z,
look_v = player:get_look_vertical() * todeg,
look_h = player:get_look_horizontal() * todeg,
fov = fov,
}
end
local positioning_fields = {
{ name = "pos_x", label = "X", step = 0.5, format = ".3f"},
{ name = "pos_y", label = "Y", step = 0.5, format = ".3f"},
{ name = "pos_z", label = "Z", step = 0.5, format = ".3f"},
{ name = "look_v", label = "Vertical", step = 5.0, format = ".1f", min = -90, max = 90},
{ name = "look_h", label = "Horiz.", step = 5.0, format = ".1f"},
{ name = "fov", label = "FOV", step = 5.0, format = ".1f", min = 45, max = 180},
}
local function fields_to_positioning(fields)
local positioning = {}
for _, field in ipairs(positioning_fields) do
positioning[field.name] = tonumber(fields[field.name])
if not positioning[field.name] then
return
end
end
return positioning
end
local function show_formspec(player_name)
local positioning = get_player_positioning(player_name)
local context = contexts[player_name] or
{ current_positioning = positioning }
context.positionings = list_positionings(player_name)
local list = "<current>"
for _, name in pairs(context.positionings) do
list = list .. "," .. name or name
end
local fs = "formspec_version[3]"..
"size[15,12]" ..
"no_prepend[]" ..
"bgcolor[black;neither;black]" ..
"container[0,0]"..
"box[0,0;3,7;#00000040]"..
"label[0.2,0.4;Saved positionings]"..
"textlist[0.2,0.7;2.6,3;list;" .. list .. ";" .. (context.selected or 1) .. ";false]" ..
"button[0.2,4;2.6,0.7;btn_delete;Delete selected]" ..
"field[0.2,5.3;2.6,0.5;name;Save positioning as;]" ..
"button[0.2,6;2.6,0.7;btn_save;Save]" ..
"container_end[]"..
"container[2.25,9.8]"..
"box[0,0;10.5,2.2;#00000040]"..
"container[0.2,0.4]"..
"label[0,0;Position:]" ..
"label[4.5,0;Look:]"
local x = 0
for _, field in ipairs(positioning_fields) do
fs = fs ..
("field[%.1f,0.5;1.4,0.5;%s;%s;%".. field.format .."]")
:format(x, field.name, field.label, positioning[field.name]) ..
("button[%.1f,1.1;0.5,0.5;btn_%s_dec;-]"):format(x, field.name) ..
("button[%.1f,1.1;0.5,0.5;btn_%s_inc;+]"):format(x + 0.9, field.name)
x = x + 1.5
end
fs = fs ..
("button[%.1f,0.5;1,0.5;btn_apply;Apply]"):format(x) ..
"container_end[]" ..
"container_end[]"
if context.message then
fs = fs ..
"container[3.5,8]"..
"box[0,0;8,1;#80000040]"..
"label[1,0.5;" .. context.message .. "]" ..
"container_end[]"
context.message = nil
end
contexts[player_name] = context
minetest.show_formspec(player_name, "dev_positioning", fs)
end
minetest.register_on_player_receive_fields(
function(player, formname, fields)
if formname ~= "dev_positioning" then
return
end
local player_name = player:get_player_name()
if fields.quit and not fields.key_enter then
contexts[player_name] = nil
minetest.close_formspec(player_name, formname)
return
end
if not minetest.check_player_privs(player_name, {server=true}) then
minetest.log("error", "Suspicious fields received. Player:" ..
player_name .. " Formspec:" .. formname .. " (missing privilege).")
minetest.close_formspec(player_name, formname)
return
end
local context = contexts[player_name]
if not context then
minetest.log("error", "Suspicious fields received. Player:" ..
player_name .. " Formspec:" .. formname .. " (no context found).")
minetest.close_formspec(player_name, formname)
return
end
local positioning = fields_to_positioning(fields)
if not positioning then
context.message = "Invalid positioning"
show_formspec(player_name)
return
end
if fields.list then
local event = minetest.explode_textlist_event(fields.list)
if event.type == "CHG" then
context.selected = event.index
if context.selected == 1 then
positioning = context.current_positioning
else
local positionings = get_positionings(player_name)
positioning = positionings[context.positionings[context.selected - 1]]
end
if positioning then
set_player_positioning(player_name, positioning)
show_formspec(player_name)
end
end
return
end
if fields.btn_save then
if not fields.name or fields.name == "" then
context.message = "Please enter a name"
show_formspec(player_name)
return
end
-- TODO : Add warning if name exists
local positionings = get_positionings(player_name)
positionings[fields.name] = positioning
save_positionings(player_name, positionings)
show_formspec(player_name)
return
end
if fields.btn_delete and context.selected then
local positionings = get_positionings(player_name)
if context.positionings[context.selected - 1] then
positionings[context.positionings[context.selected - 1]] = nil
save_positionings(player_name, positionings)
context.selected = 1
show_formspec(player_name)
end
return
end
for _, field in ipairs(positioning_fields) do
local value = math.floor(positioning[field.name]
/ field.step + 0.5) * field.step
if fields["btn_".. field.name .."_dec"] then
positioning[field.name] = value - field.step
if field.min and positioning[field.name] < field.min then
positioning[field.name] = field.min
end
end
if fields["btn_".. field.name .."_inc"] then
positioning[field.name] = value + field.step
if field.max and positioning[field.name] > field.max then
positioning[field.name] = field.max
end
end
end
set_player_positioning(player_name, positioning)
show_formspec(player_name)
end)
minetest.register_chatcommand("devpos",
{
params = "",
description = "Open dev positioning formspec",
privs = { server=true },
func = function(player_name, param)
show_formspec(player_name)
return true
end
})