code:
API documentation for the HUD bars mod
======================================

## Introduction
This API allows you to add, change, hide and unhide custom HUD bars for this mod.

## Overview
To give you a *very* brief overview over this API, here is the basic workflow on how to add your own custom HUD bar:

* Create images for your HUD bar
* Call `hb.register_hudbar` to make the definition of the HUD bar known to this mod
* Call `hb.init_hudbar` for each player for which you want to use previously defined HUD bar
* Use `hb.change_hudbar` whenever you need to change the values of a HUD bar of a certain player
* If you need it: Use `hb.hide_hudbar` and `hb.unhide_hudbar` to hide or unhide HUD bars of a certain player

## The basic rules
In order to use this API, you should be aware of a few basic rules in order to understand it:

* A HUD bar is an approximate graphical representation of the ratio of a current value and a maximum value, i.e. current health of 15 and maximum health of 20. A full HUD bar represents 100%, an empty HUD bar represents 0%.
* The current value must always be equal to or smaller then the maximum
* Both current value and maximum must not be smaller than 0
* Both current value and maximum must be real numbers. So no NaN, infinity, etc.
* The HUD bar will be hidden if the maximum equals 0. This is intentional.
* The health and breath HUD bars are hardcoded.

These are soft rules, the HUD bars mod will not enforce all of these.
But this mod has been programmed under the assumption that these rules are followed, for integrity.

## Adding a HUD bar
To make a new HUD bar known to this mod, you need …

* … an image of size 2×16 for the bar
* … an icon of size 16×16 (optional)
* … to register it with `hb.register_hudbar`

### Bar image
The image for the bar will be repeated horizontally to denote the “value” of the HUD bar.
It **must** be of size 2×16.
If neccessary, the image will be split vertically in half, and only the left half of the image
is displayed. So the final HUD bar will always be displayed on a per-pixel basis.

The default bar images are single-colored, but you can use other styles as well, for instance,
a vertical gradient.

### Icon
A 16×16 image shown left of the HUD bar. This is optional.

### `hb.register_hudbar(identifier, text_color, label, textures, default_start_value, default_start_max, default_start_hidden, format_string, format_string_config)`
This function registers a new custom HUD bar definition to the HUD bars mod, so it can be later used to be displayed, changed, hidden
and unhidden on a per-player basis.
Note this does not yet display the HUD bar.

The HUD bars will be displayed in a “first come, first serve” order. This API does not allow fow a custom order or a way to set it
manually in a reliable way. However, you can use the setting `hudbars_sorting` for this. See the advanced setting menu in Minetest
for more information.

#### Parameters
* `identifier`: A globally unique internal name for the HUD bar, will be used later to refer to it. Please only rely on alphanumeric characters for now. The identifiers “`health`” and “`breath`” are used internally for the built-in health and breath bar, respectively. Please do not use these names.
* `text_color`: A 3-octet number defining the color of the text. The octets denote, in this order red, green and blue and range from `0x00` (complete lack of this component) to `0xFF` (full intensity of this component). Example: `0xFFFFFF` for white.
* `label`: A string which is displayed on the HUD bar itself to describe the HUD bar. Try to keep this string short.
* `textures`: A table with the following fields:
 * `bar`: The file name of the bar image (as string). This is only used for the `progress_bar` bar type (see `README.txt`, settings section).
 * `icon`: The file name of the icon, as string. For the `progress_bar` type, it is shown as single image left of the bar, for the two statbar bar types, it is used as the statbar icon and will be repeated. This field can be `nil`, in which case no icon will be used, but this is not recommended, because the HUD bar will be invisible if the one of the statbar bar types is used.
 * `bgicon`: The file name of the background icon, it is used as the background for the modern statbar mode only. This field can be `nil`, in which case no background icon will be displayed in this mode.
* `default_start_value`: If this HUD bar is added to a player, and no initial value is specified, this value will be used as initial current value
* `default_max_value`: If this HUD bar is added to a player, and no initial maximum value is specified, this value will be used as initial maximum value
* `default_start_hidden`: The HUD bar will be initially start hidden by default when added to a player. Use `hb.unhide_hudbar` to unhide it.
* `format_string`: Optional; You can specify an alternative format string to use for the final text on the HUD bar. The default format string is “`@1: @2/@3`” (The “@” numbers are placeholders that have a meaning in this order: @1 = Label, @2 = current value, @3 = maximum value). Do *not* use minetest.translator on this string, the string will be translated by `hudbars`, but you still must put this string into the translation catalogue file.
* `format_string_config`: Required if `format_string` is set. This allows to change which parameters to use in the format string. It's a table with these fields:
 * `textdomain`: Text domain of the format string, used by `minetest.translate`
 * `order`: Table that contains the order of the placeholders. It's also possible to remove placeholders. Default order: `{ "label", "value", "max_value" }`
 * `format_value`: Format string to apply when displaying `value`. Syntax is same as in `string.format`. Default: `"%d"`
 * `format_max_value`: Same as `format_value` but is applied to `max_value`

#### Example
Example (mostly) from `hbarmor` mod:

```
hb.register_hudbar("armor", 0xFFFFFF, minetest.translator("hbarmor", "Armor"), { icon = "hbarmor_icon.png", bgicon = "hbarmor_bgicon.png", bar = "hbarmor_bar.png" }, 0, 100, hbarmor.autohide, N("@1: @2%"), { order = { "label", "value" }, textdomain = "hbarmor" } )
```

Displays an armor HUD bar with a label of the form „Armor: 53%“. (`N` is a dummy function that returns its argument, used to make the string visible for translator scripts.)

#### Return value
Always `nil`.

## Displaying a HUD bar
After a HUD bar has been registered, they are not yet displayed yet for any player. HUD bars must be
explicitly initialized on a per-player basis.

You probably want to do this in the `minetest.register_on_joinplayer`.

### `hb.init_hudbar(player, identifier, start_value, start_max, start_hidden)`
This function initialzes and activates a previously registered HUD bar and assigns it to a
certain client/player. This has only to be done once per player and after that, you can change
the values using `hb.change_hudbar`.

However, if `start_hidden` was set to `true` for the HUD bar (in `hb.register_hudbar`), the HUD bar
will initially be hidden, but the HUD elements are still sent to the client. Otherwise,
the HUD bar will be initially be shown to the player.

#### Parameters
* `player`: `ObjectRef` of the player to which the new HUD bar should be displayed to.
* `identifier`: The identifier of the HUD bar type, as specified in `hb.register_hudbar`.
* `start_value`: The initial current value of the HUD bar. This is optional, `default_start_value` of the registration function will be used, if this is `nil`.
* `start_max`: The initial maximum value of the HUD bar. This is optional, `default_start_max` of the registration function will be used, if this is `nil`
* `start_hidden`: Whether the HUD bar is initially hidden. This is optional, `default_start_hidden` of the registration function will be used as default

#### Return value
`true` on success, `false` otherwise.

## Modifying a HUD bar
After a HUD bar has been added, you can change the current and maximum value and other attributes on a per-player basis.
You use the function `hb.change_hudbar` for this.

### `hb.change_hudbar(player, identifier, new_value, new_max_value, new_icon, new_bgicon, new_bar, new_label, new_text_color)`
Changes the values and the appearance of an initialized HUD bar for a certain player. `new_value`
and `new_max_value` are the most important parameters as they specify the new current and maximum new values, you do not need
to worry too much about the other parameters.

The following parameters are less important and provided for styling the HUD bar after registration (if
this is desired). The “styling” parameters parallel the parameters of `hb.register_hudbar`. It is
recommended to not change the style of a HUD bar too often as this can be distracting or confusing
for players.

`new_value`, `new_max_value` `new_icon`, `new_bgicon`, `new_bar`, `new_label` and `new_text_color` can be
`nil`; if one of them is `nil`, that means the value is unchanged. If all those values are `nil`, this
function is a no-op.

This function tries to minimize the amount of calls to `hud_change` of the Minetest Lua API
(and thus, network traffic), when you only change the value and/or  maximum value. In this case,
`hud_change` is only called if it is actually needed, e.g. when the actual length of the bar
or the displayed string changed, so you do not have to worry about it. There is, however, no
such network optimization for the “styling” parameters, so keep this in mind.

#### Parameters
* `player`: `ObjectRef` of the player to which the HUD bar belongs to
* `identifier`: The identifier of the HUD bar type to change, as specified in `hb.register_hudbar`.
* `new_value`: The new current value of the HUD bar
* `new_max_value`: The new maximum value of the HUD bar
* `new_icon`: File name of the new icon
* `new_bgicon`: File name of the new background icon for the modern-style statbar
* `new_bar`: File name of the new bar segment image
* `new_label`: A new text label of the HUD bar. Note the format string still applies
* `new_text_color`: A 3-octet number defining the new color of the text.

#### Return value
`true` on success, `false` otherwise.

## Hiding and unhiding a HUD bar
You can also hide custom HUD bars, meaning they will not be displayed for a certain player. You can still
use `hb.change_hudbar` on a hidden HUD bar, the new values will be correctly displayed after the HUD bar
has been unhidden. Both functions will only call `hud_change` if there has been an actual change to avoid
unneccessary traffic.

Note that the hidden state of a HUD bar will *not* be saved by this mod on server shutdown, so you may need
to write your own routines for this or by setting the correct value for `start_hidden` when calling
`hb.init_hudbar`.

### `hb.hide_hudbar(player, identifier)`
Hides the specified HUD bar from the screen of the specified player.

#### Parameters
* `player`: `ObjectRef` of the player to which the HUD bar belongs to
* `identifier`: The identifier of the HUD bar type to hide, as specified in `hb.register_hudbar`.

#### Return value
`true` on success, `false` otherwise.

### `hb.unhide_hudbar(player, identifier)`
Makes a previously hidden HUD bar visible again to a player.

#### Parameters
* `player`: `ObjectRef` of the player to which the HUD bar belongs to
* `identifier`: The identifier of the HUD bar type to unhide, as specified in `hb.register_hudbar`.

#### Return value
`true` on success, `false` otherwise.

## Reading HUD bar information
It is also possible to read information about existing HUD bars.

### `hb.get_hudbar_state(player, identifier)`
Returns the current state of the active player's HUD bar.

#### Parameters
* `player`: `ObjectRef` of the player to which the HUD bar belongs to
* `identifier`: The identifier of the HUD bar type to hide, as specified in `hb.register_hudbar`.

#### Return value
On success, returns a table which holds information on the current state of the HUD bar. Note
the table is a deep copy of the internal HUD bar state, it is *not* a reference; the information
hold by the table is only true for the moment you called this function. The fields of this table are:

* `value`: Current value of HUD bar.
* `max`: Current maximum value of HUD bar.
* `hidden`: Boolean denoting whether the HUD bar is hidden.
* `barlength`: The length of the HUD bar in pixels. This field is meaningless if the HUD bar is currently hidden.
* `text`: The text shown on the HUD bar. This fiels is meaningless if the HUD bar is currently hidden.

If the player does not exist, returns `nil` instead.

