code:
-- tnt/init.lua

tnt = {}

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

-- Default to enabled when in singleplayer
local enable_tnt = minetest.settings:get_bool("enable_tnt")
if enable_tnt == nil then
	enable_tnt = minetest.is_singleplayer()
end

-- loss probabilities array (one in X will be lost)
local loss_prob = {}

loss_prob["default:cobble"] = 3
loss_prob["default:dirt"] = 4

local tnt_radius = tonumber(minetest.settings:get("tnt_radius") or 3)

-- Fill a list with data for content IDs, after all nodes are registered
local cid_data = {}
minetest.register_on_mods_loaded(function()
	for name, def in pairs(minetest.registered_nodes) do
		cid_data[minetest.get_content_id(name)] = {
			name = name,
			drops = def.drops,
			flammable = def.groups.flammable,
			on_blast = def.on_blast,
		}
	end
end)

local function rand_pos(center, pos, radius)
	local def
	local reg_nodes = minetest.registered_nodes
	local i = 0
	repeat
		-- Give up and use the center if this takes too long
		if i > 4 then
			pos.x, pos.z = center.x, center.z
			break
		end
		pos.x = center.x + math.random(-radius, radius)
		pos.z = center.z + math.random(-radius, radius)
		def = reg_nodes[minetest.get_node(pos).name]
		i = i + 1
	until def and not def.walkable
end

local function eject_drops(drops, pos, radius)
	local drop_pos = vector.new(pos)
	for _, item in pairs(drops) do
		local count = math.min(item:get_count(), item:get_stack_max())
		while count > 0 do
			local take = math.max(1,math.min(radius * radius,
					count,
					item:get_stack_max()))
			rand_pos(pos, drop_pos, radius)
			local dropitem = ItemStack(item)
			dropitem:set_count(take)
			local obj = minetest.add_item(drop_pos, dropitem)
			if obj then
				obj:get_luaentity().collect = true
				obj:set_acceleration({x = 0, y = -10, z = 0})
				obj:set_velocity({x = math.random(-3, 3),
						y = math.random(0, 10),
						z = math.random(-3, 3)})
			end
			count = count - take
		end
	end
end

local function add_drop(drops, item)
	item = ItemStack(item)
	local name = item:get_name()
	if loss_prob[name] ~= nil and math.random(1, loss_prob[name]) == 1 then
		return
	end

	local drop = drops[name]
	if drop == nil then
		drops[name] = item
	else
		drop:set_count(drop:get_count() + item:get_count())
	end
end

local basic_flame_on_construct -- cached value
local function destroy(drops, npos, cid, c_air, c_fire,
		on_blast_queue, on_construct_queue,
		ignore_protection, ignore_on_blast, owner)
	if not ignore_protection and minetest.is_protected(npos, owner) then
		return cid
	end

	local def = cid_data[cid]

	if not def then
		return c_air
	elseif not ignore_on_blast and def.on_blast then
		on_blast_queue[#on_blast_queue + 1] = {
			pos = vector.new(npos),
			on_blast = def.on_blast
		}
		return cid
	elseif def.flammable then
		on_construct_queue[#on_construct_queue + 1] = {
			fn = basic_flame_on_construct,
			pos = vector.new(npos)
		}
		return c_fire
	else
		local node_drops = minetest.get_node_drops(def.name, "")
		for _, item in pairs(node_drops) do
			add_drop(drops, item)
		end
		return c_air
	end
end

local function calc_velocity(pos1, pos2, old_vel, power)
	-- Avoid errors caused by a vector of zero length
	if vector.equals(pos1, pos2) then
		return old_vel
	end

	local vel = vector.direction(pos1, pos2)
	vel = vector.normalize(vel)
	vel = vector.multiply(vel, power)

	-- Divide by distance
	local dist = vector.distance(pos1, pos2)
	dist = math.max(dist, 1)
	vel = vector.divide(vel, dist)

	-- Add old velocity
	vel = vector.add(vel, old_vel)

	-- randomize it a bit
	vel = vector.add(vel, {
		x = math.random() - 0.5,
		y = math.random() - 0.5,
		z = math.random() - 0.5,
	})

	-- Limit to terminal velocity
	dist = vector.length(vel)
	if dist > 250 then
		vel = vector.divide(vel, dist / 250)
	end
	return vel
end

local function entity_physics(pos, radius, drops)
	local objs = minetest.get_objects_inside_radius(pos, radius)
	for _, obj in pairs(objs) do
		local obj_pos = obj:get_pos()
		local dist = math.max(1, vector.distance(pos, obj_pos))

		local damage = (4 / dist) * radius
		if obj:is_player() then
			local dir = vector.normalize(vector.subtract(obj_pos, pos))
			local moveoff = vector.multiply(dir, 2 / dist * radius)
			obj:add_player_velocity(moveoff)

			obj:set_hp(obj:get_hp() - damage)
		else
			local luaobj = obj:get_luaentity()

			-- object might have disappeared somehow
			if luaobj then
				local do_damage = true
				local do_knockback = true
				local entity_drops = {}
				local objdef = minetest.registered_entities[luaobj.name]

				if objdef and objdef.on_blast then
					do_damage, do_knockback, entity_drops = objdef.on_blast(luaobj, damage)
				end

				if do_knockback then
					local obj_vel = obj:get_velocity()
					obj:set_velocity(calc_velocity(pos, obj_pos,
							obj_vel, radius * 10))
				end
				if do_damage then
					if not obj:get_armor_groups().immortal then
						obj:punch(obj, 1.0, {
							full_punch_interval = 1.0,
							damage_groups = {fleshy = damage},
						}, nil)
					end
				end
				for _, item in pairs(entity_drops) do
					add_drop(drops, item)
				end
			end
		end
	end
end

local function add_effects(pos, radius, drops)
	minetest.add_particle({
		pos = pos,
		velocity = vector.new(),
		acceleration = vector.new(),
		expirationtime = 0.4,
		size = radius * 10,
		collisiondetection = false,
		vertical = false,
		texture = "tnt_boom.png",
		glow = 15,
	})
	minetest.add_particlespawner({
		amount = 64,
		time = 0.5,
		minpos = vector.subtract(pos, radius / 2),
		maxpos = vector.add(pos, radius / 2),
		minvel = {x = -10, y = -10, z = -10},
		maxvel = {x = 10, y = 10, z = 10},
		minacc = vector.new(),
		maxacc = vector.new(),
		minexptime = 1,
		maxexptime = 2.5,
		minsize = radius * 3,
		maxsize = radius * 5,
		texture = "tnt_smoke.png",
	})

	-- we just dropped some items. Look at the items entities and pick
	-- one of them to use as texture
	local texture = "tnt_blast.png" --fallback texture
	local node
	local most = 0
	for name, stack in pairs(drops) do
		local count = stack:get_count()
		if count > most then
			most = count
			local def = minetest.registered_nodes[name]
			if def then
				node = { name = name }
			end
			if def and def.tiles and def.tiles[1] then
				texture = def.tiles[1]
			end
		end
	end

	minetest.add_particlespawner({
		amount = 64,
		time = 0.1,
		minpos = vector.subtract(pos, radius / 2),
		maxpos = vector.add(pos, radius / 2),
		minvel = {x = -3, y = 0, z = -3},
		maxvel = {x = 3, y = 5,  z = 3},
		minacc = {x = 0, y = -10, z = 0},
		maxacc = {x = 0, y = -10, z = 0},
		minexptime = 0.8,
		maxexptime = 2.0,
		minsize = radius * 0.33,
		maxsize = radius,
		texture = texture,
		-- ^ only as fallback for clients without support for `node` parameter
		node = node,
		collisiondetection = true,
	})
end

function tnt.burn(pos, nodename)
	local name = nodename or minetest.get_node(pos).name
	local def = minetest.registered_nodes[name]
	if not def then
		return
	elseif def.on_ignite then
		def.on_ignite(pos)
	elseif minetest.get_item_group(name, "tnt") > 0 then
		minetest.swap_node(pos, {name = name .. "_burning"})
		minetest.sound_play("tnt_ignite", {pos = pos}, true)
		minetest.get_node_timer(pos):start(1)
	end
end

local function tnt_explode(pos, radius, ignore_protection, ignore_on_blast, owner, explode_center)
	pos = vector.round(pos)
	-- scan for adjacent TNT nodes first, and enlarge the explosion
	local vm1 = VoxelManip()
	local p1 = vector.subtract(pos, 2)
	local p2 = vector.add(pos, 2)
	local minp, maxp = vm1:read_from_map(p1, p2)
	local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
	local data = vm1:get_data()
	local count = 0
	local c_tnt
	local c_tnt_burning = minetest.get_content_id("tnt:tnt_burning")
	local c_tnt_boom = minetest.get_content_id("tnt:boom")
	local c_air = minetest.get_content_id("air")
	if enable_tnt then
		c_tnt = minetest.get_content_id("tnt:tnt")
	else
		c_tnt = c_tnt_burning -- tnt is not registered if disabled
	end
	-- make sure we still have explosion even when centre node isnt tnt related
	if explode_center then
		count = 1
	end

	for z = pos.z - 2, pos.z + 2 do
	for y = pos.y - 2, pos.y + 2 do
		local vi = a:index(pos.x - 2, y, z)
		for x = pos.x - 2, pos.x + 2 do
			local cid = data[vi]
			if cid == c_tnt or cid == c_tnt_boom or cid == c_tnt_burning then
				count = count + 1
				data[vi] = c_air
			end
			vi = vi + 1
		end
	end
	end

	vm1:set_data(data)
	vm1:write_to_map()

	-- recalculate new radius
	radius = math.floor(radius * math.pow(count, 1/3))

	-- perform the explosion
	local vm = VoxelManip()
	local pr = PseudoRandom(os.time())
	p1 = vector.subtract(pos, radius)
	p2 = vector.add(pos, radius)
	minp, maxp = vm:read_from_map(p1, p2)
	a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
	data = vm:get_data()

	local drops = {}
	local on_blast_queue = {}
	local on_construct_queue = {}
	basic_flame_on_construct = minetest.registered_nodes["fire:basic_flame"].on_construct

	local c_fire = minetest.get_content_id("fire:basic_flame")
	for z = -radius, radius do
	for y = -radius, radius do
	local vi = a:index(pos.x + (-radius), pos.y + y, pos.z + z)
	for x = -radius, radius do
		local r = vector.length(vector.new(x, y, z))
		if (radius * radius) / (r * r) >= (pr:next(80, 125) / 100) then
			local cid = data[vi]
			local p = {x = pos.x + x, y = pos.y + y, z = pos.z + z}
			if cid ~= c_air then
				data[vi] = destroy(drops, p, cid, c_air, c_fire,
					on_blast_queue, on_construct_queue,
					ignore_protection, ignore_on_blast, owner)
			end
		end
		vi = vi + 1
	end
	end
	end

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

	-- call check_single_for_falling for everything within 1.5x blast radius
	for y = -radius * 1.5, radius * 1.5 do
	for z = -radius * 1.5, radius * 1.5 do
	for x = -radius * 1.5, radius * 1.5 do
		local rad = {x = x, y = y, z = z}
		local s = vector.add(pos, rad)
		local r = vector.length(rad)
		if r / radius < 1.4 then
			minetest.check_single_for_falling(s)
		end
	end
	end
	end

	for _, queued_data in pairs(on_blast_queue) do
		local dist = math.max(1, vector.distance(queued_data.pos, pos))
		local intensity = (radius * radius) / (dist * dist)
		local node_drops = queued_data.on_blast(queued_data.pos, intensity)
		if node_drops then
			for _, item in pairs(node_drops) do
				add_drop(drops, item)
			end
		end
	end

	for _, queued_data in pairs(on_construct_queue) do
		queued_data.fn(queued_data.pos)
	end

	minetest.log("action", "TNT owned by " .. owner .. " detonated at " ..
		minetest.pos_to_string(pos) .. " with radius " .. radius)

	return drops, radius
end

function tnt.boom(pos, def)
	def = def or {}
	def.radius = def.radius or 1
	def.damage_radius = def.damage_radius or def.radius * 2
	local meta = minetest.get_meta(pos)
	local owner = meta:get_string("owner")
	if not def.explode_center and def.ignore_protection ~= true then
		minetest.set_node(pos, {name = "tnt:boom"})
	end
	local sound = def.sound or "tnt_explode"
	minetest.sound_play(sound, {pos = pos, gain = 2.5,
			max_hear_distance = math.min(def.radius * 20, 128)}, true)
	local drops, radius = tnt_explode(pos, def.radius, def.ignore_protection,
			def.ignore_on_blast, owner, def.explode_center)
	-- append entity drops
	local damage_radius = (radius / math.max(1, def.radius)) * def.damage_radius
	entity_physics(pos, damage_radius, drops)
	if not def.disable_drops then
		eject_drops(drops, pos, radius)
	end
	add_effects(pos, radius, drops)
	minetest.log("action", "A TNT explosion occurred at " .. minetest.pos_to_string(pos) ..
		" with radius " .. radius)
