1
0
mirror of https://github.com/Uberi/Minetest-WorldEdit.git synced 2025-06-29 14:40:54 +02:00

8 Commits

Author SHA1 Message Date
d42ac33d62 fix 2021-09-12 20:02:56 +02:00
5b19c17117 Revert "Warn when "misaligned" schematics are created"
This reverts commit 68f7bcc728.
2021-09-12 20:02:54 +02:00
4de0eb489f implement loading 2021-09-12 20:02:12 +02:00
adc6e00423 wip 2021-09-12 20:00:41 +02:00
5c06ae59ef wip 2021-09-12 20:00:41 +02:00
3c5f6b9665 wip 2021-09-12 20:00:41 +02:00
e387a57e15 wip 2021-09-12 20:00:41 +02:00
f43bc5278e wip 2021-09-12 20:00:41 +02:00
52 changed files with 2356 additions and 3856 deletions

View File

@ -1,15 +0,0 @@
name: "Check"
on: [push, pull_request]
jobs:
lint:
name: "Luacheck"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: apt
run: sudo apt-get install -y luarocks
- name: luacheck install
run: luarocks install --local luacheck
- name: luacheck run
run: $HOME/.luarocks/bin/luacheck ./

View File

@ -1,28 +0,0 @@
name: "Test"
on: [push, pull_request]
jobs:
test:
name: "Unit Tests ${{ matrix.cfg.image }}"
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
matrix:
cfg:
- { image: 'registry.gitlab.com/minetest/minetest/server:5.0.1', mtg: false }
- { image: 'registry.gitlab.com/minetest/minetest/server:5.5.1', mtg: false }
- { image: 'ghcr.io/minetest/minetest:5.10.0', mtg: true }
- { image: 'ghcr.io/luanti-org/luanti:master', mtg: true } # latest git
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: 'luanti-org/minetest_game'
path: minetest_game
if: ${{ matrix.cfg.mtg }}
- name: Run tests
run: ./.util/run_tests.sh --docker
env:
DOCKER_IMAGE: "${{ matrix.cfg.image }}"

View File

@ -1,28 +0,0 @@
read_globals = {
"minetest", "VoxelArea", "ItemStack",
"unified_inventory", "sfinv", "smart_inventory", "inventory_plus",
"dump",
table = {fields = {"copy", "indexof", "insert_all"}},
vector = {fields = {
-- as of 5.0
"new", "direction", "distance", "length", "normalize", "floor", "round",
"apply", "equals", "sort", "add", "subtract", "multiply", "divide",
-- polyfilled
"copy"
}},
}
globals = {"worldedit"}
-- Ignore these errors until someone decides to fix them
ignore = {"212", "213", "411", "412", "421", "422", "431", "432", "631"}
files["worldedit/common.lua"] = {
globals = {"vector"},
}
files["worldedit/test"] = {
read_globals = {"testnode1", "testnode2", "testnode3", "area", "check", "place_pattern"},
}
files["worldedit/test/init.lua"] = {
globals = {"testnode1", "testnode2", "testnode3", "area", "check", "place_pattern"},
}

View File

@ -1,42 +0,0 @@
#!/bin/bash
tempdir=$(mktemp -d)
confpath=$tempdir/minetest.conf
worldpath=$tempdir/world
trap 'rm -rf "$tempdir"' EXIT
[ -f worldedit/mod.conf ] || { echo "Must be run in modpack root folder." >&2; exit 1; }
mtserver=
if [ "$1" == "--docker" ]; then
command -v docker >/dev/null || { echo "Docker is not installed." >&2; exit 1; }
[ -d minetest_game ] || echo "A source checkout of minetest_game was not found. This can fail if your docker image does not ship a game." >&2;
else
mtserver=$(command -v luantiserver)
[[ -z "$mtserver" && -x ../../bin/luantiserver ]] && mtserver=../../bin/luantiserver
[ -z "$mtserver" ] && { echo "To run the test outside of Docker, an installation of luantiserver is required." >&2; exit 1; }
fi
mkdir $worldpath
printf '%s\n' mg_name=singlenode '[end_of_params]' >$worldpath/map_meta.txt
printf '%s\n' worldedit_run_tests=true max_forceloaded_blocks=9999 >$confpath
if [ -z "$mtserver" ]; then
chmod -R 777 $tempdir
[ -n "$DOCKER_IMAGE" ] || { echo "Missing DOCKER_IMAGE env variable" >&2; exit 1; }
vol=(
-v "$confpath":/etc/minetest/minetest.conf
-v "$tempdir":/var/lib/minetest/.minetest
-v "$PWD/worldedit":/var/lib/minetest/.minetest/world/worldmods/worldedit
)
[ -d minetest_game ] && vol+=(
-v "$PWD/minetest_game":/var/lib/minetest/.minetest/games/minetest_game
)
docker run --rm -i "${vol[@]}" "$DOCKER_IMAGE"
else
mkdir $worldpath/worldmods
ln -s "$PWD/worldedit" $worldpath/worldmods/worldedit
$mtserver --config "$confpath" --world "$worldpath" --logfile /dev/null
fi
test -f $worldpath/tests_ok || exit 1
exit 0

View File

@ -409,15 +409,14 @@ Load nodes from "(world folder)/schems/`<file>`.we" with position 1 of the curre
### `//lua <code>`
Executes `<code>` as a Lua chunk in the global namespace with the variables `name`, `player` and `pos` (= player position) available.
Executes `<code>` as a Lua chunk in the global namespace.
//lua worldedit.pos1[name] = vector.new(0, 0, 0)
//lua worldedit.rotate(worldedit.pos1["jones"], worldedit.pos2["jones"], "y", 90)
//lua player:set_pos(worldedit.pos2[name])
//lua worldedit.pos1["singleplayer"] = {x=0, y=0, z=0}
//lua worldedit.rotate(worldedit.pos1["singleplayer"], worldedit.pos2["singleplayer"], "y", 90)
### `//luatransform <code>`
Executes `<code>` as a Lua chunk in the global namespace with the variable `pos` available, for each node in the current WorldEdit region.
Executes `<code>` as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region.
//luatransform minetest.swap_node(pos, {name="default:stone"})
//luatransform if minetest.get_node(pos).name == "air" then minetest.add_node(pos, {name="default:water_source"}) end
@ -429,12 +428,11 @@ Save the current WorldEdit region using the Minetest Schematic format to "(world
//mtschemcreate some random filename
//mtschemcreate huge_base
### `//mtschemplace <file> [rotation]`
### `//mtschemplace <file>`
Load nodes from "(world folder)/schems/`<file>`.mts" with position 1 of the current WorldEdit region as the origin.
Valid values for `[rotation]` are 0, 90, 180 and 270.
//mtschemplace a_tree 270
//mtschemplace some random filename
//mtschemplace huge_base
### `//mtschemprob start/finish/get`

View File

