VoxeLibre/mods/ITEMS/mcl_tnt/init.lua

255 lines
8.3 KiB
Lua

local S = minetest.get_translator("mcl_tnt")
local mod_death_messages = minetest.get_modpath("mcl_death_messages")
local function spawn_tnt(pos, entname)
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})
return tnt
end
local function activate_if_tnt(nname, np, tnt_np, tntr)
if nname == "mcl_tnt:tnt" then
local e = spawn_tnt(np, nname)
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)})
end
end
local function do_tnt_physics(tnt_np,tntr)
local objs = minetest.get_objects_inside_radius(tnt_np, tntr)
for k, obj in pairs(objs) do
local ent = obj:get_luaentity()
local v = obj:get_velocity()
local p = obj:get_pos()
if ent and ent.name == "mcl_tnt:tnt" then
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})
else
if v ~= nil then
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})
else
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, string.format("%s was caught in an explosion.", obj:get_player_name()))
end
end
obj:set_hp(obj:get_hp() - damage)
end
end
end
end
tnt = {}
tnt.ignite = function(pos)
minetest.remove_node(pos)
spawn_tnt(pos, "mcl_tnt:tnt")
core.check_for_falling(pos)
end
local TNT_RANGE = 3
local sounds
if minetest.get_modpath("mcl_sounds") then
sounds = mcl_sounds.node_sound_wood_defaults()
end
minetest.register_node("mcl_tnt:tnt", {
tiles = {"default_tnt_top.png", "default_tnt_bottom.png",
"default_tnt_side.png", "default_tnt_side.png",
"default_tnt_side.png", "default_tnt_side.png"},
is_ground_content = false,
stack_max = 64,
description = S("TNT"),
paramtype = "light",
sunlight_propagates = true,
_doc_items_longdesc = S("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 @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),
_doc_items_usagehelp = S("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."),
groups = { dig_immediate = 3, tnt = 1, enderman_takable=1 },
mesecons = {effector = {
action_on = tnt.ignite,
rules = mesecon.rules.alldirs,
}},
_on_ignite = function(player, pointed_thing)
tnt.ignite(pointed_thing.under)
return true
end,
_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,
sounds = sounds,
})
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)
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: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")
end
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
function TNT:on_step(dtime)
local pos = self.object:get_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"
})
self.timer = self.timer + dtime
self.blinktimer = self.blinktimer + dtime
if self.blinktimer > 0.25 then
self.blinktimer = self.blinktimer - 0.25
if self.blinkstatus then
self.object:set_texture_mod("")
else
self.object:set_texture_mod("^mcl_tnt_blink.png")
end
self.blinkstatus = not self.blinkstatus
end
if self.timer > 4 then
tnt.boom(self.object:get_pos())
self.object:remove()
end
end
tnt.boom = function(pos, info)
if not info then info = {} end
local range = info.radius or TNT_RANGE
local damage_range = info.damage_radius or TNT_RANGE
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,})
local node = minetest.get_node(pos)
if minetest.get_item_group("water") == 1 or minetest.get_item_group("lava") == 1 then
-- Cancel the Explosion
return
end
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)
end
end
end
end
end
end
end
end
add_effects(pos, range, {})
end
end
minetest.register_entity("mcl_tnt:tnt", TNT)
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
if minetest.get_modpath("doc_identifier") then
doc.sub.identifier.register_object("mcl_tnt:tnt", "nodes", "mcl_tnt:tnt")
end