end

minetest.register_node("tnt:boom", {
	drawtype = "airlike",
	light_source = default.LIGHT_MAX,
	walkable = false,
	drop = "",
	groups = {dig_immediate = 3},
	-- unaffected by explosions
	on_blast = function() end,
})

minetest.register_node("tnt:gunpowder", {
	description = S("Gun Powder"),
	drawtype = "raillike",
	paramtype = "light",
	is_ground_content = false,
	sunlight_propagates = true,
	walkable = false,
	tiles = {
		"tnt_gunpowder_straight.png",
		"tnt_gunpowder_curved.png",
		"tnt_gunpowder_t_junction.png",
		"tnt_gunpowder_crossing.png"
	},
	inventory_image = "tnt_gunpowder_inventory.png",
	wield_image = "tnt_gunpowder_inventory.png",
	selection_box = {
		type = "fixed",
		fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
	},
	groups = {dig_immediate = 2, attached_node = 1, flammable = 5,
		connect_to_raillike = minetest.raillike_group("gunpowder")},
	sounds = default.node_sound_leaves_defaults(),

	on_punch = function(pos, node, puncher)
		if puncher:get_wielded_item():get_name() == "default:torch" then
			minetest.set_node(pos, {name = "tnt:gunpowder_burning"})
			minetest.log("action", puncher:get_player_name() ..
				" ignites tnt:gunpowder at " ..
				minetest.pos_to_string(pos))
		end
	end,
	on_blast = function(pos, intensity)
		minetest.set_node(pos, {name = "tnt:gunpowder_burning"})
	end,
	on_burn = function(pos)
		minetest.set_node(pos, {name = "tnt:gunpowder_burning"})
	end,
	on_ignite = function(pos, igniter)
		minetest.set_node(pos, {name = "tnt:gunpowder_burning"})
	end,
})

minetest.register_node("tnt:gunpowder_burning", {
	drawtype = "raillike",
	paramtype = "light",
	sunlight_propagates = true,
	walkable = false,
	light_source = 5,
	tiles = {{
		name = "tnt_gunpowder_burning_straight_animated.png",
		animation = {
			type = "vertical_frames",
			aspect_w = 16,
			aspect_h = 16,
			length = 1,
		}
	},
	{
		name = "tnt_gunpowder_burning_curved_animated.png",
		animation = {
			type = "vertical_frames",
			aspect_w = 16,
			aspect_h = 16,
			length = 1,
		}
	},
	{
		name = "tnt_gunpowder_burning_t_junction_animated.png",
		animation = {
			type = "vertical_frames",
			aspect_w = 16,
			aspect_h = 16,
			length = 1,
		}
	},
	{
		name = "tnt_gunpowder_burning_crossing_animated.png",
		animation = {
			type = "vertical_frames",
			aspect_w = 16,
			aspect_h = 16,
			length = 1,
		}
	}},
	selection_box = {
		type = "fixed",
		fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
	},
	drop = "",
	groups = {
		dig_immediate = 2,
		attached_node = 1,
		connect_to_raillike = minetest.raillike_group("gunpowder")
	},
	sounds = default.node_sound_leaves_defaults(),
	on_timer = function(pos, elapsed)
		for dx = -1, 1 do
		for dz = -1, 1 do
			if math.abs(dx) + math.abs(dz) == 1 then
				for dy = -1, 1 do
					tnt.burn({
						x = pos.x + dx,
						y = pos.y + dy,
						z = pos.z + dz,
					})
				end
			end
		end
		end
		minetest.remove_node(pos)
	end,
	-- unaffected by explosions
	on_blast = function() end,
	on_construct = function(pos)
		minetest.sound_play("tnt_gunpowder_burning", {pos = pos,
			gain = 2}, true)
		minetest.get_node_timer(pos):start(1)
	end,
})

minetest.register_craft({
	output = "tnt:gunpowder 5",
	type = "shapeless",
	recipe = {"default:coal_lump", "default:gravel"}
})

minetest.register_craftitem("tnt:tnt_stick", {
	description = S("TNT Stick"),
	inventory_image = "tnt_tnt_stick.png",
	groups = {flammable = 5},
})

if enable_tnt then
	minetest.register_craft({
		output = "tnt:tnt_stick 2",
		recipe = {
			{"tnt:gunpowder", "", "tnt:gunpowder"},
			{"tnt:gunpowder", "default:paper", "tnt:gunpowder"},
			{"tnt:gunpowder", "", "tnt:gunpowder"},
		}
	})

	minetest.register_craft({
		output = "tnt:tnt",
		recipe = {
			{"tnt:tnt_stick", "tnt:tnt_stick", "tnt:tnt_stick"},
			{"tnt:tnt_stick", "tnt:tnt_stick", "tnt:tnt_stick"},
			{"tnt:tnt_stick", "tnt:tnt_stick", "tnt:tnt_stick"}
		}
	})

	minetest.register_abm({
		label = "TNT ignition",
		nodenames = {"group:tnt", "tnt:gunpowder"},
		neighbors = {"fire:basic_flame", "default:lava_source", "default:lava_flowing"},
		interval = 4,
		chance = 1,
		action = function(pos, node)
			tnt.burn(pos, node.name)
		end,
	})
end

function tnt.register_tnt(def)
	local name
	if not def.name:find(':') then
		name = "tnt:" .. def.name
	else
		name = def.name
		def.name = def.name:match(":([%w_]+)")
	end
	if not def.tiles then def.tiles = {} end
	local tnt_top = def.tiles.top or def.name .. "_top.png"
	local tnt_bottom = def.tiles.bottom or def.name .. "_bottom.png"
	local tnt_side = def.tiles.side or def.name .. "_side.png"
	local tnt_burning = def.tiles.burning or def.name .. "_top_burning_animated.png"
	if not def.damage_radius then def.damage_radius = def.radius * 2 end

	if enable_tnt then
		minetest.register_node(":" .. name, {
			description = def.description,
			tiles = {tnt_top, tnt_bottom, tnt_side},
			is_ground_content = false,
			groups = {dig_immediate = 2, mesecon = 2, tnt = 1, flammable = 5},
			sounds = default.node_sound_wood_defaults(),
			after_place_node = function(pos, placer)
				if placer:is_player() then
					local meta = minetest.get_meta(pos)
					meta:set_string("owner", placer:get_player_name())
				end
			end,
			on_punch = function(pos, node, puncher)
				if puncher:get_wielded_item():get_name() == "default:torch" then
					minetest.swap_node(pos, {name = name .. "_burning"})
					minetest.registered_nodes[name .. "_burning"].on_construct(pos)
					minetest.log("action", puncher:get_player_name() ..
						" ignites " .. node.name .. " at " ..
						minetest.pos_to_string(pos))
				end
			end,
			on_blast = function(pos, intensity)
				minetest.after(0.1, function()
					tnt.boom(pos, def)
				end)
			end,
			mesecons = {effector =
				{action_on =
					function(pos)
						tnt.boom(pos, def)
					end
				}
			},
			on_burn = function(pos)
				minetest.swap_node(pos, {name = name .. "_burning"})
				minetest.registered_nodes[name .. "_burning"].on_construct(pos)
			end,
			on_ignite = function(pos, igniter)
				minetest.swap_node(pos, {name = name .. "_burning"})
				minetest.registered_nodes[name .. "_burning"].on_construct(pos)
			end,
		})
	end

	minetest.register_node(":" .. name .. "_burning", {
		tiles = {
			{
				name = tnt_burning,
				animation = {
					type = "vertical_frames",
					aspect_w = 16,
					aspect_h = 16,
					length = 1,
				}
			},
			tnt_bottom, tnt_side
			},
		light_source = 5,
		drop = "",
		sounds = default.node_sound_wood_defaults(),
		groups = {falling_node = 1},
		on_timer = function(pos, elapsed)
			tnt.boom(pos, def)
		end,
		-- unaffected by explosions
		on_blast = function() end,
		on_construct = function(pos)
			minetest.sound_play("tnt_ignite", {pos = pos}, true)
			minetest.get_node_timer(pos):start(4)
			minetest.check_for_falling(pos)
		end,
	})
end

tnt.register_tnt({
	name = "tnt:tnt",
	description = S("TNT"),
	radius = tnt_radius,
})


code:
# textdomain: tnt
Gun Powder=
TNT Stick=
TNT=

repo_name: Desour/PlayMinetestServer

license: mit


code:
# textdomain: tnt
Gun Powder=Schießpulver
TNT Stick=TNT-Stange
TNT=TNT


code:
# textdomain: tnt
Gun Powder=Pólvora
TNT Stick=Cartucho de TNT
TNT=TNT


code:
# textdomain: tnt
Gun Powder=Poudre à canon
TNT Stick=Bâton de TNT
TNT=TNT


code:
# textdomain: tnt
Gun Powder=Bubuk Mesiu
TNT Stick=Tongkat TNT
TNT=TNT


code:
# textdomain: tnt
Gun Powder=Polvere da sparo
TNT Stick=Candelotto di TNT
TNT=TNT


code:
# textdomain: tnt
Gun Powder=Serbuk Senjata Api
TNT Stick=Batang TNT
TNT=TNT


code:
# textdomain: tnt
Gun Powder=Порох
TNT Stick=Тротиловая Палка
TNT=Тротил


code:
# textdomain: tnt
Gun Powder=Krut
TNT Stick=Dynamitpinne
TNT=Dynamit


code:
# textdomain: tnt
Gun Powder=火药粉
TNT Stick=炸药棒
TNT=炸药包


code:
# textdomain: tnt
Gun Powder=火藥粉
TNT Stick=炸藥棒
TNT=炸藥包


code:
name = tnt
description = Minetest Game mod: tnt
depends = default, fire


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

Authors of source code
----------------------
Originally by Vanessa Ezekowitz (LGPLv2.1+)
Modified by Perttu Ahola <celeron55@gmail.com> (LGPLv2.1+)
Various Minetest developers and contributors (LGPLv2.1+)

Authors of media (textures)
---------------------------
All not listed below, Vanessa Ezekowitz (CC BY-SA 3.0)

The following textures were modified by Thomas-S (CC BY-SA 3.0):
  vessels_drinking_glass.png
  vessels_drinking_glass_inv.png
  vessels_glass_bottle.png
  vessels_steel_bottle.png

The following texture was created by Wuzzy (CC BY-SA 3.0):
  vessels_shelf_slot.png (based on vessels_glass_bottle.png)


code:
-- vessels/init.lua

-- Minetest 0.4 mod: vessels
-- See README.txt for licensing and other information.

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

local vessels_shelf_formspec =
	"size[8,7;]" ..
	"list[context;vessels;0,0.3;8,2;]" ..
	"list[current_player;main;0,2.85;8,1;]" ..
	"list[current_player;main;0,4.08;8,3;8]" ..
	"listring[context;vessels]" ..
	"listring[current_player;main]" ..
	default.get_hotbar_bg(0, 2.85)

local function update_vessels_shelf(pos)
	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()
	local invlist = inv:get_list("vessels")

	local formspec = vessels_shelf_formspec
	-- Inventory slots overlay
	local vx, vy = 0, 0.3
	local n_items = 0
	for i = 1, 16 do
		if i == 9 then
			vx = 0
			vy = vy + 1
		end
		if not invlist or invlist[i]:is_empty() then
			formspec = formspec ..
				"image[" .. vx .. "," .. vy .. ";1,1;vessels_shelf_slot.png]"
		else
			local stack = invlist[i]
			if not stack:is_empty() then
				n_items = n_items + stack:get_count()
			end
		end
		vx = vx + 1
	end
	meta:set_string("formspec", formspec)
	if n_items == 0 then
		meta:set_string("infotext", S("Empty Vessels Shelf"))
	else
		meta:set_string("infotext", S("Vessels Shelf (@1 items)", n_items))
	end
end

