This commit is contained in:
Elias Fleckenstein 2021-01-26 17:26:44 +01:00
commit 953a2f2cd1

View File

@ -32,6 +32,10 @@ local STEP_LENGTH = 0.3
-- How many rays to compute entity exposure to explosion -- How many rays to compute entity exposure to explosion
local N_EXPOSURE_RAYS = 16 local N_EXPOSURE_RAYS = 16
-- Nodes having a blast resistance of this value or higher are treated as
-- indestructible
local INDESTRUCT_BLASTRES = 1000000
minetest.register_on_mods_loaded(function() minetest.register_on_mods_loaded(function()
-- Store blast resistance values by content ids to improve performance. -- Store blast resistance values by content ids to improve performance.
for name, def in pairs(minetest.registered_nodes) do for name, def in pairs(minetest.registered_nodes) do
@ -135,14 +139,21 @@ end
-- strength - The strength of each ray -- strength - The strength of each ray
-- raydirs - The directions for each ray -- raydirs - The directions for each ray
-- radius - The maximum distance each ray will go -- radius - The maximum distance each ray will go
-- drop_chance - The chance that destroyed nodes will drop their items -- info - Table containing information about explosion
-- fire - If true, 1/3 of destroyed nodes become fire
-- puncher - object that punches other objects (optional) -- puncher - object that punches other objects (optional)
-- --
-- Values in info:
-- drop_chance - The chance that destroyed nodes will drop their items
-- fire - If true, 1/3 nodes become fire
-- griefing - If true, the explosion will destroy nodes (default: true)
-- max_blast_resistance - The explosion will treat all non-indestructible nodes
-- as having a blast resistance of no more than this
-- value
--
-- Note that this function has been optimized, it contains code which has been -- Note that this function has been optimized, it contains code which has been
-- inlined to avoid function calls and unnecessary table creation. This was -- inlined to avoid function calls and unnecessary table creation. This was
-- measured to give a significant performance increase. -- measured to give a significant performance increase.
local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire, puncher, creative_enabled) local function trace_explode(pos, strength, raydirs, radius, info, puncher)
local vm = minetest.get_voxel_manip() local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(vector.subtract(pos, radius), local emin, emax = vm:read_from_map(vector.subtract(pos, radius),
@ -164,39 +175,49 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
local data = vm:get_data() local data = vm:get_data()
local destroy = {} local destroy = {}
local drop_chance = info.drop_chance
local fire = info.fire
local max_blast_resistance = info.max_blast_resistance
-- Trace rays for environment destruction -- Trace rays for environment destruction
for i = 1, #raydirs do if info.griefing then
local rpos_x = pos.x for i = 1, #raydirs do
local rpos_y = pos.y local rpos_x = pos.x
local rpos_z = pos.z local rpos_y = pos.y
local rdir_x = raydirs[i].x local rpos_z = pos.z
local rdir_y = raydirs[i].y local rdir_x = raydirs[i].x
local rdir_z = raydirs[i].z local rdir_y = raydirs[i].y
local rstr = (0.7 + math.random() * 0.6) * strength local rdir_z = raydirs[i].z
local rstr = (0.7 + math.random() * 0.6) * strength
for r = 0, math.ceil(radius * (1.0 / STEP_LENGTH)) do for r = 0, math.ceil(radius * (1.0 / STEP_LENGTH)) do
local npos_x = math.floor(rpos_x + 0.5) local npos_x = math.floor(rpos_x + 0.5)
local npos_y = math.floor(rpos_y + 0.5) local npos_y = math.floor(rpos_y + 0.5)
local npos_z = math.floor(rpos_z + 0.5) local npos_z = math.floor(rpos_z + 0.5)
local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride + local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride +
npos_x - emin_x + 1 npos_x - emin_x + 1
local cid = data[idx] local cid = data[idx]
local br = node_blastres[cid] local br = node_blastres[cid]
local hash = minetest.hash_node_position({x=npos_x, y=npos_y, z=npos_z}) if br < INDESTRUCT_BLASTRES and br > max_blast_resistance then
br = max_blast_resistance
end
rpos_x = rpos_x + STEP_LENGTH * rdir_x local hash = minetest.hash_node_position({x=npos_x, y=npos_y, z=npos_z})
rpos_y = rpos_y + STEP_LENGTH * rdir_y
rpos_z = rpos_z + STEP_LENGTH * rdir_z
rstr = rstr - 0.75 * STEP_LENGTH - (br + 0.3) * STEP_LENGTH rpos_x = rpos_x + STEP_LENGTH * rdir_x
rpos_y = rpos_y + STEP_LENGTH * rdir_y
rpos_z = rpos_z + STEP_LENGTH * rdir_z
if rstr <= 0 then rstr = rstr - 0.75 * STEP_LENGTH - (br + 0.3) * STEP_LENGTH
break
end
if cid ~= minetest.CONTENT_AIR and not minetest.is_protected({x = npos_x, y = npos_y, z = npos_z}, "") then if rstr <= 0 then
destroy[hash] = idx break
end
if cid ~= minetest.CONTENT_AIR and not minetest.is_protected({x = npos_x, y = npos_y, z = npos_z}, "") then
destroy[hash] = idx
end
end end
end end
end end
@ -327,7 +348,7 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
-- Remove destroyed blocks and drop items -- Remove destroyed blocks and drop items
for hash, idx in pairs(destroy) do for hash, idx in pairs(destroy) do
local do_drop = not creative_enabled and math.random() <= drop_chance local do_drop = math.random() <= drop_chance
local on_blast = node_on_blast[data[idx]] local on_blast = node_on_blast[data[idx]]
local remove = true local remove = true
@ -377,7 +398,6 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
-- Log explosion -- Log explosion
minetest.log('action', 'Explosion at ' .. minetest.pos_to_string(pos) .. minetest.log('action', 'Explosion at ' .. minetest.pos_to_string(pos) ..
' with strength ' .. strength .. ' and radius ' .. radius) ' with strength ' .. strength .. ' and radius ' .. radius)
end end
-- Create an explosion with strength at pos. -- Create an explosion with strength at pos.
@ -385,16 +405,24 @@ end
-- Parameters: -- Parameters:
-- pos - The position where the explosion originates from -- pos - The position where the explosion originates from
-- strength - The blast strength of the explosion (a TNT explosion uses 4) -- strength - The blast strength of the explosion (a TNT explosion uses 4)
-- info - Table containing information about explosion. -- info - Table containing information about explosion
-- puncher - object that is reported as source of punches/damage (optional) -- puncher - object that is reported as source of punches/damage (optional)
-- --
-- Values in info: -- Values in info:
-- drop_chance - If specified becomes the drop chance of all nodes in the -- drop_chance - If specified becomes the drop chance of all nodes in the
-- explosion (defaults to 1.0 / strength) -- explosion (default: 1.0 / strength)
-- no_sound - If true then the explosion will not play a sound -- max_blast_resistance - If specified the explosion will treat all
-- no_particle - If true then the explosion will not create particles -- non-indestructible nodes as having a blast resistance
-- of no more than this value
-- sound - If true, the explosion will play a sound (default: true)
-- particles - If true, the explosion will create particles (default: true)
-- fire - If true, 1/3 nodes become fire (default: false) -- fire - If true, 1/3 nodes become fire (default: false)
-- griefing - If true, the explosion will destroy nodes (default: true)
function mcl_explosions.explode(pos, strength, info, puncher) function mcl_explosions.explode(pos, strength, info, puncher)
if info == nil then
info = {}
end
-- The maximum blast radius (in the air) -- The maximum blast radius (in the air)
local radius = math.ceil(1.3 * strength / (0.3 * 0.75) * 0.3) local radius = math.ceil(1.3 * strength / (0.3 * 0.75) * 0.3)
@ -403,13 +431,31 @@ function mcl_explosions.explode(pos, strength, info, puncher)
end end
local shape = sphere_shapes[radius] local shape = sphere_shapes[radius]
local creative_enabled = minetest.is_creative_enabled("") -- Default values
trace_explode(pos, strength, shape, radius, (info and info.drop_chance) or 1 / strength, info.fire == true, puncher, creative_enabled) if info.drop_chance == nil then info.drop_chance = 1 / strength end
if info.particles == nil then info.particles = true end
if info.sound == nil then info.sound = true end
if info.fire == nil then info.fire = false end
if info.griefing == nil then info.griefing = true end
if info.max_blast_resistance == nil then
info.max_blast_resistance = INDESTRUCT_BLASTRES
end
if not (info and info.no_particle) then -- For backwards compatibility
if info.no_particle then info.particles = false end
if info.no_sound then info.sound = false end
-- Dont do drops in creative mode
if minetest.is_creative_enabled("") then
info.drop_chance = 0
end
trace_explode(pos, strength, shape, radius, info, puncher)
if info.particles then
add_particles(pos, radius) add_particles(pos, radius)
end end
if not (info and info.no_sound) then if info.sound then
minetest.sound_play("tnt_explode", { minetest.sound_play("tnt_explode", {
pos = pos, gain = 1.0, pos = pos, gain = 1.0,
max_hear_distance = strength * 16 max_hear_distance = strength * 16