code:
local S = minetest.get_translator(minetest.get_current_modname())

local torchspine_min_delay = df_trees.config.blood_thorn_delay_multiplier*df_trees.config.tree_min_growth_delay
local torchspine_max_delay = df_trees.config.blood_thorn_delay_multiplier*df_trees.config.tree_max_growth_delay

local looped_node_sound_modpath = minetest.get_modpath("looped_node_sound")

-- Rather than make this whole mod depend on subterrane just for this, I copied and pasted a chunk of stalactite code.
local x_disp = 0.125
local z_disp = 0.125

local function copy_pointed_thing(pointed_thing)
	return {
		type  = pointed_thing.type,
		above = pointed_thing.above and vector.copy(pointed_thing.above),
		under = pointed_thing.under and vector.copy(pointed_thing.under),
		ref   = pointed_thing.ref,
	}
end

local stal_on_place = function(itemstack, placer, pointed_thing)
	local pt = pointed_thing
	-- check if pointing at a node
	if not pt then
		return itemstack
	end
	if pt.type ~= "node" then
		return itemstack
	end

	local under = minetest.get_node(pt.under)
	local above = minetest.get_node(pt.above)

	if minetest.is_protected(pt.above, placer:get_player_name()) then
		minetest.record_protection_violation(pt.above, placer:get_player_name())
		return
	end

	-- return if any of the nodes is not registered
	if not minetest.registered_nodes[under.name] or not minetest.registered_nodes[above.name] then
		return itemstack
	end
	-- check if you can replace the node above the pointed node
	if not minetest.registered_nodes[above.name].buildable_to then
		return itemstack
	end

	local new_param2
	-- check if pointing at an existing stalactite
	if minetest.get_item_group(under.name, "subterrane_stal_align") ~= 0 then
		new_param2 = under.param2
	else
		new_param2 = math.random(0,3)
	end

	-- add the node and remove 1 item from the itemstack
	local newnode= {name = itemstack:get_name(), param2 = new_param2, param1=0}
	local oldnode= minetest.get_node(pt.above)
	minetest.add_node(pt.above, {name = itemstack:get_name(), param2 = new_param2})

	-- Run script hook
	local take_item = true
	for _, callback in ipairs(core.registered_on_placenodes) do
		-- Deepcopy pos, node and pointed_thing because callback can modify them
		local place_to_copy = vector.copy(pt.above)
		local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
		local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
		local pointed_thing_copy = copy_pointed_thing(pointed_thing)
		if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
			take_item = false
		end
	end

	if not minetest.is_creative_enabled(placer:get_player_name()) and take_item then
		itemstack:take_item()
	end
	return itemstack
end

local stal_box_1 = {{-0.0625+x_disp, -0.5, -0.0625+z_disp, 0.0625+x_disp, 0.5, 0.0625+z_disp}}
local stal_box_2 = {{-0.125+x_disp, -0.5, -0.125+z_disp, 0.125+x_disp, 0.5, 0.125+z_disp}}
local stal_box_3 = {{-0.25+x_disp, -0.5, -0.25+z_disp, 0.25+x_disp, 0.5, 0.25+z_disp}}
local stal_box_4 = {{-0.375+x_disp, -0.5, -0.375+z_disp, 0.375+x_disp, 0.5, 0.375+z_disp}}

local torchspine_list = {"df_trees:torchspine_1","df_trees:torchspine_2","df_trees:torchspine_3","df_trees:torchspine_4"}
local grow_torchspine = function(pos)
	local param2 = minetest.get_node(pos).param2

	if param2 > 3 then
		return -- tipped over, don't grow
	end

	local node_above = minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z})
	local node_above_def = minetest.registered_nodes[node_above.name]
	if not node_above_def.buildable_to then
		-- don't grow, but do continue cycling the torch state
		minetest.swap_node(pos, {name = "df_trees:torchspine_1", param2 = param2})
		minetest.get_node_timer(pos):start(math.random(torchspine_min_delay, torchspine_max_delay))
		return
	end

	local pos_base = vector.new(pos)
	local height = 1
	for i = 1,3 do
		pos_base.y = pos_base.y-1
		if minetest.get_item_group(minetest.get_node(pos_base).name, "df_trees_torchspine") > 0 then
			height = height + 1
		else
			break
		end
	end
	if height >= 4 then
		-- don't grow, but do continue cycling the torch state
		minetest.swap_node(pos, {name = "df_trees:torchspine_1", param2 = param2})
		minetest.get_node_timer(pos):start(math.random(torchspine_min_delay, torchspine_max_delay))
		return
	end

	-- place a taller torchspine
	pos.y = pos.y + 1
	minetest.get_node_timer(pos):start(math.random(torchspine_min_delay, torchspine_max_delay))
	for i = 1, height+1 do
		minetest.swap_node(pos, {name=torchspine_list[i], param2 = param2})
		pos.y = pos.y - 1
	end
end

local torch_node = df_dependencies.node_name_torch

minetest.register_node("df_trees:torchspine_1", {
	description = S("Torchspine Tip"),
	_doc_items_longdesc = df_trees.doc.torchspine_desc,
	_doc_items_usagehelp = df_trees.doc.torchspine_usage,
	tiles = {"dfcaverns_torchspine_0.5.png", "dfcaverns_torchspine_1.5.png", "dfcaverns_torchspine_1.png"},
	groups = {oddly_breakable_by_hand = 1, subterrane_stal_align = 1, flow_through = 1, fall_damage_add_percent = 100, df_trees_torchspine = 1, pickaxey=1, building_block=1, material_stone=1},
	drawtype = "nodebox",
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	drop = torch_node,
	node_box = {
		type = "fixed",
		fixed = stal_box_1,
	},
	_mcl_blast_resistance = 6,
	_mcl_hardness = 2,

	on_place = stal_on_place,
	on_punch = function(pos, node, puncher)
		if puncher:get_wielded_item():get_name() == torch_node then
			minetest.swap_node(pos, {name = "df_trees:torchspine_1_lit", param2 = node.param2})
		end
	end,

	on_timer = function(pos)
		local above_def = minetest.registered_nodes[minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name]
		if above_def and above_def.buildable_to then
			minetest.swap_node(pos, {name="df_trees:torchspine_1_lit", param2=minetest.get_node(pos).param2})
			minetest.sound_play({pos = pos}, {name="dfcaverns_torchspine_ignite"}, true)
		end
		minetest.get_node_timer(pos):start(math.random(torchspine_min_delay, torchspine_max_delay))
	end,

	on_destruct = function(pos)
		minetest.get_node_timer(pos):stop()
	end,
})

minetest.register_node("df_trees:torchspine_1_lit", {
	description = S("Torchspine Tip"),
	_doc_items_longdesc = df_trees.doc.torchspine_desc,
	_doc_items_usagehelp = df_trees.doc.torchspine_usage,
	tiles = {df_dependencies.texture_gold_block, "dfcaverns_torchspine_1.5.png", "dfcaverns_torchspine_1_lit.png"},
	groups = {oddly_breakable_by_hand = 1, subterrane_stal_align = 1, flow_through = 1, torch = 1, fall_damage_add_percent = 150, smokey = 4, df_trees_torchspine = 1,pickaxey=1, building_block=1, material_stone=1,set_on_fire=3},
	drawtype = "nodebox",
	paramtype = "light",
	paramtype2 = "facedir",
	light_source = 8,
	is_ground_content = false,
	drop = torch_node .. " 2",
	node_box = {
		type = "fixed",
		fixed = stal_box_1,
	},
	_mcl_blast_resistance = 6,
	_mcl_hardness = 2,

	on_place = stal_on_place,

	on_timer = function(pos)
		grow_torchspine(pos)
	end,

	on_destruct = function(pos)
		minetest.get_node_timer(pos):stop()
	end,
})

if looped_node_sound_modpath then
	looped_node_sound.register({
		node_list = {"df_trees:torchspine_1_lit"},
		sound = "dfcaverns_torchspine_loop",
		max_gain = 0.5,
		gain_per_node = 0.05,
	})
end

minetest.register_node("df_trees:torchspine_2", {
	description = S("Torchspine"),
	_doc_items_longdesc = df_trees.doc.torchspine_desc,
	_doc_items_usagehelp = df_trees.doc.torchspine_usage,
	tiles = {"dfcaverns_torchspine_1.5.png", "dfcaverns_torchspine_2.5.png", "dfcaverns_torchspine_2.png"},
	groups = {oddly_breakable_by_hand = 1, subterrane_stal_align = 1, flow_through = 1, fall_damage_add_percent = 50, df_trees_torchspine = 1,pickaxey=1, building_block=1, material_stone=1},
	drawtype = "nodebox",
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	node_box = {
		type = "fixed",
		fixed = stal_box_2,
	},
	_mcl_blast_resistance = 6,
	_mcl_hardness = 2,

	drop = {
            max_items = 1,
            items = {
                {
                    items = {"df_trees:torchspine_ember"},  -- Items to drop
                    rarity = 2,  -- Probability of dropping is 1 / rarity
                },
            },
        },
	on_place = stal_on_place,
})

minetest.register_node("df_trees:torchspine_3", {
	description = S("Torchspine"),
	_doc_items_longdesc = df_trees.doc.torchspine_desc,
	_doc_items_usagehelp = df_trees.doc.torchspine_usage,
	tiles = {"dfcaverns_torchspine_2.5.png", "dfcaverns_torchspine_3.5.png", "dfcaverns_torchspine_3.png"},
	groups = {oddly_breakable_by_hand = 1, subterrane_stal_align = 1, flow_through = 1, df_trees_torchspine = 1,pickaxey=1, building_block=1, material_stone=1},
	drawtype = "nodebox",
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	node_box = {
		type = "fixed",
		fixed = stal_box_3,
	},
	_mcl_blast_resistance = 6,
	_mcl_hardness = 2,

	drop = {
            max_items = 1,
            items = {
                {
                    items = {"df_trees:torchspine_ember"},  -- Items to drop
                    rarity = 2,  -- Probability of dropping is 1 / rarity
                },
                {
                    items = {"df_trees:torchspine_ember 2"},  -- Items to drop
                    rarity = 2,  -- Probability of dropping is 1 / rarity
                },
            },
        },
	on_place = stal_on_place,
})

minetest.register_node("df_trees:torchspine_4", {
	description = S("Torchspine"),
	_doc_items_longdesc = df_trees.doc.torchspine_desc,
	_doc_items_usagehelp = df_trees.doc.torchspine_usage,
	tiles = {"dfcaverns_torchspine_3.5.png", "dfcaverns_torchspine_4.5.png", "dfcaverns_torchspine_4.png"},
	groups = {oddly_breakable_by_hand = 1, subterrane_stal_align = 1, flow_through = 1, df_trees_torchspine = 1,pickaxey=1, building_block=1, material_stone=1},
	drawtype = "nodebox",
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	node_box = {
		type = "fixed",
		fixed = stal_box_4,
	},
	_mcl_blast_resistance = 6,
	_mcl_hardness = 2,

	drop = {
            max_items = 1,
            items = {
                {
                    items = {"df_trees:torchspine_ember 2"},  -- Items to drop
                    rarity = 2,  -- Probability of dropping is 1 / rarity
                },
                {
                    items = {"df_trees:torchspine_ember"},  -- Items to drop
                    rarity = 1,  -- Probability of dropping is 1 / rarity
                },
            },
        },	on_place = stal_on_place,
})

minetest.register_node("df_trees:torchspine_ember", {
	description = S("Torchspine Ember"),
	_doc_items_longdesc = df_trees.doc.torchspine_desc,
	_doc_items_usagehelp = df_trees.doc.torchspine_usage,
	tiles = {"dfcaverns_torchspine_0.5.png",},
	groups = {oddly_breakable_by_hand = 1, subterrane_stal_align = 1, flow_through = 1,pickaxey=1, building_block=1, material_stone=1,set_on_fire=1},
	drawtype = "nodebox",
	paramtype = "light",
	light_source = 2,
	paramtype2 = "facedir",
	walkable = false,
	is_ground_content = false,
	floodable = true,
	node_box = {
		type = "fixed",
		fixed = {
			{-0.0625 + x_disp, -0.5, -0.125 + z_disp, 0.125 + x_disp, -0.375, 0.0625 + z_disp},
		}
	},
	_mcl_blast_resistance = 10,
	_mcl_hardness = 1,

	on_place = stal_on_place,

	on_construct = function(pos)
		if df_trees.torchspine_growth_permitted(pos) then
			minetest.get_node_timer(pos):start(math.random(torchspine_min_delay, torchspine_max_delay))
		end
	end,
	on_destruct = function(pos)
		minetest.get_node_timer(pos):stop()
	end,

	on_timer = function(pos)
		minetest.swap_node(pos, {name="df_trees:torchspine_1", param2=minetest.get_node(pos).param2})
		minetest.get_node_timer(pos):start(math.random(torchspine_min_delay, torchspine_max_delay))
		minetest.sound_play({pos = pos}, {name="dfcaverns_torchspine_ignite"}, true)
	end,
})

df_trees.spawn_torchspine = function(pos)
	local x, y, z = pos.x, pos.y, pos.z
	local stem_height = math.random(1,4)

	local vm = minetest.get_voxel_manip()
	local minp, maxp = vm:read_from_map(
		{x = x, y = y, z = z},
		{x = x, y = y+height-1, z = z}
	)
	local area = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
	local data = vm:get_data()
	local data_param2 = vm:get_param2_data()

	df_trees.spawn_torchspine_vm(vi, area, data, data_param2, height)

	vm:set_data(data)
	vm:write_to_map()
	vm:update_map()
end

local torchspine_c =
{
	minetest.get_content_id("df_trees:torchspine_1"),
	minetest.get_content_id("df_trees:torchspine_2"),
	minetest.get_content_id("df_trees:torchspine_3"),
	minetest.get_content_id("df_trees:torchspine_4")
}
local torchspine_lit_c =
{
	minetest.get_content_id("df_trees:torchspine_1_lit"),
	minetest.get_content_id("df_trees:torchspine_2"),
	minetest.get_content_id("df_trees:torchspine_3"),
	minetest.get_content_id("df_trees:torchspine_4")
}

