9 Commits

Author SHA1 Message Date
paramat
7fe9ca538b Fix is_ground_content settings for nodes
Remove line if set to the default of 'true'
2015-06-18 02:25:02 +01:00
Wuzzy
a2ce7629aa Add gunpowder trails
Uses group “connect_to_raillike”
2015-06-07 14:27:18 +02:00
Novatux
a7bf161875 Copy pos before giving it to on_blast because it can modify it; also remove a debug print that had nothing to do here 2015-05-12 21:03:02 +02:00
Novatux
87fc2aac60 Fix problems with TNT 2015-05-12 16:53:04 +02:00
Wuzzy
f81820203d Make TNT respect on_blast, implement on_blast for some nodes
Implemented nodes:
- Steel Door: Ignores explosion
- Locked Chest: Ignores explosion
- Fire: Ignores explosion
- TNT: Starts burning
- Burning TNT: Explodes immediately
- Gunpowder: Starts burning
- Burning Gunpowder: Ignores explosion
2015-05-12 16:32:52 +02:00
PilzAdam
6aec1811eb Remove weird constants in default 2015-01-10 15:47:30 +01:00
Gabriel Huber
02a033714d Fix TNT init for undefined enable_tnt setting 2014-07-25 23:29:44 -04:00
ShadowNinja
0822a3df13 Rewrite TNT 2014-07-22 12:05:40 -04:00
BlockMen
c7330eb153 Add protection to TNT (by @tenplus1) 2014-07-13 19:24:57 +02:00
15 changed files with 285 additions and 253 deletions

View File

@@ -1,19 +1,10 @@
=== 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

View File

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

506
init.lua
View File

