code:
diet = {
	players = {}
}

function diet.__init()
	local file = io.open(minetest.get_worldpath().."/diet.txt", "r")
	if file then
		local table = minetest.deserialize(file:read("*all"))
		if type(table) == "table" then
			diet.players = table.players
			return
		end
	end
end

function diet.save()
	local file = io.open(minetest.get_worldpath().."/diet.txt", "w")
	if file then
		file:write(minetest.serialize({
			players = diet.players
		}))
		file:close()
	end
end

-- Poison player
local function poisenp(tick, time, time_left, player)
	time_left = time_left + tick
	if time_left < time then
		minetest.after(tick, poisenp, tick, time, time_left, player)
	else
		--reset hud image
	end
	if player:get_hp()-1 > 0 then
		player:set_hp(player:get_hp()-1)
	end

end

function diet.item_eat(max, replace_with_item, poisen, heal)
	return function(itemstack, user, pointed_thing)

		-- Process player data
		local name = user:get_player_name()
		local player = diet.__player(name)
		local item = itemstack:get_name()

		-- Get type
		local ftype = ""
		if (minetest.registered_items[item] and minetest.registered_items[item].groups) then
			local groups = minetest.registered_items[item].groups
			if groups.food_type_meal then
				ftype = "meal"
			elseif groups.food_type_snack then
				ftype = "snack"
			elseif groups.food_type_dessert then
				ftype = "dessert"
			elseif groups.food_type_drink then
				ftype = "drink"
			end
		end

		-- Calculate points
		local points = max
		if (#player.eaten>0) then
			local same_food = 0
			local same_type = 0
			for _,v in pairs(player.eaten) do
				if v[1] == item then
					same_food = same_food + 1
				end
				if v[2] == ftype then
					same_type = same_type + 1
				end
			end
			local mult = same_food/10
			points = points * 1-mult

			if (mult > 0.9) then
				local desc = item
				if (minetest.registered_items[item] and minetest.registered_items[item].description) then
					desc = minetest.registered_items[item].description
				end
				minetest.chat_send_player(name,"Your stomach hates "..desc)
			elseif (mult > 0.4) then
				minetest.chat_send_player(name,"Your stomach could do with a change.")
			end
			if points > max then
				error("[DIET] This shouldn't happen! points > max")
				return
			end
		end

		-- Increase health
		if minetest.get_modpath("hbhunger") and hbhunger then
			minetest.sound_play({name = "hbhunger_eat_generic", gain = 1}, {pos=user:getpos(), max_hear_distance = 16})

			-- saturation
			local h = tonumber(hbhunger.hunger[name])
			h = h + points
			if h > 30 then h = 30 end
			hbhunger.hunger[name] = h
			hbhunger.set_hunger_raw(user)

			-- healing
			local hp = user:get_hp()
			if hp < 20 and heal then
				hp = hp + heal
				if hp > 20 then hp = 20 end
				user:set_hp(hp)
			end

			 -- Poison
			if poisen then
				--set hud-img
				poisenp(1.0, poisen, 0, user)
			end

		elseif minetest.get_modpath("hunger") and hunger then
			local h = tonumber(hunger.players[name].lvl)
			h = h + points
			hunger.update_hunger(user, h)

		elseif minetest.get_modpath("hud") and hud and hud.hunger then
			local h = tonumber(hud.hunger[name])
			h = h + points
			if h > 30 then h = 30 end
			hud.hunger[name] = h
			hud.save_hunger(user)

		else
			local hp = user:get_hp()
			if (hp+points > 20) then
				hp = 20
			else
				hp = hp + points
			end
			user:set_hp(hp)
		end

		-- Register
		diet.__register_eat(player,item,ftype)

		diet.save()

		-- Remove item
		itemstack:add_item(replace_with_item)
		itemstack:take_item()
		return itemstack
	end
end

function diet.__player(name)
	if name == "" then
		return nil
	end
	if diet.players[name] then
		return diet.players[name]
	end

	diet.players[name] = {
		name = name,
		eaten = {}
	}
	diet.save()
	return diet.players[name]
end

function diet.__register_eat(player,food,type)
	table.insert(player.eaten,{food,type})

	while (#player.eaten > 10) do
		table.remove(player.eaten,1)
	end
end

local function overwrite(name, hunger_change, replace_with_item, poisen, heal)
	local tab = minetest.registered_items[name]
	if not tab then
		return
	end
	tab.on_use = diet.item_eat(hunger_change, replace_with_item, poisen, heal)
end

diet.__init()

overwrite("default:apple", 2)
if minetest.get_modpath("farming") ~= nil then
	overwrite("farming:bread", 4)
end

if minetest.get_modpath("mobs") ~= nil then
	if mobs.mod ~= nil and mobs.mod == "redo" then
		overwrite("mobs:cheese", 4)
		overwrite("mobs:meat", 8)
		overwrite("mobs:meat_raw", 4)
		overwrite("mobs:rat_cooked", 4)
		overwrite("mobs:honey", 2)
		overwrite("mobs:pork_raw", 3, "", 3)
		overwrite("mobs:pork_cooked", 8)
		overwrite("mobs:chicken_cooked", 6)
		overwrite("mobs:chicken_raw", 2, "", 3)
		overwrite("mobs:chicken_egg_fried", 2)
		if minetest.get_modpath("mesecraft_bucket") then
			overwrite("mobs:bucket_milk", 3, "mesecraft_bucket:bucket_empty")
		end
	else
		overwrite("mobs:meat", 6)
		overwrite("mobs:meat_raw", 3)
		overwrite("mobs:rat_cooked", 5)
	end
end

if minetest.get_modpath("moretrees") ~= nil then
	overwrite("moretrees:coconut_milk", 1)
	overwrite("moretrees:raw_coconut", 2)
	overwrite("moretrees:acorn_muffin", 3)
	overwrite("moretrees:spruce_nuts", 1)
	overwrite("moretrees:pine_nuts", 1)
	overwrite("moretrees:fir_nuts", 1)
end

if minetest.get_modpath("dwarves") ~= nil then
	overwrite("dwarves:beer", 2)
	overwrite("dwarves:apple_cider", 1)
	overwrite("dwarves:midus", 2)
	overwrite("dwarves:tequila", 2)
	overwrite("dwarves:tequila_with_lime", 2)
	overwrite("dwarves:sake", 2)
end

if minetest.get_modpath("animalmaterials") ~= nil then
	overwrite("animalmaterials:milk", 2)
	overwrite("animalmaterials:meat_raw", 3)
	overwrite("animalmaterials:meat_pork", 3)
	overwrite("animalmaterials:meat_beef", 3)
	overwrite("animalmaterials:meat_chicken", 3)
	overwrite("animalmaterials:meat_lamb", 3)
	overwrite("animalmaterials:meat_venison", 3)
	overwrite("animalmaterials:meat_undead", 3, "", 3)
	overwrite("animalmaterials:meat_toxic", 3, "", 5)
	overwrite("animalmaterials:meat_ostrich", 3)
	overwrite("animalmaterials:fish_bluewhite", 2)
	overwrite("animalmaterials:fish_clownfish", 2)
end

if minetest.get_modpath("fishing") ~= nil then
	overwrite("fishing:fish_raw", 2)
	overwrite("fishing:fish_cooked", 5)
	overwrite("fishing:sushi", 6)
	overwrite("fishing:shark", 4)
	overwrite("fishing:shark_cooked", 8)
	overwrite("fishing:pike", 4)
	overwrite("fishing:pike_cooked", 8)
end

if minetest.get_modpath("glooptest") ~= nil then
	overwrite("glooptest:kalite_lump", 1)
end

if minetest.get_modpath("bushes") ~= nil then
	overwrite("bushes:sugar", 1)
	overwrite("bushes:strawberry", 2)
	overwrite("bushes:berry_pie_raw", 3)
	overwrite("bushes:berry_pie_cooked", 4)
	overwrite("bushes:basket_pies", 15)
end

if minetest.get_modpath("bushes_classic") then
	-- bushes_classic mod, as found in the plantlife modpack
	local berries = {
		"strawberry",
		"blackberry",
		"blueberry",
		"raspberry",
		"gooseberry",
		"mixed_berry"}
	for _, berry in ipairs(berries) do
		if berry ~= "mixed_berry" then
			overwrite("bushes:"..berry, 1)
		end
		overwrite("bushes:"..berry.."_pie_raw", 2)
		overwrite("bushes:"..berry.."_pie_cooked", 5)
		overwrite("bushes:"..berry.."_pie_slice", 1)
		overwrite("bushes:basket_"..berry, 15)
	end
end

if minetest.get_modpath("mushroom") ~= nil then
	overwrite("mushroom:brown", 1)
	overwrite("mushroom:red", 1, "", 3)
	-- mushroom potions: red = strong poison, brown = light restorative
	if minetest.get_modpath("vessels") then
		overwrite("mushroom:brown_essence", 1, "vessels:glass_bottle", nil, 4)
		overwrite("mushroom:poison", 1, "vessels:glass_bottle", 10)
	end
end

if minetest.get_modpath("docfarming") ~= nil then
	overwrite("docfarming:carrot", 3)
	overwrite("docfarming:cucumber", 2)
	overwrite("docfarming:corn", 3)
	overwrite("docfarming:potato", 4)
	overwrite("docfarming:bakedpotato", 5)
	overwrite("docfarming:raspberry", 3)
end

if minetest.get_modpath("farming_plus") ~= nil then
	overwrite("farming_plus:carrot_item", 3)
	overwrite("farming_plus:banana", 2)
	overwrite("farming_plus:orange_item", 2)
	overwrite("farming:pumpkin_bread", 4)
	overwrite("farming_plus:strawberry_item", 2)
	overwrite("farming_plus:tomato_item", 2)
	overwrite("farming_plus:potato_item", 4)
	overwrite("farming_plus:rhubarb_item", 2)
end

if minetest.get_modpath("mtfoods") ~= nil then
	overwrite("mtfoods:dandelion_milk", 1)
	overwrite("mtfoods:sugar", 1)
	overwrite("mtfoods:short_bread", 4)
	overwrite("mtfoods:cream", 1)
	overwrite("mtfoods:chocolate", 2)
	overwrite("mtfoods:cupcake", 2)
	overwrite("mtfoods:strawberry_shortcake", 2)
	overwrite("mtfoods:cake", 3)
	overwrite("mtfoods:chocolate_cake", 3)
	overwrite("mtfoods:carrot_cake", 3)
	overwrite("mtfoods:pie_crust", 3)
	overwrite("mtfoods:apple_pie", 3)
	overwrite("mtfoods:rhubarb_pie", 2)
	overwrite("mtfoods:banana_pie", 3)
	overwrite("mtfoods:pumpkin_pie", 3)
	overwrite("mtfoods:cookies", 2)
	overwrite("mtfoods:mlt_burger", 5)
	overwrite("mtfoods:potato_slices", 2)
	overwrite("mtfoods:potato_chips", 3)
	--mtfoods:medicine
	overwrite("mtfoods:casserole", 3)
	overwrite("mtfoods:glass_flute", 2)
	overwrite("mtfoods:orange_juice", 2)
	overwrite("mtfoods:apple_juice", 2)
	overwrite("mtfoods:apple_cider", 2)
	overwrite("mtfoods:cider_rack", 2)
end

if minetest.get_modpath("fruit") ~= nil then
	overwrite("fruit:apple", 2)
	overwrite("fruit:pear", 2)
	overwrite("fruit:bananna", 3)
	overwrite("fruit:orange", 2)
end

if minetest.get_modpath("mush45") ~= nil then
	overwrite("mush45:meal", 4)
end

if minetest.get_modpath("seaplants") ~= nil then
	overwrite("seaplants:kelpgreen", 1)
	overwrite("seaplants:kelpbrown", 1)
	overwrite("seaplants:seagrassgreen", 1)
	overwrite("seaplants:seagrassred", 1)
	overwrite("seaplants:seasaladmix", 6)
	overwrite("seaplants:kelpgreensalad", 1)
	overwrite("seaplants:kelpbrownsalad", 1)
	overwrite("seaplants:seagrassgreensalad", 1)
	overwrite("seaplants:seagrassgreensalad", 1)
end

if minetest.get_modpath("mobfcooking") ~= nil then
	overwrite("mobfcooking:cooked_pork", 6)
	overwrite("mobfcooking:cooked_ostrich", 6)
	overwrite("mobfcooking:cooked_beef", 6)
	overwrite("mobfcooking:cooked_chicken", 6)
	overwrite("mobfcooking:cooked_lamb", 6)
	overwrite("mobfcooking:cooked_venison", 6)
	overwrite("mobfcooking:cooked_fish", 6)
end

if minetest.get_modpath("creatures") ~= nil then
	overwrite("creatures:meat", 6)
	overwrite("creatures:flesh", 3)
	overwrite("creatures:rotten_flesh", 3, "", 3)
end

if minetest.get_modpath("ethereal") then
	overwrite("ethereal:strawberry", 1)
	overwrite("ethereal:banana", 4)
	overwrite("ethereal:pine_nuts", 1)
	overwrite("ethereal:bamboo_sprout", 0, "", 3)
	overwrite("ethereal:fern_tubers", 1)
	overwrite("ethereal:banana_bread", 7)
	overwrite("ethereal:mushroom_plant", 2)
	overwrite("ethereal:coconut_slice", 2)
	overwrite("ethereal:golden_apple", 4, "", nil, 10)
	overwrite("ethereal:wild_onion_plant", 2)
	overwrite("ethereal:mushroom_soup", 4, "ethereal:bowl")
	overwrite("ethereal:mushroom_soup_cooked", 6, "ethereal:bowl")
	overwrite("ethereal:hearty_stew", 6, "ethereal:bowl", 3)
	overwrite("ethereal:hearty_stew_cooked", 10, "ethereal:bowl")
	if minetest.get_modpath("mesecraft_bucket") then
		overwrite("ethereal:bucket_cactus", 2, "mesecraft_bucket:bucket_empty")
	end
	overwrite("ethereal:fish_raw", 2)
	overwrite("ethereal:fish_cooked", 5)
	overwrite("ethereal:seaweed", 1)
	overwrite("ethereal:yellowleaves", 1, "", nil, 1)
	overwrite("ethereal:sashimi", 4)
end

if minetest.get_modpath("farming") and farming.mod == "redo" then
   overwrite("farming:bread", 6)
   overwrite("farming:potato", 1)
   overwrite("farming:baked_potato", 6)
   overwrite("farming:cucumber", 4)
   overwrite("farming:tomato", 4)
   overwrite("farming:carrot", 3)
   overwrite("farming:carrot_gold", 6, "", nil, 8)
   overwrite("farming:corn", 3)
   overwrite("farming:corn_cob", 5)
   overwrite("farming:melon_slice", 2)
   overwrite("farming:pumpkin_slice", 1)
   overwrite("farming:pumpkin_bread", 9)
   overwrite("farming:coffee_cup", 2, "farming:drinking_cup")
   overwrite("farming:coffee_cup_hot", 3, "farming:drinking_cup", nil, 2)
   overwrite("farming:cookie", 2)
   overwrite("farming:chocolate_dark", 3)
   overwrite("farming:donut", 4)
   overwrite("farming:donut_chocolate", 6)
   overwrite("farming:donut_apple", 6)
   overwrite("farming:raspberries", 1)
   overwrite("farming:blueberries", 1)
   overwrite("farming:muffin_blueberry", 4)
   if minetest.get_modpath("vessels") then
      overwrite("farming:smoothie_raspberry", 2, "vessels:drinking_glass")
      overwrite("farming:pineapple_juice", 4, "vessels:drinking_glass")
   end
   overwrite("farming:rhubarb", 1)
   overwrite("farming:rhubarb_pie", 6)
   overwrite("farming:garlic", 1)
   overwrite("farming:chili_bowl", 8, "farming:bowl")
   overwrite("farming:grapes", 2)
   overwrite("farming:peas", 1)
   overwrite("farming:pea_soup", 4, "farming:bowl")
   overwrite("farming:pepper", 2)
   overwrite("farming:chili_pepper", 2)
   overwrite("farming:pineapple_ring", 1)
   overwrite("farming:onion", 1)
   overwrite("farming:bread_slice", 1)
   overwrite("farming:toast", 1)
   overwrite("farming:toast_sandwich", 4)
   overwrite("farming:porridge", 6)
end

if minetest.get_modpath("kpgmobs") ~= nil then
	overwrite("kpgmobs:uley", 3)
	overwrite("kpgmobs:meat", 6)
	overwrite("kpgmobs:rat_cooked", 5)
	overwrite("kpgmobs:med_cooked", 4)
	if minetest.get_modpath("mesecraft_bucket") then
	   overwrite("kpgmobs:bucket_milk", 4, "mesecraft_bucket:bucket_empty")
	end
end

if minetest.get_modpath("jkfarming") ~= nil then
	overwrite("jkfarming:carrot", 3)
	overwrite("jkfarming:corn", 3)
	overwrite("jkfarming:melon_part", 2)
	overwrite("jkfarming:cake", 3)
end

if minetest.get_modpath("jkanimals") ~= nil then
	overwrite("jkanimals:meat", 6)
end

if minetest.get_modpath("jkwine") ~= nil then
	overwrite("jkwine:grapes", 2)
	overwrite("jkwine:winebottle", 1)
end

if minetest.get_modpath("cooking") ~= nil then
	overwrite("cooking:meat_beef_cooked", 4)
	overwrite("cooking:fish_bluewhite_cooked", 3)
	overwrite("cooking:fish_clownfish_cooked", 1)
	overwrite("cooking:meat_chicken_cooked", 2)
	overwrite("cooking:meat_cooked", 2)
	overwrite("cooking:meat_pork_cooked", 3)
	overwrite("cooking:meat_toxic_cooked", -3)
	overwrite("cooking:meat_venison_cooked", 3)
	overwrite("cooking:meat_undead_cooked", 1)
end

-- ferns mod of plantlife_modpack
if minetest.get_modpath("ferns") ~= nil then
	overwrite("ferns:fiddlehead", 1, "", 1)
	overwrite("ferns:fiddlehead_roasted", 3)
	overwrite("ferns:ferntuber_roasted", 3)
	overwrite("ferns:horsetail_01", 1)
end

-- crops
if minetest.get_modpath("crops") ~= nil then
	overwrite("crops:potato", 1)
	overwrite("crops:roasted_pumpkin", 2)
	overwrite("crops:melon_slice", 1)
	overwrite("crops:green_bean", 1)
	overwrite("crops:tomato", 1)
	overwrite("crops:corn_on_the_cob", 1)
	overwrite("crops:vegetable_stew", 8, "crops:clay_bowl")
	overwrite("crops:uncooked_vegetable_stew", 2, "crops:clay_bowl")
end

-- nsspf
if minetest.get_modpath("nsspf") ~= nil then
	overwrite("nsspf:boletus_edulis", 2)
	overwrite("nsspf:cooked_boletus_edulis", 16)
	overwrite("nsspf:cantharellus_cibarius", 1)
	overwrite("nsspf:cooked_cantharellus_cibarius", 8)
	overwrite("nsspf:suillus_grevillei", 1)
	overwrite("nsspf:cooked_suillus_grevillei", 10)
	overwrite("nsspf:morchella_conica", 2)
	overwrite("nsspf:cooked_morchella_conica", 8)
	overwrite("nsspf:russula_xerampelina", -8)
	overwrite("nsspf:cooked_russula_xerampelina", 6)
	overwrite("nsspf:boletus_pinophilus", 2)
	overwrite("nsspf:cooked_boletus_pinophilus", 16)
	overwrite("nsspf:boletus_satanas", -20)
	overwrite("nsspf:cooked_boletus_satanas", -16)
	overwrite("nsspf:amanita_phalloides", -20)
	overwrite("nsspf:cooked_amanita_phalloides", -20)
	overwrite("nsspf:amanita_muscaria", -20)
	overwrite("nsspf:cooked_amanita_muscaria", -18)
	overwrite("nsspf:fistulina_hepatica", 4)
	overwrite("nsspf:cooked_fistulina_hepatica", 14)
	overwrite("nsspf:armillaria_mellea", 2)
	overwrite("nsspf:fomes_fomentarius", -1)
	overwrite("nsspf:cooked_armillaria_mellea", 12)
	overwrite("nsspf:mycena_chlorophos", -2)
	overwrite("nsspf:cooked_mycena_chlorophos", -4)
	overwrite("nsspf:mycena_chlorophos_light", -2)
	overwrite("nsspf:panellus_pusillus", -2)
	overwrite("nsspf:cooked_panellus_pusillus", -4)
	overwrite("nsspf:panellus_pusillus_light", -2)
	overwrite("nsspf:macrolepiota_procera", 3)
	overwrite("nsspf:cooked_macrolepiota_procera", 16)
	overwrite("nsspf:psilocybe_cubensis", -7)
	overwrite("nsspf:cooked_psilocybe_cubensis", 2)
	overwrite("nsspf:lycoperdon_pyriforme", 8)
	overwrite("nsspf:cooked_lycoperdon_pyriforme", 12)
	overwrite("nsspf:gyromitra_esculenta", -20)
	overwrite("nsspf:cooked_gyromitra_esculenta", -16)
	overwrite("nsspf:coprinus_atramentarius", -13)
	overwrite("nsspf:cooked_coprinus_atramentarius", 6)
	overwrite("nsspf:lentinus_strigosus", 1)
	overwrite("nsspf:cooked_lentinus_strigosus", 16)
	overwrite("nsspf:ganoderma_lucidum", 14)
	overwrite("nsspf:cooked_ganoderma_lucidum", 4)
	overwrite("nsspf:marasmius_haematocephalus", -1)
	overwrite("nsspf:cooked_marasmius_haematocephalus", -19)
	overwrite("nsspf:clitocybula_azurea", -6)
	overwrite("nsspf:cooked_clitocybula_azurea", 10)
	overwrite("nsspf:clitocybe_glacialis", 1)
	overwrite("nsspf:cooked_clitocybe_glacialis", 10)
	overwrite("nsspf:hygrophorus_goetzii", -4)
	overwrite("nsspf:cooked_hygrophorus_goetzii", 8)
	overwrite("nsspf:plectania_nannfeldtii", -20)
	overwrite("nsspf:cooked_plectania_nannfeldtii", -20)
end


code:
name = diet
title = Diet
author = rubenwardy
description = Adds a mechanic to encourage food variety.
license = CC0
forum = https://forum.minetest.net/viewtopic.phpt=9051
version = 0.1
depends =
optional_depends = hbhunger, hud, hunger, default, animalmaterials, bucket, bushes, bushes_classic, cooking, creatures, crops, docfarming, dwarves, ethereal, farming, farming_plus, ferns, fishing, fruit, glooptest, jkanimals, jkfarming, jkwine, kpgmobs, mobfcooking, mobs, moretrees, mtfoods, mush45, mushroom, nsspf, seaplants

repo_name: korkut/MeseCraft

license: mit


code:
![](./screenshot.jpg)

This mod makes soil and soil-like nodes capable of holding temporary "footprint" impressions, and if they're walked on repeatedly turning into hard-packed trails.

It also allows players to flatten plants by walking on them.

It includes definitions for footprint-capable nodes in the "default" mod, and has an API for allowing nodes in other mods to use the same system.

## API

New trampled nodes can be registered with this API:

```
footprints.register_trample_node(trampleable_node_name, trample_def)

trample_def:
{
	trampled_node_name = , -- If this is not defined it defaults to the
	                       -- trampleable node name with "_trampled" appended.
	trampled_node_def_override = {}, -- If trampled_node_name doesn't exist a new
	                       -- node will be registered based on the definition of
	                       -- trampleable_node_name. Any properties in this table
	                       -- will be used to override properties ignored if
	                       -- trampled_node_name is a node that already exists.
	probability = 1, -- chance that stepping on this node will cause it to turn
                           -- into the trampled version (range is 0.0 to 1.0)
	trample_count = 1, -- The number of times this node needs to be stepped on
                           -- (and pass the probability check) to transition to
                           -- the trampled state
	randomize_trampled_param2 = nil, -- if true, sets param2 of trampled node to math.random(0,3)
                           -- This is used for trampled wheat, for example, to randomize the thatch's
						   -- direction.
	erodes = true,         -- sets the trampled node to erode back into the non-trampled
                           -- version over time, if erosion is enabled in this mod's settings.
                           -- Ignored if trampled_node_name is a node that already
                           -- exists, since that may already have an erosion target established.
	add_footprint_overlay = true, -- Applies the footprint texture over the +Y tile of the
                           -- trampleable node. ignored if trampled_node_name is a node that already exists
	footprint_overlay_texture = "footprints_footprint.png",
	footprint_opacity = 64, -- defaults to 64 (0 is transparent, 255 is fully opaque)
	hard_pack_node_name = nil, -- If the trampled node is walked on again this is the
                           -- node that it can get further packed down into. ignored if
                           -- trampled_node_name is a node that already exists, since
                           -- it's expected this has already been established
	hard_pack_probability = 0.9, -- The probability that walking on a trampled node
                           -- will turn it into the hard-packed node (0.0 to 1.0).
                           -- ignored if trampled_node_name is a node that already exists
	hard_pack_count = 10, -- The number of times the trampled node needs to be stepped on
                           -- (and pass the probability check) to turn to the hard packed state
}
```

Note that all of the parameters in trample_def have default values, so if you want you can just pass in nil and the footprints mod will create a footstep-marked version of the node and set it all up for you with no further information needed. "footprints.register_trample_node("modname:dirt")" will work.

### Eroding hardpack back to soil over time

If you've defined a hard_pack_node and want to have it able to erode back to base soil, you can use this callback to manually add it to the erosion system:

```
footprints.register_erosion(starting_node_name, restored_node_name)
```
Note that the source_node should be in group footprints_erodes or an error will be thrown.

### Using a hoe to convert hardpack back to soil

If you've got the `farming` mod installed you can allow hardpack nodes to be restored back to soil using a hoe with the following function:

```
footprints.register_hoe_converts(starting_node_name, restored_node_name)
```

## License

Licenses: Source code MIT. Textures CC BY-SA (3.0)

- This mod was developed by paramat from 'desire path' mod by Casimir: https://forum.minetest.net/viewtopic.php?id=3390
- Trail 0.3.1 by paramat: https://forum.minetest.net/viewtopic.php?f=11&t=6773
- Version 0.4 for Minetest 0.5 was developed by FaceDeer and renamed "footprints"


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+

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": []
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
    "help": ['--help', '-h'],
    "mods": ['--installed-mods'],
    "verbose": ['--verbose', '-v']
}

# 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["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.")
        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:
-- global callbacks
footprints = {}

local default_modpath = minetest.get_modpath("default")

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

-- Parameters

local GLOBALSTEP_INTERVAL = 0.2 -- Function cycle in seconds.

local HARDPACK_PROBABILITY = minetest.settings:get("footprints_hardpack_probability") or 0.9 -- Chance walked dirt/grass is worn and compacted to footprints:trail.
local HARDPACK_COUNT = minetest.settings:get("footprints_hardpack_count") or 10 -- Number of times the above chance needs to be passed for soil to compact.
local EROSION = minetest.settings:get_bool("footprints_erosion", true) -- Enable footprint erosion.
local FOOTPRINTS_EROSION = minetest.settings:get_bool("footprints_trail_erosion", false) -- Allow hard-packed soil to erode back to dirt
local EROSION_INTERVAL = minetest.settings:get("footprints_erosion_interval") or 128 -- Erosion interval.
local EROSION_CHANCE = minetest.settings:get("footprints_erosion_chance") or 2 -- Erosion 1/x chance.

-- Utility

local function deep_copy(input)
	if type(input) ~= "table" then
		return input
	end
	local output = {}
	for index, value in pairs(input) do
		output[index] = deep_copy(value)
	end
	return output
end

-- Player positions

local player_pos_previous_map = {}

minetest.register_on_joinplayer(function(player)
	player_pos_previous_map[player:get_player_name()] = {x = 0, y = 0, z = 0}
end)

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

local trampleable_nodes = {}
local erosion = {}

footprints.register_trample_node = function(trampleable_node_name, trample_def)
	trample_def = trample_def or {} -- Everything has defaults, so if no trample_def is passed in just use an empty table.

	if trampleable_nodes[trampleable_node_name] then
		minetest.log("error", "[footprints] Attempted to call footprints.register_trample_node to register trampleable node "
			.. trampleable_node_name ..", which has already been registered as trampleable.")
		return
	end
	local trampleable_node_def = minetest.registered_nodes[trampleable_node_name]
	if trampleable_node_def == nil then
		minetest.log("error", "[footprints] Attempted to call footprints.register_trample_node with the trampleable node "
			.. trampleable_node_name ..", which has not yet been registered as a node.")
		return
	end

	local trampled_node_name = trample_def.trampled_node_name or trampleable_node_name.."_trampled"
	if not minetest.registered_nodes[trampled_node_name] then
		local trampled_node_def = deep_copy(trampleable_node_def) -- start with a deep copy of the source node's definition
		if trample_def.trampled_node_def_override then -- override any values that need to be overridden explicitly
			for key, value in pairs(trample_def.trampled_node_def_override) do
				trampled_node_def[key] = value
			end
		end

		-- Set up the erosion ABM group
		if EROSION and trample_def.erodes ~= false then
			local groups = trampled_node_def.groups or {}
			groups.footprints_erodes = 1
			trampled_node_def.groups = groups
			erosion[trampled_node_name] = trampleable_node_name
		end

		-- If the source node doesn't have a special drop, then set drop to drop a source node rather than dropping a node with a footstep.
		if trampled_node_def.drop == nil then
			trampled_node_def.drop = trampleable_node_name
		end

		-- Modify the +Y tile with a footprint overlay
		if trample_def.add_footprint_overlay ~= false then
			local footprint_overlay = trample_def.footprint_overlay or "footprints_footprint.png"
			local footprint_opacity = trample_def.footprint_opacity or 64
			local overlay_texture = "^(" .. footprint_overlay .. "^[opacity:" .. tostring(footprint_opacity) .. ")"

			local tiles = trampled_node_def.tiles
			local first_tile = tiles[1]
			local second_tile = tiles[2]
			if second_tile == nil then
				-- The provided node def only has one tile for all sides. We need to only modify the +Y tile,
				-- so we need to copy the original first tile into the second tile slot
				tiles[2] = deep_copy(first_tile)
			end
			if type(first_tile) == "table" then
				first_tile.name = first_tile.name .. overlay_texture
			elseif type(first_tile) == "string" then
				first_tile = first_tile .. overlay_texture
			end
			trampled_node_def.tiles[1] = first_tile
		end

		minetest.register_node(":"..trampled_node_name, trampled_node_def)

		-- If hard pack has been defined for this footprints type, add it
		local hard_pack_node_name = trample_def.hard_pack_node_name
		if hard_pack_node_name then
			local hard_pack_probability = trample_def.hard_pack_probability or 0.1
			local hard_pack_count = trample_def.hard_pack_count or 1
			trampleable_nodes[trampled_node_name] = {name=hard_pack_node_name, probability=hard_pack_probability, count = hard_pack_count}
		end
	end

	local probability = trample_def.probability or 1
	local trample_count = trample_def.trample_count or 1
	trampleable_nodes[trampleable_node_name] = {name=trampled_node_name, probability=probability, randomize_trampled_param2 = trample_def.randomize_trampled_param2, count = trample_count}
end

footprints.register_erosion = function(source_node_name, destination_node_name)
	if not EROSION then
		return
	end

	if minetest.registered_nodes[source_node_name] == nil then
		minetest.log("error", "[footprints] attempted to call footprints.register_erosion with unregistered source node "
			.. source_node_name)
		return
	end
	if minetest.registered_nodes[destination_node_name] == nil then
		minetest.log("error", "[footprints] attempted to call footprints.register_erosion with unregistered destination node "
			.. destination_node_name)
		return
	end
	if minetest.get_item_group(source_node_name, "footprints_erodes") == 0 then
		minetest.log("error", "[footprints] attempted to call footprints.register_erosion with source node "
			.. destination_node_name .. " that wasn't in group footprints_erodes.")
		return
	end

	erosion[source_node_name] = destination_node_name
end

-- Nodes

if default_modpath then
	-- hard-packed soil
	local footprints_trail_def = {
		tiles = {"footprints_trailtop.png", "default_dirt.png",
			"default_dirt.png^footprints_trailside.png"},
		groups = {crumbly = 2},
		drop = "default:dirt",
		sounds = default.node_sound_dirt_defaults(),
	}
	if FOOTPRINTS_EROSION then
		footprints_trail_def.groups.footprints_erodes = 1
	end
	minetest.register_node("footprints:trail", footprints_trail_def)
	if FOOTPRINTS_EROSION then
		footprints.register_erosion("footprints:trail", "default:dirt")
	end

	-- hard-packed dry soil
	local footprints_dry_trail_def = {
		tiles = {"footprints_trailtop.png", "default_dry_dirt.png",
			"default_dry_dirt.png^footprints_trailside.png"},
		groups = {crumbly = 2},
		drop = "default:dry_dirt",
		sounds = default.node_sound_dirt_defaults(),
	}
	if FOOTPRINTS_EROSION then
		footprints_dry_trail_def.groups.footprints_erodes = 1
	end
	minetest.register_node("footprints:dry_trail", footprints_dry_trail_def)
	if FOOTPRINTS_EROSION then
		footprints.register_erosion("footprints:dry_trail", "default:dry_dirt")
	end

	-- Default dirt

	footprints.register_trample_node("default:dirt", {
		trampled_node_name = "footprints:dirt",
		trampled_node_def_override = {description = S("Dirt with Footprint"),},
		hard_pack_node_name = "footprints:trail",
		footprint_opacity = 96,
		hard_pack_probability = HARDPACK_PROBABILITY,
		hard_pack_count = HARDPACK_COUNT,
	})

	footprints.register_trample_node("default:dirt_with_grass", {
		trampled_node_name = "footprints:dirt_with_grass",
		trampled_node_def_override = {description = S("Dirt with Grass and Footprint"),},
		hard_pack_node_name = "footprints:trail",
		hard_pack_probability = HARDPACK_PROBABILITY,
		hard_pack_count = HARDPACK_COUNT,
	})

	footprints.register_trample_node("default:dirt_with_dry_grass", {
		trampled_node_name = "footprints:dirt_with_dry_grass",
		trampled_node_def_override = {description = S("Dirt with Dry Grass and Footprint"),},
		hard_pack_node_name = "footprints:trail",
		hard_pack_probability = HARDPACK_PROBABILITY,
		hard_pack_count = HARDPACK_COUNT,
	})

	footprints.register_trample_node("default:dirt_with_snow", {
		trampled_node_name = "footprints:dirt_with_snow",
		trampled_node_def_override = {description = S("Dirt with Snow and Footprint"),},
		hard_pack_node_name = "footprints:trail",
		hard_pack_probability = HARDPACK_PROBABILITY,
		hard_pack_count = HARDPACK_COUNT,
	})

	footprints.register_trample_node("default:dirt_with_rainforest_litter", {
		trampled_node_name = "footprints:dirt_with_rainforest_litter",
		trampled_node_def_override = {description = S("Dirt with Rainforest Litter and Footprint"),},
		hard_pack_node_name = "footprints:trail",
		footprint_opacity = 96,
		hard_pack_probability = HARDPACK_PROBABILITY,
		hard_pack_count = HARDPACK_COUNT,
	})

	footprints.register_trample_node("default:dirt_with_coniferous_litter", {
		trampled_node_name = "footprints:dirt_with_coniferous_litter",
		trampled_node_def_override = {description = S("Dirt with Coniferous Litter and Footprint"),},
		hard_pack_node_name = "footprints:trail",
		footprint_opacity = 128,
		hard_pack_probability = HARDPACK_PROBABILITY,
		hard_pack_count = HARDPACK_COUNT,
	})

	footprints.register_trample_node("default:dry_dirt", {
		trampled_node_name = "footprints:dry_dirt",
		trampled_node_def_override = {description = S("Dry Dirt with Footprint"),},
		hard_pack_node_name = "footprints:dry_trail",
		footprint_opacity = 96,
		hard_pack_probability = HARDPACK_PROBABILITY,
		hard_pack_count = HARDPACK_COUNT,
	})

	footprints.register_trample_node("default:dry_dirt_with_dry_grass", {
		trampled_node_name = "footprints:dry_dirt_with_dry_grass",
		trampled_node_def_override = {description = S("Dry Dirt with Dry Grass and Footprint"),},
		hard_pack_node_name = "footprints:dry_trail",
		footprint_opacity = 96,
		hard_pack_probability = HARDPACK_PROBABILITY,
		hard_pack_count = HARDPACK_COUNT,
	})

	-- Default sand

	footprints.register_trample_node("default:sand", {
		trampled_node_name = "footprints:sand",
		trampled_node_def_override = {description = S("Sand with Footprint"),},
	})

	footprints.register_trample_node("default:desert_sand", {
		trampled_node_name = "footprints:desert_sand",
		trampled_node_def_override = {description = S("Desert Sand with Footprint"),},
	})

	footprints.register_trample_node("default:silver_sand", {
		trampled_node_name = "footprints:silver_sand",
		trampled_node_def_override = {description = S("Silver Sand with Footprint"),},
	})

	footprints.register_trample_node("default:gravel", {
		trampled_node_name = "footprints:gravel",
		trampled_node_def_override = {description = S("Gravel with Footprint"),},
		footprint_opacity = 128,
	})

	-- Default snow

	footprints.register_trample_node("default:snowblock", {
		trampled_node_name = "footprints:snowblock",
		trampled_node_def_override = {description = S("Snow Block with Footprint"),},
		hard_pack_node_name = "default:ice",
		hard_pack_probability = HARDPACK_PROBABILITY,
		hard_pack_count = HARDPACK_COUNT,
	})

	footprints.register_trample_node("default:snow", {
		trampled_node_name = "footprints:snow",
		trampled_node_def_override = {description = S("Snow with Footprint"),},
	})
end

if minetest.get_modpath("farming") then
	local hoe_converts_nodes = {}

	local sounds
	if default_modpath then
		sounds = default.node_sound_leaves_defaults()
	end
	-- Flattened wheat
	minetest.register_node("footprints:wheat", {
		description = S("Flattened Wheat"),
		tiles = {"footprints_flat_wheat.png"},
		inventory_image = "footprints_flat_wheat.png",
		drawtype = "nodebox",
		paramtype = "light",
		paramtype2 = "facedir",
		buildable_to = true,
		node_box = {
			type = "fixed",
			fixed = {
				{-0.5, -0.5, -0.5, 0.5, -3 / 8, 0.5}
			},
		},
		groups = {snappy = 3, flammable = 2, attached_node = 1},
		drop = "",
		sounds = sounds,
	})

	footprints.register_trample_node("farming:wheat_5", {
		trampled_node_name = "footprints:wheat",
		randomize_trampled_param2 = true,
	})
	footprints.register_trample_node("farming:wheat_6", {
		trampled_node_name = "footprints:wheat",
		randomize_trampled_param2 = true,
	})
	footprints.register_trample_node("farming:wheat_7", {
		trampled_node_name = "footprints:wheat",
		randomize_trampled_param2 = true,
	})
	footprints.register_trample_node("farming:wheat_8", {
		trampled_node_name = "footprints:wheat",
		randomize_trampled_param2 = true,
	})

	minetest.register_node("footprints:cotton", {
		description = S("Flattened Cotton"),
		tiles = {"footprints_flat_cotton.png"},
		inventory_image = "footprints_flat_cotton.png",
		drawtype = "nodebox",
		paramtype = "light",
		paramtype2 = "facedir",
		buildable_to = true,
		node_box = {
			type = "fixed",
			fixed = {
				{-0.5, -0.5, -0.5, 0.5, -3 / 8, 0.5}
			},
		},
		groups = {snappy = 3, flammable = 2, attached_node = 1},
		drop = "",
		sounds = sounds,
	})

	footprints.register_trample_node("farming:cotton_5", {
		trampled_node_name = "footprints:cotton",
		randomize_trampled_param2 = true,
	})
	footprints.register_trample_node("farming:cotton_6", {
		trampled_node_name = "footprints:cotton",
		randomize_trampled_param2 = true,
	})
	footprints.register_trample_node("farming:cotton_7", {
		trampled_node_name = "footprints:cotton",
		randomize_trampled_param2 = true,
	})
	footprints.register_trample_node("farming:cotton_8", {
		trampled_node_name = "footprints:cotton",
		randomize_trampled_param2 = true,
	})

	-- Allow hoes to turn hardpack back into bare dirt
	footprints.register_hoe_converts = function(target_node, converted_node)
		hoe_converts_nodes[target_node] = converted_node
	end

	local old_hoe_on_use = farming.hoe_on_use
	if not old_hoe_on_use then
		-- Something's wrong, don't override
		return
	end

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

		local under_node = minetest.get_node(pt.under)
		local restore_node = hoe_converts_nodes[under_node.name]

		-- check if pointing at hardpack
		if restore_node then
			if minetest.is_protected(pt.under, user:get_player_name()) then
				minetest.record_protection_violation(pt.under, user:get_player_name())
				return
			end

			-- turn the node into soil and play sound
			minetest.set_node(pt.under, {name = restore_node})
			minetest.sound_play("default_dig_crumbly", {
				pos = pt.under,
				gain = 0.5,
			})
			if not (creative and creative.is_enabled_for
					and creative.is_enabled_for(user:get_player_name())) then
				-- wear tool
				local wdef = itemstack:get_definition()
				itemstack:add_wear(65535/(uses-1))
				-- tool break sound
				if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
					minetest.sound_play(wdef.sound.breaks, {pos = pt.above, gain = 0.5})
				end
			end
			return itemstack
		end

		return old_hoe_on_use(itemstack, user, pointed_thing, uses)
	end

	farming.hoe_on_use = new_hoe_on_use
else
	footprints.register_hoe_converts = function(target_node, converted_node)
	end
end

if default_modpath then
	footprints.register_hoe_converts("footprints:trail", "default:dirt")
	footprints.register_hoe_converts("footprints:dry_trail", "default:dry_dirt")
end

-- Globalstep function

local timer = 0

local get_param2 = function(footprints_def)
	if footprints_def.randomize_trampled_param2 then
		return math.random(0,3)
	end
	return 0
end

local test_trample_count = function(footprints_def, pos)
	local target_count = footprints_def.count
	if target_count <= 1 then
		return true
	end
	local meta = minetest.get_meta(pos)
	local trampled_count = meta:get_int("footprints_trample_count")
	trampled_count = trampled_count + 1
	if trampled_count >= target_count then
		return true
	end
	meta:set_int("footprints_trample_count", trampled_count)
	return false
end

local math_floor = math.floor

minetest.register_globalstep(function(dtime)
	timer = timer + dtime
	if timer > GLOBALSTEP_INTERVAL then
		timer = 0
		for _, player in ipairs(minetest.get_connected_players()) do
			local pos = player:get_pos()
			local player_name = player:get_player_name()
			local pos_x_plus_half = math_floor(pos.x + 0.5)
			local pos_z_plus_half = math_floor(pos.z + 0.5)
			local pos_y = pos.y
			local current_player_pos = {
				x = pos_x_plus_half,
				y = math_floor(pos_y + 0.2),
				z = pos_z_plus_half
			}

			--if player_pos_previous_map[player_name] == nil then
				--break
			--end

			local player_pos_previous = player_pos_previous_map[player_name]

			if current_player_pos.x ~= player_pos_previous.x or
				current_player_pos.y < player_pos_previous.y or
				current_player_pos.z ~= player_pos_previous.z then

				local pos_ground_cover = {
					x = pos_x_plus_half,
					y = math_floor(pos_y + 1.2),
					z = pos_z_plus_half
				}
				local name_ground_cover = minetest.get_node(pos_ground_cover).name

				-- test ground cover first (snow, wheat)
				local footprints_def = trampleable_nodes[name_ground_cover]
				if footprints_def then
					if math.random() <= footprints_def.probability then
						local pos_ground_cover_plus = {
							x = pos_x_plus_half,
							y = math_floor(pos_y + 0.5),
							z = pos_z_plus_half
						}
						if test_trample_count(footprints_def, pos_ground_cover_plus) then
							minetest.set_node(pos_ground_cover_plus, {name = footprints_def.name, param2 = get_param2(footprints_def)})
						end
					end
				else
					local pos_ground = {
						x = pos_x_plus_half,
						y = math_floor(pos_y + 0.4),
						z = pos_z_plus_half
					}
					local name_ground = minetest.get_node(pos_ground).name
					footprints_def = trampleable_nodes[name_ground]
					if footprints_def and math.random() <= footprints_def.probability then
						local pos_groundpl = {
							x = pos_x_plus_half,
							y = math_floor(pos_y - 0.5),
							z =pos_z_plus_half
						}
						if test_trample_count(footprints_def, pos_groundpl) then
							minetest.set_node(pos_groundpl, {name = footprints_def.name, param2 = get_param2(footprints_def)})
						end
					end
				end
			end

			player_pos_previous_map[player_name] = current_player_pos
		end
	end
end)

-- ABM

if EROSION then
	minetest.register_abm({
		nodenames = {"group:footprints_erodes"},
		interval = EROSION_INTERVAL,
		chance = EROSION_CHANCE,
		catch_up = true,
		action = function(pos, node, _, _)
			local nodename = node.name
			local erodes_to = erosion[nodename]
			if erodes_to then
				local meta = minetest.get_meta(pos)
				local trampled_count = meta:get_int("footprints_trample_count") - 1
				if trampled_count <= 0 then
					minetest.set_node(pos, {name = erodes_to})
				else
					meta:set_int("footprints_trample_count", trampled_count)
				end
			else
				minetest.log("error", "[footprints] The node " .. nodename .. " is in group footprints_erodes but "
					.. " didn't have an erosion target node defined.")
			end
		end
	})
end

minetest.register_alias("trail:cotton",						"footprints:cotton")
minetest.register_alias("trail:desert_sand",				"footprints:desert_sand")
minetest.register_alias("trail:dirt",						"footprints:dirt")
minetest.register_alias("trail:dirt_with_coniferous_litter","footprints:dirt_with_coniferous_litter")
minetest.register_alias("trail:dirt_with_dry_grass",		"footprints:dirt_with_dry_grass")
minetest.register_alias("trail:dirt_with_grass",			"footprints:dirt_with_grass")
minetest.register_alias("trail:dirt_with_rainforest_litter","footprints:dirt_with_rainforest_litter")
minetest.register_alias("trail:dirt_with_snow",				"footprints:dirt_with_snow")
minetest.register_alias("trail:dry_dirt",					"footprints:dry_dirt")
minetest.register_alias("trail:dry_dirt_with_dry_grass",	"footprints:dry_dirt_with_dry_grass")
minetest.register_alias("trail:dry_trail",					"footprints:dry_trail")
minetest.register_alias("trail:gravel",						"footprints:gravel")
minetest.register_alias("trail:sand",						"footprints:sand")
minetest.register_alias("trail:silver_sand",				"footprints:silver_sand")
minetest.register_alias("trail:snow",						"footprints:snow")
minetest.register_alias("trail:snowblock",					"footprints:snowblock")
minetest.register_alias("trail:trail",						"footprints:trail")
minetest.register_alias("trail:wheat",						"footprints:wheat")


code:
# textdomain: footprints

### init.lua ###

Desert Sand with Footprint=
Dirt with Coniferous Litter and Footprint=
Dirt with Dry Grass and Footprint=
Dirt with Footprint=
Dirt with Grass and Footprint=
Dirt with Rainforest Litter and Footprint=
Dirt with Snow and Footprint=
Dry Dirt with Dry Grass and Footprint=
Dry Dirt with Footprint=
Flattened Cotton=
Flattened Wheat=
Gravel with Footprint=
Sand with Footprint=
Silver Sand with Footprint=
Snow Block with Footprint=
Snow with Footprint=


code:
name = footprints
description = Players walking over various types of terrain will leave footprints, and optionally pack the soil down into trails
optional_depends = default, farming


code:
footprints_erosion (Erode footsteps back to original soil type) bool true
#This setting only has an effect if footstep erosion is also set to true
footprints_trail_erosion (Erode hard-packed trails back to dirt) bool false
footprints_erosion_interval (Erosion ABM interval) int 128
footprints_erosion_chance (Erosion ABM 1/x chance) int 2
#This setting works in combination with footprints_hardpack_count. It gives the
#probability that a player stepping on a footprint will increment the hardpack
#counter. So by default, every time you step on a footprint there's a 1/2
#chance the counter increments for that node.
footprints_hardpack_probability (Probability footprints in dirt turn to hardpacked soil) float 0.9
#This setting works in combination with footprints_hardpack_probability. By default,
#once a footprint has been stepped on enough to increment its counter 5 times
#it'll turn the soil hard-packed.
footprints_hardpack_count (Number of times the hardpack check needs to pass) int 10


code:
# HUD bar for `3d_armor` [`hbarmor`]

* Version: 1.0.0

## Description
This mod adds a simple HUD bar which displays the current damage
of the player's armor (from the 3D Armor [`3d_armor`] mod) as a percentage (rounded).

100% armor means the armor is in perfect shape. 0% means the armor is almost destroyed
or non-existant. Note that to reach 100%, the player must wear at least 4 different
pieces of armor in perfect shape.

The armor bar also does not tell anything about the armor's strength,
only how worn out it already is.

By default, the armor bar is hidden if the player wears no armor.

## Dependencies
* HUD bars [`hudbars`], major version 2
* 3D Armor [`3d_armor`] (tested with Minetest 5.0.0)

## Licensing
This mod is entirly free softare.

### Source code

* License: MIT License (see below)
* Authors: Wuzzy, forked from the mod “Better HUD (and hunger)” [`hud`] by BlockMen (2013-2014)

### Textures

* `hbarmor_icon.png`—Stu ([CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)), modified by BlockMen
* `hbarmor_bgicon.png`—Stu (CC BY-SA 3.0), modified by BlockMen
* `hbarmor_bar.png`—Wuzzy (MIT License)

Everything else is under the MIT License:
© Copyright BlockMen (2013-2014)

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.
See <https://opensource.org/licenses/MIT> for more details.


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

if (not armor) or (not armor.def) then
	minetest.log("error", "[hbarmor] Outdated 3d_armor version. Please update your version of 3d_armor!")
end

local hbarmor = {}

-- HUD statbar values
hbarmor.armor = {}

-- Stores if player's HUD bar has been initialized so far.
hbarmor.player_active = {}

-- Time difference in seconds between updates to the HUD armor bar.
-- Increase this number for slow servers.
hbarmor.tick = 1.0

-- If true, the armor bar is hidden when the player does not wear any armor
hbarmor.autohide = false

--load custom settings
local set = minetest.settings:get_bool("hbarmor_autohide")
if set ~= nil then
	hbarmor.autohide = set
end

set = minetest.settings:get("hbarmor_tick")
if tonumber(set) ~= nil then
	hbarmor.tick = tonumber(set)
end

local must_hide = function(playername, arm)
	return ((not armor.def[playername].count or armor.def[playername].count == 0) and arm == 0)
end

local arm_printable = function(arm)
	return math.ceil(math.floor(arm+0.5))
end

local function custom_hud(player)
	local name = player:get_player_name()

	if minetest.settings:get_bool("enable_damage") then
		local ret = hbarmor.get_armor(player)
		if ret == false then
			minetest.log("error", "[hbarmor] Call to hbarmor.get_armor in custom_hud returned with false!")
		end
		local arm = tonumber(hbarmor.armor[name])
		if not arm then arm = 0 end
		local hide
		if hbarmor.autohide then
			hide = must_hide(name, arm)
		else
			hide = false
		end
		hb.init_hudbar(player, "armor", arm_printable(arm), nil, hide)
	end
end

--register and define armor HUD bar
hb.register_hudbar("armor", 0xFFFFFF, S("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" } )

function hbarmor.get_armor(player)
	if not player or not armor.def then
		return false
	end
	local name = player:get_player_name()
	local def = armor.def[name] or nil
	if def and def.state and def.count then
		hbarmor.set_armor(name, def.state, def.count)
	else
		return false
	end
	return true
end

function hbarmor.set_armor(player_name, ges_state, items)
	local max_items = 4
	if items == 5 then
		max_items = items
	end
	local max = max_items * 65535
	local lvl = max - ges_state
	lvl = lvl/max
	if ges_state == 0 and items == 0 then
		lvl = 0
	end

	hbarmor.armor[player_name] = math.max(0, math.min(lvl* (items * (100 / max_items)), 100))
end

-- update hud elemtens if value has changed
local function update_hud(player)
	local name = player:get_player_name()
	--armor
	local arm = tonumber(hbarmor.armor[name])
	if not arm then
		arm = 0
		hbarmor.armor[name] = 0
	end
	if hbarmor.autohide then
		-- hide armor bar completely when there is none
		if must_hide(name, arm) then
			hb.hide_hudbar(player, "armor")
		else
			hb.change_hudbar(player, "armor", arm_printable(arm))
			hb.unhide_hudbar(player, "armor")
		end
	else
		hb.change_hudbar(player, "armor", arm_printable(arm))
	end
end

minetest.register_on_joinplayer(function(player)
	local name = player:get_player_name()
	custom_hud(player)
	hbarmor.player_active[name] = true
end)

minetest.register_on_leaveplayer(function(player)
	local name = player:get_player_name()
	hbarmor.player_active[name] = false
end)

local main_timer = 0
local timer = 0
minetest.register_globalstep(function(dtime)
	main_timer = main_timer + dtime
	timer = timer + dtime
	if main_timer > hbarmor.tick or timer > 4 then
		if minetest.settings:get_bool("enable_damage") then
			if main_timer > hbarmor.tick then main_timer = 0 end
			for _,player in ipairs(minetest.get_connected_players()) do
				local name = player:get_player_name()
				if hbarmor.player_active[name] == true then
					local ret = hbarmor.get_armor(player)
					if ret == false then
						minetest.log("error", "[hbarmor] Call to hbarmor.get_armor in globalstep returned with false!")
					end
					-- update all hud elements
					update_hud(player)
				end
			end
		end
	end
	if timer > 4 then timer = 0 end
end)


code:
# textdomain:hbarmor
Armor=Panzerung
@1: @2%=@1: @2%


code:
# textdomain:hbarmor
Armor=Armatura
@1: @2%=@1:@2%


code:
# textdomain:hbarmor
Armor=

# Format string for displaying the armor. E.g. "Armor: 100%"
@1: @2%=


code:
name = hbarmor
description = Adds a HUD bar displaying the current damage of the player's armor.
depends = hudbars, 3d_armor


code:
#If true, automatically hides the armor HUD bar when the player wears no
#armor. Otherwise, the armor bar shows “0%”.
hbarmor_autohide (Automatically hide armor HUD bar) bool true

#Time difference in seconds between updates to the armor HUD bar.
#Increase this number for slow servers.
hbarmor_tick (Armor HUD bar update frequency) float 0.1 0.0 4.0


code:
# Hunger with HUD bar [`hbhunger`]

* Version: 1.0.1

## Using the mod

This mod adds a mechanic for hunger.
This mod depends on the HUD bars mod [`hudbars`], version 1.4.1 or any later version
starting with “1.” or “2.”.

## About hunger
This mod adds a hunger mechanic to the game. Players get a new attribute called “satiation”:

* A new player starts with 20 satiation points out of 30
* Actions like digging, placing and walking cause exhaustion, which lower the satiation
* Every 800 seconds you lose 1 satiation point without doing anything
* At 1 or 0 satiation you will suffer damage and die in case you don't eat something
* If your satiation is 16 or higher, you will slowly regenerate health points
* Eating food will increase your satiation (Duh!)

Important: Eating food will not directly increase your health anymore, as long as the food
item is supported by this mod (see below).

Careful! Some foods may be poisoned. If you eat a poisoned item, you may still get a satiation
boost, but for a brief period you lose health points because of food poisoning. However,
food poisoning can never kill you.

## Statbar mode
If you use the statbar mode of the HUD Bars mod, these things are important to know:
As with all mods using HUD Bars, the bread statbar symbols represent the rough percentage
out of 30 satiation points, in steps of 5%, so the symbols give you an estimate of your
satiation. This is different from the hunger mod by BlockMen.

You gain health at 5.5 symbols or more, as 5.5 symbols correspond to 16 satiation points.
You *may* lose health at exactly 0.5 symbols, as 0.5 symbols correspond to 1-2 satiation points.

## Supported food
All mods which add food through standard measures (`minetest.item_eat`) are already
supported automatically. Poisoned food needs special support.

### Known supported food mods
* Apple from Minetest Game [`default`]
* Red and brown mushroom from Minetest Game [`flowers`]
* Bread from Minetest Game [`farming`]
* [`animalmaterials`] (Mob Framework (`mobf` modpack))
* Bushes [`bushes`]
* [`bushes_classic`]
* Creatures [`creatures`]
* [`dwarves`] (beer and such)
* Docfarming [`docfarming`]
* Ethereal / Ethereal NG [`ethereal`]
* Farming Redo [`farming`] by TenPlus1
* Farming plus [`farming_plus`]
* Ferns [`ferns`]
* Fishing [`fishing`]
* [`fruit`]
* Glooptest [`glooptest`]
* JKMod ([`jkanimals`], [`jkfarming`], [`jkwine`])
* [`kpgmobs`]
* [`mobfcooking`]
* [`mooretrees`]
* [`mtfoods`]
* [`mushroom`]
* [`mush45`]
* Seaplants [`sea`]
* Simple mobs [`mobs`]
* Pizza [`pizza`]
* Not So Simple Mobs [`nssm`]

### Supported mods without optional dependency (mods provide their own support)

* Food ([`food`], [`food_basic`])
* Sweet Foods [`food_sweet`]

### Examples

* Eating an apple (from Minetest Game) increases your satiation by 2;
* eating a bread (from Minetest Game) increases your satiation by 4.

## Licensing
This mod is free software.

### Source code

* License: [LGPL v2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)
* Author: by Wuzzy (2015-2016)
* Forked from the “Better HUD (and hunger)” mod by BlockMen (2013-2015),
  most code comes from this mod.

### Textures

* `hbhunger_icon.png`—PilzAdam ([MIT License](https://opensource.org/licenses/MIT)), modified by BlockMen
* `hbhunger_bgicon.png`—PilzAdam (MIT License), modified by BlockMen
* `hbhunger_bar.png—Wuzzy` (MIT License)
* `hbhunger_icon_health_poison.png`—celeron55 ([CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)), modified by BlockMen, modified again by Wuzzy
* Everything else: MIT License, by BlockMen and Wuzzy


code:
-- Keep these for backwards compatibility
function hbhunger.save_hunger(player)
	hbhunger.set_hunger_raw(player)
end
function hbhunger.load_hunger(player)
	hbhunger.get_hunger_raw(player)
end

-- wrapper for minetest.item_eat (this way we make sure other mods can't break this one)
local org_eat = core.do_item_eat
core.do_item_eat = function(hp_change, replace_with_item, itemstack, user, pointed_thing)
	local old_itemstack = itemstack
	itemstack = hbhunger.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
	for _, callback in pairs(core.registered_on_item_eats) do
		local result = callback(hp_change, replace_with_item, itemstack, user, pointed_thing, old_itemstack)
		if result then
			return result
		end
	end
	return itemstack
end

-- food functions
local food = hbhunger.food

function hbhunger.register_food(name, hunger_change, replace_with_item, poisen, heal, sound)
	food[name] = {}
	food[name].saturation = hunger_change	-- hunger points added
	food[name].replace = replace_with_item	-- what item is given back after eating
	food[name].poisen = poisen				-- time its poisening
	food[name].healing = heal				-- amount of HP
	food[name].sound = sound				-- special sound that is played when eating
end

function hbhunger.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
	local item = itemstack:get_name()
	local def = food[item]
	if not def then
		def = {}
		if type(hp_change) ~= "number" then
			hp_change = 1
			core.log("error", "Wrong on_use() definition for item '" .. item .. "'")
		end
		def.saturation = hp_change * 1.3
		def.replace = replace_with_item
	end
	local func = hbhunger.item_eat(def.saturation, def.replace, def.poisen, def.healing, def.sound)
	return func(itemstack, user, pointed_thing)
end

-- Poison player
local function poisenp(tick, time, time_left, player)
	-- First check if player is still there
	if not player:is_player() then
		return
	end
	time_left = time_left + tick
	if time_left < time then
		minetest.after(tick, poisenp, tick, time, time_left, player)
	else
		hbhunger.poisonings[player:get_player_name()] = hbhunger.poisonings[player:get_player_name()] - 1
		if hbhunger.poisonings[player:get_player_name()] <= 0 then
			-- Reset HUD bar color
			hb.change_hudbar(player, "health", nil, nil, "hudbars_icon_health.png", nil, "hudbars_bar_health.png")
		end
	end
	if player:get_hp()-1 > 0 then
		player:set_hp(player:get_hp()-1)
	end

end

function hbhunger.item_eat(hunger_change, replace_with_item, poisen, heal, sound)
	return function(itemstack, user, pointed_thing)
		if itemstack:take_item() ~= nil and user ~= nil then
			local name = user:get_player_name()
			local h = tonumber(hbhunger.hunger[name])
			local hp = user:get_hp()
			if h == nil or hp == nil then
				return
			end
			minetest.sound_play({name = sound or "hbhunger_eat_generic", gain = 1}, {pos=user:getpos(), max_hear_distance = 16})

			-- Saturation
			if h < 30 and hunger_change then
				h = h + hunger_change
				if h > 30 then h = 30 end
				hbhunger.hunger[name] = h
				hbhunger.set_hunger_raw(user)
			end
			-- Healing
			if hp < 20 and heal then
				hp = hp + heal
				if hp > 20 then hp = 20 end
				user:set_hp(hp)
			end
			-- Poison
			if poisen then
				-- Set poison bar
				hb.change_hudbar(user, "health", nil, nil, "hbhunger_icon_health_poison.png", nil, "hbhunger_bar_health_poison.png")
				hbhunger.poisonings[name] = hbhunger.poisonings[name] + 1
				poisenp(1, poisen, 0, user)
			end

			if itemstack:get_count() == 0 then
				itemstack:add_item(replace_with_item)
			else
				local inv = user:get_inventory()
				if inv:room_for_item("main", replace_with_item) then
					inv:add_item("main", replace_with_item)
				else
					minetest.add_item(user:getpos(), replace_with_item)
				end
			end
		end
		return itemstack
	end
end

if minetest.get_modpath("default") ~= nil then
	hbhunger.register_food("default:apple", 2)
end
if minetest.get_modpath("flowers") ~= nil then
	hbhunger.register_food("flowers:mushroom_brown", 1)
	hbhunger.register_food("flowers:mushroom_red", 1, "", 3)
end
if minetest.get_modpath("farming") ~= nil then
	hbhunger.register_food("farming:bread", 4)
end

if minetest.get_modpath("mobs") ~= nil then
	if mobs.mod ~= nil and mobs.mod == "redo" then
		hbhunger.register_food("mobs:cheese", 4)
		hbhunger.register_food("mobs:meat", 8)
		hbhunger.register_food("mobs:meat_raw", 4)
		hbhunger.register_food("mobs:rat_cooked", 4)
		hbhunger.register_food("mobs:honey", 2)
		hbhunger.register_food("mobs:pork_raw", 3, "", 3)
		hbhunger.register_food("mobs:pork_cooked", 8)
		hbhunger.register_food("mobs:chicken_cooked", 6)
		hbhunger.register_food("mobs:chicken_raw", 2, "", 3)
		hbhunger.register_food("mobs:chicken_egg_fried", 2)
		if minetest.get_modpath("mesecraft_bucket") then
			hbhunger.register_food("mobs:bucket_milk", 3, "mesecraft_bucket:bucket_empty")
		end
	else
		hbhunger.register_food("mobs:meat", 6)
		hbhunger.register_food("mobs:meat_raw", 3)
		hbhunger.register_food("mobs:rat_cooked", 5)
	end
end

if minetest.get_modpath("moretrees") ~= nil then
	hbhunger.register_food("moretrees:coconut_milk", 1)
	hbhunger.register_food("moretrees:raw_coconut", 2)
	hbhunger.register_food("moretrees:acorn_muffin", 3)
	hbhunger.register_food("moretrees:spruce_nuts", 1)
	hbhunger.register_food("moretrees:pine_nuts", 1)
	hbhunger.register_food("moretrees:fir_nuts", 1)
end

if minetest.get_modpath("dwarves") ~= nil then
	hbhunger.register_food("dwarves:beer", 2)
	hbhunger.register_food("dwarves:apple_cider", 1)
	hbhunger.register_food("dwarves:midus", 2)
	hbhunger.register_food("dwarves:tequila", 2)
	hbhunger.register_food("dwarves:tequila_with_lime", 2)
	hbhunger.register_food("dwarves:sake", 2)
end

if minetest.get_modpath("animalmaterials") ~= nil then
	hbhunger.register_food("animalmaterials:milk", 2)
	hbhunger.register_food("animalmaterials:meat_raw", 3)
	hbhunger.register_food("animalmaterials:meat_pork", 3)
	hbhunger.register_food("animalmaterials:meat_beef", 3)
	hbhunger.register_food("animalmaterials:meat_chicken", 3)
	hbhunger.register_food("animalmaterials:meat_lamb", 3)
	hbhunger.register_food("animalmaterials:meat_venison", 3)
	hbhunger.register_food("animalmaterials:meat_undead", 3, "", 3)
	hbhunger.register_food("animalmaterials:meat_toxic", 3, "", 5)
	hbhunger.register_food("animalmaterials:meat_ostrich", 3)
	hbhunger.register_food("animalmaterials:fish_bluewhite", 2)
	hbhunger.register_food("animalmaterials:fish_clownfish", 2)
end

if minetest.get_modpath("fishing") ~= nil then
	hbhunger.register_food("fishing:fish_raw", 2)
	hbhunger.register_food("fishing:fish_cooked", 5)
	hbhunger.register_food("fishing:sushi", 6)
	hbhunger.register_food("fishing:shark", 4)
	hbhunger.register_food("fishing:shark_cooked", 8)
	hbhunger.register_food("fishing:pike", 4)
	hbhunger.register_food("fishing:pike_cooked", 8)
end

if minetest.get_modpath("glooptest") ~= nil then
	hbhunger.register_food("glooptest:kalite_lump", 1)
end

if minetest.get_modpath("bushes") ~= nil then
	hbhunger.register_food("bushes:sugar", 1)
	hbhunger.register_food("bushes:strawberry", 2)
	hbhunger.register_food("bushes:berry_pie_raw", 3)
	hbhunger.register_food("bushes:berry_pie_cooked", 4)
	hbhunger.register_food("bushes:basket_pies", 15)
end

if minetest.get_modpath("bushes_classic") then
	-- bushes_classic mod, as found in the plantlife modpack
	local berries = {
	    "strawberry",
		"blackberry",
		"blueberry",
		"raspberry",
		"gooseberry",
		"mixed_berry"}
	for _, berry in ipairs(berries) do
		if berry ~= "mixed_berry" then
			hbhunger.register_food("bushes:"..berry, 1)
		end
		hbhunger.register_food("bushes:"..berry.."_pie_raw", 2)
		hbhunger.register_food("bushes:"..berry.."_pie_cooked", 5)
		hbhunger.register_food("bushes:basket_"..berry, 15)
	end
end

if minetest.get_modpath("mushroom") ~= nil then
	hbhunger.register_food("mushroom:brown", 1)
	hbhunger.register_food("mushroom:red", 1, "", 3)
	-- mushroom potions: red = strong poison, brown = light restorative
	if minetest.get_modpath("vessels") then
		hbhunger.register_food("mushroom:brown_essence", 1, "vessels:glass_bottle", nil, 4)
		hbhunger.register_food("mushroom:poison", 1, "vessels:glass_bottle", 10)
	end
end

if minetest.get_modpath("docfarming") ~= nil then
	hbhunger.register_food("docfarming:carrot", 3)
	hbhunger.register_food("docfarming:cucumber", 2)
	hbhunger.register_food("docfarming:corn", 3)
	hbhunger.register_food("docfarming:potato", 4)
	hbhunger.register_food("docfarming:bakedpotato", 5)
	hbhunger.register_food("docfarming:raspberry", 3)
end

if minetest.get_modpath("farming_plus") ~= nil then
	hbhunger.register_food("farming_plus:carrot_item", 3)
	hbhunger.register_food("farming_plus:banana", 2)
	hbhunger.register_food("farming_plus:orange_item", 2)
	hbhunger.register_food("farming:pumpkin_bread", 4)
	hbhunger.register_food("farming_plus:strawberry_item", 2)
	hbhunger.register_food("farming_plus:tomato_item", 2)
	hbhunger.register_food("farming_plus:potato_item", 4)
	hbhunger.register_food("farming_plus:rhubarb_item", 2)
end

if minetest.get_modpath("mtfoods") ~= nil then
	hbhunger.register_food("mtfoods:dandelion_milk", 1)
	hbhunger.register_food("mtfoods:sugar", 1)
	hbhunger.register_food("mtfoods:short_bread", 4)
	hbhunger.register_food("mtfoods:cream", 1)
	hbhunger.register_food("mtfoods:chocolate", 2)
	hbhunger.register_food("mtfoods:cupcake", 2)
	hbhunger.register_food("mtfoods:strawberry_shortcake", 2)
	hbhunger.register_food("mtfoods:cake", 3)
	hbhunger.register_food("mtfoods:chocolate_cake", 3)
	hbhunger.register_food("mtfoods:carrot_cake", 3)
	hbhunger.register_food("mtfoods:pie_crust", 3)
	hbhunger.register_food("mtfoods:apple_pie", 3)
	hbhunger.register_food("mtfoods:rhubarb_pie", 2)
	hbhunger.register_food("mtfoods:banana_pie", 3)
	hbhunger.register_food("mtfoods:pumpkin_pie", 3)
	hbhunger.register_food("mtfoods:cookies", 2)
	hbhunger.register_food("mtfoods:mlt_burger", 5)
	hbhunger.register_food("mtfoods:potato_slices", 2)
	hbhunger.register_food("mtfoods:potato_chips", 3)
	--mtfoods:medicine
	hbhunger.register_food("mtfoods:casserole", 3)
	hbhunger.register_food("mtfoods:glass_flute", 2)
	hbhunger.register_food("mtfoods:orange_juice", 2)
	hbhunger.register_food("mtfoods:apple_juice", 2)
	hbhunger.register_food("mtfoods:apple_cider", 2)
	hbhunger.register_food("mtfoods:cider_rack", 2)
end

if minetest.get_modpath("fruit") ~= nil then
	hbhunger.register_food("fruit:apple", 2)
	hbhunger.register_food("fruit:pear", 2)
	hbhunger.register_food("fruit:bananna", 3)
	hbhunger.register_food("fruit:orange", 2)
end

if minetest.get_modpath("mush45") ~= nil then
	hbhunger.register_food("mush45:meal", 4)
end

if minetest.get_modpath("seaplants") ~= nil then
	hbhunger.register_food("seaplants:kelpgreen", 1)
	hbhunger.register_food("seaplants:kelpbrown", 1)
	hbhunger.register_food("seaplants:seagrassgreen", 1)
	hbhunger.register_food("seaplants:seagrassred", 1)
	hbhunger.register_food("seaplants:seasaladmix", 6)
	hbhunger.register_food("seaplants:kelpgreensalad", 1)
	hbhunger.register_food("seaplants:kelpbrownsalad", 1)
	hbhunger.register_food("seaplants:seagrassgreensalad", 1)
	hbhunger.register_food("seaplants:seagrassgreensalad", 1)
end

if minetest.get_modpath("mobfcooking") ~= nil then
	hbhunger.register_food("mobfcooking:cooked_pork", 6)
	hbhunger.register_food("mobfcooking:cooked_ostrich", 6)
	hbhunger.register_food("mobfcooking:cooked_beef", 6)
	hbhunger.register_food("mobfcooking:cooked_chicken", 6)
	hbhunger.register_food("mobfcooking:cooked_lamb", 6)
	hbhunger.register_food("mobfcooking:cooked_venison", 6)
	hbhunger.register_food("mobfcooking:cooked_fish", 6)
end

if minetest.get_modpath("creatures") ~= nil then
	hbhunger.register_food("creatures:meat", 6)
	hbhunger.register_food("creatures:flesh", 3)
	hbhunger.register_food("creatures:rotten_flesh", 3, "", 3)
end

if minetest.get_modpath("ethereal") then
   hbhunger.register_food("ethereal:strawberry", 1)
   hbhunger.register_food("ethereal:banana", 4)
   hbhunger.register_food("ethereal:pine_nuts", 1)
   hbhunger.register_food("ethereal:bamboo_sprout", 0, "", 3)
   hbhunger.register_food("ethereal:fern_tubers", 1)
   hbhunger.register_food("ethereal:banana_bread", 7)
   hbhunger.register_food("ethereal:mushroom_plant", 2)
   hbhunger.register_food("ethereal:coconut_slice", 2)
   hbhunger.register_food("ethereal:golden_apple", 4, "", nil, 10)
   hbhunger.register_food("ethereal:wild_onion_plant", 2)
   hbhunger.register_food("ethereal:mushroom_soup", 4, "ethereal:bowl")
   hbhunger.register_food("ethereal:mushroom_soup_cooked", 6, "ethereal:bowl")
   hbhunger.register_food("ethereal:hearty_stew", 6, "ethereal:bowl", 3)
   hbhunger.register_food("ethereal:hearty_stew_cooked", 10, "ethereal:bowl")
   if minetest.get_modpath("mesecraft_bucket") then
  	hbhunger.register_food("ethereal:bucket_cactus", 2, "mesecraft_bucket:bucket_empty")
   end
   hbhunger.register_food("ethereal:fish_raw", 2)
   hbhunger.register_food("ethereal:fish_cooked", 5)
   hbhunger.register_food("ethereal:seaweed", 1)
   hbhunger.register_food("ethereal:yellowleaves", 1, "", nil, 1)
   hbhunger.register_food("ethereal:sashimi", 4)
   hbhunger.register_food("ethereal:orange", 2)
end

if minetest.get_modpath("farming") and farming.mod == "redo" then
   hbhunger.register_food("farming:bread", 6)
   hbhunger.register_food("farming:potato", 1)
   hbhunger.register_food("farming:baked_potato", 6)
   hbhunger.register_food("farming:cucumber", 4)
   hbhunger.register_food("farming:tomato", 4)
   hbhunger.register_food("farming:carrot", 3)
   hbhunger.register_food("farming:carrot_gold", 6, "", nil, 8)
   hbhunger.register_food("farming:corn", 3)
   hbhunger.register_food("farming:corn_cob", 5)
   hbhunger.register_food("farming:melon_slice", 2)
   hbhunger.register_food("farming:pumpkin_slice", 1)
   hbhunger.register_food("farming:pumpkin_bread", 9)
   hbhunger.register_food("farming:coffee_cup", 2, "farming:drinking_cup")
   hbhunger.register_food("farming:coffee_cup_hot", 3, "farming:drinking_cup", nil, 2)
   hbhunger.register_food("farming:cookie", 2)
   hbhunger.register_food("farming:chocolate_dark", 3)
   hbhunger.register_food("farming:donut", 4)
   hbhunger.register_food("farming:donut_chocolate", 6)
   hbhunger.register_food("farming:donut_apple", 6)
   hbhunger.register_food("farming:raspberries", 1)
   hbhunger.register_food("farming:blueberries", 1)
   hbhunger.register_food("farming:muffin_blueberry", 4)
   if minetest.get_modpath("vessels") then
	hbhunger.register_food("farming:smoothie_raspberry", 2, "vessels:drinking_glass")
   end
   hbhunger.register_food("farming:rhubarb", 1)
   hbhunger.register_food("farming:rhubarb_pie", 6)
   hbhunger.register_food("farming:beans", 1)
end

if minetest.get_modpath("kpgmobs") ~= nil then
	hbhunger.register_food("kpgmobs:uley", 3)
	hbhunger.register_food("kpgmobs:meat", 6)
	hbhunger.register_food("kpgmobs:rat_cooked", 5)
	hbhunger.register_food("kpgmobs:med_cooked", 4)
  	if minetest.get_modpath("bucket") then
	   hbhunger.register_food("kpgmobs:bucket_milk", 4, "mesecraft_bucket:bucket_empty")
	end
end

if minetest.get_modpath("jkfarming") ~= nil then
	hbhunger.register_food("jkfarming:carrot", 3)
	hbhunger.register_food("jkfarming:corn", 3)
	hbhunger.register_food("jkfarming:melon_part", 2)
	hbhunger.register_food("jkfarming:cake", 3)
end

if minetest.get_modpath("jkanimals") ~= nil then
	hbhunger.register_food("jkanimals:meat", 6)
end

if minetest.get_modpath("jkwine") ~= nil then
	hbhunger.register_food("jkwine:grapes", 2)
	hbhunger.register_food("jkwine:winebottle", 1)
end

if minetest.get_modpath("cooking") ~= nil then
	hbhunger.register_food("cooking:meat_beef_cooked", 4)
	hbhunger.register_food("cooking:fish_bluewhite_cooked", 3)
	hbhunger.register_food("cooking:fish_clownfish_cooked", 1)
	hbhunger.register_food("cooking:meat_chicken_cooked", 2)
	hbhunger.register_food("cooking:meat_cooked", 2)
	hbhunger.register_food("cooking:meat_pork_cooked", 3)
	hbhunger.register_food("cooking:meat_toxic_cooked", -3)
	hbhunger.register_food("cooking:meat_venison_cooked", 3)
	hbhunger.register_food("cooking:meat_undead_cooked", 1)
end

-- ferns mod of plantlife_modpack
if minetest.get_modpath("ferns") ~= nil then
	hbhunger.register_food("ferns:fiddlehead", 1, "", 1)
	hbhunger.register_food("ferns:fiddlehead_roasted", 3)
	hbhunger.register_food("ferns:ferntuber_roasted", 3)
	hbhunger.register_food("ferns:horsetail_01", 1)
end

if minetest.get_modpath("pizza") ~= nil then
	hbhunger.register_food("pizza:pizza", 30, "", nil, 30)
	hbhunger.register_food("pizza:pizzaslice", 5, "", nil, 5)
end

if minetest.get_modpath("nssm") then
	hbhunger.register_food("nssm:werewolf_leg", 3)
	hbhunger.register_food("nssm:heron_leg", 2)
	hbhunger.register_food("nssm:chichibios_heron_leg", 4)
	hbhunger.register_food("nssm:crocodile_tail", 3)
	hbhunger.register_food("nssm:duck_legs", 1)
	hbhunger.register_food("nssm:ant_leg", 1)
	hbhunger.register_food("nssm:spider_leg", 1)
	hbhunger.register_food("nssm:tentacle", 2)
	hbhunger.register_food("nssm:worm_flesh", 2, "", 2) -- poisonous
	hbhunger.register_food("nssm:amphibian_heart", 1)
	hbhunger.register_food("nssm:raw_scrausics_wing", 1)
	-- superfoods
	hbhunger.register_food("nssm:phoenix_nuggets", 20, "", nil, 20)
	hbhunger.register_food("nssm:phoenix_tear", 20, "", nil, 20)
end

-- player-action based hunger changes
function hbhunger.handle_node_actions(pos, oldnode, player, ext)
	-- is_fake_player comes from the pipeworks, we are not interested in those
	if not player or not player:is_player() or player.is_fake_player == true then
		return
	end
	local name = player:get_player_name()
	local exhaus = hbhunger.exhaustion[name]
	if exhaus == nil then return end
	local new = hbhunger.EXHAUST_PLACE
	-- placenode event
	if not ext then
		new = hbhunger.EXHAUST_DIG
	end
	-- assume its send by main timer when movement detected
	if not pos and not oldnode then
		new = hbhunger.EXHAUST_MOVE
	end
	exhaus = exhaus + new
	if exhaus > hbhunger.EXHAUST_LVL then
		exhaus = 0
		local h = tonumber(hbhunger.hunger[name])
		h = h - 1
		if h < 0 then h = 0 end
		hbhunger.hunger[name] = h
		hbhunger.set_hunger_raw(player)
	end
	hbhunger.exhaustion[name] = exhaus
end

minetest.register_on_placenode(hbhunger.handle_node_actions)
minetest.register_on_dignode(hbhunger.handle_node_actions)


code:
local S = minetest.get_translator("hbhunger")

if minetest.settings:get_bool("enable_damage") then

hbhunger = {}
hbhunger.food = {}

-- HUD statbar values
hbhunger.hunger = {}
hbhunger.hunger_out = {}

-- Count number of poisonings a player has at once
hbhunger.poisonings = {}

-- HUD item ids
local hunger_hud = {}

hbhunger.HUD_TICK = 1.0

--Some hunger settings
hbhunger.exhaustion = {} -- Exhaustion is experimental!

hbhunger.HUNGER_TICK = 600 -- time in seconds after that 1 hunger point is taken
hbhunger.EXHAUST_DIG = 3  -- exhaustion increased this value after digged node
hbhunger.EXHAUST_PLACE = 1 -- exhaustion increased this value after placed
hbhunger.EXHAUST_MOVE = 0.3 -- exhaustion increased this value if player movement detected
hbhunger.EXHAUST_LVL = 160 -- at what exhaustion player satiation gets lowerd

--load custom settings
local set = io.open(minetest.get_modpath("hbhunger").."/hbhunger.conf", "r")
if set then
	dofile(minetest.get_modpath("hbhunger").."/hbhunger.conf")
	set:close()
end

local function custom_hud(player)
	hb.init_hudbar(player, "satiation", hbhunger.get_hunger_raw(player))
end

dofile(minetest.get_modpath("hbhunger").."/hunger.lua")

-- register satiation hudbar
	hb.register_hudbar("satiation", 0xFFFFFF, S("Food"), { icon = "hbhunger_icon.png", bgicon = "hbhunger_bgicon.png",  bar = "hbhunger_bar.png" }, 20, 30, false, nil, { format_value = "%.1f", format_max_value = "%d" })

-- update hud elemtens if value has changed
local function update_hud(player)
	local name = player:get_player_name()
 --hunger
	local h_out = tonumber(hbhunger.hunger_out[name])
	local h = tonumber(hbhunger.hunger[name])
	if h_out ~= h then
		hbhunger.hunger_out[name] = h
		hb.change_hudbar(player, "satiation", h)
	end
end

hbhunger.get_hunger_raw = function(player)
	local inv = player:get_inventory()
	if not inv then return nil end
	local hgp = inv:get_stack("hunger", 1):get_count()
	if hgp == 0 then
		hgp = 21
		inv:set_stack("hunger", 1, ItemStack({name=":", count=hgp}))
	else
		hgp = hgp
	end
	return hgp-1
end

hbhunger.set_hunger_raw = function(player)
	local inv = player:get_inventory()
	local name = player:get_player_name()
	local value = hbhunger.hunger[name]
	if not inv  or not value then return nil end
	if value > 30 then value = 30 end
	if value < 0 then value = 0 end

	inv:set_stack("hunger", 1, ItemStack({name=":", count=value+1}))

	return true
end

minetest.register_on_joinplayer(function(player)
	local name = player:get_player_name()
	local inv = player:get_inventory()
	inv:set_size("hunger",1)
	hbhunger.hunger[name] = hbhunger.get_hunger_raw(player)
	hbhunger.hunger_out[name] = hbhunger.hunger[name]
	hbhunger.exhaustion[name] = 0
	hbhunger.poisonings[name] = 0
	custom_hud(player)
	hbhunger.set_hunger_raw(player)
end)

minetest.register_on_respawnplayer(function(player)
	-- reset hunger (and save)
	local name = player:get_player_name()
	hbhunger.hunger[name] = 20
	hbhunger.set_hunger_raw(player)
	hbhunger.exhaustion[name] = 0
end)

local main_timer = 0
local timer = 0
local timer2 = 0
minetest.register_globalstep(function(dtime)
	main_timer = main_timer + dtime
	timer = timer + dtime
	timer2 = timer2 + dtime
	if main_timer > hbhunger.HUD_TICK or timer > 4 or timer2 > hbhunger.HUNGER_TICK then
		if main_timer > hbhunger.HUD_TICK then main_timer = 0 end
		for _,player in ipairs(minetest.get_connected_players()) do
		local name = player:get_player_name()

		local h = tonumber(hbhunger.hunger[name])
		local hp = player:get_hp()
		if timer > 4 then
			-- heal player by 1 hp if not dead and satiation is > 15 (of 30)
			if h > 15 and hp > 0 and player:get_breath() > 0 then
				player:set_hp(hp+1)
				-- or damage player by 1 hp if satiation is < 2 (of 30)
				elseif h <= 1 then
					if hp-1 >= 0 then player:set_hp(hp-1) end
				end
			end
			-- lower satiation by 1 point after xx seconds
			if timer2 > hbhunger.HUNGER_TICK then
				if h > 0 then
					h = h-1
					hbhunger.hunger[name] = h
					hbhunger.set_hunger_raw(player)
				end
			end

			-- update all hud elements
			update_hud(player)

			local controls = player:get_player_control()
			-- Determine if the player is walking
			if controls.up or controls.down or controls.left or controls.right then
				hbhunger.handle_node_actions(nil, nil, player)
			end
		end
	end
	if timer > 4 then timer = 0 end
	if timer2 > hbhunger.HUNGER_TICK then timer2 = 0 end
end)

end


code:
# textdomain:hbhunger
Satiation=Sazietà


code:
# textdomain:hbhunger
Satiation=


code:
name = hbhunger
description = Adds a simple hunger meachanic with satiation, food poisoning and different healing.
depends = hudbars
optional_depends = default, flowers, animalmaterials, bucket, bushes, bushes_classic, cooking, creatures, docfarming, dwarves, ethereal, farming, farming_plus, ferns, fishing, fruit, glooptest, jkanimals, jkfarming, jkwine, kpgmobs, mobfcooking, mobs, moretrees, mtfoods, mush45, mushroom, seaplants, pizza, nssm


code:
# Headanim
Makes player heads follow their look dir in Minetest. Requires version 5.3+


code:
minetest.register_globalstep(function(dtime)
	for _, player in pairs(minetest.get_connected_players()) do
		player:set_bone_position("Head", vector.new(0, 6.75, 0), vector.new(-math.deg(player:get_look_vertical()), 0, 0))
	end
end)