df_trees.spawn_torchspine_vm = function(vi, area, data, data_param2, height, lit)
	if height == nil then height = math.random(1,4) end
	if lit == nil then lit = math.random() < 0.3 end
	local param2 = math.random(0,3)
	local list
	if lit then list = torchspine_lit_c else list = torchspine_c end

	for i = 0, height-1 do
		if not minetest.registered_nodes[minetest.get_name_from_content_id(data[vi + area.ystride*i])].buildable_to then
			height = i
			break
		end
	end

	for i = 0, height-1 do
		local index = vi + area.ystride*i
		data[index] = list[height-i]
		data_param2[index] = param2
	end

	local pos = area:position(vi)
	pos.y = pos.y+height-1
	local node = minetest.get_node(pos)
	minetest.get_node_timer(pos):start(math.random()*3000)
end

minetest.register_lbm({
    label = "Start timers for torchspine nodes that used to depend on the ABM",
    name = "df_trees:start_torchspine_timers",
    nodenames = {"df_trees:torchspine_ember", "df_trees:torchspine_1", "df_trees:torchspine_1_lit"},
    run_at_every_load = false,
    action = function(pos, node)
		minetest.get_node_timer(pos):start(math.random(torchspine_min_delay, torchspine_max_delay))
	end,
})


code:
local S = minetest.get_translator(minetest.get_current_modname())

--stem
minetest.register_node("df_trees:tower_cap_stem", {
	description = S("Tower Cap Stem"),
	_doc_items_longdesc = df_trees.doc.tower_cap_desc,
	_doc_items_usagehelp = df_trees.doc.tower_cap_usage,
	tiles = {"dfcaverns_tower_cap.png"},
	is_ground_content = false,
	groups = {tree = 1, choppy = 2, oddly_breakable_by_hand = 1, flammable = 2, tower_cap = 1, tower_cap_trunk = 1, handy=1,axey=1, building_block=1, material_wood=1, fire_encouragement=5, fire_flammability=5},
	sounds = df_trees.node_sound_tree_soft_fungus_defaults(),
	_mcl_blast_resistance = 2,
	_mcl_hardness = 2,
})

--cap
minetest.register_node("df_trees:tower_cap", {
	description = S("Tower Cap"),
	_doc_items_longdesc = df_trees.doc.tower_cap_desc,
	_doc_items_usagehelp = df_trees.doc.tower_cap_usage,
	tiles = {"dfcaverns_tower_cap.png"},
	is_ground_content = false,
	groups = {tree = 1, choppy = 2, oddly_breakable_by_hand = 1, flammable = 2, tower_cap = 1, tower_cap_trunk = 1, handy=1,axey=1, building_block=1, material_wood=1, fire_encouragement=5, fire_flammability=5},
	sounds = df_trees.node_sound_tree_soft_fungus_defaults(),
	_mcl_blast_resistance = 2,
	_mcl_hardness = 2,
})

--gills
minetest.register_node("df_trees:tower_cap_gills", {
	description = S("Tower Cap Gills"),
	_doc_items_longdesc = df_trees.doc.tower_cap_desc,
	_doc_items_usagehelp = df_trees.doc.tower_cap_usage,
	tiles = {"dfcaverns_tower_cap_gills.png"},
	is_ground_content = false,
	groups = {snappy = 3, leafdecay = 3, flammable = 2, leaves = 1, tower_cap = 1,handy=1, hoey=1, shearsy=1, swordy=1, deco_block=1, dig_by_piston=1, fire_encouragement=15, fire_flammability=30, compostability=30},
	sounds = df_dependencies.sound_leaves(),
	drawtype = "plantlike",
	paramtype = "light",
	drop = {
		max_items = 1,
		items = {
			{
				items = {'df_trees:tower_cap_sapling'},
				rarity = 5,
			},
			{
				items = {'df_trees:tower_cap_gills'},
			}
		}
	},
	_mcl_blast_resistance = 0.2,
	_mcl_hardness = 0.2,
	after_place_node = df_dependencies.after_place_leaves,
})

df_dependencies.register_leafdecay({
	trunks = {"df_trees:tower_cap"}, -- don't need stem nodes here
	leaves = {"df_trees:tower_cap_gills"},
	radius = 1,
})

--Wood
minetest.register_craft({
	output = 'df_trees:tower_cap_wood 4',
	recipe = {
		{'group:tower_cap_trunk'},
	}
})

minetest.register_node("df_trees:tower_cap_wood", {
	description = S("Tower Cap Planks"),
	_doc_items_longdesc = df_trees.doc.tower_cap_desc,
	_doc_items_usagehelp = df_trees.doc.tower_cap_usage,
	paramtype2 = "facedir",
	place_param2 = 0,
	tiles = {"dfcaverns_tower_cap_wood.png"},
	is_ground_content = false,
	groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2, wood = 1, handy=1,axey=1, building_block=1, material_wood=1, fire_encouragement=5, fire_flammability=20},
	sounds = df_dependencies.sound_wood(),
	_mcl_blast_resistance = 3,
	_mcl_hardness = 2,
})

df_dependencies.register_all_stairs_and_fences("tower_cap_wood", {burntime = 7})

minetest.register_craft({
	type = "fuel",
	recipe = "df_trees:tower_cap_wood",
	burntime = 7,
})
minetest.register_craft({
	type = "fuel",
	recipe = "df_trees:tower_cap",
	burntime = 30,
})
minetest.register_craft({
	type = "fuel",
	recipe = "df_trees:tower_cap_stem",
	burntime = 30,
})
minetest.register_craft({
	type = "fuel",
	recipe = "df_trees:tower_cap_gills",
	burntime = 2,
})
minetest.register_craft({
	type = "fuel",
	recipe = "df_trees:tower_cap_sapling",
	burntime = 2,
})

-- sapling
minetest.register_node("df_trees:tower_cap_sapling", {
	description = S("Tower Cap Spawn"),
	_doc_items_longdesc = df_trees.doc.tower_cap_desc,
	_doc_items_usagehelp = df_trees.doc.tower_cap_usage,
	drawtype = "plantlike",
	visual_scale = 1.0,
	tiles = {"dfcaverns_tower_cap_sapling.png"},
	inventory_image = "dfcaverns_tower_cap_sapling.png",
	wield_image = "dfcaverns_tower_cap_sapling.png",
	paramtype = "light",
	sunlight_propagates = true,
	is_ground_content = false,
	walkable = false,
	floodable = true,
	selection_box = {
		type = "fixed",
		fixed = {-4 / 16, -0.5, -4 / 16, 4 / 16, 7 / 16, 4 / 16}
	},
	groups = {snappy = 2, dig_immediate = 3, flammable = 2,
		attached_node = 1, sapling = 1, light_sensitive_fungus = 11, dig_immediate=3,dig_by_piston=1,destroy_by_lava_flow=1,deco_block=1, compostability=30},
	sounds = df_dependencies.sound_leaves(),
	_mcl_blast_resistance = 0.2,
	_mcl_hardness = 0.2,

	on_construct = function(pos)
		if df_trees.tower_cap_growth_permitted(pos) then
			minetest.get_node_timer(pos):start(math.random(
				df_trees.config.tower_cap_delay_multiplier*df_trees.config.tree_min_growth_delay,
				df_trees.config.tower_cap_delay_multiplier*df_trees.config.tree_max_growth_delay))
		end
	end,
	on_destruct = function(pos)
		minetest.get_node_timer(pos):stop()
	end,

	on_timer = function(pos)
		if df_farming and df_farming.kill_if_sunlit(pos) then
			return
		end
		minetest.set_node(pos, {name="air"})
		df_trees.spawn_tower_cap(pos)
	end,
})

local c_stem = minetest.get_content_id("df_trees:tower_cap_stem")
local c_cap  = minetest.get_content_id("df_trees:tower_cap")
local c_gills  = minetest.get_content_id("df_trees:tower_cap_gills")

df_trees.spawn_tower_cap = function(pos)
	local x, y, z = pos.x, pos.y, pos.z
	local stem_height = math.random(4,10)
	local cap_radius = math.random(4,6)
	local maxy = y + stem_height + 3

	local vm = minetest.get_voxel_manip()
	local minp, maxp = vm:read_from_map(
		{x = x - cap_radius, y = y, z = z - cap_radius},
		{x = x + cap_radius, y = maxy + 3, z = z + cap_radius}
	)
	local area = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
	local data = vm:get_data()

	subterrane.giant_mushroom(area:indexp(pos), area, data, c_stem, c_cap, c_gills, stem_height, cap_radius)

	vm:set_data(data)
	vm:write_to_map()
	vm:update_map()
end

df_trees.spawn_tower_cap_vm = function(vi, area, data)
	local stem_height = math.random(4,10)
	local cap_radius = math.random(4,6)
	subterrane.giant_mushroom(vi, area, data, c_stem, c_cap, c_gills, stem_height, cap_radius)
end

repo_name: korkut/MeseCraft

license: mit


code:
--------------------------------------------------
-- Tunnel tube

-- Magenta
-- curving trunk
-- Max trunk height 	8
-- depth 2-3

local S = minetest.get_translator(minetest.get_current_modname())

minetest.register_node("df_trees:tunnel_tube", {
	description = S("Tunnel Tube"),
	_doc_items_longdesc = df_trees.doc.tunnel_tube_desc,
	_doc_items_usagehelp = df_trees.doc.tunnel_tube_usage,
	tiles = {"dfcaverns_tunnel_tube.png"},
	paramtype2 = "facedir",
	drawtype = "nodebox",
	is_ground_content = false,
	paramtype = "light",
	groups = {choppy = 3, tree = 1, oddly_breakable_by_hand=1, flammable = 2, handy=1,axey=1, building_block=1, material_wood=1, fire_encouragement=5, fire_flammability=5},
	sounds = df_dependencies.sound_wood(),
	on_place = minetest.rotate_node,
	_mcl_blast_resistance = 2,
	_mcl_hardness = 2,

	node_box = {
		type = "fixed",
		fixed = {
			{-8/16,-8/16,-8/16,-4/16,8/16,8/16},
			{4/16,-8/16,-8/16,8/16,8/16,8/16},
			{-4/16,-8/16,-8/16,4/16,8/16,-4/16},
			{-4/16,-8/16,8/16,4/16,8/16,4/16},
		},
	},
})

minetest.register_node("df_trees:tunnel_tube_slant_bottom", {
	description = S("Tunnel Tube"),
	_doc_items_longdesc = df_trees.doc.tunnel_tube_desc,
	_doc_items_usagehelp = df_trees.doc.tunnel_tube_usage,
	tiles = {"dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png"},
	paramtype2 = "facedir",
	drawtype = "mesh",
	is_ground_content = false,
	mesh = "tunnel_tube_slant.obj",
	paramtype = "light",
	drop = "df_trees:tunnel_tube",
	groups = {choppy = 3, tree = 1, oddly_breakable_by_hand=1, flammable = 2, handy=1,axey=1, building_block=1, material_wood=1, fire_encouragement=5, fire_flammability=5},
	sounds = df_dependencies.sound_wood(),
	on_place = minetest.rotate_node,
	selection_box = {
		type = "fixed",
		fixed = {
			{-0.5, -0.5, -0.625, 0.5, 0.0, 0.375},
			{-0.5, 0.0, -0.875, 0.5, 0.5, 0.125},
		},
	},
	collision_box = {
		type = "fixed",
		fixed = {
			{-0.5, -0.5, -0.625, 0.5, 0.0, 0.375},
			{-0.5, 0.0, -0.875, 0.5, 0.5, 0.125},
		},
	},
	_mcl_blast_resistance = 2,
	_mcl_hardness = 2,
})

minetest.register_node("df_trees:tunnel_tube_slant_top", {
	description = S("Tunnel Tube"),
	_doc_items_longdesc = df_trees.doc.tunnel_tube_desc,
	_doc_items_usagehelp = df_trees.doc.tunnel_tube_usage,
	tiles = {"dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png"},
	paramtype2 = "facedir",
	drawtype = "mesh",
	is_ground_content = false,
	mesh = "tunnel_tube_slant_2.obj",
	paramtype = "light",
	drop = "df_trees:tunnel_tube",
	groups = {choppy = 3, tree = 1, oddly_breakable_by_hand=1, flammable = 2, handy=1,axey=1, building_block=1, material_wood=1, fire_encouragement=5, fire_flammability=5},
	sounds = df_dependencies.sound_wood(),
	on_place = minetest.rotate_node,
	selection_box = {
		type = "fixed",
		fixed = {
			{-0.5, -0.5, -0.125, 0.5, 0.0, 0.875},
			{-0.5, 0.0, -0.375, 0.5, 0.5, 0.625},
		},
	},
	collision_box = {
		type = "fixed",
		fixed = {
			{-0.5, -0.5, -0.125, 0.5, 0.0, 0.875},
			{-0.5, 0.0, -0.375, 0.5, 0.5, 0.625},
		},
	},
	_mcl_blast_resistance = 2,
	_mcl_hardness = 2,
})

minetest.register_node("df_trees:tunnel_tube_slant_full", {
	description = S("Tunnel Tube"),
	_doc_items_longdesc = df_trees.doc.tunnel_tube_desc,
	_doc_items_usagehelp = df_trees.doc.tunnel_tube_usage,
	tiles = {"dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png", "dfcaverns_tunnel_tube.png"},
	paramtype2 = "facedir",
	drawtype = "mesh",
	is_ground_content = false,
	mesh = "tunnel_tube_slant_full.obj",
	paramtype = "light",
	drop = "df_trees:tunnel_tube",
	groups = {choppy = 3, tree = 1, oddly_breakable_by_hand=1, flammable = 2, handy=1,axey=1, building_block=1, material_wood=1, fire_encouragement=5, fire_flammability=5},
	sounds = df_dependencies.sound_wood(),
	on_place = minetest.rotate_node,
	selection_box = {
		type = "fixed",
		fixed = {
			{-0.5, -0.5, -0.75, 0.5, 0, 0.25},
			{-0.5, 0, -1.25, 0.5, 0.5, -0.25},
		},
	},
	collision_box = {
		type = "fixed",
		fixed = {
			{-0.5, -0.5, -0.75, 0.5, 0, 0.25},
			{-0.5, 0, -1.25, 0.5, 0.5, -0.25},
		},
	},
	_mcl_blast_resistance = 2,
	_mcl_hardness = 2,
})

