280 lines
7.4 KiB
Lua
280 lines
7.4 KiB
Lua
nuke = {} -- mod-global table
|
|
|
|
local all_tnt = {}
|
|
|
|
local creative_is_enabled_for =
|
|
rawget(_G, "creative") and creative.is_enabled_for or function(name)
|
|
return minetest.settings:get_bool("creative_mode")
|
|
end
|
|
|
|
nuke.spawn_tnt = function(pos, entname)
|
|
assert(table.indexof(all_tnt, entname) ~= -1, "attempting to spawn non-tnt")
|
|
minetest.sound_play("nuke_ignite", {pos = pos, gain = 1.0, max_hear_distance = 16})
|
|
return minetest.add_entity(pos, entname)
|
|
end
|
|
|
|
local function expand_tiles(tiles)
|
|
if #tiles >= 6 then
|
|
return tiles
|
|
end
|
|
local t = table.copy(tiles)
|
|
repeat
|
|
t[#t + 1] = t[#t]
|
|
until #t == 6
|
|
return t
|
|
end
|
|
|
|
local function calculate_velocity(distance, tntradius, mult)
|
|
-- d is the distance vector
|
|
-- tntradius - | d |
|
|
-- vel = d * ------------------- * mult
|
|
-- tntradius - 1
|
|
local q = (tntradius - vector.length(distance)) / (tntradius - 1)
|
|
return vector.multiply(vector.multiply(distance, q), mult)
|
|
end
|
|
|
|
local function activate_if_tnt(nodename, nodepos, tntpos, tntradius)
|
|
local explodetime_short = 4 -- seconds
|
|
local explodetime_vary = 1.5
|
|
if table.indexof(all_tnt, nodename) == -1 then
|
|
return
|
|
end
|
|
|
|
local obj = nuke.spawn_tnt(nodepos, nodename)
|
|
obj:setvelocity(calculate_velocity(vector.subtract(nodepos, tntpos), tntradius, {x=3, y=5, z=3}))
|
|
obj:get_luaentity().timer = explodetime_short + math.random(-explodetime_vary, explodetime_vary)
|
|
end
|
|
|
|
local function apply_tnt_physics(tntpos, tntradius)
|
|
local objs = minetest.get_objects_inside_radius(tntpos, tntradius)
|
|
for _, obj in ipairs(objs) do
|
|
local mult = {x=1.5, y=2.5, z=1.5}
|
|
local vel = calculate_velocity(vector.subtract(obj:getpos(), tntpos), tntradius, mult)
|
|
|
|
if obj:is_player() then
|
|
if obj:get_hp() > 0 then
|
|
obj:set_hp(obj:get_hp() - 1)
|
|
end
|
|
if obj.add_player_velocity then
|
|
vel = vector.multiply(vel, 10)
|
|
obj:add_player_velocity(vel)
|
|
end
|
|
else
|
|
if table.indexof(all_tnt, obj:get_entity_name()) ~= -1 then
|
|
vel = vector.multiply(vel, 2) -- apply more knockback to tnt entities
|
|
end
|
|
obj:setvelocity(vector.add(obj:getvelocity(), vel))
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
nuke.register_tnt = function(nodename, def)
|
|
def.explodetime = def.explodetime or 10 -- seconds
|
|
if type(def.tiles) ~= "table" then
|
|
local t = def.tiles
|
|
def.tiles = {t .. "_top.png", t .. "_bottom.png", t .. "_side.png"}
|
|
end
|
|
|
|
-- register node
|
|
minetest.register_node(nodename, {
|
|
tiles = def.tiles,
|
|
diggable = false,
|
|
description = def.description,
|
|
mesecons = {
|
|
effector = {
|
|
action_on = function(pos, node)
|
|
minetest.remove_node(pos)
|
|
nuke.spawn_tnt(pos, node.name)
|
|
minetest.check_for_falling(pos)
|
|
end,
|
|
action_off = function(pos, node) end,
|
|
action_change = function(pos, node) end,
|
|
},
|
|
},
|
|
on_punch = function(pos, node, puncher)
|
|
minetest.remove_node(pos)
|
|
nuke.spawn_tnt(pos, node.name)
|
|
minetest.check_for_falling(pos)
|
|
end,
|
|
})
|
|
|
|
-- register entity
|
|
local entity = {
|
|
physical = true, -- collision
|
|
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
|
visual = "cube",
|
|
textures = expand_tiles(def.tiles),
|
|
health = 1, -- number of punches required to defuse
|
|
|
|
timer = 0,
|
|
blinktimer = 0,
|
|
blinkstatus = true,
|
|
}
|
|
function entity:on_activate(staticdata)
|
|
self.object:setvelocity({x=0, y=4, z=0})
|
|
self.object:setacceleration({x=0, y=-10, z=0}) -- gravity
|
|
self.object:settexturemod("^[brighten")
|
|
end
|
|
function entity:on_step(dtime)
|
|
self.timer = self.timer + dtime
|
|
local mult = 1
|
|
if self.timer > def.explodetime * 0.8 then -- blink faster before explosion
|
|
mult = 4
|
|
elseif self.timer > def.explodetime * 0.5 then
|
|
mult = 2
|
|
end
|
|
self.blinktimer = self.blinktimer + mult * dtime
|
|
|
|
if self.blinktimer > 0.5 then -- actual blinking
|
|
self.blinktimer = self.blinktimer - 0.5
|
|
self.object:settexturemod(self.blinkstatus and "^[brighten" or "")
|
|
self.blinkstatus = not self.blinkstatus
|
|
end
|
|
|
|
if self.timer > def.explodetime then -- boom!
|
|
minetest.sound_play("nuke_explode", {pos = pos, gain = 0.8, max_hear_distance = 32})
|
|
def.on_explode(vector.round(self.object:get_pos()))
|
|
self.object:remove()
|
|
end
|
|
end
|
|
function entity:on_punch(hitter)
|
|
self.health = self.health - 1
|
|
if self.health == 0 then -- give tnt node back if defused
|
|
self.object:remove()
|
|
if not creative_is_enabled_for(hitter:get_player_name()) then
|
|
hitter:get_inventory():add_item("main", nodename)
|
|
end
|
|
end
|
|
end
|
|
minetest.register_entity(nodename, entity)
|
|
|
|
-- save tnt name
|
|
all_tnt[#all_tnt + 1] = nodename
|
|
end
|
|
|
|
|
|
local function on_explode_normal(pos, range)
|
|
local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
|
|
if ndef ~= nil and ndef.groups.water ~= nil then
|
|
apply_tnt_physics(pos, range)
|
|
return -- cancel explosion
|
|
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 nodepos = {x=pos.x+x, y=pos.y+y, z=pos.z+z}
|
|
local n = minetest.get_node(nodepos)
|
|
if n.name ~= "air" and n.name ~= "ignore" then
|
|
activate_if_tnt(n.name, nodepos, pos, range)
|
|
minetest.remove_node(nodepos)
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|
|
apply_tnt_physics(pos, range)
|
|
end
|
|
|
|
local function on_explode_split(pos, range, entname)
|
|
for x=-range, range do
|
|
for z=-range, range do
|
|
if x*x+z*z <= range * range then
|
|
|
|
local nodepos = vector.add(pos, {x=x, y=0, z=z})
|
|
minetest.add_entity(nodepos, entname)
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- Iron TNT
|
|
|
|
nuke.register_tnt("nuke:iron_tnt", {
|
|
description = "Iron TNT",
|
|
tiles = "nuke_iron_tnt",
|
|
on_explode = function(pos)
|
|
on_explode_normal(pos, 6)
|
|
end,
|
|
})
|
|
|
|
minetest.register_craft({
|
|
output = "nuke:iron_tnt 4",
|
|
recipe = {
|
|
{"", "group:wood", ""},
|
|
{"default:steel_ingot", "default:coal_lump", "default:steel_ingot"},
|
|
{"", "group:wood", ""},
|
|
}
|
|
})
|
|
|
|
|
|
nuke.register_tnt("nuke:iron_tntx", {
|
|
description = "Extreme Iron TNT",
|
|
tiles = {"nuke_iron_tnt_top.png", "nuke_iron_tnt_bottom.png", "nuke_iron_tnt_side_x.png"},
|
|
on_explode = function(pos)
|
|
on_explode_split(pos, 3, "nuke:iron_tnt")
|
|
end,
|
|
})
|
|
|
|
minetest.register_craft({
|
|
output = "nuke:iron_tntx 1",
|
|
recipe = {
|
|
{"", "default:coal_lump", ""},
|
|
{"default:coal_lump", "nuke:iron_tnt", "default:coal_lump"},
|
|
{"", "default:coal_lump", ""},
|
|
}
|
|
})
|
|
|
|
-- Mese TNT
|
|
|
|
nuke.register_tnt("nuke:mese_tnt", {
|
|
description = "Mese TNT",
|
|
tiles = "nuke_mese_tnt",
|
|
on_explode = function(pos)
|
|
on_explode_normal(pos, 12)
|
|
end,
|
|
})
|
|
|
|
minetest.register_craft({
|
|
output = "nuke:mese_tnt 4",
|
|
recipe = {
|
|
{"", "group:wood", ""},
|
|
{"default:mese_crystal", "default:coal_lump", "default:mese_crystal"},
|
|
{"", "group:wood", ""},
|
|
}
|
|
})
|
|
|
|
|
|
nuke.register_tnt("nuke:mese_tntx", {
|
|
description = "Extreme Mese TNT",
|
|
tiles = {"nuke_mese_tnt_top.png", "nuke_mese_tnt_bottom.png", "nuke_mese_tnt_side_x.png"},
|
|
on_explode = function(pos)
|
|
on_explode_split(pos, 3, "nuke:mese_tnt")
|
|
end,
|
|
})
|
|
|
|
minetest.register_craft({
|
|
output = "nuke:mese_tntx 1",
|
|
recipe = {
|
|
{"", "default:coal_lump", ""},
|
|
{"default:coal_lump", "nuke:mese_tnt", "default:coal_lump"},
|
|
{"", "default:coal_lump", ""},
|
|
}
|
|
})
|
|
|
|
-- Compatibility aliases
|
|
|
|
minetest.register_alias("nuke:hardcore_iron_tnt", "nuke:iron_tntx")
|
|
minetest.register_alias("nuke:hardcore_mese_tnt", "nuke:mese_tntx")
|
|
|
|
|
|
if minetest.settings:get_bool("log_mods") then
|
|
print("[Nuke] Loaded")
|
|
end
|