nssm_api.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. -- set content id's
  2. local c_air = minetest.get_content_id("air")
  3. local c_ignore = minetest.get_content_id("ignore")
  4. local c_obsidian = minetest.get_content_id("default:obsidian")
  5. local c_brick = minetest.get_content_id("default:obsidianbrick")
  6. local c_chest = minetest.get_content_id("default:chest_locked")
  7. nssm.lessvirulent = minetest.settings:get_bool("nssm.lessvirulent") or false
  8. nssm.safebones = minetest.settings:get_bool("nssm.safebones") or false
  9. nssm.cryosave = minetest.settings:get_bool("nssm.cryosave") or false
  10. function nssm:virulence(mobe)
  11. if not nssm.lessvirulent then
  12. return 0
  13. end
  14. return math.ceil(100 / mobe.hp_max)
  15. end
  16. function nssm:affectbones(mobe) -- as function for adaptable heuristic
  17. return not nssm.safebones
  18. end
  19. function drops(drop)
  20. if drop then
  21. drop:setvelocity({
  22. x = math.random(-10, 10) / 9,
  23. y = 5,
  24. z = math.random(-10, 10) / 9,
  25. })
  26. end
  27. end
  28. function perpendicular_vector(vec) --returns a vector rotated of 90° in 2D
  29. local ang = math.pi / 2
  30. local c = math.cos(ang)
  31. local s = math.sin(ang)
  32. local i = vec.x * c - vec.z * s
  33. local k = vec.x * s + vec.z * c
  34. local j = 0
  35. return {x = i, y = j, z = k}
  36. end
  37. function add_entity_and_particles(entity, pos, particles, multiplier)
  38. minetest.add_particlespawner({
  39. amount = 100*multiplier,
  40. time = 2,
  41. minpos = {x=pos.x-2, y=pos.y-1, z=pos.z-2},
  42. maxpos = {x=pos.x+2, y=pos.y+4, z=pos.z+2},
  43. minvel = {x=0, y=0, z=0},
  44. maxvel = {x=1, y=2, z=1},
  45. minacc = {x=-0.5,y=0.6,z=-0.5},
  46. maxacc = {x=0.5,y=0.7,z=0.5},
  47. minexptime = 2,
  48. maxexptime = 3,
  49. minsize = 3,
  50. maxsize = 5,
  51. collisiondetection = false,
  52. vertical = false,
  53. texture = particles,
  54. })
  55. minetest.add_entity(pos, entity)
  56. end
  57. -- get node but use fallback for nil or unknown
  58. function node_ok(pos, fallback)
  59. fallback = fallback or "default:dirt"
  60. local node = minetest.get_node_or_nil(pos)
  61. if not node then
  62. return minetest.registered_nodes[fallback]
  63. end
  64. if minetest.registered_nodes[node.name] then
  65. return node
  66. end
  67. return minetest.registered_nodes[fallback]
  68. end
  69. function dist_pos(p, s)
  70. local v = {
  71. x = math.abs(s.x - p.x),
  72. y = math.abs(s.y - p.y),
  73. z = math.abs(s.z - p.z)
  74. }
  75. return math.sqrt(v.x ^ 2 + v.y ^ 2 + v.z ^ 2)
  76. end
  77. --check_for_death functions customized for monsters who respawns (Masticone)
  78. function check_for_death_hydra(self)
  79. local hp = self.object:get_hp()
  80. if hp > 0 then
  81. self.health = hp
  82. if self.sounds.damage ~= nil then
  83. minetest.sound_play(self.sounds.damage,{
  84. object = self.object,
  85. max_hear_distance = self.sounds.distance
  86. })
  87. end
  88. return false
  89. end
  90. local pos = self.object:get_pos()
  91. local obj = nil
  92. if self.sounds.death ~= nil then
  93. minetest.sound_play(self.sounds.death,{
  94. object = self.object,
  95. max_hear_distance = self.sounds.distance
  96. })
  97. end
  98. self.object:remove()
  99. return true
  100. end
  101. function round(n)
  102. if (n > 0) then
  103. return n % 1 >= 0.5 and math.ceil(n) or math.floor(n)
  104. else
  105. n = -n
  106. local t = n % 1 >= 0.5 and math.ceil(n) or math.floor(n)
  107. return -t
  108. end
  109. end
  110. function explosion_particles(pos, exp_radius)
  111. minetest.add_particlespawner(
  112. 100*exp_radius/2, --amount
  113. 0.1, --time
  114. {x=pos.x-exp_radius, y=pos.y-exp_radius, z=pos.z-exp_radius}, --minpos
  115. {x=pos.x+exp_radius, y=pos.y+exp_radius, z=pos.z+exp_radius}, --maxpos
  116. {x=0, y=0, z=0}, --minvel
  117. {x=0.1, y=0.3, z=0.1}, --maxvel
  118. {x=-0.5,y=1,z=-0.5}, --minacc
  119. {x=0.5,y=1,z=0.5}, --maxacc
  120. 0.1, --minexptime
  121. 4, --maxexptime
  122. 6, --minsize
  123. 12, --maxsize
  124. false, --collisiondetection
  125. "tnt_smoke.png" --texture
  126. )
  127. end
  128. function digging_attack(
  129. self, --the entity of the mob
  130. group, --group of the blocks the mob can dig: nil=everything
  131. max_vel, --max velocity of the mob
  132. dim --vector representing the dimensions of the mob
  133. )
  134. --if math.random(1,nssm:virulence(self)) ~= 1 then return end
  135. if self.attack and self.attack:is_player() then
  136. local s = self.object:get_pos()
  137. local p = self.attack:get_pos()
  138. local dir = vector.subtract(p,s)
  139. dir = vector.normalize(dir)
  140. local per = perpendicular_vector(dir)
  141. local posp = vector.add(s,dir)
  142. --minetest.chat_send_all("La mia posizione:"..minetest.pos_to_string(s))
  143. --minetest.chat_send_all("La posizione davanti:"..minetest.pos_to_string(posp))
  144. posp = vector.subtract(posp,per)
  145. for j = 1,3 do
  146. --minetest.chat_send_all("pos1:"..minetest.pos_to_string(posp).." per.y= "..dim.y)
  147. if minetest.is_protected(posp, "") then
  148. return
  149. end
  150. local pos1 = posp
  151. for i = 0, dim.y do
  152. --minetest.chat_send_all("pos2:"..minetest.pos_to_string(posp).." per.y= "..per.y)
  153. local n = minetest.get_node(pos1).name
  154. --local up = {x=pos.x+dx, y=pos.y+dy, z=pos.z+dz}
  155. if group == nil then
  156. if minetest.get_item_group(n, "unbreakable") == 1
  157. or minetest.is_protected(pos1, "")
  158. or (n == "bones:bones" and not nssm:affectbones(self) ) then
  159. else
  160. --minetest.set_node(p, {name="air"})
  161. minetest.remove_node(pos1)
  162. end
  163. else
  164. if ((minetest.get_item_group(n, group)==1)
  165. and (minetest.get_item_group(n, "unbreakable") ~= 1)
  166. and (n ~= "bones:bones")
  167. and not (minetest.is_protected(pos1, "")) ) then
  168. --minetest.set_node(p, {name="air"})
  169. minetest.remove_node(pos1)
  170. end
  171. end
  172. pos1.y = pos1.y + 1
  173. end
  174. posp.y = s.y
  175. posp = vector.add(posp,per)
  176. --minetest.chat_send_all("pos3:"..minetest.pos_to_string(posp).." per.y= "..per.y)
  177. end
  178. end
  179. end
  180. function putting_ability( --puts under the mob the block defined as 'p_block'
  181. self, --the entity of the mob
  182. p_block, --definition of the block to use
  183. max_vel --max velocity of the mob
  184. )
  185. --if math.random(1,nssm:virulence(self)) ~= 1 then return end
  186. local v = self.object:get_velocity()
  187. local dx = 0
  188. local dz = 0
  189. if (math.abs(v.x)>math.abs(v.z)) then
  190. if (v.x)>0 then
  191. dx = 1
  192. else
  193. dx = -1
  194. end
  195. else
  196. if (v.z)>0 then
  197. dz = 1
  198. else
  199. dz = -1
  200. end
  201. end
  202. local pos = self.object:get_pos()
  203. local pos1
  204. pos.y=pos.y - 1
  205. pos1 = {x = pos.x + dx, y = pos.y, z = pos.z + dz}
  206. local n = minetest.get_node(pos).name
  207. local n1 = minetest.get_node(pos1).name
  208. local oldmetainf = {
  209. minetest.get_meta(pos):to_table(),
  210. minetest.get_meta(pos1):to_table()
  211. }
  212. if n ~= p_block and not minetest.is_protected(pos, "")
  213. and (n == "bones:bones"
  214. and nssm:affectbones(self) )
  215. and n ~= "air" then
  216. minetest.set_node(pos, {name=p_block})
  217. if nssm.cryosave then
  218. local metai = minetest.get_meta(pos)
  219. metai:from_table(oldmetainf[1]) -- this is enough to save the meta
  220. metai:set_string("nssm", n)
  221. end
  222. end
  223. if n1 ~= p_block and not minetest.is_protected(pos1, "")
  224. and (n == "bones:bones" and nssm:affectbones(self) )
  225. and n ~= "air" then
  226. minetest.set_node(pos1, {name = p_block})
  227. if nssm.cryosave then
  228. local metai = minetest.get_meta(pos1)
  229. metai:from_table(oldmetainf[2]) -- this is enough to save the meta
  230. metai:set_string("nssm",n1)
  231. end
  232. end
  233. end
  234. function webber_ability( --puts randomly around the block defined as w_block
  235. self, --the entity of the mob
  236. w_block, --definition of the block to use
  237. radius --max distance the block can be put
  238. )
  239. if (nssm:virulence(self) ~= 0)
  240. and (math.random(1, nssm:virulence(self)) ~= 1) then return end
  241. local pos = self.object:get_pos()
  242. if (math.random(1, 55) == 1) then
  243. local dx = math.random(1, radius)
  244. local dz = math.random(1, radius)
  245. local p = {x = pos.x + dx, y = pos.y - 1, z = pos.z + dz}
  246. local t = {x = pos.x + dx, y = pos.y, z = pos.z + dz}
  247. local n = minetest.get_node(p).name
  248. local k = minetest.get_node(t).name
  249. if ((n ~= "air")
  250. and(k == "air"))
  251. and not minetest.is_protected(t, "") then
  252. minetest.set_node(t, {name = w_block})
  253. end
  254. end
  255. end
  256. function midas_ability( --ability to transform every blocks it touches in the m_block block
  257. self, --the entity of the mob
  258. m_block,
  259. max_vel, --max velocity of the mob
  260. mult, --multiplier of the dimensions of the area around that need the transformation
  261. height --height of the mob
  262. )
  263. --if math.random(1,nssm:virulence(self)) ~= 1 then return end
  264. local v = self.object:get_velocity()
  265. local pos = self.object:get_pos()
  266. if minetest.is_protected(pos, "") then
  267. return
  268. end
  269. local max = 0
  270. local yaw = (self.object:get_yaw() + self.rotate) or 0
  271. local x = math.sin(yaw) * -1
  272. local z = math.cos(yaw)
  273. local i = 1
  274. local i1 = -1
  275. local k = 1
  276. local k1 = -1
  277. local multiplier = mult
  278. if x > 0 then
  279. i = round(x * max_vel) * multiplier
  280. else
  281. i1 = round(x * max_vel) * multiplier
  282. end
  283. if z > 0 then
  284. k = round(z * max_vel) * multiplier
  285. else
  286. k1 = round(z * max_vel) * multiplier
  287. end
  288. for dx = i1, i do
  289. for dy = -1, height do
  290. for dz = k1, k do
  291. local p = {x = pos.x + dx, y = pos.y + dy, z = pos.z + dz}
  292. local n = minetest.get_node(p).name
  293. if minetest.get_item_group(n, "unbreakable") == 1
  294. or minetest.is_protected(p, "") or n=="air"
  295. or (n == "bones:bones" and not nssm:affectbones(self))
  296. or n==m_block then
  297. else
  298. minetest.set_node(p, {name = m_block})
  299. end
  300. end
  301. end
  302. end
  303. end
  304. -- NEW EXPLOSION FUNCTION
  305. -- loss probabilities array (one in X will be lost)
  306. local loss_prob = {}
  307. loss_prob["default:cobble"] = 3
  308. loss_prob["default:dirt"] = 4
  309. local tnt_radius = tonumber(minetest.settings:get("tnt_radius") or 3)
  310. local cid_data = {}
  311. minetest.after(0, function()
  312. for name, def in pairs(minetest.registered_nodes) do
  313. cid_data[minetest.get_content_id(name)] = {
  314. name = name,
  315. drops = def.drops,
  316. flammable = def.groups.flammable,
  317. on_blast = def.on_blast,
  318. }
  319. end
  320. end)
  321. local function rand_pos(center, pos, radius)
  322. local def
  323. local reg_nodes = minetest.registered_nodes
  324. local i = 0
  325. repeat
  326. -- Give up and use the center if this takes too long
  327. if i > 4 then
  328. pos.x, pos.z = center.x, center.z
  329. break
  330. end
  331. pos.x = center.x + math.random(-radius, radius)
  332. pos.z = center.z + math.random(-radius, radius)
  333. def = reg_nodes[minetest.get_node(pos).name]
  334. i = i + 1
  335. until def and not def.walkable
  336. end
  337. local function add_effects(pos, radius, drops)
  338. minetest.add_particle({
  339. pos = pos,
  340. velocity = vector.new(),
  341. acceleration = vector.new(),
  342. expirationtime = 0.4,
  343. size = radius * 10,
  344. collisiondetection = false,
  345. vertical = false,
  346. texture = "tnt_boom.png",
  347. })
  348. minetest.add_particlespawner({
  349. amount = 64,
  350. time = 0.5,
  351. minpos = vector.subtract(pos, radius / 2),
  352. maxpos = vector.add(pos, radius / 2),
  353. minvel = {x = -10, y = -10, z = -10},
  354. maxvel = {x = 10, y = 10, z = 10},
  355. minacc = vector.new(),
  356. maxacc = vector.new(),
  357. minexptime = 1,
  358. maxexptime = 2.5,
  359. minsize = radius * 3,
  360. maxsize = radius * 5,
  361. texture = "tnt_smoke.png",
  362. })
  363. -- we just dropped some items. Look at the items entities and pick
  364. -- one of them to use as texture
  365. local texture = "tnt_blast.png" --fallback texture
  366. local most = 0
  367. for name, stack in pairs(drops) do
  368. local count = stack:get_count()
  369. if count > most then
  370. most = count
  371. local def = minetest.registered_nodes[name]
  372. if def and def.tiles and def.tiles[1] then
  373. texture = def.tiles[1]
  374. end
  375. end
  376. end
  377. minetest.add_particlespawner({
  378. amount = 64,
  379. time = 0.1,
  380. minpos = vector.subtract(pos, radius / 2),
  381. maxpos = vector.add(pos, radius / 2),
  382. minvel = {x = -3, y = 0, z = -3},
  383. maxvel = {x = 3, y = 5, z = 3},
  384. minacc = {x = 0, y = -10, z = 0},
  385. maxacc = {x = 0, y = -10, z = 0},
  386. minexptime = 0.8,
  387. maxexptime = 2.0,
  388. minsize = radius * 0.66,
  389. maxsize = radius * 2,
  390. texture = texture,
  391. collisiondetection = true,
  392. })
  393. end
  394. local function eject_drops(drops, pos, radius)
  395. local drop_pos = vector.new(pos)
  396. for _, item in pairs(drops) do
  397. local count = math.min(item:get_count(), item:get_stack_max())
  398. while count > 0 do
  399. local take = math.max(1,math.min(radius * radius,
  400. count,
  401. item:get_stack_max()))
  402. rand_pos(pos, drop_pos, radius)
  403. local dropitem = ItemStack(item)
  404. dropitem:set_count(take)
  405. local obj = minetest.add_item(drop_pos, dropitem)
  406. if obj then
  407. obj:get_luaentity().collect = true
  408. obj:set_acceleration({x = 0, y = -10, z = 0})
  409. obj:set_velocity({x = math.random(-3, 3),
  410. y = math.random(0, 10),
  411. z = math.random(-3, 3)})
  412. end
  413. count = count - take
  414. end
  415. end
  416. end
  417. local function calc_velocity(pos1, pos2, old_vel, power)
  418. -- Avoid errors caused by a vector of zero length
  419. if vector.equals(pos1, pos2) then
  420. return old_vel
  421. end
  422. local vel = vector.direction(pos1, pos2)
  423. vel = vector.normalize(vel)
  424. vel = vector.multiply(vel, power)
  425. -- Divide by distance
  426. local dist = vector.distance(pos1, pos2)
  427. dist = math.max(dist, 1)
  428. vel = vector.divide(vel, dist)
  429. -- Add old velocity
  430. vel = vector.add(vel, old_vel)
  431. -- randomize it a bit
  432. vel = vector.add(vel, {
  433. x = math.random() - 0.5,
  434. y = math.random() - 0.5,
  435. z = math.random() - 0.5,
  436. })
  437. -- Limit to terminal velocity
  438. dist = vector.length(vel)
  439. if dist > 250 then
  440. vel = vector.divide(vel, dist / 250)
  441. end
  442. return vel
  443. end
  444. local function entity_physics(pos, radius, drops)
  445. local objs = minetest.get_objects_inside_radius(pos, radius)
  446. for _, obj in pairs(objs) do
  447. local obj_pos = obj:get_pos()
  448. local dist = math.max(1, vector.distance(pos, obj_pos))
  449. local damage = (4 / dist) * radius
  450. if obj:is_player() then
  451. -- currently the engine has no method to set
  452. -- player velocity. See #2960
  453. -- instead, we knock the player back 1.0 node, and slightly upwards
  454. local dir = vector.normalize(vector.subtract(obj_pos, pos))
  455. local moveoff = vector.multiply(dir, dist + 1.0)
  456. local newpos = vector.add(pos, moveoff)
  457. newpos = vector.add(newpos, {x = 0, y = 0.2, z = 0})
  458. obj:set_pos(newpos)
  459. obj:set_hp(obj:get_hp() - damage)
  460. else
  461. local do_damage = true
  462. local do_knockback = true
  463. local entity_drops = {}
  464. local luaobj = obj:get_luaentity()
  465. local objdef = minetest.registered_entities[luaobj.name]
  466. local name = luaobj.name
  467. if objdef and objdef.on_blast then
  468. if ((name == "nssm:pumpking")
  469. or (name == "nssm:morvalar0")
  470. or (name== "nssm:morvalar5")) then
  471. do_damage = false
  472. do_knockback = false
  473. else
  474. do_damage, do_knockback, entity_drops = objdef.on_blast(luaobj, damage)
  475. end
  476. end
  477. if do_knockback then
  478. local obj_vel = obj:get_velocity()
  479. obj:set_velocity(calc_velocity(pos, obj_pos,
  480. obj_vel, radius * 10))
  481. end
  482. if do_damage then
  483. if not obj:get_armor_groups().immortal then
  484. obj:punch(obj, 1.0, {
  485. full_punch_interval = 1.0,
  486. damage_groups = {fleshy = damage},
  487. }, nil)
  488. end
  489. end
  490. for _, item in pairs(entity_drops) do
  491. add_drop(drops, item)
  492. end
  493. end
  494. end
  495. end
  496. local function add_drop(drops, item)
  497. item = ItemStack(item)
  498. local name = item:get_name()
  499. if loss_prob[name] ~= nil and math.random(1, loss_prob[name]) == 1 then
  500. return
  501. end
  502. local drop = drops[name]
  503. if drop == nil then
  504. drops[name] = item
  505. else
  506. drop:set_count(drop:get_count() + item:get_count())
  507. end
  508. end
  509. local function destroy(drops, npos, cid, c_air, c_fire, on_blast_queue, ignore_protection, ignore_on_blast)
  510. if not ignore_protection and minetest.is_protected(npos, "") then
  511. return cid
  512. end
  513. local def = cid_data[cid]
  514. if not def then
  515. return c_air
  516. elseif not ignore_on_blast and def.on_blast then
  517. on_blast_queue[#on_blast_queue + 1] = {pos = vector.new(npos), on_blast = def.on_blast}
  518. return cid
  519. elseif def.flammable then
  520. return c_fire
  521. else
  522. local node_drops = minetest.get_node_drops(def.name, "")
  523. for _, item in pairs(node_drops) do
  524. add_drop(drops, item)
  525. end
  526. return c_air
  527. end
  528. end
  529. local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast)
  530. pos = vector.round(pos)
  531. -- scan for adjacent TNT nodes first, and enlarge the explosion
  532. local vm1 = VoxelManip()
  533. local p1 = vector.subtract(pos, 2)
  534. local p2 = vector.add(pos, 2)
  535. local minp, maxp = vm1:read_from_map(p1, p2)
  536. local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
  537. local data = vm1:get_data()
  538. local count = 0
  539. local c_tnt
  540. local c_tnt_burning = minetest.get_content_id("tnt:tnt_burning")
  541. local c_tnt_boom = minetest.get_content_id("tnt:boom")
  542. local c_air = minetest.get_content_id("air")
  543. if minetest.registered_nodes["tnt:tnt"] then
  544. c_tnt = minetest.get_content_id("tnt:tnt")
  545. else
  546. c_tnt = tnt_burning
  547. end
  548. for z = pos.z - 2, pos.z + 2 do
  549. for y = pos.y - 2, pos.y + 2 do
  550. local vi = a:index(pos.x - 2, y, z)
  551. for x = pos.x - 2, pos.x + 2 do
  552. local cid = data[vi]
  553. if cid == c_tnt or cid == c_tnt_boom or cid == c_tnt_burning then
  554. count = count + 1
  555. data[vi] = c_air
  556. end
  557. vi = vi + 1
  558. end
  559. end
  560. end
  561. vm1:set_data(data)
  562. vm1:write_to_map()
  563. -- recalculate new radius
  564. radius = math.floor(radius * math.pow(count, 1/3))
  565. -- perform the explosion
  566. local vm = VoxelManip()
  567. local pr = PseudoRandom(os.time())
  568. p1 = vector.subtract(pos, radius)
  569. p2 = vector.add(pos, radius)
  570. minp, maxp = vm:read_from_map(p1, p2)
  571. a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
  572. data = vm:get_data()
  573. local drops = {}
  574. local on_blast_queue = {}
  575. local c_fire = minetest.get_content_id("fire:basic_flame")
  576. for z = -radius, radius do
  577. for y = -radius, radius do
  578. local vi = a:index(pos.x + (-radius), pos.y + y, pos.z + z)
  579. for x = -radius, radius do
  580. local r = vector.length(vector.new(x, y, z))
  581. if (radius * radius) / (r * r) >= (pr:next(80, 125) / 100) then
  582. local cid = data[vi]
  583. local p = {x = pos.x + x, y = pos.y + y, z = pos.z + z}
  584. if cid ~= c_air then
  585. data[vi] = destroy(drops, p, cid, c_air, c_fire,
  586. on_blast_queue, ignore_protection,
  587. ignore_on_blast)
  588. end
  589. end
  590. vi = vi + 1
  591. end
  592. end
  593. end
  594. vm:set_data(data)
  595. vm:write_to_map()
  596. vm:update_map()
  597. vm:update_liquids()
  598. -- call nodeupdate for everything within 1.5x blast radius
  599. for y = -radius * 1.5, radius * 1.5 do
  600. for z = -radius * 1.5, radius * 1.5 do
  601. for x = -radius * 1.5, radius * 1.5 do
  602. local rad = {x = x, y = y, z = z}
  603. local s = vector.add(pos, rad)
  604. local r = vector.length(rad)
  605. if r / radius < 1.4 then
  606. --nodeupdate_single(s)
  607. core.check_single_for_falling(s)
  608. end
  609. end
  610. end
  611. end
  612. for _, queued_data in pairs(on_blast_queue) do
  613. local dist = math.max(1, vector.distance(queued_data.pos, pos))
  614. local intensity = (radius * radius) / (dist * dist)
  615. local node_drops = queued_data.on_blast(queued_data.pos, intensity)
  616. if node_drops then
  617. for _, item in pairs(node_drops) do
  618. add_drop(drops, item)
  619. end
  620. end
  621. end
  622. return drops, radius
  623. end
  624. function tnt_boom_nssm(pos, def)
  625. minetest.sound_play("tnt_explode", {pos = pos, gain = 1.5, max_hear_distance = 2*64})
  626. minetest.set_node(pos, {name = "tnt:boom"})
  627. local drops, radius = tnt_explode(pos, def.radius, def.ignore_protection,
  628. def.ignore_on_blast)
  629. -- append entity drops
  630. local damage_radius = (radius / def.radius) * def.damage_radius
  631. entity_physics(pos, damage_radius, drops)
  632. if not def.disable_drops then
  633. eject_drops(drops, pos, radius)
  634. end
  635. add_effects(pos, radius, drops)
  636. end