--Wood
minetest.register_craft({
	output = 'df_trees:tunnel_tube_wood 4',
	recipe = {
		{'df_trees:tunnel_tube'},
	}
})

-- Paper
minetest.register_craft({
	output = df_dependencies.node_name_paper .. " 3",
	type = "shapeless",
	recipe = {'df_trees:tunnel_tube', 'mesecraft_bucket:bucket_water'},
	replacements = {{"mesecraft_bucket:bucket_water", "mesecraft_bucket:bucket_empty"}},
})

minetest.register_node("df_trees:tunnel_tube_wood", {
	description = S("Tunnel Tube Plies"),
	_doc_items_longdesc = df_trees.doc.tunnel_tube_desc,
	_doc_items_usagehelp = df_trees.doc.tunnel_tube_usage,
	paramtype2 = "facedir",
	place_param2 = 0,
	tiles = {"dfcaverns_tunnel_tube_wood_top.png", "dfcaverns_tunnel_tube_wood_top.png", "dfcaverns_tunnel_tube_wood_side.png"},
	is_ground_content = false,
	groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2, wood = 1, handy=1,axey=1, building_block=1, material_wood=1, fire_encouragement=5, fire_flammability=20},
	sounds = df_dependencies.sound_wood(),
	_mcl_blast_resistance = 10,
	_mcl_hardness = 2,
})

df_dependencies.register_all_stairs_and_fences("tunnel_tube_wood", {burntime = 9})

minetest.register_craft({
	type = "fuel",
	recipe = "df_trees:tunnel_tube_wood",
	burntime = 9,
})
minetest.register_craft({
	type = "fuel",
	recipe = "df_trees:tunnel_tube",
	burntime = 36,
})
minetest.register_craft({
	type = "fuel",
	recipe = "df_trees:tunnel_tube_sapling",
	burntime = 3,
})

-- TNT
-----------------------------------------------------------------------------------------------------------

local gunpowder = df_dependencies.node_name_gunpowder
local tnt_ignite_sound = df_dependencies.soundfile_tnt_ignite
local tnt_boom = df_dependencies.tnt_boom

if df_trees.config.enable_tnt then

	local tnt_radius = tonumber(minetest.settings:get("tnt_radius") or 3) * 2/3
	local tnt_def = {radius = tnt_radius, damage_radius = tnt_radius * 2}
	local torch_item = df_dependencies.node_name_torch

	local drop_items = {
			max_items = 1,
			items = {
				{
					items = {'df_trees:tunnel_tube_sapling'},
					rarity = 2,
				},
			}
		}
	if gunpowder then
		drop_items.max_items = 3
		table.insert(drop_items.items, {
					items = {'df_trees:tunnel_tube_sapling', gunpowder},
					rarity = 2,
				})
		table.insert(drop_items.items, {
					items = {'df_trees:tunnel_tube_sapling', gunpowder .. ' 2'},
					rarity = 2,
				})
	end

	local on_burn = function(pos)
		minetest.swap_node(pos, {name = "df_trees:tunnel_tube_fruiting_body_burning"})
		minetest.registered_nodes["df_trees:tunnel_tube_fruiting_body_burning"].on_construct(pos)
	end

	minetest.register_node("df_trees:tunnel_tube_fruiting_body", {
		description = S("Tunnel Tube Fruiting Body"),
		_doc_items_longdesc = df_trees.doc.tunnel_tube_desc,
		_doc_items_usagehelp = df_trees.doc.tunnel_tube_usage,
		tiles = {"dfcaverns_tunnel_tube.png^[multiply:#b09090"},
		paramtype2 = "facedir",
		is_ground_content = false,
		groups = {choppy = 3, oddly_breakable_by_hand=1, flammable = 2, tnt = 1, handy=1, hoey=1, shearsy=1, swordy=1, deco_block=1, dig_by_piston=1, fire_encouragement=15, fire_flammability=30},
		sounds = df_dependencies.sound_wood(),
		on_place = minetest.rotate_node,
		drop = drop_items,
		_mcl_blast_resistance = 12,
		_mcl_hardness = 2,

		on_punch = function(pos, node, puncher)
			if puncher:get_wielded_item():get_name() == torch_item then
				minetest.swap_node(pos, {name = "df_trees:tunnel_tube_fruiting_body_burning"})
				minetest.registered_nodes["df_trees:tunnel_tube_fruiting_body_burning"].on_construct(pos)
				minetest.log("action", puncher:get_player_name() .. " ignites " .. node.name .. " at " .. minetest.pos_to_string(pos))
			end
		end,
		on_blast = function(pos, intensity)
			minetest.swap_node(pos, {name="air"})
			minetest.after(0.1, function()
				tnt_boom(pos, tnt_def)
			end)
		end,
		mesecons = {effector =
			{action_on =
				function(pos)
					minetest.swap_node(pos, {name="air"})
					tnt_boom(pos, tnt_def)
				end
			}
		},
		on_burn = on_burn,
		on_ignite = on_burn,
		_on_burn = on_burn,
	})

	minetest.register_node("df_trees:tunnel_tube_fruiting_body_burning", {
		description = S("Tunnel Tube Fruiting Body"),
		_doc_items_longdesc = df_trees.doc.tunnel_tube_desc,
		_doc_items_usagehelp = df_trees.doc.tunnel_tube_usage,
		tiles = {"dfcaverns_tunnel_tube.png^[multiply:#b09090"},
		is_ground_content = false,
		groups = {not_in_creative_inventory = 1,},
		light_source = 5,
		drop = "",
		sounds = df_dependencies.sound_wood(),
		_mcl_blast_resistance = 0,
		_mcl_hardness = 2,
		on_timer = function(pos, elapsed)
			minetest.swap_node(pos, {name="air"})
			tnt_boom(pos, tnt_def)
		end,
		on_blast = function(pos)
			minetest.swap_node(pos, {name="air"})
			minetest.after(0.1, function()
				tnt_boom(pos, tnt_def)
			end)
		end,
		on_construct = function(pos)
			minetest.sound_play(tnt_ignite_sound, {pos = pos})
			minetest.get_node_timer(pos):start(4)
		end,
	})
else
	minetest.register_node("df_trees:tunnel_tube_fruiting_body", {
		description = S("Tunnel Tube Fruiting Body"),
		_doc_items_longdesc = df_trees.doc.tunnel_tube_desc,
		_doc_items_usagehelp = df_trees.doc.tunnel_tube_usage,
		tiles = {"dfcaverns_tunnel_tube.png^[multiply:#b09090"},
		paramtype2 = "facedir",
		is_ground_content = false,
		groups = {choppy = 3, oddly_breakable_by_hand=1, flammable = 2,handy=1, hoey=1, shearsy=1, swordy=1, deco_block=1, dig_by_piston=1, fire_encouragement=15, fire_flammability=30},
		sounds = df_dependencies.sound_wood(),
		on_place = minetest.rotate_node,
		_mcl_blast_resistance = 12,
		_mcl_hardness = 2,

		drop = {
			max_items = 3,
			items = {
				{
					items = {'df_trees:tunnel_tube_sapling'},
					rarity = 2,
				},
				{
					items = {'df_trees:tunnel_tube_sapling'},
					rarity = 2,
				},
				{
					items = {'df_trees:tunnel_tube_sapling'},
					rarity = 2,
				},
			},
		},
	})
end
-----------------------------------------------------------------------------------------------------------

minetest.register_node("df_trees:tunnel_tube_sapling", {
	description = S("Tunnel Tube Spawn"),
	_doc_items_longdesc = df_trees.doc.tunnel_tube_desc,
	_doc_items_usagehelp = df_trees.doc.tunnel_tube_usage,
	drawtype = "plantlike",
	visual_scale = 1.0,
	tiles = {"dfcaverns_tunnel_tube_sapling.png"},
	inventory_image = "dfcaverns_tunnel_tube_sapling.png",
	wield_image = "dfcaverns_tunnel_tube_sapling.png",
	paramtype = "light",
	sunlight_propagates = true,
	walkable = false,
	is_ground_content = false,
	floodable = true,
	selection_box = {
		type = "fixed",
		fixed = {-4 / 16, -0.5, -4 / 16, 4 / 16, 7 / 16, 4 / 16}
	},
	groups = {snappy = 2, dig_immediate = 3, flammable = 2,
		attached_node = 1, sapling = 1, light_sensitive_fungus = 11, dig_immediate=3,dig_by_piston=1,destroy_by_lava_flow=1,deco_block=1, compostability=30},
	sounds = df_dependencies.sound_leaves(),
	_mcl_blast_resistance = 10,
	_mcl_hardness = 0.2,

	on_construct = function(pos)
		if df_trees.tunnel_tube_growth_permitted(pos) then
			minetest.get_node_timer(pos):start(math.random(
				df_trees.config.tunnel_tube_delay_multiplier*df_trees.config.tree_min_growth_delay,
				df_trees.config.tunnel_tube_delay_multiplier*df_trees.config.tree_max_growth_delay))
		end
	end,

	on_destruct = function(pos)
		minetest.get_node_timer(pos):stop()
	end,

	on_timer = function(pos)
		if df_farming and df_farming.kill_if_sunlit(pos) then
			return
		end
		minetest.set_node(pos, {name="air"})
		df_trees.spawn_tunnel_tube(pos)
	end,
})

local tunnel_tube_directions = {
	[0] = {x=0,y=0,z=-1},
	[1] = {x=-1,y=0,z=0},
	[2] = {x=0,y=0,z=1},
	[3] = {x=1,y=0,z=0},
}

local tunnel_tube_displacement =
{
	[4] = 1,
	[5] = 1,
	[6] = 2,
	[7] = 2,
	[8] = 3,
	[9] = 3,
}

df_trees.spawn_tunnel_tube = function(pos)
	local direction = math.random(1,4)-1 -- serves as both the facedir and the lookup in the direction table
	local height = math.random(4,9)
	local top_pos = vector.add(pos, vector.multiply(tunnel_tube_directions[direction], tunnel_tube_displacement[height]))
	top_pos.y = pos.y + height - 1

	local vm = minetest.get_voxel_manip()
	local minp, maxp = vm:read_from_map(pos, top_pos)
	local area = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
	local data = vm:get_data()
	local param2_data = vm:get_param2_data()

	df_trees.spawn_tunnel_tube_vm(area:indexp(pos), area, data, param2_data, height, direction)

	vm:set_data(data)
	vm:set_param2_data(param2_data)
	vm:write_to_map()
	vm:update_map()
end

local c_air = minetest.get_content_id("air")
local c_ignore = minetest.get_content_id("ignore")
local c_tunnel_tube = minetest.get_content_id("df_trees:tunnel_tube")
local c_tunnel_tube_bottom = minetest.get_content_id("df_trees:tunnel_tube_slant_bottom")
local c_tunnel_tube_top = minetest.get_content_id("df_trees:tunnel_tube_slant_top")
local c_tunnel_tube_full = minetest.get_content_id("df_trees:tunnel_tube_slant_full")
local c_tunnel_tube_fruiting_body  = minetest.get_content_id("df_trees:tunnel_tube_fruiting_body")

-- was simplest to just hardcode these patterns for each height from 4 to 9
-- pattern is displacement, node
local tunnel_tube_patterns =
{
	[4] = {{0, c_tunnel_tube}, {0, c_tunnel_tube_bottom}, {1, c_tunnel_tube_top}, {1, c_tunnel_tube_fruiting_body}},
	[5] = {{0, c_tunnel_tube}, {0, c_tunnel_tube}, {0, c_tunnel_tube_bottom}, {1, c_tunnel_tube_top}, {1, c_tunnel_tube_fruiting_body}},
	[6] = {{0, c_tunnel_tube}, {0, c_tunnel_tube}, {0, c_tunnel_tube_bottom}, {1, c_tunnel_tube_top}, {1, c_tunnel_tube_full}, {2, c_tunnel_tube_fruiting_body}},
	[7] = {{0, c_tunnel_tube}, {0, c_tunnel_tube}, {0, c_tunnel_tube}, {0, c_tunnel_tube_bottom}, {1, c_tunnel_tube_top}, {1, c_tunnel_tube_full}, {2, c_tunnel_tube_fruiting_body}},
	[8] = {{0, c_tunnel_tube}, {0, c_tunnel_tube}, {0, c_tunnel_tube}, {0, c_tunnel_tube_bottom}, {1, c_tunnel_tube_top}, {1, c_tunnel_tube_full}, {2, c_tunnel_tube_full}, {3, c_tunnel_tube_fruiting_body}},
	[9] = {{0, c_tunnel_tube}, {0, c_tunnel_tube}, {0, c_tunnel_tube}, {0, c_tunnel_tube}, {0, c_tunnel_tube_bottom}, {1, c_tunnel_tube_top}, {1, c_tunnel_tube_full}, {2, c_tunnel_tube_full}, {3, c_tunnel_tube_fruiting_body}},
}

