Rewrite TNT

This commit is contained in:
ShadowNinja 2014-07-13 18:08:03 -04:00
parent c7330eb153
commit 0822a3df13
4 changed files with 241 additions and 247 deletions

@ -1,27 +1,18 @@
=== TNT-MOD for MINETEST-C55 === === TNT mod for Minetest ===
by PilzAdam by PilzAdam and ShadowNinja
Introduction: Introduction:
This mod adds TNT to Minetest. TNT is a tool to help the player This mod adds TNT to Minetest. TNT is a tool to help the player
in mining. in mining.
How to install:
Unzip the archive an place it in minetest-base-directory/mods/minetest/
if you have a windows client or a linux run-in-place client. If you have
a linux system-wide instalation place it in ~/.minetest/mods/minetest/.
If you want to install this mod only in one world create the folder
worldmods/ in your worlddirectory.
For further information or help see:
http://wiki.minetest.com/wiki/Installing_Mods
How to use the mod: How to use the mod:
Craft gunpowder by placing coal and gravel in the crafting area. The Craft gunpowder by placing coal and gravel in the crafting area. The
gunpowder can be used to craft TNT or as fuze for TNT. To craft TNT gunpowder can be used to craft TNT or as fuze for TNT. To craft TNT
surround gunpowder with 4 wood in a + shape. surround gunpowder with 4 wood in a + shape.
There are different ways to blow up TNT: There are different ways to blow up TNT:
1. Hit it with a torch. 1. Hit it with a torch.
2. Hit a gunpowder fuze that leads to a TNT block with a torch. 2. Hit a gunpowder fuze that leads to a TNT block with a torch.
3. Activate it with mesecons (fastest way) 3. Activate it with mesecons (fastest way)
Be aware of the damage radius of 7 blocks! Be aware of the damage radius of 7 blocks!
License: License:

@ -1,2 +1,3 @@
default default
fire fire

464
init.lua

