From 12c763a6c76dee4b11307f366527866349a01b6b Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Wed, 27 Apr 2016 00:26:18 -0700 Subject: [PATCH] TNT: Combine adjacent TNT into the explosion This uses a vmanip to count adjacent tnt nodes and explodes them all at once, using an inverse square law to recalculate the radius. The maximum explosion becomes 125 nodes of tnt yielding a radius of 15 nodes, which does not break my machine and makes it return in under a second. This makes both bigger explosions and less stability issues. The drop code has been simplified and now drops at all times a reasonable amount of drops, never blanketing the area with drops, even at the larges explosion level. Particles are scaled up according to explosion size as well - a bigger explosion will show bigger particles. To scale the tnt:boom particle, we move it to the _effects() function. --- mods/tnt/init.lua | 79 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/mods/tnt/init.lua b/mods/tnt/init.lua index d3efd7b8..f939d612 100644 --- a/mods/tnt/init.lua +++ b/mods/tnt/init.lua @@ -49,9 +49,8 @@ local function eject_drops(drops, pos, radius) local drop_pos = vector.new(pos) for _, item in pairs(drops) do local count = item:get_count() - local take_est = math.log(count * count) + math.random(0,4) - 2 while count > 0 do - local take = math.max(1,math.min(take_est, + local take = math.max(1,math.min(radius * radius, item:get_count(), item:get_stack_max())) rand_pos(pos, drop_pos, radius) @@ -188,6 +187,16 @@ local function entity_physics(pos, radius, drops) end local function add_effects(pos, radius, drops) + minetest.add_particle({ + pos = pos, + velocity = vector.new(), + acceleration = vector.new(), + expirationtime = 0.4, + size = radius * 10, + collisiondetection = false, + vertical = false, + texture = "tnt_boom.png", + }) minetest.add_particlespawner({ amount = 64, time = 0.5, @@ -199,8 +208,8 @@ local function add_effects(pos, radius, drops) maxacc = vector.new(), minexptime = 1, maxexptime = 2.5, - minsize = 8, - maxsize = 16, + minsize = radius * 3, + maxsize = radius * 5, texture = "tnt_smoke.png", }) @@ -230,8 +239,8 @@ local function add_effects(pos, radius, drops) maxacc = {x = 0, y = -10, z = 0}, minexptime = 0.8, maxexptime = 2.0, - minsize = 2, - maxsize = 6, + minsize = radius * 0.66, + maxsize = radius * 2, texture = texture, collisiondetection = true, }) @@ -251,6 +260,40 @@ end local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast) local 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() + local count = 0 + local c_tnt = minetest.get_content_id("tnt:tnt") + local c_tnt_burning = minetest.get_content_id("tnt:tnt_burning") + local c_tnt_boom = minetest.get_content_id("tnt:boom") + local c_air = minetest.get_content_id("air") + + 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 + + vm1:set_data(data) + vm1:write_to_map() + + -- recalculate new radius + radius = math.floor(radius * math.pow(count, 1/3)) + + -- perform the explosion local vm = VoxelManip() local pr = PseudoRandom(os.time()) local p1 = vector.subtract(pos, radius) @@ -262,7 +305,6 @@ local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast) local drops = {} local on_blast_queue = {} - local c_air = minetest.get_content_id("air") local c_fire = minetest.get_content_id("fire:basic_flame") for z = -radius, radius do for y = -radius, radius do @@ -312,21 +354,21 @@ local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast) end end - return drops + return drops, radius end function tnt.boom(pos, def) minetest.sound_play("tnt_explode", {pos = pos, gain = 1.5, max_hear_distance = 2*64}) minetest.set_node(pos, {name = "tnt:boom"}) - local drops = tnt_explode(pos, def.radius, def.ignore_protection, + local drops, radius = tnt_explode(pos, def.radius, def.ignore_protection, def.ignore_on_blast) -- append entity drops - entity_physics(pos, def.damage_radius, drops) - + local damage_radius = (radius / def.radius) * def.damage_radius + entity_physics(pos, damage_radius, drops) if not def.disable_drops then - eject_drops(drops, pos, def.radius) + eject_drops(drops, pos, radius) end - add_effects(pos, def.radius, drops) + add_effects(pos, radius, drops) end minetest.register_node("tnt:boom", { @@ -336,17 +378,6 @@ minetest.register_node("tnt:boom", { drop = "", groups = {dig_immediate = 3}, on_construct = function(pos) - minetest.add_particle({ - pos = pos, - velocity = vector.new(), - acceleration = vector.new(), - expirationtime = 0.4, - size = 30, - collisiondetection = false, - vertical = false, - texture = "tnt_boom.png", - playername = nil, - }) minetest.get_node_timer(pos):start(0.4) end, on_timer = function(pos, elapsed)