df_trees.spawn_tunnel_tube_vm = function(vi, area, data, param2_data, height, direction)
	if height == nil then height = math.random(4,9) end
	if direction == nil then direction = math.random(1,4)-1 end

	local increment
	if direction == 0 then
		increment = -area.zstride
	elseif direction == 1 then
		increment = -1
	elseif direction == 2 then
		increment = area.zstride
	elseif direction == 3 then
		increment = 1
	else
		return
	end

	local previous_vi = vi
	local pattern = tunnel_tube_patterns[height]
	if pattern == nil then
		minetest.log("error", "Tunnel tube pattern was nil somehow. height: " .. string(height) .. " location: " .. minetest.pos_to_string(area:position(vi)))
		return nil
	end
	for i, nodepattern in ipairs(pattern) do
		local current_vi = vi + nodepattern[1] * increment
		if data[current_vi] == c_air or data[current_vi] == c_ignore then
			previous_vi = current_vi
			data[current_vi] = nodepattern[2]
			param2_data[current_vi] = direction
		else
			data[previous_vi] = c_tunnel_tube_fruiting_body
			param2_data[previous_vi] = direction
			break
		end
		vi = vi + area.ystride
	end
end


code:
local S = minetest.get_translator(minetest.get_current_modname())

local lantern_nodebox = {
	{-0.5, -0.5, -0.5, -0.3125, 0.5, -0.3125},
	{-0.5, -0.5, 0.3125, -0.3125, 0.5, 0.5},
	{0.3125, -0.5, 0.3125, 0.5, 0.5, 0.5},
	{0.3125, -0.5, -0.5, 0.5, 0.5, -0.3125},
	{-0.3125, 0.3125, -0.5, 0.3125, 0.5, -0.3125},
	{-0.3125, 0.3125, 0.3125, 0.3125, 0.5, 0.5},
	{0.3125, 0.3125, -0.3125, 0.5, 0.5, 0.3125},
	{-0.5, 0.3125, -0.3125, -0.3125, 0.5, 0.3125},
	{-0.5, -0.5, -0.3125, -0.3125, -0.3125, 0.3125},
	{0.3125, -0.5, -0.3125, 0.5, -0.3125, 0.3125},
	{-0.3125, -0.5, 0.3125, 0.3125, -0.3125, 0.5},
	{-0.3125, -0.5, -0.5, 0.3125, -0.3125, -0.3125},
	{-0.375, -0.375, -0.375, 0.375, 0.375, 0.375},
}

local mese_crystal_node = df_dependencies.node_name_mese_crystal
local brick_texture = "dfcaverns_slade_brick.png"
local lantern_texture = df_dependencies.texture_meselamp
local ancient_lantern_sound = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } })

local invulnerable = df_underworld_items.config.invulnerable_slade and not minetest.settings:get_bool("creative_mode")

local can_dig
if invulnerable then
	can_dig = function(pos, player)
		return minetest.check_player_privs(player, "server")
	end
end

local slade_mcl_blast_resistance = 1200
local slade_mcl_hardness = 50
local slade_groups = {stone=1, slade=1, pit_plasma_resistant=1, mese_radiation_shield=1, cracky = 3, creative_breakable=1, building_block=1, material_stone=1}
if invulnerable then
	slade_groups.immortal = 1
	slade_mcl_blast_resistance = 3600000
	slade_mcl_hardness = -1
end

-- override this to allow achievements to be recorded without requiring a dependency
df_underworld_items.ancient_lantern_fixed = function(pos, player) end

local punch_fix = function(pos, node, puncher, pointed_thing)
	local wielded = puncher:get_wielded_item()
	if wielded:get_name() == mese_crystal_node then
		minetest.set_node(pos, {name="df_underworld_items:ancient_lantern_slade"})
		minetest.get_node_timer(pos):stop()
		if not minetest.is_creative_enabled(puncher:get_player_name()) then
			wielded:take_item()
			puncher:set_wielded_item(wielded)
		end
		df_underworld_items.ancient_lantern_fixed(pos, puncher)
		return
	end
	minetest.node_punch(pos, node, puncher, pointed_thing)
end

minetest.register_node("df_underworld_items:ancient_lantern_slade", {
	description = S("Ancient Lantern"),
	paramtype2 = "facedir",
	place_param2 = 0,
	tiles = {brick_texture .. "^(" .. lantern_texture .. "^[mask:dfcaverns_lantern_mask.png)"},
	is_ground_content = false,
	groups = slade_groups,
	sounds = ancient_lantern_sound,
	drawtype= "nodebox",
	light_source = minetest.LIGHT_MAX,
	node_box = {
		type = "fixed",
		fixed = lantern_nodebox,
	},
	can_dig = can_dig,
	_mcl_blast_resistance = slade_mcl_blast_resistance,
	_mcl_hardness = slade_mcl_hardness,
})

minetest.register_node("df_underworld_items:ancient_lantern_slade_worn", {
	description = S("Ancient Lantern"),
	paramtype2 = "facedir",
	place_param2 = 0,
	tiles = {brick_texture .. "^(" .. lantern_texture .. "^[multiply:#FF8888^[mask:dfcaverns_lantern_mask.png)"},
	is_ground_content = false,
	groups = slade_groups,
	sounds = ancient_lantern_sound,
	drawtype= "nodebox",
	light_source = 6,
	node_box = {
		type = "fixed",
		fixed = lantern_nodebox,
	},
	_mcl_blast_resistance = slade_mcl_blast_resistance,
	_mcl_hardness = slade_mcl_hardness,
	on_construct = function(pos)
		minetest.get_node_timer(pos):start(math.random(100, 200))
	end,
	on_timer = function(pos, elapsed)
		minetest.swap_node(pos, {name="df_underworld_items:ancient_lantern_slade_burnt_out"})
		if math.random() < 0.1 then
			minetest.get_node_timer(pos):start(math.random(30, 60))
		else
			minetest.get_node_timer(pos):start(0.25)
		end
	end,
	on_punch = punch_fix,
	can_dig = can_dig,
})

minetest.register_node("df_underworld_items:ancient_lantern_slade_burnt_out", {
	description = S("Ancient Lantern"),
	paramtype2 = "facedir",
	place_param2 = 0,
	tiles = {brick_texture .. "^(" .. lantern_texture .. "^[multiply:#884444^[mask:dfcaverns_lantern_mask.png)"},
	is_ground_content = false,
	groups = slade_groups,
	sounds = ancient_lantern_sound,
	drawtype= "nodebox",
	light_source = 0,
	node_box = {
		type = "fixed",
		fixed = lantern_nodebox,
	},
	drops = "df_underworld_items:ancient_lantern_slade_worn",
	_mcl_blast_resistance = slade_mcl_blast_resistance,
	_mcl_hardness = slade_mcl_hardness,
	on_construct = function(pos)
		minetest.get_node_timer(pos):start(math.random(100, 200))
	end,
	on_timer = function(pos, elapsed)
		minetest.swap_node(pos, {name="df_underworld_items:ancient_lantern_slade_worn"})
		if math.random() < 0.1 then
			minetest.get_node_timer(pos):start(math.random(300, 600))
		else
			minetest.get_node_timer(pos):start(0.25)
		end
	end,
	on_punch = punch_fix,
	can_dig = can_dig,
})

--minetest.register_craft({
--	output = "df_underworld_items:ancient_lantern_slade",
--	type = "shapeless",
--	recipe = {
--		"group:df_underworld_items_ancient_lantern",
--		mese_crystal_node,
--	}
--})


code:
local CONFIG_FILE_PREFIX = "dfcaverns_"

df_underworld_items.config = {}

local print_settingtypes = false

local function setting(stype, name, default, description)
	local value
	if stype == "bool" then
		value = minetest.settings:get_bool(CONFIG_FILE_PREFIX..name, default)
	elseif stype == "string" then
		value = minetest.settings:get(CONFIG_FILE_PREFIX..name)
	elseif stype == "int" or stype == "float" then
		value = tonumber(minetest.setting:get(CONFIG_FILE_PREFIX..name))
	end
	if value == nil then
		value = default
	end
	df_underworld_items.config[name] = value

	if print_settingtypes then
		minetest.debug(CONFIG_FILE_PREFIX..name.." ("..description..") "..stype.." "..tostring(default))
	end
end

setting("bool", "invulnerable_slade", true, "Slade is invulnerable to players")
setting("bool", "destructive_pit_plasma", true, "Pit plasma destroys adjacent nodes")
setting("bool", "underworld_hunter_statues", true, "Enable hunter statues in the underworld")
setting("bool", "enable_slade_drill", true, "Enable slade drills")


code:
local S = minetest.get_translator(minetest.get_current_modname())

minetest.register_node("df_underworld_items:glow_amethyst", {
	description = S("Glowing Amethyst Block"),
	_doc_items_longdesc = df_underworld_items.doc.glow_amethyst_desc,
	_doc_items_usagehelp = df_underworld_items.doc.glow_amethyst_usage,
	tiles = {"dfcaverns_glow_amethyst.png"},
	is_ground_content = false,
	groups = {cracky=3, pit_plasma_resistant=1, pickaxey = 4, building_block = 1,}, -- deliberately not in material_stone group to keep pit plasma ABM efficient
	sounds = df_dependencies.sound_glass(),
	light_source = 6,
	paramtype = "light",
	use_texture_alpha = "blend",
	drawtype = "glasslike",
	sunlight_propagates = true,
	_mcl_blast_resistance = 6,
	_mcl_hardness = 2,
})

if minetest.get_modpath("radiant_damage") and radiant_damage.override_radiant_damage and radiant_damage.config.enable_mese_damage then
	radiant_damage.override_radiant_damage("mese", {emitted_by={["df_underworld_items:glow_amethyst"] = radiant_damage.config.mese_damage*0.25}})
end

local c_amethyst = minetest.get_content_id("df_underworld_items:glow_amethyst")

local safe_write = function(data, area, vi, c_node)
	if area:containsi(vi) then data[vi] = c_node end
end

-- x_slant and z_slant can be 1, 0 or -1
-- rotated is true or false
-- length is 1 or more
df_underworld_items.underworld_shard = function(data, area, vi, x_slant, z_slant, rotated, length)
	if x_slant == nil then x_slant = math.random(-1,1) end
	if z_slant == nil then z_slant = math.random(-1,1) end
	if rotated == nil then rotated = math.random() > 0.5 end
	if length == nil then length = math.random(4, 12) end

	length = math.ceil(length/2) * 2 -- make it an even multiple of 2

	local xstride, ystride, zstride
	ystride = area.ystride
	if rotated then
		xstride = area.zstride
		zstride = 1
	else
		xstride = 1
		zstride = area.zstride
	end

	-- bottom end
	safe_write(data, area, vi, c_amethyst)
	safe_write(data, area, vi+xstride, c_amethyst)

	for i = 1, length do
		vi = vi + ystride

		safe_write(data, area, vi-xstride, c_amethyst)

		safe_write(data, area, vi+zstride, c_amethyst)
		safe_write(data, area, vi, c_amethyst)
		safe_write(data, area, vi-zstride, c_amethyst)

		safe_write(data, area, vi+zstride+xstride, c_amethyst)
		safe_write(data, area, vi+xstride, c_amethyst)
		safe_write(data, area, vi-zstride+xstride, c_amethyst)

		safe_write(data, area, vi+2*xstride, c_amethyst)

		if i % 2 == 0 and i ~= length then
			vi = vi + xstride * x_slant + zstride * z_slant
		end
	end

	vi = vi + ystride

	safe_write(data, area, vi, c_amethyst)
	safe_write(data, area, vi+xstride, c_amethyst)
end


code:
df_underworld_items.doc = {}

if not minetest.get_modpath("doc") then
	return
end

local S = minetest.get_translator(minetest.get_current_modname())

df_underworld_items.doc.glowstone_desc = S("Bright glowing stones of unknown origin found lodged in the crevices of the underworld's ceiling.")
df_underworld_items.doc.glowstone_usage = S("These stones are highly volatile and should not be disturbed.")

df_underworld_items.doc.slade_desc = S("The very foundation of the world, Slade is a mysterious ultra-dense substance.")
df_underworld_items.doc.slade_usage = S("Slade is extremely hard to work with so it has little use.")
if df_underworld_items.config.invulnerable_slade then
	df_underworld_items.doc.slade_usage = df_underworld_items.doc.slade_usage .. " " .. S("In fact, Slade is impervious to conventional mining entirely.")
end

df_underworld_items.doc.slade_seal_desc = S("This block of Slade, carved by an unknown hand, is engraved with mysterious symbols. Most of the engraving's meaning is lost to the mists of time but one frament in the oldest known language can be translated: \"This place is not a place of honor.\"")

df_underworld_items.doc.glow_amethyst_desc = S("Glowing purple crystals that grow through holes in the foundation of the world.")
df_underworld_items.doc.glow_amethyst_usage = S("These crystals have no known use.")

df_underworld_items.doc.pit_plasma_desc = S("The liquid found in the deepest pits in the underworld is highly dangerous and damaging.")
df_underworld_items.doc.pit_plasma_usage = S("The only use for this material is that it destroys whatever is thrown into it. It cannot otherwise be manipulated.")

df_underworld_items.doc.slade_drill_desc = S("This shard of a spent slade breacher doesn't appear to have been intended as a portable tool, but it can be used as one.")
df_underworld_items.doc.slade_drill_usage = S("Use this on a target to unleash the energies used to dig through the foundations of the world.")


code:
local S = minetest.get_translator(minetest.get_current_modname())

local glowstone_def = {
	_doc_items_longdesc = df_underworld_items.doc.glowstone_desc,
	_doc_items_usagehelp = df_underworld_items.doc.glowstone_usage,
	light_source = minetest.LIGHT_MAX,
	description = S("Lightseam"),
	tiles = {
		{
			name = "dfcaverns_glowstone_anim.png",
			backface_culling = true,
			animation = {
				type = "vertical_frames",
				aspect_w = 16,
				aspect_h = 16,
				length = 8,
			},
		},
	},
	is_ground_content = false,
	groups = {cracky=3, pickaxey = 1, building_block = 1, material_stone =1},
	sounds = df_dependencies.sound_glass(),
	paramtype = "light",
	drawtype = "glasslike",
	drop = "",
	sunlight_propagates = true,
	_mcl_blast_resistance = 0.5,
	_mcl_hardness = 0.5,
}
local tnt_boom = df_dependencies.tnt_boom
if tnt_boom then
	glowstone_def.on_dig = function(pos, node, digger)
		if minetest.node_dig(pos, node, digger) then
			tnt_boom(pos, {radius=3})
		end
	end
