local random = math.random local modname = core.get_current_modname() local modpath = core.get_modpath(modname) _G[modname] = {} local S = core.get_translator(modname) local Stnt = core.get_translator("tnt") local enable_tnt = core.settings:get_bool("enable_tnt", true) if not core.registered_nodes["tnt:tnt"] then error("TNT mod requires to be enabled!") return end local tnt_radius = tonumber(core.settings:get("tnt_radius") or 3) local cid_data = {} core.register_on_mods_loaded(function() for name, def in pairs(core.registered_nodes) do cid_data[core.get_content_id(name)] = { name = name, drops = def.drops, flammable = def.groups and (def.groups.flammable or 0) ~= 0, on_blast = def.on_blast, } end end) local function escape_argument(tex) return tex:gsub(".", {["\\"] = "\\\\", ["^"] = "\\^", [":"] = "\\:"}) end local function getburningtexture(main) local texture = "" texture = texture .. "([combine:16x64:0,0=" .. escape_argument(main) .. ")^" texture = texture .. "([combine:16x64:0,16=" .. escape_argument(main) .. ")^" texture = texture .. "([combine:16x64:0,32=" .. escape_argument(main) .. ")^" texture = texture .. "([combine:16x64:0,48=" .. escape_argument(main) .. ")^" return texture .. "^" .. modname .. "_burning.png" end local function particle_texture(name) local ret = {name = name} if core.features.particle_blend_clip then ret.blend = "clip" end return ret end local function check_surround_tnt(pos, radius, owner) for x = -radius, radius do for y = -radius, radius do for z = -radius, radius do local check = { x = pos.x + x, y = pos.y + y, z = pos.z + z } if not vector.equals(check, pos) then local node = core.get_node(check) local def = core.registered_nodes[node.name] if def and def.groups and def.groups.tnt then if not core.is_protected(check, owner) and not string.find(node.name, "_burning") then local meta = core.get_meta(check) meta:set_string("owner", owner) local burn_def = core.registered_nodes[node.name] if burn_def and burn_def.on_blast then burn_def.on_blast(check) end end end end end end end end local function removetnt(pos, radius, owner) core.remove_node(pos) check_surround_tnt(pos, radius, owner:is_player() and owner:get_player_name() or owner) end local function add_effects(pos, radius, owner, without) if not (without == false) then check_surround_tnt(pos, radius, owner:get_player_name()) end core.add_particle({ pos = pos, velocity = vector.new(), acceleration = vector.new(), expirationtime = 0.4, size = radius * 10, collisiondetection = false, vertical = false, texture = particle_texture("tnt_boom.png"), glow = 15, }) core.add_particlespawner({ amount = 64, time = 0.5, minpos = vector.subtract(pos, radius / 2), maxpos = vector.add(pos, radius / 2), minvel = {x = -10, y = -10, z = -10}, maxvel = {x = 10, y = 10, z = 10}, minacc = vector.new(), maxacc = vector.new(), minexptime = 1, maxexptime = 2.5, minsize = radius * 3, maxsize = radius * 5, texture = particle_texture("tnt_smoke.png"), }) end local function add_drops_effects(pos, radius, drops) core.add_particle({ pos = pos, velocity = vector.new(), acceleration = vector.new(), expirationtime = 0.4, size = radius * 10, collisiondetection = false, vertical = false, texture = particle_texture("tnt_boom.png"), glow = 15, }) core.add_particlespawner({ amount = 64, time = 0.5, minpos = vector.subtract(pos, radius / 2), maxpos = vector.add(pos, radius / 2), minvel = {x = -10, y = -10, z = -10}, maxvel = {x = 10, y = 10, z = 10}, minacc = vector.new(), maxacc = vector.new(), minexptime = 1, maxexptime = 2.5, minsize = radius * 3, maxsize = radius * 5, texture = particle_texture("tnt_smoke.png"), }) -- we just dropped some items. Look at the items and pick -- one of them to use as texture. local texture = "tnt_blast.png" -- fallback local node local most = 0 for name, stack in pairs(drops) do local count = stack:get_count() if count > most then most = count local def = core.registered_nodes[name] if def then node = { name = name } if def.tiles and type(def.tiles[1]) == "string" then texture = def.tiles[1] end end end end core.add_particlespawner({ amount = 64, time = 0.1, minpos = vector.subtract(pos, radius / 2), maxpos = vector.add(pos, radius / 2), minvel = {x = -3, y = 0, z = -3}, maxvel = {x = 3, y = 5, z = 3}, minacc = {x = 0, y = -10, z = 0}, maxacc = {x = 0, y = -10, z = 0}, minexptime = 0.8, maxexptime = 2.0, minsize = radius * 0.33, maxsize = radius, texture = texture, -- ^ only as fallback for clients without support for `node` parameter node = node, collisiondetection = true, }) end local function rand_pos(center, pos, radius) local def local reg_nodes = core.registered_nodes local i = 0 repeat -- Give up and use the center if this takes too long if i > 4 then pos.x, pos.z = center.x, center.z break end pos.x = center.x + random(-radius, radius) pos.z = center.z + random(-radius, radius) def = reg_nodes[core.get_node(pos).name] i = i + 1 until def and not def.walkable end local disallowed_multiplier = { "mesecons", "stone_with_", "diamond", "iron", "bronze", "tin", "gold", "coal", "carpet", "snow", "bones:bones", "chest", "chest_locked", modname .. ":poison", "butterflies", "_source", "_flowing" } local function check_multiply(name) for _, find in pairs(disallowed_multiplier) do if string.find(name, find) then return false end end return true end local function eject_drops(drops, pos, radius, multiply) if not multiply then multiply = 1 end local drop_pos = vector.new(pos) for _, item in pairs(drops) do local count = math.min(item:get_count(), item:get_stack_max()) while count > 0 do local take = math.max(1,math.min(radius * radius, count, item:get_stack_max())) rand_pos(pos, drop_pos, radius) local dropitem = ItemStack(item) if check_multiply(item:get_name()) then dropitem:set_count(take * multiply) else dropitem:set_count(take) end local obj = core.add_item(drop_pos, dropitem) if obj then obj:get_luaentity().collect = true obj:set_acceleration({x = 0, y = -10, z = 0}) obj:set_velocity({x = random(-3, 3), y = random(0, 10), z = random(-3, 3)}) end count = count - take end end end local function add_drop(drops, item) item = ItemStack(item) -- Note that this needs to be set on the dropped item, not the node. -- Value represents "one in X will be lost" local lost = item:get_definition()._tnt_loss or 0 if lost > 0 and (lost == 1 or random(1, lost) == 1) then return end local name = item:get_name() local drop = drops[name] if drop == nil then drops[name] = item else drop:set_count(drop:get_count() + item:get_count()) end end local basic_flame_on_construct -- cached value local function destroy(drops, npos, cid, c_air, c_fire, on_blast_queue, on_construct_queue, ignore_protection, ignore_on_blast, owner) if not ignore_protection and core.is_protected(npos, owner) then return cid end local def = cid_data[cid] if not def then return c_air elseif not ignore_on_blast and def.on_blast then on_blast_queue[#on_blast_queue + 1] = { pos = vector.new(npos), on_blast = def.on_blast } return cid elseif def.flammable then on_construct_queue[#on_construct_queue + 1] = { fn = basic_flame_on_construct, pos = vector.new(npos) } return c_fire else local node_drops = core.get_node_drops(def.name, "") for _, item in pairs(node_drops) do add_drop(drops, item) end return c_air end end local function calc_velocity(pos1, pos2, old_vel, power) -- Avoid errors caused by a vector of zero length if vector.equals(pos1, pos2) then return old_vel end local vel = vector.direction(pos1, pos2) 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) -- randomize it a bit vel = vector.add(vel, { x = random() - 0.5, y = random() - 0.5, z = random() - 0.5, }) -- Limit to terminal velocity dist = vector.length(vel) if dist > 250 then vel = vector.divide(vel, dist / 250) end return vel end local function entity_physics(pos, radius, drops) local objs = core.get_objects_inside_radius(pos, radius) for _, obj in pairs(objs) do local obj_pos = obj:get_pos() if obj_pos then local dist = math.max(1, vector.distance(pos, obj_pos)) local damage = (4 / dist) * radius if obj:is_player() then local dir = vector.normalize(vector.subtract(obj_pos, pos)) local moveoff = vector.multiply(dir, 2 / dist * radius) obj:add_velocity(moveoff) obj:set_hp(obj:get_hp() - damage) else local luaobj = obj:get_luaentity() -- object might have disappeared somehow if luaobj then local do_damage = true local do_knockback = true local entity_drops = {} local objdef = core.registered_entities[luaobj.name] if objdef and objdef.on_blast then do_damage, do_knockback, entity_drops = objdef.on_blast(luaobj, damage) end if do_knockback then local obj_vel = obj:get_velocity() obj:set_velocity(calc_velocity(pos, obj_pos, obj_vel, radius * 10)) end if do_damage then if not obj:get_armor_groups().immortal then obj:punch(obj, 1.0, { full_punch_interval = 1.0, damage_groups = {fleshy = damage}, }, nil) end end for _, item in pairs(entity_drops) do add_drop(drops, item) end end end end end end local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast, owner, explode_center, no_fire, circular) pos = vector.round(pos) -- scan for adjacent TNT nodes first, and enlarge the explosion local vm1 = VoxelManip() local p1 = vector.subtract(pos, 2) local p2 = vector.add(pos, 2) local minp, maxp = vm1:read_from_map(p1, p2) local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) local data = vm1:get_data() local count = 0 local c_tnt local c_tnt_burning = core.get_content_id("tnt:tnt_burning") local c_tnt_boom = core.get_content_id("tnt:boom") local c_air = core.CONTENT_AIR local c_ignore = core.CONTENT_IGNORE if enable_tnt then c_tnt = core.get_content_id("tnt:tnt") else c_tnt = c_tnt_burning -- tnt is not registered if disabled end -- make sure we still have explosion even when centre node isnt tnt related if explode_center then count = 1 end for z = pos.z - 2, pos.z + 2 do for y = pos.y - 2, pos.y + 2 do local vi = a:index(pos.x - 2, y, z) for x = pos.x - 2, pos.x + 2 do local cid = data[vi] if cid == c_tnt or cid == c_tnt_boom or cid == c_tnt_burning then count = count + 1 data[vi] = c_air end vi = vi + 1 end end end vm1:set_data(data) vm1:write_to_map() if vm1.close ~= nil then vm1:close() end -- recalculate new radius radius = math.floor(radius * math.pow(count, 1/3)) -- perform the explosion local vm = VoxelManip() local pr = PseudoRandom(os.time()) p1 = vector.subtract(pos, radius) p2 = vector.add(pos, radius) minp, maxp = vm:read_from_map(p1, p2) a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) data = vm:get_data() local drops = {} local on_blast_queue = {} local on_construct_queue = {} basic_flame_on_construct = core.registered_nodes["fire:basic_flame"].on_construct -- Used to efficiently remove metadata of nodes that were destroyed. -- Metadata is probably sparse, so this may save us some work. local has_meta = {} for _, p in ipairs(core.find_nodes_with_meta(p1, p2)) do has_meta[a:indexp(p)] = true end local c_fire = core.get_content_id("fire:basic_flame") if not circular then 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 local r = vector.length(vector.new(x, y, z)) if (radius * radius) / (r * r) >= (pr:next(80, 125) / 100) then local cid = data[vi] local p = {x = pos.x + x, y = pos.y + y, z = pos.z + z} if cid ~= c_air and cid ~= c_ignore then local new_cid = destroy(drops, p, cid, c_air, c_fire, on_blast_queue, on_construct_queue, ignore_protection, ignore_on_blast, owner) if new_cid ~= data[vi] then if ((new_cid == c_fire) and not no_fire) or (new_cid ~= c_fire) then data[vi] = new_cid end if has_meta[vi] then core.get_meta(p):from_table(nil) end end end end vi = vi + 1 end end end else pos = vector.round(pos) local p1 = vector.subtract(pos, radius) local p2 = vector.add(pos, radius) for z = p1.z, p2.z do for y = p1.y, p2.y do for x = p1.x, p2.x do local current_pos = vector.new(x, y, z) if not core.is_protected(current_pos, player_name) then local vi = a:index(x, y, z) local cid = data[vi] local distance_sq = vector.distance(pos, current_pos) ^ 2 if distance_sq <= radius ^ 2 then if cid ~= c_air and cid ~= c_ignore then local new_cid = destroy(drops, p, cid, c_air, c_fire, on_blast_queue, on_construct_queue, ignore_protection, ignore_on_blast, owner) if new_cid ~= data[vi] then if ((new_cid == c_fire) and not no_fire) or (new_cid ~= c_fire) then data[vi] = new_cid end if has_meta[vi] then core.get_meta(p):from_table(nil) end end end end end end end end end vm:set_data(data) vm:write_to_map() vm:update_liquids() if vm.close ~= nil then vm:close() end -- call check_single_for_falling for everything within 1.5x blast radius for y = -radius * 1.5, radius * 1.5 do for z = -radius * 1.5, radius * 1.5 do for x = -radius * 1.5, radius * 1.5 do local rad = {x = x, y = y, z = z} local s = vector.add(pos, rad) local r = vector.length(rad) if r / radius < 1.4 then core.check_single_for_falling(s) end end end end for _, queued_data in pairs(on_blast_queue) do local dist = math.max(1, vector.distance(queued_data.pos, pos)) local intensity = (radius * radius) / (dist * dist) local node_drops = queued_data.on_blast(queued_data.pos, intensity) if node_drops then for _, item in pairs(node_drops) do add_drop(drops, item) end end end for _, queued_data in pairs(on_construct_queue) do queued_data.fn(queued_data.pos) end core.log("action", "TNT owned by " .. owner .. " detonated at " .. core.pos_to_string(pos) .. " with radius " .. radius) return drops, radius end _G[modname].boom = function(pos, def) def = def or {} def.radius = def.radius or 1 def.damage_radius = def.damage_radius or def.radius * 2 local meta = core.get_meta(pos) local owner = meta:get_string("owner") if not def.explode_center and def.ignore_protection ~= true then core.set_node(pos, {name = "tnt:boom"}) end local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) local drops, radius = tnt_explode(pos, def.radius, def.ignore_protection, def.ignore_on_blast, owner, def.explode_center, def.no_fire, def.circular) -- append entity drops local damage_radius = (radius / math.max(1, def.radius)) * def.damage_radius entity_physics(pos, damage_radius, drops) if not def.disable_drops then eject_drops(drops, pos, radius, def.multiplier) end add_drops_effects(pos, radius, drops) core.log("action", "A TNT explosion occurred at " .. core.pos_to_string(pos) .. " with radius " .. radius) end _G[modname].square_explode = function(pos, radius, replace, player_name) local replace_id = core.CONTENT_AIR if replace and core.registered_nodes[replace] then replace_id = core.get_content_id(replace) end pos = vector.round(pos) local p1 = vector.subtract(pos, radius) local p2 = vector.add(pos, radius) local vm = VoxelManip() local minp, maxp = vm:read_from_map(p1, p2) local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) local data = vm:get_data() data[a:index(pos.x, pos.y, pos.z)] = core.CONTENT_AIR for z = p1.z, p2.z do for y = p1.y, p2.y do for x = p1.x, p2.x do local current_pos = vector.new(x, y, z) if not core.is_protected(current_pos, player_name) then local vi = a:index(x, y, z) local cid = data[vi] if cid ~= core.CONTENT_AIR and cid ~= core.CONTENT_IGNORE then data[vi] = replace_id end end end end end vm:set_data(data) vm:write_to_map() vm:update_liquids() if vm.close then vm:close() end core.log("action", "Square explosion at " .. core.pos_to_string(pos) .. " with radius " .. radius .. " by player " .. player_name) end _G[modname].circular_explode = function(pos, radius, action, replace, player_name) local replace_id = core.CONTENT_AIR if replace and core.registered_nodes[replace] then replace_id = core.get_content_id(replace) end pos = vector.round(pos) local p1 = vector.subtract(pos, radius) local p2 = vector.add(pos, radius) local vm = VoxelManip() local minp, maxp = vm:read_from_map(p1, p2) local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) local data = vm:get_data() data[a:index(pos.x, pos.y, pos.z)] = core.CONTENT_AIR for z = p1.z, p2.z do for y = p1.y, p2.y do for x = p1.x, p2.x do local current_pos = vector.new(x, y, z) if not core.is_protected(current_pos, player_name) then local vi = a:index(x, y, z) local cid = data[vi] local distance_sq = vector.distance(pos, current_pos) ^ 2 if distance_sq <= radius ^ 2 then if not action then if cid ~= core.CONTENT_AIR and cid ~= core.CONTENT_IGNORE then data[vi] = replace_id end elseif action == "random" then if cid ~= core.CONTENT_AIR and cid ~= core.CONTENT_IGNORE and random(1,3) == 3 then data[vi] = replace_id end end end end end end end vm:set_data(data) vm:write_to_map() vm:update_liquids() if vm.close then vm:close() end core.log("action", "Circular explosion at " .. core.pos_to_string(pos) .. " with radius " .. radius .. " by player " .. player_name) end _G[modname].register_tnt = function(def, custom) local name if not def.name:find(':') then name = modname .. ":" .. def.name else name = def.name def.name = def.name:match(":([%w_]+)") end if not def.tiles then def.tiles = {} end if not def.radius then def.radius = tnt_radius end local tnt_top = def.tiles.top or def.name .. "_top.png" local tnt_bottom = def.tiles.bottom or def.name .. "_bottom.png" local tnt_side = def.tiles.side or def.name .. "_side.png" local tnt_burning = def.tiles.burning or def.name .. "_top_burning_animated.png" if not custom then tnt_burning = { name = tnt_burning, animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 1, } } end if not def.damage_radius then def.damage_radius = def.radius * 2 end local explode = (def.boom and function(pos) local owner = core.get_player_by_name(core.get_meta(pos):get_string("owner")) if owner then def.boom(pos, def, owner) end end) or function(pos) tnt.boom(pos, def) end local normal_def = { description = def.description, tiles = { (def.flip and tnt_bottom or tnt_top), (def.flip and tnt_top or tnt_bottom), tnt_side }, is_ground_content = def.is_ground_content or false, paramtype = def.paramtype or nil, use_texture_alpha = def.use_texture_alpha or nil, groups = {dig_immediate = 2, mesecon = 2, tnt = 1, flammable = 5}, sounds = def.sounds or default.node_sound_wood_defaults(), light_source = def.light_source or 0, on_construct = def.on_construct or nil, drawtype = def.drawtype or nil, on_dig = def.on_dig or nil, drops = def.drops or nil, after_place_node = function(pos, placer) if placer and placer:is_player() then local meta = core.get_meta(pos) meta:set_string("owner", placer:get_player_name()) meta:set_string("owner2", placer:get_player_name()) end end, on_punch = function(pos, node, puncher) if puncher:get_wielded_item():get_name() == "default:torch" then core.swap_node(pos, {name = name .. "_burning"}) core.registered_nodes[name .. "_burning"].on_construct(pos) default.log_player_action(puncher, "ignites", node.name, "at", pos) end end, on_blast = function(pos) core.after(0.1, function() explode(pos) end) end, mesecons = {effector = {action_on = function(pos) core.swap_node(pos, {name = name .. "_burning"}) core.registered_nodes[name .. "_burning"].on_construct(pos) end } }, on_burn = function(pos) core.swap_node(pos, {name = name .. "_burning"}) core.registered_nodes[name .. "_burning"].on_construct(pos) end, on_ignite = function(pos, igniter) core.swap_node(pos, {name = name .. "_burning"}) core.registered_nodes[name .. "_burning"].on_construct(pos) if igniter and igniter:is_valid() and igniter:is_player() then local meta = core.get_meta(pos) meta:set_string("owner", igniter:get_player_name()) end end, } if enable_tnt then core.register_node(":" .. name, normal_def) if def.craft then local recipe = table.copy(def.craft) if not recipe["output"] then recipe["output"] = name end core.register_craft(recipe) end end local light = def.light_source and (def.light_source + 5) or 5 if light > 14 then light = 14 end local burn_def = table.copy(normal_def) -- im sure there's a better way to do this, but idc tbh burn_def.light_source = light burn_def.description = nil burn_def.mesecons = nil burn_def.drop = "" burn_def.on_timer = explode burn_def.groups = {falling_node = 1, not_in_creative_inventory = 1} burn_def.tiles = { (def.flip and tnt_bottom or tnt_burning), (def.flip and tnt_burning or tnt_bottom), tnt_side } burn_def.on_burn = nil burn_def.on_ignite = nil burn_def.on_punch = nil burn_def.after_place_node = nil burn_def.on_blast = function() end burn_def.on_construct = function(pos) local meta = core.get_meta(pos) if def.on_ignite then def.on_ignite(pos, core.get_player_by_name(meta:get_string("owner"))) end core.sound_play("tnt_ignite", {pos = pos}, true) core.get_node_timer(pos):start(def.ignite_timer or 4) core.check_for_falling(pos) end core.register_node(":" .. name .. "_burning", burn_def) end local function get_highest(pos, name) local highest_y = pos.y local current_y = pos.y while current_y > -50 do local npos = {x = pos.x, y = current_y, z = pos.z} local bnpos = {x = pos.x, y = current_y - 1, z = pos.z} local node = core.get_node(npos) local bnode = core.get_node(bnpos) local ndef = core.registered_nodes[node.name] if ((node.name == "air" or ndef.buildable_to) and bnode.name ~= "air") or (name and node.name == name or false) then highest_y = current_y break end if node.name == "air" and bnode.name == "air" then current_y = current_y - 1 else current_y = current_y + 1 end end return {x = pos.x, y = math.floor(highest_y), z = pos.z} end local function scatter(pos, name, max, density, owner) for radius = 1, max do local count = math.floor(density / radius) if count < 1 then break end for i = 1, count do local angle = random() * 2 * math.pi local offsetx = math.cos(angle) * radius local offsetz = math.sin(angle) * radius local new_pos = get_highest({ x = pos.x + offsetx, y = pos.y, z = pos.z + offsetz }, name) local node = core.get_node(new_pos) if (node.name ~= name) and not core.is_protected(pos, owner) then core.set_node(new_pos, {name = name}) core.check_for_falling(new_pos) end end end end local function scatterboom(pos, name, max, density, owner) for radius = 1, max do local count = math.floor(density / radius) if count < 1 then break end for i = 1, count do local angle = random() * 2 * math.pi local offsetx = math.cos(angle) * radius local offsetz = math.sin(angle) * radius local new_pos = get_highest({ x = pos.x + offsetx, y = pos.y, z = pos.z + offsetz }, name) new_pos.y = new_pos.y local node = core.get_node(new_pos) if (node.name ~= name) and not core.is_protected(pos, owner) then core.set_node(new_pos, {name = name}) local _, obj = core.spawn_falling_node(new_pos) local dx = new_pos.x - pos.x local dz = new_pos.z - pos.z local dist = math.sqrt(dx * dx + dz * dz) local dirx, dirz = 0, 0 if dist > 0 then dirx = dx / dist dirz = dz / dist end local falloff = 1 - (dist / max) local vertical_power = 24 * falloff local horizontal_power = dist * 2 obj:add_velocity({ x = dirx * horizontal_power, y = vertical_power + randomFloat(0, 2), z = dirz * horizontal_power }) end end end end local snow_def = table.copy(core.registered_nodes["default:snow"]) snow_def.groups["not_in_creative_inventory"] = 1 snow_def.drop = "" snow_def.tiles = {{name = "default_snow.png", color = "lime"}} snow_def.damage_per_second = 1 core.register_node(modname .. ":poison", snow_def) local function texture(color, icon) if icon then icon = "^" .. icon else icon = "" end return { top = modname .. "_bottom.png^" .. color .. "^" .. modname .. "_top.png", side = modname .. "_side.png^" .. color .. icon, bottom = modname .. "_bottom.png^" .. color, burning = getburningtexture(modname .. "_bottom.png^" .. color), } end local function texture_top(color, icon, top) if icon then icon = "^" .. icon else icon = "" end if top then top = "^" .. top else top = "" end return { top = modname .. "_bottom.png^" .. color .. top .. "^" .. modname .. "_top.png", side = modname .. "_side.png^" .. color .. icon, bottom = modname .. "_bottom.png^" .. color .. top, burning = getburningtexture(modname .. "_bottom.png^" .. color), } end core.register_craftitem(modname..":demon_core", { description = S("Demon Core"), inventory_image = modname.."_demon_core.png" }) core.register_craft({ output = modname..":demon_core", recipe = { {"uraniumstuff:uranium_gem", "default:steel_ingot", "uraniumstuff:uranium_gem"}, {"default:steel_ingot", "uraniumstuff:uranium_protection_gem", "default:steel_ingot"}, {"uraniumstuff:uranium_gem", "default:steel_ingot", "uraniumstuff:uranium_gem"} } }) core.register_craftitem(modname..":atomic_bomb", { description = S("Atomic Bomb"), inventory_image = modname.."_atomic_bomb.png" }) core.register_craft({ output = modname..":atomic_bomb", recipe = { {"basic_materials:steel_strip", "tnt:tnt", "basic_materials:steel_strip"}, {"uraniumstuff:uranium_protection_gem", modname..":demon_core", "uraniumstuff:uranium_protection_gem"}, {"basic_materials:steel_strip", "tnt:tnt", "basic_materials:steel_strip"} } }) _G[modname].register_tnt({ name = modname..":nuke", description = S("Nuclear TNT"), radius = 64, tiles = texture("[multiply:#ffff00", modname.."_nuke_side_overlay.png"), ignite_timer = 10, craft = { recipe = { {"basic_materials:steel_bar", "tnt:tnt", "basic_materials:steel_bar"}, {"basic_materials:steel_wire", modname..":atomic_bomb", "basic_materials:steel_wire"}, {modname..":tnt_poison", "tnt:tnt", modname..":tnt_poison"} } }, boom = function(pos, def, owner) tnt.boom(pos, def) core.after(5, function() core.chat_send_all(core.colorize("red", S("ATOMIC BOMB EXPLODED BY @1!", owner:get_player_name()))) pos.y = pos.y scatter(pos, modname..":poison", 100, 350, owner:get_player_name()) end) end }) core.register_craftitem(modname..":engine", { description = S("Engine"), inventory_image = modname.."_engine.png" }) core.register_craft({ recipe = { {"basic_materials:steel_strip", "basic_materials:steel_wire", "basic_materials:steel_strip"}, {"basic_materials:gear_steel", "basic_materials:motor", "basic_materials:gear_steel"}, {"basic_materials:steel_strip", "default:mese", "basic_materials:steel_strip"} }, output = modname..":engine" }) _G[modname].register_tnt({ name = modname..":dig", description = S("Digging TNT"), tiles = texture("[multiply:#fa0000", modname.."_dig.png"), craft = { recipe = { {"basic_materials:energy_crystal_simple", modname..":engine", "basic_materials:energy_crystal_simple"}, {"default:tin_ingot", "tnt:tnt", "default:tin_ingot"}, {"default:steel_ingot", "default:tinblock", "default:steel_ingot"} } }, boom = function(pos, def, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) add_effects(pos, def.radius, owner) core.remove_node(pos) for i = 0, 60 do pos.y = pos.y - 1 core.dig_node(pos, owner) end end }) _G[modname].register_tnt({ name = modname..":drill", description = S("Drilling TNT"), tiles = texture("[multiply:#ff4f00", modname.."_dig.png"), craft = { recipe = { {"basic_materials:gold_wire", modname..":engine", "basic_materials:gold_wire"}, {"basic_materials:energy_crystal_simple", modname..":dig", "basic_materials:energy_crystal_simple"}, {"default:gold_ingot", "default:diamond", "default:gold_ingot"} } }, boom = function(pos, def, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) add_effects(pos, def.radius, owner) core.remove_node(pos) for _ = 0, 60 do pos.y = pos.y - 1 _G[modname].circular_explode(pos, def.radius, nil, nil, owner:get_player_name()) end end }) local diamond_def = table.copy(core.registered_nodes["default:diamondblock"]) diamond_def.tiles = {"default_diamond_block.png^" .. modname .. "_vignette.png"} diamond_def.description = S("Super") .. " " .. diamond_def.description core.register_node(modname..":super_diamondblock", diamond_def) core.register_craft({ recipe = { {"default:diamondblock", "default:diamondblock", "default:diamondblock"}, {"default:diamondblock", "default:diamondblock", "default:diamondblock"}, {"default:diamondblock", "default:diamondblock", "default:diamondblock"} }, output = modname..":super_diamondblock" }) core.register_craft({ recipe = { {modname..":super_diamondblock"} }, output = "default:diamondblock 9" }) local function register_fakenode(name) local ndef = table.copy(core.registered_items["default:" .. name]) ndef.is_ground_content = nil ndef.groups["not_in_creative_inventory"] = 1 ndef.drop = "" ndef.description = nil ndef.short_description = nil name = modname .. ":fake_" .. name core.register_node(name, ndef) end register_fakenode("diamondblock") _G[modname].register_tnt({ name = modname..":diamond", description = S("Diamond TNT"), tiles = { top = "default_stone.png^default_mineral_diamond.png^" .. modname .. "_top.png", bottom = "default_stone.png^default_mineral_diamond.png", side = "default_stone.png^default_mineral_diamond.png^" .. modname .. "_diamond_side_overlay.png", burning = getburningtexture("default_stone.png^default_mineral_diamond.png"), }, craft = { recipe = { {modname..":super_diamondblock", modname..":super_diamondblock", modname..":super_diamondblock"}, {modname..":super_diamondblock", "tnt:tnt", modname..":super_diamondblock"}, {modname..":super_diamondblock", modname..":super_diamondblock", modname..":super_diamondblock"} } }, radius = 4, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) _G[modname].square_explode(pos, def.radius, "too_many_tnt:fake_diamondblock", owner:get_player_name()) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) end }) _G[modname].register_tnt({ name = modname..":lava", description = S("Lava TNT"), tiles = { top = { name = modname.."_lava_top.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3, } }, bottom = { name = "default_lava_source_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3, } }, side = { name = modname.."_lava_side.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3, } }, burning = { name = modname.."_lava_burning.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3, } } }, craft = { recipe = { {"bucket:bucket_lava", "bucket:bucket_lava", "bucket:bucket_lava"}, {"bucket:bucket_lava", "tnt:tnt", "bucket:bucket_lava"}, {"bucket:bucket_lava", "bucket:bucket_lava", "bucket:bucket_lava"} } }, radius = 3, light_source = 10, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) _G[modname].circular_explode(pos, def.radius, "random", "default:lava_source", owner:get_player_name()) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) end }, true) local function spawnhouse(pos, player, node) core.remove_node(pos) pos = vector.subtract(pos, {x = 2, y = 0, z = 2}) local center = { x = pos.x + 2, y = pos.y, z = pos.z + 2 } local ppos = player:get_pos() local dx = ppos.x - center.x local dz = ppos.z - center.z local door_x, door_z if math.abs(dx) > math.abs(dz) then if dx > 0 then door_x, door_z = 4, 2 else door_x, door_z = 0, 2 end else if dz > 0 then door_x, door_z = 2, 4 else door_x, door_z = 2, 0 end end local blocks = {} for y = 0, 4 do for x = 0, 4 do for z = 0, 4 do local is_wall = (y == 0 or y == 4 or x == 0 or x == 4 or z == 0 or z == 4) if is_wall then if not ( x == door_x and z == door_z and (y == 1 or y == 2) ) then table.insert(blocks, { pos = {x = pos.x + x, y = pos.y + y, z = pos.z + z}, name = node }) end end end end end local delay = 0.075 local minus = 0 for i, data in ipairs(blocks) do local node = core.get_node(data.pos) if node.name == "air" or core.registered_nodes[node.name].buildable_to then core.after((i - minus) * delay, function() if not core.is_protected(data.pos, player:get_player_name()) then core.sound_play(modname.."_build", { pos = data.pos, pitch = randomFloat(0.5, 1.5) }) core.set_node(data.pos, {name = data.name}) end end) else minus = minus + 1 end end end register_fakenode("cobble") _G[modname].register_tnt({ name = modname..":cobble", description = S("House TNT"), craft = { recipe = { {"default:cobble", "default:cobble", "default:cobble"}, {"default:cobble", "tnt:tnt", "default:cobble"}, {"default:cobble", "default:cobble", "default:cobble"} } }, tiles = texture("default_cobble.png", modname.."_house_overlay.png"), boom = function(pos, def, owner) spawnhouse(pos, owner, modname..":fake_cobble") end }) register_fakenode("wood") _G[modname].register_tnt({ name = modname..":wood", description = S("Wooden House TNT"), tiles = texture("default_wood.png", modname.."_house_overlay.png"), craft = { recipe = { {"default:wood", "default:wood", "default:wood"}, {"default:wood", modname..":cobble", "default:wood"}, {"default:wood", "default:wood", "default:wood"} } }, boom = function(pos, def, owner) spawnhouse(pos, owner, modname..":fake_wood") end }) register_fakenode("brick") _G[modname].register_tnt({ name = modname..":brick", description = S("Brick House TNT"), tiles = texture("default_brick.png", modname.."_house_overlay.png"), craft = { recipe = { {"default:brick", "default:brick", "default:brick"}, {"default:brick", modname..":wood", "default:brick"}, {"default:brick", "default:brick", "default:brick"} } }, boom = function(pos, def, owner) spawnhouse(pos, owner, modname..":fake_brick") end }) _G[modname].register_tnt({ name = modname..":freeze", description = S("Freeze TNT"), tiles = texture("[multiply:#80c8ff", modname.."_title.png"), craft = { recipe = { {"default:ice", "default:ice", "default:ice"}, {"default:ice", "tnt:tnt", "default:ice"}, {"default:ice", "default:ice", "default:ice"} } }, radius = 5, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) _G[modname].circular_explode(pos, def.radius, nil, "default:ice", owner:get_player_name()) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) end }) _G[modname].register_tnt({ name = modname..":multiply", description = S("Multiply TNT"), tiles = texture("[multiply:#ffff33", modname.."_title.png"), multiplier = 3, craft = { recipe = { {"default:mese_crystal", "default:mese_crystal", "default:mese_crystal"}, {"default:mese_crystal", "tnt:tnt", "default:mese_crystal"}, {"default:mese_crystal", "default:mese_crystal", "default:mese_crystal"} } }, boom = _G[modname].boom }) local disallowed_entities = { "worldedit", "__builtin", "cart", "boat", "falling_node", "decor_api", "modern", "multidecor", "homedecor_common", "enchant" } local function check_entity(name) for _, find in pairs(disallowed_entities) do if string.find(name, find) then return false end end return true end local function entities() local registered = {} for name, _ in pairs(core.registered_entities) do if check_entity(name) then table.insert(registered, name) end end return registered end local entity = entities() if #entity ~= 0 then _G[modname].register_tnt({ name = modname..":animal", description = S("Animal TNT"), tiles = texture("[multiply:#854b20", modname.."_title.png"), craft = { recipe = { {"mobs:protector2", "mobs:leather", "mobs:protector2"}, {"mobs:leather", "tnt:tnt", "mobs:leather"}, {"mobs:protector2", "mobs:leather", "mobs:protector2"} } }, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) removetnt(pos, def.radius, owner) pos.y = pos.y + 2.5 for _ = 1, random(2, 10) do core.add_entity(pos, entity[random(1, #entity)]) end end }) end _G[modname].register_tnt({ name = modname..":snow", description = S("Snow TNT"), sounds = default.node_sound_snow_defaults(), tiles = texture("[multiply:#cedbf5", modname.."_title.png"), craft = { recipe = { {"default:snowblock", "default:snowblock", "default:snowblock"}, {"default:snowblock", "tnt:tnt", "default:snowblock"}, {"default:snowblock", "default:snowblock", "default:snowblock"} } }, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) removetnt(pos, def.radius, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) scatter(pos, "default:snow", 10, 250, owner:get_player_name()) end }) _G[modname].register_tnt({ name = modname..":sand", description = S("Sand Firework TNT"), sounds = default.node_sound_sand_defaults(), tiles = texture("[multiply:#fcfbb1", modname.."_title.png"), craft = { recipe = { {"default:sandstonebrick", "default:sandstonebrick", "default:sandstonebrick"}, {"default:sandstonebrick", "tnt:tnt", "default:sandstonebrick"}, {"default:sandstonebrick", "default:sandstonebrick", "default:sandstonebrick"} } }, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) removetnt(pos, def.radius, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) scatterboom(pos, "default:sand", 128, 16, owner:get_player_name()) end }) _G[modname].register_tnt({ name = modname..":sphere", description = S("Sphere TNT"), tiles = texture("[multiply:#fa7907^" .. modname .. "_circlular_side_overlay.png", modname.."_title.png"), radius = 5, no_fire = true, circular = true, craft = { recipe = { {"default:diamond", "default:mese_crystal", "default:diamond"}, {"default:mese_crystal", "tnt:tnt", "default:mese_crystal"}, {"default:diamond", "default:mese_crystal", "default:diamond"} } }, boom = _G[modname].boom }) local function copy_area(center, radius, owner) local min_x = center.x - radius local max_x = center.x + radius local min_y = center.y - radius local max_y = center.y + radius local min_z = center.z - radius local max_z = center.z + radius for x = min_x, max_x do for y = min_y, max_y do for z = min_z, max_z do local node = core.get_node({x = x, y = y, z = z}) local npos = {x = x, y = y + 64, z = z} if not core.is_protected(npos, owner) then core.set_node(npos, node) core.check_for_falling(npos) end end end end end _G[modname].register_tnt({ name = modname..":island", description = S("Floating Island TNT"), tiles = texture("[multiply:#32cf32", modname.."_island_side_overlay.png"), radius = 10, craft = { recipe = { {"default:dirt_with_grass", "default:dirt_with_grass", "default:dirt_with_grass"}, {"default:meselamp", "tnt:tnt", "default:meselamp"}, {"ethereal:crystal_block", "ethereal:crystal_block", "ethereal:crystal_block"} } }, boom = function(pos, def, owner) core.remove_node(pos) copy_area(pos, def.radius, owner:get_player_name()) pos.y = pos.y + 64 add_effects(pos, def.radius, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) end }) core.register_node(modname..":meteor", { description = S("Meteor"), tiles = {modname.."_meteor.png"}, sounds = default.node_sound_defaults(), groups = {cracky = 1, falling_node = 1} }) function randomFloat(lower, greater) return lower + random() * (greater - lower); end _G[modname].register_tnt({ name = modname..":tnt_meteor", description = S("Meteor TNT"), sounds = default.node_sound_defaults(), tiles = texture(modname.."_meteor.png", modname.."_title.png"), craft = { recipe = { {"dye:red", "uraniumstuff:uranium_protection_gem", "dye:red"}, {"default:obsidian", "tnt:tnt", "default:obsidian"}, {"dye:red", "default:mese", "dye:red"} } }, boom = function(pos, def, owner) local bpos = table.copy(pos) bpos.y = bpos.y - 1 local bnode = core.get_node(pos) local max = random(5, 15) for i = 0, max do local npos = table.copy(pos) npos.x = npos.x + random(-32, 32) npos.z = npos.z + random(-32, 32) core.after(randomFloat(0.75, 5.0), function() npos = get_highest(npos, bnode.name) if not core.is_protected(npos, owner) then tnt.boom(npos, def) core.after(0.25, function() core.set_node(npos, {name = modname..":meteor"}) core.check_for_falling(npos) end) end if i == max then removetnt(pos, def.radius, owner) end end) end end }) if core.get_modpath("lucky_block") then local Slb = core.get_translator("lucky_block") -- functions from lb mod local function effect(pos, amount, texture, min_size, max_size, radius, gravity, glow) radius = radius or 2 gravity = gravity or -10 core.add_particlespawner({ amount = amount, time = 0.25, minpos = pos, maxpos = pos, minvel = {x = -radius, y = -radius, z = -radius}, maxvel = {x = radius, y = radius, z = radius}, minacc = {x = 0, y = gravity, z = 0}, maxacc = {x = 0, y = gravity, z = 0}, minexptime = 0.1, maxexptime = 1, minsize = min_size or 0.5, maxsize = max_size or 1.0, texture = texture, glow = glow }) end local function super_lucky(pos, player) if random(10) < 8 then effect(pos, 25, "tnt_smoke.png", 8, 8, 1, -10, 0) local pitch = 1.0 + random(-10, 10) * 0.005 core.sound_play("fart1", { pos = pos, gain = 1.0, max_hear_distance = 10, pitch = pitch}, true) if random(5) == 1 then pos.y = pos.y + 0.5 core.add_item(pos, lucky_block.def_gold .. " " .. random(5)) end else core.set_node(pos, {name = "lucky_block:lucky_block"}) end end local super_lucky_list = { {"cus", super_lucky} } _G[modname].register_tnt({ name = modname..":lucky_block", description = Slb("Lucky Block").. " " ..S("TNT"), radius = 2, light_source = 3, tiles = texture(modname.."_lucky_block_background.png", modname.."_title.png"), craft = { recipe = { {"lucky_block:lucky_block", "lucky_block:lucky_block", "lucky_block:lucky_block"}, {"lucky_block:lucky_block", "tnt:tnt", "lucky_block:lucky_block"}, {"lucky_block:lucky_block", "lucky_block:lucky_block", "lucky_block:lucky_block"} } }, on_construct = function(pos) local meta = core.get_meta(pos) meta:set_string("infotext", Slb("Lucky Block").. " " ..S("TNT")) end, boom = function(pos, def, owner) removetnt(pos, def.radius, owner) add_effects(pos, def.radius, owner) for i = 1, 8 do core.after(i / 2, function() pos.y = pos.y + 1 core.sound_play(modname.."_pop", {pos = pos, max_hear_distance = 64, gain = 2.0}, true) lucky_block:open(pos, owner) end) end end }) _G[modname].register_tnt({ name = modname..":super_lucky_block", description = Slb("Super Lucky Block").. " " ..S("TNT"), radius = 2, light_source = 3, drops = {}, tiles = texture(modname.."_super_lucky_block_background.png", modname.."_title.png"), craft = { recipe = { {"lucky_block:super_lucky_block", "lucky_block:super_lucky_block", "lucky_block:super_lucky_block"}, {"lucky_block:super_lucky_block", "tnt:tnt", "lucky_block:super_lucky_block"}, {"lucky_block:super_lucky_block", "lucky_block:super_lucky_block", "lucky_block:super_lucky_block"} } }, on_construct = function(pos) local meta = core.get_meta(pos) meta:set_string("infotext", Slb("Super Lucky Block").. " " ..S("TNT")) end, boom = function(pos, def, owner) removetnt(pos, def.radius, owner) add_effects(pos, def.radius, owner) for i = 1, 8 do core.after(i / 2, function() pos.y = pos.y + 1 core.sound_play(modname.."_pop", {pos = pos, max_hear_distance = 64, gain = 2.0}, true) lucky_block:open(pos, owner, super_lucky_list) end) end end }) end local basic_materials_items = {} function string:startswith(start) return self:sub(1, #start) == start end core.register_on_mods_loaded(function() local i = 0 for name, _ in pairs(core.registered_craftitems) do if name:startswith("basic_materials:") then basic_materials_items[i] = name i = i + 1 end end end) _G[modname].register_tnt({ name = modname..":technical", description = S("Technical TNT"), tiles = texture(modname.."_technical_background.png", modname.."_title.png"), craft = { recipe = { {"basic_materials:energy_crystal_simple", "basic_materials:ic", "basic_materials:energy_crystal_simple"}, {modname..":multiply", "tnt:tnt", modname..":multiply"}, {"basic_materials:gear_steel", modname..":multiply", "basic_materials:gear_steel"} } }, sounds = default.node_sound_metal_defaults(), boom = function(pos, def, owner) local bnode = core.get_node(pos) local max = random(8, 40) for i = 0, max do local npos = table.copy(pos) npos.x = npos.x + randomFloat(-4, 4) npos.y = npos.y + randomFloat(16, 8) npos.z = npos.z + randomFloat(-4, 4) core.after(randomFloat(0.75, 1.5), function() if not core.is_protected(npos, owner) then core.sound_play(modname.."_pop", {pos = npos}) core.add_item(npos, basic_materials_items[random(0, #basic_materials_items)]) end if i == max then core.remove_node(pos) add_effects(pos, def.radius, owner) end end) end end }) core.register_craftitem(modname..":unstable_particle", { description = S("Unstable Particle"), inventory_image = modname.."_unstable_particle.png" }) if core.get_modpath("ethereal") then _G[modname].register_tnt({ name = modname..":blackhole", description = S("Blackhole TNT"), tiles = texture("[multiply:#141414", modname.."_title.png"), craft = { recipe = { {"ethereal:etherium_dust", "ethereal:crystal_gilly_staff", "ethereal:etherium_dust"}, {"ethereal:crystal_spike", "tnt:tnt", "ethereal:crystal_spike"}, {"ethereal:etherium_dust", "ethereal:crystal_spike", "ethereal:etherium_dust"} } }, radius = 12, ignite_timer = 10, on_ignite = function(pos, igniter) local duration = 9.75 local interval = 0.05 local radius = 32 local function suck_step(time_passed) if time_passed >= duration then return end local objects = core.get_objects_inside_radius(pos, radius) for _, obj in ipairs(objects) do if obj:is_player() or obj:get_luaentity() then local obj_pos = obj:get_pos() if obj_pos then local dx = pos.x - obj_pos.x local dy = pos.y - obj_pos.y local dz = pos.z - obj_pos.z local dist = math.sqrt(dx*dx + dy*dy + dz*dz) if dist > 0 then local strength = (radius - dist) * 0.25 obj:add_velocity({ x = dx / dist * strength, y = dy / dist * strength, z = dz / dist * strength }) end end end end core.after(interval, suck_step, time_passed + interval) end suck_step(0) end, boom = function(pos, def, owner) local radius = def.radius local objects = core.get_objects_inside_radius(pos, radius) for _, obj in ipairs(objects) do if obj:is_player() or obj:get_luaentity() then local obj_pos = obj:get_pos() if obj_pos then local dx = obj_pos.x - pos.x local dy = obj_pos.y - pos.y local dz = obj_pos.z - pos.z local dist = math.sqrt(dx*dx + dy*dy + dz*dz) if dist > 0 then local strength = (radius - dist) * 4 obj:add_velocity({ x = (dx / dist * strength) * 2, y = 6 + strength * 0.5, z = (dz / dist * strength) * 2 }) end end end end add_effects(pos, def.radius, owner) core.remove_node(pos) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) end }) _G[modname].node_sound_glitch_defaults = function(tbl) tbl = tbl or {} tbl.footstep = tbl.footstep or {name = modname.."_glitch_footsteps", gain = 0.8} tbl.dug = tbl.dug or {name = modname.."_glitch_footsteps", gain = 1.0} default.node_sound_defaults(tbl) return tbl end local glitch_active_restores = 0 local max_glitch_restores = 100 local function restore_glitch(pos) local meta = core.get_meta(pos) if meta:get_int(modname..":restored") == 1 then return end meta:set_int(modname..":restored", 1) local raw = meta:get_string(modname..":replace_node") if raw and (not (raw == "")) then local data = core.parse_json(raw) if data["name"] then core.set_node(pos, data) return true else return false end else return false end end local function restore_chain(pos) if glitch_active_restores >= max_glitch_restores then return end for dz = -1, 1 do for dy = -1, 1 do for dx = -1, 1 do local p = { x = pos.x + dx, y = pos.y + dy, z = pos.z + dz } local node = core.get_node(p) if node.name == modname..":glitch" then if glitch_active_restores >= max_glitch_restores then return end glitch_active_restores = glitch_active_restores + 1 core.after(1, function() restore_glitch(p) restore_chain(p) glitch_active_restores = glitch_active_restores - 1 end) end end end end end core.register_node(modname..":glitch", { tiles = { { name = modname.."_glitch_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 0.75 } } }, light_source = 1, sounds = _G[modname].node_sound_glitch_defaults(), description = S("Glitch Block"), groups = {cracky = 3}, drops = { max_items = 1, items = { { rarity = 4, items = {modname..":unstable_particle"}, } } }, on_dig = function(pos) if restore_glitch(pos) then restore_chain(pos) else core.remove_node(pos) end end }) core.register_craft({ recipe = { {modname..":unstable_particle", modname..":unstable_particle"}, {modname..":unstable_particle", modname..":unstable_particle"} }, output = modname..":glitch 2" }) core.register_craft({ recipe = { {"ethereal:crystal_block", "default:mese", "ethereal:crystal_block"}, {"default:mese", "bones:bones", "default:mese"}, {"ethereal:crystal_block", "default:mese", "ethereal:crystal_block"} }, output = modname..":unstable_particle 2" }) local function glitch_explode(pos, radius, replace, player_name) local replace_id = core.CONTENT_AIR if replace and core.registered_nodes[replace] then replace_id = core.get_content_id(replace) end pos = vector.round(pos) local p1 = vector.subtract(pos, radius) local p2 = vector.add(pos, radius) local vm = VoxelManip() local minp, maxp = vm:read_from_map(p1, p2) local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) local data = vm:get_data() data[a:index(pos.x, pos.y, pos.z)] = core.CONTENT_AIR for z = p1.z, p2.z do for y = p1.y, p2.y do for x = p1.x, p2.x do local current_pos = vector.new(x, y, z) if not core.is_protected(current_pos, player_name) then local vi = a:index(x, y, z) local cid = data[vi] if cid ~= core.CONTENT_AIR and cid ~= core.CONTENT_IGNORE then local old_pos = {x = x, y = y, z = z} local is_empty = true local inv = core.get_inventory({type="node", pos=old_pos}) if inv then for listname, _ in pairs(inv:get_lists()) do if not inv:is_empty(listname) then is_empty = false end end end if is_empty then local old_node = core.get_node(old_pos) data[vi] = replace_id core.after(0, function() local meta = core.get_meta(old_pos) meta:from_table({ fields = { [modname..":replace_node"] = core.write_json(old_node) } }) end) end end end end end end vm:set_data(data) vm:write_to_map() vm:update_liquids() if vm.close then vm:close() end core.log("action", "Glitch explosion at " .. core.pos_to_string(pos) .. " with radius " .. radius .. " by player " .. player_name) end _G[modname].register_tnt({ name = modname..":tnt_glitch", description = S("Glitch TNT"), tiles = texture(modname.."_glitch_background.png", modname.."_title.png"), craft = { recipe = { {modname..":unstable_particle", "basic_materials:energy_crystal_simple", modname..":unstable_particle"}, {modname..":unstable_particle", "tnt:tnt", modname..":unstable_particle"}, {modname..":unstable_particle", modname..":unstable_particle", modname..":unstable_particle"} } }, radius = 6, sound = modname.."_glitch", boom = function(pos, def, owner) core.remove_node(pos) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) glitch_explode(pos, def.radius, modname..":glitch", owner:get_player_name()) end }) end _G[modname].register_tnt({ name = modname..":tnt_poison", description = S("Poison TNT"), tiles = texture("[multiply:#00ff00", modname.."_poison_side_overlay.png"), craft = { recipe = { {"uraniumstuff:bucket_evil_goo", "uraniumstuff:bucket_evil_goo", "uraniumstuff:bucket_evil_goo"}, {"uraniumstuff:bucket_evil_goo", "tnt:tnt", "uraniumstuff:bucket_evil_goo"}, {"uraniumstuff:bucket_evil_goo", "uraniumstuff:bucket_evil_goo", "uraniumstuff:bucket_evil_goo"} } }, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) core.remove_node(pos) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) scatter(pos, modname..":poison", 8, 48, owner:get_player_name()) end }) _G[modname].register_tnt({ name = modname..":hurricane", description = S("Hurricane TNT"), tiles = texture("[multiply:#6e6e6e", modname.."_title.png"), craft = { recipe = { {"default:coalblock", "ethereal:etherium_dust", "default:coalblock"}, {"ethereal:etherium_dust", "tnt:tnt", "ethereal:etherium_dust"}, {"default:coalblock", "ethereal:etherium_dust", "default:coalblock"} } }, radius = 14, ignite_timer = 15, on_ignite = function(pos, igniter) local radius = 14 local duration = 14.75 local interval = 0.05 local angle = 0 local function spawn_swirl(time_passed) if time_passed >= duration then return end for i = 1, 8 do local a = angle + (i * math.pi / 4) local x = pos.x + math.cos(a) * 5 local z = pos.z + math.sin(a) * 5 local p = {x = x, y = pos.y + (i - 0.5), z = z} core.add_particle({ pos = p, velocity = { x = -math.sin(a) * 8, y = math.random() * 1.5, z = math.cos(a) * 8 }, acceleration = {x = 0, y = 0.2, z = 0}, expirationtime = 0.5, size = 10, texture = modname.."_fog.png", glow = 0 }) end angle = angle + 0.3 core.after(interval, spawn_swirl, time_passed + interval) end spawn_swirl(0) local function swirl_step(time_passed) if time_passed >= duration then return end local objs = core.get_objects_inside_radius(pos, radius) for _, obj in ipairs(objs) do local p = obj:get_pos() if p then local dir = vector.subtract(pos, p) local dist = vector.length(dir) if dist > 0.3 then dir = vector.normalize(dir) local tangent = { x = -dir.z, y = 0, z = dir.x } local edge = (dist / radius) / 8 local pull = edge / 8 local spin = 1.25 if dist > radius * 0.85 then obj:set_velocity({ x = dir.x * 20, y = 2, z = dir.z * 20 }) else obj:add_velocity({ x = tangent.x * spin + dir.x * pull, z = tangent.z * spin + dir.z * pull, y = 1.2 + edge * 1.5 }) end end end end core.after(interval, swirl_step, time_passed + interval) end swirl_step(0) end, boom = function(pos, def, owner) local radius = def.radius local objects = core.get_objects_inside_radius(pos, radius) for _, obj in ipairs(objects) do if obj:is_player() or obj:get_luaentity() then local obj_pos = obj:get_pos() if obj_pos then local dx = obj_pos.x - pos.x local dy = obj_pos.y - pos.y local dz = obj_pos.z - pos.z local dist = math.sqrt(dx*dx + dy*dy + dz*dz) if dist > 0 then local strength = (radius - dist) * 4 obj:add_velocity({ x = (dx / dist * strength) * 2, y = 6 + strength * 0.5, z = (dz / dist * strength) * 2 }) end end end end add_effects(pos, def.radius, owner) core.remove_node(pos) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) end }) local function get_nearest_player(pos, exclude_name) local nearest = nil local nearest_dist = math.huge for _, player in ipairs(core.get_connected_players()) do local name = player:get_player_name() if name ~= exclude_name then local p = player:get_pos() if p then local dist = vector.distance(pos, p) if dist < nearest_dist then nearest_dist = dist nearest = player end end end end return nearest end local function restore(pos, def, owner) local nname = modname .. ":" .. def.name core.set_node(pos, {type="node", name=nname}) def = core.registered_nodes[nname] if def.on_construct then def.on_construct(pos) end local meta = core.get_meta(pos) meta:set_string("owner", owner:get_player_name()) end _G[modname].register_tnt({ name = modname..":switch", description = S("Switch TNT"), tiles = texture(modname.."_switch_background.png", modname.."_switch_side_overlay.png"), craft = { recipe = { {"wool:red", "default:obsidian", "wool:blue"}, {"wool:red", "tnt:tnt", "wool:blue"}, {"wool:red", "default:obsidian", "wool:blue"} } }, boom = function(pos, def, owner) local meta = core.get_meta(pos) local placer_name = meta:get_string("owner2") if placer_name == "" then restore(pos, def, owner); return end local igniter_name = owner:get_player_name() local player1 = nil local player2 = nil if placer_name == igniter_name then local nearest = nil local nearest_dist = math.huge local nearest = get_nearest_player(pos, igniter_name) if not nearest then restore(pos, def, owner); return end player1 = owner player2 = nearest else local placer = core.get_player_by_name(placer_name) if not placer then restore(pos, def, owner); return end player1 = owner player2 = placer end local pos1 = player1:get_pos() local pos2 = player2:get_pos() if pos1 and pos2 then player1:set_pos(pos2) player2:set_pos(pos1) end add_effects(pos, def.radius, owner) core.remove_node(pos) local sound = def.sound or "tnt_explode" core.sound_play(sound, { pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128) }, true) end }) _G[modname].register_tnt({ name = modname..":chaos", description = S("Chaos TNT"), tiles = texture("[multiply:#ff4400", modname.."_title.png"), craft = { recipe = { {"tnt:tnt", "tnt:tnt", "tnt:tnt"}, {"tnt:tnt", modname..":multiply", "tnt:tnt"}, {"tnt:tnt", "tnt:tnt", "tnt:tnt"} } }, boom = function(pos, def, owner) local owner_name = owner:get_player_name() local count = random(6, 10) for i = 1, count do core.after(i - 1, function() local launch_pos = { x = pos.x, y = pos.y + 1, z = pos.z } if core.is_protected(launch_pos, owner_name) then return end core.set_node(launch_pos, {name = "tnt:tnt"}) local meta = core.get_meta(launch_pos) meta:set_string("owner", owner_name) meta:set_string("owner2", owner_name) core.swap_node(launch_pos, {name = "tnt:tnt_burning"}) local _, obj = core.spawn_falling_node(launch_pos) if obj and obj:is_valid() then local sign_x = (random(0, 1) == 0) and 1 or -1 local sign_z = (random(0, 1) == 0) and 1 or -1 local epos = table.copy(pos) epos.y = epos.y + 1 add_effects(epos, def.radius, owner, false) core.sound_play("tnt_explode", {pos = epos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) obj:set_velocity({ x = sign_x * randomFloat(3, 6), y = randomFloat(12, 20), z = sign_z * randomFloat(3, 6) }) end if i == count then core.after(1, function() removetnt(pos, def.radius, owner) tnt.boom(pos, def) end) end end) end end }) _G[modname].register_tnt({ name = modname..":fake", description = S("Fake TNT"), tiles = texture("[multiply:#fc3838", modname.."_fake_side_overlay.png"), craft = { type = "shapeless", recipe = { "default:gold_ingot", "tnt:tnt", "default:steel_ingot", "default:diamondblock" } }, boom = function(pos, def, owner) if not core.is_protected(pos, owner:get_player_name()) then core.remove_node(pos) add_effects(pos, def.radius, owner) core.sound_play("tnt_explode", {pos = pos, gain = 2.5, max_hear_distance = 128}, true) core.add_item(pos, modname..":fake") else core.remove_node(pos) end end }) register_fakenode("meselamp") register_fakenode("sandstonebrick") register_fakenode("sandstone_block") _G[modname].register_tnt({ name = modname..":labyrinth", description = S("Labyrinth TNT"), tiles = texture("[multiply:#8f741d", modname.."_title.png"), craft = { recipe = { {"default:sandstonebrick", modname..":unstable_particle", "default:sandstonebrick"}, {modname..":unstable_particle", "tnt:tnt", modname..":unstable_particle"}, {"default:sandstonebrick", modname..":unstable_particle", "default:sandstonebrick"} } }, boom = function(pos, def, owner) local owner_name = owner:get_player_name() local ppos = owner:get_pos() local SIZE = 19 local WALL_H = 3 local half = math.floor(SIZE / 2) local ox = math.floor(ppos.x) - half local oy = math.floor(ppos.y) local oz = math.floor(ppos.z) - half local grid = {} for i = 0, SIZE * SIZE - 1 do grid[i] = true end local function gidx(x, z) return math.floor(z) * SIZE + math.floor(x) end grid[gidx(1, 1)] = false local stack = {{1, 1}} while #stack > 0 do local top = stack[#stack] local cx, cz = top[1], top[2] local dirs = {{2,0},{-2,0},{0,2},{0,-2}} for i = 4, 2, -1 do local j = random(1, i) dirs[i], dirs[j] = dirs[j], dirs[i] end local found = false for _, d in ipairs(dirs) do local nx = cx + d[1] local nz = cz + d[2] if nx >= 1 and nx < SIZE and nz >= 1 and nz < SIZE and grid[gidx(nx, nz)] then grid[gidx((cx + nx) / 2, (cz + nz) / 2)] = false grid[gidx(nx, nz)] = false table.insert(stack, {nx, nz}) found = true break end end if not found then table.remove(stack) end end local p1 = {x = ox, y = oy - 1, z = oz} local p2 = {x = ox + SIZE-1, y = oy + WALL_H - 1, z = oz + SIZE-1} local vm = VoxelManip() local minp, maxp = vm:read_from_map(p1, p2) local va = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) local data = vm:get_data() local c_cobble = core.get_content_id(modname..":fake_sandstonebrick") local c_stone = core.get_content_id(modname..":fake_sandstone_block") local c_air = core.CONTENT_AIR local c_ignore = core.CONTENT_IGNORE for gz = 0, SIZE - 1 do for gx = 0, SIZE - 1 do local wx = ox + gx local wz = oz + gz local is_wall = grid[gidx(gx, gz)] local fi = va:index(wx, oy - 1, wz) if data[fi] ~= c_ignore then data[fi] = c_stone end for h = 0, WALL_H - 1 do local wi = va:index(wx, oy + h, wz) if data[wi] ~= c_ignore then data[wi] = is_wall and c_cobble or c_air end end end end vm:set_data(data) vm:write_to_map() vm:update_liquids() if vm.close then vm:close() end for gz = 1, SIZE - 2, 2 do for gx = 1, SIZE - 2, 2 do if not grid[gidx(gx, gz)] and random(1, 4) == 1 then local lpos = {x = ox + gx, y = oy + WALL_H - 1, z = oz + gz} if not core.is_protected(lpos, owner_name) then core.set_node(lpos, {name = modname..":fake_meselamp"}) end end end end owner:set_pos({ x = ox + 1, y = oy, z = oz + 1 }) add_effects(pos, def.radius, owner) core.remove_node(pos) core.sound_play("tnt_explode", { pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128) }, true) end }) _G[modname].register_tnt({ name = modname..":time", description = S("Long TNT"), tiles = texture("[multiply:#1d3d61", modname.."_time_side_overlay.png"), ignite_timer = 60, craft = { recipe = { {"ethereal:crystal_ingot", "basic_materials:ic", "ethereal:crystal_ingot"}, {"basic_materials:plastic_sheet", "tnt:tnt", "basic_materials:plastic_sheet"}, {"ethereal:crystal_ingot", "basic_materials:steel_wire", "ethereal:crystal_ingot"} } }, boom = tnt.boom }) register_fakenode("sandstone") _G[modname].register_tnt({ name = modname..":pyramid", description = S("Pyramid TNT"), tiles = texture("[multiply:#c8a020", modname.."_title.png"), craft = { recipe = { {"default:sandstone", "default:sandstone", "default:sandstone"}, {"default:sandstone", "tnt:tnt", "default:sandstone"}, {"default:sandstone", "default:sandstone", "default:sandstone"} } }, boom = function(pos, def, owner) local owner_name = owner:get_player_name() local BASE = 9 local half = math.floor(BASE / 2) local ox = math.floor(pos.x) - half local oy = math.floor(pos.y) local oz = math.floor(pos.z) - half local floors = math.ceil(BASE / 2) local p1 = {x = ox, y = oy - 1, z = oz} local p2 = {x = ox + BASE - 1, y = oy + floors, z = oz + BASE - 1} local vm = VoxelManip() local minp, maxp = vm:read_from_map(p1, p2) local va = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) local data = vm:get_data() local c_block = core.get_content_id(modname..":fake_sandstone") local c_air = core.CONTENT_AIR local c_ignore = core.CONTENT_IGNORE for k = 0, floors - 1 do local side = BASE - k * 2 if side <= 0 then break end local lox = ox + k local loz = oz + k local ly = oy + k local lox2 = lox + side - 1 local loz2 = loz + side - 1 local is_top = (side <= 3) for z = loz, loz2 do for x = lox, lox2 do local on_edge = (x == lox or x == lox2 or z == loz or z == loz2) if on_edge or is_top then local vi = va:index(x, ly, z) if data[vi] == c_air or data[vi] == c_ignore then data[vi] = c_block end end end end end vm:set_data(data) vm:write_to_map() vm:update_liquids() if vm.close then vm:close() end local entrance = { x = ox + half + 0.5, y = oy, z = oz - 1 } add_effects(pos, def.radius, owner) core.remove_node(pos) core.sound_play("tnt_explode", { pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128) }, true) end }) _G[modname].register_tnt({ name = modname..":x_two", description = Stnt("TNT") .. " x4", tiles = texture("[multiply:#540000", modname.."_title.png"), radius = 6, craft = { recipe = { {"tnt:tnt", "tnt:tnt", "tnt:tnt"}, {"tnt:tnt", "tnt:tnt", "tnt:tnt"}, {"tnt:tnt", "tnt:tnt", "tnt:tnt"} } }, boom = tnt.boom }) _G[modname].register_tnt({ name = modname..":x_sixteen", description = Stnt("TNT") .. " x16", tiles = texture("[multiply:#bd2d99", modname.."_title.png"), radius = 12, craft = { recipe = { {modname..":x_two", modname..":x_two", modname..":x_two"}, {modname..":x_two", modname..":x_two", modname..":x_two"}, {modname..":x_two", modname..":x_two", modname..":x_two"} } }, boom = tnt.boom }) _G[modname].register_tnt({ name = modname..":x_fifty", description = Stnt("TNT") .. " x50", tiles = texture("[multiply:#2dbd8f", modname.."_title.png"), radius = 18, craft = { recipe = { {modname..":x_sixteen", modname..":x_sixteen", modname..":x_sixteen"}, {modname..":x_sixteen", modname..":x_sixteen", modname..":x_sixteen"}, {modname..":x_sixteen", modname..":x_sixteen", modname..":x_sixteen"} } }, boom = tnt.boom }) _G[modname].register_tnt({ name = modname..":x_hundred", description = Stnt("TNT") .. " x100", tiles = texture("[multiply:#612e1d", modname.."_title.png"), radius = 24, craft = { recipe = { {modname..":x_fifty", modname..":x_fifty", modname..":x_fifty"}, {modname..":x_fifty", modname..":x_fifty", modname..":x_fifty"}, {modname..":x_fifty", modname..":x_fifty", modname..":x_fifty"} } }, boom = tnt.boom }) _G[modname].register_tnt({ name = modname..":x_five_hundred", description = Stnt("TNT") .. " x500", tiles = texture("[multiply:#252616", modname.."_title.png"), radius = 32, craft = { recipe = { {modname..":x_hundred", modname..":x_hundred", modname..":x_hundred"}, {modname..":x_hundred", modname..":x_hundred", modname..":x_hundred"}, {modname..":x_hundred", modname..":x_hundred", modname..":x_hundred"} } }, boom = tnt.boom }) _G[modname].register_tnt({ name = modname..":x_thousand", description = Stnt("TNT") .. " x1000", tiles = texture("[multiply:#10101a", modname.."_title.png"), radius = 40, craft = { recipe = { {modname..":x_five_hundred", modname..":x_five_hundred", modname..":x_five_hundred"}, {modname..":x_five_hundred", modname..":x_five_hundred", modname..":x_five_hundred"}, {modname..":x_five_hundred", modname..":x_five_hundred", modname..":x_five_hundred"} } }, boom = tnt.boom }) register_fakenode("stone") _G[modname].register_tnt({ name = modname..":trap", description = S("Trap TNT"), tiles = texture("[multiply:#a32607", modname.."_trap_side_overlay.png"), craft = { recipe = { {"default:stone", "mobs:lava_orb", "default:stone"}, {"default:stone", "tnt:tnt", "default:stone"}, {"default:stone", "bucket:bucket_lava", "default:stone"} } }, boom = function(pos, def, owner) local owner_name = owner:get_player_name() local trapped = get_nearest_player(pos, owner_name) if not trapped then restore(pos, def, owner); return end local DEPTH = 60 local LAVA_FROM = 58 local PIT_RADIUS = 1 local WALL_EXTRA = 1 local p1 = { x = pos.x - PIT_RADIUS - WALL_EXTRA, y = pos.y - DEPTH, z = pos.z - PIT_RADIUS - WALL_EXTRA } local p2 = { x = pos.x + PIT_RADIUS + WALL_EXTRA, y = pos.y + 1, z = pos.z + PIT_RADIUS + WALL_EXTRA } local vm = VoxelManip() local minp, maxp = vm:read_from_map(p1, p2) local va = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) local data = vm:get_data() local c_stone = core.get_content_id(modname..":fake_stone") local c_lava = core.get_content_id("default:lava_source") local c_air = core.CONTENT_AIR local c_ignore = core.CONTENT_IGNORE local pr = math.floor(PIT_RADIUS) local wr = math.floor(PIT_RADIUS + WALL_EXTRA) for dy = 0, DEPTH do local y = math.floor(pos.y) - dy for dz = -wr, wr do for dx = -wr, wr do local x = math.floor(pos.x) + dx local z = math.floor(pos.z) + dz local vi = va:index(x, y, z) if data[vi] == c_ignore then goto continue end local in_pit = (dx >= -pr and dx <= pr and dz >= -pr and dz <= pr) local is_wall = not in_pit if is_wall then if not core.is_protected({x=x,y=y,z=z}, owner_name) then data[vi] = c_stone end else if dy >= LAVA_FROM then data[vi] = c_lava else data[vi] = c_air end end ::continue:: end end end vm:set_data(data) vm:write_to_map() vm:update_liquids() if vm.close then vm:close() end trapped:set_pos(pos) add_effects(pos, def.radius, owner) core.remove_node(pos) core.sound_play("tnt_explode", { pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128) }, true) end }) _G[modname].register_tnt({ name = modname..":square", description = S("Cube TNT"), tiles = texture("[multiply:#a498f5", modname.."_title.png"), no_fire = true, craft = { recipe = { {"default:mese_crystal", "default:steel_ingot", "default:mese_crystal"}, {"default:steelblock", "tnt:tnt", "default:steelblock"}, {"default:mese_crystal", "default:steel_ingot", "default:mese_crystal"} } }, boom = function(pos, def, owner) _G[modname].square_explode(pos, def.radius, nil, owner:get_player_name()) add_effects(pos, def.radius, owner) core.sound_play("tnt_explode", { pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128) }, true) end }) _G[modname].register_tnt({ name = modname..":water", description = S("Water TNT"), waving = 3, tiles = { top = { name = modname.."_water_top.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 2, } }, bottom = { name = "default_water_source_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 2, } }, side = { name = modname.."_water_side.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 2, } }, burning = { name = modname.."_water_burning.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 2, } } }, craft = { recipe = { {"bucket:bucket_water", "bucket:bucket_water", "bucket:bucket_water"}, {"bucket:bucket_water", "tnt:tnt", "bucket:bucket_water"}, {"bucket:bucket_water", "bucket:bucket_water", "bucket:bucket_water"} } }, post_effect_color = {a = 103, r = 30, g = 60, b = 90}, is_ground_content = false, drawtype = "liquid", sounds = default.node_sound_water_defaults(), use_texture_alpha = "blend", paramtype = "light", radius = 2, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) core.remove_node(pos) _G[modname].circular_explode(pos, def.radius, nil, "default:water_source", owner:get_player_name()) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) end }, true) _G[modname].register_tnt({ name = modname..":wash", description = S("Wash TNT"), tiles = texture_top(modname.."_wash_background.png", modname.."_title.png", modname.."_wash_top_background.png"), craft = { recipe = { {"bucket:bucket_river_water", "too_many_tnt:unstable_particle", "bucket:bucket_river_water"}, {"too_many_tnt:unstable_particle", "tnt:tnt", "too_many_tnt:unstable_particle"}, {"bucket:bucket_river_water", "too_many_tnt:unstable_particle", "bucket:bucket_river_water"} } }, radius = 10, boom = function(pos, def, owner) local radius = def.radius local p1 = {x = pos.x - radius, y = pos.y - radius, z = pos.z - radius} local p2 = {x = pos.x + radius, y = pos.y + radius, z = pos.z + radius} for x = p1.x, p2.x do for y = p1.y, p2.y do for z = p1.z, p2.z do if core.is_protected({x=x, y=y, z=z}, owner:get_player_name()) then restore(pos, def, owner); return end end end end add_effects(pos, radius, owner) core.remove_node(pos) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(radius * 20, 128)}, true) core.delete_area(p1, p2) core.emerge_area(p1, p2) end }) if core.get_modpath("xdecor") and core.get_modpath("stairs") and core.get_modpath("decor_api") and core.get_modpath("building_blocks") and core.get_modpath("homedecor_common") and core.get_modpath("laptop") and core.get_modpath("doors") then _G[modname].register_tnt({ name = modname..":house", description = S("Super House TNT"), tiles = texture("default_diamond_block.png", modname.."_house_overlay.png"), craft = { recipe = { {modname..":super_diamondblock", modname..":super_diamondblock", modname..":super_diamondblock"}, {modname..":super_diamondblock", modname..":brick", modname..":super_diamondblock"}, {modname..":super_diamondblock", modname..":super_diamondblock", modname..":super_diamondblock"} } }, boom = function(pos, def, owner) local name = "default:dirt_with_grass" local above_pos = table.copy(pos) above_pos.y = above_pos.y + 1 local below_pos = table.copy(pos) below_pos.y = below_pos.y - 1 local below_node = core.get_node(below_pos) local stone = nil if (below_node.name ~= name) and (string.find(below_node.name, ":dirt") or string.find(below_node.name, "_dirt")) or ((string.find(below_node.name, ":stone") or string.find(below_node.name, ":cobble")) and not string.find(below_node.name, ":stone_with_")) then name = below_node.name if string.find(below_node.name, "mossycobble") then stone = "default:cobble" else stone = below_node.name end end core.remove_node(pos) add_effects(pos, def.radius, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) pos.y = pos.y - 7 local house_path = modpath.."/schematics/supah_fuckass_house.mts" core.place_schematic(pos, house_path, "0", {}, true, {}) local schematic = core.read_schematic(house_path, {}) local size = schematic.size for x = 0, size.x - 1 do for y = 0, size.y - 1 do for z = 0, size.z - 1 do local npos = { x = pos.x + x, y = pos.y + y, z = pos.z + z, } local node = core.get_node(npos) local cnode = table.copy(node) if stone and ((node.name == "default:dirt_with_grass" or node.name == "default:dirt") and cnode.name ~= stone) or (node.name == "default:dirt_with_grass" and cnode.name ~= name) then cnode.name = stone or name core.set_node(npos, cnode) end if node and node.name ~= "air" and node.name ~= "ignore" then local ndef = core.registered_nodes[node.name] if ndef then if ndef.on_construct then ndef.on_construct(npos) end if ndef.after_place_node and not string.find(node.name, "bed") then ndef.after_place_node(npos, owner, ItemStack(modname..":"..def.name), {type="node", under=below_pos, above=above_pos}) end end end end end end end }) end if core.get_modpath("mesecons") then _G[modname].register_tnt({ name = modname..":piston_door", description = S("Piston Door TNT"), tiles = texture("jeija_microcontroller_sides.png", modname.."_title.png"), craft = { recipe = { {"mesecons_pistons:piston_sticky_off", "mesecons_walllever:wall_lever_off", "mesecons_pistons:piston_sticky_off"}, {"mesecons_delayer:delayer_off_1", "tnt:tnt", "mesecons_delayer:delayer_off_1"}, {"mesecons_pistons:piston_sticky_off", "mesecons:wire_00000000_off", "mesecons_pistons:piston_sticky_off"} } }, boom = function(pos, def, owner) core.remove_node(pos) add_effects(pos, def.radius, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) pos.y = pos.y - 2 core.place_schematic(pos, modpath.."/schematics/2_by_2_piston_door.mts", "0", {}, true) end }) core.register_craft({ output = modname..":tnt_remote", recipe = { {"mesecons:wire_00000000_off", "mesecons:wire_00000000_off", "mesecons:wire_00000000_off"}, {"mesecons:wire_00000000_off", "tnt:tnt", "mesecons:wire_00000000_off"}, {"mesecons:wire_00000000_off", "mesecons:wire_00000000_off", "mesecons:wire_00000000_off"} } }) if core.get_modpath("dye") then core.register_craft({ type = "shapeless", output = modname..":remote", recipe = { "mesecons_fpga:programmer", "mesecons_button:button_off", "mesecons:wire_00000000_off", "mesecons:wire_00000000_off", "default:steel_ingot", "default:steel_ingot", "dye:red" } }) end end local remote_tnt = texture("[multiply:#450e0e", modname.."_remote_side_overlay.png") core.register_node(modname..":tnt_remote", { description = S("Remote TNT"), tiles = {remote_tnt.top, remote_tnt.bottom, remote_tnt.side}, groups = {dig_immediate = 2, tnt = 1}, sounds = default.node_sound_wood_defaults(), on_blast = function(pos) core.after(0.1, function() tnt.boom(pos) end) end }) local remotemeta = modname.."_pos" core.register_craftitem(modname..":remote", { description = S("Remote"), stack_max = 1, inventory_image = modname.."_remote.png", on_use = function(itemstack, user, pointed_thing) local pname = user:get_player_name() local meta = itemstack:get_meta() local pos = meta:get_string(remotemeta) local ppos = core.get_pointed_thing_position(pointed_thing) if ppos ~= nil then local pnode = core.get_node(ppos) if pnode.name == modname..":tnt_remote" then if pos ~= nil and pos ~= "" then meta:set_string(remotemeta, "") meta:set_string("description", S("Remote")) else local str = vector.to_string(ppos) meta:set_string(remotemeta, str) meta:set_string("description", S("Remote").." "..str) end return itemstack end end if pos ~= nil and pos ~= "" then pos = vector.from_string(pos) if core.get_node(pos).name == modname..":tnt_remote" then local nmeta = core.get_meta(pos) nmeta:set_string("owner", pname) meta:set_string(remotemeta, "") meta:set_string("description", S("Remote")) core.sound_play(modname.."_spark", {to_player = pname}) tnt.boom(pos, {radius = 4}) end end return itemstack end }) _G[modname].register_tnt({ name = modname..":teleport", description = S("Teleport TNT"), tiles = texture("[multiply:#915966", modname.."_title.png"), craft = { recipe = { {"basic_materials:energy_crystal_simple", modname..":unstable_particle", "basic_materials:energy_crystal_simple"}, {modname..":unstable_particle", "tnt:tnt", modname..":unstable_particle"}, {"basic_materials:energy_crystal_simple", modname..":unstable_particle", "basic_materials:energy_crystal_simple"} } }, boom = function(pos, def, owner) local owner_name = owner:get_player_name() local trapped = get_nearest_player(pos, owner_name) if not trapped then restore(pos, def, owner); return end add_effects(pos, def.radius, owner) removetnt(pos, def.radius, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) local tp = table.copy(pos) tp.y = tp.y + 10000 trapped:set_pos(tp) end }) _G[modname].register_tnt({ name = modname..":random_teleport", description = S("Random Teleport TNT"), tiles = texture("[multiply:#b595b8", modname.."_title.png"), craft = { recipe = { {"default:mese_crystal", modname..":unstable_particle", "default:mese_crystal"}, {modname..":unstable_particle", "too_many_tnt:teleport", modname..":unstable_particle"}, {"default:mese_crystal", modname..":unstable_particle", "default:mese_crystal"} } }, boom = function(pos, def, owner) local owner_name = owner:get_player_name() local limit = core.get_mapgen_setting("mapgen_limit") local x = random(-30000, 30000) local trapped = get_nearest_player(pos, owner_name) if not trapped then restore(pos, def, owner); return end add_effects(pos, def.radius, owner) removetnt(pos, def.radius, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) local tp = table.copy(pos) trapped:set_pos(tp) end }) _G[modname].register_tnt({ name = modname..":flip", description = S("Reverse TNT"), tiles = texture("[multiply:#4a9e6b", modname.."_title.png^[transformFY"), flip = true, craft = { recipe = { {"default:gravel", "default:diamond", "default:gravel"}, {"default:mossycobble", "tnt:tnt", "default:mossycobble"}, {"default:gravel", "default:diamond", "default:gravel"} } }, radius = 5, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) core.remove_node(pos) local sound = def.sound or "tnt_explode" core.sound_play(sound, { pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128) }, true) local r = def.radius for dx = -r, r do for dz = -r, r do if dx*dx + dz*dz <= r*r then local col = {} local cx = pos.x + dx local cz = pos.z + dz for dy = -r, r do local p = {x = cx, y = pos.y + dy, z = cz} local node = core.get_node(p) table.insert(col, node) end local len = #col for dy = -r, r do local p = {x = cx, y = pos.y + dy, z = cz} local flipped = col[len - (dy + r)] if flipped then core.set_node(p, flipped) core.check_for_falling(p) end end end end end end }) _G[modname].register_tnt({ name = modname..":fire", description = S("Fire TNT"), tiles = texture("[multiply:#ff8800", modname.."_title.png"), craft = { recipe = { {"fire:flint_and_steel", "fire:flint_and_steel", "fire:flint_and_steel"}, {"fire:flint_and_steel", "tnt:tnt", "fire:flint_and_steel"}, {"fire:flint_and_steel", "fire:flint_and_steel", "fire:flint_and_steel"} } }, radius = 5, boom = function(pos, def, owner) core.remove_node(pos) add_effects(pos, def.radius, owner) _G[modname].circular_explode(pos, def.radius, nil, "fire:basic_flame", owner:get_player_name()) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) end }) local disallowed_nodes = { "tnt", "grass", "mesecons", "stone_with_", "stair", "_cube", "_open", "micropanel", "farming", "leaves", "door", "decor_api", "modern", "_flowing", "mineral_", "multidecor" } local function check_node(name) for _, find in pairs(disallowed_nodes) do if string.find(name, find) then return false end end return true end local function nodes() local registered = {} for name, def in pairs(core.registered_nodes) do if def.groups["not_in_creative_inventory"] ~= 1 and not (def.drawtype == "airlike" or def.air_equivalent) and check_node(def.name) then def.name = name table.insert(registered, def) end end return registered end local function items() local registered = {} for name, d in pairs(core.registered_craftitems) do if d.groups["not_in_creative_inventory"] ~= 1 then table.insert(registered, name) end end return registered end if core.get_modpath("gloopblocks") then local nodes = nodes() if #nodes ~= 0 then _G[modname].register_tnt({ name = modname..":random", description = S("Random TNT"), tiles = texture(modname.."_random_background.png", modname.."_title.png"), craft = { recipe = { {modname..":glitch_block", "gloopblocks:rainbow_block_horizontal", modname..":glitch_block"}, {"gloopblocks:rainbow_block_horizontal", "tnt:tnt", "gloopblocks:rainbow_block_horizontal"}, {modname..":glitch_block", "gloopblocks:rainbow_block_horizontal", modname..":glitch_block"} } }, radius = 2, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) core.remove_node(pos) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) local above_pos = table.copy(pos) above_pos.y = above_pos.y + 1 local below_pos = table.copy(pos) below_pos.y = below_pos.y - 1 local pname = owner:get_player_name() local ndef = nodes[random(0, #nodes)] if ndef then core.set_node(pos, {name = ndef.name}) local meta = core.get_meta(pos) meta:set_string("owner", pname) meta:set_string("owner2", pname) if ndef.on_construct then ndef.on_construct(pos) end if ndef.after_place_node then ndef.after_place_node(pos, owner, ItemStack(modname..":"..def.name), {type="node", under=below_pos, above=above_pos}) end core.check_for_falling(pos) end end }) end local items = items() if #items ~= 0 then _G[modname].register_tnt({ name = modname..":random_item", description = S("Random Item TNT"), tiles = texture(modname.."_random_item_background.png", modname.."_title.png"), craft = { recipe = { {modname..":glitch_block", "gloopblocks:rainbow_block_horizontal", modname..":glitch_block"}, {"gloopblocks:rainbow_block_horizontal", modname..":random", "gloopblocks:rainbow_block_horizontal"}, {modname..":glitch_block", "gloopblocks:rainbow_block_horizontal", modname..":glitch_block"} } }, radius = 2, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) core.remove_node(pos) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) local above_pos = table.copy(pos) above_pos.y = above_pos.y + 1 local below_pos = table.copy(pos) below_pos.y = below_pos.y - 1 local pname = owner:get_player_name() local iname = items[random(0, #items)] core.add_item(pos, iname) end }) end end _G[modname].register_tnt({ name = modname..":shuffle", description = S("Shuffle TNT"), tiles = texture("[multiply:#7b4fa8", modname.."_title.png"), craft = { recipe = { {"default:diamondblock", "default:mese", "default:diamondblock"}, {"default:mese", "tnt:tnt", "default:mese"}, {"default:diamondblock", "default:mese", "default:diamondblock"} } }, radius = 4, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) core.remove_node(pos) local sound = def.sound or "tnt_explode" core.sound_play(sound, { pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128) }, true) local r = def.radius local positions = {} local nodes = {} for dx = -r, r do for dy = -r, r do for dz = -r, r do if dx*dx + dy*dy + dz*dz <= r*r then local p = { x = pos.x + dx, y = pos.y + dy, z = pos.z + dz } local node = core.get_node(p) if node.name ~= "air" and node.name ~= "ignore" then table.insert(positions, p) table.insert(nodes, node) end end end end end local n = #nodes for i = n, 2, -1 do local j = math.random(1, i) nodes[i], nodes[j] = nodes[j], nodes[i] end for i = 1, #positions do local pos = positions[i] core.set_node(pos, nodes[i]) local ndef = core.registered_nodes[nodes[i].name] if ndef.on_construct then ndef.on_construct(pos) end local above_pos = table.copy(pos) above_pos.y = above_pos.y + 1 local below_pos = table.copy(pos) below_pos.y = below_pos.y - 1 if ndef.after_place_node then ndef.after_place_node(pos, owner, ItemStack(modname..":"..def.name), {type="node", under=below_pos, above=above_pos}) end core.check_for_falling(pos) end end }) _G[modname].register_tnt({ name = modname..":omega_nuke", description = S("Omega TNT"), radius = 96, tiles = texture("[multiply:#290000", modname.."_omega_side_overlay.png"), ignite_timer = 15, craft = { recipe = { {modname..":atomic_bomb", modname..":atomic_bomb", modname..":atomic_bomb"}, {modname..":atomic_bomb", modname..":nuke", modname..":atomic_bomb"}, {modname..":atomic_bomb", modname..":atomic_bomb", modname..":atomic_bomb"} } }, boom = function(pos, def, owner) tnt.boom(pos, def) core.after(5, function() core.chat_send_all(core.colorize("#8a0000", S("OMEGA ATOMIC BOMB EXPLODED BY @1!", owner:get_player_name()))) pos.y = pos.y scatter(pos, modname..":poison", 175, 480, owner:get_player_name()) end) end }) _G[modname].register_tnt({ name = modname..":sponge", description = S("Sponge TNT"), tiles = texture(modname.."_sponge_background.png", modname.."_title.png"), craft = { recipe = { {"bucket:bucket_empty", "bucket:bucket_empty", "bucket:bucket_empty"}, {"bucket:bucket_empty", "tnt:tnt", "bucket:bucket_empty"}, {"bucket:bucket_empty", "bucket:bucket_empty", "bucket:bucket_empty"} } }, radius = 6, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) core.remove_node(pos, def.radius, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, { pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128) }, true) local r = def.radius for dx = -r, r do for dy = -r, r do for dz = -r, r do if dx*dx + dy*dy + dz*dz <= r*r then local p = { x = pos.x + dx, y = pos.y + dy, z = pos.z + dz } local name = core.get_node(p).name if string.find(name, "_flowing") or string.find(name, "_source") then core.remove_node(p) end end end end end end }) local confetti_colors = { "#ff0000", "#00ff00", "#0000ff", "#00ffff", "#ff00ff", "#ffff00" } _G[modname].register_tnt({ name = modname..":confetti", description = S("Confetti TNT"), tiles = texture("[multiply:#ffffff", modname.."_confetti_side_overlay.png"), craft = { recipe = { {"dye:red", "default:paper", "dye:blue"}, {"default:paper", "tnt:tnt", "default:paper"}, {"dye:green", "default:paper", "dye:yellow"} } }, radius = 2, boom = function(pos, def, owner) removetnt(pos, def.radius, owner) core.sound_play(modname.."_confetti", { pos = pos, max_hear_distance = math.min(def.radius * 20, 128) }, true) pos.y = pos.y + 0.5 for i = 0, random(12, 20) do core.add_particle({ pos = pos, velocity = {x=randomFloat(-1.75, 1.75), y=randomFloat(6.5, 7), z=randomFloat(-1.75, 1.75)}, acceleration = {x=0, y=-9.80665, z=0}, -- my physics knowledge got to use expirationtime = 5, size = 3, collision_removal = true, collisiondetection = true, texture = modname.."_confetti_particle.png^[multiply:" .. confetti_colors[random(1, #confetti_colors)] }) end end }) if core.get_modpath("mobs_monster") then local challenge_entities = { "mobs_monster:mese_monster", "mobs_monster:oerkki", "mobs_monster:dungeon_master", "mobs_monster:lava_flan" } _G[modname].register_tnt({ name = modname..":challenge", description = S("Challenge TNT"), tiles = texture("[multiply:#3d21cc", modname.."_title.png"), craft = { recipe = { {"mobs:protector2", "mobs:leather", "mobs:protector2"}, {"mobs:leather", modname..":animal", "mobs:leather"}, {"mobs:protector2", "mobs:leather", "mobs:protector2"} } }, radius = 2, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) core.remove_node(pos, def.radius, owner) core.sound_play(def.sound or "tnt_explode", { pos = pos, max_hear_distance = math.min(def.radius * 20, 128) }, true) local entity = challenge_entities[random(1, #challenge_entities)] for i = 1, random(5, 10) do local epos = table.copy(pos) epos.x = epos.x + random(-10, 10) epos.z = epos.z + random(-10, 10) epos = get_highest(epos) core.after(i, function() core.add_entity(epos, entity) core.sound_play(modname.."_pop", {pos = epos, gain = 2.0, max_hear_distance = 32}) end) end end }) end if core.get_modpath("fireworks") then local fireworks_entities = { "red", "orange", "violet", "green" } _G[modname].register_tnt({ name = modname..":firework", description = S("Firework TNT"), tiles = texture("[multiply:#a82222", "firework_red.png"), craft = { recipe = { {"fireworks:red", "fireworks:green", "fireworks:violet"}, {"fireworks:orange", "tnt:tnt", "fireworks:orange"}, {"fireworks:violet", "fireworks:green", "fireworks:red"} } }, radius = 2, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) core.remove_node(pos, def.radius, owner) core.sound_play(def.sound or "tnt_explode", { pos = pos, max_hear_distance = math.min(def.radius * 20, 128) }, true) for i = 1, random(3, 6) do local epos = table.copy(pos) epos.x = epos.x + random(-4, 4) epos.z = epos.z + random(-4, 4) epos = get_highest(epos) core.after(i / 2, function() fireworks_activate(epos, fireworks_entities[random(1, #fireworks_entities)]) end) end end }) end local function flowers() local registered = {} for name, def in pairs(core.registered_nodes) do if string.find(name, "flowers:") then def.name = name table.insert(registered, name) end end return registered end local flowers = flowers() if core.get_modpath("flowers") and #flowers ~= 0 then _G[modname].register_tnt({ name = modname..":flower", description = S("Flower TNT"), tiles = texture("[multiply:#eb8fd1", "flowers_rose.png"), craft = { recipe = { {"flowers:geranium", "flowers:viola", "flowers:chrysanthemum_green"}, {"flowers:rose", "tnt:tnt", "flowers:dandelion_yellow"}, {"flowers:tulip", "flowers:tulip_black", "flowers:dandelion_white"} } }, radius = 2, boom = function(pos, def, owner) add_effects(pos, def.radius, owner) core.remove_node(pos, def.radius, owner) core.sound_play(def.sound or "tnt_explode", { pos = pos, max_hear_distance = math.min(def.radius * 20, 128) }, true) for i = 1, random(6, 8) do local epos = table.copy(pos) epos.x = epos.x + random(-5, 5) epos.z = epos.z + random(-5, 5) epos = get_highest(epos) core.after(i / 2, function() core.set_node(epos, {name = flowers[random(1, #flowers)]}) core.sound_play(modname.."_pop", {pos = epos, gain = 2.0, max_hear_distance = 32}) end) end end }) end local trees = { core.get_modpath("default") .. "/schematics/aspen_tree.mts", core.get_modpath("default") .. "/schematics/apple_tree.mts", core.get_modpath("default") .. "/schematics/jungle_tree.mts", core.get_modpath("default") .. "/schematics/pine_tree.mts", core.get_modpath("default") .. "/schematics/acacia_tree.mts" } _G[modname].register_tnt({ name = modname..":tree", description = S("Tree TNT"), tiles = texture("[multiply:#45270e", "too_many_tnt_tree_side_overlay.png"), craft = { recipe = { {"default:aspen_sapling", "default:tree", core.get_modpath("flowers") and "flowers:mushroom_red" or "default:large_cactus_seedling"}, {"default:junglesapling", "tnt:tnt", "default:acacia_sappling"}, {"default:pine_sapling", "default:sapling", "default:emergent_jungle_sappling"} } }, radius = 2, boom = function(pos, def, owner) core.remove_node(pos) add_effects(pos, def.radius, owner) local sound = def.sound or "tnt_explode" core.sound_play(sound, {pos = pos, gain = 2.5, max_hear_distance = math.min(def.radius * 20, 128)}, true) core.place_schematic(pos, trees[random(1, #trees)], "random", {}, true, "place_center_x, place_center_z") end })