minetest.register_node("vessels:shelf", {
	description = S("Vessels Shelf"),
	tiles = {"default_wood.png", "default_wood.png", "default_wood.png",
		"default_wood.png", "vessels_shelf.png", "vessels_shelf.png"},
	paramtype2 = "facedir",
	is_ground_content = false,
	groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3},
	sounds = default.node_sound_wood_defaults(),

	on_construct = function(pos)
		local meta = minetest.get_meta(pos)
		update_vessels_shelf(pos)
		local inv = meta:get_inventory()
		inv:set_size("vessels", 8 * 2)
	end,
	can_dig = function(pos,player)
		local inv = minetest.get_meta(pos):get_inventory()
		return inv:is_empty("vessels")
	end,
	allow_metadata_inventory_put = function(pos, listname, index, stack, player)
		if minetest.get_item_group(stack:get_name(), "vessel") ~= 0 then
			return stack:get_count()
		end
		return 0
	end,
	on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
		minetest.log("action", player:get_player_name() ..
			   " moves stuff in vessels shelf at ".. minetest.pos_to_string(pos))
		update_vessels_shelf(pos)
	end,
	on_metadata_inventory_put = function(pos, listname, index, stack, player)
		minetest.log("action", player:get_player_name() ..
			   " moves stuff to vessels shelf at ".. minetest.pos_to_string(pos))
		update_vessels_shelf(pos)
	end,
	on_metadata_inventory_take = function(pos, listname, index, stack, player)
		minetest.log("action", player:get_player_name() ..
			   " takes stuff from vessels shelf at ".. minetest.pos_to_string(pos))
		update_vessels_shelf(pos)
	end,
	on_blast = function(pos)
		local drops = {}
		default.get_inventory_drops(pos, "vessels", drops)
		drops[#drops + 1] = "vessels:shelf"
		minetest.remove_node(pos)
		return drops
	end,
})

minetest.register_craft({
	output = "vessels:shelf",
	recipe = {
		{"group:wood", "group:wood", "group:wood"},
		{"group:vessel", "group:vessel", "group:vessel"},
		{"group:wood", "group:wood", "group:wood"},
	}
})

minetest.register_node("vessels:glass_bottle", {
	description = S("Empty Glass Bottle"),
	drawtype = "plantlike",
	tiles = {"vessels_glass_bottle.png"},
	inventory_image = "vessels_glass_bottle.png",
	wield_image = "vessels_glass_bottle.png",
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	selection_box = {
		type = "fixed",
		fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25}
	},
	groups = {vessel = 1, dig_immediate = 3, attached_node = 1},
	sounds = default.node_sound_glass_defaults(),
})

minetest.register_craft( {
	output = "vessels:glass_bottle 10",
	recipe = {
		{"default:glass", "", "default:glass"},
		{"default:glass", "", "default:glass"},
		{"", "default:glass", ""}
	}
})

minetest.register_node("vessels:drinking_glass", {
	description = S("Empty Drinking Glass"),
	drawtype = "plantlike",
	tiles = {"vessels_drinking_glass.png"},
	inventory_image = "vessels_drinking_glass_inv.png",
	wield_image = "vessels_drinking_glass.png",
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	selection_box = {
		type = "fixed",
		fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25}
	},
	groups = {vessel = 1, dig_immediate = 3, attached_node = 1},
	sounds = default.node_sound_glass_defaults(),
})

minetest.register_craft( {
	output = "vessels:drinking_glass 14",
	recipe = {
		{"default:glass", "", "default:glass"},
		{"default:glass", "", "default:glass"},
		{"default:glass", "default:glass", "default:glass"}
	}
})

minetest.register_node("vessels:steel_bottle", {
	description = S("Empty Heavy Steel Bottle"),
	drawtype = "plantlike",
	tiles = {"vessels_steel_bottle.png"},
	inventory_image = "vessels_steel_bottle.png",
	wield_image = "vessels_steel_bottle.png",
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	selection_box = {
		type = "fixed",
		fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25}
	},
	groups = {vessel = 1, dig_immediate = 3, attached_node = 1},
	sounds = default.node_sound_defaults(),
})

minetest.register_craft( {
	output = "vessels:steel_bottle 5",
	recipe = {
		{"default:steel_ingot", "", "default:steel_ingot"},
		{"default:steel_ingot", "", "default:steel_ingot"},
		{"", "default:steel_ingot", ""}
	}
})

-- Glass and steel recycling

minetest.register_craftitem("vessels:glass_fragments", {
	description = S("Glass Fragments"),
	inventory_image = "vessels_glass_fragments.png",
})

minetest.register_craft( {
	type = "shapeless",
	output = "vessels:glass_fragments",
	recipe = {
		"vessels:glass_bottle",
		"vessels:glass_bottle",
	},
})

minetest.register_craft( {
	type = "shapeless",
	output = "vessels:glass_fragments",
	recipe = {
		"vessels:drinking_glass",
		"vessels:drinking_glass",
	},
})

minetest.register_craft({
	type = "cooking",
	output = "default:glass",
	recipe = "vessels:glass_fragments",
})

minetest.register_craft( {
	type = "cooking",
	output = "default:steel_ingot",
	recipe = "vessels:steel_bottle",
})

minetest.register_craft({
	type = "fuel",
	recipe = "vessels:shelf",
	burntime = 30,
})

-- Register glass fragments as dungeon loot
if minetest.global_exists("dungeon_loot") then
	dungeon_loot.register({
		name = "vessels:glass_fragments", chance = 0.35, count = {1, 4}
	})
end


code:
# textdomain: vessels
Empty Vessels Shelf=
Vessels Shelf (@1 items)=
Vessels Shelf=
Empty Glass Bottle=
Empty Drinking Glass=
Empty Heavy Steel Bottle=
Glass Fragments=


code:
# textdomain: vessels
Vessels Shelf=Gefäßregal
Empty Glass Bottle=Leere Glasflasche
Empty Drinking Glass=Leeres Trinkglas
Empty Heavy Steel Bottle=Leere schwere Stahlflasche
Glass Fragments=Glasfragmente
Empty Vessels Shelf=Leeres Gefäßregal
Vessels Shelf (@1 items)=Gefäßregal (@1 Gegenstände)


code:
# textdomain: vessels
Vessels Shelf=Estante de vasijas
Empty Glass Bottle=Botella de vidrio vacía
Empty Drinking Glass=Vaso para beber vacío
Empty Heavy Steel Bottle=Botella de acero vacía
Glass Fragments=Fragmentos de vidrio
Empty Vessels Shelf=Estante de vasijas vacío
Vessels Shelf (@1 items)=Estante de vasijas (@1 objetos)


code:
# textdomain: vessels
Vessels Shelf=Etagère à récipient
Empty Glass Bottle=Bouteille de verre vide
Empty Drinking Glass=Verre vide
Empty Heavy Steel Bottle=Bouteille d'acier lourde vide
Glass Fragments=Fragments de verre
Empty Vessels Shelf=Etagère à récipient vide
Vessels Shelf (@1 items)=Etagère à récipient (@1 articles)


code:
# textdomain: vessels
Empty Vessels Shelf=Rak Bejana Kosong
Vessels Shelf (@1 items)=Rak Bejana (@1 barang)
Vessels Shelf=Rak Bejana
Empty Glass Bottle=Botol Kaca Kosong
Empty Drinking Glass=Gelas Minum Kosong
Empty Heavy Steel Bottle=Botol Baja Berat Kosong
Glass Fragments=Pecahan Kaca


code:
# textdomain: vessels
Vessels Shelf=Scaffale per contenitori
Empty Glass Bottle=Bottiglia di vetro vuota
Empty Drinking Glass=Bicchiere di vetro vuoto
Empty Heavy Steel Bottle=Bottigia di metallo pesante vuota
Glass Fragments=Frammenti di vetro
Empty Vessels Shelf=Scaffale per contenitori vuoto
Vessels Shelf (@1 items)=Scaffale per contenitori (@1 oggetti)


code:
# textdomain: vessels
Vessels Shelf=Rak Bekas Kaca
Empty Glass Bottle=Botol Kaca Kosong
Empty Drinking Glass=Gelas Minuman Kosong
Empty Heavy Steel Bottle=Botol Keluli Berat Kosong
Glass Fragments=Serpihan Kaca
Empty Vessels Shelf=Rak Bekas Kaca Kosong
Vessels Shelf (@1 items)=Rak Bekas Kaca (@1 barang)


code:
# textdomain: vessels
Vessels Shelf=Полка с Сосудами
Empty Glass Bottle=Пустая Стеклянная Бутылка
Empty Drinking Glass=Пустая Питьевая Бутылка
Empty Heavy Steel Bottle=Пустая Стальная Бутылка
Glass Fragments=Стеклянные Осколки
Empty Vessels Shelf=Полка с Пустыми Сосудами
Vessels Shelf (@1 items)=Полка с Сосудами (@1 предметы)


code:
# textdomain: vessels
Vessels Shelf=Fartygshylla
Empty Glass Bottle=Tom Glasflska
Empty Drinking Glass=Tom Drycksflaska
Empty Heavy Steel Bottle=Tom Tung Stål Flaska
Glass Fragments=Glasbitar
Empty Vessels Shelf=Tom Fartygshylla
Vessels Shelf (@1 items)=Fartygshylla (@1 saker)


code:
# textdomain: vessels
Vessels Shelf=容器架
Empty Glass Bottle=空玻璃瓶
Empty Drinking Glass=空水杯
Empty Heavy Steel Bottle=空重型钢瓶
Glass Fragments=玻璃碎片
Empty Vessels Shelf=空容器架
Vessels Shelf (@1 items)=容器架（@1项）


code:
# textdomain: vessels
Vessels Shelf=容器架
Empty Glass Bottle=空玻璃瓶
Empty Drinking Glass=空水杯
Empty Heavy Steel Bottle=空重型鋼瓶
Glass Fragments=玻璃碎片
Empty Vessels Shelf=空容器架
Vessels Shelf (@1 items)=容器架（@1項）


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


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

Authors of source code
----------------------
Auke Kok <sofar@foo-projects.org> (LGPLv2.1+)


code:
-- walls/init.lua

walls = {}

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

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

walls.register = function(wall_name, wall_desc, wall_texture_table, wall_mat, wall_sounds)
	--make wall_texture_table paramenter backwards compatible for mods passing single texture
	if type(wall_texture_table) ~= "table" then
		wall_texture_table = { wall_texture_table }
	end
	-- inventory node, and pole-type wall start item
	minetest.register_node(wall_name, {
		description = wall_desc,
		drawtype = "nodebox",
		node_box = {
			type = "connected",
			fixed = {-1/4, -1/2, -1/4, 1/4, 1/2, 1/4},
			-- connect_bottom =
			connect_front = {-3/16, -1/2, -1/2,  3/16, 3/8, -1/4},
			connect_left = {-1/2, -1/2, -3/16, -1/4, 3/8,  3/16},
			connect_back = {-3/16, -1/2,  1/4,  3/16, 3/8,  1/2},
			connect_right = { 1/4, -1/2, -3/16,  1/2, 3/8,  3/16},
		},
		collision_box = {
			type = "connected",
			fixed = {-1/4, -1/2, -1/4, 1/4, 1/2 + fence_collision_extra, 1/4},
			-- connect_top =
			-- connect_bottom =
			connect_front = {-1/4,-1/2,-1/2,1/4,1/2 + fence_collision_extra,-1/4},
			connect_left = {-1/2,-1/2,-1/4,-1/4,1/2 + fence_collision_extra,1/4},
			connect_back = {-1/4,-1/2,1/4,1/4,1/2 + fence_collision_extra,1/2},
			connect_right = {1/4,-1/2,-1/4,1/2,1/2 + fence_collision_extra,1/4},
		},
		connects_to = { "group:wall", "group:stone", "group:fence" },
		paramtype = "light",
		is_ground_content = false,
		tiles = wall_texture_table,
		walkable = true,
		groups = { cracky = 3, wall = 1, stone = 2 },
		sounds = wall_sounds,
	})

	-- crafting recipe
	minetest.register_craft({
		output = wall_name .. " 6",
		recipe = {
			{ "", "", "" },
			{ wall_mat, wall_mat, wall_mat},
			{ wall_mat, wall_mat, wall_mat},
		}
	})

end

walls.register("walls:cobble", S("Cobblestone Wall"), {"default_cobble.png"},
		"default:cobble", default.node_sound_stone_defaults())

walls.register("walls:mossycobble", S("Mossy Cobblestone Wall"), {"default_mossycobble.png"},
		"default:mossycobble", default.node_sound_stone_defaults())

walls.register("walls:desertcobble", S("Desert Cobblestone Wall"), {"default_desert_cobble.png"},
		"default:desert_cobble", default.node_sound_stone_defaults())


code:
# textdomain: walls
Cobblestone Wall=
Mossy Cobblestone Wall=
Desert Cobblestone Wall=


code:
# textdomain: walls
Cobblestone Wall=Kopfsteinpflastermauer
Mossy Cobblestone Wall=Mosige Kopfsteinpflastermauer
Desert Cobblestone Wall=Wüstenkopfsteinpflastermauer


code:
# textdomain: walls
Cobblestone Wall=Pared de adoquines
Mossy Cobblestone Wall=Pared de adoquines musgosos
Desert Cobblestone Wall=Pared de adoquines desérticos


code:
# textdomain: walls
Cobblestone Wall=Mur en pavé
Mossy Cobblestone Wall=Mur en pavé moussu
Desert Cobblestone Wall=Mur en pavé du désert


