code:
-- default/torch.lua

-- support for MT game translation.
local S = default.get_translator

local function on_flood(pos, oldnode, newnode)
	minetest.add_item(pos, ItemStack("default:torch 1"))
	-- Play flame-extinguish sound if liquid is not an 'igniter'
	local nodedef = minetest.registered_items[newnode.name]
	if not (nodedef and nodedef.groups and
			nodedef.groups.igniter and nodedef.groups.igniter > 0) then
		minetest.sound_play(
			"default_cool_lava",
			{pos = pos, max_hear_distance = 16, gain = 0.1},
			true
		)
	end
	-- Remove the torch node
	return false
end

minetest.register_node("default:torch", {
	description = S("Torch"),
	drawtype = "mesh",
	mesh = "torch_floor.obj",
	inventory_image = "default_torch_on_floor.png",
	wield_image = "default_torch_on_floor.png",
	tiles = {{
		    name = "default_torch_on_floor_animated.png",
		    animation = {type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.3}
	}},
	paramtype = "light",
	paramtype2 = "wallmounted",
	sunlight_propagates = true,
	walkable = false,
	liquids_pointable = false,
	light_source = 12,
	groups = {choppy=2, dig_immediate=3, flammable=1, attached_node=1, torch=1},
	drop = "default:torch",
	selection_box = {
		type = "wallmounted",
		wall_bottom = {-1/8, -1/2, -1/8, 1/8, 2/16, 1/8},
	},
	sounds = default.node_sound_wood_defaults(),
	on_place = function(itemstack, placer, pointed_thing)
		local under = pointed_thing.under
		local node = minetest.get_node(under)
		local def = minetest.registered_nodes[node.name]
		if def and def.on_rightclick and
			not (placer and placer:is_player() and
			placer:get_player_control().sneak) then
			return def.on_rightclick(under, node, placer, itemstack,
				pointed_thing) or itemstack
		end

		local above = pointed_thing.above
		local wdir = minetest.dir_to_wallmounted(vector.subtract(under, above))
		local fakestack = itemstack
		if wdir == 0 then
			fakestack:set_name("default:torch_ceiling")
		elseif wdir == 1 then
			fakestack:set_name("default:torch")
		else
			fakestack:set_name("default:torch_wall")
		end

		itemstack = minetest.item_place(fakestack, placer, pointed_thing, wdir)
		itemstack:set_name("default:torch")

		return itemstack
	end,
	floodable = true,
	on_flood = on_flood,
	on_rotate = false
})

minetest.register_node("default:torch_wall", {
	drawtype = "mesh",
	mesh = "torch_wall.obj",
	tiles = {{
		    name = "default_torch_on_floor_animated.png",
		    animation = {type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.3}
	}},
	paramtype = "light",
	paramtype2 = "wallmounted",
	sunlight_propagates = true,
	walkable = false,
	light_source = 12,
	groups = {choppy=2, dig_immediate=3, flammable=1, not_in_creative_inventory=1, attached_node=1, torch=1},
	drop = "default:torch",
	selection_box = {
		type = "wallmounted",
		wall_side = {-1/2, -1/2, -1/8, -1/8, 1/8, 1/8},
	},
	sounds = default.node_sound_wood_defaults(),
	floodable = true,
	on_flood = on_flood,
	on_rotate = false
})

minetest.register_node("default:torch_ceiling", {
	drawtype = "mesh",
	mesh = "torch_ceiling.obj",
	tiles = {{
		    name = "default_torch_on_floor_animated.png",
		    animation = {type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.3}
	}},
	paramtype = "light",
	paramtype2 = "wallmounted",
	sunlight_propagates = true,
	walkable = false,
	light_source = 12,
	groups = {choppy=2, dig_immediate=3, flammable=1, not_in_creative_inventory=1, attached_node=1, torch=1},
	drop = "default:torch",
	selection_box = {
		type = "wallmounted",
		wall_top = {-1/8, -1/16, -5/16, 1/8, 1/2, 1/8},
	},
	sounds = default.node_sound_wood_defaults(),
	floodable = true,
	on_flood = on_flood,
	on_rotate = false
})

minetest.register_lbm({
	name = "default:3dtorch",
	nodenames = {"default:torch", "torches:floor", "torches:wall"},
	action = function(pos, node)
		if node.param2 == 0 then
			minetest.set_node(pos, {name = "default:torch_ceiling",
				param2 = node.param2})
		elseif node.param2 == 1 then
			minetest.set_node(pos, {name = "default:torch",
				param2 = node.param2})
		else
			minetest.set_node(pos, {name = "default:torch_wall",
				param2 = node.param2})
		end
	end
})

minetest.register_craft({
	output = "default:torch 4",
	recipe = {
		{"default:coal_lump"},
		{"group:stick"},
	}
})

minetest.register_craft({
	type = "fuel",
	recipe = "default:torch",
	burntime = 4,
})


code:
-- default/trees.lua

-- support for MT game translation.
local S = default.get_translator

local random = math.random

--
-- Grow trees from saplings
--

-- 'can grow' function

function default.can_grow(pos)
	local node_under = minetest.get_node_or_nil({x = pos.x, y = pos.y - 1, z = pos.z})
	if not node_under then
		return false
	end
	if minetest.get_item_group(node_under.name, "soil") == 0 then
		return false
	end
	local light_level = minetest.get_node_light(pos)
	if not light_level or light_level < 13 then
		return false
	end
	return true
end

-- 'is snow nearby' function

local function is_snow_nearby(pos)
	return minetest.find_node_near(pos, 1, {"group:snowy"})
end

-- Grow sapling

function default.grow_sapling(pos)
	if not default.can_grow(pos) then
		-- try again 5 min later
		minetest.get_node_timer(pos):start(300)
		return
	end

	local mg_name = minetest.get_mapgen_setting("mg_name")
	local node = minetest.get_node(pos)
	if node.name == "default:sapling" then
		minetest.log("action", "A sapling grows into a tree at "..
			minetest.pos_to_string(pos))
		if mg_name == "v6" then
			default.grow_tree(pos, random(1, 4) == 1)
		else
			default.grow_new_apple_tree(pos)
		end
	elseif node.name == "default:junglesapling" then
		minetest.log("action", "A jungle sapling grows into a tree at "..
			minetest.pos_to_string(pos))
		if mg_name == "v6" then
			default.grow_jungle_tree(pos)
		else
			default.grow_new_jungle_tree(pos)
		end
	elseif node.name == "default:pine_sapling" then
		minetest.log("action", "A pine sapling grows into a tree at "..
			minetest.pos_to_string(pos))
		local snow = is_snow_nearby(pos)
		if mg_name == "v6" then
			default.grow_pine_tree(pos, snow)
		elseif snow then
			default.grow_new_snowy_pine_tree(pos)
		else
			default.grow_new_pine_tree(pos)
		end
	elseif node.name == "default:acacia_sapling" then
		minetest.log("action", "An acacia sapling grows into a tree at "..
			minetest.pos_to_string(pos))
		default.grow_new_acacia_tree(pos)
	elseif node.name == "default:aspen_sapling" then
		minetest.log("action", "An aspen sapling grows into a tree at "..
			minetest.pos_to_string(pos))
		default.grow_new_aspen_tree(pos)
	elseif node.name == "default:bush_sapling" then
		minetest.log("action", "A bush sapling grows into a bush at "..
			minetest.pos_to_string(pos))
		default.grow_bush(pos)
	elseif node.name == "default:blueberry_bush_sapling" then
		minetest.log("action", "A blueberry bush sapling grows into a bush at "..
			minetest.pos_to_string(pos))
		default.grow_blueberry_bush(pos)
	elseif node.name == "default:acacia_bush_sapling" then
		minetest.log("action", "An acacia bush sapling grows into a bush at "..
			minetest.pos_to_string(pos))
		default.grow_acacia_bush(pos)
	elseif node.name == "default:pine_bush_sapling" then
		minetest.log("action", "A pine bush sapling grows into a bush at "..
			minetest.pos_to_string(pos))
		default.grow_pine_bush(pos)
	elseif node.name == "default:emergent_jungle_sapling" then
		minetest.log("action", "An emergent jungle sapling grows into a tree at "..
			minetest.pos_to_string(pos))
		default.grow_new_emergent_jungle_tree(pos)
	end
end

minetest.register_lbm({
	name = "default:convert_saplings_to_node_timer",
	nodenames = {"default:sapling", "default:junglesapling",
			"default:pine_sapling", "default:acacia_sapling",
			"default:aspen_sapling"},
	action = function(pos)
		minetest.get_node_timer(pos):start(math.random(300, 1500))
	end
})

--
-- Tree generation
--

-- Apple tree and jungle tree trunk and leaves function

local function add_trunk_and_leaves(data, a, pos, tree_cid, leaves_cid,
		height, size, iters, is_apple_tree)
	local x, y, z = pos.x, pos.y, pos.z
	local c_air = minetest.get_content_id("air")
	local c_ignore = minetest.get_content_id("ignore")
	local c_apple = minetest.get_content_id("default:apple")

	-- Trunk
	data[a:index(x, y, z)] = tree_cid -- Force-place lowest trunk node to replace sapling
	for yy = y + 1, y + height - 1 do
		local vi = a:index(x, yy, z)
		local node_id = data[vi]
		if node_id == c_air or node_id == c_ignore or node_id == leaves_cid then
			data[vi] = tree_cid
		end
	end

	-- Force leaves near the trunk
	for z_dist = -1, 1 do
	for y_dist = -size, 1 do
		local vi = a:index(x - 1, y + height + y_dist, z + z_dist)
		for x_dist = -1, 1 do
			if data[vi] == c_air or data[vi] == c_ignore then
				if is_apple_tree and random(1, 8) == 1 then
					data[vi] = c_apple
				else
					data[vi] = leaves_cid
				end
			end
			vi = vi + 1
		end
	end
	end

	-- Randomly add leaves in 2x2x2 clusters.
	for i = 1, iters do
		local clust_x = x + random(-size, size - 1)
		local clust_y = y + height + random(-size, 0)
		local clust_z = z + random(-size, size - 1)

		for xi = 0, 1 do
		for yi = 0, 1 do
		for zi = 0, 1 do
			local vi = a:index(clust_x + xi, clust_y + yi, clust_z + zi)
			if data[vi] == c_air or data[vi] == c_ignore then
				if is_apple_tree and random(1, 8) == 1 then
					data[vi] = c_apple
				else
					data[vi] = leaves_cid
				end
			end
		end
		end
		end
	end
end

-- Apple tree

function default.grow_tree(pos, is_apple_tree, bad)
	--[[
		NOTE: Tree-placing code is currently duplicated in the engine
		and in games that have saplings; both are deprecated but not
		replaced yet
	--]]
	if bad then
		error("Deprecated use of default.grow_tree")
	end

	local x, y, z = pos.x, pos.y, pos.z
	local height = random(4, 5)
	local c_tree = minetest.get_content_id("default:tree")
	local c_leaves = minetest.get_content_id("default:leaves")

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

	add_trunk_and_leaves(data, a, pos, c_tree, c_leaves, height, 2, 8, is_apple_tree)

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

-- Jungle tree

function default.grow_jungle_tree(pos, bad)
	--[[
		NOTE: Jungletree-placing code is currently duplicated in the engine
		and in games that have saplings; both are deprecated but not
		replaced yet
	--]]
	if bad then
		error("Deprecated use of default.grow_jungle_tree")
	end

	local x, y, z = pos.x, pos.y, pos.z
	local height = random(8, 12)
	local c_air = minetest.get_content_id("air")
	local c_ignore = minetest.get_content_id("ignore")
	local c_jungletree = minetest.get_content_id("default:jungletree")
	local c_jungleleaves = minetest.get_content_id("default:jungleleaves")

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

	add_trunk_and_leaves(data, a, pos, c_jungletree, c_jungleleaves,
		height, 3, 30, false)

	-- Roots
	for z_dist = -1, 1 do
		local vi_1 = a:index(x - 1, y - 1, z + z_dist)
		local vi_2 = a:index(x - 1, y, z + z_dist)
		for x_dist = -1, 1 do
			if random(1, 3) >= 2 then
				if data[vi_1] == c_air or data[vi_1] == c_ignore then
					data[vi_1] = c_jungletree
				elseif data[vi_2] == c_air or data[vi_2] == c_ignore then
					data[vi_2] = c_jungletree
				end
			end
			vi_1 = vi_1 + 1
			vi_2 = vi_2 + 1
		end
	end

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

-- Pine tree from mg mapgen mod, design by sfan5, pointy top added by paramat

local function add_pine_needles(data, vi, c_air, c_ignore, c_snow, c_pine_needles)
	local node_id = data[vi]
	if node_id == c_air or node_id == c_ignore or node_id == c_snow then
		data[vi] = c_pine_needles
	end
end

local function add_snow(data, vi, c_air, c_ignore, c_snow)
	local node_id = data[vi]
	if node_id == c_air or node_id == c_ignore then
		data[vi] = c_snow
	end
end