### `hb.get_hudbar_identifiers()`
Returns a table of all currently registered HUD bar identifiers.


code:
# HUD bars

## Description
This mod changes the HUD of Minetest. It replaces the default health and breath
symbols by horizontal colored bars with text showing the number.

Furthermore, it enables other mods to add their own custom bars to the HUD,
this mod will place them accordingly.

**Important**: Keep in mind if running a server with this mod, that the custom
position should be displayed correctly on every screen size.

## Current version
The current version is 2.3.1.
It works for Minetest 5.3.0.

This software uses [semantic versioning](http://semver.org), as defined by version 2.0.0 of the SemVer
standard.

## Settings
This mod can be configured quite a bit. You can change HUD bar appearance, offsets, ordering, and more.
Use the advanced settings menu in Minetest for detailed configuration.

## API
The API is used to add your own custom HUD bars.
Documentation for the API of this mod can be found in `API.md`.

## Legal
### License of source code
Author: Wuzzy (2015)

Also: This mod was forked from the “Better HUD” [hud] mod by BlockMen.

Translations:

* German: Wuzzy
* Portuguese: BrunoMine
* Turkish: admicos
* Dutch: kingoscargames
* Italian: Hamlet
* Malay: muhdnurhidayat
* Russian: Imk
* Spanish: wuniversales

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the MIT License.

### Licenses of textures

* `hudbars_icon_health.png`—celeron55 (CC BY-SA 3.0), modified by BlockMen
* `hudbars_bgicon_health.png`—celeron55 (CC BY-SA 3.0), modified by BlockMen
* `hudbars_icon_breath.png`—kaeza (MIT License), modified by BlockMen, modified again by Wuzzy
* `hudbars_bgicon_breath.png`—based on previous image, edited by Wuzzy (MIT License)
* `hudbars_bar_health.png`—Wuzzy (MIT License)
* `hudbars_bar_breath.png`—Wuzzy (MIT License)
* `hudbars_bar_background.png`—Wuzzy (MIT License)

### License references

* [CC-BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)
* [MIT License](https://opensource.org/licenses/MIT)

repo_name: korkut/MeseCraft

license: mit


code:
-- (Hardcoded) default settings

hb.settings.max_bar_length = 160
hb.settings.statbar_length = 20

-- Statbar positions
hb.settings.pos_left = {}
hb.settings.pos_right = {}
hb.settings.start_offset_left = {}
hb.settings.start_offset_right= {}
hb.settings.pos_left.x = hb.load_setting("hudbars_pos_left_x", "number", 0.5)
hb.settings.pos_left.y = hb.load_setting("hudbars_pos_left_y", "number", 1)
hb.settings.pos_right.x = hb.load_setting("hudbars_pos_right_x", "number", 0.5)
hb.settings.pos_right.y = hb.load_setting("hudbars_pos_right_y", "number", 1)
hb.settings.bar_type = hb.load_setting("hudbars_bar_type", "string", "progress_bar", {"progress_bar", "statbar_classic", "statbar_modern"})
if hb.settings.bar_type == "progress_bar" then
	hb.settings.start_offset_left.x = hb.load_setting("hudbars_start_offset_left_x", "number", -175)
	hb.settings.start_offset_left.y = hb.load_setting("hudbars_start_offset_left_y", "number", -86)
	hb.settings.start_offset_right.x = hb.load_setting("hudbars_start_offset_right_x", "number", 15)
	hb.settings.start_offset_right.y = hb.load_setting("hudbars_start_offset_right_y", "number", -86)
else
	hb.settings.start_offset_left.x = hb.load_setting("hudbars_start_statbar_offset_left_x", "number", -265)
	hb.settings.start_offset_left.y = hb.load_setting("hudbars_start_statbar_offset_left_y", "number", -90)
	hb.settings.start_offset_right.x = hb.load_setting("hudbars_start_statbar_offset_right_x", "number", 25)
	hb.settings.start_offset_right.y = hb.load_setting("hudbars_start_statbar_offset_right_y", "number", -90)
end
hb.settings.vmargin  = hb.load_setting("hudbars_vmargin", "number", 24)
hb.settings.tick = hb.load_setting("hudbars_tick", "number", 0.1)

-- Experimental setting: Changing this setting is not officially supported, do NOT rely on it!
hb.settings.forceload_default_hudbars = hb.load_setting("hudbars_forceload_default_hudbars", "bool", true)

-- Misc. settings
hb.settings.alignment_pattern = hb.load_setting("hudbars_alignment_pattern", "string", "zigzag", {"zigzag", "stack_up", "stack_down"})
hb.settings.autohide_breath = hb.load_setting("hudbars_autohide_breath", "bool", true)

local sorting = minetest.settings:get("hudbars_sorting")
if sorting ~= nil then
	hb.settings.sorting = {}
	hb.settings.sorting_reverse = {}
	for k,v in string.gmatch(sorting, "(%w+)=(%w+)") do
		hb.settings.sorting[k] = tonumber(v)
		hb.settings.sorting_reverse[tonumber(v)] = k
	end
else
	hb.settings.sorting = { ["health"] = 0, ["breath"] = 1 }
	hb.settings.sorting_reverse = { [0] = "health", [1] = "breath" }
end


code:
local S = minetest.get_translator("hudbars")
local N = function(s) return s end

hb = {}

hb.hudtables = {}

-- number of registered HUD bars
hb.hudbars_count = 0

-- table which records which HUD bar slots have been “registered” so far; used for automatic positioning
hb.registered_slots = {}

hb.settings = {}

function hb.load_setting(sname, stype, defaultval, valid_values)
	local sval
	if stype == "string" then
		sval = minetest.settings:get(sname)
	elseif stype == "bool" then
		sval = minetest.settings:get_bool(sname)
	elseif stype == "number" then
		sval = tonumber(minetest.settings:get(sname))
	end
	if sval ~= nil then
		if valid_values ~= nil then
			local valid = false
			for i=1,#valid_values do
				if sval == valid_values[i] then
					valid = true
				end
			end
			if not valid then
				minetest.log("error", "[hudbars] Invalid value for "..sname.."! Using default value ("..tostring(defaultval)..").")
				return defaultval
			else
				return sval
			end
		else
			return sval
		end
	else
		return defaultval
	end
end

-- Load default settings
dofile(minetest.get_modpath("hudbars").."/default_settings.lua")

local function player_exists(player)
	return player ~= nil and player:is_player()
end

local function make_label(format_string, format_string_config, label, start_value, max_value)
	local params = {}
	local order = format_string_config.order
	for o=1, #order do
		if order[o] == "label" then
			table.insert(params, label)
		elseif order[o] == "value" then
			if format_string_config.format_value then
				table.insert(params, string.format(format_string_config.format_value, start_value))
			else
				table.insert(params, start_value)
			end
		elseif order[o] == "max_value" then
			if format_string_config.format_max_value then
				table.insert(params, string.format(format_string_config.format_max_value, max_value))
			else
				table.insert(params, max_value)
			end
		end
	end
	local ret
	if format_string_config.textdomain then
		ret = minetest.translate(format_string_config.textdomain, format_string, unpack(params))
	else
		ret = S(format_string, unpack(params))
	end
--commenting out original to output a blank string
--	return ret
	return ""
end

-- Table which contains all players with active default HUD bars (only for internal use)
hb.players = {}

function hb.value_to_barlength(value, max)
	if max == 0 then
		return 0
	else
		if hb.settings.bar_type == "progress_bar" then
			local x
			if value < 0 then x=-0.5 else x = 0.5 end
			local ret = math.modf((value/max) * hb.settings.max_bar_length + x)
			return ret
		else
			local x
			if value < 0 then x=-0.5 else x = 0.5 end
			local ret = math.modf((value/max) * hb.settings.statbar_length + x)
			return ret
		end
	end
end

function hb.get_hudtable(identifier)
	return hb.hudtables[identifier]
end

function hb.get_hudbar_position_index(identifier)
	if hb.settings.sorting[identifier] ~= nil then
		return hb.settings.sorting[identifier]
	else
		local i = 0
		while true do
			if hb.registered_slots[i] ~= true and hb.settings.sorting_reverse[i] == nil then
				return i
			end
			i = i + 1
		end
	end
end

function hb.register_hudbar(identifier, text_color, label, textures, default_start_value, default_start_max, default_start_hidden, format_string, format_string_config)
	local hudtable = {}
	local pos, offset
	local index = math.floor(hb.get_hudbar_position_index(identifier))
	hb.registered_slots[index] = true
	if hb.settings.alignment_pattern == "stack_up" then
		pos = hb.settings.pos_left
		offset = {
			x = hb.settings.start_offset_left.x,
			y = hb.settings.start_offset_left.y - hb.settings.vmargin * index
		}
	elseif hb.settings.alignment_pattern == "stack_down" then
		pos = hb.settings.pos_left
		offset = {
			x = hb.settings.start_offset_left.x,
			y = hb.settings.start_offset_left.y + hb.settings.vmargin * index
		}
	else
		if index % 2 == 0 then
			pos = hb.settings.pos_left
			offset = {
				x = hb.settings.start_offset_left.x,
				y = hb.settings.start_offset_left.y - hb.settings.vmargin * (index/2)
			}
		else
			pos = hb.settings.pos_right
			offset = {
				x = hb.settings.start_offset_right.x,
				y = hb.settings.start_offset_right.y - hb.settings.vmargin * ((index-1)/2)
			}
		end
	end
	if format_string == nil then
		format_string = N("@1: @2/@3")
	end
	if format_string_config == nil then
		format_string_config = {}
	end
	if format_string_config.order == nil then
		format_string_config.order = { "label", "value", "max_value" }
	end
	if format_string_config.format_value == nil then
		format_string_config.format_value = "%d"
	end
	if format_string_config.format_max_value == nil then
		format_string_config.format_max_value = "%d"
	end

	hudtable.add_all = function(player, hudtable, start_value, start_max, start_hidden)
		if start_value == nil then start_value = hudtable.default_start_value end
		if start_max == nil then start_max = hudtable.default_start_max end
		if start_hidden == nil then start_hidden = hudtable.default_start_hidden end
		local ids = {}
		local state = {}
		local name = player:get_player_name()
		local bgscale, iconscale, text, barnumber, bgiconnumber
		if start_max == 0 or start_hidden then
			bgscale = { x=0, y=0 }
		else
			bgscale = { x=1, y=1 }
		end
		if start_hidden then
			iconscale = { x=0, y=0 }
			barnumber = 0
			bgiconnumber = 0
			text = ""
		else
			iconscale = { x=1, y=1 }
			barnumber = hb.value_to_barlength(start_value, start_max)
			bgiconnumber = hb.settings.statbar_length
			text = make_label(format_string, format_string_config, label, start_value, start_max)
		end
		if hb.settings.bar_type == "progress_bar" then
			ids.bg = player:hud_add({
				hud_elem_type = "image",
				position = pos,
				scale = bgscale,
				text = "hudbars_bar_background.png",
				alignment = {x=1,y=1},
				offset = { x = offset.x - 1, y = offset.y - 1 },
				z_index = 0,
			})
			if textures.icon ~= nil then
				ids.icon = player:hud_add({
					hud_elem_type = "image",
					position = pos,
					scale = iconscale,
					text = textures.icon,
					alignment = {x=-1,y=1},
					offset = { x = offset.x - 3, y = offset.y },
					z_index = 1,
				})
			end
		end
		local bar_image, bgicon, bar_size
		if hb.settings.bar_type == "progress_bar" then
			bar_image = textures.bar
			-- NOTE: Intentionally set to nil. For some reason, on some systems,
			-- the progress bar is displaced when the bar_size is set explicitly here.
			-- On the other hand, setting this to nil is deprecated in MT 5.0.0 due to
			-- a debug log warning, but nothing is explained in lua_api.txt.
			-- This section is a potential bug magnet, please watch with care!
			-- The size of the bar image is expected to be exactly 2×16 pixels.
			bar_size = nil
		elseif hb.settings.bar_type == "statbar_classic" or hb.settings.bar_type == "statbar_modern" then
			bar_image = textures.icon
			bgicon = textures.bgicon
			bar_size = {x=24, y=24}
		end
		ids.bar = player:hud_add({
			hud_elem_type = "statbar",
			position = pos,
			text = bar_image,
			text2 = bgicon,
			number = barnumber,
			item = bgiconnumber,
			alignment = {x=-1,y=-1},
			offset = offset,
			direction = 0,
			size = bar_size,
			z_index = 1,
		})
		if hb.settings.bar_type == "progress_bar" then
			ids.text = player:hud_add({
				hud_elem_type = "text",
				position = pos,
				text = text,
				alignment = {x=1,y=1},
				number = text_color,
				direction = 0,
				offset = { x = offset.x + 2,  y = offset.y - 1},
				z_index = 2,
		})
		end
		-- Do not forget to update hb.get_hudbar_state if you add new fields to the state table
		state.hidden = start_hidden
		state.value = start_value
		state.max = start_max
		state.text = text
		state.barlength = hb.value_to_barlength(start_value, start_max)

		local main_error_text =
			"[hudbars] Bad initial values of HUD bar identifier “"..tostring(identifier).."” for player "..name..". "

		if start_max < start_value then
			minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than start_value ("..start_value..")!")
		end
		if start_max < 0 then
			minetest.log("error", main_error_text.."start_max ("..start_max..") is smaller than 0!")
		end
		if start_value < 0 then
			minetest.log("error", main_error_text.."start_value ("..start_value..") is smaller than 0!")
		end

		hb.hudtables[identifier].hudids[name] = ids
		hb.hudtables[identifier].hudstate[name] = state
	end

	hudtable.identifier = identifier
	hudtable.format_string = format_string
	hudtable.format_string_config = format_string_config
	hudtable.label = label
	hudtable.hudids = {}
	hudtable.hudstate = {}
	hudtable.default_start_hidden = default_start_hidden
	hudtable.default_start_value = default_start_value
	hudtable.default_start_max = default_start_max

	hb.hudbars_count= hb.hudbars_count + 1

	hb.hudtables[identifier] = hudtable
end

function hb.init_hudbar(player, identifier, start_value, start_max, start_hidden)
	if not player_exists(player) then return false end
	local hudtable = hb.get_hudtable(identifier)
	hb.hudtables[identifier].add_all(player, hudtable, start_value, start_max, start_hidden)
	return true
end

function hb.change_hudbar(player, identifier, new_value, new_max_value, new_icon, new_bgicon, new_bar, new_label, new_text_color)
	if new_value == nil and new_max_value == nil and new_icon == nil and new_bgicon == nil and new_bar == nil and new_label == nil and new_text_color == nil then
		return true
	end
	if not player_exists(player) then
		return false
	end

	local name = player:get_player_name()
	local hudtable = hb.get_hudtable(identifier)
	if not hudtable.hudstate[name] then
		return false
	end
	local value_changed, max_changed = false, false

	if new_value ~= nil then
		if new_value ~= hudtable.hudstate[name].value then
			hudtable.hudstate[name].value = new_value
			value_changed = true
		end
	else
		new_value = hudtable.hudstate[name].value
	end
	if new_max_value ~= nil then
		if new_max_value ~= hudtable.hudstate[name].max then
			hudtable.hudstate[name].max = new_max_value
			max_changed = true
		end
	else
		new_max_value = hudtable.hudstate[name].max
	end

	if hb.settings.bar_type == "progress_bar" then
		if new_icon ~= nil and hudtable.hudids[name].icon ~= nil then
			player:hud_change(hudtable.hudids[name].icon, "text", new_icon)
		end
		if new_bgicon ~= nil and hudtable.hudids[name].bgicon ~= nil then
			player:hud_change(hudtable.hudids[name].bgicon, "text", new_bgicon)
		end
		if new_bar ~= nil then
			player:hud_change(hudtable.hudids[name].bar , "text", new_bar)
		end
		if new_label ~= nil then
			hudtable.label = new_label
			local new_text = make_label(hudtable.format_string, hudtable.format_string_config, new_label, hudtable.hudstate[name].value, hudtable.hudstate[name].max)
			player:hud_change(hudtable.hudids[name].text, "text", new_text)
		end
		if new_text_color ~= nil then
			player:hud_change(hudtable.hudids[name].text, "number", new_text_color)
		end
	else
		if new_icon ~= nil and hudtable.hudids[name].bar ~= nil then
			player:hud_change(hudtable.hudids[name].bar, "text", new_icon)
		end
		if new_bgicon ~= nil and hudtable.hudids[name].bg ~= nil then
			player:hud_change(hudtable.hudids[name].bg, "text", new_bgicon)
		end
	end

	local main_error_text =
		"[hudbars] Bad call to hb.change_hudbar, identifier: “"..tostring(identifier).."”, player name: “"..name.."”. "
	if new_max_value < new_value then
		minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than new_value ("..new_value..")!")
	end
	if new_max_value < 0 then
		minetest.log("error", main_error_text.."new_max_value ("..new_max_value..") is smaller than 0!")
	end
	if new_value < 0 then
		minetest.log("error", main_error_text.."new_value ("..new_value..") is smaller than 0!")
	end

	if hudtable.hudstate[name].hidden == false then
		if max_changed and hb.settings.bar_type == "progress_bar" then
			if hudtable.hudstate[name].max == 0 then
				player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
			else
				player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
			end
		end

		if value_changed or max_changed then
			local new_barlength = hb.value_to_barlength(new_value, new_max_value)
			if new_barlength ~= hudtable.hudstate[name].barlength then
				player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(new_value, new_max_value))
				hudtable.hudstate[name].barlength = new_barlength
			end

			if hb.settings.bar_type == "progress_bar" then
				local new_text = make_label(hudtable.format_string, hudtable.format_string_config, hudtable.label, new_value, new_max_value)
				if new_text ~= hudtable.hudstate[name].text then
					player:hud_change(hudtable.hudids[name].text, "text", new_text)
					hudtable.hudstate[name].text = new_text
				end
			end
		end
	end
	return true
end

function hb.hide_hudbar(player, identifier)
	if not player_exists(player) then return false end
	local name = player:get_player_name()
	local hudtable = hb.get_hudtable(identifier)
	if hudtable == nil then return false end
	if hb.settings.bar_type == "progress_bar" then
		if hudtable.hudids[name].icon ~= nil then
			player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
		end
		player:hud_change(hudtable.hudids[name].bg, "scale", {x=0,y=0})
		player:hud_change(hudtable.hudids[name].text, "text", "")
	end
	player:hud_change(hudtable.hudids[name].bar, "number", 0)
	player:hud_change(hudtable.hudids[name].bar, "item", 0)
	hudtable.hudstate[name].hidden = true
	return true
end

function hb.unhide_hudbar(player, identifier)
	if not player_exists(player) then return false end
	local name = player:get_player_name()
	local hudtable = hb.get_hudtable(identifier)
	if hudtable == nil then return false end
	local value = hudtable.hudstate[name].value
	local max = hudtable.hudstate[name].max
	if hb.settings.bar_type == "progress_bar" then
		if hudtable.hudids[name].icon ~= nil then
			player:hud_change(hudtable.hudids[name].icon, "scale", {x=1,y=1})
		end
		if hudtable.hudstate[name].max ~= 0 then
			player:hud_change(hudtable.hudids[name].bg, "scale", {x=1,y=1})
		end
		player:hud_change(hudtable.hudids[name].text, "text", make_label(hudtable.format_string, hudtable.format_string_config, hudtable.label, value, max))
	elseif hb.settings.bar_type == "statbar_modern" then
		player:hud_change(hudtable.hudids[name].bar, "scale", {x=1,y=1})
	end
	player:hud_change(hudtable.hudids[name].bar, "number", hb.value_to_barlength(value, max))
	player:hud_change(hudtable.hudids[name].bar, "item", hb.value_to_barlength(max, max))
	hudtable.hudstate[name].hidden = false
	return true
end

function hb.get_hudbar_state(player, identifier)
	if not player_exists(player) then return nil end
	local ref = hb.get_hudtable(identifier).hudstate[player:get_player_name()]
	-- Do not forget to update this chunk of code in case the state changes
	local copy = {
		hidden = ref.hidden,
		value = ref.value,
		max = ref.max,
		text = ref.text,
		barlength = ref.barlength,
	}
	return copy
end

function hb.get_hudbar_identifiers()
	local ids = {}
	for id, _ in pairs(hb.hudtables) do
		table.insert(ids, id)
	end
	return ids
end

--register built-in HUD bars
if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
	hb.register_hudbar("health", 0xFFFFFF, S("Health"), { bar = "hudbars_bar_health.png", icon = "hudbars_icon_health.png", bgicon = "hudbars_bgicon_health.png" }, 20, 20, false)
	hb.register_hudbar("breath", 0xFFFFFF, S("Breath"), { bar = "hudbars_bar_breath.png", icon = "hudbars_icon_breath.png", bgicon = "hudbars_bgicon_breath.png" }, 10, 10, true)
end

local function hide_builtin(player)
	local flags = player:hud_get_flags()
	flags.healthbar = false
	flags.breathbar = false
	player:hud_set_flags(flags)
end

local function custom_hud(player)
	if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
		local hide
		if minetest.settings:get_bool("enable_damage") then
			hide = false
		else
			hide = true
		end
		local hp = player:get_hp()
		local hp_max = player:get_properties().hp_max
		hb.init_hudbar(player, "health", math.min(hp, hp_max), hp_max, hide)
		local breath = player:get_breath()
		local breath_max = player:get_properties().breath_max
		local hide_breath
		if breath >= breath_max and hb.settings.autohide_breath == true then hide_breath = true else hide_breath = false end
		hb.init_hudbar(player, "breath", math.min(breath, breath_max), breath_max, hide_breath or hide)
	end
end

local function update_health(player)
	local hp_max = player:get_properties().hp_max
	hb.change_hudbar(player, "health", player:get_hp(), hp_max)
end

-- update built-in HUD bars
local function update_hud(player)
	if not player_exists(player) then return end
	if minetest.settings:get_bool("enable_damage") then
		if hb.settings.forceload_default_hudbars then
			hb.unhide_hudbar(player, "health")
		end
		--air
		local breath_max = player:get_properties().breath_max
		local breath = player:get_breath()

		if breath >= breath_max and hb.settings.autohide_breath == true then
			hb.hide_hudbar(player, "breath")
		else
			hb.unhide_hudbar(player, "breath")
			hb.change_hudbar(player, "breath", math.min(breath, breath_max), breath_max)
		end
		--health
		update_health(player)
	elseif hb.settings.forceload_default_hudbars then
		hb.hide_hudbar(player, "health")
		hb.hide_hudbar(player, "breath")
	end
end

minetest.register_on_player_hpchange(function(player)
	if hb.players[player:get_player_name()] ~= nil then
		update_health(player)
	end
end)

minetest.register_on_respawnplayer(function(player)
	update_health(player)
	hb.hide_hudbar(player, "breath")
end)

minetest.register_on_joinplayer(function(player)
	hide_builtin(player)
	custom_hud(player)
	hb.players[player:get_player_name()] = player
end)

minetest.register_on_leaveplayer(function(player)
	hb.players[player:get_player_name()] = nil
end)

local main_timer = 0
local timer = 0
minetest.register_globalstep(function(dtime)
	main_timer = main_timer + dtime
	timer = timer + dtime
	if main_timer > hb.settings.tick or timer > 4 then
		if main_timer > hb.settings.tick then main_timer = 0 end
		-- only proceed if damage is enabled
		if minetest.settings:get_bool("enable_damage") or hb.settings.forceload_default_hudbars then
			for _, player in pairs(hb.players) do
				-- update all hud elements
				update_hud(player)
			end
		end
	end
	if timer > 4 then timer = 0 end
end)


code:
# textdomain: hudbars
Health=Leben
Breath=Atem
@1: @2/@3=@1: @2/@3


code:
# textdomain: hudbars
Health=Salud
Breath=Aliento
@1: @2/@3=@1: @2/@3


code:
# textdomain: hudbars
Health=Salute
Breath=Ossigeno

# Default format string for progress bar-style HUD bars, e.g. “Health 5/20”
@1: @2/@3=@1: @2/@3


code:
# textdomain: hudbars
Health=Kesihatan
Breath=Nafas
@1: @2/@3=@1: @2/@3


code:
# textdomain: hudbars
Health=Gezondheid
Breath=Adem

# Default format string for progress bar-style HUD bars, e.g. “Health 5/20”
@1: @2/@3=@1: @2/@3


code:
# textdomain: hudbars
Health=Saude
Breath=Folego

# Formato de string padrão para progresso bar-style de barras do HUD, por exemplo “Saude 5/20”
@1: @2/@3=@1: @2/@3


code:
# textdomain: hudbars
Health=HP
Breath=дыхание
@1: @2/@3=@1: @2/@3


code:
# textdomain: hudbars
Health=Can
Breath=Nefes
@1: @2/@3=@1: @2/@3


code:
# textdomain: hudbars
Health=
Breath=

# Default format string for progress bar-style HUD bars, e.g. “Health 5/20”
@1: @2/@3=


code:
name = hudbars
description = Replaces the health and breath symbols in the HUD by “progress bars” and shows exact values. Other mods can add more progress bars for custom player stats.


code:
[Appearance]
# Specifies how the value indicators (i.e. health, breah, etc.) look. There are 3 styles
# available. You can choose between the default progress-bar-like bars and the good
# old statbars like you know from vanilla Minetest.
# These values are possible:
#   - progress_bar:    A horizontal progress-bar-like bar with a label, showing numerical value
#                      (current, maximum), and an icon. These bars usually convey the most
#                      information. This is the default and recommended value.
#   - statbar_classic: Classic statbar, like in vanilla Minetest. Made out of up to 20
#                      half-symbols. Those bars represent the vague ratio between
#                      the current value and the maximum value. 1 half-symbol stands for
#                      approximately 5% of the maximum value.
#   - statbar_modern:  Like the classic statbar, but also supports background images, this
#                      kind of statbar may be considered to be more user-friendly than the
#                      classic statbar. This bar type closely resembles the mod
#                      “Better HUD” [hud] by BlockMen.
hudbars_bar_type (HUD bars style) enum progress_bar progress_bar,statbar_classic,statbar_modern

# If enabled (default), the breath indicators in the HUD will be automatically hidden shortly
# after the breath has been filled up. Otherwise, the breath will always be displayed.
hudbars_autohide_breath (Automatically hide breath indicators) bool true

# This setting changes the way the HUD bars are ordered on the display. You can choose
# between a zig-zag pattern (default) or a vertically stacked pattern.
# The following values are allowed:
#    - zigzag:     Starting from the left bottom, the next is right from the first,
#                  the next is above the first, the next is right of the third, etc.
#    - stack_up:   The HUD bars are stacked vertically, going upwards.
#    - stack_down: The HUD bars are stacked vertically, going downwards.
hudbars_alignment_pattern (HUD bars alignment pattern) enum zigzag zigzag,stack_up,stack_down

# This setting allows you to specify the order of the HUD bars explicitly. If left empty
# (the default), the health and breath indicators come first, additional indicators
# may appear in any order. This setting is quite technical and normal users probably do not
# need to worry about it.
#
# Syntax:
# The setting has to be specified as a comma-seperated list of key=value pairs, where a key
# refers to the identifier of a HUD bar and the value refers to the slot number of where the
# HUD bar should be placed. The slot number must be an integer greater of equal to 0. Where
# the HUD bars will be displayed exactly depends on the alignment pattern being used.
# All HUD bars to which no order value has been applied will fill in all slots which have
# not been occupied by the HUD bars specified in this setting, the slots will be filled in
# from the lowest slot number.
# Note that the order of those remaining HUD bars is not fixed, it basically just boils
# down on which mod “came” first. Don't worry, the mod will still work perfectly fine, this
# setting is entirely optional.
# The identifier for the health bar is “health” and the identifier for the breath bar is
# “breath”. For other HUD bars, you have to learn it from the mod which is supplying them.
#
# Be careful not to use slot indices twice, or else different HUD bars will be drawn over
# each other!
#
# Example: “breath=0, health=1”
# This makes the breath bar first and the health bar second, which is the opposite order
# of the default one.
hudbars_sorting (HUD bars order) string

[Positions and offsets]
# Horizontal (x) main position of the HUD bars over the entire screen.
# 0.0 is left-most, 1.0 is right-most.
# For the zig-zag alignment pattern, this is for the left HUD bars.
hudbars_pos_left_x (Left HUD bar screen x position) float 0.5 0.0 1.0
# Vertical (y) main position of the HUD bars over the entire screen.
# 0.0 is top, 1.0 is bottom.
# For the zig-zag alignment pattern, this is for the left HUD bars.
hudbars_pos_left_y (Left HUD bar screen y position) float 1.0 0.0 1.0
# Horizontal (x) main position of the right HUD bars over the entire screen.
# 0.0 is left-most, 1.0 is right-most.
# Only used for the zig-zag alignment pattern.
hudbars_pos_right_x (Right HUD bar screen x position) float 0.5 0.0 1.0
# Vertical main position (y) of the right HUD bars over the entire screen.
# 0.0 is top, 1.0 is bottom.
# Only used for the zig-zag alignment pattern.
hudbars_pos_right_y (Right HUD bar screen y position) float 1.0 0.0 1.0

# Precise x offset in pixels from the basic screen x position of the HUD bars.
# For the zig-zag alignment pattern, this is for the left HUD bars.
# This setting is used for the progress bar HUD bar style.
hudbars_start_offset_left_x (Left HUD bar x offset) int -175
# Precise y offset in pixels from the basic screen y position of the HUD bars.
# For the zig-zag alignment pattern, this is for the left HUD bars.
# This setting is used for the progress bar HUD bar style.
hudbars_start_offset_left_y (Left HUD bar y offset) int -86
# Precise x offset in pixels from the basic screen x position of the right HUD bars.
# Only used for the zig-zag alignment pattern.
# This setting is used for the progress bar HUD bar style.
hudbars_start_offset_right_x (Right HUD bar x offset) int 15
# Precise y offset in pixels from the basic screen y position of the right HUD bars.
# Only used for the zig-zag alignment pattern.
# This setting is used for the progress bar HUD bar style.
hudbars_start_offset_right_y (Right HUD bar y offset) int -86

# Precise x offset in pixels from the basic screen x position of the HUD statbars.
# For the zig-zag alignment pattern, this is for the left HUD statbars.
# This setting is used for the classic and modern statbar styles.
hudbars_start_statbar_offset_left_x (Left HUD statbar x offset) int -265
# Precise y offset in pixels from the basic screen y position of the HUD statbars.
# For the zig-zag alignment pattern, this is for the left HUD statbars.
# This setting is used for the classic and modern statbar styles.
hudbars_start_statbar_offset_left_y (Left HUD statbar y offset) int -90
# Precise x offset in pixels from the basic screen x position of the right HUD statbars.
# Only used for the zig-zag alignment pattern.
# This setting is used for the classic and modern statbar styles.
hudbars_start_statbar_offset_right_x (Right HUD statbar x offset) int 25
# Precise y offset in pixels from the basic screen y position of the right HUD statbars.
# Only used for the zig-zag alignment pattern.
# This setting is used for the classic and modern statbar styles.
hudbars_start_statbar_offset_right_y (Right HUD statbar y offset) int -90

# The vertical distance between two HUD bars, in pixels.
hudbars_vmargin (Vertical distance between HUD bars) int 24 0

[Performance]
# The of seconds which need to pass before the server updates the default HUD bars
# (health and breath). Increase this number if you have a slow server or a slow network
# connection and experience performance problems.
hudbars_tick (Default HUD bars update interval) float 0.1 0.0 4.0


code:
API documentation for Mana mod
==============================

## Introduction
The API of the Mana mod allows you to set and receive
the current and maxiumum mana reserves of a player,
and to subtract and add mana.

## The basic rules
For integrity reasons, this mod will ensure that the following assumptions
are true at all times for all players:

* Current and maximum mana can never be smaller than 0
* The current value must not be greater than the maximum value
* Only integer numbers are permitted for mana values

It should be not possible to break these rules using this API alone.
If you somehow manage to break one ofthe rules, please report a bug.

If a real number is used as input for a value, it will be rounded
(“round up half” rule).

## Functions
Of not specified otherwise, all functions return `nil`.
`playername` always refers to the name of a player, as string.
`value` always refers to a number and for most functions it must always be equal to or greater than 0.

### `mana.set(playername, value)`
Sets the mana reserve of the specified player to `value`.
If `value` is smaller than 0, the mana will be set to 0.
If `value` is greater than the maximum, the mana will be set to the maximum.

### `mana.setmax(playername, value)`
Sets the maximum of the player to `value`.

If the new maximum would become smaller than the current value,
the current value will automatically be set to
the new maximum.

### `mana.setregen(playername, value)`
Sets the mana regeneration per mana tick of the player to `value`.
Floating-point numbers are also permitted, in which the generation
of 1 mana takes longer than 1 mana tick. I.e. `0.5`. means
that 1 mana is generated every 2 mana ticks. A negative value means the
player loses mana.

The length of one “mana tick” is specified as the server-wide setting
`mana_default_regen` in seconds.

### `mana.get(playername)`
Returns the current mana of the specified player as number.

### `mana.getmax(playername)`
Returns the current maximum mana of the specified player as number.

### `mana.getregen(playername)`
Returns the current mana regneration per mana tick of the specified
player as number.
The length of one “mana tick” is specified as the server-wide setting
`mana_default_regen` in seconds.

### `mana.add(playername, value)`
Adds the specified non-negative amount of mana to the player, but only
if the sum would not be greater than the maximum,

#### Return value
* `true` on success, all mana has been added
* `false` on failure, no mana has been added

### `mana.subtract(playername, value)`
Subtracts the specified non-negative amount of mana from the player,
but only if the player has sufficient mana reservers.

#### Return value
* `true` on success, all mana has been subtracted
* `false` on failure, no mana has been subtraceed

### `mana.add_up_to(playername, value)`
Adds the specified non-negative amount of mana to the player, but it will
be capped at the maximum.

#### Return value
* `true, excess` on success, where `excess` is the amount of Mana which could not be added because it would have exceeded the maximum. `excess` equals `0` if all mana has been added
* `false` on failure (mana could not be added)

### `mana.subtract_up_to(playername, value)`
Subtracts the specified non-negative amount of mana from the player,
but if the difference is smaller than 0, the mana will be set to 0.

#### Return value
* `true, missing` on success, where `missing` is the amount of Mana which could not be subtracted because it would have exceeded 0. `missing` equals `0` if all mana has been subtracted
* `false` on failure (mana could not be subtracted)

## Appendix
### General recommendations
If you want your mod to be portable, it is recommended that you balance your mod in such a way that it assumes
that every player starts with the following default mana values:

* Max. mana: 200
* Mana regeneration: 1 mana every 0.2 seconds

Also assume that the max. mana never changes.
This should (hopefully) ensure that multiple independent mana-using mods are more or less balanced when using
the default settings.

Also, to make life easier for subgame makers, define custom minetest.conf settings for your mod in order to
overwrite the mana costs (and other relevant values) used by your mod. That way, subgame makers only have to edit
minetest.conf, and not your mod.

You do not have to bother about default values if you want to directly integrate your mod in a subgame and do
not plan to release the mod independently.

The best way to reliable balance the mana values used by several mods is to create a standalone subgame. It is
highly recommended that you tweak the mana values of the mods to fit the subgame's needs.


code:
# Mana [mana]

* Version: 1.3.1
* Note: This mod uses semantic versioning, as defined by version 2.0.0 of the SemVer standard. See: <http://semver.org/>

## Description
This mod adds basic support for mana to Minetest.

Each player will have an additional attribute: Mana. To be precise:

* Current mana reserves: How much mana the player currently has
* Maximum possible mana: How much mana the player can have at maximum
* Mana regeneration amont: How much mana will be generated each “mana tick” (default 0.2 seconds)

By default, each player spawns with 0/200 mana, and regenerates 1 mana per fifth of a second. All
these values can be configured with the server settings (`minetest.conf`) and it is highly advised to do
so if you plan to integrate this mod into a game.

The mana regeneration rate (the “mana tick”) is a global setting and is *not* configured on a
per-player basis.

The mod provides a simple API to set, get, add and subtract the mana (and maximum) mana of any player, and
for setting the regeneration amount. Note that this mod itself does *not* change the gameplay in a meaningful
way. You should install other mods which use the Mana mod as a dependency.

The API documentation is in the file `API.md`.

If the mod “HUD bars” [`hudbars`] is installed, a blue bar will be added to the HUD showing the player's mana reserves.

Otherwise, the mana is just shown as text.

## Configuration

This mod can be configured with minetest.conf! The following settings are accepted:

* `mana_default_max`: Initial max. mana given to new players. Default: `200`. This value must be non-negative.
* `mana_default_regen`: Initial regenerated mana per “mana tick”. Default: `1`. This value can be a floating value. I.e. if the value is `0.5`, 1 mana is generated every two mana ticks (note that the player's mana amount is still a whole number). This value can be negative, in which case the player will lose mana.
* `mana_regen_timer`: The length of a “mana tick” in seconds. Each player will get his/her mana increased by the current
  regen value per mana tick. Default: `0.2`. This value must be positive, also try to avoid very small values
  as those could probably stress your machine a lot.

License information
===================
* `textures/mana_icon.png`: [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/) by [Buch](http://opengameart.org/users/Buch).
* `textures/mana_bgicon.png`: CC BY 3.0, originally by Buch, modified by Wuzzy.
* `textures/mana_bar.png`: [MIT License](https://opensource.org/licenses/MIT) by Wuzzy.
* Everything else: MIT License.


code:
--[[
Mana
This mod adds mana to players, a special attribute

License: MIT License
]]

--[===[
	Initialization
]===]

local S = minetest.get_translator("mana")

mana = {}
mana.playerlist = {}

mana.settings = {}
mana.settings.default_max = 200
mana.settings.default_regen = 1
mana.settings.regen_timer = 1.0

do
	local default_max = tonumber(minetest.settings:get("mana_default_max"))
	if default_max ~= nil then
		mana.settings.default_max = default_max
	end

	local default_regen = tonumber(minetest.settings:get("mana_default_regen"))
	if default_regen ~= nil then
		mana.settings.default_regen = default_regen
	end

	local regen_timer = tonumber(minetest.settings:get("mana_regen_timer"))
	if regen_timer ~= nil then
		mana.settings.regen_timer = regen_timer
	end
end

--[===[
	API functions
]===]

function mana.set(playername, value)
	if value < 0 then
		minetest.log("info", "[mana] Warning: mana.set was called with negative value!")
		value = 0
	end
	value = mana.round(value)
	if value > mana.playerlist[playername].maxmana then
		value = mana.playerlist[playername].maxmana
	end
	if mana.playerlist[playername].mana ~= value then
		mana.playerlist[playername].mana = value
		mana.hud_update(playername)
	end
end

function mana.setmax(playername, value)
	if value < 0 then
		value = 0
		minetest.log("info", "[mana] Warning: mana.setmax was called with negative value!")
	end
	value = mana.round(value)
	if mana.playerlist[playername].maxmana ~= value then
		mana.playerlist[playername].maxmana = value
		if(mana.playerlist[playername].mana > value) then
			mana.playerlist[playername].mana = value
		end
		mana.hud_update(playername)
	end
end

function mana.setregen(playername, value)
	mana.playerlist[playername].regen = value
end

function mana.get(playername)
	return mana.playerlist[playername].mana
end

function mana.getmax(playername)
	return mana.playerlist[playername].maxmana
end

function mana.getregen(playername)
	return mana.playerlist[playername].regen
end

function mana.add_up_to(playername, value)
	local t = mana.playerlist[playername]
	value = mana.round(value)
	if(t ~= nil and value >= 0) then
		local excess
		if((t.mana + value) > t.maxmana) then
			excess = (t.mana + value) - t.maxmana
			t.mana = t.maxmana
		else
			excess = 0
			t.mana = t.mana + value
		end
		mana.hud_update(playername)
		return true, excess
	else
		return false
	end
end

function mana.add(playername, value)
	local t = mana.playerlist[playername]
	value = mana.round(value)
	if(t ~= nil and ((t.mana + value) <= t.maxmana) and value >= 0) then
		t.mana = t.mana + value
		mana.hud_update(playername)
		return true
	else
		return false
	end
end

function mana.subtract(playername, value)
	local t = mana.playerlist[playername]
	value = mana.round(value)
	if(t ~= nil and t.mana >= value and value >= 0) then
		t.mana = t.mana -value
		mana.hud_update(playername)
		return true
	else
		return false
	end
end

function mana.subtract_up_to(playername, value)
	local t = mana.playerlist[playername]
	value = mana.round(value)
	if(t ~= nil and value >= 0) then
		local missing
		if((t.mana - value) < 0) then
			missing = math.abs(t.mana - value)
			t.mana = 0
		else
			missing = 0
			t.mana = t.mana - value
		end
		mana.hud_update(playername)
		return true, missing
	else
		return false
	end
end

--[===[
	File handling, loading data, saving data, setting up stuff for players.
]===]

-- Load the playerlist from a previous session, if available.
do
	local filepath = minetest.get_worldpath().."/mana.mt"
	local file = io.open(filepath, "r")
	if file then
		local string = file:read()
		io.close(file)
		if(string ~= nil) then
			local savetable = minetest.deserialize(string)
			mana.playerlist = savetable.playerlist
		end
	end
end

function mana.save_to_file()
	local savetable = {}
	savetable.playerlist = mana.playerlist

	local savestring = minetest.serialize(savetable)

	local filepath = minetest.get_worldpath().."/mana.mt"
	local file = io.open(filepath, "w")
	if file then
		file:write(savestring)
		io.close(file)
	else
		minetest.log("error", "[mana] Failed to write mana data into "..filepath..".")
	end
end

minetest.register_on_respawnplayer(function(player)
	local playername = player:get_player_name()
	mana.set(playername, 0)
	mana.hud_update(playername)
end)

minetest.register_on_leaveplayer(function(player)
	local playername = player:get_player_name()
	if not minetest.get_modpath("hudbars") ~= nil then
		mana.hud_remove(playername)
	end
	mana.save_to_file()
end)

minetest.register_on_shutdown(function()
	mana.save_to_file()
end)

minetest.register_on_joinplayer(function(player)
	local playername = player:get_player_name()

	if mana.playerlist[playername] == nil then
		mana.playerlist[playername] = {}
		mana.playerlist[playername].mana = 0
		mana.playerlist[playername].maxmana = mana.settings.default_max
		mana.playerlist[playername].regen = mana.settings.default_regen
		mana.playerlist[playername].remainder = 0
	end

	if minetest.get_modpath("hudbars") ~= nil then
		hb.init_hudbar(player, "mana", mana.get(playername), mana.getmax(playername))
	else
		mana.hud_add(playername)
	end
end)

--[===[
	Mana regeneration
]===]

mana.regen_timer = 0

minetest.register_globalstep(function(dtime)
	mana.regen_timer = mana.regen_timer + dtime
	if mana.regen_timer >= mana.settings.regen_timer then
		local factor = math.floor(mana.regen_timer / mana.settings.regen_timer)
		local players = minetest.get_connected_players()
		for i=1, #players do
			local name = players[i]:get_player_name()
			if mana.playerlist[name] ~= nil then
				if players[i]:get_hp() > 0 then
					local plus = mana.playerlist[name].regen * factor
					-- Compability check for version <= 1.0.2 which did not have the remainder field
					if mana.playerlist[name].remainder ~= nil then
						plus = plus + mana.playerlist[name].remainder
					end
					local plus_now = math.floor(plus)
					local floor = plus - plus_now
					if plus_now > 0 then
						mana.add_up_to(name, plus_now)
					else
						mana.subtract_up_to(name, math.abs(plus_now))
					end
					mana.playerlist[name].remainder = floor
				end
			end
		end
		mana.regen_timer = mana.regen_timer % mana.settings.regen_timer
	end
end)

--[===[
	HUD functions
]===]

if minetest.get_modpath("hudbars") ~= nil then
	hb.register_hudbar("mana", 0xFFFFFF, S("Mana"), { bar = "mana_bar.png", icon = "mana_icon.png", bgicon = "mana_bgicon.png" }, 0, mana.settings.default_max, false)

	function mana.hud_update(playername)
		local player = minetest.get_player_by_name(playername)
		if player ~= nil then
			hb.change_hudbar(player, "mana", mana.get(playername), mana.getmax(playername))
		end
	end

	function mana.hud_remove(playername)
	end

else
	function mana.manastring(playername)
		return S("Mana: @1/@2", mana.get(playername), mana.getmax(playername))
	end

	function mana.hud_add(playername)
		local player = minetest.get_player_by_name(playername)
		local id = player:hud_add({
			hud_elem_type = "text",
			position = { x = 0.5, y=1 },
			text = mana.manastring(playername),
			scale = { x = 0, y = 0 },
			alignment = { x = 1, y = 0},
			direction = 1,
			number = 0xFFFFFF,
			offset = { x = -262, y = -103}
		})
		mana.playerlist[playername].hudid = id
		return id
	end

	function mana.hud_update(playername)
		local player = minetest.get_player_by_name(playername)
		player:hud_change(mana.playerlist[playername].hudid, "text", mana.manastring(playername))
	end

	function mana.hud_remove(playername)
		local player = minetest.get_player_by_name(playername)
		player:hud_remove(mana.playerlist[playername].hudid)
	end
end

--[===[
	Helper functions
]===]
mana.round = function(x)
	return math.ceil(math.floor(x+0.5))
end


code:
# textdomain:mana
Mana=Mana
Mana: @1/@2=Mana: @1/@2


code:
# textdomain:mana
Mana=
Mana: @1/@2=


code:
name = mana
description = Adds a mana attribute to players, can be used as energy source for magical items, etc.
optional_depends = hudbars


code:
name = PLAYER
description = Meta-modpack that contains player-related mods for MineClone 2


code:
# Parties docs

Because it's always good to understand the API without surfing the code, init? :D

## 1 API

### 1.1 Utils

* `parties.is_player_invited(p_name)`: (bool) checks whether a player has a pending invite
* `parties.is_player_in_party(p_name)`: (bool) checks whether a player is in any party
* `parties.is_player_party_leader(p_name)`: (bool) checks whether a player is the party leader of any party
* `parties.chat_send_party(p_name, msg, as_broadcast)`: (nil) sends a message to every player inside the party where `p_name` is (`p_name` doesn't necessarily have to be the party leader). If `as_broadcast` is true, it'll be sent without following Minetest chat format. If false, `p_name` will be pointed as the sender when formatting the message
* `parties.change_party_leader(old_leader, new_leader)`: (nil) changes the party leader
* `parties.cancel_invite(p_name, inviter)`: (nil) cancels a pending invite from `inviter`
* `parties.cancel_invites(p_name)`: (nil) cancels all the pending invites of `p_name`

### 1.2 Getters

* `parties.get_inviters(p_name)`: (table) returns a table containing the name(s) of the player(s) who invited `p_name`, if an invite is pending
* `parties.get_party_leader(p_name)`: (string) returns the party leader of the party where `p_name` is in
* `parties.get_party_members(party_leader)`: (table) returns a list of every player inside the party where `party_leader` is in

## 2 Customisation

### 2.1 Callbacks

* `parties.register_on_pre_party_invite(function(sender, p_name))`: use it to run additional checks. Returning true keeps executing the invite, returning false/nil cancels it
* `parties.register_on_party_invite(function(sender, p_name))`: called when an invite has been successfully sent
* `parties.register_on_pre_party_join(function(party_leader, p_name))`: use it to run additional checks.Returning true keeps executing the join function, returning false/nil cancels it
* `parties.register_on_party_join(function(party_leader, p_name))`: called when a player successfully joined a party
* `parties.register_on_party_leave(function(party_leader, p_name))`: called when a player leaves a party
* `parties.register_on_party_disband(function(p_name))`: called when a party gets disbanded (by `p_name`, who is the party leader)

### 2.2 Chat

Chat is light blue by default and it adds the `[Party] ` prefix at the beginning of every message. It then follows the format of the chat set by the owner. By default is:
`[Party] <playername> message`


code:
# Parties

Minimalist party mod for Minetest

## Commands
* `/party invite <player>`: invite a player to a party
* `/party join <player>`: accept an invitation request, if any. If `player` is specified, accepts that specific player request
* `/party leave`: leave a party. If the party leader leaves, the role is assigned to another player (but if there were 2 people)
* `/party disband`: disband a party

### Chat
* `/p <message>` writes a message in the party chat

## Developers
[In-depth API documentation](https://gitlab.com/zughy-friends-minetest/parties/-/blob/master/DOCS.md)

## Want to help?
Feel free to:
* open an [issue](https://gitlab.com/zughy-friends-minetest/arena_lib/-/issues)
* submit a merge request. In this case, PLEASE, do follow milestones and my [coding guidelines](https://cryptpad.fr/pad/#/2/pad/view/-l75iHl3x54py20u2Y5OSAX4iruQBdeQXcO7PGTtGew/embed/). I won't merge features for milestones that are different from the upcoming one (if it's declared), nor messy code
* contact me on the [Minetest Forum](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=26472)

### Credits
Sounds by [suntemple](https://freesound.org/people/suntemple/) and [Heshl](https://freesound.org/people/Heshl/sounds/269153/)


code:
parties = {}

local function add_party_prefix() end
local function format_party_message() end

local S = minetest.get_translator("parties")
local current_parties = {}                  -- KEY party_leader; VALUE: {members (party_leader included)}
local players_in_parties = {}               -- KEY p_name; VALUE: party_leader
local players_invited = {}                  -- KEY p_name; VALUE: {inviter(s)}

function parties.invite(sender, p_name)

  -- se si è in un gruppo ma non si è il capo gruppo
  if players_in_parties[sender] and not current_parties[sender] then
    minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] Only the party leader can perform this action!")))
    return end

  -- se si invita se stessi
  if sender == p_name then
    minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] You can't invite yourself!")))
    return end

  -- se il giocatore non è online
  if not minetest.get_player_by_name(p_name) then
    minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] This player is not online!")))
    return end

  -- se è già in un gruppo
  if players_in_parties[p_name] then
    minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] This player is already in a party!")))
    return end

  -- se è già stato invitato dalla stessa persona
  if players_invited[p_name] then
    for _, inviter in pairs(players_invited[p_name]) do
      if inviter == sender then
        minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] You've already invited this player!")))
        return end
    end
  end

  -- se non può essere invitato (callback)
  for _, callback in ipairs(parties.registered_on_pre_party_invite) do
    if not callback(sender, p_name) then return end
  end

  minetest.sound_play("parties_invite", { to_player = p_name })
  minetest.chat_send_player(sender, add_party_prefix(S("Invite to @1 successfully sent", p_name)))
  minetest.chat_send_player(p_name, add_party_prefix(S("@1 has invited you to a party, would you like to join? (/party join, or /party join @2)", sender, sender)))

  if players_invited[p_name] then
    table.insert(players_invited[p_name], sender)
  else
    players_invited[p_name] = {sender}
  end

  -- eventuali callback
  for _, callback in ipairs(parties.registered_on_party_invite) do
    callback(sender, p_name)
  end

  -- se non ha accettato dopo 15 secondi, annullo l'invito
  minetest.after(15, function()

    if not players_invited[p_name] then return end

    for _, inviter in pairs(players_invited[p_name]) do
      if inviter == sender then

        if #players_invited[p_name] == 1 then
          players_invited[p_name] = nil
        end

        if minetest.get_player_by_name(sender) then
          minetest.chat_send_player(sender, add_party_prefix(minetest.colorize("#ededed", S("No answer from @1...", p_name))))
        end

      end
    end
  end)
end

function parties.join(p_name, inviter)

  -- se non ha nesssun invito
  if not players_invited[p_name] then
    minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] You have no pending invites!")))
    return end

  -- se chi lo ha invitato è specificato ma non esiste come giocatore
  if inviter then
    local is_inviter_legit = false
    for _, p_leader in pairs(players_invited[p_name]) do
       if p_leader == inviter then
         is_inviter_legit = true
         break
       end
    end

    if not is_inviter_legit then
     minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] You have no pending invites from this player!")))
     return end
  end

  local party_leader

  -- ottenimento capo gruppo
  if not inviter then
    party_leader = players_invited[p_name][table.maxn(players_invited[p_name])]
  else
    party_leader = inviter
  end

  -- se il capo gruppo si è disconnesso nei 15 secondi
  if not minetest.get_player_by_name(party_leader) then
    minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] The party leader has disconnected!")))

    if #players_invited[p_name] == 1 then
      players_invited[p_name] = nil
    else
      table.remove(players_invited[p_name], party_leader)
    end

    return
  end

  -- se non può accettare (callback)
  for _, callback in ipairs(parties.registered_on_pre_party_join) do
    if not callback(party_leader, p_name) then return end
  end

  -- se non esisteva un gruppo, lo creo
  if players_in_parties[party_leader] == nil then
    current_parties[party_leader] = {party_leader}
    players_in_parties[party_leader] = party_leader
  end

  -- riproduzione suono
  for _, pl_name in pairs(current_parties[party_leader]) do
    minetest.sound_play("parties_join", { to_player = pl_name })
  end

  parties.chat_send_party(party_leader, S("@1 has joined the party", p_name), true)

  for _, old_inviter in pairs(players_invited[p_name]) do
    if old_inviter ~= party_leader and minetest.get_player_by_name(old_inviter) then
      minetest.chat_send_player(old_inviter, add_party_prefix(minetest.colorize("#ededed", S("@1 has joined another party", p_name))))
    end
  end

  players_invited[p_name] = nil
  players_in_parties[p_name] = party_leader

  table.insert(current_parties[party_leader], p_name)
  minetest.chat_send_player(p_name, add_party_prefix(S("You've joined @1's party", party_leader)))

  -- eventuali callback
  for _, callback in ipairs(parties.registered_on_party_join) do
    callback(party_leader, p_name)
  end

end

function parties.leave(p_name)

  -- se non si è in un gruppo
  if not players_in_parties[p_name] then
    minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] You must enter a party first!")))
    return end

  local party_leader = players_in_parties[p_name]

  -- riproduzione suono
  for _, pl_name in pairs(current_parties[party_leader]) do
    minetest.sound_play("parties_leave", { to_player = pl_name })
  end

  -- rimuovo dal gruppo
  for k, pl_name in pairs(current_parties[party_leader]) do
    if pl_name == p_name then
      table.remove(current_parties[party_leader], k)
      break
    end
  end

  players_in_parties[p_name] = nil
  minetest.chat_send_player(p_name, add_party_prefix(S("You've left the party")), true)

  -- eventuali callback
  for _, callback in ipairs(parties.registered_on_party_leave) do
    callback(party_leader, p_name)
  end

  -- se ad abbandonare è stato il capo gruppo, lo cambio
  if p_name == party_leader then

    local new_leader = current_parties[party_leader][1]

    parties.chat_send_party(new_leader, S("@1 has left the party", p_name), true)
    parties.change_party_leader(p_name, new_leader)

    -- ...sciolgo se sono rimasti in 2
    if #current_parties[new_leader] == 1 then
      parties.disband(new_leader)
    end

  else
    parties.chat_send_party(party_leader, S("@1 has left the party", p_name), true)

    if #current_parties[party_leader] == 1 then
      parties.disband(party_leader)
    end
  end