@ -2,7 +2,7 @@ WorldEdit v1.3
==============
The ultimate in-game world editing tool for [Minetest](http://minetest.net/)! Tons of functionality to help with building, fixing, and more.
For more information, see the [forum topic](https://forum.minetest.net/viewtopic.php?t=572) at the Minetest forums.
For more information, see the [forum topic](https://forum.minetest.net/viewtopic.php?id=572) at the Minetest forums.
# New users should see the [tutorial](Tutorial.md).
@ -23,7 +23,7 @@ There is a nice installation guide over at the [Minetest Wiki](http://wiki.minet
8. You should have a mod selection screen. Select the one named something like `Minetest-WorldEdit` by left clicking once and press the **Enable Modpack** button.
9. Press the **Save** button. You can now use WorldEdit in that world. Repeat steps 7 to 9 to enable WorldEdit for other worlds too.
If you are having trouble, try asking for help in the [IRC channel](https://web.libera.chat/#minetest) (faster but may not always have helpers online)
If you are having trouble, try asking for help in the [IRC channel](https://webchat.freenode.net/?channels=#minetest) (faster but may not always have helpers online)
or ask on the [forum topic](https://forum.minetest.net/viewtopic.php?id=572) (slower but more likely to get help).
Usage

View File

@ -227,19 +227,11 @@ Code
----
Contained in code.lua, this module allows arbitrary Lua code to be used with WorldEdit.
### error = worldedit.lua(code, name)
### error = worldedit.lua(code)
the given code gets encapsulated into a function with parameters `name`, `player`, `pos`
where
* `name` is a playername or `nil`
* `player` is the player object of the above player if applicable, otherwise `nil`
* `pos` is the position of the aforementioned player (if applicable, otherwise `nil`) rounded to integers
Executes `code` as a Lua chunk in the global namespace.
the resulting function is then executed as a Lua chunk in the global namespace.
The return is
* a string in case of an error
* a tuple of `nil` and return of the function converted to a string in case of success
Returns an error if the code fails or nil otherwise.
### error = worldedit.luatransform(pos1, pos2, code)

View File

@ -1,3 +1,2 @@
name = Minetest-WorldEdit
description = WorldEdit is an in-game world editor. Use it to repair griefing, or just create awesome buildings in seconds.
min_minetest_version = 5.0

View File

@ -1,4 +0,0 @@
# For operations that potentially affect more than the specified amount of nodes
# WorldEdit will require additional confirmation via //y before proceeding.
# Set to 0 to disable the confirmation in all cases.
worldedit_safe_region_limit (Limit for safe region warning) int 20000

View File

@ -2,29 +2,17 @@
-- @module worldedit.code
--- Executes `code` as a Lua chunk in the global namespace.
-- the code will be encapsulated into a function with parameters
-- * name (the name of the player issuing the //lua command)
-- * player (the player object of the player)
-- * pos (the position of the player rounded to integers)
-- @return string in case of error, tuple of nil, return of code as string in case of success
function worldedit.lua(code, name)
local factory, err = loadstring("return function(name, player, pos)\n" .. code .. "\nend")
if not factory then -- Syntax error
-- @return An error message if the code fails, or nil on success.
function worldedit.lua(code)
local func, err = loadstring(code)
if not func then -- Syntax error
return err
end
local func = factory()
local player, pos
if name then
player = minetest.get_player_by_name(name)
if player then
pos = vector.round(player:get_pos())
end
local good, err = pcall(func)
if not good then -- Runtime error
return err
end
local good, err = pcall(func, name, player, pos)
if not good then -- Runtime error
return tostring(err)
end
return nil, dump(err)
return nil
end
@ -43,7 +31,7 @@ function worldedit.luatransform(pos1, pos2, code)
worldedit.keep_loaded(pos1, pos2)
local pos = vector.new(pos1.x, 0, 0)
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
@ -51,7 +39,7 @@ function worldedit.luatransform(pos1, pos2, code)
while pos.z <= pos2.z do
local good, err = pcall(func, pos)
if not good then -- Runtime error
return tostring(err)
return err
end
pos.z = pos.z + 1
end

View File

@ -1,21 +1,12 @@
--- Common functions [INTERNAL]. All of these functions are internal!
-- @module worldedit.common
-- Polyfill for vector.copy (added in 5.5.0)
if not vector.copy then
local vnew = vector.new
vector.copy = function(v)
return vnew(v.x, v.y, v.z)
end
end
--- Copies and modifies positions `pos1` and `pos2` so that each component of
-- `pos1` is less than or equal to the corresponding component of `pos2`.
-- Returns the new positions.
function worldedit.sort_pos(pos1, pos2)
pos1 = vector.copy(pos1)
pos2 = vector.copy(pos2)
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
if pos1.x > pos2.x then
pos2.x, pos1.x = pos1.x, pos2.x
end
@ -54,22 +45,12 @@ function worldedit.get_axis_others(axis)
end
-- Create a vmanip and read the area from map, this causes all
-- MapBlocks to be loaded into memory synchronously.
-- This doesn't actually *keep* them loaded, unlike the name implies.
function worldedit.keep_loaded(pos1, pos2)
-- rough estimate, a MapNode is 4 bytes in the engine
if worldedit.volume(pos1, pos2) > 268400000 then
print("[WorldEdit] Requested to load an area bigger than 1GB, refusing. The subsequent operation may fail.")
return
end
if minetest.load_area then
-- same effect but without unnecessary data copying
minetest.load_area(pos1, pos2)
else
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
end
-- Create a vmanip and read the area from map, this
-- causes all MapBlocks to be loaded into memory.
-- This doesn't actually *keep* them loaded, unlike the name implies.
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
end
@ -84,7 +65,7 @@ function mh.get_empty_data(area)
-- only partially modified aren't overwriten.
local data = {}
local c_ignore = minetest.get_content_id("ignore")
for i = 1, area:getVolume() do
for i = 1, worldedit.volume(area.MinEdge, area.MaxEdge) do
data[i] = c_ignore
end
return data
@ -133,77 +114,6 @@ function mh.finish(manip, data)
manip:set_data(data)
end
manip:write_to_map()
if manip.close ~= nil then
manip:close() -- explicitly free memory
end
manip:update_map()
end
-- returns an iterator that returns voxelarea indices for a hollow cuboid
function mh.iter_hollowcuboid(area, minx, miny, minz, maxx, maxy, maxz)
local i = area:index(minx, miny, minz) - 1
local xrange = maxx - minx + 1
local nextaction = i + 1 + xrange
local do_hole = false
local y = 0
local ydiff = maxy - miny
local ystride = area.ystride
local ymultistride = ydiff * ystride
local z = 0
local zdiff = maxz - minz
local zstride = area.zstride
local zcorner = true
return function()
-- continue i until it needs to jump ystride
i = i + 1
if i ~= nextaction then
return i
end
-- add the x offset if y (and z) are not 0 or maxy (or maxz)
if do_hole then
do_hole = false
i = i + xrange - 2
nextaction = i + 1
return i
end
-- continue y until maxy is exceeded
y = y+1
if y ~= ydiff + 1 then
i = i + ystride - xrange
if zcorner
or y == ydiff then
nextaction = i + xrange
else
nextaction = i + 1
do_hole = true
end
return i
end
-- continue z until maxz is exceeded
z = z+1
if z == zdiff + 1 then
-- hollowcuboid finished, return nil
return
end
-- set i to index(minx, miny, minz + z) - 1
i = i + zstride - (ymultistride + xrange)
zcorner = z == zdiff
-- y is 0, so traverse the xs
y = 0
nextaction = i + xrange
return i
end
end
function mh.iterp_hollowcuboid(area, minp, maxp)
return mh.iter_hollowcuboid(area, minp.x, minp.y, minp.z,
maxp.x, maxp.y, maxp.z)
end

View File

@ -24,14 +24,11 @@ function worldedit.metasave(pos1, pos2, filename)
return count
end
function worldedit.metaload(originpos, file_name)
function worldedit.metaload(originpos, filename)
deprecated("load")
local file_path = minetest.get_worldpath() ..
"/schems/" .. file_name .. ".wem"
local file, err = io.open(file_path, "wb")
if err then
return 0
end
filename = minetest.get_worldpath() .. "/schems/" .. file .. ".wem"
local file, err = io.open(filename, "wb")
if err then return 0 end
local data = file:read("*a")
return worldedit.deserialize(originpos, data)
end

View File

@ -2,16 +2,16 @@
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()
local delta2 = vector.new()
local delta_dir1
local delta_dir2
delta1 = vector.add(delta1, amount)
delta2 = vector.add(delta2, amount)
delta_dir1, delta_dir2 = worldedit.get_expansion_directions(pos1, pos2)
@ -19,7 +19,7 @@ worldedit.cuboid_volumetric_expand = function(name, amount)
delta2 = vector.multiply(delta2, delta_dir2)
worldedit.pos1[name] = vector.add(pos1, delta1)
worldedit.pos2[name] = vector.add(pos2, delta2)
return true
end
@ -28,18 +28,18 @@ end
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 = worldedit.marker_get_closest_to_axis(name, axis, direction)
local deltavect = vector.new()
if axis == 'x' then
deltavect.x = amount * direction
elseif axis == 'y' then
@ -49,7 +49,7 @@ worldedit.cuboid_linear_expand = function(name, axis, direction, amount)
else
return false, "invalid axis"
end
worldedit.marker_move(name, marker, deltavect)
return true
end
@ -59,13 +59,11 @@ end
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
assert(not rawequal(pos1, pos2)) -- vectors must not alias
if axis == 'x' then
worldedit.pos1[name].x = pos1.x + amount
worldedit.pos2[name].x = pos2.x + amount
@ -78,7 +76,7 @@ worldedit.cuboid_shift = function(name, axis, amount)
else
return false, "invalid axis"
end
return true
end
@ -88,7 +86,7 @@ worldedit.marker_move = function(name, marker, deltavector)
if marker ~= 1 and marker ~= 2 then
return false
end
if marker == 1 then
local pos = worldedit.pos1[name]
worldedit.pos1[name] = vector.add(deltavector, pos)
@ -96,7 +94,7 @@ worldedit.marker_move = function(name, marker, deltavector)
local pos = worldedit.pos2[name]
worldedit.pos2[name] = vector.add(deltavector, pos)
end
return true
end
@ -136,11 +134,10 @@ end
-- Return the marker that is closest to the player
worldedit.marker_get_closest_to_player = function(name)
local player = assert(minetest.get_player_by_name(name))
local playerpos = player:get_pos()
local playerpos = minetest.get_player_by_name(name):get_pos()
local dist1 = vector.distance(playerpos, worldedit.pos1[name])
local dist2 = vector.distance(playerpos, worldedit.pos2[name])
if dist1 < dist2 then
return 1
else
@ -153,7 +150,7 @@ end
worldedit.marker_get_closest_to_axis = function(name, axis, direction)
local pos1 = vector.new()
local pos2 = vector.new()
if direction ~= 1 and direction ~= -1 then
return nil
end
@ -188,20 +185,20 @@ worldedit.marker_get_closest_to_axis = function(name, axis, direction)
end
-- Translates up, down, left, right, front, back to their corresponding axes and
-- Translates up, down, left, right, front, back to their corresponding axes and
-- directions according to faced direction
worldedit.translate_direction = function(name, direction)
local axis, dir = worldedit.player_axis(name)
local resaxis, resdir
if direction == "up" then
return 'y', 1
end
if direction == "down" then
return 'y', -1
end
if direction == "front" then
if axis == "y" then
resaxis = nil
@ -211,7 +208,7 @@ worldedit.translate_direction = function(name, direction)
resdir = dir
end
end
if direction == "back" then
if axis == "y" then
resaxis = nil
@ -221,7 +218,7 @@ worldedit.translate_direction = function(name, direction)
resdir = -dir
end
end
if direction == "left" then
if axis == 'x' then
resaxis = 'z'
@ -231,7 +228,7 @@ worldedit.translate_direction = function(name, direction)
resdir = -dir
end
end
if direction == "right" then
if axis == 'x' then
resaxis = 'z'
@ -241,6 +238,6 @@ worldedit.translate_direction = function(name, direction)
resdir = dir
end
end
return resaxis, resdir
end

View File

@ -27,7 +27,6 @@ end
dofile(path .. "/common.lua")
load_module(path .. "/manipulations.lua")
load_module(path .. "/primitives.lua")
load_module(path .. "/transformations.lua")
load_module(path .. "/visualization.lua")
load_module(path .. "/serialization.lua")
load_module(path .. "/code.lua")
@ -39,7 +38,3 @@ if minetest.settings:get_bool("log_mods") then
print("[WorldEdit] Loaded!")
end
if minetest.settings:get_bool("worldedit_run_tests") then
dofile(path .. "/test/init.lua")
minetest.after(0, worldedit.run_tests)
end

View File

@ -56,7 +56,8 @@ function worldedit.set_param2(pos1, pos2, param2)
-- Update map
manip:set_param2_data(param2_data)
mh.finish(manip)
manip:write_to_map()
manip:update_map()
return worldedit.volume(pos1, pos2)
end
@ -97,6 +98,51 @@ function worldedit.replace(pos1, pos2, search_node, replace_node, inverse)
end
local function deferred_execution(next_one, finished)
-- Allocate 100% of server step for execution (might lag a little)
local allocated_usecs =
tonumber(minetest.settings:get("dedicated_server_step")) * 1000000
local function f()
local deadline = minetest.get_us_time() + allocated_usecs
repeat
local is_done = next_one()
if is_done then
if finished then
finished()
end
return
end
until minetest.get_us_time() >= deadline
minetest.after(0, f)
end
f()
end
--- Duplicates a region `amount` times with offset vector `direction`.
-- Stacking is spread across server steps.
-- @return The number of nodes stacked.
function worldedit.stack2(pos1, pos2, direction, amount, finished)
-- Protect arguments from external changes during execution
pos1 = table.copy(pos1)
pos2 = table.copy(pos2)
direction = table.copy(direction)
local i = 0
local translated = {x=0, y=0, z=0}
local function step()
translated.x = translated.x + direction.x
translated.y = translated.y + direction.y
translated.z = translated.z + direction.z
worldedit.copy2(pos1, pos2, translated)
i = i + 1
return i >= amount
end
deferred_execution(step, finished)
return worldedit.volume(pos1, pos2) * amount
end
--- Copies a region along `axis` by `amount` nodes.
-- @param pos1
-- @param pos2
@ -109,7 +155,7 @@ function worldedit.copy(pos1, pos2, axis, amount)
-- Decide if we need to copy stuff backwards (only applies to metadata)
local backwards = amount > 0 and amount < (pos2[axis] - pos1[axis] + 1)
local off = vector.new()
local off = {x=0, y=0, z=0}
off[axis] = amount
return worldedit.copy2(pos1, pos2, off, backwards)
end
@ -124,7 +170,7 @@ function worldedit.copy2(pos1, pos2, off, meta_backwards)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local src_manip, src_area = mh.init(pos1, pos2)
local src_stride = vector.new(1, src_area.ystride, src_area.zstride)
local src_stride = {x=1, y=src_area.ystride, z=src_area.zstride}
local src_offset = vector.subtract(pos1, src_area.MinEdge)
local dpos1 = vector.add(pos1, off)
@ -132,7 +178,7 @@ function worldedit.copy2(pos1, pos2, off, meta_backwards)
local dim = vector.add(vector.subtract(pos2, pos1), 1)
local dst_manip, dst_area = mh.init(dpos1, dpos2)
local dst_stride = vector.new(1, dst_area.ystride, dst_area.zstride)
local dst_stride = {x=1, y=dst_area.ystride, z=dst_area.zstride}
local dst_offset = vector.subtract(dpos1, dst_area.MinEdge)
local function do_copy(src_data, dst_data)
@ -171,6 +217,8 @@ function worldedit.copy2(pos1, pos2, off, meta_backwards)
dst_manip:set_param2_data(dst_data)
mh.finish(dst_manip)
src_data = nil
dst_data = nil
-- Copy metadata
local get_meta = minetest.get_meta
@ -178,7 +226,7 @@ function worldedit.copy2(pos1, pos2, off, meta_backwards)
for z = dim.z-1, 0, -1 do
for y = dim.y-1, 0, -1 do
for x = dim.x-1, 0, -1 do
local pos = vector.new(pos1.x+x, pos1.y+y, pos1.z+z)
local pos = {x=pos1.x+x, y=pos1.y+y, z=pos1.z+z}
local meta = get_meta(pos):to_table()
pos = vector.add(pos, off)
get_meta(pos):from_table(meta)
@ -189,7 +237,7 @@ function worldedit.copy2(pos1, pos2, off, meta_backwards)
for z = 0, dim.z-1 do
for y = 0, dim.y-1 do
for x = 0, dim.x-1 do
local pos = vector.new(pos1.x+x, pos1.y+y, pos1.z+z)
local pos = {x=pos1.x+x, y=pos1.y+y, z=pos1.z+z}
local meta = get_meta(pos):to_table()
pos = vector.add(pos, off)
get_meta(pos):from_table(meta)
@ -238,21 +286,21 @@ function worldedit.move(pos1, pos2, axis, amount)
end
-- Copy stuff to new location
local off = vector.new()
local off = {x=0, y=0, z=0}
off[axis] = amount
worldedit.copy2(pos1, pos2, off, backwards)
-- Nuke old area
if not overlap then
nuke_area(vector.new(), dim)
nuke_area({x=0, y=0, z=0}, dim)
else
-- Source and destination region are overlapping, which means we can't
-- blindly delete the [pos1, pos2] area
local leftover = vector.new(dim) -- size of the leftover slice
leftover[axis] = math.abs(amount)
if amount > 0 then
nuke_area(vector.new(), leftover)
nuke_area({x=0, y=0, z=0}, leftover)
else
local top = vector.new() -- offset of the leftover slice from pos1
local top = {x=0, y=0, z=0} -- offset of the leftover slice from pos1
top[axis] = dim[axis] - math.abs(amount)
nuke_area(top, leftover)
end
@ -261,6 +309,316 @@ function worldedit.move(pos1, pos2, axis, amount)
return worldedit.volume(pos1, pos2)
end
--- Duplicates a region along `axis` `amount` times.
-- Stacking is spread across server steps.
-- @param pos1
-- @param pos2
-- @param axis Axis direction, "x", "y", or "z".
-- @param count
-- @return The number of nodes stacked.
function worldedit.stack(pos1, pos2, axis, count, finished)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local length = pos2[axis] - pos1[axis] + 1
if count < 0 then
count = -count
length = -length
end
local i, distance = 0, 0
local function step()
distance = distance + length
worldedit.copy(pos1, pos2, axis, distance)
i = i + 1
return i >= count
end
deferred_execution(step, finished)
return worldedit.volume(pos1, pos2) * count
end
--- Stretches a region by a factor of positive integers along the X, Y, and Z
-- axes, respectively, with `pos1` as the origin.
-- @param pos1
-- @param pos2
-- @param stretch_x Amount to stretch along X axis.
-- @param stretch_y Amount to stretch along Y axis.
-- @param stretch_z Amount to stretch along Z axis.
-- @return The number of nodes scaled.
-- @return The new scaled position 1.
-- @return The new scaled position 2.
function worldedit.stretch(pos1, pos2, stretch_x, stretch_y, stretch_z)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
-- Prepare schematic of large node
local get_node, get_meta, place_schematic = minetest.get_node,
minetest.get_meta, minetest.place_schematic
local placeholder_node = {name="", param1=255, param2=0}
local nodes = {}
for i = 1, stretch_x * stretch_y * stretch_z do
nodes[i] = placeholder_node
end
local schematic = {size={x=stretch_x, y=stretch_y, z=stretch_z}, data=nodes}
local size_x, size_y, size_z = stretch_x - 1, stretch_y - 1, stretch_z - 1
local new_pos2 = {
x = pos1.x + (pos2.x - pos1.x) * stretch_x + size_x,
y = pos1.y + (pos2.y - pos1.y) * stretch_y + size_y,
z = pos1.z + (pos2.z - pos1.z) * stretch_z + size_z,
}
worldedit.keep_loaded(pos1, new_pos2)
local pos = {x=pos2.x, y=0, z=0}
local big_pos = {x=0, y=0, z=0}
while pos.x >= pos1.x do
pos.y = pos2.y
while pos.y >= pos1.y do
pos.z = pos2.z
while pos.z >= pos1.z do
local node = get_node(pos) -- Get current node
local meta = get_meta(pos):to_table() -- Get meta of current node
-- Calculate far corner of the big node
local pos_x = pos1.x + (pos.x - pos1.x) * stretch_x
local pos_y = pos1.y + (pos.y - pos1.y) * stretch_y
local pos_z = pos1.z + (pos.z - pos1.z) * stretch_z
-- Create large node
placeholder_node.name = node.name
placeholder_node.param2 = node.param2
big_pos.x, big_pos.y, big_pos.z = pos_x, pos_y, pos_z
place_schematic(big_pos, schematic)
-- Fill in large node meta
if next(meta.fields) ~= nil or next(meta.inventory) ~= nil then
-- Node has meta fields
for x = 0, size_x do
for y = 0, size_y do
for z = 0, size_z do
big_pos.x = pos_x + x
big_pos.y = pos_y + y
big_pos.z = pos_z + z
-- Set metadata of new node
get_meta(big_pos):from_table(meta)
end
end
end
end
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end
return worldedit.volume(pos1, pos2) * stretch_x * stretch_y * stretch_z, pos1, new_pos2
end
--- Transposes a region between two axes.
-- @return The number of nodes transposed.
-- @return The new transposed position 1.
-- @return The new transposed position 2.
function worldedit.transpose(pos1, pos2, axis1, axis2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local compare
local extent1, extent2 = pos2[axis1] - pos1[axis1], pos2[axis2] - pos1[axis2]
if extent1 > extent2 then
compare = function(extent1, extent2)
return extent1 > extent2
end
else
compare = function(extent1, extent2)
return extent1 < extent2
end
end
-- Calculate the new position 2 after transposition
local new_pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
new_pos2[axis1] = pos1[axis1] + extent2
new_pos2[axis2] = pos1[axis2] + extent1
local upper_bound = {x=pos2.x, y=pos2.y, z=pos2.z}
if upper_bound[axis1] < new_pos2[axis1] then upper_bound[axis1] = new_pos2[axis1] end
if upper_bound[axis2] < new_pos2[axis2] then upper_bound[axis2] = new_pos2[axis2] end
worldedit.keep_loaded(pos1, upper_bound)
local pos = {x=pos1.x, y=0, z=0}
local get_node, get_meta, set_node = minetest.get_node,
minetest.get_meta, minetest.set_node
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]
if compare(extent1, extent2) then -- Transpose only if below the diagonal
local node1 = get_node(pos)
local meta1 = get_meta(pos):to_table()
local value1, value2 = pos[axis1], pos[axis2] -- Save position values
pos[axis1], pos[axis2] = pos1[axis1] + extent2, pos1[axis2] + extent1 -- Swap axis extents
local node2 = get_node(pos)
local meta2 = get_meta(pos):to_table()
set_node(pos, node1)
get_meta(pos):from_table(meta1)
pos[axis1], pos[axis2] = value1, value2 -- Restore position values
set_node(pos, node2)
get_meta(pos):from_table(meta2)
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return worldedit.volume(pos1, pos2), pos1, new_pos2
end
--- Flips a region along `axis`.
-- @return The number of nodes flipped.
function worldedit.flip(pos1, pos2, axis)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
worldedit.keep_loaded(pos1, pos2)
--- TODO: Flip the region slice by slice along the flip axis using schematic method.
local pos = {x=pos1.x, y=0, z=0}
local start = pos1[axis] + pos2[axis]
pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)
local get_node, get_meta, set_node = minetest.get_node,
minetest.get_meta, minetest.set_node
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node1 = get_node(pos)
local meta1 = get_meta(pos):to_table()
local value = pos[axis] -- Save position
pos[axis] = start - value -- Shift position
local node2 = get_node(pos)
local meta2 = get_meta(pos):to_table()
set_node(pos, node1)
get_meta(pos):from_table(meta1)
pos[axis] = value -- Restore position
set_node(pos, node2)
get_meta(pos):from_table(meta2)
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return worldedit.volume(pos1, pos2)
end
--- Rotates a region clockwise around an axis.
-- @param pos1
-- @param pos2
-- @param axis Axis ("x", "y", or "z").
-- @param angle Angle in degrees (90 degree increments only).
-- @return The number of nodes rotated.
-- @return The new first position.
-- @return The new second position.
function worldedit.rotate(pos1, pos2, axis, angle)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local other1, other2 = worldedit.get_axis_others(axis)
angle = angle % 360
local count
if angle == 90 then
worldedit.flip(pos1, pos2, other1)
count, pos1, pos2 = worldedit.transpose(pos1, pos2, other1, other2)
elseif angle == 180 then
worldedit.flip(pos1, pos2, other1)
count = worldedit.flip(pos1, pos2, other2)
elseif angle == 270 then
worldedit.flip(pos1, pos2, other2)
count, pos1, pos2 = worldedit.transpose(pos1, pos2, other1, other2)
else
error("Only 90 degree increments are supported!")
end
return count, pos1, pos2
end
--- Rotates all oriented nodes in a region clockwise around the Y axis.
-- @param pos1
-- @param pos2
-- @param angle Angle in degrees (90 degree increments only).
-- @return The number of nodes oriented.
function worldedit.orient(pos1, pos2, angle)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local registered_nodes = minetest.registered_nodes
local wallmounted = {
[90] = {0, 1, 5, 4, 2, 3, 0, 0},
[180] = {0, 1, 3, 2, 5, 4, 0, 0},
[270] = {0, 1, 4, 5, 3, 2, 0, 0}
}
local facedir = {
[90] = { 1, 2, 3, 0, 13, 14, 15, 12, 17, 18, 19, 16,
9, 10, 11, 8, 5, 6, 7, 4, 23, 20, 21, 22},
[180] = { 2, 3, 0, 1, 10, 11, 8, 9, 6, 7, 4, 5,
18, 19, 16, 17, 14, 15, 12, 13, 22, 23, 20, 21},
[270] = { 3, 0, 1, 2, 19, 16, 17, 18, 15, 12, 13, 14,
7, 4, 5, 6, 11, 8, 9, 10, 21, 22, 23, 20}
}
angle = angle % 360
if angle == 0 then
return 0
end
if angle % 90 ~= 0 then
error("Only 90 degree increments are supported!")
end
local wallmounted_substitution = wallmounted[angle]
local facedir_substitution = facedir[angle]
worldedit.keep_loaded(pos1, pos2)
local count = 0
local get_node, swap_node = minetest.get_node, minetest.swap_node
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = get_node(pos)
local def = registered_nodes[node.name]
if def then
local paramtype2 = def.paramtype2
if paramtype2 == "wallmounted" or
paramtype2 == "colorwallmounted" then
local orient = node.param2 % 8
node.param2 = node.param2 - orient +
wallmounted_substitution[orient + 1]
swap_node(pos, node)
count = count + 1
elseif paramtype2 == "facedir" or
paramtype2 == "colorfacedir" then
local orient = node.param2 % 32
node.param2 = node.param2 - orient +
facedir_substitution[orient + 1]
swap_node(pos, node)
count = count + 1
end
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return count
end
--- Attempts to fix the lighting in a region.
-- @return The number of nodes updated.
@ -268,10 +626,8 @@ function worldedit.fixlight(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local vmanip = minetest.get_voxel_manip(pos1, pos2)
vmanip:write_to_map() -- this updates the lighting
if vmanip.close ~= nil then
vmanip:close()
end
vmanip:write_to_map()
vmanip:update_map() -- this updates the lighting
return worldedit.volume(pos1, pos2)
end
@ -290,16 +646,17 @@ function worldedit.clear_objects(pos1, pos2)
return false
end
local entity = obj:get_luaentity()
return not (entity and entity.name:find("^worldedit:"))
return not entity or not entity.name:find("^worldedit:")
end
-- Offset positions to include full nodes (positions are in the center of nodes)
pos1 = vector.add(pos1, -0.5)
pos2 = vector.add(pos2, 0.5)
local pos1x, pos1y, pos1z = pos1.x - 0.5, pos1.y - 0.5, pos1.z - 0.5
local pos2x, pos2y, pos2z = pos2.x + 0.5, pos2.y + 0.5, pos2.z + 0.5
local count = 0
if minetest.get_objects_in_area then
local objects = minetest.get_objects_in_area(pos1, pos2)
local objects = minetest.get_objects_in_area({x=pos1x, y=pos1y, z=pos1z},
{x=pos2x, y=pos2y, z=pos2z})
for _, obj in pairs(objects) do
if should_delete(obj) then
@ -313,22 +670,21 @@ function worldedit.clear_objects(pos1, pos2)
-- Fallback implementation via get_objects_inside_radius
-- Center of region
local center = {
x = pos1.x + ((pos2.x - pos1.x) / 2),
y = pos1.y + ((pos2.y - pos1.y) / 2),
z = pos1.z + ((pos2.z - pos1.z) / 2)
x = pos1x + ((pos2x - pos1x) / 2),
y = pos1y + ((pos2y - pos1y) / 2),
z = pos1z + ((pos2z - pos1z) / 2)
}
-- Bounding sphere radius
local radius = math.sqrt(
(center.x - pos1.x) ^ 2 +
(center.y - pos1.y) ^ 2 +
(center.z - pos1.z) ^ 2)
local objects = minetest.get_objects_inside_radius(center, radius)
for _, obj in pairs(objects) do
(center.x - pos1x) ^ 2 +
(center.y - pos1y) ^ 2 +
(center.z - pos1z) ^ 2)
for _, obj in pairs(minetest.get_objects_inside_radius(center, radius)) do
if should_delete(obj) then
local pos = obj:get_pos()
if pos.x >= pos1.x and pos.x <= pos2.x and
pos.y >= pos1.y and pos.y <= pos2.y and
pos.z >= pos1.z and pos.z <= pos2.z then
if pos.x >= pos1x and pos.x <= pos2x and
pos.y >= pos1y and pos.y <= pos2y and
pos.z >= pos1z and pos.z <= pos2z then
-- Inside region
obj:remove()
count = count + 1
@ -337,3 +693,4 @@ function worldedit.clear_objects(pos1, pos2)
end
return count
end

View File

@ -14,26 +14,31 @@ local mh = worldedit.manip_helpers
-- @return The number of nodes added.
function worldedit.cube(pos, width, height, length, node_name, hollow)
-- Set up voxel manipulator
local basepos = vector.subtract(pos,
vector.new(math.floor(width / 2), 0, math.floor(length / 2)))
local endpos = vector.add(basepos,
vector.new(width - 1, height - 1, length - 1))
local manip, area = mh.init(basepos, endpos)
local basepos = vector.subtract(pos, {x=math.floor(width/2), y=0, z=math.floor(length/2)})
local manip, area = mh.init(basepos, vector.add(basepos, {x=width, y=height, z=length}))
local data = mh.get_empty_data(area)
-- Add cube
local node_id = minetest.get_content_id(node_name)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = vector.subtract(basepos, area.MinEdge)
local count = 0
local iterfunc
if hollow then
iterfunc = mh.iterp_hollowcuboid(area, basepos, endpos)
else
iterfunc = area:iterp(basepos, endpos)
end
for vi in iterfunc do
data[vi] = node_id
count = count + 1
for z = 0, length-1 do
local index_z = (offset.z + z) * stride.z + 1 -- +1 for 1-based indexing
for y = 0, height-1 do
local index_y = index_z + (offset.y + y) * stride.y
for x = 0, width-1 do
local is_wall = z == 0 or z == length-1
or y == 0 or y == height-1
or x == 0 or x == width-1
if not hollow or is_wall then
local i = index_y + (offset.x + x)
data[i] = node_id
count = count + 1
end
end
end
end
mh.finish(manip, data)
@ -144,7 +149,7 @@ function worldedit.cylinder(pos, axis, length, radius1, radius2, node_name, holl
end
-- Handle negative lengths
local current_pos = vector.new(pos)
local current_pos = {x=pos.x, y=pos.y, z=pos.z}
if length < 0 then
length = -length
current_pos[axis] = current_pos[axis] - length
@ -157,8 +162,12 @@ function worldedit.cylinder(pos, axis, length, radius1, radius2, node_name, holl
-- Add desired shape (anything inbetween cylinder & cone)
local node_id = minetest.get_content_id(node_name)
local stride = vector.new(1, area.ystride, area.zstride)
local offset = vector.subtract(current_pos, area.MinEdge)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = {
x = current_pos.x - area.MinEdge.x,
y = current_pos.y - area.MinEdge.y,
z = current_pos.z - area.MinEdge.z,
}
local count = 0
for i = 0, length - 1 do
-- Calulate radius for this "height" in the cylinder
@ -216,8 +225,12 @@ function worldedit.pyramid(pos, axis, height, node_name, hollow)
-- Add pyramid
local node_id = minetest.get_content_id(node_name)
local stride = vector.new(1, area.ystride, area.zstride)
local offset = vector.subtract(pos, area.MinEdge)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = {
x = pos.x - area.MinEdge.x,
y = pos.y - area.MinEdge.y,
z = pos.z - area.MinEdge.z,
}
local size = math.abs(height * step)
local count = 0
-- For each level of the pyramid
@ -229,8 +242,8 @@ function worldedit.pyramid(pos, axis, height, node_name, hollow)
for index3 = -size, size do
local i = new_index2 + (index3 + offset[other2]) * stride[other2]
if (not hollow or size - math.abs(index2) < 2 or size - math.abs(index3) < 2) then
data[i] = node_id
count = count + 1
data[i] = node_id
count = count + 1
end
end
end
@ -258,9 +271,9 @@ function worldedit.spiral(pos, length, height, spacer, node_name)
-- Set up variables
local node_id = minetest.get_content_id(node_name)
local stride = vector.new(1, area.ystride, area.zstride)
local offset = vector.subtract(pos, area.MinEdge)
local i = offset.z * stride.z + offset.y * stride.y + offset.x + 1
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset_x, offset_y, offset_z = pos.x - area.MinEdge.x, pos.y - area.MinEdge.y, pos.z - area.MinEdge.z
local i = offset_z * stride.z + offset_y * stride.y + offset_x + 1
-- Add first column
local count = height

View File

@ -1,9 +1,7 @@
--- Schematic serialization and deserialiation.
-- @module worldedit.serialization
worldedit.LATEST_SERIALIZATION_VERSION = 5
local LATEST_SERIALIZATION_HEADER = worldedit.LATEST_SERIALIZATION_VERSION .. ":"
worldedit.LATEST_SERIALIZATION_VERSION = 6
--[[
Serialization version history:
@ -15,6 +13,7 @@ Serialization version history:
`name`, `param1`, `param2`, and `meta` fields.
5: Added header and made `param1`, `param2`, and `meta` fields optional.
Header format: <Version>,<ExtraHeaderField1>,...:<Content>
6: Much more complicated but also better format
--]]
@ -66,17 +65,105 @@ function worldedit.serialize(pos1, pos2)
has_meta[hash_node_position(meta_positions[i])] = true
end
local pos = vector.new(pos1.x, 0, 0)
-- Decide axis of saved rows
local dim = vector.add(vector.subtract(pos2, pos1), 1)
local axis
if dim.x * dim.y < math.min(dim.y * dim.z, dim.x * dim.z) then
axis = "z"
elseif dim.x * dim.z < math.min(dim.x * dim.y, dim.y * dim.z) then
axis = "y"
elseif dim.y * dim.z < math.min(dim.x * dim.y, dim.x * dim.z) then
axis = "x"
else
axis = "x" -- X or Z are usually most efficient
end
local other1, other2 = worldedit.get_axis_others(axis)
-- Helper functions (1)
local MATCH_DIST = 8
local function match_init(array, first_value)
array[1] = first_value
return {first_value}
end
local function match_try(cache, prev_pushed, value)
local i = #cache
while i >= 1 do
if cache[i] == value then
local ret = -(#cache - i + 1)
local was_value = type(prev_pushed) ~= "number" or prev_pushed >= 0
return ret, (was_value and ret == -1) or prev_pushed == ret
end
i = i - 1
end
return nil, false
end
local function match_push(cache, match, value)
if match ~= nil then -- don't advance cache
return match
end
local idx = #cache + 1
cache[idx] = value
if idx > MATCH_DIST then
table.remove(cache, 1)
end
return value
end
-- Helper functions (2)
local function cur_new(pos, pos1)
return {
a = axis,
p = {pos.x - pos1.x, pos.y - pos1.y, pos.z - pos1.z},
c = 1,
data = {},
param1 = {},
param2 = {},
meta = {},
}
end
local function is_emptyish(t)
-- returns true if <t> contains only one element and that one element is == 0
local seen = false
for _, value in pairs(t) do
if not seen then
if value ~= 0 then
return false
end
seen = true
else
return false
end
end
return true
end
local function cur_finish(result, cur)
if is_emptyish(cur.param1) then
cur.param1 = nil
end
if is_emptyish(cur.param2) then
cur.param2 = nil
end
if next(cur.meta) == nil then
cur.meta = nil
end
result[#result + 1] = cur
end
-- Serialize stuff
local pos = {}
local count = 0
local result = {}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local cur
local cache_data, cache_param1, cache_param2
local prev_data, prev_param1, prev_param2
pos[other1] = pos1[other1]
while pos[other1] <= pos2[other1] do
pos[other2] = pos1[other2]
while pos[other2] <= pos2[other2] do
pos[axis] = pos1[axis]
while pos[axis] <= pos2[axis] do
local node = get_node(pos)
if node.name ~= "air" and node.name ~= "ignore" then
count = count + 1
local meta
if has_meta[hash_node_position(pos)] then
@ -93,36 +180,76 @@ function worldedit.serialize(pos1, pos2)
end
end
result[count] = {
x = pos.x - pos1.x,
y = pos.y - pos1.y,
z = pos.z - pos1.z,
name = node.name,
param1 = node.param1 ~= 0 and node.param1 or nil,
param2 = node.param2 ~= 0 and node.param2 or nil,
meta = meta,
}
if cur == nil then -- Start a new row
cur = cur_new(pos, pos1, axis, other1, other2)
cache_data = match_init(cur.data, node.name)
cache_param1 = match_init(cur.param1, node.param1)
cache_param2 = match_init(cur.param2, node.param2)
prev_data = cur.data[1]
prev_param1 = cur.param1[1]
prev_param2 = cur.param2[1]
cur.meta[1] = meta
else -- Append to existing row
local next_c = cur.c + 1
cur.c = next_c
local value, m, can_omit
value = node.name
m, can_omit = match_try(cache_data, prev_data, node.name)
if not can_omit then
prev_data = match_push(cache_data, m, value)
cur.data[next_c] = prev_data
end
value = node.param1
m, can_omit = match_try(cache_param1, prev_param1, value)
if not can_omit then
prev_param1 = match_push(cache_param1, m, value)
cur.param1[next_c] = prev_param1
end
value = node.param2
m, can_omit = match_try(cache_param2, prev_param2, value)
if not can_omit then
prev_param2 = match_push(cache_param2, m, value)
cur.param2[next_c] = prev_param2
end
cur.meta[next_c] = meta
end
count = count + 1
else
if cur ~= nil then -- Finish row
cur_finish(result, cur)
cur = nil
end
end
pos.z = pos.z + 1
pos[axis] = pos[axis] + 1
end
pos.y = pos.y + 1
if cur ~= nil then -- Finish leftover row
cur_finish(result, cur)
cur = nil
end
pos[other2] = pos[other2] + 1
end
pos.x = pos.x + 1
pos[other1] = pos[other1] + 1
end
-- Serialize entries
result = minetest.serialize(result)
return LATEST_SERIALIZATION_HEADER .. result, count
return tonumber(worldedit.LATEST_SERIALIZATION_VERSION) .. "," ..
string.format("%d,%d,%d:", dim.x, dim.y, dim.z) .. result, count
end
-- Contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile)
-- by ChillCode, available under the MIT license.
local function deserialize_workaround(content)
local nodes, err
local nodes
if not minetest.global_exists("jit") then
nodes, err = minetest.deserialize(content, true)
elseif not content:match("^%s*return%s*{") then
-- The data doesn't look like we expect it to so we can't apply the workaround.
-- hope for the best
minetest.log("warning", "WorldEdit: deserializing data but can't apply LuaJIT workaround")
nodes, err = minetest.deserialize(content, true)
nodes = minetest.deserialize(content, true)
else
-- XXX: This is a filthy hack that works surprisingly well
-- in LuaJIT, `minetest.deserialize` will fail due to the register limit
@ -132,35 +259,25 @@ local function deserialize_workaround(content)
local escaped = content:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
local startpos, startpos1 = 1, 1
local endpos
local entry
while true do -- go through each individual node entry (except the last)
startpos, endpos = escaped:find("}%s*,%s*{", startpos)
startpos, endpos = escaped:find("},%s*{", startpos)
if not startpos then
break
end
local current = content:sub(startpos1, startpos)
entry, err = minetest.deserialize("return " .. current, true)
if not entry then
break
end
local entry = minetest.deserialize("return " .. current, true)
table.insert(nodes, entry)
startpos, startpos1 = endpos, endpos
end
if not err then
entry = minetest.deserialize("return " .. content:sub(startpos1), true) -- process the last entry
table.insert(nodes, entry)
end
end
if err then
minetest.log("warning", "WorldEdit: deserialize: " .. err)
local entry = minetest.deserialize("return " .. content:sub(startpos1), true) -- process the last entry
table.insert(nodes, entry)
end
return nodes
end
--- Loads the schematic in `value` into a node list in the latest format.
-- @return A node list in the latest format, or nil on failure.
local function load_schematic(value)
local version, _, content = worldedit.read_header(value)
local function legacy_load_schematic(version, header, content)
local nodes = {}
if version == 1 or version == 2 then -- Original flat table format
local tables = minetest.deserialize(content, true)
@ -202,6 +319,8 @@ local function load_schematic(value)
end
elseif version == 4 or version == 5 then -- Nested table format
nodes = deserialize_workaround(content)
elseif version >= 6 then
error("legacy_load_schematic called for non-legacy schematic")
else
return nil
end
@ -214,14 +333,29 @@ end
-- @return High corner position.
-- @return The number of nodes.
function worldedit.allocate(origin_pos, value)
local nodes = load_schematic(value)
if not nodes or #nodes == 0 then return nil end
return worldedit.allocate_with_nodes(origin_pos, nodes)
local version, header, content = worldedit.read_header(value)
if version == 6 then
local content = deserialize_workaround(content)
local pos2 = {
x = origin_pos.x + tonumber(header[1]) - 1,
y = origin_pos.y + tonumber(header[2]) - 1,
z = origin_pos.z + tonumber(header[3]) - 1,
}
local count = 0
for _, row in ipairs(content) do
count = count + row.c
end
return origin_pos, pos2, count
else
local nodes = legacy_load_schematic(version, header, content)
if not nodes or #nodes == 0 then return nil end
return worldedit.legacy_allocate_with_nodes(origin_pos, nodes)
end
end
-- Internal
function worldedit.allocate_with_nodes(origin_pos, nodes)
function worldedit.legacy_allocate_with_nodes(origin_pos, nodes)
local huge = math.huge
local pos1x, pos1y, pos1z = huge, huge, huge
local pos2x, pos2y, pos2z = -huge, -huge, -huge
@ -235,25 +369,37 @@ function worldedit.allocate_with_nodes(origin_pos, nodes)
if y > pos2y then pos2y = y end
if z > pos2z then pos2z = z end
end
return vector.new(pos1x, pos1y, pos1z), vector.new(pos2x, pos2y, pos2z), #nodes
local pos1 = {x=pos1x, y=pos1y, z=pos1z}
local pos2 = {x=pos2x, y=pos2y, z=pos2z}
return pos1, pos2, #nodes
end
--- Loads the nodes represented by string `value` at position `origin_pos`.
-- @return The number of nodes deserialized.
function worldedit.deserialize(origin_pos, value)
local nodes = load_schematic(value)
if not nodes then return nil end
if #nodes == 0 then return #nodes end
local version, header, content = worldedit.read_header(value)
if version == 6 then
local content = deserialize_workaround(content)
local pos2 = {
x = origin_pos.x + tonumber(header[1]) - 1,
y = origin_pos.y + tonumber(header[2]) - 1,
z = origin_pos.z + tonumber(header[3]) - 1,
}
worldedit.keep_loaded(origin_pos, pos2)
local pos1, pos2 = worldedit.allocate_with_nodes(origin_pos, nodes)
worldedit.keep_loaded(pos1, pos2)
return worldedit.deserialize_with_content(origin_pos, content)
else
local nodes = legacy_load_schematic(version, header, content)
if not nodes or #nodes == 0 then return nil end
local origin_x, origin_y, origin_z = origin_pos.x, origin_pos.y, origin_pos.z
local add_node, get_meta = minetest.add_node, minetest.get_meta
local registered_nodes = minetest.registered_nodes
for i, entry in ipairs(nodes) do
if registered_nodes[entry.name] then
local pos1, pos2 = worldedit.legacy_allocate_with_nodes(origin_pos, nodes)
worldedit.keep_loaded(pos1, pos2)
local origin_x, origin_y, origin_z = origin_pos.x, origin_pos.y, origin_pos.z
local count = 0
local add_node, get_meta = minetest.add_node, minetest.get_meta
for i, entry in ipairs(nodes) do
entry.x, entry.y, entry.z = origin_x + entry.x, origin_y + entry.y, origin_z + entry.z
-- Entry acts as both position and node
add_node(entry, entry)
@ -261,7 +407,81 @@ function worldedit.deserialize(origin_pos, value)
get_meta(entry):from_table(entry.meta)
end
end
return #nodes
end
return #nodes
end
-- Internal
function worldedit.deserialize_with_content(origin_pos, content)
-- Helper functions
local function resolve_refs(array)
-- find (and cache) highest index
local max_i = 1
for i, _ in pairs(array) do
if i > max_i then max_i = i end
end
array.max_i = max_i
-- resolve references
local cache = {}
for i = 1, max_i do
local v = array[i]
if v ~= nil then
if type(v) == "number" and v < 0 then -- is a reference
array[i] = cache[#cache + v + 1]
else
cache[#cache + 1] = v
end
end
end
end
local function read_in_array(array, idx)
if idx > array.max_i then
return array[array.max_i]
end
-- go backwards until we find something
repeat
local v = array[idx]
if v ~= nil then
return v
end
idx = idx - 1
until idx == 0
assert(false)
end
-- Actually deserialize
local count = 0
local entry = {}
local add_node, get_meta = minetest.add_node, minetest.get_meta
for _, row in ipairs(content) do
local axis = row.a
local pos = {
x = origin_pos.x + row.p[1],
y = origin_pos.y + row.p[2],
z = origin_pos.z + row.p[3],
}
if row.param1 == nil then row.param1 = {0} end
if row.param2 == nil then row.param2 = {0} end
if row.meta == nil then row.meta = {} end
resolve_refs(row.data)
resolve_refs(row.param1)
resolve_refs(row.param2)
for i = 1, row.c do
entry.name = read_in_array(row.data, i)
entry.param1 = read_in_array(row.param1, i)
entry.param2 = read_in_array(row.param2, i)
add_node(pos, entry)
local meta = row.meta[i]
if meta then
get_meta(pos):from_table(meta)
end
pos[axis] = pos[axis] + 1
end
count = count + row.c
end
return count
end

View File

@ -1,382 +0,0 @@
-- TODO: don't shit individual variables into the globals
---------------------
-- Helpers
---------------------
local vec = vector.new
local vecw = function(axis, n, base)
local ret = vec(base)
ret[axis] = n
return ret
end
local pos2str = minetest.pos_to_string
local get_node = minetest.get_node
local set_node = minetest.set_node
---------------------
-- Nodes
---------------------
local air = "air"
rawset(_G, "testnode1", "")
rawset(_G, "testnode2", "")
rawset(_G, "testnode3", "")
-- Loads nodenames to use for tests
local function init_nodes()
testnode1 = minetest.registered_aliases["mapgen_stone"]
testnode2 = minetest.registered_aliases["mapgen_dirt"]
testnode3 = minetest.registered_aliases["mapgen_cobble"] or minetest.registered_aliases["mapgen_dirt_with_grass"]
assert(testnode1 and testnode2 and testnode3)
end
-- Writes repeating pattern into given area
rawset(_G, "place_pattern", function(pos1, pos2, pattern)
local pos = vec()
local node = {name=""}
local i = 1
for z = pos1.z, pos2.z do
pos.z = z
for y = pos1.y, pos2.y do
pos.y = y
for x = pos1.x, pos2.x do
pos.x = x
node.name = pattern[i]
set_node(pos, node)
i = i % #pattern + 1
end
end
end
end)
---------------------
-- Area management
---------------------
assert(minetest.get_mapgen_setting("mg_name") == "singlenode")
rawset(_G, "area", {})
do
local areamin, areamax
local off
local c_air = minetest.get_content_id(air)
local vbuffer = {}
-- Assign a new area for use, will emerge and then call ready()
area.assign = function(min, max, ready)
areamin = min
areamax = max
minetest.emerge_area(min, max, function(bpos, action, remaining)
assert(action ~= minetest.EMERGE_ERRORED)
if remaining > 0 then return end
minetest.after(0, function()
area.clear()
ready()
end)
end)
end
-- Reset area contents and state
area.clear = function()
if off and vector.equals(off, vec(0, 0, 0)) then
return
end
local vmanip = minetest.get_voxel_manip(areamin, areamax)
local vpos1, vpos2 = vmanip:get_emerged_area()
local vcount = (vpos2.x - vpos1.x + 1) * (vpos2.y - vpos1.y + 1) * (vpos2.z - vpos1.z + 1)
if #vbuffer ~= vcount then
vbuffer = {}
for i = 1, vcount do
vbuffer[i] = c_air
end
end
vmanip:set_data(vbuffer)
vmanip:write_to_map()
off = vec(0, 0, 0)
end
-- Returns an usable area [pos1, pos2] that does not overlap previous ones
area.get = function(sizex, sizey, sizez)
local size
if sizey == nil and sizez == nil then
size = vec(sizex, sizex, sizex)
else
size = vec(sizex, sizey, sizez)
end
local pos1 = vector.add(areamin, off)
local pos2 = vector.subtract(vector.add(pos1, size), 1)
if pos2.x > areamax.x or pos2.y > areamax.y or pos2.z > areamax.z then
error("Internal failure: out of space")
end
off = vector.add(off, size)
return pos1, pos2
end
-- Returns an axis and count (= n) relative to the last-requested area that is unoccupied
area.dir = function(n)
local pos1 = vector.add(areamin, off)
if pos1.x + n <= areamax.x then
off.x = off.x + n
return "x", n
elseif pos1.x + n <= areamax.y then
off.y = off.y + n
return "y", n
elseif pos1.z + n <= areamax.z then
off.z = off.z + n
return "z", n
end
error("Internal failure: out of space")
end
-- Returns [XYZ] margin (list of pos pairs) of n around last-requested area
-- (may actually be larger but doesn't matter)
area.margin = function(n)
local pos1, pos2 = area.get(n)
return {
{ vec(areamin.x, areamin.y, pos1.z), pos2 }, -- X/Y
{ vec(areamin.x, pos1.y, areamin.z), pos2 }, -- X/Z
{ vec(pos1.x, areamin.y, areamin.z), pos2 }, -- Y/Z
}
end
end
-- Split an existing area into two non-overlapping [pos1, half1], [half2, pos2] parts; returns half1, half2
area.split = function(pos1, pos2)
local axis
if pos2.x - pos1.x >= 1 then
axis = "x"
elseif pos2.y - pos1.y >= 1 then
axis = "y"
elseif pos2.z - pos1.z >= 1 then
axis = "z"
else
error("Internal failure: area too small to split")
end
local hspan = math.floor((pos2[axis] - pos1[axis] + 1) / 2)
local half1 = vecw(axis, pos1[axis] + hspan - 1, pos2)
local half2 = vecw(axis, pos1[axis] + hspan, pos2)
return half1, half2
end
---------------------
-- Checks
---------------------
rawset(_G, "check", {})
-- Check that all nodes in [pos1, pos2] are the node(s) specified
check.filled = function(pos1, pos2, nodes)
if type(nodes) == "string" then
nodes = { nodes }
end
local _, counts = minetest.find_nodes_in_area(pos1, pos2, nodes)
local total = worldedit.volume(pos1, pos2)
local sum = 0
for _, n in pairs(counts) do
sum = sum + n
end
if sum ~= total then
error((total - sum) .. " " .. table.concat(nodes, ",") .. " nodes missing in " ..
pos2str(pos1) .. " -> " .. pos2str(pos2))
end
end
-- Check that none of the nodes in [pos1, pos2] are the node(s) specified
check.not_filled = function(pos1, pos2, nodes)
if type(nodes) == "string" then
nodes = { nodes }
end
local _, counts = minetest.find_nodes_in_area(pos1, pos2, nodes)
for nodename, n in pairs(counts) do
if n ~= 0 then
error(counts[nodename] .. " " .. nodename .. " nodes found in " ..
pos2str(pos1) .. " -> " .. pos2str(pos2))
end
end
end
-- Check that all of the areas are only made of node(s) specified
check.filled2 = function(list, nodes)
for _, pos in ipairs(list) do
check.filled(pos[1], pos[2], nodes)
end
end
-- Check that none of the areas contain the node(s) specified
check.not_filled2 = function(list, nodes)
for _, pos in ipairs(list) do
check.not_filled(pos[1], pos[2], nodes)
end
end
-- Checks presence of a repeating pattern in [pos1, po2] (cf. place_pattern)
check.pattern = function(pos1, pos2, pattern)
local pos = vec()
local i = 1
for z = pos1.z, pos2.z do
pos.z = z
for y = pos1.y, pos2.y do
pos.y = y
for x = pos1.x, pos2.x do
pos.x = x
local node = get_node(pos)
if node.name ~= pattern[i] then
error(pattern[i] .. " not found at " .. pos2str(pos) .. " (i=" .. i .. ")")
end
i = i % #pattern + 1
end
end
end
end
---------------------
-- The actual tests
---------------------
local tests = {}
worldedit.register_test = function(name, func, opts)
assert(type(name) == "string")
assert(func == nil or type(func) == "function")
if not opts then
opts = {}
else
opts = table.copy(opts)
end
opts.name = name
opts.func = func
table.insert(tests, opts)
end
local register_test = worldedit.register_test
-- How this works:
-- register_test registers a test with a name and function
-- The function should return if the test passes or otherwise cause a Lua error
-- The basic structure is: get areas + do operations + check results
-- Helpers:
-- area.get must be used to retrieve areas that can be operated on (these will be cleared before each test)
-- check.filled / check.not_filled can be used to check the result
-- area.margin + check.filled2 is useful to make sure nodes weren't placed too far
-- place_pattern + check.pattern is useful to test ops that operate on existing data
register_test("Internal self-test")
register_test("is area loaded?", function()
local pos1, _ = area.get(1)
assert(get_node(pos1).name == air)
end)
register_test("area.split", function()
for i = 2, 6 do
local pos1, pos2 = area.get(1, 1, i)
local half1, half2 = area.split(pos1, pos2)
assert(pos1.x == half1.x and pos1.y == half1.y)
assert(half1.x == half2.x and half1.y == half2.y)
assert(half1.z + 1 == half2.z)
if i % 2 == 0 then
assert((half1.z - pos1.z) == (pos2.z - half2.z)) -- divided equally
end
end
end)
register_test("check.filled", function()
local pos1, pos2 = area.get(1, 2, 1)
set_node(pos1, {name=testnode1})
set_node(pos2, {name=testnode2})
check.filled(pos1, pos1, testnode1)
check.filled(pos1, pos2, {testnode1, testnode2})
check.not_filled(pos1, pos1, air)
check.not_filled(pos1, pos2, {air, testnode3})
end)
register_test("pattern", function()
local pos1, pos2 = area.get(3, 2, 1)
local pattern = {testnode1, testnode3}
place_pattern(pos1, pos2, pattern)
assert(get_node(pos1).name == testnode1)
check.pattern(pos1, pos2, pattern)
end)
for _, name in ipairs({
"manipulations", "primitives", "schematic"
}) do
dofile(minetest.get_modpath("worldedit") .. "/test/" .. name .. ".lua")
end
register_test("Code")
register_test("worldedit.lua", function()
-- syntax error
local err, ret = worldedit.lua("?")
assert(ret == nil)
assert(err:find("unexpected symbol"))
-- runtime error
local err, ret = worldedit.lua("error(1234)")
assert(ret == nil)
assert(err:find("1234"))
-- normal operation
local err, ret = worldedit.lua("return name..tostring(player == nil)..tostring(pos == nil)", "nobody")
assert(err == nil)
assert(ret == "\"nobodytruetrue\"")
end)
register_test("worldedit.luatransform", function()
local pos1, pos2 = area.get(2)
-- syntax error
local err = worldedit.luatransform(pos1, pos2, "?")
assert(err:find("unexpected symbol"))
-- runtime error
local err = worldedit.luatransform(pos1, pos2, "error(2345)")
assert(err:find("2345"))
-- normal operation
local err = worldedit.luatransform(pos1, pos2,
"minetest.swap_node(pos, {name=" .. ("%q"):format(testnode1) .. "})")
assert(err == nil)
check.filled(pos1, pos1, testnode1)
end)
---------------------
-- Main function
---------------------
worldedit.run_tests = function()
do
local v = minetest.get_version()
print("Running " .. #tests .. " tests for WorldEdit " ..
worldedit.version_string .. " on " .. v.project .. " " .. (v.hash or v.string))
end
init_nodes()
-- emerge area from (0,0,0) ~ (56,56,56) and keep it loaded
-- Note: making this area smaller speeds up tests
local wanted = vec(56, 56, 56)
for x = 0, math.floor(wanted.x/16) do
for y = 0, math.floor(wanted.y/16) do
for z = 0, math.floor(wanted.z/16) do
assert(minetest.forceload_block(vec(x*16, y*16, z*16), true, -1))
end
end
end
area.assign(vec(0, 0, 0), wanted, function()
local failed = 0
for _, test in ipairs(tests) do
if not test.func then
local s = "---- " .. test.name .. " "
print(s .. string.rep("-", 60 - #s))
else
area.clear()
local ok, err = pcall(test.func)
print(string.format("%-60s %s", test.name, ok and "pass" or "FAIL"))
if not ok then
print(" " .. err)
failed = failed + 1
end
end
end
print("Done, " .. failed .. " tests failed.")
if failed == 0 then
io.close(io.open(minetest.get_worldpath() .. "/tests_ok", "w"))
end
minetest.request_shutdown()
end)
end
-- for debug purposes
minetest.register_on_joinplayer(function(player)
minetest.set_player_privs(player:get_player_name(),
minetest.string_to_privs("fly,fast,noclip,basic_debug,debug,interact"))
end)
minetest.register_on_punchnode(function(pos, node, puncher)
minetest.chat_send_player(puncher:get_player_name(), pos2str(pos))
end)

View File

@ -1,121 +0,0 @@
---------------------
local vec = vector.new
local vecw = function(axis, n, base)
local ret = vec(base)
ret[axis] = n
return ret
end
local air = "air"
---------------------
worldedit.register_test("Generic node manipulations")
worldedit.register_test("worldedit.set", function()
local pos1, pos2 = area.get(10)
local m = area.margin(1)
worldedit.set(pos1, pos2, testnode1)
check.filled(pos1, pos2, testnode1)
check.filled2(m, air)
end)
worldedit.register_test("worldedit.set mix", function()
local pos1, pos2 = area.get(10)
local m = area.margin(1)
worldedit.set(pos1, pos2, {testnode1, testnode2})
check.filled(pos1, pos2, {testnode1, testnode2})
check.filled2(m, air)
end)
worldedit.register_test("worldedit.replace", function()
local pos1, pos2 = area.get(10)
local half1, half2 = area.split(pos1, pos2)
worldedit.set(pos1, half1, testnode1)
worldedit.set(half2, pos2, testnode2)
worldedit.replace(pos1, pos2, testnode1, testnode3)
check.not_filled(pos1, pos2, testnode1)
check.filled(pos1, half1, testnode3)
check.filled(half2, pos2, testnode2)
end)
worldedit.register_test("worldedit.replace inverse", function()
local pos1, pos2 = area.get(10)
local half1, half2 = area.split(pos1, pos2)
worldedit.set(pos1, half1, testnode1)
worldedit.set(half2, pos2, testnode2)
worldedit.replace(pos1, pos2, testnode1, testnode3, true)
check.filled(pos1, half1, testnode1)
check.filled(half2, pos2, testnode3)
end)
-- FIXME?: this one looks overcomplicated
worldedit.register_test("worldedit.copy", function()
local pos1, pos2 = area.get(4)
local axis, n = area.dir(2)
local m = area.margin(1)
local b = pos1[axis]
-- create one slice with testnode1, one with testnode2
worldedit.set(pos1, vecw(axis, b + 1, pos2), testnode1)
worldedit.set(vecw(axis, b + 2, pos1), pos2, testnode2)
worldedit.copy(pos1, pos2, axis, n)
-- should have three slices now
check.filled(pos1, vecw(axis, b + 1, pos2), testnode1)
check.filled(vecw(axis, b + 2, pos1), pos2, testnode1)
check.filled(vecw(axis, b + 4, pos1), vector.add(pos2, vecw(axis, n)), testnode2)
check.filled2(m, air)
end)
worldedit.register_test("worldedit.copy2", function()
local pos1, pos2 = area.get(6)
local m1 = area.margin(1)
local pos1_, pos2_ = area.get(6)
local m2 = area.margin(1)
local pattern = {testnode1, testnode2, testnode3, testnode1, testnode2}
place_pattern(pos1, pos2, pattern)
worldedit.copy2(pos1, pos2, vector.subtract(pos1_, pos1))
check.pattern(pos1, pos2, pattern)
check.pattern(pos1_, pos2_, pattern)
check.filled2(m1, air)
check.filled2(m2, air)
end)
worldedit.register_test("worldedit.move (overlap)", function()
local pos1, pos2 = area.get(7)
local axis, n = area.dir(2)
local m = area.margin(1)
local pattern = {testnode2, testnode1, testnode2, testnode3, testnode3}
place_pattern(pos1, pos2, pattern)
worldedit.move(pos1, pos2, axis, n)
check.filled(pos1, vecw(axis, pos1[axis] + n - 1, pos2), air)
check.pattern(vecw(axis, pos1[axis] + n, pos1), vecw(axis, pos2[axis] + n, pos2), pattern)
check.filled2(m, air)
end)
worldedit.register_test("worldedit.move", function()
local pos1, pos2 = area.get(10)
local axis, n = area.dir(10)
local m = area.margin(1)
local pattern = {testnode1, testnode3, testnode3, testnode2}
place_pattern(pos1, pos2, pattern)
worldedit.move(pos1, pos2, axis, n)
check.filled(pos1, pos2, air)
check.pattern(vecw(axis, pos1[axis] + n, pos1), vecw(axis, pos2[axis] + n, pos2), pattern)
check.filled2(m, air)
end)
-- TODO: the rest (also testing param2 + metadata)

View File

@ -1,59 +0,0 @@
---------------------
local vec = vector.new
local vecw = function(axis, n, base)
local ret = vec(base)
ret[axis] = n
return ret
end
local air = "air"
---------------------
worldedit.register_test("Primitives")
worldedit.register_test("worldedit.cube", function()
local pos1, pos2 = area.get(6, 5, 4)
local m = area.margin(1)
local center = vec(pos1.x + 3, pos1.y, pos1.z + 2)
worldedit.cube(center, 6, 5, 4, testnode2)
check.filled(pos1, pos2, testnode2)
check.filled2(m, air)
end)
worldedit.register_test("worldedit.cube hollow small", function()
for n = 1, 2 do
local pos1, pos2 = area.get(n)
local m = area.margin(1)
local center = vec(pos1.x + math.floor(n/2), pos1.y, pos1.z + math.floor(n/2))
worldedit.cube(center, n, n, n, testnode1, true)
check.filled(pos1, pos2, testnode1) -- filled entirely
check.filled2(m, air)
end
end)
worldedit.register_test("worldedit.cube hollow", function()
local pos1, pos2 = area.get(6, 5, 4)
local m = area.margin(1)
local center = vec(pos1.x + 3, pos1.y, pos1.z + 2)
worldedit.cube(center, 6, 5, 4, testnode1, true)
check.filled(vector.add(pos1, vec(1,1,1)), vector.subtract(pos2, vec(1,1,1)), air)
check.filled2({
{ vecw("x", pos2.x, pos1), pos2 },
{ vecw("y", pos2.y, pos1), pos2 },
{ vecw("z", pos2.z, pos1), pos2 },
{ pos1, vecw("x", pos1.x, pos2) },
{ pos1, vecw("y", pos1.y, pos2) },
{ pos1, vecw("z", pos1.z, pos2) },
}, testnode1)
check.filled2(m, air)
end)

View File

@ -1,162 +0,0 @@
---------------------
local vec = vector.new
local air = "air"
---------------------
local function output_weird(numbers, body)
local s = {"return {"}
for _, parts in ipairs(numbers) do
s[#s+1] = "{"
for _, n in ipairs(parts) do
s[#s+1] = string.format(" {%d},", n)
end
s[#s+1] = "},"
end
return table.concat(s, "\n") .. table.concat(body, "\n") .. "}"
end
local fmt1p = '{\n ["x"]=%d,\n ["y"]=%d,\n ["z"]=%d,\n},'
local fmt1n = '{\n ["name"]="%s",\n},'
local fmt4 = '{ ["x"] = %d, ["y"] = %d, ["z"] = %d, ["meta"] = { ["fields"] = { }, ["inventory"] = { } }, ["param2"] = 0, ["param1"] = 0, ["name"] = "%s" }'
local fmt5 = '{ ["x"] = %d, ["y"] = %d, ["z"] = %d, ["name"] = "%s" }'
local fmt51 = '{[r2]=0,x=%d,y=%d,z=%d,name=r%d}'
local fmt52 = '{x=%d,y=%d,z=%d,name=_[%d]}'
local test_data = {
-- used by WorldEdit 0.2 (first public release)
{
name = "v1", ver = 1,
gen = function(pat)
local numbers = {
{2, 3, 4, 5, 6},
{7, 8}, {9, 10}, {11, 12},
{13, 14}, {15, 16}
}
return output_weird(numbers, {
fmt1p:format(0, 0, 0),
fmt1n:format(pat[1]),
fmt1p:format(0, 1, 0),
fmt1n:format(pat[3]),
fmt1p:format(1, 1, 0),
fmt1n:format(pat[1]),
fmt1p:format(1, 0, 1),
fmt1n:format(pat[3]),
fmt1p:format(0, 1, 1),
fmt1n:format(pat[1]),
})
end
},
-- v2: missing because I couldn't find any code in my archives that actually wrote this format
{
name = "v3", ver = 3,
gen = function(pat)
assert(pat[2] == air)
return table.concat({
"0 0 0 " .. pat[1] .. " 0 0",
"0 1 0 " .. pat[3] .. " 0 0",
"1 1 0 " .. pat[1] .. " 0 0",
"1 0 1 " .. pat[3] .. " 0 0",
"0 1 1 " .. pat[1] .. " 0 0",
}, "\n")
end
},
{
name = "v4", ver = 4,
gen = function(pat)
return table.concat({
"return { " .. fmt4:format(0, 0, 0, pat[1]),
fmt4:format(0, 1, 0, pat[3]),
fmt4:format(1, 1, 0, pat[1]),
fmt4:format(1, 0, 1, pat[3]),
fmt4:format(0, 1, 1, pat[1]) .. " }",
}, ", ")
end
},
-- like v4 but no meta and param (if empty)
{
name = "v5 (pre-5.6)", ver = 5,
gen = function(pat)
return table.concat({
"5:return { " .. fmt5:format(0, 0, 0, pat[1]),
fmt5:format(0, 1, 0, pat[3]),
fmt5:format(1, 1, 0, pat[1]),
fmt5:format(1, 0, 1, pat[3]),
fmt5:format(0, 1, 1, pat[1]) .. " }",
}, ", ")
end
},
-- reworked engine serialization in 5.6
{
name = "v5 (5.6)", ver = 5,
gen = function(pat)
return table.concat({
'5:r1="' .. pat[1] .. '";r2="param1";r3="' .. pat[3] .. '";return {'
.. fmt51:format(0, 0, 0, 1),
fmt51:format(0, 1, 0, 3),
fmt51:format(1, 1, 0, 1),
fmt51:format(1, 0, 1, 3),
fmt51:format(0, 1, 1, 1) .. "}",
}, ",")
end
},
-- small changes on engine side again
{
name = "v5 (post-5.7)", ver = 5,
gen = function(pat)
return table.concat({
'5:local _={};_[1]="' .. pat[1] .. '";_[3]="' .. pat[3] .. '";return {'
.. fmt52:format(0, 0, 0, 1),
fmt52:format(0, 1, 0, 3),
fmt52:format(1, 1, 0, 1),
fmt52:format(1, 0, 1, 3),
fmt52:format(0, 1, 1, 1) .. "}",
}, ",")
end
},
}
worldedit.register_test("Schematics")
worldedit.register_test("worldedit.read_header", function()
local value = '5,foo,BAR,-1,234:the content'
local version, header, content = worldedit.read_header(value)
assert(version == 5)
assert(#header == 4)
assert(header[1] == "foo" and header[2] == "BAR")
assert(header[3] == "-1" and header[4] == "234")
assert(content == "the content")
end)
worldedit.register_test("worldedit.allocate", function()
local value = '3:-1 0 0 dummy 0 0\n0 0 4 dummy 0 0\n0 1 0 dummy 0 0'
local pos1, pos2, count = worldedit.allocate(vec(1, 1, 1), value)
assert(vector.equals(pos1, vec(0, 1, 1)))
assert(vector.equals(pos2, vec(1, 2, 5)))
assert(count == 3)
end)
for _, e in ipairs(test_data) do
worldedit.register_test("worldedit.deserialize " .. e.name, function()
local pos1, pos2 = area.get(2)
local m = area.margin(1)
local pat = {testnode3, air, testnode2}
local value = e.gen(pat)
assert(type(value) == "string")
local version = worldedit.read_header(value)
assert(version == e.ver, "version: got " .. tostring(version) .. " expected " .. e.ver)
local count = worldedit.deserialize(pos1, value)
assert(count ~= nil and count > 0)
check.pattern(pos1, pos2, pat)
check.filled2(m, air)
end)
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

View File

@ -1,357 +0,0 @@
--- Node transformations.
-- @module worldedit.transformations
worldedit.deferred_execution = function(next_one, finished)
-- Allocate 80% of server step for execution
local allocated_usecs =
tonumber(minetest.settings:get("dedicated_server_step"):split(" ")[1]) * 1000000 * 0.8
local function f()
local deadline = minetest.get_us_time() + allocated_usecs
repeat
local is_done = next_one()
if is_done then
if finished then
finished()
end
return
end
until minetest.get_us_time() >= deadline
minetest.after(0, f)
end
f()
end
--- Duplicates a region `amount` times with offset vector `direction`.
-- Stacking is spread across server steps.
-- @return The number of nodes stacked.
function worldedit.stack2(pos1, pos2, direction, amount, finished)
-- Protect arguments from external changes during execution
pos1 = vector.copy(pos1)
pos2 = vector.copy(pos2)
direction = vector.copy(direction)
local i = 0
local translated = vector.new()
local function step()
translated.x = translated.x + direction.x
translated.y = translated.y + direction.y
translated.z = translated.z + direction.z
worldedit.copy2(pos1, pos2, translated)
i = i + 1
return i >= amount
end
worldedit.deferred_execution(step, finished)
return worldedit.volume(pos1, pos2) * amount
end
--- Duplicates a region along `axis` `amount` times.
-- Stacking is spread across server steps.
-- @param pos1
-- @param pos2
-- @param axis Axis direction, "x", "y", or "z".
-- @param count
-- @return The number of nodes stacked.
function worldedit.stack(pos1, pos2, axis, count, finished)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local length = pos2[axis] - pos1[axis] + 1
if count < 0 then
count = -count
length = -length
end
local i, distance = 0, 0
local function step()
distance = distance + length
worldedit.copy(pos1, pos2, axis, distance)
i = i + 1
return i >= count
end
worldedit.deferred_execution(step, finished)
return worldedit.volume(pos1, pos2) * count
end
--- Stretches a region by a factor of positive integers along the X, Y, and Z
-- axes, respectively, with `pos1` as the origin.
-- @param pos1
-- @param pos2
-- @param stretch_x Amount to stretch along X axis.
-- @param stretch_y Amount to stretch along Y axis.
-- @param stretch_z Amount to stretch along Z axis.
-- @return The number of nodes scaled.
-- @return The new scaled position 1.
-- @return The new scaled position 2.
function worldedit.stretch(pos1, pos2, stretch_x, stretch_y, stretch_z)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
-- Prepare schematic of large node
local get_node, get_meta, place_schematic = minetest.get_node,
minetest.get_meta, minetest.place_schematic
local placeholder_node = {name="", param1=255, param2=0}
local nodes = {}
for i = 1, stretch_x * stretch_y * stretch_z do
nodes[i] = placeholder_node
end
local schematic = {size=vector.new(stretch_x, stretch_y, stretch_z), data=nodes}
local size_x, size_y, size_z = stretch_x - 1, stretch_y - 1, stretch_z - 1
local new_pos2 = {
x = pos1.x + (pos2.x - pos1.x) * stretch_x + size_x,
y = pos1.y + (pos2.y - pos1.y) * stretch_y + size_y,
z = pos1.z + (pos2.z - pos1.z) * stretch_z + size_z,
}
worldedit.keep_loaded(pos1, new_pos2)
local pos = vector.new(pos2.x, 0, 0)
local big_pos = vector.new()
while pos.x >= pos1.x do
pos.y = pos2.y
while pos.y >= pos1.y do
pos.z = pos2.z
while pos.z >= pos1.z do
local node = get_node(pos) -- Get current node
local meta = get_meta(pos):to_table() -- Get meta of current node
-- Calculate far corner of the big node
local pos_x = pos1.x + (pos.x - pos1.x) * stretch_x
local pos_y = pos1.y + (pos.y - pos1.y) * stretch_y
local pos_z = pos1.z + (pos.z - pos1.z) * stretch_z
-- Create large node
placeholder_node.name = node.name
placeholder_node.param2 = node.param2
big_pos.x, big_pos.y, big_pos.z = pos_x, pos_y, pos_z
place_schematic(big_pos, schematic)
-- Fill in large node meta
if next(meta.fields) ~= nil or next(meta.inventory) ~= nil then
-- Node has meta fields
for x = 0, size_x do
for y = 0, size_y do
for z = 0, size_z do
big_pos.x = pos_x + x
big_pos.y = pos_y + y
big_pos.z = pos_z + z
-- Set metadata of new node
get_meta(big_pos):from_table(meta)
end
end
end
end
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end
return worldedit.volume(pos1, pos2) * stretch_x * stretch_y * stretch_z, pos1, new_pos2
end
--- Transposes a region between two axes.
-- @return The number of nodes transposed.
-- @return The new transposed position 1.
-- @return The new transposed position 2.
function worldedit.transpose(pos1, pos2, axis1, axis2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local compare
local extent1, extent2 = pos2[axis1] - pos1[axis1], pos2[axis2] - pos1[axis2]
if extent1 > extent2 then
compare = function(extent1, extent2)
return extent1 > extent2
end
else
compare = function(extent1, extent2)
return extent1 < extent2
end
end
-- Calculate the new position 2 after transposition
local new_pos2 = vector.new(pos2)
new_pos2[axis1] = pos1[axis1] + extent2
new_pos2[axis2] = pos1[axis2] + extent1
local upper_bound = vector.new(pos2)
if upper_bound[axis1] < new_pos2[axis1] then upper_bound[axis1] = new_pos2[axis1] end
if upper_bound[axis2] < new_pos2[axis2] then upper_bound[axis2] = new_pos2[axis2] end
worldedit.keep_loaded(pos1, upper_bound)
local pos = vector.new(pos1.x, 0, 0)
local get_node, get_meta, set_node = minetest.get_node,
minetest.get_meta, minetest.set_node
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]
if compare(extent1, extent2) then -- Transpose only if below the diagonal
local node1 = get_node(pos)
local meta1 = get_meta(pos):to_table()
local value1, value2 = pos[axis1], pos[axis2] -- Save position values
pos[axis1], pos[axis2] = pos1[axis1] + extent2, pos1[axis2] + extent1 -- Swap axis extents
local node2 = get_node(pos)
local meta2 = get_meta(pos):to_table()
set_node(pos, node1)
get_meta(pos):from_table(meta1)
pos[axis1], pos[axis2] = value1, value2 -- Restore position values
set_node(pos, node2)
get_meta(pos):from_table(meta2)
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return worldedit.volume(pos1, pos2), pos1, new_pos2
end
--- Flips a region along `axis`.
-- @return The number of nodes flipped.
function worldedit.flip(pos1, pos2, axis)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
worldedit.keep_loaded(pos1, pos2)
--- TODO: Flip the region slice by slice along the flip axis using schematic method.
local pos = vector.new(pos1.x, 0, 0)
local start = pos1[axis] + pos2[axis]
pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)
local get_node, get_meta, set_node = minetest.get_node,
minetest.get_meta, minetest.set_node
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node1 = get_node(pos)
local meta1 = get_meta(pos):to_table()
local value = pos[axis] -- Save position
pos[axis] = start - value -- Shift position
local node2 = get_node(pos)
local meta2 = get_meta(pos):to_table()
set_node(pos, node1)
get_meta(pos):from_table(meta1)
pos[axis] = value -- Restore position
set_node(pos, node2)
get_meta(pos):from_table(meta2)
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return worldedit.volume(pos1, pos2)
end
--- Rotates a region clockwise around an axis.
-- @param pos1
-- @param pos2
-- @param axis Axis ("x", "y", or "z").
-- @param angle Angle in degrees (90 degree increments only).
-- @return The number of nodes rotated.
-- @return The new first position.
-- @return The new second position.
function worldedit.rotate(pos1, pos2, axis, angle)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local other1, other2 = worldedit.get_axis_others(axis)
angle = angle % 360
local count
if angle == 90 then
worldedit.flip(pos1, pos2, other1)
count, pos1, pos2 = worldedit.transpose(pos1, pos2, other1, other2)
elseif angle == 180 then
worldedit.flip(pos1, pos2, other1)
count = worldedit.flip(pos1, pos2, other2)
elseif angle == 270 then
worldedit.flip(pos1, pos2, other2)
count, pos1, pos2 = worldedit.transpose(pos1, pos2, other1, other2)
else
error("Only 90 degree increments are supported!")
end
return count, pos1, pos2
end
--- Rotates all oriented nodes in a region clockwise around the Y axis.
-- @param pos1
-- @param pos2
-- @param angle Angle in degrees (90 degree increments only).
-- @return The number of nodes oriented.
function worldedit.orient(pos1, pos2, angle)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local registered_nodes = minetest.registered_nodes
local wallmounted = {
[90] = {0, 1, 5, 4, 2, 3, 0, 0},
[180] = {0, 1, 3, 2, 5, 4, 0, 0},
[270] = {0, 1, 4, 5, 3, 2, 0, 0}
}
local facedir = {
[90] = { 1, 2, 3, 0, 13, 14, 15, 12, 17, 18, 19, 16,
9, 10, 11, 8, 5, 6, 7, 4, 23, 20, 21, 22},
[180] = { 2, 3, 0, 1, 10, 11, 8, 9, 6, 7, 4, 5,
18, 19, 16, 17, 14, 15, 12, 13, 22, 23, 20, 21},
[270] = { 3, 0, 1, 2, 19, 16, 17, 18, 15, 12, 13, 14,
7, 4, 5, 6, 11, 8, 9, 10, 21, 22, 23, 20}
}
angle = angle % 360
if angle == 0 then
return 0
end
if angle % 90 ~= 0 then
error("Only 90 degree increments are supported!")
end
local wallmounted_substitution = wallmounted[angle]
local facedir_substitution = facedir[angle]
worldedit.keep_loaded(pos1, pos2)
local count = 0
local get_node, swap_node = minetest.get_node, minetest.swap_node
local pos = vector.new(pos1.x, 0, 0)
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = get_node(pos)
local def = registered_nodes[node.name]
if def then
local paramtype2 = def.paramtype2
if paramtype2 == "wallmounted" or
paramtype2 == "colorwallmounted" then
local orient = node.param2 % 8
node.param2 = node.param2 - orient +
wallmounted_substitution[orient + 1]
swap_node(pos, node)
count = count + 1
elseif paramtype2 == "facedir" or
paramtype2 == "colorfacedir" then
local orient = node.param2 % 32
node.param2 = node.param2 - orient +
facedir_substitution[orient + 1]
swap_node(pos, node)
count = count + 1
end
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return count
end

View File

@ -19,7 +19,7 @@ function worldedit.hide(pos1, pos2)
worldedit.keep_loaded(pos1, pos2)
local pos = vector.new(pos1.x, 0, 0)
local pos = {x=pos1.x, y=0, z=0}
local get_node, get_meta, swap_node = minetest.get_node,
minetest.get_meta, minetest.swap_node
while pos.x <= pos2.x do
@ -79,7 +79,7 @@ function worldedit.highlight(pos1, pos2, node_name)
worldedit.keep_loaded(pos1, pos2)
local pos = vector.new(pos1.x, 0, 0)
local pos = {x=pos1.x, y=0, z=0}
local get_node, get_meta, swap_node = minetest.get_node,
minetest.get_meta, minetest.swap_node
local count = 0

View File

@ -1,4 +1,8 @@
local S = minetest.get_translator("worldedit_brush")
if minetest.raycast == nil then
error(
"worldedit_brush requires at least Minetest 5.0"
)
end
local BRUSH_MAX_DIST = 150
local brush_on_use = function(itemstack, placer)
@ -8,8 +12,7 @@ local brush_on_use = function(itemstack, placer)
local cmd = meta:get_string("command")
if cmd == "" then
worldedit.player_notify(name,
S("This brush is not bound, use @1 to bind a command to it.",
minetest.colorize("#00ffff", "//brush")), "info")
"This brush is not bound, use //brush to bind a command to it.")
return false
end
@ -19,17 +22,17 @@ local brush_on_use = function(itemstack, placer)
local has_privs, missing_privs = minetest.check_player_privs(name, cmddef.privs)
if not has_privs then
worldedit.player_notify(name,
S("Missing privileges: @1", table.concat(missing_privs, ", ")), "error")
"Missing privileges: " .. table.concat(missing_privs, ", "))
return false
end
local raybegin = vector.add(placer:get_pos(),
vector.new(0, placer:get_properties().eye_height, 0))
{x=0, y=placer:get_properties().eye_height, z=0})
local rayend = vector.add(raybegin, vector.multiply(placer:get_look_dir(), BRUSH_MAX_DIST))
local ray = minetest.raycast(raybegin, rayend, false, true)
local pointed_thing = ray:next()
if pointed_thing == nil then
worldedit.player_notify(name, S("Too far away."), "error")
worldedit.player_notify(name, "Too far away.")
return false
end
@ -38,17 +41,17 @@ local brush_on_use = function(itemstack, placer)
worldedit.pos2[name] = nil
worldedit.marker_update(name)
-- this isn't really clean...
local player_notify_old = worldedit.player_notify
worldedit.player_notify = function(name, msg)
if string.match(msg, "^%d") then return end -- discard "1234 nodes added."
return player_notify_old(name, msg)
end
assert(cmddef.require_pos < 2)
local parsed = {cmddef.parse(meta:get_string("params"))}
if not table.remove(parsed, 1) then return false end -- shouldn't happen
-- discard success messages
local player_notify_old = worldedit.player_notify
worldedit.player_notify = function(name, msg, typ)
if typ == "ok" then return end
return player_notify_old(name, msg, typ)
end
minetest.log("action", string.format("%s uses WorldEdit brush (//%s) at %s",
name, cmd, minetest.pos_to_string(pointed_thing.under)))
cmddef.func(name, unpack(parsed))
@ -58,7 +61,7 @@ local brush_on_use = function(itemstack, placer)
end
minetest.register_tool(":worldedit:brush", {
description = S("WorldEdit Brush"),
description = "WorldEdit Brush",
inventory_image = "worldedit_brush.png",
stack_max = 1, -- no need to stack these (metadata prevents this anyway)
range = 0,
@ -71,7 +74,7 @@ minetest.register_tool(":worldedit:brush", {
worldedit.register_command("brush", {
privs = {worldedit=true},
params = "none/<cmd> [parameters]",
description = S("Assign command to WorldEdit brush item or clear assignment using 'none'"),
description = "Assign command to WorldEdit brush item",
parse = function(param)
local found, _, cmd, params = param:find("^([^%s]+)%s+(.+)$")
if not found then
@ -84,39 +87,39 @@ worldedit.register_command("brush", {
return true, cmd, params
end,
func = function(name, cmd, params)
local player = minetest.get_player_by_name(name)
if not player then return end
local itemstack = player:get_wielded_item()
local itemstack = minetest.get_player_by_name(name):get_wielded_item()
if itemstack == nil or itemstack:get_name() ~= "worldedit:brush" then
return false, S("Not holding brush item.")
worldedit.player_notify(name, "Not holding brush item.")
return
end
cmd = cmd:lower()
local meta = itemstack:get_meta()
if cmd == "none" then
meta:from_table(nil)
worldedit.player_notify(name, S("Brush assignment cleared."), "ok")
worldedit.player_notify(name, "Brush assignment cleared.")
else
local cmddef = worldedit.registered_commands[cmd]
if cmddef == nil or cmddef.require_pos ~= 1 then
return false, S("@1 cannot be used with brushes",
minetest.colorize("#00ffff", "//"..cmd))
worldedit.player_notify(name, "//" .. cmd .. " cannot be used with brushes")
return
end
-- Try parsing command params so we can give the user feedback
local ok, err = cmddef.parse(params)
if not ok then
err = err or S("invalid usage")
return false, S("Error with command: @1", err)
err = err or "invalid usage"
worldedit.player_notify(name, "Error with brush command: " .. err)
return
end
meta:set_string("command", cmd)
meta:set_string("params", params)
local fullcmd = minetest.colorize("#00ffff", "//"..cmd) .. " " .. params
local fullcmd = "//" .. cmd .. " " .. params
meta:set_string("description",
minetest.registered_tools["worldedit:brush"].description .. ": " .. fullcmd)
worldedit.player_notify(name, S("Brush assigned to command: @1", fullcmd), "ok")
worldedit.player_notify(name, "Brush assigned to command: " .. fullcmd)
end
player:set_wielded_item(itemstack)
minetest.get_player_by_name(name):set_wielded_item(itemstack)
end,
})

View File

@ -1,12 +0,0 @@
# textdomain: worldedit_brush
This brush is not bound, use @1 to bind a command to it.=
Missing privileges: @1=
Too far away.=
WorldEdit Brush=
Assign command to WorldEdit brush item or clear assignment using 'none'=
Not holding brush item.=
Brush assignment cleared.=
@1 cannot be used with brushes=
invalid usage=
Error with command: @1=
Brush assigned to command: @1=

View File

@ -1,12 +0,0 @@
# textdomain: worldedit_brush
This brush is not bound, use @1 to bind a command to it.=Dieser Pinsel ist nicht konfiguriert, nutzen Sie @1, um einen Befehl an ihn zu binden.
Missing privileges: @1=Fehlende Privilegien: @1
Too far away.=Zu weit weg.
WorldEdit Brush=WorldEdit-Pinsel
Assign command to WorldEdit brush item or clear assignment using 'none'=Befehl an WorldEdit-Pinsel binden oder Zuweisung mittels „none“ aufheben
Not holding brush item.=Kein Pinsel in der Hand.
Brush assignment cleared.=Pinsel-Zuweisung aufgehoben.
@1 cannot be used with brushes=@1 kann nicht mit Pinseln verwendet werden
invalid usage=Ungültige Verwendung
Error with command: @1=Befehl ist fehlerhaft: @1
Brush assigned to command: @1=Pinsel an Befehl gebunden: @1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 B

After

Width:  |  Height:  |  Size: 337 B

1
worldedit_commands/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*~

View File

@ -1,59 +0,0 @@
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,
})

View File

@ -1,9 +1,6 @@
local S = minetest.get_translator("worldedit_commands")
worldedit.register_command("outset", {
params = "[h/v] <amount>",
description = S("Outset the selected region."),
category = S("Region operations"),
description = "Outset the selected region.",
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
@ -14,7 +11,7 @@ worldedit.register_command("outset", {
local hv_test = dir:find("[^hv]+")
if hv_test ~= nil then
return false, S("Invalid direction: @1", dir)
return false, "Invalid direction."
end
return true, dir, tonumber(amount)
@ -31,19 +28,18 @@ worldedit.register_command("outset", {
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")
return false, "Invalid number of arguments"
end
worldedit.marker_update(name)
return true, S("Region outset by @1 nodes", amount)
return true, "Region outset by " .. amount .. " blocks"
end,
})
worldedit.register_command("inset", {
params = "[h/v] <amount>",
description = S("Inset the selected region."),
category = S("Region operations"),
description = "Inset the selected region.",
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
@ -52,7 +48,7 @@ worldedit.register_command("inset", {
return false
end
if dir:find("[^hv]") ~= nil then
return false, S("Invalid direction: @1", dir)
return false, "Invalid direction."
end
return true, dir, tonumber(amount)
@ -69,19 +65,18 @@ worldedit.register_command("inset", {
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")
return false, "Invalid number of arguments"
end
worldedit.marker_update(name)
return true, S("Region inset by @1 nodes", amount)
return true, "Region inset by " .. amount .. " blocks"
end,
})
worldedit.register_command("shift", {
params = "x/y/z/?/up/down/left/right/front/back [+/-]<amount>",
description = S("Shifts the selection area without moving its contents"),
category = S("Region operations"),
description = "Shifts the selection area without moving its contents",
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
@ -103,21 +98,20 @@ worldedit.register_command("shift", {
end
if axis == nil or dir == nil then
return false, S("Invalid if looking straight up or down")
return false, "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)
return true, "Region shifted by " .. amount .. " nodes"
end,
})
worldedit.register_command("expand", {
params = "[+/-]x/y/z/?/up/down/left/right/front/back <amount> [reverse amount]",
description = S("Expands the selection in the selected absolute or relative axis"),
category = S("Region operations"),
description = "Expands the selection in the selected absolute or relative axis",
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
@ -141,7 +135,7 @@ worldedit.register_command("expand", {
axis, dir = worldedit.translate_direction(name, direction)
if axis == nil or dir == nil then
return false, S("Invalid if looking straight up or down")
return false, "Invalid if looking straight up or down"
end
else
if direction == "?" then
@ -159,15 +153,14 @@ worldedit.register_command("expand", {
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)
return true, "Region expanded by " .. (amount + rev_amount) .. " nodes"
end,
})
worldedit.register_command("contract", {
params = "[+/-]x/y/z/?/up/down/left/right/front/back <amount> [reverse amount]",
description = S("Contracts the selection in the selected absolute or relative axis"),
category = S("Region operations"),
description = "Contracts the selection in the selected absolute or relative axis",
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
@ -191,7 +184,7 @@ worldedit.register_command("contract", {
axis, dir = worldedit.translate_direction(name, direction)
if axis == nil or dir == nil then
return false, S("Invalid if looking straight up or down")
return false, "Invalid if looking straight up or down"
end
else
if direction == "?" then
@ -209,13 +202,13 @@ worldedit.register_command("contract", {
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)
return true, "Region contracted by " .. (amount + rev_amount) .. " nodes"
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"),
description = "Select a cube with side length <size> around position 1 and run <command> on region",
privs = {worldedit=true},
require_pos = 1,
parse = function(param)
@ -237,8 +230,7 @@ worldedit.register_command("cubeapply", {
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("#00ffff", "//"..cmd))
return false, "invalid usage: //" .. cmd .. " cannot be used with cubeapply"
end
-- run parsing of target command
local parsed = {cmddef.parse(args)}
@ -249,14 +241,15 @@ worldedit.register_command("cubeapply", {
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, ", "))
worldedit.player_notify(name, "Missing privileges: " ..
table.concat(missing_privs, ", "))
return
end
-- update region to be the cuboid the user wanted

File diff suppressed because it is too large Load Diff

View File

@ -1,153 +0,0 @@
# 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>)=
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@nLeft-click to set 1st position, right-click to set 2nd=

View File

@ -1,164 +0,0 @@
# 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
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@nLeft-click to set 1st position, right-click to set 2nd=WorldEdit-Zauberstab@nSetzen der 1. Position mit Linksklick, der 2. mit Rechtsklick

View File

@ -1,155 +0,0 @@
# 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@nLeft-click to set 1st position, right-click to set 2nd=Инструмент WorldEdit Wand@nЛевая кнопка мыши, чтобы установить 1-ю позицию, правая кнопка мыши, чтобы установить 2-ю

View File

@ -1,366 +0,0 @@
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,
})

View File

@ -6,14 +6,16 @@ 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]
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[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)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos1)
--add marker
worldedit.marker1[name] = minetest.add_entity(pos1, "worldedit:pos1", init_sentinel)
@ -28,14 +30,16 @@ end
--marks worldedit region position 2
worldedit.mark_pos2 = function(name, region_too)
local pos2 = worldedit.pos2[name]
local pos1, pos2 = worldedit.pos1[name], 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)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos2, pos2)
--add marker
worldedit.marker2[name] = minetest.add_entity(pos2, "worldedit:pos2", init_sentinel)
@ -73,14 +77,15 @@ worldedit.mark_region = function(name)
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)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(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 entpos = {x=pos1.x + sizex - 0.5, y=pos1.y + sizey - 0.5, z=z}
local marker = minetest.add_entity(entpos, "worldedit:region_cube", init_sentinel)
if marker ~= nil then
marker:set_properties({
@ -94,7 +99,7 @@ worldedit.mark_region = function(name)
--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 entpos = {x=x, y=pos1.y + sizey - 0.5, z=pos1.z + sizez - 0.5}
local marker = minetest.add_entity(entpos, "worldedit:region_cube", init_sentinel)
if marker ~= nil then
marker:set_properties({

View File

@ -1,85 +0,0 @@
-- Strips any kind of escape codes (translation, colors) from a string
-- https://github.com/minetest/minetest/blob/53dd7819277c53954d1298dfffa5287c306db8d0/src/util/string.cpp#L777
local function strip_escapes(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
--print(("%q -> %q"):format(input, out))
return out
end
local function string_endswith(full, part)
return full:find(part, 1, true) == #full - #part + 1
end
local description_cache = nil
-- normalizes node "description" `nodename`, returning a string (or nil)
worldedit.normalize_nodename = function(nodename)
nodename = nodename:gsub("^%s*(.-)%s*$", "%1") -- strip spaces
if nodename == "" then return nil end
local fullname = ItemStack({name=nodename}):get_name() -- resolve aliases
if minetest.registered_nodes[fullname] or fullname == "air" then -- full name
return fullname
end
nodename = nodename:lower()
for key, _ in pairs(minetest.registered_nodes) do
if string_endswith(key:lower(), ":" .. nodename) then -- matches name (w/o mod part)
return key
end
end
if description_cache == nil then
-- cache stripped descriptions
description_cache = {}
for key, value in pairs(minetest.registered_nodes) do
local desc = strip_escapes(value.description):gsub("\n.*", "", 1):lower()
if desc ~= "" then
description_cache[key] = desc
end
end
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
local match = nil
for key, value in pairs(description_cache) do
if value:find(nodename, 1, true) ~= nil then
if match ~= nil then
return nil
end
match = key -- substring description match (only if no ambiguities)
end
end
return match
end

View File

@ -1,276 +0,0 @@
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,
})

View File

@ -1,234 +0,0 @@
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)
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)
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, vector.round(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, vector.round(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 found == nil then
return false
end
return true, flag, vector.new(tonumber(x), tonumber(y), tonumber(z))
end,
func = function(name, flag, pos)
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,
})

View File

@ -1,30 +1,15 @@
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
if count < 20000 then
return callback()
end
-- save callback to call later
--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("#00ffff", "//y")
local no_cmd = minetest.colorize("#00ffff", "//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")
worldedit.player_notify(name, "WARNING: this operation could affect up to " .. count .. " nodes; type //y to continue or //n to cancel")
end
local function reset_pending(name)
@ -33,11 +18,11 @@ end
minetest.register_chatcommand("/y", {
params = "",
description = S("Confirm a pending operation"),
description = "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")
worldedit.player_notify(name, "no operation pending")
return
end
@ -48,10 +33,10 @@ minetest.register_chatcommand("/y", {
minetest.register_chatcommand("/n", {
params = "",
description = S("Abort a pending operation"),
description = "Abort a pending operation",
func = function(name)
if not safe_region_callback[name] then
worldedit.player_notify(name, S("no operation pending"), "error")
worldedit.player_notify(name, "no operation pending")
return
end

View File

@ -1,284 +0,0 @@
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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 B

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 B

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

View File

@ -1,269 +0,0 @@
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("copy", {
params = "x/y/z/? <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 = function(param)
local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
if found == nil then
return false
end
return true, axis, tonumber(amount)
end,
nodes_needed = function(name, axis, amount)
return check_region(name) * 2
end,
func = function(name, axis, amount)
if axis == "?" then
local sign
axis, sign = worldedit.player_axis(name)
amount = amount * sign
end
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 = "x/y/z/? <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 = function(param)
local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
if found == nil then
return false
end
return true, axis, tonumber(amount)
end,
nodes_needed = function(name, axis, amount)
return check_region(name) * 2
end,
func = function(name, axis, amount)
if axis == "?" then
local sign
axis, sign = worldedit.player_axis(name)
amount = amount * sign
end
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 = "x/y/z/? <count>",
description = S("Stack the current WorldEdit region along the given axis <count> times"),
category = S("Transformations"),
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")
if found == nil then
return false
end
return true, axis, tonumber(repetitions)
end,
nodes_needed = function(name, axis, repetitions)
return check_region(name) * math.abs(repetitions)
end,
func = function(name, axis, repetitions)
if axis == "?" then
local sign
axis, sign = worldedit.player_axis(name)
repetitions = repetitions * sign
end
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, 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, 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 = "x/y/z/?",
description = S("Flip the current WorldEdit region along the given axis"),
category = S("Transformations"),
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
if param ~= "x" and param ~= "y" and param ~= "z" and param ~= "?" then
return false
end
return true, param
end,
nodes_needed = check_region,
func = function(name, param)
if param == "?" then param = worldedit.player_axis(name) end
local count = worldedit.flip(worldedit.pos1[name], worldedit.pos2[name], param)
return true, S("@1 nodes flipped", count)
end,
})
worldedit.register_command("rotate", {
params = "x/y/z/? <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, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
if found == nil 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, axis, angle
end,
nodes_needed = check_region,
func = function(name, axis, angle)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if axis == "?" then axis = worldedit.player_axis(name) end
local 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,
})

View File

@ -1,5 +1,3 @@
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
@ -11,18 +9,11 @@ end
local punched_air_time = {}
minetest.register_tool(":worldedit:wand", {
description = S("WorldEdit Wand tool\nLeft-click to set 1st position, right-click to set 2nd"),
description = "WorldEdit Wand tool\nLeft-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()
@ -41,7 +32,7 @@ minetest.register_tool(":worldedit:wand", {
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.pos1[name] = worldedit.pos2[name]
worldedit.mark_pos1(name)
end
end
@ -66,7 +57,7 @@ minetest.register_tool(":worldedit:wand", {
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.pos2[name] = worldedit.pos1[name]
worldedit.mark_pos2(name)
end
return itemstack -- nothing consumed, nothing changed

View File

@ -140,7 +140,7 @@ local function execute_worldedit_command(command_name, player_name, params)
assert(chatcmd, "unknown command: " .. command_name)
local _, msg = chatcmd.func(player_name, params)
if msg then
minetest.chat_send_player(player_name, msg)
worldedit.player_notify(player_name, msg)
end
end

View File

@ -4,10 +4,10 @@ worldedit = worldedit or {}
Example:
worldedit.register_gui_function("worldedit_gui_hollow_cylinder", {
name = "Make Hollow Cylinder",
privs = {worldedit=true},
get_formspec = function(name) return "some formspec here" end,
on_select = function(name) print(name .. " clicked the button!") end,
name = "Make Hollow Cylinder",
privs = {worldedit=true},
get_formspec = function(name) return "some formspec here" end,
on_select = function(name) print(name .. " clicked the button!") end,
})
Use `nil` for the `options` parameter to unregister the function associated with the given identifier.
@ -35,14 +35,14 @@ end
Example:
worldedit.register_gui_handler("worldedit_gui_hollow_cylinder", function(name, fields)
print(minetest.serialize(fields))
print(minetest.serialize(fields))
end)
]]
worldedit.register_gui_handler = function(identifier, handler)
local enabled = true
minetest.register_on_player_receive_fields(function(player, formname, fields)
if not enabled or formname ~= "" or fields.worldedit_gui then return false end
if not enabled then return false end
enabled = false
minetest.after(0.2, function() enabled = true end)
local name = player:get_player_name()
@ -80,7 +80,6 @@ if minetest.global_exists("unified_inventory") then -- unified inventory install
unified_inventory.register_button("worldedit_gui", {
type = "image",
image = "inventory_plus_worldedit_gui.png",
tooltip = "Edit your World!",
condition = function(player)
return minetest.check_player_privs(player:get_player_name(), {worldedit=true})
end,
@ -194,8 +193,7 @@ elseif minetest.global_exists("sfinv") then -- sfinv installed
get = function(self, player, context)
local can_worldedit = minetest.check_player_privs(player, {worldedit=true})
local fs = orig_get(self, player, context)
return fs .. (can_worldedit and "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]" ..
"tooltip[worldedit_gui;Edit your World!]" or "")
return fs .. (can_worldedit and "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]" or "")
end
})
@ -218,7 +216,7 @@ elseif minetest.global_exists("sfinv") then -- sfinv installed
end
end
else
return minetest.log("error",
error(
"worldedit_gui requires a supported gui management mod to be installed.\n"..
"To use the it you need to either:\n"..
"* use minetest_game or another sfinv-compatible subgame\n"..
@ -264,7 +262,7 @@ worldedit.register_gui_handler("worldedit_gui", function(name, fields)
--ensure player has permission to perform action
local has_privs, missing_privs = minetest.check_player_privs(name, entry.privs)
if not has_privs then
worldedit.player_notify(name, "you are not allowed to use this function (missing privileges: " .. table.concat(missing_privs, ", ") .. ")", "error")
worldedit.player_notify(name, "you are not allowed to use this function (missing privileges: " .. table.concat(missing_privs, ", ") .. ")")
return false
end
if entry.on_select then

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 B

After

Width:  |  Height:  |  Size: 597 B