nuke/init.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