end
minetest.register_node("df_underworld_items:glowstone", glowstone_def)


code:
local S = minetest.get_translator(minetest.get_current_modname())

local lava_source = df_dependencies.node_name_lava_source

minetest.register_node("df_underworld_items:pit_plasma", {
	description = S("Glowing Pit Plasma"),
	_doc_items_longdesc = df_underworld_items.doc.pit_plasma_desc,
	_doc_items_usagehelp = df_underworld_items.doc.pit_plasma_usage,
	tiles = {
		{
			name = "dfcaverns_pit_plasma.png",
			backface_culling = true,
			animation = {
				type = "vertical_frames",
				aspect_w = 32,
				aspect_h = 32,
				length = 0.5,
			},
		},
	},

	groups={pit_plasma=1, pit_plasma_resistant=1,destroys_items=1, set_on_fire=15, fire_damage=1, liquid=1},
	walkable = false,
	pointable = false,
	diggable = false,
	sunlight_propagates = false,
	buildable_to = true,
	drop = "",
	drowning = 1,
	liquid_viscosity = 7,
	damage_per_second = 4 * 2,
	post_effect_color = {a = 200, r = 245, g = 211, b = 251},
	drawtype="liquid",
	liquidtype="source",
	liquid_alternative_flowing = "df_underworld_items:pit_plasma_flowing",
	liquid_alternative_source = "df_underworld_items:pit_plasma",
	liquid_renewable = false,
	is_ground_content = false,
	light_source = minetest.LIGHT_MAX,
	paramtype = "light",
	_mcl_blast_resistance = 3600000,
	_mcl_hardness = -1,
})

minetest.register_node("df_underworld_items:pit_plasma_flowing", {
	description = S("Glowing Pit Plasma"),
	_doc_items_longdesc = df_underworld_items.doc.pit_plasma_desc,
	_doc_items_usagehelp = df_underworld_items.doc.pit_plasma_usage,
	tiles = {"dfcaverns_pit_plasma_static.png"},
	special_tiles = {
		{
			name = "dfcaverns_pit_plasma.png",
			backface_culling = true,
			animation = {
				type = "vertical_frames",
				aspect_w = 32,
				aspect_h = 32,
				length = 0.5,
			},
		},
		{
			name = "dfcaverns_pit_plasma.png",
			backface_culling = true,
			animation = {
				type = "vertical_frames",
				aspect_w = 32,
				aspect_h = 32,
				length = 0.5,
			},
		},
	},
	groups={pit_plasma=1, pit_plasma_resistant=1, not_in_creative_inventory=1,destroys_items=1, set_on_fire=15, fire_damage=1, liquid=1},
	walkable = false,
	pointable = false,
	diggable = false,
	sunlight_propagates = false,
	buildable_to = true,
	drop = "",
	drowning = 1,
	liquid_viscosity = 7,
	damage_per_second = 4 * 2,
	post_effect_color = {a = 200, r = 245, g = 211, b = 251},
	liquidtype = "flowing",
	drawtype = "flowingliquid",
	paramtype2 = "flowingliquid",
	liquid_alternative_flowing = "df_underworld_items:pit_plasma_flowing",
	liquid_alternative_source = "df_underworld_items:pit_plasma",
	liquid_renewable = false,
	is_ground_content = false,
	light_source = minetest.LIGHT_MAX,
	paramtype = "light",
	_mcl_blast_resistance = 3600000,
	_mcl_hardness = -1,
})

if minetest.get_modpath("radiant_damage") and radiant_damage.override_radiant_damage then
	if radiant_damage.config.enable_mese_damage then
		radiant_damage.override_radiant_damage("mese", {emitted_by={["group:pit_plasma"] = radiant_damage.config.mese_damage*2}})
	end
	if radiant_damage.config.enable_heat_damage then
		radiant_damage.override_radiant_damage("heat", {emitted_by={["group:pit_plasma"] = radiant_damage.config.lava_damage}})
	end
end

local sparkle = function(sparkle_pos)
	minetest.add_particlespawner({
		amount = 10,
		time = 2,
		minpos = {x=sparkle_pos.x-5, y=sparkle_pos.y-0.5, z=sparkle_pos.z-5},
		maxpos = {x=sparkle_pos.x+5, y=sparkle_pos.y+0.5, z=sparkle_pos.z+5},
		minvel = {x=-0.1, y=2, z=-0.1},
		maxvel = {x=0.1, y=2, z=0.1},
		minacc = {x=0, y=2, z=0},
		maxacc = {x=0, y=2, z=0},
		minexptime = 5,
		maxexptime = 10,
		minsize = 1,
		maxsize = 1,
		collisiondetection = true,
		collision_removal = true,
		vertical = false,
		glow = minetest.LIGHT_MAX,
		texture = "dfcaverns_glowpit_particle.png",
	})
	minetest.sound_play({name="dfcaverns_arcing"}, {
		pos = sparkle_pos,
		gain = 1,
		max_hear_distance = 32,
	})
end

if df_underworld_items.config.destructive_pit_plasma then
	minetest.register_abm({
		label = "glowing pit matter degradation",
		nodenames = {"group:pit_plasma"},
		neighbors = df_dependencies.abm_pit_plasma_neighbors,
		interval = 2,
		chance = 30,
		catch_up = false,
		action = function(pos)
			local sparkle_pos
			for x = pos.x-1, pos.x+1 do
				for y = pos.y-1, pos.y+1 do
					for z = pos.z-1, pos.z+1 do
						local test_pos = {x=x, y=y, z=z}
						local node_name = minetest.get_node(test_pos).name
						if minetest.get_item_group(node_name, "pit_plasma_resistant") == 0 then
							sparkle_pos = test_pos
							if minetest.get_item_group(node_name, "stone") > 0 then
								if math.random() < 0.66 then
									minetest.set_node(test_pos, {name="df_underworld_items:glow_amethyst"})
								else
									minetest.set_node(test_pos, {name=lava_source})
								end
							else
								minetest.set_node(test_pos, {name="air"})
							end
						end
					end
				end
			end
			if sparkle_pos then
				sparkle(sparkle_pos)
			end
		end,
	})
end
minetest.register_abm({
	label = "glowing pit sparkle",
	nodenames = {"group:pit_plasma"},
	neighbors = {"air"},
	interval = 2,
	chance = 30,
	catch_up = false,
	action = function(pos)
		local air_pos = minetest.find_node_near(pos, 1, "air")
		if air_pos then
			sparkle(air_pos)
		end
	end,
})


code:
if minetest.get_modpath("hunter_statue") and df_underworld_items.config.underworld_hunter_statues then

	local S = minetest.get_translator(minetest.get_current_modname())

	hunter_statue.register_hunter_statue("df_underworld_items:hunter_statue", {
		description = S("Guardian Statue"),
		chance = 2,
		tiles = {
			{ name = "dfcaverns_slade.png", backface_culling = true },
			{ name = "dfcaverns_slade.png^(dfcaverns_statue_eyes.png^[opacity:128)", backface_culling = true },
			{ name = "dfcaverns_slade.png^(dfcaverns_statue_fang_overlay.png^[opacity:128)", backface_culling = true },
		},
		tnt_vulnerable = true,
		tnt_debris = "df_underworld_items:slade_sand",
		groups = {hunter_statue = 1, falling_node = 1, immortal = 1},
		hunters_allowed_here = function(pos)
			return not minetest.find_node_near(pos, 6, "df_underworld_items:ancient_lantern_slade", true)
		end,
		other_overrides = {
			can_dig = function(pos, player)
				if player then
					return minetest.check_player_privs(player, "server")
				end
				return false
			end,
		}
	})

end


code:
df_underworld_items = {}

local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)

dofile(modpath.."/config.lua")
dofile(modpath.."/doc.lua")

dofile(modpath.."/crystals_amethyst.lua")
dofile(modpath.."/glow_stone.lua")
dofile(modpath.."/slade.lua")
dofile(modpath.."/hunter_statue.lua")
dofile(modpath.."/glowing_pit_plasma.lua")

dofile(modpath.."/puzzle_seal.lua")
dofile(modpath.."/puzzle_chest.lua")
dofile(modpath.."/ancient_lanterns.lua")
if df_underworld_items.config.enable_slade_drill then
	dofile(modpath.."/slade_drill.lua")
end


code:
# textdomain: df_underworld_items

### ancient_lanterns.lua ###

Ancient Lantern=

### crystals_amethyst.lua ###

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Glowing Amethyst Block=Glühender Amethystblock

### doc.lua ###

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Bright glowing stones of unknown origin found lodged in the crevices of the underworld's ceiling.=Hell leuchtende Steine unbekannter Herkunft wurden in den Spalten der Decke der Unterwelt gefunden.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Glowing purple crystals that grow through holes in the foundation of the world.=Glühende lila Kristalle, die durch Löcher im Fundament der Welt wachsen.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
In fact, Slade is impervious to conventional mining entirely.=Tatsächlich ist Slade für den konventionellen Bergbau völlig undurchlässig.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Slade is extremely hard to work with so it has little use.=Slade ist extrem schwer zu bearbeiten, daher hat es wenig Sinn.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
The liquid found in the deepest pits in the underworld is highly dangerous and damaging.=Die Flüssigkeit in den tiefsten Gruben der Unterwelt ist äußerst gefährlich und schädlich.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
The only use for this material is that it destroys whatever is thrown into it. It cannot otherwise be manipulated.=Die einzige Verwendung für dieses Material ist, dass es alles zerstört, was hineingeworfen wird. Es kann nicht anders manipuliert werden.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
The very foundation of the world, Slade is a mysterious ultra-dense substance.=Slade ist das Fundament der Welt und eine mysteriöse, ultradichte Substanz.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
These crystals have no known use.=Diese Kristalle haben keine bekannte Verwendung.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
These stones are highly volatile and should not be disturbed.=Diese Steine sind sehr flüchtig und sollten nicht gestört werden.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
This block of Slade, carved by an unknown hand, is engraved with mysterious symbols. Most of the engraving's meaning is lost to the mists of time but one frament in the oldest known language can be translated: "This place is not a place of honor."=Dieser von einer unbekannten Hand geschnitzte Slade-Block ist mit mysteriösen Symbolen versehen. Der größte Teil der Bedeutung der Gravur geht den Nebeln der Zeit verloren, aber ein Frament in der ältesten bekannten Sprache kann übersetzt werden: "Dieser Ort ist kein Ehrenplatz."

This shard of a spent slade breacher doesn't appear to have been intended as a portable tool, but it can be used as one.=

Use this on a target to unleash the energies used to dig through the foundations of the world.=

### glow_stone.lua ###

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Lightseam=Lichtnaht

### glowing_pit_plasma.lua ###

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Glowing Pit Plasma=Glühendes Grubenplasma

### hunter_statue.lua ###

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Guardian Statue=Wächterstatue

### puzzle_chest.lua ###

Puzzle Chest=

### puzzle_seal.lua ###

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Active Slade Breacher=Aktiver Slade Breacher
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Breach in the Slade=Brich den Slade ein
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Inscribed Slade Block=Beschrifteter Slade-Block
Slade Capstone=
Slade Puzzle Seal=
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Turn=Wende

### slade.lua ###

Slade=
Slade Block=
Slade Brick=
Slade Sand=
Slade Seal=
Slade Wall=

### slade_drill.lua ###

Slade Drill=

##### not used anymore #####

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Slade Block Slab=Slade Block Platte
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Slade Block Stair=Slade Block Treppe
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Slade Slab=Slade-Platte


code:
# textdomain: df_underworld_items

### ancient_lanterns.lua ###

Ancient Lantern=

### crystals_amethyst.lua ###

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Glowing Amethyst Block=Blocco di ametista incandescente

### doc.lua ###

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Bright glowing stones of unknown origin found lodged in the crevices of the underworld's ceiling.=Pietre luminose e luminose di origine sconosciuta trovate alloggiate nelle fessure del soffitto del mondo sotterraneo.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Glowing purple crystals that grow through holes in the foundation of the world.=Cristalli viola luminosi che crescono attraverso i buchi nelle fondamenta del mondo.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
In fact, Slade is impervious to conventional mining entirely.=In effetti, Slade è completamente impermeabile al mining convenzionale.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Slade is extremely hard to work with so it has little use.=Slade è estremamente difficile da lavorare, quindi è poco utile.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
The liquid found in the deepest pits in the underworld is highly dangerous and damaging.=Il liquido trovato nelle fosse più profonde degli inferi è altamente pericoloso e dannoso.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
The only use for this material is that it destroys whatever is thrown into it. It cannot otherwise be manipulated.=L'unico uso di questo materiale è che distrugge tutto ciò che viene gettato al suo interno. Non può essere altrimenti manipolato.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
The very foundation of the world, Slade is a mysterious ultra-dense substance.=Il fondamento stesso del mondo, Slade è una misteriosa sostanza ultra-densa.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
These crystals have no known use.=Questi cristalli non hanno alcun uso noto.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
These stones are highly volatile and should not be disturbed.=Queste pietre sono altamente volatili e non dovrebbero essere disturbate.

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
This block of Slade, carved by an unknown hand, is engraved with mysterious symbols. Most of the engraving's meaning is lost to the mists of time but one frament in the oldest known language can be translated: "This place is not a place of honor."=Questo blocco di Slade, scolpito da una mano sconosciuta, è inciso con simboli misteriosi. La maggior parte del significato dell'incisione si perde nella notte dei tempi, ma un frammento nella più antica lingua conosciuta può essere tradotto: "Questo posto non è un posto d'onore".

This shard of a spent slade breacher doesn't appear to have been intended as a portable tool, but it can be used as one.=

Use this on a target to unleash the energies used to dig through the foundations of the world.=

### glow_stone.lua ###

Lightseam=

### glowing_pit_plasma.lua ###

Glowing Pit Plasma=

### hunter_statue.lua ###

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Guardian Statue=Statua del guardiano