code:
# textdomain: walls
Cobblestone Wall=Tembok Bongkahan Batu
Mossy Cobblestone Wall=Tembok Bongkahan Batu Berlumut
Desert Cobblestone Wall=Tembok Bongkahan Batu Gurun


code:
# textdomain: walls
Cobblestone Wall=Muro di ciottoli
Mossy Cobblestone Wall=Muro di ciottoli muschiosi
Desert Cobblestone Wall=Muro di ciottoli del deserto


code:
# textdomain: walls
Cobblestone Wall=Pagar Batu Buntar
Mossy Cobblestone Wall=Pagar Batu Buntar Berlumut
Desert Cobblestone Wall=Pagar Batu Buntar Gurun


code:
# textdomain: walls
Cobblestone Wall=Булыжниковая Ограда
Mossy Cobblestone Wall=Мшистая Булыжниковая Ограда
Desert Cobblestone Wall=Ограда Из Пустынного Булыжника


code:
# textdomain: walls
Cobblestone Wall=Kullerstens Vägg
Mossy Cobblestone Wall=Mossig Kullerstens Vägg
Desert Cobblestone Wall=Öken Kullerstens Vägg


code:
# textdomain: walls
Cobblestone Wall=鹅卵石墙
Mossy Cobblestone Wall=苔藓覆盖的鹅卵石墙
Desert Cobblestone Wall=沙漠鹅卵石墙


code:
# textdomain: walls
Cobblestone Wall=鵝卵石牆
Mossy Cobblestone Wall=苔蘚覆蓋的鵝卵石牆
Desert Cobblestone Wall=沙漠鵝卵石牆


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


code:
Minetest Game mod: weather
==========================
See license.txt for license information.
Source code by paramat (MIT).


code:
-- Disable by mapgen or setting

local mg_name = minetest.get_mapgen_setting("mg_name")
if mg_name == "v6" or mg_name == "singlenode" or
		minetest.settings:get_bool("enable_weather") == false then
	return
end

-- Parameters

local TSCALE = 600 -- Time scale of noise variation in seconds
local CYCLE = 8 -- Time period of cyclic clouds update in seconds

local np_density = {
	offset = 0.5,
	scale = 0.5,
	spread = {x = TSCALE, y = TSCALE, z = TSCALE},
	seed = 813,
	octaves = 1,
	persist = 0,
	lacunarity = 2,
}

local np_thickness = {
	offset = 0.5,
	scale = 0.5,
	spread = {x = TSCALE, y = TSCALE, z = TSCALE},
	seed = 96,
	octaves = 1,
	persist = 0,
	lacunarity = 2,
}

local np_speedx = {
	offset = 0,
	scale = 1,
	spread = {x = TSCALE, y = TSCALE, z = TSCALE},
	seed = 911923,
	octaves = 1,
	persist = 0,
	lacunarity = 2,
}

local np_speedz = {
	offset = 0,
	scale = 1,
	spread = {x = TSCALE, y = TSCALE, z = TSCALE},
	seed = 5728,
	octaves = 1,
	persist = 0,
	lacunarity = 2,
}

-- End parameters

-- Initialise noise objects to nil

local nobj_density = nil
local nobj_thickness = nil
local nobj_speedx = nil
local nobj_speedz = nil

-- Update clouds function

local function rangelim(value, lower, upper)
	return math.min(math.max(value, lower), upper)
end

local os_time_0 = os.time()
local t_offset = math.random(0, 300000)

local function update_clouds()
	-- Time in seconds.
	-- Add random time offset to avoid identical behaviour each server session.
	local time = os.difftime(os.time(), os_time_0) - t_offset

	nobj_density = nobj_density or minetest.get_perlin(np_density)
	nobj_thickness = nobj_thickness or minetest.get_perlin(np_thickness)
	nobj_speedx = nobj_speedx or minetest.get_perlin(np_speedx)
	nobj_speedz = nobj_speedz or minetest.get_perlin(np_speedz)

	local n_density = nobj_density:get_2d({x = time, y = 0}) -- 0 to 1
	local n_thickness = nobj_thickness:get_2d({x = time, y = 0}) -- 0 to 1
	local n_speedx = nobj_speedx:get_2d({x = time, y = 0}) -- -1 to 1
	local n_speedz = nobj_speedz:get_2d({x = time, y = 0}) -- -1 to 1

	for _, player in ipairs(minetest.get_connected_players()) do
		local humid = minetest.get_humidity(player:get_pos())
		-- Default and classic density value is 0.4, make this happen
		-- at humidity midvalue 50 when n_density is at midvalue 0.5.
		-- density_max = 0.25 at humid = 0.
		-- density_max = 0.8 at humid = 50.
		-- density_max = 1.35 at humid = 100.
		local density_max = 0.8 + ((humid - 50) / 50) * 0.55
		player:set_clouds({
			-- Range limit density_max to always have occasional
			-- small scattered clouds at extreme low humidity.
			density = rangelim(density_max, 0.2, 1.0) * n_density,
			thickness = math.max(math.floor(
				rangelim(32 * humid / 100, 8, 32) * n_thickness
				), 2),
			speed = {x = n_speedx * 4, z = n_speedz * 4},
		})
	end
end

local function cyclic_update()
	update_clouds()
	minetest.after(CYCLE, cyclic_update)
end

minetest.after(0, cyclic_update)

-- Update on player join to instantly alter clouds from the default

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


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


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

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

Authors of media (textures)
---------------------------
Cisoun (CC BY-SA 3.0):
  wool_black.png wool_brown.png wool_dark_green.png wool_green.png
  wool_magenta.png wool_pink.png wool_violet.png wool_yellow.png
  wool_blue.png wool_cyan.png wool_dark_grey.png wool_grey.png
  wool_orange.png wool_red.png wool_white.png


code:
-- wool/init.lua

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

local dyes = dye.dyes

for i = 1, #dyes do
	local name, desc = unpack(dyes[i])

	minetest.register_node("wool:" .. name, {
		description = S(desc .. " Wool"),
		tiles = {"wool_" .. name .. ".png"},
		is_ground_content = false,
		groups = {snappy = 2, choppy = 2, oddly_breakable_by_hand = 3,
				flammable = 3, wool = 1},
		sounds = default.node_sound_defaults(),
	})

	minetest.register_craft{
		type = "shapeless",
		output = "wool:" .. name,
		recipe = {"group:dye,color_" .. name, "group:wool"},
	}
end

-- Legacy
-- Backwards compatibility with jordach's 16-color wool mod
minetest.register_alias("wool:dark_blue", "wool:blue")
minetest.register_alias("wool:gold", "wool:yellow")

-- 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].." Wool")) end

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


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


code:
# textdomain: wool
White Wool=Weiße Wolle
Grey Wool=Graue Wolle
Dark Grey Wool=Dunkelgraue Wolle
Black Wool=Schwarze Wolle
Violet Wool=Violette Wolle
Blue Wool=Blaue Wolle
Cyan Wool=Türkise Wolle
Dark Green Wool=Dunkelgrüne Wolle
Green Wool=Grüne Wolle
Yellow Wool=Gelbe Wolle
Brown Wool=Braune Wolle
Orange Wool=Orange Wolle
Red Wool=Rote Wolle
Magenta Wool=Magenta Wolle
Pink Wool=Rosa Wolle


code:
# textdomain: wool
White Wool=Lana blanca
Grey Wool=Lana gris
Dark Grey Wool=Lana gris oscuro
Black Wool=Lana negra
Violet Wool=Lana violeta
Blue Wool=Lana azul
Cyan Wool=Lana cián
Dark Green Wool=Lana verde oscuro
Green Wool=Lana verde
Yellow Wool=Lana amarilla
Brown Wool=Lana marrón
Orange Wool=Lana naranja
Red Wool=Lana roja
Magenta Wool=Lana magenta
Pink Wool=Lana rosa


code:
# textdomain: wool
White Wool=Laine blanche
Grey Wool=Laine grise
Dark Grey Wool=Laine grise foncée
Black Wool=Laine noire
Violet Wool=Laine violette
Blue Wool=Laine bleue
Cyan Wool=Laine cyan
Dark Green Wool=Laine verte foncée
Green Wool=Laine verte
Yellow Wool=Laine jaune
Brown Wool=Laine marron
Orange Wool=Laine orange
Red Wool=Laine rouge
Magenta Wool=Laine magenta
Pink Wool=Laine rose


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


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


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


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


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


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


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


code:
name = wool
description = Minetest Game mod: wool
depends = default, dye


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

Authors of source code
----------------------
Originally by xyz (MIT)
BlockMen (MIT)
sofar (MIT)
Various Minetest developers and contributors (MIT)

Authors of media (textures)
---------------------------
xyz (CC BY-SA 3.0):
  All textures not mentioned below.

Gambit (CC BY-SA 3.0):
  xpanes_bar.png

paramat (CC BY-SA 3.0):
  xpanes_bar_top.png

Krock (CC0 1.0):
  xpanes_edge.png

TumeniNodes (CC BY-SA 3.0):
  xpanes_door_steel_bar.png
  xpanes_item_steel_bar.png
  xpanes_trapdoor_steel_bar.png
  xpanes_trapdoor_steel_bar_side.png
  xpanes_steel_bar_door_close.ogg
  xpanes_steel_bar_door_open.ogg


code:
-- xpanes/init.lua

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

local function is_pane(pos)
	return minetest.get_item_group(minetest.get_node(pos).name, "pane") > 0
end

local function connects_dir(pos, name, dir)
	local aside = vector.add(pos, minetest.facedir_to_dir(dir))
	if is_pane(aside) then
		return true
	end

	local connects_to = minetest.registered_nodes[name].connects_to
	if not connects_to then
		return false
	end
	local list = minetest.find_nodes_in_area(aside, aside, connects_to)

	if #list > 0 then
		return true
	end

	return false
end

local function swap(pos, node, name, param2)
	if node.name == name and node.param2 == param2 then
		return
	end

	minetest.swap_node(pos, {name = name, param2 = param2})
end

local function update_pane(pos)
	if not is_pane(pos) then
		return
	end
	local node = minetest.get_node(pos)
	local name = node.name
	if name:sub(-5) == "_flat" then
		name = name:sub(1, -6)
	end

	local any = node.param2
	local c = {}
	local count = 0
	for dir = 0, 3 do
		c[dir] = connects_dir(pos, name, dir)
		if c[dir] then
			any = dir
			count = count + 1
		end
	end

	if count == 0 then
		swap(pos, node, name .. "_flat", any)
	elseif count == 1 then
		swap(pos, node, name .. "_flat", (any + 1) % 4)
	elseif count == 2 then
		if (c[0] and c[2]) or (c[1] and c[3]) then
			swap(pos, node, name .. "_flat", (any + 1) % 4)
		else
			swap(pos, node, name, 0)
		end
	else
		swap(pos, node, name, 0)
	end
end

minetest.register_on_placenode(function(pos, node)
	if minetest.get_item_group(node, "pane") then
		update_pane(pos)
	end
	for i = 0, 3 do
		local dir = minetest.facedir_to_dir(i)
		update_pane(vector.add(pos, dir))
	end
end)

minetest.register_on_dignode(function(pos)
	for i = 0, 3 do
		local dir = minetest.facedir_to_dir(i)
		update_pane(vector.add(pos, dir))
	end
end)

xpanes = {}
function xpanes.register_pane(name, def)
	for i = 1, 15 do
		minetest.register_alias("xpanes:" .. name .. "_" .. i, "xpanes:" .. name .. "_flat")
	end

	local flatgroups = table.copy(def.groups)
	flatgroups.pane = 1
	minetest.register_node(":xpanes:" .. name .. "_flat", {
		description = def.description,
		drawtype = "nodebox",
		paramtype = "light",
		is_ground_content = false,
		sunlight_propagates = true,
		inventory_image = def.inventory_image,
		wield_image = def.wield_image,
		paramtype2 = "facedir",
		tiles = {
			def.textures[3],
			def.textures[3],
			def.textures[3],
			def.textures[3],
			def.textures[1],
			def.textures[1]
		},
		groups = flatgroups,
		drop = "xpanes:" .. name .. "_flat",
		sounds = def.sounds,
		use_texture_alpha = def.use_texture_alpha or false,
		node_box = {
			type = "fixed",
			fixed = {{-1/2, -1/2, -1/32, 1/2, 1/2, 1/32}},
		},
		selection_box = {
			type = "fixed",
			fixed = {{-1/2, -1/2, -1/32, 1/2, 1/2, 1/32}},
		},
		connect_sides = { "left", "right" },
	})

	local groups = table.copy(def.groups)
	groups.pane = 1
	groups.not_in_creative_inventory = 1
	minetest.register_node(":xpanes:" .. name, {
		drawtype = "nodebox",
		paramtype = "light",
		is_ground_content = false,
		sunlight_propagates = true,
		description = def.description,
		tiles = {
			def.textures[3],
			def.textures[3],
			def.textures[1]
		},
		groups = groups,
		drop = "xpanes:" .. name .. "_flat",
		sounds = def.sounds,
		use_texture_alpha = def.use_texture_alpha or false,
		node_box = {
			type = "connected",
			fixed = {{-1/32, -1/2, -1/32, 1/32, 1/2, 1/32}},
			connect_front = {{-1/32, -1/2, -1/2, 1/32, 1/2, -1/32}},
			connect_left = {{-1/2, -1/2, -1/32, -1/32, 1/2, 1/32}},
			connect_back = {{-1/32, -1/2, 1/32, 1/32, 1/2, 1/2}},
			connect_right = {{1/32, -1/2, -1/32, 1/2, 1/2, 1/32}},
		},
		connects_to = {"group:pane", "group:stone", "group:glass", "group:wood", "group:tree"},
	})

	minetest.register_craft({
		output = "xpanes:" .. name .. "_flat 16",
		recipe = def.recipe
	})
