mirror of
https://github.com/minetest/minetest.git
synced 2024-11-23 16:13:46 +01:00
Add relative numbers for commands by prepending ~ (#9588)
* Add relative numbers for commands by prepending ~ * Some builtin code cleanup * Disallow nan and inf in minetest.string_to_area * Remove unused local variable teleportee (makes Luacheck happy) * Clean up core.string_to_pos * Make area parsing less permissive * Rewrite tests as busted tests * /time: Fix negative minutes not working Co-authored-by: Lars Mueller <appgurulars@gmx.de>
This commit is contained in:
parent
9f338f5a56
commit
ac5e8176b9
@ -425,54 +425,50 @@ function core.string_to_pos(value)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local x, y, z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
value = value:match("^%((.-)%)$") or value -- strip parentheses
|
||||||
if x and y and z then
|
|
||||||
x = tonumber(x)
|
local x, y, z = value:trim():match("^([%d.-]+)[,%s]%s*([%d.-]+)[,%s]%s*([%d.-]+)$")
|
||||||
y = tonumber(y)
|
|
||||||
z = tonumber(z)
|
|
||||||
return vector.new(x, y, z)
|
|
||||||
end
|
|
||||||
x, y, z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
|
|
||||||
if x and y and z then
|
if x and y and z then
|
||||||
x = tonumber(x)
|
x = tonumber(x)
|
||||||
y = tonumber(y)
|
y = tonumber(y)
|
||||||
z = tonumber(z)
|
z = tonumber(z)
|
||||||
return vector.new(x, y, z)
|
return vector.new(x, y, z)
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function core.string_to_area(value)
|
|
||||||
local p1, p2 = unpack(value:split(") ("))
|
do
|
||||||
if p1 == nil or p2 == nil then
|
local rel_num_cap = "(~?-?%d*%.?%d*)" -- may be overly permissive as this will be tonumber'ed anyways
|
||||||
return nil
|
local num_delim = "[,%s]%s*"
|
||||||
|
local pattern = "^" .. table.concat({rel_num_cap, rel_num_cap, rel_num_cap}, num_delim) .. "$"
|
||||||
|
|
||||||
|
local function parse_area_string(pos, relative_to)
|
||||||
|
local pp = {}
|
||||||
|
pp.x, pp.y, pp.z = pos:trim():match(pattern)
|
||||||
|
return core.parse_coordinates(pp.x, pp.y, pp.z, relative_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
p1 = core.string_to_pos(p1 .. ")")
|
function core.string_to_area(value, relative_to)
|
||||||
p2 = core.string_to_pos("(" .. p2)
|
local p1, p2 = value:match("^%((.-)%)%s*%((.-)%)$")
|
||||||
|
if not p1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
p1 = parse_area_string(p1, relative_to)
|
||||||
|
p2 = parse_area_string(p2, relative_to)
|
||||||
|
|
||||||
if p1 == nil or p2 == nil then
|
if p1 == nil or p2 == nil then
|
||||||
return nil
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
return p1, p2
|
return p1, p2
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function test_string_to_area()
|
|
||||||
local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2, 4, -12.53)")
|
|
||||||
assert(p1.x == 10.0 and p1.y == 5 and p1.z == -2)
|
|
||||||
assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53)
|
|
||||||
|
|
||||||
p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53")
|
|
||||||
assert(p1 == nil and p2 == nil)
|
|
||||||
|
|
||||||
p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53")
|
|
||||||
assert(p1 == nil and p2 == nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
test_string_to_area()
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function table.copy(t, seen)
|
function table.copy(t, seen)
|
||||||
local n = {}
|
local n = {}
|
||||||
@ -701,3 +697,71 @@ end
|
|||||||
function core.is_nan(number)
|
function core.is_nan(number)
|
||||||
return number ~= number
|
return number ~= number
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[ Helper function for parsing an optionally relative number
|
||||||
|
of a chat command parameter, using the chat command tilde notation.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
* arg: String snippet containing the number; possible values:
|
||||||
|
* "<number>": return as number
|
||||||
|
* "~<number>": return relative_to + <number>
|
||||||
|
* "~": return relative_to
|
||||||
|
* Anything else will return `nil`
|
||||||
|
* relative_to: Number to which the `arg` number might be relative to
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A number or `nil`, depending on `arg.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
* `core.parse_relative_number("5", 10)` returns 5
|
||||||
|
* `core.parse_relative_number("~5", 10)` returns 15
|
||||||
|
* `core.parse_relative_number("~", 10)` returns 10
|
||||||
|
]]
|
||||||
|
function core.parse_relative_number(arg, relative_to)
|
||||||
|
if not arg then
|
||||||
|
return nil
|
||||||
|
elseif arg == "~" then
|
||||||
|
return relative_to
|
||||||
|
elseif string.sub(arg, 1, 1) == "~" then
|
||||||
|
local number = tonumber(string.sub(arg, 2))
|
||||||
|
if not number then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if core.is_nan(number) or number == math.huge or number == -math.huge then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return relative_to + number
|
||||||
|
else
|
||||||
|
local number = tonumber(arg)
|
||||||
|
if core.is_nan(number) or number == math.huge or number == -math.huge then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return number
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[ Helper function to parse coordinates that might be relative
|
||||||
|
to another position; supports chat command tilde notation.
|
||||||
|
Intended to be used in chat command parameter parsing.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
* x, y, z: Parsed x, y, and z coordinates as strings
|
||||||
|
* relative_to: Position to which to compare the position
|
||||||
|
|
||||||
|
Syntax of x, y and z:
|
||||||
|
* "<number>": return as number
|
||||||
|
* "~<number>": return <number> + player position on this axis
|
||||||
|
* "~": return player position on this axis
|
||||||
|
|
||||||
|
Returns: a vector or nil for invalid input or if player does not exist
|
||||||
|
]]
|
||||||
|
function core.parse_coordinates(x, y, z, relative_to)
|
||||||
|
if not relative_to then
|
||||||
|
x, y, z = tonumber(x), tonumber(y), tonumber(z)
|
||||||
|
return x and y and z and { x = x, y = y, z = z }
|
||||||
|
end
|
||||||
|
local rx = core.parse_relative_number(x, relative_to.x)
|
||||||
|
local ry = core.parse_relative_number(y, relative_to.y)
|
||||||
|
local rz = core.parse_relative_number(z, relative_to.z)
|
||||||
|
return rx and ry and rz and { x = rx, y = ry, z = rz }
|
||||||
|
end
|
||||||
|
@ -67,6 +67,96 @@ describe("pos", function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("area parsing", function()
|
||||||
|
describe("valid inputs", function()
|
||||||
|
it("accepts absolute numbers", function()
|
||||||
|
local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2 4 -12.53)")
|
||||||
|
assert(p1.x == 10 and p1.y == 5 and p1.z == -2)
|
||||||
|
assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("accepts relative numbers", function()
|
||||||
|
local p1, p2 = core.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})
|
||||||
|
assert(type(p1) == "table" and type(p2) == "table")
|
||||||
|
assert(p1.x == 1 and p1.y == 2 and p1.z == 3)
|
||||||
|
assert(p2.x == 15 and p2.y == 5 and p2.z == 10)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(1 2 3) (~5 ~-5 ~)", {x=10,y=10,z=10})
|
||||||
|
assert(type(p1) == "table" and type(p2) == "table")
|
||||||
|
assert(p1.x == 1 and p1.y == 2 and p1.z == 3)
|
||||||
|
assert(p2.x == 15 and p2.y == 5 and p2.z == 10)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
describe("invalid inputs", function()
|
||||||
|
it("rejects too few numbers", function()
|
||||||
|
local p1, p2 = core.string_to_area("(1,1) (1,1,1,1)", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("rejects too many numbers", function()
|
||||||
|
local p1, p2 = core.string_to_area("(1,1,1,1) (1,1,1,1)", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("rejects nan & inf", function()
|
||||||
|
local p1, p2 = core.string_to_area("(1,1,1) (1,1,nan)", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(1,1,1) (1,1,~nan)", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(1,1,1) (1,~nan,1)", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(1,1,1) (1,1,inf)", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(1,1,1) (1,1,~inf)", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(1,1,1) (1,~inf,1)", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)")
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)")
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("rejects words", function()
|
||||||
|
local p1, p2 = core.string_to_area("bananas", {x=1,y=1,z=1})
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("bananas", "foobar")
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("bananas")
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(bananas,bananas,bananas)")
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(bananas,bananas,bananas) (bananas,bananas,bananas)")
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("requires parenthesis & valid numbers", function()
|
||||||
|
local p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53")
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
|
||||||
|
p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53")
|
||||||
|
assert(p1 == nil and p2 == nil)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
describe("table", function()
|
describe("table", function()
|
||||||
it("indexof()", function()
|
it("indexof()", function()
|
||||||
assert.equal(1, table.indexof({"foo", "bar"}, "foo"))
|
assert.equal(1, table.indexof({"foo", "bar"}, "foo"))
|
||||||
|
@ -130,8 +130,13 @@ local function parse_range_str(player_name, str)
|
|||||||
return false, S("Unable to get position of player @1.", player_name)
|
return false, S("Unable to get position of player @1.", player_name)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
p1, p2 = core.string_to_area(str)
|
local player = core.get_player_by_name(player_name)
|
||||||
if p1 == nil then
|
local relpos
|
||||||
|
if player then
|
||||||
|
relpos = player:get_pos()
|
||||||
|
end
|
||||||
|
p1, p2 = core.string_to_area(str, relpos)
|
||||||
|
if p1 == nil or p2 == nil then
|
||||||
return false, S("Incorrect area format. "
|
return false, S("Incorrect area format. "
|
||||||
.. "Expected: (x1,y1,z1) (x2,y2,z2)")
|
.. "Expected: (x1,y1,z1) (x2,y2,z2)")
|
||||||
end
|
end
|
||||||
@ -570,10 +575,15 @@ core.register_chatcommand("teleport", {
|
|||||||
description = S("Teleport to position or player"),
|
description = S("Teleport to position or player"),
|
||||||
privs = {teleport=true},
|
privs = {teleport=true},
|
||||||
func = function(name, param)
|
func = function(name, param)
|
||||||
|
local player = core.get_player_by_name(name)
|
||||||
|
local relpos
|
||||||
|
if player then
|
||||||
|
relpos = player:get_pos()
|
||||||
|
end
|
||||||
local p = {}
|
local p = {}
|
||||||
p.x, p.y, p.z = param:match("^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
p.x, p.y, p.z = string.match(param, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$")
|
||||||
p = vector.apply(p, tonumber)
|
p = core.parse_coordinates(p.x, p.y, p.z, relpos)
|
||||||
if p.x and p.y and p.z then
|
if p and p.x and p.y and p.z then
|
||||||
return teleport_to_pos(name, p)
|
return teleport_to_pos(name, p)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -587,9 +597,19 @@ core.register_chatcommand("teleport", {
|
|||||||
"other players (missing privilege: @1).", "bring")
|
"other players (missing privilege: @1).", "bring")
|
||||||
|
|
||||||
local teleportee_name
|
local teleportee_name
|
||||||
|
p = {}
|
||||||
teleportee_name, p.x, p.y, p.z = param:match(
|
teleportee_name, p.x, p.y, p.z = param:match(
|
||||||
"^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
|
"^([^ ]+) +([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$")
|
||||||
|
if teleportee_name then
|
||||||
|
local teleportee = core.get_player_by_name(teleportee_name)
|
||||||
|
if not teleportee then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
relpos = teleportee:get_pos()
|
||||||
|
p = core.parse_coordinates(p.x, p.y, p.z, relpos)
|
||||||
|
end
|
||||||
p = vector.apply(p, tonumber)
|
p = vector.apply(p, tonumber)
|
||||||
|
|
||||||
if teleportee_name and p.x and p.y and p.z then
|
if teleportee_name and p.x and p.y and p.z then
|
||||||
if not has_bring_priv then
|
if not has_bring_priv then
|
||||||
return false, missing_bring_msg
|
return false, missing_bring_msg
|
||||||
@ -842,7 +862,7 @@ core.register_chatcommand("spawnentity", {
|
|||||||
description = S("Spawn entity at given (or your) position"),
|
description = S("Spawn entity at given (or your) position"),
|
||||||
privs = {give=true, interact=true},
|
privs = {give=true, interact=true},
|
||||||
func = function(name, param)
|
func = function(name, param)
|
||||||
local entityname, p = string.match(param, "^([^ ]+) *(.*)$")
|
local entityname, pstr = string.match(param, "^([^ ]+) *(.*)$")
|
||||||
if not entityname then
|
if not entityname then
|
||||||
return false, S("EntityName required.")
|
return false, S("EntityName required.")
|
||||||
end
|
end
|
||||||
@ -856,11 +876,15 @@ core.register_chatcommand("spawnentity", {
|
|||||||
if not core.registered_entities[entityname] then
|
if not core.registered_entities[entityname] then
|
||||||
return false, S("Cannot spawn an unknown entity.")
|
return false, S("Cannot spawn an unknown entity.")
|
||||||
end
|
end
|
||||||
if p == "" then
|
local p
|
||||||
|
if pstr == "" then
|
||||||
p = player:get_pos()
|
p = player:get_pos()
|
||||||
else
|
else
|
||||||
p = core.string_to_pos(p)
|
p = {}
|
||||||
if p == nil then
|
p.x, p.y, p.z = string.match(pstr, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$")
|
||||||
|
local relpos = player:get_pos()
|
||||||
|
p = core.parse_coordinates(p.x, p.y, p.z, relpos)
|
||||||
|
if not (p and p.x and p.y and p.z) then
|
||||||
return false, S("Invalid parameters (@1).", param)
|
return false, S("Invalid parameters (@1).", param)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1019,6 +1043,13 @@ core.register_chatcommand("status", {
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
local function get_time(timeofday)
|
||||||
|
local time = math.floor(timeofday * 1440)
|
||||||
|
local minute = time % 60
|
||||||
|
local hour = (time - minute) / 60
|
||||||
|
return time, hour, minute
|
||||||
|
end
|
||||||
|
|
||||||
core.register_chatcommand("time", {
|
core.register_chatcommand("time", {
|
||||||
params = S("[<0..23>:<0..59> | <0..24000>]"),
|
params = S("[<0..23>:<0..59> | <0..24000>]"),
|
||||||
description = S("Show or set time of day"),
|
description = S("Show or set time of day"),
|
||||||
@ -1037,9 +1068,14 @@ core.register_chatcommand("time", {
|
|||||||
return false, S("You don't have permission to run "
|
return false, S("You don't have permission to run "
|
||||||
.. "this command (missing privilege: @1).", "settime")
|
.. "this command (missing privilege: @1).", "settime")
|
||||||
end
|
end
|
||||||
local hour, minute = param:match("^(%d+):(%d+)$")
|
local relative, negative, hour, minute = param:match("^(~?)(%-?)(%d+):(%d+)$")
|
||||||
if not hour then
|
if not relative then -- checking the first capture against nil suffices
|
||||||
local new_time = tonumber(param) or -1
|
local new_time = core.parse_relative_number(param, core.get_timeofday() * 24000)
|
||||||
|
if not new_time then
|
||||||
|
new_time = tonumber(param) or -1
|
||||||
|
else
|
||||||
|
new_time = new_time % 24000
|
||||||
|
end
|
||||||
if new_time ~= new_time or new_time < 0 or new_time > 24000 then
|
if new_time ~= new_time or new_time < 0 or new_time > 24000 then
|
||||||
return false, S("Invalid time (must be between 0 and 24000).")
|
return false, S("Invalid time (must be between 0 and 24000).")
|
||||||
end
|
end
|
||||||
@ -1047,14 +1083,29 @@ core.register_chatcommand("time", {
|
|||||||
core.log("action", name .. " sets time to " .. new_time)
|
core.log("action", name .. " sets time to " .. new_time)
|
||||||
return true, S("Time of day changed.")
|
return true, S("Time of day changed.")
|
||||||
end
|
end
|
||||||
|
local new_time
|
||||||
hour = tonumber(hour)
|
hour = tonumber(hour)
|
||||||
minute = tonumber(minute)
|
minute = tonumber(minute)
|
||||||
|
if relative == "" then
|
||||||
if hour < 0 or hour > 23 then
|
if hour < 0 or hour > 23 then
|
||||||
return false, S("Invalid hour (must be between 0 and 23 inclusive).")
|
return false, S("Invalid hour (must be between 0 and 23 inclusive).")
|
||||||
elseif minute < 0 or minute > 59 then
|
elseif minute < 0 or minute > 59 then
|
||||||
return false, S("Invalid minute (must be between 0 and 59 inclusive).")
|
return false, S("Invalid minute (must be between 0 and 59 inclusive).")
|
||||||
end
|
end
|
||||||
core.set_timeofday((hour * 60 + minute) / 1440)
|
new_time = (hour * 60 + minute) / 1440
|
||||||
|
else
|
||||||
|
if minute < 0 or minute > 59 then
|
||||||
|
return false, S("Invalid minute (must be between 0 and 59 inclusive).")
|
||||||
|
end
|
||||||
|
local current_time = core.get_timeofday()
|
||||||
|
if negative == "-" then -- negative time
|
||||||
|
hour, minute = -hour, -minute
|
||||||
|
end
|
||||||
|
new_time = (current_time + (hour * 60 + minute) / 1440) % 1
|
||||||
|
local _
|
||||||
|
_, hour, minute = get_time(new_time)
|
||||||
|
end
|
||||||
|
core.set_timeofday(new_time)
|
||||||
core.log("action", ("%s sets time to %d:%02d"):format(name, hour, minute))
|
core.log("action", ("%s sets time to %d:%02d"):format(name, hour, minute))
|
||||||
return true, S("Time of day changed.")
|
return true, S("Time of day changed.")
|
||||||
end,
|
end,
|
||||||
|
@ -3550,8 +3550,16 @@ Helper functions
|
|||||||
* `minetest.string_to_pos(string)`: returns a position or `nil`
|
* `minetest.string_to_pos(string)`: returns a position or `nil`
|
||||||
* Same but in reverse.
|
* Same but in reverse.
|
||||||
* If the string can't be parsed to a position, nothing is returned.
|
* If the string can't be parsed to a position, nothing is returned.
|
||||||
* `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)")`: returns two positions
|
* `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)", relative_to)`:
|
||||||
|
* returns two positions
|
||||||
* Converts a string representing an area box into two positions
|
* Converts a string representing an area box into two positions
|
||||||
|
* X1, Y1, ... Z2 are coordinates
|
||||||
|
* `relative_to`: Optional. If set to a position, each coordinate
|
||||||
|
can use the tilde notation for relative positions
|
||||||
|
* Tilde notation: "~": Relative coordinate
|
||||||
|
"~<number>": Relative coordinate plus <number>
|
||||||
|
* Example: `minetest.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})`
|
||||||
|
returns `{x=1,y=2,z=3}, {x=15,y=5,z=10}`
|
||||||
* `minetest.formspec_escape(string)`: returns a string
|
* `minetest.formspec_escape(string)`: returns a string
|
||||||
* escapes the characters "[", "]", "\", "," and ";", which can not be used
|
* escapes the characters "[", "]", "\", "," and ";", which can not be used
|
||||||
in formspecs.
|
in formspecs.
|
||||||
|
Loading…
Reference in New Issue
Block a user