diff --git a/mods/default/functions.lua b/mods/default/functions.lua index 9d164b4b..7e0e8d04 100644 --- a/mods/default/functions.lua +++ b/mods/default/functions.lua @@ -835,3 +835,17 @@ function default.can_interact_with_node(player, pos) return false end + +function default.do_with_area(p1, p2, func) + local vm = VoxelManip() + local minp, maxp = vm:read_from_map(p1, p2) + local va = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) + local data = vm:get_data() + func(va, data) + vm:set_data(data) + vm:write_to_map() + vm:update_liquids() + if vm.close ~= nil then + vm:close() + end +end diff --git a/mods/default/trees.lua b/mods/default/trees.lua index 5445a6fb..46e573c5 100644 --- a/mods/default/trees.lua +++ b/mods/default/trees.lua @@ -115,26 +115,13 @@ function default.grow_tree(pos, is_apple_tree, bad) error("Deprecated use of default.grow_tree") end - local x, y, z = pos.x, pos.y, pos.z local height = random(4, 5) local c_tree = minetest.get_content_id("default:tree") local c_leaves = minetest.get_content_id("default:leaves") - local vm = minetest.get_voxel_manip() - local minp, maxp = vm:read_from_map( - {x = x - 2, y = y, z = z - 2}, - {x = x + 2, y = y + height + 1, z = z + 2} - ) - local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) - local data = vm:get_data() - - add_trunk_and_leaves(data, a, pos, c_tree, c_leaves, height, 2, 8, is_apple_tree) - - vm:set_data(data) - vm:write_to_map() - if vm.close ~= nil then - vm:close() - end + default.do_with_area(pos:offset(-2, 0, -2), pos:offset(2, height + 1, 2), function(a, data) + add_trunk_and_leaves(data, a, pos, c_tree, c_leaves, height, 2, 8, is_apple_tree) + end) end -- Jungle tree @@ -156,39 +143,27 @@ function default.grow_jungle_tree(pos, bad) local c_jungletree = minetest.get_content_id("default:jungletree") local c_jungleleaves = minetest.get_content_id("default:jungleleaves") - local vm = minetest.get_voxel_manip() - local minp, maxp = vm:read_from_map( - {x = x - 3, y = y - 1, z = z - 3}, - {x = x + 3, y = y + height + 1, z = z + 3} - ) - local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) - local data = vm:get_data() + default.do_with_area(pos:offset(-3, -1, -3), pos:offset(3, height + 1, 3), function(a, data) + add_trunk_and_leaves(data, a, pos, c_jungletree, c_jungleleaves, + height, 3, 30, false) - add_trunk_and_leaves(data, a, pos, c_jungletree, c_jungleleaves, - height, 3, 30, false) - - -- Roots - for z_dist = -1, 1 do - local vi_1 = a:index(x - 1, y - 1, z + z_dist) - local vi_2 = a:index(x - 1, y, z + z_dist) - for x_dist = -1, 1 do - if random(1, 3) >= 2 then - if data[vi_1] == c_air or data[vi_1] == c_ignore then - data[vi_1] = c_jungletree - elseif data[vi_2] == c_air or data[vi_2] == c_ignore then - data[vi_2] = c_jungletree + -- Roots + for z_dist = -1, 1 do + local vi_1 = a:index(x - 1, y - 1, z + z_dist) + local vi_2 = a:index(x - 1, y, z + z_dist) + for x_dist = -1, 1 do + if random(1, 3) >= 2 then + if data[vi_1] == c_air or data[vi_1] == c_ignore then + data[vi_1] = c_jungletree + elseif data[vi_2] == c_air or data[vi_2] == c_ignore then + data[vi_2] = c_jungletree + end end + vi_1 = vi_1 + 1 + vi_2 = vi_2 + 1 end - vi_1 = vi_1 + 1 - vi_2 = vi_2 + 1 end - end - - vm:set_data(data) - vm:write_to_map() - if vm.close ~= nil then - vm:close() - end + end) end @@ -218,105 +193,93 @@ function default.grow_pine_tree(pos, snow) local c_pine_needles = minetest.get_content_id("default:pine_needles") local c_snow = minetest.get_content_id("default:snow") - local vm = minetest.get_voxel_manip() - local minp, maxp = vm:read_from_map( - {x = x - 3, y = y, z = z - 3}, - {x = x + 3, y = maxy + 3, z = z + 3} - ) - local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) - local data = vm:get_data() + default.do_with_area(pos:offset(-3, 0, -3), vector.new(x + 3, maxy + 3, z + 3), function(a, data) + -- Upper branches layer + local dev = 3 + for yy = maxy - 1, maxy + 1 do + for zz = z - dev, z + dev do + local vi = a:index(x - dev, yy, zz) + local via = a:index(x - dev, yy + 1, zz) + for xx = x - dev, x + dev do + if random() < 0.95 - dev * 0.05 then + add_pine_needles(data, vi, c_air, c_ignore, c_snow, + c_pine_needles) + if snow then + add_snow(data, via, c_air, c_ignore, c_snow) + end + end + vi = vi + 1 + via = via + 1 + end + end + dev = dev - 1 + end - -- Upper branches layer - local dev = 3 - for yy = maxy - 1, maxy + 1 do - for zz = z - dev, z + dev do - local vi = a:index(x - dev, yy, zz) - local via = a:index(x - dev, yy + 1, zz) - for xx = x - dev, x + dev do - if random() < 0.95 - dev * 0.05 then + -- Centre top nodes + add_pine_needles(data, a:index(x, maxy + 1, z), c_air, c_ignore, c_snow, + c_pine_needles) + add_pine_needles(data, a:index(x, maxy + 2, z), c_air, c_ignore, c_snow, + c_pine_needles) -- Paramat added a pointy top node + if snow then + add_snow(data, a:index(x, maxy + 3, z), c_air, c_ignore, c_snow) + end + + -- Lower branches layer + local my = 0 + for i = 1, 20 do -- Random 2x2 squares of needles + local xi = x + random(-3, 2) + local yy = maxy + random(-6, -5) + local zi = z + random(-3, 2) + if yy > my then + my = yy + end + for zz = zi, zi+1 do + local vi = a:index(xi, yy, zz) + local via = a:index(xi, yy + 1, zz) + for xx = xi, xi + 1 do add_pine_needles(data, vi, c_air, c_ignore, c_snow, c_pine_needles) if snow then add_snow(data, via, c_air, c_ignore, c_snow) end + vi = vi + 1 + via = via + 1 end - vi = vi + 1 - via = via + 1 end end - dev = dev - 1 - end - -- Centre top nodes - add_pine_needles(data, a:index(x, maxy + 1, z), c_air, c_ignore, c_snow, - c_pine_needles) - add_pine_needles(data, a:index(x, maxy + 2, z), c_air, c_ignore, c_snow, - c_pine_needles) -- Paramat added a pointy top node - if snow then - add_snow(data, a:index(x, maxy + 3, z), c_air, c_ignore, c_snow) - end - - -- Lower branches layer - local my = 0 - for i = 1, 20 do -- Random 2x2 squares of needles - local xi = x + random(-3, 2) - local yy = maxy + random(-6, -5) - local zi = z + random(-3, 2) - if yy > my then - my = yy - end - for zz = zi, zi+1 do - local vi = a:index(xi, yy, zz) - local via = a:index(xi, yy + 1, zz) - for xx = xi, xi + 1 do - add_pine_needles(data, vi, c_air, c_ignore, c_snow, - c_pine_needles) - if snow then - add_snow(data, via, c_air, c_ignore, c_snow) - end - vi = vi + 1 - via = via + 1 - end - end - end - - dev = 2 - for yy = my + 1, my + 2 do - for zz = z - dev, z + dev do - local vi = a:index(x - dev, yy, zz) - local via = a:index(x - dev, yy + 1, zz) - for xx = x - dev, x + dev do - if random() < 0.95 - dev * 0.05 then - add_pine_needles(data, vi, c_air, c_ignore, c_snow, - c_pine_needles) - if snow then - add_snow(data, via, c_air, c_ignore, c_snow) + dev = 2 + for yy = my + 1, my + 2 do + for zz = z - dev, z + dev do + local vi = a:index(x - dev, yy, zz) + local via = a:index(x - dev, yy + 1, zz) + for xx = x - dev, x + dev do + if random() < 0.95 - dev * 0.05 then + add_pine_needles(data, vi, c_air, c_ignore, c_snow, + c_pine_needles) + if snow then + add_snow(data, via, c_air, c_ignore, c_snow) + end end + vi = vi + 1 + via = via + 1 end - vi = vi + 1 - via = via + 1 + end + dev = dev - 1 + end + + -- Trunk + -- Force-place lowest trunk node to replace sapling + data[a:index(x, y, z)] = c_pine_tree + for yy = y + 1, maxy do + local vi = a:index(x, yy, z) + local node_id = data[vi] + if node_id == c_air or node_id == c_ignore or + node_id == c_pine_needles or node_id == c_snow then + data[vi] = c_pine_tree end end - dev = dev - 1 - end - - -- Trunk - -- Force-place lowest trunk node to replace sapling - data[a:index(x, y, z)] = c_pine_tree - for yy = y + 1, maxy do - local vi = a:index(x, yy, z) - local node_id = data[vi] - if node_id == c_air or node_id == c_ignore or - node_id == c_pine_needles or node_id == c_snow then - data[vi] = c_pine_tree - end - end - - vm:set_data(data) - vm:write_to_map() - if vm.close ~= nil then - vm:close() - end + end) end diff --git a/mods/tnt/init.lua b/mods/tnt/init.lua index 82306121..ca583444 100644 --- a/mods/tnt/init.lua +++ b/mods/tnt/init.lua @@ -294,12 +294,8 @@ end local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast, owner, explode_center) pos = vector.round(pos) -- scan for adjacent TNT nodes first, and enlarge the explosion - local vm1 = VoxelManip() - local p1 = vector.subtract(pos, 2) - local p2 = vector.add(pos, 2) - local minp, maxp = vm1:read_from_map(p1, p2) - local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) - local data = vm1:get_data() + -- TODO given that we're looking at a fraction of a mapblock here, + -- there is probably little reason to use VoxelManip. local count = 0 local c_tnt local c_tnt_burning = minetest.get_content_id("tnt:tnt_burning") @@ -311,88 +307,74 @@ local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast, owne else c_tnt = c_tnt_burning -- tnt is not registered if disabled end - -- make sure we still have explosion even when centre node isnt tnt related - if explode_center then - count = 1 - end - for z = pos.z - 2, pos.z + 2 do - for y = pos.y - 2, pos.y + 2 do - local vi = a:index(pos.x - 2, y, z) - for x = pos.x - 2, pos.x + 2 do - local cid = data[vi] - if cid == c_tnt or cid == c_tnt_boom or cid == c_tnt_burning then - count = count + 1 - data[vi] = c_air - end - vi = vi + 1 + default.do_with_area(pos:subtract(2), pos:add(2), function(a, data) + -- make sure we still have explosion even when centre node isnt tnt related + if explode_center then + count = 1 end - end - end - vm1:set_data(data) - vm1:write_to_map() - if vm1.close ~= nil then - vm1:close() - end + for z = pos.z - 2, pos.z + 2 do + for y = pos.y - 2, pos.y + 2 do + local vi = a:index(pos.x - 2, y, z) + for x = pos.x - 2, pos.x + 2 do + local cid = data[vi] + if cid == c_tnt or cid == c_tnt_boom or cid == c_tnt_burning then + count = count + 1 + data[vi] = c_air + end + vi = vi + 1 + end + end + end + end) -- recalculate new radius radius = math.floor(radius * math.pow(count, 1/3)) -- perform the explosion - local vm = VoxelManip() local pr = PseudoRandom(os.time()) - p1 = vector.subtract(pos, radius) - p2 = vector.add(pos, radius) - minp, maxp = vm:read_from_map(p1, p2) - a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) - data = vm:get_data() - local drops = {} local on_blast_queue = {} local on_construct_queue = {} basic_flame_on_construct = minetest.registered_nodes["fire:basic_flame"].on_construct - -- Used to efficiently remove metadata of nodes that were destroyed. - -- Metadata is probably sparse, so this may save us some work. - local has_meta = {} - for _, p in ipairs(minetest.find_nodes_with_meta(p1, p2)) do - has_meta[a:indexp(p)] = true - end + local p1, p2 = pos:subtract(radius), pos:add(radius) + default.do_with_area(p1, p2, function(a, data) -- luacheck: ignore + -- Used to efficiently remove metadata of nodes that were destroyed. + -- Metadata is probably sparse, so this may save us some work. + local has_meta = {} + for _, p in ipairs(minetest.find_nodes_with_meta(p1, p2)) do + has_meta[a:indexp(p)] = true + end - local c_fire = minetest.get_content_id("fire:basic_flame") - for z = -radius, radius do - for y = -radius, radius do - local vi = a:index(pos.x + (-radius), pos.y + y, pos.z + z) - for x = -radius, radius do - local r = vector.length(vector.new(x, y, z)) - if (radius * radius) / (r * r) >= (pr:next(80, 125) / 100) then - local cid = data[vi] - local p = {x = pos.x + x, y = pos.y + y, z = pos.z + z} - if cid ~= c_air and cid ~= c_ignore then - local new_cid = destroy(drops, p, cid, c_air, c_fire, - on_blast_queue, on_construct_queue, - ignore_protection, ignore_on_blast, owner) + local c_fire = minetest.get_content_id("fire:basic_flame") + for z = -radius, radius do + for y = -radius, radius do + local vi = a:index(pos.x + (-radius), pos.y + y, pos.z + z) + for x = -radius, radius do + local r = vector.length(vector.new(x, y, z)) + if (radius * radius) / (r * r) >= (pr:next(80, 125) / 100) then + local cid = data[vi] + local p = {x = pos.x + x, y = pos.y + y, z = pos.z + z} + if cid ~= c_air and cid ~= c_ignore then + local new_cid = destroy(drops, p, cid, c_air, c_fire, + on_blast_queue, on_construct_queue, + ignore_protection, ignore_on_blast, owner) - if new_cid ~= data[vi] then - data[vi] = new_cid - if has_meta[vi] then - minetest.get_meta(p):from_table(nil) + if new_cid ~= data[vi] then + data[vi] = new_cid + if has_meta[vi] then + minetest.get_meta(p):from_table(nil) + end end end end + vi = vi + 1 end - vi = vi + 1 - end - end - end - - vm:set_data(data) - vm:write_to_map() - vm:update_liquids() - if vm.close ~= nil then - vm:close() - end + end + end + end) -- call check_single_for_falling for everything within 1.5x blast radius for y = -radius * 1.5, radius * 1.5 do