dev_positioning/init.lua
2020-08-21 17:50:29 +02:00

283 lines
8.2 KiB
Lua

--[[
MIT License
Copyright (c) 2020 Pierre-Yves Rollo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
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
})