TNT: Add explode_center flag
[oweals/minetest_game.git] / mods / tnt / init.lua
index f9bc307c50b1f72600f629d5b99cc4a4cd829997..f3e22fdb0d7cb42f8789962bcd9c9d6609186a42 100644 (file)
@@ -1,7 +1,7 @@
 tnt = {}
 
 -- Default to enabled when in singleplayer
-local enable_tnt = minetest.setting_getbool("enable_tnt")
+local enable_tnt = minetest.settings:get_bool("enable_tnt")
 if enable_tnt == nil then
        enable_tnt = minetest.is_singleplayer()
 end
@@ -12,7 +12,7 @@ local loss_prob = {}
 loss_prob["default:cobble"] = 3
 loss_prob["default:dirt"] = 4
 
-local tnt_radius = tonumber(minetest.setting_get("tnt_radius") or 3)
+local tnt_radius = tonumber(minetest.settings:get("tnt_radius") or 3)
 
 -- Fill a list with data for content IDs, after all nodes are registered
 local cid_data = {}
@@ -83,8 +83,11 @@ local function add_drop(drops, item)
        end
 end
 
-local function destroy(drops, npos, cid, c_air, c_fire, on_blast_queue, ignore_protection, ignore_on_blast)
-       if not ignore_protection and minetest.is_protected(npos, "") then
+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, owner)
+       if not ignore_protection and minetest.is_protected(npos, owner) then
                return cid
        end
 
@@ -93,9 +96,16 @@ local function destroy(drops, npos, cid, c_air, c_fire, on_blast_queue, ignore_p
        if not def then
                return c_air
        elseif not ignore_on_blast and def.on_blast then
-               on_blast_queue[#on_blast_queue + 1] = {pos = vector.new(npos), on_blast = def.on_blast}
+               on_blast_queue[#on_blast_queue + 1] = {
+                       pos = vector.new(npos),
+                       on_blast = def.on_blast
+               }
                return cid
        elseif def.flammable then
+               on_construct_queue[#on_construct_queue + 1] = {
+                       fn = basic_flame_on_construct,
+                       pos = vector.new(npos)
+               }
                return c_fire
        else
                local node_drops = minetest.get_node_drops(def.name, "")
@@ -198,6 +208,7 @@ local function add_effects(pos, radius, drops)
                collisiondetection = false,
                vertical = false,
                texture = "tnt_boom.png",
+               glow = 15,
        })
        minetest.add_particlespawner({
                amount = 64,
@@ -250,17 +261,19 @@ end
 
 function tnt.burn(pos, nodename)
        local name = nodename or minetest.get_node(pos).name
-       local group = minetest.get_item_group(name, "tnt")
-       if group > 0 then
+       local def = minetest.registered_nodes[name]
+       if not def then
+               return
+       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)
-       elseif name == "tnt:gunpowder" then
-               minetest.set_node(pos, {name = "tnt:gunpowder_burning"})
        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, explode_center)
        pos = vector.round(pos)
        -- scan for adjacent TNT nodes first, and enlarge the explosion
        local vm1 = VoxelManip()
@@ -274,6 +287,10 @@ local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast)
        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")
+       -- make sure we still have explosion even when centre node isnt tnt related
+       if explode_center then
+               count = 1
+       end
 
        for z = pos.z - 2, pos.z + 2 do
        for y = pos.y - 2, pos.y + 2 do
@@ -306,6 +323,8 @@ local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast)
 
        local drops = {}
        local on_blast_queue = {}
+       local on_construct_queue = {}
+       basic_flame_on_construct = minetest.registered_nodes["fire:basic_flame"].on_construct
 
        local c_fire = minetest.get_content_id("fire:basic_flame")
        for z = -radius, radius do
@@ -318,8 +337,8 @@ local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast)
                        local p = {x = pos.x + x, y = pos.y + y, z = pos.z + z}
                        if cid ~= c_air then
                                data[vi] = destroy(drops, p, cid, c_air, c_fire,
-                                       on_blast_queue, ignore_protection,
-                                       ignore_on_blast)
+                                       on_blast_queue, on_construct_queue,
+                                       ignore_protection, ignore_on_blast, owner)
                        end
                end
                vi = vi + 1
@@ -357,14 +376,27 @@ local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast)
                end
        end
 
