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.
This commit is contained in:
Auke Kok 2016-04-27 00:26:18 -07:00 committed by paramat
parent 098ea0d10a
commit 12c763a6c7

View File

@ -49,9 +49,8 @@ local function eject_drops(drops, pos, radius)
local drop_pos = vector.new(pos) local drop_pos = vector.new(pos)
for _, item in pairs(drops) do for _, item in pairs(drops) do
local count = item:get_count() local count = item:get_count()
local take_est = math.log(count * count) + math.random(0,4) - 2
while count > 0 do 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_count(),
item:get_stack_max())) item:get_stack_max()))
rand_pos(pos, drop_pos, radius) rand_pos(pos, drop_pos, radius)
@ -188,6 +187,16 @@ local function entity_physics(pos, radius, drops)
end end
local function add_effects(pos, radius, drops) 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({ minetest.add_particlespawner({
amount = 64, amount = 64,
time = 0.5, time = 0.5,
@ -199,8 +208,8 @@ local function add_effects(pos, radius, drops)
maxacc = vector.new(), maxacc = vector.new(),
minexptime = 1, minexptime = 1,
maxexptime = 2.5, maxexptime = 2.5,
minsize = 8, minsize = radius * 3,
maxsize = 16, maxsize = radius * 5,
texture = "tnt_smoke.png", texture = "tnt_smoke.png",
}) })
@ -230,8 +239,8 @@ local function add_effects(pos, radius, drops)
maxacc = {x = 0, y = -10, z = 0}, maxacc = {x = 0, y = -10, z = 0},
minexptime = 0.8, minexptime = 0.8,
maxexptime = 2.0, maxexptime = 2.0,
minsize = 2, minsize = radius * 0.66,
maxsize = 6, maxsize = radius * 2,
texture = texture, texture = texture,
collisiondetection = true, collisiondetection = true,
}) })
@ -251,6 +260,40 @@ end
local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast) local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast)
local pos = vector.round(pos) 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 vm = VoxelManip()
local pr = PseudoRandom(os.time()) local pr = PseudoRandom(os.time())
local p1 = vector.subtract(pos, radius) local p1 = vector.subtract(pos, radius)
@ -262,7 +305,6 @@ local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast)
local drops = {} local drops = {}
local on_blast_queue = {} local on_blast_queue = {}
local c_air = minetest.get_content_id("air")
local c_fire = minetest.get_content_id("fire:basic_flame") local c_fire = minetest.get_content_id("fire:basic_flame")
for z = -radius, radius do for z = -radius, radius do
for y = -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
end end
return drops return drops, radius
end end
function tnt.boom(pos, def) function tnt.boom(pos, def)
minetest.sound_play("tnt_explode", {pos = pos, gain = 1.5, max_hear_distance = 2*64}) minetest.sound_play("tnt_explode", {pos = pos, gain = 1.5, max_hear_distance = 2*64})
minetest.set_node(pos, {name = "tnt:boom"}) 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) def.ignore_on_blast)
-- append entity drops -- 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 if not def.disable_drops then
eject_drops(drops, pos, def.radius) eject_drops(drops, pos, radius)
end end
add_effects(pos, def.radius, drops) add_effects(pos, radius, drops)
end end
minetest.register_node("tnt:boom", { minetest.register_node("tnt:boom", {
@ -336,17 +378,6 @@ minetest.register_node("tnt:boom", {
drop = "", drop = "",
groups = {dig_immediate = 3}, groups = {dig_immediate = 3},
on_construct = function(pos) 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) minetest.get_node_timer(pos):start(0.4)
end, end,
on_timer = function(pos, elapsed) on_timer = function(pos, elapsed)