function default.grow_pine_tree(pos, snow)
	local x, y, z = pos.x, pos.y, pos.z
	local maxy = y + random(9, 13) -- Trunk top

	local c_air = minetest.get_content_id("air")
	local c_ignore = minetest.get_content_id("ignore")
	local c_pine_tree = minetest.get_content_id("default:pine_tree")
	local c_pine_needles  = minetest.get_content_id("default:pine_needles")
	local c_snow = minetest.get_content_id("default:snow")

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

	-- Upper branches layer
	local dev = 3
	for yy = maxy - 1, maxy + 1 do
		for zz = z - dev, z + dev do
			local vi = a:index(x - dev, yy, zz)
			local via = a:index(x - dev, yy + 1, zz)
			for xx = x - dev, x + dev do
				if random() < 0.95 - dev * 0.05 then
					add_pine_needles(data, vi, c_air, c_ignore, c_snow,
						c_pine_needles)
					if snow then
						add_snow(data, via, c_air, c_ignore, c_snow)
					end
				end
				vi  = vi + 1
				via = via + 1
			end
		end
		dev = dev - 1
	end

	-- Centre top nodes
	add_pine_needles(data, a:index(x, maxy + 1, z), c_air, c_ignore, c_snow,
		c_pine_needles)
	add_pine_needles(data, a:index(x, maxy + 2, z), c_air, c_ignore, c_snow,
		c_pine_needles) -- Paramat added a pointy top node
	if snow then
		add_snow(data, a:index(x, maxy + 3, z), c_air, c_ignore, c_snow)
	end

	-- Lower branches layer
	local my = 0
	for i = 1, 20 do -- Random 2x2 squares of needles
		local xi = x + random(-3, 2)
		local yy = maxy + random(-6, -5)
		local zi = z + random(-3, 2)
		if yy > my then
			my = yy
		end
		for zz = zi, zi+1 do
			local vi = a:index(xi, yy, zz)
			local via = a:index(xi, yy + 1, zz)
			for xx = xi, xi + 1 do
				add_pine_needles(data, vi, c_air, c_ignore, c_snow,
					c_pine_needles)
				if snow then
					add_snow(data, via, c_air, c_ignore, c_snow)
				end
				vi  = vi + 1
				via = via + 1
			end
		end
	end

	dev = 2
	for yy = my + 1, my + 2 do
		for zz = z - dev, z + dev do
			local vi = a:index(x - dev, yy, zz)
			local via = a:index(x - dev, yy + 1, zz)
			for xx = x - dev, x + dev do
				if random() < 0.95 - dev * 0.05 then
					add_pine_needles(data, vi, c_air, c_ignore, c_snow,
						c_pine_needles)
					if snow then
						add_snow(data, via, c_air, c_ignore, c_snow)
					end
				end
				vi  = vi + 1
				via = via + 1
			end
		end
		dev = dev - 1
	end

	-- Trunk
	-- Force-place lowest trunk node to replace sapling
	data[a:index(x, y, z)] = c_pine_tree
	for yy = y + 1, maxy do
		local vi = a:index(x, yy, z)
		local node_id = data[vi]
		if node_id == c_air or node_id == c_ignore or
				node_id == c_pine_needles or node_id == c_snow then
			data[vi] = c_pine_tree
		end
	end

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

-- New apple tree

function default.grow_new_apple_tree(pos)
	local path = minetest.get_modpath("default") ..
		"/schematics/apple_tree_from_sapling.mts"
	minetest.place_schematic({x = pos.x - 3, y = pos.y - 1, z = pos.z - 3},
		path, "random", nil, false)
end

-- New jungle tree

function default.grow_new_jungle_tree(pos)
	local path = minetest.get_modpath("default") ..
		"/schematics/jungle_tree_from_sapling.mts"
	minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
		path, "random", nil, false)
end

-- New emergent jungle tree

function default.grow_new_emergent_jungle_tree(pos)
	local path = minetest.get_modpath("default") ..
		"/schematics/emergent_jungle_tree_from_sapling.mts"
	minetest.place_schematic({x = pos.x - 3, y = pos.y - 5, z = pos.z - 3},
		path, "random", nil, false)
end

-- New pine tree

function default.grow_new_pine_tree(pos)
	local path
	if math.random() > 0.5 then
		path = minetest.get_modpath("default") ..
			"/schematics/pine_tree_from_sapling.mts"
	else
		path = minetest.get_modpath("default") ..
			"/schematics/small_pine_tree_from_sapling.mts"
	end
	minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
		path, "0", nil, false)
end

-- New snowy pine tree

function default.grow_new_snowy_pine_tree(pos)
	local path
	if math.random() > 0.5 then
		path = minetest.get_modpath("default") ..
			"/schematics/snowy_pine_tree_from_sapling.mts"
	else
		path = minetest.get_modpath("default") ..
			"/schematics/snowy_small_pine_tree_from_sapling.mts"
	end
	minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
		path, "random", nil, false)
end

-- New acacia tree

function default.grow_new_acacia_tree(pos)
	local path = minetest.get_modpath("default") ..
		"/schematics/acacia_tree_from_sapling.mts"
	minetest.place_schematic({x = pos.x - 4, y = pos.y - 1, z = pos.z - 4},
		path, "random", nil, false)
end

-- New aspen tree

function default.grow_new_aspen_tree(pos)
	local path = minetest.get_modpath("default") ..
		"/schematics/aspen_tree_from_sapling.mts"
	minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
		path, "0", nil, false)
end

-- Bushes do not need 'from sapling' schematic variants because
-- only the stem node is force-placed in the schematic.

-- Bush

