mirror of
https://github.com/Uberi/Minetest-WorldEdit.git
synced 2024-12-04 21:33:43 +01:00
Split up some files in worldedit mod
This commit is contained in:
parent
dc1150fe3d
commit
acb3ecefe4
@ -6,3 +6,10 @@ read_globals = {"minetest", "vector", "VoxelArea", "ItemStack",
|
|||||||
globals = {"worldedit"}
|
globals = {"worldedit"}
|
||||||
-- Ignore these errors until someone decides to fix them
|
-- Ignore these errors until someone decides to fix them
|
||||||
ignore = {"212", "213", "411", "412", "421", "422", "431", "432", "631"}
|
ignore = {"212", "213", "411", "412", "421", "422", "431", "432", "631"}
|
||||||
|
|
||||||
|
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"},
|
||||||
|
}
|
||||||
|
@ -39,6 +39,6 @@ if minetest.settings:get_bool("log_mods") then
|
|||||||
end
|
end
|
||||||
|
|
||||||
if minetest.settings:get_bool("worldedit_run_tests") then
|
if minetest.settings:get_bool("worldedit_run_tests") then
|
||||||
dofile(path .. "/test.lua")
|
dofile(path .. "/test/init.lua")
|
||||||
minetest.after(0, worldedit.run_tests)
|
minetest.after(0, worldedit.run_tests)
|
||||||
end
|
end
|
||||||
|
@ -98,51 +98,6 @@ function worldedit.replace(pos1, pos2, search_node, replace_node, inverse)
|
|||||||
end
|
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"):split(" ")[1]) * 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 = 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
|
|
||||||
deferred_execution(step, finished)
|
|
||||||
|
|
||||||
return worldedit.volume(pos1, pos2) * amount
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Copies a region along `axis` by `amount` nodes.
|
--- Copies a region along `axis` by `amount` nodes.
|
||||||
-- @param pos1
|
-- @param pos1
|
||||||
-- @param pos2
|
-- @param pos2
|
||||||
@ -307,316 +262,6 @@ function worldedit.move(pos1, pos2, axis, amount)
|
|||||||
return worldedit.volume(pos1, pos2)
|
return worldedit.volume(pos1, pos2)
|
||||||
end
|
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=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
|
|
||||||
|
|
||||||
|
|
||||||
--- Attempts to fix the lighting in a region.
|
--- Attempts to fix the lighting in a region.
|
||||||
-- @return The number of nodes updated.
|
-- @return The number of nodes updated.
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
-- TODO: don't shit individual variables into the globals
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
-- Helpers
|
-- Helpers
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
local vec = vector.new
|
local vec = vector.new
|
||||||
local vecw = function(axis, n, base)
|
local vecw = function(axis, n, base)
|
||||||
local ret = vec(base)
|
local ret = vec(base)
|
||||||
@ -16,9 +17,9 @@ local set_node = minetest.set_node
|
|||||||
-- Nodes
|
-- Nodes
|
||||||
---------------------
|
---------------------
|
||||||
local air = "air"
|
local air = "air"
|
||||||
local testnode1
|
rawset(_G, "testnode1", "")
|
||||||
local testnode2
|
rawset(_G, "testnode2", "")
|
||||||
local testnode3
|
rawset(_G, "testnode3", "")
|
||||||
-- Loads nodenames to use for tests
|
-- Loads nodenames to use for tests
|
||||||
local function init_nodes()
|
local function init_nodes()
|
||||||
testnode1 = minetest.registered_aliases["mapgen_stone"]
|
testnode1 = minetest.registered_aliases["mapgen_stone"]
|
||||||
@ -27,7 +28,7 @@ local function init_nodes()
|
|||||||
assert(testnode1 and testnode2 and testnode3)
|
assert(testnode1 and testnode2 and testnode3)
|
||||||
end
|
end
|
||||||
-- Writes repeating pattern into given area
|
-- Writes repeating pattern into given area
|
||||||
local function place_pattern(pos1, pos2, pattern)
|
rawset(_G, "place_pattern", function(pos1, pos2, pattern)
|
||||||
local pos = vec()
|
local pos = vec()
|
||||||
local node = {name=""}
|
local node = {name=""}
|
||||||
local i = 1
|
local i = 1
|
||||||
@ -43,14 +44,14 @@ local function place_pattern(pos1, pos2, pattern)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end)
|
||||||
|
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
-- Area management
|
-- Area management
|
||||||
---------------------
|
---------------------
|
||||||
assert(minetest.get_mapgen_setting("mg_name") == "singlenode")
|
assert(minetest.get_mapgen_setting("mg_name") == "singlenode")
|
||||||
local area = {}
|
rawset(_G, "area", {})
|
||||||
do
|
do
|
||||||
local areamin, areamax
|
local areamin, areamax
|
||||||
local off
|
local off
|
||||||
@ -151,7 +152,7 @@ end
|
|||||||
---------------------
|
---------------------
|
||||||
-- Checks
|
-- Checks
|
||||||
---------------------
|
---------------------
|
||||||
local check = {}
|
rawset(_G, "check", {})
|
||||||
-- Check that all nodes in [pos1, pos2] are the node(s) specified
|
-- Check that all nodes in [pos1, pos2] are the node(s) specified
|
||||||
check.filled = function(pos1, pos2, nodes)
|
check.filled = function(pos1, pos2, nodes)
|
||||||
if type(nodes) == "string" then
|
if type(nodes) == "string" then
|
||||||
@ -218,7 +219,7 @@ end
|
|||||||
-- The actual tests
|
-- The actual tests
|
||||||
---------------------
|
---------------------
|
||||||
local tests = {}
|
local tests = {}
|
||||||
local function register_test(name, func, opts)
|
worldedit.register_test = function(name, func, opts)
|
||||||
assert(type(name) == "string")
|
assert(type(name) == "string")
|
||||||
assert(func == nil or type(func) == "function")
|
assert(func == nil or type(func) == "function")
|
||||||
if not opts then
|
if not opts then
|
||||||
@ -230,6 +231,7 @@ local function register_test(name, func, opts)
|
|||||||
opts.func = func
|
opts.func = func
|
||||||
table.insert(tests, opts)
|
table.insert(tests, opts)
|
||||||
end
|
end
|
||||||
|
local register_test = worldedit.register_test
|
||||||
-- How this works:
|
-- How this works:
|
||||||
-- register_test registers a test with a name and function
|
-- 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 function should return if the test passes or otherwise cause a Lua error
|
||||||
@ -279,270 +281,10 @@ register_test("pattern", function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
register_test("Generic node manipulations")
|
for _, name in ipairs({
|
||||||
register_test("worldedit.set", function()
|
"manipulations", "schematic"
|
||||||
local pos1, pos2 = area.get(10)
|
}) do
|
||||||
local m = area.margin(1)
|
dofile(minetest.get_modpath("worldedit") .. "/test/" .. name .. ".lua")
|
||||||
|
|
||||||
worldedit.set(pos1, pos2, testnode1)
|
|
||||||
|
|
||||||
check.filled(pos1, pos2, testnode1)
|
|
||||||
check.filled2(m, air)
|
|
||||||
end)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
register_test("Schematics")
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
do
|
|
||||||
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
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, e in ipairs(test_data) do
|
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
121
worldedit/test/manipulations.lua
Normal file
121
worldedit/test/manipulations.lua
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
---------------------
|
||||||
|
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)
|
162
worldedit/test/schematic.lua
Normal file
162
worldedit/test/schematic.lua
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
---------------------
|
||||||
|
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
|
357
worldedit/transformations.lua
Normal file
357
worldedit/transformations.lua
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
--- 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
|
Loading…
Reference in New Issue
Block a user