+       for _, queued_data in pairs(on_construct_queue) do
+               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)
-       minetest.sound_play("tnt_explode", {pos = pos, gain = 1.5, max_hear_distance = 2*64})
-       minetest.set_node(pos, {name = "tnt:boom"})
+       local meta = minetest.get_meta(pos)
+       local owner = meta:get_string("owner")
+       if not def.explode_center then
+               minetest.set_node(pos, {name = "tnt:boom"})
+       end
+       local sound = def.sound or "tnt_explode"
+       minetest.sound_play(sound, {pos = pos, gain = 1.5,
+                       max_hear_distance = math.min(def.radius * 20, 128)})
        local drops, radius = tnt_explode(pos, def.radius, def.ignore_protection,
-                       def.ignore_on_blast)
+                       def.ignore_on_blast, owner, def.explode_center)
        -- append entity drops
        local damage_radius = (radius / def.radius) * def.damage_radius
        entity_physics(pos, damage_radius, drops)
@@ -372,6 +404,8 @@ function tnt.boom(pos, def)
                eject_drops(drops, pos, radius)
        end
        add_effects(pos, radius, drops)
+       minetest.log("action", "A TNT explosion occurred at " .. minetest.pos_to_string(pos) ..
+               " with radius " .. radius)
 end
 
 minetest.register_node("tnt:boom", {
@@ -380,12 +414,6 @@ minetest.register_node("tnt:boom", {
        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,
        -- unaffected by explosions
        on_blast = function() end,
 })
@@ -397,7 +425,12 @@ minetest.register_node("tnt:gunpowder", {
        is_ground_content = false,
        sunlight_propagates = true,
        walkable = false,
-       tiles = {"tnt_gunpowder_straight.png", "tnt_gunpowder_curved.png", "tnt_gunpowder_t_junction.png", "tnt_gunpowder_crossing.png"},
+       tiles = {
+               "tnt_gunpowder_straight.png",
+               "tnt_gunpowder_curved.png",
+               "tnt_gunpowder_t_junction.png",
+               "tnt_gunpowder_crossing.png"
+       },
        inventory_image = "tnt_gunpowder_inventory.png",
        wield_image = "tnt_gunpowder_inventory.png",
        selection_box = {
@@ -411,6 +444,9 @@ minetest.register_node("tnt:gunpowder", {
        on_punch = function(pos, node, puncher)
                if puncher:get_wielded_item():get_name() == "default:torch" then
                        minetest.set_node(pos, {name = "tnt:gunpowder_burning"})
+                       minetest.log("action", puncher:get_player_name() ..
+                               " ignites tnt:gunpowder at " ..
+                               minetest.pos_to_string(pos))
                end
        end,
        on_blast = function(pos, intensity)
@@ -471,22 +507,26 @@ minetest.register_node("tnt:gunpowder_burning", {
                fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
        },
        drop = "",
-       groups = {dig_immediate = 2, attached_node = 1, connect_to_raillike = minetest.raillike_group("gunpowder")},
+       groups = {
+               dig_immediate = 2,
+               attached_node = 1,
+               connect_to_raillike = minetest.raillike_group("gunpowder")
+       },
        sounds = default.node_sound_leaves_defaults(),
        on_timer = function(pos, elapsed)
                for dx = -1, 1 do
                for dz = -1, 1 do
-               for dy = -1, 1 do
-                       if not (dx == 0 and dz == 0) then
-                               tnt.burn({
-                                       x = pos.x + dx,
-                                       y = pos.y + dy,
-                                       z = pos.z + dz,
-                               })
+                       if math.abs(dx) + math.abs(dz) == 1 then
+                               for dy = -1, 1 do
+                                       tnt.burn({
+                                               x = pos.x + dx,
+                                               y = pos.y + dy,
+                                               z = pos.z + dz,
+                                       })
+                               end
                        end
                end
                end
-               end
                minetest.remove_node(pos)
        end,
        -- unaffected by explosions
@@ -498,7 +538,7 @@ minetest.register_node("tnt:gunpowder_burning", {
 })
 
 minetest.register_craft({
-       output = "tnt:gunpowder",
+       output = "tnt:gunpowder 5",
        type = "shapeless",
        recipe = {"default:coal_lump", "default:gravel"}
 })
@@ -507,9 +547,9 @@ if enable_tnt then
        minetest.register_craft({
                output = "tnt:tnt",
                recipe = {
-                       {"",           "group:wood",    ""},
-                       {"group:wood", "tnt:gunpowder", "group:wood"},
-                       {"",           "group:wood",    ""}
+                       {"group:wood",    "tnt:gunpowder", "group:wood"},
+                       {"tnt:gunpowder", "tnt:gunpowder", "tnt:gunpowder"},
+                       {"group:wood",    "tnt:gunpowder", "group:wood"}
                }
        })
 
@@ -547,9 +587,19 @@ 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))
                                end
                        end,
                        on_blast = function(pos, intensity)
@@ -565,10 +615,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