init.lua 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. -- tnt/init.lua
  2. tnt = {}
  3. -- Load support for MT game translation.
  4. local S = minetest.get_translator('tnt')
  5. -- loss probabilities array (one in X will be lost)
  6. local loss_prob = {}
  7. loss_prob['default:cobble'] = 3
  8. loss_prob['default:dirt'] = 4
  9. local tnt_radius = tonumber(minetest.settings:get('tnt_radius') or 3)
  10. -- Fill a list with data for content IDs, after all nodes are registered
  11. local cid_data = {}
  12. minetest.after(0, function()
  13. for name, def in pairs(minetest.registered_nodes) do
  14. cid_data[minetest.get_content_id(name)] = {
  15. name = name,
  16. drops = def.drops,
  17. flammable = def.groups.flammable,
  18. on_blast = def.on_blast,
  19. }
  20. end
  21. end)
  22. local function rand_pos(center, pos, radius)
  23. local def
  24. local reg_nodes = minetest.registered_nodes
  25. local i = 0
  26. repeat
  27. -- Give up and use the center if this takes too long
  28. if i > 4 then
  29. pos.x, pos.z = center.x, center.z
  30. break
  31. end
  32. pos.x = center.x + math.random(-radius, radius)
  33. pos.z = center.z + math.random(-radius, radius)
  34. def = reg_nodes[minetest.get_node(pos).name]
  35. i = i + 1
  36. until def and not def.walkable
  37. end
  38. local function eject_drops(drops, pos, radius)
  39. local drop_pos = vector.new(pos)
  40. for _, item in pairs(drops) do
  41. local count = math.min(item:get_count(), item:get_stack_max())
  42. while count > 0 do
  43. local take = math.max(1,math.min(radius * radius,
  44. count,
  45. item:get_stack_max()))
  46. rand_pos(pos, drop_pos, radius)
  47. local dropitem = ItemStack(item)
  48. dropitem:set_count(take)
  49. local obj = minetest.add_item(drop_pos, dropitem)
  50. if obj then
  51. obj:get_luaentity().collect = true
  52. obj:set_acceleration({x = 0, y = -10, z = 0})
  53. obj:set_velocity({x = math.random(-3, 3),
  54. y = math.random(0, 10),
  55. z = math.random(-3, 3)})
  56. end
  57. count = count - take
  58. end
  59. end
  60. end
  61. local function add_drop(drops, item)
  62. item = ItemStack(item)
  63. local name = item:get_name()
  64. if loss_prob[name] ~= nil and math.random(1, loss_prob[name]) == 1 then
  65. return
  66. end
  67. local drop = drops[name]
  68. if drop == nil then
  69. drops[name] = item
  70. else
  71. drop:set_count(drop:get_count() + item:get_count())
  72. end
  73. end
  74. local basic_flame_on_construct -- cached value
  75. local function destroy(drops, npos, cid, c_air, c_fire,
  76. on_blast_queue, on_construct_queue,
  77. ignore_protection, ignore_on_blast, owner)
  78. if not ignore_protection and minetest.is_protected(npos, owner) then
  79. return cid
  80. end
  81. local def = cid_data[cid]
  82. if not def then
  83. return c_air
  84. elseif not ignore_on_blast and def.on_blast then
  85. on_blast_queue[#on_blast_queue + 1] = {
  86. pos = vector.new(npos),
  87. on_blast = def.on_blast
  88. }
  89. return cid
  90. --[[
  91. elseif def.flammable then
  92. on_construct_queue[#on_construct_queue + 1] = {
  93. fn = basic_flame_on_construct,
  94. pos = vector.new(npos)
  95. }
  96. return c_fire
  97. --]]
  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:get_pos()
  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:set_pos(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:get_velocity()
  160. obj:set_velocity(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. glow = 15,
  188. })
  189. minetest.add_particlespawner({
  190. amount = 64,
  191. time = 0.5,
  192. minpos = vector.subtract(pos, radius / 2),
  193. maxpos = vector.add(pos, radius / 2),
  194. minvel = {x = -10, y = -10, z = -10},
  195. maxvel = {x = 10, y = 10, z = 10},
  196. minacc = vector.new(),
  197. maxacc = vector.new(),
  198. minexptime = 1,
  199. maxexptime = 2.5,
  200. minsize = radius * 3,
  201. maxsize = radius * 5,
  202. texture = 'tnt_smoke.png',
  203. })
  204. -- we just dropped some items. Look at the items entities and pick
  205. -- one of them to use as texture
  206. local texture = 'tnt_blast.png' --fallback texture
  207. local most = 0
  208. for name, stack in pairs(drops) do
  209. local count = stack:get_count()
  210. if count > most then
  211. most = count
  212. local def = minetest.registered_nodes[name]
  213. if def and def.tiles and def.tiles[1] then
  214. texture = def.tiles[1]
  215. end
  216. end
  217. end
  218. minetest.add_particlespawner({
  219. amount = 64,
  220. time = 0.1,
  221. minpos = vector.subtract(pos, radius / 2),
  222. maxpos = vector.add(pos, radius / 2),
  223. minvel = {x = -3, y = 0, z = -3},
  224. maxvel = {x = 3, y = 5, z = 3},
  225. minacc = {x = 0, y = -10, z = 0},
  226. maxacc = {x = 0, y = -10, z = 0},
  227. minexptime = 0.8,
  228. maxexptime = 2.0,
  229. minsize = radius * 0.66,
  230. maxsize = radius * 2,
  231. texture = texture,
  232. collisiondetection = true,
  233. })
  234. end
  235. function tnt.burn(pos, nodename)
  236. local name = nodename or minetest.get_node(pos).name
  237. local def = minetest.registered_nodes[name]
  238. if not def then
  239. return
  240. elseif def.on_ignite then
  241. def.on_ignite(pos)
  242. elseif minetest.get_item_group(name, 'tnt') > 0 then
  243. minetest.swap_node(pos, {name = name .. '_burning'})
  244. minetest.sound_play('tnt_ignite', {pos = pos})
  245. minetest.get_node_timer(pos):start(1)
  246. end
  247. end
  248. local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast, owner, explode_center)
  249. pos = vector.round(pos)
  250. -- scan for adjacent TNT nodes first, and enlarge the explosion
  251. local vm1 = VoxelManip()
  252. local p1 = vector.subtract(pos, 2)
  253. local p2 = vector.add(pos, 2)
  254. local minp, maxp = vm1:read_from_map(p1, p2)
  255. local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
  256. local data = vm1:get_data()
  257. local count = 0
  258. local c_tnt = minetest.get_content_id('tnt:gunpowder_case')
  259. local c_tnt_burning = minetest.get_content_id('tnt:gunpowder_case_burning')
  260. local c_tnt_boom = minetest.get_content_id('tnt:boom')
  261. local c_air = minetest.get_content_id('air')
  262. -- make sure we still have explosion even when centre node isnt tnt related
  263. if explode_center then
  264. count = 1
  265. end
  266. for z = pos.z - 2, pos.z + 2 do
  267. for y = pos.y - 2, pos.y + 2 do
  268. local vi = a:index(pos.x - 2, y, z)
  269. for x = pos.x - 2, pos.x + 2 do
  270. local cid = data[vi]
  271. if cid == c_tnt or cid == c_tnt_boom or cid == c_tnt_burning then
  272. count = count + 1
  273. data[vi] = c_air
  274. end
  275. vi = vi + 1
  276. end
  277. end
  278. end
  279. vm1:set_data(data)
  280. vm1:write_to_map()
  281. -- recalculate new radius
  282. radius = math.floor(radius * math.pow(count, 1/3))
  283. -- perform the explosion
  284. local vm = VoxelManip()
  285. local pr = PseudoRandom(os.time())
  286. p1 = vector.subtract(pos, radius)
  287. p2 = vector.add(pos, radius)
  288. minp, maxp = vm:read_from_map(p1, p2)
  289. a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
  290. data = vm:get_data()
  291. local drops = {}
  292. local on_blast_queue = {}
  293. local on_construct_queue = {}
  294. basic_flame_on_construct = minetest.registered_nodes['fire:basic_flame'].on_construct
  295. local c_fire = minetest.get_content_id('fire:basic_flame')
  296. for z = -radius, radius do
  297. for y = -radius, radius do
  298. local vi = a:index(pos.x + (-radius), pos.y + y, pos.z + z)
  299. for x = -radius, radius do
  300. local r = vector.length(vector.new(x, y, z))
  301. if (radius * radius) / (r * r) >= (pr:next(80, 125) / 100) then
  302. local cid = data[vi]
  303. local p = {x = pos.x + x, y = pos.y + y, z = pos.z + z}
  304. if cid ~= c_air then
  305. data[vi] = destroy(drops, p, cid, c_air, c_fire,
  306. on_blast_queue, on_construct_queue,
  307. ignore_protection, ignore_on_blast, owner)
  308. end
  309. end
  310. vi = vi + 1
  311. end
  312. end
  313. end
  314. vm:set_data(data)
  315. vm:write_to_map()
  316. vm:update_map()
  317. vm:update_liquids()
  318. -- call check_single_for_falling for everything within 1.5x blast radius
  319. for y = -radius * 1.5, radius * 1.5 do
  320. for z = -radius * 1.5, radius * 1.5 do
  321. for x = -radius * 1.5, radius * 1.5 do
  322. local rad = {x = x, y = y, z = z}
  323. local s = vector.add(pos, rad)
  324. local r = vector.length(rad)
  325. if r / radius < 1.4 then
  326. minetest.check_single_for_falling(s)
  327. end
  328. end
  329. end
  330. end
  331. for _, queued_data in pairs(on_blast_queue) do
  332. local dist = math.max(1, vector.distance(queued_data.pos, pos))
  333. local intensity = (radius * radius) / (dist * dist)
  334. local node_drops = queued_data.on_blast(queued_data.pos, intensity)
  335. if node_drops then
  336. for _, item in pairs(node_drops) do
  337. add_drop(drops, item)
  338. end
  339. end
  340. end
  341. for _, queued_data in pairs(on_construct_queue) do
  342. queued_data.fn(queued_data.pos)
  343. end
  344. minetest.log('action', 'TNT owned by ' .. owner .. ' detonated at ' ..
  345. minetest.pos_to_string(pos) .. ' with radius ' .. radius)
  346. return drops, radius
  347. end
  348. function tnt.boom(pos, def)
  349. def = def or {}
  350. def.radius = def.radius or 1
  351. def.damage_radius = def.damage_radius or def.radius * 2
  352. local meta = minetest.get_meta(pos)
  353. local owner = meta:get_string('owner')
  354. if not def.explode_center then
  355. minetest.set_node(pos, {name = 'tnt:boom'})
  356. end
  357. local sound = def.sound or 'tnt_explode'
  358. minetest.sound_play(sound, {pos = pos, gain = 2.5,
  359. max_hear_distance = math.min(def.radius * 20, 128)})
  360. local drops, radius = tnt_explode(pos, def.radius, def.ignore_protection,
  361. def.ignore_on_blast, owner, def.explode_center)
  362. -- append entity drops
  363. local damage_radius = (radius / math.max(1, def.radius)) * def.damage_radius
  364. entity_physics(pos, damage_radius, drops)
  365. if not def.disable_drops then
  366. eject_drops(drops, pos, radius)
  367. end
  368. add_effects(pos, radius, drops)
  369. minetest.log('action', 'A TNT explosion occurred at ' .. minetest.pos_to_string(pos) ..
  370. ' with radius ' .. radius)
  371. end
  372. minetest.register_node('tnt:boom', {
  373. drawtype = 'airlike',
  374. light_source = default.LIGHT_MAX,
  375. walkable = false,
  376. drop = '',
  377. groups = {dig_immediate = 3},
  378. -- unaffected by explosions
  379. on_blast = function() end,
  380. })
  381. minetest.register_node('tnt:gunpowder', {
  382. description = S('Gun Powder'),
  383. drawtype = 'raillike',
  384. paramtype = 'light',
  385. is_ground_content = false,
  386. sunlight_propagates = true,
  387. walkable = false,
  388. tiles = {
  389. 'tnt_gunpowder_straight.png',
  390. 'tnt_gunpowder_curved.png',
  391. 'tnt_gunpowder_t_junction.png',
  392. 'tnt_gunpowder_crossing.png'
  393. },
  394. inventory_image = 'tnt_gunpowder_inventory.png',
  395. wield_image = 'tnt_gunpowder_inventory.png',
  396. selection_box = {
  397. type = 'fixed',
  398. fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
  399. },
  400. groups = {dig_immediate = 2, attached_node = 1, flammable = 5,
  401. connect_to_raillike = minetest.raillike_group('gunpowder')},
  402. sounds = default.node_sound_leaves_defaults(),
  403. on_punch = function(pos, node, puncher)
  404. if puncher:get_wielded_item():get_name() == 'default:torch' then
  405. if minetest.check_player_privs(puncher, { fire = true }) then
  406. minetest.set_node(pos, {name = 'tnt:gunpowder_burning'})
  407. minetest.log('action', puncher:get_player_name() ..
  408. ' ignites tnt:gunpowder at ' ..
  409. minetest.pos_to_string(pos))
  410. end
  411. end
  412. end,
  413. on_blast = function(pos, intensity)
  414. minetest.set_node(pos, {name = 'tnt:gunpowder_burning'})
  415. end,
  416. on_burn = function(pos)
  417. minetest.set_node(pos, {name = 'tnt:gunpowder_burning'})
  418. end,
  419. on_ignite = function(pos, igniter)
  420. minetest.set_node(pos, {name = 'tnt:gunpowder_burning'})
  421. end,
  422. })
  423. minetest.register_node('tnt:gunpowder_burning', {
  424. drawtype = 'raillike',
  425. paramtype = 'light',
  426. sunlight_propagates = true,
  427. walkable = false,
  428. light_source = 5,
  429. tiles = {{
  430. name = 'tnt_gunpowder_burning_straight_animated.png',
  431. animation = {
  432. type = 'vertical_frames',
  433. aspect_w = 16,
  434. aspect_h = 16,
  435. length = 1,
  436. }
  437. },
  438. {
  439. name = 'tnt_gunpowder_burning_curved_animated.png',
  440. animation = {
  441. type = 'vertical_frames',
  442. aspect_w = 16,
  443. aspect_h = 16,
  444. length = 1,
  445. }
  446. },
  447. {
  448. name = 'tnt_gunpowder_burning_t_junction_animated.png',
  449. animation = {
  450. type = 'vertical_frames',
  451. aspect_w = 16,
  452. aspect_h = 16,
  453. length = 1,
  454. }
  455. },
  456. {
  457. name = 'tnt_gunpowder_burning_crossing_animated.png',
  458. animation = {
  459. type = 'vertical_frames',
  460. aspect_w = 16,
  461. aspect_h = 16,
  462. length = 1,
  463. }
  464. }},
  465. selection_box = {
  466. type = 'fixed',
  467. fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
  468. },
  469. drop = '',
  470. groups = {
  471. dig_immediate = 2,
  472. attached_node = 1,
  473. connect_to_raillike = minetest.raillike_group('gunpowder')
  474. },
  475. sounds = default.node_sound_leaves_defaults(),
  476. on_timer = function(pos, elapsed)
  477. for dx = -1, 1 do
  478. for dz = -1, 1 do
  479. if math.abs(dx) + math.abs(dz) == 1 then
  480. for dy = -1, 1 do
  481. tnt.burn({
  482. x = pos.x + dx,
  483. y = pos.y + dy,
  484. z = pos.z + dz,
  485. })
  486. end
  487. end
  488. end
  489. end
  490. minetest.remove_node(pos)
  491. end,
  492. -- unaffected by explosions
  493. on_blast = function() end,
  494. on_construct = function(pos)
  495. minetest.sound_play('tnt_gunpowder_burning', {pos = pos, gain = 2})
  496. minetest.get_node_timer(pos):start(1)
  497. end,
  498. })
  499. minetest.register_craft({
  500. output = 'tnt:gunpowder 5',
  501. type = 'shapeless',
  502. recipe = {'default:coal_lump', 'default:gravel'}
  503. })
  504. minetest.register_craftitem('tnt:gunpowder_stick', {
  505. description = S('Stick of Gunpowder'),
  506. inventory_image = 'tnt_tnt_stick.png',
  507. groups = {flammable = 5},
  508. })
  509. minetest.register_craft({
  510. output = 'tnt:gunpowder_stick 2',
  511. recipe = {
  512. {'tnt:gunpowder', 'farming:string', 'tnt:gunpowder'},
  513. {'tnt:gunpowder', 'default:paper', 'tnt:gunpowder'},
  514. {'tnt:gunpowder', '', 'tnt:gunpowder'},
  515. }
  516. })
  517. minetest.register_craft({
  518. output = 'tnt:gunpowder_case',
  519. recipe = {
  520. {'tnt:gunpowder_stick', 'tnt:gunpowder_stick', 'tnt:gunpowder_stick'},
  521. {'tnt:gunpowder_stick', 'tnt:gunpowder_stick', 'tnt:gunpowder_stick'},
  522. {'tnt:gunpowder_stick', 'tnt:gunpowder_stick', 'tnt:gunpowder_stick'}
  523. }
  524. })
  525. minetest.register_abm({
  526. label = 'TNT ignition',
  527. nodenames = {'group:tnt', 'tnt:gunpowder'},
  528. neighbors = {'fire:basic_flame', 'default:lava_source', 'default:lava_flowing'},
  529. interval = 4,
  530. chance = 1,
  531. action = function(pos, node)
  532. tnt.burn(pos, node.name)
  533. end,
  534. })
  535. function tnt.register_tnt(def)
  536. local name
  537. if not def.name:find(':') then
  538. name = 'tnt:' .. def.name
  539. else
  540. name = def.name
  541. def.name = def.name:match(':([%w_]+)')
  542. end
  543. if not def.tiles then def.tiles = {} end
  544. local tnt_top = def.tiles.top or def.name .. '_top.png'
  545. local tnt_bottom = def.tiles.bottom or def.name .. '_bottom.png'
  546. local tnt_side = def.tiles.side or def.name .. '_side.png'
  547. local tnt_burning = def.tiles.burning or def.name .. '_top_burning_animated.png'
  548. if not def.damage_radius then def.damage_radius = def.radius * 2 end
  549. minetest.register_node(':' .. name, {
  550. description = def.description,
  551. tiles = {tnt_top, tnt_bottom, tnt_side},
  552. is_ground_content = false,
  553. groups = {dig_immediate = 2, mesecon = 2, tnt = 1, flammable = 5},
  554. sounds = default.node_sound_wood_defaults(),
  555. after_place_node = function(pos, placer)
  556. if placer:is_player() then
  557. local meta = minetest.get_meta(pos)
  558. meta:set_string('owner', placer:get_player_name())
  559. end
  560. end,
  561. on_punch = function(pos, node, puncher)
  562. if puncher:get_wielded_item():get_name() == 'default:torch' then
  563. if minetest.check_player_privs(puncher, { fire = true }) then
  564. minetest.swap_node(pos, {name = name .. '_burning'})
  565. minetest.registered_nodes[name .. '_burning'].on_construct(pos)
  566. minetest.log('action', puncher:get_player_name() ..
  567. ' ignites ' .. node.name .. ' at ' ..
  568. minetest.pos_to_string(pos))
  569. end
  570. end
  571. end,
  572. on_blast = function(pos, intensity)
  573. minetest.after(0.1, function()
  574. tnt.boom(pos, def)
  575. end)
  576. end,
  577. mesecons = {effector =
  578. {action_on =
  579. function(pos)
  580. tnt.boom(pos, def)
  581. end
  582. }
  583. },
  584. on_burn = function(pos)
  585. minetest.swap_node(pos, {name = name .. '_burning'})
  586. minetest.registered_nodes[name .. '_burning'].on_construct(pos)
  587. end,
  588. on_ignite = function(pos, igniter)
  589. minetest.swap_node(pos, {name = name .. '_burning'})
  590. minetest.registered_nodes[name .. '_burning'].on_construct(pos)
  591. end,
  592. })
  593. minetest.register_node(':' .. name .. '_burning', {
  594. tiles = {
  595. {
  596. name = tnt_burning,
  597. animation = {
  598. type = 'vertical_frames',
  599. aspect_w = 16,
  600. aspect_h = 16,
  601. length = 1,
  602. }
  603. },
  604. tnt_bottom, tnt_side
  605. },
  606. light_source = 5,
  607. drop = '',
  608. sounds = default.node_sound_wood_defaults(),
  609. on_timer = function(pos, elapsed)
  610. tnt.boom(pos, def)
  611. end,
  612. -- unaffected by explosions
  613. on_blast = function() end,
  614. on_construct = function(pos)
  615. minetest.sound_play('tnt_ignite', {pos = pos})
  616. minetest.get_node_timer(pos):start(4)
  617. minetest.check_for_falling(pos)
  618. end,
  619. })
  620. end
  621. tnt.register_tnt({
  622. name = 'tnt:gunpowder_case',
  623. description = 'Gunpowder case',
  624. radius = tnt_radius,
  625. })