1-- loss probabilities array (one in X will be lost) 2local loss_prob = {} 3 4loss_prob["default:cobble"] = 3 5loss_prob["default:dirt"] = 4 6 7local radius_max = tonumber(core.setting_get("tnt_radius_max") or 25) 8local time_max = tonumber(core.setting_get("tnt_time_max") or 3) 9 10local eject_drops = function(pos, stack) 11 local obj = core.add_item(pos, stack) 12 13 if obj == nil then 14 return 15 end 16 obj:get_luaentity().collect = true 17 obj:setacceleration({x=0, y=-10, z=0}) 18 obj:setvelocity({x=math.random(0,6)-3, y=10, z=math.random(0,6)-3}) 19end 20 21local add_drop = function(drops, pos, item) 22 if loss_prob[item] ~= nil then 23 if math.random(1,loss_prob[item]) == 1 then 24 return 25 end 26 end 27 28 if drops[item] == nil then 29 drops[item] = ItemStack(item) 30 else 31 drops[item]:add_item(item) 32 end 33 34 if drops[item]:get_free_space() == 0 then 35 stack = drops[item] 36 eject_drops(pos, stack) 37 drops[item] = nil 38 end 39end 40 41local function destroy(drops, pos, last, fast) 42 if core.is_protected(pos, "") then 43 return 44 end 45 46 local nodename = core.get_node(pos).name 47 if nodename ~= "air" then 48 core.remove_node(pos, (fast and 1 or 0)) 49 if last then 50 nodeupdate(pos) 51 end 52 if not core.registered_nodes[nodename] or not core.registered_nodes[nodename].groups then 53 -- broken map and unknown nodes 54 return 55 end 56 if core.registered_nodes[nodename].groups.flammable ~= nil then 57 core.set_node(pos, {name="fire:basic_flame"}, (fast and 2 or 0)) 58 return 59 end 60 local drop = core.get_node_drops(nodename, "") 61 for _,item in ipairs(drop) do 62 if type(item) == "string" then 63 add_drop(drops, pos, item) 64 else 65 for i=1,item:get_count() do 66 add_drop(drops, pos, item:get_name()) 67 end 68 end 69 end 70 end 71end 72 73boom = function(pos, time, force) 74 core.after(time, function(pos) 75 if not force and core.get_node(pos).name ~= "tnt:tnt_burning" then 76 return 77 end 78 core.sound_play("tnt_explode", {pos=pos, gain=1.5, max_hear_distance=2*64}) 79 core.set_node(pos, {name="tnt:boom"}, 2) 80 core.after(0.5, function(pos) 81 core.remove_node(pos, 2) 82 end, {x=pos.x, y=pos.y, z=pos.z}) 83 84 85 local radius = 2 86 local drops = {} 87 local list = {} 88 local dr = 0 89 local tnts = 1 90 local destroyed = 0 91 local melted = 0 92 local end_ms = os.clock() + time_max 93 local last = nil; 94 while dr<radius do 95 dr=dr+1 96 if os.clock() > end_ms or dr>=radius then last=1 end 97 for dx=-dr,dr,dr*2 do 98 for dy=-dr,dr,1 do 99 for dz=-dr,dr,1 do 100 table.insert(list, {x=dx, y=dy, z=dz}) 101 end 102 end 103 end 104 for dy=-dr,dr,dr*2 do 105 for dx=-dr+1,dr-1,1 do 106 for dz=-dr,dr,1 do 107 table.insert(list, {x=dx, y=dy, z=dz}) 108 end 109 end 110 end 111 for dz=-dr,dr,dr*2 do 112 for dx=-dr+1,dr-1,1 do 113 for dy=-dr+1,dr-1,1 do 114 table.insert(list, {x=dx, y=dy, z=dz}) 115 end 116 end 117 end 118 for _,p in ipairs(list) do 119 local np = {x=pos.x+p.x, y=pos.y+p.y, z=pos.z+p.z} 120 121 local node = core.get_node(np) 122 if node.name == "air" then 123 elseif node.name == "tnt:tnt" or node.name == "tnt:tnt_burning" then 124 if radius < radius_max and not last and dr < radius then 125 if radius <= 5 then 126 radius = radius + 1 127 elseif radius <= 10 then 128 radius = radius + 0.5 129 elseif radius <= 20 then 130 radius = radius + 0.3 131 else 132 radius = radius + 0.2 133 end 134 core.remove_node(np, 2) 135 tnts = tnts + 1 136 else 137 core.set_node(np, {name="tnt:tnt_burning"}, 2) 138 boom(np, 1) 139 end 140 elseif node.name == "fire:basic_flame" 141 --or string.find(node.name, "default:water_") 142 --or string.find(node.name, "default:lava_") 143 or node.name == "tnt:boom" 144 then 145 146 elseif last and radius > 10 and math.random(1,15) <= 1 then 147 melted = melted + core.freeze_melt(np, 1) 148 else 149 if math.abs(p.x)<2 and math.abs(p.y)<2 and math.abs(p.z)<2 then 150 destroy(drops, np, dr == radius, radius > 7) 151 destroyed = destroyed + 1 152 else 153 if math.random(1,5) <= 4 then 154 destroy(drops, np, dr == radius, radius > 7) 155 destroyed = destroyed + 1 156 end 157 end 158 end 159 end 160 if last then break end 161 end 162 163 local objects = core.get_objects_inside_radius(pos, radius*2) 164 for _,obj in ipairs(objects) do 165 --if obj:is_player() or (obj:get_luaentity() and obj:get_luaentity().name ~= "__builtin:item") then 166 local p = obj:getpos() 167 local v = obj:getvelocity() 168 local vec = {x=p.x-pos.x, y=p.y-pos.y, z=p.z-pos.z} 169 local dist = (vec.x^2+vec.y^2+vec.z^2)^0.5 170 local damage = ((radius*20)/dist) 171 --print("DMG dist="..dist.." damage="..damage) 172 if obj:is_player() or (obj:get_luaentity() and obj:get_luaentity().name ~= "__builtin:item") then 173 obj:punch(obj, 1.0, { 174 full_punch_interval=1.0, 175 damage_groups={fleshy=damage}, 176 }, vec) 177 end 178 if v ~= nil then 179 --obj:setvelocity({x=(p.x - pos.x) + (radius / 4) + v.x, y=(p.y - pos.y) + (radius / 2) + v.y, z=(p.z - pos.z) + (radius / 4) + v.z}) 180 obj:setvelocity({x=(p.x - pos.x) + (radius / 2) + v.x, y=(p.y - pos.y) + radius + v.y, z=(p.z - pos.z) + (radius / 2) + v.z}) 181 end 182 --end 183 end 184 185 core.log("action", "tnt:tnt : exploded=" .. tnts .. " radius=".. dr .." radius_want=" .. radius .. " destroyed="..destroyed .. " melted="..melted) 186 187 for _,stack in pairs(drops) do 188 eject_drops(pos, stack) 189 end 190 local radiusp = radius+1 191 core.add_particlespawner( 192 100, --amount 193 0.1, --time 194 {x=pos.x-radiusp, y=pos.y-radiusp, z=pos.z-radiusp}, --minpos 195 {x=pos.x+radiusp, y=pos.y+radiusp, z=pos.z+radiusp}, --maxpos 196 {x=-0, y=-0, z=-0}, --minvel 197 {x=0, y=0, z=0}, --maxvel 198 {x=-0.5,y=5,z=-0.5}, --minacc 199 {x=0.5,y=5,z=0.5}, --maxacc 200 0.1, --minexptime 201 1, --maxexptime 202 8, --minsize 203 15, --maxsize 204 false, --collisiondetection 205 "tnt_smoke.png" --texture 206 ) 207 end, pos) 208end 209 210core.register_node("tnt:tnt", { 211 description = "TNT", 212 tiles = {"tnt_top.png", "tnt_bottom.png", "tnt_side.png"}, 213 groups = {dig_immediate=2, mesecon=2}, 214 sounds = default.node_sound_wood_defaults(), 215 216 on_punch = function(pos, node, puncher) 217 if puncher:get_wielded_item():get_name() == "default:torch" then 218 core.sound_play("tnt_ignite", {pos=pos}) 219 core.set_node(pos, {name="tnt:tnt_burning"}) 220 boom(pos, 4) 221 elseif math.random(1, 200) <= 1 then 222 boom(pos, 0.1, 1) 223 end 224 end, 225 226 on_dig = function(pos, node, puncher) 227 if math.random(1,10) <= 1 then 228 boom(pos, 0.1, 1) 229 else 230 return core.node_dig(pos, node, puncher) 231 end 232 end, 233 234 mesecons = { 235 effector = { 236 action_on = function(pos, node) 237 core.set_node(pos, {name="tnt:tnt_burning"}) 238 boom(pos, 0) 239 end 240 }, 241 }, 242}) 243 244core.register_node("tnt:tnt_burning", { 245 tiles = {{name="tnt_top_burning_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=1}}, "tnt_bottom.png", "tnt_side.png"}, 246 light_source = 5, 247 drop = "", 248 sounds = default.node_sound_wood_defaults(), 249}) 250 251core.register_node("tnt:boom", { 252 drawtype = "plantlike", 253 tiles = {"tnt_boom.png"}, 254 light_source = LIGHT_MAX, 255 walkable = false, 256 drop = "", 257 groups = {dig_immediate=3}, 258}) 259 260burn = function(pos) 261 if core.get_node(pos).name == "tnt:tnt" then 262 core.sound_play("tnt_ignite", {pos=pos}) 263 core.set_node(pos, {name="tnt:tnt_burning"}) 264 boom(pos, 1) 265 return 266 end 267 if core.get_node(pos).name ~= "tnt:gunpowder" then 268 return 269 end 270 core.sound_play("tnt_gunpowder_burning", {pos=pos, gain=2}) 271 core.set_node(pos, {name="tnt:gunpowder_burning"}) 272 273 core.after(1, function(pos) 274 if core.get_node(pos).name ~= "tnt:gunpowder_burning" then 275 return 276 end 277 core.after(0.5, function(pos) 278 core.remove_node(pos) 279 end, {x=pos.x, y=pos.y, z=pos.z}) 280 for dx=-1,1 do 281 for dz=-1,1 do 282 for dy=-1,1 do 283 pos.x = pos.x+dx 284 pos.y = pos.y+dy 285 pos.z = pos.z+dz 286 287 if not (math.abs(dx) == 1 and math.abs(dz) == 1) then 288 if dy == 0 then 289 burn({x=pos.x, y=pos.y, z=pos.z}) 290 else 291 if math.abs(dx) == 1 or math.abs(dz) == 1 then 292 burn({x=pos.x, y=pos.y, z=pos.z}) 293 end 294 end 295 end 296 297 pos.x = pos.x-dx 298 pos.y = pos.y-dy 299 pos.z = pos.z-dz 300 end 301 end 302 end 303 end, pos) 304end 305 306core.register_node("tnt:gunpowder", { 307 description = "Gun Powder", 308 drawtype = "raillike", 309 paramtype = "light", 310 sunlight_propagates = true, 311 walkable = false, 312 tiles = {"tnt_gunpowder.png",}, 313 inventory_image = "tnt_gunpowder_inventory.png", 314 wield_image = "tnt_gunpowder_inventory.png", 315 selection_box = { 316 type = "fixed", 317 fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, 318 }, 319 groups = {dig_immediate=2,attached_node=1}, 320 sounds = default.node_sound_leaves_defaults(), 321 322 on_punch = function(pos, node, puncher) 323 if puncher:get_wielded_item():get_name() == "default:torch" then 324 burn(pos) 325 end 326 end, 327}) 328 329core.register_node("tnt:gunpowder_burning", { 330 drawtype = "raillike", 331 paramtype = "light", 332 sunlight_propagates = true, 333 walkable = false, 334 light_source = 5, 335 tiles = {{name="tnt_gunpowder_burning_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=1}}}, 336 selection_box = { 337 type = "fixed", 338 fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, 339 }, 340 drop = "", 341 groups = {dig_immediate=2,attached_node=1}, 342 sounds = default.node_sound_leaves_defaults(), 343}) 344 345core.register_abm({ 346 nodenames = {"tnt:tnt", "tnt:gunpowder"}, 347 neighbors = {"fire:basic_flame", "default:lava_source", "default:lava_flowing"}, 348 interval = 2, 349 chance = 10, 350 action = function(pos, node) 351 if node.name == "tnt:tnt" then 352 core.set_node(pos, {name="tnt:tnt_burning"}) 353 boom({x=pos.x, y=pos.y, z=pos.z}, 0) 354 else 355 burn(pos) 356 end 357 end 358}) 359 360core.register_craft({ 361 output = "tnt:gunpowder", 362 type = "shapeless", 363 recipe = {"default:coal_lump", "default:gravel"} 364}) 365 366core.register_craft({ 367 output = "tnt:tnt", 368 recipe = { 369 {"", "group:wood", ""}, 370 {"group:wood", "tnt:gunpowder", "group:wood"}, 371 {"", "group:wood", ""} 372 } 373}) 374 375if core.setting_get("log_mods") then 376 core.log("action", "tnt loaded") 377end 378