function default.grow_bush(pos)
	local path = minetest.get_modpath("default") ..
		"/schematics/bush.mts"
	minetest.place_schematic({x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
		path, "0", nil, false)
end

-- Blueberry bush

function default.grow_blueberry_bush(pos)
	local path = minetest.get_modpath("default") ..
		"/schematics/blueberry_bush.mts"
	minetest.place_schematic({x = pos.x - 1, y = pos.y, z = pos.z - 1},
		path, "0", nil, false)
end

-- Acacia bush

function default.grow_acacia_bush(pos)
	local path = minetest.get_modpath("default") ..
		"/schematics/acacia_bush.mts"
	minetest.place_schematic({x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
		path, "0", nil, false)
end

-- Pine bush

function default.grow_pine_bush(pos)
	local path = minetest.get_modpath("default") ..
		"/schematics/pine_bush.mts"
	minetest.place_schematic({x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
		path, "0", nil, false)
end

-- Large cactus

function default.grow_large_cactus(pos)
	local path = minetest.get_modpath("default") ..
		"/schematics/large_cactus.mts"
	minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
		path, "random", nil, false)
end

--
-- Sapling 'on place' function to check protection of node and resulting tree volume
--

function default.sapling_on_place(itemstack, placer, pointed_thing,
		sapling_name, minp_relative, maxp_relative, interval)
	-- Position of sapling
	local pos = pointed_thing.under
	local node = minetest.get_node_or_nil(pos)
	local pdef = node and minetest.registered_nodes[node.name]

	if pdef and pdef.on_rightclick and
			not (placer and placer:is_player() and
			placer:get_player_control().sneak) then
		return pdef.on_rightclick(pos, node, placer, itemstack, pointed_thing)
	end

	if not pdef or not pdef.buildable_to then
		pos = pointed_thing.above
		node = minetest.get_node_or_nil(pos)
		pdef = node and minetest.registered_nodes[node.name]
		if not pdef or not pdef.buildable_to then
			return itemstack
		end
	end

	local player_name = placer and placer:get_player_name() or ""
	-- Check sapling position for protection
	if minetest.is_protected(pos, player_name) then
		minetest.record_protection_violation(pos, player_name)
		return itemstack
	end
	-- Check tree volume for protection
	if minetest.is_area_protected(
			vector.add(pos, minp_relative),
			vector.add(pos, maxp_relative),
			player_name,
			interval) then
		minetest.record_protection_violation(pos, player_name)
		-- Print extra information to explain
--		minetest.chat_send_player(player_name,
--			itemstack:get_definition().description .. " will intersect protection " ..
--			"on growth")
		minetest.chat_send_player(player_name,
		    S("@1 will intersect protection on growth.",
			itemstack:get_definition().description))
		return itemstack
	end

	minetest.log("action", player_name .. " places node "
			.. sapling_name .. " at " .. minetest.pos_to_string(pos))

	local take_item = not (creative and creative.is_enabled_for
		and creative.is_enabled_for(player_name))
	local newnode = {name = sapling_name}
	local ndef = minetest.registered_nodes[sapling_name]
	minetest.set_node(pos, newnode)

	-- Run callback
	if ndef and ndef.after_place_node then
		-- Deepcopy place_to and pointed_thing because callback can modify it
		if ndef.after_place_node(table.copy(pos), placer,
				itemstack, table.copy(pointed_thing)) then
			take_item = false
		end
	end

	-- Run script hook
	for _, callback in ipairs(minetest.registered_on_placenodes) do
		-- Deepcopy pos, node and pointed_thing because callback can modify them
		if callback(table.copy(pos), table.copy(newnode),
				placer, table.copy(node or {}),
				itemstack, table.copy(pointed_thing)) then
			take_item = false
		end
	end

	if take_item then
		itemstack:take_item()
	end

	return itemstack
end

repo_name: Desour/PlayMinetestServer

license: mit


code:
Minetest Game mod: doors
========================
See license.txt for license information.

Authors of source code
----------------------
Originally by PilzAdam (MIT)

Modified by BlockMen (MIT): Added sounds, glass doors (glass, obsidian glass) and trapdoor.

Modified by sofar (sofar@foo-projects.org) (MIT):
Added Steel trapdoor.
Re-implemented most of the door algorithms, added meshes, UV wrapped texture.
Added doors API to facilitate coding mods accessing and operating doors.
Added Fence Gate model, code, and sounds.

Various Minetest developers and contributors (MIT)

Authors of media (textures)
---------------------------
Following textures created by Fernando Zapata (CC BY-SA 3.0):
  door_wood.png
  door_wood_a.png
  door_wood_a_r.png
  door_wood_b.png
  door_wood_b_r.png

Following textures created by BlockMen (CC BY-SA 3.0):
  door_trapdoor.png
  door_obsidian_glass_side.png

Following textures created by celeron55 (CC BY-SA 3.0):
  door_glass_a.png
  door_glass_b.png

Following textures created by PenguinDad (CC BY-SA 4.0):
  door_glass.png
  door_obsidian_glass.png

Following textures created by sofar (CC-BY-SA-3.0):
  doors_trapdoor_steel.png

Following textures created by paramat (CC-BY-SA-3.0):
  door_trapdoor_side.png
  doors_trapdoor_steel_side.png

Obsidian door textures by red-001 based on textures by Pilzadam and BlockMen (CC BY-SA 3.0):
  door_obsidian_glass.png

Glass door textures by Krock and paramat based on textures by VanessaE (CC BY-SA 3.0):
  doors_door_glass.png
  doors_item_glass.png

All other textures (created by PilzAdam) (CC BY-SA 3.0):

Door textures were converted to the new texture map by sofar, paramat and
red-001, under the same license as the originals.

Authors of media (models)
-------------------------
Door 3d models by sofar (CC-BY-SA-3.0)
 - door_a.obj
 - door_b.obj
Fence gate models by sofar (CC-BY-SA-3.0)
 - fencegate_open.obj
 - fencegate_closed.obj

Authors of media (sounds)
-------------------------
Opening-Sound created by CGEffex (CC BY 3.0), modified by BlockMen
  door_open.ogg
Closing-Sound created by bennstir (CC BY 3.0)
  door_close.ogg
fencegate_open.ogg:
  http://www.freesound.org/people/mhtaylor67/sounds/126041/ - (CC0 1.0)
fencegate_close.ogg:
  http://www.freesound.org/people/BarkersPinhead/sounds/274807/ - (CC-BY-3.0)
  http://www.freesound.org/people/rivernile7/sounds/249573/ - (CC-BY-3.0)
Steel door sounds open & close (CC-BY-3.0) by HazMatt
  - http://www.freesound.org/people/HazMattt/sounds/187283/
  doors_steel_door_open.ogg
  doors_steel_door_close.ogg
doors_glass_door_open.ogg, doors_glass_door_close.ogg:
  https://www.freesound.org/people/SkeetMasterFunk69/sounds/235546/ (CC0 1.0)


code:
-- doors/init.lua

-- our API object
doors = {}

doors.registered_doors = {}
doors.registered_trapdoors = {}

-- Load support for MT game translation.
local S = minetest.get_translator("doors")

local function replace_old_owner_information(pos)
	local meta = minetest.get_meta(pos)
	local owner = meta:get_string("doors_owner")
	if owner and owner ~= "" then
		meta:set_string("owner", owner)
		meta:set_string("doors_owner", "")
	end
end

-- returns an object to a door object or nil
function doors.get(pos)
	local node_name = minetest.get_node(pos).name
	if doors.registered_doors[node_name] then
		-- A normal upright door
		return {
			pos = pos,
			open = function(self, player)
				if self:state() then
					return false
				end
				return doors.door_toggle(self.pos, nil, player)
			end,
			close = function(self, player)
				if not self:state() then
					return false
				end
				return doors.door_toggle(self.pos, nil, player)
			end,
			toggle = function(self, player)
				return doors.door_toggle(self.pos, nil, player)
			end,
			state = function(self)
				local state = minetest.get_meta(self.pos):get_int("state")
				return state %2 == 1
			end
		}
	elseif doors.registered_trapdoors[node_name] then
		-- A trapdoor
		return {
			pos = pos,
			open = function(self, player)
				if self:state() then
					return false
				end
				return doors.trapdoor_toggle(self.pos, nil, player)
			end,
			close = function(self, player)
				if not self:state() then
					return false
				end
				return doors.trapdoor_toggle(self.pos, nil, player)
			end,
			toggle = function(self, player)
				return doors.trapdoor_toggle(self.pos, nil, player)
			end,
			state = function(self)
				return minetest.get_node(self.pos).name:sub(-5) == "_open"
			end
		}
	else
		return nil
	end
end

-- this hidden node is placed on top of the bottom, and prevents
-- nodes from being placed in the top half of the door.
minetest.register_node("doors:hidden", {
	description = S("Hidden Door Segment"),
	drawtype = "airlike",
	paramtype = "light",
	paramtype2 = "facedir",
	sunlight_propagates = true,
	-- has to be walkable for falling nodes to stop falling.
	walkable = true,
	pointable = false,
	diggable = false,
	buildable_to = false,
	floodable = false,
	drop = "",
	groups = {not_in_creative_inventory = 1},
	on_blast = function() end,
	-- 1px block inside door hinge near node top
	collision_box = {
		type = "fixed",
		fixed = {-15/32, 13/32, -15/32, -13/32, 1/2, -13/32},
	},
})

-- table used to aid door opening/closing
local transform = {
	{
		{v = "_a", param2 = 3},
		{v = "_a", param2 = 0},
		{v = "_a", param2 = 1},
		{v = "_a", param2 = 2},
	},
	{
		{v = "_c", param2 = 1},
		{v = "_c", param2 = 2},
		{v = "_c", param2 = 3},
		{v = "_c", param2 = 0},
	},
	{
		{v = "_b", param2 = 1},
		{v = "_b", param2 = 2},
		{v = "_b", param2 = 3},
		{v = "_b", param2 = 0},
	},
	{
		{v = "_d", param2 = 3},
		{v = "_d", param2 = 0},
		{v = "_d", param2 = 1},
		{v = "_d", param2 = 2},
	},
}

function doors.door_toggle(pos, node, clicker)
	local meta = minetest.get_meta(pos)
	node = node or minetest.get_node(pos)
	local def = minetest.registered_nodes[node.name]
	local name = def.door.name

	local state = meta:get_string("state")
	if state == "" then
		-- fix up lvm-placed right-hinged doors, default closed
		if node.name:sub(-2) == "_b" then
			state = 2
		else
			state = 0
		end
	else
		state = tonumber(state)
	end

	replace_old_owner_information(pos)

	if clicker and not default.can_interact_with_node(clicker, pos) then
		return false
	end

	-- until Lua-5.2 we have no bitwise operators :(
	if state % 2 == 1 then
		state = state - 1
	else
		state = state + 1
	end

	local dir = node.param2

	-- It's possible param2 is messed up, so, validate before using
	-- the input data. This indicates something may have rotated
	-- the door, even though that is not supported.
	if not transform[state + 1] or not transform[state + 1][dir + 1] then
		return false
	end

	if state % 2 == 0 then
		minetest.sound_play(def.door.sounds[1],
			{pos = pos, gain = 0.3, max_hear_distance = 10}, true)
	else
		minetest.sound_play(def.door.sounds[2],
			{pos = pos, gain = 0.3, max_hear_distance = 10}, true)
	end

	minetest.swap_node(pos, {
		name = name .. transform[state + 1][dir+1].v,
		param2 = transform[state + 1][dir+1].param2
	})
	meta:set_int("state", state)

	return true
end

local function on_place_node(place_to, newnode,
	placer, oldnode, itemstack, pointed_thing)
	-- Run script hook
	for _, callback in ipairs(minetest.registered_on_placenodes) do
		-- Deepcopy pos, node and pointed_thing because callback can modify them
		local place_to_copy = {x = place_to.x, y = place_to.y, z = place_to.z}
		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 = {
			type  = pointed_thing.type,
			above = vector.new(pointed_thing.above),
			under = vector.new(pointed_thing.under),
			ref   = pointed_thing.ref,
		}
		callback(place_to_copy, newnode_copy, placer,
			oldnode_copy, itemstack, pointed_thing_copy)
	end
end

local function can_dig_door(pos, digger)
	replace_old_owner_information(pos)
	return default.can_interact_with_node(digger, pos)
end

function doors.register(name, def)
	if not name:find(":") then
		name = "doors:" .. name
	end

	-- replace old doors of this type automatically
	minetest.register_lbm({
		name = ":doors:replace_" .. name:gsub(":", "_"),
		nodenames = {name.."_b_1", name.."_b_2"},
		action = function(pos, node)
			local l = tonumber(node.name:sub(-1))
			local meta = minetest.get_meta(pos)
			local h = meta:get_int("right") + 1
			local p2 = node.param2
			local replace = {
				{{type = "a", state = 0}, {type = "a", state = 3}},
				{{type = "b", state = 1}, {type = "b", state = 2}}
			}
			local new = replace[l][h]
			-- retain infotext and doors_owner fields
			minetest.swap_node(pos, {name = name .. "_" .. new.type, param2 = p2})
			meta:set_int("state", new.state)
			-- properly place doors:hidden at the right spot
			local p3 = p2
			if new.state >= 2 then
				p3 = (p3 + 3) % 4
			end
			if new.state % 2 == 1 then
				if new.state >= 2 then
					p3 = (p3 + 1) % 4
				else
					p3 = (p3 + 3) % 4
				end
			end
			-- wipe meta on top node as it's unused
			minetest.set_node({x = pos.x, y = pos.y + 1, z = pos.z},
				{name = "doors:hidden", param2 = p3})
		end
	})

	minetest.register_craftitem(":" .. name, {
		description = def.description,
		inventory_image = def.inventory_image,
		groups = table.copy(def.groups),

		on_place = function(itemstack, placer, pointed_thing)
			local pos

			if not pointed_thing.type == "node" then
				return itemstack
			end

			local node = minetest.get_node(pointed_thing.under)
			local pdef = minetest.registered_nodes[node.name]
			if pdef and pdef.on_rightclick and
					not (placer and placer:is_player() and
					placer:get_player_control().sneak) then
				return pdef.on_rightclick(pointed_thing.under,
						node, placer, itemstack, pointed_thing)
			end

			if pdef and pdef.buildable_to then
				pos = pointed_thing.under
			else
				pos = pointed_thing.above
				node = minetest.get_node(pos)
				pdef = minetest.registered_nodes[node.name]
				if not pdef or not pdef.buildable_to then
					return itemstack
				end
			end

			local above = {x = pos.x, y = pos.y + 1, z = pos.z}
			local top_node = minetest.get_node_or_nil(above)
			local topdef = top_node and minetest.registered_nodes[top_node.name]

			if not topdef or not topdef.buildable_to then
				return itemstack
			end

			local pn = placer and placer:get_player_name() or ""
			if minetest.is_protected(pos, pn) or minetest.is_protected(above, pn) then
				return itemstack
			end

			local dir = placer and minetest.dir_to_facedir(placer:get_look_dir()) or 0

			local ref = {
				{x = -1, y = 0, z = 0},
				{x = 0, y = 0, z = 1},
				{x = 1, y = 0, z = 0},
				{x = 0, y = 0, z = -1},
			}

			local aside = {
				x = pos.x + ref[dir + 1].x,
				y = pos.y + ref[dir + 1].y,
				z = pos.z + ref[dir + 1].z,
			}

			local state = 0
			if minetest.get_item_group(minetest.get_node(aside).name, "door") == 1 then
				state = state + 2
				minetest.set_node(pos, {name = name .. "_b", param2 = dir})
				minetest.set_node(above, {name = "doors:hidden", param2 = (dir + 3) % 4})
			else
				minetest.set_node(pos, {name = name .. "_a", param2 = dir})
				minetest.set_node(above, {name = "doors:hidden", param2 = dir})
			end

			local meta = minetest.get_meta(pos)
			meta:set_int("state", state)

			if def.protected then
				meta:set_string("owner", pn)
				meta:set_string("infotext", def.description .. "\n" .. S("Owned by @1", pn))
			end

			if not (creative and creative.is_enabled_for and creative.is_enabled_for(pn)) then
				itemstack:take_item()
			end

			minetest.sound_play(def.sounds.place, {pos = pos}, true)

			on_place_node(pos, minetest.get_node(pos),
				placer, node, itemstack, pointed_thing)

			return itemstack
		end
	})
	def.inventory_image = nil

	if def.recipe then
		minetest.register_craft({
			output = name,
			recipe = def.recipe,
		})
	end
	def.recipe = nil

	if not def.sounds then
		def.sounds = default.node_sound_wood_defaults()
	end

	if not def.sound_open then
		def.sound_open = "doors_door_open"
	end

	if not def.sound_close then
		def.sound_close = "doors_door_close"
	end

	def.groups.not_in_creative_inventory = 1
	def.groups.door = 1
	def.drop = name
	def.door = {
		name = name,
		sounds = { def.sound_close, def.sound_open },
	}
	if not def.on_rightclick then
		def.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
			doors.door_toggle(pos, node, clicker)
			return itemstack
		end
	end
	def.after_dig_node = function(pos, node, meta, digger)
		minetest.remove_node({x = pos.x, y = pos.y + 1, z = pos.z})
		minetest.check_for_falling({x = pos.x, y = pos.y + 1, z = pos.z})
	end
	def.on_rotate = function(pos, node, user, mode, new_param2)
		return false
	end

	if def.protected then
		def.can_dig = can_dig_door
		def.on_blast = function() end
		def.on_key_use = function(pos, player)
			local door = doors.get(pos)
			door:toggle(player)
		end
		def.on_skeleton_key_use = function(pos, player, newsecret)
			replace_old_owner_information(pos)
			local meta = minetest.get_meta(pos)
			local owner = meta:get_string("owner")
			local pname = player:get_player_name()

			-- verify placer is owner of lockable door
			if owner ~= pname then
				minetest.record_protection_violation(pos, pname)
				minetest.chat_send_player(pname, S("You do not own this locked door."))
				return nil
			end

			local secret = meta:get_string("key_lock_secret")
			if secret == "" then
				secret = newsecret
				meta:set_string("key_lock_secret", secret)
			end

			return secret, S("a locked door"), owner
		end
		def.node_dig_prediction = ""
	else
		def.on_blast = function(pos, intensity)
			minetest.remove_node(pos)
			-- hidden node doesn't get blasted away.
			minetest.remove_node({x = pos.x, y = pos.y + 1, z = pos.z})
			return {name}
		end
	end

	def.on_destruct = function(pos)
		minetest.remove_node({x = pos.x, y = pos.y + 1, z = pos.z})
	end

	def.drawtype = "mesh"
	def.paramtype = "light"
	def.paramtype2 = "facedir"
	def.sunlight_propagates = true
	def.walkable = true
	def.is_ground_content = false
	def.buildable_to = false
	def.selection_box = {type = "fixed", fixed = {-1/2,-1/2,-1/2,1/2,3/2,-6/16}}
	def.collision_box = {type = "fixed", fixed = {-1/2,-1/2,-1/2,1/2,3/2,-6/16}}

	def.mesh = "door_a.obj"
	minetest.register_node(":" .. name .. "_a", def)

	def.mesh = "door_b.obj"
	minetest.register_node(":" .. name .. "_b", def)

	def.mesh = "door_a2.obj"
	minetest.register_node(":" .. name .. "_c", def)

	def.mesh = "door_b2.obj"
	minetest.register_node(":" .. name .. "_d", def)

	doors.registered_doors[name .. "_a"] = true
	doors.registered_doors[name .. "_b"] = true
	doors.registered_doors[name .. "_c"] = true
	doors.registered_doors[name .. "_d"] = true
end

doors.register("door_wood", {
		tiles = {{ name = "doors_door_wood.png", backface_culling = true }},
		description = S("Wooden Door"),
		inventory_image = "doors_item_wood.png",
		groups = {node = 1, choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
		recipe = {
			{"group:wood", "group:wood"},
			{"group:wood", "group:wood"},
			{"group:wood", "group:wood"},
		}
})

doors.register("door_steel", {
		tiles = {{name = "doors_door_steel.png", backface_culling = true}},
		description = S("Steel Door"),
		inventory_image = "doors_item_steel.png",
		protected = true,
		groups = {node = 1, cracky = 1, level = 2},
		sounds = default.node_sound_metal_defaults(),
		sound_open = "doors_steel_door_open",
		sound_close = "doors_steel_door_close",
		recipe = {
			{"default:steel_ingot", "default:steel_ingot"},
			{"default:steel_ingot", "default:steel_ingot"},
			{"default:steel_ingot", "default:steel_ingot"},
		}
})

doors.register("door_glass", {
		tiles = {"doors_door_glass.png"},
		description = S("Glass Door"),
		inventory_image = "doors_item_glass.png",
		groups = {node = 1, cracky=3, oddly_breakable_by_hand=3},
		sounds = default.node_sound_glass_defaults(),
		sound_open = "doors_glass_door_open",
		sound_close = "doors_glass_door_close",
		recipe = {
			{"default:glass", "default:glass"},
			{"default:glass", "default:glass"},
			{"default:glass", "default:glass"},
		}
})

doors.register("door_obsidian_glass", {
		tiles = {"doors_door_obsidian_glass.png"},
		description = S("Obsidian Glass Door"),
		inventory_image = "doors_item_obsidian_glass.png",
		groups = {node = 1, cracky=3},
		sounds = default.node_sound_glass_defaults(),
		sound_open = "doors_glass_door_open",
		sound_close = "doors_glass_door_close",
		recipe = {
			{"default:obsidian_glass", "default:obsidian_glass"},
			{"default:obsidian_glass", "default:obsidian_glass"},
			{"default:obsidian_glass", "default:obsidian_glass"},
		},
})

-- Capture mods using the old API as best as possible.
function doors.register_door(name, def)
	if def.only_placer_can_open then
		def.protected = true
	end
	def.only_placer_can_open = nil

	local i = name:find(":")
	local modname = name:sub(1, i - 1)
	if not def.tiles then
		if def.protected then
			def.tiles = {{name = "doors_door_steel.png", backface_culling = true}}
		else
			def.tiles = {{name = "doors_door_wood.png", backface_culling = true}}
		end
		minetest.log("warning", modname .. " registered door \"" .. name .. "\" " ..
				"using deprecated API method \"doors.register_door()\" but " ..
				"did not provide the \"tiles\" parameter. A fallback tiledef " ..
				"will be used instead.")
	end

	doors.register(name, def)
end

----trapdoor----

function doors.trapdoor_toggle(pos, node, clicker)
	node = node or minetest.get_node(pos)

	replace_old_owner_information(pos)

	if clicker and not default.can_interact_with_node(clicker, pos) then
		return false
	end

	local def = minetest.registered_nodes[node.name]

	if string.sub(node.name, -5) == "_open" then
		minetest.sound_play(def.sound_close,
			{pos = pos, gain = 0.3, max_hear_distance = 10}, true)
		minetest.swap_node(pos, {name = string.sub(node.name, 1,
			string.len(node.name) - 5), param1 = node.param1, param2 = node.param2})
	else
		minetest.sound_play(def.sound_open,
			{pos = pos, gain = 0.3, max_hear_distance = 10}, true)
		minetest.swap_node(pos, {name = node.name .. "_open",
			param1 = node.param1, param2 = node.param2})
	end
end

function doors.register_trapdoor(name, def)
	if not name:find(":") then
		name = "doors:" .. name
	end

	local name_closed = name
	local name_opened = name.."_open"

	def.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
		doors.trapdoor_toggle(pos, node, clicker)
		return itemstack
	end

	-- Common trapdoor configuration
	def.drawtype = "nodebox"
	def.paramtype = "light"
	def.paramtype2 = "facedir"
	def.is_ground_content = false

	if def.protected then
		def.can_dig = can_dig_door
		def.after_place_node = function(pos, placer, itemstack, pointed_thing)
			local pn = placer:get_player_name()
			local meta = minetest.get_meta(pos)
			meta:set_string("owner", pn)
			meta:set_string("infotext", def.description .. "\n" .. S("Owned by @1", pn))

			return (creative and creative.is_enabled_for and creative.is_enabled_for(pn))
		end

		def.on_blast = function() end
		def.on_key_use = function(pos, player)
			local door = doors.get(pos)
			door:toggle(player)
		end
		def.on_skeleton_key_use = function(pos, player, newsecret)
			replace_old_owner_information(pos)
			local meta = minetest.get_meta(pos)
			local owner = meta:get_string("owner")
			local pname = player:get_player_name()

			-- verify placer is owner of lockable door
			if owner ~= pname then
				minetest.record_protection_violation(pos, pname)
				minetest.chat_send_player(pname, S("You do not own this trapdoor."))
				return nil
			end

			local secret = meta:get_string("key_lock_secret")
			if secret == "" then
				secret = newsecret
				meta:set_string("key_lock_secret", secret)
			end

			return secret, S("a locked trapdoor"), owner
		end
		def.node_dig_prediction = ""
	else
		def.on_blast = function(pos, intensity)
			minetest.remove_node(pos)
			return {name}
		end
	end

	if not def.sounds then
		def.sounds = default.node_sound_wood_defaults()
	end

	if not def.sound_open then
		def.sound_open = "doors_door_open"
	end

	if not def.sound_close then
		def.sound_close = "doors_door_close"
	end

	local def_opened = table.copy(def)
	local def_closed = table.copy(def)

	def_closed.node_box = {
		type = "fixed",
		fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
	}
	def_closed.selection_box = {
		type = "fixed",
		fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
	}
	def_closed.tiles = {
		def.tile_front,
		def.tile_front .. '^[transformFY',
		def.tile_side,
		def.tile_side,
		def.tile_side,
		def.tile_side
	}

	def_opened.node_box = {
		type = "fixed",
		fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
	}
	def_opened.selection_box = {
		type = "fixed",
		fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
	}
	def_opened.tiles = {
		def.tile_side,
		def.tile_side .. '^[transform2',
		def.tile_side .. '^[transform3',
		def.tile_side .. '^[transform1',
		def.tile_front .. '^[transform46',
		def.tile_front .. '^[transform6'
	}

	def_opened.drop = name_closed
	def_opened.groups.not_in_creative_inventory = 1

	minetest.register_node(name_opened, def_opened)
	minetest.register_node(name_closed, def_closed)

	doors.registered_trapdoors[name_opened] = true
	doors.registered_trapdoors[name_closed] = true
end

doors.register_trapdoor("doors:trapdoor", {
	description = S("Wooden Trapdoor"),
	inventory_image = "doors_trapdoor.png",
	wield_image = "doors_trapdoor.png",
	tile_front = "doors_trapdoor.png",
	tile_side = "doors_trapdoor_side.png",
	groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2, door = 1},
})

doors.register_trapdoor("doors:trapdoor_steel", {
	description = S("Steel Trapdoor"),
	inventory_image = "doors_trapdoor_steel.png",
	wield_image = "doors_trapdoor_steel.png",
	tile_front = "doors_trapdoor_steel.png",
	tile_side = "doors_trapdoor_steel_side.png",
	protected = true,
	sounds = default.node_sound_metal_defaults(),
	sound_open = "doors_steel_door_open",
	sound_close = "doors_steel_door_close",
	groups = {cracky = 1, level = 2, door = 1},
})

minetest.register_craft({
	output = "doors:trapdoor 2",
	recipe = {
		{"group:wood", "group:wood", "group:wood"},
		{"group:wood", "group:wood", "group:wood"},
		{"", "", ""},
	}
})

minetest.register_craft({
	output = "doors:trapdoor_steel",
	recipe = {
		{"default:steel_ingot", "default:steel_ingot"},
		{"default:steel_ingot", "default:steel_ingot"},
	}
})

----fence gate----
local fence_collision_extra = minetest.settings:get_bool("enable_fence_tall") and 3/8 or 0

function doors.register_fencegate(name, def)
	local fence = {
		description = def.description,
		drawtype = "mesh",
		tiles = {},
		paramtype = "light",
		paramtype2 = "facedir",
		sunlight_propagates = true,
		is_ground_content = false,
		drop = name .. "_closed",
		connect_sides = {"left", "right"},
		groups = def.groups,
		sounds = def.sounds,
		on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
			local node_def = minetest.registered_nodes[node.name]
			minetest.swap_node(pos, {name = node_def.gate, param2 = node.param2})
			minetest.sound_play(node_def.sound, {pos = pos, gain = 0.3,
				max_hear_distance = 8}, true)
			return itemstack
		end,
		selection_box = {
			type = "fixed",
			fixed = {-1/2, -1/2, -1/4, 1/2, 1/2, 1/4}
		},
	}

	if type(def.texture) == "string" then
		fence.tiles[1] = {name = def.texture, backface_culling = true}
	elseif def.texture.backface_culling == nil then
		fence.tiles[1] = table.copy(def.texture)
		fence.tiles[1].backface_culling = true
	else
		fence.tiles[1] = def.texture
	end

	if not fence.sounds then
		fence.sounds = default.node_sound_wood_defaults()
	end

	fence.groups.fence = 1

	local fence_closed = table.copy(fence)
	fence_closed.mesh = "doors_fencegate_closed.obj"
	fence_closed.gate = name .. "_open"
	fence_closed.sound = "doors_fencegate_open"
	fence_closed.collision_box = {
		type = "fixed",
		fixed = {-1/2, -1/2, -1/8, 1/2, 1/2 + fence_collision_extra, 1/8}
	}

	local fence_open = table.copy(fence)
	fence_open.mesh = "doors_fencegate_open.obj"
	fence_open.gate = name .. "_closed"
	fence_open.sound = "doors_fencegate_close"
	fence_open.groups.not_in_creative_inventory = 1
	fence_open.collision_box = {
		type = "fixed",
		fixed = {{-1/2, -1/2, -1/8, -3/8, 1/2 + fence_collision_extra, 1/8},
			 {-1/2, -3/8, -1/2, -3/8, 3/8,                         0  }}
	}

	minetest.register_node(":" .. name .. "_closed", fence_closed)
	minetest.register_node(":" .. name .. "_open", fence_open)

	minetest.register_craft({
		output = name .. "_closed",
		recipe = {
			{"group:stick", def.material, "group:stick"},
			{"group:stick", def.material, "group:stick"}
		}
	})
end

doors.register_fencegate("doors:gate_wood", {
	description = S("Apple Wood Fence Gate"),
	texture = "default_wood.png",
	material = "default:wood",
	groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2}
})

doors.register_fencegate("doors:gate_acacia_wood", {
	description = S("Acacia Wood Fence Gate"),
	texture = "default_acacia_wood.png",
	material = "default:acacia_wood",
	groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2}
})

doors.register_fencegate("doors:gate_junglewood", {
	description = S("Jungle Wood Fence Gate"),
	texture = "default_junglewood.png",
	material = "default:junglewood",
	groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2}
})