end

xpanes.register_pane("pane", {
	description = S("Glass Pane"),
	textures = {"default_glass.png", "", "xpanes_edge.png"},
	inventory_image = "default_glass.png",
	wield_image = "default_glass.png",
	sounds = default.node_sound_glass_defaults(),
	groups = {snappy=2, cracky=3, oddly_breakable_by_hand=3},
	recipe = {
		{"default:glass", "default:glass", "default:glass"},
		{"default:glass", "default:glass", "default:glass"}
	}
})

xpanes.register_pane("obsidian_pane", {
	description = S("Obsidian Glass Pane"),
	textures = {"default_obsidian_glass.png", "", "xpanes_edge_obsidian.png"},
	inventory_image = "default_obsidian_glass.png",
	wield_image = "default_obsidian_glass.png",
	sounds = default.node_sound_glass_defaults(),
	groups = {snappy=2, cracky=3},
	recipe = {
		{"default:obsidian_glass", "default:obsidian_glass", "default:obsidian_glass"},
		{"default:obsidian_glass", "default:obsidian_glass", "default:obsidian_glass"}
	}
})

xpanes.register_pane("bar", {
	description = S("Steel Bars"),
	textures = {"xpanes_bar.png", "", "xpanes_bar_top.png"},
	inventory_image = "xpanes_bar.png",
	wield_image = "xpanes_bar.png",
	groups = {cracky=2},
	sounds = default.node_sound_metal_defaults(),
	recipe = {
		{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
		{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}
	}
})

minetest.register_lbm({
	name = "xpanes:gen2",
	nodenames = {"group:pane"},
	action = function(pos, node)
		update_pane(pos)
		for i = 0, 3 do
			local dir = minetest.facedir_to_dir(i)
			update_pane(vector.add(pos, dir))
		end
	end
})

-- Register steel bar doors and trapdoors

if minetest.get_modpath("doors") then

	doors.register("xpanes:door_steel_bar", {
		tiles = {{name = "xpanes_door_steel_bar.png", backface_culling = true}},
		description = S("Steel Bar Door"),
		inventory_image = "xpanes_item_steel_bar.png",
		protected = true,
		groups = {node = 1, cracky = 1, level = 2},
		sounds = default.node_sound_metal_defaults(),
		sound_open = "xpanes_steel_bar_door_open",
		sound_close = "xpanes_steel_bar_door_close",
		recipe = {
			{"xpanes:bar_flat", "xpanes:bar_flat"},
			{"xpanes:bar_flat", "xpanes:bar_flat"},
			{"xpanes:bar_flat", "xpanes:bar_flat"},
		},
	})

	doors.register_trapdoor("xpanes:trapdoor_steel_bar", {
		description = S("Steel Bar Trapdoor"),
		inventory_image = "xpanes_trapdoor_steel_bar.png",
		wield_image = "xpanes_trapdoor_steel_bar.png",
		tile_front = "xpanes_trapdoor_steel_bar.png",
		tile_side = "xpanes_trapdoor_steel_bar_side.png",
		protected = true,
		groups = {node = 1, cracky = 1, level = 2, door = 1},
		sounds = default.node_sound_metal_defaults(),
		sound_open = "xpanes_steel_bar_door_open",
		sound_close = "xpanes_steel_bar_door_close",
	})

	minetest.register_craft({
		output = "xpanes:trapdoor_steel_bar",
		recipe = {
			{"xpanes:bar_flat", "xpanes:bar_flat"},
			{"xpanes:bar_flat", "xpanes:bar_flat"},
		}
	})
end


code:
# textdomain: xpanes
Glass Pane=
Obsidian Glass Pane=
Steel Bars=
Steel Bar Door=
Steel Bar Trapdoor=


code:
# textdomain: xpanes
Glass Pane=Glasscheibe
Obsidian Glass Pane=Obsidianglasscheibe
Steel Bars=Stahlgitter
Steel Bar Door=Stahlgittertür
Steel Bar Trapdoor=Stahlgitterfalltür


code:
# textdomain: xpanes
Glass Pane=Panel de vidrio
Obsidian Glass Pane=Panel de vidrio de obsidiana
Steel Bars=Barras de acero
Steel Bar Door=Puerta de barras de acero
Steel Bar Trapdoor=Trampilla de barras de acero


code:
# textdomain: xpanes
Glass Pane=Panneau de verre
Obsidian Glass Pane=Panneau de verre d'obsidienne
Steel Bars=Barreaux d'acier
Steel Bar Door=Porte en barreaux d'acier
Steel Bar Trapdoor=Trappe en barreaux d'acier


code:
# textdomain: xpanes
Glass Pane=Panel Kaca
Obsidian Glass Pane=Panel Kaca Obsidian
Steel Bars=Batang Baja
Steel Bar Door=Pintu Batang Baja
Steel Bar Trapdoor=Pintu Kolong Batang Baja


code:
# textdomain: xpanes
Glass Pane=Pannello di vetro
Obsidian Glass Pane=Pannello di vetro d'ossidiana
Steel Bars=Sbarre d'acciaio
Steel Bar Door=Porta con sbarre d'acciaio
Steel Bar Trapdoor=Botola con sbarre d'acciaio


code:
# textdomain: xpanes
Glass Pane=Kaca Tingkap
Obsidian Glass Pane=Kaca Tingkap Obsidia
Steel Bars=Jeriji Keluli
Steel Bar Door=Pintu Jeriji Keluli
Steel Bar Trapdoor=Pintu Kolong Jeriji Keluli


code:
# textdomain: xpanes
Glass Pane=Стеклянная Панель
Obsidian Glass Pane=Стеклянная Панель Из Обсидиана
Steel Bars=Стальная Решётка
Steel Bar Door=Стальная Решётчатая Дверь
Steel Bar Trapdoor=Стальной Решётчатый Люк


code:
# textdomain: xpanes
Glass Pane=Glasruta
Obsidian Glass Pane=Obsidian Glasruta
Steel Bars=Stålgaller
Steel Bar Door=Stålgallers Dörr
Steel Bar Trapdoor=Stålgallers Fallucka


code:
# textdomain: xpanes
Glass Pane=玻璃窗
Obsidian Glass Pane=黑曜石玻璃窗
Steel Bars=钢筋
Steel Bar Door=钢筋门
Steel Bar Trapdoor=钢筋活板门


code:
# textdomain: xpanes
Glass Pane=玻璃窗
Obsidian Glass Pane=黑曜石玻璃窗
Steel Bars=鋼筋
Steel Bar Door=鋼筋門
Steel Bar Trapdoor=鋼筋活板門


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


code:
name: integration-test

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1

    - name: integration-test
      run: ./integration-test.sh


code:
name: luacheck

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: apt
      run: sudo apt-get install -y luarocks
    - name: luacheck install
      run: luarocks install --local luacheck
    - name: luacheck run
      run: $HOME/.luarocks/bin/luacheck ./


code:
## Generic ignorable patterns and files
*~
.*.swp
*bak*
tags
*.vim
armor.conf

## Eclipse project files & directories
.project
.settings


code:
unused_args = false

globals = {
	"wieldview",
	"armor",
	"armor_i18n",
	"inventory_plus"
}

read_globals = {
	-- Stdlib
	string = {fields = {"split"}},
	table = {fields = {"copy", "getn"}},

	-- Minetest
	"vector", "ItemStack",
	"dump", "VoxelArea",

	-- deps
	"default",
	"minetest",
	"unified_inventory",
	"intllib",
	"wardrobe",
	"player_monoids",
	"armor_monoid",
	"sfinv",
	"ARMOR_MATERIALS",
	"ARMOR_FIRE_NODES",
	"pova",
	"skins",
	"u_skins"
}


code:
[mod] Visible Player Armor [3d_armor]
=====================================

Depends: default

Recommends: sfinv, unified_inventory or smart_inventory (use only one to avoid conflicts)

Supports: player_monoids and armor_monoid

Adds craftable armor that is visible to other players. Each armor item worn contributes to
a player's armor group level making them less vulnerable to weapons.

Armor takes damage when a player is hurt but also offers a percentage chance of healing.
Overall level is boosted by 10% when wearing a full matching set.

Fire protection added by TenPlus1 when using crystal armor if Ethereal mod active, level 1
protects against torches, level 2 for crystal spike, level 3 for fire, level 5 for lava.

Armor Configuration
-------------------

Override the following default settings by adding them to your minetest.conf file.

-- Set false to disable individual armor materials.
armor_material_wood = true
armor_material_cactus = true
armor_material_steel = true
armor_material_bronze = true
armor_material_diamond = true
armor_material_gold = true
armor_material_mithril = true
armor_material_crystal = true

-- Increase this if you get initialization glitches when a player first joins.
armor_init_delay = 2

-- Number of initialization attempts.
-- Use in conjunction with armor_init_delay if initialization problems persist.
armor_init_times = 10

-- Increase this if armor is not getting into bones due to server lag.
armor_bones_delay = 1

-- How often player armor items are updated.
armor_update_time = 1

-- Drop armor when a player dies.
-- Uses bones mod if present, otherwise items are dropped around the player.
armor_drop = true

-- Pulverise armor when a player dies, overrides armor_drop.
armor_destroy = false

-- You can use this to increase or decrease overall armor effectiveness,
-- eg: level_multiplier = 0.5 will reduce armor level by half.
armor_level_multiplier = 1

-- You can use this to increase or decrease overall armor healing,
-- eg: armor_heal_multiplier = 0 will disable healing altogether.
armor_heal_multiplier = 1

-- Enable water protection (periodically restores breath when activated)
armor_water_protect = true

-- Enable fire protection (defaults true if using ethereal mod)
armor_fire_protect = false

-- Enable punch damage effects.
armor_punch_damage = true

-- Enable migration of old armor inventories
armor_migrate_old_inventory = true

API
---

Armor Registration:

armor:register_armor(name, def)

Wrapper function for `minetest.register_tool`, while registering armor as
a tool item is still supported, this may be deprecated in future so new code
should use this method.

Additional fields supported by 3d_armor:

	texture = <filename>
	preview = <filename>
	armor_groups =

	damage_groups =

	reciprocate_damage = <bool>
	on_equip = <function>
	on_unequip = <function>
	on_destroy = <function>
	on_damage = <function>
	on_punched = <function>

armor:register_armor_group(group, base)

Example:

armor:register_armor_group("radiation", 100)

armor:register_armor("mod_name:speed_boots", {
	description = "Speed Boots",
	inventory_image = "mod_name_speed_boots_inv.png",
	texture = "mod_name_speed_boots.png",
	preview = "mod_name_speed_boots_preview.png",
	groups = {armor_feet=1, armor_use=500, physics_speed=1.2, flammable=1},
	armor_groups = {fleshy=10, radiation=10},
	damage_groups = {cracky=3, snappy=3, choppy=3, crumbly=3, level=1},
	reciprocate_damage = true,
	on_destroy = function(player, index, stack)
		local pos = player:get_pos()
		if pos then
			minetest.sound_play({
				name = "mod_name_break_sound",
				pos = pos,
				gain = 0.5,
			})
		end
	end,
})

See armor.lua, technic_armor and shields mods for more examples.

Default groups:

Elements: armor_head, armor_torso, armor_legs, armor_feet
Attributes: armor_heal, armor_fire, armor_water
Physics: physics_jump, physics_speed, physics_gravity
Durability: armor_use, flammable

Notes:

Elements may be modified by dependent mods, eg shields adds armor_shield.
Attributes and physics values are 'stackable', durability is determined
by the level of armor_use, total uses == approx (65535/armor_use), non-fleshy
damage groups need to be defined in the tool/weapon used against the player.

Reciprocal tool damage will be done only by the first armor inventory item
 with `reciprocate_damage = true`

Armor Functions:

armor:set_player_armor(player)

Primarily an internal function but can be called externally to apply any
changes that might not otherwise get handled.

armor:punch(player, hitter, time_from_last_punch, tool_capabilities)

Used to apply damage to all equipped armor based on the damage groups of
each individual item.`hitter`, `time_from_last_punch` and `tool_capabilities`
are optional but should be valid if included.

armor:damage(player, index, stack, use)

Adds wear to a single armor itemstack, triggers `on_damage` callbacks and
updates the necessary inventories. Also handles item destruction callbacks
and so should NOT be called from `on_unequip` to avoid an infinite loop.

Item Callbacks:

on_equip = func(player, index, stack)
on_unequip = func(player, index, stack)
on_destroy = func(player, index, stack)
on_damage = func(player, index, stack)
on_punched = func(player, hitter, time_from_last_punch, tool_capabilities)

Notes:

`on_punched` is called every time a player is punched or takes damage, `hitter`,
`time_from_last_punch` and `tool_capabilities` can be `nil` and will be in the
case of fall damage, etc. When fire protection is enabled, hitter == "fire"
in the event of fire damage. Return `false` to override armor damage effects.
When armor is destroyed `stack` will contain a copy of the previous stack.

Global Callbacks:

armor:register_on_update(func(player))
armor:register_on_equip(func(player, index, stack))
armor:register_on_unequip(func(player, index, stack))
armor:register_on_destroy(func(player, index, stack))

Global Callback Example:

armor:register_on_update(function(player)
	print(player:get_player_name().." armor updated!")
end)


code:
-- support for i18n
local S = armor_i18n.gettext

local skin_previews = {}
local use_player_monoids = minetest.global_exists("player_monoids")
local use_armor_monoid = minetest.global_exists("armor_monoid")
local use_pova_mod = minetest.get_modpath("pova")
local armor_def = setmetatable({}, {
	__index = function()
		return setmetatable({
			groups = setmetatable({}, {
				__index = function()
					return 0
				end})
			}, {
			__index = function()
				return 0
			end
		})
	end,
})
local armor_textures = setmetatable({}, {
	__index = function()
		return setmetatable({}, {
			__index = function()
				return "blank.png"
			end
		})
	end
})

armor = {
	timer = 0,
	elements = {"head", "torso", "legs", "feet"},
	physics = {"jump", "speed", "gravity"},
	attributes = {"heal", "fire", "water"},
	formspec = "image[2.5,0;2,4;armor_preview]"..
		default.gui_bg..
		default.gui_bg_img..
		default.gui_slots..
		default.get_hotbar_bg(0, 4.7)..
		"list[current_player;main;0,4.7;8,1;]"..
		"list[current_player;main;0,5.85;8,3;8]",
	def = armor_def,
	textures = armor_textures,
	default_skin = "character",
	materials = {
		wood = "group:wood",
		cactus = "default:cactus",
		steel = "default:steel_ingot",
		bronze = "default:bronze_ingot",
		diamond = "default:diamond",
		gold = "default:gold_ingot",
		mithril = "moreores:mithril_ingot",
		crystal = "ethereal:crystal_ingot",
	},
	fire_nodes = {
		{"default:lava_source",     5, 8},
		{"default:lava_flowing",    5, 8},
		{"fire:basic_flame",        3, 4},
		{"fire:permanent_flame",    3, 4},
		{"ethereal:crystal_spike",  2, 1},
		{"ethereal:fire_flower",    2, 1},
		{"default:torch",           1, 1},
		{"default:torch_ceiling",   1, 1},
		{"default:torch_wall",      1, 1},
	},
	registered_groups = {["fleshy"]=100},
	registered_callbacks = {
		on_update = {},
		on_equip = {},
		on_unequip = {},
		on_damage = {},
		on_destroy = {},
	},
	migrate_old_inventory = true,
	version = "0.4.13",
}

armor.config = {
	init_delay = 2,
	init_times = 10,
	bones_delay = 1,
	update_time = 1,
	drop = minetest.get_modpath("bones") ~= nil,
	destroy = false,
	level_multiplier = 1,
	heal_multiplier = 1,
	material_wood = true,
	material_cactus = true,
	material_steel = true,
	material_bronze = true,
	material_diamond = true,
	material_gold = true,
	material_mithril = true,
	material_crystal = true,
	water_protect = true,
	fire_protect = minetest.get_modpath("ethereal") ~= nil,
	punch_damage = true,
}

-- Armor Registration

armor.register_armor = function(self, name, def)
	minetest.register_tool(name, def)
end

armor.register_armor_group = function(self, group, base)
	base = base or 100
	self.registered_groups[group] = base
	if use_armor_monoid then
		armor_monoid.register_armor_group(group, base)
	end
end

-- Armor callbacks

armor.register_on_update = function(self, func)
	if type(func) == "function" then
		table.insert(self.registered_callbacks.on_update, func)
	end
end

armor.register_on_equip = function(self, func)
	if type(func) == "function" then
		table.insert(self.registered_callbacks.on_equip, func)
	end
end

armor.register_on_unequip = function(self, func)
	if type(func) == "function" then
		table.insert(self.registered_callbacks.on_unequip, func)
	end
end

armor.register_on_damage = function(self, func)
	if type(func) == "function" then
		table.insert(self.registered_callbacks.on_damage, func)
	end
end

armor.register_on_destroy = function(self, func)
	if type(func) == "function" then
		table.insert(self.registered_callbacks.on_destroy, func)
	end
end

armor.run_callbacks = function(self, callback, player, index, stack)
	if stack then
		local def = stack:get_definition() or {}
		if type(def[callback]) == "function" then
			def[callback](player, index, stack)
		end
	end
	local callbacks = self.registered_callbacks[callback]
	if callbacks then
		for _, func in pairs(callbacks) do
			func(player, index, stack)
		end
	end
end

armor.update_player_visuals = function(self, player)
	if not player then
		return
	end
	local name = player:get_player_name()
	if self.textures[name] then
		default.player_set_textures(player, {
			self.textures[name].skin,
			self.textures[name].armor,
			self.textures[name].wielditem,
		})
	end
	self:run_callbacks("on_update", player)
end

armor.set_player_armor = function(self, player)
	local name, armor_inv = self:get_valid_player(player, "[set_player_armor]")
	if not name then
		return
	end
	local state = 0
	local count = 0
	local material = {count=1}
	local preview = armor:get_preview(name)
	local texture = "3d_armor_trans.png"
	local physics = {}
	local attributes = {}
	local levels = {}
	local groups = {}
	local change = {}
	for _, phys in pairs(self.physics) do
		physics[phys] = 1
	end
	for _, attr in pairs(self.attributes) do
		attributes[attr] = 0
	end
	for group, _ in pairs(self.registered_groups) do
		change[group] = 1
		levels[group] = 0
	end
	local list = armor_inv:get_list("armor")
	if type(list) ~= "table" then
		return
	end
	for i, stack in pairs(list) do
		if stack:get_count() == 1 then
			local def = stack:get_definition()
			for _, element in pairs(self.elements) do
				if def.groups["armor_"..element] then
					if def.armor_groups then
						for group, level in pairs(def.armor_groups) do
							if levels[group] then
								levels[group] = levels[group] + level
							end
						end
					else
						local level = def.groups["armor_"..element]
						levels["fleshy"] = levels["fleshy"] + level
					end
					break
				end
				-- DEPRECATED, use armor_groups instead
				if def.groups["armor_radiation"] and levels["radiation"] then
					levels["radiation"] = def.groups["armor_radiation"]
				end
			end
			local item = stack:get_name()
			local tex = def.texture or item:gsub("%:", "_")
			tex = tex:gsub(".png$", "")
			local prev = def.preview or tex.."_preview"
			prev = prev:gsub(".png$", "")
			texture = texture.."^"..tex..".png"
			preview = preview.."^"..prev..".png"
			state = state + stack:get_wear()
			count = count + 1
			for _, phys in pairs(self.physics) do
				local value = def.groups["physics_"..phys] or 0
				physics[phys] = physics[phys] + value
			end
			for _, attr in pairs(self.attributes) do
				local value = def.groups["armor_"..attr] or 0
				attributes[attr] = attributes[attr] + value
			end
			local mat = string.match(item, "%:.+_(.+)$")
			if material.name then
				if material.name == mat then
					material.count = material.count + 1
				end
			else
				material.name = mat
			end
		end
	end
	for group, level in pairs(levels) do
		if level > 0 then
			level = level * armor.config.level_multiplier
			if material.name and material.count == #self.elements then
				level = level * 1.1
			end
		end
		local base = self.registered_groups[group]
		self.def[name].groups[group] = level
		if level > base then
			level = base
		end
		groups[group] = base - level
		change[group] = groups[group] / base
	end
	for _, attr in pairs(self.attributes) do
		local mult = attr == "heal" and self.config.heal_multiplier or 1
		self.def[name][attr] = attributes[attr] * mult
	end
	for _, phys in pairs(self.physics) do
		self.def[name][phys] = physics[phys]
	end
	if use_armor_monoid then
		armor_monoid.monoid:add_change(player, change, "3d_armor:armor")
	else
		-- Preserve immortal group (damage disabled for player)
		local immortal = player:get_armor_groups().immortal
		if immortal and immortal ~= 0 then
			groups.immortal = 1
		end
		player:set_armor_groups(groups)
	end
	if use_player_monoids then
		player_monoids.speed:add_change(player, physics.speed,
			"3d_armor:physics")
		player_monoids.jump:add_change(player, physics.jump,
			"3d_armor:physics")
		player_monoids.gravity:add_change(player, physics.gravity,
			"3d_armor:physics")
	elseif use_pova_mod then
		-- only add the changes, not the default 1.0 for each physics setting
		pova.add_override(name, "3d_armor", {
			speed = physics.speed - 1,
			jump = physics.jump - 1,
			gravity = physics.gravity - 1,
		})
		pova.do_override(player)
	else
		player:set_physics_override(physics)
	end
	self.textures[name].armor = texture
	self.textures[name].preview = preview
	self.def[name].level = self.def[name].groups.fleshy or 0
	self.def[name].state = state
	self.def[name].count = count
	self:update_player_visuals(player)
end

armor.punch = function(self, player, hitter, time_from_last_punch, tool_capabilities)
	local name, armor_inv = self:get_valid_player(player, "[punch]")
	if not name then
		return
	end
	local set_state
	local set_count
	local state = 0
	local count = 0
	local recip = true
	local default_groups = {cracky=3, snappy=3, choppy=3, crumbly=3, level=1}
	local list = armor_inv:get_list("armor")
	for i, stack in pairs(list) do
		if stack:get_count() == 1 then
			local itemname = stack:get_name()
			local use = minetest.get_item_group(itemname, "armor_use") or 0
			local damage = use > 0
			local def = stack:get_definition() or {}
			if type(def.on_punched) == "function" then
				damage = def.on_punched(player, hitter, time_from_last_punch,
					tool_capabilities) ~= false and damage == true
			end
			if damage == true and tool_capabilities then
				local damage_groups = def.damage_groups or default_groups
				local level = damage_groups.level or 0
				local groupcaps = tool_capabilities.groupcaps or {}
				local uses = 0
				damage = false
				for group, caps in pairs(groupcaps) do
					local maxlevel = caps.maxlevel or 0
					local diff = maxlevel - level
					if diff == 0 then
						diff = 1
					end
					if diff > 0 and caps.times then
						local group_level = damage_groups[group]
						if group_level then
							local time = caps.times[group_level]
							if time then
								local dt = time_from_last_punch or 0
								if dt > time / diff then
									if caps.uses then
										uses = caps.uses * math.pow(3, diff)
									end
									damage = true
									break
								end
							end
						end
					end
				end
				if damage == true and recip == true and hitter and
						def.reciprocate_damage == true and uses > 0 then
					local item = hitter:get_wielded_item()
					if item and item:get_name() ~= "" then
						item:add_wear(65535 / uses)
						hitter:set_wielded_item(item)
					end
					-- reciprocate tool damage only once
					recip = false
				end
			end
			if damage == true and hitter == "fire" then
				damage = minetest.get_item_group(itemname, "flammable") > 0
			end
			if damage == true then
				self:damage(player, i, stack, use)
				set_state = self.def[name].state
				set_count = self.def[name].count
			end
			state = state + stack:get_wear()
			count = count + 1
		end
	end
	if set_count and set_count ~= count then
		state = set_state or state
		count = set_count or count
	end
	self.def[name].state = state
	self.def[name].count = count
end

armor.damage = function(self, player, index, stack, use)
	local old_stack = ItemStack(stack)
	stack:add_wear(use)
	self:run_callbacks("on_damage", player, index, stack)
	self:set_inventory_stack(player, index, stack)
	if stack:get_count() == 0 then
		self:run_callbacks("on_unequip", player, index, old_stack)
		self:run_callbacks("on_destroy", player, index, old_stack)
		self:set_player_armor(player)
	end
end

armor.get_player_skin = function(self, name)
	if (self.skin_mod == "skins" or self.skin_mod == "simple_skins") and skins.skins[name] then
		return skins.skins[name]..".png"
	elseif self.skin_mod == "u_skins" and u_skins.u_skins[name] then
		return u_skins.u_skins[name]..".png"
	elseif self.skin_mod == "wardrobe" and wardrobe.playerSkins and wardrobe.playerSkins[name] then
		return wardrobe.playerSkins[name]
	end
	return armor.default_skin..".png"
end

armor.add_preview = function(self, preview)
	skin_previews[preview] = true
end

armor.get_preview = function(self, name)
	local preview = string.gsub(armor:get_player_skin(name), ".png", "_preview.png")
	if skin_previews[preview] then
		return preview
	end
	return "character_preview.png"
end

armor.get_armor_formspec = function(self, name, listring)
	if armor.def[name].init_time == 0 then
		return "label[0,0;Armor not initialized!]"
	end
	local formspec = armor.formspec..
		"list[detached:"..name.."_armor;armor;0,0.5;2,3;]"
	if listring == true then
		formspec = formspec.."listring[current_player;main]"..
			"listring[detached:"..name.."_armor;armor]"
	end
	formspec = formspec:gsub("armor_preview", armor.textures[name].preview)
	formspec = formspec:gsub("armor_level", armor.def[name].level)
	for _, attr in pairs(self.attributes) do
		formspec = formspec:gsub("armor_attr_"..attr, armor.def[name][attr])
	end
	for group, _ in pairs(self.registered_groups) do
		formspec = formspec:gsub("armor_group_"..group,
			armor.def[name].groups[group])
	end
	return formspec
end

armor.get_element = function(self, item_name)
	for _, element in pairs(armor.elements) do
		if minetest.get_item_group(item_name, "armor_"..element) > 0 then
			return element
		end
	end
end

armor.serialize_inventory_list = function(self, list)
	local list_table = {}
	for _, stack in ipairs(list) do
		table.insert(list_table, stack:to_string())
	end
	return minetest.serialize(list_table)
end

armor.deserialize_inventory_list = function(self, list_string)
	local list_table = minetest.deserialize(list_string)
	local list = {}
	for _, stack in ipairs(list_table or {}) do
		table.insert(list, ItemStack(stack))
	end
	return list
end

armor.load_armor_inventory = function(self, player)
	local _, inv = self:get_valid_player(player, "[load_armor_inventory]")
	if inv then
		local meta = player:get_meta()
		local armor_list_string = meta:get_string("3d_armor_inventory")
		if armor_list_string then
			inv:set_list("armor",
				self:deserialize_inventory_list(armor_list_string))
			return true
		end
	end
end

armor.save_armor_inventory = function(self, player)
	local _, inv = self:get_valid_player(player, "[save_armor_inventory]")
	if inv then
		local meta = player:get_meta()
		meta:set_string("3d_armor_inventory",
			self:serialize_inventory_list(inv:get_list("armor")))
	end
end

armor.update_inventory = function(self, player)
	-- DEPRECATED: Legacy inventory support
end

armor.set_inventory_stack = function(self, player, i, stack)
	local _, inv = self:get_valid_player(player, "[set_inventory_stack]")
	if inv then
		inv:set_stack("armor", i, stack)
		self:save_armor_inventory(player)
	end
end

armor.get_valid_player = function(self, player, msg)
	msg = msg or ""
	if not player then
		minetest.log("warning", S("3d_armor: Player reference is nil @1", msg))
		return
	end
	local name = player:get_player_name()
	if not name then
		minetest.log("warning", S("3d_armor: Player name is nil @1", msg))
		return
	end
	local inv = minetest.get_inventory({type="detached", name=name.."_armor"})
	if not inv then
		minetest.log("warning", S("3d_armor: Detached armor inventory is nil @1", msg))
		return
	end
	return name, inv
end

armor.drop_armor = function(pos, stack)
	local node = minetest.get_node_or_nil(pos)
	if node then
		local obj = minetest.add_item(pos, stack)
		if obj then
			obj:set_velocity({x=math.random(-1, 1), y=5, z=math.random(-1, 1)})
		end
	end
end


code:
-- DEPRECATED, will not be supported in future versions

-- See README.txt for new configuration options.

-- Armor Configuration (defaults)

-- You can remove any unwanted armor materials from this table.
-- Note that existing armor that is removed will show up as an unknown item.
ARMOR_MATERIALS = {
	wood = "group:wood",
	cactus = "default:cactus",
	steel = "default:steel_ingot",
	bronze = "default:bronze_ingot",
	diamond = "default:diamond",
	gold = "default:gold_ingot",
	mithril = "moreores:mithril_ingot",
	crystal = "ethereal:crystal_ingot",
}

-- Enable fire protection (defaults true if using ethereal mod)
ARMOR_FIRE_PROTECT = false

-- Fire protection nodes, (name, protection level, damage)
ARMOR_FIRE_NODES = {
	{"default:lava_source",     5, 4},
	{"default:lava_flowing",    5, 4},
	{"fire:basic_flame",        3, 4},
	{"fire:permanent_flame",    3, 4},
	{"ethereal:crystal_spike",  2, 1},
	{"ethereal:fire_flower",    2, 1},
	{"default:torch",           1, 1},
}

-- Increase this if you get initialization glitches when a player first joins.
ARMOR_INIT_DELAY = 1

-- Number of initialization attempts.
-- Use in conjunction with ARMOR_INIT_DELAY if initialization problems persist.
ARMOR_INIT_TIMES = 1

-- Increase this if armor is not getting into bones due to server lag.
ARMOR_BONES_DELAY = 1

-- How often player armor/wield items are updated.
ARMOR_UPDATE_TIME = 1

-- Drop armor when a player dies.
-- Uses bones mod if present, otherwise items are dropped around the player.
ARMOR_DROP = true

-- Pulverise armor when a player dies, overrides ARMOR_DROP.
ARMOR_DESTROY = false

-- You can use this to increase or decrease overall armor effectiveness,
-- eg: ARMOR_LEVEL_MULTIPLIER = 0.5 will reduce armor level by half.
ARMOR_LEVEL_MULTIPLIER = 1

-- You can use this to increase or decrease overall armor healing,
-- eg: ARMOR_HEAL_MULTIPLIER = 0 will disable healing altogether.
ARMOR_HEAL_MULTIPLIER = 1

-- You can use this to increase or decrease overall armor radiation protection,
-- eg: ARMOR_RADIATION_MULTIPLIER = 0 will completely disable radiation protection.
-- Note: patched technic mod is required
ARMOR_RADIATION_MULTIPLIER = 1


code:
-- support for i18n
local S = armor_i18n.gettext

armor:register_armor("3d_armor:helmet_admin", {
	description = S("Admin Helmet"),
	inventory_image = "3d_armor_inv_helmet_admin.png",
	armor_groups = {fleshy=100},
	groups = {armor_head=1, armor_heal=100, armor_use=0, armor_water=1,
			not_in_creative_inventory=1},
	on_drop = function(itemstack, dropper, pos)
		return
	end,
})

armor:register_armor("3d_armor:chestplate_admin", {
	description = S("Admin Chestplate"),
	inventory_image = "3d_armor_inv_chestplate_admin.png",
	armor_groups = {fleshy=100},
	groups = {armor_torso=1, armor_heal=100, armor_use=0,
			not_in_creative_inventory=1},
	on_drop = function(itemstack, dropper, pos)
		return
	end,
})

armor:register_armor("3d_armor:leggings_admin", {
	description = S("Admin Leggings"),
	inventory_image = "3d_armor_inv_leggings_admin.png",
	armor_groups = {fleshy=100},
	groups = {armor_legs=1, armor_heal=100, armor_use=0,
			not_in_creative_inventory=1},
	on_drop = function(itemstack, dropper, pos)
		return
	end,
})

armor:register_armor("3d_armor:boots_admin", {
	description = S("Admin Boots"),
	inventory_image = "3d_armor_inv_boots_admin.png",
	armor_groups = {fleshy=100},
	groups = {armor_feet=1, armor_heal=100, armor_use=0,
			not_in_creative_inventory=1},
	on_drop = function(itemstack, dropper, pos)
		return
	end,
})

minetest.register_alias("adminboots", "3d_armor:boots_admin")
minetest.register_alias("adminhelmet", "3d_armor:helmet_admin")
minetest.register_alias("adminchestplate", "3d_armor:chestplate_admin")
minetest.register_alias("adminleggings", "3d_armor:leggings_admin")

if armor.materials.wood then
	armor:register_armor("3d_armor:helmet_wood", {
		description = S("Wood Helmet"),
		inventory_image = "3d_armor_inv_helmet_wood.png",
		groups = {armor_head=1, armor_heal=0, armor_use=2000, flammable=1},
		armor_groups = {fleshy=5},
		damage_groups = {cracky=3, snappy=2, choppy=3, crumbly=2, level=1},
	})
	armor:register_armor("3d_armor:chestplate_wood", {
		description = S("Wood Chestplate"),
		inventory_image = "3d_armor_inv_chestplate_wood.png",
		groups = {armor_torso=1, armor_heal=0, armor_use=2000, flammable=1},
		armor_groups = {fleshy=10},
		damage_groups = {cracky=3, snappy=2, choppy=3, crumbly=2, level=1},
	})
	armor:register_armor("3d_armor:leggings_wood", {
		description = S("Wood Leggings"),
		inventory_image = "3d_armor_inv_leggings_wood.png",
		groups = {armor_legs=1, armor_heal=0, armor_use=2000, flammable=1},
		armor_groups = {fleshy=10},
		damage_groups = {cracky=3, snappy=2, choppy=3, crumbly=2, level=1},
	})
	armor:register_armor("3d_armor:boots_wood", {
		description = S("Wood Boots"),
		inventory_image = "3d_armor_inv_boots_wood.png",
		armor_groups = {fleshy=5},
		damage_groups = {cracky=3, snappy=2, choppy=3, crumbly=2, level=1},
		groups = {armor_feet=1, armor_heal=0, armor_use=2000, flammable=1},
	})
	local wood_armor_fuel = {
		helmet = 6,
		chestplate = 8,
		leggings = 7,
		boots = 5
	}
	for armor, burn in pairs(wood_armor_fuel) do
		minetest.register_craft({
			type = "fuel",
			recipe = "3d_armor:" .. armor .. "_wood",
			burntime = burn,
		})
	end
end

if armor.materials.cactus then
	armor:register_armor("3d_armor:helmet_cactus", {
		description = S("Cactus Helmet"),
		inventory_image = "3d_armor_inv_helmet_cactus.png",
		groups = {armor_head=1, armor_heal=0, armor_use=1000},
		armor_groups = {fleshy=5},
		damage_groups = {cracky=3, snappy=3, choppy=2, crumbly=2, level=1},
	})
	armor:register_armor("3d_armor:chestplate_cactus", {
		description = S("Cactus Chestplate"),
		inventory_image = "3d_armor_inv_chestplate_cactus.png",
		groups = {armor_torso=1, armor_heal=0, armor_use=1000},
		armor_groups = {fleshy=10},
		damage_groups = {cracky=3, snappy=3, choppy=2, crumbly=2, level=1},
	})
	armor:register_armor("3d_armor:leggings_cactus", {
		description = S("Cactus Leggings"),
		inventory_image = "3d_armor_inv_leggings_cactus.png",
		groups = {armor_legs=1, armor_heal=0, armor_use=1000},
		armor_groups = {fleshy=10},
		damage_groups = {cracky=3, snappy=3, choppy=2, crumbly=2, level=1},
	})
	armor:register_armor("3d_armor:boots_cactus", {
		description = S("Cactus Boots"),
		inventory_image = "3d_armor_inv_boots_cactus.png",
		groups = {armor_feet=1, armor_heal=0, armor_use=1000},
		armor_groups = {fleshy=5},
		damage_groups = {cracky=3, snappy=3, choppy=2, crumbly=2, level=1},
	})
	local cactus_armor_fuel = {
		helmet = 14,
		chestplate = 16,
		leggings = 15,
		boots = 13
	}
	for armor, burn in pairs(cactus_armor_fuel) do
		minetest.register_craft({
			type = "fuel",
			recipe = "3d_armor:" .. armor .. "_cactus",
			burntime = burn,
		})
	end
end

if armor.materials.steel then
	armor:register_armor("3d_armor:helmet_steel", {
		description = S("Steel Helmet"),
		inventory_image = "3d_armor_inv_helmet_steel.png",
		groups = {armor_head=1, armor_heal=0, armor_use=800,
			physics_speed=-0.01, physics_gravity=0.01},
		armor_groups = {fleshy=10},
		damage_groups = {cracky=2, snappy=3, choppy=2, crumbly=1, level=2},
	})
	armor:register_armor("3d_armor:chestplate_steel", {
		description = S("Steel Chestplate"),
		inventory_image = "3d_armor_inv_chestplate_steel.png",
		groups = {armor_torso=1, armor_heal=0, armor_use=800,
			physics_speed=-0.04, physics_gravity=0.04},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=2, snappy=3, choppy=2, crumbly=1, level=2},
	})
	armor:register_armor("3d_armor:leggings_steel", {
		description = S("Steel Leggings"),
		inventory_image = "3d_armor_inv_leggings_steel.png",
		groups = {armor_legs=1, armor_heal=0, armor_use=800,
			physics_speed=-0.03, physics_gravity=0.03},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=2, snappy=3, choppy=2, crumbly=1, level=2},
	})
	armor:register_armor("3d_armor:boots_steel", {
		description = S("Steel Boots"),
		inventory_image = "3d_armor_inv_boots_steel.png",
		groups = {armor_feet=1, armor_heal=0, armor_use=800,
			physics_speed=-0.01, physics_gravity=0.01},
		armor_groups = {fleshy=10},
		damage_groups = {cracky=2, snappy=3, choppy=2, crumbly=1, level=2},
	})