end

function parties.disband(p_name)

  -- se non si è in un gruppo
  if not players_in_parties[p_name] then
    minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] You must enter a party first!")))
    return end

  -- se si è in un gruppo ma non si è il capo gruppo
  if players_in_parties[p_name] and not current_parties[p_name] then
    minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] Only the party leader can perform this action!")))
    return end

  -- riproduzione suono
  for _, pl_name in pairs(current_parties[p_name]) do
    minetest.sound_play("parties_leave", { p_name = pl_name })
  end

  parties.chat_send_party(p_name, S("The party has been disbanded"), true)

  -- eventuali callback
  for _, callback in ipairs(parties.registered_on_party_disband) do
    callback(p_name)
  end

  for _, pl_name in pairs(current_parties[p_name]) do
    players_in_parties[pl_name] = nil
  end

  current_parties[p_name] = nil
end

----------------------------------------------
--------------------UTILS---------------------
----------------------------------------------

function parties.is_player_invited(p_name)
  if players_invited[p_name] then return true
  else return false end
end

function parties.is_player_in_party(p_name)
  if players_in_parties[p_name] then return true
  else return false end
end

function parties.is_player_party_leader(p_name)
  if current_parties[p_name] then return true
  else return false end
end

function parties.chat_send_party(p_name, msg, as_broadcast)

  if not players_in_parties[p_name] then
    minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] You must enter a party first!")))
    return end

  local party_leader = players_in_parties[p_name]

  if as_broadcast then
    for _, pl_name in pairs(current_parties[party_leader]) do
      minetest.chat_send_player(pl_name, add_party_prefix(msg))
    end
  else
    for _, pl_name in pairs(current_parties[party_leader]) do
      minetest.chat_send_player(pl_name,  add_party_prefix(format_party_message(p_name, msg)))
    end
  end