doors.register_fencegate("doors:gate_pine_wood", {
	description = S("Pine Wood Fence Gate"),
	texture = "default_pine_wood.png",
	material = "default:pine_wood",
	groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3}
})

doors.register_fencegate("doors:gate_aspen_wood", {
	description = S("Aspen Wood Fence Gate"),
	texture = "default_aspen_wood.png",
	material = "default:aspen_wood",
	groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3}
})

----fuels----

minetest.register_craft({
	type = "fuel",
	recipe = "doors:trapdoor",
	burntime = 7,
})

minetest.register_craft({
	type = "fuel",
	recipe = "doors:door_wood",
	burntime = 14,
})

minetest.register_craft({
	type = "fuel",
	recipe = "doors:gate_wood_closed",
	burntime = 7,
})

minetest.register_craft({
	type = "fuel",
	recipe = "doors:gate_acacia_wood_closed",
	burntime = 8,
})

minetest.register_craft({
	type = "fuel",
	recipe = "doors:gate_junglewood_closed",
	burntime = 9,
})

minetest.register_craft({
	type = "fuel",
	recipe = "doors:gate_pine_wood_closed",
	burntime = 6,
})

minetest.register_craft({
	type = "fuel",
	recipe = "doors:gate_aspen_wood_closed",
	burntime = 5,
})


code:
# textdomain: doors
Hidden Door Segment=Verborgenes Türsegment
Owned by @1=Eigentum von @1
You do not own this locked door.=Diese abgeschlossene Tür gehört Ihnen nicht.
a locked door=eine abgeschlossene Tür
Wooden Door=Holztür
Steel Door=Stahltür
Glass Door=Glastür
Obsidian Glass Door=Obsidianglastür
You do not own this trapdoor.=Diese Falltür gehört Ihnen nicht.
a locked trapdoor=eine abgeschlossene Falltür
Wooden Trapdoor=Holzfalltür
Steel Trapdoor=Stahlfalltür
Apple Wood Fence Gate=Apfelholzzauntor
Acacia Wood Fence Gate=Akazienholzzauntor
Jungle Wood Fence Gate=Dschungelholzzauntor
Pine Wood Fence Gate=Kiefernholzzauntor
Aspen Wood Fence Gate=Espenholzzauntor


code:
# textdomain: doors
Hidden Door Segment=Segmento de puerta oculta
Owned by @1=Propiedad de @1
You do not own this locked door.=Esta puerta cerrada no te pertenece.
a locked door=una puerta cerrada
Wooden Door=Puerta de madera
Steel Door=Puerta de acero
Glass Door=Puerta de vidrio
Obsidian Glass Door=Puerta de vidrio de obsidiana
You do not own this trapdoor.=Esta trampilla no te pertenece.
a locked trapdoor=una trampilla cerrada
Wooden Trapdoor=Trampilla de madera
Steel Trapdoor=Trampilla de acero
Apple Wood Fence Gate=Puerta de cerca de manzano
Acacia Wood Fence Gate=Puerta de cerca de acacia
Jungle Wood Fence Gate=Puerta de cerca de madera tropical
Pine Wood Fence Gate=Puerta de cerca de pino
Aspen Wood Fence Gate=Puerta de cerca de álamo


code:
# textdomain: doors
Hidden Door Segment=Segment de porte cachée
Owned by @1=Possédée par @1
You do not own this locked door.=Cette porte vérouillée ne vous appartient pas.
a locked door=une porte verouillée
Wooden Door=Porte en bois
Steel Door=Porte en acier
Glass Door=Porte en verre
Obsidian Glass Door=Porte en verre d'obsidienne
You do not own this trapdoor.=Vous ne possédez pas cette trappe.
a locked trapdoor=une trappe verouillée
Wooden Trapdoor=Trappe en bois
Steel Trapdoor=Trappe en acier
Apple Wood Fence Gate=Porte de clôture en bois de pommier
Acacia Wood Fence Gate=Porte de clôture en bois d'acacia
Jungle Wood Fence Gate=Porte de clôture en bois de la jungle
Pine Wood Fence Gate=Porte de clôture en bois de pin
Aspen Wood Fence Gate=Porte de clôture en bois de tremble


code:
# textdomain: doors
Hidden Door Segment=Bagian Pintu Tersembunyi
Owned by @1=Milik @1
You do not own this locked door.=Anda bukan pemilik pintu terkunci ini.
a locked door=pintu terkunci
Wooden Door=Pintu Kayu
Steel Door=Pintu Baja
Glass Door=Pintu Kaca
Obsidian Glass Door=Pintu Kaca Obsidian
You do not own this trapdoor.=Anda bukan pemilik pintu kolong ini.
a locked trapdoor=pintu kolong terkunci
Wooden Trapdoor=Pintu Kolong Kayu
Steel Trapdoor=Pintu Kolong Baja
Apple Wood Fence Gate=Gerbang Kayu Pohon Apel
Acacia Wood Fence Gate=Gerbang Kayu Akasia
Jungle Wood Fence Gate=Gerbang Kayu Pohon Rimba
Pine Wood Fence Gate=Gerbang Kayu Pinus
Aspen Wood Fence Gate=Gerbang Kayu Aspen


