diff --git a/README.md b/README.md index 4dfa43b..5632f64 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ This mod was inspired by the Fill Start and Fill End blocks in Manic Digger. | Undo | edit:undo | ![](textures/edit_undo.png) | -## Dependences +## Dependencies None @@ -43,17 +43,17 @@ When two copy nodes are placed at opposite corners of an area, they select the a ### Paste Node -The paste node is used for pasting the area copied by the copy node or a schematic loaded with the open node. When a paste node is placed, the copied area or schematic is placed at the corner of the paste node. In the case of a copied area, area is pasted relative to the position of the first placed copy node when it was copied. The copied area can be rotated when the paste preview is visable by pressing the sneak key and an arrow key. +The paste node is used for pasting the area copied by the copy node or a schematic loaded with the open node. When a paste node is placed, the copied area or schematic is placed at the corner of the paste node. In the case of a copied area, area is pasted relative to the position of the first placed copy node when it was copied. The copied area can be rotated when the paste preview is visible by pressing the sneak key and an arrow key. ### Delete Node -Delete nodes are used to delete an area. When two delete nodes are placed at opposite corners of an area, they select the area as show in figure 1. When an area is selected, it is deleted and the two delete nodes are removed as well. +Delete nodes are used to delete a 3D area. When two delete nodes are placed at opposite corners of an area, they select the area as show in figure 1. When an area is selected, it is deleted and the two delete nodes are removed as well. ### Fill Node -![figure1.png](figure2.png) +![figure2.png](figure2.png) Fill nodes are used to fill a 3D area with a certain item. Start by placing two fill nodes at opposite corners of the desired area. The selected area includes the positions of the fill nodes themselves as shown in figure 2. @@ -77,7 +77,30 @@ Use a second time to redo the undo. Only the most resent world modification can be undone. -### License +## Settings + +### edit_paste_preview_max_entities + +If the copied area has a larger number of nodes, some nodes will be randomly excluded from the preview. + + +### edit_max_operation_volume + +The maximum volume of any edit operation. Increase to allow larger operations. + + +### edit_use_fast_node_fill + +Fast filling of nodes. This uses VoxelManip for fast node placement. +No node placement callbacks are called so some nodes might be broken. + + +## Privileges + +Edit tools and nodes can only be used by players with `edit` privilege. + + +## License CC0 by MrRar check [License](LICENSE) file, this mod was started by MrRar, minetest-mods community. diff --git a/init.lua b/init.lua index 96d10d4..54ab5a0 100644 --- a/init.lua +++ b/init.lua @@ -4,8 +4,9 @@ local player_data = {} -local paste_preview_max_entities = tonumber(minetest.settings:get("paste_preview_max_entities") or 2000) -local max_operation_volume = tonumber(minetest.settings:get("max_operation_volume") or 20000) +local paste_preview_max_entities = tonumber(minetest.settings:get("edit_paste_preview_max_entities") or 2000) +local max_operation_volume = tonumber(minetest.settings:get("edit_max_operation_volume") or 20000) +local use_fast_node_fill = minetest.settings:get_bool("edit_use_fast_node_fill", false) local function create_paste_preview(player) local player_pos = player:get_pos() @@ -135,10 +136,15 @@ local function has_privilege(player) end end +local function display_size_error(player) + local msg = "Operation too large. The maximum operation volume can be changed in Minetest settings." + minetest.chat_send_player(player:get_player_name(), msg) +end + local function on_place_checks(player) return player and - player:is_player() and - has_privilege(player) + player:is_player() and + has_privilege(player) end @@ -200,7 +206,7 @@ minetest.register_node("edit:delete", { ) local size = vector.add(vector.subtract(_end, start), 1) if size.x * size.y * size.z > max_operation_volume then - minetest.chat_send_player(player:get_player_name(), "Delete operation too big.") + display_size_error(player) return end player_data[player].undo_schematic = schematic_from_map(start, size) @@ -272,7 +278,7 @@ minetest.register_node("edit:copy",{ local size = vector.add(vector.subtract(_end, start), vector.new(1, 1, 1)) if size.x * size.y * size.z > max_operation_volume then - minetest.chat_send_player(player:get_player_name(), "Copy operation too big.") + display_size_error(player) return end @@ -444,6 +450,16 @@ minetest.register_node("edit:fill",{ local itemstack, pos = minetest.item_place_node(itemstack, player, pointed_thing) if player_data[player].fill1_pos and pos then + local diff = vector.subtract(player_data[player].fill1_pos, pos) + local size = vector.add(vector.apply(diff, math.abs), 1) + if size.x * size.y * size.z > max_operation_volume then + display_size_error(player) + minetest.remove_node(player_data[player].fill1_pos) + player_data[player].fill1_pos = nil + minetest.remove_node(pos) + return + end + player_data[player].fill2_pos = pos player_data[player].fill_pointed_thing = pointed_thing @@ -529,9 +545,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local is_node = minetest.registered_nodes[name] local param2 - if def.paramtype2 == "facedir" then + if def.paramtype2 == "facedir" or def.paramtype2 == "colorfacedir" then param2 = minetest.dir_to_facedir(player:get_look_dir()) - elseif def.paramtype2 == "wallmounted" then + elseif def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted" then param2 = minetest.dir_to_wallmounted(player:get_look_dir(), true) end @@ -549,27 +565,46 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) ) local size = vector.add(vector.subtract(_end, start), 1) - if size.x * size.y * size.z > max_operation_volume then - minetest.chat_send_player(player:get_player_name(), "Fill operation too large.") - return true - end + player_data[player].undo_schematic = schematic_from_map(start, size) - for x = start.x, _end.x, 1 do - for y = start.y, _end.y, 1 do - for z = start.z, _end.z, 1 do - local pos = vector.new(x, y, z) - if is_node then - minetest.set_node(pos, {name = name, param2 = param2}) - else - minetest.remove_node(pos) - end - if on_place then - local itemstack = ItemStack(name) - pointed_thing.intersection_point = vector.new(x + 0.5, y, z + 0.5) - pointed_thing.above = pos - pointed_thing.below = vector.new(x, y - 1, z) - on_place(itemstack, player, pointed_thing) + if is_node and use_fast_node_fill then + local voxel_manip = VoxelManip() + local vm_start, vm_end = voxel_manip:read_from_map(start, _end) + local param2s = voxel_manip:get_param2_data() + local content_ids = voxel_manip:get_data() + local content_id = minetest.get_content_id(name) + + local ones = vector.new(1, 1, 1) + local vm_size = vector.add(vector.subtract(vm_end, vm_start), ones) + local voxel_area = VoxelArea:new({MinEdge = ones, MaxEdge = vm_size}) + local va_start = vector.add(vector.subtract(start, vm_start), ones) + local va_end = vector.subtract(vector.add(va_start, size), ones) + for i in voxel_area:iterp(va_start, va_end) do + content_ids[i] = content_id + param2s[i] = param2 + end + voxel_manip:set_data(content_ids) + voxel_manip:set_param2_data(param2s) + voxel_manip:write_to_map(true) + voxel_manip:update_liquids() + else + for x = start.x, _end.x, 1 do + for y = start.y, _end.y, 1 do + for z = start.z, _end.z, 1 do + local pos = vector.new(x, y, z) + if is_node then + minetest.set_node(pos, {name = name, param2 = param2}) + else + minetest.remove_node(pos) + end + if on_place then + local itemstack = ItemStack(name) + pointed_thing.intersection_point = vector.new(x + 0.5, y, z + 0.5) + pointed_thing.above = pos + pointed_thing.below = vector.new(x, y - 1, z) + on_place(itemstack, player, pointed_thing) + end end end end @@ -804,13 +839,13 @@ minetest.register_globalstep(function(dtime) local control = player:get_player_control() if control.sneak and control.left then if d.paste_preview_can_rotate then - set_schematic_rotation(d.schematic, -90) + set_schematic_rotation(d.schematic, 90) delete_paste_preview(player) d.paste_preview_can_rotate = false end elseif control.sneak and control.right then if d.paste_preview_can_rotate then - set_schematic_rotation(d.schematic, 90) + set_schematic_rotation(d.schematic, -90) delete_paste_preview(player) d.paste_preview_can_rotate = false end diff --git a/settingtypes.txt b/settingtypes.txt index 7434cda..b9f810c 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -1,4 +1,10 @@ +# The maximum number of entities used to construct the paste preview. +# If the copied area has a larger number of nodes, some nodes will be randomly excluded from the preview. +edit_paste_preview_max_entities (Paste preview max entities) int 2000 -paste_preview_max_entities (Paste preview max entities) int 2000 +# The maximum volume of any edit operation. Increase to allow larger operations. +edit_max_operation_volume (Max edit operation volume) int 20000 -max_operation_volume (Max edit operation volume) int 20000 +# Fast filling of nodes. This uses VoxelManip for fast node placement. +# No node placement callbacks are called so some nodes might be broken. +edit_use_fast_node_fill (Use fast node fill) bool false