end

function parties.change_party_leader(old_leader, new_leader)

  current_parties[new_leader] = {}

  for k, v in pairs(current_parties[old_leader]) do
    current_parties[new_leader][k] = v
    players_in_parties[v] = new_leader
  end

  current_parties[old_leader] = nil

  if #current_parties[new_leader] > 1 then
    parties.chat_send_party(new_leader, S("@1 is the new party leader", new_leader), true)
  end

end

function parties.cancel_invite(p_name, inviter)

  for _, p_leader in pairs(players_invited[p_name]) do
    if p_leader == inviter then
      table.remove(players_invited[p_name], inviter)
      return
    end
  end

  minetest.log("warning", "[PARTIES] There was an attempt to cancel an invite from " .. inviter .. " to " .. p_name .. " but there is none")
end

function parties.cancel_invites(p_name)
 players_invited[p_name] = nil
end

----------------------------------------------
-----------------GETTERS----------------------
----------------------------------------------

function parties.get_inviters(p_name)
  return players_invited[p_name]
end

function parties.get_party_leader(p_name)
  return players_in_parties[p_name]
end

function parties.get_party_members(party_leader)
  return current_parties[party_leader]
end

----------------------------------------------
---------------FUNZIONI LOCALI----------------
----------------------------------------------

function add_party_prefix(msg)
  return minetest.colorize("#ddffdd", "[" .. S("Party") .. "] " .. msg)
