Fix backwards rotation and add fast node filling

This commit is contained in:
Johannes Fritz 2022-07-29 07:59:40 -05:00
parent 3e0ed1273b
commit 72457e934c
3 changed files with 100 additions and 36 deletions

View File

@ -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) | | Undo | edit:undo | ![](textures/edit_undo.png) |
## Dependences ## Dependencies
None None
@ -43,17 +43,17 @@ When two copy nodes are placed at opposite corners of an area, they select the a
### Paste Node ### 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 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 ### 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. 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. 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, CC0 by MrRar check [License](LICENSE) file, this mod was started by MrRar,
minetest-mods community. minetest-mods community.

View File

@ -4,8 +4,9 @@
local player_data = {} local player_data = {}
local paste_preview_max_entities = tonumber(minetest.settings:get("paste_preview_max_entities") or 2000) local paste_preview_max_entities = tonumber(minetest.settings:get("edit_paste_preview_max_entities") or 2000)
local max_operation_volume = tonumber(minetest.settings:get("max_operation_volume") or 20000) 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 function create_paste_preview(player)
local player_pos = player:get_pos() local player_pos = player:get_pos()
@ -135,10 +136,15 @@ local function has_privilege(player)
end end
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) local function on_place_checks(player)
return player and return player and
player:is_player() and player:is_player() and
has_privilege(player) has_privilege(player)
end end
@ -200,7 +206,7 @@ minetest.register_node("edit:delete", {
) )
local size = vector.add(vector.subtract(_end, start), 1) local size = vector.add(vector.subtract(_end, start), 1)
if size.x * size.y * size.z > max_operation_volume then 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 return
end end
player_data[player].undo_schematic = schematic_from_map(start, size) 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)) local size = vector.add(vector.subtract(_end, start), vector.new(1, 1, 1))
if size.x * size.y * size.z > max_operation_volume then 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 return
end end
@ -444,6 +450,16 @@ minetest.register_node("edit:fill",{
local itemstack, pos = minetest.item_place_node(itemstack, player, pointed_thing) local itemstack, pos = minetest.item_place_node(itemstack, player, pointed_thing)
if player_data[player].fill1_pos and pos then 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].fill2_pos = pos
player_data[player].fill_pointed_thing = pointed_thing 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 is_node = minetest.registered_nodes[name]
local param2 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()) 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) param2 = minetest.dir_to_wallmounted(player:get_look_dir(), true)
end end
@ -549,27 +565,46 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
) )
local size = vector.add(vector.subtract(_end, start), 1) 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) player_data[player].undo_schematic = schematic_from_map(start, size)
for x = start.x, _end.x, 1 do if is_node and use_fast_node_fill then
for y = start.y, _end.y, 1 do local voxel_manip = VoxelManip()
for z = start.z, _end.z, 1 do local vm_start, vm_end = voxel_manip:read_from_map(start, _end)
local pos = vector.new(x, y, z) local param2s = voxel_manip:get_param2_data()
if is_node then local content_ids = voxel_manip:get_data()
minetest.set_node(pos, {name = name, param2 = param2}) local content_id = minetest.get_content_id(name)
else
minetest.remove_node(pos) local ones = vector.new(1, 1, 1)
end local vm_size = vector.add(vector.subtract(vm_end, vm_start), ones)
if on_place then local voxel_area = VoxelArea:new({MinEdge = ones, MaxEdge = vm_size})
local itemstack = ItemStack(name) local va_start = vector.add(vector.subtract(start, vm_start), ones)
pointed_thing.intersection_point = vector.new(x + 0.5, y, z + 0.5) local va_end = vector.subtract(vector.add(va_start, size), ones)
pointed_thing.above = pos for i in voxel_area:iterp(va_start, va_end) do
pointed_thing.below = vector.new(x, y - 1, z) content_ids[i] = content_id
on_place(itemstack, player, pointed_thing) 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 end
end end
@ -804,13 +839,13 @@ minetest.register_globalstep(function(dtime)
local control = player:get_player_control() local control = player:get_player_control()
if control.sneak and control.left then if control.sneak and control.left then
if d.paste_preview_can_rotate then if d.paste_preview_can_rotate then
set_schematic_rotation(d.schematic, -90) set_schematic_rotation(d.schematic, 90)
delete_paste_preview(player) delete_paste_preview(player)
d.paste_preview_can_rotate = false d.paste_preview_can_rotate = false
end end
elseif control.sneak and control.right then elseif control.sneak and control.right then
if d.paste_preview_can_rotate then if d.paste_preview_can_rotate then
set_schematic_rotation(d.schematic, 90) set_schematic_rotation(d.schematic, -90)
delete_paste_preview(player) delete_paste_preview(player)
d.paste_preview_can_rotate = false d.paste_preview_can_rotate = false
end end

View File

@ -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