### puzzle_chest.lua ###

Puzzle Chest=

### puzzle_seal.lua ###

#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Active Slade Breacher=Attivo Slade Breacher
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Breach in the Slade=Violazione nello Slade
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Inscribed Slade Block=Inscritto Slade Block
Slade Capstone=
Slade Puzzle Seal=
Turn=

### slade.lua ###

Slade=
Slade Block=
Slade Brick=
Slade Sand=
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Slade Seal=Sigillo Slade
Slade Wall=

### slade_drill.lua ###

Slade Drill=


code:
# textdomain: df_underworld_items

### ancient_lanterns.lua ###

Ancient Lantern=

### crystals_amethyst.lua ###

Glowing Amethyst Block=

### doc.lua ###

Bright glowing stones of unknown origin found lodged in the crevices of the underworld's ceiling.=

Glowing purple crystals that grow through holes in the foundation of the world.=

In fact, Slade is impervious to conventional mining entirely.=

Slade is extremely hard to work with so it has little use.=

The liquid found in the deepest pits in the underworld is highly dangerous and damaging.=

The only use for this material is that it destroys whatever is thrown into it. It cannot otherwise be manipulated.=

The very foundation of the world, Slade is a mysterious ultra-dense substance.=

These crystals have no known use.=

These stones are highly volatile and should not be disturbed.=

This block of Slade, carved by an unknown hand, is engraved with mysterious symbols. Most of the engraving's meaning is lost to the mists of time but one frament in the oldest known language can be translated: "This place is not a place of honor."=

This shard of a spent slade breacher doesn't appear to have been intended as a portable tool, but it can be used as one.=

Use this on a target to unleash the energies used to dig through the foundations of the world.=

### glow_stone.lua ###

Lightseam=

### glowing_pit_plasma.lua ###

Glowing Pit Plasma=

### hunter_statue.lua ###

Guardian Statue=

### puzzle_chest.lua ###

Puzzle Chest=

### puzzle_seal.lua ###

Active Slade Breacher=
Breach in the Slade=
Inscribed Slade Block=
Slade Capstone=
Slade Puzzle Seal=
Turn=

### slade.lua ###

Slade=
Slade Block=
Slade Brick=
Slade Sand=
Slade Seal=
Slade Wall=

### slade_drill.lua ###

Slade Drill=


code:
name = df_underworld_items
description = Various node types used by the dfcaverns mapgen mod for its underworld layer.
depends = df_dependencies
optional_depends = doc, radiant_damage, mesecons_mvps, hunter_statue


code:
local S = minetest.get_translator(minetest.get_current_modname())

local sum_square = function(input)
	return {input[1] + input[2], input[3] + input[4], input[1] + input[3], input[2] + input[4]}
end

local initialize = function(pos, meta)
	if not meta:contains("key") then
		local inv = meta:get_inventory()
		inv:set_size("main", 8)
		local next_seed = math.random() * 2^21
		math.randomseed(pos.x + pos.y^8 + pos.z^16)
		-- Key is consistent with location
		local key = {math.random(0,7), math.random(0,7), math.random(0,7), math.random(0,7)}
		math.randomseed(next_seed)
		local state = {math.random(0,7), math.random(0,7), math.random(0,7), math.random(0,7)}
		local key_sum = sum_square(key)
		local state_sum = sum_square(state)
		if (key_sum[1] == state_sum[1] and key_sum[2] == state_sum[2] and key_sum[3] == state_sum[3] and key_sum[4] == state_sum[4]) then
			-- if we randomly start off solved change one of the states to make it unsolved.
			-- lucky player gets an easy one.
			state[1] = (state[1] + 1) % 8
		end
		meta:set_string("key", minetest.serialize(key))
		meta:set_string("state", minetest.serialize(state))
		meta:mark_as_private("key")
		meta:mark_as_private("state") -- not really necessary, but for consistency
	end
end

-- 1 2
-- 3 4

local formspec_dial = function(identifier, state)
	return "image[0.5,0;2,1;dfcaverns_chest_dial_"..tostring(state)..".png]"
		.. "button[0,0.25;0.5,0.5;"..identifier.."+;<]"
		.. "button[2.5,0.25;0.5,0.5;"..identifier.."-;>]"
end

local formspec_bar = function(horizontal, state, key)
	if horizontal then
		return "box[0,0;".. tostring(key/14 * 2 + 0.1) ..",1;#222222ff]box[0.1,0.1;".. tostring(state/14 * 2)..",0.85;#ff00ff44]"
	end
	return "box[0,0;1,".. tostring(key/14 * 2 + 0.1) ..";#222222ff]box[0.1,0.1;0.85,".. tostring(state/14 * 2)..";#ff00ff44]"
end

local prefix = "df_underworld_items:puzzle_chest"
local prefix_len = string.len("df_underworld_items:puzzle_chest")

local itemslot_bg = df_dependencies.get_itemslot_bg_padded

local show_formspec = function(pos, node, clicker, itemstack, pointed_thing)
	local meta = minetest.get_meta(pos)
	initialize(pos, meta)
	local key = minetest.deserialize(meta:get_string("key"))
	local state = minetest.deserialize(meta:get_string("state"))
	local key_sum = sum_square(key)
	local state_sum = sum_square(state)
	local solved = key_sum[1] == state_sum[1] and key_sum[2] == state_sum[2] and key_sum[3] == state_sum[3] and key_sum[4] == state_sum[4]
	local playername = clicker:get_player_name()
	local formname = prefix .. minetest.pos_to_string(pos)
	local formspec = "formspec_version[6]"
		.. "size[11,12]"
		.. "container[1.5,0.5]"
		.. "container[0,0]" .. formspec_dial("dial1", state[1]) .. "container_end[]"
		.. "container[3,0]" .. formspec_dial("dial2", state[2]) .. "container_end[]"
		.. "container[0,1.5]" .. formspec_dial("dial3", state[3]) .. "container_end[]"
		.. "container[3,1.5]" .. formspec_dial("dial4", state[4]) .. "container_end[]"
		.. "container[6.1,0]" .. formspec_bar(true, state_sum[1], key_sum[1]) .. "container_end[]"
		.. "container[6.1,1.5]" .. formspec_bar(true, state_sum[2], key_sum[2]) .. "container_end[]"
		.. "container[1,2.75]" .. formspec_bar(false, state_sum[3], key_sum[3]) .. "container_end[]"
		.. "container[4,2.75]" .. formspec_bar(false, state_sum[4], key_sum[4]) .. "container_end[]"
		.. "container_end[]"
		.. itemslot_bg(0.6,6.7,8,4,0.25)
		.. "list[current_player;main;0.6,6.7;8,4;]"
	if solved then
		local nodemeta = "nodemeta:"..pos.x..","..pos.y..","..pos.z
		formspec = formspec
			.. itemslot_bg(0.6,5.4,8,1,0.25)
			.. "list["..nodemeta..";main;0.6,5.4;8,1;]"
			.. "listring[]"
		if meta:get_string("solved") ~= "true" then
			minetest.sound_play("dfcaverns_puzzle_chest_open", {pos = pos})
			meta:set_string("solved", "true")
			local old_node = minetest.get_node(pos)
			minetest.swap_node(pos, {name="df_underworld_items:puzzle_chest_opened", param2=old_node.param2})
		end
	elseif meta:get_string("solved") == "true" then
		minetest.sound_play("dfcaverns_puzzle_chest_close", {pos = pos})
		meta:set_string("solved", "")
		local old_node = minetest.get_node(pos)
		minetest.swap_node(pos, {name="df_underworld_items:puzzle_chest_closed", param2=old_node.param2})
	end

	minetest.show_formspec(playername, formname, formspec)
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
	if string.sub(formname, 1, prefix_len) ~= prefix then return end
	if fields.quit then return end
	local pos = minetest.string_to_pos(string.sub(formname, prefix_len+1, -1))
	local meta = minetest.get_meta(pos)
	local state = minetest.deserialize(meta:get_string("state"))
	for i = 1,4 do
		if fields["dial"..tostring(i).."+"] then
			state[i] = (state[i] + 1) % 8
			break
		elseif fields["dial"..tostring(i).."-"] then
			state[i] = (state[i] - 1) % 8
			break
		end
	end
	meta:set_string("state", minetest.serialize(state))
	show_formspec(pos, nil, player)
end)

local puzzle_chest_can_dig = function(pos, player)
	local meta = minetest.get_meta(pos)
	initialize(pos, meta)
	local inv = meta:get_inventory()
	return inv:is_empty("main")
end

minetest.register_node("df_underworld_items:puzzle_chest_closed", {
	description = S("Puzzle Chest"),
	_doc_items_longdesc = df_underworld_items.doc.puzzle_chest_desc,
	_doc_items_usagehelp = df_underworld_items.doc.puzzle_chest_usage,
	tiles = {
		"dfcaverns_slade_block.png^dfcaverns_chest_mask_top.png",
		"dfcaverns_slade_block.png^dfcaverns_chest_mask_top.png",
		"dfcaverns_slade_block.png^dfcaverns_chest_mask_side.png",
		"dfcaverns_slade_block.png^dfcaverns_chest_mask_side.png",
		"dfcaverns_slade_block.png^dfcaverns_chest_mask_side.png",
		"dfcaverns_slade_block.png^dfcaverns_chest_mask_side.png^dfcaverns_chest_lock_plate.png^dfcaverns_chest_lock_colours.png",
	},
	paramtype2="facedir",
	is_ground_content = false,
	groups = {stone=1, slade=1, pit_plasma_resistant=1, mese_radiation_shield=1, cracky = 3, building_block=1, material_stone=1},
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	_mcl_blast_resistance = 1200,
	_mcl_hardness = 50,

	on_rightclick = show_formspec,
	can_dig = puzzle_chest_can_dig,
})

minetest.register_node("df_underworld_items:puzzle_chest_opened", {
	description = S("Puzzle Chest"),
	_doc_items_longdesc = df_underworld_items.doc.puzzle_chest_desc,
	_doc_items_usagehelp = df_underworld_items.doc.puzzle_chest_usage,
	tiles = {
		{name = "dfcaverns_slade_block.png^dfcaverns_chest_mask_top.png", backface_culling = true},
		{name = "dfcaverns_slade_block.png^dfcaverns_chest_mask_top.png", backface_culling = true},
		{name = "dfcaverns_slade_block.png^dfcaverns_chest_mask_side.png", backface_culling = true},
		{name = "dfcaverns_slade_block.png^dfcaverns_chest_mask_side.png", backface_culling = true},
		{name = "dfcaverns_slade_block.png^dfcaverns_chest_mask_side.png^dfcaverns_chest_lock_plate.png^dfcaverns_chest_lock_colours.png", backface_culling = true},
		{name = "([combine:16x32:0,0=dfcaverns_slade_block.png:0,16=dfcaverns_slade_block.png)^dfcaverns_chest_mask_interior.png", backface_culling = true},
	},
	is_ground_content = false,
	mesh = "dfcaverns_chest_open.obj",
	drawtype= "mesh",
	visual = "mesh",
	paramtype2="facedir",
	groups = {stone=1, slade=1, pit_plasma_resistant=1, mese_radiation_shield=1, cracky = 3, building_block=1, material_stone=1, not_in_creative_inventory=1},
	drop = "df_underworld_items:puzzle_chest_closed",
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	_mcl_blast_resistance = 1200,
	_mcl_hardness = 50,

	on_rightclick = show_formspec,
	can_dig = puzzle_chest_can_dig,
})


code:
local S = minetest.get_translator(minetest.get_current_modname())

local named_waypoints_path = minetest.get_modpath("named_waypoints")

-- override these to allow achievements to be recorded without requiring a dependency
df_underworld_items.slade_breacher_triggered = function(pos, player) end
df_underworld_items.puzzle_seal_solved = function(pos, player) end -- player can be nil

local invulnerable = df_underworld_items.config.invulnerable_slade and not minetest.settings:get_bool("creative_mode")

local can_dig
if invulnerable then
	can_dig = function(pos, player)
		return minetest.check_player_privs(player, "server")
	end
end

local slade_mcl_blast_resistance = 1200
local slade_mcl_hardness = 50
local slade_groups = {stone=1, slade=1, pit_plasma_resistant=1, mese_radiation_shield=1, cracky = 3, not_in_creative_inventory=1,creative_breakable=1, building_block=1, material_stone=1}
if invulnerable then
	slade_groups.immortal = 1
	slade_mcl_blast_resistance = 3600000
	slade_mcl_hardness = -1
end

local lava_source = df_dependencies.node_name_lava_source

-- Ensures that the node is functioning correctly
local ensure_meta = function(pos)
    local meta = minetest.get_meta(pos)
	if not meta:contains("key") then
		local inv = meta:get_inventory()
		inv:set_size("main", 8)
		local next_seed = math.random() * 2^21
		math.randomseed(pos.x + pos.z^16)
		-- Key is consistent with location
		local key = {math.random(0,7), math.random(0,7), math.random(0,7), math.random(0,7), math.random(0,7), math.random(0,7),math.random(0,7), math.random(0,7)}
		math.randomseed(next_seed)
		meta:set_string("key", minetest.serialize(key))
		meta:mark_as_private("key")
	end

	local timer = minetest.get_node_timer(pos)
	if not timer:is_started() then
		timer:start(4)
	end
end

local colour_groups = {
	{"color_black", "basecolor_black", "excolor_black", "excolor_darkgrey", "color_dark_grey"},	-- 0
	{"color_red", "basecolor_red", "excolor_red"},	-- 1
	{"color_orange", "basecolor_orange", "excolor_orange"},	-- 2
	{"color_yellow", "basecolor_yellow", "excolor_yellow"},	-- 3
	{"color_green", "basecolor_green", "excolor_green", "excolor_lime", "color_dark_green"},	-- 4
	{"color_blue", "basecolor_blue", "excolor_blue", "excolor_sky_blue"},	-- 5
	{"color_violet", "excolor_violet",},	-- 6
	{"color_white", "basecolor_white", "excolor_white", "excolor_lightgrey", "color_grey"},	-- 7
}

