1 local mod_death_messages
= minetest
.get_modpath("mcl_death_messages")
2 local mod_hunger
= minetest
.get_modpath("mcl_hunger")
4 local function spawn_tnt(pos
, entname
)
5 minetest
.sound_play("tnt_ignite", {pos
= pos
,gain
= 1.0,max_hear_distance
= 15,})
6 local tnt
= minetest
.add_entity(pos
, entname
)
7 tnt
:set_armor_groups({immortal
=1})
11 local function activate_if_tnt(nname
, np
, tnt_np
, tntr
)
12 if nname
== "mcl_tnt:tnt" then
13 local e
= spawn_tnt(np
, nname
)
14 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)})
18 local function do_tnt_physics(tnt_np
,tntr
)
19 local objs
= minetest
.get_objects_inside_radius(tnt_np
, tntr
)
20 for k
, obj
in pairs(objs
) do
21 local ent
= obj
:get_luaentity()
22 local v
= obj
:getvelocity()
23 local p
= obj
:getpos()
24 if ent
and ent
.name
== "mcl_tnt:tnt" then
25 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
})
28 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
})
30 local dist
= math
.max(1, vector
.distance(tnt_np
, p
))
31 local damage
= (4 / dist
) * tntr
32 if obj
:is_player() == true then
33 if mod_death_messages
then
34 mcl_death_messages
.player_damage(obj
, string.format("%s was caught in an explosion.", obj
:get_player_name()))
37 mcl_hunger
.exhaust(obj
:get_player_name(), mcl_hunger
.EXHAUST_DAMAGE
)
40 obj
:set_hp(obj
:get_hp() - damage
)
47 tnt
.ignite
= function(pos
)
48 minetest
.remove_node(pos
)
49 spawn_tnt(pos
, "mcl_tnt:tnt")
50 core
.check_for_falling(pos
)
56 if minetest
.get_modpath("mcl_sounds") then
57 sounds
= mcl_sounds
.node_sound_wood_defaults()
59 minetest
.register_node("mcl_tnt:tnt", {
60 tiles
= {"default_tnt_top.png", "default_tnt_bottom.png",
61 "default_tnt_side.png", "default_tnt_side.png",
62 "default_tnt_side.png", "default_tnt_side.png"},
63 is_ground_content
= false,
67 sunlight_propagates
= true,
68 _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
),
69 _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.",
70 groups
= { dig_immediate
= 3, tnt
= 1, enderman_takable
=1 },
71 mesecons
= {effector
= {
72 action_on
= tnt
.ignite
,
73 rules
= mesecon
.rules
.alldirs
,
75 _on_ignite
= function(player
, pointed_thing
)
76 tnt
.ignite(pointed_thing
.under
)
79 _on_dispense
= function(stack
, pos
, droppos
, dropnode
, dropdir
)
80 -- Place and ignite TNT
81 if minetest
.registered_nodes
[dropnode
.name
].buildable_to
then
82 minetest
.set_node(droppos
, {name
= stack
:get_name()})
91 physical
= true, -- Collides with things
93 collisionbox
= {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
95 textures
= {"default_tnt_top.png", "default_tnt_bottom.png",
96 "default_tnt_side.png", "default_tnt_side.png",
97 "default_tnt_side.png", "default_tnt_side.png"},
98 -- Initial value for our timer
103 function TNT
:on_activate(staticdata
)
104 local phi
= math
.random(0, 65535) / 65535 * 2*math
.pi
105 local hdir_x
= math
.cos(phi
) * 0.02
106 local hdir_z
= math
.sin(phi
) * 0.02
107 self
.object
:setvelocity({x
=hdir_x
, y
=2, z
=hdir_z
})
108 self
.object
:setacceleration({x
=0, y
=-10, z
=0})
109 self
.object
:settexturemod("^mcl_tnt_blink.png")
112 local function add_effects(pos
, radius
, drops
)
113 minetest
.add_particlespawner({
116 minpos
= vector
.subtract(pos
, radius
/ 2),
117 maxpos
= vector
.add(pos
, radius
/ 2),
118 minvel
= {x
= -10, y
= -10, z
= -10},
119 maxvel
= {x
= 10, y
= 10, z
= 10},
120 minacc
= vector
.new(),
121 maxacc
= vector
.new(),
124 minsize
= radius
* 1,
125 maxsize
= radius
* 3,
126 texture
= "tnt_smoke.png",
129 -- we just dropped some items. Look at the items entities and pick
130 -- one of them to use as texture
131 local texture
= "tnt_smoke.png" --fallback texture
133 for name
, stack
in pairs(drops
) do
134 local count
= stack
:get_count()
137 local def
= minetest
.registered_nodes
[name
]
138 if def
and def
.tiles
and def
.tiles
[1] then
139 texture
= def
.tiles
[1]
144 minetest
.add_particlespawner({
147 minpos
= vector
.subtract(pos
, radius
/ 2),
148 maxpos
= vector
.add(pos
, radius
/ 2),
149 minvel
= {x
= -3, y
= 0, z
= -3},
150 maxvel
= {x
= 3, y
= 5, z
= 3},
151 minacc
= {x
= 0, y
= -10, z
= 0},
154 minsize
= radius
* 0.66,
155 maxsize
= radius
* 2,
157 collisiondetection
= true,
161 function TNT
:on_step(dtime
)
162 local pos
= self
.object
:getpos()
163 minetest
.add_particle({
164 pos
= {x
=pos
.x
,y
=pos
.y
+0.5,z
=pos
.z
},
165 velocity
= {x
=math
.random(-.1,.1),y
=math
.random(1,2),z
=math
.random(-.1,.1)},
166 acceleration
= {x
=0,y
=-0.1,z
=0},
167 expirationtime
= math
.random(.5,1),
168 size
= math
.random(1,2),
169 collisiondetection
= false,
170 texture
= "tnt_smoke.png"
172 self
.timer
= self
.timer
+ dtime
173 self
.blinktimer
= self
.blinktimer
+ dtime
174 if self
.blinktimer
> 0.25 then
175 self
.blinktimer
= self
.blinktimer
- 0.25
176 if self
.blinkstatus
then
177 self
.object
:settexturemod("")
179 self
.object
:settexturemod("^mcl_tnt_blink.png")
181 self
.blinkstatus
= not self
.blinkstatus
183 if self
.timer
> 4 then
184 tnt
.boom(self
.object
:getpos())
189 tnt
.boom
= function(pos
, info
)
190 if not info
then info
= {} end
191 local range
= info
.radius
or TNT_RANGE
192 local damage_range
= info
.damage_radius
or TNT_RANGE
194 pos
.x
= math
.floor(pos
.x
+0.5)
195 pos
.y
= math
.floor(pos
.y
+0.5)
196 pos
.z
= math
.floor(pos
.z
+0.5)
197 do_tnt_physics(pos
, range
)
198 local meta
= minetest
.get_meta(pos
)
200 if not info
.sound
then
201 sound
= "tnt_explode"
205 minetest
.sound_play(sound
, {pos
= pos
,gain
= 1.0,max_hear_distance
= 16,})
206 local node
= minetest
.get_node(pos
)
207 if minetest
.get_item_group("water") == 1 or minetest
.get_item_group("lava") == 1 or minetest
.is_protected(pos
, "tnt") then
208 -- Cancel the Explosion
211 for x
=-range
,range
do
212 for y
=-range
,range
do
213 for z
=-range
,range
do
214 if x
*x
+y
*y
+z
*z
<= range
* range
+ range
then
215 local np
={x
=pos
.x
+x
,y
=pos
.y
+y
,z
=pos
.z
+z
}
216 local n
= minetest
.get_node(np
)
217 local def
= minetest
.registered_nodes
[n
.name
]
218 -- Simple blast resistance check (for now). This keeps the important blocks like bedrock, command block, etc. intact.
219 -- TODO: Implement the real blast resistance algorithm
220 if def
and n
.name
~= "air" and n
.name
~= "ignore" and (def
._mcl_blast_resistance
== nil or def
._mcl_blast_resistance
< 1000) then
221 activate_if_tnt(n
.name
, np
, pos
, 3)
222 minetest
.remove_node(np
)
223 core
.check_for_falling(np
)
224 if n
.name
~= "mcl_tnt:tnt" and math
.random() > 0.9 then
225 local drop
= minetest
.get_node_drops(n
.name
, "")
226 for _
,item
in ipairs(drop
) do
227 if type(item
) == "string" then
228 if math
.random(1,100) > 40 then
229 local obj
= minetest
.add_item(np
, item
)
238 add_effects(pos
, range
, {})
242 minetest
.register_entity("mcl_tnt:tnt", TNT
)
244 if minetest
.get_modpath("mcl_mobitems") then
245 minetest
.register_craft({
246 output
= "mcl_tnt:tnt",
248 {'mcl_mobitems:gunpowder','group:sand','mcl_mobitems:gunpowder'},
249 {'group:sand','mcl_mobitems:gunpowder','group:sand'},
250 {'mcl_mobitems:gunpowder','group:sand','mcl_mobitems:gunpowder'}
255 if minetest
.get_modpath("doc_identifier") then
256 doc
.sub
.identifier
.register_object("mcl_tnt:tnt", "nodes", "mcl_tnt:tnt")