end

if armor.materials.bronze then
	armor:register_armor("3d_armor:helmet_bronze", {
		description = S("Bronze Helmet"),
		inventory_image = "3d_armor_inv_helmet_bronze.png",
		groups = {armor_head=1, armor_heal=6, armor_use=400,
			physics_speed=-0.01, physics_gravity=0.01},
		armor_groups = {fleshy=10},
		damage_groups = {cracky=3, snappy=2, choppy=2, crumbly=1, level=2},
	})
	armor:register_armor("3d_armor:chestplate_bronze", {
		description = S("Bronze Chestplate"),
		inventory_image = "3d_armor_inv_chestplate_bronze.png",
		groups = {armor_torso=1, armor_heal=6, armor_use=400,
			physics_speed=-0.04, physics_gravity=0.04},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=3, snappy=2, choppy=2, crumbly=1, level=2},
	})
	armor:register_armor("3d_armor:leggings_bronze", {
		description = S("Bronze Leggings"),
		inventory_image = "3d_armor_inv_leggings_bronze.png",
		groups = {armor_legs=1, armor_heal=6, armor_use=400,
			physics_speed=-0.03, physics_gravity=0.03},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=3, snappy=2, choppy=2, crumbly=1, level=2},
	})
	armor:register_armor("3d_armor:boots_bronze", {
		description = S("Bronze Boots"),
		inventory_image = "3d_armor_inv_boots_bronze.png",
		groups = {armor_feet=1, armor_heal=6, armor_use=400,
			physics_speed=-0.01, physics_gravity=0.01},
		armor_groups = {fleshy=10},
		damage_groups = {cracky=3, snappy=2, choppy=2, crumbly=1, level=2},
	})