df_underworld_items.colour_items = {}

local cull_colour_groups = function()
	local culled_colour_groups = {}
	for _, groups in ipairs(colour_groups) do
		local new_set = {}
		for _, colour_group in pairs(groups) do
			for itemname, def in pairs(minetest.registered_items) do
				if minetest.get_item_group(itemname, colour_group) ~= 0 then
					table.insert(new_set, colour_group)
					break
				end
			end
		end
		assert(#new_set > 0, "Unsolvable puzzle seals due to lack of any registered items belonging to one of " .. dump(groups))
		table.insert(culled_colour_groups, new_set)
	end
	colour_groups = culled_colour_groups

	-- collect a list of all items with useful colour groups
	-- to be used for treasure generation
	local colour_item_set = {}
	for itemname, def in pairs(minetest.registered_items) do
		for _, groups in ipairs(colour_groups) do
			for _, colour in ipairs(groups) do
				if minetest.get_item_group(itemname, colour) ~= 0 then
					colour_item_set[itemname] = true
				end
			end
		end
	end
	for colour_item, _ in pairs(colour_item_set) do
		table.insert(df_underworld_items.colour_items, colour_item)
	end
end
minetest.after(0, cull_colour_groups)

local item_represents_number = function(itemname, number)
	for _, group in pairs(colour_groups[number+1]) do
		if minetest.get_item_group(itemname, group) ~= 0 then
			return true
		end
	end
	return false
end

local test_key = function(pos, player)
    local meta = minetest.get_meta(pos)
	if not meta:contains("key") then
		return false
	end

	local keystring = meta:get_string("key")
	local key = minetest.deserialize(keystring)
	local inv = meta:get_inventory()
	for offset = 0, 7 do
		local valid
		for i = 0, 7 do
			valid = true
			local keyval = (i + offset) % 8 + 1
			local item = inv:get_stack("main", i+1)
			if not item_represents_number(item:get_name(), key[keyval]) then
				valid = false
				break
			end
		end
		if valid then
			local unlocked = meta:get_int("unlocked")
			if unlocked == 0 then
				df_underworld_items.puzzle_seal_solved(pos, player)
				meta:set_int("unlocked", 1)
			end
			return true
		end
	end
	meta:set_int("unlocked", 0)
	return false
end

--We were a powerful culture.
--This place is not a place of honor.
--No esteemed deed is commemorated here.
--What is here is dangerous and repulsive.
--This message is a warning about danger.
--The danger is still present.
--The danger is unleashed if you disturb this place.
--This place is best shunned and left uninhabited.

local formspec_prefix = "df_underworld_items_puzzle_seal:"
local itemslot_bg = df_dependencies.get_itemslot_bg
local get_formspec = function(pos, unlocked)
	local formspec =
		"size[8,8]"
		.."image[0,0;2.5,4;dfcaverns_puzzle_inscription_background.png^dfcaverns_puzzle_inscription_1.png]"
		.."image[5.8,0;2.5,4;dfcaverns_puzzle_inscription_background.png^[transformR180^dfcaverns_puzzle_inscription_2.png]"
		.."container[2.25,0]"
		.."list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;0.25,0.25;1,1;0]"
		..itemslot_bg(0.25,0.25,1,1)
		.."list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;1.25,0;1,1;1]"
		..itemslot_bg(1.25,0,1,1)
		.."list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;2.25,0.25;1,1;2]"
		..itemslot_bg(2.25,0.25,1,1)
		.."list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;2.5,1.25;1,1;3]"
		..itemslot_bg(2.5,1.25,1,1)
		.."list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;2.25,2.25;1,1;4]"
		..itemslot_bg(2.25,2.25,1,1)
		.."list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;1.25,2.5;1,1;5]"
		..itemslot_bg(1.25,2.5,1,1)
		.."list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;0.25,2.25;1,1;6]"
		..itemslot_bg(0.25,2.25,1,1)
		.."list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;0,1.25;1,1;7]"
		..itemslot_bg(0,1.25,1,1)
	if unlocked then
		formspec = formspec .. "image_button[1.25,1.25;1,1;dfcaverns_seal.png;open;"..S("Turn").."]"
	else
		formspec = formspec .. "image[1.25,1.25;1,1;dfcaverns_seal.png]"
	end

	formspec = formspec
		.."container_end[]"
--		.."container[5,0.5]"
--		.."box[0,0;1,5;#0A0000]box[0.1,0.1;0.8,4.8;#000000]box[0.1," .. 0.1 + 4.8*completion ..";0.8,".. 4.8*completion ..";#FFCC22]"
--		.."container_end[]"
		.."container[0,4]list[current_player;main;0,0;8,1;]listring[]"
		..itemslot_bg(0,0,8,1)
		.."list[current_player;main;0,1.25;8,3;8]"
		..itemslot_bg(0,1.25,8,3)
		.."container_end[]"
		return formspec
end
local refresh_formspec = function(pos, player)
	local player_name = player:get_player_name()
	local unlocked = test_key(pos, player)
	local formspec = get_formspec(pos, unlocked)
	minetest.show_formspec(player_name, formspec_prefix..minetest.pos_to_string(pos), formspec)
end

local show_key = function(pos, index)
    minetest.sound_play("dfcaverns_seal_key", {
        pos = pos,
        gain = 0.125,
        max_hear_distance = 32,
    })
    local meta = minetest.get_meta(pos)
	local keystring = meta:get_string("key")
	local key = minetest.deserialize(keystring)
	--minetest.chat_send_all(keystring)
	local minpos = vector.add(pos, {x=-1, y=1, z=-1})
	local maxpos = vector.add(pos, {x=1, y=3, z=1})
	minetest.add_particlespawner({
		amount = key[index+1],
		time = 0.5,
		minpos = minpos,
		maxpos = maxpos,
		minvel = {x=-0.25, y=0, z=-0.25},
		maxvel = {x=0.25, y=0.25, z=0.25},
		minacc = {x=0, y=0, z=0},
		maxacc = {x=0, y=0, z=0},
		minexptime = 3,
		maxexptime = 3,
		minsize = 1,
		maxsize = 1,
		texture = "dfcaverns_puzzle_particle.png",
		glow = 8,
	})
end

local show_unlocked = function(pos, global)
	-- Plays globally. This is deliberate.
	if global then
		minetest.sound_play("dfcaverns_seal_unlocked", {})
	else
		minetest.sound_play("dfcaverns_seal_unlocked", {pos = pos, max_hear_distance = 128,})
	end
	minetest.add_particle({
		pos = pos,
		velocity = {x=0, y=1, z=0},
		expirationtime = 30,
		size = 20,
		texture = "dfcaverns_seal.png^[invert:rgb^[brighten",
		glow = 15,
--		vertical = true,
	})
end

local puzzle_seal_def = {
	description = S("Slade Puzzle Seal"),
	_doc_items_longdesc = nil,
	_doc_items_usagehelp = nil,
	drawtype = "mesh",
	mesh = "underworld_seal.obj",
	tiles = {"dfcaverns_slade_block.png", "dfcaverns_slade_block.png^dfcaverns_seal.png", "dfcaverns_slade_block.png^(dfcaverns_inscription_1.png^[opacity:128)"},
	paramtype = "light",
	paramtype2 = "facedir",
	light_source = 8,
	groups = slade_groups,
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	selection_box = {
		type = "fixed",
		fixed = {-0.625, -0.625, -0.625, 0.625, 0.625, 0.625},
	},
	collision_box = {
		type = "fixed",
		fixed = {-0.625, -0.625, -0.625, 0.625, 0.625, 0.625},
	},
	is_ground_content = false,
	can_dig = can_dig,
	_mcl_blast_resistance = slade_mcl_blast_resistance,
	_mcl_hardness = slade_mcl_hardness,
	on_blast = function() end,
	on_rotate = function() return false end,
	on_construct = function(pos)
	    ensure_meta(pos)
	end,
	on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
		ensure_meta(pos)
		refresh_formspec(pos, clicker)
	end,
	on_timer = function(pos, elapsed)
		local meta = minetest.get_meta(pos)
		local unlocked = meta:get_int("unlocked")
		if unlocked > 0 then
			meta:set_int("unlocked", unlocked + 1)
			show_unlocked(pos, unlocked <= 13)
		else
			local index = meta:get_int("key_index")
			show_key(pos, index)
			meta:set_int("key_index", (index+1) % 8)
		end
		minetest.get_node_timer(pos):start(4)
	end,
	on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
		refresh_formspec(pos, player)
	end,
	on_metadata_inventory_put = function(pos, listname, index, stack, player)
		refresh_formspec(pos, player)
	end,
	on_metadata_inventory_take = function(pos, listname, index, stack, player)
		refresh_formspec(pos, player)
	end,
}

minetest.register_node("df_underworld_items:puzzle_seal", puzzle_seal_def)

--------------------------------------------------------------------------------
-- Once the seal is opened, it turns into this and digs its way down through the slade.

local tnt_boom = df_dependencies.tnt_boom

local digging_seal_def = {
	description = S("Active Slade Breacher"),
	_doc_items_longdesc = nil,
	_doc_items_usagehelp = nil,
	drawtype = "mesh",
	mesh = "underworld_seal.obj",
	tiles = {"dfcaverns_pit_plasma_static.png", "dfcaverns_pit_plasma_static.png^dfcaverns_seal.png", "dfcaverns_pit_plasma_static.png"},
	paramtype = "light",
	paramtype2 = "facedir",
	light_source = minetest.LIGHT_MAX,
	groups = {immortal=1, stone=1, slade=1, pit_plasma_resistant=1, mese_radiation_shield=1, not_in_creative_inventory=1,building_block=1, material_stone=1},
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	selection_box = {
		type = "fixed",
		fixed = {-0.625, -0.625, -0.625, 0.625, 0.625, 0.625},
	},
	collision_box = {
		type = "fixed",
		fixed = {-0.625, -0.625, -0.625, 0.625, 0.625, 0.625},
	},
	is_ground_content = false,
	_mcl_blast_resistance = 3600000,
	_mcl_hardness = -1,
	can_dig = can_dig,
	on_blast = function() end,
	on_rotate = function() return false end,
	on_construct = function(pos)
		minetest.get_node_timer(pos):start(0)
	end,
	on_timer = function(pos, elapsed)
		local node = minetest.get_node(pos)
		local below_node = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z})
		if below_node.name == "ignore" then
			-- map's not loaded below, try again later
			minetest.get_node_timer(pos):start(4)
			return
		end

		minetest.sound_play("dfcaverns_massive_digging", {
			pos = pos,
			gain = 1.0,
			max_hear_distance = 32,
		})

		local minpos = vector.add(pos, {x=0, y=-1, z=0})
		minetest.add_particlespawner({
			amount = 100,
			time = 4,
			minpos = minpos,
			maxpos = minpos,
			minvel = {x=-5, y=0, z=-5},
			maxvel = {x=5, y=20, z=5},
			minacc = {x=0, y=-10, z=0},
			maxacc = {x=0, y=-10, z=0},
			minexptime = 5,
			maxexptime = 15,
			minsize = 1,
			maxsize = 5,
			texture = "dfcaverns_glowpit_particle.png",
			collisiondetection = true,
			collision_removal = true,
			glow = 15,
		})

		if minetest.get_item_group(below_node.name, "slade") == 0 then
			if df_underworld_items.config.enable_slade_drill then
				minetest.set_node(pos, {name="air"})
				if tnt_boom then
					tnt_boom({x=pos.x, y=pos.y-2, z=pos.z}, {radius=3})
				end
				minetest.add_item(pos, "df_underworld_items:slade_drill")
			else
				minetest.set_node(pos, {name=lava_source})
			end
			return
		end

		local rot = node.param2
		if rot == 0 then
			minetest.place_schematic({x=pos.x-3, y=pos.y-2, z=pos.z-3}, df_underworld_items.seal_stair_schem, 0, {}, true)
			node.param2 = 1
		elseif rot == 1 then
			minetest.place_schematic({x=pos.x-3, y=pos.y-2, z=pos.z-3}, df_underworld_items.seal_stair_schem, 90, {}, true)
			node.param2 = 2
		elseif rot == 2 then
			minetest.place_schematic({x=pos.x-3, y=pos.y-2, z=pos.z-3}, df_underworld_items.seal_stair_schem, 180, {}, true)
			node.param2 = 3
		elseif rot == 3 then
			minetest.place_schematic({x=pos.x-3, y=pos.y-2, z=pos.z-3}, df_underworld_items.seal_stair_schem, 270, {}, true)
			node.param2 = 0
		else
			if tnt_boom then
				tnt_boom(pos, {radius=3})
			end
			minetest.set_node(pos, {name=lava_source})
			return
		end
		minetest.set_node(pos, {name="air"})
		local newpos = {x=pos.x, y=pos.y-2, z=pos.z}
		minetest.set_node(newpos, node)
		minetest.get_node_timer(newpos):start(4)
	end,
}

minetest.register_node("df_underworld_items:digging_seal", digging_seal_def)

local prefix_len = string.len(formspec_prefix)
minetest.register_on_player_receive_fields(function(player, formname, fields)
	if formname:sub(1,prefix_len) ~= formspec_prefix or not fields.open then
		return
	end
	local pos_string = formname:sub(prefix_len+1)
	local pos = minetest.string_to_pos(pos_string)
	if test_key(pos) then
		if named_waypoints_path then
			named_waypoints.update_waypoint("puzzle_seals", pos, {name=S("Breach in the Slade"), color=0xFA264F, player=player:get_player_name()})
		end
		minetest.log("action", "[df_underworld_items] " .. player:get_player_name() .. " triggered a puzzle seal at " .. pos_string)
		minetest.set_node(pos, {name="df_underworld_items:digging_seal", param2 = math.random(1,4)-1})
		minetest.get_node_timer(pos):start(4)
		minetest.close_formspec(player:get_player_name(), formname)
		df_underworld_items.slade_breacher_triggered(pos, player)
	end
end)

