diff --git a/ChatCommands.md b/ChatCommands.md index b11aaf0..b26e894 100644 --- a/ChatCommands.md +++ b/ChatCommands.md @@ -386,3 +386,38 @@ This mode can be left with `//mtschemprob finish`. `//mtschemprob get` will disp Clears all objects within the WorldEdit region. //clearobjects + +### `//shift x/y/z/?/up/down/left/right/front/back [+|-]` + +Shifts the selection area by `[+|-]` without touching its contents. The shifting axis can be absolute (`x/y/z`) or +relative (`up/down/left/right/front/back`). + + //shift left 5 + +### `//expand [+|-]x/y/z/?/up/down/left/right/front/back [reverse-amount]` + +Expands the selection by `` in the selected absolute or relative axis. If specified, the selection can be expanded in the +opposite direction over the same axis by `[reverse-amount]`. + + //expand right 7 5 + +### `//contract [+|-]x/y/z/?/up/down/left/right/front/back [reverse-amount]` + +Contracts the selection by `` in the selected absolute or relative axis. If specified, the selection can be contracted in the +opposite direction over the same axis by `[reverse-amount]`. + + //expand right 7 5 + +### `//outset [hv] ` + +Expands the selection in all directions by ``. If specified, the selection can be expanded horizontally in the x and z axes `[h]` +or vertically in the y axis `[v]`. + + //outset v 5 + +### `//inset [hv] ` + +Contracts the selection in all directions by ``. If specified, the selection can be contracted horizontally in the x and z axes `[h]` +or vertically in the y axis `[v]`. + + //outset v 5 \ No newline at end of file diff --git a/worldedit_commands/.gitignore b/worldedit_commands/.gitignore new file mode 100644 index 0000000..e4e5f6c --- /dev/null +++ b/worldedit_commands/.gitignore @@ -0,0 +1 @@ +*~ \ No newline at end of file diff --git a/worldedit_commands/cuboid.lua b/worldedit_commands/cuboid.lua new file mode 100644 index 0000000..24dad28 --- /dev/null +++ b/worldedit_commands/cuboid.lua @@ -0,0 +1,239 @@ +dofile(minetest.get_modpath("worldedit_commands") .. "/cuboidapi.lua") + + +minetest.register_chatcommand("/outset", { + params = "[h|v] ", + description = "outset the selection", + privs = {worldedit=true}, + func = function(name, param) + local find, _, dir, amount = param:find("(%a*)%s*([+-]?%d+)") + + if find == nil then + return false, "invalid usage: " .. param + end + + local pos1 = worldedit.pos1[name] + local pos2 = worldedit.pos2[name] + + if pos1 == nil or pos2 == nil then + return false, + "Undefined region. Region must be defined beforehand." + end + + local hv_test = dir:find("[^hv]+") + + if hv_test ~= nil then + return false, "Invalid direction." + end + + if dir == "" or dir == "hv" or dir == "vh" then + assert(worldedit.cuboid_volumetric_expand(name, amount)) + elseif dir == "h" then + assert(worldedit.cuboid_linear_expand(name, 'x', 1, amount)) + assert(worldedit.cuboid_linear_expand(name, 'x', -1, amount)) + assert(worldedit.cuboid_linear_expand(name, 'z', 1, amount)) + assert(worldedit.cuboid_linear_expand(name, 'z', -1, amount)) + elseif dir == "v" then + assert(worldedit.cuboid_linear_expand(name, 'y', 1, amount)) + assert(worldedit.cuboid_linear_expand(name, 'y', -1, amount)) + else + return false, "Invalid number of arguments" + end + + worldedit.marker_update(name) + return true, "Region outset by " .. amount .. " blocks" + end, + } +) + + +minetest.register_chatcommand("/inset", { + params = "[h|v] ", + description = "inset the selection", + privs = {worldedit=true}, + func = function(name, param) + local find, _, dir, amount = param:find("(%a*)%s*([+-]?%d+)") + + if find == nil then + return false, "invalid usage: " .. param + end + + local pos1 = worldedit.pos1[name] + local pos2 = worldedit.pos2[name] + + if pos1 == nil or pos2 == nil then + return false, + "Undefined region. Region must be defined beforehand." + end + + local hv_test = dir:find("[^hv]+") + + if hv_test ~= nil then + return false, "Invalid direction." + end + + if dir == "" or dir == "vh" or dir == "hv" then + assert(worldedit.cuboid_volumetric_expand(name, -amount)) + elseif dir == "h" then + assert(worldedit.cuboid_linear_expand(name, 'x', 1, -amount)) + assert(worldedit.cuboid_linear_expand(name, 'x', -1, -amount)) + assert(worldedit.cuboid_linear_expand(name, 'z', 1, -amount)) + assert(worldedit.cuboid_linear_expand(name, 'z', -1, -amount)) + elseif dir == "v" then + assert(worldedit.cuboid_linear_expand(name, 'y', 1, -amount)) + assert(worldedit.cuboid_linear_expand(name, 'y', -1, -amount)) + else + return false, "Invalid number of arguments" + end + + worldedit.marker_update(name) + return true, "Region inset by " .. amount .. " blocks" + end, + } +) + + +minetest.register_chatcommand("/shift", { + params = "[x|y|z|?|up|down|left|right|front|back] [+|-]", + description = "Moves the selection region. Does not move contents.", + privs = {worldedit=true}, + func = function(name, param) + local pos1 = worldedit.pos1[name] + local pos2 = worldedit.pos2[name] + local find, _, direction, amount = param:find("([%?%l]+)%s*([+-]?%d+)") + + if find == nil then + worldedit.player_notify(name, "invalid usage: " .. param) + return + end + + if pos1 == nil or pos2 == nil then + worldedit.player_notify(name, + "Undefined region. Region must be defined beforehand.") + return + end + + local axis, dir + if direction ~= "?" then + axis, dir = worldedit.translate_direction(name, direction) + else + axis, dir = worldedit.player_axis(name) + end + + if axis == nil or dir == nil then + return false, "Invalid if looking straight up or down" + end + + assert(worldedit.cuboid_shift(name, axis, amount * dir)) + worldedit.marker_update(name) + + return true, "region shifted by " .. amount .. " nodes" + end, + } +) + + +minetest.register_chatcommand("/expand", { + params = "[+|-] [reverse-amount]", + description = "expand the selection in one or two directions at once", + privs = {worldedit=true}, + func = function(name, param) + local find, _, sign, direction, amount, + rev_amount = param:find("([+-]?)([%?%l]+)%s*(%d+)%s*(%d*)") + + if find == nil then + worldedit.player_notify(name, "invalid use: " .. param) + return + end + + if worldedit.pos1[name] == nil or worldedit.pos2[name] == nil then + worldedit.player_notify(name, + "Undefined region. Region must be defined beforehand.") + return + end + + local absolute = direction:find("[xyz?]") + local dir, axis + + if rev_amount == "" then + rev_amount = 0 + end + + if absolute == nil then + axis, dir = worldedit.translate_direction(name, direction) + + if axis == nil or dir == nil then + return false, "Invalid if looking straight up or down" + end + else + if direction == "?" then + axis, dir = worldedit.player_axis(name) + else + axis = direction + dir = 1 + end + end + + if sign == "-" then + dir = -dir + end + + worldedit.cuboid_linear_expand(name, axis, dir, amount) + worldedit.cuboid_linear_expand(name, axis, -dir, rev_amount) + worldedit.marker_update(name) + end, + } +) + + +minetest.register_chatcommand("/contract", { + params = "[+|-] [reverse-amount]", + description = "contract the selection in one or two directions at once", + privs = {worldedit=true}, + func = function(name, param) + local find, _, sign, direction, amount, + rev_amount = param:find("([+-]?)([%?%l]+)%s*(%d+)%s*(%d*)") + + if find == nil then + worldedit.player_notify(name, "invalid use: " .. param) + return + end + + if worldedit.pos1[name] == nil or worldedit.pos2[name] == nil then + worldedit.player_notify(name, + "Undefined region. Region must be defined beforehand.") + return + end + + local absolute = direction:find("[xyz?]") + local dir, axis + + if rev_amount == "" then + rev_amount = 0 + end + + if absolute == nil then + axis, dir = worldedit.translate_direction(name, direction) + + if axis == nil or dir == nil then + return false, "Invalid if looking straight up or down" + end + else + if direction == "?" then + axis, dir = worldedit.player_axis(name) + else + axis = direction + dir = 1 + end + end + + if sign == "-" then + dir = -dir + end + + worldedit.cuboid_linear_expand(name, axis, dir, -amount) + worldedit.cuboid_linear_expand(name, axis, -dir, -rev_amount) + worldedit.marker_update(name) + end, + } +) \ No newline at end of file diff --git a/worldedit_commands/cuboidapi.lua b/worldedit_commands/cuboidapi.lua new file mode 100644 index 0000000..ce20761 --- /dev/null +++ b/worldedit_commands/cuboidapi.lua @@ -0,0 +1,258 @@ +-- Expands or contracts the cuboid in all axes by amount (positive or negative) +worldedit.cuboid_volumetric_expand = function(name, amount) + local pos1 = worldedit.pos1[name] + local pos2 = worldedit.pos2[name] + + if pos1 == nil or pos2 == nil then + return false, "Undefined cuboid" + end + + local delta1 = vector.new() + 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) + delta1 = vector.multiply(delta1, delta_dir1) + delta2 = vector.multiply(delta2, delta_dir2) + worldedit.pos1[name] = vector.add(pos1, delta1) + worldedit.pos2[name] = vector.add(pos2, delta2) + + return true +end + + +-- Expands or contracts the cuboid in a single axis by amount (positive or negative) +worldedit.cuboid_linear_expand = function(name, axis, direction, amount) + local pos1 = worldedit.pos1[name] + local pos2 = worldedit.pos2[name] + + if pos1 == nil or pos2 == nil then + return false, "undefined cuboid" + end + + if direction ~= 1 and direction ~= -1 then + return false, "invalid marker" + end + + local marker = 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 + deltavect.y = amount * direction + elseif axis == 'z' then + deltavect.z = amount * direction + else + return false, "invalid axis" + end + + worldedit.marker_move(name, marker, deltavect) + return true +end + + +-- Shifts the cuboid by '+-amount' in axis 'axis' +worldedit.cuboid_shift = function(name, axis, amount) + local pos1 = worldedit.pos1[name] + local pos2 = worldedit.pos2[name] + + if pos1 == nil or pos2 == nil then + return false, "undefined cuboid" + end + + if axis == 'x' then + worldedit.pos1[name].x = pos1.x + amount + worldedit.pos2[name].x = pos2.x + amount + elseif axis == 'y' then + worldedit.pos1[name].y = pos1.y + amount + worldedit.pos2[name].y = pos2.y + amount + elseif axis == 'z' then + worldedit.pos1[name].z = pos1.z + amount + worldedit.pos2[name].z = pos2.z + amount + else + return false, "invalid axis" + end + + return true +end + + +-- Moves the location of a single marker by adding deltavector +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) + else + local pos = worldedit.pos2[name] + worldedit.pos2[name] = vector.add(deltavector, pos) + end + + return true +end + +-- Updates the location ingame of the markers +worldedit.marker_update = function(name, marker) + if marker == nil then + worldedit.mark_pos1(name) + worldedit.mark_pos2(name) + elseif marker == 1 then + worldedit.mark_pos1(name) + elseif marker == 2 then + worldedit.mark_pos2(name) + else + minetest.debug( + "worldedit: Invalid execution of function update_markers") + end +end + + +-- Returns two vectors with the directions for volumetric expansion +worldedit.get_expansion_directions = function(mark1, mark2) + if mark1 == nil or mark2 == nil then + return + end + local dir1 = vector.new() + local dir2 = vector.new() + + if mark1.x < mark2.x then + dir1.x = -1 + dir2.x = 1 + else + dir1.x = 1 + dir2.x = -1 + end + if mark1.y < mark2.y then + dir1.y = -1 + dir2.y = 1 + else + dir1.y = 1 + dir2.y = -1 + end + if mark1.z < mark2.z then + dir1.z = -1 + dir2.z = 1 + else + dir1.z = 1 + dir2.z = -1 + end + return dir1, dir2 +end + + +-- Return the marker that is closest to the player +worldedit.marker_get_closest_to_player = function(name) + local playerpos = minetest.get_player_by_name(name):getpos() + local dist1 = vector.distance(playerpos, worldedit.pos1[name]) + local dist2 = vector.distance(playerpos, worldedit.pos2[name]) + + if dist1 < dist2 then + return 1 + else + return 2 + end +end + + +-- Returns the closest marker to the specified axis and direction +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 + + if axis == 'x' then + pos1.x = worldedit.pos1[name].x * direction + pos2.x = worldedit.pos2[name].x * direction + if pos1.x > pos2.x then + return 1 + else + return 2 + end + elseif axis == 'y' then + pos1.y = worldedit.pos1[name].y * direction + pos2.y = worldedit.pos2[name].y * direction + if pos1.y > pos2.y then + return 1 + else + return 2 + end + elseif axis == 'z' then + pos1.z = worldedit.pos1[name].z * direction + pos2.z = worldedit.pos2[name].z * direction + if pos1.z > pos2.z then + return 1 + else + return 2 + end + else + return nil + end +end + + +-- 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 + resdir = nil + else + resaxis = axis + resdir = dir + end + end + + if direction == "back" then + if axis == "y" then + resaxis = nil + resdir = nil + else + resaxis = axis + resdir = -dir + end + end + + if direction == "left" then + if axis == 'x' then + resaxis = 'z' + resdir = dir + elseif axis == 'z' then + resaxis = 'x' + resdir = -dir + end + end + + if direction == "right" then + if axis == 'x' then + resaxis = 'z' + resdir = -dir + elseif axis == 'z' then + resaxis = 'x' + resdir = dir + end + end + + return resaxis, resdir +end \ No newline at end of file diff --git a/worldedit_commands/init.lua b/worldedit_commands/init.lua index c93a760..eedc372 100644 --- a/worldedit_commands/init.lua +++ b/worldedit_commands/init.lua @@ -10,6 +10,7 @@ if minetest.place_schematic then worldedit.prob_list = {} end +dofile(minetest.get_modpath("worldedit_commands") .. "/cuboid.lua") dofile(minetest.get_modpath("worldedit_commands") .. "/mark.lua") local safe_region, check_region = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua") @@ -1181,4 +1182,4 @@ minetest.register_chatcommand("/clearobjects", { local count = worldedit.clear_objects(worldedit.pos1[name], worldedit.pos2[name]) worldedit.player_notify(name, count .. " objects cleared") end), -}) +}) \ No newline at end of file