diff --git a/ChatCommands.md b/ChatCommands.md index 07bcfd6..be885c0 100644 --- a/ChatCommands.md +++ b/ChatCommands.md @@ -22,6 +22,9 @@ Many commands also have shorter names that can be typed faster. For example, if | `//hdo` | `//hollowdome` | | `//do` | `//dome` | | `//hcyl` | `//hollowcylinder` | +| `//cyl` | `//cylinder` | +| `//hpyr` | `//hollowpyramid` | +| `//pyr` | `//pyramid` | ### `//about` @@ -98,6 +101,12 @@ Display the volume of the current WorldEdit region. //volume +### `//deleteblocks` + +Delete the MapBlocks (16x16x16 units) that contain the selected region. This means that mapgen will be invoked for that area. As only whole MapBlocks get removed, the deleted area is usually larger than the selected one. Also, mapgen can trigger mechanisms like mud reflow or cavegen, which affects nodes (up to 112 nodes away) outside the MapBlock, so dont use this near buildings. Note that active entities are not part of a MapBlock and do not get deleted. + + //deleteblocks + ### `//set ` Set the current WorldEdit region to ``. @@ -184,6 +193,15 @@ Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length ` ` + +Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height ``, composed of ``. + + //hollowpyramid x 8 Diamond Block + //hollowpyramid y -5 glass + //hollowpyramid z 2 mesecons:wire_00000000_off + //hollowpyramid ? 12 mesecons:wire_00000000_off + ### `//pyramid x/y/z? ` Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height ``, composed of ``. @@ -285,6 +303,12 @@ Fixes the lighting in the current WorldEdit region. //fixlight +### `//drain` + +Removes any fluid node within the current WorldEdit region. + + //drain + ### `//hide` Hide all nodes in the current WorldEdit region non-destructively. @@ -374,3 +398,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/LICENSE.txt b/LICENSE.txt old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/Tutorial.md b/Tutorial.md old mode 100644 new mode 100755 diff --git a/WorldEdit API.md b/WorldEdit API.md index f50b506..5867dfc 100644 --- a/WorldEdit API.md +++ b/WorldEdit API.md @@ -45,6 +45,12 @@ Copies the region defined by positions `pos1` and `pos2` along the `axis` axis ( Returns the number of nodes copied. +### count = worldedit.copy2(pos1, pos2, off) + +Copies the region defined by positions `pos1` and `pos2` by the offset vector `off`. + +Returns the number of nodes copied. + ### count = worldedit.move(pos1, pos2, axis, amount) Moves the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes. @@ -127,9 +133,9 @@ Adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length ` Returns the number of nodes added. -### count = worldedit.pyramid(pos, axis, height, node_name) +### count = worldedit.pyramid(pos, axis, height, node_name, hollow) -Adds a pyramid centered at `pos` along the `axis` axis ("x" or "y" or "z") with height `height`. +Adds a pyramid centered at `pos` along the `axis` axis ("x" or "y" or "z") with height `height`, composed of `node_name`. Returns the number of nodes added. diff --git a/config.ld b/config.ld old mode 100644 new mode 100755 diff --git a/modpack.txt b/modpack.txt old mode 100644 new mode 100755 diff --git a/worldedit/code.lua b/worldedit/code.lua old mode 100644 new mode 100755 diff --git a/worldedit/common.lua b/worldedit/common.lua old mode 100644 new mode 100755 diff --git a/worldedit/compatibility.lua b/worldedit/compatibility.lua old mode 100644 new mode 100755 diff --git a/worldedit/cuboid.lua b/worldedit/cuboid.lua new file mode 100644 index 0000000..ce20761 --- /dev/null +++ b/worldedit/cuboid.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/init.lua b/worldedit/init.lua old mode 100644 new mode 100755 index 34c9820..0305c4d --- a/worldedit/init.lua +++ b/worldedit/init.lua @@ -23,7 +23,7 @@ end local path = minetest.get_modpath(minetest.get_current_modname()) local function load_module(path) - local file = io.open(path) + local file = io.open(path, "r") if not file then return end file:close() return dofile(path) @@ -36,6 +36,7 @@ load_module(path .. "/visualization.lua") load_module(path .. "/serialization.lua") load_module(path .. "/code.lua") load_module(path .. "/compatibility.lua") +load_module(path .. "/cuboid.lua") if minetest.setting_getbool("log_mods") then diff --git a/worldedit/manipulations.lua b/worldedit/manipulations.lua index e76cf70..57c7a76 100644 --- a/worldedit/manipulations.lua +++ b/worldedit/manipulations.lua @@ -90,7 +90,7 @@ function worldedit.stack2(pos1, pos2, direction, amount, finished) translated.x = translated.x + direction.x translated.y = translated.y + direction.y translated.z = translated.z + direction.z - worldedit.copy2(pos1, pos2, translated, volume) + worldedit.copy2(pos1, pos2, translated) minetest.after(0, next_one) else if finished then @@ -164,6 +164,38 @@ function worldedit.copy(pos1, pos2, axis, amount) return worldedit.volume(pos1, pos2) end +--- Copies a region by offset vector `off`. +-- @param pos1 +-- @param pos2 +-- @param off +-- @return The number of nodes copied. +function worldedit.copy2(pos1, pos2, off) + local pos1, pos2 = worldedit.sort_pos(pos1, pos2) + + worldedit.keep_loaded(pos1, pos2) + + local get_node, get_meta, set_node = minetest.get_node, + minetest.get_meta, minetest.set_node + local pos = {} + pos.x = pos2.x + 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) -- Obtain current node + local meta = get_meta(pos):to_table() -- Get meta of current node + local newpos = vector.add(pos, off) -- Calculate new position + set_node(newpos, node) -- Copy node to new position + get_meta(newpos):from_table(meta) -- Set metadata of new node + pos.z = pos.z - 1 + end + pos.y = pos.y - 1 + end + pos.x = pos.x - 1 + end + return worldedit.volume(pos1, pos2) +end --- Moves a region along `axis` by `amount` nodes. -- @return The number of nodes moved. @@ -503,8 +535,8 @@ function worldedit.orient(pos1, pos2, angle) worldedit.keep_loaded(pos1, pos2) local count = 0 - local get_node, get_meta, swap_node = minetest.get_node, - minetest.get_meta, minetest.swap_node + local set_node, get_node, get_meta, swap_node = minetest.set_node, + minetest.get_node, minetest.get_meta, minetest.swap_node local pos = {x=pos1.x, y=0, z=0} while pos.x <= pos2.x do pos.y = pos1.y @@ -543,14 +575,11 @@ end function worldedit.fixlight(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2) - worldedit.keep_loaded(pos1, pos2) + local vmanip = minetest.get_voxel_manip(pos1, pos2) + vmanip:write_to_map() + vmanip:update_map() -- this updates the lighting - local nodes = minetest.find_nodes_in_area(pos1, pos2, "air") - local dig_node = minetest.dig_node - for _, pos in ipairs(nodes) do - dig_node(pos) - end - return #nodes + return worldedit.volume(pos1, pos2) end diff --git a/worldedit/primitives.lua b/worldedit/primitives.lua index 6d3b026..fe22fff 100644 --- a/worldedit/primitives.lua +++ b/worldedit/primitives.lua @@ -150,14 +150,15 @@ end -- @param axis Axis ("x", "y", or "z") -- @param height Pyramid height. -- @param node_name Name of node to make pyramid of. +-- @param hollow Whether the pyramid should be hollow. -- @return The number of nodes added. -function worldedit.pyramid(pos, axis, height, node_name) +function worldedit.pyramid(pos, axis, height, node_name, hollow) local other1, other2 = worldedit.get_axis_others(axis) -- Set up voxel manipulator local manip, area = mh.init_axis_radius(pos, axis, height >= 0 and height or -height) - local data = mh.get_empty_data() + local data = mh.get_empty_data(area) -- Handle inverted pyramids local start_axis, end_axis, step @@ -177,7 +178,7 @@ function worldedit.pyramid(pos, axis, height, node_name) y = pos.y - area.MinEdge.y, z = pos.z - area.MinEdge.z, } - local size = height * step + local size = math.abs(height * step) local count = 0 -- For each level of the pyramid for index1 = 0, height, step do @@ -187,10 +188,12 @@ function worldedit.pyramid(pos, axis, height, node_name) local new_index2 = new_index1 + (index2 + offset[other1]) * stride[other1] for index3 = -size, size do local i = new_index2 + (index3 + offset[other2]) * stride[other2] - data[i] = node_id + if (not hollow or size - math.abs(index2) < 2 or size - math.abs(index3) < 2) then + data[i] = node_id + count = count + 1 + end end end - count = count + (size * 2 + 1) ^ 2 size = size - 1 end diff --git a/worldedit/serialization.lua b/worldedit/serialization.lua index bec7e09..a0848e2 100644 --- a/worldedit/serialization.lua +++ b/worldedit/serialization.lua @@ -144,9 +144,9 @@ local function load_schematic(value) "([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do param1, param2 = tonumber(param1), tonumber(param2) table.insert(nodes, { - x = originx + tonumber(x), - y = originy + tonumber(y), - z = originz + tonumber(z), + x = tonumber(x), + y = tonumber(y), + z = tonumber(z), name = name, param1 = param1 ~= 0 and param1 or nil, param2 = param2 ~= 0 and param2 or nil, @@ -159,20 +159,20 @@ local function load_schematic(value) else -- XXX: This is a filthy hack that works surprisingly well - in LuaJIT, `minetest.deserialize` will fail due to the register limit nodes = {} - value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1) -- remove the starting and ending values to leave only the node data - local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end) + content = content:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1) -- remove the starting and ending values to leave only the node data + local escaped = content:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end) local startpos, startpos1, endpos = 1, 1 while true do -- go through each individual node entry (except the last) startpos, endpos = escaped:find("},%s*{", startpos) if not startpos then break end - local current = value:sub(startpos1, startpos) + local current = content:sub(startpos1, startpos) local entry = minetest.deserialize("return " .. current) table.insert(nodes, entry) startpos, startpos1 = endpos, endpos end - local entry = minetest.deserialize("return " .. value:sub(startpos1)) -- process the last entry + local entry = minetest.deserialize("return " .. content:sub(startpos1)) -- process the last entry table.insert(nodes, entry) end else diff --git a/worldedit/textures/worldedit_wand.png b/worldedit/textures/worldedit_wand.png new file mode 100644 index 0000000..13eb121 Binary files /dev/null and b/worldedit/textures/worldedit_wand.png differ diff --git a/worldedit/visualization.lua b/worldedit/visualization.lua old mode 100644 new mode 100755 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..88f0260 --- /dev/null +++ b/worldedit_commands/cuboid.lua @@ -0,0 +1,240 @@ +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 == "x" or direction == "y" or direction == "z" then + axis, dir = direction, 1 + elseif direction == "?" then + axis, dir = worldedit.player_axis(name) + else + axis, dir = worldedit.translate_direction(name, direction) + 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) + return true, "Region expanded by " .. (amount + rev_amount) .. " nodes" + 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) + return true, "Region contracted by " .. (amount + rev_amount) .. " nodes" + end, + } +) diff --git a/worldedit_commands/depends.txt b/worldedit_commands/depends.txt old mode 100644 new mode 100755 diff --git a/worldedit_commands/init.lua b/worldedit_commands/init.lua index 424082f..a62efb6 100644 --- a/worldedit_commands/init.lua +++ b/worldedit_commands/init.lua @@ -10,10 +10,12 @@ 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") -dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua"); safe_region = safe_region or function(callback) return callback end +dofile(minetest.get_modpath("worldedit_commands") .. "/wand.lua") +local safe_region, check_region, reset_pending = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua") -local get_position = function(name) --position 1 retrieval function for when not using `safe_region` +local function get_position(name) --position 1 retrieval function for when not using `safe_region` local pos1 = worldedit.pos1[name] if pos1 == nil then worldedit.player_notify(name, "no position 1 selected") @@ -21,7 +23,7 @@ local get_position = function(name) --position 1 retrieval function for when not return pos1 end -local get_node = function(name, nodename) +local function get_node(name, nodename) local node = worldedit.normalize_nodename(nodename) if not node then worldedit.player_notify(name, "invalid node name: " .. nodename) @@ -30,7 +32,7 @@ local get_node = function(name, nodename) return node end -worldedit.player_notify = function(name, message) +function worldedit.player_notify(name, message) minetest.chat_send_player(name, "WorldEdit -!- " .. message, false) end @@ -56,8 +58,8 @@ worldedit.normalize_nodename = function(nodename) return nil end ---determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1) -worldedit.player_axis = function(name) +-- Determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1) +function worldedit.player_axis(name) local dir = minetest.get_player_by_name(name):get_look_dir() local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z) if x > y then @@ -70,6 +72,19 @@ worldedit.player_axis = function(name) return "z", dir.z > 0 and 1 or -1 end +local function mkdir(path) + if minetest.mkdir then + minetest.mkdir(path) + else + os.execute('mkdir "' .. path .. '"') + end +end + +local function check_filename(name) + return name:find("^[%w%s%^&'@{}%[%],%$=!%-#%(%)%%%.%+~_]+$") ~= nil +end + + minetest.register_chatcommand("/about", { params = "", description = "Get information about the mod", @@ -78,6 +93,56 @@ minetest.register_chatcommand("/about", { end, }) +-- mostly copied from builtin/chatcommands.lua with minor modifications +minetest.register_chatcommand("/help", { + privs = {}, + params = "[all/]", + description = "Get help for WorldEdit commands", + func = function(name, param) + local function is_we_command(cmd) + return cmd:sub(0, 1) == "/" + end + local function format_help_line(cmd, def) + local msg = minetest.colorize("#00ffff", "/"..cmd) + if def.params and def.params ~= "" then + msg = msg .. " " .. def.params + end + if def.description and def.description ~= "" then + msg = msg .. ": " .. def.description + end + return msg + end + + if not minetest.check_player_privs(name, "worldedit") then + return false, "You are not allowed to use any WorldEdit commands." + end + if param == "" then + local msg = "" + local cmds = {} + for cmd, def in pairs(minetest.chatcommands) do + if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then + cmds[#cmds + 1] = cmd:sub(2) -- strip the / + end + end + table.sort(cmds) + return true, "Available commands: " .. table.concat(cmds, " ") .. "\n" + .. "Use '//help ' to get more information," + .. " or '//help all' to list everything." + elseif param == "all" then + local cmds = {} + for cmd, def in pairs(minetest.chatcommands) do + if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then + cmds[#cmds + 1] = format_help_line(cmd, def) + end + end + table.sort(cmds) + return true, "Available commands:\n"..table.concat(cmds, "\n") + else + return minetest.chatcommands["help"].func(name, "/" .. param) + end + end, +}) + minetest.register_chatcommand("/inspect", { params = "on/off/1/0/true/false/yes/no/enable/disable/", description = "Enable or disable node inspection", @@ -97,16 +162,28 @@ minetest.register_chatcommand("/inspect", { end, }) +local function get_node_rlight(pos) + local vecs = { -- neighboring nodes + {x= 1, y= 0, z= 0}, + {x=-1, y= 0, z= 0}, + {x= 0, y= 1, z= 0}, + {x= 0, y=-1, z= 0}, + {x= 0, y= 0, z= 1}, + {x= 0, y= 0, z=-1}, + } + local ret = 0 + for _, v in ipairs(vecs) do + ret = math.max(ret, minetest.get_node_light(vector.add(pos, v))) + end + return ret +end + minetest.register_on_punchnode(function(pos, node, puncher) local name = puncher:get_player_name() if worldedit.inspect[name] then - if minetest.check_player_privs(name, {worldedit=true}) then - local axis, sign = worldedit.player_axis(name) - message = string.format("inspector: %s at %s (param1=%d, param2=%d) punched by %s facing the %s axis", - node.name, minetest.pos_to_string(pos), node.param1, node.param2, name, axis .. (sign > 0 and "+" or "-")) - else - message = "inspector: worldedit privileges required" - end + local axis, sign = worldedit.player_axis(name) + message = string.format("inspector: %s at %s (param1=%d, param2=%d, received light=%d) punched facing the %s axis", + node.name, minetest.pos_to_string(pos), node.param1, node.param2, get_node_rlight(pos), axis .. (sign > 0 and "+" or "-")) worldedit.player_notify(name, message) end end) @@ -121,6 +198,8 @@ minetest.register_chatcommand("/reset", { worldedit.mark_pos1(name) worldedit.mark_pos2(name) worldedit.set_pos[name] = nil + --make sure the user does not try to confirm an operation after resetting pos: + reset_pending(name) worldedit.player_notify(name, "region reset") end, }) @@ -277,6 +356,21 @@ minetest.register_chatcommand("/volume", { end, }) +minetest.register_chatcommand("/deleteblocks", { + params = "", + description = "remove all MapBlocks (16x16x16) containing the selected area from the map", + privs = {worldedit=true}, + func = safe_region(function(name, param) + local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] + local success = minetest.delete_area(pos1, pos2) + if success then + worldedit.player_notify(name, "Area deleted.") + else + worldedit.player_notify(name, "There was an error during deletion of the area.") + end + end), +}) + minetest.register_chatcommand("/set", { params = "", description = "Set the current WorldEdit region to ", @@ -488,6 +582,39 @@ minetest.register_chatcommand("/cylinder", { end, check_cylinder), }) +local check_pyramid = function(name, param) + if worldedit.pos1[name] == nil then + worldedit.player_notify(name, "no position 1 selected") + return nil + end + local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$") + if found == nil then + worldedit.player_notify(name, "invalid usage: " .. param) + return nil + end + local node = get_node(name, nodename) + if not node then return nil end + height = tonumber(height) + return math.ceil(((height * 2 + 1) ^ 2) * height / 3) +end + +minetest.register_chatcommand("/hollowpyramid", { + params = "x/y/z/? ", + description = "Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height , composed of ", + privs = {worldedit=true}, + func = safe_region(function(name, param) + local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$") + height = tonumber(height) + if axis == "?" then + axis, sign = worldedit.player_axis(name) + height = height * sign + end + local node = get_node(name, nodename) + local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node, true) + worldedit.player_notify(name, count .. " nodes added") + end, check_pyramid), +}) + minetest.register_chatcommand("/pyramid", { params = "x/y/z/? ", description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height , composed of ", @@ -502,22 +629,7 @@ minetest.register_chatcommand("/pyramid", { local node = get_node(name, nodename) local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node) worldedit.player_notify(name, count .. " nodes added") - end, - function(name, param) - if worldedit.pos1[name] == nil then - worldedit.player_notify(name, "no position 1 selected") - return nil - end - local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$") - if found == nil then - worldedit.player_notify(name, "invalid usage: " .. param) - return nil - end - local node = get_node(name, nodename) - if not node then return nil end - height = tonumber(height) - return math.ceil(((height * 2 + 1) ^ 2) * height / 3) - end), + end, check_pyramid), }) minetest.register_chatcommand("/spiral", { @@ -542,7 +654,7 @@ minetest.register_chatcommand("/spiral", { end local node = get_node(name, nodename) if not node then return nil end - return check_region(name, param) + return 1 -- TODO: return an useful value end), }) @@ -810,6 +922,30 @@ minetest.register_chatcommand("/fixlight", { end), }) +minetest.register_chatcommand("/drain", { + params = "", + description = "Remove any fluid node within the current WorldEdit region", + privs = {worldedit=true}, + func = safe_region(function(name, param) + -- TODO: make an API function for this + local count = 0 + local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name]) + for x = pos1.x, pos2.x do + for y = pos1.y, pos2.y do + for z = pos1.z, pos2.z do + local n = minetest.get_node({x=x, y=y, z=z}).name + local d = minetest.registered_nodes[n] + if d ~= nil and (d["drawtype"] == "liquid" or d["drawtype"] == "flowingliquid") then + minetest.remove_node({x=x, y=y, z=z}) + count = count + 1 + end + end + end + end + worldedit.player_notify(name, count .. " nodes updated") + end), +}) + minetest.register_chatcommand("/hide", { params = "", description = "Hide all nodes in the current WorldEdit region non-destructively", @@ -861,20 +997,21 @@ minetest.register_chatcommand("/save", { worldedit.player_notify(name, "invalid usage: " .. param) return end - if not string.find(param, "^[%w \t.,+-_=!@#$%%^&*()%[%]{};'\"]+$") then - worldedit.player_notify(name, "invalid file name: " .. param) + if not check_filename(param) then + worldedit.player_notify(name, "Disallowed file name: " .. param) return end - - local result, count = worldedit.serialize(worldedit.pos1[name], worldedit.pos2[name]) + local result, count = worldedit.serialize(worldedit.pos1[name], + worldedit.pos2[name]) local path = minetest.get_worldpath() .. "/schems" + -- Create directory if it does not already exist + mkdir(path) + local filename = path .. "/" .. param .. ".we" - filename = filename:gsub("\"", "\\\""):gsub("\\", "\\\\") --escape any nasty characters - os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist local file, err = io.open(filename, "wb") if err ~= nil then - worldedit.player_notify(name, "could not save file to \"" .. filename .. "\"") + worldedit.player_notify(name, "Could not save file to \"" .. filename .. "\"") return end file:write(result) @@ -897,8 +1034,8 @@ minetest.register_chatcommand("/allocate", { worldedit.player_notify(name, "invalid usage: " .. param) return end - if not string.find(param, "^[%w \t.,+-_=!@#$%%^&*()%[%]{};'\"]+$") then - worldedit.player_notify(name, "invalid file name: " .. param) + if not check_filename(param) then + worldedit.player_notify(name, "Disallowed file name: " .. param) return end @@ -986,11 +1123,6 @@ minetest.register_chatcommand("/lua", { description = "Executes as a Lua chunk in the global namespace", privs = {worldedit=true, server=true}, func = function(name, param) - local admin = minetest.setting_get("name") - if not admin or not name == admin then - worldedit.player_notify(name, "this command can only be run by the server administrator") - return - end local err = worldedit.lua(param) if err then worldedit.player_notify(name, "code error: " .. err) @@ -1005,12 +1137,6 @@ minetest.register_chatcommand("/luatransform", { description = "Executes as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region", privs = {worldedit=true, server=true}, func = safe_region(function(name, param) - local admin = minetest.setting_get("name") - if not admin or not name == admin then - worldedit.player_notify(name, "this command can only be run by the server administrator") - return - end - local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param) if err then worldedit.player_notify(name, "code error: " .. err, false) @@ -1022,24 +1148,31 @@ minetest.register_chatcommand("/luatransform", { minetest.register_chatcommand("/mtschemcreate", { params = "", - description = "Save the current WorldEdit region using the Minetest Schematic format to \"(world folder)/schems/.mts\"", + description = "Save the current WorldEdit region using the Minetest ".. + "Schematic format to \"(world folder)/schems/.mts\"", privs = {worldedit=true}, func = safe_region(function(name, param) if param == nil then worldedit.player_notify(name, "No filename specified") return end + if not check_filename(param) then + worldedit.player_notify(name, "Disallowed file name: " .. param) + return + end local path = minetest.get_worldpath() .. "/schems" - local filename = path .. "/" .. param .. ".mts" - filename = filename:gsub("\"", "\\\""):gsub("\\", "\\\\") --escape any nasty characters - os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist + -- Create directory if it does not already exist + mkdir(path) - local ret = minetest.create_schematic(worldedit.pos1[name], worldedit.pos2[name], worldedit.prob_list[name], filename) + local filename = path .. "/" .. param .. ".mts" + local ret = minetest.create_schematic(worldedit.pos1[name], + worldedit.pos2[name], worldedit.prob_list[name], + filename) if ret == nil then - worldedit.player_notify(name, "failed to create Minetest schematic", false) + worldedit.player_notify(name, "Failed to create Minetest schematic", false) else - worldedit.player_notify(name, "saved Minetest schematic to " .. param, false) + worldedit.player_notify(name, "Saved Minetest schematic to " .. param, false) end worldedit.prob_list[name] = {} end), @@ -1050,10 +1183,14 @@ minetest.register_chatcommand("/mtschemplace", { description = "Load nodes from \"(world folder)/schems/.mts\" with position 1 of the current WorldEdit region as the origin", privs = {worldedit=true}, func = function(name, param) - if param == nil then + if param == "" then worldedit.player_notify(name, "no filename specified") return end + if not check_filename(param) then + worldedit.player_notify(name, "Disallowed file name: " .. param) + return + end local pos = get_position(name) if pos == nil then return end @@ -1087,8 +1224,8 @@ minetest.register_chatcommand("/mtschemprob", { return end for k,v in pairs(problist) do - local prob = math.floor(((v["prob"] / 256) * 100) * 100 + 0.5) / 100 - text = text .. minetest.pos_to_string(v["pos"]) .. ": " .. prob .. "% | " + local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100 + text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | " end worldedit.player_notify(name, "currently set node probabilities:") worldedit.player_notify(name, text) @@ -1098,16 +1235,14 @@ minetest.register_chatcommand("/mtschemprob", { end, }) -minetest.register_on_player_receive_fields( - function(player, formname, fields) - if (formname == "prob_val_enter") and (fields.text ~= "") then - local name = player:get_player_name() - local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)} - local index = table.getn(worldedit.prob_list[name]) + 1 - worldedit.prob_list[name][index] = prob_entry - end +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then + local name = player:get_player_name() + local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)} + local index = table.getn(worldedit.prob_list[name]) + 1 + worldedit.prob_list[name][index] = prob_entry end -) +end) minetest.register_chatcommand("/clearobjects", { params = "", diff --git a/worldedit_commands/mark.lua b/worldedit_commands/mark.lua index 4062cae..9d41bda 100644 --- a/worldedit_commands/mark.lua +++ b/worldedit_commands/mark.lua @@ -58,8 +58,19 @@ worldedit.mark_region = function(name) end worldedit.marker_region[name] = nil end + if pos1 ~= nil and pos2 ~= nil then local pos1, pos2 = worldedit.sort_pos(pos1, pos2) + + local vec = vector.subtract(pos2, pos1) + local maxside = math.max(vec.x, math.max(vec.y, vec.z)) + local limit = tonumber(minetest.setting_get("active_object_send_range_blocks")) * 16 + if maxside > limit * 1.5 then + -- The client likely won't be able to see the plane markers as intended anyway, + -- thus don't place them and also don't load the area into memory + return + end + local thickness = 0.2 local sizex, sizey, sizez = (1 + pos2.x - pos1.x) / 2, (1 + pos2.y - pos1.y) / 2, (1 + pos2.z - pos1.z) / 2 @@ -72,24 +83,28 @@ worldedit.mark_region = function(name) --XY plane markers for _, z in ipairs({pos1.z - 0.5, pos2.z + 0.5}) do local marker = minetest.add_entity({x=pos1.x + sizex - 0.5, y=pos1.y + sizey - 0.5, z=z}, "worldedit:region_cube") - marker:set_properties({ - visual_size={x=sizex * 2, y=sizey * 2}, - collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness}, - }) - marker:get_luaentity().player_name = name - table.insert(markers, marker) + if marker ~= nil then + marker:set_properties({ + visual_size={x=sizex * 2, y=sizey * 2}, + collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness}, + }) + marker:get_luaentity().player_name = name + table.insert(markers, marker) + end end --YZ plane markers for _, x in ipairs({pos1.x - 0.5, pos2.x + 0.5}) do local marker = minetest.add_entity({x=x, y=pos1.y + sizey - 0.5, z=pos1.z + sizez - 0.5}, "worldedit:region_cube") - marker:set_properties({ - visual_size={x=sizez * 2, y=sizey * 2}, - collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez}, - }) - marker:setyaw(math.pi / 2) - marker:get_luaentity().player_name = name - table.insert(markers, marker) + if marker ~= nil then + marker:set_properties({ + visual_size={x=sizez * 2, y=sizey * 2}, + collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez}, + }) + marker:setyaw(math.pi / 2) + marker:get_luaentity().player_name = name + table.insert(markers, marker) + end end worldedit.marker_region[name] = markers @@ -153,7 +168,11 @@ minetest.register_entity(":worldedit:region_cube", { end end, on_punch = function(self, hitter) - for _, entity in ipairs(worldedit.marker_region[self.player_name]) do + local markers = worldedit.marker_region[self.player_name] + if not markers then + return + end + for _, entity in ipairs(markers) do entity:remove() end worldedit.marker_region[self.player_name] = nil diff --git a/worldedit_commands/safe.lua b/worldedit_commands/safe.lua index c6751c1..a93e393 100644 --- a/worldedit_commands/safe.lua +++ b/worldedit_commands/safe.lua @@ -1,7 +1,7 @@ local safe_region_callback = {} local safe_region_param = {} -check_region = function(name, param) +local function check_region(name, param) local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] --obtain positions if pos1 == nil or pos2 == nil then worldedit.player_notify(name, "no region selected") @@ -12,7 +12,7 @@ end --`callback` is a callback to run when the user confirms --`nodes_needed` is a function accepting `param`, `pos1`, and `pos2` to calculate the number of nodes needed -safe_region = function(callback, nodes_needed) +local function safe_region(callback, nodes_needed) --default node volume calculation nodes_needed = nodes_needed or check_region @@ -30,6 +30,10 @@ safe_region = function(callback, nodes_needed) end end +local function reset_pending(name) + safe_region_callback[name], safe_region_param[name] = nil, nil +end + minetest.register_chatcommand("/y", { params = "", description = "Confirm a pending operation", @@ -40,15 +44,8 @@ minetest.register_chatcommand("/y", { return end - --obtain positions - local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] - if pos1 == nil or pos2 == nil then - worldedit.player_notify(name, "no region selected") - return - end - safe_region_callback[name], safe_region_param[name] = nil, nil --reset pending operation - callback(name, param, pos1, pos2) + callback(name, param) end, }) @@ -63,3 +60,6 @@ minetest.register_chatcommand("/n", { safe_region_callback[name], safe_region_param[name] = nil, nil end, }) + + +return safe_region, check_region, reset_pending diff --git a/worldedit_commands/wand.lua b/worldedit_commands/wand.lua new file mode 100644 index 0000000..0780801 --- /dev/null +++ b/worldedit_commands/wand.lua @@ -0,0 +1,24 @@ +minetest.register_tool(":worldedit:wand", { + description = "WorldEdit Wand tool, Left-click to set 1st position, right-click to set 2nd", + inventory_image = "worldedit_wand.png", + stack_max = 1, -- there is no need to have more than one + liquids_pointable = true, -- ground with only water on can be selected as well + + on_use = function(itemstack, placer, pointed_thing) + if placer ~= nil and pointed_thing ~= nil and pointed_thing.type == "node" then + local name = placer:get_player_name() + worldedit.pos1[name] = pointed_thing.under + worldedit.mark_pos1(name) + end + return itemstack -- nothing consumed, nothing changed + end, + + on_place = function(itemstack, placer, pointed_thing) -- Left Click + if placer ~= nil and pointed_thing ~= nil and pointed_thing.type == "node" then + local name = placer:get_player_name() + worldedit.pos2[name] = pointed_thing.under + worldedit.mark_pos2(name) + end + return itemstack -- nothing consumed, nothing changed + end, +}) diff --git a/worldedit_gui/depends.txt b/worldedit_gui/depends.txt index d603ac9..2558dce 100644 --- a/worldedit_gui/depends.txt +++ b/worldedit_gui/depends.txt @@ -2,4 +2,5 @@ worldedit worldedit_commands unified_inventory? inventory_plus? -creative? \ No newline at end of file +sfinv? +creative? diff --git a/worldedit_gui/functionality.lua b/worldedit_gui/functionality.lua old mode 100644 new mode 100755 index 620f6c4..fda2256 --- a/worldedit_gui/functionality.lua +++ b/worldedit_gui/functionality.lua @@ -40,13 +40,13 @@ local angle_values = {90, 180, 270} setmetatable(angle_indices, {__index = function () return 1 end}) setmetatable(angle_values, {__index = function () return 90 end}) ---given multiple sets of privileges, produces a single set of privs that would have the same effect as requiring all of them at the same time +-- given multiple sets of privileges, produces a single set of privs that would have the same effect as requiring all of them at the same time local combine_privs = function(...) local result = {} for i, privs in ipairs({...}) do for name, value in pairs(privs) do if result[name] ~= nil and result[name] ~= value then --the priv must be both true and false, which can never happen - return {__fake_priv_that_nobody_has__=true} --priviledge table that can never be satisfied + return {__fake_priv_that_nobody_has__=true} --privilege table that can never be satisfied end result[name] = value end @@ -54,22 +54,44 @@ local combine_privs = function(...) return result end +-- display node (or unknown_node image otherwise) at specified pos in formspec +local formspec_node = function(pos, nodename) + return nodename and string.format("item_image[%s;1,1;%s]", pos, nodename) + or string.format("image[%s;1,1;worldedit_gui_unknown.png]", pos) +end + +-- two further priv helpers +local function we_privs(command) + return minetest.chatcommands["/" .. command].privs +end + +local function combine_we_privs(list) + local args = {} + for _, t in ipairs(list) do + table.insert(args, we_privs(t)) + end + return combine_privs(unpack(args)) +end + worldedit.register_gui_function("worldedit_gui_about", { - name = "About", privs = minetest.chatcommands["/about"].privs, + name = "About", + privs = {interact=true}, on_select = function(name) minetest.chatcommands["/about"].func(name, "") end, }) worldedit.register_gui_function("worldedit_gui_inspect", { - name = "Toggle Inspect", privs = minetest.chatcommands["/inspect"].privs, + name = "Toggle Inspect", + privs = we_privs("inspect"), on_select = function(name) minetest.chatcommands["/inspect"].func(name, worldedit.inspect[name] and "disable" or "enable") end, }) worldedit.register_gui_function("worldedit_gui_region", { - name = "Get/Set Region", privs = combine_privs(minetest.chatcommands["/p"].privs, minetest.chatcommands["/pos1"].privs, minetest.chatcommands["/pos2"].privs, minetest.chatcommands["/reset"].privs, minetest.chatcommands["/mark"].privs, minetest.chatcommands["/unmark"].privs, minetest.chatcommands["/volume"].privs, minetest.chatcommands["/fixedpos"].privs), + name = "Get/Set Region", + privs = combine_we_privs({"p", "pos1", "pos2", "reset", "mark", "unmark", "volume", "fixedpos"}), get_formspec = function(name) local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] return "size[9,7]" .. worldedit.get_formspec_header("worldedit_gui_region") .. @@ -148,15 +170,15 @@ worldedit.register_gui_handler("worldedit_gui_region", function(name, fields) end) worldedit.register_gui_function("worldedit_gui_set", { - name = "Set Nodes", privs = minetest.chatcommands["/set"].privs, + name = "Set Nodes", + privs = we_privs("set"), get_formspec = function(name) local node = gui_nodename1[name] local nodename = worldedit.normalize_nodename(node) return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_set") .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_set_node;Name;%s]", minetest.formspec_escape(node)) .. "button[4,1.18;1.5,0.8;worldedit_gui_set_search;Search]" .. - (nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) - or "image[5.5,1.1;1,1;unknown_node.png]") .. + formspec_node("5.5,1.1", nodename) .. "button_exit[0,2.5;3,0.8;worldedit_gui_set_submit;Set Nodes]" end, }) @@ -166,7 +188,10 @@ worldedit.register_gui_handler("worldedit_gui_set", function(name, fields) gui_nodename1[name] = tostring(fields.worldedit_gui_set_node) worldedit.show_page(name, "worldedit_gui_set") if fields.worldedit_gui_set_submit then - minetest.chatcommands["/set"].func(name, gui_nodename1[name]) + local n = worldedit.normalize_nodename(gui_nodename1[name]) + if n then + minetest.chatcommands["/set"].func(name, n) + end end return true end @@ -174,19 +199,18 @@ worldedit.register_gui_handler("worldedit_gui_set", function(name, fields) end) worldedit.register_gui_function("worldedit_gui_replace", { - name = "Replace Nodes", privs = combine_privs(minetest.chatcommands["/replace"].privs, minetest.chatcommands["/replaceinverse"].privs), + name = "Replace Nodes", + privs = combine_we_privs({"replace", "replaceinverse"}), get_formspec = function(name) local search, replace = gui_nodename1[name], gui_nodename2[name] local search_nodename, replace_nodename = worldedit.normalize_nodename(search), worldedit.normalize_nodename(replace) return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_replace") .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_replace_search;Name;%s]", minetest.formspec_escape(search)) .. "button[4,1.18;1.5,0.8;worldedit_gui_replace_search_search;Search]" .. - (search_nodename and string.format("item_image[5.5,1.1;1,1;%s]", search_nodename) - or "image[5.5,1.1;1,1;unknown_node.png]") .. + formspec_node("5.5,1.1", search_nodename) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_replace_replace;Name;%s]", minetest.formspec_escape(replace)) .. "button[4,2.18;1.5,0.8;worldedit_gui_replace_replace_search;Search]" .. - (replace_nodename and string.format("item_image[5.5,2.1;1,1;%s]", replace_nodename) - or "image[5.5,2.1;1,1;unknown_node.png]") .. + formspec_node("5.5,2.1", replace_nodename) .. "button_exit[0,3.5;3,0.8;worldedit_gui_replace_submit;Replace Nodes]" .. "button_exit[3.5,3.5;3,0.8;worldedit_gui_replace_submit_inverse;Replace Inverse]" end, @@ -198,10 +222,19 @@ worldedit.register_gui_handler("worldedit_gui_replace", function(name, fields) gui_nodename1[name] = tostring(fields.worldedit_gui_replace_search) gui_nodename2[name] = tostring(fields.worldedit_gui_replace_replace) worldedit.show_page(name, "worldedit_gui_replace") + + local submit = nil if fields.worldedit_gui_replace_submit then - minetest.chatcommands["/replace"].func(name, string.format("%s %s", gui_nodename1[name], gui_nodename2[name])) + submit = "replace" elseif fields.worldedit_gui_replace_submit_inverse then - minetest.chatcommands["/replaceinverse"].func(name, string.format("%s %s", gui_nodename1[name], gui_nodename2[name])) + submit = "replaceinverse" + end + if submit then + local n1 = worldedit.normalize_nodename(gui_nodename1[name]) + local n2 = worldedit.normalize_nodename(gui_nodename2[name]) + if n1 and n2 then + minetest.chatcommands["/"..submit].func(name, string.format("%s %s", n1, n2)) + end end return true end @@ -209,15 +242,15 @@ worldedit.register_gui_handler("worldedit_gui_replace", function(name, fields) end) worldedit.register_gui_function("worldedit_gui_sphere_dome", { - name = "Sphere/Dome", privs = combine_privs(minetest.chatcommands["/hollowsphere"].privs, minetest.chatcommands["/sphere"].privs, minetest.chatcommands["/hollowdome"].privs, minetest.chatcommands["/dome"].privs), + name = "Sphere/Dome", + privs = combine_we_privs({"hollowsphere", "sphere", "hollowdome", "dome"}), get_formspec = function(name) local node, radius = gui_nodename1[name], gui_distance2[name] local nodename = worldedit.normalize_nodename(node) return "size[6.5,5]" .. worldedit.get_formspec_header("worldedit_gui_sphere_dome") .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_sphere_dome_node;Name;%s]", minetest.formspec_escape(node)) .. "button[4,1.18;1.5,0.8;worldedit_gui_sphere_dome_search;Search]" .. - (nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) - or "image[5.5,1.1;1,1;unknown_node.png]") .. + formspec_node("5.5,1.1", nodename) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_sphere_dome_radius;Radius;%s]", minetest.formspec_escape(radius)) .. "button_exit[0,3.5;3,0.8;worldedit_gui_sphere_dome_submit_hollow;Hollow Sphere]" .. "button_exit[3.5,3.5;3,0.8;worldedit_gui_sphere_dome_submit_solid;Solid Sphere]" .. @@ -233,14 +266,22 @@ worldedit.register_gui_handler("worldedit_gui_sphere_dome", function(name, field gui_nodename1[name] = tostring(fields.worldedit_gui_sphere_dome_node) gui_distance2[name] = tostring(fields.worldedit_gui_sphere_dome_radius) worldedit.show_page(name, "worldedit_gui_sphere_dome") + + local submit = nil if fields.worldedit_gui_sphere_dome_submit_hollow then - minetest.chatcommands["/hollowsphere"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name])) + submit = "hollowsphere" elseif fields.worldedit_gui_sphere_dome_submit_solid then - minetest.chatcommands["/sphere"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name])) + submit = "sphere" elseif fields.worldedit_gui_sphere_dome_submit_hollow_dome then - minetest.chatcommands["/hollowdome"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name])) + submit = "hollowdome" elseif fields.worldedit_gui_sphere_dome_submit_solid_dome then - minetest.chatcommands["/dome"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name])) + submit = "dome" + end + if submit then + local n = worldedit.normalize_nodename(gui_nodename1[name]) + if n then + minetest.chatcommands["/"..submit].func(name, string.format("%s %s", gui_distance2[name], n)) + end end return true end @@ -248,15 +289,15 @@ worldedit.register_gui_handler("worldedit_gui_sphere_dome", function(name, field end) worldedit.register_gui_function("worldedit_gui_cylinder", { - name = "Cylinder", privs = combine_privs(minetest.chatcommands["/hollowcylinder"].privs, minetest.chatcommands["/cylinder"].privs), + name = "Cylinder", + privs = combine_we_privs({"hollowcylinder", "cylinder"}), get_formspec = function(name) local node, axis, length, radius = gui_nodename1[name], gui_axis1[name], gui_distance1[name], gui_distance2[name] local nodename = worldedit.normalize_nodename(node) return "size[6.5,5]" .. worldedit.get_formspec_header("worldedit_gui_cylinder") .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_cylinder_node;Name;%s]", minetest.formspec_escape(node)) .. "button[4,1.18;1.5,0.8;worldedit_gui_cylinder_search;Search]" .. - (nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) - or "image[5.5,1.1;1,1;unknown_node.png]") .. + formspec_node("5.5,1.1", nodename) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_cylinder_length;Length;%s]", minetest.formspec_escape(length)) .. string.format("dropdown[4,2.18;2.5;worldedit_gui_cylinder_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) .. string.format("field[0.5,3.5;4,0.8;worldedit_gui_cylinder_radius;Radius;%s]", minetest.formspec_escape(radius)) .. @@ -273,56 +314,85 @@ worldedit.register_gui_handler("worldedit_gui_cylinder", function(name, fields) gui_distance1[name] = tostring(fields.worldedit_gui_cylinder_length) gui_distance2[name] = tostring(fields.worldedit_gui_cylinder_radius) worldedit.show_page(name, "worldedit_gui_cylinder") + + local submit = nil if fields.worldedit_gui_cylinder_submit_hollow then - minetest.chatcommands["/hollowcylinder"].func(name, string.format("%s %s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_distance2[name], gui_nodename1[name])) + submit = "hollowcylinder" elseif fields.worldedit_gui_cylinder_submit_solid then - minetest.chatcommands["/cylinder"].func(name, string.format("%s %s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_distance2[name], gui_nodename1[name])) + submit = "cylinder" end + if submit then + local n = worldedit.normalize_nodename(gui_nodename1[name]) + if n then + minetest.chatcommands["/"..submit].func(name, string.format("%s %s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_distance2[name], n)) + end + end + return true + end + if fields.worldedit_gui_cylinder_axis then + gui_axis1[name] = axis_indices[fields.worldedit_gui_cylinder_axis] + worldedit.show_page(name, "worldedit_gui_cylinder") return true end return false end) worldedit.register_gui_function("worldedit_gui_pyramid", { - name = "Pyramid", privs = minetest.chatcommands["/pyramid"].privs, + name = "Pyramid", + privs = we_privs("pyramid"), get_formspec = function(name) local node, axis, length = gui_nodename1[name], gui_axis1[name], gui_distance1[name] local nodename = worldedit.normalize_nodename(node) return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_pyramid") .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_pyramid_node;Name;%s]", minetest.formspec_escape(node)) .. "button[4,1.18;1.5,0.8;worldedit_gui_pyramid_search;Search]" .. - (nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) - or "image[5.5,1.1;1,1;unknown_node.png]") .. + formspec_node("5.5,1.1", nodename) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_pyramid_length;Length;%s]", minetest.formspec_escape(length)) .. string.format("dropdown[4,2.18;2.5;worldedit_gui_pyramid_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) .. - "button_exit[0,3.5;3,0.8;worldedit_gui_pyramid_submit;Pyramid]" + "button_exit[0,3.5;3,0.8;worldedit_gui_pyramid_submit_hollow;Hollow Pyramid]" .. + "button_exit[3.5,3.5;3,0.8;worldedit_gui_pyramid_submit_solid;Solid Pyramid]" end, }) worldedit.register_gui_handler("worldedit_gui_pyramid", function(name, fields) - if fields.worldedit_gui_pyramid_search or fields.worldedit_gui_pyramid_submit then + if fields.worldedit_gui_pyramid_search or fields.worldedit_gui_pyramid_submit_solid or fields.worldedit_gui_pyramid_submit_hollow or fields.worldedit_gui_pyramid_axis then gui_nodename1[name] = tostring(fields.worldedit_gui_pyramid_node) gui_axis1[name] = axis_indices[fields.worldedit_gui_pyramid_axis] gui_distance1[name] = tostring(fields.worldedit_gui_pyramid_length) worldedit.show_page(name, "worldedit_gui_pyramid") - if fields.worldedit_gui_pyramid_submit then - minetest.chatcommands["/pyramid"].func(name, string.format("%s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_nodename1[name])) + + local submit = nil + if fields.worldedit_gui_pyramid_submit_solid then + submit = "pyramid" + elseif fields.worldedit_gui_pyramid_submit_hollow then + submit = "hollowpyramid" end + if submit then + local n = worldedit.normalize_nodename(gui_nodename1[name]) + if n then + minetest.chatcommands["/"..submit].func(name, string.format("%s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], n)) + end + end + return true + end + if fields.worldedit_gui_pyramid_axis then + gui_axis1[name] = axis_indices[fields.worldedit_gui_pyramid_axis] + worldedit.show_page(name, "worldedit_gui_pyramid") return true end return false end) worldedit.register_gui_function("worldedit_gui_spiral", { - name = "Spiral", privs = minetest.chatcommands["/spiral"].privs, + name = "Spiral", + privs = we_privs("spiral"), get_formspec = function(name) local node, length, height, space = gui_nodename1[name], gui_distance1[name], gui_distance2[name], gui_distance3[name] local nodename = worldedit.normalize_nodename(node) return "size[6.5,6]" .. worldedit.get_formspec_header("worldedit_gui_spiral") .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_spiral_node;Name;%s]", minetest.formspec_escape(node)) .. "button[4,1.18;1.5,0.8;worldedit_gui_spiral_search;Search]" .. - (nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) - or "image[5.5,1.1;1,1;unknown_node.png]") .. + formspec_node("5.5,1.1", nodename) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_spiral_length;Side Length;%s]", minetest.formspec_escape(length)) .. string.format("field[0.5,3.5;4,0.8;worldedit_gui_spiral_height;Height;%s]", minetest.formspec_escape(height)) .. string.format("field[0.5,4.5;4,0.8;worldedit_gui_spiral_space;Wall Spacing;%s]", minetest.formspec_escape(space)) .. @@ -338,7 +408,10 @@ worldedit.register_gui_handler("worldedit_gui_spiral", function(name, fields) gui_distance3[name] = tostring(fields.worldedit_gui_spiral_space) worldedit.show_page(name, "worldedit_gui_spiral") if fields.worldedit_gui_spiral_submit then - minetest.chatcommands["/spiral"].func(name, string.format("%s %s %s %s", gui_distance1[name], gui_distance2[name], gui_distance3[name], gui_nodename1[name])) + local n = worldedit.normalize_nodename(gui_nodename1[name]) + if n then + minetest.chatcommands["/spiral"].func(name, string.format("%s %s %s %s", gui_distance1[name], gui_distance2[name], gui_distance3[name], n)) + end end return true end @@ -346,7 +419,8 @@ worldedit.register_gui_handler("worldedit_gui_spiral", function(name, fields) end) worldedit.register_gui_function("worldedit_gui_copy_move", { - name = "Copy/Move", privs = combine_privs(minetest.chatcommands["/copy"].privs, minetest.chatcommands["/move"].privs), + name = "Copy/Move", + privs = combine_we_privs({"copy", "move"}), get_formspec = function(name) local axis = gui_axis1[name] or 4 local amount = gui_distance1[name] or "10" @@ -370,11 +444,17 @@ worldedit.register_gui_handler("worldedit_gui_copy_move", function(name, fields) end return true end + if fields.worldedit_gui_copy_move_axis then + gui_axis1[name] = axis_indices[fields.worldedit_gui_copy_move_axis] or 4 + worldedit.show_page(name, "worldedit_gui_copy_move") + return true + end return false end) worldedit.register_gui_function("worldedit_gui_stack", { - name = "Stack", privs = minetest.chatcommands["/stack"].privs, + name = "Stack", + privs = we_privs("stack"), get_formspec = function(name) local axis, count = gui_axis1[name], gui_count1[name] return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_stack") .. @@ -392,11 +472,17 @@ worldedit.register_gui_handler("worldedit_gui_stack", function(name, fields) minetest.chatcommands["/stack"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], gui_count1[name])) return true end + if fields.worldedit_gui_stack_axis then + gui_axis1[name] = axis_indices[fields.worldedit_gui_stack_axis] + worldedit.show_page(name, "worldedit_gui_stack") + return true + end return false end) worldedit.register_gui_function("worldedit_gui_stretch", { - name = "Stretch", privs = minetest.chatcommands["/stretch"].privs, + name = "Stretch", + privs = we_privs("stretch"), get_formspec = function(name) local stretchx, stretchy, stretchz = gui_count1[name], gui_count2[name], gui_count3[name] return "size[5,5]" .. worldedit.get_formspec_header("worldedit_gui_stretch") .. @@ -420,7 +506,8 @@ worldedit.register_gui_handler("worldedit_gui_stretch", function(name, fields) end) worldedit.register_gui_function("worldedit_gui_transpose", { - name = "Transpose", privs = minetest.chatcommands["/transpose"].privs, + name = "Transpose", + privs = we_privs("transpose"), get_formspec = function(name) local axis1, axis2 = gui_axis1[name], gui_axis2[name] return "size[5.5,3]" .. worldedit.get_formspec_header("worldedit_gui_transpose") .. @@ -433,18 +520,28 @@ worldedit.register_gui_function("worldedit_gui_transpose", { worldedit.register_gui_handler("worldedit_gui_transpose", function(name, fields) if fields.worldedit_gui_transpose_submit then gui_axis1[name] = axis_indices[fields.worldedit_gui_transpose_axis1] - gui_axis2[name] = axis_indices[fields.worldedit_gui_transpose_axis2] worldedit.show_page(name, "worldedit_gui_transpose") minetest.chatcommands["/transpose"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], axis_values[gui_axis2[name]])) return true end + if fields.worldedit_gui_transpose_axis1 then + gui_axis1[name] = axis_indices[fields.worldedit_gui_transpose_axis1] + worldedit.show_page(name, "worldedit_gui_transpose") + return true + end + if fields.worldedit_gui_transpose_axis2 then + gui_axis2[name] = axis_indices[fields.worldedit_gui_transpose_axis2] + worldedit.show_page(name, "worldedit_gui_transpose") + return true + end return false end) worldedit.register_gui_function("worldedit_gui_flip", { - name = "Flip", privs = minetest.chatcommands["/flip"].privs, + name = "Flip", + privs = we_privs("flip"), get_formspec = function(name) - local axis = gui_axis2[name] + local axis = gui_axis1[name] return "size[5,3]" .. worldedit.get_formspec_header("worldedit_gui_flip") .. string.format("dropdown[0,1;2.5;worldedit_gui_flip_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) .. "button_exit[0,2.5;3,0.8;worldedit_gui_flip_submit;Flip]" @@ -453,16 +550,22 @@ worldedit.register_gui_function("worldedit_gui_flip", { worldedit.register_gui_handler("worldedit_gui_flip", function(name, fields) if fields.worldedit_gui_flip_submit then - gui_axis2[name] = axis_indices[fields.worldedit_gui_flip_axis] + gui_axis1[name] = axis_indices[fields.worldedit_gui_flip_axis] + worldedit.show_page(name, "worldedit_gui_flip") + minetest.chatcommands["/flip"].func(name, axis_values[gui_axis1[name]]) + return true + end + if fields.worldedit_gui_flip_axis then + gui_axis1[name] = axis_indices[fields.worldedit_gui_flip_axis] worldedit.show_page(name, "worldedit_gui_flip") - minetest.chatcommands["/flip"].func(name, axis_values[gui_axis2[name]]) return true end return false end) worldedit.register_gui_function("worldedit_gui_rotate", { - name = "Rotate", privs = minetest.chatcommands["/rotate"].privs, + name = "Rotate", + privs = we_privs("rotate"), get_formspec = function(name) local axis, angle = gui_axis1[name], gui_angle[name] return "size[5.5,3]" .. worldedit.get_formspec_header("worldedit_gui_rotate") .. @@ -480,15 +583,26 @@ worldedit.register_gui_handler("worldedit_gui_rotate", function(name, fields) minetest.chatcommands["/rotate"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], angle_values[gui_angle[name]])) return true end + if fields.worldedit_gui_rotate_axis then + gui_axis1[name] = axis_indices[fields.worldedit_gui_rotate_axis] + worldedit.show_page(name, "worldedit_gui_rotate") + return true + end + if fields.worldedit_gui_rotate_angle then + gui_angle[name] = angle_indices[fields.worldedit_gui_rotate_angle] + worldedit.show_page(name, "worldedit_gui_rotate") + return true + end return false end) worldedit.register_gui_function("worldedit_gui_orient", { - name = "Orient", privs = minetest.chatcommands["/orient"].privs, + name = "Orient", + privs = we_privs("orient"), get_formspec = function(name) local angle = gui_angle[name] return "size[5,3]" .. worldedit.get_formspec_header("worldedit_gui_orient") .. - string.format("dropdown[0,1;2.5;worldedit_gui_rotate_angle;90 degrees,180 degrees,270 degrees;%s]", angle) .. + string.format("dropdown[0,1;2.5;worldedit_gui_orient_angle;90 degrees,180 degrees,270 degrees;%s]", angle) .. "button_exit[0,2.5;3,0.8;worldedit_gui_orient_submit;Orient]" end, }) @@ -497,36 +611,43 @@ worldedit.register_gui_handler("worldedit_gui_orient", function(name, fields) if fields.worldedit_gui_orient_submit then gui_angle[name] = angle_indices[fields.worldedit_gui_orient_angle] worldedit.show_page(name, "worldedit_gui_orient") - minetest.chatcommands["/orient"].func(name, angle_values[gui_angle[name]]) + minetest.chatcommands["/orient"].func(name, tostring(angle_values[gui_angle[name]])) + return true + end + if fields.worldedit_gui_orient_angle then + gui_angle[name] = angle_indices[fields.worldedit_gui_orient_angle] + worldedit.show_page(name, "worldedit_gui_orient") return true end return false end) worldedit.register_gui_function("worldedit_gui_fixlight", { - name = "Fix Lighting", privs = minetest.chatcommands["/fixlight"].privs, + name = "Fix Lighting", + privs = we_privs("fixlight"), on_select = function(name) minetest.chatcommands["/fixlight"].func(name, "") end, }) worldedit.register_gui_function("worldedit_gui_hide", { - name = "Hide Region", privs = minetest.chatcommands["/hide"].privs, + name = "Hide Region", + privs = we_privs("hide"), on_select = function(name) minetest.chatcommands["/hide"].func(name, "") end, }) worldedit.register_gui_function("worldedit_gui_suppress", { - name = "Suppress Nodes", privs = minetest.chatcommands["/suppress"].privs, + name = "Suppress Nodes", + privs = we_privs("suppress"), get_formspec = function(name) local node = gui_nodename1[name] local nodename = worldedit.normalize_nodename(node) return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_suppress") .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_suppress_node;Name;%s]", minetest.formspec_escape(node)) .. "button[4,1.18;1.5,0.8;worldedit_gui_suppress_search;Search]" .. - (nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) - or "image[5.5,1.1;1,1;unknown_node.png]") .. + formspec_node("5.5,1.1", nodename) .. "button_exit[0,2.5;3,0.8;worldedit_gui_suppress_submit;Suppress Nodes]" end, }) @@ -536,7 +657,10 @@ worldedit.register_gui_handler("worldedit_gui_suppress", function(name, fields) gui_nodename1[name] = tostring(fields.worldedit_gui_suppress_node) worldedit.show_page(name, "worldedit_gui_suppress") if fields.worldedit_gui_suppress_submit then - minetest.chatcommands["/suppress"].func(name, gui_nodename1[name]) + local n = worldedit.normalize_nodename(gui_nodename1[name]) + if n then + minetest.chatcommands["/suppress"].func(name, n) + end end return true end @@ -544,15 +668,15 @@ worldedit.register_gui_handler("worldedit_gui_suppress", function(name, fields) end) worldedit.register_gui_function("worldedit_gui_highlight", { - name = "Highlight Nodes", privs = minetest.chatcommands["/highlight"].privs, + name = "Highlight Nodes", + privs = we_privs("highlight"), get_formspec = function(name) local node = gui_nodename1[name] local nodename = worldedit.normalize_nodename(node) return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_highlight") .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_highlight_node;Name;%s]", minetest.formspec_escape(node)) .. "button[4,1.18;1.5,0.8;worldedit_gui_highlight_search;Search]" .. - (nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) - or "image[5.5,1.1;1,1;unknown_node.png]") .. + formspec_node("5.5,1.1", nodename) .. "button_exit[0,2.5;3,0.8;worldedit_gui_highlight_submit;Highlight Nodes]" end, }) @@ -562,7 +686,10 @@ worldedit.register_gui_handler("worldedit_gui_highlight", function(name, fields) gui_nodename1[name] = tostring(fields.worldedit_gui_highlight_node) worldedit.show_page(name, "worldedit_gui_highlight") if fields.worldedit_gui_highlight_submit then - minetest.chatcommands["/highlight"].func(name, gui_nodename1[name]) + local n = worldedit.normalize_nodename(gui_nodename1[name]) + if n then + minetest.chatcommands["/highlight"].func(name, n) + end end return true end @@ -570,14 +697,16 @@ worldedit.register_gui_handler("worldedit_gui_highlight", function(name, fields) end) worldedit.register_gui_function("worldedit_gui_restore", { - name = "Restore Region", privs = minetest.chatcommands["/restore"].privs, + name = "Restore Region", + privs = we_privs("restore"), on_select = function(name) minetest.chatcommands["/restore"].func(name, "") end, }) worldedit.register_gui_function("worldedit_gui_save_load", { - name = "Save/Load", privs = combine_privs(minetest.chatcommands["/save"].privs, minetest.chatcommands["/allocate"].privs, minetest.chatcommands["/load"].privs), + name = "Save/Load", + privs = combine_we_privs({"save", "allocate", "load"}), get_formspec = function(name) local filename = gui_filename[name] return "size[6,4]" .. worldedit.get_formspec_header("worldedit_gui_save_load") .. @@ -588,9 +717,9 @@ worldedit.register_gui_function("worldedit_gui_save_load", { end, }) -worldedit.register_gui_handler("worldedit_gui_save", function(name, fields) - if fields.worldedit_gui_save_load_submit_save or worldedit_gui_save_load_submit_allocate or worldedit_gui_save_load_submit_load then - gui_filename[name] = tostring(fields.worldedit_gui_save_axis) +worldedit.register_gui_handler("worldedit_gui_save_load", function(name, fields) + if fields.worldedit_gui_save_load_submit_save or fields.worldedit_gui_save_load_submit_allocate or fields.worldedit_gui_save_load_submit_load then + gui_filename[name] = tostring(fields.worldedit_gui_save_filename) worldedit.show_page(name, "worldedit_gui_save_load") if fields.worldedit_gui_save_load_submit_save then minetest.chatcommands["/save"].func(name, gui_filename[name]) @@ -605,7 +734,8 @@ worldedit.register_gui_handler("worldedit_gui_save", function(name, fields) end) worldedit.register_gui_function("worldedit_gui_lua", { - name = "Run Lua", + name = "Run Lua", privs = minetest.chatcommands["/clearobjects"].privs, + privs = we_privs("lua"), get_formspec = function(name) local code = gui_code[name] return "size[8,6.5]" .. worldedit.get_formspec_header("worldedit_gui_lua") .. @@ -617,7 +747,7 @@ worldedit.register_gui_function("worldedit_gui_lua", { worldedit.register_gui_handler("worldedit_gui_lua", function(name, fields) if fields.worldedit_gui_lua_run or fields.worldedit_gui_lua_transform then - gui_code[name] = fields.worldedit_gui_lua_value + gui_code[name] = fields.worldedit_gui_lua_code worldedit.show_page(name, "worldedit_gui_lua") if fields.worldedit_gui_lua_run then minetest.chatcommands["/lua"].func(name, gui_code[name]) @@ -630,28 +760,9 @@ worldedit.register_gui_handler("worldedit_gui_lua", function(name, fields) end) worldedit.register_gui_function("worldedit_gui_clearobjects", { - name = "Clear Objects", privs = minetest.chatcommands["/clearobjects"].privs, + name = "Clear Objects", + privs = we_privs("clearobjects"), on_select = function(name) minetest.chatcommands["/clearobjects"].func(name, "") end, }) - -worldedit.register_gui_function("worldedit_gui_formspec_tester", { - name = "Formspec Tester", - get_formspec = function(name) - local value = gui_formspec[name] - return "size[8,6.5]" .. worldedit.get_formspec_header("worldedit_gui_formspec_tester") .. - string.format("textarea[0.5,1;7.5,5.5;worldedit_gui_formspec_tester_value;Formspec Code;%s]", minetest.formspec_escape(value)) .. - "button_exit[0,6;3,0.8;worldedit_gui_formspec_tester_show;Show Formspec]" - end, -}) - -worldedit.register_gui_handler("worldedit_gui_formspec_tester", function(name, fields) - if fields.worldedit_gui_formspec_tester_show then - gui_formspec[name] = fields.worldedit_gui_formspec_tester_value or "" - worldedit.show_page(name, "worldedit_gui_formspec_tester") - minetest.show_formspec(name, "worldedit:formspec_tester", gui_formspec[name]) - return true - end - return false -end) diff --git a/worldedit_gui/init.lua b/worldedit_gui/init.lua old mode 100644 new mode 100755 index 3088f72..162cae0 --- a/worldedit_gui/init.lua +++ b/worldedit_gui/init.lua @@ -14,7 +14,7 @@ Use `nil` for the `options` parameter to unregister the function associated with Use `nil` for the `get_formspec` field to denote that the function does not have its own screen. -Use `nil` for the `privs` field to denote that no special privileges are required to use the function. +The `privs` field may not be `nil`. If the identifier is already registered to another function, it will be replaced by the new one. @@ -24,6 +24,9 @@ The `on_select` function must not call `worldedit.show_page` worldedit.pages = {} --mapping of identifiers to options local identifiers = {} --ordered list of identifiers worldedit.register_gui_function = function(identifier, options) + if options.privs == nil or next(options.privs) == nil then + error("privs unset") + end worldedit.pages[identifier] = options table.insert(identifiers, identifier) end @@ -46,7 +49,7 @@ worldedit.register_gui_handler = function(identifier, handler) --ensure the player has permission to perform the action local entry = worldedit.pages[identifier] - if entry and minetest.check_player_privs(name, entry.privs or {}) then + if entry and minetest.check_player_privs(name, entry.privs) then return handler(name, fields) end return false @@ -67,7 +70,7 @@ local get_formspec = function(name, identifier) end --implement worldedit.show_page(name, page) in different ways depending on the available APIs -if unified_inventory then --unified inventory installed +if rawget(_G, "unified_inventory") then --unified inventory installed local old_func = worldedit.register_gui_function worldedit.register_gui_function = function(identifier, options) old_func(identifier, options) @@ -77,6 +80,8 @@ if unified_inventory then --unified inventory installed unified_inventory.register_button("worldedit_gui", { type = "image", image = "inventory_plus_worldedit_gui.png", + tooltip = "Worldedit GUI", + show_with = "worldedit", --Modiff MFF (Crabman 30/06/2015) }) minetest.register_on_player_receive_fields(function(player, formname, fields) @@ -100,9 +105,12 @@ if unified_inventory then --unified inventory installed player:set_inventory_formspec(get_formspec(name, page)) end end -elseif inventory_plus then --inventory++ installed +elseif rawget(_G, "inventory_plus") then --inventory++ installed minetest.register_on_joinplayer(function(player) - inventory_plus.register_button(player, "worldedit_gui", "WorldEdit") + local can_worldedit = minetest.check_player_privs(player:get_player_name(), {worldedit=true}) + if can_worldedit then + inventory_plus.register_button(player, "worldedit_gui", "WorldEdit") + end end) --show the form when the button is pressed and hide it when done @@ -115,7 +123,7 @@ elseif inventory_plus then --inventory++ installed return true elseif fields.worldedit_gui_exit then --return to original page if gui_player_formspecs[name] then - inventory_plus.set_inventory_formspec(player, gui_player_formspecs[name]) + inventory_plus.set_inventory_formspec(player, inventory_plus.get_formspec(player, "main")) end return true end @@ -128,7 +136,37 @@ elseif inventory_plus then --inventory++ installed inventory_plus.set_inventory_formspec(player, get_formspec(name, page)) end end +elseif rawget(_G, "sfinv") then --sfinv installed (part of minetest_game since 0.4.15) + assert(sfinv.enabled) + local orig_get = sfinv.pages["sfinv:crafting"].get + sfinv.override_page("sfinv:crafting", { + get = function(self, player, context) + local can_worldedit = minetest.check_player_privs(player, {worldedit=true}) + local fs = orig_get(self, player, context) + return fs .. (can_worldedit and "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]" or "") + end + }) + + --show the form when the button is pressed and hide it when done + minetest.register_on_player_receive_fields(function(player, formname, fields) + if fields.worldedit_gui then --main page + worldedit.show_page(player:get_player_name(), "worldedit_gui") + return true + elseif fields.worldedit_gui_exit then --return to original page + sfinv.set_page(player, "sfinv:crafting") + return true + end + return false + end) + + worldedit.show_page = function(name, page) + local player = minetest.get_player_by_name(name) + if player then + player:set_inventory_formspec(get_formspec(name, page)) + end + end else --fallback button + -- FIXME: this is a huge clusterfuck and the back button is broken local player_formspecs = {} local update_main_formspec = function(name) @@ -140,8 +178,21 @@ else --fallback button if not player then --this is in case the player signs off while the media is loading return end - if (minetest.check_player_privs(name, {creative=true}) or minetest.setting_getbool("creative_mode")) and creative_inventory then --creative_inventory is active, add button to modified formspec - formspec = player:get_inventory_formspec() .. "image_button[6,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]" + if (minetest.check_player_privs(name, {creative=true}) or + minetest.setting_getbool("creative_mode")) and + creative then --creative is active, add button to modified formspec + local creative_formspec = player:get_inventory_formspec() + local tab_id = tonumber(creative_formspec:match("tabheader%[.-;(%d+)%;")) + + if tab_id == 1 then + formspec = creative_formspec .. + "image_button[0,1;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]" + elseif not tab_id then + formspec = creative_formspec .. + "image_button[6,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]" + else + return + end else formspec = formspec .. "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]" end @@ -194,6 +245,7 @@ end worldedit.register_gui_function("worldedit_gui", { name = "WorldEdit GUI", + privs = {interact=true}, get_formspec = function(name) --create a form with all the buttons arranged in a grid local buttons, x, y, index = {}, 0, 1, 0 @@ -226,7 +278,7 @@ worldedit.register_gui_handler("worldedit_gui", function(name, fields) for identifier, entry in pairs(worldedit.pages) do --check for WorldEdit GUI main formspec button selection if fields[identifier] and identifier ~= "worldedit_gui" then --ensure player has permission to perform action - local has_privs, missing_privs = minetest.check_player_privs(name, entry.privs or {}) + local has_privs, missing_privs = minetest.check_player_privs(name, entry.privs) if not has_privs then worldedit.player_notify(name, "you are not allowed to use this function (missing privileges: " .. table.concat(missing_privs, ", ") .. ")") return false diff --git a/worldedit_gui/textures/worldedit_gui_unknown.png b/worldedit_gui/textures/worldedit_gui_unknown.png new file mode 100644 index 0000000..f57436c Binary files /dev/null and b/worldedit_gui/textures/worldedit_gui_unknown.png differ diff --git a/worldedit_infinity/depends.txt b/worldedit_infinity/depends.txt new file mode 100755 index 0000000..c4f6871 --- /dev/null +++ b/worldedit_infinity/depends.txt @@ -0,0 +1 @@ +worldedit? diff --git a/worldedit_infinity/init.lua b/worldedit_infinity/init.lua old mode 100644 new mode 100755 index be79101..8bd3a05 --- a/worldedit_infinity/init.lua +++ b/worldedit_infinity/init.lua @@ -1,4 +1,4 @@ -worldedit = worldedit or {} +worldedit = rawget(_G, "worldedit") or {} local minetest = minetest --local copy of global local get_pointed = function(pos, nearest, distance) @@ -100,4 +100,4 @@ worldedit.raytrace = function(pos, dir, callback) intersectz = intersectz + componentz end end -end \ No newline at end of file +end diff --git a/worldedit_limited/depends.txt b/worldedit_limited/depends.txt old mode 100644 new mode 100755 diff --git a/worldedit_limited/init.lua b/worldedit_limited/init.lua old mode 100644 new mode 100755 diff --git a/worldedit_shortcommands/depends.txt b/worldedit_shortcommands/depends.txt old mode 100644 new mode 100755 diff --git a/worldedit_shortcommands/init.lua b/worldedit_shortcommands/init.lua old mode 100644 new mode 100755 index a3cbb67..a4350ae --- a/worldedit_shortcommands/init.lua +++ b/worldedit_shortcommands/init.lua @@ -31,6 +31,7 @@ worldedit.alias_chatcommand("/hdo", "/hollowdome") worldedit.alias_chatcommand("/do", "/dome") worldedit.alias_chatcommand("/hcyl", "/hollowcylinder") worldedit.alias_chatcommand("/cyl", "/cylinder") +worldedit.alias_chatcommand("/hpyr", "/hollowpyramid") worldedit.alias_chatcommand("/pyr", "/pyramid") worldedit.alias_chatcommand("/spl", "/spiral") worldedit.alias_chatcommand("/m", "/move") @@ -47,4 +48,4 @@ worldedit.alias_chatcommand("/hlt", "/highlight") worldedit.alias_chatcommand("/rsr", "/restore") worldedit.alias_chatcommand("/l", "/lua") worldedit.alias_chatcommand("/lt", "/luatransform") -worldedit.alias_chatcommand("/clro", "/clearobjects") \ No newline at end of file +worldedit.alias_chatcommand("/clro", "/clearobjects")