TNT: Track TNT owner in metadata for protection mods
authorFoz <fozolo@gmail.com>
Sat, 7 May 2016 01:47:22 +0000 (21:47 -0400)
committerparamat <mat.gregory@virginmedia.com>
Sun, 2 Jul 2017 10:30:39 +0000 (11:30 +0100)
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.

mods/tnt/init.lua

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