edit-cd2025/fill.lua
2024-02-19 20:02:24 -06:00

160 lines
4.5 KiB
Lua

local function player_select_item_formspec(player)
edit.player_select_item(player, "Select item to use for fill", function(player, item_str)
local d = edit.player_data[player]
if
not d.fill1 or not d.fill2 or
not edit.has_privilege(player)
then return end
local p1 = d.fill1._pos
local p2 = d.fill2._pos
d.fill1.object:remove()
local item = ItemStack(item_str)
local name = item:get_name()
if name == "" then return end
local def = minetest.registered_items[name]
if not def then return end
local is_node = minetest.registered_nodes[name]
local param2
if def.paramtype2 == "facedir" or def.paramtype2 == "colorfacedir" then
param2 = minetest.dir_to_facedir(player:get_look_dir())
elseif def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted" then
param2 = minetest.dir_to_wallmounted(player:get_look_dir(), true)
end
local start = vector.new(
math.min(p1.x, p2.x),
math.min(p1.y, p2.y),
math.min(p1.z, p2.z)
)
local _end = vector.new(
math.max(p1.x, p2.x),
math.max(p1.y, p2.y),
math.max(p1.z, p2.z)
)
local size = vector.add(vector.subtract(_end, start), 1)
d.undo_schematic = edit.schematic_from_map(start, size)
local volume = size.x * size.y * size.z
if is_node and volume >= edit.fast_node_fill_threshold 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
local node = {name = item_str, param2 = param2}
-- Work top to bottom so we can remove falling nodes
for x = _end.x, start.x, -1 do
for y = _end.y, start.y, -1 do
for z = _end.z, start.z, -1 do
local pos = vector.new(x, y, z)
edit.place_item_like_player(player, node, pos)
end
end
end
end
return
end)
end
local function fill_on_place(itemstack, player, pointed_thing)
if not edit.on_place_checks(player) then return end
if not pointed_thing.above then
pointed_thing = edit.get_pointed_thing_node(player)
end
local pos = edit.pointed_thing_to_pos(pointed_thing)
local player_data = edit.player_data[player]
if player_data.fill1 and pos then
player_data.fill2 = edit.add_marker("edit:fill", pos, player)
if not player_data.fill2 then return end
local diff = vector.subtract(player_data.fill1._pos, pos)
local volume = vector.add(vector.apply(diff, math.abs), 1)
if volume.x * volume.y * volume.z > edit.max_operation_volume then
edit.display_size_error(player)
player_data.fill1.object:remove()
return
end
player_select_item_formspec(player)
elseif pos then
player_data.fill1 = edit.add_marker("edit:fill", pos, player)
end
end
minetest.register_tool("edit:fill", {
description = "Edit Fill",
tiles = {"edit_fill.png"},
inventory_image = "edit_fill.png",
range = 10,
groups = {edit_place_preview = 1, edit_box_select_preview = 1},
on_place = fill_on_place,
on_secondary_use = fill_on_place,
_edit_get_selection_points = function(player)
local d = edit.player_data[player]
return d.fill1 and d.fill1._pos, d.fill2 and d.fill2._pos
end,
})
minetest.register_entity("edit:fill", {
initial_properties = {
visual = "cube",
visual_size = { x = 1.1, y = 1.1 },
physical = false,
collide_with_objects = false,
static_save = false,
use_texture_alpha = true,
glow = -1,
backface_culling = false,
hp_max = 1,
textures = {
"edit_fill.png",
"edit_fill.png",
"edit_fill.png",
"edit_fill.png",
"edit_fill.png",
"edit_fill.png",
},
},
on_deactivate = function(self)
local player_data = edit.player_data[self._placer]
self.remove_called = true
if player_data then
if player_data.fill1 and not player_data.fill1.remove_called then
player_data.fill1.object:remove()
end
if player_data.fill2 and not player_data.fill2.remove_called then
player_data.fill2.object:remove()
end
player_data.fill1 = nil
player_data.fill2 = nil
end
end,
})