code:
-- table for quick colors.
toolranks = {
	colors = {
		grey = minetest.get_color_escape_sequence("#9d9d9d"),
		green = minetest.get_color_escape_sequence("#1eff00"),
		gold = minetest.get_color_escape_sequence("#ffdf00"),
		white = minetest.get_color_escape_sequence("#ffffff")
	}
}

-- function to create our description of the tool.
function toolranks.create_description(name, uses, level)
	return toolranks.colors.green .. (name or "") .. "\n"
		.. toolranks.colors.gold .. "Level: " .. (level or 1) .. "\n"
		.. toolranks.colors.grey .. "Used: " .. (uses or 0) .. " times"
end

-- function to calculate levels (+150 uses per level)
function toolranks.get_level(uses)
  if uses < 50 then
    return 1
  else
    return math.floor((25 + math.sqrt(625 + 100 * uses))/50)
  end
end

-- function to run after a tool is used.
function toolranks.new_afteruse(itemstack, user, node, digparams)

	-- Get tool metadata and number of times used
	local itemmeta = itemstack:get_meta()
	local dugnodes = tonumber(itemmeta:get_string("dug")) or 0

	-- Only count nodes that spend the tool
	if digparams.wear > 0 then

		dugnodes = dugnodes + 1
		itemmeta:set_string("dug", dugnodes)
	else
		return
	end

	-- Get tool description and last level
	local itemdef   = itemstack:get_definition()
	local itemdesc  = itemdef.original_description or itemdef.description or "Tool"
	local lastlevel = tonumber(itemmeta:get_string("lastlevel")) or 1
	local name = user:get_player_name()

	-- Warn player when tool is almost broken
	if itemstack:get_wear() > 60100 then

		minetest.chat_send_player(name,
			toolranks.colors.gold .. "Your tool is almost broken!")

		minetest.sound_play("default_tool_breaks", {
			to_player = name,
			gain = 2.0,
		})
	end

	local level = toolranks.get_level(dugnodes)

	-- Alert player when tool has leveled up
	if lastlevel < level then

		minetest.chat_send_player(name, "Your "
			.. toolranks.colors.green .. itemdesc
			.. toolranks.colors.white .. " just leveled up!")

		minetest.sound_play("toolranks_levelup", {
			to_player = name,
			gain = 1.0,
		})

		itemmeta:set_string("lastlevel", level)
	end

	local newdesc = toolranks.create_description(itemdesc, dugnodes, level)

	-- Set new meta
	itemmeta:set_string("description", newdesc)

	local wear = digparams.wear

	-- Set wear level
	if level > 1 then
		wear = digparams.wear / (1 + level / 20)
	end

	itemstack:add_wear(wear)

	return itemstack
end

-- Helper function
local function add_tool(name, desc, afteruse)

	minetest.override_item(name, {
		original_description = desc,
		description = toolranks.create_description(desc, 0, 1),
		after_use = afteruse and toolranks.new_afteruse
	})
end

-- Sword
add_tool("default:sword_wood", "Wooden Sword", true)
add_tool("default:sword_stone", "Stone Sword", true)
add_tool("default:sword_steel", "Steel Sword", true)
add_tool("default:sword_bronze", "Bronze Sword", true)
add_tool("default:sword_mese", "Mese Sword", true)
add_tool("default:sword_diamond", "Diamond Sword", true)

-- Pickaxe
add_tool("default:pick_wood", "Wooden Pickaxe", true)
add_tool("default:pick_stone", "Stone Pickaxe", true)
add_tool("default:pick_steel", "Steel Pickaxe", true)
add_tool("default:pick_bronze", "Bronze Pickaxe", true)
add_tool("default:pick_mese", "Mese Pickaxe", true)
add_tool("default:pick_diamond", "Diamond Pickaxe", true)

-- Axe
add_tool("default:axe_wood", "Wooden Axe", true)
add_tool("default:axe_stone", "Stone Axe", true)
add_tool("default:axe_steel", "Steel Axe", true)
add_tool("default:axe_bronze", "Bronze Axe", true)
add_tool("default:axe_mese", "Mese Axe", true)
add_tool("default:axe_diamond", "Diamond Axe", true)

-- Shovel
add_tool("default:shovel_wood", "Wooden Shovel", true)
add_tool("default:shovel_stone", "Stone Shovel", true)
add_tool("default:shovel_steel", "Steel Shovel", true)
add_tool("default:shovel_bronze", "Bronze Shovel", true)
add_tool("default:shovel_mese", "Mese Shovel", true)
add_tool("default:shovel_diamond", "Diamond Shovel", true)


code:
name = toolranks
description = Adds ranks and buffs to tools.
depends = default
optional_depends =
author = lisacvuk
title = Tool Ranks

repo_name: korkut/MeseCraft

license: mit


code:
# wielded_light mod for Minetest

Idea taken from torches_wieldlight in https://github.com/minetest-mods/torches, but written from scratch and usable for all shining items.