end

if armor.materials.diamond then
	armor:register_armor("3d_armor:helmet_diamond", {
		description = S("Diamond Helmet"),
		inventory_image = "3d_armor_inv_helmet_diamond.png",
		groups = {armor_head=1, armor_heal=12, armor_use=200},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=2, snappy=1, choppy=1, level=3},
	})
	armor:register_armor("3d_armor:chestplate_diamond", {
		description = S("Diamond Chestplate"),
		inventory_image = "3d_armor_inv_chestplate_diamond.png",
		groups = {armor_torso=1, armor_heal=12, armor_use=200},
		armor_groups = {fleshy=20},
		damage_groups = {cracky=2, snappy=1, choppy=1, level=3},
	})
	armor:register_armor("3d_armor:leggings_diamond", {
		description = S("Diamond Leggings"),
		inventory_image = "3d_armor_inv_leggings_diamond.png",
		groups = {armor_legs=1, armor_heal=12, armor_use=200},
		armor_groups = {fleshy=20},
		damage_groups = {cracky=2, snappy=1, choppy=1, level=3},
	})
	armor:register_armor("3d_armor:boots_diamond", {
		description = S("Diamond Boots"),
		inventory_image = "3d_armor_inv_boots_diamond.png",
		groups = {armor_feet=1, armor_heal=12, armor_use=200},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=2, snappy=1, choppy=1, level=3},
	})