@@ -1,295 +1,285 @@
-- 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 ~= true) or
(singleplayer and setting == false) 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,
on_blast = def.on_blast,
}
end end
end)
local function rand_pos(center, pos, radius)
pos.x = center.x + math.random(-radius, radius)
pos.z = center.z + math.random(-radius, radius)
end
local function eject_drops(drops, pos, radius)
local drop_pos = vector.new(pos)
for _, item in pairs(drops) do
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:get_luaentity().collect = true
obj:setacceleration({x=0, y=-10, z=0}) obj:setacceleration({x=0, y=-10, z=0})
obj:setvelocity({x=math.random(0,6)-3, y=10, z=math.random(0,6)-3}) obj:setvelocity({x=math.random(-3, 3), y=10,
z=math.random(-3, 3)})
end
count = count - max
end
end
end end
local add_drop = function(drops, pos, item) local function add_drop(drops, item)
if loss_prob[item] ~= nil then item = ItemStack(item)
if math.random(1,loss_prob[item]) == 1 then local name = item:get_name()
if loss_prob[name] ~= nil and math.random(1, loss_prob[name]) == 1 then
return return
end end
end
if drops[item] == nil then local drop = drops[name]
drops[item] = ItemStack(item) if drop == nil then
drops[name] = item
else else
drops[item]:add_item(item) drop:set_count(drop:get_count() + item:get_count())
end
if drops[item]:get_free_space() == 0 then
stack = drops[item]
eject_drops(pos, stack)
drops[item] = nil
end end
end end
local destroy = function(drops, pos, last, fast) local fire_node = {name="fire:basic_flame"}
local nodename = minetest.get_node(pos).name
if nodename ~= "air" then local function destroy(drops, pos, cid)
minetest.remove_node(pos, (fast and 1 or 0)) if minetest.is_protected(pos, "") then
if last then
nodeupdate(pos)
end
if minetest.registered_nodes[nodename].groups.flammable ~= nil then
minetest.set_node(pos, {name="fire:basic_flame"}, (fast and 2 or 0))
return return
end end
local drop = minetest.get_node_drops(nodename, "") local def = cid_data[cid]
for _,item in ipairs(drop) do if def and def.on_blast then
if type(item) == "string" then def.on_blast(vector.new(pos), 1)
add_drop(drops, pos, item) return
end
if def and def.flammable then
minetest.set_node(pos, fire_node)
else else
for i=1,item:get_count() do minetest.remove_node(pos)
add_drop(drops, pos, item:get_name()) if def then
end local node_drops = minetest.get_node_drops(def.name, "")
for _, item in ipairs(node_drops) do
add_drop(drops, item)
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 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
local damage = (4 / dist) * radius
obj:set_hp(obj:get_hp() - damage)
end
end
local function add_effects(pos, radius)
minetest.add_particlespawner({
amount = 128,
time = 1,
minpos = vector.subtract(pos, radius / 2),
maxpos = vector.add(pos, radius / 2),
minvel = {x=-20, y=-20, z=-20},
maxvel = {x=20, y=20, z=20},
minacc = vector.new(),
maxacc = vector.new(),
minexptime = 1,
maxexptime = 3,
minsize = 8,
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")
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_air then
destroy(drops, p, cid)
end
end
vi = vi + 1
end
end
end
return drops
end
local function boom(pos)
minetest.sound_play("tnt_explode", {pos=pos, gain=1.5, max_hear_distance=2*64}) minetest.sound_play("tnt_explode", {pos=pos, gain=1.5, max_hear_distance=2*64})
minetest.set_node(pos, {name="tnt:boom"}) minetest.set_node(pos, {name="tnt:boom"})
minetest.after(0.5, function(pos) minetest.get_node_timer(pos):start(0.5)
minetest.remove_node(pos)
end, {x=pos.x, y=pos.y, z=pos.z})
local drops = explode(pos, radius)
local radius = 2 entity_physics(pos, radius)
local drops = {} eject_drops(drops, pos, radius)
local list = {} add_effects(pos, radius)
local dr = 0
local tnts = 1
local destroyed = 0
while dr<radius do
dr=dr+1
for dx=-dr,dr,dr*2 do
for dy=-dr,dr,1 do
for dz=-dr,dr,1 do
table.insert(list, {x=dx, y=dy, z=dz})
end
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
local objects = minetest.get_objects_inside_radius(pos, radius*2)
for _,obj in ipairs(objects) do
--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)
for _,stack in pairs(drops) do
eject_drops(pos, stack)
end
local radiusp = radius+1
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", {
description = "TNT", description = "TNT",
tiles = {"tnt_top.png", "tnt_bottom.png", "tnt_side.png"}, tiles = {"tnt_top.png", "tnt_bottom.png", "tnt_side.png"},
is_ground_content = false,
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,
on_blast = function(pos, intensity)
mesecons = { burn(pos)
effector = { end,
action_on = function(pos, node) mesecons = {effector = {action_on = boom}},
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,
-- unaffected by explosions
on_blast = function() end,
}) })
minetest.register_node("tnt:boom", { minetest.register_node("tnt:boom", {
drawtype = "plantlike", drawtype = "plantlike",
tiles = {"tnt_boom.png"}, tiles = {"tnt_boom.png"},
light_source = LIGHT_MAX, light_source = default.LIGHT_MAX,
walkable = false, walkable = false,
drop = "", drop = "",
groups = {dig_immediate=3}, groups = {dig_immediate=3},
}) on_timer = function(pos, elapsed)
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) minetest.remove_node(pos)
end, {x=pos.x, y=pos.y, z=pos.z}) end,
for dx=-1,1 do -- unaffected by explosions
for dz=-1,1 do on_blast = function() end,
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",
paramtype = "light", paramtype = "light",
is_ground_content = false,
sunlight_propagates = true, sunlight_propagates = true,
walkable = false, walkable = false,
tiles = {"tnt_gunpowder.png",}, tiles = {"tnt_gunpowder_straight.png", "tnt_gunpowder_curved.png", "tnt_gunpowder_t_junction.png", "tnt_gunpowder_crossing.png"},
inventory_image = "tnt_gunpowder_inventory.png", inventory_image = "tnt_gunpowder_inventory.png",
wield_image = "tnt_gunpowder_inventory.png", wield_image = "tnt_gunpowder_inventory.png",
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},
}, },
groups = {dig_immediate=2,attached_node=1}, groups = {dig_immediate=2,attached_node=1,connect_to_raillike=minetest.raillike_group("gunpowder")},
sounds = default.node_sound_leaves_defaults(), sounds = default.node_sound_leaves_defaults(),
on_punch = function(pos, node, puncher) on_punch = function(pos, node, puncher)
@@ -297,6 +287,9 @@ minetest.register_node("tnt:gunpowder", {
burn(pos) burn(pos)
end end
end, end,
on_blast = function(pos, intensity)
burn(pos)
end,
}) })
minetest.register_node("tnt:gunpowder_burning", { minetest.register_node("tnt:gunpowder_burning", {
@@ -305,29 +298,75 @@ 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_straight_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 1,
}
},
{
name = "tnt_gunpowder_burning_curved_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 1,
}
},
{
name = "tnt_gunpowder_burning_t_junction_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 1,
}
},
{
name = "tnt_gunpowder_burning_crossing_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},
}, },
drop = "", drop = "",
groups = {dig_immediate=2,attached_node=1}, groups = {dig_immediate=2,attached_node=1,connect_to_raillike=minetest.raillike_group("gunpowder")},
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,
-- unaffected by explosions
on_blast = function() 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({
@@ -346,5 +385,6 @@ minetest.register_craft({
}) })
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: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 B

After

Width:  |  Height:  |  Size: 202 B