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,
- item:get_count(),
+ local take = math.max(1,math.min(radius * radius,
+ count,
item:get_stack_max()))
rand_pos(pos, drop_pos, radius)
local dropitem = ItemStack(item)
return vel
end
-local function entity_physics(pos, radius)
+local function entity_physics(pos, radius, drops)
local objs = minetest.get_objects_inside_radius(pos, radius)
for _, obj in pairs(objs) do
local obj_pos = obj:getpos()
obj:set_hp(obj:get_hp() - damage)
else
- local obj_vel = obj:getvelocity()
- obj:setvelocity(calc_velocity(pos, obj_pos,
- obj_vel, radius * 10))
- obj:punch(obj, 1.0, {
- full_punch_interval = 1.0,
- damage_groups = {fleshy = damage},
- }, nil)
+ local do_damage = true
+ local do_knockback = true
+ local entity_drops = {}
+ local luaobj = obj:get_luaentity()
+ local objdef = minetest.registered_entities[luaobj.name]
+
+ if objdef and objdef.on_blast then
+ do_damage, do_knockback, entity_drops = objdef.on_blast(luaobj, damage)
+ end
+
+ if do_knockback then
+ local obj_vel = obj:getvelocity()
+ obj:setvelocity(calc_velocity(pos, obj_pos,
+ obj_vel, radius * 10))
+ end
+ if do_damage then
+ if not obj:get_armor_groups().immortal then
+ obj:punch(obj, 1.0, {
+ full_punch_interval = 1.0,
+ damage_groups = {fleshy = damage},
+ }, nil)
+ end
+ end
+ for _, item in ipairs(entity_drops) do
+ add_drop(drops, item)
+ end
end
end
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 = 128,
- time = 1,
+ amount = 64,
+ time = 0.5,
minpos = vector.subtract(pos, radius / 2),
maxpos = vector.add(pos, radius / 2),
- minvel = {x = -20, y = -20, z = -20},
- maxvel = {x = 20, y = 20, z = 20},
+ minvel = {x = -10, y = -10, z = -10},
+ maxvel = {x = 10, y = 10, z = 10},
minacc = vector.new(),
maxacc = vector.new(),
minexptime = 1,
- maxexptime = 3,
- minsize = 8,
- maxsize = 16,
+ maxexptime = 2.5,
+ minsize = radius * 3,
+ maxsize = radius * 5,
texture = "tnt_smoke.png",
})
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,
})
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)
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
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"})
- minetest.get_node_timer(pos):start(0.5)
- 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)
- entity_physics(pos, def.damage_radius)
+ -- append entity 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", {
- drawtype = "plantlike",
- tiles = {"tnt_boom.png"},
+ drawtype = "airlike",
light_source = default.LIGHT_MAX,
walkable = false,
drop = "",
groups = {dig_immediate = 3},
+ on_construct = function(pos)
+ minetest.get_node_timer(pos):start(0.4)
+ end,
on_timer = function(pos, elapsed)
minetest.remove_node(pos)
end,
light_source = 5,
drop = "",
sounds = default.node_sound_wood_defaults(),
+ groups = {falling_node = 1},
on_timer = function(pos, elapsed)
tnt.boom(pos, def)
end,
on_construct = function(pos)
minetest.sound_play("tnt_ignite", {pos = pos})
minetest.get_node_timer(pos):start(4)
+ nodeupdate(pos)
end,
})
end