code:
# textdomain: doors
Hidden Door Segment=Segmento di porta nascosto
Owned by @1=Di proprietà di @1
You do not own this locked door.=Non sei il proprietario di questa porta chiusa a chiave.
a locked door=una porta chiusa a chiave
Wooden Door=Porta di legno
Steel Door=Porta d'acciaio
Glass Door=Porta di vetro
Obsidian Glass Door=Porta di vetro d'ossidiana
Owned by @1=Di proprietà di @1
You do not own this trapdoor.=Non sei il proprietario di questa botola.
a locked trapdoor=una botola chiusa a chiave
Wooden Trapdoor=Botola di legno
Steel Trapdoor=Botola d'acciaio
Apple Wood Fence Gate=Cancello della recinzione di legno di melo
Acacia Wood Fence Gate=Cancello della recinzione di legno d'acacia
Jungle Wood Fence Gate=Cancello della recinzione di legno della giungla
Pine Wood Fence Gate=Cancello della recinzione di legno di pino
Aspen Wood Fence Gate=Cancello della recinzione di legno di pioppo


code:
# textdomain: doors
Hidden Door Segment=Segmen Pintu Tersembunyi
Owned by @1=Milik @1
You do not own this locked door.=Anda bukan pemilik pintu berkunci ini.
a locked door=pintu berkunci
Wooden Door=Pintu Kayu
Steel Door=Pintu Keluli
Glass Door=Pintu Kaca
Obsidian Glass Door=Pintu Kaca Obsidia
You do not own this trapdoor.=Anda bukan pemilik pintu kolong ini.
a locked trapdoor=pintu kolong berkunci
Wooden Trapdoor=Pintu Kolong Kayu
Steel Trapdoor=Pintu Kolong Keluli
Apple Wood Fence Gate=Pintu Pagar Kayu Epal
Acacia Wood Fence Gate=Pintu Pagar Kayu Akasia
Jungle Wood Fence Gate=Pintu Pagar Kayu Hutan
Pine Wood Fence Gate=Pintu Pagar Kayu Pain
Aspen Wood Fence Gate=Pintu Pagar Kayu Aspen


code:
# textdomain: doors
Hidden Door Segment=Спрятанная Часть Двери
Owned by @1=Владелец: @1
You do not own this locked door.=Вы не владелец этой заблокированной двери.
a locked door=заблокированная дверь
Wooden Door=Деревянная Дверь
Steel Door=Стальная Дверь
Glass Door=Стеклянная Дверь
Obsidian Glass Door=Дверь Из Обсидианового Стекла
You do not own this trapdoor.=Вы не владелец этого люка.
a locked trapdoor=заблокированный люк
Wooden Trapdoor=Деревянный Люк
Steel Trapdoor=Стальной Люк
Apple Wood Fence Gate=Яблоневая Деревянная Калитка
Acacia Wood Fence Gate=Деревянная Калитка Из Акации
Jungle Wood Fence Gate=Деревянная Калитка Из Тропического Дерева
Pine Wood Fence Gate=Сосновая Деревянная Калитка
Aspen Wood Fence Gate=Осиновая Деревянная Калитка


code:
# textdomain: doors
Hidden Door Segment=Dold dörrsegment
Owned by @1=Ägd av @1
You do not own this locked door.=Du äger inte denna låsta dörr.
a locked door=en låst dörr
Wooden Door=Trä Dörr
Steel Door=Stål Dörr
Glass Door=Glas Dörr
Obsidian Glass Door=Obsidian Glas Dörr
Owned by @1=Ägd av @1
You do not own this trapdoor.=Du äger inte denna fallucka
a locked trapdoor=en låst fallucka
Wooden Trapdoor=Trä Fallucka
Steel Trapdoor=Stål Fallucka
Apple Wood Fence Gate=Äpple Trä Fallucka
Acacia Wood Fence Gate=Akacia Trä Fallucka
Jungle Wood Fence Gate=Djungel Trä Fallucka
Pine Wood Fence Gate=Tall Trä Fallucka
Aspen Wood Fence Gate=Asp Trä Fallucka


code:
# textdomain: doors
Hidden Door Segment=隐藏门段
Owned by @1=由@1拥有
You do not own this locked door.=这个门不属于你所有。
a locked door=一扇已上锁的门
Wooden Door=木门
Steel Door=铁门
Glass Door=玻璃门
Obsidian Glass Door=黑曜石玻璃门
You do not own this trapdoor.=这个活板门不属于你所有。
a locked trapdoor=一扇已上锁的活板门
Wooden Trapdoor=木活板门
Steel Trapdoor=铁活板门
Apple Wood Fence Gate=用苹果树做的木栅栏门
Acacia Wood Fence Gate=相思木栅栏门
Jungle Wood Fence Gate=丛林木栅栏门
Pine Wood Fence Gate=松木栅栏门
Aspen Wood Fence Gate=白杨木栅栏门


code:
# textdomain: doors
Hidden Door Segment=隱藏門段
Owned by @1=由@1擁有
You do not own this locked door.=這個門不屬於你所有。
a locked door=一扇已上鎖的門
Wooden Door=木門
Steel Door=鐵門
Glass Door=玻璃門
Obsidian Glass Door=黑曜石玻璃門
You do not own this trapdoor.=這個活板門不屬於你所有。
a locked trapdoor=一扇已上鎖的活板門
Wooden Trapdoor=木活板門
Steel Trapdoor=鐵活板門
Apple Wood Fence Gate=用蘋果樹做的木柵欄門
Acacia Wood Fence Gate=相思木柵欄門
Jungle Wood Fence Gate=叢林木柵欄門
Pine Wood Fence Gate=松木柵欄門
Aspen Wood Fence Gate=白楊木柵欄門


code:
# textdomain: doors
Hidden Door Segment=
Owned by @1=
You do not own this locked door.=
a locked door=
Wooden Door=
Steel Door=
Glass Door=
Obsidian Glass Door=
You do not own this trapdoor.=
a locked trapdoor=
Wooden Trapdoor=
Steel Trapdoor=
Apple Wood Fence Gate=
Acacia Wood Fence Gate=
Jungle Wood Fence Gate=
Pine Wood Fence Gate=
Aspen Wood Fence Gate=


code:
name = doors
description = Minetest Game mod: doors
depends = default
optional_depends = screwdriver


code:
Minetest Game mod: dungeon_loot
===============================
Adds randomly generated chests with some "loot" to generated dungeons,
an API to register additional loot is provided.
Only works if dungeons are actually enabled in mapgen flags.

License information can be found in license.txt

Authors of source code
----------------------
Originally by sfan5 (MIT)


code:
dungeon_loot = {}

dungeon_loot.CHESTS_MIN = 0 -- not necessarily in a single dungeon
dungeon_loot.CHESTS_MAX = 2
dungeon_loot.STACKS_PER_CHEST_MAX = 8

dofile(minetest.get_modpath("dungeon_loot") .. "/loot.lua")
dofile(minetest.get_modpath("dungeon_loot") .. "/mapgen.lua")


code:
-- Loot from the `default` mod is registered here,
-- with the rest being registered in the respective mods

dungeon_loot.registered_loot = {
	-- various items
	{name = "default:stick", chance = 0.6, count = {3, 6}},
	{name = "default:flint", chance = 0.4, count = {1, 3}},

	-- farming / consumable
	{name = "default:apple", chance = 0.4, count = {1, 4}},
	{name = "default:cactus", chance = 0.4, count = {1, 4},
		types = {"sandstone", "desert"}},

	-- minerals
	{name = "default:coal_lump", chance = 0.9, count = {1, 12}},
	{name = "default:gold_ingot", chance = 0.5},
	{name = "default:steel_ingot", chance = 0.4, count = {1, 6}},
	{name = "default:mese_crystal", chance = 0.1, count = {2, 3}},

	-- tools
	{name = "default:sword_wood", chance = 0.6},
	{name = "default:pick_stone", chance = 0.3},
	{name = "default:axe_diamond", chance = 0.05},

	-- natural materials
	{name = "default:sand", chance = 0.8, count = {4, 32}, y = {-64, 32768},
		types = {"normal"}},
	{name = "default:desert_sand", chance = 0.8, count = {4, 32}, y = {-64, 32768},
		types = {"sandstone"}},
	{name = "default:desert_cobble", chance = 0.8, count = {4, 32},
		types = {"desert"}},
	{name = "default:snow", chance = 0.8, count = {8, 64}, y = {-64, 32768},
		types = {"ice"}},
	{name = "default:dirt", chance = 0.6, count = {2, 16}, y = {-64, 32768},
		types = {"normal", "sandstone", "desert"}},
	{name = "default:obsidian", chance = 0.25, count = {1, 3}, y = {-32768, -512}},
	{name = "default:mese", chance = 0.15, y = {-32768, -512}},
}

function dungeon_loot.register(t)
	if t.name ~= nil then
		t = {t} -- single entry
	end
	for _, loot in ipairs(t) do
		table.insert(dungeon_loot.registered_loot, loot)
	end
end

function dungeon_loot._internal_get_loot(pos_y, dungeontype)
	-- filter by y pos and type
	local ret = {}
	for _, l in ipairs(dungeon_loot.registered_loot) do
		if l.y == nil or (pos_y >= l.y[1] and pos_y <= l.y[2]) then
			if l.types == nil or table.indexof(l.types, dungeontype) ~= -1 then
				table.insert(ret, l)
			end
		end
	end
	return ret
end


code:
minetest.set_gen_notify({dungeon = true, temple = true})

local function noise3d_integer(noise, pos)
	return math.abs(math.floor(noise:get_3d(pos) * 0x7fffffff))
end