@ -1,197 +1,222 @@
-- Default to enabled in singleplayer and disabled in multiplayer
local singleplayer = minetest.is_singleplayer()
local setting = minetest.setting_getbool("enable_tnt")
if (not singleplayer and setting ~= false) or
(singleplayer and setting ~= true) then
return
end
-- loss probabilities array (one in X will be lost) -- loss probabilities array (one in X will be lost)
local loss_prob = {} local loss_prob = {}
loss_prob["default:cobble"] = 3 loss_prob["default:cobble"] = 3
loss_prob["default:dirt"] = 4 loss_prob["default:dirt"] = 4
local radius_max = tonumber(minetest.setting_get("tnt_radius_max") or 25) local radius = tonumber(minetest.setting_get("tnt_radius") or 3)
local eject_drops = function(pos, stack) -- Fill a list with data for content IDs, after all nodes are registered
local obj = minetest.add_item(pos, stack) local cid_data = {}
minetest.after(0, function()
if obj == nil then for name, def in pairs(minetest.registered_nodes) do
return cid_data[minetest.get_content_id(name)] = {
name = name,
drops = def.drops,
flammable = def.groups.flammable,
}
end end
obj:get_luaentity().collect = true end)
obj:setacceleration({x=0, y=-10, z=0})
obj:setvelocity({x=math.random(0,6)-3, y=10, z=math.random(0,6)-3}) local function rand_pos(center, pos, radius)
pos.x = center.x + math.random(-radius, radius)
pos.z = center.z + math.random(-radius, radius)
end end
local add_drop = function(drops, pos, item) local function eject_drops(drops, pos, radius)
if loss_prob[item] ~= nil then local drop_pos = vector.new(pos)
if math.random(1,loss_prob[item]) == 1 then for _, item in pairs(drops) do
return local count = item:get_count()
local max = item:get_stack_max()
if count > max then
item:set_count(max)
end
while count > 0 do
if count < max then
item:set_count(count)
end
rand_pos(pos, drop_pos, radius)
local obj = minetest.add_item(drop_pos, item)
if obj then
obj:get_luaentity().collect = true
obj:setacceleration({x=0, y=-10, z=0})
obj:setvelocity({x=math.random(-3, 3), y=10,
z=math.random(-3, 3)})
end
count = count - max
end end
end end
end
if drops[item] == nil then local function add_drop(drops, item)
drops[item] = ItemStack(item) item = ItemStack(item)
else local name = item:get_name()
drops[item]:add_item(item) if loss_prob[name] ~= nil and math.random(1, loss_prob[name]) == 1 then
return
end end
if drops[item]:get_free_space() == 0 then local drop = drops[name]
stack = drops[item] if drop == nil then
eject_drops(pos, stack) drops[name] = item
drops[item] = nil else
drop:set_count(drop:get_count() + item:get_count())
end end
end end
local function destroy(drops, pos, last, fast) local fire_node = {name="fire:basic_flame"}
local function destroy(drops, pos, cid)
if minetest.is_protected(pos, "") then if minetest.is_protected(pos, "") then
return return
end end
local def = cid_data[cid]
local nodename = minetest.get_node(pos).name if def and def.flammable then
if nodename ~= "air" then minetest.set_node(pos, fire_node)
minetest.remove_node(pos, (fast and 1 or 0)) else
if last then minetest.remove_node(pos)
nodeupdate(pos) if def then
end local node_drops = minetest.get_node_drops(def.name, "")
if minetest.registered_nodes[nodename].groups.flammable ~= nil then for _, item in ipairs(node_drops) do
minetest.set_node(pos, {name="fire:basic_flame"}, (fast and 2 or 0)) add_drop(drops, item)
return
end
local drop = minetest.get_node_drops(nodename, "")
for _,item in ipairs(drop) do
if type(item) == "string" then
add_drop(drops, pos, item)
else
for i=1,item:get_count() do
add_drop(drops, pos, item:get_name())
end
end end
end end
end end
end end
boom = function(pos, time)
minetest.after(time, function(pos) local function calc_velocity(pos1, pos2, old_vel, power)
if minetest.get_node(pos).name ~= "tnt:tnt_burning" then local vel = vector.direction(pos1, pos2)
return vel = vector.normalize(vel)
vel = vector.multiply(vel, power)
-- Divide by distance
local dist = vector.distance(pos1, pos2)
dist = math.max(dist, 1)
vel = vector.divide(vel, dist)
-- Add old velocity
vel = vector.add(vel, old_vel)
return vel
end
local function entity_physics(pos, radius)
-- Make the damage radius larger than the destruction radius
radius = radius * 2
local objs = minetest.get_objects_inside_radius(pos, radius)
for _, obj in pairs(objs) do
local obj_pos = obj:getpos()
local obj_vel = obj:getvelocity()
local dist = math.max(1, vector.distance(pos, obj_pos))
if obj_vel ~= nil then
obj:setvelocity(calc_velocity(pos, obj_pos,
obj_vel, radius * 10))
end end
minetest.sound_play("tnt_explode", {pos=pos, gain=1.5, max_hear_distance=2*64})
minetest.set_node(pos, {name="tnt:boom"})
minetest.after(0.5, function(pos)
minetest.remove_node(pos)
end, {x=pos.x, y=pos.y, z=pos.z})
local damage = (4 / dist) * radius
obj:set_hp(obj:get_hp() - damage)
end
end
local radius = 2 local function add_effects(pos, radius)
local drops = {} minetest.add_particlespawner({
local list = {} amount = 128,
local dr = 0 time = 1,
local tnts = 1 minpos = vector.subtract(pos, radius / 2),
local destroyed = 0 maxpos = vector.add(pos, radius / 2),
while dr<radius do minvel = {x=-20, y=-20, z=-20},
dr=dr+1 maxvel = {x=20, y=20, z=20},
for dx=-dr,dr,dr*2 do minacc = vector.new(),
for dy=-dr,dr,1 do maxacc = vector.new(),
for dz=-dr,dr,1 do minexptime = 1,
table.insert(list, {x=dx, y=dy, z=dz}) maxexptime = 3,
end minsize = 8,
end maxsize = 16,
texture = "tnt_smoke.png",
})
end
local function burn(pos)
local name = minetest.get_node(pos).name
if name == "tnt:tnt" then
minetest.sound_play("tnt_ignite", {pos=pos})
minetest.set_node(pos, {name="tnt:tnt_burning"})
minetest.get_node_timer(pos):start(1)
elseif name == "tnt:gunpowder" then
minetest.sound_play("tnt_gunpowder_burning", {pos=pos, gain=2})
minetest.set_node(pos, {name="tnt:gunpowder_burning"})
minetest.get_node_timer(pos):start(1)
end
end
local function explode(pos, radius)
local pos = vector.round(pos)
local vm = VoxelManip()
local pr = PseudoRandom(os.time())
local p1 = vector.subtract(pos, radius)
local p2 = vector.add(pos, radius)
local minp, maxp = vm:read_from_map(p1, p2)
local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
local data = vm:get_data()
local drops = {}
local p = {}
local c_air = minetest.get_content_id("air")
local c_tnt = minetest.get_content_id("tnt:tnt")
local c_tnt_burning = minetest.get_content_id("tnt:tnt_burning")
local c_gunpowder = minetest.get_content_id("tnt:gunpowder")
local c_gunpowder_burning = minetest.get_content_id("tnt:gunpowder_burning")
local c_boom = minetest.get_content_id("tnt:boom")
local c_fire = minetest.get_content_id("fire:basic_flame")
for z = -radius, radius do
for y = -radius, radius do
local vi = a:index(pos.x + (-radius), pos.y + y, pos.z + z)
for x = -radius, radius do
if (x * x) + (y * y) + (z * z) <=
(radius * radius) + pr:next(-radius, radius) then
local cid = data[vi]
p.x = pos.x + x
p.y = pos.y + y
p.z = pos.z + z
if cid == c_tnt or cid == c_gunpowder then
burn(p)
elseif cid ~= c_tnt_burning and
cid ~= c_gunpowder_burning and
cid ~= c_air and
cid ~= c_fire and
cid ~= c_boom then
destroy(drops, p, cid)
end end
for dy=-dr,dr,dr*2 do
for dx=-dr+1,dr-1,1 do
for dz=-dr,dr,1 do
table.insert(list, {x=dx, y=dy, z=dz})
end
end
end
for dz=-dr,dr,dr*2 do
for dx=-dr+1,dr-1,1 do
for dy=-dr+1,dr-1,1 do
table.insert(list, {x=dx, y=dy, z=dz})
end
end
end
for _,p in ipairs(list) do
local np = {x=pos.x+p.x, y=pos.y+p.y, z=pos.z+p.z}
local node = minetest.get_node(np)
if node.name == "air" then
elseif node.name == "tnt:tnt" or node.name == "tnt:tnt_burning" then
if radius < radius_max then
if radius <= 5 then
radius = radius + 1
elseif radius <= 10 then
radius = radius + 0.5
else
radius = radius + 0.3
end
minetest.remove_node(np, 2)
tnts = tnts + 1
else
minetest.set_node(np, {name="tnt:tnt_burning"})
boom(np, 1)
end
elseif node.name == "fire:basic_flame"
--or string.find(node.name, "default:water_")
--or string.find(node.name, "default:lava_")
or node.name == "tnt:boom"
then
else
if math.abs(p.x)<2 and math.abs(p.y)<2 and math.abs(p.z)<2 then
destroy(drops, np, dr == radius, radius > 7)
destroyed = destroyed + 1
else
if math.random(1,5) <= 4 then
destroy(drops, np, dr == radius, radius > 7)
destroyed = destroyed + 1
end
end
end
end
end end
vi = vi + 1
end
end
end
local objects = minetest.get_objects_inside_radius(pos, radius*2) return drops
for _,obj in ipairs(objects) do end
--if obj:is_player() or (obj:get_luaentity() and obj:get_luaentity().name ~= "__builtin:item") then
local p = obj:getpos()
local v = obj:getvelocity()
local vec = {x=p.x-pos.x, y=p.y-pos.y, z=p.z-pos.z}
local dist = (vec.x^2+vec.y^2+vec.z^2)^0.5
local damage = ((radius*20)/dist)
--print("DMG dist="..dist.." damage="..damage)
if obj:is_player() or (obj:get_luaentity() and obj:get_luaentity().name ~= "__builtin:item") then
obj:punch(obj, 1.0, {
full_punch_interval=1.0,
damage_groups={fleshy=damage},
}, vec)
end
if v ~= nil then
--obj:setvelocity({x=(p.x - pos.x) + (radius / 4) + v.x, y=(p.y - pos.y) + (radius / 2) + v.y, z=(p.z - pos.z) + (radius / 4) + v.z})
obj:setvelocity({x=(p.x - pos.x) + (radius / 2) + v.x, y=(p.y - pos.y) + radius + v.y, z=(p.z - pos.z) + (radius / 2) + v.z})
end
--end
end
print("TNT exploded=" .. tnts .. " radius=" .. radius .. " destroyed="..destroyed) local function boom(pos)
minetest.sound_play("tnt_explode", {pos=pos, gain=1.5, max_hear_distance=2*64})
minetest.set_node(pos, {name="tnt:boom"})
minetest.get_node_timer(pos):start(0.5)
for _,stack in pairs(drops) do local drops = explode(pos, radius)
eject_drops(pos, stack) entity_physics(pos, radius)
end eject_drops(drops, pos, radius)
local radiusp = radius+1 add_effects(pos, radius)
minetest.add_particlespawner(
100, --amount
0.1, --time
{x=pos.x-radiusp, y=pos.y-radiusp, z=pos.z-radiusp}, --minpos
{x=pos.x+radiusp, y=pos.y+radiusp, z=pos.z+radiusp}, --maxpos
{x=-0, y=-0, z=-0}, --minvel
{x=0, y=0, z=0}, --maxvel
{x=-0.5,y=5,z=-0.5}, --minacc
{x=0.5,y=5,z=0.5}, --maxacc
0.1, --minexptime
1, --maxexptime
8, --minsize
15, --maxsize
false, --collisiondetection
"tnt_smoke.png" --texture
)
end, pos)
end end
minetest.register_node("tnt:tnt", { minetest.register_node("tnt:tnt", {
@ -199,30 +224,32 @@ minetest.register_node("tnt:tnt", {
tiles = {"tnt_top.png", "tnt_bottom.png", "tnt_side.png"}, tiles = {"tnt_top.png", "tnt_bottom.png", "tnt_side.png"},
groups = {dig_immediate=2, mesecon=2}, groups = {dig_immediate=2, mesecon=2},
sounds = default.node_sound_wood_defaults(), sounds = default.node_sound_wood_defaults(),
on_punch = function(pos, node, puncher) on_punch = function(pos, node, puncher)
if puncher:get_wielded_item():get_name() == "default:torch" then if puncher:get_wielded_item():get_name() == "default:torch" then
minetest.sound_play("tnt_ignite", {pos=pos}) minetest.sound_play("tnt_ignite", {pos=pos})
minetest.set_node(pos, {name="tnt:tnt_burning"}) minetest.set_node(pos, {name="tnt:tnt_burning"})
boom(pos, 4) minetest.get_node_timer(pos):start(4)
end end
end, end,
mesecons = {effector = {action_on = boom}},
mesecons = {
effector = {
action_on = function(pos, node)
minetest.set_node(pos, {name="tnt:tnt_burning"})
boom(pos, 0)
end
},
},
}) })
minetest.register_node("tnt:tnt_burning", { minetest.register_node("tnt:tnt_burning", {
tiles = {{name="tnt_top_burning_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=1}}, "tnt_bottom.png", "tnt_side.png"}, tiles = {
{
name = "tnt_top_burning_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 1,
}
},
"tnt_bottom.png", "tnt_side.png"},
light_source = 5, light_source = 5,
drop = "", drop = "",
sounds = default.node_sound_wood_defaults(), sounds = default.node_sound_wood_defaults(),
on_timer = boom,
}) })
minetest.register_node("tnt:boom", { minetest.register_node("tnt:boom", {
@ -232,54 +259,11 @@ minetest.register_node("tnt:boom", {
walkable = false, walkable = false,
drop = "", drop = "",
groups = {dig_immediate=3}, groups = {dig_immediate=3},
on_timer = function(pos, elapsed)
minetest.remove_node(pos)
end,
}) })
burn = function(pos)
if minetest.get_node(pos).name == "tnt:tnt" then
minetest.sound_play("tnt_ignite", {pos=pos})
minetest.set_node(pos, {name="tnt:tnt_burning"})
boom(pos, 1)
return
end
if minetest.get_node(pos).name ~= "tnt:gunpowder" then
return
end
minetest.sound_play("tnt_gunpowder_burning", {pos=pos, gain=2})
minetest.set_node(pos, {name="tnt:gunpowder_burning"})
minetest.after(1, function(pos)
if minetest.get_node(pos).name ~= "tnt:gunpowder_burning" then
return
end
minetest.after(0.5, function(pos)
minetest.remove_node(pos)
end, {x=pos.x, y=pos.y, z=pos.z})
for dx=-1,1 do
for dz=-1,1 do
for dy=-1,1 do
pos.x = pos.x+dx
pos.y = pos.y+dy
pos.z = pos.z+dz
if not (math.abs(dx) == 1 and math.abs(dz) == 1) then
if dy == 0 then
burn({x=pos.x, y=pos.y, z=pos.z})
else
if math.abs(dx) == 1 or math.abs(dz) == 1 then
burn({x=pos.x, y=pos.y, z=pos.z})
end
end
end
pos.x = pos.x-dx
pos.y = pos.y-dy
pos.z = pos.z-dz
end
end
end
end, pos)
end
minetest.register_node("tnt:gunpowder", { minetest.register_node("tnt:gunpowder", {
description = "Gun Powder", description = "Gun Powder",
drawtype = "raillike", drawtype = "raillike",
@ -309,7 +293,15 @@ minetest.register_node("tnt:gunpowder_burning", {
sunlight_propagates = true, sunlight_propagates = true,
walkable = false, walkable = false,
light_source = 5, light_source = 5,
tiles = {{name="tnt_gunpowder_burning_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=1}}}, tiles = {{
name = "tnt_gunpowder_burning_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 1,
}
}},
selection_box = { selection_box = {
type = "fixed", type = "fixed",
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
@ -317,21 +309,30 @@ minetest.register_node("tnt:gunpowder_burning", {
drop = "", drop = "",
groups = {dig_immediate=2,attached_node=1}, groups = {dig_immediate=2,attached_node=1},
sounds = default.node_sound_leaves_defaults(), sounds = default.node_sound_leaves_defaults(),
on_timer = function(pos, elapsed)
for dx = -1, 1 do
for dz = -1, 1 do
for dy = -1, 1 do
if not (dx == 0 and dz == 0) then
burn({
x = pos.x + dx,
y = pos.y + dy,
z = pos.z + dz,
})
end
end
end
end
minetest.remove_node(pos)
end
}) })
minetest.register_abm({ minetest.register_abm({
nodenames = {"tnt:tnt", "tnt:gunpowder"}, nodenames = {"tnt:tnt", "tnt:gunpowder"},
neighbors = {"fire:basic_flame", "default:lava_source", "default:lava_flowing"}, neighbors = {"fire:basic_flame", "default:lava_source", "default:lava_flowing"},
interval = 2, interval = 1,
chance = 10, chance = 1,
action = function(pos, node) action = burn,
if node.name == "tnt:tnt" then
minetest.set_node(pos, {name="tnt:tnt_burning"})
boom({x=pos.x, y=pos.y, z=pos.z}, 0)
else
burn(pos)
end
end
}) })
minetest.register_craft({ minetest.register_craft({
@ -343,12 +344,13 @@ minetest.register_craft({
minetest.register_craft({ minetest.register_craft({
output = "tnt:tnt", output = "tnt:tnt",
recipe = { recipe = {
{"", "group:wood", ""}, {"", "group:wood", ""},
{"group:wood", "tnt:gunpowder", "group:wood"}, {"group:wood", "tnt:gunpowder", "group:wood"},
{"", "group:wood", ""} {"", "group:wood", ""}
} }
}) })
if minetest.setting_get("log_mods") then if minetest.setting_get("log_mods") then
minetest.log("action", "tnt loaded") minetest.debug("[TNT] Loaded!")
end end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 B

After

Width:  |  Height:  |  Size: 202 B