Updated TNT fuse particles.
[MineClone/MineClone2.git] / mods / ITEMS / mcl_tnt / init.lua
blob306c6298da4269ba026e451f13e7a9476a08c5a1
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})
8 return tnt
9 end
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)})
15 end
16 end
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})
26 else
27 if v ~= nil then
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})
29 else
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()))
35 end
36 if mod_hunger then
37 mcl_hunger.exhaust(obj:get_player_name(), mcl_hunger.EXHAUST_DAMAGE)
38 end
39 end
40 obj:set_hp(obj:get_hp() - damage)
41 end
42 end
43 end
44 end
46 tnt = {}
47 tnt.ignite = function(pos)
48 minetest.remove_node(pos)
49 spawn_tnt(pos, "mcl_tnt:tnt")
50 core.check_for_falling(pos)
51 end
53 local TNT_RANGE = 3
55 local sounds
56 if minetest.get_modpath("mcl_sounds") then
57 sounds = mcl_sounds.node_sound_wood_defaults()
58 end
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,
64 stack_max = 64,
65 description = "TNT",
66 paramtype = "light",
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,
74 }},
75 _on_ignite = function(player, pointed_thing)
76 tnt.ignite(pointed_thing.under)
77 return true
78 end,
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()})
83 tnt.ignite(droppos)
84 end
85 end,
86 sounds = sounds,
89 local TNT = {
90 -- Static definition
91 physical = true, -- Collides with things
92 --weight = -100,
93 collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
94 visual = "cube",
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
99 timer = 0,
100 blinktimer = 0,
101 blinkstatus = true,}
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({
114 amount = 64,
115 time = 0.5,
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(),
122 minexptime = 1,
123 maxexptime = 2.5,
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
132 local most = 0
133 for name, stack in pairs(drops) do
134 local count = stack:get_count()
135 if count > most then
136 most = 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({
145 amount = 32,
146 time = 0.1,
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},
152 minexptime = 0.8,
153 maxexptime = 2.0,
154 minsize = radius * 0.66,
155 maxsize = radius * 2,
156 texture = texture,
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 = vector.new(math.random() * 0.2 - 0.1, 1.0 + math.random(), math.random() * 0.2 - 0.1),
166 acceleration = vector.new(0, -0.1, 0),
167 expirationtime = 0.15 + math.random() * 0.25,
168 size = 1.0 + math.random(),
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("")
178 else
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())
185 self.object:remove()
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)
199 local sound
200 if not info.sound then
201 sound = "tnt_explode"
202 else
203 sound = info.sound
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
209 return
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",
247 recipe = {
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")