2019-03-07 23:46:35 +00:00
local S = minetest.get_translator ( " mcl_tnt " )
2020-02-05 02:11:32 +00:00
local tnt_griefing = minetest.settings : get_bool ( " mcl_tnt_griefing " , true )
2019-03-07 23:46:35 +00:00
2017-08-09 11:38:47 +00:00
local mod_death_messages = minetest.get_modpath ( " mcl_death_messages " )
2017-01-16 13:59:16 +00:00
local function spawn_tnt ( pos , entname )
2020-04-06 22:55:45 +00:00
minetest.sound_play ( " tnt_ignite " , { pos = pos , gain = 1.0 , max_hear_distance = 15 , } , true )
2017-02-27 22:45:26 +00:00
local tnt = minetest.add_entity ( pos , entname )
tnt : set_armor_groups ( { immortal = 1 } )
2017-03-04 22:25:02 +00:00
return tnt
2015-06-29 17:55:56 +00:00
end
2017-01-16 13:59:16 +00:00
local function activate_if_tnt ( nname , np , tnt_np , tntr )
2017-01-26 10:23:09 +00:00
if nname == " mcl_tnt:tnt " then
2015-06-29 17:55:56 +00:00
local e = spawn_tnt ( np , nname )
2019-03-06 03:38:57 +00:00
e : set_velocity ( { x = ( np.x - tnt_np.x ) * 5 + ( tntr / 4 ) , y = ( np.y - tnt_np.y ) * 5 + ( tntr / 3 ) , z = ( np.z - tnt_np.z ) * 5 + ( tntr / 4 ) } )
2020-02-05 11:45:29 +00:00
minetest.remove_node ( np )
minetest.check_for_falling ( np )
2015-06-29 17:55:56 +00:00
end
end
2020-04-08 10:41:52 +00:00
local function do_tnt_physics ( tnt_np , tntr , tnt_obj )
2017-01-11 17:21:46 +00:00
local objs = minetest.get_objects_inside_radius ( tnt_np , tntr )
2015-06-29 17:55:56 +00:00
for k , obj in pairs ( objs ) do
2017-08-18 19:58:20 +00:00
local ent = obj : get_luaentity ( )
2019-03-06 03:38:57 +00:00
local v = obj : get_velocity ( )
2019-02-01 05:33:07 +00:00
local p = obj : get_pos ( )
2020-04-08 10:41:52 +00:00
if ent and ent.name == " mcl_tnt:tnt " and v ~= nil then
2019-03-06 03:38:57 +00:00
obj : set_velocity ( { x = ( p.x - tnt_np.x ) + ( tntr / 2 ) + v.x , y = ( p.y - tnt_np.y ) + tntr + v.y , z = ( p.z - tnt_np.z ) + ( tntr / 2 ) + v.z } )
2015-06-29 17:55:56 +00:00
else
2020-04-08 10:41:52 +00:00
if v ~= nil and not obj : is_player ( ) then
2019-03-06 03:38:57 +00:00
obj : set_velocity ( { x = ( p.x - tnt_np.x ) + ( tntr / 4 ) + v.x , y = ( p.y - tnt_np.y ) + ( tntr / 2 ) + v.y , z = ( p.z - tnt_np.z ) + ( tntr / 4 ) + v.z } )
2020-04-08 10:41:52 +00:00
end
local dist = math.max ( 1 , vector.distance ( tnt_np , p ) )
local damage = ( 4 / dist ) * tntr
if obj : is_player ( ) == true then
if mod_death_messages then
mcl_death_messages.player_damage ( obj , S ( " @1 was caught in an explosion. " , obj : get_player_name ( ) ) )
2015-06-29 17:55:56 +00:00
end
end
2020-04-08 10:41:52 +00:00
local puncher = tnt_obj or obj
obj : punch ( puncher , nil , { damage_groups = { fleshy = damage } } )
2015-06-29 17:55:56 +00:00
end
end
end
2017-01-16 13:59:16 +00:00
tnt = { }
tnt.ignite = function ( pos )
minetest.remove_node ( pos )
2017-01-26 10:23:09 +00:00
spawn_tnt ( pos , " mcl_tnt:tnt " )
2020-02-05 11:45:29 +00:00
minetest.check_for_falling ( pos )
2017-01-16 13:59:16 +00:00
end
2020-01-30 21:05:18 +00:00
-- Add smoke particle of entity at pos.
-- Intended to be called every step
tnt.smoke_step = function ( pos )
minetest.add_particle ( {
pos = { x = pos.x , y = pos.y + 0.5 , z = pos.z } ,
velocity = vector.new ( math.random ( ) * 0.2 - 0.1 , 1.0 + math.random ( ) , math.random ( ) * 0.2 - 0.1 ) ,
acceleration = vector.new ( 0 , - 0.1 , 0 ) ,
expirationtime = 0.15 + math.random ( ) * 0.25 ,
size = 1.0 + math.random ( ) ,
collisiondetection = false ,
texture = " tnt_smoke.png "
} )
end
tnt.BOOMTIMER = 4
tnt.BLINKTIMER = 0.25
2017-03-11 19:54:27 +00:00
local TNT_RANGE = 3
2017-01-16 13:59:16 +00:00
2017-08-09 11:38:47 +00:00
local sounds
if minetest.get_modpath ( " mcl_sounds " ) then
sounds = mcl_sounds.node_sound_wood_defaults ( )
end
2019-03-17 08:33:44 +00:00
local tnt_mesecons
if minetest.get_modpath ( " mesecons " ) then
tnt_mesecons = { effector = {
action_on = tnt.ignite ,
rules = mesecon.rules . alldirs ,
} }
end
2020-02-05 02:11:32 +00:00
local longdesc
if tnt_griefing then
longdesc = S ( " An explosive device. When it explodes, it will hurt living beings and destroy blocks around it. TNT has an explosion radius of @1. With a small chance, blocks may drop as an item (as if being mined) rather than being destroyed. TNT can be ignited by tools, explosions, fire, lava and redstone signals. " , TNT_RANGE )
else
longdesc = S ( " An explosive device. When it explodes, it will hurt living beings. TNT has an explosion radius of @1. TNT can be ignited by tools, explosions, fire, lava and redstone signals. " , TNT_RANGE )
end
2017-01-26 10:23:09 +00:00
minetest.register_node ( " mcl_tnt:tnt " , {
2017-01-16 12:00:20 +00:00
tiles = { " default_tnt_top.png " , " default_tnt_bottom.png " ,
2015-06-29 17:55:56 +00:00
" default_tnt_side.png " , " default_tnt_side.png " ,
" default_tnt_side.png " , " default_tnt_side.png " } ,
2017-01-04 21:36:51 +00:00
is_ground_content = false ,
2015-06-29 17:55:56 +00:00
stack_max = 64 ,
2019-03-07 23:46:35 +00:00
description = S ( " TNT " ) ,
2017-06-13 12:46:21 +00:00
paramtype = " light " ,
sunlight_propagates = true ,
2020-03-12 00:35:11 +00:00
_tt_help = S ( " Ignited by tools, explosions, fire, lava, redstone power " ) .. " \n " .. S ( " Explosion radius: @1 " , tostring ( TNT_RANGE ) ) ,
2020-02-05 02:11:32 +00:00
_doc_items_longdesc = longdesc ,
2019-03-15 23:07:44 +00:00
_doc_items_usagehelp = S ( " Place the TNT and ignite it with one of the methods above. Quickly get in safe distance. The TNT will start to be affected by gravity and explodes in 4 seconds. " ) ,
2017-07-05 18:14:37 +00:00
groups = { dig_immediate = 3 , tnt = 1 , enderman_takable = 1 } ,
2019-03-17 08:33:44 +00:00
mesecons = tnt_mesecons ,
2020-04-16 21:05:50 +00:00
on_blast = function ( pos )
minetest.chat_send_all ( " on_blast " .. minetest.pos_to_string ( pos ) )
spawn_tnt ( pos , " mcl_tnt:tnt " )
return true
end ,
2017-08-17 02:12:34 +00:00
_on_ignite = function ( player , pointed_thing )
tnt.ignite ( pointed_thing.under )
2017-09-19 13:45:23 +00:00
return true
2017-05-09 15:49:38 +00:00
end ,
2018-02-01 21:45:19 +00:00
_on_dispense = function ( stack , pos , droppos , dropnode , dropdir )
-- Place and ignite TNT
if minetest.registered_nodes [ dropnode.name ] . buildable_to then
minetest.set_node ( droppos , { name = stack : get_name ( ) } )
tnt.ignite ( droppos )
end
end ,
2017-08-09 11:38:47 +00:00
sounds = sounds ,
2015-06-29 17:55:56 +00:00
} )
local TNT = {
-- Static definition
physical = true , -- Collides with things
--weight = -100,
collisionbox = { - 0.5 , - 0.5 , - 0.5 , 0.5 , 0.5 , 0.5 } ,
visual = " cube " ,
textures = { " default_tnt_top.png " , " default_tnt_bottom.png " ,
" default_tnt_side.png " , " default_tnt_side.png " ,
" default_tnt_side.png " , " default_tnt_side.png " } ,
-- Initial value for our timer
timer = 0 ,
blinktimer = 0 ,
blinkstatus = true , }
function TNT : on_activate ( staticdata )
2018-01-25 15:23:54 +00:00
local phi = math.random ( 0 , 65535 ) / 65535 * 2 * math.pi
local hdir_x = math.cos ( phi ) * 0.02
local hdir_z = math.sin ( phi ) * 0.02
2019-03-06 03:38:57 +00:00
self.object : set_velocity ( { x = hdir_x , y = 2 , z = hdir_z } )
self.object : set_acceleration ( { x = 0 , y =- 10 , z = 0 } )
self.object : set_texture_mod ( " ^mcl_tnt_blink.png " )
2015-06-29 17:55:56 +00:00
end
2017-05-29 23:27:45 +00:00
local function add_effects ( pos , radius , drops )
minetest.add_particlespawner ( {
amount = 64 ,
time = 0.5 ,
minpos = vector.subtract ( pos , radius / 2 ) ,
maxpos = vector.add ( pos , radius / 2 ) ,
minvel = { x = - 10 , y = - 10 , z = - 10 } ,
maxvel = { x = 10 , y = 10 , z = 10 } ,
minacc = vector.new ( ) ,
maxacc = vector.new ( ) ,
minexptime = 1 ,
maxexptime = 2.5 ,
minsize = radius * 1 ,
maxsize = radius * 3 ,
texture = " tnt_smoke.png " ,
} )
-- we just dropped some items. Look at the items entities and pick
-- one of them to use as texture
local texture = " tnt_smoke.png " --fallback texture
local most = 0
for name , stack in pairs ( drops ) do
local count = stack : get_count ( )
if count > most then
most = count
local def = minetest.registered_nodes [ name ]
if def and def.tiles and def.tiles [ 1 ] then
texture = def.tiles [ 1 ]
end
end
end
minetest.add_particlespawner ( {
amount = 32 ,
time = 0.1 ,
minpos = vector.subtract ( pos , radius / 2 ) ,
maxpos = vector.add ( pos , radius / 2 ) ,
minvel = { x = - 3 , y = 0 , z = - 3 } ,
maxvel = { x = 3 , y = 5 , z = 3 } ,
minacc = { x = 0 , y = - 10 , z = 0 } ,
minexptime = 0.8 ,
maxexptime = 2.0 ,
minsize = radius * 0.66 ,
maxsize = radius * 2 ,
texture = texture ,
collisiondetection = true ,
} )
end
2015-06-29 17:55:56 +00:00
function TNT : on_step ( dtime )
2019-02-01 05:33:07 +00:00
local pos = self.object : get_pos ( )
2020-01-30 21:05:18 +00:00
tnt.smoke_step ( pos )
2015-06-29 17:55:56 +00:00
self.timer = self.timer + dtime
self.blinktimer = self.blinktimer + dtime
2020-01-30 21:05:18 +00:00
if self.blinktimer > tnt.BLINKTIMER then
self.blinktimer = self.blinktimer - tnt.BLINKTIMER
2015-06-29 17:55:56 +00:00
if self.blinkstatus then
2019-03-06 03:38:57 +00:00
self.object : set_texture_mod ( " " )
2015-06-29 17:55:56 +00:00
else
2019-03-06 03:38:57 +00:00
self.object : set_texture_mod ( " ^mcl_tnt_blink.png " )
2015-06-29 17:55:56 +00:00
end
self.blinkstatus = not self.blinkstatus
end
2020-01-30 21:05:18 +00:00
if self.timer > tnt.BOOMTIMER then
2020-04-08 10:41:52 +00:00
tnt.boom ( self.object : get_pos ( ) , nil , self.object )
2017-07-25 02:44:46 +00:00
self.object : remove ( )
end
end
2020-04-08 10:41:52 +00:00
tnt.boom = function ( pos , info , tnt_obj )
2017-07-31 00:06:00 +00:00
if not info then info = { } end
local range = info.radius or TNT_RANGE
local damage_range = info.damage_radius or TNT_RANGE
2017-07-25 02:44:46 +00:00
pos.x = math.floor ( pos.x + 0.5 )
pos.y = math.floor ( pos.y + 0.5 )
pos.z = math.floor ( pos.z + 0.5 )
2020-04-08 10:41:52 +00:00
do_tnt_physics ( pos , range , tnt_obj )
2017-07-25 02:44:46 +00:00
local meta = minetest.get_meta ( pos )
local sound
if not info.sound then
sound = " tnt_explode "
else
sound = info.sound
end
2020-02-05 02:11:32 +00:00
if info.is_tnt == nil then
info.is_tnt = true
end
2020-04-06 22:55:45 +00:00
minetest.sound_play ( sound , { pos = pos , gain = 1.0 , max_hear_distance = 16 , } , true )
2017-08-04 22:55:38 +00:00
local node = minetest.get_node ( pos )
2019-02-11 20:27:17 +00:00
if minetest.get_item_group ( " water " ) == 1 or minetest.get_item_group ( " lava " ) == 1 then
2017-07-25 02:44:46 +00:00
-- Cancel the Explosion
return
2015-06-29 17:55:56 +00:00
end
2017-07-25 02:44:46 +00:00
for x =- range , range do
for y =- range , range do
for z =- range , range do
if x * x + y * y + z * z <= range * range + range then
local np = { x = pos.x + x , y = pos.y + y , z = pos.z + z }
local n = minetest.get_node ( np )
local def = minetest.registered_nodes [ n.name ]
-- Simple blast resistance check (for now). This keeps the important blocks like bedrock, command block, etc. intact.
-- TODO: Implement the real blast resistance algorithm
if def and n.name ~= " air " and n.name ~= " ignore " and ( def._mcl_blast_resistance == nil or def._mcl_blast_resistance < 1000 ) then
activate_if_tnt ( n.name , np , pos , 3 )
2020-02-05 02:11:32 +00:00
if ( not tnt_griefing ) and info.is_tnt ~= false then
-- No-op
2019-12-09 20:06:08 +00:00
-- Custom blast function defined by node.
-- Node removal and drops must be handled manually.
2020-02-05 02:11:32 +00:00
elseif def.on_blast then
2019-12-09 20:06:08 +00:00
def.on_blast ( np , 1.0 )
-- Default destruction handling: Remove nodes, drop items
else
minetest.remove_node ( np )
2020-02-05 11:45:29 +00:00
minetest.check_for_falling ( np )
2019-12-09 20:06:08 +00:00
if n.name ~= " mcl_tnt:tnt " and math.random ( ) > 0.9 then
local drop = minetest.get_node_drops ( n.name , " " )
for _ , item in ipairs ( drop ) do
if type ( item ) == " string " then
if math.random ( 1 , 100 ) > 40 then
local obj = minetest.add_item ( np , item )
end
2015-06-29 17:55:56 +00:00
end
2017-05-29 23:27:45 +00:00
end
2015-06-29 17:55:56 +00:00
end
end
end
end
end
end
2017-07-25 02:44:46 +00:00
add_effects ( pos , range , { } )
2015-06-29 17:55:56 +00:00
end
end
2017-01-26 10:23:09 +00:00
minetest.register_entity ( " mcl_tnt:tnt " , TNT )
2015-06-29 17:55:56 +00:00
2017-08-09 11:38:47 +00:00
if minetest.get_modpath ( " mcl_mobitems " ) then
minetest.register_craft ( {
output = " mcl_tnt:tnt " ,
recipe = {
{ ' mcl_mobitems:gunpowder ' , ' group:sand ' , ' mcl_mobitems:gunpowder ' } ,
{ ' group:sand ' , ' mcl_mobitems:gunpowder ' , ' group:sand ' } ,
{ ' mcl_mobitems:gunpowder ' , ' group:sand ' , ' mcl_mobitems:gunpowder ' }
}
} )
end
2017-03-21 03:36:18 +00:00
if minetest.get_modpath ( " doc_identifier " ) then
doc.sub . identifier.register_object ( " mcl_tnt:tnt " , " nodes " , " mcl_tnt:tnt " )
end