local function random_sample(rand, list, count)
	local ret = {}
	for n = 1, count do
		local idx = rand:next(1, #list)
		table.insert(ret, list[idx])
		table.remove(list, idx)
	end
	return ret
end

local function find_walls(cpos)
	local is_wall = function(node)
		return node.name ~= "air" and node.name ~= "ignore"
	end

	local dirs = {{x=1, z=0}, {x=-1, z=0}, {x=0, z=1}, {x=0, z=-1}}
	local get_node = minetest.get_node

	local ret = {}
	local mindist = {x=0, z=0}
	local min = function(a, b) return a ~= 0 and math.min(a, b) or b end
	for _, dir in ipairs(dirs) do
		for i = 1, 9 do -- 9 = max room size / 2
			local pos = vector.add(cpos, {x=dir.x*i, y=0, z=dir.z*i})

			-- continue in that direction until we find a wall-like node
			local node = get_node(pos)
			if is_wall(node) then
				local front_below = vector.subtract(pos, {x=dir.x, y=1, z=dir.z})
				local above = vector.add(pos, {x=0, y=1, z=0})

				-- check that it:
				--- is at least 2 nodes high (not a staircase)
				--- has a floor
				if is_wall(get_node(front_below)) and is_wall(get_node(above)) then
					table.insert(ret, {pos = pos, facing = {x=-dir.x, y=0, z=-dir.z}})
					if dir.z == 0 then
						mindist.x = min(mindist.x, i-1)
					else
						mindist.z = min(mindist.z, i-1)
					end
				end
				-- abort even if it wasn't a wall cause something is in the way
				break
			end
		end
	end

	local biome = minetest.get_biome_data(cpos)
	biome = biome and minetest.get_biome_name(biome.biome) or ""
	local type = "normal"
	if biome:find("desert") == 1 then
		type = "desert"
	elseif biome:find("sandstone_desert") == 1 then
		type = "sandstone"
	elseif biome:find("icesheet") == 1 then
		type = "ice"
	end

	return {
		walls = ret,
		size = {x=mindist.x*2, z=mindist.z*2},
		type = type,
	}
end

local function populate_chest(pos, rand, dungeontype)
	--minetest.chat_send_all("chest placed at " .. minetest.pos_to_string(pos) .. " [" .. dungeontype .. "]")
	--minetest.add_node(vector.add(pos, {x=0, y=1, z=0}), {name="default:torch", param2=1})

	local item_list = dungeon_loot._internal_get_loot(pos.y, dungeontype)
	-- take random (partial) sample of all possible items
	local sample_n = math.min(#item_list, dungeon_loot.STACKS_PER_CHEST_MAX)
	item_list = random_sample(rand, item_list, sample_n)

	-- apply chances / randomized amounts and collect resulting items
	local items = {}
	for _, loot in ipairs(item_list) do
		if rand:next(0, 1000) / 1000 <= loot.chance then
			local itemdef = minetest.registered_items[loot.name]
			local amount = 1
			if loot.count ~= nil then
				amount = rand:next(loot.count[1], loot.count[2])
			end

			if not itemdef then
				minetest.log("warning", "Registered loot item " .. loot.name .. " does not exist")
			elseif itemdef.tool_capabilities then
				for n = 1, amount do
					local wear = rand:next(0.20 * 65535, 0.75 * 65535) -- 20% to 75% wear
					table.insert(items, ItemStack({name = loot.name, wear = wear}))
				end
			elseif itemdef.stack_max == 1 then
				-- not stackable, add separately
				for n = 1, amount do
					table.insert(items, loot.name)
				end
			else
				table.insert(items, ItemStack({name = loot.name, count = amount}))
			end
		end
	end

	-- place items at random places in chest
	local inv = minetest.get_meta(pos):get_inventory()
	local listsz = inv:get_size("main")
	assert(listsz >= #items)
	for _, item in ipairs(items) do
		local index = rand:next(1, listsz)
		if inv:get_stack("main", index):is_empty() then
			inv:set_stack("main", index, item)
		else
			inv:add_item("main", item) -- space occupied, just put it anywhere
		end
	end
end

minetest.register_on_generated(function(minp, maxp, blockseed)
	local gennotify = minetest.get_mapgen_object("gennotify")
	local poslist = gennotify["dungeon"] or {}
	for _, entry in ipairs(gennotify["temple"] or {}) do
		table.insert(poslist, entry)
	end
	if #poslist == 0 then return end

	local noise = minetest.get_perlin(10115, 4, 0.5, 1)
	local rand = PcgRandom(noise3d_integer(noise, poslist[1]))

	local candidates = {}
	-- process at most 8 rooms to keep runtime of this predictable
	local num_process = math.min(#poslist, 8)
	for i = 1, num_process do
		local room = find_walls(poslist[i])
		-- skip small rooms and everything that doesn't at least have 3 walls
		if math.min(room.size.x, room.size.z) >= 4 and #room.walls >= 3 then
			table.insert(candidates, room)
		end
	end

	local num_chests = rand:next(dungeon_loot.CHESTS_MIN, dungeon_loot.CHESTS_MAX)
	num_chests = math.min(#candidates, num_chests)
	local rooms = random_sample(rand, candidates, num_chests)

	for _, room in ipairs(rooms) do
		-- choose place somewhere in front of any of the walls
		local wall = room.walls[rand:next(1, #room.walls)]
		local v, vi -- vector / axis that runs alongside the wall
		if wall.facing.x ~= 0 then
			v, vi = {x=0, y=0, z=1}, "z"
		else
			v, vi = {x=1, y=0, z=0}, "x"
		end
		local chestpos = vector.add(wall.pos, wall.facing)
		local off = rand:next(-room.size[vi]/2 + 1, room.size[vi]/2 - 1)
		chestpos = vector.add(chestpos, vector.multiply(v, off))

		if minetest.get_node(chestpos).name == "air" then
			-- make it face inwards to the room
			local facedir = minetest.dir_to_facedir(vector.multiply(wall.facing, -1))
			minetest.add_node(chestpos, {name = "default:chest", param2 = facedir})
			populate_chest(chestpos, PcgRandom(noise3d_integer(noise, chestpos)), room.type)
		end
	end
end)


code:
name = dungeon_loot
description = Minetest Game mod: dungeon_loot
depends = default


code:
Minetest Game mod: dye
======================
See license.txt for license information.
See init.lua for documentation.

Authors of source code
----------------------
Originally by Perttu Ahola (celeron55) <celeron55@gmail.com> (MIT)
Various Minetest developers and contributors (MIT)

Authors of media (textures)
---------------------------
Perttu Ahola (celeron55) <celeron55@gmail.com> (CC BY-SA 3.0)


code:
-- dye/init.lua

dye = {}

-- Load support for MT game translation.
local S = minetest.get_translator("dye")

-- Make dye names and descriptions available globally

dye.dyes = {
	{"white",      "White"},
	{"grey",       "Grey"},
	{"dark_grey",  "Dark Grey"},
	{"black",      "Black"},
	{"violet",     "Violet"},
	{"blue",       "Blue"},
	{"cyan",       "Cyan"},
	{"dark_green", "Dark Green"},
	{"green",      "Green"},
	{"yellow",     "Yellow"},
	{"brown",      "Brown"},
	{"orange",     "Orange"},
	{"red",        "Red"},
	{"magenta",    "Magenta"},
	{"pink",       "Pink"},
}

-- Define items

for _, row in ipairs(dye.dyes) do
	local name = row[1]
	local description = row[2]
	local groups = {dye = 1}
	groups["color_" .. name] = 1

	minetest.register_craftitem("dye:" .. name, {
		inventory_image = "dye_" .. name .. ".png",
		description = S(description .. " Dye"),
		groups = groups
	})

	minetest.register_craft({
		output = "dye:" .. name .. " 4",
		recipe = {
			{"group:flower,color_" .. name}
		},
	})
end

-- Manually add coal -> black dye

minetest.register_craft({
	output = "dye:black 4",
	recipe = {
		{"group:coal"}
	},
})

-- Manually add blueberries->violet dye

minetest.register_craft({
	output = "dye:violet 2",
	recipe = {
		{"default:blueberries"}
	},
})

-- Mix recipes

local dye_recipes = {
	-- src1, src2, dst
	-- RYB mixes
	{"red", "blue", "violet"}, -- "purple"
	{"yellow", "red", "orange"},
	{"yellow", "blue", "green"},
	-- RYB complementary mixes
	{"yellow", "violet", "dark_grey"},
	{"blue", "orange", "dark_grey"},
	-- CMY mixes - approximation
	{"cyan", "yellow", "green"},
	{"cyan", "magenta", "blue"},
	{"yellow", "magenta", "red"},
	-- other mixes that result in a color we have
	{"red", "green", "brown"},
	{"magenta", "blue", "violet"},
	{"green", "blue", "cyan"},
	{"pink", "violet", "magenta"},
	-- mixes with black
	{"white", "black", "grey"},
	{"grey", "black", "dark_grey"},
	{"green", "black", "dark_green"},
	{"orange", "black", "brown"},
	-- mixes with white
	{"white", "red", "pink"},
	{"white", "dark_grey", "grey"},
	{"white", "dark_green", "green"},
}

for _, mix in pairs(dye_recipes) do
	minetest.register_craft({
		type = "shapeless",
		output = "dye:" .. mix[3] .. " 2",
		recipe = {"dye:" .. mix[1], "dye:" .. mix[2]},
	})
end

-- Dummy calls to S() to allow translation scripts to detect the strings.
-- To update this run:
-- for _,e in ipairs(dye.dyes) do print(("S(%q)"):format(e[2].." Dye")) end

--[[
S("White Dye")
S("Grey Dye")
S("Dark Grey Dye")
S("Black Dye")
S("Violet Dye")
S("Blue Dye")
S("Cyan Dye")
S("Dark Green Dye")
S("Green Dye")
S("Yellow Dye")
S("Brown Dye")
S("Orange Dye")
S("Red Dye")
S("Magenta Dye")
S("Pink Dye")
--]]


code:
# textdomain: dye
White Dye=Weißer Farbstoff
Grey Dye=Grauer Farbstoff
Dark Grey Dye=Dunkelgrauer Farbstoff
Black Dye=Schwarzer Farbstoff
Violet Dye=Violetter Farbstoff
Blue Dye=Blauer Farbstoff
Cyan Dye=Türkiser Farbstoff
Dark Green Dye=Dunkelgrüner Farbstoff
Green Dye=Grüner Farbstoff
Yellow Dye=Gelber Farbstoff
Brown Dye=Brauner Farbstoff
Orange Dye=Orange Farbstoff
Red Dye=Roter Farbstoff
Magenta Dye=Magenta Farbstoff
Pink Dye=Rosa Farbstoff


code:
# textdomain: dye
White Dye=Tinte blanco
Grey Dye=Tinte gris
Dark Grey Dye=Tinte gris oscuro
Black Dye=Tinte  negro
Violet Dye=Tinte violeta
Blue Dye=Tinte azul
Cyan Dye=Tinte cián
Dark Green Dye=Tinte verde oscuro
Green Dye=Tinte verde
Yellow Dye=Tinte amarillo
Brown Dye=Tinte marrón
Orange Dye=Tinte naranja
Red Dye=Tinte rojo
Magenta Dye=Tinte magenta
Pink Dye=Tinte rosa


code:
# textdomain: dye
White Dye=Colorant blanc
Grey Dye=Colorant gris
Dark Grey Dye=Colorant gris foncé
Black Dye=Colorant noir
Violet Dye=Colorant violet
Blue Dye=Colorant bleu
Cyan Dye=Colorant cyan
Dark Green Dye=Colorant vert foncé
Green Dye=Colorant vert
Yellow Dye=Colorant jaune
Brown Dye=Colorant marron
Orange Dye=Colorant orange
Red Dye=Colorant rouge
Magenta Dye=Colorant magenta
Pink Dye=Colorant rose


code:
# textdomain: dye
White Dye=Pewarna Putih
Grey Dye=Pewarna Abu
Dark Grey Dye=Pewarna Abu Tua
Black Dye=Pewarna Hitam
Violet Dye=Pewarna Ungu
Blue Dye=Pewarna Biru
Cyan Dye=Pewarna Sian
Dark Green Dye=Pewarna Hijau Tua
Green Dye=Pewarna Hijau
Yellow Dye=Pewarna Kuning
Brown Dye=Pewarna Cokelat
Orange Dye=Pewarna Oranye
Red Dye=Pewarna Merah
Magenta Dye=Pewarna Magenta
Pink Dye=Pewarna Jambon


code:
# textdomain: dye
White Dye=Tintura bianca
Grey Dye=Tintura grigia
Dark Grey Dye=Tintura grigia scura
Black Dye=Tintura nera
Violet Dye=Tintura viola
Blue Dye=Tintura blu
Cyan Dye=Tintura ciano
Dark Green Dye=Tintura verde scura
Green Dye=Tintura verde
Yellow Dye=Tintura gialla
Brown Dye=Tintura marrone
Orange Dye=Tintura arancione
Red Dye=Tintura rossa
Magenta Dye=Tintura magenta
Pink Dye=Tintura rosa


code:
# textdomain: dye
White Dye=Pewarna Putih
Grey Dye=Pewarna Kelabu
Dark Grey Dye=Pewarna Kelabu Gelap
Black Dye=Pewarna Hitam
Violet Dye=Pewarna Ungu
Blue Dye=Pewarna Biru
Cyan Dye=Pewarna Biru Kehijauan
Dark Green Dye=Pewarna Hijau Gelap
Green Dye=Pewarna Hijau
Yellow Dye=Pewarna Kuning
Brown Dye=Pewarna Perang
Orange Dye=Pewarna Jingga
Red Dye=Pewarna Merah
Magenta Dye=Pewarna Merah Lembayung
Pink Dye=Pewarna Merah Jambu


code:
# textdomain: dye
White Dye=Белая Краска
Grey Dye=Серая Краска
Dark Grey Dye=Тёмно-Серая Краска
Black Dye=Черная Краска
Violet Dye=Фиолетовая Краска
Blue Dye=Синяя Краска
Cyan Dye=Голубая Краска
Dark Green Dye=Тёмно-Зелёная Краска
Green Dye=Зелёная Краска
Yellow Dye=Жёлтая Краска
Brown Dye=Бурая Краска
Orange Dye=Оранжевая Краска
Red Dye=Красная Краска
Magenta Dye=Пурпурная Краска
Pink Dye=Розовая Краска


code:
# textdomain: dye
White Dye=Vit Färg
Grey Dye=Grå Färg
Dark Grey Dye=Mörk Grå Färg
Black Dye=Svart Färg
Violet Dye=Violett Färg
Blue Dye=Blå Färg
Cyan Dye=Cyan Färg
Dark Green Dye=Mörk Grön Färg
Green Dye=Grön Färg
Yellow Dye=Gul Färg
Brown Dye=Brun Färg
Orange Dye=Orange Färg
Red Dye=Röd Färg
Magenta Dye=Magenta Färg
Pink Dye=Rosa Färg


code:
# textdomain: dye
White Dye=白染料
Grey Dye=灰染料
Dark Grey Dye=暗灰染料
Black Dye=黑染料
Violet Dye=紫染料
Blue Dye=蓝染料
Cyan Dye=青染料
Dark Green Dye=暗绿染料
Green Dye=绿染料
Yellow Dye=黄染料
Brown Dye=棕染料
Orange Dye=橙染料
Red Dye=红染料
Magenta Dye=品红染料
Pink Dye=粉红染料


code:
# textdomain: dye
White Dye=白染料
Grey Dye=灰染料
Dark Grey Dye=暗灰染料
Black Dye=黑染料
Violet Dye=紫染料
Blue Dye=藍染料
Cyan Dye=青染料
Dark Green Dye=暗綠染料
Green Dye=綠染料
Yellow Dye=黃染料
Brown Dye=棕染料
Orange Dye=橙染料
Red Dye=紅染料
Magenta Dye=品紅染料
Pink Dye=粉紅染料


code:
# textdomain: dye
White Dye=
Grey Dye=
Dark Grey Dye=
Black Dye=
Violet Dye=
Blue Dye=
Cyan Dye=
Dark Green Dye=
Green Dye=
Yellow Dye=
Brown Dye=
Orange Dye=
Red Dye=
Magenta Dye=
Pink Dye=


code:
name = dye
description = Minetest Game mod: dye


code:
Minetest Game mod: env_sounds
=============================
See license.txt for license information.

Authors of source code
----------------------
paramat (MIT)

Authors of media (sounds)
-------------------------
Yuval (CC0 1.0)
https://freesound.org/people/Yuval/sounds/197023/
  env_sounds_water.*.ogg


code:
-- Parameters

local radius = 8 -- Water node search radius around player

-- End of parameters

local river_source_sounds = minetest.settings:get_bool("river_source_sounds")

-- Update sound for player

local function update_sound(player)
	local player_name = player:get_player_name()
	local ppos = player:get_pos()
	local areamin = vector.subtract(ppos, radius)
	local areamax = vector.add(ppos, radius)
	local water_nodes = {"default:water_flowing", "default:river_water_flowing"}
	if river_source_sounds then
		table.insert(water_nodes, "default:river_water_source")
	end
	local wpos, _ = minetest.find_nodes_in_area(areamin, areamax, water_nodes)
	local waters = #wpos
	if waters == 0 then
		return
	end

	-- Find average position of water positions
	local wposav = vector.new()
	for _, pos in ipairs(wpos) do
		wposav.x = wposav.x + pos.x
		wposav.y = wposav.y + pos.y
		wposav.z = wposav.z + pos.z
	end
	wposav = vector.divide(wposav, waters)

	minetest.sound_play(
		"env_sounds_water",
		{
			pos = wposav,
			to_player = player_name,
			gain = math.min(0.04 + waters * 0.004, 0.4),
		}
	)
end

-- Update sound 'on joinplayer'

minetest.register_on_joinplayer(function(player)
	update_sound(player)
end)

-- Cyclic sound update

local function cyclic_update()
	for _, player in pairs(minetest.get_connected_players()) do
		update_sound(player)
	end
	minetest.after(3.5, cyclic_update)
end

minetest.after(0, cyclic_update)


code:
name = env_sounds
description = Minetest Game mod: env_sounds
depends = default


code:
Minetest Game mod: farming
==========================
See license.txt for license information.

Authors of source code
----------------------
Originally by PilzAdam (MIT)
webdesigner97 (MIT)
Various Minetest developers and contributors (MIT)

Authors of media (textures)
---------------------------
Created by PilzAdam (CC BY 3.0):
  farming_bread.png
  farming_soil.png
  farming_soil_wet.png
  farming_soil_wet_side.png
  farming_string.png

Created by BlockMen (CC BY 3.0):
  farming_tool_diamondhoe.png
  farming_tool_mesehoe.png
  farming_tool_bronzehoe.png
  farming_tool_steelhoe.png
  farming_tool_stonehoe.png
  farming_tool_woodhoe.png

Created by MasterGollum (CC BY 3.0):
  farming_straw.png

Created by Gambit (CC BY 3.0):
  farming_wheat.png
  farming_wheat_*.png
  farming_cotton_*.png
  farming_flour.png
  farming_cotton_seed.png
  farming_wheat_seed.png

Created by Napiophelios (CC BY-SA 3.0):
  farming_cotton.png

Created by Extex101 (CC BY-SA 3.0):
  farming_cotton_wild.png


code:
-- farming/api.lua

-- support for MT game translation.
local S = farming.get_translator

-- Wear out hoes, place soil
-- TODO Ignore group:flower
farming.registered_plants = {}

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

	local under = minetest.get_node(pt.under)
	local p = {x=pt.under.x, y=pt.under.y+1, z=pt.under.z}
	local above = minetest.get_node(p)

	-- return if any of the nodes is not registered
	if not minetest.registered_nodes[under.name] then
		return
	end
	if not minetest.registered_nodes[above.name] then
		return
	end

	-- check if the node above the pointed thing is air
	if above.name ~= "air" then
		return
	end

	-- check if pointing at soil
	if minetest.get_item_group(under.name, "soil") ~= 1 then
		return
	end

	-- check if (wet) soil defined
	local regN = minetest.registered_nodes
	if regN[under.name].soil == nil or regN[under.name].soil.wet == nil or regN[under.name].soil.dry == nil then
		return
	end

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

	-- turn the node into soil and play sound
	minetest.set_node(pt.under, {name = regN[under.name].soil.dry})
	minetest.sound_play("default_dig_crumbly", {
		pos = pt.under,
		gain = 0.5,
	}, true)

	if not (creative and creative.is_enabled_for
			and creative.is_enabled_for(user:get_player_name())) then
		-- wear tool
		local wdef = itemstack:get_definition()
		itemstack:add_wear(65535/(uses-1))
		-- tool break sound
		if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
			minetest.sound_play(wdef.sound.breaks, {pos = pt.above,
				gain = 0.5}, true)
		end
	end
	return itemstack
end

-- Register new hoes
farming.register_hoe = function(name, def)
	-- Check for : prefix (register new hoes in your mod's namespace)
	if name:sub(1,1) ~= ":" then
		name = ":" .. name
	end
	-- Check def table
	if def.description == nil then
		def.description = S("Hoe")
	end
	if def.inventory_image == nil then
		def.inventory_image = "unknown_item.png"
	end
	if def.max_uses == nil then
		def.max_uses = 30
	end
	-- Register the tool
	minetest.register_tool(name, {
		description = def.description,
		inventory_image = def.inventory_image,
		on_use = function(itemstack, user, pointed_thing)
			return farming.hoe_on_use(itemstack, user, pointed_thing, def.max_uses)
		end,
		groups = def.groups,
		sound = {breaks = "default_tool_breaks"},
	})
	-- Register its recipe
	if def.recipe then
		minetest.register_craft({
			output = name:sub(2),
			recipe = def.recipe
		})
	elseif def.material then
		minetest.register_craft({
			output = name:sub(2),
			recipe = {
				{def.material, def.material},
				{"", "group:stick"},
				{"", "group:stick"}
			}
		})
	end
end

-- how often node timers for plants will tick, +/- some random value
local function tick(pos)
	minetest.get_node_timer(pos):start(math.random(166, 286))
end
-- how often a growth failure tick is retried (e.g. too dark)
local function tick_again(pos)
	minetest.get_node_timer(pos):start(math.random(40, 80))
end

-- Seed placement
farming.place_seed = function(itemstack, placer, pointed_thing, plantname)
	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)

	local player_name = placer and placer:get_player_name() or ""

	if minetest.is_protected(pt.under, player_name) then
		minetest.record_protection_violation(pt.under, player_name)
		return
	end
	if minetest.is_protected(pt.above, player_name) then
		minetest.record_protection_violation(pt.above, player_name)
		return
	end

	-- return if any of the nodes is not registered
	if not minetest.registered_nodes[under.name] then
		return itemstack
	end
	if not minetest.registered_nodes[above.name] then
		return itemstack
	end

	-- check if pointing at the top of the node
	if pt.above.y ~= pt.under.y+1 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

	-- check if pointing at soil
	if minetest.get_item_group(under.name, "soil") < 2 then
		return itemstack
	end

	-- add the node and remove 1 item from the itemstack
	minetest.log("action", player_name .. " places node " .. plantname .. " at " ..
		minetest.pos_to_string(pt.above))
	minetest.add_node(pt.above, {name = plantname, param2 = 1})
	tick(pt.above)
	if not (creative and creative.is_enabled_for
			and creative.is_enabled_for(player_name)) then
		itemstack:take_item()
	end
	return itemstack
end

farming.grow_plant = function(pos, elapsed)
	local node = minetest.get_node(pos)
	local name = node.name
	local def = minetest.registered_nodes[name]

	if not def.next_plant then
		-- disable timer for fully grown plant
		return
	end

	-- grow seed
	if minetest.get_item_group(node.name, "seed") and def.fertility then
		local soil_node = minetest.get_node_or_nil({x = pos.x, y = pos.y - 1, z = pos.z})
		if not soil_node then
			tick_again(pos)
			return
		end
		-- omitted is a check for light, we assume seeds can germinate in the dark.
		for _, v in pairs(def.fertility) do
			if minetest.get_item_group(soil_node.name, v) ~= 0 then
				local placenode = {name = def.next_plant}
				if def.place_param2 then
					placenode.param2 = def.place_param2
				end
				minetest.swap_node(pos, placenode)
				if minetest.registered_nodes[def.next_plant].next_plant then
					tick(pos)
					return
				end
			end
		end

		return
	end

	-- check if on wet soil
	local below = minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z})
	if minetest.get_item_group(below.name, "soil") < 3 then
		tick_again(pos)
		return
	end

	-- check light
	local light = minetest.get_node_light(pos)
	if not light or light < def.minlight or light > def.maxlight then
		tick_again(pos)
		return
	end

	-- grow
	local placenode = {name = def.next_plant}
	if def.place_param2 then
		placenode.param2 = def.place_param2
	end
	minetest.swap_node(pos, placenode)

	-- new timer needed?
	if minetest.registered_nodes[def.next_plant].next_plant then
		tick(pos)
	end
	return
end

-- Register plants
farming.register_plant = function(name, def)
	local mname = name:split(":")[1]
	local pname = name:split(":")[2]

	-- Check def table
	if not def.description then
		def.description = S("Seed")
	end
	if not def.harvest_description then
		def.harvest_description = pname:gsub("^%l", string.upper)
	end
	if not def.inventory_image then
		def.inventory_image = "unknown_item.png"
	end
	if not def.steps then
		return nil
	end
	if not def.minlight then
		def.minlight = 1
	end
	if not def.maxlight then
		def.maxlight = 14
	end
	if not def.fertility then
		def.fertility = {}
	end

	farming.registered_plants[pname] = def

	-- Register seed
	local lbm_nodes = {mname .. ":seed_" .. pname}
	local g = {seed = 1, snappy = 3, attached_node = 1, flammable = 2}
	for k, v in pairs(def.fertility) do
		g[v] = 1
	end
	minetest.register_node(":" .. mname .. ":seed_" .. pname, {
		description = def.description,
		tiles = {def.inventory_image},
		inventory_image = def.inventory_image,
		wield_image = def.inventory_image,
		drawtype = "signlike",
		groups = g,
		paramtype = "light",
		paramtype2 = "wallmounted",
		place_param2 = def.place_param2 or nil, -- this isn't actually used for placement
		walkable = false,
		sunlight_propagates = true,
		selection_box = {
			type = "fixed",
			fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5},
		},
		fertility = def.fertility,
		sounds = default.node_sound_dirt_defaults({
			dig = {name = "", gain = 0},
			dug = {name = "default_grass_footstep", gain = 0.2},
			place = {name = "default_place_node", gain = 0.25},
		}),

		on_place = function(itemstack, placer, pointed_thing)
			local under = pointed_thing.under
			local node = minetest.get_node(under)
			local udef = minetest.registered_nodes[node.name]
			if udef and udef.on_rightclick and
					not (placer and placer:is_player() and
					placer:get_player_control().sneak) then
				return udef.on_rightclick(under, node, placer, itemstack,
					pointed_thing) or itemstack
			end

			return farming.place_seed(itemstack, placer, pointed_thing, mname .. ":seed_" .. pname)
		end,
		next_plant = mname .. ":" .. pname .. "_1",
		on_timer = farming.grow_plant,
		minlight = def.minlight,
		maxlight = def.maxlight,
	})

	-- Register harvest
	minetest.register_craftitem(":" .. mname .. ":" .. pname, {
		description = def.harvest_description,
		inventory_image = mname .. "_" .. pname .. ".png",
		groups = def.groups or {flammable = 2},
	})

	-- Register growing steps
	for i = 1, def.steps do
		local base_rarity = 1
		if def.steps ~= 1 then
			base_rarity =  8 - (i - 1) * 7 / (def.steps - 1)
		end
		local drop = {
			items = {
				{items = {mname .. ":" .. pname}, rarity = base_rarity},
				{items = {mname .. ":" .. pname}, rarity = base_rarity * 2},
				{items = {mname .. ":seed_" .. pname}, rarity = base_rarity},
				{items = {mname .. ":seed_" .. pname}, rarity = base_rarity * 2},
			}
		}
		local nodegroups = {snappy = 3, flammable = 2, plant = 1, not_in_creative_inventory = 1, attached_node = 1}
		nodegroups[pname] = i

		local next_plant = nil

		if i < def.steps then
			next_plant = mname .. ":" .. pname .. "_" .. (i + 1)
			lbm_nodes[#lbm_nodes + 1] = mname .. ":" .. pname .. "_" .. i
		end

		minetest.register_node(":" .. mname .. ":" .. pname .. "_" .. i, {
			drawtype = "plantlike",
			waving = 1,
			tiles = {mname .. "_" .. pname .. "_" .. i .. ".png"},
			paramtype = "light",
			paramtype2 = def.paramtype2 or nil,
			place_param2 = def.place_param2 or nil,
			walkable = false,
			buildable_to = true,
			drop = drop,
			selection_box = {
				type = "fixed",
				fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5},
			},
			groups = nodegroups,
			sounds = default.node_sound_leaves_defaults(),
			next_plant = next_plant,
			on_timer = farming.grow_plant,
			minlight = def.minlight,
			maxlight = def.maxlight,
		})
	end

	-- replacement LBM for pre-nodetimer plants
	minetest.register_lbm({
		name = ":" .. mname .. ":start_nodetimer_" .. pname,
		nodenames = lbm_nodes,
		action = function(pos, node)
			tick_again(pos)
		end,
	})

	-- Return
	local r = {
		seed = mname .. ":seed_" .. pname,
		harvest = mname .. ":" .. pname
	}
	return r
end


code:
-- farming/hoes.lua

-- support for MT game translation.
local S = farming.get_translator

farming.register_hoe(":farming:hoe_wood", {
	description = S("Wooden Hoe"),
	inventory_image = "farming_tool_woodhoe.png",
	max_uses = 30,
	material = "group:wood",
	groups = {hoe = 1, flammable = 2},
})

farming.register_hoe(":farming:hoe_stone", {
	description = S("Stone Hoe"),
	inventory_image = "farming_tool_stonehoe.png",
	max_uses = 90,
	material = "group:stone",
	groups = {hoe = 1}
})

farming.register_hoe(":farming:hoe_steel", {
	description = S("Steel Hoe"),
	inventory_image = "farming_tool_steelhoe.png",
	max_uses = 500,
	material = "default:steel_ingot",
	groups = {hoe = 1}
})

-- The following are deprecated by removing the 'material' field to prevent
-- crafting and removing from creative inventory, to cause them to eventually
-- disappear from worlds. The registrations should be removed in a future
-- release.

farming.register_hoe(":farming:hoe_bronze", {
	description = S("Bronze Hoe"),
	inventory_image = "farming_tool_bronzehoe.png",
	max_uses = 220,
	groups = {hoe = 1, not_in_creative_inventory = 1},
})

farming.register_hoe(":farming:hoe_mese", {
	description = S("Mese Hoe"),
	inventory_image = "farming_tool_mesehoe.png",
	max_uses = 350,
	groups = {hoe = 1, not_in_creative_inventory = 1},
})

farming.register_hoe(":farming:hoe_diamond", {
	description = S("Diamond Hoe"),
	inventory_image = "farming_tool_diamondhoe.png",
	max_uses = 500,
	groups = {hoe = 1, not_in_creative_inventory = 1},
})


code:
-- farming/init.lua

-- Load support for MT game translation.
local S = minetest.get_translator("farming")

-- Global farming namespace

farming = {}
farming.path = minetest.get_modpath("farming")
farming.get_translator = S

-- Load files

dofile(farming.path .. "/api.lua")
dofile(farming.path .. "/nodes.lua")
dofile(farming.path .. "/hoes.lua")

-- Wheat

farming.register_plant("farming:wheat", {
	description = S("Wheat Seed"),
	harvest_description = S("Wheat"),
	paramtype2 = "meshoptions",
	inventory_image = "farming_wheat_seed.png",
	steps = 8,
	minlight = 13,
	maxlight = default.LIGHT_MAX,
	fertility = {"grassland"},
	groups = {food_wheat = 1, flammable = 4},
	place_param2 = 3,
})

minetest.register_craftitem("farming:flour", {
	description = S("Flour"),
	inventory_image = "farming_flour.png",
	groups = {food_flour = 1, flammable = 1},
})

minetest.register_craftitem("farming:bread", {
	description = S("Bread"),
	inventory_image = "farming_bread.png",
	on_use = minetest.item_eat(5),
	groups = {food_bread = 1, flammable = 2},
})

minetest.register_craft({
	type = "shapeless",
	output = "farming:flour",
	recipe = {"farming:wheat", "farming:wheat", "farming:wheat", "farming:wheat"}
})

minetest.register_craft({
	type = "cooking",
	cooktime = 15,
	output = "farming:bread",
	recipe = "farming:flour"
})

-- Cotton

farming.register_plant("farming:cotton", {
	description = S("Cotton Seed"),
	harvest_description = S("Cotton"),
	inventory_image = "farming_cotton_seed.png",
	steps = 8,
	minlight = 13,
	maxlight = default.LIGHT_MAX,
	fertility = {"grassland", "desert"},
	groups = {flammable = 4},
})

minetest.register_decoration({
	name = "farming:cotton_wild",
	deco_type = "simple",
	place_on = {"default:dry_dirt_with_dry_grass"},
	sidelen = 16,
	noise_params = {
		offset = -0.1,
		scale = 0.1,
		spread = {x = 50, y = 50, z = 50},
		seed = 4242,
		octaves = 3,
		persist = 0.7
	},
	biomes = {"savanna"},
	y_max = 31000,
	y_min = 1,
	decoration = "farming:cotton_wild",
})

minetest.register_craftitem("farming:string", {
	description = S("String"),
	inventory_image = "farming_string.png",
	groups = {flammable = 2},
})

minetest.register_craft({
	output = "wool:white",
	recipe = {
		{"farming:cotton", "farming:cotton"},
		{"farming:cotton", "farming:cotton"},
	}
})

minetest.register_craft({
	output = "farming:string 2",
	recipe = {
		{"farming:cotton"},
		{"farming:cotton"},
	}
})

-- Straw

minetest.register_craft({
	output = "farming:straw 3",
	recipe = {
		{"farming:wheat", "farming:wheat", "farming:wheat"},
		{"farming:wheat", "farming:wheat", "farming:wheat"},
		{"farming:wheat", "farming:wheat", "farming:wheat"},
	}
})

minetest.register_craft({
	output = "farming:wheat 3",
	recipe = {
		{"farming:straw"},
	}
})

-- Fuels

minetest.register_craft({
	type = "fuel",
	recipe = "farming:wheat",
	burntime = 1,
})

minetest.register_craft({
	type = "fuel",
	recipe = "farming:cotton",
	burntime = 1,
})

minetest.register_craft({
	type = "fuel",
	recipe = "farming:string",
	burntime = 1,
})

minetest.register_craft({
	type = "fuel",
	recipe = "farming:hoe_wood",
	burntime = 5,
})

-- Register farming items as dungeon loot

if minetest.global_exists("dungeon_loot") then
	dungeon_loot.register({
		{name = "farming:string", chance = 0.5, count = {1, 8}},
		{name = "farming:wheat", chance = 0.5, count = {2, 5}},
		{name = "farming:seed_cotton", chance = 0.4, count = {1, 4},
			types = {"normal"}},
	})
end


code:
# textdomain: farming
Wooden Hoe=Holzhacke
Stone Hoe=Steinhacke
Steel Hoe=Stahlhacke
Bronze Hoe=Bronzehacke
Mese Hoe=Mesehacke
Diamond Hoe=Diamanthacke
Wheat Seed=Weizensamen
Flour=Mehl
Bread=Brot
Cotton Seed=Baumwollsamen
String=Faden
Soil=Ackerboden
Wet Soil=Nasser Ackerboden
Savanna Soil=Savannenackerboden
Wet Savanna Soil=Nasser Savannenackerboden
Desert Sand Soil=Wüstensandackerboden
Wet Desert Sand Soil=Nasser Wüstensandackerboden
Straw=Stroh
Straw Stair=Strohtreppe
Straw Slab=Strohplatte
Inner Straw Stair=Innere Strohtreppe
Outer Straw Stair=Äußere Strohtreppe
Wheat=Weizen
Cotton=Baumwolle
Hoe=Hacke
Seed=Samen
Wild Cotton=Wilde Baumwolle


code:
# textdomain: farming
Wooden Hoe=Azada de madera
Stone Hoe=Azada de piedra
Steel Hoe=Azada de acero
Bronze Hoe=Azada de bronce
Mese Hoe=Azada de mese
Diamond Hoe=Azada de diamante
Wheat Seed=Semilla de trigo
Flour=Harina
Bread=Pan
Cotton Seed=Semilla de algodón
String=Hilo
Soil=Tierra de cultivo
Wet Soil=Tierra de cultivo humeda
Dry Soil=Tierra de cultivo seca
Wet Dry Soil=Tierra de cultivo seca-humeda
Desert Sand Soil=Tierra de cultivo de arena de desierto
Wet Desert Sand Soil=Tierra de cultivo de arena de desierto humeda
Straw=Paja
Straw Stair=Escalera de paja
Straw Slab=Losa de paja
Inner Straw Stair=Escalera de paja interior
Outer Straw Stair=Escalera de paja exterior
Wheat=Trigo
Cotton=Algodón
Wild Cotton=Algodón silvestre


code:
# textdomain: farming
Wooden Hoe=Houe en bois
Stone Hoe=Houe en pierre
Steel Hoe=Houe en acier
Bronze Hoe=Houe en bronze
Mese Hoe=Houe en Mese
Diamond Hoe=Houe en diamant
Wheat Seed=Grain de blé
Flour=Farine
Bread=Pain
Cotton Seed=Graine de coton
String=Ficelle
Soil=Sol
Wet Soil=Sol humide
Dry Soil=Sol sec
Wet Dry Soil=Sol sec et humide
Desert Sand Soil=Sol de sable du désert
Wet Desert Sand Soil=Sol de sable du désert humide
Straw=Paille
Straw Stair=Escalier de paille
Straw Slab=Dalle de paille
Inner Straw Stair=Escalier intérieur en paille
Outer Straw Stair=Escalier extérieur en paille
Wheat=Blé
Cotton=Coton


code:
# textdomain: farming
Soil=Tanah Tanam
Wet Soil=Tanah Tanam Basah
Savanna Soil=Tanah Tanam Sabana
Wet Savanna Soil=Tanah Tanam Sabana Basah
Desert Sand Soil=Pasir Tanam Gurun
Wet Desert Sand Soil=Pasir Tanam Gurun Basah
Straw=Jerami
Straw Stair=Tangga Jerami
Inner Straw Stair=Tangga Jerami Dalam
Outer Straw Stair=Tangga Jerami Luar
Straw Slab=Lempengan Jerami
Wild Cotton=Kapas Liar
Wheat Seed=Benih Gandum
Wheat=Gandum
Flour=Tepung
Bread=Roti
Cotton Seed=Benih Kapas
Cotton=Kapas
String=Benang
Wooden Hoe=Cangkul Kayu
Stone Hoe=Cangkul Batu
Steel Hoe=Cangkul Baja
Bronze Hoe=Cangkul Perunggu
Mese Hoe=Cangkul Mese
Diamond Hoe=Cangkul Berlian
Hoe=Cangkul
Seed=Benih


code:
# textdomain: farming
Wooden Hoe=Zappa di legno
Stone Hoe=Zappa di pietra
Steel Hoe=Zappa d'acciaio
Bronze Hoe=Zappa di bronzo
Mese Hoe=Zappa di mese
Diamond Hoe=Zappa di diamante
Wheat Seed=Seme di grano
Flour=Farina
Bread=Pane
Cotton Seed=Seme di cotone
String=Filo
Soil=Terreno
Wet Soil=Terreno bagnato
Dry Soil=Terreno asciutto
Wet Dry Soil=Terreno asciutto bagnato
Desert Sand Soil=Terreno di sabbia del deserto
Wet Desert Sand Soil=Terreno bagnato di sabbia del deserto
Straw=Paglia
Straw Stair=Scala di paglia
Inner Straw Stair=Scala di paglia interna
Outer Straw Stair=Scala di paglia esterna
Straw Slab=Lastra di paglia
Wheat=Grano
Cotton=Cotone


code:
# textdomain: farming
Wooden Hoe=Cangkul Kayu
Stone Hoe=Cangkul Batu
Steel Hoe=Cangkul Keluli
Bronze Hoe=Cangkul Gangsa
Mese Hoe=Cangkul Mese
Diamond Hoe=Cangkul Intan
Wheat Seed=Benih Gandum
Flour=Tepung
Bread=Roti
Cotton Seed=Benih Kapas
String=Benang
Soil=Tanih
Wet Soil=Tanih Lembap
Dry Soil=Tanih Kering
Wet Dry Soil=Tanih Kering Lembap
Desert Sand Soil=Tanih Pasir Gurun
Wet Desert Sand Soil=Tanih Pasir Gurun Lembap
Straw=Jerami
Straw Stair=Tangga Jerami
Inner Straw Stair=Tangga Jerami Dalaman
Outer Straw Stair=Tangga Jerami Luaran
Straw Slab=Papak Jerami
Wheat=Gandum
Cotton=Kapas


code:
# textdomain: farming
Wooden Hoe=Деревянная Мотыга
Stone Hoe=Каменная Мотыга
Steel Hoe=Стальная Мотыга
Bronze Hoe=Бронзовая Мотыга
Mese Hoe=Месе Мотыга
Diamond Hoe=Алмазная Мотыга
Wheat Seed=Семена Пшеницы
Flour=Мука
Bread=Хлеб
Cotton Seed=Семена Хлопка
String=Нить
Soil=Почва
Wet Soil=Влажная Почва
Dry Soil=Сухая Почва
Wet Dry Soil=Влажная Сухая Почва
Desert Sand Soil=Пустынная Песчаная Почва
Wet Desert Sand Soil=Влажная Пустынная Песчаная Почва
Straw=Солома
Straw Stair=Соломенная Ступень
Inner Straw Stair=Угловая Соломенная Ступень (Внутренний Угол)
Outer Straw Stair=Угловая Соломенная Ступень (Внешний Угол)
Straw Slab=Соломенная Плита
Wheat=Пшеница
Cotton=Хлопок


code:
# textdomain: farming
Wooden Hoe=Träsko
Stone Hoe=Stensko
Steel Hoe=Stålsko
Bronze Hoe=Bronssko
Mese Hoe=Mesesko
Diamond Hoe=Diamantsko
Wheat Seed=Vetefrö
Flour=Mjöl
Bread=Bröd
Cotton Seed=Bollumsfrö
String=Snöra
Soil=Odlningsmark
Wet Soil=Våt Odlningsmark
Dry Soil=Torr Odlningsmark
Wet Dry Soil=Våt Torr Odlningsmark
Desert Sand Soil=Öken Sand Odlningsmark
Wet Desert Sand Soil=Våt Öken Sand Odlningsmark
Straw=Halm
Straw Stair=Halmstrappa
Inner Straw Stair=Inre Halmstrappa
Outer Straw Stair=Yttre Halmstrappa
Straw Slab=Halmplatta
Wheat=Vete
Cotton=Bomull


code:
# textdomain: farming
Wooden Hoe=木锄头
Stone Hoe=石锄头
Steel Hoe=铁锄头
Bronze Hoe=青铜锄头
Mese Hoe=黄石锄头
Diamond Hoe=钻石锄头
Wheat Seed=小麦种子
Flour=面粉
Bread=面包
Cotton Seed=棉花种子
String=线
Soil=土
Wet Soil=湿土
Dry Soil=干土
Wet Dry Soil=湿干土
Desert Sand Soil=沙漠沙土
Wet Desert Sand Soil=湿沙漠沙土
Straw=稻草
Straw Stair=稻草台阶
Inner Straw Stair=稻草内楼梯
Outer Straw Stair=稻草外楼梯
Straw Slab=稻草板
Wheat=小麦
Cotton=棉


code:
# textdomain: farming
Wooden Hoe=木鋤頭
Stone Hoe=石鋤頭
Steel Hoe=鐵鋤頭
Bronze Hoe=青銅鋤頭
Mese Hoe=黃石鋤頭
Diamond Hoe=鑽石鋤頭
Wheat Seed=小麥種子
Flour=麵粉
Bread=麵包
Cotton Seed=棉花種子
String=線
Soil=土
Wet Soil=溼土
Dry Soil=乾土
Wet Dry Soil=溼乾土
Desert Sand Soil=沙漠沙土
Wet Desert Sand Soil=溼沙漠沙土
Straw=稻草
Straw Stair=稻草臺階
Inner Straw Stair=稻草內樓梯
Outer Straw Stair=稻草外樓梯
Straw Slab=稻草板
Wheat=小麥
Cotton=棉


code:
# textdomain: farming
Soil=
Wet Soil=
Savanna Soil=
Wet Savanna Soil=
Desert Sand Soil=
Wet Desert Sand Soil=
Straw=
Straw Stair=
Inner Straw Stair=
Outer Straw Stair=
Straw Slab=
Wild Cotton=
Wheat Seed=
Wheat=
Flour=
Bread=
Cotton Seed=
Cotton=
String=
Wooden Hoe=
Stone Hoe=
Steel Hoe=
Bronze Hoe=
Mese Hoe=
Diamond Hoe=
Hoe=
Seed=


code:
name = farming
description = Minetest Game mod: farming
depends = default, wool, stairs
optional_depends = dungeon_loot