-------------------------------------------------------------------------------------------------

local inscription_block_def = {
	description = S("Inscribed Slade Block"),
	_doc_items_longdesc = nil,
	_doc_items_usagehelp = nil,
	tiles = {
		"dfcaverns_slade_block.png",
		"dfcaverns_slade_block.png",
		"dfcaverns_slade_block.png^(dfcaverns_inscription_1.png^[opacity:128)",
		"dfcaverns_slade_block.png^(dfcaverns_inscription_2.png^[opacity:128)",
		"dfcaverns_slade_block.png^(dfcaverns_inscription_3.png^[opacity:128)",
		"dfcaverns_slade_block.png^(dfcaverns_inscription_4.png^[opacity:128)",
	},
	paramtype2 = "facedir",
	groups = slade_groups,
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	is_ground_content = false,
	_mcl_blast_resistance = slade_mcl_blast_resistance,
	_mcl_hardness = slade_mcl_hardness,
	can_dig = can_dig,
	on_blast = function() end,
	on_rotate = function() return false end,
}

minetest.register_node("df_underworld_items:inscription_block", inscription_block_def)

local capstone_def = {
	description = S("Slade Capstone"),
	_doc_items_longdesc = nil,
	_doc_items_usagehelp = nil,
	drawtype = "mesh",
	mesh = "underworld_capstone.obj",
	tiles = {
		"dfcaverns_slade_block.png",
		"dfcaverns_slade_block.png"
	},
	selection_box = {
		type = "fixed",
		fixed = {
			{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
			{-0.25, 0, -0.25, 0.25, 0.5, 0.25},
		},
	},
	collision_box = {
		type = "fixed",
		fixed = {
			{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
			{-0.25, 0, -0.25, 0.25, 0.5, 0.25},
		},
	},
	paramtype = "light",
	paramtype2 = "facedir",
	groups = slade_groups,
	light_source = 8,
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	is_ground_content = false,
	_mcl_blast_resistance = slade_mcl_blast_resistance,
	_mcl_hardness = slade_mcl_hardness,
	can_dig = can_dig,
	on_blast = function() end,
	on_rotate = function() return false end,
}

minetest.register_node("df_underworld_items:slade_capstone", capstone_def)

-----------------------------------------------------------------------------------------
-- Schematics

local n1 = { name = "df_underworld_items:slade_block" }
local n6 = { name = "air", prob = 0 } -- ceiling pieces to leave in place
local n8 = { name = "df_underworld_items:puzzle_seal" }
local n3 = { name = "air"}
local n2 = n3
local n4 = n3
local n7 = n3
local n9 = n3
local n10 = n1
local n11 = n3

local n12 = { name = "df_underworld_items:inscription_block", param2 = 0 }
local n13 = { name = "df_underworld_items:inscription_block", param2 = 1 }
local n14 = { name = "df_underworld_items:inscription_block", param2 = 2 }
local n15 = { name = "df_underworld_items:inscription_block", param2 = 3 }
local n16 = { name = "df_underworld_items:slade_capstone"}

df_dependencies.register_stairs("slade_block")

if invulnerable then
	for name, def in pairs(minetest.registered_nodes) do
		if (name:sub(1,7) == "stairs:" and name:sub(-11) == "slade_block") or
			name:sub(1,11) == "mcl_stairs:" and name:sub(-11) == "slade_block" then
				minetest.override_item(name, {can_dig = can_dig})
		end
	end
end

if df_dependencies.node_name_stair_slade_block then
	n2 = { name = df_dependencies.node_name_stair_slade_block, param2 = 3 }
	n4 = { name = df_dependencies.node_name_stair_slade_block, param2 = 1 }
	n7 = { name = df_dependencies.node_name_stair_slade_block, param2 = 2 }
	n9 = { name = df_dependencies.node_name_stair_slade_block }
	n11 = { name = df_dependencies.node_name_slab_slade_block, param2 = 1 }
	n10 = { name = df_dependencies.node_name_slab_slade_block, param2 = 21 }

	if df_dependencies.node_name_slab_slade_block_top then
	-- Mineclone slabs don't support full rotation, this is how to flip them over
		n10.name = df_dependencies.node_name_slab_slade_block_top
		n10.param2 = 1
	end
end

df_underworld_items.seal_temple_schem = {
	size = {y = 6, x = 7, z = 7},
	data = {
		n15, n2, n3, n3, n3, n4, n14, n14, n3, n3, n3, n3, n3, n15, n13, n3, n3,
		n3, n3, n3, n13, n16, n3, n3, n3, n3, n3, n16, n6, n6, n6, n6, n6, n6,
		n6, n6, n6, n6, n6, n6, n6, n6, n7, n3, n3, n3, n3, n3, n7, n3, n3,
		n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3,
		n3, n3, n6, n3, n3, n3, n3, n3, n6, n6, n6, n6, n6, n6, n6, n6, n3,
		n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3,
		n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n6, n3, n3, n3, n3, n3, n6,
		n6, n6, n3, n3, n3, n6, n6, n3, n3, n3, n8, n3, n3, n3, n3, n3, n3,
		n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3,
		n3, n6, n3, n3, n3, n3, n3, n6, n6, n6, n3, n3, n3, n6, n6, n3, n3,
		n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3,
		n3, n3, n3, n3, n3, n3, n3, n3, n3, n6, n3, n3, n3, n3, n3, n6, n6,
		n6, n3, n3, n3, n6, n6, n9, n3, n3, n3, n3, n3, n9, n3, n3, n3, n3,
		n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3, n3,
		n6, n3, n3, n3, n3, n3, n6, n6, n6, n6, n6, n6, n6, n6, n12, n2, n3,
		n3, n3, n4, n12, n14, n3, n3, n3, n3, n3, n15, n13, n3, n3, n3, n3, n3,
		n12, n16, n3, n3, n3, n3, n3, n16, n6, n6, n6, n6, n6, n6, n6, n6, n6,
		n6, n6, n6, n6, n6,
	}
}

df_underworld_items.seal_stair_schem = {
	size = {y = 2, x = 7, z = 7},
	data = {
		n1, n1, n1, n1, n1, n1, n1, n1, n1, n1, n1, n1, n1, n1, n1, n1, n10,
		n3, n3, n3, n1, n1, n3, n11, n1, n10, n3, n1, n1, n11, n3, n3, n3, n3,
		n1, n1, n3, n3, n3, n3, n3, n1, n1, n3, n3, n3, n3, n3, n1, n1, n3,
		n3, n3, n3, n3, n1, n1, n3, n3, n3, n3, n3, n1, n1, n3, n3, n3, n3,
		n3, n1, n1, n3, n3, n3, n3, n3, n1, n1, n3, n3, n3, n3, n3, n1, n1,
		n1, n1, n1, n1, n1, n1, n1, n1, n1, n1, n1, n1, n1,
	}
}


code:
dfcaverns_invulnerable_slade (Slade cannot be damaged) bool true
dfcaverns_destructive_pit_plasma (Pit plasma destroys adjacent nodes) bool true
dfcaverns_underworld_hunter_statues (Enable hunter statues in the underworld) bool true
dfcaverns_enable_slade_drill (Enable slade drills) bool true


code:
local S = minetest.get_translator(minetest.get_current_modname())

local invulnerable = df_underworld_items.config.invulnerable_slade and not minetest.settings:get_bool("creative_mode")

local server_diggable_only = function(pos, player)
	if player then
		return minetest.check_player_privs(player, "server")
	end
	return false
end

local add_immortality = function(slade_def)
	slade_def.groups.immortal = 1
	slade_def.can_dig = server_diggable_only
	return slade_def
end

local slade_groups = {cracky=3, stone=1, slade=1, pit_plasma_resistant=1, mese_radiation_shield=1,creative_breakable=1, building_block=1, material_stone=1}

local slade_def = {
	description = S("Slade"),
	_doc_items_longdesc = df_underworld_items.doc.slade_desc,
	_doc_items_usagehelp = df_underworld_items.doc.slade_usage,
	tiles = {"dfcaverns_slade.png"},
	groups = slade_groups,
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	is_ground_content = false,
	_mcl_blast_resistance = 8,
	_mcl_hardness = 5,
	on_blast = function(pos, intensity)
		if intensity >= 1.0 then
			minetest.set_node(pos, {name="df_underworld_items:slade_sand"})
			minetest.check_for_falling(pos)
		end
	end,
}
if invulnerable then
	add_immortality(slade_def)
	slade_def._mcl_blast_resistance = 8
	slade_def._mcl_hardness = 5
end
minetest.register_node("df_underworld_items:slade", slade_def)

local slade_brick_def = {
	description = S("Slade Brick"),
	_doc_items_longdesc = df_underworld_items.doc.slade_desc,
	_doc_items_usagehelp = df_underworld_items.doc.slade_usage,
	tiles = {"dfcaverns_slade_brick.png"},
	groups = slade_groups,
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	is_ground_content = false,
	_mcl_blast_resistance = 8,
	_mcl_hardness = 5,
}
if invulnerable then
	add_immortality(slade_brick_def)
end
minetest.register_node("df_underworld_items:slade_brick", slade_brick_def)

-- can't use "wall=1" because MCL has special handling for nodes in that group that explodes if it tries handling this one.
-- fortunately minetest_game walls also connect to group fence, so this should be fine.
local slade_wall_groups = {fence=1}
for key, val in pairs(slade_groups) do
	slade_wall_groups[key]=val
end
local slade_wall_def = {
	description = S("Slade Wall"),
	drawtype = "nodebox",
	node_box = {
		type = "connected",
		fixed = {{-1/4, -1/2, -1/4, 1/4, 1/2, 1/4}},
		-- connect_bottom =
		connect_front = {{-3/16, -1/2, -1/2,  3/16, 3/8, -1/4}},
		connect_left = {{-1/2, -1/2, -3/16, -1/4, 3/8,  3/16}},
		connect_back = {{-3/16, -1/2,  1/4,  3/16, 3/8,  1/2}},
		connect_right = {{ 1/4, -1/2, -3/16,  1/2, 3/8,  3/16}},
	},
	connects_to = { "group:wall", "group:stone", "group:fence" },
	paramtype = "light",
	tiles = {"dfcaverns_slade_brick.png"},
	walkable = true,
	is_ground_content = false,
	groups = slade_wall_groups,
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	_mcl_blast_resistance = 8,
	_mcl_hardness = 5,
}
if invulnerable then
	add_immortality(slade_wall_def)
end
minetest.register_node("df_underworld_items:slade_wall", slade_wall_def)

minetest.register_node("df_underworld_items:slade_sand", {
	description = S("Slade Sand"),
	_doc_items_longdesc = df_underworld_items.doc.slade_desc,
	_doc_items_usagehelp = df_underworld_items.doc.slade_usage,
	tiles = {"dfcaverns_slade_sand.png"},
	is_ground_content = false,
	groups = {crumbly = 3, falling_node = 1, slade=1, pit_plasma_resistant=1, mese_radiation_shield=1,handy=1,shovely=3, sand=1, enderman_takable=1, building_block=1, material_sand=1, crush_after_fall=1, falling_node_damage=1},
	sounds = df_dependencies.sound_gravel({footstep = {name = df_dependencies.soundfile_gravel_footstep, gain = 0.45},}),
	_mcl_blast_resistance = 8,
	_mcl_hardness = 3,
})

local slade_block_def = {
	description = S("Slade Block"),
	_doc_items_longdesc = df_underworld_items.doc.slade_desc,
	_doc_items_usagehelp = df_underworld_items.doc.slade_usage,
	tiles = {"dfcaverns_slade_block.png"},
	groups = slade_groups,
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	is_ground_content = false,
	_mcl_blast_resistance = 1200,
	_mcl_hardness = 50,
}
if invulnerable then
	add_immortality(slade_block_def)
	slade_block_def.on_blast = function() end
end
minetest.register_node("df_underworld_items:slade_block", slade_block_def)

local slade_seal_def = {
	description = S("Slade Seal"),
	_doc_items_longdesc = df_underworld_items.doc.slade_seal_desc,
	_doc_items_usagehelp = df_underworld_items.doc.slade_usage,
	tiles = {"dfcaverns_slade_block.png^dfcaverns_seal.png", "dfcaverns_slade_block.png"},
	groups = slade_groups,
	sounds = df_dependencies.sound_stone({ footstep = { name = "bedrock2_step", gain = 1 } }),
	is_ground_content = false,
	_mcl_blast_resistance = 1200,
	_mcl_hardness = 50,
}
if invulnerable then
	slade_seal_def.on_blast = function() end
	add_immortality(slade_seal_def)
end
minetest.register_node("df_underworld_items:slade_seal", slade_seal_def)

minetest.register_craft({
	type = "shapeless",
	output = "df_underworld_items:slade_brick",
	recipe = {
		"df_underworld_items:slade_sand",
		df_dependencies.node_name_bucket_lava,
	},
	replacements = {{df_dependencies.node_name_bucket_lava, df_dependencies.node_name_bucket_empty}},
})

minetest.register_abm{
	label = "slade seal scratching",
	nodenames = {"df_underworld_items:slade_seal"},
	interval = 10,
	chance = 20,
	catch_up = false,
	action = function(pos)
		minetest.sound_play({name="dfcaverns_grinding_stone"},
		{
			pos = pos,
			gain = 0.05,
			max_hear_distance = 32,
		}
	)
	end,
}

-- Register stair and slab

df_dependencies.register_stairs("slade_brick")

if invulnerable then
	for name, def in pairs(minetest.registered_nodes) do
		if (name:sub(1,7) == "stairs:" and name:sub(-11) == "slade_block") or
			name:sub(1,11) == "mcl_stairs:" and name:sub(-11) == "slade_brick" then
				minetest.override_item(name, {can_dig = server_diggable_only})
		end
	end
end

if minetest.get_modpath("mesecons_mvps") and df_underworld_items.config.invulnerable_slade then
	mesecon.register_mvps_stopper("df_underworld_items:slade")
end