end

if armor.materials.gold then
	armor:register_armor("3d_armor:helmet_gold", {
		description = S("Gold Helmet"),
		inventory_image = "3d_armor_inv_helmet_gold.png",
		groups = {armor_head=1, armor_heal=6, armor_use=300,
			physics_speed=-0.02, physics_gravity=0.02},
		armor_groups = {fleshy=10},
		damage_groups = {cracky=1, snappy=2, choppy=2, crumbly=3, level=2},
	})
	armor:register_armor("3d_armor:chestplate_gold", {
		description = S("Gold Chestplate"),
		inventory_image = "3d_armor_inv_chestplate_gold.png",
		groups = {armor_torso=1, armor_heal=6, armor_use=300,
			physics_speed=-0.05, physics_gravity=0.05},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=1, snappy=2, choppy=2, crumbly=3, level=2},
	})
	armor:register_armor("3d_armor:leggings_gold", {
		description = S("Gold Leggings"),
		inventory_image = "3d_armor_inv_leggings_gold.png",
		groups = {armor_legs=1, armor_heal=6, armor_use=300,
			physics_speed=-0.04, physics_gravity=0.04},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=1, snappy=2, choppy=2, crumbly=3, level=2},
	})
	armor:register_armor("3d_armor:boots_gold", {
		description = S("Gold Boots"),
		inventory_image = "3d_armor_inv_boots_gold.png",
		groups = {armor_feet=1, armor_heal=6, armor_use=300,
			physics_speed=-0.02, physics_gravity=0.02},
		armor_groups = {fleshy=10},
		damage_groups = {cracky=1, snappy=2, choppy=2, crumbly=3, level=2},
	})
end

if armor.materials.mithril then
	armor:register_armor("3d_armor:helmet_mithril", {
		description = S("Mithril Helmet"),
		inventory_image = "3d_armor_inv_helmet_mithril.png",
		groups = {armor_head=1, armor_heal=12, armor_use=100},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=2, snappy=1, level=3},
	})
	armor:register_armor("3d_armor:chestplate_mithril", {
		description = S("Mithril Chestplate"),
		inventory_image = "3d_armor_inv_chestplate_mithril.png",
		groups = {armor_torso=1, armor_heal=12, armor_use=100},
		armor_groups = {fleshy=20},
		damage_groups = {cracky=2, snappy=1, level=3},
	})
	armor:register_armor("3d_armor:leggings_mithril", {
		description = S("Mithril Leggings"),
		inventory_image = "3d_armor_inv_leggings_mithril.png",
		groups = {armor_legs=1, armor_heal=12, armor_use=100},
		armor_groups = {fleshy=20},
		damage_groups = {cracky=2, snappy=1, level=3},
	})
	armor:register_armor("3d_armor:boots_mithril", {
		description = S("Mithril Boots"),
		inventory_image = "3d_armor_inv_boots_mithril.png",
		groups = {armor_feet=1, armor_heal=12, armor_use=100},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=2, snappy=1, level=3},
	})
end

if armor.materials.crystal then
	armor:register_armor("3d_armor:helmet_crystal", {
		description = S("Crystal Helmet"),
		inventory_image = "3d_armor_inv_helmet_crystal.png",
		groups = {armor_head=1, armor_heal=12, armor_use=100, armor_fire=1},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=2, snappy=1, level=3},
	})
	armor:register_armor("3d_armor:chestplate_crystal", {
		description = S("Crystal Chestplate"),
		inventory_image = "3d_armor_inv_chestplate_crystal.png",
		groups = {armor_torso=1, armor_heal=12, armor_use=100, armor_fire=1},
		armor_groups = {fleshy=20},
		damage_groups = {cracky=2, snappy=1, level=3},
	})
	armor:register_armor("3d_armor:leggings_crystal", {
		description = S("Crystal Leggings"),
		inventory_image = "3d_armor_inv_leggings_crystal.png",
		groups = {armor_legs=1, armor_heal=12, armor_use=100, armor_fire=1},
		armor_groups = {fleshy=20},
		damage_groups = {cracky=2, snappy=1, level=3},
	})
	armor:register_armor("3d_armor:boots_crystal", {
		description = S("Crystal Boots"),
		inventory_image = "3d_armor_inv_boots_crystal.png",
		groups = {armor_feet=1, armor_heal=12, armor_use=100, physics_speed=1,
				physics_jump=0.5, armor_fire=1},
		armor_groups = {fleshy=15},
		damage_groups = {cracky=2, snappy=1, level=3},
	})
end

for k, v in pairs(armor.materials) do
	minetest.register_craft({
		output = "3d_armor:helmet_"..k,
		recipe = {
			{v, v, v},
			{v, "", v},
			{"", "", ""},
		},
	})
	minetest.register_craft({
		output = "3d_armor:chestplate_"..k,
		recipe = {
			{v, "", v},
			{v, v, v},
			{v, v, v},
		},
	})
	minetest.register_craft({
		output = "3d_armor:leggings_"..k,
		recipe = {
			{v, v, v},
			{v, "", v},
			{v, "", v},
		},
	})
	minetest.register_craft({
		output = "3d_armor:boots_"..k,
		recipe = {
			{v, "", v},
			{v, "", v},
		},
	})
end


code:
3d_armor -- Crafting Guide
--------------------------

Helmets:

+---+---+---+
| X | X | X |
+---+---+---+
| X |   | X |
+---+---+---+
|   |   |   |
+---+---+---+

[3d_armor:helmet_wood] X = [default:wood]
[3d_armor:helmet_cactus] X = [default:cactus]
[3d_armor:helmet_steel] X = [default:steel_ingot]
[3d_armor:helmet_bronze] X = [default:bronze_ingot]
[3d_armor:helmet_diamond] X = [default:diamond]
[3d_armor:helmet_gold] X = [default:gold_ingot]
[3d_armor:helmet_mithril] X = [moreores:mithril_ingot] *
[3d_armor:helmet_crystal] X = [ethereal:crystal_ingot] **

Chestplates:

+---+---+---+
| X |   | X |
+---+---+---+
| X | X | X |
+---+---+---+
| X | X | X |
+---+---+---+

[3d_armor:chestplate_wood] X = [default:wood]
[3d_armor:chestplate_cactus] X = [default:cactus]
[3d_armor:chestplate_steel] X = [default:steel_ingot]
[3d_armor:chestplate_bronze] X = [default:bronze_ingot]
[3d_armor:chestplate_diamond] X = [default:diamond]
[3d_armor:chestplate_gold] X = [default:gold_ingot]
[3d_armor:chestplate_mithril] X = [moreores:mithril_ingot] *
[3d_armor:chestplate_crystal] X = [ethereal:crystal_ingot] **

Leggings:

+---+---+---+
| X | X | X |
+---+---+---+
| X |   | X |
+---+---+---+
| X |   | X |
+---+---+---+

[3d_armor:leggings_wood] X = [default:wood]
[3d_armor:leggings_cactus] X = [default:cactus]
[3d_armor:leggings_steel] X = [default:steel_ingot]
[3d_armor:leggings_bronze] X = [default:bronze_ingot]
[3d_armor:leggings_diamond] X = [default:diamond]
[3d_armor:leggings_gold] X = [default:gold_ingot]
[3d_armor:leggings_mithril] X = [moreores:mithril_ingot] *
[3d_armor:leggings_crystal] X = [ethereal:crystal_ingot] **

Boots:

+---+---+---+
| X |   | X |
+---+---+---+
| X |   | X |
+---+---+---+

[3d_armor:boots_wood] X = [default:wood]
[3d_armor:boots_cactus] X = [default:cactus]
[3d_armor:boots_steel] X = [default:steel_ingot]
[3d_armor:boots_bronze] X = [default:bronze_ingot
[3d_armor:boots_diamond] X = [default:diamond]
[3d_armor:boots_gold] X = [default:gold_ingot]
[3d_armor:boots_mithril] X = [moreores:mithril_ingot] *
[3d_armor:boots_crystal] X = [ethereal:crystal_ingot] **

 * Requires moreores mod by Calinou - https://forum.minetest.net/viewtopic.php?id=549
** Requires ethereal mod by Chinchow & TenPlus1 - https://github.com/tenplus1/ethereal


code:
default
player_monoids?
armor_monoid?
pova?
fire?
ethereal?
bakedclay?
intllib?


code: Adds craftable armor that is visible to other players.