end

function format_party_message(p_name, msg)
  local msg_format = minetest.settings:get("chat_message_format")

  return minetest.colorize("#ddffdd", msg_format:gsub("@name", p_name):gsub("@message", msg))
end


code:
-- I had no idea how to do it, so, uhm... this is how Minetest handles callbacks
local function make_registration()
  local t = {}
  local registerfunc = function(func)
    t[#t+1] = func
  end
  return t, registerfunc
end

parties.registered_on_pre_party_invite, parties.register_on_pre_party_invite = make_registration()
parties.registered_on_party_invite, parties.register_on_party_invite = make_registration()
parties.registered_on_pre_party_join, parties.register_on_pre_party_join = make_registration()
parties.registered_on_party_join, parties.register_on_party_join = make_registration()
parties.registered_on_party_leave, parties.register_on_party_leave = make_registration()
parties.registered_on_party_disband, parties.register_on_party_disband = make_registration()


code:
ChatCmdBuilder = {}

function ChatCmdBuilder.new(name, func, def)
	def = def or {}
	local cmd = ChatCmdBuilder.build(func)
	cmd.def = def
	def.func = cmd.run
	minetest.register_chatcommand(name, def)
	return cmd
end

local STATE_READY = 1
local STATE_PARAM = 2
local STATE_PARAM_TYPE = 3
local bad_chars = {}
bad_chars["("] = true
bad_chars[")"] = true
bad_chars["."] = true
bad_chars["%"] = true
bad_chars["+"] = true
bad_chars["-"] = true
bad_chars["*"] = true
bad_chars["?"] = true
bad_chars["["] = true
bad_chars["^"] = true
bad_chars["$"] = true
local function escape(char)
	if bad_chars[char] then
		return "%" .. char
	else
		return char
	end
end

local dprint = function() end

ChatCmdBuilder.types = {
	pos    = "%(? *(%-?[%d.]+) *, *(%-?[%d.]+) *, *(%-?[%d.]+) *%)?",
	text   = "(.+)",
	number = "(%-?[%d.]+)",
	int    = "(%-?[%d]+)",
	word   = "([^ ]+)",
	alpha  = "([A-Za-z]+)",
	modname      = "([a-z0-9_]+)",
	alphascore   = "([A-Za-z_]+)",
	alphanumeric = "([A-Za-z0-9]+)",
	username     = "([A-Za-z0-9-_]+)",
}

function ChatCmdBuilder.build(func)
	local cmd = {
		_subs = {}
	}
	function cmd:sub(route, func, def)
		dprint("Parsing " .. route)

		def = def or {}
		if string.trim then
			route = string.trim(route)
		end

		local sub = {
			pattern = "^",
			params = {},
			func = func
		}

		-- End of param reached: add it to the pattern
		local param = ""
		local param_type = ""
		local should_be_eos = false
		local function finishParam()
			if param ~= "" and param_type ~= "" then
				dprint("   - Found param " .. param .. " type " .. param_type)

				local pattern = ChatCmdBuilder.types[param_type]
				if not pattern then
					error("Unrecognised param_type=" .. param_type)
				end

				sub.pattern = sub.pattern .. pattern

				table.insert(sub.params, param_type)

				param = ""
				param_type = ""
			end
		end

		-- Iterate through the route to find params
		local state = STATE_READY
		local catching_space = false
		local match_space = " " -- change to "%s" to also catch tabs and newlines
		local catch_space = match_space.."+"
		for i = 1, #route do
			local c = route:sub(i, i)
			if should_be_eos then
				error("Should be end of string. Nothing is allowed after a param of type text.")
			end

			if state == STATE_READY then
				if c == ":" then
					dprint(" - Found :, entering param")
					state = STATE_PARAM
					param_type = "word"
					catching_space = false
				elseif c:match(match_space) then
					print(" - Found space")
					if not catching_space then
						catching_space = true
						sub.pattern = sub.pattern .. catch_space
					end
				else
					catching_space = false
					sub.pattern = sub.pattern .. escape(c)
				end
			elseif state == STATE_PARAM then
				if c == ":" then
					dprint(" - Found :, entering param type")
					state = STATE_PARAM_TYPE
					param_type = ""
				elseif c:match(match_space) then
					print(" - Found whitespace, leaving param")
					state = STATE_READY
					finishParam()
					catching_space = true
					sub.pattern = sub.pattern .. catch_space
				elseif c:match("%W") then
					dprint(" - Found nonalphanum, leaving param")
					state = STATE_READY
					finishParam()
					sub.pattern = sub.pattern .. escape(c)
				else
					param = param .. c
				end
			elseif state == STATE_PARAM_TYPE then
				if c:match(match_space) then
					print(" - Found space, leaving param type")
					state = STATE_READY
					finishParam()
					catching_space = true
					sub.pattern = sub.pattern .. catch_space
				elseif c:match("%W") then
					dprint(" - Found nonalphanum, leaving param type")
					state = STATE_READY
					finishParam()
					sub.pattern = sub.pattern .. escape(c)
				else
					param_type = param_type .. c
				end
			end
		end
		dprint(" - End of route")
		finishParam()
		sub.pattern = sub.pattern .. "$"
		dprint("Pattern: " .. sub.pattern)

		table.insert(self._subs, sub)
	end

	if func then
		func(cmd)
	end

	cmd.run = function(name, param)
		for i = 1, #cmd._subs do
			local sub = cmd._subs[i]
			local res = { string.match(param, sub.pattern) }
			if #res > 0 then
				local pointer = 1
				local params = { name }
				for j = 1, #sub.params do
					local param = sub.params[j]
					if param == "pos" then
						local pos = {
							x = tonumber(res[pointer]),
							y = tonumber(res[pointer + 1]),
							z = tonumber(res[pointer + 2])
						}
						table.insert(params, pos)
						pointer = pointer + 3
					elseif param == "number" or param == "int" then
						table.insert(params, tonumber(res[pointer]))
						pointer = pointer + 1
					else
						table.insert(params, res[pointer])
						pointer = pointer + 1
					end
				end
				if table.unpack then
					-- lua 5.2 or later
					return sub.func(table.unpack(params))
				else
					-- lua 5.1 or earlier
					return sub.func(unpack(params))
				end
			end
		end
		return false, "Invalid command"
	end

	return cmd
end

local function run_tests()
	if not (ChatCmdBuilder.build(function(cmd)
		cmd:sub("bar :one and :two:word", function(name, one, two)
			if name == "singleplayer" and one == "abc" and two == "def" then
				return true
			end
		end)
	end)).run("singleplayer", "bar abc and def") then
		error("Test 1 failed")
	end

	local move = ChatCmdBuilder.build(function(cmd)
		cmd:sub("move :target to :pos:pos", function(name, target, pos)
			if name == "singleplayer" and target == "player1" and
					pos.x == 0 and pos.y == 1 and pos.z == 2 then
				return true
			end
		end)
	end).run
	if not move("singleplayer", "move player1 to 0,1,2") then
		error("Test 2 failed")
	end
	if not move("singleplayer", "move player1 to (0,1,2)") then
		error("Test 3 failed")
	end
	if not move("singleplayer", "move player1 to 0, 1,2") then
		error("Test 4 failed")
	end
	if not move("singleplayer", "move player1 to 0 ,1, 2") then
		error("Test 5 failed")
	end
	if not move("singleplayer", "move player1 to 0, 1, 2") then
		error("Test 6 failed")
	end
	if not move("singleplayer", "move player1 to 0 ,1 ,2") then
		error("Test 7 failed")
	end
	if not move("singleplayer", "move player1 to ( 0 ,1 ,2)") then
		error("Test 8 failed")
	end
	if move("singleplayer", "move player1 to abc,def,sdosd") then
		error("Test 9 failed")
	end
	if move("singleplayer", "move player1 to abc def sdosd") then
		error("Test 10 failed")
	end

	if not (ChatCmdBuilder.build(function(cmd)
		cmd:sub("does :one:int plus :two:int equal :three:int", function(name, one, two, three)
			if name == "singleplayer" and one + two == three then
				return true
			end
		end)
	end)).run("singleplayer", "does 1 plus 2 equal 3") then
		error("Test 11 failed")
	end

	local checknegint = ChatCmdBuilder.build(function(cmd)
		cmd:sub("checknegint :x:int", function(name, x)
			return x
		end)
	end).run
	if checknegint("checker","checknegint -2") ~= -2 then
		error("Test 12 failed")
	end

	local checknegnumber = ChatCmdBuilder.build(function(cmd)
		cmd:sub("checknegnumber :x:number", function(name, x)
			return x
		end)
	end).run
	if checknegnumber("checker","checknegnumber -3.3") ~= -3.3 then
		error("Test 13 failed")
	end

	local checknegpos = ChatCmdBuilder.build(function(cmd)
		cmd:sub("checknegpos :pos:pos", function(name, pos)
			return pos
		end)
	end).run
	local negpos = checknegpos("checker","checknegpos (-13.3,-4.6,-1234.5)")
	if negpos.x ~= -13.3 or negpos.y ~= -4.6 or negpos.z ~= -1234.5 then
		error("Test 14 failed")
	end

	local checktypes = ChatCmdBuilder.build(function(cmd)
		cmd:sub("checktypes :int:int :number:number  :pos:pos :word:word  :text:text", function(name, int, number, pos, word, text)
			return int, number, pos.x, pos.y, pos.z, word, text
		end)
	end).run
	local int, number, posx, posy, posz, word, text
	int, number, posx, posy, posz, word, text = checktypes("checker","checktypes -1 -2.4 (-3,-5.3,6.12)  some text  to finish off with")
	--dprint(int, number, posx, posy, posz, word, text)
	if int ~= -1 or number ~= -2.4 or posx ~= -3 or posy ~= -5.3 or posz ~= 6.12 or word ~= "some" or text ~= "text  to finish off with" then
		error("Test 15 failed")
	end
	dprint("All tests passed")

end
if not minetest then
	run_tests()
end


code:
ChatCmdBuilder.new("party", function(cmd)

    -- invito
    cmd:sub("invite :player", function(sender, p_name)
        parties.invite(sender, p_name)
        end)

    -- accettazione
    cmd:sub("join", function(sender)
        parties.join(sender)
        end)

    -- accettazione con nick
    cmd:sub("join :inviter", function(sender, inviter)
        parties.join(sender, inviter)
        end)

    -- abbandono
    cmd:sub("leave", function(sender)
        parties.leave(sender)
        end)

    -- scioglimento
    cmd:sub("disband", function(sender)
        parties.disband(sender)
        end)

end,{})

ChatCmdBuilder.new("p", function(cmd)

    -- chat party
    cmd:sub(":message:text", function(sender, message)
        parties.chat_send_party(sender, message)
        end)

end,{})


code:
local version = "1.3.0-dev"

dofile(minetest.get_modpath("parties") .. "/api.lua")
dofile(minetest.get_modpath("parties") .. "/callbacks.lua")
dofile(minetest.get_modpath("parties") .. "/chatcmdbuilder.lua")
dofile(minetest.get_modpath("parties") .. "/commands.lua")
dofile(minetest.get_modpath("parties") .. "/player_manager.lua")


code:
# version 1.3.0-dev
# author(s): Dragonop
# reviewer(s):
# textdomain: parties

# api.lua
[!] Only the party leader can perform this action!=[!] ¡Solo el líder del grupo puede realizar esta acción!
[!] You can't invite yourself!=[!] ¡No puedes invitarte a ti mismo!
[!] This player is not online!=[!] ¡Este jugador no está conectado!
[!] You've already invited this player!=[!] ¡Ya has invitado a este jugador!
[!] This player is already in a party!=[!] ¡Este jugador ya está en un grupo!
Invite to @1 successfully sent=Invitación a @1 enviada con éxito
@1 has invited you to a party, would you like to join? (/party join, or /party join @2)=@1 te ha invitado a un grupo, te gustaría unirte? (/party join, o /party join @2)
No answer from @1...=Sin respuesta de @1...
[!] You have no pending invites!=[!] ¡No tienes invitaciones pendientes!
[!] You have no pending invites from this player!=[!] ¡No tienes invitaciones pendientes de este jugador!
[!] The party leader has disconnected!=[!] ¡El líder del grupo se ha desconectado!
@1 has joined the party=@1 se ha unido al grupo
@1 has joined another party=@1 se ha unido a otro grupo
You've joined @1's party=Te has unido al grupo de @1
[!] You must enter a party first!=[!] ¡Debes unirte a un grupo primero!
The party has been disbanded=El grupo ha sido disuelto
@1 has left the group=@1 ha dejado el grupo
You've left the party=Has dejado el grupo
@1 is the new party leader=@1 es el nuevo líder del grupo
Party=Grupo


code:
# version 1.3.0-dev
# author(s):
# reviewer(s):
# textdomain: parties

# party_manager.lua
[!] Only the party leader can perform this action!=[!] Solo il capo gruppo può eseguire quest'azione!
[!] You can't invite yourself!=[!] Non ci si può invitare da soli!
[!] This player is not online!=[!] Questo giocatore non è online!
[!] You've already invited this player!=[!] Hai già invitato questo giocatore!
[!] This player is already in a party!=[!] Questo giocatore è già in un gruppo!
Invite to @1 successfully sent=Invito a @1 inviato con successo
@1 has invited you to a party, would you like to join? (/party join, or /party join @2)=@1 ti ha invitato in un gruppo, vuoi unirti? (/party join, o /party join @2)
No answer from @1...=Nessuna risposta da @1...
[!] You have no pending invites!=[!] Non hai nessun invito da accettare!
[!] You have no pending invites from this player!=[!] Non hai nessun invito da accettare da parte di questo giocatore!
[!] The party leader has disconnected!=[!] Il capo gruppo si è disconnesso!
@1 has joined the party=@1 si è unito al gruppo
@1 has joined another party=@1 si è unito a un altro gruppo
You've joined @1's party=Ti sei unito al gruppo di @1
[!] You must enter a party first!=[!] Devi prima entrare in un gruppo!
The party has been disbanded=Il gruppo è stato sciolto
@1 has left the party=@1 è uscito dal gruppo
You've left the party=Sei uscito dal gruppo
@1 is the new party leader=@1 è il nuovo capo gruppo
Party=Gruppo


code:
# version 1.3.0-dev
# author(s):
# reviewer(s):
# textdomain: parties

# api.lua
[!] Only the party leader can perform this action!=
[!] You can't invite yourself!=
[!] This player is not online!=
[!] You've already invited this player!=
[!] This player is already in a party!=
Invite to @1 successfully sent=
@1 has invited you to a party, would you like to join? (/party join, or /party join @2)=
No answer from @1...=
[!] You have no pending invites!=
[!] You have no pending invites from this player!=
[!] The party leader has disconnected!=
@1 has joined the party=
@1 has joined another party=
You've joined @1's party=
[!] You must enter a party first!=
The party has been disbanded=
@1 has left the group=
You've left the party=
@1 is the new party leader=
Party=


code:
minetest.register_on_leaveplayer(function(player)

  local p_name = player:get_player_name()

  if parties.is_player_in_party(p_name) then
    parties.leave(p_name)
  end

  if parties.is_player_invited(p_name) then
    parties.cancel_invites(p_name)
  end
end)


code:
read_globals = {
	"minetest",
	"vector",
}

globals = {
	"player_monoids",
}


code:
#Player Monoids

The idea behind this library is that global player state (physics overrides,
armor values, etc.) changes from multiple mods should mesh nicely with each
other. This means they must be combinable in a sane way.

Monoids
=======
A player monoid covers a single kind of player state a mod might want to change.
These can be built-in player state, like speed multipliers or fly permissions,
or could be custom state introduced by mods, such as corruption or reputation
level. When you make a player monoid, you must choose some type of value to
represent state changes - for example, numbers for speed multipliers, or vectors
for "lucky direction". Each mod can contribute different changes, represented
by this type of value, and they are all combined together. This combined value
is interpreted and converted into actual effects on the player's state.
Privileges could be set, physics overrides would be used to effect speed
changes, and a mod might change some value to match the monoid.

Definition
----------
A player monoid definition is a table with the following:

  * ```combine(elem1, elem2)``` - An associative binary operation
  * ```fold({elems})``` - Equivalent to combining a whole list with ```combine```
  * ```identity``` - An identity element for ```combine```
  * ```apply(value, player)``` - Apply the effect represented by ```value```
  to ```player```
  * ```on_change(val1, val2, player)``` - Do something when the value on a
  player changes. (optional)

Additionally, you should document what values are valid representatives of
your monoid's effects. When something says that a value is "in a monoid", it
means that value is a valid representative of your monoid's effects.

combine and fold
----------------
```combine``` should take two values in your monoid and produce a third value in
your monoid. It should also be an associative operation. ```fold`` should take a
table containing elements of your monoid as input and combine them together in
key order. It should be equivalent to using ```combine``` to combine all the
values together. For example, ```combine``` could multiply two speed multipliers
together, and ```fold``` could multiply every value together.

identity
--------
```identity```, when combined with any other value, should result in the other
value. It also represents the "default" or "neutral" state of the player, and
will be used when there are no status effects active for a particular monoid.
For example, the identity of a speed monoid could be the multiplier ```1```.

apply
-----
```apply``` is the function that interprets a value in your monoid to do
something to the player's state. For example, you could set a speed multiplier
as the speed physics override for the player.

Functions
=========
```player_monoids.make_monoid(monoid_def)``` - Creates a new monoid that can be
used to make changes to the player state. Returns a monoid.

Monoid Methods
--------------
```monoid:add_change(player, value[, "id"])``` - Applies the change represented
by ```value``` to ```player```. Returns an ID for the change. If the optional
string argument ```"id"``` is supplied, that is used as the ID instead, and any
existing change with that ID is removed. IDs are only guaranteed to be unique
per-player. Conversely, you are allowed to make multiple changes with the same
ID as long as they are all on different players.

```monoid:del_change(player, id)``` - Removes the change with the given ID, from
the given player, if it exists.

```monoid:value(player)``` - The current combined value of the monoid for the
given player.