TNT: Track TNT owner in metadata for protection mods

It is useful for protection mods to know who owns an exploding
TNT block. This allows the blocks destroyed by the TNT to be
limited to the same ones the owner could destroy without using
TNT.

TNT placed within a protected area by the area owner, and later
ignited by another player will destroy within the protected area
nodes the igniter may not otherwise be able to interact with. Any
player could significantly increase the size of an explosion by
placing more TNT in an adjacent unprotected area if the original
TNT block was placed withing 1 node of such a boundary. This
feature sounds dangerous, but we are talking about TNT. Players
should use it carefully.
This commit is contained in:
Foz 2016-05-06 21:47:22 -04:00 committed by paramat
parent b0d3bf0ecd
commit d035601c36

@ -86,8 +86,8 @@ end
local basic_flame_on_construct -- cached value local basic_flame_on_construct -- cached value
local function destroy(drops, npos, cid, c_air, c_fire, local function destroy(drops, npos, cid, c_air, c_fire,
on_blast_queue, on_construct_queue, on_blast_queue, on_construct_queue,
ignore_protection, ignore_on_blast) ignore_protection, ignore_on_blast, owner)
if not ignore_protection and minetest.is_protected(npos, "") then if not ignore_protection and minetest.is_protected(npos, owner) then
return cid return cid
end end
@ -266,13 +266,13 @@ function tnt.burn(pos, nodename)
elseif def.on_ignite then elseif def.on_ignite then
def.on_ignite(pos) def.on_ignite(pos)
elseif minetest.get_item_group(name, "tnt") > 0 then elseif minetest.get_item_group(name, "tnt") > 0 then
minetest.swap_node(pos, {name = name .. "_burning"})
minetest.sound_play("tnt_ignite", {pos = pos}) minetest.sound_play("tnt_ignite", {pos = pos})
minetest.set_node(pos, {name = name .. "_burning"})
minetest.get_node_timer(pos):start(1) minetest.get_node_timer(pos):start(1)
end end
end end
local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast) local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast, owner)
pos = vector.round(pos) pos = vector.round(pos)
-- scan for adjacent TNT nodes first, and enlarge the explosion -- scan for adjacent TNT nodes first, and enlarge the explosion
local vm1 = VoxelManip() local vm1 = VoxelManip()
@ -333,7 +333,7 @@ local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast)
if cid ~= c_air then if cid ~= c_air then
data[vi] = destroy(drops, p, cid, c_air, c_fire, data[vi] = destroy(drops, p, cid, c_air, c_fire,
on_blast_queue, on_construct_queue, on_blast_queue, on_construct_queue,
ignore_protection, ignore_on_blast) ignore_protection, ignore_on_blast, owner)
end end
end end
vi = vi + 1 vi = vi + 1
@ -375,14 +375,19 @@ local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast)
queued_data.fn(queued_data.pos) queued_data.fn(queued_data.pos)
end end
minetest.log("action", "TNT owned by " .. owner .. " detonated at " ..
minetest.pos_to_string(pos) .. " with radius " .. radius)
return drops, radius return drops, radius
end end
function tnt.boom(pos, def) function tnt.boom(pos, def)
local meta = minetest.get_meta(pos)
local owner = meta:get_string("owner")
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, radius = 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, owner)
-- append entity drops -- append entity drops
local damage_radius = (radius / def.radius) * def.damage_radius local damage_radius = (radius / def.radius) * def.damage_radius
entity_physics(pos, damage_radius, drops) entity_physics(pos, damage_radius, drops)
@ -579,9 +584,16 @@ function tnt.register_tnt(def)
is_ground_content = false, is_ground_content = false,
groups = {dig_immediate = 2, mesecon = 2, tnt = 1, flammable = 5}, groups = {dig_immediate = 2, mesecon = 2, tnt = 1, flammable = 5},
sounds = default.node_sound_wood_defaults(), sounds = default.node_sound_wood_defaults(),
after_place_node = function(pos, placer)
if placer:is_player() then
local meta = minetest.get_meta(pos)
meta:set_string("owner", placer:get_player_name())
end
end,
on_punch = function(pos, node, puncher) on_punch = function(pos, node, puncher)
if puncher:get_wielded_item():get_name() == "default:torch" then if puncher:get_wielded_item():get_name() == "default:torch" then
minetest.set_node(pos, {name = name .. "_burning"}) minetest.swap_node(pos, {name = name .. "_burning"})
minetest.registered_nodes[name .. "_burning"].on_construct(pos)
minetest.log("action", puncher:get_player_name() .. minetest.log("action", puncher:get_player_name() ..
" ignites " .. node.name .. " at " .. " ignites " .. node.name .. " at " ..
minetest.pos_to_string(pos)) minetest.pos_to_string(pos))
@ -600,10 +612,12 @@ function tnt.register_tnt(def)
} }
}, },
on_burn = function(pos) on_burn = function(pos)
minetest.set_node(pos, {name = name .. "_burning"}) minetest.swap_node(pos, {name = name .. "_burning"})
minetest.registered_nodes[name .. "_burning"].on_construct(pos)
end, end,
on_ignite = function(pos, igniter) on_ignite = function(pos, igniter)
minetest.set_node(pos, {name = name .. "_burning"}) minetest.swap_node(pos, {name = name .. "_burning"})
minetest.registered_nodes[name .. "_burning"].on_construct(pos)
end, end,
}) })
end end