![Screenshot](https://github.com/bell07/minetest-wielded_light/raw/master/screenshot.png)

All bright nodes with light value > 2 lighten the player environment if wielded, with value fewer by 2. (Torch 13->11 for example)

Dependencies: none

License: [GPL-3](https://github.com/bell07/minetest-wielded_light/blob/master/LICENSE)

Shining API:

`function wielded_light.update_light(pos, light_level)`
Enable or update the shining at pos with light_level for 0.6 seconds. Can be used in any on_step call to get other entitys shining for example

`wielded_light.register_item_light(itemname, light_level)`
Override or set custom light level to an item. This does not change the item/node definition, just the lighting in this mod.

`function wielded_light.update_light_by_item(stack, pos)`
Update light at pos using item shining settings -from registered item_light or from item definition


code:
local mod_name = minetest.get_current_modname()

-- Node replacements that emit light
-- Sets of lighting_node={ node=original_node, level=light_level }
local lighting_nodes = {}

-- The nodes that can be replaced with lighting nodes
-- Sets of original_node={ [1]=lighting_node_1, [2]=lighting_node_2, ... }
local lightable_nodes = {}

-- Prefixes used for each node so we can avoid overlap
-- Pairs of prefix=original_node
local lighting_prefixes = {}

-- node_name=true pairs of lightable nodes that are liquids and can flood some light sources
local lightable_liquids = {}

-- How often will the positions of lights be recalculated
local update_interval = 0.3

-- How long until a previously lit node should be updated - reduces flicker
local removal_delay = update_interval * 0.5

-- How often will a node attempt to check itself for deletion
local cleanup_interval = update_interval * 3

-- How far in the future will the position be projected based on the velocity
local velocity_projection = update_interval * 1

-- How many light levels should an item held in the hand be reduced by, compared to the placed node
-- does not apply to manually registered light levels
local level_delta = 2

-- item=light_level pairs of registered wielded lights
local shiny_items = {}

-- List of custom callbacks for each update step
local update_callbacks = {}
local update_player_callbacks = {}

-- position={id=light_level} sets of known about light sources and their levels by position
local active_lights = {}

--[[ Sets of entities being tracked, in the form:
entity_id = {
	obj = entity,
	items = {
		category_id..entity_id = {
			level = light_level,
			item? = item_name
		}
	},
	update = true | false,
	pos? = position_vector,
	offset? = offset_vector,
}
]]
local tracked_entities = {}

-- position=true pairs of positions that need to be recaculated this update step
local light_recalcs = {}

--[[
	Using 2-digit hex codes for categories
	Starts at 00, ends at FF
	This makes it easier extract `uid` from `cat_id..uid` by slicing off 2 characters
	The category ID must be of a fixed length (2 characters)
]]
local cat_id = 0
local cat_codes = {}
local function get_light_category_id(cat)
	-- If the category id does not already exist generate a new  one
	if not cat_codes[cat] then
		if cat_id >= 256 then
			error("Wielded item category limit exceeded, maximum 256 wield categories")
		end
		local code = string.format("%02x", cat_id)
		cat_id = cat_id+1
		cat_codes[cat] = code
	end
	-- If the category id does exist, return it
	return cat_codes[cat]
end

-- Log an error coming from this mod
local function error_log(message, ...)
	minetest.log("error", "[Wielded Light] " .. (message:format(...)))
end

-- Is a node lightable and a liquid capable of flooding some light sources
local function is_lightable_liquid(pos)
	local node = minetest.get_node_or_nil(pos)
	if not node then return end
	return lightable_liquids[node.name]
end

-- Check if an entity instance still exists in the world
local function is_entity_valid(entity)
	return entity and (entity.obj:is_player() or (entity.obj:get_luaentity() and entity.obj:get_luaentity().name) or false)
end

-- Check whether a node was registered by the wield_light mod
local function is_wieldlight_node(pos_vec)
	local name = string.sub(minetest.get_node(pos_vec).name, 1, #mod_name)
	return name == mod_name
end

-- Get the projected position of an entity based on its velocity, rounded to the nearest block
local function entity_pos(obj, offset)
	local velocity
	if (minetest.features.direct_velocity_on_players or not obj:is_player()) and obj.get_velocity then
		velocity = obj:get_velocity()
	else
		velocity = obj:get_player_velocity()
	end

	return wielded_light.get_light_position(
		vector.round(
			vector.add(
				vector.add(
					offset or { x=0, y=0, z=0 },
					obj:get_pos()
				),
				vector.multiply(
					velocity or { x=0, y=0, z=0 },
					velocity_projection
				)
			)
		)
	)
end

-- Add light to active light list and mark position for update
local function add_light(pos, id, light_level)
	if not active_lights[pos] then
		active_lights[pos] = {}
	end
	if active_lights[pos][id] ~= light_level then
		-- minetest.log("error", "add "..id.." "..pos.." "..tostring(light_level))
		active_lights[pos][id] = light_level
		light_recalcs[pos] = true
	end
end

-- Remove light from active light list and mark position for update
local function remove_light(pos, id)
	if not active_lights[pos] then return end
	-- minetest.log("error", "rem "..id.." "..pos)
	active_lights[pos][id] = nil
	minetest.after(removal_delay, function ()
		light_recalcs[pos] = true
	end)
end

-- Track an entity's position and update its light, will be called on every update step
local function update_entity(entity)
	local pos = entity_pos(entity.obj, entity.offset)
	local pos_str = pos and minetest.pos_to_string(pos)

	-- If the position has changed, remove the old light and mark the entity for update
	if entity.pos and pos_str ~= entity.pos then
		entity.update = true
		for id,_ in pairs(entity.items) do
			remove_light(entity.pos, id)
		end
	end

	-- Update the recorded position
	entity.pos = pos_str

	-- If the position is still loaded, pump the timer up so it doesn't get removed
	if pos then
		-- If the entity is marked for an update, add the light in the position if it emits light
		if entity.update then
			for id, item in pairs(entity.items) do
				if item.level > 0 and not (item.floodable and is_lightable_liquid(pos)) then
					add_light(pos_str, id, item.level)
				else
					remove_light(pos_str, id)
				end
			end
		end
	end
	if active_lights[pos_str] then
		if is_wieldlight_node(pos) then
			minetest.get_node_timer(pos):start(cleanup_interval)
		end
	end
	entity.update = false
end

-- Save the original nodes timer if it has one
local function save_timer(pos_vec)
	local timer = minetest.get_node_timer(pos_vec)
	if timer:is_started() then
		local meta = minetest.get_meta(pos_vec)
		meta:set_float("saved_timer_timeout", timer:get_timeout())
		meta:set_float("saved_timer_elapsed", timer:get_elapsed())
	end
end

-- Restore the original nodes timer if it had one
local function restore_timer(pos_vec)
	local meta = minetest.get_meta(pos_vec)
	local timeout = meta:get_float("saved_timer_timeout")
	if timeout > 0 then
		local elapsed = meta:get_float("saved_timer_elapsed")
		local timer = minetest.get_node_timer(pos_vec)
		timer:set(timeout, elapsed)
		meta:set_string("saved_timer_timeout","")
		meta:set_string("saved_timer_elapsed","")
	end
end

-- Replace a lighting node with its original counterpart
local function reset_lighting_node(pos)
	local existing_node = minetest.get_node(pos)
	local lighting_node = wielded_light.get_lighting_node(existing_node.name)
	if not lighting_node then
		return
	end
	minetest.swap_node(pos, { name = lighting_node.node,param2 = existing_node.param2 })
	restore_timer(pos)
end

-- Will be run once the node timer expires
local function cleanup_timer_callback(pos, elapsed)
	local pos_str = minetest.pos_to_string(pos)
	local lights = active_lights[pos_str]
	-- If no active lights for this position, remove itself
	if not lights then
		reset_lighting_node(pos)
	else
	-- Clean up any tracked entities for this position that no longer exist
		for id,_ in pairs(lights) do
			local uid = string.sub(id,3)
			local entity = tracked_entities[uid]
			if not is_entity_valid(entity) then
				remove_light(pos_str, id)
			end
		end
		minetest.get_node_timer(pos):start(cleanup_interval)
	end
end

-- Recalculate the total light level for a given position and update the light level there
local function recalc_light(pos)
	-- If not in active lights list we can't do anything
	if not active_lights[pos] then return end

	-- Calculate the light level of the node
	local any_light = false
	local max_light = 0
	for id, light_level in pairs(active_lights[pos]) do
		any_light = true
		if light_level > max_light then
			max_light = light_level
		end
	end

	-- Convert the position back to a vector
	local pos_vec = minetest.string_to_pos(pos)

	-- If no items in this position, delete it from the list and remove any light node
	if not any_light then
		active_lights[pos] = nil
		reset_lighting_node(pos_vec)
		return
	end

	-- If no light in this position remove any light node
	if max_light == 0 then
		reset_lighting_node(pos_vec)
		return
	end

	-- Limit the light level
	max_light = math.min(max_light, minetest.LIGHT_MAX)

	-- Get the current light level in this position
	local existing_node = minetest.get_node(pos_vec)
	local name = existing_node.name
	local old_value = wielded_light.level_of_lighting_node(name) or 0

	-- If the light level has changed, set the coresponding light node and initiate the cleanup timer
	if old_value ~= max_light then
		local node_name
		if lightable_nodes[name] then
			node_name = name
		elseif lighting_nodes[name] then
			node_name = lighting_nodes[name].node
		end
		if node_name then
			if not is_wieldlight_node(pos_vec) then
				save_timer(pos_vec)
			end

			minetest.swap_node(pos_vec, {
				name = lightable_nodes[node_name][max_light],
				param2 = existing_node.param2
			})
			minetest.get_node_timer(pos_vec):start(cleanup_interval)
		else
			active_lights[pos] = nil
		end
	end
end

local timer = 0
-- Will be run on every global step
local function global_timer_callback(dtime)
	-- Only run once per update interval, global step will be called much more often than that
	timer = timer + dtime;
	if timer < update_interval then
		return
	end
	timer = 0

	-- Run all custom player callbacks for each player
	local connected_players = minetest.get_connected_players()
	for _,callback in pairs(update_player_callbacks) do
		for _, player in pairs(connected_players) do
			callback(player)
		end
	end

	-- Run all custom callbacks
	for _,callback in pairs(update_callbacks) do
		callback()
	end

	-- Look at each tracked entity and update its position
	for uid, entity in pairs(tracked_entities) do
		if is_entity_valid(entity) then
			update_entity(entity)
		else
			-- If the entity no longer exists, stop tracking it
			tracked_entities[uid] = nil
		end
	end

	-- Recalculate light levels
	for pos,_ in pairs(light_recalcs) do
		recalc_light(pos)
	end
	light_recalcs = {}
end

--- Shining API ---
wielded_light = {}

-- Registers a callback to be called every time the update interval is passed
function wielded_light.register_lightstep(callback)
	table.insert(update_callbacks, callback)
end

-- Registers a callback to be called for each player every time the update interval is passed
function wielded_light.register_player_lightstep(callback)
	table.insert(update_player_callbacks, callback)
end

-- Returns the node name for a given light level
function wielded_light.lighting_node_of_level(light_level, prefix)
	return mod_name..":"..(prefix or "")..light_level
end

-- Gets the light level for a given node name, inverse of lighting_node_of_level
function wielded_light.level_of_lighting_node(node_name)
	local lighting_node = wielded_light.get_lighting_node(node_name)
	if lighting_node then
		return lighting_node.level
	end
end

-- Check if a node name is one of the wielded light nodes
function wielded_light.get_lighting_node(node_name)
	return lighting_nodes[node_name]
end

-- Register any node as lightable, register all light level variations for it
function wielded_light.register_lightable_node(node_name, property_overrides, custom_prefix)
	-- Node name must be string
	if type(node_name) ~= "string" then
		error_log("You must provide a node name to be registered as lightable, '%s' given.", type(node_name))
		return
	end

	-- Node must already be registered
	local original_definition = minetest.registered_nodes[node_name]
	if not original_definition then
		error_log("The node '%s' cannot be registered as lightable because it does not exist.", node_name)
		return
	end

	-- Decide the prefix for the lighting node
	local prefix = custom_prefix or node_name:gsub(":", "_", 1, true) .. "_"
	if lighting_prefixes[prefix] then
		error_log("The lighting prefix '%s' cannot be used for '%s' as it is already used for '%s'.", prefix, node_name, lighting_prefixes[prefix])
		return
	end
	lighting_prefixes[prefix] = node_name

	-- Default for property overrides
	if not property_overrides then property_overrides = {} end

	-- Copy the node definition and provide required settings for a lighting node
	local new_definition = table.copy(original_definition)
	new_definition.on_timer = cleanup_timer_callback
	new_definition.paramtype = "light"
	new_definition.mod_origin = mod_name
	new_definition.groups = new_definition.groups or {}
	new_definition.groups.not_in_creative_inventory = 1
	-- Make sure original node is dropped if a lit node is dug
	if not new_definition.drop then
		new_definition.drop = node_name
	end

	-- Allow any properties to be overridden on registration
	for prop, val in pairs(property_overrides) do
		new_definition[prop] = val
	end

	-- If it's a liquid, we need to stop it flowing
	if new_definition.groups.liquid then
		new_definition.liquid_range = 0
		lightable_liquids[node_name] = true
	end

	-- Register the lighting nodes
	lightable_nodes[node_name] = {}
	for i=1, minetest.LIGHT_MAX do
		local lighting_node_name = wielded_light.lighting_node_of_level(i, prefix)

		-- Index for quick finding later
		lightable_nodes[node_name][i] = lighting_node_name
		lighting_nodes[lighting_node_name] = {
			node = node_name,
			level = i
		}

		-- Copy the base definition and apply the light level
		local level_definition = table.copy(new_definition)
		level_definition.light_source = i

		-- If it's a liquid, we need to stop it replacing itself with the original
		if level_definition.groups.liquid then
			level_definition.liquid_alternative_source = lighting_node_name
			level_definition.liquid_alternative_flowing = lighting_node_name
		end

		minetest.register_node(":"..lighting_node_name, level_definition)
	end
end

-- Check if node can have a wielded light node placed in it
function wielded_light.is_lightable_node(node_pos)
	local name = minetest.get_node(node_pos).name
	if lightable_nodes[name] then
		return true
	elseif wielded_light.get_lighting_node(name) then
		return true
	end
	return false
end

-- Gets the closest position to pos that's a lightable node
function wielded_light.get_light_position(pos)
	local around_vector = {
		{x=0, y=0, z=0},
		{x=0, y=1, z=0}, {x=0, y=-1, z=0},
		{x=1, y=0, z=0}, {x=-1, y=0, z=0},
		{x=0, y=0, z=1}, {x=0, y=0, z=-1},
	}
	for _, around in ipairs(around_vector) do
		local light_pos = vector.add(pos, around)
		if wielded_light.is_lightable_node(light_pos) then
			return light_pos
		end
	end
end

-- Gets the emitted light level of a given item name
function wielded_light.get_light_def(item_name)
	-- Invalid item? No light
	if not item_name or item_name == "" then
		return 0, false
	end

	-- If the item is cached return the cached level
	local cached_definition = shiny_items[item_name]
	if cached_definition then
		return cached_definition.level, cached_definition.floodable
	end

	-- Get the item definition
	local stack = ItemStack(item_name)
	local itemdef = stack:get_definition()

	-- If invalid, no light
	if not itemdef then
		return 0, false
	end

	-- Get the light level of an item from its definition
	-- Reduce the light level by level_delta - original functionality
	-- Limit between 0 and the max light level
	return math.min(math.max((itemdef.light_source or 0) - level_delta, 0), minetest.LIGHT_MAX), itemdef.floodable
end

-- Register an item as shining
function wielded_light.register_item_light(item_name, light_level, floodable)
	if shiny_items[item_name] then
		if light_level then
			shiny_items[item_name].level = light_level
		end
		if floodable ~= nil then
			shiny_items[item_name].floodable = floodable
		end
	else
		if floodable == nil then
			local stack = ItemStack(item_name)
			local itemdef = stack:get_definition()
			floodable = itemdef.floodable
		end
		shiny_items[item_name] = {
			level = light_level,
			floodable = floodable or false
		}
	end
end

-- Mark an item as floodable or not
function wielded_light.register_item_floodable(item_name, floodable)
	if floodable == nil then floodable = true end
	if shiny_items[item_name] then
		shiny_items[item_name].floodable = floodable
	else
		local calced_level = wielded_light.get_light_def(item_name)
		shiny_items[item_name] = {
			level = calced_level,
			floodable = floodable
		}
	end
end

-- Keep track of an item entity. Should be called once for an item
function wielded_light.track_item_entity(obj, cat, item)
	if not is_entity_valid({ obj=obj }) then return end

	local light_level, light_is_floodable = wielded_light.get_light_def(item)
	-- If the item does not emit light do not track it
	if light_level <= 0 then return end

	-- Generate the uid for the item and the id for the light category
	local uid = tostring(obj)
	local id = get_light_category_id(cat)..uid

	-- Create the main tracking object for this item instance if it does not already exist
	if not tracked_entities[uid] then
		tracked_entities[uid] = { obj=obj, items={}, update = true }
	end

	-- Create the item tracking object for this item + category
	tracked_entities[uid].items[id] = { level=light_level, floodable=light_is_floodable }

	-- Add the light in on creation so it's immediate
	local pos = entity_pos(obj)
	local pos_str = pos and minetest.pos_to_string(pos)
	if pos_str then
		if not (light_is_floodable and is_lightable_liquid(pos)) then
			add_light(pos_str, id, light_level)
		end
	end
	tracked_entities[uid].pos = pos_str
end

-- A player's light should appear near their head not their feet
local player_height_offset = { x=0, y=1, z=0 }

-- Keep track of a user / player entity. Should be called as often as the user updates
function wielded_light.track_user_entity(obj, cat, item)
	-- Generate the uid for the player and the id for the light category
	local uid = tostring(obj)
	local id = get_light_category_id(cat)..uid

	-- Create the main tracking object for this player instance if it does not already exist
	if not tracked_entities[uid] then
		tracked_entities[uid] = { obj=obj, items={}, offset = player_height_offset, update = true }
	end

	local tracked_entity = tracked_entities[uid]
	local tracked_item = tracked_entity.items[id]

	-- If the item being tracked for the player changes, update the item tracking object for this item + category
	if not tracked_item or tracked_item.item ~= item then
		local light_level, light_is_floodable = wielded_light.get_light_def(item)
		tracked_entity.items[id] = { level=light_level, item=item, floodable=light_is_floodable }
		tracked_entity.update = true
	end
end

-- Setup --

-- Wielded item shining globalstep
minetest.register_globalstep(global_timer_callback)

-- Dropped item on_step override
-- https://github.com/minetest/minetest/issues/6909
local builtin_item = minetest.registered_entities["__builtin:item"]
local item = {
	on_step = function(self, dtime, ...)
		builtin_item.on_step(self, dtime, ...)
		-- Register an item once for tracking
		-- If it's already being tracked, exit
		if self.wielded_light then return end
		self.wielded_light = true
		local stack = ItemStack(self.itemstring)
		local item_name = stack:get_name()
		wielded_light.track_item_entity(self.object, "item", item_name)
	end
}
setmetatable(item, {__index = builtin_item})
minetest.register_entity(":__builtin:item", item)

-- Track a player's wielded item
wielded_light.register_player_lightstep(function (player)
	wielded_light.track_user_entity(player, "wield", player:get_wielded_item():get_name())
end)

-- Register helper nodes
local water_name = "default:water_source"
if minetest.get_modpath("hades_core") then
	water_name = "hades_core:water_source"
end

wielded_light.register_lightable_node("air", nil, "")
wielded_light.register_lightable_node(water_name, nil, "water_")
wielded_light.register_lightable_node("default:river_water_source", nil, "river_water_")

---TEST
--wielded_light.register_item_light('default:dirt', 14)

--do mesecraft's nodes
dofile(minetest.get_modpath("wielded_light").."/mesecraft.lua")


code:
-- mesecraft lit node registration

local rln = wielded_light.register_lightable_node
minetest.register_on_mods_loaded(function()
      rln("default:ladder_wood", nil, "ladder_wood_")
      rln("default:ladder_steel", nil, "ladder_steel_")
      rln("mtg_plus:ladder_papyrus", nil, "ladder_papyrus_")
      rln("df_trees:spore_tree_ladder", nil, "ladder_sporetree_")
      rln("ropes:ropeladder_top", nil, "ladder_ropetop_")
      rln("ropes:ropeladder", nil, "ladder_ropemid_")
      rln("ropes:ropeladder_bottom", nil, "ladder_ropebottom_")
      rln("moreblocks:rope", nil, "mb_rope_")

      local mtgplus = {
	 "gold",
	 "bronze",
	 "copper",
	 "tin",
	 "aspen_wood",
	 "acacia_wood",
	 "pine_wood",
	 "junglewood",
      }

      for i = 1, #mtgplus do
	 rln("mtg_plus:ladder_"..mtgplus[i], nil, "ladder_"..mtgplus[i].."_")
      end
      local rtype = {
	 "wood",
	 "copper",
	 "steel",
      }
      for i = 1,#rtype do
	 for len = 1,9 do
	    local name = "ropes:"..rtype[i]..len.."rope_block"
	    if minetest.registered_nodes[name] then
	       rln(name, nil, rtype[i]..len.."ropeblock_")
	    end
	 end
      end
      rln("ropes:rope_bottom", nil , "rope_bottom_")
      rln("ropes:rope", nil , "rope_")
end)


code:
name = wielded_light
optional_depends = default, hades_core


code:
--------------------------------------------------------------------------------------------------------
--Ambience Configuration for version .34
--Added Faraway & Ethereal by Amethystium

--find out why wind stops while flying
--add an extra node near feet to handle treading water as a special case, and don't have to use node under feet. which gets
	--invoked when staning on a ledge near water.
--reduce redundant code (stopplay and add ambience to list)

local max_frequency_all = 8000 --the larger you make this number the less frequent ALL sounds will happen recommended values between 100-2000.

--for frequencies below use a number between 0 and max_frequency_all
--for volumes below, use a number between 0.0 and 1, the larger the number the louder the sounds
local night_frequency = 85  --crickets
local night_volume = 0.4
local night_frequent_frequency = 160  --owls, wolves, crows
local night_frequent_volume = 0.7
local day_frequency = 70  --misc birds
local day_volume = 0.4
local day_frequent_frequency = 200  --robin, bluejay, cardinal
local day_frequent_volume = 0.18
local cave_frequency = 6  --about every 5 minutes, scary cave sounds
local cave_volume = 0.9
local cave_frequent_frequency = 150  --drops of water dripping
local cave_frequent_volume = 0.8
local beach_frequency = 150  --seagulls
local beach_volume = 0.4
local beach_frequent_frequency = max_frequency_all  --waves
local beach_frequent_volume = 0.3
local water_frequent_frequency = max_frequency_all  --water sounds
local water_frequent_volume = 0.6
local desert_frequency = 20  --coyote
local desert_volume = 0.6
local desert_frequent_frequency = max_frequency_all  --desertwind
local desert_frequent_volume = 0.6
local swimming_frequent_frequency = 0  --swimming splashes
local swimming_frequent_volume = 0
local water_surface_volume = 0   -- sloshing water
local lava_volume = 1 --lava
local flowing_water_volume = 0.4  --waterfall
local splashing_water_volume = 0.4
local pos, minp, maxp, temp, humid, nodes, height, minheight, grad, yint, desert_in_range, node_at_upper_body
--End of Config
----------------------------------------------------------------------------------------------------
local ambiences
local counter=0--*****************
local SOUNDVOLUME = 0.5
local sound_vol = 1
local last_x_pos = 0
local last_y_pos = 0
local last_z_pos = 0
local node_under_feet
local node_at_upper_body
local node_at_lower_body
local node_3_under_feet

local night = {
	name = "night",
	handler = {},
	fading = {},
	started = {},
	--on_start = "horned_owl",
	frequency = night_frequency,
	{name="Crickets_At_NightCombo", length=47, gain=night_frequent_volume},
}

local night_frequent = {
	name = "night_frequent",
	handler = {},
	fading = {},
	started = {},
	frequency = night_frequent_frequency,
	{name="horned_owl", length=3, gain=night_volume},
	{name="Wolves_Howling", length=11,  gain=night_volume},
	{name="craw", length=3, gain=night_volume},
}

local day = {
	name = "day",
	handler = {},
	fading = {},
	started = {},
	--on_start = "Best_Cardinal_Bird",
	frequency = day_frequency,
	{name="birdsongnl", length=13, gain=day_frequent_volume},
	{name="bird", length=30, gain=day_frequent_volume},
}

local day_frequent = {
	name = "day_frequent",
	handler = {},
	fading = {},
	started = {},
	frequency = day_frequent_frequency,
	{name="robin2", length=16, gain=day_frequent_volume},
	{name="bluejay", length=18, gain=day_frequent_volume},
	{name="Best_Cardinal_Bird", length=4, gain=day_frequent_volume},
}
--[[local swimming_frequent = {
	handler = {},
	frequency = 0,
	{name="water_swimming_splashing_breath", length=11.5, gain=swimming_frequent_volume},
	{name="water_swimming_splashing", length=9, gain=swimming_frequent_volume}
}]]

local cave = {
	name = "cave",
	handler = {},
	fading = {},
	started = {},
	frequency = cave_frequency,
	{name="cave_scary_1", length=10, gain=cave_volume},
	{name="cave_scary_2", length=21, gain=cave_volume},
	{name="cave_scary_3", length=5, gain=cave_volume},
	{name="cave_scary_4", length=9, gain=cave_volume},
	{name="cave_scary_5", length=6, gain=cave_volume},
	{name="cave_scary_6", length=31, gain=cave_volume},
	{name="cave_scary_7", length=11, gain=cave_volume},
	{name="cave_scary_8", length=11, gain=cave_volume},
	{name="cave_scary_9", length=6, gain=cave_volume},
	{name="cave_scary_10", length=17, gain=cave_volume},
	{name="cave_scary_11", length=51, gain=cave_volume},
	{name="cave_scary_12", length=18, gain=cave_volume},
	{name="cave_scary_13", length=34, gain=cave_volume},
	{name="cave_scary_14", length=7, gain=cave_volume},
	{name="cave_scary_15", length=6, gain=cave_volume},
	{name="cave_scary_16", length=5, gain=cave_volume},
	{name="cave_scary_17", length=8, gain=cave_volume},
	{name="cave_scary_18", length=7, gain=cave_volume},
	{name="cave_scary_19", length=7, gain=cave_volume},
	{name="cave_scary_20", length=13, gain=cave_volume},
	{name="cave_scary_21", length=20, gain=cave_volume},
	{name="cave_scary_22", length=6, gain=cave_volume},
}

local cave_frequent = {
	name = "cave_frequent",
	handler = {},
	fading = {},
	started = {},
	frequency = cave_frequent_frequency,
	{name="drippingwater_drip_a", length=2, gain=cave_frequent_volume},
	{name="drippingwater_drip_b", length=2, gain=cave_frequent_volume},
	{name="drippingwater_drip_c", length=2, gain=cave_frequent_volume},
	{name="Single_Water_Droplet", length=3, gain=cave_frequent_volume},
	{name="Spooky_Water_Drops", length=7, gain=cave_frequent_volume},
	{name="cave_dust_fall", length=3, gain=cave_frequent_volume},
	{name="cave_crumble_2", length=3, gain=cave_frequent_volume},
	{name="cave_crumble_10", length=2, gain=cave_frequent_volume},
	{name="cave_crumble_12", length=2, gain=cave_frequent_volume},
}

local beach = {
	name = "beach",
	handler = {},
	fading = {},
	started = {},
	frequency = beach_frequency,
	{name="seagull", length=4.5, gain=beach_volume}
}

local beach_frequent = {
	name = "beach_frequent",
	handler = {},
	fading = {},
	started = {},
	frequency = beach_frequent_frequency,
	{name="ambience_soundscape_beach", length=170, gain=beach_frequent_volume}
}

local desert = {
	name = "desert",
	handler = {},
	fading = {},
	started = {},
	frequency = desert_frequency,
	{name="coyote2", length=2.5, gain=desert_volume},
	{name="RattleSnake", length=8, gain=desert_volume}
}

local desert_frequent = {
	name = "desert_frequent",
	handler = {},
	fading = {},
	started = {},
	frequency = desert_frequent_frequency,
	{name="DesertMonolithMed", length=34.5, gain=desert_frequent_volume}
}
--[[
local flying = {
	handler = {},
	frequency = 8000,
	on_start = "nothing_yet",
	on_stop = "nothing_yet",
	{name="ComboWind", length=17,  gain=1}
}

local water = {
	handler = {},
	frequency = 0,--dolphins dont fit into small lakes
	{name="dolphins", length=6, gain=1},
	{name="dolphins_screaming", length=16.5, gain=1}
}
]]
local water_frequent = {
	name = "water_frequent",
	handler = {},
	fading = {},
	started = {},
	gain = water_frequent_volume,
	frequency = water_frequent_frequency,
	on_stop = "drowning_gasp",
	--on_start = "Splash",
	{name="scuba1bubbles", length=11, gain=water_frequent_volume},
	{name="scuba1calm", length=10, gain=water_frequent_volume},  --not sure why but sometimes I get errors when setting gain=water_frequent_volume here.
	{name="scuba1calm2", length=8.5, gain=water_frequent_volume},
	{name="scuba1interestingbubbles", length=11, gain=water_frequent_volume},
	{name="scuba1tubulentbubbles", length=10.5, gain=water_frequent_volume}
}
--[[
local water_surface = {
	handler = {},
	frequency = max_frequency_all,
	{name="lake_waves_2_calm", length=9.5, gain=water_surface_volume},
	{name="lake_waves_2_variety", length=13.1, gain=water_surface_volume}
}
local splashing_water = {
	handler = {},
	frequency = 0,
	{name="Splash_disabled_too_buggy", length=1.22, gain=splashing_water_volume}
}

local flowing_water = {
	handler = {},
	frequency = 0,
	{name="small_waterfall_disabled_too_buggy", length=14, gain=flowing_water_volume}
}
local flowing_water2 = {
	handler = {},
	frequency = 0,
	{name="small_waterfall_disabled_too_buggy", length=11, gain=flowing_water_volume}
}]]

local lava = {
	name = "lava",
	handler = {},
	fading = {},
	started = {},
	gain = lava_volume,
	frequency = max_frequency_all,
	{name="earth01a", length=20, gain=lava_volume}
}
local lava2 = {
	name = "lava2",
	handler = {},
	fading = {},
	started = {},
	gain = lava_volume,
	frequency = max_frequency_all,
	{name="earth01a", length=15, gain=lava_volume}
}

local play_music = false

local is_daytime = function()
	return (minetest.get_timeofday() > 0.2 and  minetest.get_timeofday() < 0.8)
end

local nodes_in_range = function(pos, search_distance, node_name)
	minp = {x=pos.x-search_distance,y=pos.y-search_distance, z=pos.z-search_distance}
	maxp = {x=pos.x+search_distance,y=pos.y+search_distance, z=pos.z+search_distance}
	nodes = minetest.env:find_nodes_in_area(minp, maxp, node_name)
	--minetest.chat_send_all("Found (" .. node_name .. ": " .. #nodes .. ")")
	return #nodes
end

local nodes_in_coords = function(minp, maxp, node_name)
	nodes = minetest.find_nodes_in_area(minp, maxp, node_name)
	--minetest.chat_send_all("Found (" .. node_name .. ": " .. #nodes .. ")")
	return #nodes
end

local atleast_nodes_in_grid = function(minp, maxp, node_name, threshold)
	--counter = counter +1
--	minetest.chat_send_all("counter: (" .. counter .. ")")
	height = maxp.y
	minheight = minp.y
	local totalnodes = 0
	local nodes
	while height >= minheight do
		minp.y = height
		maxp.y = height
		nodes = minetest.find_nodes_in_area(minp, maxp, node_name)
	--	minetest.chat_send_all("z+Found (" .. node_name .. ": " .. #nodes .. ")")
		height = height - 1
		if #nodes >= threshold then
			return true
		end
		totalnodes = totalnodes + #nodes
		if totalnodes >= threshold then
			return true
		end

	end
	return false
end

local humidmap = nil
local tempmap = nil
local function is_skylit(pos)
	if minetest.get_modpath("minetest_systemd") then
		return (minetestd.utils.get_natural_light(pos) > 0)
	else
		return (minetest.get_node_light(pos, 0.5) ~= minetest.get_node_light(pos, 0))
	end
end
local get_ambience = function(player)
	--[[local player_is_climbing = false
	local player_is_descending = false
	local player_is_moving_horiz = false
	local standing_in_water = false]]
	local pos = player:getpos()
	local skylit = is_skylit(pos)
	node_at_upper_body = minetest.registered_nodes[minetest.get_node(vector.add(pos, {x=0,y=1.2,z=0})).name]

	--Thanks/credit to the snowdrift mod for these calculations.
	humidmap = humidmap or minetest.get_perlin({
		offset = 50,
		scale = 50,
		spread = {x = 1000, y = 1000, z = 1000},
		seed = 842,
		octaves = 3,
		persist = 0.5,
		lacunarity = 2.0,
		flags = "defaults"
	})

	tempmap = tempmap or minetest.get_perlin({
		offset = 50,
		scale = 50,
		spread = {x = 1000, y = 1000, z = 1000},
		seed = 5349,
		octaves = 3,
		persist = 0.5,
		lacunarity = 2.0,
		flags = "defaults"
	})
	temp = tempmap:get2d({x = math.floor(pos.x+0.5), y = math.floor(pos.z+0.5)}) - pos.y*(20 / 90)
	humid = humidmap:get2d({x = math.floor(pos.x+0.5), y = math.floor(pos.z+0.5)})

	if node_at_upper_body and node_at_upper_body.liquidtype ~= "none" then
		if music then
			return {water_frequent=water_frequent, music=music}
		else
			return {water_frequent=water_frequent}
		end
	end

	if nodes_in_range(pos, 7, "default:lava_flowing")>5 or nodes_in_range(pos, 7, "default:lava_source")>5 or
		nodes_in_range(pos, 7, "dfcaverns:mantle_flowing")>5 or nodes_in_range(pos, 7, "dfcaverns:mantle_source")>5
	then
		if music then
			return {lava=lava, music=music}
		else
			return {lava=lava}
		end
	end

	--if we are near sea level and there is lots of water around the area
	if pos.y > -10 and pos.y < 32 and atleast_nodes_in_grid({x=pos.x-21, y=-10, z=pos.z-21}, {x=pos.x+21, y=1, z=pos.z+21}, "default:water_source", 140 ) then
		if temp > 30 then --Frozen = no seagulls
			if music then
				return {beach=beach, beach_frequent=beach_frequent, music=music}
			else
				return {beach=beach, beach_frequent=beach_frequent}
			end
		else
			if music then
				return {beach_frequent=beach_frequent, music=music}
			else
				return {beach_frequent=beach_frequent}
			end
		end
	end
	--[[if standing_in_water then
		if music then
			return {water_surface=water_surface, music=music}
		else
			return {water_surface}
		end
	end]]
	grad = 14 / 95
	yint = 1496 / 95
	desert_in_range = pos.y > -32 and humid - grad * temp < yint

	if desert_in_range then
		if music then
			return {desert=desert, desert_frequent=desert_frequent, music=music}
		else
			return {desert=desert, desert_frequent=desert_frequent}
		end
	end

	if pos.y > 128 and pos.y < 1000 and skylit then -- Play desert wind at really high altitude for an icy wind effect
		if music then
			return {desert_frequent=desert_frequent, music=music}
		else
			return {desert_frequent=desert_frequent}
		end
	end
--	pos.y = pos.y-2
--	nodename = minetest.env:get_node(pos).name
--	minetest.chat_send_all("Found " .. nodename .. pos.y )

	if player:getpos().y < 0 and not skylit then
		if music then
			return {cave=cave, cave_frequent=cave_frequent, music=music}
		else
			return {cave=cave, cave_frequent=cave_frequent}
		end
	end

	if skylit
		and temp > 30
	then
		if is_daytime()	then
			if music then
				return {day=day, day_frequent=day_frequent, music=music}
			else
				return {day=day, day_frequent=day_frequent}
			end
		else
			if music then
				return {night=night, night_frequent=night_frequent, music=music}
			else
				return {night=night, night_frequent=night_frequent}
			end
		end
	end
	if music then
		return {music=music}
	else
		return {}
	end
end

-- start playing the sound, set the handler and delete the handler after sound is played
local play_sound = function(player, list, number, is_music)
	local ambience_player_name = player:get_player_name()
	if list.handler[ambience_player_name] == nil then
		local gain = 1.0
		if list[number].gain ~= nil then
			if is_music then
				gain = list[number].gain*MUSICVOLUME
				--minetest.chat_send_all("gain music: " .. gain )
			else
				gain = list[number].gain*SOUNDVOLUME
				--minetest.chat_send_all("gain sound: " .. gain )
			end
		end
		local handler = minetest.sound_play(list[number].name, {to_player=ambience_player_name, gain=gain})
		if handler ~= nil then
			list.handler[ambience_player_name] = handler
			minetest.after(list[number].length, function(args)
				local list = args[1]
				local ambience_player_name = args[2]
				if list.handler[ambience_player_name] ~= nil then
					minetest.sound_stop(list.handler[ambience_player_name])
					list.handler[ambience_player_name] = nil
				end
			end, {list, ambience_player_name})
		end
	end
end

local fade_out_sound = function(list, name)
	if not list.fading[name] then
		list.fading[name] = list[1].gain
		if list.on_stop ~= nil then
			minetest.sound_play(list.on_stop, {to_player=name,gain=SOUNDVOLUME})
		end
	elseif list.fading[name] > 0 then
		list.fading[name] = list.fading[name] - (list[1].gain/4)
		--minetest.chat_send_all("fading "..list.name.." "..list.fading[name])
		minetest.sound_fade(list.handler[name], -(list[1].gain/8), math.max(0, list.fading[name]))
		if list.fading[name] <= 0 then
			minetest.after(0.25, function(list, name)
				--minetest.chat_send_all("stopping "..list.name.." "..(list.fading[name] or "nil"))
				if list.handler[name] then
					minetest.sound_stop(list.handler[name])
				end
				list.handler[name] = nil
				list.fading[name] = nil
			end, list, name)
		end
	end
end

minetest.register_on_leaveplayer(function(player)
	local ambience_player_name = player:get_player_name()
	local lists = {
		cave,
		cave_frequent,
		beach,
		beach_frequent,
		desert,
		desert_frequent,
		night,
		night_frequent,
		day,
		day_frequent,
		lava,
		lava2,
		water_frequent
	}
	for _,list in pairs(lists) do
		if list.handler[ambience_player_name] ~= nil then
			list.handler[ambience_player_name] = nil
		end
		if list.fading[ambience_player_name] ~= nil then
			list.fading[ambience_player_name] = nil
		end
	end
end)

-- stops all sounds that are not in still_playing
local stop_sound = function(still_playing, player, dtime)
	local ambience_player_name = player:get_player_name()
	local lists = {
		cave,
		cave_frequent,
		beach,
		beach_frequent,
		desert,
		desert_frequent,
		night,
		night_frequent,
		day,
		day_frequent,
		lava,
		lava2,
		water_frequent
	}
	for _,list in pairs(lists) do
		if still_playing[list.name] == nil then
			--minetest.log('line 556')
			if list.handler[ambience_player_name] ~= nil then
				--minetest.log('line 558')
				fade_out_sound(list, ambience_player_name, dtime)
			end
			list.started[ambience_player_name] = nil
		elseif list.handler[ambience_player_name] and list.fading[ambience_player_name] --Un-fade the sound if it started to fade out for some reason
			and list.fading[ambience_player_name] > 0 --Don't rescue sounds that are too far gone
		then
			--minetest.chat_send_all("reamplifying "..list.name.." to "..list[1].gain)
			minetest.sound_fade(list.handler[ambience_player_name], (list[1].gain/4), list[1].gain)
			list.fading[ambience_player_name] = nil
		end
	end
end

local timer = 0
minetest.register_globalstep(function(dtime)
	timer = timer+dtime
	if timer < 0.5 then
		return
	end
	timer = 0

	for _,player in ipairs(minetest.get_connected_players()) do
		ambiences = get_ambience(player)
		for _,ambience in pairs(ambiences) do
			if (ambience.on_start ~= nil) and not ambience.started[player:get_player_name()] then
				minetest.sound_play(ambience.on_start, {to_player=player:get_player_name(),gain=SOUNDVOLUME})
				ambience.started[player:get_player_name()] = true
			end
		end
		stop_sound(ambiences, player)
		--minetest.log("ambiences is "..dump(ambiences))
		for _,ambience in pairs(ambiences) do
			--minetest.log("max_frequency_all is "..dump(max_frequency_all))
			--minetest.log("ambience is "..dump(ambience))
			if math.random(1, max_frequency_all) <= ambience.frequency then
				--				if(played_on_start) then
				--				--	minetest.chat_send_all("playedOnStart "  )
				--				else
				--				--	minetest.chat_send_all("FALSEplayedOnStart "  )
				--				end
				--	minetest.chat_send_all("ambience: " ..ambience )
				--	if ambience.on_start ~= nil and played_on_start_flying == false then
				--		played_on_start_flying = true
				--		minetest.sound_play(ambience.on_start, {to_player=player:get_player_name()})
				--	end
				local is_music =false
				if ambience.is_music ~= nil then
					is_music = true
				end
				play_sound(player, ambience, math.random(1, #ambience),is_music)
			end
		end
	end
end
)
minetest.register_chatcommand("svol", {
	params = "<svol>",
	description = "set volume of ambiance, default 0.5 normal volume.",
	privs = {server=true},
	func = function(name, param)
		SOUNDVOLUME = param
	--	local player = minetest.env:get_player_by_name(name)
	--	ambiences = get_ambience(player)
	--	stop_sound({}, player)
		minetest.chat_send_player(name, "Sound volume set.")
	end,
})


code:
name = ambience
description = Adds environmental sounds.
depends = default
optional_depends =
author = Sam Hocevar
title = Ambience


code:
--------------Music Lic:
Amethystium:
--Avalon
--Ethereal
--Faraway
--Strangely Beautiful
Used with kind permission from �ystein Ramfjord / AM.mu Records

Jordach:
--dark_ambiance
--eastern_feeling
These sounds are used for the Mod for Minetest; Ambiance.
The included sounds are http://creativecommons.org/licenses/by-nc-sa/3.0/
Not Used:--mtest

-----------Sound Lic:
--Nightime Sound, Recorded by Mike Koenig, License: Attribution 3.0 http://soundbible.com/951-Nightime.html
--Crickets At Night Sound, License: Attribution 3.0 | Recorded by Mike Koenig |http://soundbible.com/365-Crickets-At-Night.html (Modified by Piezo_)

--Medium Pack Of Wolves Howling, License: Public Domain | Recorded by fws.gov, http://soundbible.com/277-Medium-Pack-Of-Wolves-Howling.html

--Horned Owl Sound, License: Attribution 3.0 | Recorded by Mike Koenig , http://soundbible.com/1851-Horned-Owl.html
--Bats In Cave Sound, License: Attr-Noncommercial 3.0 | Recorded by Mike Koenig , http://soundbible.com/1939-Bats-In-Cave.html

--Spooky Water Drops Sound, License: Attribution 3.0 | Recorded by Mike Koenig, http://soundbible.com/380-Spooky-Water-Drops.html

-- Single Water Droplet Sound, License: Attribution 3.0 | Recorded by Mike Koenig, http://soundbible.com/384-Single-Water-Droplet.html

--HollowWind, Black Boe, Creative Commons 0 License, http://www.freesound.org/people/Black%20Boe/sounds/22331/

--drippingwater*.ogg sounds: CC0, Dripping Water Mod, by kddekadenz, http://minetest.net/forum/viewtopic.php?id=1688

--best cardinal bird: License: Attribution 3.0 | Recorded by PsychoBird,  http://soundbible.com/1515-Best-Cardinal-Bird.html

--birdsongnl: the Attribution License, HerbertBoland, http://www.freesound.org/people/HerbertBoland/sounds/28312/  (end)

--robin2:  Attribution License, reinsamba, http://www.freesound.org/people/reinsamba/sounds/32479/  (end)

--Craw.WAV, Attribution License, inchadney, http://www.freesound.org/people/inchadney/sounds/52450/

--bluejay.wav,  Creative Commons 0 License,  UncleSigmund, http://www.freesound.org/people/UncleSigmund/sounds/42382/

--scuba1*.ogg- digifishmusic, Attribution License, http://www.freesound.org/people/digifishmusic/sounds/45521/

--Underwater Pool - Attribution 3.0 | Recorded by Mike Koenig, http://soundbible.com/1660-Underwater-Pool.html

--dolphin_screaming - Creative Commons 0 License, felix.blume, http://www.freesound.org/people/felix.blume/sounds/161691/

--dolphins - Attribution Noncommercial License,  acclivity, http://www.freesound.org/people/acclivity/sounds/13691/

ComboWind uses:
--wind-in-the-trees -Attribution License, laurent, http://www.freesound.org/people/laurent/sounds/16995/
--drygrassInWind- Creative Commons 0 License, felix.blume, http://www.freesound.org/people/felix.blume/sounds/146436/

--Splash: Attribution 3.0 | Recorded by BlastwaveFx.com, http://soundbible.com/546-Fish-Splashing.html

--small_waterfall Attribution License, volivieri, http://www.freesound.org/people/volivieri/sounds/38390/

--Lake_Waves_2*, Attribution License, Benboncan, http://www.freesound.org/people/Benboncan/sounds/67884/

--water_swimming_splashing*, Attribution Noncommercial License, Robinhood76, http://www.freesound.org/people/Robinhood76/sounds/79657/

--earth01a, Creative Commons 0 License., Halion , http://www.freesound.org/people/Halion/sounds/17785

--fiji_beach, Creative Commons 0 License, c97059890, http://www.freesound.org/people/c97059890/sounds/21754/ (Modified by Piezo_)

--seagull, Attribution Noncommercial License., hazure,  http://www.freesound.org/people/hazure/sounds/23707/,

desert:
coyote2, Attribution License, rogerforeman, http://www.freesound.org/people/rogerforeman/sounds/68068/
http://www.freesound.org/people/Proxima4/sounds/104319/
Desert Monolith.wav, Creative Commons 0 License, Proxima4, http://www.freesound.org/people/Proxima4/sounds/104319/ (Modified by Piezo_)
Rattlesnake Rattle, Public Domain, fws.gov,  http://soundbible.com/237-Rattlesnake-Rattle.html

flying:
crystal_airlines: Attribution License, suonho, http://www.freesound.org/people/suonho/sounds/56364/

----------------Not used yet:
desert:
Desert Simple.wav, Creative Commons 0 License, Proxima4, http://www.freesound.org/people/Proxima4/sounds/104320/

 313hummer (Jordan Craige)
--echos  http://soundcloud.com/jordan-craige/echos-1
Creative Commons Attribution license (reuse allowed)  Attribution 3.0 Unported (CC BY 3.0)
Not Used:--FoamOfTheSea http://soundcloud.com/jordan-craige/foam-of-the-sea

xi-intersection:
http://soundcloud.com/xi-intersection/mass-effect-uncharted-worlds  Creative Commons License
--not used:
http://soundcloud.com/xi-intersection/donkey-kong-country-2-flight
http://soundcloud.com/kogyo/kogyo-skalar-m1

lava:
http://www.freesound.org/people/Halion/sounds/17785/ (almost as good cc)  (combine with rocks falling?)
http://www.freesound.org/people/pushtobreak/sounds/17823/ (attrib non cc really good)
http://www.freesound.org/people/klankbeeld/sounds/123970/ (horror rhythm)
Rockfall in mine.wav http://www.freesound.org/people/Benboncan/sounds/60085/

http://www.freesound.org/people/snotch/sounds/96175/ (mud volcano)

--natural night sounds in Boquete.wav,  Attribution License, laurent, http://www.freesound.org/people/laurent/sounds/15851/
http://www.freesound.org/people/Dynamicell/sounds/17553/
http://www.freesound.org/people/juskiddink/sounds/78955/ aspen tree in wind
http://www.freesound.org/people/Benboncan/sounds/69761/ wind in hedge birds animals

ButterflyTea:
Creative Commons : Attribution-Noncommercial-Share Alike 3.0
http://www.jamendo.com/en/track/904012/dance-of-magical-flowers
http://www.jamendo.com/en/track/904013/magic-of-the-seventh-world
http://www.jamendo.com/en/track/904016/in-search-of-the-soul

zero-project


code:
To Do:

-add random_volume parameter to night normal, day normal, & cave normal sounds to create feeling of distance.

m-get constant birds more quiet during the day
m-bats seem way too frequent now?
m-when playing multiplayer seems to increase frequency of music & normal sounds to the point where they are obnoxious
m-which songs should go with which environments?   Dark songs in caves or night, light sounds during day.
m-may be drowning out some of the normal sound effects like walking on stone.
l-what about other weird bubble
l-put in wind/ wind in trees during day and night

ambience_soundscape_ocean.ogg - foongaz - https://freesound.org/people/foongaz/sounds/253287/
ambience_soundscape_night.ogg - felix.blume - https://freesound.org/people/felix.blume/sounds/410357/


code:
name = SOUND
description = Meta-modpack containing sound and related mods for MeseCraft.


code:
# SnowSong, the EPIC Sound Pack

Audio by Sn0wShepherd, license CC-BY-SA 4.0

Mod by texmex, license LGPL 2.1


code:
name = snowsong
title = SnowSong
description = SnowSong, the EPIC Sound Pack
author = Sn0wShepherd, texmex
license = CC-BY-SA 4.0
optional_depends = carts, default, doors, tnt, splash_diggers


code:
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.{lua,luacheckrc}]
indent_style = tab
indent_size = 4


code:
std = "lua51+minetest"
unused_args = false
allow_defined_top = true
max_line_length = 90

stds.minetest = {
	read_globals = {
		"DIR_DELIM",
		"minetest",
		"core",
		"dump",
		"vector",
		"nodeupdate",
		"VoxelManip",
		"VoxelArea",
		"PseudoRandom",
		"ItemStack",
		"default",
		table = {
			fields = {
				"copy",
			},
		},
	}
}


code:
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v2.1.0
    hooks:
      - id: end-of-file-fixer
      - id: trailing-whitespace

      - id: mixed-line-ending
        args: [--fix=lf]

  - repo: https://github.com/Calinou/pre-commit-luacheck
    rev: v1.0.0
    hooks:
      - id: luacheck


code:
language: generic

addons:
  apt:
    packages:
    - luarocks

install:
  - pyenv global 3.6.3
  - pip3 install --user pre-commit
  - luarocks install --local luacheck

script:
  # All linters are run with pre-commit hooks
  - export PATH="$HOME/.luarocks/bin:$PATH"
  - $HOME/.local/bin/pre-commit run --all-files


code:
# Bedrock

Bedrock for [Minetest](https://www.minetest.net/), a free and open source infinite
world block sandbox game.

[**Forum topic**](https://forum.minetest.net/viewtopic.php?f=11&t=9231)

## Installation

### Download the mod

To install Bedrock, clone this Git repository into your Minetest's `mods/`
directory:

```
git clone https://github.com/Calinou/bedrock.git
```

You can also
[download a ZIP archive](https://github.com/Calinou/bedrock/archive/master.zip)
of Bedrock. If you do so, you will need to extract the archive, then rename
the resulting folder from `bedrock-master` to `bedrock` – this is
**absolutely** necessary to do, else, it won't work!

### Enable the mod

Once you have installed Bedrock, you need to enable it in Minetest.
The procedure is as follows:

#### Using the client's main menu

This is the easiest way to enable Bedrock when playing in singleplayer
(or on a server hosted from a client).

1. Start Minetest and switch to the **Local Game** tab.
2. Select the world you want to enable Bedrock in.
3. Click **Configure**, then enable `bedrock` by double-clicking it
   (or ticking the **Enabled** checkbox).
4. Save the changes, then start a game on the world you enabled Bedrock on.
5. Bedrock should now be running on your world.

#### Using a text editor

This is the recommended way to enable the mod on a server without using a GUI.

1. Make sure Minetest is not currently running (else, it will overwrite
   the changes when exiting).
2. Open the world's `world.mt` file using a text editor.
3. Add the following line at the end of the file:

```
load_mod_bedrock = true
```

If the line is already present in the file, then replace `false` with `true` on that line.

4. Save the file, then start a game on the world you enabled Bedrock on.
5. Bedrock should now be running on your world.

## Version compatibility

Bedrock is currently primarily tested with Minetest 0.4.16.
It may or may not work with newer or older versions. Issues arising in older
versions than 0.4.16 will generally not be fixed.

## License

Copyright © 2011-2017 Hugo Locurcio and contributors

- Bedrock code is licensed under the zlib license, see
  [`LICENSE.md`](LICENSE.md) for details.
- Unless otherwise specified, Bedrock textures are licensed under
  [CC BY-SA 3.0 Unported](https://creativecommons.org/licenses/by-sa/3.0/).


code:
--[[
=====================================================================
Minetest mod: Bedrock

Copyright (c) 2013-2017 Hugo Locurcio and contributors.
Licensed under the zlib license. See LICENSE.md for more information.
=====================================================================
--]]

minetest.register_ore({
	ore_type = "scatter",
	ore = "bedrock:bedrock",
	wherein = "default:stone",
	clust_scarcity = 1 * 1 * 1,
	clust_num_ores = 5,
	clust_size = 2,
	y_min = -30912,

	-- 256 layers of bedrock (caves will still cut through it)
	y_max = -30000,
})

minetest.register_ore({
	ore_type = "scatter",
	ore = "bedrock:deepstone",
	wherein = "default:stone",
	clust_scarcity = 1 * 1 * 1,
	clust_num_ores = 5,
	clust_size = 2,
	y_min = -30656,
	y_max = -30000,
})

minetest.register_node("bedrock:bedrock", {
	description = "Bedrock",
	tiles = {"bedrock_bedrock.png"},
	drop = "",
	-- Set `unbreakable` for Map Tools' admin pickaxe
	groups = {unbreakable = 1, not_in_creative_inventory = 1},
	sounds = default.node_sound_stone_defaults(),
})

minetest.register_node("bedrock:deepstone", {
	description = "Deepstone",
	tiles = {"bedrock_deepstone.png"},
	drop = "default:stone", -- Intended
	groups = {cracky = 1},
	sounds = default.node_sound_stone_defaults(),
})


code:
name = bedrock
author = MeseCraft
depends = default


code:
MultiCraft Game mod: cleaner
============================

Introduction
------------
This mod removes unknown items and entities from inventory and map.

License of source code
----------------------
- Copyright (C) 2019 Jean-Patrick Guerrero (kilbith) <jeanpatrick.guerrero@gmail.com>
- Copyright (C) 2019-2020 MultiCraft Development Team

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3.0 of the License, or
(at your option) any later version.

http://www.gnu.org/licenses/lgpl-3.0.html


code:
local radius = 8
local height = 4
local freq = 3
local kpos = {}
local enable_kpos = false

minetest.register_on_joinplayer(function(player)
	local inv = player:get_inventory()

	for i = 1, inv:get_size("main") do
		local stack = inv:get_stack("main", i)
		local itemname = stack:get_name()

		if not minetest.registered_items[itemname] or
		minetest.registered_items[itemname].name == "air" then
			inv:set_stack("main", i, "")
		end
	end
end)

local vnew = vector.new
local function clean()
	local players = minetest.get_connected_players()
	for i = 1, #players do
		local player = players[i]
		local pos = player:get_pos()

		for x = -radius, radius do
		for y = -height, height do
		for z = -radius, radius do
			local pos_scan = vnew(pos.x + x, pos.y + y, pos.z + z)
			local nodename = minetest.get_node(pos_scan).name

			if enable_kpos then
				local hash = minetest.hash_node_position(pos_scan)
				if not kpos[hash] then
					local nodename = minetest.get_node(pos_scan).name

					if not minetest.registered_nodes[nodename] then
						minetest.remove_node(pos_scan)
					end

					kpos[hash] = true
				end
			else
				if not minetest.registered_nodes[nodename] then
					minetest.remove_node(pos_scan)
				end
			end

			local objs = minetest.get_objects_inside_radius(pos_scan, 0.5)
			if #objs > 0 then
				for j = 1, #objs do
					local obj = objs[j]
					if not obj:is_player() then
						local ent = obj:get_luaentity()
						if not (ent) or not ( minetest.registered_entities[ent.name] ) then
							obj:remove()
						end
					end
				end
			end
		end
		end
		end
	end

	minetest.after(freq, clean)
end

minetest.after(freq, clean)


code:
name = cleaner
description = Lightweight remove unknown items and entities from inventory and map.


code:
# Hallelujah Mountains for Minetest

[![License](https://img.shields.io/badge/license-LGPLv2.1%2B-blue.svg)](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)

AKA Cloudlands — giant rocks floating suspended in magnetic eddies. Now with giant trees

This can be run with or without a normal terrain mapgen, allowing pure skylands or an exotic addition to the landscape.

Many Configurable settings, including the altitude islands appear, and the ability to restrict the areas they spawn.

It's a single file with no hard dependencies for easy integration into modpacks or games, e.g. as an exotic biome.

Hallelujah Mountains [maps](https://i.imgur.com/2SkoAyB.png) can be generated by [Amidstest](https://forum.minetest.net/viewtopic.php?t=19869), provided you know the seed of the world. The islands are not homogenous — there will be areas without any, while smaller islands tend to follow eddy fields, often emanating out from much larger and rarer islands.

**Mod dependencies:** None.

**Minetest version:** v5.0+

**Recommended mods:**
* [Vines](https://forum.minetest.net/viewtopic.php?t=2344), or something that contains vines, such as plantlife_modpack or MineClone2. These will grow off the sides of the islands, climate permitting, allowing you to climb, and looking picturesque!
* [Nether](https://forum.minetest.net/viewtopic.php?t=5790) enables players to build portals to Hallelujah Mountains, allowing secret entrances into floating kingdoms, or the island altitude to be configured way up high while still having a means to get there. Stones to build these portals can only be found in the Nether (but not until [PR13](https://github.com/minetest-mods/nether/pull/13) is merged).
* Extra ways to explore
    * [Bridger](https://forum.minetest.net/viewtopic.php?t=18243) or [Bridges](https://forum.minetest.net/viewtopic.php?t=3488) if you want to build nice wooden bridges between islands
    * [Airboat](https://github.com/paramat/airboat) to sail the high clouds on a small airship
    * [Flying carpet](https://forum.minetest.net/viewtopic.php?t=12263)
    * [Jetpack, wings, etc.](https://forum.minetest.net/viewtopic.php?t=11257)
    * [Hang-glider / Elytra](https://forum.minetest.net/viewtopic.php?t=21425)
    * Teleporter mods, balloon mods, etc.

**Ores tip:** If you decide ores like iron and diamond should be obtainable from island stone, and are playing Minetest Game, you can go into Settings->Advanced Settings->Mods->cloudlands, set the "Altitude of Islands" to be 1300 (or higher), and Enable "Generate ores inside islands". This works because Minetest Game also generates the deep-underground ores in stone above 1025 to ensure they can be found in mgV7 Floatlands. Ores can be added in other games or at other altitudes by adding adjusted copies of the game's calls to minetest.register_ore().

**Video:** [Minetest Mod Review: Cloudlands](http://www.nathansalapat.com/minetest/cloudlands)

**Forum:** [link](https://forum.minetest.net/viewtopic.php?t=20257)

**Screenshots:**
![default](https://i.imgur.com/PUjjaIK.jpg)

![default](https://i.imgur.com/xn9UING.jpg)

![default](https://i.imgur.com/SeYMfZv.jpg)

![default](https://i.imgur.com/wBLEFWn.jpg)

![default](https://i.imgur.com/A6vDDGc.jpg)


code:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Script to generate the template file and update the translation files.
# Copy the script into the mod or modpack root folder and run it there.
#
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+
#
# See https://github.com/minetest-tools/update_translations for
# potential future updates to this script.

from __future__ import print_function
import os, fnmatch, re, shutil, errno
from sys import argv as _argv

# Running params
params = {"recursive": False,
    "help": False,
    "mods": False,
    "verbose": False,
    "folders": [],
    "no-old-file": False
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
    "help": ['--help', '-h'],
    "mods": ['--installed-mods'],
    "verbose": ['--verbose', '-v'],
    "no-old-file": ['--no-old-file']
}

# Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance
doublespace_threshold = 60

def set_params_folders(tab: list):
    '''Initialize params["folders"] from CLI arguments.'''
    # Discarding argument 0 (tool name)
    for param in tab[1:]:
        stop_param = False
        for option in options:
            if param in options[option]:
                stop_param = True
                break
        if not stop_param:
            params["folders"].append(os.path.abspath(param))

def set_params(tab: list):
    '''Initialize params from CLI arguments.'''
    for option in options:
        for option_name in options[option]:
            if option_name in tab:
                params[option] = True
                break

def print_help(name):
    '''Prints some help message.'''
    print(f'''SYNOPSIS
    {name} [OPTIONS] [PATHS...]
DESCRIPTION
    {', '.join(options["help"])}
        prints this help message
    {', '.join(options["recursive"])}
        run on all subfolders of paths given
    {', '.join(options["mods"])}
        run on locally installed modules
    {', '.join(options["no-old-file"])}
        do not create *.old files
    {', '.join(options["verbose"])}
        add output information
''')

def main():
    '''Main function'''
    set_params(_argv)
    set_params_folders(_argv)
    if params["help"]:
        print_help(_argv[0])
    elif params["recursive"] and params["mods"]:
        print("Option --installed-mods is incompatible with --recursive")
    else:
        # Add recursivity message
        print("Running ", end='')
        if params["recursive"]:
            print("recursively ", end='')
        # Running
        if params["mods"]:
            print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
            run_all_subfolders("~/.minetest/mods")
        elif len(params["folders"]) >= 2:
            print("on folder list:", params["folders"])
            for f in params["folders"]:
                if params["recursive"]:
                    run_all_subfolders(f)
                else:
                    update_folder(f)
        elif len(params["folders"]) == 1:
            print("on folder", params["folders"][0])
            if params["recursive"]:
                run_all_subfolders(params["folders"][0])
            else:
                update_folder(params["folders"][0])
        else:
            print("on folder", os.path.abspath("./"))
            if params["recursive"]:
                run_all_subfolders(os.path.abspath("./"))
            else:
                update_folder(os.path.abspath("./"))

#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)

# Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)

pattern_tr = re.compile(r'(.+?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$')

#attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder):
    try:
        with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
            for line in mod_conf:
                match = pattern_name.match(line)
                if match:
                    return match.group(1)
    except FileNotFoundError:
        pass
    return None

#If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder):
    out = []
    for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
        for name in files:
            if pattern_tr_filename.search(name):
                out.append(name)
    return out

# A series of search and replaces that massage a .po file's contents into
# a .tr file's equivalent
def process_po_file(text):
    # The first three items are for unused matches
    text = re.sub(r'#~ msgid "', "", text)
    text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
    text = re.sub(r'"\n#~ msgstr "', "=", text)
    # comment lines
    text = re.sub(r'#.*\n', "", text)
    # converting msg pairs into "=" pairs
    text = re.sub(r'msgid "', "", text)
    text = re.sub(r'"\nmsgstr ""\n"', "=", text)
    text = re.sub(r'"\nmsgstr "', "=", text)
    # various line breaks and escape codes
    text = re.sub(r'"\n"', "", text)
    text = re.sub(r'"\n', "\n", text)
    text = re.sub(r'\\"', '"', text)
    text = re.sub(r'\\n', '@n', text)
    # remove header text
    text = re.sub(r'=Project-Id-Version:.*\n', "", text)
    # remove double-spaced lines
    text = re.sub(r'\n\n', '\n', text)
    return text

# Go through existing .po files and, if a .tr file for that language
# *doesn't* exist, convert it and create it.
# The .tr file that results will subsequently be reprocessed so
# any "no longer used" strings will be preserved.
# Note that "fuzzy" tags will be lost in this process.
def process_po_files(folder, modname):
    for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
        for name in files:
            code_match = pattern_po_language_code.match(name)
            if code_match == None:
                continue
            language_code = code_match.group(1)
            tr_name = modname + "." + language_code + ".tr"
            tr_file = os.path.join(root, tr_name)
            if os.path.exists(tr_file):
                if params["verbose"]:
                    print(f"{tr_name} already exists, ignoring {name}")
                continue
            fname = os.path.join(root, name)
            with open(fname, "r", encoding='utf-8') as po_file:
                if params["verbose"]:
                    print(f"Importing translations from {name}")
                text = process_po_file(po_file.read())
                with open(tr_file, "wt", encoding='utf-8') as tr_out:
                    tr_out.write(text)

# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does
# nothing if it already exists
def mkdir_p(path):
    try:
        os.makedirs(path)
    except OSError as exc: # Python >2.5
        if exc.errno == errno.EEXIST and os.path.isdir(path):
            pass
        else: raise

# Converts the template dictionary to a text to be written as a file
# dKeyStrings is a dictionary of localized string to source file sets
# dOld is a dictionary of existing translations and comments from
# the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name):
    lOut = [f"# textdomain: {mod_name}\n"]

    dGroupedBySource = {}

    for key in dkeyStrings:
        sourceList = list(dkeyStrings[key])
        sourceList.sort()
        sourceString = "\n".join(sourceList)
        listForSource = dGroupedBySource.get(sourceString, [])
        listForSource.append(key)
        dGroupedBySource[sourceString] = listForSource

    lSourceKeys = list(dGroupedBySource.keys())
    lSourceKeys.sort()
    for source in lSourceKeys:
        localizedStrings = dGroupedBySource[source]
        localizedStrings.sort()
        lOut.append("")
        lOut.append(source)
        lOut.append("")
        for localizedString in localizedStrings:
            val = dOld.get(localizedString, {})
            translation = val.get("translation", "")
            comment = val.get("comment")
            if len(localizedString) > doublespace_threshold and not lOut[-1] == "":
                lOut.append("")
            if comment != None:
                lOut.append(comment)
            lOut.append(f"{localizedString}={translation}")
            if len(localizedString) > doublespace_threshold:
                lOut.append("")

    unusedExist = False
    for key in dOld:
        if key not in dkeyStrings:
            val = dOld[key]
            translation = val.get("translation")
            comment = val.get("comment")
            # only keep an unused translation if there was translated
            # text or a comment associated with it
            if translation != None and (translation != "" or comment):
                if not unusedExist:
                    unusedExist = True
                    lOut.append("\n\n##### not used anymore #####\n")
                if len(key) > doublespace_threshold and not lOut[-1] == "":
                    lOut.append("")
                if comment != None:
                    lOut.append(comment)
                lOut.append(f"{key}={translation}")
                if len(key) > doublespace_threshold:
                    lOut.append("")
    return "\n".join(lOut) + '\n'

# Writes a template.txt file
# dkeyStrings is the dictionary returned by generate_template
def write_template(templ_file, dkeyStrings, mod_name):
    # read existing template file to preserve comments
    existing_template = import_tr_file(templ_file)

    text = strings_to_text(dkeyStrings, existing_template[0], mod_name)
    mkdir_p(os.path.dirname(templ_file))
    with open(templ_file, "wt", encoding='utf-8') as template_file:
        template_file.write(text)

# Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file):
    lOut = []
    with open(lua_file, encoding='utf-8') as text_file:
        text = text_file.read()
        #TODO remove comments here

        text = re.sub(pattern_concat, "", text)

        strings = []
        for s in pattern_lua.findall(text):
            strings.append(s[1])
        for s in pattern_lua_bracketed.findall(text):
            strings.append(s)

        for s in strings:
            s = re.sub(r'"\.\.\s+"', "", s)
            s = re.sub("@[^@=0-9]", "@@", s)
            s = s.replace('\\"', '"')
            s = s.replace("\\'", "'")
            s = s.replace("\n", "@n")
            s = s.replace("\\n", "@n")
            s = s.replace("=", "@=")
            lOut.append(s)
    return lOut

# Gets strings from an existing translation file
# returns both a dictionary of translations
# and the full original source text so that the new text
# can be compared to it for changes.
def import_tr_file(tr_file):
    dOut = {}
    text = None
    if os.path.exists(tr_file):
        with open(tr_file, "r", encoding='utf-8') as existing_file :
            # save the full text to allow for comparison
            # of the old version with the new output
            text = existing_file.read()
            existing_file.seek(0)
            # a running record of the current comment block
            # we're inside, to allow preceeding multi-line comments
            # to be retained for a translation line
            latest_comment_block = None
            for line in existing_file.readlines():
                line = line.rstrip('\n')
                if line[:3] == "###":
                    # Reset comment block if we hit a header
                    latest_comment_block = None
                    continue
                if line[:1] == "#":
                    # Save the comment we're inside
                    if not latest_comment_block:
                        latest_comment_block = line
                    else:
                        latest_comment_block = latest_comment_block + "\n" + line
                    continue
                match = pattern_tr.match(line)
                if match:
                    # this line is a translated line
                    outval = {}
                    outval["translation"] = match.group(2)
                    if latest_comment_block:
                        # if there was a comment, record that.
                        outval["comment"] = latest_comment_block
                    latest_comment_block = None
                    dOut[match.group(1)] = outval
    return (dOut, text)

# Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file
# Returns a dictionary of localized strings to source file sets
# that can be used with the strings_to_text function.
def generate_template(folder, mod_name):
    dOut = {}
    for root, dirs, files in os.walk(folder):
        for name in files:
            if fnmatch.fnmatch(name, "*.lua"):
                fname = os.path.join(root, name)
                found = read_lua_file_strings(fname)
                if params["verbose"]:
                    print(f"{fname}: {str(len(found))} translatable strings")

                for s in found:
                    sources = dOut.get(s, set())
                    sources.add(f"### {os.path.basename(fname)} ###")
                    dOut[s] = sources

    if len(dOut) == 0:
        return None
    templ_file = os.path.join(folder, "locale/template.txt")
    write_template(templ_file, dOut, mod_name)
    return dOut

# Updates an existing .tr file, copying the old one to a ".old" file
# if any changes have happened
# dNew is the data used to generate the template, it has all the
# currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
    if params["verbose"]:
        print(f"updating {tr_file}")

    tr_import = import_tr_file(tr_file)
    dOld = tr_import[0]
    textOld = tr_import[1]

    textNew = strings_to_text(dNew, dOld, mod_name)

    if textOld and textOld != textNew:
        print(f"{tr_file} has changed.")
        if not params["no-old-file"]:
            shutil.copyfile(tr_file, f"{tr_file}.old")

    with open(tr_file, "w", encoding='utf-8') as new_tr_file:
        new_tr_file.write(textNew)

# Updates translation files for the mod in the given folder
def update_mod(folder):
    modname = get_modname(folder)
    if modname is not None:
        process_po_files(folder, modname)
        print(f"Updating translations for {modname}")
        data = generate_template(folder, modname)
        if data == None:
            print(f"No translatable strings found in {modname}")
        else:
            for tr_file in get_existing_tr_files(folder):
                update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
    else:
        print("Unable to find modname in folder " + folder)

# Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly
def update_folder(folder):
    is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
    if is_modpack:
        subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
        for subfolder in subfolders:
            update_mod(subfolder + "/")
    else:
        update_mod(folder)
    print("Done.")

def run_all_subfolders(folder):
    for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
        update_folder(modfolder + "/")

main()


code:
local path = minetest.get_modpath(minetest.get_current_modname())

dofile(path.."/cloudlands.lua")


code:
name = cloudlands
optional_depends = nether, vines, biomeinfo, schemlib, default, mcl_core, xpanes, ethereal, main
description = """
Hallelujah Mountains for Minetest

Giant rocks floating suspended in magnetic eddies.

This can be run with or without a normal terrain mapgen, allowing pure skylands or an exotic addition to the landscape.

Configurable settings include the altitude islands appear, and the ability to restrict the area they spawn. If you only ever see snow-covered islands then enable the "Use lowland biomes" option.
"""


code:
# If true then islands will use biomes from altitude 10 - higher than beaches
# and lower than mountains. This avoids a problem with some mods which have
# alpine biomes, causing all islands to be alpine/snowy due to their altitude,
# however plant-life may still reflect the actual altitude of the islands.
#
# Turn this off to have each island's altitude affect its biome.
cloudlands_use_lowland_biomes        (Use lowland biomes) bool false

# The average altitude islands are found at
#
# In Minetest game, ores like iron and diamond will be generated if the
# altitude of islands is set above 1200 and "Generate ores inside islands"
# is Enabled.
cloudlands_altitude             (Altitude of islands) int 200

# The rough variance of altitude in islands (plus or minus this value)
cloudlands_altitude_amplitude   (Altitude variance) int 40

# Generates patches of sand and dirt inside island stone cores.
cloudlands_generate_ores        (Generate ores inside islands - patches of dirt, sand etc.) bool false

# Set this to 0 to disable vines.
#
# A percentage value between 0 and 100 which controls the likelyhood of each
# node on an island edge having a vine growing down it.
# This option is ignored unless played with a game or mod which provides vines,
# such as plantlife_modpack, vines, or MineClone2.
# This setting does not alter the temperature or humidity determining whether
# vines will be present on an island.
cloudlands_vine_coverage        (Vine coverage %) int 30 0 100

# If you wish to have large areas of the map without islands then specify
# here the area where islands may spawn.
#
# The format is:
#      minimum_x minimum_z maximum_x maximum_z
cloudlands_limit_rect           (Limit island locations within rectange) string -32000 -32000 32000 32000

# Leave blank to allow islands to appear in any biome
# Otherwise list biomes by name, with a space between each one.
#
# Example:
#      cold_desert sandstone_desert
cloudlands_limit_biome          (Limit island locations within listed biomes) string

# Only used when "Limit island locations within listed biomes" is set.
# This specifies the altitude of biomes which determine whether islands can
# spawn, allowing islands to have their own biome system at island altitude
# while still having locations limited by the biomes at ground level.
#
#      Set to about 10 to use ground level biomes (10 is higher than beaches)
#      Leave blank to use the islands' altitudes.
cloudlands_limit_biome_altitude (Altitude for listed biomes) string

# Set this to 0 to disable giant trees.
#
# A percentage value between 0 and 100 which controls the spread of the
# rare and gigantic trees which can sometimes be found growing on islands.
cloudlands_giant_tree_rarety    (Giant tree rarety %) int 8 0 100

# Set true to allow glowing rarer variants of the rare and gigantic trees
# which can sometimes be found growing on islands.
#
# This option is ignored if tree rarety is set to 0
cloudlands_bioluminescence      (Allow glowing trees) bool false

# Set true to allow players to build portals to islands.
#
# Portals require the Nether mod.
cloudlands_enable_portals (Enable portals to the islands) bool true


code:
# decorations_sea
This mod adds various types of sea decorations spread across biomes.

![Screenshot](screenshot.png)

## Requirements

- Minetest 5.0.0+
- Minetest_game 5.0.0+

## Recommended mods

- [lootchests_modpack](https://github.com/ClockGen/lootchests_modpack), Adds rare treasure chests and barrels to the ocean
- [shipwrecks](https://github.com/ClockGen/shipwrecks), Adds configurable and lootable shipwrecks

## License
All code is licensed under GPLv3 [link to the license](https://www.gnu.org/licenses/gpl-3.0.en.html)

Kelp on_place() code is copied from minetest_game, copyright Various Minetest developers and contributors (LGPLv2.1+)

All resources are licensed under CC BY 4.0 [link to the license](https://creativecommons.org/licenses/by/4.0/legalcode)


code:
local path = minetest.get_modpath("decorations_sea")
dofile(path .. "/nodes.lua")
dofile(path .. "/mapgen.lua")


code:
local path = minetest.get_modpath("decorations_sea")

local warm_biomes = {
    "desert_ocean",
    "sandstone_desert_ocean",
    "savanna_shore",
    "savanna_ocean",
    "rainforest_ocean",
    "mesa",
    "mesa_ocean",
    "jumble",
    "jumble_ocean",
    "junglee",
    "junglee_ocean",
    "grove",
    "grove_ocean",
    "mushroom",
    "mushroom_ocean",
    "sandstone",
    "sandstone_ocean",
    "savannah",
    "savannah_ocean",
    "quicksand",
    "fiery",
    "fiery_ocean",
    "sandclay",
    "swamp",
    "swamp_ocean",
    "desert",
}

local temperate_biomes = {
    "grassland",
    "deciduous_forest",
    "grassland_ocean",
    "coniferous_forest_ocean",
    "deciduous_forest_shore",
    "decidious_forest_ocean",
    "cold_desert_ocean",
    "sakura",
    "sakura_ocean",
    "grassy",
    "grassy_ocean",
    "grayness",
    "grayness_ocean",
    "grassytwo",
    "grassytwo_ocean",
    "prairie",
    "prairie_ocean",
    "plains",
    "plains_ocean",
    "bamboo",
    "clearing",
}

local frozen_biomes = {
    "icesheet_ocean",
    "tundra_beach",
    "tundra_ocean",
    "taiga_ocean",
    "alpine",
    "snowy",
    "snowy_grassland_ocean",
    "glacier",
    "glacier_ocean",
    "frost",
    "frost_ocean",
}

local corals = {
    "blue",
    "crimson",
    "green",
    "orange",
    "pink",
    "red",
    "violet",
    "yellow"
}

local function register_coral_decoration(schemname, noisedef, biomes)
    minetest.register_decoration( {
        deco_type = "schematic",
        place_on = "default:sand",
        sidelen = 16,
        noise_params = noisedef,
        biomes = biomes,
        y_min = -32,
        y_max = -4,
        spawn_by = "default:water_source",
        num_spawn_by = 1,
        flags = "force_placement, all_floors",
        schematic = schemname,
        rotation = "random",
        place_offset_y = 0,
    })
end

local function register_tall_grass_decoration(name, hmin, hmax, noisedef, biomes)
    minetest.register_decoration({
        deco_type = "simple",
        place_on = "default:sand",
        sidelen = 16,
        noise_params = noisedef,
        biomes = biomes,
        y_min = -32,
        y_max = -hmax,
        spawn_by = "default:water_source",
        num_spawn_by = 1,
        flags = "force_placement, all_floors",
        decoration = name,
        param2 = hmin*16,
        param2_max = hmax*16,
        place_offset_y = -1,
    })
end

local function register_simple_decoration(name, noisedef, biomes)
    minetest.register_decoration({
        deco_type = "simple",
        place_on = "default:sand",
        sidelen = 16,
        noise_params = noisedef,
        biomes = biomes,
        y_min = -32,
        y_max = -1,
        spawn_by = "default:water_source",
        num_spawn_by = 1,
        flags = "force_placement, all_floors",
        decoration = name,
        param2 = 16,
        place_offset_y = -1,
    })
end

local function register_sand_decoration(name, fill_ratio, ymax, biomes)
    minetest.register_decoration({
        deco_type = "simple",
        place_on = "default:sand",
        sidelen = 16,
        fill_ratio = fill_ratio,
        biomes = biomes,
        y_min = -32,
        y_max = ymax,
        spawn_by = "default:water_source",
        num_spawn_by = 1,
        flags = "force_placement, all_floors",
        decoration = name,
        place_offset_y = -1,
    })
end

local coral_noise = {
    offset = -0.0115,
    scale = 0.024,
    spread = {x = 100, y = 100, z = 100},
    seed = 111,
    octaves = 5,
    persist = 0.6
}

local single_coral_noise = {
    offset = 0.01,
    scale = 0.024,
    spread = {x = 100, y = 100, z = 100},
    seed = 111,
    octaves = 5,
    persist = 0.6
}

local sea_pickle_noise = {
    offset = 0.005,
    scale = 0.024,
    spread = {x = 100, y = 100, z = 100},
    seed = 111,
    octaves = 5,
    persist = 0.6
}

local grass_noise = {
    offset = 0.025,
    scale = 0.024,
    spread = {x = 100, y = 100, z = 100},
    seed = 1411,
    octaves = 5,
    persist = 0.6
}

local sparce_grass_noise = {
    offset = 0.015,
    scale = 0.024,
    spread = {x = 100, y = 100, z = 100},
    seed = 1411,
    octaves = 5,
    persist = 0.6
}

local short_grass_noise = {
    offset = 0.075,
    scale = 0.024,
    spread = {x = 100, y = 100, z = 100},
    seed = 25225,
    octaves = 5,
    persist = 0.6
}

local sand_decorations_list = {
    {"decorations_sea:seashell_01_node", 0.01, 1, warm_biomes},
    {"decorations_sea:seashell_02_node", 0.01, 1, warm_biomes},
    {"decorations_sea:seashell_03_node", 0.01, 1, warm_biomes},
    {"decorations_sea:starfish_01_node", 0.005, 1, warm_biomes},
    {"decorations_sea:starfish_02_node", 0.005, 1, warm_biomes},
    {"decorations_sea:seashell_01_node", 0.002, 1, temperate_biomes},
    {"decorations_sea:seashell_02_node", 0.002, 1, temperate_biomes},
    {"decorations_sea:seashell_03_node", 0.002, 1, temperate_biomes},
}

local tall_grass_list = {
    {"decorations_sea:seagrass_01", 4, 7, sparce_grass_noise, frozen_biomes},
    {"decorations_sea:seagrass_01", 3, 4, sparce_grass_noise, frozen_biomes},
    {"decorations_sea:seagrass_01", 2, 2, sparce_grass_noise, frozen_biomes},
    {"decorations_sea:seagrass_02", 4, 7, grass_noise, warm_biomes},
    {"decorations_sea:seagrass_02", 3, 4, grass_noise, warm_biomes},
    {"decorations_sea:seagrass_02", 2, 2, grass_noise, warm_biomes},
    {"decorations_sea:seagrass_03", 4, 7, grass_noise, temperate_biomes},
    {"decorations_sea:seagrass_03", 3, 4, grass_noise, temperate_biomes},
    {"decorations_sea:seagrass_03", 2, 2, grass_noise, temperate_biomes},
    {"decorations_sea:seagrass_03", 4, 7, grass_noise, warm_biomes},
    {"decorations_sea:seagrass_03", 3, 4, grass_noise, warm_biomes},
    {"decorations_sea:seagrass_03", 2, 2, grass_noise, warm_biomes},
}

local simple_decoration_list = {
    {"decorations_sea:seagrass_01", sparce_grass_noise, frozen_biomes},
    {"decorations_sea:seagrass_02", grass_noise, warm_biomes},
    {"decorations_sea:seagrass_03", grass_noise, temperate_biomes},
    {"decorations_sea:seagrass_03", grass_noise, warm_biomes},
    {"decorations_sea:seagrass_04", short_grass_noise},
    {"decorations_sea:seagrass_05", short_grass_noise, warm_biomes},
    {"decorations_sea:seagrass_06", short_grass_noise, warm_biomes},
    {"decorations_sea:coral_plantlike_01", single_coral_noise, warm_biomes},
    {"decorations_sea:coral_plantlike_02", single_coral_noise, warm_biomes},
    {"decorations_sea:coral_plantlike_03", single_coral_noise, warm_biomes},
    {"decorations_sea:coral_plantlike_04", single_coral_noise, warm_biomes},
    {"decorations_sea:coral_plantlike_05", single_coral_noise, warm_biomes},
    {"decorations_sea:sea_pickle", sea_pickle_noise, warm_biomes},
}

for _,v in pairs(corals) do
    for i = 1, 3 do
        local schem_path = path .. "/schematics/coral_" .. v .. "_0" .. i .. ".mts"
        register_coral_decoration(schem_path, coral_noise, warm_biomes)
    end
end

for _,v in pairs(simple_decoration_list) do
    register_simple_decoration(v[1], v[2], v[3])
end

for _,v in pairs(tall_grass_list) do
    register_tall_grass_decoration(v[1], v[2], v[3], v[4], v[5])
end

for _,v in pairs(sand_decorations_list) do
    register_sand_decoration(v[1], v[2], v[3], v[4], v[5])
end


code:
name = decorations_sea
description = Adds many new types of seaweed, kelp, corals and other sea decorations, distributed across biomes
depends = default
optional_depends = ethereal
author = ClockGen
title = Decorations Sea


code:
local function register_sand_decoration(def)
    minetest.register_node(def.id .. "_node", {
        description = "Sand With " .. def.description,
        tiles = {"default_sand.png^" .. def.overlay, "default_sand.png"},
        groups = {crumbly = 3, falling_node = 1, sand = 1, not_in_creative_inventory = 1},
        sounds = default.node_sound_sand_defaults(),
        node_dig_prediction = "default:sand",
        node_placement_prediction = "",
        drop = def.id,
        after_destruct = function(pos, oldnode)
            minetest.set_node(pos, {name = "default:sand"})
        end
    })

    minetest.register_craftitem(def.id, {
        description = def.description,
        inventory_image = def.texture,
        on_place = function(itemstack, user, pointed_thing)
            if not user then return end
            if pointed_thing and pointed_thing.type == "node" then
                local pos = minetest.get_pointed_thing_position(pointed_thing)
                local node = minetest.get_node_or_nil(pos)
                if node and minetest.registered_nodes[node.name].name == "default:sand" then
                    local name = user:get_player_name()
                    if not minetest.is_protected(pos, name) then
                        minetest.set_node(pos, {name = def.id .. "_node"})
                        itemstack:take_item()
                        return itemstack
                    end
                end
            end
        end
    })
end

local function register_seagrass(def)
    local max_height = def.max_height or 3
    minetest.register_node(def.id, {
        description = def.description,
        drawtype = "plantlike_rooted",
        waving = 1,
        tiles = {"default_sand.png"},
        light_source = def.light_source,
        special_tiles = {{name = def.texture, tileable_vertical = true}},
        inventory_image = def.texture,
        paramtype = "light",
        paramtype2 = "leveled",
        groups = {snappy = 3},
        node_dig_prediction = "default:sand",
        node_placement_prediction = "",
        sounds = default.node_sound_sand_defaults({
            dig = {name = "default_dig_snappy", gain = 0.2},
            dug = {name = "default_grass_footstep", gain = 0.25},
        }),

        on_place = function(itemstack, placer, pointed_thing)
            -- Call on_rightclick if the pointed node defines it
            if pointed_thing.type == "node" and placer and
                    not placer:get_player_control().sneak then
                local node_ptu = minetest.get_node(pointed_thing.under)
                local def_ptu = minetest.registered_nodes[node_ptu.name]
                if def_ptu and def_ptu.on_rightclick then
                    return def_ptu.on_rightclick(pointed_thing.under, node_ptu, placer,
                        itemstack, pointed_thing)
                end
            end

            local pos = pointed_thing.under
            if minetest.get_node(pos).name ~= "default:sand" then
                return itemstack
            end

            local height = math.random(1, max_height)
            local pos_top = {x = pos.x, y = pos.y + height, z = pos.z}
            local node_top = minetest.get_node(pos_top)
            local def_top = minetest.registered_nodes[node_top.name]
            local player_name = placer:get_player_name()

            if def_top and def_top.liquidtype == "source" and
                    minetest.get_item_group(node_top.name, "water") > 0 then
                if not minetest.is_protected(pos, player_name) and
                        not minetest.is_protected(pos_top, player_name) then
                    minetest.set_node(pos, {name = def.id,
                        param2 = height * 16})
                    if not (creative and creative.is_enabled_for
                            and creative.is_enabled_for(player_name)) then
                        itemstack:take_item()
                    end
                else
                    minetest.chat_send_player(player_name, "Node is protected")
                    minetest.record_protection_violation(pos, player_name)
                end
            end

            return itemstack
        end,

        after_destruct  = function(pos, oldnode)
            minetest.set_node(pos, {name = "default:sand"})
        end
    })
end

local corals = {
    "Blue",
    "Orange",
    "Pink",
    "Violet",
    "Red",
    "Yellow",
    "Red",
    "Green"
}

local plantlike_corals = {5, 7, 8, 1, 6}

for k,v in pairs(corals) do
    minetest.register_node("decorations_sea:coral_0" .. k, {
        description = v .. " Coral",
        tiles = {"decorations_sea_coral_node_0" .. k .. ".png"},
        groups = {cracky = 3},
        drop = "default:coral_skeleton",
        sounds = default.node_sound_stone_defaults(),
    })
end

for k,v in pairs(plantlike_corals) do
    minetest.register_node("decorations_sea:coral_plantlike_0" .. k, {
        description = "Coral",
        drawtype = "plantlike_rooted",
        paramtype2 = "meshoptions",
        place_param2 = 4,
        tiles = {"decorations_sea_coral_node_0" .. v .. ".png"},
        special_tiles = {{name = "decorations_sea_coral_0" .. k .. ".png", tileable_vertical = true}},
        inventory_image = "decorations_sea_coral_0" .. k .. ".png",
        paramtype = "light",
        groups = {cracky = 3},
        sounds = default.node_sound_stone_defaults({
            dig = {name = "default_dig_snappy", gain = 0.2},
            dug = {name = "default_grass_footstep", gain = 0.25},
        }),
    })
end

for i = 1, 6 do
    local height = 5
    if i > 3 then height = 1 end
    register_seagrass({
        id = "decorations_sea:seagrass_0" .. i,
        description = "Seagrass",
        texture = "decorations_sea_seagrass_0" .. i .. ".png",
        max_height = height,
    })
end

for i = 1, 3 do
    register_sand_decoration({
        id = "decorations_sea:seashell_0" .. i,
        description = "Seashell",
        texture = "decorations_sea_seashell_0" .. i .. ".png",
        overlay = "decorations_sea_seashell_0" .. i .. "_overlay.png"
    })
end

for i = 1, 2 do
    register_sand_decoration({
        id = "decorations_sea:starfish_0" .. i,
        description = "Starfish",
        texture = "decorations_sea_starfish_0" .. i .. ".png",
        overlay = "decorations_sea_starfish_0" .. i .. ".png"
    })
end

register_seagrass({
    id = "decorations_sea:sea_pickle",
    description = "Sea Pickle",
    texture = "decorations_sea_pickle.png",
    light_source = 8,
    max_height = 1,
})


code:
# Auto detect text files and perform LF normalization
* text=auto

# Custom for Visual Studio
*.cs     diff=csharp

# Standard to msysgit
*.doc	 diff=astextplain
*.DOC	 diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot  diff=astextplain
*.DOT  diff=astextplain
*.pdf  diff=astextplain
*.PDF	 diff=astextplain
*.rtf	 diff=astextplain
*.RTF	 diff=astextplain


code:
[submodule "mapgen_helper"]
	path = mapgen_helper
	url = https://github.com/minetest-mods/mapgen_helper
	branch = master
[submodule "subterrane"]
	path = subterrane
	url = https://github.com/minetest-mods/subterrane
	branch = master