forked from minetest/minetest_game
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:
parent
098ea0d10a
commit
12c763a6c7
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user