199 lines
6.4 KiB
Lua
199 lines
6.4 KiB
Lua
-- Delay in seconds after which TNT falls down when stepping on it directly
|
|
local TNT_FALL_DELAY_DIRECT = 0.25
|
|
-- Delay in seconds after which TNT falls down when stepping on it indirectly via sneaking
|
|
local TNT_FALL_DELAY_SNEAK = 0.5 -- it's intentional that it's a bit longer
|
|
-- Distance from TNT node a player can be so it is still considered "stepped on"
|
|
-- by the game. The distance takes X and Z into account separately but not Z.
|
|
-- Change this if Luanti changes the sneak distance; works only for sneak distance below 0.5
|
|
local SNEAK_DISTANCE = 0.3
|
|
-- Time in seconds after which to execute tnt_loop() again
|
|
local TNT_LOOP_TIME = 0.1
|
|
-- Time in seconds after which the TNT entity dies
|
|
local TIME_UNTIL_DESPAWN = 5
|
|
-- gravity for particles
|
|
local GRAVITY = 9.81
|
|
-- Animation settings for TNT fuse
|
|
local TNT_FUSE_ANIMATION_FRAMES = 4 -- number of frames
|
|
local TNT_FUSE_ANIMATION_FRAME_LENGTH = 0.25 -- length of a frame in seconds
|
|
|
|
-- Translation boilerplate
|
|
local S = minetest.get_translator("tntrun")
|
|
|
|
local tnt_entity_base_textures = {
|
|
"tntrun_tnt_top_burning_animated.png^[verticalframe:"..TNT_FUSE_ANIMATION_FRAMES..":0",
|
|
"tntrun_tnt_bottom.png", "tntrun_tnt_side.png", "tntrun_tnt_side.png", "tntrun_tnt_side.png", "tntrun_tnt_side.png"
|
|
}
|
|
|
|
minetest.register_entity("tntrun:tnt_falling", {
|
|
initial_properties = {
|
|
physical = true,
|
|
collide_with_objects = true,
|
|
pointable = true,
|
|
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
|
selectionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
|
visual = "cube",
|
|
visual_size = {x = 1, y = 1, z = 1},
|
|
textures = tnt_entity_base_textures,
|
|
static_save = false,
|
|
},
|
|
_timer = 0,
|
|
_sound_handle = nil,
|
|
_last_anim_frame = 0,
|
|
on_activate = function(self)
|
|
self.object:set_armor_groups({immortal=1})
|
|
local pitch = 1 + math.random(-50,50)*0.001
|
|
self._sound_handle = minetest.sound_play({name="tntrun_fuse"},
|
|
{object=self.object, max_hear_distance=16, gain=0.3, pitch=pitch})
|
|
end,
|
|
on_deactivate = function(self)
|
|
if self._sound_handle then
|
|
minetest.sound_stop(self._sound_handle)
|
|
end
|
|
end,
|
|
on_step = function(self, dtime, moveresult)
|
|
self.object:set_velocity({x = 0, y = -5, z = 0 })
|
|
|
|
-- Spawn particles when TNT is removed
|
|
local poof = function(pos)
|
|
-- TNT break particles
|
|
minetest.add_particlespawner({
|
|
amount = math.random(15, 30),
|
|
time = 0.01,
|
|
minpos = vector.add(pos, {x=-0.4, y=-0.5, z=-0.4}),
|
|
maxpos = vector.add(pos, {x=0.4, y=0.5, z=0.4}),
|
|
minvel = {x=-3.5, y=-0.1, z=-3.5},
|
|
maxvel = {x=3.5, y=1.0, z=3.5},
|
|
drag = { x=2, y=0, z=2 },
|
|
minacc = {x=0, y=-GRAVITY, z=0},
|
|
maxacc = {x=0, y=-GRAVITY, z=0},
|
|
minexptime = 0.2,
|
|
maxexptime = 0.7,
|
|
minsize = 1.5,
|
|
maxsize = 2.0,
|
|
collisiondetection = true,
|
|
vertical = false,
|
|
node = {name="tntrun:tnt"},
|
|
})
|
|
-- Smoke
|
|
minetest.add_particle({
|
|
pos = vector.add(pos, {x=0, y=0.2, z=0}),
|
|
velocity = { x=0, y=0.5+math.random(0,5)*0.1, z=0 },
|
|
expirationtime = 1.5,
|
|
size = 7.0,
|
|
collisiondetection = false,
|
|
vertical = false,
|
|
texture = "tntrun_smoke_puff.png",
|
|
})
|
|
end
|
|
|
|
local checkpos = self.object:get_pos()
|
|
local tntpos = table.copy(checkpos)
|
|
checkpos.y = checkpos.y - 1
|
|
local node_below = minetest.registered_nodes[minetest.get_node(checkpos).name]
|
|
-- Remove TNT when it collides with node (might be an unknown node, hence the first check)
|
|
if node_below and node_below.walkable then
|
|
poof(tntpos)
|
|
self.object:remove()
|
|
return
|
|
end
|
|
-- Remove TNT after timer
|
|
if self._timer > TIME_UNTIL_DESPAWN then
|
|
poof(tntpos)
|
|
self.object:remove()
|
|
return
|
|
else
|
|
self._timer = self._timer + dtime
|
|
end
|
|
|
|
-- Animate texture manually
|
|
-- TODO: Replace this code once Luanti allows to animate
|
|
-- entity textures natively.
|
|
local frametimer = self._timer * (1/TNT_FUSE_ANIMATION_FRAME_LENGTH)
|
|
local frame = math.floor(frametimer) % TNT_FUSE_ANIMATION_FRAMES
|
|
if frame ~= self._last_anim_frame then
|
|
local textures = table.copy(tnt_entity_base_textures)
|
|
textures[1] = "tntrun_tnt_top_burning_animated.png^[verticalframe:"..TNT_FUSE_ANIMATION_FRAMES..":"..frame
|
|
self.object:set_properties({
|
|
textures = textures,
|
|
})
|
|
-- Remember the last frame so we only update the
|
|
-- textures property if neccessary
|
|
self._last_anim_frame = frame
|
|
end
|
|
end,
|
|
})
|
|
|
|
local sounds
|
|
if minetest.get_modpath("default") then
|
|
sounds = default.node_sound_wood_defaults()
|
|
end
|
|
|
|
minetest.register_node("tntrun:tnt",{
|
|
tiles = {"tntrun_tnt_top.png", "tntrun_tnt_bottom.png", "tntrun_tnt_side.png"},
|
|
groups = {cracky = 3, not_in_creative_inventory = 1},
|
|
sounds = sounds,
|
|
description = S("TNT in Tntrun"),
|
|
drops = "tntrun:tnt",
|
|
on_punch = function(pos, node, puncher)
|
|
if puncher:get_wielded_item():get_name() == "tntrun:torch" then
|
|
minetest.remove_node(pos)
|
|
minetest.add_entity(pos, "tntrun:tnt_falling")
|
|
end
|
|
end,
|
|
})
|
|
|
|
minetest.register_craftitem("tntrun:torch",{
|
|
inventory_image = "tntrun_torch.png",
|
|
description = S("Torch for TNT"),
|
|
groups = { not_in_creative_inventory = 1 },
|
|
sound = {
|
|
punch_use_air = { name = "tntrun_swish", gain = 0.2 },
|
|
},
|
|
})
|
|
|
|
|
|
local function tnt_loop(arena)
|
|
if not arena.in_game then return end
|
|
for pl_name, _ in pairs(arena.players) do
|
|
local player = minetest.get_player_by_name(pl_name)
|
|
local pos = player:get_pos()
|
|
pos.y = pos.y - 1
|
|
if minetest.get_node(pos).name == "tntrun:tnt" then
|
|
-- TNT falls if player stands directly on it
|
|
pos.x = math.floor(pos.x+0.5)
|
|
pos.z = math.floor(pos.z+0.5)
|
|
minetest.after(TNT_FALL_DELAY_DIRECT, function()
|
|
if minetest.get_node(pos).name ~= "tntrun:tnt" then
|
|
return
|
|
end
|
|
minetest.remove_node(pos)
|
|
minetest.add_entity(pos, "tntrun:tnt_falling")
|
|
end)
|
|
elseif minetest.get_node(pos).name == "air" then
|
|
-- When player does not stand directly on TNT, check for TNT around the player
|
|
-- within SNEAK_DISTANCE
|
|
for x = -1,1 do
|
|
for z = -1,1 do
|
|
local posb = {x = pos.x+(x*SNEAK_DISTANCE), y = pos.y, z = pos.z+(z*SNEAK_DISTANCE)}
|
|
if minetest.get_node(posb).name == "tntrun:tnt" then
|
|
posb.x = math.floor(posb.x+0.5)
|
|
posb.z = math.floor(posb.z+0.5)
|
|
minetest.after(TNT_FALL_DELAY_SNEAK, function()
|
|
if minetest.get_node(posb).name ~= "tntrun:tnt" then
|
|
return
|
|
end
|
|
minetest.remove_node(posb)
|
|
minetest.add_entity(posb, "tntrun:tnt_falling")
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
minetest.after(TNT_LOOP_TIME, function() tnt_loop(arena) end)
|
|
end
|
|
|
|
arena_lib.on_start("tntrun", function(arena)
|
|
tnt_loop(arena)
|
|
end)
|