2017-08-09 14:38:47 +03:00
local mod_death_messages = minetest.get_modpath ( " mcl_death_messages " )
local mod_hunger = minetest.get_modpath ( " mcl_hunger " )
2017-01-16 15:59:16 +02:00
local function spawn_tnt ( pos , entname )
2017-02-28 00:45:26 +02:00
minetest.sound_play ( " tnt_ignite " , { pos = pos , gain = 1.0 , max_hear_distance = 15 , } )
local tnt = minetest.add_entity ( pos , entname )
tnt : set_armor_groups ( { immortal = 1 } )
2017-03-05 00:25:02 +02:00
return tnt
2015-06-29 20:55:56 +03:00
end
2017-01-16 15:59:16 +02:00
local function activate_if_tnt ( nname , np , tnt_np , tntr )
2017-01-26 12:23:09 +02:00
if nname == " mcl_tnt:tnt " then
2015-06-29 20:55:56 +03:00
local e = spawn_tnt ( np , nname )
e : setvelocity ( { 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 ) } )
end
end
2017-01-16 15:59:16 +02:00
local function do_tnt_physics ( tnt_np , tntr )
2017-01-11 19:21:46 +02:00
local objs = minetest.get_objects_inside_radius ( tnt_np , tntr )
2015-06-29 20:55:56 +03:00
for k , obj in pairs ( objs ) do
2017-08-18 22:58:20 +03:00
local ent = obj : get_luaentity ( )
2015-06-29 20:55:56 +03:00
local v = obj : getvelocity ( )
local p = obj : getpos ( )
2017-08-18 22:58:20 +03:00
if ent and ent.name == " mcl_tnt:tnt " then
2015-06-29 20:55:56 +03:00
obj : setvelocity ( { 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 } )
else
if v ~= nil then
obj : setvelocity ( { 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 } )
else
2017-07-24 22:08:21 +03:00
local dist = math.max ( 1 , vector.distance ( tnt_np , p ) )
local damage = ( 4 / dist ) * tntr
2017-07-24 20:36:04 +03:00
if obj : is_player ( ) == true then
2017-08-09 14:38:47 +03:00
if mod_death_messages then
mcl_death_messages.player_damage ( obj , string.format ( " %s was caught in an explosion. " , obj : get_player_name ( ) ) )
end
if mod_hunger then
mcl_hunger.exhaust ( obj : get_player_name ( ) , mcl_hunger.EXHAUST_DAMAGE )
end
2015-06-29 20:55:56 +03:00
end
2017-07-24 22:08:21 +03:00
obj : set_hp ( obj : get_hp ( ) - damage )
2015-06-29 20:55:56 +03:00
end
end
end
end
2017-01-16 15:59:16 +02:00
tnt = { }
tnt.ignite = function ( pos )
minetest.remove_node ( pos )
2017-01-26 12:23:09 +02:00
spawn_tnt ( pos , " mcl_tnt:tnt " )
2017-01-26 20:14:07 +02:00
core.check_for_falling ( pos )
2017-01-16 15:59:16 +02:00
end
2017-03-11 21:54:27 +02:00
local TNT_RANGE = 3
2017-01-16 15:59:16 +02:00
2017-08-09 14:38:47 +03:00
local sounds
if minetest.get_modpath ( " mcl_sounds " ) then
sounds = mcl_sounds.node_sound_wood_defaults ( )
end
2017-01-26 12:23:09 +02:00
minetest.register_node ( " mcl_tnt:tnt " , {
2017-01-16 14:00:20 +02:00
tiles = { " default_tnt_top.png " , " default_tnt_bottom.png " ,
2015-06-29 20:55:56 +03:00
" default_tnt_side.png " , " default_tnt_side.png " ,
" default_tnt_side.png " , " default_tnt_side.png " } ,
2017-01-04 23:36:51 +02:00
is_ground_content = false ,
2015-06-29 20:55:56 +03:00
stack_max = 64 ,
description = " TNT " ,
2017-06-13 15:46:21 +03:00
paramtype = " light " ,
sunlight_propagates = true ,
2017-05-20 03:46:47 +03:00
_doc_items_longdesc = string.format ( " An explosive device. When it explodes, it will hurt living beings, destroy blocks around it, throw blocks affected by gravity all over the place and light fires. A single TNT has an explosion radius of %d. 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 ) ,
2017-03-11 21:54:27 +02:00
_doc_items_usagehelp = " Place the TNT on the ground and ignite it with one of the methods above. Quickly get in safe distance quickly. The TNT will start to be affected by gravity and explodes in 4 seconds. " ,
2017-07-05 21:14:37 +03:00
groups = { dig_immediate = 3 , tnt = 1 , enderman_takable = 1 } ,
2015-06-29 20:55:56 +03:00
mesecons = { effector = {
2017-09-14 03:20:47 +03:00
action_on = tnt.ignite ,
rules = mesecon.rules . alldirs ,
2017-01-05 01:18:59 +02:00
} } ,
2017-08-17 05:12:34 +03:00
_on_ignite = function ( player , pointed_thing )
tnt.ignite ( pointed_thing.under )
2017-09-19 16:45:23 +03:00
return true
2017-05-09 18:49:38 +03:00
end ,
2018-02-01 23:45:19 +02: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 14:38:47 +03:00
sounds = sounds ,
2015-06-29 20:55:56 +03: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 17:23:54 +02: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
self.object : setvelocity ( { x = hdir_x , y = 2 , z = hdir_z } )
2015-06-29 20:55:56 +03:00
self.object : setacceleration ( { x = 0 , y =- 10 , z = 0 } )
2017-01-26 12:27:20 +02:00
self.object : settexturemod ( " ^mcl_tnt_blink.png " )
2015-06-29 20:55:56 +03:00
end
2017-05-30 02:27:45 +03: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 20:55:56 +03:00
function TNT : on_step ( dtime )
local pos = self.object : getpos ( )
2017-02-10 17:03:38 +02:00
minetest.add_particle ( {
pos = { x = pos.x , y = pos.y + 0.5 , z = pos.z } ,
velocity = { x = math.random ( - .1 , .1 ) , y = math.random ( 1 , 2 ) , z = math.random ( - .1 , .1 ) } ,
acceleration = { x = 0 , y =- 0.1 , z = 0 } ,
expirationtime = math.random ( .5 , 1 ) ,
size = math.random ( 1 , 2 ) ,
collisiondetection = false ,
texture = " tnt_smoke.png "
} )
2015-06-29 20:55:56 +03:00
self.timer = self.timer + dtime
self.blinktimer = self.blinktimer + dtime
2017-03-11 22:00:49 +02:00
if self.blinktimer > 0.25 then
self.blinktimer = self.blinktimer - 0.25
2015-06-29 20:55:56 +03:00
if self.blinkstatus then
self.object : settexturemod ( " " )
else
2017-01-26 12:23:09 +02:00
self.object : settexturemod ( " ^mcl_tnt_blink.png " )
2015-06-29 20:55:56 +03:00
end
self.blinkstatus = not self.blinkstatus
end
2017-01-05 01:20:21 +02:00
if self.timer > 4 then
2017-07-31 03:06:00 +03:00
tnt.boom ( self.object : getpos ( ) )
2017-07-25 05:44:46 +03:00
self.object : remove ( )
end
end
tnt.boom = function ( pos , info )
2017-07-31 03:06:00 +03: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 05:44:46 +03: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 )
do_tnt_physics ( pos , range )
local meta = minetest.get_meta ( pos )
local sound
if not info.sound then
sound = " tnt_explode "
else
sound = info.sound
end
minetest.sound_play ( sound , { pos = pos , gain = 1.0 , max_hear_distance = 16 , } )
2017-08-05 01:55:38 +03:00
local node = minetest.get_node ( pos )
2017-08-05 01:58:01 +03:00
if minetest.get_item_group ( " water " ) == 1 or minetest.get_item_group ( " lava " ) == 1 or minetest.is_protected ( pos , " tnt " ) then
2017-07-25 05:44:46 +03:00
-- Cancel the Explosion
return
2015-06-29 20:55:56 +03:00
end
2017-07-25 05:44:46 +03: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 )
minetest.remove_node ( np )
core.check_for_falling ( np )
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 )
2015-06-29 20:55:56 +03:00
end
2017-05-30 02:27:45 +03:00
end
2015-06-29 20:55:56 +03:00
end
end
end
end
end
end
2017-07-25 05:44:46 +03:00
add_effects ( pos , range , { } )
2015-06-29 20:55:56 +03:00
end
end
2017-01-26 12:23:09 +02:00
minetest.register_entity ( " mcl_tnt:tnt " , TNT )
2015-06-29 20:55:56 +03:00
2017-08-09 14:38:47 +03: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 05:36:18 +02:00
if minetest.get_modpath ( " doc_identifier " ) then
doc.sub . identifier.register_object ( " mcl_tnt:tnt " , " nodes " , " mcl_tnt:tnt " )
end