diff --git a/CHANGELOG.md b/CHANGELOG.md index 04554d7..8d87c90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ Note to self: See the bottom of this file for the release template text. - Add `//sfactor` (_selection factor_) - Selection Tools by @VorTechnix are finished for now. - Add `//mface` (_measure facing_), `//midpos` (_measure middle position_), `//msize` (_measure size_), `//mtrig` (_measure trigonometry_) - Measuring Tools implemented by @VorTechnix. - Add `//airapply` for applying commands only to air nodes in the defined region + - Add `//wcorner` (_wireframe corners_), `//wbox` (_wireframe box_), `//compass` (_wireframe compass_) - Wireframes implemented by @VorTechnix. + - Add `//for` for executing commands while changing their arguments - Implemented by @VorTechnix. + - Add `//sshift` (_selection shift_) - WorldEdit cuboid manipulator replacements implemented by @VorTechnix. - Add `//noise2d` for perturbing terrain with multiple different noise functions - Add `//noiseapply2d` for running commands on columns where a noise value is over a threshold - Add `//ellipsoid2` which creates an ellipsoid that fills the defined region diff --git a/Chat-Command-Reference.md b/Chat-Command-Reference.md index 49ecb9e..d812480 100644 --- a/Chat-Command-Reference.md +++ b/Chat-Command-Reference.md @@ -193,7 +193,32 @@ Floods all connected nodes of the same type starting at _pos1_ with ` | [ [ ` +## `//wbox ` +Sets the edges of the current selection to ``to create an outline of a rectangular prism. Useful for roughing in walls. + +```weacmd +//wbox silver_sandstone +//wbox dirt +``` + +## `//wcompass []` +Creates a compass around pos1 with a single node bead pointing north (+Z). + +```weacmd +//wcompass meselamp +//wcompass desert_cobble torch +//wcompass gold diamond +``` + +## `//wcorner ` +Set the corners of the current selection to ``. Useful for outlining building sites and setting boundaries. + +```weacmd +//wcorner glass +//wcorner stone_with_iron +``` + +## `//scale | [ [ ` Advanced version of [`//stretch` from WorldEdit](https://github.com/Uberi/Minetest-WorldEdit/blob/master/ChatCommands.md#stretch-stretchx-stretchy-stretchz) that can scale both up and down at the same time by transparently splitting it into 2 different operations. Scaling up is *always* done before scaling down. Although the syntax looks complicated, it's really quite simple. The key concept to understand is that of the scale factor. It refers to how much the defined region should be scaled up or down by, and can be specified in multiple different ways: @@ -286,6 +311,8 @@ This command is best explained with examples: The above functions just like `//replace` - nothing special going on here. It replaces all `dirt` nodes with `stone`. + + Let's make it more interesting: ```weacmd @@ -728,17 +755,25 @@ Short for _select center_. Sets pos1 and pos2 to the centre point(s) of the curr //scentre ``` - -### `//srel [ [ ]]` +## `//srel [ [ ]]` Short for _select relative_. Sets the pos2 at set distances along 3 axes relative to pos1. If pos1 is not set it will default to the node directly under the player. The axis arguments accept `x, y, z` as well as `up, down, left, right, front, back`. Left, right, front and back are relative to player facing direction. Negative (`-`) can be applied to the axis, the length or both. Implementation thanks to @VorTechnix. ```weacmd //srel front 5 //srel y 12 right -2 //srel left 3 up 5 -front 7 -//scube -z 12 -y -2 x -2 +//srel -z 12 -y -2 x -2 ``` +## `//sshift [ [ ]]` +Short for _selection shift_. Shifts the WorldEdit region along 3 axes. The axis arguments accept `x, y, z` as well as `up, down, left, right, front, back`. Left, right, front and back are relative to player facing direction. Negative (`-`) can be applied to the axis, the length or both. Implementation thanks to @VorTechnix. + +```weacmd +//sshift back 4 +//sshift right -2 up 2 +//sshift -left 2 z -7 -y -4 +//sshift -z 12 -y -2 x -2 +``` ### `//smake [ []]` Short for _selection make_. Modifies existing selection by moving pos2. Allows you to make the selection an odd or even length on one or more axes or set two or more axes equal to each other or the longest, shortest or average of them. Implementation thanks to @VorTechnix. @@ -786,7 +821,7 @@ If `` | `` becomes optional. If present it will be ignored. Name | Description ------------|------------------ `` | Specify axes to perform operation on (default= xz)| -``: If `` == odd or even | Does nothing +``: If `` == odd or even | Does nothing ``: If `` == equal | Overrides `` and sets all `` axes equal to itself ### `//sfactor []` diff --git a/worldeditadditions/init.lua b/worldeditadditions/init.lua index 8138a04..7403736 100644 --- a/worldeditadditions/init.lua +++ b/worldeditadditions/init.lua @@ -9,6 +9,8 @@ worldeditadditions = {} local wea = worldeditadditions wea.modpath = minetest.get_modpath("worldeditadditions") +wea.Set = dofile(wea.modpath.."/utils/set.lua") + wea.Vector3 = dofile(wea.modpath.."/utils/vector3.lua") wea.Mesh, wea.Face = dofile(wea.modpath.."/utils/mesh.lua") @@ -30,7 +32,7 @@ dofile(wea.modpath.."/utils/nodes.lua") dofile(wea.modpath.."/utils/node_identification.lua") dofile(wea.modpath.."/utils/terrain.lua") dofile(wea.modpath.."/utils/raycast_adv.lua") -- For the farwand -dofile(wea.modpath.."/utils/axes.lua") +dofile(wea.modpath.."/utils/player.lua") -- Player info functions dofile(wea.modpath.."/lib/compat/saplingnames.lua") @@ -68,5 +70,9 @@ dofile(wea.modpath.."/lib/airapply.lua") dofile(wea.modpath.."/lib/noiseapply2d.lua") dofile(wea.modpath.."/lib/subdivide.lua") -dofile(wea.modpath.."/lib/selection/stack.lua") -dofile(wea.modpath.."/lib/selection/cloud.lua") + +dofile(wea.modpath.."/lib/selection/init.lua") -- Helpers for selections + +dofile(wea.modpath.."/lib/wireframe/corner_set.lua") +dofile(wea.modpath.."/lib/wireframe/make_compass.lua") +dofile(wea.modpath.."/lib/wireframe/wire_box.lua") diff --git a/worldeditadditions/lib/selection/cloud.lua b/worldeditadditions/lib/selection/cloud.lua deleted file mode 100644 index 2910438..0000000 --- a/worldeditadditions/lib/selection/cloud.lua +++ /dev/null @@ -1,48 +0,0 @@ --- ██████ ██ ██████ ██ ██ ██████ --- ██ ██ ██ ██ ██ ██ ██ ██ --- ██ ██ ██ ██ ██ ██ ██ ██ --- ██ ██ ██ ██ ██ ██ ██ ██ --- ██████ ███████ ██████ ██████ ██████ -worldeditadditions.add_pos = {} -worldeditadditions.selection = {} -function worldeditadditions.selection.add_point(name, pos) - if pos ~= nil then - local is_new = not worldedit.pos1[name] and not worldedit.pos2[name] - -- print("[set_pos1]", name, "("..pos.x..", "..pos.y..", "..pos.z..")") - if not worldedit.pos1[name] then worldedit.pos1[name] = vector.new(pos) end - if not worldedit.pos2[name] then worldedit.pos2[name] = vector.new(pos) end - - worldedit.marker_update(name) - - local volume_before = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) - - worldedit.pos1[name], worldedit.pos2[name] = worldeditadditions.vector.expand_region(worldedit.pos1[name], worldedit.pos2[name], pos) - - local volume_after = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) - - local volume_difference = volume_after - volume_before - - worldedit.marker_update(name) - print("DEBUG volume_before", volume_before, "volume_after", volume_after) - if is_new then - local msg = "Created new region of "..volume_after.." node" - if volume_after ~= 1 then msg = msg.."s" end - worldedit.player_notify(name, msg) - else - local msg = "Expanded region by "..volume_difference.." node" - if volume_difference ~= 1 then msg = msg.."s" end - worldedit.player_notify(name, msg) - end - else - worldedit.player_notify(name, "Error: Too far away (try raising your maxdist with //farwand maxdist )") - -- print("[set_pos1]", name, "nil") - end -end -function worldeditadditions.selection.clear_points(name) - worldedit.pos1[name] = nil - worldedit.pos2[name] = nil - worldedit.marker_update(name) - worldedit.set_pos[name] = nil - - worldedit.player_notify(name, "Region cleared") -end diff --git a/worldeditadditions/lib/selection/init.lua b/worldeditadditions/lib/selection/init.lua new file mode 100644 index 0000000..ce3fedf --- /dev/null +++ b/worldeditadditions/lib/selection/init.lua @@ -0,0 +1,7 @@ +local wea = worldeditadditions +local wea_m = wea.modpath .. "/lib/selection/" + +wea.add_pos = {} + +wea.selection = dofile(wea_m.."selection.lua") +dofile(wea_m.."stack.lua") diff --git a/worldeditadditions/lib/selection/selection.lua b/worldeditadditions/lib/selection/selection.lua new file mode 100644 index 0000000..e32fc08 --- /dev/null +++ b/worldeditadditions/lib/selection/selection.lua @@ -0,0 +1,68 @@ +-- ███████ ███████ ██ ███████ ██████ ████████ ██ ██████ ███ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +-- ███████ █████ ██ █████ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ███████ ███████ ███████ ███████ ██████ ██ ██ ██████ ██ ████ + +---Selection helpers and modifiers +local selection = {} + +--- Additively adds a point to the current selection or +-- makes a selection from the provided point. +-- @param name string Player name. +-- @param pos vector The position to include. +function selection.add_point(name, pos) + if pos ~= nil then + -- print("[set_pos1]", name, "("..pos.x..", "..pos.y..", "..pos.z..")") + if not worldedit.pos1[name] then worldedit.pos1[name] = vector.new(pos) end + if not worldedit.pos2[name] then worldedit.pos2[name] = vector.new(pos) end + + worldedit.marker_update(name) + + local volume_before = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) + + worldedit.pos1[name], worldedit.pos2[name] = worldeditadditions.vector.expand_region(worldedit.pos1[name], worldedit.pos2[name], pos) + + local volume_after = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) + + local volume_difference = volume_after - volume_before + + worldedit.marker_update(name) + worldedit.player_notify(name, "Expanded region by "..volume_difference.." nodes") + else + worldedit.player_notify(name, "Error: Too far away (try raising your maxdist with //farwand maxdist )") + -- print("[set_pos1]", name, "nil") + end +end + +--- Clears current selection. +-- @param name string Player name. +function selection.clear_points(name) + worldedit.pos1[name] = nil + worldedit.pos2[name] = nil + worldedit.marker_update(name) + worldedit.set_pos[name] = nil + + worldedit.player_notify(name, "Region cleared") +end + +--- Checks if a string is a valid axis. +-- @param str string String to check (be sure to remove any + or -). +-- @param hv bool Include "h" (general horizontal) and "v" (general vertical). +-- @return bool If string is a valid axis then true. +function selection.check_axis(str,hv) + if hv then + return (str == "x" or str == "y" or str == "z" or str == "h" or str == "v") + else + return (str == "x" or str == "y" or str == "z") + end +end + +--- Checks if a string is a valid dir. +-- @param str string String to check (be sure to remove any + or -). +-- @return bool If string is a valid dir then true. +function selection.check_dir(str) + return (str == "front" or str == "back" or str == "left" or str == "right" or str == "up" or str == "down") +end + +return selection diff --git a/worldeditadditions/lib/wireframe/corner_set.lua b/worldeditadditions/lib/wireframe/corner_set.lua new file mode 100644 index 0000000..b9a1a41 --- /dev/null +++ b/worldeditadditions/lib/wireframe/corner_set.lua @@ -0,0 +1,25 @@ +-- ██████ ██████ ██████ ███ ██ ███████ ██████ ███████ ███████ ████████ +-- ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ +-- ██ ██ ██ ██████ ██ ██ ██ █████ ██████ ███████ █████ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██████ ██████ ██ ██ ██ ████ ███████ ██ ██ ███████ ███████ ██ + +--- Puts a node at each corner of selection box. +-- @param {Position} pos1 The 1st position defining the WorldEdit selection +-- @param {Position} pos2 The 2nd positioon defining the WorldEdit selection +-- @param {string} node Name of the node to place +function worldeditadditions.corner_set(pos1,pos2,node) + + -- z y x is the preferred loop order (because CPU cache I'd guess, since then we're iterating linearly through the data array) + local counts = { replaced = 0 } + for k,z in pairs({pos1.z,pos2.z}) do + for k,y in pairs({pos1.y,pos2.y}) do + for k,x in pairs({pos1.x,pos2.x}) do + minetest.set_node(vector.new(x,y,z), {name=node}) + counts.replaced = counts.replaced + 1 + end + end + end + + return true, counts.replaced +end diff --git a/worldeditadditions/lib/wireframe/make_compass.lua b/worldeditadditions/lib/wireframe/make_compass.lua new file mode 100644 index 0000000..a272df7 --- /dev/null +++ b/worldeditadditions/lib/wireframe/make_compass.lua @@ -0,0 +1,32 @@ +-- ███ ███ █████ ██ ██ ███████ ██████ ██████ ███ ███ ██████ █████ ███████ ███████ +-- ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██ +-- ██ ████ ██ ███████ █████ █████ ██ ██ ██ ██ ████ ██ ██████ ███████ ███████ ███████ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ ██ ██ ██ ██ ██ ███████ ██████ ██████ ██ ██ ██ ██ ██ ███████ ███████ + +--- Makes a compass with a bead pointing north (+Z). +-- @param {Position} pos1 The 1st position defining the WorldEdit selection +-- @param {string} node1 Name of the node to place +-- @param {string} node2 Name of the node of the bead +function worldeditadditions.make_compass(pos1,node1,node2) + + minetest.set_node(vector.add(pos1,vector.new(0,1,3)), {name=node2}) + local counts = { replaced = 1 } + + -- z y x is the preferred loop order (because CPU cache I'd guess, since then we're iterating linearly through the data array) + for z = -3,3 do + if z ~= 0 then + for k,x in pairs({math.floor(-3/math.abs(z)),0,math.ceil(3/math.abs(z))}) do + minetest.set_node(vector.new(pos1.x+x,pos1.y,pos1.z+z), {name=node1}) + counts.replaced = counts.replaced + 1 + end + else + for x = -3,3 do + minetest.set_node(vector.new(pos1.x+x,pos1.y,pos1.z), {name=node1}) + counts.replaced = counts.replaced + 1 + end + end + end + + return true, counts.replaced +end diff --git a/worldeditadditions/lib/wireframe/wire_box.lua b/worldeditadditions/lib/wireframe/wire_box.lua new file mode 100644 index 0000000..fc24b04 --- /dev/null +++ b/worldeditadditions/lib/wireframe/wire_box.lua @@ -0,0 +1,55 @@ +-- ██ ██ ██ ██████ ███████ ██████ ██████ ██ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ █ ██ ██ ██████ █████ ██████ ██ ██ ███ +-- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ███ ███ ██ ██ ██ ███████ ██████ ██████ ██ ██ + +--- Fills the edges of the selection box with nodes. +-- @param {Position} pos1 The 1st position defining the WorldEdit selection +-- @param {Position} pos2 The 2nd positioon defining the WorldEdit selection +-- @param {string} node Name of the node to place +local v3 = worldeditadditions.Vector3 +function worldeditadditions.wire_box(pos1,pos2,node) + local node_id_replace = minetest.get_content_id(node) + local ps1, ps2 = v3.sort(pos1,pos2) + + -- Fetch the nodes in the specified area + local manip, area = worldedit.manip_helpers.init(pos1, pos2) + local data = manip:get_data() + + -- Using three loops to reduce the number of nodes processed + local counts = { replaced = 0 } + + for z = ps1.z,ps2.z do + for _j,y in pairs({pos1.y,pos2.y}) do + for _k,x in pairs({pos1.x,pos2.x}) do + data[area:index(x, y, z)] = node_id_replace + counts.replaced = counts.replaced + 1 + end + end + end + if math.abs(ps2.y-ps1.y) > 1 then + for _j,z in pairs({pos1.z,pos2.z}) do + for y = pos1.y+1,pos2.y-1 do + for _k,x in pairs({pos1.x,pos2.x}) do + data[area:index(x, y, z)] = node_id_replace + counts.replaced = counts.replaced + 1 + end + end + end + end + if math.abs(ps2.x-ps1.x) > 1 then + for _j,z in pairs({pos1.z,pos2.z}) do + for _k,y in pairs({pos1.y,pos2.y}) do + for x = pos1.x+1,pos2.x-1 do + data[area:index(x, y, z)] = node_id_replace + counts.replaced = counts.replaced + 1 + end + end + end + end + -- Save the modified nodes back to disk & return + worldedit.manip_helpers.finish(manip, data) + + return true, counts.replaced +end diff --git a/worldeditadditions/utils/axes.lua b/worldeditadditions/utils/axes.lua deleted file mode 100644 index dd7b3a6..0000000 --- a/worldeditadditions/utils/axes.lua +++ /dev/null @@ -1,39 +0,0 @@ --- Returns the player's facing direction on the horizontal axes only. --- @param name string The name of the player to return facing direction of. --- @return Returns axis name and sign multiplier. -function worldeditadditions.player_axis2d(name) - -- minetest.get_player_by_name("singleplayer"): - local dir = minetest.get_player_by_name(name):get_look_dir() - local x, z= math.abs(dir.x), math.abs(dir.z) - if x > z then return "x", dir.x > 0 and 1 or -1 - else return "z", dir.z > 0 and 1 or -1 end -end - --- Returns the axis and sign of the axis to the left of the input axis. --- @param axis string x or z. --- @param sign int Sign multiplier. --- @return Returns axis name and sign multiplier. -function worldeditadditions.axis_left(axis,sign) - if not axis:match("[xz]") then return false, "Error: Not a horizontal axis!" - elseif axis == "x" then return true, "z", sign - else return true, "x", -sign end -end - ---- Dehumanize Direction: translates up, down, left, right, front, into xyz based on player orientation. --- @param name string The name of the player to return facing direction of. --- @param dir string Relative direction to translate. --- @return Returns axis name and sign multiplier. -function worldeditadditions.dir_to_xyz(name, dir) - local axfac, drfac = worldeditadditions.player_axis2d(name) - local _, axlft, drlft = worldeditadditions.axis_left(axfac,drfac) - if dir:match("front") or dir:match("back") then - return axfac, dir:match("front") and drfac or -drfac - elseif dir:match("left") or dir:match("right") then - return axlft, dir:match("left") and drlft or -drlft - elseif dir:match("up") or dir:match("down") then - return "y", dir == "down" and -1 or 1 - else return false, "\"" .. dir .. "\" not a recognized direction! Try: (up | down | left | right | front | back)" end -end - --- Tests --- /lua print(worldeditadditions.table.unpack(worldeditadditions.player_axis2d(myname))) diff --git a/worldeditadditions/utils/numbers.lua b/worldeditadditions/utils/numbers.lua index 2bc93d1..0a6f159 100644 --- a/worldeditadditions/utils/numbers.lua +++ b/worldeditadditions/utils/numbers.lua @@ -82,9 +82,12 @@ end -- @param src string|int Input string. -- @return string|int Returns the signed multiplier (1|-1). function worldeditadditions.getsign(src) - if type(src) == "number" then return src < 0 and -1 or 1 + if type(src) == "number" then + if src < 0 then return -1 else return 1 end elseif type(src) ~= "string" then return 1 - else return src:match('-') and -1 or 1 end + else + if src:match('-') then return -1 else return 1 end + end end --- Clamp a number to ensure it falls within a given range. diff --git a/worldeditadditions/utils/player.lua b/worldeditadditions/utils/player.lua new file mode 100644 index 0000000..6eeb38e --- /dev/null +++ b/worldeditadditions/utils/player.lua @@ -0,0 +1,72 @@ +local wea = worldeditadditions +local v3 = worldeditadditions.Vector3 +--- Returns the player's position (at leg level). +-- @param name string The name of the player to return facing direction of. +-- @return vector Returns position. +function worldeditadditions.player_vector(name) + return minetest.get_player_by_name(name):get_pos() +end + +--- Returns the player's facing info including relative DIRs. +-- @param name string The name of the player to return facing direction of. +-- @return table (vector3+) xyz raw values and {axis,sign} tables for facing direction and +-- relative direction keys (front, back, left, right, up, down). +function worldeditadditions.player_dir(name) + local dir = v3.clone(minetest.get_player_by_name(name):get_look_dir()) + local abs = dir:abs() + -- Facing info + if abs.x > abs.z then dir.facing = {axis="x",sign=wea.getsign(dir.x)} + else dir.facing = {axis="z",sign=wea.getsign(dir.z)} end + -- Set front and back + dir.front = dir.facing + dir.back = {axis=dir.facing.axis,sign=dir.facing.sign*-1} + -- Set left and right + if dir.facing.axis == "x" then dir.left = {axis="z", sign=dir.facing.sign} + else dir.left = {axis="x", sign=dir.facing.sign*-1} end + dir.right = {axis=dir.left.axis,sign=dir.left.sign*-1} + -- Set up and down + dir.up = {axis="y",sign=1} + dir.down = {axis="y",sign=-1} + return dir +end +-- /lua print(worldeditadditions.vector.tostring(minetest.get_player_by_name(myname):get_look_dir())) + +--- DEPRICATED ================================================================= +-- TODO: Refactor commands that use the following functions to use player_dir then delete these functions + +--- Returns the player's facing direction on the horizontal axes only. +-- @param name string The name of the player to return facing direction of. +-- @return string,int Returns axis name and sign multiplier. +function worldeditadditions.player_axis2d(name) + -- minetest.get_player_by_name("singleplayer"): + local dir = minetest.get_player_by_name(name):get_look_dir() + local x, z= math.abs(dir.x), math.abs(dir.z) + if x > z then return "x", dir.x > 0 and 1 or -1 + else return "z", dir.z > 0 and 1 or -1 end +end + +--- Returns the axis and sign of the axis to the left of the input axis. +-- @param axis string x or z. +-- @param sign int Sign multiplier. +-- @return string,int Returns axis name and sign multiplier. +function worldeditadditions.axis_left(axis,sign) + if not axis:match("[xz]") then return false, "Error: Not a horizontal axis!" + elseif axis == "x" then return true, "z", sign + else return true, "x", -sign end +end + +--- Dehumanize Direction: translates up, down, left, right, front, into xyz based on player orientation. +-- @param name string The name of the player to return facing direction of. +-- @param dir string Relative direction to translate. +-- @return string Returns axis name and sign multiplier. +function worldeditadditions.dir_to_xyz(name, dir) + local axfac, drfac = worldeditadditions.player_axis2d(name) + local _, axlft, drlft = worldeditadditions.axis_left(axfac,drfac) + if dir:match("front") or dir:match("back") then + return axfac, dir:match("front") and drfac or -drfac + elseif dir:match("left") or dir:match("right") then + return axlft, dir:match("left") and drlft or -drlft + elseif dir:match("up") or dir:match("down") then + return "y", dir == "down" and -1 or 1 + else return false, "\"" .. dir .. "\" not a recognized direction! Try: (up | down | left | right | front | back)" end +end diff --git a/worldeditadditions/utils/set.lua b/worldeditadditions/utils/set.lua new file mode 100644 index 0000000..d5ea57d --- /dev/null +++ b/worldeditadditions/utils/set.lua @@ -0,0 +1,101 @@ +--- Sets for lua! +-- local Set = {} +--- Option 1: +-- Set.__index = Set +-- Set.__newindex = function(tbl, key, value) +-- if not tbl.__protected[key] then +-- rawset(tbl,key,value) +-- else +-- error("Protected!") +-- end +-- end +-- Set.__protected = { +-- __protected=true, +-- new=true, +-- add=true, +-- delete=true, +-- has=true +-- } + +--- Option 2: +local Set = {} +Set.__index = Set +Set.__newindex = function(tbl, key, value) + rawset(tbl.set,key,value) +end + +--- Creates a new set. +-- @param i any Initial values(s) of the set. +-- @returns Set A table of keys equal to true. +function Set.new(i) + local result = {set={}} + setmetatable(result, Set) + if type(i) == "table" then + for k,v in pairs(i) do result[v] = true end + elseif i then + result[i] = true + end + return result +end +-- a = Set.new({"add","new","thing"}) + +--- Adds item(s) to set. +-- @param a set Set to manipulate. +-- @param i not nil Values(s) to add. +-- @returns bool Success of operation. +function Set.add(a,i) + if type(i) == "table" then + for k,v in pairs(i) do a[v] = true end + else + a[i] = true + end + return true +end + +--- Deletes item(s) from set. +-- @param a set Set to manipulate. +-- @param i not nil Values(s) to delete. +-- @returns bool Success of operation. +function Set.delete(a,i) + if type(i) == "table" then + for k,v in pairs(i) do a[v] = nil end + else + a[i] = nil + end + return true +end + +--- Checks if value(s) are present in set. +-- @param a set Set to inspect. +-- @param i not nil Values(s) to check. +-- @returns bool Value(s) are present? +function Set.has(a,i) + if type(i) == "table" then + for k,v in pairs(i) do + if not a[k] then return false end + end + return true + else + return a[i] ~= nil + end +end + + + +-- ██████ ██████ ███████ ██████ █████ ████████ ██████ ██████ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ ██ ██████ █████ ██████ ███████ ██ ██ ██ ██████ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██████ ██ ███████ ██ ██ ██ ██ ██ ██████ ██ ██ +-- +-- ██████ ██ ██ ███████ ██████ ██████ ██ ██████ ███████ ███████ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ ██ ██ ██ █████ ██████ ██████ ██ ██ ██ █████ ███████ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██████ ████ ███████ ██ ██ ██ ██ ██ ██████ ███████ ███████ + +function Set.__call(i) return Set.new(i) end + + +-- Main Return: +return Set diff --git a/worldeditadditions/utils/strings/init.lua b/worldeditadditions/utils/strings/init.lua index 32a8454..d5b76c9 100644 --- a/worldeditadditions/utils/strings/init.lua +++ b/worldeditadditions/utils/strings/init.lua @@ -4,3 +4,4 @@ dofile(wea.modpath.."/utils/strings/split.lua") dofile(wea.modpath.."/utils/strings/polyfill.lua") dofile(wea.modpath.."/utils/strings/tochars.lua") wea.split_shell = dofile(wea.modpath.."/utils/strings/split_shell.lua") +wea.to_boolean = dofile(wea.modpath.."/utils/strings/to_boolean.lua") diff --git a/worldeditadditions/utils/strings/split.lua b/worldeditadditions/utils/strings/split.lua index 69e5518..738b56f 100644 --- a/worldeditadditions/utils/strings/split.lua +++ b/worldeditadditions/utils/strings/split.lua @@ -42,16 +42,46 @@ function worldeditadditions.gsplit(text, pattern, plain) end --- Split a string into substrings separated by a pattern. +--- Split a string into substrings separated by a pattern. -- Deprecated -- @param text string The string to iterate over -- @param pattern string The separator pattern -- @param plain boolean If true (or truthy), pattern is interpreted as a -- plain string, not a Lua pattern -- @returns table A sequence table containing the substrings -function worldeditadditions.split(text, pattern, plain) +function worldeditadditions.dsplit(text, pattern, plain) local ret = {} for match in worldeditadditions.gsplit(text, pattern, plain) do table.insert(ret, match) end return ret end + +--- Split a string into substrings separated by a pattern. +-- @param str string The string to iterate over +-- @param dlm string The delimiter (separator) pattern +-- @param plain boolean If true (or truthy), pattern is interpreted as a +-- plain string, not a Lua pattern +-- @returns table A sequence table containing the substrings +function worldeditadditions.split (str,dlm,plain) + local pos, ret = 0, {} + local ins, i = str:find(dlm,pos,plain) + -- "if plain" shaves off some time in the while statement + if plain then + while ins do + table.insert(ret,str:sub(pos,ins - 1)) + pos = ins + #dlm + ins = str:find(dlm,pos,true) + end + else + while ins do + table.insert(ret,str:sub(pos,ins - 1)) + pos = i + 1 + ins, i = str:find(dlm,pos) + end + end + -- print(pos..","..#str) + if str:sub(pos,#str) ~= "" then + table.insert(ret,str:sub(pos,#str)) + end + return ret +end diff --git a/worldeditadditions/utils/strings/to_boolean.lua b/worldeditadditions/utils/strings/to_boolean.lua new file mode 100644 index 0000000..a128226 --- /dev/null +++ b/worldeditadditions/utils/strings/to_boolean.lua @@ -0,0 +1,12 @@ +--- Converts input to a value of type Boolean. +-- @param arg any Input to convert +-- @returns boolean +local function to_boolean(arg) + local typ = type(arg) + if typ == "boolean" then return arg + elseif typ == "number" and arg > 0 then return true + elseif arg == "false" or arg == "no" then return false + elseif typ ~= "nil" then return true + else return false end +end +return to_boolean diff --git a/worldeditadditions/utils/tables/table_tostring.lua b/worldeditadditions/utils/tables/table_tostring.lua index 4a13586..da6778e 100644 --- a/worldeditadditions/utils/tables/table_tostring.lua +++ b/worldeditadditions/utils/tables/table_tostring.lua @@ -2,16 +2,27 @@ -- @param tbl table input table -- @param sep string key value seperator -- @param new_line string key value pair delimiter +-- @param max_depth number max recursion depth (optional) -- @return string concatenated table pairs -local function table_tostring(tbl, sep, new_line) +local function table_tostring(tbl, sep, new_line, max_depth) if type(sep) ~= "string" then sep = ": " end if type(new_line) ~= "string" then new_line = ", " end + if type(max_depth) == "number" then max_depth = {depth=0,max=max_depth} + elseif type(max_depth) ~= "table" then max_depth = {depth=0,max=5} end local ret = {} if type(tbl) ~= "table" then return "Error: input not table!" end for key,value in pairs(tbl) do - table.insert(ret,tostring(key) .. sep .. tostring(value) .. new_line) + if type(value) == "table" and max_depth.depth < max_depth.max then + table.insert(ret,tostring(key) .. sep .. + "{" .. table_tostring(value,sep,new_line,{max_depth.depth+1,max_depth.max}) .. "}") + else + table.insert(ret,tostring(key) .. sep .. tostring(value)) + end end - return table.concat(ret,"") + return table.concat(ret,new_line) end +-- Test: +-- /lua v1 = { x= 0.335, facing= { axis= "z", sign= -1 } }; print(worldeditadditions.table.tostring(v1)) + return table_tostring diff --git a/worldeditadditions/utils/vector3.lua b/worldeditadditions/utils/vector3.lua index b860079..c1d1f85 100644 --- a/worldeditadditions/utils/vector3.lua +++ b/worldeditadditions/utils/vector3.lua @@ -66,11 +66,11 @@ function Vector3.sub(a, b) return Vector3.subtract(a, b) end -- Returns the result as a new vector. -- If 1 of the inputs is a number and the other a vector, then the number will -- be multiplied to each of the components of the vector. --- +-- -- If both of the inputs are vectors, then the components are multiplied -- by each other (NOT the cross product). In other words: -- a.x * b.x, a.y * b.y, a.z * b.z --- +-- -- @param a Vector3|number The first item to multiply. -- @param a Vector3|number The second item to multiply. -- @returns Vector3 The result as a new Vector3 object. @@ -263,7 +263,7 @@ end -- This enables convenient ingesting of positions from outside. -- @param pos1 Vector3 The first vector to operate on. -- @param pos2 Vector3 The second vector to operate on. --- @returns Vector3,Vector3 The 2 sorted vectors. +-- @returns Vector3,Vector3 The 2 sorted vectors (min, max). function Vector3.sort(pos1, pos2) local pos1_new = Vector3.clone(pos1) -- This way we can accept non-Vector3 instances local pos2_new = Vector3.clone(pos2) -- This way we can accept non-Vector3 instances @@ -384,7 +384,7 @@ end -- ██ ██ ██████ █████ ██████ ███████ ██ ██ ██ ██████ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██████ ██ ███████ ██ ██ ██ ██ ██ ██████ ██ ██ --- +-- -- ██████ ██ ██ ███████ ██████ ██████ ██ ██████ ███████ ███████ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ █████ ██████ ██████ ██ ██ ██ █████ ███████ diff --git a/worldeditadditions_commands/commands/extra/basename.lua b/worldeditadditions_commands/commands/extra/basename.lua index 39d92b5..6d247bb 100644 --- a/worldeditadditions_commands/commands/extra/basename.lua +++ b/worldeditadditions_commands/commands/extra/basename.lua @@ -15,6 +15,6 @@ worldedit.register_command("basename", { end, func = function(name, params_text) if name == nil then return end - worldedit.player_notify(name, worldedit.normalize_nodename(params_text) or 'Error 404: "'..params_text..'" not found!') + return true, worldedit.normalize_nodename(params_text) or 'Error 404: "'..params_text..'" not found!' end }) diff --git a/worldeditadditions_commands/commands/meta/for.lua b/worldeditadditions_commands/commands/meta/for.lua new file mode 100644 index 0000000..7db3029 --- /dev/null +++ b/worldeditadditions_commands/commands/meta/for.lua @@ -0,0 +1,88 @@ +-- ███████ ██████ ██████ +-- ██ ██ ██ ██ ██ +-- █████ ██ ██ ██████ +-- ██ ██ ██ ██ ██ +-- ██ ██████ ██ ██ +-- Process: +-- 1: Split `params_text` into two vars with unpack(wea.split(params_text,"%sdo%s")) +-- 2: Further split the two vars into two tables, one of values and the other of {command, args} sub tables +-- 3: For each entry in the values table execute each {command, args} sub table using gsub to replace "%%" in the args with the current value + +-- Specs: +-- Command cluster support using () +-- ?Basename support for values +-- ?Comma deliniation support for values + +local wea = worldeditadditions +local function step(params) + -- Initialize additional params on first call + if not params.first then + params.i = 1 -- Iteration number + params.time = 0 -- Total execution time + params.first = true + end + + -- Load current value to use + local v = params.values[params.i] + + -- Start a timer + local start_time = wea.get_ms_time() + -- Execute command + params.cmd.func(params.player_name, params.args:gsub("%%+",v)) + -- Finish timer and add to total + params.time = params.time + wea.get_ms_time() - start_time + -- Increment iteration state + params.i = params.i + 1 + + if params.i <= #params.values then + -- If we haven't run out of values call function again + minetest.after(0, step, params) + else + worldedit.player_notify(params.player_name, "For ".. + table.concat(params.values,", ").. + ", /"..params.cmd_name.." completed in " .. + wea.format.human_time(params.time)) + end +end + +worldedit.register_command("for", { + params = " ... do // %% ", + description = "Executes a chat command for each value before \" do \" replacing any instances of \"%%\" with those values. The forward slashes at the beginning of the chat command must be the same as if you were executing it normally.", + privs = { worldedit = true }, + parse = function(params_text) + if not params_text:match("%sdo%s") then + return false, "Error: \"do\" argument is not present." + end + local parts = wea.split(params_text,"%sdo%s") + if not parts[1] == "" then + return false, "Error: No values specified." + end + if not parts[2] then + return false, "Error: No command specified." + end + local values = wea.split(parts[1],"%s") + local command, args = parts[2]:match("/([^%s]+)%s*(.*)$") + if not args then args = "" + else args = wea.trim(args) end + + return true, values, command, args + end, + func = function(name, values, command, args) + local cmd = minetest.chatcommands[command] + if not cmd then + return false, "Error: "..command.." isn't a valid command." + end + if not minetest.check_player_privs(name, cmd.privs) then + return false, "Your privileges are insufficient to run /\""..command.."\"." + end + + step({ + player_name = name, + cmd_name = command, + values = values, + cmd = cmd, + args = args + }) + + end +}) diff --git a/worldeditadditions_commands/commands/meta/init.lua b/worldeditadditions_commands/commands/meta/init.lua new file mode 100644 index 0000000..7f0d43c --- /dev/null +++ b/worldeditadditions_commands/commands/meta/init.lua @@ -0,0 +1,18 @@ +-- ███ ███ ███████ ████████ █████ +-- ████ ████ ██ ██ ██ ██ +-- ██ ████ ██ █████ ██ ███████ +-- ██ ██ ██ ██ ██ ██ ██ +-- ██ ██ ███████ ██ ██ ██ + +-- Commands that work on other commands. + +local we_cm = worldeditadditions_commands.modpath .. "/commands/meta/" + +dofile(we_cm.."airapply.lua") +dofile(we_cm.."ellipsoidapply.lua") +dofile(we_cm.."for.lua") +-- dofile(we_cm.."macro.lua") -- Async bug +dofile(we_cm.."many.lua") +dofile(we_cm.."multi.lua") +dofile(we_cm.."noiseapply2d.lua") +dofile(we_cm.."subdivide.lua") diff --git a/worldeditadditions_commands/commands/meta/macro.lua b/worldeditadditions_commands/commands/meta/macro.lua new file mode 100644 index 0000000..9bd8141 --- /dev/null +++ b/worldeditadditions_commands/commands/meta/macro.lua @@ -0,0 +1,134 @@ +-- ███ ███ █████ ██████ ██████ ██████ +-- ████ ████ ██ ██ ██ ██ ██ ██ ██ +-- ██ ████ ██ ███████ ██ ██████ ██ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ ██ ██ ██ ██████ ██ ██ ██████ +local wea = worldeditadditions +local v3 = worldeditadditions.Vector3 +local function step(params) + -- Initialize additional params on first call + if not params.first then + params.i = 1 -- Iteration number + params.time = 0 -- Total execution time + params.first = true + end + + -- Load current command string to use + local command, args = params.commands[params.i]:match("/([^%s]+)%s*(.*)$") + if not args then args = "" + else args = args:match("^%s*(.*)%s*$") end + -- Get command and test privs + local cmd = minetest.chatcommands[command] + if not cmd then + return false, "Error: "..command.." isn't a valid command." + end + if not minetest.check_player_privs(params.player_name, cmd.privs) then + return false, "Your privileges are insufficient to run /\""..command.."\"." + end + + -- Start a timer + local start_time = wea.get_ms_time() + -- Execute command + cmd.func(params.player_name, args) + -- Finish timer and add to total + params.time = params.time + wea.get_ms_time() - start_time + -- Increment iteration state + params.i = params.i + 1 + + if params.i <= #params.commands then + -- If we haven't run out of values call function again + minetest.after(params.delay, step, params) -- Time is in seconds + else + worldedit.player_notify(params.player_name, "The macro \"".. + params.file.."\" was completed in " .. + wea.format.human_time(params.time)) + end +end + +worldedit.register_command("macro", { + params = " []", + description = "Load commands from \"(world folder)/macros/[.weamac | .wmac]\" with position 1 of the current WorldEdit region as the origin.", + privs = {worldedit=true}, + require_pos = 0, + parse = function(params_text) + local parts = wea.split(params_text,"%s") + local file_name, delay -- = params_text:match("^(.-)%s*(%d*%.?%d*)$") + -- Check for params and delay + if not parts[1] then + return false, "Error: Insufficient arguments. Expected: \" []\"" + elseif not parts[#parts]:match("[^%d%.]") then + delay = table.remove(parts,#parts) + file_name = table.concat(parts," ") + else + delay = 0 + file_name = table.concat(parts," ") + end + -- Check file name + if file_name:match("[!\"#%%&'%(%)%*%+,/:;<=>%?%[\\]%^`{|}]") then + return false, "Disallowed file name: " .. params_text + end + + return true, file_name, delay + end, + func = function(name, file_name, delay) + if not worldedit.pos1[name] then + worldedit.pos1[name] = v3.add(wea.player_vector(name), v3.new(0.5,-0.5,0.5)):floor() + worldedit.mark_pos1(name) + end + worldedit.pos2[name] = worldedit.pos1[name] + + -- Find the file in the world path + local testpaths = { + minetest.get_worldpath() .. "/macros/" .. file_name, + minetest.get_worldpath() .. "/macros/" .. file_name .. ".weamac", + minetest.get_worldpath() .. "/macros/" .. file_name .. ".wmac", + } + local file, err + for index, path in ipairs(testpaths) do + file, err = io.open(path, "rb") + if not err then break end + end + -- Check if file exists + if err then + return false, "Error: File \"" .. file_name .. "\" does not exist or is corrupt." + end + local value = file:read("*a") + file:close() + + step({ + player_name = name, + file = file_name:match("^[^%.]+"), + delay = delay, + commands = wea.split(value,"[\n\r]+") + }) + + end, +}) + +-- Make default macro +local function default_macro() + local path = minetest.get_worldpath() .. "/macros" + -- Create directory if it does not already exist + minetest.mkdir(path) + local writer, err = io.open(path.."/fixlight.weamac", "ab") + if not writer then return false end + writer:write("//multi //1 //2 //outset 50 //fixlight //y") + writer:flush() + writer:close() + return true +end + +-- Check for default macro +local function chk_default_macro() + local path = minetest.get_worldpath() .. "/macros/fixlight.weamac" + local file, err = io.open(path, "rb") + if err then return false + else + file:close() + return true + end +end + +if not chk_default_macro() then + default_macro() +end diff --git a/worldeditadditions_commands/commands/selectors/init.lua b/worldeditadditions_commands/commands/selectors/init.lua index 8e9ac32..fd60556 100644 --- a/worldeditadditions_commands/commands/selectors/init.lua +++ b/worldeditadditions_commands/commands/selectors/init.lua @@ -18,4 +18,8 @@ dofile(we_cm.."smake.lua") dofile(we_cm.."spop.lua") dofile(we_cm.."spush.lua") dofile(we_cm.."srect.lua") +dofile(we_cm.."sshift.lua") dofile(we_cm.."sstack.lua") + +-- Aliases +worldedit.alias_command("sfac", "sfactor") diff --git a/worldeditadditions_commands/commands/selectors/srel.lua b/worldeditadditions_commands/commands/selectors/srel.lua index cc4b3ce..4377d38 100644 --- a/worldeditadditions_commands/commands/selectors/srel.lua +++ b/worldeditadditions_commands/commands/selectors/srel.lua @@ -36,7 +36,7 @@ worldedit.register_command("srel", { if not _ then return false, vec end if not worldedit.pos1[name] then - local pos = vector.add(minetest.get_player_by_name(name):get_pos(), vector.new(0.5,-0.5,0.5)) + local pos = vector.add(wea.player_vector(name), vector.new(0.5,-0.5,0.5)) wea.vector.floor(pos) worldedit.pos1[name] = pos worldedit.mark_pos1(name) diff --git a/worldeditadditions_commands/commands/selectors/sshift.lua b/worldeditadditions_commands/commands/selectors/sshift.lua new file mode 100644 index 0000000..8414145 --- /dev/null +++ b/worldeditadditions_commands/commands/selectors/sshift.lua @@ -0,0 +1,51 @@ +-- ███████ ███████ ██ ██ ██ ███████ ████████ +-- ██ ██ ██ ██ ██ ██ ██ +-- ███████ ███████ ███████ ██ █████ ██ +-- ██ ██ ██ ██ ██ ██ ██ +-- ███████ ███████ ██ ██ ██ ██ ██ +local wea = worldeditadditions +local v3 = worldeditadditions.Vector3 +local function parse_with_name(name,args) + local vec, tmp = v3.new(0, 0, 0), {} + local find, _, i = {}, 0, 0 + repeat + _, i, tmp.proc = args:find("([%l%s+-]+%d+)%s*", i) + if tmp.proc:match("[xyz]") then + tmp.ax = tmp.proc:match("[xyz]") + tmp.dir = tonumber(tmp.proc:match("[+-]?%d+")) * (tmp.proc:match("-%l+") and -1 or 1) + else + tmp.ax, _ = wea.dir_to_xyz(name, tmp.proc:match("%l+")) + if not tmp.ax then return false, _ end + tmp.dir = tonumber(tmp.proc:match("[+-]?%d+")) * (tmp.proc:match("-%l+") and -1 or 1) * _ + end + vec[tmp.ax] = tmp.dir + until not args:find("([%l%s+-]+%d+)%s*", i) + return true, vec +end +worldedit.register_command("sshift", { + params = " [ [ ]]", + description = "Shift the WorldEdit region in 3 dimensions.", + privs = { worldedit = true }, + require_pos = 2, + parse = function(params_text) + if params_text:match("([%l%s+-]+%d+)") then return true, params_text + else return false, "No acceptable params found" end + end, + func = function(name, params_text) + local _, vec = parse_with_name(name,params_text) + if not _ then return false, vec end + + local pos1 = vec:add(worldedit.pos1[name]) + worldedit.pos1[name] = pos1 + worldedit.mark_pos1(name) + + local pos2 = vec:add(worldedit.pos2[name]) + worldedit.pos2[name] = pos2 + worldedit.mark_pos2(name) + + return true, "Region shifted by " .. (vec.x + vec.y + vec.z) .. " nodes." + end, +}) + +-- Tests +-- //srel front 5 left 3 y 2 diff --git a/worldeditadditions_commands/commands/wireframe/init.lua b/worldeditadditions_commands/commands/wireframe/init.lua new file mode 100644 index 0000000..337a710 --- /dev/null +++ b/worldeditadditions_commands/commands/wireframe/init.lua @@ -0,0 +1,13 @@ +-- ██ ██ ██ ██████ ███████ ███████ ██████ █████ ███ ███ ███████ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ +-- ██ █ ██ ██ ██████ █████ █████ ██████ ███████ ██ ████ ██ █████ +-- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ███ ███ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ ███████ + +-- 2d and 3d outlines of shapes. + +local we_cm = worldeditadditions_commands.modpath .. "/commands/wireframe/" + +dofile(we_cm.."wbox.lua") +dofile(we_cm.."wcompass.lua") +dofile(we_cm.."wcorner.lua") diff --git a/worldeditadditions_commands/commands/wireframe/wbox.lua b/worldeditadditions_commands/commands/wireframe/wbox.lua new file mode 100644 index 0000000..c2bd9c4 --- /dev/null +++ b/worldeditadditions_commands/commands/wireframe/wbox.lua @@ -0,0 +1,39 @@ +-- ██ ██ ██████ ██████ ██ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ █ ██ ██████ ██ ██ ███ +-- ██ ███ ██ ██ ██ ██ ██ ██ ██ +-- ███ ███ ██████ ██████ ██ ██ +local wea = worldeditadditions +local v3 = worldeditadditions.Vector3 +worldedit.register_command("wbox", { + params = "", + description = "Sets the edges of the current selection to ", + privs = {worldedit=true}, + require_pos = 2, + parse = function(params_text) + if params_text == "" then + return false, "Error: too few arguments! Expected: \"\"" + end + local node = worldedit.normalize_nodename(params_text) + if not node then + return false, "invalid node name: " .. params_text + end + return true, node + end, + nodes_needed = function(name) + local delta = v3.subtract(worldedit.pos2[name], worldedit.pos1[name]):abs():add(1) + local total, mult, axes = 1, 4, {"x","y","z"} + for k,v in pairs(axes) do + if worldedit.pos1[name] ~= worldedit.pos2[name] then total = total*2 + else mult = mult/2 end + end + for k,v in pairs(axes) do + if delta[v] > 2 then total = total + (delta[v] - 2)*mult end + end + return total + end, + func = function(name, node) + local _, count = wea.wire_box(worldedit.pos1[name], worldedit.pos2[name], node) + return _, count .. " nodes set" + end, +}) diff --git a/worldeditadditions_commands/commands/wireframe/wcompass.lua b/worldeditadditions_commands/commands/wireframe/wcompass.lua new file mode 100644 index 0000000..ba012c8 --- /dev/null +++ b/worldeditadditions_commands/commands/wireframe/wcompass.lua @@ -0,0 +1,35 @@ +-- ██ ██ ██████ ██████ ███ ███ ██████ █████ ███████ ███████ +-- ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██ +-- ██ █ ██ ██ ██ ██ ██ ████ ██ ██████ ███████ ███████ ███████ +-- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ███ ███ ██████ ██████ ██ ██ ██ ██ ██ ███████ ███████ +local wea = worldeditadditions +worldedit.register_command("wcompass", { + params = " []", + description = "Creates a compass around pos1 with a single node bead pointing north (+Z).", + privs = {worldedit=true}, + require_pos = 1, + parse = function(params_text) + local parts = wea.split(params_text," ",true) + if not parts[1] then + return false, "Error: too few arguments! Expected: \" []\"" + elseif not parts[2] then + parts[2] = parts[1] + end + local node1 = worldedit.normalize_nodename(parts[1]) + local node2 = worldedit.normalize_nodename(parts[2]) + if not node1 then + return false, "Invalid : " .. parts[1] + elseif not node2 then + return false, "Invalid : " .. parts[2] + end + return true, node1, node2 + end, + nodes_needed = function(name) + return 26 + end, + func = function(name, node1, node2) + local _, count = wea.make_compass(worldedit.pos1[name], node1, node2) + return _, count .. " nodes set" + end, +}) diff --git a/worldeditadditions_commands/commands/wireframe/wcorner.lua b/worldeditadditions_commands/commands/wireframe/wcorner.lua new file mode 100644 index 0000000..af08d6f --- /dev/null +++ b/worldeditadditions_commands/commands/wireframe/wcorner.lua @@ -0,0 +1,30 @@ +-- ██ ██ ██████ ██████ ██████ ███ ██ ███████ ██████ +-- ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ +-- ██ █ ██ ██ ██ ██ ██████ ██ ██ ██ █████ ██████ +-- ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ███ ███ ██████ ██████ ██ ██ ██ ████ ███████ ██ ██ +local wea = worldeditadditions +worldedit.register_command("wcorner", { + params = "", + description = "Set the corners of the current selection to ", + privs = {worldedit=true}, + require_pos = 2, + parse = function(params_text) + local node = worldedit.normalize_nodename(params_text) + if not node then + return false, "invalid node name: " .. params_text + end + return true, node + end, + nodes_needed = function(name) + local p1, p2, total = worldedit.pos1[name], worldedit.pos2[name], 1 + for k,v in pairs({"x","y","z"}) do + if p1[v] ~= p2[v] then total = total*2 end + end + return total + end, + func = function(name, node) + local _, count = wea.corner_set(worldedit.pos1[name], worldedit.pos2[name], node) + return _, count .. " nodes set" + end, +}) diff --git a/worldeditadditions_commands/init.lua b/worldeditadditions_commands/init.lua index 7a41d7f..c639dd2 100644 --- a/worldeditadditions_commands/init.lua +++ b/worldeditadditions_commands/init.lua @@ -39,12 +39,8 @@ dofile(we_c.modpath.."/commands/spiral2.lua") dofile(we_c.modpath.."/commands/count.lua") -dofile(we_c.modpath.."/commands/meta/multi.lua") -dofile(we_c.modpath.."/commands/meta/many.lua") -dofile(we_c.modpath.."/commands/meta/subdivide.lua") -dofile(we_c.modpath.."/commands/meta/ellipsoidapply.lua") -dofile(we_c.modpath.."/commands/meta/airapply.lua") -dofile(we_c.modpath.."/commands/meta/noiseapply2d.lua") +-- Meta Commands +dofile(we_c.modpath.."/commands/meta/init.lua") -- Selection Tools dofile(we_c.modpath.."/commands/selectors/init.lua") @@ -52,6 +48,9 @@ dofile(we_c.modpath.."/commands/selectors/init.lua") -- Measure Tools dofile(we_c.modpath.."/commands/measure/init.lua") +-- Wireframe +dofile(we_c.modpath.."/commands/wireframe/init.lua") + dofile(we_c.modpath.."/commands/extra/saplingaliases.lua") dofile(we_c.modpath.."/commands/extra/basename.lua") @@ -84,9 +83,6 @@ worldedit.alias_command("naturalize", "layers") worldedit.alias_command("flora", "bonemeal") --- Selection Tools -worldedit.alias_command("sfac", "sfactor") - -- Measure Tools worldedit.alias_command("mcount", "count") worldedit.alias_command("mfacing", "mface") diff --git a/worldeditadditions_core/register/check.lua b/worldeditadditions_core/register/check.lua index 8aeb684..5c08285 100644 --- a/worldeditadditions_core/register/check.lua +++ b/worldeditadditions_core/register/check.lua @@ -1,37 +1,26 @@ -function worldeditadditions_core.register_command(name, def) - -- TODO: Implement our own deep copy function here - -- Depending on a Minetest-specific addition here makes be very uneasy - -- ...especially since it's not obvious at first glance that this isn't a - -- core feature provided by Lua itself - local def = table.copy(def) - - assert( - def.privs, - "Error: No privileges specified in definition of command '"..name.."'." - ) - - def.require_pos = def.require_pos or 0 - - assert(def.require_pos >= 0 and def.require_pos < 3) - - if def.params == "" and not def.parse then - def.parse = function(params_text) return true end - else - assert( - def.parse, - "Error: No parameter parsing function specified, even though parameters were specified in definition of command '"..name.."'." - ) +function worldeditadditions_core.check_command(name, def) + if not (name and #name > 0) then + return false, "Error: No command name." end - - assert( - def.nodes_needed == nil or type(def.nodes_needed) == "function", - "Error: nodes_needed must be either not specified or be a function that returns the number of nodes that could potentially be changed for a given set fo parsed parameters in definition of command '"..name.."'" - ) - - assert( - def.func, - "Error: 'func' is not defined. It must be defined to the function to call to run the command in definition of command '"..name.."'." - ) - - return def + if not def.privs then + return false, "Error: privs is nill. Expected table." + end + def.require_pos = def.require_pos or 0 + if not (def.require_pos >= 0 and def.require_pos < 3) then + return false, "Error: require_pos must be greater than -1 and less than 3." + end + if not def.parse then + if def.params == "" then + def.parse = function(params_text) return true end + else + return false, "Error: parse function is invalid." + end + end + if not (def.nodes_needed == nil or type(def.nodes_needed) == "function") then + return false, "Error: nodes_needed must be nil or function." + end + if not def.func then + return false, "Error: main function is invalid." + end + return true end diff --git a/worldeditadditions_core/register/override.lua b/worldeditadditions_core/register/override.lua index 816ea9c..cc74d2a 100644 --- a/worldeditadditions_core/register/override.lua +++ b/worldeditadditions_core/register/override.lua @@ -1,9 +1,11 @@ local we_c = worldeditadditions_core function we_c.override_command(name, def) - local success, def = we_c.check(name, def) - + local def = table.copy(def) + local success, err = we_c.check_command(name, def) + if not success then - return false, def + error(err) + return false end minetest.override_chatcommand("/" .. name, { @@ -16,3 +18,18 @@ function we_c.override_command(name, def) }) worldedit.registered_commands[name] = def end + +function we_c.alias_override(alias, original) + if not worldedit.registered_commands[original] then + minetest.log("error", "worldedit_shortcommands: original " .. original .. " does not exist") + return + end + if minetest.chatcommands["/" .. alias] then + minetest.override_chatcommand("/" .. alias, minetest.chatcommands["/" .. original]) + worldedit.registered_commands[alias] = worldedit.registered_commands[original] + else + minetest.register_chatcommand("/" .. alias, minetest.chatcommands["/" .. original]) + worldedit.registered_commands[alias] = worldedit.registered_commands[original] + end + +end diff --git a/worldeditadditions_core/register/register.lua b/worldeditadditions_core/register/register.lua index 6efe21c..acd845d 100644 --- a/worldeditadditions_core/register/register.lua +++ b/worldeditadditions_core/register/register.lua @@ -1,9 +1,9 @@ local we_c = worldeditadditions_core function we_c.register_command(name, def) - local success, def = we_c.check(name, def) - + local def = table.copy(def) + local success, err = we_c.check_command(name, def) if not success then - return false, def + return false, err end minetest.register_chatcommand("/" .. name, { @@ -16,3 +16,17 @@ function we_c.register_command(name, def) }) worldedit.registered_commands[name] = def end + +function we_c.alias_command(alias, original) + if not worldedit.registered_commands[original] then + minetest.log("error", "worldedit_shortcommands: original " .. original .. " does not exist") + return + end + if minetest.chatcommands["/" .. alias] then + minetest.log("error", "worldedit_shortcommands: alias " .. alias .. " already exists") + return + end + + minetest.register_chatcommand("/" .. alias, minetest.chatcommands["/" .. original]) + worldedit.registered_commands[alias] = worldedit.registered_commands[original] +end