mods that hopefully work for a TechAge-based pack
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
local S = minetest.get_translator("worldedit_commands")
|
||||
|
||||
local function check_region(name)
|
||||
return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
|
||||
end
|
||||
|
||||
|
||||
worldedit.register_command("lua", {
|
||||
params = "<code>",
|
||||
description = S("Executes <code> as a Lua chunk in the global namespace"),
|
||||
category = S("Code"),
|
||||
privs = {worldedit=true, server=true},
|
||||
parse = function(param)
|
||||
if param == "" then
|
||||
return false
|
||||
end
|
||||
return true, param
|
||||
end,
|
||||
func = function(name, param)
|
||||
-- shorthand like in the Lua interpreter
|
||||
if param:sub(1, 1) == "=" then
|
||||
param = "return " .. param:sub(2)
|
||||
end
|
||||
local err, ret = worldedit.lua(param, name)
|
||||
if err == nil then
|
||||
minetest.log("action", name .. " executed " .. param)
|
||||
if ret ~= "nil" then
|
||||
worldedit.player_notify(name, "code successfully executed, returned " .. ret, "info")
|
||||
else
|
||||
worldedit.player_notify(name, "code successfully executed", "ok")
|
||||
end
|
||||
else
|
||||
minetest.log("action", name .. " tried to execute " .. param)
|
||||
worldedit.player_notify(name, "code error: " .. err, "error")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("luatransform", {
|
||||
params = "<code>",
|
||||
description = S("Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region"),
|
||||
category = S("Code"),
|
||||
privs = {worldedit=true, server=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
return true, param
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, param)
|
||||
local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)
|
||||
if err then
|
||||
worldedit.player_notify(name, "code error: " .. err, "error")
|
||||
minetest.log("action", name.." tried to execute luatransform "..param)
|
||||
else
|
||||
worldedit.player_notify(name, "code successfully executed", "ok")
|
||||
minetest.log("action", name.." executed luatransform "..param)
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,239 @@
|
||||
local S = minetest.get_translator("worldedit_commands")
|
||||
|
||||
local VALID_DIR = worldedit.valid_directions
|
||||
|
||||
worldedit.register_command("outset", {
|
||||
params = "[h/v] <amount>",
|
||||
description = S("Outset the selected region."),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local find, _, dir, amount = param:find("^(%a*)%s+([+-]?%d+)$")
|
||||
if find == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
local hv_test = dir:find("[^hv]+")
|
||||
if hv_test ~= nil then
|
||||
return false, S("Invalid direction: @1", dir)
|
||||
end
|
||||
|
||||
return true, dir, tonumber(amount)
|
||||
end,
|
||||
func = function(name, dir, amount)
|
||||
if dir == "" or dir == "hv" or dir == "vh" then
|
||||
assert(worldedit.cuboid_volumetric_expand(name, amount))
|
||||
elseif dir == "h" then
|
||||
assert(worldedit.cuboid_linear_expand(name, 'x', 1, amount))
|
||||
assert(worldedit.cuboid_linear_expand(name, 'x', -1, amount))
|
||||
assert(worldedit.cuboid_linear_expand(name, 'z', 1, amount))
|
||||
assert(worldedit.cuboid_linear_expand(name, 'z', -1, amount))
|
||||
elseif dir == "v" then
|
||||
assert(worldedit.cuboid_linear_expand(name, 'y', 1, amount))
|
||||
assert(worldedit.cuboid_linear_expand(name, 'y', -1, amount))
|
||||
else
|
||||
return false, S("Invalid number of arguments")
|
||||
end
|
||||
|
||||
worldedit.marker_update(name)
|
||||
return true, S("Region outset by @1 nodes", amount)
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
worldedit.register_command("inset", {
|
||||
params = "[h/v] <amount>",
|
||||
description = S("Inset the selected region."),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local find, _, dir, amount = param:find("^(%a*)%s+([+-]?%d+)$")
|
||||
if find == nil then
|
||||
return false
|
||||
end
|
||||
if dir:find("[^hv]") ~= nil then
|
||||
return false, S("Invalid direction: @1", dir)
|
||||
end
|
||||
|
||||
return true, dir, tonumber(amount)
|
||||
end,
|
||||
func = function(name, dir, amount)
|
||||
if dir == "" or dir == "vh" or dir == "hv" then
|
||||
assert(worldedit.cuboid_volumetric_expand(name, -amount))
|
||||
elseif dir == "h" then
|
||||
assert(worldedit.cuboid_linear_expand(name, 'x', 1, -amount))
|
||||
assert(worldedit.cuboid_linear_expand(name, 'x', -1, -amount))
|
||||
assert(worldedit.cuboid_linear_expand(name, 'z', 1, -amount))
|
||||
assert(worldedit.cuboid_linear_expand(name, 'z', -1, -amount))
|
||||
elseif dir == "v" then
|
||||
assert(worldedit.cuboid_linear_expand(name, 'y', 1, -amount))
|
||||
assert(worldedit.cuboid_linear_expand(name, 'y', -1, -amount))
|
||||
else
|
||||
return false, S("Invalid number of arguments")
|
||||
end
|
||||
|
||||
worldedit.marker_update(name)
|
||||
return true, S("Region inset by @1 nodes", amount)
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
worldedit.register_command("shift", {
|
||||
params = tostring(VALID_DIR) .. " [+/-]<amount>",
|
||||
description = S("Shifts the selection area without moving its contents"),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local find, _, direction, amount = param:find("^([^%s]+)%s+([+-]?%d+)$")
|
||||
if find == nil or not VALID_DIR[direction] then
|
||||
return false
|
||||
end
|
||||
|
||||
return true, direction, tonumber(amount)
|
||||
end,
|
||||
func = function(name, direction, amount)
|
||||
local axis, dir = worldedit.player_direction(name, direction)
|
||||
if axis == nil or dir == nil then
|
||||
return false, S("Invalid if looking straight up or down")
|
||||
end
|
||||
|
||||
assert(worldedit.cuboid_shift(name, axis, amount * dir))
|
||||
worldedit.marker_update(name)
|
||||
|
||||
return true, S("Region shifted by @1 nodes", amount)
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
worldedit.register_command("expand", {
|
||||
params = "[+/-]" .. tostring(VALID_DIR) .. " <amount> [reverse amount]",
|
||||
description = S("Expands the selection in the selected absolute or relative axis"),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local find, _, sign, direction, amount,
|
||||
rev_amount = param:find("^([+-]?)([^%s]+)%s+(%d+)%s*(%d*)$")
|
||||
if find == nil or not VALID_DIR[direction] then
|
||||
return false
|
||||
end
|
||||
|
||||
if rev_amount == "" then
|
||||
rev_amount = "0"
|
||||
end
|
||||
|
||||
return true, sign, direction, tonumber(amount), tonumber(rev_amount)
|
||||
end,
|
||||
func = function(name, sign, direction, amount, rev_amount)
|
||||
local axis, dir = worldedit.player_direction(name, direction)
|
||||
if axis == nil or dir == nil then
|
||||
return false, S("Invalid if looking straight up or down")
|
||||
end
|
||||
if sign == "-" then
|
||||
dir = -dir
|
||||
end
|
||||
|
||||
worldedit.cuboid_linear_expand(name, axis, dir, amount)
|
||||
worldedit.cuboid_linear_expand(name, axis, -dir, rev_amount)
|
||||
worldedit.marker_update(name)
|
||||
return true, S("Region expanded by @1 nodes", amount + rev_amount)
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
worldedit.register_command("contract", {
|
||||
params = "[+/-]" .. tostring(VALID_DIR) .. " [reverse amount]",
|
||||
description = S("Contracts the selection in the selected absolute or relative axis"),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local find, _, sign, direction, amount,
|
||||
rev_amount = param:find("^([+-]?)([^%s]+)%s+(%d+)%s*(%d*)$")
|
||||
if find == nil or not VALID_DIR[direction] then
|
||||
return false
|
||||
end
|
||||
|
||||
if rev_amount == "" then
|
||||
rev_amount = "0"
|
||||
end
|
||||
|
||||
return true, sign, direction, tonumber(amount), tonumber(rev_amount)
|
||||
end,
|
||||
func = function(name, sign, direction, amount, rev_amount)
|
||||
local axis, dir = worldedit.player_direction(name, direction)
|
||||
if axis == nil or dir == nil then
|
||||
return false, S("Invalid if looking straight up or down")
|
||||
end
|
||||
if sign == "-" then
|
||||
dir = -dir
|
||||
end
|
||||
|
||||
worldedit.cuboid_linear_expand(name, axis, dir, -amount)
|
||||
worldedit.cuboid_linear_expand(name, axis, -dir, -rev_amount)
|
||||
worldedit.marker_update(name)
|
||||
return true, S("Region contracted by @1 nodes", amount + rev_amount)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("cubeapply", {
|
||||
params = "<size>/(<sizex> <sizey> <sizez>) <command> [parameters]",
|
||||
description = S("Select a cube with side length <size> around position 1 and run <command> on region"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = function(param)
|
||||
local found, _, sidex, sidey, sidez, cmd, args =
|
||||
param:find("^(%d+)%s+(%d+)%s+(%d+)%s+([^%s]+)%s*(.*)$")
|
||||
if found == nil then
|
||||
found, _, sidex, cmd, args = param:find("^(%d+)%s+([^%s]+)%s*(.*)$")
|
||||
if found == nil then
|
||||
return false
|
||||
end
|
||||
sidey = sidex
|
||||
sidez = sidex
|
||||
end
|
||||
sidex = tonumber(sidex)
|
||||
sidey = tonumber(sidey)
|
||||
sidez = tonumber(sidez)
|
||||
if sidex < 1 or sidey < 1 or sidez < 1 then
|
||||
return false
|
||||
end
|
||||
local cmddef = worldedit.registered_commands[cmd]
|
||||
if cmddef == nil or cmddef.require_pos ~= 2 then
|
||||
return false, S("invalid usage: @1 cannot be used with cubeapply",
|
||||
minetest.colorize("#0ff", "//"..cmd))
|
||||
end
|
||||
-- run parsing of target command
|
||||
local parsed = {cmddef.parse(args)}
|
||||
if not table.remove(parsed, 1) then
|
||||
return false, parsed[1]
|
||||
end
|
||||
return true, sidex, sidey, sidez, cmd, parsed
|
||||
end,
|
||||
nodes_needed = function(name, sidex, sidey, sidez, cmd, parsed)
|
||||
-- its not possible to defer to the target command at this point
|
||||
-- FIXME: why not?
|
||||
return sidex * sidey * sidez
|
||||
end,
|
||||
func = function(name, sidex, sidey, sidez, cmd, parsed)
|
||||
local cmddef = assert(worldedit.registered_commands[cmd])
|
||||
local success, missing_privs = minetest.check_player_privs(name, cmddef.privs)
|
||||
if not success then
|
||||
return false, S("Missing privileges: @1", table.concat(missing_privs, ", "))
|
||||
end
|
||||
|
||||
-- update region to be the cuboid the user wanted
|
||||
local half = vector.divide(vector.new(sidex, sidey, sidez), 2)
|
||||
local sizea, sizeb = vector.apply(half, math.floor), vector.apply(half, math.ceil)
|
||||
local center = worldedit.pos1[name]
|
||||
worldedit.pos1[name] = vector.subtract(center, sizea)
|
||||
worldedit.pos2[name] = vector.add(center, vector.subtract(sizeb, 1))
|
||||
worldedit.marker_update(name)
|
||||
|
||||
-- actually run target command
|
||||
return cmddef.func(name, unpack(parsed))
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,152 @@
|
||||
-- Moves the location of a single marker by adding deltavector
|
||||
local function marker_move(name, marker, deltavector)
|
||||
if marker == 1 then
|
||||
local pos = worldedit.pos1[name]
|
||||
worldedit.pos1[name] = vector.add(deltavector, pos)
|
||||
elseif marker == 2 then
|
||||
local pos = worldedit.pos2[name]
|
||||
worldedit.pos2[name] = vector.add(deltavector, pos)
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- Returns two vectors with the directions for volumetric expansion
|
||||
local function get_expansion_directions(mark1, mark2)
|
||||
assert(mark1 and mark2)
|
||||
local dir1 = vector.new()
|
||||
local dir2 = vector.new()
|
||||
|
||||
if mark1.x < mark2.x then
|
||||
dir1.x = -1
|
||||
dir2.x = 1
|
||||
else
|
||||
dir1.x = 1
|
||||
dir2.x = -1
|
||||
end
|
||||
if mark1.y < mark2.y then
|
||||
dir1.y = -1
|
||||
dir2.y = 1
|
||||
else
|
||||
dir1.y = 1
|
||||
dir2.y = -1
|
||||
end
|
||||
if mark1.z < mark2.z then
|
||||
dir1.z = -1
|
||||
dir2.z = 1
|
||||
else
|
||||
dir1.z = 1
|
||||
dir2.z = -1
|
||||
end
|
||||
return dir1, dir2
|
||||
end
|
||||
|
||||
|
||||
-- Returns the closest marker to the specified axis and direction
|
||||
local function marker_get_closest_to_axis(name, axis, direction)
|
||||
assert(direction == 1 or direction == -1)
|
||||
local pos1 = worldedit.pos1[name]
|
||||
local pos2 = worldedit.pos2[name]
|
||||
|
||||
if axis == "x" then
|
||||
if pos1.x * direction > pos2.x * direction then
|
||||
return 1
|
||||
else
|
||||
return 2
|
||||
end
|
||||
elseif axis == "y" then
|
||||
if pos1.y * direction > pos2.y * direction then
|
||||
return 1
|
||||
else
|
||||
return 2
|
||||
end
|
||||
elseif axis == "z" then
|
||||
if pos1.z * direction > pos2.z * direction then
|
||||
return 1
|
||||
else
|
||||
return 2
|
||||
end
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Expands or contracts the cuboid in all axes by amount (positive or negative)
|
||||
worldedit.cuboid_volumetric_expand = function(name, amount)
|
||||
local pos1 = worldedit.pos1[name]
|
||||
local pos2 = worldedit.pos2[name]
|
||||
|
||||
if pos1 == nil or pos2 == nil then
|
||||
return false, "Undefined cuboid"
|
||||
end
|
||||
|
||||
local delta1 = vector.new(amount, amount, amount)
|
||||
local delta2 = vector.new(amount, amount, amount)
|
||||
local delta_dir1, delta_dir2 = get_expansion_directions(pos1, pos2)
|
||||
delta1 = vector.multiply(delta1, delta_dir1)
|
||||
delta2 = vector.multiply(delta2, delta_dir2)
|
||||
worldedit.pos1[name] = vector.add(pos1, delta1)
|
||||
worldedit.pos2[name] = vector.add(pos2, delta2)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- Expands or contracts the cuboid in a single axis by amount (positive or negative)
|
||||
worldedit.cuboid_linear_expand = function(name, axis, direction, amount)
|
||||
local pos1 = worldedit.pos1[name]
|
||||
local pos2 = worldedit.pos2[name]
|
||||
|
||||
if pos1 == nil or pos2 == nil then
|
||||
return false, "undefined cuboid"
|
||||
end
|
||||
|
||||
if direction ~= 1 and direction ~= -1 then
|
||||
return false, "invalid marker"
|
||||
end
|
||||
|
||||
local marker = marker_get_closest_to_axis(name, axis, direction)
|
||||
local deltavect = vector.new()
|
||||
|
||||
if axis == "x" then
|
||||
deltavect.x = amount * direction
|
||||
elseif axis == "y" then
|
||||
deltavect.y = amount * direction
|
||||
elseif axis == "z" then
|
||||
deltavect.z = amount * direction
|
||||
else
|
||||
return false, "invalid axis"
|
||||
end
|
||||
|
||||
marker_move(name, marker, deltavect)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- Shifts the cuboid by '+-amount' in axis 'axis'
|
||||
worldedit.cuboid_shift = function(name, axis, amount)
|
||||
local pos1 = worldedit.pos1[name]
|
||||
local pos2 = worldedit.pos2[name]
|
||||
|
||||
if pos1 == nil or pos2 == nil then
|
||||
return false, "undefined cuboid"
|
||||
end
|
||||
|
||||
local delta = vector.new()
|
||||
if axis == "x" then
|
||||
delta.x = amount
|
||||
elseif axis == "y" then
|
||||
delta.y = amount
|
||||
elseif axis == "z" then
|
||||
delta.z = amount
|
||||
else
|
||||
return false, "invalid axis"
|
||||
end
|
||||
|
||||
worldedit.pos1[name] = vector.add(pos1, delta)
|
||||
worldedit.pos2[name] = vector.add(pos2, delta)
|
||||
return true
|
||||
end
|
||||
@@ -0,0 +1,407 @@
|
||||
local S = minetest.get_translator("worldedit_commands")
|
||||
|
||||
minetest.register_privilege("worldedit", S("Can use WorldEdit commands"))
|
||||
|
||||
worldedit.pos1 = {}
|
||||
worldedit.pos2 = {}
|
||||
|
||||
|
||||
local safe_region, reset_pending = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua")
|
||||
|
||||
worldedit.registered_commands = {}
|
||||
|
||||
local function copy_state(which, name)
|
||||
if which == 0 then
|
||||
return {}
|
||||
elseif which == 1 then
|
||||
return {
|
||||
worldedit.pos1[name] and vector.copy(worldedit.pos1[name])
|
||||
}
|
||||
else
|
||||
return {
|
||||
worldedit.pos1[name] and vector.copy(worldedit.pos1[name]),
|
||||
worldedit.pos2[name] and vector.copy(worldedit.pos2[name])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function compare_state(state, old_state)
|
||||
for i, v in ipairs(state) do
|
||||
if not (v == nil and old_state[i] == nil) and not vector.equals(v, old_state[i]) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function chatcommand_handler(cmd_name, name, param)
|
||||
local def = assert(worldedit.registered_commands[cmd_name])
|
||||
|
||||
if def.require_pos == 2 then
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
if pos1 == nil or pos2 == nil then
|
||||
worldedit.player_notify(name, S("no region selected"), "error")
|
||||
return
|
||||
end
|
||||
elseif def.require_pos == 1 then
|
||||
local pos1 = worldedit.pos1[name]
|
||||
if pos1 == nil then
|
||||
worldedit.player_notify(name, S("no position 1 selected"), "error")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
param = param:trim()
|
||||
local parsed = {def.parse(param)}
|
||||
local success = table.remove(parsed, 1)
|
||||
if not success then
|
||||
worldedit.player_notify(name, parsed[1] or S("invalid usage"), "error")
|
||||
return
|
||||
end
|
||||
|
||||
local run = function()
|
||||
local ok, msg = def.func(name, unpack(parsed))
|
||||
if msg then
|
||||
worldedit.player_notify(name, msg, ok and "ok" or "error")
|
||||
end
|
||||
end
|
||||
|
||||
if not def.nodes_needed then
|
||||
-- no safe region check
|
||||
run()
|
||||
return
|
||||
end
|
||||
|
||||
local count = def.nodes_needed(name, unpack(parsed))
|
||||
local old_state = copy_state(def.require_pos, name)
|
||||
safe_region(name, count, function()
|
||||
local state = copy_state(def.require_pos, name)
|
||||
if not compare_state(state, old_state) then
|
||||
worldedit.player_notify(name, S("ERROR: the operation was cancelled because the region has changed."), "error")
|
||||
return
|
||||
end
|
||||
|
||||
run()
|
||||
end)
|
||||
end
|
||||
|
||||
-- Registers a chatcommand for WorldEdit
|
||||
-- name = "about" -- Name of the chat command (without any /)
|
||||
-- def = {
|
||||
-- privs = {}, -- Privileges needed
|
||||
-- params = "", -- Human readable parameter list (optional)
|
||||
-- -- if params = "" then a parse() implementation will automatically be provided
|
||||
-- description = "", -- Description
|
||||
-- category = "", -- Category of the command (optional)
|
||||
-- require_pos = 0, -- Number of positions required to be set (optional)
|
||||
-- parse = function(param)
|
||||
-- return true, foo, bar, ...
|
||||
-- -- or
|
||||
-- return false
|
||||
-- -- or
|
||||
-- return false, "error message"
|
||||
-- end,
|
||||
-- nodes_needed = function(name, foo, bar, ...), -- (optional)
|
||||
-- return n
|
||||
-- end,
|
||||
-- func = function(name, foo, bar, ...)
|
||||
-- return success, "message"
|
||||
-- end,
|
||||
-- }
|
||||
function worldedit.register_command(name, def)
|
||||
local def = table.copy(def)
|
||||
assert(name and #name > 0)
|
||||
def.name = name
|
||||
assert(def.privs)
|
||||
def.category = def.category or ""
|
||||
def.require_pos = def.require_pos or 0
|
||||
assert(def.require_pos >= 0 and def.require_pos < 3)
|
||||
if def.params == "" and not def.parse then
|
||||
def.parse = function(param)
|
||||
return param == ""
|
||||
end
|
||||
else
|
||||
assert(def.parse)
|
||||
end
|
||||
assert(def.nodes_needed == nil or type(def.nodes_needed) == "function")
|
||||
assert(def.func)
|
||||
|
||||
-- for development
|
||||
--[[if def.require_pos == 2 and not def.nodes_needed then
|
||||
minetest.log("warning", "//" .. name .. " might be missing nodes_needed")
|
||||
end--]]
|
||||
|
||||
-- disable further modification
|
||||
setmetatable(def, {__newindex = function() end})
|
||||
|
||||
minetest.register_chatcommand("/" .. name, {
|
||||
privs = def.privs,
|
||||
params = def.params,
|
||||
description = def.description,
|
||||
func = function(player_name, param)
|
||||
return chatcommand_handler(name, player_name, param)
|
||||
end,
|
||||
})
|
||||
worldedit.registered_commands[name] = def
|
||||
end
|
||||
|
||||
-- Notifies a player of something related to WorldEdit.
|
||||
-- Message types:
|
||||
-- "error" = An operation did not work as expected.
|
||||
-- "ok" = An operation completed successfully. Because notifications of this type
|
||||
-- can be filtered, use this ONLY for generic messages like "1234 nodes set".
|
||||
-- "info" = Other informational messages
|
||||
-- @param name Name of player
|
||||
-- @param message Message text
|
||||
-- @param typ Type of message (optional but strongly recommend)
|
||||
function worldedit.player_notify(name, message, typ)
|
||||
local t = {
|
||||
"WorldEdit",
|
||||
"-!-",
|
||||
tostring(message)
|
||||
}
|
||||
if typ == "error" then
|
||||
t[2] = minetest.colorize("#f22", t[2])
|
||||
elseif typ == "ok" then
|
||||
t[2] = minetest.colorize("#2f2", t[2])
|
||||
end
|
||||
minetest.chat_send_player(name, table.concat(t, " "))
|
||||
end
|
||||
|
||||
-- Determines the axis in which a player is facing
|
||||
-- @return axis ("x", "y", or "z") and the sign (1 or -1)
|
||||
-- @note Not part of API
|
||||
function worldedit.player_axis(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if not player then
|
||||
-- we promised to return something valid...
|
||||
return "y", -1
|
||||
end
|
||||
local dir = player:get_look_dir()
|
||||
local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z)
|
||||
if x > y then
|
||||
if x > z then
|
||||
return "x", dir.x > 0 and 1 or -1
|
||||
end
|
||||
elseif y > z then
|
||||
return "y", dir.y > 0 and 1 or -1
|
||||
end
|
||||
return "z", dir.z > 0 and 1 or -1
|
||||
end
|
||||
|
||||
-- Look-up table of valid directions (for worldedit.player_direction)
|
||||
-- Can be stringified for usage in help texts
|
||||
-- @note Not part of API
|
||||
worldedit.valid_directions = setmetatable({
|
||||
x = true, y = true, z = true,
|
||||
["?"] = true,
|
||||
up = true, down = true,
|
||||
front = true, back = true,
|
||||
left = true, right = true,
|
||||
}, {
|
||||
__tostring = function()
|
||||
return "x/y/z/?/up/down/left/right/front/back"
|
||||
end
|
||||
})
|
||||
|
||||
-- Accepts a valid directions as above
|
||||
-- @return axis ("x", "y", or "z") and the sign (1 or -1) *or* nil for invalid combinations
|
||||
-- @note Not part of API
|
||||
worldedit.player_direction = function(name, str)
|
||||
if str == "x" or str == "y" or str == "z" then
|
||||
return str, 1
|
||||
elseif str == "up" then
|
||||
return "y", 1
|
||||
elseif str == "down" then
|
||||
return "y", -1
|
||||
end
|
||||
|
||||
local axis, dir = worldedit.player_axis(name)
|
||||
|
||||
if str == "?" then
|
||||
return axis, dir
|
||||
elseif str == "front" then
|
||||
if axis ~= "y" then
|
||||
return axis, dir
|
||||
end
|
||||
elseif str == "back" then
|
||||
if axis ~= "y" then
|
||||
return axis, -dir
|
||||
end
|
||||
elseif str == "left" then
|
||||
if axis == "x" then
|
||||
return "z", dir
|
||||
elseif axis == "z" then
|
||||
return "x", -dir
|
||||
end
|
||||
elseif str == "right" then
|
||||
if axis == "x" then
|
||||
return "z", -dir
|
||||
elseif axis == "z" then
|
||||
return "x", dir
|
||||
end
|
||||
end
|
||||
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-- Wrapper for the engine"s parse_coordinates
|
||||
-- @return vector or nil
|
||||
-- @note Not part of API
|
||||
function worldedit.parse_coordinates(x, y, z, player_name)
|
||||
local relpos
|
||||
local player = minetest.get_player_by_name(player_name or "")
|
||||
if player then
|
||||
relpos = player:get_pos()
|
||||
end
|
||||
-- we don't bother to support ~ in the fallback path here
|
||||
if not minetest.parse_coordinates then
|
||||
x, y, z = tonumber(x), tonumber(y), tonumber(z)
|
||||
return x and y and z and vector.new(x, y, z)
|
||||
end
|
||||
return minetest.parse_coordinates(x, y, z, relpos)
|
||||
end
|
||||
|
||||
|
||||
worldedit.register_command("about", {
|
||||
privs = {},
|
||||
params = "",
|
||||
description = S("Get information about the WorldEdit mod"),
|
||||
func = function(name)
|
||||
worldedit.player_notify(name, S("WorldEdit @1"..
|
||||
" is available on this server. Type @2 to get a list of "..
|
||||
"commands, or find more information at @3",
|
||||
worldedit.version_string, minetest.colorize("#0ff", "//help"),
|
||||
"https://github.com/Uberi/Minetest-WorldEdit"
|
||||
), "info")
|
||||
end,
|
||||
})
|
||||
|
||||
-- initially copied from builtin/chatcommands.lua
|
||||
local function help_command(name, param)
|
||||
local function format_help_line(cmd, def, follow_alias)
|
||||
local msg = minetest.colorize("#0ff", "//"..cmd)
|
||||
if def.name ~= cmd then
|
||||
msg = msg .. ": " .. S("alias to @1",
|
||||
minetest.colorize("#0ff", "//"..def.name))
|
||||
if follow_alias then
|
||||
msg = msg .. "\n" .. format_help_line(def.name, def)
|
||||
end
|
||||
else
|
||||
if def.params and def.params ~= "" then
|
||||
msg = msg .. " " .. def.params
|
||||
end
|
||||
if def.description and def.description ~= "" then
|
||||
msg = msg .. ": " .. def.description
|
||||
end
|
||||
end
|
||||
return msg
|
||||
end
|
||||
-- @param cmds list of {cmd, def}
|
||||
local function sort_cmds(cmds)
|
||||
table.sort(cmds, function(c1, c2)
|
||||
local cmd1, cmd2 = c1[1], c2[1]
|
||||
local def1, def2 = c1[2], c2[2]
|
||||
-- by category (this puts the empty category first)
|
||||
if def1.category ~= def2.category then
|
||||
return def1.category < def2.category
|
||||
end
|
||||
-- put aliases last
|
||||
if (cmd1 ~= def1.name) ~= (cmd2 ~= def2.name) then
|
||||
return cmd2 ~= def2.name
|
||||
end
|
||||
-- then by name
|
||||
return c1[1] < c2[1]
|
||||
end)
|
||||
end
|
||||
|
||||
if not minetest.check_player_privs(name, "worldedit") then
|
||||
return false, S("You are not allowed to use any WorldEdit commands.")
|
||||
end
|
||||
if param == "" then
|
||||
local list = {}
|
||||
for cmd, def in pairs(worldedit.registered_commands) do
|
||||
if minetest.check_player_privs(name, def.privs) then
|
||||
list[#list + 1] = cmd
|
||||
end
|
||||
end
|
||||
table.sort(list)
|
||||
local help = minetest.colorize("#0ff", "//help")
|
||||
return true, S("Available commands: @1@n"
|
||||
.. "Use '@2' to get more information,"
|
||||
.. " or '@3' to list everything.",
|
||||
table.concat(list, " "), help .. " <cmd>", help .. " all")
|
||||
elseif param == "all" then
|
||||
local cmds = {}
|
||||
for cmd, def in pairs(worldedit.registered_commands) do
|
||||
if minetest.check_player_privs(name, def.privs) then
|
||||
cmds[#cmds + 1] = {cmd, def}
|
||||
end
|
||||
end
|
||||
sort_cmds(cmds)
|
||||
local list = {}
|
||||
local last_cat = ""
|
||||
for _, e in ipairs(cmds) do
|
||||
if e[2].category ~= last_cat then
|
||||
last_cat = e[2].category
|
||||
list[#list + 1] = "---- " .. last_cat
|
||||
end
|
||||
list[#list + 1] = format_help_line(e[1], e[2])
|
||||
end
|
||||
return true, S("Available commands:@n") .. table.concat(list, "\n")
|
||||
else
|
||||
local def = worldedit.registered_commands[param]
|
||||
if not def then
|
||||
return false, S("Command not available: ") .. param
|
||||
else
|
||||
return true, format_help_line(param, def, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
worldedit.register_command("help", {
|
||||
privs = {},
|
||||
params = "[all/<cmd>]",
|
||||
description = S("Get help for WorldEdit commands"),
|
||||
parse = function(param)
|
||||
return true, param
|
||||
end,
|
||||
func = function(name, param)
|
||||
local ok, msg = help_command(name, param)
|
||||
if msg then
|
||||
worldedit.player_notify(name, msg, ok and "info" or "error")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- needs to be here due to reset_pending()
|
||||
worldedit.register_command("reset", {
|
||||
params = "",
|
||||
description = S("Reset the region so that it is empty"),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
func = function(name)
|
||||
worldedit.pos1[name] = nil
|
||||
worldedit.pos2[name] = nil
|
||||
worldedit.marker_update(name)
|
||||
worldedit.set_pos[name] = nil
|
||||
--make sure the user does not try to confirm an operation after resetting pos:
|
||||
reset_pending(name)
|
||||
return true, S("region reset")
|
||||
end,
|
||||
})
|
||||
|
||||
-- Load the other parts
|
||||
do
|
||||
local modpath = minetest.get_modpath("worldedit_commands")
|
||||
for _, name in ipairs({
|
||||
"code", "cuboid_funcs", "cuboid", "manipulations", "marker", "nodename",
|
||||
"primitives", "region", "schematics", "transform", "wand"
|
||||
}) do
|
||||
dofile(modpath .. "/" .. name .. ".lua")
|
||||
end
|
||||
|
||||
if worldedit.register_test then
|
||||
dofile(modpath .. "/test/init.lua")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,155 @@
|
||||
# textdomain: worldedit_commands
|
||||
Outset the selected region.=
|
||||
Invalid direction: @1=
|
||||
Invalid number of arguments=
|
||||
Region outset by @1 nodes=
|
||||
Inset the selected region.=
|
||||
Region inset by @1 nodes=
|
||||
Shifts the selection area without moving its contents=
|
||||
Invalid if looking straight up or down=
|
||||
Region shifted by @1 nodes=
|
||||
Expands the selection in the selected absolute or relative axis=
|
||||
Region expanded by @1 nodes=
|
||||
Contracts the selection in the selected absolute or relative axis=
|
||||
Region contracted by @1 nodes=
|
||||
Select a cube with side length <size> around position 1 and run <command> on region=
|
||||
invalid usage: @1 cannot be used with cubeapply=
|
||||
Missing privileges: @1=
|
||||
Region operations=
|
||||
Can use WorldEdit commands=
|
||||
no region selected=
|
||||
no position 1 selected=
|
||||
invalid usage=
|
||||
ERROR: the operation was cancelled because the region has changed.=
|
||||
Could not open file "@1"=
|
||||
Invalid file format!=
|
||||
Schematic was created with a newer version of WorldEdit.=
|
||||
Get information about the WorldEdit mod=
|
||||
WorldEdit @1 is available on this server. Type @2 to get a list of commands, or find more information at @3=
|
||||
Get help for WorldEdit commands=
|
||||
alias to @1=
|
||||
You are not allowed to use any WorldEdit commands.=
|
||||
Available commands: @1@nUse '@2' to get more information, or '@3' to list everything.=
|
||||
Available commands:@n=
|
||||
Command not available: =
|
||||
Enable or disable node inspection=
|
||||
inspector: inspection enabled for @1, currently facing the @2 axis=
|
||||
inspector: inspection disabled=
|
||||
inspector: @1 at @2 (param1@=@3, param2@=@4, received light@=@5) punched facing the @6 axis=
|
||||
Reset the region so that it is empty=
|
||||
region reset=
|
||||
Show markers at the region positions=
|
||||
region marked=
|
||||
Hide markers if currently shown=
|
||||
region unmarked=
|
||||
Set WorldEdit region position @1 to the player's location=
|
||||
position @1 set to @2=
|
||||
Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region=
|
||||
unknown subcommand: @1=
|
||||
select positions by punching two nodes=
|
||||
select position @1 by punching a node=
|
||||
position @1: @2=
|
||||
position @1 not set=
|
||||
Set a WorldEdit region position to the position at (<x>, <y>, <z>)=
|
||||
invalid position=
|
||||
Display the volume of the current WorldEdit region=
|
||||
current region has a volume of @1 nodes (@2*@3*@4)=
|
||||
Remove all MapBlocks (16x16x16) containing the selected area from the map=
|
||||
Node manipulation=
|
||||
Area deleted.=
|
||||
There was an error during deletion of the area.=
|
||||
Set the current WorldEdit region to <node>=
|
||||
invalid node name: @1=
|
||||
@1 nodes set=
|
||||
Set param2 of all nodes in the current WorldEdit region to <param2>=
|
||||
Param2 is out of range (must be between 0 and 255 inclusive!)=
|
||||
@1 nodes altered=
|
||||
Fill the current WorldEdit region with a random mix of <node1>, ...=
|
||||
invalid search node name: @1=
|
||||
invalid replace node name: @1=
|
||||
Replace all instances of <search node> with <replace node> in the current WorldEdit region=
|
||||
@1 nodes replaced=
|
||||
Replace all nodes other than <search node> with <replace node> in the current WorldEdit region=
|
||||
Add a hollow cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.=
|
||||
Shapes=
|
||||
@1 nodes added=
|
||||
Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.=
|
||||
Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>=
|
||||
Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>=
|
||||
Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>=
|
||||
Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>=
|
||||
Add hollow cylinder at WorldEdit position 1 along the given axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>=
|
||||
Add cylinder at WorldEdit position 1 along the given axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>=
|
||||
Add hollow pyramid centered at WorldEdit position 1 along the given axis with height <height>, composed of <node>=
|
||||
Add pyramid centered at WorldEdit position 1 along the given axis with height <height>, composed of <node>=
|
||||
Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>=
|
||||
Copy the current WorldEdit region along the given axis by <amount> nodes=
|
||||
Transformations=
|
||||
@1 nodes copied=
|
||||
Move the current WorldEdit region along the given axis by <amount> nodes=
|
||||
@1 nodes moved=
|
||||
Stack the current WorldEdit region along the given axis <count> times=
|
||||
@1 nodes stacked=
|
||||
Stack the current WorldEdit region <count> times by offset <x>, <y>, <z>=
|
||||
invalid count: @1=
|
||||
invalid increments: @1=
|
||||
Scale the current WorldEdit positions and region by a factor of <stretchx>, <stretchy>, <stretchz> along the X, Y, and Z axes, repectively, with position 1 as the origin=
|
||||
invalid scaling factors: @1=
|
||||
@1 nodes stretched=
|
||||
Transpose the current WorldEdit region along the given axes=
|
||||
invalid usage: axes must be different=
|
||||
@1 nodes transposed=
|
||||
Flip the current WorldEdit region along the given axis=
|
||||
@1 nodes flipped=
|
||||
Rotate the current WorldEdit region around the given axis by angle <angle> (90 degree increment)=
|
||||
invalid usage: angle must be multiple of 90=
|
||||
@1 nodes rotated=
|
||||
Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)=
|
||||
@1 nodes oriented=
|
||||
Fix the lighting in the current WorldEdit region=
|
||||
@1 nodes updated=
|
||||
Remove any fluid node within the current WorldEdit region=
|
||||
Remove any plant, tree or foliage-like nodes in the selected region=
|
||||
@1 nodes removed=
|
||||
Hide all nodes in the current WorldEdit region non-destructively=
|
||||
@1 nodes hidden=
|
||||
Suppress all <node> in the current WorldEdit region non-destructively=
|
||||
@1 nodes suppressed=
|
||||
Highlight <node> in the current WorldEdit region by hiding everything else non-destructively=
|
||||
@1 nodes highlighted=
|
||||
Restores nodes hidden with WorldEdit in the current WorldEdit region=
|
||||
@1 nodes restored=
|
||||
Warning: The schematic contains excessive free space and WILL be misaligned when allocated or loaded. To avoid this, shrink your area to cover exactly the nodes to be saved.=
|
||||
Save the current WorldEdit region to "(world folder)/schems/<file>.we"=
|
||||
Schematics=
|
||||
Disallowed file name: @1=
|
||||
Could not save file to "@1"=
|
||||
@1 nodes saved=
|
||||
Set the region defined by nodes from "(world folder)/schems/<file>.we" as the current WorldEdit region=
|
||||
Schematic empty, nothing allocated=
|
||||
@1 nodes allocated=
|
||||
Load nodes from "(world folder)/schems/<file>[.we[m]]" with position 1 of the current WorldEdit region as the origin=
|
||||
Loading failed!=
|
||||
@1 nodes loaded=
|
||||
Executes <code> as a Lua chunk in the global namespace=
|
||||
Code=
|
||||
Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region=
|
||||
Save the current WorldEdit region using the Minetest Schematic format to "(world folder)/schems/<filename>.mts"=
|
||||
Failed to create Minetest schematic=
|
||||
Saved Minetest schematic to @1=
|
||||
Load nodes from "(world folder)/schems/<file>.mts" with position 1 of the current WorldEdit region as the origin=
|
||||
failed to place Minetest schematic=
|
||||
placed Minetest schematic @1 at @2=
|
||||
Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry=
|
||||
select Minetest schematic probability values by punching nodes=
|
||||
finished Minetest schematic probability selection=
|
||||
currently set node probabilities:=
|
||||
invalid node probability given, not saved=
|
||||
Clears all objects within the WorldEdit region=
|
||||
@1 objects cleared=
|
||||
WARNING: this operation could affect up to @1 nodes; type @2 to continue or @3 to cancel=
|
||||
Confirm a pending operation=
|
||||
no operation pending=
|
||||
Abort a pending operation=
|
||||
WorldEdit Wand tool=
|
||||
Left-click to set 1st position, right-click to set 2nd=
|
||||
@@ -0,0 +1,166 @@
|
||||
# textdomain: worldedit_commands
|
||||
##
|
||||
# Übersetzungshinweise:
|
||||
# node = Block
|
||||
# command = Befehl
|
||||
# region = Gebiet
|
||||
# position 1/2 = Position 1/2
|
||||
# schematic = Schematic
|
||||
# Der Anwender wird gesiezt.
|
||||
# Diese Datei auf Rechtschreibfehler prüfen (unter Linux):
|
||||
# sed -rne 's|^.*[^@]=(.*)$|\1|gp' worldedit_commands/locale/worldedit_commands.de.tr | sed -re 's/\b(WorldEdit|Schematic|Minetest)\b/Beispiel/g' | LANG=de_DE.UTF-8 hunspell -L
|
||||
##
|
||||
Outset the selected region.=Ausgewähltes Gebiet vergrößern.
|
||||
Invalid direction: @1=Ungültige Richtung: @1
|
||||
Invalid number of arguments=Ungültige Anzahl der Argumente
|
||||
Region outset by @1 nodes=Gebiet um @1 Blöcke vergrößert
|
||||
Inset the selected region.=Ausgewähltes Gebiet verkleinern.
|
||||
Region inset by @1 nodes=Gebiet um @1 Blöcke verkleinert
|
||||
Shifts the selection area without moving its contents=Verschiebt das ausgewählte Gebiet ohne dessen Inhalt
|
||||
Invalid if looking straight up or down=Nicht zulässig bei gerader Blickrichtung nach unten oder oben
|
||||
Region shifted by @1 nodes=Gebiet um @1 Blöcke verschoben
|
||||
Expands the selection in the selected absolute or relative axis=Erweitert das Gebiet in Richtung einer absoluten oder relativen Achse
|
||||
Region expanded by @1 nodes=Gebiet um @1 Blöcke erweitert
|
||||
Contracts the selection in the selected absolute or relative axis=Schrumpft das Gebiet in Richtung einer absoluten oder relativen Achse
|
||||
Region contracted by @1 nodes=Gebiet um @1 Blöcke geschrumpft
|
||||
Select a cube with side length <size> around position 1 and run <command> on region=Einen Würfel mit Seitenlänge <size> um Position 1 auswählen und <command> auf diesem Gebiet ausführen
|
||||
invalid usage: @1 cannot be used with cubeapply=Ungültige Verwendung: @1 kann nicht mit cubeapply verwendet werden
|
||||
Missing privileges: @1=Fehlende Privilegien: @1
|
||||
Region operations=Gebietsoperationen
|
||||
Can use WorldEdit commands=Kann WorldEdit-Befehle benutzen
|
||||
no region selected=Kein Gebiet ausgewählt
|
||||
no position 1 selected=Keine Position 1 ausgewählt
|
||||
invalid usage=Ungültige Verwendung
|
||||
ERROR: the operation was cancelled because the region has changed.=FEHLER: Der Vorgang wurde abgebrochen, weil das Gebiet verändert wurde.
|
||||
Could not open file "@1"=Konnte die Datei „@1“ nicht öffnen
|
||||
Invalid file format!=Ungültiges Dateiformat!
|
||||
Schematic was created with a newer version of WorldEdit.=Schematic wurde mit einer neueren Version von WorldEdit erstellt.
|
||||
Get information about the WorldEdit mod=Informationen über die WorldEdit-Mod erhalten
|
||||
WorldEdit @1 is available on this server. Type @2 to get a list of commands, or find more information at @3=WorldEdit @1 ist auf diesem Server verfügbar. Nutzen Sie @2, um eine Liste der Befehle zu erhalten, oder finden Sie weitere Informationen unter @3
|
||||
Get help for WorldEdit commands=Hilfe für WorldEdit-Befehle erhalten
|
||||
alias to @1=Alias für @1
|
||||
You are not allowed to use any WorldEdit commands.=Ihnen ist nicht erlaubt, WorldEdit-Befehle zu nutzen.
|
||||
Available commands: @1@nUse '@2' to get more information, or '@3' to list everything.=Verfügbare Befehle: @1@n„@2“ benutzen, um mehr Informationen zu erhalten, oder „@3“, um alles aufzulisten.
|
||||
Available commands:@n=Verfügbare Befehle:@n
|
||||
Command not available: =Befehl nicht verfügbar:
|
||||
Enable or disable node inspection=Inspizieren von Blöcken ein- oder ausschalten
|
||||
inspector: inspection enabled for @1, currently facing the @2 axis=Inspektur: für @1 aktiviert, aktuell der @2-Achse zugewandt
|
||||
inspector: inspection disabled=Inspektur: deaktiviert
|
||||
inspector: @1 at @2 (param1@=@3, param2@=@4, received light@=@5) punched facing the @6 axis=Inspektur: @1 bei @2 (param1@=@3, param2@=@4, einfallendes Licht@=@5), der „@6“-Achse zugewandt, gehauen
|
||||
Reset the region so that it is empty=Gebiet zurücksetzen, sodass es leer ist
|
||||
region reset=Gebiet zurückgesetzt
|
||||
Show markers at the region positions=Markierungen bei den Gebietspositionen anzeigen
|
||||
region marked=Gebiet markiert
|
||||
Hide markers if currently shown=Markierungen verstecken, falls derzeit sichtbar
|
||||
region unmarked=Gebiet nicht mehr markiert
|
||||
Set WorldEdit region position @1 to the player's location=Position @1 des WorldEdit-Gebiets auf die Position des Spielers setzen
|
||||
position @1 set to @2=Position @1 auf @2 gesetzt
|
||||
Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region=WorldEdit-Gebiet, WorldEdit-Position 1 oder WorldEdit-Position 2 durch Schlagen von Blöcken setzen oder aktuelles WorldEdit-Gebiet anzeigen
|
||||
unknown subcommand: @1=Unbekannter Unterbefehl: @1
|
||||
select positions by punching two nodes=Wählen Sie die Position durch Hauen zweier Blöcke
|
||||
select position @1 by punching a node=Wählen Sie Position @1 durch Hauen eines Blocks
|
||||
position @1: @2=Position @1: @2
|
||||
position @1 not set=Position @1 ist nicht gesetzt
|
||||
Set a WorldEdit region position to the position at (<x>, <y>, <z>)=Eine Position des WorldEdit-Gebiets auf (<x>, <y>, <z>) setzen
|
||||
invalid position=Ungültige Position
|
||||
Display the volume of the current WorldEdit region=Volumen des aktuellen WorldEdit-Gebiets anzeigen
|
||||
current region has a volume of @1 nodes (@2*@3*@4)=Das aktuelle Gebiet hat ein Volumen von @1 Blöcken (@2×@3×@4)
|
||||
Remove all MapBlocks (16x16x16) containing the selected area from the map=Alle Kartenblöcke (16×16×16) entfernen, die das gewählte Gebiet enthalten
|
||||
Node manipulation=Block-Manipulation
|
||||
Area deleted.=Gebiet gelöscht.
|
||||
There was an error during deletion of the area.=Während des Löschens des Gebiets trat ein Fehler auf.
|
||||
Set the current WorldEdit region to <node>=Das aktuelle WorldEdit-Gebiet mit <node> füllen
|
||||
invalid node name: @1=Ungültiger Blockname: @1
|
||||
@1 nodes set=@1 Blöcke gesetzt
|
||||
Set param2 of all nodes in the current WorldEdit region to <param2>=Den param2 aller Blocke im aktuellen WorldEdit-Gebiet auf <param2> setzen
|
||||
Param2 is out of range (must be between 0 and 255 inclusive!)=Param2 muss zwischen 0 und 255 (inklusiv) sein
|
||||
@1 nodes altered=@1 Blöcke verändert
|
||||
Fill the current WorldEdit region with a random mix of <node1>, ...=Das aktuelle WorldEdit-Gebiet mit einer zufälligen Mischung aus <node1>, … füllen
|
||||
invalid search node name: @1=Ungültiger Blockname für die Suche: @1
|
||||
invalid replace node name: @1=Ungültiger Blockname für das Ersetzen: @1
|
||||
Replace all instances of <search node> with <replace node> in the current WorldEdit region=Alle Vorkommen von <search node> im aktuellen WorldEdit-Gebiet mit <replace node> ersetzen
|
||||
@1 nodes replaced=@1 Blöcke ersetzt
|
||||
Replace all nodes other than <search node> with <replace node> in the current WorldEdit region=Alle Blöcke außer <search node> im aktuellen WorldEdit-Gebiet mit <replace node> ersetzen
|
||||
Add a hollow cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.=Einen hohlen Würfel mit der Unterseite zentriert auf WorldEdit-Position 1 setzen, mit den Abmessungen <width>×<height>×<length>, bestehend aus <node>.
|
||||
Shapes=Formen
|
||||
@1 nodes added=@1 Blöcke hinzugefügt
|
||||
Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.=Einen Würfel mit der Unterseite zentriert auf WorldEdit-Position 1 setzen, mit den Abmessungen <width>×<height>×<length>, bestehend aus <node>.
|
||||
Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>=Eine hohle Kugel zentriert auf WorldEdit-Position 1 setzen mit Radius <radius>, bestehend aus <node>
|
||||
Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>=Eine Kugel zentriert auf WorldEdit-Position 1 setzen mit Radius <radius>, bestehend aus <node>
|
||||
Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>=Eine hohle Kuppel zentriert auf WorldEdit-Position 1 setzen mit Radius <radius>, bestehend aus <node>
|
||||
Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>=Eine Kuppel zentriert auf WorldEdit-Position 1 setzen mit Radius <radius>, bestehend aus <node>
|
||||
Add hollow cylinder at WorldEdit position 1 along the given axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>=Einen hohlen Zylinder zentriert auf WorldEdit-Position 1 entlang der gegebenen Achse mit Länge <length>, Bodenradius <radius1> (und oberen Radius [radius2]) setzen, bestehend aus <node>
|
||||
Add cylinder at WorldEdit position 1 along the given axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>=Einen Zylinder zentriert auf WorldEdit-Position 1 entlang der gegebenen Achse mit Länge <length>, Bodenradius <radius1> (und oberen Radius [radius2]) setzen, bestehend aus <node>
|
||||
Add hollow pyramid centered at WorldEdit position 1 along the given axis with height <height>, composed of <node>=Eine hohle Pyramide zentriert auf WorldEdit-Position 1 entlang der gegebenen Achse mit Länge <height> setzen, bestehend aus <node>
|
||||
Add pyramid centered at WorldEdit position 1 along the given axis with height <height>, composed of <node>=Eine Pyramide zentriert auf WorldEdit-Position 1 entlang der gegebenen Achse mit Länge <height> setzen, bestehend aus <node>
|
||||
Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>=Eine Spirale zentriert auf WorldEdit-Position 1 mit Seitenlänge <length>, Höhe <height>, Wandabstand <space> setzen, bestehend aus <node>
|
||||
Copy the current WorldEdit region along the given axis by <amount> nodes=Das aktuelle WorldEdit-Gebiet entlang der gegebenen Achse um <amount> Blöcke kopieren
|
||||
Transformations=Transformationen
|
||||
@1 nodes copied=@1 Blöcke kopiert
|
||||
Move the current WorldEdit region along the given axis by <amount> nodes=Das aktuelle WorldEdit-Gebiet entlang der gegebenen Achse um <amount> Blöcke verschieben
|
||||
@1 nodes moved=@1 Blöcke verschoben
|
||||
Stack the current WorldEdit region along the given axis <count> times=Das aktuelle WorldEdit-Gebiet entlang der gegebenen Achse <count> mal stapeln
|
||||
@1 nodes stacked=@1 Blöcke gestapelt
|
||||
Stack the current WorldEdit region <count> times by offset <x>, <y>, <z>=Das aktuelle WorldEdit-Gebiet <count> mal um <x>, <y>, <z> versetzt stapeln
|
||||
invalid count: @1=Ungültige Anzahl: @1
|
||||
invalid increments: @1=Ungültige Schrittweite: @1
|
||||
Scale the current WorldEdit positions and region by a factor of <stretchx>, <stretchy>, <stretchz> along the X, Y, and Z axes, repectively, with position 1 as the origin=Die aktuellen WorldEdit-Positionen und -Region um einen Faktor von <stretchx>, <stretchy>, <stretchz> entlang der X-, Y- und Z-Achsen skalieren, mit Position 1 als Ausgangspunkt
|
||||
invalid scaling factors: @1=Ungültige Skalierungsfaktoren: @1
|
||||
@1 nodes stretched=@1 Blöcke gestreckt
|
||||
Transpose the current WorldEdit region along the given axes=Das aktuelle WorldEdit-Gebiet entlang den gegebenen Achsen transponieren
|
||||
invalid usage: axes must be different=Ungültige Verwendung: Achsen müssen verschieden sein
|
||||
@1 nodes transposed=@1 Blöcke transponiert
|
||||
Flip the current WorldEdit region along the given axis=Das aktuelle WorldEdit-Gebiet entlang der gegebenen Achse spiegeln
|
||||
@1 nodes flipped=@1 Blöcke gespiegelt
|
||||
Rotate the current WorldEdit region around the given axis by angle <angle> (90 degree increment)=Das aktuelle WorldEdit-Gebiet um der gegebenen Achse um den Winkel <angle> rotieren (90-Grad-Schritte)
|
||||
invalid usage: angle must be multiple of 90=Ungültige Verwendung: Winkel muss ein Vielfaches von 90 sein
|
||||
@1 nodes rotated=@1 Blöcke rotiert
|
||||
Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)=Ausgerichtete Blöcke im aktuellen WorldEdit-Gebiet um die Y-Achse mit dem Winkel <angle> rotieren (90-Grad-Schritte)
|
||||
@1 nodes oriented=@1 Blöcke ausgerichtet
|
||||
Fix the lighting in the current WorldEdit region=Belichtung im aktuellen WorldEdit-Gebiet korrigieren
|
||||
@1 nodes updated=@1 Blöcke verändert
|
||||
Remove any fluid node within the current WorldEdit region=Alle flüssigen Blöcke im WorldEdit-Gebiet entfernen
|
||||
Remove any plant, tree or foliage-like nodes in the selected region=Alle pflanzen-, baum- oder laubähnliche Blöcke im WorldEdit-Gebiet entfernen
|
||||
@1 nodes removed=@1 Blöcke entfernt
|
||||
Hide all nodes in the current WorldEdit region non-destructively=Alle Blöcke im WorldEdit-Gebiet nicht-destruktiv verstecken
|
||||
@1 nodes hidden=@1 Blöcke versteckt
|
||||
Suppress all <node> in the current WorldEdit region non-destructively=Alle Blöcke vom Typ <node> in der aktuellen WorldEdit-Region nicht-destruktiv verstecken
|
||||
@1 nodes suppressed=@1 Blöcke versteckt
|
||||
Highlight <node> in the current WorldEdit region by hiding everything else non-destructively=Alle Blöcke vom Typ <node> im WorldEdit-Gebiet hervorheben, indem alle anderen nicht-destruktiv versteckt werden
|
||||
@1 nodes highlighted=@1 Blöcke hervorgehoben
|
||||
Restores nodes hidden with WorldEdit in the current WorldEdit region=Stellt die von WorldEdit versteckten Blöcke im aktuellen WorldEdit-Gebiet wieder her
|
||||
@1 nodes restored=@1 Blöcke wiederhergestellt
|
||||
Warning: The schematic contains excessive free space and WILL be misaligned when allocated or loaded. To avoid this, shrink your area to cover exactly the nodes to be saved.=Warnung: Das Schematic hat überschüssigen freien Platz und WIRD nach dem Laden falsch ausgerichtet sein. Um dies zu vermeiden, verkleinern Sie das Gebiet, sodass es die zu speichernden Blöcke exakt umfasst.
|
||||
Save the current WorldEdit region to "(world folder)/schems/<file>.we"=Das aktuelle WorldEdit-Gebiet in „(Weltordner)/schems/<file>.we“ speichern
|
||||
Schematics=Schematic
|
||||
Disallowed file name: @1=Nicht erlaubter Dateiname: @1
|
||||
Could not save file to "@1"=Konnte die Datei nicht unter „@1“ speichern
|
||||
@1 nodes saved=@1 Blöcke gespeichert
|
||||
Set the region defined by nodes from "(world folder)/schems/<file>.we" as the current WorldEdit region=Das aktuelle WorldEdit-Gebiet anhand der Blöcke in „(Weltordner)/schems/<file>.we“ setzen
|
||||
Schematic empty, nothing allocated=Schematic leer, nichts reserviert
|
||||
@1 nodes allocated=@1 Blöcke reserviert
|
||||
Load nodes from "(world folder)/schems/<file>[.we[m]]" with position 1 of the current WorldEdit region as the origin=Blöcke aus „(Weltordner)/schems/<file>[.we[m]]“ mit Position 1 als Ausgangspunkt laden
|
||||
Loading failed!=Laden fehlgeschlagen!
|
||||
@1 nodes loaded=@1 Blöcke geladen
|
||||
Executes <code> as a Lua chunk in the global namespace=<code> als einen Lua-Chunk im globalen Namensraum ausführen
|
||||
Code=Code
|
||||
Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region=Führt <code> als einen Lua-Chunk im globalen Namensraum aus, wobei die Variable pos verfügbar ist, für jeden Block im aktuellen WorldEdit-Gebiet
|
||||
Save the current WorldEdit region using the Minetest Schematic format to "(world folder)/schems/<filename>.mts"=Das aktuelle WorldEdit-Gebiet als Minetest-Schematic in „(Weltordner)/schems/<filename>.mts“ speichern
|
||||
Failed to create Minetest schematic=Konnte Minetest-Schematic nicht erstellen
|
||||
Saved Minetest schematic to @1=Minetest-Schematic in @1 gespeichert
|
||||
Load nodes from "(world folder)/schems/<file>.mts" with position 1 of the current WorldEdit region as the origin=Blöcke aus „(Weltordner)/schems/<file>.mts“ mit Position 1 des aktuellen WorldEdit-Gebiets als Ausgangspunkt laden
|
||||
failed to place Minetest schematic=Konnte Minetest-Schematic nicht platzieren
|
||||
placed Minetest schematic @1 at @2=Minetest-Schematic @1 bei @2 platziert
|
||||
Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry=Startet die Eingabe von Block-Wahrscheinlichkeiten für Minetest-Schematics, holt die Blöcke, welche Wahrscheinlichkeiten gesetzt haben, oder beendet die Eingabe von Block-Wahrscheinlichkeiten
|
||||
select Minetest schematic probability values by punching nodes=Minetest-Schematic-Wahrscheinlichkeitswerte mit Hauen eingeben
|
||||
finished Minetest schematic probability selection=Minetest-Schematic-Wahrscheinlichkeitsauswahl abgeschlossen
|
||||
currently set node probabilities:=Momentan gesetzte Block-Wahrscheinlichkeiten:
|
||||
invalid node probability given, not saved=Ungültige Block-Wahrscheinlichkeit übergeben, nicht gespeichert
|
||||
Clears all objects within the WorldEdit region=Löscht alle Objekte innerhalb des WorldEdit-Gebiets
|
||||
@1 objects cleared=@1 Objekte gelöscht
|
||||
WARNING: this operation could affect up to @1 nodes; type @2 to continue or @3 to cancel=WARNUNG: Dieser Vorgang könnte bis zu @1 Blöcke betreffen; @2 zum Fortfahren oder @3 zum Abbrechen
|
||||
Confirm a pending operation=Einen ausstehenden Vorgang bestätigen
|
||||
no operation pending=Kein Vorgang ausstehend
|
||||
Abort a pending operation=Einen ausstehenden Vorgang abbrechen
|
||||
WorldEdit Wand tool=WorldEdit-Zauberstab
|
||||
Left-click to set 1st position, right-click to set 2nd=Setzen der 1. Position mit Linksklick, der 2. mit Rechtsklick
|
||||
@@ -0,0 +1,156 @@
|
||||
# textdomain: worldedit_commands
|
||||
|
||||
### init.lua ###
|
||||
Can use WorldEdit commands=Возможность редактировать мир с помощью команд WorldEdit
|
||||
|
||||
no region selected=не выделен регион
|
||||
no position 1 selected=не установлена позиция региона 1
|
||||
invalid usage=не верное использование команды
|
||||
Could not open file "@1"=Не удаётся открыть файл "@1"
|
||||
Invalid file format!=Не верный формат файла!
|
||||
Schematic was created with a newer version of WorldEdit.=Схема была создана с использованием более новой версии WorldEdit.
|
||||
|
||||
Get information about the WorldEdit mod=Вывести информацию о WorldEdit
|
||||
WorldEdit @1 is available on this server. Type @2 to get a list of commands, or get more information at @3=WorldEdit @1 доступен на этом сервере. Наберите команду @2 чтобы увидеть список команд, больше информации по ссылке @3
|
||||
Get help for WorldEdit commands=Вывести информацию об использовании команд WorldEdit
|
||||
You are not allowed to use any WorldEdit commands.=У вас нет привилегий, чтобы использовать команды WorldEdit.
|
||||
Available commands: @1@nUse '@2' to get more information, or '@3' to list everything.=Доступные команды: @1@nИспользуйте '@2' для получения информации по команде или '@3' для вывода подсказок по всем командам.
|
||||
Available commands:@n=Доступные команды:@n
|
||||
Command not available: =Команда не найдена:
|
||||
Enable or disable node inspection=Включить/отключить инспекцию блоков
|
||||
inspector: inspection enabled for @1, currently facing the @2 axis=inspector: инспекция включена для @1, текущий взор в направлении оси @2
|
||||
inspector: inspection disabled=inspector: инспекция отключена
|
||||
inspector: @1 at @2 (param1@=@3, param2@=@4, received light@=@5) punched facing the @6 axis=inspector: @1 в @2 (param1@=@3, param2@=@4, received light@=@5) ударен по поверхности @6
|
||||
Reset the region so that it is empty=Сбросить выделение области
|
||||
region reset=регион сброшен
|
||||
Show markers at the region positions=Отобразить маркеры выделенной области
|
||||
region marked=маркеры отображены
|
||||
Hide markers if currently shown=Скрыть маркеры выделенной области
|
||||
region unmarked=маркеры скрыты
|
||||
Set WorldEdit region position @1 to the player's location=Установить маркер @1 для WorldEdit-региона в месте нахождения игрока
|
||||
Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region=Выделить WorldEdit-регион или установить маркеры для WorldEdit-региона, либо отобразить уже выбранную область
|
||||
unknown subcommand: @1=неизвестная подкоманда: @1
|
||||
select positions by punching two nodes=выберите позиции, ударив по блокам
|
||||
select position @1 by punching a node=выберите позицию @1, ударив по блоку
|
||||
position @1: @2=позиция @1: @2
|
||||
position @1 not set=позиция @1 не установлена
|
||||
Set a WorldEdit region position to the position at (<x>, <y>, <z>)=Установить маркер для WorldEdit в позиции (<x>, <y>, <z>)
|
||||
position @1 set to @2=позиция @1 установлена в @2
|
||||
Display the volume of the current WorldEdit region=Вывести информацию об объёме текущей выделенной области WorldEdit (кол-во нод, размеры)
|
||||
current region has a volume of @1 nodes (@2*@3*@4)=текущий регион имеет объем @1 нод (@2*@3*@4)
|
||||
Remove all MapBlocks (16x16x16) containing the selected area from the map=Удалить MapBlocks (16x16x16), содержащие выбранную область
|
||||
Area deleted.=Область удалена.
|
||||
There was an error during deletion of the area.=Что-то пошло не так при удалении области.
|
||||
Set the current WorldEdit region to <node>=Заполнить выбранный WorldEdit-регион указанным типом блоков
|
||||
invalid node name: @1=неверное название блока: @1
|
||||
@1 nodes set=@1 блок(а/ов) установленно
|
||||
Set param2 of all nodes in the current WorldEdit region to <param2>=Проставить param2 для всех блоков в текущем WorldEdit-регионе
|
||||
Param2 is out of range (must be between 0 and 255 inclusive!)=Значение param2 должно быть от 0 до 255 (включительно!)
|
||||
@1 nodes altered=изменено @1 блок(а/ов)
|
||||
Fill the current WorldEdit region with a random mix of <node1>, ...=Заполнить выбранный WorldEdit-регион смесью указанных типов блоков
|
||||
invalid search node name: @1=неверное название блока-поиска: @1
|
||||
invalid replace node name: @1=неверное название блока-замены: @1
|
||||
Replace all instances of <search node> with <replace node> in the current WorldEdit region=Заменить все блоки <search node> на <replace node> в выбранной WorldEdit-области
|
||||
@1 nodes replaced=заменено @1 нод(а/ы)
|
||||
Replace all nodes other than <search node> with <replace node> in the current WorldEdit region=Заменить все блоки, кроме <search node>, на <replace node> в выбранной WorldEdit-области
|
||||
Add a hollow cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.=Установить полый куб с центром нижней грани в позиции 1 и указанными размерами, состоящий из блоков <node>.
|
||||
@1 nodes added=добавлен(о) @1 блок(а/ов)
|
||||
Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.=Установить куб с центром нижней грани в позиции 1 и указанными размерами, состоящий из блоков <node>.
|
||||
Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>=Установить полую сферу с центром в WorldEdit-позиции 1 радиусом <radius>, состоящую из блоков <node>
|
||||
Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>=Установить сферу с центром в WorldEdit-позиции 1 радиусом <radius>, состоящую из блоков <node>
|
||||
Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>=Установить полый купол с центром в WorldEdit-позиции 1 радиусом <radius>, состоящий из блоков <node>
|
||||
Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>=Установить купол с центром в WorldEdit-позиции 1 радиусом <radius>, состоящий из блоков <node>
|
||||
Add hollow cylinder at WorldEdit position 1 along the given axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>=Установить полый цилиндр вдоль указанной оси, с центром в позиции 1, высотой/длинной <length>, с радиусом основания <radius> (и радиусом вершины [radius 2]), состоящий из блоков <node>
|
||||
Add cylinder at WorldEdit position 1 along the given axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>=Установить цилиндр вдоль указанной оси, с центром в позиции 1, высотой/длинной <length>, с радиусом основания <radius> (и радиусом вершины [radius 2]), состоящий из блоков <node>
|
||||
Add hollow pyramid centered at WorldEdit position 1 along the given axis with height <height>, composed of <node>=Установить полую пирамиду вдоль указанной оси, с центром в WorldEdit-позиции 1 высотой <height>, состоящую из блоков <node>
|
||||
Add pyramid centered at WorldEdit position 1 along the given axis with height <height>, composed of <node>=Установить пирамиду вдоль указанной оси, с центром в WorldEdit-позиции 1 высотой <height>, состоящую из блоков <node>
|
||||
Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>=Установить спираль с центром в WorldEdit-позиции 1 шириной <length>, высотой <height> и с расстоянием между витками <space>, состоящую из блоков <node>
|
||||
Copy the current WorldEdit region along the given axis by <amount> nodes=Копировать текущий WorldEdit-регион со смещением вдоль указанной оси (x/y/z) на <amount> блоков
|
||||
@1 nodes copied=скопировано @1 нод(а/ы)
|
||||
Move the current WorldEdit region along the given axis by <amount> nodes=Переместить текущий WorldEdit-регион вдоль указанной оси (x/y/z) на <amount> блоков
|
||||
@1 nodes moved=перемещено @1 нод(а/ы)
|
||||
Stack the current WorldEdit region along the given axis <count> times=Размножить текущий WorldEdit-регион вдоль указанной оси <count> раз
|
||||
@1 nodes stacked=размножено @1 нод(а/ы)
|
||||
Stack the current WorldEdit region <count> times by offset <x>, <y>, <z>=Размножить текущий WorldEdit-регион <count> раз с шагом <x>, <y>, <z> по соответствующим осям
|
||||
invalid count: @1=неверное количество: @1
|
||||
invalid increments: @1=неверные приращения(шаг): @1
|
||||
Scale the current WorldEdit positions and region by a factor of <stretchx>, <stretchy>, <stretchz> along the X, Y, and Z axes, repectively, with position 1 as the origin=Масштабировать текущий WorldEdit-регион с коэффициентами <stretchx>, <stretchy>, <stretchz> вдоль осей X, Y и Z, используя WorldEdit-позицию 1 в качестве точки отсчёта
|
||||
invalid scaling factors: @1=неверные коэффициенты масштабирования: @1
|
||||
@1 nodes stretched=масштабировано @1 нод(а/ы)
|
||||
Transpose the current WorldEdit region along the given axes=Транспонировать текущий WorldEdit-регион по заданным осям.
|
||||
invalid usage: axes must be different=недопустимое использование: оси должны быть разными
|
||||
@1 nodes transposed=транспонировано @1 нод(а/ы)
|
||||
Flip the current WorldEdit region along the given axis=Перевернуть/Отразить текущий WorldEdit-регион вдоль указанной оси
|
||||
@1 nodes flipped=отражено @1 нод(а/ы)
|
||||
Rotate the current WorldEdit region around the given axis by angle <angle> (90 degree increment)=Повернуть текущий WorldEdit-регион вокруг оси <axis> на угол <angle> (шаг - 90 градусов)
|
||||
invalid usage: angle must be multiple of 90=недопустимое использование: угол должен быть кратен 90
|
||||
@1 nodes rotated=повёрнуто @1 нод(а/ы)
|
||||
Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)=Повернуть блоки в текущем WorldEdit-регионе вокруг оси Y на угол <angle> (шаг - 90 градусов)
|
||||
@1 nodes oriented=повёрнуто @1 нод(а/ы)
|
||||
Fix the lighting in the current WorldEdit region=Исправить освещение в текущем WorldEdit-регионе
|
||||
@1 nodes updated=обновлено @1 нод(а/ы)
|
||||
Remove any fluid node within the current WorldEdit region=Удалить любые жидкости в текущем WorldEdit-регионе
|
||||
Remove any plant, tree or foliage-like nodes in the selected region=Удалить любые растения, деревья или листье-подобные ноды в текущем WorldEdit-регионе
|
||||
@1 nodes removed=удалено @1 нод(а/ы)
|
||||
Hide all nodes in the current WorldEdit region non-destructively=Скрыть узлы текущего WorldEdit-региона, не удаляя их
|
||||
@1 nodes hidden=скрыто @1 нод(а/ы)
|
||||
Suppress all <node> in the current WorldEdit region non-destructively=Скрыть все блоки <node> в текущем WorldEdit-регионе, не удаляя их
|
||||
@1 nodes suppressed=скрыто @1 нод(а/ы)
|
||||
Highlight <node> in the current WorldEdit region by hiding everything else non-destructively=Скрыть все блоки, кроме <node>, в текущем WorldEdit-регионе, не удаляя их
|
||||
@1 nodes highlighted="подсвечено" @1 нод(а/ы)
|
||||
Restores nodes hidden with WorldEdit in the current WorldEdit region=Восстановить скрытые WorldEdit'ом узлы в текущем WorldEdit-регионе
|
||||
@1 nodes restored=восстановлено @1 нод(а/ы)
|
||||
Warning: The schematic contains excessive free space and WILL be misaligned when allocated or loaded. To avoid this, shrink your area to cover exactly the nodes to be saved.=Предупреждение: Схема содержит слишком много свободного места и будет смещена при размещении или загрузке. Чтобы избежать этого, уменьшите область так, чтобы она охватывала именно те узлы, которые необходимо сохранить.
|
||||
Save the current WorldEdit region to "(world folder)/schems/<file>.we"=Сохранить текущий WorldEdit-регион в файл "(world folder)/schems/<file>.we"
|
||||
Disallowed file name: @1=Недопустимое имя файла: @1
|
||||
Could not save file to "@1"=Не удалось сохранить файл в "@1"
|
||||
@1 nodes saved=сохранено @1 нод(а/ы)
|
||||
Set the region defined by nodes from "(world folder)/schems/<file>.we" as the current WorldEdit region=Выделить область, определённую узлами из "(world folder)/schems/<file>.we", как текущий WorldEdit-регион
|
||||
Schematic empty, nothing allocated=Схема пуста, ничего не выделено
|
||||
@1 nodes allocated=выделено @1 нод(а/ы)
|
||||
Load nodes from "(world folder)/schems/<file>[.we[m]]" with position 1 of the current WorldEdit region as the origin=Загрузить регион из "(world folder)/schems/<file>[.we[m]]" с WorldEdit-позицией 1 в качестве точки отсчёта
|
||||
Loading failed!=Не удалось загрузить!
|
||||
@1 nodes loaded=загружено @1 нод(а/ы)
|
||||
Executes <code> as a Lua chunk in the global namespace=Выполнить <code> как Lua-код в глобальном пространстве имён
|
||||
Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region=Выполнить <code> как Lua-код в глобальном пространстве имён, с доступом к переменным позиций для каждого блока в текущем WordEdit-регионе
|
||||
Save the current WorldEdit region using the Minetest Schematic format to "(world folder)/schems/<filename>.mts"=Сохранить текущий WorldEdit-регион с использованием сжатия в формат Minetest Schematic в файл "(world folder)/schems/<filename>.mts"
|
||||
Failed to create Minetest schematic=Не удалось создать Minetest-схему
|
||||
Saved Minetest schematic to @1=Minetest-схема сохранена в @1
|
||||
Load nodes from "(world folder)/schems/<file>.mts" with position 1 of the current WorldEdit region as the origin=Загрузить блоки из "(world folder)/schems/<file>.mts" с WorldEdit-позицией 1 в качестве точки отсчёта
|
||||
failed to place Minetest schematic=не удалось загрузить Minetest-схему
|
||||
placed Minetest schematic @1 at @2=Minetest-схема @1 загружена в @2
|
||||
Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry=Начать запись вероятностей для Minetest Schematic, ударяя по блокам, закончить запись вероятностей или вывести уже записанные вероятности
|
||||
select Minetest schematic probability values by punching nodes=выберите значения вероятностей для Minetest-схемы, ударя по нодам
|
||||
finished Minetest schematic probability selection=выбор вероятностей для Minetest-схемы завершен
|
||||
currently set node probabilities:=заданные вероятности нод на данный момент:
|
||||
invalid node probability given, not saved=недопустимая вероятность ноды, не сохранена
|
||||
Clears all objects within the WorldEdit region=Очистить все объекты в текущем WorldEdit-регионе
|
||||
@1 objects cleared=очищено @1 объектов
|
||||
|
||||
### safe.lua ###
|
||||
WARNING: this operation could affect up to @1 nodes; type @2 to continue or @3 to cancel=ПРЕДУПРЕЖДЕНИЕ: эта операция может затронуть до @1 нод; введите @2 для продолжения или @3 для отмены
|
||||
Confirm a pending operation=Подтвердить отложенную операцию
|
||||
no operation pending=нет ожидающей операции
|
||||
Abort a pending operation=Отклонить отложенную операцию
|
||||
|
||||
### cuboid.lua ###
|
||||
Outset the selected region.=Расширить выделение региона.
|
||||
Invalid direction: @1=Недопустимое направление: @1
|
||||
Invalid number of arguments=Недопустимое количество аргументов
|
||||
Region outset by @1 nodes=Регион расширен на @1 нод(у/ы)
|
||||
Inset the selected region.=Сузить выделение региона.
|
||||
Region inset by @1 nodes=Регион сужен на @1 нод(у/ы)
|
||||
Shifts the selection area without moving its contents=Сдвинуть выделение региона без перемещения его содержимого
|
||||
Invalid if looking straight up or down=Недопустимо, если смотреть прямо вверх или вниз
|
||||
Region shifted by @1 nodes=Регион сдвинут на @1 нод(у/ы)
|
||||
Expands the selection in the selected absolute or relative axis=Увеличить выделение региона по выбранной абсолютной или относительной оси
|
||||
Region expanded by @1 nodes=Регион увеличен на @1 нод(у/ы)
|
||||
Contracts the selection in the selected absolute or relative axis=Уменьшить выделение региона по выбранной абсолютной или относительной оси
|
||||
Region contracted by @1 nodes=Регион уменьшен на @1 нод(у/ы)
|
||||
Select a cube with side length <size> around position 1 and run <command> on region=Выделить куб с длиной стороны <size> вокруг позиции 1 и запустите <команду> в области
|
||||
invalid usage: @1 cannot be used with cubeapply=недопустимое использование: @1 не может быть применено в cubeapply
|
||||
Missing privileges: @1=Отсутствуют привилегии: @1
|
||||
|
||||
### wand.lua ###
|
||||
WorldEdit Wand tool=Инструмент WorldEdit Wand
|
||||
Left-click to set 1st position, right-click to set 2nd=Левая кнопка мыши, чтобы установить 1-ю позицию, правая кнопка мыши, чтобы установить 2-ю
|
||||
@@ -0,0 +1,366 @@
|
||||
local S = minetest.get_translator("worldedit_commands")
|
||||
|
||||
local function check_region(name)
|
||||
return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
|
||||
end
|
||||
|
||||
|
||||
worldedit.register_command("deleteblocks", {
|
||||
params = "",
|
||||
description = S("Remove all MapBlocks (16x16x16) containing the selected area from the map"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
nodes_needed = check_region,
|
||||
func = function(name)
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
local success = minetest.delete_area(pos1, pos2)
|
||||
if success then
|
||||
return true, S("Area deleted.")
|
||||
else
|
||||
return false, S("There was an error during deletion of the area.")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("clearobjects", {
|
||||
params = "",
|
||||
description = S("Clears all objects within the WorldEdit region"),
|
||||
category = S("Node manipulation"), -- not really, but it doesn't fit anywhere else
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
nodes_needed = check_region,
|
||||
func = function(name)
|
||||
local count = worldedit.clear_objects(worldedit.pos1[name], worldedit.pos2[name])
|
||||
return true, S("@1 objects cleared", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("set", {
|
||||
params = "<node>",
|
||||
description = S("Set the current WorldEdit region to <node>"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local node = worldedit.normalize_nodename(param)
|
||||
if not node then
|
||||
return false, S("invalid node name: @1", param)
|
||||
end
|
||||
return true, node
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, node)
|
||||
local count = worldedit.set(worldedit.pos1[name], worldedit.pos2[name], node)
|
||||
return true, S("@1 nodes set", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("param2", {
|
||||
params = "<param2>",
|
||||
description = S("Set param2 of all nodes in the current WorldEdit region to <param2>"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local param2 = tonumber(param)
|
||||
if not param2 then
|
||||
return false
|
||||
elseif param2 < 0 or param2 > 255 then
|
||||
return false, S("Param2 is out of range (must be between 0 and 255 inclusive!)")
|
||||
end
|
||||
return true, param2
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, param2)
|
||||
local count = worldedit.set_param2(worldedit.pos1[name], worldedit.pos2[name], param2)
|
||||
return true, S("@1 nodes altered", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("mix", {
|
||||
params = "<node1> [count1] <node2> [count2] ...",
|
||||
description = S("Fill the current WorldEdit region with a random mix of <node1>, ..."),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local nodes = {}
|
||||
for nodename in param:gmatch("[^%s]+") do
|
||||
if tonumber(nodename) ~= nil and #nodes > 0 then
|
||||
local last_node = nodes[#nodes]
|
||||
for i = 1, tonumber(nodename) do
|
||||
nodes[#nodes + 1] = last_node
|
||||
end
|
||||
else
|
||||
local node = worldedit.normalize_nodename(nodename)
|
||||
if not node then
|
||||
return false, S("invalid node name: @1", nodename)
|
||||
end
|
||||
nodes[#nodes + 1] = node
|
||||
end
|
||||
end
|
||||
if #nodes == 0 then
|
||||
return false
|
||||
end
|
||||
return true, nodes
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, nodes)
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
local count = worldedit.set(pos1, pos2, nodes)
|
||||
return true, S("@1 nodes set", count)
|
||||
end,
|
||||
})
|
||||
|
||||
local check_replace = function(param)
|
||||
local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$")
|
||||
if found == nil then
|
||||
return false
|
||||
end
|
||||
local newsearchnode = worldedit.normalize_nodename(searchnode)
|
||||
if not newsearchnode then
|
||||
return false, S("invalid search node name: @1", searchnode)
|
||||
end
|
||||
local newreplacenode = worldedit.normalize_nodename(replacenode)
|
||||
if not newreplacenode then
|
||||
return false, S("invalid replace node name: @1", replacenode)
|
||||
end
|
||||
return true, newsearchnode, newreplacenode
|
||||
end
|
||||
|
||||
worldedit.register_command("replace", {
|
||||
params = "<search node> <replace node>",
|
||||
description = S("Replace all instances of <search node> with <replace node> in the current WorldEdit region"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = check_replace,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, search_node, replace_node)
|
||||
local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
|
||||
search_node, replace_node)
|
||||
return true, S("@1 nodes replaced", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("replaceinverse", {
|
||||
params = "<search node> <replace node>",
|
||||
description = S("Replace all nodes other than <search node> with <replace node> in the current WorldEdit region"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = check_replace,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, search_node, replace_node)
|
||||
local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
|
||||
search_node, replace_node, true)
|
||||
return true, S("@1 nodes replaced", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("fixlight", {
|
||||
params = "",
|
||||
description = S("Fix the lighting in the current WorldEdit region"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
nodes_needed = check_region,
|
||||
func = function(name)
|
||||
local count = worldedit.fixlight(worldedit.pos1[name], worldedit.pos2[name])
|
||||
return true, S("@1 nodes updated", count)
|
||||
end,
|
||||
})
|
||||
|
||||
local drain_cache
|
||||
|
||||
local function drain(pos1, pos2)
|
||||
if drain_cache == nil then
|
||||
drain_cache = {}
|
||||
for name, d in pairs(minetest.registered_nodes) do
|
||||
if d.drawtype == "liquid" or d.drawtype == "flowingliquid" then
|
||||
drain_cache[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local count = 0
|
||||
|
||||
local get_node, remove_node = minetest.get_node, minetest.remove_node
|
||||
for x = pos1.x, pos2.x do
|
||||
for y = pos1.y, pos2.y do
|
||||
for z = pos1.z, pos2.z do
|
||||
local p = vector.new(x, y, z)
|
||||
local n = get_node(p).name
|
||||
if drain_cache[n] then
|
||||
remove_node(p)
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
worldedit.register_command("drain", {
|
||||
params = "",
|
||||
description = S("Remove any fluid node within the current WorldEdit region"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
nodes_needed = check_region,
|
||||
func = function(name)
|
||||
local count = drain(worldedit.pos1[name], worldedit.pos2[name])
|
||||
return true, S("@1 nodes updated", count)
|
||||
end,
|
||||
})
|
||||
|
||||
local clearcut_cache
|
||||
|
||||
local function clearcut(pos1, pos2)
|
||||
-- decide which nodes we consider plants
|
||||
if clearcut_cache == nil then
|
||||
clearcut_cache = {}
|
||||
for name, def in pairs(minetest.registered_nodes) do
|
||||
local groups = def.groups or {}
|
||||
if (
|
||||
-- the groups say so
|
||||
groups.flower or groups.grass or groups.flora or groups.plant or
|
||||
groups.leaves or groups.tree or groups.leafdecay or groups.sapling or
|
||||
-- drawtype heuristic
|
||||
(def.is_ground_content and def.buildable_to and
|
||||
(def.sunlight_propagates or not def.walkable)
|
||||
and def.drawtype == "plantlike") or
|
||||
-- if it's flammable, it probably needs to go too
|
||||
(def.is_ground_content and not def.walkable and groups.flammable)
|
||||
) then
|
||||
clearcut_cache[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
local plants = clearcut_cache
|
||||
|
||||
local count = 0
|
||||
local prev, any
|
||||
|
||||
local get_node, remove_node = minetest.get_node, minetest.remove_node
|
||||
for x = pos1.x, pos2.x do
|
||||
for z = pos1.z, pos2.z do
|
||||
prev = false
|
||||
any = false
|
||||
-- first pass: remove floating nodes that would be left over
|
||||
for y = pos1.y, pos2.y do
|
||||
local pos = vector.new(x, y, z)
|
||||
local n = get_node(pos).name
|
||||
if plants[n] then
|
||||
prev = true
|
||||
any = true
|
||||
elseif prev then
|
||||
local def = minetest.registered_nodes[n] or {}
|
||||
local groups = def.groups or {}
|
||||
if groups.attached_node or (def.buildable_to and groups.falling_node) then
|
||||
remove_node(pos)
|
||||
count = count + 1
|
||||
else
|
||||
prev = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- second pass: remove plants, top-to-bottom to avoid item drops
|
||||
if any then
|
||||
for y = pos2.y, pos1.y, -1 do
|
||||
local pos = vector.new(x, y, z)
|
||||
local n = get_node(pos).name
|
||||
if plants[n] then
|
||||
remove_node(pos)
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
worldedit.register_command("clearcut", {
|
||||
params = "",
|
||||
description = S("Remove any plant, tree or foliage-like nodes in the selected region"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
nodes_needed = check_region,
|
||||
func = function(name)
|
||||
local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
|
||||
local count = clearcut(pos1, pos2)
|
||||
return true, S("@1 nodes removed", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("hide", {
|
||||
params = "",
|
||||
description = S("Hide all nodes in the current WorldEdit region non-destructively"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
nodes_needed = check_region,
|
||||
func = function(name)
|
||||
local count = worldedit.hide(worldedit.pos1[name], worldedit.pos2[name])
|
||||
return true, S("@1 nodes hidden", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("suppress", {
|
||||
params = "<node>",
|
||||
description = S("Suppress all <node> in the current WorldEdit region non-destructively"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local node = worldedit.normalize_nodename(param)
|
||||
if not node then
|
||||
return false, S("invalid node name: @1", param)
|
||||
end
|
||||
return true, node
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, node)
|
||||
local count = worldedit.suppress(worldedit.pos1[name], worldedit.pos2[name], node)
|
||||
return true, S("@1 nodes suppressed", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("highlight", {
|
||||
params = "<node>",
|
||||
description = S("Highlight <node> in the current WorldEdit region by hiding everything else non-destructively"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local node = worldedit.normalize_nodename(param)
|
||||
if not node then
|
||||
return false, S("invalid node name: @1", param)
|
||||
end
|
||||
return true, node
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, node)
|
||||
local count = worldedit.highlight(worldedit.pos1[name], worldedit.pos2[name], node)
|
||||
return true, S("@1 nodes highlighted", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("restore", {
|
||||
params = "",
|
||||
description = S("Restores nodes hidden with WorldEdit in the current WorldEdit region"),
|
||||
category = S("Node manipulation"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
nodes_needed = check_region,
|
||||
func = function(name)
|
||||
local count = worldedit.restore(worldedit.pos1[name], worldedit.pos2[name])
|
||||
return true, S("@1 nodes restored", count)
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,201 @@
|
||||
worldedit.marker1 = {}
|
||||
worldedit.marker2 = {}
|
||||
worldedit.marker_region = {}
|
||||
|
||||
local init_sentinel = "new" .. tostring(math.random(99999))
|
||||
|
||||
--marks worldedit region position 1
|
||||
worldedit.mark_pos1 = function(name, region_too)
|
||||
local pos1 = worldedit.pos1[name]
|
||||
|
||||
if worldedit.marker1[name] ~= nil then --marker already exists
|
||||
worldedit.marker1[name]:remove() --remove marker
|
||||
worldedit.marker1[name] = nil
|
||||
end
|
||||
if pos1 ~= nil then
|
||||
worldedit.keep_loaded(pos1, pos1)
|
||||
|
||||
--add marker
|
||||
worldedit.marker1[name] = minetest.add_entity(pos1, "worldedit:pos1", init_sentinel)
|
||||
if worldedit.marker1[name] ~= nil then
|
||||
worldedit.marker1[name]:get_luaentity().player_name = name
|
||||
end
|
||||
end
|
||||
if region_too == nil or region_too then
|
||||
worldedit.mark_region(name)
|
||||
end
|
||||
end
|
||||
|
||||
--marks worldedit region position 2
|
||||
worldedit.mark_pos2 = function(name, region_too)
|
||||
local pos2 = worldedit.pos2[name]
|
||||
|
||||
if worldedit.marker2[name] ~= nil then --marker already exists
|
||||
worldedit.marker2[name]:remove() --remove marker
|
||||
worldedit.marker2[name] = nil
|
||||
end
|
||||
if pos2 ~= nil then
|
||||
worldedit.keep_loaded(pos2, pos2)
|
||||
|
||||
--add marker
|
||||
worldedit.marker2[name] = minetest.add_entity(pos2, "worldedit:pos2", init_sentinel)
|
||||
if worldedit.marker2[name] ~= nil then
|
||||
worldedit.marker2[name]:get_luaentity().player_name = name
|
||||
end
|
||||
end
|
||||
if region_too == nil or region_too then
|
||||
worldedit.mark_region(name)
|
||||
end
|
||||
end
|
||||
|
||||
worldedit.mark_region = function(name)
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
|
||||
if worldedit.marker_region[name] ~= nil then --marker already exists
|
||||
for _, entity in ipairs(worldedit.marker_region[name]) do
|
||||
entity:remove()
|
||||
end
|
||||
worldedit.marker_region[name] = nil
|
||||
end
|
||||
|
||||
if pos1 ~= nil and pos2 ~= nil then
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
local vec = vector.subtract(pos2, pos1)
|
||||
local maxside = math.max(vec.x, math.max(vec.y, vec.z))
|
||||
local limit = tonumber(minetest.settings:get("active_object_send_range_blocks")) * 16
|
||||
if maxside > limit * 1.5 then
|
||||
-- The client likely won't be able to see the plane markers as intended anyway,
|
||||
-- thus don't place them and also don't load the area into memory
|
||||
return
|
||||
end
|
||||
|
||||
local thickness = 0.2
|
||||
local sizex, sizey, sizez = (1 + pos2.x - pos1.x) / 2, (1 + pos2.y - pos1.y) / 2, (1 + pos2.z - pos1.z) / 2
|
||||
|
||||
-- TODO maybe we could skip this actually?
|
||||
worldedit.keep_loaded(pos1, pos2)
|
||||
|
||||
local markers = {}
|
||||
|
||||
--XY plane markers
|
||||
for _, z in ipairs({pos1.z - 0.5, pos2.z + 0.5}) do
|
||||
local entpos = vector.new(pos1.x + sizex - 0.5, pos1.y + sizey - 0.5, z)
|
||||
local marker = minetest.add_entity(entpos, "worldedit:region_cube", init_sentinel)
|
||||
if marker ~= nil then
|
||||
marker:set_properties({
|
||||
visual_size={x=sizex * 2, y=sizey * 2},
|
||||
collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness},
|
||||
})
|
||||
marker:get_luaentity().player_name = name
|
||||
table.insert(markers, marker)
|
||||
end
|
||||
end
|
||||
|
||||
--YZ plane markers
|
||||
for _, x in ipairs({pos1.x - 0.5, pos2.x + 0.5}) do
|
||||
local entpos = vector.new(x, pos1.y + sizey - 0.5, pos1.z + sizez - 0.5)
|
||||
local marker = minetest.add_entity(entpos, "worldedit:region_cube", init_sentinel)
|
||||
if marker ~= nil then
|
||||
marker:set_properties({
|
||||
visual_size={x=sizez * 2, y=sizey * 2},
|
||||
collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez},
|
||||
})
|
||||
marker:set_yaw(math.pi / 2)
|
||||
marker:get_luaentity().player_name = name
|
||||
table.insert(markers, marker)
|
||||
end
|
||||
end
|
||||
|
||||
worldedit.marker_region[name] = markers
|
||||
end
|
||||
end
|
||||
|
||||
--convenience function that calls everything
|
||||
worldedit.marker_update = function(name)
|
||||
worldedit.mark_pos1(name, false)
|
||||
worldedit.mark_pos2(name, false)
|
||||
worldedit.mark_region(name)
|
||||
end
|
||||
|
||||
minetest.register_entity(":worldedit:pos1", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
textures = {"worldedit_pos1.png", "worldedit_pos1.png",
|
||||
"worldedit_pos1.png", "worldedit_pos1.png",
|
||||
"worldedit_pos1.png", "worldedit_pos1.png"},
|
||||
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
|
||||
physical = false,
|
||||
static_save = false,
|
||||
},
|
||||
on_activate = function(self, staticdata, dtime_s)
|
||||
if staticdata ~= init_sentinel then
|
||||
-- we were loaded from before static_save = false was added
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
self.object:remove()
|
||||
worldedit.marker1[self.player_name] = nil
|
||||
end,
|
||||
on_blast = function(self, damage)
|
||||
return false, false, {} -- don't damage or knockback
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_entity(":worldedit:pos2", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
textures = {"worldedit_pos2.png", "worldedit_pos2.png",
|
||||
"worldedit_pos2.png", "worldedit_pos2.png",
|
||||
"worldedit_pos2.png", "worldedit_pos2.png"},
|
||||
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
|
||||
physical = false,
|
||||
static_save = false,
|
||||
},
|
||||
on_activate = function(self, staticdata, dtime_s)
|
||||
if staticdata ~= init_sentinel then
|
||||
-- we were loaded from before static_save = false was added
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
self.object:remove()
|
||||
worldedit.marker2[self.player_name] = nil
|
||||
end,
|
||||
on_blast = function(self, damage)
|
||||
return false, false, {} -- don't damage or knockback
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_entity(":worldedit:region_cube", {
|
||||
initial_properties = {
|
||||
visual = "upright_sprite",
|
||||
textures = {"worldedit_cube.png"},
|
||||
visual_size = {x=10, y=10},
|
||||
physical = false,
|
||||
static_save = false,
|
||||
},
|
||||
on_activate = function(self, staticdata, dtime_s)
|
||||
if staticdata ~= init_sentinel then
|
||||
-- we were loaded from before static_save = false was added
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
local markers = worldedit.marker_region[self.player_name]
|
||||
if not markers then
|
||||
return
|
||||
end
|
||||
for _, entity in ipairs(markers) do
|
||||
entity:remove()
|
||||
end
|
||||
worldedit.marker_region[self.player_name] = nil
|
||||
end,
|
||||
on_blast = function(self, damage)
|
||||
return false, false, {} -- don't damage or knockback
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
name = worldedit_commands
|
||||
description = WorldEdit chat commands
|
||||
depends = worldedit
|
||||
@@ -0,0 +1,106 @@
|
||||
local strip_escapes = minetest.strip_escapes or function(input)
|
||||
local s = function(idx) return input:sub(idx, idx) end
|
||||
local out = ""
|
||||
local i = 1
|
||||
while i <= #input do
|
||||
if s(i) == "\027" then -- escape sequence
|
||||
i = i + 1
|
||||
if s(i) == "(" then -- enclosed
|
||||
i = i + 1
|
||||
while i <= #input and s(i) ~= ")" do
|
||||
if s(i) == "\\" then
|
||||
i = i + 2
|
||||
else
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
out = out .. s(i)
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
local function string_endswith(full, part)
|
||||
if #full < #part then
|
||||
return false
|
||||
end
|
||||
return full:sub(-#part) == part
|
||||
end
|
||||
|
||||
local function make_description_cache()
|
||||
local t = {}
|
||||
for key, def in pairs(minetest.registered_nodes) do
|
||||
local desc = def.short_description or (def.description or ""):gsub("\n.*", "", 1)
|
||||
desc = strip_escapes(desc):lower()
|
||||
if def.groups.not_in_creative_inventory ~= 1 and desc ~= "" then
|
||||
t[key] = desc
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local description_cache = nil
|
||||
|
||||
-- normalizes node "description" `nodename`, returning a string (or nil)
|
||||
worldedit.normalize_nodename = function(nodename)
|
||||
nodename = nodename:trim()
|
||||
if nodename == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
if nodename:find(" ", 1, true) == nil then
|
||||
local fullname = ItemStack({name=nodename}):get_name() -- resolve aliases
|
||||
if minetest.registered_nodes[fullname] then -- full name
|
||||
return fullname
|
||||
end
|
||||
end
|
||||
|
||||
local match
|
||||
for key, _ in pairs(minetest.registered_nodes) do
|
||||
if string_endswith(key, ":" .. nodename) then
|
||||
if match then
|
||||
match = nil
|
||||
break
|
||||
end
|
||||
match = key -- matches name w/o mod part (only if unique)
|
||||
end
|
||||
end
|
||||
if match then
|
||||
return match
|
||||
end
|
||||
|
||||
nodename = nodename:lower()
|
||||
if description_cache == nil then
|
||||
-- Note: since we don't handle translations this will work only in the original
|
||||
-- language of the description (English)
|
||||
description_cache = make_description_cache()
|
||||
end
|
||||
|
||||
for key, desc in pairs(description_cache) do
|
||||
if desc == nodename then -- matches description
|
||||
return key
|
||||
end
|
||||
end
|
||||
for key, desc in pairs(description_cache) do
|
||||
if desc == nodename .. " block" then
|
||||
-- fuzzy description match (e.g. "Steel" == "Steel Block")
|
||||
return key
|
||||
end
|
||||
end
|
||||
|
||||
match = nil
|
||||
for key, value in pairs(description_cache) do
|
||||
if value:find(nodename, 1, true) ~= nil then
|
||||
if match then
|
||||
match = nil
|
||||
break
|
||||
end
|
||||
match = key -- substring description match (only if unique)
|
||||
end
|
||||
end
|
||||
|
||||
return match
|
||||
end
|
||||
@@ -0,0 +1,276 @@
|
||||
local S = minetest.get_translator("worldedit_commands")
|
||||
|
||||
|
||||
local check_cube = function(param)
|
||||
local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
|
||||
if found == nil then
|
||||
return false
|
||||
end
|
||||
local node = worldedit.normalize_nodename(nodename)
|
||||
if not node then
|
||||
return false, S("invalid node name: @1", nodename)
|
||||
end
|
||||
return true, tonumber(w), tonumber(h), tonumber(l), node
|
||||
end
|
||||
|
||||
worldedit.register_command("hollowcube", {
|
||||
params = "<width> <height> <length> <node>",
|
||||
description = S("Add a hollow cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>."),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = check_cube,
|
||||
nodes_needed = function(name, w, h, l, node)
|
||||
return w * h * l
|
||||
end,
|
||||
func = function(name, w, h, l, node)
|
||||
local count = worldedit.cube(worldedit.pos1[name], w, h, l, node, true)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("cube", {
|
||||
params = "<width> <height> <length> <node>",
|
||||
description = S("Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>."),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = check_cube,
|
||||
nodes_needed = function(name, w, h, l, node)
|
||||
return w * h * l
|
||||
end,
|
||||
func = function(name, w, h, l, node)
|
||||
local count = worldedit.cube(worldedit.pos1[name], w, h, l, node)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
|
||||
local check_sphere = function(param)
|
||||
local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
|
||||
if found == nil then
|
||||
return false
|
||||
end
|
||||
local node = worldedit.normalize_nodename(nodename)
|
||||
if not node then
|
||||
return false, S("invalid node name: @1", nodename)
|
||||
end
|
||||
return true, tonumber(radius), node
|
||||
end
|
||||
|
||||
worldedit.register_command("hollowsphere", {
|
||||
params = "<radius> <node>",
|
||||
description = S("Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>"),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = check_sphere,
|
||||
nodes_needed = function(name, radius, node)
|
||||
return math.ceil((4 * math.pi * (radius ^ 3)) / 3) --volume of sphere
|
||||
end,
|
||||
func = function(name, radius, node)
|
||||
local count = worldedit.sphere(worldedit.pos1[name], radius, node, true)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("sphere", {
|
||||
params = "<radius> <node>",
|
||||
description = S("Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>"),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = check_sphere,
|
||||
nodes_needed = function(name, radius, node)
|
||||
return math.ceil((4 * math.pi * (radius ^ 3)) / 3) --volume of sphere
|
||||
end,
|
||||
func = function(name, radius, node)
|
||||
local count = worldedit.sphere(worldedit.pos1[name], radius, node)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
|
||||
local check_dome = function(param)
|
||||
local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
|
||||
if found == nil then
|
||||
return false
|
||||
end
|
||||
local node = worldedit.normalize_nodename(nodename)
|
||||
if not node then
|
||||
return false, S("invalid node name: @1", nodename)
|
||||
end
|
||||
return true, tonumber(radius), node
|
||||
end
|
||||
|
||||
worldedit.register_command("hollowdome", {
|
||||
params = "<radius> <node>",
|
||||
description = S("Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>"),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = check_dome,
|
||||
nodes_needed = function(name, radius, node)
|
||||
return math.ceil((2 * math.pi * (radius ^ 3)) / 3) --volume of dome
|
||||
end,
|
||||
func = function(name, radius, node)
|
||||
local count = worldedit.dome(worldedit.pos1[name], radius, node, true)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("dome", {
|
||||
params = "<radius> <node>",
|
||||
description = S("Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>"),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = check_dome,
|
||||
nodes_needed = function(name, radius, node)
|
||||
return math.ceil((2 * math.pi * (radius ^ 3)) / 3) --volume of dome
|
||||
end,
|
||||
func = function(name, radius, node)
|
||||
local count = worldedit.dome(worldedit.pos1[name], radius, node)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
|
||||
local check_cylinder = function(param)
|
||||
-- two radii
|
||||
local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
|
||||
if found == nil then
|
||||
-- single radius
|
||||
found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
|
||||
radius2 = radius1
|
||||
end
|
||||
if found == nil then
|
||||
return false
|
||||
end
|
||||
local node = worldedit.normalize_nodename(nodename)
|
||||
if not node then
|
||||
return false, S("invalid node name: @1", nodename)
|
||||
end
|
||||
return true, axis, tonumber(length), tonumber(radius1), tonumber(radius2), node
|
||||
end
|
||||
|
||||
worldedit.register_command("hollowcylinder", {
|
||||
params = "x/y/z/? <length> <radius1> [radius2] <node>",
|
||||
description = S("Add hollow cylinder at WorldEdit position 1 along the given axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>"),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = check_cylinder,
|
||||
nodes_needed = function(name, axis, length, radius1, radius2, node)
|
||||
local radius = math.max(radius1, radius2)
|
||||
return math.ceil(math.pi * (radius ^ 2) * length)
|
||||
end,
|
||||
func = function(name, axis, length, radius1, radius2, node)
|
||||
if axis == "?" then
|
||||
local sign
|
||||
axis, sign = worldedit.player_axis(name)
|
||||
length = length * sign
|
||||
end
|
||||
local count = worldedit.cylinder(worldedit.pos1[name], axis, length, radius1, radius2, node, true)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("cylinder", {
|
||||
params = "x/y/z/? <length> <radius1> [radius2] <node>",
|
||||
description = S("Add cylinder at WorldEdit position 1 along the given axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>"),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = check_cylinder,
|
||||
nodes_needed = function(name, axis, length, radius1, radius2, node)
|
||||
local radius = math.max(radius1, radius2)
|
||||
return math.ceil(math.pi * (radius ^ 2) * length)
|
||||
end,
|
||||
func = function(name, axis, length, radius1, radius2, node)
|
||||
if axis == "?" then
|
||||
local sign
|
||||
axis, sign = worldedit.player_axis(name)
|
||||
length = length * sign
|
||||
end
|
||||
local count = worldedit.cylinder(worldedit.pos1[name], axis, length, radius1, radius2, node)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
|
||||
local check_pyramid = function(param)
|
||||
local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
|
||||
if found == nil then
|
||||
return false
|
||||
end
|
||||
local node = worldedit.normalize_nodename(nodename)
|
||||
if not node then
|
||||
return false, S("invalid node name: @1", nodename)
|
||||
end
|
||||
return true, axis, tonumber(height), node
|
||||
end
|
||||
|
||||
worldedit.register_command("hollowpyramid", {
|
||||
params = "x/y/z/? <height> <node>",
|
||||
description = S("Add hollow pyramid centered at WorldEdit position 1 along the given axis with height <height>, composed of <node>"),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = check_pyramid,
|
||||
nodes_needed = function(name, axis, height, node)
|
||||
return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
|
||||
end,
|
||||
func = function(name, axis, height, node)
|
||||
if axis == "?" then
|
||||
local sign
|
||||
axis, sign = worldedit.player_axis(name)
|
||||
height = height * sign
|
||||
end
|
||||
local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node, true)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("pyramid", {
|
||||
params = "x/y/z/? <height> <node>",
|
||||
description = S("Add pyramid centered at WorldEdit position 1 along the given axis with height <height>, composed of <node>"),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = check_pyramid,
|
||||
nodes_needed = function(name, axis, height, node)
|
||||
return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
|
||||
end,
|
||||
func = function(name, axis, height, node)
|
||||
if axis == "?" then
|
||||
local sign
|
||||
axis, sign = worldedit.player_axis(name)
|
||||
height = height * sign
|
||||
end
|
||||
local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("spiral", {
|
||||
params = "<length> <height> <space> <node>",
|
||||
description = S("Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>"),
|
||||
category = S("Shapes"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = function(param)
|
||||
local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
|
||||
if found == nil then
|
||||
return false
|
||||
end
|
||||
local node = worldedit.normalize_nodename(nodename)
|
||||
if not node then
|
||||
return false, S("invalid node name: @1", nodename)
|
||||
end
|
||||
return true, tonumber(length), tonumber(height), tonumber(space), node
|
||||
end,
|
||||
nodes_needed = function(name, length, height, space, node)
|
||||
return (length + space) * height -- TODO: this is not the upper bound
|
||||
end,
|
||||
func = function(name, length, height, space, node)
|
||||
local count = worldedit.spiral(worldedit.pos1[name], length, height, space, node)
|
||||
return true, S("@1 nodes added", count)
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,241 @@
|
||||
local S = minetest.get_translator("worldedit_commands")
|
||||
|
||||
worldedit.set_pos = {}
|
||||
worldedit.inspect = {}
|
||||
|
||||
|
||||
worldedit.register_command("inspect", {
|
||||
params = "[on/off/1/0/true/false/yes/no/enable/disable]",
|
||||
description = S("Enable or disable node inspection"),
|
||||
privs = {worldedit=true},
|
||||
parse = function(param)
|
||||
if param == "on" or param == "1" or param == "true" or param == "yes" or param == "enable" or param == "" then
|
||||
return true, true
|
||||
elseif param == "off" or param == "0" or param == "false" or param == "no" or param == "disable" then
|
||||
return true, false
|
||||
end
|
||||
return false
|
||||
end,
|
||||
func = function(name, enable)
|
||||
if enable then
|
||||
worldedit.inspect[name] = true
|
||||
local axis, sign = worldedit.player_axis(name)
|
||||
worldedit.player_notify(name, S(
|
||||
"inspector: inspection enabled for @1, currently facing the @2 axis",
|
||||
name,
|
||||
axis .. (sign > 0 and "+" or "-")
|
||||
), "info")
|
||||
else
|
||||
worldedit.inspect[name] = nil
|
||||
worldedit.player_notify(name, S("inspector: inspection disabled"), "info")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local VEC_6DIRS = {
|
||||
vector.new( 1, 0, 0),
|
||||
vector.new(-1, 0, 0),
|
||||
vector.new( 0, 1, 0),
|
||||
vector.new( 0,-1, 0),
|
||||
vector.new( 0, 0, 1),
|
||||
vector.new( 0, 0,-1),
|
||||
}
|
||||
local function get_node_rlight(pos)
|
||||
local ret = 0
|
||||
for _, v in ipairs(VEC_6DIRS) do
|
||||
ret = math.max(ret, minetest.get_node_light(vector.add(pos, v)))
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
minetest.register_on_punchnode(function(pos, node, puncher)
|
||||
local name = puncher:get_player_name()
|
||||
if worldedit.inspect[name] then
|
||||
local axis, sign = worldedit.player_axis(name)
|
||||
local message = S(
|
||||
"inspector: @1 at @2 (param1=@3, param2=@4, received light=@5) punched facing the @6 axis",
|
||||
node.name,
|
||||
minetest.pos_to_string(pos),
|
||||
node.param1,
|
||||
node.param2,
|
||||
get_node_rlight(pos),
|
||||
axis .. (sign > 0 and "+" or "-")
|
||||
)
|
||||
worldedit.player_notify(name, message, "info")
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
worldedit.register_command("mark", {
|
||||
params = "",
|
||||
description = S("Show markers at the region positions"),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
func = function(name)
|
||||
worldedit.marker_update(name)
|
||||
return true, S("region marked")
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("unmark", {
|
||||
params = "",
|
||||
description = S("Hide markers if currently shown"),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
func = function(name)
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
worldedit.pos1[name] = nil
|
||||
worldedit.pos2[name] = nil
|
||||
worldedit.marker_update(name)
|
||||
worldedit.pos1[name] = pos1
|
||||
worldedit.pos2[name] = pos2
|
||||
return true, S("region unmarked")
|
||||
end,
|
||||
})
|
||||
|
||||
local function set_pos1(name, pos)
|
||||
assert(pos)
|
||||
pos = vector.round(pos)
|
||||
worldedit.pos1[name] = pos
|
||||
worldedit.mark_pos1(name)
|
||||
worldedit.player_notify(name, S("position @1 set to @2", 1, minetest.pos_to_string(pos)), "ok")
|
||||
end
|
||||
|
||||
local function set_pos2(name, pos)
|
||||
assert(pos)
|
||||
pos = vector.round(pos)
|
||||
worldedit.pos2[name] = pos
|
||||
worldedit.mark_pos2(name)
|
||||
worldedit.player_notify(name, S("position @1 set to @2", 2, minetest.pos_to_string(pos)), "ok")
|
||||
end
|
||||
|
||||
worldedit.register_command("pos1", {
|
||||
params = "",
|
||||
description = S("Set WorldEdit region position @1 to the player's location", 1),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
func = function(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if not player then return end
|
||||
set_pos1(name, player:get_pos())
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("pos2", {
|
||||
params = "",
|
||||
description = S("Set WorldEdit region position @1 to the player's location", 2),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
func = function(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if not player then return end
|
||||
set_pos2(name, player:get_pos())
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("p", {
|
||||
params = "set/set1/set2/get",
|
||||
description = S("Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region"),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
parse = function(param)
|
||||
if param == "set" or param == "set1" or param == "set2" or param == "get" then
|
||||
return true, param
|
||||
end
|
||||
return false, S("unknown subcommand: @1", param)
|
||||
end,
|
||||
func = function(name, param)
|
||||
local msg
|
||||
if param == "set" then --set both WorldEdit positions
|
||||
worldedit.set_pos[name] = "pos1"
|
||||
msg = S("select positions by punching two nodes")
|
||||
elseif param == "set1" then --set WorldEdit position 1
|
||||
worldedit.set_pos[name] = "pos1only"
|
||||
msg = S("select position @1 by punching a node", 1)
|
||||
elseif param == "set2" then --set WorldEdit position 2
|
||||
worldedit.set_pos[name] = "pos2"
|
||||
msg = S("select position @1 by punching a node", 2)
|
||||
elseif param == "get" then --display current WorldEdit positions
|
||||
if worldedit.pos1[name] ~= nil then
|
||||
msg = S("position @1: @2", 1, minetest.pos_to_string(worldedit.pos1[name]))
|
||||
else
|
||||
msg = S("position @1 not set", 1)
|
||||
end
|
||||
msg = msg .. "\n"
|
||||
if worldedit.pos2[name] ~= nil then
|
||||
msg = msg .. S("position @1: @2", 2, minetest.pos_to_string(worldedit.pos2[name]))
|
||||
else
|
||||
msg = msg .. S("position @1 not set", 2)
|
||||
end
|
||||
end
|
||||
if msg then
|
||||
worldedit.player_notify(name, msg, "info")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("fixedpos", {
|
||||
params = "set1/set2 <x> <y> <z>",
|
||||
description = S("Set a WorldEdit region position to the position at (<x>, <y>, <z>)"),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
parse = function(param)
|
||||
local found, _, flag, x, y, z = param:find("^(set[12])%s+(~?[+-]?%d+)%s+(~?[+-]?%d+)%s+(~?[+-]?%d+)$")
|
||||
if not found then
|
||||
return false
|
||||
end
|
||||
return true, flag, x, y, z
|
||||
end,
|
||||
func = function(name, flag, x, y, z)
|
||||
-- Parse here, since player name isn't known in parse()
|
||||
local pos = worldedit.parse_coordinates(x, y, z, name)
|
||||
if not pos then
|
||||
return false, S("invalid position")
|
||||
end
|
||||
if flag == "set1" then
|
||||
set_pos1(name, pos)
|
||||
else --flag == "set2"
|
||||
set_pos2(name, pos)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_on_punchnode(function(pos, node, puncher)
|
||||
local name = puncher:get_player_name()
|
||||
if name ~= "" and worldedit.set_pos[name] ~= nil then --currently setting position
|
||||
if worldedit.set_pos[name] == "pos1" then --setting position 1
|
||||
set_pos1(name, pos)
|
||||
worldedit.set_pos[name] = "pos2" --set position 2 on the next invocation
|
||||
elseif worldedit.set_pos[name] == "pos1only" then --setting position 1 only
|
||||
set_pos1(name, pos)
|
||||
worldedit.set_pos[name] = nil --finished setting positions
|
||||
elseif worldedit.set_pos[name] == "pos2" then --setting position 2
|
||||
set_pos2(name, pos)
|
||||
worldedit.set_pos[name] = nil --finished setting positions
|
||||
elseif worldedit.set_pos[name] == "prob" then --setting Minetest schematic node probabilities
|
||||
worldedit.prob_pos[name] = pos
|
||||
minetest.show_formspec(name, "prob_val_enter", "field[text;;]")
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
worldedit.register_command("volume", {
|
||||
params = "",
|
||||
description = S("Display the volume of the current WorldEdit region"),
|
||||
category = S("Region operations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
func = function(name)
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
|
||||
local volume = worldedit.volume(pos1, pos2)
|
||||
local abs = math.abs
|
||||
worldedit.player_notify(name, S(
|
||||
"current region has a volume of @1 nodes (@2*@3*@4)",
|
||||
volume,
|
||||
abs(pos2.x - pos1.x) + 1,
|
||||
abs(pos2.y - pos1.y) + 1,
|
||||
abs(pos2.z - pos1.z) + 1
|
||||
), "info")
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,63 @@
|
||||
local S = minetest.get_translator("worldedit_commands")
|
||||
|
||||
local safe_region_limit = tonumber(minetest.settings:get("worldedit_safe_region_limit") or "20000")
|
||||
|
||||
local safe_region_callback = {}
|
||||
|
||||
--`count` is the number of nodes that would possibly be modified
|
||||
--`callback` is a callback to run when the user confirms
|
||||
local function safe_region(name, count, callback)
|
||||
if safe_region_limit <= 0 or count < safe_region_limit then
|
||||
return callback()
|
||||
end
|
||||
|
||||
-- save callback to call later
|
||||
safe_region_callback[name] = callback
|
||||
|
||||
local count_str = tostring(count)
|
||||
-- highlight millions, 1 mln <=> 100x100x100 cube
|
||||
if #count_str > 6 then
|
||||
count_str = minetest.colorize("#f33", count_str:sub(1, -7)) .. count_str:sub(-6, -1)
|
||||
end
|
||||
|
||||
local yes_cmd = minetest.colorize("#0ff", "//y")
|
||||
local no_cmd = minetest.colorize("#0ff", "//n")
|
||||
local msg = S("WARNING: this operation could affect up to @1 nodes; type @2 to continue or @3 to cancel",
|
||||
count_str, yes_cmd, no_cmd)
|
||||
worldedit.player_notify(name, msg, "info")
|
||||
end
|
||||
|
||||
local function reset_pending(name)
|
||||
safe_region_callback[name] = nil
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("/y", {
|
||||
params = "",
|
||||
description = S("Confirm a pending operation"),
|
||||
func = function(name)
|
||||
local callback = safe_region_callback[name]
|
||||
if not callback then
|
||||
worldedit.player_notify(name, S("no operation pending"), "error")
|
||||
return
|
||||
end
|
||||
|
||||
reset_pending(name)
|
||||
callback(name)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("/n", {
|
||||
params = "",
|
||||
description = S("Abort a pending operation"),
|
||||
func = function(name)
|
||||
if not safe_region_callback[name] then
|
||||
worldedit.player_notify(name, S("no operation pending"), "error")
|
||||
return
|
||||
end
|
||||
|
||||
reset_pending(name)
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
return safe_region, reset_pending
|
||||
@@ -0,0 +1,284 @@
|
||||
local S = minetest.get_translator("worldedit_commands")
|
||||
|
||||
worldedit.prob_pos = {}
|
||||
worldedit.prob_list = {}
|
||||
|
||||
local function check_region(name)
|
||||
return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
|
||||
end
|
||||
|
||||
local function check_filename(name)
|
||||
return name:find("^[%w%s%^&'@{}%[%],%$=!%-#%(%)%%%.%+~_]+$") ~= nil
|
||||
end
|
||||
|
||||
local function open_schematic(name, param)
|
||||
-- find the file in the world path
|
||||
local testpaths = {
|
||||
minetest.get_worldpath() .. "/schems/" .. param,
|
||||
minetest.get_worldpath() .. "/schems/" .. param .. ".we",
|
||||
minetest.get_worldpath() .. "/schems/" .. param .. ".wem",
|
||||
}
|
||||
local file, err
|
||||
for index, path in ipairs(testpaths) do
|
||||
file, err = io.open(path, "rb")
|
||||
if not err then
|
||||
break
|
||||
end
|
||||
end
|
||||
if err then
|
||||
worldedit.player_notify(name, S("Could not open file \"@1\"", param), "error")
|
||||
return
|
||||
end
|
||||
local value = file:read("*a")
|
||||
file:close()
|
||||
|
||||
local version = worldedit.read_header(value)
|
||||
if version == nil or version == 0 then
|
||||
worldedit.player_notify(name, S("Invalid file format!"), "error")
|
||||
return
|
||||
elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
|
||||
worldedit.player_notify(name, S("Schematic was created with a newer version of WorldEdit."), "error")
|
||||
return
|
||||
end
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
local function detect_misaligned_schematic(name, pos1, pos2)
|
||||
pos1 = worldedit.sort_pos(pos1, pos2)
|
||||
-- Check that allocate/save can position the schematic correctly
|
||||
-- The expected behaviour is that the (0,0,0) corner of the schematic stays
|
||||
-- at pos1, this only works when the minimum position is actually present
|
||||
-- in the schematic.
|
||||
local node = minetest.get_node(pos1)
|
||||
local have_node_at_origin = node.name ~= "air" and node.name ~= "ignore"
|
||||
if not have_node_at_origin then
|
||||
worldedit.player_notify(name,
|
||||
S("Warning: The schematic contains excessive free space and WILL be "..
|
||||
"misaligned when allocated or loaded. To avoid this, shrink your "..
|
||||
"area to cover exactly the nodes to be saved.")
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
worldedit.register_command("save", {
|
||||
params = "<file>",
|
||||
description = S("Save the current WorldEdit region to \"(world folder)/schems/<file>.we\""),
|
||||
category = S("Schematics"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
if param == "" then
|
||||
return false
|
||||
end
|
||||
if not check_filename(param) then
|
||||
return false, S("Disallowed file name: @1", param)
|
||||
end
|
||||
return true, param
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, param)
|
||||
local result, count = worldedit.serialize(worldedit.pos1[name],
|
||||
worldedit.pos2[name])
|
||||
detect_misaligned_schematic(name, worldedit.pos1[name], worldedit.pos2[name])
|
||||
|
||||
local path = minetest.get_worldpath() .. "/schems"
|
||||
-- Create directory if it does not already exist
|
||||
minetest.mkdir(path)
|
||||
|
||||
local filename = path .. "/" .. param .. ".we"
|
||||
local file, err = io.open(filename, "wb")
|
||||
if err ~= nil then
|
||||
return false, S("Could not save file to \"@1\"", filename)
|
||||
end
|
||||
file:write(result)
|
||||
file:flush()
|
||||
file:close()
|
||||
|
||||
return true, S("@1 nodes saved", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("allocate", {
|
||||
params = "<file>",
|
||||
description = S("Set the region defined by nodes from \"(world folder)/schems/<file>.we\" as the current WorldEdit region"),
|
||||
category = S("Schematics"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = function(param)
|
||||
if param == "" then
|
||||
return false
|
||||
end
|
||||
if not check_filename(param) then
|
||||
return false, S("Disallowed file name: @1", param)
|
||||
end
|
||||
return true, param
|
||||
end,
|
||||
func = function(name, param)
|
||||
local pos = worldedit.pos1[name]
|
||||
|
||||
local value = open_schematic(name, param)
|
||||
if not value then
|
||||
return false
|
||||
end
|
||||
|
||||
local nodepos1, nodepos2, count = worldedit.allocate(pos, value)
|
||||
if not nodepos1 then
|
||||
return false, S("Schematic empty, nothing allocated")
|
||||
end
|
||||
|
||||
worldedit.pos1[name] = nodepos1
|
||||
worldedit.pos2[name] = nodepos2
|
||||
worldedit.marker_update(name)
|
||||
|
||||
return true, S("@1 nodes allocated", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("load", {
|
||||
params = "<file>",
|
||||
description = S("Load nodes from \"(world folder)/schems/<file>[.we[m]]\" with position 1 of the current WorldEdit region as the origin"),
|
||||
category = S("Schematics"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = function(param)
|
||||
if param == "" then
|
||||
return false
|
||||
end
|
||||
if not check_filename(param) then
|
||||
return false, S("Disallowed file name: @1", param)
|
||||
end
|
||||
return true, param
|
||||
end,
|
||||
func = function(name, param)
|
||||
local pos = worldedit.pos1[name]
|
||||
|
||||
local value = open_schematic(name, param)
|
||||
if not value then
|
||||
return false
|
||||
end
|
||||
|
||||
local count = worldedit.deserialize(pos, value)
|
||||
if count == nil then
|
||||
return false, S("Loading failed!")
|
||||
end
|
||||
return true, S("@1 nodes loaded", count)
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
worldedit.register_command("mtschemcreate", {
|
||||
params = "<file>",
|
||||
description = S("Save the current WorldEdit region using the Minetest "..
|
||||
"Schematic format to \"(world folder)/schems/<filename>.mts\""),
|
||||
category = S("Schematics"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
if param == "" then
|
||||
return false
|
||||
end
|
||||
if not check_filename(param) then
|
||||
return false, S("Disallowed file name: @1", param)
|
||||
end
|
||||
return true, param
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, param)
|
||||
local path = minetest.get_worldpath() .. "/schems"
|
||||
-- Create directory if it does not already exist
|
||||
minetest.mkdir(path)
|
||||
|
||||
local filename = path .. "/" .. param .. ".mts"
|
||||
local ret = minetest.create_schematic(worldedit.pos1[name],
|
||||
worldedit.pos2[name], worldedit.prob_list[name],
|
||||
filename)
|
||||
worldedit.prob_list[name] = {}
|
||||
if ret == nil then
|
||||
return false, S("Failed to create Minetest schematic")
|
||||
end
|
||||
return true, S("Saved Minetest schematic to @1", param)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("mtschemplace", {
|
||||
params = "<file>",
|
||||
description = S("Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin"),
|
||||
category = S("Schematics"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 1,
|
||||
parse = function(param)
|
||||
local found, _, filename, rotation = param:find("^(.+)%s+([012789]+)$")
|
||||
if found == nil then
|
||||
filename = param
|
||||
elseif rotation ~= "0" and rotation ~= "90" and rotation ~= "180" and rotation ~= "270" then
|
||||
return false, S("Invalid rotation: @1", rotation)
|
||||
end
|
||||
if not check_filename(filename) then
|
||||
return false, S("Disallowed file name: @1", filename)
|
||||
end
|
||||
return true, filename, rotation
|
||||
end,
|
||||
func = function(name, filename, rotation)
|
||||
local pos = worldedit.pos1[name]
|
||||
|
||||
local path = minetest.get_worldpath() .. "/schems/" .. filename .. ".mts"
|
||||
if minetest.place_schematic(pos, path, rotation) == nil then
|
||||
return false, S("failed to place Minetest schematic")
|
||||
end
|
||||
return true, S("placed Minetest schematic @1 at @2",
|
||||
filename, minetest.pos_to_string(pos))
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("mtschemprob", {
|
||||
params = "start/finish/get",
|
||||
description = S("Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry"),
|
||||
category = S("Schematics"),
|
||||
privs = {worldedit=true},
|
||||
parse = function(param)
|
||||
if param ~= "start" and param ~= "finish" and param ~= "get" then
|
||||
return false, S("unknown subcommand: @1", param)
|
||||
end
|
||||
return true, param
|
||||
end,
|
||||
func = function(name, param)
|
||||
if param == "start" then --start probability setting
|
||||
worldedit.set_pos[name] = "prob"
|
||||
worldedit.prob_list[name] = {}
|
||||
worldedit.player_notify(name, S("select Minetest schematic probability values by punching nodes"), "info")
|
||||
elseif param == "finish" then --finish probability setting
|
||||
worldedit.set_pos[name] = nil
|
||||
worldedit.player_notify(name, S("finished Minetest schematic probability selection"), "info")
|
||||
elseif param == "get" then --get all nodes that had probabilities set on them
|
||||
local text = ""
|
||||
local problist = worldedit.prob_list[name]
|
||||
if problist == nil then
|
||||
return
|
||||
end
|
||||
for k,v in pairs(problist) do
|
||||
local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100
|
||||
text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "
|
||||
end
|
||||
worldedit.player_notify(name, S("currently set node probabilities:") .. "\n" .. text, "info")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname == "prob_val_enter" then
|
||||
local name = player:get_player_name()
|
||||
local problist = worldedit.prob_list[name]
|
||||
if problist == nil then
|
||||
return
|
||||
end
|
||||
local e = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
|
||||
if e.pos == nil or e.prob == nil or e.prob < 0 or e.prob > 256 then
|
||||
worldedit.player_notify(name, S("invalid node probability given, not saved"), "error")
|
||||
return
|
||||
end
|
||||
problist[#problist+1] = e
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
local register_test = worldedit.register_test
|
||||
|
||||
-- Basic test that just checks if certain parameter combinations
|
||||
-- parse correctly (valid or invalid)
|
||||
local make_parsing_test = function(cmd, valid, invalid)
|
||||
return function()
|
||||
local def = worldedit.registered_commands[cmd]
|
||||
assert(def, "Command not defined")
|
||||
for _, param in ipairs(valid or {}) do
|
||||
local parsed = {def.parse(param)}
|
||||
assert(parsed[1], string.format("Did not parse: %q", param))
|
||||
end
|
||||
for _, param in ipairs(invalid or {}) do
|
||||
local parsed = {def.parse(param)}
|
||||
assert(not parsed[1], string.format("Did parse: %q", param))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
register_test("Command parsing")
|
||||
register_test("//set", make_parsing_test("set", {
|
||||
"air",
|
||||
"mapgen_stone",
|
||||
minetest.registered_aliases["mapgen_dirt"],
|
||||
}, {
|
||||
"this long text could not possibly ever match a node",
|
||||
"",
|
||||
}))
|
||||
|
||||
register_test("//mix", make_parsing_test("mix", {
|
||||
"air",
|
||||
"air 2",
|
||||
"air mapgen_stone",
|
||||
"air 2 air 1 mapgen_stone 1",
|
||||
}, {
|
||||
"this_will_never_match_any_node",
|
||||
"air 1 this_will_never_match_any_node",
|
||||
"air this_will_never_match_any_node",
|
||||
"",
|
||||
}))
|
||||
|
||||
register_test("//fixedpos", make_parsing_test("fixedpos", {
|
||||
"set1 0 0 0",
|
||||
"set2 -10 20 31000",
|
||||
"set1 ~0 ~0 ~0",
|
||||
"set2 ~-5 2 ~+2",
|
||||
}, {
|
||||
"set1 0 0",
|
||||
"set 1 2 3",
|
||||
"set2 ~ ~ ~",
|
||||
"set2 + 0 0",
|
||||
"",
|
||||
}))
|
||||
|
||||
register_test("//copy", make_parsing_test("copy", {
|
||||
"x 10",
|
||||
"right +1",
|
||||
"? -4",
|
||||
}, {
|
||||
"eee 1",
|
||||
"up 0",
|
||||
"",
|
||||
}))
|
||||
|
||||
register_test("//rotate", make_parsing_test("rotate", {
|
||||
"z 90",
|
||||
"left -180",
|
||||
}, {
|
||||
"x 0",
|
||||
"back 77",
|
||||
"",
|
||||
}))
|
||||
|
||||
register_test("//flip", make_parsing_test("flip", {
|
||||
"y",
|
||||
"down",
|
||||
"?",
|
||||
}, {
|
||||
"1",
|
||||
"",
|
||||
}))
|
||||
|
||||
|
||||
register_test("//inset", make_parsing_test("inset", {
|
||||
"h 1",
|
||||
"v 0",
|
||||
"hv 2",
|
||||
"vh 3",
|
||||
}, {
|
||||
"x 4",
|
||||
"xyz 5",
|
||||
"v foo",
|
||||
"",
|
||||
}))
|
||||
|
||||
register_test("//shift", make_parsing_test("shift", {
|
||||
"x 1",
|
||||
"x -4",
|
||||
"back 1",
|
||||
"? 1",
|
||||
}, {
|
||||
"+z 1212",
|
||||
"-z 9",
|
||||
"xx -5",
|
||||
"?? 123",
|
||||
"",
|
||||
}))
|
||||
|
||||
register_test("//expand", make_parsing_test("expand", {
|
||||
"x 1",
|
||||
"z 1 2",
|
||||
"? 1",
|
||||
"+? 1",
|
||||
"+left 1",
|
||||
"-right 1",
|
||||
}, {
|
||||
"x -4",
|
||||
"? 4 -333",
|
||||
"stupid 5 5",
|
||||
"",
|
||||
}))
|
||||
|
||||
register_test("//cubeapply", make_parsing_test("cubeapply", {
|
||||
"2 orient 90",
|
||||
"2 3 4 orient 90",
|
||||
"1 1 1 drain",
|
||||
"4 stack z 1",
|
||||
}, {
|
||||
"1 1 1 orient",
|
||||
"0 drain",
|
||||
"4 stack z",
|
||||
"2 2 2 asasasasasas",
|
||||
"",
|
||||
}))
|
||||
|
||||
register_test("//save", make_parsing_test("save", {
|
||||
"filename",
|
||||
"filename.abc",
|
||||
}, {
|
||||
"\"hmm",
|
||||
"../../oops",
|
||||
"",
|
||||
}))
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 79 B |
Binary file not shown.
|
After Width: | Height: | Size: 100 B |
Binary file not shown.
|
After Width: | Height: | Size: 112 B |
Binary file not shown.
|
After Width: | Height: | Size: 145 B |
@@ -0,0 +1,274 @@
|
||||
local S = minetest.get_translator("worldedit_commands")
|
||||
|
||||
local VALID_DIR = worldedit.valid_directions
|
||||
|
||||
local function check_region(name)
|
||||
return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
|
||||
end
|
||||
|
||||
local function parse_copylike(param)
|
||||
local found, _, direction, amount = param:find("^([^%s]+)%s+([+-]?%d+)$")
|
||||
if found == nil or not VALID_DIR[direction] then
|
||||
return false
|
||||
end
|
||||
if tonumber(amount) == 0 then
|
||||
return false
|
||||
end
|
||||
return true, direction, tonumber(amount)
|
||||
end
|
||||
|
||||
worldedit.register_command("copy", {
|
||||
params = tostring(VALID_DIR) .. " <amount>",
|
||||
description = S("Copy the current WorldEdit region along the given axis by <amount> nodes"),
|
||||
category = S("Transformations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = parse_copylike,
|
||||
nodes_needed = function(name, direction, amount)
|
||||
return check_region(name) * 2
|
||||
end,
|
||||
func = function(name, direction, amount)
|
||||
local axis, sign = worldedit.player_direction(name, direction)
|
||||
if not axis or not sign then
|
||||
return false, S("Invalid if looking straight up or down")
|
||||
end
|
||||
amount = amount * sign
|
||||
|
||||
local count = worldedit.copy(worldedit.pos1[name], worldedit.pos2[name], axis, amount)
|
||||
return true, S("@1 nodes copied", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("move", {
|
||||
params = tostring(VALID_DIR) .. " <amount>",
|
||||
description = S("Move the current WorldEdit region along the given axis by <amount> nodes"),
|
||||
category = S("Transformations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = parse_copylike,
|
||||
nodes_needed = function(name, direction, amount)
|
||||
return check_region(name) * 2
|
||||
end,
|
||||
func = function(name, direction, amount)
|
||||
local axis, sign = worldedit.player_direction(name, direction)
|
||||
if not axis or not sign then
|
||||
return false, S("Invalid if looking straight up or down")
|
||||
end
|
||||
amount = amount * sign
|
||||
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
local count = worldedit.move(pos1, pos2, axis, amount)
|
||||
|
||||
pos1[axis] = pos1[axis] + amount
|
||||
pos2[axis] = pos2[axis] + amount
|
||||
worldedit.marker_update(name)
|
||||
return true, S("@1 nodes moved", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("stack", {
|
||||
params = tostring(VALID_DIR) .. " <count>",
|
||||
description = S("Stack the current WorldEdit region along the given axis <count> times"),
|
||||
category = S("Transformations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = parse_copylike,
|
||||
nodes_needed = function(name, direction, repetitions)
|
||||
return check_region(name) * math.abs(repetitions)
|
||||
end,
|
||||
func = function(name, direction, repetitions)
|
||||
local axis, sign = worldedit.player_direction(name, direction)
|
||||
if not axis or not sign then
|
||||
return false, S("Invalid if looking straight up or down")
|
||||
end
|
||||
repetitions = repetitions * sign
|
||||
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
local count = worldedit.volume(pos1, pos2) * math.abs(repetitions)
|
||||
worldedit.stack(pos1, pos2, axis, repetitions, function()
|
||||
worldedit.player_notify(name, S("@1 nodes stacked", count), "ok")
|
||||
end)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("stack2", {
|
||||
params = "<count> <x> <y> <z>",
|
||||
description = S("Stack the current WorldEdit region <count> times by offset <x>, <y>, <z>"),
|
||||
category = S("Transformations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local repetitions, incs = param:match("(%d+)%s*(.+)")
|
||||
if repetitions == nil then
|
||||
return false, S("invalid count: @1", param)
|
||||
end
|
||||
local x, y, z = incs:match("([+-]?%d+) ([+-]?%d+) ([+-]?%d+)")
|
||||
if x == nil then
|
||||
return false, S("invalid increments: @1", param)
|
||||
end
|
||||
|
||||
return true, tonumber(repetitions), vector.new(tonumber(x), tonumber(y), tonumber(z))
|
||||
end,
|
||||
nodes_needed = function(name, repetitions, offset)
|
||||
return check_region(name) * repetitions
|
||||
end,
|
||||
func = function(name, repetitions, offset)
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
local count = worldedit.volume(pos1, pos2) * repetitions
|
||||
worldedit.stack2(pos1, pos2, offset, repetitions, function()
|
||||
worldedit.player_notify(name, S("@1 nodes stacked", count), "ok")
|
||||
end)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("stretch", {
|
||||
params = "<stretchx> <stretchy> <stretchz>",
|
||||
description = S("Scale the current WorldEdit positions and region by a factor of <stretchx>, <stretchy>, <stretchz> along the X, Y, and Z axes, repectively, with position 1 as the origin"),
|
||||
category = S("Transformations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")
|
||||
if found == nil then
|
||||
return false
|
||||
end
|
||||
stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)
|
||||
if stretchx == 0 or stretchy == 0 or stretchz == 0 then
|
||||
return false, S("invalid scaling factors: @1", param)
|
||||
end
|
||||
return true, stretchx, stretchy, stretchz
|
||||
end,
|
||||
nodes_needed = function(name, stretchx, stretchy, stretchz)
|
||||
return check_region(name) * stretchx * stretchy * stretchz
|
||||
end,
|
||||
func = function(name, stretchx, stretchy, stretchz)
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
local count
|
||||
count, pos1, pos2 = worldedit.stretch(pos1, pos2, stretchx, stretchy, stretchz)
|
||||
|
||||
-- reset markers to scaled positions
|
||||
worldedit.pos1[name] = pos1
|
||||
worldedit.pos2[name] = pos2
|
||||
worldedit.marker_update(name)
|
||||
|
||||
return true, S("@1 nodes stretched", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("transpose", {
|
||||
params = "x/y/z/? x/y/z/?",
|
||||
description = S("Transpose the current WorldEdit region along the given axes"),
|
||||
category = S("Transformations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
|
||||
if found == nil then
|
||||
return false
|
||||
elseif axis1 == axis2 then
|
||||
return false, S("invalid usage: axes must be different")
|
||||
end
|
||||
return true, axis1, axis2
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, axis1, axis2)
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
if axis1 == "?" then axis1 = worldedit.player_axis(name) end
|
||||
if axis2 == "?" then axis2 = worldedit.player_axis(name) end
|
||||
local count
|
||||
count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
|
||||
|
||||
-- reset markers to transposed positions
|
||||
worldedit.pos1[name] = pos1
|
||||
worldedit.pos2[name] = pos2
|
||||
worldedit.marker_update(name)
|
||||
|
||||
return true, S("@1 nodes transposed", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("flip", {
|
||||
params = tostring(VALID_DIR),
|
||||
description = S("Flip the current WorldEdit region along the given axis"),
|
||||
category = S("Transformations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
if not VALID_DIR[param] then
|
||||
return false
|
||||
end
|
||||
return true, param
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, param)
|
||||
local axis = worldedit.player_direction(name, param)
|
||||
if axis == nil then
|
||||
return false, S("Invalid if looking straight up or down")
|
||||
end
|
||||
|
||||
local count = worldedit.flip(worldedit.pos1[name], worldedit.pos2[name], axis)
|
||||
return true, S("@1 nodes flipped", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("rotate", {
|
||||
params = tostring(VALID_DIR) .. " <angle>",
|
||||
description = S("Rotate the current WorldEdit region around the given axis by angle <angle> (90 degree increment)"),
|
||||
category = S("Transformations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local found, _, direction, angle = param:find("^([^%s]+)%s+([+-]?%d+)$")
|
||||
if found == nil or not VALID_DIR[direction] then
|
||||
return false
|
||||
end
|
||||
angle = tonumber(angle)
|
||||
if angle % 90 ~= 0 or angle % 360 == 0 then
|
||||
return false, S("invalid usage: angle must be multiple of 90")
|
||||
end
|
||||
return true, direction, angle
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, direction, angle)
|
||||
local axis = worldedit.player_direction(name, direction)
|
||||
if axis == nil then
|
||||
return false, S("Invalid if looking straight up or down")
|
||||
end
|
||||
|
||||
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
|
||||
local count
|
||||
count, pos1, pos2 = worldedit.rotate(pos1, pos2, axis, angle)
|
||||
|
||||
-- reset markers to rotated positions
|
||||
worldedit.pos1[name] = pos1
|
||||
worldedit.pos2[name] = pos2
|
||||
worldedit.marker_update(name)
|
||||
|
||||
return true, S("@1 nodes rotated", count)
|
||||
end,
|
||||
})
|
||||
|
||||
worldedit.register_command("orient", {
|
||||
params = "<angle>",
|
||||
description = S("Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)"),
|
||||
category = S("Transformations"),
|
||||
privs = {worldedit=true},
|
||||
require_pos = 2,
|
||||
parse = function(param)
|
||||
local found, _, angle = param:find("^([+-]?%d+)$")
|
||||
if found == nil then
|
||||
return false
|
||||
end
|
||||
angle = tonumber(angle)
|
||||
if angle % 90 ~= 0 then
|
||||
return false, S("invalid usage: angle must be multiple of 90")
|
||||
end
|
||||
return true, angle
|
||||
end,
|
||||
nodes_needed = check_region,
|
||||
func = function(name, angle)
|
||||
local count = worldedit.orient(worldedit.pos1[name], worldedit.pos2[name], angle)
|
||||
return true, S("@1 nodes oriented", count)
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
local S = minetest.get_translator("worldedit_commands")
|
||||
|
||||
local function above_or_under(placer, pointed_thing)
|
||||
if placer:get_player_control().sneak then
|
||||
return pointed_thing.above
|
||||
else
|
||||
return pointed_thing.under
|
||||
end
|
||||
end
|
||||
|
||||
local punched_air_time = {}
|
||||
|
||||
minetest.register_tool(":worldedit:wand", {
|
||||
description = S("WorldEdit Wand tool") .. "\n" .. S("Left-click to set 1st position, right-click to set 2nd"),
|
||||
inventory_image = "worldedit_wand.png",
|
||||
stack_max = 1, -- there is no need to have more than one
|
||||
liquids_pointable = true, -- ground with only water on can be selected as well
|
||||
|
||||
-- ignore marker cube so the clicking on the position markers works reliably
|
||||
pointabilities = {
|
||||
objects = {
|
||||
["worldedit:region_cube"] = false
|
||||
}
|
||||
},
|
||||
|
||||
on_use = function(itemstack, placer, pointed_thing)
|
||||
if placer == nil or pointed_thing == nil then return end
|
||||
local name = placer:get_player_name()
|
||||
if pointed_thing.type == "node" then
|
||||
-- set and mark pos1
|
||||
worldedit.pos1[name] = above_or_under(placer, pointed_thing)
|
||||
worldedit.mark_pos1(name)
|
||||
elseif pointed_thing.type == "nothing" then
|
||||
local now = minetest.get_us_time()
|
||||
if now - (punched_air_time[name] or 0) < 1000 * 1000 then
|
||||
-- reset markers
|
||||
worldedit.registered_commands["reset"].func(name)
|
||||
end
|
||||
punched_air_time[name] = now
|
||||
elseif pointed_thing.type == "object" then
|
||||
local entity = pointed_thing.ref:get_luaentity()
|
||||
if entity and entity.name == "worldedit:pos2" then
|
||||
-- set pos1 = pos2
|
||||
worldedit.pos1[name] = vector.copy(worldedit.pos2[name])
|
||||
worldedit.mark_pos1(name)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if placer == nil or (pointed_thing or {}).type ~= "node" then
|
||||
return itemstack
|
||||
end
|
||||
local name = placer:get_player_name()
|
||||
-- set and mark pos2
|
||||
worldedit.pos2[name] = above_or_under(placer, pointed_thing)
|
||||
worldedit.mark_pos2(name)
|
||||
return itemstack -- nothing consumed, nothing changed
|
||||
end,
|
||||
|
||||
on_secondary_use = function(itemstack, user, pointed_thing)
|
||||
if user == nil or (pointed_thing or {}).type ~= "object" then
|
||||
return itemstack
|
||||
end
|
||||
local name = user:get_player_name()
|
||||
local entity = pointed_thing.ref:get_luaentity()
|
||||
if entity and entity.name == "worldedit:pos1" then
|
||||
-- set pos2 = pos1
|
||||
worldedit.pos2[name] = vector.copy(worldedit.pos1[name])
|
||||
worldedit.mark_pos2(name)
|
||||
end
|
||||
return itemstack -- nothing consumed, nothing changed
|
||||
end,
|
||||
})
|
||||
Reference in New Issue
Block a user