init.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. tnt = {}
  2. -- Default to enabled when in singleplayer
  3. local enable_tnt = minetest.settings:get_bool("enable_tnt")
  4. if enable_tnt == nil then
  5. enable_tnt = minetest.is_singleplayer()
  6. end
  7. -- loss probabilities array (one in X will be lost)
  8. local loss_prob = {}
  9. loss_prob["default:cobble"] = 3
  10. loss_prob["default:dirt"] = 4
  11. local tnt_radius = tonumber(minetest.settings:get("tnt_radius") or 3)
  12. -- Fill a list with data for content IDs, after all nodes are registered
  13. local cid_data = {}
  14. minetest.after(0, function()
  15. for name, def in pairs(minetest.registered_nodes) do
  16. cid_data[minetest.get_content_id(name)] = {
  17. name = name,
  18. drops = def.drops,
  19. flammable = def.groups.flammable,
  20. on_blast = def.on_blast,
  21. }
  22. end
  23. end)
  24. local function rand_pos(center, pos, radius)
  25. local def
  26. local reg_nodes = minetest.registered_nodes
  27. local i = 0
  28. repeat
  29. -- Give up and use the center if this takes too long
  30. if i > 4 then
  31. pos.x, pos.z = center.x, center.z
  32. break
  33. end
  34. pos.x = center.x + math.random(-radius, radius)
  35. pos.z = center.z + math.random(-radius, radius)
  36. def = reg_nodes[minetest.get_node(pos).name]
  37. i = i + 1
  38. until def and not def.walkable
  39. end
  40. local function eject_drops(drops, pos, radius)
  41. local drop_pos = vector.new(pos)
  42. for _, item in pairs(drops) do
  43. local count = math.min(item:get_count(), item:get_stack_max())
  44. while count > 0 do
  45. local take = math.max(1,math.min(radius * radius,
  46. count,
  47. item:get_stack_max()))
  48. rand_pos(pos, drop_pos, radius)
  49. local dropitem = ItemStack(item)
  50. dropitem:set_count(take)
  51. local obj = minetest.add_item(drop_pos, dropitem)
  52. if obj then
  53. obj:get_luaentity().collect = true
  54. obj:setacceleration({x = 0, y = -10, z = 0})
  55. obj:setvelocity({x = math.random(-3, 3),
  56. y = math.random(0, 10),
  57. z = math.random(-3, 3)})
  58. end
  59. count = count - take
  60. end
  61. end
  62. end
  63. local function add_drop(drops, item)
  64. item = ItemStack(item)
  65. local name = item:get_name()
  66. if loss_prob[name] ~= nil and math.random(1, loss_prob[name]) == 1 then
  67. return
  68. end
  69. local drop = drops[name]
  70. if drop == nil then
  71. drops[name] = item
  72. else
  73. drop:set_count(drop:get_count() + item:get_count())
  74. end
  75. end
  76. local basic_flame_on_construct -- cached value
  77. local function destroy(drops, npos, cid, c_air, c_fire,
  78. on_blast_queue, on_construct_queue,
  79. ignore_protection, ignore_on_blast, owner)
  80. if not ignore_protection and minetest.is_protected(npos, owner) then
  81. return cid
  82. end
  83. local def = cid_data[cid]
  84. if not def then
  85. return c_air
  86. elseif not ignore_on_blast and def.on_blast then
  87. on_blast_queue[#on_blast_queue + 1] = {
  88. pos = vector.new(npos),
  89. on_blast = def.on_blast
  90. }
  91. return cid
  92. elseif def.flammable then
  93. on_construct_queue[#on_construct_queue + 1] = {
  94. fn = basic_flame_on_construct,
  95. pos = vector.new(npos)
  96. }
  97. return c_fire
  98. else
  99. local node_drops = minetest.get_node_drops(def.name, "")
  100. for _, item in pairs(node_drops) do
  101. add_drop(drops, item)
  102. end
  103. return c_air
  104. end
  105. end
  106. local function calc_velocity(pos1, pos2, old_vel, power)
  107. -- Avoid errors caused by a vector of zero length
  108. if vector.equals(pos1, pos2) then
  109. return old_vel
  110. end
  111. local vel = vector.direction(pos1, pos2)
  112. vel = vector.normalize(vel)
  113. vel = vector.multiply(vel, power)
  114. -- Divide by distance
  115. local dist = vector.distance(pos1, pos2)
  116. dist = math.max(dist, 1)
  117. vel = vector.divide(vel, dist)
  118. -- Add old velocity
  119. vel = vector.add(vel, old_vel)
  120. -- randomize it a bit
  121. vel = vector.add(vel, {
  122. x = math.random() - 0.5,
  123. y = math.random() - 0.5,
  124. z = math.random() - 0.5,
  125. })
  126. -- Limit to terminal velocity
  127. dist = vector.length(vel)
  128. if dist > 250 then
  129. vel = vector.divide(vel, dist / 250)
  130. end
  131. return vel
  132. end
  133. local function entity_physics(pos, radius, drops)
  134. local objs = minetest.get_objects_inside_radius(pos, radius)
  135. for _, obj in pairs(objs) do
  136. local obj_pos = obj:getpos()
  137. local dist = math.max(1, vector.distance(pos, obj_pos))
  138. local damage = (4 / dist) * radius
  139. if obj:is_player() then
  140. -- currently the engine has no method to set
  141. -- player velocity. See #2960
  142. -- instead, we knock the player back 1.0 node, and slightly upwards
  143. local dir = vector.normalize(vector.subtract(obj_pos, pos))
  144. local moveoff = vector.multiply(dir, dist + 1.0)
  145. local newpos = vector.add(pos, moveoff)
  146. newpos = vector.add(newpos, {x = 0, y = 0.2, z = 0})
  147. obj:setpos(newpos)
  148. obj:set_hp(obj:get_hp() - damage)
  149. else
  150. local do_damage = true
  151. local do_knockback = true
  152. local entity_drops = {}
  153. local luaobj = obj:get_luaentity()
  154. local objdef = minetest.registered_entities[luaobj.name]
  155. if objdef and objdef.on_blast then
  156. do_damage, do_knockback, entity_drops = objdef.on_blast(luaobj, damage)
  157. end
  158. if do_knockback then
  159. local obj_vel = obj:getvelocity()
  160. obj:setvelocity(calc_velocity(pos, obj_pos,
  161. obj_vel, radius * 10))
  162. end
  163. if do_damage then
  164. if not obj:get_armor_groups().immortal then
  165. obj:punch(obj, 1.0, {
  166. full_punch_interval = 1.0,
  167. damage_groups = {fleshy = damage},
  168. }, nil)
  169. end
  170. end
  171. for _, item in pairs(entity_drops) do
  172. add_drop(drops, item)
  173. end
  174. end
  175. end
  176. end
  177. local function add_effects(pos, radius, drops)
  178. minetest.add_particle({
  179. pos = pos,
  180. velocity = vector.new(),
  181. acceleration = vector.new(),
  182. expirationtime = 0.4,
  183. size = radius * 10,
  184. collisiondetection = false,
  185. vertical = false,
  186. texture = "tnt_boom.png",
  187. })
  188. minetest.add_particlespawner({
  189. amount = 64,
  190. time = 0.5,
  191. minpos = vector.subtract(pos, radius / 2),
  192. maxpos = vector.add(pos, radius / 2),
  193. minvel = {x = -10, y = -10, z = -10},
  194. maxvel = {x = 10, y = 10, z = 10},
  195. minacc = vector.new(),
  196. maxacc = vector.new(),
  197. minexptime = 1,
  198. maxexptime = 2.5,
  199. minsize = radius * 3,
  200. maxsize = radius * 5,
  201. texture = "tnt_smoke.png",
  202. })
  203. -- we just dropped some items. Look at the items entities and pick
  204. -- one of them to use as texture
  205. local texture = "tnt_blast.png" --fallback texture
  206. local most = 0
  207. for name, stack in pairs(drops) do
  208. local count = stack:get_count()
  209. if count > most then
  210. most = count
  211. local def = minetest.registered_nodes[name]
  212. if def and def.tiles and def.tiles[1] then
  213. texture = def.tiles[1]
  214. end
  215. end
  216. end
  217. minetest.add_particlespawner({
  218. amount = 64,
  219. time = 0.1,
  220. minpos = vector.subtract(pos, radius / 2),
  221. maxpos = vector.add(pos, radius / 2),
  222. minvel = {x = -3, y = 0, z = -3},
  223. maxvel = {x = 3, y = 5, z = 3},
  224. minacc = {x = 0, y = -10, z = 0},
  225. maxacc = {x = 0, y = -10, z = 0},
  226. minexptime = 0.8,
  227. maxexptime = 2.0,
  228. minsize = radius * 0.66,
  229. maxsize = radius * 2,
  230. texture = texture,
  231. collisiondetection = true,
  232. })
  233. end
  234. function tnt.burn(pos, nodename)
  235. local name = nodename or minetest.get_node(pos).name
  236. local def = minetest.registered_nodes[name]
  237. if not def then
  238. return
  239. elseif def.on_ignite then
  240. def.on_ignite(pos)
  241. elseif minetest.get_item_group(name, "tnt") > 0 then
  242. minetest.swap_node(pos, {name = name .. "_burning"})
  243. minetest.sound_play("tnt_ignite", {pos = pos})
  244. minetest.get_node_timer(pos):start(1)
  245. end
  246. end
  247. local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast, owner)
  248. pos = vector.round(pos)
  249. -- scan for adjacent TNT nodes first, and enlarge the explosion
  250. local vm1 = VoxelManip()
  251. local p1 = vector.subtract(pos, 2)
  252. local p2 = vector.add(pos, 2)
  253. local minp, maxp = vm1:read_from_map(p1, p2)
  254. local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
  255. local data = vm1:get_data()
  256. local count = 0
  257. local c_tnt = minetest.get_content_id("tnt:tnt")
  258. local c_tnt_burning = minetest.get_content_id("tnt:tnt_burning")
  259. local c_tnt_boom = minetest.get_content_id("tnt:boom")
  260. local c_air = minetest.get_content_id("air")
  261. for z = pos.z - 2, pos.z + 2 do
  262. for y = pos.y - 2, pos.y + 2 do
  263. local vi = a:index(pos.x - 2, y, z)
  264. for x = pos.x - 2, pos.x + 2 do
  265. local cid = data[vi]
  266. if cid == c_tnt or cid == c_tnt_boom or cid == c_tnt_burning then
  267. count = count + 1
  268. data[vi] = c_air
  269. end
  270. vi = vi + 1
  271. end
  272. end
  273. end
  274. vm1:set_data(data)
  275. vm1:write_to_map()
  276. -- recalculate new radius
  277. radius = math.floor(radius * math.pow(count, 1/3))
  278. -- perform the explosion
  279. local vm = VoxelManip()
  280. local pr = PseudoRandom(os.time())
  281. p1 = vector.subtract(pos, radius)
  282. p2 = vector.add(pos, radius)
  283. minp, maxp = vm:read_from_map(p1, p2)
  284. a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
  285. data = vm:get_data()
  286. local drops = {}
  287. local on_blast_queue = {}
  288. local on_construct_queue = {}
  289. basic_flame_on_construct = minetest.registered_nodes["fire:basic_flame"].on_construct
  290. local c_fire = minetest.get_content_id("fire:basic_flame")
  291. for z = -radius, radius do
  292. for y = -radius, radius do
  293. local vi = a:index(pos.x + (-radius), pos.y + y, pos.z + z)
  294. for x = -radius, radius do
  295. local r = vector.length(vector.new(x, y, z))
  296. if (radius * radius) / (r * r) >= (pr:next(80, 125) / 100) then
  297. local cid = data[vi]
  298. local p = {x = pos.x + x, y = pos.y + y, z = pos.z + z}
  299. if cid ~= c_air then
  300. data[vi] = destroy(drops, p, cid, c_air, c_fire,
  301. on_blast_queue, on_construct_queue,
  302. ignore_protection, ignore_on_blast, owner)
  303. end
  304. end
  305. vi = vi + 1
  306. end
  307. end
  308. end
  309. vm:set_data(data)
  310. vm:write_to_map()
  311. vm:update_map()
  312. vm:update_liquids()
  313. -- call check_single_for_falling for everything within 1.5x blast radius
  314. for y = -radius * 1.5, radius * 1.5 do
  315. for z = -radius * 1.5, radius * 1.5 do
  316. for x = -radius * 1.5, radius * 1.5 do
  317. local rad = {x = x, y = y, z = z}
  318. local s = vector.add(pos, rad)
  319. local r = vector.length(rad)
  320. if r / radius < 1.4 then
  321. minetest.check_single_for_falling(s)
  322. end
  323. end
  324. end
  325. end
  326. for _, queued_data in pairs(on_blast_queue) do
  327. local dist = math.max(1, vector.distance(queued_data.pos, pos))
  328. local intensity = (radius * radius) / (dist * dist)
  329. local node_drops = queued_data.on_blast(queued_data.pos, intensity)
  330. if node_drops then
  331. for _, item in pairs(node_drops) do
  332. add_drop(drops, item)
  333. end
  334. end
  335. end
  336. for _, queued_data in pairs(on_construct_queue) do
  337. queued_data.fn(queued_data.pos)
  338. end
  339. minetest.log("action", "TNT owned by " .. owner .. " detonated at " ..
  340. minetest.pos_to_string(pos) .. " with radius " .. radius)
  341. return drops, radius
  342. end
  343. function tnt.boom(pos, def)
  344. local meta = minetest.get_meta(pos)
  345. local owner = meta:get_string("owner")
  346. minetest.sound_play("tnt_explode", {pos = pos, gain = 1.5, max_hear_distance = 2*64})
  347. minetest.set_node(pos, {name = "tnt:boom"})
  348. local drops, radius = tnt_explode(pos, def.radius, def.ignore_protection,
  349. def.ignore_on_blast, owner)
  350. -- append entity drops
  351. local damage_radius = (radius / def.radius) * def.damage_radius
  352. entity_physics(pos, damage_radius, drops)
  353. if not def.disable_drops then
  354. eject_drops(drops, pos, radius)
  355. end
  356. add_effects(pos, radius, drops)
  357. minetest.log("action", "A TNT explosion occurred at " .. minetest.pos_to_string(pos) ..
  358. " with radius " .. radius)
  359. end
  360. minetest.register_node("tnt:boom", {
  361. drawtype = "airlike",
  362. light_source = default.LIGHT_MAX,
  363. walkable = false,
  364. drop = "",
  365. groups = {dig_immediate = 3},
  366. on_construct = function(pos)
  367. minetest.get_node_timer(pos):start(0.4)
  368. end,
  369. on_timer = function(pos, elapsed)
  370. minetest.remove_node(pos)
  371. end,
  372. -- unaffected by explosions
  373. on_blast = function() end,
  374. })
  375. minetest.register_node("tnt:gunpowder", {
  376. description = "Gun Powder",
  377. drawtype = "raillike",
  378. paramtype = "light",
  379. is_ground_content = false,
  380. sunlight_propagates = true,
  381. walkable = false,
  382. tiles = {
  383. "tnt_gunpowder_straight.png",
  384. "tnt_gunpowder_curved.png",
  385. "tnt_gunpowder_t_junction.png",
  386. "tnt_gunpowder_crossing.png"
  387. },
  388. inventory_image = "tnt_gunpowder_inventory.png",
  389. wield_image = "tnt_gunpowder_inventory.png",
  390. selection_box = {
  391. type = "fixed",
  392. fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
  393. },
  394. groups = {dig_immediate = 2, attached_node = 1, flammable = 5,
  395. connect_to_raillike = minetest.raillike_group("gunpowder")},
  396. sounds = default.node_sound_leaves_defaults(),
  397. on_punch = function(pos, node, puncher)
  398. if puncher:get_wielded_item():get_name() == "default:torch" then
  399. minetest.set_node(pos, {name = "tnt:gunpowder_burning"})
  400. minetest.log("action", puncher:get_player_name() ..
  401. " ignites tnt:gunpowder at " ..
  402. minetest.pos_to_string(pos))
  403. end
  404. end,
  405. on_blast = function(pos, intensity)
  406. minetest.set_node(pos, {name = "tnt:gunpowder_burning"})
  407. end,
  408. on_burn = function(pos)
  409. minetest.set_node(pos, {name = "tnt:gunpowder_burning"})
  410. end,
  411. on_ignite = function(pos, igniter)
  412. minetest.set_node(pos, {name = "tnt:gunpowder_burning"})
  413. end,
  414. })
  415. minetest.register_node("tnt:gunpowder_burning", {
  416. drawtype = "raillike",
  417. paramtype = "light",
  418. sunlight_propagates = true,
  419. walkable = false,
  420. light_source = 5,
  421. tiles = {{
  422. name = "tnt_gunpowder_burning_straight_animated.png",
  423. animation = {
  424. type = "vertical_frames",
  425. aspect_w = 16,
  426. aspect_h = 16,
  427. length = 1,
  428. }
  429. },
  430. {
  431. name = "tnt_gunpowder_burning_curved_animated.png",
  432. animation = {
  433. type = "vertical_frames",
  434. aspect_w = 16,
  435. aspect_h = 16,
  436. length = 1,
  437. }
  438. },
  439. {
  440. name = "tnt_gunpowder_burning_t_junction_animated.png",
  441. animation = {
  442. type = "vertical_frames",
  443. aspect_w = 16,
  444. aspect_h = 16,
  445. length = 1,
  446. }
  447. },
  448. {
  449. name = "tnt_gunpowder_burning_crossing_animated.png",
  450. animation = {
  451. type = "vertical_frames",
  452. aspect_w = 16,
  453. aspect_h = 16,
  454. length = 1,
  455. }
  456. }},
  457. selection_box = {
  458. type = "fixed",
  459. fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
  460. },
  461. drop = "",
  462. groups = {
  463. dig_immediate = 2,
  464. attached_node = 1,
  465. connect_to_raillike = minetest.raillike_group("gunpowder")
  466. },
  467. sounds = default.node_sound_leaves_defaults(),
  468. on_timer = function(pos, elapsed)
  469. for dx = -1, 1 do
  470. for dz = -1, 1 do
  471. if math.abs(dx) + math.abs(dz) == 1 then
  472. for dy = -1, 1 do
  473. tnt.burn({
  474. x = pos.x + dx,
  475. y = pos.y + dy,
  476. z = pos.z + dz,
  477. })
  478. end
  479. end
  480. end
  481. end
  482. minetest.remove_node(pos)
  483. end,
  484. -- unaffected by explosions
  485. on_blast = function() end,
  486. on_construct = function(pos)
  487. minetest.sound_play("tnt_gunpowder_burning", {pos = pos, gain = 2})
  488. minetest.get_node_timer(pos):start(1)
  489. end,
  490. })
  491. minetest.register_craft({
  492. output = "tnt:gunpowder 5",
  493. type = "shapeless",
  494. recipe = {"default:coal_lump", "default:gravel"}
  495. })
  496. if enable_tnt then
  497. minetest.register_craft({
  498. output = "tnt:tnt",
  499. recipe = {
  500. {"group:wood", "tnt:gunpowder", "group:wood"},
  501. {"tnt:gunpowder", "tnt:gunpowder", "tnt:gunpowder"},
  502. {"group:wood", "tnt:gunpowder", "group:wood"}
  503. }
  504. })
  505. minetest.register_abm({
  506. label = "TNT ignition",
  507. nodenames = {"group:tnt", "tnt:gunpowder"},
  508. neighbors = {"fire:basic_flame", "default:lava_source", "default:lava_flowing"},
  509. interval = 4,
  510. chance = 1,
  511. action = function(pos, node)
  512. tnt.burn(pos, node.name)
  513. end,
  514. })
  515. end
  516. function tnt.register_tnt(def)
  517. local name
  518. if not def.name:find(':') then
  519. name = "tnt:" .. def.name
  520. else
  521. name = def.name
  522. def.name = def.name:match(":([%w_]+)")
  523. end
  524. if not def.tiles then def.tiles = {} end
  525. local tnt_top = def.tiles.top or def.name .. "_top.png"
  526. local tnt_bottom = def.tiles.bottom or def.name .. "_bottom.png"
  527. local tnt_side = def.tiles.side or def.name .. "_side.png"
  528. local tnt_burning = def.tiles.burning or def.name .. "_top_burning_animated.png"
  529. if not def.damage_radius then def.damage_radius = def.radius * 2 end
  530. if enable_tnt then
  531. minetest.register_node(":" .. name, {
  532. description = def.description,
  533. tiles = {tnt_top, tnt_bottom, tnt_side},
  534. is_ground_content = false,
  535. groups = {dig_immediate = 2, mesecon = 2, tnt = 1, flammable = 5},
  536. sounds = default.node_sound_wood_defaults(),
  537. after_place_node = function(pos, placer)
  538. if placer:is_player() then
  539. local meta = minetest.get_meta(pos)
  540. meta:set_string("owner", placer:get_player_name())
  541. end
  542. end,
  543. on_punch = function(pos, node, puncher)
  544. if puncher:get_wielded_item():get_name() == "default:torch" then
  545. minetest.swap_node(pos, {name = name .. "_burning"})
  546. minetest.registered_nodes[name .. "_burning"].on_construct(pos)
  547. minetest.log("action", puncher:get_player_name() ..
  548. " ignites " .. node.name .. " at " ..
  549. minetest.pos_to_string(pos))
  550. end
  551. end,
  552. on_blast = function(pos, intensity)
  553. minetest.after(0.1, function()
  554. tnt.boom(pos, def)
  555. end)
  556. end,
  557. mesecons = {effector =
  558. {action_on =
  559. function(pos)
  560. tnt.boom(pos, def)
  561. end
  562. }
  563. },
  564. on_burn = function(pos)
  565. minetest.swap_node(pos, {name = name .. "_burning"})
  566. minetest.registered_nodes[name .. "_burning"].on_construct(pos)
  567. end,
  568. on_ignite = function(pos, igniter)
  569. minetest.swap_node(pos, {name = name .. "_burning"})
  570. minetest.registered_nodes[name .. "_burning"].on_construct(pos)
  571. end,
  572. })
  573. end
  574. minetest.register_node(":" .. name .. "_burning", {
  575. tiles = {
  576. {
  577. name = tnt_burning,
  578. animation = {
  579. type = "vertical_frames",
  580. aspect_w = 16,
  581. aspect_h = 16,
  582. length = 1,
  583. }
  584. },
  585. tnt_bottom, tnt_side
  586. },
  587. light_source = 5,
  588. drop = "",
  589. sounds = default.node_sound_wood_defaults(),
  590. groups = {falling_node = 1},
  591. on_timer = function(pos, elapsed)
  592. tnt.boom(pos, def)
  593. end,
  594. -- unaffected by explosions
  595. on_blast = function() end,
  596. on_construct = function(pos)
  597. minetest.sound_play("tnt_ignite", {pos = pos})
  598. minetest.get_node_timer(pos):start(4)
  599. minetest.check_for_falling(pos)
  600. end,
  601. })
  602. end
  603. tnt.register_tnt({
  604. name = "tnt:tnt",
  605. description = "TNT",
  606. radius = tnt_radius,
  607. })